refactor: move document transcription, annotation, viewer sub-packages
- transcription/: TranscriptionBlock, Column, EditView, PanelHeader, ReadView, Section + transcriptionMarkers, blockConflictMerge, saveBlockWithConflictRetry + useBlockAutoSave, useBlockDragDrop hooks - annotation/: AnnotationLayer, AnnotationShape, AnnotationEditOverlay - viewer/: PdfViewer, PdfControls + useFileLoader, usePdfRenderer hooks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import type { PersonMention, TranscriptionBlockData } from '$lib/types';
|
||||
|
||||
/**
|
||||
* Sentinel thrown by saveBlockWithConflictRetry after a 409 rename-mid-edit
|
||||
* has been merged into local state. Surfaces to the autosave hook as an
|
||||
* error (so the UI shows the retry indicator), but distinguishable from a
|
||||
* genuine network failure via the code. Carries the merged block snapshot
|
||||
* on its `merged` property so the caller can update local state without
|
||||
* a second roundtrip.
|
||||
*/
|
||||
export class BlockConflictResolvedError extends Error {
|
||||
readonly code = 'CONFLICT_RESOLVED' as const;
|
||||
merged?: TranscriptionBlockData;
|
||||
constructor(blockId: string) {
|
||||
super(
|
||||
`Block ${blockId} was rebased onto the latest server snapshot — retry to save the merged result`
|
||||
);
|
||||
this.name = 'BlockConflictResolvedError';
|
||||
}
|
||||
}
|
||||
|
||||
type MergeArgs = {
|
||||
serverBlock: TranscriptionBlockData;
|
||||
localText: string;
|
||||
localMentions: PersonMention[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a 409-Conflict from the server by combining the latest server
|
||||
* snapshot with the transcriber's unsaved local edits (B12b).
|
||||
*
|
||||
* Rules:
|
||||
* - The transcriber's typed text always wins — never overwrite their input.
|
||||
* - Server is the source of truth for the displayName of any person it
|
||||
* knows about; renames that just landed on the server replace stale local
|
||||
* names by personId.
|
||||
* - Local-only mentions added since the last save are preserved.
|
||||
* - All non-mention fields (version, sortOrder, reviewed, updatedAt, ...)
|
||||
* come from the server snapshot so the next save sends the current
|
||||
* revision and matches the latest persisted state.
|
||||
*/
|
||||
export function mergeBlockOnConflict(args: MergeArgs): TranscriptionBlockData {
|
||||
const serverIds = new Set(args.serverBlock.mentionedPersons.map((m) => m.personId));
|
||||
const localOnly = args.localMentions.filter((m) => !serverIds.has(m.personId));
|
||||
return {
|
||||
...args.serverBlock,
|
||||
text: args.localText,
|
||||
mentionedPersons: [...args.serverBlock.mentionedPersons, ...localOnly]
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user