Compare commits

...

14 Commits

Author SHA1 Message Date
zsviczian
c97d08c997 Merge pull request #2017 from zsviczian/fix-getExcalidrawViews
fixed getExcalidrawView
2024-09-12 12:29:49 +02:00
zsviczian
097d1bcd1b fixed getExcalidrawView 2024-09-12 10:27:04 +00:00
zsviczian
7c91186ed5 Update ExcalidrawViewUtils.ts 2024-09-12 11:41:47 +02:00
zsviczian
904bc7c994 Update ExcalidrawView.ts 2024-09-12 11:39:25 +02:00
zsviczian
9fd4ae2615 Update manifest-beta.json 2024-09-12 11:16:51 +02:00
zsviczian
18fbb0934e Merge pull request #2016 from zsviczian/style-tweaks
Obsidian 1.7.2: rework getExcalidrawViews from leaves, fix tools pane…
2024-09-12 11:10:11 +02:00
zsviczian
3ae59c85d2 Obsidian 1.7.2: rework getExcalidrawViews from leaves, fix tools panel width, fix math types 2024-09-12 09:07:43 +00:00
zsviczian
55db9b0ddb 2.5.0-beta-3 2024-09-11 22:21:50 +02:00
zsviczian
3cca2fedb0 2.5.0-beta-2 2024-09-10 18:56:38 +02:00
zsviczian
a0682b8e3c Merge pull request #2014 from dmscode/master
Update zh-cn.ts to 2.4.3
2024-09-10 07:38:37 +02:00
dmscode
da19ce1b84 Update zh-cn.ts to 2.4.3
Add a folder in ./docs for more Chinese docs, and move zh-cn README into it. Translated AutomateHowTo.md
2024-09-10 07:54:46 +08:00
zsviczian
2b1f504b5b 2.5.0-beta-1, changed to Excalidraw.com search 2024-09-09 23:03:57 +02:00
zsviczian
d8f2776b40 Improved template load performance by filtering fileIds based on selection. 2024-09-09 21:27:39 +02:00
zsviczian
2fd800e0a0 2.4.3 2024-09-09 20:20:12 +02:00
28 changed files with 792 additions and 151 deletions

View File

@@ -1,6 +1,6 @@
# Excalidraw
[简体中文](./README.zh-cn.md)
[简体中文](./docs/zh-cn/README.md)
👉👉👉 Check out and contribute to the new [Obsidian-Excalidraw Community Wiki](https://excalidraw-obsidian.online/Hobbies/Excalidraw+Blog/WIKI/Welcome+to+the+WIKI)

484
docs/zh-cn/AutomateHowTo.md Normal file
View File

@@ -0,0 +1,484 @@
# Excalidraw 自动化使用指南
> 此说明当前更新至 `5569cff`。
[English](./AutomateHowTo.md)
Excalidraw 自动化允许您使用 [Templater](https://github.com/SilentVoid13/Templater) 插件创建 Excalidraw 绘图。
通过一些工作,使用 Excalidraw 自动化,您可以根据保管库中的文档生成简单的思维导图、填写 SVG 表单、创建自定义图表等。
您可以通过 ExcalidrawAutomate 对象访问 Excalidraw 自动化。我建议您以以下代码开始您的自动化脚本。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
const ea = ExcalidrawAutomate;
ea.reset();
```
第一行创建了一个实用的常量,这样您就可以避免写 100 次 `ExcalidrawAutomate`
第二行将 `ExcalidrawAutomate` 重置为默认值。这一点很重要,因为您将不知道之前执行了哪个模板,因此您也不知道 `Excalidraw` 的状态。
## 使用 Excalidraw 自动化的基本逻辑
1. 设置您想要绘制的元素的样式
2. 添加元素。每添加一个新元素,它都会在上一个元素的上方添加一层,因此在重叠对象的情况下,后添加的元素会在前一个元素之上。
3. 调用 `await ea.create();` 来实例化绘图
您可以在添加不同元素之间更改样式。我将元素样式与创建分开是基于这样的假设:您可能会设置描边颜色、描边样式、描边粗糙度等,并使用这些设置绘制大多数元素。每次添加元素时设置所有这些参数是没有意义的。
### 在深入探讨之前,这里有两个简单的示例脚本
#### 使用模板在自定义文件夹中创建具有自定义名称的新绘图
这个简单的脚本为您提供了比 Excalidraw 插件设置更大的灵活性,可以为您的绘图命名、将其放入文件夹中,并应用模板。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
await ea.create({
filename : tp.date.now("HH.mm"),
foldername : tp.date.now("YYYY-MM-DD"),
templatePath: "Excalidraw/Template1.excalidraw",
onNewPane : false
});
%>
```
#### 创建一个简单的绘图
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
ea.addRect(-150,-50,450,300);
ea.addText(-100,70,"Left to right");
ea.addArrow([[-100,100],[100,100]]);
ea.style.strokeColor = "red";
ea.addText(100,-30,"top to bottom",{width:200,textAligh:"center"});
ea.addArrow([[200,0],[200,200]]);
await ea.create();
%>
```
该脚本将生成以下绘图:
![FristDemo](https://user-images.githubusercontent.com/14358394/116825643-6e5a8b00-ab90-11eb-9e3a-37c524620d0d.png)
## 属性和功能一览
这是 ExcalidrawAutomate 实现的接口:
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
ExcalidrawAutomate: {
style: {
strokeColor: string;
backgroundColor: string;
angle: number;
fillStyle: FillStyle;
strokeWidth: number;
storkeStyle: StrokeStyle;
roughness: number;
opacity: number;
strokeSharpness: StrokeSharpness;
fontFamily: FontFamily;
fontSize: number;
textAlign: string;
verticalAlign: string;
startArrowHead: string;
endArrowHead: string;
}
canvas: {theme: string, viewBackgroundColor: string};
setFillStyle: Function;
setStrokeStyle: Function;
setStrokeSharpness: Function;
setFontFamily: Function;
setTheme: Function;
addRect: Function;
addDiamond: Function;
addEllipse: Function;
addText: Function;
addLine: Function;
addArrow: Function;
connectObjects: Function;
addToGroup: Function;
toClipboard: Function;
create: Function;
createPNG: Function;
createSVG: Function;
clear: Function;
reset: Function;
};
```
## 元素样式
正如您所注意到的,某些样式具有设置函数。这是为了帮助您浏览属性的可用值。不过,您并不需要使用设置函数,您也可以直接设置值。
### strokeColor
字符串。线条的颜色。[CSS 合法颜色值](https://www.w3schools.com/cssref/css_colors_legal.asp)
允许的值包括 [HTML 颜色名称](https://www.w3schools.com/colors/colors_names.asp)、十六进制 RGB 字符串,例如 `#FF0000` 表示红色。
### backgroundColor
字符串。对象的填充颜色。[CSS 合法颜色值](https://www.w3schools.com/cssref/css_colors_legal.asp)
允许的值包括 [HTML 颜色名称](https://www.w3schools.com/colors/colors_names.asp)、十六进制 RGB 字符串,例如 `#FF0000` 表示红色,或 `transparent`(透明)。
### angle
数字。以弧度表示的旋转。90° == `Math.PI/2`
### fillStyle, setFillStyle()
```typescript
type FillStyle = "hachure" | "cross-hatch" | "solid";
setFillStyle (val:number);
```
fillStyle 是一个字符串.
`setFillStyle()` 接受一个数字:
- 0: "hachure"(斜线填充)
- 1: "cross-hatch"(交叉斜线填充)
- 其他任何数字: "solid"(实心填充)
### strokeWidth
数字,设置描边的宽度。
### strokeStyle, setStrokeStyle()
```typescript
type StrokeStyle = "solid" | "dashed" | "dotted";
setStrokeStyle (val:number);
```
strokeStyle 是一个字符串。
`setStrokeStyle()` 接受一个数字:
- 0: "solid"(实线)
- 1: "dashed"(虚线)
- 其他任何数字: "dotted"(点线)
### roughness
数字。在 Excalidraw 中称为“粗糙度”。接受三个值:
- 0: 建筑师
- 1: 艺术家
- 2: 卡通画家
### opacity
介于 0 和 100 之间的数字。对象的透明度,包括描边和填充。
### strokeSharpness, setStrokeSharpness()
```typescript
type StrokeSharpness = "round" | "sharp";
setStrokeSharpness(val:nmuber);
```
strokeSharpness 是一个字符串。
“round” 线条是曲线“sharp” 线条在转折点处断开(硬弯折)。
`setStrokeSharpness()` 接受一个数字:
- 0: "round"(圆滑)
- 其他任何数字: "sharp"(尖锐)
### fontFamily, setFontFamily()
数字。有效值为 1、2 和 3。
`setFontFamily()` 也会接受一个数字并返回字体名称。
- 1: "Virgil, Segoe UI Emoji"
- 2: "Helvetica, Segoe UI Emoji"
- 3: "Cascadia, Segoe UI Emoji"
### fontSize
数字。默认值为 20 像素。
### textAlign
字符串。文本的水平对齐方式。有效值为 "left"(左对齐)、"center"(居中对齐)、"right"(右对齐)。
在使用 `addText()` 函数设置固定宽度时,这一点很重要。
### verticalAlign
字符串。文本的垂直对齐方式。有效值为 "top"(顶部)和 "middle"(中间)。
在使用 `addText()` 函数设置固定高度时,这一点很重要。
### startArrowHead, endArrowHead
字符串。有效值为 "arrow"(箭头)、"bar"(线条)、"dot"(点)和 "none"(无)。指定箭头的起始和结束。
在使用 `addArrow()``connectObjects()` 函数时,这一点很重要。
## canvas
设置画布的属性。
### theme, setTheme()
字符串。有效值为 "light"(明亮)和 "dark"(黑暗)。
`setTheme()` 接受一个数字:
- 0: "light"(明亮)
- 其他任何数字: "dark"(黑暗)
### viewBackgroundColor
字符串。对象的填充颜色。[CSS 合法颜色值](https://www.w3schools.com/cssref/css_colors_legal.asp)
允许的值包括 [HTML 颜色名称](https://www.w3schools.com/colors/colors_names.asp)、十六进制 RGB 字符串,例如 `#FF0000` 表示红色,或 `transparent`(透明)。
## 添加对象
这些函数将向您的绘图中添加对象。画布是无限的,接受负值和正值的 X 和 Y 坐标。X 值从左到右增加Y 值从上到下增加。
![坐标](https://user-images.githubusercontent.com/14358394/116825632-6569b980-ab90-11eb-827b-ada598e91e46.png)
### addRect(), addDiamond(), addEllipse()
```typescript
addRect(topX:number, topY:number, width:number, height:number):string
addDiamond(topX:number, topY:number, width:number, height:number):string
addEllipse(topX:number, topY:number, width:number, height:number):string
```
返回对象的 `id`。在用线连接对象时,需要使用 `id`。请参见后文。
### addText
```typescript
addText(topX:number, topY:number, text:string, formatting?:{width:number, height:number,textAlign: string, verticalAlign:string, box: boolean, boxPadding: number}):string
```
向绘图中添加文本。
格式参数是可选的:
- 如果未指定 `width`(宽度)和 `height`(高度),函数将根据 `fontFamily``fontSize` 和提供的文本计算宽度和高度。
- 如果您希望文本相对于绘图中的其他元素居中,可以提供固定的高度和宽度,同时可以指定 `textAlign``verticalAlign`,如上所述。例如:`{width:500, textAlign:"center"}`
- 如果您想在文本周围添加一个框,请设置 `{box:true}`
返回对象的 `id`。在用线连接对象时,需要使用 `id`。请参见后文。如果 `{box:true}`,则返回包围框的 `id`
### addLine()
```typescript
addLine(points: [[x:number,y:number]]):void
```
添加一条连接提供的点的线。必须至少包含两个点 `points.length >= 2`。如果提供的点超过两个,间隔点将作为断点添加。如果 `strokeSharpness` 设置为 "sharp",线条将在转折处断开;如果设置为 "round",线条将是曲线。
### addArrow()
```typescript
addArrow(points: [[x:number,y:number]],formatting?:{startArrowHead:string,endArrowHead:string,startObjectId:string,endObjectId:string}):void
```
添加一条连接提供的点的箭头。必须至少包含两个点 `points.length >= 2`。如果提供的点超过两个,间隔点将作为断点添加。如果元素 `style.strokeSharpness` 设置为 "sharp",线条将在转折处断开;如果设置为 "round",线条将是曲线。
`startArrowHead``endArrowHead` 指定要使用的箭头类型,如上所述。有效值为 "none"(无)、"arrow"(箭头)、"dot"(点)和 "bar"(线条)。例如:`{startArrowHead: "dot", endArrowHead: "arrow"}`
`startObjectId``endObjectId` 是连接对象的对象 ID。我建议使用 `connectObjects` 而不是调用 `addArrow()` 来连接对象。
### connectObjects()
```typescript
declare type ConnectionPoint = "top"|"bottom"|"left"|"right";
connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?:{numberOfPoints: number,startArrowHead:string,endArrowHead:string, padding: number}):void
```
连接两个对象的箭头。
`objectA``objectB` 是字符串。这些是要连接的对象的 ID。这些 ID 是通过 `addRect()``addDiamond()``addEllipse()``addText()` 创建这些对象时返回的。
`connectionA``connectionB` 指定在对象上的连接位置。有效值为:"top"(上)、"bottom"(下)、"left"(左)和 "right"(右)。
`numberOfPoints` 设置线条的间隔断点数量。默认值为零,意味着箭头的起点和终点之间不会有断点。当在绘图中移动对象时,这些断点将影响 Excalidraw 如何重新调整线条。
`startArrowHead``endArrowHead` 的功能与 `addArrow()` 中描述的一致。
### addToGroup()
```typescript
addToGroup(objectIds:[]):void
```
`objectIds` 中列出的对象进行分组。
## Utility functions
### clear()
`clear()` 将从缓存中清除对象,但会保留元素样式设置。
### reset()
`reset()` 将首先调用 `clear()`,然后将元素样式重置为默认值。
### toClipboard()
```typescript
async toClipboard(templatePath?:string)
```
将生成的图形放入剪贴板。当您不想创建新图形,而是想将其他项目粘贴到现有图形上时,这非常有用。
### create()
```typescript
async create(params?:{filename: string, foldername:string, templatePath:string, onNewPane: boolean})
```
创建图形并打开它。
`filename` 是要创建的图形的文件名(不带扩展名)。如果为 `null`,则 Excalidraw 会生成一个文件名。
`foldername` 是文件应创建的文件夹。如果为 `null`,则将根据 Excalidraw 设置使用新图形的默认文件夹。
`templatePath` 是包含完整路径和扩展名的模板文件名。该模板文件将作为基础层添加,所有通过 ExcalidrawAutomate 添加的额外对象将出现在模板元素之上。如果为 `null`,则不使用模板,即空白图形将作为添加对象的基础。
`onNewPane` 定义新图形应创建的位置。`false` 将在当前活动的标签页中打开图形;`true` 将通过垂直分割当前标签页来打开图形。
示例:
```javascript
create({filename:"my drawing", foldername:"myfolder/subfolder/", templatePath: "Excalidraw/template.excalidraw", onNewPane: true});
```
### createSVG()
```typescript
async createSVG(templatePath?:string)
```
返回一个包含生成图形的 HTML `SVGSVGElement`
### createPNG()
```typescript
async createPNG(templatePath?:string)
```
返回一个包含生成图形的 PNG 图像的 blob。
## 示例
### 将新图形插入到当前编辑的文档中
此模板将提示您输入图形的标题。它将在您提供的标题下创建一个新图形,并在您正在编辑的文档的文件夹中。然后,它将在光标位置插入新图形,并通过分割当前标签页在新的工作区标签页中打开新图形。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
<%*
const defaultTitle = tp.date.now("HHmm")+' '+tp.file.title;
const title = await tp.system.prompt("Title of the drawing?", defaultTitle);
const folder = tp.file.folder(true);
const transcludePath = (folder== '/' ? '' : folder + '/') + title + '.excalidraw';
tR = String.fromCharCode(96,96,96)+'excalidraw\n[['+transcludePath+']]\n'+String.fromCharCode(96,96,96);
const ea = ExcalidrawAutomate;
ea.reset();
ea.setTheme(1); //set Theme to dark
await ea.create({
filename : title,
foldername : folder,
//templatePath: 'Excalidraw/Template.excalidraw', //uncomment if you want to use a template
onNewPane : true
});
%>
```
### 连接对象
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
ea.addText(-130,-100,"Connecting two objects");
const a = ea.addRect(-100,-100,100,100);
const b = ea.addEllipse(200,200,100,100);
ea.connectObjects(a,"bottom",b,"left",{numberOfPoints: 2}); //see how the line breaks differently when moving objects around
ea.style.strokeColor = "red";
ea.connectObjects(a,"right",b,"top",1);
await ea.create();
%>
```
### 使用模板
这个示例与第一个类似,但旋转了 90°并使用了模板同时指定了文件名和保存图形的文件夹并在新的标签页中打开新图形。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
ea.style.angle = Math.PI/2;
ea.style.strokeWidth = 3.5;
ea.addRect(-150,-50,450,300);
ea.addText(-100,70,"Left to right");
ea.addArrow([[-100,100],[100,100]]);
ea.style.strokeColor = "red";
await ea.addText(100,-30,"top to bottom",{width:200,textAlign:"center"});
ea.addArrow([[200,0],[200,200]]);
await ea.create({filename:"My Drawing",foldername:"myfolder/fordemo/",templatePath:"Excalidraw/Template2.excalidraw",onNewPane:true});
%>
```
### 从文本大纲生成简单思维导图
这是一个稍微复杂一些的示例。这个示例将从一个表格化的大纲生成思维导图。
![Drawing 2021-05-05 20 52 34](https://user-images.githubusercontent.com/14358394/117194124-00a69d00-ade4-11eb-8b75-5e18a9cbc3cd.png)
输入示例:
```
- Test 1
- Test 1.1
- Test 2
- Test 2.1
- Test 2.2
- Test 2.2.1
- Test 2.2.2
- Test 2.2.3
- Test 2.2.3.1
- Test 3
- Test 3.1
```
The script:
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
<%*
const IDX = Object.freeze({"depth":0, "text":1, "parent":2, "size":3, "children": 4, "objectId":5});
//check if an editor is the active view
const editor = this.app.workspace.activeLeaf?.view?.editor;
if(!editor) return;
//initialize the tree with the title of the document as the first element
let tree = [[0,this.app.workspace.activeLeaf?.view?.getDisplayText(),-1,0,[],0]];
const linecount = editor.lineCount();
//helper function, use regex to calculate indentation depth, and to get line text
function getLineProps (i) {
props = editor.getLine(i).match(/^(\t*)-\s+(.*)/);
return [props[1].length+1, props[2]];
}
//a vector that will hold last valid parent for each depth
let parents = [0];
//load outline into tree
for(i=0;i<linecount;i++) {
[depth,text] = getLineProps(i);
if(depth>parents.length) parents.push(i+1);
else parents[depth] = i+1;
tree.push([depth,text,parents[depth-1],1,[]]);
tree[parents[depth-1]][IDX.children].push(i+1);
}
//recursive function to crawl the tree and identify height aka. size of each node
function crawlTree(i) {
if(i>linecount) return 0;
size = 0;
if((i+1<=linecount && tree[i+1][IDX.depth] <= tree[i][IDX.depth])|| i == linecount) { //I am a leaf
tree[i][IDX.size] = 1;
return 1;
}
tree[i][IDX.children].forEach((node)=>{
size += crawlTree(node);
});
tree[i][IDX.size] = size;
return size;
}
crawlTree(0);
//Build the mindmap in Excalidraw
const width = 300;
const height = 100;
const ea = ExcalidrawAutomate;
ea.reset();
//stores position offset of branch/leaf in height units
offsets = [0];
for(i=0;i<=linecount;i++) {
depth = tree[i][IDX.depth];
if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
tree[i][IDX.objectId] = ea.addText(depth*width,((tree[i][IDX.size]/2)+offsets[depth])*height,tree[i][IDX.text],{box:true});
//set child offset equal to parent offset
if((depth+1)>offsets.length) offsets.push(offsets[depth]);
else offsets[depth+1] = offsets[depth];
offsets[depth] += tree[i][IDX.size];
if(tree[i][IDX.parent]!=-1) {
ea.connectObjects(tree[tree[i][IDX.parent]][IDX.objectId],"right",tree[i][IDX.objectId],"left",{startArrowHead: 'dot'});
}
}
await ea.create({onNewPane: true});
%>
```

View File

@@ -1,6 +1,10 @@
# Excalidraw
> 此说明当前更新至 2.4.0-beta-9
> 此说明当前更新至 `5569cff`
[English](./README.md)
👉👉👉 快来查看并为新的 [Obsidian-Excalidraw 社区维基](https://excalidraw-obsidian.online/Hobbies/Excalidraw+Blog/WIKI/Welcome+to+the+WIKI)贡献你的力量吧
Obsidian-Excalidraw 插件将 [Excalidraw](https://excalidraw.com/) 这一功能丰富的草图工具集成到 Obsidian 中。您可以在您的库中存储和编辑 Excalidraw 文件,可以将图形嵌入到文档中,还可以在 Excalidraw 中链接到文档和其他图形。有关 Excalidraw 功能的展示,请查看我的博客文章 [这里](https://www.zsolt.blog/2021/03/showcasing-excalidraw.html) 或观看以下视频。

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.4.3-rc-2",
"version": "2.5.0-beta-5",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.4.2",
"version": "2.4.3",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

View File

@@ -19,7 +19,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.1-obsidian-49",
"@zsviczian/excalidraw": "0.17.1-obsidian-52",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"@zsviczian/colormaster": "^1.2.2",

View File

@@ -593,6 +593,7 @@ export class EmbeddedFilesLoader {
addFiles: (files: FileData[], isDark: boolean, final?: boolean) => void,
depth:number,
isThemeChange:boolean = false,
fileIDWhiteList?: Set<FileId>,
) {
if(depth > 7) {
@@ -607,6 +608,7 @@ export class EmbeddedFilesLoader {
let entry: IteratorResult<[FileId, EmbeddedFile]>;
const files: FileData[] = [];
while (!this.terminate && !(entry = entries.next()).done) {
if(fileIDWhiteList && !fileIDWhiteList.has(entry.value[0])) continue;
const embeddedFile: EmbeddedFile = entry.value[1];
if (!embeddedFile.isLoaded(this.isDark)) {
//debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,status:"embedded Files are not loaded"});
@@ -653,6 +655,7 @@ export class EmbeddedFilesLoader {
let equation;
const equations = excalidrawData.getEquationEntries();
while (!this.terminate && !(equation = equations.next()).done) {
if(fileIDWhiteList && !fileIDWhiteList.has(entry.value[0])) continue;
if (!excalidrawData.getEquation(equation.value[0]).isLoaded) {
const latex = equation.value[1].latex;
const data = await tex2dataURL(latex);

View File

@@ -21,7 +21,6 @@ import { ExcalidrawData, getMarkdownDrawingSection, REGEX_LINK } from "src/Excal
import {
FRONTMATTER,
nanoid,
VIEW_TYPE_EXCALIDRAW,
MAX_IMAGE_SIZE,
COLOR_NAMES,
fileid,
@@ -55,14 +54,14 @@ import {
wrapTextAtCharLength,
arrayToMap,
} from "src/utils/Utils";
import { getAttachmentsFolderAndFilePath, getLeaf, getNewOrAdjacentLeaf, isObsidianThemeDark, mergeMarkdownFiles, openLeaf } from "src/utils/ObsidianUtils";
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, Point } from "@zsviczian/excalidraw/types/excalidraw/types";
import { getAttachmentsFolderAndFilePath, getExcalidrawViews, getLeaf, getNewOrAdjacentLeaf, isObsidianThemeDark, mergeMarkdownFiles, openLeaf } from "src/utils/ObsidianUtils";
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
import { EmbeddedFile, EmbeddedFilesLoader, FileData } from "src/EmbeddedFileLoader";
import { tex2dataURL } from "src/LaTeX";
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 { ConnectionPoint, DeviceType, Point } from "src/types/types";
import CM, { ColorMaster, extendPlugins } from "@zsviczian/colormaster";
import HarmonyPlugin from "@zsviczian/colormaster/plugins/harmony";
import MixPlugin from "@zsviczian/colormaster/plugins/mix"
@@ -1826,33 +1825,23 @@ export class ExcalidrawAutomate {
*/
setView(view?: ExcalidrawView | "first" | "active"): ExcalidrawView {
if(!view) {
const v = app.workspace.getActiveViewOfType(ExcalidrawView);
const v = this.plugin.app.workspace.getActiveViewOfType(ExcalidrawView);
if (v instanceof ExcalidrawView) {
this.targetView = v;
}
else {
const leaves =
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
if (!leaves || leaves.length == 0) {
return;
}
this.targetView = leaves[0].view as ExcalidrawView;
this.targetView = getExcalidrawViews(this.plugin.app)[0];
}
}
if (view == "active") {
const v = app.workspace.getActiveViewOfType(ExcalidrawView);
const v = this.plugin.app.workspace.getActiveViewOfType(ExcalidrawView);
if (!(v instanceof ExcalidrawView)) {
return;
}
this.targetView = v;
}
if (view == "first") {
const leaves =
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
if (!leaves || leaves.length == 0) {
return;
}
this.targetView = leaves[0].view as ExcalidrawView;
this.targetView = getExcalidrawViews(this.plugin.app)[0];
}
if (view instanceof ExcalidrawView) {
this.targetView = view;
@@ -2963,27 +2952,6 @@ async function getTemplate(
}
let scene = excalidrawData.scene;
if (loadFiles) {
//debug({where:"getTemplate",template:file.name,loader:loader.uid});
await loader.loadSceneFiles(excalidrawData, (fileArray: FileData[]) => {
//, isDark: boolean) => {
if (!fileArray || fileArray.length === 0) {
return;
}
for (const f of fileArray) {
if (f.hasSVGwithBitmap) {
hasSVGwithBitmap = true;
}
excalidrawData.scene.files[f.id] = {
mimeType: f.mimeType,
id: f.id,
dataURL: f.dataURL,
created: f.created,
};
}
scene = scaleLoadedImage(excalidrawData.scene, fileArray).scene;
}, depth);
}
let groupElements:ExcalidrawElement[] = scene.elements;
if(filenameParts.hasGroupref) {
@@ -3010,8 +2978,49 @@ async function getTemplate(
));
}
let fileIDWhiteList:Set<FileId>;
if(groupElements.length < scene.elements.length) {
fileIDWhiteList = new Set<FileId>();
groupElements.filter(el=>el.type==="image").forEach((el:ExcalidrawImageElement)=>fileIDWhiteList.add(el.fileId));
}
if (loadFiles) {
//debug({where:"getTemplate",template:file.name,loader:loader.uid});
await loader.loadSceneFiles(excalidrawData, (fileArray: FileData[]) => {
//, isDark: boolean) => {
if (!fileArray || fileArray.length === 0) {
return;
}
for (const f of fileArray) {
if (f.hasSVGwithBitmap) {
hasSVGwithBitmap = true;
}
excalidrawData.scene.files[f.id] = {
mimeType: f.mimeType,
id: f.id,
dataURL: f.dataURL,
created: f.created,
};
}
scene = scaleLoadedImage(excalidrawData.scene, fileArray).scene;
}, depth, false, fileIDWhiteList);
}
excalidrawData.destroy();
const filehead = data.substring(0, trimLocation);
let files:any = {};
const sceneFilesSize = Object.values(scene.files).length;
if (sceneFilesSize > 0) {
if(fileIDWhiteList && (sceneFilesSize > fileIDWhiteList.size)) {
Object.values(scene.files).filter((f: any) => fileIDWhiteList.has(f.id)).forEach((f: any) => {
files[f.id] = f;
});
} else {
files = scene.files;
}
}
return {
elements: convertMarkdownLinksToObsidianURLs
? updateElementLinksToObsidianLinks({
@@ -3020,7 +3029,7 @@ async function getTemplate(
}) : groupElements,
appState: scene.appState,
frontmatter: filehead.match(/^---\n.*\n---\n/ms)?.[0] ?? filehead,
files: scene.files,
files,
hasSVGwithBitmap,
};
}

View File

@@ -763,6 +763,8 @@ export default class ExcalidrawView extends TextFileView {
this.clearPreventReloadTimer();
this.semaphores.preventReload = preventReload;
await this.prepareGetViewData();
//added this to avoid Electron crash when terminating a popout window and saving the drawing, need to check back
//can likely be removed once this is resolved: https://github.com/electron/electron/issues/40607
if(this.semaphores?.viewunload) {
@@ -774,10 +776,10 @@ export default class ExcalidrawView extends TextFileView {
await plugin.app.vault.modify(file,d);
await imageCache.addBAKToCache(file.path,d);
},200)
this.semaphores.saving = false;
return;
}
await this.prepareGetViewData();
await super.save();
if (process.env.NODE_ENV === 'development') {
if (DEBUGGING) {
@@ -1548,7 +1550,7 @@ export default class ExcalidrawView extends TextFileView {
this.registerDomEvent(this.ownerWindow, "keydown", onKeyDown, false);
this.registerDomEvent(this.ownerWindow, "keyup", onKeyUp, false);
this.registerDomEvent(this.contentEl, "mouseleave", onBlurOrLeave, false);
//this.registerDomEvent(this.contentEl, "mouseleave", onBlurOrLeave, false); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2004
this.registerDomEvent(this.ownerWindow, "blur", onBlurOrLeave, false);
});
@@ -1787,7 +1789,7 @@ export default class ExcalidrawView extends TextFileView {
//deliberately not calling super.onUnloadFile() to avoid autosave (saved in unload)
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onUnloadFile,`ExcalidrawView.onUnloadFile, file:${this.file?.name}`);
let counter = 0;
while (this.semaphores.saving) {
while (this.semaphores.saving && (counter++ < 200)) {
await sleep(50); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1988
if(counter++ === 15) {
new Notice(t("SAVE_IS_TAKING_LONG"));
@@ -1796,6 +1798,10 @@ export default class ExcalidrawView extends TextFileView {
new Notice(t("SAVE_IS_TAKING_VERY_LONG"));
}
}
if(counter >= 200) {
new Notice("Unknown error, save is taking too long");
return;
}
}
private async forceSaveIfRequired():Promise<boolean> {
@@ -2319,14 +2325,35 @@ export default class ExcalidrawView extends TextFileView {
});
}
private getGridColor(bgColor: string, st: AppState):{Bold: string, Regular: string} {
private getGridColor(bgColor: string, st: AppState): { Bold: string, Regular: string } {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.getGridColor, "ExcalidrawView.getGridColor", bgColor, st);
const cm = this.plugin.ea.getCM(bgColor);
const isDark = cm.isDark();
const Regular = (isDark ? cm.lighterBy(7) : cm.darkerBy(7)).stringHEX({alpha: false});
const Bold = (isDark ? cm.lighterBy(14) : cm.darkerBy(14)).stringHEX({alpha: false});
return {Bold, Regular};
let Regular: string;
let Bold: string;
const opacity = this.plugin.settings.gridSettings.OPACITY/100;
if (this.plugin.settings.gridSettings.DYNAMIC_COLOR) {
// Dynamic color: concatenate opacity to the HEX string
Regular = (isDark ? cm.lighterBy(7) : cm.darkerBy(7)).alphaTo(opacity).stringRGB({ alpha: true });
Bold = (isDark ? cm.lighterBy(14) : cm.darkerBy(14)).alphaTo(opacity).stringRGB({ alpha: true });
} else {
// Custom color handling
const customCM = this.plugin.ea.getCM(this.plugin.settings.gridSettings.COLOR);
const customIsDark = customCM.isDark();
// Regular uses the custom color directly
Regular = customCM.alphaTo(opacity).stringRGB({ alpha: true });
// Bold is 7 shades lighter or darker based on the custom color's darkness
Bold = (customIsDark ? customCM.lighterBy(7) : customCM.darkerBy(7)).alphaTo(opacity).stringRGB({ alpha: true });
}
return { Bold, Regular };
}
public activeLoader: EmbeddedFilesLoader = null;
private nextLoader: EmbeddedFilesLoader = null;
@@ -3612,6 +3639,7 @@ export default class ExcalidrawView extends TextFileView {
private excalidrawDIVonKeyDown(event: KeyboardEvent) {
//(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.excalidrawDIVonKeyDown, "ExcalidrawView.excalidrawDIVonKeyDown", event);
if (this.semaphores?.viewunload) return;
if (event.target === this.excalidrawWrapperRef.current) {
return;
} //event should originate from the canvas
@@ -3738,10 +3766,18 @@ export default class ExcalidrawView extends TextFileView {
}
}
public updateGridColor(canvasColor?: string, st?: any) {
if(!canvasColor) {
st = (this.excalidrawAPI as ExcalidrawImperativeAPI).getAppState();
canvasColor = canvasColor ?? st.viewBackgroundColor === "transparent" ? "white" : st.viewBackgroundColor;
}
window.setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor, st)}, storeAction: "update"}));
}
private canvasColorChangeHook(st: AppState) {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.canvasColorChangeHook, "ExcalidrawView.canvasColorChangeHook", st);
const canvasColor = st.viewBackgroundColor === "transparent" ? "white" : st.viewBackgroundColor;
window.setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor, st)}, storeAction: "update"}));
this.updateGridColor(canvasColor,st);
setDynamicStyle(this.plugin.ea,this,canvasColor,this.plugin.settings.dynamicStyling);
if(this.plugin.ea.onCanvasColorChangeHook) {
try {

View File

@@ -5,7 +5,7 @@ import {
TFile,
WorkspaceLeaf,
} from "obsidian";
import { PLUGIN_ID, VIEW_TYPE_EXCALIDRAW } from "./constants/constants";
import { PLUGIN_ID } from "./constants/constants";
import ExcalidrawView from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import { ButtonDefinition, GenericInputPrompt, GenericSuggester } from "./dialogs/Prompt";
@@ -14,6 +14,7 @@ import { splitFolderAndFilename } from "./utils/FileUtils";
import { getEA } from "src";
import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
import { WeakArray } from "./utils/WeakArray";
import { getExcalidrawViews } from "./utils/ObsidianUtils";
export type ScriptIconMap = {
[key: string]: { name: string; group: string; svgString: string };
@@ -303,10 +304,8 @@ export class ScriptEngine {
}
private updateToolPannels() {
const leaves =
this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
leaves.forEach((leaf: WorkspaceLeaf) => {
const excalidrawView = leaf.view as ExcalidrawView;
const excalidrawViews = getExcalidrawViews(this.plugin.app);
excalidrawViews.forEach(excalidrawView => {
excalidrawView.toolsPanelRef?.current?.updateScriptIconMap(
this.scriptIconMap,
);

View File

@@ -17,6 +17,17 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
`,
"2.4.3": `
Check out the [Excalidraw Plugin's Community WIKI](https://excalidraw-obsidian.online/Hobbies/Excalidraw+Blog/WIKI/Welcome+to+the+WIKI) and help with your content contribution.
## Fixed
- In some situations Excalidraw hangs indefinitely when opening a different file in the same tab
- Can't exit arrow tool on phone [#2006](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2006)
- Save is triggered every few seconds, leading to glitches in handwriting [#2004](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2004)
- Canvas shifts when editing text reaches right hand side of the canvas, especially at higher zoom values
- Minor styling tweaks to adapt to Obsidian 1.7.1 new stylesheet, in particular to scale Excalidraw properly in line with Obsidian Appearance Setting Font-Size value.
- Tweaked Compatibilty Setting description to mention Obsidian 1.7.1 Footnotes support
`,
"2.4.2": `
This release addresses critical issues for all Obsidian Mobile users.

View File

@@ -1,12 +1,13 @@
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
import { ColorComponent, Modal, Setting, SliderComponent, TextComponent, ToggleComponent } from "obsidian";
import { COLOR_NAMES, VIEW_TYPE_EXCALIDRAW } from "src/constants/constants";
import { ColorComponent, Modal, Setting, TextComponent, ToggleComponent } from "obsidian";
import { COLOR_NAMES } from "src/constants/constants";
import ExcalidrawView from "src/ExcalidrawView";
import ExcalidrawPlugin from "src/main";
import { setPen } from "src/menu/ObsidianMenu";
import { ExtendedFillStyle, PenStyle, PenType } from "src/PenTypes";
import { ExtendedFillStyle, PenType } from "src/PenTypes";
import { getExcalidrawViews } from "src/utils/ObsidianUtils";
import { PENS } from "src/utils/Pens";
import { fragWithHTML, getExportPadding, getExportTheme, getPNGScale, getWithBackground } from "src/utils/Utils";
import { fragWithHTML } from "src/utils/Utils";
import { __values } from "tslib";
const EASINGFUNCTIONS: Record<string,string> = {
@@ -65,9 +66,7 @@ export class PenSettingsModal extends Modal {
async onClose() {
if(this.dirty) {
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
if (v.view instanceof ExcalidrawView) v.view.updatePinnedCustomPens()
})
getExcalidrawViews(this.app).forEach(excalidrawView=>excalidrawView.updatePinnedCustomPens());
this.plugin.saveSettings();
const pen = this.plugin.settings.customPens[this.pen]
const api = this.view.excalidrawAPI;

View File

@@ -22,7 +22,7 @@ export class ScriptInstallPrompt extends Modal {
searchBar.type = "text";
searchBar.id = "search-bar";
searchBar.placeholder = "Search...";
searchBar.style.width = "calc(100% - 120px)"; // space for the buttons and hit count
//searchBar.style.width = "calc(100% - 120px)"; // space for the buttons and hit count
const nextButton = document.createElement("button");
nextButton.textContent = "→";

View File

@@ -913,7 +913,7 @@ export const FRONTMATTER_KEYS_INFO: SuggesterInfo[] = [
after: ": 1",
},
{
field: "excalidraw-export-embed-scene",
field: "export-embed-scene",
code: null,
desc: "If this key is present it will override the default excalidraw embed and export setting.",
after: ": false",

View File

@@ -3,7 +3,7 @@ import "obsidian";
//export ExcalidrawAutomate from "./ExcalidrawAutomate";
//export {ExcalidrawAutomate} from "./ExcaildrawAutomate";
export type { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, StrokeRoundness, StrokeStyle } from "@zsviczian/excalidraw/types/excalidraw/element/types";
export type { Point } from "@zsviczian/excalidraw/types/excalidraw/types";
export type { Point } from "src/types/types";
export const getEA = (view?:any): any => {
try {
return window.ExcalidrawAutomate.getAPI(view);

View File

@@ -395,6 +395,14 @@ FILENAME_HEAD: "Filename",
ZOOM_TO_FIT_MAX_LEVEL_NAME: "Zoom to fit max ZOOM level",
ZOOM_TO_FIT_MAX_LEVEL_DESC:
"Set the maximum level to which zoom to fit will enlarge the drawing. Minimum is 0.5 (50%) and maximum is 10 (1000%).",
GRID_HEAD: "Grid",
GRID_DYNAMIC_COLOR_NAME: "Dynamic grid color",
GRID_DYNAMIC_COLOR_DESC:
"<b><u>Toggle ON:</u></b>Change grid color to match the canvas color<br><b><u>Toggle OFF:</u></b>Use the color below as the grid color",
GRID_COLOR_NAME: "Grid color",
GRID_OPACITY_NAME: "Grid opacity",
GRID_OPACITY_DESC: "Grid opacity will also control the opacity of the binding box when binding an arrow to an element.<br>" +
"Set the opacity of the grid. 0 is transparent, 1 is opaque.",
LASER_HEAD: "Laser pointer",
LASER_COLOR: "Laser pointer color",
LASER_DECAY_TIME_NAME: "Laser pointer decay time",
@@ -653,7 +661,7 @@ FILENAME_HEAD: "Filename",
"Use this setting if for good reasons you have decided to ignore my recommendation and configured linting of Excalidraw files.<br> " +
"The <code>## Text Elements</code> section is sensitive to empty lines. A common linting approach is to add an empty line after section headings. In case of Excalidraw this will break/change the first text element in your drawing. " +
"To overcome this, you can enable this setting. When enabled, Excalidraw will add a dummy element to the beginning of <code>## Text Elements</code> that the linter can safely modify." ,
PRESERVE_TEXT_AFTER_DRAWING_NAME: "Zotero compatibility",
PRESERVE_TEXT_AFTER_DRAWING_NAME: "Zotero and Footnotes compatibility",
PRESERVE_TEXT_AFTER_DRAWING_DESC: "Preserve text after the ## Drawing section of the markdown file. This may have a very slight performance impact when saving very large drawings.",
DEBUGMODE_NAME: "Enable debug messages",
DEBUGMODE_DESC: "I recommend restarting Obsidian after enabling/disabling this setting. This enable debug messages in the console. This is useful for troubleshooting issues. " +

View File

@@ -653,7 +653,7 @@ FILENAME_HEAD: "文件名",
"如果出于某些合理的原因,您决定忽略我的建议并配置了 Excalidraw 文件的自动代码格式化,那么可以使用这个设置<br> " +
"<code>## Text Elements</code> 部分对空行很敏感。一种常见的代码格式化是在章节标题后添加一个空行。但对于 Excalidraw 来说,这将破坏/改变您绘图中的第一个文本元素。" +
"为了解决这个问题,您可以启用这个设置。启用后 Excalidraw 将在 <code>## Text Elements</code> 的开头添加一个虚拟元素,供自动代码格式化工具修改。" ,
PRESERVE_TEXT_AFTER_DRAWING_NAME: "Zotero 兼容性",
PRESERVE_TEXT_AFTER_DRAWING_NAME: "Zotero 和脚注footnotes兼容性",
PRESERVE_TEXT_AFTER_DRAWING_DESC: "保留 Markdown 文件中 <code>## Drawing</code> 部分之后的文本内容。保存非常大的绘图时,这可能会造成微小的性能影响。",
DEBUGMODE_NAME: "开启 debug 信息",
DEBUGMODE_DESC: "我建议在启用/禁用此设置后重新启动 Obsidian。这将在控制台中启用调试消息。这对于排查问题很有帮助。" +

View File

@@ -101,7 +101,7 @@ import {
versionUpdateCheckTimer,
getFontMetrics,
} from "./utils/Utils";
import { editorInsertText, extractSVGPNGFileName, foldExcalidrawSection, getActivePDFPageNumberFromPDFView, getAttachmentsFolderAndFilePath, getNewOrAdjacentLeaf, getParentOfClass, isObsidianThemeDark, mergeMarkdownFiles, openLeaf, setExcalidrawView } from "./utils/ObsidianUtils";
import { editorInsertText, extractSVGPNGFileName, foldExcalidrawSection, getActivePDFPageNumberFromPDFView, getAttachmentsFolderAndFilePath, getExcalidrawViews, getNewOrAdjacentLeaf, getParentOfClass, isObsidianThemeDark, mergeMarkdownFiles, openLeaf, setExcalidrawView } from "./utils/ObsidianUtils";
import { ExcalidrawElement, ExcalidrawEmbeddableElement, ExcalidrawImageElement, ExcalidrawTextElement, FileId } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { ScriptEngine } from "./Scripts";
import {
@@ -758,9 +758,8 @@ export default class ExcalidrawPlugin extends Plugin {
setTimeout(()=>{ //run async to avoid blocking the UI
const theme = isObsidianThemeDark() ? "dark" : "light";
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
leaves.forEach((leaf: WorkspaceLeaf) => {
const excalidrawView = leaf.view as ExcalidrawView;
const excalidrawViews = getExcalidrawViews(this.app);
excalidrawViews.forEach(excalidrawView => {
if (excalidrawView.file && excalidrawView.excalidrawAPI) {
excalidrawView.setTheme(theme);
}
@@ -1495,7 +1494,6 @@ export default class ExcalidrawPlugin extends Plugin {
this.addCommand({
id: "toggle-lock",
hotkeys: [{ modifiers: ["Ctrl" || "Meta", "Shift"], key: "e" }],
name: t("TOGGLE_LOCK"),
checkCallback: (checking: boolean) => {
if (checking) {
@@ -1585,7 +1583,7 @@ export default class ExcalidrawPlugin extends Plugin {
this.addCommand({
id: "insert-link",
hotkeys: [{ modifiers: ["Ctrl" || "Meta", "Shift"], key: "k" }],
hotkeys: [{ modifiers: ["Mod", "Shift"], key: "k" }],
name: t("INSERT_LINK"),
checkCallback: (checking: boolean) => {
if (checking) {
@@ -1618,7 +1616,6 @@ export default class ExcalidrawPlugin extends Plugin {
this.addCommand({
id: "insert-link-to-element",
hotkeys: [{ modifiers: ["Ctrl" || "Meta", "Shift"], key: "k" }],
name: t("INSERT_LINK_TO_ELEMENT_NORMAL"),
checkCallback: (checking: boolean) => {
if (checking) {
@@ -2851,14 +2848,7 @@ export default class ExcalidrawPlugin extends Plugin {
// 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;
});
const handler_ctrlF = scope.register(["Mod"], "f", () => true);
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
const overridSaveShortcut = (
this.forceSaveCommand &&
@@ -2958,9 +2948,8 @@ export default class ExcalidrawPlugin extends Plugin {
const modifyEventHandler = async (file: TFile) => {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(modifyEventHandler,`ExcalidrawPlugin.modifyEventHandler`, file);
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
leaves.forEach(async (leaf: WorkspaceLeaf) => {
const excalidrawView = leaf.view as ExcalidrawView;
const excalidrawViews = getExcalidrawViews(this.app);
excalidrawViews.forEach(async (excalidrawView) => {
if(excalidrawView.semaphores?.viewunload) {
return;
}
@@ -3020,10 +3009,10 @@ export default class ExcalidrawPlugin extends Plugin {
}
//close excalidraw view where this file is open
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
for (let i = 0; i < leaves.length; i++) {
if ((leaves[i].view as ExcalidrawView).file.path == file.path) {
await leaves[i].setViewState({
const excalidrawViews = getExcalidrawViews(this.app);
for (const excalidrawView of excalidrawViews) {
if (excalidrawView.file.path === file.path) {
await excalidrawView.leaf.setViewState({
type: VIEW_TYPE_EXCALIDRAW,
state: { file: null },
});
@@ -3219,8 +3208,8 @@ export default class ExcalidrawPlugin extends Plugin {
}
onunload() {
const excalidrawLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
excalidrawLeaves.forEach((leaf) => {
const excalidrawViews = getExcalidrawViews(this.app);
excalidrawViews.forEach(({leaf}) => {
this.setMarkdownView(leaf);
});

View File

@@ -2,7 +2,7 @@ import { AppState, ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/e
import clsx from "clsx";
import { TFile } from "obsidian";
import * as React from "react";
import { DEVICE, VIEW_TYPE_EXCALIDRAW } from "src/constants/constants";
import { DEVICE } from "src/constants/constants";
import { PenSettingsModal } from "src/dialogs/PenSettingsModal";
import ExcalidrawView from "src/ExcalidrawView";
import { PenStyle } from "src/PenTypes";
@@ -11,8 +11,7 @@ import ExcalidrawPlugin from "../main";
import { ICONS, penIcon, stringToSVG } from "./ActionIcons";
import { UniversalInsertFileModal } from "src/dialogs/UniversalInsertFileModal";
import { t } from "src/lang/helpers";
declare const PLUGIN_VERSION:string;
import { getExcalidrawViews } from "src/utils/ObsidianUtils";
export function setPen (pen: PenStyle, api: any) {
const st = api.getAppState();
@@ -134,10 +133,8 @@ export class ObsidianMenu {
this.view.excalidrawAPI?.setToast({message:`Pin removed: ${name}`, duration: 3000, closable: true});
}
await this.plugin.saveSettings();
this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
if (v.view instanceof ExcalidrawView) v.view.updatePinnedScripts()
})
})()
getExcalidrawViews(this.plugin.app).forEach(excalidrawView=>excalidrawView.updatePinnedScripts());
})()
},
1500
)

View File

@@ -3,7 +3,7 @@ import { Notice, TFile } from "obsidian";
import * as React from "react";
import { ActionButton } from "./ActionButton";
import { ICONS, saveIcon, stringToSVG } from "./ActionIcons";
import { DEVICE, SCRIPT_INSTALL_FOLDER, VIEW_TYPE_EXCALIDRAW } from "../constants/constants";
import { DEVICE, SCRIPT_INSTALL_FOLDER } from "../constants/constants";
import { insertLaTeXToView, search } from "../ExcalidrawAutomate";
import ExcalidrawView, { TextMode } from "../ExcalidrawView";
import { t } from "../lang/helpers";
@@ -17,10 +17,10 @@ import { ExportDialog } from "src/dialogs/ExportDialog";
import { openExternalLink } from "src/utils/ExcalidrawViewUtils";
import { UniversalInsertFileModal } from "src/dialogs/UniversalInsertFileModal";
import { DEBUGGING, debug } from "src/utils/DebugHelper";
import { REM_VALUE } from "src/utils/StylesManager";
import { getExcalidrawViews } from "src/utils/ObsidianUtils";
declare const PLUGIN_VERSION:string;
const dark = '<svg style="stroke:#ced4da;#212529;color:#ced4da;fill:#ced4da" ';
const light = '<svg style="stroke:#212529;color:#212529;fill:#212529" ';
type PanelProps = {
visible: boolean;
@@ -42,7 +42,7 @@ export type PanelState = {
scriptIconMap: ScriptIconMap | null;
};
const TOOLS_PANEL_WIDTH = 228;
const TOOLS_PANEL_WIDTH = () => REM_VALUE * 14.4;
export class ToolsPanel extends React.Component<PanelProps, PanelState> {
pos1: number = 0;
@@ -155,11 +155,11 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
return {
left:
(this.containerRef.current.clientWidth -
TOOLS_PANEL_WIDTH -
(isMobileOrZen ? 0 : TOOLS_PANEL_WIDTH + 4)) /
TOOLS_PANEL_WIDTH() -
(isMobileOrZen ? 0 : TOOLS_PANEL_WIDTH() + 4)) /
2 +
this.containerRef.current.parentElement.offsetLeft +
(isMobileOrZen ? 0 : TOOLS_PANEL_WIDTH + 4),
(isMobileOrZen ? 0 : TOOLS_PANEL_WIDTH() + 4),
top: 64 + this.containerRef.current.parentElement.offsetTop,
};
});
@@ -366,11 +366,11 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
async actionRunScript(key: string) {
const view = this.view;
const plugin = view.plugin;
const f = app.vault.getAbstractFileByPath(key);
const f = plugin.app.vault.getAbstractFileByPath(key);
if (f && f instanceof TFile) {
plugin.scriptEngine.executeScript(
view,
await app.vault.read(f),
await plugin.app.vault.read(f),
plugin.scriptEngine.getScriptName(f),
f
);
@@ -391,9 +391,7 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
api?.setToast({message:`Pinned: ${scriptName}`, duration: 3000, closable: true})
}
await plugin.saveSettings();
plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
if (v.view instanceof ExcalidrawView) v.view.updatePinnedScripts()
})
getExcalidrawViews(plugin.app).forEach(excalidrawView=>excalidrawView.updatePinnedScripts());
}
private islandOnClick(event: React.MouseEvent<HTMLDivElement, MouseEvent>) {
@@ -453,7 +451,7 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
style={{
top: `${this.state.top}px`,
left: `${this.state.left}px`,
width: `13.75rem`,
width: `14.4rem`,
display:
this.state.visible && !this.state.excalidrawViewMode
? "block"
@@ -492,7 +490,7 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
maxHeight: "350px",
width: "initial",
//@ts-ignore
"--padding": 2,
"--padding": "0.125rem",
display: this.state.minimized ? "none" : "block",
}}
>

View File

@@ -10,12 +10,11 @@ import {
TextComponent,
TFile,
} from "obsidian";
import { GITHUB_RELEASES, VIEW_TYPE_EXCALIDRAW } from "./constants/constants";
import ExcalidrawView from "./ExcalidrawView";
import { GITHUB_RELEASES } from "./constants/constants";
import { t } from "./lang/helpers";
import type ExcalidrawPlugin from "./main";
import { PenStyle } from "./PenTypes";
import { DynamicStyle } from "./types/types";
import { DynamicStyle, GridSettings } from "./types/types";
import { PreviewImageType } from "./utils/UtilTypes";
import { setDynamicStyle } from "./utils/DynamicStyling";
import {
@@ -41,6 +40,7 @@ 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";
import { getExcalidrawViews } from "./utils/ObsidianUtils";
export interface ExcalidrawSettings {
folder: string;
@@ -179,6 +179,7 @@ export interface ExcalidrawSettings {
pdfNumRows: number;
pdfDirection: "down" | "right";
pdfImportScale: number;
gridSettings: GridSettings;
laserSettings: {
DECAY_TIME: number,
DECAY_LENGTH: number,
@@ -355,6 +356,11 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
pdfNumRows: 1,
pdfDirection: "right",
pdfImportScale: 0.3,
gridSettings: {
DYNAMIC_COLOR: true,
COLOR: "#000000",
OPACITY: 50,
},
laserSettings: {
DECAY_LENGTH: 50,
DECAY_TIME: 1000,
@@ -509,31 +515,25 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}
this.plugin.saveSettings();
if (this.requestUpdatePinnedPens) {
this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
if (v.view instanceof ExcalidrawView) v.view.updatePinnedCustomPens()
})
getExcalidrawViews(this.app).forEach(excalidrawView =>
excalidrawView.updatePinnedCustomPens()
)
}
if (this.requestUpdateDynamicStyling) {
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);
}
})
getExcalidrawViews(this.app).forEach(excalidrawView =>
setDynamicStyle(this.plugin.ea,excalidrawView,excalidrawView.previousBackgroundColor,this.plugin.settings.dynamicStyling)
)
}
this.hotkeyEditor.unload();
if (this.hotkeyEditor.isDirty) {
this.plugin.registerHotkeyOverrides();
}
if (this.requestReloadDrawings) {
const exs =
this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
for (const v of exs) {
if (v.view instanceof ExcalidrawView) {
await v.view.save(false);
//debug({where:"ExcalidrawSettings.hide",file:v.view.file.name,before:"reload(true)"})
await v.view.reload(true);
}
const excalidrawViews = getExcalidrawViews(this.app);
for (const excalidrawView of excalidrawViews) {
await excalidrawView.save(false);
//debug({where:"ExcalidrawSettings.hide",file:v.view.file.name,before:"reload(true)"})
await excalidrawView.reload(true);
}
this.requestEmbedUpdate = true;
}
@@ -1241,9 +1241,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.allowPinchZoom)
.onChange(async (value) => {
this.plugin.settings.allowPinchZoom = value;
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
if (v.view instanceof ExcalidrawView) v.view.updatePinchZoom()
})
getExcalidrawViews(this.app).forEach(excalidrawView=>excalidrawView.updatePinchZoom())
this.applySettingsUpdate();
}),
);
@@ -1257,9 +1255,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.allowWheelZoom)
.onChange(async (value) => {
this.plugin.settings.allowWheelZoom = value;
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).forEach(v=> {
if (v.view instanceof ExcalidrawView) v.view.updateWheelZoom()
})
getExcalidrawViews(this.app).forEach(excalidrawView=>excalidrawView.updateWheelZoom());
this.applySettingsUpdate();
}),
);
@@ -1310,7 +1306,79 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
el.innerText = ` ${this.plugin.settings.zoomToFitMaxLevel.toString()}`;
});
// ------------------------------------------------
// Grid
// ------------------------------------------------
detailsEl = displayDetailsEl.createEl("details");
detailsEl.createEl("summary", {
text: t("GRID_HEAD"),
cls: "excalidraw-setting-h3",
});
const updateGridColor = () => {
getExcalidrawViews(this.app).forEach(excalidrawView=>excalidrawView.updateGridColor());
};
// Dynamic color toggle
let gridColorSection: HTMLDivElement;
new Setting(detailsEl)
.setName(t("GRID_DYNAMIC_COLOR_NAME"))
.setDesc(fragWithHTML(t("GRID_DYNAMIC_COLOR_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.gridSettings.DYNAMIC_COLOR)
.onChange(async (value) => {
this.plugin.settings.gridSettings.DYNAMIC_COLOR = value;
gridColorSection.style.display = value ? "none" : "block";
this.applySettingsUpdate();
updateGridColor();
}),
);
// Create a div to contain color and opacity settings
gridColorSection = detailsEl.createDiv();
gridColorSection.style.display = this.plugin.settings.gridSettings.DYNAMIC_COLOR ? "none" : "block";
// Grid color picker
new Setting(gridColorSection)
.setName(t("GRID_COLOR_NAME"))
.addColorPicker((colorPicker) =>
colorPicker
.setValue(this.plugin.settings.gridSettings.COLOR)
.onChange(async (value) => {
this.plugin.settings.gridSettings.COLOR = value;
this.applySettingsUpdate();
updateGridColor();
}),
);
// Grid opacity slider (hex value between 00 and FF)
let opacityValue: HTMLDivElement;
new Setting(detailsEl)
.setName(t("GRID_OPACITY_NAME"))
.setDesc(fragWithHTML(t("GRID_OPACITY_DESC")))
.addSlider((slider) =>
slider
.setLimits(0, 100, 1) // 0 to 100 in decimal
.setValue(this.plugin.settings.gridSettings.OPACITY)
.onChange(async (value) => {
opacityValue.innerText = ` ${value.toString()}`;
this.plugin.settings.gridSettings.OPACITY = value;
this.applySettingsUpdate();
updateGridColor();
}),
)
.settingEl.createDiv("", (el) => {
opacityValue = el;
el.style.minWidth = "3em";
el.style.textAlign = "right";
el.innerText = ` ${this.plugin.settings.gridSettings.OPACITY}`;
});
// ------------------------------------------------
// Laser Pointer
// ------------------------------------------------
detailsEl = displayDetailsEl.createEl("details");
detailsEl.createEl("summary", {
text: t("LASER_HEAD"),

View File

@@ -13,6 +13,12 @@ export type ValueOf<T> = T[keyof T];
export type DynamicStyle = "none" | "gray" | "colorful";
export type GridSettings = {
DYNAMIC_COLOR: boolean; // Whether the grid color is dynamic
COLOR: string; // The grid color (in hex format)
OPACITY: number; // The grid opacity (hex value between "00" and "FF")
};
export type DeviceType = {
isDesktop: boolean,
isPhone: boolean,
@@ -25,6 +31,8 @@ export type DeviceType = {
isAndroid: boolean,
};
export type Point = [number, number];
declare global {
interface Window {
ExcalidrawAutomate: ExcalidrawAutomate;

View File

@@ -4,6 +4,7 @@ import { getEA } from "src";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
import { getCropFileNameAndFolder, getListOfTemplateFiles, splitFolderAndFilename } from "./FileUtils";
import { Notice, TFile } from "obsidian";
import { Radians } from "@zsviczian/excalidraw/types/math";
export const CROPPED_PREFIX = "cropped_";
export const ANNOTATED_PREFIX = "annotated_";
@@ -30,7 +31,7 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E
const scale = newImage.scale;
const angle = newImage.angle;
newImage.scale = [1,1];
newImage.angle = 0;
newImage.angle = 0 as Radians;
const ef = sourceEA.targetView.excalidrawData.getFile(viewImageEl.fileId);
let imageLink = "";

View File

@@ -82,6 +82,7 @@ export const setDynamicStyle = (
[`--color-surface-high`]: str(gray1().lighterBy(step)),
[`--color-on-primary-container`]: str(!isDark?accent().darkerBy(15):accent().lighterBy(15)),
[`--color-surface-primary-container`]: str(isDark?accent().darkerBy(step):accent().lighterBy(step)),
[`--bold-color`]: str(!isDark?accent().darkerBy(15):accent().lighterBy(15)),
//[`--color-primary-darker`]: str(accent().darkerBy(step)),
//[`--color-primary-darkest`]: str(accent().darkerBy(step)),
[`--button-gray-1`]: str(gray1()),
@@ -96,6 +97,7 @@ export const setDynamicStyle = (
[`--overlay-bg-color`]: gray2().alphaTo(0.6).stringHEX(),
[`--popup-bg-color`]: str(gray1()),
[`--color-on-surface`]: str(text),
[`--default-border-color`]: str(text),
//[`--color-gray-100`]: str(text),
[`--color-gray-40`]: str(text), //frame
[`--color-gray-50`]: str(text), //frame
@@ -117,6 +119,8 @@ export const setDynamicStyle = (
['--excalidraw-caret-color']: str(isLightTheme ? text : cmBG()),
[`--select-highlight-color`]: str(gray1()),
[`--color-gray-80`]: str(isDark?text.darkerBy(40):text.lighterBy(40)), //frame
[`--color-gray-90`]: str(isDark?text.darkerBy(5):text.lighterBy(5)), //search background
[`--default-bg-color`]: str(text), //search background,
};
const styleString = Object.keys(styleObject)

View File

@@ -129,8 +129,9 @@ export function openExternalLink (link:string, app: App, element?: ExcalidrawEle
* the link to the file path. By default as a wiki link, or as a file path if returnWikiLink is false.
*/
export function parseObsidianLink(link: string, app: App, returnWikiLink: boolean = true): boolean | string {
if(!link) return false;
link = getLinkFromMarkdownLink(link);
if (!link.startsWith("obsidian://")) {
if (!link?.startsWith("obsidian://")) {
return false;
}
const url = new URL(link);
@@ -394,4 +395,4 @@ export function isTextImageTransclusion (
}
}
return false;
}
}

View File

@@ -9,8 +9,9 @@ import ExcalidrawPlugin from "../main";
import { checkAndCreateFolder, splitFolderAndFilename } from "./FileUtils";
import { linkClickModifierType, ModifierKeys } from "./ModifierkeyHelper";
import { REG_BLOCK_REF_CLEAN, REG_SECTION_REF_CLEAN, VIEW_TYPE_EXCALIDRAW } from "src/constants/constants";
import yaml, { Mark } from "js-yaml";
import yaml from "js-yaml";
import { debug, DEBUGGING } from "./DebugHelper";
import ExcalidrawView from "src/ExcalidrawView";
export const getParentOfClass = (element: Element, cssClass: string):HTMLElement | null => {
let parent = element.parentElement;
@@ -24,6 +25,11 @@ export const getParentOfClass = (element: Element, cssClass: string):HTMLElement
return parent?.classList?.contains(cssClass) ? parent : null;
};
export function getExcalidrawViews(app: App): ExcalidrawView[] {
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW).filter(l=>l.view instanceof ExcalidrawView);
return leaves.map(l=>l.view as ExcalidrawView);
}
export const getLeaf = (
plugin: ExcalidrawPlugin,
origo: WorkspaceLeaf,

View File

@@ -3,6 +3,8 @@ import ExcalidrawPlugin from "src/main";
import { getAllWindowDocuments } from "./ObsidianUtils";
import { DEBUGGING, debug } from "./DebugHelper";
export let REM_VALUE = 16;
const STYLE_VARIABLES = [
"--background-modifier-cover",
"--background-primary-alt",
@@ -77,6 +79,11 @@ export class StylesManager {
}
private async harvestStyles() {
REM_VALUE = parseInt(window.getComputedStyle(document.body).getPropertyValue('--font-text-size').trim());
if (isNaN(REM_VALUE)) {
REM_VALUE = 16;
}
const body = document.body;
const iframe:HTMLIFrameElement = document.createElement("iframe");
iframe.style.display = "none";

View File

@@ -226,7 +226,7 @@ li[data-testid] {
}
.excalidraw .ToolIcon__icon img{
height: 1em;
height: 1rem;
}
.excalidraw-scriptengine-install tbody>tr>td>div>img {
@@ -394,23 +394,28 @@ div.excalidraw-draginfo {
.modal-content.excalidraw-scriptengine-install .search-bar-wrapper {
position: sticky;
top: 1em;
margin-right: 1em;
top: 1rem;
margin-right: 1rem;
display: flex;
align-items: center;
gap: 5px;
flex-wrap: nowrap;
z-index: 10;
background: var(--background-secondary);
padding: 0.5em;
padding: 0.5rem;
border-bottom: 1px solid var(--background-modifier-border);
float: right;
max-width: 28em;
max-width: 28rem;
}
div.search-bar-wrapper input {
margin-right: -0.5rem;
}
.modal-content.excalidraw-scriptengine-install .hit-count {
margin-left: 0.5em;
white-space: nowrap;
font-size: smaller;
}
.modal-content.excalidraw-scriptengine-install .active-highlight {
@@ -629,4 +634,8 @@ textarea.excalidraw-wysiwyg, .excalidraw input {
.excalidraw .color-picker-content input[type="color"] {
filter: var(--theme-filter);
}
.ExcTextField__input input::placeholder {
color: var(--select-highlight-color);
}