From 3cf1bd9de8ea9e28b06d2a975a7f6d98d37ec305 Mon Sep 17 00:00:00 2001 From: slnsys Date: Fri, 3 May 2024 15:26:54 +0200 Subject: [PATCH] ordering by hierarchy and skipping multiple occs --- .gitignore | 1 + CHANGELOG.md | 23 ++++ main.ts | 302 ++++++++++++++++++++++++++++++++++++++++++-------- manifest.json | 2 +- 4 files changed, 279 insertions(+), 49 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.gitignore b/.gitignore index cc18d45..586fd42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules/ main.js package-lock.json +hierarchtest.js diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b0000d8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog for Obsidian Plugin **Canvas2Document** + +# 22.04.2024: Conversion processing + +**Feature completion** + +Conversion now consists of 2 steps: generating a canvas level intermediate document and a cleared target document to continue editing in content level. + +**Improvements** + +- documentation optimization +- added slim inline documentation in generated process media + +**Errors** + +- Fixed an error when the canvas files being located in the root of the obsidian vault +- Fixed handling of variations of media filenames + +## Initial Releases + +11.04. + +20.03. \ No newline at end of file diff --git a/main.ts b/main.ts index e62d609..e70055a 100644 --- a/main.ts +++ b/main.ts @@ -81,6 +81,7 @@ export default class Canvas2DocumentPlugin extends Plugin { let activeFile = this.app.workspace.getActiveFile(); let mdFolderPath: string = path.dirname(activeFile.path); + // TODO also for links, not just embeddings const pattern = /\!\[\[([^[\]]+)\]\]/g; const matches = canvStruct.match(pattern); @@ -164,57 +165,259 @@ export default class Canvas2DocumentPlugin extends Plugin { return content; } + async findAllXChildren(node, level = 0) { + console.log("level: " + level); + const children = canvasData.edges + .filter(edge => edge.fromNode === node) + .map(edge => edge.toNode); + + children.forEach(child => { + const grandchildren = findChildren(child, level + 1); + children.push(...grandchildren); + }); + + return children; + } + + // TODO diese wird die findDefinedChildren, nicht rekursiver call, sondern 2-3 mal -> 5 Mal like headings + // dann der Rest über findAllXChrildren wird dann der letzten Ebene noch zugeordnet + async findChildren(node) { + + const children1 = canvasData.edges + .filter(edge => edge.fromNode === node) + .map(edge => edge.toNode); + + // console.log("CHILDREN1") + // console.log(children1) + // hier alle Ebenen quasi teilrekursiv durchgehen, wenn keine gefunden, natürlich break + children1.forEach(child1 => { + + const children2 = canvasData.edges + .filter(edge => edge.fromNode === child1) + .map(edge => edge.toNode); + + // console.log("CHILDREN2") + // console.log(children2) + + children2.forEach(child2 => { + + const children3 = canvasData.edges + .filter(edge => edge.fromNode === child2) + .map(edge => edge.toNode); + + // console.log("CHILDREN3") + // console.log(children3) + + children3.forEach(child3 => { + + const children4 = canvasData.edges + .filter(edge => edge.fromNode === child3) + .map(edge => edge.toNode); + + // console.log("CHILDREN4") + // console.log(children4) + + children4.forEach(child4 => { + + const children5 = canvasData.edges + .filter(edge => edge.fromNode === child4) + .map(edge => edge.toNode); + + // console.log("CHILDREN5") + // console.log(children5) + + }); + + }); + + }); + + // children.push(...grandchildren); + }); + + // return children; + } - async readCanvasData(struct): Promise { + async readCanvasData(struct) { // TODO: nochmal nach https://docs.obsidian.md/Plugins/Vault, read all files // input liste eben aus canvas-JSON alle nodes - const fileContents: [string, string, string, string][] = []; + const fileContents: [string, string, string, number, string][] = []; let myparsed_data = JSON.parse(struct); + console.log(myparsed_data) + console.log("reading canvas data") - for (const node of myparsed_data.nodes) { - const id = node.id; - const type = node.type; - let nodefile = ""; - if (type === "file") { - nodefile = node.file; - const { name, ext } = path.parse(nodefile); + const singleNodeIDs = new Set(); + myparsed_data.nodes.forEach(node => { + singleNodeIDs.add(node.id); + }); - // main file type markdown note - if (ext === ".md") { - fileContents.push([id, type, nodefile, "textfile"]); + // Extract unique fromNodes and toNodes + const fromNodes = new Set(); + const toNodes = new Set(); + myparsed_data.edges.forEach(edge => { + fromNodes.add(edge.fromNode); + toNodes.add(edge.toNode); + }); - } else if (ext === ".jpg" || ext == ".jpeg" || ext === ".png" || ext === ".gif") { - fileContents.push([id, type, nodefile, "contentimage"]); - } else if (ext === ".pdf") { - fileContents.push([id, type, nodefile, "contentpdf"]); - } else { - //TODO handle unknown file type"); - } - } else if (type === "link") { - if (node.url.includes("youtube")) { - const url = node.url; - fileContents.push([id, type, url, "contentyoutube"]); + let handledNodes = new Set(); + // TODO make this a setting + const skiphandledNodes = true + + console.log("nodes" ) + console.log(singleNodeIDs) + console.log("fromNodes") + console.log(fromNodes) + console.log("toNodes: ") + console.log(toNodes) + + + const nodesWithoutParents = [...singleNodeIDs].filter(node => !toNodes.has(node)); + console.log("nodesWithoutParents: " + nodesWithoutParents) + + // TODO wenn nodeswithoutparents leer, liste alle node ids gleichwertig + nodesWithoutParents.forEach(node => { + + const nodeentry = myparsed_data.nodes.find(entry => entry.id === node); + + // is skiphandledNodes is true, check if node is already handled + if (! handledNodes.has(node)) { + this.formatNode(nodeentry, 1).then((result) => { + fileContents.push(result); + }); + } + + handledNodes.add(node); + + const children1 = myparsed_data.edges + .filter(edge => edge.fromNode === node) + .map(edge => edge.toNode); + + // console.log("CHILDREN1") + // console.log(children1) + // TODO hier alle Ebenen quasi teilrekursiv durchgehen, wenn keine gefunden, natürlich break + children1.forEach(child1 => { + + const nodeentry = myparsed_data.nodes.find(entry => entry.id === child1); + + if (! handledNodes.has(child1)) { + this.formatNode(nodeentry, 2).then((result) => { + fileContents.push(result); + }); } else { - fileContents.push([id, type, node.url, "contentlink"]); + console.log("already handled: " + child1) } - } else if (type === "text") { - const text = node.text; - fileContents.push([id, type, "node", text]); - } - } + handledNodes.add(child1); + + const children2 = myparsed_data.edges + .filter(edge => edge.fromNode === child1) + .map(edge => edge.toNode); + + console.log("CHILDREN2") + console.log(children2) + + children2.forEach(child2 => { + + const nodeentry = myparsed_data.nodes.find(entry => entry.id === child2); + + if (! handledNodes.has(child2)) { + this.formatNode(nodeentry, 3).then((result) => { + fileContents.push(result); + }); + } + + handledNodes.add(child2); + + const children3 = myparsed_data.edges + .filter(edge => edge.fromNode === child2) + .map(edge => edge.toNode); + + console.log("CHILDREN3") + console.log(children3) + + children3.forEach(child3 => { + + const nodeentry = myparsed_data.nodes.find(entry => entry.id === child3); + + if (! handledNodes.has(child3)) { + this.formatNode(nodeentry, 4).then((result) => { + fileContents.push(result); + }); + } + + handledNodes.add(child3); + + const children4 = myparsed_data.edges + .filter(edge => edge.fromNode === child3) + .map(edge => edge.toNode); + + console.log("CHILDREN4") + console.log(children4) + + children4.forEach(child4 => { + + const nodeentry = myparsed_data.nodes.find(entry => entry.id === child4); + + if (! handledNodes.has(child4)) { + this.formatNode(nodeentry, 5).then((result) => { + fileContents.push(result); + }); + } else { + console.log("already handled: " + child4) + } + handledNodes.add(child4); + + const children5 = myparsed_data.edges + .filter(edge => edge.fromNode === child4) + .map(edge => edge.toNode); + + console.log("CHILDREN5") + console.log(children5) + }); + }); + }); + // children.push(...grandchildren); + }); + // return children; + }); + console.log("fileContents") + console.log(fileContents) return fileContents; } - getNodes(id) { - // get all nodes from the canvas file - // return array of nodes - return; - + async formatNode(node, level): Promise<[string, string, string, number, string]> { + const id = node.id; + const type = node.type; + let nodefile = ""; + + if (type === "file") { + nodefile = node.file; + const { name, ext } = path.parse(nodefile); + + if (ext === ".md") { + return [id, type, nodefile, level, "textfile"]; + } else if (ext === ".jpg" || ext == ".jpeg" || ext === ".png" || ext === ".gif") { + return [id, type, nodefile, level, "contentimage"]; + } else if (ext === ".pdf") { + return [id, type, nodefile, level, "contentpdf"]; + } else { + //TODO handle unknown file type"); + } + } else if (type === "link") { + if (node.url.includes("youtube")) { + const url = node.url; + return [id, type, url, level, "contentyoutube"]; + } else { + return [id, type, node.url, level, "contentlink"]; + } + } else if (type === "text") { + const text = node.text; + return [id, type, "node", level, text]; + } } async writeCanvDocFile(content, convStruct) { @@ -240,12 +443,19 @@ export default class Canvas2DocumentPlugin extends Plugin { for (const element of content) { let cnfname = "" + + // place number of # according to level + let heading = "" + for (let i = 0; i < element[3]; i++) { + heading += "#" + } + if (element[1] == "text") { cnfname = writeworkdir + "/" + "newdoc-node_" + element[0] + "_fromCanvas.md" // heading für navi links - contentString += "\n\n# ___card from Canvas\n" + contentString += "\n\n" + heading + " ___card from Canvas\n" // Filename und interner link anchor contentString += element[2] + " ^" + element[0] + "\n\n" @@ -268,7 +478,7 @@ export default class Canvas2DocumentPlugin extends Plugin { // cnfname = writeworkdir + "/" + "newdoc-node_" + element[0] + " _fromCanvas.md" // heading für navi links - contentString += "\n\n# ___link from Canvas\n" + contentString += "\n\n" + heading + " ___link from Canvas\n" // Filename und interner link anchor contentString += element[2] + " ^" + element[0] + "\n\n" @@ -285,22 +495,18 @@ export default class Canvas2DocumentPlugin extends Plugin { } //Embedding media specific - if (element[3] == "contentyoutube") { + if (element[4] == "contentyoutube") { contentString += "\n ![](" + element[2] + ")\n\n" - } else if (element[3] == "contentlink") { + } else if (element[4] == "contentlink") { contentString += "\n \n\n" } } else if (element[1] == "file") { - if (element[3] == "contentimage" || element[3] == "contentpdf") { + if (element[4] == "contentimage" || element[4] == "contentpdf") { // heading für navi links - contentString += "\n\n# ___Media from Canvas\n" + contentString += "\n\n" + heading + " ___Media from Canvas\n" - // starttag meta data block - // contentString += "\n%%\ncanvas2document plugin metadata header start\n%%\n" - - // Filename und interner link anchor contentString += element[2] + " ^" + element[0] + "\n\n" // linking box @@ -318,16 +524,16 @@ export default class Canvas2DocumentPlugin extends Plugin { // contentString += "\n%%\ncanvas2document plugin metadata header end\n%%\n" //Embedding media specific - if (element[3] == "contentpdf") { + if (element[4] == "contentpdf") { contentString += "\n ![[" + element[2] + "#height=500]]\n\n" - } else if (element[3] == "contentimage") { + } else if (element[4] == "contentimage") { contentString += "\n ![[" + element[2] + "|500]]\n\n" } } else { // heading für navi links - contentString += "\n\n# ___noteFile from Canvas\n" + contentString += "\n\n" + heading + " ___noteFile from Canvas\n" // Filename und interner link anchor contentString += element[2] + " ^" + element[0] + "\n\n" @@ -354,7 +560,7 @@ export default class Canvas2DocumentPlugin extends Plugin { try { let cnfabst = this.app.vault.getAbstractFileByPath(cnfname); this.app.vault.delete(cnfabst, true) - canvasnodeFile = this.app.vault.create(cnfname, element[3]) + canvasnodeFile = this.app.vault.create(cnfname, element[4]) } catch (e) { console.log(e) return diff --git a/manifest.json b/manifest.json index 14f7110..b2d2cd5 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "canvas2document", "name": "Canvas2Document", - "version": "1.1.0", + "version": "1.2.0", "minAppVersion": "1.5.12", "description": "Convert a complete Canvas to a long form document, integrating all cards, notes, images and other media content into a single markdown file.", "author": "slnsys",