diff --git a/CHANGELOG.md b/CHANGELOG.md index a296e52..73960e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.8.0] - 2023-10-05 + +Updated [crossnote](https://github.com/shd101wyy/crossnote) to version [0.8.17](https://github.com/shd101wyy/crossnote/releases/tag/0.8.17) then version [0.8.18](https://github.com/shd101wyy/crossnote/releases/tag/0.8.18). + +### New features + +- 📝 Supported in-preview editor that allows you to edit the markdown file directly in the preview 🎉. + This feature is currently in beta. + When the editor is open, you can press `ctrl+s` or `cmd+s` to save the markdown file. You can also press `esc` to close the editor. +- Deprecated the VS Code setting `markdown-preview-enhanced.singlePreview`. + Now replaced by `markdown-preview-enhanced.previewMode`: + + - **Single Preview** (_default_) + Only one preview will be shown for all editors. + - **Multiple Previews** + Multiple previews will be shown. Each editor has its own preview. + - **Previews Only** 🆕 + No editor will be shown. Only previews will be shown. You can use the in-preview editor to edit the markdown. + + 🔔 Please note that enable this option will automatically modify the `workbench.editorAssociations` setting to make sure the markdown files are opened in the custom editor for preview. + +- Added two new VS Code commands `Markdown Preview Enhanced: Customize Preview Html Head (Workspace)` and `Markdown Preview Enhanced: Customize Preview Html Head (Global)`, which will open the `head.html` file for you to customize the `` of the preview. + +- Supported to set attribute to image and link, e.g.: + + ```markdown + ![](path/to/image.png){width=100 height=100} + ``` + +- Improved the markdown transformer to better insert anchors for scroll sync and highlight lines and elements. + Added more tests for the markdown transformer to make sure it works as expected. +- Added the reading time estimation in the preview footer ⏲️. +- Added `Edit Markdown` menu item to the context menu of the preview, which offers two options: + - **Open VS Code Editor** + Open the markdown file in VS Code editor. + - **Open In-preview Editor** + Open the markdown file in the in-preview editor. +- Updated the mermaid version to the latest `10.5.0` +- Updated the `katex` version to `0.16.9`. +- Added the API website: https://shd101wyy.github.io/crossnote/ + +### Bug fixes + +- Fixed the font size of the `github-dark.css` code block theme. +- Fixed the anchor jump bugs: https://github.com/shd101wyy/vscode-markdown-preview-enhanced/issues/1790 +- Fixed list item style bug: https://github.com/shd101wyy/vscode-markdown-preview-enhanced/issues/1789 +- Fixed a data race bug that caused the preview to hang. + ## [0.7.10] - 2023-09-24 Updated [crossnote](https://github.com/shd101wyy/crossnote) to version [0.8.16](https://github.com/shd101wyy/crossnote/releases/tag/0.8.16) diff --git a/build.js b/build.js index 7a9e874..02d57a1 100644 --- a/build.js +++ b/build.js @@ -1,3 +1,4 @@ +const { execSync } = require('child_process'); const { context, build } = require('esbuild'); const { polyfillNode } = require('esbuild-plugin-polyfill-node'); @@ -10,6 +11,10 @@ const esbuildProblemMatcherPlugin = { setup(build) { build.onStart(() => { console.log('[watch] build started'); + + // Run `gulp copy:files` before build + execSync('gulp copy-files'); + console.log('[watch] gulp copy-files'); }); build.onEnd((result) => { if (result.errors.length) { @@ -18,7 +23,9 @@ const esbuildProblemMatcherPlugin = { `> ${error.location.file}:${error.location.line}:${error.location.column}: error: ${error.text}`, ), ); - } else console.log('[watch] build finished'); + } else { + console.log('[watch] build finished'); + } }); }, }; diff --git a/package.json b/package.json index 7c89630..0e76ebe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "markdown-preview-enhanced", "displayName": "%displayName%", - "version": "0.7.10", + "version": "0.8.0", "description": "%description%", "categories": [ "Other" @@ -23,11 +23,10 @@ "main": "./out/native/extension.js", "browser": "./out/web/extension.js", "scripts": { - "build": "yarn copy:files && gulp clean-out && node build.js", + "build": "gulp copy-files && gulp clean-out && node build.js", "check:all": "yarn check:eslint && yarn check:prettier", "check:eslint": "eslint \"**/*\"", "check:prettier": "prettier --check \"**/*.*\"", - "copy:files": "gulp copy-files", "fix:all": "yarn fix:eslint && yarn fix:eslint && yarn fix:prettier", "fix:eslint": "eslint --fix \"**/*\"", "fix:prettier": "prettier --write \"**/*.*\"", @@ -36,7 +35,7 @@ "run-in-vscode-dev": "npx serve --cors -l 5000 --ssl-cert $HOME/certs/localhost.pem --ssl-key $HOME/certs/localhost-key.pem", "test": "yarn build && node ./node_modules/vscode/bin/test", "vscode:prepublish": "yarn install && yarn build", - "watch": "yarn copy:files && gulp clean-out && node build.js --watch" + "watch": "gulp copy-files && gulp clean-out && node build.js --watch" }, "contributes": { "commands": [ @@ -122,6 +121,11 @@ "title": "%markdown-preview-enhanced.extendParser.title%", "enablement": "!isWeb" }, + { + "command": "markdown-preview-enhanced.customizePreviewHtmlHead", + "title": "%markdown-preview-enhanced.customizePreviewHtmlHead.title%", + "enablement": "!isWeb" + }, { "command": "markdown-preview-enhanced.openConfigScriptInWorkspace", "title": "%markdown-preview-enhanced.openConfigScriptInWorkspace.title%" @@ -130,6 +134,10 @@ "command": "markdown-preview-enhanced.extendParserInWorkspace", "title": "%markdown-preview-enhanced.extendParserInWorkspace.title%" }, + { + "command": "markdown-preview-enhanced.customizePreviewHtmlHeadInWorkspace", + "title": "%markdown-preview-enhanced.customizePreviewHtmlHeadInWorkspace.title%" + }, { "command": "markdown-preview-enhanced.showUploadedImages", "title": "%markdown-preview-enhanced.showUploadedImages.title%", @@ -141,7 +149,7 @@ "title": "Markdown Preview Enhanced", "properties": { "markdown-preview-enhanced.configPath": { - "markdownDescription": "Restart is required after changes. The configuration directory path. Leave it empty to use `$HOME/.crossnote` for Windows or `$XDG_CONFIG_HOME/.crossnote` or `$HOME/.local/state/crossnote` as the config path.", + "markdownDescription": "Restart is required after changes. The global configuration directory path. Leave it empty to use `$HOME/.crossnote` for Windows or `$XDG_CONFIG_HOME/.crossnote` or `$HOME/.local/state/crossnote` as the config path.", "default": "", "type": "string" }, @@ -165,10 +173,15 @@ "default": true, "type": "boolean" }, - "markdown-preview-enhanced.singlePreview": { - "description": "Open Only One Preview.", - "default": true, - "type": "boolean" + "markdown-preview-enhanced.previewMode": { + "markdownDescription": "- **Single Preview**: Only one preview will be shown for all editors.\n- **Multiple Previews**: Multiple previews will be shown. Each editor has its own preview.\n- **Previews Only**: No editor will be shown. Only previews will be shown. You can use the in-preview editor to edit the markdown.\n\nRestart is required after changes.", + "type": "string", + "enum": [ + "Single Preview", + "Multiple Previews", + "Previews Only" + ], + "default": "Single Preview" }, "markdown-preview-enhanced.automaticallyShowPreviewOfMarkdownBeingEdited": { "description": "Automatically show preview of markdown being edited.", @@ -408,22 +421,22 @@ "qiniu" ] }, - "markdown-preview-enhanced.AccessKey": { + "markdown-preview-enhanced.qiniuAccessKey": { "type": "string", "default": "", "description": "Qiniu AccessKey" }, - "markdown-preview-enhanced.SecretKey": { + "markdown-preview-enhanced.qiniuSecretKey": { "type": "string", "default": "", "description": "Qiniu SecretKey" }, - "markdown-preview-enhanced.Bucket": { + "markdown-preview-enhanced.qiniuBucket": { "type": "string", "default": "", "description": "Qiniu Bucket" }, - "markdown-preview-enhanced.Domain": { + "markdown-preview-enhanced.qiniuDomain": { "type": "string", "default": "http://", "description": "Qiniu Domain" @@ -556,6 +569,18 @@ } } }, + "customEditors": [ + { + "viewType": "markdown-preview-enhanced", + "displayName": "%customEditorPreviewDisplayName%", + "selector": [ + { + "filenamePattern": "*.{md,markdown,mdown,mkdn,mkd,rmd,qmd}" + } + ], + "priority": "option" + } + ], "keybindings": [ { "command": "markdown-preview-enhanced.openPreviewToTheSide", @@ -620,7 +645,8 @@ "dependencies": { "@types/crypto-js": "^4.1.2", "@types/vfile": "^3.0.2", - "crossnote": "^0.8.16", + "async-mutex": "^0.4.0", + "crossnote": "^0.8.18", "crypto-js": "^4.1.1" }, "devDependencies": { diff --git a/package.nls.json b/package.nls.json index 66cc0e1..41f24b4 100644 --- a/package.nls.json +++ b/package.nls.json @@ -1,6 +1,7 @@ { "displayName": "Markdown Preview Enhanced", "description": "Markdown Preview Enhanced ported to vscode", + "customEditorPreviewDisplayName": "Markdown Preview Enhanced", "markdown-preview-enhanced.openPreviewToTheSide.title": "Markdown Preview Enhanced: Open Preview to the Side", "markdown-preview-enhanced.openPreview.title": "Markdown Preview Enhanced: Open Preview", "markdown-preview-enhanced.toggleScrollSync.title": "Markdown Preview Enhanced: Toggle Scroll Sync", @@ -20,5 +21,7 @@ "markdown-preview-enhanced.openConfigScriptInWorkspace.title": "Markdown Preview Enhanced: Open Config Script (Workspace)", "markdown-preview-enhanced.extendParser.title": "Markdown Preview Enhanced: Extend Parser (Global)", "markdown-preview-enhanced.extendParserInWorkspace.title": "Markdown Preview Enhanced: Extend Parser (Workspace)", + "markdown-preview-enhanced.customizePreviewHtmlHead.title": "Markdown Preview Enhanced: Customize Preview Html Head (Global)", + "markdown-preview-enhanced.customizePreviewHtmlHeadInWorkspace.title": "Markdown Preview Enhanced: Customize Preview Html Head (Workspace)", "markdown-preview-enhanced.showUploadedImages.title": "Markdown Preview Enhanced: Show Uploaded Images" } diff --git a/package.nls.zh.json b/package.nls.zh.json index 20db3ac..b02a239 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -1,6 +1,7 @@ { "displayName": "Markdown 增强预览", "description": " 这款插件意在让你拥有飘逸的 Markdown 写作体验", + "customEditorPreviewDisplayName": "Markdown 增强预览", "markdown-preview-enhanced.openPreviewToTheSide.title": "MPE:打开侧边预览", "markdown-preview-enhanced.openPreview.title": "MPE:打开预览", "markdown-preview-enhanced.toggleScrollSync.title": "MPE:开关预览滑动同步", @@ -20,5 +21,7 @@ "markdown-preview-enhanced.openConfigScriptInWorkspace.title": "MPE:打开配置脚本(工作区)", "markdown-preview-enhanced.extendParser.title": "MPE:扩展 Parser(全局)", "markdown-preview-enhanced.extendParserInWorkspace.title": "MPE:扩展 Parser(工作区)", + "markdown-preview-enhanced.customizePreviewHtmlHead.title": "MPE:自定义预览 HTML 头部(全局)", + "markdown-preview-enhanced.customizePreviewHtmlHeadInWorkspace.title": "MPE:自定义预览 HTML 头部(工作区)", "markdown-preview-enhanced.showUploadedImages.title": "MPE:显示图片上传历史" } diff --git a/prettier.config.js b/prettier.config.js index 8148853..ddcacbd 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,9 +1,10 @@ /**@type {import("prettier").Config} */ module.exports = { + semi: true, arrowParens: 'always', endOfLine: 'lf', quoteProps: 'consistent', + trailingComma: 'all', singleQuote: true, tabWidth: 2, - trailingComma: 'all', }; diff --git a/src/config.ts b/src/config.ts index 4f7e120..4b620ea 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,14 +2,18 @@ import { CodeBlockTheme, FrontMatterRenderingOption, ImageUploader, + KatexOptions, MathRenderingOption, + MermaidConfig, MermaidTheme, NotebookConfig, ParserConfig, + PreviewMode, PreviewTheme, RevealJsTheme, getDefaultNotebookConfig, } from 'crossnote'; +import { JsonObject } from 'type-fest'; import * as vscode from 'vscode'; import { isVSCodeWebExtension } from './utils'; @@ -19,6 +23,22 @@ export enum PreviewColorScheme { editorColorScheme = 'editorColorScheme', } +type VSCodeMPEConfigKey = + | 'automaticallyShowPreviewOfMarkdownBeingEdited' + | 'configPath' + | 'imageUploader' + | 'hideDefaultVSCodeMarkdownPreviewButtons' + | 'liveUpdate' + | 'previewColorScheme' + | 'previewMode' + | 'qiniuAccessKey' + | 'qiniuBucket' + | 'qiniuDomain' + | 'qiniuSecretKey' + | 'scrollSync'; + +type ConfigKey = keyof NotebookConfig | VSCodeMPEConfigKey; + export class MarkdownPreviewEnhancedConfig implements NotebookConfig { public static getCurrentConfig() { return new MarkdownPreviewEnhancedConfig(); @@ -47,7 +67,6 @@ export class MarkdownPreviewEnhancedConfig implements NotebookConfig { public readonly revealjsTheme: RevealJsTheme; public readonly protocolsWhiteList: string; public readonly imageFolderPath: string; - public readonly imageUploader: ImageUploader; public readonly printBackground: boolean; public readonly chromePath: string; public readonly imageMagickPath: string; @@ -66,162 +85,167 @@ export class MarkdownPreviewEnhancedConfig implements NotebookConfig { public readonly puppeteerArgs: string[]; public readonly plantumlServer: string; public readonly plantumlJarPath: string; - public readonly hideDefaultVSCodeMarkdownPreviewButtons: boolean; public readonly jsdelivrCdnHost: string; public readonly krokiServer: string; public readonly alwaysShowBacklinksInPreview: boolean; + // Don't set values for these properties in constructor: + public readonly includeInHeader: string; + public readonly globalCss: string; + public readonly mermaidConfig: MermaidConfig; + public readonly mathjaxConfig: JsonObject; + public readonly katexConfig: KatexOptions; + public readonly parserConfig: ParserConfig; + public readonly isVSCode: boolean = true; // preview config - public readonly scrollSync: boolean; - public readonly liveUpdate: boolean; - public readonly singlePreview: boolean; public readonly automaticallyShowPreviewOfMarkdownBeingEdited: boolean; + public readonly hideDefaultVSCodeMarkdownPreviewButtons: boolean; + public readonly imageUploader: ImageUploader; + public readonly liveUpdate: boolean; public readonly previewColorScheme: PreviewColorScheme; + public readonly previewMode: PreviewMode; + public readonly scrollSync: boolean; private constructor() { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); const defaultConfig = getDefaultNotebookConfig(); this.markdownFileExtensions = - config.get('markdownFileExtensions') ?? + getMPEConfig('markdownFileExtensions') ?? defaultConfig.markdownFileExtensions; - this.configPath = config.get('configPath') ?? ''; + this.configPath = getMPEConfig('configPath') ?? ''; this.usePandocParser = isVSCodeWebExtension() ? false // pandoc is not supported in web extension - : config.get('usePandocParser') ?? defaultConfig.usePandocParser; + : getMPEConfig('usePandocParser') ?? + defaultConfig.usePandocParser; this.breakOnSingleNewLine = - config.get('breakOnSingleNewLine') ?? + getMPEConfig('breakOnSingleNewLine') ?? defaultConfig.breakOnSingleNewLine; this.enableTypographer = - config.get('enableTypographer') ?? + getMPEConfig('enableTypographer') ?? defaultConfig.enableTypographer; this.enableWikiLinkSyntax = - config.get('enableWikiLinkSyntax') ?? + getMPEConfig('enableWikiLinkSyntax') ?? defaultConfig.enableWikiLinkSyntax; this.enableLinkify = - config.get('enableLinkify') ?? defaultConfig.enableLinkify; + getMPEConfig('enableLinkify') ?? defaultConfig.enableLinkify; this.useGitHubStylePipedLink = - config.get('useGitHubStylePipedLink') ?? + getMPEConfig('useGitHubStylePipedLink') ?? defaultConfig.useGitHubStylePipedLink; this.enableEmojiSyntax = - config.get('enableEmojiSyntax') ?? + getMPEConfig('enableEmojiSyntax') ?? defaultConfig.enableEmojiSyntax; this.enableExtendedTableSyntax = - config.get('enableExtendedTableSyntax') ?? + getMPEConfig('enableExtendedTableSyntax') ?? defaultConfig.enableExtendedTableSyntax; this.enableCriticMarkupSyntax = - config.get('enableCriticMarkupSyntax') ?? + getMPEConfig('enableCriticMarkupSyntax') ?? defaultConfig.enableCriticMarkupSyntax; this.frontMatterRenderingOption = - config.get('frontMatterRenderingOption') ?? + getMPEConfig('frontMatterRenderingOption') ?? defaultConfig.frontMatterRenderingOption; this.mermaidTheme = - config.get('mermaidTheme') ?? defaultConfig.mermaidTheme; + getMPEConfig('mermaidTheme') ?? defaultConfig.mermaidTheme; this.mathRenderingOption = - (config.get('mathRenderingOption') as MathRenderingOption) ?? + (getMPEConfig('mathRenderingOption') as MathRenderingOption) ?? defaultConfig.mathRenderingOption; this.mathInlineDelimiters = - config.get('mathInlineDelimiters') ?? + getMPEConfig('mathInlineDelimiters') ?? defaultConfig.mathInlineDelimiters; this.mathBlockDelimiters = - config.get('mathBlockDelimiters') ?? + getMPEConfig('mathBlockDelimiters') ?? defaultConfig.mathBlockDelimiters; this.mathRenderingOnlineService = - config.get('mathRenderingOnlineService') ?? + getMPEConfig('mathRenderingOnlineService') ?? defaultConfig.mathRenderingOnlineService; this.mathjaxV3ScriptSrc = - config.get('mathjaxV3ScriptSrc') ?? + getMPEConfig('mathjaxV3ScriptSrc') ?? defaultConfig.mathjaxV3ScriptSrc; this.codeBlockTheme = - config.get('codeBlockTheme') ?? + getMPEConfig('codeBlockTheme') ?? defaultConfig.codeBlockTheme; this.previewTheme = - config.get('previewTheme') ?? defaultConfig.previewTheme; + getMPEConfig('previewTheme') ?? defaultConfig.previewTheme; this.revealjsTheme = - config.get('revealjsTheme') ?? defaultConfig.revealjsTheme; + getMPEConfig('revealjsTheme') ?? + defaultConfig.revealjsTheme; this.protocolsWhiteList = - config.get('protocolsWhiteList') ?? + getMPEConfig('protocolsWhiteList') ?? defaultConfig.protocolsWhiteList; this.imageFolderPath = - config.get('imageFolderPath') ?? defaultConfig.imageFolderPath; - this.imageUploader = config.get('imageUploader') ?? 'imgur'; + getMPEConfig('imageFolderPath') ?? defaultConfig.imageFolderPath; + this.imageUploader = + getMPEConfig('imageUploader') ?? 'imgur'; this.printBackground = - config.get('printBackground') ?? defaultConfig.printBackground; + getMPEConfig('printBackground') ?? defaultConfig.printBackground; this.chromePath = - config.get('chromePath') ?? defaultConfig.chromePath; + getMPEConfig('chromePath') ?? defaultConfig.chromePath; this.imageMagickPath = - config.get('imageMagickPath') ?? defaultConfig.imageMagickPath; + getMPEConfig('imageMagickPath') ?? defaultConfig.imageMagickPath; this.pandocPath = - config.get('pandocPath') ?? defaultConfig.pandocPath; + getMPEConfig('pandocPath') ?? defaultConfig.pandocPath; this.pandocMarkdownFlavor = - config.get('pandocMarkdownFlavor') ?? + getMPEConfig('pandocMarkdownFlavor') ?? defaultConfig.pandocMarkdownFlavor; this.pandocArguments = - config.get('pandocArguments') ?? defaultConfig.pandocArguments; + getMPEConfig('pandocArguments') ?? + defaultConfig.pandocArguments; this.latexEngine = - config.get('latexEngine') ?? defaultConfig.latexEngine; + getMPEConfig('latexEngine') ?? defaultConfig.latexEngine; this.enableScriptExecution = - config.get('enableScriptExecution') ?? + getMPEConfig('enableScriptExecution') ?? defaultConfig.enableScriptExecution; - this.scrollSync = config.get('scrollSync') ?? true; - this.liveUpdate = config.get('liveUpdate') ?? true; - this.singlePreview = config.get('singlePreview') ?? true; + this.scrollSync = getMPEConfig('scrollSync') ?? true; + this.liveUpdate = getMPEConfig('liveUpdate') ?? true; + this.previewMode = + getMPEConfig('previewMode') ?? PreviewMode.SinglePreview; this.automaticallyShowPreviewOfMarkdownBeingEdited = - config.get('automaticallyShowPreviewOfMarkdownBeingEdited') ?? + getMPEConfig('automaticallyShowPreviewOfMarkdownBeingEdited') ?? false; this.previewColorScheme = - config.get('previewColorScheme') ?? + getMPEConfig('previewColorScheme') ?? PreviewColorScheme.selectedPreviewTheme; this.enableHTML5Embed = - config.get('enableHTML5Embed') ?? defaultConfig.enableHTML5Embed; + getMPEConfig('enableHTML5Embed') ?? + defaultConfig.enableHTML5Embed; this.HTML5EmbedUseImageSyntax = - config.get('HTML5EmbedUseImageSyntax') ?? + getMPEConfig('HTML5EmbedUseImageSyntax') ?? defaultConfig.HTML5EmbedUseImageSyntax; this.HTML5EmbedUseLinkSyntax = - config.get('HTML5EmbedUseLinkSyntax') ?? + getMPEConfig('HTML5EmbedUseLinkSyntax') ?? defaultConfig.HTML5EmbedUseLinkSyntax; this.HTML5EmbedIsAllowedHttp = - config.get('HTML5EmbedIsAllowedHttp') ?? + getMPEConfig('HTML5EmbedIsAllowedHttp') ?? defaultConfig.HTML5EmbedIsAllowedHttp; this.HTML5EmbedAudioAttributes = - config.get('HTML5EmbedAudioAttributes') ?? + getMPEConfig('HTML5EmbedAudioAttributes') ?? defaultConfig.HTML5EmbedAudioAttributes; this.HTML5EmbedVideoAttributes = - config.get('HTML5EmbedVideoAttributes') ?? + getMPEConfig('HTML5EmbedVideoAttributes') ?? defaultConfig.HTML5EmbedVideoAttributes; this.puppeteerWaitForTimeout = - config.get('puppeteerWaitForTimeout') ?? + getMPEConfig('puppeteerWaitForTimeout') ?? defaultConfig.puppeteerWaitForTimeout; this.puppeteerArgs = - config.get('puppeteerArgs') ?? defaultConfig.puppeteerArgs; + getMPEConfig('puppeteerArgs') ?? defaultConfig.puppeteerArgs; this.plantumlJarPath = - config.get('plantumlJarPath') ?? defaultConfig.plantumlJarPath; + getMPEConfig('plantumlJarPath') ?? defaultConfig.plantumlJarPath; this.plantumlServer = - config.get('plantumlServer') ?? defaultConfig.plantumlServer; + getMPEConfig('plantumlServer') ?? defaultConfig.plantumlServer; if (!this.plantumlServer && isVSCodeWebExtension()) { this.plantumlServer = 'https://kroki.io/plantuml/svg/'; } this.hideDefaultVSCodeMarkdownPreviewButtons = - config.get('hideDefaultVSCodeMarkdownPreviewButtons') ?? true; + getMPEConfig('hideDefaultVSCodeMarkdownPreviewButtons') ?? true; this.jsdelivrCdnHost = - config.get('jsdelivrCdnHost') ?? defaultConfig.jsdelivrCdnHost; + getMPEConfig('jsdelivrCdnHost') ?? defaultConfig.jsdelivrCdnHost; this.krokiServer = - config.get('krokiServer') ?? defaultConfig.krokiServer; + getMPEConfig('krokiServer') ?? defaultConfig.krokiServer; this.alwaysShowBacklinksInPreview = - config.get('alwaysShowBacklinksInPreview') ?? + getMPEConfig('alwaysShowBacklinksInPreview') ?? defaultConfig.alwaysShowBacklinksInPreview; } - globalCss: string; - mermaidConfig; - mathjaxConfig; - katexConfig; - parserConfig: ParserConfig; - isVSCode: boolean; - public isEqualTo(otherConfig: MarkdownPreviewEnhancedConfig) { const json1 = JSON.stringify(this); const json2 = JSON.stringify(otherConfig); @@ -230,3 +254,18 @@ export class MarkdownPreviewEnhancedConfig implements NotebookConfig { [key: string]: any; } + +export function getMPEConfig(section: ConfigKey) { + const config = vscode.workspace.getConfiguration('markdown-preview-enhanced'); + return config.get(section); +} + +export function updateMPEConfig( + section: ConfigKey, + value: T, + configurationTarget?: boolean | vscode.ConfigurationTarget | null | undefined, + overrideInLanguage?: boolean | undefined, +) { + const config = vscode.workspace.getConfiguration('markdown-preview-enhanced'); + return config.update(section, value, configurationTarget, overrideInLanguage); +} diff --git a/src/extension-common.ts b/src/extension-common.ts index 47485de..7df4819 100644 --- a/src/extension-common.ts +++ b/src/extension-common.ts @@ -1,13 +1,16 @@ // For both node.js and browser environments -import { utility } from 'crossnote'; +import { PreviewMode, utility } from 'crossnote'; import { SHA256 } from 'crypto-js'; import * as vscode from 'vscode'; -import { PreviewColorScheme } from './config'; +import { PreviewColorScheme, getMPEConfig, updateMPEConfig } from './config'; import { pasteImageFile, uploadImageFile } from './image-helper'; import NotebooksManager from './notebooks-manager'; +import { PreviewCustomEditorProvider } from './preview-custom-editor-provider'; import { PreviewProvider, getPreviewUri } from './preview-provider'; import { getBottomVisibleLine, + getEditorActiveLine, + getPreviewMode, getTopVisibleLine, getWorkspaceFolderUri, isMarkdownFile, @@ -28,8 +31,9 @@ if (hideDefaultVSCodeMarkdownPreviewButtons) { ); } -export function initExtensionCommon(context: vscode.ExtensionContext) { +export async function initExtensionCommon(context: vscode.ExtensionContext) { const notebooksManager = new NotebooksManager(context); + await notebooksManager.updateWorkbenchEditorAssociationsBasedOnPreviewMode(); PreviewProvider.notebooksManager = notebooksManager; function getCurrentWorkingDirectory() { @@ -57,9 +61,14 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { } const previewProvider = await getPreviewContentProvider(uri); - previewProvider.initPreview(uri, editor, { - viewColumn: vscode.ViewColumn.Two, - preserveFocus: true, + previewProvider.initPreview({ + sourceUri: uri, + document: editor.document, + activeLine: getEditorActiveLine(editor), + viewOptions: { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + }, }); } @@ -73,61 +82,49 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { } const previewProvider = await getPreviewContentProvider(uri); - previewProvider.initPreview(uri, editor, { - viewColumn: vscode.ViewColumn.One, - preserveFocus: false, + previewProvider.initPreview({ + sourceUri: uri, + document: editor.document, + activeLine: getEditorActiveLine(editor), + viewOptions: { + viewColumn: vscode.ViewColumn.One, + preserveFocus: false, + }, }); } - function toggleScrollSync() { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - const scrollSync = !config.get('scrollSync'); - config.update('scrollSync', scrollSync, true).then(() => { - notebooksManager.updateConfiguration(); - if (scrollSync) { - vscode.window.showInformationMessage('Scroll Sync is enabled'); - } else { - vscode.window.showInformationMessage('Scroll Sync is disabled'); - } - }); + async function toggleScrollSync() { + const scrollSync = !getMPEConfig('scrollSync'); + await updateMPEConfig('scrollSync', scrollSync, true); + if (scrollSync) { + vscode.window.showInformationMessage('Scroll Sync is enabled'); + } else { + vscode.window.showInformationMessage('Scroll Sync is disabled'); + } } - function toggleLiveUpdate() { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - const liveUpdate = !config.get('liveUpdate'); - config.update('liveUpdate', liveUpdate, true).then(() => { - notebooksManager.updateConfiguration(); - if (liveUpdate) { - vscode.window.showInformationMessage('Live Update is enabled'); - } else { - vscode.window.showInformationMessage('Live Update is disabled'); - } - }); + async function toggleLiveUpdate() { + const liveUpdate = !getMPEConfig('liveUpdate'); + await updateMPEConfig('liveUpdate', liveUpdate, true); + if (liveUpdate) { + vscode.window.showInformationMessage('Live Update is enabled'); + } else { + vscode.window.showInformationMessage('Live Update is disabled'); + } } - function toggleBreakOnSingleNewLine() { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - const breakOnSingleNewLine = !config.get('breakOnSingleNewLine'); - config - .update('breakOnSingleNewLine', breakOnSingleNewLine, true) - .then(() => { - notebooksManager.updateConfiguration(); - if (breakOnSingleNewLine) { - vscode.window.showInformationMessage( - 'Break On Single New Line is enabled', - ); - } else { - vscode.window.showInformationMessage( - 'Break On Single New Line is disabled', - ); - } - }); + async function toggleBreakOnSingleNewLine() { + const breakOnSingleNewLine = !getMPEConfig('breakOnSingleNewLine'); + updateMPEConfig('breakOnSingleNewLine', breakOnSingleNewLine, true); + if (breakOnSingleNewLine) { + vscode.window.showInformationMessage( + 'Break On Single New Line is enabled', + ); + } else { + vscode.window.showInformationMessage( + 'Break On Single New Line is disabled', + ); + } } function insertNewSlide() { @@ -382,31 +379,19 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { } function setPreviewTheme(uri, theme) { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - config.update('previewTheme', theme, true); + updateMPEConfig('previewTheme', theme, true); } function setCodeBlockTheme(uri, theme) { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - config.update('codeBlockTheme', theme, true); + updateMPEConfig('codeBlockTheme', theme, true); } function setRevealjsTheme(uri, theme) { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - config.update('revealjsTheme', theme, true); + updateMPEConfig('revealjsTheme', theme, true); } function setImageUploader(imageUploader) { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - config.update('imageUploader', imageUploader, true); + updateMPEConfig('imageUploader', imageUploader, true); } function openConfigFileInWorkspace( @@ -418,11 +403,7 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { vscode.commands.executeCommand('vscode.open', filePath); }, async () => { - const provider = await getPreviewContentProvider(workspaceUri); - await provider.updateCrossnoteConfig( - path.join(workspaceUri.fsPath, '.crossnote'), - true, - ); + await notebooksManager.updateNotebookConfig(workspaceUri, true); vscode.commands.executeCommand('vscode.open', filePath); }, ); @@ -475,6 +456,22 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { openConfigFileInWorkspace(currentWorkingDirectory, parserConfigPath); } + function customizePreviewHtmlHeadInWorkspace() { + const currentWorkingDirectory = getCurrentWorkingDirectory(); + if (!currentWorkingDirectory) { + return vscode.window.showErrorMessage( + 'Please open a folder before customizing preview html head', + ); + } + + const headHtmlPath = vscode.Uri.joinPath( + currentWorkingDirectory, + './.crossnote/head.html', + ); + + openConfigFileInWorkspace(currentWorkingDirectory, headHtmlPath); + } + async function clickTagA({ uri, href, @@ -541,9 +538,8 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { } } - // open file if needed, if not we will use already opened editor + // open file if needed, if not we will use already opened editor // (by specifying view column in which it is already shown) - let fileExists = false; try { fileExists = !!(await vscode.workspace.fs.stat(fileUri)); @@ -552,21 +548,51 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { } if (fileExists) { - // Open fileUri - vscode.workspace.openTextDocument(fileUri).then((doc) => { - vscode.window.showTextDocument(doc, col).then((editor) => { - // if there was line fragment, jump to line - if (line >= 0) { - let viewPos = vscode.TextEditorRevealType.InCenter; - if (editor.selection.active.line === line) { - viewPos = vscode.TextEditorRevealType.InCenterIfOutsideViewport; - } - const sel = new vscode.Selection(line, 0, line, 0); - editor.selection = sel; - editor.revealRange(sel, viewPos); - } + const previewMode = getPreviewMode(); + const document = await vscode.workspace.openTextDocument(fileUri); + // Open custom editor + if ( + previewMode === PreviewMode.PreviewsOnly && + isMarkdownFile(document) + ) { + /* + // NOTE: This doesn't work for the `line` + // so we use the `initPreview` instead. + const options: vscode.TextDocumentShowOptions = { + selection: new vscode.Selection(line, 0, line, 0), + viewColumn: vscode.ViewColumn.Active, + }; + vscode.commands.executeCommand( + 'vscode.openWith', + fileUri, + 'markdown-preview-enhanced', + options, + ); + */ + const previewProvider = await getPreviewContentProvider(fileUri); + previewProvider.initPreview({ + sourceUri: fileUri, + document, + activeLine: line, + viewOptions: { + viewColumn: vscode.ViewColumn.Active, + preserveFocus: true, + }, }); - }); + } else { + // Open fileUri + const editor = await vscode.window.showTextDocument(document, col); + // if there was line fragment, jump to line + if (line >= 0) { + let viewPos = vscode.TextEditorRevealType.InCenter; + if (editor.selection.active.line === line) { + viewPos = vscode.TextEditorRevealType.InCenterIfOutsideViewport; + } + const sel = new vscode.Selection(line, 0, line, 0); + editor.selection = sel; + editor.revealRange(sel, viewPos); + } + } } else { vscode.commands.executeCommand( 'vscode.open', @@ -603,6 +629,15 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(url)); } + async function openExternalEditor(uri: string) { + const sourceUri = vscode.Uri.parse(uri); + const document = await vscode.workspace.openTextDocument(sourceUri); + await vscode.window.showTextDocument(document, { + preview: false, + viewColumn: vscode.ViewColumn.Active, + }); + } + async function showBacklinks({ uri, forceRefreshingNotes, @@ -627,11 +662,22 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { }); } + async function updateMarkdown(uri: string, markdown: string) { + try { + const sourceUri = vscode.Uri.parse(uri); + // Write markdown to file + await vscode.workspace.fs.writeFile(sourceUri, Buffer.from(markdown)); + // Update preview + const previewProvider = await getPreviewContentProvider(sourceUri); + previewProvider.updateMarkdown(sourceUri); + } catch (error) { + vscode.window.showErrorMessage(error); + console.error(error); + } + } + async function toggleAlwaysShowBacklinksInPreview(uri, flag) { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - config.update('alwaysShowBacklinksInPreview', flag, true); + updateMPEConfig('alwaysShowBacklinksInPreview', flag, true); } context.subscriptions.push( @@ -657,9 +703,7 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { ) ) { const provider = await getPreviewContentProvider(document.uri); - await provider.updateCrossnoteConfig( - path.join(workspaceDir, '.crossnote'), - ); + await notebooksManager.updateNotebookConfig(workspaceUri); provider.refreshAllPreviews(); } } @@ -678,14 +722,25 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { ); context.subscriptions.push( - vscode.workspace.onDidChangeConfiguration(() => { - notebooksManager.updateConfiguration(); + vscode.workspace.onDidChangeConfiguration((event) => { + // console.log( + // 'onDidChangeConfiguration: ', + // event.affectsConfiguration('markdown-preview-enhanced'), + // ); + if (event.affectsConfiguration('markdown-preview-enhanced')) { + notebooksManager.updateAllNotebooksConfig(); + } }), ); context.subscriptions.push( vscode.window.onDidChangeTextEditorSelection(async (event) => { if (isMarkdownFile(event.textEditor.document)) { + const previewMode = getPreviewMode(); + if (previewMode === PreviewMode.PreviewsOnly) { + return; + } + const firstVisibleScreenRow = getTopVisibleLine(event.textEditor); const lastVisibleScreenRow = getBottomVisibleLine(event.textEditor); @@ -756,20 +811,16 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { /** * Open preview automatically if the `automaticallyShowPreviewOfMarkdownBeingEdited` is on. - * @param textEditor */ context.subscriptions.push( - vscode.window.onDidChangeActiveTextEditor(async (textEditor) => { - if (textEditor && textEditor.document && textEditor.document.uri) { - if (isMarkdownFile(textEditor.document)) { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - const sourceUri = textEditor.document.uri; - const automaticallyShowPreviewOfMarkdownBeingEdited = config.get< + vscode.window.onDidChangeActiveTextEditor(async (editor) => { + if (editor && editor.document && editor.document.uri) { + if (isMarkdownFile(editor.document)) { + const sourceUri = editor.document.uri; + const automaticallyShowPreviewOfMarkdownBeingEdited = getMPEConfig< boolean >('automaticallyShowPreviewOfMarkdownBeingEdited'); - const isUsingSinglePreview = config.get('singlePreview'); + const previewMode = getPreviewMode(); /** * Is using single preview and the preview is on. * When we switched text ed()tor, update preview to that text editor. @@ -777,21 +828,27 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { const previewProvider = await getPreviewContentProvider(sourceUri); if (previewProvider.isPreviewOn(sourceUri)) { if ( - isUsingSinglePreview && + previewMode === PreviewMode.SinglePreview && !previewProvider.previewHasTheSameSingleSourceUri(sourceUri) ) { - previewProvider.initPreview(sourceUri, textEditor, { - viewColumn: - previewProvider.getPreview(sourceUri)?.viewColumn ?? - vscode.ViewColumn.One, - preserveFocus: true, + previewProvider.initPreview({ + sourceUri, + document: editor.document, + activeLine: getEditorActiveLine(editor), + viewOptions: { + viewColumn: + previewProvider.getPreviews(sourceUri)?.at(0)?.viewColumn ?? + vscode.ViewColumn.One, + preserveFocus: true, + }, }); - } else if (!isUsingSinglePreview) { - const previewPanel = previewProvider.getPreview(sourceUri); - if (previewPanel) { - previewPanel.reveal(/*vscode.ViewColumn.Two*/ undefined, true); + } else if (previewMode === PreviewMode.MultiplePreviews) { + const previews = previewProvider.getPreviews(sourceUri); + if (previews && previews.length > 0) { + previews[0].reveal(/*vscode.ViewColumn.Two*/ undefined, true); } } + // NOTE: For PreviewMode.PreviewsOnly, we don't need to do anything. } else if (automaticallyShowPreviewOfMarkdownBeingEdited) { openPreviewToTheSide(sourceUri); } @@ -803,23 +860,22 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { // Changed editor color theme context.subscriptions.push( vscode.window.onDidChangeActiveColorTheme((theme) => { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); if ( - config.get('previewColorScheme') === + getMPEConfig('previewColorScheme') === PreviewColorScheme.editorColorScheme ) { - notebooksManager.updateConfiguration(); + notebooksManager.updateAllNotebooksConfig(); } }), ); /* - context.subscriptions.push(vscode.workspace.onDidOpenTextDocument((textDocument)=> { - // console.log('onDidOpenTextDocument', textDocument.uri) - })) - */ + context.subscriptions.push( + vscode.workspace.onDidOpenTextDocument((document) => { + // console.log('onDidOpenTextDocument: ', document.uri.fsPath); + }), + ); + */ /* context.subscriptions.push(vscode.window.onDidChangeVisibleTextEditors(textEditors=> { @@ -1060,6 +1116,13 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { vscode.commands.registerCommand('_crossnote.openSponsors', openSponsors), ); + context.subscriptions.push( + vscode.commands.registerCommand( + '_crossnote.openExternalEditor', + openExternalEditor, + ), + ); + context.subscriptions.push( vscode.commands.registerCommand( 'markdown-preview-enhanced.customizeCssInWorkspace', @@ -1081,6 +1144,13 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { ), ); + context.subscriptions.push( + vscode.commands.registerCommand( + 'markdown-preview-enhanced.customizePreviewHtmlHeadInWorkspace', + customizePreviewHtmlHeadInWorkspace, + ), + ); + context.subscriptions.push( vscode.commands.registerCommand('_crossnote.clickTagA', clickTagA), ); @@ -1089,12 +1159,26 @@ export function initExtensionCommon(context: vscode.ExtensionContext) { vscode.commands.registerCommand('_crossnote.showBacklinks', showBacklinks), ); + context.subscriptions.push( + vscode.commands.registerCommand( + '_crossnote.updateMarkdown', + updateMarkdown, + ), + ); + context.subscriptions.push( vscode.commands.registerCommand( '_crossnote.toggleAlwaysShowBacklinksInPreview', toggleAlwaysShowBacklinksInPreview, ), ); + + context.subscriptions.push( + vscode.window.registerCustomEditorProvider( + 'markdown-preview-enhanced', + new PreviewCustomEditorProvider(context), + ), + ); } function revealLine(uri, line) { diff --git a/src/extension-web.ts b/src/extension-web.ts index 8b5c5e8..3fca217 100644 --- a/src/extension-web.ts +++ b/src/extension-web.ts @@ -1,9 +1,9 @@ import * as vscode from 'vscode'; import { initExtensionCommon } from './extension-common'; -export function activate(context: vscode.ExtensionContext) { +export async function activate(context: vscode.ExtensionContext) { // Init the extension-common module - initExtensionCommon(context); + await initExtensionCommon(context); } // This method is called when your extension is deactivated export function deactivate() {} diff --git a/src/extension.ts b/src/extension.ts index e3e1046..76f2f55 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,12 +5,12 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import { initExtensionCommon } from './extension-common'; -import { getAllPreviewProviders } from './preview-provider'; +import { PreviewProvider } from './preview-provider'; import { globalConfigPath } from './utils'; -// this method is called when your extension iopenTextDocuments activated +// this method is called when your extension openTextDocuments activated // your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) { +export async function activate(context: vscode.ExtensionContext) { try { if (!fs.existsSync(globalConfigPath)) { fs.mkdirSync(globalConfigPath, { recursive: true }); @@ -23,11 +23,7 @@ export function activate(context: vscode.ExtensionContext) { fileName ?? '', ) ) { - const providers = getAllPreviewProviders(); - providers.forEach(async (provider) => { - await provider.updateCrossnoteConfig(globalConfigPath); - provider.refreshAllPreviews(); - }); + PreviewProvider.notebooksManager?.updateAllNotebooksConfig(); } }); } catch (error) { @@ -35,7 +31,7 @@ export function activate(context: vscode.ExtensionContext) { } // Init the extension-common module - initExtensionCommon(context); + await initExtensionCommon(context); function customizeCSS() { const globalStyleLessFile = utility.addFileProtocol( @@ -67,6 +63,16 @@ export function activate(context: vscode.ExtensionContext) { ); } + function customizePreviewHtmlHead() { + const headHtmlPath = utility.addFileProtocol( + path.resolve(globalConfigPath, './head.html'), + ); + vscode.commands.executeCommand( + 'vscode.open', + vscode.Uri.parse(headHtmlPath), + ); + } + function showUploadedImages() { const imageHistoryFilePath = utility.addFileProtocol( path.resolve(globalConfigPath, './image_history.md'), @@ -98,6 +104,13 @@ export function activate(context: vscode.ExtensionContext) { ), ); + context.subscriptions.push( + vscode.commands.registerCommand( + 'markdown-preview-enhanced.customizePreviewHtmlHead', + customizePreviewHtmlHead, + ), + ); + context.subscriptions.push( vscode.commands.registerCommand( 'markdown-preview-enhanced.showUploadedImages', @@ -106,7 +119,6 @@ export function activate(context: vscode.ExtensionContext) { ); // context.subscriptions.push(vscode.commands.registerCommand('_crossnote.cacheSVG', cacheSVG)) - context.subscriptions.push( vscode.commands.registerCommand( '_crossnote.showUploadedImageHistory', diff --git a/src/file-watcher.ts b/src/file-watcher.ts index 553c385..2556b85 100644 --- a/src/file-watcher.ts +++ b/src/file-watcher.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { getMPEConfig } from './config'; import NotebooksManager from './notebooks-manager'; import { getAllPreviewProviders } from './preview-provider'; @@ -14,11 +15,8 @@ export default class FileWatcher { return; } - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); const markdownFileExtensions = - config.get('markdownFileExtensions') ?? []; + getMPEConfig('markdownFileExtensions') ?? []; const glob: string = `**/*.{${markdownFileExtensions .map((ext) => ext.replace(/^\./, '')) .join(',')}}`; diff --git a/src/image-helper.ts b/src/image-helper.ts index 5143644..58f544c 100644 --- a/src/image-helper.ts +++ b/src/image-helper.ts @@ -2,6 +2,7 @@ import { utility } from 'crossnote'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; +import { getMPEConfig } from './config'; import { getWorkspaceFolderUri, isMarkdownFile } from './utils'; /** @@ -197,13 +198,10 @@ export function uploadImageFile( textEditorEdit.insert(curPos, hint); }); - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); - const AccessKey = config.get('AccessKey') || ''; - const SecretKey = config.get('SecretKey') || ''; - const Bucket = config.get('Bucket') || ''; - const Domain = config.get('Domain') || ''; + const AccessKey = getMPEConfig('qiniuAccessKey') || ''; + const SecretKey = getMPEConfig('qiniuSecretKey') || ''; + const Bucket = getMPEConfig('qiniuBucket') || ''; + const Domain = getMPEConfig('qiniuDomain') || ''; utility .uploadImage(imageFilePath, { diff --git a/src/notebooks-manager.ts b/src/notebooks-manager.ts index 99ebe9d..4b15095 100644 --- a/src/notebooks-manager.ts +++ b/src/notebooks-manager.ts @@ -1,6 +1,16 @@ -import { Notebook, PreviewTheme, loadConfigsInDirectory } from 'crossnote'; +import { + Notebook, + NotebookConfig, + PreviewMode, + PreviewTheme, + loadConfigsInDirectory, +} from 'crossnote'; import * as vscode from 'vscode'; -import { MarkdownPreviewEnhancedConfig, PreviewColorScheme } from './config'; +import { + MarkdownPreviewEnhancedConfig, + PreviewColorScheme, + getMPEConfig, +} from './config'; import FileWatcher from './file-watcher'; import { getAllPreviewProviders } from './preview-provider'; import { @@ -12,12 +22,11 @@ import { wrapVSCodeFSAsApi } from './vscode-fs'; class NotebooksManager { private notebooks: Notebook[] = []; - public config: MarkdownPreviewEnhancedConfig; public systemColorScheme: 'light' | 'dark' = 'light'; private fileWatcher: FileWatcher; + private currentMPEConfig: MarkdownPreviewEnhancedConfig = MarkdownPreviewEnhancedConfig.getCurrentConfig(); constructor(private context: vscode.ExtensionContext) { - this.config = MarkdownPreviewEnhancedConfig.getCurrentConfig(); this.fileWatcher = new FileWatcher(this.context, this); } @@ -34,75 +43,159 @@ class NotebooksManager { const notebook = await Notebook.init({ notebookPath: workspaceFolderUri.toString(), fs: wrapVSCodeFSAsApi(workspaceFolderUri.scheme), - config: { ...this.config }, + config: {}, }); this.notebooks.push(notebook); + notebook.updateConfig(await this.loadNotebookConfig(uri)); + return notebook; + } - // Check if ${workspaceDir}/.crossnote directory exists - // If not, then use the global config. - const crossnoteDir = vscode.Uri.joinPath( - workspaceFolderUri, - './.crossnote', + public async updateNotebookConfig( + uri: vscode.Uri, + createWorkspaceConfigDirectoryIfNotExists: boolean = false, + ) { + const notebook = await this.getNotebook(uri); + const config = await this.loadNotebookConfig( + uri, + createWorkspaceConfigDirectoryIfNotExists, ); - if ( - !(await notebook.fs.exists(crossnoteDir.fsPath)) && - !isVSCodeWebExtension() - ) { + notebook.updateConfig(config); + } + + private async loadNotebookConfig( + uri: vscode.Uri, + createWorkspaceConfigDirectoryIfNotExists: boolean = false, + ): Promise> { + const notebook = await this.getNotebook(uri); + + // Global config + let globalConfig: Partial = {}; + if (!isVSCodeWebExtension()) { try { - const globalConfig = await loadConfigsInDirectory( + globalConfig = await loadConfigsInDirectory( globalConfigPath, notebook.fs, true, ); - notebook.updateConfig(globalConfig); } catch (error) { console.error(error); } } - return notebook; + // Workspace config + let workspaceConfig: Partial = {}; + const workspaceConfigPath = vscode.Uri.joinPath( + getWorkspaceFolderUri(uri), + './.crossnote', + ); + try { + workspaceConfig = await loadConfigsInDirectory( + workspaceConfigPath.fsPath, + notebook.fs, + createWorkspaceConfigDirectoryIfNotExists, + ); + } catch (error) { + console.error(error); + } + + // VSCode config + const vscodeMPEConfig = MarkdownPreviewEnhancedConfig.getCurrentConfig(); + this.currentMPEConfig = vscodeMPEConfig; + + // Preview theme + const previewTheme = this.getPreviewTheme( + vscodeMPEConfig.previewTheme, + vscodeMPEConfig.previewColorScheme, + ); + + return { + ...vscodeMPEConfig, + ...globalConfig, + ...workspaceConfig, + previewTheme, + }; } public setSystemColorScheme(colorScheme: 'light' | 'dark') { if (this.systemColorScheme !== colorScheme) { this.systemColorScheme = colorScheme; if ( - this.config.previewColorScheme === PreviewColorScheme.systemColorScheme + getMPEConfig('previewColorScheme') === + PreviewColorScheme.systemColorScheme ) { - this.updateConfiguration(true); + this.updateAllNotebooksConfig(); } } } - public updateConfiguration(forceUpdate = false) { - const newConfig = MarkdownPreviewEnhancedConfig.getCurrentConfig(); - if (forceUpdate || !this.config.isEqualTo(newConfig)) { - const previewProviders = getAllPreviewProviders(); - // if `singlePreview` setting is changed, close all previews. - if (this.config.singlePreview !== newConfig.singlePreview) { - previewProviders.forEach((provider) => { - provider.closeAllPreviews(this.config.singlePreview); - }); - - this.config = newConfig; - } else { - this.config = newConfig; - - const previewTheme = this.getPreviewTheme( - newConfig.previewTheme, - newConfig.previewColorScheme, - ); - this.notebooks.forEach((notebook) => { - notebook.updateConfig({ ...newConfig, previewTheme }); - }); - // update all generated md documents - previewProviders.forEach((provider) => { - provider.refreshAllPreviews(); - }); - } + public async updateWorkbenchEditorAssociationsBasedOnPreviewMode() { + const workbenchConfig = vscode.workspace.getConfiguration('workbench'); + const previewMode = getMPEConfig('previewMode'); + const markdownFileExtensions = getMPEConfig( + 'markdownFileExtensions', + ) ?? ['.md']; + const editorAssociations = + workbenchConfig.get<{ [key: string]: string }>('editorAssociations') ?? + {}; + let newEditorAssociations = { ...editorAssociations }; + if (previewMode === PreviewMode.PreviewsOnly) { + const associations: { [key: string]: string } = {}; + markdownFileExtensions.forEach((ext) => { + associations[`*${ext}`] = 'markdown-preview-enhanced'; + }); + // Add associations to editorAssociations + newEditorAssociations = { ...editorAssociations, ...associations }; + } else { + // delete associations from editorAssociations + newEditorAssociations = Object.fromEntries( + Object.entries(editorAssociations).filter(([key]) => { + return !markdownFileExtensions.find((ext) => { + return key.endsWith(ext); + }); + }), + ); + } + + if ( + JSON.stringify(newEditorAssociations) !== + JSON.stringify(editorAssociations) + ) { + await workbenchConfig.update( + 'editorAssociations', + newEditorAssociations, + vscode.ConfigurationTarget.Global, + ); } } + public async updateAllNotebooksConfig() { + const previewProviders = getAllPreviewProviders(); + await this.updateWorkbenchEditorAssociationsBasedOnPreviewMode(); + const newMPEConfig = MarkdownPreviewEnhancedConfig.getCurrentConfig(); + + // If previewMode changed, close all previews. + if (this.currentMPEConfig.previewMode !== newMPEConfig.previewMode) { + previewProviders.forEach((provider) => { + provider.closeAllPreviews(this.currentMPEConfig.previewMode); + }); + } + + // Update all notebooks config + await Promise.all( + this.notebooks.map(async (notebook) => { + const config = await this.loadNotebookConfig(notebook.notebookPath); + notebook.updateConfig(config); + }), + ); + + // Refresh all previews + previewProviders.forEach((provider) => { + provider.refreshAllPreviews(); + }); + + this.currentMPEConfig = newMPEConfig; + } + private getPreviewThemeByLightOrDark( theme: PreviewTheme, color: 'light' | 'dark', diff --git a/src/preview-custom-editor-provider.ts b/src/preview-custom-editor-provider.ts new file mode 100644 index 0000000..5238a7b --- /dev/null +++ b/src/preview-custom-editor-provider.ts @@ -0,0 +1,33 @@ +import * as vscode from 'vscode'; +import { PreviewProvider } from './preview-provider'; + +export class PreviewCustomEditorProvider + implements vscode.CustomTextEditorProvider { + constructor(private context: vscode.ExtensionContext) {} + + async resolveCustomTextEditor( + document: vscode.TextDocument, + webviewPanel: vscode.WebviewPanel, + token: vscode.CancellationToken, + ): Promise { + try { + const provider = await PreviewProvider.getPreviewContentProvider( + document.uri, + this.context, + ); + return await provider.initPreview({ + sourceUri: document.uri, + document, + // HACK: The `viewOptions` below will not actually be used. + viewOptions: { + viewColumn: webviewPanel.viewColumn ?? vscode.ViewColumn.One, + preserveFocus: true, + }, + webviewPanel, + }); + } catch (error) { + console.error(error); + vscode.window.showErrorMessage(error.message); + } + } +} diff --git a/src/preview-provider.ts b/src/preview-provider.ts index 6bb7d85..20c9049 100644 --- a/src/preview-provider.ts +++ b/src/preview-provider.ts @@ -1,11 +1,14 @@ -import { Notebook, loadConfigsInDirectory, utility } from 'crossnote'; +import { Mutex } from 'async-mutex'; +import { ImageUploader, Notebook, PreviewMode, utility } from 'crossnote'; import { tmpdir } from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; import { Uri } from 'vscode'; +import { getMPEConfig } from './config'; import NotebooksManager from './notebooks-manager'; import { getCrossnoteVersion, + getPreviewMode, getWorkspaceFolderUri, globalConfigPath, isMarkdownFile, @@ -20,11 +23,8 @@ if (isVSCodeWebExtension()) { console.debug('* Loading /crossnote directory at http://localhost:6789/'); utility.setCrossnoteBuildDirectory('http://localhost:6789/'); } else { - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); const jsdelivrCdnHost = - config.get('jsdelivrCdnHost') ?? 'cdn.jsdelivr.net'; + getMPEConfig('jsdelivrCdnHost') ?? 'cdn.jsdelivr.net'; utility.setCrossnoteBuildDirectory( `https://${jsdelivrCdnHost}/npm/crossnote@${getCrossnoteVersion()}/out/`, ); @@ -62,13 +62,16 @@ utility.useExternalAddFileProtocolFunction((filePath, preview) => { }); /** - * key is `workspaceDir` + * key is workspaceUri.toString() * value is the `PreviewProvider` */ -const WORKSPACE_PREVIEW_PROVIDER_MAP: Map< - string, // workspaceDir fsPath - PreviewProvider -> = new Map(); +const WORKSPACE_PREVIEW_PROVIDER_MAP: Map = new Map(); + +/** + * key is workspaceUri.toString() + * value is the `Mutex` + */ +const WORKSPACE_MUTEX_MAP: Map = new Map(); export function getAllPreviewProviders(): PreviewProvider[] { return Array.from(WORKSPACE_PREVIEW_PROVIDER_MAP.values()); @@ -79,6 +82,9 @@ export function getAllPreviewProviders(): PreviewProvider[] { // https://github.com/Microsoft/vscode/tree/master/extensions/markdown/src // https://github.com/tomoki1207/gfm-preview/blob/master/src/gfmProvider.ts // https://github.com/cbreeden/vscode-markdownit +/** + * One workspace folder has one PreviewProvider + */ export class PreviewProvider { private waiting: boolean = false; @@ -93,15 +99,15 @@ export class PreviewProvider { private context: vscode.ExtensionContext; /** - * The key is markdown file fspath + * The key is sourceUri.toString() * value is Preview (vscode.Webview) object */ - private previewMaps: { [key: string]: vscode.WebviewPanel } = {}; - - private preview2EditorMap: Map< + private previewMaps: Map> = new Map(); + private previewToDocumentMap: Map< vscode.WebviewPanel, - vscode.TextEditor + vscode.TextDocument > = new Map(); + private initializedPreviews: Set = new Set(); private static singlePreviewPanel: vscode.WebviewPanel | null; private static singlePreviewPanelSourceUriTarget: Uri | null; @@ -135,44 +141,45 @@ export class PreviewProvider { return PreviewProvider.notebooksManager; } - public async updateCrossnoteConfig(directory: string, forceUpdate = false) { - // If directory is globalConfigDirectory && ${workspaceDir}/.crossnote directory exists - // then return without updating. - if ( - directory === globalConfigPath && - (await this.notebook.fs.exists( - path.join(this.notebook.notebookPath.fsPath, '.crossnote'), - )) - ) { - return; - } - - if ((await this.notebook.fs.exists(directory)) || forceUpdate) { - const configs = await loadConfigsInDirectory( - directory, - this.notebook.fs, - true, - ); - this.notebook.updateConfig(configs); - } - } - public static async getPreviewContentProvider( uri: vscode.Uri, context: vscode.ExtensionContext, ) { const workspaceUri = getWorkspaceFolderUri(uri); - if (WORKSPACE_PREVIEW_PROVIDER_MAP.has(workspaceUri.fsPath)) { - const provider = WORKSPACE_PREVIEW_PROVIDER_MAP.get(workspaceUri.fsPath); - if (!provider) { - throw new Error('Cannot find preview provider'); + + // Acquire mutex + let mutex: Mutex; + const mutexKey = workspaceUri.toString(); + if (WORKSPACE_MUTEX_MAP.has(mutexKey)) { + const mutex_ = WORKSPACE_MUTEX_MAP.get(mutexKey); + if (!mutex_) { + throw new Error('Cannot find mutex'); } - return provider; + mutex = mutex_; } else { - const provider = new PreviewProvider(); - await provider.init(context, workspaceUri); - WORKSPACE_PREVIEW_PROVIDER_MAP.set(workspaceUri.fsPath, provider); - return provider; + mutex = new Mutex(); + WORKSPACE_MUTEX_MAP.set(mutexKey, mutex); + } + + const release = await mutex.acquire(); + try { + if (WORKSPACE_PREVIEW_PROVIDER_MAP.has(mutexKey)) { + const provider = WORKSPACE_PREVIEW_PROVIDER_MAP.get(mutexKey); + if (!provider) { + throw new Error('Cannot find preview provider'); + } + release(); + return provider; + } else { + const provider = new PreviewProvider(); + await provider.init(context, workspaceUri); + WORKSPACE_PREVIEW_PROVIDER_MAP.set(mutexKey, provider); + release(); + return provider; + } + } catch (error) { + release(); + throw error; } } @@ -181,28 +188,50 @@ export class PreviewProvider { this.notebook.clearAllNoteMarkdownEngineCaches(); // refresh iframes - if (useSinglePreview()) { + if (getPreviewMode() === PreviewMode.SinglePreview) { this.refreshPreviewPanel( PreviewProvider.singlePreviewPanelSourceUriTarget, ); } else { - for (const key in this.previewMaps) { - if (this.previewMaps.hasOwnProperty(key)) { - this.refreshPreviewPanel(vscode.Uri.file(key)); - } + for (const [sourceUriString] of this.previewMaps) { + this.refreshPreviewPanel(vscode.Uri.parse(sourceUriString)); } } } + private addPreviewToMap(sourceUri: Uri, previewPanel: vscode.WebviewPanel) { + let previews = this.previewMaps.get(sourceUri.toString()); + if (!previews) { + previews = new Set(); + this.previewMaps.set(sourceUri.toString(), previews); + } + previews.add(previewPanel); + } + + private deletePreviewFromMap( + sourceUri: Uri, + previewPanel: vscode.WebviewPanel, + ) { + this.previewMaps.get(sourceUri.toString())?.delete(previewPanel); + } + /** - * return markdown preview of sourceUri + * return markdown previews of sourceUri * @param sourceUri */ - public getPreview(sourceUri: Uri): vscode.WebviewPanel | null { - if (useSinglePreview()) { - return PreviewProvider.singlePreviewPanel; + public getPreviews(sourceUri: Uri): vscode.WebviewPanel[] | null | undefined { + if ( + getPreviewMode() === PreviewMode.SinglePreview && + PreviewProvider.singlePreviewPanel + ) { + return [PreviewProvider.singlePreviewPanel]; } else { - return this.previewMaps[sourceUri.fsPath]; + const previews = this.previewMaps.get(sourceUri.toString()); + if (previews) { + return Array.from(previews); + } else { + return null; + } } } @@ -211,24 +240,28 @@ export class PreviewProvider { * @param textEditor */ public isPreviewOn(sourceUri: Uri) { - if (useSinglePreview()) { + if (getPreviewMode() === PreviewMode.SinglePreview) { return !!PreviewProvider.singlePreviewPanel; } else { - return !!this.getPreview(sourceUri); + const previews = this.getPreviews(sourceUri); + return previews && previews.length > 0; } } public destroyPreview(sourceUri: Uri) { - if (useSinglePreview()) { + const previewMode = getPreviewMode(); + if (previewMode === PreviewMode.SinglePreview) { PreviewProvider.singlePreviewPanel = null; PreviewProvider.singlePreviewPanelSourceUriTarget = null; - this.preview2EditorMap = new Map(); - this.previewMaps = {}; + this.previewToDocumentMap = new Map(); + this.previewMaps = new Map(); } else { - const previewPanel = this.getPreview(sourceUri); - if (previewPanel) { - this.preview2EditorMap.delete(previewPanel); - delete this.previewMaps[sourceUri.fsPath]; + const previews = this.getPreviews(sourceUri); + if (previews) { + previews.forEach((preview) => { + this.previewToDocumentMap.delete(preview); + this.deletePreviewFromMap(sourceUri, preview); + }); } } } @@ -242,14 +275,27 @@ export class PreviewProvider { return this.notebook.getNoteMarkdownEngine(sourceUri.fsPath); } - public async initPreview( - sourceUri: vscode.Uri, - editor: vscode.TextEditor, - viewOptions: { viewColumn: vscode.ViewColumn; preserveFocus?: boolean }, - ) { - const isUsingSinglePreview = useSinglePreview(); + public async initPreview({ + sourceUri, + document, + webviewPanel, + activeLine, + viewOptions, + }: { + sourceUri: vscode.Uri; + document: vscode.TextDocument; + webviewPanel?: vscode.WebviewPanel; + activeLine?: number; + viewOptions: { viewColumn: vscode.ViewColumn; preserveFocus?: boolean }; + }): Promise { + // console.log('@initPreview: ', sourceUri); + const previewMode = getPreviewMode(); let previewPanel: vscode.WebviewPanel; - if (isUsingSinglePreview && PreviewProvider.singlePreviewPanel) { + const previews = this.getPreviews(sourceUri); + if ( + previewMode === PreviewMode.SinglePreview && + PreviewProvider.singlePreviewPanel + ) { const oldResourceRoot = PreviewProvider.singlePreviewPanelSourceUriTarget ? getWorkspaceFolderUri( PreviewProvider.singlePreviewPanelSourceUriTarget, @@ -261,13 +307,29 @@ export class PreviewProvider { PreviewProvider.singlePreviewPanel = null; PreviewProvider.singlePreviewPanelSourceUriTarget = null; singlePreview.dispose(); - return this.initPreview(sourceUri, editor, viewOptions); + return await this.initPreview({ + sourceUri, + document, + viewOptions, + activeLine, + }); } else { previewPanel = PreviewProvider.singlePreviewPanel; PreviewProvider.singlePreviewPanelSourceUriTarget = sourceUri; } - } else if (this.previewMaps[sourceUri.fsPath]) { - previewPanel = this.previewMaps[sourceUri.fsPath]; + } else if (previews && previews.length > 0 && !webviewPanel) { + await Promise.all( + previews.map((preview) => + this.initPreview({ + sourceUri, + document, + webviewPanel: preview, + viewOptions, + activeLine, + }), + ), + ); + return; } else { const localResourceRoots = [ vscode.Uri.file(this.context.extensionPath), @@ -280,127 +342,153 @@ export class PreviewProvider { localResourceRoots.push(workspaceUri); } - previewPanel = vscode.window.createWebviewPanel( - 'markdown-preview-enhanced', - `Preview ${path.basename(sourceUri.fsPath)}`, - viewOptions, - { - enableFindWidget: true, + if (webviewPanel) { + previewPanel = webviewPanel; + previewPanel.webview.options = { + enableScripts: true, localResourceRoots, - enableScripts: true, // TODO: This might be set by enableScriptExecution config. But for now we just enable it. - }, - ); + }; + // @ts-ignore + previewPanel.options.retainContextWhenHidden = true; + } else { + previewPanel = vscode.window.createWebviewPanel( + 'markdown-preview-enhanced', + `Preview ${path.basename(sourceUri.fsPath)}`, + viewOptions, + { + enableFindWidget: true, + localResourceRoots, + enableScripts: true, // TODO: This might be set by enableScriptExecution config. But for now we just enable it. + retainContextWhenHidden: true, + }, + ); + } // set icon + // NOTE: This doesn't work for custom editor. previewPanel.iconPath = vscode.Uri.file( path.join(this.context.extensionPath, 'media', 'preview.svg'), ); - // register previewPanel message events - previewPanel.webview.onDidReceiveMessage( - (message) => { - // console.log('@ onDidReceiveMessage: ', message); - vscode.commands.executeCommand( - `_crossnote.${message.command}`, - ...message.args, - ); - }, - null, - this.context.subscriptions, - ); - - // unregister previewPanel - previewPanel.onDidDispose( - () => { - this.destroyPreview(sourceUri); - this.destroyEngine(sourceUri); - }, - null, - this.context.subscriptions, - ); + // NOTE: We only register for the webview event listeners once. + if (!this.initializedPreviews.has(previewPanel)) { + this.initializedPreviews.add(previewPanel); + + // register previewPanel message events. + previewPanel.webview.onDidReceiveMessage( + (message) => { + // console.log('@ receiveMessage: ', message, previewPanel); + vscode.commands.executeCommand( + `_crossnote.${message.command}`, + ...message.args, + ); + }, + null, + this.context.subscriptions, + ); + + // unregister previewPanel. + previewPanel.onDidDispose( + () => { + this.destroyPreview(sourceUri); + this.destroyEngine(sourceUri); + this.initializedPreviews.delete(previewPanel); + }, + null, + this.context.subscriptions, + ); + } - if (isUsingSinglePreview) { + if (previewMode === PreviewMode.SinglePreview) { PreviewProvider.singlePreviewPanel = previewPanel; PreviewProvider.singlePreviewPanelSourceUriTarget = sourceUri; } } // register previewPanel - this.previewMaps[sourceUri.fsPath] = previewPanel; - this.preview2EditorMap.set(previewPanel, editor); + this.addPreviewToMap(sourceUri, previewPanel); + this.previewToDocumentMap.set(previewPanel, document); // set title previewPanel.title = `Preview ${path.basename(sourceUri.fsPath)}`; // init markdown engine let initialLine: number | undefined; - if (editor && editor.document.uri.fsPath === sourceUri.fsPath) { - initialLine = await new Promise((resolve, reject) => { - // HACK: sometimes we only get 0. I couldn't find API to wait for editor getting loaded. - setTimeout(() => { - return resolve(editor.selections[0].active.line || 0); - }, 100); - }); + if (document.uri.fsPath === sourceUri.fsPath) { + initialLine = activeLine; } - const text = editor.document.getText(); + const inputString = document.getText() ?? ''; const engine = this.getEngine(sourceUri); - engine - .generateHTMLTemplateForPreview({ - inputString: text, + try { + const html = await engine.generateHTMLTemplateForPreview({ + inputString, config: { sourceUri: sourceUri.toString(), - initialLine: initialLine as number, + initialLine, isVSCode: true, - scrollSync: this.getNotebooksManager().config.scrollSync, - imageUploader: this.getNotebooksManager().config.imageUploader, + scrollSync: getMPEConfig('scrollSync'), + imageUploader: getMPEConfig('imageUploader'), }, contentSecurityPolicy: '', vscodePreviewPanel: previewPanel, isVSCodeWebExtension: isVSCodeWebExtension(), - }) - .then((html) => { - previewPanel.webview.html = html; }); + previewPanel.webview.html = html; + } catch (error) { + vscode.window.showErrorMessage(error.toString()); + console.error(error); + } } /** - * Close all previews + * Close all previews. */ - public closeAllPreviews(singlePreview: boolean) { - if (singlePreview) { + public closeAllPreviews(previewMode: PreviewMode) { + if (previewMode === PreviewMode.SinglePreview) { if (PreviewProvider.singlePreviewPanel) { PreviewProvider.singlePreviewPanel.dispose(); } } else { - const previewPanels: vscode.WebviewPanel[] = []; - for (const key in this.previewMaps) { - if (this.previewMaps.hasOwnProperty(key)) { - const previewPanel = this.previewMaps[key]; - if (previewPanel) { - previewPanels.push(previewPanel); - } + for (const [sourceUriString] of this.previewMaps) { + const previews = this.previewMaps.get(sourceUriString); + if (previews) { + previews.forEach((preview) => preview.dispose()); } } - - previewPanels.forEach((previewPanel) => previewPanel.dispose()); } - this.previewMaps = {}; - this.preview2EditorMap = new Map(); + this.previewMaps = new Map(); + this.previewToDocumentMap = new Map(); // this.engineMaps = {}; PreviewProvider.singlePreviewPanel = null; PreviewProvider.singlePreviewPanelSourceUriTarget = null; } - public postMessageToPreview( + public async postMessageToPreview( sourceUri: Uri, - message: { command: string; [key: string]: any }, // TODO: Define a type for message + message: { command: string; [key: string]: any }, // TODO: Define a type for message. ) { - const preview = this.getPreview(sourceUri); - if (preview) { - // console.log('@ postMessageToPreview: ', preview, message); - preview.webview.postMessage(message); + const previews = this.getPreviews(sourceUri); + if (previews) { + // console.log( + // '@ postMessageToPreview: ', + // sourceUri.fsPath, + // message, + // previews, + // ); + + for (let i = 0; i < previews.length; i++) { + const preview = previews[i]; + if (preview.visible) { + const result = await preview.webview.postMessage(message); + if (!result) { + console.error( + `Failed to send message "${message.command}" to preview panel for ${sourceUri.fsPath}`, + ); + } + } + } } } @@ -417,8 +505,9 @@ export class PreviewProvider { public updateMarkdown(sourceUri: Uri, triggeredBySave?: boolean) { const engine = this.getEngine(sourceUri); - const previewPanel = this.getPreview(sourceUri); - if (!previewPanel) { + const previews = this.getPreviews(sourceUri); + // console.log('updateMarkdown: ', previews?.length); + if (!previews || !previews.length) { return; } @@ -428,34 +517,44 @@ export class PreviewProvider { } // not presentation mode - vscode.workspace.openTextDocument(sourceUri).then((document) => { + vscode.workspace.openTextDocument(sourceUri).then(async (document) => { const text = document.getText(); - this.postMessageToPreview(sourceUri, { + await this.postMessageToPreview(sourceUri, { command: 'startParsingMarkdown', }); - const preview = this.getPreview(sourceUri); - engine - .parseMD(text, { - isForPreview: true, - useRelativeFilePath: false, - hideFrontMatter: false, - triggeredBySave, - vscodePreviewPanel: preview, - }) - .then(({ markdown, html, tocHTML, JSAndCssFiles, yamlConfig }) => { + const previews = this.getPreviews(sourceUri); + if (!previews || !previews.length) { + return; + } + for (let i = 0; i < previews.length; i++) { + try { + const preview = previews[0]; + const { + html, + tocHTML, + JSAndCssFiles, + yamlConfig, + } = await engine.parseMD(text, { + isForPreview: true, + useRelativeFilePath: false, + hideFrontMatter: false, + triggeredBySave, + vscodePreviewPanel: preview, // TODO: + }); // check JSAndCssFiles if ( JSON.stringify(JSAndCssFiles) !== - JSON.stringify(this.jsAndCssFilesMaps[sourceUri.fsPath]) || + JSON.stringify(this.jsAndCssFilesMaps[sourceUri.fsPath] ?? []) || yamlConfig['isPresentationMode'] ) { this.jsAndCssFilesMaps[sourceUri.fsPath] = JSAndCssFiles; // restart iframe this.refreshPreview(sourceUri); } else { - this.postMessageToPreview(sourceUri, { + await this.postMessageToPreview(sourceUri, { command: 'updateHtml', + markdown: text, html, tocHTML, totalLineCount: document.lineCount, @@ -475,30 +574,38 @@ export class PreviewProvider { } ${isVSCodeWebExtension() ? 'vscode-web-extension' : ''}`, }); } - }) - .catch((error) => { - vscode.window.showErrorMessage(error.toString()); - }); + break; + } catch (error) { + if (i === previews.length - 1) { + // This is the + vscode.window.showErrorMessage(error.toString()); + } else { + continue; + } + } + } }); } - public refreshPreviewPanel(sourceUri: Uri | null) { + private refreshPreviewPanel(sourceUri: Uri | null) { if (!sourceUri) { return; } - this.preview2EditorMap.forEach((editor, previewPanel) => { + this.previewToDocumentMap.forEach(async (document, previewPanel) => { if ( previewPanel && - editor && - editor.document && - isMarkdownFile(editor.document) && - editor.document.uri && - editor.document.uri.fsPath === sourceUri.fsPath + isMarkdownFile(document) && + document.uri && + document.uri.fsPath === sourceUri.fsPath ) { - this.initPreview(sourceUri, editor, { - viewColumn: previewPanel.viewColumn ?? vscode.ViewColumn.One, - preserveFocus: true, + await this.initPreview({ + sourceUri, + document, + viewOptions: { + viewColumn: previewPanel.viewColumn ?? vscode.ViewColumn.One, + preserveFocus: true, + }, }); } }); @@ -683,10 +790,8 @@ export class PreviewProvider { } public update(sourceUri: Uri) { - if ( - !this.getNotebooksManager().config.liveUpdate || - !this.getPreview(sourceUri) - ) { + const previews = this.getPreviews(sourceUri); + if (!getMPEConfig('liveUpdate') || !previews || !previews.length) { return; } @@ -700,34 +805,26 @@ export class PreviewProvider { } } - public openImageHelper(sourceUri: Uri) { + public async openImageHelper(sourceUri: Uri) { if (sourceUri.scheme === 'markdown-preview-enhanced') { return vscode.window.showWarningMessage('Please focus a markdown file.'); } else if (!this.isPreviewOn(sourceUri)) { return vscode.window.showWarningMessage('Please open preview first.'); } else { - return this.postMessageToPreview(sourceUri, { + return await this.postMessageToPreview(sourceUri, { command: 'openImageHelper', }); } } } -/** - * check whehter to use only one preview or not - */ -export function useSinglePreview() { - const config = vscode.workspace.getConfiguration('markdown-preview-enhanced'); - return config.get('singlePreview'); -} - export function getPreviewUri(uri: vscode.Uri) { if (uri.scheme === 'markdown-preview-enhanced') { return uri; } let previewUri: Uri; - if (useSinglePreview()) { + if (getPreviewMode() === PreviewMode.SinglePreview) { previewUri = uri.with({ scheme: 'markdown-preview-enhanced', path: 'single-preview.rendered', diff --git a/src/utils.ts b/src/utils.ts index e5782b1..a25fd4d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,12 +1,15 @@ import path = require('path'); +import { PreviewMode } from 'crossnote'; import * as os from 'os'; import * as vscode from 'vscode'; import * as packageJSON from '../package.json'; +import { getMPEConfig } from './config'; /** * Format pathString if it is on Windows. Convert `c:\` like string to `C:\` * @param pathString */ +/* function formatPathIfNecessary(pathString: string) { if (process.platform === 'win32') { pathString = pathString.replace( @@ -16,6 +19,7 @@ function formatPathIfNecessary(pathString: string) { } return pathString; } +*/ /** * Get the workspace folder uri of the given uri @@ -42,9 +46,8 @@ export function getWorkspaceFolderUri(uri: vscode.Uri) { } function getGlobalConfigPath(): string { - const config = vscode.workspace.getConfiguration('markdown-preview-enhanced'); - const configPath = config.get('configPath'); - if (configPath && configPath !== '') { + const configPath = getMPEConfig('configPath'); + if (typeof configPath === 'string' && configPath && configPath !== '') { return configPath.replace(/^~/, os.homedir()); } @@ -70,11 +73,8 @@ export function isMarkdownFile(document: vscode.TextDocument) { if (!flag) { // Check file extension - const config = vscode.workspace.getConfiguration( - 'markdown-preview-enhanced', - ); const markdownFileExtensions = - config.get('markdownFileExtensions') ?? []; + getMPEConfig('markdownFileExtensions') ?? []; const fileName = document.fileName; const ext = path.extname(fileName).toLowerCase(); flag = markdownFileExtensions.includes(ext); @@ -137,3 +137,11 @@ export function isVSCodewebExtensionDevMode() { export function getCrossnoteVersion() { return packageJSON.dependencies['crossnote']; } + +export function getPreviewMode() { + return getMPEConfig('previewMode'); +} + +export function getEditorActiveLine(editor: vscode.TextEditor) { + return editor.selections[0].active.line ?? 0; +} diff --git a/yarn.lock b/yarn.lock index 615b7ed..a391c14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -276,6 +276,20 @@ dependencies: prop-types "^15.7.2" +"@monaco-editor/loader@^1.3.3": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.4.0.tgz#f08227057331ec890fa1e903912a5b711a2ad558" + integrity sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg== + dependencies: + state-local "^1.0.6" + +"@monaco-editor/react@^4.5.2": + version "4.5.2" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.5.2.tgz#e8cc802203f729b423a998ea6fcb466604d61258" + integrity sha512-emcWu6vg1OpXPiYll4aPOaXe8bwYB4UaaNTwtArFLgMoNGBzRZb2Xn0Bra2HMIFM7QLgs7fCGunHO5LkfT2LBA== + dependencies: + "@monaco-editor/loader" "^1.3.3" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -309,15 +323,15 @@ picocolors "^1.0.0" tslib "^2.6.0" -"@puppeteer/browsers@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.7.0.tgz#714a25ad6963f5478e36004ea7eda254870a4659" - integrity sha512-sl7zI0IkbQGak/+IE3VEEZab5SSOlI5F6558WvzWGC1n3+C722rfewC1ZIkcF9dsoGSsxhsONoseVlNQG4wWvQ== +"@puppeteer/browsers@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.7.1.tgz#04f1e3aec4b87f50a7acc8f64be2149bda014f0a" + integrity sha512-nIb8SOBgDEMFY2iS2MdnUZOg2ikcYchRrBoF+wtdjieRFKR2uGRipHY/oFLo+2N6anDualyClPzGywTHRGrLfw== dependencies: debug "4.3.4" extract-zip "2.0.1" progress "2.0.3" - proxy-agent "6.3.0" + proxy-agent "6.3.1" tar-fs "3.0.4" unbzip2-stream "1.4.3" yargs "17.7.1" @@ -329,6 +343,11 @@ dependencies: any-observable "^0.3.0" +"@sanity/diff-match-patch@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@sanity/diff-match-patch/-/diff-match-patch-3.1.1.tgz#16514d3a550d880bae1f59cc3ffe6865f5a4b58a" + integrity sha512-dSZqGeYjHKGIkqAzGqLcG92LZyJGX+nYbs/FWawhBbTBDWi21kvQ0hsL3DJThuFVWtZMWTQijN3z6Cnd44Pf2g== + "@tootallnate/quickjs-emscripten@^0.23.0": version "0.23.0" resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" @@ -341,11 +360,6 @@ dependencies: "@types/node" "*" -"@types/clone@~2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/clone/-/clone-2.1.1.tgz#9b880d0ce9b1f209b5e0bd6d9caa38209db34024" - integrity sha512-BZIU34bSYye0j/BFcPraiDZ5ka6MJADjcDVELGf7glr9K+iE8NYVjFslJFVWzskSxkLLyCrSPScE82/UUoBSvg== - "@types/crypto-js@^4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.2.tgz#fb56b34f397d9ae2335611e416f15e7d65e276e6" @@ -357,21 +371,21 @@ integrity sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw== "@types/d3-scale@^4.0.3": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.4.tgz#3c5e2263eea5a3670cd91043b9f4d150a94c43f1" - integrity sha512-eq1ZeTj0yr72L8MQk6N6heP603ubnywSDRfNpi5enouR112HzGLS6RIvExCzZTraFF4HdzNpJMwA/zGiMoHUUw== + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.5.tgz#daa4faa5438315a37a1f5eb1bcdc5aeb3d3e5a2d" + integrity sha512-w/C++3W394MHzcLKO2kdsIn5KKNTOqeQVzyPSGPLzQbkPw/jpeaGtSRlakcKevGgGsjJxGsbqS0fPrVFDbHrDA== dependencies: "@types/d3-time" "*" "@types/d3-time@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.0.tgz#e1ac0f3e9e195135361fa1a1d62f795d87e6e819" - integrity sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.1.tgz#f0c8f9037632cc4511ae55e7e1459dcb95fb3619" + integrity sha512-5j/AnefKAhCw4HpITmLDTPlf4vhi8o/dES+zbegfPb7LaGfNyqkLxBR6E+4yvTAgnJLmhe80EXFMzUs38fw4oA== "@types/debug@^4.0.0": - version "4.1.8" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" - integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.9.tgz#906996938bc672aaf2fb8c0d3733ae1dda05b005" + integrity sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow== dependencies: "@types/ms" "*" @@ -483,9 +497,9 @@ integrity sha512-qK/CmOdS2o7ry3k6YqU4zD3R2AYlJfbwBoSbKpBoP+GpXNE+0NEgJOli4n0bm0diK5kfBnchgCEj4igQz/44Hg== "@types/yauzl@^2.9.1": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" - integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== + version "2.10.1" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.1.tgz#4e8f299f0934d60f36c74f59cb5a8483fd786691" + integrity sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw== dependencies: "@types/node" "*" @@ -575,9 +589,9 @@ eslint-visitor-keys "^3.4.1" "@viz-js/viz@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@viz-js/viz/-/viz-3.1.0.tgz#f192a4f98f1fa187b959f2f54d169531677a0d99" - integrity sha512-rQi2PL9HoqTpE5DVodGgSLRdRVkDVgTG36AdcUR8EWxHL45KoUw7PAzZLm70vgK+QbUN6nCraTtqgxyqeJzxBA== + version "3.2.0" + resolved "https://registry.yarnpkg.com/@viz-js/viz/-/viz-3.2.0.tgz#fc912f8f95aace478c391bebeedd3a3a7a86ac17" + integrity sha512-CWSfQKrwt9ZrLTUK4rqhqfkdkk89rnsp7hnt3sPs01GmgA9kU2yBUwMYMmb+/w8zKFut/ZtLcMeLeaAQrZ9oOg== "@vscode/test-web@^0.0.45": version "0.0.45" @@ -772,7 +786,7 @@ acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== -agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: +agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== @@ -1107,13 +1121,13 @@ atob@^2.1.2: integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== autoprefixer@^10.4.13: - version "10.4.15" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.15.tgz#a1230f4aeb3636b89120b34a1f513e2f6834d530" - integrity sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew== + version "10.4.16" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" + integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== dependencies: browserslist "^4.21.10" - caniuse-lite "^1.0.30001520" - fraction.js "^4.2.0" + caniuse-lite "^1.0.30001538" + fraction.js "^4.3.6" normalize-range "^0.1.2" picocolors "^1.0.0" postcss-value-parser "^4.2.0" @@ -1294,7 +1308,7 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" -browserslist@^4.14.5, browserslist@^4.21.10: +browserslist@^4.14.5: version "4.21.10" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== @@ -1304,6 +1318,16 @@ browserslist@^4.14.5, browserslist@^4.21.10: node-releases "^2.0.13" update-browserslist-db "^1.0.11" +browserslist@^4.21.10: + version "4.22.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.0.tgz#6adc8116589ccea8a99d0df79c5de2436199abdb" + integrity sha512-v+Jcv64L2LbfTC6OnRcaxtqJNJuQAVhZKSJfR/6hn7lhnChUXl4amwVviqN1k411BB+3rRoKMitELRn1CojeRA== + dependencies: + caniuse-lite "^1.0.30001539" + electron-to-chromium "^1.4.530" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -1409,10 +1433,15 @@ caniuse-lite@^1.0.30001517: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz#d2e8fdec6116ffa36284ca2c33ef6d53612fe1c8" integrity sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q== -caniuse-lite@^1.0.30001520: - version "1.0.30001532" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001532.tgz#c6a4d5d2da6d2b967f0ee5e12e7f680db6ad2fca" - integrity sha512-FbDFnNat3nMnrROzqrsg314zhqN5LGQ1kyyMk2opcrwGbVGpHRhgCWtAgD5YJUqNAiQ+dklreil/c3Qf1dfCTw== +caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001539: + version "1.0.30001540" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001540.tgz#a316ca4f2ae673ab02ff0ec533334016d56ff658" + integrity sha512-9JL38jscuTJBTcuETxm8QLsFr/F6v0CYYTEU6r5+qSM98P2Q0Hmu0eG1dTG5GBUmywU3UlcVOUSIJYY47rdFSw== + +case-anything@^2.1.13: + version "2.1.13" + resolved "https://registry.yarnpkg.com/case-anything/-/case-anything-2.1.13.tgz#0cdc16278cb29a7fcdeb072400da3f342ba329e9" + integrity sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng== caseless@~0.12.0: version "0.12.0" @@ -1528,12 +1557,13 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -chromium-bidi@0.4.22: - version "0.4.22" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.22.tgz#625dab72946e177f538da2d2b8a681652ef916da" - integrity sha512-wR7Y9Ioez+cNXT4ZP7VNM1HRTljpNnMSLw4/RnwhhZUP4yCU7kIQND00YiktuHekch68jklGPK1q9Jkb29+fQg== +chromium-bidi@0.4.28: + version "0.4.28" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.28.tgz#05befef4f3f19003198237245780d1c60e6f4dbc" + integrity sha512-2HZ74QlAApJrEwcGlU/sUu0s4VS+FI3CJ09Toc9aE9VemMyhHZXeaROQgJKNRaYMUTUx6qIv1cLBs3F+vfgjSw== dependencies: mitt "3.0.1" + urlpattern-polyfill "9.0.0" class-utils@^0.3.5: version "0.3.6" @@ -1621,7 +1651,7 @@ clone-stats@^1.0.0: resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" integrity sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag== -clone@^2.1.1, clone@~2.1.2: +clone@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== @@ -1890,18 +1920,21 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crossnote@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/crossnote/-/crossnote-0.8.16.tgz#9e0ffc810cdf8a0276c34c7a66838df63549bb07" - integrity sha512-SI4gl19HJFuwiXuJSb5CX2N6g+DkYouuzy1MjQ3Rri9OLGPzuQvc5NOGdPYFQXTuxVBSvLna/nL/6Y0NutyVqQ== +crossnote@^0.8.18: + version "0.8.18" + resolved "https://registry.yarnpkg.com/crossnote/-/crossnote-0.8.18.tgz#806a37f6ff8627cada55ee645b9e8c1e2307479d" + integrity sha512-LvhWOU3/T6BAoDrWC/5I7XgnC/PMvKZJFyVBnrl/l5+OZtNw4QTU0jx+Q3dge2HCpER7xdApQo0ITZWER8Xm9g== dependencies: "@headlessui/react" "^1.7.17" "@heroicons/react" "^2.0.18" "@mdi/js" "^7.2.96" "@mdi/react" "^1.6.1" + "@monaco-editor/react" "^4.5.2" + "@sanity/diff-match-patch" "^3.1.1" "@viz-js/viz" "^3.1.0" async-mutex "^0.4.0" bit-field "^1.8.0" + case-anything "^2.1.13" cheerio "^1.0.0-rc.12" chrome-paths "^1.0.1" classnames "^2.3.2" @@ -1914,7 +1947,7 @@ crossnote@^0.8.16: html-react-parser "^4.2.2" imagemagick-cli "^0.5.0" jquery "^3.7.1" - katex "^0.16.8" + katex "^0.16.9" less "^4.2.0" markdown-it "^13.0.1" markdown-it-abbr "^1.0.4" @@ -1929,6 +1962,7 @@ crossnote@^0.8.16: mermaid "^10.4.0" minisearch "^6.1.0" mkdirp "^3.0.1" + monaco-editor "^0.43.0" object-hash "^3.0.0" onml "^2.1.0" pako "^2.1.0" @@ -1939,10 +1973,10 @@ crossnote@^0.8.16: react "^18.2.0" react-contexify "^6.0.0" react-dom "^18.2.0" + reading-time "^1.5.0" request "^2.88.0" simple-icons "^9.13.0" slash "^5.1.0" - snake-case "^3.0.4" sval "^0.4.8" temp "^0.9.0" twemoji "^13.1.0" @@ -2320,9 +2354,9 @@ dagre-d3-es@7.0.10: lodash-es "^4.17.21" daisyui@^3.7.3: - version "3.7.3" - resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-3.7.3.tgz#247cfefa0112c31679af9035eff65e1bd866f062" - integrity sha512-gKlz3RwfaukZxf8nQZsDAZ7quUSi7F8HjGGB34tkHruvfQB9cgVDQsmqUqSQtkAJYvzbrg/3dLUa9+5jF4iC1A== + version "3.8.0" + resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-3.8.0.tgz#e61f77bb52a2d7f45e3bf2d9f6b0897144caa9d1" + integrity sha512-VsWD//XlHkOBFSiRNTOZTxTJ/W8xI65erowErfbDWrPTkMYqf0ee/FTaqn4rquZoCcumENdFegAL8eELMnztxQ== dependencies: colord "^2.9" css-selector-tokenizer "^0.8" @@ -2355,9 +2389,9 @@ date-fns@^2.30.0: "@babel/runtime" "^7.21.0" dayjs@^1.11.7: - version "1.11.9" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" - integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -2566,10 +2600,10 @@ detect-newline@^4.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-4.0.0.tgz#450ac3f864d5f61112b53a524123b012c59581bc" integrity sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw== -devtools-protocol@0.0.1159816: - version "0.0.1159816" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1159816.tgz#b5848e8597de01e4738589e7553674c7312c8d2a" - integrity sha512-2cZlHxC5IlgkIWe2pSDmCrDiTzbSJWywjbDDnupOImEBcG31CQgBLV8wWE+5t+C4rimcjHsbzy7CBzf9oFjboA== +devtools-protocol@0.0.1179426: + version "0.0.1179426" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1179426.tgz#c4c3ee671efae868395569123002facbbbffa267" + integrity sha512-KKC7IGwdOr7u9kTGgjUvGTov/z1s2H7oHi3zKCdR9eSDyCPia5CBi4aRhtp7d8uR7l0GS5UTDw3TjKGu5CqINg== didyoumean@^1.2.2: version "1.2.2" @@ -2645,14 +2679,6 @@ domutils@^3.0.1, domutils@^3.1.0: domelementtype "^2.3.0" domhandler "^5.0.3" -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - duplexify@^3.5.0, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -2689,6 +2715,11 @@ electron-to-chromium@^1.4.477: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz#5641ff2f5ba11df4bd960fe6a2f9f70aa8b9af96" integrity sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg== +electron-to-chromium@^1.4.530: + version "1.4.531" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.531.tgz#22966d894c4680726c17cf2908ee82ff5d26ac25" + integrity sha512-H6gi5E41Rn3/mhKlPaT1aIMg/71hTAqn0gYEllSuw9igNWtvQwu185jiCZoZD29n7Zukgh7GVZ3zGf0XvkhqjQ== + elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -3203,7 +3234,7 @@ fancy-log@^1.3.2: parse-node-version "^1.0.0" time-stamp "^1.0.0" -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -3224,7 +3255,7 @@ fast-glob@^3.0.3, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@~2.1.0: +fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -3450,7 +3481,7 @@ formstream@^1.1.0: mime "^2.5.2" pause-stream "~0.0.11" -fraction.js@^4.2.0: +fraction.js@^4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.6.tgz#e9e3acec6c9a28cf7bc36cbe35eea4ceb2c5c92d" integrity sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg== @@ -4121,7 +4152,7 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1: +https-proxy-agent@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz#0277e28f13a07d45c663633841e20a40aaafe0ab" integrity sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ== @@ -4129,6 +4160,14 @@ https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1: agent-base "^7.0.2" debug "4" +https-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -4885,10 +4924,10 @@ karma-chrome-launcher@^2.2.0: fs-access "^1.0.0" which "^1.2.1" -katex@^0.16.8: - version "0.16.8" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.8.tgz#89b453f40e8557f423f31a1009e9298dd99d5ceb" - integrity sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg== +katex@^0.16.9: + version "0.16.9" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.9.tgz#bc62d8f7abfea6e181250f85a56e4ef292dcb1fa" + integrity sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ== dependencies: commander "^8.3.0" @@ -5280,13 +5319,6 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -5370,9 +5402,9 @@ markdown-it-sup@^1.0.0: integrity sha512-E32m0nV9iyhRR7CrhnzL5msqic7rL1juWre6TQNxsnApg7Uf+F97JOKxUijg5YwXz86lZ0mqfOnutoryyNdntQ== markdown-it@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430" - integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q== + version "13.0.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" + integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== dependencies: argparse "^2.0.1" entities "~3.0.1" @@ -5846,6 +5878,11 @@ mockdate@^3.0.5: resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb" integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ== +monaco-editor@^0.43.0: + version "0.43.0" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.43.0.tgz#cb02a8d23d1249ad00b7cffe8bbecc2ac09d4baf" + integrity sha512-cnoqwQi/9fml2Szamv1XbSJieGJ1Dc8tENVMD26Kcfl7xGQWp7OBKMjlwKVGYFJ3/AXJjSOGvcqK7Ry/j9BM1Q== + morgan@^1.6.1: version "1.10.0" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" @@ -5957,14 +5994,6 @@ next-tick@^1.1.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - node-environment-flags@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" @@ -6339,19 +6368,19 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pac-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.0.tgz#db42120c64292685dafaf2bd921e223c56bfb13b" - integrity sha512-t4tRAMx0uphnZrio0S0Jw9zg3oDbz1zVhQ/Vy18FjLfP1XOLNUEjaVxYCYRI6NS+BsMBXKIzV6cTLOkO9AtywA== +pac-proxy-agent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" + integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A== dependencies: "@tootallnate/quickjs-emscripten" "^0.23.0" agent-base "^7.0.2" debug "^4.3.4" get-uri "^6.0.1" http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" pac-resolver "^7.0.0" - socks-proxy-agent "^8.0.1" + socks-proxy-agent "^8.0.2" pac-resolver@^7.0.0: version "7.0.0" @@ -6711,9 +6740,9 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8, postcss@^8.4.21, postcss@^8.4.23: - version "8.4.29" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" - integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== + version "8.4.30" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.30.tgz#0e0648d551a606ef2192a26da4cabafcc09c1aa7" + integrity sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -6761,19 +6790,19 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" -proxy-agent@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.0.tgz#72f7bb20eb06049db79f7f86c49342c34f9ba08d" - integrity sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og== +proxy-agent@6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.1.tgz#40e7b230552cf44fd23ffaf7c59024b692612687" + integrity sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ== dependencies: agent-base "^7.0.2" debug "^4.3.4" http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" lru-cache "^7.14.1" - pac-proxy-agent "^7.0.0" + pac-proxy-agent "^7.0.1" proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.1" + socks-proxy-agent "^8.0.2" proxy-from-env@^1.1.0: version "1.1.0" @@ -6821,16 +6850,16 @@ punycode@^2.1.0, punycode@^2.1.1: integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== puppeteer-core@^21.1.0: - version "21.1.1" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-21.1.1.tgz#59be20b6f69acc2139ba2d9e02a33793b59254ff" - integrity sha512-Tlcajcf44zwfa9Sbwv3T8BtaNMJ69wtpHIxwl2NOBTyTK3D1wppQovXTjfw0TDOm3a16eCfQ+5BMi3vRQ4kuAQ== + version "21.3.5" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-21.3.5.tgz#f9f592590ce2c3633725ff4a6f924bc66c420640" + integrity sha512-C/yVgvob/HbUVTedhnURDruFkJYHEqJWlb6YltJGj/T7yzWdG4ouQ0JER8aX5g2RS4DMQ0xMNuhUVYMqC2QfnQ== dependencies: - "@puppeteer/browsers" "1.7.0" - chromium-bidi "0.4.22" + "@puppeteer/browsers" "1.7.1" + chromium-bidi "0.4.28" cross-fetch "4.0.0" debug "4.3.4" - devtools-protocol "0.0.1159816" - ws "8.13.0" + devtools-protocol "0.0.1179426" + ws "8.14.2" qiniu@^7.9.0: version "7.9.0" @@ -6972,6 +7001,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +reading-time@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" + integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -7150,7 +7184,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.2, resolve@^1.4.0: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.4.0: version "1.22.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== @@ -7159,6 +7193,15 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22. path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.2: + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -7278,9 +7321,9 @@ safe-regex@^1.1.0: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@^1.2.1, sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== scheduler@^0.23.0: version "0.23.0" @@ -7398,9 +7441,9 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-icons@^9.13.0: - version "9.13.0" - resolved "https://registry.yarnpkg.com/simple-icons/-/simple-icons-9.13.0.tgz#c91f161d0744d3328fb7459c8c785f0f8b83e418" - integrity sha512-XpfRX8sX4kfizDW0SeD8ndy3twYndovdAmcIsJmJmwyLSOEP88emOVPBVj9zwb9apxRBqn+rHu4vMSPbbk8u5A== + version "9.16.0" + resolved "https://registry.yarnpkg.com/simple-icons/-/simple-icons-9.16.0.tgz#574ae8aa14d10451ad088c5578e26e8474402182" + integrity sha512-KM+7MWNlRnAjeURKV6VvJmjqfHuRLwgXxZ2IrtV+bqsz/8P/xmFEvJd+8xUww94bbPEIsHeXK7A9BwJASYnXrQ== slash@^3.0.0: version "3.0.0" @@ -7427,14 +7470,6 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -snake-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" - integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== - dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -7465,12 +7500,12 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" - integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== +socks-proxy-agent@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad" + integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g== dependencies: - agent-base "^7.0.1" + agent-base "^7.0.2" debug "^4.3.4" socks "^2.7.1" @@ -7607,6 +7642,11 @@ stack-trace@0.0.10: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== +state-local@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" + integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w== + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -8128,16 +8168,11 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.0: +tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.0, tslib@~2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@~2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== - tspan@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/tspan/-/tspan-0.4.0.tgz#49c2347598214443a77d7823e7c456e200a4aea2" @@ -8188,9 +8223,9 @@ type-fest@^0.20.2: integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.3.1.tgz#5cb58cdab5120f7ab0b40cfdc35073fb9adb651d" - integrity sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw== + version "4.3.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.3.2.tgz#bb7948127bb644364994dc1b44b8a797da8aadcd" + integrity sha512-VpwuOgnTsQUUWi0id8Hl4/xiQ+OoaeJGe8dnFjzubJYe/lOc2/d1Qx/d3FqWR0FlpOG/cvukAXfB12A49Y4iiA== type-is@^1.6.16: version "1.6.18" @@ -8400,6 +8435,14 @@ update-browserslist-db@^1.0.11: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -8437,6 +8480,11 @@ urllib@^2.34.1: statuses "^1.3.1" utility "^1.16.1" +urlpattern-polyfill@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz#bc7e386bb12fd7898b58d1509df21d3c29ab3460" + integrity sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g== + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -8471,9 +8519,9 @@ uuid@^3.3.2: integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== uvu@^0.5.0: version "0.5.6" @@ -8628,16 +8676,12 @@ vega-label@~1.2.1: vega-util "^1.15.2" vega-lite@^5.14.1: - version "5.14.1" - resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-5.14.1.tgz#02177cb04af85c7011ab302a441d17e0bb6ac810" - integrity sha512-VFvi0QtUoLQqwfAXTGjo0Acw/OTjiK3zOrcO/HyksGnnNDBHWM1GTcFryiWZYoAi99ehvv7tI/q94O46+fGRSQ== - dependencies: - "@types/clone" "~2.1.1" - clone "~2.1.2" - fast-deep-equal "~3.1.3" - fast-json-stable-stringify "~2.1.0" + version "5.15.0" + resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-5.15.0.tgz#aa339a0f66df2ef4b02e729f446a91b502f541e9" + integrity sha512-Eac4VBhdtwbJQWH8m2OaRba/YVZbUHlmTAiPfiF3XIapJ73rcs+gHZBE1DfYgfoGjBN+5YJUMvdgm4UE7j/Ncg== + dependencies: json-stringify-pretty-compact "~3.0.0" - tslib "~2.5.0" + tslib "~2.6.2" vega-event-selector "~3.0.1" vega-expression "~5.1.0" vega-util "~1.17.2" @@ -9111,10 +9155,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +ws@8.14.2: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== xtend@~4.0.0, xtend@~4.0.1: version "4.0.2"