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) } - return gc.fetchUserManifestsFromEndpoint(ctx, userDID, pdsEndpoint, gc.pds.DID()) + + 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 "" + } + // 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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 SE9iftOAsRq!v_676aE92t&F+= literal 5629 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 ZqRG6W8U6-kcVpdq8mc-fRZ4He{~xeox#R!< 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 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 TQ9>0diOncV5lMpD#)|&~%6bV7 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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 literal 6123 zcmdUT^;gv2xBk$Gz$i#c3DPx445);JfOPjz(yeq3(v8w6HPiso43d&VBMnM-I&|0E zdA~p3@4f%QUF-hvdYvE6v-a8hInUnftS3rcRi1>1mIwd%W$ zxzCP}pB!oge>_0Xe;g6JFz$-nd8h+om;}!P;iM5|e1TYA!ZMgjOx*orvMKhT@DW zv5{8xm{R?GULV*yp>;Uo!QyMD_AiQaZc!m~n>ef4D@n@Ldz6o31 z;Iz1UI5oUAhzfM#n%3x-+^#Zq#qBMREk!a8WP7kb8jSM)OVJk+%-EKLO!eM^O?MIu~8cL zIV3SXkPa%8S2V~n)5;}X^|!pb0UefvfozPv#treg1`ZfJMw1nzh0Rv#L9yzu|ty-TMjkK!?iinppN{=ol2HDRK z8T#XJ60uqEg)FAmo#dOv%f9~Aul6K1G|pIfm-Et-pTYRB>GELT5YN6L#?;n+q$Bo* zFQGwxCi@2#d3>eBHCkf+#Ur&A0X{rGeheE#-;L|YFwy9>H0vrx9;(j9FG0>plL5;m zz>6cV!!Vxo>xi;ul*Sg{*e`E09v?dBlLn{rhz9q8Goo+Mt`^he5r>Y!`uMP$cPm@k6y=xmHL%&`K|wy{QrBc;4w+!g2)Zauu&DwT^~4t zv0$evzn&0$kMbY3s_J$HjKfbt4h(UPxo*pQ<4GnPj$K!B_{Wajif{;+;zR)fjQ_LP z63L8xj#xwV{qWKFyyQ8QAkye~>FCm!njlF3Z#P4QV36F;Ce~3s7C){v5-+Oo`6ZEhqSzFLVo&eEIOMsKjMA&$Ih(34!oU6AX>^n zfCmes!wkP&y~`=NgP8HSdK!?vnhgAmK}zhrzq*10a9XEt17XFnSv<)XLY2&8Qu@%_ zf=K^3K&jDW)y_w)OhG8Z3tjbnxk(u?%=Kx}Qa;2S5ZGkFR(kI0Wm}3#^8Hmgf9?kb z`q+%(PUG|N-|uqIiQ)HZ7S7eJDss)WN&vujIBi4(M9tAhjuOKhiS1a~7G?Pp9CNlC z1c*WcAlyI|yc-4Gjc-Ke%7=}Ni33b0Q`a)tt&7ZEHhQoWLO%8$bgq^ylCge`VF(x4 z;^N3od*m1y#8Os=lO4V<{wDt1QMv6PTIg^2Kl!_^;pLd!i#_c^p#u*(%(6n&_e=;8 zw<7z~aDqOqnO1fM0crt<{zACRaFh}LL_B81C{^B2snU1=@K3;IoTfl(M?w)}Vk9|a z|22b4$R~BbiyU9q;Sz7>1@KzHH&!T4qJ^Air7b#b-N&ph0J! z=>;eBZdxK}`C&s%dACNVVg+&%1#pU7g@$;aUhBz~6t&Em547Rf`%z#E7uzn9ondRZ zsOv>!;iaF9z8>DFy2PM8H~!?T6=m$nmSBQo&R~q*CQZnfJx@!_R#4Y*p1z+s&hO@oH%ubP1;{KoBMQ?*jFOAtX_)pSL?+<=%D3iH-P5$c_ZU8YyuoZHf)9d zB=C_)+6l|I_^z+Gb8hmENQxmK=*9i{{SPv}B#b((%@nzH`3zYhN$-bVlW*4Ob=PAHC*{@$ z-5nE`C!1Zq75$QM0Yr}6OQ<+#@ZkT%F6jHScD{Vi7*;I)^@B@j$ua{xxUv)6K3Se0 zvi*R_6Kz)h3RrvJU4|^gCSa1QkPH&1R4yYc%!pwK5!lk?*bid*4M&Gh%@iQtuhN5|4O8>Dr{>sK5oITo36TYtb*JZT_T- z*B{dNYYeCfnDCBk0$_JbmF;*TR`0#>Ife+QM?zOg+Q@VgYzGSx^?uTZHQ)P$XImJs zW2i|NS_Lt*typP%5%3ckLaO0J%haK#M$H$DB2U@+Nt-j3T(9y!x~C0a5+%M!Q@g|d zVN4wegmR))(~kp5r+!cdHyyAcNUD{I#OcWMDAq|0kpTcDQd2C=<%_h+j#zT9$S*^M zWoAQ}@rfTfrH3Vznobo43kdOXHYJ6h8UfRuw~}PI0lvHk!C`kn{&M7IbrCkuOahkQ zr{o+J>`6TIpVJh@R{K4Y4u5ApP;`;8rh`6@qx&7X6%xR<+UbsYibRmVZQ@?W; z{Pyji5=kQPSMUP^G&My5io4zi`~tI;;f;qQ>LJYuJi)6fHSV$|Y+*v)sD|Z)G}iwd ze|qwVarF!5N(t#^CxLk#nmT$*4)>|%$u_e%of@!To-Fji)mFG9qc+_1qW4BGRsqlRTKYJ%0kt-W zE;9z|Y%KdBnW`0Lj_e>djpJ$zTzc`h1h$vNh*I$$5=)^oc_83&BEgiLmAba5!?=l7 zy>$hvD)*eqvYXr~<4H|rAt4oiT-z#L4CX-)>hNn0nxnkL^bWMKK%{k#mOcxLmO||) zCeNDGS!qUslY<4(?f)-u$U{i)WVN5%gUwYdko8E|sxNj&(cTvhL^Fmq_XcOAvg0~f zakmp|l#{*8qz=s;{cRFJM!rm(s)3mgv56;-vkKo*I%GUK9twA{?0?E$=>D~48L%xB zs85pJ!s(yy&<77K4Dwz;OD%}02dsd*3Sx;G>{Qi5tu*_JGKq|D*6}#!zA1|WUYhv# zq+KGv0g=UVf2g{QZ}kLD1Y2;UWo$~uM#LqEaD<>Xfe}a#=ydHELHItQEd9vsFkg|b+OePdKeR1{3b?6)Z8rpvaazI!{M*D z@H2@}!~N0!;CBw&=Zejz*e%CvP-ts90DX8G*m48KZ7vkbzH7u)A7%p;fsi6wHRYp@ z#%eoGV_k&~_8G$GayAMl$dOp1E4PZTTxTd&401vj0W#puXa}*}YxiVo)9;uf^XjpO zOul4Z$-h_uY#-JdFs?-lf_E<8fB5H?Z=>aiBRNZ-HO47~T^(z0R z-(%zI(shfe6{!@3*~Q#d$O7dGE5uIsy&J6kZ*)_N6=Om#`N1){Mbz?tKmOg;w=O}x z>NwFK#g78(6^OkzsLb!b+?=y?7VLj$E*Pqe8Xbr5Y%}r*PcEHgHX`U2danss6GjrL+ zL1`B{{3@VU^eRfgk6|e60=oxRFfhrS!TF@R2cOvu?|XXlDOp{EAmXo~?CG5i#S>;h zNygOg${Zb!uo=MGa$&_K8gvhJqHdXjOeeN*618;011;OaK6Qkc_s5I>iRm0|@uQ-x zcb3`fz4zYN(@NyAcLa+*ixTr_jzPB(CXlWFOALqM|RVl%E^z-mJ(c zI0TJl_glPKaqChS@)tcO0EUmz-Ppr%Lzr=SZ!YqjPc{p2G4{58YqsWt?qJw>)yqDpczly5Lw%5Z`M4m zaz^XrbQ)m=^9h(EytVeYn0mfVog7EQqUwL`$E5)vEx&0{!^_jdPBhWP_L2#?bS~kw zJ6HnZ$+QJ`uEmoJt2Nazh(a?}a`LT~0y*+3m&Ao&FvX$$=c&gd$hgW?f4Yv%oFWH3 z&39^mdg0`i(q6tNH|D>FWRDKlH2gai)>HKg+?SZ$rYCc)*WL=DW_{{1?5nLDnly_I zD{+_CpE}Dn58@?F5!FtWUU|W+Iv0L=ingL`f#GI|bTbi}&vpVV>`FJgwv(^z7h9h; zFZpUYeqH>l;Nne8ko)fS+*3zG0P?JEGhk1}7kl||4s>_V7`?zCXv6n`yDsPQnVekN z9D97QwqDn`@Faq`qd0*FbyQichYj*sQF(9&n=mOy z-w@%_+M5fNdavk>SlAjUwJ_g27st{MGGlT!mXNuXAg_QxV^vLtw7X_Rn!BSIiCGuB zCModpL`TApJL%T9WEq1J*#PVd4eI`!S!4Z+qq}d)yI5yaN@VA_hDYAIK`3hk1(zLw z7C|Q5-@Z!@#)3WY&u<_^v)S9s)2H91w z-*ac{!!=lk$|DW4mU|{c@t>i56XDZr`lXBHR_ZA9JS?Tk&?3U+CXaa%v7TE!9R9L* z2Iu2x_~k&J2emN#nKMoQZyghk^A*=xdfp1<%ei4ums8z!>+8g{ov>j#v{)Er8;Rh( zcVqf<8(QDj*j-%Yr8_%;G0vXlsfSQ~1r1r-7S!X#-7ILVa2fgK&g6CqpiXT{f=H3w zS23GJuF(KCz3scP9qY@UnFY}Xi^(}rrjdQ}IVlE-Oh+=BxbE9D1ojj!NYs0tn6(`5 zxPG6Lkox9cJ-Xi5SnSv#SL~wthj^U-Jk|V6p5$EAHIZoxq}bFZhR|EudXJd0!C zuLJ*7i!Xi%Ir_{lkuNsIsGBTgB$6 z^Jl9|Dfc0dUz__6!FEKopZr0(jgHq&2Up+=537zj6d)A20|0RA9lb{qJGFwmzVSZb z$)q>nDa7zTyj8I%V9so|c4OP7L~I?HslMR;_q(D!|J>ljHX?!31&MkP1+lWHtf7=@m^u3&a{KGIzZ@vU*`Xx0QxkBx)plj!^|%tl@v&}FqMXo=uwyVs<{K6AXh zdRp)J21RAszGtg?x`yr4=6~z`gi%s@=E>ts=MSz6z08D9h?_no>*!kal@#xA@uzGL zD6hSCHkG>VucFqtNhsEAe4ygH!6rKPdHEMyd};G2W;O&{c9?B9PN5)x7N|18RLvHm z(VXflIg9gb3_cc1l7w<%&@*JH1H};+;E@6whfhqqBQ-FwFKOmpQMj6Kc`C ztsN;uRG}l!-WhydpMRlZs8J}}`)qpK#fA>b5@P57~D_+yRzRX*=dC}1IWr?aK?+wx<#!G31i{|6y;Q9 JE2Pan{TCHvr=0)* 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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 MKT$5dYx(kj0bUu(xBvhE literal 17825 zcmeHvWmHw)*Y2hp6a=JOKvE^7QBe*cEu9KThjeo=3F(sVPU$`%4bpJvZVugj?nZyV z|F|FC_l|ML{c=CNA2??Vd+oK>TysA2na^5Vemw zH*ak0?rUe$*~3KJ{nz3bLw;c2>8{w|=Z|mT2h{iW^8W2_|L5|5?C?KM@c$GH_sxoF z@|VMF0y9dh<>6ywUDk@fBUvmT5{QyOYp?<6TLtlTrt^)iVjHKCAnOVHUNo%kvfbBw zTUNj-BLIA@Sv&CQ`R*T|7VN&C7iQnrYasaVpWLp$!epK)xQr-kjFNXI{1#C-}pQ4gdk*B))eEU(;N%Vfkl)uCWDe0kZi*2^a_J!lyzS=#hiN!g7M_SlQT1Odj+}5eUPJQX z%{cCdu$!&5La#Ra1tbkuF%Q*JYY{Lm1pvD9)z{HIJc=GQ{o&L5ziI8KmddC(a@Bhb z>f5ZKRid?3qNlTj!B##Vf<8`%=^j0EH&0r^u2D)rB&=@2!Qza_S8&Um3!gsUqCJSo zK8f5(>BnFRwmw*QubJfD6I`DyvXap!#5;ZZ?HzWE*?B~UbK7$q-19Ubw&^pjgF%%H&0q-NM z>ph*Jp|GfvA;d5&m}`K}YjKR#hX4hb_5~+1Ao;who~n^S4*n*q;fSe6dpR~AIbkYP zeU7$sYbcAM&R+Wt7Db5LTaPz56`1pT$$buOBo)q=9{Vn*?N&{ly>xaw;8@;~JfvDF zlA{a6ML89P#ZRAgHiAPMEK~2YRq*+c@B3ZdhOG?qDs__1jupJ!KB~>ye&Z2d;TS3x zP3%EyTHS$okrY$Y+PshiS`sM5Ar_ma-a5toQt+=t;xc=(I7cH~)ku7#ILz6B#V2CYp>36z3 zWHcM#_IZZuE<&17hv($tWodxuV$VsZUVmSx4(*i3zjymR?A!=nE2`;_D{|4%3%ZL_ zZtbu(_f=wyX@Z4T=yJ8VXDa^{;b0m1k3i5U%GuOcsmD7$I_^tj0PmgHFSKrZ+Z*=R zFD{9ei_z{&-H!OU!&ZXn+iM*vCr28mw>VA&S1B<9-^1{1l127NFT$ekhRZMs$SHi+ z_SY9)7@XWB-oW)7ypJNOatDAibMRQj1g>#MZ!F~-sdw1QP)%=G`5YR=$JyoXAO~C2 zlaXBJpI|3(cDhq1%NHhd$%%Ah!czxsE$TX--M(?tC@G@T#Low3pN5JgO?;xj@MUHI z0GW*41@fz{XO^;auG5ECHug(Qm+fV;k)}mA+21)WCpM8-B1}VC5+$2R{Tr*0-?f{+ zK6l&SB)F_B)&wrjwp)kvmp%u8wY%T|OFfo`S?7f$B)C$!T_2%IN=pd~c8~)j-mq(h z=cXI}2D3ay9C2Wyi%G`wNghRi4>4d$8UVuAUFI%|sl_a#3dNWwFT~XW?DAI``Rt9r z{C>dJWqd1Y(=UnXikUIGpXDwlo#HXcMu;nvbvhIP!dQ3$*9Jt6x-^oZ+xDtJH5kDr z$lT;MFE$Wkn8 zzc0Ms6oNTIuj27y`gG9|%n9KjGs9Q5!2^KlFSl1D5AtF=pMOI2G3Q>Q(g^%Djyu+COdn z53z~qyLhG0)r*^vnX%ME$;^{4vg0LD<)|`^03bBrK(MEsHjExSbmWysmZx)brat{f zxaa!vdx687MBTckVj+wkR%_S|V+seXyiYBx!LA zw3jon$f$JFB3T`F5kYC@@w@&$Nc|AcOS-vG7R1*M7&kP72>`fP55Jn+DAL}BTXtdO zOz+(d8*r(mmgxdd!>5wtqbf_$)~P_)|CwuKxndyU{H8K zv^+;?1*fdLMRi#iYLIxMqAhoSlWZd=$cI@{Z7zz(!B&*1a0N>ev`Qj}qPx6;&{<1) zv05Y>D*bY}ryUnIXtpXuk-TzAFa>eAG@4ZIsvp>(?G2zDru7Zk=5_eCuQi z%%0RDl_b6k&W}=Wmh-UK6LL^$J?lo3VGWdXL1}mjW@Wrj<~6noaOwR)$|6ToIYn8s zab>EfEWC}8(txt1Ij8b)uKqaQSsf!(3iOrCMu(%RgYEQL=a(Cf)Bp`pQ*a+7i~6F@ zVf)`_%-A$oj*TTXX=zoC`i3^FhxV$rzQV#$ zr3af{9DMQ@Fun>LT*KxwVt-jxN*@Suy`Fyx#w4*i(JXguM8S_4I?R3 z>l0K-BYKVCi|be!4l3vwXiM^1_t`-Etws)`Xe-1f5{biq^Xj?$81uFnYT?}ti&Aoc zk+wL&?$W&UgTu3-Zmhr4qdnVyQF{2JHYeQe%81#h_1#@t<&jduUL4{b*T?44BwODh zrVR~1ijCJfP=h?)kl~<%p51!pg!!8Wejv1i#H;Lhm74wLEnb=&wM}$WyB0A{k=*3q z=TBlUP^XS3C;bmvkf&;O`Dk|cL5sxJl!-@MHXc9!t#TevbjM(SRJn&y&lX}h{8mPS zm;j(e-^#Jzh$gWS8tuyi!okR_{!J9@q1yL!>(h_SgP1v{+4_jI=r1}uq4TS|EY}d> zO(x>a@>9wboh$oJ+kQoN3KV?&+m1wFm(^Qh=ZE(*4`kar+ZMb0k~CSX&+CY7YMyLJ zrv(8Ok5ag3!#L=KRP^-)n&F#3!b{Nhx3qZ`Y20{k&FNKCcZ87M<5%^hW#zu|-()io zy%}`=&S)b$Q0ME*7IXV+Bdke18t5m`M+2$L5yCawzp{;@JL6sQ&U$tOw8?OaqMF)c z85fv?P^&R$upwCJrIsdMnF+@+OWQ?+P0v98df69$Sfu;dMfo@JxvV!wBEMP{Jnq|o{ za+V)S^F;wSBLZ#W?!No7j!P0}e!kpx!QakUN}qgsjO;#tY@?~_=wELlMgVv*rA1>{ zuE|tiFWk1oD;P$ba_XSE?404*e(T}{5Q0e0{d0;iewl6#>GFHXwTy_&XXVLY5918{ z5($}Killkz;viM4vxNn%5~t#cxn9bNj3Sq`0_Vn}Aif8d8iEEvT9gLOFBdr)7%a3h zF7Q6g~Vomzt_OVt@`Sv}HwulOiky51= zQeulnF0dWW>2I7A*mmtjZp%vSsY`jCFo1Q-_Uo+9Mw2nqo>SI)nay z97eq3$-W|;!VhSZ7>*71xI`OBJf7Ow1gJp()HUo&%3rxCOAgT@w%z%;SX?b(p~YM- z^Kla3y(eaESL!4n#XN2UqiJFdBDKFrR(ax#0v!jv1kZE#N7Zry(`=v?vB7?StH#lA zj>$ss8jI!=L9Wa(ePEKRsp!Nm+H+;nVGHyS6wrzoH%E!t%TPUd2d6)5(O|Jx?16<= z&iMlz{wrp%%?H3y6qu@~t(l))GTyk5or(uS@1p{t?=vxI;D99Q%k4py{pBNkU^)4I z1*fJ<@aL%;18v!2CX`J-sDHJ~{_FEYhbJTx=umW!mGMovtPvc<1Sb1w;VQykciq2{ z{cYkKgA)_rz=kGa4LSNgv2yLkQ@Ll)n^r-$9K#va4&-7h&U)rAwlC^Vw6-09G z$6T|61}S-ss&;XdFs|y#D7^WLc(WAt<)X+l)?l4Cuj@%coyVZlZ{~ zTJOdiMgKB5F91Cg3^|Ldn$K}*-!K(2r94n`5XtdeNd0<;hk*njl4Q(*)#l0#U0YID z{}PosKXA|XXr!h&&K6S5)V?gqe(w+1T~h+bbE6_%{d8BdxN^7cs+&ICro<#N4`vD5 zzPpQbCuWZ9EkU>bZ}vhvq{%-CK*aYf$5$mLz?Xp6Z5#l7P?6{cztGA&kx_J$hHK54 zY>~e<*@mYY>Zo+a&k=*OG*-!{zDYo0*lVF9m$fXUCV&PK5x&~XQfZSA87i=q!HwYl zY9et3=ZJmXX90txb`_+ew4T&+J)C;XJwMM~-{PLd(R95JqEH!-4aR-vwynejx_by) zIiI!ryq2-d9u}=9lF)D}M zr0;s}l)xs_fdFbeVKihcjW5dG(iiUM$j_pLM$af``KlYkM+{Edf%fAwXvi*?V13Rx z@y)~^9;d5`oHqw`>CrDgCPPvzq9M%fCC%n{HorrpKZ>DH)*UYAP&Gz<9YT8#5*2g% z9*q7@u$nj8nbGj9I14E9(~#z6UE98d91{@nm`Bdy`68=rDFHrg=EV>m8aAXmQ*8qpbQ#1g zb6$szhc$?yj(58gI?NPIj+jl4lzTo^WJ9X2=giqY%sKMX)7)dbTU->&CQ*yb_qRfo zOaP5Z`8&ShjkoJB#G})#u1SN(UnS@oIEMzG07`oix1G(%gZxUK6Y!i>=VYC=&&vf7~E5Yiz{d1THiE&*1m~c9I`QB9vLQ~57%UYik3RY zUES=XRl!~W!C>UD3%Kltti(Oh_p8we@$u{jB$KXkR+=}Nn5WMz0^~$^xB1^%+NVer z;y%kpc-l0n~g=DIwmmBdE}xC^vS+u-yWm{3UMG( zV8&W^T9f5nmN$L^VBW$JMB%;Cs)3L}JE3JjjkH0R(;~-}%}A-;)HQK|h{(&#^5T33 zM&FaxDU_?tE~e!Pq6ZT^*iaPEv)M~8!Hhh+dny(d@|ToSs>La_C^C$_-0`;)V7?6S%ojfGTs0cFrF!=p$YJ3ndk#RR0C{O}l>Ksu5&S$~-#hm@R@ zGspC4Eg1HPHYrr^NR+J%R=GI8Qw83E@hVd}*O%Ot%o9A=wu^TYaK6}kt^J1md60%& z&W4K&cj6%*V0H&Ga+vQNp1w}|uGtp?Wv7Ng~N`)3`}Ttx6B z=7p1v2wmZ|4GzGIG&IMl*TlZtnw1sw5zvleMPi3A`;p&jk6d=TCZ!*Lh200C@1bwr zEvuJNOeIOV<;{j^!Y#4(>lJGc{4YAoSL8ECyr05;=#s&SQB^@=Odkt?z(Ya*AY z1g)GZ#To1M59#GPr0}cR8{;>>;!*DeX@DZe_Tx(RTeZh)r`f>0%oUH@Pn*`}JRe=q zAiOo4C4>+?0}WMZ!C<#9R6@M0m?R#xgaG6=QXSh>y54S+JzhHuMrVhMU6T?PZf1v> z#1`jr#B$N1!uLuDaSC3bv09iPLu%%Wj7Vniq4&WRAl7pmvb67RrfZ-6)FF{&3ebbq z&OuXFF(dGB6C;xd`CL4Vd1?)Qy>jX&xSNr) zlA478DSjYx)I>dF1rSHS9&Tg1gRPCB>nV3xDVMW`)Hq&KXXUEhn@{Qy) zU?KWK?WV47n?0Xlq^an`$GLK|{F!jpp{n|Cvm4juC-$936_2WRx)Qi4m}Ucha+b=pPXNID?5$1AZ`c6?h4Lh^17fybwavgA0Lt1W-6S^KYhubPVa9{GJY$dUgQ$0-T=VYOtF9SZ2s^jfp{*ZJUOx*2sY! z!i%_oj#9C15>s*t_37K@@D2MG60D~`+5b>J8D85H;`0R@+oKv2WJyPByo2k=fl^Bd z^T<^Pb0phwIXUzM)9zP5?SlOYO@|QJ3bcotx7S6W{cmv&qunN*$zfnX@Uewk$%Pq? zOJivL$IQ@Rw3H>3r>R0em>h~Edi>>4z=-QQoCk?zs4^C5}yk z`86%BBeNvU-2t4tA=0Ze=ki*rs*A0wfZrZ2U^l6dzFCL@6-^q~IU)6w_RJJ~#Wlj} zQ;2f^A!8ViJL=~Ip)?6tW%2Z7H@>OBZ3=j5y)#SK!*6<|7qrWB8R2@Eo?rH&w+fQ8 z(4j!iTIi@QnMgB$gEtlu)3Ynm4UR4a6R?{l3if%?_ta4)nX2#pO@M3Z`&V3JaO5PO zA=`$MDZj?Y-N34pc;k_z2RB;qOXLMDZu zQBS@P9SS{L8L;e+P^u1b;Q$svC#2@9<4;G!w=s9P($Ujam)%i*rnb50ZlcHe$yT zz76s}jQI(Q+^bJU4`W0GizjCFC|F9Vka#cF^&|*YEmL0 zJmEuk?2hQk?&c4nUL08k(n0wGD^X%iuf}yJO}uKoxZ0q;rURVSABaqapg}QFSPN`& zLZ8@341!i@2dO0Cf0Ay)OGRgp#oVkv+%oLl2X%|?=J0B1X)megiGB5$W_Tt&3uB4f zNuSU#AA#Egp$WfJZW9cJ&fqAzWb2?vP>7CEgNcFH5OzqowD=2zut9)7u8my{Hy4^^E#_x!GF z->>_hW_hamVI+i_@HO~VRKK6#LyM6Mj;Azi zyq#c4`|av50OPT>=a#>xXyaWmG{CJV?SyQcPlr(t5A054BSTVHD3s(mZ8EjnlGe~< z>;~w+M(21@!~dQIxLu9c_h;#Eg}KMqKTR9=SE;#g-iEx~W+&EV0&@Xlm^zm1g1#_U zi2~79q-C<$8(I(30M&T3?^2-C@S66I+FwufpVqJ#ESM~EoLhsTeYr$jAlBteT&?p* zEl9uv`U~SYVLyC+)DcnI34sd(l?wsWvG`(xc-1lwOs?K7`xO@vKPa^aX^1GHy5N;d zXXH26040^VuNAIg5i?!`(4O0oPJ0hVlp;s^FQ!%6{i<3cgv7uAKjFiv;;~CX-yypd z44iBr^TBpm)Xv*AJK|U!OHQ${vj5sxaeQBqv1^l3LX!8~5_u$j2p zfSGfsk)88y_Cpj=#+YfJg8IV^$SIxZyb6da&n~yGVz15@Sudvp1b3PDW9CRIM2D_} z(FJHj=V!4(P9{q}*Clf7UhnQOREd{`I8dSEgQ!RB2w&LkG3hv&3F`l+{F#)iKBmQi z`-n6*5YZ}}qHC&%$u@e8J^HkU4siczvVE%nzdr#Hg051sUP$`YHp__Jm(Ovwa(26) zz72ylR_)>PI#naTs{%0p7#tjmw}Iv@t$Yw{=GQ=K8)p1{fCA`{;v!P-53^hA!P)Y% zyJgP9%}K!95RJwU2+>^YJ~~}iR686nw?g&WN4u{jJgfV;TuV=4zAp?ep-DZbVo~B( zW!6n@n#CgbLKbfW366(>SY4lNcQ%(FpcsMF8J)Un&i;JG+)JYE5|{gq!A7sL2eykS zYja#NYsp%3$0m*vN#!c0U3X8iLO2J~>GQ3-fEYt;w<1&A#g64OzD5S}hJY&iTA2Gk z@`kS%k%BN8G#F<-lAw^qeuz%-gM|b1mP(=SG+rrB7%jGoSA{BrwSOsC`-_j50$YK> zm^taF!kHZ53}rqpN&j?WMX5S#16zSskLVVth(4wl<$uXAUoPBa>K*e6duh z7kJaxU(?){K+4_BLIJpd&L2YD*MRBgDr!GTlifKv`L0Zn^A0O2VA%3IF>kqOSM0r! z{2vCqstYJp!77ulmPnsWsR7Pl@KCMx>N84+E)Y;zG7eCN6+JLTYl=s(cAoAACfTA$ z&Vd#nfQetf6nam0Si3L8FhUU)><-?Ob;2mLvpSr>Mr(+F+tVivac0$5V-uPr3x zBv~<9Z3(dN1-m9pbMj_@!p75WzPworV0}T~D1MZL-pu3pN0Iwg{=wDI`m-3|cH3c* z@87D_fy*klOq{V3 zvj?{|QNT}7dHJs|4Mn%H4AjTDSAbG@Yci5=l%iLNWMA6kN3K=KYAGD_ySa4o za3yM~jKy)tZ%>rgTm0bE6?-MXPl~@XjDG|c_)A;DjjytT^3M-W&$?eN_4!Uo{bFoB zNizSKNXzKz@$qePfhA{HjK&T<5znj|rFL0W6)rFH9l-EADDO%I|JctxrrIF|wNe+= zR$Po+GO`d)C4Si=p94uP4W01K88r5nKvSron`5G`>&TS-x{c+xtDl6VVWALHqt-a# zr!;)n^+)^6=WQQbnD3orrDIDD-354!H$1N0%3`|Bzi~!wz5bS+^kT>yd);QkjVqL! zAFOW2VobY9b`=yf?m{yd07J;6_4A4Z0gqh%4@8(%e+vB!9&~gj@$;$pvgMta_xL*` zI3OW}GDzzj&w*_Dab;R=ztN%bYckte(f#+m#QPz{A-0iz9V6`q&p__QnA&b2lVKT# z5GR^sQ<_45D&}P;Dk>h9Es?;eAz^POnvYWS3b?;mii_8l9A$F7p6uboFR0^#kM&|` zVw%(Uq>|yogZJAjlPZpJi25m-aQl83yDdx8x^X2P_J7t`W zzvS99uJAr3A64;`eDLvigwiJJg#Uc4Rj7p7f!$V?r9N?xGT>=N5Mv%-Y`$WL(9-eeabol;K;hekt zIs!*R`J!o{)I5T((7^V(9R8;6V)9nX{`6WwCKo?mc=VzRJeFg=Q6XS>$eLXJ82`73 zqyO#h^1CD=ugz1^>!+D(7QDDFqFl{!OS*cFf+MIoU;A{0E{IICu60ew$>sK|_f>@7 zesmf>y|^}KS7o~s+M8Su9cR*0APH^u1vhfdDjL1J0y{z?(gt^2H`mR~LbLkCBUTBL zwP3^Nd4?Tf@waMLD7(>S+6})Lg(_I=>M({U;Vq@yDT+}-ToYT=svq^+WHq3aYe1<) z_XppcZ>Q(_kSg)L|9&rs!t$E?{-a+6+3z^4`T2*cZ3_7cbWBGne>GqO+|dSF>@Cg{ zYTc}gm6ZE6Q3Ch!v`)gTJdvNu`5uV=J$!NWfR4-P%m>_Xm~&Q4tn?m>-op`u2J*=; zPrK}XLh(m``8=6uz4(Ncc*Ob$nFP<dTRg2??dD>p!}#?}#p17AZikBGJ`*%vF-$}3snIam>T}Go#?x2j!L=oy zYo|ZF|3@|JqM&Pvi8XKN=b@F*mu*)pDI)k3CBO#1qXe_4aIBE>(FmhO2Vy$8AC}r& zxF7P|7aC&8EM=v}mc3@pFQiZLrH>`R3Yjp?yj4Hb@iw`_e=F1ZyBY}wfB!5VV|Y9W zmXPHxp^$h-BK-2q(fDs&6h7k3gAQRDj|$e>CEnJLy8E!17Zz21Y+4=7FC6oO@SsY@ z9jk8#O5e4R_!|LEzp86NSZb9z%a(vLkIvE0l-7clu*@KR}CL!824xVd*O%C0BT ze~Zj~wG!C--JQ6y`OTE*tiY@=`DA?%(+hNgtU^Rm{QCTJ#GI=hCA{FL8fQcFgG03? zrI$d-v&%&nJ}cV2xXGYmvn@>u0jHP@g7GKg>+A2FIHGmLuoi`4bc78z-Oq<#D!!zt zE#7;#A9W>u5h*2{>CwVG=Hm1MIZX!4&OCj^eRDbOjaMkhX5COM=(RnXu6W~@^=$sI zEpTavpZ9&T*K*kxJfTcbq#x%!tUok4KJ3@xnyHdc)UL24fN4GYz1f~^;4bL0;P5Fr zh^nyp%cgpPpQWg;(WxGTM!yI9=AA22^a8u7} ze0Qj)pu%yFisqppHLe!2FvxZKjD<*=acF`jaz|28$iu|sf`xTh3~kx$#}JB?MNDzY zB(9Zh!9EF*<^u_dm8f+<>F@T-G4h4HAS7phP2i?&;Q@FC#yXW#=&IJ?LiAy|C6{Sq;7c`8^!>+20_tn`!d!ybV*=(pSSR(6Nv zwH$ZCd%0+a#wXcnW}1_zeC!ZZ$Y@d8u!P_EU0p-Gp$u*3S}F-KT;2v-TEA2>^E2Bh zr9eB|YWoDQ{7RpV#tZQlu?Q<(&tBxX(HcP#!423J;B~4_*bw3>3~01cu;+1Y**n@o z_)Tx^o`{Sv2^tChm(1KkxwuJ@%*=+V+6U{%VJaV=dfJd>6H|!3bJp_O(G~aa-iyb~ z)dVQ?-sk`oxc=HAwV7GD@|*D@8HG=NesyX%aClruucZEiiRFAkpfCK1y6BB&vKZC{ zE-*X2)MoHuP94L~v4Pz;jzsDgnr$753nOr(XnA$Skdm6I8aG*2fcT_(m39?R9-%b`^$o>e& zjwD;w^p!*=DEwUjNE>-APnaLX$tb^|*gX1#+cfE9NjWrvJ-r9dMDs(^F<+>avjq^@ zAt-xWKFE-cXf zQhTjYQrLfRNbA#iSj_*f7o9-VAJSZ6`D>0|Cc=XZe999;rrz9J(Uw?wS;Jn)#r(%) zFCL;n8~dlKYt#fr1nLfYii*)*MU!@pzx@)vaXRrd{u;4M+9Y_Q!vGWJs->d;Dgb(h zB2|ycZ1Tm9{$}g(^J27%+Ty&%(Y#eU{BnXYeexsHXAke(9zZE+_UAb|StoDIYi!Hg z+$|=B=`eF(7kC|$=DM#X{`ls$SMFqws7iM3VV4Z3<^t*u&oyh7lZx9!FQhzB%zBf> z0jtUB+8Omrn@s-COB{g(lfR4^|Bm`G8dP9*LTQXMd8`7zo5$1k&VY+A#Z4=CEJL%r zlai4y+I-1!Ia!0VWS_^c`L{L7{06B=PceBbEv}~NS;FHgq%vw1|NZ0>EXIjvP~eUP zDwLoL8;CnP&VuEp@)riVC8Rx*oI?wK$OFl7+Uu^G4Z9Fb$GTM3qF``4udQPd*Ou+cOF)+}0nLJjdN#OqV;kHo9Hu^)kgGGyb<&9Y~@B)2SiolzxwcHx;c(8F_ zQePi0wu^%*;O8ez2;0{+-wl|d)3?2$M7>Bub1~=eZrB%@F!13WNJ3*d7gEtmFs&eg zWV9<;Dv-~(_ZU94(qI6_L(PBz9(QZiy5r0-<(k*2E#KAenIH5=q}3OQw?Hec^viMw zYkBj&k0lW+x@>(_%%(j^y*g_hdoA?kPFUYpKlG{Lu@!5%Wd4}WuBw+rA~Tkof{hGq zOYOE5!QN~9dkB6>sv~n3@nQ4(10T@4aqTnGp7InibRVz69Ib;KMZIAg*^g{MNO+ZG zA3aJgp0r@EBV1dkUhl5zU^JHaW>^Cqqeh2_ixU53fd&IXA&rjSz3ZO2ySwd9j{-Fl z=GgnMSLxsg;74k@0B7pa>F0{+GS{rDm73T`ik{vpb`!^~BrJ=^TFK_yjbrmeMIJWk z4w3hk-)mKT6P`4DrT|yxM|U3{v&ns$5Trr)>nIOSrHZdo!s&{ zO+(m5N&N-GsQ2Xtb%s&Np?Bk}D~>tDA@A*0?ql1Np=R$N_n~%@84VBk)2FTbZ;RN4 zkZjqf)E-g=-~C18?1qZe`6a?b{(2Q9F#S4z*m052+A1P`rRt`DA+xIeU5^%wPslPV zLqEgOb)ipEskZjT(Ddv|TpX^VN8p)+tEwnX;Uh&4x3_ho?xU~_dRDKHFR3LOc~l1( znM|s2A~%A}d2>|J-HS;Y6tNTx`339ANj?1tyo}mMinVCsxt2Eur$6y+A>9tzZ3rB> zN*?+)H$9ceQHEIvgps|mfA-+u*+CPc?!=9^dE-*H+VeBVO^wrc!ofCtxr zh>|HO0tOgw6`s?mkv7I2^N^Gb27+3ZhjTWwYHFRyT2ze65!KC#h5xC z;wEmZxl?nc94nqW`LhKcKY06=0gL6guDay!q3!BxrY}bX@TGTZw1cN?YhKgp?P^ZU zSSBVoNh`gXr5xnVMyo!81kXel9!OCE-%7gizd=*K$+SxgTnjauRTDC_J zsgLj2`HrTDZ{h`+B^T6oVrp??)8jXKp*jpaoxIfO%dO9^a&wYQad1)k2uEE0uESxe5D$PM$^f_f9N zswgq~HV}pxXF6KfXY)hdmlRJdVXo#aZwXK*N42QPvnl_ME1U*!3{6lr!C+5T7-n~8 z7*`s5S3zp$85&^+c}&(|c#OXuqMkkH+5E&y{2Ai%dMfD$JC7%Cb8d@5-tBQwP?qUz z9){~QHwBerwO(nsgk9<g z9T&7Phi_kH2wv1GN)M03`_vO$sBq~yQ2Ye|1_~6(UZE3GkL7_LG z+Q5N1@Z{q_d}C#z*+d#%1_@XOE%tB3DyW;n!79M<=a!9il#)gax^M#3a}OjtVp&?c zfpKL5i5HWY#ChI)Ee%GX4NSzqZc?1M$gb#tjLxB@PI8#ZIZkhOm_lwsZ|GkbR=|MH z{wz7pD&9Ri#*?_WLE?KtJK&6}Ei#X;c=*1{b(cSE;N+VMJlk}erlhh~Q(s@0dx6U5 zl}*xeWHiJ^t7bVm+E0*4we;YyEk2eZw#E%kAZM$qvuQTYNfS?%QCRs3ET6*Qo2~8~ z>*$a&7afj{tp=6%J8z<*wSB?&JPPu>uLmuVQnvz)w@K!*+8c<1d9E=jnXL2MRHvfL z^k_XC>P;3-ULN8C#3^HE5LK_zefHc|OtB<}6qd1$oNZtEN_Z9xB@gg4ZZqw#4ba_x-AaBW1x;5kK(x68nB%~s45`Z6H8%PR-=QcFz z3pAbli?C9M2Z>9=mc=48?aJuxTk{v7ednuik(KkP zQZva>#7$K?>%seU@_C)$jeC((+MR2k_-M{Br{c!NP7MA0w2qxB*qhg};L++sw2>p$ zf!POCb4W=6uf0a}x&$}1v?C{9IPnps)}#-b?C4+=L%v`%Va|mI{SmuQ>rR~(Ow~(2 zDye_cq{ny(Ygf+HXh|}lUXSVh?@$~px_8%N*9ET{&GzGB7nVHeSjC*=trOun>B8-P zi8#a9f9U7{6>#)EL+v~hq>tnMN0pGqqSg23XSvxKCd03EM21~@(@py#yEleg5puRM zJ$7tt{B+)IYP$_<mG2X!j@3oCc;`|L5O z)L1U=Ufqqy1PD-_dbiP%;{T!DvC=-O&eU2KPGg@u*4r936J3n2<83v4XtBMLixuse zPNzBY7W8X39+`b=&U2Zq%qzvhYuMLNNVdVq+vZ=CBd>cVX@IruIHFvH!Qwt@aCOj&8=DW5B_(dpL~qLLBBg{WSllaTg&8=60`gfBH$MnKn#cI6fXGs(0a*`=hih^7W6<=t{i% zPyZ%W{AeJBz*4vtZD^1*Qt~@|MZ9>7QsGU0|Fn4Rk9g||z)DAp;wEF1%sjpMYXu_1 zt;O_K)*U5eR4ad|YkM)N`fCGzHA(*_-=hxoS^lFO_0!GW9m(;F1dA~(Yu)^yZAWhp z>%jCNn{Vs9l)=M?`McvHweEQ%Rhet*RAVPM>ztoHq5MMwPABNl6#%UWgEfl}ZDrF)I7i4V}t_L0RpQWsQ+$`%WOft&$@B?cRQ1X++ zq8x(n_&amFxd>pcO8Zs2v8IM!hPgGUeUEiLci1L{i$)J#GrE(q?`h*<&O1Q+6hlZ0 zUc=SbvwE1{K==GD!aYR-UbBtcIy0jnkzF%E0T&we{}ymqrgz@-7g;c?&;i!kz9Z?n ziH&Lu_ZHc;??xL#9{VG?ipQw6CN1ew$IeuvhxuqPHfj_w3t869tipqP<1_2{YcJ<2 zD=>veie~pJSU(4MW(#^*^DOW+Odz02lS3kdH*-JDtR=O4gR^2p%+^Lq{lJTJ;+_$yDms6~T{f7lF-qq2y%7UP;oRV;3iJ1~|q_dS*p ziQFJS9$t_ks+J-sj#FJqW4ammt`L<+UHWTZ2V~NSX)qqsYKx?#+XUxxQkJ6auCKi@ zU64DmN|+krRZ@;*Ws*BAjPrH;r#`MlmE2txc6X5NaEzCQ?On7AXVvp=8@RHX=S*g# z<`uCN9>$$208eV`oAQQeagW+zMYS?{R643;IqwGhi!jG=qS{`p9z*r9KnC3`TTHZ( zQGCbczOvPqLdSn5lLwaIenZB^FW%eidO1AYu=Mq3lEpj@lI>qJt>}d-))MeBDF6V5 z((V5X@LbKnF+7LnYYhHme3XvO)zr{iw+{7t-Rl9*JEq=d1c>h=_}}WuMT z#xmWO;0eszLOC=KP@21f<(UZ%TOjsZ0t;y|K7G-bJob0&&1#QSm+p|u_Md$#kxS5dJT=Leq<>i_c!GjV**~dqwUJt znWowNj~&Xuvd%X9&)igN zrI|CM4eNE`dS(gs8SH<=gEs&+dJEi`9IR)HL|*u->&1|p3l@mxBkgK2%?Gm|Gze|< z8xpEd4MlH9v7D>-e>axUYD@F#KK@#~t)eoKCP8|Xt2SqZkTRGAV{GZ|I$zBsTSe_p zYlxRkl(+M7Z3;iyq(f8gKJPzs0trRqvi6s%n#|#g?W|W`UE!O&Ddhc04%3g8KdNzR ze<>s%8v1C(VPzg8pKUFqSLW@^5BzwJMfQ-RGb~`SKBK~65b=@FFM|m6R#R&+AX)D< z=(n|(x@0WZHrlX#)L!SLiu?7eu9HLFdd-o$n88k8Tv^lD6GL@g z>PnllEUaOv_4D3ACBxpreb`}rW-rlPd?z<}5~d_o5ZqXvW;pzUvBTC${~Rds1^>PC z{Eru7qJr-g;Qd8p@MXh!?!UDmJpkOkVSOvY{Wmv14tYA;t_?tqH*We{x+af8!Izf) qw^s@O=MsEH6+9~N|DPR@bfvoWnUq9j;2VD6sos|gQbo@VKmIT2b9x2< 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; +}