109 lines
3.5 KiB
Python
109 lines
3.5 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""Default style definitions and style resolution for document rendering."""
|
|
|
|
from typing import Any, Dict
|
|
|
|
|
|
DEFAULT_STYLE: Dict[str, Any] = {
|
|
"fonts": {
|
|
"primary": "Calibri",
|
|
"monospace": "Consolas",
|
|
},
|
|
"colors": {
|
|
"primary": "#24292e",
|
|
"secondary": "#586069",
|
|
"accent": "#0366d6",
|
|
"background": "#FFFFFF",
|
|
},
|
|
"documentTitle": {
|
|
"sizePt": 28,
|
|
"weight": "bold",
|
|
"color": "#24292e",
|
|
"spaceBeforePt": 0,
|
|
"spaceAfterPt": 18,
|
|
"align": "center",
|
|
},
|
|
"headings": {
|
|
"h1": {"sizePt": 22, "weight": "bold", "color": "#24292e", "spaceBeforePt": 24, "spaceAfterPt": 8},
|
|
"h2": {"sizePt": 18, "weight": "bold", "color": "#24292e", "spaceBeforePt": 20, "spaceAfterPt": 6},
|
|
"h3": {"sizePt": 14, "weight": "bold", "color": "#586069", "spaceBeforePt": 16, "spaceAfterPt": 4},
|
|
"h4": {"sizePt": 12, "weight": "bold", "color": "#586069", "spaceBeforePt": 12, "spaceAfterPt": 3},
|
|
},
|
|
"paragraph": {"sizePt": 11, "lineSpacing": 1.5, "color": "#24292e", "align": "left"},
|
|
"table": {
|
|
"headerBg": "#f6f8fa",
|
|
"headerFg": "#24292e",
|
|
"headerSizePt": 10,
|
|
"bodySizePt": 10,
|
|
"rowBandingEven": "#f6f8fa",
|
|
"rowBandingOdd": "#FFFFFF",
|
|
"borderColor": "#e1e4e8",
|
|
"borderWidthPt": 0.5,
|
|
"borderStyle": "grid",
|
|
"bandingEnabled": True,
|
|
"cellPaddingPt": 4,
|
|
},
|
|
"list": {"bulletChar": "\u2022", "indentPt": 18, "sizePt": 11},
|
|
"image": {"defaultWidthPt": 480, "maxWidthPt": 800, "alignment": "center"},
|
|
"codeBlock": {"fontSizePt": 9, "background": "#f6f8fa", "borderColor": "#e1e4e8"},
|
|
"coverPage": {
|
|
"titleSizePt": 28,
|
|
"subtitleSizePt": 16,
|
|
"authorSizePt": 12,
|
|
"dateSizePt": 12,
|
|
"titleColor": "#24292e",
|
|
"subtitleColor": "#586069",
|
|
},
|
|
"caption": {
|
|
"sizePt": 10,
|
|
"color": "#586069",
|
|
"italic": True,
|
|
"align": "center",
|
|
},
|
|
"page": {
|
|
"format": "A4",
|
|
"marginsPt": {"top": 60, "bottom": 60, "left": 60, "right": 60},
|
|
"showPageNumbers": True,
|
|
"headerHeight": 30,
|
|
"footerHeight": 30,
|
|
"headerLogo": None,
|
|
"headerText": "",
|
|
"footerText": "",
|
|
},
|
|
}
|
|
|
|
|
|
|
|
|
|
def deepMerge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Recursively merge override into base. Both dicts left unchanged; returns new dict."""
|
|
result = {}
|
|
for key in base:
|
|
if key in override:
|
|
baseVal = base[key]
|
|
overVal = override[key]
|
|
if isinstance(baseVal, dict) and isinstance(overVal, dict):
|
|
result[key] = deepMerge(baseVal, overVal)
|
|
else:
|
|
result[key] = overVal
|
|
else:
|
|
result[key] = base[key]
|
|
for key in override:
|
|
if key not in base:
|
|
result[key] = override[key]
|
|
return result
|
|
|
|
|
|
def resolveStyle(agentStyle: dict | None = None) -> Dict[str, Any]:
|
|
"""Resolve the effective style: ``DEFAULT_STYLE <- agentStyle``.
|
|
|
|
Precedence (lowest to highest): platform defaults, then any explicit
|
|
per-call ``agentStyle`` override. With no override this returns plain
|
|
:data:`DEFAULT_STYLE`. Context-aware styling is handled by the AI
|
|
enhancement step in ``mainServiceGeneration.renderReport``.
|
|
"""
|
|
resolved = dict(DEFAULT_STYLE)
|
|
if agentStyle:
|
|
resolved = deepMerge(resolved, agentStyle)
|
|
return resolved
|