diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index 90bcf0c22..398585bdb 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -11,25 +11,59 @@ const emit = defineEmits<{ const codeRef = useTemplateRef('codeRef') -// Generate line numbers array -const lineNumbers = computed(() => { - return Array.from({ length: props.lines }, (_, i) => i + 1) -}) +const LINE_HEIGHT_PX = 24 +const lineMultipliers = ref([]) -// Used for CSS calculation of line number column width -const lineDigits = computed(() => { - return String(props.lines).length -}) +function updateLineMultipliers() { + if (!codeRef.value) return + const lines = codeRef.value.querySelectorAll('code > .line') + const result: number[] = Array.from({ length: lines.length }) + for (let i = 0; i < lines.length; i++) + result[i] = Math.max(1, Math.round(lines[i]!.offsetHeight / LINE_HEIGHT_PX)) + lineMultipliers.value = result +} + +watch( + () => props.html, + () => nextTick(updateLineMultipliers), + { immediate: true }, +) +useResizeObserver(codeRef, updateLineMultipliers) + +const lineDigits = computed(() => String(props.lines).length) -// Check if a line is selected function isLineSelected(lineNum: number): boolean { if (!props.selectedLines) return false return lineNum >= props.selectedLines.start && lineNum <= props.selectedLines.end } -// Handle line number click -function onLineClick(lineNum: number, event: MouseEvent) { - emit('lineClick', lineNum, event) +const lineNumbersHtml = computed(() => { + const multipliers = lineMultipliers.value + const total = props.lines + const parts: string[] = [] + + for (let i = 0; i < total; i++) { + const num = i + 1 + const cls = isLineSelected(num) + ? 'bg-yellow-500/20 text-fg' + : 'text-fg-subtle hover:text-fg-muted' + parts.push( + `${num}`, + ) + + const extra = (multipliers[i] ?? 1) - 1 + for (let j = 0; j < extra; j++) parts.push('\u00a0') + } + + return parts.join('') +}) + +function onLineNumberClick(event: MouseEvent) { + const target = (event.target as HTMLElement).closest('a[data-line]') + if (!target) return + event.preventDefault() + const lineNum = Number(target.dataset.line) + if (lineNum) emit('lineClick', lineNum, event) } // Apply highlighting to code lines when selection changes @@ -86,36 +120,21 @@ watch(