{"product":"edge-apps","releases":[{"date":"2026-09-01","entries":[{"areas":["developers"],"breaking":false,"content_html":"\u003cp\u003eA screenshot spec for an Edge App does the same 7 things for every resolution: open a browser\ncontext at the right size, fix the clock, mock \u003ccode\u003escreenly.js\u003c/code\u003e, load the page, wait for the network\nto go quiet, save the image, and close the context. \u003ccode\u003ecaptureScreenshot()\u003c/code\u003e does all of it, and your\nspec keeps only what\u0026rsquo;s specific to your app:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cbutton type=\"button\" class=\"code-copy\" aria-label=\"Copy code\" data-copy-code\u003e\n    \u003csvg data-copy viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"\u003e\n      \u003crect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"\u003e\u003c/rect\u003e\n      \u003cpath d=\"M5 15V5a2 2 0 0 1 2-2h10\"\u003e\u003c/path\u003e\n    \u003c/svg\u003e\n    \u003csvg data-check viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"\u003e\n      \u003cpath d=\"M20 6 9 17l-5-5\"\u003e\u003c/path\u003e\n    \u003c/svg\u003e\n  \u003c/button\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-typescript\" data-lang=\"typescript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6ab825;font-weight:bold\"\u003eimport\u003c/span\u003e { test } \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#ed9d13\"\u003e\u0026#39;@playwright/test\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6ab825;font-weight:bold\"\u003eimport\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  captureScreenshot,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  createMockScreenlyForScreenshots,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  RESOLUTIONS,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#ed9d13\"\u003e\u0026#39;@screenly/edge-apps/test/screenshots\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6ab825;font-weight:bold\"\u003econst\u003c/span\u003e { screenlyJsContent } = createMockScreenlyForScreenshots()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#6ab825;font-weight:bold\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#6ab825;font-weight:bold\"\u003econst\u003c/span\u003e { width, height } \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003eof\u003c/span\u003e RESOLUTIONS) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  test(\u003cspan style=\"color:#ed9d13\"\u003e`screenshot \u003c/span\u003e\u003cspan style=\"color:#ed9d13\"\u003e${\u003c/span\u003ewidth\u003cspan style=\"color:#ed9d13\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#ed9d13\"\u003ex\u003c/span\u003e\u003cspan style=\"color:#ed9d13\"\u003e${\u003c/span\u003eheight\u003cspan style=\"color:#ed9d13\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#ed9d13\"\u003e`\u003c/span\u003e, \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003easync\u003c/span\u003e ({ browser }) =\u0026gt; {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003eawait\u003c/span\u003e captureScreenshot(browser, {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      width,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      height,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      filenamePrefix: \u003cspan style=\"color:#ed9d13\"\u003e\u0026#39;menu-board\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      screenlyJsContent,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      setupMocks: \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003easync\u003c/span\u003e (page) =\u0026gt; {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003eawait\u003c/span\u003e page.route(\u003cspan style=\"color:#ed9d13\"\u003e\u0026#39;**/api/prices**\u0026#39;\u003c/span\u003e, (route) =\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          route.fulfill({ status: \u003cspan style=\"color:#6ab825;font-weight:bold\"\u003e200\u003c/span\u003e, body: \u003cspan style=\"color:#ed9d13\"\u003e\u0026#39;{\u0026#34;items\u0026#34;:[]}\u0026#39;\u003c/span\u003e }),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003eEach run writes \u003ccode\u003escreenshots/menu-board-1920x1080.png\u003c/code\u003e and so on, 1 file per resolution.\n\u003ccode\u003esetupMocks\u003c/code\u003e runs before the page loads, so your routes are in place for the first request. Leave\nit out when there\u0026rsquo;s nothing app-specific to mock.\u003c/p\u003e\n\u003cp\u003eKeep \u003ccode\u003etest()\u003c/code\u003e in your own spec file, as above. That way Playwright reports failures against your\nfile and line, not the library\u0026rsquo;s.\u003c/p\u003e\n\u003cp\u003eApps made with \u003ccode\u003enpx @screenly/edge-apps create\u003c/code\u003e from this release on get a spec that already uses\nit.\u003c/p\u003e\n","description":"captureScreenshot in @screenly/edge-apps/test/screenshots opens a page at a given size, mocks the clock and screenly.js, and saves the screenshot, replacing the setup every Edge App's screenshot spec repeated.","title":"A new captureScreenshot helper takes an Edge App screenshot in 1 call","type":"release","url":"https://www.screenly.io/releases/edge-apps/26.8.0/capture-screenshot/"},{"areas":["developers"],"breaking":false,"content_html":"\u003cp\u003eLibrary releases are numbered by year and month now, so the release after 1.5.0 is 26.8.0. That\u0026rsquo;s\nthe same scheme Screenly, ScreenlyOS, and the CLI use.\u003c/p\u003e\n\u003cp\u003eNothing about the library\u0026rsquo;s behavior changes with the numbering, and a version you\u0026rsquo;ve already\npinned keeps resolving to exactly what it did before.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eIf this affects you:\u003c/strong\u003e a range like \u003ccode\u003e^1.5.0\u003c/code\u003e in your Edge App\u0026rsquo;s \u003ccode\u003epackage.json\u003c/code\u003e won\u0026rsquo;t match a\nrelease again. Change it to \u003ccode\u003e^26.8.0\u003c/code\u003e, which covers releases through the end of 2026. The first\nrelease of 2027 will be 27.1.0.\u003c/p\u003e\n\u003c/blockquote\u003e\n","description":"Releases jumped from 1.5.0 to 26.8.0 and are now named by year and month, matching the rest of Screenly.","title":"The library's version numbers moved to CalVer","type":"improvement","url":"https://www.screenly.io/releases/edge-apps/26.8.0/calver-version-numbers/"}],"json":"https://www.screenly.io/releases/edge-apps/26.8.0/v1.json","product":"edge-apps","product_label":"Edge Apps library","url":"https://www.screenly.io/releases/edge-apps/26.8.0/","version":"26.8.0"}],"title":"Edge Apps library 26.8.0","url":"https://www.screenly.io/releases/edge-apps/26.8.0/","version":1}