From e886192aebdf62e45b6e8fbdcea9d134412956db Mon Sep 17 00:00:00 2001 From: Evan Jarrett Date: Mon, 16 Mar 2026 20:26:56 -0500 Subject: [PATCH] update seamark theme, add 'delete all untagged' option on record page. add garbage collection flag for untagged --- config-appview.example.yaml | 3 +- lexicons/io/atcr/sailor/profile.json | 4 + package-lock.json | 378 +++++++++--------- package.json | 6 +- pkg/appview/db/queries.go | 68 ++++ pkg/appview/db/queries_test.go | 67 ++++ pkg/appview/handlers/images.go | 68 ++++ pkg/appview/handlers/settings.go | 44 +- pkg/appview/jetstream/processor.go | 7 + pkg/appview/middleware/registry.go | 105 ++--- pkg/appview/middleware/registry_test.go | 8 +- pkg/appview/public/js/bundle.min.js | 4 +- pkg/appview/routes/routes.go | 2 + pkg/appview/server.go | 3 +- pkg/appview/src/js/app.js | 40 ++ pkg/appview/storage/context.go | 21 +- pkg/appview/storage/manifest_store.go | 92 ++++- pkg/appview/templates/pages/repository.html | 34 +- pkg/appview/templates/pages/settings.html | 19 + pkg/atproto/lexicon.go | 5 + pkg/atproto/relays.go | 1 + pkg/hold/gc/gc.go | 176 +++++++- pkg/hold/gc/gc_test.go | 300 ++++++++++++++ themes/seamark/embed.go | 8 +- themes/seamark/public/apple-touch-icon.png | Bin 5629 -> 3394 bytes themes/seamark/public/favicon-96x96.png | Bin 4979 -> 2083 bytes themes/seamark/public/favicon.ico | Bin 15086 -> 15086 bytes themes/seamark/public/favicon.svg | 52 +-- .../seamark/public/static/seamark_seagull.svg | 34 ++ .../public/static/seamark_seagull_buoy.png | Bin 0 -> 62082 bytes .../public/web-app-manifest-192x192.png | Bin 6123 -> 3696 bytes .../public/web-app-manifest-512x512.png | Bin 17825 -> 10917 bytes themes/seamark/templates/components/hero.html | 66 +++ themes/seamark/theme.css | 10 + 34 files changed, 1304 insertions(+), 321 deletions(-) create mode 100644 themes/seamark/public/static/seamark_seagull.svg create mode 100644 themes/seamark/public/static/seamark_seagull_buoy.png create mode 100644 themes/seamark/templates/components/hero.html diff --git a/config-appview.example.yaml b/config-appview.example.yaml index 6ab814e..cd29815 100644 --- a/config-appview.example.yaml +++ b/config-appview.example.yaml @@ -45,7 +45,7 @@ ui: # SQLite/libSQL database for OAuth sessions, stars, pull counts, and device approvals. database_path: /var/lib/atcr/ui.db # Visual theme name (e.g. "seamark"). Empty uses default atcr.io branding. - theme: "" + theme: "seamark" # libSQL sync URL (libsql://...). Works with Turso cloud or self-hosted libsql-server. Leave empty for local-only SQLite. libsql_sync_url: "" # Auth token for libSQL sync. Required if libsql_sync_url is set. @@ -74,6 +74,7 @@ jetstream: relay_endpoints: - https://relay1.us-east.bsky.network - https://relay1.us-west.bsky.network + - https://zlay.waow.tech # JWT authentication settings. auth: # RSA private key for signing registry JWTs issued to Docker clients. diff --git a/lexicons/io/atcr/sailor/profile.json b/lexicons/io/atcr/sailor/profile.json index 63877da..d13a09d 100644 --- a/lexicons/io/atcr/sailor/profile.json +++ b/lexicons/io/atcr/sailor/profile.json @@ -15,6 +15,10 @@ "format": "uri", "description": "Default hold endpoint for blob storage. If null, user has opted out of defaults." }, + "autoRemoveUntagged": { + "type": "boolean", + "description": "Automatically delete manifest records that become untagged after a tag overwrite. Layers are cleaned up by hold garbage collection." + }, "createdAt": { "type": "string", "format": "datetime", diff --git a/package-lock.json b/package-lock.json index e6bb743..a334db9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,21 +11,21 @@ "actor-typeahead": "^0.1.2", "htmx-ext-json-enc": "^2.0.3", "htmx.org": "^2.0.8", - "lucide": "^0.575.0" + "lucide": "^0.577.0" }, "devDependencies": { - "@tailwindcss/cli": "^4.2.0", + "@tailwindcss/cli": "^4.2.1", "@tailwindcss/typography": "^0.5.19", "daisyui": "^5.5.19", - "esbuild": "^0.27.3", + "esbuild": "^0.27.4", "glob": "^13.0.6", "tailwindcss": "^4.2" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "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" ], @@ -40,9 +40,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", "cpu": [ "arm" ], @@ -57,9 +57,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "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" ], @@ -74,9 +74,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", "cpu": [ "x64" ], @@ -91,9 +91,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "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" ], @@ -108,9 +108,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "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" ], @@ -125,9 +125,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "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" ], @@ -142,9 +142,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "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" ], @@ -159,9 +159,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "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" ], @@ -176,9 +176,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", "cpu": [ "arm64" ], @@ -193,9 +193,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "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" ], @@ -210,9 +210,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "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" ], @@ -227,9 +227,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "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" ], @@ -244,9 +244,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", "cpu": [ "ppc64" ], @@ -261,9 +261,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "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" ], @@ -278,9 +278,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "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" ], @@ -295,9 +295,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "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" ], @@ -312,9 +312,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "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" ], @@ -329,9 +329,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", "cpu": [ "x64" ], @@ -346,9 +346,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "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" ], @@ -363,9 +363,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "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" ], @@ -380,9 +380,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", "cpu": [ "arm64" ], @@ -397,9 +397,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", "cpu": [ "x64" ], @@ -414,9 +414,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "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" ], @@ -431,9 +431,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "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" ], @@ -448,9 +448,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", "cpu": [ "x64" ], @@ -824,28 +824,28 @@ } }, "node_modules/@tailwindcss/cli": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.2.0.tgz", - "integrity": "sha512-C62SWDp+6Rj5DHJDlMyAqESpmljiQ35H4SncAcVn3Gm0rEPrKFDIdAheT74s9zAbrsa2D/L+jJaPgCO1fyZG6g==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.2.1.tgz", + "integrity": "sha512-b7MGn51IA80oSG+7fuAgzfQ+7pZBgjzbqwmiv6NO7/+a1sev32cGqnwhscT7h0EcAvMa9r7gjRylqOH8Xhc4DA==", "dev": true, "license": "MIT", "dependencies": { "@parcel/watcher": "^2.5.1", - "@tailwindcss/node": "4.2.0", - "@tailwindcss/oxide": "4.2.0", + "@tailwindcss/node": "4.2.1", + "@tailwindcss/oxide": "4.2.1", "enhanced-resolve": "^5.19.0", "mri": "^1.2.0", "picocolors": "^1.1.1", - "tailwindcss": "4.2.0" + "tailwindcss": "4.2.1" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "node_modules/@tailwindcss/node": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.0.tgz", - "integrity": "sha512-Yv+fn/o2OmL5fh/Ir62VXItdShnUxfpkMA4Y7jdeC8O81WPB8Kf6TT6GSHvnqgSwDzlB5iT7kDpeXxLsUS0T6Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", + "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", "dev": true, "license": "MIT", "dependencies": { @@ -855,37 +855,37 @@ "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.0" + "tailwindcss": "4.2.1" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.0.tgz", - "integrity": "sha512-AZqQzADaj742oqn2xjl5JbIOzZB/DGCYF/7bpvhA8KvjUj9HJkag6bBuwZvH1ps6dfgxNHyuJVlzSr2VpMgdTQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", + "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.0", - "@tailwindcss/oxide-darwin-arm64": "4.2.0", - "@tailwindcss/oxide-darwin-x64": "4.2.0", - "@tailwindcss/oxide-freebsd-x64": "4.2.0", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.0", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.0", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.0", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.0", - "@tailwindcss/oxide-linux-x64-musl": "4.2.0", - "@tailwindcss/oxide-wasm32-wasi": "4.2.0", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.0", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.0" + "@tailwindcss/oxide-android-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-x64": "4.2.1", + "@tailwindcss/oxide-freebsd-x64": "4.2.1", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-x64-musl": "4.2.1", + "@tailwindcss/oxide-wasm32-wasi": "4.2.1", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.0.tgz", - "integrity": "sha512-F0QkHAVaW/JNBWl4CEKWdZ9PMb0khw5DCELAOnu+RtjAfx5Zgw+gqCHFvqg3AirU1IAd181fwOtJQ5I8Yx5wtw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", + "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", "cpu": [ "arm64" ], @@ -900,9 +900,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.0.tgz", - "integrity": "sha512-I0QylkXsBsJMZ4nkUNSR04p6+UptjcwhcVo3Zu828ikiEqHjVmQL9RuQ6uT/cVIiKpvtVA25msu/eRV97JeNSA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", + "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", "cpu": [ "arm64" ], @@ -917,9 +917,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.0.tgz", - "integrity": "sha512-6TmQIn4p09PBrmnkvbYQ0wbZhLtbaksCDx7Y7R3FYYx0yxNA7xg5KP7dowmQ3d2JVdabIHvs3Hx4K3d5uCf8xg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", + "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", "cpu": [ "x64" ], @@ -934,9 +934,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.0.tgz", - "integrity": "sha512-qBudxDvAa2QwGlq9y7VIzhTvp2mLJ6nD/G8/tI70DCDoneaUeLWBJaPcbfzqRIWraj+o969aDQKvKW9dvkUizw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", + "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", "cpu": [ "x64" ], @@ -951,9 +951,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.0.tgz", - "integrity": "sha512-7XKkitpy5NIjFZNUQPeUyNJNJn1CJeV7rmMR+exHfTuOsg8rxIO9eNV5TSEnqRcaOK77zQpsyUkBWmPy8FgdSg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", + "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", "cpu": [ "arm" ], @@ -968,9 +968,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.0.tgz", - "integrity": "sha512-Mff5a5Q3WoQR01pGU1gr29hHM1N93xYrKkGXfPw/aRtK4bOc331Ho4Tgfsm5WDGvpevqMpdlkCojT3qlCQbCpA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", + "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", "cpu": [ "arm64" ], @@ -985,9 +985,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.0.tgz", - "integrity": "sha512-XKcSStleEVnbH6W/9DHzZv1YhjE4eSS6zOu2eRtYAIh7aV4o3vIBs+t/B15xlqoxt6ef/0uiqJVB6hkHjWD/0A==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", + "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", "cpu": [ "arm64" ], @@ -1002,9 +1002,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.0.tgz", - "integrity": "sha512-/hlXCBqn9K6fi7eAM0RsobHwJYa5V/xzWspVTzxnX+Ft9v6n+30Pz8+RxCn7sQL/vRHHLS30iQPrHQunu6/vJA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", + "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", "cpu": [ "x64" ], @@ -1019,9 +1019,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.0.tgz", - "integrity": "sha512-lKUaygq4G7sWkhQbfdRRBkaq4LY39IriqBQ+Gk6l5nKq6Ay2M2ZZb1tlIyRNgZKS8cbErTwuYSor0IIULC0SHw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", + "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", "cpu": [ "x64" ], @@ -1036,9 +1036,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.0.tgz", - "integrity": "sha512-xuDjhAsFdUuFP5W9Ze4k/o4AskUtI8bcAGU4puTYprr89QaYFmhYOPfP+d1pH+k9ets6RoE23BXZM1X1jJqoyw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", + "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1066,9 +1066,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.0.tgz", - "integrity": "sha512-2UU/15y1sWDEDNJXxEIrfWKC2Yb4YgIW5Xz2fKFqGzFWfoMHWFlfa1EJlGO2Xzjkq/tvSarh9ZTjvbxqWvLLXA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", + "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", "cpu": [ "arm64" ], @@ -1083,9 +1083,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.0.tgz", - "integrity": "sha512-CrFadmFoc+z76EV6LPG1jx6XceDsaCG3lFhyLNo/bV9ByPrE+FnBPckXQVP4XRkN76h3Fjt/a+5Er/oA/nCBvQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", + "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", "cpu": [ "x64" ], @@ -1129,9 +1129,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { @@ -1175,9 +1175,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", + "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1189,9 +1189,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "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, "license": "MIT", @@ -1202,32 +1202,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" + "@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" } }, "node_modules/glob": { @@ -1564,9 +1564,9 @@ } }, "node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -1574,9 +1574,9 @@ } }, "node_modules/lucide": { - "version": "0.575.0", - "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.575.0.tgz", - "integrity": "sha512-+xwqZpvrqPioU8bSH49zH2xARfnKyZgIjdnfbex0CrURB3q4wNFhinYN1Z9Q3lE16Q/6N9iEXnStvyS3c70RKw==", + "version": "0.577.0", + "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.577.0.tgz", + "integrity": "sha512-PpC/m5eOItp/WU/GlQPFBXDOhq6HibL73KzYP37OX3LM7VmzWQF8voEj8QRWUFvy9FIKfeDQkWYoyS1D/MdWFA==", "license": "ISC" }, "node_modules/magic-string": { @@ -1590,9 +1590,9 @@ } }, "node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -1694,9 +1694,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.0.tgz", - "integrity": "sha512-yYzTZ4++b7fNYxFfpnberEEKu43w44aqDMNM9MHMmcKuCH7lL8jJ4yJ7LGHv7rSwiqM0nkiobF9I6cLlpS2P7Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", + "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index ff031aa..db07c4d 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,10 @@ "watch": "npm run css:watch & npm run js:watch" }, "devDependencies": { - "@tailwindcss/cli": "^4.2.0", + "@tailwindcss/cli": "^4.2.1", "@tailwindcss/typography": "^0.5.19", "daisyui": "^5.5.19", - "esbuild": "^0.27.3", + "esbuild": "^0.27.4", "glob": "^13.0.6", "tailwindcss": "^4.2" }, @@ -27,6 +27,6 @@ "actor-typeahead": "^0.1.2", "htmx-ext-json-enc": "^2.0.3", "htmx.org": "^2.0.8", - "lucide": "^0.575.0" + "lucide": "^0.577.0" } } diff --git a/pkg/appview/db/queries.go b/pkg/appview/db/queries.go index 69b7291..a5e759c 100644 --- a/pkg/appview/db/queries.go +++ b/pkg/appview/db/queries.go @@ -1252,6 +1252,25 @@ func UpdateFirehoseCursor(db DBTX, cursor int64) error { return err } +// IsManifestReferenced checks if a manifest digest is referenced as a child of +// any manifest list for the given user. Used to protect manifest list children +// from auto-removal (they are untagged but still needed by their parent list). +func IsManifestReferenced(db DBTX, did, digest string) (bool, error) { + var count int + err := db.QueryRow(` + SELECT COUNT(*) FROM manifest_references mr + JOIN manifests m ON mr.manifest_id = m.id + WHERE mr.digest = ? AND m.did = ? + LIMIT 1 + `, digest, did).Scan(&count) + + if err != nil { + return false, err + } + + return count > 0, nil +} + // IsManifestTagged checks if a manifest has any tags func IsManifestTagged(db DBTX, did, repository, digest string) (bool, error) { var count int @@ -1295,6 +1314,49 @@ func GetManifestTags(db DBTX, did, repository, digest string) ([]string, error) return tags, nil } +// GetUntaggedTopLevelManifestDigests returns digests of top-level manifests that have no tags. +// Uses the same filtering logic as GetTopLevelManifests (manifest lists + orphaned single-arch). +func GetUntaggedTopLevelManifestDigests(db DBTX, did, repository string) ([]string, error) { + rows, err := db.Query(` + WITH manifest_list_children AS ( + SELECT DISTINCT mr.digest + FROM manifest_references mr + JOIN manifests m ON mr.manifest_id = m.id + WHERE m.did = ? AND m.repository = ? + ) + SELECT m.digest + FROM manifests m + LEFT JOIN tags t ON m.digest = t.digest AND m.did = t.did AND m.repository = t.repository + WHERE m.did = ? AND m.repository = ? + AND ( + m.media_type LIKE '%index%' OR m.media_type LIKE '%manifest.list%' + OR + m.digest NOT IN (SELECT digest FROM manifest_list_children WHERE digest IS NOT NULL) + ) + GROUP BY m.id + HAVING COUNT(t.tag) = 0 + `, did, repository, did, repository) + if err != nil { + return nil, err + } + defer rows.Close() + + var digests []string + for rows.Next() { + var digest string + if err := rows.Scan(&digest); err != nil { + return nil, err + } + digests = append(digests, digest) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return digests, nil +} + // GetAttestationDetails returns attestation manifests and their layers for a manifest list. // Joins manifest_references (is_attestation=true) → manifests → layers. func GetAttestationDetails(db DBTX, did, repository, manifestListDigest string) ([]AttestationDetail, error) { @@ -1795,6 +1857,12 @@ func (h *HoldDIDDB) UpdateManifestHoldDID(did, oldHoldDID, newHoldDID string) (i return UpdateManifestHoldDID(h.db, did, oldHoldDID, newHoldDID) } +// IsManifestReferenced checks if a digest is a child of any manifest list for the user. +// Implements storage.ManifestReferenceChecker. +func (h *HoldDIDDB) IsManifestReferenced(did, digest string) (bool, error) { + return IsManifestReferenced(h.db, did, digest) +} + // RepoCardSortOrder specifies how repo cards should be sorted type RepoCardSortOrder string diff --git a/pkg/appview/db/queries_test.go b/pkg/appview/db/queries_test.go index ee0faf4..9abb392 100644 --- a/pkg/appview/db/queries_test.go +++ b/pkg/appview/db/queries_test.go @@ -1307,3 +1307,70 @@ func TestDeleteUserData(t *testing.T) { t.Errorf("Deleting non-existent user should not error, got: %v", err) } } + +func TestIsManifestReferenced(t *testing.T) { + db, err := InitDB(":memory:", LibsqlConfig{}) + if err != nil { + t.Fatalf("Failed to init database: %v", err) + } + defer db.Close() + + // Insert test user + if err := UpsertUser(db, &User{ + DID: "did:plc:test123", + Handle: "testuser.bsky.social", + PDSEndpoint: "https://test.pds.example.com", + LastSeen: time.Now(), + }); err != nil { + t.Fatalf("Failed to insert user: %v", err) + } + + // Insert a manifest list + _, err = db.Exec(` + INSERT INTO manifests (did, repository, digest, hold_endpoint, schema_version, media_type, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?) + `, "did:plc:test123", "myapp", "sha256:indexabc", "did:web:hold.example.com", 2, + "application/vnd.oci.image.index.v1+json", time.Now()) + if err != nil { + t.Fatalf("Failed to insert manifest list: %v", err) + } + + var manifestID int64 + db.QueryRow(`SELECT id FROM manifests WHERE digest = ?`, "sha256:indexabc").Scan(&manifestID) + + // Insert a child manifest reference + _, err = db.Exec(` + INSERT INTO manifest_references (manifest_id, digest, media_type, size, platform_architecture, platform_os, reference_index) + VALUES (?, ?, ?, ?, ?, ?, ?) + `, manifestID, "sha256:childdef", "application/vnd.oci.image.manifest.v1+json", 1000, "amd64", "linux", 0) + if err != nil { + t.Fatalf("Failed to insert manifest reference: %v", err) + } + + // Test 1: child digest should be referenced + referenced, err := IsManifestReferenced(db, "did:plc:test123", "sha256:childdef") + if err != nil { + t.Fatalf("IsManifestReferenced error: %v", err) + } + if !referenced { + t.Error("Expected sha256:childdef to be referenced as a manifest list child") + } + + // Test 2: unrelated digest should NOT be referenced + referenced, err = IsManifestReferenced(db, "did:plc:test123", "sha256:unrelated") + if err != nil { + t.Fatalf("IsManifestReferenced error: %v", err) + } + if referenced { + t.Error("Expected sha256:unrelated to NOT be referenced") + } + + // Test 3: same digest but different user should NOT be referenced + referenced, err = IsManifestReferenced(db, "did:plc:otheruser", "sha256:childdef") + if err != nil { + t.Fatalf("IsManifestReferenced error: %v", err) + } + if referenced { + t.Error("Expected sha256:childdef to NOT be referenced for different user") + } +} diff --git a/pkg/appview/handlers/images.go b/pkg/appview/handlers/images.go index c31f2e0..49b639b 100644 --- a/pkg/appview/handlers/images.go +++ b/pkg/appview/handlers/images.go @@ -179,6 +179,74 @@ func (h *DeleteManifestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request w.WriteHeader(http.StatusOK) } +// deleteUntaggedRequest is the JSON body for delete untagged manifests requests +type deleteUntaggedRequest struct { + Repo string `json:"repo"` +} + +// DeleteUntaggedManifestsHandler handles bulk-deleting all untagged manifests in a repository +type DeleteUntaggedManifestsHandler struct { + BaseUIHandler +} + +func (h *DeleteUntaggedManifestsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + user := middleware.GetUser(r) + if user == nil { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + var req deleteUntaggedRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + digests, err := db.GetUntaggedTopLevelManifestDigests(h.DB, user.DID, req.Repo) + if err != nil { + http.Error(w, fmt.Sprintf("Failed to query untagged manifests: %v", err), http.StatusInternalServerError) + return + } + + if len(digests) == 0 { + render.JSON(w, r, map[string]int{"deleted": 0}) + return + } + + pdsClient := atproto.NewClientWithSessionProvider(user.PDSEndpoint, user.DID, h.Refresher) + + deleted := 0 + for _, digest := range digests { + rkey := strings.TrimPrefix(digest, "sha256:") + + if err := pdsClient.DeleteRecord(r.Context(), atproto.ManifestCollection, rkey); err != nil { + if handleOAuthError(r.Context(), h.Refresher, user.DID, err) { + http.Error(w, "Authentication failed, please log in again", http.StatusUnauthorized) + return + } + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, map[string]any{ + "error": fmt.Sprintf("Failed to delete manifest %s from PDS: %v", digest, err), + "deleted": deleted, + }) + return + } + + if err := db.DeleteManifest(h.DB, user.DID, req.Repo, digest); err != nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, map[string]any{ + "error": fmt.Sprintf("Failed to delete manifest %s from cache: %v", digest, err), + "deleted": deleted, + }) + return + } + + deleted++ + } + + render.JSON(w, r, map[string]int{"deleted": deleted}) +} + // UploadAvatarHandler handles uploading/updating a repository avatar type UploadAvatarHandler struct { BaseUIHandler diff --git a/pkg/appview/handlers/settings.go b/pkg/appview/handlers/settings.go index 7830400..4e536da 100644 --- a/pkg/appview/handlers/settings.go +++ b/pkg/appview/handlers/settings.go @@ -132,10 +132,11 @@ func (h *SettingsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { PageData Meta *PageMeta Profile struct { - Handle string - DID string - PDSEndpoint string - DefaultHold string + Handle string + DID string + PDSEndpoint string + DefaultHold string + AutoRemoveUntagged bool } ActiveHold *HoldDisplay OtherHolds []HoldDisplay @@ -156,6 +157,7 @@ func (h *SettingsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { data.Profile.DID = user.DID data.Profile.PDSEndpoint = user.PDSEndpoint data.Profile.DefaultHold = profile.DefaultHold + data.Profile.AutoRemoveUntagged = profile.AutoRemoveUntagged if err := h.Templates.ExecuteTemplate(w, "settings", data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -381,6 +383,40 @@ func (h *UpdateDefaultHoldHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ } } +// UpdateAutoRemoveUntaggedHandler handles toggling the auto-remove-untagged setting +type UpdateAutoRemoveUntaggedHandler struct { + BaseUIHandler +} + +func (h *UpdateAutoRemoveUntaggedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + user := middleware.GetUser(r) + if user == nil { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Create ATProto client with session provider + client := atproto.NewClientWithSessionProvider(user.PDSEndpoint, user.DID, h.Refresher) + + // Fetch existing profile + profile, err := storage.GetProfile(r.Context(), client) + if err != nil || profile == nil { + http.Error(w, "Failed to fetch profile", http.StatusInternalServerError) + return + } + + // Toggle the setting (checkbox sends value when checked, absent when unchecked) + profile.AutoRemoveUntagged = !profile.AutoRemoveUntagged + profile.UpdatedAt = time.Now() + + if err := storage.UpdateProfile(r.Context(), client, profile); err != nil { + http.Error(w, "Failed to update profile: "+err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusNoContent) +} + // refreshCaptainRecord fetches a hold's captain record via XRPC and caches it locally. // This ensures badge tiers and other captain metadata are available immediately // without waiting for Jetstream or the next backfill cycle. diff --git a/pkg/appview/jetstream/processor.go b/pkg/appview/jetstream/processor.go index 3023f55..6f8049b 100644 --- a/pkg/appview/jetstream/processor.go +++ b/pkg/appview/jetstream/processor.go @@ -772,6 +772,13 @@ func (p *Processor) ProcessStats(ctx context.Context, holdDID string, recordData return fmt.Errorf("failed to unmarshal stats record: %w", err) } + // Ensure the owner user exists in the users table (repository_stats has FK to users.did) + if !isDelete && statsRecord.OwnerDID != "" { + if err := p.EnsureUserExists(ctx, statsRecord.OwnerDID); err != nil { + return fmt.Errorf("failed to ensure stats owner user exists: %w", err) + } + } + if isDelete { // Delete from in-memory cache p.statsCache.Delete(holdDID, statsRecord.OwnerDID, statsRecord.Repository) diff --git a/pkg/appview/middleware/registry.go b/pkg/appview/middleware/registry.go index e02c089..de25b37 100644 --- a/pkg/appview/middleware/registry.go +++ b/pkg/appview/middleware/registry.go @@ -170,10 +170,11 @@ func (vc *validationCache) getOrFetch(ctx context.Context, cacheKey string, fetc // These are set by main.go during startup and copied into NamespaceResolver instances. // After initialization, request handling uses the NamespaceResolver's instance fields. var ( - globalRefresher *oauth.Refresher - globalDatabase storage.HoldDIDLookup - globalAuthorizer auth.HoldAuthorizer - globalWebhookDispatcher storage.PushWebhookDispatcher + globalRefresher *oauth.Refresher + globalDatabase storage.HoldDIDLookup + globalAuthorizer auth.HoldAuthorizer + globalWebhookDispatcher storage.PushWebhookDispatcher + globalManifestRefChecker storage.ManifestReferenceChecker ) // SetGlobalRefresher sets the OAuth refresher instance during initialization @@ -188,6 +189,11 @@ func SetGlobalDatabase(database storage.HoldDIDLookup) { globalDatabase = database } +// SetGlobalManifestRefChecker sets the manifest reference checker during initialization +func SetGlobalManifestRefChecker(checker storage.ManifestReferenceChecker) { + globalManifestRefChecker = checker +} + // SetGlobalAuthorizer sets the authorizer instance during initialization // Must be called before the registry starts serving requests func SetGlobalAuthorizer(authorizer auth.HoldAuthorizer) { @@ -216,15 +222,16 @@ func init() { // NamespaceResolver wraps a namespace and resolves names type NamespaceResolver struct { distribution.Namespace - defaultHoldDID string // Default hold DID (e.g., "did:web:hold01.atcr.io") - baseURL string // Base URL for error messages (e.g., "https://atcr.io") - testMode bool // If true, fallback to default hold when user's hold is unreachable - refresher *oauth.Refresher // OAuth session manager (copied from global on init) - database storage.HoldDIDLookup // Database for hold DID lookups (copied from global on init) - authorizer auth.HoldAuthorizer // Hold authorization (copied from global on init) - webhookDispatcher storage.PushWebhookDispatcher // Push webhook dispatcher (copied from global on init) - validationCache *validationCache // Request-level service token cache - readmeFetcher *readme.Fetcher // README fetcher for repo pages + defaultHoldDID string // Default hold DID (e.g., "did:web:hold01.atcr.io") + baseURL string // Base URL for error messages (e.g., "https://atcr.io") + testMode bool // If true, fallback to default hold when user's hold is unreachable + refresher *oauth.Refresher // OAuth session manager (copied from global on init) + database storage.HoldDIDLookup // Database for hold DID lookups (copied from global on init) + authorizer auth.HoldAuthorizer // Hold authorization (copied from global on init) + webhookDispatcher storage.PushWebhookDispatcher // Push webhook dispatcher (copied from global on init) + manifestRefChecker storage.ManifestReferenceChecker // Manifest reference checker (copied from global on init) + validationCache *validationCache // Request-level service token cache + readmeFetcher *readme.Fetcher // README fetcher for repo pages } // initATProtoResolver initializes the name resolution middleware @@ -251,16 +258,17 @@ func initATProtoResolver(ctx context.Context, ns distribution.Namespace, _ drive // Copy shared services from globals into the instance // This avoids accessing globals during request handling return &NamespaceResolver{ - Namespace: ns, - defaultHoldDID: defaultHoldDID, - baseURL: baseURL, - testMode: testMode, - refresher: globalRefresher, - database: globalDatabase, - authorizer: globalAuthorizer, - webhookDispatcher: globalWebhookDispatcher, - validationCache: newValidationCache(), - readmeFetcher: readme.NewFetcher(), + Namespace: ns, + defaultHoldDID: defaultHoldDID, + baseURL: baseURL, + testMode: testMode, + refresher: globalRefresher, + database: globalDatabase, + authorizer: globalAuthorizer, + webhookDispatcher: globalWebhookDispatcher, + manifestRefChecker: globalManifestRefChecker, + validationCache: newValidationCache(), + readmeFetcher: readme.NewFetcher(), }, nil } @@ -297,7 +305,8 @@ func (nr *NamespaceResolver) Repository(ctx context.Context, name reference.Name slog.Debug("Resolved identity", "component", "registry/middleware", "did", did, "pds", pdsEndpoint, "handle", handle) // Query for hold DID - either user's hold or default hold service - holdDID := nr.findHoldDID(ctx, did, pdsEndpoint) + // Also returns the sailor profile so we can read preferences (e.g. AutoRemoveUntagged) + holdDID, sailorProfile := nr.findHoldDIDAndProfile(ctx, did, pdsEndpoint) if holdDID == "" { // This is a fatal configuration error - registry cannot function without a hold service return nil, fmt.Errorf("no hold DID configured: ensure default_hold_did is set in middleware config") @@ -476,22 +485,24 @@ func (nr *NamespaceResolver) Repository(ctx context.Context, name reference.Name // 3. The refresher already caches sessions efficiently (in-memory + DB) // 4. Caching the repository with a stale ATProtoClient causes refresh token errors registryCtx := &storage.RegistryContext{ - DID: did, - Handle: handle, - HoldDID: holdDID, - HoldURL: holdURL, - PDSEndpoint: pdsEndpoint, - Repository: repositoryName, - ServiceToken: serviceToken, // Cached service token from puller's PDS - ATProtoClient: atprotoClient, - AuthMethod: authMethod, // Auth method from JWT token - PullerDID: pullerDID, // Authenticated user making the request - PullerPDSEndpoint: pullerPDSEndpoint, // Puller's PDS for service token refresh - Database: nr.database, - Authorizer: nr.authorizer, - Refresher: nr.refresher, - ReadmeFetcher: nr.readmeFetcher, - WebhookDispatcher: nr.webhookDispatcher, + DID: did, + Handle: handle, + HoldDID: holdDID, + HoldURL: holdURL, + PDSEndpoint: pdsEndpoint, + Repository: repositoryName, + ServiceToken: serviceToken, // Cached service token from puller's PDS + ATProtoClient: atprotoClient, + AuthMethod: authMethod, // Auth method from JWT token + PullerDID: pullerDID, // Authenticated user making the request + PullerPDSEndpoint: pullerPDSEndpoint, // Puller's PDS for service token refresh + AutoRemoveUntagged: sailorProfile != nil && sailorProfile.AutoRemoveUntagged, + Database: nr.database, + Authorizer: nr.authorizer, + Refresher: nr.refresher, + ReadmeFetcher: nr.readmeFetcher, + WebhookDispatcher: nr.webhookDispatcher, + ManifestRefChecker: nr.manifestRefChecker, } return storage.NewRoutingRepository(repo, registryCtx), nil @@ -512,12 +523,14 @@ func (nr *NamespaceResolver) BlobStatter() distribution.BlobStatter { return nr.Namespace.BlobStatter() } -// findHoldDID determines which hold DID to use for blob storage +// findHoldDIDAndProfile determines which hold DID to use for blob storage and +// returns the user's sailor profile (if available) for reading preferences like +// AutoRemoveUntagged without an extra PDS call. // Priority order: // 1. User's sailor profile defaultHold (if set) // 2. AppView's default hold DID // Returns a hold DID (e.g., "did:web:hold01.atcr.io"), or empty string if none configured -func (nr *NamespaceResolver) findHoldDID(ctx context.Context, did, pdsEndpoint string) string { +func (nr *NamespaceResolver) findHoldDIDAndProfile(ctx context.Context, did, pdsEndpoint string) (string, *atproto.SailorProfileRecord) { // Create ATProto client (without auth - reading public records) client := atproto.NewClient(pdsEndpoint, did, "") @@ -533,16 +546,16 @@ func (nr *NamespaceResolver) findHoldDID(ctx context.Context, did, pdsEndpoint s // In test mode, verify it's reachable before using it if nr.testMode { if nr.isHoldReachable(ctx, profile.DefaultHold) { - return profile.DefaultHold + return profile.DefaultHold, profile } slog.Debug("User's defaultHold unreachable, falling back to default", "component", "registry/middleware/testmode", "default_hold", profile.DefaultHold) - return nr.defaultHoldDID + return nr.defaultHoldDID, profile } - return profile.DefaultHold + return profile.DefaultHold, profile } // No profile defaultHold - use AppView default - return nr.defaultHoldDID + return nr.defaultHoldDID, profile } // resolveSuccessor checks if a hold has declared a successor and returns it. diff --git a/pkg/appview/middleware/registry_test.go b/pkg/appview/middleware/registry_test.go index f2e6235..80d9bf9 100644 --- a/pkg/appview/middleware/registry_test.go +++ b/pkg/appview/middleware/registry_test.go @@ -160,7 +160,7 @@ func TestFindHoldDID_DefaultFallback(t *testing.T) { } ctx := context.Background() - holdDID := resolver.findHoldDID(ctx, "did:plc:test123", mockPDS.URL) + holdDID, _ := resolver.findHoldDIDAndProfile(ctx, "did:plc:test123", mockPDS.URL) assert.Equal(t, "did:web:default.atcr.io", holdDID, "should fall back to default hold DID") } @@ -188,7 +188,7 @@ func TestFindHoldDID_SailorProfile(t *testing.T) { } ctx := context.Background() - holdDID := resolver.findHoldDID(ctx, "did:plc:test123", mockPDS.URL) + holdDID, _ := resolver.findHoldDIDAndProfile(ctx, "did:plc:test123", mockPDS.URL) assert.Equal(t, "did:web:user.hold.io", holdDID, "should use sailor profile's defaultHold") } @@ -215,7 +215,7 @@ func TestFindHoldDID_Priority(t *testing.T) { } ctx := context.Background() - holdDID := resolver.findHoldDID(ctx, "did:plc:test123", mockPDS.URL) + holdDID, _ := resolver.findHoldDIDAndProfile(ctx, "did:plc:test123", mockPDS.URL) // Profile should take priority over hold records and default assert.Equal(t, "did:web:profile.hold.io", holdDID, "should prioritize sailor profile over hold records") @@ -244,7 +244,7 @@ func TestFindHoldDID_TestModeFallback(t *testing.T) { } ctx := context.Background() - holdDID := resolver.findHoldDID(ctx, "did:plc:test123", mockPDS.URL) + holdDID, _ := resolver.findHoldDIDAndProfile(ctx, "did:plc:test123", mockPDS.URL) // In test mode with unreachable hold, should fall back to default assert.Equal(t, "did:web:default.atcr.io", holdDID, "should fall back to default in test mode when hold unreachable") diff --git a/pkg/appview/public/js/bundle.min.js b/pkg/appview/public/js/bundle.min.js index 739403f..7f74411 100644 --- a/pkg/appview/public/js/bundle.min.js +++ b/pkg/appview/public/js/bundle.min.js @@ -1,4 +1,4 @@ -var ae=(function(){"use strict";let htmx={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(e,t){return getInputValues(e,t||"post").values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:!0,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null,disableInheritance:!1,responseHandling:[{code:"204",swap:!1},{code:"[23]..",swap:!0},{code:"[45]..",swap:!1,error:!0}],allowNestedOobSwaps:!0,historyRestoreAsHxRequest:!0,reportValidityOfForms:!1},parseInterval:null,location,_:null,version:"2.0.8"};htmx.onLoad=onLoadHelper,htmx.process=processNode,htmx.on=addEventListenerImpl,htmx.off=removeEventListenerImpl,htmx.trigger=triggerEvent,htmx.ajax=ajaxHelper,htmx.find=find,htmx.findAll=findAll,htmx.closest=closest,htmx.remove=removeElement,htmx.addClass=addClassToElement,htmx.removeClass=removeClassFromElement,htmx.toggleClass=toggleClassOnElement,htmx.takeClass=takeClassForElement,htmx.swap=swap,htmx.defineExtension=defineExtension,htmx.removeExtension=removeExtension,htmx.logAll=logAll,htmx.logNone=logNone,htmx.parseInterval=parseInterval,htmx._=internalEval;let internalAPI={addTriggerHandler,bodyContains,canAccessLocalStorage,findThisElement,filterValues,swap,hasAttribute,getAttributeValue,getClosestAttributeValue,getClosestMatch,getExpressionVars,getHeaders,getInputValues,getInternalData,getSwapSpecification,getTriggerSpecs,getTarget,makeFragment,mergeObjects,makeSettleInfo,oobSwap,querySelectorExt,settleImmediately,shouldCancel,triggerEvent,triggerErrorEvent,withExtensions},VERBS=["get","post","put","delete","patch"],VERB_SELECTOR=VERBS.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function parseInterval(e){if(e==null)return;let t=NaN;return e.slice(-2)=="ms"?t=parseFloat(e.slice(0,-2)):e.slice(-1)=="s"?t=parseFloat(e.slice(0,-1))*1e3:e.slice(-1)=="m"?t=parseFloat(e.slice(0,-1))*1e3*60:t=parseFloat(e),isNaN(t)?void 0:t}function getRawAttribute(e,t){return e instanceof Element&&e.getAttribute(t)}function hasAttribute(e,t){return!!e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function getAttributeValue(e,t){return getRawAttribute(e,t)||getRawAttribute(e,"data-"+t)}function parentElt(e){let t=e.parentElement;return!t&&e.parentNode instanceof ShadowRoot?e.parentNode:t}function getDocument(){return document}function getRootNode(e,t){return e.getRootNode?e.getRootNode({composed:t}):getDocument()}function getClosestMatch(e,t){for(;e&&!t(e);)e=parentElt(e);return e||null}function getAttributeValueWithDisinheritance(e,t,n){let r=getAttributeValue(t,n),o=getAttributeValue(t,"hx-disinherit");var i=getAttributeValue(t,"hx-inherit");if(e!==t){if(htmx.config.disableInheritance)return i&&(i==="*"||i.split(" ").indexOf(n)>=0)?r:null;if(o&&(o==="*"||o.split(" ").indexOf(n)>=0))return"unset"}return r}function getClosestAttributeValue(e,t){let n=null;if(getClosestMatch(e,function(r){return!!(n=getAttributeValueWithDisinheritance(e,asElement(r),t))}),n!=="unset")return n}function matches(e,t){return e instanceof Element&&e.matches(t)}function getStartTag(e){let n=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i.exec(e);return n?n[1].toLowerCase():""}function parseHTML(e){return"parseHTMLUnsafe"in Document?Document.parseHTMLUnsafe(e):new DOMParser().parseFromString(e,"text/html")}function takeChildrenFor(e,t){for(;t.childNodes.length>0;)e.append(t.childNodes[0])}function duplicateScript(e){let t=getDocument().createElement("script");return forEach(e.attributes,function(n){t.setAttribute(n.name,n.value)}),t.textContent=e.textContent,t.async=!1,htmx.config.inlineScriptNonce&&(t.nonce=htmx.config.inlineScriptNonce),t}function isJavaScriptScriptNode(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}function normalizeScriptTags(e){Array.from(e.querySelectorAll("script")).forEach(t=>{if(isJavaScriptScriptNode(t)){let n=duplicateScript(t),r=t.parentNode;try{r.insertBefore(n,t)}catch(o){logError(o)}finally{t.remove()}}})}function makeFragment(e){let t=e.replace(/]*)?>[\s\S]*?<\/head>/i,""),n=getStartTag(t),r;if(n==="html"){r=new DocumentFragment;let i=parseHTML(e);takeChildrenFor(r,i.body),r.title=i.title}else if(n==="body"){r=new DocumentFragment;let i=parseHTML(t);takeChildrenFor(r,i.body),r.title=i.title}else{let i=parseHTML('");r=i.querySelector("template").content,r.title=i.title;var o=r.querySelector("title");o&&o.parentNode===r&&(o.remove(),r.title=o.innerText)}return r&&(htmx.config.allowScriptTags?normalizeScriptTags(r):r.querySelectorAll("script").forEach(i=>i.remove())),r}function maybeCall(e){e&&e()}function isType(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function isFunction(e){return typeof e=="function"}function isRawObject(e){return isType(e,"Object")}function getInternalData(e){let t="htmx-internal-data",n=e[t];return n||(n=e[t]={}),n}function toArray(e){let t=[];if(e)for(let n=0;n=0}function bodyContains(e){return e.getRootNode({composed:!0})===document}function splitOnWhitespace(e){return e.trim().split(/\s+/)}function mergeObjects(e,t){for(let n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function parseJSON(e){try{return JSON.parse(e)}catch(t){return logError(t),null}}function canAccessLocalStorage(){let e="htmx:sessionStorageTest";try{return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch{return!1}}function normalizePath(e){let t=new URL(e,"http://x");return t&&(e=t.pathname+t.search),e!="/"&&(e=e.replace(/\/+$/,"")),e}function internalEval(str){return maybeEval(getDocument().body,function(){return eval(str)})}function onLoadHelper(e){return htmx.on("htmx:load",function(n){e(n.detail.elt)})}function logAll(){htmx.logger=function(e,t,n){console&&console.log(t,e,n)}}function logNone(){htmx.logger=null}function find(e,t){return typeof e!="string"?e.querySelector(t):find(getDocument(),e)}function findAll(e,t){return typeof e!="string"?e.querySelectorAll(t):findAll(getDocument(),e)}function getWindow(){return window}function removeElement(e,t){e=resolveTarget(e),t?getWindow().setTimeout(function(){removeElement(e),e=null},t):parentElt(e).removeChild(e)}function asElement(e){return e instanceof Element?e:null}function asHtmlElement(e){return e instanceof HTMLElement?e:null}function asString(e){return typeof e=="string"?e:null}function asParentNode(e){return e instanceof Element||e instanceof Document||e instanceof DocumentFragment?e:null}function addClassToElement(e,t,n){e=asElement(resolveTarget(e)),e&&(n?getWindow().setTimeout(function(){addClassToElement(e,t),e=null},n):e.classList&&e.classList.add(t))}function removeClassFromElement(e,t,n){let r=asElement(resolveTarget(e));r&&(n?getWindow().setTimeout(function(){removeClassFromElement(r,t),r=null},n):r.classList&&(r.classList.remove(t),r.classList.length===0&&r.removeAttribute("class")))}function toggleClassOnElement(e,t){e=resolveTarget(e),e.classList.toggle(t)}function takeClassForElement(e,t){e=resolveTarget(e),forEach(e.parentElement.children,function(n){removeClassFromElement(n,t)}),addClassToElement(asElement(e),t)}function closest(e,t){return e=asElement(resolveTarget(e)),e?e.closest(t):null}function startsWith(e,t){return e.substring(0,t.length)===t}function endsWith(e,t){return e.substring(e.length-t.length)===t}function normalizeSelector(e){let t=e.trim();return startsWith(t,"<")&&endsWith(t,"/>")?t.substring(1,t.length-2):t}function querySelectorAllExt(e,t,n){if(t.indexOf("global ")===0)return querySelectorAllExt(e,t.slice(7),!0);e=resolveTarget(e);let r=[];{let s=0,a=0;for(let l=0;l"&&s--}a0;){let s=normalizeSelector(r.shift()),a;s.indexOf("closest ")===0?a=closest(asElement(e),normalizeSelector(s.slice(8))):s.indexOf("find ")===0?a=find(asParentNode(e),normalizeSelector(s.slice(5))):s==="next"||s==="nextElementSibling"?a=asElement(e).nextElementSibling:s.indexOf("next ")===0?a=scanForwardQuery(e,normalizeSelector(s.slice(5)),!!n):s==="previous"||s==="previousElementSibling"?a=asElement(e).previousElementSibling:s.indexOf("previous ")===0?a=scanBackwardsQuery(e,normalizeSelector(s.slice(9)),!!n):s==="document"?a=document:s==="window"?a=window:s==="body"?a=document.body:s==="root"?a=getRootNode(e,!!n):s==="host"?a=e.getRootNode().host:i.push(s),a&&o.push(a)}if(i.length>0){let s=i.join(","),a=asParentNode(getRootNode(e,!!n));o.push(...toArray(a.querySelectorAll(s)))}return o}var scanForwardQuery=function(e,t,n){let r=asParentNode(getRootNode(e,n)).querySelectorAll(t);for(let o=0;o=0;o--){let i=r[o];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING)return i}};function querySelectorExt(e,t){return typeof e!="string"?querySelectorAllExt(e,t)[0]:querySelectorAllExt(getDocument().body,e)[0]}function resolveTarget(e,t){return typeof e=="string"?find(asParentNode(t)||document,e):e}function processEventArgs(e,t,n,r){return isFunction(t)?{target:getDocument().body,event:asString(e),listener:t,options:n}:{target:resolveTarget(e),event:asString(t),listener:n,options:r}}function addEventListenerImpl(e,t,n,r){return ready(function(){let i=processEventArgs(e,t,n,r);i.target.addEventListener(i.event,i.listener,i.options)}),isFunction(t)?t:n}function removeEventListenerImpl(e,t,n){return ready(function(){let r=processEventArgs(e,t,n);r.target.removeEventListener(r.event,r.listener)}),isFunction(t)?t:n}let DUMMY_ELT=getDocument().createElement("output");function findAttributeTargets(e,t){let n=getClosestAttributeValue(e,t);if(n){if(n==="this")return[findThisElement(e,t)];{let r=querySelectorAllExt(e,n);if(/(^|,)(\s*)inherit(\s*)($|,)/.test(n)){let i=asElement(getClosestMatch(e,function(s){return s!==e&&hasAttribute(asElement(s),t)}));i&&r.push(...findAttributeTargets(i,t))}return r.length===0?(logError('The selector "'+n+'" on '+t+" returned no matches!"),[DUMMY_ELT]):r}}}function findThisElement(e,t){return asElement(getClosestMatch(e,function(n){return getAttributeValue(asElement(n),t)!=null}))}function getTarget(e){let t=getClosestAttributeValue(e,"hx-target");return t?t==="this"?findThisElement(e,"hx-target"):querySelectorExt(e,t):getInternalData(e).boosted?getDocument().body:e}function shouldSettleAttribute(e){return htmx.config.attributesToSettle.includes(e)}function cloneAttributes(e,t){forEach(Array.from(e.attributes),function(n){!t.hasAttribute(n.name)&&shouldSettleAttribute(n.name)&&e.removeAttribute(n.name)}),forEach(t.attributes,function(n){shouldSettleAttribute(n.name)&&e.setAttribute(n.name,n.value)})}function isInlineSwap(e,t){let n=getExtensions(t);for(let r=0;r0?(i=e.substring(0,e.indexOf(":")),o=e.substring(e.indexOf(":")+1)):i=e),t.removeAttribute("hx-swap-oob"),t.removeAttribute("data-hx-swap-oob");let s=querySelectorAllExt(r,o,!1);return s.length?(forEach(s,function(a){let l,c=t.cloneNode(!0);l=getDocument().createDocumentFragment(),l.appendChild(c),isInlineSwap(i,a)||(l=asParentNode(c));let h={shouldSwap:!0,target:a,fragment:l};triggerEvent(a,"htmx:oobBeforeSwap",h)&&(a=h.target,h.shouldSwap&&(handlePreservedElements(l),swapWithStyle(i,a,a,l,n),restorePreservedElements()),forEach(n.elts,function(u){triggerEvent(u,"htmx:oobAfterSwap",h)}))}),t.parentNode.removeChild(t)):(t.parentNode.removeChild(t),triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:t})),e}function restorePreservedElements(){let e=find("#--htmx-preserve-pantry--");if(e){for(let t of[...e.children]){let n=find("#"+t.id);n.parentNode.moveBefore(t,n),n.remove()}e.remove()}}function handlePreservedElements(e){forEach(findAll(e,"[hx-preserve], [data-hx-preserve]"),function(t){let n=getAttributeValue(t,"id"),r=getDocument().getElementById(n);if(r!=null)if(t.moveBefore){let o=find("#--htmx-preserve-pantry--");o==null&&(getDocument().body.insertAdjacentHTML("afterend","
"),o=find("#--htmx-preserve-pantry--")),o.moveBefore(r,null)}else t.parentNode.replaceChild(r,t)})}function handleAttributes(e,t,n){forEach(t.querySelectorAll("[id]"),function(r){let o=getRawAttribute(r,"id");if(o&&o.length>0){let i=o.replace("'","\\'"),s=r.tagName.replace(":","\\:"),a=asParentNode(e),l=a&&a.querySelector(s+"[id='"+i+"']");if(l&&l!==a){let c=r.cloneNode();cloneAttributes(r,l),n.tasks.push(function(){cloneAttributes(r,c)})}}})}function makeAjaxLoadTask(e){return function(){removeClassFromElement(e,htmx.config.addedClass),processNode(asElement(e)),processFocus(asParentNode(e)),triggerEvent(e,"htmx:load")}}function processFocus(e){let t="[autofocus]",n=asHtmlElement(matches(e,t)?e:e.querySelector(t));n?.focus()}function insertNodesBefore(e,t,n,r){for(handleAttributes(e,n,r);n.childNodes.length>0;){let o=n.firstChild;addClassToElement(asElement(o),htmx.config.addedClass),e.insertBefore(o,t),o.nodeType!==Node.TEXT_NODE&&o.nodeType!==Node.COMMENT_NODE&&r.tasks.push(makeAjaxLoadTask(o))}}function stringHash(e,t){let n=0;for(;n0}function swap(e,t,n,r){r||(r={});let o=null,i=null,s=function(){maybeCall(r.beforeSwapCallback),e=resolveTarget(e);let c=r.contextElement?getRootNode(r.contextElement,!1):getDocument(),h=document.activeElement,u={};u={elt:h,start:h?h.selectionStart:null,end:h?h.selectionEnd:null};let f=makeSettleInfo(e);if(n.swapStyle==="textContent")e.textContent=t;else{let d=makeFragment(t);if(f.title=r.title||d.title,r.historyRequest&&(d=d.querySelector("[hx-history-elt],[data-hx-history-elt]")||d),r.selectOOB){let y=r.selectOOB.split(",");for(let m=0;m0?getWindow().setTimeout(v,n.settleDelay):v()},a=htmx.config.globalViewTransitions;n.hasOwnProperty("transition")&&(a=n.transition);let l=r.contextElement||getDocument();if(a&&triggerEvent(l,"htmx:beforeTransition",r.eventInfo)&&typeof Promise<"u"&&document.startViewTransition){let c=new Promise(function(u,f){o=u,i=f}),h=s;s=function(){document.startViewTransition(function(){return h(),c})}}try{n?.swapDelay&&n.swapDelay>0?getWindow().setTimeout(s,n.swapDelay):s()}catch(c){throw triggerErrorEvent(l,"htmx:swapError",r.eventInfo),maybeCall(i),c}}function handleTriggerHeader(e,t,n){let r=e.getResponseHeader(t);if(r.indexOf("{")===0){let o=parseJSON(r);for(let i in o)if(o.hasOwnProperty(i)){let s=o[i];isRawObject(s)?n=s.target!==void 0?s.target:n:s={value:s},triggerEvent(n,i,s)}}else{let o=r.split(",");for(let i=0;i0;){let s=t[0];if(s==="]"){if(r--,r===0){i===null&&(o=o+"true"),t.shift(),o+=")})";try{let a=maybeEval(e,function(){return Function(o)()},function(){return!0});return a.source=o,a}catch(a){return triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:a,source:o}),null}}}else s==="["&&r++;isPossibleRelativeReference(s,i,n)?o+="(("+n+"."+s+") ? ("+n+"."+s+") : (window."+s+"))":o=o+s,i=t.shift()}}}function consumeUntil(e,t){let n="";for(;e.length>0&&!t.test(e[0]);)n+=e.shift();return n}function consumeCSSSelector(e){let t;return e.length>0&&COMBINED_SELECTOR_START.test(e[0])?(e.shift(),t=consumeUntil(e,COMBINED_SELECTOR_END).trim(),e.shift()):t=consumeUntil(e,WHITESPACE_OR_COMMA),t}let INPUT_SELECTOR="input, textarea, select";function parseAndCacheTrigger(e,t,n){let r=[],o=tokenizeString(t);do{consumeUntil(o,NOT_WHITESPACE);let a=o.length,l=consumeUntil(o,/[,\[\s]/);if(l!=="")if(l==="every"){let c={trigger:"every"};consumeUntil(o,NOT_WHITESPACE),c.pollInterval=parseInterval(consumeUntil(o,/[,\[\s]/)),consumeUntil(o,NOT_WHITESPACE);var i=maybeGenerateConditional(e,o,"event");i&&(c.eventFilter=i),r.push(c)}else{let c={trigger:l};var i=maybeGenerateConditional(e,o,"event");for(i&&(c.eventFilter=i),consumeUntil(o,NOT_WHITESPACE);o.length>0&&o[0]!==",";){let u=o.shift();if(u==="changed")c.changed=!0;else if(u==="once")c.once=!0;else if(u==="consume")c.consume=!0;else if(u==="delay"&&o[0]===":")o.shift(),c.delay=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA));else if(u==="from"&&o[0]===":"){if(o.shift(),COMBINED_SELECTOR_START.test(o[0]))var s=consumeCSSSelector(o);else{var s=consumeUntil(o,WHITESPACE_OR_COMMA);if(s==="closest"||s==="find"||s==="next"||s==="previous"){o.shift();let v=consumeCSSSelector(o);v.length>0&&(s+=" "+v)}}c.from=s}else u==="target"&&o[0]===":"?(o.shift(),c.target=consumeCSSSelector(o)):u==="throttle"&&o[0]===":"?(o.shift(),c.throttle=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA))):u==="queue"&&o[0]===":"?(o.shift(),c.queue=consumeUntil(o,WHITESPACE_OR_COMMA)):u==="root"&&o[0]===":"?(o.shift(),c[u]=consumeCSSSelector(o)):u==="threshold"&&o[0]===":"?(o.shift(),c[u]=consumeUntil(o,WHITESPACE_OR_COMMA)):triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()});consumeUntil(o,NOT_WHITESPACE)}r.push(c)}o.length===a&&triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()}),consumeUntil(o,NOT_WHITESPACE)}while(o[0]===","&&o.shift());return n&&(n[t]=r),r}function getTriggerSpecs(e){let t=getAttributeValue(e,"hx-trigger"),n=[];if(t){let r=htmx.config.triggerSpecsCache;n=r&&r[t]||parseAndCacheTrigger(e,t,r)}return n.length>0?n:matches(e,"form")?[{trigger:"submit"}]:matches(e,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(e,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}function cancelPolling(e){getInternalData(e).cancelled=!0}function processPolling(e,t,n){let r=getInternalData(e);r.timeout=getWindow().setTimeout(function(){bodyContains(e)&&r.cancelled!==!0&&(maybeFilterEvent(n,e,makeEvent("hx:poll:trigger",{triggerSpec:n,target:e}))||t(e),processPolling(e,t,n))},n.pollInterval)}function isLocalLink(e){return location.hostname===e.hostname&&getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")!==0}function eltIsDisabled(e){return closest(e,htmx.config.disableSelector)}function boostElement(e,t,n){if(e instanceof HTMLAnchorElement&&isLocalLink(e)&&(e.target===""||e.target==="_self")||e.tagName==="FORM"&&String(getRawAttribute(e,"method")).toLowerCase()!=="dialog"){t.boosted=!0;let r,o;if(e.tagName==="A")r="get",o=getRawAttribute(e,"href");else{let i=getRawAttribute(e,"method");r=i?i.toLowerCase():"get",o=getRawAttribute(e,"action"),(o==null||o==="")&&(o=location.href),r==="get"&&o.includes("?")&&(o=o.replace(/\?[^#]+/,""))}n.forEach(function(i){addEventListener(e,function(s,a){let l=asElement(s);if(eltIsDisabled(l)){cleanUpElement(l);return}issueAjaxRequest(r,o,l,a)},t,i,!0)})}}function shouldCancel(e,t){if(e.type==="submit"&&t.tagName==="FORM")return!0;if(e.type==="click"){let n=t.closest('input[type="submit"], button');if(n&&n.form&&n.type==="submit")return!0;let r=t.closest("a"),o=/^#.+/;if(r&&r.href&&!o.test(r.getAttribute("href")))return!0}return!1}function ignoreBoostedAnchorCtrlClick(e,t){return getInternalData(e).boosted&&e instanceof HTMLAnchorElement&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function maybeFilterEvent(e,t,n){let r=e.eventFilter;if(r)try{return r.call(t,n)!==!0}catch(o){let i=r.source;return triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:o,source:i}),!0}return!1}function addEventListener(e,t,n,r,o){let i=getInternalData(e),s;r.from?s=querySelectorAllExt(e,r.from):s=[e],r.changed&&("lastValue"in i||(i.lastValue=new WeakMap),s.forEach(function(a){i.lastValue.has(r)||i.lastValue.set(r,new WeakMap),i.lastValue.get(r).set(a,a.value)})),forEach(s,function(a){let l=function(c){if(!bodyContains(e)){a.removeEventListener(r.trigger,l);return}if(ignoreBoostedAnchorCtrlClick(e,c)||((o||shouldCancel(c,a))&&c.preventDefault(),maybeFilterEvent(r,e,c)))return;let h=getInternalData(c);if(h.triggerSpec=r,h.handledFor==null&&(h.handledFor=[]),h.handledFor.indexOf(e)<0){if(h.handledFor.push(e),r.consume&&c.stopPropagation(),r.target&&c.target&&!matches(asElement(c.target),r.target))return;if(r.once){if(i.triggeredOnce)return;i.triggeredOnce=!0}if(r.changed){let u=c.target,f=u.value,v=i.lastValue.get(r);if(v.has(u)&&v.get(u)===f)return;v.set(u,f)}if(i.delayed&&clearTimeout(i.delayed),i.throttle)return;r.throttle>0?i.throttle||(triggerEvent(e,"htmx:trigger"),t(e,c),i.throttle=getWindow().setTimeout(function(){i.throttle=null},r.throttle)):r.delay>0?i.delayed=getWindow().setTimeout(function(){triggerEvent(e,"htmx:trigger"),t(e,c)},r.delay):(triggerEvent(e,"htmx:trigger"),t(e,c))}};n.listenerInfos==null&&(n.listenerInfos=[]),n.listenerInfos.push({trigger:r.trigger,listener:l,on:a}),a.addEventListener(r.trigger,l)})}let windowIsScrolling=!1,scrollHandler=null;function initScrollHandler(){scrollHandler||(scrollHandler=function(){windowIsScrolling=!0},window.addEventListener("scroll",scrollHandler),window.addEventListener("resize",scrollHandler),setInterval(function(){windowIsScrolling&&(windowIsScrolling=!1,forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(e){maybeReveal(e)}))},200))}function maybeReveal(e){!hasAttribute(e,"data-hx-revealed")&&isScrolledIntoView(e)&&(e.setAttribute("data-hx-revealed","true"),getInternalData(e).initHash?triggerEvent(e,"revealed"):e.addEventListener("htmx:afterProcessNode",function(){triggerEvent(e,"revealed")},{once:!0}))}function loadImmediately(e,t,n,r){let o=function(){n.loaded||(n.loaded=!0,triggerEvent(e,"htmx:trigger"),t(e))};r>0?getWindow().setTimeout(o,r):o()}function processVerbs(e,t,n){let r=!1;return forEach(VERBS,function(o){if(hasAttribute(e,"hx-"+o)){let i=getAttributeValue(e,"hx-"+o);r=!0,t.path=i,t.verb=o,n.forEach(function(s){addTriggerHandler(e,s,t,function(a,l){let c=asElement(a);if(eltIsDisabled(c)){cleanUpElement(c);return}issueAjaxRequest(o,i,c,l)})})}}),r}function addTriggerHandler(e,t,n,r){if(t.trigger==="revealed")initScrollHandler(),addEventListener(e,r,n,t),maybeReveal(asElement(e));else if(t.trigger==="intersect"){let o={};t.root&&(o.root=querySelectorExt(e,t.root)),t.threshold&&(o.threshold=parseFloat(t.threshold)),new IntersectionObserver(function(s){for(let a=0;a0?(n.polling=!0,processPolling(asElement(e),r,t)):addEventListener(e,r,n,t)}function shouldProcessHxOn(e){let t=asElement(e);if(!t)return!1;let n=t.attributes;for(let r=0;r", "+i).join(""))}else return[]}function maybeSetLastButtonClicked(e){let t=getTargetButton(e.target),n=getRelatedFormData(e);n&&(n.lastButtonClicked=t)}function maybeUnsetLastButtonClicked(e){let t=getRelatedFormData(e);t&&(t.lastButtonClicked=null)}function getTargetButton(e){return closest(asElement(e),"button, input[type='submit']")}function getRelatedForm(e){return e.form||closest(e,"form")}function getRelatedFormData(e){let t=getTargetButton(e.target);if(!t)return;let n=getRelatedForm(t);if(n)return getInternalData(n)}function initButtonTracking(e){e.addEventListener("click",maybeSetLastButtonClicked),e.addEventListener("focusin",maybeSetLastButtonClicked),e.addEventListener("focusout",maybeUnsetLastButtonClicked)}function addHxOnEventHandler(e,t,n){let r=getInternalData(e);Array.isArray(r.onHandlers)||(r.onHandlers=[]);let o,i=function(s){maybeEval(e,function(){eltIsDisabled(e)||(o||(o=new Function("event",n)),o.call(e,s))})};e.addEventListener(t,i),r.onHandlers.push({event:t,listener:i})}function processHxOnWildcard(e){deInitOnHandlers(e);for(let t=0;thtmx.config.historyCacheSize;)i.shift();for(;i.length>0;)try{sessionStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(a){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:a,cache:i}),i.shift()}}function getCachedHistory(e){if(!canAccessLocalStorage())return null;e=normalizePath(e);let t=parseJSON(sessionStorage.getItem("htmx-history-cache"))||[];for(let n=0;n=200&&this.status<400?(r.response=this.response,triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",r),swap(r.historyElt,r.response,n,{contextElement:r.historyElt,historyRequest:!0}),setCurrentPathForHistory(r.path),triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,cacheMiss:!0,serverResponse:r.response})):triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",r)},triggerEvent(getDocument().body,"htmx:historyCacheMiss",r)&&t.send()}function restoreHistory(e){saveCurrentPageToHistory(),e=e||location.pathname+location.search;let t=getCachedHistory(e);if(t){let n={swapStyle:"innerHTML",swapDelay:0,settleDelay:0,scroll:t.scroll},r={path:e,item:t,historyElt:getHistoryElement(),swapSpec:n};triggerEvent(getDocument().body,"htmx:historyCacheHit",r)&&(swap(r.historyElt,t.content,n,{contextElement:r.historyElt,title:t.title}),setCurrentPathForHistory(r.path),triggerEvent(getDocument().body,"htmx:historyRestore",r))}else htmx.config.refreshOnHistoryMiss?htmx.location.reload(!0):loadHistoryFromServer(e)}function addRequestIndicatorClasses(e){let t=findAttributeTargets(e,"hx-indicator");return t==null&&(t=[e]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.classList.add.call(n.classList,htmx.config.requestClass)}),t}function disableElements(e){let t=findAttributeTargets(e,"hx-disabled-elt");return t==null&&(t=[]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.setAttribute("disabled",""),n.setAttribute("data-disabled-by-htmx","")}),t}function removeRequestIndicators(e,t){forEach(e.concat(t),function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||1)-1}),forEach(e,function(n){getInternalData(n).requestCount===0&&n.classList.remove.call(n.classList,htmx.config.requestClass)}),forEach(t,function(n){getInternalData(n).requestCount===0&&(n.removeAttribute("disabled"),n.removeAttribute("data-disabled-by-htmx"))})}function haveSeenNode(e,t){for(let n=0;nt.indexOf(o)<0):r=r.filter(o=>o!==t),n.delete(e),forEach(r,o=>n.append(e,o))}}function getValueFromInput(e){return e instanceof HTMLSelectElement&&e.multiple?toArray(e.querySelectorAll("option:checked")).map(function(t){return t.value}):e instanceof HTMLInputElement&&e.files?toArray(e.files):e.value}function processInputValue(e,t,n,r,o){if(!(r==null||haveSeenNode(e,r))){if(e.push(r),shouldInclude(r)){let i=getRawAttribute(r,"name");addValueToFormData(i,getValueFromInput(r),t),o&&validateElement(r,n)}r instanceof HTMLFormElement&&(forEach(r.elements,function(i){e.indexOf(i)>=0?removeValueFromFormData(i.name,getValueFromInput(i),t):e.push(i),o&&validateElement(i,n)}),new FormData(r).forEach(function(i,s){i instanceof File&&i.name===""||addValueToFormData(s,i,t)}))}}function validateElement(e,t){let n=e;n.willValidate&&(triggerEvent(n,"htmx:validation:validate"),n.checkValidity()||(triggerEvent(n,"htmx:validation:failed",{message:n.validationMessage,validity:n.validity})&&!t.length&&htmx.config.reportValidityOfForms&&n.reportValidity(),t.push({elt:n,message:n.validationMessage,validity:n.validity})))}function overrideFormData(e,t){for(let n of t.keys())e.delete(n);return t.forEach(function(n,r){e.append(r,n)}),e}function getInputValues(e,t){let n=[],r=new FormData,o=new FormData,i=[],s=getInternalData(e);s.lastButtonClicked&&!bodyContains(s.lastButtonClicked)&&(s.lastButtonClicked=null);let a=e instanceof HTMLFormElement&&e.noValidate!==!0||getAttributeValue(e,"hx-validate")==="true";if(s.lastButtonClicked&&(a=a&&s.lastButtonClicked.formNoValidate!==!0),t!=="get"&&processInputValue(n,o,i,getRelatedForm(e),a),processInputValue(n,r,i,e,a),s.lastButtonClicked||e.tagName==="BUTTON"||e.tagName==="INPUT"&&getRawAttribute(e,"type")==="submit"){let c=s.lastButtonClicked||e,h=getRawAttribute(c,"name");addValueToFormData(h,c.value,o)}let l=findAttributeTargets(e,"hx-include");return forEach(l,function(c){processInputValue(n,r,i,asElement(c),a),matches(c,"form")||forEach(asParentNode(c).querySelectorAll(INPUT_SELECTOR),function(h){processInputValue(n,r,i,h,a)})}),overrideFormData(r,o),{errors:i,formData:r,values:formDataProxy(r)}}function appendParam(e,t,n){e!==""&&(e+="&"),String(n)==="[object Object]"&&(n=JSON.stringify(n));let r=encodeURIComponent(n);return e+=encodeURIComponent(t)+"="+r,e}function urlEncode(e){e=formDataFromObject(e);let t="";return e.forEach(function(n,r){t=appendParam(t,r,n)}),t}function getHeaders(e,t,n){let r={"HX-Request":"true","HX-Trigger":getRawAttribute(e,"id"),"HX-Trigger-Name":getRawAttribute(e,"name"),"HX-Target":getAttributeValue(t,"id"),"HX-Current-URL":location.href};return getValuesForElement(e,"hx-headers",!1,r),n!==void 0&&(r["HX-Prompt"]=n),getInternalData(e).boosted&&(r["HX-Boosted"]="true"),r}function filterValues(e,t){let n=getClosestAttributeValue(t,"hx-params");if(n){if(n==="none")return new FormData;if(n==="*")return e;if(n.indexOf("not ")===0)return forEach(n.slice(4).split(","),function(r){r=r.trim(),e.delete(r)}),e;{let r=new FormData;return forEach(n.split(","),function(o){o=o.trim(),e.has(o)&&e.getAll(o).forEach(function(i){r.append(o,i)})}),r}}else return e}function isAnchorLink(e){return!!getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")>=0}function getSwapSpecification(e,t){let n=t||getClosestAttributeValue(e,"hx-swap"),r={swapStyle:getInternalData(e).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};if(htmx.config.scrollIntoViewOnBoost&&getInternalData(e).boosted&&!isAnchorLink(e)&&(r.show="top"),n){let s=splitOnWhitespace(n);if(s.length>0)for(let a=0;a0?o.join(":"):null;r.scroll=h,r.scrollTarget=i}else if(l.indexOf("show:")===0){var o=l.slice(5).split(":");let u=o.pop();var i=o.length>0?o.join(":"):null;r.show=u,r.showTarget=i}else if(l.indexOf("focus-scroll:")===0){let c=l.slice(13);r.focusScroll=c=="true"}else a==0?r.swapStyle=l:logError("Unknown modifier in hx-swap: "+l)}}return r}function usesFormData(e){return getClosestAttributeValue(e,"hx-encoding")==="multipart/form-data"||matches(e,"form")&&getRawAttribute(e,"enctype")==="multipart/form-data"}function encodeParamsForBody(e,t,n){let r=null;return withExtensions(t,function(o){r==null&&(r=o.encodeParameters(e,n,t))}),r??(usesFormData(t)?overrideFormData(new FormData,formDataFromObject(n)):urlEncode(n))}function makeSettleInfo(e){return{tasks:[],elts:[e]}}function updateScrollState(e,t){let n=e[0],r=e[e.length-1];if(t.scroll){var o=null;t.scrollTarget&&(o=asElement(querySelectorExt(n,t.scrollTarget))),t.scroll==="top"&&(n||o)&&(o=o||n,o.scrollTop=0),t.scroll==="bottom"&&(r||o)&&(o=o||r,o.scrollTop=o.scrollHeight),typeof t.scroll=="number"&&getWindow().setTimeout(function(){window.scrollTo(0,t.scroll)},0)}if(t.show){var o=null;if(t.showTarget){let s=t.showTarget;t.showTarget==="window"&&(s="body"),o=asElement(querySelectorExt(n,s))}t.show==="top"&&(n||o)&&(o=o||n,o.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})),t.show==="bottom"&&(r||o)&&(o=o||r,o.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior}))}}function getValuesForElement(e,t,n,r,o){if(r==null&&(r={}),e==null)return r;let i=getAttributeValue(e,t);if(i){let s=i.trim(),a=n;if(s==="unset")return null;s.indexOf("javascript:")===0?(s=s.slice(11),a=!0):s.indexOf("js:")===0&&(s=s.slice(3),a=!0),s.indexOf("{")!==0&&(s="{"+s+"}");let l;a?l=maybeEval(e,function(){return o?Function("event","return ("+s+")").call(e,o):Function("return ("+s+")").call(e)},{}):l=parseJSON(s);for(let c in l)l.hasOwnProperty(c)&&r[c]==null&&(r[c]=l[c])}return getValuesForElement(asElement(parentElt(e)),t,n,r,o)}function maybeEval(e,t,n){return htmx.config.allowEval?t():(triggerErrorEvent(e,"htmx:evalDisallowedError"),n)}function getHXVarsForElement(e,t,n){return getValuesForElement(e,"hx-vars",!0,n,t)}function getHXValsForElement(e,t,n){return getValuesForElement(e,"hx-vals",!1,n,t)}function getExpressionVars(e,t){return mergeObjects(getHXVarsForElement(e,t),getHXValsForElement(e,t))}function safelySetHeaderValue(e,t,n){if(n!==null)try{e.setRequestHeader(t,n)}catch{e.setRequestHeader(t,encodeURIComponent(n)),e.setRequestHeader(t+"-URI-AutoEncoded","true")}}function getPathFromResponse(e){if(e.responseURL)try{let t=new URL(e.responseURL);return t.pathname+t.search}catch{triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:e.responseURL})}}function hasHeader(e,t){return t.test(e.getAllResponseHeaders())}function ajaxHelper(e,t,n){if(e=e.toLowerCase(),n){if(n instanceof Element||typeof n=="string")return issueAjaxRequest(e,t,null,null,{targetOverride:resolveTarget(n)||DUMMY_ELT,returnPromise:!0});{let r=resolveTarget(n.target);return(n.target&&!r||n.source&&!r&&!resolveTarget(n.source))&&(r=DUMMY_ELT),issueAjaxRequest(e,t,resolveTarget(n.source),n.event,{handler:n.handler,headers:n.headers,values:n.values,targetOverride:r,swapOverride:n.swap,select:n.select,returnPromise:!0,push:n.push,replace:n.replace,selectOOB:n.selectOOB})}}else return issueAjaxRequest(e,t,null,null,{returnPromise:!0})}function hierarchyForElt(e){let t=[];for(;e;)t.push(e),e=e.parentElement;return t}function verifyPath(e,t,n){let r=new URL(t,location.protocol!=="about:"?location.href:window.origin),i=(location.protocol!=="about:"?location.origin:window.origin)===r.origin;return htmx.config.selfRequestsOnly&&!i?!1:triggerEvent(e,"htmx:validateUrl",mergeObjects({url:r,sameHost:i},n))}function formDataFromObject(e){if(e instanceof FormData)return e;let t=new FormData;for(let n in e)e.hasOwnProperty(n)&&(e[n]&&typeof e[n].forEach=="function"?e[n].forEach(function(r){t.append(n,r)}):typeof e[n]=="object"&&!(e[n]instanceof Blob)?t.append(n,JSON.stringify(e[n])):t.append(n,e[n]));return t}function formDataArrayProxy(e,t,n){return new Proxy(n,{get:function(r,o){return typeof o=="number"?r[o]:o==="length"?r.length:o==="push"?function(i){r.push(i),e.append(t,i)}:typeof r[o]=="function"?function(){r[o].apply(r,arguments),e.delete(t),r.forEach(function(i){e.append(t,i)})}:r[o]&&r[o].length===1?r[o][0]:r[o]},set:function(r,o,i){return r[o]=i,e.delete(t),r.forEach(function(s){e.append(t,s)}),!0}})}function formDataProxy(e){return new Proxy(e,{get:function(t,n){if(typeof n=="symbol"){let o=Reflect.get(t,n);return typeof o=="function"?function(){return o.apply(e,arguments)}:o}if(n==="toJSON")return()=>Object.fromEntries(e);if(n in t&&typeof t[n]=="function")return function(){return e[n].apply(e,arguments)};let r=e.getAll(n);if(r.length!==0)return r.length===1?r[0]:formDataArrayProxy(t,n,r)},set:function(t,n,r){return typeof n!="string"?!1:(t.delete(n),r&&typeof r.forEach=="function"?r.forEach(function(o){t.append(n,o)}):typeof r=="object"&&!(r instanceof Blob)?t.append(n,JSON.stringify(r)):t.append(n,r),!0)},deleteProperty:function(t,n){return typeof n=="string"&&t.delete(n),!0},ownKeys:function(t){return Reflect.ownKeys(Object.fromEntries(t))},getOwnPropertyDescriptor:function(t,n){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(t),n)}})}function issueAjaxRequest(e,t,n,r,o,i){let s=null,a=null;if(o=o??{},o.returnPromise&&typeof Promise<"u")var l=new Promise(function(p,w){s=p,a=w});n==null&&(n=getDocument().body);let c=o.handler||handleAjaxResponse,h=o.select||null;if(!bodyContains(n))return maybeCall(s),l;let u=o.targetOverride||asElement(getTarget(n));if(u==null||u==DUMMY_ELT)return triggerErrorEvent(n,"htmx:targetError",{target:getClosestAttributeValue(n,"hx-target")}),maybeCall(a),l;let f=getInternalData(n),v=f.lastButtonClicked;if(v){let p=getRawAttribute(v,"formaction");p!=null&&(t=p);let w=getRawAttribute(v,"formmethod");if(w!=null)if(VERBS.includes(w.toLowerCase()))e=w;else return maybeCall(s),l}let d=getClosestAttributeValue(n,"hx-confirm");if(i===void 0&&triggerEvent(n,"htmx:confirm",{target:u,elt:n,path:t,verb:e,triggeringEvent:r,etc:o,issueRequest:function(T){return issueAjaxRequest(e,t,n,r,o,!!T)},question:d})===!1)return maybeCall(s),l;let y=n,m=getClosestAttributeValue(n,"hx-sync"),x=null,b=!1;if(m){let p=m.split(":"),w=p[0].trim();if(w==="this"?y=findThisElement(n,"hx-sync"):y=asElement(querySelectorExt(n,w)),m=(p[1]||"drop").trim(),f=getInternalData(y),m==="drop"&&f.xhr&&f.abortable!==!0)return maybeCall(s),l;if(m==="abort"){if(f.xhr)return maybeCall(s),l;b=!0}else m==="replace"?triggerEvent(y,"htmx:abort"):m.indexOf("queue")===0&&(x=(m.split(" ")[1]||"last").trim())}if(f.xhr)if(f.abortable)triggerEvent(y,"htmx:abort");else{if(x==null){if(r){let p=getInternalData(r);p&&p.triggerSpec&&p.triggerSpec.queue&&(x=p.triggerSpec.queue)}x==null&&(x="last")}return f.queuedRequests==null&&(f.queuedRequests=[]),x==="first"&&f.queuedRequests.length===0?f.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o)}):x==="all"?f.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o)}):x==="last"&&(f.queuedRequests=[],f.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o)})),maybeCall(s),l}let E=new XMLHttpRequest;f.xhr=E,f.abortable=b;let g=function(){f.xhr=null,f.abortable=!1,f.queuedRequests!=null&&f.queuedRequests.length>0&&f.queuedRequests.shift()()},X=getClosestAttributeValue(n,"hx-prompt");if(X){var k=prompt(X);if(k===null||!triggerEvent(n,"htmx:prompt",{prompt:k,target:u}))return maybeCall(s),g(),l}if(d&&!i&&!confirm(d))return maybeCall(s),g(),l;let H=getHeaders(n,u,k);e!=="get"&&!usesFormData(n)&&(H["Content-Type"]="application/x-www-form-urlencoded"),o.headers&&(H=mergeObjects(H,o.headers));let $=getInputValues(n,e),R=$.errors,z=$.formData;o.values&&overrideFormData(z,formDataFromObject(o.values));let oe=formDataFromObject(getExpressionVars(n,r)),P=overrideFormData(z,oe),D=filterValues(P,n);htmx.config.getCacheBusterParam&&e==="get"&&D.set("org.htmx.cache-buster",getRawAttribute(u,"id")||"true"),(t==null||t==="")&&(t=location.href);let N=getValuesForElement(n,"hx-request"),J=getInternalData(n).boosted,I=htmx.config.methodsThatUseUrlParams.indexOf(e)>=0,S={boosted:J,useUrlParams:I,formData:D,parameters:formDataProxy(D),unfilteredFormData:P,unfilteredParameters:formDataProxy(P),headers:H,elt:n,target:u,verb:e,errors:R,withCredentials:o.credentials||N.credentials||htmx.config.withCredentials,timeout:o.timeout||N.timeout||htmx.config.timeout,path:t,triggeringEvent:r};if(!triggerEvent(n,"htmx:configRequest",S))return maybeCall(s),g(),l;if(t=S.path,e=S.verb,H=S.headers,D=formDataFromObject(S.parameters),R=S.errors,I=S.useUrlParams,R&&R.length>0)return triggerEvent(n,"htmx:validation:halted",S),maybeCall(s),g(),l;let Y=t.split("#"),ie=Y[0],M=Y[1],A=t;if(I&&(A=ie,!D.keys().next().done&&(A.indexOf("?")<0?A+="?":A+="&",A+=urlEncode(D),M&&(A+="#"+M))),!verifyPath(n,A,S))return triggerErrorEvent(n,"htmx:invalidPath",S),maybeCall(a),g(),l;if(E.open(e.toUpperCase(),A,!0),E.overrideMimeType("text/html"),E.withCredentials=S.withCredentials,E.timeout=S.timeout,!N.noHeaders){for(let p in H)if(H.hasOwnProperty(p)){let w=H[p];safelySetHeaderValue(E,p,w)}}let C={xhr:E,target:u,requestConfig:S,etc:o,boosted:J,select:h,pathInfo:{requestPath:t,finalRequestPath:A,responsePath:null,anchor:M}};if(E.onload=function(){try{let p=hierarchyForElt(n);if(C.pathInfo.responsePath=getPathFromResponse(E),c(n,C),C.keepIndicators!==!0&&removeRequestIndicators(L,O),triggerEvent(n,"htmx:afterRequest",C),triggerEvent(n,"htmx:afterOnLoad",C),!bodyContains(n)){let w=null;for(;p.length>0&&w==null;){let T=p.shift();bodyContains(T)&&(w=T)}w&&(triggerEvent(w,"htmx:afterRequest",C),triggerEvent(w,"htmx:afterOnLoad",C))}maybeCall(s)}catch(p){throw triggerErrorEvent(n,"htmx:onLoadError",mergeObjects({error:p},C)),p}finally{g()}},E.onerror=function(){removeRequestIndicators(L,O),triggerErrorEvent(n,"htmx:afterRequest",C),triggerErrorEvent(n,"htmx:sendError",C),maybeCall(a),g()},E.onabort=function(){removeRequestIndicators(L,O),triggerErrorEvent(n,"htmx:afterRequest",C),triggerErrorEvent(n,"htmx:sendAbort",C),maybeCall(a),g()},E.ontimeout=function(){removeRequestIndicators(L,O),triggerErrorEvent(n,"htmx:afterRequest",C),triggerErrorEvent(n,"htmx:timeout",C),maybeCall(a),g()},!triggerEvent(n,"htmx:beforeRequest",C))return maybeCall(s),g(),l;var L=addRequestIndicatorClasses(n),O=disableElements(n);forEach(["loadstart","loadend","progress","abort"],function(p){forEach([E,E.upload],function(w){w.addEventListener(p,function(T){triggerEvent(n,"htmx:xhr:"+p,{lengthComputable:T.lengthComputable,loaded:T.loaded,total:T.total})})})}),triggerEvent(n,"htmx:beforeSend",C);let se=I?null:encodeParamsForBody(E,n,D);return E.send(se),l}function determineHistoryUpdates(e,t){let n=t.xhr,r=null,o=null;if(hasHeader(n,/HX-Push:/i)?(r=n.getResponseHeader("HX-Push"),o="push"):hasHeader(n,/HX-Push-Url:/i)?(r=n.getResponseHeader("HX-Push-Url"),o="push"):hasHeader(n,/HX-Replace-Url:/i)&&(r=n.getResponseHeader("HX-Replace-Url"),o="replace"),r)return r==="false"?{}:{type:o,path:r};let i=t.pathInfo.finalRequestPath,s=t.pathInfo.responsePath,a=t.etc.push||getClosestAttributeValue(e,"hx-push-url"),l=t.etc.replace||getClosestAttributeValue(e,"hx-replace-url"),c=getInternalData(e).boosted,h=null,u=null;return a?(h="push",u=a):l?(h="replace",u=l):c&&(h="push",u=s||i),u?u==="false"?{}:(u==="true"&&(u=s||i),t.pathInfo.anchor&&u.indexOf("#")===-1&&(u=u+"#"+t.pathInfo.anchor),{type:h,path:u}):{}}function codeMatches(e,t){var n=new RegExp(e.code);return n.test(t.toString(10))}function resolveResponseHandling(e){for(var t=0;t.${t}{opacity:0;visibility: hidden} .${n} .${t}, .${n}.${t}{opacity:1;visibility: visible;transition: opacity 200ms ease-in}`)}}function getMetaConfig(){let e=getDocument().querySelector('meta[name="htmx-config"]');return e?parseJSON(e.content):null}function mergeMetaConfig(){let e=getMetaConfig();e&&(htmx.config=mergeObjects(htmx.config,e))}return ready(function(){mergeMetaConfig(),insertIndicatorStyles();let e=getDocument().body;processNode(e);let t=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(r){let o=r.detail.elt||r.target,i=getInternalData(o);i&&i.xhr&&i.xhr.abort()});let n=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(r){r.state&&r.state.htmx?(restoreHistory(),forEach(t,function(o){triggerEvent(o,"htmx:restored",{document:getDocument(),triggerEvent})})):n&&n(r)},getWindow().setTimeout(function(){triggerEvent(e,"htmx:load",{}),e=null},0)}),htmx})(),F=ae;(function(){let e;F.defineExtension("json-enc",{init:function(t){e=t},onEvent:function(t,n){t==="htmx:configRequest"&&(n.detail.headers["Content-Type"]="application/json")},encodeParameters:function(t,n,r){t.overrideMimeType("text/json");let o={};n.forEach(function(s,a){Object.hasOwn(o,a)?(Array.isArray(o[a])||(o[a]=[o[a]]),o[a].push(s)):o[a]=s});let i=e.getExpressionVars(r);return Object.keys(o).forEach(function(s){o[s]=Object.hasOwn(i,s)?i[s]:o[s]}),JSON.stringify(o)}})})();var G=document.createElement("template");G.innerHTML=` +var ae=(function(){"use strict";let htmx={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(e,t){return getInputValues(e,t||"post").values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:!0,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null,disableInheritance:!1,responseHandling:[{code:"204",swap:!1},{code:"[23]..",swap:!0},{code:"[45]..",swap:!1,error:!0}],allowNestedOobSwaps:!0,historyRestoreAsHxRequest:!0,reportValidityOfForms:!1},parseInterval:null,location,_:null,version:"2.0.8"};htmx.onLoad=onLoadHelper,htmx.process=processNode,htmx.on=addEventListenerImpl,htmx.off=removeEventListenerImpl,htmx.trigger=triggerEvent,htmx.ajax=ajaxHelper,htmx.find=find,htmx.findAll=findAll,htmx.closest=closest,htmx.remove=removeElement,htmx.addClass=addClassToElement,htmx.removeClass=removeClassFromElement,htmx.toggleClass=toggleClassOnElement,htmx.takeClass=takeClassForElement,htmx.swap=swap,htmx.defineExtension=defineExtension,htmx.removeExtension=removeExtension,htmx.logAll=logAll,htmx.logNone=logNone,htmx.parseInterval=parseInterval,htmx._=internalEval;let internalAPI={addTriggerHandler,bodyContains,canAccessLocalStorage,findThisElement,filterValues,swap,hasAttribute,getAttributeValue,getClosestAttributeValue,getClosestMatch,getExpressionVars,getHeaders,getInputValues,getInternalData,getSwapSpecification,getTriggerSpecs,getTarget,makeFragment,mergeObjects,makeSettleInfo,oobSwap,querySelectorExt,settleImmediately,shouldCancel,triggerEvent,triggerErrorEvent,withExtensions},VERBS=["get","post","put","delete","patch"],VERB_SELECTOR=VERBS.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function parseInterval(e){if(e==null)return;let t=NaN;return e.slice(-2)=="ms"?t=parseFloat(e.slice(0,-2)):e.slice(-1)=="s"?t=parseFloat(e.slice(0,-1))*1e3:e.slice(-1)=="m"?t=parseFloat(e.slice(0,-1))*1e3*60:t=parseFloat(e),isNaN(t)?void 0:t}function getRawAttribute(e,t){return e instanceof Element&&e.getAttribute(t)}function hasAttribute(e,t){return!!e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function getAttributeValue(e,t){return getRawAttribute(e,t)||getRawAttribute(e,"data-"+t)}function parentElt(e){let t=e.parentElement;return!t&&e.parentNode instanceof ShadowRoot?e.parentNode:t}function getDocument(){return document}function getRootNode(e,t){return e.getRootNode?e.getRootNode({composed:t}):getDocument()}function getClosestMatch(e,t){for(;e&&!t(e);)e=parentElt(e);return e||null}function getAttributeValueWithDisinheritance(e,t,n){let r=getAttributeValue(t,n),o=getAttributeValue(t,"hx-disinherit");var i=getAttributeValue(t,"hx-inherit");if(e!==t){if(htmx.config.disableInheritance)return i&&(i==="*"||i.split(" ").indexOf(n)>=0)?r:null;if(o&&(o==="*"||o.split(" ").indexOf(n)>=0))return"unset"}return r}function getClosestAttributeValue(e,t){let n=null;if(getClosestMatch(e,function(r){return!!(n=getAttributeValueWithDisinheritance(e,asElement(r),t))}),n!=="unset")return n}function matches(e,t){return e instanceof Element&&e.matches(t)}function getStartTag(e){let n=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i.exec(e);return n?n[1].toLowerCase():""}function parseHTML(e){return"parseHTMLUnsafe"in Document?Document.parseHTMLUnsafe(e):new DOMParser().parseFromString(e,"text/html")}function takeChildrenFor(e,t){for(;t.childNodes.length>0;)e.append(t.childNodes[0])}function duplicateScript(e){let t=getDocument().createElement("script");return forEach(e.attributes,function(n){t.setAttribute(n.name,n.value)}),t.textContent=e.textContent,t.async=!1,htmx.config.inlineScriptNonce&&(t.nonce=htmx.config.inlineScriptNonce),t}function isJavaScriptScriptNode(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}function normalizeScriptTags(e){Array.from(e.querySelectorAll("script")).forEach(t=>{if(isJavaScriptScriptNode(t)){let n=duplicateScript(t),r=t.parentNode;try{r.insertBefore(n,t)}catch(o){logError(o)}finally{t.remove()}}})}function makeFragment(e){let t=e.replace(/]*)?>[\s\S]*?<\/head>/i,""),n=getStartTag(t),r;if(n==="html"){r=new DocumentFragment;let i=parseHTML(e);takeChildrenFor(r,i.body),r.title=i.title}else if(n==="body"){r=new DocumentFragment;let i=parseHTML(t);takeChildrenFor(r,i.body),r.title=i.title}else{let i=parseHTML('");r=i.querySelector("template").content,r.title=i.title;var o=r.querySelector("title");o&&o.parentNode===r&&(o.remove(),r.title=o.innerText)}return r&&(htmx.config.allowScriptTags?normalizeScriptTags(r):r.querySelectorAll("script").forEach(i=>i.remove())),r}function maybeCall(e){e&&e()}function isType(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function isFunction(e){return typeof e=="function"}function isRawObject(e){return isType(e,"Object")}function getInternalData(e){let t="htmx-internal-data",n=e[t];return n||(n=e[t]={}),n}function toArray(e){let t=[];if(e)for(let n=0;n=0}function bodyContains(e){return e.getRootNode({composed:!0})===document}function splitOnWhitespace(e){return e.trim().split(/\s+/)}function mergeObjects(e,t){for(let n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function parseJSON(e){try{return JSON.parse(e)}catch(t){return logError(t),null}}function canAccessLocalStorage(){let e="htmx:sessionStorageTest";try{return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch{return!1}}function normalizePath(e){let t=new URL(e,"http://x");return t&&(e=t.pathname+t.search),e!="/"&&(e=e.replace(/\/+$/,"")),e}function internalEval(str){return maybeEval(getDocument().body,function(){return eval(str)})}function onLoadHelper(e){return htmx.on("htmx:load",function(n){e(n.detail.elt)})}function logAll(){htmx.logger=function(e,t,n){console&&console.log(t,e,n)}}function logNone(){htmx.logger=null}function find(e,t){return typeof e!="string"?e.querySelector(t):find(getDocument(),e)}function findAll(e,t){return typeof e!="string"?e.querySelectorAll(t):findAll(getDocument(),e)}function getWindow(){return window}function removeElement(e,t){e=resolveTarget(e),t?getWindow().setTimeout(function(){removeElement(e),e=null},t):parentElt(e).removeChild(e)}function asElement(e){return e instanceof Element?e:null}function asHtmlElement(e){return e instanceof HTMLElement?e:null}function asString(e){return typeof e=="string"?e:null}function asParentNode(e){return e instanceof Element||e instanceof Document||e instanceof DocumentFragment?e:null}function addClassToElement(e,t,n){e=asElement(resolveTarget(e)),e&&(n?getWindow().setTimeout(function(){addClassToElement(e,t),e=null},n):e.classList&&e.classList.add(t))}function removeClassFromElement(e,t,n){let r=asElement(resolveTarget(e));r&&(n?getWindow().setTimeout(function(){removeClassFromElement(r,t),r=null},n):r.classList&&(r.classList.remove(t),r.classList.length===0&&r.removeAttribute("class")))}function toggleClassOnElement(e,t){e=resolveTarget(e),e.classList.toggle(t)}function takeClassForElement(e,t){e=resolveTarget(e),forEach(e.parentElement.children,function(n){removeClassFromElement(n,t)}),addClassToElement(asElement(e),t)}function closest(e,t){return e=asElement(resolveTarget(e)),e?e.closest(t):null}function startsWith(e,t){return e.substring(0,t.length)===t}function endsWith(e,t){return e.substring(e.length-t.length)===t}function normalizeSelector(e){let t=e.trim();return startsWith(t,"<")&&endsWith(t,"/>")?t.substring(1,t.length-2):t}function querySelectorAllExt(e,t,n){if(t.indexOf("global ")===0)return querySelectorAllExt(e,t.slice(7),!0);e=resolveTarget(e);let r=[];{let s=0,a=0;for(let l=0;l"&&s--}a0;){let s=normalizeSelector(r.shift()),a;s.indexOf("closest ")===0?a=closest(asElement(e),normalizeSelector(s.slice(8))):s.indexOf("find ")===0?a=find(asParentNode(e),normalizeSelector(s.slice(5))):s==="next"||s==="nextElementSibling"?a=asElement(e).nextElementSibling:s.indexOf("next ")===0?a=scanForwardQuery(e,normalizeSelector(s.slice(5)),!!n):s==="previous"||s==="previousElementSibling"?a=asElement(e).previousElementSibling:s.indexOf("previous ")===0?a=scanBackwardsQuery(e,normalizeSelector(s.slice(9)),!!n):s==="document"?a=document:s==="window"?a=window:s==="body"?a=document.body:s==="root"?a=getRootNode(e,!!n):s==="host"?a=e.getRootNode().host:i.push(s),a&&o.push(a)}if(i.length>0){let s=i.join(","),a=asParentNode(getRootNode(e,!!n));o.push(...toArray(a.querySelectorAll(s)))}return o}var scanForwardQuery=function(e,t,n){let r=asParentNode(getRootNode(e,n)).querySelectorAll(t);for(let o=0;o=0;o--){let i=r[o];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING)return i}};function querySelectorExt(e,t){return typeof e!="string"?querySelectorAllExt(e,t)[0]:querySelectorAllExt(getDocument().body,e)[0]}function resolveTarget(e,t){return typeof e=="string"?find(asParentNode(t)||document,e):e}function processEventArgs(e,t,n,r){return isFunction(t)?{target:getDocument().body,event:asString(e),listener:t,options:n}:{target:resolveTarget(e),event:asString(t),listener:n,options:r}}function addEventListenerImpl(e,t,n,r){return ready(function(){let i=processEventArgs(e,t,n,r);i.target.addEventListener(i.event,i.listener,i.options)}),isFunction(t)?t:n}function removeEventListenerImpl(e,t,n){return ready(function(){let r=processEventArgs(e,t,n);r.target.removeEventListener(r.event,r.listener)}),isFunction(t)?t:n}let DUMMY_ELT=getDocument().createElement("output");function findAttributeTargets(e,t){let n=getClosestAttributeValue(e,t);if(n){if(n==="this")return[findThisElement(e,t)];{let r=querySelectorAllExt(e,n);if(/(^|,)(\s*)inherit(\s*)($|,)/.test(n)){let i=asElement(getClosestMatch(e,function(s){return s!==e&&hasAttribute(asElement(s),t)}));i&&r.push(...findAttributeTargets(i,t))}return r.length===0?(logError('The selector "'+n+'" on '+t+" returned no matches!"),[DUMMY_ELT]):r}}}function findThisElement(e,t){return asElement(getClosestMatch(e,function(n){return getAttributeValue(asElement(n),t)!=null}))}function getTarget(e){let t=getClosestAttributeValue(e,"hx-target");return t?t==="this"?findThisElement(e,"hx-target"):querySelectorExt(e,t):getInternalData(e).boosted?getDocument().body:e}function shouldSettleAttribute(e){return htmx.config.attributesToSettle.includes(e)}function cloneAttributes(e,t){forEach(Array.from(e.attributes),function(n){!t.hasAttribute(n.name)&&shouldSettleAttribute(n.name)&&e.removeAttribute(n.name)}),forEach(t.attributes,function(n){shouldSettleAttribute(n.name)&&e.setAttribute(n.name,n.value)})}function isInlineSwap(e,t){let n=getExtensions(t);for(let r=0;r0?(i=e.substring(0,e.indexOf(":")),o=e.substring(e.indexOf(":")+1)):i=e),t.removeAttribute("hx-swap-oob"),t.removeAttribute("data-hx-swap-oob");let s=querySelectorAllExt(r,o,!1);return s.length?(forEach(s,function(a){let l,c=t.cloneNode(!0);l=getDocument().createDocumentFragment(),l.appendChild(c),isInlineSwap(i,a)||(l=asParentNode(c));let h={shouldSwap:!0,target:a,fragment:l};triggerEvent(a,"htmx:oobBeforeSwap",h)&&(a=h.target,h.shouldSwap&&(handlePreservedElements(l),swapWithStyle(i,a,a,l,n),restorePreservedElements()),forEach(n.elts,function(u){triggerEvent(u,"htmx:oobAfterSwap",h)}))}),t.parentNode.removeChild(t)):(t.parentNode.removeChild(t),triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:t})),e}function restorePreservedElements(){let e=find("#--htmx-preserve-pantry--");if(e){for(let t of[...e.children]){let n=find("#"+t.id);n.parentNode.moveBefore(t,n),n.remove()}e.remove()}}function handlePreservedElements(e){forEach(findAll(e,"[hx-preserve], [data-hx-preserve]"),function(t){let n=getAttributeValue(t,"id"),r=getDocument().getElementById(n);if(r!=null)if(t.moveBefore){let o=find("#--htmx-preserve-pantry--");o==null&&(getDocument().body.insertAdjacentHTML("afterend","
"),o=find("#--htmx-preserve-pantry--")),o.moveBefore(r,null)}else t.parentNode.replaceChild(r,t)})}function handleAttributes(e,t,n){forEach(t.querySelectorAll("[id]"),function(r){let o=getRawAttribute(r,"id");if(o&&o.length>0){let i=o.replace("'","\\'"),s=r.tagName.replace(":","\\:"),a=asParentNode(e),l=a&&a.querySelector(s+"[id='"+i+"']");if(l&&l!==a){let c=r.cloneNode();cloneAttributes(r,l),n.tasks.push(function(){cloneAttributes(r,c)})}}})}function makeAjaxLoadTask(e){return function(){removeClassFromElement(e,htmx.config.addedClass),processNode(asElement(e)),processFocus(asParentNode(e)),triggerEvent(e,"htmx:load")}}function processFocus(e){let t="[autofocus]",n=asHtmlElement(matches(e,t)?e:e.querySelector(t));n?.focus()}function insertNodesBefore(e,t,n,r){for(handleAttributes(e,n,r);n.childNodes.length>0;){let o=n.firstChild;addClassToElement(asElement(o),htmx.config.addedClass),e.insertBefore(o,t),o.nodeType!==Node.TEXT_NODE&&o.nodeType!==Node.COMMENT_NODE&&r.tasks.push(makeAjaxLoadTask(o))}}function stringHash(e,t){let n=0;for(;n0}function swap(e,t,n,r){r||(r={});let o=null,i=null,s=function(){maybeCall(r.beforeSwapCallback),e=resolveTarget(e);let c=r.contextElement?getRootNode(r.contextElement,!1):getDocument(),h=document.activeElement,u={};u={elt:h,start:h?h.selectionStart:null,end:h?h.selectionEnd:null};let f=makeSettleInfo(e);if(n.swapStyle==="textContent")e.textContent=t;else{let d=makeFragment(t);if(f.title=r.title||d.title,r.historyRequest&&(d=d.querySelector("[hx-history-elt],[data-hx-history-elt]")||d),r.selectOOB){let y=r.selectOOB.split(",");for(let m=0;m0?getWindow().setTimeout(v,n.settleDelay):v()},a=htmx.config.globalViewTransitions;n.hasOwnProperty("transition")&&(a=n.transition);let l=r.contextElement||getDocument();if(a&&triggerEvent(l,"htmx:beforeTransition",r.eventInfo)&&typeof Promise<"u"&&document.startViewTransition){let c=new Promise(function(u,f){o=u,i=f}),h=s;s=function(){document.startViewTransition(function(){return h(),c})}}try{n?.swapDelay&&n.swapDelay>0?getWindow().setTimeout(s,n.swapDelay):s()}catch(c){throw triggerErrorEvent(l,"htmx:swapError",r.eventInfo),maybeCall(i),c}}function handleTriggerHeader(e,t,n){let r=e.getResponseHeader(t);if(r.indexOf("{")===0){let o=parseJSON(r);for(let i in o)if(o.hasOwnProperty(i)){let s=o[i];isRawObject(s)?n=s.target!==void 0?s.target:n:s={value:s},triggerEvent(n,i,s)}}else{let o=r.split(",");for(let i=0;i0;){let s=t[0];if(s==="]"){if(r--,r===0){i===null&&(o=o+"true"),t.shift(),o+=")})";try{let a=maybeEval(e,function(){return Function(o)()},function(){return!0});return a.source=o,a}catch(a){return triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:a,source:o}),null}}}else s==="["&&r++;isPossibleRelativeReference(s,i,n)?o+="(("+n+"."+s+") ? ("+n+"."+s+") : (window."+s+"))":o=o+s,i=t.shift()}}}function consumeUntil(e,t){let n="";for(;e.length>0&&!t.test(e[0]);)n+=e.shift();return n}function consumeCSSSelector(e){let t;return e.length>0&&COMBINED_SELECTOR_START.test(e[0])?(e.shift(),t=consumeUntil(e,COMBINED_SELECTOR_END).trim(),e.shift()):t=consumeUntil(e,WHITESPACE_OR_COMMA),t}let INPUT_SELECTOR="input, textarea, select";function parseAndCacheTrigger(e,t,n){let r=[],o=tokenizeString(t);do{consumeUntil(o,NOT_WHITESPACE);let a=o.length,l=consumeUntil(o,/[,\[\s]/);if(l!=="")if(l==="every"){let c={trigger:"every"};consumeUntil(o,NOT_WHITESPACE),c.pollInterval=parseInterval(consumeUntil(o,/[,\[\s]/)),consumeUntil(o,NOT_WHITESPACE);var i=maybeGenerateConditional(e,o,"event");i&&(c.eventFilter=i),r.push(c)}else{let c={trigger:l};var i=maybeGenerateConditional(e,o,"event");for(i&&(c.eventFilter=i),consumeUntil(o,NOT_WHITESPACE);o.length>0&&o[0]!==",";){let u=o.shift();if(u==="changed")c.changed=!0;else if(u==="once")c.once=!0;else if(u==="consume")c.consume=!0;else if(u==="delay"&&o[0]===":")o.shift(),c.delay=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA));else if(u==="from"&&o[0]===":"){if(o.shift(),COMBINED_SELECTOR_START.test(o[0]))var s=consumeCSSSelector(o);else{var s=consumeUntil(o,WHITESPACE_OR_COMMA);if(s==="closest"||s==="find"||s==="next"||s==="previous"){o.shift();let v=consumeCSSSelector(o);v.length>0&&(s+=" "+v)}}c.from=s}else u==="target"&&o[0]===":"?(o.shift(),c.target=consumeCSSSelector(o)):u==="throttle"&&o[0]===":"?(o.shift(),c.throttle=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA))):u==="queue"&&o[0]===":"?(o.shift(),c.queue=consumeUntil(o,WHITESPACE_OR_COMMA)):u==="root"&&o[0]===":"?(o.shift(),c[u]=consumeCSSSelector(o)):u==="threshold"&&o[0]===":"?(o.shift(),c[u]=consumeUntil(o,WHITESPACE_OR_COMMA)):triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()});consumeUntil(o,NOT_WHITESPACE)}r.push(c)}o.length===a&&triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()}),consumeUntil(o,NOT_WHITESPACE)}while(o[0]===","&&o.shift());return n&&(n[t]=r),r}function getTriggerSpecs(e){let t=getAttributeValue(e,"hx-trigger"),n=[];if(t){let r=htmx.config.triggerSpecsCache;n=r&&r[t]||parseAndCacheTrigger(e,t,r)}return n.length>0?n:matches(e,"form")?[{trigger:"submit"}]:matches(e,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(e,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}function cancelPolling(e){getInternalData(e).cancelled=!0}function processPolling(e,t,n){let r=getInternalData(e);r.timeout=getWindow().setTimeout(function(){bodyContains(e)&&r.cancelled!==!0&&(maybeFilterEvent(n,e,makeEvent("hx:poll:trigger",{triggerSpec:n,target:e}))||t(e),processPolling(e,t,n))},n.pollInterval)}function isLocalLink(e){return location.hostname===e.hostname&&getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")!==0}function eltIsDisabled(e){return closest(e,htmx.config.disableSelector)}function boostElement(e,t,n){if(e instanceof HTMLAnchorElement&&isLocalLink(e)&&(e.target===""||e.target==="_self")||e.tagName==="FORM"&&String(getRawAttribute(e,"method")).toLowerCase()!=="dialog"){t.boosted=!0;let r,o;if(e.tagName==="A")r="get",o=getRawAttribute(e,"href");else{let i=getRawAttribute(e,"method");r=i?i.toLowerCase():"get",o=getRawAttribute(e,"action"),(o==null||o==="")&&(o=location.href),r==="get"&&o.includes("?")&&(o=o.replace(/\?[^#]+/,""))}n.forEach(function(i){addEventListener(e,function(s,a){let l=asElement(s);if(eltIsDisabled(l)){cleanUpElement(l);return}issueAjaxRequest(r,o,l,a)},t,i,!0)})}}function shouldCancel(e,t){if(e.type==="submit"&&t.tagName==="FORM")return!0;if(e.type==="click"){let n=t.closest('input[type="submit"], button');if(n&&n.form&&n.type==="submit")return!0;let r=t.closest("a"),o=/^#.+/;if(r&&r.href&&!o.test(r.getAttribute("href")))return!0}return!1}function ignoreBoostedAnchorCtrlClick(e,t){return getInternalData(e).boosted&&e instanceof HTMLAnchorElement&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function maybeFilterEvent(e,t,n){let r=e.eventFilter;if(r)try{return r.call(t,n)!==!0}catch(o){let i=r.source;return triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:o,source:i}),!0}return!1}function addEventListener(e,t,n,r,o){let i=getInternalData(e),s;r.from?s=querySelectorAllExt(e,r.from):s=[e],r.changed&&("lastValue"in i||(i.lastValue=new WeakMap),s.forEach(function(a){i.lastValue.has(r)||i.lastValue.set(r,new WeakMap),i.lastValue.get(r).set(a,a.value)})),forEach(s,function(a){let l=function(c){if(!bodyContains(e)){a.removeEventListener(r.trigger,l);return}if(ignoreBoostedAnchorCtrlClick(e,c)||((o||shouldCancel(c,a))&&c.preventDefault(),maybeFilterEvent(r,e,c)))return;let h=getInternalData(c);if(h.triggerSpec=r,h.handledFor==null&&(h.handledFor=[]),h.handledFor.indexOf(e)<0){if(h.handledFor.push(e),r.consume&&c.stopPropagation(),r.target&&c.target&&!matches(asElement(c.target),r.target))return;if(r.once){if(i.triggeredOnce)return;i.triggeredOnce=!0}if(r.changed){let u=c.target,f=u.value,v=i.lastValue.get(r);if(v.has(u)&&v.get(u)===f)return;v.set(u,f)}if(i.delayed&&clearTimeout(i.delayed),i.throttle)return;r.throttle>0?i.throttle||(triggerEvent(e,"htmx:trigger"),t(e,c),i.throttle=getWindow().setTimeout(function(){i.throttle=null},r.throttle)):r.delay>0?i.delayed=getWindow().setTimeout(function(){triggerEvent(e,"htmx:trigger"),t(e,c)},r.delay):(triggerEvent(e,"htmx:trigger"),t(e,c))}};n.listenerInfos==null&&(n.listenerInfos=[]),n.listenerInfos.push({trigger:r.trigger,listener:l,on:a}),a.addEventListener(r.trigger,l)})}let windowIsScrolling=!1,scrollHandler=null;function initScrollHandler(){scrollHandler||(scrollHandler=function(){windowIsScrolling=!0},window.addEventListener("scroll",scrollHandler),window.addEventListener("resize",scrollHandler),setInterval(function(){windowIsScrolling&&(windowIsScrolling=!1,forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(e){maybeReveal(e)}))},200))}function maybeReveal(e){!hasAttribute(e,"data-hx-revealed")&&isScrolledIntoView(e)&&(e.setAttribute("data-hx-revealed","true"),getInternalData(e).initHash?triggerEvent(e,"revealed"):e.addEventListener("htmx:afterProcessNode",function(){triggerEvent(e,"revealed")},{once:!0}))}function loadImmediately(e,t,n,r){let o=function(){n.loaded||(n.loaded=!0,triggerEvent(e,"htmx:trigger"),t(e))};r>0?getWindow().setTimeout(o,r):o()}function processVerbs(e,t,n){let r=!1;return forEach(VERBS,function(o){if(hasAttribute(e,"hx-"+o)){let i=getAttributeValue(e,"hx-"+o);r=!0,t.path=i,t.verb=o,n.forEach(function(s){addTriggerHandler(e,s,t,function(a,l){let c=asElement(a);if(eltIsDisabled(c)){cleanUpElement(c);return}issueAjaxRequest(o,i,c,l)})})}}),r}function addTriggerHandler(e,t,n,r){if(t.trigger==="revealed")initScrollHandler(),addEventListener(e,r,n,t),maybeReveal(asElement(e));else if(t.trigger==="intersect"){let o={};t.root&&(o.root=querySelectorExt(e,t.root)),t.threshold&&(o.threshold=parseFloat(t.threshold)),new IntersectionObserver(function(s){for(let a=0;a0?(n.polling=!0,processPolling(asElement(e),r,t)):addEventListener(e,r,n,t)}function shouldProcessHxOn(e){let t=asElement(e);if(!t)return!1;let n=t.attributes;for(let r=0;r", "+i).join(""))}else return[]}function maybeSetLastButtonClicked(e){let t=getTargetButton(e.target),n=getRelatedFormData(e);n&&(n.lastButtonClicked=t)}function maybeUnsetLastButtonClicked(e){let t=getRelatedFormData(e);t&&(t.lastButtonClicked=null)}function getTargetButton(e){return closest(asElement(e),"button, input[type='submit']")}function getRelatedForm(e){return e.form||closest(e,"form")}function getRelatedFormData(e){let t=getTargetButton(e.target);if(!t)return;let n=getRelatedForm(t);if(n)return getInternalData(n)}function initButtonTracking(e){e.addEventListener("click",maybeSetLastButtonClicked),e.addEventListener("focusin",maybeSetLastButtonClicked),e.addEventListener("focusout",maybeUnsetLastButtonClicked)}function addHxOnEventHandler(e,t,n){let r=getInternalData(e);Array.isArray(r.onHandlers)||(r.onHandlers=[]);let o,i=function(s){maybeEval(e,function(){eltIsDisabled(e)||(o||(o=new Function("event",n)),o.call(e,s))})};e.addEventListener(t,i),r.onHandlers.push({event:t,listener:i})}function processHxOnWildcard(e){deInitOnHandlers(e);for(let t=0;thtmx.config.historyCacheSize;)i.shift();for(;i.length>0;)try{sessionStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(a){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:a,cache:i}),i.shift()}}function getCachedHistory(e){if(!canAccessLocalStorage())return null;e=normalizePath(e);let t=parseJSON(sessionStorage.getItem("htmx-history-cache"))||[];for(let n=0;n=200&&this.status<400?(r.response=this.response,triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",r),swap(r.historyElt,r.response,n,{contextElement:r.historyElt,historyRequest:!0}),setCurrentPathForHistory(r.path),triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,cacheMiss:!0,serverResponse:r.response})):triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",r)},triggerEvent(getDocument().body,"htmx:historyCacheMiss",r)&&t.send()}function restoreHistory(e){saveCurrentPageToHistory(),e=e||location.pathname+location.search;let t=getCachedHistory(e);if(t){let n={swapStyle:"innerHTML",swapDelay:0,settleDelay:0,scroll:t.scroll},r={path:e,item:t,historyElt:getHistoryElement(),swapSpec:n};triggerEvent(getDocument().body,"htmx:historyCacheHit",r)&&(swap(r.historyElt,t.content,n,{contextElement:r.historyElt,title:t.title}),setCurrentPathForHistory(r.path),triggerEvent(getDocument().body,"htmx:historyRestore",r))}else htmx.config.refreshOnHistoryMiss?htmx.location.reload(!0):loadHistoryFromServer(e)}function addRequestIndicatorClasses(e){let t=findAttributeTargets(e,"hx-indicator");return t==null&&(t=[e]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.classList.add.call(n.classList,htmx.config.requestClass)}),t}function disableElements(e){let t=findAttributeTargets(e,"hx-disabled-elt");return t==null&&(t=[]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.setAttribute("disabled",""),n.setAttribute("data-disabled-by-htmx","")}),t}function removeRequestIndicators(e,t){forEach(e.concat(t),function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||1)-1}),forEach(e,function(n){getInternalData(n).requestCount===0&&n.classList.remove.call(n.classList,htmx.config.requestClass)}),forEach(t,function(n){getInternalData(n).requestCount===0&&(n.removeAttribute("disabled"),n.removeAttribute("data-disabled-by-htmx"))})}function haveSeenNode(e,t){for(let n=0;nt.indexOf(o)<0):r=r.filter(o=>o!==t),n.delete(e),forEach(r,o=>n.append(e,o))}}function getValueFromInput(e){return e instanceof HTMLSelectElement&&e.multiple?toArray(e.querySelectorAll("option:checked")).map(function(t){return t.value}):e instanceof HTMLInputElement&&e.files?toArray(e.files):e.value}function processInputValue(e,t,n,r,o){if(!(r==null||haveSeenNode(e,r))){if(e.push(r),shouldInclude(r)){let i=getRawAttribute(r,"name");addValueToFormData(i,getValueFromInput(r),t),o&&validateElement(r,n)}r instanceof HTMLFormElement&&(forEach(r.elements,function(i){e.indexOf(i)>=0?removeValueFromFormData(i.name,getValueFromInput(i),t):e.push(i),o&&validateElement(i,n)}),new FormData(r).forEach(function(i,s){i instanceof File&&i.name===""||addValueToFormData(s,i,t)}))}}function validateElement(e,t){let n=e;n.willValidate&&(triggerEvent(n,"htmx:validation:validate"),n.checkValidity()||(triggerEvent(n,"htmx:validation:failed",{message:n.validationMessage,validity:n.validity})&&!t.length&&htmx.config.reportValidityOfForms&&n.reportValidity(),t.push({elt:n,message:n.validationMessage,validity:n.validity})))}function overrideFormData(e,t){for(let n of t.keys())e.delete(n);return t.forEach(function(n,r){e.append(r,n)}),e}function getInputValues(e,t){let n=[],r=new FormData,o=new FormData,i=[],s=getInternalData(e);s.lastButtonClicked&&!bodyContains(s.lastButtonClicked)&&(s.lastButtonClicked=null);let a=e instanceof HTMLFormElement&&e.noValidate!==!0||getAttributeValue(e,"hx-validate")==="true";if(s.lastButtonClicked&&(a=a&&s.lastButtonClicked.formNoValidate!==!0),t!=="get"&&processInputValue(n,o,i,getRelatedForm(e),a),processInputValue(n,r,i,e,a),s.lastButtonClicked||e.tagName==="BUTTON"||e.tagName==="INPUT"&&getRawAttribute(e,"type")==="submit"){let c=s.lastButtonClicked||e,h=getRawAttribute(c,"name");addValueToFormData(h,c.value,o)}let l=findAttributeTargets(e,"hx-include");return forEach(l,function(c){processInputValue(n,r,i,asElement(c),a),matches(c,"form")||forEach(asParentNode(c).querySelectorAll(INPUT_SELECTOR),function(h){processInputValue(n,r,i,h,a)})}),overrideFormData(r,o),{errors:i,formData:r,values:formDataProxy(r)}}function appendParam(e,t,n){e!==""&&(e+="&"),String(n)==="[object Object]"&&(n=JSON.stringify(n));let r=encodeURIComponent(n);return e+=encodeURIComponent(t)+"="+r,e}function urlEncode(e){e=formDataFromObject(e);let t="";return e.forEach(function(n,r){t=appendParam(t,r,n)}),t}function getHeaders(e,t,n){let r={"HX-Request":"true","HX-Trigger":getRawAttribute(e,"id"),"HX-Trigger-Name":getRawAttribute(e,"name"),"HX-Target":getAttributeValue(t,"id"),"HX-Current-URL":location.href};return getValuesForElement(e,"hx-headers",!1,r),n!==void 0&&(r["HX-Prompt"]=n),getInternalData(e).boosted&&(r["HX-Boosted"]="true"),r}function filterValues(e,t){let n=getClosestAttributeValue(t,"hx-params");if(n){if(n==="none")return new FormData;if(n==="*")return e;if(n.indexOf("not ")===0)return forEach(n.slice(4).split(","),function(r){r=r.trim(),e.delete(r)}),e;{let r=new FormData;return forEach(n.split(","),function(o){o=o.trim(),e.has(o)&&e.getAll(o).forEach(function(i){r.append(o,i)})}),r}}else return e}function isAnchorLink(e){return!!getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")>=0}function getSwapSpecification(e,t){let n=t||getClosestAttributeValue(e,"hx-swap"),r={swapStyle:getInternalData(e).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};if(htmx.config.scrollIntoViewOnBoost&&getInternalData(e).boosted&&!isAnchorLink(e)&&(r.show="top"),n){let s=splitOnWhitespace(n);if(s.length>0)for(let a=0;a0?o.join(":"):null;r.scroll=h,r.scrollTarget=i}else if(l.indexOf("show:")===0){var o=l.slice(5).split(":");let u=o.pop();var i=o.length>0?o.join(":"):null;r.show=u,r.showTarget=i}else if(l.indexOf("focus-scroll:")===0){let c=l.slice(13);r.focusScroll=c=="true"}else a==0?r.swapStyle=l:logError("Unknown modifier in hx-swap: "+l)}}return r}function usesFormData(e){return getClosestAttributeValue(e,"hx-encoding")==="multipart/form-data"||matches(e,"form")&&getRawAttribute(e,"enctype")==="multipart/form-data"}function encodeParamsForBody(e,t,n){let r=null;return withExtensions(t,function(o){r==null&&(r=o.encodeParameters(e,n,t))}),r??(usesFormData(t)?overrideFormData(new FormData,formDataFromObject(n)):urlEncode(n))}function makeSettleInfo(e){return{tasks:[],elts:[e]}}function updateScrollState(e,t){let n=e[0],r=e[e.length-1];if(t.scroll){var o=null;t.scrollTarget&&(o=asElement(querySelectorExt(n,t.scrollTarget))),t.scroll==="top"&&(n||o)&&(o=o||n,o.scrollTop=0),t.scroll==="bottom"&&(r||o)&&(o=o||r,o.scrollTop=o.scrollHeight),typeof t.scroll=="number"&&getWindow().setTimeout(function(){window.scrollTo(0,t.scroll)},0)}if(t.show){var o=null;if(t.showTarget){let s=t.showTarget;t.showTarget==="window"&&(s="body"),o=asElement(querySelectorExt(n,s))}t.show==="top"&&(n||o)&&(o=o||n,o.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})),t.show==="bottom"&&(r||o)&&(o=o||r,o.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior}))}}function getValuesForElement(e,t,n,r,o){if(r==null&&(r={}),e==null)return r;let i=getAttributeValue(e,t);if(i){let s=i.trim(),a=n;if(s==="unset")return null;s.indexOf("javascript:")===0?(s=s.slice(11),a=!0):s.indexOf("js:")===0&&(s=s.slice(3),a=!0),s.indexOf("{")!==0&&(s="{"+s+"}");let l;a?l=maybeEval(e,function(){return o?Function("event","return ("+s+")").call(e,o):Function("return ("+s+")").call(e)},{}):l=parseJSON(s);for(let c in l)l.hasOwnProperty(c)&&r[c]==null&&(r[c]=l[c])}return getValuesForElement(asElement(parentElt(e)),t,n,r,o)}function maybeEval(e,t,n){return htmx.config.allowEval?t():(triggerErrorEvent(e,"htmx:evalDisallowedError"),n)}function getHXVarsForElement(e,t,n){return getValuesForElement(e,"hx-vars",!0,n,t)}function getHXValsForElement(e,t,n){return getValuesForElement(e,"hx-vals",!1,n,t)}function getExpressionVars(e,t){return mergeObjects(getHXVarsForElement(e,t),getHXValsForElement(e,t))}function safelySetHeaderValue(e,t,n){if(n!==null)try{e.setRequestHeader(t,n)}catch{e.setRequestHeader(t,encodeURIComponent(n)),e.setRequestHeader(t+"-URI-AutoEncoded","true")}}function getPathFromResponse(e){if(e.responseURL)try{let t=new URL(e.responseURL);return t.pathname+t.search}catch{triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:e.responseURL})}}function hasHeader(e,t){return t.test(e.getAllResponseHeaders())}function ajaxHelper(e,t,n){if(e=e.toLowerCase(),n){if(n instanceof Element||typeof n=="string")return issueAjaxRequest(e,t,null,null,{targetOverride:resolveTarget(n)||DUMMY_ELT,returnPromise:!0});{let r=resolveTarget(n.target);return(n.target&&!r||n.source&&!r&&!resolveTarget(n.source))&&(r=DUMMY_ELT),issueAjaxRequest(e,t,resolveTarget(n.source),n.event,{handler:n.handler,headers:n.headers,values:n.values,targetOverride:r,swapOverride:n.swap,select:n.select,returnPromise:!0,push:n.push,replace:n.replace,selectOOB:n.selectOOB})}}else return issueAjaxRequest(e,t,null,null,{returnPromise:!0})}function hierarchyForElt(e){let t=[];for(;e;)t.push(e),e=e.parentElement;return t}function verifyPath(e,t,n){let r=new URL(t,location.protocol!=="about:"?location.href:window.origin),i=(location.protocol!=="about:"?location.origin:window.origin)===r.origin;return htmx.config.selfRequestsOnly&&!i?!1:triggerEvent(e,"htmx:validateUrl",mergeObjects({url:r,sameHost:i},n))}function formDataFromObject(e){if(e instanceof FormData)return e;let t=new FormData;for(let n in e)e.hasOwnProperty(n)&&(e[n]&&typeof e[n].forEach=="function"?e[n].forEach(function(r){t.append(n,r)}):typeof e[n]=="object"&&!(e[n]instanceof Blob)?t.append(n,JSON.stringify(e[n])):t.append(n,e[n]));return t}function formDataArrayProxy(e,t,n){return new Proxy(n,{get:function(r,o){return typeof o=="number"?r[o]:o==="length"?r.length:o==="push"?function(i){r.push(i),e.append(t,i)}:typeof r[o]=="function"?function(){r[o].apply(r,arguments),e.delete(t),r.forEach(function(i){e.append(t,i)})}:r[o]&&r[o].length===1?r[o][0]:r[o]},set:function(r,o,i){return r[o]=i,e.delete(t),r.forEach(function(s){e.append(t,s)}),!0}})}function formDataProxy(e){return new Proxy(e,{get:function(t,n){if(typeof n=="symbol"){let o=Reflect.get(t,n);return typeof o=="function"?function(){return o.apply(e,arguments)}:o}if(n==="toJSON")return()=>Object.fromEntries(e);if(n in t&&typeof t[n]=="function")return function(){return e[n].apply(e,arguments)};let r=e.getAll(n);if(r.length!==0)return r.length===1?r[0]:formDataArrayProxy(t,n,r)},set:function(t,n,r){return typeof n!="string"?!1:(t.delete(n),r&&typeof r.forEach=="function"?r.forEach(function(o){t.append(n,o)}):typeof r=="object"&&!(r instanceof Blob)?t.append(n,JSON.stringify(r)):t.append(n,r),!0)},deleteProperty:function(t,n){return typeof n=="string"&&t.delete(n),!0},ownKeys:function(t){return Reflect.ownKeys(Object.fromEntries(t))},getOwnPropertyDescriptor:function(t,n){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(t),n)}})}function issueAjaxRequest(e,t,n,r,o,i){let s=null,a=null;if(o=o??{},o.returnPromise&&typeof Promise<"u")var l=new Promise(function(p,w){s=p,a=w});n==null&&(n=getDocument().body);let c=o.handler||handleAjaxResponse,h=o.select||null;if(!bodyContains(n))return maybeCall(s),l;let u=o.targetOverride||asElement(getTarget(n));if(u==null||u==DUMMY_ELT)return triggerErrorEvent(n,"htmx:targetError",{target:getClosestAttributeValue(n,"hx-target")}),maybeCall(a),l;let f=getInternalData(n),v=f.lastButtonClicked;if(v){let p=getRawAttribute(v,"formaction");p!=null&&(t=p);let w=getRawAttribute(v,"formmethod");if(w!=null)if(VERBS.includes(w.toLowerCase()))e=w;else return maybeCall(s),l}let d=getClosestAttributeValue(n,"hx-confirm");if(i===void 0&&triggerEvent(n,"htmx:confirm",{target:u,elt:n,path:t,verb:e,triggeringEvent:r,etc:o,issueRequest:function(T){return issueAjaxRequest(e,t,n,r,o,!!T)},question:d})===!1)return maybeCall(s),l;let y=n,m=getClosestAttributeValue(n,"hx-sync"),x=null,b=!1;if(m){let p=m.split(":"),w=p[0].trim();if(w==="this"?y=findThisElement(n,"hx-sync"):y=asElement(querySelectorExt(n,w)),m=(p[1]||"drop").trim(),f=getInternalData(y),m==="drop"&&f.xhr&&f.abortable!==!0)return maybeCall(s),l;if(m==="abort"){if(f.xhr)return maybeCall(s),l;b=!0}else m==="replace"?triggerEvent(y,"htmx:abort"):m.indexOf("queue")===0&&(x=(m.split(" ")[1]||"last").trim())}if(f.xhr)if(f.abortable)triggerEvent(y,"htmx:abort");else{if(x==null){if(r){let p=getInternalData(r);p&&p.triggerSpec&&p.triggerSpec.queue&&(x=p.triggerSpec.queue)}x==null&&(x="last")}return f.queuedRequests==null&&(f.queuedRequests=[]),x==="first"&&f.queuedRequests.length===0?f.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o)}):x==="all"?f.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o)}):x==="last"&&(f.queuedRequests=[],f.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o)})),maybeCall(s),l}let E=new XMLHttpRequest;f.xhr=E,f.abortable=b;let g=function(){f.xhr=null,f.abortable=!1,f.queuedRequests!=null&&f.queuedRequests.length>0&&f.queuedRequests.shift()()},X=getClosestAttributeValue(n,"hx-prompt");if(X){var k=prompt(X);if(k===null||!triggerEvent(n,"htmx:prompt",{prompt:k,target:u}))return maybeCall(s),g(),l}if(d&&!i&&!confirm(d))return maybeCall(s),g(),l;let H=getHeaders(n,u,k);e!=="get"&&!usesFormData(n)&&(H["Content-Type"]="application/x-www-form-urlencoded"),o.headers&&(H=mergeObjects(H,o.headers));let $=getInputValues(n,e),R=$.errors,J=$.formData;o.values&&overrideFormData(J,formDataFromObject(o.values));let oe=formDataFromObject(getExpressionVars(n,r)),P=overrideFormData(J,oe),D=filterValues(P,n);htmx.config.getCacheBusterParam&&e==="get"&&D.set("org.htmx.cache-buster",getRawAttribute(u,"id")||"true"),(t==null||t==="")&&(t=location.href);let N=getValuesForElement(n,"hx-request"),z=getInternalData(n).boosted,L=htmx.config.methodsThatUseUrlParams.indexOf(e)>=0,S={boosted:z,useUrlParams:L,formData:D,parameters:formDataProxy(D),unfilteredFormData:P,unfilteredParameters:formDataProxy(P),headers:H,elt:n,target:u,verb:e,errors:R,withCredentials:o.credentials||N.credentials||htmx.config.withCredentials,timeout:o.timeout||N.timeout||htmx.config.timeout,path:t,triggeringEvent:r};if(!triggerEvent(n,"htmx:configRequest",S))return maybeCall(s),g(),l;if(t=S.path,e=S.verb,H=S.headers,D=formDataFromObject(S.parameters),R=S.errors,L=S.useUrlParams,R&&R.length>0)return triggerEvent(n,"htmx:validation:halted",S),maybeCall(s),g(),l;let Y=t.split("#"),ie=Y[0],M=Y[1],A=t;if(L&&(A=ie,!D.keys().next().done&&(A.indexOf("?")<0?A+="?":A+="&",A+=urlEncode(D),M&&(A+="#"+M))),!verifyPath(n,A,S))return triggerErrorEvent(n,"htmx:invalidPath",S),maybeCall(a),g(),l;if(E.open(e.toUpperCase(),A,!0),E.overrideMimeType("text/html"),E.withCredentials=S.withCredentials,E.timeout=S.timeout,!N.noHeaders){for(let p in H)if(H.hasOwnProperty(p)){let w=H[p];safelySetHeaderValue(E,p,w)}}let C={xhr:E,target:u,requestConfig:S,etc:o,boosted:z,select:h,pathInfo:{requestPath:t,finalRequestPath:A,responsePath:null,anchor:M}};if(E.onload=function(){try{let p=hierarchyForElt(n);if(C.pathInfo.responsePath=getPathFromResponse(E),c(n,C),C.keepIndicators!==!0&&removeRequestIndicators(O,F),triggerEvent(n,"htmx:afterRequest",C),triggerEvent(n,"htmx:afterOnLoad",C),!bodyContains(n)){let w=null;for(;p.length>0&&w==null;){let T=p.shift();bodyContains(T)&&(w=T)}w&&(triggerEvent(w,"htmx:afterRequest",C),triggerEvent(w,"htmx:afterOnLoad",C))}maybeCall(s)}catch(p){throw triggerErrorEvent(n,"htmx:onLoadError",mergeObjects({error:p},C)),p}finally{g()}},E.onerror=function(){removeRequestIndicators(O,F),triggerErrorEvent(n,"htmx:afterRequest",C),triggerErrorEvent(n,"htmx:sendError",C),maybeCall(a),g()},E.onabort=function(){removeRequestIndicators(O,F),triggerErrorEvent(n,"htmx:afterRequest",C),triggerErrorEvent(n,"htmx:sendAbort",C),maybeCall(a),g()},E.ontimeout=function(){removeRequestIndicators(O,F),triggerErrorEvent(n,"htmx:afterRequest",C),triggerErrorEvent(n,"htmx:timeout",C),maybeCall(a),g()},!triggerEvent(n,"htmx:beforeRequest",C))return maybeCall(s),g(),l;var O=addRequestIndicatorClasses(n),F=disableElements(n);forEach(["loadstart","loadend","progress","abort"],function(p){forEach([E,E.upload],function(w){w.addEventListener(p,function(T){triggerEvent(n,"htmx:xhr:"+p,{lengthComputable:T.lengthComputable,loaded:T.loaded,total:T.total})})})}),triggerEvent(n,"htmx:beforeSend",C);let se=L?null:encodeParamsForBody(E,n,D);return E.send(se),l}function determineHistoryUpdates(e,t){let n=t.xhr,r=null,o=null;if(hasHeader(n,/HX-Push:/i)?(r=n.getResponseHeader("HX-Push"),o="push"):hasHeader(n,/HX-Push-Url:/i)?(r=n.getResponseHeader("HX-Push-Url"),o="push"):hasHeader(n,/HX-Replace-Url:/i)&&(r=n.getResponseHeader("HX-Replace-Url"),o="replace"),r)return r==="false"?{}:{type:o,path:r};let i=t.pathInfo.finalRequestPath,s=t.pathInfo.responsePath,a=t.etc.push||getClosestAttributeValue(e,"hx-push-url"),l=t.etc.replace||getClosestAttributeValue(e,"hx-replace-url"),c=getInternalData(e).boosted,h=null,u=null;return a?(h="push",u=a):l?(h="replace",u=l):c&&(h="push",u=s||i),u?u==="false"?{}:(u==="true"&&(u=s||i),t.pathInfo.anchor&&u.indexOf("#")===-1&&(u=u+"#"+t.pathInfo.anchor),{type:h,path:u}):{}}function codeMatches(e,t){var n=new RegExp(e.code);return n.test(t.toString(10))}function resolveResponseHandling(e){for(var t=0;t.${t}{opacity:0;visibility: hidden} .${n} .${t}, .${n}.${t}{opacity:1;visibility: visible;transition: opacity 200ms ease-in}`)}}function getMetaConfig(){let e=getDocument().querySelector('meta[name="htmx-config"]');return e?parseJSON(e.content):null}function mergeMetaConfig(){let e=getMetaConfig();e&&(htmx.config=mergeObjects(htmx.config,e))}return ready(function(){mergeMetaConfig(),insertIndicatorStyles();let e=getDocument().body;processNode(e);let t=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(r){let o=r.detail.elt||r.target,i=getInternalData(o);i&&i.xhr&&i.xhr.abort()});let n=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(r){r.state&&r.state.htmx?(restoreHistory(),forEach(t,function(o){triggerEvent(o,"htmx:restored",{document:getDocument(),triggerEvent})})):n&&n(r)},getWindow().setTimeout(function(){triggerEvent(e,"htmx:load",{}),e=null},0)}),htmx})(),q=ae;(function(){let e;q.defineExtension("json-enc",{init:function(t){e=t},onEvent:function(t,n){t==="htmx:configRequest"&&(n.detail.headers["Content-Type"]="application/json")},encodeParameters:function(t,n,r){t.overrideMimeType("text/json");let o={};n.forEach(function(s,a){Object.hasOwn(o,a)?(Array.isArray(o[a])||(o[a]=[o[a]]),o[a].push(s)):o[a]=s});let i=e.getExpressionVars(r);return Object.keys(o).forEach(function(s){o[s]=Object.hasOwn(i,s)?i[s]:o[s]}),JSON.stringify(o)}})})();var G=document.createElement("template");G.innerHTML=` @@ -92,4 +92,4 @@ var ae=(function(){"use strict";let htmx={onLoad:null,process:null,on:null,off:n -`;function K(e){return e.cloneNode(!0)}var B=class extends HTMLElement{static tag="actor-typeahead";static define(t=this.tag){this.tag=t;let n=customElements.getName(this);if(n&&n!==t)return console.warn(`${this.name} already defined as <${n}>!`);let r=customElements.get(t);if(r&&r!==this)return console.warn(`<${t}> already defined as ${r.name}!`);customElements.define(t,this)}static{let t=new URL(import.meta.url).searchParams.get("tag")||this.tag;t!=="none"&&this.define(t)}#n=this.attachShadow({mode:"closed"});#r=[];#e=-1;#o=!1;constructor(){super(),this.#n.append(K(G).content),this.#t(),this.addEventListener("input",this),this.addEventListener("focusout",this),this.addEventListener("keydown",this),this.#n.addEventListener("pointerdown",this),this.#n.addEventListener("pointerup",this),this.#n.addEventListener("click",this)}get#i(){let t=Number.parseInt(this.getAttribute("rows")??"");return Number.isNaN(t)?5:t}handleEvent(t){switch(t.type){case"input":this.#a(t);break;case"keydown":this.#s(t);break;case"focusout":this.#l(t);break;case"pointerdown":this.#c(t);break;case"pointerup":this.#u(t);break}}#s(t){switch(t.key){case"ArrowDown":t.preventDefault(),this.#e=Math.min(this.#e+1,this.#i-1),this.#t();break;case"PageDown":t.preventDefault(),this.#e=this.#i-1,this.#t();break;case"ArrowUp":t.preventDefault(),this.#e=Math.max(this.#e-1,0),this.#t();break;case"PageUp":t.preventDefault(),this.#e=0,this.#t();break;case"Escape":t.preventDefault(),this.#r=[],this.#e=-1,this.#t();break;case"Enter":t.preventDefault(),this.#n.querySelectorAll("button")[this.#e]?.dispatchEvent(new PointerEvent("pointerup",{bubbles:!0}));break}}async#a(t){let n=t.target?.value;if(!n){this.#r=[],this.#t();return}let r=this.getAttribute("host")??"https://public.api.bsky.app",o=new URL("xrpc/app.bsky.actor.searchActorsTypeahead",r);o.searchParams.set("q",n),o.searchParams.set("limit",`${this.#i}`);let s=await(await fetch(o)).json();this.#r=s.actors,this.#e=-1,this.#t()}async#l(t){this.#o||(this.#r=[],this.#e=-1,this.#t())}#t(){let t=document.createDocumentFragment(),n=-1;for(let r of this.#r){let o=K(Q).content,i=o.querySelector("button");i&&(i.dataset.handle=r.handle,++n===this.#e&&(i.dataset.active="true"));let s=o.querySelector("img");s&&r.avatar&&(s.src=r.avatar);let a=o.querySelector(".handle");a&&(a.textContent=r.handle),t.append(o)}this.#n.querySelector(".menu")?.replaceChildren(...t.children)}#c(t){this.#o=!0}#u(t){this.#o=!1,this.querySelector("input")?.focus();let n=t.target?.closest("button"),r=this.querySelector("input");!r||!n||(r.value=n.dataset.handle||"",this.#r=[],this.#t())}};function ee(){return localStorage.getItem("theme")||"system"}function le(e){return e==="dark"||e==="light"?e:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function _(){let e=ee(),n=le(e)==="dark";document.documentElement.classList.toggle("dark",n),document.documentElement.setAttribute("data-theme",n?"dark":"light"),ce(e)}function te(e){localStorage.setItem("theme",e),_(),ue()}function ce(e){let t={system:"sun-moon",light:"sun",dark:"moon"};document.querySelectorAll("[data-theme-icon] use").forEach(n=>{n.setAttribute("href",`/icons.svg#${t[e]||"sun-moon"}`)}),document.querySelectorAll(".theme-option").forEach(n=>{let r=n.dataset.value===e,o=n.querySelector(".theme-check");o&&(o.style.visibility=r?"visible":"hidden")})}function ue(){document.querySelectorAll("[data-theme-toggle]").forEach(e=>{let t=e.closest("details");t&&t.removeAttribute("open")})}window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{ee()==="system"&&_()});function fe(){let e=document.querySelector(".nav-search-wrapper"),t=document.getElementById("nav-search-input");!e||!t||(e.classList.toggle("expanded"),e.classList.contains("expanded")&&t.focus())}function V(){let e=document.querySelector(".nav-search-wrapper");e&&e.classList.remove("expanded")}document.addEventListener("DOMContentLoaded",()=>{let e=document.querySelector(".nav-search-wrapper"),t=document.getElementById("nav-search-input");!e||!t||(document.addEventListener("keydown",n=>{if(n.key==="Escape"&&e.classList.contains("expanded")&&V(),n.key==="/"&&!e.classList.contains("expanded")){let r=n.target.tagName;if(r==="INPUT"||r==="TEXTAREA"||n.target.isContentEditable)return;n.preventDefault(),e.classList.add("expanded"),t.focus()}}),document.addEventListener("click",n=>{e.classList.contains("expanded")&&!e.contains(n.target)&&V()}))});function ne(e,t){!t&&typeof event<"u"&&(t=event.target.closest("button")),navigator.clipboard.writeText(e).then(()=>{if(!t)return;let n=t.innerHTML;t.innerHTML=' Copied!',setTimeout(()=>{t.innerHTML=n},2e3)}).catch(n=>{console.error("Failed to copy:",n)})}document.addEventListener("DOMContentLoaded",()=>{document.addEventListener("click",e=>{let t=e.target.closest("button[data-cmd]");if(t){ne(t.getAttribute("data-cmd"),t);return}if(e.target.closest("a, button, input, .cmd"))return;let n=e.target.closest("[data-href]");n&&(window.location=n.getAttribute("data-href"))})});function de(e){let t=Math.floor((new Date-new Date(e))/1e3),n={year:31536e3,month:2592e3,week:604800,day:86400,hour:3600,minute:60,second:1};for(let[r,o]of Object.entries(n)){let i=Math.floor(t/o);if(i>=1)return i===1?`1 ${r} ago`:`${i} ${r}s ago`}return"just now"}function j(){document.querySelectorAll("time[datetime]").forEach(e=>{let t=e.getAttribute("datetime");if(t&&!e.dataset.noUpdate){let n=de(t);e.textContent!==n&&(e.textContent=n)}})}document.addEventListener("DOMContentLoaded",()=>{j(),_(),document.querySelectorAll("[data-theme-menu]").forEach(e=>{e.querySelectorAll(".theme-option").forEach(t=>{t.addEventListener("click",()=>{te(t.dataset.value)})})}),document.addEventListener("click",e=>{let t=e.target.closest("details.dropdown");document.querySelectorAll("details.dropdown[open]").forEach(n=>{n!==t&&n.removeAttribute("open")})})});document.addEventListener("htmx:afterSwap",j);setInterval(j,6e4);function he(){let e=document.getElementById("show-offline-toggle"),t=document.querySelector(".manifests-list");!e||!t||(localStorage.setItem("showOfflineManifests",e.checked),e.checked?t.classList.add("show-offline"):t.classList.remove("show-offline"))}document.addEventListener("DOMContentLoaded",()=>{let e=document.getElementById("show-offline-toggle");if(!e)return;let t=localStorage.getItem("showOfflineManifests")==="true";e.checked=t;let n=document.querySelector(".manifests-list");n&&(t?n.classList.add("show-offline"):n.classList.remove("show-offline"))});async function me(e,t,n){try{let r=await fetch("/api/manifests",{method:"DELETE",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({repo:e,digest:t,confirm:!1})});if(r.status===409){let o=await r.json();ge(e,t,n,o.tags)}else if(r.ok)re(n);else{let o=await r.text();alert(`Failed to delete manifest: ${o}`)}}catch(r){console.error("Error deleting manifest:",r),alert(`Error deleting manifest: ${r.message}`)}}function ge(e,t,n,r){let o=document.getElementById("manifest-delete-modal"),i=document.getElementById("manifest-delete-tags"),s=document.getElementById("confirm-manifest-delete-btn");i.innerHTML="",r.forEach(a=>{let l=document.createElement("li");l.textContent=a,i.appendChild(l)}),s.onclick=()=>pe(e,t,n),o.style.display="flex"}function W(){let e=document.getElementById("manifest-delete-modal");e.style.display="none"}async function pe(e,t,n){let r=document.getElementById("confirm-manifest-delete-btn"),o=r.textContent;try{r.disabled=!0,r.textContent="Deleting...";let i=await fetch("/api/manifests",{method:"DELETE",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({repo:e,digest:t,confirm:!0})});if(i.ok)W(),re(n),location.reload();else{let s=await i.text();alert(`Failed to delete manifest: ${s}`),r.disabled=!1,r.textContent=o}}catch(i){console.error("Error deleting manifest:",i),alert(`Error deleting manifest: ${i.message}`),r.disabled=!1,r.textContent=o}}function re(e){let t=document.getElementById(`manifest-${e}`);t&&t.remove()}document.addEventListener("DOMContentLoaded",()=>{let e=document.getElementById("manifest-delete-modal");e&&e.addEventListener("click",t=>{t.target===e&&W()})});async function Ee(e,t){let n=document.getElementById("vuln-detail-modal"),r=document.getElementById("vuln-modal-body");if(!(!n||!r)){r.innerHTML='
',n.showModal();try{let o=await fetch(`/api/vuln-details?digest=${encodeURIComponent(e)}&holdEndpoint=${encodeURIComponent(t)}`);r.innerHTML=await o.text()}catch{r.innerHTML='

Failed to load vulnerability details

'}}}var U=class{constructor(t){this.input=t,this.typeahead=t.closest("actor-typeahead"),this.dropdown=null,this.currentFocus=-1,this.typeaheadClosed=!1,this.init()}init(){this.createDropdown(),this.input.addEventListener("focus",()=>this.handleFocus()),this.input.addEventListener("input",()=>this.handleInput()),this.input.addEventListener("keydown",t=>this.handleKeydown(t)),document.addEventListener("click",t=>{!this.input.contains(t.target)&&!this.dropdown.contains(t.target)&&this.hideDropdown()})}createDropdown(){this.dropdown=document.createElement("div"),this.dropdown.className="recent-accounts-dropdown",this.dropdown.style.display="none",this.typeahead?this.typeahead.insertAdjacentElement("afterend",this.dropdown):this.input.insertAdjacentElement("afterend",this.dropdown)}handleFocus(){this.input.value.trim().length<1&&this.showRecentAccounts()}handleInput(){this.input.value.trim().length>=1&&this.hideDropdown(),this.typeaheadClosed=!1}showRecentAccounts(){let t=this.getRecentAccounts();if(t.length===0){this.hideDropdown();return}this.dropdown.innerHTML="",this.currentFocus=-1;let n=document.createElement("div");n.className="recent-accounts-header",n.textContent="Recent accounts",this.dropdown.appendChild(n),t.forEach((r,o)=>{let i=document.createElement("div");i.className="recent-accounts-item",i.dataset.index=o,i.dataset.handle=r,i.textContent=r,i.addEventListener("click",()=>this.selectItem(r)),this.dropdown.appendChild(i)}),this.dropdown.style.display="block"}selectItem(t){this.input.value=t,this.hideDropdown(),this.input.focus()}hideDropdown(){this.dropdown.style.display="none",this.currentFocus=-1}handleKeydown(t){if(t.key==="Enter"&&this.input.value.trim().length>=2&&(this.typeaheadClosed=!0),t.key==="Tab"&&this.input.value.trim().length>=2&&!this.typeaheadClosed){t.preventDefault();let o=t.shiftKey?"ArrowUp":"ArrowDown",i=new KeyboardEvent("keydown",{key:o,bubbles:!0,cancelable:!0});this.typeahead.dispatchEvent(i);return}if(this.dropdown.style.display==="none")return;let n=this.dropdown.querySelectorAll(".recent-accounts-item");t.key==="ArrowDown"?(t.preventDefault(),this.currentFocus++,this.currentFocus>=n.length&&(this.currentFocus=0),this.updateFocus(n)):t.key==="ArrowUp"?(t.preventDefault(),this.currentFocus--,this.currentFocus<0&&(this.currentFocus=n.length-1),this.updateFocus(n)):t.key==="Enter"&&this.currentFocus>-1&&n[this.currentFocus]?(t.preventDefault(),this.selectItem(n[this.currentFocus].dataset.handle)):t.key==="Escape"&&this.hideDropdown()}updateFocus(t){t.forEach((n,r)=>{n.classList.toggle("focused",r===this.currentFocus)})}getRecentAccounts(){try{let t=localStorage.getItem("atcr_recent_handles");return t?JSON.parse(t):[]}catch{return[]}}saveRecentAccount(t){if(t)try{let n=this.getRecentAccounts();n=n.filter(r=>r!==t),n.unshift(t),n=n.slice(0,5),localStorage.setItem("atcr_recent_handles",JSON.stringify(n))}catch(n){console.error("Failed to save recent account:",n)}}};document.addEventListener("DOMContentLoaded",()=>{let e=document.getElementById("login-form"),t=document.getElementById("handle");e&&t&&new U(t)});document.addEventListener("DOMContentLoaded",()=>{let e=document.cookie.split("; ").find(n=>n.startsWith("atcr_login_handle="));if(!e)return;let t=decodeURIComponent(e.split("=")[1]);if(t){try{let n="atcr_recent_handles",r=JSON.parse(localStorage.getItem(n)||"[]");r=r.filter(o=>o!==t),r.unshift(t),r=r.slice(0,5),localStorage.setItem(n,JSON.stringify(r))}catch(n){console.error("Failed to save recent account:",n)}document.cookie="atcr_login_handle=; path=/; max-age=0"}});function Z(){let e=document.getElementById("featured-carousel"),t=document.getElementById("carousel-prev"),n=document.getElementById("carousel-next");if(!e)return;let r=Array.from(e.querySelectorAll(".carousel-item"));if(r.length===0)return;let o=null,i=5e3,s=0,a=0,l=0;function c(){let b=r[0];if(!b)return;let E=getComputedStyle(e),g=parseFloat(E.gap)||24;s=b.offsetWidth+g,a=e.offsetWidth,l=e.scrollWidth}requestAnimationFrame(()=>{requestAnimationFrame(()=>{c(),m()})});let h;window.addEventListener("resize",()=>{clearTimeout(h),h=setTimeout(c,150)});function u(){return s||c(),s}function f(){return(!a||!s)&&c(),Math.round(a/s)||1}function v(){return(!l||!a)&&c(),l-a}function d(){let b=u(),E=v(),g=e.scrollLeft;g>=E-10?e.scrollTo({left:0,behavior:"smooth"}):e.scrollTo({left:g+b,behavior:"smooth"})}function y(){let b=u(),E=v(),g=e.scrollLeft;g<=10?e.scrollTo({left:E,behavior:"smooth"}):e.scrollTo({left:g-b,behavior:"smooth"})}t&&t.addEventListener("click",()=>{x(),y(),m()}),n&&n.addEventListener("click",()=>{x(),d(),m()});function m(){o||r.length<=f()||(o=setInterval(d,i))}function x(){o&&(clearInterval(o),o=null)}e.addEventListener("mouseenter",x),e.addEventListener("mouseleave",m)}document.addEventListener("DOMContentLoaded",()=>{"requestIdleCallback"in window?requestIdleCallback(Z,{timeout:2e3}):setTimeout(Z,100)});function q(e,t){let n=document.getElementById("toast-container");n||(n=document.createElement("div"),n.id="toast-container",n.className="toast toast-end toast-bottom z-50",document.body.appendChild(n));let r=t==="error"?"alert-error":"alert-success",o=document.createElement("div");o.className=`alert ${r} shadow-lg transition-opacity duration-300`,o.innerHTML=`${e}`,n.appendChild(o),setTimeout(()=>{o.style.opacity="0",setTimeout(()=>o.remove(),300)},3e3)}async function ye(e){try{let t=await fetch(`/api/webhooks/${e}/test`,{method:"POST",credentials:"include"}),n=await t.text();n.includes('class="success"')||t.ok&&!n.includes('class="error"')?q("Test webhook delivered successfully!","success"):q("Test delivery failed \u2014 check the webhook URL","error")}catch{q("Failed to reach server","error")}}window.setTheme=te;window.toggleSearch=fe;window.closeSearch=V;window.copyToClipboard=ne;window.toggleOfflineManifests=he;window.deleteManifest=me;window.closeManifestDeleteModal=W;window.openVulnDetails=Ee;window.showToast=q;window.testWebhook=ye;window.htmx=F; +`;function K(e){return e.cloneNode(!0)}var B=class extends HTMLElement{static tag="actor-typeahead";static define(t=this.tag){this.tag=t;let n=customElements.getName(this);if(n&&n!==t)return console.warn(`${this.name} already defined as <${n}>!`);let r=customElements.get(t);if(r&&r!==this)return console.warn(`<${t}> already defined as ${r.name}!`);customElements.define(t,this)}static{let t=new URL(import.meta.url).searchParams.get("tag")||this.tag;t!=="none"&&this.define(t)}#n=this.attachShadow({mode:"closed"});#r=[];#e=-1;#o=!1;constructor(){super(),this.#n.append(K(G).content),this.#t(),this.addEventListener("input",this),this.addEventListener("focusout",this),this.addEventListener("keydown",this),this.#n.addEventListener("pointerdown",this),this.#n.addEventListener("pointerup",this),this.#n.addEventListener("click",this)}get#i(){let t=Number.parseInt(this.getAttribute("rows")??"");return Number.isNaN(t)?5:t}handleEvent(t){switch(t.type){case"input":this.#a(t);break;case"keydown":this.#s(t);break;case"focusout":this.#l(t);break;case"pointerdown":this.#c(t);break;case"pointerup":this.#u(t);break}}#s(t){switch(t.key){case"ArrowDown":t.preventDefault(),this.#e=Math.min(this.#e+1,this.#i-1),this.#t();break;case"PageDown":t.preventDefault(),this.#e=this.#i-1,this.#t();break;case"ArrowUp":t.preventDefault(),this.#e=Math.max(this.#e-1,0),this.#t();break;case"PageUp":t.preventDefault(),this.#e=0,this.#t();break;case"Escape":t.preventDefault(),this.#r=[],this.#e=-1,this.#t();break;case"Enter":t.preventDefault(),this.#n.querySelectorAll("button")[this.#e]?.dispatchEvent(new PointerEvent("pointerup",{bubbles:!0}));break}}async#a(t){let n=t.target?.value;if(!n){this.#r=[],this.#t();return}let r=this.getAttribute("host")??"https://public.api.bsky.app",o=new URL("xrpc/app.bsky.actor.searchActorsTypeahead",r);o.searchParams.set("q",n),o.searchParams.set("limit",`${this.#i}`);let s=await(await fetch(o)).json();this.#r=s.actors,this.#e=-1,this.#t()}async#l(t){this.#o||(this.#r=[],this.#e=-1,this.#t())}#t(){let t=document.createDocumentFragment(),n=-1;for(let r of this.#r){let o=K(Q).content,i=o.querySelector("button");i&&(i.dataset.handle=r.handle,++n===this.#e&&(i.dataset.active="true"));let s=o.querySelector("img");s&&r.avatar&&(s.src=r.avatar);let a=o.querySelector(".handle");a&&(a.textContent=r.handle),t.append(o)}this.#n.querySelector(".menu")?.replaceChildren(...t.children)}#c(t){this.#o=!0}#u(t){this.#o=!1,this.querySelector("input")?.focus();let n=t.target?.closest("button"),r=this.querySelector("input");!r||!n||(r.value=n.dataset.handle||"",this.#r=[],this.#t())}};function ee(){return localStorage.getItem("theme")||"system"}function le(e){return e==="dark"||e==="light"?e:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function _(){let e=ee(),n=le(e)==="dark";document.documentElement.classList.toggle("dark",n),document.documentElement.setAttribute("data-theme",n?"dark":"light"),ce(e)}function te(e){localStorage.setItem("theme",e),_(),ue()}function ce(e){let t={system:"sun-moon",light:"sun",dark:"moon"};document.querySelectorAll("[data-theme-icon] use").forEach(n=>{n.setAttribute("href",`/icons.svg#${t[e]||"sun-moon"}`)}),document.querySelectorAll(".theme-option").forEach(n=>{let r=n.dataset.value===e,o=n.querySelector(".theme-check");o&&(o.style.visibility=r?"visible":"hidden")})}function ue(){document.querySelectorAll("[data-theme-toggle]").forEach(e=>{let t=e.closest("details");t&&t.removeAttribute("open")})}window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{ee()==="system"&&_()});function fe(){let e=document.querySelector(".nav-search-wrapper"),t=document.getElementById("nav-search-input");!e||!t||(e.classList.toggle("expanded"),e.classList.contains("expanded")&&t.focus())}function V(){let e=document.querySelector(".nav-search-wrapper");e&&e.classList.remove("expanded")}document.addEventListener("DOMContentLoaded",()=>{let e=document.querySelector(".nav-search-wrapper"),t=document.getElementById("nav-search-input");!e||!t||(document.addEventListener("keydown",n=>{if(n.key==="Escape"&&e.classList.contains("expanded")&&V(),n.key==="/"&&!e.classList.contains("expanded")){let r=n.target.tagName;if(r==="INPUT"||r==="TEXTAREA"||n.target.isContentEditable)return;n.preventDefault(),e.classList.add("expanded"),t.focus()}}),document.addEventListener("click",n=>{e.classList.contains("expanded")&&!e.contains(n.target)&&V()}))});function ne(e,t){!t&&typeof event<"u"&&(t=event.target.closest("button")),navigator.clipboard.writeText(e).then(()=>{if(!t)return;let n=t.innerHTML;t.innerHTML=' Copied!',setTimeout(()=>{t.innerHTML=n},2e3)}).catch(n=>{console.error("Failed to copy:",n)})}document.addEventListener("DOMContentLoaded",()=>{document.addEventListener("click",e=>{let t=e.target.closest("button[data-cmd]");if(t){ne(t.getAttribute("data-cmd"),t);return}if(e.target.closest("a, button, input, .cmd"))return;let n=e.target.closest("[data-href]");n&&(window.location=n.getAttribute("data-href"))})});function de(e){let t=Math.floor((new Date-new Date(e))/1e3),n={year:31536e3,month:2592e3,week:604800,day:86400,hour:3600,minute:60,second:1};for(let[r,o]of Object.entries(n)){let i=Math.floor(t/o);if(i>=1)return i===1?`1 ${r} ago`:`${i} ${r}s ago`}return"just now"}function j(){document.querySelectorAll("time[datetime]").forEach(e=>{let t=e.getAttribute("datetime");if(t&&!e.dataset.noUpdate){let n=de(t);e.textContent!==n&&(e.textContent=n)}})}document.addEventListener("DOMContentLoaded",()=>{j(),_(),document.querySelectorAll("[data-theme-menu]").forEach(e=>{e.querySelectorAll(".theme-option").forEach(t=>{t.addEventListener("click",()=>{te(t.dataset.value)})})}),document.addEventListener("click",e=>{let t=e.target.closest("details.dropdown");document.querySelectorAll("details.dropdown[open]").forEach(n=>{n!==t&&n.removeAttribute("open")})})});document.addEventListener("htmx:afterSwap",j);setInterval(j,6e4);function he(){let e=document.getElementById("show-offline-toggle"),t=document.querySelector(".manifests-list");!e||!t||(localStorage.setItem("showOfflineManifests",e.checked),e.checked?t.classList.add("show-offline"):t.classList.remove("show-offline"))}document.addEventListener("DOMContentLoaded",()=>{let e=document.getElementById("show-offline-toggle");if(!e)return;let t=localStorage.getItem("showOfflineManifests")==="true";e.checked=t;let n=document.querySelector(".manifests-list");n&&(t?n.classList.add("show-offline"):n.classList.remove("show-offline"))});async function me(e,t,n){try{let r=await fetch("/api/manifests",{method:"DELETE",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({repo:e,digest:t,confirm:!1})});if(r.status===409){let o=await r.json();ge(e,t,n,o.tags)}else if(r.ok)re(n);else{let o=await r.text();alert(`Failed to delete manifest: ${o}`)}}catch(r){console.error("Error deleting manifest:",r),alert(`Error deleting manifest: ${r.message}`)}}function ge(e,t,n,r){let o=document.getElementById("manifest-delete-modal"),i=document.getElementById("manifest-delete-tags"),s=document.getElementById("confirm-manifest-delete-btn");i.innerHTML="",r.forEach(a=>{let l=document.createElement("li");l.textContent=a,i.appendChild(l)}),s.onclick=()=>pe(e,t,n),o.style.display="flex"}function W(){let e=document.getElementById("manifest-delete-modal");e.style.display="none"}async function pe(e,t,n){let r=document.getElementById("confirm-manifest-delete-btn"),o=r.textContent;try{r.disabled=!0,r.textContent="Deleting...";let i=await fetch("/api/manifests",{method:"DELETE",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({repo:e,digest:t,confirm:!0})});if(i.ok)W(),re(n),location.reload();else{let s=await i.text();alert(`Failed to delete manifest: ${s}`),r.disabled=!1,r.textContent=o}}catch(i){console.error("Error deleting manifest:",i),alert(`Error deleting manifest: ${i.message}`),r.disabled=!1,r.textContent=o}}async function Ee(e){let t=document.getElementById("confirm-untagged-delete-btn"),n=t.textContent;try{t.disabled=!0,t.textContent="Deleting...";let r=await fetch("/api/manifests/untagged",{method:"DELETE",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({repo:e})}),o=await r.json();r.ok?(document.getElementById("untagged-delete-modal").close(),I(`Deleted ${o.deleted} untagged manifest(s)`,"success"),o.deleted>0&&location.reload(),t.disabled=!1,t.textContent=n):(alert(`Failed to delete untagged manifests: ${o.error||"Unknown error"}`),t.disabled=!1,t.textContent=n)}catch(r){console.error("Error deleting untagged manifests:",r),alert(`Error: ${r.message}`),t.disabled=!1,t.textContent=n}}function re(e){let t=document.getElementById(`manifest-${e}`);t&&t.remove()}document.addEventListener("DOMContentLoaded",()=>{let e=document.getElementById("manifest-delete-modal");e&&e.addEventListener("click",t=>{t.target===e&&W()})});async function ye(e,t){let n=document.getElementById("vuln-detail-modal"),r=document.getElementById("vuln-modal-body");if(!(!n||!r)){r.innerHTML='
',n.showModal();try{let o=await fetch(`/api/vuln-details?digest=${encodeURIComponent(e)}&holdEndpoint=${encodeURIComponent(t)}`);r.innerHTML=await o.text()}catch{r.innerHTML='

Failed to load vulnerability details

'}}}var U=class{constructor(t){this.input=t,this.typeahead=t.closest("actor-typeahead"),this.dropdown=null,this.currentFocus=-1,this.typeaheadClosed=!1,this.init()}init(){this.createDropdown(),this.input.addEventListener("focus",()=>this.handleFocus()),this.input.addEventListener("input",()=>this.handleInput()),this.input.addEventListener("keydown",t=>this.handleKeydown(t)),document.addEventListener("click",t=>{!this.input.contains(t.target)&&!this.dropdown.contains(t.target)&&this.hideDropdown()})}createDropdown(){this.dropdown=document.createElement("div"),this.dropdown.className="recent-accounts-dropdown",this.dropdown.style.display="none",this.typeahead?this.typeahead.insertAdjacentElement("afterend",this.dropdown):this.input.insertAdjacentElement("afterend",this.dropdown)}handleFocus(){this.input.value.trim().length<1&&this.showRecentAccounts()}handleInput(){this.input.value.trim().length>=1&&this.hideDropdown(),this.typeaheadClosed=!1}showRecentAccounts(){let t=this.getRecentAccounts();if(t.length===0){this.hideDropdown();return}this.dropdown.innerHTML="",this.currentFocus=-1;let n=document.createElement("div");n.className="recent-accounts-header",n.textContent="Recent accounts",this.dropdown.appendChild(n),t.forEach((r,o)=>{let i=document.createElement("div");i.className="recent-accounts-item",i.dataset.index=o,i.dataset.handle=r,i.textContent=r,i.addEventListener("click",()=>this.selectItem(r)),this.dropdown.appendChild(i)}),this.dropdown.style.display="block"}selectItem(t){this.input.value=t,this.hideDropdown(),this.input.focus()}hideDropdown(){this.dropdown.style.display="none",this.currentFocus=-1}handleKeydown(t){if(t.key==="Enter"&&this.input.value.trim().length>=2&&(this.typeaheadClosed=!0),t.key==="Tab"&&this.input.value.trim().length>=2&&!this.typeaheadClosed){t.preventDefault();let o=t.shiftKey?"ArrowUp":"ArrowDown",i=new KeyboardEvent("keydown",{key:o,bubbles:!0,cancelable:!0});this.typeahead.dispatchEvent(i);return}if(this.dropdown.style.display==="none")return;let n=this.dropdown.querySelectorAll(".recent-accounts-item");t.key==="ArrowDown"?(t.preventDefault(),this.currentFocus++,this.currentFocus>=n.length&&(this.currentFocus=0),this.updateFocus(n)):t.key==="ArrowUp"?(t.preventDefault(),this.currentFocus--,this.currentFocus<0&&(this.currentFocus=n.length-1),this.updateFocus(n)):t.key==="Enter"&&this.currentFocus>-1&&n[this.currentFocus]?(t.preventDefault(),this.selectItem(n[this.currentFocus].dataset.handle)):t.key==="Escape"&&this.hideDropdown()}updateFocus(t){t.forEach((n,r)=>{n.classList.toggle("focused",r===this.currentFocus)})}getRecentAccounts(){try{let t=localStorage.getItem("atcr_recent_handles");return t?JSON.parse(t):[]}catch{return[]}}saveRecentAccount(t){if(t)try{let n=this.getRecentAccounts();n=n.filter(r=>r!==t),n.unshift(t),n=n.slice(0,5),localStorage.setItem("atcr_recent_handles",JSON.stringify(n))}catch(n){console.error("Failed to save recent account:",n)}}};document.addEventListener("DOMContentLoaded",()=>{let e=document.getElementById("login-form"),t=document.getElementById("handle");e&&t&&new U(t)});document.addEventListener("DOMContentLoaded",()=>{let e=document.cookie.split("; ").find(n=>n.startsWith("atcr_login_handle="));if(!e)return;let t=decodeURIComponent(e.split("=")[1]);if(t){try{let n="atcr_recent_handles",r=JSON.parse(localStorage.getItem(n)||"[]");r=r.filter(o=>o!==t),r.unshift(t),r=r.slice(0,5),localStorage.setItem(n,JSON.stringify(r))}catch(n){console.error("Failed to save recent account:",n)}document.cookie="atcr_login_handle=; path=/; max-age=0"}});function Z(){let e=document.getElementById("featured-carousel"),t=document.getElementById("carousel-prev"),n=document.getElementById("carousel-next");if(!e)return;let r=Array.from(e.querySelectorAll(".carousel-item"));if(r.length===0)return;let o=null,i=5e3,s=0,a=0,l=0;function c(){let b=r[0];if(!b)return;let E=getComputedStyle(e),g=parseFloat(E.gap)||24;s=b.offsetWidth+g,a=e.offsetWidth,l=e.scrollWidth}requestAnimationFrame(()=>{requestAnimationFrame(()=>{c(),m()})});let h;window.addEventListener("resize",()=>{clearTimeout(h),h=setTimeout(c,150)});function u(){return s||c(),s}function f(){return(!a||!s)&&c(),Math.round(a/s)||1}function v(){return(!l||!a)&&c(),l-a}function d(){let b=u(),E=v(),g=e.scrollLeft;g>=E-10?e.scrollTo({left:0,behavior:"smooth"}):e.scrollTo({left:g+b,behavior:"smooth"})}function y(){let b=u(),E=v(),g=e.scrollLeft;g<=10?e.scrollTo({left:E,behavior:"smooth"}):e.scrollTo({left:g-b,behavior:"smooth"})}t&&t.addEventListener("click",()=>{x(),y(),m()}),n&&n.addEventListener("click",()=>{x(),d(),m()});function m(){o||r.length<=f()||(o=setInterval(d,i))}function x(){o&&(clearInterval(o),o=null)}e.addEventListener("mouseenter",x),e.addEventListener("mouseleave",m)}document.addEventListener("DOMContentLoaded",()=>{"requestIdleCallback"in window?requestIdleCallback(Z,{timeout:2e3}):setTimeout(Z,100)});function I(e,t){let n=document.getElementById("toast-container");n||(n=document.createElement("div"),n.id="toast-container",n.className="toast toast-end toast-bottom z-50",document.body.appendChild(n));let r=t==="error"?"alert-error":"alert-success",o=document.createElement("div");o.className=`alert ${r} shadow-lg transition-opacity duration-300`,o.innerHTML=`${e}`,n.appendChild(o),setTimeout(()=>{o.style.opacity="0",setTimeout(()=>o.remove(),300)},3e3)}async function ve(e){try{let t=await fetch(`/api/webhooks/${e}/test`,{method:"POST",credentials:"include"}),n=await t.text();n.includes('class="success"')||t.ok&&!n.includes('class="error"')?I("Test webhook delivered successfully!","success"):I("Test delivery failed \u2014 check the webhook URL","error")}catch{I("Failed to reach server","error")}}window.setTheme=te;window.toggleSearch=fe;window.closeSearch=V;window.copyToClipboard=ne;window.toggleOfflineManifests=he;window.deleteManifest=me;window.deleteUntaggedManifests=Ee;window.closeManifestDeleteModal=W;window.openVulnDetails=ye;window.showToast=I;window.testWebhook=ve;window.htmx=q; diff --git a/pkg/appview/routes/routes.go b/pkg/appview/routes/routes.go index 51001ea..29782b8 100644 --- a/pkg/appview/routes/routes.go +++ b/pkg/appview/routes/routes.go @@ -158,6 +158,7 @@ func RegisterUIRoutes(router chi.Router, deps UIDependencies) { r.Get("/settings", (&uihandlers.SettingsHandler{BaseUIHandler: base}).ServeHTTP) r.Get("/api/storage", (&uihandlers.StorageHandler{BaseUIHandler: base}).ServeHTTP) r.Post("/api/profile/default-hold", (&uihandlers.UpdateDefaultHoldHandler{BaseUIHandler: base}).ServeHTTP) + r.Post("/api/profile/auto-remove-untagged", (&uihandlers.UpdateAutoRemoveUntaggedHandler{BaseUIHandler: base}).ServeHTTP) // Subscription management r.Get("/settings/subscription/checkout", (&uihandlers.SubscriptionCheckoutHandler{BaseUIHandler: base}).ServeHTTP) @@ -165,6 +166,7 @@ func RegisterUIRoutes(router chi.Router, deps UIDependencies) { r.Delete("/api/tags", (&uihandlers.DeleteTagHandler{BaseUIHandler: base}).ServeHTTP) r.Delete("/api/manifests", (&uihandlers.DeleteManifestHandler{BaseUIHandler: base}).ServeHTTP) + r.Delete("/api/manifests/untagged", (&uihandlers.DeleteUntaggedManifestsHandler{BaseUIHandler: base}).ServeHTTP) r.Post("/api/avatar", (&uihandlers.UploadAvatarHandler{BaseUIHandler: base}).ServeHTTP) // Webhook management diff --git a/pkg/appview/server.go b/pkg/appview/server.go index a19df95..137b60d 100644 --- a/pkg/appview/server.go +++ b/pkg/appview/server.go @@ -231,9 +231,10 @@ func NewAppViewServer(cfg *Config, branding *BrandingOverrides) (*AppViewServer, // Set global refresher for middleware middleware.SetGlobalRefresher(s.Refresher) - // Set global database for hold DID lookups + // Set global database for hold DID lookups and manifest reference checks holdDIDDB := db.NewHoldDIDDB(s.Database) middleware.SetGlobalDatabase(holdDIDDB) + middleware.SetGlobalManifestRefChecker(holdDIDDB) // Create RemoteHoldAuthorizer for hold authorization with caching s.HoldAuthorizer = auth.NewRemoteHoldAuthorizer(s.Database, testMode) diff --git a/pkg/appview/src/js/app.js b/pkg/appview/src/js/app.js index 8491bda..0406abc 100644 --- a/pkg/appview/src/js/app.js +++ b/pkg/appview/src/js/app.js @@ -352,6 +352,45 @@ async function confirmManifestDelete(repository, digest, sanitizedId) { } } +// Delete all untagged manifests in a repository +async function deleteUntaggedManifests(repository) { + const confirmBtn = document.getElementById('confirm-untagged-delete-btn'); + const originalText = confirmBtn.textContent; + + try { + confirmBtn.disabled = true; + confirmBtn.textContent = 'Deleting...'; + + const response = await fetch('/api/manifests/untagged', { + method: 'DELETE', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ repo: repository }), + }); + + const data = await response.json(); + + if (response.ok) { + document.getElementById('untagged-delete-modal').close(); + showToast(`Deleted ${data.deleted} untagged manifest(s)`, 'success'); + if (data.deleted > 0) { + location.reload(); + } + confirmBtn.disabled = false; + confirmBtn.textContent = originalText; + } else { + alert(`Failed to delete untagged manifests: ${data.error || 'Unknown error'}`); + confirmBtn.disabled = false; + confirmBtn.textContent = originalText; + } + } catch (err) { + console.error('Error deleting untagged manifests:', err); + alert(`Error: ${err.message}`); + confirmBtn.disabled = false; + confirmBtn.textContent = originalText; + } +} + // Remove a manifest element from the DOM function removeManifestElement(sanitizedId) { const element = document.getElementById(`manifest-${sanitizedId}`); @@ -758,6 +797,7 @@ window.closeSearch = closeSearch; window.copyToClipboard = copyToClipboard; window.toggleOfflineManifests = toggleOfflineManifests; window.deleteManifest = deleteManifest; +window.deleteUntaggedManifests = deleteUntaggedManifests; window.closeManifestDeleteModal = closeManifestDeleteModal; window.openVulnDetails = openVulnDetails; window.showToast = showToast; diff --git a/pkg/appview/storage/context.go b/pkg/appview/storage/context.go index 1c7d9fd..ea1d98e 100644 --- a/pkg/appview/storage/context.go +++ b/pkg/appview/storage/context.go @@ -15,6 +15,13 @@ type PushWebhookDispatcher interface { DispatchForPush(ctx context.Context, event PushWebhookEvent) } +// ManifestReferenceChecker checks if a manifest digest is referenced as a child +// of a manifest list (multi-arch image). Used to protect manifest list children +// from auto-removal when their parent list is still tagged. +type ManifestReferenceChecker interface { + IsManifestReferenced(did, digest string) (bool, error) +} + // PushWebhookEvent contains the data needed to dispatch a push webhook. type PushWebhookEvent struct { OwnerDID string @@ -53,10 +60,14 @@ type RegistryContext struct { PullerDID string // Puller's DID - who is making the request (from JWT Subject) PullerPDSEndpoint string // Puller's PDS endpoint URL + // Per-request user preferences + AutoRemoveUntagged bool // Whether to auto-delete untagged manifests on tag overwrite + // Shared services (same for all requests) - Database HoldDIDLookup // Database for hold DID lookups - Authorizer auth.HoldAuthorizer // Hold access authorization - Refresher *oauth.Refresher // OAuth session manager - ReadmeFetcher *readme.Fetcher // README fetcher for repo pages - WebhookDispatcher PushWebhookDispatcher // Push webhook dispatcher (nil if not configured) + Database HoldDIDLookup // Database for hold DID lookups + Authorizer auth.HoldAuthorizer // Hold access authorization + Refresher *oauth.Refresher // OAuth session manager + ReadmeFetcher *readme.Fetcher // README fetcher for repo pages + WebhookDispatcher PushWebhookDispatcher // Push webhook dispatcher (nil if not configured) + ManifestRefChecker ManifestReferenceChecker // Checks if digest is a manifest list child (nil-safe) } diff --git a/pkg/appview/storage/manifest_store.go b/pkg/appview/storage/manifest_store.go index 05394d7..73e4829 100644 --- a/pkg/appview/storage/manifest_store.go +++ b/pkg/appview/storage/manifest_store.go @@ -210,11 +210,25 @@ func (s *ManifestStore) Put(ctx context.Context, manifest distribution.Manifest, // Also handle tag if specified var tag string + var oldDigest string // Track previous digest for auto-remove-untagged cleanup for _, option := range options { if tagOpt, ok := option.(distribution.WithTagOption); ok { tag = tagOpt.Tag - tagRecord := atproto.NewTagRecord(s.ctx.ATProtoClient.DID(), s.ctx.Repository, tag, dgst.String()) tagRKey := atproto.RepositoryTagToRKey(s.ctx.Repository, tag) + + // Before overwriting, capture the old digest so we can clean it up + if s.ctx.AutoRemoveUntagged { + if oldRec, getErr := s.ctx.ATProtoClient.GetRecord(ctx, atproto.TagCollection, tagRKey); getErr == nil { + var oldTag atproto.TagRecord + if json.Unmarshal(oldRec.Value, &oldTag) == nil { + if d, dErr := oldTag.GetManifestDigest(); dErr == nil { + oldDigest = d + } + } + } + } + + tagRecord := atproto.NewTagRecord(s.ctx.ATProtoClient.DID(), s.ctx.Repository, tag, dgst.String()) _, err = s.ctx.ATProtoClient.PutRecord(ctx, atproto.TagCollection, tagRKey, tagRecord) if err != nil { return "", fmt.Errorf("failed to store tag in ATProto: %w", err) @@ -222,6 +236,12 @@ func (s *ManifestStore) Put(ctx context.Context, manifest distribution.Manifest, } } + // Auto-remove untagged manifests: if the tag pointed to a different digest before, + // check if the old manifest is now untagged and clean it up + if s.ctx.AutoRemoveUntagged && oldDigest != "" && oldDigest != dgst.String() { + go s.cleanupUntaggedManifest(oldDigest) + } + // Notify hold about manifest push (for layer tracking, Bluesky posts, and stats) // Do this asynchronously to avoid blocking the push // Note: We notify even for tagless pushes (e.g., buildx platform manifests) to create layer records @@ -697,3 +717,73 @@ func detectImageMimeType(contentType, url string) string { // Unknown or unsupported type - reject return "" } + +// cleanupUntaggedManifest checks if a manifest digest is now untagged and deletes it. +// Runs asynchronously after a tag overwrite. Protects manifest list children. +func (s *ManifestStore) cleanupUntaggedManifest(oldDigest string) { + defer func() { + if r := recover(); r != nil { + slog.Error("Panic in cleanupUntaggedManifest", "panic", r) + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Re-check: list all tags for this repository and see if any still point to the old digest + records, err := s.ctx.ATProtoClient.ListRecords(ctx, atproto.TagCollection, 100) + if err != nil { + slog.Warn("Auto-remove: failed to list tags", "error", err) + return + } + + for _, rec := range records { + var tagRecord atproto.TagRecord + if json.Unmarshal(rec.Value, &tagRecord) != nil { + continue + } + if tagRecord.Repository != s.ctx.Repository { + continue + } + d, dErr := tagRecord.GetManifestDigest() + if dErr == nil && d == oldDigest { + // Still tagged by another tag — do not delete + slog.Debug("Auto-remove: manifest still tagged, skipping", + "digest", oldDigest, "tag", tagRecord.Tag) + return + } + } + + // Check if this digest is a child of a manifest list (multi-arch) + if s.ctx.ManifestRefChecker != nil { + referenced, err := s.ctx.ManifestRefChecker.IsManifestReferenced(s.ctx.DID, oldDigest) + if err != nil { + slog.Warn("Auto-remove: failed to check manifest references", "digest", oldDigest, "error", err) + return + } + if referenced { + slog.Debug("Auto-remove: manifest is a manifest list child, skipping", + "digest", oldDigest) + return + } + } + + // Safe to delete — manifest is untagged and not referenced by any manifest list + dgst, err := digest.Parse(oldDigest) + if err != nil { + slog.Warn("Auto-remove: invalid digest", "digest", oldDigest, "error", err) + return + } + + rkey := digestToRKey(dgst) + if err := s.ctx.ATProtoClient.DeleteRecord(ctx, atproto.ManifestCollection, rkey); err != nil { + slog.Warn("Auto-remove: failed to delete untagged manifest", "digest", oldDigest, "error", err) + return + } + + slog.Info("Auto-removed untagged manifest", + "component", "manifest-store", + "digest", oldDigest, + "repository", s.ctx.Repository, + "did", s.ctx.DID) +} diff --git a/pkg/appview/templates/pages/repository.html b/pkg/appview/templates/pages/repository.html index b6e0439..fb32ecc 100644 --- a/pkg/appview/templates/pages/repository.html +++ b/pkg/appview/templates/pages/repository.html @@ -200,10 +200,19 @@

Manifests

- +
+ {{ if $.IsOwner }} + + {{ end }} + +
{{ if .Manifests }}
@@ -313,6 +322,23 @@ + + + + + + {{ end }} + + +
+

Storage Preferences

+ +
diff --git a/pkg/atproto/lexicon.go b/pkg/atproto/lexicon.go index 67b538d..9ff8059 100644 --- a/pkg/atproto/lexicon.go +++ b/pkg/atproto/lexicon.go @@ -335,6 +335,11 @@ type SailorProfileRecord struct { // If null/empty, user has opted out of defaults DefaultHold string `json:"defaultHold,omitempty"` + // AutoRemoveUntagged controls whether untagged manifests are automatically + // cleaned up. When true, manifests that lose all tags (e.g., after a tag + // overwrite) are deleted from PDS, and their layers are cleaned up by hold GC. + AutoRemoveUntagged bool `json:"autoRemoveUntagged,omitempty"` + // CreatedAt timestamp CreatedAt time.Time `json:"createdAt"` diff --git a/pkg/atproto/relays.go b/pkg/atproto/relays.go index 6755fb4..92c9f53 100644 --- a/pkg/atproto/relays.go +++ b/pkg/atproto/relays.go @@ -35,6 +35,7 @@ var KnownRelays = []KnownRelay{ {Name: "Feeds Blue", URL: "https://relay.feeds.blue"}, {Name: "Waow", URL: "https://relay.waow.tech"}, {Name: "Zlay", URL: "https://zlay.waow.tech"}, + {Name: "Bassh", URL: "https://relay.bas.sh"}, } // RelayHTTPError indicates the relay responded with a non-200 status code. diff --git a/pkg/hold/gc/gc.go b/pkg/hold/gc/gc.go index af494ba..dbdd72d 100644 --- a/pkg/hold/gc/gc.go +++ b/pkg/hold/gc/gc.go @@ -578,13 +578,38 @@ func (gc *GarbageCollector) analyzeRecords(ctx context.Context) (*analysisResult result.usersChecked++ gc.setProgress("manifests", fmt.Sprintf("Fetching manifests (%d/%d users)", result.usersChecked, totalUsers), gc.operationType) - manifests, err := gc.fetchUserManifests(ctx, did) + // Resolve PDS endpoint (needed for both manifests and optional profile/tag fetches) + pdsEndpoint, err := atproto.ResolveDIDToPDS(ctx, did) + if err != nil { + gc.logger.Warn("Failed to resolve PDS for user, treating their records as referenced", + "did", did, "error", err) + continue + } + + manifests, err := gc.fetchUserManifestsFromEndpoint(ctx, did, pdsEndpoint, gc.pds.DID()) if err != nil { gc.logger.Warn("Failed to fetch manifests for user, treating their records as referenced", "did", did, "error", err) continue } + // If user opted into auto-remove-untagged, filter out untagged manifests + // so their layers are treated as orphaned and cleaned up + if profile, _ := gc.fetchUserProfile(ctx, pdsEndpoint, did); profile != nil && profile.AutoRemoveUntagged { + taggedDigests, tagErr := gc.fetchUserTags(ctx, pdsEndpoint, did) + if tagErr != nil { + gc.logger.Warn("Failed to fetch tags for auto-remove, keeping all manifests referenced", + "did", did, "error", tagErr) + } else { + before := len(manifests) + manifests = gc.filterUntaggedManifests(manifests, taggedDigests) + if filtered := before - len(manifests); filtered > 0 { + gc.logger.Info("Filtered untagged manifests for auto-remove user", + "did", did, "filtered", filtered, "remaining", len(manifests)) + } + } + } + fetchedUsers[did] = true for _, m := range manifests { result.manifestsChecked++ @@ -835,18 +860,153 @@ func (gc *GarbageCollector) discoverUserDIDs(ctx context.Context) ([]string, err return dids, nil } -// fetchUserManifests fetches all io.atcr.manifest records for a user from their PDS, -// filtered to manifests that reference this hold. -func (gc *GarbageCollector) fetchUserManifests(ctx context.Context, userDID string) ([]*manifestInfo, error) { - pdsEndpoint, err := atproto.ResolveDIDToPDS(ctx, userDID) +// fetchUserProfile fetches the sailor profile from a user's PDS. +// Returns nil without error if the profile doesn't exist. +func (gc *GarbageCollector) fetchUserProfile(ctx context.Context, pdsEndpoint, userDID string) (*atproto.SailorProfileRecord, error) { + client := &http.Client{Timeout: 10 * time.Second} + + reqURL := fmt.Sprintf("%s/xrpc/com.atproto.repo.getRecord?repo=%s&collection=%s&rkey=self", + pdsEndpoint, userDID, atproto.SailorProfileCollection) + + req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil) if err != nil { - return nil, fmt.Errorf("resolve PDS for %s: %w", userDID, err) + return nil, fmt.Errorf("create request: %w", err) + } + + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("http request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusNotFound { + return nil, nil // No profile + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("getRecord returned status %d", resp.StatusCode) + } + + var envelope struct { + Value json.RawMessage `json:"value"` + } + if err := json.NewDecoder(resp.Body).Decode(&envelope); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + var profile atproto.SailorProfileRecord + if err := json.Unmarshal(envelope.Value, &profile); err != nil { + return nil, fmt.Errorf("unmarshal profile: %w", err) + } + + return &profile, nil +} + +// fetchUserTags fetches all tag records from a user's PDS. +// Returns a map of digest → true for all tagged manifest digests. +func (gc *GarbageCollector) fetchUserTags(ctx context.Context, pdsEndpoint, userDID string) (map[string]bool, error) { + tagged := make(map[string]bool) + cursor := "" + client := &http.Client{Timeout: 30 * time.Second} + + for { + reqURL := fmt.Sprintf("%s/xrpc/com.atproto.repo.listRecords?repo=%s&collection=%s&limit=100", + pdsEndpoint, userDID, atproto.TagCollection) + if cursor != "" { + reqURL += "&cursor=" + cursor + } + + req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("http request: %w", err) + } + + if resp.StatusCode != http.StatusOK { + resp.Body.Close() + return nil, fmt.Errorf("listRecords returned status %d", resp.StatusCode) + } + + var listResult struct { + Records []struct { + Value json.RawMessage `json:"value"` + } `json:"records"` + Cursor string `json:"cursor,omitempty"` + } + + if err := json.NewDecoder(resp.Body).Decode(&listResult); err != nil { + resp.Body.Close() + return nil, fmt.Errorf("decode response: %w", err) + } + resp.Body.Close() + + for _, rec := range listResult.Records { + var tag atproto.TagRecord + if err := json.Unmarshal(rec.Value, &tag); err != nil { + continue + } + if d, err := tag.GetManifestDigest(); err == nil { + tagged[d] = true + } + } + + if listResult.Cursor == "" { + break + } + cursor = listResult.Cursor + } + + return tagged, nil +} + +// filterUntaggedManifests removes untagged manifests from the list, preserving +// manifest list children that are referenced by tagged manifest lists. +func (gc *GarbageCollector) filterUntaggedManifests(manifests []*manifestInfo, taggedDigests map[string]bool) []*manifestInfo { + // Build a set of digests that are children of tagged manifest lists + childDigests := make(map[string]bool) + for _, m := range manifests { + // Check if this manifest is a manifest list AND is tagged + mDigest := extractDigestFromManifestURI(m.URI) + if mDigest == "" || !taggedDigests[mDigest] { + continue + } + // This is a tagged manifest list — protect its children + for _, ref := range m.Record.Manifests { + childDigests[ref.Digest] = true + } + } + + // Filter: keep manifests that are tagged, or are children of tagged manifest lists + var kept []*manifestInfo + for _, m := range manifests { + mDigest := extractDigestFromManifestURI(m.URI) + if mDigest == "" { + kept = append(kept, m) + continue + } + if taggedDigests[mDigest] || childDigests[mDigest] { + kept = append(kept, m) + } + } + return kept +} + +// extractDigestFromManifestURI extracts the digest from a manifest AT-URI. +// URI format: at://did:plc:xxx/io.atcr.manifest/{encoded_digest} +// Returns the full digest (e.g., "sha256:abc123...") or empty string. +func extractDigestFromManifestURI(uri string) string { + parts := parseATURI(uri) + if parts == nil || parts.Collection != atproto.ManifestCollection { + return "" } - return gc.fetchUserManifestsFromEndpoint(ctx, userDID, pdsEndpoint, gc.pds.DID()) + // The rkey is the digest without the "sha256:" prefix + return "sha256:" + parts.Rkey } // fetchUserManifestsFromEndpoint fetches manifests from a specific PDS endpoint. -// Separated from fetchUserManifests for testability (avoids DID resolution). func (gc *GarbageCollector) fetchUserManifestsFromEndpoint(ctx context.Context, userDID, pdsEndpoint, holdDID string) ([]*manifestInfo, error) { var manifests []*manifestInfo cursor := "" diff --git a/pkg/hold/gc/gc_test.go b/pkg/hold/gc/gc_test.go index db91a8c..6503e4e 100644 --- a/pkg/hold/gc/gc_test.go +++ b/pkg/hold/gc/gc_test.go @@ -451,6 +451,306 @@ func TestFetchUserManifests_PDSError(t *testing.T) { } } +func TestExtractDigestFromManifestURI(t *testing.T) { + tests := []struct { + name string + uri string + expected string + }{ + { + name: "valid manifest URI", + uri: "at://did:plc:user1/io.atcr.manifest/abc123def456", + expected: "sha256:abc123def456", + }, + { + name: "wrong collection", + uri: "at://did:plc:user1/io.atcr.tag/abc123def456", + expected: "", + }, + { + name: "invalid URI", + uri: "not-an-at-uri", + expected: "", + }, + { + name: "empty URI", + uri: "", + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := extractDigestFromManifestURI(tt.uri) + if got != tt.expected { + t.Errorf("extractDigestFromManifestURI(%q) = %q, want %q", tt.uri, got, tt.expected) + } + }) + } +} + +func TestFilterUntaggedManifests(t *testing.T) { + gc := &GarbageCollector{logger: newTestLogger()} + + t.Run("keeps tagged manifests, removes untagged", func(t *testing.T) { + manifests := []*manifestInfo{ + {URI: "at://did:plc:u1/io.atcr.manifest/aaa111", Record: &atproto.ManifestRecord{}}, + {URI: "at://did:plc:u1/io.atcr.manifest/bbb222", Record: &atproto.ManifestRecord{}}, + {URI: "at://did:plc:u1/io.atcr.manifest/ccc333", Record: &atproto.ManifestRecord{}}, + } + tagged := map[string]bool{ + "sha256:aaa111": true, + "sha256:ccc333": true, + } + + result := gc.filterUntaggedManifests(manifests, tagged) + if len(result) != 2 { + t.Fatalf("expected 2 kept, got %d", len(result)) + } + if result[0].URI != manifests[0].URI || result[1].URI != manifests[2].URI { + t.Errorf("unexpected manifests kept: %v, %v", result[0].URI, result[1].URI) + } + }) + + t.Run("preserves manifest list children", func(t *testing.T) { + // Manifest list (tagged) references child (untagged) + manifests := []*manifestInfo{ + { + URI: "at://did:plc:u1/io.atcr.manifest/index111", + Record: &atproto.ManifestRecord{ + Manifests: []atproto.ManifestReference{ + {Digest: "sha256:child222", MediaType: "application/vnd.oci.image.manifest.v1+json"}, + {Digest: "sha256:child333", MediaType: "application/vnd.oci.image.manifest.v1+json"}, + }, + }, + }, + {URI: "at://did:plc:u1/io.atcr.manifest/child222", Record: &atproto.ManifestRecord{}}, + {URI: "at://did:plc:u1/io.atcr.manifest/child333", Record: &atproto.ManifestRecord{}}, + {URI: "at://did:plc:u1/io.atcr.manifest/orphan444", Record: &atproto.ManifestRecord{}}, + } + tagged := map[string]bool{ + "sha256:index111": true, // Only the index is tagged + } + + result := gc.filterUntaggedManifests(manifests, tagged) + if len(result) != 3 { + t.Fatalf("expected 3 kept (index + 2 children), got %d", len(result)) + } + // orphan444 should be filtered out + for _, m := range result { + if extractDigestFromManifestURI(m.URI) == "sha256:orphan444" { + t.Error("orphan manifest should have been filtered out") + } + } + }) + + t.Run("removes everything when nothing is tagged", func(t *testing.T) { + manifests := []*manifestInfo{ + {URI: "at://did:plc:u1/io.atcr.manifest/aaa111", Record: &atproto.ManifestRecord{}}, + {URI: "at://did:plc:u1/io.atcr.manifest/bbb222", Record: &atproto.ManifestRecord{}}, + } + tagged := map[string]bool{} + + result := gc.filterUntaggedManifests(manifests, tagged) + if len(result) != 0 { + t.Fatalf("expected 0 kept, got %d", len(result)) + } + }) + + t.Run("keeps everything when all are tagged", func(t *testing.T) { + manifests := []*manifestInfo{ + {URI: "at://did:plc:u1/io.atcr.manifest/aaa111", Record: &atproto.ManifestRecord{}}, + {URI: "at://did:plc:u1/io.atcr.manifest/bbb222", Record: &atproto.ManifestRecord{}}, + } + tagged := map[string]bool{ + "sha256:aaa111": true, + "sha256:bbb222": true, + } + + result := gc.filterUntaggedManifests(manifests, tagged) + if len(result) != 2 { + t.Fatalf("expected 2 kept, got %d", len(result)) + } + }) +} + +func TestFetchUserProfile(t *testing.T) { + gc := &GarbageCollector{logger: newTestLogger()} + + t.Run("returns profile with autoRemoveUntagged", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]any{ + "value": map[string]any{ + "$type": "io.atcr.sailor.profile", + "defaultHold": "did:web:hold.example.com", + "autoRemoveUntagged": true, + "createdAt": "2025-01-01T00:00:00Z", + }, + }) + })) + defer server.Close() + + profile, err := gc.fetchUserProfile(t.Context(), server.URL, "did:plc:test") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if profile == nil { + t.Fatal("expected non-nil profile") + } + if !profile.AutoRemoveUntagged { + t.Error("expected AutoRemoveUntagged to be true") + } + if profile.DefaultHold != "did:web:hold.example.com" { + t.Errorf("expected DefaultHold %q, got %q", "did:web:hold.example.com", profile.DefaultHold) + } + }) + + t.Run("returns nil for 404", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer server.Close() + + profile, err := gc.fetchUserProfile(t.Context(), server.URL, "did:plc:test") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if profile != nil { + t.Error("expected nil profile for 404") + } + }) + + t.Run("returns error for server failure", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer server.Close() + + _, err := gc.fetchUserProfile(t.Context(), server.URL, "did:plc:test") + if err == nil { + t.Fatal("expected error for 500 response") + } + }) +} + +func TestFetchUserTags(t *testing.T) { + gc := &GarbageCollector{logger: newTestLogger()} + + t.Run("returns tagged digests", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]any{ + "records": []map[string]any{ + { + "uri": "at://did:plc:user1/io.atcr.tag/myapp_latest", + "cid": "bafyrei1", + "value": map[string]any{ + "$type": "io.atcr.tag", + "repository": "myapp", + "tag": "latest", + "manifest": "at://did:plc:user1/io.atcr.manifest/abc123", + }, + }, + { + "uri": "at://did:plc:user1/io.atcr.tag/myapp_v1", + "cid": "bafyrei2", + "value": map[string]any{ + "$type": "io.atcr.tag", + "repository": "myapp", + "tag": "v1", + "manifestDigest": "sha256:def456", + }, + }, + }, + }) + })) + defer server.Close() + + tagged, err := gc.fetchUserTags(t.Context(), server.URL, "did:plc:user1") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(tagged) != 2 { + t.Fatalf("expected 2 tagged digests, got %d", len(tagged)) + } + if !tagged["sha256:abc123"] { + t.Error("expected sha256:abc123 to be tagged") + } + if !tagged["sha256:def456"] { + t.Error("expected sha256:def456 to be tagged") + } + }) + + t.Run("handles pagination", func(t *testing.T) { + callCount := 0 + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + callCount++ + w.Header().Set("Content-Type", "application/json") + if callCount == 1 { + json.NewEncoder(w).Encode(map[string]any{ + "records": []map[string]any{ + { + "uri": "at://did:plc:user1/io.atcr.tag/myapp_latest", + "cid": "bafyrei1", + "value": map[string]any{ + "$type": "io.atcr.tag", + "repository": "myapp", + "tag": "latest", + "manifestDigest": "sha256:aaa111", + }, + }, + }, + "cursor": "page2", + }) + } else { + json.NewEncoder(w).Encode(map[string]any{ + "records": []map[string]any{ + { + "uri": "at://did:plc:user1/io.atcr.tag/myapp_v2", + "cid": "bafyrei2", + "value": map[string]any{ + "$type": "io.atcr.tag", + "repository": "myapp", + "tag": "v2", + "manifestDigest": "sha256:bbb222", + }, + }, + }, + }) + } + })) + defer server.Close() + + tagged, err := gc.fetchUserTags(t.Context(), server.URL, "did:plc:user1") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if callCount != 2 { + t.Errorf("expected 2 HTTP calls for pagination, got %d", callCount) + } + if len(tagged) != 2 { + t.Fatalf("expected 2 tagged digests, got %d", len(tagged)) + } + }) + + t.Run("returns empty for no tags", func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]any{"records": []any{}}) + })) + defer server.Close() + + tagged, err := gc.fetchUserTags(t.Context(), server.URL, "did:plc:user1") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(tagged) != 0 { + t.Errorf("expected 0 tagged digests, got %d", len(tagged)) + } + }) +} + func newTestLogger() *slog.Logger { return slog.Default().With("component", "gc-test") } diff --git a/themes/seamark/embed.go b/themes/seamark/embed.go index 14a454f..4114cf3 100644 --- a/themes/seamark/embed.go +++ b/themes/seamark/embed.go @@ -13,6 +13,9 @@ import ( //go:embed public var publicFS embed.FS +//go:embed templates +var templatesFS embed.FS + //go:embed theme.css var themeCSS string @@ -29,8 +32,9 @@ func branding() *appview.BrandingOverrides { } return &appview.BrandingOverrides{ - PublicFS: prefixFS{sub: pubSub}, - ExtraCSS: themeCSS, + PublicFS: prefixFS{sub: pubSub}, + TemplatesFS: templatesFS, + ExtraCSS: themeCSS, } } diff --git a/themes/seamark/public/apple-touch-icon.png b/themes/seamark/public/apple-touch-icon.png index 53ec455db0a76daed044daf3d8cbd38c9b4369bc..1b0909a0428a3b7c8cc331fdb8912e34a7d97a5c 100644 GIT binary patch literal 3394 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 diff --git a/themes/seamark/public/favicon-96x96.png b/themes/seamark/public/favicon-96x96.png index 3650661e18144cfa8685c61f89a580ad999b9870..efcfb250a7fd085701fba90ee08f0cdfdd4a52a8 100644 GIT binary patch delta 2071 zcmeyYwpd_-ay|PiPZ!6KinzCP>T_nhOCH~U{W1{w-TSbGC{7EYsIf zPJ*+{N^i?2=g6`MLr zg=MK62Lo;$^}M;Mt8;B`#vi$=%Vj1eesRC=U7vpR`@}_0`m5*H@2|ToR{N4!{4!=^n zH-kIwz{{L(W(_itMF7+U{#avm_{3&*;J$IQ{c1~9J_Z5BrDyRMU*pRi zpuepC#@!z~?Mk+6fBJTnhyi;mM@4mw%_QXUEw}| zpDlgEB6A|;Rd4qXHNmnED*m}#3oPXxavHiua(eKj?Q>+4V_P`QyqHVy+Lw|;XDzu- zIXV1a_@zo?3%f&)`N!**gt`?(o>XlrNaWsObpF3Y%kH;6e?q1Hlx9SiF^9ikwPxb8 zsZXh2%=oySW!2+T7j%Azg)UgWd#?7MCgFs8DK}Z_*d7SS%-q;;dg+4l((SF2enti` zv9Vp2KPhSA(V`F@rjsPsu|9hDsic>Zh9$nb8JvIqpPB9a^GHG(yJ3wu!~aKjl)4r@ zSZpqC&m@s7ym7;}6G^4UN-+m6uMF`$y5#lqXZv{l-Rrv!zW$nUcGbBgy}3<{|NAnv zuJ3CLU}Qd7lFcWfq*NUzuRB32UNx-rY5li)J&6LdWh)o%Jax9sfU)z*L|ztU#zkK9 z_&z*XAKmlq+O$Q^1`qTNePaVl7zF*@<*m!k%(eag?>j?H>+*+X3)q-5`ab5kZ@B+@ z*0#Rox~j5_%qv6cCmX)B;(3rZ&+WFWw@p_-@W1Wx&zcN*jvac{w)524ZcU9sjmzF# z-i#qN{khMMoeK*IkF0C_R3$O*!r@ofD`tgG5}e?XwvXijr_z~U=h8I|r!UyMI=pGc z4-tXG&KEai`7L)1u6ZkV_GjL5(dpx)N9$L+R%aW>&-JO_>CMk>Y~v^+ zY%sykT;l)2#SB#yQZGcj9|y8nKC2hzcMK2x%HuCBD`+s`PiS~wkvr#uC}r!Dw`TA9 z;deyBdU^X+)jhdeVh=PPJd-YQc=I0KVj=5o3mKP`_--xXvz6Y)^`hSHuZF|@GNa0} z3m3PEeBEjqvrrZAf z$LGJ#*$E%ZaMzK!mFxb$OU%H);!ELS1HR@1TV7ChA6nYE)~9E^GW`Dk=R*0HQ7=~iDbx$z(C{y%O21>n zBSRj#KJQ8m-d}0xe<0vt?{TI^!+R0o8+8uY=$wyDXcukWu`S-av+}{Ei??{zPq+U$ z{dwJ+uGXvhH*d0gGYPF+th=*YSbyhTy?JLpPpTIRn=R0vwe9N3hQ#^D@7}y|r@e>P)c+0K|le(TmG+2!4k_@KO~HD_kgy2ME$uX@{@j<4NzRBLlz!`{E` za^FJ3-ffav6EY+EyL+3=C&R@J`_A5$Q$OU~s&dlv-R|#sYu@d=cv5TK;h2va zExLie#U;swvp{NQalWw>I6K?+Plig9zWtA*LF)gPNWb%aSa6whx%NMY1Ix{AKU6Ax z6IQzM`CT8cPW-hd|J)tyrdLDFTsAW#M=Cv?xI*k!`ob-`H460)e7@z#C+|G&5b~$l zNIotufTLPz)vwb*<&9_7m#6de3tv1H6dq@F!^kZ%ntwn literal 4979 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 literal 15086 zcmZQzU}RusFfaho3Jfb$85qnM7#I{3pnL%ahI^_E3<3fWeg+EzLz@``g9ZZwg8>5r zLjnUtoB;QUWtcU#2&+Z#jvUs!1S|JY>B|A+gO{~zjA`hTcb5ln;lr)HV_zq-ov|Lsko z|L^aN`+s|L$p6brUH%`NtOZfu2U4%}|4^UO|HJ(%|If~~`hRtm&;P4y{K5Fz8o&Q% z=9q)!K<0tX2dV8-{(p3$`u_`y9sl249|*_S*ZTcGGus5L9%Lq5eZR{8)3YINxwAR! z|J|*T|L^aJ1-t3NuK52CcPIY8w%Y6enK`EaPtP*`e`c=P|7)wg!Qu~gC4kl4-x2r! z&bGM!m)1r9zqKXy|CJS9|If~|0;@g0(C+{Bbs_(cFAe{{ti$X7!e)0c-ZCu^tp57? z(Ek?}IsHE~+wA|jc~<`~FL(ccb5qp+LkmN}W`e>7 zA?QCy4x|q3?j5oJk5AS8e|~}W|7)v#zn8a9-@hR2|BY>l|98y_{@;*h^FO#S=D)?vssAlzPJ`g-lmAC& z2K;ZzwEn+olK=n9n_~ap*c$(TO`re&!bsEqo|U=(CzaUzKeNE@|JBuA;P?f*Z)e

sJA*y}K>u|Lsj-;PicF4k*pR(mymk_bLBBK1KWgrNxf_?`#hL-Dib6+_yi$G1u+S|Jkp8{Lg;)6&Q0IdikGg|D%7N6**rEJe8bsJyhKC zJk{psd8s~YjMn?SxX$t0vPP%xOX{7zO~^F+QtYSxAXmCI#0OJD z37uxlz?$!+8ky&*cH6zB{J-VQY5%QYC7Bv-pu~(UdwGfEDBHgx_rL$Y@be#j_h0@Dc0Z`R zajPx)hb?bW;udoC=6kB%6Fz(Y|An7_{|jR&FJW~6>$#5*_aAxlKify=6S!$iuBBAe zm+z^%SZC$B|8qY5`S0=k7tT0fJqf9Exeq=5pXaH%gNja|$f!Jbl?>;Oy8o%KfBmm} z_Xn%{*^%ljnT<#O=Xt2rQ)DR>HD`M&=|-jb|1)^<^Z%Bw*z+*kZA95)HetelP}xpJ zCs1k>W1g4#JD!{G|KI=p??2}Q%yN|N+E;Me2eotJV;%pbxd`%8YAH4K=X}p8EiHztHZh z|MLeyy@l@Qe0ODspyK#{elLFgpZXCs4};swCm>~y)`C@oqTWJxzlRC~dsd+SFR2IL z{-6ATT93iX9#FmIU6DO#>Me9X;{RbUfBjc}gyjB< zkg`X9&3;h5+c;R;f3~0A|1^6^k-_9%bhm)|R6^$;poBl?F-Z6)M%n!X z_1n<(3?_cQr|Q1&?69w4`BDFaN|OHvmZto7ZK(L~)Li#1+ehcwU}-aBgxUZ9j12$( zvqvO5iRXGL$HpbO{}RBHCvdwar)sD;z{LgmzGkBcGY~sZKxo*lOb>9JCBh4toMF~v)%tu6=K1_nd7Avljo$m60PK;<8>L)4+uAE4ssG%oW#@I%aj(VzSw z{D1YR^#A`5K6PkNLsx-;fx!XXu?DfA0g6VQnrZa&*c9!bhx(L%9_&^8d9X+E=b>ID zF#qIC!=ERn>4DiWIhg#xUZtN$CTadWG2QUz;R)(sJqLRfeu6Ma|FNk$KS5?6ouq-P zzgO|+p+2RbFnQ408PK|)n;U}upPOe1UK4R?x%2cX(=-hJ8|JQb;{J*z74s6Da zbphaYNuV`S;Pn>k{K0F7Kz4)HU4g_w>xe*n7{0#B`~T^gM*lao82oqbtpA@~AOC+| ztJnYeZQlQDYC`@ybvOQ3+r0C?+NK@$2mlk;bpIYPoe_or<|Lp3R{{}M`{9oN{ z{{PfWqyLweyMos+!ORBnL3SRUp!War3eW#nR=EE^I$87oiD`QOPfXYUe`KO6Sl{`@ zj{hri9KmC@ps{Az184qA9=U`u2LPIf1j$Pr1&vEQ0{87fW75&J8UN2Nu={^xqT2rx z({%rzoMHI?$OKjJ`kqTmo&R56?)iU9zsILbYa)N#-WK-{wC3yL66gQd*9U>t#r)Yj z-S2Bkme0@nH$VT2KK%Kg9}DI}9#2M^i(@FxU17WrcAHnI) zuBG8KvF4GgF4s$Kp2LHW|3hD5nFl-b;XmuqH~&*YOt+9~F0txC>nhZ@@BiQO0c{@Z z{?Grc$KL+uJo5BEXgvu{(@S2{W^&)Z9`t#%dq2Q)fD&6y{mb)FqUl&nKyl*7 zo)5pl>*GN24#I3VAalS*Q)m4KjSrFPFH+Q}h8S&$d->zP=~Kjd2DU4ZwC|svK=b)q zQ2bh+y7oWo4I&M)o(HY}cnzAv=_kc6-`)XtKX4p z9;y2BJyqv`*4u*C_LJBG){U^j-PSX46_Jn&+u9NoCFU|Dg4u1)i#NybI%`8E)NTC&?@_6th0~6V3YI zXG^}PYO3t^qrV0A-T9a2rB=am|3@*){a;yR7)iR8|NmK?s?w!XLrnK2M_Rp#jkUL7 zxbmNkbc4v#!FvB^B*Vj>+6>SCi7`C=FURokyAtdDpGoAIM!qi4oD%bc-#+Am#;SXdC8ukDUWgz1p^8f$CFiafA2Mu$8hBRCeuYj(>LbYyH_Z zQTNZW1x|m?&A0kwAs6RlFP^a z(_B6+sx1Jm6+!R6E>=>dGJ+z!UYcHwnIsRV(ibeKc|F1J|>4VTz4|`C56x3e? zt-S&1#fIaeZBu-neg}^SfY;KUeD^;%*{=|rT5Mtk-f~LOm+$-ox8d*q_|Jai#lIXk zc~F#KQ;t~t>sc#w&funrC6oHvcl?h z?|=T!apB#6>yDO1SQX(B%<|Rpu$t2QM{Duwzez!Q!FZHnmwNI~)^NtGONuLZJOJ0l z*j3>cee_S-p}8X3wzDpp;mJQ)+zRo?y!p?^@cut28{ttje558YGBEs*hu}Z<3=G)t Jzk1ZMVgSl|2@U`N diff --git a/themes/seamark/public/favicon.svg b/themes/seamark/public/favicon.svg index c4d9e17..2900bc0 100644 --- a/themes/seamark/public/favicon.svg +++ b/themes/seamark/public/favicon.svg @@ -1,51 +1 @@ - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/themes/seamark/public/static/seamark_seagull.svg b/themes/seamark/public/static/seamark_seagull.svg new file mode 100644 index 0000000..7390a4b --- /dev/null +++ b/themes/seamark/public/static/seamark_seagull.svg @@ -0,0 +1,34 @@ + + + + diff --git a/themes/seamark/public/static/seamark_seagull_buoy.png b/themes/seamark/public/static/seamark_seagull_buoy.png new file mode 100644 index 0000000000000000000000000000000000000000..262bc8681ec7dabf000b59b5e312b2229780a535 GIT binary patch literal 62082 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

    n* z^TEaO{ieY{%FodB1NK`nZUm|ID}K&BNWG)O11jNV|OPgxH7*`}!|8 zPP0BvoHpfLOsOzAoRbY3Fz(#H-8RI$OaAuiSd<-{0S_>-=BeslEODeAW4FYs^#Eb1N;F z9cyvEX2#svvrq4@c~jBYk=9wFH`&X&i(}pMMv=o|A+hUMX8RX3HS8-5z0S4r^)AqO zNZ&-miny)Y)-@THXDd#)@?J`Vb^d7={&6@uH`PZ6@j;~*yy{p@HeNlM!-?lfUjw~g@gt<2P0~YS8w&rYPLDw?8+s#V_EmLO*%b&eMwnaT$3k1K9OS7kiKYRpsw!EStgQe>rQob zy!fWnqxUy?ZAjd~^Z(wQf0SmJ}!yL^I~5s@-3CvF1;m(wy1mr`FuRt~HB0dS!^goU;$S z<9`Y1%v~khy4{oYO7r}kfjytk{P}HMU1qE8-lV`}#c*d=t@`If6UFNu{`e^T|G~4f zv+sK>H@2P?7gN0|znsw;J2@M_Z zpPF%Hi{IUeW7YLKTl%p!ciC=^8w$@gl$uWeW4#sZZ`&H9$6mH~+V2_H7G|!P*s#v1 zH9Dn&UFWpcn>puB{_%HYDN){8_c-j^&*$^&AKh4e{QtkZyUXhj`zQ$Pl{i>^tl^=fX6r*AvB{Gs-`H6!byJbOYt5_^SIj4y z^X}G~z4L^M`i?C^exA4UY#DjK>l!b9687PQa{q(k`M+hfrgDjF7rW=Hwjf;MUcKSw z>ht_hG6Y;4%)Kn~@13x#|55Po$4BAT+uQTcTb;YGCHHn;P*;y3gRsDrNsqk!cKOaX zGTw5nj!RcU{ixjG4eY0bBqSvr^`;;G@x!96ZH|KLoBR9>9$PE;*UailGFl{h{`T73 z+v4j~%VNcqm6d-SjsN2sF^%FVQt@@mT$^b1FaHD>qx49KjhJ-_bmB^#BFhIQuC z_2SRUSr)1MKh+t|?d|QoSyO$k_w7DmagE2Uj}}{XUyABX|5Gj#x=MuS_gnXM;cZQA zshU1MJ_$)lOp_;1KC$Ey!ynfV7au1tG;OBH`MlXZEoakmpT7^kuKoDqarxuQ{PtZzD?{e*I*G& zOFnW&D!25!gn(v%!L>8IEJh;5Ya13WRP5>R{~d4bmbkoRYuu}aD<21L&x{tlepYwM z(;`Xx&oA9S-aKC?Hg!|D`a09oOJ?bGOBFEgeR=S=_|1h2_shu3PCm)q#PK4k=&EtcdAa{T)&DFFiF;enRCe0KY*puq?)}pJGtWWmfnwpyZ})lIGXyOO@Ik4c}&R&yP8?@57(&pY#7bQEy#ZU7jB7 zXO;eXitw@>J%$P)>rV)WZ{8II zUHI*f>5K&}7ah->s5Lmm-8Jp(k)zu8jvP7Tpi#Fm!*JfF)Nibz*}XYt(rff(zrJ4T z)yU%>=ymqB##DivuAprXkGg%bJ9JAp@B0&1?~`j2zQ4O$@#(q!`|D17T~66;%;B=- z(m4LkIi~(@`_|JoBE5$8SH%7{C1$Y{Zsy)uW1Xn1kF2}VzhII|JsjD z!n;5D%X_VS;k74rZvNWotJj43>i8U$N>fO{EL*0wA^CXUi8YGz zp7rLhb~9@{xl;bnuaaEp8x%e0kiixm~VGW0vvlyq|%4c53S|=CCpH>hYbM{OJCyn|q(nkdbWS$e6z8_q~^I z8lUq$eg6D8_p&6=WU=P$BB$W*6{j}e4VmbsXM5X}S2ACN>-EyZhYy3=15ch|4m|ez73OK-ubj%&qDKMZ;jK|Y^)BjX8d|!MTpjkw6+gF&)3WCoEu!A zow&^*l%20+$GOe7XKAee^l|T_gV9RQ>ZhJe`PZrwE$HH~`@xZu^6BwaFPHxLoo=Ty z<@D2=C03I+Z+V-cG~>V>*^Rf-HVc<8o_yom+J`e&R2xU0{$isq6ny)dgrwxbdA8LC zW@gW>d-rmiu^i6p?W|qBY|#=AlU)rHf2nQnvFSC`*a==S@$vjWnM*IX?C0lk{VimW zE8S++-EeH%ajwJF2RE(Be|zff9+l-ajx1Z4<1BwJe)I6~`RC{7*@hdiZGKr|wR}p^ zl%NkW!X~VRzWMZ;vHpW!rPJGm>`X z-TeMAck{k2S6^&>d&Begj%hu&*w!szTo<(RfYDeYqcr|Ni{^%&MJwep%4U zIZb6EFHbBs^SQa|h02+&rl-xE7A-o^xh>2*lb`8U#)7h%*%kA|E4~V~8A!vqclP!L5`@@<4W;Rd$lnViv1k$4OH1m8!YVeC3NN-`8?Jo36FgV9MEqsv@q*lP62a z$$h(iv#KsuuV><+j(Y1QXC}YSd~Cn_jeA(BZ&qV??v{P#8C<7QjDB2MZrA$v_jmWx zVNzG8uk>a1-_Nq;vf9EpRuc=iX%hDo*M8kz{{BvBtl!rrjtu6wijM`~mQIg5RsZGV z&!(U(v&oX@G?&UWbsej0mESy5F5}g{>}`2dZluj>-`U4>>%-ID{c~;B{d$|V)^Jxc zyPDdu3A5)Mx^O`tf|bv|?e;E-+-?n%owiLID;6#Y>xr-T%jD*_|6?Fm`9yF@(8{)X z$KLuexv)$wP`bs#Bqby6%HZzs?0Z_-d%@jyjw~i8Pp_9dFK6>3=HKIfdp&`RZ!QFw zE#F!;>Ge&i$U9e;vfbuzt%w)x(^k%_{B-Zfmqng4w(*B0d*9ub{E5AZUA^k|HbXPB zwwOFFVPWBdH&gD5p(@NVh6nR{M%m7P82%@){u zk6Wg-Nr-9py!(uEH*Ku{UuW~{gR+F2T%Xjg1Dwy-s2x9SQt=4RH^myyQ{i)QJ#XY&RZX0N|;c(P#d zYhjyGG23ITTjt(yx~5RJPq&HjL85qx&D9wvml&GbwSN2K;l{rowV&wWr@^ycSng~?vY-{0N6@&ALfgrsC=(1VN5Ip*uH3C>q! z%zaS0`^l!C|J&Nz>+7>}LU@E(1Opf>irzeZ^H5p;>G}Eg@dkS5mtB52=i0vT+zO>D z3lvNx{yEM%7Q2;meMH_Tb(y6Zvu);S+IY6s?YLvWdWvnLY20Cv%w-IQlDr5tm_rbS*sP(mblH+D<#j*K z-{s0U&5z=eay}kV+#vwaSyK73w7wi8{>Jjm)Og1gHUX*Hh^Nc{w z?RCp^q?dlIF35ZpekJyQ6zBPy5t#};zHReWMb1BbnE0f~^3C1d;>pR$fx&?qa|*qW zy;WLpuq<}fj8g}<@9wlR@tlpCX1Z|8Q-%#U zn0pFWt8=*~tlM=*W5uniFyp-eD+CY3T`GUbsrk}NWc|v*(3{p)u2(y|ADT@)f9qDB zgrn!6BtzyEx^zTBnd?yJAQ){F0Rcd`Hde*d<(8G8gCrrfeNlbACzal#r?_S?!Y z9+@6HuqpSAgyg{|PgK;@)hC`zSrl0OaLbHW`*`2J%38~Oc>ZAygF6OtdyaVC>OP~) zAj%o{x1k{Pd)3D^ZhPiQtv_58!p@ifdG_iuo^(T7+upg>pYUs7 zU8G}d?WM`5QFbLz~a# zXKSLk4@Mp2$^E!W+@ZI<&;7H1Z(~rMt@&KPCumOlJr z9XCeN346sdvp( zu3c~cto|>R7?S7`==+=1m(|z*!xtORn}Fzki;E|6xs#ebMY$vktwxyZf)Iq2a{N zphL0C^Rjh$R&QU~ChpMnx7(`u)>89rxraCDED6$FayfHJkR}g5zw%ta!-YW}FGEs} z%{Q5rv1QKk+iG+3l&|E;bYE+K!Sqz`u(gj%y~gQKmQIK525mnR8SY2zD*yg|TKCcU zY0pe1FI%#~xHv^7KvX4GSTM9cT-q{}HP}`2;D?X@oT`Q2hpl<}D(A_XD{D6In0vC$ z#&+i{sbu5j%cBmaIIN!aR@8hscYM4i>)O&qk}J%ta#D}PWf#Qz-VD=^TJ?LmpTptG zRS&=M2fuRRZR;!ks&p{mZo=oX&!O8VH?Zy7TP}Ied*1WE(|@OZn>W$8XMwjo?#5iZV5Jj$U~~U`*jj+hl8-`ZvP#SxN6UK*-8(+I8yg!7RdNY6 z=9TKj?fDUW=1;8BDPseX{3OBai|Xdq3Lm<5d~(&JwkS@|e>M*^?%9ZIf9&b`^nZU! z&v%&y`?)h`T57ZJRSZm*RaHI}Ye@vUW>8HRvPf-Vl*O{TApyZL(8yZOQ;ahg+;)0Q7o(tB8_#k9-w ze}&mepP*X_%NNLP|2XM+CA;Lk@^5F~h<+4taoBD&KW5ja^AewJm43AaWvyAZaIr6M z?uFo4la#_{Z<=-||6tDdno{wO*kuLE_1Q+=Zz|chUb}yDZ}IewqAXk(lg;>Gu~UzywZJna_)|s`QKI46+hU38eH@CH~ z)iiNvY^&H?Y;RmIe!%>Y-t8T;`!*bO61bvw_#xBmjh$@K1+lrM6Pmf3*o?(HU(9-5 z=^mW<`h3~bGOtr%4ObktYnbQXlZlFq{K;kfUwG*ieMX7y>h<3qJyGU}4zzeI);sUH zW47#{wKG)BEY6+zU!A5=8xjOsuYTjy*K^XlyO*BY-uYrSLy^l972U_TT<4z@xtMww$xdoq1L^&5qOTzt&@mOByO3EW57$eDZ1O-<%iKE?-I+lHalvYcDAqL7_;3(lP{`_t2n@^vLx%PNhWsjC8Zxh= zh+&SOyK8Rh<{vw@uUc5O`P0_2eGB!Thv%hf?>;M%s^)&!RZxAmed4qF*UKm?VA0`t;LJ-#au`m2aE<{l?qcZIc{-rWNlKyr$wR{crz?GuLPQ)Sd8i|CVd+ zW$*8(9@6Y+$YTc`XK?@PnewSY_Z@UnwHG|?SmU76-$WALvR>+bk_W8P;U!wCn zLr>k*k8#qWqu*xT-?=_Qr_O(3X!v`P3#HQy3w@US5|}6ybv@y!)`o{Jljj{Zd8r^` z#%5T3ea27Ow13~d9{2W4wzM`i+^JLTFo8ql_NDXRs$IT({VH_cZ?{us?=Bzf+M4yT z>*m_+lNbEBtL$Ofu6ZTdJYUNgLm2KDzPXqBti1DuQ*$37pIY4A}F75;7o$6sc)E*krj-4DIfp@``R3P-nxab+dpYmbTP!_RZ`VIl+ok0A`s_tV&gdI__GY-Z zNvBjl*ScIyP0g!lmq<&l-U4vhxATwqLREQ&tURXurq6TACKbJ1=9#`#^K#wN6|?`% z+3@6bsKafpxa})rj8=PHo%MpL`%#|#K8H#1&DWxs=jqSzTKS=Ajf+b;&u&)+gJTYw zp()Rv&-|$&xOeT=efg2s?%uibrE88Ht0T)6H=Eexeco+#zM7_K9JeHxd6SeDRdAVG zBo=Od%KGoUZPvys6L#I$cjs_j)@p9qCBi$uz8AR~=b=*HXQyATBQx>FimNdT0&Kj( zy$ZX}hhMh2bBw(y`(Ek=!!sWa9n%n8!2Rn=GZ6u?ER$T63-g{kat#x z)ZTq7BVRwV_g7VO+Ggk`I8jZhf2Qx7(mfAOv%L2<-mIq7V9V@aoH(N+=u8fCnn$CN z*v^$A(@!dir${VGa=t1VxN0h6MS?`?!lo-;H-pbtvek1gy`Svts$?|LWVXUnF8w=s zd>bouT>rFZ=I(P__DJB4g7@3~^OTy>o1WUW9c0-d*Rwikdr8?J4ej5O4C^(PF8Sy# z;Nozx^s+#M>_5v;_KR)tIzgwBl6kIPKf~jm8573x>HYaLA+{f-1YSN^a+0UvZ?XPr z11H}4_xGm@T;36&WAXC+5aLs{Kp*dF|)?NW1^nJ_y`bcI7$BmT+D?fW_23 zEa8*#scN;uS+CN?j(@NHR<4!x;>e2|CGJVvioUZ|mfQVYDxL0cKUquPHl@GS8vZ(3&E$6{=~Y=-{4Io976H;2#pEiNzhNXev9K|(-6ibbhSsm+j0?`FYXlf>(K zysPF+j8y|2{$L=nQu=MkjorWEPTet8-r+lW3yU;^X8f5Mdm}#cHHtPaOgNx*mS1Ao zmD+G^eew36WtlVblg?;0_prP2_-@p@tB@JA(dfgC6(U`d6>+ZLW`K9+Fg#qhVY^SJ z&g5ox4@brvrNxeHtY1Cfs&`Fodz!iFg!jT`Rh_R>ZXV)l%V5onYfE#`{B-#k!;K@? z7E3uuw>7O+C_c!_nEf(Lm&57$jQL?XK}W9sQ++fevyqWQ%gEZ`7^|RTw(sg!b_Q}H z*RLJ?`t~g$`0;pS)ce~N*NeB_zU81P zD(v_FD@WDU^{O1875WLhF?Sdzbm{gp2A3+HoAq1a`VP63vsM`ITQ$wah848RoK4_* z(5_>9S4|Y()K&NF-oGz15}E#Y|H%HijCWPO+?ghDFhxCL+>zc>xLWPg+W&K${w@if z{n2cO*pZOhI6*(~W)gzG&gvqO%@q_Swej0D-vl5_9rf|%K}dVgMOb6zLCEbiu) zDZADRo_7fk3f<7V-)&0I8x4@U7RMm}t7fy-{I~rcGJQ><^rEPCrT;hguIl)-ZFZCf z$dwy1O}4yiV@nd{V}83;dt3I#X=+L!oh&@76a!6MY|K{dseE!{KAZg22j|0G5AU9= z#tjLehHed09*591YEqn1U-#84ooaFIkDp5jX!&Mh!2cer$W=}}60dZ%o@j{1+`fBz z%Rvr?9uq-Mu+IxvcFoe*wEXLQ&W$Q;2^JHLrg1(%cdO4*m3sqs%r1GYX4%SOa6t$tE(*$>V-y`UXteJfIWvYbxdV9O?feA>A#Xk z*L0E-|E~^P;tEM!1`E(-jH;^8d+w7zHk*Jj-5ZsTHF!W}Na=I?m%44?U` zexd6n4}~sDT~+l}Sn)9<+((23v~BUgGqdQ7ZC{qIPSxR=u*z-v$7Ks|J=NCnb!h@` z-FkPwfBJ@#$*b0xJveth`a_-w5{sLeH#1B^E+T|LmozqjEB<9M>E9`^>{bA!B87Ud z(w9kBlXA0i=Y|M?&kSPgFcLHs-L$K9;S>HV?-bAE-aP8fBfzos?+%q}Pw*~Wo*m+P zQIRrVY8%WaXHNbo`cVY5C||MzD6nRk#-b#V|7TdFT(we5a0iTH|tILS{|N%cdY6y?|S{!2eS7Z zYh+~1KEoyQD2aumaS3aQw5RdnnU>4n$HvA^z5n}N|NGxY`_G(O<6T}lf8QQ|m-$vV zL#Ixid3C10-TSANt|^J{Z(Z5HldJi=)qSnczr9X&TuCrsGLHgF#Q4Nk;BJ_wN+$yl{U;;~)L5%0dmA%0lK1^#5Z;P3m)Oh7b&GLDA@0NPQbYFMxiz|=_7Y&Ge zb%i&q?%Q1n9=8Q-heKsn_@2Ec80xy>oUi?h4SyyVS3dL;)!uq-wd}XuY|V@y?J*NG zbtDD37Vs!#@|`>8v;AkWaJj^-TvsQyX3cJd>NCC~5r@x5YA65x zH2+qq*X-8$5XW8op{t>8n$@1OyXV=ZT$X<2%U6c3wnKr>^wG5;(NZ-)k<4o20t8Y+`!7CTR0c_??%rlyd^`s?0D z?4SD#9Lxp0eu`JC7R=bzmatw+F><2H)t}e5J+cvxlm?sgQ0PsE)}o-R3D?zQ)gzaA zb)Ns`xN(1QsN>2-;-z(4v$=e}Dm6oV*SlH$^VX>j8LMVV{xB6?v1(raf^SUEzJ6YI zSb~QwLUi512JH*SE``^<$;@Gh$Zu)$R9v)Rh2Koc z2?o5!8V_xf)Pg3Q#$U`$Kh_sEm8$cpuv}u&(tD=X+$rC@aGt}Fgu(=i1If0N9v01* z67O2ze|2(#M1c7613O+BGcpx3USd*VS&?_!JPnepnd?5eP59T*oW~z<;G)$WF^LaW z9$Xf&?v1<~)+9QVAK1lr$&_b98*kBi#usO;EF&UXUp<+se&8s-1<$+|v4p68`|N@| z*#(Rp<_>!pkCz|V(swX{w~sxW;oc&njb2L4jMt7O8N~FRx>LQQ@#9M~zqXc>ECKv0 z@(xWu|6ua`e}Wf&M0cOJyHTL8eWJB&VnDzXhL%U+X>F?-78f3x#qh3}KfYQ2=b8Hc zt+6lW*Zz5|Aa#({x=O%R@Fhc1(+R7NeNy^+|1keJbguf#!u`!3Wv3iv&`{s_eoB(v z6vd!JI~l#S3_iAeYCZh>$D8N>#qKbEnOy(NUE!9yUG;3q0v-$TGqN(;GVd=>UN3nw zzTsel>s?dcwrHPeyRBbWJe``m=ZDSo`M>`>dslqkcJuc`+~?)?eR?4K`0-+1OWW38 zoE%(iYb`JR_DgL()_y~yKj4LC#O%23LTd`Omn$_cT2eYktbd`*9of#?Kax0W1P`nf zTJWUlw1C8NhHs~Z=2gfZn7rm`a9&NtaryTj+x!l^dT5frbfQE6SN}YTr4}b{#rm*) zcT-*emeJ^3_k^PFN<}=XY~p!G%CFTw4fWsujCp<4uTuHSPrUgOb{}_LPkhU}KG^2t zs@kfrt9v4N*^~nguT?+$eE$6j{&k;L>eqZ(9RKmqar=L@)${+J*{#MepR;cMyrj)v zvdcr>t=AUKc6ivq@UdV?Xng92J)#y@SEVkw$nSO0Ax@G>ex5ozqoYZW>1UykRW8OX ztj@8Gikxv$y@jU~@1G1k=lU>d;v{C~B?rwWh=@#I__ZNF)G7AVl7%ZcWVT;!ZOmA8 z!Q+FIhWFG@A~&pC`}}7<=r-k?`vOPul?)&B{V)W zB@0wO=k;H@qx|RQ{Cm|uq~-s3&;NCyz3#_@&GUcmI#^JWFT88-!-@?D*A>JWDl{Bj zoWXl`nZ+6_bN2@xftM0DEfC=_IrDMOx3yPytlRU|bKU}N-`h5TI-SzpOrT0Blt@^dqZ5gQ8d*~+g{9In6{h!0;e-8exe;FP3W9|JK z{`!yI@&B`?s&Aia_O5B;F_!nX3%K6f&M8i~dQNio)J&&8d%cfLZ_O#b-<(;@{7z+S z*NGJeE(pfWm|~&U8s4|_$^wPealsdl{rY^~dp+-3y^XDI%f2tR-E6|kK0U)xB2M_z z?l%nUyMpyCRC~UKe&f4-oqykx?ETLc-~ab+|2O@6AExhr&HUxb>i#$D&w^^HJ0hR{ z+`V7<%lQAZ^nbJM|6b*|cTPK^#?@lyOGl}`vtCz?jG5zJ z`%OBQxy0CSwa><^zrP#;I!~x{ob*_5e!6O(VhXGCv)-`Y5d z;ndXd{8LMV?ie=qzvg}|!uk67A;$w2q5Z-de0$}2ue0gz|0q50r~mzb^W^``e_wmM zyk&spX|G&Qf*ShonKK=fG>=w6v&FA2*x5poLnKJmW2+if1 z|7?cZ1($}i&&8E5{Q5b^;r4rRrVfeY=N|qn>RG6*_0-~Ou~=uH_N6IlKO#zR)$D6_ zSp6|Wmx(od&8O09_RnRT>oqPcl&P}_kUVGHezct(c$o_Kc`V1*kaLZ$!js5SL*Xv%@zW;q& z{_oBD@A0-?1ABwPtej4}nP~3$$b;MdZ(_O!>0gv=b$G*>}zV@8=ds|h>GV4}>mQuLA?&6sWYa}cW zbuYd+ujXZT{Y(4**7eWd@B6m@$7;7lY%|2y9V`ev@j1Ni=jr;#$M63R{_*4Zy`N8y z-ukb+UunLR8Lp`K&#jjC`ulped+?Mx5wEE2hgA-#Fcn@ao0OZbJZH_E z>1_H_FTYWX&5Kbo$qV;gFXy%L{T0)gUIvF#LfLXZE`2@wJ6nI>pLOv+I_Fose|Y}y z@9VeY*MOQ2JGh?w%-;Vib^kYE`+t-Fq}PACn_trWyT8(Lq0sHXEsnEd`@3JCcHfY1 zs59$(X4%Wb?wxmbZP+xm<96f*XT>Jo^%cGw>;9iR&42&vzWx8d-~WB-{NJXDD?mn9 zEP3*CA^*OA=Jl`C>udgAp8tE@^@|DBcexh`@d`903k2pmO_==LXzKFP<Zau)$!k?qwT|bd-dq}BExNAg@Ek>n2baH`?bKphRnf%${kr^{ zeXpPYdsuycwfvt2P&-wg{9IoDY1jRKC+FAX|LpYN^ElJ|#=h60%$$|yzp>hc-qE|2 zR+Re9(AcG7$?c6#7EZc$mP_-2$c=ypD-Zsvt@-z}eD8btKR>F!KAvAO_phpSj{|Sp z={*bf|2TAQ{=d)l-;&+s|1K`K^*#F6n(F|&tLT|avu72`MXK%H<#UmL{glflyM)$8 zaE5nnoOxR1=`Ec^zQScM4XhpqU9_unSvYBj)jJuk-ViUIkdL#rT7Um;_ph`3ul@fQ z^}h{kpB|j{*A;A^_o0va{~m3w|Mb58-_7Nf1@+Q=I(1s&*QXlCObL*0J(Z(p92M6% zb<^eS4}U`@CuBr@++2~P`Ow?^-M#;h<9|&}xBt7@{`VS~X&<-#_0RwD;dkAyd^=ki z+n+PELRddVJxdir}Q(v1p^={&&HVY3C>+29+0uWs*-F{`MH z?sI$U#kc5W--HE9J65+`7qb1C{r}VdKeHe6|34`GM*lXbK#x(Wtozuy_WsZG|HuDp z%>R97``)%Y$Ewm!UU#)I&-*_!%NeaZH1!Q(Q~edLm0Q9C3iv0m%6di~eh_rLSM{uE!c z-~`BXYovC||M;3NU-M=0{U2HndrLVyQqnv&8~@+AVpETw%XERsxw-!J7cXZyZ+Dcu z*0p8t9xYY9!iI{~tEM+~3##}WuMFL|?c*i)|37Bi{gki!8u{DX98{cY&wc%K<=Xx~ zhtB{1@HlSX9_ix~Ll++CJE>{z*|_cS_UpO(rEbrjSorH-gw4B(qjx=jKFR-cd;Z_V zzkkGiTBSfK^Qv#;&%5dU_8&Lq-(t$O5O~14>foNea(;K$n7>t-wQZ9R*XqNG&udO) zJNjtG&dQ#?y~`kPw^sR0yBkZ_{`+Kmx^ML=rbP}fKOVU?xA(Ds-K*K(>YtZG&7L~p zxi-Jqzy0xl`|bZ6$@ug{%=&|j|818!wYoaot)aZri*~Mjwx(&rQNHNK7boxk%)S4| z;a>&ihx?|2GQWks-QShV_kH*+|8v*zy*W8Aw&>pXip?(mq;fEnt2r+1McLJ^EphXG zo;n?53FTkjq`u=u#XJ6gFJ3<}K5v^^{{vj*&G~Vt-Tn`6-unmlk`pYZ?i5|Ay)tC4 z){~jfsxO_Dy&b(W;NuSGmXGJo?X~~6_WnPv->1Rh`aDoifA5x$cjRlH+$??n-ulOy z_nV`YRofRl%W)p_b)HI{+0*a zAxS^l9e?NE-z)0(Hy#ZUSunG9{`2EkRdg@S{wo^3Fq89`M0t18hokntH4`QlfLx$a;3QWo_2`>KcK zDqK&!`JRUzZx3I#efPIr_tXzsi64FPu=(s`|N2{b?|7Fv@V0G!^knDrcX@OAHXL+4 zGc~c))hXQa`^~5Fw@p|4Vq0~Qsq0`y%X7PbA9t6rNq|y>ankqC=l7@P#^i}yIOd`A z_CrB#kJ0Q?I+v~%$XO-d)qWYOt@6k?){2Aa73b9D^8%K4>3;a7eZKaaq%||R*>m%k z{d>9kk4xouE#CCjWa5(Ma~-$ug|tq)6|t_5`{JUsPu?*dGZwBY;L2&(R&MwI&*oo` zy361AfJ5x`p))nfu(be-=H$$kd9y#$d1b)YjjOi3Tzf08)x&Guc4-sMv+nCGW?xQT zCOdW6+dtEGTSoXPE_?O#n}2-y+dX;FFK14__3c(JzXLl1`yr{z51u@Du)EM^Pr7;& zC}Bm29d|devWVDktPvBgnzioO6Q|Y1>z{>8?!I^5L~Pw|t_o8wll#lHZ20?`-ng|N zmp&XWH(@RZxc|LD#jovyy1Ilz^1U7@=9!!9yTpZvPx-X=foZC|P#=iXP%eJAvK zkL?pTZ@;~9>p1(D=q>Bgnq%FydPTj3&swFGyS=vWtq;+BnRaz^&}#W9$#NnrlKusI zc4t3t1Px1UFyZT0Tx`g;jv;$(*seo$=Gp(!9WRghGx+Bns`gdC|M+MC8oqOy1AEspjxl?7gMITsV zVRvJ@FnCBILhShN-3`;-4tm)os!mLaZe6A2!gBp2_cn8T4U zNOjS%q6qG-S3)^t+FxX@;PG9b*)F^LmeH?Y3cs%||H8-iz9jC{uL$Ye3lC1%Yb7@I z^Z7m7g!BHCSlX_=G;xJC&waajeH&}{?Eh|7yCXY|3sfW}KIDFVxV_wuzyF?D!mW3^ zb}Wm@2$z3oBR+rM@on#Os;`8F$8FvFu4}?#$3sd_G#^>l=5=bc1ex0D-QceZJAcQ0 zX@%!*+xpIof<4>6m(7_oV`dZBFB>*+U)(!+@80eAoB2$hIWVs6_6btdY_(C(RM?Ti z9zKzAtJBVv^(L_!L<t&7fbozqjj8kQ5A^+jn;lhp0oWnTr?I$`AK~V{hjO?wS z2?nfL20A^975%<{PgY&a9MI&#xXf$qlPrU;?LxDj?mY5VCUNaKlbiotHzht>@nG51 z*zo?$-9PPP{%kJIT*Y)Pvze*oVh&5gj0LZUwu#f_| z3GC??5)$(d9c`Mwr@r~uqNPg`9*J!<;WSh~;8J9%D0ul!O;&WK>Y+L5PcDQlJn7J| z$un(DLy*jmsfXq-5q+3-+CoV%OG!nLvH3xgMb%6vwJ&EBT&GPB3EjYCce}so$Dhr9 zlZ(#fF|qXp^to9W8nc4Nb~mtm`>wcHaYxZJ&La*26N*$sSYuSCdN?gwd27n<;!eZL zSz7VSFU`2++w`+2FUDTmM=DPFuH_+*9hv_lECtxPr~)`c^*wRgrV)qX0*` zx#qqjANL7@+v*WA?Y|XkPV&5$=vrjd=O4NMt?>v)s_%?hZMp1Ir#(v-D$Wpo zxa7s7euYUYru7|4mKU!07dUk-;b~g7DnY=5Q-M9GX;Sx3-&NZ-p6*%Um=_iPRzt8w zV)>hbGd+o}I$LcgcB_33U|;y)fQpakNvi-3_w=sy3#YAgZ84cUo9A|r$=>Fj|Lm3D zt~TrW;L7SFV;L7EdScyDhuGpd&uAgMi%lCiybZgiBg4TP5 ztP{6rdrO`=8?(g4UHYx&ubDwj#a*oiiyJs(!|GxykC?E`F};6pLCWjv*SU6SoohVY z(a_FX@J3WCBvNwOgwhP*nS1x%J!(C3(z_Lp=AQZ(blGMR*Ua-0U*@h_yn0pCtUP9} z{rtBTnGCM)7uPto=)lC?-TE@JH#ieP-Dk0*+54YHpMGEc>*ZeEBGqpBC%Js5LNu4& z`Qn?k#^l(o>hxL8+n=$9B|5xuT+Vu6;_5Fvsf~U?K`A|*R=G!Cg9m*Ru zjvH#s;uSd};AE?As{QZdOt${as0~IfDX$FIo^O1x@apT!MbWk2&nlliez;UH{cl~k zX5Pe26SMh#pAVJSV!ZK1uKD=3H!a{4*8M2M=;7s)6_Xj=KX&QF>Wj?1nbssZ*}uT= zyWqrdV{-PJ9!Y|8A z7t>lOdgNhXhvCPia|J+k8DsbDM}||(L#A5$az49p`0VTRF(Hj+zT8$_IlH!bmmVuC z%C1Yx_}UP#X3N>mBkrvdveiNtk4-3;zK|t*yR-V)4XvJt2O=|oTM0Vw99FE@w4P- z^VMZv>4~xjx=Z`yu9sPCi*V`OwJ%)U+YmHMrDX0HP;Xtu8@>#>|*ZU3ruGhgjVo5&pFBX^}}0b2y$m4t{FO)ooM7^dmf zoO$(faS`v12fss>UKJ8KyE1QX{;Be9Z$0C7z0JKK`ehMYwDU&c!{FH}Ceakb{u2?13uqV^V^6sYm3m~m$j-}0e|0RI?;k{?vBAbJ+e%++=whV0O znU4o+*$&JSe0Eg(%)aaAPtVl*Ww&O-iZfA>;NB9?n|JSX8GfWOd|&hG+Jx^-v!o|7 zK66PiI1?c46dmv{kAXRK@3VK#{pYm&pP#vD^SbK!+2E_HvXe_Xf7iU<`5!|*rfCRmZ0GQryXp}Rka0^UnwYUnALi}RU>?@`U;J? z^?k1*@81vIdi(0{(>WEIKCif{P&;NCB;7^PcbSGVu|bV|GY%?gPG z#hB)WvmItuW*MDcyL-MWH68(JFEWbxGU)I3v26QSi>Az z`#Z&GC;NjV=R`mg!Uq!~9<+6J$jrE=KDo1137iw;a^@}Py3){H))|#o85+7L@MfPk zwC(xpgBwG3nFLR8Wnt>(&&HqulGq!!ttA+K_%W2=1?|~gMY3##uVqqC zxdddhf`_H&>gs%FsNrwmwU%nr>p7lm%>hoz83(&XT)tQciUzlF?b!EsT6f6qGHa%f zYfD2eZJWuYJtIFkbQ`$SHiJo(!7p@e*$IQ(=k5*P>k{0LfMQ_AL#HWvb38l!e8o@t zi0P^GpERBxy0W-hY_I4JukAcP3y-TlvD-Z*qt3!bbO{gBf~>MSP%+@V{(a_r{k-W~ zJF~PhufDGq`MuNYlaHU%^IZ;C-{1eBmA}#WC^*f1I{5bI%hl)h?#K$so2zv9bm)vi z&rdV!_L{89-(7UjB)gK+|JG@R=PPF~y7v9bzq;M~{6kp}S#4YR$64RL;%ErC!pMld zz<&Mece9B{7T^1p!dHLO>$8&co{OCvE+GsRmw#-zvuX2DhSQBwJiVGS{u4Z&wpwe8 zyW8H|V3mI_rR3DYY3T<4ar`g;CuU0>Y>mLoRn6?i<}-r2wJZD8Elvj_Hk z)&KEx%Y*M%Ue+Ap5oKyUydiGdQqh@`pp?tGyWO4Tf#mZ$Zo4X~HKuSD@T9If7Gr4K zvGeAu39(Zyzi!zi6Z1!L$1x+Lq{|f#r-$vF@Z|N``RWZ17KBdK2yL0pp3GVp{raTR z!~a`1t=g{tZ%OS(S#yD(|4fpn?b`d#Pd^mc5_diB{-o2rkLSKk|1~GF z-z!9xdEtiKB;yFl@)*2EVaPE3KE-)oP|4c+Kvn)&nC z=Sfrd1VyO%to80xEDTHg%F#B-;nL^Ao2#7;Ph%6h=CI^tt!HoP*&RJScO(DK5{@W# zOWRg|YP*0=INq58M2prUv0^k zl)2%gskv^0X58WWTa!;}31$A3cUdT)5x!dX_xdaKHASqm_s4~5pYxml+EgSjbIk)m zjgVJjik<&f5q>wy#ZQAPp9q;h)OlM6cwoO(?RU3*mV}^Jg!%7r<-`hC^ZT- zT59MCavpa!YMpu9?eqEXw{-S>QhXA9cfyo?Y7h8Sy*oW7-txLsms;s@)gj}_LeGFz z+iW>Ez@~>`qgK)|YWjIzNa!1nJ=`aL>1fUnRQY?Bqbox5&g7EH zPizY{Li@H%JJI>V)LL(nt^Dr_%N@o`pTBlvI6!>n(uVv3WTm7G> z!T)w-GN>|R*fZ+`kIS`1M>xG&qmDlEG%M24QaahyT(QfkJ!wUNXh3}VtK***{i<5u zFy-ges)vP1yscNIyS}WwKmD%!M7#Jbr&~omlddiPRh+=frK@?$<(`ks|M$=RR;+Vt z4h-1mdws#1+$%hu=C|sSRzzHFK5=#7n<+WBOvsP2Y_u#~n&W4kO z^kwCe)-c2f-in*TsAjN2&`r%(*YMVjV>Q1&|9!Bk!((}$%k_WP_Rl^Sxy^q4!Xw&F z{X4H`{f>E`K1Hcuqio0H@(D!;SM95)GpRam{`c$mKN1Ye0nLId9+o|xYX9%XxBUN~ z>woqwcVB*VR?+4?EzYM~mDhE72Fa~cQeLTl;MT&7T@NO0%2H+2b`>w~Us=()D0Y6@ zT7$+VkJB}M1wB_Bn_H%F%2RD`U!ZVymb0YshohGb{ahBDb94y(D4^;5;9!Aby_bm3 z^@*$ZJ(bhc`sPrTXer^~v6**;W0lCE2UdUo{b^g+o#AYE{iw8P)}Hx0SJXmxyK5*T2;~cr1KfuE^^r3?CJahX&WjWzOO#S>;r`D!#~agW3K2 zab=yeHa!pB%C)#m*MGhLvfB|srmGh@>O>j%br=L3njqWK+~~aY%#?*|6ga(X`QJ9N zs5s8lHfoiV?=*dq;ptHDu(WXJ!WWyr3do4;^O_YFdSIo%iszqK&6ataxj*pTpF3YP zu6{pvFygRb+hfI3&Cbx~m76vH{2My9a>~aVRF+sB4Nqg5V#)SYOLxD4dacP-`x9ro z?)_w5yEJLvy7Moi-GUAVUu!sPHY3ipC#ttf!$aawPh*A1BSxu^SGyFL9yngoiPL)Y zpZR9B1Ec)swKBhLZM`O3ySmnC;jfuV9U&*2T~}Xcw%fMl%GE<>rFl9}Km6QsNcs4M zwG+PGo6sW7w{qM1opvkt)i*X=ZtW7ujx(s)k=*u)ZN}9355t_lm%pqie=jMvU(myC z;RLNEoV<1`xa39nJTt!a@3d`ndeE#qK`W&8Pv?1W&QSSy@pa-PU&Uv4MYEGd)YZr|mi5$JyRC9k&X5P=q z^))ko?a!*MxutmJ%PkxGS+`PlecGG3`;y?RsA(#F&B6+*vo!i?t zrm-mp9M*M@%#(ZncHeG=obBvs+y7fE=Z@SEvoztkMehgS#Oi8XJ78;b}xCDyz7KvzJmzgE?b)${}pS0Hm(c+EwbCO zSE7Hr-}i6#xA18>?N50jD8j{Rc!psmTlze`{Ow`YN59-%xLUjDx7r?!^K(`SO_>wI zEB15Kt1Z_48dgOgzjyoQznpC|IV3}AXJ%Eb&i36NqJ_V|9{jK{@AAV_9~!Ez+rR%j zJ=V|MW6n=b@gqk+{P^K(VR@6OAV+=TL6H>?%O0h;EN^dr-CC^YV>WZ`t_)ArYjtb4 z{gPXLY^v_!xaTV_r%p|(zWcM`;~oX}=d*X-3OVi4C{g{by<=77oNkS%sIGhG!(JR! zD6g`KIsV~>Rv~DLWcy49{|Ei;8XelwdoT1kUYyydRa~~>*v<(d+PnAdoNLRqUWuLW z=dokeMXIwjuT>_Vc{*d#^0dmZWOLJEfv$f$6*`{@etuV?Jve_(gJ<>U?1(_S4XedBD{Wf!BE67x zH&=j(UqqpC(hiUGkU$bJr{XxNNBQ`*+ztP{TjZ zyz$jbO|G(qpBKGtVqV)8V(z)V(sBK?71qyhoC`JkEmEp@{o-& zW>&6shUB{M3-2CZb+IGqo-bo)u@iIWHrZA=$w^-BCff>R)J{%G=HdVL2I|^3?;DRR zTg;pr{w8?I^U!2*>4q4igYEakrm0rBU$zUoy1AsdD@0n?NlU|};77srXW$<1cJbr8 zcYmMzed^TPHJ`G=HioV5=d-W8?f!4iv&V;`EU%PhdoQkiGN~)&6WfGSE{}Q^-Z5c_ zDKza^Uz=QVEuv)Bn@1fRj%_L1vi*0@Tt`q&TYI!1|9|%NhSE5N9xHA3S^ey+7_ zUeaRkvMRW3YM65M>bBD%Z2Cc&ckKA~{#{$~rSf#>lXbJjb(|P?)oUs1fV@Kn|6L&gz+!nM&cbwGp@9Q(W z-q=*7KW(cq-}7bcx<^k67_glBT5~3=CG3s7PxFpo)776|`AD4$b`Z&(woI$wyjRQg z-G9aBi2Lq*Q!zz=eVw&{pMTqdij?OQ4{}^7;5L*=GO|%)f4gN-^c06*w+wd`b$Mof z3;i8eCi-s896gu(C5LRh<4o>)Eq$NgD|_m)p<4Sh)%=F9jZ%5ajdF`+_zV@?>gOw# zepB|kUC6PpDD3vm^ODCHHyq zA!FG0{rkIHGePz}__k9(*5+YVTAknR8z&EOt*d^f9{PBf+|!!Vm4DZ~)e2Ro4)L zYd3F|ySz*#V3I+ld*B>7+1w*?#im1OpRurDnkujN4Cn$o)%^XtUFfUvcTRk&4Iq z`B$AbaRtSO{0x=b`(U%+a+m!#rmYrwxk%+y_~Rf0)7s_lx^pMLIAC~cvSLBPS{+ac zkbCH(z|)T$*-xrYzOrGt7B;P0>w;eN{;51IahIlMh|b%!QlNWXajp=j$iB~-?mTmS z&YU~X@cEGG9X%fR370Qh`A^sTQnD{VDf?=+eA=80)6(L1zctoB>iz$G&8g`PQ)lX5 znYZt(JA0OdT}!SwL*6qD^I6WTu3TnY^6{Ug?fUQzi`iTyQN2bSF@6P`>NB2C233+` z4-@2TtPVBAD(J`7@)g|tUcPtx-`(9Ii@NU^oZ&jW&gxH(cE+*^AzM2dLJr0iZrPsw zd@>{;OBl4&J<8ZB`(7LA|Epd2*iL-$o=MwRSAAcz^Y$L89kntdXJgX-?F_!X-Y(>U z-^9Z5KI72WtA3pd-m%)h?B1tS#q&?~-l@2^uSu(gncs*9q0|Mm_7|FGcyeoVG9bFP+nS?$@p_wOe|HquDzh8j$%J!~`n8*H~R<$b?f3oNQxI9y5*3$y%`OcS*F}=w6 z9>B!nz2(`2=BCXtt3oap-csXV{lb5)<@eyq!pEI6jO8`f%D;cN@9H@XP%BUH}G<<&^h$}VztjE4^Us(6%8o%39HO}u}x2j{M_VL|oe*R0nd*@3o`^p5~Ewy`g z|2C_A`P8~B3^V{{F1Ks@cEelNKmL^-wOwYxK1Jk6!!MyFO{>lPR-6BsX(`jnm9W-n zzU5wxbMMQ%T|)Pj%-VCy^4x|sQ?6v&nw##KzR+u``v$Gn?=G&l%w^T&HOpq28_I;H zuiN1%%KG!*$%hYb8_LawWMggvNqaM^8}+9hN*}Nkw|m5YHOO1V6MMDHY*ps&sGw;x zE?o-S@HtOt+S}uM4b+!QTzb1D?MhT5-+??Eo4Dg2H_l{}PH=T;o8ABM$lQ1GIcphR zUoc;7)%m_|)kdqp%kA4gFRJhR9o@g+j|f-$yYm;$&B?IYGWUtv)xDd!7?*_dnXKt~ zeNx@nw!E2JsJJA+>V5f?PYXix4HPrZSJi*oD=Bbz{+vfoPQ3X88mrsRsO~&RPVSOa zh3Z;`aLp^#Q%ZucdGT~p?T!AB(3s~%uivJp z8fbdh^qt|q4ypu2Ev%a4=Phb5<4WpS!N(f6SnJL=oAaR)_`mMpUijR*L}N~9rr#+C_jEqn^}iFYsW_m+Z*jY#|vB>!V0DJCIwYq{1!VU_*|gN0|Vc< zM(HGviI=JuPfBL(WVrdeW?J-`?vfynu(a@pS>HdT_w95~T~##Y%7(|?m&3K%m2+pZ zUip1+;*B}Q8r5d*x7k=`RayV-F-$zT#!>5heAb@*fk&?BWzC8b>&OV$V3>bPE$~$F z=C_g?*1peP#bv&CwvEF{bIUJhH&w~JmuQsoX_U%iY^$!Gt)&{H(zfl@8|gK7!>VFW z>6d@>e4V-4<>{~gvCcbYi`UJ&e`Wp^>C0C>8O3ee=__mX%loh8p4#-MW`~W|Z+V)2 zExla!{g(Nm`?JGt-Vv?xTc0j4gQK0loV_d!G$@y6*ZA(up1?mTx+`qBjy*Wi;P%O9 zv!UkhfMWscw=D7JoLlc0zE(6eWX~iKwd34nHS;cq2rW7%b*seD|GoF^TLJwJTlXzl z<-uI7CT%r;oBwPtk;3_^-_o+qY%B^nx{9lDQEc_qdG8K+-rO#tsu6N?)s~ZYxA)C1 z5t1=J{=R#e`k^iNu1Efzo$tTwooX0!ILqpe-yTz|)GrmpbSMOFY~tWK_VM4on?J!W zHT#l~Ut3Y}!7uC%_rj>A436Vfq8%Jwq7_%XTtXi%{Q6!fQ?<$KdfGXzj4gQ=1=e2m zU3{WVb6dfVDAP&J%OsbwXcYe8Jl!b}x+1a9;SBHMmM_1y_$x8RFZ(+~?#tXbWhITh z2iza6_;z30>*Lj5pS~}fUs5Aw?{xM~rrVoZ`&h-#kvs2~ES_xd7tQx6q+WfY-8&Zc zv~?jChT&RqmI6z~Ti&ZrEbB2c_T+~@APskiSdDfND@3Q(xvJI&p!9-Pq2lBU}05A=c=}4&nH~DEwWXu(4@fM)uK?# zyhKZ6)^oe5D-Cy@aXvVev4+R6*|6ymBeOHhTdd4q zYCqzC|9z&C_=F;r@Vt8y7#^lA-^cX$vNb~wXb!7Qn(Nua2Vd9!-(LRjk@;U&)_E!dHVjKermc3a zZJe;=Q-kjl;dO`q`u?lDyVBqC^eQRSr%ttRbDRS;jY4O&TRY9@6bPQWE~TqWG(m5c z)#Rd2J*`%T{Il%LCvMVMklB}hhS7+jyn;ijpHYBohZcj6XrSkwLWiaIre-sxzEj(_ zC4b%JpC_6hF49_7VSF%>HTT26wOVtc-{(uc`TgT>;ble5ewKjjmgsHuE?fm1AN`)r zf4uJ=qu1SA|5!ZQ%bLLTj@XvJJEqP5_r(29{Lg!Wd-%iS`&d<1G3>UQcdH=c1&2Xa z?ZH-;N>5u>ohfm~$+af3o7PA;-wylm@~e>C)W>E|%gTbP&vTZ3uRro9ZtLF<4=-9S z_+nA(w^lZH}9^iM{1i{M;0}Cox58v0k5-xx!A5HB;AY z=?IOwx@Dn8*A(Z%kWi7M3w*Y5@8B{wKlDUJV&yy8%Ma2&+(=e0YXiGiO1*r0*_MdI z2P`|LOypRh)clQguE%EXEhbLeY9AkXX8I{y(QlIRl}jhDeB;=Wu6ya_pUEO&KWfeu zA1TcUXVuTKWqWTsqf6{%;FGUu>1lt3vRYc6mlvJeTynzcChwyhyKV9QR{uXA`mL-O zeEoUa%+Np!@jMxmZ%oxI^jp7WWP87TV+3l9n($rq-x9&y@?gj26DhYs=FDy}fBv<$ ztnBl(=}kLMo?BO3s}lTvJ!ioq=q>xLUVY6!ckvI`{`mX6 zY&$&bRk~*$EZ@5J%#S^;`~8zLznsiE7;v>W==AlsvTrW`t-kHH2-HeE>w2$^VM*JM zB{In?)@yxx=yv+l6;`WOwS~Rkub;kEx$xRN?tM+w9Ez*CLg!W0|6M2cP{Y6as#!{i9PqS}Zu>tDb8iiO*6^lj#Qu(~#K*32eQ>oQH* z@@$KjU!SRAkZ8Y&ZDO>SMzMI-H_5EK?K5^i`BHJLEcHp+#NydE#AKt~iiH8+UY_kiuT>lcc&5zdF9Xea>_j*OcI)An0W+Ar| zxULjDcHS}j&f~<}Pp4Yt>!n`*Joi)lWG^Q4;0jQXs|m00V0`fLL9LwL=Mvs2{=YwJJ@F`v<~q0Q)BK&H z2{P-`EI}hK%y|`zf{X=nUUMq%#66uCdgoEoTzC8G1LgM;pC02|=rPL&(u7#PX~(HH z{$(cXua!?a-+4B4<@bBPz$M~r?)SB(6_UnX)pC{x%R|0|O%5tD%GHho&Vj9i$({E z7PQ?iFx<1s^1_}7)$e(0=wNvG#M^}7!?%wWHT(^|rl2|7 zycH8|E>AtLsplN$R?=_h#c*KdtE$LpV*cg_oOZNH*IwNHNc{24&931$cV`{BBhA!s z?4<=)+lp^dhR=?=24#a!zp}T=#^>i8GRl^{#tqt?b0_7F zZ{_*bi#8NZwalI!vNgy}Y*%dU>VWN=M0|QHUavZrY6f0p_wd+bNruzs_NsnRZnu8ZUHx6V zE>C0>5_qj!B6rA%%YG}rpti`qsg6IZ(bswsn{ohi-U9B4%pYb5aJ8>~*nIoN*Ebuxv$YkC{deqGbvreG zjmOSSo0Jxr3RWKDTL$XIJyr-1YFezVasQU$?R7HI|K|K;jX(3zUvx+MnfUwS6Vv72 zz00|A6SS=1&W6=0<$a5H32B-9TB9VCKHH<1eNI{2mq*)Pe|z#dD9=(Azvoh=l|n2>BE7u|FV z)Cg}(+@)QVxmc2UmbL!5m$tgCRf&AArx^0`T{Eri=UvIT{(h&ZphW-ivQ41$WXAtx zQCHh8u4a|<%eRVX=w6t;^HagPSxj@ix132}4Ox<4g^;`bW1FMpTK-zJJXKu8l6ke^DI<| zJGK03LE+9!tL4+zfg`U$I;^p}zvCWfLV;*6$GJrYyy4j&D%}=9#)2NGJ++SvHD?sb z?cU?=H09)Vvy~TpewLVi-md#tZ=R&ekB9f1Sm*5H|8i$fN#ws&H=b;zPpM_cKtW)> zihT;xx-$yz^}a8(+w|sC`z))yJKkqqOpcy;>bkkZZKY3pt*xzJm#Of&EohTYU-Us^ zb$)v2|8q(=-GfuLH<+pGi18|Lt#eUy{h;@L?@mFmnWtZGDf+Y1MJRXm@yg2~Ik!$- zoh`WQNI(PAfoCa38(+@?rJ2oo9Y1$$2%Y%C#_8OA|D9V`eNZ{wTDEn^jg^&K&zeoi zJQd5e!7HYD?$S2ty26rkYuCL9$^0Y3FlXMpTa6~5 zTK0@<;RU{>&nho=^D*mPimzCDD||}+-l_MM?j2fFT4!4y8ab`(l4L^Mj<(rNYa`#; zJ^#AxZS?&;0U!4~mzyeN`ge94|Ajm67q50W!*Kpp+@{t3tKvut&5`pv*zxFa#ms`I+Kh0UUJf!l_w;8Kvp5FK>v{>}^jeQ?1cPVIQEwxE~ zxF_>A_jJXtWxlP;EdQOkH}B8SDZz6}Z3-H9UOskREZ^f+J#(&n(y2w9y>^eToS*$| zVddv7YggUhw0rmd#Ps_FQF z7N&;e(3JtPJ}XyEofY!bZMWCI|HoKOFY$-FY6R`wKdb1{R+;#u9I8K1v)?Q)+OeJX7Csy@$E_m}()uP_i@8?dgVz3cJs%ya)1rAJ1;kH1<}S2JbV z{PZ2KD%$^+6z;AKt(_h2xcqOTmz2q>FCTt6)$WY+^YfLF=yBk^F5Pck=jOHl8AC=+ zutJ~!bDW8Wu%@xr#W#irwS>B5Gq^&Q+`SdFe_n?|sI}+o%7m=Q zO>dvin|1Hl`{Z}UMl%yGPY=-05c0gW&Tcc~)DmXJwdW^Zd#?Na&#Gy=SIt`bx>C1T z@>WHyLPVx4bG$rz=#+q>$DMO)Kr#BY)vh9yS%3c*otQ%T3(rcUCRa_Eb%Sa4wJDRl zjI@_dSvN~>kKV7Fix%sbxb>Ln-{w92ve-g*dFj#IGxp9a|ILZyUFLH6_T_tFXTKTl zozL08?6zEjzx_j^NX(hvTR$z#b(Q37Wz-0iH4}X+sMVq%?6SvFXmg!{rJ^1mixh)^I6~enCH%4~OpY*<{^MP-1^J%f&O; zWAkpM&?v3BCPG^TPNZ5sl`v9W>HF>WcUH}sSZUcO4~y3w+FYq&V{U&UI$&AxoI92k z3hzJr%$Kj3!hZGrs@fiNabebPZR!=z^y{C;S~G*n+3fC<%k5m*_y3HR?iUodnYlor zEravXqA)L6n2`=DiY`R=!)OyTvVp{Fe z7ps1h1PXGNn|(EBE1KN!P+_Be=+*Dv4r)pG^sK!cEVH^{XT!1D@I@;plx~*aWTu$V zvSss+9k~}~G%vBz_da{VVfKz?-78<6yY^vE$e~#sRo3y@pU&LBuX-kP+f0|3=6;uX z9hRb^mF5RhOrmbrzgoGT{ez}8H^}v`e;(gjU+_KjF=w3pjRRsFkDS*D#%US7+7+;T zkx*m4gtmv-w4RQJ6%kuy-%T#-Fk1acwc*w&o!oA-sf!g>9d>zfA#d^Lu&FsqCoJbk zxO6Jf%z1{Wc7uiL+W(Sa6@bzg2DeRy#D>vQjZzwA8jUZw_Guy~!%-P$_-z1mZ$ml8%Z9Xy#i zkI9H>eEYQ3X@cD3cX3CxdL%RrTECiDT`o{s_`~M9_QlMHX0rQS%VQ_y@AX@<^2)C> zNv7}Oo2P^x%si}NsdjaW9*YLYzJQMT2e=zgE)e-c%ZnsfwvDFG0r?o$Qy#fwp z8aoSINqwTb;D)gDl>$y7`LfPW(vmAzWh`B_s(1d%IZssvHobeFTl4Du-@W%{qAhdnsMNK$C&Lxr)YLwC{C)P?_!V*T+~LoDIPrPpdZ|2|v}DfA=huJ6 zpMA7$?WBJYf(Si$|&wSnW?AH|O+iTw}P`cXN?-tr=qHlKhf=Av2@3Y?)d=rsQ;<=G% zGLzx3ur)s@H)MC5Txo4-b0cY@lXB%Z(atU1d11M#D^4mXwpE1At}@g2%bayx-_o-s zb6t7v?u=Eke%HdR{Z_CSeJy6tXiW&6Hcw}9NPEraRX6?A7hLCmrdvGT;tSv z#%WnH^;ZM#T$PC5zCu1PD=?`4@?=Fne!jOhpytpz<;JQXlZB)5SI(PbGIeF3&y{s5 zOIh`9bUSGDxOo@5-hM0Vq?_DSHaq?NCy~gl9ZLWAbj_H_=(#Gm`i@C6w_xhtB~y;( zZcRNHVErYdyxRY^V&^i?Qn8QPPc1`d&6{NGddv6uGM68r%a<3QeUs}Sojad5!S#pJ zTFVW5Q~rHzy7+M8+;&jPGd=QA(67J0K-{x%=b2f$v&}9oRQYwM?ACsNxs9Kvyk0ap zDvkGQT#3>NCAr;Kg1UqMuYYr`^j$Bbwv?`A`rHSTzE5A2J}0Kye$^o*SAUg0_T(E*AfHOz}VUpg`IzRiWrS_dLmec06bbxm+oi`KTcZ|2XP&z$?U z+30M@y|3jLO%Cq=BQ5l9?s2b^LL2#v+7~Ia^_72PD^mh{jqmYe(H=sAbI9g(Xl zKVDI)XZ~vTqxWw+|NM)0Z8vSc2x>rwS=_wO|98TzoV^}ttUEIu<3yB0W_~UG^(3zR z$!`^rGYl;pLaWO!zn?XAxsmCo)h`>agf#rzxR!_O(i*=-FBDV^A87i9E6j>J=hG4O z=331@^DVQlt(NJGi8AN7RDC$$c|q(7v3F8ue7Bw!R1na8=&{|Mo&9~=_t|A;;DF;= z+`M@A9Dm1;`=S|3S4<0it|lKU7&u=l+GKlm;O*UBtrasbHo1ywShsIY3Ya4!w}*4_ zWL;}%-VHJ<9!6O%5r?ab}+tvYY5+T?q`@{hI0 z=ltqJ*9Gs zvEDL(Mth{*5=9~{}*6+Gq>grrikM{gZ z+iU-ix8c*IjqfhH!S!dcX)5DwLCcFIMmfY99NGh z*Sm4LvrK%cPLf%Hx9AJ4Z;=PX-CCb9AC_KgRQzrC+}CqLdBV-gpD7>*@d#GYYEytc>vbp5?G-i+A_ zX{s{~_iz1q**b0Q@rPX<2NXmWy=Lc^w@JRg7Stim4Um&b49HU6-oB5`HORx%BTCxH zr6~TI>fzQ~VJS1aqRh5eNS@lf{eqVAgjc0&|Go@earJ-Yvz6-~xUEkwt$9{^Zq51g zGF?`yD<4;V{_FX7{pG|M`L^AhrgM6n?6nscX$8ffR|q{S_2b^hcki~pdUvmF?o?3m zy!F7v=Jw`BtvSc8b-RY9w1-EpbIDT{`^r1ddr5Eb-1TRA4_DT-+@73q>v+!Hj}I!| zKbUJ_wJ$w=DY&!5-c$B{o+r?@BVocH5jzls`7mTOI^ zdaw4>N4zX%4u8Vccg541=B|9a=<~DUbsG0A*L#`Q<==T6VRBdcRK^UW(901~>q39Z z{Ifw0!Mb}PJy7H=e%@nzl zXPg$Tt9f7)y6TR(#+t9Yw%7*W{(jT2QS)@^Hx ztS;G!FM9d(r9j!c$#>K)Ry4O7{A_jnuv}T@&HG!GpfiACn0<~#KVJOUe64U-)`rbz z)b~|-ZS|aIr5{m!OmlX)_>zdujelIF9nWO@MtzH(W%|8jnN{clg(g2Q8M`fU;8`!L z2PrPg+1+3FYg$(@h8*m35=}kdx@B$3ESH-h^H$B$JR)-I%CX;yF`f7SH0jI~dpg5= z?Ju^KWlf?VrY^X>?3bOpVBq{ub54coZM*;V^Tl(O(wXgc@q1-k_RGDxsw5QwfW^;+GR=CY+hIe$(pLM!M?PcdZDr?b%&5@qCrb z!fgVJxk2$OEZ_}lK-^%q=9p-@sU+`TRZ|oxgPyW)-x7dq7V|dd_R*r zOTo0?ETNz6XIcg?owCn}!!RSj;n;EsIk~$Vg)f6@txq?sEiSNMxP11|RQ|p0nnqzS z966=h58hlSHKivbkB{@*OD;}6Bd>%LZVxkd2Z*pvj!tHG?_y$D-Ej1<=D~@FRyw&l zdrXvEbcw58Ex0IT;Vr8#%Z=0h9*KRM^-S?%*$NZEjG*$9>vya^pv}K?*@ulDKNOWX zgk2gvO?FNHv1=p89uG@Fd&T3wnPgXM?O{_AmA9WC&Aj94Vg)wF*XMS=b!>d}jnLRsWxT`x_s(zx@5qt!vjld~i;! zYpdg)2dx*CpH?&#*&KR#QegSo;+x~qJS@T&f}GR24R~1d-VN9s+C{-xMKL? z9NV31v3GWU-yt@8_TlR2==b}J|KH2Mw|D#BiCfQFAO3oFcf{_^jL+E^jJp;&L{8H< zp4{WEE+HrX_{)zM?+X5ZnECMFzs-l6oBz+>YtBFE@vieM`@hGfrCz%lS{u5!&_v4I z;|>eQ1OZQzlM5>%CaDI?>6h}PX1Y(?Vmz;RM(^AkGdJ#Wet+zp%Ndsn+s79-Zgc6& z3Dox9;zDMXcdHA(-wln8ofYu23FwM*KendL;CAjhWuRqIy$ zdHViO-Tm(iObrV)@@M_sT>k9MPR;E%-nbi7r~L}J-)dm8m$Pl%f=tP2J~ys^wQ)Wi z_(VPFM2civd&9rgzkaly);+xZ{2KX}*8fxTGAfqJoR>JXjhES@#q#>$p6&@sEesh7 z6N#GDT{d`$*#P<>s#6pa{&iw&P4y_3yME> z{uEvE-&9*o&8t#o`<%>xL#w~Uo_i?ppeQX}M>|(I((AH7o?pbtkmKI6-Mh-S=top6 zC_T6*=JcMO4%ylhJQx;Ea5`~f!?PWwvGX5)h>-A8Z0n5a(|tK_#$N68^EnK2Z8x(s z^!6Fw_{rhsmi)>o{>`Uq_v`U?RZG-spPf0LnQ_2Gt;mf#Lh0NN$w^#NGuYEUKF?6m zE>WGiXxEg=uB+RO^OrN6l6qO==PDl2nx{JDyLNL%cqyI^zrx0(r#r5P4I z2OHJq*a~~gTkQDvr;&NSv|Vi)?|Hu?yy2>|OA@8HE_5D{Vc)|caj9$bRWF0C_#GSh zSu9M78Nb|0d9sRGBWqh;Xyv+-whUsNyhm7K&KW+m+90up(=%B~?ySnQa+#>A>#^tW z7rx*7dy)2gnG|pFvtdn})6E*r&AVBAn*GYbg9duBUSGd|%d^?|dF%3H{&SDTlXcf4!uZ|^V{a#%fcN>=`-lQZjA>?&Hj?nL)~#aO1x zEG_FCGo{+pimpiCu&!eGBK_HlJ5leLeVb`Y$tg*_l~aOO20NZfRnEw`(cal;62$`5S7s$vCj; zS+^!Jd_CxYUCN>Is@C~0Oc`gbyaZ)eGZ@&i%;qp-zEpOw=tKi8jITlaWz z|JzjUY2P+q$+-Ztl|SCV_P)6H{;Rh?zw_3&wXJ$_;MX#rZ7Y3Oxy(s;aVGouq8sD3p=iOwms!luazUZj6 z$iaoq-F|yN@Xz+!|Lxl3DyzKBY zZ}O|PE@$=K6j~d*RU|s$>=Et-i!7LR71|%gz24&BGIebvTWI<8tNefVy!!S%eNGQ& zfue%r1_z0fJ0-_>_TJt3^Ig9EpP$R)GJpQCpSjj7{XEnWERQ1Izk9M&Jnn_%uS?$b zOL}|mTwd5Nl*pMTq~OswB|7Qe$|e7}3;tYlTpyRuu=kOOfur2shrMhUc1>NbV{A5c z%h^`8aG{)Y22mOjk+D1_YvQ(ko~U2*Zg%JSx@ULROg%I2rm?g``g!)6V;wvU4;Q`P zCe(9t`>E{3w$;z&|6J_fSNL%1eY>e=Qmmf|@Vk3BCds5FgT$@y_dZ_o1^8D^(r zRw_ueuX}xjwRvT`z$KPLX&i}j6_c10`DV;J5wfiE+70_H=~GUGI85d);5;ztKnw4a z+@@6P6H#KHwux?)-RY;fHBj@b;klicOy2UwZuq-voxD-+g_f1s9qv3XP7RVwJPcL} ztOoz;EWM>9jW^_fU-tLB-S_qD-rd@~G&FG4)Z)t_EQ+cYpHr^>`MHAMWY;nUD_4fM z<$D;omYFKJWXL!)E3A-m6>(J!;#9Mc@ho6*5A)Ps-OaeUIPU%5`L&;RuJf-;Vt+kr zdaeAMHb#!!4nB-`*b=lA(iwN|aXD9*AU@|r%hdVd1y5}I9@}`ha2}hnHuILH;t_qP zS#>5dLN_CBlpIo&-N@0tl97$QwP6k46QQ1qZ(@Hvw8>OD%=m~UgO6c)gA?P1oCDLi zwrK|z$Os6=TyIrlS(GSywy;@9F(s(yX?_<Xm!M7#+(C)9+QhICJ#;yd4+z8zp-l z4&_Q<>gj5^^g$xdMdcCdFf>6;MrpF zyin;9gFv!R)5=06tqdN49_2OJ22M%f@&3QL=ikZy{o(!Q$H(2)OT3QGH1K8cvXZN;6rJo}s=d9fdz*y8>g_2a z&MRB34|4pyq#L&2)|P7#Qig)8OOM*HEmRT~7B4tBp)|Id#bicy{@v35@_#O;U$_5x zQ+7@DL0{fEhh5U-GGvvvd`sW(?b3@Q@8hIoqHQOfYjZz#Eb>~>gw0$hZt_iGT5`N< ziD1Qy4y7JL#sjH6o(zuzLIipi6tcxW=8#r&abnW_xZ>_b8T(5&r~TQzQ}<-~ZQ-0H zM_m-zQhXJz+enAxpEh<7ImhUmDZcTb$+fS`)!*&2npT;VQ=>F--i8RXbj9@BC&XoT z^i4Om@?zUDi?i+Ag z;^S=ACs&8BDy`RAm^wROL3oC(`Trh)nPL}AW;nljlu-3|r8l$fb2c}*y+&qveNWz8 zXgMszs#Y@nrIZP?N{N9SQ=_=-Hnw9cy}WutJ5{v}o)+HFddZN-Sdjao;O>N?xjXuN zs&#VC=jU$PZFMUy?#j)5wRPS(_1Pki)h1-_^c2;aYazJ%PxJoD9~Zd)f4DlWCNbN1 zLXUP+z@^nLD^K*wuW-w^XxPPN>2}mqY|4obuP^dlQ*D~>GVjdOhQln!BriFy+noJ! z8~eVmFUulFj)+F*So&ru1tsPx+NYv-(SR~JcYe%vuvS|a9tdFqPik360| z4tFz3yH(^QJjqn_z!#>LbFLgJPIA#=77~vi_%I9aVKQ32gj-GbB$uT+Uv@!d_T@(R zce_5XH`nvrdui?FH@xf@guBH4{GF}-?{Pl=+}awMG@sT(QkQu(GA1N6yl45_79w`dRs&n)LGL-$aXNeLL5ipkOpx zvF<_o3=JEuk}tgpk00(^eY-Dq S1V66Jk2TEp)PveM6ZN*7atohS6nKyn%9bu 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 B+^zrs literal 6123 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 TYth8Oz`)??>gTe~DWM4f6=bKK diff --git a/themes/seamark/public/web-app-manifest-512x512.png b/themes/seamark/public/web-app-manifest-512x512.png index 25350ec86d33cbb4550018f03e856f75c7fea03b..8a95be97fbe4cd15a2b402f95825c1f43ff5f80d 100644 GIT binary patch literal 10917 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 diff --git a/themes/seamark/templates/components/hero.html b/themes/seamark/templates/components/hero.html new file mode 100644 index 0000000..5423b49 --- /dev/null +++ b/themes/seamark/templates/components/hero.html @@ -0,0 +1,66 @@ +{{ define "hero" }} +{{/* + Hero section component - displays landing page hero for non-authenticated users +*/}} +
    + +
    +

    your beacon at sea.

    + +

    + Push and pull Docker images on the AT Protocol.
    + Browse public registries or control your data. +

    + +
    +
    docker login {{ .RegistryURL }}
    +
    docker push {{ .RegistryURL }}/you/app
    +
    same docker, decentralized
    +
    + + + + +
    +
    +
    + {{ icon "ship" "size-8" }} +
    +

    Works with Docker

    +

    Use docker push & pull. No new tools to learn.

    +
    +
    +
    + {{ icon "anchor" "size-8" }} +
    +

    Your Data

    +

    Join shared holds or captain your own storage.

    +
    +
    +
    + {{ icon "compass" "size-8" }} +
    +

    Discover Images

    +

    Browse and star public container registries.

    +
    +
    +
    + +
    +{{ end }} diff --git a/themes/seamark/theme.css b/themes/seamark/theme.css index ca0cd67..e4dd52a 100644 --- a/themes/seamark/theme.css +++ b/themes/seamark/theme.css @@ -12,3 +12,13 @@ --color-accent: oklch(66.6% 0.121 28); --color-accent-content: oklch(28% 0.121 28); } + +@keyframes rock { + 0%, 100% { transform: translateY(-50%) rotate(-3deg); } + 50% { transform: translateY(-50%) rotate(3deg); } +} + +.animate-rock { + animation: rock 3s ease-in-out infinite; + transform-origin: bottom center; +} -- 2.51.2