diff --git a/src/utils/message-method.ts b/src/utils/message-method.ts index 41ab919..ec58345 100644 --- a/src/utils/message-method.ts +++ b/src/utils/message-method.ts @@ -8,7 +8,9 @@ export enum W2VMessage { EXECUTE_COMMAND = 'EXECUTE_COMMAND', GET_ALL_UPLOADERS = 'GET_ALL_UPLOADERS', GET_ALL_UPLOADER_CONFIGS = 'GET_ALL_UPLOADER_CONFIGS', - SET_CONFIG = 'SET_CONFIG' + SET_CONFIG = 'SET_CONFIG', + REVEAL_FILE_IN_OS = 'REVEAL_FILE_IN_OS', + GET_PICGO_SETTINGS = 'GET_PICGO_SETTINGS' } /** diff --git a/src/vscode/CommandManager.ts b/src/vscode/CommandManager.ts index cbe2165..449f3d5 100644 --- a/src/vscode/CommandManager.ts +++ b/src/vscode/CommandManager.ts @@ -13,10 +13,10 @@ export class CommandManager { const pluginName = 'vspicgo' PicgoAPI.picgoAPI.setCurrentPluginName(pluginName) const [id, plugin] = PicgoAddon.picgoAddon.beforeUploadPlugin() - PicgoAPI.picgoAPI.helper.beforeUploadPlugins.register(id, plugin) + PicgoAPI.picgoAPI.picgo.helper.beforeUploadPlugins.register(id, plugin) const output = await PicgoAPI.picgoAPI.upload(input) - PicgoAPI.picgoAPI.helper.beforeUploadPlugins.unregister(pluginName) + PicgoAPI.picgoAPI.picgo.helper.beforeUploadPlugins.unregister(pluginName) // error has been handled in picgoAPI.upload if (!output) return diff --git a/src/vscode/PicgoAPI.ts b/src/vscode/PicgoAPI.ts index ed8e23e..5376b2b 100644 --- a/src/vscode/PicgoAPI.ts +++ b/src/vscode/PicgoAPI.ts @@ -1,41 +1,52 @@ -import { IConfig, PicGo, IHelper, LifecyclePlugins, IPluginConfig } from 'picgo' +import { IConfig, PicGo, LifecyclePlugins, IPluginConfig } from 'picgo' import { DataStore } from './DataStore' import vscode from 'vscode' import { decorateMessage, showError, showInfo } from './utils' -import { defaultSettings } from './settings' -import _ from 'lodash-es' +import { defaultSettings, IPicGoSettings } from './settings' +import _, { isObject } from 'lodash-es' import { Get } from 'type-fest' export type GetConfig = Get export interface IUploaderConfig { uploaderName: string uploaderID: string + /** + * The current list of all configurations of the uploader. An uploader will have a `IPlugin.config` function to determine the current configurations. Usually it will be calculated as something like `default: userConfig.xxx || 'github'`, that is, read the picgo core configuration first and set something as fallback + */ configList?: IPluginConfig[] } export class PicgoAPI { static picgoAPI = new PicgoAPI() - private readonly picgo: PicGo - helper: IHelper + readonly picgo: PicGo constructor() { this.picgo = new PicGo(DataStore.dataStore.configPath) this.picgo.saveConfig({ debug: true }) this.initConfig() - this.helper = this.picgo.helper } initConfig() { - this.setConfigIfNotExist( - 'settings.vsPicgo.customOutputFormat', - defaultSettings.settings.vsPicgo.customOutputFormat - ) - this.setConfigIfNotExist( - 'settings.vsPicgo.customUploadName', - defaultSettings.settings.vsPicgo.customUploadName - ) + const dfs = (val: any, path: string[]) => { + if (isObject(val)) { + for (const key of Object.keys(val)) { + dfs((val as any)[key], [...path, key]) + } + } else { + // Leaf condition is a non-object value, we init this value if it does not exist in the config file + this.setConfigIfNotExist(path.join('.'), val) + // for example: + // this.setConfigIfNotExist( + // 'settings.vsPicgo.customOutputFormat', + // defaultSettings.settings.vsPicgo.customOutputFormat + // ) + } + } + + // Init all values in the `defaultSettings` object + dfs(defaultSettings, []) } setConfigIfNotExist(configName: T, value: GetConfig) { @@ -93,6 +104,15 @@ export class PicgoAPI { }) } + getPicGoSettings(): IPicGoSettings { + const config = this.picgo.getConfig() + // Although there is no configuration in the config file, we should let user know the current default log level and log path etc. + config.settings.logLevel ??= this.picgo.log.logLevel + config.settings.logPath ??= this.picgo.log.logPath + + return config + } + /** * @param input This image file paths to be uploaded, will upload from clipboard if no input specified */ diff --git a/src/vscode/settings.ts b/src/vscode/settings.ts index 217c322..794df7b 100644 --- a/src/vscode/settings.ts +++ b/src/vscode/settings.ts @@ -1,13 +1,25 @@ /* eslint-disable no-template-curly-in-string */ -export interface IPicgoSettings { +import { PartialDeep } from 'type-fest' +import { IConfig, LogLevel } from 'picgo' +/** + * All this settings are configurable in the settings page of the picgo control panel + */ +export interface IPicGoSettings extends IConfig { settings: { vsPicgo: { customUploadName: string customOutputFormat: string } + logLevel: LogLevel | LogLevel[] + logPath: string + } + picBed: { + current: string + uploader: string } } +// TODO: add a select in settings page for user to select this presets export const outputFormats = { Markdown: '![${uploadedName}](${url})', HTML: '${uploadedName}', @@ -16,17 +28,16 @@ export const outputFormats = { } export const uploadName = { - auto: + SelectionOrOriginalName: '${editorSelectionText ? `${editorSelectionText}${imgIdx}` : fileName}${extName}', - dateExt: '${date}${extName}' + Date: '${date}${extName}' } -export const defaultSettings: IPicgoSettings = { +export const defaultSettings: PartialDeep = { settings: { vsPicgo: { customOutputFormat: outputFormats.Markdown, - customUploadName: - '${editorSelectionText ? `${editorSelectionText}${imgIdx}` : fileName}${extName}' + customUploadName: uploadName.SelectionOrOriginalName } } } diff --git a/src/vscode/utils/channel.ts b/src/vscode/utils/channel.ts index dbecb94..06026d0 100644 --- a/src/vscode/utils/channel.ts +++ b/src/vscode/utils/channel.ts @@ -57,5 +57,16 @@ export const getChannel = ( } ) + channel.bind(W2VMessage.REVEAL_FILE_IN_OS, async (path) => { + return await vscode.commands.executeCommand( + 'revealFileInOS', + vscode.Uri.file(path) + ) + }) + + channel.bind(W2VMessage.GET_PICGO_SETTINGS, () => { + return PicgoAPI.picgoAPI.getPicGoSettings() + }) + return channel } diff --git a/src/webview/pages/PicGoControlPanel/PicGoDrawerList/index.tsx b/src/webview/pages/PicGoControlPanel/PicGoDrawerList/index.tsx index f6e8efa..a426668 100644 --- a/src/webview/pages/PicGoControlPanel/PicGoDrawerList/index.tsx +++ b/src/webview/pages/PicGoControlPanel/PicGoDrawerList/index.tsx @@ -60,7 +60,7 @@ export const PicGoDrawerList = () => { my: 0.5 }}> - + {uploaderListOpened ? ( @@ -86,6 +86,19 @@ export const PicGoDrawerList = () => { ))} + + history.push('/settings/vs-picgo')} + selected={pathname === '/settings/vs-picgo'} + sx={{ + borderRadius: 4, + my: 0.5 + }}> + + + + + ) } diff --git a/src/webview/pages/PicGoControlPanel/PicGoSettings/PicGoPluginConfigForm.tsx b/src/webview/pages/PicGoControlPanel/PicGoSettings/PicGoPluginConfigForm.tsx index b849e57..f233f5f 100644 --- a/src/webview/pages/PicGoControlPanel/PicGoSettings/PicGoPluginConfigForm.tsx +++ b/src/webview/pages/PicGoControlPanel/PicGoSettings/PicGoPluginConfigForm.tsx @@ -4,7 +4,6 @@ import { Grid, Paper, TextField, - Select, MenuItem, Typography, Box, @@ -84,12 +83,15 @@ export const PicGoPluginConfigForm: React.FC = ({ /> } label={config.name} + sx={{ + my: 1 + }} /> ) case 'list': case 'checkbox': return ( - + ) default: return ( diff --git a/src/webview/pages/PicGoControlPanel/PicGoSettings/VSPicGoSettings.tsx b/src/webview/pages/PicGoControlPanel/PicGoSettings/VSPicGoSettings.tsx new file mode 100644 index 0000000..0c8105d --- /dev/null +++ b/src/webview/pages/PicGoControlPanel/PicGoSettings/VSPicGoSettings.tsx @@ -0,0 +1,79 @@ +import React from 'react' +import { useRouteMatch } from 'react-router' +import { + Grid, + Paper, + FormControl, + InputLabel, + OutlinedInput, + InputAdornment, + IconButton +} from '@mui/material' +import * as MuiIconsMaterial from '@mui/icons-material' +import { useAsync } from 'react-use' +import { useDispatch, useState } from '../hooks' +import { + getPicGoSettings, + revealFileInOS, + setConfig +} from '../../../utils/channel' + +/** + * This page contains all the settings in the IPicgoSettings.settings.vsPicgo, and application wide configuration such current uploader in the `picBed.uploader` field + */ +export const VSPicGoSettings: React.FC = () => { + const match = useRouteMatch('/settings/vs-picgo') + if (!match) return null + + const dispatch = useDispatch('settings') + useAsync(async () => { + dispatch.setPicGoSettings(await getPicGoSettings()) + }, []) + + const { picgoSettings } = useState('settings') + + return ( + + + {/* Log file settings */} + + Log file path + + { + const logPath = picgoSettings?.settings.logPath + if (!logPath) return + revealFileInOS(logPath) + }} + title="Reveal log file in system file explorer"> + + + + } + fullWidth + id="log-file-path" + label="Log file path" + onChange={async (e) => { + await setConfig('settings.logPath', e.target.value) + dispatch.setPicGoSettings(await getPicGoSettings()) + }} + value={picgoSettings?.settings.logPath ?? 'N/A'} + /> + + + + ) +} diff --git a/src/webview/pages/PicGoControlPanel/PicGoSettings/index.tsx b/src/webview/pages/PicGoControlPanel/PicGoSettings/index.tsx index 7f8742f..bdfda88 100644 --- a/src/webview/pages/PicGoControlPanel/PicGoSettings/index.tsx +++ b/src/webview/pages/PicGoControlPanel/PicGoSettings/index.tsx @@ -3,6 +3,7 @@ import React from 'react' import { PicGoPluginConfigForm } from './PicGoPluginConfigForm' import { useState, useDispatch } from '../hooks' import { useParams } from 'react-router-dom' +import { VSPicGoSettings } from './VSPicGoSettings' export interface IPicGoSettingsParams { uploaderID?: string @@ -17,6 +18,8 @@ export const PicGoSettings: React.FC = () => { return ( + {/* This vs-picgo Settings page */} + {/* If it is a uploader config page */} {params.uploaderID ? (() => { diff --git a/src/webview/pages/PicGoControlPanel/index.tsx b/src/webview/pages/PicGoControlPanel/index.tsx index 6369cd1..eed0007 100644 --- a/src/webview/pages/PicGoControlPanel/index.tsx +++ b/src/webview/pages/PicGoControlPanel/index.tsx @@ -28,6 +28,9 @@ export const PicGoControlPanelInner = () => { + + + diff --git a/src/webview/pages/PicGoControlPanel/models/settings.ts b/src/webview/pages/PicGoControlPanel/models/settings.ts index eecca93..08f35fd 100644 --- a/src/webview/pages/PicGoControlPanel/models/settings.ts +++ b/src/webview/pages/PicGoControlPanel/models/settings.ts @@ -1,5 +1,6 @@ import { createModel } from '@rematch/core' import type { IUploaderConfig } from '../../../../vscode/PicgoAPI' +import type { IPicGoSettings } from '../../../../vscode/settings' import { IRootModel } from '../models' import { setConfig } from '../../../utils/channel' export interface IPicGoSettingState { @@ -13,6 +14,7 @@ export interface IPicGoSettingState { * currentUploaderConfigs[uploaderID][config.name] = value */ currentUploaderConfigs: Record> + picgoSettings: IPicGoSettings | null } export interface IUpdateCurrentUploaderConfigsPayload { @@ -21,13 +23,14 @@ export interface IUpdateCurrentUploaderConfigsPayload { value: any } -export const defaultCommonState: IPicGoSettingState = { +export const defaultSettingsState: IPicGoSettingState = { allUploaderConfigs: [], - currentUploaderConfigs: {} + currentUploaderConfigs: {}, + picgoSettings: null } export const settings = createModel()({ - state: defaultCommonState, + state: defaultSettingsState, reducers: { /** * This reducer should be called on paged loaded so that we have the initial `currentUploaderConfigs` state for page to render correctly, such as the uploader list in the drawer and the whole settings page. @@ -51,6 +54,10 @@ export const settings = createModel()({ ) { setConfig(`picBed.${uploaderID}.${configName}`, value) state.currentUploaderConfigs[uploaderID][configName] = value + }, + + setPicGoSettings(state, picgoSettings: IPicGoSettings) { + state.picgoSettings = picgoSettings } }, selectors: (slice, createSelector, hasProps) => ({ diff --git a/src/webview/utils/channel.ts b/src/webview/utils/channel.ts index c13b444..a577c9e 100644 --- a/src/webview/utils/channel.ts +++ b/src/webview/utils/channel.ts @@ -3,6 +3,7 @@ import { IMessageToShow } from '../../utils' import { W2VMessage } from '../../utils/message-method' import { IImgInfo } from 'picgo' import type { IUploaderConfig, PicgoAPI } from '../../vscode/PicgoAPI' +import type { IPicGoSettings } from '../../vscode/settings' export const channel = new Channel() @@ -40,3 +41,14 @@ export const getAllUploaderConfigs = async () => /** Set picgo config */ export const setConfig = async (...args: Parameters) => await channel.call(W2VMessage.SET_CONFIG, args) + +/** + * Reveal a file in system file explorer + */ +export const revealFileInOS = async (path: string) => { + return await channel.call(W2VMessage.REVEAL_FILE_IN_OS, path) +} + +/** Get picgo configurations and settings */ +export const getPicGoSettings = () => + channel.call(W2VMessage.GET_PICGO_SETTINGS) diff --git a/test/suite/extension.test.ts b/test/suite/extension.test.ts index 3a9a417..1fbe6c8 100644 --- a/test/suite/extension.test.ts +++ b/test/suite/extension.test.ts @@ -26,7 +26,7 @@ describe('VSPicgo', async function () { this.timeout(1e10) PicgoAPI.picgoAPI.setConfig( 'settings.vsPicgo.customUploadName', - uploadName.auto + uploadName.SelectionOrOriginalName ) const res = await VSPicgoUploadStarter({ args4uploader: [TEST_PICTURE_PATH], @@ -43,7 +43,7 @@ describe('VSPicgo', async function () { this.timeout(1e10) PicgoAPI.picgoAPI.setConfig( 'settings.vsPicgo.customUploadName', - uploadName.dateExt + uploadName.Date ) const res = await VSPicgoUploadStarter({ args4uploader: [TEST_PICTURE_PATH], @@ -60,7 +60,7 @@ describe('VSPicgo', async function () { this.timeout(1e10) PicgoAPI.picgoAPI.setConfig( 'settings.vsPicgo.customUploadName', - uploadName.auto + uploadName.SelectionOrOriginalName ) const res = await VSPicgoUploadStarter({ args4uploader: [TEST_PICTURE_PATH],