mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-07 17:32:58 +02:00
#669 - video preview + caption field
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
- [#659](https://github.com/estruyf/vscode-front-matter/issues/659): Implement a filter for the taxonomy dashboard
|
||||
- [#662](https://github.com/estruyf/vscode-front-matter/issues/662): Always show the `all articles` tab with the page counter
|
||||
- [#663](https://github.com/estruyf/vscode-front-matter/issues/663): Make card tags clickable to filter the view
|
||||
- [#669](https://github.com/estruyf/vscode-front-matter/issues/669): Add the video preview to the details panel + caption field
|
||||
|
||||
### ⚡️ Optimizations
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface IDetailsSlideOverProps {
|
||||
media: MediaInfo;
|
||||
showForm: boolean;
|
||||
isImageFile: boolean;
|
||||
isVideoFile: boolean;
|
||||
onEdit: () => void;
|
||||
onEditClose: () => void;
|
||||
onDismiss: () => void;
|
||||
@@ -38,7 +39,8 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
onEdit,
|
||||
onEditClose,
|
||||
onDismiss,
|
||||
isImageFile
|
||||
isImageFile,
|
||||
isVideoFile
|
||||
}: React.PropsWithChildren<IDetailsSlideOverProps>) => {
|
||||
const [filename, setFilename] = React.useState<string>(media.filename);
|
||||
const [caption, setCaption] = React.useState<string | undefined>(media.caption);
|
||||
@@ -136,9 +138,19 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
<div className="relative mt-6 flex-1 px-4 sm:px-6">
|
||||
<div className="absolute inset-0 px-4 sm:px-6 space-y-8">
|
||||
<div>
|
||||
{isImageFile && (
|
||||
{(isImageFile || isVideoFile) && (
|
||||
<div className={`block w-full aspect-w-10 aspect-h-7 overflow-hidden border rounded border-[var(--frontmatter-border)] bg-[var(--vscode-editor-background)]`}>
|
||||
<img src={imgSrc} alt={media.filename} className="object-cover max-h-[300px] mx-auto" />
|
||||
{
|
||||
isImageFile && (
|
||||
<img src={imgSrc} alt={media.filename} className="object-cover max-h-[300px] mx-auto" />
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
isVideoFile && (
|
||||
<video src={media.vsPath} className="mx-auto object-cover" controls muted />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-4 flex items-start justify-between">
|
||||
@@ -211,25 +223,25 @@ export const DetailsSlideOver: React.FunctionComponent<IDetailsSlideOverProps> =
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(isImageFile || isVideoFile) && (
|
||||
<div>
|
||||
<label className={`block text-sm font-medium text-[var(--vscode-editor-foreground)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonCaption)}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<DetailsInput value={caption || ""} onChange={(e) => setCaption(e.target.value)} isTextArea />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isImageFile && (
|
||||
<>
|
||||
<div>
|
||||
<label className={`block text-sm font-medium text-[var(--vscode-editor-foreground)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonCaption)}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<DetailsInput value={caption || ""} onChange={(e) => setCaption(e.target.value)} isTextArea />
|
||||
</div>
|
||||
<div>
|
||||
<label className={`block text-sm font-medium text-[var(--vscode-editor-foreground)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonAlt)}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<DetailsInput value={alt || ""} onChange={(e) => setAlt(e.target.value)} isTextArea />
|
||||
</div>
|
||||
<div>
|
||||
<label className={`block text-sm font-medium text-[var(--vscode-editor-foreground)]`}>
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonAlt)}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<DetailsInput value={alt || ""} onChange={(e) => setAlt(e.target.value)} isTextArea />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -701,6 +701,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
media={media}
|
||||
showForm={showForm}
|
||||
isImageFile={isImageFile}
|
||||
isVideoFile={isVideoFile}
|
||||
onEdit={() => setShowForm(true)}
|
||||
onEditClose={() => setShowForm(false)}
|
||||
onDismiss={() => {
|
||||
|
||||
@@ -350,6 +350,8 @@ export class MediaHelpers {
|
||||
try {
|
||||
await unlinkAsync(file);
|
||||
|
||||
MediaLibrary.getInstance().remove(file);
|
||||
|
||||
MediaHelpers.media = [];
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -18,6 +18,7 @@ interface MediaRecord {
|
||||
export class MediaLibrary {
|
||||
private db: JsonDB | undefined;
|
||||
private renameFilesListener: Disposable | undefined;
|
||||
private removeFilesListener: Disposable | undefined;
|
||||
private static instance: MediaLibrary;
|
||||
|
||||
private constructor() {
|
||||
@@ -74,20 +75,26 @@ export class MediaLibrary {
|
||||
this.renameFilesListener = workspace.onDidRenameFiles((e) => {
|
||||
e.files.forEach(async (f) => {
|
||||
const path = f.oldUri.path.toLowerCase();
|
||||
const mimeType = lookup(path);
|
||||
// Check if file is an image
|
||||
if (
|
||||
mimeType &&
|
||||
(mimeType.startsWith('image/') ||
|
||||
mimeType.startsWith('video/') ||
|
||||
mimeType.startsWith('audio/') ||
|
||||
mimeType.startsWith('application/pdf'))
|
||||
) {
|
||||
if (MediaLibrary.isMediaFile(path)) {
|
||||
await this.rename(f.oldUri.fsPath, f.newUri.fsPath);
|
||||
MediaHelpers.resetMedia();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (this.removeFilesListener) {
|
||||
this.removeFilesListener.dispose();
|
||||
}
|
||||
|
||||
this.removeFilesListener = workspace.onDidDeleteFiles((e) => {
|
||||
e.files.forEach(async (f) => {
|
||||
const path = f.path.toLowerCase();
|
||||
if (MediaLibrary.isMediaFile(path)) {
|
||||
this.remove(f.fsPath);
|
||||
MediaHelpers.resetMedia();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static getInstance(): MediaLibrary {
|
||||
@@ -138,6 +145,11 @@ export class MediaLibrary {
|
||||
}
|
||||
}
|
||||
|
||||
public async remove(path: string): Promise<void> {
|
||||
const fileId = this.parsePath(path);
|
||||
await this.db?.delete(fileId);
|
||||
}
|
||||
|
||||
public async updateFilename(filePath: string, filename: string) {
|
||||
const name = basename(filePath);
|
||||
|
||||
@@ -167,4 +179,19 @@ export class MediaLibrary {
|
||||
absPath = isWindows ? absPath.split('\\').join('/') : absPath;
|
||||
return absPath.toLowerCase();
|
||||
}
|
||||
|
||||
private static isMediaFile(path: string) {
|
||||
const mimeType = lookup(path);
|
||||
// Check if file is an image
|
||||
if (
|
||||
mimeType &&
|
||||
(mimeType.startsWith('image/') ||
|
||||
mimeType.startsWith('video/') ||
|
||||
mimeType.startsWith('audio/') ||
|
||||
mimeType.startsWith('application/pdf'))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user