diff --git a/src/Mod/Create/silo_viewers.py b/src/Mod/Create/silo_viewers.py index 2f4f00da64..c95b8a00c5 100644 --- a/src/Mod/Create/silo_viewers.py +++ b/src/Mod/Create/silo_viewers.py @@ -423,6 +423,131 @@ class SiloMetadataEditor(QtWidgets.QWidget): event.accept() +# --------------------------------------------------------------------------- +# History Viewer +# --------------------------------------------------------------------------- + +_BADGE_STYLES = { + "draft": "background: #45475a; color: #cdd6f4;", + "review": "background: #f9e2af; color: #11111b;", + "released": "background: #a6e3a1; color: #11111b;", + "obsolete": "background: #f38ba8; color: #11111b;", +} + + +class SiloHistoryViewer(QtWidgets.QWidget): + """Read-only revision timeline from ``silo/history.json``.""" + + WINDOW_TITLE = "Silo \u2014 History" + + def __init__(self, obj, parent=None): + super().__init__(parent) + self.setObjectName(f"SiloViewer_{obj.Name}") + self._build_ui(obj.RawContent) + + def _build_ui(self, raw_content): + try: + data = json.loads(raw_content) + except Exception: + data = {} + revisions = data.get("revisions", []) + + outer = QtWidgets.QVBoxLayout(self) + outer.setContentsMargins(16, 16, 16, 16) + outer.setSpacing(12) + + # Header + header = QtWidgets.QHBoxLayout() + title = QtWidgets.QLabel("Revision History") + font = title.font() + font.setPointSize(font.pointSize() + 2) + font.setBold(True) + title.setFont(font) + header.addWidget(title) + header.addStretch() + outer.addLayout(header) + + # Separator + line = QtWidgets.QFrame() + line.setFrameShape(QtWidgets.QFrame.HLine) + line.setFrameShadow(QtWidgets.QFrame.Sunken) + outer.addWidget(line) + + if not revisions: + placeholder = QtWidgets.QLabel("No revision history available.") + placeholder.setAlignment(QtCore.Qt.AlignCenter) + outer.addWidget(placeholder) + outer.addStretch() + return + + # Scroll area with revision cards + scroll = QtWidgets.QScrollArea() + scroll.setWidgetResizable(True) + scroll.setFrameShape(QtWidgets.QFrame.NoFrame) + content = QtWidgets.QWidget() + cards_layout = QtWidgets.QVBoxLayout(content) + cards_layout.setContentsMargins(0, 0, 0, 0) + cards_layout.setSpacing(0) + + for i, rev in enumerate(revisions): + cards_layout.addWidget(self._make_revision_card(rev)) + if i < len(revisions) - 1: + sep = QtWidgets.QFrame() + sep.setFrameShape(QtWidgets.QFrame.HLine) + sep.setFrameShadow(QtWidgets.QFrame.Sunken) + cards_layout.addWidget(sep) + + cards_layout.addStretch() + scroll.setWidget(content) + outer.addWidget(scroll, 1) + + def _make_revision_card(self, rev): + """Build a widget for a single revision entry.""" + card = QtWidgets.QWidget() + layout = QtWidgets.QVBoxLayout(card) + layout.setContentsMargins(4, 8, 4, 8) + layout.setSpacing(4) + + # Top line: Rev N · status badge · author · timestamp + top = QtWidgets.QHBoxLayout() + top.setSpacing(8) + + rev_num = rev.get("revision", "?") + rev_label = QtWidgets.QLabel(f"Rev {rev_num}") + top.addWidget(rev_label) + + status = rev.get("status", "draft") + badge = QtWidgets.QLabel(status) + style = _BADGE_STYLES.get(status, _BADGE_STYLES["draft"]) + badge.setStyleSheet( + f"QLabel {{ {style} border-radius: 4px; " + f"padding: 1px 6px; font-size: 11px; }}" + ) + top.addWidget(badge) + + author = rev.get("author", "") + if author: + top.addWidget(QtWidgets.QLabel(f"\u00b7 {author}")) + + timestamp = rev.get("timestamp", "") + if timestamp: + display_ts = _format_value("created_at", timestamp) + top.addWidget(QtWidgets.QLabel(f"\u00b7 {display_ts}")) + + top.addStretch() + layout.addLayout(top) + + # Comment line + comment = rev.get("comment", "") + if comment: + comment_label = QtWidgets.QLabel(comment) + comment_label.setWordWrap(True) + comment_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) + layout.addWidget(comment_label) + + return card + + # --------------------------------------------------------------------------- # Viewer factory # --------------------------------------------------------------------------- @@ -430,6 +555,7 @@ class SiloMetadataEditor(QtWidgets.QWidget): _VIEWER_REGISTRY = { "silo/manifest.json": SiloManifestViewer, "silo/metadata.json": SiloMetadataEditor, + "silo/history.json": SiloHistoryViewer, }