FN-6534: track docs screenshots
Restore the screenshot assets that published docs reference so rendered documentation no longer shows broken images. - Keep docs/screenshots trackable by removing the blanket ignore rule. - Add the referenced screenshot PNG assets under docs/screenshots. - Add a regression test that verifies screenshot Markdown links resolve to tracked files. Files changed: .gitignore | 2 +- docs/screenshots/agents-view.png | Bin 0 -> 88902 bytes docs/screenshots/chat-view.png | Bin 0 -> 74669 bytes docs/screenshots/dashboard-overview.png | Bin 0 -> 209599 bytes docs/screenshots/documents-view.png | Bin 0 -> 37607 bytes docs/screenshots/git-manager.png | Bin 0 -> 135486 bytes docs/screenshots/list-view.png | Bin 0 -> 108702 bytes docs/screenshots/mailbox-view.png | Bin 0 -> 72370 bytes docs/screenshots/memory-view.png | Bin 0 -> 118122 bytes docs/screenshots/mission-manager.png | Bin 0 -> 80643 bytes docs/screenshots/nodes-view.png | Bin 0 -> 59576 bytes docs/screenshots/roadmaps-view.png | Bin 0 -> 37828 bytes docs/screenshots/settings.png | Bin 0 -> 131052 bytes docs/screenshots/skills-view.png | Bin 0 -> 92259 bytes docs/screenshots/task-detail.png | Bin 0 -> 171502 bytes docs/screenshots/terminal.png | Bin 0 -> 70273 bytes docs/screenshots/workflow-steps.png | Bin 0 -> 179100 bytes .../src/__tests__/docs-screenshot-links.test.ts | 96 +++++++++++++++++++++ 18 files changed, 97 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-6534 Fusion-Task-Lineage: a0840905-8eb5-4da0-8c52-56904ac82e04
2
.gitignore
vendored
@@ -65,7 +65,7 @@ homebrew-tap/
|
||||
# never meant to be committed.
|
||||
.DONE
|
||||
improvements.md
|
||||
docs/screenshots/
|
||||
# FNXC:RepoHygiene 2026-06-17-00:38: Published Markdown docs embed `docs/screenshots/*.png`; keep that directory trackable so GitHub, npm-rendered docs, and fresh clones do not show broken images.
|
||||
.claude/
|
||||
|
||||
# Local kb state and backups
|
||||
|
||||
BIN
docs/screenshots/agents-view.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
docs/screenshots/chat-view.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
docs/screenshots/dashboard-overview.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
docs/screenshots/documents-view.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
docs/screenshots/git-manager.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
docs/screenshots/list-view.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
docs/screenshots/mailbox-view.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
docs/screenshots/memory-view.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
docs/screenshots/mission-manager.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/screenshots/nodes-view.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
docs/screenshots/roadmaps-view.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
docs/screenshots/settings.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
docs/screenshots/skills-view.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
docs/screenshots/task-detail.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
docs/screenshots/terminal.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
docs/screenshots/workflow-steps.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
96
packages/cli/src/__tests__/docs-screenshot-links.test.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { dirname, relative, resolve, sep } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const workspaceRoot = resolve(import.meta.dirname, "../../../..");
|
||||
const docsRoot = resolve(workspaceRoot, "docs");
|
||||
|
||||
/*
|
||||
FNXC:DocsScreenshots 2026-06-17-00:38:
|
||||
Published docs render on GitHub and in fresh clones, so screenshot image references must resolve to committed files, not only developer-local files that happen to exist on disk.
|
||||
Assert both filesystem presence and `git ls-files` tracking so a gitignored-but-present `docs/screenshots/` directory cannot regress silently.
|
||||
*/
|
||||
|
||||
function collectMarkdownFiles(directory: string): string[] {
|
||||
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
||||
const entryPath = resolve(directory, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
return collectMarkdownFiles(entryPath);
|
||||
}
|
||||
if (entry.isFile() && entry.name.endsWith(".md")) {
|
||||
return [entryPath];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
function toRepoRelativePath(absolutePath: string): string {
|
||||
return relative(workspaceRoot, absolutePath).split(sep).join("/");
|
||||
}
|
||||
|
||||
function gitTracks(relativePath: string): boolean {
|
||||
const output = execFileSync("git", ["ls-files", "--", relativePath], {
|
||||
cwd: workspaceRoot,
|
||||
encoding: "utf8",
|
||||
}).trim();
|
||||
return output.length > 0;
|
||||
}
|
||||
|
||||
describe("docs screenshot links", () => {
|
||||
it("points every screenshot image reference at an existing tracked asset", () => {
|
||||
const markdownFiles = [...collectMarkdownFiles(docsRoot), resolve(workspaceRoot, "README.md")];
|
||||
const screenshotReferences: Array<{ source: string; target: string; resolvedPath: string; repoPath: string }> = [];
|
||||
|
||||
for (const markdownFile of markdownFiles) {
|
||||
const markdown = readFileSync(markdownFile, "utf8");
|
||||
const imagePattern = /!\[[^\]]*\]\(([^)]+)\)/g;
|
||||
for (const match of markdown.matchAll(imagePattern)) {
|
||||
const rawTarget = match[1]?.trim().replace(/^<|>$/g, "") ?? "";
|
||||
const targetWithoutTitle = rawTarget.split(/\s+/)[0] ?? "";
|
||||
const targetWithoutFragment = targetWithoutTitle.replace(/[?#].*$/, "");
|
||||
if (!/(?:^|\/)screenshots\/[^/]+\.png$/i.test(targetWithoutFragment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const resolvedPath = resolve(dirname(markdownFile), targetWithoutFragment);
|
||||
screenshotReferences.push({
|
||||
source: toRepoRelativePath(markdownFile),
|
||||
target: targetWithoutTitle,
|
||||
resolvedPath,
|
||||
repoPath: toRepoRelativePath(resolvedPath),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
expect(screenshotReferences.map(({ repoPath }) => repoPath).sort()).toEqual([
|
||||
"docs/screenshots/agents-view.png",
|
||||
"docs/screenshots/chat-view.png",
|
||||
"docs/screenshots/dashboard-overview.png",
|
||||
"docs/screenshots/dashboard-overview.png",
|
||||
"docs/screenshots/dashboard-overview.png",
|
||||
"docs/screenshots/documents-view.png",
|
||||
"docs/screenshots/git-manager.png",
|
||||
"docs/screenshots/list-view.png",
|
||||
"docs/screenshots/mailbox-view.png",
|
||||
"docs/screenshots/memory-view.png",
|
||||
"docs/screenshots/mission-manager.png",
|
||||
"docs/screenshots/nodes-view.png",
|
||||
"docs/screenshots/skills-view.png",
|
||||
"docs/screenshots/task-detail.png",
|
||||
"docs/screenshots/task-detail.png",
|
||||
"docs/screenshots/terminal.png",
|
||||
"docs/screenshots/workflow-steps.png",
|
||||
]);
|
||||
|
||||
const missingFiles = screenshotReferences
|
||||
.filter(({ resolvedPath }) => !existsSync(resolvedPath))
|
||||
.map(({ source, target, repoPath }) => `${source} -> ${target} (${repoPath})`);
|
||||
const untrackedFiles = screenshotReferences
|
||||
.filter(({ repoPath }) => !gitTracks(repoPath))
|
||||
.map(({ source, target, repoPath }) => `${source} -> ${target} (${repoPath})`);
|
||||
|
||||
expect(missingFiles).toEqual([]);
|
||||
expect(untrackedFiles).toEqual([]);
|
||||
});
|
||||
});
|
||||