Skip to content

Commit 3bafb67

Browse files
committed
feat: fixed codewrap lines, but with a perfromance hit. TBD:fix
1 parent a201a36 commit 3bafb67

File tree

1 file changed

+39
-43
lines changed

1 file changed

+39
-43
lines changed

app/components/Code/Viewer.vue

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ const emit = defineEmits<{
1111
1212
const codeRef = useTemplateRef('codeRef')
1313
14-
// Using this so we can track the height of each line, and therefore compute digit sidebar
14+
const LINE_HEIGHT_PX = 24
1515
const lineMultipliers = ref<number[]>([])
16-
const LINE_HEIGHT_PX = 24 // also used in css
1716
1817
function updateLineMultipliers() {
1918
if (!codeRef.value) return
20-
const lines = Array.from(codeRef.value.querySelectorAll('code > .line'))
21-
lineMultipliers.value = lines.map(line =>
22-
Math.max(1, Math.round(parseFloat(getComputedStyle(line).height) / LINE_HEIGHT_PX)),
23-
)
19+
const lines = codeRef.value.querySelectorAll<HTMLElement>('code > .line')
20+
const result: number[] = Array.from({ length: lines.length })
21+
for (let i = 0; i < lines.length; i++)
22+
result[i] = Math.max(1, Math.round(lines[i]!.offsetHeight / LINE_HEIGHT_PX))
23+
lineMultipliers.value = result
2424
}
2525
2626
watch(
@@ -30,28 +30,40 @@ watch(
3030
)
3131
useResizeObserver(codeRef, updateLineMultipliers)
3232
33-
// Line numbers ++ blank rows for the wrapped lines
34-
const displayLines = computed(() => {
35-
const result: (number | null)[] = []
36-
for (let i = 0; i < props.lines; i++) {
37-
result.push(i + 1)
38-
const extra = (lineMultipliers.value[i] ?? 1) - 1
39-
for (let j = 0; j < extra; j++) result.push(null)
40-
}
41-
return result
42-
})
43-
4433
const lineDigits = computed(() => String(props.lines).length)
4534
46-
// Check if a line is selected
4735
function isLineSelected(lineNum: number): boolean {
4836
if (!props.selectedLines) return false
4937
return lineNum >= props.selectedLines.start && lineNum <= props.selectedLines.end
5038
}
5139
52-
// Handle line number click
53-
function onLineClick(lineNum: number, event: MouseEvent) {
54-
emit('lineClick', lineNum, event)
40+
const lineNumbersHtml = computed(() => {
41+
const multipliers = lineMultipliers.value
42+
const total = props.lines
43+
const parts: string[] = []
44+
45+
for (let i = 0; i < total; i++) {
46+
const num = i + 1
47+
const cls = isLineSelected(num)
48+
? 'bg-yellow-500/20 text-fg'
49+
: 'text-fg-subtle hover:text-fg-muted'
50+
parts.push(
51+
`<a id="L${num}" href="#L${num}" tabindex="-1" class="line-number block px-3 py-0 font-mono text-sm leading-6 cursor-pointer transition-colors no-underline ${cls}" data-line="${num}">${num}</a>`,
52+
)
53+
54+
const extra = (multipliers[i] ?? 1) - 1
55+
for (let j = 0; j < extra; j++) parts.push('<span class="block px-3 leading-6">\u00a0</span>')
56+
}
57+
58+
return parts.join('')
59+
})
60+
61+
function onLineNumberClick(event: MouseEvent) {
62+
const target = (event.target as HTMLElement).closest<HTMLAnchorElement>('a[data-line]')
63+
if (!target) return
64+
event.preventDefault()
65+
const lineNum = Number(target.dataset.line)
66+
if (lineNum) emit('lineClick', lineNum, event)
5567
}
5668
5769
// Apply highlighting to code lines when selection changes
@@ -109,31 +121,15 @@ watch(
109121

110122
<template>
111123
<div class="code-viewer flex min-h-full max-w-full" :style="{ '--line-digits': lineDigits }">
112-
<!-- Line numbers column -->
124+
<!-- Line numbers column — raw HTML + event delegation to avoid v-for overhead on large files -->
125+
<!-- eslint-disable vue/no-v-html -->
113126
<div
114127
class="line-numbers shrink-0 bg-bg-subtle border-ie border-solid border-border text-end select-none relative"
115128
aria-hidden="true"
116-
>
117-
<!-- This needs to be a native <a> element, because `LinkBase` (or specifically `NuxtLink`) does not seem to work when trying to prevent default behavior (jumping to the anchor) -->
118-
<template v-for="(lineNum, idx) in displayLines" :key="idx">
119-
<a
120-
v-if="lineNum !== null"
121-
:id="`L${lineNum}`"
122-
:href="`#L${lineNum}`"
123-
tabindex="-1"
124-
class="line-number block px-3 py-0 font-mono text-sm leading-6 cursor-pointer transition-colors no-underline"
125-
:class="[
126-
isLineSelected(lineNum)
127-
? 'bg-yellow-500/20 text-fg'
128-
: 'text-fg-subtle hover:text-fg-muted',
129-
]"
130-
@click.prevent="onLineClick(lineNum, $event)"
131-
>
132-
{{ lineNum }}
133-
</a>
134-
<span v-else class="block px-3 leading-6">&nbsp;</span>
135-
</template>
136-
</div>
129+
v-html="lineNumbersHtml"
130+
@click="onLineNumberClick"
131+
/>
132+
<!-- eslint-enable vue/no-v-html -->
137133

138134
<!-- Code content -->
139135
<div class="code-content">

0 commit comments

Comments
 (0)