Skip to content

Conversation

@iamplugged
Copy link
Contributor

@iamplugged iamplugged commented Jan 20, 2026

User description

@screenly/edge-apps is a TypeScript library and tooling set for building Screenly Edge Apps. It provides:

  • Runtime helpers for working with Edge Apps metadata, settings, themes, locales, time formatting, and screen information.
  • UI components (header, brand logo, dev tools, auto-scaler) to quickly scaffold consistent, production-ready apps.
  • A template app and CLI scripts to generate new Edge Apps with a batteries‑included setup.

Use this package when you want to build modern, Vite-based Edge Apps that follow Screenly’s best practices out of the box.

How is it structured?

At a high level:

  • src/: Source for the library itself.
    • src/components/: Reusable UI components (app-header, brand-logo, dev-tools, auto-scaler, and registry utilities).
    • src/core/: Core entry points for Edge Apps runtime helpers. This is there for backward compatibility.
    • src/styles/: Base styles and design tokens (fonts, colors, spacing, typography, etc.).
    • src/utils/: Utility functions for locale, time/date formatting, metadata, screen data, settings, theme, UTM, and weather.
    • src/types/: Shared TypeScript types.
    • src/test/: Test helpers and JSDOM setup for unit tests.
  • template/: A minimal Edge App template (Vite + TypeScript + Tailwind CSS) used as the starting point when creating new apps.
  • scripts/: CLI helpers (cli.ts, create.ts) used to generate apps from the template.
  • bin/edge-apps-scripts.ts: The published CLI entry point (wired via the edge-apps-scripts binary in package.json).

PR Type

Enhancement, Tests


Description

  • Add core web components (auto-scaler, header, dev-tools, logo)

  • Integrate CLI generator and app scaffolding scripts

  • Provide sample apps (clock-new, weather-new) with Vite configs

  • Include screen & weather utilities with unit tests


File Walkthrough

Relevant files
Enhancement
13 files
auto-scaler.ts
Implement `` web component for scaling           
+346/-0 
app-header.ts
Create `` component with time/date display     
+266/-0 
dev-tools.ts
Add `` overlay for development               
+213/-0 
brand-logo.ts
Introduce `` component with fallback                 
+215/-0 
index.ts
Export and register all web components                                     
+12/-0   
index.ts
Add `initEdgeApp` helper for declarative setup                     
+108/-0 
index.ts
Define orientation and auto-scaler option types                   
+50/-0   
screen.ts
Add screen orientation detection utilities                             
+35/-0   
weather.ts
Provide weather icon mapping utilities                                     
+97/-0   
cli.ts
Extend CLI with `create` command                                                 
+15/-0   
main.ts
Add Weather App main logic and rendering                                 
+303/-0 
main.ts
Add Clock App main logic and UI updates                                   
+161/-0 
index.ts
Consolidate exports for utils, core, components, types     
+10/-1   
Configuration changes
4 files
create.ts
Implement app generator CLI script                                             
+198/-0 
vite.config.ts
Configure Vite for library with custom plugins                     
+211/-0 
vite.config.ts
Setup Vite config for weather-new app                                       
+211/-0 
vite.config.ts
Setup Vite config for clock-new app                                           
+207/-0 
Tests
1 files
screen.test.ts
Add unit tests for screen utilities                                           
+139/-0 
Additional files
55 files
package.json +4/-4     
.prettierrc.json +6/-0     
README.md +62/-0   
eslint.config.ts +15/-0   
index.html +34/-0   
package.json +32/-0   
postcss.config.js +7/-0     
screenly.yml +16/-0   
styles.css +156/-0 
vite-env.d.ts +12/-0   
tailwind.config.js +12/-0   
tsconfig.json +34/-0   
README.md +387/-78
tailwind.config.base.js +36/-0   
index.html [link]   
package.json +20/-13 
README.md +166/-0 
register.ts +10/-0   
vite-env.d.ts +12/-0   
fonts.css +31/-0   
index.css +9/-0     
reset.css +39/-0   
index.css +11/-0   
colors.css +4/-0     
index.css +11/-0   
shadows.css +8/-0     
spacing.css +6/-0     
typography.css +9/-0     
index.ts +2/-0     
locale.ts +4/-1     
.prettierrc.json +6/-0     
README.md +62/-0   
eslint.config.ts +15/-0   
index.html +81/-0   
package.json +32/-0   
postcss.config.js +7/-0     
screenly.yml +14/-0   
screenly_qc.yml +17/-0   
main.ts +63/-0   
styles.css +20/-0   
vite-env.d.ts +12/-0   
tailwind.config.js +36/-0   
tsconfig.json +34/-0   
.prettierrc.json +6/-0     
README.md +62/-0   
eslint.config.ts +15/-0   
index.html +49/-0   
package.json +32/-0   
postcss.config.js +7/-0     
screenly.yml +50/-0   
styles.css +232/-0 
vite-env.d.ts +16/-0   
tailwind.config.js +36/-0   
tsconfig.json +34/-0   
package.json +9/-0     

@github-actions
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

ResizeObserver scope

The component uses a ResizeObserver on document.body (fallback to window.resize) which may not catch all layout changes or could lead to unnecessary observations; consider observing the host element or always relying on window resize for consistency and tearing down observers properly.

private setupResizeHandling(): void {
  this.teardownResizeHandling()

  // Use ResizeObserver if available, fallback to window resize
  if (typeof ResizeObserver !== 'undefined') {
    this.resizeObserver = new ResizeObserver(() => {
      this.debouncedResize()
    })
    this.resizeObserver.observe(document.body)
  } else {
Path replacements

The template copy logic uses replaceAll on placeholder keys and constructs relative paths which could break on edge cases (e.g., Windows backslashes or nested templates); verify replacement correctness and cross-platform path handling.

  let content = fs.readFileSync(srcPath, 'utf8')

  // Replace placeholders
  for (const [key, value] of Object.entries(replacements)) {
    content = content.replaceAll(key, value)
  }

  fs.writeFileSync(destPath, content)
}
Sleet mapping

The getWeatherIconKey logic groups all IDs 600–699 under 'snow', making the 'sleet' night override unreachable; ensure sleet conditions (e.g., ID 611/612) map correctly or adjust the range check.

if (id >= 200 && id <= 299) {
  icon = 'thunderstorm'
} else if (id >= 300 && id <= 399) {
  icon = 'drizzle'
} else if (id >= 500 && id <= 599) {
  icon = 'rain'
} else if (id >= 600 && id <= 699) {
  icon = 'snow'
} else if (id >= 700 && id <= 799) {
  icon = 'haze'
} else if (id === 800) {
  icon = 'clear'
} else if (id === 801) {
  icon = 'partially-cloudy'
} else if (id >= 802 && id <= 804) {
  icon = 'mostly-cloudy'
}

@github-actions
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Point to compiled distribution

The package.json points main/module/types to TypeScript source files which won’t be
consumable by Node or bundlers. Point these fields to the compiled JavaScript output
(in dist) and corresponding declaration files to ensure correct resolution.

edge-apps/edge-apps-library/package.json [6-8]

-"main": "./src/index.ts",
-"module": "./src/index.ts",
-"types": "./src/index.ts",
+"main": "./dist/index.js",
+"module": "./dist/index.js",
+"types": "./dist/index.d.ts",
Suggestion importance[1-10]: 9

__

Why: The package.json should reference compiled JavaScript and declaration files (./dist/index.js, ./dist/index.d.ts) so Node and bundlers can properly consume the package instead of raw TypeScript.

High
General
Use Tailwind directives

Replace Tailwind CSS @import rules with the official @tailwind directives to ensure
proper layer processing by Tailwind’s PostCSS plugin.

edge-apps/edge-apps-library/template/src/styles.css [6-8]

-@import 'tailwindcss/base';
-@import 'tailwindcss/components';
-@import 'tailwindcss/utilities';
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
Suggestion importance[1-10]: 7

__

Why: Replacing @import 'tailwindcss/*' with @tailwind directives ensures Tailwind’s PostCSS plugin processes the base, components, and utilities layers correctly rather than importing raw CSS files.

Medium
Show correct temperature unit

The temperature unit label is hard-coded to a degree symbol only. Use the computed
scale (C or F) to display the correct unit next to the degree symbol.

edge-apps/weather-new/src/main.ts [254-256]

 const tempScale = getTempScale(locationData.country)
 setText(cityEl, cityName)
-setText(tempUnitEl, '°')
+setText(tempUnitEl, `°${tempScale}`)
Suggestion importance[1-10]: 6

__

Why: The code always displays only °, omitting the actual unit. Appending tempScale ensures the user sees °C or °F, improving clarity.

Low
Observe documentElement for resize

Observing document.body may not catch viewport‐size changes reliably. Instead,
observe document.documentElement or fallback to window resize events alongside the
observer for robust resizing detection.

edge-apps/edge-apps-library/src/components/auto-scaler/auto-scaler.ts [264-271]

 if (typeof ResizeObserver !== 'undefined') {
   this.resizeObserver = new ResizeObserver(() => {
     this.debouncedResize()
   })
-  this.resizeObserver.observe(document.body)
+  // Observe the root element for viewport changes
+  this.resizeObserver.observe(document.documentElement)
+  // Also listen to window resize as a fallback
+  window.addEventListener('resize', this.boundDebouncedResize)
 } else {
   window.addEventListener('resize', this.boundDebouncedResize)
 }
Suggestion importance[1-10]: 5

__

Why: Observing document.body may miss viewport size changes in some cases. Switching to document.documentElement and also listening to window.resize makes resize detection more robust.

Low
Simplify input validation loop

The prompt loop logic and indentation are confusing and may skip feedback. Refactor
to a do...while loop to ensure the user is prompted until a non-empty name is
entered.

edge-apps/edge-apps-library/scripts/create.ts [82-87]

 let appName = ''
-while (!appName || appName.trim() === '') {
+do {
   appName = await question(rl, 'App name (e.g., my-dashboard): ')
-if (!appName || appName.trim() === '') {
+  if (!appName.trim()) {
     console.log('❌ App name is required. Please try again.\n')
   }
-}
+} while (!appName.trim())
Suggestion importance[1-10]: 4

__

Why: Refactoring the while loop into a do…while makes the prompt flow clearer and avoids the awkward indentation, improving maintainability.

Low

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant