From 00950b36b9d1fcb5a74607c7ea511241096b9158 Mon Sep 17 00:00:00 2001 From: blindmansion Date: Sun, 8 Sep 2024 21:11:24 -0400 Subject: [PATCH] Fix bugs with reference nodes --- packages/cannoli-core/src/fileManager.ts | 2 +- .../vertices/nodes/content/ReferenceNode.ts | 14 ++- .../cannoli-plugin/assets/cannoliCollege.js | 34 ++++---- .../cannoli-plugin/src/vault_interface.ts | 86 ++++++++----------- 4 files changed, 63 insertions(+), 73 deletions(-) diff --git a/packages/cannoli-core/src/fileManager.ts b/packages/cannoli-core/src/fileManager.ts index fddd2ce..e2b4232 100644 --- a/packages/cannoli-core/src/fileManager.ts +++ b/packages/cannoli-core/src/fileManager.ts @@ -52,7 +52,7 @@ export interface FileManager { path: string, content?: string, verbose?: boolean - ): Promise; + ): Promise; getNotePath(noteName: string): Promise; diff --git a/packages/cannoli-core/src/graph/objects/vertices/nodes/content/ReferenceNode.ts b/packages/cannoli-core/src/graph/objects/vertices/nodes/content/ReferenceNode.ts index f29ca11..39ff3bf 100644 --- a/packages/cannoli-core/src/graph/objects/vertices/nodes/content/ReferenceNode.ts +++ b/packages/cannoli-core/src/graph/objects/vertices/nodes/content/ReferenceNode.ts @@ -123,7 +123,7 @@ export class ReferenceNode extends ContentNode { (this.reference.type === ReferenceType.Variable && this.reference.shouldExtract) ) { - await this.processDynamicReference(""); + await this.processDynamicReference(null); const fetchedContent = await this.getContent(); await this.loadOutgoingEdges(fetchedContent); @@ -208,12 +208,12 @@ export class ReferenceNode extends ContentNode { return `Could not find reference.`; } - async processDynamicReference(content: string) { + async processDynamicReference(content: string | null) { if (this.run.isMock) { return; } - const incomingEdges = this.getIncomingEdges(); + const incomingEdges = this.getAllAvailableProvideEdges(); // Find the incoming edge with the same name as the reference name const referenceNameEdge = incomingEdges.find( @@ -303,7 +303,7 @@ export class ReferenceNode extends ContentNode { noteName = await this.run.fileManager.createNoteAtExistingPath( referenceNameEdge.content, path, - content + content ?? "" ); } catch (e) { this.error(`Could not create note: ${e.message}`); @@ -520,6 +520,12 @@ export class ReferenceNode extends ContentNode { } } else if (edgeObject.edgeModifier === EdgeModifier.Note) { // Load the edge with the name of the note + + // Add double brackets if the reference name is not already in double brackets + if (!this.reference.name.startsWith("[[") && !this.reference.name.endsWith("]]")) { + this.reference.name = `[[${this.reference.name}]]`; + } + edgeObject.load({ content: `${this.reference.name}`, request: request, diff --git a/packages/cannoli-plugin/assets/cannoliCollege.js b/packages/cannoli-plugin/assets/cannoliCollege.js index 58536ed..c0acc9d 100644 --- a/packages/cannoli-plugin/assets/cannoliCollege.js +++ b/packages/cannoli-plugin/assets/cannoliCollege.js @@ -1,5 +1,5 @@ export const cannoliCollege = { - "1. Basics": [ + "1. Basics": [ { name: "1. Hello world.canvas", content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"161279baf7763214\",\"x\":-174,\"y\":-160,\"width\":394,\"height\":340,\"label\":\"Cannoli\"},{\"type\":\"text\",\"text\":\"Hello world!\",\"id\":\"dc3f9351f787531e\",\"x\":-97,\"y\":-91,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"\",\"id\":\"bc1a550bbb4aac7b\",\"x\":-97,\"y\":60,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"The purple node is a content node. Content nodes can be used to store and display text that can be read or written by you or another node.\",\"id\":\"f77169b141243e65\",\"x\":240,\"y\":30,\"width\":420,\"height\":120},{\"type\":\"text\",\"text\":\"The colorless, green, or yellow node is an AI node. AI nodes make a chat completion call to the LLM with the text of the node as a user message.\",\"id\":\"0af5a453bd4d6ec9\",\"x\":240,\"y\":-110,\"width\":380,\"height\":120},{\"type\":\"text\",\"text\":\"If a node in a cannoli is floating (no arrows attached) it won't affect the cannoli unless it is formatted in a special way we'll go over later.\",\"id\":\"7d005c80299f3674\",\"x\":-202,\"y\":220,\"width\":461,\"height\":112},{\"type\":\"text\",\"text\":\"This is a Cannoli. It's made up of different types of nodes and arrows.\\n\\nTry running it by clicking the Cannoli button in the control ribbon on the left side of your Obsidian window.\",\"id\":\"24dd96c964700992\",\"x\":-147,\"y\":-400,\"width\":350,\"height\":180},{\"type\":\"text\",\"text\":\"Cannolis can be run in several ways:\\n\\n- Click the Cannoli ribbon icon\\n - If you're on a canvas file, it will be run as a cannoli\\n - If you're on a note with a \\\"cannoli\\\" property, the canvas file in that property will be run as a cannoli\\n- Run the \\\"Start/Stop cannoli\\\" command in the command palette (functions the same as the ribbon icon)\\n- If a canvas file name ends with \\\".cno\\\", it will have its own run command in the command palette\\n- Make an audio recording on a note with a \\\"cannoli\\\" property\\n\\t- That recording will be transcribed, replace the reference, and trigger the cannoli defined in the property.\",\"id\":\"927cf33512b0dfc1\",\"x\":-740,\"y\":-182,\"width\":538,\"height\":402}],\"edges\":[{\"id\":\"23c2d7dffb49bf75\",\"fromNode\":\"dc3f9351f787531e\",\"fromSide\":\"bottom\",\"toNode\":\"bc1a550bbb4aac7b\",\"toSide\":\"top\"}]}" @@ -20,8 +20,8 @@ export const cannoliCollege = { name: "5. Variable arrows.canvas", content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"4dd63eded63256f2\",\"x\":-172,\"y\":240,\"width\":665,\"height\":460,\"label\":\"Multiple variables\"},{\"type\":\"group\",\"id\":\"d83444fe5b49fb2c\",\"x\":-160,\"y\":800,\"width\":653,\"height\":463,\"label\":\"Between call nodes\"},{\"type\":\"group\",\"id\":\"c409a28317a5ae26\",\"x\":11,\"y\":-360,\"width\":294,\"height\":510,\"label\":\"Variables\"},{\"type\":\"text\",\"text\":\"Pannetone\",\"id\":\"0d48f7722e5db52e\",\"x\":31,\"y\":-340,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"The label of the arrow names the variable, and you can inject it into an AI node using the format \\\"{{variable name}}\\\"\",\"id\":\"1a3b5db348375dba\",\"x\":-380,\"y\":-199,\"width\":371,\"height\":119},{\"type\":\"text\",\"text\":\"How do you make {{dessert}}?\",\"id\":\"10cedae5ccf81664\",\"x\":31,\"y\":-180,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"\",\"id\":\"5cf958d72a55d52d\",\"x\":32,\"y\":-24,\"width\":250,\"height\":154,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Labeling an arrow makes it a variable you can access in AI nodes.\",\"id\":\"db8924f8ddaaa08e\",\"x\":-13,\"y\":-520,\"width\":330,\"height\":86},{\"type\":\"text\",\"text\":\"Hannibal\",\"id\":\"b2aa8b695a7b7774\",\"x\":-152,\"y\":260,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Elephants\",\"id\":\"c23271f22630c4a9\",\"x\":223,\"y\":260,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"What connects {{a}} and {{b}}?\",\"id\":\"4f84654825f9df4c\",\"x\":34,\"y\":420,\"width\":293,\"height\":75},{\"type\":\"text\",\"text\":\"You can name and inject the output of AI nodes the same way.\",\"id\":\"835c4e523796df6e\",\"x\":-500,\"y\":915,\"width\":320,\"height\":80},{\"type\":\"text\",\"text\":\"Rate this limerick:\\n{{limerick}}\",\"id\":\"25d842fe884f4e78\",\"x\":-140,\"y\":995,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"Respond with a limerick about Cannolis\",\"id\":\"1714bc9f52099a91\",\"x\":-140,\"y\":820,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"\",\"id\":\"84fb2c8243704c1a\",\"x\":55,\"y\":550,\"width\":250,\"height\":130,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"\",\"id\":\"13cde1cb12272304\",\"x\":-140,\"y\":1110,\"width\":250,\"height\":133,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Are you sure its {{city}}?\",\"id\":\"fe8260fd047fe01a\",\"x\":192,\"y\":996,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"\",\"id\":\"26d4da45eb95d333\",\"x\":192,\"y\":1110,\"width\":250,\"height\":133,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"What's the capital of Italy?\",\"id\":\"44d0ee25c2f23e66\",\"x\":192,\"y\":820,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"Multiple arrows going into an AI node can be used to inject multiple variables.\",\"id\":\"0fd0a4b39c942f0b\",\"x\":-426,\"y\":399,\"width\":246,\"height\":117},{\"type\":\"text\",\"text\":\"Labeled basic arrows between AI nodes will not pass along the chat history by default, only the content of the response.\\n\\nYou can override this by adding a \\\"|\\\" symbol at the end of the arrow label like so. Then you can continue a conversation as well as use a variable.\",\"id\":\"1fadbba4649fa725\",\"x\":520,\"y\":955,\"width\":380,\"height\":245}],\"edges\":[{\"id\":\"139dad9317421572\",\"fromNode\":\"0d48f7722e5db52e\",\"fromSide\":\"bottom\",\"toNode\":\"10cedae5ccf81664\",\"toSide\":\"top\",\"label\":\"dessert\"},{\"id\":\"fa1b26557addc3e5\",\"fromNode\":\"b2aa8b695a7b7774\",\"fromSide\":\"bottom\",\"toNode\":\"4f84654825f9df4c\",\"toSide\":\"top\",\"label\":\"a\"},{\"id\":\"ece89fb689451485\",\"fromNode\":\"c23271f22630c4a9\",\"fromSide\":\"bottom\",\"toNode\":\"4f84654825f9df4c\",\"toSide\":\"top\",\"label\":\"b\"},{\"id\":\"b9eadfcbf2c54fe1\",\"fromNode\":\"10cedae5ccf81664\",\"fromSide\":\"bottom\",\"toNode\":\"5cf958d72a55d52d\",\"toSide\":\"top\"},{\"id\":\"3a41dc5849268edf\",\"fromNode\":\"4f84654825f9df4c\",\"fromSide\":\"bottom\",\"toNode\":\"84fb2c8243704c1a\",\"toSide\":\"top\"},{\"id\":\"7cf5bbc070866504\",\"fromNode\":\"25d842fe884f4e78\",\"fromSide\":\"bottom\",\"toNode\":\"13cde1cb12272304\",\"toSide\":\"top\"},{\"id\":\"3b347865a761d32b\",\"fromNode\":\"1714bc9f52099a91\",\"fromSide\":\"bottom\",\"toNode\":\"25d842fe884f4e78\",\"toSide\":\"top\",\"label\":\"limerick\"},{\"id\":\"f93a6b2b699a1bf6\",\"fromNode\":\"44d0ee25c2f23e66\",\"fromSide\":\"bottom\",\"toNode\":\"fe8260fd047fe01a\",\"toSide\":\"top\",\"label\":\"city|\"},{\"id\":\"0fa4378c8752aca4\",\"fromNode\":\"fe8260fd047fe01a\",\"fromSide\":\"bottom\",\"toNode\":\"26d4da45eb95d333\",\"toSide\":\"top\"}]}" }, -], - "2. Special arrows": [ + ], + "2. Special arrows": [ { name: "1. Config arrows.canvas", content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"b388b74fc76f9d5b\",\"x\":-411,\"y\":420,\"width\":800,\"height\":480,\"label\":\"Config Arrows\"},{\"type\":\"group\",\"id\":\"50e47210c3597f2c\",\"x\":-382,\"y\":-274,\"width\":742,\"height\":442,\"label\":\"Logging Arrows\"},{\"type\":\"text\",\"text\":\"What's the weakest?\",\"id\":\"01b6cd0557436d0d\",\"x\":-362,\"y\":77,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"You're an expert in metallurgy\",\"id\":\"cd8bccad9c0ff0e1\",\"x\":-362,\"y\":-254,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"What's the strongest metal?\",\"id\":\"d90d1eda5b1cee39\",\"x\":-362,\"y\":-94,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"The logging arrow will write the chat history, response, and config of the AI node to the content node it is pointing to.\",\"id\":\"82607dbca24bcc3b\",\"x\":-720,\"y\":48,\"width\":325,\"height\":120},{\"type\":\"text\",\"text\":\"As a cannoli gets more complex, it can be difficult to know what the LLM is actually seeing in each AI node.\\n\\nA blank config arrow leaving a AI node will log out everything the LLM saw on that request, as well as the config of that AI node.\",\"id\":\"2eddfb2df05ffd92\",\"x\":-932,\"y\":-224,\"width\":537,\"height\":160},{\"type\":\"text\",\"text\":\"Orange arrows are config arrows. You can change the color of arrows by left clicking on them and then clicking on the color pallet \",\"id\":\"f1b3951087221764\",\"x\":-160,\"y\":-440,\"width\":393,\"height\":118},{\"type\":\"text\",\"text\":\"\",\"id\":\"18629c0795e99c52\",\"x\":-28,\"y\":-158,\"width\":365,\"height\":295,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Labeled config arrows pointing to AI nodes will try to set their content to the LLM config setting named in the arrow's label.\",\"id\":\"9750898ccab8b2ec\",\"x\":-190,\"y\":240,\"width\":325,\"height\":130},{\"type\":\"text\",\"text\":\"1.5\",\"id\":\"f2066662ccfdf12b\",\"x\":-228,\"y\":450,\"width\":106,\"height\":68,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"When will gpt-5 come out?\",\"id\":\"901e534d654dbe8d\",\"x\":-285,\"y\":640,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"gpt-4\",\"id\":\"32895b9cbe65ebf5\",\"x\":-385,\"y\":450,\"width\":100,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"\",\"id\":\"f9768071dcaaf641\",\"x\":24,\"y\":445,\"width\":345,\"height\":415,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Config arrows can also be used to set the LLM config of AI nodes, overriding the default.\",\"id\":\"f75c463c060098f5\",\"x\":-760,\"y\":442,\"width\":324,\"height\":106},{\"type\":\"text\",\"text\":\"This AI node will request a gpt-4 response with a temperature of 1.5, overriding the defaults.\\n\\n(You can set the defaults in the plugin settings)\",\"id\":\"1a6e63c23f2fd598\",\"x\":-760,\"y\":581,\"width\":324,\"height\":179}],\"edges\":[{\"id\":\"3e19de32f2cf2234\",\"fromNode\":\"01b6cd0557436d0d\",\"fromSide\":\"right\",\"toNode\":\"18629c0795e99c52\",\"toSide\":\"left\",\"color\":\"2\"},{\"id\":\"a5ea03afb9e7519f\",\"fromNode\":\"d90d1eda5b1cee39\",\"fromSide\":\"bottom\",\"toNode\":\"01b6cd0557436d0d\",\"toSide\":\"top\"},{\"id\":\"e60e398ffd796673\",\"fromNode\":\"cd8bccad9c0ff0e1\",\"fromSide\":\"bottom\",\"toNode\":\"d90d1eda5b1cee39\",\"toSide\":\"top\"},{\"id\":\"4802b580405f5936\",\"fromNode\":\"f2066662ccfdf12b\",\"fromSide\":\"bottom\",\"toNode\":\"901e534d654dbe8d\",\"toSide\":\"top\",\"color\":\"2\",\"label\":\"temperature\"},{\"id\":\"44d9a85dc1209c3f\",\"fromNode\":\"901e534d654dbe8d\",\"fromSide\":\"right\",\"toNode\":\"f9768071dcaaf641\",\"toSide\":\"left\",\"color\":\"2\"},{\"id\":\"18f98f277862e948\",\"fromNode\":\"32895b9cbe65ebf5\",\"fromSide\":\"bottom\",\"toNode\":\"901e534d654dbe8d\",\"toSide\":\"left\",\"color\":\"2\",\"label\":\"model\"}]}" @@ -42,8 +42,8 @@ export const cannoliCollege = { name: "5. Chat arrows.cno.canvas", content: "{\"nodes\":[{\"id\":\"167d3b7b2f6f548d\",\"type\":\"group\",\"x\":-460,\"y\":400,\"width\":1004,\"height\":660,\"label\":\"Limiting chat messages\"},{\"id\":\"ec1597e4e7f3530c\",\"type\":\"group\",\"x\":-209,\"y\":-208,\"width\":290,\"height\":348,\"label\":\"Chat arrows\"},{\"id\":\"09ce49517baf7eda\",\"type\":\"text\",\"text\":\"\",\"x\":-189,\"y\":-60,\"width\":250,\"height\":60},{\"id\":\"b90c7fbfa70e4300\",\"type\":\"text\",\"text\":\"{{NOTE}}\",\"x\":-189,\"y\":60,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"4857524edbae8263\",\"type\":\"text\",\"text\":\"This chat arrow will parse the contents of the reference node into an array of messages\",\"x\":100,\"y\":-147,\"width\":380,\"height\":87},{\"id\":\"ef3cdc2a117f9156\",\"type\":\"text\",\"text\":\"{{NOTE}}\",\"x\":-330,\"y\":420,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"82ba7993840f1760\",\"type\":\"text\",\"text\":\"\",\"x\":-330,\"y\":568,\"width\":250,\"height\":60},{\"id\":\"09a9d8dd0741d74b\",\"type\":\"text\",\"text\":\"\",\"x\":163,\"y\":568,\"width\":250,\"height\":60},{\"id\":\"9a309223d6ce3931\",\"type\":\"text\",\"text\":\"\",\"x\":-440,\"y\":680,\"width\":471,\"height\":360,\"color\":\"6\"},{\"id\":\"f2d8da368e1c51b6\",\"type\":\"text\",\"text\":\"\",\"x\":53,\"y\":680,\"width\":471,\"height\":360,\"color\":\"6\"},{\"id\":\"cd757b7484ffd798\",\"type\":\"text\",\"text\":\"{{NOTE}}\",\"x\":163,\"y\":420,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"d9739a4acdf10a34\",\"type\":\"text\",\"text\":\"This chat node is empty, so it will just send the previous messages it was given to the LLM\",\"x\":-610,\"y\":-86,\"width\":343,\"height\":106},{\"id\":\"2b08e45e39b8eb39\",\"type\":\"text\",\"text\":\"This chat arrow will append the content of the response to the same note, as a stream.\",\"x\":100,\"y\":-30,\"width\":380,\"height\":120},{\"id\":\"ce80fff3047872f3\",\"type\":\"text\",\"text\":\"The final chat arrow only sends the final response, so you can do anything you like with the space between the two chat arrows. \\n\\nUse this flexibility to build chatbots that send http requests, make decisions, edit notes, or gather relevant context.\",\"x\":-717,\"y\":60,\"width\":459,\"height\":200},{\"id\":\"1097b1b80a3abe81\",\"type\":\"text\",\"text\":\"{{NOTE}}\",\"x\":-189,\"y\":-188,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"a8e6750d3ca80fff\",\"type\":\"text\",\"text\":\"This is a special reference node that references the note that was active when the cannoli was run.\",\"x\":-717,\"y\":-211,\"width\":450,\"height\":83},{\"id\":\"2d01d9688ecbe4da\",\"type\":\"text\",\"text\":\"Green arrows are chat arrows. They can be used to build custom chatbots within obsidian using Cannoli.\\n\\nTry this by moving to a note, then running this cannoli using the \\\"4. Chat arrows\\\" command.\",\"x\":-277,\"y\":-440,\"width\":427,\"height\":180},{\"id\":\"6ff1bc87ce822f42\",\"x\":-890,\"y\":489,\"width\":403,\"height\":218,\"type\":\"text\",\"text\":\"You can limit the number of messages parsed from a note by adding a number to the label.\\n\\nJust a number will limit the number of messages, and adding a \\\"#\\\" to the beginning of the label will limit based on an approximation of tokens. \"},{\"id\":\"2213c3caec2e3835\",\"x\":-800,\"y\":804,\"width\":315,\"height\":96,\"type\":\"text\",\"text\":\"The example on the left will send the most recent two messages from the note\"},{\"id\":\"fd4602caf5adb9bc\",\"x\":560,\"y\":508,\"width\":440,\"height\":90,\"type\":\"text\",\"text\":\"This chat arrow will send messages not exceeding the estimated token limit (but at least 1 message)\"}],\"edges\":[{\"id\":\"dddd3b587c8b7340\",\"fromNode\":\"1097b1b80a3abe81\",\"fromSide\":\"bottom\",\"toNode\":\"09ce49517baf7eda\",\"toSide\":\"top\",\"color\":\"4\"},{\"id\":\"847946cb0fc14d7a\",\"fromNode\":\"09ce49517baf7eda\",\"fromSide\":\"bottom\",\"toNode\":\"b90c7fbfa70e4300\",\"toSide\":\"top\",\"color\":\"4\"},{\"id\":\"8cc7de72b0881d19\",\"fromNode\":\"ef3cdc2a117f9156\",\"fromSide\":\"bottom\",\"toNode\":\"82ba7993840f1760\",\"toSide\":\"top\",\"color\":\"4\",\"label\":\"2\"},{\"id\":\"a738da5d15aa4bd8\",\"fromNode\":\"82ba7993840f1760\",\"fromSide\":\"bottom\",\"toNode\":\"9a309223d6ce3931\",\"toSide\":\"top\",\"color\":\"2\"},{\"id\":\"b2376898a9f7d1f1\",\"fromNode\":\"09a9d8dd0741d74b\",\"fromSide\":\"bottom\",\"toNode\":\"f2d8da368e1c51b6\",\"toSide\":\"top\",\"color\":\"2\"},{\"id\":\"9406dd17d5c41032\",\"fromNode\":\"cd757b7484ffd798\",\"fromSide\":\"bottom\",\"toNode\":\"09a9d8dd0741d74b\",\"toSide\":\"top\",\"color\":\"4\",\"label\":\"#500\"}]}" }, -], - "3. Special nodes": [ + ], + "3. Special nodes": [ { name: "1. Floating nodes.canvas", content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"6221f4693ddb3417\",\"x\":-320,\"y\":-80,\"width\":600,\"height\":460,\"label\":\"Floating nodes\"},{\"type\":\"text\",\"text\":\"Use a specific format on a floating node (no attached arrows) to create a global variable. You can read and write to this variable anywhere in the cannoli.\",\"id\":\"9650a6926008b16f\",\"x\":-234,\"y\":-259,\"width\":414,\"height\":119},{\"type\":\"text\",\"text\":\"Now just write the new version by itself in your next response.\",\"id\":\"867aca6f08aff01f\",\"x\":-1,\"y\":124,\"width\":250,\"height\":116,\"color\":\"0\"},{\"type\":\"text\",\"text\":\"Please critique and rewrite this email to be a bit more apologetic:\\n{{Email Draft}}\",\"id\":\"848648ac1ab74998\",\"x\":-14,\"y\":-60,\"width\":274,\"height\":120,\"color\":\"0\"},{\"type\":\"text\",\"text\":\"{{Email Draft}}\",\"id\":\"5246f4d07c4012a8\",\"x\":-1,\"y\":300,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"In this example we reference and write to a floating node.\",\"id\":\"38e2ac4e746d3f44\",\"x\":-580,\"y\":-30,\"width\":250,\"height\":90},{\"type\":\"text\",\"text\":\"When a floating node's first line is in single square brackets, it can be accessed as a variable.\",\"id\":\"ff6cf4c87583cf77\",\"x\":-580,\"y\":111,\"width\":250,\"height\":129},{\"type\":\"text\",\"text\":\"You can reference floating node variables the same way you'd reference normal variables.\",\"id\":\"80a63e1ff8700f60\",\"x\":300,\"y\":-40,\"width\":340,\"height\":120},{\"type\":\"text\",\"text\":\"This is an example of a Reference Node. When a content node is just a floating node reference or note reference wrapped in double curly braces, you can write to the referenced location instead of the node itself.\",\"id\":\"f0466f473408290b\",\"x\":300,\"y\":240,\"width\":516,\"height\":127},{\"type\":\"text\",\"text\":\"[Email Draft]\\nLet me get back to you on that\",\"id\":\"329298ec10140a51\",\"x\":-300,\"y\":75,\"width\":250,\"height\":99}],\"edges\":[{\"id\":\"f33f402d10538f79\",\"fromNode\":\"848648ac1ab74998\",\"fromSide\":\"bottom\",\"toNode\":\"867aca6f08aff01f\",\"toSide\":\"top\"},{\"id\":\"bfad0ddbfa29f18f\",\"fromNode\":\"867aca6f08aff01f\",\"fromSide\":\"bottom\",\"toNode\":\"5246f4d07c4012a8\",\"toSide\":\"top\"}]}" @@ -60,15 +60,15 @@ export const cannoliCollege = { name: "4. Reference nodes.canvas", content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"546578b6fae29490\",\"x\":-420,\"y\":-160,\"width\":580,\"height\":640,\"label\":\"Reference nodes\"},{\"id\":\"8d414d7be725ce11\",\"type\":\"group\",\"x\":-420,\"y\":880,\"width\":610,\"height\":259,\"label\":\"Getting properties\"},{\"id\":\"8fd4e3f6e22304ba\",\"type\":\"group\",\"x\":-420,\"y\":560,\"width\":290,\"height\":259,\"label\":\"Getting note name\"},{\"id\":\"8c0fb44530075f7d\",\"type\":\"group\",\"x\":-420,\"y\":1200,\"width\":290,\"height\":259,\"label\":\"Getting note path\"},{\"type\":\"text\",\"text\":\"{{Current state}}\",\"id\":\"079588e3f7e0afa0\",\"x\":-393,\"y\":203,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Reference nodes can reference floating nodes or notes from your vault.\",\"id\":\"a8346a9d96a737bb\",\"x\":-692,\"y\":238,\"width\":262,\"height\":114},{\"type\":\"text\",\"text\":\"Reference nodes are content nodes surrounded by double curly braces.\",\"id\":\"a9ea85b101bc8776\",\"x\":-686,\"y\":-149,\"width\":250,\"height\":129},{\"type\":\"text\",\"text\":\"You can write or read from them the same way you use normal content nodes.\",\"id\":\"be5c114d263a0107\",\"x\":-686,\"y\":66,\"width\":250,\"height\":137},{\"type\":\"text\",\"text\":\"Write a poem about walking\",\"id\":\"fba8c017a84a0a74\",\"x\":-380,\"y\":-124,\"width\":250,\"height\":60,\"color\":\"0\"},{\"type\":\"text\",\"text\":\"{{[[Walking Poem]]}}\",\"id\":\"e8e998b3d784e3a1\",\"x\":-380,\"y\":6,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Reference nodes can be used to write to and read from notes in your vault or floating nodes on the cannoli.\",\"id\":\"28c071d10dfda17f\",\"x\":-333,\"y\":-340,\"width\":379,\"height\":122},{\"type\":\"text\",\"text\":\"[Current state]\\nNot much going on\",\"id\":\"e08885cd2f146f7d\",\"x\":-113,\"y\":203,\"width\":250,\"height\":92},{\"id\":\"344bb3da359269a4\",\"type\":\"text\",\"text\":\"\",\"x\":-100,\"y\":327,\"width\":237,\"height\":113,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Does this change our plan at all:\\n{{a}}\",\"id\":\"e9768515f6295d9b\",\"x\":-393,\"y\":333,\"width\":250,\"height\":100,\"color\":\"0\"},{\"id\":\"963eacfd11f92b34\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-400,\"y\":580,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"ef10f47160ceb92a\",\"type\":\"text\",\"text\":\"\",\"x\":-400,\"y\":739,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"f85bdd621bacd536\",\"type\":\"text\",\"text\":\"If a variable arrow coming from a reference node starts with a square bracket, it will extract the link for the reference.\\n\\nThis is uses in cases when you dynamically create nodes and need to get their link reliably.\",\"x\":-763,\"y\":580,\"width\":318,\"height\":239},{\"id\":\"da10ade6ef9ec7fb\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-400,\"y\":900,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"d0e83b31dc2a1576\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-90,\"y\":900,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"1566407420c6d37d\",\"type\":\"text\",\"text\":\"\",\"x\":-400,\"y\":1059,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"68e42dd6d4721f26\",\"type\":\"text\",\"text\":\"\",\"x\":-90,\"y\":1059,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"28a0281c87a0c588\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-400,\"y\":1220,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"babacacceece0d0f\",\"type\":\"text\",\"text\":\"\",\"x\":-400,\"y\":1379,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"bad9bf674fca2bfa\",\"type\":\"text\",\"text\":\"You can use arrows that start with \\\":\\\" to retrieve the associated property of the same name from a note.\\n\\nIf there is nothing on the arrow other than \\\":\\\", all properties will be retrieved. \",\"x\":-763,\"y\":900,\"width\":318,\"height\":239},{\"id\":\"b6b1e156ad3548a2\",\"type\":\"text\",\"text\":\"You can use arrows that start with \\\"/\\\" to retrieve the path of a reference node.\\n\\nThis could be useful for creating notes in the same folder as dynamically selected notes.\",\"x\":-763,\"y\":1220,\"width\":318,\"height\":219}],\"edges\":[{\"id\":\"bc3f3aa5abc3914d\",\"fromNode\":\"fba8c017a84a0a74\",\"fromSide\":\"bottom\",\"toNode\":\"e8e998b3d784e3a1\",\"toSide\":\"top\"},{\"id\":\"c98efd6bf2dff5d4\",\"fromNode\":\"079588e3f7e0afa0\",\"fromSide\":\"bottom\",\"toNode\":\"e9768515f6295d9b\",\"toSide\":\"top\",\"label\":\"a\"},{\"id\":\"20b93bd61e36c19f\",\"fromNode\":\"963eacfd11f92b34\",\"fromSide\":\"bottom\",\"toNode\":\"ef10f47160ceb92a\",\"toSide\":\"top\",\"label\":\"[\"},{\"id\":\"d1ab9632f705d309\",\"fromNode\":\"e9768515f6295d9b\",\"fromSide\":\"right\",\"toNode\":\"344bb3da359269a4\",\"toSide\":\"left\",\"color\":\"2\"},{\"id\":\"4320417bc6736dd9\",\"fromNode\":\"da10ade6ef9ec7fb\",\"fromSide\":\"bottom\",\"toNode\":\"1566407420c6d37d\",\"toSide\":\"top\",\"label\":\":Date\"},{\"id\":\"e56ac04c2a7ef6da\",\"fromNode\":\"d0e83b31dc2a1576\",\"fromSide\":\"bottom\",\"toNode\":\"68e42dd6d4721f26\",\"toSide\":\"top\",\"label\":\":\"},{\"id\":\"06d6a4450c69c049\",\"fromNode\":\"28a0281c87a0c588\",\"fromSide\":\"bottom\",\"toNode\":\"babacacceece0d0f\",\"toSide\":\"top\",\"label\":\"/\"}]}" }, -], - "4. Vault interaction": [ + ], + "4. Vault interaction": [ { name: "1. Referencing notes.canvas", content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"34ab67ca730bb363\",\"x\":-700,\"y\":420,\"width\":960,\"height\":500,\"label\":\"Extraction modifiers\"},{\"type\":\"group\",\"id\":\"1e30c5b622c33707\",\"x\":-700,\"y\":-160,\"width\":600,\"height\":500,\"label\":\"Reference nodes\"},{\"type\":\"group\",\"id\":\"4b47baa584657fc0\",\"x\":-700,\"y\":-620,\"width\":650,\"height\":360,\"label\":\"Notes as variables\"},{\"id\":\"3c55aea3ab7679cf\",\"type\":\"group\",\"x\":-690,\"y\":1680,\"width\":610,\"height\":259,\"label\":\"Getting properties\"},{\"id\":\"71f82d60459605e9\",\"type\":\"group\",\"x\":-690,\"y\":1000,\"width\":290,\"height\":290,\"label\":\"Special \\\"NOTE\\\" reference\"},{\"id\":\"bc6196ed2a59141c\",\"type\":\"group\",\"x\":-690,\"y\":1360,\"width\":290,\"height\":259,\"label\":\"Getting note names\"},{\"id\":\"f5c0436b8aa42d7f\",\"type\":\"group\",\"x\":-690,\"y\":2000,\"width\":290,\"height\":259,\"label\":\"Getting note path\"},{\"type\":\"text\",\"text\":\"Check this out:\\n{{[[Lemons for Cleaning]]}}\",\"id\":\"24fc10789416284c\",\"x\":-680,\"y\":-600,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"What was in that note?\",\"id\":\"e62b59877d19a53b\",\"x\":-680,\"y\":-490,\"width\":250,\"height\":50},{\"type\":\"text\",\"text\":\"\",\"id\":\"62447884955fc816\",\"x\":-409,\"y\":-560,\"width\":349,\"height\":280,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"You can reference notes in Cannolis to give LLMs up-to-date context conveniently.\",\"id\":\"305dbf844cee4b22\",\"x\":-562,\"y\":-780,\"width\":328,\"height\":102},{\"type\":\"text\",\"text\":\"To show the LLM the content of a note, simply wrap a note link in curly braces, just as you would a variable.\",\"id\":\"9ae16a6025e83401\",\"x\":-1060,\"y\":-521,\"width\":345,\"height\":113},{\"type\":\"file\",\"file\":\"Cannoli College/Example notes/Apple Trees.md\",\"text\":\"{{[[Apple Trees]]}}\",\"id\":\"b7597d6f39e7acc9\",\"x\":-690,\"y\":-120,\"width\":283,\"height\":145},{\"type\":\"text\",\"text\":\"What's the note above about\",\"id\":\"8039b20f5e260110\",\"x\":-673,\"y\":80,\"width\":250,\"height\":80},{\"type\":\"text\",\"text\":\"What's this about:\\n{{a}}\",\"id\":\"dec28b770c6e0601\",\"x\":-359,\"y\":80,\"width\":250,\"height\":80},{\"type\":\"text\",\"text\":\"\",\"id\":\"37c33e8f19a09beb\",\"x\":-680,\"y\":-380,\"width\":250,\"height\":100,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"You can also pull the content of notes with arrows leaving Obsidian's built-in file cards.\",\"id\":\"7563d53b40bf5f1a\",\"x\":-986,\"y\":-101,\"width\":271,\"height\":126},{\"type\":\"text\",\"text\":\"The same effect can be achieved with a reference node, using this double curly brace notation in a content node.\",\"id\":\"7486aafd14a866f8\",\"x\":-80,\"y\":-103,\"width\":340,\"height\":111},{\"type\":\"text\",\"text\":\"{{[[Lemons for Cleaning]]}}\",\"id\":\"aa6a67a2ff0f2fe8\",\"x\":-359,\"y\":-77,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"\",\"id\":\"a96e14fbb7d1265f\",\"x\":-673,\"y\":200,\"width\":250,\"height\":120,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"\",\"id\":\"7f19194be69549ec\",\"x\":-359,\"y\":200,\"width\":250,\"height\":120,\"color\":\"6\"},{\"id\":\"e7990bd20a1a3f11\",\"type\":\"text\",\"text\":\"{{NOTE}}\",\"x\":-670,\"y\":1020,\"width\":250,\"height\":60},{\"id\":\"da19d9eee04a6946\",\"type\":\"text\",\"text\":\"This reference node will always correspond to the active note when the cannoli was started.\\n\\nYou can use this syntax to build chatbots (see [[4. Chat arrows.cno.canvas|4. Chat arrows.cno]]) or cannolis that process the content of the active note somehow.\",\"x\":-1039,\"y\":1010,\"width\":324,\"height\":260},{\"id\":\"dd06bd8ffd8959c3\",\"type\":\"text\",\"text\":\"\",\"x\":-670,\"y\":1140,\"width\":250,\"height\":130,\"color\":\"6\"},{\"id\":\"d399962128a18b2e\",\"type\":\"text\",\"text\":\"\",\"x\":-680,\"y\":615,\"width\":268,\"height\":277,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]#}}\",\"id\":\"30afb34c1019f5df\",\"x\":-670,\"y\":473,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"b63aa69459ed0e33\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-670,\"y\":1380,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"d19cad0060fcb71c\",\"type\":\"text\",\"text\":\"\",\"x\":-670,\"y\":1539,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"9aff2c48988f7fdf\",\"type\":\"text\",\"text\":\"If a variable arrow coming from a reference node starts with a square bracket, it will extract the link for the reference.\\n\\nThis is uses in cases when you dynamically create nodes and need to get their link reliably.\",\"x\":-1033,\"y\":1380,\"width\":318,\"height\":239},{\"id\":\"9967b60050a55863\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-670,\"y\":1700,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"570d87dc498def43\",\"type\":\"text\",\"text\":\"\",\"x\":-670,\"y\":1859,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"94c9f6ee12de2723\",\"type\":\"text\",\"text\":\"You can use arrows that start with \\\":\\\" to retrieve the associated property of the same name from a note.\\n\\nIf there is nothing on the arrow other than \\\":\\\", all properties will be retrieved. \",\"x\":-1033,\"y\":1700,\"width\":318,\"height\":239},{\"id\":\"382cdbdf89daa53c\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-360,\"y\":1700,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"386367aa8acf2917\",\"type\":\"text\",\"text\":\"\",\"x\":-360,\"y\":1859,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"47f1774a10a6de98\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-670,\"y\":2020,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"09ec11f71f736c5a\",\"type\":\"text\",\"text\":\"\",\"x\":-670,\"y\":2179,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"82df68e8b67c09c8\",\"type\":\"text\",\"text\":\"You can use arrows that start with \\\"/\\\" to retrieve the path of a reference node.\\n\\nThis could be useful for creating notes in the same folder as dynamically selected notes.\",\"x\":-1033,\"y\":2020,\"width\":318,\"height\":219},{\"id\":\"8352e64802f9ff93\",\"type\":\"text\",\"text\":\"You can add multiple modifiers in a single reference:\\n{{[[Note]]^!#}}\",\"x\":280,\"y\":785,\"width\":398,\"height\":120},{\"id\":\"0856fc4c98e0e01c\",\"type\":\"text\",\"text\":\"To include the properties (YAML frontmatter) in the note, reference it like this:\\n{{[[Note]]^}}\\n\\nIf your default setting is to include the properties, you can remove them like this:\\n{{[[Note]]!^}}\\n\\nIf you are editing a note, this modifier will also change if the edit can affect the existing properties. This is useful if you don't want to overwrite properties when you edit a note.\",\"x\":280,\"y\":413,\"width\":420,\"height\":342},{\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]^}}\",\"id\":\"10439db683a6c0a7\",\"x\":-35,\"y\":473,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"919f3852d5538bac\",\"type\":\"text\",\"text\":\"\",\"x\":-44,\"y\":615,\"width\":268,\"height\":277,\"color\":\"6\"},{\"id\":\"faa26d9da34c53d9\",\"type\":\"text\",\"text\":\"\",\"x\":-368,\"y\":615,\"width\":268,\"height\":277,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]@}}\",\"id\":\"cf0cf7433914ad7f\",\"x\":-359,\"y\":473,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"You can change how notes are extracted or edited using symbols after the note within a reference.\\n\\nThese modifiers will override the related settings you chose in the cannoli settings.\",\"id\":\"f98df3d186b6b8b3\",\"x\":-1079,\"y\":371,\"width\":365,\"height\":205},{\"id\":\"21a132196d1026bf\",\"type\":\"text\",\"text\":\"To add the filename of a note as a header, reference it like this:\\n{{[[Note]]#}}\\n\\nIf your default setting is to add the filename header, you can remove it like this:\\n{{[[Note]]!#}}\\n\\nThe same is true of the \\\"@\\\" modifier, which does the same thing but with a markdown link to the note.\",\"x\":-1073,\"y\":615,\"width\":354,\"height\":340}],\"edges\":[{\"id\":\"7ebbdaba21c78245\",\"fromNode\":\"24fc10789416284c\",\"fromSide\":\"right\",\"toNode\":\"62447884955fc816\",\"toSide\":\"top\",\"color\":\"2\"},{\"id\":\"ee4e6166d0ffe43d\",\"fromNode\":\"24fc10789416284c\",\"fromSide\":\"bottom\",\"toNode\":\"e62b59877d19a53b\",\"toSide\":\"top\"},{\"id\":\"063d6d8d083b2666\",\"fromNode\":\"b7597d6f39e7acc9\",\"fromSide\":\"bottom\",\"toNode\":\"8039b20f5e260110\",\"toSide\":\"top\"},{\"id\":\"9bcad28d8d703638\",\"fromNode\":\"aa6a67a2ff0f2fe8\",\"fromSide\":\"bottom\",\"toNode\":\"dec28b770c6e0601\",\"toSide\":\"top\",\"label\":\"a\"},{\"id\":\"ba5e154b30f8168f\",\"fromNode\":\"e62b59877d19a53b\",\"fromSide\":\"bottom\",\"toNode\":\"37c33e8f19a09beb\",\"toSide\":\"top\"},{\"id\":\"6e838ae4192a0925\",\"fromNode\":\"8039b20f5e260110\",\"fromSide\":\"bottom\",\"toNode\":\"a96e14fbb7d1265f\",\"toSide\":\"top\"},{\"id\":\"7f3f25c31ae38f6d\",\"fromNode\":\"dec28b770c6e0601\",\"fromSide\":\"bottom\",\"toNode\":\"7f19194be69549ec\",\"toSide\":\"top\"},{\"id\":\"8af2e272382f3038\",\"fromNode\":\"e7990bd20a1a3f11\",\"fromSide\":\"bottom\",\"toNode\":\"dd06bd8ffd8959c3\",\"toSide\":\"top\"},{\"id\":\"aba263db1eb0f4b6\",\"fromNode\":\"9967b60050a55863\",\"fromSide\":\"bottom\",\"toNode\":\"570d87dc498def43\",\"toSide\":\"top\",\"label\":\":Date\"},{\"id\":\"f0e0c3d61fe7aca3\",\"fromNode\":\"30afb34c1019f5df\",\"fromSide\":\"bottom\",\"toNode\":\"d399962128a18b2e\",\"toSide\":\"top\"},{\"id\":\"e137cb2b47fff78e\",\"fromNode\":\"10439db683a6c0a7\",\"fromSide\":\"bottom\",\"toNode\":\"919f3852d5538bac\",\"toSide\":\"top\"},{\"id\":\"7919954c7360c103\",\"fromNode\":\"b63aa69459ed0e33\",\"fromSide\":\"bottom\",\"toNode\":\"d19cad0060fcb71c\",\"toSide\":\"top\",\"label\":\"[\"},{\"id\":\"a149899b3a0f51b7\",\"fromNode\":\"47f1774a10a6de98\",\"fromSide\":\"bottom\",\"toNode\":\"09ec11f71f736c5a\",\"toSide\":\"top\",\"label\":\"/\"},{\"id\":\"1bb84d00af5d18f5\",\"fromNode\":\"382cdbdf89daa53c\",\"fromSide\":\"bottom\",\"toNode\":\"386367aa8acf2917\",\"toSide\":\"top\",\"label\":\":\"},{\"id\":\"2d839e95573e05a0\",\"fromNode\":\"cf0cf7433914ad7f\",\"fromSide\":\"bottom\",\"toNode\":\"faa26d9da34c53d9\",\"toSide\":\"top\"}]}" }, { name: "2. Writing to notes.canvas", - content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"a81f6864d57513f0\",\"x\":-335,\"y\":300,\"width\":755,\"height\":620,\"label\":\"Tips\"},{\"type\":\"group\",\"id\":\"92f439736587539d\",\"x\":-335,\"y\":-760,\"width\":615,\"height\":477,\"label\":\"Reference nodes\"},{\"id\":\"50c183ba68857287\",\"type\":\"group\",\"x\":-335,\"y\":1000,\"width\":610,\"height\":259,\"label\":\"Editing properties\"},{\"type\":\"group\",\"id\":\"255a410c19a12e56\",\"x\":-335,\"y\":-160,\"width\":355,\"height\":328,\"label\":\"Protecting properties\"},{\"type\":\"text\",\"text\":\"You can write to notes using file cards or the reference node format shown below.\",\"id\":\"d9b027f149b4da0f\",\"x\":-140,\"y\":-942,\"width\":253,\"height\":122},{\"type\":\"text\",\"text\":\"Write a short article about using lemons for cleaning. It should have the title \\\"Lemons for Cleaning\\\" and use proper markdown.\",\"id\":\"e19b655090530949\",\"x\":-312,\"y\":-740,\"width\":255,\"height\":150},{\"type\":\"text\",\"text\":\"Write a short article about using lemons for cleaning. It should have the title \\\"Lemons for Cleaning\\\" and use proper markdown.\",\"id\":\"1de6b113e5ad75c5\",\"x\":8,\"y\":-740,\"width\":250,\"height\":150},{\"type\":\"file\",\"file\":\"Cannoli College/Example notes/Lemons for Cleaning.md\",\"text\":\"{{[[Lemons for Cleaning]]}}\",\"id\":\"d6552cf2b811cb0a\",\"x\":-315,\"y\":-540,\"width\":261,\"height\":237},{\"type\":\"text\",\"text\":\"You can write to a note the same way you'd write to a normal content node, from AI nodes or other content nodes.\",\"id\":\"fe1f2c35a7426056\",\"x\":-640,\"y\":-622,\"width\":280,\"height\":118},{\"type\":\"text\",\"text\":\"{{[[Lemons for Cleaning]]}}\",\"id\":\"2160d3369fea2bcb\",\"x\":8,\"y\":-451,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Write a sample meeting minutes\",\"id\":\"cc3989fddfc7607b\",\"x\":-297,\"y\":-134,\"width\":250,\"height\":80},{\"type\":\"text\",\"text\":\"You can change how notes are extracted or edited using symbols after the note within a reference.\\n\\nThese modifiers will override the related settings you chose in the cannoli settings.\",\"id\":\"bffb0679a38e68b5\",\"x\":-720,\"y\":-134,\"width\":365,\"height\":205},{\"type\":\"text\",\"text\":\"Utilize multiple chat messages to get a clean written response. You can even use a more expensive model to generate the article, then use a less expensive one to copy the final product to the note.\",\"id\":\"98deb4f5f13687c5\",\"x\":-800,\"y\":470,\"width\":440,\"height\":145},{\"type\":\"text\",\"text\":\"Outline and write a short article about growing apple trees.\",\"id\":\"63db7ba6f5cb3524\",\"x\":-312,\"y\":320,\"width\":250,\"height\":100},{\"type\":\"text\",\"text\":\"Write a short poem about walking.\",\"id\":\"9de84ebf983156f8\",\"x\":8,\"y\":320,\"width\":250,\"height\":100},{\"type\":\"file\",\"file\":\"Cannoli College/Example notes/Apple Trees.md\",\"text\":\"{{[[Apple Trees]]}}\",\"id\":\"0561c9ae1669460f\",\"x\":-315,\"y\":715,\"width\":255,\"height\":189},{\"type\":\"text\",\"text\":\"\",\"id\":\"9de536515698d256\",\"x\":220,\"y\":618,\"width\":182,\"height\":62,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Field arrows can help get good formatting for notes. Use \\\"content\\\" and \\\"title\\\" field names to keep the LLM from writing the title of the note into the content.\",\"id\":\"5943bd16d1a71072\",\"x\":440,\"y\":506,\"width\":357,\"height\":119},{\"type\":\"text\",\"text\":\"Ok, now respond with just the article\\n\\nThere should be nothing in your response other than the article\",\"id\":\"94567e5a2b31c42b\",\"x\":-315,\"y\":475,\"width\":255,\"height\":180},{\"id\":\"9fa03f1308317478\",\"type\":\"text\",\"text\":\"To allow the edit to overwrite the properties (YAML frontmatter) in the note, reference it like this:\\n{{[[Note]]^}}\\n\\nIf your default setting is to include (overwrite) the properties, you can remove them like this:\\n{{[[Note]]!^}}\",\"x\":48,\"y\":-122,\"width\":420,\"height\":229},{\"type\":\"text\",\"text\":\"{{[[Lemons for Cleaning]]!^}}\",\"id\":\"6c3aaf24c1410da5\",\"x\":-309,\"y\":77,\"width\":274,\"height\":60,\"color\":\"6\"},{\"type\":\"file\",\"file\":\"Cannoli College/Example notes/Walking Poem.md\",\"text\":\"{{[[Apple Trees]]}}\",\"id\":\"5e54d6df61248d37\",\"x\":8,\"y\":715,\"width\":255,\"height\":189},{\"id\":\"fe4f45ec55ab88f8\",\"type\":\"text\",\"text\":\"You can use arrows that start with \\\":\\\" to edit the property in the arrow label.\\n\\nThis also works for list format properties.\",\"x\":-678,\"y\":1020,\"width\":318,\"height\":189},{\"id\":\"ae32c84e56f650cb\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-310,\"y\":1179,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"b58f063da822b869\",\"type\":\"text\",\"text\":\"Tuesday\",\"x\":-310,\"y\":1020,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"d283ed9e9d24b01d\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":0,\"y\":1179,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"f82f1adc99fcc4db\",\"type\":\"text\",\"text\":\"- jim\\n- mary\",\"x\":0,\"y\":1020,\"width\":250,\"height\":80,\"color\":\"6\"}],\"edges\":[{\"id\":\"32c1cb193a6bfc19\",\"fromNode\":\"e19b655090530949\",\"fromSide\":\"bottom\",\"toNode\":\"d6552cf2b811cb0a\",\"toSide\":\"top\"},{\"id\":\"e339725c61ba4f0b\",\"fromNode\":\"1de6b113e5ad75c5\",\"fromSide\":\"bottom\",\"toNode\":\"2160d3369fea2bcb\",\"toSide\":\"top\"},{\"id\":\"741c9e0b211241a2\",\"fromNode\":\"94567e5a2b31c42b\",\"fromSide\":\"bottom\",\"toNode\":\"0561c9ae1669460f\",\"toSide\":\"top\"},{\"id\":\"38d8c5cd09414fe8\",\"fromNode\":\"9de84ebf983156f8\",\"fromSide\":\"bottom\",\"toNode\":\"5e54d6df61248d37\",\"toSide\":\"top\",\"color\":\"6\",\"label\":\"content\"},{\"id\":\"6e207e77a539fbd3\",\"fromNode\":\"9de84ebf983156f8\",\"fromSide\":\"bottom\",\"toNode\":\"9de536515698d256\",\"toSide\":\"top\",\"color\":\"6\",\"label\":\"title\"},{\"id\":\"e7cd177b0fe4bf6b\",\"fromNode\":\"63db7ba6f5cb3524\",\"fromSide\":\"bottom\",\"toNode\":\"94567e5a2b31c42b\",\"toSide\":\"top\"},{\"id\":\"996659a79f50e2da\",\"fromNode\":\"cc3989fddfc7607b\",\"fromSide\":\"bottom\",\"toNode\":\"6c3aaf24c1410da5\",\"toSide\":\"top\"},{\"id\":\"790403f81a184f3b\",\"fromNode\":\"b58f063da822b869\",\"fromSide\":\"bottom\",\"toNode\":\"ae32c84e56f650cb\",\"toSide\":\"top\",\"label\":\":weekday\"},{\"id\":\"503b5af7f363b512\",\"fromNode\":\"f82f1adc99fcc4db\",\"fromSide\":\"bottom\",\"toNode\":\"d283ed9e9d24b01d\",\"toSide\":\"top\",\"label\":\":tags\"}]}" + content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"a81f6864d57513f0\",\"x\":-335,\"y\":300,\"width\":755,\"height\":620,\"label\":\"Tips\"},{\"type\":\"group\",\"id\":\"92f439736587539d\",\"x\":-335,\"y\":-760,\"width\":615,\"height\":477,\"label\":\"Reference nodes\"},{\"id\":\"50c183ba68857287\",\"type\":\"group\",\"x\":-335,\"y\":1000,\"width\":610,\"height\":259,\"label\":\"Editing properties\"},{\"type\":\"group\",\"id\":\"255a410c19a12e56\",\"x\":-335,\"y\":-160,\"width\":355,\"height\":328,\"label\":\"Protecting properties\"},{\"type\":\"text\",\"text\":\"You can write to notes using file cards or the reference node format shown below.\",\"id\":\"d9b027f149b4da0f\",\"x\":-140,\"y\":-942,\"width\":253,\"height\":122},{\"type\":\"text\",\"text\":\"Write a short article about using lemons for cleaning. It should have the title \\\"Lemons for Cleaning\\\" and use proper markdown.\",\"id\":\"e19b655090530949\",\"x\":-312,\"y\":-740,\"width\":255,\"height\":150},{\"type\":\"text\",\"text\":\"Write a short article about using lemons for cleaning. It should have the title \\\"Lemons for Cleaning\\\" and use proper markdown.\",\"id\":\"1de6b113e5ad75c5\",\"x\":8,\"y\":-740,\"width\":250,\"height\":150},{\"type\":\"file\",\"file\":\"Cannoli College/Example notes/Lemons for Cleaning.md\",\"text\":\"{{[[Lemons for Cleaning]]}}\",\"id\":\"d6552cf2b811cb0a\",\"x\":-315,\"y\":-540,\"width\":261,\"height\":237},{\"type\":\"text\",\"text\":\"You can write to a note the same way you'd write to a normal content node, from AI nodes or other content nodes.\",\"id\":\"fe1f2c35a7426056\",\"x\":-640,\"y\":-622,\"width\":280,\"height\":118},{\"type\":\"text\",\"text\":\"{{[[Lemons for Cleaning]]}}\",\"id\":\"2160d3369fea2bcb\",\"x\":8,\"y\":-451,\"width\":250,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Write a sample meeting minutes\",\"id\":\"cc3989fddfc7607b\",\"x\":-297,\"y\":-134,\"width\":250,\"height\":80},{\"type\":\"text\",\"text\":\"You can change how notes are extracted or edited using symbols after the note within a reference.\\n\\nThese modifiers will override the related settings you chose in the cannoli settings.\",\"id\":\"bffb0679a38e68b5\",\"x\":-720,\"y\":-134,\"width\":365,\"height\":205},{\"type\":\"text\",\"text\":\"Utilize multiple chat messages to get a clean written response. You can even use a more expensive model to generate the article, then use a less expensive one to copy the final product to the note.\",\"id\":\"98deb4f5f13687c5\",\"x\":-800,\"y\":470,\"width\":440,\"height\":145},{\"type\":\"text\",\"text\":\"Outline and write a short article about growing apple trees.\",\"id\":\"63db7ba6f5cb3524\",\"x\":-312,\"y\":320,\"width\":250,\"height\":100},{\"type\":\"text\",\"text\":\"Write a short poem about walking.\",\"id\":\"9de84ebf983156f8\",\"x\":8,\"y\":320,\"width\":250,\"height\":100},{\"type\":\"file\",\"file\":\"Cannoli College/Example notes/Apple Trees.md\",\"text\":\"{{[[Apple Trees]]}}\",\"id\":\"0561c9ae1669460f\",\"x\":-315,\"y\":715,\"width\":255,\"height\":189},{\"type\":\"text\",\"text\":\"\",\"id\":\"9de536515698d256\",\"x\":220,\"y\":618,\"width\":182,\"height\":62,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Field arrows can help get good formatting for notes. Use \\\"content\\\" and \\\"title\\\" field names to keep the LLM from writing the title of the note into the content.\",\"id\":\"5943bd16d1a71072\",\"x\":440,\"y\":506,\"width\":357,\"height\":119},{\"type\":\"text\",\"text\":\"Ok, now respond with just the article\\n\\nThere should be nothing in your response other than the article\",\"id\":\"94567e5a2b31c42b\",\"x\":-315,\"y\":475,\"width\":255,\"height\":180},{\"id\":\"9fa03f1308317478\",\"type\":\"text\",\"text\":\"To allow the edit to overwrite the properties (YAML frontmatter) in the note, reference it like this:\\n{{[[Note]]^}}\\n\\nIf your default setting is to include (overwrite) the properties, you can remove them like this:\\n{{[[Note]]!^}}\",\"x\":48,\"y\":-122,\"width\":420,\"height\":229},{\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]!^}}\",\"id\":\"6c3aaf24c1410da5\",\"x\":-309,\"y\":77,\"width\":274,\"height\":60,\"color\":\"6\"},{\"type\":\"file\",\"file\":\"Cannoli College/Example notes/Walking Poem.md\",\"text\":\"{{[[Apple Trees]]}}\",\"id\":\"5e54d6df61248d37\",\"x\":8,\"y\":715,\"width\":255,\"height\":189},{\"id\":\"fe4f45ec55ab88f8\",\"type\":\"text\",\"text\":\"You can use arrows that start with \\\":\\\" to edit the property in the arrow label.\\n\\nThis also works for list format properties.\",\"x\":-678,\"y\":1020,\"width\":318,\"height\":189},{\"id\":\"ae32c84e56f650cb\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":-310,\"y\":1179,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"b58f063da822b869\",\"type\":\"text\",\"text\":\"Tuesday\",\"x\":-310,\"y\":1020,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"d283ed9e9d24b01d\",\"type\":\"text\",\"text\":\"{{[[Meeting Notes]]}}\",\"x\":0,\"y\":1179,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"f82f1adc99fcc4db\",\"type\":\"text\",\"text\":\"- jim\\n- mary\",\"x\":0,\"y\":1020,\"width\":250,\"height\":80,\"color\":\"6\"}],\"edges\":[{\"id\":\"32c1cb193a6bfc19\",\"fromNode\":\"e19b655090530949\",\"fromSide\":\"bottom\",\"toNode\":\"d6552cf2b811cb0a\",\"toSide\":\"top\"},{\"id\":\"e339725c61ba4f0b\",\"fromNode\":\"1de6b113e5ad75c5\",\"fromSide\":\"bottom\",\"toNode\":\"2160d3369fea2bcb\",\"toSide\":\"top\"},{\"id\":\"741c9e0b211241a2\",\"fromNode\":\"94567e5a2b31c42b\",\"fromSide\":\"bottom\",\"toNode\":\"0561c9ae1669460f\",\"toSide\":\"top\"},{\"id\":\"38d8c5cd09414fe8\",\"fromNode\":\"9de84ebf983156f8\",\"fromSide\":\"bottom\",\"toNode\":\"5e54d6df61248d37\",\"toSide\":\"top\",\"color\":\"6\",\"label\":\"content\"},{\"id\":\"6e207e77a539fbd3\",\"fromNode\":\"9de84ebf983156f8\",\"fromSide\":\"bottom\",\"toNode\":\"9de536515698d256\",\"toSide\":\"top\",\"color\":\"6\",\"label\":\"title\"},{\"id\":\"e7cd177b0fe4bf6b\",\"fromNode\":\"63db7ba6f5cb3524\",\"fromSide\":\"bottom\",\"toNode\":\"94567e5a2b31c42b\",\"toSide\":\"top\"},{\"id\":\"996659a79f50e2da\",\"fromNode\":\"cc3989fddfc7607b\",\"fromSide\":\"bottom\",\"toNode\":\"6c3aaf24c1410da5\",\"toSide\":\"top\"},{\"id\":\"790403f81a184f3b\",\"fromNode\":\"b58f063da822b869\",\"fromSide\":\"bottom\",\"toNode\":\"ae32c84e56f650cb\",\"toSide\":\"top\",\"label\":\":weekday\"},{\"id\":\"503b5af7f363b512\",\"fromNode\":\"f82f1adc99fcc4db\",\"fromSide\":\"bottom\",\"toNode\":\"d283ed9e9d24b01d\",\"toSide\":\"top\",\"label\":\":tags\"}]}" }, { name: "3. Dynamic note references.canvas", @@ -90,8 +90,8 @@ export const cannoliCollege = { name: "7. Embedding Search (Smart Connections).canvas", content: "{\"nodes\":[{\"id\":\"6343f06af4ec924a\",\"type\":\"group\",\"x\":-775,\"y\":-280,\"width\":1084,\"height\":1140,\"label\":\"Using Smart Connections to search\"},{\"id\":\"b74952d80e6a6d39\",\"type\":\"group\",\"x\":-684,\"y\":-1313,\"width\":901,\"height\":800,\"label\":\"Rendering Smart Connection search\"},{\"id\":\"48769029686ff688\",\"type\":\"text\",\"text\":\"\",\"x\":-664,\"y\":-913,\"width\":385,\"height\":380,\"color\":\"6\"},{\"id\":\"d244ad86313a5ef4\",\"type\":\"text\",\"text\":\"\",\"x\":-201,\"y\":-913,\"width\":398,\"height\":380,\"color\":\"6\"},{\"id\":\"46c4ce310ed88cf1\",\"type\":\"text\",\"text\":\"Here is a smart connections dynamic codeblock:\\n\\n```smart-connections\\nAyy lmao\\n```\\nWhat's the most similar note probably about?\",\"x\":-641,\"y\":-1293,\"width\":339,\"height\":340},{\"id\":\"6ce747453d1d423f\",\"type\":\"text\",\"text\":\"Cannoli can leverage Smart Connections to do embedding search on your vault.\\n\\nSmart Connections is a popular plugin which, among many other features, can embed your whole vault using a variety of embeddings models. This lets you search for notes or blocks that are semantically similar to a given note or query.\\n\\nIf you have Smart Connections enabled on this vault, you can write a dynamic code block (triple backticks with \\\"smart-connections\\\" following the first set of backticks). Then you can show an LLM the resulting similarity ranking table or embed the most similar X notes.\\n\\n\",\"x\":-540,\"y\":-1700,\"width\":615,\"height\":320},{\"id\":\"38595c66057d2f38\",\"type\":\"text\",\"text\":\"Here we've embedded the top 3 results of the similarity search:\\n\\n{{3\\n```smart-connections\\nAyy lmao\\n```\\n#@}}\\n\\nSummarize the first note\",\"x\":-171,\"y\":-1293,\"width\":339,\"height\":340},{\"id\":\"b4ec110ce33df376\",\"type\":\"text\",\"text\":\"To include the contents of the top results, wrap the whole code block in curly braces and newlines.\\n\\nYou can define the number of results to include using an integer after the first set of braces.\\n\\nYou can also include the modifiers as you would with note reference nodes. This time we included the new \\\"@\\\" link modifier to include the markdown link above the note. This will help make cannolis that can browse and search.\",\"x\":240,\"y\":-1266,\"width\":480,\"height\":287},{\"id\":\"6e728caab4a543a8\",\"type\":\"text\",\"text\":\"What was the metaphor about the earth in that poem about walking?\",\"x\":-387,\"y\":-260,\"width\":462,\"height\":80,\"color\":\"6\"},{\"id\":\"b4a0c102d3eeac17\",\"type\":\"text\",\"text\":\"Embedding search enables lots of cool stuff. You can make open queries about your vault or even have the LLM generate queries where needed, then zero in on the results and inject them as context wherever needed.\",\"x\":-572,\"y\":-440,\"width\":607,\"height\":103},{\"id\":\"5db01d3546fde562\",\"type\":\"text\",\"text\":\"Which of the notes below fits the search query best?\\n\\nQuery: \\\"{{query}}\\\"\\n\\n{{4\\n```smart-connections\\n{{query}}\\n```\\n@}}\",\"x\":-726,\"y\":-90,\"width\":339,\"height\":280},{\"id\":\"5230a36585d39219\",\"type\":\"text\",\"text\":\"\",\"x\":-112,\"y\":-137,\"width\":385,\"height\":615,\"color\":\"6\"},{\"id\":\"44636ef212354a8b\",\"type\":\"text\",\"text\":\"Here's a note that resulted from a search:\\n\\n{{@bestFit}}\\n\\nUse that note to answer this query: \\\"{{query}}\\\"\",\"x\":-510,\"y\":280,\"width\":241,\"height\":198},{\"id\":\"79b80456d62de306\",\"type\":\"text\",\"text\":\"\",\"x\":-678,\"y\":520,\"width\":577,\"height\":300,\"color\":\"6\"},{\"id\":\"9f3d2a5adb087657\",\"type\":\"text\",\"text\":\"In this cannoli we perform an embedding search on a query, have the LLM select the most relevant note from the top 4, and use that as context to answer the initial query.\",\"x\":-1180,\"y\":17,\"width\":383,\"height\":154}],\"edges\":[{\"id\":\"9c97bc5b033f7912\",\"fromNode\":\"46c4ce310ed88cf1\",\"fromSide\":\"bottom\",\"toNode\":\"48769029686ff688\",\"toSide\":\"top\",\"color\":\"2\"},{\"id\":\"8488abe3896113e6\",\"fromNode\":\"5db01d3546fde562\",\"fromSide\":\"bottom\",\"toNode\":\"44636ef212354a8b\",\"toSide\":\"top\",\"label\":\"[bestFit\"},{\"id\":\"e0bd7da21aff0c3d\",\"fromNode\":\"44636ef212354a8b\",\"fromSide\":\"right\",\"toNode\":\"5230a36585d39219\",\"toSide\":\"left\",\"color\":\"2\"},{\"id\":\"782881c8520b4911\",\"fromNode\":\"44636ef212354a8b\",\"fromSide\":\"bottom\",\"toNode\":\"79b80456d62de306\",\"toSide\":\"top\"},{\"id\":\"8780fbd775315770\",\"fromNode\":\"38595c66057d2f38\",\"fromSide\":\"bottom\",\"toNode\":\"d244ad86313a5ef4\",\"toSide\":\"top\",\"color\":\"2\"},{\"id\":\"2df29e8ef4d81f5d\",\"fromNode\":\"6e728caab4a543a8\",\"fromSide\":\"bottom\",\"toNode\":\"5db01d3546fde562\",\"toSide\":\"top\",\"label\":\"query\"},{\"id\":\"73a455ab933fce60\",\"fromNode\":\"6e728caab4a543a8\",\"fromSide\":\"bottom\",\"toNode\":\"44636ef212354a8b\",\"toSide\":\"top\",\"label\":\"query\"}]}" }, -], - "5. Groups": [ + ], + "5. Groups": [ { name: "1. Basic groups.canvas", content: "{\"nodes\":[{\"type\":\"group\",\"id\":\"3507f53b8ef19875\",\"x\":-400,\"y\":-280,\"width\":610,\"height\":940,\"label\":\"Basic Groups\"},{\"type\":\"group\",\"id\":\"9ea4603066155724\",\"x\":-100,\"y\":-220,\"width\":290,\"height\":273,\"label\":\"Group 1\"},{\"type\":\"group\",\"id\":\"9d076987e99ce6a6\",\"x\":-100,\"y\":260,\"width\":290,\"height\":240,\"label\":\"Group 2\"},{\"type\":\"text\",\"text\":\"Hey what model are you?\",\"id\":\"554b1577865f24d4\",\"x\":-80,\"y\":280,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"Actually you don't know that.\",\"id\":\"35881fd57b1d4269\",\"x\":-80,\"y\":420,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"\",\"id\":\"fd9b2131408190a4\",\"x\":-80,\"y\":520,\"width\":250,\"height\":120,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"gpt-4\",\"id\":\"e7d78cc1a7a948f8\",\"x\":-320,\"y\":250,\"width\":105,\"height\":60,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Rubiks cube\",\"id\":\"e83b4c5b2409b738\",\"x\":-370,\"y\":-163,\"width\":180,\"height\":57,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Here we use a basic group to provide all of the member nodes with a variable.\",\"id\":\"1354395dbba49c59\",\"x\":-700,\"y\":-163,\"width\":290,\"height\":93},{\"type\":\"text\",\"text\":\"Basic groups allow you to conveniently provide variables or change config settings of multiple nodes.\\n\\nFunctionally, its the same as drawing arrows to every node in the group.\",\"id\":\"cb4e55a56aa20105\",\"x\":-320,\"y\":-500,\"width\":423,\"height\":170},{\"type\":\"text\",\"text\":\"Groups whose label is not an integer are basic groups.\",\"id\":\"ad8c877c4bdbc784\",\"x\":-298,\"y\":-620,\"width\":380,\"height\":94},{\"type\":\"text\",\"text\":\"You can use {{a}} in this node\",\"id\":\"3f724ea642f3f39d\",\"x\":-80,\"y\":-200,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"And you can use {{a}} in this node\",\"id\":\"6b128a864045c0e7\",\"x\":-80,\"y\":-27,\"width\":250,\"height\":60},{\"type\":\"text\",\"text\":\"\",\"id\":\"b6f9f0b26ae8e68e\",\"x\":-80,\"y\":80,\"width\":250,\"height\":100,\"color\":\"6\"},{\"type\":\"text\",\"text\":\"Here we change the model config setting of all the member nodes of a group.\",\"id\":\"e58e6effa966f52b\",\"x\":-728,\"y\":242,\"width\":318,\"height\":98}],\"edges\":[{\"id\":\"3986f98e5c79c58a\",\"fromNode\":\"e83b4c5b2409b738\",\"fromSide\":\"right\",\"toNode\":\"9ea4603066155724\",\"toSide\":\"left\",\"label\":\"a\"},{\"id\":\"b54add99d36d3b53\",\"fromNode\":\"e7d78cc1a7a948f8\",\"fromSide\":\"right\",\"toNode\":\"9d076987e99ce6a6\",\"toSide\":\"left\",\"color\":\"2\",\"label\":\"model\"},{\"id\":\"3ec08b096671959f\",\"fromNode\":\"3f724ea642f3f39d\",\"fromSide\":\"bottom\",\"toNode\":\"6b128a864045c0e7\",\"toSide\":\"top\"},{\"id\":\"71449d8dc3ac9bac\",\"fromNode\":\"6b128a864045c0e7\",\"fromSide\":\"bottom\",\"toNode\":\"b6f9f0b26ae8e68e\",\"toSide\":\"top\",\"color\":\"2\"},{\"id\":\"b2ca430257741d71\",\"fromNode\":\"554b1577865f24d4\",\"fromSide\":\"bottom\",\"toNode\":\"35881fd57b1d4269\",\"toSide\":\"top\"},{\"id\":\"948da6eeffd07b7e\",\"fromNode\":\"35881fd57b1d4269\",\"fromSide\":\"bottom\",\"toNode\":\"fd9b2131408190a4\",\"toSide\":\"top\",\"color\":\"2\"}]}" @@ -108,8 +108,8 @@ export const cannoliCollege = { name: "4. Parallel groups.canvas", content: "{\"nodes\":[{\"id\":\"e0f003ced217a6f7\",\"type\":\"group\",\"x\":-539,\"y\":1345,\"width\":1014,\"height\":1030,\"label\":\"Nested parallel groups\"},{\"id\":\"a582b0f31eb5b273\",\"type\":\"group\",\"x\":-305,\"y\":-524,\"width\":933,\"height\":777,\"label\":\"Static list input\"},{\"id\":\"65c66e6f113726aa\",\"type\":\"group\",\"x\":-333,\"y\":338,\"width\":555,\"height\":926,\"label\":\"Dynamic list input\"},{\"id\":\"0d7740a882d40f92\",\"type\":\"group\",\"x\":-272,\"y\":1532,\"width\":454,\"height\":426,\"color\":\"5\",\"label\":\"2\"},{\"id\":\"6c63c8db5daf8225\",\"type\":\"group\",\"x\":-243,\"y\":561,\"width\":383,\"height\":272,\"color\":\"5\",\"label\":\"2\"},{\"id\":\"83456d09f082fe8a\",\"type\":\"group\",\"x\":-246,\"y\":1741,\"width\":399,\"height\":191,\"color\":\"5\",\"label\":\"2\"},{\"id\":\"bf04efea7cb87629\",\"type\":\"group\",\"x\":-259,\"y\":-301,\"width\":383,\"height\":160,\"color\":\"5\",\"label\":\"2\"},{\"id\":\"c0e7756d10aed69f\",\"type\":\"text\",\"text\":\"\",\"x\":-260,\"y\":-114,\"width\":385,\"height\":346,\"color\":\"6\"},{\"id\":\"9bc6d0bb497eb44c\",\"type\":\"text\",\"text\":\"Parallel groups are Cyan groups with a number in their label\\n\\nThey will copy all of their member nodes and arrows based on the number in their label, and each will run in parallel\",\"x\":-657,\"y\":-510,\"width\":330,\"height\":208},{\"id\":\"3668418bbe12ccd5\",\"type\":\"text\",\"text\":\"List arrows can also come from AI nodes. They will try to parse a list from either markdown list format or from a JSON array.\",\"x\":-696,\"y\":423,\"width\":342,\"height\":129},{\"id\":\"1e0f6e1a8645a214\",\"type\":\"text\",\"text\":\"The \\\"season\\\" arrow in this group is a list arrow (cyan color). When a parallel group has a list arrow pointing to it, each copy of the group will get a different item from the list\\n\\nThe number of the group doesn't always have to match the number of items, it just serves as a limit. Only that many copies will be made.\",\"x\":-751,\"y\":-270,\"width\":427,\"height\":230},{\"id\":\"0dc3d3584861444b\",\"type\":\"text\",\"text\":\"By default, the arrow leaving the group will simply combine all of the content from each version of the node with no formatting.\\n\\nThere are several pre-made formats you can use (read below), but you can also define your own output formats using a formatter node. This could be used to write CSVs, for example.\",\"x\":-840,\"y\":14,\"width\":513,\"height\":208},{\"id\":\"f28b98b566aff8a3\",\"type\":\"text\",\"text\":\"Write a list of 5 obscure colors (markdown format)\",\"x\":-175,\"y\":358,\"width\":250,\"height\":94},{\"id\":\"e92c8263c3cbedd9\",\"type\":\"text\",\"text\":\"In a sentence, describe {{color}}.\",\"x\":-199,\"y\":594,\"width\":294,\"height\":89},{\"id\":\"03f168ccb5370bb2\",\"type\":\"text\",\"text\":\"Give an even more obscure term for it.\",\"x\":-195,\"y\":713,\"width\":288,\"height\":87},{\"id\":\"7b28e3a04655a650\",\"type\":\"text\",\"text\":\"\",\"x\":-300,\"y\":896,\"width\":501,\"height\":346,\"color\":\"6\"},{\"id\":\"ac89af9188e32032\",\"type\":\"text\",\"text\":\"The arrow leaving this group has a \\\"`_`\\\" prefix. This means we will render the results as a markdown table.\\n\\nThe current merge arrow prefixes are:\\n\\n`#`: Headers\\n`-`: List\\n`_`: Table\\n\",\"x\":-733,\"y\":797,\"width\":383,\"height\":255},{\"id\":\"b04d27fecfd47af4\",\"type\":\"text\",\"text\":\"Parallel groups can be nested! Just be wary of how many AI nodes will be executed at the most nested levels.\\n\\nIn this example we merge the result using the \\\"-\\\" prefix, which formats it as a nested list.\\n\\nArbitrary nesting works for the headers (`#`) and list (`-`) prefixes, but the table (`_`) prefix works best with 2 levels parallel groups (2 dimensions of the table)\",\"x\":-967,\"y\":1505,\"width\":405,\"height\":301},{\"id\":\"22d9b1ea6f478316\",\"type\":\"text\",\"text\":\"\",\"x\":-490,\"y\":1990,\"width\":886,\"height\":346,\"color\":\"6\"},{\"id\":\"b3938cc38770d918\",\"type\":\"text\",\"text\":\"List the 3 steps to make a PBJ sandwich (markdown format)\",\"x\":-172,\"y\":1362,\"width\":250,\"height\":94},{\"id\":\"8b3843db8e3a0626\",\"type\":\"text\",\"text\":\"- summer\\n- winter\",\"x\":-191,\"y\":-504,\"width\":250,\"height\":94,\"color\":\"6\"},{\"id\":\"beb438d1c026fe71\",\"type\":\"text\",\"text\":\"Just list the most popular events in the {{season}} olympics\",\"x\":-215,\"y\":-268,\"width\":294,\"height\":89},{\"id\":\"ad1b9b99f7179469\",\"type\":\"text\",\"text\":\"Break this step in making a PBJ sandwich down into 2 sub-steps (markdown list):\\n{{step}}\",\"x\":-228,\"y\":1566,\"width\":372,\"height\":103},{\"id\":\"60a2a625e8300dc9\",\"type\":\"text\",\"text\":\"Break this substep in making a PBJ sandwich into 2 sub-sub-steps (markdown list):\\n{{substep}}\",\"x\":-202,\"y\":1774,\"width\":311,\"height\":123},{\"id\":\"0305c934c9aace83\",\"type\":\"text\",\"text\":\"\",\"x\":153,\"y\":-115,\"width\":433,\"height\":339,\"color\":\"6\"},{\"id\":\"071083739c5edbf9\",\"type\":\"text\",\"text\":\"Logging edges exiting parallel groups will give information about each copy.\",\"x\":213,\"y\":-336,\"width\":341,\"height\":84}],\"edges\":[{\"id\":\"5bd1086a494d6e32\",\"fromNode\":\"8b3843db8e3a0626\",\"fromSide\":\"bottom\",\"toNode\":\"bf04efea7cb87629\",\"toSide\":\"top\",\"color\":\"5\",\"label\":\"season\"},{\"id\":\"ae4c863a53cd9348\",\"fromNode\":\"beb438d1c026fe71\",\"fromSide\":\"bottom\",\"toNode\":\"c0e7756d10aed69f\",\"toSide\":\"top\"},{\"id\":\"425c2f28bb3e0b99\",\"fromNode\":\"f28b98b566aff8a3\",\"fromSide\":\"bottom\",\"toNode\":\"6c63c8db5daf8225\",\"toSide\":\"top\",\"color\":\"5\",\"label\":\"color\"},{\"id\":\"73233b93df26e03e\",\"fromNode\":\"e92c8263c3cbedd9\",\"fromSide\":\"bottom\",\"toNode\":\"03f168ccb5370bb2\",\"toSide\":\"top\"},{\"id\":\"0caf1bba05c8aa27\",\"fromNode\":\"03f168ccb5370bb2\",\"fromSide\":\"bottom\",\"toNode\":\"7b28e3a04655a650\",\"toSide\":\"top\",\"label\":\"_\"},{\"id\":\"607232d635891e37\",\"fromNode\":\"b3938cc38770d918\",\"fromSide\":\"bottom\",\"toNode\":\"0d7740a882d40f92\",\"toSide\":\"top\",\"color\":\"5\",\"label\":\"step\"},{\"id\":\"432d78d795c6f4ec\",\"fromNode\":\"ad1b9b99f7179469\",\"fromSide\":\"bottom\",\"toNode\":\"83456d09f082fe8a\",\"toSide\":\"top\",\"color\":\"5\",\"label\":\"substep\"},{\"id\":\"7565755b265a1759\",\"fromNode\":\"60a2a625e8300dc9\",\"fromSide\":\"bottom\",\"toNode\":\"22d9b1ea6f478316\",\"toSide\":\"top\",\"label\":\"-\"},{\"id\":\"48ca5d2dc4865bce\",\"fromNode\":\"beb438d1c026fe71\",\"fromSide\":\"right\",\"toNode\":\"0305c934c9aace83\",\"toSide\":\"top\",\"color\":\"2\"}]}" }, -], - "6. Built-in Actions": [ + ], + "6. Built-in Actions": [ { name: "1. Dalle.canvas", content: "{\"nodes\":[{\"id\":\"9c131929cf9cd0ae\",\"type\":\"group\",\"x\":-436,\"y\":579,\"width\":858,\"height\":1205,\"label\":\"Template prompt\"},{\"id\":\"52b5f0705400f7d5\",\"type\":\"group\",\"x\":-314,\"y\":-276,\"width\":588,\"height\":789,\"label\":\"Standard use\"},{\"id\":\"1d951e2c23093a2b\",\"type\":\"text\",\"text\":\"The template-able argument of the dalle action is the prompt\",\"x\":-708,\"y\":685,\"width\":255,\"height\":109},{\"id\":\"b9ea77725ca55e8f\",\"type\":\"text\",\"text\":\"make a picture of a frog at a concert\",\"x\":-154,\"y\":-256,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"8a701ef86405bac2\",\"type\":\"text\",\"text\":\"dalle\",\"x\":-152,\"y\":-120,\"width\":250,\"height\":60,\"color\":\"2\"},{\"id\":\"1cb415bf1256f272\",\"type\":\"text\",\"text\":\"\",\"x\":-294,\"y\":-11,\"width\":534,\"height\":504,\"color\":\"6\"},{\"id\":\"bf9bc6d3de30f54b\",\"type\":\"text\",\"text\":\"The built-in dalle action can be referenced by the name \\\"dalle\\\"\\n\\nArguments:\\n- prompt (required)\\n- size (one of the following)\\n\\t- 256x256\\n\\t- 512x512\\n\\t- 1024x1024 (default)\\n\\t- 1024x1792\\n\\t- 1792x1024\\n- model\\n\\t- dall-e-3 (default)\\n\\t- dall-e-2\",\"x\":-648,\"y\":-172,\"width\":311,\"height\":417},{\"id\":\"41ad397423fb17d8\",\"type\":\"text\",\"text\":\"Note that the dalle action currently only returns a link to the images it produces, so if you want to save the image to you're vault you have to copy the image itself, as OpenAI will not maintain these image links permanently.\\n\\n(if you'd benefit from an option to save images, please let us know)\",\"x\":286,\"y\":-8,\"width\":433,\"height\":236},{\"id\":\"c36034b72096726f\",\"type\":\"text\",\"text\":\"disco\",\"x\":-143,\"y\":599,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"0a4a5477edeb0a9b\",\"type\":\"text\",\"text\":\"guinea pig\",\"x\":-416,\"y\":601,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"f66a3d132929fc8d\",\"type\":\"text\",\"text\":\"1024x1792\",\"x\":152,\"y\":633,\"width\":250,\"height\":60,\"color\":\"6\"},{\"id\":\"4c9b9042079da5ed\",\"type\":\"text\",\"text\":\"[dalle]\\nmake a picture of a {{animal}} at a {{event}}\",\"x\":-142,\"y\":731,\"width\":250,\"height\":98,\"color\":\"2\"},{\"id\":\"5b2a6b4b87c80207\",\"type\":\"text\",\"text\":\"\",\"x\":-284,\"y\":878,\"width\":534,\"height\":883,\"color\":\"6\"}],\"edges\":[{\"id\":\"a2a83b896a844f8b\",\"fromNode\":\"b9ea77725ca55e8f\",\"fromSide\":\"bottom\",\"toNode\":\"8a701ef86405bac2\",\"toSide\":\"top\",\"label\":\"prompt\"},{\"id\":\"7ef0ede979ae0cf7\",\"fromNode\":\"4c9b9042079da5ed\",\"fromSide\":\"bottom\",\"toNode\":\"5b2a6b4b87c80207\",\"toSide\":\"top\"},{\"id\":\"e3be85b19da03b44\",\"fromNode\":\"f66a3d132929fc8d\",\"fromSide\":\"bottom\",\"toNode\":\"4c9b9042079da5ed\",\"toSide\":\"right\",\"label\":\"size\"},{\"id\":\"abab8814929330b2\",\"fromNode\":\"0a4a5477edeb0a9b\",\"fromSide\":\"bottom\",\"toNode\":\"4c9b9042079da5ed\",\"toSide\":\"left\",\"label\":\"animal\"},{\"id\":\"6bed803745429637\",\"fromNode\":\"c36034b72096726f\",\"fromSide\":\"bottom\",\"toNode\":\"4c9b9042079da5ed\",\"toSide\":\"top\",\"label\":\"event\"},{\"id\":\"f2bde8c9ede6acdf\",\"fromNode\":\"8a701ef86405bac2\",\"fromSide\":\"bottom\",\"toNode\":\"1cb415bf1256f272\",\"toSide\":\"top\"}]}" @@ -134,8 +134,8 @@ export const cannoliCollege = { name: "6. Modal maker.canvas", content: "{\"nodes\":[{\"id\":\"c44e5acaab6c078d\",\"type\":\"group\",\"x\":-1118,\"y\":580,\"width\":1334,\"height\":1598,\"label\":\"Non-templated\"},{\"id\":\"4dbad7a5011269a5\",\"type\":\"group\",\"x\":-780,\"y\":-602,\"width\":729,\"height\":1119,\"label\":\"Templated \"},{\"id\":\"1c9c52c115176b48\",\"type\":\"text\",\"text\":\"Here's an example of a modal action usage where we send the syntax description in as a system message, and have an AI node write the modal layout.\",\"x\":-1509,\"y\":773,\"width\":368,\"height\":156},{\"id\":\"bfef661a2f487714\",\"type\":\"text\",\"text\":\"The output of a modal action node is the results in json object format\\n\\nIndividual responses can be accessed using arrow labels\",\"x\":-1133,\"y\":270,\"width\":327,\"height\":170},{\"id\":\"219ce8cb0d584413\",\"type\":\"text\",\"text\":\"Inputs are wrapped in double equals signs (which is why they are showing up highlighted)\\n\\nThe full syntax is described in the system prompt below.\",\"x\":-1288,\"y\":-116,\"width\":485,\"height\":145},{\"id\":\"00f40af99c1962cf\",\"type\":\"text\",\"text\":\"The first line of the modal layout is rendered as the title\",\"x\":-1111,\"y\":-340,\"width\":314,\"height\":90},{\"id\":\"b2af08970bb10b59\",\"type\":\"text\",\"text\":\"The built-in Modal action can be referenced by the name \\\"modal\\\"\\n\\nArguments:\\n- layout (required) (templatable)\",\"x\":-1203,\"y\":-593,\"width\":401,\"height\":189},{\"id\":\"da8d006d9c506316\",\"type\":\"text\",\"text\":\"\",\"x\":-746,\"y\":263,\"width\":269,\"height\":158,\"color\":\"6\"},{\"id\":\"63a29488761470bd\",\"type\":\"text\",\"text\":\"[modal]\\nA modal!\\n{{intro}}\\n\\nHere's a text box: ==heya==\\n\\nhere's a toggle: ==yo (toggle)==\\n\\na textarea (for multiline text):\\n==longText (textarea)==\\n\\nHere's a dropdown:\\n==color (dropdown) red, blue, green==\\n\\nA time, default formatting: ==a time (time)==\\n\\nA datetime, weird custom formatting:\\n==moment (datetime) YYYY-[Day]DDD [at] HH:mm:ss.SSS [in the] A==\\n\\nA date, custom formatting: ==someDay (date) MM/DD/YY==\",\"x\":-696,\"y\":-384,\"width\":478,\"height\":517,\"color\":\"2\"},{\"id\":\"83801bea07140e30\",\"type\":\"text\",\"text\":\"Write three sentences begging the user to fill out this modal.\",\"x\":-605,\"y\":-568,\"width\":291,\"height\":101},{\"id\":\"978ff46d7aede680\",\"type\":\"text\",\"text\":\"\",\"x\":-360,\"y\":207,\"width\":289,\"height\":294,\"color\":\"6\"},{\"id\":\"44d26a1390cef81a\",\"type\":\"text\",\"text\":\"# Modal Layout Syntax\\n\\n \\n\\n## Title\\n\\n- First line: modal title (if not an input field)\\n\\n- Default: \\\"Cannoli modal\\\"\\n\\n \\n\\n## Structure\\n\\n- Plain text: Rendered as-is\\n\\n- Input fields: ==Field Name(field_type) options==\\n\\n- Newlines create paragraphs\\n\\n \\n\\n## Input Fields\\n\\n- Field Name: Required, used as placeholder text\\n\\n- (field_type): Optional (default: text)\\n\\n- options: For dropdowns/date formats\\n\\n \\n\\n## Field Types\\n\\n1. text: Single-line input\\n\\n2. textarea: Multi-line input\\n\\n3. toggle: Boolean switch (default: false)\\n\\n4. dropdown: Option selection (first is default)\\n\\n5. date: Date picker (default: today)\\n\\n6. time: Time picker (default: now)\\n\\n7. datetime: Date and time picker (default: now)\\n\\n \\n\\n## Examples\\n\\nText input: ==User Name==\\n\\nTextarea: ==Comments(textarea)==\\n\\nToggle: ==Enable Feature(toggle)==\\n\\nDropdown: ==Select Option(dropdown) Option1, Option2==\\n\\nDate: ==Select Date(date) YYYY-MM-DD==\\n\\n \\n\\n## Important Notes\\n\\n- Field names are not automatically displayed in the modal\\n\\n- Add explicit field labels/descriptions as plain text before each input\\n\\n- Example:\\n\\nEnter your name: ==User Name==\\n\\nSelect a color: ==Color(dropdown) Red, Green, Blue==\\n\\n- Whitespace before == is preserved in layout\\n\\n- Empty text inputs default to \\\"No input\\\"\\n\\n- Date/time formats are customizable (e.g., YYYY-MM-DD, HH:mm)\\n\\n- Dropdown options: Use comma-separated list or JSON array\\n\\n- Markdown formatting is not supported in the modal\",\"x\":-1069,\"y\":623,\"width\":607,\"height\":1491,\"color\":\"6\"},{\"id\":\"6a75473410518f98\",\"type\":\"text\",\"text\":\"Respond with the layout of a modal that asks for a user's pizza order, including when they'd like it delivered.\",\"x\":-183,\"y\":771,\"width\":291,\"height\":148},{\"id\":\"75c101099767d0ab\",\"type\":\"text\",\"text\":\"modal\",\"x\":-92,\"y\":1033,\"width\":109,\"height\":53,\"color\":\"2\"},{\"id\":\"1ff553b1f86e103f\",\"type\":\"text\",\"text\":\"\",\"x\":-234,\"y\":1199,\"width\":393,\"height\":508,\"color\":\"6\"},{\"id\":\"30aa8a195cc87135\",\"type\":\"text\",\"text\":\"The modal action can be used to get user input during a cannoli run\\n\\nVariables can be referenced as normal, and they will be injected before we parse the modal layout\",\"x\":-645,\"y\":-831,\"width\":427,\"height\":168}],\"edges\":[{\"id\":\"62433987bedc07fd\",\"fromNode\":\"63a29488761470bd\",\"fromSide\":\"bottom\",\"toNode\":\"da8d006d9c506316\",\"toSide\":\"top\",\"label\":\"heya\"},{\"id\":\"03c59e6894a798a9\",\"fromNode\":\"63a29488761470bd\",\"fromSide\":\"bottom\",\"toNode\":\"978ff46d7aede680\",\"toSide\":\"top\"},{\"id\":\"8b1ff6c7a0fc0d69\",\"fromNode\":\"83801bea07140e30\",\"fromSide\":\"bottom\",\"toNode\":\"63a29488761470bd\",\"toSide\":\"top\",\"label\":\"intro\"},{\"id\":\"8faee503bb993fc0\",\"fromNode\":\"75c101099767d0ab\",\"fromSide\":\"bottom\",\"toNode\":\"1ff553b1f86e103f\",\"toSide\":\"top\"},{\"id\":\"fe8aa0d0d919f994\",\"fromNode\":\"6a75473410518f98\",\"fromSide\":\"bottom\",\"toNode\":\"75c101099767d0ab\",\"toSide\":\"top\",\"label\":\"layout\"},{\"id\":\"7b43add0e05eb2da\",\"fromNode\":\"44d26a1390cef81a\",\"fromSide\":\"right\",\"toNode\":\"6a75473410518f98\",\"toSide\":\"left\"}]}" }, -], - "7. Additional features": [ + ], + "7. Additional features": [ { name: "1. Vision.canvas", content: "{\"nodes\":[{\"id\":\"a2a3d07c8e04b7fb\",\"type\":\"group\",\"x\":-264,\"y\":321,\"width\":614,\"height\":658,\"label\":\"Disabling vision\"},{\"id\":\"7be62958bc86b912\",\"type\":\"group\",\"x\":-213,\"y\":-452,\"width\":529,\"height\":680,\"label\":\"Embedding images\"},{\"id\":\"b258a15559b32956\",\"type\":\"text\",\"text\":\"What is this?\\n\\n![nice](https://thisitaliankitchen.com/wp-content/uploads/2023/06/ricotta-cannolis.jpg)\",\"x\":-91,\"y\":-432,\"width\":285,\"height\":403},{\"id\":\"885ecd1433cb7016\",\"type\":\"text\",\"text\":\"\",\"x\":-193,\"y\":19,\"width\":489,\"height\":189,\"color\":\"6\"},{\"id\":\"ef5e7ce3002cecdc\",\"type\":\"text\",\"text\":\"If there's a markdown image embed anywhere in the chat history being sent in an AI node, it will be included as an image to be seen by multi-modal LLMs\",\"x\":-608,\"y\":-267,\"width\":371,\"height\":129},{\"id\":\"9b331c6bba11b723\",\"type\":\"text\",\"text\":\"Embedding urls from the web:\\n```\\n![description](link)\\n```\\n\\nEmbedding images from your vault:\\n```\\n![[image.jpg]]\\n```\",\"x\":-583,\"y\":-97,\"width\":344,\"height\":243},{\"id\":\"f853930eb8a28a12\",\"type\":\"text\",\"text\":\"What is this?\\n\\n![nice](https://static.scientificamerican.com/sciam/cache/file/41DF7DA0-EE58-4259-AA815A390FB37C55_source.jpg?w=900)\",\"x\":-26,\"y\":348,\"width\":340,\"height\":290},{\"id\":\"4cc364fe8b26d5fe\",\"type\":\"text\",\"text\":\"false\",\"x\":-233,\"y\":358,\"width\":110,\"height\":60,\"color\":\"6\"},{\"id\":\"689e145cde7ec714\",\"type\":\"text\",\"text\":\"\",\"x\":-183,\"y\":741,\"width\":489,\"height\":189,\"color\":\"6\"},{\"id\":\"f8ff937e38d8cd72\",\"type\":\"text\",\"text\":\"Vision is enabled by default, but you can disable it at the top of the settings tab, and override that setting at the node level using a config arrow labeled \\\"enableVision\\\"\",\"x\":-654,\"y\":418,\"width\":375,\"height\":150},{\"id\":\"b7d929b4a01df064\",\"x\":-640,\"y\":781,\"width\":355,\"height\":96,\"type\":\"text\",\"text\":\"The LLM will still see the link, but we won't pass it the content of the image.\"}],\"edges\":[{\"id\":\"f6a6fc6a3202bc06\",\"fromNode\":\"b258a15559b32956\",\"fromSide\":\"bottom\",\"toNode\":\"885ecd1433cb7016\",\"toSide\":\"top\"},{\"id\":\"b44dfaaff63347b5\",\"fromNode\":\"f853930eb8a28a12\",\"fromSide\":\"bottom\",\"toNode\":\"689e145cde7ec714\",\"toSide\":\"top\"},{\"id\":\"d5deb032b93596f3\",\"fromNode\":\"4cc364fe8b26d5fe\",\"fromSide\":\"bottom\",\"toNode\":\"f853930eb8a28a12\",\"toSide\":\"left\",\"color\":\"2\",\"label\":\"enableVision\"}]}" @@ -144,8 +144,8 @@ export const cannoliCollege = { name: "2. Running subsets of a canvas.canvas", content: "{\"nodes\":[{\"id\":\"cefc78df00b335d0\",\"type\":\"group\",\"x\":-139,\"y\":-15,\"width\":1050,\"height\":446,\"label\":\"Multiple \\\"cannoli\\\" groups\"},{\"id\":\"d8e91535899d2710\",\"type\":\"group\",\"x\":-151,\"y\":-537,\"width\":693,\"height\":446,\"label\":\"\\\"cannoli\\\" groups\"},{\"id\":\"fcb4324889bc2035\",\"type\":\"group\",\"x\":-107,\"y\":-482,\"width\":290,\"height\":362,\"label\":\"cannoli\"},{\"id\":\"b59d6c3fdb77444b\",\"type\":\"group\",\"x\":-95,\"y\":40,\"width\":290,\"height\":362,\"label\":\"cannoli\"},{\"id\":\"0e3927260d444185\",\"type\":\"group\",\"x\":566,\"y\":39,\"width\":290,\"height\":362,\"label\":\"cannoli\"},{\"id\":\"5e872db1381df5e5\",\"type\":\"text\",\"text\":\"hello!\",\"x\":329,\"y\":-463,\"width\":112,\"height\":60},{\"id\":\"4817b55892e44954\",\"type\":\"text\",\"text\":\"\",\"x\":261,\"y\":-290,\"width\":250,\"height\":149,\"color\":\"6\"},{\"id\":\"2c62a49f4d7d07f9\",\"type\":\"text\",\"text\":\"If you'd only like a specific part of a canvas to be run as a cannoli, you can wrap it in a group labeled \\\"cannoli\\\".\\n\\nIf a group with that label is present on a canvas, when we run it, only the nodes within \\\"cannoli\\\" groups will be run.\",\"x\":-607,\"y\":-411,\"width\":433,\"height\":205},{\"id\":\"92889b47100a022a\",\"type\":\"text\",\"text\":\"hello!\",\"x\":-19,\"y\":-462,\"width\":112,\"height\":60},{\"id\":\"7d4766ac0a8356f2\",\"type\":\"text\",\"text\":\"\",\"x\":-87,\"y\":-289,\"width\":250,\"height\":149,\"color\":\"6\"},{\"id\":\"791a20c14bc962c7\",\"type\":\"text\",\"text\":\"hello!\",\"x\":-7,\"y\":60,\"width\":112,\"height\":60},{\"id\":\"76a7b1f7be41b27a\",\"type\":\"text\",\"text\":\"\",\"x\":-75,\"y\":233,\"width\":250,\"height\":149,\"color\":\"6\"},{\"id\":\"b2d4aec8efc72155\",\"type\":\"text\",\"text\":\"If you'd only like a specific part of a canvas to be run as a cannoli, you can wrap it in a group labeled \\\"cannoli\\\".\\n\\nIf a group with that label is present on a canvas, when we run it, only the nodes within \\\"cannoli\\\" groups will be run.\",\"x\":-595,\"y\":111,\"width\":433,\"height\":205},{\"id\":\"e0064917d60d3a1e\",\"type\":\"text\",\"text\":\"\",\"x\":259,\"y\":229,\"width\":250,\"height\":149,\"color\":\"6\"},{\"id\":\"63c6531a4f4afe03\",\"type\":\"text\",\"text\":\"hello!\",\"x\":328,\"y\":62,\"width\":112,\"height\":60},{\"id\":\"e9af95fbd2cc5541\",\"type\":\"text\",\"text\":\"hello!\",\"x\":653,\"y\":62,\"width\":112,\"height\":60},{\"id\":\"5ec3f63efea0a200\",\"type\":\"text\",\"text\":\"\",\"x\":584,\"y\":234,\"width\":250,\"height\":149,\"color\":\"6\"},{\"id\":\"e3b07a284fbf614e\",\"x\":-584,\"y\":-128,\"width\":395,\"height\":164,\"type\":\"text\",\"text\":\"It's important to note that arrows entering \\\"cannoli\\\" groups from outside it will not be run as part of the cannoli, so they will not pass content to nodes inside the group.\"},{\"id\":\"6701e0a23ee498e3\",\"x\":587,\"y\":-454,\"width\":446,\"height\":245,\"type\":\"text\",\"text\":\"If you'd like to ensure that you only run parts of any canvas which you've specified with \\\"cannoli\\\" groups, you can enable the \\\"Only run canvases with a 'cannoli' group\\\" setting, in the \\\"Canvas preferences\\\" section of the settings tab.\\n\\nThis will mean that canvases without any \\\"cannoli\\\" groups will not be run at all.\"}],\"edges\":[{\"id\":\"8494f89ecf0405e8\",\"fromNode\":\"92889b47100a022a\",\"fromSide\":\"bottom\",\"toNode\":\"7d4766ac0a8356f2\",\"toSide\":\"top\"},{\"id\":\"7bae9bee7dc75c20\",\"fromNode\":\"5e872db1381df5e5\",\"fromSide\":\"bottom\",\"toNode\":\"4817b55892e44954\",\"toSide\":\"top\"},{\"id\":\"4a0ba2bef1aa5c20\",\"fromNode\":\"e9af95fbd2cc5541\",\"fromSide\":\"bottom\",\"toNode\":\"5ec3f63efea0a200\",\"toSide\":\"top\"},{\"id\":\"7dd99e00e4b9a462\",\"fromNode\":\"791a20c14bc962c7\",\"fromSide\":\"bottom\",\"toNode\":\"76a7b1f7be41b27a\",\"toSide\":\"top\"},{\"id\":\"bb7daccfc6f53f57\",\"fromNode\":\"63c6531a4f4afe03\",\"fromSide\":\"bottom\",\"toNode\":\"e0064917d60d3a1e\",\"toSide\":\"top\"}]}" }, -], - "Example notes": [ + ], + "Example notes": [ { name: "Apple Trees.md", content: "---\ntags:\n - cannoliArticle\n---\nApple trees, scientifically known as *Malus domestica*, are not just providers of one of the world's most consumed fruits but are also a model of efficiency and adaptability. Originating in Central Asia, these trees have been cultivated for thousands of years and have adapted to a variety of climates, from temperate to sub-tropical. The tree's rootstock can often be grafted to optimize for disease resistance, soil compatibility, or fruit yield, a testament to its remarkable genetic flexibility.\n\nThe life cycle of an apple tree involves a series of well-coordinated biological events. After the cold dormancy of winter, apple trees enter a phase of bud break in the spring, followed by flowering and pollination. Apple trees are usually not self-pollinating and require cross-pollination from another apple tree of a different variety. This ensures genetic diversity, which is crucial for the tree's adaptability and long-term survival. Post-pollination, the tree focuses on fruit development, channeling nutrients and energy into the growing apples.\n\nApple trees also have a profound impact on ecosystems. They offer habitat and food sources for various species, including but not limited to, bees, birds, and small mammals. Their flowers provide nectar for bees, aiding in the process of pollination for not just apple trees but other plants in the ecosystem. Their leaves and bark offer shelter and their fallen fruits are a food source for multiple ground-dwelling organisms. \n\nHowever, it's worth considering the impact of monoculture and industrial farming practices on apple trees. While these methods might increase short-term yield, they can also make the trees more susceptible to diseases and pests by reducing genetic diversity. This opens up a discourse on the need for more sustainable and diversified farming practices. In a world that's increasingly facing the brunt of climate change and biodiversity loss, the apple tree stands as a symbol of both the possibilities and responsibilities that come with cultivation.\n" @@ -162,5 +162,5 @@ export const cannoliCollege = { name: "Walking Poem.md", content: "In the early dawn, when daylight gleams,\nA journey begins, a walk to dream.\nFootsteps upon the earth’s embrace,\nInto the boundless, open space.\n\nBeneath the azure canopy above,\nGuided by whispers of a gentle dove.\nMy soul alights with each tranquil stride,\nBound to the world, I’m opened wide.\n\nThrough groves and meadows, I wander free,\nThe earth, my canvas, my muse, you see.\nAs daisies sway, and the willows weep,\nI find solace in footsteps, slow and deep.\n\nThrough cobbled streets of history,\nI stroll amongst tales of reverie.\nThe past’s echoes guide my way,\nUnveiling lost stories of yesterday.\n\nFootprints on sand, near the ocean's shore,\nSoft whispers of waves, beguiling more.\nThe salty breeze upon my face,\nA dance of nature, in this sacred place.\n\nUp slopes and mountains, I ascend,\nEager to conquer, to transcend.\nWith each steep climb, my spirit soars,\nCloaked by nature's abundant doors.\n\nThrough crowded cities, I navigate,\nObscured by chaos, yet radiate\nA sense of peace, a tranquil grace,\nFostering moments, without a chase.\n\nInto the woods, where silence bloomed,\nUnderneath arboreal canopy, assumed.\nWhispers of leaves, in serenade,\nA sacred symphony, nature's accolade.\n\nWalking through life, step by step,\nEach footfall closer, as dreams intercept.\nTrusting the path, as it twists and turns,\nWith newfound knowledge, my spirit yearns.\n\nFor walking uncovers hidden truth,\nAs thoughts diffuse, my heart finds youth.\nIt is in motion that I discover,\nThe world and myself, forever uncover.\n\nSo embark on journeys, in each new day,\nFor walking provides solace, come what may.\nTake time to marvel, to breathe and roam,\nFor in the rhythm of footsteps, we find our home." }, -], + ], }; diff --git a/packages/cannoli-plugin/src/vault_interface.ts b/packages/cannoli-plugin/src/vault_interface.ts index e36271a..1f76845 100644 --- a/packages/cannoli-plugin/src/vault_interface.ts +++ b/packages/cannoli-plugin/src/vault_interface.ts @@ -15,6 +15,16 @@ export class VaultInterface implements FileManager { this.replaceSmartConnections = this.replaceSmartConnections.bind(this); } + getPathFromName(name: string): string { + let path = name; + + if (path.includes("|")) { + path = path.split("|")[0]; + } + + return path.replace("[[", "").replace("]]", ""); + } + async editNote( reference: Reference, newContent: string, @@ -26,10 +36,10 @@ export class VaultInterface implements FileManager { return; } - // Get the file - const filename = reference.name.replace("[[", "").replace("]]", ""); + const path = this.getPathFromName(reference.name); + const file = this.cannoli.app.metadataCache.getFirstLinkpathDest( - filename, + path, "" ); @@ -114,15 +124,10 @@ export class VaultInterface implements FileManager { return `# ${reference.name}\nMock note content`; } - // If the note is formatted with the path, get rid of the display name and just use the path - if (reference.name.includes("|")) { - reference.name = reference.name.split("|")[0]; - } + const path = this.getPathFromName(reference.name); - // Get the file - const filename = reference.name.replace("[[", "").replace("]]", ""); const file = this.cannoli.app.metadataCache.getFirstLinkpathDest( - filename, + path, "" ); @@ -631,10 +636,9 @@ export class VaultInterface implements FileManager { ): Promise { const resultContents = []; for (const noteLink of noteLinks) { - // Get rid of the double brackets - const cleanedLink = noteLink.replace("[[", "").replace("]]", ""); + const path = this.getPathFromName(noteLink); - const [noteName, subpath] = cleanedLink.split("#"); + const [noteName, subpath] = path.split("#"); const reference: Reference = { name: noteName, @@ -702,10 +706,10 @@ export class VaultInterface implements FileManager { propertyName: string, yamlFormat = false ): Promise { - // Get the file - const filename = noteName.replace("[[", "").replace("]]", ""); + const path = this.getPathFromName(noteName); + const file = this.cannoli.app.metadataCache.getFirstLinkpathDest( - filename, + path, "" ); @@ -753,10 +757,10 @@ export class VaultInterface implements FileManager { noteName: string, yamlFormat = false ): Promise { - // Get the file - const filename = noteName.replace("[[", "").replace("]]", ""); + const path = this.getPathFromName(noteName); + const file = this.cannoli.app.metadataCache.getFirstLinkpathDest( - filename, + path, "" ); @@ -799,10 +803,10 @@ export class VaultInterface implements FileManager { propertyName: string, newValue: string ): Promise { - // Get the file - const filename = noteName.replace("[[", "").replace("]]", ""); + const path = this.getPathFromName(noteName); + const file = this.cannoli.app.metadataCache.getFirstLinkpathDest( - filename, + path, "" ); @@ -856,7 +860,7 @@ export class VaultInterface implements FileManager { verbose = false ): Promise { // If there are double brackets, remove them - noteName = noteName.replace("[[", "").replace("]]", ""); + noteName = this.getPathFromName(noteName); // Attempt to create the note, adding or incrementing a number at the end of the note name if it already exists let i = 1; @@ -879,11 +883,7 @@ export class VaultInterface implements FileManager { // Create the note await this.cannoli.app.vault.create(fullPath, content ?? ""); - if (verbose) { - console.log(`Note "${noteName}" created at path "${fullPath}"`); - } - - return noteName; + return `[[${fullPath}|${noteName}]]`; } async createNoteAtNewPath( @@ -891,24 +891,21 @@ export class VaultInterface implements FileManager { path: string, content?: string, verbose = false - ): Promise { + ): Promise { // Create the path by appending the note name to the path with .md const fullPath = `${path}/${noteName}.md`; // Create the note await this.cannoli.app.vault.create(fullPath, content ?? ""); - if (verbose) { - console.log(`Note "${noteName}" created at path "${fullPath}"`); - } - - return true; + return `[[${fullPath}|${noteName}]]`; } async getNotePath(noteName: string): Promise { - const filename = noteName.replace("[[", "").replace("]]", ""); + const path = this.getPathFromName(noteName); + const file = this.cannoli.app.metadataCache.getFirstLinkpathDest( - filename, + path, "" ); @@ -930,10 +927,6 @@ export class VaultInterface implements FileManager { // Create the folder this.cannoli.app.vault.createFolder(path); - if (verbose) { - console.log(`Folder created at path "${path}"`); - } - return true; } @@ -945,15 +938,12 @@ export class VaultInterface implements FileManager { // Create the path by appending the note name to the paths with .md const newFullPath = `${newPath}/${noteName}.md`; - const filename = noteName.replace("[[", "").replace("]]", ""); + const path = this.getPathFromName(noteName); const note = this.cannoli.app.metadataCache.getFirstLinkpathDest( - filename, + path, "" ); - // Get the old path - const oldFullPath = note?.path; - if (!note) { return false; } @@ -961,12 +951,6 @@ export class VaultInterface implements FileManager { // Move the note await this.cannoli.app.vault.rename(note, newFullPath); - if (verbose) { - console.log( - `Note "${noteName}" moved from path "${oldFullPath}" to path "${newFullPath}"` - ); - } - return true; }