WebTorrent (handleTorrentStream) hardening:
- Resolve only when playback is actually viable (loadeddata/canplay/playing),
not the instant streamTo() is called. A dead/too-slow swarm is no longer
reported as success, so the caller's fallback can run.
- Pre-playback torrent errors reject (caller falls back); post-playback errors
go to an onPlaybackError hook instead of a dead reject().
- Mid-stream stall watchdog surfaces an onStall hook ("waiting for peers").
- Fix a <video> listener leak: stream listeners are tracked and torn down
before the next stream and on cleanup() (the modal reuses one element).
- Stream the largest decodable file from a multi-file torrent and deselect the
rest (wider ext set: mp4/m4v/webm/mkv/mov/ogv/ogg) so the swarm isn't spent
downloading files we never play.
playbackCoordinator wires onStall/onPlaybackError to user-facing status.
Forced-source deep links (test affordance):
- The main app honors ?v=<nevent>&playback=torrent|url (mirrors the embed
player) to force a playback path; a missing source shows a clear message.
- The video share menu gains "Copy WebTorrent URL" / "Copy CDN URL" items that
copy the forced-source deep link (disabled with a reason when unavailable).
Tests: webtorrent-handlers (resolve-on-readiness, pre/post-ready errors, stall
fires/clears, largest-file select/deselect, no listener accumulation),
playback-forced-source (param normalization), videoMenuRenderers (copy items +
availability). All mutation-verified.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Editing a video with a newly-uploaded file produced a new content-addressed
object and orphaned the old one. editModalController.handleSubmit now removes the
old object (best-effort) when the hosted URL was genuinely replaced — guarded so
it only fires when both old and new URLs are present and different (so clearing
the URL, which may still be referenced by a magnet web-seed, never deletes it),
and it deliberately leaves thumbnails alone (the new note may still point at
them). Reuses R2Service.deleteVideoStorage (owned-objects-only, never throws).
Documented the cleanup contract in AGENTS.md §4a.
Test: tests/ui/edit-modal-storage-cleanup.test.mjs (3). Full suite green.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Ran `npm run format` (no changes)
- Ran `npx stylelint --fix` (no changes)
- Fixed `lint:inline-styles` failure in `tests/ui/components/DeleteModal.test.mjs` by using `Object.defineProperty` for the `style` mock property instead of direct assignment.
- Verified all lint checks pass.
- Task log: `torch/task-logs/daily/2026-02-27T00-06-51Z__style-agent__completed.md`
Co-authored-by: PR0M3TH3AN <[email protected]>
- Replace direct `this.style = {}` assignment with `Object.defineProperty` in `MockElement` class.
- Resolves `npm run lint:inline-styles` failure flagged during daily maintenance.
- No functional changes to test logic.
Co-authored-by: PR0M3TH3AN <[email protected]>
- Fixed direct `.style` assignment in `tests/ui/components/DeleteModal.test.mjs` to comply with linting rules.
- Verified linting and formatting across the repository.
- Published daily task log and completion event via TORCH.
Co-authored-by: PR0M3TH3AN <[email protected]>
Extracted excessive DOM rendering logic from `RevertModal.js` into `revertModalRenderers.js`.
This improves code readability and maintainability by separating the view construction from the modal logic.
Added unit tests for the new renderers using a lightweight DOM mock.
Replaced local d-tag extraction with shared utility `getDTagValueFromTags`.
Co-authored-by: PR0M3TH3AN <[email protected]>
Replaced the local `extractDTagValue` function in `js/ui/components/DeleteModal.js` with the shared `getDTagValueFromTags` utility from `js/nostr/nip71.js` to reduce code duplication and improve maintainability.
Added `tests/ui/components/DeleteModal.test.mjs` with manual DOM mocks to verify the refactor and ensure no regressions.
Co-authored-by: PR0M3TH3AN <[email protected]>
Replaced the local `extractDTagValue` function in `js/ui/components/DeleteModal.js` with the shared `getDTagValueFromTags` utility from `js/nostr/nip71.js` (exported via `js/nostr/index.js`).
Added a new unit test `tests/ui/components/DeleteModal.test.mjs` to verify the d-tag extraction logic and ensure the refactor is correct and safe. This test uses `jsdom` to mock the necessary DOM environment.
Co-authored-by: PR0M3TH3AN <[email protected]>
Refactored tests to prevent global pollution. Cleaned up global variables in after hooks.
- tests/ui/uploadModal-integration.test.mjs: Save and restore global window, document, fetch, etc.
- tests/ui/storageService.test.mjs: Save and restore global indexedDB, crypto, etc.
- Added daily agent report.
Co-authored-by: PR0M3TH3AN <[email protected]>
Decomposed js/ui/components/VideoModal.js by extracting "Similar Video" and "Pointer Key" logic into a new helper module js/ui/components/VideoModalSimilarHelpers.js.
- Reduced VideoModal.js line count from 6334 to 6045.
- Added comprehensive unit tests for extracted helpers.
- Updated check-file-size.mjs baseline.
Co-authored-by: PR0M3TH3AN <[email protected]>
- Refactor `tests/nostr/videoEventBuffer.test.mjs` to safely restore `global.document` using try...finally.
- Refactor `tests/ui/engagement-controller.test.mjs` to safely delete `global.window` using try...finally.
- Fixes potential test pollution that could cause flakes in shared environments.
Ran `npm run test:unit` for verification.
Co-authored-by: PR0M3TH3AN <[email protected]>
- The `setup-localstorage.mjs` test helper polyfills `ResizeObserver` on `globalThis`, which prevented `HashtagStripHelper` from falling back to `window.resize` logic during testing.
- Updated `tests/ui/components/hashtagStripHelper.test.mjs` to temporarily unset `globalThis.ResizeObserver` during tests that verify fallback behavior.
- Ensures the component correctly detects the absence of `ResizeObserver` and falls back to `requestAnimationFrame` or `setTimeout` mechanisms.
Co-authored-by: PR0M3TH3AN <[email protected]>
- Added `tests/ui/components/hashtagStripHelper.test.mjs` to test `ResizeObserver` integration, fallback mechanisms (RAF/setTimeout), and teardown.
- Implemented robust mocks for `ResizeObserver`, `requestAnimationFrame`, and `setTimeout` within `createDom` helper.
- Verified correct behavior for resize events and cleanup.
- Ensured test data matches `collectVideoTags` expected structure.
Co-authored-by: PR0M3TH3AN <[email protected]>
Add comprehensive unit tests for `js/ui/dm/zapHelpers.js` in `tests/ui/dm/zapHelpers.test.mjs`.
- Covers `formatZapAmount` for standard and compact notation.
- Covers `aggregateZapTotals` for aggregation logic and edge cases.
- Covers `normalizeZapReceipt` for input normalization.
- Ensures robust handling of invalid inputs (NaN, null, etc.).
Signed-off-by: Jules <[email protected]>
Co-authored-by: PR0M3TH3AN <[email protected]>
* Update `refreshAllVideoGrids` in `js/app.js` to detect the currently active feed (For You, Kids, Explore, or Recent) and invoke the corresponding loader, preventing staleness when interacting with the UI.
* Refactor `HashtagPreferencesService` mutation methods (`addInterest`, etc.) to be `async` and await the `publish()` call, ensuring state is persisted before returning.
* Remove redundant `persistPreferencesFromMenu` call in `TagPreferenceMenuController`, relying on the service's internal persistence logic.
* Update unit tests for `HashtagPreferencesService` and `TagPreferenceMenuController` to align with the async behavior.
Co-authored-by: PR0M3TH3AN <[email protected]>
Moves `handleAddProfile` logic from the main application class into `ProfileModalController` to improve separation of concerns as per `AGENTS.md`.
- Extracts `handleAddProfile` to `js/ui/profileModalController.js`.
- Updates `ProfileModalController` to handle add account flow internally.
- Removes legacy callback wiring from `js/app.js` and `js/ui/applicationBootstrap.js`.
- Adds unit tests for the extracted logic in `tests/ui/profileModalController-addProfile.test.mjs`.
Co-authored-by: PR0M3TH3AN <[email protected]>
Removed unused exports and functions identified as dead code.
- Removed unused exports from `js/utils/storage.js`, `js/utils/storagePointer.js`, `js/utils/videoTags.js`, `js/ui/moderationCopy.js`.
- Removed unused function `updateTagPillStrip` from `js/ui/components/tagPillList.js` and associated tests.
- Updated `tests/utils/video-tags.test.mjs` to remove test for private function.
- Added `artifacts/depcheck.json` showing dependency analysis.
Co-authored-by: PR0M3TH3AN <[email protected]>
- Refactoring `scripts/check-hex.js` to use git's native search instead of `ripgrep` for better portability and dependency reduction.
- Updating `tests/ui/uploadModal-integration.test.mjs` to ensure DOM isolation between tests using `beforeEach` cleanup, resolving state pollution issues.
Co-authored-by: PR0M3TH3AN <[email protected]>
Refactored the test suite to use `beforeEach` for mocking `global.fetch` and `afterEach` for cleaning up `document.body`. This ensures that `fetch` is available for all tests and that DOM state (specifically elements with duplicate IDs) does not leak between tests, resolving `TypeError: Cannot read properties of null` failures in `UploadModal.bindEvents`.
Co-authored-by: PR0M3TH3AN <[email protected]>
- Explicitly add `@noble/hashes@^2.0.1` to `devDependencies` to satisfy `jsdom`'s peer dependency requirement, resolving the version conflict with `nostr-tools`.
- Update `package-lock.json` to reflect the new dependency resolution.
- Fix `UploadModal` integration tests (`tests/ui/uploadModal-integration.test.mjs`) to robustly handle JSDOM container reuse and `fetch` mocking, preventing false positives in CI.
- Improve `UploadModal.load` robustness by falling back to `lastElementChild` if `querySelector` fails (defensive coding against DOM quirks).
Co-authored-by: PR0M3TH3AN <[email protected]>
Update `tests/ui/video-list-view-popular-tags.test.mjs` to account for
the `#` prefix that `TagPillList` (and by extension `VideoListView`)
adds to tags. This fixes the assertion failures where "Nostr" was
expected but "#Nostr" was found, and updates the tag preference resolver
in the test to handle the prefixed format.
Fixes unit test failures in CI.
Co-authored-by: PR0M3TH3AN <[email protected]>
Update `tests/ui/tag-pill-list.test.mjs` to reflect the behavior of `js/ui/components/tagPillList.js` which normalizes tags with a `#` prefix in datasets and callbacks.
This fixes the `AssertionError` failures where the tests expected raw tag strings (e.g., 'nostr') but received hashtags (e.g., '#nostr').
Co-authored-by: PR0M3TH3AN <[email protected]>
- Add fallback mock to `js/nostrToolsBootstrap.js` for test environments.
- Add guards to `js/ui/components/UploadModal.js` to prevent stale state updates.
- Ensure service calls are awaited in `js/ui/tagPreferenceMenuController.js`.
- Fix `tests/ui/uploadModal-reset.test.mjs` setup and environment.
Co-authored-by: PR0M3TH3AN <[email protected]>
- Add helper text to the S3 storage configuration form to guide users on Endpoint URL format.
- Add 'Force Path Style' checkbox to S3 storage configuration to support MinIO and other S3-compatible providers that require it.
- Update `ProfileModalController` to toggle visibility of these new fields based on the selected provider.
- Update `ProfileModalController` to save and load the `forcePathStyle` setting.
- Update `tests/ui/storageService.test.mjs` to verify persistence of `forcePathStyle`.
- Update CONTRIBUTING.md and README.md to clarify npm run format scope and recommend test shards
- Fix broken unit tests in sessionActor and tagPreferenceMenu to unblock shards
Simplified the Upload Modal by removing inline storage configuration forms.
The modal now displays the active storage provider (read-only) or prompts the user
to configure storage via the Profile Modal if missing.
Changes:
- Refactored `js/ui/components/UploadModal.js` to consume stored credentials directly.
- Updated `components/upload-modal.html` to remove complex forms.
- Updated `tests/ui/uploadModal-integration.test.mjs` to match new UI structure.
- Removed race-prone logic for saving credentials within the upload flow.
Extracted video reaction logic (subscription, optimistic updates, and publishing) from `js/app.js` into a dedicated `ReactionController`.
* Created `js/ui/reactionController.js`.
* Refactored `js/app.js` to usage `ReactionController` and removed legacy methods.
* Added unit tests in `tests/ui/reactionController.test.mjs`.
* Ensured reaction subscription updates on user login/logout.
This follows the `AGENTS.md` guideline to move UI logic to controllers.
Added `ensurePool` mock to `nostrClient` in `tests/ui/profile-modal-moderation-settings.test.mjs` to prevent runtime errors when `ModerationService` is instantiated.
Updated assertions to include `trustedMuteHideThresholds` as it is present in the default settings snapshot.
- Add `#videoListTags` container to `views/explore.html` to support the hashtag helper bar in the Explore view.
- Rename `#recentVideoTags` to `#videoListTags` in `views/most-recent-videos.html` for consistency across views.
- Update `js/ui/videoListViewController.js` and `js/app.js` to reference the new generic `#videoListTags` ID.
- Update `tests/ui/video-list-view-popular-tags.test.mjs` to reflect the ID change.
This enables the hashtag aggregation and interaction features (filtering, muting, following) on the Explore tab, reusing the existing `VideoListView` logic.
Hidden scrollbars for `.video-tag-strip--scroll` in CSS to improve aesthetics.
Implemented a "peek" (bounce) animation in `HashtagStripHelper` that triggers when the tag list is rendered and overflows the container, indicating to the user that the list is scrollable.
Added unit tests for the new logic.
When navigating away from a view, `VideoListView` calls `destroy()` on its `HashtagStripHelper`. Previously, this cleared the `tagStateResolver` and `activateHandler` callbacks. Since `VideoListView` is a singleton and reuses the helper instance, subsequent navigations to the view would result in a broken hashtag strip (neutral colors, no menu on click).
This change modifies `HashtagStripHelper.destroy()` to preserve these configuration callbacks while still cleaning up DOM references and observers.
Also updated `tests/ui/video-list-view-popular-tags.test.mjs` to:
1. Mock `IntersectionObserver` and `ResizeObserver` on `global` to support `VideoCard` in the test environment.
2. Skip the trimming test case, as `VideoListView` now enforces `scrollable: true`.
Fixes the issue where the hashtag pill menu fails to open and pills do not show interest colors after navigation.