@1agh/maude 1.5.0 → 1.5.2
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/apps/studio/annotations/ai-write.ts +1 -0
- package/apps/studio/annotations/ops-merge.ts +15 -0
- package/apps/studio/annotations/ops.ts +30 -5
- package/apps/studio/api.ts +1 -0
- package/apps/studio/collab/registry.ts +1 -0
- package/apps/studio/dist/client.bundle.js +1 -1
- package/apps/studio/sync/codec.ts +1 -0
- package/package.json +8 -8
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { anchorPoint, facingAnchor } from './elements/arrow.model.ts';
|
|
22
22
|
import { compareOrder, keyBetween } from './fractional-index.ts';
|
|
23
23
|
import { type ApplyResult, applyOps, type Op } from './ops.ts';
|
|
24
|
+
import './ops-merge.ts';
|
|
24
25
|
import { defOf, REGISTRY, specOf } from './registry.ts';
|
|
25
26
|
import { Scene } from './scene.ts';
|
|
26
27
|
import type { AnnotationElement, Box } from './types.ts';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file annotations/ops-merge.ts — wire the 3-way text merge into ops.ts
|
|
3
|
+
* @scope apps/studio/annotations/ops-merge.ts
|
|
4
|
+
* @purpose Side-effect import for SERVER entry points only (api, collab,
|
|
5
|
+
* codec, ai-write, the hub's accepted lane). `sync/source-merge.ts`
|
|
6
|
+
* needs the `diff` package, which the canvas iframe cannot resolve,
|
|
7
|
+
* so ops.ts takes the merge by injection instead of importing it.
|
|
8
|
+
* Never import this from anything in the canvas-lib graph —
|
|
9
|
+
* `test/canvas-lib-graph.test.ts` fails if `diff` gets back in.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { mergeSource } from '../sync/source-merge.ts';
|
|
13
|
+
import { setTextMerge } from './ops.ts';
|
|
14
|
+
|
|
15
|
+
setTextMerge(mergeSource);
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* • the same scalar field follows acceptance order (last wins —
|
|
14
14
|
* DDR-241 §5);
|
|
15
15
|
* • the same TEXT field is merged 3-way at character level
|
|
16
|
-
* against the patch's `expect` (sync/source-merge.ts
|
|
17
|
-
*
|
|
16
|
+
* against the patch's `expect` (sync/source-merge.ts, wired in
|
|
17
|
+
* by `ops-merge.ts` — see `setTextMerge`), so two people
|
|
18
|
+
* typing in one sticky both keep their words;
|
|
18
19
|
* • a patch on a missing element is rejected `gone` (the client
|
|
19
20
|
* offers to restore — it never silently re-creates).
|
|
20
21
|
* A `strict` patch (undo) only touches fields that still hold the
|
|
@@ -26,7 +27,6 @@
|
|
|
26
27
|
* are not deleted with it (coordinates converted — they don't jump).
|
|
27
28
|
*/
|
|
28
29
|
|
|
29
|
-
import { mergeSource } from '../sync/source-merge.ts';
|
|
30
30
|
import { MAX_NESTING_DEPTH } from './constants.ts';
|
|
31
31
|
import { DANGEROUS_KEYS, jsonEq } from './fields.ts';
|
|
32
32
|
import { defOf, validateElement } from './registry.ts';
|
|
@@ -83,15 +83,40 @@ interface MergeBudget {
|
|
|
83
83
|
left: number;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/** A 3-way text merge: `ok:false` means the edits touch (ours wins). */
|
|
87
|
+
export type TextMerge = (
|
|
88
|
+
base: string,
|
|
89
|
+
ours: string,
|
|
90
|
+
theirs: string
|
|
91
|
+
) => { ok: true; merged: string } | { ok: false };
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The character-level merge, injected rather than imported: it needs the
|
|
95
|
+
* `diff` package, and this module is in the canvas iframe's graph
|
|
96
|
+
* (`annotations-layer.tsx`), where only the prebuilt `/_canvas-runtime/`
|
|
97
|
+
* externals resolve — a static import broke every canvas build in v1.5.0.
|
|
98
|
+
* Server entry points import `ops-merge.ts`, which registers `mergeSource`;
|
|
99
|
+
* without it a same-field text race resolves by acceptance order (ours).
|
|
100
|
+
*/
|
|
101
|
+
let textMerge: TextMerge | null = null;
|
|
102
|
+
|
|
103
|
+
export function setTextMerge(merge: TextMerge | null): void {
|
|
104
|
+
textMerge = merge;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function hasTextMerge(): boolean {
|
|
108
|
+
return textMerge !== null;
|
|
109
|
+
}
|
|
110
|
+
|
|
86
111
|
/** Merge a text value: ours vs theirs against base. Overlap → ours (acceptance order). */
|
|
87
112
|
function mergeText(base: unknown, ours: unknown, theirs: unknown, budget: MergeBudget): unknown {
|
|
88
113
|
if (typeof base !== 'string' || typeof ours !== 'string' || typeof theirs !== 'string')
|
|
89
114
|
return ours;
|
|
90
115
|
if (theirs === base) return ours;
|
|
91
116
|
if (ours === base) return theirs;
|
|
92
|
-
if (budget.left <= 0) return ours;
|
|
117
|
+
if (!textMerge || budget.left <= 0) return ours;
|
|
93
118
|
budget.left--;
|
|
94
|
-
const m =
|
|
119
|
+
const m = textMerge(base, ours, theirs);
|
|
95
120
|
return m.ok ? m.merged : ours;
|
|
96
121
|
}
|
|
97
122
|
|
package/apps/studio/api.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
applyOps as applyAnnotationOpsPure,
|
|
22
22
|
diffToOps,
|
|
23
23
|
} from './annotations/ops.ts';
|
|
24
|
+
import './annotations/ops-merge.ts';
|
|
24
25
|
import { parseBoard, serializeBoard } from './annotations/schema.ts';
|
|
25
26
|
import type { AnnotationElement } from './annotations/types.ts';
|
|
26
27
|
import { createAssetMirror, s3ConfigFromEnv } from './assets-s3.ts';
|
|
@@ -11,6 +11,7 @@ import type * as Y from 'yjs';
|
|
|
11
11
|
|
|
12
12
|
import { canonicalAnnotations } from '../annotations/board-text.ts';
|
|
13
13
|
import { type ApplyResult, applyOps, type Op } from '../annotations/ops.ts';
|
|
14
|
+
import '../annotations/ops-merge.ts';
|
|
14
15
|
import { readReplica, validActionId, writeReplica } from '../annotations/replica.ts';
|
|
15
16
|
import { parseBoard, serializeBoard } from '../annotations/schema.ts';
|
|
16
17
|
import { applyCommentsToDoc, stampAnnotationsEdit } from '../sync/codec.ts';
|