Skip to content

Commit

Permalink
feat(monaco-editor): 添加 复制导出超链接 菜单项 | Add Copy export hyperlink m…
Browse files Browse the repository at this point in the history
…enu item.
  • Loading branch information
Zuoqiu-Yingyi committed Aug 15, 2023
1 parent 8515ab8 commit e57e503
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 79 deletions.
6 changes: 6 additions & 0 deletions public/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,15 @@
"copy": {
"label": "Copy"
},
"copyDownloadHyperlink": {
"label": "Copy Download Hyperlink"
},
"copyEditHyperlink": {
"label": "Copy Edit Hyperlink"
},
"copyExportHyperlink": {
"label": "Copy Export Hyperlink"
},
"copyFullPath": {
"label": "Copy Absolute Path"
},
Expand Down
6 changes: 6 additions & 0 deletions public/i18n/zh_CHT.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,15 @@
"copy": {
"label": "複製"
},
"copyDownloadHyperlink": {
"label": "複製下載超連結"
},
"copyEditHyperlink": {
"label": "複製編輯超連結"
},
"copyExportHyperlink": {
"label": "複製匯出超連結"
},
"copyFullPath": {
"label": "複製完整路徑"
},
Expand Down
6 changes: 6 additions & 0 deletions public/i18n/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,15 @@
"copy": {
"label": "复制"
},
"copyDownloadHyperlink": {
"label": "复制下载超链接"
},
"copyEditHyperlink": {
"label": "复制编辑超链接"
},
"copyExportHyperlink": {
"label": "复制导出超链接"
},
"copyFullPath": {
"label": "复制完整路径"
},
Expand Down
2 changes: 1 addition & 1 deletion src/explorer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ export class Explorer implements ITree {
const type = get(node.type);
const name = get(node.name);
const relative = get(node.relative);
await this.contextMenu.download(relative, name, type);
await this.plugin.download(relative, name, type);
}
break;
case FileTreeNodeType.Root: // 根目录无法拖拽下载
Expand Down
106 changes: 30 additions & 76 deletions src/explorer/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { copyText } from "@workspace/utils/misc/copy";
import { isStaticWebFileServicePath, workspacePath2StaticPathname } from "@workspace/utils/siyuan/url";
import { ExplorerIcon } from "./icon";
import { Explorer, ProtectedResourceType } from ".";
import { basename, extname, join, parse } from "@workspace/utils/path/browserify";
import { extname, join, parse } from "@workspace/utils/path/browserify";
import {
fn__code,
ft__error,
Expand All @@ -41,7 +41,7 @@ import {
openPath,
showItemInFolder,
} from "@workspace/utils/electron/shell";
import { showOpenDialog, showSaveDialog } from "@workspace/utils/electron/dialog";
import { showOpenDialog } from "@workspace/utils/electron/dialog";
import { cp } from "@workspace/utils/node/fs/promises";
import { normalize } from "@workspace/utils/path/normalize";
import { OpenType } from "@/utils/url";
Expand Down Expand Up @@ -503,7 +503,7 @@ export class ExplorerContextMenu {
);

{
const url = new URL(`siyuan://plugins/${this.plugin.name}/workspace/${relative}`);
const url = new URL(`siyuan://plugins/${this.plugin.name}/open/workspace/${relative}`);
const href_edit = url.href;

/* 复制编辑超链接 */
Expand Down Expand Up @@ -543,10 +543,28 @@ export class ExplorerContextMenu {
});
}

const href_export = `siyuan://plugins/${this.plugin.name}/export/workspace/${relative}`;

/* 复制导出超链接 */
submenu.push({
type: MenuItemType.Action,
options: {
icon: "iconDownload",
label: this.i18n.menu.copyExportHyperlink.label,
accelerator: escapeHTML(href_export),
click: () => {
copyText(href_export);
},
},
root: false,
folder: true,
file: true,
});

submenu.push({
type: MenuItemType.Separator,
root: false,
folder: false,
folder: true,
file: true,
});
}
Expand Down Expand Up @@ -661,7 +679,7 @@ export class ExplorerContextMenu {
label: this.i18n.menu.importFile.label,
click: async () => {
const result = await showOpenDialog({
title: i10n_save_as.title.replaceAll("${1}", relative),
title: this.i18n.menu.importFile.title.replaceAll("${1}", relative),
buttonLabel: this.i18n.menu.import.label,
properties: [
"openFile",
Expand Down Expand Up @@ -697,7 +715,7 @@ export class ExplorerContextMenu {
label: this.i18n.menu.importFolder.label,
click: async () => {
const result = await showOpenDialog({
title: i10n_save_as.title.replaceAll("${1}", relative),
title: this.i18n.menu.importFolder.title.replaceAll("${1}", relative),
buttonLabel: this.i18n.menu.import.label,
properties: [
"openDirectory",
Expand Down Expand Up @@ -738,45 +756,17 @@ export class ExplorerContextMenu {
* 文件/文件夹另存为
* REF: https://www.electronjs.org/zh/docs/latest/api/dialog#dialogshowsavedialogbrowserwindow-options
*/
const i10n_save_as = file
? this.i18n.menu.exportFile
: this.i18n.menu.exportFolder;
items.push({
type: MenuItemType.Action,
options: {
icon: "iconDownload",
label: this.i18n.menu.export.label,
click: async () => {
const asyncFs = globalThis.require("fs/promises") as typeof import("fs/promises");
const result = await showSaveDialog({
title: i10n_save_as.title.replaceAll("${1}", relative),
defaultPath: name,
properties: [
"showHiddenFiles",
"createDirectory",
"treatPackageAsDirectory",
"showOverwriteConfirmation",
],
});
if (!result.canceled && result.filePath) {
// this.plugin.logger.debugs(path, result.filePath);
await asyncFs.cp(
path,
result.filePath,
{
recursive: true, // 递归复制
},
);
this.plugin.siyuan.confirm(
i10n_save_as.label,
i10n_save_as.message
.replaceAll("${1}", fn__code(relative))
.replaceAll("${2}", fn__code(result.filePath)),
() => {
showItemInFolder(result.filePath);
},
)
}
await this.plugin.export(
relative,
name,
type,
);
},
},
root: false,
Expand Down Expand Up @@ -871,7 +861,7 @@ export class ExplorerContextMenu {
icon: "iconDownload",
label: this.i18n.menu.download.label,
click: async () => {
await this.download(relative, name, type);
await this.plugin.download(relative, name, type);
},
},
root: false,
Expand Down Expand Up @@ -1068,42 +1058,6 @@ export class ExplorerContextMenu {
});
}

/**
* 下载文件/文件夹
* @param path - 所需下载的文件/文件夹相对于工作空间目录的路径
* @param name - 所需下载的文件/文件夹名称
* @param type - 下载内容类型
*/
public async download(
path: string,
name: string,
type: FileTreeNodeType,
): Promise<void> {
/* 文件夹需要首先打包为压缩文件 */
if (type === FileTreeNodeType.Folder) {
const response = await this.plugin.client.exportResources({
paths: [
path,
],
name,
});
path = response.data.path;
}

/* 下载文件流 */
const response = await this.plugin.client.getFile({ path }, "stream");
if (response) {
// this.plugin.logger.debugs(basename(path), response);
const write_stream = this.plugin.streamsaver.createWriteStream(basename(path));
await response.pipeTo(write_stream);
}

/* 下载完压缩文件后删除 */
if (type === FileTreeNodeType.Folder) {
await this.plugin.client.removeFile({ path });
}
}

/**
* 新建文件/文件夹弹出框
* @param node: 当前节点节点
Expand Down
122 changes: 120 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ import type { I18N } from "@/utils/i18n";
import type { IDockData } from "./types/dock";
import { trimPrefix } from "@workspace/utils/misc/string";
import { OpenMode, OpenType } from "./utils/url";
import { FileTreeNodeType } from "@workspace/components/siyuan/tree/file";
import { basename, parse } from "@workspace/utils/path/browserify";
import { showSaveDialog } from "@workspace/utils/electron/dialog";
import { fn__code } from "@workspace/utils/siyuan/text/span";
import { showItemInFolder } from "@workspace/utils/electron/shell";

export default class MonacoEditorPlugin extends siyuan.Plugin {
public static readonly GLOBAL_CONFIG_NAME = "global-config";
Expand Down Expand Up @@ -910,10 +915,10 @@ export default class MonacoEditorPlugin extends siyuan.Plugin {
protected readonly openSiyuanUrlEventListener = async (e: IOpenSiyuanUrlPluginEvent) => {
// this.logger.debug(e);
const url = new URL(e.detail.url);
if (url.pathname.startsWith(`//plugins/${this.name}/workspace/`)) {
if (url.pathname.startsWith(`//plugins/${this.name}/open/workspace/`)) { // 打开文件
const type = (url.searchParams.get("type") as OpenType | null);
const mode = (url.searchParams.get("mode") as OpenMode | null);
const relative = trimPrefix(url.pathname, `//plugins/${this.name}/workspace/`);
const relative = trimPrefix(url.pathname, `//plugins/${this.name}/open/workspace/`);

switch (type) {
default:
Expand Down Expand Up @@ -1046,6 +1051,31 @@ export default class MonacoEditorPlugin extends siyuan.Plugin {
}
}
}
else if (url.pathname.startsWith(`//plugins/${this.name}/export/workspace/`)) { // 导出文件/文件夹
const relative = trimPrefix(url.pathname, `//plugins/${this.name}/export/workspace/`);
const info = parse(relative);
const response = await this.client.readDir({ path: info.dir });
const entry = response.data.find((entry) => entry.name === info.base);

switch (entry?.isDir) {
case true:
await this.export(
relative,
info.base,
FileTreeNodeType.Folder,
);
break;
case false:
await this.export(
relative,
info.base,
FileTreeNodeType.File,
);
break;
default:
break;
}
}
}

/**
Expand Down Expand Up @@ -1303,4 +1333,92 @@ export default class MonacoEditorPlugin extends siyuan.Plugin {
...options,
});
}

/**
* 下载文件/文件夹
* @param path - 所需下载的文件/文件夹相对于工作空间目录的路径
* @param name - 所需下载的文件/文件夹名称
* @param type - 下载内容类型
*/
public async download(
path: string,
name: string,
type: FileTreeNodeType,
): Promise<void> {
/* 文件夹需要首先打包为压缩文件 */
if (type === FileTreeNodeType.Folder) {
const response = await this.client.exportResources({
paths: [
path,
],
name,
});
path = response.data.path;
}

/* 下载文件流 */
const response = await this.client.getFile({ path }, "stream");
if (response) {
// this.logger.debugs(basename(path), response);
const write_stream = this.streamsaver.createWriteStream(basename(path));
await response.pipeTo(write_stream);
}

/* 下载完压缩文件后删除 */
if (type === FileTreeNodeType.Folder) {
await this.client.removeFile({ path });
}
}

/**
* 导出文件/文件夹
* @param path - 所需导出的文件/文件夹相对于工作空间目录的路径
* @param name - 所需导出的文件/文件夹名称
* @param type - 下载内容类型
*/
public async export(
path: string,
name: string,
type: FileTreeNodeType,
): Promise<void> {
const i10n_save_as = type === FileTreeNodeType.File
? this.i18n.menu.exportFile
: this.i18n.menu.exportFolder;

const asyncFs = globalThis.require("fs/promises") as typeof import("fs/promises");
const result = await showSaveDialog({
title: i10n_save_as.title.replaceAll("${1}", path),
defaultPath: name,
properties: [
"showHiddenFiles",
"createDirectory",
"treatPackageAsDirectory",
"showOverwriteConfirmation",
],
});
if (!result.canceled && result.filePath) {
// this.logger.debugs(path, result.filePath);
const { join } = globalThis.require("path") as typeof import("path");
const source = join(
globalThis.siyuan.config.system.workspaceDir,
path,
);
await asyncFs.cp(
source,
result.filePath,
{
recursive: true, // 递归复制
},
);
this.siyuan.confirm(
i10n_save_as.label,
i10n_save_as.message
.replaceAll("${1}", fn__code(path))
.replaceAll("${2}", fn__code(result.filePath)),
() => {
showItemInFolder(result.filePath);
},
)
}
}
}

0 comments on commit e57e503

Please # to comment.