@pilotiq/pilotiq 0.10.0 → 0.12.0
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/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +128 -0
- package/dist/react/AppShell.d.ts +1 -1
- package/dist/react/AppShell.d.ts.map +1 -1
- package/dist/react/AppShell.js +7 -1
- package/dist/react/AppShell.js.map +1 -1
- package/dist/react/CollabTextRendererRegistry.d.ts +75 -0
- package/dist/react/CollabTextRendererRegistry.d.ts.map +1 -0
- package/dist/react/CollabTextRendererRegistry.js +18 -0
- package/dist/react/CollabTextRendererRegistry.js.map +1 -0
- package/dist/react/CurrentUserContext.d.ts +39 -0
- package/dist/react/CurrentUserContext.d.ts.map +1 -0
- package/dist/react/CurrentUserContext.js +27 -0
- package/dist/react/CurrentUserContext.js.map +1 -0
- package/dist/react/FormCollabBindingRegistry.d.ts +161 -17
- package/dist/react/FormCollabBindingRegistry.d.ts.map +1 -1
- package/dist/react/FormCollabBindingRegistry.js.map +1 -1
- package/dist/react/FormStateContext.d.ts +39 -1
- package/dist/react/FormStateContext.d.ts.map +1 -1
- package/dist/react/FormStateContext.js +126 -33
- package/dist/react/FormStateContext.js.map +1 -1
- package/dist/react/fields/BuilderInput.d.ts.map +1 -1
- package/dist/react/fields/BuilderInput.js +112 -10
- package/dist/react/fields/BuilderInput.js.map +1 -1
- package/dist/react/fields/MarkdownInput.d.ts.map +1 -1
- package/dist/react/fields/MarkdownInput.js +60 -1
- package/dist/react/fields/MarkdownInput.js.map +1 -1
- package/dist/react/fields/RepeaterInput.d.ts.map +1 -1
- package/dist/react/fields/RepeaterInput.js +113 -10
- package/dist/react/fields/RepeaterInput.js.map +1 -1
- package/dist/react/fields/TextLikeInput.d.ts.map +1 -1
- package/dist/react/fields/TextLikeInput.js +83 -6
- package/dist/react/fields/TextLikeInput.js.map +1 -1
- package/dist/react/formStateHelpers.d.ts +102 -0
- package/dist/react/formStateHelpers.d.ts.map +1 -1
- package/dist/react/formStateHelpers.js +234 -0
- package/dist/react/formStateHelpers.js.map +1 -1
- package/dist/react/index.d.ts +4 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +5 -5
- package/src/react/AppShell.tsx +11 -1
- package/src/react/CollabTextRendererRegistry.ts +84 -0
- package/src/react/CurrentUserContext.tsx +50 -0
- package/src/react/FormCollabBindingRegistry.ts +160 -17
- package/src/react/FormStateContext.tsx +157 -34
- package/src/react/fields/BuilderInput.tsx +97 -8
- package/src/react/fields/MarkdownInput.tsx +118 -1
- package/src/react/fields/RepeaterInput.tsx +97 -8
- package/src/react/fields/TextLikeInput.tsx +129 -5
- package/src/react/formStateHelpers.test.ts +312 -0
- package/src/react/formStateHelpers.ts +246 -0
- package/src/react/index.ts +15 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
> @pilotiq/pilotiq@0.
|
|
2
|
+
> @pilotiq/pilotiq@0.12.0 build /home/runner/work/pilotiq/pilotiq/packages/pilotiq
|
|
3
3
|
> tsc -p tsconfig.build.json && pnpm run copy-assets
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @pilotiq/pilotiq@0.
|
|
6
|
+
> @pilotiq/pilotiq@0.12.0 copy-assets /home/runner/work/pilotiq/pilotiq/packages/pilotiq
|
|
7
7
|
> mkdir -p dist/styles && cp -R src/styles/*.css dist/styles/
|
|
8
8
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,133 @@
|
|
|
1
1
|
# @pilotiq/pilotiq
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add `CurrentUserContext` exported from `@pilotiq/pilotiq/react` so plugins can read the active user (driven by `Pilotiq.user(req => …)`) without prop-drilling through `panel`. `AppShell` mounts the provider around the layout-provider chain seeded from `panel.userMenu?.user`; plugins call `useCurrentUser()` from inside any layout provider or descendant component.
|
|
8
|
+
|
|
9
|
+
Also fixes collab text field chrome: the single-line `TextLikeInput.tsx` chrome now uses `overflow-x-clip` instead of `overflow-x-auto` so `CollaborationCaret` user-name labels can escape the input upward (CSS spec: `auto` on either axis forces the other axis to non-visible too; `clip` is the one non-visible value that allows the orthogonal axis to remain `visible`). Trade-off: long text gets clipped on the right rather than horizontally scrollable inside a collab field — acceptable for plain-text fields where the presence label is the higher-value affordance.
|
|
10
|
+
|
|
11
|
+
## 0.11.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- d36902d: feat(pilotiq): F.5a — Repeater/Builder row-identity contract for collab
|
|
16
|
+
|
|
17
|
+
Widens `FormCollabBinding` with five optional row-array methods plus a
|
|
18
|
+
`RowsEvent` type so the upcoming `Y.Array<Y.Map>` impl in
|
|
19
|
+
`@pilotiq-pro/collab` (F.5b) has a stable surface to hook into. Renderer
|
|
20
|
+
side wiring is live in this release — `RepeaterInput` + `BuilderInput`
|
|
21
|
+
already dispatch `add` / `remove` / `reorder` / `subscribe` through the
|
|
22
|
+
binding when one is registered and reconcile remote row events into
|
|
23
|
+
their local state by `__id`. No behaviour change for non-collab forms
|
|
24
|
+
or bindings that pre-date F.5; pre-F.5 bindings keep typechecking
|
|
25
|
+
because every new method is optional.
|
|
26
|
+
|
|
27
|
+
### New public surface
|
|
28
|
+
|
|
29
|
+
- **`useRowBinding(arrayName)`** — returns a `RowBindingApi` pre-bound
|
|
30
|
+
to a Repeater/Builder field name, or `null` when no F.5 binding is
|
|
31
|
+
active (outside a collab room, pre-F.5 plugin, opted out via
|
|
32
|
+
`.collab(false)`, or non-array field).
|
|
33
|
+
- **`RowBindingApi`** — `{ add, remove, reorder, subscribe }`. Each
|
|
34
|
+
method's `arrayName` arg is pre-bound; `subscribe(fn)` returns an
|
|
35
|
+
unsubscribe function for `useEffect` cleanup.
|
|
36
|
+
- **`RowsEvent`** — `add | remove | move` discriminated union with
|
|
37
|
+
`rowId` + indices for the renderer to reconcile against its current
|
|
38
|
+
`rows` state.
|
|
39
|
+
- **`FormCollabBinding.addRow / removeRow / reorderRows / setRow /
|
|
40
|
+
getRowTextBinding / subscribeRows`** — all optional. Bindings opt
|
|
41
|
+
into F.5 by implementing the trio `addRow + removeRow + reorderRows`;
|
|
42
|
+
`subscribeRows` and `setRow` layer on for remote-event + dotted-path
|
|
43
|
+
routing; `getRowTextBinding` is reserved for F.5c (per-row `Y.Text`).
|
|
44
|
+
|
|
45
|
+
### `FormStateProvider` routing
|
|
46
|
+
|
|
47
|
+
- `setValue` and the live-resolve overlay both route through
|
|
48
|
+
`routeBindingWrite` — top-level names go to `binding.set`, row leaves
|
|
49
|
+
(matching `parseRowFieldPath`) go to `binding.setRow` when available.
|
|
50
|
+
Pre-F.5 row leaves continue to stay local-only.
|
|
51
|
+
- The provider walks `formMeta` for top-level Repeater/Builder field
|
|
52
|
+
names at binding mount and builds a per-array `RowBindingApi` map
|
|
53
|
+
exposed via `useRowBinding`.
|
|
54
|
+
|
|
55
|
+
### Known v1 limitations (kept from the F.5 plan)
|
|
56
|
+
|
|
57
|
+
- Nested Repeaters (e.g. `articles.0.comments.0.body`) stay local-only
|
|
58
|
+
— `parseRowFieldPath` returns `null` and the binding never sees them.
|
|
59
|
+
- Server-derived row values now propagate through `setRow` when
|
|
60
|
+
available; without an F.5 binding they continue to be dropped.
|
|
61
|
+
- F.5c (`getRowTextBinding`) — character-level `Y.Text` per row text
|
|
62
|
+
field — lands in a follow-up; row leaves stay on row-level LWW until
|
|
63
|
+
then.
|
|
64
|
+
|
|
65
|
+
- b70cb49: feat(pilotiq): F.5c — per-row Y.Text composition with F.6
|
|
66
|
+
|
|
67
|
+
Wires `useFieldState(dottedName).textBinding` to resolve through
|
|
68
|
+
`FormCollabBinding.getRowTextBinding(arrayName, rowId, fieldName)` so
|
|
69
|
+
Repeater/Builder row text fields ride character-level CRDT when the
|
|
70
|
+
plugin implements F.5c. Previously dotted-name `textBinding` always
|
|
71
|
+
returned `null`; now it returns a stable handle when:
|
|
72
|
+
|
|
73
|
+
- the row's `__id` is already stamped in the values map,
|
|
74
|
+
- the inner field's `fieldType` is in the F.6 allowlist
|
|
75
|
+
(`text / textarea / email / slug / markdown`),
|
|
76
|
+
- the field isn't opted out via `.collab(false)`,
|
|
77
|
+
- the active binding implements `getRowTextBinding`.
|
|
78
|
+
|
|
79
|
+
### Walker
|
|
80
|
+
|
|
81
|
+
A new `collectRowTextLeavesByArray(formMeta)` helper walks each
|
|
82
|
+
Repeater's inner schema + each Builder block's template once at
|
|
83
|
+
binding mount and stashes the per-array text-leaf names on
|
|
84
|
+
`FormStateApi.rowTextLeaves`. Nested Repeater/Builder boundaries stop
|
|
85
|
+
the walk — 5-segment dotted paths remain out of scope.
|
|
86
|
+
|
|
87
|
+
### Renderer surface unchanged
|
|
88
|
+
|
|
89
|
+
`BoundTextInput` already branches on `textBinding != null` from F.6,
|
|
90
|
+
so rows pick up the character-level path automatically once an F.5c-
|
|
91
|
+
capable binding is registered. No new renderer wiring beyond the
|
|
92
|
+
walker + `useFieldState` resolver.
|
|
93
|
+
|
|
94
|
+
### Patch Changes
|
|
95
|
+
|
|
96
|
+
- 08ab5bb: fix(pilotiq): F.5c row-text integration — stamp row `__id` and walk `template` not `children`
|
|
97
|
+
|
|
98
|
+
Two integration gaps in the just-shipped F.5c per-row Y.Text path made
|
|
99
|
+
character-level CRDT silently fall back to LWW for every Repeater /
|
|
100
|
+
Builder row text leaf. Both green-CI / broken-at-render bugs.
|
|
101
|
+
|
|
102
|
+
### `collectRowTextLeavesByArray` walked the wrong meta key
|
|
103
|
+
|
|
104
|
+
The walker read `meta.children` for the Repeater's inner row schema,
|
|
105
|
+
but `RepeaterField.toMeta()` emits the row schema under `meta.template`
|
|
106
|
+
(`meta.children` is the per-resolved-row child list, not the field-
|
|
107
|
+
level template). Walker always returned empty → `FormStateApi.rowTextLeaves`
|
|
108
|
+
stayed `null` → `useFieldState(dottedName).textBinding` short-circuited
|
|
109
|
+
on every dotted row-leaf name. The unit-test fixture mirrored the same
|
|
110
|
+
wrong shape, so CI passed while the renderer was inert.
|
|
111
|
+
|
|
112
|
+
### `RepeaterInput` / `BuilderInput` never stamped row `__id` in `ctx.values`
|
|
113
|
+
|
|
114
|
+
`resolveRowTextBinding` looks up `rowIdAtIndex(ctx.values, name, i)` which
|
|
115
|
+
reads `values['${name}.${i}.__id']`. The renderer maintained row identity
|
|
116
|
+
in local component state but never mirrored it into `ctx.values`, so the
|
|
117
|
+
lookup returned `null` and the binding chain never fired — even for
|
|
118
|
+
locally-added rows.
|
|
119
|
+
|
|
120
|
+
Both renderers now mirror `rows` into `ctx.values` via a single
|
|
121
|
+
`useEffect` keyed on the rows array. Pre-existing server-seeded rows
|
|
122
|
+
were unaffected because the seed wasn't a renderer concern; only
|
|
123
|
+
locally-added or remote-reconciled rows hit the gap.
|
|
124
|
+
|
|
125
|
+
### Tests
|
|
126
|
+
|
|
127
|
+
`formStateHelpers.test.ts`'s hand-built `repeater()` helper now emits
|
|
128
|
+
`template:` instead of `children:` to match `RepeaterField.toMeta()`.
|
|
129
|
+
Catches future drift between meta producers and walkers.
|
|
130
|
+
|
|
3
131
|
## 0.10.0
|
|
4
132
|
|
|
5
133
|
### Minor Changes
|
package/dist/react/AppShell.d.ts
CHANGED
|
@@ -83,5 +83,5 @@ export interface AppShellProps {
|
|
|
83
83
|
componentSlotRegistry?: ComponentSlotRegistry;
|
|
84
84
|
children: React.ReactNode;
|
|
85
85
|
}
|
|
86
|
-
export declare function AppShell({ layout, notifications, componentRegistry, rightPanelRegistry, layoutProviderRegistry, ...props }: AppShellProps):
|
|
86
|
+
export declare function AppShell({ layout, notifications, componentRegistry, rightPanelRegistry, layoutProviderRegistry, ...props }: AppShellProps): import("react/jsx-runtime").JSX.Element;
|
|
87
87
|
//# sourceMappingURL=AppShell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/react/AppShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAKlD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAInE,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACxG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/react/AppShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAKlD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAInE,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACxG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAGjE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QAC3C,oEAAoE;QACpE,UAAU,CAAC,EAAE,OAAO,EAAE,CAAA;QACtB;sEAC8D;QAC9D,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;QAC9B;;qEAE6D;QAC7D,qBAAqB,CAAC,EAAE,yBAAyB,CAAA;QACjD;;yEAEiE;QACjE,YAAY,CAAC,EAAE,gBAAgB,CAAA;QAC/B;;wEAEgE;QAChE,YAAY,CAAC,EAAE,eAAe,CAAA;QAC9B;;;yCAGiC;QACjC,WAAW,CAAC,EAAE,aAAa,CAAA;QAC3B,WAAW,CAAC,EAAE,OAAO,CAAA;QACrB;;;iEAGyD;QACzD,iBAAiB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;KACtC,CAAA;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC7B;8CAC0C;IAC1C,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAClC;;;;wCAIoC;IACpC,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,aAAa,CAAC;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;IAC7G;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED,wBAAgB,QAAQ,CAAC,EAAE,MAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,2CAqGrJ"}
|
package/dist/react/AppShell.js
CHANGED
|
@@ -11,6 +11,7 @@ import { RightSidebar } from './RightSidebar.js';
|
|
|
11
11
|
import { RecordWrapperGate } from './RecordWrapperGate.js';
|
|
12
12
|
import { useIsMobile } from './hooks/use-mobile.js';
|
|
13
13
|
import { RenderHookSlot } from './RenderHookSlot.js';
|
|
14
|
+
import { CurrentUserProvider } from './CurrentUserContext.js';
|
|
14
15
|
export function AppShell({ layout = 'sidebar', notifications, componentRegistry, rightPanelRegistry, layoutProviderRegistry, ...props }) {
|
|
15
16
|
const Layout = layout === 'topbar' ? TopbarLayout : SidebarLayout;
|
|
16
17
|
// exactOptionalPropertyTypes: only spread `initialNotifications` when set.
|
|
@@ -53,7 +54,12 @@ export function AppShell({ layout = 'sidebar', notifications, componentRegistry,
|
|
|
53
54
|
// provider sits OUTERMOST (closest to the layout root); LAST sits
|
|
54
55
|
// INNERMOST (closest to the page tree). Empty / unset → no wrap.
|
|
55
56
|
const wrapped = wrapInLayoutProviders(_jsx(ComponentRegistryProvider, { value: componentRegistry, children: _jsx(RightPanelRegistryProvider, { value: rightPanelRegistry, children: rightSidebarMeta ? (_jsx(RightSidebarProvider, { meta: rightSidebarMeta, basePath: props.basePath, children: inner })) : (inner) }) }), layoutProviderRegistry, props.basePath);
|
|
56
|
-
|
|
57
|
+
// `CurrentUserProvider` sits OUTSIDE the layout-provider chain so
|
|
58
|
+
// plugin-registered providers (e.g. `@pilotiq-pro/collab`'s
|
|
59
|
+
// CollabProvider, which threads the user into CollaborationCaret
|
|
60
|
+
// presence labels) can read the active user via `useCurrentUser()`.
|
|
61
|
+
// Value source mirrors what the top-right user dropdown renders.
|
|
62
|
+
return (_jsx(CurrentUserProvider, { value: props.panel.userMenu?.user ?? null, children: wrapped }));
|
|
57
63
|
}
|
|
58
64
|
/**
|
|
59
65
|
* Fold registered layout providers around `tree` from last to first so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.js","sourceRoot":"","sources":["../../src/react/AppShell.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAG5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAA;AAE7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AACtE,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAwB,MAAM,wBAAwB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"AppShell.js","sourceRoot":"","sources":["../../src/react/AppShell.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Module-level registry slot for the collab-aware plain-text editor renderer.
|
|
4
|
+
*
|
|
5
|
+
* Wiring posture (mirrors `CollabExtensionFactoryRegistry` /
|
|
6
|
+
* `FormCollabBindingRegistry`):
|
|
7
|
+
* - `@pilotiq/tiptap`'s `registerTiptap(...)` calls `registerCollabTextRenderer(...)`
|
|
8
|
+
* once at boot. The registered component closes over `@tiptap/*` imports so
|
|
9
|
+
* pilotiq core stays free of any tiptap peer dep — same posture as the
|
|
10
|
+
* existing rich-text renderer registry.
|
|
11
|
+
* - `TextLikeInput` checks for the registered component when a `<RecordCollabRoom>`
|
|
12
|
+
* is mounted up-tree AND the field hasn't opted out via `.collab(false)` AND
|
|
13
|
+
* the field has no `.mask()`. If present, the legacy `BoundTextInput`
|
|
14
|
+
* (Y.Text + `computeDelta` + heuristic `preserveCursor`) is bypassed in
|
|
15
|
+
* favour of a y-prosemirror-backed Tiptap editor that anchors selections to
|
|
16
|
+
* `Yjs.RelativePosition` — the architectural fix for the cursor-jump and
|
|
17
|
+
* two-peer concurrent-insert races documented in
|
|
18
|
+
* `docs/plans/text-fields-tiptap-backed-collab.md`.
|
|
19
|
+
*
|
|
20
|
+
* Wire props are deliberately framework-agnostic — the renderer doesn't take a
|
|
21
|
+
* `binding` since it consumes the room's `ydoc` directly via the existing
|
|
22
|
+
* `useCollabRoom()` + `getCollabExtensions()` plumbing on its own side. Core
|
|
23
|
+
* keeps the seam narrow: handler callbacks + DOM chrome only.
|
|
24
|
+
*/
|
|
25
|
+
export interface CollabTextRendererProps {
|
|
26
|
+
/** Field name — drives the `Y.XmlFragment` selector inside the collab adapter. */
|
|
27
|
+
name: string;
|
|
28
|
+
/** `true` for textarea-like (multiple paragraphs); `false` for input-like. */
|
|
29
|
+
multiline: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Server-rendered default value. The renderer is expected to seed the
|
|
32
|
+
* `Y.XmlFragment` from this on first connect when the room has no
|
|
33
|
+
* persisted state for this field (i.e. brand-new record).
|
|
34
|
+
*/
|
|
35
|
+
defaultValue: string;
|
|
36
|
+
/** Optional placeholder hint. */
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
/** Disabled / read-only state. */
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
/** Fired on every editor `update` with the editor's current plain text. */
|
|
41
|
+
onChange: (text: string) => void;
|
|
42
|
+
/** Fired on editor blur — host wires this to live-onBlur trigger semantics. */
|
|
43
|
+
onBlur: () => void;
|
|
44
|
+
/**
|
|
45
|
+
* Single-line submit — fired when `multiline: false` AND the user presses
|
|
46
|
+
* Enter. The renderer is expected to blur the editor after invoking this.
|
|
47
|
+
* Multiline mode ignores it (Enter inserts a paragraph instead).
|
|
48
|
+
*/
|
|
49
|
+
onSubmit?: () => void;
|
|
50
|
+
/**
|
|
51
|
+
* Tailwind className applied to the editor's contenteditable wrapper so the
|
|
52
|
+
* rendered editor matches the native `<input>` / `<textarea>` chrome it
|
|
53
|
+
* replaces. The host owns the styling — the adapter just forwards.
|
|
54
|
+
*/
|
|
55
|
+
className?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Additional DOM attributes for the editor's contenteditable wrapper —
|
|
58
|
+
* typically `id`, `aria-*`, `autocomplete`, etc.
|
|
59
|
+
*/
|
|
60
|
+
editorAttributes?: Record<string, string>;
|
|
61
|
+
}
|
|
62
|
+
export type CollabTextRenderer = ComponentType<CollabTextRendererProps>;
|
|
63
|
+
/**
|
|
64
|
+
* Register the collab plain-text editor component. Called once at boot by
|
|
65
|
+
* `@pilotiq/tiptap`'s `registerTiptap()` (or directly by an app that imports
|
|
66
|
+
* the renderer). Calling with `null` clears the registry — useful for tests.
|
|
67
|
+
*
|
|
68
|
+
* No-op behaviour when no renderer is registered: `TextLikeInput` falls back
|
|
69
|
+
* to the legacy `BoundTextInput` path (or the plain controlled / uncontrolled
|
|
70
|
+
* input when no collab room is mounted).
|
|
71
|
+
*/
|
|
72
|
+
export declare function registerCollabTextRenderer(component: CollabTextRenderer | null): void;
|
|
73
|
+
/** Returns the registered component, or `null` when no adapter is installed. */
|
|
74
|
+
export declare function getCollabTextRenderer(): CollabTextRenderer | null;
|
|
75
|
+
//# sourceMappingURL=CollabTextRendererRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollabTextRendererRegistry.d.ts","sourceRoot":"","sources":["../../src/react/CollabTextRendererRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,uBAAuB;IACtC,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAA;IACZ,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2EAA2E;IAC3E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1C;AAED,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC,uBAAuB,CAAC,CAAA;AAIvE;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAErF;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,IAAI,kBAAkB,GAAG,IAAI,CAEjE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
let _renderer = null;
|
|
2
|
+
/**
|
|
3
|
+
* Register the collab plain-text editor component. Called once at boot by
|
|
4
|
+
* `@pilotiq/tiptap`'s `registerTiptap()` (or directly by an app that imports
|
|
5
|
+
* the renderer). Calling with `null` clears the registry — useful for tests.
|
|
6
|
+
*
|
|
7
|
+
* No-op behaviour when no renderer is registered: `TextLikeInput` falls back
|
|
8
|
+
* to the legacy `BoundTextInput` path (or the plain controlled / uncontrolled
|
|
9
|
+
* input when no collab room is mounted).
|
|
10
|
+
*/
|
|
11
|
+
export function registerCollabTextRenderer(component) {
|
|
12
|
+
_renderer = component;
|
|
13
|
+
}
|
|
14
|
+
/** Returns the registered component, or `null` when no adapter is installed. */
|
|
15
|
+
export function getCollabTextRenderer() {
|
|
16
|
+
return _renderer;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=CollabTextRendererRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollabTextRendererRegistry.js","sourceRoot":"","sources":["../../src/react/CollabTextRendererRegistry.ts"],"names":[],"mappings":"AAiEA,IAAI,SAAS,GAA8B,IAAI,CAAA;AAE/C;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAoC;IAC7E,SAAS,GAAG,SAAS,CAAA;AACvB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,qBAAqB;IACnC,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Resolved identity of the user driving the current page. Mirrors the
|
|
4
|
+
* `UserMenuMeta.user` shape that `panelInfo()` ships to the renderer —
|
|
5
|
+
* whichever fields the `Pilotiq.user(req => …)` resolver populated.
|
|
6
|
+
*
|
|
7
|
+
* `null` is the no-user state: either the panel never wired a resolver,
|
|
8
|
+
* or the resolver returned `null` for this request. Consumers should
|
|
9
|
+
* gracefully fall back (no avatar, no presence label, etc.) rather than
|
|
10
|
+
* treating absence as an error.
|
|
11
|
+
*/
|
|
12
|
+
export interface CurrentUser {
|
|
13
|
+
name?: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
avatar?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Mounted by `AppShell` around the layout-provider chain so plugins
|
|
19
|
+
* (collab user presence, audit-trail attribution, analytics
|
|
20
|
+
* client-side opt-outs, …) can read the active user via
|
|
21
|
+
* `useCurrentUser()` without prop-drilling through `panel`.
|
|
22
|
+
*
|
|
23
|
+
* Value source is `viewProps.panel.userMenu?.user` — the same shape the
|
|
24
|
+
* top-right dropdown renders. The provider sits OUTSIDE
|
|
25
|
+
* `layoutProviderRegistry` so plugin-registered layout providers can
|
|
26
|
+
* subscribe.
|
|
27
|
+
*/
|
|
28
|
+
export declare function CurrentUserProvider({ value, children, }: {
|
|
29
|
+
value: CurrentUser | null;
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
}): ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Read the active user inside any descendant of `<AppShell>`. Returns
|
|
34
|
+
* `null` outside an `AppShell` mount (defensive — keeps storybook /
|
|
35
|
+
* isolated-render tests from throwing) and when no user resolved for
|
|
36
|
+
* the request.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useCurrentUser(): CurrentUser | null;
|
|
39
|
+
//# sourceMappingURL=CurrentUserContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CurrentUserContext.d.ts","sourceRoot":"","sources":["../../src/react/CurrentUserContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjE;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAI,MAAM,CAAA;IACf,KAAK,CAAC,EAAG,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,SAAS,CAAA;CACpB,GAAG,SAAS,CAEZ;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
const CurrentUserContext = createContext(null);
|
|
4
|
+
/**
|
|
5
|
+
* Mounted by `AppShell` around the layout-provider chain so plugins
|
|
6
|
+
* (collab user presence, audit-trail attribution, analytics
|
|
7
|
+
* client-side opt-outs, …) can read the active user via
|
|
8
|
+
* `useCurrentUser()` without prop-drilling through `panel`.
|
|
9
|
+
*
|
|
10
|
+
* Value source is `viewProps.panel.userMenu?.user` — the same shape the
|
|
11
|
+
* top-right dropdown renders. The provider sits OUTSIDE
|
|
12
|
+
* `layoutProviderRegistry` so plugin-registered layout providers can
|
|
13
|
+
* subscribe.
|
|
14
|
+
*/
|
|
15
|
+
export function CurrentUserProvider({ value, children, }) {
|
|
16
|
+
return _jsx(CurrentUserContext.Provider, { value: value, children: children });
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read the active user inside any descendant of `<AppShell>`. Returns
|
|
20
|
+
* `null` outside an `AppShell` mount (defensive — keeps storybook /
|
|
21
|
+
* isolated-render tests from throwing) and when no user resolved for
|
|
22
|
+
* the request.
|
|
23
|
+
*/
|
|
24
|
+
export function useCurrentUser() {
|
|
25
|
+
return useContext(CurrentUserContext);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=CurrentUserContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CurrentUserContext.js","sourceRoot":"","sources":["../../src/react/CurrentUserContext.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAkB,MAAM,OAAO,CAAA;AAkBjE,MAAM,kBAAkB,GAAG,aAAa,CAAqB,IAAI,CAAC,CAAA;AAElE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,GAIT;IACC,OAAO,KAAC,kBAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA+B,CAAA;AAC5F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAA;AACvC,CAAC"}
|
|
@@ -62,11 +62,18 @@ export interface TextBinding {
|
|
|
62
62
|
* - `subscribe(fn)` registers a listener that fires when REMOTE
|
|
63
63
|
* changes land; `fn(snapshot)` receives the full updated map.
|
|
64
64
|
* The provider re-applies this snapshot onto its React state.
|
|
65
|
-
* - `getTextBinding(name)` (Phase F.6)
|
|
66
|
-
* handle for
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
65
|
+
* - `getTextBinding(name)` (Phase F.6) was the per-field `Y.Text`
|
|
66
|
+
* handle for character-level CRDT on top-level text inputs. After
|
|
67
|
+
* the Tiptap-backed text-collab swap (Phase D, 2026-05), bindings
|
|
68
|
+
* are expected to return `null` for every top-level field —
|
|
69
|
+
* character-level CRDT now lives in the Tiptap renderer's
|
|
70
|
+
* `Collaboration` extension (`Y.XmlFragment` per field). Returning
|
|
71
|
+
* a non-null `TextBinding` here is still supported for legacy
|
|
72
|
+
* bindings, but the active `@pilotiq-pro/collab` impl no longer
|
|
73
|
+
* allocates `Y.Text` for top-level fields. Row-text leaves under
|
|
74
|
+
* Repeater / Builder continue to route through Y.Text via
|
|
75
|
+
* `getRowTextBinding` — the Tiptap-backed renderer only handles
|
|
76
|
+
* top-level (non-dotted-path) field names today.
|
|
70
77
|
* - `destroy()` is called on unmount — gives the plugin a chance to
|
|
71
78
|
* remove its CRDT observer. Implementations are expected to cascade
|
|
72
79
|
* into every `TextBinding` they issued.
|
|
@@ -81,15 +88,150 @@ export interface FormCollabBinding {
|
|
|
81
88
|
set(name: string, value: unknown): void;
|
|
82
89
|
/** Subscribe to remote changes. Returns an unsubscribe function. */
|
|
83
90
|
subscribe(fn: (snapshot: Record<string, unknown>) => void): () => void;
|
|
84
|
-
/** Phase F.6 — per-field text-CRDT handle.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
91
|
+
/** Phase F.6 / Phase D — per-field text-CRDT handle. Post Phase D,
|
|
92
|
+
* `@pilotiq-pro/collab` returns `null` for every top-level text
|
|
93
|
+
* field (character-level CRDT moved into `@pilotiq/tiptap`'s
|
|
94
|
+
* `CollabTextRenderer`, which mounts its own `Y.XmlFragment` per
|
|
95
|
+
* field via the `Collaboration` extension). The method is preserved
|
|
96
|
+
* on the contract for legacy bindings and for the row-text leaves
|
|
97
|
+
* routed through `getRowTextBinding` (under Repeater / Builder).
|
|
98
|
+
* Optional so existing F1-era plugins keep type-checking without a
|
|
99
|
+
* no-op stub; absent reads as "binding has no top-level text CRDT
|
|
100
|
+
* surface," same as returning `null` for every field. */
|
|
89
101
|
getTextBinding?(name: string): TextBinding | null;
|
|
90
102
|
/** Cleanup hook called when the form unmounts. */
|
|
91
103
|
destroy(): void;
|
|
104
|
+
/**
|
|
105
|
+
* Add a row to a Repeater or Builder field. `rowId` is the stable
|
|
106
|
+
* `__id` the renderer already mints (UUID for new rows / DB PK for
|
|
107
|
+
* relationship-backed rows) — the binding indexes the row's CRDT
|
|
108
|
+
* surface by this id so concurrent inserts from peers both survive.
|
|
109
|
+
*
|
|
110
|
+
* Idempotent — calling with a `rowId` that already exists in the
|
|
111
|
+
* Repeater's array is a no-op. `initial` may carry pre-filled values
|
|
112
|
+
* for the new row (e.g. the inner fields' `default()` from schema
|
|
113
|
+
* resolution); the binding seeds them onto the row's storage under
|
|
114
|
+
* the same idempotent posture as top-level `set` (first writer wins
|
|
115
|
+
* across concurrent first-mounters).
|
|
116
|
+
*
|
|
117
|
+
* Renderer call sites: `RepeaterInput.addRow()` + `BuilderInput.addBlock()`.
|
|
118
|
+
* When absent, F.5 row-level CRDT degrades to v1 behaviour (whole-array
|
|
119
|
+
* LWW under the top-level Y.Map).
|
|
120
|
+
*/
|
|
121
|
+
addRow?(arrayName: string, rowId: string, initial: Record<string, unknown>): void;
|
|
122
|
+
/**
|
|
123
|
+
* Remove a row from a Repeater/Builder field by stable id. Idempotent
|
|
124
|
+
* — a missing `rowId` is a no-op. Triggered by the renderer's row
|
|
125
|
+
* delete button.
|
|
126
|
+
*/
|
|
127
|
+
removeRow?(arrayName: string, rowId: string): void;
|
|
128
|
+
/**
|
|
129
|
+
* Apply a new row order. `newOrder` is the full list of row ids in
|
|
130
|
+
* their final positions; the binding computes the minimal CRDT move
|
|
131
|
+
* sequence and applies it inside a single transaction (peers see one
|
|
132
|
+
* coalesced update, not N intermediate states).
|
|
133
|
+
*
|
|
134
|
+
* Called by drag-and-drop / up-down move handlers in
|
|
135
|
+
* `RepeaterInput` + `BuilderInput`.
|
|
136
|
+
*/
|
|
137
|
+
reorderRows?(arrayName: string, newOrder: string[]): void;
|
|
138
|
+
/**
|
|
139
|
+
* Write a single field on a row. Replaces the dotted-path `set` for
|
|
140
|
+
* row leaves (`tags.0.label` → `setRow('tags', rowId, 'label', value)`).
|
|
141
|
+
*
|
|
142
|
+
* Binding routes by allowlist same as top-level `set`: text-shaped
|
|
143
|
+
* fields go through the row's `TextBinding` when a `Y.Text` exists
|
|
144
|
+
* (see `getRowTextBinding`); non-text fields land on the row's
|
|
145
|
+
* scalar field-map under LWW.
|
|
146
|
+
*
|
|
147
|
+
* `FormStateProvider` calls this on every local edit when both a
|
|
148
|
+
* dotted-path name is being written AND `setRow` is implemented;
|
|
149
|
+
* otherwise the v1 skip-on-dot path runs.
|
|
150
|
+
*/
|
|
151
|
+
setRow?(arrayName: string, rowId: string, fieldName: string, value: unknown): void;
|
|
152
|
+
/**
|
|
153
|
+
* Per-row text-CRDT handle. Composes with F.6's `BoundTextInput`:
|
|
154
|
+
* the renderer asks for one of these when a row leaf is text-shaped
|
|
155
|
+
* AND not opted out via `.collab(false)`, then threads it through
|
|
156
|
+
* the existing `TextBinding` plumbing inside the row.
|
|
157
|
+
*
|
|
158
|
+
* Returns `null` for non-text fields, fields opted out via collab,
|
|
159
|
+
* rows not yet present in the binding's index (e.g. before
|
|
160
|
+
* `addRow` has propagated), or when the binding doesn't yet
|
|
161
|
+
* implement per-row text CRDT (deferred to F.5c).
|
|
162
|
+
*/
|
|
163
|
+
getRowTextBinding?(arrayName: string, rowId: string, fieldName: string): TextBinding | null;
|
|
164
|
+
/**
|
|
165
|
+
* Subscribe to row-lifecycle events for a Repeater/Builder array.
|
|
166
|
+
* Fires on remote add / remove / move; the renderer reconciles its
|
|
167
|
+
* `rows` state by `__id`. Local mutations fire too — the renderer
|
|
168
|
+
* deduplicates by checking whether the event's `rowId` matches one
|
|
169
|
+
* it just dispatched itself (the binding's roundtrip + the
|
|
170
|
+
* renderer's optimistic update converge).
|
|
171
|
+
*/
|
|
172
|
+
subscribeRows?(arrayName: string, fn: (event: RowsEvent) => void): () => void;
|
|
92
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Phase F.5 — array-scoped imperative API exposed to Repeater / Builder
|
|
176
|
+
* renderers through `useRowBinding(arrayName)`. Each method's first arg
|
|
177
|
+
* (`arrayName`) is pre-bound by the hook so call sites read naturally.
|
|
178
|
+
*
|
|
179
|
+
* Returned by the hook only when the active binding implements all four
|
|
180
|
+
* F.5 row methods (`addRow + removeRow + reorderRows`); partial impls
|
|
181
|
+
* read as null so renderers can do a single existence check before
|
|
182
|
+
* proceeding. `setRow` is intentionally NOT exposed here — it's invoked
|
|
183
|
+
* implicitly via `FormStateProvider.setValue`'s dotted-path routing,
|
|
184
|
+
* so renderers never need to touch it directly.
|
|
185
|
+
*/
|
|
186
|
+
export interface RowBindingApi {
|
|
187
|
+
/** Register a new row in the array's CRDT surface. `initial` may
|
|
188
|
+
* carry pre-seeded values (e.g. clone of a sibling); pass `{}` (or
|
|
189
|
+
* omit) for empty rows. Idempotent under existing rowId. */
|
|
190
|
+
add(rowId: string, initial?: Record<string, unknown>): void;
|
|
191
|
+
/** Remove the row with the given id. Idempotent for unknown ids. */
|
|
192
|
+
remove(rowId: string): void;
|
|
193
|
+
/** Replace the array's row order. `newOrder` is the full list of row
|
|
194
|
+
* ids in their post-reorder positions; the binding emits the minimal
|
|
195
|
+
* CRDT move sequence in one transaction so peers observe a single
|
|
196
|
+
* coalesced update. */
|
|
197
|
+
reorder(newOrder: string[]): void;
|
|
198
|
+
/** Subscribe to remote row-lifecycle events on this array. Returns
|
|
199
|
+
* an unsubscribe fn the renderer's `useEffect` cleanup hangs on.
|
|
200
|
+
* Local mutations may also surface here (Yjs observers fire on
|
|
201
|
+
* local transactions); the renderer is expected to dedupe by
|
|
202
|
+
* rowId presence in its current state. Optional on the underlying
|
|
203
|
+
* contract — `null` when the binding lacks `subscribeRows`. */
|
|
204
|
+
subscribe(fn: (event: RowsEvent) => void): () => void;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Phase F.5 — row-lifecycle event surfaced through `subscribeRows`.
|
|
208
|
+
*
|
|
209
|
+
* - `add` — a new row was inserted at `index`. `values` carries
|
|
210
|
+
* the row's seeded field values (mirrors `addRow.initial`).
|
|
211
|
+
* - `remove` — a row was removed; `index` is its position at the
|
|
212
|
+
* moment of removal.
|
|
213
|
+
* - `move` — a row shifted positions. `from` and `to` are 0-based
|
|
214
|
+
* indices in the post-reorder layout (i.e. the row at
|
|
215
|
+
* position `from` ended up at `to`).
|
|
216
|
+
*
|
|
217
|
+
* Pilotiq core stays Yjs-free — the binding impl in `@pilotiq-pro/collab`
|
|
218
|
+
* translates `Y.Array<Y.Map>` `observe` deltas into these events.
|
|
219
|
+
*/
|
|
220
|
+
export type RowsEvent = {
|
|
221
|
+
kind: 'add';
|
|
222
|
+
rowId: string;
|
|
223
|
+
index: number;
|
|
224
|
+
values: Record<string, unknown>;
|
|
225
|
+
} | {
|
|
226
|
+
kind: 'remove';
|
|
227
|
+
rowId: string;
|
|
228
|
+
index: number;
|
|
229
|
+
} | {
|
|
230
|
+
kind: 'move';
|
|
231
|
+
rowId: string;
|
|
232
|
+
from: number;
|
|
233
|
+
to: number;
|
|
234
|
+
};
|
|
93
235
|
export interface FormCollabBindingFactoryArgs {
|
|
94
236
|
/** Active collab room — provides `ydoc`, `provider`, `user`. Opaque to pilotiq core. */
|
|
95
237
|
room: CollabRoom;
|
|
@@ -110,13 +252,15 @@ export interface FormCollabBindingFactoryArgs {
|
|
|
110
252
|
initial: Record<string, unknown>;
|
|
111
253
|
/**
|
|
112
254
|
* Phase F.6 — initial form meta from the server. The binding walks
|
|
113
|
-
* this once at construction to
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
255
|
+
* this once at construction to index Repeater / Builder array names
|
|
256
|
+
* for row-level CRDT and to identify which row leaves are text-shaped
|
|
257
|
+
* (`fieldType ∈ { text, textarea, email, slug, markdown }` and not
|
|
258
|
+
* `.collab(false)`). Post Phase D, top-level text fields no longer
|
|
259
|
+
* need to be partitioned here — their character-level CRDT lives in
|
|
260
|
+
* the Tiptap renderer's `Y.XmlFragment`, not in a binding-allocated
|
|
261
|
+
* `Y.Text`. The meta is captured at mount; later structural changes
|
|
262
|
+
* from `live()` re-resolves aren't re-walked (rare in practice —
|
|
263
|
+
* dynamic field add/remove is an F-followup).
|
|
120
264
|
*/
|
|
121
265
|
formMeta: ElementMeta;
|
|
122
266
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormCollabBindingRegistry.d.ts","sourceRoot":"","sources":["../../src/react/FormCollabBindingRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAG,MAAM,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,IAAQ,MAAM,CAAA;IAClB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAClC,OAAO,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAC/C,OAAO,IAAK,IAAI,CAAA;CACjB;AAED
|
|
1
|
+
{"version":3,"file":"FormCollabBindingRegistry.d.ts","sourceRoot":"","sources":["../../src/react/FormCollabBindingRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormCollabBindingRegistry.js","sourceRoot":"","sources":["../../src/react/FormCollabBindingRegistry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FormCollabBindingRegistry.js","sourceRoot":"","sources":["../../src/react/FormCollabBindingRegistry.ts"],"names":[],"mappings":"AAsQA,IAAI,QAAQ,GAAoC,IAAI,CAAA;AAEpD;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAiC;IACzE,QAAQ,GAAG,OAAO,CAAA;AACpB,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,oBAAoB;IAClC,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|