feat: complete symbol view (#218)

* dev: make detypify work

* dev: add symbol view

* fix: some bugs in symbol picker

* dev: make names correct

* dev: add help panel in symbol picker

* feat: paste the sym directly into the source position

* dev: tuning the detail

* dev: remove debug logging

* dev: recover mock

* docs: improve accessibility

* dev: change symbol picker to symbol view

* dev: download detypify assets

* dev: fix download script
This commit is contained in:
Myriad-Dreamin 2024-04-27 09:58:58 +08:00 committed by GitHub
parent 5ad5294fca
commit bbc6e3e4e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 744 additions and 253 deletions

View file

@ -74,6 +74,14 @@ jobs:
with:
submodules: recursive
if: (startsWith(github.ref, 'refs/tags/') || matrix.regular_build == 'true')
- name: Download detypify assets
run: |
mkdir -p tools/editor-tools/assets/
curl -L https://github.com/QuarticCat/detypify/releases/download/0.2.3/train-out.zip -o train-out.zip
unzip -d tools/editor-tools/assets/ train-out.zip
mv tools/editor-tools/assets/train-out/ tools/editor-tools/assets/detypify/
rm train-out.zip
if: (startsWith(github.ref, 'refs/tags/') || matrix.regular_build == 'true')
- name: Install Node.js
uses: actions/setup-node@v4
with:
@ -198,6 +206,13 @@ jobs:
uses: actions/checkout@v4
with:
submodules: recursive
- name: Download detypify assets
run: |
mkdir -p tools/editor-tools/assets/
curl -L https://github.com/QuarticCat/detypify/releases/download/0.2.3/train-out.zip -o train-out.zip
unzip -d tools/editor-tools/assets/ train-out.zip
mv tools/editor-tools/assets/train-out/ tools/editor-tools/assets/detypify/
rm train-out.zip
- name: Run rust-cache
uses: Swatinem/rust-cache@v2
- name: Install deps

View file

@ -66,13 +66,20 @@ impl SyntaxRequest for InteractCodeContextRequest {
match query {
InteractCodeContextQuery::ModeAt { position } => {
let pos = lsp_to_typst::position(position, positing_encoding, source)?;
if pos == 0 || pos == source.text().len() {
// smart special case
responses.push(InteractCodeContextResponse::ModeAt {
mode: InterpretMode::Markup,
});
continue;
}
// get mode
let root = LinkedNode::new(source.root());
let leaf = root.leaf_at(pos);
let mut leaf = leaf.as_ref();
let mode = loop {
log::info!("leaf for context: {:?}", leaf);
log::debug!("leaf for context: {leaf:?}");
use SyntaxKind::*;
if let Some(t) = leaf {
match t.kind() {
@ -82,7 +89,8 @@ impl SyntaxRequest for InteractCodeContextRequest {
CodeBlock | Code => break InterpretMode::Code,
ContentBlock | Markup => break InterpretMode::Markup,
Equation | Math => break InterpretMode::Math,
Space | Linebreak | Parbreak | Escape | Shorthand | SmartQuote
Ident | FieldAccess | Bool | Int | Float | Numeric | Space
| Linebreak | Parbreak | Escape | Shorthand | SmartQuote
| RawLang | RawDelim | RawTrimmed | Hash | LeftBrace
| RightBrace | LeftBracket | RightBracket | LeftParen
| RightParen | Comma | Semicolon | Colon | Star | Underscore
@ -96,10 +104,9 @@ impl SyntaxRequest for InteractCodeContextRequest {
MathIdent | MathAlignPoint | MathDelimited | MathAttach
| MathPrimes | MathFrac | MathRoot => break InterpretMode::Math,
Let | Set | Show | Context | If | Else | For | In | While
| Break | Continue | Return | Import | Include | Ident | Bool
| Int | Float | Numeric | FieldAccess | Args | Spread | Closure
| Params | LetBinding | SetRule | ShowRule | Contextual
| Conditional | WhileLoop | ForLoop | ModuleImport
| Break | Continue | Return | Import | Include | Args | Spread
| Closure | Params | LetBinding | SetRule | ShowRule
| Contextual | Conditional | WhileLoop | ForLoop | ModuleImport
| ImportItems | RenamedImportItem | ModuleInclude | LoopBreak
| LoopContinue | FuncReturn | FuncCall | Unary | Binary
| Parenthesized | Dict | Array | Destructuring

View file

@ -1,2 +1,3 @@
tinymist-*.vsix
*.log
icons/ti.svg

BIN
editors/vscode/icons/ti.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

View file

@ -22,7 +22,26 @@
"vscode": "^1.71.0"
},
"main": "./out/extension.js",
"icon": "./icons/ti.png",
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "tinymist-activitybar",
"title": "Tinymist",
"icon": "./icons/ti.png"
}
]
},
"views": {
"tinymist-activitybar": [
{
"id": "tinymist.side-symbol-view",
"type": "webview",
"name": "Symbol View"
}
]
},
"configuration": {
"type": "object",
"title": "Tinymist Typst LSP",
@ -433,8 +452,8 @@
"category": "Typst"
},
{
"command": "tinymist.showSymbolPicker",
"title": "Show symbol picker",
"command": "tinymist.showSymbolView",
"title": "Show symbol view",
"category": "Typst"
},
{

View file

@ -1,7 +1,7 @@
import * as vscode from "vscode";
import * as path from "path";
import { readFile } from "fs/promises";
import { getFocusingFile } from "./extension";
import { getFocusingFile, getLastFocusingDoc } from "./extension";
async function loadHTMLFile(context: vscode.ExtensionContext, relativePath: string) {
const filePath = path.resolve(context.extensionPath, relativePath);
@ -38,25 +38,17 @@ export function getUserPackageData(context: vscode.ExtensionContext) {
return userPackageData;
}
export async function activateEditorTool(context: vscode.ExtensionContext, tool: string) {
if (
tool !== "template-gallery" &&
tool !== "tracing" &&
tool !== "summary" &&
tool !== "symbol-picker"
) {
vscode.window.showErrorMessage(`Unknown editor tool: ${tool}`);
return;
}
export async function activateEditorTool(
context: vscode.ExtensionContext,
tool: "template-gallery" | "tracing" | "summary" | "symbol-view"
) {
// Create and show a new WebView
const title = {
"template-gallery": "Template Gallery",
"symbol-picker": "Symbol Picker",
"symbol-view": "Symbol View",
tracing: "Tracing",
summary: "Summary",
}[tool];
// Create and show a new WebView
const panel = vscode.window.createWebviewPanel(
`tinymist-${tool}`,
title,
@ -70,6 +62,38 @@ export async function activateEditorTool(context: vscode.ExtensionContext, tool:
}
);
await activateEditorToolAt(context, tool, panel);
}
export class SymbolViewProvider implements vscode.WebviewViewProvider {
constructor(private context: vscode.ExtensionContext) {}
public resolveWebviewView(
webviewView: vscode.WebviewView,
_context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken
) {
webviewView.webview.options = {
// Allow scripts in the webview
enableScripts: true,
};
activateEditorToolAt(this.context, "symbol-view", webviewView);
}
}
async function activateEditorToolAt(
context: vscode.ExtensionContext,
tool: "template-gallery" | "tracing" | "summary" | "symbol-view",
panel: vscode.WebviewView | vscode.WebviewPanel
) {
const dispose = () => {
// if has dispose method
if ("dispose" in panel) {
panel.dispose();
}
};
panel.webview.onDidReceiveMessage(async (message) => {
console.log("onDidReceiveMessage", message);
switch (message.type) {
@ -101,7 +125,99 @@ export async function activateEditorTool(context: vscode.ExtensionContext, tool:
initArgs.push(path[0].fsPath);
await vscode.commands.executeCommand("tinymist.initTemplate", ...initArgs);
panel.dispose();
dispose();
break;
}
case "editText": {
const activeDocument = getLastFocusingDoc();
if (!activeDocument) {
await vscode.window.showErrorMessage("No focusing document");
return;
}
const editor = vscode.window.visibleTextEditors.find(
(editor) => editor.document === activeDocument
);
if (!editor) {
await vscode.window.showErrorMessage("No focusing editor");
return;
}
// get cursor
const selection = editor.selection;
const selectionStart = selection.start;
const edit = message.edit;
if (typeof edit.newText === "string") {
// replace the selection with the new text
await editor.edit((editBuilder) => {
editBuilder.replace(selection, edit.newText);
});
} else {
const {
kind,
math,
comment,
markup,
code,
string: stringContent,
raw,
rest,
} = edit.newText;
const newText = kind === "by-mode" ? rest || "" : "";
const res = await vscode.commands.executeCommand<
[{ mode: "math" | "markup" | "code" | "comment" | "string" | "raw" }]
>("tinymist.interactCodeContext", {
textDocument: {
uri: activeDocument.uri.toString(),
},
query: [
{
kind: "modeAt",
position: {
line: selectionStart.line,
character: selectionStart.character,
},
},
],
});
const mode = res[0].mode;
await editor.edit((editBuilder) => {
if (mode === "math") {
// todo: whether to keep stupid
// if it is before an identifier character, then add a space
let replaceText = math || newText;
let range = new vscode.Range(
selectionStart.with(undefined, selectionStart.character - 1),
selectionStart
);
const before =
selectionStart.character > 0 ? activeDocument.getText(range) : "";
if (before.match(/[\p{xid_start}\p{XID_Continue}_]/)) {
replaceText = " " + math;
}
editBuilder.replace(selection, replaceText);
} else if (mode === "markup") {
editBuilder.replace(selection, markup || newText);
} else if (mode === "comment") {
editBuilder.replace(selection, comment || markup || newText);
} else if (mode === "string") {
editBuilder.replace(selection, stringContent || raw || newText);
} else if (mode === "raw") {
editBuilder.replace(selection, raw || stringContent || newText);
} else if (mode === "code") {
editBuilder.replace(selection, code || newText);
} else {
editBuilder.replace(selection, newText);
}
});
}
break;
}
default: {
@ -164,7 +280,7 @@ export async function activateEditorTool(context: vscode.ExtensionContext, tool:
vscode.window.showErrorMessage("No server info");
}
panel.dispose();
dispose();
return;
}
@ -172,7 +288,7 @@ export async function activateEditorTool(context: vscode.ExtensionContext, tool:
html = html.replace(":[[preview:ServerInfo]]:", btoa(serverInfo));
break;
}
case "symbol-picker": {
case "symbol-view": {
// tinymist.getCurrentDocumentMetrics
const result = await vscode.commands.executeCommand(
"tinymist.getResources",
@ -181,7 +297,7 @@ export async function activateEditorTool(context: vscode.ExtensionContext, tool:
if (!result) {
vscode.window.showErrorMessage("No resource");
panel.dispose();
dispose();
return;
}

View file

@ -18,7 +18,11 @@ import {
type ServerOptions,
} from "vscode-languageclient/node";
import vscodeVariables from "vscode-variables";
import { activateEditorTool, getUserPackageData } from "./editor-tools";
import {
SymbolViewProvider as SymbolPickerProvider,
activateEditorTool,
getUserPackageData,
} from "./editor-tools";
import { triggerStatusBar, wordCountItemProcess } from "./ui-extends";
let client: LanguageClient | undefined = undefined;
@ -130,65 +134,42 @@ async function startClient(context: ExtensionContext): Promise<void> {
});
context.subscriptions.push(
commands.registerCommand("tinymist.exportCurrentPdf", () => commandExport("Pdf"))
);
context.subscriptions.push(
commands.registerCommand("tinymist.exportCurrentPdf", () => commandExport("Pdf")),
commands.registerCommand("tinymist.getCurrentDocumentMetrics", () =>
commandGetCurrentDocumentMetrics()
)
);
context.subscriptions.push(
commands.registerCommand("tinymist.pinMainToCurrent", () => commandPinMain(true))
);
context.subscriptions.push(
commands.registerCommand("tinymist.unpinMain", () => commandPinMain(false))
);
context.subscriptions.push(
commands.registerCommand("typst-lsp.pinMainToCurrent", () => commandPinMain(true))
);
context.subscriptions.push(
commands.registerCommand("typst-lsp.unpinMain", () => commandPinMain(false))
);
context.subscriptions.push(
commands.registerCommand("tinymist.showPdf", () => commandShow("Pdf"))
);
context.subscriptions.push(commands.registerCommand("tinymist.clearCache", commandClearCache));
context.subscriptions.push(
commands.registerCommand("tinymist.runCodeLens", commandRunCodeLens)
);
context.subscriptions.push(
),
commands.registerCommand("tinymist.pinMainToCurrent", () => commandPinMain(true)),
commands.registerCommand("tinymist.unpinMain", () => commandPinMain(false)),
commands.registerCommand("typst-lsp.pinMainToCurrent", () => commandPinMain(true)),
commands.registerCommand("typst-lsp.unpinMain", () => commandPinMain(false)),
commands.registerCommand("tinymist.showPdf", () => commandShow("Pdf")),
commands.registerCommand("tinymist.clearCache", commandClearCache),
commands.registerCommand("tinymist.runCodeLens", commandRunCodeLens),
commands.registerCommand("tinymist.initTemplate", (...args) =>
commandInitTemplate(context, false, ...args)
)
);
context.subscriptions.push(
),
commands.registerCommand("tinymist.initTemplateInPlace", (...args) =>
commandInitTemplate(context, true, ...args)
)
);
context.subscriptions.push(
),
commands.registerCommand("tinymist.showTemplateGallery", () =>
commandShowTemplateGallery(context)
)
);
context.subscriptions.push(
commands.registerCommand("tinymist.showSummary", () => commandShowSummary(context))
);
context.subscriptions.push(
),
commands.registerCommand("tinymist.showSummary", () => commandShowSummary(context)),
commands.registerCommand("tinymist.showSymbolPicker", () =>
commandShowSymbolPicker(context)
)
);
context.subscriptions.push(
commands.registerCommand("tinymist.profileCurrentFile", () => commandShowTrace(context))
);
context.subscriptions.push(
),
commands.registerCommand("tinymist.profileCurrentFile", () => commandShowTrace(context)),
commands.registerCommand("tinymist.showLog", () => {
if (client) {
client.outputChannel.show();
}
})
);
// context.subscriptions.push
const provider = new SymbolPickerProvider(context);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider("tinymist.side-symbol-view", provider)
);
await client.start();
@ -389,7 +370,7 @@ async function commandShowSummary(context: vscode.ExtensionContext): Promise<voi
}
async function commandShowSymbolPicker(context: vscode.ExtensionContext): Promise<void> {
await activateEditorTool(context, "symbol-picker");
await activateEditorTool(context, "symbol-view");
}
async function commandShowTrace(context: vscode.ExtensionContext): Promise<void> {
@ -529,6 +510,9 @@ let focusingDoc: vscode.TextDocument | undefined = undefined;
export function getFocusingFile() {
return focusingFile;
}
export function getLastFocusingDoc() {
return focusingDoc;
}
async function commandActivateDoc(doc: vscode.TextDocument | undefined): Promise<void> {
await commandActivateDocPath(doc, doc?.uri.fsPath);

View file

@ -23,3 +23,5 @@ dist/
*.njsproj
*.sln
*.sw?
assets/detypify

View file

@ -13,6 +13,7 @@
},
"dependencies": {
"minisearch": "^6.3.0",
"onnxruntime-web": "^1.17.3",
"vanjs-core": "^1.5.0"
},
"devDependencies": {

View file

@ -0,0 +1,77 @@
// import { ChildDom } from "vanjs-core";
import van from "vanjs-core";
const { button } = van.tags;
export function startModal(...contents: Node[]) {
// mask window with a shadow and show message in floating window
const shadow = document.createElement("div");
shadow.style.position = "fixed";
shadow.style.top = "0";
shadow.style.left = "0";
shadow.style.width = "100%";
shadow.style.height = "100%";
shadow.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
shadow.style.zIndex = "1000";
document.body.appendChild(shadow);
const floatingWindow = document.createElement("div");
floatingWindow.classList.add("tinymist-window");
floatingWindow.style.position = "fixed";
floatingWindow.style.top = "50%";
floatingWindow.style.left = "50%";
floatingWindow.style.transform = "translate(-50%, -50%)";
floatingWindow.style.width = "80%";
floatingWindow.style.maxWidth = "800px";
floatingWindow.style.height = "80%";
floatingWindow.style.maxHeight = "600px";
floatingWindow.style.backgroundColor = "var(--modal-background)";
floatingWindow.style.padding = "1rem";
floatingWindow.style.overflow = "auto";
floatingWindow.style.zIndex = "1001";
floatingWindow.style.borderRadius = "6px";
// also shows close button and help
// Press button/space/enter to close this window
const close = button(
{
class: "tinymist-button",
},
"Close"
);
const keydownHandler = (e: KeyboardEvent) => {
if (e.key === "Escape" || e.key === " " || e.key === "Enter") {
removeModal();
}
};
const removeModal = () => {
document.body.removeChild(shadow);
document.body.removeChild(floatingWindow);
window.removeEventListener("keydown", keydownHandler);
};
close.onclick = removeModal;
window.addEventListener("keydown", keydownHandler);
floatingWindow.appendChild(close);
const help = button(
{
class: "tinymist-button",
style: "margin-left: 0.5em",
title:
"Click the close button or press esc/space/enter to close this window",
},
"Help"
);
help.onclick = () => {
alert(
"Click the close button or press esc/space/enter to close this window"
);
};
floatingWindow.appendChild(help);
for (const content of contents) {
floatingWindow.appendChild(content);
}
document.body.appendChild(floatingWindow);
}

View file

@ -1 +1 @@
symbol-picker.mock.ts
symbol-view.mock.ts

View file

@ -0,0 +1,102 @@
import inferSymbols from "../../assets/detypify/infer.json";
// @ts-ignore
import modelUrl from "../../assets/detypify/model.onnx";
import { InferenceSession, Tensor, env as ortConfig } from "onnxruntime-web";
ortConfig.wasm.numThreads = 1;
ortConfig.wasm.wasmPaths =
"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.17.1/dist/";
export type Point = [number, number];
export type Stroke = Point[];
export interface DetypifySymbol {
names: string[];
codepoint: number;
}
export class Detypify {
strokes?: Stroke[];
dstCanvas: HTMLCanvasElement;
dstCtx: CanvasRenderingContext2D;
private constructor(public session: InferenceSession) {
let dstCanvas = document.createElement("canvas");
dstCanvas.width = dstCanvas.height = 32;
let dstCtx = dstCanvas.getContext("2d", { willReadFrequently: true })!;
dstCtx.fillStyle = "white";
this.dstCanvas = dstCanvas;
this.dstCtx = dstCtx;
}
static async create() {
return new Detypify(await InferenceSession.create(modelUrl));
}
async candidates(strokes: Stroke[]): Promise<DetypifySymbol[] | undefined> {
console.log("candidates", this.session, strokes);
// not loaded or clear
if (!this.session || !strokes?.length) return [];
this.drawToDst(strokes);
// to grayscale
let dstWidth = this.dstCanvas.width;
let rgba = this.dstCtx.getImageData(0, 0, dstWidth, dstWidth).data;
let grey = new Float32Array(rgba.length / 4);
for (let i = 0; i < grey.length; ++i) {
grey[i] = rgba[i * 4] == 255 ? 1 : 0;
}
// infer
let tensor = new Tensor("float32", grey, [1, 1, 32, 32]);
let output = await this.session.run({
[this.session.inputNames[0]]: tensor,
});
let ddd = Array.prototype.slice.call(
output[this.session.outputNames[0]].data
);
// select top K
let withIdx = ddd.map((x, i) => [x, i]);
withIdx.sort((a, b) => b[0] - a[0]);
let result = withIdx.slice(0, 5).map(([_, i]) => inferSymbols[i]);
console.log("candidates finished", result);
return result;
}
private drawToDst(strokes: Stroke[]) {
// find rect
let minX = Infinity;
let maxX = 0;
let minY = Infinity;
let maxY = 0;
for (let stroke of strokes) {
for (let [x, y] of stroke) {
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
}
// normalize
let dstWidth = this.dstCanvas.width;
let width = Math.max(maxX - minX, maxY - minY);
if (width == 0) return;
width = width * 1.2 + 20;
let zeroX = (minX + maxX - width) / 2;
let zeroY = (minY + maxY - width) / 2;
let scale = dstWidth / width;
// draw to dstCanvas
this.dstCtx.fillRect(0, 0, dstWidth, dstWidth);
this.dstCtx.translate(0.5, 0.5);
for (let stroke of strokes) {
this.dstCtx.beginPath();
for (let [x, y] of stroke) {
this.dstCtx.lineTo(
Math.round((x - zeroX) * scale),
Math.round((y - zeroY) * scale)
);
}
this.dstCtx.stroke();
}
this.dstCtx.translate(-0.5, -0.5);
}
}

View file

@ -1,8 +1,12 @@
import "./symbol-picker.css";
import "./symbol-view.css";
import van, { State } from "vanjs-core";
// import { SYMBOL_MOCK } from "./symbol-picker.mock";
const { div, input, canvas, button } = van.tags;
// import { SYMBOL_MOCK } from "./symbol-view.mock";
const { div, input, canvas, button, h4, a, p, span } = van.tags;
import MiniSearch from "minisearch";
import { Detypify, DetypifySymbol, Stroke } from "./symbol-view.detypify";
import { ContributeIcon, HelpIcon } from "../icons";
import { startModal } from "../components/modal";
import { requestTextEdit } from "../vscode";
interface SymbolCategory {
value?: string;
@ -86,7 +90,7 @@ const SearchBar = (
sym.categoryHuman = categoryIndex.get(sym.category);
sym.typstCode = key;
}
console.log("search", Object.values(state.val.symbols));
// console.log("search", Object.values(state.val.symbols));
search.addAll(Object.values(state.val.symbols));
return search;
});
@ -107,7 +111,7 @@ const SearchBar = (
});
};
const CanvasPanel = () => {
const CanvasPanel = (strokesState: State<Stroke[] | undefined>) => {
const srcCanvas = canvas({
width: "160",
height: "160",
@ -122,13 +126,15 @@ const CanvasPanel = () => {
srcCtx.lineJoin = "round";
srcCtx.lineCap = "round";
const dstCanvas = document.createElement("canvas");
dstCanvas.width = dstCanvas.height = 32;
const dstCtx = dstCanvas.getContext("2d", { willReadFrequently: true })!;
if (!dstCtx) {
throw new Error("Could not get context");
// todo: decouple with CanvasPanel
const serverDark = document.body.classList.contains("typst-preview-dark");
if (serverDark) {
srcCtx.fillStyle = "white";
srcCtx.strokeStyle = "white";
} else {
srcCtx.fillStyle = "black";
srcCtx.strokeStyle = "black";
}
dstCtx.fillStyle = "white";
type Point = [number, number];
type PointEvent = Pick<MouseEvent, "offsetX" | "offsetY">;
@ -136,98 +142,50 @@ const CanvasPanel = () => {
let isDrawing = false;
let currP: Point;
let stroke: Point[];
let strokes: Point[][] = [];
let minX = Infinity;
let minY = Infinity;
let maxX = 0;
let maxY = 0;
const touchCall = (fn: (e: PointEvent) => any) => (e: TouchEvent) => {
const { left: touchL, top: touchT } = srcCanvas.getBoundingClientRect();
let rect = srcCanvas.getBoundingClientRect();
fn({
offsetX: e.touches[0].clientX - touchL,
offsetY: e.touches[0].clientY - touchT,
offsetX: e.touches[0].clientX - rect.left,
offsetY: e.touches[0].clientY - rect.top,
});
};
function drawStart({ offsetX, offsetY }: PointEvent) {
isDrawing = true;
offsetX = Math.round(offsetX);
offsetY = Math.round(offsetY);
currP = [offsetX, offsetY];
stroke = [currP!];
stroke = [currP];
}
function drawMove({ offsetX, offsetY }: PointEvent) {
if (!isDrawing) return;
let prevP = currP;
offsetX = Math.round(offsetX);
offsetY = Math.round(offsetY);
srcCtx.beginPath();
srcCtx.moveTo(currP[0], currP[1]);
srcCtx.lineTo(offsetX, offsetY);
srcCtx.stroke();
currP = [offsetX, offsetY];
stroke.push(currP);
srcCtx.strokeStyle = "white";
srcCtx.beginPath();
srcCtx.moveTo(...prevP);
srcCtx.lineTo(...currP);
srcCtx.stroke();
}
function drawEnd() {
if (!isDrawing) return; // normal mouse leave
isDrawing = false;
// update
strokes.push(stroke);
let xs = stroke.map((p) => p[0]);
minX = Math.min(minX, ...xs);
maxX = Math.max(maxX, ...xs);
let ys = stroke.map((p) => p[1]);
minY = Math.min(minY, ...ys);
maxY = Math.max(maxY, ...ys);
// normalize
let dstWidth = dstCanvas.width;
let width = Math.max(maxX - minX, maxY - minY);
if (width == 0) return;
width *= 1.2;
let zeroX = (maxX + minX) / 2 - width / 2;
let zeroY = (maxY + minY) / 2 - width / 2;
let scale = dstWidth / width;
// draw to dstCanvas
dstCtx.fillRect(0, 0, dstWidth, dstWidth);
dstCtx.translate(0.5, 0.5);
for (let stroke of strokes) {
dstCtx.beginPath();
for (let [x, y] of stroke) {
dstCtx.lineTo(
Math.round((x - zeroX) * scale),
Math.round((y - zeroY) * scale)
);
}
dstCtx.stroke();
}
dstCtx.translate(-0.5, -0.5);
// // [debug] download dstCanvas image
// let img = document.createElement("a");
// img.href = dstCanvas.toDataURL();
// img.download = "test.png";
// img.click();
// to greyscale
let rgba = dstCtx.getImageData(0, 0, dstWidth, dstWidth).data;
let grey = new Float32Array(rgba.length / 4);
for (let i = 0; i < grey.length; ++i) {
grey[i] = rgba[i * 4] == 255 ? 1 : 0;
}
// greyscale = grey;
if (stroke.length === 1) return; // no line
strokesState.val = [...(strokesState.oldVal || []), stroke];
}
function drawClear() {
strokesState.val = undefined;
srcCtx.clearRect(0, 0, srcCanvas.width, srcCanvas.height);
strokes = [];
minX = minY = Infinity;
maxX = maxY = 0;
// greyscale = null;
}
srcCanvas.addEventListener("mousedown", drawStart);
@ -248,6 +206,78 @@ const CanvasPanel = () => {
{
class: "tinymist-canvas-panel",
},
div(
{
style: "float: right; margin-right: -18px; cursor: pointer;",
title: `The offline handwritten stroke recognizer is powered by Detypify. Draw a symbol to search for it.`,
onclick: () => {
startModal(
p(
"The ",
span(
{ style: "font-weight: bold; text-decoration: underline" },
"offline"
),
" handwritten stroke recognizer is powered by ",
a(
{
href: "https://github.com/QuarticCat/detypify",
},
"Detypify"
),
". Draw a symbol to search for it."
),
h4("Cannot find some symbols?"),
p(
"🔍: Check the supported symbols listed in ",
a(
{
href: "https://github.com/QuarticCat/detypify/blob/main/assets/supported-symbols.txt",
},
"supported-symbols.txt"
),
"."
),
p(
"❤️‍🔥: Click the ",
span({ style: "font-style: italic" }, "contribute mode button"),
" (",
ContributeIcon(16, true),
") and contribute at ",
a(
{
href: "https://detypify.quarticcat.com/",
},
"Detypify"
),
"."
),
p(
"📝: Report the missing symbol to ",
a(
{
href: "https://github.com/QuarticCat/detypify/issues/new",
},
"GitHub Issues"
),
"."
),
h4("Like it?"),
p(
"Give a star🌟 to the ",
a(
{
href: "https://github.com/QuarticCat/detypify",
},
"Detypify"
),
"!"
)
);
},
},
HelpIcon()
),
srcCanvas
),
button(
@ -316,6 +346,19 @@ export const SymbolPicker = () => {
: JSON.parse(atob(symbolInformationData))
);
console.log("symbolInformation", symInfo);
const detypifyPromise = Detypify.create();
const detypify = van.state<Detypify | undefined>(undefined);
detypifyPromise.then((d) => (detypify.val = d));
const strokes = van.state<Stroke[] | undefined>(undefined);
const drawCandidates = van.state<DetypifySymbol[] | undefined>();
(drawCandidates as any)._drawCandidateAsyncNode = van.derive(async () => {
let candidates;
if (strokes.val === undefined) candidates = undefined;
else if (!detypify.val || !strokes.val) candidates = [];
else candidates = await detypify.val.candidates(strokes.val);
drawCandidates.val = candidates;
});
// console.log("symbolInformationEnc", JSON.stringify(symInfo.val));
const symbolDefs = div({
@ -342,8 +385,9 @@ export const SymbolPicker = () => {
return Math.abs(max - min);
};
const bboxXWidth = diff(primaryGlyph.xMin, primaryGlyph.xMax);
let xWidth = Math.max(
diff(primaryGlyph.xMin, primaryGlyph.xMax),
bboxXWidth,
primaryGlyph.xAdvance || fontSelected.unitsPerEm
);
@ -367,8 +411,11 @@ export const SymbolPicker = () => {
? Math.abs(primaryGlyph.yMax || 0)
: (Math.abs(primaryGlyph.yMax || 0) + yWidth) / 2;
// centering-x the symbol
let xShift = -(primaryGlyph.xMin || 0) + (xWidth - bboxXWidth) / 2;
// translate(0, ${fontSelected.ascender * fontSelected.unitsPerEm})
const imageData = `<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="${symWidth}" height="${symHeight}" viewBox="0 0 ${xWidth} ${yWidth}" xmlns="http://www.w3.org/2000/svg" ><g transform="translate(0, ${yShift}) scale(1, -1)">${path?.outerHTML || ""}</g></svg>`;
const imageData = `<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="${symWidth}" height="${symHeight}" viewBox="0 0 ${xWidth} ${yWidth}" xmlns="http://www.w3.org/2000/svg" ><g transform="translate(${xShift}, ${yShift}) scale(1, -1)">${path?.outerHTML || ""}</g></svg>`;
// console.log(sym.typstCode, div({ innerHTML: imageData }));
maskInfo = `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;`;
}
@ -384,7 +431,18 @@ export const SymbolPicker = () => {
d.classList.add("active");
setTimeout(() => d.classList.remove("active"), 500);
// clipboard
navigator.clipboard.writeText(sym.typstCode || "");
const rest = sym.typstCode || "";
const markup = `#${rest}`;
// math mode will trim the sym. prefix
const math = `${rest.startsWith("sym.") ? rest.slice(4) : rest}`;
requestTextEdit({
newText: {
kind: "by-mode",
math,
markup,
rest,
},
});
},
},
maskInfo ? div({ style: maskInfo }) : null
@ -416,7 +474,7 @@ export const SymbolPicker = () => {
undefined
);
function pickSymbols(
function pickSymbolsBySearch(
pickers: { key: string; value: SymbolItem; elem: Element }[],
filteredPickers: SelectedSymbolItem[] | undefined
) {
@ -426,6 +484,22 @@ export const SymbolPicker = () => {
);
}
function pickSymbolsByDrawCandidates(
pickers: { key: string; value: SymbolItem; elem: Element }[],
drawCandidates: DetypifySymbol[] | undefined
) {
if (drawCandidates === undefined) return pickers;
if (!drawCandidates.length) return [];
return pickers.filter((picker) => {
if (!picker.value.typstCode) return false;
let c = picker.value.typstCode;
// remove sym. prefix
if (c.startsWith("sym.")) c = c.slice(4);
return drawCandidates.some((f) => f.names.includes(c));
});
}
return div(
{
class: "tinymist-symbol-main",
@ -437,13 +511,16 @@ export const SymbolPicker = () => {
style: "flex: 0 0 auto; gap: 5px",
},
SearchBar(symInfo, filteredPickers),
CanvasPanel()
CanvasPanel(strokes)
),
div({ style: "flex: 1;" }, (_dom?: Element) =>
div(
...categorize(
CATEGORY_INFO,
pickSymbols(pickers.val, filteredPickers.val)
pickSymbolsBySearch(
pickSymbolsByDrawCandidates(pickers.val, drawCandidates.val),
filteredPickers.val
)
)
.filter((cat) => cat.symbols?.length)
.map((info) => CategoryPicker(info))

View file

@ -5,6 +5,7 @@ import {
LspResponse,
traceData as traceReport,
} from "../vscode";
import { startModal } from "../components/modal";
const { div, h2, button, iframe, code, br, span } = van.tags;
const ORIGIN = "https://ui.perfetto.dev";
@ -130,73 +131,7 @@ export const Tracing = () => {
message.innerText = "";
mainWindow.style.display = "none";
// mask window with a shadow and show message in floating window
const shadow = document.createElement("div");
shadow.style.position = "fixed";
shadow.style.top = "0";
shadow.style.left = "0";
shadow.style.width = "100%";
shadow.style.height = "100%";
shadow.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
shadow.style.zIndex = "1000";
document.body.appendChild(shadow);
const floatingWindow = document.createElement("div");
floatingWindow.classList.add("tinymist-window");
floatingWindow.style.position = "fixed";
floatingWindow.style.top = "50%";
floatingWindow.style.left = "50%";
floatingWindow.style.transform = "translate(-50%, -50%)";
floatingWindow.style.width = "80%";
floatingWindow.style.maxWidth = "800px";
floatingWindow.style.height = "80%";
floatingWindow.style.maxHeight = "600px";
floatingWindow.style.backgroundColor = "var(--modal-background)";
floatingWindow.style.padding = "1rem";
floatingWindow.style.overflow = "auto";
floatingWindow.style.zIndex = "1001";
floatingWindow.style.borderRadius = "6px";
// also shows close button and help
// Press button/space/enter to close this window
const close = button(
{
class: "tinymist-button",
},
"Close"
);
const keydownHandler = (e: KeyboardEvent) => {
if (e.key === "Escape" || e.key === " " || e.key === "Enter") {
removeModal();
}
};
const removeModal = () => {
document.body.removeChild(shadow);
document.body.removeChild(floatingWindow);
window.removeEventListener("keydown", keydownHandler);
};
close.onclick = removeModal;
window.addEventListener("keydown", keydownHandler);
floatingWindow.appendChild(close);
const help = button(
{
class: "tinymist-button",
style: "margin-left: 0.5em",
title:
"Click the close button or press esc/space/enter to close this window",
},
"Help"
);
help.onclick = () => {
alert(
"Click the close button or press esc/space/enter to close this window"
);
};
floatingWindow.appendChild(help);
floatingWindow.appendChild(
startModal(
div(
{ style: "margin: 1em 0" },
...(msg.length > 0 ? [code(msg), br()] : []),
@ -217,12 +152,9 @@ export const Tracing = () => {
".",
optionalInputs(rep.request.inputs),
optionalFontPaths(rep.request.fontPaths)
)
);
floatingWindow.appendChild(
),
diagReport(diagnosticsMessage?.params) as Node
);
document.body.appendChild(floatingWindow);
if (tracingContent) {
openTrace(tracingContent);

View file

@ -1,5 +1,5 @@
import van from "vanjs-core";
const { div } = van.tags;
const { div, span } = van.tags;
export const HeartIcon = (sz: number = 16) =>
div({
@ -12,6 +12,33 @@ export const HeartIcon = (sz: number = 16) =>
</svg>`,
});
export const HelpIcon = (sz: number = 16) =>
div({
class: "tinymist-icon",
style: `height: ${sz}px; width: ${sz}px;`,
innerHTML: `<svg
viewBox="0 0 24 24"
preserveAspectRatio="xMidYMid meet"
xmlns="http://www.w3.org/2000/svg"
>
<path
class="stroke-based"
d="M9.08997 9.00007C9.32507 8.33174 9.78912 7.76818 10.3999 7.40921C11.0107 7.05023 11.7289 6.91901 12.4271 7.03879C13.1254 7.15856 13.7588 7.5216 14.215 8.0636C14.6713 8.60561 14.921 9.2916 14.92 10.0001C14.92 12.0001 11.92 13.0001 11.92 13.0001M12 17.0001H12.01M3 7.94153V16.0586C3 16.4013 3 16.5726 3.05048 16.7254C3.09515 16.8606 3.16816 16.9847 3.26463 17.0893C3.37369 17.2077 3.52345 17.2909 3.82297 17.4573L11.223 21.5684C11.5066 21.726 11.6484 21.8047 11.7985 21.8356C11.9315 21.863 12.0685 21.863 12.2015 21.8356C12.3516 21.8047 12.4934 21.726 12.777 21.5684L20.177 17.4573C20.4766 17.2909 20.6263 17.2077 20.7354 17.0893C20.8318 16.9847 20.9049 16.8606 20.9495 16.7254C21 16.5726 21 16.4013 21 16.0586V7.94153C21 7.59889 21 7.42756 20.9495 7.27477C20.9049 7.13959 20.8318 7.01551 20.7354 6.91082C20.6263 6.79248 20.4766 6.70928 20.177 6.54288L12.777 2.43177C12.4934 2.27421 12.3516 2.19543 12.2015 2.16454C12.0685 2.13721 11.9315 2.13721 11.7985 2.16454C11.6484 2.19543 11.5066 2.27421 11.223 2.43177L3.82297 6.54288C3.52345 6.70928 3.37369 6.79248 3.26463 6.91082C3.16816 7.01551 3.09515 7.13959 3.05048 7.27477C3 7.42756 3 7.59889 3 7.94153Z"
stroke-width="2"
fill-rule="nonzero"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>`,
});
export const ContributeIcon = (sz: number = 16, inline?: boolean) =>
(inline ? span : div)({
class: "tinymist-icon",
style: `height: ${sz}px; width: ${sz}px;`,
innerHTML: `<svg xmlns="http://www.w3.org/2000/svg" width="${sz}px" height="${sz}px" class="shrink-0 w-5 h-5 inline align-text-top" role="img" aria-label="fire solid" viewBox="0 0 24 24"><path d="M8.597 3.2A1 1 0 0 0 7.04 4.289a3.49 3.49 0 0 1 .057 1.795 3.448 3.448 0 0 1-.84 1.575.999.999 0 0 0-.077.094c-.596.817-3.96 5.6-.941 10.762l.03.049a7.73 7.73 0 0 0 2.917 2.602 7.617 7.617 0 0 0 3.772.829 8.06 8.06 0 0 0 3.986-.975 8.185 8.185 0 0 0 3.04-2.864c1.301-2.2 1.184-4.556.588-6.441-.583-1.848-1.68-3.414-2.607-4.102a1 1 0 0 0-1.594.757c-.067 1.431-.363 2.551-.794 3.431-.222-2.407-1.127-4.196-2.224-5.524-1.147-1.39-2.564-2.3-3.323-2.788a8.487 8.487 0 0 1-.432-.287Z"></path></svg>`,
});
export const AddIcon = (sz: number = 16) =>
div({
class: "tinymist-icon",

View file

@ -5,7 +5,7 @@ import { TemplateGallery } from "./features/template-gallery";
import { Tracing } from "./features/tracing";
import { Summary } from "./features/summary";
import { Diagnostics } from "./features/diagnostics";
import { SymbolPicker } from "./features/symbol-picker";
import { SymbolPicker } from "./features/symbol-view";
/// The components that can be rendered by the frontend.
/// Typically, each component corresponds to a single tool (Application).
@ -14,7 +14,7 @@ type PageComponent =
| "tracing"
| "summary"
| "diagnostics"
| "symbol-picker";
| "symbol-view";
/// The frontend arguments that are passed from the backend.
interface Arguments {
@ -31,7 +31,7 @@ function retrieveArgs(): Arguments {
/// let frontend_html = frontend_html.replace(
/// "editor-tools-args:{}", ...);
/// ```
let mode = `editor-tools-args:{"page": "tracing"}`;
let mode = `editor-tools-args:{"page": "symbol-view"}`;
/// Remove the placeholder prefix.
mode = mode.replace("editor-tools-args:", "");
@ -58,7 +58,7 @@ function main() {
case "diagnostics":
van.add(appHook, Diagnostics());
break;
case "symbol-picker":
case "symbol-view":
van.add(appHook, SymbolPicker());
break;
default:

View file

@ -168,7 +168,12 @@ body.typst-preview-light .tinymist-button.warning.activated {
}
.tinymist-icon path {
fill: WindowText;
fill: currentColor;
}
.tinymist-icon path.stroke-based {
fill: none;
stroke: currentColor;
}
.hidden {

View file

@ -80,3 +80,29 @@ export function requestRevealPath(path: string) {
vscodeAPI.postMessage({ type: "revealPath", path });
}
}
export interface TextEdit {
range?: undefined;
newText:
| string
| {
kind: "by-mode";
math?: string;
markup?: string;
code?: string;
rest?: string;
};
}
export function requestTextEdit(edit: TextEdit) {
if (vscodeAPI?.postMessage) {
vscodeAPI.postMessage({ type: "editText", edit });
} else {
// copy to clipboard
navigator.clipboard.writeText(
typeof edit.newText === "string"
? edit.newText
: edit.newText.code || edit.newText.rest || ""
);
}
}

View file

@ -3,6 +3,7 @@ import { viteSingleFile } from "vite-plugin-singlefile";
export default defineConfig({
plugins: [viteSingleFile()],
assetsInclude: ["assets/**/*.onnx"],
build: {
minify: false,
rollupOptions: {

135
yarn.lock
View file

@ -333,6 +333,59 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
"@protobufjs/base64@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
"@protobufjs/codegen@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
"@protobufjs/eventemitter@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
"@protobufjs/fetch@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
dependencies:
"@protobufjs/aspromise" "^1.1.1"
"@protobufjs/inquire" "^1.1.0"
"@protobufjs/float@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
"@protobufjs/inquire@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
"@protobufjs/path@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
"@protobufjs/pool@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
"@protobufjs/utf8@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
"@sinclair/typebox@^0.27.8":
version "0.27.8"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
@ -367,6 +420,13 @@
dependencies:
undici-types "~5.26.4"
"@types/node@>=13.7.0":
version "20.12.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384"
integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==
dependencies:
undici-types "~5.26.4"
"@types/node@^20.8.10":
version "20.11.25"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.25.tgz#0f50d62f274e54dd7a49f7704cc16bfbcccaf49f"
@ -1484,6 +1544,11 @@ flat-cache@^3.0.4:
keyv "^4.5.3"
rimraf "^3.0.2"
flatbuffers@^1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-1.12.0.tgz#72e87d1726cb1b216e839ef02658aa87dcef68aa"
integrity sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==
flatted@^3.2.9:
version "3.3.1"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
@ -1656,6 +1721,11 @@ graphemer@^1.4.0:
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
guid-typescript@^1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/guid-typescript/-/guid-typescript-1.0.9.tgz#e35f77003535b0297ea08548f5ace6adb1480ddc"
integrity sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==
has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
@ -2012,6 +2082,11 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
long@^5.0.0, long@^5.2.3:
version "5.2.3"
resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
loupe@^2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
@ -2233,6 +2308,23 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
onnxruntime-common@1.17.3:
version "1.17.3"
resolved "https://registry.yarnpkg.com/onnxruntime-common/-/onnxruntime-common-1.17.3.tgz#aadc456477873a540ee3d611ae9cd4f3de7c43e5"
integrity sha512-IkbaDelNVX8cBfHFgsNADRIq2TlXMFWW+nG55mwWvQT4i0NZb32Jf35Pf6h9yjrnK78RjcnlNYaI37w394ovMw==
onnxruntime-web@^1.17.3:
version "1.17.3"
resolved "https://registry.yarnpkg.com/onnxruntime-web/-/onnxruntime-web-1.17.3.tgz#943063f9ddad0e5f787bc934938dd6c7fbe4897a"
integrity sha512-MSDrNUWgc1biP0YzY488OJ9n/jTMS9EXysgm9Aw4CUj2A836ALbO2J1sgzguWJeVUHTlM6p7tRzo8IGAgaXWKw==
dependencies:
flatbuffers "^1.12.0"
guid-typescript "^1.0.9"
long "^5.2.3"
onnxruntime-common "1.17.3"
platform "^1.3.6"
protobufjs "^7.2.4"
optionator@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
@ -2375,6 +2467,11 @@ pkg-types@^1.0.3:
mlly "^1.2.0"
pathe "^1.1.0"
platform@^1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
possible-typed-array-names@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
@ -2426,6 +2523,24 @@ pretty-format@^29.5.0:
ansi-styles "^5.0.0"
react-is "^18.0.0"
protobufjs@^7.2.4:
version "7.2.6"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215"
integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==
dependencies:
"@protobufjs/aspromise" "^1.1.2"
"@protobufjs/base64" "^1.1.2"
"@protobufjs/codegen" "^2.0.4"
"@protobufjs/eventemitter" "^1.1.0"
"@protobufjs/fetch" "^1.1.0"
"@protobufjs/float" "^1.0.2"
"@protobufjs/inquire" "^1.1.0"
"@protobufjs/path" "^1.1.2"
"@protobufjs/pool" "^1.1.0"
"@protobufjs/utf8" "^1.1.0"
"@types/node" ">=13.7.0"
long "^5.0.0"
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
@ -2678,16 +2793,7 @@ std-env@^3.3.3:
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2"
integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0:
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -2739,14 +2845,7 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==