diff --git a/README.md b/README.md
index 9cc6703..4d7e99d 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
ForgeFlow is een Windows-desktopapp voor wie Git, Gitea en eigen Docker- of Unraid-servers gebruikt. Je ziet in één werkruimte wat lokaal gewijzigd is, wat op Gitea staat en welke exacte commit op de server draait. ForgeFlow begeleidt je daarna veilig door review, commit, push, deployment en verificatie.
-> Huidige release: **0.10.13** · [download de laatste Windows-release](https://gitea.itworx.tech/Jens/ForgeFlow/releases/latest)
+> Huidige release: **0.10.14** · [download de laatste Windows-release](https://gitea.itworx.tech/Jens/ForgeFlow/releases/latest)

@@ -17,6 +17,7 @@ ForgeFlow is een Windows-desktopapp voor wie Git, Gitea en eigen Docker- of Unra
- **Automatische serverinventaris:** ForgeFlow herkent draaiende en gestopte Docker-, Compose- en DockerMan-workloads, koppelt alleen op betrouwbaar bewijs en houdt tijdelijke of externe containers apart.
- **Veilige server-pull:** Unraid haalt de exacte commit uit Gitea met een unieke, repository-scoped read-only deploy key en een vastgepinde SSH-hostsleutel.
- **Ingebouwde Git Validator:** controleer repository-identiteit, branch protection, synchronisatie-instellingen, documentatie, geheimen en grote bestanden; veilige verbeteringen kunnen gericht worden toegepast.
+- **Doorzoekbaar Helpcentrum:** open **Help** voor stapsgewijze uitleg of spring vanuit workspace sync meteen naar de relevante veiligheidsinstructies.
- **Lokale controle:** configuratie en credentials blijven op het toestel en diagnostische exports worden lokaal geredigeerd.
## Snel starten
diff --git a/SOURCE_MANIFEST.txt b/SOURCE_MANIFEST.txt
index b56c88a..34b9c58 100644
--- a/SOURCE_MANIFEST.txt
+++ b/SOURCE_MANIFEST.txt
@@ -1,4 +1,4 @@
-ForgeFlow 0.10.13 source manifest
+ForgeFlow 0.10.14 source manifest
SHA-256 BYTES PATH
(The manifest excludes itself, dependencies and generated release artifacts.)
61f37822ae5502219a38b2eaf23fdcb611875f0e675efb4abe6157c9f072c0cc 937 .gitea/workflows/quality.yml
@@ -39,6 +39,7 @@ f79908fb3dad98c38030c6e6be7c79a1999e0478ed9c2496923891954438daa1 4581
36edb4f096a248fb8679bd13e5766befb478cf628c6ccfb21e3eda71bbec7633 992 docs/RELEASE_NOTES_0.10.11.md
a355d3f577c2ec85dde5dfd7b6995f4f1615e2f6bac597529f3ff102acd93c35 1292 docs/RELEASE_NOTES_0.10.12.md
609c55a1c0b06c307ebe16f2daaf1e48601edd57137586e4f2be1febd6a7060a 1931 docs/RELEASE_NOTES_0.10.13.md
+09eaf3f7671fbc5c25d5ca444415616ccf6dd8e6351855291d392b0aeac20bce 1556 docs/RELEASE_NOTES_0.10.14.md
8d713471a437a8a55b00d7e1dd95290680862107bc4e586cf27d727f6274e46c 577 docs/RELEASE_NOTES_0.10.2.md
0942fb2c4a4f972296423b5232687f7389e2c6417a9d48a3244beef9dec907b9 1164 docs/RELEASE_NOTES_0.10.3.md
8f4a0fe6dc250ae210cc2fc1c57c46091822ae6c2a58caa76e0091f255f9f30d 775 docs/RELEASE_NOTES_0.10.4.md
@@ -108,16 +109,16 @@ c230b931abf2293d2d44b7a69b94c35f1142c093cc46b88739a0de5cbd6d1896 1532
4a561ead5ba7cdfaf4efce91842a4308c5f2a77980205879d83835efb8a579db 1067 LICENSE
e2daa28bbc01c68c3702add6ea8259dff5920b22f6fdc3c9193ed78a153f2e9e 14708 main.cjs
91a984a89dd57a084b9a2331763cacdb061582fb590f13df379d92c1a77a2ee1 352 OVERLAY-INSTRUCTIONS.md
-46e77e759b75c39737fc9812d105b31c20b68f0c46c7994bebf01a487ec9f207 179808 package-lock.json
-ba81105b16f3f3605a9e578a9e7a40d6fbc29f0001ad96f7f296441e563ceeb4 6367 package.json
+65c548b9072c90530ee32686c34959e940e694684af243b17d5cbdb6bd47cb61 179808 package-lock.json
+fe6f311914aa4513e476267c795ca22311ab78d1bb4388d5a3e50f687d5e193f 6415 package.json
1237df9ddcbb5ac7dc4316f18c34ff4a7030e3e0d56216ade6dd07369e5e2a04 1353 playwright.config.mjs
e8f678b26a1b651ee0e06e499b0538d8a193d0565687e9f750b58f801e4fabd8 12473 preload.cjs
abe5dd6fd68f2970cd19ef134094907c67219061d8fe9a1a08324c78de4ad437 484 PUBLISH-AND-ENABLE-UPDATE.cmd
6d0858d6654c3c3dc7083ebbd234c88324afcebaecd7b772719440a8afbc2e4e 10736 Publish-ForgeFlow-Release.ps1
33f3c4795705ab77c6e6603c88a32c123b3a286bc77e8e472b76970485699338 4386 Publish-Missing-Binary-Release.ps1
-423447da25b001d869f9e9d7c860710f470945e72344332fc16b6c42a70d3ff6 10965 README.md
-79d93000a8b9dec65448f866f48d83b62d6bf37af26480becd73654544d454e1 15831 reports/architecture-audit.json
-dd54fd4a24120a466603acedab329bd3ac6f482ddb1e65c305d4cc755be5ec0f 1516 reports/architecture-audit.md
+521356cc921145e8229751f85d57652fa50e0267f0d953a9f86cdcfd33eb5838 11117 README.md
+d75cfae88987ff6e8b92a53c988e59cf6dcffb74cd7cf8d27b417001f68f6bc6 16450 reports/architecture-audit.json
+654fea47bb851d5865908debcdc2140593c8bd78d03c7a5bfb6573518de08919 1726 reports/architecture-audit.md
509c7bcff5280349bd9f45ed6151f70372bad7010a9ea582c13e2ccab91fe0cd 6272 scripts/acceptance.mjs
d0745072321aca2c80f44460974a7926715a9f429164aaf7660dced40b52c736 4790 scripts/apply-binary-update.ps1
404863bcbe7292355662e3a326455df864d7279badc29f90866a3b837420df54 10745 scripts/apply-source-update.ps1
@@ -133,7 +134,7 @@ d0e6fd6ce67b553a3654acd4393e5b9c3be825c45d03d957fee36fb2a3c56a85 8308
c76507857292c5713e1c699cf02e24b80265da39af2cecd148034bdb874adbb6 5246 scripts/test-authenticode-chain.ps1
4393f7dc5f417e6d601a68238f4e26791799a3634acec228fe4d79deaee85eb5 3109 scripts/validate-installed-connections.cjs
e6127e1e62f39c70ddb1abf72f4d7e7b8e3f19ff1f219e1a3660353c2e0cdfac 2411 scripts/verify-release-signatures.mjs
-587efcb60a363614fb61f722fa84a81e4da09c5ea35e6897009f82c5d244d740 21919 scripts/verify.mjs
+de14527e9fddfb904dd73ffe4e7836d8bb65abfdf7bd0c350bb9eb911b1d5de3 22289 scripts/verify.mjs
c2c9e4ba251d93a530a52b2d0079787680261c314083bb99d2356fc177719613 2434 scripts/write-release-checksums.mjs
619515f524cb89960370ffcbd3fafd3c0e178b95f69c5868b1dd44777f23ec1e 2081 setup-windows.ps1
dd613d04b366f2cd071a1685a414016a5fb008082ed1b4cb8b24b79c100f640a 2412 src/main/audit-service.cjs
@@ -179,22 +180,22 @@ d0bf607dd1de9d55f2947d0adf0997cd3ca5c269d10a5362cc1d8bc4d1a2a8ae 6706
48bed91dd2a85bb51ee7307f7acc3b79c881ce8cf63b22ba79d5d079b265eb4b 7785 src/renderer/actions/inventory.js
13b8611b5389625deeec59ff2a6cfebcfc93bd7972902439715be371d1f9f573 14936 src/renderer/actions/recovery.js
2414a0d29a0380d343b9b0e58ba1909e7a7eeb46357fd45ddbb3ad411d119f78 16280 src/renderer/actions/setup-and-settings.js
-a980f2e86d8286ea605a7259b9e9adcf3fda4f657b8d54a5d2d8765a7bbbec13 19065 src/renderer/actions/shell.js
-65e305965d6d00d45b516f271c0f905854797af07c38982768199bb30d988c2b 26659 src/renderer/app.js
+6900075c7cbcf6638b071735a3ce34954339d3d0ad324373c2bf5afcbd3cfe02 19618 src/renderer/actions/shell.js
+d8eee8bc10b23d877919560790c311924bc3c339775dfe225b5b460e040f25de 26829 src/renderer/app.js
16efd2fca83004f781eae40ae0f706a004ce0bddf338dd087b8adf7eb10c1d84 85704 src/renderer/assets/itworx-mark.png
813b8cdeecac43794166f3db9d3c5d2c441e0292f9ab7bd465ba136d6201e95d 82476 src/renderer/assets/itworx-wordmark-dark.png
094c1b71cc2482a9db250ac175f45f3de68f53277dfbde371a03e61923d00988 75240 src/renderer/assets/itworx-wordmark-light.png
813b8cdeecac43794166f3db9d3c5d2c441e0292f9ab7bd465ba136d6201e95d 82476 src/renderer/assets/itworx-wordmark.png
-fd47efd296409c19b80fd7719429e3eb33963ef78f30bc748d1770308aa472a3 55138 src/renderer/dialogs.js
+a9dfda1adb8910bb882428c237907b56163ee4901992a54a6e50c74f6037786a 55199 src/renderer/dialogs.js
dede1f21a06c73a2c2a462a869d27530d85f99baff202a2eb509c57436ad6aec 2732 src/renderer/diff-view.js
-eef2f269ba4fbb76bf66ad328d481b461255d0acb753b30878dd4d4eaac57dc6 6924 src/renderer/events.js
+e0ea09d8d3ab1033452a77a9dfa4557b29ad5428c9050713661fe4699e007bda 7245 src/renderer/events.js
c4a71213d412166093f7bd8254b847de4d8beb58c1aaa356a0cdc8d728080326 1524 src/renderer/index.html
06180d9656dd254edfb6949c397f8e313954fc560ddcb22b3a35fce3c3e35655 21350 src/renderer/mock-bridge.js
870024aff376826a92c9cf7452689cc1ecc5d9034f055bea56734f3f7fcea5e5 28703 src/renderer/mock-deployment-bridge.js
-92cacf58a3576044800ee5f9823d4fd2a1c768571b7f5d271950f5002a80d212 25031 src/renderer/mock-repository-bridge.js
+2e7ed10ac9555470f989e6e8d721fcf0db979a03c9508844e028fd9028e31465 25031 src/renderer/mock-repository-bridge.js
94fa265c2fe9ca8d644f0ce9b620b6f85d9b25dca5802c4e9195b66dcbe80120 6522 src/renderer/operations.js
-431ed7bbadecbcc99572aea9a5ae5550dbb5bacbd2d8028f8a05bf77f97b251e 81161 src/renderer/styles.css
-8ef063feee96c0a3692da21fa797bf44325388b94e051d53f20a5198036c9252 102998 src/renderer/views.js
+b541b5173d9b2a0063825243e87be4b8709d34d4f82a96d3a91d17471b3a60b7 87983 src/renderer/styles.css
+17a568f0844d6bbdac2c4e9e8d9e564b06c0557d9c2ddcb2a8eb513962306360 112565 src/renderer/views.js
e9e72c072a5c5d04f59cd6763de0cfbf736c2a5ffa2f722143f3bad2bdbc630b 1411 src/shared/clone-target.cjs
5d425d5c2f939d0f6beebee7ebb0c77146cb7e318535ba7286ec7081a4dc2269 2497 src/shared/deployment-policy.cjs
029e600229714d033c28e2dcb77817aa8269847001782ae0012960e83ffd183f 3057 src/shared/git-status.cjs
@@ -208,7 +209,7 @@ f8853dce6fdf360d5df2fbe2b6df3e5687630c807fee5ba8436679b34ec737ea 2436
058aeaa5d9bfe377c7e322f213c7871ecc4151b5d08ef790992f4ee28d857658 743 START-FORGEFLOW-OVERLAY.ps1
f5b0ea887fcdeadec78c1ad49b0ec7979723562f5c0b730703acb77a37281ee0 1009 tests/acceptance.test.mjs
a4e5947204ff6878e601e32477bc85b53cd0153baf95a161c8935b6e5466c257 1155 tests/audit-service.test.mjs
-0841f6a2515508f8d28562d5c565c1519d357b8579515f890883dc2aec7d7737 16881 tests/browser/forgeflow.spec.mjs
+8eb8023bfd1366f4cc8c1867fe16fa0bed8d6e9d29219100ae1336aa06d10110 19040 tests/browser/forgeflow.spec.mjs
1728c0a7abd92f4d7d9e68df32e4a6b00730555f23795e9b36416795d9d127af 5978 tests/clone-target.test.mjs
aa2ae0e5a12bc47f8024e0d7408148e3af797e3f3f0ecb2525fb1cc54cf4e1f0 17378 tests/config-store.test.mjs
f1463326aee79842d265687ae628189ce54e92544600f2bd14073780287cfb14 2502 tests/configuration-backup.test.mjs
@@ -233,7 +234,7 @@ caf98cbd9de9b119dae610ee53fa333a7a11214f34762247452fbb85e8bbf725 2392
1b6c920e18a248f78acaed6187197c88ec8d911b62d5e2a9f8ad57b91ae80499 11827 tests/preflight.test.mjs
7f2751ea2621f76b5427f442e931344d13e97faa7b6ef3151949bbd6a03097cf 1205 tests/process-error-policy.test.mjs
0cb884cf62c1cb02cf59a81662be055bcb5339d176de85e2a3eeb8e8573e11b3 6435 tests/production-acceptance.test.mjs
-252e0345fcd3cd3899467f2a9ed363df0675d529e59d4d6e1204f4464fd8a5f7 11485 tests/renderer-workflow.test.mjs
+1635efed857c776e677a064175a01b0f8b21bf7ead0b02c08956286077b8a37e 12294 tests/renderer-workflow.test.mjs
2b4956fa4df4624a04117737e57ba74020564330ff71303b5746d8ccc881e880 854 tests/repository-matching.test.mjs
76712a5d26f2598b00b83b925c9c84a90ab81c0eb1760895e9d6a2bd2f6eb425 5828 tests/repository-monitor.test.mjs
5476f3ba90bc096d4172900d9b54ada7c12da521f8627913d87794eade3cee23 13494 tests/repository-service.test.mjs
diff --git a/docs/RELEASE_NOTES_0.10.14.md b/docs/RELEASE_NOTES_0.10.14.md
new file mode 100644
index 0000000..ffe1657
--- /dev/null
+++ b/docs/RELEASE_NOTES_0.10.14.md
@@ -0,0 +1,15 @@
+# ForgeFlow 0.10.14
+
+## Betere uitleg en een stabiele repositorywerkruimte
+
+- Een nieuw doorzoekbaar **Help center** legt de belangrijkste workflows stap voor stap uit: eerste configuratie, changes en commits, Gitea workspace sync, deploymentdetectie, exacte serverdeployments, deploykeys, Git Validator, updates en diagnose.
+- Contextuele help vanuit **Gitea workspace sync** opent onmiddellijk de relevante uitleg. De instructies maken expliciet wat ForgeFlow wijzigt, welke recovery ForgeFlow vooraf maakt en welke genegeerde runtimebestanden onaangeroerd blijven.
+- De variabele **repository context** is samengebracht in één structurele zone. Quick actions, Local → Gitea → Server-status en gekoppelde deployments kunnen daardoor niet langer over de tabnavigatie of inhoud heen schuiven.
+- Smalle werkruimtes gebruiken gecontroleerde **horizontal tab navigation**. Elke tab behoudt zijn volledige label en blijft bereikbaar zonder dat tekst door andere bedieningselementen loopt.
+- Het Help center heeft een eigen premium, responsieve presentatie met categorieën, zoekresultaten, uitklapbare stappen, veiligheidsnotities en motion-safe projectillustratie.
+
+## Verificatie
+
+- Volledige Node-testset en statische renderercontroles.
+- **84 browser flows** over dark/light, compact/desktop/wide, 100–150% schaal en reduced motion; de drie tijdens een semantische testaanpassing geraakte flows zijn daarna opnieuw groen uitgevoerd.
+- Extra layoutasserties bewijzen dat repository context, tabs en tabinhoud elkaar niet overlappen bij 1024 × 768.
diff --git a/package-lock.json b/package-lock.json
index 71fcbfa..70eb8e2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "forgeflow",
- "version": "0.10.13",
+ "version": "0.10.14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "forgeflow",
- "version": "0.10.13",
+ "version": "0.10.14",
"dependencies": {
"ssh2": "1.17.0"
},
diff --git a/package.json b/package.json
index 2695941..9922dc8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "forgeflow",
- "version": "0.10.13",
+ "version": "0.10.14",
"private": true,
"description": "Desktop release cockpit for local Git, Gitea Actions and controlled exact-commit deployments.",
"main": "main.cjs",
@@ -122,6 +122,7 @@
"docs/RELEASE_NOTES_0.10.11.md",
"docs/RELEASE_NOTES_0.10.12.md",
"docs/RELEASE_NOTES_0.10.13.md",
+ "docs/RELEASE_NOTES_0.10.14.md",
"docs/CURRENT_STATE.md",
"docs/MUTATION_MODEL.md",
"docs/RELEASING.md",
diff --git a/reports/architecture-audit.json b/reports/architecture-audit.json
index 8ef89e3..cedf8d8 100644
--- a/reports/architecture-audit.json
+++ b/reports/architecture-audit.json
@@ -1,5 +1,5 @@
{
- "generatedAt": "2026-08-26T22:18:32.525Z",
+ "generatedAt": "2026-08-26T23:19:01.915Z",
"thresholds": {
"preferredMaximumLines": 750,
"justificationRequiredLines": 1000
@@ -19,6 +19,22 @@
],
"hotspotScore": 154
},
+ {
+ "file": "src/renderer/views.js",
+ "lines": 876,
+ "branches": 61,
+ "functions": 161,
+ "ipcHandlers": 0,
+ "responsibilities": [
+ "inventory",
+ "deployment",
+ "git",
+ "renderer",
+ "security",
+ "updates"
+ ],
+ "hotspotScore": 101
+ },
{
"file": "src/main/update-service.cjs",
"lines": 854,
@@ -31,6 +47,20 @@
"updates"
],
"hotspotScore": 74
+ },
+ {
+ "file": "src/renderer/mock-repository-bridge.js",
+ "lines": 780,
+ "branches": 19,
+ "functions": 100,
+ "ipcHandlers": 0,
+ "responsibilities": [
+ "deployment",
+ "git",
+ "security",
+ "updates"
+ ],
+ "hotspotScore": 39
}
],
"over1000": [],
@@ -51,9 +81,9 @@
},
{
"file": "src/renderer/actions/shell.js",
- "lines": 518,
- "branches": 101,
- "functions": 86,
+ "lines": 531,
+ "branches": 103,
+ "functions": 90,
"ipcHandlers": 0,
"responsibilities": [
"inventory",
@@ -62,11 +92,11 @@
"renderer",
"updates"
],
- "hotspotScore": 131
+ "hotspotScore": 133
},
{
"file": "src/renderer/app.js",
- "lines": 734,
+ "lines": 738,
"branches": 80,
"functions": 124,
"ipcHandlers": 0,
@@ -128,9 +158,9 @@
},
{
"file": "src/renderer/actions/shell.js",
- "lines": 518,
- "branches": 101,
- "functions": 86,
+ "lines": 531,
+ "branches": 103,
+ "functions": 90,
"ipcHandlers": 0,
"responsibilities": [
"inventory",
@@ -139,11 +169,11 @@
"renderer",
"updates"
],
- "hotspotScore": 131
+ "hotspotScore": 133
},
{
"file": "src/renderer/app.js",
- "lines": 734,
+ "lines": 738,
"branches": 80,
"functions": 124,
"ipcHandlers": 0,
@@ -187,9 +217,25 @@
],
"hotspotScore": 106
},
+ {
+ "file": "src/renderer/views.js",
+ "lines": 876,
+ "branches": 61,
+ "functions": 161,
+ "ipcHandlers": 0,
+ "responsibilities": [
+ "inventory",
+ "deployment",
+ "git",
+ "renderer",
+ "security",
+ "updates"
+ ],
+ "hotspotScore": 101
+ },
{
"file": "src/renderer/dialogs.js",
- "lines": 433,
+ "lines": 435,
"branches": 60,
"functions": 83,
"ipcHandlers": 0,
@@ -234,22 +280,6 @@
],
"hotspotScore": 94
},
- {
- "file": "src/renderer/views.js",
- "lines": 736,
- "branches": 53,
- "functions": 152,
- "ipcHandlers": 0,
- "responsibilities": [
- "inventory",
- "deployment",
- "git",
- "renderer",
- "security",
- "updates"
- ],
- "hotspotScore": 93
- },
{
"file": "src/main/ssh-service.cjs",
"lines": 513,
@@ -418,9 +448,9 @@
},
{
"file": "src/renderer/events.js",
- "lines": 181,
- "branches": 35,
- "functions": 27,
+ "lines": 189,
+ "branches": 37,
+ "functions": 28,
"ipcHandlers": 0,
"responsibilities": [
"inventory",
@@ -429,7 +459,7 @@
"renderer",
"security"
],
- "hotspotScore": 65
+ "hotspotScore": 67
},
{
"file": "src/main/git-validator-service.cjs",
@@ -640,6 +670,20 @@
],
"hotspotScore": 42
},
+ {
+ "file": "src/renderer/mock-repository-bridge.js",
+ "lines": 780,
+ "branches": 19,
+ "functions": 100,
+ "ipcHandlers": 0,
+ "responsibilities": [
+ "deployment",
+ "git",
+ "security",
+ "updates"
+ ],
+ "hotspotScore": 39
+ },
{
"file": "src/renderer/mock-bridge.js",
"lines": 590,
@@ -654,20 +698,6 @@
],
"hotspotScore": 37
},
- {
- "file": "src/renderer/mock-repository-bridge.js",
- "lines": 701,
- "branches": 14,
- "functions": 91,
- "ipcHandlers": 0,
- "responsibilities": [
- "deployment",
- "git",
- "security",
- "updates"
- ],
- "hotspotScore": 34
- },
{
"file": "src/main/deployment-identity.cjs",
"lines": 36,
diff --git a/reports/architecture-audit.md b/reports/architecture-audit.md
index 3a2202d..dae9892 100644
--- a/reports/architecture-audit.md
+++ b/reports/architecture-audit.md
@@ -1,13 +1,15 @@
# ForgeFlow architecture audit
-Generated 2026-08-26T22:18:32.525Z. Complexity is a deterministic decision-point count used for hotspot ranking, not a claim of exact McCabe complexity.
+Generated 2026-08-26T23:19:01.915Z. Complexity is a deterministic decision-point count used for hotspot ranking, not a claim of exact McCabe complexity.
## Files above 750 lines
| File | Lines | Decisions | Functions | IPC handlers | Responsibilities |
|---|---:|---:|---:|---:|---|
| `src/main/git-service.cjs` | 881 | 134 | 147 | 0 | git, renderer, security, updates |
+| `src/renderer/views.js` | 876 | 61 | 161 | 0 | inventory, deployment, git, renderer, security, updates |
| `src/main/update-service.cjs` | 854 | 64 | 65 | 0 | git, security, updates |
+| `src/renderer/mock-repository-bridge.js` | 780 | 19 | 100 | 0 | deployment, git, security, updates |
## Files above 1,000 lines
@@ -20,8 +22,8 @@ No findings.
| File | Lines | Decisions | Functions | IPC handlers | Responsibilities |
|---|---:|---:|---:|---:|---|
| `src/main/git-service.cjs` | 881 | 134 | 147 | 0 | git, renderer, security, updates |
-| `src/renderer/actions/shell.js` | 518 | 101 | 86 | 0 | inventory, deployment, git, renderer, updates |
-| `src/renderer/app.js` | 734 | 80 | 124 | 0 | inventory, deployment, git, renderer, security, updates |
+| `src/renderer/actions/shell.js` | 531 | 103 | 90 | 0 | inventory, deployment, git, renderer, updates |
+| `src/renderer/app.js` | 738 | 80 | 124 | 0 | inventory, deployment, git, renderer, security, updates |
| `src/main/server-inventory.cjs` | 578 | 89 | 104 | 0 | inventory, deployment, git, security, updates |
| `src/main/unraid-inventory-methods.cjs` | 710 | 76 | 93 | 0 | inventory, deployment, git, security, updates |
diff --git a/scripts/verify.mjs b/scripts/verify.mjs
index 18b036f..8aaea78 100644
--- a/scripts/verify.mjs
+++ b/scripts/verify.mjs
@@ -95,6 +95,7 @@ const required = [
"docs/RELEASE_NOTES_0.10.11.md",
"docs/RELEASE_NOTES_0.10.12.md",
"docs/RELEASE_NOTES_0.10.13.md",
+ "docs/RELEASE_NOTES_0.10.14.md",
"docs/UPDATING.md",
"docs/DIAGNOSTICS.md",
"docs/DEPLOYMENT_SETUP.md",
@@ -134,9 +135,9 @@ for (const file of required) await access(path.join(root, file));
const packageJson = JSON.parse(
await readFile(path.join(root, "package.json"), "utf8"),
);
-if (packageJson.version !== "0.10.13")
+if (packageJson.version !== "0.10.14")
throw new Error(
- `Expected package version 0.10.13, got ${packageJson.version}.`,
+ `Expected package version 0.10.14, got ${packageJson.version}.`,
);
const sourceManifest = await readFile(
path.join(root, "SOURCE_MANIFEST.txt"),
@@ -502,6 +503,10 @@ const release01013 = await readFile(path.join(root, "docs/RELEASE_NOTES_0.10.13.
for (const phrase of ["Gitea workspace sync", "recovery branch", "Stale deployment links", "Ed25519-signed release manifest", "Git-toolsgrid"]) {
if (!release01013.includes(phrase)) throw new Error(`0.10.13 release notes are missing: ${phrase}`);
}
+const release01014 = await readFile(path.join(root, "docs/RELEASE_NOTES_0.10.14.md"), "utf8");
+for (const phrase of ["Help center", "Gitea workspace sync", "repository context", "horizontal tab navigation", "84 browser flows"]) {
+ if (!release01014.includes(phrase)) throw new Error(`0.10.14 release notes are missing: ${phrase}`);
+}
const configSource = await readFile(path.join(root, "src/main/config-store.cjs"), "utf8");
for (const mode of ["server-git", "push-bundle", "monitor-only"]) {
if (!configSource.includes(mode)) throw new Error(`Deployment configuration is missing mode: ${mode}`);
diff --git a/src/renderer/actions/shell.js b/src/renderer/actions/shell.js
index 604eae7..4dc8a12 100644
--- a/src/renderer/actions/shell.js
+++ b/src/renderer/actions/shell.js
@@ -8,6 +8,19 @@ async function handleShellActions(event, target, action, repository) {
await refreshDeploymentTruth(true);
setLoading(false);
}
+ } else if (action === "open-context-help" || action === "help-topic") {
+ ui.helpTopic = target.dataset.topic || "getting-started";
+ ui.helpQuery = "";
+ ui.currentView = "help";
+ ui.modal = null;
+ render();
+ requestAnimationFrame(() =>
+ document.querySelector(`[data-help-topic="${ui.helpTopic}"]`)?.scrollIntoView({ block: "start", behavior: "smooth" }),
+ );
+ } else if (action === "clear-help-search") {
+ ui.helpQuery = "";
+ render();
+ requestAnimationFrame(() => document.querySelector("#help-search")?.focus());
} else if (action === "select-repo") selectRepository(target.dataset.id);
else if (action === "open-deployment-link") {
if (!repository) return true;
diff --git a/src/renderer/app.js b/src/renderer/app.js
index ef24e01..cfaf45b 100644
--- a/src/renderer/app.js
+++ b/src/renderer/app.js
@@ -59,6 +59,8 @@ const icons = {
'',
wrench:
'',
+ help:
+ '',
};
function icon(name, className = "") {
@@ -183,6 +185,8 @@ const ui = {
deploymentTruthPromise: null,
deploymentTruthRequest: null,
paletteQuery: "",
+ helpQuery: "",
+ helpTopic: "getting-started",
updateStatus: null,
updateChecking: false,
servers: [],
diff --git a/src/renderer/dialogs.js b/src/renderer/dialogs.js
index 4a1be1e..586b760 100644
--- a/src/renderer/dialogs.js
+++ b/src/renderer/dialogs.js
@@ -404,6 +404,8 @@ function render() {
? renderOverview()
: ui.currentView === "deployments"
? renderDeployments()
+ : ui.currentView === "help"
+ ? renderHelp()
: ui.currentView === "settings"
? renderSettings()
: ui.currentView === "diagnostics"
diff --git a/src/renderer/events.js b/src/renderer/events.js
index a2b540a..2fb6aa5 100644
--- a/src/renderer/events.js
+++ b/src/renderer/events.js
@@ -51,6 +51,9 @@ app.addEventListener("input", (event) => {
else if (event.target.id === "palette-input") {
ui.paletteQuery = event.target.value;
scheduleInputRender(60);
+ } else if (event.target.id === "help-search") {
+ ui.helpQuery = event.target.value;
+ scheduleInputRender(60);
}
});
@@ -94,6 +97,11 @@ document.addEventListener("keydown", (event) => {
render();
return;
}
+ if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "f" && ui.currentView === "help") {
+ event.preventDefault();
+ document.querySelector("#help-search")?.focus();
+ return;
+ }
if (
(event.ctrlKey || event.metaKey) &&
event.key === "Enter" &&
diff --git a/src/renderer/mock-repository-bridge.js b/src/renderer/mock-repository-bridge.js
index b130d7c..ae3a097 100644
--- a/src/renderer/mock-repository-bridge.js
+++ b/src/renderer/mock-repository-bridge.js
@@ -5,7 +5,7 @@ function createMockRepositoryBridge(context) {
await wait(80);
snapshot();
return {
- appVersion: "0.10.13-demo",
+ appVersion: "0.10.14-demo",
platform: "win32",
state: clone(state),
git: { available: true, version: "git version 2.47.3" },
diff --git a/src/renderer/styles.css b/src/renderer/styles.css
index 3ebdccb..ec00243 100644
--- a/src/renderer/styles.css
+++ b/src/renderer/styles.css
@@ -823,7 +823,11 @@ select:focus-visible {
height: 100%;
min-height: 0;
display: grid;
- grid-template-rows: auto auto auto 39px minmax(0, 1fr);
+ grid-template-rows: auto auto 39px minmax(0, 1fr);
+}
+.repo-context {
+ min-width: 0;
+ min-height: 0;
}
.repo-header {
padding: 16px 18px 13px;
@@ -995,8 +999,14 @@ select:focus-visible {
padding: 0 12px;
border-bottom: 1px solid var(--line);
background: var(--surface-1);
+ min-width: 0;
+ overflow-x: auto;
+ overflow-y: hidden;
+ scrollbar-width: thin;
}
.tab {
+ flex: 0 0 auto;
+ white-space: nowrap;
height: 38px;
padding: 0 12px;
background: transparent;
@@ -4026,3 +4036,301 @@ html[data-theme="light"] .visual-page-header {
display: none;
}
}
+
+/* Help center and compact repository-shell safeguards */
+.panel-header-actions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.help-page {
+ max-width: 1500px;
+}
+.help-hero {
+ position: relative;
+ isolation: isolate;
+ display: flex;
+ justify-content: space-between;
+ gap: 32px;
+ overflow: hidden;
+ margin-bottom: 24px;
+ padding: 30px 34px;
+ border: 1px solid var(--line);
+ border-radius: 18px;
+ background:
+ radial-gradient(circle at 78% 15%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 32%),
+ linear-gradient(125deg, color-mix(in srgb, var(--surface-2) 92%, transparent), var(--surface-1));
+ box-shadow: 0 22px 65px -48px color-mix(in srgb, var(--accent) 65%, transparent);
+}
+.help-hero::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ z-index: -1;
+ pointer-events: none;
+ background-image: radial-gradient(color-mix(in srgb, var(--text-faint) 20%, transparent) 0.8px, transparent 0.8px);
+ background-size: 18px 18px;
+ mask-image: linear-gradient(90deg, transparent, black 65%, transparent);
+ opacity: 0.42;
+}
+.help-hero > div:first-child {
+ flex: 1;
+ min-width: 0;
+ max-width: 750px;
+}
+.help-hero h1 {
+ margin: 8px 0 6px;
+ font-size: clamp(28px, 3vw, 42px);
+ line-height: 1.1;
+ letter-spacing: -0.045em;
+}
+.help-hero p {
+ max-width: 680px;
+ margin: 0;
+ color: var(--text-muted);
+ font-size: 13px;
+ line-height: 1.6;
+}
+.help-search {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 10px;
+ max-width: 680px;
+ margin-top: 22px;
+ padding: 0 12px;
+ border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--line));
+ border-radius: 11px;
+ background: color-mix(in srgb, var(--surface-0) 90%, transparent);
+ box-shadow: 0 12px 36px -30px var(--accent);
+}
+.help-search:focus-within {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent);
+}
+.help-search > .icon {
+ color: var(--primary);
+}
+.help-search input {
+ min-width: 0;
+ height: 46px;
+ border: 0;
+ outline: 0;
+ background: transparent;
+ color: var(--text);
+}
+.help-search kbd {
+ padding: 3px 6px;
+ border: 1px solid var(--line);
+ border-radius: 5px;
+ background: var(--surface-2);
+ color: var(--text-faint);
+ font: 10px var(--font-mono);
+}
+.help-layout {
+ display: grid;
+ grid-template-columns: minmax(190px, 240px) minmax(0, 1fr);
+ align-items: start;
+ gap: 20px;
+}
+.help-navigation {
+ position: sticky;
+ top: 0;
+ display: grid;
+ gap: 16px;
+ padding: 18px;
+ border: 1px solid var(--line);
+ border-radius: 14px;
+ background: color-mix(in srgb, var(--surface-1) 94%, transparent);
+}
+.help-category {
+ display: grid;
+ gap: 3px;
+}
+.help-category > strong {
+ padding: 4px 8px;
+ color: var(--text-faint);
+ font-size: 10px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+.help-category button {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px;
+ border-radius: 8px;
+ background: transparent;
+ color: var(--text-muted);
+ cursor: pointer;
+ text-align: left;
+ font-size: 11px;
+ line-height: 1.3;
+}
+.help-category button:hover,
+.help-category button.active {
+ background: color-mix(in srgb, var(--accent) 10%, var(--surface-2));
+ color: var(--text);
+}
+.help-category button.active {
+ box-shadow: inset 2px 0 var(--primary);
+}
+.help-category button .icon {
+ flex: 0 0 auto;
+ width: 15px;
+ height: 15px;
+ color: var(--primary);
+}
+.help-results {
+ min-width: 0;
+}
+.help-results-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: end;
+ min-height: 48px;
+ margin-bottom: 10px;
+}
+.help-results-header h2 {
+ margin: 0 0 4px;
+ font-size: 18px;
+}
+.help-results-header span {
+ color: var(--text-faint);
+ font-size: 11px;
+}
+.help-topic {
+ overflow: hidden;
+ margin-bottom: 10px;
+ border: 1px solid var(--line);
+ border-radius: 13px;
+ background: var(--surface-1);
+ transition: border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease;
+ scroll-margin-top: 14px;
+}
+.help-topic:hover {
+ border-color: color-mix(in srgb, var(--accent) 34%, var(--line));
+}
+.help-topic[open] {
+ border-color: color-mix(in srgb, var(--accent) 26%, var(--line));
+ box-shadow: 0 18px 42px -40px var(--accent);
+}
+.help-topic summary {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 14px;
+ padding: 16px 18px;
+ cursor: pointer;
+ list-style: none;
+}
+.help-topic summary::-webkit-details-marker {
+ display: none;
+}
+.help-topic summary > span:nth-child(2) {
+ display: grid;
+ gap: 3px;
+ min-width: 0;
+}
+.help-topic summary small {
+ color: var(--primary);
+ font-size: 9px;
+ font-weight: 750;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+.help-topic summary strong {
+ font-size: 14px;
+}
+.help-topic summary span span {
+ overflow: hidden;
+ color: var(--text-muted);
+ font-size: 11px;
+ line-height: 1.45;
+ text-overflow: ellipsis;
+}
+.help-topic-icon {
+ display: grid;
+ place-items: center;
+ width: 38px;
+ height: 38px;
+ border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--line));
+ border-radius: 11px;
+ background: color-mix(in srgb, var(--accent) 9%, var(--surface-2));
+ color: var(--primary);
+}
+.help-topic-icon .icon {
+ width: 19px;
+ height: 19px;
+}
+.help-chevron {
+ color: var(--text-faint);
+ transition: transform 150ms ease;
+}
+.help-topic[open] .help-chevron {
+ transform: rotate(90deg);
+}
+.help-topic-body {
+ padding: 0 18px 18px 70px;
+ border-top: 1px solid var(--line-soft);
+}
+.help-topic-body ol {
+ display: grid;
+ gap: 11px;
+ margin: 16px 0;
+ padding-left: 22px;
+ color: var(--text-muted);
+ font-size: 12px;
+ line-height: 1.55;
+}
+.help-topic-body li::marker {
+ color: var(--primary);
+ font-family: var(--font-mono);
+ font-weight: 700;
+}
+.help-note {
+ display: flex;
+ align-items: flex-start;
+ gap: 9px;
+ padding: 11px 12px;
+ border: 1px solid color-mix(in srgb, var(--success) 20%, var(--line));
+ border-radius: 9px;
+ background: color-mix(in srgb, var(--success) 6%, var(--surface-0));
+ color: var(--text-muted);
+ font-size: 11px;
+ line-height: 1.5;
+}
+.help-note .icon {
+ flex: 0 0 auto;
+ color: var(--success);
+}
+
+@media (max-width: 900px) {
+ .help-layout {
+ grid-template-columns: 1fr;
+ }
+ .help-navigation {
+ position: static;
+ display: none;
+ }
+ .help-hero .project-illustration {
+ display: none;
+ }
+}
+@media (max-width: 680px) {
+ .help-hero {
+ padding: 22px;
+ }
+ .help-search kbd {
+ display: none;
+ }
+ .help-search {
+ grid-template-columns: auto minmax(0, 1fr);
+ }
+ .help-topic-body {
+ padding-left: 18px;
+ }
+}
diff --git a/src/renderer/views.js b/src/renderer/views.js
index b1186c1..374839b 100644
--- a/src/renderer/views.js
+++ b/src/renderer/views.js
@@ -15,6 +15,7 @@ function renderTitlebar() {
deployments: "Deployments",
diagnostics: "Diagnostics",
settings: "Settings",
+ help: "Help center",
"deployment-run": "Deployment run",
}[ui.currentView] || "Workspace";
return `
@@ -87,7 +88,7 @@ function renderSidebar() {
).length;
const rows = (list) => list.map(renderRepositoryRow).join("");
return `