diff --git a/bun.lock b/bun.lock index 033607d..767f963 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "clock", "devDependencies": { "@playwright/test": "^1.60.0", - "@screenly/edge-apps": "^1.2.1", + "@screenly/edge-apps": "^1.5.0", "@types/bun": "^1.3.14", "@types/jsdom": "^28.0.3", "bun-types": "^1.3.14", @@ -44,7 +44,7 @@ "@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], - "@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], @@ -120,53 +120,57 @@ "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="], - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.3", "", { "dependencies": { "@img/sharp-wasm32": "0.35.3" }, "os": "freebsd" }, "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg=="], - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="], - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="], - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="], - "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="], - "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw=="], - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.2", "", { "os": "linux", "cpu": "none" }, "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w=="], - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="], - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="], - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="], - "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="], - "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.2" }, "os": "linux", "cpu": "ppc64" }, "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA=="], - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.2" }, "os": "linux", "cpu": "none" }, "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="], - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="], - "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.3", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w=="], - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.3", "", { "dependencies": { "@img/sharp-wasm32": "0.35.3" }, "cpu": "none" }, "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], @@ -414,7 +418,7 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], - "@screenly/edge-apps": ["@screenly/edge-apps@1.2.1", "", { "dependencies": { "@eslint/js": "^10.0.1", "@photostructure/tz-lookup": "^11.5.0", "@sentry/browser": "^10.59.0", "@tailwindcss/vite": "^4.2.1", "country-locale-map": "^1.9.12", "dayjs": "^1.11.19", "eslint": "^10.0.3", "jiti": "^2.6.1", "jsdom": "^28.1.0", "offline-geocode-city": "^1.0.2", "panic-overlay": "^1.0.51", "sharp": "^0.34.5", "tailwindcss": "^4.2.1", "typescript": "^5.9.3", "typescript-eslint": "^8.57.0", "vite": "^8.1.2", "yaml": "^2.8.2" }, "peerDependencies": { "@playwright/test": "^1.58.0" }, "optionalPeers": ["@playwright/test"], "bin": { "edge-apps-scripts": "bin/edge-apps-scripts.js" } }, "sha512-9CTA8eaYtVlrACHWU9uhml3Yf7hIgc6mELmXhqDdHKDC2fmrgma3sAOpvr0sBjTJr37QRvpeqUjlZ9BJlwT0nQ=="], + "@screenly/edge-apps": ["@screenly/edge-apps@1.5.0", "", { "dependencies": { "@eslint/js": "^10.0.1", "@photostructure/tz-lookup": "^11.5.0", "@sentry/browser": "^10.59.0", "@tailwindcss/vite": "^4.2.1", "country-locale-map": "^1.9.12", "dayjs": "^1.11.19", "eslint": "^10.0.3", "jiti": "^2.6.1", "jsdom": "^28.1.0", "offline-geocode-city": "^1.0.2", "panic-overlay": "^1.0.51", "sharp": "^0.35.3", "tailwindcss": "^4.2.1", "typescript": "^5.9.3", "typescript-eslint": "^8.57.0", "vite": "^8.1.2", "yaml": "^2.8.2" }, "peerDependencies": { "@playwright/test": "^1.58.0" }, "optionalPeers": ["@playwright/test"], "bin": { "edge-apps-scripts": "bin/edge-apps-scripts.js" } }, "sha512-yEO2KA61p6FHTYQUuw1ffyAcDDoxhiCrj8iQ2evAw9OCid2yYQ5Z2zJbAWebGfhxSo2NzfUEcC9O7rZd+VS7OA=="], "@sentry/browser": ["@sentry/browser@10.67.0", "", { "dependencies": { "@sentry/browser-utils": "10.67.0", "@sentry/conventions": "^0.16.0", "@sentry/core": "10.67.0", "@sentry/feedback": "10.67.0", "@sentry/replay": "10.67.0", "@sentry/replay-canvas": "10.67.0" } }, "sha512-/ZhsAvte4rYhg0A0RtSFFgAgXhyMOfQIeOAfMfptN+X6IVSYOfkA9jtrP+Ej4+6vlaUFWRir1HweF56y63dEEA=="], @@ -874,11 +878,11 @@ "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], - "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], - "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + "sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.3", "@img/sharp-darwin-x64": "0.35.3", "@img/sharp-freebsd-wasm32": "0.35.3", "@img/sharp-libvips-darwin-arm64": "1.3.2", "@img/sharp-libvips-darwin-x64": "1.3.2", "@img/sharp-libvips-linux-arm": "1.3.2", "@img/sharp-libvips-linux-arm64": "1.3.2", "@img/sharp-libvips-linux-ppc64": "1.3.2", "@img/sharp-libvips-linux-riscv64": "1.3.2", "@img/sharp-libvips-linux-s390x": "1.3.2", "@img/sharp-libvips-linux-x64": "1.3.2", "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", "@img/sharp-libvips-linuxmusl-x64": "1.3.2", "@img/sharp-linux-arm": "0.35.3", "@img/sharp-linux-arm64": "0.35.3", "@img/sharp-linux-ppc64": "0.35.3", "@img/sharp-linux-riscv64": "0.35.3", "@img/sharp-linux-s390x": "0.35.3", "@img/sharp-linux-x64": "0.35.3", "@img/sharp-linuxmusl-arm64": "0.35.3", "@img/sharp-linuxmusl-x64": "0.35.3", "@img/sharp-webcontainers-wasm32": "0.35.3", "@img/sharp-win32-arm64": "0.35.3", "@img/sharp-win32-ia32": "0.35.3", "@img/sharp-win32-x64": "0.35.3" } }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -976,9 +980,19 @@ "@jsheaven/easybuild/typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="], + "@parcel/core/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "@parcel/node-resolver-core/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "@parcel/package-manager/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "@parcel/source-map/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], - "@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], + "@parcel/transformer-babel/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "@parcel/transformer-js/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "@parcel/transformer-postcss/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "@screenly/edge-apps/jsdom": ["jsdom@28.1.0", "", { "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", "@bramus/specificity": "^2.4.2", "@exodus/bytes": "^1.11.0", "cssstyle": "^6.0.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "undici": "^7.21.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug=="], @@ -1000,6 +1014,8 @@ "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "@typescript-eslint/typescript-estree/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], diff --git a/index.html b/index.html index 12e0001..bb90b18 100644 --- a/index.html +++ b/index.html @@ -12,29 +12,19 @@ reference-height="1080" orientation="auto" > -
- -
-
-
--
-
- --:-- - -- -
-
- -- -
- weather icon - --° -
-
-
-
+
+

--

+
+ --:-- + +
+
+ -- + + + --° + +
diff --git a/package.json b/package.json index 9b18253..0158f88 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dependencies": {}, "prettier": "./.prettierrc.json", "devDependencies": { - "@screenly/edge-apps": "^1.2.1", + "@screenly/edge-apps": "^1.5.0", "@playwright/test": "^1.60.0", "@types/bun": "^1.3.14", "@types/jsdom": "^28.0.3", diff --git a/screenly.yml b/screenly.yml index a4cac2a..1af27d9 100644 --- a/screenly.yml +++ b/screenly.yml @@ -43,6 +43,23 @@ settings: 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. You can find the available timezones here: https://momentjs.com/timezone/ + style: + type: string + default_value: traditional + title: Style + optional: true + help_text: + properties: + help_text: Select the visual style for the clock. + options: + - label: Traditional + value: traditional + - label: Modern + value: modern + - label: Minimal + value: minimal + type: select + schema_version: 1 tag_manager_id: type: string default_value: GTM-P98SPZ9Z diff --git a/screenly_qc.yml b/screenly_qc.yml index 28dded1..00a6039 100644 --- a/screenly_qc.yml +++ b/screenly_qc.yml @@ -43,6 +43,23 @@ settings: 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. You can find the available timezones here: https://momentjs.com/timezone/ + style: + type: string + default_value: traditional + title: Style + optional: true + help_text: + properties: + help_text: Select the visual style for the clock. + options: + - label: Traditional + value: traditional + - label: Modern + value: modern + - label: Minimal + value: minimal + type: select + schema_version: 1 tag_manager_id: type: string default_value: GTM-P98SPZ9Z diff --git a/screenshots/1080x1920.webp b/screenshots/1080x1920.webp index 21a5e68..5cfe2cd 100644 Binary files a/screenshots/1080x1920.webp and b/screenshots/1080x1920.webp differ diff --git a/screenshots/1280x720.webp b/screenshots/1280x720.webp index 3d0df4a..e9aa943 100644 Binary files a/screenshots/1280x720.webp and b/screenshots/1280x720.webp differ diff --git a/screenshots/1920x1080.webp b/screenshots/1920x1080.webp index 1b2de67..b88f4a0 100644 Binary files a/screenshots/1920x1080.webp and b/screenshots/1920x1080.webp differ diff --git a/screenshots/2160x3840.webp b/screenshots/2160x3840.webp index 2172827..c7be4cc 100644 Binary files a/screenshots/2160x3840.webp and b/screenshots/2160x3840.webp differ diff --git a/screenshots/2160x4096.webp b/screenshots/2160x4096.webp index 0c97b29..c5e7254 100644 Binary files a/screenshots/2160x4096.webp and b/screenshots/2160x4096.webp differ diff --git a/screenshots/3840x2160.webp b/screenshots/3840x2160.webp index 4436e27..55ecb92 100644 Binary files a/screenshots/3840x2160.webp and b/screenshots/3840x2160.webp differ diff --git a/screenshots/4096x2160.webp b/screenshots/4096x2160.webp index bf558ad..c8e74a9 100644 Binary files a/screenshots/4096x2160.webp and b/screenshots/4096x2160.webp differ diff --git a/screenshots/480x800.webp b/screenshots/480x800.webp index 7d3f73a..64c5edd 100644 Binary files a/screenshots/480x800.webp and b/screenshots/480x800.webp differ diff --git a/screenshots/720x1280.webp b/screenshots/720x1280.webp index 83a3fc1..ff0edcc 100644 Binary files a/screenshots/720x1280.webp and b/screenshots/720x1280.webp differ diff --git a/screenshots/800x480.webp b/screenshots/800x480.webp index ffffe32..bfeb497 100644 Binary files a/screenshots/800x480.webp and b/screenshots/800x480.webp differ diff --git a/src/css/style.css b/src/css/style.css index 952eb6e..a401284 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1,43 +1,76 @@ -/* Import Screenly Design System */ @import '@screenly/edge-apps/styles'; -/* App-specific styles */ - -@font-face { - font-family: 'Kelly Slab'; - src: url('/static/fonts/KellySlab-Regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; - font-display: swap; -} - -* { - box-sizing: border-box; +:root { + --clock-accent: var(--theme-color-primary, #972eff); + --clock-canvas: #08080d; + --clock-ink: #fff; + --clock-ink-dim: rgb(255 255 255 / 55%); + --clock-rule: rgb(255 255 255 / 12%); } 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; - text-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); -} + --clock-gap: 2.5rem; + --clock-place-size: 2.5rem; + --clock-place-tracking: 0.15em; + --clock-place-transform: uppercase; + --clock-place-weight: 500; + --clock-time-size: 30rem; + --clock-time-tracking: -0.03em; + --clock-time-weight: 500; + --clock-period-size: 5rem; + --clock-meta-size: 2.25rem; + --clock-wash: radial-gradient( + ellipse 75% 50% at 50% 0%, + color-mix(in srgb, var(--clock-accent) 45%, transparent), + transparent + ); -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); + color: var(--clock-ink); + background-color: var(--clock-canvas); + background-image: var(--clock-wash); +} + +body.is-light-brand { + --clock-canvas: #f6f6f8; + --clock-ink: #0a0a0f; + --clock-ink-dim: rgb(10 10 15 / 58%); + --clock-rule: rgb(10 10 15 / 14%); +} + +body.style-modern { + --clock-gap: 1.5rem; + --clock-place-size: 2.75rem; + --clock-place-tracking: -0.01em; + --clock-place-transform: none; + --clock-place-weight: 600; + --clock-time-size: 33rem; + --clock-time-tracking: -0.055em; + --clock-time-weight: 600; + --clock-period-size: 5.5rem; + --clock-meta-size: 2rem; + --clock-wash: radial-gradient( + ellipse 70% 65% at 12% 0%, + color-mix(in srgb, var(--clock-accent) 55%, transparent), + transparent + ); } -.text-shadow { - text-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); +body.style-minimal { + --clock-gap: 4.5rem; + --clock-place-size: 1.75rem; + --clock-place-tracking: 0.45em; + --clock-place-transform: uppercase; + --clock-place-weight: 400; + --clock-time-size: 23rem; + --clock-time-tracking: 0.01em; + --clock-time-weight: 400; + --clock-period-size: 3.25rem; + --clock-meta-size: 1.75rem; + --clock-wash: radial-gradient( + ellipse 90% 60% at 50% 0%, + color-mix(in srgb, var(--clock-accent) 20%, transparent), + transparent + ); } auto-scaler { @@ -45,118 +78,142 @@ auto-scaler { z-index: var(--z-index-content); } -#app { +.clock { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--clock-gap); width: 100%; height: 100%; + text-align: center; } -.content { - margin-top: 160px; - margin-left: 300px; +.clock-place { + margin: 0; + color: var(--clock-ink-dim); + font-size: var(--clock-place-size); + font-weight: var(--clock-place-weight); + letter-spacing: var(--clock-place-tracking); + text-transform: var(--clock-place-transform); } -.current { +.clock-time { display: flex; - flex-direction: column; + align-items: baseline; + justify-content: center; + gap: 1.5rem; } -.city { - font-size: 32px; - text-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); +.clock-time-value { + font-size: var(--clock-time-size); + font-weight: var(--clock-time-weight); + line-height: 0.82; + letter-spacing: var(--clock-time-tracking); + font-variant-numeric: tabular-nums; } -.time-wrapper { - display: flex; - align-items: baseline; - gap: 0.5rem; +.clock-time-period { + color: var(--clock-ink-dim); + font-size: var(--clock-period-size); + font-weight: var(--clock-time-weight); } -.time { - font-family: 'Kelly Slab', serif; - font-size: 160px; - font-weight: 400; - line-height: 1; - letter-spacing: -5px; - margin: 1rem 0; - text-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); +.clock-meta { + display: flex; + align-items: center; + justify-content: center; + gap: 1.75rem; + color: var(--clock-ink-dim); + font-size: var(--clock-meta-size); } -.clock-period { - font-size: 2.25rem; - opacity: 0.9; +.clock-weather { + display: none; + align-items: center; + gap: 0.75rem; + padding-left: 1.75rem; + border-left: 0.0625rem solid var(--clock-rule); } -.status-row { +.clock-weather.is-visible { display: inline-flex; - align-items: center; - gap: 1rem; - padding: 0.625rem 1.25rem; - border-radius: 1rem; - border: 1px solid rgba(255, 255, 255, 0.06); - background: linear-gradient( - 135deg, - rgba(255, 255, 255, 0.06) 0%, - rgba(255, 255, 255, 0.01) 100% - ); - box-shadow: - 0 13.438px 40.315px 0 rgba(0, 0, 0, 0.15), - 0 1.68px 0 0 rgba(255, 255, 255, 0.1) inset, - 0 -1.68px 0 0 rgba(255, 255, 255, 0.03) inset; - width: fit-content; } -.date { - font-size: 1rem; - text-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); +.clock-weather-icon { + width: 2.5rem; + height: 2.5rem; } -.temperature-wrapper { - display: flex; - align-items: center; - gap: 0.375rem; +.style-modern .clock { + align-items: flex-start; + padding: 0 5rem; + text-align: left; +} + +.style-modern .clock-place { + color: var(--clock-ink); +} + +.style-modern .clock-time, +.style-modern .clock-meta { + justify-content: flex-start; } -.temperature { - font-size: 1rem; - text-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); +.style-minimal .clock-weather { + padding-left: 0; + border-left: 0; } -.temperature-icon { - width: 1rem; - height: 1rem; +.style-minimal .clock-meta { + gap: 2.5rem; +} + +.style-minimal .clock-weather-icon { + width: 1.75rem; + height: 1.75rem; } @media (orientation: portrait) { - #app { - display: flex; - flex-direction: column; - height: 100%; + body { + --clock-gap: 4.5rem; + --clock-place-size: 3rem; + --clock-time-size: 19rem; + --clock-period-size: 4rem; + --clock-meta-size: 2.75rem; } - main.content { - margin: 0; - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - flex: 1; - height: 100%; - padding-left: 60px; + body.style-modern { + --clock-gap: 2.5rem; + --clock-place-size: 3rem; + --clock-time-size: 20rem; + --clock-period-size: 4.5rem; + --clock-meta-size: 2.5rem; } - .current { - margin-left: 60px; + body.style-minimal { + --clock-gap: 6rem; + --clock-place-size: 2rem; + --clock-time-size: 15rem; + --clock-period-size: 2.75rem; + --clock-meta-size: 2rem; } - .city { - font-size: 40px; + .clock-meta { + flex-direction: column; + gap: 2rem; + } + + .clock-weather { + padding-left: 0; + border-left: 0; } - .time { - font-size: 180px; + .style-modern .clock { + padding: 0 3.5rem; } - .status-row { - font-size: 24px; + .style-modern .clock-meta { + align-items: flex-start; } } diff --git a/src/main.ts b/src/main.ts index 3c8ea2f..9e0d987 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,47 +4,30 @@ import { getMetadata, getTimeZone, getLocale, + isLightColor, + setupTheme, signalReady, getCityInfo, } from '@screenly/edge-apps' -// Import components to register them as custom elements -// This registers , , , and +// Side-effect import: registers as a custom element import '@screenly/edge-apps/components' import { getWeatherData } from './weather' import { getTimeData } from './time' +import { getClockStyle } from './style' -// Note: Auto-scaling and dev tools are now handled declaratively in index.html -// via and web components +const WEATHER_REFRESH_MS = 15 * 60 * 1000 -// DOM elements (will be initialized in DOMContentLoaded) let locationEl: Element | null let timeEl: Element | null let periodEl: Element | null let dateEl: Element | null let temperatureEl: Element | null let weatherIconEl: HTMLImageElement | null -let temperatureWrapperEl: Element | null +let weatherEl: Element | null -// State let timezone: string = 'UTC' let locale: string = 'en' -let locationName: string = 'Unknown Location' -// Hide temperature section helper -function hideTemperatureSection() { - if (temperatureWrapperEl) { - ;(temperatureWrapperEl as HTMLElement).style.display = 'none' - } -} - -// Show temperature section helper -function showTemperatureSection() { - if (temperatureWrapperEl) { - ;(temperatureWrapperEl as HTMLElement).style.display = '' - } -} - -// Update weather display async function updateWeatherDisplay( latitude: number, longitude: number, @@ -53,12 +36,8 @@ async function updateWeatherDisplay( ) { const weatherData = await getWeatherData(latitude, longitude, tz, countryCode) - if (!weatherData) { - hideTemperatureSection() - return - } - - showTemperatureSection() + weatherEl?.classList.toggle('is-visible', Boolean(weatherData)) + if (!weatherData) return if (temperatureEl) { temperatureEl.textContent = weatherData.displayText @@ -70,7 +49,6 @@ async function updateWeatherDisplay( } } -// Update time display function updateTime() { const now = new Date() const data = getTimeData(now, locale, timezone) @@ -88,55 +66,43 @@ function updateTime() { } } -// Initialize app document.addEventListener('DOMContentLoaded', async () => { try { - // Query DOM elements now that DOM is ready locationEl = document.querySelector('[data-location]') timeEl = document.querySelector('[data-time]') periodEl = document.querySelector('[data-period]') dateEl = document.querySelector('[data-date]') temperatureEl = document.querySelector('[data-temperature]') - weatherIconEl = document.querySelector( - '[data-weather-icon]', - ) as HTMLImageElement | null - temperatureWrapperEl = document.querySelector('.temperature-wrapper') + weatherIconEl = document.querySelector('[data-weather-icon]') + weatherEl = document.querySelector('[data-weather]') + + const { primary } = setupTheme() + document.body.classList.toggle('is-light-brand', isLightColor(primary)) + document.body.classList.add(`style-${getClockStyle()}`) - // Get metadata (includes coordinates) const metadata = getMetadata() const [latitude, longitude] = metadata.coordinates - // Get timezone and locale from coordinates timezone = await getTimeZone() locale = await getLocale() - // Get location info (includes city name and country code) const { cityName, countryCode } = await getCityInfo(latitude, longitude) - locationName = cityName if (locationEl) { - locationEl.textContent = locationName + locationEl.textContent = cityName } - // Get weather data (optional) await updateWeatherDisplay(latitude, longitude, timezone, countryCode) - // Update time immediately updateTime() - - // Update time every second setInterval(updateTime, 1000) - - // Refresh weather every 15 minutes - setInterval( - () => { - updateWeatherDisplay(latitude, longitude, timezone, countryCode) - }, - 15 * 60 * 1000, - ) + setInterval(() => { + updateWeatherDisplay(latitude, longitude, timezone, countryCode).catch( + (error) => console.error('Failed to refresh weather:', error), + ) + }, WEATHER_REFRESH_MS) } catch (error) { console.error('Failed to initialize app:', error) } - // Signal that the app is ready to be shown signalReady() }) diff --git a/src/style.test.ts b/src/style.test.ts new file mode 100644 index 0000000..f0fd99d --- /dev/null +++ b/src/style.test.ts @@ -0,0 +1,23 @@ +import '@screenly/edge-apps/test' +import { describe, test, expect } from 'bun:test' +import { resolveStyle } from './style' + +describe('resolveStyle', () => { + test('should accept every style the manifest offers', () => { + expect(resolveStyle('traditional')).toBe('traditional') + expect(resolveStyle('modern')).toBe('modern') + expect(resolveStyle('minimal')).toBe('minimal') + }) + + test('should tolerate casing and whitespace from the dashboard', () => { + expect(resolveStyle(' Modern ')).toBe('modern') + expect(resolveStyle('MINIMAL')).toBe('minimal') + }) + + test('should fall back to traditional for unknown values', () => { + expect(resolveStyle('brutalist')).toBe('traditional') + expect(resolveStyle('')).toBe('traditional') + expect(resolveStyle(undefined)).toBe('traditional') + expect(resolveStyle(null)).toBe('traditional') + }) +}) diff --git a/src/style.ts b/src/style.ts new file mode 100644 index 0000000..cbf9cd0 --- /dev/null +++ b/src/style.ts @@ -0,0 +1,23 @@ +import { getSettingWithDefault } from '@screenly/edge-apps' + +export type ClockStyle = 'traditional' | 'modern' | 'minimal' + +export const DEFAULT_STYLE: ClockStyle = 'traditional' + +const STYLES: readonly string[] = ['traditional', 'modern', 'minimal'] + +/** + * Normalise a style setting, falling back to the default for anything the app + * does not recognise. Settings are free text at the API level, so an instance + * can hold a value this build has never heard of. + */ +export function resolveStyle(value: string | undefined | null): ClockStyle { + const normalised = value?.trim().toLowerCase() + if (!normalised || !STYLES.includes(normalised)) return DEFAULT_STYLE + + return normalised as ClockStyle +} + +export function getClockStyle(): ClockStyle { + return resolveStyle(getSettingWithDefault('style', DEFAULT_STYLE)) +} diff --git a/static/fonts/KellySlab-Regular.ttf b/static/fonts/KellySlab-Regular.ttf deleted file mode 100644 index 416c690..0000000 Binary files a/static/fonts/KellySlab-Regular.ttf and /dev/null differ diff --git a/static/images/bg.webp b/static/images/bg.webp deleted file mode 100644 index ece5f3c..0000000 Binary files a/static/images/bg.webp and /dev/null differ