diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index daa3eae57..d57240180 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -8,11 +8,10 @@ - Edge Apps allows you to build custom digital signage content without provisioning or managing servers. - You could think of it as something similar to other serverless technologies like Cloudflare Workers or AWS Lambda. -- More details for Screenly's Edge Apps could be found in [https://developer.screenly.io/edge-apps](mdc:https:/developer.screenly.io/edge-apps). -- This repository contains a variety of Edge Apps like a simple clock app or an app that displays - real-time bus schedules. -- Each of the available Edge Apps have their own directory, which could be found in the - [edge-apps](mdc:edge-apps) directory. +- More details for Screenly's Edge Apps could be found in [https://developer.screenly.io/edge-apps](mdc:https://developer.screenly.io/edge-apps). +- Most Edge Apps have migrated to standalone repositories under the Screenly org. The + [edge-apps](mdc:edge-apps) directory contains the apps that have not migrated yet, redirect stubs + for the apps that have, plus shared assets like icons. ## Players @@ -22,11 +21,11 @@ - Screenly Player, a Raspberry-pi based player - Compatible with Raspberry Pi 3 and 4 devices. - Screenly Player Max, a more powerful alternative to the Screenly Player -- See [https://www.screenly.io/digital-signage-players/](mdc:https:/www.screenly.io/digital-signage-players) for more details about the physical players. +- See [https://www.screenly.io/digital-signage-players/](mdc:https://www.screenly.io/digital-signage-players) for more details about the physical players. - Screenly also offers a virtual alternative, which we call "Screenly Anywhere". -- Screenly Anywhere allows to you to deploy screens with no hardware required. +- Screenly Anywhere allows you to deploy screens with no hardware required. - Screenly Anywhere can be set up on a web browser, on a smartphone, or on a smart TV. - - See [https://www.screenly.io/end-user/screenly-anywhere/](mdc:https:/www.screenly.io/end-user/screenly-anywhere) for more details. + - See [https://www.screenly.io/end-user/screenly-anywhere/](mdc:https://www.screenly.io/end-user/screenly-anywhere) for more details. ## Supported Resolutions diff --git a/.claude/rules/edge-apps.md b/.claude/rules/edge-apps.md index 820c0aeac..0b44f056d 100644 --- a/.claude/rules/edge-apps.md +++ b/.claude/rules/edge-apps.md @@ -53,7 +53,8 @@ This approach provides a better user experience in the web interface by renderin This rule applies to Edge Apps that are written in plain HTML, CSS, and JavaScript. -- Create a directory inside the [edge-apps](mdc:edge-apps) directory. +- If you're one of the maintainers of this repository, it's encouraged to create the new Edge App in its own standalone GitHub repo under the Screenly org, rather than inside this monorepo's `edge-apps/` directory. +- If the app stays in this monorepo, create a directory inside the [edge-apps](mdc:edge-apps) directory. - That new directory should at least contain the following files: - `index.html` - `screenly.yml` diff --git a/.claude/skills/create-an-edge-app/SKILL.md b/.claude/skills/create-an-edge-app/SKILL.md index d75353246..c6c99bcf8 100644 --- a/.claude/skills/create-an-edge-app/SKILL.md +++ b/.claude/skills/create-an-edge-app/SKILL.md @@ -7,11 +7,8 @@ description: Use when scaffolding a new Screenly Edge App — covers the templat ## When Creating an Edge App -- Scaffold the new Edge App using the `bun create` template from inside the `edge-apps/` directory: - ```bash - bun create edge-app-template --no-git - ``` - - The app name should follow the `kebab-case` naming convention. +- If you're one of the maintainers of this repository, it's encouraged to create the new Edge App in its own standalone GitHub repo under the Screenly org, rather than inside this monorepo's `edge-apps/` directory. +- Scaffold the new Edge App by starting from one of the apps in the [Reference Apps](#reference-apps) section below — pick the closest match in complexity and adapt it, following the `kebab-case` naming convention for the app name. - After scaffolding, add an `id` field to `screenly.yml` and `screenly_qc.yml` before running `bun run dev`. - **Verify it boots** before building features: run `bun run dev`, `bun run lint`, and the tests. A scaffold that doesn't start is the first thing to fix. - **Consult Figma designs** before starting implementation. @@ -33,7 +30,7 @@ When the app shows data from a third-party service, **do not hand-roll an auth f To develop locally (real credentials aren't present), set up a **super simple** way to supply them — pick the lighter of these two: -- **Read a secret (CLI is fine).** Declare an `access_token` secret marked "for testing only", set it with `screenly edge-app setting set access_token=...` (or in `mock-data.yml`), and read it with `getSettingWithDefault('access_token', '')`. See `edge-apps/google-calendar/` (`src/main.ts`). +- **Read a secret (CLI is fine).** Declare an `access_token` secret marked "for testing only", set it with `screenly edge-app setting set access_token=...` (or in `mock-data.yml`), and read it with `getSettingWithDefault('access_token', '')`. See [Screenly/google-calendar-app](https://github.com/Screenly/google-calendar-app) (`src/main.ts`). - **Handle the OAuth flow with a tiny companion app.** A small Express + Bun server that runs the flow, stores the tokens, refreshes them, and exposes `GET /access_token/` returning `{ token, metadata }` — mimicking the Screenly OAuth service. Wire it in via `mock-data.yml`'s `screenly_oauth_tokens_url`. See the `mock-authenticator/` in [Screenly/salesforce-app](https://github.com/Screenly/salesforce-app) for a complete, minimal example. Both paths feed the same `getCredentials()` — the Edge App code does not change between them. @@ -51,12 +48,12 @@ Both paths feed the same `getCredentials()` — the Edge App code does not chang ## Reference Apps -For reference on more complex implementations, consult: +Most Edge Apps have migrated to standalone repos under the Screenly org. For reference on more complex implementations, consult: -- QR Code (`edge-apps/qr-code/`) — simple, low-footprint example -- Menu Board (`edge-apps/menu-board/`) — more complex layout -- CAP Alerting (`edge-apps/cap-alerting/`) — advanced settings and data fetching -- Google Calendar (`edge-apps/google-calendar/`) — integration via a test secret +- [Screenly/qr-code-app](https://github.com/Screenly/qr-code-app) — simple, low-footprint example +- [Screenly/menu-board-app](https://github.com/Screenly/menu-board-app) — more complex layout +- [Screenly/cap-alerting-app](https://github.com/Screenly/cap-alerting-app) — advanced settings and data fetching +- [Screenly/google-calendar-app](https://github.com/Screenly/google-calendar-app) — integration via a test secret - [Screenly/salesforce-app](https://github.com/Screenly/salesforce-app) — integration with a companion OAuth authenticator All apps depend on the `@screenly/edge-apps` NPM package and use `edge-apps-scripts` for tooling. diff --git a/.github/workflows/edge-app-checks.yml b/.github/workflows/edge-app-checks.yml index ab9d63192..010872944 100644 --- a/.github/workflows/edge-app-checks.yml +++ b/.github/workflows/edge-app-checks.yml @@ -50,7 +50,7 @@ jobs: # Function to add app to list if not already included add_app_if_valid() { local app="$1" - if [[ -n "$app" && "$app" != "helpers" && "$app" != ".bun-create" ]]; then + if [[ -n "$app" && "$app" != "helpers" ]]; then if [[ -d "edge-apps/$app" ]]; then if [[ " $CHANGED_APPS " != *" $app "* ]]; then CHANGED_APPS="$CHANGED_APPS $app" diff --git a/README.md b/README.md index 71eda9db7..2a3ddcdb0 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,7 @@ If you are not familiar with Edge Apps, we suggest you review our [developer doc ### Creating a New Edge App -To scaffold a new Edge App, run the following from the `edge-apps/` directory: - -```bash -bun create edge-app-template --no-git -``` - -This generates a new app with TypeScript, the Screenly design system, manifest files, and all standard scripts pre-configured. See [`edge-apps/README.md`](/edge-apps/README.md) for full details. +New Edge Apps are encouraged to live in their own standalone GitHub repo under the Screenly org rather than in this monorepo. See [`edge-apps/README.md`](/edge-apps/README.md) for guidance on creating a new app. ### TypeScript Library diff --git a/edge-apps/.bun-create/edge-app-template/.gitignore b/edge-apps/.bun-create/edge-app-template/.gitignore deleted file mode 100644 index aed24721e..000000000 --- a/edge-apps/.bun-create/edge-app-template/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules/ -dist/ -*.log -.DS_Store -screenshots/*.png diff --git a/edge-apps/.bun-create/edge-app-template/.ignore b/edge-apps/.bun-create/edge-app-template/.ignore deleted file mode 100644 index c2658d7d1..000000000 --- a/edge-apps/.bun-create/edge-app-template/.ignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/edge-apps/.bun-create/edge-app-template/README.md b/edge-apps/.bun-create/edge-app-template/README.md deleted file mode 100644 index ffbca6332..000000000 --- a/edge-apps/.bun-create/edge-app-template/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# {{APP_TITLE}} - -## Getting Started - -```bash -bun install -``` - -## Deployment - -Create and deploy the Edge App: - -```bash -screenly edge-app create --name {{APP_NAME}} --in-place -bun run deploy -screenly edge-app instance create -``` - -## Configuration - -The app accepts the following settings via `screenly.yml`: - -| Setting | Description | Type | Default | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------- | -| `display_errors` | Display errors on screen for debugging purposes | optional | `false` | -| `message` | The message to display on screen | required | `Hello, World!` | -| `override_locale` | Override the default locale with a supported language code | optional | `en` | -| `override_timezone` | Override the default timezone with a supported timezone identifier (e.g., `Europe/London`, `America/New_York`). Defaults to the system timezone if left blank | optional | - | - -## Development - -```bash -bun install # Install dependencies -bun run dev # Start development server -``` - -## Testing - -```bash -bun test -``` - -## Screenshots - -Generate screenshots at all supported resolutions: - -```bash -bun run screenshots -``` - -Screenshots are saved to the `screenshots/` directory. diff --git a/edge-apps/.bun-create/edge-app-template/e2e/screenshots.spec.ts b/edge-apps/.bun-create/edge-app-template/e2e/screenshots.spec.ts deleted file mode 100644 index 7a1f052b3..000000000 --- a/edge-apps/.bun-create/edge-app-template/e2e/screenshots.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { test } from '@playwright/test' -import { - createMockScreenlyForScreenshots, - getScreenshotsDir, - RESOLUTIONS, - setupClockMock, - setupScreenlyJsMock, -} from '@screenly/edge-apps/test/screenshots' -import path from 'path' - -const { screenlyJsContent } = createMockScreenlyForScreenshots( - { coordinates: [40.7128, -74.006], location: 'New York, NY' }, - { - display_errors: 'false', - message: 'Hello, World!', - override_locale: 'en', - override_timezone: 'America/New_York', - }, -) - -for (const { width, height } of RESOLUTIONS) { - test(`screenshot ${width}x${height}`, async ({ browser }) => { - const screenshotsDir = getScreenshotsDir() - - const context = await browser.newContext({ viewport: { width, height } }) - const page = await context.newPage() - - await setupClockMock(page) - await setupScreenlyJsMock(page, screenlyJsContent) - - await page.goto('/') - await page.waitForLoadState('networkidle') - - await page.screenshot({ - path: path.join(screenshotsDir, `${width}x${height}.png`), - fullPage: false, - }) - - await context.close() - }) -} diff --git a/edge-apps/.bun-create/edge-app-template/index.html b/edge-apps/.bun-create/edge-app-template/index.html deleted file mode 100644 index 23deb596e..000000000 --- a/edge-apps/.bun-create/edge-app-template/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - {{APP_TITLE}} - - - - -
- -
-

-
-
-
- - - diff --git a/edge-apps/.bun-create/edge-app-template/package.json b/edge-apps/.bun-create/edge-app-template/package.json deleted file mode 100644 index 10f7a7971..000000000 --- a/edge-apps/.bun-create/edge-app-template/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "edge-app-template", - "version": "1.0.0", - "type": "module", - "scripts": { - "prebuild": "bun run type-check", - "generate-mock-data": "screenly edge-app run --generate-mock-data", - "predev": "bun run generate-mock-data", - "dev": "edge-apps-scripts dev", - "build": "edge-apps-scripts build", - "build:dev": "edge-apps-scripts build:dev", - "build:prod": "edge-apps-scripts build", - "test": "bun test --pass-with-no-tests src/", - "test:unit": "bun test --pass-with-no-tests src/", - "lint": "edge-apps-scripts lint --fix", - "format": "prettier --write src/ README.md index.html", - "format:check": "prettier --check src/ README.md index.html", - "deploy": "bun run build && screenly edge-app deploy --path=dist/", - "type-check": "edge-apps-scripts type-check", - "screenshots": "edge-apps-scripts screenshots" - }, - "prettier": "../.prettierrc.json", - "devDependencies": { - "@playwright/test": "^1.58.0", - "@screenly/edge-apps": "^1.0.0", - "@types/bun": "^1.3.9", - "@types/jsdom": "^27.0.0", - "bun-types": "^1.3.9", - "jsdom": "^28.1.0", - "npm-run-all2": "^8.0.4", - "prettier": "^3.8.1", - "typescript": "^5.9.3" - }, - "bun-create": { - "postinstall": "edge-apps-scripts create" - } -} diff --git a/edge-apps/.bun-create/edge-app-template/screenly.yml b/edge-apps/.bun-create/edge-app-template/screenly.yml deleted file mode 100644 index b00b05652..000000000 --- a/edge-apps/.bun-create/edge-app-template/screenly.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -syntax: manifest_v1 -description: {{APP_DESCRIPTION}} -icon: https://playground.srly.io/edge-apps/{{APP_NAME}}/static/img/icon.svg -author: Screenly, Inc. -categories: - - Messaging & Content -ready_signal: true -settings: - display_errors: - type: string - default_value: 'false' - title: Display Errors - optional: true - help_text: - properties: - advanced: true - help_text: For debugging purposes to display errors on the screen. - type: boolean - schema_version: 1 - message: - type: string - default_value: Hello, World! - title: Message - optional: false - help_text: | - The message to display on screen. - override_locale: - type: string - default_value: en - title: Override Locale - optional: true - help_text: | - Override the default locale with a supported language code (e.g., en, fr, de). Defaults to English if not specified. - override_timezone: - type: string - default_value: '' - title: Override Timezone - optional: true - help_text: | - Override the default timezone with a supported timezone identifier (e.g., Europe/London, America/New_York). Defaults to the system timezone if left blank. diff --git a/edge-apps/.bun-create/edge-app-template/screenly_qc.yml b/edge-apps/.bun-create/edge-app-template/screenly_qc.yml deleted file mode 100644 index b00b05652..000000000 --- a/edge-apps/.bun-create/edge-app-template/screenly_qc.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -syntax: manifest_v1 -description: {{APP_DESCRIPTION}} -icon: https://playground.srly.io/edge-apps/{{APP_NAME}}/static/img/icon.svg -author: Screenly, Inc. -categories: - - Messaging & Content -ready_signal: true -settings: - display_errors: - type: string - default_value: 'false' - title: Display Errors - optional: true - help_text: - properties: - advanced: true - help_text: For debugging purposes to display errors on the screen. - type: boolean - schema_version: 1 - message: - type: string - default_value: Hello, World! - title: Message - optional: false - help_text: | - The message to display on screen. - override_locale: - type: string - default_value: en - title: Override Locale - optional: true - help_text: | - Override the default locale with a supported language code (e.g., en, fr, de). Defaults to English if not specified. - override_timezone: - type: string - default_value: '' - title: Override Timezone - optional: true - help_text: | - Override the default timezone with a supported timezone identifier (e.g., Europe/London, America/New_York). Defaults to the system timezone if left blank. diff --git a/edge-apps/.bun-create/edge-app-template/src/css/style.css b/edge-apps/.bun-create/edge-app-template/src/css/style.css deleted file mode 100644 index 5311ce50b..000000000 --- a/edge-apps/.bun-create/edge-app-template/src/css/style.css +++ /dev/null @@ -1,68 +0,0 @@ -@import '@screenly/edge-apps/styles'; - -* { - box-sizing: border-box; -} - -body { - margin: 0; - padding: 0; - overflow: hidden; - color: white; - font-family: 'Inter', system-ui, sans-serif; - background: url('/static/images/bg.webp') no-repeat center center; - background-size: cover; -} - -body::before { - content: ''; - position: fixed; - inset: 0; - z-index: var(--z-index-backdrop); - pointer-events: none; - background: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(7px); -} - -auto-scaler { - position: relative; - z-index: var(--z-index-content); -} - -#app { - width: 100%; - height: 100%; -} - -.content { - display: flex; - justify-content: center; - align-items: center; - height: calc(100% - 5rem); -} - -#message { - font-size: 4rem; - font-weight: 300; - text-align: center; - text-shadow: 0 0.125rem 0.375rem rgba(0, 0, 0, 0.25); - margin: 0; - padding: 0 4rem; -} - -@media (orientation: portrait) { - #app { - display: flex; - flex-direction: column; - height: 100%; - } - - .content { - flex: 1; - height: auto; - } - - #message { - font-size: 3rem; - } -} diff --git a/edge-apps/.bun-create/edge-app-template/src/main.ts b/edge-apps/.bun-create/edge-app-template/src/main.ts deleted file mode 100644 index a78f407db..000000000 --- a/edge-apps/.bun-create/edge-app-template/src/main.ts +++ /dev/null @@ -1,21 +0,0 @@ -import './css/style.css' -import '@screenly/edge-apps/components' -import { - getSettingWithDefault, - setupErrorHandling, - setupTheme, - signalReady, -} from '@screenly/edge-apps' - -document.addEventListener('DOMContentLoaded', () => { - setupErrorHandling() - setupTheme() - - const message = getSettingWithDefault('message', '') - const messageEl = document.querySelector('#message') - if (messageEl && message) { - messageEl.textContent = message - } - - signalReady() -}) diff --git a/edge-apps/.bun-create/edge-app-template/static/images/bg.webp b/edge-apps/.bun-create/edge-app-template/static/images/bg.webp deleted file mode 100644 index 12bb16571..000000000 Binary files a/edge-apps/.bun-create/edge-app-template/static/images/bg.webp and /dev/null differ diff --git a/edge-apps/README.md b/edge-apps/README.md index 7b9feafa9..88cb7eb7c 100644 --- a/edge-apps/README.md +++ b/edge-apps/README.md @@ -4,19 +4,9 @@ This directory contains all Screenly Edge Apps in this repository. ## Creating a New Edge App -From this directory, run: +It's encouraged to create new Edge Apps in their own standalone GitHub repo under the Screenly org rather than in this directory. If you're a maintainer, start from one of the existing standalone apps (see the reference apps listed in the [`create-an-edge-app` skill](/.claude/skills/create-an-edge-app/SKILL.md)) that's closest in complexity to what you're building, and adapt it. -```bash -bun create edge-app-template --no-git -``` - -For example: - -```bash -bun create edge-app-template --no-git my-new-app -``` - -This scaffolds a new app under `edge-apps//` with TypeScript, the Screenly design system, manifest files, screenshot tests, and all standard scripts pre-configured. +If the app must stay in this monorepo, scaffold it under `edge-apps//` by copying the structure of an existing app in this directory (TypeScript, the Screenly design system, manifest files, screenshot tests, and standard scripts). After scaffolding: