feat: initial commit web-cad-neu with docker-compose, frontend and backend

This commit is contained in:
2026-06-26 10:50:24 +02:00
commit 4ec76fe406
102 changed files with 25722 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
/**
* YjsDocument Manages a Y.Doc with shared maps for CAD elements, layers, and blocks.
* Provides helpers to sync local state with the CRDT document.
*/
import * as Y from 'yjs';
import type { CADElement, CADLayer, BlockDefinition } from '../types/cad.types';
export interface YjsDocumentData {
elements: Y.Map<CADElement>;
layers: Y.Map<CADLayer>;
blocks: Y.Map<BlockDefinition>;
meta: Y.Map<unknown>;
}
export class YjsDocument {
readonly doc: Y.Doc;
readonly data: YjsDocumentData;
constructor() {
this.doc = new Y.Doc();
this.data = {
elements: this.doc.getMap<CADElement>('elements'),
layers: this.doc.getMap<CADLayer>('layers'),
blocks: this.doc.getMap<BlockDefinition>('blocks'),
meta: this.doc.getMap<unknown>('meta'),
};
}
/** Get all elements as a plain array */
getElements(): CADElement[] {
return Array.from(this.data.elements.values());
}
/** Get all layers as a plain array */
getLayers(): CADLayer[] {
return Array.from(this.data.layers.values());
}
/** Get all blocks as a plain array */
getBlocks(): BlockDefinition[] {
return Array.from(this.data.blocks.values());
}
/** Add or update a single element */
setElement(el: CADElement): void {
this.doc.transact(() => {
this.data.elements.set(el.id, el);
});
}
/** Remove an element by id */
deleteElement(id: string): void {
this.data.elements.delete(id);
}
/** Add or update a layer */
setLayer(layer: CADLayer): void {
this.data.layers.set(layer.id, layer);
}
/** Remove a layer by id */
deleteLayer(id: string): void {
this.data.layers.delete(id);
}
/** Add or update a block definition */
setBlock(block: BlockDefinition): void {
this.data.blocks.set(block.id, block);
}
/** Remove a block by id */
deleteBlock(id: string): void {
this.data.blocks.delete(id);
}
/** Bulk-load local state into the Y.Doc (replaces all content) */
loadFromState(state: {
elements: CADElement[];
layers: CADLayer[];
blocks: BlockDefinition[];
}): void {
this.doc.transact(() => {
this.data.elements.clear();
for (const el of state.elements) {
this.data.elements.set(el.id, el);
}
this.data.layers.clear();
for (const layer of state.layers) {
this.data.layers.set(layer.id, layer);
}
this.data.blocks.clear();
for (const block of state.blocks) {
this.data.blocks.set(block.id, block);
}
});
}
/** Export current state as a plain object */
toState(): {
elements: CADElement[];
layers: CADLayer[];
blocks: BlockDefinition[];
} {
return {
elements: this.getElements(),
layers: this.getLayers(),
blocks: this.getBlocks(),
};
}
/** Encode full document state as update binary */
encodeState(): Uint8Array {
return Y.encodeStateAsUpdate(this.doc);
}
/** Apply a remote update binary */
applyUpdate(update: Uint8Array): void {
Y.applyUpdate(this.doc, update);
}
/** Register a callback for document changes */
onChange(callback: () => void): () => void {
this.doc.on('update', callback);
return () => this.doc.off('update', callback);
}
/** Destroy the document */
destroy(): void {
this.doc.destroy();
}
}