go-gitea / gitea

Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD
https://gitea.com
MIT License
44.4k stars 5.43k forks source link

Add support for more diagram types (using Kroki) #20803

Open cweagans opened 2 years ago

cweagans commented 2 years ago

Feature Description

It would be cool if Gitea allowed users to opt in to using Kroki for rendering diagrams. It is possible to self-host Kroki in case users don't want to send their diagram data to a third party service for rendering. Kroki supports almost two dozen kinds of diagrams (including Mermaid, which is already supported -- maybe it would be worth replacing the Mermaid support with Kroki?).

I think this will need some thinking around what configuration options are offered. Off the top of my head, I think it would need the following, but not sure:

In my Gitea instance, I have this in a kroki.js file that I load from my custom footer template:

$(document).ready(function() {

  var languages = [
    "blockdiag",
    "bpmn",
    "bytefield",
    "seqdiag",
    "actdiag",
    "nwdiag",
    "packetdiag",
    "rackdiag",
    "c4plantuml",
    "ditaa",
    "erd",
    "excalidraw",
    "graphviz",
    "mermaid",
    "nomnoml",
    "pikchr",
    "plantuml",
    "structurizr",
    "svgbob",
    "vega",
    "vegalite",
    "wavedrom",
  ];

  function textEncode(str) {
    if (window.TextEncoder) {
    return new TextEncoder('utf-8').encode(str);
    }
    var utf8 = unescape(encodeURIComponent(str));
    var result = new Uint8Array(utf8.length);
    for (var i = 0; i < utf8.length; i++) {
    result[i] = utf8.charCodeAt(i);
    }
    return result;
  }

  function loadDiagrams() {
    languages.forEach(async function(currentValue, index, arr) {

    var codeBlocks = document.getElementsByClassName('language-' + currentValue);
    for (var block_i = 0; block_i < codeBlocks.length; block_i++) {
      var ta = document.createElement('textarea');
      ta.innerHTML = codeBlocks[block_i].innerHTML;
      var data = textEncode(ta.value);
      ta.remove();

      var compressed = pako.deflate(data, { level: 9, to: 'string' });
      var payload = btoa(compressed).replace(/\+/g, '-').replace(/\//g, '_');

      var newDiagram = document.createElement('div');
      newDiagram.setAttribute('class', 'kroki kroki-' + currentValue);
      newDiagram.innerHTML = '<img src="https://kroki.io/' + currentValue + '/svg/' + payload + '" />';
      codeBlocks[block_i].parentNode.insertAdjacentElement("afterend", newDiagram);
      codeBlocks[block_i].parentNode.remove();
    }
    });

    var styleElement = document.createElement('style');
    styleElement.innerHTML = `
    .kroki {
    background-color: white;
    padding: 10px;
    margin: 10px;
    text-align: center;
    }
    `
    document.body.insertAdjacentElement("beforeend", styleElement);
  }

  var lang = document.getElementsByClassName('code-block');
  if (lang.length != 0) {
    $.getScript('https://unpkg.com/pako@1.0.10/dist/pako_deflate.min.js').done(loadDiagrams);
  }
});

Screenshots

This is a test page that I created in a wiki that shows all the different diagram types (note that the Mermaid diagram looks different because it's using the default Gitea rendering instead of Kroki):

Screenshot ![kroki demo](https://user-images.githubusercontent.com/101590/184734904-7f39b890-fb1e-4e18-8c8a-ac2c11e7a751.png)

Markdown source for demo page ```` ```blockdiag blockdiag { Kroki -> generates -> "Block diagrams"; Kroki -> is -> "very easy!"; Kroki [color = "greenyellow"]; "Block diagrams" [color = "pink"]; "very easy!" [color = "orange"]; } ``` ```bpmn OrderReceivedEvent _6-652 _6-674 CalmCustomerTask _6-463 _6-514 _6-565 _6-616 _6-630 _6-630 _6-691 _6-693 _6-691 _6-746 _6-748 _6-748 _6-746 _6-693 _6-632 _6-632 _6-634 _6-634 _6-636 _6-636 _6-125 _6-125 _6-178 _6-178 _6-420 _6-420 _6-430 _6-422 _6-424 _6-422 _6-428 _6-424 _6-426 _6-426 _6-430 _6-428 _6-434 _6-434 _6-436 _6-436 ``` ```bytefield (defattrs :bg-green {:fill "#a0ffa0"}) (defattrs :bg-yellow {:fill "#ffffa0"}) (defattrs :bg-pink {:fill "#ffb0a0"}) (defattrs :bg-cyan {:fill "#a0fafa"}) (defattrs :bg-purple {:fill "#e4b5f7"}) (defn draw-group-label-header [span label] (draw-box (text label [:math {:font-size 12}]) {:span span :borders #{} :height 14})) (defn draw-remotedb-header [kind args] (draw-column-headers) (draw-group-label-header 5 "start") (draw-group-label-header 5 "TxID") (draw-group-label-header 3 "type") (draw-group-label-header 2 "args") (draw-group-label-header 1 "tags") (next-row 18) (draw-box 0x11 :bg-green) (draw-box 0x872349ae [{:span 4} :bg-green]) (draw-box 0x11 :bg-yellow) (draw-box (text "TxID" :math) [{:span 4} :bg-yellow]) (draw-box 0x10 :bg-pink) (draw-box (hex-text kind 4 :bold) [{:span 2} :bg-pink]) (draw-box 0x0f :bg-cyan) (draw-box (hex-text args 2 :bold) :bg-cyan) (draw-box 0x14 :bg-purple) (draw-box (text "0000000c" :hex [[:plain {:font-weight "light" :font-size 16}] " (12)"]) [{:span 4} :bg-purple]) (draw-box (hex-text 6 2 :bold) [:box-first :bg-purple]) (doseq [val [6 6 3 6 6 6 6 3]] (draw-box (hex-text val 2 :bold) [:box-related :bg-purple])) (doseq [val [0 0]] (draw-box val [:box-related :bg-purple])) (draw-box 0 [:box-last :bg-purple])) (draw-remotedb-header 0x4702 9) (draw-box 0x11) (draw-box 0x2104 {:span 4}) (draw-box 0x11) (draw-box 0 {:span 4}) (draw-box 0x11) (draw-box (text "length" [:math] [:sub 1]) {:span 4}) (draw-box 0x14) (draw-box (text "length" [:math] [:sub 1]) {:span 4}) (draw-gap "Cue and loop point bytes") (draw-box nil :box-below) (draw-box 0x11) (draw-box 0x36 {:span 4}) (draw-box 0x11) (draw-box (text "num" [:math] [:sub "hot"]) {:span 4}) (draw-box 0x11) (draw-box (text "num" [:math] [:sub "cue"]) {:span 4}) (draw-box 0x11) (draw-box (text "length" [:math] [:sub 2]) {:span 4}) (draw-box 0x14) (draw-box (text "length" [:math] [:sub 2]) {:span 4}) (draw-gap "Unknown bytes" {:min-label-columns 6}) (draw-bottom) ``` ```seqdiag seqdiag { browser -> webserver [label = "GET /seqdiag/svg/base64"]; webserver -> processor [label = "Convert text to image"]; webserver <-- processor; browser <-- webserver; } ``` ```actdiag actdiag { write -> convert -> image lane user { label = "User" write [label = "Writing text"]; image [label = "Get diagram image"]; } lane Kroki { convert [label = "Convert text to image"]; } } ``` ```nwdiag nwdiag { network dmz { address = "210.x.x.x/24" web01 [address = "210.x.x.1"]; web02 [address = "210.x.x.2"]; } network internal { address = "172.x.x.x/24"; web01 [address = "172.x.x.1"]; web02 [address = "172.x.x.2"]; db01; db02; } } ``` ```packetdiag packetdiag { colwidth = 32; node_height = 72; 0-15: Source Port; 16-31: Destination Port; 32-63: Sequence Number; 64-95: Acknowledgment Number; 96-99: Data Offset; 100-105: Reserved; 106: URG [rotate = 270]; 107: ACK [rotate = 270]; 108: PSH [rotate = 270]; 109: RST [rotate = 270]; 110: SYN [rotate = 270]; 111: FIN [rotate = 270]; 112-127: Window; 128-143: Checksum; 144-159: Urgent Pointer; 160-191: (Options and Padding); 192-223: data [colheight = 3]; } ``` ```rackdiag rackdiag { 16U; 1: UPS [2U]; 3: DB Server; 4: Web Server; 5: Web Server; 6: Web Server; 7: Load Balancer; 8: L3 Switch; } ``` ```c4plantuml @startuml !include C4_Context.puml title System Context diagram for Internet Banking System Person(customer, "Banking Customer", "A customer of the bank, with personal bank accounts.") System(banking_system, "Internet Banking System", "Allows customers to check their accounts.") System_Ext(mail_system, "E-mail system", "The internal Microsoft Exchange e-mail system.") System_Ext(mainframe, "Mainframe Banking System", "Stores all of the core banking information.") Rel(customer, banking_system, "Uses") Rel_Back(customer, mail_system, "Sends e-mails to") Rel_Neighbor(banking_system, mail_system, "Sends e-mails", "SMTP") Rel(banking_system, mainframe, "Uses") @enduml ``` ```ditaa +--------+ | | | User | | | +--------+ ^ request | v +-------------+ | | | Kroki | | |---+ +-------------+ | ^ ^ | inflate | | | v +---------+ +-------------+ | | | Ditaa | | |----+ +-------------+ | ^ | process | | +-------+ ``` ```erd [Person] *name height weight +birth_location_id [Location] *id city state country Person *--1 Location ``` ```excalidraw { "type": "excalidraw", "version": 2, "source": "https://excalidraw.com", "elements": [ { "type": "rectangle", "version": 175, "versionNonce": 279344008, "isDeleted": false, "id": "2ZYh24ed28FJ0yE-S3YNY", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 580, "y": 140, "strokeColor": "#000000", "backgroundColor": "#15aabf", "width": 80, "height": 19.999999999999996, "seed": 521916552, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "Be1y2yzhV3Zd4nwCro__8" ] }, { "type": "rectangle", "version": 180, "versionNonce": 164784376, "isDeleted": false, "id": "bO0OVt6m7LowYpq22ePCA", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 660, "y": 140, "strokeColor": "#000000", "backgroundColor": "#4c6ef5", "width": 120, "height": 19.999999999999996, "seed": 1303206904, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "KaCO9-QjUenSyCuuanoTo" ] }, { "type": "rectangle", "version": 183, "versionNonce": 27181704, "isDeleted": false, "id": "jz0Huq9-s6pNxDw0RqHcR", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 780, "y": 140, "strokeColor": "#000000", "backgroundColor": "#fab005", "width": 180, "height": 19.999999999999996, "seed": 861962120, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "74ifmqmu0vN0NK0_0FwPm" ] }, { "type": "rectangle", "version": 192, "versionNonce": 2123008504, "isDeleted": false, "id": "UnmNTmwJtm6moubcGtSgB", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 960, "y": 140, "strokeColor": "#000000", "backgroundColor": "#fa5252", "width": 80, "height": 19.999999999999996, "seed": 277814520, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "1v60NED2criGG-wo9-oQL" ] }, { "type": "rectangle", "version": 202, "versionNonce": 1823814024, "isDeleted": false, "id": "of76J4WOJHnHi0L61Vst_", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1040, "y": 140, "strokeColor": "#000000", "backgroundColor": "#be4bdb", "width": 180, "height": 19.999999999999996, "seed": 1496796808, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "jjuPzyRneMv3f65lps_6a" ] }, { "type": "rectangle", "version": 193, "versionNonce": 1234602744, "isDeleted": false, "id": "SlvbjeV-9lXbcrlKib-hj", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1220, "y": 140, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 60, "height": 19.999999999999996, "seed": 1938865656, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "5QQzhw_uqk_rBaW2wMriT" ] }, { "type": "text", "version": 81, "versionNonce": 1188901129, "isDeleted": false, "id": "vrdt3JfbD2Xwz4K4TWScI", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 840, "y": -60, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 190, "height": 45, "seed": 1499217288, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [], "fontSize": 36, "fontFamily": 1, "text": "JavaScript", "baseline": 32, "textAlign": "left", "verticalAlign": "top" }, { "type": "arrow", "version": 343, "versionNonce": 1369065096, "isDeleted": false, "id": "Be1y2yzhV3Zd4nwCro__8", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 597.5075333823274, "y": 299, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 40, "height": 139, "seed": 666255096, "groupIds": [], "strokeSharpness": "round", "boundElementIds": [], "startBinding": { "focus": -0.41953339688473495, "gap": 1, "elementId": "UxgtvUBaIPnDWJZ9kUQH8" }, "endBinding": { "focus": -0.11111111111111113, "gap": 1, "elementId": "2ZYh24ed28FJ0yE-S3YNY" }, "points": [ [ 0, 0 ], [ -17.507533382327438, -59 ], [ 22.492466617672562, -139 ] ], "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow" }, { "type": "text", "version": 81, "versionNonce": 690339976, "isDeleted": false, "id": "UxgtvUBaIPnDWJZ9kUQH8", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 580, "y": 300, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 94, "height": 45, "seed": 84626568, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "Be1y2yzhV3Zd4nwCro__8" ], "fontSize": 36, "fontFamily": 1, "text": "Fetch", "baseline": 32, "textAlign": "left", "verticalAlign": "top" }, { "type": "rectangle", "version": 60, "versionNonce": 897215480, "isDeleted": false, "id": "-Lq0agjWQ31TR_Av5Z4HW", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 520, "y": -60, "strokeColor": "#000000", "backgroundColor": "transparent", "width": 820, "height": 540, "seed": 495165432, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "jjuPzyRneMv3f65lps_6a" ] }, { "type": "arrow", "version": 537, "versionNonce": 1626949112, "isDeleted": false, "id": "KaCO9-QjUenSyCuuanoTo", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 721.0588599991052, "y": 60.17790458606555, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 1.0588599991051524, "height": 79.82209541393445, "seed": 637565832, "groupIds": [], "strokeSharpness": "round", "boundElementIds": [], "startBinding": null, "endBinding": { "focus": 0, "gap": 1, "elementId": "bO0OVt6m7LowYpq22ePCA" }, "points": [ [ 0, 0 ], [ -1.0588599991051524, 39.82209541393445 ], [ -1.0588599991051524, 79.82209541393445 ] ], "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow" }, { "type": "text", "version": 112, "versionNonce": 358083143, "isDeleted": false, "id": "4hEOdlcwK6AHyVhjc-MXS", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 660, "y": 20, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 103, "height": 45, "seed": 352116984, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [], "fontSize": 36, "fontFamily": 1, "text": "Parse", "baseline": 32, "textAlign": "left", "verticalAlign": "top" }, { "type": "arrow", "version": 534, "versionNonce": 983577992, "isDeleted": false, "id": "74ifmqmu0vN0NK0_0FwPm", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 841.6574209245741, "y": 219, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 43.15128973100309, "height": 59.174989629909305, "seed": 1853344392, "groupIds": [], "strokeSharpness": "round", "boundElementIds": [], "startBinding": { "focus": 0.09211398277003865, "gap": 1, "elementId": "K4so-arfr0JX0NJx8vd7T" }, "endBinding": { "focus": -0.2163077865936296, "gap": 1, "elementId": "jz0Huq9-s6pNxDw0RqHcR" }, "points": [ [ 0, 0 ], [ -1.6574209245741258, 1 ], [ 41.493868806428964, -58.174989629909305 ] ], "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow" }, { "type": "text", "version": 118, "versionNonce": 1185705864, "isDeleted": false, "id": "K4so-arfr0JX0NJx8vd7T", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 640, "y": 220, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 366, "height": 45, "seed": 765854200, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "74ifmqmu0vN0NK0_0FwPm" ], "fontSize": 36, "fontFamily": 1, "text": "Compile and Optimize", "baseline": 32, "textAlign": "left", "verticalAlign": "top" }, { "type": "arrow", "version": 791, "versionNonce": 1724761848, "isDeleted": false, "id": "1v60NED2criGG-wo9-oQL", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 960, "y": 320, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 80, "height": 160, "seed": 1764571528, "groupIds": [], "strokeSharpness": "round", "boundElementIds": [], "startBinding": { "focus": -0.1637630662020906, "gap": 1, "elementId": "dviXudWNxiHYQMZfqHWsH" }, "endBinding": { "focus": 0.07692307692307691, "gap": 1, "elementId": "UnmNTmwJtm6moubcGtSgB" }, "points": [ [ 0, 0 ], [ 80, -40 ], [ 40, -160 ] ], "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow" }, { "type": "text", "version": 194, "versionNonce": 473574648, "isDeleted": false, "id": "dviXudWNxiHYQMZfqHWsH", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 720, "y": 320, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 484, "height": 45, "seed": 1988297464, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "1v60NED2criGG-wo9-oQL" ], "fontSize": 36, "fontFamily": 1, "text": "Re-optimize and Deoptimize", "baseline": 32, "textAlign": "left", "verticalAlign": "top" }, { "type": "arrow", "version": 708, "versionNonce": 185615496, "isDeleted": false, "id": "jjuPzyRneMv3f65lps_6a", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1140, "y": 80, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 20, "height": 60, "seed": 1767688328, "groupIds": [], "strokeSharpness": "round", "boundElementIds": [], "startBinding": { "focus": -0.3021784319542362, "gap": 14.800415739789742, "elementId": "qhkjvI1VmWZdnKvU5QKZK" }, "endBinding": { "focus": 0.15789473684210528, "gap": 1, "elementId": "of76J4WOJHnHi0L61Vst_" }, "points": [ [ 0, 0 ], [ -20, 20 ], [ 0, 60 ] ], "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow" }, { "type": "text", "version": 213, "versionNonce": 2105884296, "isDeleted": false, "id": "qhkjvI1VmWZdnKvU5QKZK", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1080, "y": 20.19958426021026, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 139, "height": 45, "seed": 2115494904, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "jjuPzyRneMv3f65lps_6a" ], "fontSize": 36, "fontFamily": 1, "text": "Execute", "baseline": 32, "textAlign": "left", "verticalAlign": "top" }, { "type": "arrow", "version": 707, "versionNonce": 543827960, "isDeleted": false, "id": "5QQzhw_uqk_rBaW2wMriT", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1220, "y": 240, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 20, "height": 80, "seed": 2059564936, "groupIds": [], "strokeSharpness": "round", "boundElementIds": [], "startBinding": { "focus": 0.7391304347826086, "gap": 2, "elementId": "C6fyzTg2FHAmrRYfC_THm" }, "endBinding": { "focus": 0.3333333333333333, "gap": 1, "elementId": "SlvbjeV-9lXbcrlKib-hj" }, "points": [ [ 0, 0 ], [ 20, -40 ], [ 20, -80 ] ], "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow" }, { "type": "text", "version": 227, "versionNonce": 2002374136, "isDeleted": false, "id": "C6fyzTg2FHAmrRYfC_THm", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1160, "y": 220, "strokeColor": "#000000", "backgroundColor": "#868e96", "width": 58, "height": 45, "seed": 1651025144, "groupIds": [], "strokeSharpness": "sharp", "boundElementIds": [ "5QQzhw_uqk_rBaW2wMriT" ], "fontSize": 36, "fontFamily": 1, "text": "GC", "baseline": 32, "textAlign": "left", "verticalAlign": "top" } ], "appState": { "viewBackgroundColor": "#ffffff", "gridSize": 20 } } ``` ```graphviz digraph D { subgraph cluster_p { label = "Kroki"; subgraph cluster_c1 { label = "Server"; Filebeat; subgraph cluster_gc_1 { label = "Docker/Server"; Java; } subgraph cluster_gc_2 { label = "Docker/Mermaid"; "Node.js"; "Puppeteer"; "Chrome"; } } subgraph cluster_c2 { label = "CLI"; Golang; } } } ``` ```mermaid graph TD A[ Anyone ] -->|Can help | B( Go to github.com/yuzutech/kroki ) B --> C{ How to contribute? } C --> D[ Reporting bugs ] C --> E[ Sharing ideas ] C --> F[ Advocating ] ``` ```nomnoml [Pirate|eyeCount: Int|raid();pillage()| [beard]--[parrot] [beard]-:>[foul mouth] ] [Marauder]<:--[Pirate] [Pirate]- 0..7[mischief] [jollyness]->[Pirate] [jollyness]->[rum] [jollyness]->[singing] [Pirate]-> *[rum|tastiness: Int|swig()] [Pirate]->[singing] [singing]<->[rum] ``` ```pikchr $r = 0.2in linerad = 0.75*$r linewid = 0.25 # Start and end blocks # box "element" bold fit line down 50% from last box.sw dot rad 250% color black X0: last.e + (0.3,0) arrow from last dot to X0 move right 3.9in box wid 5% ht 25% fill black X9: last.w - (0.3,0) arrow from X9 to last box.w # The main rule that goes straight through from start to finish # box "object-definition" italic fit at 11/16 way between X0 and X9 arrow to X9 arrow from X0 to last box.w # The LABEL: rule # arrow right $r from X0 then down 1.25*$r then right $r oval " LABEL " fit arrow 50% oval "\":\"" fit arrow 200% box "position" italic fit arrow line right until even with X9 - ($r,0) \ then up until even with X9 then to X9 arrow from last oval.e right $r*0.5 then up $r*0.8 right $r*0.8 line up $r*0.45 right $r*0.45 then right # The VARIABLE = rule # arrow right $r from X0 then down 2.5*$r then right $r oval " VARIABLE " fit arrow 70% box "assignment-operator" italic fit arrow 70% box "expr" italic fit line right until even with X9 - ($r,0) \ then up until even with X9 then to X9 # The PRINT rule # arrow right $r from X0 then down 3.75*$r then right $r oval "\"print\"" fit arrow box "print-args" italic fit line right until even with X9 - ($r,0) \ then up until even with X9 then to X9 ``` ```plantuml skinparam monochrome true skinparam ranksep 20 skinparam dpi 150 skinparam arrowThickness 0.7 skinparam packageTitleAlignment left skinparam usecaseBorderThickness 0.4 skinparam defaultFontSize 12 skinparam rectangleBorderThickness 1 rectangle "Main" { (main.view) (singleton) } rectangle "Base" { (base.component) (component) (model) } rectangle "main.ts" as main_ts (component) ..> (base.component) main_ts ==> (main.view) (main.view) --> (component) (main.view) ...> (singleton) (singleton) ---> (model) ``` ```structurizr workspace { model { user = person "User" softwareSystem = softwareSystem "Software System" { webapp = container "Web Application" { user -> this "Uses!!!" } database = container "Database" { webapp -> this "Reads from and writes to" } } } views { systemContext softwareSystem { include * autolayout lr } container softwareSystem { include * autolayout lr } theme default } } ``` ```svgbob .-,( ),-. ___ _ .-( )-. [___]|=| -->( ) __________ /::/ |_| '-( ).-' --->[_...__... ] '-.( ).-' \ ____ __ '--->| | |==| |____| | | /::::/ |__| ``` ```vega { "$schema": "https://vega.github.io/schema/vega/v5.json", "width": 400, "height": 200, "padding": 5, "data": [ { "name": "table", "values": [ {"category": "A", "amount": 28}, {"category": "B", "amount": 55}, {"category": "C", "amount": 43}, {"category": "D", "amount": 91}, {"category": "E", "amount": 81}, {"category": "F", "amount": 53}, {"category": "G", "amount": 19}, {"category": "H", "amount": 87} ] } ], "signals": [ { "name": "tooltip", "value": {}, "on": [ {"events": "rect:mouseover", "update": "datum"}, {"events": "rect:mouseout", "update": "{}"} ] } ], "scales": [ { "name": "xscale", "type": "band", "domain": {"data": "table", "field": "category"}, "range": "width", "padding": 0.05, "round": true }, { "name": "yscale", "domain": {"data": "table", "field": "amount"}, "nice": true, "range": "height" } ], "axes": [ { "orient": "bottom", "scale": "xscale" }, { "orient": "left", "scale": "yscale" } ], "marks": [ { "type": "rect", "from": {"data":"table"}, "encode": { "enter": { "x": {"scale": "xscale", "field": "category"}, "width": {"scale": "xscale", "band": 1}, "y": {"scale": "yscale", "field": "amount"}, "y2": {"scale": "yscale", "value": 0} }, "update": { "fill": {"value": "steelblue"} }, "hover": { "fill": {"value": "red"} } } }, { "type": "text", "encode": { "enter": { "align": {"value": "center"}, "baseline": {"value": "bottom"}, "fill": {"value": "#333"} }, "update": { "x": {"scale": "xscale", "signal": "tooltip.category", "band": 0.5}, "y": {"scale": "yscale", "signal": "tooltip.amount", "offset": -2}, "text": {"signal": "tooltip.amount"}, "fillOpacity": [ {"test": "datum === tooltip", "value": 0}, {"value": 1} ] } } } ] } ``` ```vegalite { "$schema": "https://vega.github.io/schema/vega-lite/v4.json", "description": "Horizontally concatenated charts that show different types of discretizing scales.", "data": { "values": [ {"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43}, {"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53}, {"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52} ] }, "hconcat": [ { "mark": "circle", "encoding": { "y": { "field": "b", "type": "nominal", "sort": null, "axis": { "ticks": false, "domain": false, "title": null } }, "size": { "field": "b", "type": "quantitative", "scale": { "type": "quantize" } }, "color": { "field": "b", "type": "quantitative", "scale": { "type": "quantize", "zero": true }, "legend": { "title": "Quantize" } } } }, { "mark": "circle", "encoding": { "y": { "field": "b", "type": "nominal", "sort": null, "axis": { "ticks": false, "domain": false, "title": null } }, "size": { "field": "b", "type": "quantitative", "scale": { "type": "quantile", "range": [80, 160, 240, 320, 400] } }, "color": { "field": "b", "type": "quantitative", "scale": { "type": "quantile", "scheme": "magma" }, "legend": { "format": "d", "title": "Quantile" } } } }, { "mark": "circle", "encoding": { "y": { "field": "b", "type": "nominal", "sort": null, "axis": { "ticks": false, "domain": false, "title": null } }, "size": { "field": "b", "type": "quantitative", "scale": { "type": "threshold", "domain": [30, 70], "range": [80, 200, 320] } }, "color": { "field": "b", "type": "quantitative", "scale": { "type": "threshold", "domain": [30, 70], "scheme": "viridis" }, "legend": { "title": "Threshold" } } } } ], "resolve": { "scale": { "color": "independent", "size": "independent" } } } ``` ```wavedrom { signal: [ { name: "clk", wave: "p.....|..." }, { name: "Data", wave: "x.345x|=.x", data: ["head", "body", "tail", "data"] }, { name: "Request", wave: "0.1..0|1.0" }, {}, { name: "Acknowledge", wave: "1.....|01." } ]} ``` ````
eeyrjmr commented 2 years ago
The Kroki server only includes the following JVM-based diagrams libraries:

[Ditaa](http://ditaa.sourceforge.net/)

[PlantUML](https://github.com/plantuml/plantuml) including [C4 model](https://github.com/RicardoNiepel/C4-PlantUML)

[Structurizr](https://github.com/structurizr/dsl)

[UMlet](https://github.com/umlet/umlet)

If you want to use additional diagram libraries, you will either need to install them manually on your system or use [Docker or Podman](https://docs.kroki.io/kroki/setup/install/#docker-podman)

It appears to be a wrapper around the other diagram renderers so including the other type is required

cweagans commented 2 years ago

True. The hosted service has all of them included though.

alexandreSalconiDenis commented 1 year ago

@cweagans Quick question, cold you provide an example of footer.tmpl use with your current script please?

cweagans commented 1 year ago

Literally just this:

<script src="/assets/kroki.js" type="text/javascript"></script>

This is the entire contents of my custom/ directory:

custom/public/kroki.js
custom/templates/custom/footer.tmpl

kroki.js is the JS in the issue body above.

alexandreSalconiDenis commented 1 year ago

Literally just this:

<script src="/assets/kroki.js" type="text/javascript"></script>

This is the entire contents of my custom/ directory:

custom/public/kroki.js
custom/templates/custom/footer.tmpl

kroki.js is the JS in the issue body above.

Thanks, with that and a docker-compose self-host it works perfectly. :partying_face:

alexandreSalconiDenis commented 1 year ago

@cweagans I've found a weird beaviour not sure if the issue is from the kroki.js. If you put two wavedrom (any block of code) back two back only the first is render. I suspect that a sequece exist to early during the fetch of block of code. Do you have the same issue on your side?

alexandreSalconiDenis commented 1 year ago

I've fixed the issue by remplacing the:

for (var block_i = 0; block_i < codeBlocks.length; block_i++) {

with:

Array.from(codeBlocks).forEach(function(element, index, array) {

and codeBlocks[block_i] to element

Crown0815 commented 5 months ago

Hi, I am trying to get this to work in a dockerized instance but cannot get the examples to render properly. My docker compose file looks like this:

version: "3"

networks:
  gitea:
    external: false

services:
  server:
    image: gitea/gitea:1.21.10
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=****
      - GITEA__server__PROTOCOL=https
      - GITEA__server__HTTP_PORT=443
      - GITEA__server__DOMAIN=${DOMAIN}
      - GITEA__server__SSH_DOMAIN=${DOMAIN}
      - GITEA__server__ROOT_URL=https://${DOMAIN}/
      - GITEA__server__CERT_FILE=/certificates/fullchain.pem
      - GITEA__server__KEY_FILE=/certificates/privkey.pem
      - GITEA__APP_NAME=${APP_NAME}
    restart: unless-stopped
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - ./customizations/public:/data/gitea/public
      - ./customizations/templates/custom:/data/gitea/templates/custom
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      - ./certificates:/certificates
    ports:
      - "443:443"
      - "222:22"
    depends_on:
      - db

  db:
    image: postgres:14
    restart: unless-stopped
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=****
      - POSTGRES_DB=gitea
    networks:
      - gitea
    volumes:
      - ./postgres:/var/lib/postgresql/data

I have the files configured as described above (without the fix so far) but none of the diagrams renders.

I am probably missing something, but I cannot tell what.

alexandreSalconiDenis commented 5 months ago

@Crown0815 Your compose file doesn't have the docker for kroki. Just to be sure do you have at least the base docker up for kroki?

check: here

Crown0815 commented 5 months ago

Hi @alexandreSalconiDenis,

I did not have these services indeed. I added them now, but still none of the diagrams render. I assumed it was unnecessary to self-host based on this line from the kroki.js script:

newDiagram.innerHTML = '<img src="https://kroki.io/' + currentValue + '/svg/' + payload + '" />';

I assumed that this would go to the hosted kroki instance and render the images there.

The contents of kroki.js from here can be taken as is, right? I am unfortunately not familiar with JavaScript so I just took the script as I found it.

arika0093 commented 1 month ago

For everyone interested in this topic: As described in help, there are two ways to do this. The first way is to draw when rendering markdown (including wiki), which is basically the way @cweagans does it. However, it needed to be placed in custom/public/assets/kroki.js instead of custom/public/kroki.js. Also, if you want to reference a self-hosted kroki server, you need to replace https://kroki.io/ with your own server.

- newDiagram.innerHTML = '<img src="https://kroki.io/' + currentValue + '/svg/' + payload + '" />';
+ const SERVER_URL = "https://your-kroki-server.example.com/"
+ newDiagram.innerHTML = '<img src=' + SERVER_URL + currentValue + '/svg/' + payload + '" />';

And another way is to render file.plantuml when opened directly. This one requires editing app.ini and calling the renderer. I didn't know how to call it at first, but it is no problem, just run the API from curl.

[markup.plantuml]
ENABLED = true
FILE_EXTENSIONS = .puml,.plantuml,.pu
RENDER_COMMAND = "curl --data-binary @- https://your-kroki-server.example.com/plantuml/svg"
IS_INPUT_FILE = false
RENDER_CONTENT_MODE = no-sanitizer

I hope this helps.

Crown0815 commented 1 month ago

@arika0093 Thank you so much.

However, it needed to be placed in custom/public/assets/kroki.js instead of custom/public/kroki.js.

This was the issue on my side. Now I get the diagrams to render properly.

I came across an issue which I am curious if there is a solution for. I am using (U+a789) in a Ditaa diagram, which breaks the diagram because the warning

The character U+a789 "꞉" could be confused with the ASCII character U+003a ":", which is more common in source code. [Adjust settings](https://github.com/go-gitea/gitea/issues/20803)

Is also included in the rendering. Is there a way to exclude it from rendering the warning?