#615 - Added relative option to the public folder setting

This commit is contained in:
Elio Struyf
2023-08-06 15:00:31 -04:00
parent 5ad545ae5c
commit 598785570f
24 changed files with 213 additions and 272 deletions
+17 -18
View File
@@ -9,7 +9,7 @@ import {
} from './../constants';
import { commands, Uri, workspace, window } from 'vscode';
import { basename, dirname, join, relative, sep } from 'path';
import { ContentFolder, FileInfo, FolderInfo } from '../models';
import { ContentFolder, FileInfo, FolderInfo, StaticFolder } from '../models';
import uniqBy = require('lodash.uniqby');
import { Template } from './Template';
import { Notifications } from '../helpers/Notifications';
@@ -56,13 +56,6 @@ export class Folders {
);
}
if (startPath.includes(STATIC_FOLDER_PLACEHOLDER.astro.placeholder)) {
startPath = startPath.replace(
STATIC_FOLDER_PLACEHOLDER.astro.placeholder,
STATIC_FOLDER_PLACEHOLDER.astro.assetsFolder
);
}
const folderName = await window.showInputBox({
title: `Add media folder`,
prompt: `Which name would you like to give to your folder (use "/" to create multi-level folders)?`,
@@ -179,26 +172,32 @@ export class Folders {
* @returns
*/
public static getStaticFolderRelativePath(): string | undefined {
let staticFolder = Settings.get<string>(SETTING_CONTENT_STATIC_FOLDER);
const staticFolder = Settings.get<string | StaticFolder>(SETTING_CONTENT_STATIC_FOLDER);
let assetFolder: string | undefined;
if (staticFolder && typeof staticFolder !== 'string' && staticFolder.path) {
assetFolder = staticFolder.path;
} else if (staticFolder && typeof staticFolder === 'string') {
assetFolder = staticFolder;
}
if (
staticFolder &&
(staticFolder.includes(WORKSPACE_PLACEHOLDER) ||
staticFolder === '/' ||
staticFolder === './')
assetFolder &&
(assetFolder.includes(WORKSPACE_PLACEHOLDER) || assetFolder === '/' || assetFolder === './')
) {
staticFolder =
staticFolder === '/' || staticFolder === './'
assetFolder =
assetFolder === '/' || assetFolder === './'
? Folders.getAbsFilePath(WORKSPACE_PLACEHOLDER)
: Folders.getAbsFilePath(staticFolder);
: Folders.getAbsFilePath(assetFolder);
const wsFolder = Folders.getWorkspaceFolder();
if (wsFolder) {
const relativePath = relative(parseWinPath(wsFolder.fsPath), parseWinPath(staticFolder));
const relativePath = relative(parseWinPath(wsFolder.fsPath), parseWinPath(assetFolder));
return relativePath === '' ? '/' : relativePath;
}
}
return staticFolder;
return assetFolder;
}
/**
+1 -1
View File
@@ -3,7 +3,7 @@ export const FrameworkDetectors = [
framework: {
name: 'astro',
dist: 'dist',
static: 'public',
static: ['public', 'src/assets'],
build: 'npm run build',
server: 'http://localhost:3000'
},
-4
View File
@@ -2,9 +2,5 @@ export const STATIC_FOLDER_PLACEHOLDER = {
hexo: {
postsFolder: 'source/_posts',
placeholder: 'hexo:post_asset_folder'
},
astro: {
placeholder: 'astro:assets',
assetsFolder: 'src/assets'
}
};
+1
View File
@@ -12,6 +12,7 @@ export enum DashboardMessage {
initializeProject = 'initializeProject',
setFramework = 'setFramework',
addFolder = 'addFolder',
addAssetsFolder = 'addAssetsFolder',
// Content dashboard
getData = 'getData',
@@ -33,12 +33,8 @@ export const Breadcrumb: React.FunctionComponent<IBreadcrumbProps> = (
const { wsFolder, staticFolder, contentFolders } = settings;
const isValid = (folderPath: string) => {
let crntStaticFolder = staticFolder;
if (staticFolder === STATIC_FOLDER_PLACEHOLDER.astro.placeholder) {
crntStaticFolder = STATIC_FOLDER_PLACEHOLDER.astro.assetsFolder;
}
if (crntStaticFolder) {
const staticPath = parseWinPath(join(wsFolder, crntStaticFolder)) as string;
if (staticFolder) {
const staticPath = parseWinPath(join(wsFolder, staticFolder)) as string;
const relPath = folderPath.replace(staticPath, '') as string;
if (relPath.length > 1 && folderPath.startsWith(staticPath)) {
@@ -47,8 +47,6 @@ export const Media: React.FunctionComponent<IMediaProps> = (
let staticFolderPath = join('/', settings?.staticFolder || '', '/');
if (settings?.staticFolder === STATIC_FOLDER_PLACEHOLDER.hexo.placeholder) {
staticFolderPath = join('/', STATIC_FOLDER_PLACEHOLDER.hexo.postsFolder, '/');
} else if (settings?.staticFolder === STATIC_FOLDER_PLACEHOLDER.astro.placeholder) {
staticFolderPath = join('/', STATIC_FOLDER_PLACEHOLDER.astro.assetsFolder, '/');
}
return staticFolderPath;
}
@@ -62,8 +60,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
viewData.data &&
typeof viewData.data.pageBundle !== 'undefined' &&
!viewData.data.pageBundle &&
settings?.staticFolder !== STATIC_FOLDER_PLACEHOLDER.hexo.placeholder &&
settings?.staticFolder !== STATIC_FOLDER_PLACEHOLDER.astro.placeholder
settings?.staticFolder !== STATIC_FOLDER_PLACEHOLDER.hexo.placeholder
) {
return [];
}
@@ -0,0 +1,36 @@
import * as React from 'react';
import { CheckCircleIcon } from '@heroicons/react/outline';
import { CheckCircleIcon as CheckCircleIconSolid } from '@heroicons/react/solid';
export interface ISelectItemProps {
title: string;
buttonTitle: string;
isSelected: boolean;
onClick: () => void;
}
export const SelectItem: React.FunctionComponent<ISelectItemProps> = ({
title,
buttonTitle,
isSelected,
onClick
}: React.PropsWithChildren<ISelectItemProps>) => {
return (
<div
className={`text-sm flex items-center ${isSelected ? 'text-[var(--vscode-textLink-foreground)]' : ''}`}
>
<button
onClick={onClick}
className={`mr-2 flex gap-2 items-center hover:text-[var(--vscode-textLink-activeForeground)]`}
title={buttonTitle}
>
{isSelected ? (
<CheckCircleIconSolid className={`h-4 w-4`} />
) : (
<CheckCircleIcon className={`h-4 w-4`} />
)}
<span>{title}</span>
</button>
</div>
);
};
@@ -7,14 +7,14 @@ import { Step } from './Step';
import { useMemo, useState } from 'react';
import { Menu } from '@headlessui/react';
import { MenuItem } from '../Menu';
import { ContentFolder, Framework } from '../../../models';
import { CheckCircleIcon, ChevronDownIcon } from '@heroicons/react/outline';
import { CheckCircleIcon as CheckCircleIconSolid } from '@heroicons/react/solid';
import { ContentFolder, Framework, StaticFolder } from '../../../models';
import { ChevronDownIcon } from '@heroicons/react/outline';
import { FrameworkDetectors } from '../../../constants/FrameworkDetectors';
import { join } from 'path';
import useThemeColors from '../../hooks/useThemeColors';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { SelectItem } from './SelectItem';
export interface IStepsToGetStartedProps {
settings: Settings;
@@ -31,7 +31,6 @@ const Folder = ({
folders: ContentFolder[];
addFolder: (folder: string) => void;
}) => {
const { getColors } = useThemeColors();
const isAdded = useMemo(
() => folders.find((f) => f.path.toLowerCase() === join(wsFolder, folder).toLowerCase()),
@@ -39,23 +38,11 @@ const Folder = ({
);
return (
<div
className={`text-sm flex items-center ${isAdded ? getColors('text-teal-800', 'text-[var(--vscode-textLink-foreground)]') : getColors('text-vulcan-300 dark:text-whisper-800', '')
}`}
>
<button
onClick={() => addFolder(folder)}
className={`mr-2 flex gap-2 items-center ${getColors('hover:text-teal-500', 'hover:text-[var(--vscode-textLink-activeForeground)]')}`}
title={l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedButtonAddFolderTitle)}
>
{isAdded ? (
<CheckCircleIconSolid className={`h-4 w-4`} />
) : (
<CheckCircleIcon className={`h-4 w-4`} />
)}
<span>{folder}</span>
</button>
</div>
<SelectItem
title={folder}
buttonTitle={l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedButtonAddFolderTitle)}
isSelected={!!isAdded}
onClick={() => addFolder(folder)} />
);
};
@@ -77,6 +64,10 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
Messenger.send(DashboardMessage.addFolder, folder);
};
const addAssetFolder = (folder: string | StaticFolder) => {
Messenger.send(DashboardMessage.addAssetsFolder, folder);
}
const reload = () => {
const crntState: any = Messenger.getState() || {};
@@ -98,6 +89,7 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
id: `welcome-init`,
name: l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedInitializeProjectName),
description: <>{l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedInitializeProjectDescription)}</>,
show: true,
status: settings.initialized ? Status.Completed : Status.NotStarted,
onClick: settings.initialized
? undefined
@@ -164,9 +156,40 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
</Menu>
</div>
),
show: true,
status: settings.crntFramework ? Status.Completed : Status.NotStarted,
onClick: undefined
},
{
id: `welcome-assets`,
name: l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedAssetsFolderName),
description: (
<div className='mt-4'>
<div className="text-sm">{l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedAssetsFolderDescription)}</div>
<div className="mt-1 space-y-1">
<SelectItem
title={l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedAssetsFolderPublicTitle)}
buttonTitle={l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedAssetsFolderPublicTitle)}
isSelected={settings.staticFolder === "public"}
onClick={() => addAssetFolder(`public`)} />
<SelectItem
title={l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedAssetsFolderAssetsTitle)}
buttonTitle={l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedAssetsFolderAssetsTitle)}
isSelected={settings.staticFolder === "src/assets"}
onClick={() => addAssetFolder({
"path": "src/assets",
"relative": true
})} />
<p className='text-sm'>
<b>{l10n.t(LocalizationKey.commonInformation)}</b>: {l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedAssetsFolderOtherDescription)}</p>
</div>
</div>
),
show: settings.crntFramework === 'astro' || framework === 'astro',
status: !settings.staticFolder ? Status.NotStarted : Status.Completed,
},
{
id: `welcome-content-folders`,
name: l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedContentFoldersName),
@@ -198,6 +221,7 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
</p>
</>
),
show: true,
status:
settings.contentFolders && settings.contentFolders.length > 0
? Status.Completed
@@ -207,6 +231,7 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
id: `welcome-import`,
name: l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedTagsName),
description: <>{l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedTagsDescription)}</>,
show: true,
status: taxImported ? Status.Completed : Status.NotStarted,
onClick:
settings.contentFolders && settings.contentFolders.length > 0 ? importTaxonomy : undefined
@@ -215,6 +240,7 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
id: `welcome-show-dashboard`,
name: l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedShowDashboardName),
description: <>{l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedShowDashboardDescription)}</>,
show: true,
status:
settings.initialized && settings.contentFolders && settings.contentFolders.length > 0
? Status.Active
@@ -236,19 +262,21 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
<nav aria-label="Progress">
<ol role="list">
{steps.map((step, stepIdx) => (
<li
key={step.id}
className={`${stepIdx !== steps.length - 1 ? 'pb-10' : ''} relative`}
data-test={step.id}
>
<Step
name={step.name}
description={step.description}
status={step.status}
showLine={stepIdx !== steps.length - 1}
onClick={step.onClick}
/>
</li>
step.show && (
<li
key={step.id}
className={`${stepIdx !== steps.length - 1 ? 'pb-10' : ''} relative`}
data-test={step.id}
>
<Step
name={step.name}
description={step.description}
status={step.status}
showLine={stepIdx !== steps.length - 1}
onClick={step.onClick}
/>
</li>
)
))}
</ol>
</nav>
+9 -3
View File
@@ -11,7 +11,7 @@ import {
STATIC_FOLDER_PLACEHOLDER
} from '../constants';
import { FrameworkDetectors } from '../constants/FrameworkDetectors';
import { Framework } from '../models';
import { Framework, StaticFolder } from '../models';
import { Logger } from './Logger';
import { existsAsync, readFileAsync } from '../utils';
import { Settings } from '.';
@@ -102,6 +102,7 @@ export class FrameworkDetector {
* @param filePath
*/
public static relAssetPathUpdate(relAssetPath: string, filePath: string): string {
const staticFolderValue = Settings.get<string | StaticFolder>(SETTING_CONTENT_STATIC_FOLDER);
const staticFolder = Folders.getStaticFolderRelativePath();
const frameworkId = Settings.get(SETTING_FRAMEWORK_ID);
@@ -129,9 +130,14 @@ export class FrameworkDetector {
}
}
// Support for the Astro assets folder
else if (staticFolder === STATIC_FOLDER_PLACEHOLDER.astro.placeholder) {
else if (
staticFolder &&
staticFolderValue &&
typeof staticFolderValue !== 'string' &&
staticFolderValue.relative
) {
const absAssetPath = parseWinPath(
join(Folders.getWorkspaceFolder()?.fsPath || '', relAssetPath)
join(Folders.getWorkspaceFolder()?.fsPath || '', staticFolder, relAssetPath)
);
const fileDir = dirname(filePath);
+1 -16
View File
@@ -109,11 +109,7 @@ export class MediaHelpers {
allMedia = [...media];
} else {
if (
staticFolder &&
staticFolder !== STATIC_FOLDER_PLACEHOLDER.hexo.placeholder &&
staticFolder !== STATIC_FOLDER_PLACEHOLDER.astro.placeholder
) {
if (staticFolder && staticFolder !== STATIC_FOLDER_PLACEHOLDER.hexo.placeholder) {
const folderSearch = join(staticFolder || '', '/*');
const files = await workspace.findFiles(folderSearch);
const media = await MediaHelpers.updateMediaData(MediaHelpers.filterMedia(files));
@@ -124,12 +120,6 @@ export class MediaHelpers {
const files = await workspace.findFiles(folderSearch);
const media = await MediaHelpers.updateMediaData(MediaHelpers.filterMedia(files));
allMedia = [...media];
} else if (staticFolder && staticFolder === STATIC_FOLDER_PLACEHOLDER.astro.placeholder) {
const folderSearch = join(STATIC_FOLDER_PLACEHOLDER.astro.assetsFolder, '/*');
const files = await workspace.findFiles(folderSearch);
const media = await MediaHelpers.updateMediaData(MediaHelpers.filterMedia(files));
allMedia = [...media];
}
@@ -235,11 +225,6 @@ export class MediaHelpers {
parseWinPath(wsFolder?.fsPath || ''),
STATIC_FOLDER_PLACEHOLDER.hexo.postsFolder
);
} else if (staticFolder === STATIC_FOLDER_PLACEHOLDER.astro.placeholder) {
staticPath = join(
parseWinPath(wsFolder?.fsPath || ''),
STATIC_FOLDER_PLACEHOLDER.astro.assetsFolder
);
}
if (staticPath && (await existsAsync(staticPath))) {
+10 -2
View File
@@ -12,7 +12,7 @@ import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
import { DashboardSettings, Extension, Settings } from '../../helpers';
import { FrameworkDetector } from '../../helpers/FrameworkDetector';
import { Framework, PostMessageData } from '../../models';
import { Framework, PostMessageData, StaticFolder } from '../../models';
import { BaseListener } from './BaseListener';
import { Cache } from '../../commands/Cache';
import { Preview } from '../../commands';
@@ -43,6 +43,9 @@ export class SettingsListener extends BaseListener {
case DashboardMessage.addFolder:
this.addFolder(msg?.payload);
break;
case DashboardMessage.addAssetsFolder:
this.addAssetsFolder(msg?.payload);
break;
case DashboardMessage.switchProject:
this.switchProject(msg.payload);
break;
@@ -109,7 +112,7 @@ export class SettingsListener extends BaseListener {
(f: Framework) => f.name === frameworkId
);
if (framework) {
if (framework.static) {
if (framework.static && typeof framework.static === 'string') {
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, framework.static, true);
}
@@ -126,6 +129,11 @@ export class SettingsListener extends BaseListener {
SettingsListener.getSettings(true);
}
public static async addAssetsFolder(assetFolder: string | StaticFolder) {
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, assetFolder, true);
SettingsListener.getSettings(true);
}
private static addFolder(folder: string) {
if (folder) {
const wsFolder = Folders.getWorkspaceFolder();
+20
View File
@@ -739,6 +739,26 @@ export enum LocalizationKey {
* other
*/
dashboardStepsStepsToGetStartedFrameworkSelectOther = 'dashboard.steps.stepsToGetStarted.framework.select.other',
/**
* What is your assets folder?
*/
dashboardStepsStepsToGetStartedAssetsFolderName = 'dashboard.steps.stepsToGetStarted.assetsFolder.name',
/**
* Select the folder containing your assets. This folder will be used to store all your media files for your articles.
*/
dashboardStepsStepsToGetStartedAssetsFolderDescription = 'dashboard.steps.stepsToGetStarted.assetsFolder.description',
/**
* Use the 'public' folder
*/
dashboardStepsStepsToGetStartedAssetsFolderPublicTitle = 'dashboard.steps.stepsToGetStarted.assetsFolder.public.title',
/**
* Use the Astro assets folder (src/assets)
*/
dashboardStepsStepsToGetStartedAssetsFolderAssetsTitle = 'dashboard.steps.stepsToGetStarted.assetsFolder.assets.title',
/**
* In case you want to configure another folder, you can do this manually in the frontmatter.json file.
*/
dashboardStepsStepsToGetStartedAssetsFolderOtherDescription = 'dashboard.steps.stepsToGetStarted.assetsFolder.other.description',
/**
* Register content folder(s)
*/
+1 -1
View File
@@ -1,7 +1,7 @@
export interface Framework {
name: string;
dist: string;
static: string;
static: string | string[];
build: string;
server?: string;
}
+4
View File
@@ -0,0 +1,4 @@
export interface StaticFolder {
path: string;
relative?: boolean;
}
+1
View File
@@ -21,6 +21,7 @@ export * from './Snippets';
export * from './SortOrder';
export * from './SortType';
export * from './SortingSetting';
export * from './StaticFolder';
export * from './TaxonomyData';
export * from './TaxonomyType';
export * from './VersionInfo';
@@ -73,7 +73,7 @@ export const ContentTypeValidator: React.FunctionComponent<IContentTypeValidator
</VsLabel>
{l10n.t(LocalizationKey.panelContentTypeContentTypeValidatorHint).split(`\n`).map(s => (<p className="inline_hint">{s}</p>))}
{l10n.t(LocalizationKey.panelContentTypeContentTypeValidatorHint).split(`\n`).map(s => (<p className="inline_hint" key={s}>{s}</p>))}
<div className="hint__buttons">