// MarkLite — Editor Module: line numbers, cursor, file size, scroll sync (function () { 'use strict'; var ML = window.MarkLite; // ===== Line Numbers ===== function countNewlines(text) { var count = 1; for (var i = 0, len = text.length; i < len; i++) { if (text.charCodeAt(i) === 10) count++; } return count; } ML.updateLineNumbers = function () { var count = countNewlines(ML.editor.value); if (count === ML.lineCountCache) return; ML.lineCountCache = count; if (count > 2000) { ML.renderVirtualLineNumbers(count); } else { var arr = new Array(count); for (var i = 0; i < count; i++) arr[i] = '
' + (i + 1) + '
'; ML.lineNumbers.innerHTML = arr.join(''); } }; ML.renderVirtualLineNumbers = function (totalLines) { var lineHeight = 20.8; var scrollTop = ML.editor.scrollTop; var viewportHeight = ML.editor.clientHeight; var buffer = 20; var startLine = Math.max(0, Math.floor(scrollTop / lineHeight) - buffer); var endLine = Math.min(totalLines, Math.ceil((scrollTop + viewportHeight) / lineHeight) + buffer); var visibleCount = endLine - startLine; var topPadding = startLine * lineHeight; var bottomPadding = (totalLines - endLine) * lineHeight; var arr = new Array(visibleCount + 2); arr[0] = '
'; for (var i = 0; i < visibleCount; i++) arr[i + 1] = '
' + (startLine + i + 1) + '
'; arr[visibleCount + 1] = '
'; ML.lineNumbers.innerHTML = arr.join(''); }; // ===== Cursor Position ===== ML.updateCursorPosition = function () { var text = ML.editor.value; var pos = ML.editor.selectionStart; var textBefore = text.substring(0, pos); var lines = textBefore.split('\n'); ML.statusCursor.textContent = '行 ' + lines.length + ', 列 ' + (lines[lines.length - 1].length + 1); }; // ===== File Size ===== ML.updateFileSize = function () { var bytes = new Blob([ML.editor.value]).size; ML.statusSize.textContent = ML.formatBytes(bytes); ML.statusSizeDivider.classList.add('visible'); }; // ===== Scroll Sync ===== var previewPositions = null; var lineToPreviewMap = null; ML.cachePreviewPositions = function () { previewPositions = []; for (var i = 0; i < ML.preview.children.length; i++) previewPositions.push(ML.preview.children[i].offsetTop); buildLineToPreviewMap(); }; function buildLineToPreviewMap() { if (!previewPositions || previewPositions.length === 0) { lineToPreviewMap = null; return; } var text = ML.editor.value; var lines = text.split('\n'); var totalLines = lines.length; var blockStartLines = []; var inCodeBlock = false; for (var i = 0; i < totalLines; i++) { var trimmed = lines[i].trimStart(); if (trimmed.startsWith('```')) { if (!inCodeBlock) { blockStartLines.push(i); inCodeBlock = true; } else inCodeBlock = false; continue; } if (inCodeBlock) continue; if (trimmed.startsWith('#') || trimmed.startsWith('- ') || trimmed.startsWith('* ') || trimmed.startsWith('+ ') || /^\d+\.\s/.test(trimmed) || trimmed.startsWith('> ') || trimmed.startsWith('|') || trimmed.startsWith('---') || trimmed.startsWith('***') || trimmed.startsWith('___') || (trimmed === '' && i > 0 && lines[i - 1].trim() === '')) { blockStartLines.push(i); } } var uniqueStartsArr = [].concat(_toConsumableArray(new Set(blockStartLines))).sort(function (a, b) { return a - b; }); var uniqueStartsSet = new Set(uniqueStartsArr); lineToPreviewMap = new Float32Array(totalLines); if (uniqueStartsArr.length === 0) { for (var _i = 0; _i < totalLines; _i++) lineToPreviewMap[_i] = (_i / Math.max(1, totalLines - 1)) * previewPositions[previewPositions.length - 1]; return; } var domCount = previewPositions.length; for (var _i2 = 0; _i2 < uniqueStartsArr.length; _i2++) { var lineIdx = uniqueStartsArr[_i2]; var domIdx = Math.min(Math.floor((_i2 / uniqueStartsArr.length) * domCount), domCount - 1); lineToPreviewMap[lineIdx] = previewPositions[domIdx]; } for (var _i3 = 0; _i3 < totalLines; _i3++) { if (lineToPreviewMap[_i3] !== 0 && uniqueStartsSet.has(_i3)) continue; var prevLine = -1, nextLine = -1; for (var j = _i3 - 1; j >= 0; j--) { if (lineToPreviewMap[j] !== 0 || uniqueStartsSet.has(j)) { prevLine = j; break; } } for (var _j = _i3 + 1; _j < totalLines; _j++) { if (lineToPreviewMap[_j] !== 0 || uniqueStartsSet.has(_j)) { nextLine = _j; break; } } if (prevLine === -1 && nextLine === -1) lineToPreviewMap[_i3] = 0; else if (prevLine === -1) lineToPreviewMap[_i3] = lineToPreviewMap[nextLine]; else if (nextLine === -1) lineToPreviewMap[_i3] = lineToPreviewMap[prevLine]; else { var ratio = (_i3 - prevLine) / (nextLine - prevLine); lineToPreviewMap[_i3] = lineToPreviewMap[prevLine] + ratio * (lineToPreviewMap[nextLine] - lineToPreviewMap[prevLine]); } } } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } return Array.from(arr); } ML.syncScroll = function () { if (ML.viewMode !== 'split') return; var lineHeight = parseFloat(getComputedStyle(ML.editor).lineHeight) || 20.8; var scrollTop = ML.editor.scrollTop; var topLine = Math.floor(scrollTop / lineHeight); if (!previewPositions || previewPositions.length === 0) ML.cachePreviewPositions(); if (!previewPositions || previewPositions.length === 0) return; if (lineToPreviewMap && topLine >= 0 && topLine < lineToPreviewMap.length) { ML.previewPanel.scrollTop = lineToPreviewMap[topLine]; } else { var totalLines = ML.editor.value.split('\n').length; var ratio = totalLines > 1 ? topLine / (totalLines - 1) : 0; var targetIndex = Math.min(Math.floor(ratio * previewPositions.length), previewPositions.length - 1); if (targetIndex >= 0 && previewPositions[targetIndex] !== undefined) ML.previewPanel.scrollTop = previewPositions[targetIndex]; } }; // ===== Schedule Update ===== ML.scheduleUpdate = function () { if (ML.updateTimer) clearTimeout(ML.updateTimer); ML.updateTimer = setTimeout(function () { ML.renderMarkdown(ML.editor.value); ML.updateLineNumbers(); ML.updateFileSize(); ML.cachePreviewPositions(); }, 150); }; })();