v-code-diff 1.10.0 → 1.12.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "v-code-diff",
3
3
  "type": "module",
4
- "version": "1.10.0",
4
+ "version": "1.12.0",
5
5
  "description": "template component for vue-demi, can dev and build",
6
6
  "license": "MIT",
7
7
  "exports": {
@@ -53,7 +53,8 @@
53
53
  "diff": "^5.1.0",
54
54
  "diff-match-patch": "^1.0.5",
55
55
  "highlight.js": "^11.9.0",
56
- "vue-demi": "^0.14.6"
56
+ "vue-demi": "^0.14.6",
57
+ "vue-i18n": "9"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@antfu/eslint-config": "^2.4.5",
package/src/CodeDiff.vue CHANGED
@@ -3,6 +3,8 @@ import { computed, ref, watch } from 'vue-demi'
3
3
  import { createSplitDiff, createUnifiedDiff } from './utils'
4
4
  import UnifiedViewer from './unified/UnifiedViewer.vue'
5
5
  import SplitViewer from './split/SplitViewer.vue'
6
+ import DownArrowIcon from './icons/DownArrowIcon.vue'
7
+ import UpArrowIcon from './icons/UpArrowIcon.vue'
6
8
 
7
9
  import './style.scss'
8
10
 
@@ -12,6 +14,7 @@ interface Props {
12
14
  language?: string
13
15
  context?: number
14
16
  diffStyle?: 'word' | 'char'
17
+ forceInlineComparison?: boolean
15
18
  outputFormat?: 'line-by-line' | 'side-by-side'
16
19
  trim?: boolean
17
20
  noDiffLineFeed?: boolean
@@ -29,6 +32,7 @@ const props = withDefaults(defineProps<Props>(), {
29
32
  language: 'plaintext',
30
33
  context: 10,
31
34
  diffStyle: 'word',
35
+ forceInlineComparison: false,
32
36
  outputFormat: 'line-by-line',
33
37
  trim: false,
34
38
  noDiffLineFeed: false,
@@ -70,12 +74,41 @@ const newString = computed(() => {
70
74
 
71
75
  const raw = computed(() =>
72
76
  isUnifiedViewer.value
73
- ? createUnifiedDiff(oldString.value, newString.value, props.language, props.diffStyle, props.context, props.ignoreMatchingLines)
74
- : createSplitDiff(oldString.value, newString.value, props.language, props.diffStyle, props.context, props.ignoreMatchingLines),
77
+ ? createUnifiedDiff(oldString.value, newString.value, props.language, props.diffStyle, props.forceInlineComparison, props.context, props.ignoreMatchingLines)
78
+ : createSplitDiff(oldString.value, newString.value, props.language, props.diffStyle, props.forceInlineComparison, props.context, props.ignoreMatchingLines),
75
79
  )
76
80
  const diffChange = ref(raw.value)
77
81
  const isNotChanged = computed(() => diffChange.value.stat.additionsNum === 0 && diffChange.value.stat.deletionsNum === 0)
78
82
 
83
+ const currentDiffIndex = ref(-1)
84
+
85
+ function goToNextDiff() {
86
+ const diffs = document.querySelectorAll('.blob-code-addition')
87
+ if (currentDiffIndex.value < diffs.length - 1) {
88
+ currentDiffIndex.value++
89
+ updateCurrentDiffHighlight(diffs)
90
+ }
91
+ }
92
+
93
+ function goToPrevDiff() {
94
+ const diffs = document.querySelectorAll('.blob-code-addition')
95
+ if (currentDiffIndex.value > 0) {
96
+ currentDiffIndex.value--
97
+ updateCurrentDiffHighlight(diffs)
98
+ }
99
+ }
100
+
101
+ function updateCurrentDiffHighlight(diffs: NodeListOf<Element>) {
102
+ diffs.forEach((diff: { classList: { remove: (arg0: string) => any } }) => diff.classList.remove('current-diff'))
103
+
104
+ const currentDiff = diffs[currentDiffIndex.value]
105
+
106
+ if (currentDiff) {
107
+ currentDiff.classList.add('current-diff')
108
+ currentDiff.scrollIntoView({ behavior: 'smooth', block: 'center' })
109
+ }
110
+ }
111
+
79
112
  watch(() => props, () => {
80
113
  diffChange.value = raw.value
81
114
  emits('diff', {
@@ -97,6 +130,14 @@ watch(() => props, () => {
97
130
  <div class="info-left">{{ filename }}</div>
98
131
  <div class="info-left">{{ newFilename }}</div>
99
132
  </span>
133
+ <span class="diff-commandbar">
134
+ <button class="command-item-button" title="Next Change" @click="goToNextDiff">
135
+ <DownArrowIcon />
136
+ </button>
137
+ <button class="command-item-button" title="Previous Change" @click="goToPrevDiff">
138
+ <UpArrowIcon />
139
+ </button>
140
+ </span>
100
141
  <span v-if="!hideStat" class="diff-stat">
101
142
  <slot name="stat" :stat="diffChange.stat">
102
143
  <span class="diff-stat-added">+{{ diffChange.stat.additionsNum }} additions</span>
@@ -109,6 +150,14 @@ watch(() => props, () => {
109
150
  <span class="info-left">{{ filename }}</span>
110
151
  <span class="info-right">
111
152
  <span style="margin-left: 20px;">{{ newFilename }}</span>
153
+ <span class="diff-commandbar">
154
+ <button class="command-item-button" title="Next Change" @click="goToNextDiff">
155
+ <DownArrowIcon />
156
+ </button>
157
+ <button class="command-item-button" title="Previous Change" @click="goToPrevDiff">
158
+ <UpArrowIcon />
159
+ </button>
160
+ </span>
112
161
  <span v-if="!hideStat" class="diff-stat">
113
162
  <slot name="stat" :stat="diffChange.stat">
114
163
  <span class="diff-stat-added">+{{ diffChange.stat.additionsNum }} additions</span>
@@ -0,0 +1,11 @@
1
+ <script>
2
+ export default {
3
+ name: 'DownArrowIcon',
4
+ }
5
+ </script>
6
+
7
+ <template>
8
+ <svg width="1rem" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
9
+ <path d="M383.6,322.7L278.6,423c-5.8,6-13.7,9-22.4,9c-8.7,0-16.5-3-22.4-9L128.4,322.7c-12.5-11.9-12.5-31.3,0-43.2 c12.5-11.9,32.7-11.9,45.2,0l50.4,48.2v-217c0-16.9,14.3-30.6,32-30.6c17.7,0,32,13.7,32,30.6v217l50.4-48.2 c12.5-11.9,32.7-11.9,45.2,0C396.1,291.4,396.1,310.7,383.6,322.7z" />
10
+ </svg>
11
+ </template>
@@ -0,0 +1,11 @@
1
+ <script>
2
+ export default {
3
+ name: 'UpArrowIcon',
4
+ }
5
+ </script>
6
+
7
+ <template>
8
+ <svg width="1rem" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
9
+ <path d="M128.4,189.3L233.4,89c5.8-6,13.7-9,22.4-9c8.7,0,16.5,3,22.4,9l105.4,100.3c12.5,11.9,12.5,31.3,0,43.2 c-12.5,11.9-32.7,11.9-45.2,0L288,184.4v217c0,16.9-14.3,30.6-32,30.6c-17.7,0-32-13.7-32-30.6v-217l-50.4,48.2 c-12.5,11.9-32.7,11.9-45.2,0C115.9,220.6,115.9,201.3,128.4,189.3z" />
10
+ </svg>
11
+ </template>
package/src/style.scss CHANGED
@@ -15,12 +15,14 @@
15
15
  overflow-y: auto;
16
16
 
17
17
  .file-header {
18
+ position: sticky;
19
+ top: 0;
20
+ z-index: 1;
18
21
  background-color: var(--color-canvas-subtle);
19
22
  border-bottom: 1px solid var(--color-border-default);
20
23
  padding: 8px 16px;
21
24
  font-size: 12px;
22
- font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
23
-
25
+ font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
24
26
  .file-info {
25
27
  display: flex;
26
28
  justify-content: space-between;
@@ -51,6 +53,25 @@
51
53
  color: var(--color-fg-subtle);
52
54
  }
53
55
  }
56
+
57
+ .diff-commandbar {
58
+ margin-left: auto;
59
+ margin-right: 1rem;
60
+
61
+ .command-item-button {
62
+ background-color: transparent;
63
+ color: var(--color-fg-subtle);
64
+ border: none;
65
+
66
+ svg {
67
+ fill: var(--color-fg-subtle);
68
+ }
69
+ }
70
+
71
+ .command-item-button:hover {
72
+ background-color: var(--color-btn-outline-hover-border);
73
+ }
74
+ }
54
75
  }
55
76
  }
56
77
 
@@ -122,6 +143,10 @@
122
143
  background-color: var(--color-diff-blob-addition-word-bg);
123
144
  }
124
145
  }
146
+
147
+ .current-diff {
148
+ border: 1px solid var(--color-border-muted);
149
+ }
125
150
 
126
151
  .blob-code-context,
127
152
  .blob-code-addition,
package/src/utils.ts CHANGED
@@ -170,6 +170,7 @@ export function createSplitDiff(
170
170
  newString: string,
171
171
  language = 'plaintext',
172
172
  diffStyle = 'word',
173
+ forceInlineComparison = false,
173
174
  context = 10,
174
175
  ignoreMatchingLines?: string,
175
176
  ): SplitViewerChange {
@@ -270,9 +271,9 @@ export function createSplitDiff(
270
271
  addNum++
271
272
 
272
273
  const [curLine, nextLine] = [curLines[j], nextLines[j]]
273
-
274
- const leftLine = curLines.length === nextLines.length ? renderWords(nextLine, curLine, diffStyle) : curLine
275
- const rightLine = curLines.length === nextLines.length ? renderWords(curLine, nextLine, diffStyle) : nextLine
274
+ const shouldRenderWords = forceInlineComparison || curLines.length === nextLines.length
275
+ const leftLine = shouldRenderWords ? renderWords(nextLine, curLine, diffStyle) : curLine
276
+ const rightLine = shouldRenderWords ? renderWords(curLine, nextLine, diffStyle) : nextLine
276
277
 
277
278
  // 忽略匹配的行等价于相等
278
279
  const leftDiffType = ignoreRegex?.test(curLine) ? DiffType.EQUAL : DiffType.DELETE
@@ -361,6 +362,7 @@ export function createUnifiedDiff(
361
362
  newString: string,
362
363
  language = 'plaintext',
363
364
  diffStyle = 'word',
365
+ forceInlineComparison = false,
364
366
  context = 10,
365
367
  ignoreMatchingLines?: string,
366
368
  ): UnifiedViewerChange {
@@ -430,7 +432,7 @@ export function createUnifiedDiff(
430
432
  // 处理当前 diff 为删除的情况
431
433
  if (curType === DiffType.DELETE) {
432
434
  // 下一处差异为新增,且删除与新增行数相同时,对每行依次 diff
433
- if (nextType === DiffType.ADD && curLines.length === nextLines.length) {
435
+ if (nextType === DiffType.ADD && (curLines.length === nextLines.length || forceInlineComparison)) {
434
436
  for (let j = 0; j < curLines.length; j++) {
435
437
  const curLine = curLines[j]
436
438
  const nextLine = nextLines[j]