Merge pull request 'feat(create): history viewer — revision timeline display' (#271) from feat/history-viewer into main
Some checks failed
Build and Test / build (push) Has been cancelled

Reviewed-on: #271
This commit was merged in pull request #271.
This commit is contained in:
2026-02-19 00:43:14 +00:00

View File

@@ -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"<b>Rev {rev_num}</b>")
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,
}