mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea4a0c91e8 | ||
|
|
34af6dd447 | ||
|
|
ed2e700946 | ||
|
|
7eb23ab5e1 | ||
|
|
7cccf1d4e2 | ||
|
|
2a5545964c | ||
|
|
4ce22883cc | ||
|
|
272804afc8 | ||
|
|
dc0b50f717 | ||
|
|
a0eb625b8a | ||
|
|
524dc54d03 | ||
|
|
918718be90 | ||
|
|
78ee784be1 | ||
|
|
7e0e016bf9 | ||
|
|
4f875a03a0 | ||
|
|
63c56e0e98 | ||
|
|
46477208be | ||
|
|
3194c014c7 | ||
|
|
25ccb9dc43 | ||
|
|
fa46f8c39d | ||
|
|
8ffe5c3942 | ||
|
|
88f256cd8f | ||
|
|
1562600cd3 | ||
|
|
d759abbc47 | ||
|
|
90533138e5 | ||
|
|
80d8f0e5b6 | ||
|
|
9829fab97c | ||
|
|
a33c8b6eab | ||
|
|
f0921856c1 | ||
|
|
31e06ac0e0 | ||
|
|
033d764b1c | ||
|
|
00f98dd14e | ||
|
|
0f601d969a | ||
|
|
8fa0fb37b2 | ||
|
|
5a58d17d99 | ||
|
|
982958a4c6 | ||
|
|
d425884bb8 | ||
|
|
d3b61a0df1 | ||
|
|
4bab0162ba | ||
|
|
d3f4437478 | ||
|
|
a64586c3e6 | ||
|
|
7a92e78851 | ||
|
|
af0122b21a | ||
|
|
1f95f57e97 | ||
|
|
f384e95e44 | ||
|
|
a40521f07b | ||
|
|
9649b36175 | ||
|
|
6cb1394793 | ||
|
|
e5b2977c0c | ||
|
|
22d3f25dc4 | ||
|
|
d9534fcc4f | ||
|
|
fd1604c3a4 | ||
|
|
8f0f8d64df | ||
|
|
5a413ab910 | ||
|
|
d3133f055c | ||
|
|
fe05518e31 | ||
|
|
8adcb7d850 | ||
|
|
be383f2b48 | ||
|
|
682307b51d | ||
|
|
60328613ea | ||
|
|
4a2e054ac6 | ||
|
|
eebc428f1b | ||
|
|
ab8ba66eb5 | ||
|
|
97b3050270 |
9
.github/ISSUE_TEMPLATE/How-to.yml
vendored
9
.github/ISSUE_TEMPLATE/How-to.yml
vendored
@@ -12,6 +12,8 @@ body:
|
||||
Before submitting a support request, please:
|
||||
1. **Review the [documentation](https://github.com/zsviczian/obsidian-excalidraw-plugin/wiki)** – your question may already be answered.
|
||||
2. **[Search issues](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) (including closed ones)** to see if your question has already been addressed.
|
||||
3. **[Watch the Feature Walkthrough Video](https://youtu.be/P_Q6avJGoWI)**: As it infact answers 90% of the typical questions I receive
|
||||
4. **[Consult NotebookLM with your question](https://excalidraw-obsidian.online/WIKI/09+Video+Transcripts/Videos/Turn+any+YouTube+Channel+into+your+AI+Mentor+-+Obsidian+is+the+ultimate+automation+workbench+for+PKM)**
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -31,6 +33,13 @@ body:
|
||||
options:
|
||||
- label: Yes, I have reviewed the documentation and searched for related issues.
|
||||
|
||||
- type: textarea
|
||||
id: notebook_lm
|
||||
attributes:
|
||||
label: "Your NotebookLM query"
|
||||
description: "See point 4) above. Paste the question and answer you received from NotebookLM. This serves partly as proof, partly to help me see where the model is incorrect"
|
||||
placeholder: "Copy/Paste your question and the resulting answer you got from NotebookLM"
|
||||
|
||||
- type: textarea
|
||||
id: support_question
|
||||
attributes:
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
11
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Bug report
|
||||
description: If something is clearly broken, it’s a bug. Everything else is a feature or support request. Most reported “bugs” are actually how-to questions or feature requests.
|
||||
description: If something is clearly broken, it’s a bug. **Everything else** is a feature or support request. Most reported “bugs” are actually how-to questions or feature requests.
|
||||
title: "BUG: "
|
||||
body:
|
||||
- type: markdown
|
||||
@@ -12,6 +12,8 @@ body:
|
||||
Before creating a bug report, please:
|
||||
1. **Review recent [release notes](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases)** – maybe there is already an answer.
|
||||
2. **[Search issues](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) (including closed ones)** to see if there is anything similar.
|
||||
3. **[Watch the Feature Walkthrough Video](https://youtu.be/P_Q6avJGoWI)**: As it infact answers 90% of the typical questions I receive
|
||||
4. **[Consult NotebookLM with your question](https://excalidraw-obsidian.online/WIKI/09+Video+Transcripts/Videos/Turn+any+YouTube+Channel+into+your+AI+Mentor+-+Obsidian+is+the+ultimate+automation+workbench+for+PKM)**
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -46,6 +48,13 @@ body:
|
||||
description: "Run `Command Palette/Show Debug info` in Obsidian and paste the result here."
|
||||
placeholder: "Paste your Obsidian debug info here..."
|
||||
|
||||
- type: textarea
|
||||
id: notebook_lm
|
||||
attributes:
|
||||
label: "Your NotebookLM query"
|
||||
description: "See point 4) above. Paste the question and answer you received from NotebookLM. This serves partly as proof, partly to help me see where the model is incorrect"
|
||||
placeholder: "Copy/Paste your question and the resulting answer you got from NotebookLM"
|
||||
|
||||
- type: textarea
|
||||
id: bug_description
|
||||
attributes:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,7 +4,6 @@
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
package-lock.json
|
||||
|
||||
# build
|
||||
main.js
|
||||
@@ -14,6 +13,7 @@ hot-reload.bat
|
||||
data.json
|
||||
lib
|
||||
dist
|
||||
tmp
|
||||
|
||||
#VSCode
|
||||
.vscode
|
||||
|
||||
1340
MathjaxToSVG/package-lock.json
generated
Normal file
1340
MathjaxToSVG/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,12 @@
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-typescript": "^11.1.6",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"obsidian": "1.5.7-1",
|
||||
"rollup": "^2.70.1",
|
||||
"typescript": "^5.2.2",
|
||||
"rollup-plugin-terser": "^7.0.2"
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ export default {
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfig: '../tsconfig.json',
|
||||
tsconfig: 'tsconfig.json',
|
||||
}),
|
||||
commonjs(),
|
||||
nodeResolve({
|
||||
nodeResolve({
|
||||
browser: true,
|
||||
preferBuiltins: false
|
||||
}),
|
||||
@@ -32,4 +32,4 @@ export default {
|
||||
}
|
||||
})
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
26
MathjaxToSVG/tsconfig.json
Normal file
26
MathjaxToSVG/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"sourceMap": false,
|
||||
"module": "es2020",
|
||||
"target": "es2022", //min es2017 because script engine requires for async execution and min es2018 for named capture groups
|
||||
"allowJs": false,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"importHelpers": true,
|
||||
"resolveJsonModule": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"scripthost",
|
||||
"es2022",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"jsx": "react",
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx", "src/shared/Dialogs/OpenDrawing.ts",
|
||||
"src/types/types.d.ts",
|
||||
]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
The project runs with `node 18`.
|
||||
|
||||
After running `npm -i` you'll need to make two manual changes:
|
||||
|
||||
1032
docs/API/ExcalidrawAutomate.d.ts
vendored
1032
docs/API/ExcalidrawAutomate.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ import { ConnectionPoint, DeviceType } from "src/types";
|
||||
import { ColorMaster } from "colormaster";
|
||||
import { TInput } from "colormaster/types";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
import { PaneTarget } from "src/utils/ModifierkeyHelper";
|
||||
import { PaneTarget } from "src/utils/modifierkeyHelper";
|
||||
export declare class ExcalidrawAutomate {
|
||||
/**
|
||||
* Utility function that returns the Obsidian Module object.
|
||||
|
||||
5782
docs/Release-notes.md
Normal file
5782
docs/Release-notes.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,4 +22,4 @@ elements.forEach((el)=>{
|
||||
);
|
||||
ea.addToGroup([el.id,ellipseId]);
|
||||
});
|
||||
ea.addElementsToView(false,false);
|
||||
await ea.addElementsToView(false,false,true);
|
||||
|
||||
@@ -8,20 +8,76 @@ if(lines.length !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
||||
const rotate = (point, element) => {
|
||||
const [x1, y1] = point;
|
||||
const x2 = element.x + element.width/2;
|
||||
const y2 = element.y - element.height/2;
|
||||
const angle = element.angle;
|
||||
return [
|
||||
(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2,
|
||||
(x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2,
|
||||
];
|
||||
//Same line but with angle=0
|
||||
function getNormalizedLine(originalElement) {
|
||||
if(originalElement.angle === 0) return originalElement;
|
||||
|
||||
// Get absolute coordinates for all points first
|
||||
const pointRotateRads = (point, center, angle) => {
|
||||
const [x, y] = point;
|
||||
const [cx, cy] = center;
|
||||
return [
|
||||
(x - cx) * Math.cos(angle) - (y - cy) * Math.sin(angle) + cx,
|
||||
(x - cx) * Math.sin(angle) + (y - cy) * Math.cos(angle) + cy
|
||||
];
|
||||
};
|
||||
|
||||
// Get element absolute coordinates (matching Excalidraw's approach)
|
||||
const getElementAbsoluteCoords = (element) => {
|
||||
const points = element.points;
|
||||
let minX = Infinity;
|
||||
let minY = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let maxY = -Infinity;
|
||||
|
||||
for (const [x, y] of points) {
|
||||
const absX = x + element.x;
|
||||
const absY = y + element.y;
|
||||
minX = Math.min(minX, absX);
|
||||
minY = Math.min(minY, absY);
|
||||
maxX = Math.max(maxX, absX);
|
||||
maxY = Math.max(maxY, absY);
|
||||
}
|
||||
|
||||
return [minX, minY, maxX, maxY];
|
||||
};
|
||||
|
||||
// Calculate center point based on absolute coordinates
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(originalElement);
|
||||
const centerX = (x1 + x2) / 2;
|
||||
const centerY = (y1 + y2) / 2;
|
||||
|
||||
// Calculate absolute coordinates of all points
|
||||
const absolutePoints = originalElement.points.map(([x, y]) => [
|
||||
x + originalElement.x,
|
||||
y + originalElement.y
|
||||
]);
|
||||
|
||||
// Rotate all points around the center
|
||||
const rotatedPoints = absolutePoints.map(point =>
|
||||
pointRotateRads(point, [centerX, centerY], originalElement.angle)
|
||||
);
|
||||
|
||||
// Convert back to relative coordinates
|
||||
const newPoints = rotatedPoints.map(([x, y]) => [
|
||||
x - rotatedPoints[0][0],
|
||||
y - rotatedPoints[0][1]
|
||||
]);
|
||||
|
||||
const newLineId = ea.addLine(newPoints);
|
||||
|
||||
// Set the position of the new line to the first rotated point
|
||||
const newLine = ea.getElement(newLineId);
|
||||
newLine.x = rotatedPoints[0][0];
|
||||
newLine.y = rotatedPoints[0][1];
|
||||
newLine.angle = 0;
|
||||
delete ea.elementsDict[newLine.id];
|
||||
return newLine;
|
||||
}
|
||||
|
||||
const points = lines.map(
|
||||
el=>el.points.map(p=>rotate([p[0]+el.x, p[1]+el.y],el))
|
||||
|
||||
const points = lines.map(getNormalizedLine).map(
|
||||
el=>el.points.map(p=>[p[0]+el.x, p[1]+el.y])
|
||||
);
|
||||
|
||||
const last = (p) => p[p.length-1];
|
||||
@@ -99,4 +155,4 @@ switch (lineTypes) {
|
||||
}
|
||||
|
||||
|
||||
ea.addElementsToView();
|
||||
await ea.addElementsToView();
|
||||
@@ -9,7 +9,7 @@ Select some elements in the scene. The script will take these elements and move
|
||||
|
||||
```javascript
|
||||
*/
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.0.25")) {
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.7.3")) {
|
||||
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
|
||||
return;
|
||||
}
|
||||
@@ -79,15 +79,19 @@ ea.copyViewElementsToEAforEditing(els);
|
||||
ea.getElements().filter(el=>el.type==="image").forEach(el=>{
|
||||
const img = ea.targetView.excalidrawData.getFile(el.fileId);
|
||||
const path = (img?.linkParts?.original)??(img?.file?.path);
|
||||
if(img && path) {
|
||||
const hyperlink = img?.hyperlink;
|
||||
if(img && (path || hyperlink)) {
|
||||
const colorMap = ea.getColorMapForImageElement(el);
|
||||
ea.imagesDict[el.fileId] = {
|
||||
mimeType: img.mimeType,
|
||||
id: el.fileId,
|
||||
dataURL: img.img,
|
||||
created: img.mtime,
|
||||
file: path,
|
||||
hyperlink,
|
||||
hasSVGwithBitmap: img.isSVGwithBitmap,
|
||||
latex: null,
|
||||
colorMap,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
157
ea-scripts/Full-Year Calendar Generator.md
Normal file
157
ea-scripts/Full-Year Calendar Generator.md
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
|
||||
This script generates a complete calendar for a specified year, visually distinguishing weekends from weekdays through color coding.
|
||||
|
||||

|
||||
|
||||
## Customizable Colors
|
||||
|
||||
You can personalize the calendar’s appearance by defining your own colors:
|
||||
|
||||
1. Create two rectangles in your design.
|
||||
2. Select both rectangles before running the script:
|
||||
• The **fill and stroke colors of the first rectangle** will be applied to weekdays.
|
||||
• The **fill and stroke colors of the second rectangle** will be used for weekends.
|
||||
|
||||
If no rectangle are selected, the default color schema will be used (white and purple).
|
||||
|
||||

|
||||
|
||||
```javascript
|
||||
*/
|
||||
|
||||
ea.reset();
|
||||
|
||||
// -------------------------------------
|
||||
// Constants initiation
|
||||
// -------------------------------------
|
||||
|
||||
const RECT_WIDTH = 300; // day width
|
||||
const RECT_HEIGHT = 45; // day height
|
||||
const START_X = 0; // X start position
|
||||
const START_Y = 0; // PY start position
|
||||
const MONTH_SPACING = 30; // space between months
|
||||
const DAY_SPACING = 0; // space between days
|
||||
const DAY_NAME_SPACING = 45; // space between day number and day letters
|
||||
const DAY_NAME_AND_NUMBER_X_MARGIN = 5;
|
||||
const MONTH_NAME_SPACING = -40;
|
||||
const YEAR_X = (RECT_WIDTH + MONTH_SPACING) * 6 - 150;
|
||||
const YEAR_Y = -200;
|
||||
|
||||
let COLOR_WEEKEND = "#c3abf3";
|
||||
let COLOR_WEEKDAY = "#ffffff";
|
||||
const COLOR_DAY_STROKE = "none";
|
||||
let STROKE_DAY = 4;
|
||||
let FILLSTYLE_DAY = "solid";
|
||||
|
||||
const FONT_SIZE_MONTH = 60;
|
||||
const FONT_SIZE_DAY = 30;
|
||||
const FONT_SIZE_YEAR = 100;
|
||||
|
||||
const LINE_STROKE_SIZE = 4;
|
||||
let LINE_STROKE_COLOR_WEEKDAY = "black";
|
||||
let LINE_STROKE_COLOR_WEEKEND = "black";
|
||||
|
||||
const SATURDAY = 6;
|
||||
const SUNDAY = 0;
|
||||
const JANUARY = 0;
|
||||
const FIRST_DAY_OF_THE_MONTH = 1;
|
||||
|
||||
const DAY_NAME_AND_NUMBER_Y_MARGIN = (RECT_HEIGHT - FONT_SIZE_DAY) / 2;
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
// ask for requested Year
|
||||
// Default value is the current year
|
||||
let requestedYear = parseFloat(new Date().getFullYear());
|
||||
requestedYear = parseFloat(await utils.inputPrompt("Year ?", requestedYear, requestedYear));
|
||||
if(isNaN(requestedYear)) {
|
||||
new Notice("Invalid number");
|
||||
return;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
// Use selected element for the calendar style
|
||||
// -------------------------------------
|
||||
|
||||
let elements = ea.getViewSelectedElements();
|
||||
if (elements.length>=1){
|
||||
COLOR_WEEKDAY = elements[0].backgroundColor;
|
||||
FILLSTYLE_DAY = elements[0].fillStyle;
|
||||
STROKE_DAY = elements[0].strokeWidth;
|
||||
LINE_STROKE_COLOR_WEEKDAY = elements[0].strokeColor;
|
||||
|
||||
}
|
||||
if (elements.length>=2){
|
||||
COLOR_WEEKEND = elements[1].backgroundColor;
|
||||
LINE_STROKE_COLOR_WEEKEND = elements[1].strokeColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// get the first day of the current year (01/01)
|
||||
var firstDayOfYear = new Date(requestedYear, JANUARY, FIRST_DAY_OF_THE_MONTH);
|
||||
|
||||
var currentDay = firstDayOfYear
|
||||
|
||||
// write year number
|
||||
let calendarYear = firstDayOfYear.getFullYear();
|
||||
ea.style.fontSize = FONT_SIZE_YEAR;
|
||||
ea.addText(START_X + YEAR_X, START_Y + YEAR_Y, String(calendarYear));
|
||||
|
||||
|
||||
// while we do not reach the end of the year iterate on all the day of the current year
|
||||
do {
|
||||
|
||||
var curentDayOfTheMonth = currentDay.getDate();
|
||||
var currentMonth = currentDay.getMonth();
|
||||
var isWeekend = currentDay.getDay() == SATURDAY || currentDay.getDay() == SUNDAY;
|
||||
|
||||
// set background color if it's a weekend or weekday
|
||||
ea.style.backgroundColor = isWeekend ? COLOR_WEEKEND : COLOR_WEEKDAY ;
|
||||
|
||||
|
||||
ea.style.strokeColor = COLOR_DAY_STROKE;
|
||||
ea.style.fillStyle = FILLSTYLE_DAY;
|
||||
ea.style.strokeWidth = STROKE_DAY;
|
||||
|
||||
|
||||
let x = START_X + currentMonth * (RECT_WIDTH + MONTH_SPACING);
|
||||
let y = START_Y + curentDayOfTheMonth * (RECT_HEIGHT + DAY_SPACING);
|
||||
|
||||
// only one time per month
|
||||
if(curentDayOfTheMonth == FIRST_DAY_OF_THE_MONTH) {
|
||||
|
||||
// add month name
|
||||
ea.style.fontSize = FONT_SIZE_MONTH;
|
||||
ea.addText(x + DAY_NAME_AND_NUMBER_X_MARGIN, START_Y+MONTH_NAME_SPACING, currentDay.toLocaleString('default', { month: 'long' }));
|
||||
}
|
||||
|
||||
// Add day rectangle
|
||||
ea.style.fontSize = FONT_SIZE_DAY;
|
||||
ea.addRect(x, y, RECT_WIDTH, RECT_HEIGHT);
|
||||
|
||||
// set stroke color based on weekday
|
||||
ea.style.strokeColor = isWeekend ? LINE_STROKE_COLOR_WEEKEND : LINE_STROKE_COLOR_WEEKDAY;
|
||||
|
||||
// add line between days
|
||||
//ea.style.strokeColor = LINE_STROKE_COLOR_WEEKDAY;
|
||||
ea.style.strokeWidth = LINE_STROKE_SIZE;
|
||||
ea.addLine([[x,y],[x+RECT_WIDTH, y]]);
|
||||
|
||||
|
||||
// add day number
|
||||
ea.addText(x + DAY_NAME_AND_NUMBER_X_MARGIN, y + DAY_NAME_AND_NUMBER_Y_MARGIN, String(curentDayOfTheMonth));
|
||||
// add day name
|
||||
ea.addText(x + DAY_NAME_AND_NUMBER_X_MARGIN + DAY_NAME_SPACING, y + DAY_NAME_AND_NUMBER_Y_MARGIN, String(currentDay.toLocaleString('default', { weekday: 'narrow' })));
|
||||
|
||||
// go to the next day
|
||||
currentDay.setDate(currentDay.getDate() + 1);
|
||||
|
||||
} while (!(currentDay.getMonth() == JANUARY && currentDay.getDate() == FIRST_DAY_OF_THE_MONTH)) // stop if we reach the 01/01 of the next year
|
||||
|
||||
|
||||
await ea.addElementsToView(false, false, true);
|
||||
10
ea-scripts/Full-Year Calendar Generator.svg
Normal file
10
ea-scripts/Full-Year Calendar Generator.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50.097575068473816 56.100231877975375" width="50.097575068473816" height="56.100231877975375" class="excalidraw-svg">
|
||||
<!-- svg-source:excalidraw -->
|
||||
|
||||
<defs>
|
||||
<style class="style-fonts">
|
||||
@font-face { font-family: Nunito; src: url(data:font/woff2;base64,d09GMgABAAAAAAN4AA8AAAAABswAAAMeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbgQwcLgZgP1NUQVREAEQRCAqCMIIHCwoAATYCJAMQBCAFhCQHIBuUBcguChxjano4VFxM2vQ1QySkcTXuPsXzT/vRzn0z64qIV7yJJ/EmGSp5Q0EalEiqlgiVyv/L7uv1Qpy/pAfsqfk9Cx44ly4UHRVC2VW+YH51qZj/wAD1eZu/7e/zfzNtkm0LNEs8kV4W4FwgUb7BEox+1s0hcpt7B9o7UD9zWyypoLCgyLAoF3TIGFjCt/9zgmlAiWgimHRUDsvrVQ0d4PV6RpPA690oUcCLFQz/C/KW1hSwQxBdGRfjymFWuCiNpQ7DZwDDscLwyUUTpYlomhJRStJMOi7BBt/PBqzAjvfKyhfW1JZFD4AbvUueQVDCsDDk3yTfBN7BFQ3t838A/UISdgP6BED+1nvsZim+dJYbtD/zgeUIAj7ZZBGCWGbFzydiAggCGUFfAAoNyywFy6ycBsbpAlxRrmEgQGPIthJUmvOuGydAT4H3YPkMSmTbaOxSp3F7M1DceuB47Z4/v7F+08G4H9CV65T/cP2u2BPnHPNo1Njby9l9lqCzm7uyMRu86fNiz8HY2fFxdzcm+/lrt24Fx+vVjuOWhqzPHlHBaqZuP3PXoc7G406+6sZfeO7ufn05DaoJRR5e5HzVrTsP79AzLra5Dm2pRJjYVsGw41e6W67j9sQLnUPzqc0Fimnx1xZHPf0V1+aX3CiUZM329mTN07WNyR3+eb++hXAzh+fUMLjgc9WH8Z3yyaPxrSryLvf0qvAGgKB9DTasXLeHXQv+jTfLj/DT99Yu4E/arcz/tgqsErobMKpAeLRyF0C2LRAeQ88XaXVqd92A/IrQCaF7wtobVgIAumDKFlhKtwe+w49BxmkfyDLrB9lcN1numByxaYAYdVJSCoFpewlG+CpsjU9+k4kD7sNkoxSNaBN4OlktYpSEN64bjcfiEE10Ch6BVZpGaEY1oGqiArsLTWOeiiko6ZJkSZEm3HxNmjWpzFMZbutn6SSjtL1mKvApcDlMNUNDv0uTIlUGSgcOjVL8TAsNJqCNIyildAQH05hRYnAIQmWWJ1kyFl8W6cYkGYfJCxXDbbqExsAUhFky5ZIfyxKLDU8LAgAA); }
|
||||
</style>
|
||||
|
||||
</defs>
|
||||
<g stroke-linecap="round"><g transform="translate(17.077535418245503 32.086027259866626) rotate(0 -6.0775204356467825 0)"><path d="M0 0 C-2.03 0, -10.13 0, -12.16 0 M0 0 C-2.03 0, -10.13 0, -12.16 0" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(16.865850031647774 39.07185193521212) rotate(0 -6.077520435646779 0)"><path d="M0 0 C-2.03 0, -10.13 0, -12.16 0 M0 0 C-2.03 0, -10.13 0, -12.16 0" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(16.946763254178506 46.743160072731996) rotate(0 -6.0775204356467825 0)"><path d="M0 0 C-2.03 0, -10.13 0, -12.16 0 M0 0 C-2.03 0, -10.13 0, -12.16 0" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(16.680839244467208 53.831041680294504) rotate(0 -6.0775204356467825 0)"><path d="M0 0 C-2.03 0, -10.13 0, -12.16 0 M0 0 C-2.03 0, -10.13 0, -12.16 0" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(47.41441860670051 32.80666516147113) rotate(0 -6.077520435646779 0)"><path d="M0 0 C-2.03 0, -10.13 0, -12.16 0 M0 0 C-2.03 0, -10.13 0, -12.16 0" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(47.20273322010279 39.79248983681666) rotate(0 -6.077520435646779 0)"><path d="M0 0 C-2.03 0, -10.13 0, -12.16 0 M0 0 C-2.03 0, -10.13 0, -12.16 0" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask><g transform="translate(0 0) rotate(0 25.048787534236908 17.010119812063635)"><text x="0" y="25.30097820935094" font-family="Nunito, Segoe UI Emoji" font-size="25.200177499353526px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">CAL</text></g><g stroke-linecap="round"><g transform="translate(26.079917947816256 27.03803518092093) rotate(0 0 14.531098348527223)"><path d="M0 0 C0 4.84, 0 24.22, 0 29.06 M0 0 C0 4.84, 0 24.22, 0 29.06" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(47.199941306131535 47.15190785557627) rotate(0 -6.077520435646779 0)"><path d="M0 0 C-2.03 0, -10.13 0, -12.16 0 M0 0 C-2.03 0, -10.13 0, -12.16 0" stroke="#1e1e1e" stroke-width="4" fill="none"></path></g></g><mask></mask></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -7,29 +7,58 @@ This script enables the selection of elements based on matching properties. Sele
|
||||
```js */
|
||||
|
||||
let config = window.ExcalidrawSelectConfig;
|
||||
config = Boolean(config) && (Date.now() - config.timestamp < 60000) ? config : null;
|
||||
const isValidConfig = config && (Date.now() - config.timestamp < 60000);
|
||||
config = isValidConfig ? config : null;
|
||||
|
||||
let elements = ea.getViewSelectedElements();
|
||||
if(!config && (elements.length !==1)) {
|
||||
new Notice("Select a single element");
|
||||
return;
|
||||
} else {
|
||||
if(elements.length === 0) {
|
||||
elements = ea.getViewElements();
|
||||
if(!config) {
|
||||
|
||||
async function shouldAbort() {
|
||||
if(elements.length === 1) return false;
|
||||
if(elements.length !== 2) return true;
|
||||
|
||||
//maybe container?
|
||||
const textEl = elements.find(el=>el.type==="text");
|
||||
if(!textEl || !textEl.containerId) return true;
|
||||
|
||||
const containerEl = elements.find(el=>el.id === textEl.containerId);
|
||||
if(!containerEl) return true;
|
||||
|
||||
const id = await utils.suggester(
|
||||
elements.map(el=>el.type),
|
||||
elements.map(el=>el.id),
|
||||
"Select container component"
|
||||
);
|
||||
if(!id) return true;
|
||||
elements = elements.filter(el=>el.id === id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(await shouldAbort()) {
|
||||
new Notice("Select a single element");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(Boolean(config) && elements.length === 0) {
|
||||
elements = ea.getViewElements();
|
||||
}
|
||||
|
||||
const {angle, backgroundColor, fillStyle, fontFamily, fontSize, height, width, opacity, roughness, roundness, strokeColor, strokeStyle, strokeWidth, type, startArrowhead, endArrowhead, fileId} = ea.getViewSelectedElement();
|
||||
|
||||
const fragWithHTML = (html) => createFragment((frag) => (frag.createDiv().innerHTML = html));
|
||||
|
||||
function lc(x) {
|
||||
return x?.toLocaleLowerCase();
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// RUN
|
||||
//--------------------------
|
||||
const run = () => {
|
||||
selectedElements = elements.filter(el=>
|
||||
((typeof config.angle === "undefined") || (el.angle === config.angle)) &&
|
||||
((typeof config.backgroundColor === "undefined") || (el.backgroundColor === config.backgroundColor)) &&
|
||||
((typeof config.backgroundColor === "undefined") || (lc(el.backgroundColor) === lc(config.backgroundColor))) &&
|
||||
((typeof config.fillStyle === "undefined") || (el.fillStyle === config.fillStyle)) &&
|
||||
((typeof config.fontFamily === "undefined") || (el.fontFamily === config.fontFamily)) &&
|
||||
((typeof config.fontSize === "undefined") || (el.fontSize === config.fontSize)) &&
|
||||
@@ -38,7 +67,7 @@ const run = () => {
|
||||
((typeof config.opacity === "undefined") || (el.opacity === config.opacity)) &&
|
||||
((typeof config.roughness === "undefined") || (el.roughness === config.roughness)) &&
|
||||
((typeof config.roundness === "undefined") || (el.roundness === config.roundness)) &&
|
||||
((typeof config.strokeColor === "undefined") || (el.strokeColor === config.strokeColor)) &&
|
||||
((typeof config.strokeColor === "undefined") || (lc(el.strokeColor) === lc(config.strokeColor))) &&
|
||||
((typeof config.strokeStyle === "undefined") || (el.strokeStyle === config.strokeStyle)) &&
|
||||
((typeof config.strokeWidth === "undefined") || (el.strokeWidth === config.strokeWidth)) &&
|
||||
((typeof config.type === "undefined") || (el.type === config.type)) &&
|
||||
|
||||
@@ -17,4 +17,5 @@ if(isNaN(width)) {
|
||||
const elements=ea.getViewSelectedElements();
|
||||
ea.copyViewElementsToEAforEditing(elements);
|
||||
ea.getElements().forEach((el)=>el.strokeWidth=width);
|
||||
ea.addElementsToView(false,false);
|
||||
await ea.addElementsToView(false,false);
|
||||
ea.viewUpdateScene({appState: {currentItemStrokeWidth: width}});
|
||||
|
||||
729
ea-scripts/Shade Master.md
Normal file
729
ea-scripts/Shade Master.md
Normal file
@@ -0,0 +1,729 @@
|
||||
/*
|
||||
This is an experimental script. If you find bugs, please consider debugging yourself then submitting a PR on github with the fix, instead of raising an issue. Thank you!
|
||||
|
||||
This script modifies the color lightness/hue/saturation/transparency of selected Excalidraw elements and SVG and nested Excalidraw drawings. Select eligible elements in the scene, then run the script.
|
||||
|
||||
- The color of Excalidraw elements (lines, ellipses, rectangles, etc.) will be changed by the script.
|
||||
- The color of SVG elements and nested Excalidraw drawings will only be mapped. When mapping colors, the original image remains unchanged, only a mapping table is created and the image is recolored during rendering of your Excalidraw screen. In case you want to make manual changes you can also edit the mapping in Markdown View Mode under `## Embedded Files`
|
||||
|
||||
If you select only a single SVG or nested Excalidraw element, then the script offers an additional feature. You can map colors one by one in the image.
|
||||
```js*/
|
||||
|
||||
const HELP_TEXT = `
|
||||
<ul>
|
||||
<li dir="auto">Select SVG images, nested Excalidraw drawings and/or regular Excalidraw elements</li>
|
||||
<li dir="auto">For a single selected image, you can map colors individually in the color mapping section</li>
|
||||
<li dir="auto">For Excalidraw elements: stroke and background colors are modified permanently</li>
|
||||
<li dir="auto">For SVG/nested drawings: original files stay unchanged, color mapping is stored under <code>## Embedded Files</code></li>
|
||||
<li dir="auto">Using color maps helps maintain links between drawings while allowing different color themes</li>
|
||||
<li dir="auto">Sliders work on relative scale - the amount of change is applied to current values</li>
|
||||
<li dir="auto">Unlike Excalidraw's opacity setting which affects the whole element:
|
||||
<ul>
|
||||
<li dir="auto">Shade Master can set different opacity for stroke vs background</li>
|
||||
<li dir="auto">Note: SVG/nested drawing colors are mapped at color name level, thus "black" is different from "#000000"</li>
|
||||
<li dir="auto">Additionally if the same color is used as fill and stroke the color can only be mapped once</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li dir="auto">This is an experimental script - contributions welcome on GitHub via PRs</li>
|
||||
</ul>
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/ISuORbVKyhQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
`;
|
||||
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.7.2")) {
|
||||
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
SVGColorInfo is returned by ea.getSVGColorInfoForImgElement. Color info will all the color strings in the SVG file plus "fill" which represents the default fill color for SVG icons set at the SVG root element level. Fill if not set defaults to black:
|
||||
|
||||
type SVGColorInfo = Map<string, {
|
||||
mappedTo: string;
|
||||
fill: boolean;
|
||||
stroke: boolean;
|
||||
}>;
|
||||
|
||||
In the Excalidraw file under `## Embedded Files` the color map is included after the file. That color map implements ColorMap. ea.updateViewSVGImageColorMap takes a ColorMap as input.
|
||||
interface ColorMap {
|
||||
[color: string]: string;
|
||||
};
|
||||
*/
|
||||
|
||||
// Main script execution
|
||||
const allElements = ea.getViewSelectedElements();
|
||||
const svgImageElements = allElements.filter(el => {
|
||||
if(el.type !== "image") return false;
|
||||
const file = ea.getViewFileForImageElement(el);
|
||||
if(!file) return false;
|
||||
return el.type === "image" && (
|
||||
file.extension === "svg" ||
|
||||
ea.isExcalidrawFile(file)
|
||||
);
|
||||
});
|
||||
|
||||
if(allElements.length === 0) {
|
||||
new Notice("Select at least one rectangle, ellipse, diamond, line, arrow, freedraw, text or SVG image elment");
|
||||
return;
|
||||
}
|
||||
|
||||
const originalColors = new Map();
|
||||
const currentColors = new Map();
|
||||
const colorInputs = new Map();
|
||||
const sliderResetters = [];
|
||||
let terminate = false;
|
||||
const FORMAT = "Color Format";
|
||||
const STROKE = "Modify Stroke Color";
|
||||
const BACKGROUND = "Modify Background Color"
|
||||
const ACTIONS = ["Hue", "Lightness", "Saturation", "Transparency"];
|
||||
const precision = [1,2,2,3];
|
||||
const minLigtness = 1/Math.pow(10,precision[2]);
|
||||
const maxLightness = 100 - minLigtness;
|
||||
const minSaturation = 1/Math.pow(10,precision[2]);
|
||||
|
||||
let settings = ea.getScriptSettings();
|
||||
//set default values on first run
|
||||
if(!settings[STROKE]) {
|
||||
settings = {};
|
||||
settings[FORMAT] = {
|
||||
value: "HEX",
|
||||
valueset: ["HSL", "RGB", "HEX"],
|
||||
description: "Output color format."
|
||||
};
|
||||
settings[STROKE] = { value: true }
|
||||
settings[BACKGROUND] = {value: true }
|
||||
ea.setScriptSettings(settings);
|
||||
}
|
||||
|
||||
function getRegularElements() {
|
||||
ea.clear();
|
||||
//loading view elements again as element objects change when colors are updated
|
||||
const allElements = ea.getViewSelectedElements();
|
||||
return allElements.filter(el =>
|
||||
["rectangle", "ellipse", "diamond", "line", "arrow", "freedraw", "text"].includes(el.type)
|
||||
);
|
||||
}
|
||||
|
||||
const updatedImageElementColorMaps = new Map();
|
||||
let isWaitingForSVGUpdate = false;
|
||||
function updateViewImageColors() {
|
||||
if(terminate || isWaitingForSVGUpdate || updatedImageElementColorMaps.size === 0) {
|
||||
return;
|
||||
}
|
||||
isWaitingForSVGUpdate = true;
|
||||
elementArray = Array.from(updatedImageElementColorMaps.keys());
|
||||
colorMapArray = Array.from(updatedImageElementColorMaps.values());
|
||||
updatedImageElementColorMaps.clear();
|
||||
ea.updateViewSVGImageColorMap(elementArray, colorMapArray).then(()=>{
|
||||
isWaitingForSVGUpdate = false;
|
||||
updateViewImageColors();
|
||||
});
|
||||
}
|
||||
|
||||
async function storeOriginalColors() {
|
||||
// Store colors for regular elements
|
||||
for (const el of getRegularElements()) {
|
||||
const key = el.id;
|
||||
const colorData = {
|
||||
type: "regular",
|
||||
strokeColor: el.strokeColor,
|
||||
backgroundColor: el.backgroundColor
|
||||
};
|
||||
originalColors.set(key, colorData);
|
||||
}
|
||||
|
||||
// Store colors for SVG elements
|
||||
for (const el of svgImageElements) {
|
||||
const colorInfo = await ea.getSVGColorInfoForImgElement(el);
|
||||
const svgColors = new Map();
|
||||
for (const [color, info] of colorInfo.entries()) {
|
||||
svgColors.set(color, {...info});
|
||||
}
|
||||
|
||||
originalColors.set(el.id, {type: "svg",colors: svgColors});
|
||||
}
|
||||
copyOriginalsToCurrent();
|
||||
}
|
||||
|
||||
function copyOriginalsToCurrent() {
|
||||
for (const [key, value] of originalColors.entries()) {
|
||||
if(value.type === "regular") {
|
||||
currentColors.set(key, {...value});
|
||||
} else {
|
||||
const newColorMap = new Map();
|
||||
for (const [color, info] of value.colors.entries()) {
|
||||
newColorMap.set(color, {...info});
|
||||
}
|
||||
currentColors.set(key, {type: "svg", colors: newColorMap});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearSVGMapping() {
|
||||
for (const resetter of sliderResetters) {
|
||||
resetter();
|
||||
}
|
||||
// Reset SVG elements
|
||||
if (svgImageElements.length === 1) {
|
||||
const el = svgImageElements[0];
|
||||
const original = originalColors.get(el.id);
|
||||
const current = currentColors.get(el.id);
|
||||
if (original && original.type === "svg") {
|
||||
|
||||
for (const color of original.colors.keys()) {
|
||||
current.colors.get(color).mappedTo = color;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const el of svgImageElements) {
|
||||
const original = originalColors.get(el.id);
|
||||
const current = currentColors.get(el.id);
|
||||
if (original && original.type === "svg") {
|
||||
for (const color of original.colors.keys()) {
|
||||
current.colors.get(color).mappedTo = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
run("clear");
|
||||
}
|
||||
|
||||
// Set colors
|
||||
async function setColors(colors) {
|
||||
debounceColorPicker = true;
|
||||
const regularElements = getRegularElements();
|
||||
|
||||
if (regularElements.length > 0) {
|
||||
ea.copyViewElementsToEAforEditing(regularElements);
|
||||
for (const el of ea.getElements()) {
|
||||
const original = colors.get(el.id);
|
||||
if (original && original.type === "regular") {
|
||||
if (original.strokeColor) el.strokeColor = original.strokeColor;
|
||||
if (original.backgroundColor) el.backgroundColor = original.backgroundColor;
|
||||
}
|
||||
}
|
||||
await ea.addElementsToView(false, false);
|
||||
}
|
||||
|
||||
// Reset SVG elements
|
||||
if (svgImageElements.length === 1) {
|
||||
const el = svgImageElements[0];
|
||||
const original = colors.get(el.id);
|
||||
if (original && original.type === "svg") {
|
||||
const newColorMap = {};
|
||||
|
||||
for (const [color, info] of original.colors.entries()) {
|
||||
newColorMap[color] = info.mappedTo;
|
||||
// Update UI components
|
||||
const inputs = colorInputs.get(color);
|
||||
if (inputs) {
|
||||
if(info.mappedTo === "fill") {
|
||||
info.mappedTo = "black";
|
||||
//"fill" is a special value in case the SVG has no fill color defined (i.e black)
|
||||
inputs.textInput.setValue("black");
|
||||
inputs.colorPicker.setValue("#000000");
|
||||
} else {
|
||||
const cm = ea.getCM(info.mappedTo);
|
||||
inputs.textInput.setValue(info.mappedTo);
|
||||
inputs.colorPicker.setValue(cm.stringHEX({alpha: false}).toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedImageElementColorMaps.set(el, newColorMap);
|
||||
}
|
||||
} else {
|
||||
for (const el of svgImageElements) {
|
||||
const original = colors.get(el.id);
|
||||
if (original && original.type === "svg") {
|
||||
const newColorMap = {};
|
||||
|
||||
for (const [color, info] of original.colors.entries()) {
|
||||
newColorMap[color] = info.mappedTo;
|
||||
}
|
||||
updatedImageElementColorMaps.set(el, newColorMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateViewImageColors();
|
||||
}
|
||||
|
||||
function modifyColor(color, isDecrease, step, action) {
|
||||
if (!color) return null;
|
||||
|
||||
const cm = ea.getCM(color);
|
||||
if (!cm) return color;
|
||||
|
||||
let modified = cm;
|
||||
if (modified.lightness === 0) modified = modified.lightnessTo(minLigtness);
|
||||
if (modified.lightness === 100) modified = modified.lightnessTo(maxLightness);
|
||||
if (modified.saturation === 0) modified = modified.saturationTo(minSaturation);
|
||||
|
||||
switch(action) {
|
||||
case "Lightness":
|
||||
// handles edge cases where lightness is 0 or 100 would convert saturation and hue to 0
|
||||
let lightness = cm.lightness;
|
||||
const shouldRoundLight = (lightness === minLigtness || lightness === maxLightness);
|
||||
if (shouldRoundLight) lightness = Math.round(lightness);
|
||||
lightness += isDecrease ? -step : step;
|
||||
if (lightness <= 0) lightness = minLigtness;
|
||||
if (lightness >= 100) lightness = maxLightness;
|
||||
modified = modified.lightnessTo(lightness);
|
||||
break;
|
||||
case "Hue":
|
||||
modified = isDecrease ? modified.hueBy(-step) : modified.hueBy(step);
|
||||
break;
|
||||
case "Transparency":
|
||||
modified = isDecrease ? modified.alphaBy(-step) : modified.alphaBy(step);
|
||||
break;
|
||||
default:
|
||||
let saturation = cm.saturation;
|
||||
const shouldRoundSat = saturation === minSaturation;
|
||||
if (shouldRoundSat) saturation = Math.round(saturation);
|
||||
saturation += isDecrease ? -step : step;
|
||||
if (saturation <= 0) saturation = minSaturation;
|
||||
modified = modified.saturationTo(saturation);
|
||||
}
|
||||
|
||||
const hasAlpha = modified.alpha < 1;
|
||||
const opts = { alpha: hasAlpha, precision };
|
||||
|
||||
const format = settings[FORMAT].value;
|
||||
switch(format) {
|
||||
case "RGB": return modified.stringRGB(opts).toLowerCase();
|
||||
case "HEX": return modified.stringHEX(opts).toLowerCase();
|
||||
default: return modified.stringHSL(opts).toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
function slider(contentEl, action, min, max, step, invert) {
|
||||
let prevValue = (max-min)/2;
|
||||
let debounce = false;
|
||||
let sliderControl;
|
||||
new ea.obsidian.Setting(contentEl)
|
||||
.setName(action)
|
||||
.addSlider(slider => {
|
||||
sliderControl = slider;
|
||||
slider
|
||||
.setLimits(min, max, step)
|
||||
.setValue(prevValue)
|
||||
.onChange(async (value) => {
|
||||
if (debounce) return;
|
||||
const isDecrease = invert ? value > prevValue : value < prevValue;
|
||||
const step = Math.abs(value-prevValue);
|
||||
prevValue = value;
|
||||
if(step>0) {
|
||||
run(action, isDecrease, step);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
return () => {
|
||||
debounce = true;
|
||||
prevValue = (max-min)/2;
|
||||
sliderControl.setValue(prevValue);
|
||||
debounce = false;
|
||||
}
|
||||
}
|
||||
|
||||
function showModal() {
|
||||
let debounceColorPicker = true;
|
||||
const modal = new ea.obsidian.Modal(app);
|
||||
let dirty = false;
|
||||
|
||||
modal.onOpen = async () => {
|
||||
const { contentEl, modalEl } = modal;
|
||||
const { width, height } = ea.getExcalidrawAPI().getAppState();
|
||||
modal.bgOpacity = 0;
|
||||
contentEl.createEl('h2', { text: 'Shade Master' });
|
||||
|
||||
const helpDiv = contentEl.createEl("details", {
|
||||
attr: { style: "margin-bottom: 1em;background: var(--background-secondary); padding: 1em; border-radius: 4px;" }});
|
||||
helpDiv.createEl("summary", { text: "Help & Usage Guide", attr: { style: "cursor: pointer; color: var(--text-accent);" } });
|
||||
const helpDetailsDiv = helpDiv.createEl("div", {
|
||||
attr: { style: "margin-top: 0em; " }
|
||||
});
|
||||
helpDetailsDiv.innerHTML = HELP_TEXT;
|
||||
|
||||
const component = new ea.obsidian.Setting(contentEl)
|
||||
.setName(FORMAT)
|
||||
.setDesc("Output color format")
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOptions({
|
||||
"HSL": "HSL",
|
||||
"RGB": "RGB",
|
||||
"HEX": "HEX"
|
||||
})
|
||||
.setValue(settings[FORMAT].value)
|
||||
.onChange(value => {
|
||||
settings[FORMAT].value = value;
|
||||
run();
|
||||
dirty = true;
|
||||
})
|
||||
);
|
||||
|
||||
new ea.obsidian.Setting(contentEl)
|
||||
.setName(STROKE)
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(settings[STROKE].value)
|
||||
.onChange(value => {
|
||||
settings[STROKE].value = value;
|
||||
dirty = true;
|
||||
})
|
||||
);
|
||||
|
||||
new ea.obsidian.Setting(contentEl)
|
||||
.setName(BACKGROUND)
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(settings[BACKGROUND].value)
|
||||
.onChange(value => {
|
||||
settings[BACKGROUND].value = value;
|
||||
dirty = true;
|
||||
})
|
||||
);
|
||||
|
||||
// lightness and saturation are on a scale of 0%-100%
|
||||
// Hue is in degrees, 360 for the full circle
|
||||
// transparency is on a range between 0 and 1 (equivalent to 0%-100%)
|
||||
// The range for lightness, saturation and transparency are double since
|
||||
// the input could be at either end of the scale
|
||||
// The range for Hue is 360 since regarless of the position on the circle moving
|
||||
// the slider to the two extremes will travel the entire circle
|
||||
// To modify blacks and whites, lightness first needs to be changed to value between 1% and 99%
|
||||
sliderResetters.push(slider(contentEl, "Hue", 0, 360, 1, false));
|
||||
sliderResetters.push(slider(contentEl, "Saturation", 0, 200, 1, false));
|
||||
sliderResetters.push(slider(contentEl, "Lightness", 0, 200, 1, false));
|
||||
sliderResetters.push(slider(contentEl, "Transparency", 0, 2, 0.05, true));
|
||||
|
||||
// Add color pickers if a single SVG image is selected
|
||||
if (svgImageElements.length === 1) {
|
||||
const svgElement = svgImageElements[0];
|
||||
//note that the objects in currentColors might get replaced when
|
||||
//colors are reset, thus in the onChange functions I will always
|
||||
//read currentColorInfo from currentColors based on svgElement.id
|
||||
const initialColorInfo = currentColors.get(svgElement.id).colors;
|
||||
const colorSection = contentEl.createDiv();
|
||||
colorSection.createEl('h3', { text: 'SVG Colors' });
|
||||
|
||||
for (const [color, info] of initialColorInfo.entries()) {
|
||||
const row = new ea.obsidian.Setting(colorSection)
|
||||
.setName(color === "fill" ? "SVG default" : color)
|
||||
.setDesc(`${info.fill ? "Fill" : ""}${info.fill && info.stroke ? " & " : ""}${info.stroke ? "Stroke" : ""}`);
|
||||
row.descEl.style.width = "100px";
|
||||
row.nameEl.style.width = "100px";
|
||||
|
||||
// Create color preview div
|
||||
const previewDiv = row.controlEl.createDiv();
|
||||
previewDiv.style.width = "50px";
|
||||
previewDiv.style.height = "20px";
|
||||
previewDiv.style.border = "1px solid var(--background-modifier-border)";
|
||||
if (color === "transparent") {
|
||||
previewDiv.style.backgroundImage = "linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%)";
|
||||
previewDiv.style.backgroundSize = "10px 10px";
|
||||
previewDiv.style.backgroundPosition = "0 0, 0 5px, 5px -5px, -5px 0px";
|
||||
} else {
|
||||
previewDiv.style.backgroundColor = ea.getCM(color).stringHEX({alpha: false}).toLowerCase();
|
||||
}
|
||||
|
||||
const resetButton = new ea.obsidian.Setting(row.controlEl)
|
||||
.addButton(button => button
|
||||
.setButtonText(">>")
|
||||
.setClass("reset-color-button")
|
||||
.onClick(async () => {
|
||||
const original = originalColors.get(svgElement.id);
|
||||
const current = currentColors.get(svgElement.id);
|
||||
if (original?.type === "svg") {
|
||||
const originalInfo = original.colors.get(color);
|
||||
const currentInfo = current.colors.get(color);
|
||||
if (originalInfo) {
|
||||
currentInfo.mappedTo = color;
|
||||
run("reset single color");
|
||||
}
|
||||
}
|
||||
}))
|
||||
resetButton.settingEl.style.padding = "0";
|
||||
resetButton.settingEl.style.border = "0";
|
||||
|
||||
// Add text input for color value
|
||||
const textInput = new ea.obsidian.TextComponent(row.controlEl)
|
||||
.setValue(info.mappedTo)
|
||||
.setPlaceholder("Color value");
|
||||
textInput.inputEl.style.width = "100%";
|
||||
textInput.onChange(value => {
|
||||
const lower = value.toLowerCase();
|
||||
if (lower === color) return;
|
||||
textInput.setValue(lower);
|
||||
})
|
||||
|
||||
const applyButtonComponent = new ea.obsidian.Setting(row.controlEl)
|
||||
.addButton(button => button
|
||||
.setIcon("check")
|
||||
.setTooltip("Apply")
|
||||
.onClick(async () => {
|
||||
const value = textInput.getValue();
|
||||
try {
|
||||
if(!CSS.supports("color",value)) {
|
||||
new Notice (`${value} is not a valid color string`);
|
||||
return;
|
||||
}
|
||||
const cm = ea.getCM(value);
|
||||
if (cm) {
|
||||
const format = settings[FORMAT].value;
|
||||
const alpha = cm.alpha < 1 ? true : false;
|
||||
const newColor = format === "RGB"
|
||||
? cm.stringRGB({alpha , precision }).toLowerCase()
|
||||
: format === "HEX"
|
||||
? cm.stringHEX({alpha}).toLowerCase()
|
||||
: cm.stringHSL({alpha, precision }).toLowerCase();
|
||||
|
||||
textInput.setValue(newColor);
|
||||
const currentInfo = currentColors.get(svgElement.id).colors;
|
||||
currentInfo.get(color).mappedTo = newColor;
|
||||
run("Update SVG color");
|
||||
debounceColorPicker = true;
|
||||
colorPicker.setValue(cm.stringHEX({alpha: false}).toLowerCase());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Invalid color value:", e);
|
||||
}
|
||||
}));
|
||||
applyButtonComponent.settingEl.style.padding = "0";
|
||||
applyButtonComponent.settingEl.style.border = "0";
|
||||
|
||||
// Add color picker
|
||||
const colorPicker = new ea.obsidian.ColorComponent(row.controlEl)
|
||||
.setValue(ea.getCM(info.mappedTo).stringHEX({alpha: false}).toLowerCase());
|
||||
|
||||
colorPicker.colorPickerEl.style.maxWidth = "2.5rem";
|
||||
|
||||
// Store references to the components
|
||||
colorInputs.set(color, {
|
||||
textInput,
|
||||
colorPicker,
|
||||
previewDiv,
|
||||
resetButton
|
||||
});
|
||||
|
||||
colorPicker.colorPickerEl.addEventListener('click', () => {
|
||||
debounceColorPicker = false;
|
||||
});
|
||||
|
||||
colorPicker.onChange(async (value) => {
|
||||
try {
|
||||
if(!debounceColorPicker) {
|
||||
const currentInfo = currentColors.get(svgElement.id).colors.get(color);
|
||||
// Preserve alpha from original color
|
||||
const originalAlpha = ea.getCM(currentInfo.mappedTo).alpha;
|
||||
const cm = ea.getCM(value);
|
||||
cm.alphaTo(originalAlpha);
|
||||
const alpha = originalAlpha < 1 ? true : false;
|
||||
const format = settings[FORMAT].value;
|
||||
const newColor = format === "RGB"
|
||||
? cm.stringRGB({alpha, precision }).toLowerCase()
|
||||
: format === "HEX"
|
||||
? cm.stringHEX({alpha}).toLowerCase()
|
||||
: cm.stringHSL({alpha, precision }).toLowerCase();
|
||||
|
||||
// Update text input
|
||||
textInput.setValue(newColor);
|
||||
|
||||
// Update SVG
|
||||
currentInfo.mappedTo = newColor;
|
||||
run("Update SVG color");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Invalid color value:", e);
|
||||
} finally {
|
||||
debounceColorPicker = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const buttons = new ea.obsidian.Setting(contentEl);
|
||||
if(svgImageElements.length > 0) {
|
||||
buttons.addButton(button => button
|
||||
.setButtonText("Initialize SVG Colors")
|
||||
.onClick(() => {
|
||||
debounceColorPicker = true;
|
||||
clearSVGMapping();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
buttons
|
||||
.addButton(button => button
|
||||
.setButtonText("Reset")
|
||||
.onClick(() => {
|
||||
for (const resetter of sliderResetters) {
|
||||
resetter();
|
||||
}
|
||||
copyOriginalsToCurrent();
|
||||
setColors(originalColors);
|
||||
}))
|
||||
.addButton(button => button
|
||||
.setButtonText("Close")
|
||||
.setCta(true)
|
||||
.onClick(() => modal.close()));
|
||||
|
||||
makeModalDraggable(modalEl);
|
||||
|
||||
const maxHeight = Math.round(height * 0.6);
|
||||
const maxWidth = Math.round(width * 0.9);
|
||||
modalEl.style.maxHeight = `${maxHeight}px`;
|
||||
modalEl.style.maxWidth = `${maxWidth}px`;
|
||||
};
|
||||
|
||||
modal.onClose = () => {
|
||||
terminate = true;
|
||||
if (dirty) {
|
||||
ea.setScriptSettings(settings);
|
||||
}
|
||||
if(ea.targetView.isDirty()) {
|
||||
ea.targetView.save(false);
|
||||
}
|
||||
};
|
||||
|
||||
modal.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add draggable functionality to the modal element.
|
||||
* @param {HTMLElement} modalEl - The modal element to make draggable.
|
||||
*/
|
||||
function makeModalDraggable(modalEl) {
|
||||
let isDragging = false;
|
||||
let startX, startY, initialX, initialY;
|
||||
|
||||
const header = modalEl.querySelector('.modal-titlebar') || modalEl; // Default to modalEl if no titlebar
|
||||
header.style.cursor = 'move';
|
||||
|
||||
const onPointerDown = (e) => {
|
||||
// Ensure the event target isn't an interactive element like slider, button, or input
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;
|
||||
|
||||
isDragging = true;
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
const rect = modalEl.getBoundingClientRect();
|
||||
initialX = rect.left;
|
||||
initialY = rect.top;
|
||||
|
||||
modalEl.style.position = 'absolute';
|
||||
modalEl.style.margin = '0';
|
||||
modalEl.style.left = `${initialX}px`;
|
||||
modalEl.style.top = `${initialY}px`;
|
||||
};
|
||||
|
||||
const onPointerMove = (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const dx = e.clientX - startX;
|
||||
const dy = e.clientY - startY;
|
||||
|
||||
modalEl.style.left = `${initialX + dx}px`;
|
||||
modalEl.style.top = `${initialY + dy}px`;
|
||||
};
|
||||
|
||||
const onPointerUp = () => {
|
||||
isDragging = false;
|
||||
};
|
||||
|
||||
header.addEventListener('pointerdown', onPointerDown);
|
||||
document.addEventListener('pointermove', onPointerMove);
|
||||
document.addEventListener('pointerup', onPointerUp);
|
||||
|
||||
// Clean up event listeners on modal close
|
||||
modalEl.addEventListener('remove', () => {
|
||||
header.removeEventListener('pointerdown', onPointerDown);
|
||||
document.removeEventListener('pointermove', onPointerMove);
|
||||
document.removeEventListener('pointerup', onPointerUp);
|
||||
});
|
||||
}
|
||||
|
||||
function executeChange(isDecrease, step, action) {
|
||||
const modifyStroke = settings[STROKE].value;
|
||||
const modifyBackground = settings[BACKGROUND].value;
|
||||
const regularElements = getRegularElements();
|
||||
|
||||
// Process regular elements
|
||||
if (regularElements.length > 0) {
|
||||
for (const el of regularElements) {
|
||||
const currentColor = currentColors.get(el.id);
|
||||
|
||||
if (modifyStroke && currentColor.strokeColor) {
|
||||
currentColor.strokeColor = modifyColor(el.strokeColor, isDecrease, step, action);
|
||||
}
|
||||
|
||||
if (modifyBackground && currentColor.backgroundColor) {
|
||||
currentColor.backgroundColor = modifyColor(el.backgroundColor, isDecrease, step, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process SVG image elements
|
||||
if (svgImageElements.length === 1) { // Only update UI for single SVG
|
||||
const el = svgImageElements[0];
|
||||
colorInfo = currentColors.get(el.id).colors;
|
||||
|
||||
// Process each color in the SVG
|
||||
for (const [color, info] of colorInfo.entries()) {
|
||||
let shouldModify = (modifyBackground && info.fill) || (modifyStroke && info.stroke);
|
||||
|
||||
if (shouldModify) {
|
||||
const modifiedColor = modifyColor(info.mappedTo, isDecrease, step, action);
|
||||
colorInfo.get(color).mappedTo = modifiedColor;
|
||||
// Update UI components if they exist
|
||||
const inputs = colorInputs.get(color);
|
||||
if (inputs) {
|
||||
const cm = ea.getCM(modifiedColor);
|
||||
inputs.textInput.setValue(modifiedColor);
|
||||
inputs.colorPicker.setValue(cm.stringHEX({alpha: false}).toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (svgImageElements.length > 0) {
|
||||
for (const el of svgImageElements) {
|
||||
const colorInfo = currentColors.get(el.id).colors;
|
||||
|
||||
// Process each color in the SVG
|
||||
for (const [color, info] of colorInfo.entries()) {
|
||||
let shouldModify = (modifyBackground && info.fill) || (modifyStroke && info.stroke);
|
||||
|
||||
if (shouldModify) {
|
||||
const modifiedColor = modifyColor(info.mappedTo, isDecrease, step, action);
|
||||
colorInfo.get(color).mappedTo = modifiedColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let isRunning = false;
|
||||
let queue = false;
|
||||
function processQueue() {
|
||||
if (!terminate && !isRunning && queue) {
|
||||
queue = false;
|
||||
isRunning = true;
|
||||
setColors(currentColors).then(() => {
|
||||
isRunning = false;
|
||||
if (queue) processQueue();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function run(action="Hue", isDecrease=true, step=0) {
|
||||
// passing invalid action (such as "clear") will bypass rewriting of colors using CM
|
||||
// this is useful when resetting colors to original values
|
||||
if(ACTIONS.includes(action)) {
|
||||
executeChange(isDecrease, step, action);
|
||||
}
|
||||
queue = true;
|
||||
if (!isRunning) processQueue();
|
||||
}
|
||||
|
||||
await storeOriginalColors();
|
||||
showModal();
|
||||
processQueue();
|
||||
1
ea-scripts/Shade Master.svg
Normal file
1
ea-scripts/Shade Master.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg class="skip" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 17a4 4 0 0 1-8 0V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2Z"/><path d="M16.7 13H19a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H7"/><path d="M 7 17h.01"/><path d="m11 8 2.3-2.3a2.4 2.4 0 0 1 3.404.004L18.6 7.6a2.4 2.4 0 0 1 .026 3.434L9.9 19.8"/></svg>
|
||||
|
After Width: | Height: | Size: 434 B |
@@ -26,6 +26,10 @@ if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.1.7")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ea.targetView.isDirty()) {
|
||||
ea.targetView.forceSave(true);
|
||||
}
|
||||
|
||||
const hostLeaf = ea.targetView.leaf;
|
||||
const hostView = hostLeaf.view;
|
||||
const statusBarElement = document.querySelector("div.status-bar");
|
||||
@@ -33,7 +37,7 @@ const ctrlKey = ea.targetView.modifierKeyDown.ctrlKey || ea.targetView.modifierK
|
||||
const altKey = ea.targetView.modifierKeyDown.altKey || ctrlKey;
|
||||
const shiftKey = ea.targetView.modifierKeyDown.shiftKey;
|
||||
const shouldStartWithLastSlide = shiftKey && window.ExcalidrawSlideshow &&
|
||||
(window.ExcalidrawSlideshow.script === utils.scriptFile.path) && (typeof window.ExcalidrawSlideshow.slide === "number")
|
||||
(window.ExcalidrawSlideshow.script === utils.scriptFile.path) && (typeof window.ExcalidrawSlideshow.slide?.[ea.targetView.file.path] === "number")
|
||||
//-------------------------------
|
||||
//constants
|
||||
//-------------------------------
|
||||
@@ -57,8 +61,9 @@ const SVG_LASER_OFF = ea.obsidian.getIcon("lucide-wand").outerHTML;
|
||||
//-------------------------------
|
||||
//utility & convenience functions
|
||||
//-------------------------------
|
||||
let shouldSaveAfterThePresentation = false;
|
||||
let isLaserOn = false;
|
||||
let slide = shouldStartWithLastSlide ? window.ExcalidrawSlideshow.slide : 0;
|
||||
let slide = shouldStartWithLastSlide ? window.ExcalidrawSlideshow.slide?.[ea.targetView.file.path] : 0;
|
||||
let isFullscreen = false;
|
||||
const ownerDocument = ea.targetView.ownerDocument;
|
||||
const startFullscreen = !altKey;
|
||||
@@ -350,8 +355,8 @@ const navigate = async (dir) => {
|
||||
}
|
||||
if(selectSlideDropdown) selectSlideDropdown.value = slide+1;
|
||||
await scrollToNextRect(nextRect);
|
||||
if(window.ExcalidrawSlideshow && (typeof window.ExcalidrawSlideshow.slide === "number")) {
|
||||
window.ExcalidrawSlideshow.slide = slide;
|
||||
if(window.ExcalidrawSlideshow && (typeof window.ExcalidrawSlideshow.slide?.[ea.targetView.file.path] === "number")) {
|
||||
window.ExcalidrawSlideshow.slide[ea.targetView.file.path] = slide;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,6 +510,7 @@ const createPresentationNavigationPanel = () => {
|
||||
new ea.obsidian.ToggleComponent(el)
|
||||
.setValue(isHidden)
|
||||
.onChange(value => {
|
||||
shouldSaveAfterThePresentation = true;
|
||||
if(value) {
|
||||
excalidrawAPI.setToast({
|
||||
message:"The presentation path remain hidden after the presentation. No need to select the line again. Just click the slideshow button to start the next presentation.",
|
||||
@@ -730,6 +736,9 @@ const exitPresentation = async (openForEdit = false) => {
|
||||
hostView.refreshCanvasOffset();
|
||||
excalidrawAPI.setActiveTool({type: "selection"});
|
||||
})
|
||||
if(!shouldSaveAfterThePresentation) {
|
||||
ea.targetView.clearDirty();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
@@ -755,6 +764,7 @@ const start = async () => {
|
||||
resetControlPanelElPosition();
|
||||
}
|
||||
if(presentationPathType === "line") await toggleArrowVisibility(isHidden);
|
||||
ea.targetView.clearDirty();
|
||||
}
|
||||
|
||||
const timestamp = Date.now();
|
||||
@@ -769,10 +779,14 @@ if(window.ExcalidrawSlideshow && (window.ExcalidrawSlideshow.script === utils.sc
|
||||
window.clearTimeout(window.ExcalidrawSlideshowStartTimer);
|
||||
delete window.ExcalidrawSlideshowStartTimer;
|
||||
}
|
||||
window.ExcalidrawSlideshow = {
|
||||
script: utils.scriptFile.path,
|
||||
timestamp,
|
||||
slide: 0
|
||||
};
|
||||
if(!window.ExcalidrawSlideshow) {
|
||||
window.ExcalidrawSlideshow = {
|
||||
script: utils.scriptFile.path,
|
||||
slide: {},
|
||||
};
|
||||
}
|
||||
window.ExcalidrawSlideshow.timestamp = timestamp;
|
||||
window.ExcalidrawSlideshow.slide[ea.targetView.file.path] = 0;
|
||||
|
||||
window.ExcalidrawSlideshowStartTimer = window.setTimeout(start,500);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ if (!ellipse) return;
|
||||
|
||||
let lines = elements.filter(el => el.type == "line" || el.type == "arrow");
|
||||
if (lines.length == 0) lines = ea.getViewElements().filter(el => el.type == "line" || el.type == "arrow");
|
||||
lines = lines.map(getNormalizedLine);
|
||||
const subLines = getSubLines(lines);
|
||||
|
||||
const angles = subLines.flatMap(line => {
|
||||
@@ -206,3 +207,70 @@ function isBetween(num, min, max) {
|
||||
function clamp(number, min, max) {
|
||||
return Math.max(min, Math.min(number, max));
|
||||
}
|
||||
|
||||
//Same line but with angle=0
|
||||
function getNormalizedLine(originalElement) {
|
||||
if(originalElement.angle === 0) return originalElement;
|
||||
|
||||
// Get absolute coordinates for all points first
|
||||
const pointRotateRads = (point, center, angle) => {
|
||||
const [x, y] = point;
|
||||
const [cx, cy] = center;
|
||||
return [
|
||||
(x - cx) * Math.cos(angle) - (y - cy) * Math.sin(angle) + cx,
|
||||
(x - cx) * Math.sin(angle) + (y - cy) * Math.cos(angle) + cy
|
||||
];
|
||||
};
|
||||
|
||||
// Get element absolute coordinates (matching Excalidraw's approach)
|
||||
const getElementAbsoluteCoords = (element) => {
|
||||
const points = element.points;
|
||||
let minX = Infinity;
|
||||
let minY = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let maxY = -Infinity;
|
||||
|
||||
for (const [x, y] of points) {
|
||||
const absX = x + element.x;
|
||||
const absY = y + element.y;
|
||||
minX = Math.min(minX, absX);
|
||||
minY = Math.min(minY, absY);
|
||||
maxX = Math.max(maxX, absX);
|
||||
maxY = Math.max(maxY, absY);
|
||||
}
|
||||
|
||||
return [minX, minY, maxX, maxY];
|
||||
};
|
||||
|
||||
// Calculate center point based on absolute coordinates
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(originalElement);
|
||||
const centerX = (x1 + x2) / 2;
|
||||
const centerY = (y1 + y2) / 2;
|
||||
|
||||
// Calculate absolute coordinates of all points
|
||||
const absolutePoints = originalElement.points.map(([x, y]) => [
|
||||
x + originalElement.x,
|
||||
y + originalElement.y
|
||||
]);
|
||||
|
||||
// Rotate all points around the center
|
||||
const rotatedPoints = absolutePoints.map(point =>
|
||||
pointRotateRads(point, [centerX, centerY], originalElement.angle)
|
||||
);
|
||||
|
||||
// Convert back to relative coordinates
|
||||
const newPoints = rotatedPoints.map(([x, y]) => [
|
||||
x - rotatedPoints[0][0],
|
||||
y - rotatedPoints[0][1]
|
||||
]);
|
||||
|
||||
const newLineId = ea.addLine(newPoints);
|
||||
|
||||
// Set the position of the new line to the first rotated point
|
||||
const newLine = ea.getElement(newLineId);
|
||||
newLine.x = rotatedPoints[0][0];
|
||||
newLine.y = rotatedPoints[0][1];
|
||||
newLine.angle = 0;
|
||||
delete ea.elementsDict[newLine.id];
|
||||
return newLine;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -94,6 +94,7 @@ I would love to include your contribution in the script library. If you have a s
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Dimensions.svg"/></div>|[[#Set Dimensions]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Grid.svg"/></div>|[[#Set Grid]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.svg"/></div>|[[#Set Stroke Width of Selected Elements]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Shade%20Master.svg"/></div>|[[#Shade Master]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Toggle%20Grid.svg"/></div>|[[#Toggle Grid]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Uniform%20size.svg"/></div>|[[#Uniform Size]]|
|
||||
|
||||
@@ -147,6 +148,7 @@ I would love to include your contribution in the script library. If you have a s
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Next%20Step%20in%20Process.svg"/></div>|[[#Add Next Step in Process]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20freedraw%20to%20line.svg"/></div>|[[#Convert freedraw to line]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.svg"/></div>|[[#Deconstruct selected elements into new drawing]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Full-Year%20Calendar%20Generator.svg"/></div>|[[#Full-Year Calendar Generator]]|
|
||||
|
||||
## Masking and cropping
|
||||
**Keywords**: Crop, Mask, Transform images
|
||||
@@ -393,6 +395,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Excalidraw%20Writing%20Machine.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Creates a hierarchical Markdown document out of a visual layout of an article that can be fed to Templater and converted into an article using AI for Templater.<br>Watch this video to understand how the script is intended to work:<br><iframe width="400" height="225" src="https://www.youtube.com/embed/zvRpCOZAUSs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><br>You can download the sample Obsidian Templater file from <a href="https://gist.github.com/zsviczian/bf49d4b2d401f5749aaf8c2fa8a513d9">here</a>. You can download the demo PDF document showcased in the video from <a href="https://zsviczian.github.io/DemoArticle-AtomicHabits.pdf">here</a>.</td></tr></table>
|
||||
|
||||
## Full-Year Calendar Generator
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Full-Year%20Calendar%20Generator.md
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/simonperet'>@simonperet</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Full-Year%20Calendar%20Generator.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Generates a complete calendar for a specified year.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-full-year-calendar-exemple.excalidraw.png'></td></tr></table>
|
||||
|
||||
## GPT Draw-a-UI
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/GPT-Draw-a-UI.md
|
||||
@@ -563,6 +571,13 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Text%20Alignment.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg'></td></tr></table>
|
||||
|
||||
## Shade Master
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Shade%20Master.md
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Shade%20Master.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">You can modify the colors of SVG images, embedded files, and Excalidraw elements in a drawing by changing Hue, Saturation, Lightness and Transparency; and if only a single SVG or nested Excalidraw drawing is selected, then you can remap image colors.<br><iframe width="560" height="315" src="https://www.youtube.com/embed/ISuORbVKyhQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td></tr></table>
|
||||
|
||||
|
||||
## Slideshow
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Slideshow.md
|
||||
|
||||
BIN
images/scripts-full-year-calendar-customize.excalidraw.png
Normal file
BIN
images/scripts-full-year-calendar-customize.excalidraw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
images/scripts-full-year-calendar-exemple.excalidraw.png
Normal file
BIN
images/scripts-full-year-calendar-exemple.excalidraw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 442 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.7.1",
|
||||
"version": "2.7.6-beta-1",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.7.1",
|
||||
"version": "2.7.5",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
9290
package-lock.json
generated
Normal file
9290
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -24,7 +24,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/excalidraw": "0.17.6-22",
|
||||
"@zsviczian/excalidraw": "0.17.6-26",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"@zsviczian/colormaster": "^1.2.2",
|
||||
@@ -58,7 +58,7 @@
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-typescript": "^11.1.6",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/chroma-js": "^2.4.0",
|
||||
"@types/js-beautify": "^1.14.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
@@ -79,10 +79,10 @@
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"@zsviczian/rollup-plugin-postprocess": "^1.0.3",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.34.1",
|
||||
"tslib": "^2.6.1",
|
||||
"rollup-plugin-typescript2": "^0.36.0",
|
||||
"tslib": "^2.8.1",
|
||||
"ttypescript": "^1.5.15",
|
||||
"typescript": "^5.2.2",
|
||||
"typescript": "^5.7.3",
|
||||
"fs-extra": "^11.2.0",
|
||||
"uglify-js": "^3.19.3"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import { terser } from "rollup-plugin-terser";
|
||||
import copy from "rollup-plugin-copy";
|
||||
import typescript2 from "rollup-plugin-typescript2";
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import LZString from 'lz-string';
|
||||
import postprocess from '@zsviczian/rollup-plugin-postprocess';
|
||||
import cssnano from 'cssnano';
|
||||
@@ -16,6 +17,8 @@ import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const DIST_FOLDER = 'dist';
|
||||
const absolutePath = path.resolve(DIST_FOLDER);
|
||||
fs.mkdirSync(absolutePath, { recursive: true });
|
||||
const isProd = (process.env.NODE_ENV === "production");
|
||||
const isLib = (process.env.NODE_ENV === "lib");
|
||||
console.log(`Running: ${process.env.NODE_ENV}; isProd: ${isProd}; isLib: ${isLib}`);
|
||||
@@ -32,13 +35,16 @@ function trimLastSemicolon(input) {
|
||||
}
|
||||
|
||||
function minifyCode(code) {
|
||||
const minified = minify(code,{
|
||||
compress: true,
|
||||
const minified = minify(code, {
|
||||
compress: {
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2170
|
||||
reduce_vars: false,
|
||||
},
|
||||
mangle: true,
|
||||
output: {
|
||||
comments: false,
|
||||
beautify: false,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
if (minified.error) {
|
||||
@@ -55,7 +61,7 @@ function compressLanguageFile(lang) {
|
||||
return LZString.compressToBase64(minifyCode(`x = ${content};`));
|
||||
}
|
||||
|
||||
const excalidraw_pkg = isLib ? "" : minifyCode( isProd
|
||||
const excalidraw_pkg = isLib ? "" : minifyCode(isProd
|
||||
? fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.production.min.js", "utf8")
|
||||
: fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.development.js", "utf8"));
|
||||
const react_pkg = isLib ? "" : minifyCode(isProd
|
||||
@@ -102,7 +108,7 @@ const packageString = isLib
|
||||
'const PLUGIN_VERSION="' + manifest.version + '";';
|
||||
|
||||
const BASE_CONFIG = {
|
||||
input: 'src/main.ts',
|
||||
input: 'src/core/main.ts',
|
||||
external: [
|
||||
'@codemirror/autocomplete',
|
||||
'@codemirror/collab',
|
||||
@@ -141,7 +147,12 @@ const BUILD_CONFIG = {
|
||||
exports: 'default',
|
||||
},
|
||||
plugins: getRollupPlugins(
|
||||
{tsconfig: isProd ? "tsconfig.json" : "tsconfig.dev.json"},
|
||||
{
|
||||
tsconfig: isProd ? "tsconfig.json" : "tsconfig.dev.json",
|
||||
sourcemap: !isProd,
|
||||
clean: true,
|
||||
//verbosity: isProd ? 1 : 2,
|
||||
},
|
||||
...(isProd ? [
|
||||
terser({
|
||||
toplevel: false,
|
||||
@@ -166,10 +177,10 @@ const BUILD_CONFIG = {
|
||||
|
||||
const LIB_CONFIG = {
|
||||
...BASE_CONFIG,
|
||||
input: "src/index.ts",
|
||||
input: "src/core/index.ts",
|
||||
output: {
|
||||
dir: "lib",
|
||||
sourcemap: true,
|
||||
sourcemap: false,
|
||||
format: "cjs",
|
||||
name: "Excalidraw (Library)",
|
||||
},
|
||||
|
||||
368
src/OneOffs.ts
368
src/OneOffs.ts
@@ -1,368 +0,0 @@
|
||||
import ExcalidrawPlugin from "./main";
|
||||
|
||||
export class OneOffs {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
|
||||
constructor(plugin: ExcalidrawPlugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/*
|
||||
public patchCommentBlock() {
|
||||
//This is a once off cleanup process to remediate incorrectly placed comment %% before # Text Elements
|
||||
if (!this.plugin.settings.patchCommentBlock) {
|
||||
return;
|
||||
}
|
||||
const plugin = this.plugin;
|
||||
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format("HH:mm:ss")}: Excalidraw will patch drawings in 5 minutes`,
|
||||
);
|
||||
setTimeout(async () => {
|
||||
await plugin.loadSettings();
|
||||
if (!plugin.settings.patchCommentBlock) {
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format(
|
||||
"HH:mm:ss",
|
||||
)}: Excalidraw patching aborted because synched data.json is already patched`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format("HH:mm:ss")}: Excalidraw is starting the patching process`,
|
||||
);
|
||||
let i = 0;
|
||||
const excalidrawFiles = plugin.app.vault.getFiles();
|
||||
for (const f of (excalidrawFiles || []).filter((f: TFile) =>
|
||||
plugin.isExcalidrawFile(f),
|
||||
)) {
|
||||
if (
|
||||
f.extension !== "excalidraw" && //legacy files do not need to be touched
|
||||
plugin.app.workspace.getActiveFile() !== f
|
||||
) {
|
||||
//file is currently being edited
|
||||
let drawing = await plugin.app.vault.read(f);
|
||||
const orig_drawing = drawing;
|
||||
drawing = drawing.replaceAll("\r\n", "\n").replaceAll("\r", "\n"); //Win, Mac, Linux compatibility
|
||||
drawing = drawing.replace(
|
||||
"\n%%\n# Text Elements\n",
|
||||
"\n# Text Elements\n",
|
||||
);
|
||||
if (drawing.search("\n%%\n# Drawing\n") === -1) {
|
||||
const sceneJSONandPOS = getJSON(drawing);
|
||||
drawing = `${drawing.substr(
|
||||
0,
|
||||
sceneJSONandPOS.pos,
|
||||
)}\n%%\n# Drawing\n\`\`\`json\n${sceneJSONandPOS.scene}\n\`\`\`%%`;
|
||||
}
|
||||
if (drawing !== orig_drawing) {
|
||||
i++;
|
||||
log(`Excalidraw patched: ${f.path}`);
|
||||
await plugin.app.vault.modify(f, drawing);
|
||||
}
|
||||
}
|
||||
}
|
||||
plugin.settings.patchCommentBlock = false;
|
||||
plugin.saveSettings();
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format("HH:mm:ss")}: Excalidraw patched in total ${i} files`,
|
||||
);
|
||||
}, 300000); //5 minutes
|
||||
}
|
||||
|
||||
public migrationNotice() {
|
||||
if (this.plugin.settings.loadCount > 0) {
|
||||
return;
|
||||
}
|
||||
const plugin = this.plugin;
|
||||
|
||||
plugin.app.workspace.onLayoutReady(async () => {
|
||||
plugin.settings.loadCount++;
|
||||
plugin.saveSettings();
|
||||
const files = plugin.app.vault
|
||||
.getFiles()
|
||||
.filter((f) => f.extension === "excalidraw");
|
||||
if (files.length > 0) {
|
||||
const prompt = new MigrationPrompt(plugin.app, plugin);
|
||||
prompt.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public imageElementLaunchNotice() {
|
||||
if (!this.plugin.settings.imageElementNotice) {
|
||||
return;
|
||||
}
|
||||
const plugin = this.plugin;
|
||||
|
||||
plugin.app.workspace.onLayoutReady(async () => {
|
||||
const prompt = new ImageElementNotice(plugin.app, plugin);
|
||||
prompt.open();
|
||||
});
|
||||
}
|
||||
|
||||
public wysiwygPatch() {
|
||||
if (this.plugin.settings.patchCommentBlock) {
|
||||
return;
|
||||
} //the comment block patch needs to happen first (unlikely that someone has waited this long with the update...)
|
||||
//This is a once off process to patch excalidraw files remediate incorrectly placed comment %% before # Text Elements
|
||||
if (
|
||||
!(
|
||||
this.plugin.settings.runWYSIWYGpatch ||
|
||||
this.plugin.settings.fixInfinitePreviewLoop
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const plugin = this.plugin;
|
||||
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format(
|
||||
"HH:mm:ss",
|
||||
)}: Excalidraw will patch drawings to support WYSIWYG in 7 minutes`,
|
||||
);
|
||||
setTimeout(async () => {
|
||||
await plugin.loadSettings();
|
||||
if (
|
||||
!(
|
||||
this.plugin.settings.runWYSIWYGpatch ||
|
||||
this.plugin.settings.fixInfinitePreviewLoop
|
||||
)
|
||||
) {
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format(
|
||||
"HH:mm:ss",
|
||||
)}: Excalidraw patching aborted because synched data.json is already patched`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format("HH:mm:ss")}: Excalidraw is starting the patching process`,
|
||||
);
|
||||
let i = 0;
|
||||
const excalidrawFiles = plugin.app.vault.getFiles();
|
||||
for (const f of (excalidrawFiles || []).filter((f: TFile) =>
|
||||
plugin.isExcalidrawFile(f),
|
||||
)) {
|
||||
if (
|
||||
f.extension !== "excalidraw" && //legacy files do not need to be touched
|
||||
plugin.app.workspace.getActiveFile() !== f
|
||||
) {
|
||||
//file is currently being edited
|
||||
try {
|
||||
const excalidrawData = new ExcalidrawData(plugin);
|
||||
const data = await plugin.app.vault.read(f);
|
||||
const textMode = getTextMode(data);
|
||||
await excalidrawData.loadData(data, f, textMode);
|
||||
|
||||
let trimLocation = data.search(/(^%%\n)?# Text Elements\n/m);
|
||||
if (trimLocation == -1) {
|
||||
trimLocation = data.search(/(%%\n)?# Drawing\n/);
|
||||
}
|
||||
if (trimLocation > -1) {
|
||||
let header = data
|
||||
.substring(0, trimLocation)
|
||||
.replace(
|
||||
/excalidraw-plugin:\s.*\n/,
|
||||
`${FRONTMATTER_KEY}: ${
|
||||
textMode == TextMode.raw ? "raw\n" : "parsed\n"
|
||||
}`,
|
||||
);
|
||||
|
||||
header = header.replace(
|
||||
/cssclass:[\s]*excalidraw-hide-preview-text[\s]*\n/,
|
||||
"",
|
||||
);
|
||||
|
||||
const REG_IMG = /(^---[\w\W]*?---\n)(!\[\[.*?]]\n(%%\n)?)/m; //(%%\n)? because of 1.4.8-beta... to be backward compatible with anyone who installed that version
|
||||
if (header.match(REG_IMG)) {
|
||||
header = header.replace(REG_IMG, "$1");
|
||||
}
|
||||
const newData = header + excalidrawData.generateMD();
|
||||
|
||||
if (data !== newData) {
|
||||
i++;
|
||||
log(`Excalidraw patched: ${f.path}`);
|
||||
await plugin.app.vault.modify(f, newData);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
errorlog({
|
||||
where: "OneOffs.wysiwygPatch",
|
||||
message: `Unable to process: ${f.path}`,
|
||||
error: e,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
plugin.settings.runWYSIWYGpatch = false;
|
||||
plugin.settings.fixInfinitePreviewLoop = false;
|
||||
plugin.saveSettings();
|
||||
log(
|
||||
`${window
|
||||
.moment()
|
||||
.format("HH:mm:ss")}: Excalidraw patched in total ${i} files`,
|
||||
);
|
||||
}, 420000); //7 minutes
|
||||
}
|
||||
}
|
||||
|
||||
class MigrationPrompt extends Modal {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
|
||||
constructor(app: App, plugin: ExcalidrawPlugin) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
onOpen(): void {
|
||||
this.titleEl.setText("Welcome to Excalidraw 1.2");
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
onClose(): void {
|
||||
this.contentEl.empty();
|
||||
}
|
||||
|
||||
createForm(): void {
|
||||
const div = this.contentEl.createDiv();
|
||||
// div.addClass("excalidraw-prompt-div");
|
||||
// div.style.maxWidth = "600px";
|
||||
div.createEl("p", {
|
||||
text: "This version comes with tons of new features and possibilities. Please read the description in Community Plugins to find out more.",
|
||||
});
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
el.innerHTML =
|
||||
"Drawings you've created with version 1.1.x need to be converted to take advantage of the new features. You can also continue to use them in compatibility mode. " +
|
||||
"During conversion your old *.excalidraw files will be replaced with new *.excalidraw.md files.";
|
||||
});
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
//files manually follow one of two options:
|
||||
el.innerHTML =
|
||||
"To convert your drawings you have the following options:<br><ul>" +
|
||||
"<li>Click <code>CONVERT FILES</code> now to convert all of your *.excalidraw files, or if you prefer to make a backup first, then click <code>CANCEL</code>.</li>" +
|
||||
"<li>In the Command Palette select <code>Excalidraw: Convert *.excalidraw files to *.excalidraw.md files</code></li>" +
|
||||
"<li>Right click an <code>*.excalidraw</code> file in File Explorer and select one of the following options to convert files one by one: <ul>" +
|
||||
"<li><code>*.excalidraw => *.excalidraw.md</code></li>" +
|
||||
"<li><code>*.excalidraw => *.md (Logseq compatibility)</code>. This option will retain the original *.excalidraw file next to the new Obsidian format. " +
|
||||
"Make sure you also enable <code>Compatibility features</code> in Settings for a full solution.</li></ul></li>" +
|
||||
"<li>Open a drawing in compatibility mode and select <code>Convert to new format</code> from the <code>Options Menu</code></li></ul>";
|
||||
});
|
||||
div.createEl("p", {
|
||||
text: "This message will only appear maximum 3 times in case you have *.excalidraw files in your Vault.",
|
||||
});
|
||||
const bConvert = div.createEl("button", { text: "CONVERT FILES" });
|
||||
bConvert.onclick = () => {
|
||||
this.plugin.convertExcalidrawToMD();
|
||||
this.close();
|
||||
};
|
||||
const bCancel = div.createEl("button", { text: "CANCEL" });
|
||||
bCancel.onclick = () => {
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ImageElementNotice extends Modal {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
private saveChanges: boolean = false;
|
||||
|
||||
constructor(app: App, plugin: ExcalidrawPlugin) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
onOpen(): void {
|
||||
this.titleEl.setText("Image Elements have arrived!");
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
this.contentEl.empty();
|
||||
if (!this.saveChanges) {
|
||||
return;
|
||||
}
|
||||
await this.plugin.loadSettings();
|
||||
this.plugin.settings.imageElementNotice = false;
|
||||
this.plugin.saveSettings();
|
||||
}
|
||||
|
||||
createForm(): void {
|
||||
const div = this.contentEl.createDiv();
|
||||
//div.addClass("excalidraw-prompt-div");
|
||||
//div.style.maxWidth = "600px";
|
||||
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
el.innerHTML =
|
||||
"Welcome to Obsidian-Excalidraw 1.4! I've added Image Elements. " +
|
||||
"Please watch the video below to learn how to use this new feature.";
|
||||
});
|
||||
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
el.innerHTML =
|
||||
"<u>⚠ WARNING:</u> Opening new drawings with an older version of the plugin will lead to loss of images. " +
|
||||
"Update the plugin on all your devices.";
|
||||
});
|
||||
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
el.innerHTML =
|
||||
"Since March, I have spent most of my free time building this plugin. Close to 75 workdays worth of my time (assuming 8-hour days). " +
|
||||
"Some of you have already bought me a coffee. THANK YOU! Your support really means a lot to me! If you have not yet done so, please consider clicking the button below.";
|
||||
});
|
||||
|
||||
const coffeeDiv = div.createDiv("coffee");
|
||||
coffeeDiv.addClass("ex-coffee-div");
|
||||
const coffeeLink = coffeeDiv.createEl("a", {
|
||||
href: "https://ko-fi.com/zsolt",
|
||||
});
|
||||
const coffeeImg = coffeeLink.createEl("img", {
|
||||
attr: {
|
||||
src: "https://cdn.ko-fi.com/cdn/kofi3.png?v=3",
|
||||
},
|
||||
});
|
||||
coffeeImg.height = 45;
|
||||
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
//files manually follow one of two options:
|
||||
el.style.textAlign = "center";
|
||||
el.innerHTML =
|
||||
'<iframe width="560" height="315" src="https://www.youtube.com/embed/_c_0zpBJ4Xc?start=20" title="YouTube video player" ' +
|
||||
'frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" ' +
|
||||
"allowfullscreen></iframe>";
|
||||
});
|
||||
|
||||
div.createEl("p", { text: "" }, (el) => {
|
||||
//files manually follow one of two options:
|
||||
el.style.textAlign = "right";
|
||||
|
||||
const bOk = el.createEl("button", { text: "OK - Don't show this again" });
|
||||
bOk.onclick = () => {
|
||||
this.saveChanges = true;
|
||||
this.close();
|
||||
};
|
||||
|
||||
const bCancel = el.createEl("button", {
|
||||
text: "CANCEL - Read next time",
|
||||
});
|
||||
bCancel.onclick = () => {
|
||||
this.saveChanges = false;
|
||||
this.close();
|
||||
};
|
||||
});
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Copy, Crop, Globe, RotateCcw, Scan, Settings, TextSelect } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { PenStyle } from "src/types/PenTypes";
|
||||
import { PenStyle } from "src/types/penTypes";
|
||||
|
||||
export const ICONS = {
|
||||
ExportImage: (
|
||||
@@ -1,8 +1,8 @@
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { DeviceType } from "../types/types";
|
||||
import { ExcalidrawLib } from "../ExcalidrawLib";
|
||||
import { ExcalidrawLib } from "../types/excalidrawLib";
|
||||
import { moment } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { DeviceType } from "src/types/types";
|
||||
//This is only for backward compatibility because an early version of obsidian included an encoding to avoid fantom links from littering Obsidian graph view
|
||||
declare const PLUGIN_VERSION:string;
|
||||
export let EXCALIDRAW_PLUGIN: ExcalidrawPlugin = null;
|
||||
@@ -198,7 +198,7 @@ export const REG_BLOCK_REF_CLEAN = /[!"#$%&()*+,.:;<=>?@^`{|}~\/\[\]\\\r\n]/g;
|
||||
// /[!"#$%&()*+,.:;<=>?@^`{|}~\/\[\]\\]/g;
|
||||
// https://discord.com/channels/686053708261228577/989603365606531104/1000128926619816048
|
||||
// /\+|\/|~|=|%|\(|\)|{|}|,|&|\.|\$|!|\?|;|\[|]|\^|#|\*|<|>|&|@|\||\\|"|:|\s/g;
|
||||
export const IMAGE_TYPES = ["jpeg", "jpg", "png", "gif", "svg", "webp", "bmp", "ico", "jtif", "tif"];
|
||||
export const IMAGE_TYPES = ["jpeg", "jpg", "png", "gif", "svg", "webp", "bmp", "ico", "jtif", "tif", "jfif", "avif"];
|
||||
export const ANIMATED_IMAGE_TYPES = ["gif", "webp", "apng", "svg"];
|
||||
export const EXPORT_TYPES = ["svg", "dark.svg", "light.svg", "png", "dark.png", "light.png"];
|
||||
export const MAX_IMAGE_SIZE = 500;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Extension } from "@codemirror/state";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { HideTextBetweenCommentsExtension } from "./Fadeout";
|
||||
import { debug, DEBUGGING } from "src/utils/DebugHelper";
|
||||
import { debug, DEBUGGING } from "src/utils/debugHelper";
|
||||
export const EDITOR_FADEOUT = "fadeOutExcalidrawMarkup";
|
||||
|
||||
const editorExtensions: {[key:string]:Extension}= {
|
||||
@@ -1,14 +1,14 @@
|
||||
import "obsidian";
|
||||
//import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
|
||||
//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 "src/types/types";
|
||||
export const getEA = (view?:any): any => {
|
||||
try {
|
||||
return window.ExcalidrawAutomate.getAPI(view);
|
||||
} catch(e) {
|
||||
console.log({message: "Excalidraw not available", fn: getEA});
|
||||
return null;
|
||||
}
|
||||
import "obsidian";
|
||||
//import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
|
||||
//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 "src/types/types";
|
||||
export const getEA = (view?:any): any => {
|
||||
try {
|
||||
return window.ExcalidrawAutomate.getAPI(view);
|
||||
} catch(e) {
|
||||
console.log({message: "Excalidraw not available", fn: getEA});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,25 +16,23 @@ import {
|
||||
IMAGE_TYPES,
|
||||
DEVICE,
|
||||
sceneCoordsToViewportCoords,
|
||||
} from "../constants/constants";
|
||||
import ExcalidrawView, { TextMode } from "../ExcalidrawView";
|
||||
fileid,
|
||||
} from "../../constants/constants";
|
||||
import ExcalidrawView, { TextMode } from "../../view/ExcalidrawView";
|
||||
import {
|
||||
REGEX_LINK,
|
||||
} from "../ExcalidrawData";
|
||||
} from "../../shared/ExcalidrawData";
|
||||
import { ExcalidrawSettings } from "../settings";
|
||||
import { openDialogAction, OpenFileDialog } from "../dialogs/OpenDrawing";
|
||||
import { InsertLinkDialog } from "../dialogs/InsertLinkDialog";
|
||||
import { InsertCommandDialog } from "../dialogs/InsertCommandDialog";
|
||||
import { InsertImageDialog } from "../dialogs/InsertImageDialog";
|
||||
import { ImportSVGDialog } from "../dialogs/ImportSVGDialog";
|
||||
import { InsertMDDialog } from "../dialogs/InsertMDDialog";
|
||||
import {
|
||||
ExcalidrawAutomate,
|
||||
insertLaTeXToView,
|
||||
search,
|
||||
} from "../ExcalidrawAutomate";
|
||||
import { templatePromt } from "../dialogs/Prompt";
|
||||
import { t } from "../lang/helpers";
|
||||
import { openDialogAction, OpenFileDialog } from "../../shared/Dialogs/OpenDrawing";
|
||||
import { InsertLinkDialog } from "../../shared/Dialogs/InsertLinkDialog";
|
||||
import { InsertCommandDialog } from "../../shared/Dialogs/InsertCommandDialog";
|
||||
import { InsertImageDialog } from "../../shared/Dialogs/InsertImageDialog";
|
||||
import { ImportSVGDialog } from "../../shared/Dialogs/ImportSVGDialog";
|
||||
import { InsertMDDialog } from "../../shared/Dialogs/InsertMDDialog";
|
||||
import { ExcalidrawAutomate } from "../../shared/ExcalidrawAutomate";
|
||||
import { insertLaTeXToView, search } from "src/utils/excalidrawAutomateUtils";
|
||||
import { templatePromt } from "../../shared/Dialogs/Prompt";
|
||||
import { t } from "../../lang/helpers";
|
||||
import {
|
||||
getAliasWithSize,
|
||||
getAnnotationFileNameAndFolder,
|
||||
@@ -45,32 +43,33 @@ import {
|
||||
getLink,
|
||||
getListOfTemplateFiles,
|
||||
getURLImageExtension,
|
||||
} from "../utils/FileUtils";
|
||||
} from "../../utils/fileUtils";
|
||||
import {
|
||||
setLeftHandedMode,
|
||||
sleep,
|
||||
decompress,
|
||||
getImageSize,
|
||||
} from "../utils/Utils";
|
||||
import { extractSVGPNGFileName, getActivePDFPageNumberFromPDFView, getAttachmentsFolderAndFilePath, isObsidianThemeDark, mergeMarkdownFiles, setExcalidrawView } from "../utils/ObsidianUtils";
|
||||
import { ExcalidrawElement, ExcalidrawEmbeddableElement, ExcalidrawImageElement, ExcalidrawTextElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { ReleaseNotes } from "../dialogs/ReleaseNotes";
|
||||
import { ScriptInstallPrompt } from "../dialogs/ScriptInstallPrompt";
|
||||
import Taskbone from "../ocr/Taskbone";
|
||||
import { emulateCTRLClickForLinks, linkClickModifierType, PaneTarget } from "../utils/ModifierkeyHelper";
|
||||
import { InsertPDFModal } from "../dialogs/InsertPDFModal";
|
||||
import { ExportDialog } from "../dialogs/ExportDialog";
|
||||
import { UniversalInsertFileModal } from "../dialogs/UniversalInsertFileModal";
|
||||
import { PublishOutOfDateFilesDialog } from "../dialogs/PublishOutOfDateFiles";
|
||||
import { EmbeddableSettings } from "../dialogs/EmbeddableSettings";
|
||||
import { processLinkText } from "../utils/CustomEmbeddableUtils";
|
||||
import { getEA } from "src";
|
||||
} from "../../utils/utils";
|
||||
import { extractSVGPNGFileName, getActivePDFPageNumberFromPDFView, getAttachmentsFolderAndFilePath, isObsidianThemeDark, mergeMarkdownFiles, setExcalidrawView } from "../../utils/obsidianUtils";
|
||||
import { ExcalidrawElement, ExcalidrawEmbeddableElement, ExcalidrawImageElement, ExcalidrawTextElement, FileId } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { ReleaseNotes } from "../../shared/Dialogs/ReleaseNotes";
|
||||
import { ScriptInstallPrompt } from "../../shared/Dialogs/ScriptInstallPrompt";
|
||||
import Taskbone from "../../shared/OCR/Taskbone";
|
||||
import { emulateCTRLClickForLinks, linkClickModifierType, PaneTarget } from "../../utils/modifierkeyHelper";
|
||||
import { InsertPDFModal } from "../../shared/Dialogs/InsertPDFModal";
|
||||
import { ExportDialog } from "../../shared/Dialogs/ExportDialog";
|
||||
import { UniversalInsertFileModal } from "../../shared/Dialogs/UniversalInsertFileModal";
|
||||
import { PublishOutOfDateFilesDialog } from "../../shared/Dialogs/PublishOutOfDateFiles";
|
||||
import { EmbeddableSettings } from "../../shared/Dialogs/EmbeddableSettings";
|
||||
import { processLinkText } from "../../utils/customEmbeddableUtils";
|
||||
import { getEA } from "src/core";
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
|
||||
import { carveOutImage, carveOutPDF, createImageCropperFile } from "../utils/CarveOut";
|
||||
import { showFrameSettings } from "../dialogs/FrameSettings";
|
||||
import { insertImageToView } from "../utils/ExcalidrawViewUtils";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { carveOutImage, carveOutPDF, createImageCropperFile } from "../../utils/carveout";
|
||||
import { showFrameSettings } from "../../shared/Dialogs/FrameSettings";
|
||||
import { insertImageToView } from "../../utils/excalidrawViewUtils";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { get } from "http";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
@@ -1044,6 +1043,43 @@ export class CommandManager {
|
||||
}
|
||||
})
|
||||
|
||||
this.addCommand({
|
||||
id: "duplicate-image",
|
||||
name: t("DUPLICATE_IMAGE"),
|
||||
checkCallback: (checking:boolean) => {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if(!view) return false;
|
||||
if(!view.excalidrawAPI) return false;
|
||||
const els = view.getViewSelectedElements().filter(el=>el.type==="image");
|
||||
if(els.length !== 1) {
|
||||
if(checking) return false;
|
||||
new Notice("Select a single image element and try again");
|
||||
return false;
|
||||
}
|
||||
const el = els[0] as ExcalidrawImageElement;
|
||||
const ef = view.excalidrawData.getFile(el.fileId);
|
||||
if(!ef?.file) return false;
|
||||
if(checking) return true;
|
||||
(async()=>{
|
||||
const ea = getEA(view) as ExcalidrawAutomate;
|
||||
const isAnchored = Boolean(el.customData?.isAnchored);
|
||||
const imgId = await ea.addImage(el.x+el.width/5, el.y+el.height/5, ef.file,!isAnchored, isAnchored);
|
||||
const img = ea.getElement(imgId) as Mutable<ExcalidrawImageElement>;
|
||||
img.width = el.width;
|
||||
img.height = el.height;
|
||||
if(el.crop) img.crop = {...el.crop};
|
||||
const newFileId = fileid() as FileId;
|
||||
ea.imagesDict[newFileId] = ea.imagesDict[img.fileId];
|
||||
ea.imagesDict[newFileId].id = newFileId;
|
||||
delete ea.imagesDict[img.fileId]
|
||||
img.fileId = newFileId;
|
||||
await ea.addElementsToView(false, false, true);
|
||||
ea.selectElementsInView([imgId]);
|
||||
ea.destroy();
|
||||
})();
|
||||
}
|
||||
})
|
||||
|
||||
this.addCommand({
|
||||
id: "reset-image-to-100",
|
||||
name: t("RESET_IMG_TO_100"),
|
||||
@@ -1,12 +1,12 @@
|
||||
import { WorkspaceLeaf, TFile, Editor, MarkdownView, MarkdownFileInfo, MetadataCache, App, EventRef, Menu, FileView } from "obsidian";
|
||||
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { getLink } from "../utils/FileUtils";
|
||||
import { editorInsertText, getParentOfClass, setExcalidrawView } from "../utils/ObsidianUtils";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { DEBUGGING, debug } from "src/utils/DebugHelper";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { getLink } from "../../utils/fileUtils";
|
||||
import { editorInsertText, getExcalidrawViews, getParentOfClass, setExcalidrawView } from "../../utils/obsidianUtils";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { DEBUGGING, debug } from "src/utils/debugHelper";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import { DEVICE, FRONTMATTER_KEYS, ICON_NAME, VIEW_TYPE_EXCALIDRAW } from "src/constants/constants";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
import { t } from "src/lang/helpers";
|
||||
|
||||
/**
|
||||
@@ -81,6 +81,8 @@ export class EventManager {
|
||||
//save Excalidraw leaf and update embeds when switching to another leaf
|
||||
this.registerEvent(this.plugin.app.workspace.on("active-leaf-change", this.onActiveLeafChangeHandler.bind(this)));
|
||||
|
||||
this.registerEvent(this.app.workspace.on("layout-change", this.onLayoutChangeHandler.bind(this)));
|
||||
|
||||
//File Save Trigger Handlers
|
||||
//Save the drawing if the user clicks outside the Excalidraw Canvas
|
||||
const onClickEventSaveActiveDrawing = this.onClickSaveActiveDrawing.bind(this);
|
||||
@@ -101,6 +103,10 @@ export class EventManager {
|
||||
this.plugin.registerEvent(this.plugin.app.workspace.on("editor-menu", this.onEditorMenuHandler.bind(this)));
|
||||
}
|
||||
|
||||
private onLayoutChangeHandler() {
|
||||
getExcalidrawViews(this.app).forEach(excalidrawView=>excalidrawView.refresh());
|
||||
}
|
||||
|
||||
private onPasteHandler (evt: ClipboardEvent, editor: Editor, info: MarkdownView | MarkdownFileInfo ) {
|
||||
if(evt.defaultPrevented) return
|
||||
const data = evt.clipboardData.getData("text/plain");
|
||||
@@ -1,15 +1,15 @@
|
||||
import { debug } from "src/utils/DebugHelper";
|
||||
import { debug } from "src/utils/debugHelper";
|
||||
import { App, FrontMatterCache, MarkdownView, MetadataCache, normalizePath, Notice, TAbstractFile, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { BLANK_DRAWING, DARK_BLANK_DRAWING, DEVICE, EXPORT_TYPES, FRONTMATTER, FRONTMATTER_KEYS, JSON_parse, nanoid, VIEW_TYPE_EXCALIDRAW } from "src/constants/constants";
|
||||
import { Prompt, templatePromt } from "src/dialogs/Prompt";
|
||||
import { changeThemeOfExcalidrawMD, ExcalidrawData, getMarkdownDrawingSection } from "src/ExcalidrawData";
|
||||
import ExcalidrawView, { getTextMode } from "src/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { DEBUGGING } from "src/utils/DebugHelper";
|
||||
import { checkAndCreateFolder, download, getIMGFilename, getLink, getListOfTemplateFiles, getNewUniqueFilepath } from "src/utils/FileUtils";
|
||||
import { PaneTarget } from "src/utils/ModifierkeyHelper";
|
||||
import { getExcalidrawViews, getNewOrAdjacentLeaf, isObsidianThemeDark, openLeaf } from "src/utils/ObsidianUtils";
|
||||
import { errorlog, getExportTheme } from "src/utils/Utils";
|
||||
import { Prompt, templatePromt } from "src/shared/Dialogs/Prompt";
|
||||
import { changeThemeOfExcalidrawMD, ExcalidrawData, getMarkdownDrawingSection } from "../../shared/ExcalidrawData";
|
||||
import ExcalidrawView, { getTextMode } from "src/view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { DEBUGGING } from "src/utils/debugHelper";
|
||||
import { checkAndCreateFolder, download, getIMGFilename, getLink, getListOfTemplateFiles, getNewUniqueFilepath } from "src/utils/fileUtils";
|
||||
import { PaneTarget } from "src/utils/modifierkeyHelper";
|
||||
import { getExcalidrawViews, getNewOrAdjacentLeaf, isObsidianThemeDark, openLeaf } from "src/utils/obsidianUtils";
|
||||
import { errorlog, getExportTheme } from "src/utils/utils";
|
||||
|
||||
export class PluginFileManager {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
@@ -324,7 +324,7 @@ export class PluginFileManager {
|
||||
const textElements = excalidrawData.elements?.filter(
|
||||
(el: any) => el.type == "text",
|
||||
);
|
||||
let outString = `# Excalidraw Data\n## Text Elements\n`;
|
||||
let outString = `# Excalidraw Data\n\n## Text Elements\n`;
|
||||
let id: string;
|
||||
for (const te of textElements) {
|
||||
id = te.id;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import { debug, DEBUGGING } from "src/utils/DebugHelper";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { CustomMutationObserver } from "src/utils/DebugHelper";
|
||||
import { getExcalidrawViews, isObsidianThemeDark } from "src/utils/ObsidianUtils";
|
||||
import { debug, DEBUGGING } from "src/utils/debugHelper";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { CustomMutationObserver } from "src/utils/debugHelper";
|
||||
import { getExcalidrawViews, isObsidianThemeDark } from "src/utils/obsidianUtils";
|
||||
import { App, Notice, TFile } from "obsidian";
|
||||
|
||||
export class ObserverManager {
|
||||
@@ -1,9 +1,9 @@
|
||||
import { updateExcalidrawLib } from "src/constants/constants";
|
||||
import { ExcalidrawLib } from "../ExcalidrawLib";
|
||||
import { Packages } from "../types/types";
|
||||
import { debug, DEBUGGING } from "../utils/DebugHelper";
|
||||
import { ExcalidrawLib } from "../../types/excalidrawLib";
|
||||
import { Packages } from "../../types/types";
|
||||
import { debug, DEBUGGING } from "../../utils/debugHelper";
|
||||
import { Notice } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
|
||||
declare let REACT_PACKAGES:string;
|
||||
declare let react:any;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { WorkspaceWindow } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { getAllWindowDocuments } from "./ObsidianUtils";
|
||||
import { DEBUGGING, debug } from "./DebugHelper";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { getAllWindowDocuments } from "../../utils/obsidianUtils";
|
||||
import { DEBUGGING, debug } from "../../utils/debugHelper";
|
||||
|
||||
export let REM_VALUE = 16;
|
||||
|
||||
@@ -10,38 +10,38 @@ import {
|
||||
TextComponent,
|
||||
TFile,
|
||||
} from "obsidian";
|
||||
import { GITHUB_RELEASES, setRootElementSize } from "./constants/constants";
|
||||
import { t } from "./lang/helpers";
|
||||
import type ExcalidrawPlugin from "./main";
|
||||
import { PenStyle } from "./types/PenTypes";
|
||||
import { DynamicStyle, GridSettings } from "./types/types";
|
||||
import { PreviewImageType } from "./utils/UtilTypes";
|
||||
import { setDynamicStyle } from "./utils/DynamicStyling";
|
||||
import { GITHUB_RELEASES, setRootElementSize } from "src/constants/constants";
|
||||
import { t } from "src/lang/helpers";
|
||||
import type ExcalidrawPlugin from "src/core/main";
|
||||
import { PenStyle } from "src/types/penTypes";
|
||||
import { DynamicStyle, GridSettings } from "src/types/types";
|
||||
import { PreviewImageType } from "src/types/utilTypes";
|
||||
import { setDynamicStyle } from "src/utils/dynamicStyling";
|
||||
import {
|
||||
getDrawingFilename,
|
||||
getEmbedFilename,
|
||||
} from "./utils/FileUtils";
|
||||
import { PENS } from "./utils/Pens";
|
||||
} from "src/utils/fileUtils";
|
||||
import { PENS } from "src/utils/pens";
|
||||
import {
|
||||
addIframe,
|
||||
fragWithHTML,
|
||||
setLeftHandedMode,
|
||||
} from "./utils/Utils";
|
||||
import { imageCache } from "./utils/ImageCache";
|
||||
import { ConfirmationPrompt } from "./dialogs/Prompt";
|
||||
import { EmbeddableMDCustomProps } from "./dialogs/EmbeddableSettings";
|
||||
import { EmbeddalbeMDFileCustomDataSettingsComponent } from "./dialogs/EmbeddableMDFileCustomDataSettingsComponent";
|
||||
import { startupScript } from "./constants/starutpscript";
|
||||
import { ModifierKeySet, ModifierSetType } from "./utils/ModifierkeyHelper";
|
||||
import { ModifierKeySettingsComponent } from "./dialogs/ModifierKeySettings";
|
||||
import { ANNOTATED_PREFIX, CROPPED_PREFIX } from "./utils/CarveOut";
|
||||
import { EDITOR_FADEOUT } from "./CodeMirrorExtension/EditorHandler";
|
||||
import { setDebugging } from "./utils/DebugHelper";
|
||||
import { Rank } from "./menu/ActionIcons";
|
||||
} from "src/utils/utils";
|
||||
import { imageCache } from "src/shared/ImageCache";
|
||||
import { ConfirmationPrompt } from "src/shared/Dialogs/Prompt";
|
||||
import { EmbeddableMDCustomProps } from "src/shared/Dialogs/EmbeddableSettings";
|
||||
import { EmbeddalbeMDFileCustomDataSettingsComponent } from "src/shared/Dialogs/EmbeddableMDFileCustomDataSettingsComponent";
|
||||
import { startupScript } from "src/constants/starutpscript";
|
||||
import { ModifierKeySet, ModifierSetType } from "src/utils/modifierkeyHelper";
|
||||
import { ModifierKeySettingsComponent } from "src/shared/Dialogs/ModifierKeySettings";
|
||||
import { ANNOTATED_PREFIX, CROPPED_PREFIX } from "src/utils/carveout";
|
||||
import { EDITOR_FADEOUT } from "src/core/editor/EditorHandler";
|
||||
import { setDebugging } from "src/utils/debugHelper";
|
||||
import { Rank } from "src/constants/actionIcons";
|
||||
import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/constSettingsTags";
|
||||
import { HotkeyEditor } from "./dialogs/HotkeyEditor";
|
||||
import { getExcalidrawViews } from "./utils/ObsidianUtils";
|
||||
import de from "./lang/locale/de";
|
||||
import { HotkeyEditor } from "src/shared/Dialogs/HotkeyEditor";
|
||||
import { getExcalidrawViews } from "src/utils/obsidianUtils";
|
||||
import { createSliderWithText } from "src/utils/sliderUtils";
|
||||
|
||||
export interface ExcalidrawSettings {
|
||||
folder: string;
|
||||
@@ -71,6 +71,7 @@ export interface ExcalidrawSettings {
|
||||
annotatePreserveSize: boolean;
|
||||
displaySVGInPreview: boolean; //No longer used since 1.9.13
|
||||
previewImageType: PreviewImageType; //Introduced with 1.9.13
|
||||
renderingConcurrency: number;
|
||||
allowImageCache: boolean;
|
||||
allowImageCacheInScene: boolean;
|
||||
displayExportedImageIfAvailable: boolean;
|
||||
@@ -249,6 +250,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
annotatePreserveSize: false,
|
||||
displaySVGInPreview: undefined,
|
||||
previewImageType: undefined,
|
||||
renderingConcurrency: 3,
|
||||
allowImageCache: true,
|
||||
allowImageCacheInScene: true,
|
||||
displayExportedImageIfAvailable: false,
|
||||
@@ -1054,55 +1056,6 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
cls: "excalidraw-setting-h1",
|
||||
});
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("DEFAULT_PEN_MODE_NAME"))
|
||||
.setDesc(fragWithHTML(t("DEFAULT_PEN_MODE_DESC")))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("never", "Never")
|
||||
.addOption("mobile", "On Obsidian Mobile")
|
||||
.addOption("always", "Always")
|
||||
.setValue(this.plugin.settings.defaultPenMode)
|
||||
.onChange(async (value: "never" | "always" | "mobile") => {
|
||||
this.plugin.settings.defaultPenMode = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("DISABLE_DOUBLE_TAP_ERASER_NAME"))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.penModeDoubleTapEraser)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.penModeDoubleTapEraser = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("DISABLE_SINGLE_FINGER_PANNING_NAME"))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.penModeSingleFingerPanning)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.penModeSingleFingerPanning = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("SHOW_PEN_MODE_FREEDRAW_CROSSHAIR_NAME"))
|
||||
.setDesc(fragWithHTML(t("SHOW_PEN_MODE_FREEDRAW_CROSSHAIR_DESC")))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.penModeCrosshairVisible)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.penModeCrosshairVisible = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
const readingModeEl = new Setting(detailsEl)
|
||||
.setName(t("SHOW_DRAWING_OR_MD_IN_READING_MODE_NAME"))
|
||||
.setDesc(fragWithHTML(t("SHOW_DRAWING_OR_MD_IN_READING_MODE_DESC")))
|
||||
@@ -1329,27 +1282,77 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
let zoomText: HTMLDivElement;
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("ZOOM_TO_FIT_MAX_LEVEL_NAME"))
|
||||
.setDesc(fragWithHTML(t("ZOOM_TO_FIT_MAX_LEVEL_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(0.5, 10, 0.5)
|
||||
.setValue(this.plugin.settings.zoomToFitMaxLevel)
|
||||
.onChange(async (value) => {
|
||||
zoomText.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.zoomToFitMaxLevel = value;
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("ZOOM_TO_FIT_MAX_LEVEL_NAME"),
|
||||
desc: t("ZOOM_TO_FIT_MAX_LEVEL_DESC"),
|
||||
value: this.plugin.settings.zoomToFitMaxLevel,
|
||||
min: 0.5,
|
||||
max: 10,
|
||||
step: 0.5,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.zoomToFitMaxLevel = value;
|
||||
this.applySettingsUpdate();
|
||||
}
|
||||
})
|
||||
|
||||
// ------------------------------------------------
|
||||
// Pen
|
||||
// ------------------------------------------------
|
||||
detailsEl = displayDetailsEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
text: t("PEN_HEAD"),
|
||||
cls: "excalidraw-setting-h3",
|
||||
});
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("DEFAULT_PEN_MODE_NAME"))
|
||||
.setDesc(fragWithHTML(t("DEFAULT_PEN_MODE_DESC")))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("never", "Never")
|
||||
.addOption("mobile", "On Obsidian Mobile")
|
||||
.addOption("always", "Always")
|
||||
.setValue(this.plugin.settings.defaultPenMode)
|
||||
.onChange(async (value: "never" | "always" | "mobile") => {
|
||||
this.plugin.settings.defaultPenMode = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
zoomText = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.zoomToFitMaxLevel.toString()}`;
|
||||
});
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("DISABLE_DOUBLE_TAP_ERASER_NAME"))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.penModeDoubleTapEraser)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.penModeDoubleTapEraser = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("DISABLE_SINGLE_FINGER_PANNING_NAME"))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.penModeSingleFingerPanning)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.penModeSingleFingerPanning = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("SHOW_PEN_MODE_FREEDRAW_CROSSHAIR_NAME"))
|
||||
.setDesc(fragWithHTML(t("SHOW_PEN_MODE_FREEDRAW_CROSSHAIR_DESC")))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.penModeCrosshairVisible)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.penModeCrosshairVisible = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
// ------------------------------------------------
|
||||
// Grid
|
||||
@@ -1398,28 +1401,20 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
);
|
||||
|
||||
// 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}`;
|
||||
});
|
||||
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("GRID_OPACITY_NAME"),
|
||||
desc: t("GRID_OPACITY_DESC"),
|
||||
value: this.plugin.settings.gridSettings.OPACITY,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.gridSettings.OPACITY = value;
|
||||
this.applySettingsUpdate();
|
||||
updateGridColor();
|
||||
},
|
||||
minWidth: "3em",
|
||||
})
|
||||
|
||||
// ------------------------------------------------
|
||||
// Laser Pointer
|
||||
@@ -1440,47 +1435,33 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
let decayTime: HTMLDivElement;
|
||||
new Setting(detailsEl)
|
||||
.setName(t("LASER_DECAY_TIME_NAME"))
|
||||
.setDesc(fragWithHTML(t("LASER_DECAY_TIME_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(500, 20000, 500)
|
||||
.setValue(this.plugin.settings.laserSettings.DECAY_TIME)
|
||||
.onChange(async (value) => {
|
||||
decayTime.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.laserSettings.DECAY_TIME = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
decayTime = el;
|
||||
el.style.minWidth = "3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.laserSettings.DECAY_TIME.toString()}`;
|
||||
});
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("LASER_DECAY_TIME_NAME"),
|
||||
desc: t("LASER_DECAY_TIME_DESC"),
|
||||
value: this.plugin.settings.laserSettings.DECAY_TIME,
|
||||
min: 500,
|
||||
max: 20000,
|
||||
step: 500,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.laserSettings.DECAY_TIME = value;
|
||||
this.applySettingsUpdate();
|
||||
},
|
||||
minWidth: "3em",
|
||||
})
|
||||
|
||||
let decayLength: HTMLDivElement;
|
||||
new Setting(detailsEl)
|
||||
.setName(t("LASER_DECAY_LENGTH_NAME"))
|
||||
.setDesc(fragWithHTML(t("LASER_DECAY_LENGTH_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(25, 2000, 25)
|
||||
.setValue(this.plugin.settings.laserSettings.DECAY_LENGTH)
|
||||
.onChange(async (value) => {
|
||||
decayLength.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.laserSettings.DECAY_LENGTH = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
decayLength = el;
|
||||
el.style.minWidth = "3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.laserSettings.DECAY_LENGTH.toString()}`;
|
||||
});
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("LASER_DECAY_LENGTH_NAME"),
|
||||
desc: t("LASER_DECAY_LENGTH_DESC"),
|
||||
value: this.plugin.settings.laserSettings.DECAY_LENGTH,
|
||||
min: 25,
|
||||
max: 2000,
|
||||
step: 25,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.laserSettings.DECAY_LENGTH = value;
|
||||
this.applySettingsUpdate();
|
||||
},
|
||||
minWidth: "3em",
|
||||
})
|
||||
|
||||
detailsEl = displayDetailsEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
@@ -1489,47 +1470,31 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
});
|
||||
detailsEl.createDiv({ text: t("DRAG_MODIFIER_DESC"), cls: "setting-item-description" });
|
||||
|
||||
let longPressDesktop: HTMLDivElement;
|
||||
new Setting(detailsEl)
|
||||
.setName(t("LONG_PRESS_DESKTOP_NAME"))
|
||||
.setDesc(fragWithHTML(t("LONG_PRESS_DESKTOP_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(300, 3000, 100)
|
||||
.setValue(this.plugin.settings.longPressDesktop)
|
||||
.onChange(async (value) => {
|
||||
longPressDesktop.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.longPressDesktop = value;
|
||||
this.applySettingsUpdate(true);
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
longPressDesktop = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.longPressDesktop.toString()}`;
|
||||
});
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("LONG_PRESS_DESKTOP_NAME"),
|
||||
desc: t("LONG_PRESS_DESKTOP_DESC"),
|
||||
value: this.plugin.settings.longPressDesktop,
|
||||
min: 300,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.longPressDesktop = value;
|
||||
this.applySettingsUpdate(true);
|
||||
},
|
||||
})
|
||||
|
||||
let longPressMobile: HTMLDivElement;
|
||||
new Setting(detailsEl)
|
||||
.setName(t("LONG_PRESS_MOBILE_NAME"))
|
||||
.setDesc(fragWithHTML(t("LONG_PRESS_MOBILE_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(300, 3000, 100)
|
||||
.setValue(this.plugin.settings.longPressMobile)
|
||||
.onChange(async (value) => {
|
||||
longPressMobile.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.longPressMobile = value;
|
||||
this.applySettingsUpdate(true);
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
longPressMobile = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.longPressMobile.toString()}`;
|
||||
});
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("LONG_PRESS_MOBILE_NAME"),
|
||||
desc: t("LONG_PRESS_MOBILE_DESC"),
|
||||
value: this.plugin.settings.longPressMobile,
|
||||
min: 300,
|
||||
max: 3000,
|
||||
step: 100,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.longPressMobile = value;
|
||||
this.applySettingsUpdate(true);
|
||||
},
|
||||
})
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("DOUBLE_CLICK_LINK_OPEN_VIEW_MODE"))
|
||||
@@ -1701,26 +1666,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
);
|
||||
donePrefixSetting.setDisabled(!this.plugin.settings.parseTODO);
|
||||
|
||||
let opacityText: HTMLDivElement;
|
||||
new Setting(detailsEl)
|
||||
.setName(t("LINKOPACITY_NAME"))
|
||||
.setDesc(fragWithHTML(t("LINKOPACITY_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(0, 1, 0.05)
|
||||
.setValue(this.plugin.settings.linkOpacity)
|
||||
.onChange(async (value) => {
|
||||
opacityText.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.linkOpacity = value;
|
||||
this.applySettingsUpdate(true);
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
opacityText = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.linkOpacity.toString()}`;
|
||||
});
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("LINKOPACITY_NAME"),
|
||||
desc: t("LINKOPACITY_DESC"),
|
||||
value: this.plugin.settings.linkOpacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.linkOpacity = value;
|
||||
this.applySettingsUpdate(true);
|
||||
},
|
||||
});
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("HOVERPREVIEW_NAME"))
|
||||
@@ -1954,6 +1911,19 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
cls: "excalidraw-setting-h3",
|
||||
});
|
||||
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("RENDERING_CONCURRENCY_NAME"),
|
||||
desc: t("RENDERING_CONCURRENCY_DESC"),
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
value: this.plugin.settings.renderingConcurrency,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.renderingConcurrency = value;
|
||||
this.applySettingsUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("EMBED_IMAGE_CACHE_NAME"))
|
||||
.setDesc(fragWithHTML(t("EMBED_IMAGE_CACHE_DESC")))
|
||||
@@ -2078,49 +2048,31 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
let scaleText: HTMLDivElement;
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("EXPORT_PNG_SCALE_NAME"),
|
||||
desc: t("EXPORT_PNG_SCALE_DESC"),
|
||||
value: this.plugin.settings.pngExportScale,
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 0.5,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.pngExportScale = value;
|
||||
this.applySettingsUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("EXPORT_PNG_SCALE_NAME"))
|
||||
.setDesc(fragWithHTML(t("EXPORT_PNG_SCALE_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(1, 5, 0.5)
|
||||
.setValue(this.plugin.settings.pngExportScale)
|
||||
.onChange(async (value) => {
|
||||
scaleText.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.pngExportScale = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
scaleText = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.pngExportScale.toString()}`;
|
||||
});
|
||||
|
||||
let exportPadding: HTMLDivElement;
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("EXPORT_PADDING_NAME"))
|
||||
.setDesc(fragWithHTML(t("EXPORT_PADDING_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(0, 50, 5)
|
||||
.setValue(this.plugin.settings.exportPaddingSVG)
|
||||
.onChange(async (value) => {
|
||||
exportPadding.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.exportPaddingSVG = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
exportPadding = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.exportPaddingSVG.toString()}`;
|
||||
});
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("EXPORT_PADDING_NAME"),
|
||||
desc: fragWithHTML(t("EXPORT_PADDING_DESC")),
|
||||
value: this.plugin.settings.exportPaddingSVG,
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 5,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.exportPaddingSVG = value;
|
||||
this.applySettingsUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
detailsEl = exportDetailsEl.createEl("details");
|
||||
detailsEl.createEl("summary", {
|
||||
@@ -2461,27 +2413,19 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
cls: "excalidraw-setting-h3",
|
||||
});
|
||||
|
||||
let areaZoomText: HTMLDivElement;
|
||||
|
||||
new Setting(detailsEl)
|
||||
.setName(t("MAX_IMAGE_ZOOM_IN_NAME"))
|
||||
.setDesc(fragWithHTML(t("MAX_IMAGE_ZOOM_IN_DESC")))
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(1, 10, 0.5)
|
||||
.setValue(this.plugin.settings.areaZoomLimit)
|
||||
.onChange(async (value) => {
|
||||
areaZoomText.innerText = ` ${value.toString()}`;
|
||||
this.plugin.settings.areaZoomLimit = value;
|
||||
this.applySettingsUpdate();
|
||||
this.plugin.excalidrawConfig.updateValues(this.plugin);
|
||||
}),
|
||||
)
|
||||
.settingEl.createDiv("", (el) => {
|
||||
areaZoomText = el;
|
||||
el.style.minWidth = "2.3em";
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.areaZoomLimit.toString()}`;
|
||||
createSliderWithText(detailsEl, {
|
||||
name: t("MAX_IMAGE_ZOOM_IN_NAME"),
|
||||
desc: fragWithHTML(t("MAX_IMAGE_ZOOM_IN_DESC")),
|
||||
value: this.plugin.settings.areaZoomLimit,
|
||||
min: 1,
|
||||
max: 10,
|
||||
step: 0.5,
|
||||
onChange: (value) => {
|
||||
this.plugin.settings.areaZoomLimit = value;
|
||||
this.applySettingsUpdate();
|
||||
this.plugin.excalidrawConfig.updateValues(this.plugin);
|
||||
},
|
||||
});
|
||||
|
||||
detailsEl = nonstandardDetailsEl.createEl("details");
|
||||
@@ -1,133 +0,0 @@
|
||||
export const FIRST_RUN = `
|
||||
The Excalidraw Obsidian plugin is much more than "just" a drawing tool. To help you get started here's a showcase of the key Excalidraw plugin features.
|
||||
|
||||
If you'd like to learn more, please subscribe to my YouTube channel: [Visual PKM](https://www.youtube.com/channel/UCC0gns4a9fhVkGkngvSumAQ) where I regularly share videos about Obsidian-Excalidraw and about tools and techniques for Visual Personal Knowledge Management.
|
||||
|
||||
Thank you & Enjoy!
|
||||
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/P_Q6avJGoWI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
`;
|
||||
|
||||
export const RELEASE_NOTES: { [k: string]: string } = {
|
||||
Intro: `After each update you'll be prompted with the release notes. You can disable this in plugin settings.
|
||||
|
||||
I develop this plugin as a hobby, spending my free time doing this. If you find it valuable, then please say THANK YOU or...
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://storage.ko-fi.com/cdn/kofi6.png?v=6" border="0" alt="Buy Me a Coffee at ko-fi.com" height=45></a></div>
|
||||
`,
|
||||
"2.7.1":`
|
||||
## Fixed
|
||||
- Deleting excalidraw file from file system while it is open in fullscreen mode in Obsidian causes Obsidian to be stuck in full-screen view [#2161](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2161)
|
||||
- Chinese fonts are not rendered in LaTeX statements [#2162](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2162)
|
||||
- Since Electron 32 (newer Obsidian Desktop installers) drag and drop links from Finder or OS File Explorer did not work. [Electron breaking change](https://www.electronjs.org/docs/latest/breaking-changes#removed-filepath). This is now fixed
|
||||
- Addressed unnecessary image reloads when changing windows in Obsidian
|
||||
`,
|
||||
"2.7.0":`
|
||||
## Fixed
|
||||
- Various Markdown embeddable "fuzziness":
|
||||
- Fixed issues with appearance settings and edit mode toggling when single-click editing is enabled.
|
||||
- Ensured embeddable file editing no longer gets interrupted unexpectedly.
|
||||
- **Hover Preview**: Disabled hover preview for back-of-the-note cards to reduce distractions.
|
||||
- **Settings Save**: Fixed an issue where plugin settings unnecessarily saved on every startup.
|
||||
|
||||
## New Features
|
||||
- **Image Cropping Snaps to Objects**: When snapping is enabled in the scene, image cropping now aligns to nearby objects.
|
||||
- **Session Persistence for Pen Mode**: Excalidraw remembers the last pen mode when switching between drawings within the same session.
|
||||
|
||||
## Refactoring
|
||||
- **Mermaid Diagrams**: Excalidraw now uses its own Mermaid package, breaking future dependencies on Obsidian's Mermaid updates. This ensures stability and includes all fixes and improvements made to Excalidraw Mermaid since February 2024. The plugin file size has increased slightly, but this change significantly improves maintainability while remaining invisible to users.
|
||||
- **MathJax Optimization**: MathJax (LaTeX equation SVG image generation) now loads only on demand, with the package compressed to minimize the startup and file size impact caused by the inclusion of Mermaid.
|
||||
- **On-Demand Language Loading**: Non-English language files are now compressed and load only when needed, counterbalancing the increase in file size due to Mermaid and improving load speeds.
|
||||
- **Codebase Restructuring**: Improved type safety by removing many ${String.fromCharCode(96)}//@ts-ignore${String.fromCharCode(96)} commands and enhancing modularity. Introduced new management classes: **CommandManager**, **EventManager**, **PluginFileManager**, **ObserverManager**, and **PackageManager**. Further restructuring is planned for upcoming releases to improve maintainability and stability.
|
||||
`,
|
||||
"2.6.8":`
|
||||
## New
|
||||
- **QoL improvements**:
|
||||
- Obsidian-link search button in Element Link Editor.
|
||||
- Add Any File now searches file aliases as well.
|
||||
- Cosmetic changes to file search modals (display path, show file type icon).
|
||||
- Text Element cursor-color matches the text color.
|
||||
- New script in script store: [Image Occlusion](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Image%20Occlusion.md) by [@TrillStones](https://github.com/TrillStones) 🙏
|
||||
|
||||
## Fixed
|
||||
- Excalidraw icon on the **ribbon menu kept reappearing** every time you reopen Obsidian [#2115](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2115)
|
||||
- In pen mode, when **single-finger panning** is enabled, Excalidraw should still **allow actions with the mouse**.
|
||||
- When **editing a drawing in split mode** (drawing is on one side, markdown view is on the other), editing the markdown note sometimes causes the drawing to re-zoom and jump away from the selected area.
|
||||
- Hover-Editor compatibility resolved [2041](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2041)
|
||||
- ${String.fromCharCode(96)}ExcalidrawAutomate.create() ${String.fromCharCode(96)} will now correctly include the markdown text in templates above Excalidraw Data and below YAML front matter. This also fixes the same issue with the **Deconstruct Selected Element script**.
|
||||
|
||||
`,
|
||||
"2.6.7":`
|
||||
Hoping to finally move on to 2.7.0... but still have one last bug to fix in 2.6.x!
|
||||
|
||||
## Fixed
|
||||
I misread a line in the Excalidraw package code... ended up breaking image loading in 2.6.6. The icon library script didn't work right, and updating nested drawings caused all images in the scene to be dropped from memory. This led to image-placeholders in exports and broke copy-paste to Excalidraw.com and between drawings. I am surprised no one reported it! 😳
|
||||
`,
|
||||
"2.6.6":`
|
||||
## Fixed
|
||||
- Images and LaTeX formulas did not update in the scene when the source was changed until the Excalidraw drawing was closed and reopened. [#2105](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2105)
|
||||
`,
|
||||
"2.6.5":`
|
||||
## Fixed
|
||||
- Text sizing issue in the drawing that is first loaded after Obsidian restarts [#2086](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2086)
|
||||
- Excalidraw didn't load if there was a file in the Excalidraw folder with a name that starts the same way as the Scripts folder name. [#2095](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2095)
|
||||
- **OVERSIZED EXCALIDRAW TOOLBAR**: Added a new setting under "Excalidraw Appearance and Behavior > Theme and Styling" called "Limit Obsidian Font Size to Editor Text." This setting is off by default. When enabled, it restricts Obsidian's custom font size adjustments to editor text only, preventing unintended scaling of Excalidraw UI elements and other themes that rely on the default interface font size. Feel free to experiment with this setting to improve Excalidraw UI consistency. However, because this change affects the broader Obsidian UI, it's recommended to turn it off if any layout issues arise. [#2087](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2087)`,
|
||||
"2.6.4":`
|
||||
## Fixed
|
||||
- Error saving when cropping images embedded from a URL (not from a file in the Vault) [#2096](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2096)
|
||||
`,
|
||||
"2.6.3":`
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/OfUWAvCgbXk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
## New
|
||||
- **Cropping PDF Pages**
|
||||
- Improved PDF++ cropping: You can now double-click cropped images in Excalidraw to adjust the crop area, which will also appear as a highlight in PDF++. This feature applies to PDF cut-outs created in version 2.6.3 and beyond.
|
||||
- **Insert Last Active PDF Page as Image**
|
||||
- New command palette action lets you insert the currently active PDF page into Excalidraw. Ideal for setups with PDF and Excalidraw side-by-side. You can assign a hotkey for quicker access. Cropped areas in Excalidraw will show as highlights in PDF++.
|
||||
|
||||
## Fixed
|
||||
- Fixed **Close Settings** button toggle behavior [#2085](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2085)
|
||||
- Resolved text wrapping issues causing layout shifts due to trailing whitespaces [#8714](https://github.com/excalidraw/excalidraw/pull/8714)
|
||||
- **Aspect Ratio and Size Reset** commands now function correctly with cropped images.
|
||||
- **Cropped Drawings**: Adjustments to cropped Excalidraw drawings are now supported. However, for nested Excalidraw drawings, it's recommended to use area, group, and frame references instead of cropping.
|
||||
|
||||
## Refactoring
|
||||
- Further font loading optimizations on Excalidraw.com; no impact expected in Obsidian [#8693](https://github.com/excalidraw/excalidraw/pull/8693)
|
||||
- Text wrapping improvements [#8715](https://github.com/excalidraw/excalidraw/pull/8715)
|
||||
- Plugin initiation and error handling
|
||||
`,
|
||||
"2.6.2":`
|
||||
## Fixed
|
||||
- Image scaling issue with SVGs that miss the width and height property. [#8729](https://github.com/excalidraw/excalidraw/issues/8729)
|
||||
`,
|
||||
"2.6.1":`
|
||||
## New
|
||||
- Pen-mode single-finger panning enabled also for the "Selection" tool.
|
||||
- You can disable pen-mode single-finger panning in Plugin Settings under Excalidraw Appearance and Behavior [#2080](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2080)
|
||||
|
||||
## Fixed
|
||||
- Text tool did not work in pen-mode using finger [#2080](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2080)
|
||||
- Pasting images to Excalidraw from the web resulted in filenames of "image_1.png", "image_2.png" instead of "Pasted Image TIMESTAMP" [#2081](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2081)
|
||||
`,
|
||||
"2.6.0":`
|
||||
## Performance
|
||||
- Much faster plugin initialization. Down from 1000-3000ms to 100-300ms. According to my testing speed varies on a wide spectrum depending on device, size of Vault and other plugins being loaded. I measured values ranging from 84ms up to 782ms [#2068](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2068)
|
||||
- Faster loading of scenes with many embedded illustrations or PDF pages.
|
||||
- SVG export results in even smaller files by further optimizing which characters are included in the embedded fonts. [#8641](https://github.com/excalidraw/excalidraw/pull/8641)
|
||||
|
||||
## New
|
||||
- Image cropping tool. Double click the image to crop it. [#8613](https://github.com/excalidraw/excalidraw/pull/8613)
|
||||
- Single finger panning in pen mode.
|
||||
- Native handwritten CJK Font support [8530](https://github.com/excalidraw/excalidraw/pull/8530)
|
||||
- Created a new **Fonts** section in settings. This includes configuration of the "Local Font" and downloading of the CJK fonts in case you need them offline.
|
||||
- Option under **Appearance and Behavior / Link Click** to disable double-click link navigation in view mode. [#2075](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2075)
|
||||
- New RU translation 🙏[@tovBender](https://github.com/tovBender)
|
||||
|
||||
## Updated
|
||||
- CN translation 🙏[@dmscode](https://github.com/dmscode)
|
||||
`,
|
||||
};
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
CJK_FONTS,
|
||||
} from "src/constants/constants";
|
||||
import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/constSettingsTags";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/modifierkeyHelper";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
@@ -30,6 +30,7 @@ export default {
|
||||
"Script is up to date - Click to reinstall",
|
||||
OPEN_AS_EXCALIDRAW: "Open as Excalidraw Drawing",
|
||||
TOGGLE_MODE: "Toggle between Excalidraw and Markdown mode",
|
||||
DUPLICATE_IMAGE: "Duplicate selected image with a different image ID",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "Convert markdown note to Excalidraw Drawing",
|
||||
CONVERT_EXCALIDRAW: "Convert *.excalidraw to *.md files",
|
||||
CREATE_NEW: "Create new drawing",
|
||||
@@ -416,6 +417,7 @@ 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%).",
|
||||
PEN_HEAD: "Pen",
|
||||
GRID_HEAD: "Grid",
|
||||
GRID_DYNAMIC_COLOR_NAME: "Dynamic grid color",
|
||||
GRID_DYNAMIC_COLOR_DESC:
|
||||
@@ -578,7 +580,11 @@ FILENAME_HEAD: "Filename",
|
||||
EMBED_CANVAS_DESC:
|
||||
"Hide canvas node border and background when embedding an Excalidraw drawing to Canvas. " +
|
||||
"Note that for a full transparent background for your image, you will still need to configure Excalidraw to export images with transparent background.",
|
||||
EMBED_CACHING: "Image caching",
|
||||
EMBED_CACHING: "Image caching and rendering optimization",
|
||||
RENDERING_CONCURRENCY_NAME: "Image rendering concurrency",
|
||||
RENDERING_CONCURRENCY_DESC:
|
||||
"Number of parallel workers to use for image rendering. Increasing this number will speed up the rendering process, but may slow down the rest of the system. " +
|
||||
"The default value is 3. You can increase this number if you have a powerful system.",
|
||||
EXPORT_SUBHEAD: "Export Settings",
|
||||
EMBED_SIZING: "Image sizing",
|
||||
EMBED_THEME_BACKGROUND: "Image theme and background color",
|
||||
@@ -975,7 +981,7 @@ FILENAME_HEAD: "Filename",
|
||||
UPDATE_AVAILABLE: `A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${PLUGIN_VERSION}.\nThe latest is`,
|
||||
ERROR_PNG_TOO_LARGE: "Error exporting PNG - PNG too large, try a smaller resolution",
|
||||
|
||||
//ModifierkeyHelper.ts
|
||||
//modifierkeyHelper.ts
|
||||
// WebBrowserDragAction
|
||||
WEB_DRAG_IMPORT_IMAGE: "Import Image to Vault",
|
||||
WEB_DRAG_IMAGE_URL: "Insert Image or YouTube Thumbnail with URL",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DEVICE, FRONTMATTER_KEYS, CJK_FONTS } from "src/constants/constants";
|
||||
import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/constSettingsTags";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/modifierkeyHelper";
|
||||
|
||||
// русский
|
||||
export default {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { DEVICE, FRONTMATTER_KEYS, CJK_FONTS } from "src/constants/constants";
|
||||
import {
|
||||
DEVICE,
|
||||
FRONTMATTER_KEYS,
|
||||
CJK_FONTS
|
||||
} from "src/constants/constants";
|
||||
import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/constSettingsTags";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/modifierkeyHelper";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
// 简体中文
|
||||
@@ -25,6 +30,7 @@ export default {
|
||||
"脚本已是最新 - 点击重新安装",
|
||||
OPEN_AS_EXCALIDRAW: "打开为 Excalidraw 绘图",
|
||||
TOGGLE_MODE: "在 Excalidraw 和 Markdown 模式之间切换",
|
||||
DUPLICATE_IMAGE : "复制选定的图像,并分配一个不同的图像 ID",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "转换:空白 Markdown 文档 => Excalidraw 绘图文件",
|
||||
CONVERT_EXCALIDRAW: "转换: *.excalidraw => *.md",
|
||||
CREATE_NEW: "新建绘图文件",
|
||||
@@ -105,6 +111,7 @@ export default {
|
||||
SELECT_LINK_TO_OPEN: "选择要打开的链接",
|
||||
|
||||
//ExcalidrawView.ts
|
||||
ERROR_CANT_READ_FILEPATH : "错误,无法读取文件路径。正在改为导入文件",
|
||||
NO_SEARCH_RESULT: "在绘图中未找到匹配的元素",
|
||||
FORCE_SAVE_ABORTED: "自动保存被中止,因为文件正在保存中",
|
||||
LINKLIST_SECOND_ORDER_LINK: "二级链接",
|
||||
@@ -190,7 +197,7 @@ export default {
|
||||
NEWVERSION_NOTIFICATION_DESC:
|
||||
"<b>开启:</b>当本插件存在可用更新时,显示通知。<br>" +
|
||||
"<b>关闭:</b>您需要手动检查本插件的更新(设置 - 第三方插件 - 检查更新)。",
|
||||
|
||||
|
||||
BASIC_HEAD: "基本",
|
||||
BASIC_DESC: `包括:更新说明,更新提示,新绘图文件、模板文件、脚本文件的存储路径等的设置。`,
|
||||
FOLDER_NAME: "Excalidraw 文件夹(區分大小寫!)",
|
||||
@@ -400,7 +407,7 @@ FILENAME_HEAD: "文件名",
|
||||
DEFAULT_WHEELZOOM_NAME: "鼠标滚轮缩放页面",
|
||||
DEFAULT_WHEELZOOM_DESC:
|
||||
`<b>开启:</b>鼠标滚轮为缩放页面,${labelCTRL()}+鼠标滚轮为滚动页面</br><b>关闭:</b>鼠标滚轮为滚动页面,${labelCTRL()}+鼠标滚轮为缩放页面`,
|
||||
|
||||
|
||||
ZOOM_TO_FIT_NAME: "调节面板尺寸后自动缩放页面",
|
||||
ZOOM_TO_FIT_DESC: "调节面板尺寸后,自适应地缩放页面" +
|
||||
"<br><b>开启:</b>自动缩放。<br><b>关闭:</b>禁用自动缩放。",
|
||||
@@ -410,6 +417,7 @@ FILENAME_HEAD: "文件名",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_NAME: "自动缩放的最高级别",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_DESC:
|
||||
"自动缩放画布时,允许放大的最高级别。该值不能低于 0.5(50%)且不能超过 10(1000%)。",
|
||||
PEN_HEAD: "手写笔",
|
||||
GRID_HEAD: "网格",
|
||||
GRID_DYNAMIC_COLOR_NAME: "动态网格颜色",
|
||||
GRID_DYNAMIC_COLOR_DESC:
|
||||
@@ -569,10 +577,14 @@ FILENAME_HEAD: "文件名",
|
||||
此外,还有自动导出 SVG 或 PNG 文件并保持与绘图文件状态同步的设置。`,
|
||||
EMBED_CANVAS: "Obsidian 白板支持",
|
||||
EMBED_CANVAS_NAME: "沉浸式嵌入",
|
||||
EMBED_CANVAS_DESC:
|
||||
EMBED_CANVAS_DESC:
|
||||
"当嵌入绘图到 Obsidian 白板中时,隐藏元素的边界和背景。" +
|
||||
"注意:如果想要背景完全透明,您依然需要在 Excalidraw 中设置“导出的图像不包含背景”。",
|
||||
EMBED_CACHING: "预览图缓存",
|
||||
EMBED_CACHING : "图像缓存和渲染优化" ,
|
||||
RENDERING_CONCURRENCY_NAME : "图像渲染并发性" ,
|
||||
RENDERING_CONCURRENCY_DESC :
|
||||
"用于图像渲染的并行工作线程数。增加此数值可以加快渲染速度,但可能会减慢系统的其他部分运行速度。" +
|
||||
"默认值为 3。如果您的系统性能强大,可以增加此数值。" ,
|
||||
EXPORT_SUBHEAD: "导出",
|
||||
EMBED_SIZING: "图像尺寸",
|
||||
EMBED_THEME_BACKGROUND: "图像的主题和背景色",
|
||||
@@ -580,7 +592,7 @@ FILENAME_HEAD: "文件名",
|
||||
EMBED_IMAGE_CACHE_DESC: "可提高下次嵌入的速度。" +
|
||||
"但如果绘图中又嵌入了子绘图,当子绘图改变时,您需要打开子绘图并手动保存,才能够更新父绘图的预览图。",
|
||||
SCENE_IMAGE_CACHE_NAME: "缓存场景中嵌套的 Excalidraw",
|
||||
SCENE_IMAGE_CACHE_DESC: "缓存场景中嵌套的 Excalidraw 以加快场景渲染速度。这将加快渲染过程,特别是在您的场景中有深度嵌套的 Excalidraw 时。" +
|
||||
SCENE_IMAGE_CACHE_DESC: "缓存场景中嵌套的 Excalidraw 以加快场景渲染速度。这将加快渲染过程,特别是在您的场景中有深度嵌套的 Excalidraw 时。" +
|
||||
"Excalidraw 将智能地尝试识别嵌套 Excalidraw 的子元素是否发生变化,并更新缓存。 " +
|
||||
"如果您怀疑缓存未能正确更新,您可能需要关闭此功能。",
|
||||
EMBED_IMAGE_CACHE_CLEAR: "清除缓存",
|
||||
@@ -624,7 +636,7 @@ FILENAME_HEAD: "文件名",
|
||||
"如果您选择了 PNG 或 SVG 副本,当副本不存在时,该命令将会插入一条损坏的链接,您需要打开绘图文件并手动导出副本才能修复 —— " +
|
||||
"也就是说,该选项不会自动帮您生成 PNG/SVG 副本,而只会引用已有的 PNG/SVG 副本。",
|
||||
EMBED_MARKDOWN_COMMENT_NAME: "将链接作为注释嵌入",
|
||||
EMBED_MARKDOWN_COMMENT_DESC:
|
||||
EMBED_MARKDOWN_COMMENT_DESC:
|
||||
"在图像下方以 Markdown 链接的形式嵌入原始 Excalidraw 文件的链接,例如:<code>%%[[drawing.excalidraw]]%%</code>。<br>" +
|
||||
"除了添加 Markdown 注释之外,您还可以选择嵌入的 SVG 或 PNG,并使用命令面板:" +
|
||||
"'<code>Excalidraw: 打开 Excalidraw 绘图</code>'来打开该绘图",
|
||||
@@ -703,7 +715,7 @@ FILENAME_HEAD: "文件名",
|
||||
"文件浏览器等创建的绘图都将是旧格式(*.excalidraw)。" +
|
||||
"此外,您打开旧格式绘图文件时将不再收到警告消息。",
|
||||
MATHJAX_NAME: "MathJax (LaTeX) 的 javascript 库服务器",
|
||||
MATHJAX_DESC: "如果您在绘图中使用 LaTeX,插件需要从服务器获取并加载一个 javascript 库。" +
|
||||
MATHJAX_DESC: "如果您在绘图中使用 LaTeX,插件需要从服务器获取并加载一个 javascript 库。" +
|
||||
"如果您的网络无法访问某些库服务器,可以尝试通过此选项更换库服务器。"+
|
||||
"更改此选项后,您可能需要重启 Obsidian 来使其生效。",
|
||||
LATEX_DEFAULT_NAME: "插入 LaTeX 时的默认表达式",
|
||||
@@ -722,7 +734,7 @@ FILENAME_HEAD: "文件名",
|
||||
EXPERIMENTAL_HEAD: "杂项",
|
||||
EXPERIMENTAL_DESC: `包括:默认的 LaTeX 公式,字段建议,绘图文件的类型标识符,OCR 等设置。`,
|
||||
EA_HEAD: "Excalidraw 自动化",
|
||||
EA_DESC:
|
||||
EA_DESC:
|
||||
"ExcalidrawAutomate 是用于 Excalidraw 自动化脚本的 API,但是目前说明文档还不够完善," +
|
||||
"建议阅读 <a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/docs/API/ExcalidrawAutomate.d.ts'>ExcalidrawAutomate.d.ts</a> 文件源码," +
|
||||
"参考 <a href='https://zsviczian.github.io/obsidian-excalidraw-plugin/'>ExcalidrawAutomate How-to</a> 网页(不过该网页" +
|
||||
@@ -785,7 +797,7 @@ FILENAME_HEAD: "文件名",
|
||||
CJK_ASSETS_FOLDER_NAME: "CJK 字体文件夹(區分大小寫!)",
|
||||
CJK_ASSETS_FOLDER_DESC: `您可以在此设置 CJK 字体文件夹的位置。例如,您可以选择将其放置在 <code>Excalidraw/CJK Fonts</code> 下。<br><br>
|
||||
<strong>重要:</strong> 请勿将此文件夹设置为 Vault 根目录!请勿在此文件夹中放置其他字体。<br><br>
|
||||
<strong>注意:</strong> 如果您使用 Obsidian Sync 并希望在设备之间同步这些字体文件,请确保 Obsidian Sync 设置为同步“所有其他文件类型”。`,
|
||||
<strong>注意:</strong> 如果您使用 Obsidian Sync 并希望在设备之间同步这些字体文件,请确保 Obsidian Sync 设置为同步“所有其他文件类型”。`,
|
||||
LOAD_CHINESE_FONTS_NAME: "启动时从文件加载中文字体",
|
||||
LOAD_JAPANESE_FONTS_NAME: "启动时从文件加载日文字体",
|
||||
LOAD_KOREAN_FONTS_NAME: "启动时从文件加载韩文字体",
|
||||
@@ -800,7 +812,7 @@ FILENAME_HEAD: "文件名",
|
||||
TASKBONE_ENABLE_DESC: "启用意味着您同意 Taskbone <a href='https://www.taskbone.com/legal/terms/' target='_blank'>条款及细则</a> 以及 " +
|
||||
"<a href='https://www.taskbone.com/legal/privacy/' target='_blank'>隐私政策</a>。",
|
||||
TASKBONE_APIKEY_NAME: "Taskbone API Key",
|
||||
TASKBONE_APIKEY_DESC: "Taskbone 的免费 API key 提供了一定数量的每月识别次数。如果您非常频繁地使用此功能,或者想要支持 " +
|
||||
TASKBONE_APIKEY_DESC: "Taskbone 的免费 API key 提供了一定数量的每月识别次数。如果您非常频繁地使用此功能,或者想要支持 " +
|
||||
"Taskbone 的开发者(您懂的,没有人能用爱发电,Taskbone 开发者也需要投入资金来维持这项 OCR 服务)您可以" +
|
||||
"到 <a href='https://www.taskbone.com/' target='_blank'>taskbone.com</a> 购买一个商用 API key。购买后请将它填写到旁边这个文本框里,替换掉原本自动生成的免费 API key。",
|
||||
|
||||
@@ -968,4 +980,30 @@ FILENAME_HEAD: "文件名",
|
||||
//Utils.ts
|
||||
UPDATE_AVAILABLE: `Excalidraw 的新版本已在社区插件中可用。\n\n您正在使用 ${PLUGIN_VERSION}。\n最新版本是`,
|
||||
ERROR_PNG_TOO_LARGE: "导出 PNG 时出错 - PNG 文件过大,请尝试较小的分辨率",
|
||||
};
|
||||
|
||||
// ModifierkeyHelper.ts
|
||||
// WebBrowserDragAction
|
||||
WEB_DRAG_IMPORT_IMAGE : "导入图片到 Vault" ,
|
||||
WEB_DRAG_IMAGE_URL : "通过 URL 插入图片或 YouTube 缩略图" ,
|
||||
WEB_DRAG_LINK : "插入链接" ,
|
||||
WEB_DRAG_EMBEDDABLE : "插入交互框架" ,
|
||||
|
||||
// LocalFileDragAction
|
||||
LOCAL_DRAG_IMPORT : "导入外部文件,或在路径来自 Vault 时复用现有文件" ,
|
||||
LOCAL_DRAG_IMAGE : "插入图片:使用本地 URI,或在路径来自 Vault 时使用内部链接" ,
|
||||
LOCAL_DRAG_LINK : "插入链接:使用本地 URI,或在路径来自 Vault 时使用内部链接" ,
|
||||
LOCAL_DRAG_EMBEDDABLE : "插入交互框架:使用本地 URI,或在路径来自 Vault 时使用内部链接" ,
|
||||
|
||||
// InternalDragAction
|
||||
INTERNAL_DRAG_IMAGE : "插入图片" ,
|
||||
INTERNAL_DRAG_IMAGE_FULL : "插入图片(100% 尺寸)" ,
|
||||
INTERNAL_DRAG_LINK : "插入链接" ,
|
||||
INTERNAL_DRAG_EMBEDDABLE : "插入交互框架" ,
|
||||
|
||||
// LinkClickAction
|
||||
LINK_CLICK_ACTIVE : "在当前活动窗口中打开" ,
|
||||
LINK_CLICK_NEW_PANE : "在相邻的新窗口中打开" ,
|
||||
LINK_CLICK_POPOUT : "在弹出窗口中打开" ,
|
||||
LINK_CLICK_NEW_TAB : "在新标签页中打开" ,
|
||||
LINK_CLICK_MD_PROPS : "显示 Markdown 图片属性对话框(仅在嵌入 Markdown 文档为图片时适用)" ,
|
||||
};
|
||||
@@ -3,12 +3,37 @@ import { BinaryFileData } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
|
||||
import { Notice } from "obsidian";
|
||||
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate, cloneElement } from "src/ExcalidrawAutomate";
|
||||
import { ExportSettings } from "src/ExcalidrawView";
|
||||
import { getEA } from "src/core";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import { cloneElement } from "src/utils/excalidrawAutomateUtils";
|
||||
import { ExportSettings } from "src/view/ExcalidrawView";
|
||||
import { nanoid } from "src/constants/constants";
|
||||
import { svgToBase64 } from "./Utils";
|
||||
import { svgToBase64 } from "../utils/utils";
|
||||
|
||||
/**
|
||||
* Creates a masked image from an Excalidraw scene.
|
||||
*
|
||||
* The scene must contain:
|
||||
* - One element.type="frame" element that defines the crop area
|
||||
* - One or more element.type="image" elements
|
||||
* - Zero or more non-image shape elements (rectangles, ellipses etc) that define the mask
|
||||
*
|
||||
* The class splits the scene into two parts:
|
||||
* 1. Images (managed in imageEA)
|
||||
* 2. Mask shapes (managed in maskEA)
|
||||
*
|
||||
* A transparent rectangle matching the combined bounding box is added to both
|
||||
* imageEA and maskEA to ensure consistent sizing between image and mask.
|
||||
*
|
||||
* For performance, if there is only one image, it is not rotated, and
|
||||
* its size matches the bounding box,
|
||||
* the image data is used directly from cache rather than regenerating.
|
||||
*
|
||||
* @example
|
||||
* const cropper = new CropImage(elements, files);
|
||||
* const pngBlob = await cropper.getCroppedPNG();
|
||||
* cropper.destroy();
|
||||
*/
|
||||
export class CropImage {
|
||||
private imageEA: ExcalidrawAutomate;
|
||||
private maskEA: ExcalidrawAutomate;
|
||||
@@ -106,10 +131,15 @@ export class CropImage {
|
||||
withTheme: false,
|
||||
isMask: false,
|
||||
}
|
||||
const isRotated = this.imageEA.getElements().some(el=>el.type === "image" && el.angle !== 0);
|
||||
const images = Object.values(this.imageEA.imagesDict);
|
||||
if(!isRotated && (images.length === 1)) {
|
||||
return images[0].dataURL;
|
||||
const images = this.imageEA.getElements().filter(el=>el.type === "image" && el.isDeleted === false);
|
||||
const isRotated = images.some(el=>el.angle !== 0);
|
||||
const imageDataURLs = Object.values(this.imageEA.imagesDict);
|
||||
if(!isRotated && images.length === 1 && imageDataURLs.length === 1) {
|
||||
const { width, height } = this.bbox;
|
||||
if(images[0].width === width && images[0].height === height) {
|
||||
//get image from the cache if mask is not bigger than the image, and if there is a single image element
|
||||
return imageDataURLs[0].dataURL;
|
||||
}
|
||||
}
|
||||
return await this.imageEA.createPNGBase64(null,1,exportSettings,null,null,0);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Setting, ToggleComponent } from "obsidian";
|
||||
import { EmbeddableMDCustomProps } from "./EmbeddableSettings";
|
||||
import { fragWithHTML } from "src/utils/Utils";
|
||||
import { fragWithHTML } from "src/utils/utils";
|
||||
import { t } from "src/lang/helpers";
|
||||
|
||||
export class EmbeddalbeMDFileCustomDataSettingsComponent {
|
||||
@@ -1,16 +1,16 @@
|
||||
import { ExcalidrawEmbeddableElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
|
||||
import { Modal, Notice, Setting, TFile, ToggleComponent } from "obsidian";
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import { getEA } from "src/core";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
import { t } from "src/lang/helpers";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { getNewUniqueFilepath, getPathWithoutExtension, splitFolderAndFilename } from "src/utils/FileUtils";
|
||||
import { addAppendUpdateCustomData, fragWithHTML } from "src/utils/Utils";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { getNewUniqueFilepath, getPathWithoutExtension, splitFolderAndFilename } from "src/utils/fileUtils";
|
||||
import { addAppendUpdateCustomData, fragWithHTML } from "src/utils/utils";
|
||||
import { getYouTubeStartAt, isValidYouTubeStart, isYouTube, updateYouTubeStartTime } from "src/utils/YoutTubeUtils";
|
||||
import { EmbeddalbeMDFileCustomDataSettingsComponent } from "./EmbeddableMDFileCustomDataSettingsComponent";
|
||||
import { isWinCTRLorMacCMD } from "src/utils/ModifierkeyHelper";
|
||||
import { isWinCTRLorMacCMD } from "src/utils/modifierkeyHelper";
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
|
||||
export type EmbeddableMDCustomProps = {
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { Modal, Setting, TFile } from "obsidian";
|
||||
import { getEA } from "src";
|
||||
import { getEA } from "src/core";
|
||||
import { DEVICE } from "src/constants/constants";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { fragWithHTML, getExportPadding, getExportTheme, getPNGScale, getWithBackground, shouldEmbedScene } from "src/utils/Utils";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { fragWithHTML, getExportPadding, getExportTheme, getPNGScale, getWithBackground, shouldEmbedScene } from "src/utils/utils";
|
||||
|
||||
export class ExportDialog extends Modal {
|
||||
private ea: ExcalidrawAutomate;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import { t } from "src/lang/helpers";
|
||||
|
||||
export const showFrameSettings = (ea: ExcalidrawAutomate) => {
|
||||
@@ -1,9 +1,9 @@
|
||||
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';
|
||||
import { ExcalidrawSettings } from 'src/core/settings';
|
||||
import { modifierLabel } from 'src/utils/modifierkeyHelper';
|
||||
import { fragWithHTML } from 'src/utils/utils';
|
||||
|
||||
export class HotkeyEditor extends BaseComponent {
|
||||
private settings: ExcalidrawSettings;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../constants/constants";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../../constants/constants";
|
||||
import ExcalidrawView from "../../view/ExcalidrawView";
|
||||
import { t } from "../../lang/helpers";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { getEA } from "src/core";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
|
||||
export class ImportSVGDialog extends FuzzySuggestModal<TFile> {
|
||||
public plugin: ExcalidrawPlugin;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../constants/constants";
|
||||
import { t } from "../lang/helpers";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../../constants/constants";
|
||||
import { t } from "../../lang/helpers";
|
||||
|
||||
export class InsertCommandDialog extends FuzzySuggestModal<TFile> {
|
||||
private addText: Function;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import { scaleToFullsizeModifier } from "src/utils/ModifierkeyHelper";
|
||||
import { DEVICE, IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS } from "../constants/constants";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { getEA } from "src";
|
||||
import { FuzzySuggestModal, TFile } from "obsidian";
|
||||
import { scaleToFullsizeModifier } from "src/utils/modifierkeyHelper";
|
||||
import { DEVICE, IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS } from "../../constants/constants";
|
||||
import ExcalidrawView from "../../view/ExcalidrawView";
|
||||
import { t } from "../../lang/helpers";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { getEA } from "src/core";
|
||||
|
||||
export class InsertImageDialog extends FuzzySuggestModal<TFile> {
|
||||
public plugin: ExcalidrawPlugin;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { FuzzyMatch, FuzzySuggestModal, setIcon } from "obsidian";
|
||||
import { AUDIO_TYPES, CODE_TYPES, ICON_NAME, IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS, VIDEO_TYPES } from "../constants/constants";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { getLink } from "src/utils/FileUtils";
|
||||
import { AUDIO_TYPES, CODE_TYPES, ICON_NAME, IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS, VIDEO_TYPES } from "../../constants/constants";
|
||||
import { t } from "../../lang/helpers";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { getLink } from "src/utils/fileUtils";
|
||||
import { LinkSuggestion } from "src/types/types";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { getEA } from "src";
|
||||
import { FuzzySuggestModal, TFile } from "obsidian";
|
||||
import ExcalidrawView from "../../view/ExcalidrawView";
|
||||
import { t } from "../../lang/helpers";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { getEA } from "src/core";
|
||||
|
||||
export class InsertMDDialog extends FuzzySuggestModal<TFile> {
|
||||
public plugin: ExcalidrawPlugin;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ButtonComponent, TFile, ToggleComponent } from "obsidian";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { getPDFDoc } from "src/utils/FileUtils";
|
||||
import ExcalidrawView from "../../view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { getPDFDoc } from "src/utils/fileUtils";
|
||||
import { Modal, Setting, TextComponent } from "obsidian";
|
||||
import { FileSuggestionModal } from "../Components/Suggesters/FileSuggestionModal";
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { FileSuggestionModal } from "../Suggesters/FileSuggestionModal";
|
||||
import { getEA } from "src/core";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { t } from "src/lang/helpers";
|
||||
|
||||
194
src/shared/Dialogs/Messages.ts
Normal file
194
src/shared/Dialogs/Messages.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
export const FIRST_RUN = `
|
||||
The Excalidraw Obsidian plugin is much more than "just" a drawing tool. To help you get started here's a showcase of the key Excalidraw plugin features.
|
||||
|
||||
If you'd like to learn more, please subscribe to my YouTube channel: [Visual PKM](https://www.youtube.com/channel/UCC0gns4a9fhVkGkngvSumAQ) where I regularly share videos about Obsidian-Excalidraw and about tools and techniques for Visual Personal Knowledge Management.
|
||||
|
||||
Thank you & Enjoy!
|
||||
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/P_Q6avJGoWI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
`;
|
||||
|
||||
export const RELEASE_NOTES: { [k: string]: string } = {
|
||||
Intro: `After each update you'll be prompted with the release notes. You can disable this in plugin settings.
|
||||
|
||||
I develop this plugin as a hobby, spending my free time doing this. If you find it valuable, then please say THANK YOU or...
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://storage.ko-fi.com/cdn/kofi6.png?v=6" border="0" alt="Buy Me a Coffee at ko-fi.com" height=45></a></div>
|
||||
`,
|
||||
"2.7.5":`
|
||||
## Fixed
|
||||
- PDF export scenario described in [#2184](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2184)
|
||||
- Elbow arrows do not work within frames [#2187](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2187)
|
||||
- Embedding images into Excalidraw with areaRef links did not work as expected due to conflicting SVG viewbox and width and height values
|
||||
- Can't exit full-screen mode in popout windows using the Command Palette toggle action [#2188](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2188)
|
||||
- If the image mask extended beyond the image in "Mask and Crop" image mode, the mask got misaligned from the image.
|
||||
- PDF image embedding fixes that impacted some PDF files (not all):
|
||||
- When cropping the PDF page in the scene (by double-clicking the image to crop), the size and position of the PDF cutout drifted.
|
||||
- Using PDF++ there was a small offset in the position of the cutout in PDF++ and the image in Excalidraw.
|
||||
- Updated a number of scripts including Split Ellipse, Select Similar Elements, and Concatenate Lines
|
||||
|
||||
## New in ExcalidrawAutomate
|
||||
${String.fromCharCode(96,96,96)}
|
||||
/**
|
||||
* Add, modify, or delete keys in element.customData and preserve existing keys.
|
||||
* Creates customData={} if it does not exist.
|
||||
* Takes the element id for an element in ea.elementsDict and the newData to add or modify.
|
||||
* To delete keys set key value in newData to undefined. So {keyToBeDeleted:undefined} will be deleted.
|
||||
* @param id
|
||||
* @param newData
|
||||
* @returns undefined if element does not exist in elementsDict, returns the modified element otherwise.
|
||||
*/
|
||||
public addAppendUpdateCustomData(id:string, newData: Partial<Record<string, unknown>>);
|
||||
${String.fromCharCode(96,96,96)}
|
||||
`,
|
||||
"2.7.4":`
|
||||
## Fixed
|
||||
- Regression from 2.7.3 where image fileId got overwritten in some cases
|
||||
- White flash when opening a dark drawing [#2178](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2178)
|
||||
`,
|
||||
"2.7.3":`
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/ISuORbVKyhQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
## Fixed
|
||||
- Toggling image size anchoring on and off by modifying the image link did not update the image in the view until the user forced saved it or closed and opened the drawing again. This was a side-effect of the less frequent view save introduced in 2.7.1
|
||||
|
||||
## New
|
||||
- **Shade Master Script**: A new script that allows you to modify the color lightness, hue, saturation, and transparency of selected Excalidraw elements, SVG images, and nested Excalidraw drawings. When a single image is selected, you can map colors individually. The original image remains unchanged, and a mapping table is added under ${String.fromCharCode(96)}## Embedded Files${String.fromCharCode(96)} for SVG and nested drawings. This helps maintain links between drawings while allowing different color themes.
|
||||
- New Command Palette Command: "Duplicate selected image with a different image ID". Creates a copy of the selected image with a new image ID. This allows you to add multiple color mappings to the same image. In the scene, the image will be treated as if a different image, but loaded from the same file in the Vault.
|
||||
|
||||
## QoL Improvements
|
||||
- New setting under ${String.fromCharCode(96)}Embedding Excalidraw into your notes and Exporting${String.fromCharCode(96)} > ${String.fromCharCode(96)}Image Caching and rendering optimization${String.fromCharCode(96)}. You can now set the number of concurrent workers that render your embedded images. Increasing the number will increase the speed but temporarily reduce the responsiveness of your system in case of large drawings.
|
||||
- Moved pen-related settings under ${String.fromCharCode(96)}Excalidraw appearance and behavior${String.fromCharCode(96)} to their sub-heading called ${String.fromCharCode(96)}Pen${String.fromCharCode(96)}.
|
||||
- Minor error fixing and performance optimizations when loading and updating embedded images.
|
||||
- Color maps in ${String.fromCharCode(96)}## Embedded Files${String.fromCharCode(96)} may now include color keys "stroke" and "fill". If set, these will change the fill and stroke attributes of the SVG root element of the relevant file.
|
||||
|
||||
## New in ExcalidrawAutomate
|
||||
${String.fromCharCode(96,96,96)}ts
|
||||
// Updates the color map of an SVG image element in the view. If a ColorMap is provided, it will be used directly.
|
||||
// If an SVGColorInfo is provided, it will be converted to a ColorMap.
|
||||
// The view will be marked as dirty and the image will be reset using the color map.
|
||||
updateViewSVGImageColorMap(
|
||||
elements: ExcalidrawImageElement | ExcalidrawImageElement[],
|
||||
colors: ColorMap | SVGColorInfo | ColorMap[] | SVGColorInfo[]
|
||||
): Promise<void>;
|
||||
|
||||
// Retrieves the color map for an image element.
|
||||
// The color map contains information about the mapping of colors used in the image.
|
||||
// If the element already has a color map, it will be returned.
|
||||
getColorMapForImageElement(el: ExcalidrawElement): ColorMap;
|
||||
|
||||
// Retrieves the color map for an SVG image element.
|
||||
// The color map contains information about the fill and stroke colors used in the SVG.
|
||||
// If the element already has a color map, it will be merged with the colors extracted from the SVG.
|
||||
getColorMapForImgElement(el: ExcalidrawElement): Promise<SVGColorInfo>;
|
||||
|
||||
// Extracts the fill (background) and stroke colors from an Excalidraw file and returns them as an SVGColorInfo.
|
||||
getColosFromExcalidrawFile(file:TFile, img: ExcalidrawImageElement): Promise<SVGColorInfo>;
|
||||
|
||||
// Extracts the fill and stroke colors from an SVG string and returns them as an SVGColorInfo.
|
||||
getColorsFromSVGString(svgString: string): SVGColorInfo;
|
||||
|
||||
// upgraded the addImage function.
|
||||
// 1. It now accepts an object as the input parameter, making your scripts more readable
|
||||
// 2. AddImageOptions now includes colorMap as an optional parameter, this will only have an effect in case of SVGs and nested Excalidraws
|
||||
// 3. The API function is backwards compatible, but I recommend new implementations to use the object based input
|
||||
addImage(opts: AddImageOptions}): Promise<string>;
|
||||
|
||||
interface AddImageOptions {
|
||||
topX: number;
|
||||
topY: number;
|
||||
imageFile: TFile | string;
|
||||
scale?: boolean;
|
||||
anchor?: boolean;
|
||||
colorMap?: ColorMap;
|
||||
}
|
||||
|
||||
type SVGColorInfo = Map<string, {
|
||||
mappedTo: string;
|
||||
fill: boolean;
|
||||
stroke: boolean;
|
||||
}>;
|
||||
|
||||
interface ColorMap {
|
||||
[color: string]: string;
|
||||
};
|
||||
${String.fromCharCode(96,96,96)}
|
||||
`,
|
||||
"2.7.2":`
|
||||
## Fixed
|
||||
- The plugin did not load on **iOS 16 and older**. [#2170](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2170)
|
||||
- Added empty line between ${String.fromCharCode(96)}# Excalidraw Data${String.fromCharCode(96)} and ${String.fromCharCode(96)}## Text Elements${String.fromCharCode(96)}. This will now follow **correct markdown linting**. [#2168](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2168)
|
||||
- Adding an **embeddable** to view did not **honor the element background and element stroke colors**, even if it was configured in plugin settings. [#2172](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2172)
|
||||
- **Deconstruct selected elements script** did not copy URLs and URIs for images embedded from outside Obsidian. Please update your script from the script library.
|
||||
- When **rearranging tabs in Obsidian**, e.g. having two tabs side by side, and moving one of them to another location, if the tab was an Excalidraw tab, it appeared as non-responsive after the move, until the tab was resized.
|
||||
|
||||
## Source Code Refactoring
|
||||
- Updated filenames, file locations, and file name letter-casing across the project
|
||||
- Extracted onDrop, onDragover, etc. handlers to DropManger in ExcalidrawView
|
||||
`,
|
||||
"2.7.1":`
|
||||
## Fixed
|
||||
- Deleting excalidraw file from file system while it is open in fullscreen mode in Obsidian causes Obsidian to be stuck in full-screen view [#2161](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2161)
|
||||
- Chinese fonts are not rendered in LaTeX statements [#2162](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2162)
|
||||
- Since Electron 32 (newer Obsidian Desktop installers) drag and drop links from Finder or OS File Explorer did not work. [Electron breaking change](https://www.electronjs.org/docs/latest/breaking-changes#removed-filepath). This is now fixed
|
||||
- Addressed unnecessary image reloads when changing windows in Obsidian
|
||||
`,
|
||||
"2.7.0":`
|
||||
## Fixed
|
||||
- Various Markdown embeddable "fuzziness":
|
||||
- Fixed issues with appearance settings and edit mode toggling when single-click editing is enabled.
|
||||
- Ensured embeddable file editing no longer gets interrupted unexpectedly.
|
||||
- **Hover Preview**: Disabled hover preview for back-of-the-note cards to reduce distractions.
|
||||
- **Settings Save**: Fixed an issue where plugin settings unnecessarily saved on every startup.
|
||||
|
||||
## New Features
|
||||
- **Image Cropping Snaps to Objects**: When snapping is enabled in the scene, image cropping now aligns to nearby objects.
|
||||
- **Session Persistence for Pen Mode**: Excalidraw remembers the last pen mode when switching between drawings within the same session.
|
||||
|
||||
## Refactoring
|
||||
- **Mermaid Diagrams**: Excalidraw now uses its own Mermaid package, breaking future dependencies on Obsidian's Mermaid updates. This ensures stability and includes all fixes and improvements made to Excalidraw Mermaid since February 2024. The plugin file size has increased slightly, but this change significantly improves maintainability while remaining invisible to users.
|
||||
- **MathJax Optimization**: MathJax (LaTeX equation SVG image generation) now loads only on demand, with the package compressed to minimize the startup and file size impact caused by the inclusion of Mermaid.
|
||||
- **On-Demand Language Loading**: Non-English language files are now compressed and load only when needed, counterbalancing the increase in file size due to Mermaid and improving load speeds.
|
||||
- **Codebase Restructuring**: Improved type safety by removing many ${String.fromCharCode(96)}//@ts-ignore${String.fromCharCode(96)} commands and enhancing modularity. Introduced new management classes: **CommandManager**, **EventManager**, **PluginFileManager**, **ObserverManager**, and **PackageManager**. Further restructuring is planned for upcoming releases to improve maintainability and stability.
|
||||
`,
|
||||
"2.6.8":`
|
||||
## New
|
||||
- **QoL improvements**:
|
||||
- Obsidian-link search button in Element Link Editor.
|
||||
- Add Any File now searches file aliases as well.
|
||||
- Cosmetic changes to file search modals (display path, show file type icon).
|
||||
- Text Element cursor-color matches the text color.
|
||||
- New script in script store: [Image Occlusion](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Image%20Occlusion.md) by [@TrillStones](https://github.com/TrillStones) 🙏
|
||||
|
||||
## Fixed
|
||||
- Excalidraw icon on the **ribbon menu kept reappearing** every time you reopen Obsidian [#2115](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2115)
|
||||
- In pen mode, when **single-finger panning** is enabled, Excalidraw should still **allow actions with the mouse**.
|
||||
- When **editing a drawing in split mode** (drawing is on one side, markdown view is on the other), editing the markdown note sometimes causes the drawing to re-zoom and jump away from the selected area.
|
||||
- Hover-Editor compatibility resolved [2041](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2041)
|
||||
- ${String.fromCharCode(96)}ExcalidrawAutomate.create() ${String.fromCharCode(96)} will now correctly include the markdown text in templates above Excalidraw Data and below YAML front matter. This also fixes the same issue with the **Deconstruct Selected Element script**.
|
||||
|
||||
`,
|
||||
"2.6.7":`
|
||||
Hoping to finally move on to 2.7.0... but still have one last bug to fix in 2.6.x!
|
||||
|
||||
## Fixed
|
||||
I misread a line in the Excalidraw package code... ended up breaking image loading in 2.6.6. The icon library script didn't work right, and updating nested drawings caused all images in the scene to be dropped from memory. This led to image-placeholders in exports and broke copy-paste to Excalidraw.com and between drawings. I am surprised no one reported it! 😳
|
||||
`,
|
||||
"2.6.6":`
|
||||
## Fixed
|
||||
- Images and LaTeX formulas did not update in the scene when the source was changed until the Excalidraw drawing was closed and reopened. [#2105](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2105)
|
||||
`,
|
||||
"2.6.5":`
|
||||
## Fixed
|
||||
- Text sizing issue in the drawing that is first loaded after Obsidian restarts [#2086](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2086)
|
||||
- Excalidraw didn't load if there was a file in the Excalidraw folder with a name that starts the same way as the Scripts folder name. [#2095](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2095)
|
||||
- **OVERSIZED EXCALIDRAW TOOLBAR**: Added a new setting under "Excalidraw Appearance and Behavior > Theme and Styling" called "Limit Obsidian Font Size to Editor Text." This setting is off by default. When enabled, it restricts Obsidian's custom font size adjustments to editor text only, preventing unintended scaling of Excalidraw UI elements and other themes that rely on the default interface font size. Feel free to experiment with this setting to improve Excalidraw UI consistency. However, because this change affects the broader Obsidian UI, it's recommended to turn it off if any layout issues arise. [#2087](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2087)`,
|
||||
"2.6.4":`
|
||||
## Fixed
|
||||
- Error saving when cropping images embedded from a URL (not from a file in the Vault) [#2096](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2096)
|
||||
`,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Setting } from "obsidian";
|
||||
import { DEVICE } from "src/constants/constants";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { ModifierKeySet, ModifierSetType, modifierKeyTooltipMessages } from "src/utils/ModifierkeyHelper";
|
||||
import { ModifierKeySet, ModifierSetType, modifierKeyTooltipMessages } from "src/utils/modifierkeyHelper";
|
||||
|
||||
type ModifierKeyCategories = Partial<{
|
||||
[modifierSetType in ModifierSetType]: string;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { EMPTY_MESSAGE } from "../constants/constants";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { EMPTY_MESSAGE } from "../../constants/constants";
|
||||
import { t } from "../../lang/helpers";
|
||||
|
||||
export enum openDialogAction {
|
||||
openFile,
|
||||
@@ -1,13 +1,13 @@
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
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, PenType } from "src/types/PenTypes";
|
||||
import { getExcalidrawViews } from "src/utils/ObsidianUtils";
|
||||
import { PENS } from "src/utils/Pens";
|
||||
import { fragWithHTML } from "src/utils/Utils";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { setPen } from "src/view/components/menu/ObsidianMenu";
|
||||
import { ExtendedFillStyle, PenType } from "src/types/penTypes";
|
||||
import { getExcalidrawViews } from "src/utils/obsidianUtils";
|
||||
import { PENS } from "src/utils/pens";
|
||||
import { fragWithHTML } from "src/utils/utils";
|
||||
import { __values } from "tslib";
|
||||
|
||||
const EASINGFUNCTIONS: Record<string,string> = {
|
||||
@@ -8,21 +8,20 @@ import {
|
||||
TFile,
|
||||
Notice,
|
||||
TextAreaComponent,
|
||||
TFolder,
|
||||
} from "obsidian";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { escapeRegExp, getLinkParts, sleep } from "../utils/Utils";
|
||||
import { getLeaf, openLeaf } from "../utils/ObsidianUtils";
|
||||
import { checkAndCreateFolder, splitFolderAndFilename } from "src/utils/FileUtils";
|
||||
import { KeyEvent, isWinCTRLorMacCMD } from "src/utils/ModifierkeyHelper";
|
||||
import ExcalidrawView from "../../view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { escapeRegExp, getLinkParts, sleep } from "../../utils/utils";
|
||||
import { getLeaf, openLeaf } from "../../utils/obsidianUtils";
|
||||
import { checkAndCreateFolder, splitFolderAndFilename } from "src/utils/fileUtils";
|
||||
import { KeyEvent, isWinCTRLorMacCMD } from "src/utils/modifierkeyHelper";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { ExcalidrawElement, getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { ExcalidrawElement, getEA } from "src/core";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import { MAX_IMAGE_SIZE, REG_LINKINDEX_INVALIDCHARS } from "src/constants/constants";
|
||||
import { REGEX_LINK, REGEX_TAGS } from "src/ExcalidrawData";
|
||||
import { ScriptEngine } from "src/Scripts";
|
||||
import { openExternalLink, openTagSearch, parseObsidianLink } from "src/utils/ExcalidrawViewUtils";
|
||||
import { REGEX_LINK, REGEX_TAGS } from "../ExcalidrawData";
|
||||
import { ScriptEngine } from "../Scripts";
|
||||
import { openExternalLink, openTagSearch, parseObsidianLink } from "src/utils/excalidrawViewUtils";
|
||||
|
||||
export type ButtonDefinition = { caption: string; tooltip?:string; action: Function };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Modal, Setting, TFile } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { getIMGFilename } from "src/utils/FileUtils";
|
||||
import { addIframe } from "src/utils/Utils";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { getIMGFilename } from "src/utils/fileUtils";
|
||||
import { addIframe } from "src/utils/utils";
|
||||
|
||||
const haveLinkedFilesChanged = (depth: number, mtime: number, path: string, sourceList: Set<string>, plugin: ExcalidrawPlugin):boolean => {
|
||||
if(depth++ > 5) return false;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { App, MarkdownRenderer, Modal } from "obsidian";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { Rank, SwordColors } from "src/menu/ActionIcons";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { Rank, SwordColors } from "src/constants/actionIcons";
|
||||
|
||||
export class RankMessage extends Modal {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { App, MarkdownRenderer, Modal } from "obsidian";
|
||||
import { isVersionNewerThanOther } from "src/utils/Utils";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { isVersionNewerThanOther } from "src/utils/utils";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { FIRST_RUN, RELEASE_NOTES } from "./Messages";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MarkdownRenderer, Modal, Notice, request } from "obsidian";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { errorlog, escapeRegExp } from "../utils/Utils";
|
||||
import { log } from "src/utils/DebugHelper";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { errorlog, escapeRegExp } from "../../utils/utils";
|
||||
import { log } from "src/utils/debugHelper";
|
||||
|
||||
const URL =
|
||||
"https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/index-new.md";
|
||||
@@ -1,10 +1,10 @@
|
||||
import { App, FuzzySuggestModal, Notice, TFile } from "obsidian";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { App, FuzzySuggestModal, Notice } from "obsidian";
|
||||
import { t } from "../../lang/helpers";
|
||||
import ExcalidrawView from "src/view/ExcalidrawView";
|
||||
import { getEA } from "src/core";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import { MD_EX_SECTIONS } from "src/constants/constants";
|
||||
import { addBackOfTheNoteCard } from "src/utils/ExcalidrawViewUtils";
|
||||
import { addBackOfTheNoteCard } from "src/utils/excalidrawViewUtils";
|
||||
|
||||
export class SelectCard extends FuzzySuggestModal<string> {
|
||||
|
||||
@@ -157,6 +157,15 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
desc: "Set ea.style.roundness. 0: is the legacy value, 3: is the current default value, null is sharp",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "addAppendUpdateCustomData",
|
||||
code: "addAppendUpdateCustomData(id: string, newData: Partial<Record<string, unknown>>)",
|
||||
desc: "Add, modify keys in element customData and preserve existing keys.\n" +
|
||||
"Creates customData={} if it does not exist.\n" +
|
||||
"Takes the element ID for an element in the elementsDict and the new data to add or modify.\n" +
|
||||
"To delete keys set key value in newData to undefined. so {keyToBeDeleted:undefined} will be deleted.",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "addToGroup",
|
||||
code: "addToGroup(objectIds: []): string;",
|
||||
@@ -297,8 +306,13 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
},
|
||||
{
|
||||
field: "addImage",
|
||||
code: "async addImage(topX: number, topY: number, imageFile: TFile|string, scale?: boolean, anchor?: boolean): Promise<string>;",
|
||||
desc: "imageFile may be a TFile or a string that contains a hyperlink. imageFile may also be an obsidian filepath including a reference eg.: 'path/my.pdf#page=3'\nSet scale to false if you want to embed the image at 100% of its original size. Default is true which will insert a scaled image.\nanchor will only be evaluated if scale is false. anchor true will add |100% to the end of the filename, resulting in an image that will always pop back to 100% when the source file is updated or when the Excalidraw file is reopened.",
|
||||
code: "async addImage(opts: {topX: number, topY: number, imageFile: TFile|string, scale?: boolean, anchor?: boolean, colorMap?: ColorMap}): Promise<string>;",
|
||||
desc: "imageFile may be a TFile or a string that contains a hyperlink.\n"+
|
||||
"imageFile may also be an obsidian filepath including a reference eg.: 'path/my.pdf#page=3'\n"+
|
||||
"Set scale to false if you want to embed the image at 100% of its original size. Default is true which will insert a scaled image.\n"+
|
||||
"anchor will only be evaluated if scale is false. anchor true will add |100% to the end of the filename, resulting in an image that will always pop back to 100% when the source file is updated or when the Excalidraw file is reopened.\n"+
|
||||
"colorMap is only used for SVG images and nested Excalidraw images. See the Shade Master script and the Deconstruct Selected Elements script for examples using colorMap.\n"+
|
||||
"type ColorMap = { [color: string]: string; }",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
@@ -415,6 +429,47 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
desc: "Returns the TFile file handle for the image element",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "updateViewSVGImageColorMap",
|
||||
code: "async updateViewSVGImageColorMap(elements: ExcalidrawImageElement | ExcalidrawImageElement[], colors: ColorMap | SVGColorInfo | ColorMap[] | SVGColorInfo[]): Promise<void>;",
|
||||
desc: 'Updates the color map of an SVG image element in the view. If a ColorMap is provided, it will be used directly. If an SVGColorInfo is provided, it will be converted to a ColorMap. The view will be marked as dirty (i.e. will be saved at next scheduled time) and the image will be reset using the color map.\n'+
|
||||
'See "Shade Master" scritp in Script Library for an example of using this function.\n\n' +
|
||||
'type SVGColorInfo = Map<string, { mappedTo: string; fill: boolean; stroke: boolean; }>\n' +
|
||||
'type ColorMap = { [color: string]: string; }',
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "getColorMapForImageElement",
|
||||
code: "getColorMapForImageElement(el: ExcalidrawElement): ColorMap",
|
||||
desc: 'Retrieves the color map for an image element. The color map contains information about the mapping of colors used in the image. If the element already has a color map, it will be returned. The colorMap does not include all colors in the image, only those that have been mapped.\n' +
|
||||
'See "Shade Master" scritp in Script Library for an example of using this function.\n\n' +
|
||||
'type ColorMap = { [color: string]: string; }',
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "getSVGColorInfoForImgElement",
|
||||
code: "async getColorMapForImgElement(el: ExcalidrawElement): Promise<SVGColorInfo>",
|
||||
desc: 'This function must be awaited. Retrieves the color map for an SVG image element. The color map contains information about the fill and stroke colors used in the SVG. If the element already has a color map, it will be merged with the colors extracted from the SVG.\n' +
|
||||
'See "Shade Master" scritp in Script Library for an example of using this function.\n\n' +
|
||||
'type SVGColorInfo = Map<string, { mappedTo: string; fill: boolean; stroke: boolean; }>',
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "getColosFromExcalidrawFile",
|
||||
code: "async getColosFromExcalidrawFile(file:TFile, img: ExcalidrawImageElement): Promise<SVGColorInfo>",
|
||||
desc: 'Must be awaited. Extracts the fill (background) and stroke colors from an excalidraw file and returns them as an SVGColorInfo. The SVGColorInfo is a map where the keys are the colors used in the SVG and the values contain information about whether the color is used for fill, stroke, or both.\n' +
|
||||
'See "Shade Master" scritp in Script Library for an example of using this function.\n\n' +
|
||||
'type SVGColorInfo = Map<string, { mappedTo: string; fill: boolean; stroke: boolean; }>',
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "getColorsFromSVGString",
|
||||
code: "getColorsFromSVGString(svgString: string): SVGColorInfo",
|
||||
desc: 'Extracts the fill and stroke colors from an SVG string and returns them as an SVGColorInfo. The SVGColorInfo is a map where the keys are the colors used in the SVG and the values contain information about whether the color is used for fill, stroke, or both.\n' +
|
||||
'See "Shade Master" scritp in Script Library for an example of using this function.\n\n' +
|
||||
'type SVGColorInfo = Map<string, { mappedTo: string; fill: boolean; stroke: boolean; }>',
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "copyViewElementsToEAforEditing",
|
||||
code: "copyViewElementsToEAforEditing(elements: ExcalidrawElement[], copyImages: boolean = false): void;",
|
||||
@@ -1,15 +1,15 @@
|
||||
import { ButtonComponent, DropdownComponent, TFile, ToggleComponent } from "obsidian";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import ExcalidrawView from "../../view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../../core/main";
|
||||
import { Modal, Setting, TextComponent } from "obsidian";
|
||||
import { FileSuggestionModal } from "../Components/Suggesters/FileSuggestionModal";
|
||||
import { FileSuggestionModal } from "../Suggesters/FileSuggestionModal";
|
||||
import { IMAGE_TYPES, sceneCoordsToViewportCoords, viewportCoordsToSceneCoords, MAX_IMAGE_SIZE, ANIMATED_IMAGE_TYPES, MD_EX_SECTIONS } from "src/constants/constants";
|
||||
import { insertEmbeddableToView, insertImageToView } from "src/utils/ExcalidrawViewUtils";
|
||||
import { getEA } from "src";
|
||||
import { insertEmbeddableToView, insertImageToView } from "src/utils/excalidrawViewUtils";
|
||||
import { getEA } from "src/core";
|
||||
import { InsertPDFModal } from "./InsertPDFModal";
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { cleanSectionHeading } from "src/utils/ObsidianUtils";
|
||||
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
|
||||
import { cleanSectionHeading } from "src/utils/obsidianUtils";
|
||||
|
||||
export class UniversalInsertFileModal extends Modal {
|
||||
private center: { x: number, y: number } = { x: 0, y: 0 };
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
import { DEVICE } from "src/constants/constants";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
|
||||
export class ExcalidrawConfig {
|
||||
public areaLimit: number = 16777216;
|
||||
@@ -18,9 +18,9 @@ import {
|
||||
refreshTextDimensions,
|
||||
getContainerElement,
|
||||
loadSceneFonts,
|
||||
} from "./constants/constants";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { TextMode } from "./ExcalidrawView";
|
||||
} from "../constants/constants";
|
||||
import ExcalidrawPlugin from "../core/main";
|
||||
import { TextMode } from "../view/ExcalidrawView";
|
||||
import {
|
||||
addAppendUpdateCustomData,
|
||||
compress,
|
||||
@@ -37,8 +37,8 @@ import {
|
||||
wrapTextAtCharLength,
|
||||
arrayToMap,
|
||||
compressAsync,
|
||||
} from "./utils/Utils";
|
||||
import { cleanBlockRef, cleanSectionHeading, getAttachmentsFolderAndFilePath, isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
} from "../utils/utils";
|
||||
import { cleanBlockRef, cleanSectionHeading, getAttachmentsFolderAndFilePath, isObsidianThemeDark } from "../utils/obsidianUtils";
|
||||
import {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawImageElement,
|
||||
@@ -47,15 +47,15 @@ import {
|
||||
} from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { BinaryFiles, DataURL, SceneData } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { EmbeddedFile, MimeType } from "./EmbeddedFileLoader";
|
||||
import { ConfirmationPrompt } from "./dialogs/Prompt";
|
||||
import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "./utils/MermaidUtils";
|
||||
import { DEBUGGING, debug } from "./utils/DebugHelper";
|
||||
import { ConfirmationPrompt } from "./Dialogs/Prompt";
|
||||
import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "../utils/mermaidUtils";
|
||||
import { DEBUGGING, debug } from "../utils/debugHelper";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
|
||||
import { updateElementIdsInScene } from "./utils/ExcalidrawSceneUtils";
|
||||
import { getNewUniqueFilepath } from "./utils/FileUtils";
|
||||
import { t } from "./lang/helpers";
|
||||
import { displayFontMessage } from "./utils/ExcalidrawViewUtils";
|
||||
import { getPDFRect } from "./utils/PDFUtils";
|
||||
import { updateElementIdsInScene } from "../utils/excalidrawSceneUtils";
|
||||
import { getNewUniqueFilepath } from "../utils/fileUtils";
|
||||
import { t } from "../lang/helpers";
|
||||
import { displayFontMessage } from "../utils/excalidrawViewUtils";
|
||||
import { getPDFRect } from "../utils/PDFUtils";
|
||||
|
||||
type SceneDataWithFiles = SceneData & { files: BinaryFiles };
|
||||
|
||||
@@ -649,7 +649,6 @@ export class ExcalidrawData {
|
||||
containers.forEach((container: any) => {
|
||||
if(ellipseAndRhombusContainerWrapping && !container.customData?.legacyTextWrap) {
|
||||
addAppendUpdateCustomData(container, {legacyTextWrap: true});
|
||||
//container.customData = {...container.customData, legacyTextWrap: true};
|
||||
}
|
||||
const filteredBoundElements = container.boundElements.filter(
|
||||
(boundEl: any) => elements.some((el: any) => el.id === boundEl.id),
|
||||
@@ -859,7 +858,7 @@ export class ExcalidrawData {
|
||||
return true; //Text Elements header does not exist
|
||||
}
|
||||
data = data.slice(position);
|
||||
const normalMatch = data.match(/^((%%\n*)?# Excalidraw Data\n## Text Elements(?:\n|$))/m)
|
||||
const normalMatch = data.match(/^((%%\n*)?# Excalidraw Data\n\n?## Text Elements(?:\n|$))/m)
|
||||
?? data.match(/^((%%\n*)?##? Text Elements(?:\n|$))/m);
|
||||
|
||||
const textElementsMatch = normalMatch
|
||||
@@ -1427,7 +1426,7 @@ export class ExcalidrawData {
|
||||
disableCompression: boolean = false;
|
||||
generateMDBase(deletedElements: ExcalidrawElement[] = []) {
|
||||
let outString = this.textElementCommentedOut ? "%%\n" : "";
|
||||
outString += `# Excalidraw Data\n## Text Elements\n`;
|
||||
outString += `# Excalidraw Data\n\n## Text Elements\n`;
|
||||
if (this.plugin.settings.addDummyTextElement) {
|
||||
outString += `\n^_dummy!_\n\n`;
|
||||
}
|
||||
@@ -1569,13 +1568,13 @@ export class ExcalidrawData {
|
||||
filepath,
|
||||
);
|
||||
|
||||
embeddedFile.setImage(
|
||||
dataURL,
|
||||
embeddedFile.setImage({
|
||||
imgBase64: dataURL,
|
||||
mimeType,
|
||||
{ height: 0, width: 0 },
|
||||
scene.appState?.theme === "dark",
|
||||
mimeType === "image/svg+xml", //this treat all SVGs as if they had embedded images REF:addIMAGE
|
||||
);
|
||||
size: { height: 0, width: 0 },
|
||||
isDark: scene.appState?.theme === "dark",
|
||||
isSVGwithBitmap: mimeType === "image/svg+xml", //this treat all SVGs as if they had embedded images REF:addIMAGE
|
||||
});
|
||||
this.setFile(key as FileId, embeddedFile);
|
||||
return file;
|
||||
}
|
||||
@@ -1593,7 +1592,9 @@ export class ExcalidrawData {
|
||||
const pageRef = ef.linkParts.original.split("#")?.[1];
|
||||
if(!pageRef || !pageRef.startsWith("page=") || pageRef.includes("rect")) return;
|
||||
const restOfLink = el.link ? el.link.match(/&rect=\d*,\d*,\d*,\d*(.*)/)?.[1] : "";
|
||||
const link = ef.linkParts.original + getPDFRect(el.crop, pdfScale) + (restOfLink ? restOfLink : "]]");
|
||||
const link = ef.linkParts.original +
|
||||
getPDFRect({elCrop: el.crop, scale: pdfScale, customData: el.customData}) +
|
||||
(restOfLink ? restOfLink : "]]");
|
||||
el.link = `[[${link}`;
|
||||
this.elementLinks.set(el.id, el.link);
|
||||
dirty = true;
|
||||
@@ -1992,7 +1993,7 @@ export class ExcalidrawData {
|
||||
isLocalLink: data.isLocalLink,
|
||||
path: data.hyperlink,
|
||||
blockrefData: null,
|
||||
hasSVGwithBitmap: data.isSVGwithBitmap
|
||||
hasSVGwithBitmap: data.isSVGwithBitmap,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -2028,7 +2029,8 @@ export class ExcalidrawData {
|
||||
this.file.path,
|
||||
masterFile.blockrefData
|
||||
? masterFile.path + "#" + masterFile.blockrefData
|
||||
: masterFile.path
|
||||
: masterFile.path,
|
||||
masterFile.colorMapJSON
|
||||
);
|
||||
this.files.set(fileId,embeddedFile);
|
||||
return embeddedFile;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { App, Notice, TFile } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { convertSVGStringToElement } from "./Utils";
|
||||
import { FILENAMEPARTS, PreviewImageType } from "./UtilTypes";
|
||||
import { hasExcalidrawEmbeddedImagesTreeChanged } from "./FileUtils";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { convertSVGStringToElement } from "../utils/utils";
|
||||
import { FILENAMEPARTS, PreviewImageType } from "../types/utilTypes";
|
||||
import { hasExcalidrawEmbeddedImagesTreeChanged } from "../utils/fileUtils";
|
||||
|
||||
//@ts-ignore
|
||||
const DB_NAME = "Excalidraw " + app.appId;
|
||||
@@ -1,6 +1,6 @@
|
||||
// LaTeX.ts
|
||||
import { DataURL } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import ExcalidrawView from "./ExcalidrawView";
|
||||
import ExcalidrawView from "../view/ExcalidrawView";
|
||||
import { FileData, MimeType } from "./EmbeddedFileLoader";
|
||||
import { FileId } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { App } from "obsidian";
|
||||
@@ -1,13 +1,13 @@
|
||||
import { ExcalidrawAutomate } from "../ExcalidrawAutomate";
|
||||
import {Notice, requestUrl} from "obsidian"
|
||||
import ExcalidrawPlugin from "../main"
|
||||
import ExcalidrawView, { ExportSettings } from "../ExcalidrawView"
|
||||
import FrontmatterEditor from "src/utils/Frontmatter";
|
||||
import ExcalidrawPlugin from "../../core/main"
|
||||
import ExcalidrawView, { ExportSettings } from "../../view/ExcalidrawView"
|
||||
import FrontmatterEditor from "src/shared/Frontmatter";
|
||||
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { EmbeddedFilesLoader } from "src/EmbeddedFileLoader";
|
||||
import { blobToBase64 } from "src/utils/FileUtils";
|
||||
import { getEA } from "src";
|
||||
import { log } from "src/utils/DebugHelper";
|
||||
import { EmbeddedFilesLoader } from "../EmbeddedFileLoader";
|
||||
import { blobToBase64 } from "src/utils/fileUtils";
|
||||
import { getEA } from "src/core";
|
||||
import { log } from "src/utils/debugHelper";
|
||||
|
||||
const TASKBONE_URL = "https://api.taskbone.com/"; //"https://excalidraw-preview.onrender.com/";
|
||||
const TASKBONE_OCR_FN = "execute?id=60f394af-85f6-40bc-9613-5d26dc283cbb";
|
||||
@@ -4,18 +4,17 @@ import {
|
||||
normalizePath,
|
||||
TAbstractFile,
|
||||
TFile,
|
||||
WorkspaceLeaf,
|
||||
} from "obsidian";
|
||||
import { PLUGIN_ID } from "./constants/constants";
|
||||
import ExcalidrawView from "./ExcalidrawView";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { ButtonDefinition, GenericInputPrompt, GenericSuggester } from "./dialogs/Prompt";
|
||||
import { getIMGFilename } from "./utils/FileUtils";
|
||||
import { splitFolderAndFilename } from "./utils/FileUtils";
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
|
||||
import { WeakArray } from "./utils/WeakArray";
|
||||
import { getExcalidrawViews } from "./utils/ObsidianUtils";
|
||||
import { PLUGIN_ID } from "../constants/constants";
|
||||
import ExcalidrawView from "../view/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../core/main";
|
||||
import { ButtonDefinition, GenericInputPrompt, GenericSuggester } from "./Dialogs/Prompt";
|
||||
import { getIMGFilename } from "../utils/fileUtils";
|
||||
import { splitFolderAndFilename } from "../utils/fileUtils";
|
||||
import { getEA } from "src/core";
|
||||
import { ExcalidrawAutomate } from "../shared/ExcalidrawAutomate";
|
||||
import { WeakArray } from "./WeakArray";
|
||||
import { getExcalidrawViews } from "../utils/obsidianUtils";
|
||||
|
||||
export type ScriptIconMap = {
|
||||
[key: string]: { name: string; group: string; svgString: string };
|
||||
@@ -254,6 +253,12 @@ export class ScriptEngine {
|
||||
if (!view || !script || !title) {
|
||||
return;
|
||||
}
|
||||
//addresses the situation when after paste text element IDs are not updated to 8 characters
|
||||
//linked to onPaste save issue with the false parameter
|
||||
if(view.getScene().elements.some(el=>!el.isDeleted && el.type === "text" && el.id.length > 8)) {
|
||||
await view.save(false, true);
|
||||
}
|
||||
|
||||
script = script.replace(/^---.*?---\n/gs, "");
|
||||
const ea = getEA(view);
|
||||
this.eaInstances.push(ea);
|
||||
@@ -6,12 +6,12 @@ import {
|
||||
EditorSuggestTriggerInfo,
|
||||
TFile,
|
||||
} from "obsidian";
|
||||
import { FRONTMATTER_KEYS_INFO } from "../../dialogs/SuggesterInfo";
|
||||
import { FRONTMATTER_KEYS_INFO } from "../Dialogs/SuggesterInfo";
|
||||
import {
|
||||
EXCALIDRAW_AUTOMATE_INFO,
|
||||
EXCALIDRAW_SCRIPTENGINE_INFO,
|
||||
} from "../../dialogs/SuggesterInfo";
|
||||
import type ExcalidrawPlugin from "../../main";
|
||||
} from "../Dialogs/SuggesterInfo";
|
||||
import type ExcalidrawPlugin from "../../core/main";
|
||||
|
||||
/**
|
||||
* The field suggester recommends document properties in source mode, ea and utils function and attribute names.
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { SuggestionModal } from "./SuggestionModal";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { LinkSuggestion } from "src/types/types";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import ExcalidrawPlugin from "src/core/main";
|
||||
import { AUDIO_TYPES, CODE_TYPES, ICON_NAME, IMAGE_TYPES, VIDEO_TYPES } from "src/constants/constants";
|
||||
|
||||
export class FileSuggestionModal extends SuggestionModal<LinkSuggestion> {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user