Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions desktop-app/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
<title>Markdown Viewer</title>
<link href="/assets/icon.jpg" rel="icon" type="image/jpg">
<!-- Updated libraries to latest versions with Subresource Integrity (SRI) -->
<link rel="stylesheet" href="/libs/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="/libs/github-markdown.min.css" integrity="sha384-hZuxRjC/Dsr4zEx1JlUhDQqkvqBPp2VLHsgXfnxPq1ULDy1eIdWCiux7nvO1RIZP" crossorigin="anonymous">
<link rel="preload" href="/libs/bootstrap.min.css" as="style" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/libs/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"></noscript>
<link rel="preload" href="/libs/github-markdown.min.css" as="style" integrity="sha384-hZuxRjC/Dsr4zEx1JlUhDQqkvqBPp2VLHsgXfnxPq1ULDy1eIdWCiux7nvO1RIZP" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/libs/github-markdown.min.css" integrity="sha384-hZuxRjC/Dsr4zEx1JlUhDQqkvqBPp2VLHsgXfnxPq1ULDy1eIdWCiux7nvO1RIZP" crossorigin="anonymous"></noscript>
<link rel="preload" href="/libs/bootstrap-icons.min.css" as="style" integrity="sha384-XGjxtQfXaH2tnPFa9x+ruJTuLE3Aa6LhHSWRr1XeTyhezb4abCG4ccI5AkVDxqC+" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/libs/bootstrap-icons.min.css" integrity="sha384-XGjxtQfXaH2tnPFa9x+ruJTuLE3Aa6LhHSWRr1XeTyhezb4abCG4ccI5AkVDxqC+" crossorigin="anonymous"></noscript>
<link rel="stylesheet" href="/styles.css">
Expand Down
104 changes: 71 additions & 33 deletions desktop-app/resources/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -3680,8 +3680,8 @@ document.addEventListener("DOMContentLoaded", function () {
return;
}
const text = markdownEditor.value || '';
const scrollTop = markdownEditor.scrollTop;
const scrollLeft = markdownEditor.scrollLeft;
const scrollTop = cachedScrollTop;
const scrollLeft = cachedScrollLeft;
const fragment = document.createDocumentFragment();
let lastIndex = 0;
findMatches.forEach(function(match, index) {
Expand All @@ -3701,8 +3701,8 @@ document.addEventListener("DOMContentLoaded", function () {

function syncHighlightScroll() {
if (!editorHighlightLayer) return;
editorHighlightLayer.scrollTop = markdownEditor.scrollTop;
editorHighlightLayer.scrollLeft = markdownEditor.scrollLeft;
editorHighlightLayer.scrollTop = cachedScrollTop;
editorHighlightLayer.scrollLeft = cachedScrollLeft;
}

function updateLineNumberGutter(lineCount) {
Expand Down Expand Up @@ -3765,42 +3765,71 @@ document.addEventListener("DOMContentLoaded", function () {
}

const lineCache = new Map();
let lastEditorWidth = 0;
let charWidth = 0;
let maxCharsPerLine = 0;
let cachedPaddingLeft = 10;
let cachedPaddingRight = 10;
let cachedCharWidth = 0;
let cachedLineHeight = 21;
let cachedEditorWidth = 0;
let cachedMaxCharsPerLine = 80;
let cachedScrollTop = 0;
let cachedScrollLeft = 0;
let isGeometryInitialized = false;

function initEditorGeometry() {
if (!markdownEditor) return;
const styles = window.getComputedStyle(markdownEditor);
cachedPaddingLeft = parseFloat(styles.paddingLeft) || 10;
cachedPaddingRight = parseFloat(styles.paddingRight) || 10;

// Measure character width
const testSpan = document.createElement('span');
testSpan.style.fontFamily = styles.fontFamily;
testSpan.style.fontSize = styles.fontSize;
testSpan.style.visibility = 'hidden';
testSpan.style.position = 'absolute';
testSpan.style.whiteSpace = 'pre';
testSpan.textContent = 'a'.repeat(100);
document.body.appendChild(testSpan);
cachedCharWidth = testSpan.getBoundingClientRect().width / 100;
document.body.removeChild(testSpan);

// Calculate line height
const computed = parseFloat(styles.lineHeight);
if (!Number.isNaN(computed)) {
cachedLineHeight = computed;
} else {
const fontSize = parseFloat(styles.fontSize) || 14;
cachedLineHeight = fontSize * 1.5;
}

isGeometryInitialized = true;
lineCache.clear();
}

function refreshEditorWidth() {
if (!markdownEditor) return;
if (!isGeometryInitialized) {
initEditorGeometry();
}
cachedEditorWidth = markdownEditor.clientWidth;
const availableWidth = cachedEditorWidth - cachedPaddingLeft - cachedPaddingRight;
cachedMaxCharsPerLine = Math.max(1, Math.floor(availableWidth / cachedCharWidth));

cachedScrollTop = markdownEditor.scrollTop;
cachedScrollLeft = markdownEditor.scrollLeft;
}

function updateLineNumbers() {
if (!lineNumbers || !markdownEditor) return;
const lines = (markdownEditor.value || '').split('\n');
const lineCount = Math.max(1, lines.length);

const currentWidth = markdownEditor.clientWidth;
const styles = window.getComputedStyle(markdownEditor);
const paddingLeft = parseFloat(styles.paddingLeft) || 10;
const paddingRight = parseFloat(styles.paddingRight) || 10;
const availableWidth = currentWidth - paddingLeft - paddingRight;

// Measure character width exactly once per resize / layout width change
if (currentWidth !== lastEditorWidth || charWidth === 0) {
lineCache.clear();
lastEditorWidth = currentWidth;

const testSpan = document.createElement('span');
testSpan.style.fontFamily = styles.fontFamily;
testSpan.style.fontSize = styles.fontSize;
testSpan.style.visibility = 'hidden';
testSpan.style.position = 'absolute';
testSpan.style.whiteSpace = 'pre';
testSpan.textContent = 'a'.repeat(100);
document.body.appendChild(testSpan);
charWidth = testSpan.getBoundingClientRect().width / 100;
document.body.removeChild(testSpan);

maxCharsPerLine = Math.max(1, Math.floor(availableWidth / charWidth));
if (cachedEditorWidth === 0) {
refreshEditorWidth();
}

updateLineNumberGutter(lineCount);
const lineHeight = getLineHeight(styles);
const lineHeight = cachedLineHeight;

const existingItems = lineNumbers.children;
const existingCount = existingItems.length;
Expand All @@ -3825,7 +3854,7 @@ document.addEventListener("DOMContentLoaded", function () {
const lineText = lines[i];
let wrapHeight = lineCache.get(lineText);
if (wrapHeight === undefined) {
const wrapCount = getWrappedLineCountMonospace(lineText, maxCharsPerLine);
const wrapCount = getWrappedLineCountMonospace(lineText, cachedMaxCharsPerLine);
wrapHeight = wrapCount * lineHeight;
lineCache.set(lineText, wrapHeight);
}
Expand Down Expand Up @@ -3855,7 +3884,7 @@ document.addEventListener("DOMContentLoaded", function () {

function syncLineNumberScroll() {
if (!lineNumbers) return;
lineNumbers.scrollTop = markdownEditor.scrollTop;
lineNumbers.scrollTop = cachedScrollTop;
}

// Class encapsulating Search & Replace Engine
Expand Down Expand Up @@ -4344,6 +4373,7 @@ document.addEventListener("DOMContentLoaded", function () {
const targetScrollTop = Math.max(0, (lineIndex * lineHeight) - (editorHeight / 2) + (lineHeight / 2));

markdownEditor.scrollTop = targetScrollTop;
cachedScrollTop = targetScrollTop;
syncHighlightScroll();
syncLineNumberScroll();
} catch (e) {
Expand Down Expand Up @@ -4938,12 +4968,14 @@ document.addEventListener("DOMContentLoaded", function () {
const previewPercent = 100 - editorWidthPercent;
editorPaneElement.style.flex = `0 0 calc((100% - var(--dock-width, 0px)) * ${editorWidthPercent / 100} - 4px)`;
previewPaneElement.style.flex = `0 0 calc((100% - var(--dock-width, 0px)) * ${previewPercent / 100} - 4px)`;
refreshEditorWidth();
scheduleLineNumberUpdate();
}

function resetPaneWidths() {
editorPaneElement.style.flex = '';
previewPaneElement.style.flex = '';
refreshEditorWidth();
}

function openMobileMenu() {
Expand Down Expand Up @@ -5035,6 +5067,8 @@ document.addEventListener("DOMContentLoaded", function () {

initTabs();
if (loadGlobalState().syncScrollingEnabled === false) toggleSyncScrolling();
initEditorGeometry();
refreshEditorWidth();
updateMobileStats();
updateFindHighlights();
syncHighlightScroll();
Expand Down Expand Up @@ -5066,6 +5100,8 @@ document.addEventListener("DOMContentLoaded", function () {
}

window.addEventListener('resize', () => {
initEditorGeometry();
refreshEditorWidth();
scheduleLineNumberUpdate();
if (window.innerWidth < 1080 && isFrDocked && isFindModalOpen) {
toggleFrDockMode(true);
Expand Down Expand Up @@ -5136,6 +5172,8 @@ document.addEventListener("DOMContentLoaded", function () {
});

editorPane.addEventListener("scroll", function() {
cachedScrollTop = this.scrollTop;
cachedScrollLeft = this.scrollLeft;
syncEditorToPreview();
syncHighlightScroll();
syncLineNumberScroll();
Expand Down
6 changes: 4 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@
<title>Markdown Viewer</title>
<link href="assets/icon.jpg" rel="icon" type="image/jpg">
<!-- Updated libraries to latest versions with Subresource Integrity (SRI) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.3.0/github-markdown.min.css" integrity="sha384-hZuxRjC/Dsr4zEx1JlUhDQqkvqBPp2VLHsgXfnxPq1ULDy1eIdWCiux7nvO1RIZP" crossorigin="anonymous">
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" as="style" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"></noscript>
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.3.0/github-markdown.min.css" as="style" integrity="sha384-hZuxRjC/Dsr4zEx1JlUhDQqkvqBPp2VLHsgXfnxPq1ULDy1eIdWCiux7nvO1RIZP" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.3.0/github-markdown.min.css" integrity="sha384-hZuxRjC/Dsr4zEx1JlUhDQqkvqBPp2VLHsgXfnxPq1ULDy1eIdWCiux7nvO1RIZP" crossorigin="anonymous"></noscript>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" as="style" integrity="sha384-XGjxtQfXaH2tnPFa9x+ruJTuLE3Aa6LhHSWRr1XeTyhezb4abCG4ccI5AkVDxqC+" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" integrity="sha384-XGjxtQfXaH2tnPFa9x+ruJTuLE3Aa6LhHSWRr1XeTyhezb4abCG4ccI5AkVDxqC+" crossorigin="anonymous"></noscript>
<link rel="stylesheet" href="styles.css">
Expand Down
Loading
Loading