@wakastellar/ui 3.3.3 → 3.5.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/badge-BbwO7QeZ.js +1 -0
- package/dist/badge-BfiocODp.mjs +23 -0
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunk-14q5BKub.js +1 -0
- package/dist/{chunk-BH6uBOac.mjs → chunk-Cr9pTUWm.mjs} +5 -5
- package/dist/cn-DEtaFQsA.js +1 -0
- package/dist/cn-DUn6aSIQ.mjs +24 -0
- package/dist/doc.cjs.js +2 -2
- package/dist/doc.es.js +19 -19
- package/dist/editor.cjs.js +48 -0
- package/dist/editor.d.ts +1 -0
- package/dist/editor.es.js +6551 -0
- package/dist/{exceljs.min-DG9M8IZ1.mjs → exceljs.min-DL1XYDll.mjs} +1 -1
- package/dist/{exceljs.min-BuefmDRS.js → exceljs.min-qeIfSCbF.js} +1 -1
- package/dist/export.cjs.js +1 -1
- package/dist/export.es.js +1 -1
- package/dist/index.cjs.js +150 -150
- package/dist/index.es.js +26782 -27591
- package/dist/input-BfaSAGVw.js +1 -0
- package/dist/input-DVr_Qkl8.mjs +14 -0
- package/dist/rich-text.cjs.js +1 -1
- package/dist/rich-text.es.js +1 -1
- package/dist/security-CyBpuklN.mjs +122 -0
- package/dist/security-bFWwDrlg.js +1 -0
- package/dist/separator-NrkltulH.js +1 -0
- package/dist/separator-ibN2mycs.mjs +51 -0
- package/dist/src/components/editor/blocks/index.d.ts +51 -0
- package/dist/src/components/editor/blocks/waka-acceptance-criteria-block.d.ts +60 -0
- package/dist/src/components/editor/blocks/waka-ai-assist-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-api-endpoint-block.d.ts +63 -0
- package/dist/src/components/editor/blocks/waka-code-playground-block.d.ts +61 -0
- package/dist/src/components/editor/blocks/waka-comment-thread-block.d.ts +85 -0
- package/dist/src/components/editor/blocks/waka-diagram-block.d.ts +52 -0
- package/dist/src/components/editor/blocks/waka-embed-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-slash-menu-block.d.ts +67 -0
- package/dist/src/components/editor/blocks/waka-user-story-block.d.ts +79 -0
- package/dist/src/components/editor/blocks/waka-version-diff-block.d.ts +73 -0
- package/dist/src/components/editor/index.d.ts +66 -0
- package/dist/src/components/editor/waka-ai-writer.d.ts +80 -0
- package/dist/src/components/editor/waka-collaborative-editor.d.ts +93 -0
- package/dist/src/components/editor/waka-diff-viewer.d.ts +71 -0
- package/dist/src/components/editor/waka-dnd-editor.d.ts +64 -0
- package/dist/src/components/editor/waka-document-editor.d.ts +92 -0
- package/dist/src/components/editor/waka-editor-elements.d.ts +79 -0
- package/dist/src/components/editor/waka-editor-leaves.d.ts +39 -0
- package/dist/src/components/editor/waka-editor-plugins.d.ts +41 -0
- package/dist/src/components/editor/waka-editor-toolbar.d.ts +20 -0
- package/dist/src/components/editor/waka-editor.d.ts +59 -0
- package/dist/src/components/editor/waka-floating-toolbar.d.ts +47 -0
- package/dist/src/components/editor/waka-markdown-editor.d.ts +60 -0
- package/dist/src/components/editor/waka-mention-editor.d.ts +125 -0
- package/dist/src/components/editor/waka-slash-menu.d.ts +70 -0
- package/dist/src/components/editor/waka-spec-editor.d.ts +88 -0
- package/dist/src/components/index.d.ts +1 -15
- package/dist/src/editor.d.ts +26 -0
- package/dist/textarea-CdQWggYG.js +1 -0
- package/dist/textarea-DJDXJ3nd.mjs +23 -0
- package/dist/types-C2St0wOW.js +1 -0
- package/dist/{types-B6GVaSIP.mjs → types-JnqoLyuv.mjs} +214 -211
- package/dist/{useDataTableImport-BPvfo--2.mjs → useDataTableImport-BWUFesPi.mjs} +3 -3
- package/dist/{useDataTableImport-Cm_pCKnO.js → useDataTableImport-T7ddpN5k.js} +3 -3
- package/dist/waka-doc-renderer-CTxC7Trf.js +3 -0
- package/dist/{waka-doc-renderer-BkIvas3z.mjs → waka-doc-renderer-Cw-Xnyen.mjs} +264 -281
- package/dist/waka-editor-plugins-DR6tpsUC.mjs +135 -0
- package/dist/waka-editor-plugins-sGSh9hn2.js +1 -0
- package/dist/waka-rich-text-editor-BlIdtknG.js +1 -0
- package/dist/waka-rich-text-editor-D1uA3zbB.js +1 -0
- package/dist/waka-rich-text-editor-DgSWiXMW.mjs +342 -0
- package/dist/waka-rich-text-editor-DndVJuDw.mjs +2 -0
- package/package.json +87 -2
- package/src/blocks/footer/index.tsx +1 -6
- package/src/blocks/login/index.tsx +1 -7
- package/src/blocks/profile/index.tsx +3 -5
- package/src/components/editor/blocks/index.ts +182 -0
- package/src/components/editor/blocks/waka-acceptance-criteria-block.tsx +326 -0
- package/src/components/editor/blocks/waka-ai-assist-block.tsx +284 -0
- package/src/components/editor/blocks/waka-api-endpoint-block.tsx +382 -0
- package/src/components/editor/blocks/waka-code-playground-block.tsx +331 -0
- package/src/components/editor/blocks/waka-comment-thread-block.tsx +448 -0
- package/src/components/editor/blocks/waka-diagram-block.tsx +293 -0
- package/src/components/editor/blocks/waka-embed-block.tsx +416 -0
- package/src/components/editor/blocks/waka-slash-menu-block.tsx +432 -0
- package/src/components/editor/blocks/waka-user-story-block.tsx +295 -0
- package/src/components/editor/blocks/waka-version-diff-block.tsx +426 -0
- package/src/components/editor/index.ts +279 -0
- package/src/components/editor/waka-ai-writer.tsx +434 -0
- package/src/components/editor/waka-collaborative-editor.tsx +426 -0
- package/src/components/editor/waka-diff-viewer.tsx +352 -0
- package/src/components/editor/waka-dnd-editor.tsx +284 -0
- package/src/components/editor/waka-document-editor.tsx +502 -0
- package/src/components/editor/waka-editor-elements.tsx +312 -0
- package/src/components/editor/waka-editor-leaves.tsx +101 -0
- package/src/components/editor/waka-editor-plugins.ts +207 -0
- package/src/components/editor/waka-editor-toolbar.tsx +358 -0
- package/src/components/editor/waka-editor.tsx +431 -0
- package/src/components/editor/waka-floating-toolbar.tsx +268 -0
- package/src/components/editor/waka-markdown-editor.tsx +395 -0
- package/src/components/editor/waka-mention-editor.tsx +459 -0
- package/src/components/editor/waka-slash-menu.tsx +392 -0
- package/src/components/editor/waka-spec-editor.tsx +657 -0
- package/src/components/index.ts +1 -18
- package/dist/chunk-BDDJmn7V.js +0 -1
- package/dist/cn-DnPbmOCy.js +0 -1
- package/dist/cn-DpLcAzrf.mjs +0 -22
- package/dist/separator-BDReXBvI.mjs +0 -59
- package/dist/separator-BKjNl9sI.js +0 -1
- package/dist/src/components/waka-actor-badge/index.d.ts +0 -8
- package/dist/src/components/waka-actors-list/index.d.ts +0 -18
- package/dist/src/components/waka-ai-assistant-button/index.d.ts +0 -8
- package/dist/src/components/waka-document-flyover/index.d.ts +0 -10
- package/dist/src/components/waka-document-preview-popup/index.d.ts +0 -26
- package/dist/src/components/waka-hour-balance-badge/index.d.ts +0 -8
- package/dist/src/components/waka-hour-consumption-table/index.d.ts +0 -15
- package/dist/src/components/waka-hour-pack-dialog/index.d.ts +0 -8
- package/dist/src/components/waka-project-stats-header/index.d.ts +0 -15
- package/dist/src/components/waka-step-comment-bubble/index.d.ts +0 -13
- package/dist/src/components/waka-step-comment-panel/index.d.ts +0 -20
- package/dist/src/components/waka-step-permission-matrix/index.d.ts +0 -12
- package/dist/src/components/waka-time-entry-dialog/index.d.ts +0 -16
- package/dist/src/components/waka-time-tracking-flyover/index.d.ts +0 -11
- package/dist/types-BH9cQRqZ.js +0 -1
- package/dist/waka-doc-renderer-BZ2-SqyT.js +0 -3
- package/dist/waka-rich-text-editor-BJGlQgpq.js +0 -1
- package/dist/waka-rich-text-editor-BJzzxeP1.mjs +0 -361
- package/dist/waka-rich-text-editor-wnXLwvUo.js +0 -1
- package/src/components/waka-actor-badge/index.tsx +0 -34
- package/src/components/waka-actors-list/index.tsx +0 -125
- package/src/components/waka-ai-assistant-button/index.tsx +0 -31
- package/src/components/waka-document-flyover/index.tsx +0 -36
- package/src/components/waka-document-preview-popup/index.tsx +0 -103
- package/src/components/waka-hour-balance-badge/index.tsx +0 -43
- package/src/components/waka-hour-consumption-table/index.tsx +0 -72
- package/src/components/waka-hour-pack-dialog/index.tsx +0 -72
- package/src/components/waka-project-stats-header/index.tsx +0 -69
- package/src/components/waka-step-comment-bubble/index.tsx +0 -71
- package/src/components/waka-step-comment-panel/index.tsx +0 -106
- package/src/components/waka-step-permission-matrix/index.tsx +0 -65
- package/src/components/waka-time-entry-dialog/index.tsx +0 -131
- package/src/components/waka-time-tracking-flyover/index.tsx +0 -41
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../../utils/cn"
|
|
5
|
+
import type { PlateElementProps } from "../waka-editor-elements"
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Types
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
export const VERSION_DIFF_BLOCK_TYPE = "version_diff" as const
|
|
12
|
+
|
|
13
|
+
/** A single diff hunk (line-level diff) */
|
|
14
|
+
export interface DiffLine {
|
|
15
|
+
/** Line content */
|
|
16
|
+
content: string
|
|
17
|
+
/** Diff type */
|
|
18
|
+
type: "added" | "removed" | "unchanged" | "modified"
|
|
19
|
+
/** Original line number (for removed/unchanged) */
|
|
20
|
+
oldLineNumber?: number
|
|
21
|
+
/** New line number (for added/unchanged) */
|
|
22
|
+
newLineNumber?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Represents a section of changes */
|
|
26
|
+
export interface DiffHunk {
|
|
27
|
+
/** Start line in old version */
|
|
28
|
+
oldStart: number
|
|
29
|
+
/** Start line in new version */
|
|
30
|
+
newStart: number
|
|
31
|
+
/** Lines in this hunk */
|
|
32
|
+
lines: DiffLine[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Slate node for version diff blocks */
|
|
36
|
+
export interface VersionDiffElement {
|
|
37
|
+
type: typeof VERSION_DIFF_BLOCK_TYPE
|
|
38
|
+
/** Old (previous) version label */
|
|
39
|
+
oldVersionLabel: string
|
|
40
|
+
/** New (current) version label */
|
|
41
|
+
newVersionLabel: string
|
|
42
|
+
/** Old version full text */
|
|
43
|
+
oldText: string
|
|
44
|
+
/** New version full text */
|
|
45
|
+
newText: string
|
|
46
|
+
/** Pre-computed diff hunks */
|
|
47
|
+
hunks: DiffHunk[]
|
|
48
|
+
/** Summary stats */
|
|
49
|
+
stats: { additions: number; deletions: number; modifications: number }
|
|
50
|
+
/** Author of the change */
|
|
51
|
+
author?: string
|
|
52
|
+
/** Timestamp of the change */
|
|
53
|
+
timestamp?: string
|
|
54
|
+
children: Array<{ text: string }>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface WakaVersionDiffBlockProps extends PlateElementProps {
|
|
58
|
+
element?: VersionDiffElement & Record<string, unknown>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Diff View Modes
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
type DiffViewMode = "unified" | "split" | "summary"
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Element Component
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* WakaVersionDiffBlock - A Plate.js block that displays a visual diff between
|
|
73
|
+
* two versions of text content. Shows additions, deletions, and modifications
|
|
74
|
+
* with unified or split view modes.
|
|
75
|
+
*
|
|
76
|
+
* Designed to integrate with `@platejs/diff` or work standalone with pre-computed
|
|
77
|
+
* diff data.
|
|
78
|
+
*
|
|
79
|
+
* Register in Plate editor:
|
|
80
|
+
* ```ts
|
|
81
|
+
* components: {
|
|
82
|
+
* [VERSION_DIFF_BLOCK_TYPE]: WakaVersionDiffBlock,
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export function WakaVersionDiffBlock({
|
|
87
|
+
attributes,
|
|
88
|
+
children,
|
|
89
|
+
element,
|
|
90
|
+
className,
|
|
91
|
+
}: WakaVersionDiffBlockProps) {
|
|
92
|
+
const el = element as VersionDiffElement | undefined
|
|
93
|
+
const [viewMode, setViewMode] = React.useState<DiffViewMode>("unified")
|
|
94
|
+
const [expanded, setExpanded] = React.useState(true)
|
|
95
|
+
|
|
96
|
+
const hunks = el?.hunks || []
|
|
97
|
+
const stats = el?.stats || { additions: 0, deletions: 0, modifications: 0 }
|
|
98
|
+
const oldLabel = el?.oldVersionLabel || "Previous"
|
|
99
|
+
const newLabel = el?.newVersionLabel || "Current"
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div
|
|
103
|
+
{...attributes}
|
|
104
|
+
contentEditable={false}
|
|
105
|
+
className={cn(
|
|
106
|
+
"my-4 rounded-lg overflow-hidden border border-border shadow-sm",
|
|
107
|
+
className
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
{/* Header */}
|
|
111
|
+
<div className="flex items-center justify-between px-4 py-2.5 bg-muted/30 border-b border-border">
|
|
112
|
+
<div className="flex items-center gap-3">
|
|
113
|
+
{/* Git-like icon */}
|
|
114
|
+
<svg
|
|
115
|
+
className="h-4 w-4 text-orange-600 dark:text-orange-400"
|
|
116
|
+
viewBox="0 0 24 24"
|
|
117
|
+
fill="none"
|
|
118
|
+
stroke="currentColor"
|
|
119
|
+
strokeWidth={2}
|
|
120
|
+
aria-hidden="true"
|
|
121
|
+
>
|
|
122
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3v6m0 0l-3 3m3-3l3 3M6 21h12a2 2 0 002-2V7l-5-5H6a2 2 0 00-2 2v15a2 2 0 002 2z" />
|
|
123
|
+
</svg>
|
|
124
|
+
<span className="text-xs font-bold uppercase tracking-wider text-foreground">
|
|
125
|
+
Version Diff
|
|
126
|
+
</span>
|
|
127
|
+
|
|
128
|
+
{/* Version labels */}
|
|
129
|
+
<div className="flex items-center gap-1 text-[10px]">
|
|
130
|
+
<span className="px-1.5 py-0.5 rounded bg-red-100 dark:bg-red-500/10 text-red-700 dark:text-red-400 font-mono font-medium">
|
|
131
|
+
{oldLabel}
|
|
132
|
+
</span>
|
|
133
|
+
<svg className="h-3 w-3 text-muted-foreground" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
134
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
|
135
|
+
</svg>
|
|
136
|
+
<span className="px-1.5 py-0.5 rounded bg-emerald-100 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 font-mono font-medium">
|
|
137
|
+
{newLabel}
|
|
138
|
+
</span>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div className="flex items-center gap-3">
|
|
143
|
+
{/* Stats */}
|
|
144
|
+
<div className="flex items-center gap-2 text-[10px] font-mono">
|
|
145
|
+
{stats.additions > 0 && (
|
|
146
|
+
<span className="text-emerald-600 dark:text-emerald-400 font-semibold">+{stats.additions}</span>
|
|
147
|
+
)}
|
|
148
|
+
{stats.deletions > 0 && (
|
|
149
|
+
<span className="text-red-600 dark:text-red-400 font-semibold">-{stats.deletions}</span>
|
|
150
|
+
)}
|
|
151
|
+
{stats.modifications > 0 && (
|
|
152
|
+
<span className="text-amber-600 dark:text-amber-400 font-semibold">~{stats.modifications}</span>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
{/* View mode toggle */}
|
|
157
|
+
<div className="flex items-center border border-border rounded overflow-hidden">
|
|
158
|
+
{(["unified", "split", "summary"] as DiffViewMode[]).map((mode) => (
|
|
159
|
+
<button
|
|
160
|
+
key={mode}
|
|
161
|
+
type="button"
|
|
162
|
+
onClick={() => setViewMode(mode)}
|
|
163
|
+
className={cn(
|
|
164
|
+
"px-2 py-1 text-[10px] font-medium transition-colors",
|
|
165
|
+
viewMode === mode
|
|
166
|
+
? "bg-foreground/10 text-foreground"
|
|
167
|
+
: "text-muted-foreground hover:text-foreground"
|
|
168
|
+
)}
|
|
169
|
+
>
|
|
170
|
+
{mode.charAt(0).toUpperCase() + mode.slice(1)}
|
|
171
|
+
</button>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{/* Collapse toggle */}
|
|
176
|
+
<button
|
|
177
|
+
type="button"
|
|
178
|
+
onClick={() => setExpanded(!expanded)}
|
|
179
|
+
className="text-muted-foreground hover:text-foreground transition-colors"
|
|
180
|
+
>
|
|
181
|
+
<svg
|
|
182
|
+
className={cn("h-4 w-4 transition-transform", !expanded && "-rotate-90")}
|
|
183
|
+
viewBox="0 0 24 24"
|
|
184
|
+
fill="none"
|
|
185
|
+
stroke="currentColor"
|
|
186
|
+
strokeWidth={2}
|
|
187
|
+
>
|
|
188
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
|
189
|
+
</svg>
|
|
190
|
+
</button>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
{/* Author + timestamp */}
|
|
195
|
+
{(el?.author || el?.timestamp) && (
|
|
196
|
+
<div className="px-4 py-1.5 border-b border-border/50 bg-muted/10 flex items-center gap-3 text-[10px] text-muted-foreground">
|
|
197
|
+
{el.author && (
|
|
198
|
+
<div className="flex items-center gap-1">
|
|
199
|
+
<svg className="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
200
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
201
|
+
</svg>
|
|
202
|
+
<span className="font-medium">{el.author}</span>
|
|
203
|
+
</div>
|
|
204
|
+
)}
|
|
205
|
+
{el.timestamp && (
|
|
206
|
+
<span>{new Date(el.timestamp).toLocaleString("fr-FR")}</span>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{/* Diff content */}
|
|
212
|
+
{expanded && (
|
|
213
|
+
<div className="overflow-x-auto">
|
|
214
|
+
{viewMode === "summary" ? (
|
|
215
|
+
/* Summary view */
|
|
216
|
+
<div className="p-4 space-y-3">
|
|
217
|
+
<div className="grid grid-cols-3 gap-3">
|
|
218
|
+
<div className="rounded-lg bg-emerald-50 dark:bg-emerald-500/5 border border-emerald-200 dark:border-emerald-500/20 p-3 text-center">
|
|
219
|
+
<div className="text-2xl font-bold text-emerald-600 dark:text-emerald-400">{stats.additions}</div>
|
|
220
|
+
<div className="text-[10px] font-medium text-emerald-700/70 dark:text-emerald-400/70 uppercase tracking-wider">Additions</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="rounded-lg bg-red-50 dark:bg-red-500/5 border border-red-200 dark:border-red-500/20 p-3 text-center">
|
|
223
|
+
<div className="text-2xl font-bold text-red-600 dark:text-red-400">{stats.deletions}</div>
|
|
224
|
+
<div className="text-[10px] font-medium text-red-700/70 dark:text-red-400/70 uppercase tracking-wider">Deletions</div>
|
|
225
|
+
</div>
|
|
226
|
+
<div className="rounded-lg bg-amber-50 dark:bg-amber-500/5 border border-amber-200 dark:border-amber-500/20 p-3 text-center">
|
|
227
|
+
<div className="text-2xl font-bold text-amber-600 dark:text-amber-400">{stats.modifications}</div>
|
|
228
|
+
<div className="text-[10px] font-medium text-amber-700/70 dark:text-amber-400/70 uppercase tracking-wider">Modified</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
{/* Change bar */}
|
|
232
|
+
<div className="h-2 rounded-full bg-muted overflow-hidden flex">
|
|
233
|
+
{stats.additions > 0 && (
|
|
234
|
+
<div className="bg-emerald-500 h-full" style={{ width: `${(stats.additions / (stats.additions + stats.deletions + stats.modifications)) * 100}%` }} />
|
|
235
|
+
)}
|
|
236
|
+
{stats.modifications > 0 && (
|
|
237
|
+
<div className="bg-amber-500 h-full" style={{ width: `${(stats.modifications / (stats.additions + stats.deletions + stats.modifications)) * 100}%` }} />
|
|
238
|
+
)}
|
|
239
|
+
{stats.deletions > 0 && (
|
|
240
|
+
<div className="bg-red-500 h-full" style={{ width: `${(stats.deletions / (stats.additions + stats.deletions + stats.modifications)) * 100}%` }} />
|
|
241
|
+
)}
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
) : viewMode === "split" ? (
|
|
245
|
+
/* Split view */
|
|
246
|
+
<div className="grid grid-cols-2 divide-x divide-border">
|
|
247
|
+
<div className="p-0">
|
|
248
|
+
<div className="px-3 py-1.5 border-b border-border/50 bg-red-50/50 dark:bg-red-950/10">
|
|
249
|
+
<span className="text-[10px] font-semibold text-red-700 dark:text-red-400 uppercase tracking-wider">{oldLabel}</span>
|
|
250
|
+
</div>
|
|
251
|
+
<div className="font-mono text-[12px] leading-relaxed">
|
|
252
|
+
{hunks.map((hunk, hi) => (
|
|
253
|
+
<React.Fragment key={hi}>
|
|
254
|
+
{hunk.lines.filter((l) => l.type !== "added").map((line, li) => (
|
|
255
|
+
<div
|
|
256
|
+
key={`${hi}-${li}`}
|
|
257
|
+
className={cn(
|
|
258
|
+
"px-3 py-0.5 flex",
|
|
259
|
+
line.type === "removed" && "bg-red-100/60 dark:bg-red-500/10",
|
|
260
|
+
line.type === "modified" && "bg-amber-100/60 dark:bg-amber-500/10"
|
|
261
|
+
)}
|
|
262
|
+
>
|
|
263
|
+
<span className="w-8 text-right pr-2 text-muted-foreground/50 select-none shrink-0">
|
|
264
|
+
{line.oldLineNumber}
|
|
265
|
+
</span>
|
|
266
|
+
<span className={cn(
|
|
267
|
+
line.type === "removed" && "text-red-800 dark:text-red-300",
|
|
268
|
+
line.type === "modified" && "text-amber-800 dark:text-amber-300"
|
|
269
|
+
)}>
|
|
270
|
+
{line.type === "removed" && <span className="text-red-500 mr-1">-</span>}
|
|
271
|
+
{line.content}
|
|
272
|
+
</span>
|
|
273
|
+
</div>
|
|
274
|
+
))}
|
|
275
|
+
</React.Fragment>
|
|
276
|
+
))}
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
<div className="p-0">
|
|
280
|
+
<div className="px-3 py-1.5 border-b border-border/50 bg-emerald-50/50 dark:bg-emerald-950/10">
|
|
281
|
+
<span className="text-[10px] font-semibold text-emerald-700 dark:text-emerald-400 uppercase tracking-wider">{newLabel}</span>
|
|
282
|
+
</div>
|
|
283
|
+
<div className="font-mono text-[12px] leading-relaxed">
|
|
284
|
+
{hunks.map((hunk, hi) => (
|
|
285
|
+
<React.Fragment key={hi}>
|
|
286
|
+
{hunk.lines.filter((l) => l.type !== "removed").map((line, li) => (
|
|
287
|
+
<div
|
|
288
|
+
key={`${hi}-${li}`}
|
|
289
|
+
className={cn(
|
|
290
|
+
"px-3 py-0.5 flex",
|
|
291
|
+
line.type === "added" && "bg-emerald-100/60 dark:bg-emerald-500/10",
|
|
292
|
+
line.type === "modified" && "bg-amber-100/60 dark:bg-amber-500/10"
|
|
293
|
+
)}
|
|
294
|
+
>
|
|
295
|
+
<span className="w-8 text-right pr-2 text-muted-foreground/50 select-none shrink-0">
|
|
296
|
+
{line.newLineNumber}
|
|
297
|
+
</span>
|
|
298
|
+
<span className={cn(
|
|
299
|
+
line.type === "added" && "text-emerald-800 dark:text-emerald-300",
|
|
300
|
+
line.type === "modified" && "text-amber-800 dark:text-amber-300"
|
|
301
|
+
)}>
|
|
302
|
+
{line.type === "added" && <span className="text-emerald-500 mr-1">+</span>}
|
|
303
|
+
{line.content}
|
|
304
|
+
</span>
|
|
305
|
+
</div>
|
|
306
|
+
))}
|
|
307
|
+
</React.Fragment>
|
|
308
|
+
))}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
) : (
|
|
313
|
+
/* Unified view */
|
|
314
|
+
<div className="font-mono text-[12px] leading-relaxed">
|
|
315
|
+
{hunks.map((hunk, hi) => (
|
|
316
|
+
<React.Fragment key={hi}>
|
|
317
|
+
{/* Hunk header */}
|
|
318
|
+
<div className="px-3 py-1 bg-blue-50/50 dark:bg-blue-950/10 text-blue-600 dark:text-blue-400 text-[10px]">
|
|
319
|
+
@@ -{hunk.oldStart} +{hunk.newStart} @@
|
|
320
|
+
</div>
|
|
321
|
+
{hunk.lines.map((line, li) => (
|
|
322
|
+
<div
|
|
323
|
+
key={`${hi}-${li}`}
|
|
324
|
+
className={cn(
|
|
325
|
+
"px-3 py-0.5 flex",
|
|
326
|
+
line.type === "added" && "bg-emerald-100/60 dark:bg-emerald-500/10",
|
|
327
|
+
line.type === "removed" && "bg-red-100/60 dark:bg-red-500/10",
|
|
328
|
+
line.type === "modified" && "bg-amber-100/60 dark:bg-amber-500/10"
|
|
329
|
+
)}
|
|
330
|
+
>
|
|
331
|
+
<span className="w-8 text-right pr-2 text-muted-foreground/50 select-none shrink-0">
|
|
332
|
+
{line.oldLineNumber || ""}
|
|
333
|
+
</span>
|
|
334
|
+
<span className="w-8 text-right pr-2 text-muted-foreground/50 select-none shrink-0">
|
|
335
|
+
{line.newLineNumber || ""}
|
|
336
|
+
</span>
|
|
337
|
+
<span className="w-4 shrink-0 text-center select-none">
|
|
338
|
+
{line.type === "added" ? (
|
|
339
|
+
<span className="text-emerald-500">+</span>
|
|
340
|
+
) : line.type === "removed" ? (
|
|
341
|
+
<span className="text-red-500">-</span>
|
|
342
|
+
) : line.type === "modified" ? (
|
|
343
|
+
<span className="text-amber-500">~</span>
|
|
344
|
+
) : (
|
|
345
|
+
<span className="text-muted-foreground/30"> </span>
|
|
346
|
+
)}
|
|
347
|
+
</span>
|
|
348
|
+
<span className={cn(
|
|
349
|
+
line.type === "added" && "text-emerald-800 dark:text-emerald-300",
|
|
350
|
+
line.type === "removed" && "text-red-800 dark:text-red-300 line-through",
|
|
351
|
+
line.type === "modified" && "text-amber-800 dark:text-amber-300"
|
|
352
|
+
)}>
|
|
353
|
+
{line.content}
|
|
354
|
+
</span>
|
|
355
|
+
</div>
|
|
356
|
+
))}
|
|
357
|
+
</React.Fragment>
|
|
358
|
+
))}
|
|
359
|
+
{hunks.length === 0 && (
|
|
360
|
+
<div className="py-8 text-center text-sm text-muted-foreground italic">
|
|
361
|
+
No differences found.
|
|
362
|
+
</div>
|
|
363
|
+
)}
|
|
364
|
+
</div>
|
|
365
|
+
)}
|
|
366
|
+
</div>
|
|
367
|
+
)}
|
|
368
|
+
|
|
369
|
+
{/* Hidden Slate children */}
|
|
370
|
+
<div className="hidden">{children}</div>
|
|
371
|
+
</div>
|
|
372
|
+
)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
WakaVersionDiffBlock.displayName = "WakaVersionDiffBlock"
|
|
376
|
+
|
|
377
|
+
// ============================================================================
|
|
378
|
+
// Node Factory
|
|
379
|
+
// ============================================================================
|
|
380
|
+
|
|
381
|
+
export function createVersionDiffNodes(options?: Partial<Omit<VersionDiffElement, "type" | "children">>): VersionDiffElement[] {
|
|
382
|
+
return [
|
|
383
|
+
{
|
|
384
|
+
type: VERSION_DIFF_BLOCK_TYPE,
|
|
385
|
+
oldVersionLabel: options?.oldVersionLabel || "v1.0",
|
|
386
|
+
newVersionLabel: options?.newVersionLabel || "v1.1",
|
|
387
|
+
oldText: options?.oldText || "",
|
|
388
|
+
newText: options?.newText || "",
|
|
389
|
+
hunks: options?.hunks || [
|
|
390
|
+
{
|
|
391
|
+
oldStart: 1,
|
|
392
|
+
newStart: 1,
|
|
393
|
+
lines: [
|
|
394
|
+
{ content: "The system SHALL authenticate users via OAuth2.", type: "unchanged", oldLineNumber: 1, newLineNumber: 1 },
|
|
395
|
+
{ content: "Rate limiting: 50 req/min per user.", type: "removed", oldLineNumber: 2 },
|
|
396
|
+
{ content: "Rate limiting: 100 req/min per user.", type: "added", newLineNumber: 2 },
|
|
397
|
+
{ content: "All API responses use JSON format.", type: "unchanged", oldLineNumber: 3, newLineNumber: 3 },
|
|
398
|
+
{ content: "Added: Support for WebSocket real-time events.", type: "added", newLineNumber: 4 },
|
|
399
|
+
],
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
stats: options?.stats || { additions: 2, deletions: 1, modifications: 0 },
|
|
403
|
+
author: options?.author,
|
|
404
|
+
timestamp: options?.timestamp,
|
|
405
|
+
children: [{ text: "" }],
|
|
406
|
+
},
|
|
407
|
+
]
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export async function createVersionDiffPlugin() {
|
|
411
|
+
try {
|
|
412
|
+
const { createPlatePlugin } = await import("platejs/react")
|
|
413
|
+
return createPlatePlugin({
|
|
414
|
+
key: VERSION_DIFF_BLOCK_TYPE,
|
|
415
|
+
node: {
|
|
416
|
+
isElement: true,
|
|
417
|
+
isVoid: true,
|
|
418
|
+
type: VERSION_DIFF_BLOCK_TYPE,
|
|
419
|
+
component: WakaVersionDiffBlock,
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
} catch {
|
|
423
|
+
console.warn("[WakaVersionDiffBlock] platejs not installed")
|
|
424
|
+
return null
|
|
425
|
+
}
|
|
426
|
+
}
|