mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-12-23 08:47:50 +00:00
feat: run prettier correctly (#1893)
This commit is contained in:
parent
0849d6c641
commit
9bb784e6f0
23 changed files with 351 additions and 566 deletions
|
|
@ -8,6 +8,7 @@ target/**
|
|||
dist/**
|
||||
icons/
|
||||
node_modules/
|
||||
editors/vscode/test-dist/
|
||||
editors/vscode/out/
|
||||
editors/vscode/.vscode-test/**
|
||||
*.toml
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
function check() {
|
||||
const cargoToml = readFileSync('../../Cargo.toml', 'utf8');
|
||||
const cargoVersion = cargoToml.match(/version = "(.*?)"/)[1];
|
||||
const pkgVersion = JSON.parse(readFileSync('package.json', 'utf8')).version;
|
||||
const cargoToml = readFileSync("../../Cargo.toml", "utf8");
|
||||
const cargoVersion = cargoToml.match(/version = "(.*?)"/)[1];
|
||||
const pkgVersion = JSON.parse(readFileSync("package.json", "utf8")).version;
|
||||
|
||||
if (cargoVersion !== pkgVersion) {
|
||||
throw new Error(`Version mismatch: ${cargoVersion} (in Cargo.toml) !== ${pkgVersion} (in package.json)`);
|
||||
}
|
||||
if (cargoVersion !== pkgVersion) {
|
||||
throw new Error(
|
||||
`Version mismatch: ${cargoVersion} (in Cargo.toml) !== ${pkgVersion} (in package.json)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
|
|
|
|||
|
|
@ -1,28 +1,24 @@
|
|||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const rimraf = require("rimraf");
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const rimraf = require('rimraf');
|
||||
const vscodeDir = path.join(__dirname, "../");
|
||||
const editorToolsDir = path.join(vscodeDir, "../../tools/editor-tools/");
|
||||
|
||||
const vscodeDir = path.join(__dirname, '../');
|
||||
const editorToolsDir = path.join(vscodeDir, '../../tools/editor-tools/');
|
||||
|
||||
rimraf.sync(path.join(vscodeDir, 'out/editor-tools/'));
|
||||
fs.mkdirSync(path.join(vscodeDir, 'out/editor-tools/'), { recursive: true });
|
||||
rimraf.sync(path.join(vscodeDir, "out/editor-tools/"));
|
||||
fs.mkdirSync(path.join(vscodeDir, "out/editor-tools/"), { recursive: true });
|
||||
|
||||
function copyDir(src, dest) {
|
||||
fs.readdirSync(src).forEach((item) => {
|
||||
const srcPath = path.join(src, item);
|
||||
const destPath = path.join(dest, item);
|
||||
if (fs.lstatSync(srcPath).isDirectory()) {
|
||||
fs.mkdirSync(destPath,
|
||||
{ recursive: true });
|
||||
copyDir(srcPath, destPath);
|
||||
fs.mkdirSync(destPath, { recursive: true });
|
||||
copyDir(srcPath, destPath);
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
}
|
||||
else {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
copyDir(path.join(editorToolsDir, "dist"), path.join(vscodeDir, 'out/editor-tools/'));
|
||||
|
||||
copyDir(path.join(editorToolsDir, "dist"), path.join(vscodeDir, "out/editor-tools/"));
|
||||
|
|
|
|||
|
|
@ -681,4 +681,4 @@ export const revealDocumentCompat = async (args: any) => {
|
|||
|
||||
export const ejectPreviewPanelCompat = async () => {
|
||||
vscode.window.showWarningMessage("Eject is not supported in compat mode");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"tools/typst-preview-frontend"
|
||||
],
|
||||
"scripts": {
|
||||
"fmt": "cargo fmt --all && npx prettier tools/**/*.{js,mjs,cjs,ts,mts,cts} editors/vscode/**/*.{js,mjs,cjs,ts,mts,cts}",
|
||||
"fmt": "cargo fmt --all && npx prettier --write tools/**/*.{js,mjs,cjs,ts,mts,cts} editors/vscode/**/*.{js,mjs,cjs,ts,mts,cts}",
|
||||
"fmt:check": "cargo fmt --check --all && prettier --check tools/**/*.{js,mjs,cjs,ts,mts,cts} editors/vscode/**/*.{js,mjs,cjs,ts,mts,cts}",
|
||||
"build:editor-tools": "cd tools/editor-tools/ && yarn run build",
|
||||
"build:preview": "cd tools/typst-preview-frontend && yarn run build && rimraf ../../crates/tinymist-assets/src/typst-preview.html && cpr ./dist/index.html ../../crates/tinymist-assets/src/typst-preview.html",
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export function startModal(...contents: Node[]) {
|
|||
{
|
||||
class: "tinymist-button",
|
||||
},
|
||||
"Close"
|
||||
"Close",
|
||||
);
|
||||
const keydownHandler = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" || e.key === " " || e.key === "Enter") {
|
||||
|
|
@ -58,15 +58,12 @@ export function startModal(...contents: Node[]) {
|
|||
{
|
||||
class: "tinymist-button",
|
||||
style: "margin-left: 0.5em",
|
||||
title:
|
||||
"Click the close button or press esc/space/enter to close this window",
|
||||
title: "Click the close button or press esc/space/enter to close this window",
|
||||
},
|
||||
"Help"
|
||||
"Help",
|
||||
);
|
||||
help.onclick = () => {
|
||||
alert(
|
||||
"Click the close button or press esc/space/enter to close this window"
|
||||
);
|
||||
alert("Click the close button or press esc/space/enter to close this window");
|
||||
};
|
||||
floatingWindow.appendChild(help);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ export const Diagnostics = () => {
|
|||
`: error occurred in this call of function \`f\``,
|
||||
br(),
|
||||
a({ href: "javascript:void(0)" }, `test.typ(6, 2)`),
|
||||
`: error occurred in this call of function \`g\``
|
||||
)
|
||||
`: error occurred in this call of function \`g\``,
|
||||
),
|
||||
),
|
||||
div(
|
||||
{ class: `tinymist-card`, style: "flex: 1; width: 100%; padding: 10px" },
|
||||
|
|
@ -44,7 +44,7 @@ export const Diagnostics = () => {
|
|||
},
|
||||
`#let f(x, y) = `,
|
||||
span({ style: "text-decoration: underline" }, `x + y`),
|
||||
`;`
|
||||
`;`,
|
||||
),
|
||||
br(),
|
||||
"where ",
|
||||
|
|
@ -61,7 +61,7 @@ export const Diagnostics = () => {
|
|||
"2nd",
|
||||
" function parameter of ",
|
||||
code(a({ href: "javascript:void(0)" }, `f`)),
|
||||
"."
|
||||
".",
|
||||
),
|
||||
div(
|
||||
{
|
||||
|
|
@ -82,7 +82,7 @@ export const Diagnostics = () => {
|
|||
},
|
||||
`#let g(x, y, z) = `,
|
||||
span({ style: "text-decoration: underline" }, `f(x, y)`),
|
||||
` + z;`
|
||||
` + z;`,
|
||||
),
|
||||
br(),
|
||||
"where ",
|
||||
|
|
@ -99,7 +99,7 @@ export const Diagnostics = () => {
|
|||
"2nd",
|
||||
" function parameter of ",
|
||||
code(a({ href: "javascript:void(0)" }, `g`)),
|
||||
"."
|
||||
".",
|
||||
),
|
||||
div(
|
||||
{
|
||||
|
|
@ -120,9 +120,9 @@ export const Diagnostics = () => {
|
|||
},
|
||||
`#`,
|
||||
span({ style: "text-decoration: underline" }, `g(1, left, red)`),
|
||||
`;`
|
||||
)
|
||||
)
|
||||
)
|
||||
`;`,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ export const Docs = () => {
|
|||
const v = parsedDocs.val;
|
||||
// console.log("updated", v);
|
||||
return div(MakeDoc(v));
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -89,9 +89,7 @@ async function recoverDocsStructure(content: string) {
|
|||
let match;
|
||||
let lastIndex = 0;
|
||||
while ((match = reg.exec(content))) {
|
||||
tokenPromises.push(
|
||||
Promise.resolve([TokenKind.Text, content.slice(lastIndex, match.index)])
|
||||
);
|
||||
tokenPromises.push(Promise.resolve([TokenKind.Text, content.slice(lastIndex, match.index)]));
|
||||
tokenPromises.push(identifyCommentToken(match[1]));
|
||||
lastIndex = reg.lastIndex;
|
||||
}
|
||||
|
|
@ -157,8 +155,7 @@ async function recoverDocsStructure(content: string) {
|
|||
};
|
||||
if (sym) {
|
||||
current.id = `${sym.id}-param-${token[1]}`;
|
||||
const renderedParams = (sym.data.renderedParams =
|
||||
sym.data.renderedParams || {});
|
||||
const renderedParams = (sym.data.renderedParams = sym.data.renderedParams || {});
|
||||
renderedParams[current.id] = current;
|
||||
}
|
||||
break;
|
||||
|
|
@ -240,19 +237,11 @@ async function identifyCommentToken(comment: string) {
|
|||
case "end:errors":
|
||||
return [TokenKind.ErrorEnd, cs[1]];
|
||||
case "begin:module":
|
||||
return [
|
||||
TokenKind.ModuleStart,
|
||||
cs[1],
|
||||
JSON.parse(await base64ToUtf8(cs[2])),
|
||||
];
|
||||
return [TokenKind.ModuleStart, cs[1], JSON.parse(await base64ToUtf8(cs[2]))];
|
||||
case "end:module":
|
||||
return [TokenKind.ModuleEnd, cs[1]];
|
||||
case "begin:symbol":
|
||||
return [
|
||||
TokenKind.SymbolStart,
|
||||
cs[1],
|
||||
JSON.parse(await base64ToUtf8(cs[2])),
|
||||
];
|
||||
return [TokenKind.SymbolStart, cs[1], JSON.parse(await base64ToUtf8(cs[2]))];
|
||||
case "end:symbol":
|
||||
return [TokenKind.SymbolEnd, cs[1]];
|
||||
case "begin:sig":
|
||||
|
|
@ -312,7 +301,7 @@ function MakeDoc(root: DocElement) {
|
|||
(e) =>
|
||||
e.namespace === child.data.namespace &&
|
||||
e.name === child.data.name &&
|
||||
e.version === child.data.version
|
||||
e.version === child.data.version,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -494,9 +483,9 @@ function MakeDoc(root: DocElement) {
|
|||
style: "text-decoration: underline",
|
||||
title: `It is inaccessible by paths`,
|
||||
},
|
||||
"Module"
|
||||
"Module",
|
||||
),
|
||||
code(" ", knownFiles[fileLoc]?.path || v.id)
|
||||
code(" ", knownFiles[fileLoc]?.path || v.id),
|
||||
);
|
||||
} else {
|
||||
title.push(span(`Module: ${v.id}`));
|
||||
|
|
@ -505,7 +494,7 @@ function MakeDoc(root: DocElement) {
|
|||
return div(
|
||||
{ class: "tinymist-module" },
|
||||
h1({ id: v.id }, ...(fid ? [span({ id: fid }, ...title)] : title)),
|
||||
ModuleBody(v)
|
||||
ModuleBody(v),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -514,19 +503,14 @@ function MakeDoc(root: DocElement) {
|
|||
return div(
|
||||
h1(`@${v.data.namespace}/${v.data.name}:${v.data.version}`),
|
||||
p(
|
||||
span(
|
||||
"This documentation is generated locally. Please submit issues to "
|
||||
),
|
||||
a(
|
||||
{ href: "https://github.com/Myriad-Dreamin/tinymist/issues" },
|
||||
"tinymist"
|
||||
),
|
||||
span("This documentation is generated locally. Please submit issues to "),
|
||||
a({ href: "https://github.com/Myriad-Dreamin/tinymist/issues" }, "tinymist"),
|
||||
span(" if you see "),
|
||||
strong(i("incorrect")),
|
||||
span(" information in it.")
|
||||
span(" information in it."),
|
||||
),
|
||||
// ModuleBody(v)
|
||||
...v.children.map(Item)
|
||||
...v.children.map(Item),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -546,16 +530,16 @@ function MakeDoc(root: DocElement) {
|
|||
style: "text-decoration: underline",
|
||||
title: `In external package @${extPkg.namespace}/${extPkg.name}:${extPkg.version}`,
|
||||
},
|
||||
"external"
|
||||
"external",
|
||||
)
|
||||
: span(
|
||||
{
|
||||
style: "text-decoration: underline",
|
||||
title: `In local package @${extPkg.namespace}/${extPkg.name}:${extPkg.version}`,
|
||||
},
|
||||
"external"
|
||||
"external",
|
||||
),
|
||||
code(" ", v.data.name)
|
||||
code(" ", v.data.name),
|
||||
);
|
||||
} else {
|
||||
const file = knownFiles[fileLoc?.[0]];
|
||||
|
|
@ -567,9 +551,9 @@ function MakeDoc(root: DocElement) {
|
|||
style: "text-decoration: underline",
|
||||
title: `This module is inaccessible by paths`,
|
||||
},
|
||||
"internal"
|
||||
"internal",
|
||||
),
|
||||
code(" ")
|
||||
code(" "),
|
||||
)
|
||||
: code();
|
||||
|
||||
|
|
@ -579,8 +563,8 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
href: `#${fid}`,
|
||||
},
|
||||
v.data.name
|
||||
)
|
||||
v.data.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -598,8 +582,8 @@ function MakeDoc(root: DocElement) {
|
|||
// <span class="sr-only">View Source</span>
|
||||
// </a>
|
||||
},
|
||||
h3({ class: "doc-symbol-name" }, body)
|
||||
)
|
||||
h3({ class: "doc-symbol-name" }, body),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -618,7 +602,7 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
id: v.id,
|
||||
},
|
||||
code(v.data.name)
|
||||
code(v.data.name),
|
||||
);
|
||||
let funcTitle = [...is_external, name];
|
||||
if (sig) {
|
||||
|
|
@ -652,10 +636,10 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
class: `detail-header doc-symbol-${v.data.kind}`,
|
||||
},
|
||||
h3({ class: "doc-symbol-name" }, code(...funcTitle))
|
||||
h3({ class: "doc-symbol-name" }, code(...funcTitle)),
|
||||
),
|
||||
...SigPreview(v),
|
||||
...(v.data.is_external ? ShortItemDoc(v) : [ItemDoc(v), ...SigDocs(v)])
|
||||
...(v.data.is_external ? ShortItemDoc(v) : [ItemDoc(v), ...SigDocs(v)]),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -728,10 +712,10 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
class: "doc-param-title",
|
||||
},
|
||||
strong(paramTitle)
|
||||
)
|
||||
)
|
||||
)
|
||||
strong(paramTitle),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -751,7 +735,7 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
id: `param-${v.id}-${param.name}`,
|
||||
},
|
||||
param.name
|
||||
param.name,
|
||||
),
|
||||
];
|
||||
if (param.cano_type) {
|
||||
|
|
@ -782,13 +766,13 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
class: "doc-param-title",
|
||||
},
|
||||
strong(code(paramTitle))
|
||||
strong(code(paramTitle)),
|
||||
),
|
||||
div({
|
||||
style: "margin-left: 0.62em",
|
||||
innerHTML: docsAll ? docsAll : "<p>-</p>",
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -826,19 +810,13 @@ function MakeDoc(root: DocElement) {
|
|||
href: v.data.external_link,
|
||||
title: "this symbol is re-exported from other modules",
|
||||
},
|
||||
kwHl("external")
|
||||
kwHl("external"),
|
||||
),
|
||||
code(" "),
|
||||
]
|
||||
: [];
|
||||
|
||||
const sigTitle = [
|
||||
...is_external,
|
||||
kwHl("let"),
|
||||
code(" "),
|
||||
code(fnHl(v.data.name)),
|
||||
code("("),
|
||||
];
|
||||
const sigTitle = [...is_external, kwHl("let"), code(" "), code(fnHl(v.data.name)), code("(")];
|
||||
for (let i = 0; i < paramsAll.length; i++) {
|
||||
if (i > 0) {
|
||||
sigTitle.push(code(", "));
|
||||
|
|
@ -856,8 +834,8 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
href: `#param-${v.id}-${paramsAll[i].param.name}`,
|
||||
},
|
||||
...paramTitle
|
||||
)
|
||||
...paramTitle,
|
||||
),
|
||||
);
|
||||
}
|
||||
sigTitle.push(code(")"));
|
||||
|
|
@ -877,9 +855,9 @@ function MakeDoc(root: DocElement) {
|
|||
{
|
||||
style: "margin: 0 1em",
|
||||
},
|
||||
code(...sigTitle)
|
||||
)
|
||||
)
|
||||
code(...sigTitle),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return res;
|
||||
|
|
@ -896,26 +874,23 @@ function MakeDoc(root: DocElement) {
|
|||
},
|
||||
h3(
|
||||
{ class: "doc-symbol-name" },
|
||||
code(`${v.data.name}`)
|
||||
code(`${v.data.name}`),
|
||||
// code(
|
||||
// {
|
||||
// style: "float: right; line-height: 1em",
|
||||
// },
|
||||
// `${v.data.kind}`
|
||||
// )
|
||||
)
|
||||
),
|
||||
),
|
||||
ItemDoc(v)
|
||||
ItemDoc(v),
|
||||
);
|
||||
}
|
||||
|
||||
return Item(root);
|
||||
}
|
||||
|
||||
function sigTypeHighlighted(
|
||||
inferred: [string, string] | undefined,
|
||||
target: ChildDom[]
|
||||
) {
|
||||
function sigTypeHighlighted(inferred: [string, string] | undefined, target: ChildDom[]) {
|
||||
// todo: determine whether it is inferred
|
||||
// if (types) {
|
||||
// typeHighlighted(types, target);
|
||||
|
|
@ -925,24 +900,20 @@ function sigTypeHighlighted(
|
|||
typeHighlighted(inferred[0], rendered, "|");
|
||||
const infer = span(
|
||||
{ class: "code-kw type-inferred", title: "inferred by type checker" },
|
||||
"infer"
|
||||
"infer",
|
||||
);
|
||||
target.push(
|
||||
code(
|
||||
{ class: "type-inferred" },
|
||||
infer,
|
||||
code(" "),
|
||||
span({ class: "type-inferred-as", title: inferred[1] }, ...rendered)
|
||||
)
|
||||
span({ class: "type-inferred-as", title: inferred[1] }, ...rendered),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function typeHighlighted(
|
||||
types: string,
|
||||
target: ChildDom[],
|
||||
by: RegExp | string = /[|,]/g
|
||||
) {
|
||||
function typeHighlighted(types: string, target: ChildDom[], by: RegExp | string = /[|,]/g) {
|
||||
const type = types.split(by);
|
||||
for (let i = 0; i < type.length; i++) {
|
||||
if (i > 0) {
|
||||
|
|
|
|||
|
|
@ -15,17 +15,13 @@ export const FontView = () => {
|
|||
|
||||
const FontResourcesData = `:[[preview:FontInformation]]:`;
|
||||
const fontResources = van.state<FontResources>(
|
||||
FontResourcesData.startsWith(":")
|
||||
? DOC_MOCK
|
||||
: JSON.parse(base64Decode(FontResourcesData))
|
||||
FontResourcesData.startsWith(":") ? DOC_MOCK : JSON.parse(base64Decode(FontResourcesData)),
|
||||
);
|
||||
console.log("fontResources", fontResources);
|
||||
|
||||
const StyleAtCursorData = `:[[preview:StyleAtCursor]]:`;
|
||||
const lastStylesAtCursor = van.state<any>(
|
||||
StyleAtCursorData.startsWith(":")
|
||||
? undefined
|
||||
: JSON.parse(base64Decode(StyleAtCursorData))
|
||||
StyleAtCursorData.startsWith(":") ? undefined : JSON.parse(base64Decode(StyleAtCursorData)),
|
||||
);
|
||||
console.log("styleAtCursorBase", lastStylesAtCursor);
|
||||
van.derive(() => {
|
||||
|
|
@ -72,12 +68,11 @@ export const FontView = () => {
|
|||
icon: ChildDom,
|
||||
title: string,
|
||||
onclick: (this: HTMLDivElement) => void,
|
||||
opts?: PropsWithKnownKeys<HTMLButtonElement> & { active?: State<boolean> }
|
||||
opts?: PropsWithKnownKeys<HTMLButtonElement> & { active?: State<boolean> },
|
||||
) => {
|
||||
const classProp = opts?.active
|
||||
? van.derive(
|
||||
() =>
|
||||
`tinymist-button tinymist-font-action${opts?.active?.val ? " activated" : ""}`
|
||||
() => `tinymist-button tinymist-font-action${opts?.active?.val ? " activated" : ""}`,
|
||||
)
|
||||
: "tinymist-button tinymist-font-action";
|
||||
|
||||
|
|
@ -89,7 +84,7 @@ export const FontView = () => {
|
|||
title,
|
||||
onclick,
|
||||
},
|
||||
icon
|
||||
icon,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -106,17 +101,15 @@ export const FontView = () => {
|
|||
|
||||
const machineTitle = `Weight ${font.weight || 400}, Stretch ${font.stretch || 1000}, at `;
|
||||
const baseName = code(
|
||||
font.style === "normal" || !font.style
|
||||
? ""
|
||||
: `${humanStyle(font.style)}, `,
|
||||
font.style === "normal" || !font.style ? "" : `${humanStyle(font.style)}, `,
|
||||
(_dom?: Element) => {
|
||||
return span(
|
||||
humanWeight(font.weight, showNumberOpt.val),
|
||||
showNumber.val ? ", " : " ",
|
||||
humanStretch(font.stretch, showNumberOpt.val)
|
||||
humanStretch(font.stretch, showNumberOpt.val),
|
||||
);
|
||||
},
|
||||
` (${fileName})`
|
||||
` (${fileName})`,
|
||||
);
|
||||
|
||||
let variantName;
|
||||
|
|
@ -127,8 +120,7 @@ export const FontView = () => {
|
|||
title = machineTitle + w.path;
|
||||
variantName = a(
|
||||
{
|
||||
style:
|
||||
"font-size: 1.2em; text-decoration: underline; cursor: pointer;",
|
||||
style: "font-size: 1.2em; text-decoration: underline; cursor: pointer;",
|
||||
title,
|
||||
onclick() {
|
||||
if (w.kind === "fs") {
|
||||
|
|
@ -136,7 +128,7 @@ export const FontView = () => {
|
|||
}
|
||||
},
|
||||
},
|
||||
baseName
|
||||
baseName,
|
||||
);
|
||||
} else {
|
||||
title = machineTitle + `Embedded: ${w.name}`;
|
||||
|
|
@ -145,7 +137,7 @@ export const FontView = () => {
|
|||
style: "font-size: 1.2em",
|
||||
title,
|
||||
},
|
||||
baseName
|
||||
baseName,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -184,56 +176,44 @@ export const FontView = () => {
|
|||
div(
|
||||
{ style: "margin: 1.2em; margin-left: 0.5em" },
|
||||
div(
|
||||
FontAction(
|
||||
"Copy",
|
||||
"Copy to clipboard",
|
||||
function (this: HTMLDivElement) {
|
||||
activeMe(this);
|
||||
copyToClipboard(`"${family.name || ""}"`);
|
||||
}
|
||||
),
|
||||
FontAction("Copy", "Copy to clipboard", function (this: HTMLDivElement) {
|
||||
activeMe(this);
|
||||
copyToClipboard(`"${family.name || ""}"`);
|
||||
}),
|
||||
" | ",
|
||||
FontAction(
|
||||
"Paste string",
|
||||
"Paste as String",
|
||||
function (this: HTMLDivElement) {
|
||||
activeMe(this);
|
||||
const rest = name;
|
||||
const markup = `#${rest}`;
|
||||
requestTextEdit({
|
||||
newText: {
|
||||
kind: "by-mode",
|
||||
markup,
|
||||
rest,
|
||||
},
|
||||
});
|
||||
}
|
||||
),
|
||||
FontAction("Paste string", "Paste as String", function (this: HTMLDivElement) {
|
||||
activeMe(this);
|
||||
const rest = name;
|
||||
const markup = `#${rest}`;
|
||||
requestTextEdit({
|
||||
newText: {
|
||||
kind: "by-mode",
|
||||
markup,
|
||||
rest,
|
||||
},
|
||||
});
|
||||
}),
|
||||
" ",
|
||||
FontAction(
|
||||
"#set",
|
||||
"Paste as Set Font Rule",
|
||||
function (this: HTMLDivElement) {
|
||||
activeMe(this);
|
||||
const rest = name;
|
||||
const markup = `#set text(font: ${rest})`;
|
||||
requestTextEdit({
|
||||
newText: {
|
||||
kind: "by-mode",
|
||||
markup,
|
||||
rest,
|
||||
},
|
||||
});
|
||||
}
|
||||
)
|
||||
FontAction("#set", "Paste as Set Font Rule", function (this: HTMLDivElement) {
|
||||
activeMe(this);
|
||||
const rest = name;
|
||||
const markup = `#set text(font: ${rest})`;
|
||||
requestTextEdit({
|
||||
newText: {
|
||||
kind: "by-mode",
|
||||
markup,
|
||||
rest,
|
||||
},
|
||||
});
|
||||
}),
|
||||
),
|
||||
span({ style: "font-size: 1.2em" }, family.name),
|
||||
".",
|
||||
br(),
|
||||
code("Variant"),
|
||||
": ",
|
||||
family.infos.map(FontSlot)
|
||||
)
|
||||
family.infos.map(FontSlot),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -245,7 +225,7 @@ export const FontView = () => {
|
|||
code(fontAtCursor),
|
||||
br(),
|
||||
"Checked at ",
|
||||
code(fontPosition)
|
||||
code(fontPosition),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -257,8 +237,7 @@ export const FontView = () => {
|
|||
div(
|
||||
{
|
||||
class: "flex-col",
|
||||
style:
|
||||
"justify-content: center; align-items: center; gap: 10px; width: 100%;",
|
||||
style: "justify-content: center; align-items: center; gap: 10px; width: 100%;",
|
||||
},
|
||||
div(
|
||||
{
|
||||
|
|
@ -270,24 +249,22 @@ export const FontView = () => {
|
|||
() => {
|
||||
showNumber.val = !showNumber.val;
|
||||
},
|
||||
{ active: showNumber }
|
||||
)
|
||||
{ active: showNumber },
|
||||
),
|
||||
),
|
||||
div(
|
||||
{
|
||||
class: `tinymist-card`,
|
||||
style: "flex: 1; width: 100%; padding: 10px; display: none",
|
||||
},
|
||||
(_dom?: Element) => SelectingSlot()
|
||||
(_dom?: Element) => SelectingSlot(),
|
||||
),
|
||||
...fontResources.val.families.map(FontFamilySlot)
|
||||
)
|
||||
...fontResources.val.families.map(FontFamilySlot),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
export type fontLocation = FontSource extends { kind: infer Kind }
|
||||
? Kind
|
||||
: never;
|
||||
export type fontLocation = FontSource extends { kind: infer Kind } ? Kind : never;
|
||||
|
||||
interface FontInfo {
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ import { base64Decode } from "../utils";
|
|||
// };
|
||||
|
||||
ortEnv.wasm.numThreads = 4;
|
||||
ortEnv.wasm.wasmPaths =
|
||||
"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.17.1/dist/";
|
||||
ortEnv.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.17.1/dist/";
|
||||
|
||||
type Point = [number, number];
|
||||
type Stroke = Point[];
|
||||
|
|
@ -85,7 +84,7 @@ const SYMBOL_MOCK: SymbolInformation = {
|
|||
|
||||
const SearchBar = (
|
||||
state: State<SymbolInformation>,
|
||||
symbolSelected: State<SelectedSymbolItem[] | undefined>
|
||||
symbolSelected: State<SelectedSymbolItem[] | undefined>,
|
||||
) => {
|
||||
const def = MiniSearch.getDefault("tokenize");
|
||||
const search = van.derive(() => {
|
||||
|
|
@ -233,18 +232,15 @@ const CanvasPanel = (strokesState: State<Stroke[] | undefined>) => {
|
|||
startModal(
|
||||
p(
|
||||
"The ",
|
||||
span(
|
||||
{ style: "font-weight: bold; text-decoration: underline" },
|
||||
"offline"
|
||||
),
|
||||
span({ style: "font-weight: bold; text-decoration: underline" }, "offline"),
|
||||
" handwritten stroke recognizer is powered by ",
|
||||
a(
|
||||
{
|
||||
href: "https://github.com/QuarticCat/detypify",
|
||||
},
|
||||
"Detypify"
|
||||
"Detypify",
|
||||
),
|
||||
". Draw a symbol to search for it."
|
||||
". Draw a symbol to search for it.",
|
||||
),
|
||||
h4("Cannot find some symbols?"),
|
||||
p(
|
||||
|
|
@ -253,9 +249,9 @@ const CanvasPanel = (strokesState: State<Stroke[] | undefined>) => {
|
|||
{
|
||||
href: "https://github.com/QuarticCat/detypify/blob/main/assets/supported-symbols.txt",
|
||||
},
|
||||
"supported-symbols.txt"
|
||||
"supported-symbols.txt",
|
||||
),
|
||||
"."
|
||||
".",
|
||||
),
|
||||
p(
|
||||
"❤️🔥: Click the ",
|
||||
|
|
@ -267,9 +263,9 @@ const CanvasPanel = (strokesState: State<Stroke[] | undefined>) => {
|
|||
{
|
||||
href: "https://detypify.quarticcat.com/",
|
||||
},
|
||||
"Detypify"
|
||||
"Detypify",
|
||||
),
|
||||
"."
|
||||
".",
|
||||
),
|
||||
p(
|
||||
"📝: Report the missing symbol to ",
|
||||
|
|
@ -277,9 +273,9 @@ const CanvasPanel = (strokesState: State<Stroke[] | undefined>) => {
|
|||
{
|
||||
href: "https://github.com/QuarticCat/detypify/issues/new",
|
||||
},
|
||||
"GitHub Issues"
|
||||
"GitHub Issues",
|
||||
),
|
||||
"."
|
||||
".",
|
||||
),
|
||||
h4("Like it?"),
|
||||
p(
|
||||
|
|
@ -288,16 +284,16 @@ const CanvasPanel = (strokesState: State<Stroke[] | undefined>) => {
|
|||
{
|
||||
href: "https://github.com/QuarticCat/detypify",
|
||||
},
|
||||
"Detypify"
|
||||
"Detypify",
|
||||
),
|
||||
"!"
|
||||
)
|
||||
"!",
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
HelpIcon()
|
||||
HelpIcon(),
|
||||
),
|
||||
srcCanvas
|
||||
srcCanvas,
|
||||
),
|
||||
button(
|
||||
{
|
||||
|
|
@ -305,8 +301,8 @@ const CanvasPanel = (strokesState: State<Stroke[] | undefined>) => {
|
|||
title: "clear",
|
||||
onclick: drawClear,
|
||||
},
|
||||
"Clear"
|
||||
)
|
||||
"Clear",
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -441,16 +437,14 @@ const CATEGORY_INFO: SymbolCategory[] = [
|
|||
},
|
||||
];
|
||||
// generate map from category value to category name
|
||||
const categoryIndex = new Map(
|
||||
CATEGORY_INFO.map((cat) => [cat.value, cat.name.toLowerCase()])
|
||||
);
|
||||
const categoryIndex = new Map(CATEGORY_INFO.map((cat) => [cat.value, cat.name.toLowerCase()]));
|
||||
|
||||
export const SymbolPicker = () => {
|
||||
const symbolInformationData = `:[[preview:SymbolInformation]]:`;
|
||||
const symInfo = van.state<SymbolInformation>(
|
||||
symbolInformationData.startsWith(":")
|
||||
? SYMBOL_MOCK
|
||||
: JSON.parse(base64Decode(symbolInformationData))
|
||||
: JSON.parse(base64Decode(symbolInformationData)),
|
||||
);
|
||||
console.log("symbolInformation", symInfo);
|
||||
const detypifyPromise = Detypify.create();
|
||||
|
|
@ -473,7 +467,7 @@ export const SymbolPicker = () => {
|
|||
() =>
|
||||
`<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0" viewBox="0 0 0 0" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" style="opacity: 0; position: absolute">
|
||||
${symInfo.val.glyphDefs || ""}
|
||||
</svg>`
|
||||
</svg>`,
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -494,10 +488,7 @@ export const SymbolPicker = () => {
|
|||
};
|
||||
|
||||
const bboxXWidth = diff(primaryGlyph.xMin, primaryGlyph.xMax);
|
||||
let xWidth = Math.max(
|
||||
bboxXWidth,
|
||||
primaryGlyph.xAdvance || fontSelected.unitsPerEm
|
||||
);
|
||||
let xWidth = Math.max(bboxXWidth, primaryGlyph.xAdvance || fontSelected.unitsPerEm);
|
||||
|
||||
let yReal = diff(primaryGlyph.yMin, primaryGlyph.yMax);
|
||||
let yGlobal = primaryGlyph.yAdvance || fontSelected.unitsPerEm;
|
||||
|
|
@ -527,7 +518,7 @@ export const SymbolPicker = () => {
|
|||
// console.log(sym.typstCode, div({ innerHTML: imageData }));
|
||||
maskInfo.setAttribute(
|
||||
"style",
|
||||
`width: ${symWidth}; height: ${symHeight}; -webkit-mask-image: url('data:image/svg+xml;utf8,${encodeURIComponent(imageData)}'); -webkit-mask-size: auto ${symHeight}; -webkit-mask-repeat: no-repeat; transition: background-color 200ms; background-color: currentColor;`
|
||||
`width: ${symWidth}; height: ${symHeight}; -webkit-mask-image: url('data:image/svg+xml;utf8,${encodeURIComponent(imageData)}'); -webkit-mask-size: auto ${symHeight}; -webkit-mask-repeat: no-repeat; transition: background-color 200ms; background-color: currentColor;`,
|
||||
);
|
||||
}, 1);
|
||||
}
|
||||
|
|
@ -557,7 +548,7 @@ export const SymbolPicker = () => {
|
|||
});
|
||||
},
|
||||
},
|
||||
maskInfo
|
||||
maskInfo,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -566,8 +557,8 @@ export const SymbolPicker = () => {
|
|||
div({ style: "font-size: 14px; margin: 8px 0" }, cat.name),
|
||||
div(
|
||||
{ class: "flex-row", style: "flex-wrap: wrap; gap: 5px; width: 100%" },
|
||||
...(cat.symbols || []).map((sym) => sym.elem)
|
||||
)
|
||||
...(cat.symbols || []).map((sym) => sym.elem),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -580,25 +571,21 @@ export const SymbolPicker = () => {
|
|||
value,
|
||||
elem: SymbolCell(value),
|
||||
};
|
||||
})
|
||||
);
|
||||
const filteredPickers = van.state<SelectedSymbolItem[] | undefined>(
|
||||
undefined
|
||||
}),
|
||||
);
|
||||
const filteredPickers = van.state<SelectedSymbolItem[] | undefined>(undefined);
|
||||
|
||||
function pickSymbolsBySearch(
|
||||
pickers: { key: string; value: SymbolItem; elem: Element }[],
|
||||
filteredPickers: SelectedSymbolItem[] | undefined
|
||||
filteredPickers: SelectedSymbolItem[] | undefined,
|
||||
) {
|
||||
if (!filteredPickers) return pickers;
|
||||
return pickers.filter((picker) =>
|
||||
filteredPickers.some((f) => f.typstCode === picker.key)
|
||||
);
|
||||
return pickers.filter((picker) => filteredPickers.some((f) => f.typstCode === picker.key));
|
||||
}
|
||||
|
||||
function pickSymbolsByDrawCandidates(
|
||||
pickers: { key: string; value: SymbolItem; elem: Element }[],
|
||||
drawCandidates: DetypifySymbol[] | undefined
|
||||
drawCandidates: DetypifySymbol[] | undefined,
|
||||
) {
|
||||
if (drawCandidates === undefined) return pickers;
|
||||
if (!drawCandidates.length) return [];
|
||||
|
|
@ -623,7 +610,7 @@ export const SymbolPicker = () => {
|
|||
style: "flex: 0 0 auto; gap: 5px",
|
||||
},
|
||||
SearchBar(symInfo, filteredPickers),
|
||||
CanvasPanel(strokes)
|
||||
CanvasPanel(strokes),
|
||||
),
|
||||
div({ style: "flex: 1;" }, (_dom?: Element) =>
|
||||
div(
|
||||
|
|
@ -631,27 +618,23 @@ export const SymbolPicker = () => {
|
|||
CATEGORY_INFO,
|
||||
pickSymbolsBySearch(
|
||||
pickSymbolsByDrawCandidates(pickers.val, drawCandidates.val),
|
||||
filteredPickers.val
|
||||
)
|
||||
filteredPickers.val,
|
||||
),
|
||||
)
|
||||
.filter((cat) => cat.symbols?.length)
|
||||
.map((info) => CategoryPicker(info))
|
||||
)
|
||||
)
|
||||
.map((info) => CategoryPicker(info)),
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
function categorize(
|
||||
catsRaw: SymbolCategory[],
|
||||
symInfo: InstantiatedSymbolItem[]
|
||||
symInfo: InstantiatedSymbolItem[],
|
||||
): InstantiatedSymbolCategory[] {
|
||||
let cats: InstantiatedSymbolCategory[] = [
|
||||
...catsRaw.map((cat) => ({ ...cat })),
|
||||
];
|
||||
let cats: InstantiatedSymbolCategory[] = [...catsRaw.map((cat) => ({ ...cat }))];
|
||||
// let misc
|
||||
let misc: InstantiatedSymbolCategory = cats.find(
|
||||
(cat) => cat.name === "Miscellaneous"
|
||||
)!;
|
||||
let misc: InstantiatedSymbolCategory = cats.find((cat) => cat.name === "Miscellaneous")!;
|
||||
// misc.symbols = symInfo.val.symbols;
|
||||
for (let sym of symInfo) {
|
||||
const { key, value } = sym;
|
||||
|
|
|
|||
|
|
@ -30,10 +30,7 @@ interface PackageMeta {
|
|||
template: any;
|
||||
}
|
||||
|
||||
const TemplateList = (
|
||||
packages: State<PackageMeta[]>,
|
||||
catState: FilterState
|
||||
) => {
|
||||
const TemplateList = (packages: State<PackageMeta[]>, catState: FilterState) => {
|
||||
const AuthorItem = (author: string) => {
|
||||
// split by <
|
||||
const [nameStart, emailRest] = author.split("<");
|
||||
|
|
@ -55,10 +52,7 @@ const TemplateList = (
|
|||
|
||||
const AuthorList = (authors: string[]) => {
|
||||
if (authors.length <= 1) {
|
||||
return span(
|
||||
{ class: `tinymist-author-container` },
|
||||
...authors.map(AuthorItem)
|
||||
);
|
||||
return span({ class: `tinymist-author-container` }, ...authors.map(AuthorItem));
|
||||
}
|
||||
|
||||
return span(
|
||||
|
|
@ -70,57 +64,51 @@ const TemplateList = (
|
|||
style: "text-decoration: underline",
|
||||
title: authors.slice(1).join(", "),
|
||||
},
|
||||
"et al."
|
||||
)
|
||||
"et al.",
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const highlightMatches = (text: string, searchResults?: SearchResult[]): HTMLSpanElement => {
|
||||
if (!searchResults || !text) return van.tags.span({}, text);
|
||||
const searchTerms = searchResults.flatMap(result => result.queryTerms);
|
||||
const regex = new RegExp(`(${searchTerms.join("|")})`, 'gi');
|
||||
const searchTerms = searchResults.flatMap((result) => result.queryTerms);
|
||||
const regex = new RegExp(`(${searchTerms.join("|")})`, "gi");
|
||||
|
||||
const parts = text.split(regex);
|
||||
|
||||
return van.tags.span({}, ...parts.map(part =>
|
||||
regex.test(part)
|
||||
? van.tags.span({ class: 'tinymist-highlight' }, part)
|
||||
: part
|
||||
));
|
||||
}
|
||||
return van.tags.span(
|
||||
{},
|
||||
...parts.map((part) =>
|
||||
regex.test(part) ? van.tags.span({ class: "tinymist-highlight" }, part) : part,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const TemplateListItem = (item: PackageMeta) => {
|
||||
const TemplateAction = (
|
||||
icon: ChildDom,
|
||||
title: string,
|
||||
onclick: () => void
|
||||
) =>
|
||||
const TemplateAction = (icon: ChildDom, title: string, onclick: () => void) =>
|
||||
button(
|
||||
{
|
||||
class: "tinymist-button tinymist-template-action",
|
||||
title,
|
||||
onclick,
|
||||
},
|
||||
icon
|
||||
icon,
|
||||
);
|
||||
|
||||
return Card(
|
||||
"template-card",
|
||||
div(
|
||||
a({ href: item.repository, style: "font-size: 1.2em" },
|
||||
() => {
|
||||
return highlightMatches(item.name, catState.searchSelected.val);
|
||||
}
|
||||
),
|
||||
a({ href: item.repository, style: "font-size: 1.2em" }, () => {
|
||||
return highlightMatches(item.name, catState.searchSelected.val);
|
||||
}),
|
||||
span(" "),
|
||||
span({ style: "font-size: 0.8em" }, "v" + item.version),
|
||||
span(" by "),
|
||||
AuthorList(item.authors)
|
||||
AuthorList(item.authors),
|
||||
),
|
||||
div(
|
||||
{
|
||||
style:
|
||||
"display: flex; align-items: center; gap: 0.25em; margin-top: 0.4em;",
|
||||
style: "display: flex; align-items: center; gap: 0.25em; margin-top: 0.4em;",
|
||||
class: "tinymist-template-actions",
|
||||
},
|
||||
button(
|
||||
|
|
@ -134,13 +122,13 @@ const TemplateList = (
|
|||
title: van.derive(() =>
|
||||
catState.getIsFavorite("preview", item.name)
|
||||
? "Removes from favorite"
|
||||
: "Adds to favorite"
|
||||
: "Adds to favorite",
|
||||
),
|
||||
onclick() {
|
||||
catState.negIsFavorite("preview", item.name);
|
||||
},
|
||||
},
|
||||
HeartIcon(16)
|
||||
HeartIcon(16),
|
||||
),
|
||||
TemplateAction(AddIcon(16), "Creates project", () => {
|
||||
const packageSpec = `@preview/${item.name}:${item.version}`;
|
||||
|
|
@ -156,22 +144,22 @@ const TemplateList = (
|
|||
}
|
||||
return { value: cat };
|
||||
})
|
||||
.map(CategoryButton(catState))
|
||||
.map(CategoryButton(catState)),
|
||||
),
|
||||
div({ style: "clear: both" }),
|
||||
div({ style: "margin-top: 0.4em" },
|
||||
div(
|
||||
{ style: "margin-top: 0.4em" },
|
||||
div({}, () => {
|
||||
return highlightMatches(item.description, catState.searchSelected.val);
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
function runFilterSearch(searchResult: SearchResult[] | undefined) {
|
||||
// console.log("search", searchResult);
|
||||
const searchResultMap = new Set(searchResult?.map((result) => result.id));
|
||||
return (value: PackageMeta) =>
|
||||
searchResult === undefined || searchResultMap.has(value.id);
|
||||
return (value: PackageMeta) => searchResult === undefined || searchResultMap.has(value.id);
|
||||
}
|
||||
|
||||
function runFilterCategory(categoryFilter: Set<string>) {
|
||||
|
|
@ -197,8 +185,8 @@ const TemplateList = (
|
|||
.filter(runFilterCategory(catState.categories.val))
|
||||
.filter(runFilterFavorite)
|
||||
.filter(runFilterSearch(catState.searchSelected.val))
|
||||
.map(TemplateListItem) || []
|
||||
)
|
||||
.map(TemplateListItem) || [],
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -280,8 +268,7 @@ const CategoryButton = (catState: FilterState) => (category: Category) => {
|
|||
return button(
|
||||
{
|
||||
class: van.derive(() => {
|
||||
const activatingCls =
|
||||
category.value === catState.activating.val ? " activated" : "";
|
||||
const activatingCls = category.value === catState.activating.val ? " activated" : "";
|
||||
return "tinymist-button" + activatingCls;
|
||||
}),
|
||||
title: "Filter by category: " + category.value,
|
||||
|
|
@ -291,8 +278,8 @@ const CategoryButton = (catState: FilterState) => (category: Category) => {
|
|||
{
|
||||
style: "height: 16px;",
|
||||
},
|
||||
category.display || category.value
|
||||
)
|
||||
category.display || category.value,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -304,15 +291,14 @@ const FilterRow = (catState: FilterState) => {
|
|||
return "tinymist-button" + activatingCls;
|
||||
}),
|
||||
title: "Filter by favorite state",
|
||||
onclick: () =>
|
||||
(catState.filterFavorite.val = !catState.filterFavorite.val),
|
||||
onclick: () => (catState.filterFavorite.val = !catState.filterFavorite.val),
|
||||
},
|
||||
HeartIcon(16)
|
||||
HeartIcon(16),
|
||||
);
|
||||
return div(
|
||||
{ class: "tinymist-category-filter" },
|
||||
favButton,
|
||||
...CATEGORIES.map(CategoryButton(catState))
|
||||
...CATEGORIES.map(CategoryButton(catState)),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -322,15 +308,13 @@ export const TemplateGallery = () => {
|
|||
const favoritePlaceholders = `:[[preview:FavoritePlaceholder]]:`;
|
||||
const catState = new FilterState(
|
||||
JSON.parse(
|
||||
favoritePlaceholders.startsWith(":")
|
||||
? favoriteState
|
||||
: base64Decode(favoritePlaceholders)
|
||||
)
|
||||
favoritePlaceholders.startsWith(":") ? favoriteState : base64Decode(favoritePlaceholders),
|
||||
),
|
||||
);
|
||||
van.derive(async () => {
|
||||
const rawPackages = await fetch(
|
||||
"https://packages.typst.org/preview/index.json"
|
||||
).then((res) => res.json());
|
||||
const rawPackages = await fetch("https://packages.typst.org/preview/index.json").then((res) =>
|
||||
res.json(),
|
||||
);
|
||||
|
||||
// collect packages by version
|
||||
const packagesIndex = new Map<string, PackageMeta[]>();
|
||||
|
|
@ -355,9 +339,5 @@ export const TemplateGallery = () => {
|
|||
packages.val = packagesList;
|
||||
});
|
||||
|
||||
return div(
|
||||
SearchBar(packages, catState),
|
||||
FilterRow(catState),
|
||||
TemplateList(packages, catState)
|
||||
);
|
||||
return div(SearchBar(packages, catState), FilterRow(catState), TemplateList(packages, catState));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,8 +15,4 @@ export const base64Decode = (encoded: string) =>
|
|||
* @returns Base64 encoded string
|
||||
*/
|
||||
export const base64Encode = (utf8Str: string) =>
|
||||
btoa(
|
||||
Array.from(utf82bytes.encode(utf8Str), (c) => String.fromCharCode(c)).join(
|
||||
""
|
||||
)
|
||||
);
|
||||
btoa(Array.from(utf82bytes.encode(utf8Str), (c) => String.fromCharCode(c)).join(""));
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ import { viteSingleFile } from "vite-plugin-singlefile";
|
|||
|
||||
// /src/main.ts
|
||||
|
||||
const compPrefix = '--component=';
|
||||
const componentArgs = process.argv.find(arg => arg.startsWith(compPrefix));
|
||||
let output = 'dist/default';
|
||||
const compPrefix = "--component=";
|
||||
const componentArgs = process.argv.find((arg) => arg.startsWith(compPrefix));
|
||||
let output = "dist/default";
|
||||
if (componentArgs) {
|
||||
const component = componentArgs.substring(compPrefix.length);
|
||||
process.env.VITE_ENTRY = `/src/main.${component}.ts`;
|
||||
output = `dist/${component}`;
|
||||
} else {
|
||||
process.env.VITE_ENTRY = '/src/main.ts';
|
||||
process.env.VITE_ENTRY = "/src/main.ts";
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
|
|
@ -19,13 +19,13 @@ export default defineConfig({
|
|||
assetsInclude: ["**/*.onnx"],
|
||||
build: {
|
||||
minify: false,
|
||||
outDir: output
|
||||
outDir: output,
|
||||
},
|
||||
optimizeDeps: {
|
||||
esbuildOptions: {
|
||||
loader: {
|
||||
".onnx": "dataurl",
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,5 +21,5 @@ import { TypstDocumentContext, composeDoc, provideDoc } from "./typst-doc.mjs";
|
|||
* ) {}
|
||||
*/
|
||||
export class TypstDocument extends provideDoc(
|
||||
composeDoc(TypstDocumentContext, provideCanvasDoc, provideSvgDoc)
|
||||
composeDoc(TypstDocumentContext, provideCanvasDoc, provideSvgDoc),
|
||||
) {}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ export class TypstPreviewDocument extends provideDoc(
|
|||
provideOutlineDoc,
|
||||
provideCanvasDoc,
|
||||
provideSvgDoc,
|
||||
provideDebugJumpDoc
|
||||
)
|
||||
provideDebugJumpDoc,
|
||||
),
|
||||
) {}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export function triggerRipple(
|
|||
top: number,
|
||||
className: string,
|
||||
animation: string,
|
||||
color?: string
|
||||
color?: string,
|
||||
) {
|
||||
const ripple = document.createElement("div");
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,7 @@ export interface TypstCanvasDocument {
|
|||
}
|
||||
|
||||
export function provideCanvasDoc<
|
||||
TBase extends GConstructor<
|
||||
TypstDocumentContext & Partial<TypstOutlineDocument>
|
||||
>,
|
||||
TBase extends GConstructor<TypstDocumentContext & Partial<TypstOutlineDocument>>,
|
||||
>(Base: TBase): TBase & GConstructor<TypstCanvasDocument> {
|
||||
return class CanvasDocument extends Base {
|
||||
feat$canvas = true;
|
||||
|
|
@ -62,40 +60,28 @@ export function provideCanvasDoc<
|
|||
pageInfo.elem = document.createElement("div");
|
||||
pageInfo.elem.setAttribute("class", "typst-page-canvas");
|
||||
pageInfo.elem.style.transformOrigin = "0 0";
|
||||
pageInfo.elem.setAttribute(
|
||||
"data-page-number",
|
||||
pageInfo.index.toString()
|
||||
);
|
||||
pageInfo.elem.setAttribute("data-page-number", pageInfo.index.toString());
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
pageInfo.elem.appendChild(canvas);
|
||||
|
||||
pageInfo.container = document.createElement("div");
|
||||
// todo: reuse by key
|
||||
pageInfo.container.setAttribute(
|
||||
TypstPatchAttrs.Tid,
|
||||
`canvas:` + pageInfo.index
|
||||
);
|
||||
pageInfo.container.setAttribute(TypstPatchAttrs.Tid, `canvas:` + pageInfo.index);
|
||||
pageInfo.container.setAttribute("class", "typst-page canvas-mode");
|
||||
pageInfo.container.setAttribute(
|
||||
"data-page-number",
|
||||
pageInfo.index.toString()
|
||||
);
|
||||
pageInfo.container.setAttribute("data-page-number", pageInfo.index.toString());
|
||||
pageInfo.container.appendChild(pageInfo.elem);
|
||||
|
||||
// do scaling early
|
||||
this.prepareCanvas(pageInfo, canvas);
|
||||
rescale(
|
||||
pageInfo.container,
|
||||
this.isContentPreview || this.renderMode !== "canvas" || isFirst
|
||||
this.isContentPreview || this.renderMode !== "canvas" || isFirst,
|
||||
);
|
||||
|
||||
if (this.isContentPreview) {
|
||||
const pageNumberIndicator = document.createElement("div");
|
||||
pageNumberIndicator.setAttribute(
|
||||
"class",
|
||||
"typst-preview-canvas-page-number"
|
||||
);
|
||||
pageNumberIndicator.setAttribute("class", "typst-preview-canvas-page-number");
|
||||
pageNumberIndicator.textContent = `${pageInfo.index + 1}`;
|
||||
pageInfo.container.appendChild(pageNumberIndicator);
|
||||
|
||||
|
|
@ -146,10 +132,7 @@ export function provideCanvasDoc<
|
|||
return cached;
|
||||
}
|
||||
|
||||
async updateCanvas(
|
||||
pages: CanvasPage[],
|
||||
opts?: UpdateCanvasOptions
|
||||
): Promise<void> {
|
||||
async updateCanvas(pages: CanvasPage[], opts?: UpdateCanvasOptions): Promise<void> {
|
||||
const tok = opts?.cancel || undefined;
|
||||
const perf = performance.now();
|
||||
console.log("updateCanvas start");
|
||||
|
|
@ -181,8 +164,7 @@ export function provideCanvasDoc<
|
|||
|
||||
let cached = this.prepareCanvas(pageInfo, canvas);
|
||||
|
||||
const cacheKey =
|
||||
pageInfo.elem.getAttribute("data-cache-key") || undefined;
|
||||
const cacheKey = pageInfo.elem.getAttribute("data-cache-key") || undefined;
|
||||
const result = await this.kModule.renderCanvas({
|
||||
canvas: canvas.getContext("2d")!,
|
||||
pageOffset: pageInfo.index,
|
||||
|
|
@ -221,18 +203,12 @@ export function provideCanvasDoc<
|
|||
if (noSpacingFromTop) {
|
||||
canvasContainer.style.marginTop = `0px`;
|
||||
} else {
|
||||
canvasContainer.style.marginTop = `${
|
||||
this.isContentPreview ? 6 : 5
|
||||
}px`;
|
||||
canvasContainer.style.marginTop = `${this.isContentPreview ? 6 : 5}px`;
|
||||
}
|
||||
let elem = canvasContainer.firstElementChild as HTMLDivElement;
|
||||
|
||||
const canvasWidth = Number.parseFloat(
|
||||
elem.getAttribute("data-page-width")!
|
||||
);
|
||||
const canvasHeight = Number.parseFloat(
|
||||
elem.getAttribute("data-page-height")!
|
||||
);
|
||||
const canvasWidth = Number.parseFloat(elem.getAttribute("data-page-width")!);
|
||||
const canvasHeight = Number.parseFloat(elem.getAttribute("data-page-height")!);
|
||||
|
||||
this.currentRealScale =
|
||||
this.previewMode === PreviewMode.Slide
|
||||
|
|
@ -240,9 +216,7 @@ export function provideCanvasDoc<
|
|||
: cw / canvasWidth;
|
||||
const scale =
|
||||
// The element in svg is already scaled by svg host
|
||||
this.renderMode === "svg"
|
||||
? 1
|
||||
: this.currentRealScale * this.currentScaleRatio;
|
||||
this.renderMode === "svg" ? 1 : this.currentRealScale * this.currentScaleRatio;
|
||||
|
||||
// apply scale
|
||||
const appliedScale = (scale / this.pixelPerPt).toString();
|
||||
|
|
@ -309,18 +283,16 @@ export function provideCanvasDoc<
|
|||
|
||||
async rerender$canvas() {
|
||||
// console.log('toggleCanvasViewportChange!!!!!!', this.id, this.isRendering);
|
||||
const pages: CanvasPage[] = this.kModule
|
||||
.retrievePagesInfo()
|
||||
.map((x, index) => {
|
||||
return {
|
||||
tag: "canvas",
|
||||
index,
|
||||
width: x.width,
|
||||
height: x.height,
|
||||
container: undefined as any as HTMLDivElement,
|
||||
elem: undefined as any as HTMLDivElement,
|
||||
};
|
||||
});
|
||||
const pages: CanvasPage[] = this.kModule.retrievePagesInfo().map((x, index) => {
|
||||
return {
|
||||
tag: "canvas",
|
||||
index,
|
||||
width: x.width,
|
||||
height: x.height,
|
||||
container: undefined as any as HTMLDivElement,
|
||||
elem: undefined as any as HTMLDivElement,
|
||||
};
|
||||
});
|
||||
|
||||
if (!this.hookedElem.firstElementChild) {
|
||||
this.hookedElem.innerHTML = `<div class="typst-doc" data-render-mode="canvas"></div>`;
|
||||
|
|
@ -338,9 +310,7 @@ export function provideCanvasDoc<
|
|||
checkChildren(canvasContainer);
|
||||
}
|
||||
if (canvasContainer.classList.contains("typst-page")) {
|
||||
const pageNumber = Number.parseInt(
|
||||
ch.getAttribute("data-page-number")!
|
||||
);
|
||||
const pageNumber = Number.parseInt(ch.getAttribute("data-page-number")!);
|
||||
if (pageNumber >= pages.length) {
|
||||
// todo: cache key can shifted
|
||||
elem.removeChild(ch);
|
||||
|
|
@ -358,9 +328,7 @@ export function provideCanvasDoc<
|
|||
if (!ch.classList.contains("typst-page")) {
|
||||
continue;
|
||||
}
|
||||
const pageNumber = Number.parseInt(
|
||||
ch.getAttribute("data-page-number")!
|
||||
);
|
||||
const pageNumber = Number.parseInt(ch.getAttribute("data-page-number")!);
|
||||
if (pageNumber >= pages.length) {
|
||||
// todo: cache key shifted
|
||||
docDiv.removeChild(ch);
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ interface TypstCanvasDocument {
|
|||
renderCanvas(): number;
|
||||
}
|
||||
|
||||
function provideCanvas<
|
||||
TBase extends GConstructor<TypstDocument & TypstSvgDocument>
|
||||
>(Base: TBase): TBase & GConstructor<TypstCanvasDocument> {
|
||||
function provideCanvas<TBase extends GConstructor<TypstDocument & TypstSvgDocument>>(
|
||||
Base: TBase,
|
||||
): TBase & GConstructor<TypstCanvasDocument> {
|
||||
return class extends Base {
|
||||
canvasFeat = 10;
|
||||
renderCanvas() {
|
||||
|
|
@ -25,9 +25,9 @@ function provideCanvas<
|
|||
};
|
||||
}
|
||||
|
||||
function provideSvg<
|
||||
TBase extends GConstructor<TypstDocument & TypstCanvasDocument>
|
||||
>(Base: TBase): TBase & GConstructor<TypstSvgDocument> {
|
||||
function provideSvg<TBase extends GConstructor<TypstDocument & TypstCanvasDocument>>(
|
||||
Base: TBase,
|
||||
): TBase & GConstructor<TypstSvgDocument> {
|
||||
return class extends Base {
|
||||
feat = 100;
|
||||
svgProp() {
|
||||
|
|
@ -42,9 +42,7 @@ function provideSvg<
|
|||
describe("mixinClass", () => {
|
||||
it("doMixin", () => {
|
||||
const T = provideSvg(
|
||||
provideCanvas(
|
||||
TypstDocument as GConstructor<TypstDocument & TypstSvgDocument>
|
||||
)
|
||||
provideCanvas(TypstDocument as GConstructor<TypstDocument & TypstSvgDocument>),
|
||||
);
|
||||
const t = new T();
|
||||
expect(t.renderCanvas()).toBe(51);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class GenElem {
|
|||
constructor(
|
||||
public tag: string,
|
||||
public container: HTMLElement,
|
||||
public additions?: Record<string, any>
|
||||
public additions?: Record<string, any>,
|
||||
) {}
|
||||
|
||||
push(child: GenNode) {
|
||||
|
|
@ -171,7 +171,7 @@ class GenContext {
|
|||
export function patchOutlineEntry(
|
||||
prev: HTMLDivElement,
|
||||
pages: CanvasPage[],
|
||||
items: OutlineItemData[]
|
||||
items: OutlineItemData[],
|
||||
) {
|
||||
const ctx = new GenContext(pages);
|
||||
// the root element of the generated outline
|
||||
|
|
@ -190,8 +190,7 @@ export function patchOutlineEntry(
|
|||
for (const elem of ctx.allElemList) {
|
||||
// apply clickable behavior to node containing children
|
||||
if (elem.children.some(isDataNode)) {
|
||||
const titleContentSpan = elem.additions!.title!.additions!
|
||||
.content as HTMLSpanElement;
|
||||
const titleContentSpan = elem.additions!.title!.additions!.content as HTMLSpanElement;
|
||||
|
||||
titleContentSpan.style.textDecoration = "underline";
|
||||
titleContentSpan.style.cursor = "pointer";
|
||||
|
|
@ -221,11 +220,7 @@ export function patchOutlineEntry(
|
|||
/// Replace the `prev` element with `next` element.
|
||||
/// Return true if the `prev` element is reused.
|
||||
/// Return false if the `prev` element is replaced.
|
||||
function reuseOrPatchOutlineElem(
|
||||
ctx: GenContext,
|
||||
prev: Element,
|
||||
next: Element
|
||||
) {
|
||||
function reuseOrPatchOutlineElem(ctx: GenContext, prev: Element, next: Element) {
|
||||
const canReuse = equalPatchElem(prev, next);
|
||||
|
||||
/// Even if the element is reused, we still need to replace its attributes.
|
||||
|
|
@ -237,9 +232,7 @@ function reuseOrPatchOutlineElem(
|
|||
|
||||
if (canReuse) {
|
||||
if (isPageElem) {
|
||||
const pageNumber = Number.parseInt(
|
||||
next.getAttribute("data-page-number")!
|
||||
);
|
||||
const pageNumber = Number.parseInt(next.getAttribute("data-page-number")!);
|
||||
// console.log('reuse canvas', ctx.pages[pageNumber], prev, next);
|
||||
const page = ctx.pages[pageNumber];
|
||||
page.inserter = poisionCanvasMoved;
|
||||
|
|
@ -264,7 +257,7 @@ function patchOutlineChildren(ctx: GenContext, prev: Element, next: Element) {
|
|||
prev.children as unknown as Element[],
|
||||
next.children as unknown as Element[],
|
||||
// todo: accurate calculation
|
||||
false
|
||||
false,
|
||||
);
|
||||
|
||||
// console.log("interpreted origin outline", targetView, toPatch);
|
||||
|
|
@ -275,10 +268,7 @@ function patchOutlineChildren(ctx: GenContext, prev: Element, next: Element) {
|
|||
|
||||
// console.log("interpreted target outline", targetView);
|
||||
|
||||
const originView = changeViewPerspective(
|
||||
prev.children as unknown as Element[],
|
||||
targetView
|
||||
);
|
||||
const originView = changeViewPerspective(prev.children as unknown as Element[], targetView);
|
||||
|
||||
runOriginViewInstructionsOnOutline(ctx, prev, originView);
|
||||
}
|
||||
|
|
@ -286,7 +276,7 @@ function patchOutlineChildren(ctx: GenContext, prev: Element, next: Element) {
|
|||
function runOriginViewInstructionsOnOutline(
|
||||
ctx: GenContext,
|
||||
prev: Element,
|
||||
originView: OriginViewInstruction<Node>[]
|
||||
originView: OriginViewInstruction<Node>[],
|
||||
) {
|
||||
// console.log("interpreted origin view", originView);
|
||||
for (const [op, off, fr] of originView) {
|
||||
|
|
@ -300,9 +290,7 @@ function runOriginViewInstructionsOnOutline(
|
|||
break;
|
||||
case "remove":
|
||||
if (elem?.classList?.contains("typst-page")) {
|
||||
const pageNumber = Number.parseInt(
|
||||
elem.getAttribute("data-page-number")!
|
||||
);
|
||||
const pageNumber = Number.parseInt(elem.getAttribute("data-page-number")!);
|
||||
if (pageNumber < ctx.pages.length) {
|
||||
const page = ctx.pages[pageNumber];
|
||||
// console.log('recover canvas', page, pageNumber);
|
||||
|
|
@ -321,22 +309,14 @@ function runOriginViewInstructionsOnOutline(
|
|||
}
|
||||
|
||||
export interface TypstOutlineDocument {
|
||||
patchOutlineEntry(
|
||||
prev: HTMLDivElement,
|
||||
pages: CanvasPage[],
|
||||
items: OutlineItemData[]
|
||||
): void;
|
||||
patchOutlineEntry(prev: HTMLDivElement, pages: CanvasPage[], items: OutlineItemData[]): void;
|
||||
}
|
||||
|
||||
export function provideOutlineDoc<
|
||||
TBase extends GConstructor<TypstDocumentContext>,
|
||||
>(Base: TBase): TBase & GConstructor<TypstOutlineDocument> {
|
||||
export function provideOutlineDoc<TBase extends GConstructor<TypstDocumentContext>>(
|
||||
Base: TBase,
|
||||
): TBase & GConstructor<TypstOutlineDocument> {
|
||||
return class DebugJumpDocument extends Base {
|
||||
patchOutlineEntry(
|
||||
prev: HTMLDivElement,
|
||||
pages: CanvasPage[],
|
||||
items: OutlineItemData[]
|
||||
) {
|
||||
patchOutlineEntry(prev: HTMLDivElement, pages: CanvasPage[], items: OutlineItemData[]) {
|
||||
patchOutlineEntry(prev, pages, items);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,10 +72,7 @@ export function equalPatchElem(prev: ElementChildren, next: ElementChildren) {
|
|||
/// To remove unused resources, An extra remove inst can remove a specify element
|
||||
///
|
||||
/// Example5: resource:[o1, o2] -> <reuse o1> <append t1> <remove o2> -> [o1, t1] and remove o2
|
||||
export type TargetViewInstruction<T> =
|
||||
| ["append", T]
|
||||
| ["reuse", number]
|
||||
| ["remove", number];
|
||||
export type TargetViewInstruction<T> = ["append", T] | ["reuse", number] | ["remove", number];
|
||||
|
||||
/// The recursive patch operation must be applied to this two element.
|
||||
export type PatchPair<T> = [T /* origin */, T /* target */];
|
||||
|
|
@ -89,7 +86,7 @@ export function interpretTargetView<T extends ElementChildren, U extends T = T>(
|
|||
targetChildren: T[],
|
||||
// todo: remove this tag
|
||||
isPatchingSvg: boolean = true, // patch svg or outline
|
||||
tIsU = (x: T): x is U => !!x.getAttribute(TypstPatchAttrs.Tid)
|
||||
tIsU = (x: T): x is U => !!x.getAttribute(TypstPatchAttrs.Tid),
|
||||
): ViewTransform<U> {
|
||||
const availableOwnedResource = new Map<string, [T, number[]]>();
|
||||
const targetView: TargetViewInstruction<U>[] = [];
|
||||
|
|
@ -187,13 +184,10 @@ export type OriginViewInstruction<T> =
|
|||
/// + Finally, it inserts the extra elements.
|
||||
///
|
||||
/// Some better strategy would help and be implemented in future.
|
||||
export function changeViewPerspective<
|
||||
T extends ElementChildren,
|
||||
U extends T = T
|
||||
>(
|
||||
export function changeViewPerspective<T extends ElementChildren, U extends T = T>(
|
||||
originChildren: T[],
|
||||
targetView: TargetViewInstruction<U>[],
|
||||
tIsU = (_x: T): _x is U => true
|
||||
tIsU = (_x: T): _x is U => true,
|
||||
): OriginViewInstruction<U>[] {
|
||||
const originView: OriginViewInstruction<U>[] = [];
|
||||
|
||||
|
|
@ -321,7 +315,7 @@ export function changeViewPerspective<
|
|||
|
||||
export function runOriginViewInstructions(
|
||||
prev: Element,
|
||||
originView: OriginViewInstruction<Node>[]
|
||||
originView: OriginViewInstruction<Node>[],
|
||||
) {
|
||||
// console.log("interpreted origin view", originView);
|
||||
for (const [op, off, fr] of originView) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ function patchChildren(prev: Element, next: Element) {
|
|||
const originView = changeViewPerspective(
|
||||
prev.children as unknown as SVGGElement[],
|
||||
targetView,
|
||||
isGElem
|
||||
isGElem,
|
||||
);
|
||||
|
||||
runOriginViewInstructions(prev, originView);
|
||||
|
|
@ -76,10 +76,7 @@ interface FrozenReplacement {
|
|||
debug?: string;
|
||||
}
|
||||
|
||||
function preReplaceNonSVGElements(
|
||||
prev: Element,
|
||||
next: Element
|
||||
): FrozenReplacement {
|
||||
function preReplaceNonSVGElements(prev: Element, next: Element): FrozenReplacement {
|
||||
const removedIndices: number[] = [];
|
||||
const frozenReplacement: FrozenReplacement = {
|
||||
inserts: [],
|
||||
|
|
@ -118,9 +115,9 @@ function postReplaceNonSVGElements(prev: Element, frozen: FrozenReplacement) {
|
|||
/// Retrieve the `<g>` elements from the `prev` element.
|
||||
const gElements = Array.from(prev.children).filter(isGElem);
|
||||
if (gElements.length + 1 !== frozen.inserts.length) {
|
||||
throw new Error(`invalid frozen replacement: gElements.length (${gElements.length
|
||||
}) + 1 !=== frozen.inserts.length (${frozen.inserts.length}) ${frozen.debug || ""
|
||||
}
|
||||
throw new Error(`invalid frozen replacement: gElements.length (${
|
||||
gElements.length
|
||||
}) + 1 !=== frozen.inserts.length (${frozen.inserts.length}) ${frozen.debug || ""}
|
||||
current: ${prev.outerHTML}`);
|
||||
}
|
||||
|
||||
|
|
@ -156,10 +153,7 @@ function initOrPatchSvgHeader(svg: SVGElement) {
|
|||
}
|
||||
|
||||
/// Create a global resource header
|
||||
const resourceHeader = document.createElementNS(
|
||||
"http://www.w3.org/2000/svg",
|
||||
"svg"
|
||||
);
|
||||
const resourceHeader = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
resourceHeader.id = "typst-svg-resources";
|
||||
// set viewbox, width, and height
|
||||
resourceHeader.setAttribute("viewBox", "0 0 0 0");
|
||||
|
|
@ -195,10 +189,7 @@ function patchSvgHeader(prev: SVGElement, next: SVGElement) {
|
|||
// todo: gc
|
||||
prevChild.append(...nextChild.children);
|
||||
}
|
||||
} else if (
|
||||
prevChild.tagName === "style" &&
|
||||
nextChild.getAttribute("data-reuse") !== "1"
|
||||
) {
|
||||
} else if (prevChild.tagName === "style" && nextChild.getAttribute("data-reuse") !== "1") {
|
||||
// console.log("replace extra style", prevChild, nextChild);
|
||||
|
||||
// todo: gc
|
||||
|
|
@ -240,7 +231,7 @@ function patchSvgHeader(prev: SVGElement, next: SVGElement) {
|
|||
export function patchSvgToContainer(
|
||||
hookedElem: Element,
|
||||
patchStr: string,
|
||||
decorateSvgElement: (elem: SVGElement) => void = () => void 0
|
||||
decorateSvgElement: (elem: SVGElement) => void = () => void 0,
|
||||
) {
|
||||
if (hookedElem.firstElementChild) {
|
||||
const elem = document.createElement("div");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
PatchPair,
|
||||
interpretTargetView,
|
||||
changeViewPerspective,
|
||||
} from "./typst-patch.mjs";
|
||||
import { PatchPair, interpretTargetView, changeViewPerspective } from "./typst-patch.mjs";
|
||||
|
||||
interface Attributes {
|
||||
[key: string]: string | null | undefined;
|
||||
|
|
@ -15,7 +11,7 @@ interface Attributes {
|
|||
class MockElement {
|
||||
tagName = "g";
|
||||
|
||||
constructor(public attrs: Attributes) { }
|
||||
constructor(public attrs: Attributes) {}
|
||||
|
||||
getAttribute(s: string): string | null {
|
||||
return this.attrs[s] ?? null;
|
||||
|
|
@ -45,7 +41,7 @@ const repeatOrJust = (n: number | (number | null)[]): MockElement[] => {
|
|||
(i) =>
|
||||
new MockElement({
|
||||
"data-tid": i !== null ? i.toString() : null,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +60,7 @@ const reuseStub = (n: number | null) =>
|
|||
|
||||
function toSnapshot([targetView, patchPair]: [
|
||||
(MockElement | number | string)[][],
|
||||
PatchPair<MockElement>[]
|
||||
PatchPair<MockElement>[],
|
||||
]): string[] {
|
||||
const repr = (elem: unknown) => {
|
||||
if (elem instanceof MockElement) {
|
||||
|
|
@ -76,33 +72,24 @@ function toSnapshot([targetView, patchPair]: [
|
|||
const instructions = targetView.map((i) => {
|
||||
return i.map(repr).join(",");
|
||||
});
|
||||
const patches = patchPair.length
|
||||
? [patchPair.map((i) => i.map(repr).join("->")).join(",")]
|
||||
: [];
|
||||
const patches = patchPair.length ? [patchPair.map((i) => i.map(repr).join("->")).join(",")] : [];
|
||||
return [...instructions, ...patches];
|
||||
}
|
||||
|
||||
const hasTid = (elem: MockElement): elem is MockElement =>
|
||||
elem.getAttribute("data-tid") !== null;
|
||||
const hasTid = (elem: MockElement): elem is MockElement => elem.getAttribute("data-tid") !== null;
|
||||
|
||||
const indexTargetView = (
|
||||
init: number | (number | null)[],
|
||||
rearrange: (number | null)[]
|
||||
) =>
|
||||
const indexTargetView = (init: number | (number | null)[], rearrange: (number | null)[]) =>
|
||||
interpretTargetView<MockElement>(
|
||||
injectOffsets("o", repeatOrJust(init)),
|
||||
injectOffsets("t", rearrange.map(reuseStub)),
|
||||
true,
|
||||
hasTid
|
||||
hasTid,
|
||||
);
|
||||
const indexOriginView = (
|
||||
init: number | (number | null)[],
|
||||
rearrange: (number | null)[]
|
||||
) =>
|
||||
const indexOriginView = (init: number | (number | null)[], rearrange: (number | null)[]) =>
|
||||
changeViewPerspective<MockElement>(
|
||||
injectOffsets("o", repeatOrJust(init)),
|
||||
indexTargetView(init, rearrange)[0],
|
||||
hasTid
|
||||
hasTid,
|
||||
);
|
||||
|
||||
describe("interpretView", () => {
|
||||
|
|
@ -262,10 +249,7 @@ describe("interpretView", () => {
|
|||
`);
|
||||
});
|
||||
it("handleReusePreserveOrder2", () => {
|
||||
const result = indexTargetView(
|
||||
[0, 1, 2, 1, 2, 3, 4, 3, 4],
|
||||
[1, 2, 3, 4, 3, 4, 1, 2]
|
||||
);
|
||||
const result = indexTargetView([0, 1, 2, 1, 2, 3, 4, 3, 4], [1, 2, 3, 4, 3, 4, 1, 2]);
|
||||
expect(toSnapshot(result)).toMatchInlineSnapshot(`
|
||||
[
|
||||
"reuse,1",
|
||||
|
|
@ -282,10 +266,7 @@ describe("interpretView", () => {
|
|||
`);
|
||||
});
|
||||
it("handleReusePreserveOrder2_origin", () => {
|
||||
const result = indexOriginView(
|
||||
[0, 1, 2, 1, 2, 3, 4, 3, 4],
|
||||
[1, 2, 3, 4, 3, 4, 1, 2]
|
||||
);
|
||||
const result = indexOriginView([0, 1, 2, 1, 2, 3, 4, 3, 4], [1, 2, 3, 4, 3, 4, 1, 2]);
|
||||
expect(toSnapshot([result, []])).toMatchInlineSnapshot(`
|
||||
[
|
||||
"remove,0",
|
||||
|
|
@ -301,17 +282,8 @@ describe("interpretView", () => {
|
|||
const target = injectOffsets("t", [0, null].map(reuseStub));
|
||||
target[0].attrs["data-tid"] = "1";
|
||||
target[1].attrs["data-tid"] = "0";
|
||||
const result = interpretTargetView<MockElement>(
|
||||
origin,
|
||||
target,
|
||||
true,
|
||||
hasTid
|
||||
);
|
||||
const result2 = changeViewPerspective<MockElement>(
|
||||
origin,
|
||||
result[0],
|
||||
hasTid
|
||||
);
|
||||
const result = interpretTargetView<MockElement>(origin, target, true, hasTid);
|
||||
const result2 = changeViewPerspective<MockElement>(origin, result[0], hasTid);
|
||||
expect(toSnapshot(result)).toMatchInlineSnapshot(`
|
||||
[
|
||||
"reuse,3",
|
||||
|
|
@ -328,17 +300,8 @@ describe("interpretView", () => {
|
|||
it("handleMasterproefThesisAffectedByEmptyPageAntiCase", () => {
|
||||
const origin = injectOffsets("o", repeatOrJust([null, null, null, 0, 1]));
|
||||
const target = injectOffsets("t", [0, 1].map(reuseStub));
|
||||
const result = interpretTargetView<MockElement>(
|
||||
origin,
|
||||
target,
|
||||
true,
|
||||
hasTid
|
||||
);
|
||||
const result2 = changeViewPerspective<MockElement>(
|
||||
origin,
|
||||
result[0],
|
||||
hasTid
|
||||
);
|
||||
const result = interpretTargetView<MockElement>(origin, target, true, hasTid);
|
||||
const result2 = changeViewPerspective<MockElement>(origin, result[0], hasTid);
|
||||
expect(toSnapshot(result)).toMatchInlineSnapshot(`
|
||||
[
|
||||
"reuse,3",
|
||||
|
|
@ -354,17 +317,8 @@ describe("interpretView", () => {
|
|||
it("handleReuseAppend", () => {
|
||||
const origin = injectOffsets("o", repeatOrJust([null, null, null, 0, 1]));
|
||||
const target = injectOffsets("t", [1, null, 0, null, 1].map(reuseStub));
|
||||
const result = interpretTargetView<MockElement>(
|
||||
origin,
|
||||
target,
|
||||
true,
|
||||
hasTid
|
||||
);
|
||||
const result2 = changeViewPerspective<MockElement>(
|
||||
origin,
|
||||
result[0],
|
||||
hasTid
|
||||
);
|
||||
const result = interpretTargetView<MockElement>(origin, target, true, hasTid);
|
||||
const result2 = changeViewPerspective<MockElement>(origin, result[0], hasTid);
|
||||
expect(toSnapshot(result)).toMatchInlineSnapshot(`
|
||||
[
|
||||
"reuse,4",
|
||||
|
|
|
|||
|
|
@ -1,50 +1,49 @@
|
|||
export function setupDrag() {
|
||||
let lastPos = { x: 0, y: 0 };
|
||||
let moved = false;
|
||||
let containerElement: HTMLElement | null = null;
|
||||
const mouseMoveHandler = function (e: MouseEvent) {
|
||||
// How far the mouse has been moved
|
||||
const dx = e.clientX - lastPos.x;
|
||||
const dy = e.clientY - lastPos.y;
|
||||
let lastPos = { x: 0, y: 0 };
|
||||
let moved = false;
|
||||
let containerElement: HTMLElement | null = null;
|
||||
const mouseMoveHandler = function (e: MouseEvent) {
|
||||
// How far the mouse has been moved
|
||||
const dx = e.clientX - lastPos.x;
|
||||
const dy = e.clientY - lastPos.y;
|
||||
|
||||
window.scrollBy(-dx, -dy);
|
||||
lastPos = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
moved = true;
|
||||
window.scrollBy(-dx, -dy);
|
||||
lastPos = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
const mouseUpHandler = function () {
|
||||
document.removeEventListener('mousemove', mouseMoveHandler);
|
||||
document.removeEventListener('mouseup', mouseUpHandler);
|
||||
if (!containerElement) return;
|
||||
if (!moved) {
|
||||
document.getSelection()?.removeAllRanges();
|
||||
}
|
||||
containerElement.style.cursor = 'grab';
|
||||
};
|
||||
const mouseDownHandler = function (e: MouseEvent) {
|
||||
lastPos = {
|
||||
// Get the current mouse position
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
if (!containerElement) return;
|
||||
const elementUnderMouse = e.target as HTMLElement | null;
|
||||
if (elementUnderMouse !== null && elementUnderMouse.classList.contains('tsel')) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
containerElement.style.cursor = 'grabbing';
|
||||
moved = false;
|
||||
|
||||
document.addEventListener('mousemove', mouseMoveHandler);
|
||||
document.addEventListener('mouseup', mouseUpHandler);
|
||||
};
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
containerElement = document.getElementById('typst-container');
|
||||
if (!containerElement) return;
|
||||
containerElement.addEventListener('mousedown', mouseDownHandler);
|
||||
moved = true;
|
||||
};
|
||||
const mouseUpHandler = function () {
|
||||
document.removeEventListener("mousemove", mouseMoveHandler);
|
||||
document.removeEventListener("mouseup", mouseUpHandler);
|
||||
if (!containerElement) return;
|
||||
if (!moved) {
|
||||
document.getSelection()?.removeAllRanges();
|
||||
}
|
||||
);
|
||||
containerElement.style.cursor = "grab";
|
||||
};
|
||||
const mouseDownHandler = function (e: MouseEvent) {
|
||||
lastPos = {
|
||||
// Get the current mouse position
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
if (!containerElement) return;
|
||||
const elementUnderMouse = e.target as HTMLElement | null;
|
||||
if (elementUnderMouse !== null && elementUnderMouse.classList.contains("tsel")) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
containerElement.style.cursor = "grabbing";
|
||||
moved = false;
|
||||
|
||||
document.addEventListener("mousemove", mouseMoveHandler);
|
||||
document.addEventListener("mouseup", mouseUpHandler);
|
||||
};
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
containerElement = document.getElementById("typst-container");
|
||||
if (!containerElement) return;
|
||||
containerElement.addEventListener("mousedown", mouseDownHandler);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue