From 5c131a0b723243be107f4a6fd45b3f2884ed3667 Mon Sep 17 00:00:00 2001 From: terwer Date: Tue, 19 Mar 2024 15:31:05 +0800 Subject: [PATCH] feat: add clipboard image support --- libs/Universal-PicGo-Core/.eslintrc.cjs | 1 + libs/Universal-PicGo-Core/package.json | 1 + .../src/core/Lifecycle.ts | 128 +++++++++++++++++- libs/Universal-PicGo-Core/src/i18n/index.ts | 4 +- libs/Universal-PicGo-Core/src/index.ts | 2 + .../src/lib/PluginLoader.ts | 6 +- .../src/utils/clipboard/browser.ts | 16 +++ .../src/utils/clipboard/electron.ts | 72 ++++++++++ .../src/utils/clipboard/script/linux.sh | 49 +++++++ .../utils/clipboard/script/mac.applescript | 41 ++++++ .../src/utils/clipboard/script/windows.ps1 | 26 ++++ .../src/utils/clipboard/script/windows10.ps1 | 47 +++++++ .../src/utils/clipboard/script/wsl.sh | 18 +++ libs/Universal-PicGo-Core/src/utils/common.ts | 17 +++ .../src/utils/constants.ts | 10 ++ .../src/utils/createContext.ts | 56 ++++++++ .../src/utils/getClipboardImage.ts | 9 +- .../src/utils/os/index.ts | 36 +++++ .../src/utils/os/is-docker/index.ts | 41 ++++++ .../src/utils/os/is-inside-container/index.ts | 32 +++++ .../src/utils/os/is-wsl/index.ts | 35 +++++ .../src/components/home/BrowserIndex.vue | 39 ++++++ .../src/components/home/ElectronIndex.vue | 39 ++++++ .../picgo-plugin-app/src/pages/PicGoIndex.vue | 24 +--- pnpm-lock.yaml | 3 + 25 files changed, 724 insertions(+), 28 deletions(-) create mode 100644 libs/Universal-PicGo-Core/src/utils/clipboard/browser.ts create mode 100644 libs/Universal-PicGo-Core/src/utils/clipboard/electron.ts create mode 100644 libs/Universal-PicGo-Core/src/utils/clipboard/script/linux.sh create mode 100644 libs/Universal-PicGo-Core/src/utils/clipboard/script/mac.applescript create mode 100644 libs/Universal-PicGo-Core/src/utils/clipboard/script/windows.ps1 create mode 100644 libs/Universal-PicGo-Core/src/utils/clipboard/script/windows10.ps1 create mode 100644 libs/Universal-PicGo-Core/src/utils/clipboard/script/wsl.sh create mode 100644 libs/Universal-PicGo-Core/src/utils/constants.ts create mode 100644 libs/Universal-PicGo-Core/src/utils/createContext.ts create mode 100644 libs/Universal-PicGo-Core/src/utils/os/index.ts create mode 100644 libs/Universal-PicGo-Core/src/utils/os/is-docker/index.ts create mode 100644 libs/Universal-PicGo-Core/src/utils/os/is-inside-container/index.ts create mode 100644 libs/Universal-PicGo-Core/src/utils/os/is-wsl/index.ts create mode 100644 packages/picgo-plugin-app/src/components/home/BrowserIndex.vue create mode 100644 packages/picgo-plugin-app/src/components/home/ElectronIndex.vue diff --git a/libs/Universal-PicGo-Core/.eslintrc.cjs b/libs/Universal-PicGo-Core/.eslintrc.cjs index 41f5589..15a8935 100644 --- a/libs/Universal-PicGo-Core/.eslintrc.cjs +++ b/libs/Universal-PicGo-Core/.eslintrc.cjs @@ -16,6 +16,7 @@ module.exports = { "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/ban-types": "off", "turbo/no-undeclared-env-vars": "off", "prettier/prettier": "error", }, diff --git a/libs/Universal-PicGo-Core/package.json b/libs/Universal-PicGo-Core/package.json index 64f2418..d8feccc 100644 --- a/libs/Universal-PicGo-Core/package.json +++ b/libs/Universal-PicGo-Core/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@picgo/i18n": "^1.0.0", + "dayjs": "^1.11.10", "js-yaml": "^4.1.0", "universal-picgo-store": "workspace:*", "zhi-lib-base": "^0.8.0" diff --git a/libs/Universal-PicGo-Core/src/core/Lifecycle.ts b/libs/Universal-PicGo-Core/src/core/Lifecycle.ts index 140e709..61b1728 100644 --- a/libs/Universal-PicGo-Core/src/core/Lifecycle.ts +++ b/libs/Universal-PicGo-Core/src/core/Lifecycle.ts @@ -8,8 +8,11 @@ */ import { EventEmitter } from "../utils/nodePolyfill" -import { IPicGo } from "../types" +import { ILifecyclePlugins, IPicGo, IPlugin, Undefinable } from "../types" import { ILogger } from "zhi-lib-base" +import { createContext } from "../utils/createContext" +import { IBuildInEvent } from "../utils/enums" +import { handleUrlEncode } from "../utils/common" export class Lifecycle extends EventEmitter { private readonly ctx: IPicGo @@ -23,6 +26,127 @@ export class Lifecycle extends EventEmitter { } async start(input: any[]): Promise { - throw new Error("Lifecycle.start is not implemented") + // ensure every upload process has an unique context + const ctx = createContext(this.ctx) + try { + // images input + if (!Array.isArray(input)) { + throw new Error("Input must be an array.") + } + ctx.input = input + ctx.output = [] + + // lifecycle main + await this.beforeTransform(ctx) + await this.doTransform(ctx) + await this.beforeUpload(ctx) + await this.doUpload(ctx) + await this.afterUpload(ctx) + return ctx + } catch (e: any) { + ctx.log.warn(IBuildInEvent.FAILED) + ctx.emit(IBuildInEvent.UPLOAD_PROGRESS, -1) + ctx.emit(IBuildInEvent.FAILED, e) + ctx.log.error(e) + if (ctx.getConfig>("debug")) { + throw e + } + return ctx + } + } + + private async beforeTransform(ctx: IPicGo): Promise { + ctx.emit(IBuildInEvent.UPLOAD_PROGRESS, 0) + ctx.emit(IBuildInEvent.BEFORE_TRANSFORM, ctx) + ctx.log.info("Before transform") + await this.handlePlugins(ctx.helper.beforeTransformPlugins, ctx) + return ctx + } + + private async doTransform(ctx: IPicGo): Promise { + ctx.emit(IBuildInEvent.UPLOAD_PROGRESS, 30) + const type = ctx.getConfig>("picBed.transformer") || "path" + let currentTransformer = type + let transformer = ctx.helper.transformer.get(type) + if (!transformer) { + transformer = ctx.helper.transformer.get("path") + currentTransformer = "path" + ctx.log.warn(`Can't find transformer - ${type}, switch to default transformer - path`) + } + ctx.log.info(`Transforming... Current transformer is [${currentTransformer}]`) + await transformer?.handle(ctx) + return ctx + } + + private async beforeUpload(ctx: IPicGo): Promise { + ctx.emit(IBuildInEvent.UPLOAD_PROGRESS, 60) + ctx.log.info("Before upload") + ctx.emit(IBuildInEvent.BEFORE_UPLOAD, ctx) + await this.handlePlugins(ctx.helper.beforeUploadPlugins, ctx) + return ctx + } + + private async doUpload(ctx: IPicGo): Promise { + let type = + ctx.getConfig>("picBed.uploader") || + ctx.getConfig>("picBed.current") || + "smms" + let uploader = ctx.helper.uploader.get(type) + let currentTransformer = type + if (!uploader) { + type = "smms" + currentTransformer = "smms" + uploader = ctx.helper.uploader.get("smms") + ctx.log.warn(`Can't find uploader - ${type}, switch to default uploader - smms`) + } + ctx.log.info(`Uploading... Current uploader is [${currentTransformer}]`) + await uploader?.handle(ctx) + for (const outputImg of ctx.output) { + outputImg.type = type + } + return ctx + } + + private async afterUpload(ctx: IPicGo): Promise { + ctx.emit(IBuildInEvent.AFTER_UPLOAD, ctx) + ctx.emit(IBuildInEvent.UPLOAD_PROGRESS, 100) + await this.handlePlugins(ctx.helper.afterUploadPlugins, ctx) + let msg = "" + const length = ctx.output.length + // notice, now picgo builtin uploader will encodeOutputURL by default + const isEncodeOutputURL = ctx.getConfig>("settings.encodeOutputURL") === true + for (let i = 0; i < length; i++) { + if (typeof ctx.output[i].imgUrl !== "undefined") { + msg += isEncodeOutputURL ? handleUrlEncode(ctx.output[i].imgUrl!) : ctx.output[i].imgUrl! + if (i !== length - 1) { + msg += "\n" + } + } + delete ctx.output[i].base64Image + delete ctx.output[i].buffer + } + ctx.emit(IBuildInEvent.FINISHED, ctx) + ctx.log.info(`\n${msg}`) + return ctx + } + + // =================================================================================================================== + + private async handlePlugins(lifeCyclePlugins: ILifecyclePlugins, ctx: IPicGo): Promise { + const plugins = lifeCyclePlugins.getList() + const pluginNames = lifeCyclePlugins.getIdList() + const lifeCycleName = lifeCyclePlugins.getName() + await Promise.all( + plugins.map(async (plugin: IPlugin, index: number) => { + try { + ctx.log.info(`${lifeCycleName}: ${pluginNames[index]} running`) + await plugin.handle(ctx) + } catch (e) { + ctx.log.error(`${lifeCycleName}: ${pluginNames[index]} error`) + throw e + } + }) + ) + return ctx } } diff --git a/libs/Universal-PicGo-Core/src/i18n/index.ts b/libs/Universal-PicGo-Core/src/i18n/index.ts index d68f71f..f152e47 100644 --- a/libs/Universal-PicGo-Core/src/i18n/index.ts +++ b/libs/Universal-PicGo-Core/src/i18n/index.ts @@ -90,9 +90,7 @@ class I18nManager implements II18nManager { const fs = win.fs const path = win.require("path") i18nFolder = path.join(this.ctx.baseDir, "i18n-cli") - if (!pathExistsSync(fs, path, i18nFolder)) { - ensureFolderSync(fs, i18nFolder) - } + ensureFolderSync(fs, i18nFolder) } else { i18nFolder = browserPathJoin(this.ctx.baseDir, "i18n-cli", "i18n.json") } diff --git a/libs/Universal-PicGo-Core/src/index.ts b/libs/Universal-PicGo-Core/src/index.ts index e699cf3..79d9188 100644 --- a/libs/Universal-PicGo-Core/src/index.ts +++ b/libs/Universal-PicGo-Core/src/index.ts @@ -1,3 +1,5 @@ import { UniversalPicGo } from "./core/UniversalPicGo" +import { win, hasNodeEnv } from "universal-picgo-store" export { UniversalPicGo } +export { win, hasNodeEnv } diff --git a/libs/Universal-PicGo-Core/src/lib/PluginLoader.ts b/libs/Universal-PicGo-Core/src/lib/PluginLoader.ts index 550a689..ee3279e 100644 --- a/libs/Universal-PicGo-Core/src/lib/PluginLoader.ts +++ b/libs/Universal-PicGo-Core/src/lib/PluginLoader.ts @@ -13,12 +13,14 @@ import { hasNodeEnv, win } from "universal-picgo-store/src" import { readJSONSync } from "../utils/nodeUtils" import { IBuildInEvent } from "../utils/enums" import { setCurrentPluginName } from "./LifecyclePlugins" +import { ILogger } from "zhi-lib-base" /** * Local plugin loader, file system is required */ export class PluginLoader implements IPluginLoader { private readonly ctx: IPicGo + private readonly logger: ILogger private db: PluginLoaderDb private list: string[] = [] private readonly fullList: Set = new Set() @@ -26,6 +28,7 @@ export class PluginLoader implements IPluginLoader { constructor(ctx: IPicGo) { this.ctx = ctx + this.logger = ctx.getLogger("plugin-loader") this.db = new PluginLoaderDb(this.ctx) this.init() } @@ -54,7 +57,8 @@ export class PluginLoader implements IPluginLoader { } return true } else { - throw new Error("load is not supported in browser") + this.logger.warn("load is not supported in browser") + return false } } diff --git a/libs/Universal-PicGo-Core/src/utils/clipboard/browser.ts b/libs/Universal-PicGo-Core/src/utils/clipboard/browser.ts new file mode 100644 index 0000000..42b5c2a --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/clipboard/browser.ts @@ -0,0 +1,16 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +import { IClipboardImage, IPicGo } from "../../types" + +const getClipboardImageBrowser = async (ctx: IPicGo): Promise => { + throw new Error("getClipboardImage is not supported in browser") +} + +export { getClipboardImageBrowser } diff --git a/libs/Universal-PicGo-Core/src/utils/clipboard/electron.ts b/libs/Universal-PicGo-Core/src/utils/clipboard/electron.ts new file mode 100644 index 0000000..aa876ef --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/clipboard/electron.ts @@ -0,0 +1,72 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +import { IClipboardImage, IPicGo } from "../../types" +import { win } from "universal-picgo-store" +import { CLIPBOARD_IMAGE_FOLDER } from "../constants" +import { ensureFolderSync } from "../nodeUtils" +import dayjs from "dayjs" +import { getCurrentPlatform, Platform } from "../os" +import macClipboardScript from "./script/mac.applescript?raw" +import windowsClipboardScript from "./script/windows.ps1?raw" +import windows10ClipboardScript from "./script/windows10.ps1?raw" +import linuxClipboardScript from "./script/linux.sh?raw" +import wslClipboardScript from "./script/wsl.sh?raw" + +const platform2ScriptContent: { + [key in Platform]: string +} = { + darwin: macClipboardScript, + win32: windowsClipboardScript, + win10: windows10ClipboardScript, + linux: linuxClipboardScript, + wsl: wslClipboardScript, +} + +/** + * powershell will report error if file does not have a '.ps1' extension, + * so we should keep the extension name consistent with corresponding shell + */ +const platform2ScriptFilename: { + [key in Platform]: string +} = { + darwin: "mac.applescript", + win32: "windows.ps1", + win10: "windows10.ps1", + linux: "linux.sh", + wsl: "wsl.sh", +} + +const createImageFolder = (ctx: IPicGo): void => { + const fs = win.fs + const path = win.require("path") + const imagePath = path.join(ctx.baseDir, CLIPBOARD_IMAGE_FOLDER) + ensureFolderSync(fs, imagePath) +} + +const getClipboardImageElectron = async (ctx: IPicGo): Promise => { + const fs = win.fs + const path = win.require("path") + + createImageFolder(ctx) + // add an clipboard image folder to control the image cache file + const imagePath = path.join(ctx.baseDir, CLIPBOARD_IMAGE_FOLDER, `${dayjs().format("YYYYMMDDHHmmss")}.png`) + return await new Promise((resolve: Function, reject: Function): void => { + const platform = getCurrentPlatform() + const scriptPath = path.join(ctx.baseDir, platform2ScriptFilename[platform]) + // If the script does not exist yet, we need to write the content to the script file + if (!fs.existsSync(scriptPath)) { + fs.writeFileSync(scriptPath, platform2ScriptContent[platform], "utf8") + } + + throw new Error("开发中...") + }) +} + +export { getClipboardImageElectron } diff --git a/libs/Universal-PicGo-Core/src/utils/clipboard/script/linux.sh b/libs/Universal-PicGo-Core/src/utils/clipboard/script/linux.sh new file mode 100644 index 0000000..5c7914a --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/clipboard/script/linux.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +if [ -z "$DISPLAY" ]; then + echo "no support" >&2 + exit 1 +fi + +case "$XDG_SESSION_TYPE" in +wayland) + command -v wl-copy >/dev/null 2>&1 || { + echo >&2 "no wl-clipboard" + exit 1 + } + filePath=$(wl-copy -o 2>/dev/null | grep ^file:// | cut -c8-) + if [ -z "$filePath" ]; then + if + wl-copy -t image/png image/png -o >"$1" 2>/dev/null + then + echo "$1" + else + rm -f "$1" + echo "no image" + fi + else + echo "$filePath" + fi + ;; +x11 | tty) + # require xclip(see http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212) + command -v xclip >/dev/null 2>&1 || { + echo >&2 "no xclip" + exit 1 + } + # write image in clipboard to file (see http://unix.stackexchange.com/questions/145131/copy-image-from-clipboard-to-file) + filePath=$(xclip -selection clipboard -o 2>/dev/null | grep ^file:// | cut -c8-) + if [ -z "$filePath" ]; then + if + xclip -selection clipboard -target image/png -o >"$1" 2>/dev/null + then + echo "$1" + else + rm -f "$1" + echo "no image" + fi + else + echo "$filePath" + fi + ;; +esac diff --git a/libs/Universal-PicGo-Core/src/utils/clipboard/script/mac.applescript b/libs/Universal-PicGo-Core/src/utils/clipboard/script/mac.applescript new file mode 100644 index 0000000..1bb0b72 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/clipboard/script/mac.applescript @@ -0,0 +1,41 @@ +-- From https://github.com/mushanshitiancai/vscode-paste-image +property fileTypes : {{«class PNGf», ".png"}} + +on run argv + if argv is {} then + return "" + end if + + if ((clipboard info) as string) contains "«class furl»" then + return POSIX path of (the clipboard as «class furl») + else + set imagePath to (item 1 of argv) + set theType to getType() + + if theType is not missing value then + try + set myFile to (open for access imagePath with write permission) + set eof myFile to 0 + write (the clipboard as (first item of theType)) to myFile + close access myFile + return (POSIX path of imagePath) + on error + try + close access myFile + end try + return "no image" + end try + else + return "no image" + end if + end if +end run + +on getType() + repeat with aType in fileTypes + repeat with theInfo in (clipboard info) + if (first item of theInfo) is equal to (first item of aType) then return aType + end repeat + end repeat + return missing value +end getType diff --git a/libs/Universal-PicGo-Core/src/utils/clipboard/script/windows.ps1 b/libs/Universal-PicGo-Core/src/utils/clipboard/script/windows.ps1 new file mode 100644 index 0000000..480b58f --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/clipboard/script/windows.ps1 @@ -0,0 +1,26 @@ + +param($imagePath) + +# Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1 + +Add-Type -Assembly PresentationCore +$img = [Windows.Clipboard]::GetImage() + +if ($img -eq $null) { + "no image" + Exit 1 +} + +if (-not $imagePath) { + "no image" + Exit 1 +} + +$fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0) +$stream = [IO.File]::Open($imagePath, "OpenOrCreate") +$encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder +$encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null +$encoder.Save($stream) | out-null +$stream.Dispose() | out-null + +$imagePath diff --git a/libs/Universal-PicGo-Core/src/utils/clipboard/script/windows10.ps1 b/libs/Universal-PicGo-Core/src/utils/clipboard/script/windows10.ps1 new file mode 100644 index 0000000..cadeb72 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/clipboard/script/windows10.ps1 @@ -0,0 +1,47 @@ +# Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1 +param($imagePath) + +# https://github.com/PowerShell/PowerShell/issues/7233 +# fix the output encoding bug +[console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding + +Add-Type -Assembly PresentationCore +function main { + $img = [Windows.Clipboard]::GetImage() + + if ($img -eq $null) { + "no image" + Exit 1 + } + + if (-not $imagePath) { + "no image" + Exit 1 + } + + $fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0) + $stream = [IO.File]::Open($imagePath, "OpenOrCreate") + $encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder + $encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null + $encoder.Save($stream) | out-null + $stream.Dispose() | out-null + + $imagePath + # fix windows 10 native cmd crash bug when "picgo upload" + # https://github.com/PicGo/PicGo-Core/issues/32 + Exit 1 +} + +try { + # For WIN10 + $file = Get-Clipboard -Format FileDropList + if ($file -ne $null) { + Convert-Path $file + Exit 1 + } +} catch { + # For WIN7 WIN8 WIN10 + main +} + +main \ No newline at end of file diff --git a/libs/Universal-PicGo-Core/src/utils/clipboard/script/wsl.sh b/libs/Universal-PicGo-Core/src/utils/clipboard/script/wsl.sh new file mode 100644 index 0000000..649ccd0 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/clipboard/script/wsl.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# grab the paths +scriptPath=$(echo $0 | awk '{ print substr( $0, 1, length($0)-6 ) }')"windows10.ps1" +imagePath=$(echo $1 | awk '{ print substr( $0, 1, length($0)-18 ) }') +imageName=$(echo $1 | awk '{ print substr( $0, length($0)-17, length($0) ) }') + +# run the powershell script +res=$(powershell.exe -noprofile -noninteractive -nologo -sta -executionpolicy unrestricted -file $(wslpath -w $scriptPath) $(wslpath -w $imagePath)"\\"$imageName) + +# note that there is a return symbol in powershell result +noImage=$(echo "no image\r") + +# check whether image exists +if [ "$res" = "$noImage" ] ;then + echo "no image" +else + echo $(wslpath -u -a "${res}") +fi diff --git a/libs/Universal-PicGo-Core/src/utils/common.ts b/libs/Universal-PicGo-Core/src/utils/common.ts index 6b0e34e..363a7e6 100644 --- a/libs/Universal-PicGo-Core/src/utils/common.ts +++ b/libs/Universal-PicGo-Core/src/utils/common.ts @@ -10,6 +10,23 @@ import { IPluginNameType } from "../types" import { hasNodeEnv, win } from "universal-picgo-store" +export const isUrlEncode = (url: string): boolean => { + url = url || "" + try { + // the whole url encode or decode shold not use encodeURIComponent or decodeURIComponent + return url !== decodeURI(url) + } catch (e) { + // if some error caught, try to let it go + return false + } +} +export const handleUrlEncode = (url: string): string => { + if (!isUrlEncode(url)) { + url = encodeURI(url) + } + return url +} + /** * detect the input string's type * for example diff --git a/libs/Universal-PicGo-Core/src/utils/constants.ts b/libs/Universal-PicGo-Core/src/utils/constants.ts new file mode 100644 index 0000000..7e2fc79 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/constants.ts @@ -0,0 +1,10 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +export const CLIPBOARD_IMAGE_FOLDER = "picgo-clipboard-images" diff --git a/libs/Universal-PicGo-Core/src/utils/createContext.ts b/libs/Universal-PicGo-Core/src/utils/createContext.ts new file mode 100644 index 0000000..32c61fa --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/createContext.ts @@ -0,0 +1,56 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +import { IPicGo } from "../types" + +/** + * create an unique context for each upload process + * + * @param ctx + */ +const createContext = (ctx: IPicGo): IPicGo => { + return { + configPath: ctx.configPath, + baseDir: ctx.baseDir, + log: ctx.log, + // cmd: ctx.cmd, + output: [], + input: [], + pluginLoader: ctx.pluginLoader, + pluginHandler: ctx.pluginHandler, + helper: ctx.helper, + VERSION: ctx.VERSION, + // GUI_VERSION: ctx.GUI_VERSION, + i18n: ctx.i18n, + getLogger: ctx.getLogger.bind(ctx), + getConfig: ctx.getConfig.bind(ctx), + saveConfig: ctx.saveConfig.bind(ctx), + removeConfig: ctx.removeConfig.bind(ctx), + setConfig: ctx.setConfig.bind(ctx), + unsetConfig: ctx.unsetConfig.bind(ctx), + upload: ctx.upload.bind(ctx), + addListener: ctx.addListener.bind(ctx), + on: ctx.on.bind(ctx), + once: ctx.once.bind(ctx), + removeListener: ctx.removeListener.bind(ctx), + off: ctx.off.bind(ctx), + removeAllListeners: ctx.removeAllListeners.bind(ctx), + setMaxListeners: ctx.setMaxListeners.bind(ctx), + getMaxListeners: ctx.getMaxListeners.bind(ctx), + listeners: ctx.listeners.bind(ctx), + rawListeners: ctx.rawListeners.bind(ctx), + emit: ctx.emit.bind(ctx), + listenerCount: ctx.listenerCount.bind(ctx), + prependListener: ctx.prependListener.bind(ctx), + prependOnceListener: ctx.prependOnceListener.bind(ctx), + eventNames: ctx.eventNames.bind(ctx), + } +} + +export { createContext } diff --git a/libs/Universal-PicGo-Core/src/utils/getClipboardImage.ts b/libs/Universal-PicGo-Core/src/utils/getClipboardImage.ts index 3830d1d..3ba71dc 100644 --- a/libs/Universal-PicGo-Core/src/utils/getClipboardImage.ts +++ b/libs/Universal-PicGo-Core/src/utils/getClipboardImage.ts @@ -8,10 +8,17 @@ */ import { IClipboardImage, IPicGo } from "../types" +import { hasNodeEnv } from "universal-picgo-store" +import { getClipboardImageElectron } from "./clipboard/electron" +import { getClipboardImageBrowser } from "./clipboard/browser" // Thanks to vs-picgo: https://github.com/Spades-S/vs-picgo/blob/master/src/extension.ts const getClipboardImage = async (ctx: IPicGo): Promise => { - throw new Error("getClipboardImage Not Implemented") + if (hasNodeEnv) { + return await getClipboardImageElectron(ctx) + } else { + return await getClipboardImageBrowser(ctx) + } } export default getClipboardImage diff --git a/libs/Universal-PicGo-Core/src/utils/os/index.ts b/libs/Universal-PicGo-Core/src/utils/os/index.ts new file mode 100644 index 0000000..e29b289 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/os/index.ts @@ -0,0 +1,36 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +import { isWsl } from "./is-wsl" +import { win } from "universal-picgo-store" + +export type Platform = "darwin" | "win32" | "win10" | "linux" | "wsl" + +const getCurrentPlatform = (): Platform => { + const os = win.require("os") + + const platform = win.process.platform + if (isWsl()) { + return "wsl" + } + if (platform === "win32") { + const currentOS = os.release().split(".")[0] + if (currentOS === "10") { + return "win10" + } else { + return "win32" + } + } else if (platform === "darwin") { + return "darwin" + } else { + return "linux" + } +} + +export { getCurrentPlatform } diff --git a/libs/Universal-PicGo-Core/src/utils/os/is-docker/index.ts b/libs/Universal-PicGo-Core/src/utils/os/is-docker/index.ts new file mode 100644 index 0000000..c43e372 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/os/is-docker/index.ts @@ -0,0 +1,41 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +import { win } from "universal-picgo-store" + +let isDockerCached: boolean + +function hasDockerEnv() { + try { + const fs = win.fs + fs.statSync("/.dockerenv") + return true + } catch { + return false + } +} + +function hasDockerCGroup() { + try { + const fs = win.fs + return fs.readFileSync("/proc/self/cgroup", "utf8").includes("docker") + } catch { + return false + } +} + +const isDocker = () => { + if (isDockerCached === undefined) { + isDockerCached = hasDockerEnv() || hasDockerCGroup() + } + + return isDockerCached +} + +export { isDocker } diff --git a/libs/Universal-PicGo-Core/src/utils/os/is-inside-container/index.ts b/libs/Universal-PicGo-Core/src/utils/os/is-inside-container/index.ts new file mode 100644 index 0000000..394f433 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/os/is-inside-container/index.ts @@ -0,0 +1,32 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +import { win } from "universal-picgo-store" +import { isDocker } from "../is-docker" + +let cachedResult: boolean + +// Podman detection +const hasContainerEnv = () => { + try { + const fs = win.fs + fs.statSync("/run/.containerenv") + return true + } catch { + return false + } +} + +export default function isInsideContainer() { + if (cachedResult === undefined) { + cachedResult = hasContainerEnv() || isDocker() + } + + return cachedResult +} diff --git a/libs/Universal-PicGo-Core/src/utils/os/is-wsl/index.ts b/libs/Universal-PicGo-Core/src/utils/os/is-wsl/index.ts new file mode 100644 index 0000000..03e7d06 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/utils/os/is-wsl/index.ts @@ -0,0 +1,35 @@ +/* + * GNU GENERAL PUBLIC LICENSE + * Version 3, 29 June 2007 + * + * Copyright (C) 2024 Terwer, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +import isInsideContainer from "../is-inside-container" +import { win } from "universal-picgo-store" + +const isWsl = () => { + const fs = win.fs + const os = win.require("os") + if (win.process.platform !== "linux") { + return false + } + + if (os.release().toLowerCase().includes("microsoft")) { + if (isInsideContainer()) { + return false + } + + return true + } + + try { + return fs.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false + } catch { + return false + } +} + +export { isWsl } diff --git a/packages/picgo-plugin-app/src/components/home/BrowserIndex.vue b/packages/picgo-plugin-app/src/components/home/BrowserIndex.vue new file mode 100644 index 0000000..547d37f --- /dev/null +++ b/packages/picgo-plugin-app/src/components/home/BrowserIndex.vue @@ -0,0 +1,39 @@ + + + + + + + diff --git a/packages/picgo-plugin-app/src/components/home/ElectronIndex.vue b/packages/picgo-plugin-app/src/components/home/ElectronIndex.vue new file mode 100644 index 0000000..b24aace --- /dev/null +++ b/packages/picgo-plugin-app/src/components/home/ElectronIndex.vue @@ -0,0 +1,39 @@ + + + + + + + diff --git a/packages/picgo-plugin-app/src/pages/PicGoIndex.vue b/packages/picgo-plugin-app/src/pages/PicGoIndex.vue index 2f6a4e1..ea184e4 100644 --- a/packages/picgo-plugin-app/src/pages/PicGoIndex.vue +++ b/packages/picgo-plugin-app/src/pages/PicGoIndex.vue @@ -8,31 +8,13 @@ --> diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e9493a..6970705 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@picgo/i18n': specifier: ^1.0.0 version: 1.0.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 js-yaml: specifier: ^4.1.0 version: 4.1.0