mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-05 00:13:11 +02:00
#97 - Custom scripts to support media management
This commit is contained in:
+14
-14
@@ -227,6 +227,20 @@ export class Dashboard {
|
||||
Dashboard.resetMedia();
|
||||
Dashboard.getMedia(0, folderPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post data to the dashboard
|
||||
* @param msg
|
||||
*/
|
||||
public static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) {
|
||||
if (Dashboard.isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Dashboard.webview) {
|
||||
Dashboard.webview?.webview.postMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an image into the front matter or contents
|
||||
@@ -696,20 +710,6 @@ export class Dashboard {
|
||||
|
||||
Dashboard.getMedia(page || 0, folder || "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Post data to the dashboard
|
||||
* @param msg
|
||||
*/
|
||||
private static postWebviewMessage(msg: { command: DashboardCommand, data?: any }) {
|
||||
if (Dashboard.isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Dashboard.webview) {
|
||||
Dashboard.webview?.webview.postMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the webview HTML contents
|
||||
|
||||
@@ -3,5 +3,6 @@ export enum DashboardCommand {
|
||||
pages = "pages",
|
||||
settings = "settings",
|
||||
media = "media",
|
||||
viewData = "viewData"
|
||||
viewData = "viewData",
|
||||
mediaUpdate = "mediaUpdate"
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { ClipboardCopyIcon, CodeIcon, PencilIcon, PhotographIcon, PlayIcon, PlusCircleIcon, TrashIcon } from '@heroicons/react/outline';
|
||||
import { Menu } from '@headlessui/react';
|
||||
import { PhotographIcon } from '@heroicons/react/outline';
|
||||
import { basename, dirname } from 'path';
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { CompressIcon } from '../../../components/icons/CompressIcon';
|
||||
import { CustomScript } from '../../../helpers/CustomScript';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import { ScriptType } from '../../../models';
|
||||
import { MediaInfo } from '../../../models/MediaPaths';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { LightboxAtom, PageSelector, SelectedMediaFolderSelector, SettingsSelector, ViewDataSelector } from '../../state';
|
||||
import { MenuItem, MenuItems } from '../Menu';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
import { Metadata } from '../Modals/Metadata';
|
||||
import { MenuButton } from './MenuButton'
|
||||
|
||||
export interface IItemProps {
|
||||
media: MediaInfo;
|
||||
@@ -65,8 +67,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
|
||||
};
|
||||
|
||||
const runCustomScript = (script: CustomScript) => {
|
||||
const relPath = getRelPath();
|
||||
Messenger.send(DashboardMessage.runCustomScript, {script, path: relPath});
|
||||
Messenger.send(DashboardMessage.runCustomScript, {script, path: media.fsPath});
|
||||
};
|
||||
|
||||
const insertToArticle = () => {
|
||||
@@ -159,6 +160,15 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
|
||||
setFilename(getFileName());
|
||||
};
|
||||
|
||||
const customScriptActions = () => {
|
||||
return (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFile).map(script => (
|
||||
<MenuItem
|
||||
key={script.title}
|
||||
title={script.title}
|
||||
onClick={() => runCustomScript(script)} />
|
||||
))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (media.alt !== alt) {
|
||||
setAlt(media.alt);
|
||||
@@ -195,65 +205,52 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
|
||||
</button>
|
||||
<div className={`relative py-4 pl-4 pr-12`}>
|
||||
<div className={`absolute top-4 right-4 flex flex-col space-y-4`}>
|
||||
<button title={`Edit metadata`}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={updateMetadata}>
|
||||
<PencilIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>Edit metadata</span>
|
||||
</button>
|
||||
|
||||
{
|
||||
viewData?.data?.filePath ? (
|
||||
<>
|
||||
<button
|
||||
title={`Insert into your content`}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={insertToArticle}>
|
||||
<PlusCircleIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>Insert into your content</span>
|
||||
</button>
|
||||
<div className="flex items-center">
|
||||
<Menu as="div" className="relative z-10 inline-block text-left">
|
||||
<MenuButton title={`Menu`} />
|
||||
|
||||
<MenuItems>
|
||||
<MenuItem
|
||||
title={`Edit metadata`}
|
||||
onClick={updateMetadata}
|
||||
/>
|
||||
|
||||
{
|
||||
(viewData?.data?.position && settings?.mediaSnippet && settings?.mediaSnippet.length > 0) && (
|
||||
<button
|
||||
title={`Insert your media snippet`}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={insertSnippet}>
|
||||
<CodeIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>Insert your media snippet</span>
|
||||
</button>
|
||||
viewData?.data?.filePath ? (
|
||||
<>
|
||||
<MenuItem
|
||||
title={`Insert image markdown`}
|
||||
onClick={insertToArticle} />
|
||||
|
||||
{
|
||||
(viewData?.data?.position && settings?.mediaSnippet && settings?.mediaSnippet.length > 0) && (
|
||||
<MenuItem
|
||||
title={`Insert snippet`}
|
||||
onClick={insertSnippet} />
|
||||
)
|
||||
}
|
||||
|
||||
{ customScriptActions() }
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MenuItem
|
||||
title={`Copy media path`}
|
||||
onClick={copyToClipboard} />
|
||||
|
||||
{ customScriptActions() }
|
||||
|
||||
<MenuItem
|
||||
title={`Delete`}
|
||||
onClick={deleteMedia} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button title={`Copy media path`}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={copyToClipboard}>
|
||||
<ClipboardCopyIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>Copy media path</span>
|
||||
</button>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
{
|
||||
(settings?.scripts || []).filter(script => script.type === ScriptType.MediaFile).map(script => (
|
||||
<button
|
||||
title={script.title}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={() => runCustomScript(script)}>
|
||||
<PlayIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>{script.title}</span>
|
||||
</button>
|
||||
))
|
||||
}
|
||||
|
||||
<button title={`Delete media`}
|
||||
className={`hover:text-teal-900 focus:outline-none`}
|
||||
onClick={deleteMedia}>
|
||||
<TrashIcon className={`h-5 w-5`} />
|
||||
<span className={`sr-only`}>Delete media</span>
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<p className="text-sm dark:text-whisper-900 font-bold pointer-events-none flex items-center">
|
||||
{basename(parseWinPath(media.fsPath) || "")}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Menu } from '@headlessui/react';
|
||||
import { DotsVerticalIcon } from '@heroicons/react/outline';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IMenuButtonProps {
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const MenuButton: React.FunctionComponent<IMenuButtonProps> = ({ title, disabled }: React.PropsWithChildren<IMenuButtonProps>) => {
|
||||
return (
|
||||
<div className={`inline-flex items-center ${disabled ? 'opacity-50' : ''}`}>
|
||||
<Menu.Button disabled={disabled} className="group inline-flex justify-center text-sm font-medium text-vulcan-400 hover:text-vulcan-600 dark:text-gray-400 dark:hover:text-whisper-600">
|
||||
<span className="sr-only">{title}</span>
|
||||
<DotsVerticalIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</Menu.Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { EventData } from '@estruyf/vscode';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { RefreshIcon } from '@heroicons/react/outline';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { DashboardCommand } from '../../DashboardCommand';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { LoadingAtom, MediaTotalSelector, PageAtom, SelectedMediaFolderSelector, SortingSelector } from '../../state';
|
||||
import { FolderCreation } from './FolderCreation';
|
||||
@@ -47,6 +49,17 @@ export const Pagination: React.FunctionComponent<IPaginationProps> = ({}: React.
|
||||
Messenger.send(DashboardMessage.refreshMedia, { folder: selectedFolder });
|
||||
}
|
||||
|
||||
const mediaUpdate = (message: MessageEvent<EventData<{ key: string, value: any }>>) => {
|
||||
if (message.data.command === DashboardCommand.mediaUpdate) {
|
||||
setLoading(true);
|
||||
Messenger.send(DashboardMessage.getMedia, {
|
||||
page,
|
||||
folder: selectedFolder || '',
|
||||
sorting: crntSorting
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
setLoading(true);
|
||||
Messenger.send(DashboardMessage.getMedia, {
|
||||
@@ -75,6 +88,10 @@ export const Pagination: React.FunctionComponent<IPaginationProps> = ({}: React.
|
||||
});
|
||||
}, [crntSorting]);
|
||||
|
||||
React.useEffect(() => {
|
||||
Messenger.listen(mediaUpdate);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className="py-4 px-5 flex items-center justify-between bg-gray-200 border-b border-gray-300 dark:bg-vulcan-400 dark:border-vulcan-100"
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as React from 'react';
|
||||
|
||||
export interface IMenuItemProps {
|
||||
title: string;
|
||||
value: any;
|
||||
value?: any;
|
||||
isCurrent?: boolean;
|
||||
disabled?: boolean;
|
||||
onClick: (value: any) => void;
|
||||
|
||||
@@ -8,6 +8,8 @@ import * as os from 'os';
|
||||
import { join } from 'path';
|
||||
import { Notifications } from './Notifications';
|
||||
import ContentProvider from '../providers/ContentProvider';
|
||||
import { Dashboard } from '../commands/Dashboard';
|
||||
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
|
||||
|
||||
export class CustomScript {
|
||||
|
||||
@@ -17,10 +19,8 @@ export class CustomScript {
|
||||
if (wsFolder) {
|
||||
const wsPath = wsFolder.fsPath;
|
||||
|
||||
if (script.type === ScriptType.MediaFile) {
|
||||
console.log(path);
|
||||
} else if (script.type === ScriptType.MediaFolder) {
|
||||
console.log(path);
|
||||
if (script.type === ScriptType.MediaFile || script.type === ScriptType.MediaFolder) {
|
||||
CustomScript.runMediaScript(wsPath, path, script);
|
||||
} else {
|
||||
if (script.bulk) {
|
||||
// Run script on all files
|
||||
@@ -85,6 +85,31 @@ export class CustomScript {
|
||||
});
|
||||
}
|
||||
|
||||
private static async runMediaScript(wsPath: string, path: string | null, script: ICustomScript): Promise<void> {
|
||||
if (!path) {
|
||||
Notifications.error(`${script.title}: There was no folder or media path specified.`);
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`${script.nodeBin || "node"} ${join(wsPath, script.script)} "${wsPath}" "${path}"`, (error, stdout) => {
|
||||
if (error) {
|
||||
Notifications.error(`${script.title}: ${error.message}`);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
CustomScript.showOutput(stdout, script);
|
||||
|
||||
Dashboard.postWebviewMessage({
|
||||
command: DashboardCommand.mediaUpdate
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static async runScript(wsPath: string, article: matter.GrayMatterFile<string> | null, contentPath: string, script: ICustomScript): Promise<string | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let articleData = "";
|
||||
@@ -112,7 +137,7 @@ export class CustomScript {
|
||||
if (script.output === "editor") {
|
||||
ContentProvider.show(output, script.title, script.outputType || "text");
|
||||
} else {
|
||||
window.showInformationMessage(`${script.title}: ${output}}`, 'Copy output').then(value => {
|
||||
window.showInformationMessage(`${script.title}: ${output}`, 'Copy output').then(value => {
|
||||
if (value === 'Copy output') {
|
||||
vscodeEnv.clipboard.writeText(output);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user