mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
14 Commits
2.4.0-rc-1
...
window-onb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23d1ad0da6 | ||
|
|
49173dc766 | ||
|
|
03a563856d | ||
|
|
c3809c409d | ||
|
|
dfdca90ca5 | ||
|
|
6a8e1735db | ||
|
|
c0e9a0553e | ||
|
|
e1501165d9 | ||
|
|
3b0f706059 | ||
|
|
7d19662f68 | ||
|
|
5c949dc71c | ||
|
|
0439d67a0c | ||
|
|
d3446a20b1 | ||
|
|
5b37dc2e38 |
@@ -2,31 +2,3 @@ The project runs with `node 18`.
|
||||
|
||||
After running `npm -i` you'll need to make two manual changes:
|
||||
|
||||
## postprocess
|
||||
postprocess is used in rollup.config.js.
|
||||
However, the version available on npmjs does not work, after installing packages you need this update:
|
||||
`npm install brettz9/rollup-plugin-postprocess#update --save-dev``
|
||||
|
||||
More info here: https://github.com/developit/rollup-plugin-postprocess/issues/10
|
||||
|
||||
## colormaster
|
||||
1.2.1 misses 3 plugin references after installing the package you need to update
|
||||
`node_modules/colormaster/package.json` adding the following to the `exports:` section:
|
||||
```typescript
|
||||
,
|
||||
"./plugins/luv": {
|
||||
"import": "./plugins/luv.mjs",
|
||||
"require": "./plugins/luv.js",
|
||||
"default": "./plugins/luv.mjs"
|
||||
},
|
||||
"./plugins/uvw": {
|
||||
"import": "./plugins/uvw.mjs",
|
||||
"require": "./plugins/uvw.js",
|
||||
"default": "./plugins/uvw.mjs"
|
||||
},
|
||||
"./plugins/ryb": {
|
||||
"import": "./plugins/ryb.mjs",
|
||||
"require": "./plugins/ryb.js",
|
||||
"default": "./plugins/ryb.mjs"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.4.0-rc-1",
|
||||
"version": "2.4.0-rc-2",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
28
package.json
28
package.json
@@ -19,28 +19,32 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/excalidraw": "0.17.1-obsidian-43",
|
||||
"@zsviczian/excalidraw": "0.17.1-obsidian-45",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"colormaster": "^1.2.1",
|
||||
"@zsviczian/colormaster": "^1.2.2",
|
||||
"gl-matrix": "^3.4.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lucide-react": "^0.263.1",
|
||||
"mathjax-full": "^3.2.2",
|
||||
"monkey-around": "^2.3.0",
|
||||
"nanoid": "^4.0.2",
|
||||
"opentype.js": "^1.3.4",
|
||||
"polybooljs": "^1.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"roughjs": "^4.5.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"opentype.js": "^1.3.4",
|
||||
"woff2sfnt-sfnt2woff": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "^16.4.5",
|
||||
"@babel/core": "^7.22.9",
|
||||
"@babel/preset-env": "^7.22.10",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@codemirror/commands": "^6.3.3",
|
||||
"@codemirror/language": "^6.10.0",
|
||||
"@codemirror/search": "^6.5.5",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
"@excalidraw/eslint-config": "^1.0.3",
|
||||
"@excalidraw/prettier-config": "^1.0.2",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
@@ -50,14 +54,15 @@
|
||||
"@rollup/plugin-typescript": "^11.1.6",
|
||||
"@types/chroma-js": "^2.4.0",
|
||||
"@types/js-beautify": "^1.14.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.10.5",
|
||||
"@types/opentype.js": "^1.3.8",
|
||||
"@types/react": "^18.2.45",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/opentype.js": "^1.3.8",
|
||||
"@zerollup/ts-transform-paths": "^1.7.18",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^6.0.2",
|
||||
"dotenv": "^16.4.5",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"lz-string": "^1.5.0",
|
||||
@@ -65,17 +70,12 @@
|
||||
"prettier": "^3.0.1",
|
||||
"rollup": "^2.70.1",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"rollup-plugin-postprocess": "github:brettz9/rollup-plugin-postprocess#update",
|
||||
"@zsviczian/rollup-plugin-postprocess": "^1.0.3",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.34.1",
|
||||
"tslib": "^2.6.1",
|
||||
"ttypescript": "^1.5.15",
|
||||
"typescript": "^5.2.2",
|
||||
"@codemirror/commands": "^6.3.3",
|
||||
"@codemirror/language": "^6.10.0",
|
||||
"@codemirror/search": "^6.5.5",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.23.0"
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"@typescript-eslint/typescript-estree": "5.3.0"
|
||||
|
||||
@@ -6,7 +6,7 @@ import copy from "rollup-plugin-copy";
|
||||
import typescript2 from "rollup-plugin-typescript2";
|
||||
import fs from 'fs';
|
||||
import LZString from 'lz-string';
|
||||
import postprocess from 'rollup-plugin-postprocess';
|
||||
import postprocess from '@zsviczian/rollup-plugin-postprocess';
|
||||
import cssnano from 'cssnano';
|
||||
|
||||
// Load environment variables
|
||||
@@ -100,10 +100,13 @@ const BUILD_CONFIG = {
|
||||
plugins: getRollupPlugins(
|
||||
{tsconfig: isProd ? "tsconfig.json" : "tsconfig.dev.json"},
|
||||
...(isProd ? [
|
||||
terser({ toplevel: false, compress: { passes: 2 } }),
|
||||
//!postprocess - the version available on npmjs does not work, need this update:
|
||||
// npm install brettz9/rollup-plugin-postprocess#update --save-dev
|
||||
// https://github.com/developit/rollup-plugin-postprocess/issues/10
|
||||
terser({
|
||||
toplevel: false,
|
||||
compress: { passes: 2 },
|
||||
format: {
|
||||
comments: false, // Remove all comments
|
||||
},
|
||||
}),
|
||||
postprocess([
|
||||
[/React=require\("react"\),state=require\("@codemirror\/state"\),view=require\("@codemirror\/view"\)/,
|
||||
`state=require("@codemirror/state"),view=require("@codemirror/view")` + packageString],
|
||||
|
||||
@@ -473,6 +473,8 @@ export class EmbeddedFilesLoader {
|
||||
return null;
|
||||
}
|
||||
|
||||
const app = this.plugin.app;
|
||||
|
||||
const isHyperLink = inFile instanceof EmbeddedFile ? inFile.isHyperLink : false;
|
||||
const isLocalLink = inFile instanceof EmbeddedFile ? inFile.isLocalLink : false;
|
||||
const hyperlink = inFile instanceof EmbeddedFile ? inFile.hyperlink : "";
|
||||
|
||||
@@ -63,21 +63,21 @@ import { GenericInputPrompt, NewFileActions } from "src/dialogs/Prompt";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { ScriptEngine } from "src/Scripts";
|
||||
import { ConnectionPoint, DeviceType } from "src/types/types";
|
||||
import CM, { ColorMaster, extendPlugins } from "colormaster";
|
||||
import HarmonyPlugin from "colormaster/plugins/harmony";
|
||||
import MixPlugin from "colormaster/plugins/mix"
|
||||
import A11yPlugin from "colormaster/plugins/accessibility"
|
||||
import NamePlugin from "colormaster/plugins/name"
|
||||
import LCHPlugin from "colormaster/plugins/lch";
|
||||
import LUVPlugin from "colormaster/plugins/luv";
|
||||
import LABPlugin from "colormaster/plugins/lab";
|
||||
import UVWPlugin from "colormaster/plugins/uvw";
|
||||
import XYZPlugin from "colormaster/plugins/xyz";
|
||||
import HWBPlugin from "colormaster/plugins/hwb";
|
||||
import HSVPlugin from "colormaster/plugins/hsv";
|
||||
import RYBPlugin from "colormaster/plugins/ryb";
|
||||
import CMYKPlugin from "colormaster/plugins/cmyk";
|
||||
import { TInput } from "colormaster/types";
|
||||
import CM, { ColorMaster, extendPlugins } from "@zsviczian/colormaster";
|
||||
import HarmonyPlugin from "@zsviczian/colormaster/plugins/harmony";
|
||||
import MixPlugin from "@zsviczian/colormaster/plugins/mix"
|
||||
import A11yPlugin from "@zsviczian/colormaster/plugins/accessibility"
|
||||
import NamePlugin from "@zsviczian/colormaster/plugins/name"
|
||||
import LCHPlugin from "@zsviczian/colormaster/plugins/lch";
|
||||
import LUVPlugin from "@zsviczian/colormaster/plugins/luv";
|
||||
import LABPlugin from "@zsviczian/colormaster/plugins/lab";
|
||||
import UVWPlugin from "@zsviczian/colormaster/plugins/uvw";
|
||||
import XYZPlugin from "@zsviczian/colormaster/plugins/xyz";
|
||||
import HWBPlugin from "@zsviczian/colormaster/plugins/hwb";
|
||||
import HSVPlugin from "@zsviczian/colormaster/plugins/hsv";
|
||||
import RYBPlugin from "@zsviczian/colormaster/plugins/ryb";
|
||||
import CMYKPlugin from "@zsviczian/colormaster/plugins/cmyk";
|
||||
import { TInput } from "@zsviczian/colormaster/types";
|
||||
import {ConversionResult, svgToExcalidraw} from "src/svgToExcalidraw/parser"
|
||||
import { ROUNDNESS } from "src/constants/constants";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/excalidraw/clipboard";
|
||||
|
||||
@@ -838,6 +838,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(triggerReload) {
|
||||
this.reload(true, this.file);
|
||||
}
|
||||
this.resetAutosaveTimer(); //next autosave period starts after save
|
||||
}
|
||||
|
||||
// get the new file content
|
||||
@@ -1534,8 +1535,17 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
};
|
||||
|
||||
const onBlurOrLeave = () => {
|
||||
if(!this.excalidrawAPI || !this.excalidrawData.loaded || !this.isDirty()) {
|
||||
return;
|
||||
}
|
||||
this.save();
|
||||
};
|
||||
|
||||
this.registerDomEvent(this.ownerWindow, "keydown", onKeyDown, false);
|
||||
this.registerDomEvent(this.ownerWindow, "keyup", onKeyUp, false);
|
||||
this.registerDomEvent(this.contentEl, "mouseleave", onBlurOrLeave, false);
|
||||
this.registerDomEvent(this.ownerWindow, "blur", onBlurOrLeave, false);
|
||||
});
|
||||
|
||||
this.setupAutosaveTimer();
|
||||
@@ -1728,7 +1738,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.semaphores.autosaving = true;
|
||||
//changed from await to then to avoid lag during saving of large file
|
||||
this.save().then(()=>this.semaphores.autosaving = false);
|
||||
}
|
||||
}
|
||||
this.autosaveTimer = window.setTimeout(
|
||||
timer,
|
||||
this.autosaveInterval,
|
||||
@@ -1762,7 +1772,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.autosaveFunction,
|
||||
this.autosaveInterval,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
unload(): void {
|
||||
@@ -2611,6 +2620,10 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
public setDirty(location?:number) {
|
||||
if(this.semaphores.saving) return; //do not set dirty if saving
|
||||
if(!this.isDirty()) {
|
||||
//the autosave timer should start when the first stroke was made... thus avoiding an immediate impact by saving right then
|
||||
this.resetAutosaveTimer();
|
||||
}
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.setDirty,`ExcalidrawView.setDirty, location:${location}`);
|
||||
this.semaphores.dirty = this.file?.path;
|
||||
this.actionButtons['save'].querySelector("svg").addClass("excalidraw-dirty");
|
||||
@@ -3342,6 +3355,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
currentStrokeOptions: st.currentStrokeOptions,
|
||||
frameRendering: st.frameRendering,
|
||||
objectsSnapModeEnabled: st.objectsSnapModeEnabled,
|
||||
activeTool: st.activeTool,
|
||||
},
|
||||
prevTextMode: this.prevTextMode,
|
||||
files,
|
||||
@@ -5246,6 +5260,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
private renderWelcomeScreen () {
|
||||
if(!this.plugin.settings.showSplashscreen) return null;
|
||||
const React = this.packages.react;
|
||||
const {WelcomeScreen} = this.packages.excalidrawLib;
|
||||
const filecount = this.app.vault.getFiles().filter(f=>this.plugin.isExcalidrawFile(f)).length;
|
||||
|
||||
162
src/dialogs/HotkeyEditor.ts
Normal file
162
src/dialogs/HotkeyEditor.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { BaseComponent, Setting, Modifier } from 'obsidian';
|
||||
import { DEVICE } from 'src/constants/constants';
|
||||
import { t } from 'src/lang/helpers';
|
||||
import { ExcalidrawSettings } from 'src/settings';
|
||||
import { modifierLabel } from 'src/utils/ModifierkeyHelper';
|
||||
import { fragWithHTML } from 'src/utils/Utils';
|
||||
|
||||
export class HotkeyEditor extends BaseComponent {
|
||||
private settings: ExcalidrawSettings;
|
||||
private containerEl: HTMLElement;
|
||||
private capturing: boolean = false;
|
||||
private activeModifiers: Modifier[] = [];
|
||||
public isDirty: boolean = false;
|
||||
private applySettingsUpdate: Function;
|
||||
|
||||
// Store bound event handlers
|
||||
private boundKeydownHandler: (event: KeyboardEvent) => void;
|
||||
private boundKeyupHandler: (event: KeyboardEvent) => void;
|
||||
|
||||
constructor(containerEl: HTMLElement, settings: ExcalidrawSettings, applySettingsUpdate: Function) {
|
||||
super();
|
||||
this.containerEl = containerEl.createDiv();
|
||||
this.settings = settings;
|
||||
this.applySettingsUpdate = applySettingsUpdate;
|
||||
|
||||
// Bind the event handlers once in the constructor
|
||||
this.boundKeydownHandler = this.onKeydown.bind(this);
|
||||
this.boundKeyupHandler = this.onKeyup.bind(this);
|
||||
}
|
||||
|
||||
onload(): void {
|
||||
this.render();
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
// Clear previous content
|
||||
this.containerEl.empty();
|
||||
|
||||
// Render current overrides
|
||||
this.settings.modifierKeyOverrides.forEach((override, index) => {
|
||||
const key = override.key.toUpperCase();
|
||||
new Setting(this.containerEl)
|
||||
.setDesc(fragWithHTML(`<b>Code:</b> <kbd>${override.modifiers.join("+")} + ${key}</kbd> | ` +
|
||||
`<b>Apple:</b> <kbd>${modifierLabel(override.modifiers, "Mac")} + ${key}</kbd> | ` +
|
||||
`<b>Windows:</b> <kbd>${modifierLabel(override.modifiers, "Other")} + ${key}</kbd>`))
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText(t("HOTKEY_BUTTON_REMOVE"))
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
this.settings.modifierKeyOverrides.splice(index, 1);
|
||||
this.isDirty = true;
|
||||
this.applySettingsUpdate();
|
||||
this.render();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Render Add New Override or Capture Instruction
|
||||
if (this.capturing) {
|
||||
new Setting(this.containerEl)
|
||||
.setName(t("HOTKEY_PRESS_COMBO_NANE"))
|
||||
.setDesc(t("HOTKEY_PRESS_COMBO_DESC"))
|
||||
.controlEl.style.cursor = 'pointer';
|
||||
} else {
|
||||
new Setting(this.containerEl)
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText(t("HOTKEY_BUTTON_ADD_OVERRIDE"))
|
||||
.setCta()
|
||||
.onClick(() => this.startCapture())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private startCapture(): void {
|
||||
this.capturing = true;
|
||||
this.activeModifiers = [];
|
||||
this.render();
|
||||
// Use the pre-bound handlers
|
||||
window.addEventListener('keydown', this.boundKeydownHandler);
|
||||
window.addEventListener('keyup', this.boundKeyupHandler);
|
||||
}
|
||||
|
||||
private onKeydown(event: KeyboardEvent): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const modifiers = this.getModifiersFromEvent(event);
|
||||
|
||||
// If only modifiers are pressed, update activeModifiers and continue listening
|
||||
if (['Control', 'Shift', 'Alt', 'Meta'].includes(event.key)) {
|
||||
this.activeModifiers = modifiers;
|
||||
return;
|
||||
}
|
||||
|
||||
const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;
|
||||
|
||||
// Check for duplicate overrides
|
||||
const exists = this.settings.modifierKeyOverrides.some(
|
||||
(override) =>
|
||||
override.key === key &&
|
||||
override.modifiers.length === modifiers.length &&
|
||||
override.modifiers.every((mod) => modifiers.includes(mod))
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
this.settings.modifierKeyOverrides.push({ modifiers, key });
|
||||
this.isDirty = true;
|
||||
this.applySettingsUpdate();
|
||||
}
|
||||
|
||||
this.stopCapture();
|
||||
}
|
||||
|
||||
private onKeyup(event: KeyboardEvent): void {
|
||||
// If all modifier keys are released, stop capturing
|
||||
if (!event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
|
||||
this.stopCapture();
|
||||
}
|
||||
}
|
||||
|
||||
private stopCapture(): void {
|
||||
this.capturing = false;
|
||||
// Use the pre-bound handlers for removal
|
||||
window.removeEventListener('keydown', this.boundKeydownHandler);
|
||||
window.removeEventListener('keyup', this.boundKeyupHandler);
|
||||
this.render();
|
||||
}
|
||||
|
||||
public unload(): void {
|
||||
// Ensure listeners are removed when the component is unloaded
|
||||
this.stopCapture();
|
||||
}
|
||||
|
||||
private getModifiersFromEvent(event: KeyboardEvent): Modifier[] {
|
||||
const modifiers: Modifier[] = [];
|
||||
|
||||
if (DEVICE.isMacOS && event.metaKey) {
|
||||
modifiers.push('Mod');
|
||||
} else if (!DEVICE.isMacOS && event.ctrlKey) {
|
||||
modifiers.push('Mod');
|
||||
}
|
||||
|
||||
if (DEVICE.isMacOS && event.ctrlKey) {
|
||||
modifiers.push('Ctrl');
|
||||
}
|
||||
|
||||
if (!DEVICE.isMacOS && event.metaKey) {
|
||||
modifiers.push('Meta');
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
modifiers.push('Shift');
|
||||
}
|
||||
if (event.altKey) {
|
||||
modifiers.push('Alt');
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ export default {
|
||||
TRANSCLUDE: "Embed a drawing",
|
||||
TRANSCLUDE_MOST_RECENT: "Embed the most recently edited drawing",
|
||||
TOGGLE_LEFTHANDED_MODE: "Toggle left-handed mode",
|
||||
TOGGLE_SPLASHSCREEN: "Show splash screen in new drawings",
|
||||
FLIP_IMAGE: "Open the back-of-the-note of the selected excalidraw image",
|
||||
NEW_IN_NEW_PANE: "Create new drawing - IN AN ADJACENT WINDOW",
|
||||
NEW_IN_NEW_TAB: "Create new drawing - IN A NEW TAB",
|
||||
@@ -352,6 +353,10 @@ FILENAME_HEAD: "Filename",
|
||||
"<li>When <b>disabled</b> the PDF will show the markdown side of the document.</li></ul>" +
|
||||
"See the other related setting for <a href='#"+TAG_MDREADINGMODE+"'>Markdown Reading Mode</a> under 'Appearnace and Behavior' further above.<br>" +
|
||||
"⚠️ Note, you must close the active excalidraw/markdown file and reopen for this change to take effect. ⚠️",
|
||||
HOTKEY_OVERRIDE_HEAD: "Hotkey overrides",
|
||||
HOTKEY_OVERRIDE_DESC: `Some of the Excalidraw hotkeys such as <code>${labelCTRL()}+Enter</code> to edit text or <code>${labelCTRL()}+K</code> to create an element link ` +
|
||||
"conflict with Obsidian hotkey settings. The hotkey combinations you add below will override Obsidian's hotkey settings while useing Excalidraw, thus " +
|
||||
`you can add <code>${labelCTRL()}+G</code> if you want to default to Group Object in Excalidraw instead of opening Graph View.`,
|
||||
THEME_HEAD: "Theme and styling",
|
||||
ZOOM_HEAD: "Zoom",
|
||||
DEFAULT_PINCHZOOM_NAME: "Allow pinch zoom in pen mode",
|
||||
@@ -739,6 +744,12 @@ FILENAME_HEAD: "Filename",
|
||||
"the developer of Taskbone (as you can imagine, there is no such thing as 'free', providing this awesome OCR service costs some money to the developer of Taskbone), you can " +
|
||||
"purchase a paid API key from <a href='https://www.taskbone.com/' target='_blank'>taskbone.com</a>. In case you have purchased a key, simply overwrite this auto generated free-tier API-key with your paid key.",
|
||||
|
||||
//HotkeyEditor
|
||||
HOTKEY_PRESS_COMBO_NANE: "Press your hotkey combination",
|
||||
HOTKEY_PRESS_COMBO_DESC: "Please press the desired key combination",
|
||||
HOTKEY_BUTTON_ADD_OVERRIDE: "Add New Override",
|
||||
HOTKEY_BUTTON_REMOVE: "Remove",
|
||||
|
||||
//openDrawings.ts
|
||||
SELECT_FILE: "Select a file then press enter.",
|
||||
SELECT_COMMAND: "Select a command then press enter.",
|
||||
|
||||
@@ -37,6 +37,7 @@ export default {
|
||||
TRANSCLUDE: "嵌入绘图(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
TRANSCLUDE_MOST_RECENT: "嵌入最近编辑过的绘图(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
TOGGLE_LEFTHANDED_MODE: "切换为左手模式",
|
||||
TOGGLE_SPLASHSCREEN: "在新绘图中显示启动画面",
|
||||
FLIP_IMAGE: "打开当前所选 excalidraw 图像的“背景笔记”",
|
||||
NEW_IN_NEW_PANE: "新建绘图 - 于新面板",
|
||||
NEW_IN_NEW_TAB: "新建绘图 - 于新页签",
|
||||
@@ -352,6 +353,10 @@ FILENAME_HEAD: "文件名",
|
||||
"<li>当 <b>禁用</b> 时,PDF 将显示文档的 Markdown 部分(背景笔记)。</li></ul>" +
|
||||
"请参阅上面‘外观和行为’部分的 <<a href='#"+TAG_MDREADINGMODE+"'>>Markdown 阅读模式</a> 相关设置。" +
|
||||
"⚠️ 注意,您必须关闭当前的 Excalidraw/Markdown 文件并重新打开,以使此更改生效。⚠️",
|
||||
HOTKEY_OVERRIDE_HEAD: "热键覆盖",
|
||||
HOTKEY_OVERRIDE_DESC: `一些 Excalidraw 的热键,例如 ${labelCTRL()}+Enter 用于编辑文本,或 ${labelCTRL()}+K 用于创建元素链接。` +
|
||||
"与 Obsidian 的热键设置发生冲突。您在下面添加的热键组合将在使用 Excalidraw 时覆盖 Obsidian 的热键设置," +
|
||||
`因此如果您希望在 Excalidraw 中默认选择“组合对象”,而不是打开“图形视图”,您可以添加 ${labelCTRL()}+G。`,
|
||||
THEME_HEAD: "主题和样式",
|
||||
ZOOM_HEAD: "缩放",
|
||||
DEFAULT_PINCHZOOM_NAME: "允许在触控笔模式下进行双指缩放",
|
||||
@@ -477,7 +482,11 @@ FILENAME_HEAD: "文件名",
|
||||
EMBED_TOEXCALIDRAW_DESC: "包括:以图像形式嵌入到绘图中的 PDF 文档、以交互形式嵌入到绘图中的 Markdown 文档(MD-Embeddable)、以图像形式嵌入的 Markdown 文档(MD-Embed)等。",
|
||||
MD_HEAD: "以图像形式嵌入到绘图中的 Markdown 文档(MD-Embed)",
|
||||
MD_EMBED_CUSTOMDATA_HEAD_NAME: "以交互形式嵌入到绘图中的 Markdown 文档(MD-Embeddable)",
|
||||
MD_EMBED_CUSTOMDATA_HEAD_DESC: `这些选项不会影响到已存在的 MD-Embeddable。MD-Embeddable 的主题风格在“显示 & 行为”小节设置。`,
|
||||
MD_EMBED_CUSTOMDATA_HEAD_DESC: `以下设置只会影响以后的嵌入。已存在的嵌入保持不变。嵌入框的主题设置位于 “Excalidraw 外观和行为” 部分。`,
|
||||
MD_EMBED_SINGLECLICK_EDIT_NAME: "单击以编辑嵌入的 markdown。",
|
||||
MD_EMBED_SINGLECLICK_EDIT_DESC:
|
||||
"单击嵌入的 markdown 文件以进行编辑。 " +
|
||||
"当此功能关闭时,markdown 文件将首先以预览模式打开,然后在您再次单击时切换到编辑模式。",
|
||||
MD_TRANSCLUDE_WIDTH_NAME: "MD-Embed 的默认宽度",
|
||||
MD_TRANSCLUDE_WIDTH_DESC:
|
||||
"MD-Embed 的宽度。该选项会影响到折行,以及图像元素的宽度。<br>" +
|
||||
@@ -735,6 +744,12 @@ FILENAME_HEAD: "文件名",
|
||||
"Taskbone 的开发者(您懂的,没有人能用爱发电,Taskbone 开发者也需要投入资金来维持这项 OCR 服务)您可以" +
|
||||
"到 <a href='https://www.taskbone.com/' target='_blank'>taskbone.com</a> 购买一个商用 API key。购买后请将它填写到旁边这个文本框里,替换掉原本自动生成的免费 API key。",
|
||||
|
||||
//HotkeyEditor
|
||||
HOTKEY_PRESS_COMBO_NANE: "按下您的组合键",
|
||||
HOTKEY_PRESS_COMBO_DESC: "请按下所需的组合键",
|
||||
HOTKEY_BUTTON_ADD_OVERRIDE: "添加新的(热键)覆写",
|
||||
HOTKEY_BUTTON_REMOVE: "移除",
|
||||
|
||||
//openDrawings.ts
|
||||
SELECT_FILE: "选择一个文件后按回车。",
|
||||
SELECT_COMMAND: "选择一个命令后按回车。",
|
||||
|
||||
76
src/main.ts
76
src/main.ts
@@ -2826,37 +2826,53 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
this.popScope = null;
|
||||
}
|
||||
if (newActiveviewEV) {
|
||||
const scope = this.app.keymap.getRootScope();
|
||||
const handler_ctrlEnter = scope.register(["Mod"], "Enter", () => true);
|
||||
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
|
||||
const handler_ctrlK = scope.register(["Mod"], "k", () => true);
|
||||
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
|
||||
const handler_ctrlF = scope.register(["Mod"], "f", () => {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
search(view);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
|
||||
const overridSaveShortcut = (
|
||||
this.forceSaveCommand &&
|
||||
this.forceSaveCommand.hotkeys[0].key === "s" &&
|
||||
this.forceSaveCommand.hotkeys[0].modifiers.includes("Ctrl")
|
||||
)
|
||||
const saveHandler = overridSaveShortcut
|
||||
? scope.register(["Ctrl"], "s", () => this.forceSaveActiveView(false))
|
||||
: undefined;
|
||||
if(saveHandler) {
|
||||
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
|
||||
}
|
||||
this.popScope = () => {
|
||||
scope.unregister(handler_ctrlEnter);
|
||||
scope.unregister(handler_ctrlK);
|
||||
scope.unregister(handler_ctrlF);
|
||||
Boolean(saveHandler) && scope.unregister(saveHandler);
|
||||
this.registerHotkeyOverrides();
|
||||
}
|
||||
}
|
||||
|
||||
public registerHotkeyOverrides() {
|
||||
//this is repeated here because the same function is called when settings is closed after hotkeys have changed
|
||||
if (this.popScope) {
|
||||
this.popScope();
|
||||
this.popScope = null;
|
||||
}
|
||||
|
||||
if(!this.activeExcalidrawView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scope = this.app.keymap.getRootScope();
|
||||
// Register overrides from settings
|
||||
const overrideHandlers = this.settings.modifierKeyOverrides.map(override => {
|
||||
return scope.register(override.modifiers, override.key, () => true);
|
||||
});
|
||||
// Force handlers to the front of the list
|
||||
overrideHandlers.forEach(() => scope.keys.unshift(scope.keys.pop()));
|
||||
|
||||
const handler_ctrlF = scope.register(["Mod"], "f", () => {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
search(view);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
|
||||
const overridSaveShortcut = (
|
||||
this.forceSaveCommand &&
|
||||
this.forceSaveCommand.hotkeys[0].key === "s" &&
|
||||
this.forceSaveCommand.hotkeys[0].modifiers.includes("Ctrl")
|
||||
)
|
||||
const saveHandler = overridSaveShortcut
|
||||
? scope.register(["Ctrl"], "s", () => this.forceSaveActiveView(false))
|
||||
: undefined;
|
||||
if(saveHandler) {
|
||||
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
|
||||
}
|
||||
this.popScope = () => {
|
||||
overrideHandlers.forEach(handler => scope.unregister(handler));
|
||||
scope.unregister(handler_ctrlF);
|
||||
Boolean(saveHandler) && scope.unregister(saveHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ButtonComponent,
|
||||
DropdownComponent,
|
||||
getIcon,
|
||||
Modifier,
|
||||
normalizePath,
|
||||
PluginSettingTab,
|
||||
Setting,
|
||||
@@ -39,6 +40,7 @@ import { EDITOR_FADEOUT } from "./CodeMirrorExtension/EditorHandler";
|
||||
import { setDebugging } from "./utils/DebugHelper";
|
||||
import { Rank } from "./menu/ActionIcons";
|
||||
import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/constSettingsTags";
|
||||
import { HotkeyEditor } from "./dialogs/HotkeyEditor";
|
||||
|
||||
export interface ExcalidrawSettings {
|
||||
folder: string;
|
||||
@@ -204,6 +206,8 @@ export interface ExcalidrawSettings {
|
||||
longPressMobile: number;
|
||||
isDebugMode: boolean;
|
||||
rank: Rank;
|
||||
modifierKeyOverrides: {modifiers: Modifier[], key: string}[];
|
||||
showSplashscreen: boolean;
|
||||
}
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
@@ -220,8 +224,8 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
onceOffCompressFlagReset: false,
|
||||
onceOffGPTVersionReset: false,
|
||||
autosave: true,
|
||||
autosaveIntervalDesktop: 15000,
|
||||
autosaveIntervalMobile: 10000,
|
||||
autosaveIntervalDesktop: 30000,
|
||||
autosaveIntervalMobile: 20000,
|
||||
drawingFilenamePrefix: "Drawing ",
|
||||
drawingEmbedPrefixWithFilename: true,
|
||||
drawingFilnameEmbedPostfix: " ",
|
||||
@@ -464,6 +468,12 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
longPressMobile: 500,
|
||||
isDebugMode: false,
|
||||
rank: "Bronze",
|
||||
modifierKeyOverrides: [
|
||||
{modifiers: ["Mod"], key:"Enter"},
|
||||
{modifiers: ["Mod"], key:"k"},
|
||||
{modifiers: ["Mod"], key:"G"},
|
||||
],
|
||||
showSplashscreen: true,
|
||||
};
|
||||
|
||||
export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
@@ -472,6 +482,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
private requestReloadDrawings: boolean = false;
|
||||
private requestUpdatePinnedPens: boolean = false;
|
||||
private requestUpdateDynamicStyling: boolean = false;
|
||||
private hotkeyEditor: HotkeyEditor;
|
||||
//private reloadMathJax: boolean = false;
|
||||
//private applyDebounceTimer: number = 0;
|
||||
|
||||
@@ -498,21 +509,25 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}
|
||||
this.plugin.saveSettings();
|
||||
if (this.requestUpdatePinnedPens) {
|
||||
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
|
||||
this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
|
||||
if (v.view instanceof ExcalidrawView) v.view.updatePinnedCustomPens()
|
||||
})
|
||||
}
|
||||
if (this.requestUpdateDynamicStyling) {
|
||||
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
|
||||
this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
|
||||
if (v.view instanceof ExcalidrawView) {
|
||||
setDynamicStyle(this.plugin.ea,v.view,v.view.previousBackgroundColor,this.plugin.settings.dynamicStyling);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
this.hotkeyEditor.unload();
|
||||
if (this.hotkeyEditor.isDirty) {
|
||||
this.plugin.registerHotkeyOverrides();
|
||||
}
|
||||
if (this.requestReloadDrawings) {
|
||||
const exs =
|
||||
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
for (const v of exs) {
|
||||
if (v.view instanceof ExcalidrawView) {
|
||||
await v.view.save(false);
|
||||
@@ -747,7 +762,8 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
.setDesc(fragWithHTML(t("AUTOSAVE_INTERVAL_DESKTOP_DESC")))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("15000", "Frequent (every 15 seconds)")
|
||||
.addOption("15000", "Very frequent (every 15 seconds)")
|
||||
.addOption("30000", "Frequent (every 30 seconds)")
|
||||
.addOption("60000", "Moderate (every 60 seconds)")
|
||||
.addOption("300000", "Rare (every 5 minutes)")
|
||||
.addOption("900000", "Practically never (every 15 minutes)")
|
||||
@@ -763,7 +779,8 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
.setDesc(fragWithHTML(t("AUTOSAVE_INTERVAL_MOBILE_DESC")))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("10000", "Frequent (every 10 seconds)")
|
||||
.addOption("10000", "Very frequent (every 10 seconds)")
|
||||
.addOption("20000", "Frequent (every 20 seconds)")
|
||||
.addOption("30000", "Moderate (every 30 seconds)")
|
||||
.addOption("60000", "Rare (every 1 minute)")
|
||||
.addOption("300000", "Practically never (every 5 minutes)")
|
||||
@@ -1095,6 +1112,29 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
);
|
||||
addIframe(detailsEl, "H8Njp7ZXYag",999);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("TOGGLE_SPLASHSCREEN"))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.showSplashscreen)
|
||||
.onChange((value)=> {
|
||||
this.plugin.settings.showSplashscreen = value;
|
||||
this.applySettingsUpdate();
|
||||
})
|
||||
)
|
||||
|
||||
detailsEl = displayDetailsEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
text: t("HOTKEY_OVERRIDE_HEAD"),
|
||||
cls: "excalidraw-setting-h3",
|
||||
});
|
||||
detailsEl.createEl("span", {}, (el) => {
|
||||
el.innerHTML = t("HOTKEY_OVERRIDE_DESC");
|
||||
});
|
||||
|
||||
this.hotkeyEditor = new HotkeyEditor(detailsEl, this.plugin.settings, this.applySettingsUpdate);
|
||||
this.hotkeyEditor.onload();
|
||||
|
||||
detailsEl = displayDetailsEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
text: t("THEME_HEAD"),
|
||||
|
||||
1
src/types/types.d.ts
vendored
1
src/types/types.d.ts
vendored
@@ -28,6 +28,7 @@ export type DeviceType = {
|
||||
declare global {
|
||||
interface Window {
|
||||
ExcalidrawAutomate: ExcalidrawAutomate;
|
||||
pdfjsLib: any;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { ColorMaster } from "colormaster";
|
||||
import { ColorMaster } from "@zsviczian/colormaster";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import { DynamicStyle } from "src/types/types";
|
||||
|
||||
@@ -291,9 +291,7 @@ export const blobToBase64 = async (blob: Blob): Promise<string> => {
|
||||
}
|
||||
|
||||
export const getPDFDoc = async (f: TFile): Promise<any> => {
|
||||
//@ts-ignore
|
||||
if(typeof window.pdfjsLib === "undefined") await loadPdfJs();
|
||||
//@ts-ignore
|
||||
return await window.pdfjsLib.getDocument(app.vault.getResourcePath(f)).promise;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Modifier } from "obsidian";
|
||||
import { DEVICE } from "src/constants/constants";
|
||||
import { ExcalidrawSettings } from "src/settings";
|
||||
export type ModifierKeys = {shiftKey:boolean, ctrlKey: boolean, metaKey: boolean, altKey: boolean};
|
||||
@@ -177,4 +178,26 @@ export const emulateKeysForLinkClick = (action: PaneTarget): ModifierKeys => {
|
||||
|
||||
export const anyModifierKeysPressed = (e: ModifierKeys): boolean => {
|
||||
return e.shiftKey || e.ctrlKey || e.metaKey || e.altKey;
|
||||
}
|
||||
|
||||
export function modifierLabel(modifiers: Modifier[], platform?: "Mac" | "Other"): string {
|
||||
const isMacPlatform = platform === "Mac" ||
|
||||
(platform === undefined && (DEVICE.isIOS || DEVICE.isMacOS));
|
||||
|
||||
return modifiers.map(modifier => {
|
||||
switch (modifier) {
|
||||
case "Mod":
|
||||
return isMacPlatform ? "CMD" : "CTRL";
|
||||
case "Ctrl":
|
||||
return "CTRL";
|
||||
case "Meta":
|
||||
return isMacPlatform ? "CMD" : "WIN";
|
||||
case "Shift":
|
||||
return "SHIFT";
|
||||
case "Alt":
|
||||
return isMacPlatform ? "OPTION" : "ALT";
|
||||
default:
|
||||
return modifier;
|
||||
}
|
||||
}).join("+");
|
||||
}
|
||||
Reference in New Issue
Block a user