From 5a9ef1add835fc695b67229b8ca7960a384b3936 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Thu, 28 Sep 2023 11:33:08 +0200 Subject: [PATCH] #677 - Support for Browse Lite --- CHANGELOG.md | 1 + package.json | 274 ++++++++++++--------------- src/commands/Preview.ts | 47 +++-- src/commands/StatusListener.ts | 2 +- src/extension.ts | 6 +- src/helpers/ArticleHelper.ts | 18 +- src/helpers/ImageHelper.ts | 8 +- src/listeners/panel/DataListener.ts | 5 +- src/listeners/panel/MediaListener.ts | 7 +- src/panelWebView/PanelProvider.ts | 49 ++++- 10 files changed, 227 insertions(+), 190 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b36f6e65..ccf8877f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - [#669](https://github.com/estruyf/vscode-front-matter/issues/669): Add the video preview to the details panel + caption field - [#674](https://github.com/estruyf/vscode-front-matter/issues/674): Specify to disable content creation for a specific page folder - [#676](https://github.com/estruyf/vscode-front-matter/issues/676): Allow the `frontmatter.json` file to be placed in a sub-directory +- [#677](https://github.com/estruyf/vscode-front-matter/issues/677): Added support for [Browse Lite](https://marketplace.visualstudio.com/items?itemName=antfu.browse-lite) extension to open preview when installed ### ⚡️ Optimizations diff --git a/package.json b/package.json index 47720fef..84cfb338 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ "color": "#0e131f", "theme": "dark" }, - "badges": [ - { + "badges": [{ "description": "version", "url": "https://img.shields.io/github/package-json/v/estruyf/vscode-front-matter?color=green&label=vscode-front-matter&style=flat-square", "href": "https://github.com/estruyf/vscode-front-matter" @@ -71,8 +70,7 @@ "**/.frontmatter/config/*.json": "jsonc" } }, - "keybindings": [ - { + "keybindings": [{ "command": "frontMatter.dashboard", "key": "alt+d" }, @@ -90,24 +88,19 @@ } ], "viewsContainers": { - "activitybar": [ - { - "id": "frontmatter-explorer", - "title": "Front Matter", - "icon": "assets/frontmatter-short-min.svg" - } - ] + "activitybar": [{ + "id": "frontmatter-explorer", + "title": "FM", + "icon": "assets/frontmatter-short-min.svg" + }] }, "views": { - "frontmatter-explorer": [ - { - "id": "frontMatter.explorer", - "name": "Front Matter", - "icon": "assets/frontmatter-short-min.svg", - "contextualTitle": "Front Matter", - "type": "webview" - } - ] + "frontmatter-explorer": [{ + "id": "frontMatter.explorer", + "name": "Front Matter", + "icon": "assets/frontmatter-short-min.svg", + "type": "webview" + }] }, "configuration": { "title": "%settings.configuration.title%", @@ -175,8 +168,7 @@ "frontMatter.content.defaultFileType": { "type": "string", "default": "md", - "oneOf": [ - { + "oneOf": [{ "enum": [ "md", "mdx" @@ -192,8 +184,7 @@ "frontMatter.content.defaultSorting": { "type": "string", "default": "", - "oneOf": [ - { + "oneOf": [{ "enum": [ "LastModifiedAsc", "LastModifiedDesc", @@ -545,8 +536,7 @@ "command": { "$id": "#scriptCommand", "type": "string", - "anyOf": [ - { + "anyOf": [{ "enum": [ "node", "bash", @@ -746,8 +736,7 @@ "title", "file" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -801,8 +790,7 @@ "id", "path" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -1176,8 +1164,7 @@ "default": "", "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -1366,8 +1353,7 @@ "type", "name" ], - "allOf": [ - { + "allOf": [{ "if": { "properties": { "type": { @@ -1567,51 +1553,48 @@ "fields" ] }, - "default": [ - { - "name": "default", - "pageBundle": false, - "fields": [ - { - "title": "Title", - "name": "title", - "type": "string" - }, - { - "title": "Description", - "name": "description", - "type": "string" - }, - { - "title": "Publishing date", - "name": "date", - "type": "datetime", - "default": "{{now}}", - "isPublishDate": true - }, - { - "title": "Content preview", - "name": "preview", - "type": "image" - }, - { - "title": "Is in draft", - "name": "draft", - "type": "boolean" - }, - { - "title": "Tags", - "name": "tags", - "type": "tags" - }, - { - "title": "Categories", - "name": "categories", - "type": "categories" - } - ] - } - ], + "default": [{ + "name": "default", + "pageBundle": false, + "fields": [{ + "title": "Title", + "name": "title", + "type": "string" + }, + { + "title": "Description", + "name": "description", + "type": "string" + }, + { + "title": "Publishing date", + "name": "date", + "type": "datetime", + "default": "{{now}}", + "isPublishDate": true + }, + { + "title": "Content preview", + "name": "preview", + "type": "image" + }, + { + "title": "Is in draft", + "name": "draft", + "type": "boolean" + }, + { + "title": "Tags", + "name": "tags", + "type": "tags" + }, + { + "title": "Categories", + "name": "categories", + "type": "categories" + } + ] + }], "scope": "Taxonomy" }, "frontMatter.taxonomy.customTaxonomy": { @@ -1624,8 +1607,7 @@ "type": "string", "description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -1811,8 +1793,7 @@ } } }, - "commands": [ - { + "commands": [{ "command": "frontMatter.project.switch", "title": "%command.frontMatter.project.switch%", "category": "Front Matter", @@ -2147,15 +2128,12 @@ "category": "Front Matter" } ], - "submenus": [ - { - "id": "frontmatter.submenu", - "label": "Front Matter" - } - ], + "submenus": [{ + "id": "frontmatter.submenu", + "label": "Front Matter" + }], "menus": { - "editor/title": [ - { + "editor/title": [{ "command": "frontMatter.markup.heading", "group": "navigation@-133", "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" @@ -2236,14 +2214,11 @@ "when": "resourceFilename == 'frontmatter.json'" } ], - "explorer/context": [ - { - "submenu": "frontmatter.submenu", - "group": "frontmatter@1" - } - ], - "frontmatter.submenu": [ - { + "explorer/context": [{ + "submenu": "frontmatter.submenu", + "group": "frontmatter@1" + }], + "frontmatter.submenu": [{ "command": "frontMatter.createFromTemplate", "when": "explorerResourceIsFolder", "group": "frontmatter@1" @@ -2259,8 +2234,7 @@ "group": "frontmatter@3" } ], - "commandPalette": [ - { + "commandPalette": [{ "command": "frontMatter.init", "when": "frontMatterCanInit" }, @@ -2405,8 +2379,7 @@ "when": "frontMatter:file:isValid == true" } ], - "view/title": [ - { + "view/title": [{ "command": "frontMatter.chatbot", "group": "navigation@0", "when": "view == frontMatter.explorer" @@ -2438,57 +2411,52 @@ } ] }, - "grammars": [ - { - "path": "./syntaxes/hugo.tmLanguage.json", - "scopeName": "frontmatter.markdown.hugo", - "injectTo": [ - "text.html.markdown" - ] - } - ], - "walkthroughs": [ - { - "id": "frontmatter.welcome", - "title": "Get started with Front Matter", - "description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.", - "steps": [ - { - "id": "frontmatter.welcome.init", - "title": "Get started", - "description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)", - "media": { - "markdown": "assets/walkthrough/get-started.md" - }, - "completionEvents": [ - "onContext:frontMatterInitialized" - ] + "grammars": [{ + "path": "./syntaxes/hugo.tmLanguage.json", + "scopeName": "frontmatter.markdown.hugo", + "injectTo": [ + "text.html.markdown" + ] + }], + "walkthroughs": [{ + "id": "frontmatter.welcome", + "title": "Get started with Front Matter", + "description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.", + "steps": [{ + "id": "frontmatter.welcome.init", + "title": "Get started", + "description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)", + "media": { + "markdown": "assets/walkthrough/get-started.md" }, - { - "id": "frontmatter.welcome.documentation", - "title": "Documentation", - "description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)", - "media": { - "markdown": "assets/walkthrough/documentation.md" - }, - "completionEvents": [ - "onLink:https://frontmatter.codes/docs" - ] + "completionEvents": [ + "onContext:frontMatterInitialized" + ] + }, + { + "id": "frontmatter.welcome.documentation", + "title": "Documentation", + "description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)", + "media": { + "markdown": "assets/walkthrough/documentation.md" }, - { - "id": "frontmatter.welcome.supporter", - "title": "Support the project", - "description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)", - "media": { - "markdown": "assets/walkthrough/support-the-project.md" - }, - "completionEvents": [ - "onLink:https://github.com/sponsors/estruyf" - ] - } - ] - } - ] + "completionEvents": [ + "onLink:https://frontmatter.codes/docs" + ] + }, + { + "id": "frontmatter.welcome.supporter", + "title": "Support the project", + "description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)", + "media": { + "markdown": "assets/walkthrough/support-the-project.md" + }, + "completionEvents": [ + "onLink:https://github.com/sponsors/estruyf" + ] + } + ] + }] }, "scripts": { "dev:ext": "npm run clean && npm run localization:generate && npm-run-all --parallel watch:*", @@ -2625,4 +2593,4 @@ "vsce": { "dependencies": false } -} +} \ No newline at end of file diff --git a/src/commands/Preview.ts b/src/commands/Preview.ts index 2f9e3890..17a512d8 100644 --- a/src/commands/Preview.ts +++ b/src/commands/Preview.ts @@ -13,7 +13,7 @@ import { } from './../constants'; import { ArticleHelper } from './../helpers/ArticleHelper'; import { join, parse } from 'path'; -import { commands, env, Uri, ViewColumn, window, WebviewPanel } from 'vscode'; +import { commands, env, Uri, ViewColumn, window, WebviewPanel, extensions } from 'vscode'; import { Extension, parseWinPath, processKnownPlaceholders, Settings } from '../helpers'; import { ContentFolder, ContentType, PreviewSettings } from '../models'; import { format } from 'date-fns'; @@ -22,7 +22,6 @@ import { Article } from '.'; import { urlJoin } from 'url-join-ts'; import { WebviewHelper } from '@estruyf/vscode'; import { Folders } from './Folders'; -import { DataListener } from '../listeners/panel'; import { ParsedFrontMatter } from '../parsers'; import { getLocalizationFile } from '../utils/getLocalizationFile'; @@ -48,17 +47,26 @@ export class Preview { return; } + const browserLiteCommand = await this.getBrowserLiteCommand(); + const editor = window.activeTextEditor; const crntFilePath = editor?.document.uri.fsPath; this.filePath = crntFilePath; - if (crntFilePath && this.webviews[crntFilePath]) { + if (crntFilePath && this.webviews[crntFilePath] && !browserLiteCommand) { this.webviews[crntFilePath].reveal(); return; } const article = editor ? ArticleHelper.getFrontMatter(editor) : null; const slug = await this.getContentSlug(article, editor?.document.uri.fsPath); + const localhostUrl = await this.getLocalServerUrl(); + + if (browserLiteCommand) { + const pageUrl = urlJoin(localhostUrl.toString(), slug || ''); + commands.executeCommand(browserLiteCommand, pageUrl); + return; + } // Create the preview webview const webView = window.createWebviewPanel( @@ -82,29 +90,12 @@ export class Preview { light: Uri.file(join(extensionPath, 'assets/icons/frontmatter-short-light.svg')) }; - const localhostUrl = await this.getLocalServerUrl(); - const cspSource = webView.webview.cspSource; webView.onDidDispose(() => { - this.filePath = undefined; - if (crntFilePath) { - delete this.webviews[crntFilePath]; - } webView.dispose(); }); - webView.onDidChangeViewState(async (e) => { - if (e.webviewPanel.visible) { - this.filePath = crntFilePath; - - if (crntFilePath) { - const article = await ArticleHelper.getFrontMatterByPath(crntFilePath); - DataListener.pushMetadata(article?.data); - } - } - }); - webView.webview.onDidReceiveMessage(async (message) => { switch (message.command) { case PreviewCommands.toVSCode.open: @@ -344,6 +335,22 @@ export class Preview { return slug; } + /** + * Check if Browser Lite is installed + */ + private static async getBrowserLiteCommand() { + const ext = extensions.getExtension(`antfu.browse-lite`); + if (ext && ext.packageJSON) { + const hasCommand = ext.packageJSON.contributes?.commands?.find( + (c: { command: string }) => c.command === 'browse-lite.open' + ); + if (hasCommand) { + return 'browse-lite.open'; + } + } + return undefined; + } + /** * Retrieve the localhost url * @returns diff --git a/src/commands/StatusListener.ts b/src/commands/StatusListener.ts index 0ad6fc7c..b2585957 100644 --- a/src/commands/StatusListener.ts +++ b/src/commands/StatusListener.ts @@ -29,7 +29,7 @@ export class StatusListener { let document = editor?.document; if (!document) { - const filePath = Preview.filePath; + const filePath = Preview.filePath || ArticleHelper.getActiveFile(); if (filePath) { document = await vscode.workspace.openTextDocument(vscode.Uri.file(filePath)); } diff --git a/src/extension.ts b/src/extension.ts index 62aded6f..f13e8966 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -397,8 +397,12 @@ const handleAutoDateUpdate = (e: vscode.TextDocumentWillSaveEvent) => { }; const triggerPageUpdate = (location: string) => { - Logger.info(`Triggering draft status update: ${location}`); + Logger.info(`Trigger page update: ${location}`); pageUpdateDebouncer(() => { StatusListener.verify(collection); }, 1000); + + if (location === 'onDidChangeActiveTextEditor') { + PanelProvider.getInstance()?.updateCurrentFile(); + } }; diff --git a/src/helpers/ArticleHelper.ts b/src/helpers/ArticleHelper.ts index 0e357dfb..bd7b5075 100644 --- a/src/helpers/ArticleHelper.ts +++ b/src/helpers/ArticleHelper.ts @@ -23,7 +23,7 @@ import { } from '../constants'; import { DumpOptions } from 'js-yaml'; import { FrontMatterParser, ParsedFrontMatter } from '../parsers'; -import { ContentType, Extension, Logger, Settings, SlugHelper, parseWinPath } from '.'; +import { ContentType, Extension, Logger, Settings, SlugHelper, isValidFile, parseWinPath } from '.'; import { format, parse } from 'date-fns'; import { Notifications } from './Notifications'; import { Article } from '../commands'; @@ -638,6 +638,22 @@ export class ArticleHelper { return null; } + /** + * Retrieve the active file + * @returns + */ + public static getActiveFile() { + const editors = window.visibleTextEditors; + if (editors.length === 1) { + const editor = editors[0]; + const filePath = parseWinPath(editor.document.uri.fsPath); + if (isValidFile(filePath)) { + return filePath; + } + } + return undefined; + } + /** * Retrieve all the elements from the markdown content * @param node diff --git a/src/helpers/ImageHelper.ts b/src/helpers/ImageHelper.ts index a8091801..3c20de29 100644 --- a/src/helpers/ImageHelper.ts +++ b/src/helpers/ImageHelper.ts @@ -7,6 +7,7 @@ import { existsSync } from 'fs'; import { Folders } from '../commands/Folders'; import { parseWinPath } from './parseWinPath'; import { Preview } from '../commands'; +import { ArticleHelper } from './ArticleHelper'; export class ImageHelper { /** @@ -16,7 +17,10 @@ export class ImageHelper { * @returns */ public static allRelToAbs(field: Field, value: string | string[] | undefined) { - const filePath = window.activeTextEditor?.document.uri.fsPath || Preview.filePath; + let filePath = + window.activeTextEditor?.document.uri.fsPath || + Preview.filePath || + ArticleHelper.getActiveFile(); if (!filePath) { return; } @@ -27,7 +31,7 @@ export class ImageHelper { if (Array.isArray(value)) { previewUri = value.map((v) => ({ original: v, - absPath: v.startsWith('http') ? v : ImageHelper.relToAbs(filePath, v) + absPath: v.startsWith('http') ? v : ImageHelper.relToAbs(filePath as string, v) })); } } else { diff --git a/src/listeners/panel/DataListener.ts b/src/listeners/panel/DataListener.ts index 28d9a039..a8acee27 100644 --- a/src/listeners/panel/DataListener.ts +++ b/src/listeners/panel/DataListener.ts @@ -153,7 +153,10 @@ export class DataListener extends BaseListener { */ public static async pushMetadata(metadata: any) { const wsFolder = Folders.getWorkspaceFolder(); - const filePath = window.activeTextEditor?.document.uri.fsPath || Preview.filePath; + const filePath = + window.activeTextEditor?.document.uri.fsPath || + Preview.filePath || + ArticleHelper.getActiveFile(); const commaSeparated = Settings.get(SETTING_COMMA_SEPARATED_FIELDS); const contentTypes = Settings.get(SETTING_TAXONOMY_CONTENT_TYPES); diff --git a/src/listeners/panel/MediaListener.ts b/src/listeners/panel/MediaListener.ts index 49775127..1fb79529 100644 --- a/src/listeners/panel/MediaListener.ts +++ b/src/listeners/panel/MediaListener.ts @@ -2,7 +2,7 @@ import { PanelProvider } from './../../panelWebView/PanelProvider'; import { commands, window } from 'vscode'; import { Dashboard } from '../../commands/Dashboard'; import { COMMAND_NAME } from '../../constants'; -import { ImageHelper } from '../../helpers'; +import { ArticleHelper, ImageHelper } from '../../helpers'; import { DashboardData, PostMessageData } from '../../models'; import { Command } from '../../panelWebView/Command'; import { CommandToCode } from '../../panelWebView/CommandToCode'; @@ -42,7 +42,10 @@ export class MediaListener extends BaseListener { if (typeof payload === 'string') { const imagePath = payload; - const filePath = window.activeTextEditor?.document.uri.fsPath || Preview.filePath; + const filePath = + window.activeTextEditor?.document.uri.fsPath || + Preview.filePath || + ArticleHelper.getActiveFile(); if (!filePath) { return; } diff --git a/src/panelWebView/PanelProvider.ts b/src/panelWebView/PanelProvider.ts index 98fb27d8..0f8630c3 100644 --- a/src/panelWebView/PanelProvider.ts +++ b/src/panelWebView/PanelProvider.ts @@ -20,7 +20,7 @@ import { WebviewViewResolveContext, window } from 'vscode'; -import { Logger, Settings } from '../helpers'; +import { ArticleHelper, Logger, Settings } from '../helpers'; import { Command } from '../panelWebView/Command'; import { TagType } from '../panelWebView/TagType'; import { WebviewHelper } from '@estruyf/vscode'; @@ -28,6 +28,7 @@ import { Extension } from '../helpers/Extension'; import { Telemetry } from '../helpers/Telemetry'; import { GitListener, ModeListener } from '../listeners/general'; import { Folders } from '../commands'; +import { basename } from 'path'; export class PanelProvider implements WebviewViewProvider, Disposable { public static readonly viewType = 'frontMatter.explorer'; @@ -96,6 +97,8 @@ export class PanelProvider implements WebviewViewProvider, Disposable { }, this) ); + this.updateCurrentFile(); + webviewView.webview.onDidReceiveMessage(async (msg) => { Logger.info(`Receiving message from webview to panel: ${msg.command}`); @@ -159,15 +162,43 @@ export class PanelProvider implements WebviewViewProvider, Disposable { this.sendMessage({ command: Command.closeSections }); } - private counts(acc: any, node: any) { - // add 1 to an initial or existing value - acc[node.type] = (acc[node.type] || 0) + 1; + /** + * On view change, update the current file that is loaded to show the correct data + */ + public async updateCurrentFile() { + const crntPanel = PanelProvider.getInstance(); + if (!crntPanel.visible) { + return; + } + + const filePath = ArticleHelper.getActiveFile(); + if (filePath) { + const article = await ArticleHelper.getFrontMatterByPath(filePath); + DataListener.pushMetadata(article?.data); - // find and add up the counts from all of this node's children - return (node.children || []).reduce( - (childAcc: any, childNode: any) => this.counts(childAcc, childNode), - acc - ); + const fileName = basename(filePath); + crntPanel.updateTitle(fileName); + } else { + crntPanel.updateTitle(undefined); + } + } + + /** + * Update the title of the panel + * @param title + * @returns + */ + private updateTitle(title: string | undefined) { + if (!this.panel) { + return; + } + + if (!title) { + this.panel.title = 'General'; + return; + } + + this.panel.title = title; } /**