From 122a5f0375e337c821ba9cc03fcea0994848e8e9 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 13 Feb 2026 20:23:41 -0800 Subject: [PATCH 01/21] feat: add version history browser (CE3) Add getArticleHistory, readVersion, and restoreVersion to CmsService with ancestry validation and provenance trailers. Wire up three new server endpoints and an Admin UI history panel with lazy-fetch, preview, and restore. Includes 18 new tests (13 unit, 5 integration). --- CHANGELOG.md | 7 ++ public/index.html | 173 ++++++++++++++++++++++++++++++++++++++++++ src/lib/CmsService.js | 125 ++++++++++++++++++++++++++++++ src/server/index.js | 42 ++++++++++ test/git.test.js | 149 ++++++++++++++++++++++++++++++++++++ test/server.test.js | 93 +++++++++++++++++++++++ 6 files changed, 589 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3118d2..b8cecb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to git-cms are documented in this file. ### Added +- **Version History Browser (CE3):** Browse prior versions of an article, preview old content, and restore a selected version as a new draft commit + - `CmsService.getArticleHistory()` — walk parent chain to list version summaries (SHA, title, status, author, date) + - `CmsService.readVersion()` — read full content of a specific commit by SHA + - `CmsService.restoreVersion()` — restore historical content as a new draft with ancestry validation and provenance trailers (`restoredFromSha`, `restoredAt`) + - `GET /api/cms/history`, `GET /api/cms/show-version`, `POST /api/cms/restore` server endpoints + - Admin UI: collapsible history panel with lazy-fetch, version preview, and restore button + - **Content Identity Policy (M1.1):** Canonical slug validation with NFKC normalization, reserved word rejection, and `CmsValidationError` contract (`ContentIdentityPolicy.js`) - **State Machine (M1.2):** Explicit draft/published/unpublished/reverted states with enforced transition rules (`ContentStatePolicy.js`) - **Admin UI overhaul:** Split/edit/preview markdown editor (via `marked`), autosave, toast notifications, skeleton loading, drag-and-drop file uploads, metadata trailer editor, keyboard shortcuts (`Cmd+S`, `Esc`), dark mode token system diff --git a/public/index.html b/public/index.html index 6cd5884..f02ba1a 100644 --- a/public/index.html +++ b/public/index.html @@ -359,6 +359,50 @@ margin-top: var(--size-2); } + /* ── History Section ── */ + #historySection .history-list { + max-height: 240px; + overflow-y: auto; + margin-top: var(--size-2); + display: flex; + flex-direction: column; + gap: 2px; + } + .history-item { + display: flex; + align-items: center; + gap: var(--size-3); + padding: var(--size-2) var(--size-3); + border-radius: var(--radius-2); + cursor: pointer; + transition: background 0.15s; + font-size: var(--font-size-0); + } + .history-item:hover { background: var(--surface-3); } + .history-item.active { background: var(--brand); color: white; } + .history-item .hist-sha { font-family: var(--font-mono); font-size: var(--font-size-00); opacity: 0.7; } + .history-item .hist-title { flex: 1; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .history-item .hist-date { font-size: var(--font-size-00); opacity: 0.7; white-space: nowrap; } + .history-item .hist-status { font-size: var(--font-size-00); opacity: 0.7; } + + #historyPreview { + margin-top: var(--size-3); + border: 1px solid var(--surface-3); + border-radius: var(--radius-2); + padding: var(--size-3); + display: none; + } + #historyPreview .preview-content { + max-height: 300px; + overflow-y: auto; + line-height: 1.7; + } + #historyPreview .preview-actions { + margin-top: var(--size-3); + display: flex; + gap: var(--size-2); + } + /* ── Status Bar ── */ .status-bar { font-size: var(--font-size-0); @@ -457,6 +501,17 @@

Git CMS

+
+ Version History +
+
+
+
+ +
+
+
+
Drop files here or