MarkMindCkm / obsidian-markmind

A mind map, outline for obsidian,It support mobile and desktop
770 stars 35 forks source link

[dev] MNR.js v042 here, usable, likely no more dev anymore. thx see last and last 2nd msg. sync update merge #688

Open ccchan234 opened 1 year ago

ccchan234 commented 1 year ago

hi, previous say the last block for data flow between basic/rich, old, new is how to update a rich map from a rich map. just used gpt to write a .js script, to show proof of concept first, that you can do so.

points:

  1. the main node is untouched as told by gpt, yet i found the source.md will cover up the targer.md
  2. for any node, all 3 of id, pid and text need to be same to be "identical"
  3. 50% by gpt, 50% by me, no warranty, suppose use as common creatives.

pls advise and see if author would make it into a ctrlP function. thanks

ccchan234 commented 1 year ago

rich1 image rich2 image rich1 as source, rich2 as target -> richr image

ccchan234 commented 1 year ago

rich1's json tree, for reference: Enter the name of the file to read: rich1.md YAML Front Matter:

mindmap-plugin: rich

Parsed JSON Block:

{ "theme": "", "mindData": [ [ { "id": "f2cbb51d-be9e-5909", "text": "111", "isExpand": true, "isRoot": true, "useScale": true, "useHandMode": false, "x": 3940, "y": 3800, "main": true, "layout": { "layoutName": "mindmap2", "direct": "mindmap" }, "stroke": "" }, { "id": "41457844-5c49-418d", "text": "123", "stroke": "#6ed3ea", "x": 4058, "y": 3779, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "86601dce-6d29-312c", "text": "345", "stroke": "#acdb48", "x": 4058, "y": 3840, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "8f1d1c02-0b0c-7add", "text": "567", "stroke": "#f0c2f9", "x": 3847, "y": 3810, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" } ] ], "induceData": [], "wireFrameData": [], "relateLinkData": [], "calloutData": [] }

JSON Block Details:

ccchan234 commented 1 year ago

rich2: Enter the name of the file to read: rich2.md YAML Front Matter:

mindmap-plugin: rich

Parsed JSON Block:

{ "theme": "", "mindData": [ [ { "id": "f2cbb51d-be9e-5909", "text": "bbb", "isExpand": true, "isRoot": true, "useScale": true, "useHandMode": false, "x": 3940, "y": 3800, "main": true, "layout": { "layoutName": "mindmap2", "direct": "mindmap" }, "stroke": "" }, { "id": "7d9a6445-96e6-ab1f", "text": "abc", "stroke": "#6ed3ea", "x": 4065, "y": 3779, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "f6f65024-6e6b-08b8", "text": "def", "stroke": "#acdb48", "x": 4065, "y": 3840, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "0e395d2b-ae71-6342", "text": "ght", "stroke": "#f0c2f9", "x": 3854, "y": 3810, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" } ] ], "induceData": [], "wireFrameData": [], "relateLinkData": [], "calloutData": [] }

JSON Block Details:

ccchan234 commented 1 year ago

Enter the name of the file to read: richr.md YAML Front Matter:

mindmap-plugin: rich

Parsed JSON Block:

{ "theme": "", "mindData": [ [ { "id": "f2cbb51d-be9e-5909", "text": "bbb", "isExpand": true, "isRoot": true, "useScale": true, "useHandMode": false, "x": 3940, "y": 3800, "main": true, "layout": { "layoutName": "mindmap2", "direct": "mindmap" }, "stroke": "" }, { "id": "7d9a6445-96e6-ab1f", "text": "abc", "stroke": "#6ed3ea", "x": 4065, "y": 3779, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "f6f65024-6e6b-08b8", "text": "def", "stroke": "#acdb48", "x": 4065, "y": 3840, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "0e395d2b-ae71-6342", "text": "ght", "stroke": "#f0c2f9", "x": 3854, "y": 3810, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "f2cbb51d-be9e-5909", "text": "111", "isExpand": true, "isRoot": true, "useScale": true, "useHandMode": false, "x": 3940, "y": 3800, "main": true, "layout": { "layoutName": "mindmap2", "direct": "mindmap" }, "stroke": "" }, { "id": "41457844-5c49-418d", "text": "123", "stroke": "#6ed3ea", "x": 4058, "y": 3779, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "86601dce-6d29-312c", "text": "345", "stroke": "#acdb48", "x": 4058, "y": 3840, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" }, { "id": "8f1d1c02-0b0c-7add", "text": "567", "stroke": "#f0c2f9", "x": 3847, "y": 3810, "layout": null, "isExpand": true, "pid": "f2cbb51d-be9e-5909" } ] ], "induceData": [], "wireFrameData": [], "relateLinkData": [], "calloutData": [] }

JSON Block Details:

ccchan234 commented 1 year ago

037-merge.txt

ccchan234 commented 1 year ago

me: i wanna introduce this script to my friend, can you simply explain how it work? gpt: Certainly! This script is a Node.js program designed to merge two mind map files in Markdown format. The mind map data is stored in a JSON block within the Markdown files. The script reads the JSON blocks from the source and target files, combines the mind maps (by adding nodes from the source to the target), and saves the updated JSON block back to the target file.

Here's a step-by-step explanation of how the script works:

  1. Import the required modules: fs (for file system operations), path (to handle file paths), and readline-sync (to read user input).

  2. Define the readJSONBlock function, which reads a file and extracts the JSON block from it. This function takes a file path as input and a callback function that will be called with the parsed JSON object.

  3. Define the updateMindmap function, which takes two JSON objects (target and source) as input. This function iterates through the nodes in the source mind map and adds them to the target mind map, if they don't already exist.

  4. Use readline-sync to prompt the user for the source and target file names.

  5. Form the full file paths using the path module.

  6. Call the readJSONBlock function for the source file to get the source JSON object.

  7. Call the readJSONBlock function for the target file to get the target JSON object.

  8. Use the updateMindmap function to merge the source and target JSON objects, creating an updated target JSON object.

  9. Read the target file content using the fs module.

  10. Replace the original JSON block in the target file content with the updated JSON block.

  11. Write the updated target file content back to the target file using the fs module.

  12. If everything is successful, print a message to the console indicating that the target file has been updated.

ccchan234 commented 1 year ago

me: can you explain more about how the maps are compared, and what criterias are used to determine identical/different node? gpt: Sure! The script compares the nodes in the source and target mind maps based on three properties: id, pid, and text. The comparison is performed in the updateMindmap function. Here's a more detailed explanation of the comparison process:

  1. Iterate through each node in the source mind map using a for loop.

  2. For each source node, the script checks if there is any node in the target mind map that matches all three properties (id, pid, and text). This check is performed using the Array.prototype.some() method, which tests whether at least one element in the array passes the provided test function.

  3. The test function inside some() compares the id, pid, and text properties of the target node with the corresponding properties of the source node. If all three properties match, the test function returns true, indicating that an identical node was found in the target mind map.

    target.mindData[0].some(tNode => tNode.id === node.id && tNode.pid === node.pid && tNode.text === node.text)
  4. If an identical node is found in the target mind map (some() returns true), the script skips the current source node and continues with the next one.

  5. If no identical node is found in the target mind map (some() returns false), the script adds the current source node to the target mind map.

    target.mindData[0].push(node);
  6. After iterating through all the nodes in the source mind map, the updated target JSON object is returned.

In summary, two nodes are considered identical if their id, pid, and text properties match. If a source node is not found in the target mind map (based on these criteria), it is added to the target mind map.

ccchan234 commented 1 year ago

what i think the future use is, made into a ctrlP function.

then the user 's current active document is targer.md, use ctrlP then will ask use which file as source.md and update the target.md

pls advise thx

ccchan234 commented 1 year ago

037-merge.txt

just download, rename as .js, then run with node.js

ccchan234 commented 1 year ago

gpt made it into a typescript, but i cant build it. there is something wrong with my npm/ts compiler.

import { Plugin, TFile } from 'obsidian'; import { readFile, writeFile } from 'fs/promises';

export default class MindmapMergerPlugin extends Plugin { async onload() { this.addCommand({ id: 'merge-mindmaps', name: 'Merge Mindmaps', checkCallback: (checking: boolean) => { if (!checking) { this.mergeMindmaps(); } return true; }, }); }

async mergeMindmaps() { const targetFile = this.app.workspace.getActiveFile(); if (!targetFile) { new Notice('No active file found. Please open the target file.'); return; }

const sourceFile = await this.app.fileManager.getFileChooser().choose();
if (!sourceFile) {
  new Notice('No source file selected. Aborting merge process.');
  return;
}

new Notice(`Merging ${sourceFile.basename} into ${targetFile.basename}.`);

const targetJson = await this.extractJsonFromMarkdownFile(targetFile);
const sourceJson = await this.extractJsonFromMarkdownFile(sourceFile);

const updatedTargetJson = this.updateMindmap(targetJson, sourceJson);

const targetContent = await this.app.vault.read(targetFile);
const updatedTargetContent = this.updateTargetFileContent(targetContent, updatedTargetJson);

await this.app.vault.modify(targetFile, updatedTargetContent);
new Notice('Merge process finished. The target file has been updated.');

}

async extractJsonFromMarkdownFile(file: TFile) { const fileContent = await this.app.vault.read(file); const jsonStart = fileContent.indexOf('{'); const jsonEnd = fileContent.lastIndexOf('}') + 1; const jsonBlock = JSON.parse(fileContent.substring(jsonStart, jsonEnd)); return jsonBlock; }

updateMindmap(target: any, source: any) { for (const node of source.mindData[0]) { if (!target.mindData[0].some(tNode => tNode.id === node.id && tNode.pid === node.pid && tNode.text === node.text)) { target.mindData[0].push(node); } } return target; }

updateTargetFileContent(fileContent: string, updatedJson: any) { const jsonStart = fileContent.indexOf('{'); const jsonEnd = fileContent.lastIndexOf('}') + 1; const updatedJsonBlock = JSON.stringify(updatedJson, null, 2); const updatedFileContent = fileContent.substring(0, jsonStart) + updatedJsonBlock + fileContent.substring(jsonEnd); return updatedFileContent; } }

ccchan234 commented 1 year ago

the compiling is always hard to me. i give up.

runing that as .js is ok.

ccchan234 commented 1 year ago

v042, MNR.js, just dnld as .js, rune /w node of node.js

  1. this now support UNC path, so you can run anywhere and merge .md files anywhere. so you could now manually merge 2 files you want. i obtain the unc path by "shift+Rt click then copy as path".
  2. it's now useable, and its hard for me to deal /w a. change to typescript b. compile. so be it. thx.

`const fs = require('fs'); const path = require('path'); const readlineSync = require('readline-sync');

const readJSONBlock = (filePath, callback) => { fs.readFile(filePath, 'utf-8', (err, data) => { if (err) { callback(err); return; }

const jsonStart = data.indexOf('{');
const jsonEnd = data.lastIndexOf('}') + 1;
const jsonBlock = JSON.parse(data.substring(jsonStart, jsonEnd));
callback(null, jsonBlock);

}); };

const updateMindmap = (target, source) => { for (const node of source.mindData[0]) { if (!target.mindData[0].some(tNode => tNode.id === node.id && tNode.pid === node.pid && tNode.text === node.text)) { target.mindData[0].push(node); } } return target; };

const sourceFilePath = readlineSync.question('Enter the source file UNC path: ').trim().replace(/^"|"$/g, ''); const targetFilePath = readlineSync.question('Enter the target file UNC path: ').trim().replace(/^"|"$/g, '');

readJSONBlock(sourceFilePath, (err, sourceMindmap) => { if (err) { console.error(err); return; }

readJSONBlock(targetFilePath, (err, targetMindmap) => { if (err) { console.error(err); return; }

const updatedMindmap = updateMindmap(targetMindmap, sourceMindmap);

fs.readFile(targetFilePath, 'utf-8', (err, targetFileContent) => {
  if (err) {
    console.error(err);
    return;
  }

  const jsonStart = targetFileContent.indexOf('{');
  const jsonEnd = targetFileContent.lastIndexOf('}') + 1;
  const updatedJSONBlock = JSON.stringify(updatedMindmap, null, 2);
  const updatedTargetFileContent = targetFileContent.substring(0, jsonStart) + updatedJSONBlock + targetFileContent.substring(jsonEnd);

  fs.writeFile(targetFilePath, updatedTargetFileContent, 'utf-8', (err) => {
    if (err) {
      console.error(err);
    } else {
      console.log('Updated mindmap JSON block saved to target.md');
    }
  });
});

}); });`

ccchan234 commented 1 year ago

v041, a reader to read the json tree, handy and also support unc path.

`const readline = require('readline'); const fs = require('fs'); const yaml = require('js-yaml');

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

rl.question('Enter the UNC path of the file to read: ', (inputPath) => { const filePath = inputPath.trim().replace(/^"|"$/g, '');

fs.readFile(filePath, 'utf-8', (err, data) => { if (err) { console.error(err); rl.close(); return; }

// Extract the YAML front matter
const frontMatterEnd = data.indexOf('---', 4);
const frontMatter = data.substring(4, frontMatterEnd);

console.log('YAML Front Matter:\n');
console.log(frontMatter);

// Extract and parse the JSON block
const jsonStart = data.indexOf('{');
const jsonEnd = data.lastIndexOf('}') + 1;
const jsonBlock = JSON.parse(data.substring(jsonStart, jsonEnd));

console.log('\nParsed JSON Block:\n');
console.log(JSON.stringify(jsonBlock, null, 2));

// Display details about the JSON block
console.log('\nJSON Block Details:');
console.log(`- Number of keys: ${Object.keys(jsonBlock).length}`);

for (const key in jsonBlock) {
  console.log(`- Key "${key}" has type ${typeof jsonBlock[key]} and value: ${JSON.stringify(jsonBlock[key])}`);
}

rl.close();

}); });`