50
50
</template >
51
51
</Navbar >
52
52
<main >
53
- <section class =" flex items-center gap-4 py-4" >
53
+ <section
54
+ class ="
55
+ flex
56
+ items-center
57
+ gap-4
58
+ px-4
59
+ py-2
60
+ mb-4
61
+ sticky
62
+ top-[80px]
63
+ dark:bg-gray-700
64
+ bg-gray-300
65
+ rounded-md
66
+ shadow-sm
67
+ w-full
68
+ z-1
69
+ "
70
+ >
54
71
<div
55
- class =" inline-flex items-center gap-1"
56
72
id =" toggleScrollInSyncSection"
73
+ class =" inline-flex items-center gap-1"
57
74
>
58
75
<label
59
76
for =" toggleScrollInSync"
69
86
@click =" toggleInSyncScroll"
70
87
/>
71
88
</div >
89
+ <div id =" nextDiffSection" class =" inline-flex items-center gap-1" >
90
+ <button
91
+ id =" nextDiff"
92
+ class =" inline-flex items-center justify-center px-1 py-1 text-sm text-gray-600 transition-transform transform bg-gray-300 border border-gray-800 rounded-sm outline-none dark:border-gray-400 dark:text-white dark:bg-gray-800 align-center focus:ring-4 active:scale-y-75"
93
+ aria-label =" Go to next diff"
94
+ type =" button"
95
+ @click =" goToNextDiff"
96
+ >
97
+ Next diff
98
+ <svg
99
+ class =" w-4 h-4"
100
+ fill =" none"
101
+ stroke =" currentColor"
102
+ viewBox =" 0 0 24 24"
103
+ xmlns =" http://www.w3.org/2000/svg"
104
+ >
105
+ <path
106
+ stroke-linecap =" round"
107
+ stroke-linejoin =" round"
108
+ stroke-width =" 2"
109
+ d =" M16 17l-4 4m0 0l-4-4m4 4V3"
110
+ ></path >
111
+ </svg >
112
+ </button >
113
+ </div >
114
+ <div id =" prevDiffSection" class =" inline-flex items-center gap-1" >
115
+ <button
116
+ id =" prevDiff"
117
+ class =" inline-flex items-center justify-center px-1 py-1 text-sm text-gray-600 transition-transform transform bg-gray-300 border border-gray-800 rounded-sm outline-none dark:border-gray-400 dark:text-white dark:bg-gray-800 align-center focus:ring-4 active:scale-y-75"
118
+ aria-label =" Go to previous diff"
119
+ type =" button"
120
+ @click =" goToPreviousDiff"
121
+ >
122
+ Previous diff
123
+ <svg
124
+ class =" w-4 h-4"
125
+ fill =" none"
126
+ stroke =" currentColor"
127
+ viewBox =" 0 0 24 24"
128
+ xmlns =" http://www.w3.org/2000/svg"
129
+ >
130
+ <path
131
+ stroke-linecap =" round"
132
+ stroke-linejoin =" round"
133
+ stroke-width =" 2"
134
+ d =" M8 7l4-4m0 0l4 4m-4-4v18"
135
+ ></path >
136
+ </svg >
137
+ </button >
138
+ </div >
72
139
</section >
73
140
<section
74
141
class =" flex items-stretch w-full gap-4 font-mono text-gray-800 dark:text-gray-50"
78
145
{{ lhsLabel }}
79
146
</p >
80
147
<div
148
+ id =" lhsDiff"
81
149
class =" relative flex-1 px-4 py-2 border-2 rounded-md dark:border-gray-500 line-number-gutter min-h-80"
82
150
:class =" {
83
151
'overflow-y-auto max-h-screen--nav': !isSrollInSyncEnabled,
103
171
{{ rhsLabel }}
104
172
</p >
105
173
<div
174
+ id =" rhsDiff"
106
175
class =" relative flex-1 px-4 py-2 border-2 rounded-md dark:border-gray-500 line-number-gutter min-h-80"
107
176
:class =" {
108
177
'overflow-y-auto max-h-screen--nav': !isSrollInSyncEnabled,
@@ -135,6 +204,47 @@ import { undoUrlSafeBase64, escapeHtml } from '../../helpers/utils'
135
204
export default {
136
205
layout: ' main' ,
137
206
data () {
207
+ const _diff = this .$route .hash
208
+ if (_diff) {
209
+ const gunzip = pako .ungzip (
210
+ Buffer .from (undoUrlSafeBase64 (_diff), ' base64' )
211
+ )
212
+ const diffData = JSON .parse (Buffer .from (gunzip).toString (' utf8' ))
213
+ const { diff , lhsLabel , rhsLabel } = diffData
214
+ this .lhsLabel = lhsLabel
215
+ this .rhsLabel = rhsLabel
216
+ this .isSrollInSyncEnabled = true
217
+ this .lhsDiff = diff
218
+ .map ((item ) => {
219
+ const hunkState = item[0 ]
220
+ if (hunkState === - 1 || hunkState === 0 ) {
221
+ const className =
222
+ hunkState === - 1 ? ' isModified bg-red-300 dark:bg-red-500' : ' '
223
+ return ` <span class="break-all inline p-0 m-0 ${ className} ">${ escapeHtml (
224
+ item[1 ]
225
+ )} </span>`
226
+ }
227
+ return false
228
+ })
229
+ .filter (Boolean )
230
+ .join (' ' )
231
+ .split (' \n ' )
232
+ this .rhsDiff = diff
233
+ .map ((item ) => {
234
+ const hunkState = item[0 ]
235
+ if (hunkState === 1 || hunkState === 0 ) {
236
+ const className =
237
+ hunkState === 1 ? ' isModified bg-green-400 dark:bg-green-900' : ' '
238
+ return ` <span class="break-all inline p-0 m-0 ${ className} ">${ escapeHtml (
239
+ item[1 ]
240
+ )} </span>`
241
+ }
242
+ return false
243
+ })
244
+ .filter (Boolean )
245
+ .join (' ' )
246
+ .split (' \n ' )
247
+ }
138
248
return {
139
249
lhsDiff: this .lhsDiff ,
140
250
rhsDiff: this .rhsDiff ,
@@ -147,43 +257,15 @@ export default {
147
257
}
148
258
},
149
259
async mounted () {
150
- const _diff = this .$route .hash
151
- const gunzip = pako .ungzip (Buffer .from (undoUrlSafeBase64 (_diff), ' base64' ))
152
- const diffData = JSON .parse (Buffer .from (gunzip).toString (' utf8' ))
153
- const { diff , lhsLabel , rhsLabel } = diffData
154
- this .lhsLabel = lhsLabel
155
- this .rhsLabel = rhsLabel
156
- this .isSrollInSyncEnabled = true
157
- this .lhsDiff = diff
158
- .map ((item ) => {
159
- const hunkState = item[0 ]
160
- if (hunkState === - 1 || hunkState === 0 ) {
161
- const className =
162
- hunkState === - 1 ? ' isModified bg-red-300 dark:bg-red-500' : ' '
163
- return ` <span class="break-all inline p-0 m-0 ${ className} ">${ escapeHtml (
164
- item[1 ]
165
- )} </span>`
166
- }
167
- return false
168
- })
169
- .filter (Boolean )
170
- .join (' ' )
171
- .split (' \n ' )
172
- this .rhsDiff = diff
173
- .map ((item ) => {
174
- const hunkState = item[0 ]
175
- if (hunkState === 1 || hunkState === 0 ) {
176
- const className =
177
- hunkState === 1 ? ' isModified bg-green-400 dark:bg-green-900' : ' '
178
- return ` <span class="break-all inline p-0 m-0 ${ className} ">${ escapeHtml (
179
- item[1 ]
180
- )} </span>`
181
- }
182
- return false
183
- })
184
- .filter (Boolean )
185
- .join (' ' )
186
- .split (' \n ' )
260
+ this .treeWalker = document .createTreeWalker (
261
+ document .getElementById (' lhsDiff' ),
262
+ NodeFilter .SHOW_ELEMENT ,
263
+ {
264
+ acceptNode : (node ) => {
265
+ return node .classList .contains (' bg-red-200' )
266
+ },
267
+ }
268
+ )
187
269
const maxLineCount =
188
270
this .lhsDiff .length > this .rhsDiff .length
189
271
? this .lhsDiff .length
@@ -194,7 +276,6 @@ export default {
194
276
)
195
277
const { default: Driver } = await import (' driver.js' )
196
278
const driver = new Driver ({
197
- // ...driverJSConfig(this.$isDarkMode),
198
279
closeBtnText: ' Skip' ,
199
280
className: ' dark:filter dark:invert' ,
200
281
stageBackground: this .isDarkMode
@@ -205,7 +286,6 @@ export default {
205
286
' isSkipScrollInSyncTutorial=true; max-age=31536000; path=/;'
206
287
},
207
288
})
208
- // Define the steps for introduction
209
289
if (! this .isSkipScrollInSyncTutorial ) {
210
290
driver .defineSteps ([
211
291
{
@@ -215,6 +295,22 @@ export default {
215
295
description: ' Now you can choose to scroll both sides in sync.' ,
216
296
},
217
297
},
298
+ {
299
+ element: ' #nextDiffSection' ,
300
+ popover: {
301
+ title: ' Travel through diff hunks' ,
302
+ description:
303
+ ' Now you can move between next and previous diff hunks.' ,
304
+ },
305
+ },
306
+ {
307
+ element: ' #prevDiffSection' ,
308
+ popover: {
309
+ title: ' Travel through diff hunks' ,
310
+ description:
311
+ ' Now you can move between next and previous diff hunks.' ,
312
+ },
313
+ },
218
314
])
219
315
driver .start ()
220
316
}
@@ -262,6 +358,26 @@ export default {
262
358
toggleInSyncScroll () {
263
359
this .isSrollInSyncEnabled = ! this .isSrollInSyncEnabled
264
360
},
361
+ goToNextDiff () {
362
+ const currentNode = this .treeWalker .currentNode
363
+ const nextNode = this .treeWalker .nextNode ()
364
+ if (nextNode) {
365
+ currentNode .querySelector (' p' ).classList .remove (' selected' )
366
+ nextNode .focus ()
367
+ nextNode .querySelector (' p' ).classList .add (' selected' )
368
+ nextNode .scrollIntoView ()
369
+ }
370
+ },
371
+ goToPreviousDiff () {
372
+ const currentNode = this .treeWalker .currentNode
373
+ const prevNode = this .treeWalker .previousNode ()
374
+ if (prevNode) {
375
+ currentNode .querySelector (' p' ).classList .remove (' selected' )
376
+ prevNode .focus ()
377
+ prevNode .querySelector (' p' ).classList .add (' selected' )
378
+ prevNode .scrollIntoView ()
379
+ }
380
+ },
265
381
},
266
382
}
267
383
</script >
@@ -292,6 +408,9 @@ export default {
292
408
padding-left : calc (var (--line-number-gutter-width ) - 10px );
293
409
line-height : 1.65 ;
294
410
@apply relative ;
411
+ & .selected {
412
+ @apply dark :bg- blue- 800 bg- blue- 200;
413
+ }
295
414
& :hover {
296
415
@apply bg-gray-200 dark :bg- gray- 600;
297
416
& > span {
0 commit comments