mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-06 17:02:54 +02:00
Refactoring explorer view to listeners
This commit is contained in:
@@ -13,6 +13,7 @@ import { DateHelper } from '../helpers/DateHelper';
|
||||
import { parseWinPath } from '../helpers/parseWinPath';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
import { ParsedFrontMatter } from '../parsers';
|
||||
import { MediaListener } from '../listeners/panel';
|
||||
|
||||
|
||||
export class Article {
|
||||
@@ -337,7 +338,7 @@ export class Article {
|
||||
} as DashboardData);
|
||||
|
||||
// Let the editor panel know you are selecting an image
|
||||
ExplorerView.getInstance().getMediaSelection();
|
||||
MediaListener.getMediaSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Credentials } from "../services/Credentials";
|
||||
import fetch from "node-fetch";
|
||||
import { ExplorerView } from '../explorerView/ExplorerView';
|
||||
import { Dashboard } from './Dashboard';
|
||||
import { SettingsListener } from '../listeners/panel';
|
||||
|
||||
export class Backers {
|
||||
private static creds: Credentials | null = null;
|
||||
@@ -60,7 +61,7 @@ export class Backers {
|
||||
if (!prevData) {
|
||||
const explorerView = ExplorerView.getInstance();
|
||||
if (explorerView.visible) {
|
||||
explorerView.getSettings();
|
||||
SettingsListener.getSettings();
|
||||
}
|
||||
|
||||
if (Dashboard.isOpen) {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { PagesListener } from './../listeners/PagesListener';
|
||||
import { ExtensionListener } from './../listeners/ExtensionListener';
|
||||
import { SETTINGS_DASHBOARD_OPENONSTART, CONTEXT } from '../constants';
|
||||
import { join } from "path";
|
||||
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from "vscode";
|
||||
@@ -10,7 +8,8 @@ import { WebviewHelper } from '@estruyf/vscode';
|
||||
import { DashboardData } from '../models/DashboardData';
|
||||
import { ExplorerView } from '../explorerView/ExplorerView';
|
||||
import { MediaLibrary } from '../helpers/MediaLibrary';
|
||||
import { DashboardListener, MediaListener, SettingsListener, TelemetryListener, DataListener } from '../listeners';
|
||||
import { DashboardListener, MediaListener, SettingsListener, TelemetryListener, DataListener, PagesListener, ExtensionListener } from '../listeners/dashboard';
|
||||
import { MediaListener as PanelMediaListener } from '../listeners/panel'
|
||||
|
||||
export class Dashboard {
|
||||
private static webview: WebviewPanel | null = null;
|
||||
@@ -115,8 +114,7 @@ export class Dashboard {
|
||||
Dashboard.webview.onDidChangeViewState(async () => {
|
||||
if (!this.webview?.visible) {
|
||||
Dashboard._viewData = undefined;
|
||||
const panel = ExplorerView.getInstance(extensionUri);
|
||||
panel.getMediaSelection();
|
||||
PanelMediaListener.getMediaSelection();
|
||||
|
||||
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: null });
|
||||
}
|
||||
@@ -127,8 +125,7 @@ export class Dashboard {
|
||||
Dashboard.webview.onDidDispose(async () => {
|
||||
Dashboard.isDisposed = true;
|
||||
Dashboard._viewData = undefined;
|
||||
const panel = ExplorerView.getInstance(extensionUri);
|
||||
panel.getMediaSelection();
|
||||
PanelMediaListener.getMediaSelection();
|
||||
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, false);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { format } from 'date-fns';
|
||||
import { Dashboard } from './Dashboard';
|
||||
import { parseWinPath } from '../helpers/parseWinPath';
|
||||
import { MediaHelpers } from '../helpers/MediaHelpers';
|
||||
import { MediaListener, PagesListener } from '../listeners';
|
||||
import { MediaListener, PagesListener } from '../listeners/dashboard';
|
||||
import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ArticleHelper, SeoHelper, Settings } from '../helpers';
|
||||
import { ExplorerView } from '../explorerView/ExplorerView';
|
||||
import { DefaultFields } from '../constants';
|
||||
import { ContentType } from '../helpers/ContentType';
|
||||
import { DataListener } from '../listeners/panel';
|
||||
|
||||
export class StatusListener {
|
||||
|
||||
@@ -58,7 +59,7 @@ export class StatusListener {
|
||||
|
||||
const panel = ExplorerView.getInstance();
|
||||
if (panel && panel.visible) {
|
||||
panel.pushMetadata(article!.data);
|
||||
DataListener.pushMetadata(article!.data);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -68,7 +69,7 @@ export class StatusListener {
|
||||
} else {
|
||||
const panel = ExplorerView.getInstance();
|
||||
if (panel && panel.visible) {
|
||||
panel.pushMetadata(null);
|
||||
DataListener.pushMetadata(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Project } from './Project';
|
||||
import { Folders } from './Folders';
|
||||
import { ContentType } from '../helpers/ContentType';
|
||||
import { ContentType as IContentType } from '../models';
|
||||
import { PagesListener } from '../listeners';
|
||||
import { PagesListener } from '../listeners/dashboard';
|
||||
import { extname } from 'path';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
|
||||
|
||||
@@ -1,32 +1,12 @@
|
||||
import { DashboardData } from '../models/DashboardData';
|
||||
import { Template } from '../commands/Template';
|
||||
import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST, SETTING_DATE_FORMAT, SETTING_COMMA_SEPARATED_FIELDS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS, SETTINGS_CONTENT_DRAFT_FIELD, SETTING_SEO_SLUG_LENGTH, SETTING_SITE_BASEURL, SETTING_TAXONOMY_CUSTOM, CONTEXT, SETTINGS_FRAMEWORK_ID, SETTINGS_FRAMEWORK_START, TelemetryEvent, SETTINGS_DATA_TYPES } from '../constants';
|
||||
import * as os from 'os';
|
||||
import { PanelSettings, CustomScript as ICustomScript } from '../models/PanelSettings';
|
||||
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv, ThemeIcon } from "vscode";
|
||||
import { ArticleHelper, Settings } from "../helpers";
|
||||
import { ArticleListener, ExtensionListener, MediaListener, ScriptListener, TaxonomyListener, DataListener, SettingsListener } from './../listeners/panel';
|
||||
import { TelemetryEvent } from '../constants';
|
||||
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window } from "vscode";
|
||||
import { Logger, Settings } from "../helpers";
|
||||
import { Command } from "../panelWebView/Command";
|
||||
import { CommandToCode } from '../panelWebView/CommandToCode';
|
||||
import { Article } from '../commands';
|
||||
import { TagType } from '../panelWebView/TagType';
|
||||
import { CustomTaxonomyData, DraftField, Field, ScriptType, TaxonomyType } from '../models';
|
||||
import { exec } from 'child_process';
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown';
|
||||
import { Content } from 'mdast';
|
||||
import { COMMAND_NAME, EXTENSION_BETA_ID, EXTENSION_ID } from '../constants/Extension';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { Preview } from '../commands/Preview';
|
||||
import { openFileInEditor } from '../helpers/openFileInEditor';
|
||||
import { WebviewHelper } from '@estruyf/vscode';
|
||||
import { Extension } from '../helpers/Extension';
|
||||
import { Dashboard } from '../commands/Dashboard';
|
||||
import { ImageHelper } from '../helpers/ImageHelper';
|
||||
import { CustomScript } from '../helpers/CustomScript';
|
||||
import { Link, Parent } from 'mdast-util-from-markdown/lib';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
import { DataType } from '../models/DataType';
|
||||
|
||||
const FILE_LIMIT = 10;
|
||||
|
||||
export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
public static readonly viewType = "frontMatter.explorer";
|
||||
@@ -65,6 +45,10 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public getWebview() {
|
||||
return this.panel?.webview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default resolve webview panel
|
||||
* @param webviewView
|
||||
@@ -87,122 +71,15 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
);
|
||||
|
||||
webviewView.webview.onDidReceiveMessage(async (msg) => {
|
||||
switch(msg.command) {
|
||||
case CommandToCode.getData:
|
||||
this.getSettings();
|
||||
this.getFoldersAndFiles();
|
||||
this.getFileData();
|
||||
break;
|
||||
case CommandToCode.updateSlug:
|
||||
Article.generateSlug();
|
||||
break;
|
||||
case CommandToCode.updateLastMod:
|
||||
Article.setLastModifiedDate();
|
||||
break;
|
||||
case CommandToCode.publish:
|
||||
Article.toggleDraft();
|
||||
break;
|
||||
case CommandToCode.updateTags:
|
||||
this.updateTags(TagType.tags, msg.data?.values || [], msg.data?.parents || []);
|
||||
break;
|
||||
case CommandToCode.updateCategories:
|
||||
this.updateTags(TagType.categories, msg.data?.values || [], msg.data?.parents || []);
|
||||
break;
|
||||
case CommandToCode.updateKeywords:
|
||||
this.updateTags(TagType.keywords, msg.data?.values || [], msg.data?.parents || []);
|
||||
break;
|
||||
case CommandToCode.updateCustomTaxonomy:
|
||||
this.updateCustomTaxonomy(msg.data);
|
||||
break;
|
||||
case CommandToCode.addTagToSettings:
|
||||
this.addTags(TagType.tags, msg.data);
|
||||
break;
|
||||
case CommandToCode.addCategoryToSettings:
|
||||
this.addTags(TagType.categories, msg.data);
|
||||
break;
|
||||
case CommandToCode.addToCustomTaxonomy:
|
||||
this.addCustomTaxonomy(msg.data);
|
||||
break;
|
||||
case CommandToCode.openSettings:
|
||||
const isBeta = Extension.getInstance().isBetaVersion();
|
||||
commands.executeCommand('workbench.action.openSettings', `@ext:${isBeta ? EXTENSION_BETA_ID : EXTENSION_ID}`);
|
||||
break;
|
||||
case CommandToCode.openFile:
|
||||
if (os.type() === "Linux" && vscodeEnv.remoteName?.toLowerCase() === "wsl") {
|
||||
commands.executeCommand('remote-wsl.revealInExplorer');
|
||||
} else {
|
||||
commands.executeCommand('revealFileInOS');
|
||||
}
|
||||
break;
|
||||
case CommandToCode.runCustomScript:
|
||||
this.runCustomScript(msg);
|
||||
break;
|
||||
case CommandToCode.openProject:
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (wsFolder) {
|
||||
const wsPath = wsFolder.fsPath;
|
||||
if (os.type() === "Darwin") {
|
||||
exec(`open ${wsPath}`);
|
||||
} else if (os.type() === "Windows_NT") {
|
||||
exec(`explorer ${wsPath}`);
|
||||
} else if (os.type() === "Linux" && vscodeEnv.remoteName?.toLowerCase() === "wsl") {
|
||||
exec('explorer.exe `wslpath -w "$PWD"`');
|
||||
} else {
|
||||
exec(`xdg-open ${wsPath}`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CommandToCode.initProject:
|
||||
await commands.executeCommand(COMMAND_NAME.init);
|
||||
this.getSettings();
|
||||
break;
|
||||
case CommandToCode.createContent:
|
||||
await commands.executeCommand(COMMAND_NAME.createContent);
|
||||
break;
|
||||
case CommandToCode.createTemplate:
|
||||
await commands.executeCommand(COMMAND_NAME.createTemplate);
|
||||
break;
|
||||
case CommandToCode.updateModifiedUpdating:
|
||||
this.updateSetting(SETTING_AUTO_UPDATE_DATE, msg.data || false);
|
||||
break;
|
||||
case CommandToCode.toggleWritingSettings:
|
||||
this.toggleWritingSettings();
|
||||
break;
|
||||
case CommandToCode.updateFmHighlight:
|
||||
this.updateSetting(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, (msg.data !== null && msg.data !== undefined) ? msg.data : false);
|
||||
break;
|
||||
case CommandToCode.toggleCenterMode:
|
||||
await commands.executeCommand(`workbench.action.toggleCenteredLayout`);
|
||||
break;
|
||||
case CommandToCode.openPreview:
|
||||
await commands.executeCommand(COMMAND_NAME.preview);
|
||||
break;
|
||||
case CommandToCode.openDashboard:
|
||||
await commands.executeCommand(COMMAND_NAME.dashboard);
|
||||
break;
|
||||
case CommandToCode.updatePreviewUrl:
|
||||
this.updateSetting(SETTING_PREVIEW_HOST, msg.data || "");
|
||||
break;
|
||||
case CommandToCode.openInEditor:
|
||||
openFileInEditor(msg.data);
|
||||
break;
|
||||
case CommandToCode.updateMetadata:
|
||||
this.updateMetadata(msg.data);
|
||||
break;
|
||||
case CommandToCode.selectImage:
|
||||
await commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
type: "media",
|
||||
data: msg.data
|
||||
} as DashboardData);
|
||||
this.getMediaSelection();
|
||||
break;
|
||||
case CommandToCode.frameworkCommand:
|
||||
this.openTerminalWithCommand(msg.data.command);
|
||||
break;
|
||||
case CommandToCode.updateStartCommand:
|
||||
await this.updateSetting(SETTINGS_FRAMEWORK_START, msg.data || "");
|
||||
break;
|
||||
}
|
||||
Logger.info(`Receiving message from webview to panel: ${msg.command}`);
|
||||
|
||||
ArticleListener.process(msg);
|
||||
DataListener.process(msg);
|
||||
ExtensionListener.process(msg);
|
||||
MediaListener.process(msg);
|
||||
ScriptListener.process(msg);
|
||||
SettingsListener.process(msg);
|
||||
TaxonomyListener.process(msg);
|
||||
});
|
||||
|
||||
webviewView.onDidChangeVisibility(() => {
|
||||
@@ -213,67 +90,24 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
});
|
||||
|
||||
window.onDidChangeActiveTextEditor(() => {
|
||||
this.postWebviewMessage({ command: Command.loading, data: true });
|
||||
this.sendMessage({ command: Command.loading, data: true });
|
||||
|
||||
if (this.visible) {
|
||||
this.getFileData();
|
||||
DataListener.getFileData();
|
||||
}
|
||||
}, this);
|
||||
|
||||
Settings.onConfigChange((global?: any) => {
|
||||
this.getSettings();
|
||||
SettingsListener.getSettings();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a metadata change in the panel
|
||||
* @param metadata
|
||||
* Post data to the panel
|
||||
* @param msg
|
||||
*/
|
||||
public pushMetadata(metadata: any) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const filePath = window.activeTextEditor?.document.uri.fsPath;
|
||||
const commaSeparated = Settings.get<string[]>(SETTING_COMMA_SEPARATED_FIELDS);
|
||||
const contentTypes = Settings.get<string>(SETTING_TAXONOMY_CONTENT_TYPES);
|
||||
|
||||
const articleDetails = this.getArticleDetails();
|
||||
|
||||
if (articleDetails) {
|
||||
metadata.articleDetails = articleDetails;
|
||||
}
|
||||
|
||||
let updatedMetadata = Object.assign({}, metadata);
|
||||
if (commaSeparated) {
|
||||
for (const key of commaSeparated) {
|
||||
if (updatedMetadata[key] && typeof updatedMetadata[key] === "string") {
|
||||
updatedMetadata[key] = updatedMetadata[key].split(",").map((s: string) => s.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const keys = Object.keys(updatedMetadata);
|
||||
if (keys.length > 0) {
|
||||
updatedMetadata.filePath = filePath;
|
||||
}
|
||||
|
||||
if (keys.length > 0 && contentTypes && wsFolder) {
|
||||
// Get the current content type
|
||||
const contentType = ArticleHelper.getContentType(updatedMetadata);
|
||||
if (contentType) {
|
||||
this.processImageFields(updatedMetadata, contentType.fields)
|
||||
}
|
||||
}
|
||||
|
||||
// Check slug
|
||||
if (!updatedMetadata[DefaultFields.Slug]) {
|
||||
const slug = Article.getSlug();
|
||||
|
||||
if (slug) {
|
||||
updatedMetadata[DefaultFields.Slug] = slug;
|
||||
}
|
||||
}
|
||||
|
||||
this.postWebviewMessage({ command: Command.metadata, data: {
|
||||
...updatedMetadata
|
||||
}});
|
||||
public sendMessage(msg: { command: Command, data?: any }) {
|
||||
this.panel?.webview?.postMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,9 +116,9 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
*/
|
||||
public triggerInputFocus(tagType: TagType) {
|
||||
if (tagType === TagType.tags) {
|
||||
this.postWebviewMessage({ command: Command.focusOnTags });
|
||||
this.sendMessage({ command: Command.focusOnTags });
|
||||
} else {
|
||||
this.postWebviewMessage({ command: Command.focusOnCategories });
|
||||
this.sendMessage({ command: Command.focusOnCategories });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,396 +126,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
* Trigger all sections to close
|
||||
*/
|
||||
public collapseAll() {
|
||||
this.postWebviewMessage({ command: Command.closeSections });
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the metadata of the article
|
||||
*/
|
||||
public async updateMetadata({field, parents, value }: { field: string, value: any, parents?: string[], fieldData?: { multiple: boolean, value: string[] } }) {
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article.data);
|
||||
const dateFields = contentType.fields.filter((field) => field.type === "datetime");
|
||||
const imageFields = contentType.fields.filter((field) => field.type === "image" && field.multiple);
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = article.data;
|
||||
for (const parent of parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
for (const dateField of dateFields) {
|
||||
if ((field === dateField.name) && value) {
|
||||
parentObj[field] = Article.formatDate(new Date(value));
|
||||
} else if (!imageFields.find(f => f.name === field)) {
|
||||
// Only override the field data if it is not an multiselect image field
|
||||
parentObj[field] = value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const imageField of imageFields) {
|
||||
if (field === imageField.name) {
|
||||
// If value is an array, it means it comes from the explorer view itself (deletion)
|
||||
if (Array.isArray(value)) {
|
||||
parentObj[field] = value || [];
|
||||
} else { // Otherwise it is coming from the media dashboard (addition)
|
||||
let fieldValue = parentObj[field];
|
||||
if (fieldValue && !Array.isArray(fieldValue)) {
|
||||
fieldValue = [fieldValue];
|
||||
}
|
||||
const crntData = Object.assign([], fieldValue);
|
||||
const allRelPaths = [...(crntData || []), value];
|
||||
parentObj[field] = [...new Set(allRelPaths)].filter(f => f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArticleHelper.update(editor, article);
|
||||
this.pushMetadata(article.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a terminal and run the passed command
|
||||
* @param command
|
||||
*/
|
||||
private openTerminalWithCommand(command: string) {
|
||||
if (command) {
|
||||
let terminal = window.activeTerminal;
|
||||
|
||||
if (!terminal || (terminal && terminal.state.isInteractedWith === true)) {
|
||||
terminal = window.createTerminal({
|
||||
name: `Starting local server`,
|
||||
iconPath: new ThemeIcon('server-environment'),
|
||||
message: `Starting local server`,
|
||||
});
|
||||
}
|
||||
|
||||
if (terminal) {
|
||||
terminal.sendText(command);
|
||||
terminal.show(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a custom script
|
||||
* @param msg
|
||||
*/
|
||||
private runCustomScript(msg: { command: string, data: any}) {
|
||||
const scripts: ICustomScript[] | undefined = Settings.get(SETTING_CUSTOM_SCRIPTS);
|
||||
|
||||
if (msg?.data?.title && msg?.data?.script && scripts) {
|
||||
const customScript = scripts.find((s: ICustomScript) => s.title === msg.data.title);
|
||||
if (customScript?.script && customScript?.title) {
|
||||
CustomScript.run(customScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the media selection
|
||||
*/
|
||||
public async getMediaSelection() {
|
||||
this.postWebviewMessage({
|
||||
command: Command.mediaSelectionData,
|
||||
data: Dashboard.viewData
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the extension settings
|
||||
*/
|
||||
public async getSettings() {
|
||||
this.postWebviewMessage({
|
||||
command: Command.settings,
|
||||
data: {
|
||||
seo: {
|
||||
title: Settings.get(SETTING_SEO_TITLE_LENGTH) as number || -1,
|
||||
slug: Settings.get(SETTING_SEO_SLUG_LENGTH) as number || -1,
|
||||
description: Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1,
|
||||
content: Settings.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number || -1,
|
||||
descriptionField: Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string || DefaultFields.Description
|
||||
},
|
||||
slug: {
|
||||
prefix: Settings.get(SETTING_SLUG_PREFIX) || "",
|
||||
suffix: Settings.get(SETTING_SLUG_SUFFIX) || "",
|
||||
updateFileName: !!Settings.get<boolean>(SETTING_SLUG_UPDATE_FILE_NAME),
|
||||
},
|
||||
date: {
|
||||
format: Settings.get(SETTING_DATE_FORMAT)
|
||||
},
|
||||
tags: Settings.get(SETTING_TAXONOMY_TAGS, true) || [],
|
||||
categories: Settings.get(SETTING_TAXONOMY_CATEGORIES, true) || [],
|
||||
customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [],
|
||||
freeform: Settings.get(SETTING_PANEL_FREEFORM),
|
||||
scripts: (Settings.get<ICustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type === ScriptType.Content || !s.type),
|
||||
isInitialized: await Template.isInitialized(),
|
||||
modifiedDateUpdate: Settings.get(SETTING_AUTO_UPDATE_DATE) || false,
|
||||
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
|
||||
fmHighlighting: Settings.get(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT),
|
||||
preview: Preview.getSettings(),
|
||||
commaSeparatedFields: Settings.get(SETTING_COMMA_SEPARATED_FIELDS) || [],
|
||||
contentTypes: Settings.get(SETTING_TAXONOMY_CONTENT_TYPES) || [],
|
||||
dashboardViewData: Dashboard.viewData,
|
||||
draftField: Settings.get<DraftField>(SETTINGS_CONTENT_DRAFT_FIELD),
|
||||
isBacker: await Extension.getInstance().getState<boolean | undefined>(CONTEXT.backer, 'global'),
|
||||
framework: Settings.get<string>(SETTINGS_FRAMEWORK_ID),
|
||||
commands: {
|
||||
start: Settings.get<string>(SETTINGS_FRAMEWORK_START)
|
||||
},
|
||||
dataTypes: Settings.get<DataType[]>(SETTINGS_DATA_TYPES),
|
||||
} as PanelSettings
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the information about the registered folders and its files
|
||||
*/
|
||||
public async getFoldersAndFiles() {
|
||||
this.postWebviewMessage({
|
||||
command: Command.folderInfo,
|
||||
data: await Folders.getInfo(FILE_LIMIT) || null
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the image fields in the content type
|
||||
* @param updatedMetadata
|
||||
* @param fields
|
||||
* @param parents
|
||||
*/
|
||||
private processImageFields(updatedMetadata: any, fields: Field[], parents: string[] = []) {
|
||||
const imageFields = fields.filter((field) => field.type === "image");
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = updatedMetadata;
|
||||
for (const parent of parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
// Process image fields
|
||||
if (parentObj) {
|
||||
for (const field of imageFields) {
|
||||
if (parentObj[field.name]) {
|
||||
const imageData = ImageHelper.allRelToAbs(field, parentObj[field.name])
|
||||
|
||||
if (imageData) {
|
||||
if (field.multiple && imageData instanceof Array) {
|
||||
const preview = imageData.map(preview => preview && preview.absPath ? ({
|
||||
...preview,
|
||||
webviewUrl: this.panel?.webview.asWebviewUri(preview.absPath).toString()
|
||||
}) : null);
|
||||
|
||||
parentObj[field.name] = preview || [];
|
||||
} else if (!field.multiple && !Array.isArray(imageData) && imageData.absPath) {
|
||||
const preview = this.panel?.webview.asWebviewUri(imageData.absPath);
|
||||
parentObj[field.name] = {
|
||||
...imageData,
|
||||
webviewUrl: preview ? preview.toString() : null
|
||||
};
|
||||
}
|
||||
} else {
|
||||
parentObj[field.name] = field.multiple ? [] : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are sub-fields to process
|
||||
const subFields = fields.filter((field) => field.type === "fields");
|
||||
if (subFields?.length > 0) {
|
||||
for (const field of subFields) {
|
||||
this.processImageFields(updatedMetadata, field.fields || [], [...parents, field.name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the file its front matter
|
||||
*/
|
||||
private getFileData() {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article?.data) {
|
||||
this.pushMetadata(article!.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tags in the current document
|
||||
* @param tagType
|
||||
* @param values
|
||||
*/
|
||||
private updateTags(tagType: TagType, values: string[], parents: string[]) {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article && article.data) {
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = article.data;
|
||||
for (const parent of parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
parentObj[tagType.toLowerCase()] = values || [];
|
||||
ArticleHelper.update(editor, article);
|
||||
this.pushMetadata(article!.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tags in the current document
|
||||
* @param data
|
||||
*/
|
||||
private updateCustomTaxonomy(data: CustomTaxonomyData) {
|
||||
if (!data?.id || !data?.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article && article.data) {
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = article.data;
|
||||
for (const parent of data.parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
parentObj[data.name] = data.options || [];
|
||||
ArticleHelper.update(editor, article);
|
||||
this.pushMetadata(article!.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to the settings
|
||||
* @param data
|
||||
*/
|
||||
private async addCustomTaxonomy(data: CustomTaxonomyData) {
|
||||
if (!data?.id || !data?.option) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Settings.updateCustomTaxonomy(data.id, data.option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to the settings
|
||||
* @param tagType
|
||||
* @param value
|
||||
*/
|
||||
private async addTags(tagType: TagType, value: string) {
|
||||
if (value) {
|
||||
let options = tagType === TagType.tags ? Settings.get<string[]>(SETTING_TAXONOMY_TAGS, true) : Settings.get<string[]>(SETTING_TAXONOMY_CATEGORIES, true);
|
||||
|
||||
if (!options) {
|
||||
options = [];
|
||||
}
|
||||
|
||||
options.push(value);
|
||||
const taxType = tagType === TagType.tags ? TaxonomyType.Tag : TaxonomyType.Category;
|
||||
await Settings.updateTaxonomy(taxType, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get article details
|
||||
*/
|
||||
private getArticleDetails() {
|
||||
const baseUrl = Settings.get<string>(SETTING_SITE_BASEURL);
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ArticleHelper.isMarkdownFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
|
||||
if (article && article.content) {
|
||||
let content = article.content;
|
||||
content = content.replace(/({{(.*?)}})/g, ''); // remove hugo shortcodes
|
||||
|
||||
const mdTree = fromMarkdown(content);
|
||||
const elms: Parent[] | Link[] = this.getAllElms(mdTree);
|
||||
|
||||
const headings = elms.filter(node => node.type === 'heading');
|
||||
const paragraphs = elms.filter(node => node.type === 'paragraph').length;
|
||||
const images = elms.filter(node => node.type === 'image').length;
|
||||
const links: string[] = elms.filter(node => node.type === 'link').map(node => (node as Link).url);
|
||||
|
||||
const internalLinks = links.filter(link => !link.startsWith('http') || (baseUrl && link.toLowerCase().includes((baseUrl || "").toLowerCase()))).length;
|
||||
let externalLinks = links.filter(link => link.startsWith('http'));
|
||||
if (baseUrl) {
|
||||
externalLinks = externalLinks.filter(link => !link.toLowerCase().includes(baseUrl.toLowerCase()));
|
||||
}
|
||||
|
||||
const headers = [];
|
||||
for (const header of headings) {
|
||||
const text = header?.children?.filter((node: any) => node.type === 'text').map((node: any) => node.value).join(" ");
|
||||
if (text) {
|
||||
headers.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
const wordCount = this.wordCount(0, mdTree);
|
||||
|
||||
return {
|
||||
headings: headings.length,
|
||||
headingsText: headers,
|
||||
paragraphs,
|
||||
images,
|
||||
internalLinks,
|
||||
externalLinks: externalLinks.length,
|
||||
wordCount,
|
||||
content: article.content
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private getAllElms(node: Content | any, allElms?: any[]): any[] {
|
||||
if (!allElms) {
|
||||
allElms = [];
|
||||
}
|
||||
|
||||
if (node.children?.length > 0) {
|
||||
for (const child of node.children) {
|
||||
allElms.push(Object.assign({}, child));
|
||||
this.getAllElms(child, allElms);
|
||||
}
|
||||
}
|
||||
|
||||
return allElms;
|
||||
this.sendMessage({ command: Command.closeSections });
|
||||
}
|
||||
|
||||
private counts(acc: any, node: any) {
|
||||
@@ -695,69 +140,6 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the word count for the current document
|
||||
*/
|
||||
private wordCount(count: number, node: Content | any) {
|
||||
if (node.type === "text") {
|
||||
return count + node.value.split(" ").length;
|
||||
} else {
|
||||
return (node.children || []).reduce((childCount: number, childNode: any) => this.wordCount(childCount, childNode), count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the writing settings
|
||||
*/
|
||||
private async toggleWritingSettings() {
|
||||
const config = workspace.getConfiguration("", { languageId: "markdown" });
|
||||
const enabled = this.isWritingSettingsEnabled();
|
||||
|
||||
await config.update("editor.fontSize", enabled ? undefined : 14, false, true);
|
||||
await config.update("editor.lineHeight", enabled ? undefined : 26, false, true);
|
||||
await config.update("editor.wordWrap", enabled ? undefined : "wordWrapColumn", false, true);
|
||||
await config.update("editor.wordWrapColumn", enabled ? undefined : 64, false, true);
|
||||
await config.update("editor.lineNumbers", enabled ? undefined : "off", false, true);
|
||||
await config.update("editor.quickSuggestions", enabled ? undefined : false, false, true);
|
||||
await config.update("editor.minimap.enabled", enabled ? undefined : false, false, true);
|
||||
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the writing settings are enabled
|
||||
*/
|
||||
private isWritingSettingsEnabled() {
|
||||
const config = workspace.getConfiguration("", { languageId: "markdown" });
|
||||
|
||||
const fontSize = config.get("editor.fontSize");
|
||||
const lineHeight = config.get("editor.lineHeight");
|
||||
const wordWrap = config.get("editor.wordWrap");
|
||||
const wordWrapColumn = config.get("editor.wordWrapColumn");
|
||||
const lineNumbers = config.get("editor.lineNumbers");
|
||||
const quickSuggestions = config.get<boolean>("editor.quickSuggestions");
|
||||
|
||||
return fontSize && lineHeight && wordWrap && wordWrapColumn && lineNumbers && quickSuggestions !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a setting and refreshes the retrieved settings
|
||||
* @param setting
|
||||
* @param value
|
||||
*/
|
||||
private async updateSetting(setting: string, value: any) {
|
||||
await Settings.update(setting, value);
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Post data to the panel
|
||||
* @param msg
|
||||
*/
|
||||
private postWebviewMessage(msg: { command: Command, data?: any }) {
|
||||
this.panel?.webview?.postMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the webview HTML contents
|
||||
* @param webView
|
||||
|
||||
+5
-5
@@ -19,8 +19,9 @@ import { Content } from './commands/Content';
|
||||
import ContentProvider from './providers/ContentProvider';
|
||||
import { Wysiwyg } from './commands/Wysiwyg';
|
||||
import { Diagnostics } from './commands/Diagnostics';
|
||||
import { PagesListener } from './listeners';
|
||||
import { PagesListener } from './listeners/dashboard';
|
||||
import { Backers } from './commands/Backers';
|
||||
import { DataListener, SettingsListener } from './listeners/panel';
|
||||
|
||||
let frontMatterStatusBar: vscode.StatusBarItem;
|
||||
let statusDebouncer: { (fnc: any, time: number): void; };
|
||||
@@ -171,9 +172,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
Template.init();
|
||||
Preview.init();
|
||||
|
||||
const exView = ExplorerView.getInstance();
|
||||
exView.getSettings();
|
||||
exView.getFoldersAndFiles();
|
||||
SettingsListener.getSettings();
|
||||
DataListener.getFoldersAndFiles();
|
||||
MarkdownFoldingProvider.triggerHighlighting();
|
||||
});
|
||||
|
||||
@@ -197,7 +197,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
subscriptions.push(vscode.workspace.onDidSaveTextDocument((doc: vscode.TextDocument) => {
|
||||
if (doc.languageId === 'markdown') {
|
||||
// Optimize the list of recently changed files
|
||||
ExplorerView.getInstance().getFoldersAndFiles();
|
||||
DataListener.getFoldersAndFiles();
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { MarkdownFoldingProvider } from './../providers/MarkdownFoldingProvider'
|
||||
import { DEFAULT_CONTENT_TYPE, DEFAULT_CONTENT_TYPE_NAME } from './../constants/ContentType';
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from "fs";
|
||||
import { DefaultFields, SETTINGS_CONTENT_DEFAULT_FILETYPE, SETTINGS_CONTENT_PLACEHOLDERS, SETTINGS_CONTENT_SUPPORTED_FILETYPES, SETTING_COMMA_SEPARATED_FIELDS, SETTING_DATE_FIELD, SETTING_DATE_FORMAT, SETTING_INDENT_ARRAY, SETTING_REMOVE_QUOTES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TEMPLATES_PREFIX } from '../constants';
|
||||
import { DefaultFields, SETTINGS_CONTENT_DEFAULT_FILETYPE, SETTINGS_CONTENT_PLACEHOLDERS, SETTINGS_CONTENT_SUPPORTED_FILETYPES, SETTING_COMMA_SEPARATED_FIELDS, SETTING_DATE_FIELD, SETTING_DATE_FORMAT, SETTING_INDENT_ARRAY, SETTING_REMOVE_QUOTES, SETTING_SITE_BASEURL, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TEMPLATES_PREFIX } from '../constants';
|
||||
import { DumpOptions } from 'js-yaml';
|
||||
import { FrontMatterParser, ParsedFrontMatter } from '../parsers';
|
||||
import { Extension, Logger, Settings, SlugHelper } from '.';
|
||||
@@ -17,6 +17,9 @@ import { ContentType } from '../models';
|
||||
import { DateHelper } from './DateHelper';
|
||||
import { DiagnosticSeverity, Position, window, Range } from 'vscode';
|
||||
import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown';
|
||||
import { Link, Parent } from 'mdast-util-from-markdown/lib';
|
||||
import { Content } from 'mdast';
|
||||
|
||||
export class ArticleHelper {
|
||||
private static notifiedFiles: string[] = [];
|
||||
@@ -347,6 +350,98 @@ export class ArticleHelper {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the details of the current article
|
||||
* @returns
|
||||
*/
|
||||
public static getDetails() {
|
||||
const baseUrl = Settings.get<string>(SETTING_SITE_BASEURL);
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ArticleHelper.isMarkdownFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
|
||||
if (article && article.content) {
|
||||
let content = article.content;
|
||||
content = content.replace(/({{(.*?)}})/g, ''); // remove hugo shortcodes
|
||||
|
||||
const mdTree = fromMarkdown(content);
|
||||
const elms: Parent[] | Link[] = this.getAllElms(mdTree);
|
||||
|
||||
const headings = elms.filter(node => node.type === 'heading');
|
||||
const paragraphs = elms.filter(node => node.type === 'paragraph').length;
|
||||
const images = elms.filter(node => node.type === 'image').length;
|
||||
const links: string[] = elms.filter(node => node.type === 'link').map(node => (node as Link).url);
|
||||
|
||||
const internalLinks = links.filter(link => !link.startsWith('http') || (baseUrl && link.toLowerCase().includes((baseUrl || "").toLowerCase()))).length;
|
||||
let externalLinks = links.filter(link => link.startsWith('http'));
|
||||
if (baseUrl) {
|
||||
externalLinks = externalLinks.filter(link => !link.toLowerCase().includes(baseUrl.toLowerCase()));
|
||||
}
|
||||
|
||||
const headers = [];
|
||||
for (const header of headings) {
|
||||
const text = header?.children?.filter((node: any) => node.type === 'text').map((node: any) => node.value).join(" ");
|
||||
if (text) {
|
||||
headers.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
const wordCount = this.wordCount(0, mdTree);
|
||||
|
||||
return {
|
||||
headings: headings.length,
|
||||
headingsText: headers,
|
||||
paragraphs,
|
||||
images,
|
||||
internalLinks,
|
||||
externalLinks: externalLinks.length,
|
||||
wordCount,
|
||||
content: article.content
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the elements from the markdown content
|
||||
* @param node
|
||||
* @param allElms
|
||||
* @returns
|
||||
*/
|
||||
private static getAllElms(node: Content | any, allElms?: any[]): any[] {
|
||||
if (!allElms) {
|
||||
allElms = [];
|
||||
}
|
||||
|
||||
if (node.children?.length > 0) {
|
||||
for (const child of node.children) {
|
||||
allElms.push(Object.assign({}, child));
|
||||
this.getAllElms(child, allElms);
|
||||
}
|
||||
}
|
||||
|
||||
return allElms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the word count for the current document
|
||||
*/
|
||||
private static wordCount(count: number, node: Content | any) {
|
||||
if (node.type === "text") {
|
||||
return count + node.value.split(" ").length;
|
||||
} else {
|
||||
return (node.children || []).reduce((childCount: number, childNode: any) => this.wordCount(childCount, childNode), count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a markdown file and its front matter
|
||||
* @param fileContents
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PagesListener } from './../listeners/PagesListener';
|
||||
import { PagesListener } from './../listeners/dashboard';
|
||||
import { ArticleHelper, Settings } from ".";
|
||||
import { SETTINGS_CONTENT_DRAFT_FIELD, SETTING_TAXONOMY_CONTENT_TYPES, TelemetryEvent } from "../constants";
|
||||
import { ContentType as IContentType, DraftField, Field } from '../models';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ExplorerView } from './../explorerView/ExplorerView';
|
||||
import { Uri, window } from 'vscode';
|
||||
import { dirname, join } from "path";
|
||||
import { Field } from '../models';
|
||||
@@ -78,4 +79,57 @@ export class ImageHelper {
|
||||
}
|
||||
return relPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the image fields in the content type
|
||||
* @param updatedMetadata
|
||||
* @param fields
|
||||
* @param parents
|
||||
*/
|
||||
public static processImageFields(updatedMetadata: any, fields: Field[], parents: string[] = []) {
|
||||
const imageFields = fields.filter((field) => field.type === "image");
|
||||
const panel = ExplorerView.getInstance();
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = updatedMetadata;
|
||||
for (const parent of parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
// Process image fields
|
||||
if (parentObj) {
|
||||
for (const field of imageFields) {
|
||||
if (parentObj[field.name]) {
|
||||
const imageData = ImageHelper.allRelToAbs(field, parentObj[field.name])
|
||||
|
||||
if (imageData) {
|
||||
if (field.multiple && imageData instanceof Array) {
|
||||
const preview = imageData.map(preview => preview && preview.absPath ? ({
|
||||
...preview,
|
||||
webviewUrl: panel.getWebview()?.asWebviewUri(preview.absPath).toString()
|
||||
}) : null);
|
||||
|
||||
parentObj[field.name] = preview || [];
|
||||
} else if (!field.multiple && !Array.isArray(imageData) && imageData.absPath) {
|
||||
const preview = panel.getWebview()?.asWebviewUri(imageData.absPath);
|
||||
parentObj[field.name] = {
|
||||
...imageData,
|
||||
webviewUrl: preview ? preview.toString() : null
|
||||
};
|
||||
}
|
||||
} else {
|
||||
parentObj[field.name] = field.multiple ? [] : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are sub-fields to process
|
||||
const subFields = fields.filter((field) => field.type === "fields");
|
||||
if (subFields?.length > 0) {
|
||||
for (const field of subFields) {
|
||||
this.processImageFields(updatedMetadata, field.fields || [], [...parents, field.name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import imageSize from "image-size";
|
||||
import { EditorHelper } from "@estruyf/vscode";
|
||||
import { ExplorerView } from "../explorerView/ExplorerView";
|
||||
import { SortOption } from "../dashboardWebView/constants/SortOption";
|
||||
import { DataListener, MediaListener } from "../listeners/panel";
|
||||
|
||||
|
||||
export class MediaHelpers {
|
||||
@@ -301,10 +302,10 @@ export class MediaHelpers {
|
||||
}
|
||||
});
|
||||
}
|
||||
panel.getMediaSelection();
|
||||
MediaListener.getMediaSelection();
|
||||
} else {
|
||||
panel.getMediaSelection();
|
||||
panel.updateMetadata({field: data.fieldName, value: data.image, parents: data.parents });
|
||||
MediaListener.getMediaSelection();
|
||||
DataListener.updateMetadata({field: data.fieldName, value: data.image, parents: data.parents });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { workspace } from "vscode"
|
||||
import { Extension, Settings } from "."
|
||||
import { Dashboard } from "../commands/Dashboard"
|
||||
import { Preview } from "../commands/Preview"
|
||||
import { Template } from "../commands/Template"
|
||||
import { CONTEXT, DefaultFields, SETTINGS_CONTENT_DRAFT_FIELD, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTINGS_DATA_TYPES, SETTINGS_FRAMEWORK_ID, SETTINGS_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_COMMA_SEPARATED_FIELDS, SETTING_CUSTOM_SCRIPTS, SETTING_DATE_FORMAT, SETTING_PANEL_FREEFORM, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_SLUG_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TAXONOMY_TAGS } from "../constants"
|
||||
import { CustomScript, DataType, DraftField, PanelSettings as IPanelSettings, ScriptType } from "../models"
|
||||
|
||||
export class PanelSettings {
|
||||
|
||||
public static async get(): Promise<IPanelSettings> {
|
||||
return {
|
||||
seo: {
|
||||
title: Settings.get(SETTING_SEO_TITLE_LENGTH) as number || -1,
|
||||
slug: Settings.get(SETTING_SEO_SLUG_LENGTH) as number || -1,
|
||||
description: Settings.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1,
|
||||
content: Settings.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number || -1,
|
||||
descriptionField: Settings.get(SETTING_SEO_DESCRIPTION_FIELD) as string || DefaultFields.Description
|
||||
},
|
||||
slug: {
|
||||
prefix: Settings.get(SETTING_SLUG_PREFIX) || "",
|
||||
suffix: Settings.get(SETTING_SLUG_SUFFIX) || "",
|
||||
updateFileName: !!Settings.get<boolean>(SETTING_SLUG_UPDATE_FILE_NAME),
|
||||
},
|
||||
date: {
|
||||
format: Settings.get<string>(SETTING_DATE_FORMAT) || ""
|
||||
},
|
||||
tags: Settings.get(SETTING_TAXONOMY_TAGS, true) || [],
|
||||
categories: Settings.get(SETTING_TAXONOMY_CATEGORIES, true) || [],
|
||||
customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [],
|
||||
freeform: Settings.get(SETTING_PANEL_FREEFORM),
|
||||
scripts: (Settings.get<CustomScript[]>(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type === ScriptType.Content || !s.type),
|
||||
isInitialized: await Template.isInitialized(),
|
||||
modifiedDateUpdate: Settings.get(SETTING_AUTO_UPDATE_DATE) || false,
|
||||
writingSettingsEnabled: this.isWritingSettingsEnabled() || false,
|
||||
fmHighlighting: Settings.get(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT),
|
||||
preview: Preview.getSettings(),
|
||||
commaSeparatedFields: Settings.get(SETTING_COMMA_SEPARATED_FIELDS) || [],
|
||||
contentTypes: Settings.get(SETTING_TAXONOMY_CONTENT_TYPES) || [],
|
||||
dashboardViewData: Dashboard.viewData,
|
||||
draftField: Settings.get<DraftField>(SETTINGS_CONTENT_DRAFT_FIELD),
|
||||
isBacker: await Extension.getInstance().getState<boolean | undefined>(CONTEXT.backer, 'global'),
|
||||
framework: Settings.get<string>(SETTINGS_FRAMEWORK_ID),
|
||||
commands: {
|
||||
start: Settings.get<string>(SETTINGS_FRAMEWORK_START)
|
||||
},
|
||||
dataTypes: Settings.get<DataType[]>(SETTINGS_DATA_TYPES),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the writing settings are enabled
|
||||
*/
|
||||
public static isWritingSettingsEnabled(): boolean {
|
||||
const config = workspace.getConfiguration("", { languageId: "markdown" });
|
||||
|
||||
const fontSize = config.get("editor.fontSize");
|
||||
const lineHeight = config.get("editor.lineHeight");
|
||||
const wordWrap = config.get("editor.wordWrap");
|
||||
const wordWrapColumn = config.get("editor.wordWrapColumn");
|
||||
const lineNumbers = config.get("editor.lineNumbers");
|
||||
const quickSuggestions = config.get<boolean>("editor.quickSuggestions");
|
||||
|
||||
return( fontSize && lineHeight && wordWrap && wordWrapColumn && lineNumbers && quickSuggestions !== undefined) as boolean;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Dashboard } from "../commands/Dashboard";
|
||||
import { DashboardCommand } from "../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { Logger } from "../helpers/Logger";
|
||||
import { Dashboard } from "../../commands/Dashboard";
|
||||
import { DashboardCommand } from "../../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { Logger } from "../../helpers/Logger";
|
||||
|
||||
|
||||
export abstract class BaseListener {
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Dashboard } from "../commands/Dashboard";
|
||||
import { ExtensionState } from "../constants";
|
||||
import { DashboardCommand } from "../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { Extension } from "../helpers";
|
||||
import { Dashboard } from "../../commands/Dashboard";
|
||||
import { ExtensionState } from "../../constants";
|
||||
import { DashboardCommand } from "../../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { Extension } from "../../helpers";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { DataFile } from './../models/DataFile';
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { DataFile } from './../../models/DataFile';
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
|
||||
import { Folders } from '../../commands/Folders';
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
||||
import { dirname } from 'path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { Logger, Notifications } from '../helpers';
|
||||
import { Logger, Notifications } from '../../helpers';
|
||||
import { commands } from 'vscode';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { commands, env } from "vscode";
|
||||
import { SettingsListener } from ".";
|
||||
import { COMMAND_NAME } from "../constants";
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { CustomScript, Extension } from "../helpers";
|
||||
import { openFileInEditor } from "../helpers/openFileInEditor";
|
||||
import { COMMAND_NAME } from "../../constants";
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { CustomScript, Extension } from "../../helpers";
|
||||
import { openFileInEditor } from "../../helpers/openFileInEditor";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Telemetry } from './../helpers/Telemetry';
|
||||
import { MediaHelpers } from './../helpers/MediaHelpers';
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { Telemetry } from '../../helpers/Telemetry';
|
||||
import { MediaHelpers } from '../../helpers/MediaHelpers';
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
|
||||
import { SortingOption } from '../dashboardWebView/models';
|
||||
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
|
||||
import { SortingOption } from '../../dashboardWebView/models';
|
||||
import { commands, env, Uri } from 'vscode';
|
||||
import { COMMAND_NAME, TelemetryEvent } from '../constants';
|
||||
import { COMMAND_NAME, TelemetryEvent } from '../../constants';
|
||||
import * as os from 'os';
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { isValidFile } from './../helpers/isValidFile';
|
||||
import { isValidFile } from '../../helpers/isValidFile';
|
||||
import { existsSync } from "fs";
|
||||
import { basename, dirname, join } from "path";
|
||||
import { commands, FileSystemWatcher, RelativePattern, Uri, workspace } from "vscode";
|
||||
import { Dashboard } from "../commands/Dashboard";
|
||||
import { Folders } from "../commands/Folders";
|
||||
import { COMMAND_NAME, DefaultFields, SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD } from "../constants";
|
||||
import { DashboardCommand } from "../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { Page } from "../dashboardWebView/models";
|
||||
import { ArticleHelper, Logger, Settings } from "../helpers";
|
||||
import { ContentType } from "../helpers/ContentType";
|
||||
import { DateHelper } from "../helpers/DateHelper";
|
||||
import { Notifications } from "../helpers/Notifications";
|
||||
import { Dashboard } from "../../commands/Dashboard";
|
||||
import { Folders } from "../../commands/Folders";
|
||||
import { COMMAND_NAME, DefaultFields, SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD } from "../../constants";
|
||||
import { DashboardCommand } from "../../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { Page } from "../../dashboardWebView/models";
|
||||
import { ArticleHelper, Logger, Settings } from "../../helpers";
|
||||
import { ContentType } from "../../helpers/ContentType";
|
||||
import { DateHelper } from "../../helpers/DateHelper";
|
||||
import { Notifications } from "../../helpers/Notifications";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { SETTINGS_CONTENT_STATIC_FOLDER, SETTINGS_FRAMEWORK_ID } from "../constants";
|
||||
import { DashboardCommand } from "../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { DashboardSettings, Settings } from "../helpers";
|
||||
import { FrameworkDetector } from "../helpers/FrameworkDetector";
|
||||
import { Framework } from "../models";
|
||||
import { SETTINGS_CONTENT_STATIC_FOLDER, SETTINGS_FRAMEWORK_ID } from "../../constants";
|
||||
import { DashboardCommand } from "../../dashboardWebView/DashboardCommand";
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { DashboardSettings, Settings } from "../../helpers";
|
||||
import { FrameworkDetector } from "../../helpers/FrameworkDetector";
|
||||
import { Framework } from "../../models";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
|
||||
import { Telemetry } from "../helpers/Telemetry";
|
||||
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
|
||||
import { Telemetry } from "../../helpers/Telemetry";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './BaseListener';
|
||||
export * from './DashboardListener';
|
||||
export * from './DataListener';
|
||||
export * from './ExtensionListener';
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Article } from "../../commands";
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
export class ArticleListener extends BaseListener {
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: any, data: any }) {
|
||||
super.process(msg);
|
||||
|
||||
switch(msg.command) {
|
||||
case CommandToCode.updateSlug:
|
||||
Article.generateSlug();
|
||||
break;
|
||||
case CommandToCode.updateLastMod:
|
||||
Article.setLastModifiedDate();
|
||||
break;
|
||||
case CommandToCode.publish:
|
||||
Article.toggleDraft();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Extension } from './../../helpers/Extension';
|
||||
import { ExplorerView } from './../../explorerView/ExplorerView';
|
||||
import { Logger } from "../../helpers";
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { Command } from '../../panelWebView/Command';
|
||||
|
||||
|
||||
export abstract class BaseListener {
|
||||
|
||||
public static process(msg: { command: CommandToCode, data: any }) {}
|
||||
|
||||
/**
|
||||
* Send a message to the webview
|
||||
* @param command
|
||||
* @param data
|
||||
*/
|
||||
public static sendMsg(command: Command, data: any) {
|
||||
Logger.info(`Sending message to panel: ${command}`);
|
||||
|
||||
const extPath = Extension.getInstance().extensionPath;
|
||||
const panel = ExplorerView.getInstance(extPath);
|
||||
|
||||
panel.sendMessage({
|
||||
command,
|
||||
data
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
import { ImageHelper } from './../../helpers/ImageHelper';
|
||||
import { Folders } from "../../commands/Folders";
|
||||
import { Command } from "../../panelWebView/Command";
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
import { commands, ThemeIcon, window } from 'vscode';
|
||||
import { ArticleHelper, Settings } from "../../helpers";
|
||||
import { COMMAND_NAME, DefaultFields, SETTING_COMMA_SEPARATED_FIELDS, SETTING_TAXONOMY_CONTENT_TYPES } from "../../constants";
|
||||
import { Article } from '../../commands';
|
||||
|
||||
const FILE_LIMIT = 10;
|
||||
|
||||
export class DataListener extends BaseListener {
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: CommandToCode, data: any }) {
|
||||
super.process(msg);
|
||||
|
||||
switch(msg.command) {
|
||||
case CommandToCode.getData:
|
||||
this.getFoldersAndFiles();
|
||||
this.getFileData();
|
||||
break;
|
||||
case CommandToCode.createContent:
|
||||
commands.executeCommand(COMMAND_NAME.createContent);
|
||||
break;
|
||||
case CommandToCode.createTemplate:
|
||||
commands.executeCommand(COMMAND_NAME.createTemplate);
|
||||
break;
|
||||
case CommandToCode.updateMetadata:
|
||||
this.updateMetadata(msg.data);
|
||||
break;
|
||||
case CommandToCode.frameworkCommand:
|
||||
this.openTerminalWithCommand(msg.data.command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the information about the registered folders and its files
|
||||
*/
|
||||
public static async getFoldersAndFiles() {
|
||||
const folders = await Folders.getInfo(FILE_LIMIT) || null;
|
||||
|
||||
this.sendMsg(Command.folderInfo, folders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a metadata change in the panel
|
||||
* @param metadata
|
||||
*/
|
||||
public static pushMetadata(metadata: any) {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const filePath = window.activeTextEditor?.document.uri.fsPath;
|
||||
const commaSeparated = Settings.get<string[]>(SETTING_COMMA_SEPARATED_FIELDS);
|
||||
const contentTypes = Settings.get<string>(SETTING_TAXONOMY_CONTENT_TYPES);
|
||||
|
||||
const articleDetails = ArticleHelper.getDetails();
|
||||
|
||||
if (articleDetails) {
|
||||
metadata.articleDetails = articleDetails;
|
||||
}
|
||||
|
||||
let updatedMetadata = Object.assign({}, metadata);
|
||||
if (commaSeparated) {
|
||||
for (const key of commaSeparated) {
|
||||
if (updatedMetadata[key] && typeof updatedMetadata[key] === "string") {
|
||||
updatedMetadata[key] = updatedMetadata[key].split(",").map((s: string) => s.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const keys = Object.keys(updatedMetadata);
|
||||
if (keys.length > 0) {
|
||||
updatedMetadata.filePath = filePath;
|
||||
}
|
||||
|
||||
if (keys.length > 0 && contentTypes && wsFolder) {
|
||||
// Get the current content type
|
||||
const contentType = ArticleHelper.getContentType(updatedMetadata);
|
||||
if (contentType) {
|
||||
ImageHelper.processImageFields(updatedMetadata, contentType.fields)
|
||||
}
|
||||
}
|
||||
|
||||
// Check slug
|
||||
if (!updatedMetadata[DefaultFields.Slug]) {
|
||||
const slug = Article.getSlug();
|
||||
|
||||
if (slug) {
|
||||
updatedMetadata[DefaultFields.Slug] = slug;
|
||||
}
|
||||
}
|
||||
|
||||
this.sendMsg(Command.metadata, updatedMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the metadata of the article
|
||||
*/
|
||||
public static async updateMetadata({field, parents, value }: { field: string, value: any, parents?: string[], fieldData?: { multiple: boolean, value: string[] } }) {
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (!article) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = ArticleHelper.getContentType(article.data);
|
||||
const dateFields = contentType.fields.filter((field) => field.type === "datetime");
|
||||
const imageFields = contentType.fields.filter((field) => field.type === "image" && field.multiple);
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = article.data;
|
||||
for (const parent of parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
for (const dateField of dateFields) {
|
||||
if ((field === dateField.name) && value) {
|
||||
parentObj[field] = Article.formatDate(new Date(value));
|
||||
} else if (!imageFields.find(f => f.name === field)) {
|
||||
// Only override the field data if it is not an multiselect image field
|
||||
parentObj[field] = value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const imageField of imageFields) {
|
||||
if (field === imageField.name) {
|
||||
// If value is an array, it means it comes from the explorer view itself (deletion)
|
||||
if (Array.isArray(value)) {
|
||||
parentObj[field] = value || [];
|
||||
} else { // Otherwise it is coming from the media dashboard (addition)
|
||||
let fieldValue = parentObj[field];
|
||||
if (fieldValue && !Array.isArray(fieldValue)) {
|
||||
fieldValue = [fieldValue];
|
||||
}
|
||||
const crntData = Object.assign([], fieldValue);
|
||||
const allRelPaths = [...(crntData || []), value];
|
||||
parentObj[field] = [...new Set(allRelPaths)].filter(f => f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArticleHelper.update(editor, article);
|
||||
this.pushMetadata(article.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the file its front matter
|
||||
*/
|
||||
public static async getFileData() {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article?.data) {
|
||||
this.pushMetadata(article!.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a terminal and run the passed command
|
||||
* @param command
|
||||
*/
|
||||
private static openTerminalWithCommand(command: string) {
|
||||
if (command) {
|
||||
let terminal = window.activeTerminal;
|
||||
|
||||
if (!terminal || (terminal && terminal.state.isInteractedWith === true)) {
|
||||
terminal = window.createTerminal({
|
||||
name: `Starting local server`,
|
||||
iconPath: new ThemeIcon('server-environment'),
|
||||
message: `Starting local server`,
|
||||
});
|
||||
}
|
||||
|
||||
if (terminal) {
|
||||
terminal.sendText(command);
|
||||
terminal.show(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
import { commands, env as vscodeEnv } from 'vscode';
|
||||
import * as os from 'os';
|
||||
import { exec } from 'child_process';
|
||||
import { Folders } from "../../commands/Folders";
|
||||
import { COMMAND_NAME } from "../../constants";
|
||||
import { SettingsListener } from ".";
|
||||
import { openFileInEditor } from "../../helpers";
|
||||
|
||||
|
||||
export class ExtensionListener extends BaseListener {
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: any, data: any }) {
|
||||
super.process(msg);
|
||||
|
||||
switch(msg.command) {
|
||||
case CommandToCode.openFile:
|
||||
this.openFile();
|
||||
break;
|
||||
case CommandToCode.openProject:
|
||||
this.openFolder();
|
||||
break;
|
||||
case CommandToCode.openInEditor:
|
||||
openFileInEditor(msg.data);
|
||||
break;
|
||||
case CommandToCode.initProject:
|
||||
this.initialize();
|
||||
break;
|
||||
case CommandToCode.toggleCenterMode:
|
||||
commands.executeCommand(`workbench.action.toggleCenteredLayout`);
|
||||
break;
|
||||
case CommandToCode.openPreview:
|
||||
commands.executeCommand(COMMAND_NAME.preview);
|
||||
break;
|
||||
case CommandToCode.openDashboard:
|
||||
commands.executeCommand(COMMAND_NAME.dashboard);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize project
|
||||
*/
|
||||
private static async initialize() {
|
||||
await commands.executeCommand(COMMAND_NAME.init);
|
||||
SettingsListener.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the file in your explorer
|
||||
*/
|
||||
private static openFile() {
|
||||
if (os.type() === "Linux" && vscodeEnv.remoteName?.toLowerCase() === "wsl") {
|
||||
commands.executeCommand('remote-wsl.revealInExplorer');
|
||||
} else {
|
||||
commands.executeCommand('revealFileInOS');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the project folder
|
||||
*/
|
||||
private static openFolder() {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (wsFolder) {
|
||||
const wsPath = wsFolder.fsPath;
|
||||
if (os.type() === "Darwin") {
|
||||
exec(`open ${wsPath}`);
|
||||
} else if (os.type() === "Windows_NT") {
|
||||
exec(`explorer ${wsPath}`);
|
||||
} else if (os.type() === "Linux" && vscodeEnv.remoteName?.toLowerCase() === "wsl") {
|
||||
exec('explorer.exe `wslpath -w "$PWD"`');
|
||||
} else {
|
||||
exec(`xdg-open ${wsPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { commands } from "vscode";
|
||||
import { Dashboard } from "../../commands/Dashboard";
|
||||
import { COMMAND_NAME } from "../../constants";
|
||||
import { DashboardData } from "../../models";
|
||||
import { Command } from "../../panelWebView/Command";
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
export class MediaListener extends BaseListener {
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: any, data: any }) {
|
||||
super.process(msg);
|
||||
|
||||
switch(msg.command) {
|
||||
case CommandToCode.selectImage:
|
||||
this.selectMedia(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a media file
|
||||
*/
|
||||
private static async selectMedia(msg: { data: any }) {
|
||||
await commands.executeCommand(COMMAND_NAME.dashboard, {
|
||||
type: "media",
|
||||
data: msg.data
|
||||
} as DashboardData);
|
||||
this.getMediaSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the media selection
|
||||
*/
|
||||
public static async getMediaSelection() {
|
||||
this.sendMsg(Command.mediaSelectionData, Dashboard.viewData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { SETTING_CUSTOM_SCRIPTS } from "../../constants";
|
||||
import { CustomScript, Settings } from "../../helpers";
|
||||
import { CustomScript as ICustomScript } from "../../models";
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
export class ScriptListener extends BaseListener {
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: any, data: any }) {
|
||||
super.process(msg);
|
||||
|
||||
switch(msg.command) {
|
||||
case CommandToCode.runCustomScript:
|
||||
this.runCustomScript(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a custom script
|
||||
* @param msg
|
||||
*/
|
||||
private static runCustomScript(msg: { command: string, data: any}) {
|
||||
const scripts: ICustomScript[] | undefined = Settings.get(SETTING_CUSTOM_SCRIPTS);
|
||||
|
||||
if (msg?.data?.title && msg?.data?.script && scripts) {
|
||||
const customScript = scripts.find((s: ICustomScript) => s.title === msg.data.title);
|
||||
if (customScript?.script && customScript?.title) {
|
||||
CustomScript.run(customScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { commands, workspace } from "vscode";
|
||||
import { EXTENSION_BETA_ID, EXTENSION_ID, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTINGS_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_PREVIEW_HOST } from "../../constants";
|
||||
import { Extension, Settings } from "../../helpers";
|
||||
import { PanelSettings } from "../../helpers/PanelSettings";
|
||||
import { Command } from "../../panelWebView/Command";
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
|
||||
|
||||
export class SettingsListener extends BaseListener {
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: CommandToCode, data: any }) {
|
||||
super.process(msg);
|
||||
|
||||
switch(msg.command) {
|
||||
case CommandToCode.getData:
|
||||
this.getSettings();
|
||||
break;
|
||||
case CommandToCode.openSettings:
|
||||
this.openVSCodeSettings();
|
||||
break;
|
||||
case CommandToCode.toggleWritingSettings:
|
||||
this.toggleWritingSettings();
|
||||
break;
|
||||
case CommandToCode.updateModifiedUpdating:
|
||||
this.updateSetting(SETTING_AUTO_UPDATE_DATE, msg.data || false);
|
||||
break;
|
||||
case CommandToCode.updateFmHighlight:
|
||||
this.updateSetting(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, (msg.data !== null && msg.data !== undefined) ? msg.data : false);
|
||||
break;
|
||||
case CommandToCode.updatePreviewUrl:
|
||||
this.updateSetting(SETTING_PREVIEW_HOST, msg.data || "");
|
||||
break;
|
||||
case CommandToCode.updateStartCommand:
|
||||
this.updateSetting(SETTINGS_FRAMEWORK_START, msg.data || "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the extension settings required to render the panel
|
||||
*/
|
||||
public static async getSettings() {
|
||||
const panelSettings = await PanelSettings.get();
|
||||
this.sendMsg(Command.settings, panelSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the settings view of VS Code
|
||||
*/
|
||||
public static openVSCodeSettings() {
|
||||
const isBeta = Extension.getInstance().isBetaVersion();
|
||||
commands.executeCommand('workbench.action.openSettings', `@ext:${isBeta ? EXTENSION_BETA_ID : EXTENSION_ID}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a setting and refreshes the retrieved settings
|
||||
* @param setting
|
||||
* @param value
|
||||
*/
|
||||
private static async updateSetting(setting: string, value: any) {
|
||||
await Settings.update(setting, value);
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the writing settings
|
||||
*/
|
||||
private static async toggleWritingSettings() {
|
||||
const config = workspace.getConfiguration("", { languageId: "markdown" });
|
||||
const enabled = PanelSettings.isWritingSettingsEnabled();
|
||||
|
||||
await config.update("editor.fontSize", enabled ? undefined : 14, false, true);
|
||||
await config.update("editor.lineHeight", enabled ? undefined : 26, false, true);
|
||||
await config.update("editor.wordWrap", enabled ? undefined : "wordWrapColumn", false, true);
|
||||
await config.update("editor.wordWrapColumn", enabled ? undefined : 64, false, true);
|
||||
await config.update("editor.lineNumbers", enabled ? undefined : "off", false, true);
|
||||
await config.update("editor.quickSuggestions", enabled ? undefined : false, false, true);
|
||||
await config.update("editor.minimap.enabled", enabled ? undefined : false, false, true);
|
||||
|
||||
this.getSettings();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import { CommandToCode } from "../../panelWebView/CommandToCode";
|
||||
import { TagType } from "../../panelWebView/TagType";
|
||||
import { BaseListener } from "./BaseListener";
|
||||
import { window } from "vscode";
|
||||
import { ArticleHelper, Settings } from "../../helpers";
|
||||
import { CustomTaxonomyData, TaxonomyType } from "../../models";
|
||||
import { DataListener } from ".";
|
||||
import { SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from "../../constants";
|
||||
|
||||
|
||||
export class TaxonomyListener extends BaseListener {
|
||||
|
||||
/**
|
||||
* Process the messages for the dashboard views
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: any, data: any }) {
|
||||
super.process(msg);
|
||||
|
||||
switch(msg.command) {
|
||||
case CommandToCode.updateTags:
|
||||
this.updateTags(TagType.tags, msg.data?.values || [], msg.data?.parents || []);
|
||||
break;
|
||||
case CommandToCode.updateCategories:
|
||||
this.updateTags(TagType.categories, msg.data?.values || [], msg.data?.parents || []);
|
||||
break;
|
||||
case CommandToCode.updateKeywords:
|
||||
this.updateTags(TagType.keywords, msg.data?.values || [], msg.data?.parents || []);
|
||||
break;
|
||||
case CommandToCode.updateCustomTaxonomy:
|
||||
this.updateCustomTaxonomy(msg.data);
|
||||
break;
|
||||
case CommandToCode.addTagToSettings:
|
||||
this.addTags(TagType.tags, msg.data);
|
||||
break;
|
||||
case CommandToCode.addCategoryToSettings:
|
||||
this.addTags(TagType.categories, msg.data);
|
||||
break;
|
||||
case CommandToCode.addToCustomTaxonomy:
|
||||
this.addCustomTaxonomy(msg.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tags in the current document
|
||||
* @param tagType
|
||||
* @param values
|
||||
*/
|
||||
private static updateTags(tagType: TagType, values: string[], parents: string[]) {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article && article.data) {
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = article.data;
|
||||
for (const parent of parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
parentObj[tagType.toLowerCase()] = values || [];
|
||||
ArticleHelper.update(editor, article);
|
||||
DataListener.pushMetadata(article!.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tags in the current document
|
||||
* @param data
|
||||
*/
|
||||
private static updateCustomTaxonomy(data: CustomTaxonomyData) {
|
||||
if (!data?.id || !data?.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (article && article.data) {
|
||||
|
||||
// Support multi-level fields
|
||||
let parentObj = article.data;
|
||||
for (const parent of data.parents || []) {
|
||||
parentObj = parentObj[parent];
|
||||
}
|
||||
|
||||
parentObj[data.name] = data.options || [];
|
||||
ArticleHelper.update(editor, article);
|
||||
DataListener.pushMetadata(article!.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to the settings
|
||||
* @param data
|
||||
*/
|
||||
private static async addCustomTaxonomy(data: CustomTaxonomyData) {
|
||||
if (!data?.id || !data?.option) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Settings.updateCustomTaxonomy(data.id, data.option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to the settings
|
||||
* @param tagType
|
||||
* @param value
|
||||
*/
|
||||
private static async addTags(tagType: TagType, value: string) {
|
||||
if (value) {
|
||||
let options = tagType === TagType.tags ? Settings.get<string[]>(SETTING_TAXONOMY_TAGS, true) : Settings.get<string[]>(SETTING_TAXONOMY_CATEGORIES, true);
|
||||
|
||||
if (!options) {
|
||||
options = [];
|
||||
}
|
||||
|
||||
options.push(value);
|
||||
const taxType = tagType === TagType.tags ? TaxonomyType.Tag : TaxonomyType.Category;
|
||||
await Settings.updateTaxonomy(taxType, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export * from './ArticleListener';
|
||||
export * from './BaseListener';
|
||||
export * from './DataListener';
|
||||
export * from './ExtensionListener';
|
||||
export * from './MediaListener';
|
||||
export * from './ScriptListener';
|
||||
export * from './SettingsListener';
|
||||
export * from './TaxonomyListener';
|
||||
@@ -11,20 +11,21 @@ export interface PanelSettings {
|
||||
date: DateInfo;
|
||||
categories: string[];
|
||||
customTaxonomy: CustomTaxonomy[];
|
||||
freeform: boolean;
|
||||
freeform: boolean | undefined;
|
||||
scripts: CustomScript[];
|
||||
isInitialized: boolean;
|
||||
modifiedDateUpdate: boolean;
|
||||
writingSettingsEnabled: boolean;
|
||||
fmHighlighting: boolean;
|
||||
fmHighlighting: boolean | undefined;
|
||||
preview: PreviewSettings;
|
||||
contentTypes: ContentType[];
|
||||
dashboardViewData: DashboardData | undefined;
|
||||
draftField: DraftField;
|
||||
draftField: DraftField | undefined;
|
||||
isBacker: boolean | undefined;
|
||||
framework: string | undefined;
|
||||
commands: FrameworkCommands;
|
||||
dataTypes: DataType[] | undefined;
|
||||
commaSeparatedFields: string[];
|
||||
}
|
||||
|
||||
export interface FrameworkCommands {
|
||||
@@ -70,8 +71,9 @@ export interface SEO {
|
||||
}
|
||||
|
||||
export interface Slug {
|
||||
prefix: number;
|
||||
suffix: number;
|
||||
prefix: number | string;
|
||||
suffix: number | string;
|
||||
updateFileName?: boolean;
|
||||
}
|
||||
|
||||
export interface FolderInfo {
|
||||
|
||||
@@ -2,6 +2,9 @@ export * from './Choice';
|
||||
export * from './ContentFolder';
|
||||
export * from './CustomTaxonomyData';
|
||||
export * from './DashboardData';
|
||||
export * from './DataFile';
|
||||
export * from './DataFolder';
|
||||
export * from './DataType';
|
||||
export * from './DraftField';
|
||||
export * from './Framework';
|
||||
export * from './MediaPaths';
|
||||
|
||||
@@ -161,7 +161,7 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({settings, metadata,
|
||||
icon={<TagIcon />}
|
||||
crntSelected={parent[field.name] as string[] || []}
|
||||
options={settings?.tags || []}
|
||||
freeform={settings.freeform}
|
||||
freeform={settings.freeform || false}
|
||||
focussed={focusElm === TagType.tags}
|
||||
unsetFocus={unsetFocus}
|
||||
parents={parentFields} />
|
||||
@@ -179,7 +179,7 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({settings, metadata,
|
||||
icon={<ListUnorderedIcon />}
|
||||
crntSelected={selectedValues as string[] || []}
|
||||
options={taxonomyData?.options || []}
|
||||
freeform={settings.freeform}
|
||||
freeform={settings.freeform || false}
|
||||
focussed={focusElm === TagType.custom}
|
||||
unsetFocus={unsetFocus}
|
||||
fieldName={field.name}
|
||||
@@ -196,7 +196,7 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({settings, metadata,
|
||||
icon={<ListUnorderedIcon />}
|
||||
crntSelected={parent.categories as string[] || []}
|
||||
options={settings.categories}
|
||||
freeform={settings.freeform}
|
||||
freeform={settings.freeform || false}
|
||||
focussed={focusElm === TagType.categories}
|
||||
unsetFocus={unsetFocus}
|
||||
parents={parentFields} />
|
||||
@@ -210,8 +210,8 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({settings, metadata,
|
||||
<FieldBoundary key={field.name} fieldName={field.title || field.name}>
|
||||
<DraftField
|
||||
label={field.title || field.name}
|
||||
type={draftField.type}
|
||||
choices={draftField.choices || []}
|
||||
type={draftField?.type || "boolean"}
|
||||
choices={draftField?.choices || []}
|
||||
value={value as boolean | string | null | undefined}
|
||||
onChanged={(value: boolean | string) => sendUpdate(field.name, value, parentFields)} />
|
||||
</FieldBoundary>
|
||||
|
||||
Reference in New Issue
Block a user