@doxbrix/doxloop 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1153 -0
- package/README.md +422 -226
- package/assets/doxbrix-preview.css +77 -15
- package/contracts/agent-events-v1.schema.json +1 -0
- package/contracts/coverage-v1.schema.json +59 -0
- package/contracts/drift-v1.schema.json +1 -0
- package/contracts/evaluation-v1.schema.json +62 -0
- package/contracts/quality-codes-v1.json +1 -0
- package/contracts/quality-report-v1.schema.json +1 -0
- package/contracts/validation-v1.schema.json +1 -0
- package/dist/agent-failure.d.ts +36 -0
- package/dist/agent-failure.js +98 -0
- package/dist/agent-isolation.d.ts +17 -0
- package/dist/agent-isolation.js +94 -0
- package/dist/agent-log.d.ts +197 -0
- package/dist/agent-log.js +1094 -0
- package/dist/agent-process.d.ts +63 -0
- package/dist/agent-process.js +128 -0
- package/dist/agent-reply.d.ts +47 -0
- package/dist/agent-reply.js +315 -0
- package/dist/agents.d.ts +40 -4
- package/dist/agents.js +115 -11
- package/dist/api-coverage.d.ts +11 -0
- package/dist/api-coverage.js +72 -0
- package/dist/application-probe.d.ts +28 -0
- package/dist/application-probe.js +93 -0
- package/dist/application-url.d.ts +14 -0
- package/dist/application-url.js +26 -0
- package/dist/args.js +15 -0
- package/dist/artifact-deploy.d.ts +9 -0
- package/dist/artifact-deploy.js +28 -8
- package/dist/assets.d.ts +72 -0
- package/dist/assets.js +382 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.js +38 -9
- package/dist/author.d.ts +206 -4
- package/dist/author.js +1650 -52
- package/dist/authoring-batches.d.ts +190 -0
- package/dist/authoring-batches.js +489 -0
- package/dist/authoring-postpass.d.ts +79 -0
- package/dist/authoring-postpass.js +1289 -0
- package/dist/authoring-progress.d.ts +95 -0
- package/dist/authoring-progress.js +269 -0
- package/dist/autosync.d.ts +56 -0
- package/dist/autosync.js +442 -0
- package/dist/batch-artifacts.d.ts +81 -0
- package/dist/batch-artifacts.js +412 -0
- package/dist/batch-limits.d.ts +37 -0
- package/dist/batch-limits.js +80 -0
- package/dist/branding.d.ts +41 -0
- package/dist/branding.js +229 -0
- package/dist/bundle-upload.d.ts +26 -0
- package/dist/bundle-upload.js +99 -0
- package/dist/capture-auth.d.ts +95 -0
- package/dist/capture-auth.js +204 -0
- package/dist/capture.d.ts +48 -0
- package/dist/capture.js +20 -1
- package/dist/cli.js +839 -32
- package/dist/content-links.d.ts +10 -0
- package/dist/content-links.js +49 -0
- package/dist/contract-validation.d.ts +4 -0
- package/dist/contract-validation.js +25 -0
- package/dist/coverage-actions.d.ts +8 -0
- package/dist/coverage-actions.js +71 -0
- package/dist/coverage-resolutions.d.ts +16 -0
- package/dist/coverage-resolutions.js +58 -0
- package/dist/db.d.ts +37 -0
- package/dist/db.js +288 -0
- package/dist/demo.d.ts +12 -0
- package/dist/demo.js +122 -0
- package/dist/deploy-credentials.d.ts +7 -0
- package/dist/deploy-credentials.js +76 -0
- package/dist/deploy-targets/github-pages.d.ts +3 -0
- package/dist/deploy-targets/github-pages.js +100 -0
- package/dist/deploy-targets/index.d.ts +8 -0
- package/dist/deploy-targets/index.js +78 -0
- package/dist/deploy-targets/netlify.d.ts +3 -0
- package/dist/deploy-targets/netlify.js +49 -0
- package/dist/deploy-targets/types.d.ts +32 -0
- package/dist/deploy-targets/types.js +2 -0
- package/dist/deploy-targets/vercel.d.ts +3 -0
- package/dist/deploy-targets/vercel.js +95 -0
- package/dist/deploy.d.ts +9 -0
- package/dist/deploy.js +87 -13
- package/dist/deterministic-capture.d.ts +90 -0
- package/dist/deterministic-capture.js +435 -0
- package/dist/direct-edit.d.ts +40 -0
- package/dist/direct-edit.js +159 -0
- package/dist/docs-crawl.d.ts +86 -0
- package/dist/docs-crawl.js +536 -0
- package/dist/docs-site.d.ts +49 -0
- package/dist/docs-site.js +233 -0
- package/dist/doctor.js +8 -0
- package/dist/documentation-collections.d.ts +23 -0
- package/dist/documentation-collections.js +191 -0
- package/dist/documentation-plan.d.ts +253 -0
- package/dist/documentation-plan.js +2563 -0
- package/dist/doxbrix-build.d.ts +19 -0
- package/dist/doxbrix-build.js +167 -0
- package/dist/doxbrix-markdown.d.ts +9 -0
- package/dist/doxbrix-markdown.js +115 -14
- package/dist/drift.d.ts +10 -0
- package/dist/drift.js +164 -0
- package/dist/evaluation.d.ts +46 -0
- package/dist/evaluation.js +113 -0
- package/dist/evidence-pack.d.ts +47 -0
- package/dist/evidence-pack.js +358 -0
- package/dist/evidence.d.ts +25 -0
- package/dist/evidence.js +175 -0
- package/dist/fs.d.ts +8 -2
- package/dist/fs.js +41 -11
- package/dist/generator-api.d.ts +89 -0
- package/dist/generator-preflight.d.ts +27 -0
- package/dist/generator-preflight.js +105 -0
- package/dist/generator-runtime.d.ts +7 -0
- package/dist/generator-runtime.js +17 -1
- package/dist/generators.d.ts +24 -2
- package/dist/generators.js +54 -1
- package/dist/git-delivery.d.ts +17 -0
- package/dist/git-delivery.js +123 -0
- package/dist/globs.d.ts +16 -0
- package/dist/globs.js +65 -0
- package/dist/glossary.d.ts +26 -0
- package/dist/glossary.js +179 -0
- package/dist/history.d.ts +106 -0
- package/dist/history.js +600 -0
- package/dist/html-markdown.d.ts +46 -0
- package/dist/html-markdown.js +423 -0
- package/dist/interactive.js +16 -15
- package/dist/job-events.d.ts +74 -0
- package/dist/job-events.js +377 -0
- package/dist/keep-awake.d.ts +50 -0
- package/dist/keep-awake.js +123 -0
- package/dist/local-source-snapshot.d.ts +20 -0
- package/dist/local-source-snapshot.js +61 -0
- package/dist/mintlify-detect.d.ts +3 -0
- package/dist/mintlify-detect.js +18 -0
- package/dist/mintlify-import.d.ts +75 -0
- package/dist/mintlify-import.js +190 -0
- package/dist/navigation.d.ts +98 -0
- package/dist/navigation.js +310 -0
- package/dist/openapi.d.ts +60 -0
- package/dist/openapi.js +439 -0
- package/dist/page-editor-bridge.d.ts +3 -0
- package/dist/page-editor-bridge.js +109 -0
- package/dist/page-editor-preview.d.ts +10 -0
- package/dist/page-editor-preview.js +55 -0
- package/dist/page-extension.d.ts +9 -0
- package/dist/page-extension.js +15 -0
- package/dist/page-metadata.d.ts +28 -0
- package/dist/page-metadata.js +166 -0
- package/dist/page-operations.d.ts +34 -0
- package/dist/page-operations.js +215 -0
- package/dist/page-routes.d.ts +4 -0
- package/dist/page-routes.js +61 -0
- package/dist/pages.d.ts +20 -0
- package/dist/pages.js +184 -0
- package/dist/plan-generator.d.ts +3 -0
- package/dist/plan-generator.js +21 -0
- package/dist/plan-navigation.d.ts +11 -0
- package/dist/plan-navigation.js +30 -0
- package/dist/planning-captures.d.ts +20 -0
- package/dist/planning-captures.js +143 -0
- package/dist/planning-research.d.ts +135 -0
- package/dist/planning-research.js +472 -0
- package/dist/planning-triage.d.ts +23 -0
- package/dist/planning-triage.js +131 -0
- package/dist/preview.d.ts +24 -0
- package/dist/preview.js +280 -29
- package/dist/project-detect.d.ts +36 -0
- package/dist/project-detect.js +251 -0
- package/dist/project-import.d.ts +54 -0
- package/dist/project-import.js +157 -0
- package/dist/project-lock.d.ts +6 -0
- package/dist/project-lock.js +96 -0
- package/dist/project-registry.d.ts +25 -0
- package/dist/project-registry.js +79 -0
- package/dist/project.d.ts +35 -3
- package/dist/project.js +388 -38
- package/dist/prompts.d.ts +9 -0
- package/dist/prompts.js +33 -4
- package/dist/proposal-replay.d.ts +32 -0
- package/dist/proposal-replay.js +99 -0
- package/dist/quality-claims.d.ts +8 -0
- package/dist/quality-claims.js +168 -0
- package/dist/quality-config.d.ts +5 -0
- package/dist/quality-config.js +84 -0
- package/dist/quality-contract.d.ts +37 -0
- package/dist/quality-contract.js +46 -0
- package/dist/quality-examples.d.ts +4 -0
- package/dist/quality-examples.js +233 -0
- package/dist/quality-gates.d.ts +16 -0
- package/dist/quality-gates.js +192 -0
- package/dist/quality-links.d.ts +7 -0
- package/dist/quality-links.js +149 -0
- package/dist/quality-lint.d.ts +6 -0
- package/dist/quality-lint.js +124 -0
- package/dist/quality-rendered.d.ts +11 -0
- package/dist/quality-rendered.js +222 -0
- package/dist/quality-schema.d.ts +3 -0
- package/dist/quality-schema.js +71 -0
- package/dist/release-notes.d.ts +44 -0
- package/dist/release-notes.js +183 -0
- package/dist/remote-monitor.d.ts +16 -0
- package/dist/remote-monitor.js +74 -0
- package/dist/remote-source.d.ts +34 -0
- package/dist/remote-source.js +426 -0
- package/dist/review-diff.d.ts +82 -0
- package/dist/review-diff.js +400 -0
- package/dist/review-learning.d.ts +11 -0
- package/dist/review-learning.js +60 -0
- package/dist/review-render.d.ts +38 -0
- package/dist/review-render.js +224 -0
- package/dist/review-report.d.ts +9 -0
- package/dist/review-report.js +89 -0
- package/dist/review-ui.d.ts +14 -0
- package/dist/review-ui.js +1248 -0
- package/dist/schedule.d.ts +78 -0
- package/dist/schedule.js +480 -0
- package/dist/screen-capture-provider.d.ts +52 -0
- package/dist/screen-capture-provider.js +218 -0
- package/dist/screenshot-workflow.d.ts +167 -0
- package/dist/screenshot-workflow.js +1237 -0
- package/dist/settings.d.ts +1 -1
- package/dist/settings.js +95 -7
- package/dist/site-export.d.ts +18 -0
- package/dist/site-export.js +87 -0
- package/dist/source-connectors.d.ts +33 -0
- package/dist/source-connectors.js +268 -0
- package/dist/source-discovery.d.ts +132 -0
- package/dist/source-discovery.js +823 -0
- package/dist/source-intelligence.d.ts +9 -0
- package/dist/source-intelligence.js +306 -0
- package/dist/sync-review.d.ts +28 -0
- package/dist/sync-review.js +264 -0
- package/dist/sync-runs.d.ts +192 -0
- package/dist/sync-runs.js +2244 -0
- package/dist/sync.d.ts +35 -0
- package/dist/sync.js +298 -32
- package/dist/text-diff.d.ts +9 -0
- package/dist/text-diff.js +59 -0
- package/dist/types.d.ts +946 -1
- package/dist/ui/assets/doxloop-logo-light-De7Nx7j7.png +0 -0
- package/dist/ui/assets/index-BHBYU2aG.css +1 -0
- package/dist/ui/assets/index-Cq3RPQiC.js +33 -0
- package/dist/ui/index.html +18 -0
- package/dist/ui-server.d.ts +83 -0
- package/dist/ui-server.js +3532 -0
- package/dist/usage-budget.d.ts +28 -0
- package/dist/usage-budget.js +90 -0
- package/dist/validation.d.ts +25 -1
- package/dist/validation.js +312 -26
- package/dist/workspace-tools.d.ts +54 -0
- package/dist/workspace-tools.js +123 -0
- package/docs/agent-compatibility.md +50 -28
- package/docs/ci-and-automation.md +105 -66
- package/docs/doxbrix-http-api.md +8 -1
- package/docs/existing-documentation.md +80 -0
- package/docs/generation-performance.md +108 -0
- package/docs/generator-authoring.md +68 -5
- package/docs/generator-selection.md +50 -13
- package/docs/mintlify-import.md +71 -0
- package/docs/openapi-security.md +25 -0
- package/docs/project-format.md +331 -36
- package/docs/release-quality.md +158 -0
- package/docs/releasing.md +72 -0
- package/docs/review-workflows.md +51 -0
- package/docs/security-model.md +102 -39
- package/docs/troubleshooting.md +226 -93
- package/package.json +46 -15
- package/scripts/test-auto-screenshot.mjs +172 -0
- package/skills/doxloop-authoring/SKILL.md +242 -361
- package/skills/doxloop-authoring/references/existing-documentation.md +94 -0
- package/skills/doxloop-authoring/references/navigation-architecture.md +18 -7
- package/skills/doxloop-authoring/references/page-depth.md +169 -0
- package/skills/doxloop-authoring/references/project-format.md +106 -7
- package/skills/doxloop-authoring/references/quality.md +10 -0
- package/skills/doxloop-authoring/references/screenshot-manifest.md +113 -0
- package/skills/doxloop-authoring/references/screenshots.md +149 -235
- package/skills/doxloop-authoring/references/workflows.md +76 -0
- package/skills/doxloop-doxbrix/SKILL.md +38 -22
- package/skills/doxloop-doxbrix/references/api-endpoints.md +15 -14
- package/skills/doxloop-doxbrix/references/components.md +30 -3
- package/skills/doxloop-doxbrix/references/manifest.md +3 -2
- package/vendor/doxbrix-import/LICENSE +202 -0
- package/vendor/doxbrix-import/README.md +17 -0
- package/vendor/doxbrix-import/UPSTREAM.json +47 -0
- package/vendor/doxbrix-import/dist/docs/frontmatter.d.ts +13 -0
- package/vendor/doxbrix-import/dist/docs/frontmatter.js +83 -0
- package/vendor/doxbrix-import/dist/docs/import.d.ts +21 -0
- package/vendor/doxbrix-import/dist/docs/import.js +147 -0
- package/vendor/doxbrix-import/dist/docs/manifest.d.ts +163 -0
- package/vendor/doxbrix-import/dist/docs/manifest.js +64 -0
- package/vendor/doxbrix-import/dist/docs/project.d.ts +25 -0
- package/vendor/doxbrix-import/dist/docs/project.js +77 -0
- package/vendor/doxbrix-import/dist/docs/starter.d.ts +4 -0
- package/vendor/doxbrix-import/dist/docs/starter.js +11 -0
- package/vendor/doxbrix-import/dist/importer.d.ts +227 -0
- package/vendor/doxbrix-import/dist/importer.js +1567 -0
- package/vendor/doxbrix-import/dist/mintlify-openapi.d.ts +37 -0
- package/vendor/doxbrix-import/dist/mintlify-openapi.js +305 -0
- package/vendor/doxbrix-import/dist/safe-path.d.ts +9 -0
- package/vendor/doxbrix-import/dist/safe-path.js +47 -0
- package/dist/agents.d.ts.map +0 -1
- package/dist/agents.js.map +0 -1
- package/dist/args.d.ts.map +0 -1
- package/dist/args.js.map +0 -1
- package/dist/artifact-deploy.d.ts.map +0 -1
- package/dist/artifact-deploy.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/author.d.ts.map +0 -1
- package/dist/author.js.map +0 -1
- package/dist/capture.d.ts.map +0 -1
- package/dist/capture.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/deploy.d.ts.map +0 -1
- package/dist/deploy.js.map +0 -1
- package/dist/deployment-visibility.d.ts.map +0 -1
- package/dist/deployment-visibility.js.map +0 -1
- package/dist/doctor.d.ts.map +0 -1
- package/dist/doctor.js.map +0 -1
- package/dist/doxbrix-markdown.d.ts.map +0 -1
- package/dist/doxbrix-markdown.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/fs.d.ts.map +0 -1
- package/dist/fs.js.map +0 -1
- package/dist/generator-api.d.ts.map +0 -1
- package/dist/generator-api.js.map +0 -1
- package/dist/generator-manager.d.ts.map +0 -1
- package/dist/generator-manager.js.map +0 -1
- package/dist/generator-runtime.d.ts.map +0 -1
- package/dist/generator-runtime.js.map +0 -1
- package/dist/generators.d.ts.map +0 -1
- package/dist/generators.js.map +0 -1
- package/dist/interactive.d.ts.map +0 -1
- package/dist/interactive.js.map +0 -1
- package/dist/preview.d.ts.map +0 -1
- package/dist/preview.js.map +0 -1
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js.map +0 -1
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js.map +0 -1
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js.map +0 -1
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the rendered before/after comparison shown inside the review center.
|
|
3
|
+
*
|
|
4
|
+
* Both versions of a page are rendered with the same engine the published site
|
|
5
|
+
* uses, then lined up block by block so a writer can read the proposal as a
|
|
6
|
+
* finished page instead of as a patch.
|
|
7
|
+
*/
|
|
8
|
+
import { renderMarkdown } from './doxbrix-markdown.js';
|
|
9
|
+
import { alignBlocks, escapeHtml, markHtmlWordDiff, splitHtmlBlocks, } from './review-diff.js';
|
|
10
|
+
/** Number of unchanged blocks kept visible on each side of a change. */
|
|
11
|
+
const CONTEXT_BLOCKS = 1;
|
|
12
|
+
export function renderedDiff(options) {
|
|
13
|
+
const before = safeRender(options.before);
|
|
14
|
+
const after = safeRender(options.after);
|
|
15
|
+
const rows = [];
|
|
16
|
+
const headerBefore = pageHeader(options.before);
|
|
17
|
+
const headerAfter = pageHeader(options.after);
|
|
18
|
+
if (headerBefore !== '' || headerAfter !== '') {
|
|
19
|
+
rows.push({
|
|
20
|
+
status: !options.before.exists
|
|
21
|
+
? 'added'
|
|
22
|
+
: !options.after.exists
|
|
23
|
+
? 'removed'
|
|
24
|
+
: headerBefore === headerAfter
|
|
25
|
+
? 'equal'
|
|
26
|
+
: 'changed',
|
|
27
|
+
...(options.before.exists ? { before: headerBefore } : {}),
|
|
28
|
+
...(options.after.exists ? { after: headerAfter } : {}),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
rows.push(...alignBlocks(splitHtmlBlocks(before), splitHtmlBlocks(after)));
|
|
32
|
+
const added = rows.filter((row) => row.status === 'added').length;
|
|
33
|
+
const removed = rows.filter((row) => row.status === 'removed').length;
|
|
34
|
+
const changed = rows.filter((row) => row.status === 'changed').length;
|
|
35
|
+
const hasChanges = added + removed + changed > 0;
|
|
36
|
+
return {
|
|
37
|
+
html: rowGroups(rows, hasChanges).map(groupHtml).join(''),
|
|
38
|
+
added,
|
|
39
|
+
removed,
|
|
40
|
+
changed,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function safeRender(side) {
|
|
44
|
+
if (!side.exists)
|
|
45
|
+
return '';
|
|
46
|
+
try {
|
|
47
|
+
return renderMarkdown(side.body).html;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return `<div class="dp-p rd-render-error">This version could not be rendered: ${escapeHtml(error instanceof Error ? error.message : String(error))}</div>`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function pageHeader(side) {
|
|
54
|
+
if (!side.exists)
|
|
55
|
+
return '';
|
|
56
|
+
const description = side.description
|
|
57
|
+
? `<p class="dp-page-description">${escapeHtml(side.description)}</p>`
|
|
58
|
+
: '';
|
|
59
|
+
return `<h1 class="dp-page-title">${escapeHtml(side.title)}</h1>${description}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Group the aligned rows so long stretches of untouched content collapse into a
|
|
63
|
+
* single expandable strip, keeping the changes close together on screen.
|
|
64
|
+
*/
|
|
65
|
+
function rowGroups(rows, hasChanges) {
|
|
66
|
+
if (!hasChanges)
|
|
67
|
+
return [{ folded: false, rows: [...rows] }];
|
|
68
|
+
const visible = rows.map((row, index) => {
|
|
69
|
+
// The page title always stays on screen so the reader keeps their bearings.
|
|
70
|
+
if (row.status !== 'equal' || index === 0)
|
|
71
|
+
return true;
|
|
72
|
+
return rows.some((other, otherIndex) => other.status !== 'equal' && Math.abs(otherIndex - index) <= CONTEXT_BLOCKS);
|
|
73
|
+
});
|
|
74
|
+
const groups = [];
|
|
75
|
+
for (const [index, row] of rows.entries()) {
|
|
76
|
+
const folded = !visible[index];
|
|
77
|
+
const last = groups.at(-1);
|
|
78
|
+
if (last && last.folded === folded)
|
|
79
|
+
last.rows.push(row);
|
|
80
|
+
else
|
|
81
|
+
groups.push({ folded, rows: [row] });
|
|
82
|
+
}
|
|
83
|
+
// A one-block fold costs more attention than it saves.
|
|
84
|
+
return groups.map((group) => (group.folded && group.rows.length < 2 ? { ...group, folded: false } : group));
|
|
85
|
+
}
|
|
86
|
+
function groupHtml(group) {
|
|
87
|
+
const rows = group.rows.map(rowHtml).join('');
|
|
88
|
+
if (!group.folded)
|
|
89
|
+
return rows;
|
|
90
|
+
const count = group.rows.length;
|
|
91
|
+
return `<details class="rd-fold"><summary class="rd-fold-summary"><span class="rd-fold-line"></span><span class="rd-fold-label">${count} unchanged block${count === 1 ? '' : 's'}</span><span class="rd-fold-line"></span></summary><div class="rd-fold-body">${rows}</div></details>`;
|
|
92
|
+
}
|
|
93
|
+
function rowHtml(row) {
|
|
94
|
+
let before = row.before ?? '';
|
|
95
|
+
let after = row.after ?? '';
|
|
96
|
+
if (row.status === 'changed' && before !== '' && after !== '') {
|
|
97
|
+
const marked = markHtmlWordDiff(before, after);
|
|
98
|
+
before = marked.before;
|
|
99
|
+
after = marked.after;
|
|
100
|
+
}
|
|
101
|
+
return `<div class="rd-row" data-status="${row.status}">${cell('before', before, row.status)}${cell('after', after, row.status)}</div>`;
|
|
102
|
+
}
|
|
103
|
+
function cell(side, html, status) {
|
|
104
|
+
const empty = html === ''
|
|
105
|
+
? `<div class="rd-empty">${side === 'before' ? 'Not in the current page' : 'Removed from the page'}</div>`
|
|
106
|
+
: '';
|
|
107
|
+
// The tag names the side whenever the two columns are stacked and the sticky
|
|
108
|
+
// column headers no longer apply.
|
|
109
|
+
const tag = `<span class="rd-cell-tag">${side === 'before' ? 'Current' : 'Proposed'}</span>`;
|
|
110
|
+
return `<div class="rd-cell rd-cell--${side}" data-status="${status}"><div class="rd-gutter" aria-hidden="true"></div><div class="dp-blocks rd-content">${tag}${html}${empty}</div></div>`;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Document for the comparison frame. It carries no controls of its own — the
|
|
114
|
+
* review screen owns every choice — so the whole frame is documentation.
|
|
115
|
+
*/
|
|
116
|
+
export function renderedDiffDocument(input) {
|
|
117
|
+
return `<!doctype html>
|
|
118
|
+
<html lang="en">
|
|
119
|
+
<head>
|
|
120
|
+
<meta charset="utf-8">
|
|
121
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
122
|
+
<title>Rendered comparison</title>
|
|
123
|
+
<link rel="stylesheet" href="/reader.css">
|
|
124
|
+
<style>${renderedDiffCss()}</style>
|
|
125
|
+
</head>
|
|
126
|
+
<body class="rd-body${input.onlyChanges ? ' rd-only-changes' : ''}" data-mode="${input.layout}" data-theme="${input.theme}">
|
|
127
|
+
${input.notice ? `<div class="rd-notice">${escapeHtml(input.notice)}</div>` : ''}
|
|
128
|
+
<div class="rd-scroll">
|
|
129
|
+
<div class="rd-heads">
|
|
130
|
+
<div class="rd-head rd-head--before"><span class="rd-dot rd-dot--before"></span>Current documentation</div>
|
|
131
|
+
<div class="rd-head rd-head--after"><span class="rd-dot rd-dot--after"></span>Proposed update</div>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="dp-root rd-root" data-color-theme="${input.theme}" data-code-theme="auto">${input.diff.html}</div>
|
|
134
|
+
${input.diff.added + input.diff.removed + input.diff.changed === 0
|
|
135
|
+
? '<div class="rd-none">Nothing in the rendered page changed. The difference is in the file itself — open the source diff to see it.</div>'
|
|
136
|
+
: ''}
|
|
137
|
+
</div>
|
|
138
|
+
</body>
|
|
139
|
+
</html>`;
|
|
140
|
+
}
|
|
141
|
+
function renderedDiffCss() {
|
|
142
|
+
return `
|
|
143
|
+
:root{--rv-accent:#0B7F7B;--rv-border:#E1E5ED;--rv-text:#193B73;--rv-heading:#032F7B;--rv-muted:#647392;--rv-added:#16794B;--rv-added-soft:#F0FAF4;--rv-added-line:#C7EAD5;--rv-removed:#B4233F;--rv-removed-soft:#FFF5F6;--rv-removed-line:#F3CDD4;--rv-changed:#865A00;--rv-changed-line:#EEDCA9;--rv-page:#F7F8FB;color-scheme:light}
|
|
144
|
+
*{box-sizing:border-box}
|
|
145
|
+
html{scroll-behavior:smooth}
|
|
146
|
+
body.rd-body{margin:0;background:var(--rv-page);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;color:var(--rv-text);-webkit-font-smoothing:antialiased}
|
|
147
|
+
body.rd-body[data-theme='dark']{--rv-border:#334155;--rv-text:#cbd5e1;--rv-heading:#f8fafc;--rv-muted:#94a3b8;--rv-added:#6ee7a8;--rv-added-soft:#102a20;--rv-added-line:#245c43;--rv-removed:#fda4b4;--rv-removed-soft:#32151c;--rv-removed-line:#713141;--rv-changed:#f5cf75;--rv-changed-line:#705a25;--rv-page:#0f172a;color-scheme:dark}
|
|
148
|
+
body.rd-body[data-theme='dark'] .rd-none,body.rd-body[data-theme='dark'] .rd-head,body.rd-body[data-theme='dark'] .rd-cell{background:#172033}
|
|
149
|
+
.rd-notice{margin:16px 18px 0;padding:10px 14px;border-radius:7px;background:#FFFAEB;border:1px solid var(--rv-changed-line);color:var(--rv-changed);font-size:12.5px;font-weight:600}
|
|
150
|
+
.rd-none{margin:18px auto;max-width:560px;padding:22px;text-align:center;border:1px solid var(--rv-border);border-radius:8px;color:var(--rv-muted);font-size:13px;background:#fff}
|
|
151
|
+
|
|
152
|
+
.rd-scroll{padding:14px 16px 64px}
|
|
153
|
+
.rd-heads{position:sticky;top:0;z-index:15;display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:6px 0 10px;background:linear-gradient(180deg,var(--rv-page) 70%,rgba(247,248,246,0))}
|
|
154
|
+
body.rd-body[data-mode='unified'] .rd-heads{display:none}
|
|
155
|
+
.rd-head{display:flex;align-items:center;gap:7px;font-size:12px;font-weight:600;color:var(--rv-muted)}
|
|
156
|
+
.rd-dot{width:8px;height:8px;border-radius:50%}
|
|
157
|
+
.rd-dot--before{background:#B3BAC5}
|
|
158
|
+
.rd-dot--after{background:var(--rv-accent)}
|
|
159
|
+
|
|
160
|
+
/* The reader stylesheet paints its own page background; the comparison supplies
|
|
161
|
+
the surface instead, so it is cleared for both themes. */
|
|
162
|
+
.rd-root,.rd-root[data-color-theme='light'],.rd-root[data-color-theme='dark']{min-height:0;background:transparent;background-image:none;display:flex;flex-direction:column;gap:2px}
|
|
163
|
+
.rd-row{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-items:stretch}
|
|
164
|
+
body.rd-body[data-mode='unified'] .rd-row{grid-template-columns:1fr;gap:0}
|
|
165
|
+
body.rd-body[data-mode='unified'] .rd-row[data-status='equal'] .rd-cell--before{display:none}
|
|
166
|
+
body.rd-body[data-mode='unified'] .rd-row[data-status='changed'] .rd-cell--before{margin-bottom:2px}
|
|
167
|
+
body.rd-body.rd-only-changes .rd-row[data-status='equal']{display:none}
|
|
168
|
+
body.rd-body.rd-only-changes .rd-fold{display:none}
|
|
169
|
+
|
|
170
|
+
.rd-cell{position:relative;display:flex;gap:10px;padding:6px 14px 6px 0;border-radius:7px;background:#fff;border:1px solid transparent;min-width:0}
|
|
171
|
+
.rd-gutter{width:4px;flex:none;border-radius:4px;background:transparent;margin:6px 0 6px 6px}
|
|
172
|
+
.rd-content{margin-top:0;padding:8px 0;min-width:0;flex:1;overflow-x:auto}
|
|
173
|
+
.rd-content>*:first-child{margin-top:0}
|
|
174
|
+
.rd-content>*:last-child{margin-bottom:0}
|
|
175
|
+
/* Wide blocks scroll inside their own cell instead of being clipped by the
|
|
176
|
+
column, and identifiers stay whole rather than breaking mid-token. */
|
|
177
|
+
.rd-content{overflow-wrap:normal}
|
|
178
|
+
.rd-content table{min-width:min-content}
|
|
179
|
+
.rd-content code,.rd-content kbd,.rd-content samp{overflow-wrap:normal;word-break:keep-all}
|
|
180
|
+
.rd-content pre{max-width:100%;overflow-x:auto}
|
|
181
|
+
|
|
182
|
+
.rd-cell[data-status='equal']{background:#fff;border-color:#ECEFEB}
|
|
183
|
+
.rd-cell--before[data-status='changed']{background:var(--rv-removed-soft);border-color:var(--rv-removed-line)}
|
|
184
|
+
.rd-cell--before[data-status='changed'] .rd-gutter{background:var(--rv-removed)}
|
|
185
|
+
.rd-cell--after[data-status='changed']{background:var(--rv-added-soft);border-color:var(--rv-added-line)}
|
|
186
|
+
.rd-cell--after[data-status='changed'] .rd-gutter{background:var(--rv-added)}
|
|
187
|
+
.rd-cell--after[data-status='added']{background:var(--rv-added-soft);border-color:var(--rv-added-line)}
|
|
188
|
+
.rd-cell--after[data-status='added'] .rd-gutter{background:var(--rv-added)}
|
|
189
|
+
.rd-cell--before[data-status='removed']{background:var(--rv-removed-soft);border-color:var(--rv-removed-line)}
|
|
190
|
+
.rd-cell--before[data-status='removed'] .rd-gutter{background:var(--rv-removed)}
|
|
191
|
+
.rd-cell--before[data-status='added'],.rd-cell--after[data-status='removed']{background:repeating-linear-gradient(45deg,#FBFCFD,#FBFCFD 8px,#F2F5F8 8px,#F2F5F8 16px);border-color:#EEF1F5}
|
|
192
|
+
.rd-empty{padding:14px 2px;font-size:12px;font-style:italic;color:#8B94A5}
|
|
193
|
+
|
|
194
|
+
.rd-word{border-radius:3px;padding:0 2px;background:transparent;color:inherit;font-weight:inherit}
|
|
195
|
+
.rd-cell--after .rv-word--ins,.rv-word--ins{background:#CBF4C9;color:var(--rv-added);border-radius:3px;padding:0 2px;box-decoration-break:clone;-webkit-box-decoration-break:clone}
|
|
196
|
+
.rd-cell--before .rv-word--del,.rv-word--del{background:#FDE2DD;color:var(--rv-removed);border-radius:3px;padding:0 2px;box-decoration-break:clone;-webkit-box-decoration-break:clone}
|
|
197
|
+
|
|
198
|
+
.rd-fold{margin:2px 0}
|
|
199
|
+
.rd-fold-summary{list-style:none;display:flex;align-items:center;gap:12px;cursor:pointer;padding:6px 4px;user-select:none}
|
|
200
|
+
.rd-fold-summary::-webkit-details-marker{display:none}
|
|
201
|
+
.rd-fold-line{flex:1;height:1px;background:linear-gradient(90deg,transparent,var(--rv-border) 20%,var(--rv-border) 80%,transparent)}
|
|
202
|
+
.rd-fold-label{font-size:11.5px;font-weight:600;color:#8B94A5;white-space:nowrap;padding:3px 10px;border:1px solid var(--rv-border);border-radius:100px;background:#fff}
|
|
203
|
+
.rd-fold[open] .rd-fold-label{color:var(--rv-muted)}
|
|
204
|
+
.rd-fold-body{display:flex;flex-direction:column;gap:2px;margin-top:2px}
|
|
205
|
+
.rd-render-error{color:#B91C1C}
|
|
206
|
+
|
|
207
|
+
.rd-cell-tag{display:none;margin-bottom:8px;padding:2px 8px;border-radius:5px;font-size:11px;font-weight:600;border:1px solid var(--rv-border);color:var(--rv-muted);background:#fff}
|
|
208
|
+
.rd-cell--before .rd-cell-tag{border-color:var(--rv-removed-line);color:var(--rv-removed)}
|
|
209
|
+
.rd-cell--after .rd-cell-tag{border-color:var(--rv-added-line);color:var(--rv-added)}
|
|
210
|
+
body.rd-body[data-mode='unified'] .rd-cell-tag{display:inline-block}
|
|
211
|
+
body.rd-body[data-mode='unified'] .rd-row[data-status='equal'] .rd-cell-tag{display:none}
|
|
212
|
+
|
|
213
|
+
/* Below this width the two columns cannot sit side by side, so the comparison
|
|
214
|
+
stacks and every cell names the side it belongs to. */
|
|
215
|
+
@media(max-width:640px){
|
|
216
|
+
.rd-row{grid-template-columns:1fr;gap:2px}
|
|
217
|
+
.rd-heads{display:none}
|
|
218
|
+
.rd-cell-tag{display:inline-block}
|
|
219
|
+
.rd-row[data-status='equal'] .rd-cell--before{display:none}
|
|
220
|
+
.rd-row[data-status='equal'] .rd-cell-tag{display:none}
|
|
221
|
+
}
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=review-render.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AgentName, DocumentationReviewReport } from './types.js';
|
|
2
|
+
export declare function persistReviewReport(root: string, output: string, execution: {
|
|
3
|
+
agent: AgentName;
|
|
4
|
+
model?: string;
|
|
5
|
+
reasoning?: string;
|
|
6
|
+
}): Promise<DocumentationReviewReport>;
|
|
7
|
+
export declare function listReviewReports(root: string): Promise<DocumentationReviewReport[]>;
|
|
8
|
+
export declare function parseReviewReport(output: string): Pick<DocumentationReviewReport, 'score' | 'hardGates' | 'summary' | 'findings'>;
|
|
9
|
+
//# sourceMappingURL=review-report.d.ts.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const REVIEW_DIRECTORY = join('.doxloop', 'reviews');
|
|
5
|
+
export async function persistReviewReport(root, output, execution) {
|
|
6
|
+
const parsed = parseReviewReport(output);
|
|
7
|
+
const report = {
|
|
8
|
+
schemaVersion: 1,
|
|
9
|
+
id: `review-${Date.now()}-${randomBytes(3).toString('hex')}`,
|
|
10
|
+
createdAt: new Date().toISOString(),
|
|
11
|
+
agent: execution.agent,
|
|
12
|
+
...(execution.model ? { model: execution.model } : {}),
|
|
13
|
+
...(execution.reasoning ? { reasoning: execution.reasoning } : {}),
|
|
14
|
+
...parsed,
|
|
15
|
+
};
|
|
16
|
+
const directory = join(root, REVIEW_DIRECTORY);
|
|
17
|
+
await mkdir(directory, { recursive: true });
|
|
18
|
+
await writeFile(join(directory, `${report.id}.json`), `${JSON.stringify(report, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
19
|
+
return report;
|
|
20
|
+
}
|
|
21
|
+
export async function listReviewReports(root) {
|
|
22
|
+
let entries;
|
|
23
|
+
try {
|
|
24
|
+
entries = await readdir(join(root, REVIEW_DIRECTORY), { withFileTypes: true });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const reports = [];
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (!entry.isFile() || !/^review-[a-z0-9-]+\.json$/.test(entry.name))
|
|
32
|
+
continue;
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(await readFile(join(root, REVIEW_DIRECTORY, entry.name), 'utf8'));
|
|
35
|
+
if (isReviewReport(raw))
|
|
36
|
+
reports.push(raw);
|
|
37
|
+
}
|
|
38
|
+
catch { /* A damaged operational report does not hide other reviews. */ }
|
|
39
|
+
}
|
|
40
|
+
return reports.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
41
|
+
}
|
|
42
|
+
export function parseReviewReport(output) {
|
|
43
|
+
const structured = /<doxloop-review>\s*([\s\S]*?)\s*<\/doxloop-review>/i.exec(output)?.[1];
|
|
44
|
+
if (structured) {
|
|
45
|
+
try {
|
|
46
|
+
const raw = JSON.parse(structured);
|
|
47
|
+
const findings = Array.isArray(raw.findings) ? raw.findings.flatMap(normalizeFinding).slice(0, 50) : [];
|
|
48
|
+
return { score: boundedScore(raw.score), hardGates: gate(raw.hardGates), summary: text(raw.summary) || summaryFromOutput(output), findings };
|
|
49
|
+
}
|
|
50
|
+
catch { /* Fall through to the safe Markdown parser. */ }
|
|
51
|
+
}
|
|
52
|
+
const scoreMatch = /(?:quality|rubric|overall)?\s*score[^\d]{0,20}(\d{1,3})(?:\s*\/\s*100|%)/i.exec(output);
|
|
53
|
+
const gateMatch = /hard[- ]?gates?[^\n]*(pass|fail)/i.exec(output)?.[1]?.toLowerCase();
|
|
54
|
+
const findings = output.split(/\r?\n/).flatMap((line, index) => {
|
|
55
|
+
const match = /^\s*(?:[-*]|\d+\.)\s*(?:(blocker|major|minor)\s*[:—-]\s*)?(.{12,})$/i.exec(line);
|
|
56
|
+
if (!match?.[2])
|
|
57
|
+
return [];
|
|
58
|
+
const severity = (match[1]?.toLowerCase() ?? inferSeverity(match[2]));
|
|
59
|
+
return [{ id: `finding-${index + 1}`, severity, title: match[2].slice(0, 120), description: match[2], pages: pageReferences(match[2]), evidence: [], recommendation: match[2] }];
|
|
60
|
+
}).slice(0, 50);
|
|
61
|
+
return { score: scoreMatch ? boundedScore(Number(scoreMatch[1])) : 0, hardGates: gateMatch === 'pass' ? 'pass' : gateMatch === 'fail' ? 'fail' : 'unknown', summary: summaryFromOutput(output), findings };
|
|
62
|
+
}
|
|
63
|
+
function normalizeFinding(value, index) {
|
|
64
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
65
|
+
return [];
|
|
66
|
+
const raw = value;
|
|
67
|
+
const title = text(raw.title);
|
|
68
|
+
if (!title)
|
|
69
|
+
return [];
|
|
70
|
+
return [{ id: text(raw.id) || `finding-${index + 1}`, severity: severity(raw.severity), title, description: text(raw.description) || title, pages: textList(raw.pages), evidence: textList(raw.evidence), recommendation: text(raw.recommendation) || title }];
|
|
71
|
+
}
|
|
72
|
+
function summaryFromOutput(output) {
|
|
73
|
+
const line = output.split(/\r?\n/).map((item) => item.replace(/^#+\s*/, '').trim()).find((item) => item.length >= 20 && !item.startsWith('{') && !item.startsWith('<'));
|
|
74
|
+
return line?.slice(0, 500) ?? 'The review completed without a structured summary.';
|
|
75
|
+
}
|
|
76
|
+
function pageReferences(value) { return [...new Set(value.match(/[\w./-]+\.(?:md|mdx|rst|html)/gi) ?? [])]; }
|
|
77
|
+
function inferSeverity(value) { return /block|critical|security|broken/i.test(value) ? 'blocker' : /missing|incorrect|major|stale/i.test(value) ? 'major' : 'minor'; }
|
|
78
|
+
function severity(value) { return value === 'blocker' || value === 'major' ? value : 'minor'; }
|
|
79
|
+
function boundedScore(value) { const number = typeof value === 'number' ? value : Number(value); return Number.isFinite(number) ? Math.max(0, Math.min(100, Math.round(number))) : 0; }
|
|
80
|
+
function gate(value) { return value === 'pass' || value === 'fail' ? value : 'unknown'; }
|
|
81
|
+
function text(value) { return typeof value === 'string' ? value.trim() : ''; }
|
|
82
|
+
function textList(value) { return Array.isArray(value) ? value.filter((item) => typeof item === 'string' && item.trim() !== '').map((item) => item.trim()) : []; }
|
|
83
|
+
function isReviewReport(value) {
|
|
84
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
85
|
+
return false;
|
|
86
|
+
const report = value;
|
|
87
|
+
return report.schemaVersion === 1 && typeof report.id === 'string' && typeof report.createdAt === 'string' && typeof report.agent === 'string' && typeof report.score === 'number' && ['pass', 'fail', 'unknown'].includes(report.hardGates ?? '') && typeof report.summary === 'string' && Array.isArray(report.findings);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=review-report.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The review center: four screens joined by real URLs.
|
|
3
|
+
*
|
|
4
|
+
* / the proposals waiting for a decision
|
|
5
|
+
* /runs/:id what one proposal changes, and why
|
|
6
|
+
* /runs/:id/pages/:n one page at a time, current beside proposed
|
|
7
|
+
* /runs/:id/done what was applied
|
|
8
|
+
*
|
|
9
|
+
* The reading screen carries one bar above the comparison and one below it;
|
|
10
|
+
* everything else lives on the screen where it belongs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function icon(name: string, size?: number): string;
|
|
13
|
+
export declare function reviewDocument(token: string): string;
|
|
14
|
+
//# sourceMappingURL=review-ui.d.ts.map
|