milkdown-inline-diff 1.0.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/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # milkdown-inline-diff
2
+
3
+ Block-level Markdown diff and merge controls for Milkdown editors.
4
+
5
+ ![milkdown-inline-diff demo](./docs/demo-page.png)
6
+
7
+ ## Overview
8
+
9
+ `milkdown-inline-diff` compares Markdown documents as block structures instead of raw text lines. It is designed for in-editor review flows where users need to inspect and merge changes without leaving the editor.
10
+
11
+ It works well for:
12
+
13
+ - `heading`
14
+ - `paragraph`
15
+ - `blockquote`
16
+ - nested `blockquote`
17
+ - `list`
18
+ - nested `list`
19
+ - `table`
20
+ - `code block`
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pnpm add milkdown-inline-diff
26
+ ```
27
+
28
+ You also need the normal Milkdown editor dependencies used by your app.
29
+
30
+ ## Quick Start
31
+
32
+ ```ts
33
+ import { Editor } from "@milkdown/core";
34
+ import { commonmark } from "@milkdown/preset-commonmark";
35
+ import {
36
+ diffConfig,
37
+ diffPlugins,
38
+ } from "milkdown-inline-diff";
39
+ import "milkdown-inline-diff/style.css";
40
+
41
+ const editor = Editor.make()
42
+ .use(commonmark)
43
+ .use(diffPlugins)
44
+ .config(
45
+ diffConfig({
46
+ acceptButtonTitle: "Accept Change",
47
+ rejectButtonTitle: "Keep Original",
48
+ originContent: "# Original",
49
+ modifiedContent: "# Modified",
50
+ }),
51
+ );
52
+
53
+ await editor.create();
54
+ ```
55
+
56
+ If both `originContent` and `modifiedContent` are provided, the plugin will automatically enter diff mode after the editor view is ready.
57
+
58
+ ## Public API
59
+
60
+ ### `diffPlugins`
61
+
62
+ Milkdown plugin array used with:
63
+
64
+ ```ts
65
+ editor.use(diffPlugins);
66
+ ```
67
+
68
+ ### `diffConfig(options)`
69
+
70
+ Configuration helper used with:
71
+
72
+ ```ts
73
+ editor.config(diffConfig({ ... }));
74
+ ```
75
+
76
+ ```ts
77
+ interface DiffConfig {
78
+ acceptButtonTitle?: string;
79
+ rejectButtonTitle?: string;
80
+ originContent?: string;
81
+ modifiedContent?: string;
82
+ }
83
+ ```
84
+
85
+ ### `diff(ctx, modifiedContent, originContent?)`
86
+
87
+ Enters diff mode on demand.
88
+
89
+ ```ts
90
+ editor.action((ctx) => {
91
+ diff(ctx, modifiedContent, originContent);
92
+ });
93
+ ```
94
+
95
+ Use this when you want to trigger comparison after editor creation, after loading remote content, or after switching between document versions.
96
+
97
+ ### `jumpTo(ctx, index)`
98
+
99
+ Moves focus to a specific diff group.
100
+
101
+ ```ts
102
+ editor.action((ctx) => {
103
+ jumpTo(ctx, 0);
104
+ });
105
+ ```
106
+
107
+ This is useful for custom navigation bars, side panels, or review workflows.
108
+
109
+ ### `merge(ctx, action, index, all?)`
110
+
111
+ Accepts or rejects a diff group, or resolves all groups at once.
112
+
113
+ ```ts
114
+ editor.action((ctx) => {
115
+ merge(ctx, "accept", 0);
116
+ });
117
+
118
+ editor.action((ctx) => {
119
+ merge(ctx, "reject", 0, true);
120
+ });
121
+ ```
122
+
123
+ ### `getDiffState(ctx)`
124
+
125
+ Returns the current diff state for external UI.
126
+
127
+ ```ts
128
+ const state = editor.action((ctx) => getDiffState(ctx));
129
+ ```
130
+
131
+ The returned state includes:
132
+
133
+ - `count`: total number of diff groups
134
+ - `currentIndex`: current focused diff group index, or `-1` before the first group
135
+
136
+ This is the function to use when building your own merge bar, status panel, or conflict navigator.
137
+
138
+ ## Typical Review Flow
139
+
140
+ ```ts
141
+ editor.use(diffPlugins).config(
142
+ diffConfig({
143
+ acceptButtonTitle: "Accept Change",
144
+ rejectButtonTitle: "Keep Original",
145
+ }),
146
+ );
147
+
148
+ editor.action((ctx) => {
149
+ diff(ctx, modifiedMarkdown, originMarkdown);
150
+ });
151
+
152
+ const state = editor.action((ctx) => getDiffState(ctx));
153
+
154
+ editor.action((ctx) => {
155
+ jumpTo(ctx, state.currentIndex + 1);
156
+ });
157
+
158
+ editor.action((ctx) => {
159
+ merge(ctx, "accept", state.currentIndex);
160
+ });
161
+ ```
162
+
163
+ ## Listening For Changes
164
+
165
+ If you want to keep external UI in sync with the editor, listen to Milkdown or Crepe update events and read diff state inside the callback.
166
+
167
+ ```ts
168
+ const syncDiffState = () => {
169
+ const state = editor.action((ctx) => getDiffState(ctx));
170
+ setDiffState(state);
171
+ };
172
+
173
+ crepe.on((listener) => {
174
+ listener.mounted(syncDiffState);
175
+ listener.updated(syncDiffState);
176
+ listener.selectionUpdated(syncDiffState);
177
+ });
178
+ ```
179
+
180
+ This pattern is used in the React demo to drive the custom merge bar.
181
+
182
+ ## Demo
183
+
184
+ The repository includes a runnable React demo in [examples/react](/Users/binzhang/Documents/falcon/milkdown-inline-diff/examples/react).
185
+
186
+ It demonstrates:
187
+
188
+ - `editor.use(diffPlugins).config(diffConfig(...))`
189
+ - automatic diff on initial content
190
+ - custom tooltip button titles
191
+ - custom merge bar UI driven by `getDiffState(ctx)`
192
+ - `jumpTo` navigation
193
+ - `merge(..., all)` bulk actions
194
+
195
+ ## Styling
196
+
197
+ Import the plugin stylesheet once:
198
+
199
+ ```ts
200
+ import "milkdown-inline-diff/style.css";
201
+ ```
202
+
203
+ You can then override the tooltip and review UI styles in your app theme if needed.
204
+
205
+ ## License
206
+
207
+ MIT
@@ -0,0 +1,11 @@
1
+ import { Ctx } from '@milkdown/ctx';
2
+ export interface DiffConfig {
3
+ acceptButtonTitle?: string;
4
+ rejectButtonTitle?: string;
5
+ originContent?: string;
6
+ modifiedContent?: string;
7
+ }
8
+ export declare const defaultDiffConfig: DiffConfig;
9
+ export declare const diffConfigCtx: import('@milkdown/kit/utils').$Ctx<DiffConfig, "diffInlineConfig">;
10
+ export declare function diffConfig(config?: DiffConfig): (ctx: Ctx) => void;
11
+ //# sourceMappingURL=diff-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-config.d.ts","sourceRoot":"","sources":["../src/diff-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC,MAAM,WAAW,UAAU;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,iBAAiB,EAAE,UAG/B,CAAC;AAEF,eAAO,MAAM,aAAa,oEAGzB,CAAC;AAEF,wBAAgB,UAAU,CAAC,MAAM,GAAE,UAAe,IACxC,KAAK,GAAG,UAMjB"}
@@ -0,0 +1,37 @@
1
+ import { Ctx, MilkdownPlugin } from '@milkdown/ctx';
2
+ import { EditorState, PluginView } from '@milkdown/prose/state';
3
+ import { EditorView, Decoration } from '@milkdown/prose/view';
4
+ export declare const onMergeCtx: import('@milkdown/kit/utils').$Ctx<((action: "accept" | "reject", index: number) => void) | undefined, "onMergeCtx">;
5
+ export declare const diffToolTooltip: import('@milkdown/kit/plugin/tooltip').TooltipPlugin<"MERGE_TOOL", any>;
6
+ export declare class DiffTooltipView implements PluginView {
7
+ private ctx;
8
+ private editView;
9
+ private tooltipProvider;
10
+ private showRect?;
11
+ private shouldShow;
12
+ private currentGroupIndex?;
13
+ private currentPos?;
14
+ private currentDisplayPos?;
15
+ private scrollTarget;
16
+ tooltipElement: HTMLDivElement;
17
+ acceptButton: HTMLButtonElement;
18
+ rejectButton: HTMLButtonElement;
19
+ constructor(ctx: Ctx, editView: EditorView);
20
+ handleScroll: () => void;
21
+ handleAccept(): void;
22
+ handleReject(): void;
23
+ toggleShow(): void;
24
+ show(groupIndex: number, rect: DOMRect, pos?: number): void;
25
+ hide: () => void;
26
+ hideImmediate: () => void;
27
+ isVisible(): boolean;
28
+ update(view: EditorView, _prevState?: EditorState): void;
29
+ destroy: () => void;
30
+ }
31
+ export declare const diffTooltipPlugins: MilkdownPlugin[];
32
+ export declare const tooltipViewConfig: (ctx: Ctx) => void;
33
+ export declare function shouldShowMergeToolForPos(ctx: Ctx, pos: number): {
34
+ index: number;
35
+ decoration: Decoration;
36
+ } | undefined;
37
+ //# sourceMappingURL=diff-tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-tooltip.d.ts","sourceRoot":"","sources":["../src/diff-tooltip.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGzD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAOlD,eAAO,MAAM,UAAU,+CACX,QAAQ,GAAG,QAAQ,SAAS,MAAM,KAAK,IAAI,4BAE7B,CAAC;AAE3B,eAAO,MAAM,eAAe,yEAA+B,CAAC;AAE5D,qBAAa,eAAgB,YAAW,UAAU;IAapC,OAAO,CAAC,GAAG;IAAO,OAAO,CAAC,QAAQ;IAZ9C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,CAAU;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,YAAY,CAA4B;IAEhD,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,iBAAiB,CAAC;IAChC,YAAY,EAAE,iBAAiB,CAAC;gBAEZ,GAAG,EAAE,GAAG,EAAU,QAAQ,EAAE,UAAU;IA+C1D,YAAY,aASV;IAEF,YAAY,IAAI,IAAI;IAMpB,YAAY,IAAI,IAAI;IAMpB,UAAU;IAYV,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM;IAUpD,IAAI,aAGF;IAEF,aAAa,aAIX;IAEF,SAAS,IAAI,OAAO;IAIpB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,WAAW;IAWjD,OAAO,aAML;CACH;AAYD,eAAO,MAAM,kBAAkB,EAAE,cAAc,EAG9C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,KAAK,GAAG,SA8BzC,CAAC;AAoDF,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,MAAM,GACV;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,GAAG,SAAS,CAiBvD"}
@@ -0,0 +1,7 @@
1
+ import { DecorationSet } from '@milkdown/prose/view';
2
+ export interface DiffDecorationState {
3
+ decorations: DecorationSet | null;
4
+ }
5
+ export declare const diffDecorationState: import('@milkdown/kit/utils').$Ctx<DiffDecorationState, "diffDecorationState">;
6
+ export declare const diffDecorationPlugin: import('@milkdown/kit/utils').$Prose;
7
+ //# sourceMappingURL=diffDecorationState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diffDecorationState.d.ts","sourceRoot":"","sources":["../src/diffDecorationState.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,aAAa,GAAG,IAAI,CAAC;CACnC;AAED,eAAO,MAAM,mBAAmB,gFAQ/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,sCA6B/B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const extendedTableSchema: import('@milkdown/kit/utils').$NodeSchema<"table">;
2
+ //# sourceMappingURL=extended-table-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extended-table-schema.d.ts","sourceRoot":"","sources":["../src/extended-table-schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,oDAQ9B,CAAC"}