From 49c2a63d6850d454537e2ee3d66fee6a65ae0ce8 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Sat, 14 Mar 2026 22:20:20 +0300 Subject: [PATCH] Bundle frontend with esbuild, serve fonts offline, fix dashboard - Add esbuild bundling: JS (IIFE, minified, sourcemapped) and CSS into single dist/ files, replacing 15+ individual CSS links and CDN scripts - Bundle Chart.js and ELK.js from npm instead of CDN (fully offline) - Serve DM Sans and Orbitron fonts locally from static/fonts/ - Fix dashboard automation card stretching full width (max-width: 500px) - Fix time_of_day condition not localized in automation cards - Add Chrome browser tools context file for MCP testing workflow - Update frontend context with bundling docs and Chrome tools reference Co-Authored-By: Claude Opus 4.6 --- .gitignore | 3 + contexts/chrome-tools.md | 66 ++ contexts/frontend.md | 47 ++ server/esbuild.mjs | 43 + server/package-lock.json | 734 ++++++++++++++++++ server/package.json | 24 + server/src/wled_controller/static/css/all.css | 17 + .../wled_controller/static/css/dashboard.css | 4 + .../src/wled_controller/static/css/fonts.css | 31 + .../static/fonts/dm-sans-latin-ext.woff2 | Bin 0 -> 18228 bytes .../static/fonts/dm-sans-latin.woff2 | Bin 0 -> 36932 bytes .../static/fonts/orbitron-700-latin.woff2 | Bin 0 -> 6528 bytes .../static/js/core/graph-layout.js | 2 +- .../static/js/features/dashboard.js | 3 +- .../static/js/features/perf-charts.js | 4 + .../src/wled_controller/templates/index.html | 23 +- 16 files changed, 978 insertions(+), 23 deletions(-) create mode 100644 contexts/chrome-tools.md create mode 100644 server/esbuild.mjs create mode 100644 server/package-lock.json create mode 100644 server/package.json create mode 100644 server/src/wled_controller/static/css/all.css create mode 100644 server/src/wled_controller/static/css/fonts.css create mode 100644 server/src/wled_controller/static/fonts/dm-sans-latin-ext.woff2 create mode 100644 server/src/wled_controller/static/fonts/dm-sans-latin.woff2 create mode 100644 server/src/wled_controller/static/fonts/orbitron-700-latin.woff2 diff --git a/.gitignore b/.gitignore index fbdbe80..24db73b 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ ENV/ env/ .venv +# Node +node_modules/ + # IDE .vscode/ .idea/ diff --git a/contexts/chrome-tools.md b/contexts/chrome-tools.md new file mode 100644 index 0000000..67f6c1c --- /dev/null +++ b/contexts/chrome-tools.md @@ -0,0 +1,66 @@ +# Chrome Browser Tools (MCP) + +**Read this file when using Chrome browser tools** (`mcp__claude-in-chrome__*`) for testing or debugging the frontend. + +## Tool Loading + +All Chrome MCP tools are deferred — they must be loaded with `ToolSearch` before first use: + +``` +ToolSearch query="select:mcp__claude-in-chrome__" +``` + +Commonly used tools: +- `tabs_context_mcp` — get available tabs (call first in every session) +- `navigate` — go to a URL +- `computer` — screenshots, clicks, keyboard, scrolling, zoom +- `read_page` — accessibility tree of page elements +- `find` — find elements by text/selector +- `javascript_tool` — run JS in the page console +- `form_input` — fill form fields + +## Browser Tricks + +### Hard Reload (bypass cache) + +After rebuilding the frontend bundle (`npm run build`), do a hard reload to bypass browser cache: + +``` +computer action="key" text="ctrl+shift+r" +``` + +This is equivalent to Ctrl+Shift+R and forces the browser to re-fetch all resources, ignoring cached versions. + +### Zoom into UI regions + +Use the `zoom` action to inspect small UI elements (icons, badges, text): + +``` +computer action="zoom" region=[x0, y0, x1, y1] +``` + +Coordinates define a rectangle from top-left to bottom-right in viewport pixels. + +### Scroll to element + +Use `scroll_to` with a `ref` from `read_page` to bring an element into view: + +``` +computer action="scroll_to" ref="ref_123" +``` + +### Console messages + +Use `read_console_messages` to check for JS errors after page load or interactions. + +### Network requests + +Use `read_network_requests` to inspect API calls, check response codes, and debug loading issues. + +## Typical Verification Workflow + +1. Rebuild bundle: `npm run build` (from `server/` directory) +2. Hard reload: `ctrl+shift+r` +3. Take screenshot to verify visual changes +4. Zoom into specific regions if needed +5. Check console for errors diff --git a/contexts/frontend.md b/contexts/frontend.md index c6fcee9..6b7f7a9 100644 --- a/contexts/frontend.md +++ b/contexts/frontend.md @@ -157,6 +157,53 @@ document.addEventListener('languageChanged', () => { Static HTML using `data-i18n` attributes is handled automatically by the i18n system. Only dynamically generated HTML needs this pattern. +## Bundling & Development Workflow + +The frontend uses **esbuild** to bundle all JS modules and CSS files into single files for production. + +### Files + +- **Entry points:** `static/js/app.js` (JS), `static/css/all.css` (CSS imports all individual sheets) +- **Output:** `static/dist/app.bundle.js` and `static/dist/app.bundle.css` (minified + source maps) +- **Config:** `server/esbuild.mjs` +- **HTML:** `templates/index.html` references the bundles, not individual source files + +### Commands (from `server/` directory) + +| Command | Description | +|---|---| +| `npm run build` | One-shot bundle + minify (~30ms) | +| `npm run watch` | Watch mode — auto-rebuilds on any JS/CSS file save | + +### Development workflow + +1. Run `npm run watch` in a terminal (stays running) +2. Edit source files in `static/js/` or `static/css/` as usual +3. esbuild rebuilds the bundle automatically (~30ms) +4. Refresh the browser to see changes + +### Dependencies + +All JS/CSS dependencies are bundled — **no CDN or external requests** at runtime: + +- **Chart.js** — imported in `perf-charts.js`, exposed as `window.Chart` for `targets.js` and `dashboard.js` +- **ELK.js** — imported in `graph-layout.js` for graph auto-layout +- **Fonts** — DM Sans (400-700) and Orbitron (700) woff2 files in `static/fonts/`, declared in `css/fonts.css` + +When adding a new JS dependency: `npm install ` in `server/`, then `import` it in the relevant source file. esbuild bundles it automatically. + +### Notes + +- The `dist/` directory is gitignored — bundles are build artifacts, run `npm run build` after clone +- Source maps are generated so browser DevTools show original source files +- The server sets `Cache-Control: no-cache` on static JS/CSS/JSON to prevent stale browser caches during development +- GZip compression middleware reduces transfer sizes by ~75% +- **Do not edit files in `static/dist/`** — they are overwritten by the build + +## Chrome Browser Tools + +See [`contexts/chrome-tools.md`](chrome-tools.md) for Chrome MCP tool usage, browser tricks (hard reload, zoom, console), and verification workflow. + ## Visual Graph Editor See [`contexts/graph-editor.md`](graph-editor.md) for full graph editor architecture and conventions. diff --git a/server/esbuild.mjs b/server/esbuild.mjs new file mode 100644 index 0000000..49dafe0 --- /dev/null +++ b/server/esbuild.mjs @@ -0,0 +1,43 @@ +import * as esbuild from 'esbuild'; + +const srcDir = 'src/wled_controller/static'; +const outDir = `${srcDir}/dist`; + +const watch = process.argv.includes('--watch'); + +/** @type {esbuild.BuildOptions} */ +const jsOpts = { + entryPoints: [`${srcDir}/js/app.js`], + bundle: true, + format: 'iife', + outfile: `${outDir}/app.bundle.js`, + minify: true, + sourcemap: true, + target: ['es2020'], + logLevel: 'info', +}; + +/** @type {esbuild.BuildOptions} */ +const cssOpts = { + entryPoints: [`${srcDir}/css/all.css`], + bundle: true, + outdir: outDir, + outbase: `${srcDir}/css`, + minify: true, + sourcemap: true, + logLevel: 'info', + loader: { '.woff2': 'file' }, + assetNames: '[name]', + entryNames: 'app.bundle', +}; + +if (watch) { + const jsCtx = await esbuild.context(jsOpts); + const cssCtx = await esbuild.context(cssOpts); + await jsCtx.watch(); + await cssCtx.watch(); + console.log('Watching for changes...'); +} else { + await esbuild.build(jsOpts); + await esbuild.build(cssOpts); +} diff --git a/server/package-lock.json b/server/package-lock.json new file mode 100644 index 0000000..e39abb2 --- /dev/null +++ b/server/package-lock.json @@ -0,0 +1,734 @@ +{ + "name": "server", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "chart.js": "^4.5.1", + "elkjs": "^0.11.1" + }, + "devDependencies": { + "esbuild": "^0.27.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, + "node_modules/chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/elkjs": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.11.1.tgz", + "integrity": "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==" + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + } + }, + "dependencies": { + "@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "dev": true, + "optional": true + }, + "@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, + "chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "requires": { + "@kurkle/color": "^0.3.0" + } + }, + "elkjs": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.11.1.tgz", + "integrity": "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==" + }, + "esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + } + } +} diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..aec9cce --- /dev/null +++ b/server/package.json @@ -0,0 +1,24 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "High-performance FastAPI server that captures screen content and controls WLED devices for ambient lighting.", + "main": "index.js", + "directories": { + "doc": "docs", + "test": "tests" + }, + "scripts": { + "build": "node esbuild.mjs", + "watch": "node esbuild.mjs --watch" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "esbuild": "^0.27.4" + }, + "dependencies": { + "chart.js": "^4.5.1", + "elkjs": "^0.11.1" + } +} diff --git a/server/src/wled_controller/static/css/all.css b/server/src/wled_controller/static/css/all.css new file mode 100644 index 0000000..66e5dc8 --- /dev/null +++ b/server/src/wled_controller/static/css/all.css @@ -0,0 +1,17 @@ +/* CSS bundle entry — imports all stylesheets in correct order */ +@import './fonts.css'; +@import './base.css'; +@import './layout.css'; +@import './components.css'; +@import './cards.css'; +@import './modal.css'; +@import './calibration.css'; +@import './advanced-calibration.css'; +@import './dashboard.css'; +@import './streams.css'; +@import './patterns.css'; +@import './automations.css'; +@import './tree-nav.css'; +@import './tutorials.css'; +@import './graph-editor.css'; +@import './mobile.css'; diff --git a/server/src/wled_controller/static/css/dashboard.css b/server/src/wled_controller/static/css/dashboard.css index f97afa2..ecd1c4a 100644 --- a/server/src/wled_controller/static/css/dashboard.css +++ b/server/src/wled_controller/static/css/dashboard.css @@ -306,6 +306,10 @@ flex-shrink: 0; } +.dashboard-automation { + max-width: 500px; +} + .dashboard-automation .dashboard-target-metrics { min-width: 48px; } diff --git a/server/src/wled_controller/static/css/fonts.css b/server/src/wled_controller/static/css/fonts.css new file mode 100644 index 0000000..d5dcbd7 --- /dev/null +++ b/server/src/wled_controller/static/css/fonts.css @@ -0,0 +1,31 @@ +/* Local font faces — no external CDN dependency */ + +/* DM Sans — latin-ext */ +@font-face { + font-family: 'DM Sans'; + font-style: normal; + font-weight: 400 700; + font-display: swap; + src: url('../fonts/dm-sans-latin-ext.woff2') format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* DM Sans — latin */ +@font-face { + font-family: 'DM Sans'; + font-style: normal; + font-weight: 400 700; + font-display: swap; + src: url('../fonts/dm-sans-latin.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* Orbitron 700 — latin */ +@font-face { + font-family: 'Orbitron'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('../fonts/orbitron-700-latin.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/server/src/wled_controller/static/fonts/dm-sans-latin-ext.woff2 b/server/src/wled_controller/static/fonts/dm-sans-latin-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..db39c6263056cff430c828fe7035b9ba5edaa6ee GIT binary patch literal 18228 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 literal 0 HcmV?d00001 diff --git a/server/src/wled_controller/static/fonts/dm-sans-latin.woff2 b/server/src/wled_controller/static/fonts/dm-sans-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..01383d7e5c4340847b8fe4570ad0458e6582989d GIT binary patch literal 36932 zcmZ5`V~{AkvhCQmZQHgzv&Z(#9^1BU+qP}nw(WV}Ip^L}bzlA@m8wpqI^AonbmA&6 z#smNe@K1{q0YLeu1EhQd0EnIY&)mP}|8K(z!NT_UW59`$;+IepR0<&i)8|)K5mfQl zCT9W&2BEh`gn;t*r#TUX4#ERq2#93?WCJ1r4&wkJ01ZU~59fdZ*Fwg$4~Uk{^ia1| zd){Dt9kehb-ms-c=xg;h6$^?6M)3cdGyVPb)l|Y1UM!G=_ABW41n$;h5taQKG9qrg4Pi9stFfJcs?daF2`xki13eFU5+DxNFb2L;?<$z6(r}id2a-q z^f-UEU*9G<(dFCuX!g}B3>aA$d|-Nvw7oe3$_9CTyr@3;4I7O-@RZ+nDji4thH2aY ztL=n}(!nFP6xG%XC~f>W9PtO0Z{utjnGJaymxa@_Tl)D>ETBmjY+h9LwI zNg<3%2Mx195JmzD!k`E_hZ+<>9TmEOmjDG(n4ba06**7i+{DshmHk{l!L*fVcns@T)lEL zD6L|JmSG-pPEt?r5n6iwB|fZ_ScEm-xi)-7M9$b=B)dSynvoJA2)&~uL;fc6Wou$6 z6PHfls1Nm9n7a;tYr2~Y5{VER1X4iaA0*^Qu%dL&oo%r?mG1KFntRc^z8kTg)_VB2 z6!fb$>-e)*i?)GAdZa!zPL4AKzEtHxZhK`a8F7}>#h%}j>pD05DYdEu8Wlp9b zB2>iD_K4MzHj@`>UKe6sysX@#xZ@=}D0)6NK?lIV;O@jXvqJvVNgJMb>jk3?`_T9? z2{d90K{A43mv&OLSoF2GH$WKK!lqAEX*wtU zVqi;(#7vW$aG^G4-32k)*e`5u0xoT0+gBMim_skHcph zqi&k%e3jFzR|@8`2m}_Q4wDNI6$FZ4@7F1%dPI{ph!Hvagr}J?Q zI!)^q&;3Fa-LKbc8k5dQ(*WuJEe19v6Gf|LXz?nL}~^$wdp$mkq#8$^t@k0r3(f1{pm@ z_PoSV(SdWO%1gbX^6RzOLU;FeMC%8Uu#-_L*J*&mM+pwL^fLrOUM^J_T)$*~dalpz z3d~g9(hmw)VdFb3zr{m9$)bP#_p%xKsJz{+11>93AZ755RVZ z9}I|0D*0OE&z*Sx+-U#RqxHkzmOCgdw`|(p8FIaS=u~&(L~*^n`ct>e*Q?t}!#zyK z$Eq}UNg6)lgV~57lXy2V7L8drb@JU;g2md2F=QqWjgj0WYIKvA0GgW0d*@~+Nm?DX z@kXk~8YXNiM>&((@+L?EUge~ za|UUVo2w@3SZP_dF7*r9@(!Q6T#WNMw@JpIQeN zx%4J40Qa1QH_zb>ykFRpf9M3^95KBF#K6R{%*ek$L?uLE?g}raU#yEpuQQwk=|&S5 zGGu-*XHe5N(fB05Z_;7;ZpNcXJf!~tS9R1gPT5gtm*vc|-W-SZ!h|>I($E@gH`z7Ec&J+~)q&KzBbE_6%@)W(>Ux4gdDBx*oM{elT zh`AfyHHxLjY5j(QP6ss(J!i8Wz3OadQyGU|@5+8bQ#U0+2m1F4A}7^?V5WsPFJqwf z&tcG0glNL=O?E-2HS}w_CMYj=DKh-BB3$OK)?OyBRJ!N%jkCfdnz8X|;WN`1&BV>I z#&S~_w69U%mR2;e{-qScE8-+*p5wt(bSQw={(K@EyKnk04;$&qtBlr)MdXIa^A zJQ%1truv&>x-R@hhrdkkWhsZR-)42eJ5!-}Qfu<9wCtJUq1UccgA%8}i5#t#jiGSYl~2xlcQ_!OU;yZO-fpFDOT+(~Mw+I zL(LV()B4v?Th9QOGTjR+J-!=Hz%yBcU{cJq>-*Yw3>z0Gy61LrH{i-Bjv=Y1VG#&V zsWq%r@${uWSeu*2P9aRp%XH^|j&eG)dS-v~lgO@|1g$AZaTMk}4=hNA8&eajsjTI3 zaBjD15%&{_AJ?jczb|(A(Gh#XW)ZA0`wOD3Xn4HXN(H=nG#cmo?4ZkZ2hO5^r z7iI-}z}}Q~#Znf(&{;$N(HPG4l4Y^y!ZLHQ5^E}!bNNH2v8U^0T04}R^ThLSi_3*S zwPB@?H+4Q<{5b=;$r5gq#H%&w^9|h1Zn(g9rzHQcYr30&MBWTs_%v{0slZGC=;HwT zpjMVl|Im@)@mR8cxmS3KqI8L(SXEX`R*r6r<+8)FB|~cbJdmj=^z&K$n1ML0%ZA1K z3|DEN`zdON{*c2i)}Ma}i>h^74Mx#Lpd2`F9xzA;7lDh5;Ofk;t`QjH3gDl<6Ie(N zya3_zf}$8V!hDnSw+H0|y@;i4G^x&o2<7QBfrFgis7d%l?27qL`>iQaDu( zE|EAiQM>)@Bx6!Jhet*FxKPWKlM0kIN(?e81+7GzlL{A?+rwjqs!Bw;I-^_j*_y?? zuVI1;$JCV07}gl`b%QYW2GJ zrJ2CGA1tF-h5o%h73yyYeR*2~qf!OTj5fzmGDkyrrUVCv0cEFMtC>A#4vxIfvR2OX z5OTu7*GYX|S`t`Rk1;8e1|a{KOw6vD9i8pPAr^P>gA@?8ZS zw`#JG6Q!*}f?+Hv6;uqBqMH@vIHZ0Y`ry}3F+D@+cKjheS?f@k?HMA&4zJYT=>`4B zL>QMZl+uz3m|^V~f;fNSDua|^`D~+eUnEU2dVj${oXeO*XjaR{v67Chl#04)v~_2t z@v7K~{>`(e&jF}W95cHCPS)n8lFk*16KEsMMJG>BN5{nN>f%q{GTN$s!X(AUVq;Ci z9KHqN$chd6-_hpYG6PJ~?;u86+xRTurAKm%eng5g^D)M424qM>UBqdR9`r#^nM3uj zBhnk1`>pEY+}9=CcRo|SVdZTA@w~hOLVD=6*^+|%6bz@swAqOR`b-#Q1%4p~ z@Lo)lh;DC28@2~4ahZXWQ9XbhfqtPNRlr+gmQzVuG4;cz2z>0Lzz)mWC86w(bJNL7 zjKrh!qQB-Pt#pThKlidJWrG%v@r@;(f+n~&#%T*QHbUv(0{UaSlO!;9AW&3TOiuTs zKxc@Q8$S*(9|++(WNx}yt+87N?qwyHOQtDYO8gkrZNkKg=2DHTO_Hm`YL!QX<2Q-(1why zeZD~VAW>*U)E5d#fSF7X6XO!%l_?5}3Q81(J`dIjA3R84{=JCH-zGiyp`rge);aj> z*$q+ziBvL$NVNkdy&+6eVtkAeMNvs{ks?33hS2mQ{L+7Af0c21%%fz z@afoI03QEoKZ8lL;a+LQ>*V1rbS6*}|Zg4(Ef$_UD1e z5V{`Mn=(|}+NS4jIs40#3tX+M?Y5RdHgO7ZMi`VUIC7{~hXt5YFY;38G0e-Ww}91C z*l0VO(CwfNW`HEd2(8DIX-1idYvz}|zIbG_sygp>yJI$9;{7GztxV2Sx>#|J=cv+X z^!t%$__q;y-LL3>w%C~G$gIae3X(o9gzVDCk28Q1T4r+@+6DiV47vIO00;ov?Owlz{^X6cyGTyQ4CZ56;S4 zlH&0M3GY9l;4HDaB@Jf$e?9C{gL>={xzCXU$6`0+1dI`@+~tpT`$zXO(OGshLRLr zr|m0Vtrf+oevLU*qdaNvqFbA9+x`mu#Ct&^MmiXK5GX1vM=^AGS`V4A z9b7x{$$-hM;8b{8SZd2Wrc1^k!m82r=Y1lJg|bn_f$K&5Bl`W@jexqY_CA3zM@?i7 zD_b@yOf|5@nEDoDpZmsZ!2g2|B!kW}SDnPHK5RcE0P=er64N5kVnPI0gJN>FJBH(YGUr^jWAHOu>;hidH;eRCOXn+ zKQv1CPkFBG@1B)$83>~xCYz)sdR9)g>5h>%E5y^#s?w4g=ELIYZ5tLoXL)Cfg8nN~ zsfwJzxza(Y-X~6NmlzE3P(X^waIs0ri=4nv$$RvI4ubP)IhQly5sl%rO0b zfb`<7a_HCKEL^^>(WpwDL4geLB$**Bh9BA$bRi@G5HBK`P%M#M>>C~Xzm~Z(G0ROj z5vwSnDEZP*#vC@U5frz(dw%X>Q4Qk

a75QtR1ueZFwpcC%SaHl#gT+x-(7Xv8T+s47KuQFQ{dfDx>D(`B*z zgG$SuTjL>8pzxbkzD-{fA(}an8h_}q5u!S-M=QtOLZdf5r`Rs-0d!*NW`~p8VA4wg ztHO4)1a{-qWFP2rx9n1=6q@|?OUn{F^aJ)MLLEo%F&8r6{Kc0|% zgLmu53l=5m^~l3UK_)mdHvS)=I}j)|qAKbl<3I&7N&xae~}N{Q92IS3few$6jKcFh?+-CVK1bxno;idoxU~$Cb#a{sHT4`A~T{VIOi_tg}VAqE!wj;WYo4+8LhS%(cB*4OY z;pI8UKrfGof8rwgu(nO47=|Z+V>j`5;+ve(DD$zoIAk!#hgn9EmQjK6Ldm|kNGCoj z5@M$&fYz#h6qy0G4&JO2Bf1Uhl;~1mKF%t^@;jYHl*$#QN(IchZzn+}i@+|660i&O z$u*|O20R<1U)-_glsr}}!qeO@oOuRej|O@qmS|#bb*evj z@Gjn}Bp(e&5uzynBT{o9zYe%wVMbU|%QXw~H8HSyc3|>rOQ&NTrOZgBNs=FE0VYRK zi=T!S32zTDG5(FlUA)`=n8O@Bqm=bTha@vjP2yj~j!?MJtR@Qo;STjHORC0xtwL0jmXz>!+r0cs8iWGTz*j(eX@+r`wt{Z zgLED(eJ!YH=f=AquzE2GK8u8x=a*X*m-=Ti1lqPu!kRkGZYE}%s#nCrh8Ausar1hp z4Iq7(f3ud6Tq<7GAX}wH&*FdXPP1n-lJGpDrf(@1>Np-m|G`J-3Z_5U|AUYCry%B} z!Lo?|QIb6+R!9FR7}v2gDfn(KJJ?E?G5{3-Q|#3hSKs8#VWO+{%CyNIk$vWsr^hgu zI*FsJ>4U84%NGw0x+z0+hq5XnzW}Oec@>RyA|KmoELk)Ut8=6~O@9Qw=mhNEY)&{6090Q_`Fp6g8fQ(t9LaeAO(p^ALM=T4Pf zO!XIjrHEoY%IVP>)oYnp@4>qLu6EdH)|E*%u#CaSf5Ixf(W%C#w>ZNH%2(`Il{9P< zUe;2Pxu}z1PDcPusbsrc1;j`%t?Q*GK&nYW6Op?bNRMN+C>#@!y?JeP`C^-Q0qwG? zcmel14s2aQF9*#@fE?jtT3>K`Xo3Kx-zFOM8VCSc=XA=tw>V`hq&#-xkaFO>h}mj7LX z!XJaQh{tvCA*|?|_*J1Fvabic>7CD$-oY$E0CssT{K!azg7Po~4opIWu9e~xO`%MB zKY8!jzfzP;#jm(Q)1{uQfV?r)iI6w$64#qIrHYQagP%$=Fcm9)fCzrWYd52hwgjeX z2%h?(9@)Qs3^!T)d>?0SEk*f=hjn_}x)tyN>n^n>F8SCUWQIFEXQuxd9(P7ro`m%l zJNPVlMZP5_I(BV^xIwxdq?Ws)JJ$V~m27#!d;I`6!(8mWS=6%D zSp3|MP2CcaxuxwL6I+^!(=#J>FwHuAygHn;ZKxC?>$64Z7usNA8t^v4fUH=q!>aZu zZ$}{MB&7~kb0DPcEy%X}&M>%A_`AEzx?w*M2ZqHeXwcX=_r9pSK0qKgP0%rGaVd8f zvYzX;rCSpb0Q{hsuVot5X7)EyzM;g3x0sDp^ge&`A_+;EJ&T_yl4 zolg@oI%XVxERB2S^Kp=PZ{rrK?N!HEmEVq`aV31d55fHwdD=J)`J}~Pi*neiHK?(B zpxIXE%hTwhugJ}LO&2p9hwlo*TQZ{N!E1Dam;8Jmj4Kbb|fItoX0frr5k`p+>quui>52G&s zcvt*Pw%gmpr+?J!tencH_3HE;`HWK9(=Ph!>biEXOpfjA?ss2n#`67)amcTg@!4BP z2fzb>^77>^3ix2@X809&|FJoC`F6|v_P!DS^0@L{neA!x-2*^hyW(W3`dd=)TYS0s z=c(!CqsHM)ljH5>(rUjlQ+x)VUVDskra4fHdJo7@A0i5ge9W6a6n@8O6L^;Tm3z~( zb(W2|mC|O%#^kB;f&Qr$qD!;`$}zZjKv7Ty+4;r8JF)!G=CjXL$XkK5rYBFjrru9pN|H!c(XC&N>0I7jh* zlB%cN;ne9UAVzy3o|?-=l3r(S9GR;-Wvo_4o?F%x*k*m~FF2q;j=JDYErFS&v8syd z;-Tcy>pxI_-#k3_eXJ+gn|gke_!Z28rzHq@?xYJ-)!O~Jrf57+EeFoDI@EEeY+n5g zh!j=#7v5N&w;L+XS9yLBfDT|t&jUg~0~>^JUx1&Cf&VHmkUW*1pg$k{6P-R!*jMTI zb_&QfjW2rxQ&mYzhDH=K%amB2RlP`P|2pw=sR(^dTLL z%#_EnL8J4ZhXW~>>0U5dZJ>J(m>>gPu)8l#ZyBz}d%#PxY*=gKZh7YYR=?lcPw^l7 z-CjD;4OwsCKJn8MtezjdiQevqqrJo}+a4z}UnfceeG7fZ*e=ka_r6s^xf0m7V)yh} z9lJ4D<&mhjfxJL6XU?fAz1D8Lw$mQnLIg1EmaF=@cDv#Iap+Wde&*nPXmSKX%_w#I zledK7KX}*>(Fz$(=|(*X#IH|izc9)69>ID67Ttlj1!*ZFYK6g@~l_aw@6tqFbgu8-)tO8B3}u9y0Q_fIn9t2io_SkgvZNHzVQf>L#HTTjq^?~epbG6AavJYy7y?9nH#Y!n z5=0#j5X694CwT0b|1Aq`i92XQ1N>7$O$cJ1#h-9KI7IhnH^5L%xE6<&2nO7=a@s8qSYtn3nI(`SB`7m?|p^#vd{X#q(j-Vay^}SRN*q5;+{8o{cZT zuzQekdOploo<5p_Ufl54YAj3_Gq|8A30)qE^Ky-l43HeXg2U7{1Ki|MK$%7MDc!H4 zN3VBJoNKO+{O9Pg&9AiI3`3b#l|IK-$2;F|fHy36_7COK@q&xAk%5bS`9y_5f2z-v z;@p$PgS(oGlMm@?1Ncydcb3W_K90Yggc3zFACKJ6yYQyEKpUo>9Tw_R@4&{6dh3B5 z6{klX2G+)B@bcYmjR z=E%bjMuCdvBLzNV;AkgTplgy3EM9kJOe(gjYbl3(6bo}VM-*tTvGfMB9>{z#CP6u0 z`Fti1c^gko{l#yk!I$}#huBh@nZ#nXV2kGSI+c!h#~JaLg2lPY_I=D$h&0oAHK2X> z=xJsCNbKEK0DNuFh24|lG(FIK-hn^%->dx5-{V+}S4Hu(1_?Ndk#;IDZ}ioMTgqPU z_z;!i9^nER&8I5wofRgijUw6Iwn^F2Cs0I3o(*tZfyWycDU4f-rqj8@1={_2(G`AX zX;ya=`p$Y$n&du;AiK}@E>9EJhTR=}?&^$pmfT02bJ@gN)Y_S4m2wRQ(WZ@}(#%#8 za~6)Z%a{~mGk2-|!M}8IJE;kMQnV-0*yM5f^zd3;${1)M2@V86MV^3@#NT!BX@k?v z&=(j9OKxsfdFw*wIMwC$Og1%sliPkOb%fo1nU7j%9tV-Db4gIz_aYq{CWf;tzqg&` zzr5)>7LU)2bCLp{Y4yt0BSF9>&r%FQ$)|smmQQD=Ix2H6jA6k%x#c|sK~^E3u6s6c z^kljmYG{r)QuqCX9P($AP4Q8h7w`;<+Tso_SaQq3Fv@CreE#&TDtt@Ni5*7kE>9id z+?KK&H~X9~=MWdSDTPayrq<8LxH`cu^jYLbvg93n5jlFqT%X4gZ?l!_nDkam&AMEh zQpHuSEKpsWM-{dfk7b4+^K?$wv}&wEVn~Q9wsN7>nCOi8Y5eIvl2V&nlBO@WhLUt% zJ69k-ccoL_+>u+GMg>CU`vmT&Dn%egp}ACLOQ+}s)3FBD(BPfh5PJgp*u=rreoSnF zTU3jbhE*ACIY#IFxvk2uod)`wcg0bBiiKNJ&v+Bdl$BQ-y6U^PTJ;6T4&dhW+{vQJ z?zgjMSLa+5yt~Bqzrm-u1S8yR;=2bu>UXet*M@%VO!cY?CKOePqvV z#=-iA*4SJ&iucD}3$v>Q?&wZ-Yn&^?taha=*T==w!cMsx)z5=c$+^PnNl- zje8h!g4Ub_qVY)yW(l7AORpu)r_0o&q|=IN;+TO|^D2C#+u<*;v1~3b_$Au;f%m(F zpE8=j3vv>7WmT%$bm8fi#-{Yb+m}-QR2%o4{7ln{C#JF7v6Gq8<)&#czOTpvPUi3X|TVml-(-~9UX-OD1dC- zgHkmagvqUQM1n1plo_=>*}@fbsxP6_++FnM(-B*EYlq*tryS&CTw6}^I^d~iuEfj6 z4Ecb`m4sWcYb_?@-VZ@Z=32)S6g9NNBou4U#~kD#f?lKH3n4_P3GKy^e1^inp`_w5 z0QCx6>1VKQj;I{SST~ zkJq%z3GjLavHQBI?9it3vG^C?yQfE{7jP@h)}AlBG@}U?dFcVRW}AxOadKIrGInNflPU4!-z#0u3L664?coYl7}B6~OagiJyS zJ;kjtf6TZhs&srNn4>VwkRUO_+{)-~dvViuk^%jn?!r-9m3M@Rg?9p5#nosAQ(t5^ zlc|mVPN*)%bh|?3S#(KvMPr53EDXx;2_+GTsXYAB4WBwUp_fKCPIy^~740W{jqU(L|=60M&We41->R1s|a7yFSKE5;= zf1}eFaa`Y{ft4LQvX@R;oDIrn3P_YSDgF2`L}PT6iEOZXyuIe$K7{^*laz|+g4|h~ z<9hvNW6zDB<&FG(?%c=geQn4oP=*evE=|3Yns6Va^SrN=I~?$QAD=jqC89{N)*OFMsWUA-8|9p7QzBp^ zz4AnsJs4cYK0N-&kIP|kTJJg{u4rBAAYP|0>BFR{(#L)!RJKJa$qRqRJg!Kv^ej(4 zfLKd?`qR05)0ufYGr%Ty{lH0#4G3|Xy@vI5ZBvA2igIb}YMQ4d?7JfON4>2d-Tq8< z-iGt!Ost8~TWb;BRx%Kt{?4qI{iuSw|7mgB!omc5m)=ma|3cA5htA%Ze&Ilt{3TiO&kdgm%JSI+WVou0Bip`+rE)m ztys?PpWQk}^qhGbLJ0~A$@=QdquP>SB=Go^+*HV(0kl-wU*>6c4)hI{NE*>*Qme~h zzJS?PP8Hu!@aP>7YdaYA8KPE?j5iQ#bZ8xgb*y!7Ppk{}oa#1;*^vP}=H31;fH!jn zvDHNY)~oNhfYJlaPAd#%S@-Uea|7|Ua5rNFlR4q}Jz^;OumX?3umO1{f}(gMHm3=E zk<_U5@gXPAbb6Og`SK7Q1RPNlnA1vlrgl%Y=%EQ7Y307y6ReS_McrC^%c1>-CgM%~ z3t?Mb44Ku#XPi@w#w0=d2)+r@*Nv+d=+Nn<8GU{x zZSIQVx@F~{#5horhO}{t!Y{*j1Sw*%#kaytJ*E4~o_2BgH5P|4(;O%eS!pqAIoDj- z#xYG5Er+p_@d1ilRF?;X)|z>u7!J`mti+s19JYkNLUXcXzaM`EyOpP?_l;cnB$*`p zctW2WpNl$T9=CZr?=PLy78HR8^;lROX=biGfk>hH%R6S866cY94*{7Ev#Dsvr=Y_O z%3GoDtHn2s6Y_)zVVfotmp&5!uz5;VP5W8&%$J^DGgT{Bv^U+d2px?8ry<)fg)f8!tY_w(=>-Ea^3>_x?ngvhNl~bTWvFD^Nf2vR zujV0pj^3VzQ7~5?cbgMj2etY)*phcJk|#t2V2xkX2s7hums0`oqA{8al>OtJEvKY7 z#H$RrFq&keE7Xx(1QRGMQI+K+VA=^Wko0hl^}rGMJZbNao5?NGwecXqXczztiCzq- z+U_vL<{hEUCDF)mF$d#w!J^tKcT4Clbu&eg@cWT3B$jV$5G4mp#e45Z<-Y;pc=5nO zW}}`v*`Omo6bMI9HU&P$<-6Sp@AC0#Q`F>0A0mWQAnK(H zlAYSbBE1tVa?2KgQSGe*)az*u0oKyeT1#5TcEIGUfAJ63XmR6^DNp1F_{Ux=W1Pvz zvZvdKJ%{=)OPHIDc#b#)LhW2*A2-lvUJoEhEIPkZRGVv7NFs&RP0r)RjPR6G0Df7- z5OQub_hC+-ZrB3`thr4C9kKfs%7|9j;a4Q$wPM(VqoG=AEjnPQBuN50++3eEM5{s0 zMxUu>V*;-=m5!pl*MF+?cKXV1vWr>)h%9D(ODTJCeF>qsQb0iR`%!@M;Ut^`hmjTO zvF;1K<=R3!HdjE5nj5uLBu%j~qn!e0olKuim5*3V8a~|@Z!3CAEo}0Ya$qlSzQlDb@U60_mMh71qX?c4P|?kFOdd7)PoRd1JxCKIgGq>YBnGR zE!GKwW-#>Ati~TFmOvCJiFY7o<@u{1q8diX*l-Rw^^-{(O6=Q#cF7@&?UP8_r;VRJ zDV9x^FmXkzCG}88QI*2pS-ijnlcC+;KBvK4AXKPl7SGHK>TO-v3PycvB7PUx$cvdt zvdUv5+rfc*6GsXCn7?3dXSr& zTs(eKSRMnY6~hd?uhkxp@twfwo-owIJ#GLtX-pUt32CtvmFXI8T32tPQfq*x-fm@j z`JF!TRA^$dB;GsE)Pvu8`VhGUnf#IOKIS}1f7|x%#C9v1$dMG2K}{+SVJ0SE&ZHq@ zd}lHxibDoi>?9h;%_s!}Q_tAKj6ubb-Kuh{D-7K6emHptQg6QOgW$%Y4Ra=UVfwiv za{;*2r8#)1yY)?*HUByTyz+<7#0UVtHS;85 z!bgw9A-KN7A^AuP54}Wnn@{7|s6*=YmO{p1g++7l&~WRk&==>`hQ9e3pVsgD=F>uc z0G7aO%MJi~hz;;8}k2sdC^st5?>%@b?CM%f+wLVkDU zWh}jDM)5u6WuAWH^_4H`fw!B*oegqX5|51O**`Zx585I>TBAu1_`AOt0|p)2qqu}s z41n@=P#7-)r8duF)y&Jg(To?%9>9n*g{+fu0#EvBBL~gJvECe3!_jkLM0#mqYi{_J z?dZX+{tWkeJc{geH1rY+btk!;uDW*?@6SE(_wxfCadyVWYlNA{fgN>s)2a+#t@T4i zDsh6fk7}LqtjU9D+%IL>=zf$0i`hap-O^05$UQH0l*XWAqM)G7>X=NNs(qA=$Al-z z+O=lg{MN7B;WE|MNp%STBUK`RBfbU|DrZMe_<_|DdrXhhRdX+1N}q^g&MHtSjV4;c z1)@leZ&BNORycN^3B4S~B2!2rxaxJwtp~So6Ic^*H{`KsBkMkYTqBofrSl!IJCxWci4>LlhJ=U_ZT^Y|^rtuyt$H^|o za@ik#>galP{XjU+-$SG5_lq?tbztADYE;#(gJS+1NZsGVM=nzb=PM;)J%}c=R2-S1 zo3SU)@qRZlM}i#>ao-SQa3t@^=xr?Fkz*p_s-$_|i(035xqYSZJ*wTp)%)&0AWWT4 zfAmQCu$)8}GXrjLaV;P+fzFps0KS~Sk^aI3_@ayiDaEs3poRxcn1@yBs~cre)Kp1i zb|$~vG$9fa_-n;LXip&wLB5CKfcbcReTZgkZ!c_3rzlSjld^Dghy0}|+s*4Kh#7=? zBQ9g+QtK(=S+f|&o=uKDLH9Gs`7Ba|aL+k%xMdL%I!rYE;8k^F&bEIH&IP`~LoK${ zxGeOFZ{z~co2r?s$$J?l<9csH7T2qI5;W`;BfkG61U-mM@R#`I{5mX=;pqASI)-lo z1G=@i$nljW@Vb8I`gnAO0(Irf_25RC3KFsvEYRlw<{No>gBbj#_(H6G={-w4L;j?d z*R*``$$7L57~ZIM;h(bEQu7x?b7ra*7hEAG0&h_c+*)49Q`#yp#3(bwfp!4m$ zuOhEbrtaj?N!}bwB%$b8dAlO2Xdzs+!fmcPVd+LKwZw9qT4^$Yb#pTv)d<}u?)yAT z&GmL>dszTu=!N+@VvN{t{7_h!P z*Wl`q871}*VsppaiGvuBbX)hXdWg!|5IpKaEBY{t3cQYWKTH)TYF2F#zcPD$2crbJ zG1(cu-UdwoeYRQe^E&<1gHFl=(1ZJYHy^t@VgGczDtS_#{}pCn=DeqIDD&jbcD9ty z_nd!r-H*PGkNfxF==Fl{X{#~fG>5a>RMo#we|q9Fm^qad{;LC<@AvP|uvv8<`dW^W zQ5J=czolO;C4!)7A@Mj3HMe7huX zrv$M8YAb;8osA5tqjh#re6fH2C@S%OwQK)y((o1h2%n8fJI&3hOVwK0SW|$HKErM_ zvK~XfOQ2`2_VubM&yNZRNFj(NQjDp!IzF5?yW<#_$3W}|9kzU>?E6{E_3&xMyY2I~A0-1NhSNHbN_&5LnO#t#Cwku|N;Re^c<42QM6WPBgH|mS9&C!os zF@t?Fx*^aMJ?84m+t!^5I(k!sf|ZZB#>e&=7f(vp_40E(92vo^-gyRx5G%e}?TqS6 zQY$y^eNz&lccBwuz#(cwAyO06K;DSIas9=Q`CUTlUCj9c*zSGuPX6)^pzvP!T^(fP z4mWXR3Ok1}^ayYZroF8;_8_bId8d51xh#F9_QW>@qR^9a>?Nkk%K4GIEn*lxP^Qll z`o=-Ee%Xb66Rhqw+}IBTt%ELGPyd>7oeVAh)q`8}?_8Ek zm15Rv7T&&gmKvMP--JsaHGkPz*cZ*VAn7!yp=1xfV4lU%ScN%20zoqix>nI~1c=H7 z6D|)*z}2?<#IMcGS_iLNDE|DpOau$K4c-Zc)#VFymTzPP_L~OG(plj* z_mA1wk+h@nKf|VW99DG#S`ag|nuy#t$UV$vPZ&BB<0nUvFZpI!sy@G5>sBZT{GrNs zctOA&%!=}vUG>ruz^{Qwze}sy2% zNM6fH)VxmEA$T4@xeXd9z6A5okk2?okjxTVBv2M8O=1?P47&LvYzBeCJc}^@r#jdp zIy?Gf`!V5=CDkCz!{Efag5Oi(bxlHQkuEW%ta_A1z)l5D=Uq(LuPl6fiJJ zl=9|vA7LBCX$9>gPU{8Mr`-lVIlMoKW7T8uB0jvH8ZXBl>>srAxijZe=`-teW~b|G ztD`R(Pjq0)RWzcIX?37?s(B3`ifgQ9;$iT@t} zNIBiFTGMBQa%a!G>^3L+< z^6llHR(w>+s_d#UY;ry8;vtPLw0?rS*Q@JYjs#wCs0n{t{G zO|LbbZH_np-6Cmuf|+ElV<}lDSr^#Z>^e5gM%fW|hP{Hlhy4`$82cQjgfqkiTsarx zwsG6ITe**L-{PL(UgsHjEBFieJNR!376`n8-GXNXp9y{u76>^)i*SeVjA#wi3|XO0 zXf5=@D6;yi9%sS%^HRC{l10nBu5%T*X!$ zQp4(5^{wg`)E{e#H4~a!G_PuY(N<~4wL7&h>ne0V>zni;{ha;<{dq%^A)yC)>v~7$ z*w0z3zoP$a|GbcEq6Rw#ZzRwYHl95=`(DB`k*#P!%jn$v+{cN&=-BY4;j_apk{H&P zTOV2ZUz^*Ovdyh>^D2MYgZ4rDD=QoEnz4Z!%F1a3Kyl?5!D~LHuuCfdBS1nF2D}<;c%?G@o;-*&L){8OjlzviAGb&<#JsP{xVOHhw3gRy3_0i^}a(V9{t*Z%Cyk|m12 zV{AN|f9iKYNBeIA%-nwV7%lnLQ1S1vZw({jGu92@$z2o#=C5B%RM9CE=g#;k(ZAgt zdB1dB{hS;TsC%u-a0}0ob2AHJu4C}XXv@y|LJTg0V17jBiimF5J7|zt0+95%08p4l zHbYVpdIqe5oV+`t4>zlOjZ*bd^Rghe5h%y_3j8UV%L##!o(VZnB2hvqCK;v@2x^f= zL&W5v+=JT;964<0cDp;ydwRnao+RwJ+lljbbhri4XZ&G*%g0KbCSSC-fC))}sAnby zuLwHTM3;#Pxzh-oX5$RL?Irog4)(}wSvU5MAw(d72Tu4BEr+pyh~zCY^mr%6O4$gU z6Ht7UIOa@Cc8g?nqJ);=o~ohib!&$bwv0h|y>O9n08c$Ak82Ne#TIcs4+p!7gfWb8 z-d<%-Bp22j#lpAh`hqfiAtgqY_(`qwRFeRCB+QBDaTdeM|08a>G~Juq*^X!$x0eTu zV;tf|)K*aRWPJ7H-IMQOrx7s2F2>j92K0(- zW*JplI=6x|%8@6EH_Hi(OV??-OQYRNV6%`2QE&&-w}{mP@4InECY~GHq;b%&Q$3{xNoCT=u0^`Ow0~lIsU)mymSkW zj<@%hE-Rm-9~s6#!xX-h3E)zPjQ0-@4oS(W`UfX=YmgC0AqCWyxU@h~>VO?_Bh-yj z>jIV$X@_oYaLy~Ys<*f^e(4ZR1GDs!OmE2^XMG^X0TGCvVoh*40=8lar&&avFds;? zAHfUY=&~MMfsrg@rg_#ab>II#+;V<6X`MMQYJNkU%pZyIju6i&cQgJ;B&(`$ zb+_PP1mocS$*=4#eD8!N*ZKRO`M0<(oS58v0c3$V;(%8m#(TR#gSRY1Z_9yv&pG~A zeZbK<|0rp=2=Y&Q9sKlxBIScqpTa4u+MTHhosfV&`oxb(NeYN7YhxDTK+!8g1LO0Y zQru`K^q3nWyv#+EXyi{W-)##4Y2e)z1e8T$u;DnmT_)&=IyCLccUd&zZ^VM80zdJ9 zU$1Ck1){p=`@y^)2YqVzH8(PIwCt-i)7aE+od2EKQ7A8<`piZRWav3NqW&``SWdm? z@(&Ls@TUzp4V&uXU3)<#3ABl+r6szt1g7R;3W&q|1O67P%TeZ;c;*7}1a~3O+i>`s z*tTQNuS(j7KM}MZ3|rN+^$!y4r?+oM4ls>rkeY-AO_JI4KcJSEd9+fcDnf(nKmpVW zIKArF!v8<&3*Pq7V1dCa&umRY;gh0^h$#>vA+LN;eA3qIODVOk9K_=KH8m-P~bpn6VEu>=+(FG zbK63FG?cPez;t; zxTx=Zz;y+j$9vTKdt)C8uZYL(@7L1VXWR_<&Jec1@z(NE?lt>sS_nr=&X0=^nlG7) z{H1XeS9kU0$KQEW+jd0un18iqxb_!~0n`K38IJ5hBsiIgar zWDHTbvn5VCfMU^iULOt?VU}jFrEw$o8Yz1V7^DIzuXPrIf>Ug|9)FluS+RyqXi_G& zmIb!8Bgb4|4B-a`TGBK)30Sg<7}AW|7yw!RhZx?fP_A`;<`C-A^X_z&YOW0DiXuCq z@H=KF1jqP?x`|))#cGR0)w!UWYca=2&vs40^DH&J94$~3+cCI*)v#I%WwdF>s7U?)Hi zY9#h{(i{t^j|j4>!baPHO8DVMrGUKJ?&NZ7(KoHlq3WUi0QhG0+;sP07q<+>DG;39BkrBJ5J3=1SOaeMCX)dR?(O@aaC4clRFj>^r(W;4C}#83rVG`sTlLT zJSj^jF&shSp=88dSndod>i)nVZ(fc#3%Pu8{7KqKVR57B&MAU8TS`{uPN^uK9d8Jc zQ=zO_N^*oGnMT$^jqe{S!kJyJHG;gxMYDoABiz^Y8jaAA#Q1ur3Zm-wF)xQSixl}k zT^^DP$I~To9U(^Ox79oiwkTfPtoVg$@Y|sH5rc#10w=2mILda_Q zX}1M^16s_wSj8$-vmj{>KCR(A7S;mF*qEV)Vb0Vz44qMoLu!~YFN?XLuLN)B8k`=8 z1U_8DWYCss#P*?m)+!UUq#jz?=CG9-1Mq8{DKC-hGkC97)u0YdnWVzzU^7DCmZf<6 zo7wmr%DWPWJ^0v@qrsb$`gU-SQ#ch;^PS;NNI(ub!p+4cuS*$~M&7qdmU*AsfWGOn zb!Kf35DEP=+a(eGBGb5ka{^dt@~+5+Y|uoLm=P|7OGLd|hicX9R_S3aPEr{fvdn@S z`Mn6i$B)p{*L&{l|64f9K0?bX{i%um(n23f%FE*SLj02l;nLB)J(sKUIuHYWFhL*b zc*SqMTc0nnPlF+-mLX8%a_neX&SW+U?STyv#F^vbHJ`5BDOlc?> zn6Qc|PR&RNP`{4aOcHy=B~jnqE8hA0uD9nYs*w{+^h$~5v9piFK3{p**t!Y^JD4a5 zv_!P%dPwt>PG?MFZgMhBq2L=6m}beBCAX5Jyf@{lGJfhP}Rfb?UdvZJ*;6O7GU35X{Xi4iTByI)6YoJSuSfjaR=W$R3J`p zL`Pg4>djJDHyCEH8qR{9C%pmI4oXvYF7WnoN~q3%LK1FE<01tqw5o!ChJQ)^vKng( z*NDwPCOqd^d*3lrPM8$T3I^w9`mQq}i_|yMQi@_3#R8Z{#2NZ>%tLG7+&IQ|P{M#x zLYTSO1VuIXpp0pvMTi8_AbAOpS^T_=wPFZC5}_m!z$yjiQJ@)y^6{CF7nklfQYtd; zh}^Ybu2 zV>?xnN=1y%|7>csVJ}933MS&TSXZL0pvMXKdyD-b;}(ZMpMmN^5kzsRzNBst6$K(I z7g;4%p*h_}kQkF-x7U7>)|#7^yENGCY6jhUdTSw7@EbupZK;*~3S6m752r9EPU1`5wSn|H zRAGd-5rho2vY!Q|;muw{X1euMZ_%1BYqAt{IYXIKe^IpszYbLM5oyx&OV#qMMrIhlu?$&GCdw1Lo_f!z{+L71S6 zk(`A%%jr8(z9HThrn|XjP1fafJ)U=Lr4=ix;_ZB^`zsO*{!)^~d^$r++$XSi`tvY= zf#jow=DA9gt=@)TC4v(@%bq*HCN)iF4p%X%6f*sZ1iD`H9%KMW4{7?0-2IFX(l#KH z*zSofhlxI7bIRaDQdYH8<5Do%KD(wR(a`w1Vs=?z9J*B$J2=Enj}+xM#K{6FlWf<0 zw+%l$5ixTyMalZ!9c9JD=SGlS!uj>6jv{G_C;Sma!Gf(t({ zOR8_Zdb>Dng5xcXT4W{-cQ&<+KwRDJUr*MWSQ2)))9J#b9{*s|vn@C^Ex?5V$IN(V zH5pIvQYlRX8z$g6d21va9V!_CPb-QGZ_5~hU&`zFqsW6FI7Z8{!K}0Sqltb_f5%PS zhV7|L0Rq2FFb{89$AB|9ug2$SO)3uR zL`!rp6)+a3r*h73cWX5VgWr+~wX~X`MQmIJ{&0A+*VCW}W5kT}d0vKkW2&0A5PxVJ zeiLKrhZ;+Fkcde_Iak<+tMI9Bv<~mm911B_W6$sg4XeeyMS_B~L(@gNL$MM_2bI*u zOyrmW@K54&n@&i_)G|W@hX8y^blr*}hz3-yCXg?QYDDu6DVC|DOjmtqqJ<9P2Xxh8 zv0iTu*DL?1O5;1tg zu8g*ir-BJ7Ob9ZNrW3V+bolPXIYS5I0HPO)V@lgFKRBBhNcEOduo#sAG2l^Vp%9>! z#Q@|qZxu#(yNzj@LgnK@Xy%NO4`Njg4{$r2Pt-C|!pK?Mf2F;>vI+}UA6cA~uP}4Z9MTOwS4N{DXBR4JPSa`DAU|F~sZx(#dwXzcXNZCdNjo4w@ z=0k=8&(NI3m?V$zwW>ZntXKEkWTpBB(AOE?&Lx%bRcG5n?S43$P>SAXg?OqPDg2)F zV2Qr$!0ie{)orMmr6N}pL(v$65g^IqW{GTgHmORr90wMt_TcQ6?O3;Cr+3O7W&KpF zd7??G5nVywGX<_r>f8tPJhS~-}ijswIMYd8a74~4hcJUA?CkenVRbHgMjHMt-?FOg8 ztRf$sqtrbRf*9`eyMar!gPp>{qy*gAS|gvB<+e^*rG`T`s3L+U+K?|3_DAJ4r2#Fi zOU1RdF*dd-*;gh6;1vCM%V8%Kc1A(Bq?^HAllVKl6nb%AF+Od2yNDjjzu;`bmtwP{ zaAUBo93=g6%HNmmV5}{XV_MsZF%rO&5EOvIhd>Tcs37prL(7|e3m~UFm-m#4r%(z# zIDSf`tlPPkh?~R4*ikyB26FM*FwiL@iP9%x5TbD|jn+0oWl~j5O@L8}YZcD>!(mo* zZ#-qE8Df_wKnX1ezD+Qd=4oR;Wt>n>aHwv9FF2)4?ZwX6yd13ZGH5}|8@^SmYQ)D4 zEUiutM5Brt=%0$4O@`c2LSH?DNfG$G@L{4}_72iyJTJU8?>tp8g()KKlXZ@quYR0JQU;eaEI)Keu#>AcCwYxn|)x7El}A3leeI=V@b{~8P=#WEs{yWrO=## zIkhFLe5dxR60Nw?4)C3h*=tbkR;Ss_KiCfCw*^nLE#58dY~OP14Jld3qa+hudrOM$ zC%CvplFaPT^#-%TN^xY@OPY`!ztU^s|FqX~cfjk!3Bv94#NtuGKiQ|-!Ab=Fu0Pe7 zS=#iF=xMnSfKs5Y6)ei$zh<>1%4{_u3d<_2Xp#2na<^0ef_Ysh%<&`x_39H zN$gl@QT#B0cW3~=q~Kspa|*0%;dAU&3T=RO&WkL~xfE+Ve5$JRLpcHEpv4*gai-Do?>a5sACLuR25R2L&i*GWrlR(GeX%rX+7 zY??FSdt=lAUwz&5<6wUW=~Mk(G{^Wx5)5J|msS@9ZS>tn`DbS%cggJUzqw;wGPL5K zS~IrsluwqV{pEKn8TYV(01D9%++LB-XLPJS2OGfDBUcg)g>{Aw7T@OOfikCT@eP1N zzY%c4gXE|Qc@s4v<*c-k;6)~%uptlM>&b4(f*IYycjj{2n-~R-dN#H5XhjxxuZpk! z++b}%Aalno-n|Wuj!*}zHT`#KI!Q2F5f;JE4npc{i$)-c@V1j%O0)pAR$aR9}tyXZf% z%`N6NY??2Vy5=C9FbLgWEQiS^e_@3C0C%6BPlC$X4tB23Sq=NEWC)|u9Uc$+yNYz|GHeJ+v5(RU z!iCqEW*Tlm2Ny<8NT}o$2iajhR6}jksR^buh4vd&po_9P*AzKD z#~|;_=l4ownpugewb@d|A>05WRaaRrr(Le@#X<11fk1=`kfgXe*w|7dN2Q{I>QlNc z`D;!4TcEP?YUK^~8w^7pL@UcU@Ph3FwD}F@%&( z%7Ud2u~LklF@vf*M_xaVonzodGy=Z$@eW0EQ+#+vR-NIQl&d|5BFAiWalfJ-g))jE zt#%6&2EAq!e5aJN0Z|9(Vldj2WL3@Zw9I0u=uS9Lj*#_6ku@i%WsZSyybt)R9<$9N z=dyTvY(!`F(pYU3>Wy3KO!Q_o(kWThZn(Ur4|Z^ElJ)AGggdI? zgTH@SLiVRW|NZYTdCNL*%d`=~AzZd~0xgD3o5z(6<4IZ>xoDy;!qM$V``=iMVQyCh|1dA7<#T8`|N+dl`_x!IL+Yn@_=p5e*ta8IhMSDN&I zR!mt6!^=IzT8!`ZK^08bnj#6^)4kszr#T(=^C&2-{RXo#@lGz65e2HEk24T_Jry>5 zm(MC;uhmN6I@*vzIB`ijghGPS+6MduIi9DX{q#uk5!r3*Gd|tIpcxm@x7{=0+ZC>H-h=Et`QBt6w#6-Rc*C3-w|4Aj~!1YXQdpr;2oJZ*z7Rn zLyn~&rK)r<6EVMCgCZWLD!PRE?Ye&O2fLs}w6DUa!e3;W4{sy(NA}siljw zcPDMIup}BF4UmGNm6z~;d}-II2gg_5I%cBlteuzsOLob-u?&Vp|&Ls*}YzRj~mAv4c^f2PU40o>2@B9B(K|2V}CvG zyJPgchi{F>6VM`^Mv1y}dy3zfJY=al~;L+U~5T?U_vVQ~Xl-0!r zki8wAXGFGBs%(#J9byz&YWkGw;s_4clEX3jseAk2x$nPD!hbq8{r2!XUa4tEfAmGP zPDeP8&-XTM^G1h+?8L=*o7{*{Kl${NcLNlxK+k%2B21WJ%4TR%+q9MkQvDZ-(%1^9 zpvQ!PMVlq|mn_jNmp?XDw5K=xeBS^6f2t?ZnjU(iJOW-pEES;mGDhx^!JrUbbWbe+ z*bil9c$^-2N-ON_EG*mxN*xco6mE3x>;fS){n%&#b5;78^tp%~3(*^I`G`(Rr>zVC zm0=&whUdX=$q}5vHC!d@)0Sc^xIC2HLb>ZQfyIcK#o#RO-~xsrlS-I7G%=tM4BMfQ zw9J+`#znyzAxj!ulECq2@p;UM_QKuI9OsIxGD!e*R6nc;U^1uOgzRfeBK+ZM>|=C8 zyYcXFH`56V-Q6;y$=+CdYMCxSiyn;G^k8OTXED^!0g-1@MU7^;h*b8d641BQ9TvUc z@>`nVWcMz>j6WPuHlH%_ z6jnUP@meer`54jE*yNP?y*_98;_6JXl*fzUaOAvWeZvPX7)4}Q@0K%U-8!4ob{RsH zy677Nn^}H0!CIr0*A^xV#OgLO z7fv?anP@c9(|ruW7Kn4K>pf=xLD*Npe3*23nneyCtWBoR;bD7c4UCbo$ zqbuFAqbyCbt;D6H>$6U+zML4HIVC79t5dxD^ABfaprJF zTl*<*y6q(#Uk6JqcuU2o08Pj~Gg)I4_U#a2a+$WpRK!vn=ad(aM${t))-D%p!1X2) z^h+CoM4NH@B#@jb5JG5TQw;X(qWow6EYdxFRESQTx?ynF59MUBszC=yZe1(U;zSO{ zc+Itd+8s?hfyS&yzCwtPUBjd5WJB){u7D_#nE6tWZ1JQsMuNJ7!D$v-{AG_xC)Yo& zkm05Qy@$!om0YKP*NVu^OixYI3}&p#kkjx~n08>MoZ4K0Q%Z@&kLs|-9MHtHDvPyy>7w)*ZA-fLSo5?&$5U{H>=0OWFZY zJmh+u6m-|h-!?ygVYa*A%S*&s2bY(S^`Xu<0#|GU7jf;fuyN^S2(oWA^(B}ntB5&j z$lS!jdTD-WS!hsyU?XKJ%&_OV0z?mp^X_(_Y5Z)7jl^C)Nu=pMTFE8RyK=iy#TAZ~ zG{x9a1@|y9bB%cji6gx6@OpTfjwFr5vG#2*Q>BEcYP)J zCwXzcy@4t7r5Q&fMq~oG4HIKGVJjQrw3+z&dyn6btsIvYU;kCA%@jq^J`>JBiCv-R5pnGmC~<*NxyhoYNBEzzoi*l!P>8VAerkeDbjf z>Z=f}F%Elx=B-y2PVS@Kih{UN94j7GvV}siv|B_(Ni*TX${t+txol*;YUR5?Jg_jR zfk~GYlsHK)we;#~<;mXFwc5B2o)t+b5J7)@qejET`W8cDXqp@%`el$aO;^9yaOczQ zb0Ok`PL-i$1TJYz@#A6>i_~tr+!Dz^q{hO^+E8ab+`MI>+vxYq2SK&wi&$@~MW5j( zGioWa_^!Lc5$o%{*4munreQW(vX=~)YBIwV?%dhjm~d+!gMLbnwxDnB*lmF)Y!RaL zyMMtZnZunr!3oCX~qP;b}6%kW)OzFnhAi z#(63)P=LVr3>vtC!tj9oyo;z@8w>CliopAZe4uyODY1N1G0iBMpCn<{n+%4>8EVLO zr=&IDQoZF=YqkDaLf=gnv+2VKY?k&nnIb)X#HL%Njg^AHgYiKHPN1?QW0`lM0fSLc zxj-TOR9uoiTG38}t@2{6INi+4m@=l@iUqz3-vRHuLMw|5cbi7V0u(7{fqWDb6@d+- zG(sr^XT<;;5H_%*PbwLxIfqs33nWXv@!_<=i`s@`1ZXla&U~b$^xZYTP;l*{ox=3j z#Kuu}-3HDMs(Qc3>3H>|n$x#5U}6eEnLe)Q^@D=h-!{q7<$yLKF8~6E7``>*24Xhf zeu*wER(Bg242?Eh)B0c}8!x{9unBzgO2n|kniuxtcqVWxE-t>8#;+C5$>UWqwIi@ zou*OlEBhbxp{pL;#y(joXViiJ#gT?M;)I+T>$q=$F>^UP{os8p+g)xXerFO`#a5Fn zVu&e8QMiu*&fIa$=>O0V#mGr;zn;*^wBoWmGddY6Wc3FC<1|ClmN()5f$Qc#kM=?peW+aM&bzuMG%0Yi;KEFlGCilMWQpk zaHWV^dyo1SDiP5y$Zi_t!QAw&*IsbA)l^!gh zp!~>d@kxYp!fpyr_f^#%N~0v_1e%IbwI$O@FmdgO6w~%1%THwTtk&mHcqj*#0 zcsdSvm^pKjgbYhHW`Vw?$lATVSyI!n75r$D%aOQ{>WLQA5A877E!I`91wl)dl)?;p z14a{8aXeT~zy~;D;IC*-wQawzKOzt!3nn4G$`!z#UR5c0sFSG&+Bm8FQ_+rxpV!Fx z+_fcY0VgK73K49W)o(AhhsZZ$)zg3vYGyxyl$!nw+%(xJ`(g85NuwYw(6maUY9+)3 zGUGOOP?hy3!gmRPdc+&Tx-%SdahLmP$GT*i=ou}XBlrdxWqXyyM8yu;SIRBe}%oWp$S`yo}E#%bo#)PKzcHB3*uk9MLA;IvND zE_Ge28V}UYd%`Gh+pshR$|nOAEEnh);gs~|HM~6v+wn-b1p#@mzqomcMxqSyN3BXV zZgv!|rdtTG5Des-Q+9r9F~uIuUqo@+vIaHjT^ii7tj(kvXcbKTyDd-iWJp0C+Traw zwY&Lhs`(6^|I}}G=H#3w!sLL48_&ZmUAvpDTWB+PT*Q~cOUr5dz`){qD=JXqyJ<5) z(&;|Bibf*Dq*b59)G;X&SmF$nT7D@-*v!d^a64Ko$Vt}7=Vzl*P}(UXJd43_FdnjT zSP{GmMB?fKFE~@RQ3!v}zC^fCWwubtDxx)ABfU?R)aexl5ywoD%d=;Z3sf_PVhuQ^n8UVfmhXT2cK?Rt+v36PemutMC%mv}Fgl4X=)6DnFP zaWUy9_KsNcE@*9g0-`gO3X&F9%FndX(nl9qa@63cFbRT+i-;>7O~Ro9Nn1(?Tb0ID zQ8dDe=8@#$_+o{Lss1o?hf?vw*V#c6BJCC*$qGt`)`7=}%*SJuZDK}mD5}*(N5&YH zZ0}dAXUp|;g8P`}a3#vix=X0z2l`NNictv+DShl7{Nqew|#$(*ISqUewLqz{-0Sy3N71LXGk`a%OfI|X9>fER-oGQ z?4Sq2l#udFiY^yHA!W3wUA~N%MTYK}fV=i3k%NP`3s(QQFh6{ngx#V4PO74vR;z+P zB=qgJrs0)zMh|`B^%M*zRz5RSVpRNB5@UcGI+R2&)MfEGYoLgjXHj>tZ@Trq_=4Pf z1d|4LDZmk-jr2nio!ZlJXCDF+7tlN_C0|XC*KV@W@0^h;vt?nX1zqSct)7f5o|8-H z!`ywmeM6VN=%Pd1#kmCSx{3-nk}IR>+?A%5IRZQ%^m^$NZ;a$og8SQ#HT=}-h4>p< z?z#W-GyV{S2_@}vpTOp&u}Qw!`&QJNduQJj`YZVP)Rz|jW`Da7#82TyYvi#j{47}W zv()n=*{u@NNE13HMqE0mAmqM`^0TaX&mO3-_Ivhe&UHWU0_GzaTQ9zt!TJ{$6`981 z;+{Fwgu%tvuDB)&7-Jas;%Y5yu5mLQwwes@_Rw;r{^&yDk0vieq&1&?Pp&7QjiI-7 zZLn64t-p1o%&iJ~^|zqaDy+AftBAo@tmO4|^1%f^?%ShigxF0Y$;dmK$>|kM((bS9 z`?!XLE z$C2GKtw2t7AXTUY#LYf3I>$K}@RkCP6fB zt>+O6w5VIB-?QJtx8iIVMoBUi1W}Y!zfR?RuJ+ZQW+`s`E1aA+3!}L+$~pBf1Szr$ zC2@El3>4(U&3l2{*)s9Z2>J-Z7rZxTXn?ly@a{ZE+uHTuaE-O6GV#SBNztvn` z5P88cV->x(#Tr&vT6c*^BTQm)zohSjcs`HM{o5kcjiIJjt-Jm2Q9E?&Y_|M6a`Dg` z_;dR?;6GvAPM`Utx*Rx9{KvoCeM|4X^ysCbtgkP8=}f9}Q2md43oqHf-V^s8KeYwU zus7ekb&K;J9efd}`V7YvEfwg3gJjIDuHRM4GiXj$0VD6TNN_Px*0a>vT*CfW7w z?X-bGd-?>YCi|Q1Z=XL@#)aR9tKaZ%UxnYNne#)mvf=*MT01_$s~2PFn82M7?sS*z zERwZ@4eZd_h06vzAjysiw_Lil3%BB|=PY+L^PJxTpsz3Ac|U0+THNJC_rLo=xp?80 zo28>W`()vD5$TfP@6}*OTeGGeJG!$^Ze@MTRqo6n8*E(HHp#}BMXpjVp#v-u9UnGv zooZ7S_Q7HirC8(e6@m0B==?*(|sQ0_$%FZQMF&!QKRp@9q5ch)n8*uQkN*gp^x)OEoUN^#QhihG~P>e;m+<#LwezAGE zn-^SGKZnlCOG-vkhMf%(Re)20q>=zB8EO4#!DIfNHu!@eDm8H3(j4cab@ht8oF749 zUaU1Z52ZA^)pbT&nL=e}ShpyD_xE_nXje-TKQIf6V0{JY2HkJT-_|6XwcV?-!u;)r zkO)OV7zL(I0g#|%bzEv1l1Cx@Pg^NqvrT%LSK@5phm9tjEfl;W1bZg)@H>n23REq= z&{UEJcGstB*3aN+9niXk)&#Sj%}-6MT80XYALsS^h1NF2iLQck(V$-B+ga@45-eQ7 z=cHU{M(^CIpQ?K;g*9%fm9iiSn5IfWqUTEE5dO6-4TS8}d>(~4%6_G*TFMRvOkjhl zwfpe*YaZdby@kPaR18w4>^+J8$l7vi9#HLw%)|PKoE&o05RT!91 zjf;GN@K5ny;NMHNPHW)_Asp8?%Sj!hV7<#Y|AFag*aD z$K3alOTwa9h~63##)5%$DUHVq)dUK|oTg0OkOVS90+B9OaSVxZ5?g@bP1G}dY$XP5 zRF79jI5omi_1>b`kc##b^BK5}Q-alNBpX%|hj*II2F9uVEjgoZ zwws#p7x_)Tq|&C}NxbIwRkW-1GK{;r^6qdYjb2#&?O2ynaCEuXPT>de!{oyodPd5b zKr(vgltwo!TY@10bXS20x8qBB5owKn57U4F*|fxpy$O}lO}4T zk&-xI@KDjWVqY3bMJk$)s=hm(TmOWR3uOF1N1I+*9lHWNnO`k$&t-#`SS`S-6 z6Yl*QsUNh1O<&chKuOR!DJH z(REa-pY^lTs*3Ki1Fl9Qf<_{h#!N5L@-R4TE^&OcSu>>wZwpClP-3En4mc&;SZisN zXKw0e!=i5-BBk(B#8bP2YF$a6vEU$)&4RxfZ8wBcwuhY4zF-e=lJ;L`(9L`N=VhAK zrW=fA*xri+J0$2IJ67KK=lC!1FIw0QqX3Xb$kvthB5xhGzZkzBB4MRm_g<(KLPK6}RI35V4 zln#8!m|_GN-n#qRy=$+|m?>S?GgRAp4Kphxgb;0Os&uz~vVfbmv)OF6*p|FgiKc0W zBq@rL>=jWo41-5IeGlsv?l(29XL`yR2X`?Q7`#7gLr_hbs$?r`7z$(TCdb5%{d};f zoSV7LW~idwu$35^R-=oq@2w{VUtKCgoMd&?4LUO{JU-2ni7 z3oO>H!5c6YF`BS)xK5IM%D2lTj1Zitnx^IMz-(1ne=ww(YWtr1K`T-^&9m{eaH?U5 zr>hKw!PP>IHD;pexW!V78*v-Y!){$EipkEs#dMYOwd3qcfpQN;P(%nvjvxdGG6+F2 zdeOI0L(Bo0wR3P9_eqql%6jdHwKDdyaAXJegGZP7$J$p%hb%8K)&0gC^Xq-4c+g;a z6F(UFI*La#pW1wpuO0_N_00CXw{#nZN2QW^K(<<}w=iiaEY?cTx!VDT-1DdA#JR`4 zCI?ST&UqOEjvrojDwbE9kz29yZ0GJ%faJD@I45eTHVcUXE-;ocR;^5zlP%w^Wi6GEYS?9ty4xN+EEJ_lscRK*x2*f8Yf^buD7DrTi zpR=HH2{Q4uF|TElW-ih>JL5T|Gt*g3f#5V&W!(vDp7-URX>~ci=zU7wnxWe+hf8Dq znJEnWZf}Lg6&tn+pI2jW1}jkcQ-e{?_hVGp;x=q{c3k#h7~o9Xx|TqPH(1fJ}>- z0BaQ|5lEu62LZ*(2j+spo3q_U75ErY(-V)s>rB3aTElo>9Nlc$%A>fAYz!xRWqO7J zl>U;|GO%*F>mEBhEly^UMg9>gL=YSmNu-is57E`wIXFQMjJpHdX&=N%ylaQOn9!C_ z&F_>VH^NMcAaQOhqZTXe1r_g~SyLx%^To>j$m zPuDWdW+LEUZf$cI-6&q>Tp(btdsumX@+;iIe}yv9oh_=B&q5;=POj&_R*{ope}}(Z zjUsxtyt`S)!1G}{H`u57W&CvCC^v`Q@g?{YjSx|6BAq!T+K*OUN){c0CXOZ5yXEm? z?De|IC(6f^Cv}>c&Y8q9kjg%|Tm$&2&IWNb1GzWaR^b@tL=Nn~yIoqp0W)~HjA04n z(k;>XY?(5{M2oEurys86|jO zuRl%D{h<6D%zTNt81pV_eOdb*sw#Ny%YviyAKv}8*hBv;8UA+Un3f;zrD0CzE0Kv9zivlG{RUx`1K?G7LPMB4VK zW_m-Z2cWgrDrWbyRPUyo`0nQ{)IQYpn-*P7lwVv`?@WWwLjJpP7KV(=M#$yM!s>W5 zxg)5$qlmnSB=w_GnXtH&w(U-A*Vzmkxf7j%#%}LwHW>-LAMQ39BpR^3b`~s0JPXJ2$M4wzz>i-#(>Bvd(zkUWLMXp%O}nt*}nYGuC`4I z>-IvufkCUH$wcA%?)Sx|P@g-hhZ%nNeT?HDzTd5RyWlh85QN>)Z@qf@H?Wa9J-|+* zQ&JE}P=}bLEQA&g>>)4TTEFp{R!|FY(0kt+{F$-;2B4r##OrIgjol2_doa9jg%2(+ z7>RFCb7*h_vg`Y|FslrR&O`WHAeD|3FYZ3^z(Zk3hdjPwim++os3+tHr!c$lW zT?Vzjllq>FbM7agyt#ETG+%pkie2mR9|i4&X*qBLRy8rMNCAv8l*)FYe1wDbCR&+B7Ewf zTXVz?xM>jF$%dn!^Oi`6{@pH`w6X&_1lO)Bx_@1GjX{U1c>eZeuiwptq*&F?yRmE; z{7!1d++u;+azb)m2k6-=B;=8b@tc!CJ((7M0L9S$aUH}7+EOp)HYuJ_o4r@JnAUpP z$eOvo?8*9nLOniBl>jbvJ3+2MC!=U?pyyY5>}rEwPW>}MU?OcOzbI-Wgid~64g0x!5Kswk% z<9F9*JWH0j>l9Y71niX6i8`WgCf6Q4WtV=`Ji>e$urn2w`HI)g(i8k$-?49~93Su8 zHl8CkAhTwD^5=%$+Z}X}$G@%61x`~{h)Yf2A=LgbNQHCY_I5dbU~^WHwXs%|CT3r({|n-*G&Q2MJFNy@gXb1S}Cqvn1pZlvKZnSb1|={Wji_c-|%Sj36pp7x;T zTR2YFyGlw94&1i~FQW&XvDlL9DqM7Vkd=h}{w&SQUi02Am_EWxYD*WyF+xV4`+ZeD z*q>OE9eG^+DSNiAH>vq9Y0hij_jK@@rSt#fitiQXMRz=ExIELdkzM)QHyu7`0^s-iWMP|;o`LpN2EMw3t z=c_s2DxG&ibkHFs`6$$f^U*^H%Ob*L+0(*9LNZ<_#KdW*Z=JJoZo-bce|l-f%>?rJ z33}J%GXzOPv#`&+DxIl!wUhJZw=%x_`1^BToBVev<-Orcet*#Wu~8AebKm246DF>> z!!LFGFuH#I)aW(&{Q^SJMwi&#I9=|$o*tWZ!pZ9<4`9e?LdA`{E zLj3cS<(<0su08KVM1bP!um1CyRd>>M_SE7-r;kdqXwH@=e^or8`xbukFVA@1&V}!Q zCc)I==8KQ_MEL7Zzn?Sf^KUDiL>chQu3iOQ=CSj${CyI#bY(fih8TG^+vPZ&*W|-@ zoK^c_C+Z@JpKLx2ke;_nT}n6Dhcfs1co7MX|50RgUbxkdM9|7h&nLQ$pw!GLU!P7j zA41WiOZoZwfxAe!9~7ud%VKe{U34!n9#a={In1mq+?@f{?7ML(*m}_)=YcA!s?Ld= zA=q4ei^$(c?S&hXt%aIR&FUB|1b)FnVd0N3KRERceqUX_p`Iyv`tm0}z)UGy-Wj!b z;`n?fMT^z-WYHbwh02}H;tC?0=pL?Wab~eedz5>7NH~4&Ua07iJ$R8jntfQcpG3L! zonA4rb~%Kh)%B!7@^0YHm}S`VptUSrg+&7a{H^4(EEZ3RFW1#SMV;^PfZUc8$L8IeohkOGvXU#FFeRkc0n^Wy3QRP1i$>E~=djnQA^~K7q+EEL} zG^vN~$EpgV^%UivX5%v5Qb!bkS4X zXlVHU8aP!D;|p;@( z*HirdWdDY`t_y2fee-jeulDP8IpU|{3_&_tpHNRGCe}a z&<*ZHjYY0y0LXbii`}Bmgy{{GtD=oEw7WH9oOjICn>;x4m+1}_hIjHoSbo?nvgYu) zwxS1xcg)qBJmi5NRFDOw9gv>pik0jFYwj%ER|_qnebe1-36Eb=lM6Be+(~B2%*+%O z?hj8&&TVgG(H6!;TxbDhS{E`42JC#q)X!kV$U2Ct8=?QYO3fXvzjlOHfCFl60H;5g zgT=K9Jih`C;4xB25k?yc1~koX4DUTzV_hg=W|D+iqiyro$;0&qSe9TG>yT@~G$=+p zF7+5kD@xy36uY_ru9y;-R*BvTySXA}t>}!5$-9-EEZRW@CogO35XMzxt7jaQ1pyp) zTeasil-bHAW;PG%{vbOx^;m%*ART<6U=g_cUw1Qsu3W1pIyz+OA6=64>rq%Vxizd{Ge265Bl_lUza53Ow5O#U53D^wA;Y# zV?el#`&I?p%mL+BhdI>xP8;Oha=D3@qd{*^ z!_~JMFx=!(hR(RkUFO$xs85}o?L@6oW@c%dlty<795-*(^%l%)gJ9S7UdU1;SNg{3 zlruaw#se4PiH!%|XiR88xNauw(8cr?E-<`R-O*PwMZ;lZR<98@%01r7yoriM%L2D; z`87lWOz_`_BS^?R5N0X8plCUORFP@b+w=^r z%*CfE6Ug0?g#w3;5M^AU(|)QFrtWkpWhH6at;TWMz6n7eD`e(x%Bz|#%LB=4)^1cP zxygZHxFtF6@LH7kx?}XM4+Z+@O_)8A_Bz^Ltc@j%{7!+4CkhyO-4ZYm=`T0lnB)?>&%!KI!2QX{q+=}199DR2X3|N{1E3j#6@x|)QDAO9x0rt>Se(}j6Cv$zl#Ks zFGXO$%A9b;rPhNJuKt=SAI;XeVRn$k;nrvqwIeKH+ta=B0gpNr7@PnJheFB=G1E~) z3E1oCya33auUX@|OXd}%#|LMokpP`ybBa{hASxAXju~v7DaO?^fgD$BGQTc1^^GhW zgX0Yzi|q(&7($q4;w^-UjN+ygJzz|+J!`PuUK5`Dss*GCl9!dMOn!@FaTrBV^!~Cf z;GxNzs1dCJ>A3Nng!anf0?S1Q9==9B`JR(v69TkE;t3&6B3vCKKbld|)xV@+O)>eoVeUm!N|-5#nm>Np zzyVjDvZ|}p${OsfrJ8^dUJhgaNL1oU*A*H<#P-7n@k+b7CSwR4i402^KP!HC_O7je zfECmJfUF)Pq@)5$(#)x9?u(j+s7CO}B7s`wQe7n2k}FN?f^)4U__dUGTEQtE{s+m? zQY1rCq(W*GAjPlMjGy#)l_jG&32sXSRrS%_m}YLNdpSV*wYf>n%&*5hsjVZ#qTJjD zSV}C$!AthBA&0CvPT(gXK3lXbKqW>%!1F#f6!-UNg(`4ECKeTO$ypU+7q_Um*cem- zFWD+Zd{`=#uh+-gi8geqsBqJ#Rxx#|HKeU-9m{I9fuX9PnLZS)Jw>B-aj;PBM#@%u zI1#G77$RAJt%Rr}SX|UmOpI+E!vHo_VTB@kVg^NY%BZBPn^@JwQZ7`KTrNVC%0}US zv5G68-iDp0*a~HPBu8pioiLe1iP9|^ntBxTmBv85MT)_5QbJCUo8Vd*@dm}`l0lyn z@xq0Vs7jTyrLwrQhp-%;y#_T2rP>m;4VJyayDFdtVE_9IekLsby* zTz5klzVP4#B4|XK@INJP+2#v_X8A*u*`mdWHR-lecf^S|htM+#(v&Huqoub(k8~L- zWO^z~r7A0ZLQFzR<}QO8wINiKhay`}sB+C!Cr`aqR%`INHIA9*h4~g(=u?X$a4u0qt<6W(;RO^{UvCqjSI|WgJD@Hy9f-2WYIa*3x zpzJL5=B{M|0g~6itk)^`^f%BH7ylo`r@ooqAHXsax%xB=}=v j0(Jvqm&l@ECM#M^&a#uA+%6rr0L%zA-H#3ld$b%Qv; literal 0 HcmV?d00001 diff --git a/server/src/wled_controller/static/fonts/orbitron-700-latin.woff2 b/server/src/wled_controller/static/fonts/orbitron-700-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..46db8ddaa0f1fecad9cb02268f0b637f38b4011a GIT binary patch literal 6528 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 literal 0 HcmV?d00001 diff --git a/server/src/wled_controller/static/js/core/graph-layout.js b/server/src/wled_controller/static/js/core/graph-layout.js index f2a4f12..0d84a0a 100644 --- a/server/src/wled_controller/static/js/core/graph-layout.js +++ b/server/src/wled_controller/static/js/core/graph-layout.js @@ -2,7 +2,7 @@ * Graph layout via ELK.js — converts entity data into positioned nodes/edges. */ -/* global ELK */ +import ELK from 'elkjs/lib/elk.bundled.js'; const NODE_WIDTH = 190; const NODE_HEIGHT = 56; diff --git a/server/src/wled_controller/static/js/features/dashboard.js b/server/src/wled_controller/static/js/features/dashboard.js index 7373cb7..8a4789c 100644 --- a/server/src/wled_controller/static/js/features/dashboard.js +++ b/server/src/wled_controller/static/js/features/dashboard.js @@ -681,7 +681,8 @@ function renderDashboardAutomation(automation, sceneMap = new Map()) { return `${apps} (${matchLabel})`; } if (c.condition_type === 'startup') return t('automations.condition.startup'); - return c.condition_type; + if (c.condition_type === 'time_of_day') return t('automations.condition.time_of_day'); + return t(`automations.condition.${c.condition_type}`) || c.condition_type; }); const logic = automation.condition_logic === 'and' ? ' & ' : ' | '; condSummary = parts.join(logic); diff --git a/server/src/wled_controller/static/js/features/perf-charts.js b/server/src/wled_controller/static/js/features/perf-charts.js index 6044835..a72357d 100644 --- a/server/src/wled_controller/static/js/features/perf-charts.js +++ b/server/src/wled_controller/static/js/features/perf-charts.js @@ -3,6 +3,10 @@ * History is seeded from the server-side ring buffer on init. */ +import { Chart, registerables } from 'chart.js'; +Chart.register(...registerables); +window.Chart = Chart; // expose globally for targets.js, dashboard.js + import { API_BASE, getHeaders } from '../core/api.js'; import { t } from '../core/i18n.js'; import { dashboardPollInterval } from '../core/state.js'; diff --git a/server/src/wled_controller/templates/index.html b/server/src/wled_controller/templates/index.html index c6c168e..a77d6de 100644 --- a/server/src/wled_controller/templates/index.html +++ b/server/src/wled_controller/templates/index.html @@ -5,9 +5,6 @@ LED Grab - - - @@ -15,23 +12,7 @@ - - - - - - - - - - - - - - - - - + @@ -221,7 +202,7 @@ - +