11<script setup lang="ts">
22import { codeToHtml } from ' shiki/bundle/web'
33import type { ComponentInternalInstance , VNode } from ' vue'
4+ import { diffLines , type ChangeObject } from ' diff'
5+ import { transformerNotationDiff } from ' @shikijs/transformers'
46
57const props = defineProps <{
68 issue: { instance: ComponentInternalInstance , vnode: VNode , htmlPreHydration: string | undefined , htmlPostHydration: string | undefined }
@@ -14,63 +16,40 @@ const element = computed(() => props.issue.instance.vnode.el as HTMLElement | un
1416
1517const { highlightElement, inspectElementInEditor, clearHighlight } = useElementHighlighter ()
1618
17- const compact = ref (true )
18- const contextLines = 3
19+ const diffHtml = ref (' ' )
1920
20- function diffSlice(a : string , b : string ) {
21- const la = (a || ' ' ).split (' \n ' )
22- const lb = (b || ' ' ).split (' \n ' )
23- let start = 0
24- while (start < la .length && start < lb .length && la [start ] === lb [start ]) start ++
25- let enda = la .length - 1
26- let endb = lb .length - 1
27- while (enda >= start && endb >= start && la [enda ] === lb [endb ]) {
28- enda --
29- endb --
30- }
31- // If identical, show a small head
32- if (start >= la .length && start >= lb .length ) {
33- return {
34- a: la .slice (0 , Math .min (10 , la .length )).join (' \n ' ),
35- b: lb .slice (0 , Math .min (10 , lb .length )).join (' \n ' ),
36- }
37- }
38- const from = Math .max (0 , start - contextLines )
39- const toA = Math .min (la .length , enda + 1 + contextLines )
40- const toB = Math .min (lb .length , endb + 1 + contextLines )
41- return {
42- a: la .slice (from , toA ).join (' \n ' ),
43- b: lb .slice (from , toB ).join (' \n ' ),
44- }
21+ async function render(pre : string , post : string ) {
22+ const diff = diffLines (pre , post , { stripTrailingCr: true , ignoreNewlineAtEof: true })
23+ diffHtml .value = await codeToHtml (generateDiffHtml (diff ), {
24+ theme: ' github-dark' , lang: ' html' , transformers: [
25+ transformerNotationDiff (),
26+ ],
27+ })
4528}
4629
47- const preHtml = ref (' ' )
48- const postHtml = ref (' ' )
49-
50- async function render(pre : string , post : string ) {
51- const preOut = await codeToHtml (pre , { theme: ' github-dark' , lang: ' html' })
52- const postOut = await codeToHtml (post , { theme: ' github-dark' , lang: ' html' })
53- preHtml .value = preOut
54- postHtml .value = postOut
30+ function generateDiffHtml(change : ChangeObject <string >[]) {
31+ return change .map ((part ) => {
32+ if (part .added ) {
33+ return ` // [!code ++]\n ${part .value } `
34+ }
35+ else if (part .removed ) {
36+ return ` // [!code --]\n ${part .value } `
37+ }
38+ else {
39+ return part .value
40+ }
41+ }).join (' ' )
5542}
5643
5744const fullPre = computed (() => props .issue .htmlPreHydration ?? ' ' )
5845const fullPost = computed (() => props .issue .htmlPostHydration ?? ' ' )
5946
60- watchEffect (async () => {
61- const pre = fullPre .value
62- const post = fullPost .value
63- if (compact .value ) {
64- const { a, b } = diffSlice (pre , post )
65- await render (a , b )
66- }
67- else {
68- await render (pre , post )
69- }
70- })
47+ watch ([fullPre , fullPost ], ([newPre , newPost ]) => {
48+ render (newPre , newPost )
49+ }, { immediate: true })
7150
7251function copy(text : string ) {
73- navigator .clipboard ?.writeText (text ).catch (() => {})
52+ navigator .clipboard ?.writeText (text ).catch (() => { })
7453}
7554 </script >
7655
@@ -110,17 +89,6 @@ function copy(text: string) {
11089 class =" text-lg"
11190 />
11291 </n-button >
113- <n-button
114- size =" small"
115- quaternary
116- @click =" compact = !compact"
117- >
118- <Icon
119- name =" material-symbols:compare-arrows"
120- class =" text-lg"
121- />
122- <span class =" ml-1" >{{ compact ? 'Show full' : 'Compact' }}</span >
123- </n-button >
12492 <n-button
12593 size =" small"
12694 quaternary
@@ -146,25 +114,21 @@ function copy(text: string) {
146114 </div >
147115 </div >
148116
149- <div class =" grid mt-3 gap-2 grid-cols-2" >
150- <div >
151- <div class =" text-xs text-neutral-500 mb-1" >
152- Pre Hydration
153- </div >
154- <div
155- class =" w-full overflow-auto"
156- v-html =" preHtml"
157- />
158- </div >
159- <div >
160- <div class =" text-xs text-neutral-500 mb-1" >
161- Post Hydration
162- </div >
163- <div
164- class =" w-full overflow-auto"
165- v-html =" postHtml"
166- />
167- </div >
168- </div >
117+ <div
118+ class =" w-full mt-3 overflow-auto rounded-lg"
119+ v-html =" diffHtml"
120+ />
169121 </n-card >
170122</template >
123+
124+ <style lang="scss" scoped>
125+ :deep(.diff ) {
126+ & .add {
127+ background-color : rgba (22 , 163 , 74 , 0.15 ); // green-600 at 15% opacity
128+ }
129+
130+ & .remove {
131+ background-color : rgba (220 , 38 , 38 , 0.15 ); // red-600 at 15% opacity
132+ }
133+ }
134+ </style >
0 commit comments