TTanasartUpdate docs
786c0ca4创建于 23 天前历史提交
# %%
import os
import re
import math
import fileUtil

import nodeParser
import juncWriter

with open("docsdata/templates/node.html", "r") as f:
    template = f.read()

def generateBadge(title, tooltip, color, page = ""):
    style = f"color: #{color}; background-color: #{color}16; border-color: #{color}60;"
    return f'<a href="{page}" class="badge" style="{style}" title="{tooltip}">{title}</a>'

def generateBasicData(nodeData, metadata):  
    nodeName   = metadata["name"]
    node       = metadata["baseNode"]
    spr        = metadata["spr"]     if "spr" in metadata else f"s_{node.lower()}"
    
    categories = nodeData["categories"]
    parents    = nodeData["inheritances"]

    basicData = '<tr><th class="head first" colspan="2"><p>Node Data</p></th></tr>'
    basicData += f'<tr><th colspan="2"><img {spr}></th></tr>'

    badges = ""
    for btitle, bdesp, bcolor, bpage in categories:
        badges += generateBadge(btitle, bdesp, bcolor, bpage)

    if badges != "":
        basicData += '<tr style="height: 4px;"></tr>'
        basicData += f'<tr><th colspan="2">{badges}</th></tr>'
        basicData += '<tr style="height: 8px;"></tr>'

    basicData += f'<tr><th colspan="2" class="summary-topic"><p class="subtopic">Display name</p></th></tr>'
    basicData += f'<tr><th colspan="2" class="summary-content"><p>{nodeName}</p></th></tr>'
    
    basicData += f'<tr><th colspan="2" class="summary-topic"><p class="subtopic">Internal name</p></th></tr>'
    basicData += f'<tr><th colspan="2" class="summary-content"><p>{node}</p></th></tr>'

    if "tags" in metadata:
        basicData += f'<tr><th colspan="2" class="summary-tag"><div>'
        for tag in metadata["tags"]:
            href = f"/nodes/_tags/{fileUtil.pathSanitize(tag)}.html"
            basicData += f'<a href="{href}" class="tag {tag.lower().replace(" ", "_")}">{tag}</a>'
        basicData += '</div></th></tr>'
    
    basicData += '<tr height="8px"></tr>'
    basicData += '<tr><th class="head" colspan="2"><p>Inheritances</p></th></tr>'

    for i, p in enumerate(parents):
        pName = p["name"]
        link  = ""

        if pName == "Node":
            link = "/nodes/index.html"
        elif pName == "Node_Processor":
            link = "/nodes/array_processor.html"
        else:
            link = f"/nodes/_index/{pName}.html"
        
        _class = "inheritance-block"
        if i == len(parents) - 1:
            _class = "inheritance-block current"
            link   = "#"

        content = f'<a href="{link}">{pName}</a>'
        if i > 0:
            content = f'''<div class="vertical"></div>{content}'''
        basicData += f'''<tr><th colspan="2" class="{_class}">{content}</th></tr>'''
            
    return basicData

def applySummaryTable(basicData, junctionText, attributeText):
    return basicData + '<tr height="8px"></tr>' + \
           junctionText +                         \
           attributeText

def applyTemplate(template, nodeName, tooltip, summary):
    return template.replace("{{nodeName}}", nodeName) \
                   .replace("{{tooltip}}",  tooltip)  \
                   .replace("{{summary}}",  summary)

def versionToInt(version):
    v  = re.sub(r"[^0-9]", "", version)
    v += "0" * (10 - len(v))

    return int(v)

def writeChangeTable(metadata, changeData):
    nodeName = metadata["name"]
    hasAnyChange = False

    changeText = '''
<br><h2 class="nosidebar">Commit History</h2><br>
<table class="change-table">'''

    addVersion = metadata["pxc_version"] if "pxc_version" in metadata else None
    addString  = ""

    if addVersion:
        addVersion = math.floor(addVersion)
        vMaj = 1

        if addVersion > 100_000:
            vMin = math.floor((addVersion % 100_000) / 1_000)
            vBet = math.floor((addVersion %   1_000) /    10)
            addString = f"{vMaj}.{vMin}.{vBet}"

            vPat = addVersion % 10
            if vPat != 0:
                addString = f"{addString}.{vPat}"

        elif addVersion > 10_000:
            vMin = math.floor((addVersion %  10_000) /   100)
            vBet = math.floor((addVersion %     100) /    10)
            addString = f"{vMaj}.{vMin}.{vBet}"
            
            vPat = addVersion % 10
            if vPat != 0:
                addString = f"{addString}.{vPat}"
        else:
            vMin = math.floor((addVersion %  1_000) /    10)
            vBet = addVersion % 10
            addString = f"{vMaj}.{vMin}.{vBet}"

        # print(f"Node {nodeName} was introduced in version {addString}/{addVersion}.")
        changeText += f'<tr><th>{addString}</th><td><ul><li><span>Introduced</span></li></ul></td></tr>'
        hasAnyChange = True

    changeData.sort(key=lambda x: versionToInt(x["version"]), reverse=True)
    for change in changeData:
        version = change["version"]
        changes = change["changes"]

        changeTextV   = f'<tr><th>{version}</th>'
        commitMessage = "<ul>"
        hasChanges    = False

        for c in changes:
            commit  = c["commit"]
            node    = c["node"]
            if(node != nodeName and node != "any"):
                continue
            if(commit.startswith("Fix")):
                continue
            
            if commit == "Introduced" and addString != "":
                continue
            
            commit = commit.split(":")[1] if ":" in commit else commit
            commitMessage += f'<li><span>{commit}</span></li>'
            hasChanges = True

        commitMessage += "</ul>"
        changeTextV   += f'<td>{commitMessage}</td></tr>'

        if hasChanges:
            changeText += changeTextV
            hasAnyChange = True

    changeText += '</table>'
    if not hasAnyChange:
        return ""
    return changeText

def writeLinks(linkContents, metadata, nodeData):
    nodeBase = metadata["baseNode"]
    srchub = "https://github.com/Ttanasart-pt/Pixel-Composer/blob/main"

    links = []
    baseLower  = nodeBase.lower()
    links.append(("Base Code", f'/scripts/{baseLower}/{baseLower}.gml'))

    shaders = nodeData["shaders"] if "shaders" in nodeData else []
    for shader in shaders:
        shaderLower = shader.lower()
        links.append((f"Shader [{shader}]", f'/shaders/{shaderLower}/{shaderLower}.fsh'))
    
    linkText = '''<br><h2 class="nosidebar">Related Links</h2><br><ul class="link-table">'''
    for link in linkContents:
        if link[0] is not None:
            linkText += f'''<li><span>{link[0]}: </span><a href="{link[1]}">{link[1]}</a></li>'''
        else:
            linkText += f'''<li><span><a href="{link[1]}">{link[1]}</a></span></li>'''

    for link in links:
        fullLink = srchub + link[1]
        linkText += f'''<li><span>{link[0]}: </span><a href="{fullLink}">{link[1]}</a></li>'''
    linkText += '</ul>'
    return linkText

def writeNode(metadata, contentPath, changeData = None):

    nodeName = metadata["name"]
    nodeBase = metadata["baseNode"]
    tooltip  = metadata["tooltip"] if "tooltip" in metadata else ""
    nodeData = nodeParser.getNodeData(nodeBase)

    if not nodeData:
        if nodeBase != "BaseNode":
            print(f"Node data for {nodeBase} not found.")
        return None
    
    basicData     = generateBasicData(nodeData, metadata)
    junctionText  = juncWriter.IOTable(nodeData)
    attributeText = juncWriter.AttributeTable(nodeData)

    summary  = applySummaryTable(basicData, junctionText, attributeText)
    content  = applyTemplate(template, nodeName, tooltip, summary)

    junctions = {}
    for junc in nodeData["inputs"] + nodeData["outputs"]:
        jName = junc["name"].lower()
        junctions[jName] = junc

    rawContent  = ""
    if os.path.exists(contentPath):
        with open(contentPath, "r") as f:
            rawContent = f.read()

    linkContents = []
    reLink = re.compile(r"\[link\]([\s\S]*?)\[\/link\]")
    links  = reLink.findall(rawContent)
    for l in links:
        lStrip = l.replace("<p>", "").replace("</p>", "")
        ls = lStrip.split("|")
        if len(ls) == 2:
            linkContents.append((ls[0].strip(), ls[1].strip()))
        elif len(ls) == 1:
            linkContents.append((None, ls[0].strip()))
        rawContent = rawContent.replace(f"[link]{l}[/link]", "")
    
    juncTags = re.findall(r'<junc\s(.*?)>', rawContent)
    for tag in juncTags:
        jName = tag.strip("/").lower()
        
        if jName == "": 
            continue

        if jName in junctions:
            jColor  = juncWriter.getColor(junctions[jName]["type"])
            rawContent = rawContent.replace(f'<junc {tag}>', f'<span class="junction" style="border-color: {jColor}AA">{jName.title()}</span>')
        else:
            rawContent = rawContent.replace(f'<junc {tag}>', f'<span class="junction">{jName.title()}</span>')

    attrTags = re.findall(r'<attr\s(.*?)/>', rawContent)
    for tag in attrTags:
        rawContent = rawContent.replace(f'<attr {tag}/>', f'<span class="inline-code">{tag}</span>')

    # PRE CONTENT
    nodeBaseLower = nodeBase.lower()
    imagePath = f"src/images/nodegen/{nodeBaseLower}/{nodeBaseLower}-graph.png"
    if os.path.exists(f"docsdata/{imagePath}"):
        content += f'<img class="node-graph-preview" src="/{imagePath}">'
        
    samplePath = f"docsdata/src/images/nodegen/{nodeBaseLower}/{nodeBaseLower}-sample.txt"
    if os.path.exists(samplePath):
        with open(samplePath, "r") as f:
            sampleContent = f.read()
        sampleContent = sampleContent.replace('"', '\\"').replace("\n", "")
        content += f'''<script>const sampleContent = "{sampleContent}";</script>'''
        content += f'''<button class="copy-sample" onclick="copyToClipboard(sampleContent)">
        Copy to Clipboard
</button>'''
    content += "<br>"

    # ADD CONTENT
    content += rawContent

    if changeData:
        content += writeChangeTable(metadata, changeData)

    content += writeLinks(linkContents, metadata, nodeData)
    
    return content

# %%
def writeCategory(name, nodes, nodeMetadata, subFilter = None):
    title   = name
    content = f'<h1>{title}</h1><br><br>'
    sgName  = ''
    subGroupCurrent = None

    closeGroup      = False
    closeNodeGroup  = False

    for node in nodes:
        if not isinstance(node, str):
            subGroup    = node["label"]
            sgName      = subGroup.strip("/")
            subsubGroup = subGroup.startswith("/")
            
            if subsubGroup:
                if subFilter and subFilter != subGroupCurrent:
                    continue
                
                if closeNodeGroup:
                    content += '</div>'
                    closeNodeGroup = False

                content += f'<h5 class="node-group-title">{sgName}</h5>'
                
            else:
                subGroupCurrent = sgName
                if subFilter and subFilter != subGroupCurrent:
                    continue

                if closeNodeGroup:
                    content += '</div>'
                    closeNodeGroup = False

                if closeGroup:
                    content += '</div>'
                    closeGroup = False
                
                content += '<div class="node-category">'
                content += f'<h3 class="node-group-title">{sgName}</h3>'
                closeGroup = True
            continue

        if subFilter and subFilter != subGroupCurrent:
            continue

        if not closeNodeGroup:
            content += '<div class="node-group">'
            closeNodeGroup = True
        
        if node in nodeMetadata:
            metadata = nodeMetadata[node]
            name     = metadata["name"]
            spr      = metadata["spr"]  if "spr" in metadata else f"s_{node.lower()}"
        else:
            name = node.replace("_", " ").title()
            spr  = "node_generic"

        content += f'<div class="node-card"><a href="/nodes/_index/{node.lower()}.html"><img {spr}>{name}</a></div>\n'

    if closeGroup:
        content += '</div>'

    if closeNodeGroup:
        content += '</div>'

    return content

def writeTag(tag, nodes, nodeMetadata):
    title    = f"Tag: {tag}"
    content  = f"""<h1>{title}</h1><br><br>"""
    content += '<div class="node-category">'
    content += f'<h3 class="node-group-title">Nodes</h3>'
    content += '<div class="node-group">'

    for node in nodes:
        if node not in nodeMetadata:
            print(f"Node metadata for {node} not found.")
            continue
        
        metadata = nodeMetadata[node]
        name     = metadata["name"]
        spr      = metadata["spr"]  if "spr" in metadata else f"s_{node.lower()}"
        nodeName = fileUtil.pathSanitize(name)

        content += f'''<div class="node-card"><a href="/nodes/_index/{nodeName}.html"><img {spr}>{name}</a></div>\n'''
    
    content += '</div></div>'
    return content