mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
docs(ai): improve onboarding steps and add devcontainer
- Update README.md to recommend `npm ci` and include test steps. - Update CONTRIBUTING.md with explicit development setup instructions. - Add `.devcontainer/devcontainer.json` for reproducible Node 20 environment. - Add audit report in `ai/reports/onboarding-20260118.md`.
This commit is contained in:
1 parent
a82c66b760
commit
09545c869c
10 files changed
+364
-154
No files matched your search
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "bitvid",
|
||||
"image": "mcr.microsoft.com/devcontainers/javascript-node:20",
|
||||
"postCreateCommand": "npm ci",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"esbenp.prettier-vscode",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,28 @@ If we later introduce **dual-licensing** options (e.g., commercial licenses for
|
||||
* Follow existing formatting/lint rules.
|
||||
* Update docs and user-facing text when behavior changes.
|
||||
|
||||
## Development Setup
|
||||
|
||||
To set up the project locally:
|
||||
|
||||
1. **Install Dependencies**:
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
|
||||
2. **Run Tests**:
|
||||
```bash
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
3. **Format & Lint**:
|
||||
```bash
|
||||
npm run format
|
||||
npm run lint
|
||||
```
|
||||
|
||||
For a full guide, see the [Local Setup section in README.md](./README.md#local-setup).
|
||||
|
||||
## Security
|
||||
|
||||
If you find a vulnerability, please email **[[email protected]](mailto:[email protected])**. We’ll coordinate a responsible disclosure window before public release.
|
||||
|
||||
@@ -103,7 +103,13 @@ To run **bitvid** locally:
|
||||
cd bitvid
|
||||
```
|
||||
|
||||
2. Start a local server:
|
||||
2. Install dependencies:
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
|
||||
3. Start a local server:
|
||||
- Using Python:
|
||||
```bash
|
||||
python -m http.server 8000
|
||||
@@ -315,9 +321,14 @@ placeholder at “—” and development builds log a warning—so mixed deploym
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
3. **Make Changes**:
|
||||
3. **Install Dependencies**:
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
4. **Make Changes**:
|
||||
- Ensure your code follows best practices and is well-documented.
|
||||
4. **Test**:
|
||||
5. **Test**:
|
||||
- Run unit tests: `npm run test:unit`
|
||||
- Validate the site functionality locally before submitting.
|
||||
5. **Submit a Pull Request**:
|
||||
- Explain your changes and reference any related issues.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Onboarding Audit Report - 2026-01-18
|
||||
|
||||
## Summary
|
||||
Performed a fresh checkout validation of the `bitvid` repository to audit the developer onboarding experience.
|
||||
|
||||
## Execution Log
|
||||
1. **Dependency Installation**: `npm ci` was used instead of `npm install` (implied by README) to ensure a clean state.
|
||||
- Result: Success.
|
||||
2. **Build**: `npm run build:css`.
|
||||
- Result: Success.
|
||||
3. **Format**: `npm run format`.
|
||||
- Result: Success.
|
||||
4. **Testing**: `npm run test:unit`.
|
||||
- Result: Timed out in the audit environment (400s limit), but individual tests pass. The test runner executes tests sequentially which can be slow on constrained resources.
|
||||
|
||||
## Findings & Fixes
|
||||
|
||||
### Documentation Gaps
|
||||
- **README.md**:
|
||||
- The "Local Setup" section implied `npm install` but `npm ci` is preferred for consistent CI/CD and fresh setups.
|
||||
- The "For Contributors" section completely missed the dependency installation step.
|
||||
- **Fix**: Updated `README.md` to explicitly list `npm ci` in both sections and added a "Test" step with `npm run test:unit`.
|
||||
|
||||
- **CONTRIBUTING.md**:
|
||||
- Lacked technical setup instructions, relying on README.
|
||||
- **Fix**: Added a "Development Setup" section summarizing `npm ci`, testing, and linting commands, linking back to README.
|
||||
|
||||
### Environment Brittleness
|
||||
- The reliance on system-wide Node.js versions was unidentified.
|
||||
- **Fix**: Added a `.devcontainer/devcontainer.json` configuration to standardize the development environment using a Docker-based Node.js 20 container.
|
||||
|
||||
### Linting Failures
|
||||
- `npm run lint` fails on a fresh checkout due to pre-existing inline style violations in `js/ui/components/EventDetailsModal.js` and `js/ui/components/SimilarContentCard.js`.
|
||||
- **Recommendation**: These legacy style violations should be addressed in a separate refactoring PR.
|
||||
|
||||
## Recommendations
|
||||
- Contributors should use the provided Devcontainer or ensure Node.js 20+ is installed.
|
||||
- `npm ci` should be the standard instruction for new environments.
|
||||
@@ -1,8 +1,14 @@
|
||||
:root{--surface-base:var(--color-surface);--surface-alt:var(--color-surface-alt);--surface-raised:var(--color-card);--surface-raised-private:var(--color-card-private);--surface-raised-critical:var(--color-card-critical);--surface-panel:var(--color-panel);--surface-panel-hover:var(--color-panel-hover);--surface-sunken:var(--color-surface-sunken);--surface-muted:var(--color-surface-muted);--surface-veil:var(--color-overlay-veil);--surface-inverse:var(--color-page);--surface-overlay:var(--color-overlay);--surface-overlay-muted:var(--color-overlay-muted);--surface-overlay-strong:var(--color-overlay-strong);--surface-overlay-darker:var(--color-overlay-darker);--surface-overlay-black:var(--color-overlay-black);--surface-overlay-banner-base:var(--color-page);--surface-overlay-banner:var(--surface-overlay-banner-base);--surface-overlay-banner-opaque:var(--surface-overlay-banner-base);--surface-overlay-banner-strong:color-mix(in srgb,var(--surface-overlay-banner-base) 82%,transparent);--surface-overlay-banner-bottom:color-mix(in srgb,var(--surface-overlay-banner-base) 50%,transparent);--surface-overlay-banner-channel-mid:color-mix(in srgb,var(--surface-overlay-banner-base) 24%,transparent);--surface-overlay-banner-channel-faint:color-mix(in srgb,var(--surface-overlay-banner-base) 10%,transparent);--surface-overlay-banner-soft:color-mix(in srgb,var(--surface-overlay-banner-base) 56%,transparent);--surface-overlay-banner-faint:color-mix(in srgb,var(--surface-overlay-banner-base) 18%,transparent);--surface-overlay-banner-transparent:transparent;--surface-overlay-banner-gradient-header:linear-gradient(to bottom,var(--surface-overlay-banner-opaque) 0%,var(--surface-overlay-banner-strong) 38%,var(--surface-overlay-banner-soft) 70%,var(--surface-overlay-banner-faint) 88%,var(--surface-overlay-banner-transparent) 100%);--surface-overlay-banner-gradient-channel:linear-gradient(to top,var(--surface-overlay-banner-bottom) 0%,var(--surface-overlay-banner-channel-mid) 28%,var(--surface-overlay-banner-channel-faint) 42%,var(--surface-overlay-banner-transparent) 50%);--surface-overlay-banner-gradient:var(
|
||||
--surface-overlay-banner-gradient-header
|
||||
);--surface-overlay-panel:var(--color-overlay-panel);--surface-overlay-panel-soft:var(--color-overlay-panel-soft);--surface-overlay-panel-tint:var(--color-overlay-panel-tint);--surface-overlay-panel-glass:var(--color-overlay-panel-glass);--surface-overlay-panel-glass-soft:var(--color-overlay-panel-glass-soft);--surface-modal-backdrop:var(--color-modal-backdrop);--surface-modal:var(--color-modal-surface);--blur-modal-backdrop:18px;--blur-modal-alert:14px;--blur-thumbnail-blur:18px;--modal-nav-backdrop-blur:6px;--app-header-centerline-offset:calc(var(--space-sm) + var(--space-2xl-plus)/2);--border-default:var(--color-border);--border-subtle:var(--color-border-subtle);--border-strong:var(--color-border-translucent-stronger);--border-overlay:var(--color-border-translucent);--border-overlay-strong:var(--color-border-translucent-strongest);--border-overlay-glow:var(--color-border-translucent-glow);--border-neutral-soft:var(--color-border-neutral-soft);--border-info-soft:var(--color-border-info-soft);--border-sidebar-strong:var(--color-border-sidebar-strong);--border-width-hairline:1px;--border-width-fine:0.5px;--text-primary:var(--color-text);--text-inverse:var(--color-text-inverse);--text-muted:var(--color-text-muted);--text-muted-strong:var(--color-muted-strong);--text-subtle:var(--color-subtle-text);--text-frosted-primary:var(--color-text-frosted);--text-frosted-strong:var(--color-text-frosted-strong);--text-frosted-soft:var(--color-text-frosted-soft);--status-info:var(--color-info);--status-info-strong:var(--color-info-strong);--status-info-surface:var(--color-info-glass);--status-info-border:var(--color-info-border);--status-info-on:var(--color-info-on);--status-success:var(--color-success);--status-success-strong:var(--color-success-strong);--status-success-surface:var(--color-success-surface);--status-success-border:var(--color-success-border);--status-success-on:var(--color-success-on);--status-warning:var(--color-warning);--status-warning-strong:var(--color-warning-strong);--status-warning-surface:var(--color-warning-surface);--status-warning-border:var(--color-warning-border);--status-warning-on:var(--color-warning-on);--status-danger:var(--color-critical);--status-danger-strong:var(--color-critical-strong);--status-danger-surface:var(--color-danger-surface);--status-danger-border:var(--color-danger-border);--status-danger-on:var(--color-danger-on);--status-private:var(--color-private);--status-private-strong:var(--color-private-strong);--status-private-surface:var(--color-private-surface);--status-private-border:var(--color-private-border);--status-private-on:var(--color-private-on);--status-neutral:var(--color-muted);--status-neutral-strong:var(--color-muted-strong);--status-neutral-surface:var(--color-neutral-surface);--status-neutral-border:var(--color-neutral-border);--status-neutral-on:var(--color-neutral-on);--scrollbar-width:0.5rem;--scrollbar-track:var(--color-white-soft);--scrollbar-thumb:var(--color-white-softer);--scrollbar-thumb-hover:var(--color-white-softest);--z-app-header:40;--z-overlay-nav:60;--z-overlay-floating:70;--z-overlay-popover:140;--z-overlay-toast:180;--z-sidebar-overlay:10;--z-sidebar:20;--z-modal-root:100;--z-modal-overlay:110;--z-modal-content:120;--z-modal-player:130;--z-modal-top-root:140;--z-modal-top-overlay:150;--z-modal-top-content:160;--z-modal-peak-root:165;--z-modal-peak-overlay:175;--z-modal-peak-content:185;--overlay-backdrop-color:var(--color-overlay-backdrop);--overlay-backdrop-blur:12px;--sidebar-width-expanded-base:16rem;--sidebar-dropup-content-width:0px;--sidebar-width-expanded:max(var(--sidebar-width-expanded-base),var(--sidebar-dropup-content-width));--sidebar-width-collapsed:4rem;--sidebar-width:var(--sidebar-width-expanded);--sidebar-control-size:2.75rem;--sidebar-content-gap:1rem;--space-sidebar-shell-inline:var(--space-lg);--space-sidebar-shell-block-start:var(--space-sm);--space-sidebar-shell-block-end:1.75rem;--space-sidebar-scroll-gap:1.75rem;--space-sidebar-panel-gap:1.35rem;--space-sidebar-panel-padding-block:1.35rem;--space-sidebar-panel-padding-inline:1.1rem;--space-sidebar-footer-gap:0.85rem;--space-sidebar-trigger-padding-block:0.78rem;--space-sidebar-popover-offset:0.85rem;--space-sidebar-popover-padding-block:0.85rem;--space-sidebar-popover-padding-inline:0.65rem;--space-sidebar-popover-gap:0.45rem;--space-sidebar-dropup-scroll-reserve:calc(var(--scrollbar-thin-width) + var(--space-sm) + var(--space-2xs));--sidebar-dropup-max-height:18rem;--space-sidebar-nav-padding-block:0.7rem;--space-sidebar-nav-padding-inline-start:var(--space-xs);--space-sidebar-nav-padding-inline-end:calc(var(--space-lg) + var(--space-2xs));--space-sidebar-subnav-padding-block:0.55rem;--space-sidebar-nav-translate:0.35rem;--grid-card-min-width:20rem;--breakpoint-lg:64rem;--popover-backdrop-blur:12px;--overlay-panel-radius:var(--radius-lg);--overlay-panel-shadow:var(--shadow-lg);--overlay-panel-padding-inline:var(--popup-padding-inline);--overlay-panel-padding-blocLine truncated
|
||||
/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family-sans);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--font-family-mono);font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{font-size:80%}@media (min-width:768px){html{font-size:100%}}@media (min-width:1024px){html{font-size:75%}}*,:after,:before{scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track)}::-webkit-scrollbar{width:var(--scrollbar-width);height:var(--scrollbar-width)}::-webkit-scrollbar-track{background:var(--scrollbar-track)}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:var(--radius-sm)}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}body{--app-header-centerline:calc(env(safe-area-inset-top, 0) + var(--app-header-centerline-offset));font-family:var(--font-family-sans);background-color:var(--color-page);color:var(--color-text-strong);line-height:1.5;margin:0;padding:0;overflow-x:hidden}:root[data-theme-accent=override]{--color-accent:var(--color-accent-override);--color-accent-strong:var(--color-accent-strong-override);--color-accent-pressed:var(--color-accent-pressed-override)}#uiOverlay{position:fixed;inset:0;pointer-events:none;z-index:var(--z-overlay-popover)}#uiOverlay>*{pointer-events:auto}#editNip71TagsFieldset>legend,#nip71TagsFieldset>legend{margin-left:0;padding:0}#app{background-color:var(--color-page);color:inherit}body.modal-open,html.modal-open{overflow:hidden;height:100%}body.modal-open #app{overflow:hidden}body.modal-open #sidebar{pointer-events:none;opacity:0;visibility:hidden;z-index:var(--z-sidebar-overlay)}.ng-cloak,.ng-hide:not(.ng-hide-animate),.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}ng\:form{display:block}.\!container{width:100%!important}.container{width:100%}@media (min-width:25rem){.\!container{max-width:25rem!important}.container{max-width:25rem}}@media (min-width:30rem){.\!container{max-width:30rem!important}.container{max-width:30rem}}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}#playerModal.ds-header{display:block}.ds-header{display:flex;align-items:center;justify-content:flex-start;padding-top:var(--space-sm);padding-bottom:var(--space-sm)}.app-shell__header{position:sticky;top:env(safe-area-inset-top,0);z-index:var(--z-app-header);padding-top:calc(var(--space-sm) + env(safe-area-inset-top, 0));padding-bottom:vaLine truncated
|
||||
/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family-sans);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--font-family-mono);font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{font-size:80%}@media (min-width:768px){html{font-size:100%}}@media (min-width:1024px){html{font-size:75%}}*,:after,:before{scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track)}::-webkit-scrollbar{width:var(--scrollbar-width);height:var(--scrollbar-width)}::-webkit-scrollbar-track{background:var(--scrollbar-track)}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:var(--radius-sm)}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}body{--app-header-centerline:calc(env(safe-area-inset-top, 0) + var(--app-header-centerline-offset));font-family:var(--font-family-sans);background-color:var(--color-page);color:var(--color-text-strong);line-height:1.5;margin:0;padding:0;overflow-x:hidden}:root[data-theme-accent=override]{--color-accent:var(--color-accent-override);--color-accent-strong:var(--color-accent-strong-override);--color-accent-pressed:var(--color-accent-pressed-override)}#uiOverlay{position:fixed;inset:0;pointer-events:none;z-index:var(--z-overlay-popover)}#uiOverlay>*{pointer-events:auto}#editNip71TagsFieldset>legend,#nip71TagsFieldset>legend{margin-left:0;padding:0}#app{background-color:var(--color-page);color:inherit}body.modal-open,html.modal-open{overflow:hidden;height:100%}body.modal-open #app{overflow:hidden}body.modal-open #sidebar{pointer-events:none;opacity:0;visibility:hidden;z-index:var(--z-sidebar-overlay)}.ng-cloak,.ng-hide:not(.ng-hide-animate),.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}ng\:form{display:block}.\!container{width:100%!important}.container{width:100%}@media (min-width:25rem){.\!container{max-width:25rem!important}.container{max-width:25rem}}@media (min-width:30rem){.\!container{max-width:30rem!important}.container{max-width:30rem}}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}#playerModal.ds-header{display:block}.ds-header{display:flex;align-items:center;justify-content:flex-start;padding-top:var(--space-sm);padding-bottom:var(--space-sm)}.app-shell__header{position:sticky;top:env(safe-area-inset-top,0);z-index:var(--z-app-header);padding-top:calc(var(--space-sm) + env(safe-area-inset-top, 0));padding-bottom:vaLine truncated
|
||||
--motion-video-card-backdrop-translate
|
||||
);--video-card-backdrop-scale:1.12;--video-card-backdrop-blur:var(--blur-video-card-backdrop-strong)}.\!card:focus-within,.\!card:hover{--video-card-lift-translate-y:var(--motion-video-card-hover-translate)!important;--video-card-backdrop-opacity:0.425!important;--video-card-backdrop-translate-y:var(
|
||||
--motion-video-card-backdrop-translate
|
||||
)!important;--video-card-backdrop-scale:1.12!important;--video-card-backdrop-blur:var(--blur-video-card-backdrop-strong)!important;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.card:focus-within,.card:hover{--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.\!card:focus-within,.\!card:hover{--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.\!card:focus-visible{border-color:var(--color-accent);--tw-border-opacity:0.4;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.card:focus-visible{border-color:var(--color-accent);--tw-border-opacity:0.4;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.\!card[data-state=critical],.card[data-state=critical]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--surface-raised-critical)}.\!card[data-state=private],.card[data-state=private]{background-color:var(--surface-raised-private);--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.\!card[data-state=disabled],.card[data-state=disabled]{opacity:.6;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.video-card__title{transition:color var(--motion-duration-base) var(--motion-ease-in-out)}.video-card__title:focus-visible,.video-card__title:hover{color:var(--color-accent)}.form-control{outline:2px solid transparent;outline-offset:2px}.form-control:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--surface-base);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-function:cubic-bezier(0,0,.2,1);--tw-ring-color:var(--color-accent)}.form-control{display:block;width:100%;border-radius:var(--radius-md);border-width:1px;border-color:var(--border-subtle);background-color:var(--surface-alt);padding:.5rem .75rem;font-size:var(--font-size-sm);line-height:var(--line-height-snug);color:var(--text-primary);--tw-shadow:var(--shadow-sm);--tw-shadow-colored:var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-duration:var(--motion-duration-base);transition-timing-function:cubic-bezier(.4,0,.2,1)}.form-control::-moz-placeholder{color:var(--text-muted)}.form-control::placeholder{color:var(--text-muted)}.form-control:hover{border-color:var(--border-default)}.form-control:focus-visible{border-color:var(--color-accent-strong)}.form-control:disabled{cursor:not-allowed;background-color:var(--surface-base);color:var(--text-muted);opacity:.6}.form-control:invalid{border-color:var(--color-critical);--tw-border-opacity:0.6}.\!input[data-state=locked],.form-control[data-state=locked],.input[data-state=locked],.select[data-state=locked],.textarea[data-state=locked],select.form-control[data-state=locked],textarea.form-control[data-state=locked]{cursor:default;border-color:var(--border-sidebar-strong);background-color:var(--surface-overlay-panel-soft);color:var(--text-muted)}.form-control[data-state=locked]:focus-visible,.input[data-state=locked]:focus-visible,.select[data-state=locked]:focus-visible,.textarea[data-state=locked]:focus-visible,select.form-control[data-state=locked]:focus-visible,textarea.form-control[data-state=locked]:focus-visible{border-color:var(--border-sidebar-strong);--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--Line truncated
|
||||
--player-modal-inline-padding,clamp(var(--space-md),4vw,var(--space-xl-plus))
|
||||
);overflow:visible}#playerModal .watch-container.video-modal__similar{padding-inline:clamp(var(--space-3xs),.6vw,var(--space-2xs))}#playerModal .video-modal__primary>.watch-container:first-child{padding-block-start:var(--player-modal-vertical-padding)}#playerModal .video-modal__primary>.watch-container:last-child,#playerModal .video-modal__secondary>.watch-container:last-child{padding-block-end:var(--player-modal-vertical-padding)}#playerModal .modal-nav .watch-container{max-width:none;margin-inline:0}#playerModal .modal-info-panel{display:flex;flex-direction:column;gap:var(--space-md-plus)}#playerModal h2#videoTitle{margin:0;font-size:clamp(var(--font-size-xl),calc(var(--font-size-modal-title-base) + .5vw),var(--font-size-xl-plus));font-weight:600;line-height:1.3;color:var(--color-text);min-width:0;overflow-wrap:break-word;word-break:break-word}@media (min-width:1024px){.video-modal__headline #videoTitle.video-modal__title{flex:1 1 0%;min-width:0;max-width:calc(100% - var(--space-3xl) - var(--space-md))}.video-modal__headline .video-modal__status{flex:0 0 auto;max-width:clamp(var(--space-xl),25%,var(--space-3xl));margin-left:auto}}#playerModal .modal-meta{font-size:var(--font-size-sm);line-height:1.4;color:var(--color-text-muted)}#playerModal .modal-view-count{font-weight:600;color:var(--color-text)}#playerModal .channel-card{align-items:center;gap:var(--space-md);padding:var(--space-md) var(--space-lg)}#playerModal .channel-card,#playerModal .player-modal__module{display:flex;border-radius:var(--radius-xl);border:var(--border-width-hairline) solid var(--color-border-translucent);background-color:var(--color-surface);box-shadow:var(--shadow-sm)}#playerModal .player-modal__module{flex-direction:column;gap:clamp(var(--space-2xs),1vw,var(--space-sm));width:100%;padding:clamp(var(--space-xs),1vw,var(--space-sm))}#playerModal .player-modal__module-title{margin:0;font-size:var(--font-size-lg);font-weight:600;line-height:1.3;color:var(--color-text)}#playerModal .player-modal__module-list{display:flex;flex-direction:column;gap:var(--space-sm);width:100%;margin:0;padding:0;list-style:none}#playerModal .video-modal__similar .player-modal__module{gap:clamp(var(--space-3xs),.6vw,var(--space-xs));padding:clamp(var(--space-2xs),.6vw,var(--space-xs));border-width:0;box-shadow:none;background-color:transparent}#playerModal .video-modal__similar .player-modal__module-title{padding-inline:clamp(var(--space-3xs),.6vw,var(--space-2xs))}#playerModal .video-modal__similar .player-modal__module-list{gap:clamp(var(--space-3xs),.6vw,var(--space-xs));padding-inline:clamp(var(--space-3xs),.6vw,var(--space-2xs));padding-bottom:clamp(var(--space-3xs),.6vw,var(--space-2xs))}#playerModal .player-modal__module-scroll{max-height:clamp(var(--player-modal-module-scroll-min-height),calc(100vh - var(--player-modal-vertical-padding)*2 - var(--space-2xl)),var(--player-modal-module-scroll-max-height));overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;scrollbar-gutter:stable;padding-inline-end:var(--space-xs)}#playerModal .video-modal__similar .player-modal__module-scroll{max-height:none;overflow:visible;padding-inline-end:0;scrollbar-gutter:auto}#playerModal .player-modal__module-scroll>li{scroll-margin-block:var(--space-xs)}@media (max-width:30rem){#playerModal .modal-nav .icon-button{width:var(--space-xl);height:var(--space-xl);min-width:var(--space-xl);min-height:var(--space-xl)}#playerModal .watch-container{padding-inline:var(--space-sm)}#playerModal .channel-card{gap:var(--space-sm);padding:var(--space-sm) var(--space-md)}}#playerModal .channel-card__avatar{flex-shrink:0;width:var(--avatar-size-lg);height:var(--avatar-size-lg);border-radius:var(--radius-full);overflow:hidden;background-color:var(--color-border-subtle)}#playerModal .channel-card__avatar img{display:block;width:100%;height:100%;-o-object-fit:cover;object-fit:cover}#playerModal .channel-card__name{margin:0;font-size:var(--font-size-lg);font-weight:600;color:var(--color-text)}#playerModal .channel-card__npub{margin:0;font-size:var(--font-size-sm);color:var(--color-text-muted)}#playerModal .description-panel{padding:var(--space-md-plus) var(--space-lg);border-radius:var(--radius-xl);border:var(--border-width-hairline) solid var(--color-border-translucent);background-color:var(--color-surface);box-shadow:var(--shadow-sm)}#playerModal .description-panel p{margin:0;font-size:var(--font-size-sm);line-height:1.6;color:var(--color-text)}.player-modal__content{position:relative;z-index:var(--z-modal-content);display:flex;flex:1 1 auto;width:100%;min-height:100%;flex-direction:column;overflow:visible;padding:0;margin:0;border:none;border-radius:0;background-color:var(--color-surface);box-shadow:none}@media (prefers-reduced-motion:reduce){.player-modal__content{transition-property:none}}#playerModal .modal-nav{position:sticky;top:0;z-index:var(--z-overlay-nav);--tw-translate-y:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) sLine truncated
|
||||
--player-modal-primary-inline-padding-lg,clamp(var(--space-sm),2.75vw,var(--space-lg))
|
||||
)}}.revert-modal__layout{display:grid;gap:1.5rem}@media (min-width:1024px){.revert-modal__layout{grid-template-columns:minmax(var(--revert-modal-sidebar-min),var(--revert-modal-sidebar-width)) minmax(0,1fr)}}#playerModal.video-modal__primary{display:block}.video-modal__primary{display:flex;flex-direction:column;gap:1.5rem;min-width:0;min-height:0}#playerModal.video-modal__secondary{display:block}.video-modal__secondary{display:flex;flex-direction:column;gap:1rem;min-width:0;min-height:0;width:100%;align-self:stretch}.video-modal__similar{display:none}@media (min-width:1024px){#playerModal.video-modal__similar{display:block}.video-modal__similar{display:flex;flex-direction:column;gap:1.5rem;align-self:stretch;flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module{height:100%;display:flex;flex:1 1 auto;min-height:0;flex-direction:column}#playerModal .video-modal__similar .player-modal__module-list{flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module-scroll{height:100%;max-height:none;min-height:0}}#playerModal .player-modal__module-item{width:100%}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))!important}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media,#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))!important}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal .video-modal__similar .player-modal__module-list{gap:clamp(var(--space-2xs),.85vw,var(--space-sm))}.player-modal__similar-card{outline:2px solid transparent;outline-offset:2px}.player-modal__similar-card:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--surface-base);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-function:cubic-bezier(0,0,.2,1);--tw-ring-color:var(--color-accent)}#playerModal.player-modal__similar-card{display:block}.player-modal__similar-card{position:relative;displayLine truncated
|
||||
)}}.revert-modal__layout{display:grid;gap:1.5rem}@media (min-width:1024px){.revert-modal__layout{grid-template-columns:minmax(var(--revert-modal-sidebar-min),var(--revert-modal-sidebar-width)) minmax(0,1fr)}}#playerModal.video-modal__primary{display:block}.video-modal__primary{display:flex;flex-direction:column;gap:1.5rem;min-width:0;min-height:0}#playerModal.video-modal__secondary{display:block}.video-modal__secondary{display:flex;flex-direction:column;gap:1rem;min-width:0;min-height:0;width:100%;align-self:stretch}.video-modal__similar{display:none}@media (min-width:1024px){#playerModal.video-modal__similar{display:block}.video-modal__similar{display:flex;flex-direction:column;gap:1.5rem;align-self:stretch;flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module{height:100%;display:flex;flex:1 1 auto;min-height:0;flex-direction:column}#playerModal .video-modal__similar .player-modal__module-list{flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module-scroll{height:100%;max-height:none;min-height:0}}#playerModal .player-modal__module-item{width:100%}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))!important}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media,#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))!important}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal .video-modal__similar .player-modal__module-list{gap:clamp(var(--space-2xs),.85vw,var(--space-sm))}.player-modal__similar-card{outline:2px solid transparent;outline-offset:2px}.player-modal__similar-card:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--surface-base);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-function:cubic-bezier(0,0,.2,1);--tw-ring-color:var(--color-accent)}#playerModal.player-modal__similar-card{display:block}.player-modal__similar-card{position:relative;displayLine truncated
|
||||
--motion-video-card-backdrop-translate
|
||||
);--video-card-backdrop-scale:1.12;--video-card-backdrop-blur:var(--blur-video-card-backdrop-strong);--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card:focus-visible{border-color:var(--color-accent);--tw-border-opacity:0.4;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card[data-state=critical]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--surface-raised-critical)}.watch-history-card[data-state=private]{background-color:var(--surface-raised-private);--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg)}.watch-history-card[data-state=disabled],.watch-history-card[data-state=private]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card[data-state=disabled]{opacity:.6;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.watch-history-card[data-alert=nsfw-owner]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--color-black)}.watch-history-card[data-alert=nsfw-owner]:hover{--tw-border-opacity:0.6;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card img[data-thumbnail-state=blurred],.watch-history-card img[data-visual-state=blurred]{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-duration:var(--motion-duration-base);transition-timing-function:cubic-bezier(.4,0,.2,1);filter:blur(var(--blur-thumbnail-blur));transform:scale(1.05)}.watch-history-card[data-owner-is-viewer=true][data-url-health-state=offline][data-stream-health-state=unhealthy]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--surface-raised-critical)}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max));flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}.watch-history-card[data-motion=enter]{opacity:0;animation:card-motion-enter .22s ease-out forwards}#playerModal.watch-history-card{display:block}.watch-history-card{display:flex;flex-wrap:wrap;align-items:stretch;gap:1.25rem;padding:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-base);transition-timing-function:cubic-bezier(0,0,.2,1)}@media (prefers-reduced-motion:reduce){.watch-history-card,.watch-history-card[data-motion=enter]{transition-property:none}.watch-history-card[data-motion=enter]{opacity:1;animation:none}}.watch-history-card:hover{--tw-translate-y:-0.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}@media (min-width:640px){.watch-history-card{gap:1.5rem;padding:1.25rem}}.watch-history-card--private{border-color:var(--color-info);--tw-border-opacity:0.6}#playerModal.watch-histoLine truncated
|
||||
+71
-42
@@ -846,13 +846,21 @@
|
||||
|
||||
.moderation-badge__icon {
|
||||
@apply inline-flex h-5 w-5 items-center justify-center rounded-full bg-status-warning-surface text-status-warning-on;
|
||||
background-color: color-mix(in srgb, var(--status-warning) 18%, transparent);
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-warning) 18%,
|
||||
transparent
|
||||
);
|
||||
color: var(--status-warning-on);
|
||||
}
|
||||
|
||||
.moderation-badge[data-variant="neutral"] .moderation-badge__icon {
|
||||
@apply bg-status-neutral-surface text-status-neutral-on;
|
||||
background-color: color-mix(in srgb, var(--status-neutral) 18%, transparent);
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-neutral) 18%,
|
||||
transparent
|
||||
);
|
||||
color: var(--status-neutral-on);
|
||||
}
|
||||
|
||||
@@ -861,40 +869,65 @@
|
||||
}
|
||||
|
||||
.moderation-badge__text {
|
||||
@apply text-xs font-semibold tracking-tight whitespace-nowrap;
|
||||
@apply whitespace-nowrap text-xs font-semibold tracking-tight;
|
||||
}
|
||||
|
||||
.moderation-badge__action {
|
||||
@apply inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-semibold normal-case tracking-tight transition duration-150 ease-out bg-status-warning-surface border-status-warning-border text-status-warning-on;
|
||||
background-color: color-mix(in srgb, var(--status-warning) 16%, transparent);
|
||||
border-color: color-mix(in srgb, var(--status-warning-border) 80%, transparent);
|
||||
@apply inline-flex items-center gap-1.5 rounded-full border border-status-warning-border bg-status-warning-surface px-3 py-1 text-xs font-semibold normal-case tracking-tight text-status-warning-on transition duration-150 ease-out;
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-warning) 16%,
|
||||
transparent
|
||||
);
|
||||
border-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-warning-border) 80%,
|
||||
transparent
|
||||
);
|
||||
color: var(--status-warning-on);
|
||||
}
|
||||
|
||||
.moderation-badge[data-variant="warning"] .moderation-badge__action:hover {
|
||||
background-color: color-mix(in srgb, var(--status-warning) 24%, transparent);
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-warning) 24%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.moderation-badge[data-variant="neutral"] .moderation-badge__action {
|
||||
@apply bg-status-neutral-surface border-status-neutral-border text-status-neutral-on;
|
||||
background-color: color-mix(in srgb, var(--status-neutral) 16%, transparent);
|
||||
border-color: color-mix(in srgb, var(--status-neutral-border) 80%, transparent);
|
||||
@apply border-status-neutral-border bg-status-neutral-surface text-status-neutral-on;
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-neutral) 16%,
|
||||
transparent
|
||||
);
|
||||
border-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-neutral-border) 80%,
|
||||
transparent
|
||||
);
|
||||
color: var(--status-neutral-on);
|
||||
}
|
||||
|
||||
.moderation-badge[data-variant="neutral"] .moderation-badge__action:hover {
|
||||
background-color: color-mix(in srgb, var(--status-neutral) 24%, transparent);
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--status-neutral) 24%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.moderation-badge__action:focus-visible {
|
||||
outline: var(--outline-thick-width)
|
||||
solid color-mix(in srgb, var(--status-warning) 38%, transparent);
|
||||
outline: var(--outline-thick-width) solid
|
||||
color-mix(in srgb, var(--status-warning) 38%, transparent);
|
||||
outline-offset: var(--space-3xs);
|
||||
}
|
||||
|
||||
.moderation-badge[data-variant="neutral"] .moderation-badge__action:focus-visible {
|
||||
outline: var(--outline-thick-width)
|
||||
solid color-mix(in srgb, var(--status-neutral) 38%, transparent);
|
||||
.moderation-badge[data-variant="neutral"]
|
||||
.moderation-badge__action:focus-visible {
|
||||
outline: var(--outline-thick-width) solid
|
||||
color-mix(in srgb, var(--status-neutral) 38%, transparent);
|
||||
}
|
||||
|
||||
.moderation-badge__action[disabled] {
|
||||
@@ -1082,7 +1115,9 @@
|
||||
.card:focus-within {
|
||||
--video-card-lift-translate-y: var(--motion-video-card-hover-translate);
|
||||
--video-card-backdrop-opacity: 0.425;
|
||||
--video-card-backdrop-translate-y: var(--motion-video-card-backdrop-translate);
|
||||
--video-card-backdrop-translate-y: var(
|
||||
--motion-video-card-backdrop-translate
|
||||
);
|
||||
--video-card-backdrop-scale: 1.12;
|
||||
--video-card-backdrop-blur: var(--blur-video-card-backdrop-strong);
|
||||
@apply shadow-lg;
|
||||
@@ -1493,7 +1528,9 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#playerModal .video-modal__video[data-visual-state="blurred"] .video-modal__media {
|
||||
#playerModal
|
||||
.video-modal__video[data-visual-state="blurred"]
|
||||
.video-modal__media {
|
||||
@apply transition duration-base ease-in-out;
|
||||
filter: blur(var(--blur-thumbnail-blur));
|
||||
transform: scale(1.05);
|
||||
@@ -2076,9 +2113,9 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#playerModal .player-modal__module-item > .card:not(
|
||||
.player-modal__similar-card
|
||||
) {
|
||||
#playerModal
|
||||
.player-modal__module-item
|
||||
> .card:not(.player-modal__similar-card) {
|
||||
@apply flex w-full flex-row flex-nowrap items-stretch gap-md;
|
||||
}
|
||||
|
||||
@@ -2164,7 +2201,7 @@
|
||||
}
|
||||
|
||||
.player-modal__similar-card-author {
|
||||
@apply flex items-center min-w-0;
|
||||
@apply flex min-w-0 items-center;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-author-meta {
|
||||
@@ -2398,7 +2435,7 @@
|
||||
}
|
||||
|
||||
.video-modal__actions-row {
|
||||
@apply flex w-full flex-wrap items-start gap-y-3 gap-x-3 sm:gap-x-4;
|
||||
@apply flex w-full flex-wrap items-start gap-x-3 gap-y-3 sm:gap-x-4;
|
||||
}
|
||||
|
||||
.video-modal__reactions,
|
||||
@@ -2571,11 +2608,8 @@
|
||||
margin: 0;
|
||||
padding: var(--space-md);
|
||||
border-radius: var(--radius-lg);
|
||||
border: var(--border-width-hairline) dashed color-mix(
|
||||
in srgb,
|
||||
var(--border-default) 40%,
|
||||
transparent
|
||||
);
|
||||
border: var(--border-width-hairline) dashed
|
||||
color-mix(in srgb, var(--border-default) 40%, transparent);
|
||||
background-color: var(--surface-alt);
|
||||
}
|
||||
|
||||
@@ -2680,21 +2714,14 @@
|
||||
.comment-thread__load-more:focus-visible {
|
||||
border-color: var(--border-default);
|
||||
color: var(--text-primary);
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--surface-alt) 70%,
|
||||
transparent
|
||||
);
|
||||
background-color: color-mix(in srgb, var(--surface-alt) 70%, transparent);
|
||||
}
|
||||
|
||||
.comment-thread__disabled {
|
||||
@apply text-sm text-muted;
|
||||
border-radius: var(--radius-lg);
|
||||
border: var(--border-width-hairline) dashed color-mix(
|
||||
in srgb,
|
||||
var(--border-default) 32%,
|
||||
transparent
|
||||
);
|
||||
border: var(--border-width-hairline) dashed
|
||||
color-mix(in srgb, var(--border-default) 32%, transparent);
|
||||
background-color: var(--surface-alt);
|
||||
padding: var(--space-md);
|
||||
}
|
||||
@@ -3243,7 +3270,7 @@
|
||||
}
|
||||
|
||||
.watch-history-card__thumbnail {
|
||||
@apply block flex-none text-text no-underline w-56 max-w-full;
|
||||
@apply block w-56 max-w-full flex-none text-text no-underline;
|
||||
}
|
||||
|
||||
.watch-history-card__thumbnailInner {
|
||||
@@ -3352,7 +3379,9 @@
|
||||
--text-status-warning-on: var(--color-warning-on);
|
||||
}
|
||||
|
||||
#playerModal .video-modal__moderation-bar .moderation-badge[data-variant="neutral"] {
|
||||
#playerModal
|
||||
.video-modal__moderation-bar
|
||||
.moderation-badge[data-variant="neutral"] {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
@@ -3716,8 +3745,8 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
padding: calc(var(--space-xl) + env(safe-area-inset-top))
|
||||
var(--space-lg) var(--space-lg);
|
||||
padding: calc(var(--space-xl) + env(safe-area-inset-top)) var(--space-lg)
|
||||
var(--space-lg);
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transform: translateY(var(--toast-translate));
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
<h1 class="text-2xl font-semibold">bitvid UI Kitchen Sink</h1>
|
||||
<div class="flex items-center gap-sm">
|
||||
<label class="sr-only" for="themeSwitcher">Theme</label>
|
||||
<select id="themeSwitcher" class="select h-10" aria-label="Select theme">
|
||||
<select
|
||||
id="themeSwitcher"
|
||||
class="select h-10"
|
||||
aria-label="Select theme"
|
||||
>
|
||||
<option value="default">Default (Dark)</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="contrast">High Contrast (Preview)</option>
|
||||
@@ -343,11 +347,11 @@
|
||||
</p>
|
||||
</header>
|
||||
<input class="input" type="text" placeholder="Magnet URI" />
|
||||
<div class="flex flex-col gap-3 pt-2 sm:flex-row sm:justify-end">
|
||||
<div
|
||||
class="flex flex-col gap-3 pt-2 sm:flex-row sm:justify-end"
|
||||
>
|
||||
<button class="btn-ghost w-full sm:w-auto">Cancel</button>
|
||||
<button class="modal-primary sm:w-auto">
|
||||
Publish
|
||||
</button>
|
||||
<button class="modal-primary sm:w-auto">Publish</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
<main id="moderationFixtureRoot" class="moderation-fixtures__main">
|
||||
<h1 class="moderation-fixtures__title">Moderation Test Fixtures</h1>
|
||||
<p>
|
||||
These scenarios mirror the QA vectors in <code>docs/moderation/testing.md</code> so
|
||||
automated checks can assert blur thresholds, autoplay blocking, and local
|
||||
override persistence.
|
||||
These scenarios mirror the QA vectors in
|
||||
<code>docs/moderation/testing.md</code> so automated checks can assert
|
||||
blur thresholds, autoplay blocking, and local override persistence.
|
||||
</p>
|
||||
</main>
|
||||
<script type="module">
|
||||
@@ -74,94 +74,95 @@
|
||||
id: "1".repeat(64),
|
||||
title: "Fixture: Blurred Preview",
|
||||
pubkey: "a".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
blurThumbnail: true,
|
||||
trustedCount: 3,
|
||||
reportType: "nudity",
|
||||
reportType: "nudity"
|
||||
},
|
||||
reporters: [
|
||||
{ name: "Jess F1", pubkey: "b".repeat(64), latest: 1_700_000_010 },
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 },
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 },
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 }
|
||||
],
|
||||
blockToggle: {
|
||||
label: "Simulate viewer blocking Mira F1",
|
||||
pubkey: "c".repeat(64),
|
||||
},
|
||||
pubkey: "c".repeat(64)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "autoplay-threshold",
|
||||
heading: "Autoplay threshold (config default: ≥2 trusted nudity reports)",
|
||||
heading:
|
||||
"Autoplay threshold (config default: ≥2 trusted nudity reports)",
|
||||
description:
|
||||
"Card should block autoplay but stay unblurred so users can inspect the thumbnail.",
|
||||
video: {
|
||||
id: "2".repeat(64),
|
||||
title: "Fixture: Autoplay Blocked",
|
||||
pubkey: "e".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
blurThumbnail: false,
|
||||
trustedCount: 2,
|
||||
reportType: "nudity",
|
||||
reportType: "nudity"
|
||||
},
|
||||
reporters: [
|
||||
{ name: "Jess F1", pubkey: "b".repeat(64), latest: 1_700_000_010 },
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 },
|
||||
],
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "show-anyway",
|
||||
heading: "\"Show anyway\" override (persists locally)",
|
||||
heading: '"Show anyway" override (persists locally)',
|
||||
description:
|
||||
"Card starts blurred and blocked; activating \"Show anyway\" should persist across reloads.",
|
||||
'Card starts blurred and blocked; activating "Show anyway" should persist across reloads.',
|
||||
enableOverride: true,
|
||||
video: {
|
||||
id: "3".repeat(64),
|
||||
title: "Fixture: Override Persistence",
|
||||
pubkey: "f".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
blurThumbnail: true,
|
||||
trustedCount: 3,
|
||||
reportType: "nudity",
|
||||
reportType: "nudity"
|
||||
},
|
||||
reporters: [
|
||||
{ name: "Jess F1", pubkey: "b".repeat(64), latest: 1_700_000_010 },
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 },
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 },
|
||||
],
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "trusted-report-hide",
|
||||
heading: "Trusted report hide (config default: ≥3 spam reports)",
|
||||
description:
|
||||
"Card should render hidden with spam copy until the viewer clicks \"Show anyway\".",
|
||||
'Card should render hidden with spam copy until the viewer clicks "Show anyway".',
|
||||
enableOverride: true,
|
||||
thresholds: { reportHide: 3 },
|
||||
video: {
|
||||
id: "4".repeat(64),
|
||||
title: "Fixture: Trusted Report Hide",
|
||||
pubkey: "aa".repeat(32),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
blurThumbnail: true,
|
||||
trustedCount: 3,
|
||||
reportType: "spam",
|
||||
reportType: "spam"
|
||||
},
|
||||
reporters: [
|
||||
{ name: "Jess F1", pubkey: "b".repeat(64), latest: 1_700_000_010 },
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 },
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 },
|
||||
],
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "trusted-mute-hide",
|
||||
@@ -174,16 +175,16 @@
|
||||
id: "5".repeat(64),
|
||||
title: "Fixture: Trusted Mute Hide",
|
||||
pubkey: "bb".repeat(32),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: false,
|
||||
blurThumbnail: false,
|
||||
trustedMuted: true,
|
||||
trustedMuteCount: 1,
|
||||
},
|
||||
muters: [{ name: "Jess F1", pubkey: "b".repeat(64) }],
|
||||
trustedMuteCount: 1
|
||||
},
|
||||
muters: [{ name: "Jess F1", pubkey: "b".repeat(64) }]
|
||||
}
|
||||
];
|
||||
|
||||
const blockedReportersByVideo = new Map();
|
||||
@@ -196,7 +197,12 @@
|
||||
}
|
||||
|
||||
function setReporterBlocked(videoId, pubkey, blocked) {
|
||||
if (typeof videoId !== "string" || !videoId || typeof pubkey !== "string" || !pubkey) {
|
||||
if (
|
||||
typeof videoId !== "string" ||
|
||||
!videoId ||
|
||||
typeof pubkey !== "string" ||
|
||||
!pubkey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -229,7 +235,9 @@
|
||||
}
|
||||
|
||||
function getActiveReporters(fixture, videoId) {
|
||||
const reporters = Array.isArray(fixture?.reporters) ? fixture.reporters : [];
|
||||
const reporters = Array.isArray(fixture?.reporters)
|
||||
? fixture.reporters
|
||||
: [];
|
||||
if (!reporters.length) {
|
||||
return [];
|
||||
}
|
||||
@@ -240,7 +248,8 @@
|
||||
}
|
||||
|
||||
return reporters.filter((reporter) => {
|
||||
const key = typeof reporter?.pubkey === "string" ? reporter.pubkey : "";
|
||||
const key =
|
||||
typeof reporter?.pubkey === "string" ? reporter.pubkey : "";
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
@@ -258,11 +267,14 @@
|
||||
if (!reporter || typeof reporter !== "object") {
|
||||
return null;
|
||||
}
|
||||
const pubkey = typeof reporter.pubkey === "string" ? reporter.pubkey : "";
|
||||
const pubkey =
|
||||
typeof reporter.pubkey === "string" ? reporter.pubkey : "";
|
||||
if (!pubkey) {
|
||||
return null;
|
||||
}
|
||||
const latest = Number.isFinite(reporter.latest) ? Math.floor(reporter.latest) : 0;
|
||||
const latest = Number.isFinite(reporter.latest)
|
||||
? Math.floor(reporter.latest)
|
||||
: 0;
|
||||
const name = typeof reporter.name === "string" ? reporter.name : "";
|
||||
return { name, pubkey, latest };
|
||||
})
|
||||
@@ -302,7 +314,10 @@
|
||||
? sanitizedReporters
|
||||
: normalizeFixtureReporters(getActiveReporters(fixture, videoId));
|
||||
|
||||
const latest = reporters.reduce((max, reporter) => Math.max(max, reporter.latest), 0);
|
||||
const latest = reporters.reduce(
|
||||
(max, reporter) => Math.max(max, reporter.latest),
|
||||
0
|
||||
);
|
||||
|
||||
return {
|
||||
eventId: fixture.video.id,
|
||||
@@ -311,10 +326,10 @@
|
||||
[fixture.moderation.reportType]: {
|
||||
trusted: reporters.length,
|
||||
total: reporters.length,
|
||||
latest,
|
||||
latest
|
||||
}
|
||||
},
|
||||
},
|
||||
updatedAt: Date.now(),
|
||||
updatedAt: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -324,7 +339,13 @@
|
||||
const sanitizedReporters = normalizeFixtureReporters(activeReporters);
|
||||
const thresholds =
|
||||
fixture.thresholds && typeof fixture.thresholds === "object"
|
||||
? { blur: 3, autoplay: 2, reportHide: 3, muteHide: 1, ...fixture.thresholds }
|
||||
? {
|
||||
blur: 3,
|
||||
autoplay: 2,
|
||||
reportHide: 3,
|
||||
muteHide: 1,
|
||||
...fixture.thresholds
|
||||
}
|
||||
: { blur: 3, autoplay: 2, reportHide: 3, muteHide: 1 };
|
||||
const autoplayThreshold = Number.isFinite(thresholds.autoplay)
|
||||
? Math.max(0, Math.floor(thresholds.autoplay))
|
||||
@@ -346,16 +367,22 @@
|
||||
.map((reporter) => reporter.name)
|
||||
.filter((value) => typeof value === "string" && value);
|
||||
|
||||
const reporterPubkeys = sanitizedReporters.map((reporter) => reporter.pubkey);
|
||||
const reporterPubkeys = sanitizedReporters.map(
|
||||
(reporter) => reporter.pubkey
|
||||
);
|
||||
|
||||
const trustedReporterEntries = sanitizedReporters.map((reporter) => ({
|
||||
pubkey: reporter.pubkey,
|
||||
latest: reporter.latest,
|
||||
latest: reporter.latest
|
||||
}));
|
||||
|
||||
const muterEntries = Array.isArray(fixture.muters) ? fixture.muters : [];
|
||||
const muterEntries = Array.isArray(fixture.muters)
|
||||
? fixture.muters
|
||||
: [];
|
||||
const muterPubkeys = muterEntries
|
||||
.map((muter) => (typeof muter?.pubkey === "string" ? muter.pubkey : ""))
|
||||
.map((muter) =>
|
||||
typeof muter?.pubkey === "string" ? muter.pubkey : ""
|
||||
)
|
||||
.filter(Boolean);
|
||||
const muterDisplayNames = muterEntries
|
||||
.map((muter) => (typeof muter?.name === "string" ? muter.name : ""))
|
||||
@@ -390,7 +417,7 @@
|
||||
const hideCounts = hidden
|
||||
? {
|
||||
trustedMuteCount: muteCount,
|
||||
trustedReportCount: trustedCount,
|
||||
trustedReportCount: trustedCount
|
||||
}
|
||||
: null;
|
||||
|
||||
@@ -403,7 +430,7 @@
|
||||
trustedMuted,
|
||||
trustedMuters: muterPubkeys,
|
||||
trustedMuteCount: muteCount,
|
||||
trustedMuterDisplayNames: muterDisplayNames,
|
||||
trustedMuterDisplayNames: muterDisplayNames
|
||||
};
|
||||
|
||||
const summary = buildSummary(fixture, videoId, sanitizedReporters);
|
||||
@@ -425,12 +452,18 @@
|
||||
hideReason: hidden ? hideReason : "",
|
||||
hideCounts: hideCounts
|
||||
? {
|
||||
trustedMuteCount: Math.max(0, Number(hideCounts.trustedMuteCount) || 0),
|
||||
trustedReportCount: Math.max(0, Number(hideCounts.trustedReportCount) || 0),
|
||||
trustedMuteCount: Math.max(
|
||||
0,
|
||||
Number(hideCounts.trustedMuteCount) || 0
|
||||
),
|
||||
trustedReportCount: Math.max(
|
||||
0,
|
||||
Number(hideCounts.trustedReportCount) || 0
|
||||
)
|
||||
}
|
||||
: null,
|
||||
hideBypass,
|
||||
hideTriggered: hidden,
|
||||
hideTriggered: hidden
|
||||
};
|
||||
|
||||
base.original = {
|
||||
@@ -440,7 +473,7 @@
|
||||
hideReason: originalState.hideReason,
|
||||
hideCounts: originalState.hideCounts,
|
||||
hideBypass: originalState.hideBypass,
|
||||
hideTriggered: originalState.hideTriggered,
|
||||
hideTriggered: originalState.hideTriggered
|
||||
};
|
||||
|
||||
if (overrideEntry?.showAnyway) {
|
||||
@@ -448,7 +481,7 @@
|
||||
showAnyway: true,
|
||||
updatedAt: Number.isFinite(overrideEntry.updatedAt)
|
||||
? Math.floor(overrideEntry.updatedAt)
|
||||
: Date.now(),
|
||||
: Date.now()
|
||||
};
|
||||
base.blockAutoplay = false;
|
||||
base.blurThumbnail = false;
|
||||
@@ -501,7 +534,7 @@
|
||||
...entry,
|
||||
updatedAt: Number.isFinite(entry?.updatedAt)
|
||||
? Math.floor(entry.updatedAt)
|
||||
: Date.now(),
|
||||
: Date.now()
|
||||
};
|
||||
persistOverrides(next);
|
||||
return next;
|
||||
@@ -509,7 +542,7 @@
|
||||
|
||||
function createCard(fixture, overrides) {
|
||||
const video = {
|
||||
...fixture.video,
|
||||
...fixture.video
|
||||
};
|
||||
video.moderation = buildModerationState(fixture, overrides, video.id);
|
||||
|
||||
@@ -524,7 +557,7 @@
|
||||
return value.toLocaleString("en-US");
|
||||
}
|
||||
return String(value ?? "");
|
||||
},
|
||||
}
|
||||
},
|
||||
helpers: {
|
||||
isMagnetSupported: () => true,
|
||||
@@ -534,19 +567,23 @@
|
||||
return value.toLocaleString(locales || "en-US", options);
|
||||
}
|
||||
return String(value ?? "");
|
||||
},
|
||||
}
|
||||
},
|
||||
assets: {
|
||||
fallbackThumbnailSrc: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
fallbackThumbnailSrc: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
},
|
||||
designSystem: {
|
||||
getMode: () => "new",
|
||||
isNew: () => true,
|
||||
},
|
||||
isNew: () => true
|
||||
}
|
||||
});
|
||||
|
||||
const refreshModeration = () => {
|
||||
card.video.moderation = buildModerationState(fixture, overrides, video.id);
|
||||
card.video.moderation = buildModerationState(
|
||||
fixture,
|
||||
overrides,
|
||||
video.id
|
||||
);
|
||||
if (typeof card.refreshModerationUi === "function") {
|
||||
card.refreshModerationUi();
|
||||
}
|
||||
@@ -585,11 +622,16 @@
|
||||
if (fixture.description) {
|
||||
const description = document.createElement("p");
|
||||
description.textContent = fixture.description;
|
||||
description.classList.add("moderation-fixtures__section-description");
|
||||
description.classList.add(
|
||||
"moderation-fixtures__section-description"
|
||||
);
|
||||
section.appendChild(description);
|
||||
}
|
||||
|
||||
const { root: cardRoot, refreshModeration } = createCard(fixture, overrides);
|
||||
const { root: cardRoot, refreshModeration } = createCard(
|
||||
fixture,
|
||||
overrides
|
||||
);
|
||||
section.appendChild(cardRoot);
|
||||
|
||||
if (fixture.blockToggle && fixture.blockToggle.pubkey) {
|
||||
@@ -601,14 +643,25 @@
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.checked = isReporterBlocked(fixture.video.id, fixture.blockToggle.pubkey);
|
||||
input.checked = isReporterBlocked(
|
||||
fixture.video.id,
|
||||
fixture.blockToggle.pubkey
|
||||
);
|
||||
input.addEventListener("change", () => {
|
||||
const checked = input.checked;
|
||||
setReporterBlocked(fixture.video.id, fixture.blockToggle.pubkey, checked);
|
||||
setReporterBlocked(
|
||||
fixture.video.id,
|
||||
fixture.blockToggle.pubkey,
|
||||
checked
|
||||
);
|
||||
const action = checked ? "block" : "unblock";
|
||||
try {
|
||||
const detail = { action, targetPubkey: fixture.blockToggle.pubkey };
|
||||
const maybePromise = moderationService?.queueUserBlockRefresh?.(detail);
|
||||
const detail = {
|
||||
action,
|
||||
targetPubkey: fixture.blockToggle.pubkey
|
||||
};
|
||||
const maybePromise =
|
||||
moderationService?.queueUserBlockRefresh?.(detail);
|
||||
if (maybePromise && typeof maybePromise.then === "function") {
|
||||
maybePromise.catch(() => {});
|
||||
}
|
||||
|
||||
+35
-16
@@ -11,9 +11,9 @@
|
||||
<header class="space-y-sm">
|
||||
<h1 class="text-2xl font-semibold">Popover positioning scenarios</h1>
|
||||
<p class="max-w-3xl text-sm text-muted">
|
||||
Interactive harness that exercises the popover engine near the edges of the
|
||||
viewport, inside modals, and within scroll containers. Playwright scenarios
|
||||
rely on the data attributes surfaced here.
|
||||
Interactive harness that exercises the popover engine near the edges
|
||||
of the viewport, inside modals, and within scroll containers.
|
||||
Playwright scenarios rely on the data attributes surfaced here.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@@ -21,13 +21,16 @@
|
||||
<div class="space-y-xs">
|
||||
<h2 class="text-xl font-semibold">Grid edge cases</h2>
|
||||
<p class="text-sm text-muted">
|
||||
The rightmost card hugs the viewport edge to verify collision handling.
|
||||
The rightmost card hugs the viewport edge to verify collision
|
||||
handling.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid gap-lg sm:grid-cols-2 lg:grid-cols-3">
|
||||
<article class="card space-y-sm p-md">
|
||||
<h3 class="text-lg font-semibold">Top left</h3>
|
||||
<p class="text-sm text-muted">Baseline placement without collisions.</p>
|
||||
<p class="text-sm text-muted">
|
||||
Baseline placement without collisions.
|
||||
</p>
|
||||
<button
|
||||
class="btn-ghost"
|
||||
type="button"
|
||||
@@ -37,7 +40,10 @@
|
||||
Menu
|
||||
</button>
|
||||
</article>
|
||||
<article class="card space-y-sm p-md" data-test-card="grid-bottom-right">
|
||||
<article
|
||||
class="card space-y-sm p-md"
|
||||
data-test-card="grid-bottom-right"
|
||||
>
|
||||
<h3 class="text-lg font-semibold">Bottom right</h3>
|
||||
<p class="text-sm text-muted">
|
||||
Forces the panel to flip when it would overflow the viewport.
|
||||
@@ -55,7 +61,9 @@
|
||||
</article>
|
||||
<article class="card space-y-sm p-md">
|
||||
<h3 class="text-lg font-semibold">Center</h3>
|
||||
<p class="text-sm text-muted">Acts as a control for viewport padding.</p>
|
||||
<p class="text-sm text-muted">
|
||||
Acts as a control for viewport padding.
|
||||
</p>
|
||||
<div class="flex justify-center">
|
||||
<button
|
||||
class="btn-ghost"
|
||||
@@ -74,23 +82,28 @@
|
||||
<div class="space-y-xs">
|
||||
<h2 class="text-xl font-semibold">Video modal integration</h2>
|
||||
<p class="text-sm text-muted">
|
||||
Mimics the production video modal so we can verify popovers respect the overlay root.
|
||||
Mimics the production video modal so we can verify popovers respect
|
||||
the overlay root.
|
||||
</p>
|
||||
</div>
|
||||
<button class="btn w-40" type="button" data-test-open-modal>
|
||||
Open modal
|
||||
</button>
|
||||
<div
|
||||
class="fixed inset-0 z-overlay-modal hidden items-center justify-center bg-overlay/60 backdrop-blur"
|
||||
class="z-overlay-modal bg-overlay/60 fixed inset-0 hidden items-center justify-center backdrop-blur"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="testModalTitle"
|
||||
data-test-modal
|
||||
>
|
||||
<div class="bv-modal__panel flex max-h-modal-shell flex-col gap-md p-lg">
|
||||
<div
|
||||
class="bv-modal__panel flex max-h-modal-shell flex-col gap-md p-lg"
|
||||
>
|
||||
<header class="flex items-center justify-between gap-sm">
|
||||
<div>
|
||||
<p class="text-xs uppercase tracking-wide text-muted">Now playing</p>
|
||||
<p class="text-xs uppercase tracking-wide text-muted">
|
||||
Now playing
|
||||
</p>
|
||||
<h3 id="testModalTitle" class="text-lg font-semibold">
|
||||
Edge-of-viewport sample
|
||||
</h3>
|
||||
@@ -101,7 +114,8 @@
|
||||
</header>
|
||||
<div class="space-y-md">
|
||||
<p class="text-sm text-muted">
|
||||
Interact with the more menu to verify popovers inside scroll-locked contexts.
|
||||
Interact with the more menu to verify popovers inside
|
||||
scroll-locked contexts.
|
||||
</p>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
@@ -122,14 +136,15 @@
|
||||
<div class="space-y-xs">
|
||||
<h2 class="text-xl font-semibold">Scroll container</h2>
|
||||
<p class="text-sm text-muted">
|
||||
Ensures auto-update keeps popovers aligned when the trigger lives in overflow regions.
|
||||
Ensures auto-update keeps popovers aligned when the trigger lives in
|
||||
overflow regions.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="h-64 overflow-auto rounded-xl border border-border bg-surface-alt p-md"
|
||||
data-test-scroll-region
|
||||
>
|
||||
<div class="space-y-lg docs-popover-scenarios__scroll-spacer">
|
||||
<div class="docs-popover-scenarios__scroll-spacer space-y-lg">
|
||||
<p class="text-sm text-muted">
|
||||
Scroll to the bottom to find the trigger used in automated tests.
|
||||
</p>
|
||||
@@ -155,7 +170,8 @@
|
||||
|
||||
function buildMenu({ container, trigger, id }) {
|
||||
const panel = container.ownerDocument.createElement("div");
|
||||
panel.className = "popover__panel card w-64 max-w-popover-safe space-y-sm";
|
||||
panel.className =
|
||||
"popover__panel card w-64 max-w-popover-safe space-y-sm";
|
||||
panel.dataset.testPanel = id;
|
||||
|
||||
const heading = container.ownerDocument.createElement("h4");
|
||||
@@ -198,7 +214,10 @@
|
||||
|
||||
const triggers = [
|
||||
{ selector: "[data-test-trigger=grid-top-left]", id: "grid-top-left" },
|
||||
{ selector: "[data-test-trigger=grid-bottom-right]", id: "grid-bottom-right" },
|
||||
{
|
||||
selector: "[data-test-trigger=grid-bottom-right]",
|
||||
id: "grid-bottom-right"
|
||||
},
|
||||
{ selector: "[data-test-trigger=grid-center]", id: "grid-center" },
|
||||
{ selector: "[data-test-trigger=modal-menu]", id: "modal-menu" },
|
||||
{ selector: "[data-test-trigger=scroll-bottom]", id: "scroll-bottom" }
|
||||
|
||||
+38
-23
@@ -14,7 +14,7 @@ module.exports = {
|
||||
extend: {
|
||||
screens: {
|
||||
xs: "30rem",
|
||||
compact: "25rem",
|
||||
compact: "25rem"
|
||||
},
|
||||
colors: {
|
||||
page: "var(--color-page)",
|
||||
@@ -29,7 +29,7 @@ module.exports = {
|
||||
"panel-hover": "var(--surface-panel-hover)",
|
||||
sunken: "var(--surface-sunken)",
|
||||
muted: "var(--surface-muted)",
|
||||
inverse: "var(--surface-inverse)",
|
||||
inverse: "var(--surface-inverse)"
|
||||
},
|
||||
overlay: {
|
||||
DEFAULT: "var(--surface-overlay)",
|
||||
@@ -202,7 +202,7 @@ module.exports = {
|
||||
borderColor: {
|
||||
surface: "var(--border-default)",
|
||||
"surface-strong": "var(--border-strong)",
|
||||
"overlay": "var(--border-overlay)",
|
||||
overlay: "var(--border-overlay)",
|
||||
"overlay-strong": "var(--border-overlay-strong)",
|
||||
"status-info-border": "var(--status-info-border)",
|
||||
"status-success-border": "var(--status-success-border)",
|
||||
@@ -278,16 +278,22 @@ module.exports = {
|
||||
"sidebar-shell-block-end": "var(--space-sidebar-shell-block-end)",
|
||||
"sidebar-scroll-gap": "var(--space-sidebar-scroll-gap)",
|
||||
"sidebar-panel-gap": "var(--space-sidebar-panel-gap)",
|
||||
"sidebar-panel-padding-block": "var(--space-sidebar-panel-padding-block)",
|
||||
"sidebar-panel-padding-inline": "var(--space-sidebar-panel-padding-inline)",
|
||||
"sidebar-panel-padding-block":
|
||||
"var(--space-sidebar-panel-padding-block)",
|
||||
"sidebar-panel-padding-inline":
|
||||
"var(--space-sidebar-panel-padding-inline)",
|
||||
"sidebar-footer-gap": "var(--space-sidebar-footer-gap)",
|
||||
"sidebar-trigger-padding-block": "var(--space-sidebar-trigger-padding-block)",
|
||||
"sidebar-trigger-padding-block":
|
||||
"var(--space-sidebar-trigger-padding-block)",
|
||||
"sidebar-popover-offset": "var(--space-sidebar-popover-offset)",
|
||||
"sidebar-popover-padding-block": "var(--space-sidebar-popover-padding-block)",
|
||||
"sidebar-popover-padding-inline": "var(--space-sidebar-popover-padding-inline)",
|
||||
"sidebar-popover-padding-block":
|
||||
"var(--space-sidebar-popover-padding-block)",
|
||||
"sidebar-popover-padding-inline":
|
||||
"var(--space-sidebar-popover-padding-inline)",
|
||||
"sidebar-popover-gap": "var(--space-sidebar-popover-gap)",
|
||||
"sidebar-nav-padding-block": "var(--space-sidebar-nav-padding-block)",
|
||||
"sidebar-subnav-padding-block": "var(--space-sidebar-subnav-padding-block)",
|
||||
"sidebar-subnav-padding-block":
|
||||
"var(--space-sidebar-subnav-padding-block)",
|
||||
"sidebar-mobile-gutter": "var(--sidebar-mobile-gutter)",
|
||||
"popup-inline": "var(--popup-padding-inline)",
|
||||
"popup-block": "var(--popup-padding-block)",
|
||||
@@ -322,9 +328,18 @@ module.exports = {
|
||||
mono: ["var(--font-family-mono)"]
|
||||
},
|
||||
fontSize: {
|
||||
"4xs": ["var(--font-size-4xs)", { lineHeight: "var(--line-height-tight)" }],
|
||||
"3xs": ["var(--font-size-3xs)", { lineHeight: "var(--line-height-tight)" }],
|
||||
"2xs": ["var(--font-size-2xs)", { lineHeight: "var(--line-height-tight)" }],
|
||||
"4xs": [
|
||||
"var(--font-size-4xs)",
|
||||
{ lineHeight: "var(--line-height-tight)" }
|
||||
],
|
||||
"3xs": [
|
||||
"var(--font-size-3xs)",
|
||||
{ lineHeight: "var(--line-height-tight)" }
|
||||
],
|
||||
"2xs": [
|
||||
"var(--font-size-2xs)",
|
||||
{ lineHeight: "var(--line-height-tight)" }
|
||||
],
|
||||
xs: ["var(--font-size-xs)", { lineHeight: "var(--line-height-tight)" }],
|
||||
sm: ["var(--font-size-sm)", { lineHeight: "var(--line-height-snug)" }],
|
||||
base: [
|
||||
@@ -379,19 +394,19 @@ module.exports = {
|
||||
lineHeight: "1.6",
|
||||
maxWidth: "none",
|
||||
fontFamily: theme("fontFamily.sans").join(", "),
|
||||
"h1": {
|
||||
h1: {
|
||||
fontWeight: theme("fontWeight.bold"),
|
||||
marginTop: "0.5rem",
|
||||
marginBottom: "1rem",
|
||||
fontSize: theme("fontSize.2xl")[0]
|
||||
},
|
||||
"h2": {
|
||||
h2: {
|
||||
fontWeight: theme("fontWeight.bold"),
|
||||
marginTop: "1.25rem",
|
||||
marginBottom: "0.75rem",
|
||||
fontSize: theme("fontSize.xl")[0]
|
||||
},
|
||||
"h3": {
|
||||
h3: {
|
||||
fontWeight: theme("fontWeight.semibold"),
|
||||
marginTop: "1rem",
|
||||
marginBottom: "0.75rem",
|
||||
@@ -402,7 +417,7 @@ module.exports = {
|
||||
marginTop: "0.75rem",
|
||||
marginBottom: "0.5rem"
|
||||
},
|
||||
"p": {
|
||||
p: {
|
||||
marginTop: "0",
|
||||
marginBottom: "1rem"
|
||||
},
|
||||
@@ -411,11 +426,11 @@ module.exports = {
|
||||
marginBottom: "1rem",
|
||||
paddingLeft: "1.25rem"
|
||||
},
|
||||
"li": {
|
||||
li: {
|
||||
marginTop: "0.5rem",
|
||||
marginBottom: "0.5rem"
|
||||
},
|
||||
"blockquote": {
|
||||
blockquote: {
|
||||
borderLeftWidth: "4px",
|
||||
borderLeftColor: "var(--color-border-subtle)",
|
||||
paddingLeft: "1rem",
|
||||
@@ -423,7 +438,7 @@ module.exports = {
|
||||
marginBottom: "1rem",
|
||||
color: "var(--color-muted)"
|
||||
},
|
||||
"code": {
|
||||
code: {
|
||||
backgroundColor: "var(--color-page-alt)",
|
||||
color: "var(--color-secondary)",
|
||||
padding: "0.2rem 0.4rem",
|
||||
@@ -438,7 +453,7 @@ module.exports = {
|
||||
"code::after": {
|
||||
content: "none"
|
||||
},
|
||||
"pre": {
|
||||
pre: {
|
||||
backgroundColor: "var(--color-page-alt)",
|
||||
color: "var(--color-text-strong)",
|
||||
padding: "1rem",
|
||||
@@ -456,7 +471,7 @@ module.exports = {
|
||||
fontWeight: "inherit",
|
||||
color: "inherit"
|
||||
},
|
||||
"a": {
|
||||
a: {
|
||||
color: "var(--color-primary)",
|
||||
textDecoration: "none",
|
||||
fontWeight: theme("fontWeight.medium")
|
||||
@@ -464,13 +479,13 @@ module.exports = {
|
||||
"a:hover": {
|
||||
textDecoration: "underline"
|
||||
},
|
||||
"hr": {
|
||||
hr: {
|
||||
borderColor: "var(--color-border-subtle)",
|
||||
borderTopWidth: "1px",
|
||||
marginTop: "2rem",
|
||||
marginBottom: "2rem"
|
||||
},
|
||||
"img": {
|
||||
img: {
|
||||
marginTop: "0",
|
||||
marginBottom: "1rem",
|
||||
borderRadius: theme("borderRadius.md"),
|
||||
|
||||
Reference in new issue
Block a user