From da6f15aa1d6708dacdbc3e63466e81dc3492138d Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 13 Mar 2026 22:41:13 +0100 Subject: [PATCH 1/8] fix: wrap lines and compute width --- app/components/Code/Viewer.vue | 115 ++++++++++-------- .../v/[version]/[...filePath].vue | 23 +++- 2 files changed, 82 insertions(+), 56 deletions(-) diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index 90bcf0c221..ab27fed728 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -86,11 +86,10 @@ watch( + + From 6955710074c0996dce16ec15295a0038104c804d Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 13 Mar 2026 22:50:41 +0100 Subject: [PATCH 2/8] chore: revert to non-nested css --- app/components/Code/Viewer.vue | 102 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index ab27fed728..bd0f304780 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -135,57 +135,57 @@ watch( flex: 1; min-width: 0; max-width: calc(100% - var(--line-numbers-width)); +} + +.code-content:deep(pre) { + margin: 0; + padding: 0; + background: transparent !important; + overflow: visible; + max-width: 100%; +} + +.code-content:deep(code) { + display: block; + padding: 0 1rem; + background: transparent !important; + max-width: 100%; +} + +.code-content:deep(.line) { + display: flex; + flex-wrap: wrap; + /* Ensure consistent height matching line numbers */ + line-height: 24px; + min-height: 24px; + white-space: pre-wrap; + overflow: hidden; + transition: background-color 0.1s; + max-width: 100%; +} + +/* Highlighted lines in code content - extend full width with negative margin */ +.code-content:deep(.line.highlighted) { + @apply bg-yellow-500/20; + margin: 0 -1rem; + padding: 0 1rem; +} + +/* Clickable import links */ +.code-content:deep(.import-link) { + color: inherit; + text-decoration: underline; + text-decoration-style: dotted; + text-decoration-color: rgba(158, 203, 255, 0.5); /* syntax.str with transparency */ + text-underline-offset: 2px; + transition: + text-decoration-color 0.15s, + text-decoration-style 0.15s; + cursor: pointer; +} - &:deep(pre) { - margin: 0; - padding: 0; - background: transparent !important; - overflow: visible; - max-width: 100%; - } - - &:deep(code) { - display: block; - padding: 0 1rem; - background: transparent !important; - max-width: 100%; - } - - &:deep(.line) { - display: flex; - flex-wrap: wrap; - /* Ensure consistent height matching line numbers */ - line-height: 24px; - min-height: 24px; - white-space: pre-wrap; - overflow: hidden; - transition: background-color 0.1s; - max-width: 100%; - } - - /* Highlighted lines in code content - extend full width with negative margin */ - &:deep(.line.highlighted) { - @apply bg-yellow-500/20; - margin: 0 -1rem; - padding: 0 1rem; - } - - /* Clickable import links */ - &:deep(.import-link) { - color: inherit; - text-decoration: underline; - text-decoration-style: dotted; - text-decoration-color: rgba(158, 203, 255, 0.5); /* syntax.str with transparency */ - text-underline-offset: 2px; - transition: - text-decoration-color 0.15s, - text-decoration-style 0.15s; - cursor: pointer; - } - - &:deep(.import-link:hover) { - text-decoration-style: solid; - text-decoration-color: #9ecbff; /* syntax.str - light blue */ - } +.code-content:deep(.import-link:hover) { + text-decoration-style: solid; + text-decoration-color: #9ecbff; /* syntax.str - light blue */ } From 9e08913fa74354256b4cf462ef5c9d71443637d5 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 13 Mar 2026 22:52:40 +0100 Subject: [PATCH 3/8] fix: Fix @screen usage --- .../[packageName]/v/[version]/[...filePath].vue | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 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 db41ab7455..fe78e55386 100644 --- a/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue +++ b/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue @@ -588,16 +588,17 @@ defineOgImageComponent('Default', { From a6e9ca5c6e2bcc6b267a46808480e982bfcecc16 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 13 Mar 2026 22:53:53 +0100 Subject: [PATCH 4/8] fix: 100vw calculation err --- .../[[org]]/[packageName]/v/[version]/[...filePath].vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fe78e55386..5d3fb67dd4 100644 --- a/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue +++ b/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue @@ -599,6 +599,6 @@ defineOgImageComponent('Default', { width: var(--sidebar-space); } .file-viewer { - width: calc(100vw - var(--sidebar-space)); + width: calc(100% - var(--sidebar-space)); } From f62fefeeccf774c098b0b2d285303b8185fcb1eb Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 15 Mar 2026 06:00:25 +0100 Subject: [PATCH 5/8] fix: track line digits as well --- app/components/Code/Viewer.vue | 69 ++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index bd0f304780..5e924f27e6 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -11,16 +11,37 @@ const emit = defineEmits<{ const codeRef = useTemplateRef('codeRef') -// Generate line numbers array -const lineNumbers = computed(() => { - return Array.from({ length: props.lines }, (_, i) => i + 1) -}) +// Using this so we can track the height of each line, and therefore compute digit sidebar +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 = Array.from(codeRef.value.querySelectorAll('code > .line')) + lineMultipliers.value = lines + .map(line => Math.round(parseFloat(getComputedStyle(line).height) / 24)) // since each line "row" is 24px high + .filter(m => m > 0) +} + +watch( + () => props.html, + () => nextTick(updateLineMultipliers), + { immediate: true }, +) +useResizeObserver(codeRef, updateLineMultipliers) + +// Line numbers ++ blank rows for the wrapped lines +const displayLines = computed(() => { + const result: (number | null)[] = [] + for (let i = 0; i < props.lines; i++) { + result.push(i + 1) + const extra = (lineMultipliers.value[i] ?? 1) - 1 + for (let j = 0; j < extra; j++) result.push(null) + } + return result }) +const lineDigits = computed(() => String(props.lines).length) + // Check if a line is selected function isLineSelected(lineNum: number): boolean { if (!props.selectedLines) return false @@ -93,22 +114,24 @@ watch( aria-hidden="true" > - - {{ lineNum }} - + From 81c79a9d867f12995db9d90c892f930f8c306dfd Mon Sep 17 00:00:00 2001 From: "jyc.dev" Date: Sat, 14 Mar 2026 06:28:51 +0100 Subject: [PATCH 6/8] fix: always align weekly chart buckets from range end date (#2071) --- app/utils/chart-data-buckets.ts | 12 ++++-------- test/unit/app/utils/chart-data-buckets.spec.ts | 12 ++++++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/utils/chart-data-buckets.ts b/app/utils/chart-data-buckets.ts index 1165c4541f..1cc4a43308 100644 --- a/app/utils/chart-data-buckets.ts +++ b/app/utils/chart-data-buckets.ts @@ -34,17 +34,13 @@ export function buildWeeklyEvolution( if (sorted.length === 0) return [] const rangeStartDate = parseIsoDate(rangeStartIso) + const rangeEndDate = parseIsoDate(rangeEndIso) - // Align from last day with actual data (npm has 1-2 day delay, today is incomplete) - const lastNonZero = sorted.findLast(d => d.value > 0) - const pickerEnd = parseIsoDate(rangeEndIso) - const effectiveEnd = lastNonZero ? parseIsoDate(lastNonZero.day) : pickerEnd - const rangeEndDate = effectiveEnd.getTime() < pickerEnd.getTime() ? effectiveEnd : pickerEnd - - // Group into 7-day buckets from END backwards const buckets = new Map() + for (const item of sorted) { - const offset = Math.floor((rangeEndDate.getTime() - parseIsoDate(item.day).getTime()) / DAY_MS) + const itemDate = parseIsoDate(item.day) + const offset = Math.floor((rangeEndDate.getTime() - itemDate.getTime()) / DAY_MS) if (offset < 0) continue const idx = Math.floor(offset / 7) buckets.set(idx, (buckets.get(idx) ?? 0) + item.value) diff --git a/test/unit/app/utils/chart-data-buckets.spec.ts b/test/unit/app/utils/chart-data-buckets.spec.ts index 8538f18138..141a9688c3 100644 --- a/test/unit/app/utils/chart-data-buckets.spec.ts +++ b/test/unit/app/utils/chart-data-buckets.spec.ts @@ -84,7 +84,7 @@ describe('buildWeeklyEvolution', () => { expect(result[1]!.weekEnd).toBe('2025-03-10') }) - it('aligns from last non-zero data day, ignoring trailing zeros', () => { + it('always aligns from rangeEnd, even with trailing zeros', () => { const daily = [ { day: '2025-03-01', value: 10 }, { day: '2025-03-02', value: 10 }, @@ -99,10 +99,14 @@ describe('buildWeeklyEvolution', () => { const result = buildWeeklyEvolution(daily, '2025-03-01', '2025-03-09') - expect(result).toHaveLength(1) - expect(result[0]!.value).toBe(70) + // Bucket 0: 03-03..03-09 = 50, Bucket 1: 03-01..03-02 (partial, scaled) + expect(result).toHaveLength(2) expect(result[0]!.weekStart).toBe('2025-03-01') - expect(result[0]!.weekEnd).toBe('2025-03-07') + expect(result[0]!.weekEnd).toBe('2025-03-02') + expect(result[0]!.value).toBe(Math.round((20 * 7) / 2)) + expect(result[1]!.weekStart).toBe('2025-03-03') + expect(result[1]!.weekEnd).toBe('2025-03-09') + expect(result[1]!.value).toBe(50) }) it('returns empty array for empty input', () => { From a201a36758039233529493788b8043f779f16971 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 15 Mar 2026 06:16:41 +0100 Subject: [PATCH 7/8] fix: make line-height a constant --- app/components/Code/Viewer.vue | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index 5e924f27e6..2b1338db12 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -13,13 +13,14 @@ const codeRef = useTemplateRef('codeRef') // Using this so we can track the height of each line, and therefore compute digit sidebar const lineMultipliers = ref([]) +const LINE_HEIGHT_PX = 24 // also used in css function updateLineMultipliers() { if (!codeRef.value) return const lines = Array.from(codeRef.value.querySelectorAll('code > .line')) - lineMultipliers.value = lines - .map(line => Math.round(parseFloat(getComputedStyle(line).height) / 24)) // since each line "row" is 24px high - .filter(m => m > 0) + lineMultipliers.value = lines.map(line => + Math.max(1, Math.round(parseFloat(getComputedStyle(line).height) / LINE_HEIGHT_PX)), + ) } watch( @@ -179,8 +180,8 @@ watch( display: flex; flex-wrap: wrap; /* Ensure consistent height matching line numbers */ - line-height: 24px; - min-height: 24px; + line-height: calc(v-bind(LINE_HEIGHT_PX) * 1px); + min-height: calc(v-bind(LINE_HEIGHT_PX) * 1px); white-space: pre-wrap; overflow: hidden; transition: background-color 0.1s; From 3bafb67fc6038ee4246a0864cded7164dd4d43c2 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 15 Mar 2026 08:03:51 +0100 Subject: [PATCH 8/8] feat: fixed codewrap lines, but with a perfromance hit. TBD:fix --- app/components/Code/Viewer.vue | 82 ++++++++++++++++------------------ 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/app/components/Code/Viewer.vue b/app/components/Code/Viewer.vue index 2b1338db12..398585bdba 100644 --- a/app/components/Code/Viewer.vue +++ b/app/components/Code/Viewer.vue @@ -11,16 +11,16 @@ const emit = defineEmits<{ const codeRef = useTemplateRef('codeRef') -// Using this so we can track the height of each line, and therefore compute digit sidebar +const LINE_HEIGHT_PX = 24 const lineMultipliers = ref([]) -const LINE_HEIGHT_PX = 24 // also used in css function updateLineMultipliers() { if (!codeRef.value) return - const lines = Array.from(codeRef.value.querySelectorAll('code > .line')) - lineMultipliers.value = lines.map(line => - Math.max(1, Math.round(parseFloat(getComputedStyle(line).height) / LINE_HEIGHT_PX)), - ) + 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( @@ -30,28 +30,40 @@ watch( ) useResizeObserver(codeRef, updateLineMultipliers) -// Line numbers ++ blank rows for the wrapped lines -const displayLines = computed(() => { - const result: (number | null)[] = [] - for (let i = 0; i < props.lines; i++) { - result.push(i + 1) - const extra = (lineMultipliers.value[i] ?? 1) - 1 - for (let j = 0; j < extra; j++) result.push(null) - } - return result -}) - 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 @@ -109,31 +121,15 @@ watch(