This commit is contained in:
frostime 2023-05-19 19:50:46 +08:00
parent 760a54efd1
commit ef59d612c3
23 changed files with 2139 additions and 0 deletions

387
src/api.ts Normal file
View file

@ -0,0 +1,387 @@
/**
* Copyright (c) 2023 frostime. All rights reserved.
* https://github.com/frostime/sy-plugin-template-vite
*
* See API Document in [API.md](https://github.com/siyuan-note/siyuan/blob/master/API.md)
* API [API_zh_CN.md](https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md)
*/
import {
Block, Notebook, NotebookConf, NotebookId, DocumentId, BlockId,
doOperation, PreviousID, ParentID, BlockType, BlockSubType
} from "sy-dtype";
import { fetchSyncPost, IWebSocketData } from "siyuan";
async function request(url: string, data: any) {
let response: IWebSocketData = await fetchSyncPost(url, data);
let res = response.code === 0 ? response.data : null;
return res;
}
// **************************************** Noteboook ****************************************
export type ReslsNotebooks = {
notebooks: Notebook[];
}
export async function lsNotebooks(): Promise<ReslsNotebooks> {
let url = '/api/notebook/lsNotebooks';
return request(url, '');
}
export async function openNotebook(notebook: NotebookId) {
let url = '/api/notebook/openNotebook';
return request(url, { notebook: notebook });
}
export async function closeNotebook(notebook: NotebookId) {
let url = '/api/notebook/closeNotebook';
return request(url, { notebook: notebook });
}
export async function renameNotebook(notebook: NotebookId, name: string) {
let url = '/api/notebook/renameNotebook';
return request(url, { notebook: notebook, name: name });
}
export async function createNotebook(name: string): Promise<Notebook> {
let url = '/api/notebook/createNotebook';
return request(url, { name: name });
}
export async function removeNotebook(notebook: NotebookId) {
let url = '/api/notebook/removeNotebook';
return request(url, { notebook: notebook });
}
export type ResGetNotebookConf = {
box: string;
conf: NotebookConf;
name: string;
}
export async function getNotebookConf(notebook: NotebookId): Promise<ResGetNotebookConf> {
let data = { notebook: notebook };
let url = '/api/notebook/getNotebookConf';
return request(url, data);
}
export async function setNotebookConf(notebook: NotebookId, conf: NotebookConf): Promise<NotebookConf> {
let data = { notebook: notebook, conf: conf };
let url = '/api/notebook/setNotebookConf';
return request(url, data);
}
// **************************************** Document ****************************************
export async function createDocWithMd(notebook: NotebookId, path: string, markdown: string): Promise<DocumentId> {
let data = {
notebook: notebook,
path: path,
markdown: markdown,
};
let url = '/api/filetree/createDocWithMd';
return request(url, data);
}
export async function renameDoc(notebook: NotebookId, path: string, title: string): Promise<DocumentId> {
let data = {
doc: notebook,
path: path,
title: title
};
let url = '/api/filetree/renameDoc';
return request(url, data);
}
export async function removeDoc(notebook: NotebookId, path: string) {
let data = {
notebook: notebook,
path: path,
};
let url = '/api/filetree/removeDoc';
return request(url, data);
}
export async function moveDocs(fromPaths: string[], toNotebook: NotebookId, toPath: string) {
let data = {
fromPaths: fromPaths,
toNotebook: toNotebook,
toPath: toPath
};
let url = '/api/filetree/moveDocs';
return request(url, data);
}
export async function getHPathByPath(notebook: NotebookId, path: string): Promise<string> {
let data = {
notebook: notebook,
path: path
};
let url = '/api/filetree/getHPathByPath';
return request(url, data);
}
export async function getHPathByID(id: BlockId): Promise<string> {
let data = {
id: id
};
let url = '/api/filetree/getHPathByID';
return request(url, data);
}
// **************************************** Asset Files ****************************************
export type ResUpload = {
errFiles: string[];
succMap: { [key: string]: string };
}
export async function upload(assetsDirPath: string, files: any[]): Promise<ResUpload> {
let form = new FormData();
form.append('assetsDirPath', assetsDirPath);
for (let file of files) {
form.append('file[]', file);
}
let url = '/api/asset/upload';
return request(url, form);
}
// **************************************** Block ****************************************
export type ResdoOperations = {
doOperations: doOperation[];
undoOperations: doOperation[] | null;
}
type DataType = "markdown" | "dom";
export async function insertBlock(dataType: DataType, data: string, previousID: BlockId): Promise<ResdoOperations> {
let data1 = {
dataType: dataType,
data: data,
previousID: previousID
}
let url = '/api/block/insertBlock';
return request(url, data1);
}
export async function appendBlock(dataType: DataType, data: string, parentID: BlockId | DocumentId): Promise<ResdoOperations> {
let data1 = {
dataType: dataType,
data: data,
parentID: parentID
}
let url = '/api/block/appendBlock';
return request(url, data1);
}
export async function updateBlock(dataType: DataType, data: string, id: BlockId): Promise<ResdoOperations> {
let data1 = {
dataType: dataType,
data: data,
id: id
}
let url = '/api/block/updateBlock';
return request(url, data1);
}
export async function deleteBlock(id: BlockId): Promise<ResdoOperations> {
let data = {
id: id
}
let url = '/api/block/deleteBlock';
return request(url, data);
}
export async function moveBlock(id: BlockId, previousID: PreviousID | null = null, parentID: ParentID | null = null): Promise<ResdoOperations> {
let data = {
id: id,
previousID: previousID,
parentID: parentID
}
let url = '/api/block/moveBlock';
return request(url, data);
}
export type ResGetBlockKramdown = {
id: BlockId;
kramdown: string;
}
export async function getBlockKramdown(id: BlockId): Promise<ResGetBlockKramdown> {
let data = {
id: id
}
let url = '/api/block/getBlockKramdown';
return request(url, data);
}
export type ChildBlock = {
id: BlockId;
type: BlockType;
subtype?: BlockSubType;
}
export async function getChildBlocks(id: BlockId): Promise<ChildBlock[]> {
let data = {
id: id
}
let url = '/api/block/getChildBlocks';
return request(url, data);
}
// **************************************** Attributes ****************************************
export async function setBlockAttrs(id: BlockId, attrs: { [key: string]: string }) {
let data = {
id: id,
attrs: attrs
}
let url = '/api/attr/setBlockAttrs';
return request(url, data);
}
export async function getBlockAttrs(id: BlockId): Promise<{ [key: string]: string }> {
let data = {
id: id
}
let url = '/api/attr/getBlockAttrs';
return request(url, data);
}
// **************************************** SQL ****************************************
export async function sql(sql: string): Promise<any[]> {
let sqldata = {
stmt: sql,
};
let url = '/api/query/sql';
return request(url, sqldata);
}
export async function getBlockByID(blockId: string): Promise<Block> {
let sqlScript = `select * from blocks where id ='${blockId}'`;
let data = await sql(sqlScript);
return data[0];
}
// **************************************** Template ****************************************
export type ResGetTemplates = {
content: string;
path: string;
}
export async function render(id: DocumentId, path: string): Promise<ResGetTemplates> {
let data = {
id: id,
path: path
}
let url = '/api/template/render';
return request(url, data);
}
export async function renderSprig(template: string): Promise<string> {
let url = '/api/template/renderSprig';
return request(url, { template: template });
}
// **************************************** File ****************************************
export async function getFile(path: string): Promise<any> {
let data = {
path: path
}
let url = '/api/file/getFile';
return request(url, data);
}
export async function putFile(path: string, isDir: boolean, file: any) {
let form = new FormData();
form.append('path', path);
form.append('isDir', isDir.toString());
// Copyright (c) 2023, terwer.
// https://github.com/terwer/siyuan-plugin-importer/blob/v1.4.1/src/api/kernel-api.ts
form.append('modTime', Math.floor(Date.now() / 1000).toString());
form.append('file', file);
let url = '/api/file/putFile';
return request(url, form);
}
export async function removeFile(path: string) {
let data = {
path: path
}
let url = '/api/file/removeFile';
return request(url, data);
}
export type ResReadDir = {
isDir: boolean;
name: string;
}
export async function readDir(path: string): Promise<ResReadDir> {
let data = {
path: path
}
let url = '/api/file/readDir';
return request(url, data);
}
export type ResExportMdContent = {
hPath: string;
content: string;
}
export async function exportMdContent(id: DocumentId): Promise<ResExportMdContent> {
let data = {
id: id
}
let url = '/api/export/exportMdContent';
return request(url, data);
}
export type PandocArgs = string;
export async function pandoc(args: PandocArgs[]) {
let data = {
args: args
}
let url = '/api/convert/pandoc';
return request(url, data);
}
// **************************************** System ****************************************
export type ResBootProgress = {
progress: number;
details: string;
}
export async function bootProgress(): Promise<ResBootProgress> {
return request('/api/system/bootProgress', {});
}
export async function version(): Promise<string> {
return request('/api/system/version', {});
}
export async function currentTime(): Promise<number> {
return request('/api/system/currentTime', {});
}

70
src/hello.svelte Normal file
View file

@ -0,0 +1,70 @@
<!--
* Copyright (c) 2023 frostime. All rights reserved.
* https://github.com/frostime/sy-plugin-template-vite
-->
<script lang="ts">
import { onMount } from "svelte";
import { version } from "./api";
export let name: string;
let time;
let ver;
onMount(async () => {
time = new Date();
ver = await version();
});
$: time_str = new Date(time).toLocaleTimeString();
setInterval(async () => {
time = new Date();
}, 1000);
</script>
<div id="hello">
<div class="row">
<div class="col left">
<h2>Hello {name} v{ver}</h2>
</div>
<div class="col right">
{time_str}
</div>
</div>
<br />
<p>
使用这个模板之前,请阅读<a
href="https://github.com/siyuan-note/plugin-sample">官方教程</a
>, 确保自己已经理解了插件的基本开发流程。
</p>
<p>
Before using this template, please read the <a
href="https://github.com/siyuan-note/plugin-sample">offical sample</a
>, make sure that you've known about the pipeline for plugin developing.
</p>
</div>
<style lang="scss">
#hello {
margin: 1rem;
text-align: left;
}
.row {
display: flex;
flex-direction: row;
.col {
flex: 1;
}
.left {
text-align: left;
}
.right {
text-align: right;
}
}
</style>

3
src/i18n/en_US.json Normal file
View file

@ -0,0 +1,3 @@
{
"name": "SiYuan"
}

3
src/i18n/zh_CN.json Normal file
View file

@ -0,0 +1,3 @@
{
"name": "思源"
}

3
src/index.scss Normal file
View file

@ -0,0 +1,3 @@
#helloPanel {
border: 1px rgb(189, 119, 119) dashed;
}

38
src/index.ts Normal file
View file

@ -0,0 +1,38 @@
/**
* Copyright (c) 2023 frostime. All rights reserved.
* https://github.com/frostime/sy-plugin-template-vite
*/
import { Plugin, showMessage, Dialog } from "siyuan"
import Hello from "./hello.svelte"
import "./index.scss"
export default class SamplePlugin extends Plugin {
async onload() {
console.log("onload");
showMessage("Hello World");
this.addTopBar(
{
icon: "iconEmoji",
"title": "Hello SiYuan",
"callback": () => {
let dialog = new Dialog({
title: "Hello World",
content: `<div id="helloPanel"></div>`,
});
new Hello({
target: dialog.element.querySelector("#helloPanel"),
props: {
name: this.i18n.name,
}
});
}
}
)
}
async onunload() {
showMessage("Goodbye World");
console.log("onunload");
}
}

206
src/siyuan.d.ts vendored Normal file
View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2023, Terwer . All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Terwer designates this
* particular file as subject to the "Classpath" exception as provided
* by Terwer in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com
* or visit www.terwer.space if you need additional information or have any
* questions.
*/
declare module "siyuan" {
type TEventBus = "ws-main"
interface IObject {
[key: string]: string;
}
interface IWebSocketData {
cmd: string
callback?: string
data: any
msg: string
code: number
sid: string
}
declare interface IPluginDockTab {
position: "LeftTop" | "LeftBottom" | "RightTop" | "RightBottom" | "BottomLeft" | "BottomRight",
size: { width: number, height: number },
icon: string,
hotkey?: string,
title: string,
}
interface IMenuItemOption {
label?: string,
click?: (element: HTMLElement) => void,
type?: "separator" | "submenu" | "readonly",
accelerator?: string,
action?: string,
id?: string,
submenu?: IMenuItemOption[]
disabled?: boolean
icon?: string
iconHTML?: string
current?: boolean
bind?: (element: HTMLElement) => void
}
export function fetchPost(url: string, data?: any, cb?: (response: IWebSocketData) => void, headers?: IObject): void;
export function fetchSyncPost(url: string, data?: any): Promise<IWebSocketData>;
export function fetchGet(url: string, cb: (response: IWebSocketData) => void): void;
export function openTab(options: {
custom?: {
title: string,
icon: string,
data?: any
fn?: () => any,
} // card 和自定义页签 必填
position?: "right" | "bottom",
keepCursor?: boolean // 是否跳转到新 tab 上
removeCurrentTab?: boolean // 在当前页签打开时需移除原有页签
afterOpen?: () => void // 打开后回调
}): void
export function isMobile(): boolean;
export function adaptHotkey(hotkey: string): string;
export function confirm(title: string, text: string, confirmCB?: () => void, cancelCB?: () => void): void;
/**
* @param timeout - ms. 0: manual close-1: always show; 6000: default
* @param {string} [type=info]
*/
export function showMessage(text: string, timeout?: number, type?: "info" | "error", id?: string): void;
export class App {
plugins: Plugin[];
}
export abstract class Plugin {
eventBus: EventBus;
i18n: IObject;
data: any;
name: string;
constructor(options: {
app: App,
id: string,
name: string,
i18n: IObject
})
onload(): void;
onunload(): void;
/*
* @param {string} [options.position=right]
*/
addTopBar(options: {
icon: string,
title: string,
callback: (evt: MouseEvent) => void
position?: "right" | "left"
}): HTMLDivElement;
openSetting(): void
// registerCommand(command: IPluginCommand): void;
// registerSettingRender(settingRender: SettingRender): void;
loadData(storageName: string): Promise<any>;
saveData(storageName: string, content: any): Promise<void>;
removeData(storageName: string): Promise<any>;
addTab(options: {
type: string,
destroy?: () => void,
resize?: () => void,
update?: () => void,
init: () => void
}): () => any
addDock(options: {
config: IPluginDockTab,
data: any,
type: string,
destroy?: () => void,
resize?: () => void,
update?: () => void,
init: () => void
}): any
}
export class EventBus {
on(type: TEventBus, listener: (event: CustomEvent<any>) => void): void;
once(type: TEventBus, listener: (event: CustomEvent<any>) => void): void;
off(type: TEventBus, listener: (event: CustomEvent<any>) => void): void;
emit(type: TEventBus, detail?: any): boolean;
}
export class Dialog {
element: HTMLElement;
constructor(options: {
title?: string,
transparent?: boolean,
content: string,
width?: string
height?: string,
destroyCallback?: (options?: IObject) => void
disableClose?: boolean
disableAnimation?: boolean
});
destroy(options?: IObject): void;
bindInput(inputElement: HTMLInputElement | HTMLTextAreaElement, enterEvent?: () => void): void;
}
export class Menu {
constructor(id?: string, closeCB?: () => void);
showSubMenu(subMenuElement: HTMLElement): void;
addItem(options: IMenuItemOption): HTMLElement;
addSeparator(): void;
open(options: { x: number, y: number, h?: number, w?: number, isLeft?: boolean }): void;
/*
* @param {string} [position=all]
*/
fullscreen(position?: "bottom" | "all"): void;
close(): void;
}
}

70
src/sy-dtype.d.ts vendored Normal file
View file

@ -0,0 +1,70 @@
/**
* Copyright (c) 2023 frostime. All rights reserved.
* https://github.com/frostime/sy-plugin-template-vite
*/
/**
*
*/
declare module "sy-dtype" {
export type DocumentId = string;
export type BlockId = string;
export type NotebookId = string;
export type PreviousID = BlockId;
export type ParentID = BlockId | DocumentId;
export type Notebook = {
id: NotebookId;
name: string;
icon: string;
sort: number;
closed: boolean;
}
export type NotebookConf = {
name: string;
closed: boolean;
refCreateSavePath: string;
createDocNameTemplate: string;
dailyNoteSavePath: string;
dailyNoteTemplatePath: string;
}
export type BlockType = "d" | "s" | "h" | "t" | "i" | "p" | "f" | "audio" | "video" | "other";
export type BlockSubType = "d1" | "d2" | "s1" | "s2" | "s3" | "t1" | "t2" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "table" | "task" | "toggle" | "latex" | "quote" | "html" | "code" | "footnote" | "cite" | "collection" | "bookmark" | "attachment" | "comment" | "mindmap" | "spreadsheet" | "calendar" | "image" | "audio" | "video" | "other";
export type Block = {
id: BlockId;
parent_id?: BlockId;
root_id: DocumentId;
hash: string;
box: string;
path: string;
hpath: string;
name: string;
alias: string;
memo: string;
tag: string;
content: string;
fcontent?: string;
markdown: string;
length: number;
type: BlockType;
subtype: BlockSubType;
ial?: { [key: string]: string };
sort: number;
created: string;
updated: string;
}
export type doOperation = {
action: string;
data: string;
id: BlockId;
parentID: BlockId | DocumentId;
previousID: BlockId;
retData: null;
}
}