Idea
v0.1 ships multi-tab conflict detection (`multiTab: 'warn'`) and exposes `onConflictData` + `resolveConflict('local' | 'remote' | merged)`. But the actual merge UI — showing per-field diffs, letting the user pick field-by-field which version to keep — is left to the consumer.
This results in every app reinventing the same UI pattern. v0.2 should ship reusable UI helpers.
Proposed
A headless `` component and a `useConflictResolver` hook:
```tsx
import { ConflictResolver } from 'formdraft/ui';
{draft.onConflictData && (
<ConflictResolver
local={draft.values}
remote={draft.onConflictData}
onResolve={draft.resolveConflict}
renderField={(name, localValue, remoteValue, onPickLocal, onPickRemote) => (
{name}
Mine: {String(localValue)}
Theirs: {String(remoteValue)}
)}
/>
)}
```
Or fully styled default `` for "drop in and done":
```tsx
import { ConflictDialog } from 'formdraft/ui';
\`\`\`
Implementation notes
- Headless variant: just orchestration. Consumer brings their own markup via render props.
- Styled variant: minimal default CSS. Could ship in `formdraft/ui` subpath to avoid coupling main bundle to UI code.
- Diff algorithm: shallow object diff for v0.2 (deep diff later). For string fields, optionally do char-level diff for visualization.
Acceptance
- New subpath export `formdraft/ui` with `ConflictResolver` (headless) and `ConflictDialog` (styled)
- 5+ unit tests covering: render fields with diff, pick local, pick remote, mixed merge, no conflict
- Bundle: `formdraft/ui` is separate chunk; main bundle stays ≤ 8 KB
- README new section "Conflict UI" with both variants demonstrated
Related to v0.1 design
Spec §10 lists "Full field-level conflict merge UI helpers" as v0.2 scope. This issue is the v0.2 implementation of that line.
Idea
v0.1 ships multi-tab conflict detection (`multiTab: 'warn'`) and exposes `onConflictData` + `resolveConflict('local' | 'remote' | merged)`. But the actual merge UI — showing per-field diffs, letting the user pick field-by-field which version to keep — is left to the consumer.
This results in every app reinventing the same UI pattern. v0.2 should ship reusable UI helpers.
Proposed
A headless `` component and a `useConflictResolver` hook:
```tsx
import { ConflictResolver } from 'formdraft/ui';
{draft.onConflictData && (
<ConflictResolver
local={draft.values}
remote={draft.onConflictData}
onResolve={draft.resolveConflict}
renderField={(name, localValue, remoteValue, onPickLocal, onPickRemote) => (
{name}
Mine: {String(localValue)}
Theirs: {String(remoteValue)}
)}
/>
)}
```
Or fully styled default `` for "drop in and done":
```tsx
\`\`\`import { ConflictDialog } from 'formdraft/ui';
Implementation notes
Acceptance
Related to v0.1 design
Spec §10 lists "Full field-level conflict merge UI helpers" as v0.2 scope. This issue is the v0.2 implementation of that line.