improve markdown preview scroll sync (#58852)
* improve markdown preview scroll sync
This commit is contained in:
parent
0f4893299a
commit
f8f4d3af30
7 changed files with 112 additions and 11 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -27,6 +27,10 @@ const messaging = createPosterForVsCode(vscode);
|
|||
window.cspAlerter.setPoster(messaging);
|
||||
window.styleLoadingMonitor.setPoster(messaging);
|
||||
|
||||
window.onload = () => {
|
||||
updateImageSizes();
|
||||
};
|
||||
|
||||
onceDocumentLoaded(() => {
|
||||
if (settings.scrollPreviewWithEditor) {
|
||||
setTimeout(() => {
|
||||
|
@ -53,8 +57,32 @@ const onUpdateView = (() => {
|
|||
};
|
||||
})();
|
||||
|
||||
let updateImageSizes = throttle(() => {
|
||||
const imageInfo: { id: string, height: number, width: number }[] = [];
|
||||
let images = document.getElementsByTagName('img');
|
||||
if (images) {
|
||||
let i;
|
||||
for (i = 0; i < images.length; i++) {
|
||||
const img = images[i];
|
||||
|
||||
if (img.classList.contains('loading')) {
|
||||
img.classList.remove('loading');
|
||||
}
|
||||
|
||||
imageInfo.push({
|
||||
id: img.id,
|
||||
height: img.height,
|
||||
width: img.width
|
||||
});
|
||||
}
|
||||
|
||||
messaging.postMessage('cacheImageSizes', imageInfo);
|
||||
}
|
||||
}, 50);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
scrollDisabled = true;
|
||||
updateImageSizes();
|
||||
}, true);
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
|
|
|
@ -33,6 +33,7 @@ export class MarkdownPreview {
|
|||
private forceUpdate = false;
|
||||
private isScrolling = false;
|
||||
private _disposed: boolean = false;
|
||||
private imageInfo: { id: string, width: number, height: number }[] = [];
|
||||
|
||||
public static async revive(
|
||||
webview: vscode.WebviewPanel,
|
||||
|
@ -123,6 +124,10 @@ export class MarkdownPreview {
|
|||
}
|
||||
|
||||
switch (e.type) {
|
||||
case 'cacheImageSizes':
|
||||
this.onCacheImageSizes(e.body);
|
||||
break;
|
||||
|
||||
case 'command':
|
||||
vscode.commands.executeCommand(e.body.command, ...e.body.args);
|
||||
break;
|
||||
|
@ -181,7 +186,8 @@ export class MarkdownPreview {
|
|||
return {
|
||||
resource: this.resource.toString(),
|
||||
locked: this._locked,
|
||||
line: this.line
|
||||
line: this.line,
|
||||
imageInfo: this.imageInfo
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -400,6 +406,10 @@ export class MarkdownPreview {
|
|||
|
||||
vscode.workspace.openTextDocument(this._resource).then(vscode.window.showTextDocument);
|
||||
}
|
||||
|
||||
private async onCacheImageSizes(imageInfo: { id: string, width: number, height: number }[]) {
|
||||
this.imageInfo = imageInfo;
|
||||
}
|
||||
}
|
||||
|
||||
export interface PreviewSettings {
|
||||
|
|
|
@ -79,7 +79,7 @@ export class MarkdownContentProvider {
|
|||
data-strings="${JSON.stringify(previewStrings).replace(/"/g, '"')}"
|
||||
data-state="${JSON.stringify(state || {}).replace(/"/g, '"')}">
|
||||
<script src="${this.extensionResourcePath('pre.js')}" nonce="${nonce}"></script>
|
||||
${this.getStyles(sourceUri, nonce, config)}
|
||||
${this.getStyles(sourceUri, nonce, config, state)}
|
||||
<base href="${markdownDocument.uri.with({ scheme: 'vscode-resource' }).toString(true)}">
|
||||
</head>
|
||||
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
|
||||
|
@ -147,14 +147,30 @@ export class MarkdownContentProvider {
|
|||
</style>`;
|
||||
}
|
||||
|
||||
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration): string {
|
||||
private getImageStabilizerStyles(state?: any) {
|
||||
let ret = '<style>\n';
|
||||
if (state && state.imageInfo) {
|
||||
state.imageInfo.forEach((imgInfo: any) => {
|
||||
ret += `#${imgInfo.id}.loading {
|
||||
height: ${imgInfo.height}px;
|
||||
width: ${imgInfo.width}px;
|
||||
}\n`;
|
||||
});
|
||||
}
|
||||
ret += '</style>\n';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string {
|
||||
const baseStyles = this.contributions.previewStyles
|
||||
.map(resource => `<link rel="stylesheet" type="text/css" href="${resource.toString()}">`)
|
||||
.join('\n');
|
||||
|
||||
return `${baseStyles}
|
||||
${this.getSettingsOverrideStyles(nonce, config)}
|
||||
${this.computeCustomStyleSheetIncludes(resource, config)}`;
|
||||
${this.computeCustomStyleSheetIncludes(resource, config)}
|
||||
${this.getImageStabilizerStyles(state)}`;
|
||||
}
|
||||
|
||||
private getScripts(nonce: string): string {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import { MarkdownIt, Token } from 'markdown-it';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as crypto from 'crypto';
|
||||
import { MarkdownContributions } from './markdownExtensions';
|
||||
import { Slugifier } from './slugify';
|
||||
import { getUriForLinkWithKnownExternalScheme } from './util/links';
|
||||
|
@ -62,6 +63,7 @@ export class MarkdownEngine {
|
|||
this.addLineNumberRenderer(this.md, renderName);
|
||||
}
|
||||
|
||||
this.addImageStabilizer(this.md);
|
||||
this.addFencedRenderer(this.md);
|
||||
|
||||
this.addLinkNormalizer(this.md);
|
||||
|
@ -131,6 +133,28 @@ export class MarkdownEngine {
|
|||
};
|
||||
}
|
||||
|
||||
private addImageStabilizer(md: any): void {
|
||||
const original = md.renderer.rules.image;
|
||||
md.renderer.rules.image = (tokens: any, idx: number, options: any, env: any, self: any) => {
|
||||
const token = tokens[idx];
|
||||
token.attrJoin('class', 'loading');
|
||||
|
||||
const src = token.attrGet('src');
|
||||
if (src) {
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(src);
|
||||
const imgHash = hash.digest('hex');
|
||||
token.attrSet('id', `image-hash-${imgHash}`);
|
||||
}
|
||||
|
||||
if (original) {
|
||||
return original(tokens, idx, options, env, self);
|
||||
} else {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private addFencedRenderer(md: any): void {
|
||||
const original = md.renderer.rules['fenced'];
|
||||
md.renderer.rules['fenced'] = (tokens: any, idx: number, options: any, env: any, self: any) => {
|
||||
|
|
|
@ -308,21 +308,21 @@
|
|||
|
||||
const frame = getActiveFrame();
|
||||
|
||||
// keep current scrollTop around and use later
|
||||
// keep current scrollY around and use later
|
||||
var setInitialScrollPosition;
|
||||
if (firstLoad) {
|
||||
firstLoad = false;
|
||||
setInitialScrollPosition = (body, window) => {
|
||||
if (!isNaN(initData.initialScrollProgress)) {
|
||||
if (body.scrollTop === 0) {
|
||||
if (window.scrollY === 0) {
|
||||
window.scroll(0, body.clientHeight * initData.initialScrollProgress);
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentDocument.body.scrollTop : 0;
|
||||
const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentWindow.scrollY : 0;
|
||||
setInitialScrollPosition = (body, window) => {
|
||||
if (body.scrollTop === 0) {
|
||||
if (window.scrollY === 0) {
|
||||
window.scroll(0, scrollY);
|
||||
}
|
||||
};
|
||||
|
@ -359,7 +359,7 @@
|
|||
var onLoad = (contentDocument, contentWindow) => {
|
||||
if (contentDocument.body) {
|
||||
// Workaround for https://github.com/Microsoft/vscode/issues/12865
|
||||
// check new scrollTop and reset if neccessary
|
||||
// check new scrollY and reset if neccessary
|
||||
setInitialScrollPosition(contentDocument.body, contentWindow);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue