5050 </template >
5151 </Navbar >
5252 <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+ >
5471 <div
55- class =" inline-flex items-center gap-1"
5672 id =" toggleScrollInSyncSection"
73+ class =" inline-flex items-center gap-1"
5774 >
5875 <label
5976 for =" toggleScrollInSync"
6986 @click =" toggleInSyncScroll"
7087 />
7188 </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 >
72139 </section >
73140 <section
74141 class =" flex items-stretch w-full gap-4 font-mono text-gray-800 dark:text-gray-50"
78145 {{ lhsLabel }}
79146 </p >
80147 <div
148+ id =" lhsDiff"
81149 class =" relative flex-1 px-4 py-2 border-2 rounded-md dark:border-gray-500 line-number-gutter min-h-80"
82150 :class =" {
83151 'overflow-y-auto max-h-screen--nav': !isSrollInSyncEnabled,
103171 {{ rhsLabel }}
104172 </p >
105173 <div
174+ id =" rhsDiff"
106175 class =" relative flex-1 px-4 py-2 border-2 rounded-md dark:border-gray-500 line-number-gutter min-h-80"
107176 :class =" {
108177 'overflow-y-auto max-h-screen--nav': !isSrollInSyncEnabled,
@@ -135,6 +204,47 @@ import { undoUrlSafeBase64, escapeHtml } from '../../helpers/utils'
135204export default {
136205 layout: ' main' ,
137206 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+ }
138248 return {
139249 lhsDiff: this .lhsDiff ,
140250 rhsDiff: this .rhsDiff ,
@@ -147,43 +257,15 @@ export default {
147257 }
148258 },
149259 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+ )
187269 const maxLineCount =
188270 this .lhsDiff .length > this .rhsDiff .length
189271 ? this .lhsDiff .length
@@ -194,7 +276,6 @@ export default {
194276 )
195277 const { default: Driver } = await import (' driver.js' )
196278 const driver = new Driver ({
197- // ...driverJSConfig(this.$isDarkMode),
198279 closeBtnText: ' Skip' ,
199280 className: ' dark:filter dark:invert' ,
200281 stageBackground: this .isDarkMode
@@ -205,7 +286,6 @@ export default {
205286 ' isSkipScrollInSyncTutorial=true; max-age=31536000; path=/;'
206287 },
207288 })
208- // Define the steps for introduction
209289 if (! this .isSkipScrollInSyncTutorial ) {
210290 driver .defineSteps ([
211291 {
@@ -215,6 +295,22 @@ export default {
215295 description: ' Now you can choose to scroll both sides in sync.' ,
216296 },
217297 },
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+ },
218314 ])
219315 driver .start ()
220316 }
@@ -262,6 +358,26 @@ export default {
262358 toggleInSyncScroll () {
263359 this .isSrollInSyncEnabled = ! this .isSrollInSyncEnabled
264360 },
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+ },
265381 },
266382}
267383 </script >
@@ -292,6 +408,9 @@ export default {
292408 padding-left : calc (var (--line-number-gutter-width ) - 10px );
293409 line-height : 1.65 ;
294410 @apply relative ;
411+ & .selected {
412+ @apply dark :bg- blue- 800 bg- blue- 200;
413+ }
295414 & :hover {
296415 @apply bg-gray-200 dark :bg- gray- 600;
297416 & > span {
0 commit comments