mirror of
https://github.com/zigholding/obsidian-notesync-plugin.git
synced 2026-07-22 12:20:23 +00:00
Compare commits
6 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1539fb2eb8 | ||
|
|
4ba68e4f24 | ||
|
|
58d58d13ce | ||
|
|
56f7156e2e | ||
|
|
66385c0201 | ||
|
|
6673fe5d50 |
8 changed files with 1040 additions and 370 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "note-sync",
|
"id": "note-sync",
|
||||||
"name": "Note Sync",
|
"name": "Note Sync",
|
||||||
"version": "0.6.0",
|
"version": "0.6.3",
|
||||||
"minAppVersion": "1.8.3",
|
"minAppVersion": "1.8.3",
|
||||||
"description": "Sync notes or plugins between vaults.",
|
"description": "Sync notes or plugins between vaults.",
|
||||||
"author": "ZigHolding",
|
"author": "ZigHolding",
|
||||||
|
|
|
||||||
283
src/commands.ts
283
src/commands.ts
|
|
@ -1,30 +1,31 @@
|
||||||
import {
|
import {
|
||||||
Notice, TFile
|
Notice, TFile,
|
||||||
|
TFolder
|
||||||
} from 'obsidian';
|
} from 'obsidian';
|
||||||
|
|
||||||
import NoteSyncPlugin from '../main';
|
import NoteSyncPlugin from '../main';
|
||||||
import { it } from 'node:test';
|
import { it } from 'node:test';
|
||||||
|
|
||||||
const cmd_export_current_note = (plugin:NoteSyncPlugin) => ({
|
const cmd_export_current_note = (plugin: NoteSyncPlugin) => ({
|
||||||
id: 'export_current_note',
|
id: 'export_current_note',
|
||||||
name: plugin.strings.cmd_export_current_note,
|
name: plugin.strings.cmd_export_current_note,
|
||||||
icon:'file-export',
|
icon: 'file-export',
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
let tfile = plugin.app.workspace.getActiveFile();
|
let tfile = plugin.app.workspace.getActiveFile();
|
||||||
await plugin.export_readme(tfile,null);
|
await plugin.export_readme(tfile, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const cmd_set_vexporter = (plugin:NoteSyncPlugin) => ({
|
const cmd_set_vexporter = (plugin: NoteSyncPlugin) => ({
|
||||||
id: 'set_vexporter',
|
id: 'set_vexporter',
|
||||||
name: plugin.strings.cmd_set_vexporter,
|
name: plugin.strings.cmd_set_vexporter,
|
||||||
icon:'settings',
|
icon: 'settings',
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
let tfile = plugin.app.workspace.getActiveFile();
|
let tfile = plugin.app.workspace.getActiveFile();
|
||||||
if(!tfile){return}
|
if (!tfile) { return }
|
||||||
let dir = await plugin.dialog_prompt(plugin.strings.prompt_path_of_folder);
|
let dir = await plugin.dialog_prompt(plugin.strings.prompt_path_of_folder);
|
||||||
let item: { [key: string]: any } = {};
|
let item: { [key: string]: any } = {};
|
||||||
if(plugin.fsEditor.fs.existsSync(dir)){
|
if (plugin.fsEditor.fs.existsSync(dir)) {
|
||||||
item['Dir'] = dir;
|
item['Dir'] = dir;
|
||||||
}
|
}
|
||||||
item['Name'] = 'readMe';
|
item['Name'] = 'readMe';
|
||||||
|
|
@ -35,176 +36,176 @@ const cmd_set_vexporter = (plugin:NoteSyncPlugin) => ({
|
||||||
|
|
||||||
await plugin.app.fileManager.processFrontMatter(
|
await plugin.app.fileManager.processFrontMatter(
|
||||||
tfile,
|
tfile,
|
||||||
async(fm) =>{
|
async (fm) => {
|
||||||
fm[plugin.yaml] = item
|
fm[plugin.yaml] = item
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const cmd_export_plugin = (plugin:NoteSyncPlugin) => ({
|
const cmd_export_plugin = (plugin: NoteSyncPlugin) => ({
|
||||||
id: 'export_plugin',
|
id: 'export_plugin',
|
||||||
name: plugin.strings.cmd_export_plugin,
|
name: plugin.strings.cmd_export_plugin,
|
||||||
icon:'arrow-right-from-line',
|
icon: 'arrow-right-from-line',
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
|
|
||||||
let plugins = Object.keys((plugin.app as any).plugins.plugins);
|
let plugins = Object.keys((plugin.app as any).plugins.plugins);
|
||||||
let p = await plugin.dialog_suggest(plugins,plugins);
|
let p = await plugin.dialog_suggest(plugins, plugins);
|
||||||
let eplugin = (plugin.app as any).plugins.getPlugin(p);
|
let eplugin = (plugin.app as any).plugins.getPlugin(p);
|
||||||
if(eplugin){
|
if (eplugin) {
|
||||||
let paths = plugin.settings.vaultDir.split("\n")
|
let paths = plugin.settings.vaultDir.split("\n")
|
||||||
let target = await plugin.fsEditor.select_valid_dir(
|
let target = await plugin.fsEditor.select_valid_dir(
|
||||||
paths
|
paths
|
||||||
)
|
)
|
||||||
if(target){
|
if (target) {
|
||||||
let items = plugin.fsEditor.list_dir(target,false)
|
let items = plugin.fsEditor.list_dir(target, false)
|
||||||
items = items.filter((x:string)=>x.startsWith('.') && x!='.git').filter(
|
items = items.filter((x: string) => x.startsWith('.') && x != '.git').filter(
|
||||||
(x:string)=>{
|
(x: string) => {
|
||||||
let path = plugin.fsEditor.path.join(target,x)
|
let path = plugin.fsEditor.path.join(target, x)
|
||||||
if(!plugin.fsEditor.isdir(path)){
|
if (!plugin.fsEditor.isdir(path)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let items = plugin.fsEditor.list_dir(path,false)
|
let items = plugin.fsEditor.list_dir(path, false)
|
||||||
return items.contains('plugins')
|
return items.contains('plugins')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if(items.length==1){
|
if (items.length == 1) {
|
||||||
target = plugin.fsEditor.path.join(target,items[0],'plugins')
|
target = plugin.fsEditor.path.join(target, items[0], 'plugins')
|
||||||
}else if(items.length>1){
|
} else if (items.length > 1) {
|
||||||
let item = await plugin.dialog_suggest(
|
let item = await plugin.dialog_suggest(
|
||||||
items,items,'config'
|
items, items, 'config'
|
||||||
)
|
)
|
||||||
if(item){
|
if (item) {
|
||||||
target = plugin.fsEditor.path.join(target,item,'plugins')
|
target = plugin.fsEditor.path.join(target, item, 'plugins')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!plugin.fsEditor.fs.existsSync(target) ||
|
if (!plugin.fsEditor.fs.existsSync(target) ||
|
||||||
plugin.fsEditor.path.basename(target)!='plugins'){
|
plugin.fsEditor.path.basename(target) != 'plugins') {
|
||||||
target = await plugin.dialog_prompt(plugin.strings.prompt_path_of_folder);
|
target = await plugin.dialog_prompt(plugin.strings.prompt_path_of_folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
target = target.replace(/\\/g,'/');
|
target = target.replace(/\\/g, '/');
|
||||||
if(!target.endsWith('/' + p)){
|
if (!target.endsWith('/' + p)) {
|
||||||
target = target + '/' + p;
|
target = target + '/' + p;
|
||||||
}
|
}
|
||||||
if(!plugin.fsEditor.fs.existsSync(target)){
|
if (!plugin.fsEditor.fs.existsSync(target)) {
|
||||||
plugin.fsEditor.fs.mkdirSync(target);
|
plugin.fsEditor.fs.mkdirSync(target);
|
||||||
}
|
}
|
||||||
let items = ['main.js','manifest.json','styles.css'];
|
let items = ['main.js', 'manifest.json', 'styles.css'];
|
||||||
let dj = await plugin.dialog_suggest(
|
let dj = await plugin.dialog_suggest(
|
||||||
[plugin.strings.item_skip_data_json,plugin.strings.item_copy_data_json],
|
[plugin.strings.item_skip_data_json, plugin.strings.item_copy_data_json],
|
||||||
[false,true],
|
[false, true],
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
if(dj){
|
if (dj) {
|
||||||
items.push('data.json')
|
items.push('data.json')
|
||||||
}
|
}
|
||||||
for(let item of items){
|
for (let item of items) {
|
||||||
let src = `${plugin.fsEditor.root}/${eplugin.manifest.dir}/${item}`;
|
let src = `${plugin.fsEditor.root}/${eplugin.manifest.dir}/${item}`;
|
||||||
let dst = `${target}/${item}`;
|
let dst = `${target}/${item}`;
|
||||||
let flag = plugin.fsEditor.copy_file(src,dst,'overwrite');
|
let flag = plugin.fsEditor.copy_file(src, dst, 'overwrite');
|
||||||
if(flag){
|
if (flag) {
|
||||||
console.log(`Copy ${item} to ${target}`,5000)
|
console.log(`Copy ${item} to ${target}`, 5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const cmd_download_git_repo = (plugin:NoteSyncPlugin) => ({
|
const cmd_download_git_repo = (plugin: NoteSyncPlugin) => ({
|
||||||
id: 'cmd_download_git_repo',
|
id: 'cmd_download_git_repo',
|
||||||
name: plugin.strings.cmd_download_git_repo,
|
name: plugin.strings.cmd_download_git_repo,
|
||||||
icon:'cloud-download',
|
icon: 'cloud-download',
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
let repos = plugin.settings.git_repo.split('\n')
|
let repos = plugin.settings.git_repo.split('\n')
|
||||||
let repo = await plugin.dialog_suggest(repos,repos);
|
let repo = await plugin.dialog_suggest(repos, repos);
|
||||||
if(!repo){return}
|
if (!repo) { return }
|
||||||
|
|
||||||
let match = repo.match(/^https?:\/\/(.*)\.com\/([^/]*)\/([^/]*)\/tree\/([^/]*)\/?(.*)$/);
|
let match = repo.match(/^https?:\/\/(.*)\.com\/([^/]*)\/([^/]*)\/tree\/([^/]*)\/?(.*)$/);
|
||||||
if(!match){return}
|
if (!match) { return }
|
||||||
|
|
||||||
let SOURCE = match[1]; // gitee
|
let SOURCE = match[1]; // gitee
|
||||||
let repoOwner = match[2]; // 开发者
|
let repoOwner = match[2]; // 开发者
|
||||||
let repoName = match[3]; // 项目名称
|
let repoName = match[3]; // 项目名称
|
||||||
let branch = match[4]; // 分支名称
|
let branch = match[4]; // 分支名称
|
||||||
let path = match[5]; // 初始路径
|
let path = match[5]; // 初始路径
|
||||||
|
|
||||||
async function list_files_of_path(repoOwner:string, repoName:string, path:string, branch = 'master') {
|
async function list_files_of_path(repoOwner: string, repoName: string, path: string, branch = 'master') {
|
||||||
let url;
|
let url;
|
||||||
if(SOURCE=='github'){
|
if (SOURCE == 'github') {
|
||||||
url = `https://api.github.com/repos/${repoOwner}/${repoName}/contents/${path}?ref=${branch}`;
|
url = `https://api.github.com/repos/${repoOwner}/${repoName}/contents/${path}?ref=${branch}`;
|
||||||
}else{
|
} else {
|
||||||
url = `https://gitee.com/api/v5/repos/${repoOwner}/${repoName}/contents/${path}?ref=${branch}`
|
url = `https://gitee.com/api/v5/repos/${repoOwner}/${repoName}/contents/${path}?ref=${branch}`
|
||||||
}
|
}
|
||||||
let req = await (window as any).requestUrl(url)
|
let req = await (window as any).requestUrl(url)
|
||||||
req = JSON.parse(req.text)
|
req = JSON.parse(req.text)
|
||||||
if(!Array.isArray(req)){
|
if (!Array.isArray(req)) {
|
||||||
req = [req]
|
req = [req]
|
||||||
}
|
}
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
async function download_file(url:string,folder_path:string,file_name:string){
|
async function download_file(url: string, folder_path: string, file_name: string) {
|
||||||
let req = await (window as any).requestUrl(url)
|
let req = await (window as any).requestUrl(url)
|
||||||
// req = JSON.parse(req.text)
|
// req = JSON.parse(req.text)
|
||||||
// let ctx = atob(req.content)
|
// let ctx = atob(req.content)
|
||||||
let ctx = req.text
|
let ctx = req.text
|
||||||
let tfile_path = `${folder_path}/${file_name}`
|
let tfile_path = `${folder_path}/${file_name}`
|
||||||
// console.log(ctx)
|
// console.log(ctx)
|
||||||
if(folder_path.startsWith('.')){
|
if (folder_path.startsWith('.')) {
|
||||||
let flag = (plugin.app.vault as any).exists(folder_path);
|
let flag = (plugin.app.vault as any).exists(folder_path);
|
||||||
if(!flag){
|
if (!flag) {
|
||||||
await plugin.app.vault.createFolder(folder_path)
|
await plugin.app.vault.createFolder(folder_path)
|
||||||
}
|
}
|
||||||
flag = (plugin.app.vault as any).exists(tfile_path);
|
flag = (plugin.app.vault as any).exists(tfile_path);
|
||||||
if(flag){
|
if (flag) {
|
||||||
await (plugin.app.vault as any).adapter.remove(tfile_path)
|
await (plugin.app.vault as any).adapter.remove(tfile_path)
|
||||||
await plugin.app.vault.create(tfile_path,ctx)
|
await plugin.app.vault.create(tfile_path, ctx)
|
||||||
new Notice(`更新:${tfile_path}`,5000)
|
new Notice(`更新:${tfile_path}`, 5000)
|
||||||
}else{
|
} else {
|
||||||
await plugin.app.vault.create(tfile_path,ctx)
|
await plugin.app.vault.create(tfile_path, ctx)
|
||||||
new Notice(`下载:${tfile_path}`,5000)
|
new Notice(`下载:${tfile_path}`, 5000)
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
let folder = plugin.app.vault.getFolderByPath(folder_path)
|
let folder = plugin.app.vault.getFolderByPath(folder_path)
|
||||||
if(!folder){
|
if (!folder) {
|
||||||
folder = await plugin.app.vault.createFolder(folder_path)
|
folder = await plugin.app.vault.createFolder(folder_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
let tfile = plugin.app.vault.getFileByPath(tfile_path)
|
let tfile = plugin.app.vault.getFileByPath(tfile_path)
|
||||||
if(!tfile){
|
if (!tfile) {
|
||||||
await plugin.app.vault.create(tfile_path,ctx);
|
await plugin.app.vault.create(tfile_path, ctx);
|
||||||
new Notice(`下载:${tfile_path}`,5000)
|
new Notice(`下载:${tfile_path}`, 5000)
|
||||||
}else{
|
} else {
|
||||||
await plugin.app.vault.modify(tfile,ctx)
|
await plugin.app.vault.modify(tfile, ctx)
|
||||||
new Notice(`更新:${tfile.path}`,5000)
|
new Notice(`更新:${tfile.path}`, 5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function download_file_of_dir(repoOwner:string, repoName:string, path:string, branch:string){
|
async function download_file_of_dir(repoOwner: string, repoName: string, path: string, branch: string) {
|
||||||
let items = await list_files_of_path(repoOwner, repoName, path, branch)
|
let items = await list_files_of_path(repoOwner, repoName, path, branch)
|
||||||
let nc = this.app.plugins.getPlugin('note-chain');
|
let nc = this.app.plugins.getPlugin('note-chain');
|
||||||
let item = await nc.dialog_suggest(
|
let item = await nc.dialog_suggest(
|
||||||
items.map((x:any)=>(x.type=='file'?'📃':'📁')+x.path),
|
items.map((x: any) => (x.type == 'file' ? '📃' : '📁') + x.path),
|
||||||
items,'',true
|
items, '', true
|
||||||
);
|
);
|
||||||
if(!item){return}
|
if (!item) { return }
|
||||||
if(typeof(item)=='string' && item=='all'){
|
if (typeof (item) == 'string' && item == 'all') {
|
||||||
for(let item of items){
|
for (let item of items) {
|
||||||
if(item.type=='file'){
|
if (item.type == 'file') {
|
||||||
let file_name = item.path.split('/').last();
|
let file_name = item.path.split('/').last();
|
||||||
let folder_path = item.path.slice(0,item.path.length-file_name.length-1);
|
let folder_path = item.path.slice(0, item.path.length - file_name.length - 1);
|
||||||
await download_file(item.download_url,folder_path,file_name)
|
await download_file(item.download_url, folder_path, file_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else if(item.type=='file'){
|
} else if (item.type == 'file') {
|
||||||
let file_name = item.path.split('/').last();
|
let file_name = item.path.split('/').last();
|
||||||
let folder_path = item.path.slice(0,item.path.length-file_name.length-1);
|
let folder_path = item.path.slice(0, item.path.length - file_name.length - 1);
|
||||||
await download_file(item.download_url,folder_path,file_name)
|
await download_file(item.download_url, folder_path, file_name)
|
||||||
}else if(item.type=='dir'){
|
} else if (item.type == 'dir') {
|
||||||
await download_file_of_dir(repoOwner, repoName, item.path, branch)
|
await download_file_of_dir(repoOwner, repoName, item.path, branch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,55 +215,111 @@ const cmd_download_git_repo = (plugin:NoteSyncPlugin) => ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const cmd_export_wxmp = (plugin:NoteSyncPlugin) => ({
|
const cmd_export_wxmp = (plugin: NoteSyncPlugin) => ({
|
||||||
id: 'cmd_export_wxmp',
|
id: 'cmd_export_wxmp',
|
||||||
name: plugin.strings.cmd_export_wxmp,
|
name: plugin.strings.cmd_export_wxmp,
|
||||||
icon:'aperture',
|
icon: 'aperture',
|
||||||
hotkeys: [{ modifiers: ['Alt', 'Shift'], key: 'P' }],
|
hotkeys: [{ modifiers: ['Alt', 'Shift'], key: 'P' }],
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
if(!plugin.easyapi.cfile){return}
|
if (!plugin.easyapi.cfile) { return }
|
||||||
let htmls = [];
|
|
||||||
let ctx = plugin.easyapi.ceditor.getSelection();
|
let ctx = plugin.easyapi.ceditor.getSelection();
|
||||||
if(!ctx){
|
if (!ctx) {
|
||||||
ctx = await plugin.app.vault.read(plugin.easyapi.cfile);
|
plugin.wxmp.tfile_to_wxmp(plugin.easyapi.cfile);
|
||||||
for(let section of plugin.easyapi.cmeta?.sections || []){
|
} else {
|
||||||
if(section.type=='yaml'){
|
await plugin.wxmp.selection_to_wxmp();
|
||||||
continue
|
|
||||||
}
|
|
||||||
let sec = plugin.easyapi.editor.slice_by_position(ctx,section.position);
|
|
||||||
let html = plugin.wxmp.marked.marked(sec);
|
|
||||||
let rhtml = await plugin.wxmp.html_to_wxmp(html);
|
|
||||||
htmls.push(rhtml);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
let html = plugin.wxmp.marked.marked(ctx);
|
|
||||||
let rhtml = await plugin.wxmp.html_to_wxmp(html);
|
|
||||||
htmls.push(rhtml);
|
|
||||||
}
|
}
|
||||||
plugin.wxmp.copy_as_html(htmls);
|
new Notice(`${plugin.strings.cmd_export_wxmp}: OK`, 5000)
|
||||||
new Notice(`${plugin.strings.cmd_export_wxmp}: OK`,5000)
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cmd_export_as_single_note = (plugin: NoteSyncPlugin) => ({
|
||||||
|
id: 'cmd_export_as_single_note',
|
||||||
|
name: plugin.strings.cmd_export_as_single_note,
|
||||||
|
icon: 'aperture',
|
||||||
|
// hotkeys: [{ modifiers: ['Alt', 'Shift'], key: 'P' }],
|
||||||
|
callback: async () => {
|
||||||
|
if (!plugin.easyapi.cfile) { return }
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const { dialog } = require("electron").remote;
|
||||||
|
|
||||||
|
let cfiles = plugin.easyapi.file.get_selected_files();
|
||||||
|
if(cfiles.length<=2){
|
||||||
|
// 1. 当前文件
|
||||||
|
let cfile = plugin.easyapi.cfile;
|
||||||
|
if (!cfile) {
|
||||||
|
new Notice("未找到当前文件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 读取内容
|
||||||
|
let n = 0;
|
||||||
|
if(cfile.parent && cfile.parent.children.filter(x=> x instanceof TFolder).length>0){
|
||||||
|
n = await plugin.easyapi.dialog_suggest([`-1 - All`,`0 - Brother`,`1 - Subfolder`],[-1,0,1]);
|
||||||
|
if(n == null){n=0}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfiles = plugin.easyapi.file.get_tfiles_of_folder(cfile.parent,n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(plugin.easyapi.nc){
|
||||||
|
cfiles = plugin.easyapi.nc.chain.sort_tfiles_by_chain(cfiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cfiles.length==0){
|
||||||
|
new Notice("没有文件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 打开「另存为」对话框
|
||||||
|
const result = await dialog.showSaveDialog({
|
||||||
|
title: plugin.strings.cmd_export_as_single_note,
|
||||||
|
defaultPath: (cfiles[0].parent?.name || cfiles[0].basename+'_parent') + ".md",
|
||||||
|
filters: [
|
||||||
|
{ name: "Markdown", extensions: ["md"] },
|
||||||
|
{ name: "All Files", extensions: ["*"] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.canceled || !result.filePath) {
|
||||||
|
new Notice("已取消另存为");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = '';
|
||||||
|
for(let cfile of cfiles){
|
||||||
|
let tmp = await plugin.app.vault.read(cfile);
|
||||||
|
content = content + `=====\n${cfile.name}\n=====\n\n${tmp}\n\n`
|
||||||
|
}
|
||||||
|
// 4. 写入外部文件
|
||||||
|
fs.writeFileSync(result.filePath, content, "utf-8");
|
||||||
|
|
||||||
|
new Notice(`已保存到:${result.filePath}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const commandBuilders:Array<Function> = [
|
|
||||||
|
const commandBuilders: Array<Function> = [
|
||||||
cmd_export_wxmp,
|
cmd_export_wxmp,
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const commandBuildersDesktop:Array<Function> = [
|
const commandBuildersDesktop: Array<Function> = [
|
||||||
cmd_export_current_note,
|
cmd_export_current_note,
|
||||||
cmd_set_vexporter,
|
cmd_set_vexporter,
|
||||||
cmd_export_plugin,
|
cmd_export_plugin,
|
||||||
cmd_download_git_repo,
|
cmd_download_git_repo,
|
||||||
cmd_export_wxmp
|
cmd_export_wxmp,
|
||||||
|
cmd_export_as_single_note
|
||||||
];
|
];
|
||||||
|
|
||||||
export function addCommands(plugin:NoteSyncPlugin) {
|
export function addCommands(plugin: NoteSyncPlugin) {
|
||||||
commandBuilders.forEach((c) => {
|
commandBuilders.forEach((c) => {
|
||||||
plugin.addCommand(c(plugin));
|
plugin.addCommand(c(plugin));
|
||||||
});
|
});
|
||||||
if((plugin.app as any).isMobile==false){
|
if ((plugin.app as any).isMobile == false) {
|
||||||
commandBuildersDesktop.forEach((c) => {
|
commandBuildersDesktop.forEach((c) => {
|
||||||
plugin.addCommand(c(plugin));
|
plugin.addCommand(c(plugin));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {Random } from './random';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { Templater } from './templater';
|
import { Templater } from './templater';
|
||||||
import {Time} from './time'
|
import {Time} from './time'
|
||||||
|
import { Web } from './web';
|
||||||
|
|
||||||
export class EasyAPI {
|
export class EasyAPI {
|
||||||
app: App;
|
app: App;
|
||||||
|
|
@ -21,6 +22,7 @@ export class EasyAPI {
|
||||||
waiter: Waiter
|
waiter: Waiter
|
||||||
tpl: Templater
|
tpl: Templater
|
||||||
time: Time
|
time: Time
|
||||||
|
web: Web
|
||||||
|
|
||||||
constructor(app: App) {
|
constructor(app: App) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
|
@ -31,7 +33,8 @@ export class EasyAPI {
|
||||||
this.waiter = new Waiter(app,this);
|
this.waiter = new Waiter(app,this);
|
||||||
this.random = new Random(app,this);
|
this.random = new Random(app,this);
|
||||||
this.tpl = new Templater(app,this);
|
this.tpl = new Templater(app,this);
|
||||||
this.time = new Time(app,this)
|
this.time = new Time(app,this);
|
||||||
|
this.web = new Web(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
get_plugin(name:string){
|
get_plugin(name:string){
|
||||||
|
|
@ -50,6 +53,10 @@ export class EasyAPI {
|
||||||
return this.get_plugin('note-sync');
|
return this.get_plugin('note-sync');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get wv(){
|
||||||
|
return this.get_plugin('webview-llm');
|
||||||
|
}
|
||||||
|
|
||||||
get qa(){
|
get qa(){
|
||||||
return this.get_plugin('quickadd')?.api;
|
return this.get_plugin('quickadd')?.api;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,50 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { App, View, WorkspaceLeaf,TFile } from 'obsidian';
|
import { App, View, WorkspaceLeaf, TFile } from 'obsidian';
|
||||||
|
|
||||||
import {EasyAPI} from 'src/easyapi/easyapi'
|
import { EasyAPI } from 'src/easyapi/easyapi'
|
||||||
|
|
||||||
export class EasyEditor {
|
export class EasyEditor {
|
||||||
yamljs = require('js-yaml');
|
yamljs = require('js-yaml');
|
||||||
app: App;
|
app: App;
|
||||||
ea: EasyAPI;
|
ea: EasyAPI;
|
||||||
|
|
||||||
constructor(app: App, api:EasyAPI) {
|
constructor(app: App, api: EasyAPI) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.ea = api;
|
this.ea = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
cn2num(chinese:string) {
|
cn2num(chinese: string) {
|
||||||
let v = parseFloat(chinese);
|
let v = parseFloat(chinese);
|
||||||
if(!Number.isNaN(v)){return v}
|
if (!Number.isNaN(v)) { return v }
|
||||||
|
|
||||||
chinese = chinese.trim()
|
chinese = chinese.trim()
|
||||||
const cnNumbers:{[key:string]:number} = {
|
const cnNumbers: { [key: string]: number } = {
|
||||||
"零": 0, "一": 1, "二": 2, "三": 3, "四": 4,
|
"零": 0, "一": 1, "二": 2, "三": 3, "四": 4,
|
||||||
"五": 5, "六": 6, "七": 7, "八": 8, "九": 9,
|
"五": 5, "六": 6, "七": 7, "八": 8, "九": 9,
|
||||||
"十": 10, "百": 100, "千": 1000, "万": 10000
|
"十": 10, "百": 100, "千": 1000, "万": 10000
|
||||||
};
|
};
|
||||||
|
|
||||||
let sign = 1.0;
|
let sign = 1.0;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
// 处理负号(JavaScript中汉字是双字节字符)
|
// 处理负号(JavaScript中汉字是双字节字符)
|
||||||
if (i + 1 <= chinese.length && chinese[i] === "负") {
|
if (i + 1 <= chinese.length && chinese[i] === "负") {
|
||||||
sign = -1.0;
|
sign = -1.0;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let integer_total = 0;
|
let integer_total = 0;
|
||||||
let decimal_total = 0.0;
|
let decimal_total = 0.0;
|
||||||
let temp = 0;
|
let temp = 0;
|
||||||
let processing_decimal = false;
|
let processing_decimal = false;
|
||||||
let decimal_factor = 0.1;
|
let decimal_factor = 0.1;
|
||||||
|
|
||||||
while (i < chinese.length) {
|
while (i < chinese.length) {
|
||||||
const c = chinese[i];
|
const c = chinese[i];
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
// 处理小数点
|
// 处理小数点
|
||||||
if (c === "点") {
|
if (c === "点") {
|
||||||
processing_decimal = true;
|
processing_decimal = true;
|
||||||
|
|
@ -52,14 +52,14 @@ export class EasyEditor {
|
||||||
temp = 0;
|
temp = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(!(c in cnNumbers)){
|
if (!(c in cnNumbers)) {
|
||||||
return parseFloat('-')
|
return parseFloat('-')
|
||||||
}
|
}
|
||||||
if (!processing_decimal) {
|
if (!processing_decimal) {
|
||||||
// 整数部分处理
|
// 整数部分处理
|
||||||
if (cnNumbers.hasOwnProperty(c)) {
|
if (cnNumbers.hasOwnProperty(c)) {
|
||||||
const num = cnNumbers[c];
|
const num = cnNumbers[c];
|
||||||
|
|
||||||
if (num >= 10) { // 处理单位
|
if (num >= 10) { // 处理单位
|
||||||
if (temp === 0 && num === 10) {
|
if (temp === 0 && num === 10) {
|
||||||
integer_total += 1 * num; // 特殊处理"十"前无数字的情况
|
integer_total += 1 * num; // 特殊处理"十"前无数字的情况
|
||||||
|
|
@ -79,33 +79,33 @@ export class EasyEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理最后的临时值
|
// 处理最后的临时值
|
||||||
integer_total += temp;
|
integer_total += temp;
|
||||||
|
|
||||||
return sign * (integer_total + decimal_total);
|
return sign * (integer_total + decimal_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
slice_by_position(ctx:string,pos:any){
|
slice_by_position(ctx: string, pos: any) {
|
||||||
if(pos.position){
|
if (pos.position) {
|
||||||
pos = pos.position
|
pos = pos.position
|
||||||
}
|
}
|
||||||
return ctx.slice(pos.start.offset,pos.end.offset);
|
return ctx.slice(pos.start.offset, pos.end.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_list_regx(aline:string,regx:RegExp,field:{[key:string]:number}={}){
|
parse_list_regx(aline: string, regx: RegExp, field: { [key: string]: number } = {}) {
|
||||||
let match = aline.match(regx);
|
let match = aline.match(regx);
|
||||||
if(!match){return null}
|
if (!match) { return null }
|
||||||
let res:{[key:string]:string} = {src:aline}
|
let res: { [key: string]: string } = { src: aline }
|
||||||
for(let k in field){
|
for (let k in field) {
|
||||||
res[k] = match[field[k]]
|
res[k] = match[field[k]]
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_list_dataview(aline:string,src='_src_'){
|
parse_list_dataview(aline: string, src = '_src_') {
|
||||||
let res:{[key:string]:string} = {};
|
let res: { [key: string]: string } = {};
|
||||||
if(src){
|
if (src) {
|
||||||
res[src] = aline;
|
res[src] = aline;
|
||||||
}
|
}
|
||||||
let regex = /[($$](.*?)::(.*?)[)$$]/g;
|
let regex = /[($$](.*?)::(.*?)[)$$]/g;
|
||||||
|
|
@ -117,203 +117,295 @@ export class EasyEditor {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
keys_in(keys:Array<string>,obj:object){
|
keys_in(keys: Array<string>, obj: object) {
|
||||||
for(let k of keys){
|
for (let k of keys) {
|
||||||
if(!(k in obj)){
|
if (!(k in obj)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async extract_code_block(tfile:TFile|string,btype:string){
|
async extract_code_block(tfile: TFile | string, btype: string) {
|
||||||
let xfile = this.ea.file.get_tfile(tfile);
|
let xfile = this.ea.file.get_tfile(tfile);
|
||||||
if(xfile){
|
if (xfile) {
|
||||||
tfile = await this.app.vault.cachedRead(xfile);
|
tfile = await this.app.vault.cachedRead(xfile);
|
||||||
}
|
}
|
||||||
if(typeof(tfile)!='string'){return []}
|
if (typeof (tfile) != 'string') { return [] }
|
||||||
|
|
||||||
let blocks = [];
|
let blocks = [];
|
||||||
let reg = new RegExp(`\`\`\`${btype}\\n([\\s\\S]*?)\n\`\`\``,'g');;
|
let reg = new RegExp(`\`\`\`${btype}\\n([\\s\\S]*?)\n\`\`\``, 'g');;
|
||||||
let matches;
|
let matches;
|
||||||
while ((matches = reg.exec(tfile)) !== null) {
|
while ((matches = reg.exec(tfile)) !== null) {
|
||||||
blocks.push(matches[1].trim());
|
blocks.push(matches[1].trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
reg = new RegExp(`~~~${btype}\\n([\\s\\S]*?)\n~~~`,'g');;
|
reg = new RegExp(`~~~${btype}\\n([\\s\\S]*?)\n~~~`, 'g');;
|
||||||
while ((matches = reg.exec(tfile)) !== null) {
|
while ((matches = reg.exec(tfile)) !== null) {
|
||||||
blocks.push(matches[1].trim());
|
blocks.push(matches[1].trim());
|
||||||
}
|
}
|
||||||
return blocks;
|
return blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_selection(cancel_selection=false){
|
async get_selection(cancel_selection = false) {
|
||||||
let editor = (this.app.workspace as any).getActiveFileView()?.editor;
|
let editor = (this.app.workspace as any).getActiveFileView()?.editor;
|
||||||
if(editor){
|
if (editor) {
|
||||||
let sel = editor.getSelection();
|
let sel = editor.getSelection();
|
||||||
if(cancel_selection){
|
if (cancel_selection) {
|
||||||
let cursor = editor.getCursor();
|
let cursor = editor.getCursor();
|
||||||
await editor.setSelection(cursor, cursor);
|
await editor.setSelection(cursor, cursor);
|
||||||
}
|
}
|
||||||
return sel;
|
if (sel) {
|
||||||
}else{
|
return sel;
|
||||||
return '';
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let selection = window.getSelection();
|
||||||
|
if (selection && !selection.isCollapsed) {
|
||||||
|
const selectedText = selection.toString();
|
||||||
|
if (selectedText) {
|
||||||
|
return selectedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let areas = document.querySelectorAll('textarea');
|
||||||
|
for (let area of Array.from(areas)) {
|
||||||
|
let sel = area.value.slice(area.selectionStart, area.selectionEnd);
|
||||||
|
|
||||||
|
if (sel) {
|
||||||
|
area.selectionStart = area.selectionEnd;
|
||||||
|
area.blur();
|
||||||
|
return sel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_code_section(tfile:TFile,ctype='',idx=0,as_simple=true){
|
async get_code_section(tfile: TFile, ctype = '', idx = 0, as_simple = true) {
|
||||||
let dvmeta = this.app.metadataCache.getFileCache(tfile);
|
let dvmeta = this.app.metadataCache.getFileCache(tfile);
|
||||||
let ctx = await this.app.vault.cachedRead(tfile);
|
let ctx = await this.app.vault.cachedRead(tfile);
|
||||||
let section = dvmeta?.sections?.filter(x=>x.type=='code').filter(x=>{
|
|
||||||
let c = ctx.slice(x.position.start.offset,x.position.end.offset).trim();
|
let sections = dvmeta?.sections
|
||||||
return c.startsWith('```'+ctype) || c.startsWith('~~~'+ctype);
|
?.filter(x => x.type == 'code')
|
||||||
})[idx]
|
.filter(x => {
|
||||||
if(section){
|
let c = ctx.slice(x.position.start.offset, x.position.end.offset).trim();
|
||||||
let c = ctx.slice(
|
return c.startsWith('```' + ctype) || c.startsWith('~~~' + ctype);
|
||||||
section.position.start.offset,
|
});
|
||||||
section.position.end.offset
|
|
||||||
|
if (!sections || sections.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let selected: any;
|
||||||
|
|
||||||
|
if(sections.length==1){
|
||||||
|
selected = sections[0];
|
||||||
|
}else if (idx >= 2 && sections[idx]) {
|
||||||
|
selected = sections[idx];
|
||||||
|
} else {
|
||||||
|
let sel = await this.ea.dialog_suggest(
|
||||||
|
sections.map(x => ctx.slice(x.position.start.offset, x.position.end.offset)),
|
||||||
|
[...Array(sections.length).keys()]
|
||||||
);
|
);
|
||||||
|
if (sel == null) return;
|
||||||
if(as_simple){
|
selected = sections[sel];
|
||||||
return c.slice(4+ctype.length,c.length-4)
|
}
|
||||||
}else{
|
|
||||||
let res = {
|
// Now safely use selected
|
||||||
code:c,
|
let c = ctx.slice(
|
||||||
section:section,
|
selected.position.start.offset,
|
||||||
ctx:ctx
|
selected.position.end.offset
|
||||||
}
|
);
|
||||||
return res;
|
|
||||||
}
|
if (as_simple) {
|
||||||
|
return c.slice(4 + ctype.length, c.length - 4);
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
code: c,
|
||||||
|
section: selected,
|
||||||
|
ctx: ctx
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_heading_section(tfile:TFile,heading:string,idx=0, with_heading=true){
|
get_heading_ctx(ctx: string, headings: any[], heading: any, with_heading = true) {
|
||||||
|
// 找到 heading 在 headings 中的位置
|
||||||
|
if(!heading){return ''}
|
||||||
|
let idx = headings.indexOf(heading);
|
||||||
|
let nextIdx = idx + 1;
|
||||||
|
|
||||||
|
// 找下一个同级或更高的 heading(level <= target.level)
|
||||||
|
while (nextIdx < headings.length) {
|
||||||
|
if (headings[nextIdx].level <= heading.level) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 起点
|
||||||
|
let start = with_heading
|
||||||
|
? heading.position.start.offset
|
||||||
|
: heading.position.end.offset;
|
||||||
|
|
||||||
|
// 终点:如果找到下一个 heading
|
||||||
|
if (nextIdx < headings.length) {
|
||||||
|
let nextSec = headings[nextIdx];
|
||||||
|
return ctx.slice(start, nextSec.position.start.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有更下一级 heading,截到文末
|
||||||
|
return ctx.slice(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async get_heading_section(tfile: TFile, heading: string, idx = 0, with_heading = true) {
|
||||||
let dvmeta = this.app.metadataCache.getFileCache(tfile);
|
let dvmeta = this.app.metadataCache.getFileCache(tfile);
|
||||||
let ctx = await this.app.vault.cachedRead(tfile);
|
let ctx = await this.app.vault.cachedRead(tfile);
|
||||||
if(!dvmeta?.headings){
|
|
||||||
|
if (!dvmeta?.headings) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
let section = dvmeta?.headings?.filter(x=>x.heading==heading)[idx]
|
|
||||||
if(section){
|
// 找到所有匹配开头的 headings
|
||||||
let idx = dvmeta.headings.indexOf(section)+1;
|
let sections = dvmeta.headings.filter(x => x.heading==heading);
|
||||||
while(idx<dvmeta.headings.length){
|
|
||||||
let csec = dvmeta.headings[idx];
|
if (sections.length === 0) return '';
|
||||||
if(csec.level<=section.level){
|
|
||||||
break
|
let selected: any;
|
||||||
}
|
|
||||||
idx = idx+1;
|
// idx >= 0 时直接取
|
||||||
}
|
if(sections.length==1){
|
||||||
if(idx<dvmeta.headings.length){
|
selected = sections[0];
|
||||||
let csec = dvmeta.headings[idx];
|
}else if (idx >= 2 && sections[idx]) {
|
||||||
let c = ctx.slice(
|
selected = sections[idx];
|
||||||
with_heading?section.position.start.offset : section.position.end.offset,
|
} else {
|
||||||
csec.position.start.offset
|
// 弹窗选择
|
||||||
);
|
const choices = sections.map(x =>
|
||||||
return c;
|
this.get_heading_ctx(ctx,dvmeta?.headings??[],x,with_heading)
|
||||||
}else{
|
);
|
||||||
let c = ctx.slice(
|
|
||||||
with_heading?section.position.start.offset : section.position.end.offset,
|
const nums = [...Array(sections.length).keys()];
|
||||||
);
|
let sel = await this.ea.dialog_suggest(choices, nums);
|
||||||
return c;
|
|
||||||
}
|
if (sel == null) return;
|
||||||
|
selected = sections[sel];
|
||||||
}
|
}
|
||||||
|
return this.get_heading_ctx(ctx,dvmeta.headings??[],selected,with_heading).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_current_section(){
|
|
||||||
let editor = this.ea.ceditor;
|
async get_current_section(with_section = false) {
|
||||||
let tfile = this.ea.cfile;
|
let editor = this.ea.ceditor;
|
||||||
if(!editor || !tfile){return null}
|
let tfile = this.ea.cfile;
|
||||||
let cursor = editor.getCursor();
|
if (!editor || !tfile) { return null }
|
||||||
let cache = this.app.metadataCache.getFileCache(tfile)
|
let cursor = editor.getCursor();
|
||||||
if(!cache){return null}
|
let cache = this.app.metadataCache.getFileCache(tfile)
|
||||||
if(cursor){
|
if (!cache || !cache?.sections) { return null }
|
||||||
let section = cache?.sections?.filter(
|
if (cursor) {
|
||||||
x=>{return x.position.start.line<=cursor.line && x.position.end.line>=cursor.line}
|
let section = cache?.sections?.filter(
|
||||||
)[0]
|
x => { return x.position.start.line <= cursor.line && x.position.end.line >= cursor.line }
|
||||||
|
)[0]
|
||||||
|
if (!section && cursor.line > cache.sections[cache.sections.length - 1].position.end.line) {
|
||||||
|
section = cache.sections[cache.sections.length - 1]
|
||||||
|
}
|
||||||
|
if (!section && cursor.line < cache.sections[0].position.start.line) {
|
||||||
|
section = cache.sections[0]
|
||||||
|
}
|
||||||
|
if (!section) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
let ctx = await this.app.vault.cachedRead(tfile);
|
let ctx = await this.app.vault.cachedRead(tfile);
|
||||||
if(!section){return ''}
|
ctx = ctx.slice(
|
||||||
return ctx.slice(
|
|
||||||
section.position.start.offset,
|
section.position.start.offset,
|
||||||
section.position.end.offset
|
section.position.end.offset
|
||||||
)
|
)
|
||||||
}else{
|
if (with_section) {
|
||||||
return null;
|
return {
|
||||||
}
|
'section': section,
|
||||||
}
|
'sec': ctx
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
set_obj_value(data:any,key:string,value:any){
|
set_obj_value(data: any, key: string, value: any) {
|
||||||
let items = key.trim().split('.')
|
let items = key.trim().split('.')
|
||||||
if(!items){return}
|
if (!items) { return }
|
||||||
let curr = data
|
let curr = data
|
||||||
for(let item of items.slice(0,items.length-1)){
|
for (let item of items.slice(0, items.length - 1)) {
|
||||||
let kv = item.match(/^(.*?)(\[-?\d+\])?$/) // 匹配数组索引, 如 key[0] 或 key
|
let kv = item.match(/^(.*?)(\[-?\d+\])?$/) // 匹配数组索引, 如 key[0] 或 key
|
||||||
if(!kv){return}
|
if (!kv) { return }
|
||||||
let k = kv[1] // 键名
|
let k = kv[1] // 键名
|
||||||
if(kv[2]){ // 有索引
|
if (kv[2]) { // 有索引
|
||||||
let i = parseInt(kv[2].slice(1,kv[2].length-1)) // 索引
|
let i = parseInt(kv[2].slice(1, kv[2].length - 1)) // 索引
|
||||||
if(!(k in curr)){ // 键不存在
|
if (!(k in curr)) { // 键不存在
|
||||||
curr[k] = [{}] // 创建空数组
|
curr[k] = [{}] // 创建空数组
|
||||||
curr = curr[k][0]
|
curr = curr[k][0]
|
||||||
}else{
|
} else {
|
||||||
if(Array.isArray(curr[k])){
|
if (Array.isArray(curr[k])) {
|
||||||
let tmp = {}
|
let tmp = {}
|
||||||
if(i<0){
|
if (i < 0) {
|
||||||
curr[k].splice(-i-1,0,tmp)
|
curr[k].splice(-i - 1, 0, tmp)
|
||||||
}else if(i<curr[k].length){
|
} else if (i < curr[k].length) {
|
||||||
curr[k][i]=tmp
|
curr[k][i] = tmp
|
||||||
}else{
|
} else {
|
||||||
curr[k].push(tmp)
|
curr[k].push(tmp)
|
||||||
}
|
}
|
||||||
curr = tmp
|
curr = tmp
|
||||||
}else{
|
} else {
|
||||||
curr[k] = [{}]
|
curr[k] = [{}]
|
||||||
curr = curr[k][0]
|
curr = curr[k][0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
if(!(k in curr)){
|
if (!(k in curr)) {
|
||||||
curr[k] = {}
|
curr[k] = {}
|
||||||
curr = curr[k]
|
curr = curr[k]
|
||||||
}else{
|
} else {
|
||||||
if(typeof(curr[k])!='object'){
|
if (typeof (curr[k]) != 'object') {
|
||||||
curr[k] = {}
|
curr[k] = {}
|
||||||
curr = curr[k]
|
curr = curr[k]
|
||||||
}else{
|
} else {
|
||||||
curr = curr[k]
|
curr = curr[k]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let kv = items[items.length-1].match(/^(.*?)(\[-?\d+\])?$/)
|
let kv = items[items.length - 1].match(/^(.*?)(\[-?\d+\])?$/)
|
||||||
if(!kv){return}
|
if (!kv) { return }
|
||||||
let k = kv[1]
|
let k = kv[1]
|
||||||
if(kv[2]){
|
if (kv[2]) {
|
||||||
let i = parseInt(kv[2].slice(1,kv[2].length-1))
|
let i = parseInt(kv[2].slice(1, kv[2].length - 1))
|
||||||
if(k in curr){
|
if (k in curr) {
|
||||||
if(Array.isArray(curr[k])){
|
if (Array.isArray(curr[k])) {
|
||||||
if(i<0){
|
if (i < 0) {
|
||||||
curr[k].splice(-i-1,0,value)
|
curr[k].splice(-i - 1, 0, value)
|
||||||
}else if(i<curr[k].length){
|
} else if (i < curr[k].length) {
|
||||||
curr[k][i] = value
|
curr[k][i] = value
|
||||||
}else{
|
} else {
|
||||||
curr[k].push(value)
|
curr[k].push(value)
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
curr[k] = value
|
curr[k] = value
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
curr[k] = [value]
|
curr[k] = [value]
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
curr[k] = value
|
curr[k] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_obj_value(data:any,key:string):any{
|
get_obj_value(data: any, key: string): any {
|
||||||
try {
|
try {
|
||||||
// key 直接在对象中
|
// key 直接在对象中
|
||||||
if(data[key]){
|
if (data[key]) {
|
||||||
return data[key]
|
return data[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,59 +413,61 @@ export class EasyEditor {
|
||||||
let left = keys[0];
|
let left = keys[0];
|
||||||
let right = keys.slice(1).join('.');
|
let right = keys.slice(1).join('.');
|
||||||
|
|
||||||
if(left){
|
if (left) {
|
||||||
// key[3],key[-3]
|
// key[3],key[-3]
|
||||||
let items = left.match(/^(.*?)(\[-?\d+\])?$/)
|
let items = left.match(/^(.*?)(\[-?\d+\])?$/)
|
||||||
if(!items){return null}
|
if (!items) { return null }
|
||||||
if(items[1]){
|
if (items[1]) {
|
||||||
data = data[items[1]]
|
data = data[items[1]]
|
||||||
}
|
}
|
||||||
if(!data){return null}
|
if (!data) { return null }
|
||||||
if(items[2]){
|
if (items[2]) {
|
||||||
if(Array.isArray(data)){
|
if (Array.isArray(data)) {
|
||||||
if(data.length == 0){
|
if (data.length == 0) {
|
||||||
data = null;
|
data = null;
|
||||||
}else{
|
} else {
|
||||||
let i = parseInt(items[2].slice(1,items[2].length-1))
|
let i = parseInt(items[2].slice(1, items[2].length - 1))
|
||||||
i = ((i % data.length) + data.length) % data.length;
|
i = ((i % data.length) + data.length) % data.length;
|
||||||
data = data[i]
|
data = data[i]
|
||||||
}
|
}
|
||||||
}else if(typeof data=='object'){
|
} else if (typeof data == 'object') {
|
||||||
let keys = Object.keys(data).sort();
|
let keys = Object.keys(data).sort();
|
||||||
if(keys.length==0){
|
if (keys.length == 0) {
|
||||||
data = null;
|
data = null;
|
||||||
}else{
|
} else {
|
||||||
let i = parseInt(items[2].slice(1,items[2].length-1))
|
let i = parseInt(items[2].slice(1, items[2].length - 1))
|
||||||
i = ((i % keys.length) + keys.length) % keys.length;
|
i = ((i % keys.length) + keys.length) % keys.length;
|
||||||
data = data[keys[i]]
|
data = data[keys[i]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!right){
|
if (!right) {
|
||||||
return data;
|
return data;
|
||||||
}else{
|
} else {
|
||||||
return this.get_obj_value(data,right);
|
return this.get_obj_value(data, right);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LINE 存在时在其之后插件,不存在在末尾
|
// LINE 存在时在其之后插件,不存在在末尾
|
||||||
async insert_after_line(tfile:TFile,aline:string,LINE:string, tail=true,suffix='\n\n'){
|
async insert_after_line(tfile: TFile, aline: string, LINE: string, tail = true, suffix = '\n\n') {
|
||||||
if(!tfile){return false}
|
if (!tfile) { return false }
|
||||||
let ctx = await this.ea.app.vault.cachedRead(tfile)
|
let ctx = await this.ea.app.vault.cachedRead(tfile)
|
||||||
|
|
||||||
let idx = ctx.indexOf(LINE)
|
let idx = ctx.indexOf(LINE)
|
||||||
|
|
||||||
if(idx==-1 && tail){
|
if (idx == -1 && tail) {
|
||||||
ctx = `${ctx}${suffix}${aline}`
|
ctx = `${ctx}${suffix}${aline}`
|
||||||
}else{
|
} else {
|
||||||
ctx = `${ctx.slice(0,idx+LINE.length)}\n${aline}${ctx.slice(idx+LINE.length)}`
|
ctx = `${ctx.slice(0, idx + LINE.length)}\n${aline}${ctx.slice(idx + LINE.length)}`
|
||||||
}
|
}
|
||||||
await this.ea.app.vault.modify(tfile,ctx)
|
await this.ea.app.vault.modify(tfile, ctx)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,11 @@ export class File {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_tfile(path:string|TFile,only_first=true){
|
get_tfile(path:string|TFile|null,only_first=true){
|
||||||
try{
|
try{
|
||||||
|
if(!path){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if(path instanceof TFile){
|
if(path instanceof TFile){
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
@ -57,6 +60,11 @@ export class File {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_all_tfiles(){
|
||||||
|
let files = this.app.vault.getMarkdownFiles();
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
get_tfiles_of_folder(tfolder:TFolder|null,n=0):any{
|
get_tfiles_of_folder(tfolder:TFolder|null,n=0):any{
|
||||||
if(!tfolder){return [];}
|
if(!tfolder){return [];}
|
||||||
let notes = [];
|
let notes = [];
|
||||||
|
|
@ -73,6 +81,30 @@ export class File {
|
||||||
return notes;
|
return notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_all_tfiles_of_tags(tags:string|Array<string>,sort_mode=''){
|
||||||
|
if(!Array.isArray(tags)){
|
||||||
|
tags = [tags]
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = tags.map(x=>{
|
||||||
|
if(x.startsWith('#')){
|
||||||
|
return x;
|
||||||
|
}else{
|
||||||
|
return '#'+x;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let tfiles = this.get_all_tfiles().filter(x=>{
|
||||||
|
let ttags = this.get_tags(x);
|
||||||
|
for(let tag of tags){
|
||||||
|
if(ttags.contains(tag)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return tfiles;
|
||||||
|
}
|
||||||
|
|
||||||
generate_structure(tfolder:TFolder, depth = 0, isRoot = true,only_folder=false,only_md=true) {
|
generate_structure(tfolder:TFolder, depth = 0, isRoot = true,only_folder=false,only_md=true) {
|
||||||
let structure = '';
|
let structure = '';
|
||||||
const indentUnit = ' '; // 关键修改点:每层缩进 4 空格
|
const indentUnit = ' '; // 关键修改点:每层缩进 4 空格
|
||||||
|
|
@ -102,5 +134,55 @@ export class File {
|
||||||
});
|
});
|
||||||
return structure;
|
return structure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_tags(tfile:TFile){
|
||||||
|
if(!tfile){return []}
|
||||||
|
let mcache= this.app.metadataCache.getFileCache(tfile);
|
||||||
|
let tags:Array<string> = []
|
||||||
|
if(mcache?.tags){
|
||||||
|
for(let curr of mcache.tags){
|
||||||
|
if(!tags.contains(curr.tag)){
|
||||||
|
tags.push(curr.tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(mcache?.frontmatter?.tags){
|
||||||
|
if(Array.isArray(mcache.frontmatter.tags)){
|
||||||
|
for(let curr of mcache.frontmatter.tags){
|
||||||
|
let tag = '#'+curr;
|
||||||
|
if(!tags.contains(tag)){
|
||||||
|
tags.push(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if(typeof mcache.frontmatter.tags === 'string'){
|
||||||
|
let tag = `#`+mcache.frontmatter.tags
|
||||||
|
if(!tags.contains(tag)){
|
||||||
|
tags.push(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
get_selected_files(current_if_no_selected = true) {
|
||||||
|
let selector = document.querySelectorAll(
|
||||||
|
".tree-item-self.is-selected"
|
||||||
|
);
|
||||||
|
let items = Object.values(selector).map((x: any) => {
|
||||||
|
var _a;
|
||||||
|
return (_a = x.dataset) == null ? void 0 : _a.path;
|
||||||
|
});
|
||||||
|
let tfiles = items.map(
|
||||||
|
(x) => this.get_tfile(x)).filter((x) => x.extension == "md"
|
||||||
|
)
|
||||||
|
if (tfiles.length > 0) {
|
||||||
|
return tfiles
|
||||||
|
} else if (current_if_no_selected && this.app.workspace.getActiveFile()) {
|
||||||
|
return [this.app.workspace.getActiveFile()]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
199
src/easyapi/web.ts
Normal file
199
src/easyapi/web.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
import { App } from 'obsidian';
|
||||||
|
|
||||||
|
const http = require('http');
|
||||||
|
const https = require('https');
|
||||||
|
const url = require('url');
|
||||||
|
|
||||||
|
export interface WebRequestOptions {
|
||||||
|
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
||||||
|
headers?: { [key: string]: string };
|
||||||
|
body?: any;
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebResponse {
|
||||||
|
statusCode: number;
|
||||||
|
headers: { [key: string]: string };
|
||||||
|
body: any;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Web {
|
||||||
|
app: App;
|
||||||
|
|
||||||
|
constructor(app: App) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行 HTTP 请求
|
||||||
|
* @param urlStr 请求 URL
|
||||||
|
* @param options 请求选项
|
||||||
|
* @returns Promise<WebResponse>
|
||||||
|
*/
|
||||||
|
async request(urlStr: string, options: WebRequestOptions = {}): Promise<WebResponse> {
|
||||||
|
// 解析 URL 并正确处理编码
|
||||||
|
const parsed = url.parse(urlStr);
|
||||||
|
const isHttps = parsed.protocol === 'https:';
|
||||||
|
const client = isHttps ? https : http;
|
||||||
|
|
||||||
|
// 处理路径和查询字符串的编码
|
||||||
|
let requestPath = parsed.pathname || '/';
|
||||||
|
|
||||||
|
// 处理查询字符串,确保中文字符被正确编码
|
||||||
|
if (parsed.query || parsed.search) {
|
||||||
|
let queryString = '';
|
||||||
|
if (parsed.search) {
|
||||||
|
// 解析查询字符串并重新编码
|
||||||
|
const queryParams = new URLSearchParams(parsed.search.substring(1));
|
||||||
|
queryString = '?' + queryParams.toString();
|
||||||
|
} else if (parsed.query) {
|
||||||
|
// 手动处理查询字符串
|
||||||
|
const parts: string[] = [];
|
||||||
|
const queryPairs = parsed.query.split('&');
|
||||||
|
for (const pair of queryPairs) {
|
||||||
|
const [key, ...valueParts] = pair.split('=');
|
||||||
|
const value = valueParts.join('=');
|
||||||
|
// 对键和值进行编码(如果还没有编码)
|
||||||
|
const encodedKey = encodeURIComponent(decodeURIComponent(key || ''));
|
||||||
|
const encodedValue = encodeURIComponent(decodeURIComponent(value || ''));
|
||||||
|
parts.push(`${encodedKey}=${encodedValue}`);
|
||||||
|
}
|
||||||
|
queryString = '?' + parts.join('&');
|
||||||
|
}
|
||||||
|
requestPath = requestPath + queryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
const method = options.method || 'GET';
|
||||||
|
const headers = options.headers || {};
|
||||||
|
const timeout = options.timeout || 30000; // 默认 30 秒
|
||||||
|
|
||||||
|
// 如果没有设置 Content-Type,根据 body 类型自动设置
|
||||||
|
if (method !== 'GET' && options.body && !headers['Content-Type']) {
|
||||||
|
if (typeof options.body === 'string') {
|
||||||
|
headers['Content-Type'] = 'text/plain; charset=utf-8';
|
||||||
|
} else if (options.body instanceof Buffer) {
|
||||||
|
headers['Content-Type'] = 'application/octet-stream';
|
||||||
|
} else {
|
||||||
|
headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||||
|
options.body = JSON.stringify(options.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestOptions: any = {
|
||||||
|
hostname: parsed.hostname,
|
||||||
|
port: parsed.port || (isHttps ? 443 : 80),
|
||||||
|
path: requestPath,
|
||||||
|
method: method,
|
||||||
|
headers: headers,
|
||||||
|
timeout: timeout
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = client.request(requestOptions, (res: any) => {
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
res.on('data', (chunk: Buffer) => {
|
||||||
|
data += chunk.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
let body: any = data;
|
||||||
|
|
||||||
|
// 尝试解析 JSON
|
||||||
|
const contentType = res.headers['content-type'] || '';
|
||||||
|
if (contentType.includes('application/json')) {
|
||||||
|
try {
|
||||||
|
body = JSON.parse(data);
|
||||||
|
} catch {
|
||||||
|
// 解析失败,保持原样
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response: WebResponse = {
|
||||||
|
statusCode: res.statusCode || 200,
|
||||||
|
headers: res.headers,
|
||||||
|
body: body,
|
||||||
|
text: data
|
||||||
|
};
|
||||||
|
|
||||||
|
resolve(response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (error: Error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('timeout', () => {
|
||||||
|
req.destroy();
|
||||||
|
reject(new Error('Request timeout'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发送请求体
|
||||||
|
if (method !== 'GET' && options.body) {
|
||||||
|
if (typeof options.body === 'string' || options.body instanceof Buffer) {
|
||||||
|
req.write(options.body);
|
||||||
|
} else {
|
||||||
|
req.write(JSON.stringify(options.body));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET 请求
|
||||||
|
* @param urlStr 请求 URL
|
||||||
|
* @param options 请求选项(可选)
|
||||||
|
* @returns Promise<WebResponse>
|
||||||
|
*/
|
||||||
|
async get(urlStr: string, options: Omit<WebRequestOptions, 'method'> = {}): Promise<WebResponse> {
|
||||||
|
return this.request(urlStr, { ...options, method: 'GET' });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST 请求
|
||||||
|
* @param urlStr 请求 URL
|
||||||
|
* @param body 请求体(可选)
|
||||||
|
* @param options 请求选项(可选)
|
||||||
|
* @returns Promise<WebResponse>
|
||||||
|
*/
|
||||||
|
async post(urlStr: string, body?: any, options: Omit<WebRequestOptions, 'method' | 'body'> = {}): Promise<WebResponse> {
|
||||||
|
return this.request(urlStr, { ...options, method: 'POST', body: body });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT 请求
|
||||||
|
* @param urlStr 请求 URL
|
||||||
|
* @param body 请求体(可选)
|
||||||
|
* @param options 请求选项(可选)
|
||||||
|
* @returns Promise<WebResponse>
|
||||||
|
*/
|
||||||
|
async put(urlStr: string, body?: any, options: Omit<WebRequestOptions, 'method' | 'body'> = {}): Promise<WebResponse> {
|
||||||
|
return this.request(urlStr, { ...options, method: 'PUT', body: body });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE 请求
|
||||||
|
* @param urlStr 请求 URL
|
||||||
|
* @param options 请求选项(可选)
|
||||||
|
* @returns Promise<WebResponse>
|
||||||
|
*/
|
||||||
|
async delete(urlStr: string, options: Omit<WebRequestOptions, 'method'> = {}): Promise<WebResponse> {
|
||||||
|
return this.request(urlStr, { ...options, method: 'DELETE' });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PATCH 请求
|
||||||
|
* @param urlStr 请求 URL
|
||||||
|
* @param body 请求体(可选)
|
||||||
|
* @param options 请求选项(可选)
|
||||||
|
* @returns Promise<WebResponse>
|
||||||
|
*/
|
||||||
|
async patch(urlStr: string, body?: any, options: Omit<WebRequestOptions, 'method' | 'body'> = {}): Promise<WebResponse> {
|
||||||
|
return this.request(urlStr, { ...options, method: 'PATCH', body: body });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -50,6 +50,14 @@ export class Strings{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get cmd_export_as_single_note(){
|
||||||
|
if(this.language=='zh'){
|
||||||
|
return '导出多条笔记';
|
||||||
|
}else{
|
||||||
|
return 'Export notes';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get prompt_path_of_folder(){
|
get prompt_path_of_folder(){
|
||||||
if(this.language=='zh'){
|
if(this.language=='zh'){
|
||||||
return '输入文件夹路径'
|
return '输入文件夹路径'
|
||||||
|
|
|
||||||
301
src/wxmp.ts
301
src/wxmp.ts
|
|
@ -16,30 +16,49 @@ export class Wxmp {
|
||||||
this.hljs = require('highlight.js');
|
this.hljs = require('highlight.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
get ctx_map(){
|
get blank_line(){
|
||||||
let config:{[key:string]:any} = {};
|
return '<section><br></section>';
|
||||||
for(let line of this.plugin.settings.wxmp_config.split('\n')){
|
}
|
||||||
let [key, value] = line.split(':');
|
|
||||||
if(!key || !value){continue}
|
get ctx_map() {
|
||||||
key = key.trim();
|
let msg = this.plugin.settings.wxmp_config.trim();
|
||||||
value = value.trim();
|
if(msg.trim()==''){
|
||||||
if(!key || !value){continue}
|
return {};
|
||||||
config[key] = value;
|
}
|
||||||
|
let config = this.plugin.easyapi.editor.yamljs.load(msg);
|
||||||
|
if(!config){
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
if(!config['h1']){
|
if(!config['h1']){
|
||||||
config['h1'] = this.format_wxmp_h1;
|
config['h1'] = this.format_wxmp_h1;
|
||||||
}
|
}
|
||||||
if(!config['h2']){
|
|
||||||
config['h2'] = this.format_wxmp_h2;
|
if (!config['h1']) {
|
||||||
|
config['h1'] = this.format_wxmp_h1.bind(this);
|
||||||
}
|
}
|
||||||
if(!config['h3']){
|
if (!config['h2']) {
|
||||||
config['h3'] = this.format_wxmp_h3;
|
config['h2'] = this.format_wxmp_h2.bind(this);
|
||||||
}
|
}
|
||||||
if(!config['p code']){
|
if (!config['h3']) {
|
||||||
config['p code'] = this.format_wxmp_p_code;
|
config['h3'] = this.format_wxmp_h3.bind(this);
|
||||||
}
|
}
|
||||||
if(!config['li code']){
|
if (!config['p code']) {
|
||||||
config['li code'] = this.format_wxmp_li_code;
|
config['p code'] = this.format_wxmp_p_code.bind(this);
|
||||||
|
}
|
||||||
|
if (!config['li code']) {
|
||||||
|
config['li code'] = this.format_wxmp_li_code.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config['section@html']) {
|
||||||
|
config['section@html'] = this.format_section_html.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config['section@cards-album']) {
|
||||||
|
config['section@cards-album'] = this.format_code_block_cards_album.bind(this);
|
||||||
|
}
|
||||||
|
let tfiles = this.plugin.easyapi.file.get_all_tfiles_of_tags('NoteSyncWxmp');
|
||||||
|
for(let i in tfiles){
|
||||||
|
config[`section@${i}`] = tfiles[i].basename;
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +83,7 @@ export class Wxmp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async replace_regx_with_tpl(rhtml:string, regx:RegExp, tpl:string) {
|
async replace_regx_with_tpl(rhtml: string, regx: RegExp, tpl: string) {
|
||||||
let matches = [...rhtml.matchAll(regx)];
|
let matches = [...rhtml.matchAll(regx)];
|
||||||
|
|
||||||
let replacements = await Promise.all(
|
let replacements = await Promise.all(
|
||||||
|
|
@ -82,13 +101,13 @@ export class Wxmp {
|
||||||
return rhtml;
|
return rhtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
convertVaultImageLinksToImgTag(htmlString:string) {
|
convertVaultImageLinksToImgTag(htmlString: string) {
|
||||||
return htmlString.replace(/!\[\[([^\]]+?)\]\]/g, (match, filename) => {
|
return htmlString.replace(/!\[\[([^\]]+?)\]\]/g, (match, filename) => {
|
||||||
return `<img src="${filename.trim()}">`;
|
return `<img src="${filename.trim()}">`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async convertImageTagsToBase64(htmlString:string) {
|
async convertImageTagsToBase64(htmlString: string) {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(htmlString, 'text/html');
|
const doc = parser.parseFromString(htmlString, 'text/html');
|
||||||
|
|
||||||
|
|
@ -110,12 +129,119 @@ export class Wxmp {
|
||||||
console.warn(`图片 ${src} 转换失败:`, e);
|
console.warn(`图片 ${src} 转换失败:`, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new XMLSerializer().serializeToString(doc.body);
|
return new XMLSerializer().serializeToString(doc.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async section_to_wxmp(section:any,sec:string,ctx:string){
|
||||||
|
if (section.type == 'yaml') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async html_to_wxmp(html:string) {
|
let rhtml:any = null;
|
||||||
|
let ctx_map = this.ctx_map;
|
||||||
|
for(let k in ctx_map){
|
||||||
|
if(k.startsWith('section@')){
|
||||||
|
let tpl = ctx_map[k];
|
||||||
|
if (typeof tpl == 'function') {
|
||||||
|
rhtml = await tpl(section,sec);
|
||||||
|
} else {
|
||||||
|
let rendered = await this.plugin.easyapi.tpl.parse_templater(
|
||||||
|
tpl, true,{section:section,sec:sec,ctx:ctx},[0]
|
||||||
|
);
|
||||||
|
rendered = rendered.filter(x=>x);
|
||||||
|
if (rendered && rendered.length > 0 && rendered[0].trim() != '') {
|
||||||
|
rhtml = rendered[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rhtml){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rhtml){
|
||||||
|
if(section.type == 'code'){
|
||||||
|
let items = sec.split('\n');
|
||||||
|
items[0] = items[0].slice(0,3)+'js'+'\n//'+items[0].slice(3);
|
||||||
|
sec = items.join('\n');
|
||||||
|
}
|
||||||
|
let html = this.marked.marked(sec);
|
||||||
|
rhtml = await this.html_to_wxmp(html);
|
||||||
|
}
|
||||||
|
return rhtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
async selection_to_wxmp(){
|
||||||
|
let htmls = [this.blank_line];
|
||||||
|
let sel = this.plugin.easyapi.ceditor.cm.state.selection.main;
|
||||||
|
if(sel.from==sel.to){
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let ctx = await this.plugin.easyapi.ccontent;
|
||||||
|
if(!ctx){
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
for(let section of this.plugin.easyapi.cmeta?.sections || []){
|
||||||
|
if(sel.to<=section.position.start.offset){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if(sel.from>=section.position.end.offset){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let from = Math.max(sel.from,section.position.start.offset);
|
||||||
|
let to = Math.min(sel.to,section.position.end.offset);
|
||||||
|
let sec = ctx.slice(from,to).trim();
|
||||||
|
|
||||||
|
if(section.type=='code'){
|
||||||
|
let items = this.plugin.easyapi.editor.slice_by_position(
|
||||||
|
ctx, section.position
|
||||||
|
).trim().split('\n')
|
||||||
|
if(!sec.startsWith(items[0])){
|
||||||
|
sec = items[0]+'\n'+sec;
|
||||||
|
}
|
||||||
|
if(!sec.endsWith(items.last() || '')){
|
||||||
|
sec = sec + '\n' + items.last();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sec==''){continue}
|
||||||
|
let rhtml = await this.section_to_wxmp(section,sec,sec);
|
||||||
|
if(!rhtml){continue}
|
||||||
|
|
||||||
|
if(Array.isArray(rhtml)){
|
||||||
|
htmls.push(...rhtml);
|
||||||
|
}else{
|
||||||
|
htmls.push(rhtml);
|
||||||
|
}
|
||||||
|
htmls.push(this.blank_line)
|
||||||
|
}
|
||||||
|
this.copy_as_html(htmls);
|
||||||
|
}
|
||||||
|
|
||||||
|
async tfile_to_wxmp(tfile: TFile) {
|
||||||
|
let htmls = [this.blank_line];
|
||||||
|
let ctx = await this.app.vault.read(tfile);
|
||||||
|
for (let section of this.app.metadataCache.getFileCache(tfile)?.sections || []) {
|
||||||
|
if (section.type == 'yaml') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let sec = this.plugin.easyapi.editor.slice_by_position(ctx, section.position);
|
||||||
|
let rhtml = await this.section_to_wxmp(section,sec,ctx);
|
||||||
|
if(!rhtml){continue}
|
||||||
|
|
||||||
|
if(Array.isArray(rhtml)){
|
||||||
|
htmls.push(...rhtml);
|
||||||
|
}else{
|
||||||
|
htmls.push(rhtml);
|
||||||
|
}
|
||||||
|
htmls.push(this.blank_line);
|
||||||
|
}
|
||||||
|
this.copy_as_html(htmls);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async html_to_wxmp(html: string) {
|
||||||
let rhtml;
|
let rhtml;
|
||||||
|
|
||||||
// 替换图片
|
// 替换图片
|
||||||
|
|
@ -128,8 +254,10 @@ export class Wxmp {
|
||||||
rhtml = this.html_replace_code(rhtml)
|
rhtml = this.html_replace_code(rhtml)
|
||||||
|
|
||||||
// 替换标题
|
// 替换标题
|
||||||
for (let k in this.ctx_map) {
|
let ctx_map = this.ctx_map;
|
||||||
rhtml = await this.set_tag_with_tpl(rhtml, k, this.ctx_map[k]);
|
for (let k in ctx_map) {
|
||||||
|
if(k.contains('@')){continue;}
|
||||||
|
rhtml = await this.set_tag_with_tpl(rhtml, k, ctx_map[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [[格式化图片链接]]
|
// [[格式化图片链接]]
|
||||||
|
|
@ -142,7 +270,7 @@ export class Wxmp {
|
||||||
return rhtml
|
return rhtml
|
||||||
}
|
}
|
||||||
|
|
||||||
async set_tag_with_tpl(htmlString:string, selector:string, tpl:string|Function) {
|
async set_tag_with_tpl(htmlString: string, selector: string, tpl: string | Function) {
|
||||||
let parser = new DOMParser();
|
let parser = new DOMParser();
|
||||||
let doc = parser.parseFromString(htmlString, 'text/html');
|
let doc = parser.parseFromString(htmlString, 'text/html');
|
||||||
let items = doc.querySelectorAll(selector);
|
let items = doc.querySelectorAll(selector);
|
||||||
|
|
@ -150,12 +278,12 @@ export class Wxmp {
|
||||||
await Promise.all(Array.from(items).map(async (item) => {
|
await Promise.all(Array.from(items).map(async (item) => {
|
||||||
let content = item.textContent;
|
let content = item.textContent;
|
||||||
|
|
||||||
if(typeof tpl == 'function'){
|
if (typeof tpl == 'function') {
|
||||||
let rendered = tpl(content);
|
let rendered = tpl(content);
|
||||||
if(rendered){
|
if (rendered) {
|
||||||
item.innerHTML = rendered;
|
item.innerHTML = rendered;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
// 模板渲染,传入content
|
// 模板渲染,传入content
|
||||||
let rendered = await this.plugin.easyapi.tpl.parse_templater(tpl, true, content);
|
let rendered = await this.plugin.easyapi.tpl.parse_templater(tpl, true, content);
|
||||||
if (rendered.length > 0) {
|
if (rendered.length > 0) {
|
||||||
|
|
@ -171,7 +299,7 @@ export class Wxmp {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setLastLiMargin(htmlString:string) {
|
setLastLiMargin(htmlString: string) {
|
||||||
let parser = new DOMParser();
|
let parser = new DOMParser();
|
||||||
let doc = parser.parseFromString(htmlString, 'text/html');
|
let doc = parser.parseFromString(htmlString, 'text/html');
|
||||||
|
|
||||||
|
|
@ -197,7 +325,7 @@ export class Wxmp {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setParagraphSpacingBeforeList(htmlString:string) {
|
setParagraphSpacingBeforeList(htmlString: string) {
|
||||||
// 创建一个新的DOM解析器
|
// 创建一个新的DOM解析器
|
||||||
let parser = new DOMParser();
|
let parser = new DOMParser();
|
||||||
// 将HTML字符串解析为文档对象
|
// 将HTML字符串解析为文档对象
|
||||||
|
|
@ -222,7 +350,7 @@ export class Wxmp {
|
||||||
return modifiedHtmlString;
|
return modifiedHtmlString;
|
||||||
}
|
}
|
||||||
|
|
||||||
html_replace_url(html:string) {
|
html_replace_url(html: string) {
|
||||||
let regx = /<a[^>]*class="external-link"[^>]*href="(.*?)"[^>]*?>([\s\S]*?)<\/a>/g
|
let regx = /<a[^>]*class="external-link"[^>]*href="(.*?)"[^>]*?>([\s\S]*?)<\/a>/g
|
||||||
let rhtml = html.replace(regx, (m, href, text) => {
|
let rhtml = html.replace(regx, (m, href, text) => {
|
||||||
|
|
||||||
|
|
@ -245,7 +373,7 @@ export class Wxmp {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
html_replace_code(html:string) {
|
html_replace_code(html: string) {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(html, 'text/html');
|
const doc = parser.parseFromString(html, 'text/html');
|
||||||
const codeBlocks = Array.from(doc.querySelectorAll('pre > code[class^="language-"]'));
|
const codeBlocks = Array.from(doc.querySelectorAll('pre > code[class^="language-"]'));
|
||||||
|
|
@ -344,10 +472,16 @@ export class Wxmp {
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
async copy_as_html(ctx: string | string[]) {
|
async copy_as_html(ctx: string | string[],pre_blank=true,next_blank=true) {
|
||||||
if (typeof (ctx) == 'string') {
|
if (typeof (ctx) == 'string') {
|
||||||
ctx = [ctx]
|
ctx = [ctx]
|
||||||
}
|
}
|
||||||
|
if(pre_blank && ctx[0]!=this.blank_line){
|
||||||
|
ctx.unshift(this.blank_line)
|
||||||
|
}
|
||||||
|
if(next_blank && ctx[ctx.length-1]!=this.blank_line){
|
||||||
|
ctx.push(this.blank_line)
|
||||||
|
}
|
||||||
let data = new ClipboardItem({
|
let data = new ClipboardItem({
|
||||||
"text/html": new Blob(ctx, {
|
"text/html": new Blob(ctx, {
|
||||||
type: "text/html"
|
type: "text/html"
|
||||||
|
|
@ -359,7 +493,7 @@ export class Wxmp {
|
||||||
await navigator.clipboard.write([data]);
|
await navigator.clipboard.write([data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatWeChatImageLink(inputHtml:string) {
|
formatWeChatImageLink(inputHtml: string) {
|
||||||
// 创建一个临时的 div 元素来解析 HTML
|
// 创建一个临时的 div 元素来解析 HTML
|
||||||
let tempDiv = document.createElement('div');
|
let tempDiv = document.createElement('div');
|
||||||
tempDiv.innerHTML = inputHtml;
|
tempDiv.innerHTML = inputHtml;
|
||||||
|
|
@ -397,36 +531,125 @@ export class Wxmp {
|
||||||
return tempDiv.innerHTML;
|
return tempDiv.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
format_wxmp_h1(title:string){
|
format_wxmp_h1(title: string) {
|
||||||
let css = `
|
let css = `
|
||||||
<h1 style="box-sizing: border-box; border-width: 0px 0px 2px; border-style: solid; border-bottom-color: rgb(0, 152, 116); font-size: 19.6px; font-weight: bold; margin: 2em auto 1em; text-align: center; line-height: 1.75; font-family: Menlo, Monaco, "Courier New", monospace; display: table; padding: 0.5em 1em; color: rgb(63, 63, 63); text-shadow: rgba(0, 0, 0, 0.1) 2px 2px 4px; visibility: visible;"><span leaf="" style="visibility: visible;">${title}</span></h1>
|
<h1 style="box-sizing: border-box; border-width: 0px 0px 2px; border-style: solid; border-bottom-color: rgb(0, 152, 116); font-size: 19.6px; font-weight: bold; margin: 2em auto 1em; text-align: center; line-height: 1.75; font-family: Menlo, Monaco, "Courier New", monospace; display: table; padding: 0.5em 1em; color: rgb(63, 63, 63); text-shadow: rgba(0, 0, 0, 0.1) 2px 2px 4px; visibility: visible;"><span leaf="" style="visibility: visible;">${title}</span></h1>
|
||||||
`.trim()
|
`.trim()
|
||||||
return css;
|
return css;
|
||||||
}
|
}
|
||||||
|
|
||||||
format_wxmp_h2(title:string){
|
format_wxmp_h2(title: string) {
|
||||||
let css = `
|
let css = `
|
||||||
<h2 style="box-sizing: border-box;border-width: 0px;border-style: solid;border-color: hsl(var(--border));font-size: 18.2px;font-weight: bold;margin: 4em auto 2em;text-align: center;line-height: 1.75;font-family: Menlo, Monaco, "Courier New", monospace;display: table;padding: 0.3em 1em;color: rgb(255, 255, 255);background: rgb(0, 152, 116);border-radius: 8px;box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px;"><span leaf="">${title}</span></h2>
|
<h2 style="box-sizing: border-box;border-width: 0px;border-style: solid;border-color: hsl(var(--border));font-size: 18.2px;font-weight: bold;margin: 4em auto 2em;text-align: center;line-height: 1.75;font-family: Menlo, Monaco, "Courier New", monospace;display: table;padding: 0.3em 1em;color: rgb(255, 255, 255);background: rgb(0, 152, 116);border-radius: 8px;box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px;"><span leaf="">${title}</span></h2>
|
||||||
`.trim()
|
`.trim()
|
||||||
return css;
|
return css;
|
||||||
}
|
}
|
||||||
|
|
||||||
format_wxmp_h3(title:string){
|
format_wxmp_h3(title: string) {
|
||||||
let css = `
|
let css = `
|
||||||
<h3 style="box-sizing: border-box;border-width: 0px 0px 1px 4px;border-style: solid solid dashed;border-bottom-color: rgb(0, 152, 116);border-left-color: rgb(0, 152, 116);font-size: 16.8px;font-weight: bold;margin: 2em 8px 0.75em 0px;text-align: left;line-height: 1.2;font-family: Menlo, Monaco, "Courier New", monospace;padding-left: 12px;color: rgb(63, 63, 63);"><span leaf="">${title}</span></h3>
|
<h3 style="box-sizing: border-box;border-width: 0px 0px 1px 4px;border-style: solid solid dashed;border-bottom-color: rgb(0, 152, 116);border-left-color: rgb(0, 152, 116);font-size: 16.8px;font-weight: bold;margin: 2em 8px 0.75em 0px;text-align: left;line-height: 1.2;font-family: Menlo, Monaco, "Courier New", monospace;padding-left: 12px;color: rgb(63, 63, 63);"><span leaf="">${title}</span></h3>
|
||||||
`.trim()
|
`.trim()
|
||||||
return css;
|
return css;
|
||||||
}
|
}
|
||||||
|
|
||||||
format_wxmp_p_code(code:string){
|
format_wxmp_p_code(code: string) {
|
||||||
let css = `
|
let css = `
|
||||||
<code style="box-sizing: border-box; border-width: 0px; border-style: solid; border-color: hsl(var(--border)); font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-feature-settings: normal; font-variation-settings: normal; font-size: 12.6px; text-align: left; line-height: 1.75; color: rgb(221, 17, 68); background: rgba(27, 31, 35, 0.05); padding: 3px 5px; border-radius: 4px; visibility: visible;"><span leaf="" style="visibility: visible;">${code}</span></code>`.trim()
|
<code style="box-sizing: border-box; border-width: 0px; border-style: solid; border-color: hsl(var(--border)); font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-feature-settings: normal; font-variation-settings: normal; font-size: 12.6px; text-align: left; line-height: 1.75; color: rgb(221, 17, 68); background: rgba(27, 31, 35, 0.05); padding: 3px 5px; border-radius: 4px; visibility: visible;"><span leaf="" style="visibility: visible;">${code}</span></code>`.trim()
|
||||||
return css;
|
return css;
|
||||||
}
|
}
|
||||||
|
|
||||||
format_wxmp_li_code(code:string){
|
format_wxmp_li_code(code: string) {
|
||||||
let css = `
|
let css = `
|
||||||
<code style="box-sizing: border-box; border-width: 0px; border-style: solid; border-color: hsl(var(--border)); font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-feature-settings: normal; font-variation-settings: normal; font-size: 12.6px; text-align: left; line-height: 1.75; color: rgb(221, 17, 68); background: rgba(27, 31, 35, 0.05); padding: 3px 5px; border-radius: 4px; visibility: visible;"><span leaf="" style="visibility: visible;">${code}</span></code>`.trim()
|
<code style="box-sizing: border-box; border-width: 0px; border-style: solid; border-color: hsl(var(--border)); font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-feature-settings: normal; font-variation-settings: normal; font-size: 12.6px; text-align: left; line-height: 1.75; color: rgb(221, 17, 68); background: rgba(27, 31, 35, 0.05); padding: 3px 5px; border-radius: 4px; visibility: visible;"><span leaf="" style="visibility: visible;">${code}</span></code>`.trim()
|
||||||
return css;
|
return css;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_code_balck(section:any,sec:string,lang:string){
|
||||||
|
return section.type == 'code' && sec.trim().slice(3).startsWith(lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
async format_code_block_cards_album(section: any, sec:string) {
|
||||||
|
if(section.type != 'code' || !sec.trim().slice(3).startsWith('cards-album')){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let items = [];
|
||||||
|
for(let ctx of sec.split('images:')[1].split(/\n\s+/)){
|
||||||
|
if(ctx.trim() == ''){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let img = await this.images2html(ctx);
|
||||||
|
if(img){
|
||||||
|
items.push(img);;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(items.length == 0){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
async format_section_html(section: any, sec:string) {
|
||||||
|
if(section.type=='html'){
|
||||||
|
return sec;
|
||||||
|
}else{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
generateSideBySideImages(base64Images: string[]) {
|
||||||
|
const sectionStart = `<section style="color: rgb(0, 0, 0);font-family: 'Microsoft YaHei';font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: start;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;margin-bottom: 18px;display: flex;justify-content: space-between;flex-shrink: 0;">`;
|
||||||
|
|
||||||
|
const sectionEnd = `</section>`;
|
||||||
|
|
||||||
|
const imageSections = base64Images.map((base64, index) => {
|
||||||
|
return `
|
||||||
|
<section key="${index}" style="font-family: 'PingFang SC', system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;display: inline-block;flex: 1 1 0%;padding: 0px 4px;">
|
||||||
|
<section>
|
||||||
|
<section style="text-align: center;margin-bottom: 12px;" nodeleaf="">
|
||||||
|
<img alt="Image" class="rich_pages wxw-img"
|
||||||
|
style="display: inline-block; max-width: 100%; height: auto !important; border-radius: 12px; visibility: visible !important; width: 330.5px !important;"
|
||||||
|
src="${base64}" crossorigin="anonymous">
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
`;
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
return sectionStart + imageSections + sectionEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
async images2html(imgs: string | string[]): Promise<string | null> {
|
||||||
|
if (Array.isArray(imgs)) {
|
||||||
|
let ximgs = [];
|
||||||
|
for (let x of imgs) {
|
||||||
|
if (x.startsWith('http')) {
|
||||||
|
ximgs.push(x)
|
||||||
|
} else {
|
||||||
|
let c = await this.image_to_img(x, true);
|
||||||
|
ximgs.push(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ximgs = ximgs.filter((x): x is string => x !== undefined && x !== null);
|
||||||
|
if (ximgs.length > 0) {
|
||||||
|
let html = this.generateSideBySideImages(ximgs);
|
||||||
|
return html
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (typeof imgs == 'string') {
|
||||||
|
let obsidianImgs = imgs.match(/!?\[\[([^|\]]+\.(?:png|jpg|jpeg|gif|webp|bmp|svg))(?:\|.*?)?]]/g) || [];
|
||||||
|
|
||||||
|
let markdownImgs = Array.from(
|
||||||
|
imgs.matchAll(/!\[\]\((https?:\/\/[^\s)]+)\)/g),
|
||||||
|
m => m[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 合并两种图片链接
|
||||||
|
let ximgs = [...obsidianImgs, ...markdownImgs];
|
||||||
|
return this.images2html(ximgs);
|
||||||
|
}else{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue