diff --git a/CHANGELOG.md b/CHANGELOG.md index 890bfb89..075f4669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## [10.6.0] - 2024-11-06 - [Release notes](https://beta.frontmatter.codes/updates/v10.6.0) + +### 🎨 Enhancements + +- [#878](https://github.com/estruyf/vscode-front-matter/issues/878): Allow the `select all` button to work on other pages when there is a selection present +- [#882](https://github.com/estruyf/vscode-front-matter/issues/882): Dynamic evaluation of the `node` executable path +- [#884](https://github.com/estruyf/vscode-front-matter/issues/884): Hide WYSIWYG actions when the file is in git diff mode + +### 🐞 Fixes + +- [#859](https://github.com/estruyf/vscode-front-matter/issues/859): Fix label in the data view dropdown field +- [#876](https://github.com/estruyf/vscode-front-matter/issues/876): Fix snippet type on the snippet card +- [#879](https://github.com/estruyf/vscode-front-matter/issues/879): Fix for auto updating last modified date on save +- [#885](https://github.com/estruyf/vscode-front-matter/issues/885): Fix content relationship for none i18n content + ## [10.5.1] - 2024-10-23 ### 🎨 Enhancements diff --git a/package.json b/package.json index cfcfab5f..48cb83aa 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Front Matter CMS", "description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and many more...", "icon": "assets/frontmatter-teal-128x128.png", - "version": "10.5.1", + "version": "10.6.0", "preview": false, "publisher": "eliostruyf", "galleryBanner": { @@ -31,7 +31,7 @@ "l10n": "./l10n", "categories": [ "AI", - "Other" + "Visualization" ], "keywords": [ "Front Matter", @@ -2421,32 +2421,32 @@ "editor/title": [{ "command": "frontMatter.markup.heading", "group": "navigation@-133", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.bold", "group": "navigation@-132", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.italic", "group": "navigation@-131", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.hyperlink", "group": "navigation@-130", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.insertSnippet", "group": "navigation@-129", - "when": "frontMatter:file:isValid == true && frontMatter:dashboard:snippets:enabled" + "when": "frontMatter:file:isValid == true && frontMatter:dashboard:snippets:enabled && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.insertMedia", "group": "navigation@-128", - "when": "frontMatter:file:isValid == true" + "when": "frontMatter:file:isValid == true && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.i18n.createOrOpen", @@ -2456,37 +2456,37 @@ { "command": "frontMatter.markup.options", "group": "navigation@-126", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.orderedlist", "group": "1_markup@1", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.unorderedlist", "group": "1_markup@2", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.tasklist", "group": "1_markup@3", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.code", "group": "1_markup@4", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.codeblock", "group": "1_markup@5", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.markup.blockquote", "group": "1_markup@6", - "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" + "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg && activeEditor == 'workbench.editors.files.textFileEditor'" }, { "command": "frontMatter.dashboard", diff --git a/src/commands/Article.ts b/src/commands/Article.ts index b0e35076..044ef7da 100644 --- a/src/commands/Article.ts +++ b/src/commands/Article.ts @@ -52,7 +52,7 @@ export class Article { * * @param subscriptions - The array of subscriptions to register the commands with. */ - public static async registerCommands(subscriptions: unknown[]) { + public static registerCommands(subscriptions: unknown[]) { subscriptions.push( commands.registerCommand(COMMAND_NAME.setLastModifiedDate, Article.setLastModifiedDate) ); @@ -66,6 +66,15 @@ export class Article { subscriptions.push(commands.registerCommand(COMMAND_NAME.insertSnippet, Article.insertSnippet)); } + /** + * Registers event listeners for the Article class. + * + * @param subscriptions - An array to which the event listener will be added. + */ + public static registerListeners(subscriptions: unknown[]) { + subscriptions.push(workspace.onWillSaveTextDocument(Article.autoUpdate)); + } + /** * Sets the article date */ @@ -369,15 +378,15 @@ export class Article { * Article auto updater * @param event */ - public static async autoUpdate(event: TextDocumentWillSaveEvent) { + public static autoUpdate(event: TextDocumentWillSaveEvent) { const document = event.document; if (document && ArticleHelper.isSupportedFile(document)) { const autoUpdate = Settings.get(SETTING_AUTO_UPDATE_DATE); // Is article located in one of the content folders - const folders = await Folders.getCachedOrFresh(); + const folders = Folders.getCached(); const documentPath = parseWinPath(document.fileName); - const folder = folders.find((f) => documentPath.startsWith(f.path)); + const folder = folders?.find((f) => documentPath.startsWith(f.path)); if (!folder) { return; } diff --git a/src/components/uniforms-frontmatter/SelectField.tsx b/src/components/uniforms-frontmatter/SelectField.tsx index 05f14200..331de380 100644 --- a/src/components/uniforms-frontmatter/SelectField.tsx +++ b/src/components/uniforms-frontmatter/SelectField.tsx @@ -42,6 +42,7 @@ function Select({ ...props }: SelectFieldProps) { const multiple = fieldType === Array; + return (
@@ -84,11 +85,12 @@ function Select({ }} ref={inputRef} value={value ?? ''} + className='text-[var(--vscode-foreground)] bg-[var(--vscode-list-activeSelectionBackground)] rounded-[2px] active:border-transparent disabled:opacity-40 disabled:cursor-not-allowed focus:outline-none' style={{ width: '100%', padding: '0.5rem' }} > - {(!!placeholder || !required || value === undefined) && !multiple && ( + {(!required || value === undefined) && !multiple && ( )} diff --git a/src/dashboardWebView/components/Common/ItemSelection.tsx b/src/dashboardWebView/components/Common/ItemSelection.tsx index eeda2de4..13f9a99d 100644 --- a/src/dashboardWebView/components/Common/ItemSelection.tsx +++ b/src/dashboardWebView/components/Common/ItemSelection.tsx @@ -25,6 +25,9 @@ export const ItemSelection: React.FunctionComponent = ({
{ + e.stopPropagation(); + }} onChange={() => { onMultiSelect(filePath); }} diff --git a/src/dashboardWebView/components/Header/ActionsBar.tsx b/src/dashboardWebView/components/Header/ActionsBar.tsx index 612a7f17..8035a773 100644 --- a/src/dashboardWebView/components/Header/ActionsBar.tsx +++ b/src/dashboardWebView/components/Header/ActionsBar.tsx @@ -4,8 +4,7 @@ import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, Eye import { useRecoilState, useRecoilValue } from 'recoil'; import { MultiSelectedItemsAtom, PagedItems, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state'; import { ActionsBarItem } from './ActionsBarItem'; -import * as l10n from '@vscode/l10n'; -import { LocalizationKey } from '../../../localization'; +import { LocalizationKey, localize } from '../../../localization'; import { Alert } from '../Modals/Alert'; import { messageHandler } from '@estruyf/vscode/dist/client'; import { DashboardMessage } from '../../DashboardMessage'; @@ -68,8 +67,14 @@ export const ActionsBar: React.FunctionComponent = ({ }, [selectedFiles]); const selectAllItems = React.useCallback(() => { - setSelectedFiles([...pagedItems]); - }, [pagedItems]); + const allSelected = [...selectedFiles, ...pagedItems]; + setSelectedFiles(Array.from(new Set(allSelected))); + }, [selectedFiles, pagedItems]); + + const hasAllItemsSelectedOnPage = React.useMemo(() => { + const selectedItemsOnPage = selectedFiles.filter((file) => pagedItems.includes(file)); + return selectedItemsOnPage.length >= pagedItems.length; + }, [selectedFiles, pagedItems]); const languageActions = React.useMemo(() => { const actions: React.ReactNode[] = []; @@ -92,7 +97,7 @@ export const ActionsBar: React.FunctionComponent = ({ }) }}> - {l10n.t(LocalizationKey.commonTranslate)} + {localize(LocalizationKey.commonTranslate)} ) @@ -107,7 +112,7 @@ export const ActionsBar: React.FunctionComponent = ({ className='flex items-center text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]' > - {l10n.t(LocalizationKey.commonLanguages)} + {localize(LocalizationKey.commonLanguages)} @@ -163,7 +168,7 @@ export const ActionsBar: React.FunctionComponent = ({ disabled={selectedFiles.length === 0} > - {l10n.t(LocalizationKey.commonScripts)} + {localize(LocalizationKey.commonScripts)} @@ -197,10 +202,10 @@ export const ActionsBar: React.FunctionComponent = ({ 1} onClick={viewFile} - title={l10n.t(LocalizationKey.commonView)} + title={localize(LocalizationKey.commonView)} > { @@ -211,10 +216,10 @@ export const ActionsBar: React.FunctionComponent = ({ messageHandler.send(DashboardMessage.rename, selectedFiles[0]); setSelectedFiles([]); }} - title={l10n.t(LocalizationKey.commonRename)} + title={localize(LocalizationKey.commonRename)} >
@@ -258,33 +263,33 @@ export const ActionsBar: React.FunctionComponent = ({ setSelectedFiles([])} - title={l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)} + title={localize(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)} > ) }
- {l10n.t(LocalizationKey.dashboardHeaderActionsBarSelectAll)} + {localize(LocalizationKey.dashboardHeaderActionsBarSelectAll)}
{showAlert && ( setShowAlert(false)} trigger={onDeleteConfirm} /> diff --git a/src/dashboardWebView/components/Header/Filters.tsx b/src/dashboardWebView/components/Header/Filters.tsx index c0c714e0..78fae729 100644 --- a/src/dashboardWebView/components/Header/Filters.tsx +++ b/src/dashboardWebView/components/Header/Filters.tsx @@ -10,7 +10,7 @@ import { LanguageFilter } from '../Filters/LanguageFilter'; export interface IFiltersProps { } -export const Filters: React.FunctionComponent = (_: React.PropsWithChildren) => { +export const Filters: React.FunctionComponent = () => { const [crntFilters, setCrntFilters] = useRecoilState(FiltersAtom); const [crntTag, setCrntTag] = useRecoilState(TagAtom); const [crntCategory, setCrntCategory] = useRecoilState(CategoryAtom); diff --git a/src/dashboardWebView/components/SnippetsView/Item.tsx b/src/dashboardWebView/components/SnippetsView/Item.tsx index b62be87f..d54e4359 100644 --- a/src/dashboardWebView/components/SnippetsView/Item.tsx +++ b/src/dashboardWebView/components/SnippetsView/Item.tsx @@ -183,7 +183,7 @@ export const Item: React.FunctionComponent = ({
{ - snippet.isMediaSnippet ? l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeContent) : l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeMedia) + snippet.isMediaSnippet ? l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeMedia) : l10n.t(LocalizationKey.dashboardSnippetsViewItemTypeContent) }
diff --git a/src/extension.ts b/src/extension.ts index 49c560bf..8c523e4e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,7 +14,7 @@ import ContentProvider from './providers/ContentProvider'; import { PagesListener } from './listeners/dashboard'; import { ModeSwitch } from './services/ModeSwitch'; import { PagesParser } from './services/PagesParser'; -import { ContentType, Telemetry, Extension } from './helpers'; +import { ContentType, Extension } from './helpers'; import * as l10n from '@vscode/l10n'; import { Backers, @@ -121,8 +121,9 @@ export async function activate(context: vscode.ExtensionContext) { // Register the taxonomy commands Taxonomy.registerCommands(subscriptions); - // Register all the article commands + // Register all the article commands and listeners Article.registerCommands(subscriptions); + Article.registerListeners(subscriptions); // Template creation Template.registerCommands(); @@ -181,9 +182,6 @@ export async function activate(context: vscode.ExtensionContext) { // Automatically run the command triggerPageUpdate(`main`); - // Listener for file edit changes - subscriptions.push(vscode.workspace.onWillSaveTextDocument(handleAutoDateUpdate)); - // Listener for file saves subscriptions.push(PagesListener.saveFileWatcher()); @@ -241,16 +239,13 @@ export async function activate(context: vscode.ExtensionContext) { // Subscribe all commands subscriptions.push(PanelView, collapseAll, fmStatusBarItem); + // eslint-disable-next-line no-console console.log(`π–₯π—‹π—ˆπ—‡π— 𝖬𝖺𝗍𝗍𝖾𝗋 𝖒𝖬𝖲 𝖺𝖼𝗍𝗂𝗏𝖺𝗍𝖾𝖽! 𝖱𝖾𝖺𝖽𝗒 π—π—ˆ π—Œπ—π–Ίπ—‹π— 𝗐𝗋𝗂𝗍𝗂𝗇𝗀... πŸ‘©β€πŸ’»πŸ§‘β€πŸ’»πŸ‘¨β€πŸ’»`); } // eslint-disable-next-line @typescript-eslint/no-empty-function export function deactivate() {} -const handleAutoDateUpdate = (e: vscode.TextDocumentWillSaveEvent) => { - Article.autoUpdate(e); -}; - const triggerPageUpdate = (location: string) => { Logger.verbose(`Trigger page update: ${location}`); pageUpdateDebouncer(() => { diff --git a/src/helpers/CustomScript.ts b/src/helpers/CustomScript.ts index 33c0e529..79d9aa70 100644 --- a/src/helpers/CustomScript.ts +++ b/src/helpers/CustomScript.ts @@ -1,5 +1,5 @@ import { Settings } from './SettingsHelper'; -import { CommandType, EnvironmentType } from './../models/PanelSettings'; +import { CommandType } from './../models/PanelSettings'; import { CustomScript as ICustomScript, ScriptType } from '../models/PanelSettings'; import { window, env as vscodeEnv, ProgressLocation, Uri, commands } from 'vscode'; import { ArticleHelper, Logger, MediaHelpers } from '.'; @@ -13,9 +13,8 @@ import { Dashboard } from '../commands/Dashboard'; import { DashboardCommand } from '../dashboardWebView/DashboardCommand'; import { ParsedFrontMatter } from '../parsers'; import { SETTING_CUSTOM_SCRIPTS } from '../constants'; -import { existsAsync } from '../utils'; -import * as l10n from '@vscode/l10n'; -import { LocalizationKey } from '../localization'; +import { evaluateCommand, existsAsync, getPlatform } from '../utils'; +import { LocalizationKey, localize } from '../localization'; export class CustomScript { /** @@ -101,7 +100,7 @@ export class CustomScript { ); } else { Notifications.warning( - l10n.t(LocalizationKey.helpersCustomScriptSingleRunArticleWarning, script.title) + localize(LocalizationKey.helpersCustomScriptSingleRunArticleWarning, script.title) ); } } @@ -117,7 +116,7 @@ export class CustomScript { if (!folders || folders.length === 0) { Notifications.warning( - l10n.t(LocalizationKey.helpersCustomScriptBulkRunNoFilesWarning, script.title) + localize(LocalizationKey.helpersCustomScriptBulkRunNoFilesWarning, script.title) ); return; } @@ -127,7 +126,7 @@ export class CustomScript { window.withProgress( { location: ProgressLocation.Notification, - title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title), + title: localize(LocalizationKey.helpersCustomScriptExecuting, script.title), cancellable: false }, async (_, __) => { @@ -173,7 +172,7 @@ export class CustomScript { ): Promise { if (!path) { Notifications.error( - l10n.t(LocalizationKey.helpersCustomScriptRunMediaScriptNoFolderWarning, script.title) + localize(LocalizationKey.helpersCustomScriptRunMediaScriptNoFolderWarning, script.title) ); return; } @@ -182,7 +181,7 @@ export class CustomScript { window.withProgress( { location: ProgressLocation.Notification, - title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title), + title: localize(LocalizationKey.helpersCustomScriptExecuting, script.title), cancellable: false }, async () => { @@ -309,7 +308,10 @@ export class CustomScript { throw new Error(`Couldn't update article.`); } Notifications.info( - l10n.t(LocalizationKey.helpersCustomScriptShowOutputFrontMatterSuccess, script.title) + localize( + LocalizationKey.helpersCustomScriptShowOutputFrontMatterSuccess, + script.title + ) ); } } else if (data.fmAction) { @@ -345,10 +347,12 @@ export class CustomScript { window .showInformationMessage( `${script.title}: ${output}`, - l10n.t(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction) + localize(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction) ) .then((value) => { - if (value === l10n.t(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction)) { + if ( + value === localize(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction) + ) { vscodeEnv.clipboard.writeText(output); } }); @@ -356,7 +360,7 @@ export class CustomScript { } } else { Notifications.info( - l10n.t(LocalizationKey.helpersCustomScriptShowOutputSuccess, script.title) + localize(LocalizationKey.helpersCustomScriptShowOutputSuccess, script.title) ); } } @@ -373,7 +377,7 @@ export class CustomScript { wsPath: string, args: string ): Promise { - const osType = os.type(); + const platform = getPlatform(); // Check the command to use let command = script.nodeBin || 'node'; @@ -381,6 +385,10 @@ export class CustomScript { command = script.command; } + if (script.command === CommandType.Node && platform !== 'windows') { + command = await evaluateCommand(CommandType.Node); + } + let scriptPath = join(wsPath, script.script); if (script.script.includes(WORKSPACE_PLACEHOLDER)) { scriptPath = Folders.getAbsFilePath(script.script); @@ -388,19 +396,15 @@ export class CustomScript { // Check if there is an environments overwrite required if (script.environments) { - let crntType: EnvironmentType | null = null; - if (osType === 'Windows_NT') { - crntType = 'windows'; - } else if (osType === 'Darwin') { - crntType = 'macos'; - } else { - crntType = 'linux'; - } - - const environment = script.environments.find((e) => e.type === crntType); + const environment = script.environments.find((e) => e.type === platform); if (environment && environment.script && environment.command) { if (await CustomScript.validateCommand(environment.command)) { command = environment.command; + + if (command === CommandType.Node && platform !== 'windows') { + command = await evaluateCommand(CommandType.Node); + } + scriptPath = join(wsPath, environment.script); if (environment.script.includes(WORKSPACE_PLACEHOLDER)) { scriptPath = Folders.getAbsFilePath(environment.script); @@ -414,12 +418,12 @@ export class CustomScript { throw new Error(`Script not found: ${scriptPath}`); } - if (osType === 'Windows_NT' && command.toLowerCase() === 'powershell') { + if (platform === 'windows' && command.toLowerCase() === 'powershell') { command = `${command} -File`; } const fullScript = `${command} "${scriptPath}" ${args}`; - Logger.info(l10n.t(LocalizationKey.helpersCustomScriptExecuting, fullScript)); + Logger.info(localize(LocalizationKey.helpersCustomScriptExecuting, fullScript)); const output: string = await CustomScript.executeScriptAsync(fullScript, wsPath); @@ -502,7 +506,7 @@ export class CustomScript { return true; } catch (e) { - Logger.error(l10n.t(LocalizationKey.helpersCustomScriptValidateCommandError, command)); + Logger.error(localize(LocalizationKey.helpersCustomScriptValidateCommandError, command)); return false; } } diff --git a/src/helpers/DashboardSettings.ts b/src/helpers/DashboardSettings.ts index d2275227..c26a8477 100644 --- a/src/helpers/DashboardSettings.ts +++ b/src/helpers/DashboardSettings.ts @@ -1,5 +1,5 @@ import { GitListener } from './../listeners/general/GitListener'; -import { basename, join } from 'path'; +import { join } from 'path'; import { workspace } from 'vscode'; import { Folders } from '../commands/Folders'; import { Project } from '../commands/Project'; @@ -23,7 +23,6 @@ import { SETTING_MEDIA_SUPPORTED_MIMETYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TEMPLATES_ENABLED, - SETTING_GIT_ENABLED, SETTING_DASHBOARD_CONTENT_PAGINATION, SETTING_SNIPPETS_WRAPPER, SETTING_DASHBOARD_CONTENT_CARD_DATE, diff --git a/src/listeners/dashboard/SsgListener.ts b/src/listeners/dashboard/SsgListener.ts index c7199df8..2b5ad5dd 100644 --- a/src/listeners/dashboard/SsgListener.ts +++ b/src/listeners/dashboard/SsgListener.ts @@ -12,7 +12,7 @@ import { } from '../../constants'; import { SettingsListener } from './SettingsListener'; import { Terminal } from '../../services'; -import { existsAsync, readFileAsync } from '../../utils'; +import { evaluateCommand, existsAsync, getPlatform, readFileAsync } from '../../utils'; import { join } from 'path'; export class SsgListener extends BaseListener { @@ -170,7 +170,12 @@ export class SsgListener extends BaseListener { workspace.fs.copy(scriptPath, tempScriptPath, { overwrite: true }); } - const fullScript = `node "${tempScriptPath.fsPath}" "${contentConfigFile.fsPath}"`; + let nodeExecPath = 'node'; + const platform = getPlatform(); + if (platform !== 'windows') { + nodeExecPath = await evaluateCommand('node'); + } + const fullScript = `${nodeExecPath} "${tempScriptPath.fsPath}" "${contentConfigFile.fsPath}"`; try { const result: string = await SsgListener.executeScript(fullScript, wsFolder?.fsPath || ''); diff --git a/src/listeners/panel/FieldsListener.ts b/src/listeners/panel/FieldsListener.ts index 6ebc62e0..65f673b3 100644 --- a/src/listeners/panel/FieldsListener.ts +++ b/src/listeners/panel/FieldsListener.ts @@ -39,17 +39,21 @@ export class FieldsListener extends BaseListener { return; } + const isLocaleEnabled = await i18n.isLocaleEnabled(data.activePath); const activeLocale = await i18n.getLocale(data.activePath); - if (!activeLocale?.locale) { + if (isLocaleEnabled && !activeLocale?.locale) { return; } PagesListener.getPagesData(false, async (pages) => { + const fuseKeys: Fuse.FuseOptionKey[] = [{ name: 'fmContentType', weight: 1 }]; + + if (isLocaleEnabled && data.sameLocale) { + fuseKeys.push({ name: 'fmLocale.locale', weight: 1 }); + } + const fuseOptions: Fuse.IFuseOptions = { - keys: [ - { name: 'fmContentType', weight: 1 }, - ...(data.sameLocale ? [{ name: 'fmLocale.locale', weight: 1 }] : []) - ], + keys: fuseKeys, findAllMatches: true, threshold: 0 }; @@ -60,11 +64,14 @@ export class FieldsListener extends BaseListener { ); const fuseIndex = Fuse.parseIndex(pagesIndex); const fuse = new Fuse(pages || [], fuseOptions, fuseIndex); + + const andExpression: Fuse.Expression[] = [{ fmContentType: data.type ?? '' }]; + if (isLocaleEnabled && activeLocale?.locale && data.sameLocale) { + andExpression.push({ 'fmLocale.locale': activeLocale.locale }); + } + const results = fuse.search({ - $and: [ - { fmContentType: data.type! }, - ...(data.sameLocale ? [{ 'fmLocale.locale': activeLocale.locale }] : []) - ] + $and: andExpression }); const pageResults = results.map((page) => page.item); diff --git a/src/models/ShellSetting.ts b/src/models/ShellSetting.ts new file mode 100644 index 00000000..05a90dd8 --- /dev/null +++ b/src/models/ShellSetting.ts @@ -0,0 +1,3 @@ +export interface ShellSetting { + path: string; +} diff --git a/src/models/index.ts b/src/models/index.ts index 82b09148..81684673 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -22,6 +22,7 @@ export * from './Mode'; export * from './PanelSettings'; export * from './PostMessageData'; export * from './Project'; +export * from './ShellSetting'; export * from './Snippets'; export * from './SortOrder'; export * from './SortType'; diff --git a/src/services/Terminal.ts b/src/services/Terminal.ts index fa6b2ff3..4c9aa06c 100644 --- a/src/services/Terminal.ts +++ b/src/services/Terminal.ts @@ -1,12 +1,7 @@ import { workspace, window, ThemeIcon, TerminalOptions } from 'vscode'; -import * as os from 'os'; import { Folders } from '../commands'; -import * as l10n from '@vscode/l10n'; -import { LocalizationKey } from '../localization'; - -interface ShellSetting { - path: string; -} +import { LocalizationKey, localize } from '../localization'; +import { getShellPath } from '../utils'; export class Terminal { public static readonly terminalName: string = 'Local server'; @@ -15,7 +10,7 @@ export class Terminal { * Return the shell path for the current platform */ public static get shell() { - const shell: string | { path: string } | undefined = Terminal.getShellPath(); + const shell: string | { path: string } | undefined = getShellPath(); let shellPath: string | undefined = undefined; if (typeof shell !== 'string' && !!shell) { @@ -47,7 +42,7 @@ export class Terminal { const terminalOptions: TerminalOptions = { name: Terminal.terminalName, iconPath: new ThemeIcon('server-environment'), - message: l10n.t( + message: localize( LocalizationKey.servicesTerminalOpenLocalServerTerminalTerminalOptionMessage ) }; @@ -90,46 +85,4 @@ export class Terminal { return localServerTerminal; } } - - /** - * Retrieve the automation profile for the current platform - * @returns - */ - private static getShellPath(): string | ShellSetting | undefined { - const platform = Terminal.getPlatform(); - const terminalSettings = workspace.getConfiguration('terminal'); - - const automationProfile = terminalSettings.get( - `integrated.automationProfile.${platform}` - ); - if (!!automationProfile) { - return automationProfile; - } - - const defaultProfile = terminalSettings.get(`integrated.defaultProfile.${platform}`); - const profiles = terminalSettings.get<{ [prop: string]: ShellSetting }>( - `integrated.profiles.${platform}` - ); - - if (defaultProfile && profiles && profiles[defaultProfile]) { - return profiles[defaultProfile]; - } - - return terminalSettings.get(`integrated.shell.${platform}`); - } - - /** - * Get the current platform - * @returns - */ - private static getPlatform = (): 'windows' | 'linux' | 'osx' => { - const platform = os.platform(); - if (platform === 'win32') { - return 'windows'; - } else if (platform === 'darwin') { - return 'osx'; - } - - return 'linux'; - }; } diff --git a/src/utils/evaluateCommand.ts b/src/utils/evaluateCommand.ts new file mode 100644 index 00000000..735c6a5a --- /dev/null +++ b/src/utils/evaluateCommand.ts @@ -0,0 +1,30 @@ +import { exec } from 'child_process'; +import { getShellPath } from '../utils'; +import { Logger } from '../helpers'; + +/** + * Evaluate the command dynamically using `which` command + * @param command + * @returns + */ +export const evaluateCommand = (command: string): Promise => { + const shell = getShellPath(); + let shellPath: string | undefined = undefined; + if (typeof shell !== 'string' && !!shell) { + shellPath = shell.path; + } else { + shellPath = shell || undefined; + } + + return new Promise((resolve, reject) => { + exec(`which ${command}`, { shell: shellPath }, (error, stdout) => { + if (error) { + Logger.error(`Error evaluating command: ${command}`); + reject(error); + return; + } + + resolve(stdout.trim()); + }); + }); +}; diff --git a/src/utils/getPlatform.ts b/src/utils/getPlatform.ts new file mode 100644 index 00000000..da5b4f77 --- /dev/null +++ b/src/utils/getPlatform.ts @@ -0,0 +1,20 @@ +import * as os from 'os'; + +/** + * Determines the current operating system platform. + * + * @returns {'windows' | 'linux' | 'osx'} - A string representing the platform: + * - 'windows' for Windows OS + * - 'osx' for macOS + * - 'linux' for Linux OS + */ +export const getPlatform = (): 'windows' | 'linux' | 'osx' => { + const platform = os.platform(); + if (platform === 'win32') { + return 'windows'; + } else if (platform === 'darwin') { + return 'osx'; + } + + return 'linux'; +}; diff --git a/src/utils/getShellPath.ts b/src/utils/getShellPath.ts new file mode 100644 index 00000000..62bca2a0 --- /dev/null +++ b/src/utils/getShellPath.ts @@ -0,0 +1,36 @@ +import { workspace } from 'vscode'; +import { ShellSetting } from '../models'; +import { getPlatform } from './getPlatform'; + +/** + * Retrieves the shell path configuration based on the current platform and terminal settings. + * + * This method checks for the following configurations in order: + * 1. `integrated.automationProfile.`: Returns the automation profile if it exists. + * 2. `integrated.defaultProfile.` and `integrated.profiles.`: Returns the shell setting from the default profile if it exists. + * 3. `integrated.shell.`: Returns the shell setting if the above configurations are not found. + * + * @returns {string | ShellSetting | undefined} The shell path configuration or undefined if not found. + */ +export const getShellPath = (): string | ShellSetting | undefined => { + const platform = getPlatform(); + const terminalSettings = workspace.getConfiguration('terminal'); + + const automationProfile = terminalSettings.get( + `integrated.automationProfile.${platform}` + ); + if (!!automationProfile) { + return automationProfile; + } + + const defaultProfile = terminalSettings.get(`integrated.defaultProfile.${platform}`); + const profiles = terminalSettings.get<{ [prop: string]: ShellSetting }>( + `integrated.profiles.${platform}` + ); + + if (defaultProfile && profiles && profiles[defaultProfile]) { + return profiles[defaultProfile]; + } + + return terminalSettings.get(`integrated.shell.${platform}`); +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index f1013c87..49951555 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,7 @@ export * from './cn'; export * from './copyFileAsync'; export * from './encodeEmoji'; +export * from './evaluateCommand'; export * from './existsAsync'; export * from './fetchWithTimeout'; export * from './fieldWhenClause'; @@ -8,6 +9,8 @@ export * from './flattenObjectKeys'; export * from './getDescriptionField'; export * from './getExtensibilityScripts'; export * from './getLocalizationFile'; +export * from './getPlatform'; +export * from './getShellPath'; export * from './getTitleField'; export * from './getWebviewJsFiles'; export * from './ignoreMsgCommand';