scottrogowski / code2flow

Pretty good call graphs for dynamic languages
MIT License
3.9k stars 281 forks source link

feature: Configure colors / dark theme / theme that works with both light and dark schemes #100

Open pawamoy opened 1 month ago

pawamoy commented 1 month ago

Hey, thanks for the nice project!

I was wondering if you'd consider adding options to customize colors. The final use-case for me is to display call graphs generated with Code2Flow in pages that can be toggled between light and dark color schemes. If I set the background to transparent instead of white, the dark parts of the graph are then almost impossible to see (black arrows, black dot borders, black text).

If not, any guidance at how to achieve this through programmatic use would be great, too! Looking at the code, it looks like I can probably duplicate most of the code2flow function, to alter file_groups, all_nodes and edges, which probably hardcode their colors?

pawamoy commented 1 month ago

Or maybe I should simply use CSS, since I'm injecting the call graphs as SVG in HTML pages. EDIT: seems like it would be hard since there's no way to easily select relevant objects (black arrows for example).

pawamoy commented 1 month ago

OK looks like I can change the values of TRUNK_COLOR, LEAF_COLOR, EDGE_COLORS and NODE_COLOR in code2flow/model.py.

pawamoy commented 1 month ago

OK here's my code for future readers:

import subprocess
from io import StringIO

from code2flow import code2flow, model

model.TRUNK_COLOR = "#fca311"
model.LEAF_COLOR = "#98c1d9"
model.EDGE_COLORS = ["#b8b8ff"] * 8
model.NODE_COLOR = "#e5e5e5"

def _render_call_graph(module: Path) -> None:
    buffer = StringIO()
    code2flow(str(module), buffer)
    svg = subprocess.check_output(["dot", "-Tsvg"], input=buffer.getvalue(), text=True)  # noqa: S603, S607
    if 'class="node"' not in svg:
        print("")
    else:
        print(f'<div class="call-graph">{svg}</div>')

...

This _render_call_graph is actually used in another code block that is inserted directly in Markdown pages and executed by Markdown Exec. Pages are built with MkDocs and Material for MkDocs.

```python exec="1" idprefix="internal-" session="comment_blocks"
--8<-- "scripts/gen_internal_api.py"

render_internal_api(heading_level=4)

I customize CSS of the SVG elements:

```html
<style>
    .call-graph svg {
        /* min-width: 100%; */
        min-height: 200px;
    }
    .graph > polygon {
        fill-opacity: 0.0;
    }

    [data-md-color-scheme="default"] .cluster > polygon {
        stroke: black;
    }
    [data-md-color-scheme="default"] .cluster > text {
        fill: black;
    }

    [data-md-color-scheme="slate"] .cluster > polygon {
        stroke: white;
    }
    [data-md-color-scheme="slate"] .cluster > text {
        fill: white;
    }
</style>

...and even make the SVG zoomable and pannable with the JS library of the same name: https://github.com/bumbu/svg-pan-zoom.

<script>
    document.addEventListener("DOMContentLoaded", function(){
        const callGraphs = document.getElementsByClassName("call-graph");
        for (let i = 0; i < callGraphs.length; i++) {
            if (!callGraphs[i].firstElementChild.id) {
                callGraphs[i].firstElementChild.id = `call-graph-${i}`
            }
            svgPanZoom(`#${callGraphs[i].firstElementChild.id}`, {});
        }
    });
</script>

View from afar:

afarview