-
Notifications
You must be signed in to change notification settings - Fork 12
New App - Klipfolio Power Metrics Dashboard with Passcode Access #238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,14 @@ | ||||||
| <!DOCTYPE html> | ||||||
| <html lang="en"> | ||||||
| <head> | ||||||
| <meta charset="UTF-8"> | ||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
| <title>Klipfolio Dashboard</title> | ||||||
| <link rel="stylesheet" href="styles.css"> | ||||||
| </head> | ||||||
| <body> | ||||||
| <iframe id="dashboard" title="Klipfolio Dashboard"></iframe> | ||||||
| <script src="screenly.js?version=1" defer></script> | ||||||
| <script src="script.js"></script> | ||||||
|
||||||
| <script src="script.js"></script> | |
| <script src="script.js" defer></script> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| syntax: instance_v1 | ||
| id: 01JT3644XKZ7BWNVY1ZQXTW4AG | ||
| name: Klipfolio Dashboard Edge App |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||
| syntax: manifest_v1 | ||||||||||||||||||||||||||
| id: 01JSR8A2CW98PG8B0SR3T0MYX5 | ||||||||||||||||||||||||||
| description: A Klipfolio dashboard edge app for Screenly | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| description: A Klipfolio dashboard edge app for Screenly | |
| description: An edge app that displays a Klipfolio PowerMetrics dashboard within Screenly. | |
| icon: https://assets.screenlyapp.com/apps/klipfolio-power-metrics-dashboard/icon.png | |
| author: Screenly, Inc. | |
| ready_signal: true |
Copilot
AI
Feb 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dashboard_url and dashboard_passcode settings are marked as optional, but the code does not handle the case where they are not provided. In script.js lines 4-5, fallback messages are set ('Dashboard URL not set', 'Passcode not set'), but these will be used as actual values rather than triggering an error. Since the app cannot function without these values, they should be marked as 'optional: false' in the manifest.
| optional: true | |
| help_text: The URL for the dashboard | |
| dashboard_passcode: | |
| type: secret | |
| title: Dashboard Passcode | |
| optional: true | |
| optional: false | |
| help_text: The URL for the dashboard | |
| dashboard_passcode: | |
| type: secret | |
| title: Dashboard Passcode | |
| optional: false |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,68 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* global screenly, Sentry */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* eslint-disable-next-line no-unused-vars, no-useless-catch */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+2
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dashboardUrlRaw = screenly.settings.dashboard_url || 'Dashboard URL not set' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const passcode = screenly.settings.dashboard_passcode || 'Passcode not set' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+5
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Initialize when page loads | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.addEventListener('load', function() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Wait a short time for screenly.js to load | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(function() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get the dashboard iframe | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dashboard = document.getElementById('dashboard') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Construct the URL after screenly.js has loaded | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dashboardUrl | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try to use Screenly's CORS proxy | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dashboardUrl = screenly.cors_proxy_url + encodeURIComponent(dashboardUrlRaw) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fallback to direct URL if screenly is not available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Error accessing screenly.cors_proxy_url, using direct URL", e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dashboardUrl = dashboardUrlRaw; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set dashboard URL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dashboard.src = dashboardUrl | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Attempt to inject passcode after iframe loads | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dashboard.onload = function() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(injectPasscodeIntoIframe, 1000) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(injectPasscodeIntoIframe, 1000) | |
| setTimeout(function() { | |
| // First, try to inject the passcode | |
| injectPasscodeIntoIframe() | |
| // Then signal to Screenly that the app is ready for rendering | |
| try { | |
| if (typeof screenly !== 'undefined' && | |
| typeof screenly.signalReadyForRendering === 'function') { | |
| screenly.signalReadyForRendering() | |
| } | |
| } catch (e) { | |
| console.error("Error calling screenly.signalReadyForRendering:", e) | |
| } | |
| }, 1000) |
Copilot
AI
Feb 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 500ms delay for screenly.js to load is arbitrary and may cause race conditions. The screenly.js script is loaded with the defer attribute, which means it will execute after the DOM is parsed but before the load event. Using a setTimeout with a fixed delay is unreliable. Instead, listen for a screenly-specific event or check for the existence of the screenly object, or move the script loading to use proper defer attributes as seen in other edge apps.
| // Wait a short time for screenly.js to load | |
| setTimeout(function() { | |
| // Get the dashboard iframe | |
| const dashboard = document.getElementById('dashboard') | |
| // Construct the URL after screenly.js has loaded | |
| let dashboardUrl | |
| try { | |
| // Try to use Screenly's CORS proxy | |
| dashboardUrl = screenly.cors_proxy_url + encodeURIComponent(dashboardUrlRaw) | |
| } catch (e) { | |
| // Fallback to direct URL if screenly is not available | |
| console.error("Error accessing screenly.cors_proxy_url, using direct URL", e) | |
| dashboardUrl = dashboardUrlRaw; | |
| } | |
| // Set dashboard URL | |
| dashboard.src = dashboardUrl | |
| // Attempt to inject passcode after iframe loads | |
| dashboard.onload = function() { | |
| setTimeout(injectPasscodeIntoIframe, 1000) | |
| }; | |
| }, 500) // 500ms delay for screenly.js to load | |
| // Get the dashboard iframe | |
| const dashboard = document.getElementById('dashboard') | |
| // Construct the URL after screenly.js has loaded | |
| let dashboardUrl | |
| try { | |
| // Try to use Screenly's CORS proxy | |
| dashboardUrl = screenly.cors_proxy_url + encodeURIComponent(dashboardUrlRaw) | |
| } catch (e) { | |
| // Fallback to direct URL if screenly is not available | |
| console.error("Error accessing screenly.cors_proxy_url, using direct URL", e) | |
| dashboardUrl = dashboardUrlRaw; | |
| } | |
| // Set dashboard URL | |
| dashboard.src = dashboardUrl | |
| // Attempt to inject passcode after iframe loads | |
| dashboard.onload = function() { | |
| setTimeout(injectPasscodeIntoIframe, 1000) | |
| }; |
Copilot
AI
Feb 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing null check for the button element before calling click(). If the submit button with id 'submit' doesn't exist in the iframe document (line 59), the code will throw an error when trying to call button.click() on line 60. Add a null check similar to the one used for the input field.
| button.click() | |
| console.log("Clicked submit button") | |
| if (button) { | |
| button.click() | |
| console.log("Clicked submit button") | |
| } else { | |
| console.log("Submit button not found"); | |
| } |
Copilot
AI
Feb 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approach of injecting passcode into an iframe will not work due to cross-origin security restrictions. Lines 43-46 attempt to catch cross-origin errors, but even if the iframe loads through the CORS proxy, the Same-Origin Policy prevents accessing or manipulating the iframe's DOM from the parent window. This fundamental security restriction means the passcode injection functionality (lines 36-68) cannot work as designed. The TODO comment in the PR description acknowledges CORS and iframe issues that need to be resolved.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||
| /* This file is no longer needed as all styles are defined in the HTML */ | ||||||||||
| /* The file is kept for reference in case styles need to be extracted later */ | ||||||||||
|
Comment on lines
+1
to
+2
|
||||||||||
| /* This file is no longer needed as all styles are defined in the HTML */ | |
| /* The file is kept for reference in case styles need to be extracted later */ | |
| /* Styles for the Klipfolio Power Metrics dashboard layout */ | |
| /* Defines full-screen behavior for the page and embedded dashboard iframe */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CSS and JavaScript files should be organized in a static/ folder structure following the codebase convention. All other edge apps in this repository (iframe, clock, weather, etc.) use static/css/, static/js/, and static/images/ subdirectories rather than placing assets in the root directory. This improves organization and maintainability.