@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.
@@ -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 = '&nbsp;';
88
+ if (i >= safeNew.length) rightContent = '&nbsp;';
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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 | undefined;
5
- newCode: string | undefined;
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 _react = require("react");
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
- 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 = '&nbsp;';
88
- if (i >= safeNew.length) rightContent = '&nbsp;';
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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 = '&nbsp;';
80
+ if (i >= safeNew.length) rightContent = '&nbsp;';
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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 | undefined;
5
- newCode: string | undefined;
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 _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';
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
- 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 = '&nbsp;';
80
- if (i >= safeNew.length) rightContent = '&nbsp;';
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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.74",
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.3",
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",