mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-25 05:22:52 +00:00
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:
parent
5ad5294fca
commit
bbc6e3e4e9
21 changed files with 744 additions and 253 deletions
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
1
editors/vscode/.gitignore
vendored
1
editors/vscode/.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
tinymist-*.vsix
|
||||
*.log
|
||||
icons/ti.svg
|
||||
BIN
editors/vscode/icons/ti.png
Normal file
BIN
editors/vscode/icons/ti.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 957 B |
|
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
2
tools/editor-tools/.gitignore
vendored
2
tools/editor-tools/.gitignore
vendored
|
|
@ -23,3 +23,5 @@ dist/
|
|||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
assets/detypify
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"minisearch": "^6.3.0",
|
||||
"onnxruntime-web": "^1.17.3",
|
||||
"vanjs-core": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
77
tools/editor-tools/src/components/modal.ts
Normal file
77
tools/editor-tools/src/components/modal.ts
Normal 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);
|
||||
}
|
||||
2
tools/editor-tools/src/features/.gitignore
vendored
2
tools/editor-tools/src/features/.gitignore
vendored
|
|
@ -1 +1 @@
|
|||
symbol-picker.mock.ts
|
||||
symbol-view.mock.ts
|
||||
102
tools/editor-tools/src/features/symbol-view.detypify.ts
Normal file
102
tools/editor-tools/src/features/symbol-view.detypify.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 || ""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
135
yarn.lock
|
|
@ -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==
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue