openwriter 0.13.0 → 0.15.0
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/dist/client/assets/index-B3iORmCT.css +1 -0
- package/dist/client/assets/index-B5MXw2pg.js +212 -0
- package/dist/client/index.html +2 -2
- package/dist/server/comments.js +256 -0
- package/dist/server/documents.js +71 -29
- package/dist/server/helpers.js +63 -8
- package/dist/server/index.js +96 -45
- package/dist/server/logger.js +246 -0
- package/dist/server/markdown-parse.js +144 -5
- package/dist/server/markdown-serialize.js +214 -30
- package/dist/server/markdown.js +32 -0
- package/dist/server/mcp.js +289 -77
- package/dist/server/node-blocks.js +274 -0
- package/dist/server/node-fingerprint.js +264 -0
- package/dist/server/node-matcher.js +616 -0
- package/dist/server/node-sync-check.js +110 -0
- package/dist/server/pending-overlay.js +845 -0
- package/dist/server/state.js +1139 -110
- package/dist/server/versions.js +18 -0
- package/dist/server/workspaces.js +15 -0
- package/dist/server/ws.js +184 -37
- package/package.json +1 -1
- package/skill/SKILL.md +31 -19
- package/dist/client/assets/index-BlLnLdoc.js +0 -212
- package/dist/client/assets/index-OV13QtgQ.css +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync observer — detects when TipTap state and markdown body have drifted
|
|
3
|
+
* out of structural alignment.
|
|
4
|
+
*
|
|
5
|
+
* The two views of a document MUST describe the same shape:
|
|
6
|
+
* - The TipTap tree (what the editor renders and the agent operates on)
|
|
7
|
+
* - The markdown body (what gets persisted to disk)
|
|
8
|
+
*
|
|
9
|
+
* If a save/load cycle produces a different shape than it started with,
|
|
10
|
+
* a node has been misapplied — content ended up attached to the wrong
|
|
11
|
+
* block, or a structural element was added/dropped during the round-trip.
|
|
12
|
+
*
|
|
13
|
+
* The shape is a flat ordered list of (type, charCount, sentenceCount)
|
|
14
|
+
* per block. Comparing two shapes by position immediately localizes any
|
|
15
|
+
* drift to the exact block where the round-trip broke.
|
|
16
|
+
*
|
|
17
|
+
* Wire points:
|
|
18
|
+
* - Save-time: after serialize, re-parse and confirm shape preserved.
|
|
19
|
+
* - Load-time: after parse, compare body shape to TipTap-tree shape.
|
|
20
|
+
*
|
|
21
|
+
* Output is logged (not thrown) so saves and loads still complete; the
|
|
22
|
+
* report tells the consumer / operator exactly where to look.
|
|
23
|
+
*
|
|
24
|
+
* adr: adr/node-identity-matcher.md
|
|
25
|
+
*/
|
|
26
|
+
import { splitSentences } from './node-fingerprint.js';
|
|
27
|
+
import { tiptapToBlocks } from './node-blocks.js';
|
|
28
|
+
/** Reduce a block list to its structural signature. */
|
|
29
|
+
export function computeShape(blocks) {
|
|
30
|
+
return blocks.map((b) => ({
|
|
31
|
+
type: b.type,
|
|
32
|
+
charCount: (b.text || '').length,
|
|
33
|
+
sentenceCount: splitSentences(b.text || '').length,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/** Compute the shape signature of a TipTap document. */
|
|
37
|
+
export function shapeOfTiptap(doc) {
|
|
38
|
+
return computeShape(tiptapToBlocks(doc));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Compare two shapes. Returns ok=true if every position aligns.
|
|
42
|
+
* mismatches[] lists every position where the shapes disagree — the first
|
|
43
|
+
* entry is typically the root cause; the rest are downstream consequences.
|
|
44
|
+
*/
|
|
45
|
+
export function compareShapes(expected, actual) {
|
|
46
|
+
const mismatches = [];
|
|
47
|
+
const maxLen = Math.max(expected.length, actual.length);
|
|
48
|
+
for (let i = 0; i < maxLen; i++) {
|
|
49
|
+
const e = expected[i] ?? null;
|
|
50
|
+
const a = actual[i] ?? null;
|
|
51
|
+
if (!e || !a) {
|
|
52
|
+
mismatches.push({
|
|
53
|
+
position: i,
|
|
54
|
+
expected: e,
|
|
55
|
+
actual: a,
|
|
56
|
+
reason: !e ? 'extra block in actual' : 'missing block in actual',
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (e.type !== a.type) {
|
|
61
|
+
mismatches.push({
|
|
62
|
+
position: i,
|
|
63
|
+
expected: e,
|
|
64
|
+
actual: a,
|
|
65
|
+
reason: `type mismatch: expected ${e.type}, got ${a.type}`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else if (e.charCount !== a.charCount) {
|
|
69
|
+
mismatches.push({
|
|
70
|
+
position: i,
|
|
71
|
+
expected: e,
|
|
72
|
+
actual: a,
|
|
73
|
+
reason: `charCount mismatch: expected ${e.charCount}, got ${a.charCount}`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (e.sentenceCount !== a.sentenceCount) {
|
|
77
|
+
mismatches.push({
|
|
78
|
+
position: i,
|
|
79
|
+
expected: e,
|
|
80
|
+
actual: a,
|
|
81
|
+
reason: `sentenceCount mismatch: expected ${e.sentenceCount}, got ${a.sentenceCount}`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
ok: mismatches.length === 0,
|
|
87
|
+
expectedLength: expected.length,
|
|
88
|
+
actualLength: actual.length,
|
|
89
|
+
mismatches,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Format a sync report for human-readable logging. Returns a multi-line string
|
|
94
|
+
* pointing at the first mismatch and counting any cascade.
|
|
95
|
+
*/
|
|
96
|
+
export function formatSyncReport(report, context) {
|
|
97
|
+
if (report.ok)
|
|
98
|
+
return `[sync-check ${context}] OK (${report.expectedLength} blocks aligned)`;
|
|
99
|
+
const first = report.mismatches[0];
|
|
100
|
+
const lines = [
|
|
101
|
+
`[sync-check ${context}] FAIL: ${report.mismatches.length} mismatch(es) across ${report.expectedLength}/${report.actualLength} blocks`,
|
|
102
|
+
` first mismatch at position ${first.position}: ${first.reason}`,
|
|
103
|
+
` expected: ${first.expected ? JSON.stringify(first.expected) : 'none'}`,
|
|
104
|
+
` actual: ${first.actual ? JSON.stringify(first.actual) : 'none'}`,
|
|
105
|
+
];
|
|
106
|
+
if (report.mismatches.length > 1) {
|
|
107
|
+
lines.push(` (${report.mismatches.length - 1} more mismatch(es) — likely cascade from first)`);
|
|
108
|
+
}
|
|
109
|
+
return lines.join('\n');
|
|
110
|
+
}
|