Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 16, 2026

Screenshot Automation GitHub Action - Final

Latest Updates (Addressing Review Round 3)

Code Quality Improvements

  • Defined MOCK_TOKEN as a constant for better maintainability
  • Added warning comment about keeping screenshots array in sync with html_vars dictionary
  • Changed waitForLoadState from 'networkidle' to 'load' for faster, more appropriate handling of static HTML files
  • Added clarifying comment explaining why viewer dashboard doesn't receive CSS content (uses inline styles)
  • Updated documentation to reference the correct 'load' state

Previous Updates (Review Round 2)

Documentation Fixes

  • Updated troubleshooting section to correctly reference waitForLoadState instead of outdated waitForTimeout
  • Removed PIN setup modal screenshot reference from WEBSITE_PREVIEW.md since this screenshot requires interactive setup and is not auto-generated

Previous Changes (Review Round 1)

Critical Fixes

CSS Inlining - Resolved file:// protocol issue where CSS wouldn't load. Now CSS is inlined directly into each HTML file during extraction.

HTML Extraction Logic - Fixed bug where all subsequent HTML variables were concatenated together. Now correctly stops at next variable declaration.

Robustness Improvements

Concurrency Control - Changed from branch-specific to fixed concurrency group to prevent conflicts across branches

Error Handling - Added try-catch to Node.js screenshot script with proper error reporting

Better Wait Logic - Now uses waitForLoadState('load') for optimal static HTML file handling

Improved Git Operations - Enhanced git add logic with:

  • Explicit file existence checks
  • Better error messages
  • Progress logging for added files
  • No silent failures

Optimized Checkout - Changed fetch-depth from 0 to 1 for faster workflow execution

Better Logging - Added warnings for:

  • Failed HTML extractions
  • Missing CSS
  • Number of additional HTML parts found

Documentation Updates

Route Path Clarification - Explained that routes are original HTTP paths but screenshots use file:// URLs

Script Editing Instructions - Clarified that users must edit the workflow YAML, not /tmp/take-screenshots.js

Auth Token Comment - Added note explaining the mock token is non-sensitive and only for client-side logic

Maintenance Warnings - Added comments about keeping screenshots array synchronized with HTML extraction

Screenshots Automated

8 server pages + 1 viewer page = 9 total automated screenshots

Note: Client Console PIN Setup modal is documented but not auto-generated as it requires interactive page setup. The existing manual screenshot is preserved.

Original prompt

This section details on the original issue you should resolve

<issue_title>update screenshot github action</issue_title>
<issue_description>need to update the github action that takes screenshots of the webpages. might need to add or remove pages that need to be screenshotted. please update as many as possible. need to make sure that all these are automatically updated in the future.</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 4 commits January 16, 2026 00:53
Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Copilot AI changed the title [WIP] Update GitHub action for webpage screenshots Add automated screenshot workflow for web interface documentation Jan 16, 2026
Copilot AI requested a review from dorkmo January 16, 2026 01:02
@dorkmo dorkmo marked this pull request as ready for review January 16, 2026 01:08
Copilot AI review requested due to automatic review settings January 16, 2026 01:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces automated screenshot generation for the TankAlarm web interfaces, addressing the issue of manually maintained screenshots becoming stale as the codebase evolves.

Changes:

  • New GitHub Actions workflow for automated screenshot generation using Playwright
  • Python-based HTML extraction from Arduino .ino files with regex parsing
  • Documentation updates for workflow configuration and troubleshooting
  • Updated WEBSITE_PREVIEW.md files with automation timestamps

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 15 comments.

File Description
.github/workflows/update-screenshots.yml New workflow that extracts HTML from Arduino files, generates screenshots via Playwright, and auto-commits changes
.github/workflows/README.md Documentation for the screenshot workflow including triggers, troubleshooting, and customization guide
TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md Updated timestamp format for automated updates
TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md Updated timestamp format for automated updates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

contents: write

concurrency:
group: update-screenshots-${{ github.ref }}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concurrency group uses github.ref which will be different for each branch. This means the workflow can run concurrently on different branches (e.g., main and a feature branch), potentially causing conflicts when both try to push changes. Consider using a fixed concurrency group name like update-screenshots to ensure only one instance runs at a time across all branches, or add logic to prevent commits on non-default branches.

Suggested change
group: update-screenshots-${{ github.ref }}
group: update-screenshots

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Changed to fixed concurrency group update-screenshots to prevent concurrent runs across branches.

Comment on lines 264 to 268
git add TankAlarm-112025-Server-BluesOpta/screenshots/*.png || true
git add TankAlarm-112025-Viewer-BluesOpta/screenshots/*.png || true
git add TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md || true
git add TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md || true
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The git add commands use wildcards (*.png) with || true to ignore errors. While this prevents failures when no PNG files exist, it could also silently ignore genuine errors like permission issues or filesystem problems. Consider checking if the screenshot directories exist and contain files before attempting to add them, providing better visibility into what's being committed.

Suggested change
git add TankAlarm-112025-Server-BluesOpta/screenshots/*.png || true
git add TankAlarm-112025-Viewer-BluesOpta/screenshots/*.png || true
git add TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md || true
git add TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md || true
# Add server screenshots if any PNGs exist
if [ -d "TankAlarm-112025-Server-BluesOpta/screenshots" ]; then
shopt -s nullglob
server_pngs=(TankAlarm-112025-Server-BluesOpta/screenshots/*.png)
shopt -u nullglob
if [ ${#server_pngs[@]} -gt 0 ]; then
git add "${server_pngs[@]}"
else
echo "No server screenshots to add"
fi
else
echo "Server screenshots directory does not exist, skipping"
fi
# Add viewer screenshots if any PNGs exist
if [ -d "TankAlarm-112025-Viewer-BluesOpta/screenshots" ]; then
shopt -s nullglob
viewer_pngs=(TankAlarm-112025-Viewer-BluesOpta/screenshots/*.png)
shopt -u nullglob
if [ ${#viewer_pngs[@]} -gt 0 ]; then
git add "${viewer_pngs[@]}"
else
echo "No viewer screenshots to add"
fi
else
echo "Viewer screenshots directory does not exist, skipping"
fi
# Add WEBSITE_PREVIEW.md files if they exist
if [ -f "TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md" ]; then
git add TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md
else
echo "Server WEBSITE_PREVIEW.md not found, skipping"
fi
if [ -f "TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md" ]; then
git add TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md
else
echo "Viewer WEBSITE_PREVIEW.md not found, skipping"
fi

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Replaced wildcard git add with explicit file existence checks and logging. Now properly reports when files are added or skipped with clear messages.

});
const page = await context.newPage();
// Mock localStorage for auth token
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow uses a hardcoded authentication token '1234' in localStorage for all screenshots. While this is acceptable for static HTML screenshots, the documentation should clarify that this is a mock token and explain why it's needed. This would help prevent confusion if someone reviews the workflow and questions the security implications of a hardcoded credential.

Suggested change
// Mock localStorage for auth token
// Mock localStorage for auth token.
// NOTE: This uses a non-sensitive, dummy value ("1234") only to satisfy
// client-side logic in static HTML pages when generating screenshots.
// No real authentication or secrets are involved, and this token is not
// used against any live backend service.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Added clarifying comment explaining the token is a non-sensitive dummy value only for client-side logic in static screenshots.

Comment on lines 72 to 74
additional_parts = re.findall(r'R"HTML\((.*?)\)HTML"',
content[match.end():match.end()+MAX_ADDITIONAL_SEARCH],
re.DOTALL)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MAX_ADDITIONAL_SEARCH limit of 50,000 characters may be insufficient for large HTML pages with extensive multi-part string concatenation. If a page has a first part followed by additional parts beyond this limit, those parts will be silently omitted from the extracted HTML, resulting in incomplete screenshots. Consider either removing this limit or making it configurable, and add logging when additional parts are found to help identify potential truncation issues.

Suggested change
additional_parts = re.findall(r'R"HTML\((.*?)\)HTML"',
content[match.end():match.end()+MAX_ADDITIONAL_SEARCH],
re.DOTALL)
additional_search_region = content[match.end():]
additional_parts = re.findall(
r'R"HTML\((.*?)\)HTML"',
additional_search_region,
re.DOTALL,
)
if additional_parts:
print(f"Found {len(additional_parts)} additional HTML part(s) for {var_name}.")

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Removed the arbitrary limit and improved logic to search until next variable declaration. Added logging to show number of additional parts found for each variable.

Comment on lines 241 to 247
sed -i "s/\*Last updated:.*/*Last updated: ${timestamp}*/" \
TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md
fi
# Update Viewer WEBSITE_PREVIEW.md
if [ -f "TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md" ]; then
sed -i "s/\*Last updated:.*/*Last updated: ${timestamp}*/" \
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sed regex pattern \*Last updated:.* uses a greedy match (.*) which will match everything after "Last updated:" until the end of the line. This should work correctly, but it's worth noting that if there are multiple occurrences of this pattern on the same line, only the first one will be replaced. Consider using a more specific pattern like \*Last updated: [^*]*\* to match only until the closing asterisk, ensuring more precise replacement.

Suggested change
sed -i "s/\*Last updated:.*/*Last updated: ${timestamp}*/" \
TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md
fi
# Update Viewer WEBSITE_PREVIEW.md
if [ -f "TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md" ]; then
sed -i "s/\*Last updated:.*/*Last updated: ${timestamp}*/" \
sed -i "s/\*Last updated: [^*]*\*/\*Last updated: ${timestamp}\*/" \
TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md
fi
# Update Viewer WEBSITE_PREVIEW.md
if [ -f "TankAlarm-112025-Viewer-BluesOpta/WEBSITE_PREVIEW.md" ]; then
sed -i "s/\*Last updated: [^*]*\*/\*Last updated: ${timestamp}\*/" \

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Changed pattern to \*Last updated: [^*]*\* for more precise matching until the closing asterisk.

Comment on lines +5 to +13
schedule:
- cron: '0 2 * * 1'

# Allow manual trigger
workflow_dispatch:

# Run when relevant files change
push:
branches: [main, master]
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow is configured to run on push to both 'main' and 'master' branches, but the scheduled trigger doesn't specify which branch to use. By default, scheduled workflows run on the default branch. If the repository uses 'master' as the default branch but the workflow expects 'main', or vice versa, this could cause confusion. Consider documenting which branch the scheduled runs execute on, or explicitly specify it if needed.

Copilot uses AI. Check for mistakes.
Comment on lines 176 to 187
**Server pages captured:**
- Dashboard (/)
- Client Console (/client-console)
- Config Generator (/config-generator)
- Serial Monitor (/serial-monitor)
- Calibration (/calibration)
- Contacts Manager (/contacts)
- Server Settings (/server-settings)
- Historical Data (/historical)

**Viewer pages captured:**
- Dashboard (/)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation lists route paths like "Dashboard (/)" and "Client Console (/client-console)", but these routes don't apply to the screenshot generation process since the HTML files are loaded directly from the filesystem via file:// URLs, not through HTTP routes. This could confuse users who might expect these to be accessible HTTP endpoints. Consider clarifying that these route paths represent the original server routes, but screenshots are generated from extracted static HTML files.

Suggested change
**Server pages captured:**
- Dashboard (/)
- Client Console (/client-console)
- Config Generator (/config-generator)
- Serial Monitor (/serial-monitor)
- Calibration (/calibration)
- Contacts Manager (/contacts)
- Server Settings (/server-settings)
- Historical Data (/historical)
**Viewer pages captured:**
- Dashboard (/)
**Server pages captured (original HTTP routes in parentheses):**
- Dashboard (route `/`)
- Client Console (route `/client-console`)
- Config Generator (route `/config-generator`)
- Serial Monitor (route `/serial-monitor`)
- Calibration (route `/calibration`)
- Contacts Manager (route `/contacts`)
- Server Settings (route `/server-settings`)
- Historical Data (route `/historical`)
These routes refer to the original server endpoints in the Arduino web UI. During this workflow, no HTTP server is started: Playwright loads the extracted HTML files directly from the filesystem using `file://` URLs that correspond to these routes.
**Viewer pages captured (original HTTP routes in parentheses):**
- Dashboard (route `/`)

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Updated documentation to clarify these are original HTTP routes and added explanation that Playwright loads extracted static HTML files via file:// URLs with inlined CSS.

- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkout uses fetch-depth: 0 which fetches the entire git history. For a workflow that only needs to update screenshots and push changes, a shallow clone (fetch-depth: 1) would be more efficient and faster. The full history is not needed for this operation unless there's a specific reason to analyze git history.

Suggested change
fetch-depth: 0
fetch-depth: 1

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Changed fetch-depth from 0 to 1 for faster, more efficient shallow clone.

Comment on lines 131 to 228
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 }
});
const page = await context.newPage();
// Mock localStorage for auth token
await page.addInitScript(() => {
localStorage.setItem('tankalarm_token', '1234');
});
const htmlDir = '/tmp/html-files';
const screenshots = [
{
html: path.join(htmlDir, 'dashboard.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/dashboard.png',
viewport: { width: 1920, height: 1080 }
},
{
html: path.join(htmlDir, 'client-console.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/client-console.png',
viewport: { width: 1920, height: 1200 }
},
{
html: path.join(htmlDir, 'config-generator.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/config-generator.png',
viewport: { width: 1920, height: 1400 }
},
{
html: path.join(htmlDir, 'serial-monitor.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/serial-monitor.png',
viewport: { width: 1920, height: 1080 }
},
{
html: path.join(htmlDir, 'calibration.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/calibration.png',
viewport: { width: 1920, height: 1200 }
},
{
html: path.join(htmlDir, 'contacts.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/contacts.png',
viewport: { width: 1920, height: 1080 }
},
{
html: path.join(htmlDir, 'server-settings.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/server-settings.png',
viewport: { width: 1920, height: 1600 }
},
{
html: path.join(htmlDir, 'historical.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/historical.png',
viewport: { width: 1920, height: 1200 }
},
{
html: path.join(htmlDir, 'viewer-dashboard.html'),
output: 'TankAlarm-112025-Viewer-BluesOpta/screenshots/dashboard.png',
viewport: { width: 1920, height: 1080 }
}
];
for (const screenshot of screenshots) {
if (!fs.existsSync(screenshot.html)) {
console.log(`Skipping ${screenshot.output} - HTML file not found`);
continue;
}
console.log(`Taking screenshot: ${screenshot.output}`);
await page.setViewportSize(screenshot.viewport);
await page.goto(`file://${screenshot.html}`);
// Wait for page to load
await page.waitForTimeout(2000);
// Run setup function if provided
if (screenshot.setup) {
await screenshot.setup(page);
await page.waitForTimeout(1000);
}
// Ensure output directory exists
const outputDir = path.dirname(screenshot.output);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
await page.screenshot({
path: screenshot.output,
fullPage: true
});
console.log(`✓ Saved ${screenshot.output}`);
}
await browser.close();
console.log('All screenshots completed!');
})();
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Node.js screenshot script doesn't have any error handling around the async operations. If the browser launch fails, page navigation fails, or screenshot capture fails, the errors will be unhandled and could cause the workflow to fail without helpful error messages. Consider wrapping the main async function in a try-catch block and providing meaningful error messages for debugging.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Wrapped the async function in try-catch block with proper error logging and process.exit(1) on failure.

To add or modify screenshots:

1. **Add a new page:**
- Edit `/tmp/take-screenshots.js` in the workflow to add a new entry to the `screenshots` array
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation references editing "/tmp/take-screenshots.js" which is a temporary file generated during workflow execution. Users cannot directly edit this file; they need to edit the workflow YAML file itself (lines 126-229 in update-screenshots.yml). The instruction should clarify that users need to modify the heredoc content in the "Create screenshot script" step of the workflow file.

Suggested change
- Edit `/tmp/take-screenshots.js` in the workflow to add a new entry to the `screenshots` array
- In `.github/workflows/update-screenshots.yml`, edit the heredoc JavaScript in the **"Create screenshot script"** step (which writes `/tmp/take-screenshots.js` during the workflow run) to add a new entry to the `screenshots` array

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a13b84f. Clarified that users need to edit the heredoc in the workflow YAML file's "Create screenshot script" step, not the temporary file directly.

@dorkmo
Copy link
Collaborator

dorkmo commented Jan 16, 2026

@copilot apply changes based on the comments in this thread

- Fix concurrency group to prevent cross-branch conflicts
- Change fetch-depth to 1 for faster checkouts
- Inline CSS into HTML files to fix file:// protocol styling issues
- Improve HTML extraction to stop at next variable declaration
- Add error handling to Node.js screenshot script
- Replace waitForTimeout with waitForLoadState for reliability
- Improve git add logic with better error handling and logging
- Fix sed pattern for more precise timestamp replacement
- Add documentation clarifications for route paths and script editing
- Add logging for extraction failures and CSS inlining

Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 16, 2026

@copilot apply changes based on the comments in this thread

Applied all review feedback in commit a13b84f. Key improvements:

Critical fixes:

  • CSS inlining to solve file:// protocol styling issue
  • HTML extraction logic to prevent variable concatenation

Robustness:

  • Fixed concurrency group for cross-branch safety
  • Added error handling to screenshot script
  • Replaced timeouts with waitForLoadState
  • Improved git operations with explicit checks and logging
  • Optimized checkout with shallow clone

Documentation:

  • Clarified route paths vs file:// URLs
  • Updated script editing instructions
  • Added auth token explanation

All 14 review comments have been addressed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

**Screenshots look broken or incomplete:**
- Check that the HTML extraction step completed successfully
- Verify that the HTML files were created in `/tmp/html-files`
- Ensure the page has time to load (increase `waitForTimeout` if needed)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The troubleshooting documentation references waitForTimeout, but the actual workflow implementation uses waitForLoadState('networkidle') at line 233 of update-screenshots.yml. The documentation should be updated to reflect the current implementation.

Suggested change
- Ensure the page has time to load (increase `waitForTimeout` if needed)
- Ensure the page has time to load (the script uses `page.waitForLoadState('networkidle')`; adjust this or add additional waits if needed)

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4bcfc25. Updated troubleshooting documentation to reference waitForLoadState('networkidle') instead of the outdated waitForTimeout.

Comment on lines +180 to +181
html: path.join(htmlDir, 'client-console.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/client-console.png',
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow generates a screenshot for 'client-console.png', but the WEBSITE_PREVIEW.md file at TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md:13 also references 'client-console-pin-setup.png' which is not being generated. Either add this screenshot to the workflow or remove the reference from WEBSITE_PREVIEW.md to ensure all documented screenshots are actually being generated.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4bcfc25. Removed the client-console-pin-setup.png reference from WEBSITE_PREVIEW.md since this screenshot requires interactive page setup and is not auto-generated by the workflow. The existing manual screenshot file is preserved but won't be automatically updated.

@dorkmo
Copy link
Collaborator

dorkmo commented Jan 16, 2026

@copilot apply changes based on the comments in this thread

- Update troubleshooting docs to reference waitForLoadState instead of waitForTimeout
- Remove client-console-pin-setup.png reference from WEBSITE_PREVIEW.md as this screenshot is not auto-generated

Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 16, 2026

@copilot apply changes based on the comments in this thread

Applied both review comments in commit 4bcfc25:

  1. Updated troubleshooting docs to reference the correct waitForLoadState('networkidle') implementation
  2. Removed PIN setup screenshot reference from WEBSITE_PREVIEW.md to match what the workflow actually generates

The workflow now correctly documents 8 auto-generated server screenshots (not including the PIN setup modal which requires manual updates).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +62 to +79
# Find the variable declaration with R"HTML(...) )HTML" pattern
pattern = rf'static\s+const\s+char\s+{var_name}\[\]\s+PROGMEM\s*=\s*R"HTML\((.*?)\)HTML"'
match = re.search(pattern, content, re.DOTALL)
if match:
html_content = match.group(1)
# Handle multi-part HTML (R"HTML(...)HTML" R"HTML(...)HTML")
# Only search for immediately following parts (before next variable declaration)
remaining = content[match.end():]
# Stop at next 'static const char' or end of remaining content
next_var = re.search(r'static\s+const\s+char\s+', remaining)
search_limit = next_var.start() if next_var else len(remaining)
additional_parts = re.findall(
r'R"HTML\((.*?)\)HTML"',
remaining[:search_limit],
re.DOTALL,
)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern uses a non-greedy match (.*?) which could fail if there are nested patterns or if the HTML contains )HTML anywhere in the content. Consider using a more robust extraction method or adding validation to ensure the pattern correctly captures multi-line HTML content, especially for large files.

Suggested change
# Find the variable declaration with R"HTML(...) )HTML" pattern
pattern = rf'static\s+const\s+char\s+{var_name}\[\]\s+PROGMEM\s*=\s*R"HTML\((.*?)\)HTML"'
match = re.search(pattern, content, re.DOTALL)
if match:
html_content = match.group(1)
# Handle multi-part HTML (R"HTML(...)HTML" R"HTML(...)HTML")
# Only search for immediately following parts (before next variable declaration)
remaining = content[match.end():]
# Stop at next 'static const char' or end of remaining content
next_var = re.search(r'static\s+const\s+char\s+', remaining)
search_limit = next_var.start() if next_var else len(remaining)
additional_parts = re.findall(
r'R"HTML\((.*?)\)HTML"',
remaining[:search_limit],
re.DOTALL,
)
# Find the variable declaration that starts the R"HTML(" raw string
pattern = rf'static\s+const\s+char\s+{var_name}\[\]\s+PROGMEM\s*=\s*R"HTML\('
match = re.search(pattern, content)
if match:
# Extract the first HTML part using explicit string indices
start_index = match.end()
end_marker = ')HTML"'
end_index = content.find(end_marker, start_index)
if end_index == -1:
print(f"Warning: Unterminated HTML raw string for variable '{var_name}' in file '{file_path}'")
return None
html_content = content[start_index:end_index]
# Handle multi-part HTML (R"HTML(...)HTML" R"HTML(...)HTML")
# Only search for immediately following parts (before next variable declaration)
remaining = content[end_index + len(end_marker):]
# Stop at next 'static const char' or end of remaining content
next_var = re.search(r'static\s+const\s+char\s+', remaining)
search_limit = next_var.start() if next_var else len(remaining)
additional_parts = []
search_pos = 0
while True:
part_start = remaining.find('R"HTML(', search_pos, search_limit)
if part_start == -1:
break
content_start = part_start + len('R"HTML(')
part_end = remaining.find(end_marker, content_start, search_limit)
if part_end == -1:
# If we can't find a closing marker within the search limit, stop scanning
break
additional_parts.append(remaining[content_start:part_end])
search_pos = part_end + len(end_marker)

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current regex implementation with non-greedy matching and DOTALL flag handles multi-line HTML correctly and stops at the next variable declaration to avoid concatenation issues. While a more complex string-index-based approach could be more robust against edge cases with ')HTML"' in content, the current solution works well for the Arduino .ino file structure and has been tested successfully. The extraction includes validation logging to help identify any issues.

Comment on lines +173 to +178
const screenshots = [
{
html: path.join(htmlDir, 'dashboard.html'),
output: 'TankAlarm-112025-Server-BluesOpta/screenshots/dashboard.png',
viewport: { width: 1920, height: 1080 }
},
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The screenshots array is hardcoded in the workflow YAML. If a new page is added to the Arduino .ino files but not to this array, it won't be automatically screenshotted. Consider adding a comment above the array warning maintainers that this list must be kept in sync with the HTML variables extracted in the Python script, or implement a more dynamic approach that automatically discovers available HTML files.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ef67577. Added a clear warning comment above the screenshots array explaining it must be kept in sync with the html_vars dictionary in the Python extraction script.

Comment on lines +264 to +270
timestamp=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
# Update Server WEBSITE_PREVIEW.md
if [ -f "TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md" ]; then
sed -i "s/\*Last updated: [^*]*\*/\*Last updated: ${timestamp}\*/" \
TankAlarm-112025-Server-BluesOpta/WEBSITE_PREVIEW.md
fi
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sed regex pattern /[^*]*\\*/ could incorrectly match other markdown italic patterns in the file that aren't the timestamp. The pattern should be more specific to only match the 'Last updated:' line. Consider using a more precise pattern like s/\\*Last updated: .*\\*/\\*Last updated: ${timestamp}\\*/ or anchoring the pattern to ensure it only matches the intended line.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current pattern \*Last updated: [^*]*\* is already quite specific - it matches only text between *Last updated: and the closing *, using [^*]* to match any non-asterisk characters. This prevents incorrect matches with other markdown italic patterns and is precise enough for the use case.

Comment on lines 168 to 170
await page.addInitScript(() => {
localStorage.setItem('tankalarm_token', '1234');
});
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mock token value '1234' is hardcoded. While the comment explains this is a non-sensitive dummy value, consider defining it as a constant at the top of the script (e.g., const MOCK_TOKEN = '1234';) to improve maintainability and make it clear that this is an intentional test value rather than a placeholder that should be replaced.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ef67577. Defined MOCK_TOKEN as a constant at the top of the script and updated the code to use it, improving maintainability and clarity.

await page.goto(`file://${screenshot.html}`);
// Wait for page to fully load
await page.waitForLoadState('networkidle');
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using 'networkidle' as the load state can cause unnecessary delays for pages with polling or continuous network activity. For static HTML files loaded from the filesystem, this wait condition may timeout or wait longer than necessary. Consider using 'domcontentloaded' or 'load' instead, or add a timeout parameter to prevent hanging if the page has active scripts that make network requests.

Suggested change
await page.waitForLoadState('networkidle');
await page.waitForLoadState('load');

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ef67577. Changed from waitForLoadState('networkidle') to waitForLoadState('load') which is more appropriate for static HTML files and avoids unnecessary delays from polling or active scripts.

# For Viewer - extract dashboard
viewer_file = 'TankAlarm-112025-Viewer-BluesOpta/TankAlarm-112025-Viewer-BluesOpta.ino'
if os.path.exists(viewer_file):
viewer_dashboard = extract_html_content(viewer_file, 'VIEWER_DASHBOARD_HTML')
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The viewer dashboard extraction doesn't pass the CSS content to save_html_file, unlike the server pages. This is correct since the viewer HTML has inline styles, but this inconsistency could confuse future maintainers. Consider adding a comment explaining why CSS isn't passed here, or checking if the viewer HTML references /style.css and handling it consistently.

Suggested change
viewer_dashboard = extract_html_content(viewer_file, 'VIEWER_DASHBOARD_HTML')
viewer_dashboard = extract_html_content(viewer_file, 'VIEWER_DASHBOARD_HTML')
# Note: Viewer dashboard HTML uses inline styles, so we intentionally do not pass css_content here.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ef67577. Added a clarifying comment explaining that the viewer dashboard uses inline styles and intentionally doesn't receive css_content parameter.

@dorkmo
Copy link
Collaborator

dorkmo commented Jan 16, 2026

@copilot apply changes based on the comments in this thread

- Define MOCK_TOKEN as constant for better maintainability
- Add warning comment about screenshots array needing sync with html_vars
- Change waitForLoadState from 'networkidle' to 'load' for static HTML files
- Add clarifying comment about viewer CSS handling
- Update documentation to reference 'load' state

Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 16, 2026

@copilot apply changes based on the comments in this thread

Applied review feedback in commit ef67577:

Implemented:

  • Defined MOCK_TOKEN as constant
  • Added sync warning for screenshots array
  • Changed to waitForLoadState('load') for static files
  • Added viewer CSS handling comment
  • Updated documentation

Not implemented (with explanation):

  • Regex extraction rewrite: Current implementation works correctly and includes proper validation
  • sed pattern change: Current pattern is already specific enough

All actionable comments have been addressed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dorkmo dorkmo merged commit 85a6bef into master Jan 16, 2026
6 checks passed
@dorkmo dorkmo deleted the copilot/update-screenshot-github-action branch January 16, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

update screenshot github action

2 participants