From ca13ccad9c1abbe9d043678dea4f5cc43455a3a6 Mon Sep 17 00:00:00 2001 From: stephansama Date: Tue, 10 Mar 2026 14:11:02 -0400 Subject: [PATCH 1/5] feat(word-wrap): added word wrap to code viewer --- app/components/Code/Viewer.vue | 53 ++++++++++++++++++- .../v/[version]/[...filePath].vue | 12 +++++ i18n/locales/en.json | 3 +- i18n/schema.json | 3 ++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index 90bcf0c22..ec544b6e8 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -3,6 +3,7 @@ const props = defineProps<{ html: string lines: number selectedLines: { start: number; end: number } | null + wordWrap?: boolean }>() const emit = defineEmits<{ @@ -10,6 +11,7 @@ const emit = defineEmits<{ }>() const codeRef = useTemplateRef('codeRef') +const lineNumbersRef = useTemplateRef('lineNumbersRef') // Generate line numbers array const lineNumbers = computed(() => { @@ -32,6 +34,30 @@ function onLineClick(lineNum: number, event: MouseEvent) { emit('lineClick', lineNum, event) } +// Synchronize line number heights with code line heights (needed for word wrap) +function syncLineHeights() { + if (!props.wordWrap || !codeRef.value || !lineNumbersRef.value) { + // Reset heights if word wrap is disabled + if (lineNumbersRef.value) { + const nums = lineNumbersRef.value.querySelectorAll('.line-number') + nums.forEach(num => (num.style.height = '')) + } + return + } + + const lines = codeRef.value.querySelectorAll('code > .line') + const nums = lineNumbersRef.value.querySelectorAll('.line-number') + + lines.forEach((line, index) => { + const num = nums[index] + if (num) { + // Use getBoundingClientRect for more precision if needed, but offsetHeight is usually enough + const height = line.offsetHeight + num.style.height = `${height}px` + } + }) +} + // Apply highlighting to code lines when selection changes function updateLineHighlighting() { if (!codeRef.value) return @@ -53,11 +79,27 @@ function updateLineHighlighting() { watch( () => [props.selectedLines, props.html] as const, () => { - nextTick(updateLineHighlighting) + nextTick(() => { + updateLineHighlighting() + syncLineHeights() + }) }, { immediate: true }, ) +// Also watch wordWrap specifically +watch( + () => props.wordWrap, + () => { + nextTick(syncLineHeights) + }, +) + +// Sync on resize +if (import.meta.client) { + useEventListener(window, 'resize', syncLineHeights) +} + // Use Nuxt's `navigateTo` for the rendered import links function handleImportLinkNavigate() { if (!codeRef.value) return @@ -86,9 +128,10 @@ watch( diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 00ba413a5..f35de882f 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -792,7 +792,8 @@ "code": "code" }, "file_path": "File path", - "scroll_to_top": "Scroll to top" + "scroll_to_top": "Scroll to top", + "word_wrap": "Word wrap" }, "badges": { "provenance": { diff --git a/i18n/schema.json b/i18n/schema.json index e3c3c0a4b..9a7a0b1b9 100644 --- a/i18n/schema.json +++ b/i18n/schema.json @@ -2382,6 +2382,9 @@ }, "scroll_to_top": { "type": "string" + }, + "word_wrap": { + "type": "string" } }, "additionalProperties": false From e14b94f96f8829df3ba882d6e0e31db409018961 Mon Sep 17 00:00:00 2001 From: stephansama Date: Tue, 17 Mar 2026 05:58:45 -0400 Subject: [PATCH 2/5] chore: made toggle icon switch between modes --- .../v/[version]/[...filePath].vue | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue b/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue index bcf85e8d7..9bf08c6de 100644 --- a/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue +++ b/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue @@ -125,7 +125,9 @@ const { data: fileContent, status: fileStatus, execute: fetchFileContent, -} = useFetch(() => fileContentUrl.value!, { immediate: false }) +} = useFetch(() => fileContentUrl.value!, { + immediate: false, +}) watch( fileContentUrl, @@ -237,7 +239,9 @@ function handleLineClick(lineNum: number, event: MouseEvent) { } // Copy link to current line(s) -const { copied: permalinkCopied, copy: copyPermalink } = useClipboard({ copiedDuring: 2000 }) +const { copied: permalinkCopied, copy: copyPermalink } = useClipboard({ + copiedDuring: 2000, +}) function copyPermalinkUrl() { const url = new URL(window.location.href) copyPermalink(url.toString()) @@ -424,7 +428,8 @@ defineOgImageComponent('Default', { :class="{ 'bg-accent/10 text-accent border-accent/20': wordWrap }" @click="wordWrap = !wordWrap" > - + + {{ $t('code.word_wrap') }}