From 7bc16f791521a86e9132e8e170c370b5c12c78c6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 22 Feb 2026 16:49:02 +0000 Subject: [PATCH 1/4] docs(agents): daily prompt curation and path fixes Executed daily prompt-curator-agent. - Verified file paths in all prompts. - Fixed broken references in: - weekly/bitvid-smoke-agent.md - weekly/bitvid-test-coverage-agent.md - daily/bitvid-content-audit-agent.md - daily/bitvid-deps-security-agent.md - daily/bitvid-const-refactor-agent.md - daily/bitvid-nip-research-agent.md - daily/bitvid-test-audit-agent.md - Updated PROMPT_LIBRARY_STATUS.md. - Added exclusion for task logs in scripts/check-hex.js to prevent lint failures. - Created completion log. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- docs/agents/PROMPT_LIBRARY_STATUS.md | 23 ++++++++++++------- .../daily/bitvid-const-refactor-agent.md | 4 ++-- .../daily/bitvid-content-audit-agent.md | 6 ++--- .../daily/bitvid-deps-security-agent.md | 2 +- .../daily/bitvid-nip-research-agent.md | 2 +- .../prompts/daily/bitvid-test-audit-agent.md | 4 ++-- .../prompts/weekly/bitvid-smoke-agent.md | 2 +- .../weekly/bitvid-test-coverage-agent.md | 4 ++-- ...26-02-22_prompt-curator-agent_completed.md | 21 +++++++++++++++++ scripts/check-hex.js | 3 ++- 10 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 docs/agents/task-logs/daily/2026-02-22_prompt-curator-agent_completed.md diff --git a/docs/agents/PROMPT_LIBRARY_STATUS.md b/docs/agents/PROMPT_LIBRARY_STATUS.md index a6323bf3..4122a240 100644 --- a/docs/agents/PROMPT_LIBRARY_STATUS.md +++ b/docs/agents/PROMPT_LIBRARY_STATUS.md @@ -1,26 +1,33 @@ # Prompt Library Status -Last updated: 2026-02-18 +Last updated: 2026-02-22 Run type: Daily ## Summary - **Total prompts**: 36 (21 daily, 15 weekly) + 2 schedulers -- **Health**: Good. All prompts follow the canonical structure. - Verified `test-audit-agent` update is consistent with repo policy. +- **Health**: Good. Fixed broken path references in multiple prompts. ## Changes This Run ### Verified Fixes -None. +- **`weekly/bitvid-smoke-agent.md`**: Fixed missing `.js` extension in `js/nostrClientFacade.js` reference. +- **`weekly/bitvid-test-coverage-agent.md`**: Updated examples to reference existing files (`js/nostr/utils.js`, `js/services/storageService.js`) instead of placeholders. +- **`daily/bitvid-content-audit-agent.md`**: + - Removed reference to non-existent `content/docs/upload.md`. + - Updated `js/services/uploadService.js` to `js/services/s3UploadService.js`. + - Updated `js/ui/uploadModal.js` to `js/ui/components/UploadModal.js`. +- **`daily/bitvid-deps-security-agent.md`**: Updated `scripts/deps-audit.sh` to `scripts/generate-deps-report.cjs`. +- **`daily/bitvid-const-refactor-agent.md`**: + - Updated regex/grep instructions to be generic. + - Updated target file examples to existing files (`js/nostr/watchHistory.js`, `js/nostr/relayBatchFetcher.js`). +- **`daily/bitvid-nip-research-agent.md`**: Updated test file reference to `tests/dm-decryptor.test.mjs`. +- **`daily/bitvid-test-audit-agent.md`**: Clarified wildcard `js/webtorrent*` to explicit `js/webtorrent.js`. ### Audited Prompts (Sample) -- `daily/bitvid-test-audit-agent.md` (Verified recent update) -- `daily/bitvid-docs-alignment-agent.md` (Verified valid) -- `daily/bitvid-docs-agent.md` (Verified valid) -- `daily/bitvid-known-issues-agent.md` (Verified valid) +- `daily/bitvid-prompt-curator-agent.md` (Self) ## Known Remaining Issues diff --git a/docs/agents/prompts/daily/bitvid-const-refactor-agent.md b/docs/agents/prompts/daily/bitvid-const-refactor-agent.md index 8229957a..06254f6e 100644 --- a/docs/agents/prompts/daily/bitvid-const-refactor-agent.md +++ b/docs/agents/prompts/daily/bitvid-const-refactor-agent.md @@ -100,7 +100,7 @@ DETAILED WORKFLOW (step-by-step) - Generic app-level: `js/constants.js` - Nostr/relay-level: `js/nostr/relayConstants.js` - Cache TTLs: `js/nostr/cachePolicies.js` - - If a more specific domain exists (e.g., `js/services/watchHistoryConstants.js`), prefer creating in that domain. + - If a more specific domain exists (e.g., `js/nostr/watchHistory.js`), prefer creating in that domain. - Choose a clear semantic name. Naming guidelines: - Use all-caps snake-case with domain and unit: e.g., `FAST_RELAY_TIMEOUT_MS`, `DM_DECRYPT_WORKER_MAX_PENDING`, `WATCH_HISTORY_REPUBLISH_MAX_DELAY_MS`, `DEFAULT_RETRY_ATTEMPTS`. - Suffix with `_MS` for millisecond timeouts, `_S` for seconds, `_COUNT`, `_TTL_MS`. @@ -210,7 +210,7 @@ PR / REVIEW CHECKLIST (what maintainers expect) =============================================================================== EXAMPLE: end-to-end flow for 5000ms duplicates -- Discover `5000` appears in `js/relayManager.js`, `js/nostr/watchHistory.js`, `js/nostr/relayClient.js`. +- Discover `5000` appears in `js/relayManager.js`, `js/nostr/watchHistory.js`, `js/nostr/relayBatchFetcher.js`. - Determine these uses are all "fast relay timeout" semantics. - Check `js/nostr/relayConstants.js` — no `FAST_RELAY_TIMEOUT_MS`. Decide to add: ```js diff --git a/docs/agents/prompts/daily/bitvid-content-audit-agent.md b/docs/agents/prompts/daily/bitvid-content-audit-agent.md index 92204a4d..f3946a11 100644 --- a/docs/agents/prompts/daily/bitvid-content-audit-agent.md +++ b/docs/agents/prompts/daily/bitvid-content-audit-agent.md @@ -49,7 +49,7 @@ Each phase below includes concrete steps and deliverables. Goal: build a complete mapping: `/content` page → concrete claims → code locations that implement the claim. Steps: -- List every page in `/content` related to uploading, contributing, or media (e.g., `content/docs/upload.md`, `content/contribute/*`, `content/guides/*`). Use: +- List every page in `/content` related to uploading, contributing, or media (e.g., `content/contribute/*`, `content/guides/*`). Use: ``` rg --hidden --files --glob 'content/**' | rg '/content/' -n @@ -67,7 +67,7 @@ rg --hidden --files --glob 'content/**' | rg '/content/' -n - Licensing & attribution templates and policies - How to edit/delete content and check status - For each claim, find the authoritative code location(s) — the file(s) and function(s) implementing it: -- Upload UI: look under `js/` for upload modal/upload service controllers (e.g., `js/services/uploadService.js`, `js/ui/uploadModal.js`). +- Upload UI: look under `js/` for upload modal/upload service controllers (e.g., `js/services/s3UploadService.js`, `js/ui/components/UploadModal.js`). - API endpoints: search backend server for upload handlers (e.g., `server/` or `api/`). - Storage/processing: check any `torrent/`, `storage/`, or `cloud` integration code. - Moderation code: e.g., `js/userBlocks.js`, moderation service. @@ -77,7 +77,7 @@ rg --hidden --files --glob 'content/**' | rg '/content/' -n - Produce a deliverable CSV or markdown table: ``` -/content/path.md | Claim: "Max file size 100MB" | Code: js/services/uploadService.js#L123-L160 | Verified? (unknown) +/content/path.md | Claim: "Max file size 100MB" | Code: js/services/s3UploadService.js#L123-L160 | Verified? (unknown) ``` diff --git a/docs/agents/prompts/daily/bitvid-deps-security-agent.md b/docs/agents/prompts/daily/bitvid-deps-security-agent.md index 74a5291d..16f292c6 100644 --- a/docs/agents/prompts/daily/bitvid-deps-security-agent.md +++ b/docs/agents/prompts/daily/bitvid-deps-security-agent.md @@ -33,7 +33,7 @@ Create/update these files/folders (commit them in the branch when PRing): - `todo/TODO_.md` — upgrade tasks and statuses. - `decisions/DECISIONS_.md` — rationale for upgrade choices and tradeoffs. - `test_logs/TEST_LOG_.md` — exact commands run and their outputs (timestamped). -- Optionally: `scripts/deps-audit.sh` for reproducible automation. +- Optionally: `scripts/generate-deps-report.cjs` for reproducible automation. Also read `AGENTS.md` and `KNOWN_ISSUES.md` for project-specific caveats (e.g., the repo uses `nostr-tools`, `webtorrent`, `Playwright`, `Tailwind`). Tag these libraries for special handling. diff --git a/docs/agents/prompts/daily/bitvid-nip-research-agent.md b/docs/agents/prompts/daily/bitvid-nip-research-agent.md index e9d495b6..8b460605 100644 --- a/docs/agents/prompts/daily/bitvid-nip-research-agent.md +++ b/docs/agents/prompts/daily/bitvid-nip-research-agent.md @@ -156,7 +156,7 @@ SEARCH PATTERNS & REPO COMMANDS - Example validation commands: - Fetch NIP markdown: `curl -sSL https://raw.githubusercontent.com/nostr-protocol/nips/master/text/0000-nip.md > artifacts/nips/nip-0000.md` - Run a local node test harness: `node test/nostr-specs/validate-serialization.js` - - Run unit tests for nostr helpers: `npm test -- js/nostr/dmDecryptWorker.test.js --runInBand` + - Run unit tests for nostr helpers: `npm test -- tests/dm-decryptor.test.mjs --runInBand` =============================================================================== TESTS & AUTOMATION (what to add) diff --git a/docs/agents/prompts/daily/bitvid-test-audit-agent.md b/docs/agents/prompts/daily/bitvid-test-audit-agent.md index 7c41b13e..0f4033f7 100644 --- a/docs/agents/prompts/daily/bitvid-test-audit-agent.md +++ b/docs/agents/prompts/daily/bitvid-test-audit-agent.md @@ -106,7 +106,7 @@ DAILY WORKFLOW (run every day or as scheduled) - `js/nostr/watchHistory.js` — normalization, payload limits, republish backoff/jitter. - `js/userBlocks.js` — block list parsing/sanitation. - `js/ui/ambientBackground.js` — RAF and visibility gating. - - `js/webtorrent*` / `torrent/app.js` — lazy initialization. + - `js/webtorrent.js` / `torrent/app.js` — lazy initialization. - Parse coverage data (LCOV/coverage-summary) and map production files to coverage percentages. - Flag critical files with coverage < X% (configurable threshold, default 70%) and record in `test-audit/coverage-gaps.json`. @@ -199,7 +199,7 @@ Focus on tests that exercise real behavior for these critical modules (examples - Tests: normalization, payload size limits, republish backoff/jitter handling, serialization. - **Block lists & moderation**: `js/userBlocks.js` - Tests: tag sanitation, self-target filtering, parsing, and dumping behavior. -- **Playback & WebTorrent**: `js/webtorrent*`, `torrent/app.js`, `js/ui/ambientBackground.js` +- **Playback & WebTorrent**: `js/webtorrent.js`, `torrent/app.js`, `js/ui/ambientBackground.js` - Tests: lazy webtorrent init, visibility gating, ensure heavy loops paused when hidden. (If you need file paths to scan, search the `js/` tree for the filenames above). diff --git a/docs/agents/prompts/weekly/bitvid-smoke-agent.md b/docs/agents/prompts/weekly/bitvid-smoke-agent.md index a4f3caba..96513f04 100644 --- a/docs/agents/prompts/weekly/bitvid-smoke-agent.md +++ b/docs/agents/prompts/weekly/bitvid-smoke-agent.md @@ -93,7 +93,7 @@ WORKFLOW (implementation & run steps) - If Playwright is available: - Launch `chromium` headless and open `http://localhost:8000` - Optionally use Playwright page APIs to interact with the UI to trigger login/publish flows - - Alternatively implement a headless Node client using `js/nostrClientFacade` directly to simulate flows without UI. + - Alternatively implement a headless Node client using `js/nostrClientFacade.js` directly to simulate flows without UI. 4) Login with ephemeral keys - Generate ephemeral keypair in memory (do not persist) diff --git a/docs/agents/prompts/weekly/bitvid-test-coverage-agent.md b/docs/agents/prompts/weekly/bitvid-test-coverage-agent.md index e0f92824..57fcc42a 100644 --- a/docs/agents/prompts/weekly/bitvid-test-coverage-agent.md +++ b/docs/agents/prompts/weekly/bitvid-test-coverage-agent.md @@ -146,8 +146,8 @@ PR & COMMIT CONVENTIONS - Branch: `ai/tests--YYYYMMDD` - Commit message examples: - - `test(ai): add unit tests for js/nostr/parseEvent.js (agent)` - - `test(ai): add fake-indexeddb tests for js/state/kvStore.js (agent)` + - `test(ai): add unit tests for js/nostr/utils.js (agent)` + - `test(ai): add fake-indexeddb tests for js/services/storageService.js (agent)` - PR title: `test(ai): add unit tests for ` - PR body must include: - Baseline test/coverage run summary diff --git a/docs/agents/task-logs/daily/2026-02-22_prompt-curator-agent_completed.md b/docs/agents/task-logs/daily/2026-02-22_prompt-curator-agent_completed.md new file mode 100644 index 00000000..8c64bb92 --- /dev/null +++ b/docs/agents/task-logs/daily/2026-02-22_prompt-curator-agent_completed.md @@ -0,0 +1,21 @@ +# prompt-curator-agent - Daily Run + +Date: 2026-02-22 +Status: Completed + +## Summary + +Executed daily prompt curation. Verified paths in all prompt files against the codebase. + +## Changes + +- **Verified Paths**: Scanned all prompts for broken file references. +- **Fixed Issues**: + - `weekly/bitvid-smoke-agent.md`: Fixed missing `.js` extension. + - `weekly/bitvid-test-coverage-agent.md`: Updated examples to real files. + - `daily/bitvid-content-audit-agent.md`: Fixed `docs/upload.md` reference and `js/services/s3UploadService.js`. + - `daily/bitvid-deps-security-agent.md`: Updated `scripts/deps-audit.sh` to `scripts/generate-deps-report.cjs`. + - `daily/bitvid-const-refactor-agent.md`: Updated target file examples. + - `daily/bitvid-nip-research-agent.md`: Updated test file reference. + - `daily/bitvid-test-audit-agent.md`: Clarified wildcard usage. +- **Updated Status**: Updated `docs/agents/PROMPT_LIBRARY_STATUS.md`. diff --git a/scripts/check-hex.js b/scripts/check-hex.js index 070bda19..396d386c 100755 --- a/scripts/check-hex.js +++ b/scripts/check-hex.js @@ -40,7 +40,8 @@ const IGNORED_GLOBS = [ 'test_logs/**', 'context/**', 'decisions/**', - 'todo/**' + 'todo/**', + 'docs/agents/task-logs/**' ]; const gitArgs = [ From e79b2500c8fba49408f40d8315e468eccfac1520 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 22 Feb 2026 17:09:11 +0000 Subject: [PATCH 2/4] fix(ci): install firefox in e2e-headless and fix e2e test isolation - Updated `.github/workflows/ci.yml` to install both chromium and firefox in `e2e-headless` job, resolving missing executable errors. - Updated `js/relayManager.js` to respect `__testRelays__` overrides (via URL/localStorage) during initialization, preventing race conditions where tests fell back to production relays. - Updated `tests/e2e/repro_issue.spec.ts` to use dynamic relay ports based on worker index, preventing `EADDRINUSE` errors in parallel runs. - Updated `scripts/check-hex.js` to ignore `docs/agents/task-logs/**` to prevent lint failures on hex strings in log files. - Executed `prompt-curator-agent` daily task to fix broken paths in prompt library. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- js/relayManager.js | 35 +++++++++++++++++++++++++++++++++++ tests/e2e/repro_issue.spec.ts | 12 ++++++------ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afcbe295..6dc5260c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -478,7 +478,7 @@ jobs: run: npm ci - name: Install Playwright browsers - run: npx playwright install --with-deps chromium + run: npx playwright install --with-deps chromium firefox - name: Run headless E2E tests run: npm run test:e2e diff --git a/js/relayManager.js b/js/relayManager.js index 670962aa..1e37511a 100644 --- a/js/relayManager.js +++ b/js/relayManager.js @@ -197,6 +197,31 @@ function resolveEntryInput(entry) { return { url, mode: deriveMode(mode) }; } +function getTestRelayOverrides() { + if (typeof window === "undefined") return null; + + const params = new URLSearchParams(window.location.search); + const paramRelays = params.get("__testRelays__"); + if (paramRelays) { + return paramRelays + .split(",") + .map((r) => r.trim()) + .filter(Boolean); + } + + try { + const stored = localStorage.getItem("__bitvidTestRelays__"); + if (stored) { + const parsed = JSON.parse(stored); + if (Array.isArray(parsed)) return parsed; + } + } catch { + // Ignore + } + + return null; +} + class RelayPreferencesManager { constructor() { this.entries = []; @@ -209,6 +234,16 @@ class RelayPreferencesManager { return createEntry(normalized, "both"); }); + const testOverrides = getTestRelayOverrides(); + if (testOverrides && testOverrides.length) { + this.defaultEntries = testOverrides + .map((url) => { + const normalized = normalizeRelayUrl(url) || url; + return createEntry(normalized, "both"); + }) + .filter((e) => e.url); + } + // Attempt to load from storage if policy allows const loaded = this.loadFromStorage(); if (loaded && loaded.length) { diff --git a/tests/e2e/repro_issue.spec.ts b/tests/e2e/repro_issue.spec.ts index 966c3a4f..e1ec07e5 100644 --- a/tests/e2e/repro_issue.spec.ts +++ b/tests/e2e/repro_issue.spec.ts @@ -8,17 +8,17 @@ if (!global.WebSocket) { global.WebSocket = WebSocket; } -const RELAY_PORT = 8899; -const RELAY_URL = `ws://127.0.0.1:${RELAY_PORT}`; - test.describe('Embed Player', () => { let relayServer; + let RELAY_URL; - test.beforeAll(async () => { - relayServer = startRelay(RELAY_PORT); + test.beforeEach(async ({}, testInfo) => { + const port = 8900 + (testInfo.workerIndex * 10); + RELAY_URL = `ws://127.0.0.1:${port}`; + relayServer = startRelay(port); }); - test.afterAll(async () => { + test.afterEach(async () => { if (relayServer) { await relayServer.close(); } From e6f5ef253fd15bee1613042ac41411a96ba12852 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 22 Feb 2026 17:27:48 +0000 Subject: [PATCH 3/4] fix(relayManager): robust check for window.location in getTestRelayOverrides - Fixes a unit test regression where `getTestRelayOverrides` crashed in Node.js environments (where `window` exists but `window.location` is undefined). - Updated the check to explicitly verify `window.location` before accessing `.search`. - Verified via `npm run test:unit:shard2`. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/relayManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/relayManager.js b/js/relayManager.js index 1e37511a..7f2cded3 100644 --- a/js/relayManager.js +++ b/js/relayManager.js @@ -198,7 +198,7 @@ function resolveEntryInput(entry) { } function getTestRelayOverrides() { - if (typeof window === "undefined") return null; + if (typeof window === "undefined" || !window.location) return null; const params = new URLSearchParams(window.location.search); const paramRelays = params.get("__testRelays__"); From 218159701a8064c0d3dcdc226f9a27e03485f683 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 22 Feb 2026 17:48:57 +0000 Subject: [PATCH 4/4] fix(ci): restore firefox and prevent dom crashes - Fixed `package.json` to restrict `test:visual` to `visual` project, preventing execution of E2E tests in the `build` job (which lacks firefox). - Fixed `js/ui/components/UploadModal.js` and `js/ui/components/SearchFilterModal.js` with defensive null checks to prevent "Cannot set properties of null" crashes during E2E tests. - Verified `ci.yml` installs firefox for `e2e-headless`. - Verified `js/relayManager.js` handles missing `window.location`. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/ui/components/SearchFilterModal.js | 112 ++++++++++++++------------ js/ui/components/UploadModal.js | 30 +++---- package.json | 4 +- 3 files changed, 77 insertions(+), 69 deletions(-) diff --git a/js/ui/components/SearchFilterModal.js b/js/ui/components/SearchFilterModal.js index 570eccc8..5320a072 100644 --- a/js/ui/components/SearchFilterModal.js +++ b/js/ui/components/SearchFilterModal.js @@ -197,8 +197,8 @@ const formatDateInputValue = (timestampSeconds) => { function handleDateChip(days) { if (days === "all") { - dateStartInput.value = ""; - dateEndInput.value = ""; + if (dateStartInput) dateStartInput.value = ""; + if (dateEndInput) dateEndInput.value = ""; return; } @@ -209,8 +209,8 @@ function handleDateChip(days) { const start = new Date(); start.setDate(now.getDate() - numDays); - dateStartInput.value = formatDateInputValue(Math.floor(start.getTime() / 1000)); - dateEndInput.value = formatDateInputValue(Math.floor(now.getTime() / 1000)); + if (dateStartInput) dateStartInput.value = formatDateInputValue(Math.floor(start.getTime() / 1000)); + if (dateEndInput) dateEndInput.value = formatDateInputValue(Math.floor(now.getTime() / 1000)); } // Helpers for Chips @@ -234,49 +234,53 @@ function syncStateToControls(filters) { const safeFilters = filters || DEFAULT_FILTERS; // Date - dateStartInput.value = formatDateInputValue(safeFilters.dateRange?.after); - dateEndInput.value = formatDateInputValue(safeFilters.dateRange?.before); + if (dateStartInput) dateStartInput.value = formatDateInputValue(safeFilters.dateRange?.after); + if (dateEndInput) dateEndInput.value = formatDateInputValue(safeFilters.dateRange?.before); // Logic to highlight correct date chip if it matches // (Simplified: just highlight 'All Time' if no dates, otherwise clear chips) const hasDateRange = Number.isFinite(safeFilters.dateRange?.after) || Number.isFinite(safeFilters.dateRange?.before); - const dateBtns = dateChipsContainer.querySelectorAll("button"); - dateBtns.forEach(btn => { - if (btn.dataset.days === "all") { - const isActive = !hasDateRange; - btn.setAttribute("aria-pressed", isActive); - btn.dataset.state = isActive ? "on" : "off"; - } else { - // We could try to calculate if range matches 24h/7d etc, but for now just clear them if custom dates are set - btn.setAttribute("aria-pressed", "false"); - btn.dataset.state = "off"; - } - }); + if (dateChipsContainer) { + const dateBtns = dateChipsContainer.querySelectorAll("button"); + dateBtns.forEach(btn => { + if (btn.dataset.days === "all") { + const isActive = !hasDateRange; + btn.setAttribute("aria-pressed", isActive); + btn.dataset.state = isActive ? "on" : "off"; + } else { + // We could try to calculate if range matches 24h/7d etc, but for now just clear them if custom dates are set + btn.setAttribute("aria-pressed", "false"); + btn.dataset.state = "off"; + } + }); + } // Sort - sortSelect.value = safeFilters.sort || "relevance"; + if (sortSelect) sortSelect.value = safeFilters.sort || "relevance"; // Author - authorInput.value = safeFilters.authorPubkeys?.join(", ") || ""; + if (authorInput) authorInput.value = safeFilters.authorPubkeys?.join(", ") || ""; // Tags - tagsInput.value = ""; + if (tagsInput) tagsInput.value = ""; // We separate tags that are in chips vs typed const activeTags = new Set(safeFilters.tags || []); - const tagBtns = tagChipsContainer.querySelectorAll("button"); const chippedTags = new Set(); - tagBtns.forEach(btn => { - const tag = btn.dataset.tag; - chippedTags.add(tag); - const isActive = activeTags.has(tag); - btn.setAttribute("aria-pressed", isActive); - btn.dataset.state = isActive ? "on" : "off"; - }); + if (tagChipsContainer) { + const tagBtns = tagChipsContainer.querySelectorAll("button"); + tagBtns.forEach(btn => { + const tag = btn.dataset.tag; + chippedTags.add(tag); + const isActive = activeTags.has(tag); + btn.setAttribute("aria-pressed", isActive); + btn.dataset.state = isActive ? "on" : "off"; + }); + } // Remaining tags go to input const otherTags = (safeFilters.tags || []).filter(t => !chippedTags.has(t)); - if (otherTags.length) { + if (otherTags.length && tagsInput) { tagsInput.value = otherTags.join(", "); } @@ -284,34 +288,38 @@ function syncStateToControls(filters) { const minSec = safeFilters.duration?.minSeconds; const maxSec = safeFilters.duration?.maxSeconds; - durationShortCheckbox.checked = false; - durationMediumCheckbox.checked = false; - durationLongCheckbox.checked = false; + if (durationShortCheckbox) durationShortCheckbox.checked = false; + if (durationMediumCheckbox) durationMediumCheckbox.checked = false; + if (durationLongCheckbox) durationLongCheckbox.checked = false; - if (minSec === null && maxSec === 300) durationShortCheckbox.checked = true; - if (minSec === 300 && maxSec === 1200) durationMediumCheckbox.checked = true; - if (minSec === 1200 && maxSec === null) durationLongCheckbox.checked = true; + if (minSec === null && maxSec === 300 && durationShortCheckbox) durationShortCheckbox.checked = true; + if (minSec === 300 && maxSec === 1200 && durationMediumCheckbox) durationMediumCheckbox.checked = true; + if (minSec === 1200 && maxSec === null && durationLongCheckbox) durationLongCheckbox.checked = true; // Has - const hasBtns = hasChipsContainer.querySelectorAll("button"); - hasBtns.forEach(btn => { - const type = btn.dataset.has; - let isActive = false; - if (type === "magnet") isActive = safeFilters.hasMagnet === true; - if (type === "url") isActive = safeFilters.hasUrl === true; + if (hasChipsContainer) { + const hasBtns = hasChipsContainer.querySelectorAll("button"); + hasBtns.forEach(btn => { + const type = btn.dataset.has; + let isActive = false; + if (type === "magnet") isActive = safeFilters.hasMagnet === true; + if (type === "url") isActive = safeFilters.hasUrl === true; - btn.setAttribute("aria-pressed", isActive); - btn.dataset.state = isActive ? "on" : "off"; - }); + btn.setAttribute("aria-pressed", isActive); + btn.dataset.state = isActive ? "on" : "off"; + }); + } // NSFW - if (ALLOW_NSFW_CONTENT) { - const nsfwIsOn = safeFilters.nsfw === "true" || safeFilters.nsfw === "only"; - nsfwToggle.setAttribute("aria-checked", nsfwIsOn); - nsfwToggle.classList.toggle("is-on", nsfwIsOn); - } else { - nsfwToggle.setAttribute("aria-checked", "false"); - nsfwToggle.classList.remove("is-on"); + if (nsfwToggle) { + if (ALLOW_NSFW_CONTENT) { + const nsfwIsOn = safeFilters.nsfw === "true" || safeFilters.nsfw === "only"; + nsfwToggle.setAttribute("aria-checked", nsfwIsOn); + nsfwToggle.classList.toggle("is-on", nsfwIsOn); + } else { + nsfwToggle.setAttribute("aria-checked", "false"); + nsfwToggle.classList.remove("is-on"); + } } // Auto-expand Advanced if needed diff --git a/js/ui/components/UploadModal.js b/js/ui/components/UploadModal.js index 40f446d5..2c6920ad 100644 --- a/js/ui/components/UploadModal.js +++ b/js/ui/components/UploadModal.js @@ -517,9 +517,9 @@ export class UploadModal { // Show UI this.sourceSections.progress.classList.remove("hidden"); this.sourceSections.results.classList.remove("hidden"); - this.results.videoUrl.value = "Uploading..."; - this.results.magnet.value = "Pending..."; - this.results.torrentUrl.value = "Pending..."; + if (this.results.videoUrl) this.results.videoUrl.value = "Uploading..."; + if (this.results.magnet) this.results.magnet.value = "Pending..."; + if (this.results.torrentUrl) this.results.torrentUrl.value = "Pending..."; this.updateVideoProgress(0, "Preparing upload..."); @@ -615,7 +615,7 @@ export class UploadModal { provider: providerLabel, prefix: storagePrefix, }); - this.results.videoUrl.value = videoPublicUrl; + if (this.results.videoUrl) this.results.videoUrl.value = videoPublicUrl; this.updateVideoProgress(1, "Video uploaded."); // 5. Handle Torrent Result & Upload .torrent file @@ -670,15 +670,15 @@ export class UploadModal { this.torrentState.magnet = magnet; - this.results.magnet.value = magnet; - this.results.torrentUrl.value = torrentPublicUrl; + if (this.results.magnet) this.results.magnet.value = magnet; + if (this.results.torrentUrl) this.results.torrentUrl.value = torrentPublicUrl; this.updateVideoProgress(1, "Ready to publish!"); } else { this.torrentState.status = 'skipped'; // Failed hash or invalid this.updateVideoProgress(1, "Upload complete (No torrent fallback)."); - this.results.magnet.value = "Not available (Info Hash failed)"; - this.results.torrentUrl.value = "Not available"; + if (this.results.magnet) this.results.magnet.value = "Not available (Info Hash failed)"; + if (this.results.torrentUrl) this.results.torrentUrl.value = "Not available"; } } catch (err) { @@ -699,7 +699,7 @@ export class UploadModal { alert(`Upload failed: ${err.message}`); - this.inputs.file.value = ""; // Reset + if (this.inputs.file) this.inputs.file.value = ""; // Reset } } @@ -782,7 +782,7 @@ export class UploadModal { this.thumbnailUploadState.status = 'complete'; this.thumbnailUploadState.url = publicUrl; - this.inputs.thumbnail.value = publicUrl; + if (this.inputs.thumbnail) this.inputs.thumbnail.value = publicUrl; this.updateThumbnailProgress(1, "Thumbnail uploaded."); @@ -813,18 +813,18 @@ export class UploadModal { } const pct = Math.round(fraction * 100); - this.inputs.progress.value = pct; - this.statusText.uploadPercent.textContent = `${pct}%`; + if (this.inputs.progress) this.inputs.progress.value = pct; + if (this.statusText.uploadPercent) this.statusText.uploadPercent.textContent = `${pct}%`; } updateThumbnailProgress(fraction, text) { - if (text) this.statusText.thumbnailMain.textContent = text; + if (text && this.statusText.thumbnailMain) this.statusText.thumbnailMain.textContent = text; if (fraction === null) return; const pct = Math.round(fraction * 100); - this.inputs.thumbnailProgress.value = pct; - this.statusText.thumbnailPercent.textContent = `${pct}%`; + if (this.inputs.thumbnailProgress) this.inputs.thumbnailProgress.value = pct; + if (this.statusText.thumbnailPercent) this.statusText.thumbnailPercent.textContent = `${pct}%`; } diff --git a/package.json b/package.json index 335c5317..3204275c 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "lint:tailwind-brackets": "node scripts/check-design-tokens.mjs --check=brackets", "lint:tailwind-colors": "node scripts/check-tailwind-colors.mjs", "check:css": "npm run build:css", - "test:visual": "npm run build && npx playwright test", - "test:visual:update": "npm run build && cross-env UPDATE_VISUAL_BASELINES=1 npx playwright test", + "test:visual": "npm run build && npx playwright test --project=visual", + "test:visual:update": "npm run build && cross-env UPDATE_VISUAL_BASELINES=1 npx playwright test --project=visual", "test:e2e": "npm run build && npx playwright test tests/e2e --project=e2e --project=e2e-firefox", "test:e2e:chromium": "npm run build && npx playwright test tests/e2e --project=e2e", "test:e2e:firefox": "npm run build && npx playwright test tests/e2e --project=e2e-firefox",