mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-07 21:15:03 +00:00
fix: bidirectional jump in slide mode (#1873)
Some checks are pending
tinymist::ci / Duplicate Actions Detection (push) Waiting to run
tinymist::ci / Check Clippy, Formatting, Completion, Documentation, and Tests (Linux) (push) Waiting to run
tinymist::ci / Check Minimum Rust version and Tests (Windows) (push) Waiting to run
tinymist::ci / E2E Tests (darwin-arm64 on macos-latest) (push) Blocked by required conditions
tinymist::ci / E2E Tests (linux-x64 on ubuntu-22.04) (push) Blocked by required conditions
tinymist::ci / E2E Tests (linux-x64 on ubuntu-latest) (push) Blocked by required conditions
tinymist::ci / E2E Tests (win32-x64 on windows-2022) (push) Blocked by required conditions
tinymist::ci / E2E Tests (win32-x64 on windows-latest) (push) Blocked by required conditions
tinymist::ci / prepare-build (push) Waiting to run
tinymist::ci / build-binary (push) Blocked by required conditions
tinymist::ci / build-vsc-assets (push) Blocked by required conditions
tinymist::ci / build-vscode (push) Blocked by required conditions
tinymist::ci / build-vscode-others (push) Blocked by required conditions
tinymist::ci / publish-vscode (push) Blocked by required conditions
tinymist::gh_pages / build-gh-pages (push) Waiting to run
Some checks are pending
tinymist::ci / Duplicate Actions Detection (push) Waiting to run
tinymist::ci / Check Clippy, Formatting, Completion, Documentation, and Tests (Linux) (push) Waiting to run
tinymist::ci / Check Minimum Rust version and Tests (Windows) (push) Waiting to run
tinymist::ci / E2E Tests (darwin-arm64 on macos-latest) (push) Blocked by required conditions
tinymist::ci / E2E Tests (linux-x64 on ubuntu-22.04) (push) Blocked by required conditions
tinymist::ci / E2E Tests (linux-x64 on ubuntu-latest) (push) Blocked by required conditions
tinymist::ci / E2E Tests (win32-x64 on windows-2022) (push) Blocked by required conditions
tinymist::ci / E2E Tests (win32-x64 on windows-latest) (push) Blocked by required conditions
tinymist::ci / prepare-build (push) Waiting to run
tinymist::ci / build-binary (push) Blocked by required conditions
tinymist::ci / build-vsc-assets (push) Blocked by required conditions
tinymist::ci / build-vscode (push) Blocked by required conditions
tinymist::ci / build-vscode-others (push) Blocked by required conditions
tinymist::ci / publish-vscode (push) Blocked by required conditions
tinymist::gh_pages / build-gh-pages (push) Waiting to run
This commit is contained in:
parent
32bd813be6
commit
2b7482b263
9 changed files with 157 additions and 99 deletions
|
@ -96,7 +96,7 @@ export function previewActivate(context: vscode.ExtensionContext, isCompat: bool
|
|||
);
|
||||
|
||||
const launchBrowsingPreview = launch("webview", "doc", { isBrowsing: true });
|
||||
const launchDevPreview = launch("webview", "doc", { isDev: true });
|
||||
const launchDevPreview = (mode: "doc" | "slide") => launch("webview", mode, { isDev: true });
|
||||
// Registers preview commands, check `package.json` for descriptions.
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand("tinymist.browsingPreview", launchBrowsingPreview),
|
||||
|
@ -104,16 +104,19 @@ export function previewActivate(context: vscode.ExtensionContext, isCompat: bool
|
|||
vscode.commands.registerCommand("typst-preview.browser", launch("browser", "doc")),
|
||||
vscode.commands.registerCommand("typst-preview.preview-slide", launch("webview", "slide")),
|
||||
vscode.commands.registerCommand("typst-preview.browser-slide", launch("browser", "slide")),
|
||||
vscode.commands.registerCommand("typst-preview.eject", isCompat ? ejectPreviewPanelCompat : ejectPreviewPanelLsp),
|
||||
vscode.commands.registerCommand("tinymist.previewDev", launchDevPreview),
|
||||
vscode.commands.registerCommand(
|
||||
"typst-preview.revealDocument",
|
||||
isCompat ? revealDocumentCompat : revealDocumentLsp,
|
||||
),
|
||||
vscode.commands.registerCommand(
|
||||
"typst-preview.sync",
|
||||
isCompat ? panelSyncScrollCompat : panelSyncScrollLsp,
|
||||
),
|
||||
vscode.commands.registerCommand("tinymist.previewDev", launchDevPreview("doc")),
|
||||
vscode.commands.registerCommand("tinymist.previewDevSlide", launchDevPreview("slide")),
|
||||
...(isCompat
|
||||
? [
|
||||
vscode.commands.registerCommand("typst-preview.eject", ejectPreviewPanelCompat),
|
||||
vscode.commands.registerCommand("typst-preview.revealDocument", revealDocumentCompat),
|
||||
vscode.commands.registerCommand("typst-preview.sync", panelSyncScrollCompat),
|
||||
]
|
||||
: [
|
||||
vscode.commands.registerCommand("typst-preview.eject", ejectPreviewPanelLsp),
|
||||
vscode.commands.registerCommand("typst-preview.revealDocument", revealDocumentLsp),
|
||||
vscode.commands.registerCommand("typst-preview.sync", panelSyncScrollLsp),
|
||||
]),
|
||||
vscode.commands.registerCommand("tinymist.doInspectPreviewState", () => {
|
||||
const tasks = Array.from(activeTask.values()).map((t) => {
|
||||
return {
|
||||
|
@ -186,7 +189,12 @@ export function previewActivate(context: vscode.ExtensionContext, isCompat: bool
|
|||
};
|
||||
}
|
||||
|
||||
async function launchForURI(uri: vscode.Uri, kind: "browser" | "webview", mode: "doc" | "slide", opts?: LaunchOpts) {
|
||||
async function launchForURI(
|
||||
uri: vscode.Uri,
|
||||
kind: "browser" | "webview",
|
||||
mode: "doc" | "slide",
|
||||
opts?: LaunchOpts,
|
||||
) {
|
||||
const doc =
|
||||
vscode.workspace.textDocuments.find((doc) => {
|
||||
return doc.uri.toString() === uri.toString();
|
||||
|
|
20
tests/workspaces/slides/touying.typ
Normal file
20
tests/workspaces/slides/touying.typ
Normal file
|
@ -0,0 +1,20 @@
|
|||
#import "@preview/touying:0.6.1": *
|
||||
#import themes.dewdrop: *
|
||||
|
||||
#show: dewdrop-theme.with(aspect-ratio: "16-9")
|
||||
|
||||
= Section 1
|
||||
|
||||
== Subsection 1
|
||||
|
||||
=== Title
|
||||
|
||||
Hello, Touying!
|
||||
|
||||
= Section 2
|
||||
|
||||
== Subsection 2
|
||||
|
||||
=== Title
|
||||
|
||||
Hello, Touying!
|
|
@ -1,5 +1,5 @@
|
|||
import { triggerRipple } from "./typst-animation.mjs";
|
||||
import type { GConstructor, TypstDocumentContext } from "./typst-doc.mjs";
|
||||
import { PreviewMode, type GConstructor, type TypstDocumentContext } from "./typst-doc.mjs";
|
||||
|
||||
const enum SourceMappingType {
|
||||
Text = 0,
|
||||
|
@ -225,5 +225,83 @@ export function provideDebugJumpDoc<TBase extends GConstructor<TypstDocumentCont
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
scrollTo(pageRect: ScrollRect, pageNo: number, innerLeft: number, innerTop: number) {
|
||||
if (this.previewMode === PreviewMode.Slide) {
|
||||
this.setPartialPageNumber(pageNo);
|
||||
return;
|
||||
}
|
||||
|
||||
const windowRoot = document.body || document.firstElementChild;
|
||||
const basePos = windowRoot.getBoundingClientRect();
|
||||
|
||||
const left = innerLeft - basePos.left;
|
||||
const top = innerTop - basePos.top;
|
||||
|
||||
// evaluate window viewport 1vw
|
||||
const pw = window.innerWidth * 0.01;
|
||||
const ph = window.innerHeight * 0.01;
|
||||
|
||||
const xOffsetInnerFix = 7 * pw;
|
||||
const yOffsetInnerFix = 38.2 * ph;
|
||||
|
||||
const xOffset = left - xOffsetInnerFix;
|
||||
const yOffset = top - yOffsetInnerFix;
|
||||
|
||||
const widthOccupied = (100 * 100 * pw) / pageRect.width;
|
||||
|
||||
const pageAdjustLeft = pageRect.left - basePos.left - 5 * pw;
|
||||
const pageAdjust = pageRect.left - basePos.left + pageRect.width - 95 * pw;
|
||||
|
||||
// default single-column or multi-column layout
|
||||
if (widthOccupied >= 90 || widthOccupied < 50) {
|
||||
window.scrollTo({ behavior: "smooth", left: xOffset, top: yOffset });
|
||||
} else {
|
||||
// for double-column layout
|
||||
// console.log('occupied adjustment', widthOccupied, page);
|
||||
|
||||
const xOffsetAdjsut = xOffset > pageAdjust ? pageAdjust : pageAdjustLeft;
|
||||
|
||||
window.scrollTo({ behavior: "smooth", left: xOffsetAdjsut, top: yOffset });
|
||||
}
|
||||
|
||||
// grid ripple for debug vw
|
||||
// triggerRipple(
|
||||
// windowRoot,
|
||||
// svgRect.left + 50 * vw,
|
||||
// svgRect.top + 1 * vh,
|
||||
// "typst-jump-ripple",
|
||||
// "typst-jump-ripple-effect .4s linear",
|
||||
// "green",
|
||||
// );
|
||||
|
||||
// triggerRipple(
|
||||
// windowRoot,
|
||||
// pageRect.left - basePos.left + vw,
|
||||
// pageRect.top - basePos.top + vh,
|
||||
// "typst-jump-ripple",
|
||||
// "typst-jump-ripple-effect .4s linear",
|
||||
// "red",
|
||||
// );
|
||||
|
||||
// triggerRipple(
|
||||
// windowRoot,
|
||||
// pageAdjust,
|
||||
// pageRect.top - basePos.top + vh,
|
||||
// "typst-jump-ripple",
|
||||
// "typst-jump-ripple-effect .4s linear",
|
||||
// "red",
|
||||
// );
|
||||
|
||||
triggerRipple(
|
||||
windowRoot,
|
||||
left,
|
||||
top,
|
||||
"typst-jump-ripple",
|
||||
"typst-jump-ripple-effect .4s linear",
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
type ScrollRect = Pick<DOMRect, "left" | "top" | "width" | "height">;
|
||||
|
|
|
@ -463,6 +463,19 @@ export class TypstDocumentContext<O = any> {
|
|||
addViewportChange() {
|
||||
this.addChangement(["viewport-change", ""]);
|
||||
}
|
||||
|
||||
setPartialPageNumber(page: number): boolean {
|
||||
if (page <= 0 || page > this.kModule.retrievePagesInfo().length) {
|
||||
return false;
|
||||
}
|
||||
this.partialRenderPage = page - 1;
|
||||
this.addViewportChange();
|
||||
return true;
|
||||
}
|
||||
|
||||
getPartialPageNumber(): number {
|
||||
return this.partialRenderPage + 1;
|
||||
}
|
||||
}
|
||||
|
||||
export interface TypstDocument<T> {
|
||||
|
@ -536,16 +549,11 @@ export function provideDoc<T extends TypstDocumentContext>(
|
|||
}
|
||||
|
||||
setPartialPageNumber(page: number): boolean {
|
||||
if (page <= 0 || page > this.kModule.retrievePagesInfo().length) {
|
||||
return false;
|
||||
}
|
||||
this.impl.partialRenderPage = page - 1;
|
||||
this.addViewportChange();
|
||||
return true;
|
||||
return this.impl.setPartialPageNumber(page);
|
||||
}
|
||||
|
||||
getPartialPageNumber(): number {
|
||||
return this.impl.partialRenderPage + 1;
|
||||
return this.impl.getPartialPageNumber();
|
||||
}
|
||||
|
||||
setOutineData(outline: any) {
|
||||
|
|
|
@ -350,7 +350,7 @@ export function provideSvgDoc<
|
|||
const height = Number.parseFloat(elem.getAttribute("data-page-height")!);
|
||||
maxWidth = Math.max(maxWidth, width);
|
||||
return {
|
||||
index,
|
||||
index: mode === PreviewMode.Slide ? this.partialRenderPage : index,
|
||||
elem,
|
||||
width,
|
||||
height,
|
||||
|
|
1
tools/typst-preview-frontend/src/global.d.ts
vendored
1
tools/typst-preview-frontend/src/global.d.ts
vendored
|
@ -8,6 +8,7 @@ interface Window {
|
|||
initTypstSvg(docRoot: SVGElement): void;
|
||||
currentPosition(elem: Element): TypstPosition | undefined;
|
||||
handleTypstLocation(elem: Element, page: number, x: number, y: number);
|
||||
documents: any[];
|
||||
typstWebsocket: WebSocket;
|
||||
}
|
||||
const acquireVsCodeApi: any;
|
||||
|
|
|
@ -9,6 +9,8 @@ import "./styles/outline.css";
|
|||
import { wsMain, PreviewMode } from "./ws";
|
||||
import { setupDrag } from "./drag";
|
||||
|
||||
window.documents = [];
|
||||
|
||||
/// Main entry point of the frontend program.
|
||||
main();
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { triggerRipple } from "typst-dom/typst-animation.mjs";
|
||||
|
||||
// debounce https://stackoverflow.com/questions/23181243/throttling-a-mousemove-event-to-fire-no-more-than-5-times-a-second
|
||||
// ignore fast events, good for capturing double click
|
||||
// @param (callback): function to be run when done
|
||||
|
@ -240,6 +238,7 @@ window.currentPosition = function (elem: Element) {
|
|||
return result;
|
||||
};
|
||||
|
||||
type ScrollRect = Pick<DOMRect, "left" | "top" | "width" | "height">;
|
||||
window.handleTypstLocation = function (elem: Element, pageNo: number, x: number, y: number) {
|
||||
const docRoot = findAncestor(elem, "typst-doc");
|
||||
if (!docRoot) {
|
||||
|
@ -247,76 +246,12 @@ window.handleTypstLocation = function (elem: Element, pageNo: number, x: number,
|
|||
return;
|
||||
}
|
||||
|
||||
type ScrollRect = Pick<DOMRect, "left" | "top" | "width" | "height">;
|
||||
const scrollTo = (pageRect: ScrollRect, innerLeft: number, innerTop: number) => {
|
||||
const windowRoot = document.body || document.firstElementChild;
|
||||
const basePos = windowRoot.getBoundingClientRect();
|
||||
// scrollTo(pageRect: ScrollRect, pageNo: number, innerLeft: number, innerTop: number)
|
||||
|
||||
const left = innerLeft - basePos.left;
|
||||
const top = innerTop - basePos.top;
|
||||
|
||||
// evaluate window viewport 1vw
|
||||
const pw = window.innerWidth * 0.01;
|
||||
const ph = window.innerHeight * 0.01;
|
||||
|
||||
const xOffsetInnerFix = 7 * pw;
|
||||
const yOffsetInnerFix = 38.2 * ph;
|
||||
|
||||
const xOffset = left - xOffsetInnerFix;
|
||||
const yOffset = top - yOffsetInnerFix;
|
||||
|
||||
const widthOccupied = (100 * 100 * pw) / pageRect.width;
|
||||
|
||||
const pageAdjustLeft = pageRect.left - basePos.left - 5 * pw;
|
||||
const pageAdjust = pageRect.left - basePos.left + pageRect.width - 95 * pw;
|
||||
|
||||
// default single-column or multi-column layout
|
||||
if (widthOccupied >= 90 || widthOccupied < 50) {
|
||||
window.scrollTo({ behavior: "smooth", left: xOffset, top: yOffset });
|
||||
} else {
|
||||
// for double-column layout
|
||||
// console.log('occupied adjustment', widthOccupied, page);
|
||||
|
||||
const xOffsetAdjsut = xOffset > pageAdjust ? pageAdjust : pageAdjustLeft;
|
||||
|
||||
window.scrollTo({ behavior: "smooth", left: xOffsetAdjsut, top: yOffset });
|
||||
const scrollTo = (pageRect: ScrollRect, pageNo: number, innerLeft: number, innerTop: number) => {
|
||||
for (const doc of window.documents) {
|
||||
doc.impl.scrollTo(pageRect, pageNo, innerLeft, innerTop);
|
||||
}
|
||||
|
||||
// grid ripple for debug vw
|
||||
// triggerRipple(
|
||||
// windowRoot,
|
||||
// svgRect.left + 50 * vw,
|
||||
// svgRect.top + 1 * vh,
|
||||
// "typst-jump-ripple",
|
||||
// "typst-jump-ripple-effect .4s linear",
|
||||
// "green",
|
||||
// );
|
||||
|
||||
// triggerRipple(
|
||||
// windowRoot,
|
||||
// pageRect.left - basePos.left + vw,
|
||||
// pageRect.top - basePos.top + vh,
|
||||
// "typst-jump-ripple",
|
||||
// "typst-jump-ripple-effect .4s linear",
|
||||
// "red",
|
||||
// );
|
||||
|
||||
// triggerRipple(
|
||||
// windowRoot,
|
||||
// pageAdjust,
|
||||
// pageRect.top - basePos.top + vh,
|
||||
// "typst-jump-ripple",
|
||||
// "typst-jump-ripple-effect .4s linear",
|
||||
// "red",
|
||||
// );
|
||||
|
||||
triggerRipple(
|
||||
windowRoot,
|
||||
left,
|
||||
top,
|
||||
"typst-jump-ripple",
|
||||
"typst-jump-ripple-effect .4s linear",
|
||||
);
|
||||
};
|
||||
|
||||
const renderMode = docRoot.getAttribute("data-render-mode");
|
||||
|
@ -359,7 +294,7 @@ window.handleTypstLocation = function (elem: Element, pageNo: number, x: number,
|
|||
|
||||
console.log("canvas mode jump", left, top, canvasRect, dataWidth, dataHeight, x, y);
|
||||
|
||||
scrollTo(canvasRect, left, top);
|
||||
scrollTo(canvasRect, pageNo, left, top);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -394,7 +329,7 @@ window.handleTypstLocation = function (elem: Element, pageNo: number, x: number,
|
|||
const left = svgRect.left + (x / dataWidth) * svgRect.width;
|
||||
const top = svgRect.top + (y / dataHeight) * svgRect.height;
|
||||
|
||||
scrollTo(pageRect, left, top);
|
||||
scrollTo(pageRect, pageNo, left, top);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -218,6 +218,8 @@ export async function wsMain({ url, previewMode, isContentPreview }: WsArgs) {
|
|||
}
|
||||
|
||||
function setupSocket(svgDoc: TypstDocument): () => void {
|
||||
window.documents.push(svgDoc);
|
||||
|
||||
// todo: reconnect setTimeout(() => setupSocket(svgDoc), 1000);
|
||||
$ws = webSocket<ArrayBuffer>({
|
||||
url,
|
||||
|
@ -249,6 +251,10 @@ export async function wsMain({ url, previewMode, isContentPreview }: WsArgs) {
|
|||
const dispose = () => {
|
||||
disposed = true;
|
||||
svgDoc.dispose();
|
||||
const index = window.documents.indexOf(svgDoc);
|
||||
if (index >= 0) {
|
||||
window.documents.splice(index, 1);
|
||||
}
|
||||
for (const sub of subsribes.splice(0, subsribes.length)) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue