This commit is contained in:
zsviczian
2023-11-26 16:06:55 +01:00
parent 44c828c7e7
commit 8b1daed0ef
4 changed files with 303 additions and 1 deletions

293
ea-scripts/ExcaliAI.md Normal file
View File

@@ -0,0 +1,293 @@
/*
Based on https://github.com/SawyerHood/draw-a-ui
<iframe width="560" height="315" src="https://www.youtube.com/embed/y3kHl_6Ll4w" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-draw-a-ui.jpg)
```js*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.0.4")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
// --------------------------------------
// Initialize values and settings
// --------------------------------------
let settings = ea.getScriptSettings();
if(!settings["Custom System Prompts"]) {
settings = {
"Custom System Prompts" : {},
"Agent's Task": "Wireframe to code",
"Output Type": "html"
};
await ea.setScriptSettings(settings);
}
const instructions = {
"html": "Turn this into a single html file using tailwind. Return a single message containing only the html file in a codeblock.",
"mermaid": "Challenge my thinking. Return a single message containing only the mermaid diagram in a codeblock."
}
const defaultSystemPrompts = {
"Wireframe to code": `You are an expert tailwind developer. A user will provide you with a low-fidelity wireframe of an application and you will return a single html file that uses tailwind to create the website. Use creative license to make the application more fleshed out. Write the necessary javascript code. If you need to insert an image, use placehold.co to create a placeholder image.`,
"Challenge my thinking": `The user will provide you with a screenshot of a whiteboard covering ideas about a subject. Your task is to understand the topic of the whiteboard, to give it a title, and then to identify ideas, web links, images, and any additional content that challenges, disputes, contradicts what is on the whiteboard, plus ideas that extend, add, builds on, and takes the thinking of the user further.`
}
const OPENAI_API_KEY = ea.plugin.settings.openAIAPIToken;
if(!OPENAI_API_KEY || OPENAI_API_KEY === "") {
new Notice("You must first configure your API key in Excalidraw Plugin Settings");
return;
}
let customSystemPrompts = settings["Custom System Prompts"];
let agentTask = settings["Agent's Task"];
let outputType = settings["Output Type"];
if (!Object.keys(instructions).includes(outputType)) {
outputType = "html";
}
let allSystemPrompts = {
...defaultSystemPrompts,
...customSystemPrompts
};
if(!allSystemPrompts.hasOwnProperty(agentTask)) {
agentTask = Object.keys(defaultSystemPrompts)[0];
}
// --------------------------------------
// Generate image
// --------------------------------------
const getRequestObjFromSelectedElements = async (view) => {
await view.forceSave(true);
const viewElements = ea.getViewSelectedElements();
if(viewElements.length === 0) {
new Notice ("Aborting because there is nothing selected.",4000);
return;
}
ea.copyViewElementsToEAforEditing(viewElements);
const bb = ea.getBoundingBox(viewElements);
const size = (bb.width*bb.height);
const minRatio = Math.sqrt(360000/size);
const maxRatio = Math.sqrt(size/16000000);
const scale = minRatio > 1
? minRatio
: (
maxRatio > 1
? 1/maxRatio
: 1
);
const loader = ea.getEmbeddedFilesLoader(false);
const exportSettings = {
withBackground: true,
withTheme: true,
};
const dataURL =
await ea.createPNGBase64(
null,
scale,
exportSettings,
loader,
"light",
);
ea.clear();
return { image: dataURL };
}
// --------------------------------------
// Submit Prompt
// --------------------------------------
const run = async () => {
const requestObject = await getRequestObjFromSelectedElements(ea.targetView);
requestObject.systemPrompt = allSystemPrompts[agentTask];
requestObject.instruction = instructions[outputType];
const spinner = await ea.convertStringToDataURL(`
<html><head><style>
html, body {width: 100%; height: 100%; color: ${ea.getExcalidrawAPI().getAppState().theme === "dark" ? "white" : "black"};}
body {display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 1rem; overflow: hidden;}
.Spinner {display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto;}
.Spinner svg {animation: rotate 1.6s linear infinite; transform-origin: center center; width: 40px; height: 40px;}
.Spinner circle {stroke: currentColor; animation: dash 1.6s linear 0s infinite; stroke-linecap: round;}
@keyframes rotate {100% {transform: rotate(360deg);}}
@keyframes dash {0% {stroke-dasharray: 1, 300; stroke-dashoffset: 0;} 50% {stroke-dasharray: 150, 300; stroke-dashoffset: -200;} 100% {stroke-dasharray: 1, 300; stroke-dashoffset: -280;}}
</style></head><body>
<div class="Spinner">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="46" stroke-width="8" fill="none" stroke-miter-limit="10"/>
</svg>
</div>
<div>Generating...</div>
</body></html>`);
const bb = ea.getBoundingBox(ea.getViewSelectedElements());
const id = ea.addEmbeddable(bb.topX+bb.width+100,bb.topY-(720-bb.height)/2,550,720,spinner);
await ea.addElementsToView(false,true);
ea.clear();
const embeddable = ea.getViewElements().filter(el=>el.id===id);
ea.copyViewElementsToEAforEditing(embeddable);
const els = ea.getViewSelectedElements();
ea.viewZoomToElements(false, els.concat(embeddable));
//Get result from GPT
const result = await ea.postOpenAI(requestObject);
const errorMessage = async () => {
const error = "Something went wrong! Check developer console for more.";
const errorDataURL = await ea.convertStringToDataURL(`
<html><head><style>
html, body {height: 100%;}
body {display: flex; flex-direction: column; align-items: center; justify-content: center; color: red;}
h1, h3 {margin-top: 0;margin-bottom: 0.5rem;}
</style></head><body>
<h1>Error!</h1>
<h3>${error}</h3>
</body></html>`);
new Notice (error);
ea.getElement(id).src = errorDataURL;
ea.addElementsToView(false,true);
}
if(!result?.json?.hasOwnProperty("choices")) {
await errorMessage();
return;
}
console.log(result);
let content = ea.extractCodeBlocks(result.json.choices[0]?.message?.content)[0]?.data;
if(!content) {
await errorMessage();
return;
}
switch(outputType) {
case "html":
ea.getElement(id).link = await ea.convertStringToDataURL(content);
ea.addElementsToView(false,true);
break;
case "mermaid":
if(content.startsWith("mermaid")) {
content = content.replace(/^mermaid/,"").trim();
}
ea.getElement(id).isDeleted = true;
try {
await ea.addMermaid(content);
} catch (e) {
ea.addText(0,0,content);
}
ea.targetView.currentPosition = {x: bb.topX+bb.width+100, y: bb.topY-bb.height-100};
await ea.addElementsToView(true, false);
ea.clear();
if(content.startsWith("graph LR") || content.startsWith("graph TD")) {
try {
if(content.startsWith("graph LR") || content.startsWith("flowchart LR")) {
content = content.replaceAll("graph LR", "graph TD");
content = content.replaceAll("flowchart LR", "flowchart TD");
await ea.addMermaid(content);
} else if (content.startsWith("graph TD") || content.startsWith("flowchart TD")) {
content = content.replaceAll("graph TD", "graph LR");
content = content.replaceAll("flowchart TD", "flowchart LR");
await ea.addMermaid(content);
}
} catch (e) {
ea.addText(0,0,content);
}
ea.targetView.currentPosition = {x: bb.topX-100, y: bb.topY + 100};
ea.addElementsToView(true, true);
}
break;
}
}
// --------------------------------------
// User Interface
// --------------------------------------
const fragWithHTML = (html) => createFragment((frag) => (frag.createDiv().innerHTML = html));
const configModal = new ea.obsidian.Modal(app);
let dirty=false;
configModal.onOpen = () => {
const contentEl = configModal.contentEl;
contentEl.createEl("h1", {text: "ExcaliAI"});
let textArea, promptTitle;
new ea.obsidian.Setting(contentEl)
.setName("Select Prompt")
.addDropdown(dropdown=>{
Object.keys(allSystemPrompts).forEach(key=>dropdown.addOption(key,key));
dropdown
.setValue(agentTask)
.onChange(value => {
dirty = true;
agentTask = value;
textArea.setValue(allSystemPrompts[value]);
promptTitle.setValue(value);
});
})
new ea.obsidian.Setting(contentEl)
.setName("Select response type")
.addDropdown(dropdown=>{
Object.keys(instructions).forEach(key=>dropdown.addOption(key,key));
dropdown
.setValue(outputType)
.onChange(value => {
dirty = true;
outputType = value;
});
})
contentEl.createEl("h3", {text: "Customize Prompt"});
/* const titleSetting = new ea.obsidian.Setting(contentEl)
.addText(text => {
promptTitle = text;
text.inputEl.style.width = "100%";
text.setValue(agentTask);
})
titleSetting.nameEl.style.display = "none";
titleSetting.descEl.style.display = "none";
titleSetting.infoEl.style.display = "none";
*/
const textAreaSetting = new ea.obsidian.Setting(contentEl)
.addTextArea(text => {
textArea = text;
text.inputEl.style.minHeight = "10em";
text.inputEl.style.minWidth = "400px";
text.inputEl.style.width = "100%";
text.setValue(allSystemPrompts[agentTask]);
text.onChange(value => {
// dirty = true;
//Needs further work
allSystemPrompts[agentTask] = value;
})
})
textAreaSetting.nameEl.style.display = "none";
textAreaSetting.descEl.style.display = "none";
textAreaSetting.infoEl.style.display = "none";
new ea.obsidian.Setting(contentEl)
.addButton(button =>
button
.setButtonText("Run")
.onClick((event)=>{
setTimeout(()=>{run()},500); //Obsidian crashes otherwise, likely has to do with requesting an new frame for react
configModal.close();
})
);
}
configModal.onClose = () => {
if(dirty) {
settings["Custom System Prompts"] = customSystemPrompts;
settings["Agent's Task"] = agentTask;
settings["Output Type"] = outputType;
ea.setScriptSettings(settings);
}
}
configModal.open();

1
ea-scripts/ExcaliAI.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M320 0c17.7 0 32 14.3 32 32V96H472c39.8 0 72 32.2 72 72V440c0 39.8-32.2 72-72 72H168c-39.8 0-72-32.2-72-72V168c0-39.8 32.2-72 72-72H288V32c0-17.7 14.3-32 32-32zM208 384c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H208zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H304zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H400zM264 256a40 40 0 1 0 -80 0 40 40 0 1 0 80 0zm152 40a40 40 0 1 0 0-80 40 40 0 1 0 0 80zM48 224H64V416H48c-26.5 0-48-21.5-48-48V272c0-26.5 21.5-48 48-48zm544 0c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H576V224h16z"/></svg>

After

Width:  |  Height:  |  Size: 694 B

File diff suppressed because one or more lines are too long

View File

@@ -115,6 +115,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/Auto%20Draw%20for%20Pen.svg"/></div>|[[#Auto Draw for Pen]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Boolean%20Operations.svg"/></div>|[[#Boolean Operations]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Copy%20Selected%20Element%20Styles%20to%20Global.svg"/></div>|[[#Copy Selected Element Styles to Global]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/ExcaliAI.svg"/></div>|[[#ExcaliAI]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/GPT-Draw-a-UI.svg"/></div>|[[#GPT Draw-a-UI]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Hardware%20Eraser%20Support.svg"/></div>|[[#Hardware Eraser Support]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Palette%20loader.svg"/></div>|[[#Palette Loader]]|
@@ -351,6 +352,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/7flash'>@7flash</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/Grid%20Selected%20Images.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script arranges selected images into compact grid view, removing gaps in-between, resizing when necessary and breaking into multiple rows/columns.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid-selected-images.png'></td></tr></table>
## ExcaliAI
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/ExcaliAI.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/ExcaliAI.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Draw a UI and let GPT create the code for you.<br><iframe width="400" height="225" src="https://www.youtube.com/embed/y3kHl_6Ll4w" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-draw-a-ui.jpg'></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