Initial Upload
Some checks failed
Create Release on Tag Push / build (push) Has been cancelled

This commit is contained in:
MassiveBox 2025-03-31 19:14:17 +02:00
parent 777f31761c
commit eaca7fc192
Signed by: massivebox
GPG key ID: 9B74D3A59181947D
20 changed files with 469 additions and 1718 deletions

7
src/const.ts Normal file
View file

@ -0,0 +1,7 @@
export const SVG_MIME = "image/svg+xml";
export const JSON_MIME = "application/json";
export const DATA_PATH = "/data/assets";
export const STORAGE_PATH = "/data/storage/petal/siyuan-jsdraw-plugin";
export const TOOLBAR_PATH = STORAGE_PATH + "/toolbar.json";
export const CONFIG_PATH = STORAGE_PATH + "/conf.json";
export const EMBED_PATH = "/plugins/siyuan-jsdraw-plugin/webapp/?id=";

75
src/editorTab.ts Normal file
View file

@ -0,0 +1,75 @@
import {ITabModel, openTab, Plugin} from "siyuan"
import Editor, {BaseWidget, EditorEventType} from "js-draw";
import { MaterialIconProvider } from '@js-draw/material-icons';
import 'js-draw/styles';
import {getFile, saveFile} from "@/file";
import {JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const";
import {idToPath} from "@/helper";
export function openEditorTab(p: Plugin, fileID: string) {
openTab({
app: p.app,
custom: {
title: 'Drawing',
icon: 'iconDraw',
id: "siyuan-jsdraw-pluginwhiteboard",
data: { id: fileID }
}
});
}
async function saveCallback(editor: Editor, fileID: string, saveButton: BaseWidget) {
const svgElem = editor.toSVG();
try {
saveFile(idToPath(fileID), SVG_MIME, svgElem.outerHTML);
saveButton.setDisabled(true);
setTimeout(() => { // @todo improve save button feedback
saveButton.setDisabled(false);
}, 500);
} catch (error) {
alert("Error saving drawing! Enter developer mode to find the error, and a copy of the current status.");
console.error(error);
console.log("Couldn't save SVG: ", svgElem.outerHTML)
}
}
export function createEditor(i: ITabModel) {
const fileID = i.data.id;
if(fileID == null) {
alert("File ID missing - couldn't open file.")
return;
}
const editor = new Editor(i.element, {
iconProvider: new MaterialIconProvider(),
});
const toolbar = editor.addToolbar();
// restore toolbar state
getFile(TOOLBAR_PATH).then(toolbarState => {
if(toolbarState!= null) {
toolbar.deserializeState(toolbarState)
}
});
// restore drawing
getFile(idToPath(fileID)).then(svg => {
if(svg != null) {
editor.loadFromSVG(svg);
}
});
// save logic
const saveButton = toolbar.addSaveButton(() => saveCallback(editor, fileID, saveButton));
// save toolbar config on tool change (toolbar state is not saved in SVGs!)
editor.notifier.on(EditorEventType.ToolUpdated, () => {
saveFile(TOOLBAR_PATH, JSON_MIME, toolbar.serializeState());
});
editor.dispatch(editor.setBackgroundStyle({ autoresize: true }), false);
editor.getRootElement().style.height = '100%';
}

37
src/file.ts Normal file
View file

@ -0,0 +1,37 @@
import {getFileBlob, putFile} from "@/api";
function toFile(title: string, content: string, mimeType: string){
const blob = new Blob([content], { type: mimeType });
return new File([blob], title, { type: mimeType });
}
export function saveFile(path: string, mimeType: string, content: string) {
const file = toFile(path.split('/').pop(), content, mimeType);
try {
putFile(path, false, file);
} catch (error) {
console.error("Error saving file:", error);
throw error;
}
}
export async function getFile(path: string) {
const blob = await getFileBlob(path);
const jsonText = await blob.text();
// if we got a 404 api response, we will return null
try {
const res = JSON.parse(jsonText);
if(res.code == 404) {
return null;
}
}catch {}
// js-draw expects a string!
return jsonText;
}

View file

@ -1,63 +0,0 @@
<!--
Copyright (c) 2024 by frostime. All Rights Reserved.
Author : frostime
Date : 2023-11-19 12:30:45
FilePath : /src/hello.svelte
LastEditTime : 2024-10-16 14:37:50
Description :
-->
<script lang="ts">
import { onDestroy, onMount } from "svelte";
import { version, sql as query } from "@/api";
import { showMessage, fetchPost, Protyle } from "siyuan";
export let app;
let time: string = "";
let ver: string = "";
let divProtyle: HTMLDivElement;
let protyle: any;
let blockID: string = '';
onMount(async () => {
ver = await version();
fetchPost("/api/system/currentTime", {}, (response) => {
time = new Date(response.data).toString();
});
protyle = await initProtyle();
});
onDestroy(() => {
showMessage("Hello panel closed");
protyle.destroy();
});
async function initProtyle() {
let sql = "SELECT * FROM blocks ORDER BY RANDOM () LIMIT 1;";
let blocks: Block[] = await query(sql);
blockID = blocks[0].id;
return new Protyle(app, divProtyle, {
blockId: blockID
});
}
</script>
<div class="b3-dialog__content">
<div>appId:</div>
<div class="fn__hr"></div>
<div class="plugin-sample__time">${app?.appId}</div>
<div class="fn__hr"></div>
<div class="fn__hr"></div>
<div>API demo:</div>
<div class="fn__hr" />
<div class="plugin-sample__time">
System current time: <span id="time">{time}</span>
</div>
<div class="fn__hr" />
<div class="fn__hr" />
<div>Protyle demo: id = {blockID}</div>
<div class="fn__hr" />
<div id="protyle" style="height: 360px;" bind:this={divProtyle}/>
</div>

57
src/helper.ts Normal file
View file

@ -0,0 +1,57 @@
import { Plugin } from 'siyuan';
import {DATA_PATH, EMBED_PATH} from "@/const";
const drawIcon: string = `
<symbol id="iconDraw" viewBox="0 0 28 28">
<path clip-rule="evenodd" d="M26.4097 9.61208C27.196 8.8358 27.1969 7.57578 26.4117 6.79842L21.1441 1.58305C20.3597 0.806412 19.0875 0.805538 18.302 1.5811L3.55214 16.1442C3.15754 16.5338 2.87982 17.024 2.74985 17.5603L1.05726 24.5451C0.697341 26.0304 2.09375 27.3461 3.57566 26.918L10.3372 24.9646C10.8224 24.8244 11.2642 24.5658 11.622 24.2125L26.4097 9.61208ZM20.4642 12.6725L10.2019 22.8047C10.0827 22.9225 9.9354 23.0087 9.77366 23.0554L4.17079 24.6741C3.65448 24.8232 3.16963 24.359 3.2962 23.8367L4.70476 18.024C4.74809 17.8453 4.84066 17.6819 4.97219 17.552L15.195 7.45865L20.4642 12.6725ZM21.8871 11.2676L16.618 6.05372L19.0185 3.68356C19.4084 3.29865 20.0354 3.29908 20.4247 3.68454L24.271 7.49266C24.6666 7.88436 24.6661 8.52374 24.27 8.91488L21.8871 11.2676Z" fill-rule="evenodd"/>
</symbol>
`;
export function loadIcons(p: Plugin) {
const icons = drawIcon;
p.addIcons(icons);
}
export function getMenuHTML(icon: string, text: string): string {
return `
<div class="b3-list-item__first">
<svg class="b3-list-item__graphic">
<use xlink:href="#${icon}"></use>
</svg>
<span class="b3-list-item__text">${text}</span>
</div>
`;
}
export function generateSiyuanId() {
const now = new Date();
const year = now.getFullYear().toString();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
const timestamp = `${year}${month}${day}${hours}${minutes}${seconds}`;
const characters = 'abcdefghijklmnopqrstuvwxyz';
let random = '';
for (let i = 0; i < 7; i++) {
random += characters.charAt(Math.floor(Math.random() * characters.length));
}
return `${timestamp}-${random}`;
}
export function idToPath(id: string) {
return DATA_PATH + '/' + id + '.svg';
}
// [Edit](siyuan://plugins/siyuan-jsdraw-pluginwhiteboard/?icon=iconDraw&title=Drawing&data={"id":"${id}"})
// ![Drawing](assets/${id}.svg)
export function getPreviewHTML(id: string): string {
return `
<iframe src="${EMBED_PATH + id}&antiCache=0"></iframe>
`
}

View file

View file

@ -1,943 +1,44 @@
import {
Plugin,
showMessage,
confirm,
Dialog,
Menu,
openTab,
adaptHotkey,
getFrontend,
getBackend,
IModel,
Protyle,
openWindow,
IOperation,
Constants,
openMobileFileById,
lockScreen,
ICard,
ICardData
} from "siyuan";
import "@/index.scss";
import {Plugin, Protyle} from 'siyuan';
import {getPreviewHTML, loadIcons, getMenuHTML, generateSiyuanId} from "@/helper";
import {createEditor, openEditorTab} from "@/editorTab";
import HelloExample from "@/hello.svelte";
import SettingExample from "@/setting-example.svelte";
export default class DrawJSPlugin extends Plugin {
onload() {
import { SettingUtils } from "./libs/setting-utils";
import { svelteDialog } from "./libs/dialog";
const STORAGE_NAME = "menu-config";
const TAB_TYPE = "custom_tab";
const DOCK_TYPE = "dock_tab";
export default class PluginSample extends Plugin {
customTab: () => IModel;
private isMobile: boolean;
private blockIconEventBindThis = this.blockIconEvent.bind(this);
private settingUtils: SettingUtils;
async onload() {
this.data[STORAGE_NAME] = { readonlyText: "Readonly" };
console.log("loading plugin-sample", this.i18n);
const frontEnd = getFrontend();
this.isMobile = frontEnd === "mobile" || frontEnd === "browser-mobile";
// 图标的制作参见帮助文档
this.addIcons(`<symbol id="iconFace" viewBox="0 0 32 32">
<path d="M13.667 17.333c0 0.92-0.747 1.667-1.667 1.667s-1.667-0.747-1.667-1.667 0.747-1.667 1.667-1.667 1.667 0.747 1.667 1.667zM20 15.667c-0.92 0-1.667 0.747-1.667 1.667s0.747 1.667 1.667 1.667 1.667-0.747 1.667-1.667-0.747-1.667-1.667-1.667zM29.333 16c0 7.36-5.973 13.333-13.333 13.333s-13.333-5.973-13.333-13.333 5.973-13.333 13.333-13.333 13.333 5.973 13.333 13.333zM14.213 5.493c1.867 3.093 5.253 5.173 9.12 5.173 0.613 0 1.213-0.067 1.787-0.16-1.867-3.093-5.253-5.173-9.12-5.173-0.613 0-1.213 0.067-1.787 0.16zM5.893 12.627c2.28-1.293 4.040-3.4 4.88-5.92-2.28 1.293-4.040 3.4-4.88 5.92zM26.667 16c0-1.040-0.16-2.040-0.44-2.987-0.933 0.2-1.893 0.32-2.893 0.32-4.173 0-7.893-1.92-10.347-4.92-1.4 3.413-4.187 6.093-7.653 7.4 0.013 0.053 0 0.12 0 0.187 0 5.88 4.787 10.667 10.667 10.667s10.667-4.787 10.667-10.667z"></path>
</symbol>
<symbol id="iconSaving" viewBox="0 0 32 32">
<path d="M20 13.333c0-0.733 0.6-1.333 1.333-1.333s1.333 0.6 1.333 1.333c0 0.733-0.6 1.333-1.333 1.333s-1.333-0.6-1.333-1.333zM10.667 12h6.667v-2.667h-6.667v2.667zM29.333 10v9.293l-3.76 1.253-2.24 7.453h-7.333v-2.667h-2.667v2.667h-7.333c0 0-3.333-11.28-3.333-15.333s3.28-7.333 7.333-7.333h6.667c1.213-1.613 3.147-2.667 5.333-2.667 1.107 0 2 0.893 2 2 0 0.28-0.053 0.533-0.16 0.773-0.187 0.453-0.347 0.973-0.427 1.533l3.027 3.027h2.893zM26.667 12.667h-1.333l-4.667-4.667c0-0.867 0.12-1.72 0.347-2.547-1.293 0.333-2.347 1.293-2.787 2.547h-8.227c-2.573 0-4.667 2.093-4.667 4.667 0 2.507 1.627 8.867 2.68 12.667h2.653v-2.667h8v2.667h2.68l2.067-6.867 3.253-1.093v-4.707z"></path>
</symbol>`);
const topBarElement = this.addTopBar({
icon: "iconFace",
title: this.i18n.addTopBarIcon,
position: "right",
callback: () => {
if (this.isMobile) {
this.addMenu();
} else {
let rect = topBarElement.getBoundingClientRect();
// 如果被隐藏,则使用更多按钮
if (rect.width === 0) {
rect = document.querySelector("#barMore").getBoundingClientRect();
}
if (rect.width === 0) {
rect = document.querySelector("#barPlugins").getBoundingClientRect();
}
this.addMenu(rect);
}
loadIcons(this);
//const id = Math.random().toString(36).substring(7);
this.addTab({
'type': "whiteboard",
init() {
createEditor(this);
}
});
const statusIconTemp = document.createElement("template");
statusIconTemp.innerHTML = `<div class="toolbar__item ariaLabel" aria-label="Remove plugin-sample Data">
<svg>
<use xlink:href="#iconTrashcan"></use>
</svg>
</div>`;
statusIconTemp.content.firstElementChild.addEventListener("click", () => {
confirm("⚠️", this.i18n.confirmRemove.replace("${name}", this.name), () => {
this.removeData(STORAGE_NAME).then(() => {
this.data[STORAGE_NAME] = { readonlyText: "Readonly" };
showMessage(`[${this.name}]: ${this.i18n.removedData}`);
});
});
});
this.addStatusBar({
element: statusIconTemp.content.firstElementChild as HTMLElement,
});
this.addCommand({
langKey: "showDialog",
hotkey: "⇧⌘O",
callback: () => {
this.showDialog();
},
fileTreeCallback: (file: any) => {
console.log(file, "fileTreeCallback");
},
editorCallback: (protyle: any) => {
console.log(protyle, "editorCallback");
},
dockCallback: (element: HTMLElement) => {
console.log(element, "dockCallback");
},
});
this.addCommand({
langKey: "getTab",
hotkey: "⇧⌘M",
globalCallback: () => {
console.log(this.getOpenedTab());
},
});
this.addDock({
config: {
position: "LeftBottom",
size: { width: 200, height: 0 },
icon: "iconSaving",
title: "Custom Dock",
hotkey: "⌥⌘W",
},
data: {
text: "This is my custom dock"
},
type: DOCK_TYPE,
resize() {
console.log(DOCK_TYPE + " resize");
},
update() {
console.log(DOCK_TYPE + " update");
},
init: (dock) => {
if (this.isMobile) {
dock.element.innerHTML = `<div class="toolbar toolbar--border toolbar--dark">
<svg class="toolbar__icon"><use xlink:href="#iconEmoji"></use></svg>
<div class="toolbar__text">Custom Dock</div>
</div>
<div class="fn__flex-1 plugin-sample__custom-dock">
${dock.data.text}
</div>
</div>`;
} else {
dock.element.innerHTML = `<div class="fn__flex-1 fn__flex-column">
<div class="block__icons">
<div class="block__logo">
<svg class="block__logoicon"><use xlink:href="#iconEmoji"></use></svg>
Custom Dock
</div>
<span class="fn__flex-1 fn__space"></span>
<span data-type="min" class="block__icon b3-tooltips b3-tooltips__sw" aria-label="Min ${adaptHotkey("W")}"><svg class="block__logoicon"><use xlink:href="#iconMin"></use></svg></span>
</div>
<div class="fn__flex-1 plugin-sample__custom-dock">
${dock.data.text}
</div>
</div>`;
}
},
destroy() {
console.log("destroy dock:", DOCK_TYPE);
}
});
this.settingUtils = new SettingUtils({
plugin: this, name: STORAGE_NAME
});
this.settingUtils.addItem({
key: "Input",
value: "",
type: "textinput",
title: "Readonly text",
description: "Input description",
action: {
// Called when focus is lost and content changes
callback: () => {
// Return data and save it in real time
let value = this.settingUtils.takeAndSave("Input");
console.log(value);
}
}
});
this.settingUtils.addItem({
key: "InputArea",
value: "",
type: "textarea",
title: "Readonly text",
description: "Input description",
// Called when focus is lost and content changes
action: {
callback: () => {
// Read data in real time
let value = this.settingUtils.take("InputArea");
console.log(value);
}
}
});
this.settingUtils.addItem({
key: "Check",
value: true,
type: "checkbox",
title: "Checkbox text",
description: "Check description",
action: {
callback: () => {
// Return data and save it in real time
let value = !this.settingUtils.get("Check");
this.settingUtils.set("Check", value);
console.log(value);
}
}
});
this.settingUtils.addItem({
key: "Select",
value: 1,
type: "select",
title: "Select",
description: "Select description",
options: {
1: "Option 1",
2: "Option 2"
},
action: {
callback: () => {
// Read data in real time
let value = this.settingUtils.take("Select");
console.log(value);
}
}
});
this.settingUtils.addItem({
key: "Slider",
value: 50,
type: "slider",
title: "Slider text",
description: "Slider description",
direction: "column",
slider: {
min: 0,
max: 100,
step: 1,
},
action:{
callback: () => {
// Read data in real time
let value = this.settingUtils.take("Slider");
console.log(value);
}
}
});
this.settingUtils.addItem({
key: "Btn",
value: "",
type: "button",
title: "Button",
description: "Button description",
button: {
label: "Button",
callback: () => {
showMessage("Button clicked");
}
}
});
this.settingUtils.addItem({
key: "Custom Element",
value: "",
type: "custom",
direction: "row",
title: "Custom Element",
description: "Custom Element description",
//Any custom element must offer the following methods
createElement: (currentVal: any) => {
let div = document.createElement('div');
div.style.border = "1px solid var(--b3-theme-primary)";
div.contentEditable = "true";
div.textContent = currentVal;
return div;
},
getEleVal: (ele: HTMLElement) => {
return ele.textContent;
},
setEleVal: (ele: HTMLElement, val: any) => {
ele.textContent = val;
}
});
this.settingUtils.addItem({
key: "Hint",
value: "",
type: "hint",
title: this.i18n.hintTitle,
description: this.i18n.hintDesc,
});
try {
this.settingUtils.load();
} catch (error) {
console.error("Error loading settings storage, probably empty config json:", error);
}
this.protyleSlash = [{
filter: ["insert emoji 😊", "插入表情 😊", "crbqwx"],
html: `<div class="b3-list-item__first"><span class="b3-list-item__text">${this.i18n.insertEmoji}</span><span class="b3-list-item__meta">😊</span></div>`,
id: "insertEmoji",
callback(protyle: Protyle) {
protyle.insert("😊");
id: "insert-drawing",
filter: ["Insert Drawing", "Add drawing", "whiteboard", "freehand", "graphics", "jsdraw"],
html: getMenuHTML("iconDraw", this.i18n.insertDrawing),
callback: (protyle: Protyle) => {
const uid = generateSiyuanId();
protyle.insert(getPreviewHTML(uid), true, false);
openEditorTab(this, uid);
}
}];
this.protyleOptions = {
toolbar: ["block-ref",
"a",
"|",
"text",
"strong",
"em",
"u",
"s",
"mark",
"sup",
"sub",
"clear",
"|",
"code",
"kbd",
"tag",
"inline-math",
"inline-memo",
"|",
{
name: "insert-smail-emoji",
icon: "iconEmoji",
hotkey: "⇧⌘I",
tipPosition: "n",
tip: this.i18n.insertEmoji,
click(protyle: Protyle) {
protyle.insert("😊");
}
}],
};
console.log(this.i18n.helloPlugin);
}
onLayoutReady() {
// this.loadData(STORAGE_NAME);
this.settingUtils.load();
console.log(`frontend: ${getFrontend()}; backend: ${getBackend()}`);
console.log(
"Official settings value calling example:\n" +
this.settingUtils.get("InputArea") + "\n" +
this.settingUtils.get("Slider") + "\n" +
this.settingUtils.get("Select") + "\n"
);
let tabDiv = document.createElement("div");
new HelloExample({
target: tabDiv,
props: {
app: this.app,
}
});
this.customTab = this.addTab({
type: TAB_TYPE,
init() {
this.element.appendChild(tabDiv);
console.log(this.element);
},
beforeDestroy() {
console.log("before destroy tab:", TAB_TYPE);
},
destroy() {
console.log("destroy tab:", TAB_TYPE);
}
});
// This function is automatically called when the layout is loaded.
}
async onunload() {
console.log(this.i18n.byePlugin);
showMessage("Goodbye SiYuan Plugin");
console.log("onunload");
onunload() {
// This function is automatically called when the plugin is disabled.
}
uninstall() {
console.log("uninstall");
// This function is automatically called when the plugin is uninstalled.
}
async updateCards(options: ICardData) {
options.cards.sort((a: ICard, b: ICard) => {
if (a.blockID < b.blockID) {
return -1;
}
if (a.blockID > b.blockID) {
return 1;
}
return 0;
});
return options;
}
/**
* A custom setting pannel provided by svelte
*/
openDIYSetting(): void {
let dialog = new Dialog({
title: "SettingPannel",
content: `<div id="SettingPanel" style="height: 100%;"></div>`,
width: "800px",
destroyCallback: (options) => {
console.log("destroyCallback", options);
//You'd better destroy the component when the dialog is closed
pannel.$destroy();
}
});
let pannel = new SettingExample({
target: dialog.element.querySelector("#SettingPanel"),
});
}
private eventBusPaste(event: any) {
// 如果需异步处理请调用 preventDefault 否则会进行默认处理
event.preventDefault();
// 如果使用了 preventDefault必须调用 resolve否则程序会卡死
event.detail.resolve({
textPlain: event.detail.textPlain.trim(),
});
}
private eventBusLog({ detail }: any) {
console.log(detail);
}
private blockIconEvent({ detail }: any) {
detail.menu.addItem({
iconHTML: "",
label: this.i18n.removeSpace,
click: () => {
const doOperations: IOperation[] = [];
detail.blockElements.forEach((item: HTMLElement) => {
const editElement = item.querySelector('[contenteditable="true"]');
if (editElement) {
editElement.textContent = editElement.textContent.replace(/ /g, "");
doOperations.push({
id: item.dataset.nodeId,
data: item.outerHTML,
action: "update"
});
}
});
detail.protyle.getInstance().transaction(doOperations);
}
});
}
private showDialog() {
// let dialog = new Dialog({
// title: `SiYuan ${Constants.SIYUAN_VERSION}`,
// content: `<div id="helloPanel" class="b3-dialog__content"></div>`,
// width: this.isMobile ? "92vw" : "720px",
// destroyCallback() {
// // hello.$destroy();
// },
// });
// new HelloExample({
// target: dialog.element.querySelector("#helloPanel"),
// props: {
// app: this.app,
// }
// });
svelteDialog({
title: `SiYuan ${Constants.SIYUAN_VERSION}`,
width: this.isMobile ? "92vw" : "720px",
constructor: (container: HTMLElement) => {
return new HelloExample({
target: container,
props: {
app: this.app,
}
});
}
});
}
private addMenu(rect?: DOMRect) {
const menu = new Menu("topBarSample", () => {
console.log(this.i18n.byeMenu);
});
menu.addItem({
icon: "iconInfo",
label: "Dialog(open help first)",
accelerator: this.commands[0].customHotkey,
click: () => {
this.showDialog();
}
});
if (!this.isMobile) {
menu.addItem({
icon: "iconFace",
label: "Open Custom Tab",
click: () => {
const tab = openTab({
app: this.app,
custom: {
icon: "iconFace",
title: "Custom Tab",
data: {
text: "This is my custom tab",
},
id: this.name + TAB_TYPE
},
});
console.log(tab);
}
});
menu.addItem({
icon: "iconImage",
label: "Open Asset Tab(open help first)",
click: () => {
const tab = openTab({
app: this.app,
asset: {
path: "assets/paragraph-20210512165953-ag1nib4.svg"
}
});
console.log(tab);
}
});
menu.addItem({
icon: "iconFile",
label: "Open Doc Tab(open help first)",
click: async () => {
const tab = await openTab({
app: this.app,
doc: {
id: "20200812220555-lj3enxa",
}
});
console.log(tab);
}
});
menu.addItem({
icon: "iconSearch",
label: "Open Search Tab",
click: () => {
const tab = openTab({
app: this.app,
search: {
k: "SiYuan"
}
});
console.log(tab);
}
});
menu.addItem({
icon: "iconRiffCard",
label: "Open Card Tab",
click: () => {
const tab = openTab({
app: this.app,
card: {
type: "all"
}
});
console.log(tab);
}
});
menu.addItem({
icon: "iconLayout",
label: "Open Float Layer(open help first)",
click: () => {
this.addFloatLayer({
ids: ["20210428212840-8rqwn5o", "20201225220955-l154bn4"],
defIds: ["20230415111858-vgohvf3", "20200813131152-0wk5akh"],
x: window.innerWidth - 768 - 120,
y: 32
});
}
});
menu.addItem({
icon: "iconOpenWindow",
label: "Open Doc Window(open help first)",
click: () => {
openWindow({
doc: {id: "20200812220555-lj3enxa"}
});
}
});
} else {
menu.addItem({
icon: "iconFile",
label: "Open Doc(open help first)",
click: () => {
openMobileFileById(this.app, "20200812220555-lj3enxa");
}
});
}
menu.addItem({
icon: "iconLock",
label: "Lockscreen",
click: () => {
lockScreen(this.app);
}
});
menu.addItem({
icon: "iconScrollHoriz",
label: "Event Bus",
type: "submenu",
submenu: [{
icon: "iconSelect",
label: "On ws-main",
click: () => {
this.eventBus.on("ws-main", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off ws-main",
click: () => {
this.eventBus.off("ws-main", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On click-blockicon",
click: () => {
this.eventBus.on("click-blockicon", this.blockIconEventBindThis);
}
}, {
icon: "iconClose",
label: "Off click-blockicon",
click: () => {
this.eventBus.off("click-blockicon", this.blockIconEventBindThis);
}
}, {
icon: "iconSelect",
label: "On click-pdf",
click: () => {
this.eventBus.on("click-pdf", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off click-pdf",
click: () => {
this.eventBus.off("click-pdf", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On click-editorcontent",
click: () => {
this.eventBus.on("click-editorcontent", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off click-editorcontent",
click: () => {
this.eventBus.off("click-editorcontent", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On click-editortitleicon",
click: () => {
this.eventBus.on("click-editortitleicon", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off click-editortitleicon",
click: () => {
this.eventBus.off("click-editortitleicon", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On click-flashcard-action",
click: () => {
this.eventBus.on("click-flashcard-action", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off click-flashcard-action",
click: () => {
this.eventBus.off("click-flashcard-action", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-noneditableblock",
click: () => {
this.eventBus.on("open-noneditableblock", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-noneditableblock",
click: () => {
this.eventBus.off("open-noneditableblock", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On loaded-protyle-static",
click: () => {
this.eventBus.on("loaded-protyle-static", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off loaded-protyle-static",
click: () => {
this.eventBus.off("loaded-protyle-static", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On loaded-protyle-dynamic",
click: () => {
this.eventBus.on("loaded-protyle-dynamic", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off loaded-protyle-dynamic",
click: () => {
this.eventBus.off("loaded-protyle-dynamic", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On switch-protyle",
click: () => {
this.eventBus.on("switch-protyle", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off switch-protyle",
click: () => {
this.eventBus.off("switch-protyle", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On destroy-protyle",
click: () => {
this.eventBus.on("destroy-protyle", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off destroy-protyle",
click: () => {
this.eventBus.off("destroy-protyle", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-doctree",
click: () => {
this.eventBus.on("open-menu-doctree", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-doctree",
click: () => {
this.eventBus.off("open-menu-doctree", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-blockref",
click: () => {
this.eventBus.on("open-menu-blockref", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-blockref",
click: () => {
this.eventBus.off("open-menu-blockref", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-fileannotationref",
click: () => {
this.eventBus.on("open-menu-fileannotationref", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-fileannotationref",
click: () => {
this.eventBus.off("open-menu-fileannotationref", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-tag",
click: () => {
this.eventBus.on("open-menu-tag", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-tag",
click: () => {
this.eventBus.off("open-menu-tag", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-link",
click: () => {
this.eventBus.on("open-menu-link", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-link",
click: () => {
this.eventBus.off("open-menu-link", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-image",
click: () => {
this.eventBus.on("open-menu-image", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-image",
click: () => {
this.eventBus.off("open-menu-image", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-av",
click: () => {
this.eventBus.on("open-menu-av", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-av",
click: () => {
this.eventBus.off("open-menu-av", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-content",
click: () => {
this.eventBus.on("open-menu-content", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-content",
click: () => {
this.eventBus.off("open-menu-content", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-breadcrumbmore",
click: () => {
this.eventBus.on("open-menu-breadcrumbmore", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-breadcrumbmore",
click: () => {
this.eventBus.off("open-menu-breadcrumbmore", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-menu-inbox",
click: () => {
this.eventBus.on("open-menu-inbox", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-menu-inbox",
click: () => {
this.eventBus.off("open-menu-inbox", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On input-search",
click: () => {
this.eventBus.on("input-search", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off input-search",
click: () => {
this.eventBus.off("input-search", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On paste",
click: () => {
this.eventBus.on("paste", this.eventBusPaste);
}
}, {
icon: "iconClose",
label: "Off paste",
click: () => {
this.eventBus.off("paste", this.eventBusPaste);
}
}, {
icon: "iconSelect",
label: "On open-siyuan-url-plugin",
click: () => {
this.eventBus.on("open-siyuan-url-plugin", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-siyuan-url-plugin",
click: () => {
this.eventBus.off("open-siyuan-url-plugin", this.eventBusLog);
}
}, {
icon: "iconSelect",
label: "On open-siyuan-url-block",
click: () => {
this.eventBus.on("open-siyuan-url-block", this.eventBusLog);
}
}, {
icon: "iconClose",
label: "Off open-siyuan-url-block",
click: () => {
this.eventBus.off("open-siyuan-url-block", this.eventBusLog);
}
}]
});
menu.addSeparator();
menu.addItem({
icon: "iconSettings",
label: "Official Setting Dialog",
click: () => {
this.openSetting();
}
});
menu.addItem({
icon: "iconSettings",
label: "A custom setting dialog (by svelte)",
click: () => {
this.openDIYSetting();
}
});
menu.addItem({
icon: "iconSparkles",
label: this.data[STORAGE_NAME].readonlyText || "Readonly",
type: "readonly",
});
if (this.isMobile) {
menu.fullscreen();
} else {
menu.open({
x: rect.right,
y: rect.bottom,
isLeft: true,
});
}
}
}
}

View file

@ -1,139 +0,0 @@
<script lang="ts">
import { showMessage } from "siyuan";
import SettingPanel from "./libs/components/setting-panel.svelte";
let groups: string[] = ["🌈 Group 1", "✨ Group 2"];
let focusGroup = groups[0];
const group1Items: ISettingItem[] = [
{
type: 'checkbox',
title: 'checkbox',
description: 'checkbox',
key: 'a',
value: true
},
{
type: 'textinput',
title: 'text',
description: 'This is a text',
key: 'b',
value: 'This is a text',
placeholder: 'placeholder'
},
{
type: 'textarea',
title: 'textarea',
description: 'This is a textarea',
key: 'b2',
value: 'This is a textarea',
placeholder: 'placeholder',
direction: 'row'
},
{
type: 'select',
title: 'select',
description: 'select',
key: 'c',
value: 'x',
options: {
x: 'x',
y: 'y',
z: 'z'
}
}
];
const group2Items: ISettingItem[] = [
{
type: 'button',
title: 'button',
description: 'This is a button',
key: 'e',
value: 'Click Button',
button: {
label: 'Click Me',
callback: () => {
showMessage('Hello, world!');
}
}
},
{
type: 'slider',
title: 'slider',
description: 'slider',
key: 'd',
value: 50,
slider: {
min: 0,
max: 100,
step: 1
}
}
];
/********** Events **********/
interface ChangeEvent {
group: string;
key: string;
value: any;
}
const onChanged = ({ detail }: CustomEvent<ChangeEvent>) => {
if (detail.group === groups[0]) {
// setting.set(detail.key, detail.value);
//Please add your code here
//Udpate the plugins setting data, don't forget to call plugin.save() for data persistence
}
};
</script>
<div class="fn__flex-1 fn__flex config__panel">
<ul class="b3-tab-bar b3-list b3-list--background">
{#each groups as group}
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<li
data-name="editor"
class:b3-list-item--focus={group === focusGroup}
class="b3-list-item"
on:click={() => {
focusGroup = group;
}}
on:keydown={() => {}}
>
<span class="b3-list-item__text">{group}</span>
</li>
{/each}
</ul>
<div class="config__tab-wrap">
<SettingPanel
group={groups[0]}
settingItems={group1Items}
display={focusGroup === groups[0]}
on:changed={onChanged}
on:click={({ detail }) => { console.debug("Click:", detail.key); }}
>
<div class="fn__flex b3-label">
💡 This is our default settings.
</div>
</SettingPanel>
<SettingPanel
group={groups[1]}
settingItems={group2Items}
display={focusGroup === groups[1]}
on:changed={onChanged}
on:click={({ detail }) => { console.debug("Click:", detail.key); }}
>
</SettingPanel>
</div>
</div>
<style lang="scss">
.config__panel {
height: 100%;
}
.config__panel > ul > li {
padding-left: 1rem;
}
</style>