docs: add UI screenshots (Playwright) for docs (#705)
Co-authored-by: Clawdbot <clawdbot@Clawdbots-Mac-mini.local>
taylorcopilot committed
Feb 14, 2026 at 17:35 UTC
88a85e811d8972102d3753823fb659fa11b8dba3
35 files changed
+190
docs/assets/ui/agents-copilot-actions.png
Binary files /dev/null and b/docs/assets/ui/agents-copilot-actions.png differ
docs/assets/ui/agents-detection-rules.png
Binary files /dev/null and b/docs/assets/ui/agents-detection-rules.png differ
docs/assets/ui/agents-groups.png
Binary files /dev/null and b/docs/assets/ui/agents-groups.png differ
docs/assets/ui/agents-sca-overview.png
Binary files /dev/null and b/docs/assets/ui/agents-sca-overview.png differ
docs/assets/ui/agents-sysmon-config.png
Binary files /dev/null and b/docs/assets/ui/agents-sysmon-config.png differ
docs/assets/ui/agents-vulnerability-overview.png
Binary files /dev/null and b/docs/assets/ui/agents-vulnerability-overview.png differ
docs/assets/ui/agents.png
Binary files /dev/null and b/docs/assets/ui/agents.png differ
docs/assets/ui/alerts-atomic-red-team.png
Binary files /dev/null and b/docs/assets/ui/alerts-atomic-red-team.png differ
docs/assets/ui/alerts-mitre.png
Binary files /dev/null and b/docs/assets/ui/alerts-mitre.png differ
docs/assets/ui/alerts-siem.png
Binary files /dev/null and b/docs/assets/ui/alerts-siem.png differ
docs/assets/ui/artifacts.png
Binary files /dev/null and b/docs/assets/ui/artifacts.png differ
docs/assets/ui/connectors.png
Binary files /dev/null and b/docs/assets/ui/connectors.png differ
docs/assets/ui/customer-portal.png
Binary files /dev/null and b/docs/assets/ui/customer-portal.png differ
docs/assets/ui/customers.png
Binary files /dev/null and b/docs/assets/ui/customers.png differ
docs/assets/ui/external-network-connectors.png
Binary files /dev/null and b/docs/assets/ui/external-network-connectors.png differ
docs/assets/ui/external-singul-app-auth.png
Binary files /dev/null and b/docs/assets/ui/external-singul-app-auth.png differ
docs/assets/ui/external-third-party-integrations.png
Binary files /dev/null and b/docs/assets/ui/external-third-party-integrations.png differ
docs/assets/ui/graylog-management.png
Binary files /dev/null and b/docs/assets/ui/graylog-management.png differ
docs/assets/ui/graylog-metrics.png
Binary files /dev/null and b/docs/assets/ui/graylog-metrics.png differ
docs/assets/ui/graylog-pipelines.png
Binary files /dev/null and b/docs/assets/ui/graylog-pipelines.png differ
docs/assets/ui/healthcheck.png
Binary files /dev/null and b/docs/assets/ui/healthcheck.png differ
docs/assets/ui/incident-alerts.png
Binary files /dev/null and b/docs/assets/ui/incident-alerts.png differ
docs/assets/ui/incident-cases.png
Binary files /dev/null and b/docs/assets/ui/incident-cases.png differ
docs/assets/ui/incident-sources.png
Binary files /dev/null and b/docs/assets/ui/incident-sources.png differ
docs/assets/ui/indices-management.png
Binary files /dev/null and b/docs/assets/ui/indices-management.png differ
docs/assets/ui/indices-snapshots.png
Binary files /dev/null and b/docs/assets/ui/indices-snapshots.png differ
docs/assets/ui/overview.png
Binary files /dev/null and b/docs/assets/ui/overview.png differ
docs/assets/ui/patch-tuesday.png
Binary files /dev/null and b/docs/assets/ui/patch-tuesday.png differ
docs/assets/ui/report-general.png
Binary files /dev/null and b/docs/assets/ui/report-general.png differ
docs/assets/ui/report-sca.png
Binary files /dev/null and b/docs/assets/ui/report-sca.png differ
docs/assets/ui/report-vulnerability.png
Binary files /dev/null and b/docs/assets/ui/report-vulnerability.png differ
docs/assets/ui/scheduler.png
Binary files /dev/null and b/docs/assets/ui/scheduler.png differ
tools/ui-screenshots/package-lock.json
new
+57
@@ -0,0 +1,57 @@
1
+{
2
+ "name": "copilot-ui-screenshots",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {
6
+ "": {
7
+ "name": "copilot-ui-screenshots",
8
+ "dependencies": {
9
+ "playwright": "^1.51.1"
10
+ }
11
+ },
12
+ "node_modules/fsevents": {
13
+ "version": "2.3.2",
14
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
15
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
16
+ "hasInstallScript": true,
17
+ "license": "MIT",
18
+ "optional": true,
19
+ "os": [
20
+ "darwin"
21
+ ],
22
+ "engines": {
23
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
24
+ }
25
+ },
26
+ "node_modules/playwright": {
27
+ "version": "1.58.2",
28
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
29
+ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
30
+ "license": "Apache-2.0",
31
+ "dependencies": {
32
+ "playwright-core": "1.58.2"
33
+ },
34
+ "bin": {
35
+ "playwright": "cli.js"
36
+ },
37
+ "engines": {
38
+ "node": ">=18"
39
+ },
40
+ "optionalDependencies": {
41
+ "fsevents": "2.3.2"
42
+ }
43
+ },
44
+ "node_modules/playwright-core": {
45
+ "version": "1.58.2",
46
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
47
+ "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
48
+ "license": "Apache-2.0",
49
+ "bin": {
50
+ "playwright-core": "cli.js"
51
+ },
52
+ "engines": {
53
+ "node": ">=18"
54
+ }
55
+ }
56
+ }
57
+}
tools/ui-screenshots/package.json
new
+11
@@ -0,0 +1,11 @@
1
+{
2
+ "name": "copilot-ui-screenshots",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "shots": "node ./screenshots.mjs"
7
+ },
8
+ "dependencies": {
9
+ "playwright": "^1.51.1"
10
+ }
11
+}
tools/ui-screenshots/screenshots.mjs
new
+122
@@ -0,0 +1,122 @@
1
+import fs from 'node:fs/promises';
2
+import path from 'node:path';
3
+import {chromium} from 'playwright';
4
+
5
+const BASE_URL = process.env.COPILOT_BASE_URL || 'https://copilot-lab.socfortressmdr.us';
6
+const USERNAME = process.env.COPILOT_USERNAME;
7
+const PASSWORD = process.env.COPILOT_PASSWORD;
8
+
9
+if (!USERNAME || !PASSWORD) {
10
+ console.error('Missing COPILOT_USERNAME or COPILOT_PASSWORD in environment.');
11
+ process.exit(1);
12
+}
13
+
14
+const OUT_DIR = path.resolve(process.cwd(), '..', '..', 'docs', 'assets', 'ui');
15
+
16
+const routes = [
17
+ {key: 'overview', path: '/overview'},
18
+ {key: 'incident-sources', path: '/incident-management/sources'},
19
+ {key: 'incident-alerts', path: '/incident-management/alerts'},
20
+ {key: 'incident-cases', path: '/incident-management/cases'},
21
+ {key: 'alerts-siem', path: '/alerts/siem'},
22
+ {key: 'alerts-mitre', path: '/alerts/mitre'},
23
+ {key: 'alerts-atomic-red-team', path: '/alerts/atomic-red-team'},
24
+ {key: 'artifacts', path: '/artifacts'},
25
+ {key: 'customers', path: '/customers'},
26
+ {key: 'agents', path: '/agents'},
27
+ {key: 'agents-groups', path: '/agents/groups'},
28
+ {key: 'agents-sysmon-config', path: '/agents/sysmon-config'},
29
+ {key: 'agents-detection-rules', path: '/agents/detection-rules'},
30
+ {key: 'agents-copilot-actions', path: '/agents/copilot-actions'},
31
+ {key: 'agents-vulnerability-overview', path: '/agents/vulnerability-overview'},
32
+ {key: 'agents-sca-overview', path: '/agents/sca-overview'},
33
+ {key: 'patch-tuesday', path: '/patch-tuesday'},
34
+ {key: 'report-general', path: '/report-creation/general'},
35
+ {key: 'report-vulnerability', path: '/report-creation/vulnerability-reports'},
36
+ {key: 'report-sca', path: '/report-creation/sca-reports'},
37
+ {key: 'healthcheck', path: '/healthcheck'},
38
+ {key: 'indices-management', path: '/indices/management'},
39
+ {key: 'indices-snapshots', path: '/indices/snapshots'},
40
+ {key: 'graylog-management', path: '/graylog/management'},
41
+ {key: 'graylog-metrics', path: '/graylog/metrics'},
42
+ {key: 'graylog-pipelines', path: '/graylog/pipelines'},
43
+ {key: 'connectors', path: '/connectors'},
44
+ {key: 'external-third-party-integrations', path: '/external-services/third-party-integrations'},
45
+ {key: 'external-network-connectors', path: '/external-services/network-connectors'},
46
+ {key: 'external-singul-app-auth', path: '/external-services/singul-app-auth'},
47
+ {key: 'scheduler', path: '/scheduler'},
48
+ {key: 'customer-portal', path: '/customer-portal'}
49
+];
50
+
51
+const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
52
+
53
+async function ensureDir(p) {
54
+ await fs.mkdir(p, {recursive: true});
55
+}
56
+
57
+async function main() {
58
+ await ensureDir(OUT_DIR);
59
+
60
+ const browser = await chromium.launch({headless: true});
61
+ const context = await browser.newContext({
62
+ viewport: {width: 1440, height: 900},
63
+ deviceScaleFactor: 2,
64
+ });
65
+ const page = await context.newPage();
66
+
67
+ // Login
68
+ await page.goto(`${BASE_URL}/login`, {waitUntil: 'domcontentloaded'});
69
+ await page.waitForTimeout(500);
70
+
71
+ // Try a few common selectors for the login form
72
+ const userInput = page.getByRole('textbox', {name: /username/i}).first()
73
+ .or(page.getByPlaceholder(/username/i).first())
74
+ .or(page.locator('input[type="text"], input[type="email"]').first());
75
+
76
+ const passInput = page.getByRole('textbox', {name: /password/i}).first()
77
+ .or(page.getByPlaceholder(/password/i).first())
78
+ .or(page.locator('input[type="password"]').first());
79
+
80
+ await userInput.fill(USERNAME);
81
+ await passInput.fill(PASSWORD);
82
+
83
+ const signIn = page.getByRole('button', {name: /sign in/i}).first()
84
+ .or(page.locator('button[type="submit"]').first());
85
+
86
+ await Promise.all([
87
+ page.waitForLoadState('networkidle').catch(() => undefined),
88
+ signIn.click()
89
+ ]);
90
+
91
+ // Give SPA time to hydrate
92
+ await sleep(1500);
93
+
94
+ // If still on /login, fail early
95
+ if (page.url().includes('/login')) {
96
+ const shot = path.join(OUT_DIR, '_login-failed.png');
97
+ await page.screenshot({path: shot, fullPage: true});
98
+ throw new Error(`Login appears to have failed; screenshot saved to ${shot}`);
99
+ }
100
+
101
+ // Screenshot sweep
102
+ for (const r of routes) {
103
+ const url = `${BASE_URL}${r.path}`;
104
+ console.log(`SHOT ${r.key} -> ${url}`);
105
+ await page.goto(url, {waitUntil: 'domcontentloaded'});
106
+ await page.waitForTimeout(1200);
107
+
108
+ // Attempt to close any toast/overlay by pressing Escape (best-effort)
109
+ await page.keyboard.press('Escape').catch(() => undefined);
110
+
111
+ const outPath = path.join(OUT_DIR, `${r.key}.png`);
112
+ await page.screenshot({path: outPath, fullPage: true});
113
+ }
114
+
115
+ await browser.close();
116
+ console.log(`Done. Wrote ${routes.length} screenshots to ${OUT_DIR}`);
117
+}
118
+
119
+main().catch((err) => {
120
+ console.error(err?.stack || String(err));
121
+ process.exit(1);
122
+});