fix(crdt): sync groups and bgConfig via Yjs (Issue #6)

This commit is contained in:
2026-06-29 23:42:03 +02:00
parent cf62a777d2
commit 8493138b4b
3 changed files with 167 additions and 4 deletions
+64 -2
View File
@@ -8,6 +8,8 @@ import { YjsDocument } from './YjsDocument';
import { WebSocketProvider, type ConnectionStatus } from './WebSocketProvider';
import { AwarenessManager, type UserCursor, type UserSelection } from './AwarenessManager';
import type { CADElement, CADLayer, BlockDefinition } from '../types/cad.types';
import type { ElementGroup } from '../tools/modification/GroupTool';
import type { BackgroundConfig } from '../services/backgroundService';
export interface UseYjsBindingOptions {
docName: string;
@@ -27,6 +29,8 @@ export interface UseYjsBindingResult {
elements: CADElement[];
layers: CADLayer[];
blocks: BlockDefinition[];
groups: ElementGroup[];
bgConfigs: BackgroundConfig[];
cursors: UserCursor[];
selections: UserSelection[];
setElement: (el: CADElement) => void;
@@ -35,7 +39,17 @@ export interface UseYjsBindingResult {
deleteLayer: (id: string) => void;
setBlock: (block: BlockDefinition) => void;
deleteBlock: (id: string) => void;
loadFromState: (state: { elements: CADElement[]; layers: CADLayer[]; blocks: BlockDefinition[] }) => void;
setGroup: (group: ElementGroup) => void;
deleteGroup: (id: string) => void;
setBgConfig: (id: string, config: BackgroundConfig) => void;
deleteBgConfig: (id: string) => void;
loadFromState: (state: {
elements: CADElement[];
layers: CADLayer[];
blocks: BlockDefinition[];
groups?: ElementGroup[];
bgConfigs?: BackgroundConfig[];
}) => void;
setCursor: (x: number, y: number) => void;
hideCursor: () => void;
setSelection: (elementIds: string[]) => void;
@@ -56,6 +70,8 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
const [elements, setElements] = useState<CADElement[]>([]);
const [layers, setLayers] = useState<CADLayer[]>([]);
const [blocks, setBlocks] = useState<BlockDefinition[]>([]);
const [groups, setGroups] = useState<ElementGroup[]>([]);
const [bgConfigs, setBgConfigs] = useState<BackgroundConfig[]>([]);
const [cursors, setCursors] = useState<UserCursor[]>([]);
const [selections, setSelections] = useState<UserSelection[]>([]);
@@ -82,6 +98,8 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
setElements(doc.getElements());
setLayers(doc.getLayers());
setBlocks(doc.getBlocks());
setGroups(doc.getGroups());
setBgConfigs(doc.getBgConfigs());
setCursors(awareness.getAllCursors());
setSelections(awareness.getAllSelections());
};
@@ -89,6 +107,20 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
const unbindDoc = doc.onChange(syncState);
const unbindAwareness = awareness.onChange(syncState);
// Dedicated observers for groups and bgConfig maps
const groupsMap = doc.data.groups;
const bgConfigMap = doc.data.bgConfig;
const groupsObserver = () => {
setGroups(Array.from(groupsMap.values()));
};
const bgConfigObserver = () => {
setBgConfigs(Array.from(bgConfigMap.values()));
};
groupsMap.observe(groupsObserver);
bgConfigMap.observe(bgConfigObserver);
// Forward local updates to server
const unbindLocal = provider.bindLocalUpdates();
@@ -98,6 +130,8 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
// Cleanup
return () => {
groupsMap.unobserve(groupsObserver);
bgConfigMap.unobserve(bgConfigObserver);
unbindDoc();
unbindAwareness();
unbindLocal();
@@ -135,7 +169,29 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
yjsDocRef.current?.deleteBlock(id);
}, []);
const loadFromState = useCallback((state: { elements: CADElement[]; layers: CADLayer[]; blocks: BlockDefinition[] }) => {
const setGroup = useCallback((group: ElementGroup) => {
yjsDocRef.current?.setGroup(group);
}, []);
const deleteGroup = useCallback((id: string) => {
yjsDocRef.current?.deleteGroup(id);
}, []);
const setBgConfig = useCallback((id: string, config: BackgroundConfig) => {
yjsDocRef.current?.setBgConfig(id, config);
}, []);
const deleteBgConfig = useCallback((id: string) => {
yjsDocRef.current?.deleteBgConfig(id);
}, []);
const loadFromState = useCallback((state: {
elements: CADElement[];
layers: CADLayer[];
blocks: BlockDefinition[];
groups?: ElementGroup[];
bgConfigs?: BackgroundConfig[];
}) => {
yjsDocRef.current?.loadFromState(state);
}, []);
@@ -163,6 +219,8 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
elements,
layers,
blocks,
groups,
bgConfigs,
cursors,
selections,
setElement,
@@ -171,6 +229,10 @@ export function useYjsBinding(opts: UseYjsBindingOptions): UseYjsBindingResult {
deleteLayer,
setBlock,
deleteBlock,
setGroup,
deleteGroup,
setBgConfig,
deleteBgConfig,
loadFromState,
setCursor,
hideCursor,