@gingkoo/pandora-metabase 1.0.74 → 1.0.76
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/lib/cjs/components/dialog/diff-viewer/index copy.d.ts +10 -0
- package/lib/cjs/components/dialog/diff-viewer/index copy.js +154 -0
- package/lib/cjs/components/dialog/diff-viewer/index.d.ts +2 -5
- package/lib/cjs/components/dialog/diff-viewer/index.js +9 -143
- package/lib/es/components/dialog/diff-viewer/index copy.d.ts +10 -0
- package/lib/es/components/dialog/diff-viewer/index copy.js +146 -0
- package/lib/es/components/dialog/diff-viewer/index.d.ts +2 -5
- package/lib/es/components/dialog/diff-viewer/index.js +10 -142
- package/package.json +2 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './index.less';
|
|
3
|
+
export interface DiffViewerProps {
|
|
4
|
+
oldCode: string | undefined;
|
|
5
|
+
newCode: string | undefined;
|
|
6
|
+
oldFileName?: string;
|
|
7
|
+
newFileName?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const DiffViewer: React.FC<DiffViewerProps>;
|
|
10
|
+
export default DiffViewer;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports["default"] = void 0;
|
|
8
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/slicedToArray"));
|
|
9
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
var _react = require("react");
|
|
11
|
+
var _diff = require("diff");
|
|
12
|
+
require("./index.less");
|
|
13
|
+
// DiffViewer.tsx
|
|
14
|
+
|
|
15
|
+
var DiffViewer = function DiffViewer(_ref) {
|
|
16
|
+
var oldCode = _ref.oldCode,
|
|
17
|
+
newCode = _ref.newCode,
|
|
18
|
+
_ref$oldFileName = _ref.oldFileName,
|
|
19
|
+
oldFileName = _ref$oldFileName === void 0 ? 'old.js' : _ref$oldFileName,
|
|
20
|
+
_ref$newFileName = _ref.newFileName,
|
|
21
|
+
newFileName = _ref$newFileName === void 0 ? 'new.js' : _ref$newFileName;
|
|
22
|
+
var leftRef = (0, _react.useRef)(null);
|
|
23
|
+
var rightRef = (0, _react.useRef)(null);
|
|
24
|
+
var isSyncing = (0, _react.useRef)(false);
|
|
25
|
+
var _useState = (0, _react.useState)('--- (unknown)'),
|
|
26
|
+
_useState2 = (0, _slicedToArray2["default"])(_useState, 2),
|
|
27
|
+
oldFileHeader = _useState2[0],
|
|
28
|
+
setOldFileHeader = _useState2[1];
|
|
29
|
+
var _useState3 = (0, _react.useState)([]),
|
|
30
|
+
_useState4 = (0, _slicedToArray2["default"])(_useState3, 2),
|
|
31
|
+
hunkHeaders = _useState4[0],
|
|
32
|
+
setHunkHeaders = _useState4[1];
|
|
33
|
+
// 提取文件头和 hunk headers(安全依赖)
|
|
34
|
+
(0, _react.useEffect)(function () {
|
|
35
|
+
var safeOldStr = typeof oldCode === 'string' ? oldCode : '';
|
|
36
|
+
var safeNewStr = typeof newCode === 'string' ? newCode : '';
|
|
37
|
+
var patch = (0, _diff.createTwoFilesPatch)(oldFileName, newFileName, safeOldStr, safeNewStr, '', '', {
|
|
38
|
+
context: 10
|
|
39
|
+
});
|
|
40
|
+
var lines = patch.split('\n');
|
|
41
|
+
// for (const line of lines) {
|
|
42
|
+
// if (line.startsWith('--- ')) {
|
|
43
|
+
// setOldFileHeader(line);
|
|
44
|
+
// break;
|
|
45
|
+
// }
|
|
46
|
+
// }
|
|
47
|
+
setHunkHeaders(lines.filter(function (l) {
|
|
48
|
+
return l.startsWith('@@ ') && l.endsWith(' @@');
|
|
49
|
+
}));
|
|
50
|
+
}, [oldCode, newCode, oldFileName, newFileName]);
|
|
51
|
+
// 主 diff 渲染逻辑
|
|
52
|
+
(0, _react.useEffect)(function () {
|
|
53
|
+
var safeOld = (typeof oldCode === 'string' ? oldCode : '').split('\n');
|
|
54
|
+
var safeNew = (typeof newCode === 'string' ? newCode : '').split('\n');
|
|
55
|
+
var maxLines = Math.max(safeOld.length, safeNew.length);
|
|
56
|
+
var leftHtml = [];
|
|
57
|
+
var rightHtml = [];
|
|
58
|
+
for (var i = 0; i < maxLines; i++) {
|
|
59
|
+
var _safeOld$i, _safeNew$i;
|
|
60
|
+
var oldLine = (_safeOld$i = safeOld[i]) !== null && _safeOld$i !== void 0 ? _safeOld$i : '';
|
|
61
|
+
var newLine = (_safeNew$i = safeNew[i]) !== null && _safeNew$i !== void 0 ? _safeNew$i : '';
|
|
62
|
+
var diffs = (0, _diff.diffWordsWithSpace)(oldLine, newLine);
|
|
63
|
+
var leftContent = '';
|
|
64
|
+
var rightContent = '';
|
|
65
|
+
// ✅ 检查最后一个 token 是否是行尾空格且被删除
|
|
66
|
+
var lastToken = diffs[diffs.length - 1];
|
|
67
|
+
var ignoreLastTrailingSpace = diffs.length > 0 && (lastToken === null || lastToken === void 0 ? void 0 : lastToken.removed) && /^\s+$/.test(lastToken.value) && oldLine.endsWith(lastToken.value);
|
|
68
|
+
for (var j = 0; j < diffs.length; j++) {
|
|
69
|
+
var part = diffs[j];
|
|
70
|
+
var escaped = escapeHtml(part.value);
|
|
71
|
+
if (part.added) {
|
|
72
|
+
rightContent += "<span class=\"diff-add\">".concat(escaped, "</span>");
|
|
73
|
+
} else if (part.removed) {
|
|
74
|
+
// ✅ 如果是最后一个 token 且是行尾空格 → 不高亮
|
|
75
|
+
if (ignoreLastTrailingSpace && j === diffs.length - 1) {
|
|
76
|
+
leftContent += escaped; // 显示空格但不标红
|
|
77
|
+
// rightContent 不加(因为新行没有)
|
|
78
|
+
} else {
|
|
79
|
+
leftContent += "<span class=\"diff-del\">".concat(escaped, "</span>");
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
leftContent += escaped;
|
|
83
|
+
rightContent += escaped;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// 处理缺失行
|
|
87
|
+
if (i >= safeOld.length) leftContent = ' ';
|
|
88
|
+
if (i >= safeNew.length) rightContent = ' ';
|
|
89
|
+
leftHtml.push("<div class=\"diff-line\">".concat(leftContent, "</div>"));
|
|
90
|
+
rightHtml.push("<div class=\"diff-line\">".concat(rightContent, "</div>"));
|
|
91
|
+
}
|
|
92
|
+
var timer = setTimeout(function () {
|
|
93
|
+
if (leftRef.current) leftRef.current.innerHTML = leftHtml.join('');
|
|
94
|
+
if (rightRef.current) rightRef.current.innerHTML = rightHtml.join('');
|
|
95
|
+
}, 0);
|
|
96
|
+
return function () {
|
|
97
|
+
return clearTimeout(timer);
|
|
98
|
+
};
|
|
99
|
+
}, [oldCode, newCode]);
|
|
100
|
+
// 同步滚动
|
|
101
|
+
(0, _react.useEffect)(function () {
|
|
102
|
+
var left = leftRef.current;
|
|
103
|
+
var right = rightRef.current;
|
|
104
|
+
if (!left || !right) return;
|
|
105
|
+
var sync = function sync(src, dst) {
|
|
106
|
+
return function () {
|
|
107
|
+
if (isSyncing.current) return;
|
|
108
|
+
isSyncing.current = true;
|
|
109
|
+
dst.scrollTop = src.scrollTop;
|
|
110
|
+
dst.scrollLeft = src.scrollLeft;
|
|
111
|
+
requestAnimationFrame(function () {
|
|
112
|
+
isSyncing.current = false;
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
var onLeft = sync(left, right);
|
|
117
|
+
var onRight = sync(right, left);
|
|
118
|
+
left.addEventListener('scroll', onLeft, {
|
|
119
|
+
passive: true
|
|
120
|
+
});
|
|
121
|
+
right.addEventListener('scroll', onRight, {
|
|
122
|
+
passive: true
|
|
123
|
+
});
|
|
124
|
+
return function () {
|
|
125
|
+
left.removeEventListener('scroll', onLeft);
|
|
126
|
+
right.removeEventListener('scroll', onRight);
|
|
127
|
+
};
|
|
128
|
+
}, []);
|
|
129
|
+
return (0, _jsxRuntime.jsxs)("div", {
|
|
130
|
+
className: 'diff-viewer',
|
|
131
|
+
children: [hunkHeaders.length > 0 && (0, _jsxRuntime.jsx)("div", {
|
|
132
|
+
className: 'diff-hunk-headers',
|
|
133
|
+
children: hunkHeaders.map(function (h, i) {
|
|
134
|
+
return (0, _jsxRuntime.jsx)("div", {
|
|
135
|
+
className: 'hunk-header',
|
|
136
|
+
children: h
|
|
137
|
+
}, i);
|
|
138
|
+
})
|
|
139
|
+
}), (0, _jsxRuntime.jsxs)("div", {
|
|
140
|
+
className: 'diff-panes',
|
|
141
|
+
children: [(0, _jsxRuntime.jsx)("div", {
|
|
142
|
+
ref: leftRef,
|
|
143
|
+
className: 'diff-pane diff-old'
|
|
144
|
+
}), (0, _jsxRuntime.jsx)("div", {
|
|
145
|
+
ref: rightRef,
|
|
146
|
+
className: 'diff-pane diff-new'
|
|
147
|
+
})]
|
|
148
|
+
})]
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
function escapeHtml(str) {
|
|
152
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
153
|
+
}
|
|
154
|
+
var _default = exports["default"] = DiffViewer;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import './index.less';
|
|
3
2
|
export interface DiffViewerProps {
|
|
4
|
-
oldCode: string
|
|
5
|
-
newCode: string
|
|
6
|
-
oldFileName?: string;
|
|
7
|
-
newFileName?: string;
|
|
3
|
+
oldCode: string;
|
|
4
|
+
newCode: string;
|
|
8
5
|
}
|
|
9
6
|
declare const DiffViewer: React.FC<DiffViewerProps>;
|
|
10
7
|
export default DiffViewer;
|
|
@@ -1,154 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
3
|
Object.defineProperty(exports, "__esModule", {
|
|
5
4
|
value: true
|
|
6
5
|
});
|
|
7
6
|
exports["default"] = void 0;
|
|
8
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/slicedToArray"));
|
|
9
7
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
-
var
|
|
11
|
-
var _diff = require("diff");
|
|
12
|
-
require("./index.less");
|
|
13
|
-
// DiffViewer.tsx
|
|
14
|
-
|
|
8
|
+
var _pandora = require("@gingkoo/pandora");
|
|
15
9
|
var DiffViewer = function DiffViewer(_ref) {
|
|
16
|
-
var oldCode = _ref.oldCode,
|
|
17
|
-
|
|
18
|
-
_ref$
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
var isSyncing = (0, _react.useRef)(false);
|
|
25
|
-
var _useState = (0, _react.useState)('--- (unknown)'),
|
|
26
|
-
_useState2 = (0, _slicedToArray2["default"])(_useState, 2),
|
|
27
|
-
oldFileHeader = _useState2[0],
|
|
28
|
-
setOldFileHeader = _useState2[1];
|
|
29
|
-
var _useState3 = (0, _react.useState)([]),
|
|
30
|
-
_useState4 = (0, _slicedToArray2["default"])(_useState3, 2),
|
|
31
|
-
hunkHeaders = _useState4[0],
|
|
32
|
-
setHunkHeaders = _useState4[1];
|
|
33
|
-
// 提取文件头和 hunk headers(安全依赖)
|
|
34
|
-
(0, _react.useEffect)(function () {
|
|
35
|
-
var safeOldStr = typeof oldCode === 'string' ? oldCode : '';
|
|
36
|
-
var safeNewStr = typeof newCode === 'string' ? newCode : '';
|
|
37
|
-
var patch = (0, _diff.createTwoFilesPatch)(oldFileName, newFileName, safeOldStr, safeNewStr, '', '', {
|
|
38
|
-
context: 10
|
|
39
|
-
});
|
|
40
|
-
var lines = patch.split('\n');
|
|
41
|
-
// for (const line of lines) {
|
|
42
|
-
// if (line.startsWith('--- ')) {
|
|
43
|
-
// setOldFileHeader(line);
|
|
44
|
-
// break;
|
|
45
|
-
// }
|
|
46
|
-
// }
|
|
47
|
-
setHunkHeaders(lines.filter(function (l) {
|
|
48
|
-
return l.startsWith('@@ ') && l.endsWith(' @@');
|
|
49
|
-
}));
|
|
50
|
-
}, [oldCode, newCode, oldFileName, newFileName]);
|
|
51
|
-
// 主 diff 渲染逻辑
|
|
52
|
-
(0, _react.useEffect)(function () {
|
|
53
|
-
var safeOld = (typeof oldCode === 'string' ? oldCode : '').split('\n');
|
|
54
|
-
var safeNew = (typeof newCode === 'string' ? newCode : '').split('\n');
|
|
55
|
-
var maxLines = Math.max(safeOld.length, safeNew.length);
|
|
56
|
-
var leftHtml = [];
|
|
57
|
-
var rightHtml = [];
|
|
58
|
-
for (var i = 0; i < maxLines; i++) {
|
|
59
|
-
var _safeOld$i, _safeNew$i;
|
|
60
|
-
var oldLine = (_safeOld$i = safeOld[i]) !== null && _safeOld$i !== void 0 ? _safeOld$i : '';
|
|
61
|
-
var newLine = (_safeNew$i = safeNew[i]) !== null && _safeNew$i !== void 0 ? _safeNew$i : '';
|
|
62
|
-
var diffs = (0, _diff.diffWordsWithSpace)(oldLine, newLine);
|
|
63
|
-
var leftContent = '';
|
|
64
|
-
var rightContent = '';
|
|
65
|
-
// ✅ 检查最后一个 token 是否是行尾空格且被删除
|
|
66
|
-
var lastToken = diffs[diffs.length - 1];
|
|
67
|
-
var ignoreLastTrailingSpace = diffs.length > 0 && (lastToken === null || lastToken === void 0 ? void 0 : lastToken.removed) && /^\s+$/.test(lastToken.value) && oldLine.endsWith(lastToken.value);
|
|
68
|
-
for (var j = 0; j < diffs.length; j++) {
|
|
69
|
-
var part = diffs[j];
|
|
70
|
-
var escaped = escapeHtml(part.value);
|
|
71
|
-
if (part.added) {
|
|
72
|
-
rightContent += "<span class=\"diff-add\">".concat(escaped, "</span>");
|
|
73
|
-
} else if (part.removed) {
|
|
74
|
-
// ✅ 如果是最后一个 token 且是行尾空格 → 不高亮
|
|
75
|
-
if (ignoreLastTrailingSpace && j === diffs.length - 1) {
|
|
76
|
-
leftContent += escaped; // 显示空格但不标红
|
|
77
|
-
// rightContent 不加(因为新行没有)
|
|
78
|
-
} else {
|
|
79
|
-
leftContent += "<span class=\"diff-del\">".concat(escaped, "</span>");
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
leftContent += escaped;
|
|
83
|
-
rightContent += escaped;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// 处理缺失行
|
|
87
|
-
if (i >= safeOld.length) leftContent = ' ';
|
|
88
|
-
if (i >= safeNew.length) rightContent = ' ';
|
|
89
|
-
leftHtml.push("<div class=\"diff-line\">".concat(leftContent, "</div>"));
|
|
90
|
-
rightHtml.push("<div class=\"diff-line\">".concat(rightContent, "</div>"));
|
|
91
|
-
}
|
|
92
|
-
var timer = setTimeout(function () {
|
|
93
|
-
if (leftRef.current) leftRef.current.innerHTML = leftHtml.join('');
|
|
94
|
-
if (rightRef.current) rightRef.current.innerHTML = rightHtml.join('');
|
|
95
|
-
}, 0);
|
|
96
|
-
return function () {
|
|
97
|
-
return clearTimeout(timer);
|
|
98
|
-
};
|
|
99
|
-
}, [oldCode, newCode]);
|
|
100
|
-
// 同步滚动
|
|
101
|
-
(0, _react.useEffect)(function () {
|
|
102
|
-
var left = leftRef.current;
|
|
103
|
-
var right = rightRef.current;
|
|
104
|
-
if (!left || !right) return;
|
|
105
|
-
var sync = function sync(src, dst) {
|
|
106
|
-
return function () {
|
|
107
|
-
if (isSyncing.current) return;
|
|
108
|
-
isSyncing.current = true;
|
|
109
|
-
dst.scrollTop = src.scrollTop;
|
|
110
|
-
dst.scrollLeft = src.scrollLeft;
|
|
111
|
-
requestAnimationFrame(function () {
|
|
112
|
-
isSyncing.current = false;
|
|
113
|
-
});
|
|
114
|
-
};
|
|
115
|
-
};
|
|
116
|
-
var onLeft = sync(left, right);
|
|
117
|
-
var onRight = sync(right, left);
|
|
118
|
-
left.addEventListener('scroll', onLeft, {
|
|
119
|
-
passive: true
|
|
120
|
-
});
|
|
121
|
-
right.addEventListener('scroll', onRight, {
|
|
122
|
-
passive: true
|
|
123
|
-
});
|
|
124
|
-
return function () {
|
|
125
|
-
left.removeEventListener('scroll', onLeft);
|
|
126
|
-
right.removeEventListener('scroll', onRight);
|
|
127
|
-
};
|
|
128
|
-
}, []);
|
|
129
|
-
return (0, _jsxRuntime.jsxs)("div", {
|
|
130
|
-
className: 'diff-viewer',
|
|
131
|
-
children: [hunkHeaders.length > 0 && (0, _jsxRuntime.jsx)("div", {
|
|
132
|
-
className: 'diff-hunk-headers',
|
|
133
|
-
children: hunkHeaders.map(function (h, i) {
|
|
134
|
-
return (0, _jsxRuntime.jsx)("div", {
|
|
135
|
-
className: 'hunk-header',
|
|
136
|
-
children: h
|
|
137
|
-
}, i);
|
|
138
|
-
})
|
|
139
|
-
}), (0, _jsxRuntime.jsxs)("div", {
|
|
140
|
-
className: 'diff-panes',
|
|
141
|
-
children: [(0, _jsxRuntime.jsx)("div", {
|
|
142
|
-
ref: leftRef,
|
|
143
|
-
className: 'diff-pane diff-old'
|
|
144
|
-
}), (0, _jsxRuntime.jsx)("div", {
|
|
145
|
-
ref: rightRef,
|
|
146
|
-
className: 'diff-pane diff-new'
|
|
147
|
-
})]
|
|
148
|
-
})]
|
|
10
|
+
var _ref$oldCode = _ref.oldCode,
|
|
11
|
+
oldCode = _ref$oldCode === void 0 ? '' : _ref$oldCode,
|
|
12
|
+
_ref$newCode = _ref.newCode,
|
|
13
|
+
newCode = _ref$newCode === void 0 ? '' : _ref$newCode;
|
|
14
|
+
return (0, _jsxRuntime.jsx)(_pandora.Code, {
|
|
15
|
+
mode: 'diff',
|
|
16
|
+
prevData: oldCode,
|
|
17
|
+
newData: newCode
|
|
149
18
|
});
|
|
150
19
|
};
|
|
151
|
-
function escapeHtml(str) {
|
|
152
|
-
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
153
|
-
}
|
|
154
20
|
var _default = exports["default"] = DiffViewer;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './index.less';
|
|
3
|
+
export interface DiffViewerProps {
|
|
4
|
+
oldCode: string | undefined;
|
|
5
|
+
newCode: string | undefined;
|
|
6
|
+
oldFileName?: string;
|
|
7
|
+
newFileName?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const DiffViewer: React.FC<DiffViewerProps>;
|
|
10
|
+
export default DiffViewer;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// DiffViewer.tsx
|
|
4
|
+
import { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { diffWordsWithSpace, createTwoFilesPatch } from 'diff';
|
|
6
|
+
import './index.less';
|
|
7
|
+
var DiffViewer = function DiffViewer(_ref) {
|
|
8
|
+
var oldCode = _ref.oldCode,
|
|
9
|
+
newCode = _ref.newCode,
|
|
10
|
+
_ref$oldFileName = _ref.oldFileName,
|
|
11
|
+
oldFileName = _ref$oldFileName === void 0 ? 'old.js' : _ref$oldFileName,
|
|
12
|
+
_ref$newFileName = _ref.newFileName,
|
|
13
|
+
newFileName = _ref$newFileName === void 0 ? 'new.js' : _ref$newFileName;
|
|
14
|
+
var leftRef = useRef(null);
|
|
15
|
+
var rightRef = useRef(null);
|
|
16
|
+
var isSyncing = useRef(false);
|
|
17
|
+
var _useState = useState('--- (unknown)'),
|
|
18
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
19
|
+
oldFileHeader = _useState2[0],
|
|
20
|
+
setOldFileHeader = _useState2[1];
|
|
21
|
+
var _useState3 = useState([]),
|
|
22
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
23
|
+
hunkHeaders = _useState4[0],
|
|
24
|
+
setHunkHeaders = _useState4[1];
|
|
25
|
+
// 提取文件头和 hunk headers(安全依赖)
|
|
26
|
+
useEffect(function () {
|
|
27
|
+
var safeOldStr = typeof oldCode === 'string' ? oldCode : '';
|
|
28
|
+
var safeNewStr = typeof newCode === 'string' ? newCode : '';
|
|
29
|
+
var patch = createTwoFilesPatch(oldFileName, newFileName, safeOldStr, safeNewStr, '', '', {
|
|
30
|
+
context: 10
|
|
31
|
+
});
|
|
32
|
+
var lines = patch.split('\n');
|
|
33
|
+
// for (const line of lines) {
|
|
34
|
+
// if (line.startsWith('--- ')) {
|
|
35
|
+
// setOldFileHeader(line);
|
|
36
|
+
// break;
|
|
37
|
+
// }
|
|
38
|
+
// }
|
|
39
|
+
setHunkHeaders(lines.filter(function (l) {
|
|
40
|
+
return l.startsWith('@@ ') && l.endsWith(' @@');
|
|
41
|
+
}));
|
|
42
|
+
}, [oldCode, newCode, oldFileName, newFileName]);
|
|
43
|
+
// 主 diff 渲染逻辑
|
|
44
|
+
useEffect(function () {
|
|
45
|
+
var safeOld = (typeof oldCode === 'string' ? oldCode : '').split('\n');
|
|
46
|
+
var safeNew = (typeof newCode === 'string' ? newCode : '').split('\n');
|
|
47
|
+
var maxLines = Math.max(safeOld.length, safeNew.length);
|
|
48
|
+
var leftHtml = [];
|
|
49
|
+
var rightHtml = [];
|
|
50
|
+
for (var i = 0; i < maxLines; i++) {
|
|
51
|
+
var _safeOld$i, _safeNew$i;
|
|
52
|
+
var oldLine = (_safeOld$i = safeOld[i]) !== null && _safeOld$i !== void 0 ? _safeOld$i : '';
|
|
53
|
+
var newLine = (_safeNew$i = safeNew[i]) !== null && _safeNew$i !== void 0 ? _safeNew$i : '';
|
|
54
|
+
var diffs = diffWordsWithSpace(oldLine, newLine);
|
|
55
|
+
var leftContent = '';
|
|
56
|
+
var rightContent = '';
|
|
57
|
+
// ✅ 检查最后一个 token 是否是行尾空格且被删除
|
|
58
|
+
var lastToken = diffs[diffs.length - 1];
|
|
59
|
+
var ignoreLastTrailingSpace = diffs.length > 0 && (lastToken === null || lastToken === void 0 ? void 0 : lastToken.removed) && /^\s+$/.test(lastToken.value) && oldLine.endsWith(lastToken.value);
|
|
60
|
+
for (var j = 0; j < diffs.length; j++) {
|
|
61
|
+
var part = diffs[j];
|
|
62
|
+
var escaped = escapeHtml(part.value);
|
|
63
|
+
if (part.added) {
|
|
64
|
+
rightContent += "<span class=\"diff-add\">".concat(escaped, "</span>");
|
|
65
|
+
} else if (part.removed) {
|
|
66
|
+
// ✅ 如果是最后一个 token 且是行尾空格 → 不高亮
|
|
67
|
+
if (ignoreLastTrailingSpace && j === diffs.length - 1) {
|
|
68
|
+
leftContent += escaped; // 显示空格但不标红
|
|
69
|
+
// rightContent 不加(因为新行没有)
|
|
70
|
+
} else {
|
|
71
|
+
leftContent += "<span class=\"diff-del\">".concat(escaped, "</span>");
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
leftContent += escaped;
|
|
75
|
+
rightContent += escaped;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// 处理缺失行
|
|
79
|
+
if (i >= safeOld.length) leftContent = ' ';
|
|
80
|
+
if (i >= safeNew.length) rightContent = ' ';
|
|
81
|
+
leftHtml.push("<div class=\"diff-line\">".concat(leftContent, "</div>"));
|
|
82
|
+
rightHtml.push("<div class=\"diff-line\">".concat(rightContent, "</div>"));
|
|
83
|
+
}
|
|
84
|
+
var timer = setTimeout(function () {
|
|
85
|
+
if (leftRef.current) leftRef.current.innerHTML = leftHtml.join('');
|
|
86
|
+
if (rightRef.current) rightRef.current.innerHTML = rightHtml.join('');
|
|
87
|
+
}, 0);
|
|
88
|
+
return function () {
|
|
89
|
+
return clearTimeout(timer);
|
|
90
|
+
};
|
|
91
|
+
}, [oldCode, newCode]);
|
|
92
|
+
// 同步滚动
|
|
93
|
+
useEffect(function () {
|
|
94
|
+
var left = leftRef.current;
|
|
95
|
+
var right = rightRef.current;
|
|
96
|
+
if (!left || !right) return;
|
|
97
|
+
var sync = function sync(src, dst) {
|
|
98
|
+
return function () {
|
|
99
|
+
if (isSyncing.current) return;
|
|
100
|
+
isSyncing.current = true;
|
|
101
|
+
dst.scrollTop = src.scrollTop;
|
|
102
|
+
dst.scrollLeft = src.scrollLeft;
|
|
103
|
+
requestAnimationFrame(function () {
|
|
104
|
+
isSyncing.current = false;
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
var onLeft = sync(left, right);
|
|
109
|
+
var onRight = sync(right, left);
|
|
110
|
+
left.addEventListener('scroll', onLeft, {
|
|
111
|
+
passive: true
|
|
112
|
+
});
|
|
113
|
+
right.addEventListener('scroll', onRight, {
|
|
114
|
+
passive: true
|
|
115
|
+
});
|
|
116
|
+
return function () {
|
|
117
|
+
left.removeEventListener('scroll', onLeft);
|
|
118
|
+
right.removeEventListener('scroll', onRight);
|
|
119
|
+
};
|
|
120
|
+
}, []);
|
|
121
|
+
return _jsxs("div", {
|
|
122
|
+
className: 'diff-viewer',
|
|
123
|
+
children: [hunkHeaders.length > 0 && _jsx("div", {
|
|
124
|
+
className: 'diff-hunk-headers',
|
|
125
|
+
children: hunkHeaders.map(function (h, i) {
|
|
126
|
+
return _jsx("div", {
|
|
127
|
+
className: 'hunk-header',
|
|
128
|
+
children: h
|
|
129
|
+
}, i);
|
|
130
|
+
})
|
|
131
|
+
}), _jsxs("div", {
|
|
132
|
+
className: 'diff-panes',
|
|
133
|
+
children: [_jsx("div", {
|
|
134
|
+
ref: leftRef,
|
|
135
|
+
className: 'diff-pane diff-old'
|
|
136
|
+
}), _jsx("div", {
|
|
137
|
+
ref: rightRef,
|
|
138
|
+
className: 'diff-pane diff-new'
|
|
139
|
+
})]
|
|
140
|
+
})]
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
function escapeHtml(str) {
|
|
144
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
145
|
+
}
|
|
146
|
+
export default DiffViewer;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import './index.less';
|
|
3
2
|
export interface DiffViewerProps {
|
|
4
|
-
oldCode: string
|
|
5
|
-
newCode: string
|
|
6
|
-
oldFileName?: string;
|
|
7
|
-
newFileName?: string;
|
|
3
|
+
oldCode: string;
|
|
4
|
+
newCode: string;
|
|
8
5
|
}
|
|
9
6
|
declare const DiffViewer: React.FC<DiffViewerProps>;
|
|
10
7
|
export default DiffViewer;
|
|
@@ -1,146 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
// DiffViewer.tsx
|
|
4
|
-
import { useEffect, useRef, useState } from 'react';
|
|
5
|
-
import { diffWordsWithSpace, createTwoFilesPatch } from 'diff';
|
|
6
|
-
import './index.less';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Code } from '@gingkoo/pandora';
|
|
7
3
|
var DiffViewer = function DiffViewer(_ref) {
|
|
8
|
-
var oldCode = _ref.oldCode,
|
|
9
|
-
|
|
10
|
-
_ref$
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var isSyncing = useRef(false);
|
|
17
|
-
var _useState = useState('--- (unknown)'),
|
|
18
|
-
_useState2 = _slicedToArray(_useState, 2),
|
|
19
|
-
oldFileHeader = _useState2[0],
|
|
20
|
-
setOldFileHeader = _useState2[1];
|
|
21
|
-
var _useState3 = useState([]),
|
|
22
|
-
_useState4 = _slicedToArray(_useState3, 2),
|
|
23
|
-
hunkHeaders = _useState4[0],
|
|
24
|
-
setHunkHeaders = _useState4[1];
|
|
25
|
-
// 提取文件头和 hunk headers(安全依赖)
|
|
26
|
-
useEffect(function () {
|
|
27
|
-
var safeOldStr = typeof oldCode === 'string' ? oldCode : '';
|
|
28
|
-
var safeNewStr = typeof newCode === 'string' ? newCode : '';
|
|
29
|
-
var patch = createTwoFilesPatch(oldFileName, newFileName, safeOldStr, safeNewStr, '', '', {
|
|
30
|
-
context: 10
|
|
31
|
-
});
|
|
32
|
-
var lines = patch.split('\n');
|
|
33
|
-
// for (const line of lines) {
|
|
34
|
-
// if (line.startsWith('--- ')) {
|
|
35
|
-
// setOldFileHeader(line);
|
|
36
|
-
// break;
|
|
37
|
-
// }
|
|
38
|
-
// }
|
|
39
|
-
setHunkHeaders(lines.filter(function (l) {
|
|
40
|
-
return l.startsWith('@@ ') && l.endsWith(' @@');
|
|
41
|
-
}));
|
|
42
|
-
}, [oldCode, newCode, oldFileName, newFileName]);
|
|
43
|
-
// 主 diff 渲染逻辑
|
|
44
|
-
useEffect(function () {
|
|
45
|
-
var safeOld = (typeof oldCode === 'string' ? oldCode : '').split('\n');
|
|
46
|
-
var safeNew = (typeof newCode === 'string' ? newCode : '').split('\n');
|
|
47
|
-
var maxLines = Math.max(safeOld.length, safeNew.length);
|
|
48
|
-
var leftHtml = [];
|
|
49
|
-
var rightHtml = [];
|
|
50
|
-
for (var i = 0; i < maxLines; i++) {
|
|
51
|
-
var _safeOld$i, _safeNew$i;
|
|
52
|
-
var oldLine = (_safeOld$i = safeOld[i]) !== null && _safeOld$i !== void 0 ? _safeOld$i : '';
|
|
53
|
-
var newLine = (_safeNew$i = safeNew[i]) !== null && _safeNew$i !== void 0 ? _safeNew$i : '';
|
|
54
|
-
var diffs = diffWordsWithSpace(oldLine, newLine);
|
|
55
|
-
var leftContent = '';
|
|
56
|
-
var rightContent = '';
|
|
57
|
-
// ✅ 检查最后一个 token 是否是行尾空格且被删除
|
|
58
|
-
var lastToken = diffs[diffs.length - 1];
|
|
59
|
-
var ignoreLastTrailingSpace = diffs.length > 0 && (lastToken === null || lastToken === void 0 ? void 0 : lastToken.removed) && /^\s+$/.test(lastToken.value) && oldLine.endsWith(lastToken.value);
|
|
60
|
-
for (var j = 0; j < diffs.length; j++) {
|
|
61
|
-
var part = diffs[j];
|
|
62
|
-
var escaped = escapeHtml(part.value);
|
|
63
|
-
if (part.added) {
|
|
64
|
-
rightContent += "<span class=\"diff-add\">".concat(escaped, "</span>");
|
|
65
|
-
} else if (part.removed) {
|
|
66
|
-
// ✅ 如果是最后一个 token 且是行尾空格 → 不高亮
|
|
67
|
-
if (ignoreLastTrailingSpace && j === diffs.length - 1) {
|
|
68
|
-
leftContent += escaped; // 显示空格但不标红
|
|
69
|
-
// rightContent 不加(因为新行没有)
|
|
70
|
-
} else {
|
|
71
|
-
leftContent += "<span class=\"diff-del\">".concat(escaped, "</span>");
|
|
72
|
-
}
|
|
73
|
-
} else {
|
|
74
|
-
leftContent += escaped;
|
|
75
|
-
rightContent += escaped;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// 处理缺失行
|
|
79
|
-
if (i >= safeOld.length) leftContent = ' ';
|
|
80
|
-
if (i >= safeNew.length) rightContent = ' ';
|
|
81
|
-
leftHtml.push("<div class=\"diff-line\">".concat(leftContent, "</div>"));
|
|
82
|
-
rightHtml.push("<div class=\"diff-line\">".concat(rightContent, "</div>"));
|
|
83
|
-
}
|
|
84
|
-
var timer = setTimeout(function () {
|
|
85
|
-
if (leftRef.current) leftRef.current.innerHTML = leftHtml.join('');
|
|
86
|
-
if (rightRef.current) rightRef.current.innerHTML = rightHtml.join('');
|
|
87
|
-
}, 0);
|
|
88
|
-
return function () {
|
|
89
|
-
return clearTimeout(timer);
|
|
90
|
-
};
|
|
91
|
-
}, [oldCode, newCode]);
|
|
92
|
-
// 同步滚动
|
|
93
|
-
useEffect(function () {
|
|
94
|
-
var left = leftRef.current;
|
|
95
|
-
var right = rightRef.current;
|
|
96
|
-
if (!left || !right) return;
|
|
97
|
-
var sync = function sync(src, dst) {
|
|
98
|
-
return function () {
|
|
99
|
-
if (isSyncing.current) return;
|
|
100
|
-
isSyncing.current = true;
|
|
101
|
-
dst.scrollTop = src.scrollTop;
|
|
102
|
-
dst.scrollLeft = src.scrollLeft;
|
|
103
|
-
requestAnimationFrame(function () {
|
|
104
|
-
isSyncing.current = false;
|
|
105
|
-
});
|
|
106
|
-
};
|
|
107
|
-
};
|
|
108
|
-
var onLeft = sync(left, right);
|
|
109
|
-
var onRight = sync(right, left);
|
|
110
|
-
left.addEventListener('scroll', onLeft, {
|
|
111
|
-
passive: true
|
|
112
|
-
});
|
|
113
|
-
right.addEventListener('scroll', onRight, {
|
|
114
|
-
passive: true
|
|
115
|
-
});
|
|
116
|
-
return function () {
|
|
117
|
-
left.removeEventListener('scroll', onLeft);
|
|
118
|
-
right.removeEventListener('scroll', onRight);
|
|
119
|
-
};
|
|
120
|
-
}, []);
|
|
121
|
-
return _jsxs("div", {
|
|
122
|
-
className: 'diff-viewer',
|
|
123
|
-
children: [hunkHeaders.length > 0 && _jsx("div", {
|
|
124
|
-
className: 'diff-hunk-headers',
|
|
125
|
-
children: hunkHeaders.map(function (h, i) {
|
|
126
|
-
return _jsx("div", {
|
|
127
|
-
className: 'hunk-header',
|
|
128
|
-
children: h
|
|
129
|
-
}, i);
|
|
130
|
-
})
|
|
131
|
-
}), _jsxs("div", {
|
|
132
|
-
className: 'diff-panes',
|
|
133
|
-
children: [_jsx("div", {
|
|
134
|
-
ref: leftRef,
|
|
135
|
-
className: 'diff-pane diff-old'
|
|
136
|
-
}), _jsx("div", {
|
|
137
|
-
ref: rightRef,
|
|
138
|
-
className: 'diff-pane diff-new'
|
|
139
|
-
})]
|
|
140
|
-
})]
|
|
4
|
+
var _ref$oldCode = _ref.oldCode,
|
|
5
|
+
oldCode = _ref$oldCode === void 0 ? '' : _ref$oldCode,
|
|
6
|
+
_ref$newCode = _ref.newCode,
|
|
7
|
+
newCode = _ref$newCode === void 0 ? '' : _ref$newCode;
|
|
8
|
+
return _jsx(Code, {
|
|
9
|
+
mode: 'diff',
|
|
10
|
+
prevData: oldCode,
|
|
11
|
+
newData: newCode
|
|
141
12
|
});
|
|
142
13
|
};
|
|
143
|
-
function escapeHtml(str) {
|
|
144
|
-
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
145
|
-
}
|
|
146
14
|
export default DiffViewer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gingkoo/pandora-metabase",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.76",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/es/index.js",
|
|
6
6
|
"module": "lib/es/index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@gingkoo/pandora": "1.2.
|
|
30
|
+
"@gingkoo/pandora": "^1.2.20-alpha.1",
|
|
31
31
|
"@gingkoo/pandora-hooks": "1.0.5",
|
|
32
32
|
"@gingkoo/pandora-icons": "0.0.1-alpha.28",
|
|
33
33
|
"@types/diff": "^8.0.0",
|