Nothing here yet! Click me to open the editor.
" +async function getFile(path) { + + const response = await fetch('/api/file/getFile', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({path: path}) + }); + + if (!response.ok) { + console.log('Failed to fetch HTML content'); + return null; + } + + const blob = await response.blob(); + const resTxt = await blob.text(); + + // if we got a 404 api response, we will return null + try { + const res = JSON.parse(resTxt); + if(res.code === 404) { + return null; + } + }catch {} + + return resTxt; + +} + +async function getSVG(fileID) { + + const resp = await getFile("/data/assets/" + fileID + '.svg'); + if(resp == null) { + return FALLBACK; + } + return resp; + +} + +function getEditLink(fileID) { + const data = encodeURIComponent( + JSON.stringify({ + id: fileID + }) + ) + return `siyuan://plugins/siyuan-jsdraw-pluginwhiteboard/?icon=iconDraw&title=Drawing&data=${data}`; +} \ No newline at end of file diff --git a/public/webapp/index.css b/public/webapp/index.css new file mode 100644 index 0000000..e1dc21d --- /dev/null +++ b/public/webapp/index.css @@ -0,0 +1,91 @@ +a > div > p { + color: var(--text, black); +} + +html, body { + height: 100%; + width: 100%; + margin: 0; + padding: 0; + overflow: hidden; /* Prevent scrollbars */ +} + +body { + display: flex; + justify-content: center; + align-items: center; + background: transparent; +} + +div { + max-width: min(100vw, 100vh * var(--svg-aspect-ratio)); + max-height: min(100vh, 100vw / var(--svg-aspect-ratio)); + display: flex; + justify-content: center; + align-items: center; +} + +svg { + width: 100%; + height: 100%; + object-fit: contain; + overflow: hidden; +} + +/* Floating button styles */ +#floatingButton { + position: fixed; + bottom: 20px; + right: 20px; + background-color: #007bff; + color: white; + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + font-size: 20px; + cursor: pointer; + display: none; /* Initially hidden */ +} + +/* Popup menu styles */ +#popupMenu { + position: fixed; + bottom: 70px; + right: 20px; + background-color: var(--popup-bg, white); + border: 1px solid var(--popup-border, #ccc); + border-radius: 5px; + padding: 10px; + display: none; /* Initially hidden */ + max-height: calc(100vh - 90px); /* Adjust based on window height */ + overflow-y: auto; /* Add scroll if content overflows */ +} + +#popupMenu button { + display: block; + margin: 5px 0; + padding: 5px 10px; + background-color: var(--button-bg, #f8f9fa); + border: 1px solid var(--button-border, #ccc); + border-radius: 3px; + cursor: pointer; + color: var(--button-text, black); +} + +#popupMenu button:hover { + background-color: var(--button-hover-bg, #e2e6ea); +} + +/* Dark theme styles */ +@media (prefers-color-scheme: dark) { + :root { + --text: white; + --popup-bg: #333; + --popup-border: #555; + --button-bg: #444; + --button-border: #666; + --button-text: #fff; + --button-hover-bg: #555; + } +} \ No newline at end of file diff --git a/public/webapp/index.html b/public/webapp/index.html new file mode 100644 index 0000000..a1fd2f6 --- /dev/null +++ b/public/webapp/index.html @@ -0,0 +1,26 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/const.ts b/src/const.ts new file mode 100644 index 0000000..1acaa80 --- /dev/null +++ b/src/const.ts @@ -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="; \ No newline at end of file diff --git a/src/editorTab.ts b/src/editorTab.ts new file mode 100644 index 0000000..bef75c7 --- /dev/null +++ b/src/editorTab.ts @@ -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%'; + +} \ No newline at end of file diff --git a/src/file.ts b/src/file.ts new file mode 100644 index 0000000..5ce0d20 --- /dev/null +++ b/src/file.ts @@ -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; + +} diff --git a/src/hello.svelte b/src/hello.svelte deleted file mode 100644 index 967c7f6..0000000 --- a/src/hello.svelte +++ /dev/null @@ -1,63 +0,0 @@ - - - -