QAMichaelPeng / obsidian-graphviz

Graphviz plugin for obsidian md.
MIT License
73 stars 10 forks source link

Does not render graphs sometimes #17

Open dgudim opened 1 year ago

dgudim commented 1 year ago

I have about 15 graphs in my note and some of them don't render Screenshot_20230310_195314 When I open and close the code block they render again normally for some time, then break again. I am using obsidian-columns plugin, maybe that will help

dgudim commented 1 year ago

I have checked, the same thing happens without any additional plugins

Also, my /tmp folder looks like this after a couple of minutes image I suggest to delete the file after graph construction and maybe store a cache of rendered graphs (hashing the input maybe)

dgudim commented 1 year ago

Upd: I was able to fix this by modifying the plugin to write image to the temp directory instead of attaching it as a blob.

Here is what I changed:


import * as crypto from 'crypto';
const md5 = (contents: string) => crypto.createHash('md5').update(contents).digest('hex');

private async writeDotFile(sourceFile: string): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      const cmdPath = this.plugin.settings.dotPath;
      const imageFormat = this.plugin.settings.imageFormat;
      const out = `${sourceFile}.${imageFormat}`;

      if (fs.existsSync(out)) {
        return resolve(out);
      }

      const parameters = [`-T${imageFormat}`, sourceFile, '-o', out];

      console.debug(`Starting dot process ${cmdPath}, ${parameters}`);
      const dotProcess = spawn(cmdPath, parameters);
      let errData = '';

      dotProcess.stderr.on('data', (data) => {
        errData += data;
      });
      dotProcess.stdin.end();

      dotProcess.on('exit', (code) => {
        if (code !== 0) {
          return reject(`"${cmdPath} ${parameters}" failed, error code: ${code}, stderr: ${errData}`);
        }
        return resolve(out);
      });

      dotProcess.on('error', (err: Error) => reject(`"${cmdPath} ${parameters}" failed, ${err}`));
    });
  }

private async convertToImage(source: string): Promise<string> {
    const self = this;

    const dir = path.join(os.tmpdir(), 'obsidian-dot');
    const file = path.join(dir, md5(source));

    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir);
    }

    if (!fs.existsSync(file)) {
      fs.writeFileSync(file, source);
    }
    return self.writeDotFile(file);
  }

public async imageProcessor(source: string, el: HTMLElement, _: MarkdownPostProcessorContext): Promise<void> {
    try {
      console.debug('Call image processor');
      //make sure url is defined. once the setting gets reset to default, an empty string will be returned by settings
      const imagePath = await this.convertToImage(source);
      const img = document.createElement('img');
      img.src = `app://local${imagePath}`;
      el.appendChild(img);
    } catch (errMessage) {
      console.error('convert to image error', errMessage);
      const pre = document.createElement('pre');
      const code = document.createElement('code');
      pre.appendChild(code);
      code.setText(errMessage);
      el.appendChild(pre);
    }
  }

Kind of a dirty fix, but it works much more reliable now