mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Merge pull request #1367 from PR0M3TH3AN/codex/add-dm-ui-surface-with-components
Add DM AppShell and atomic DM UI components
This commit is contained in:
12 files changed
+1032
-2
No files matched your search
@@ -1,7 +1,7 @@
|
||||
: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.17 | 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
|
||||
@@ -11,4 +11,4 @@
|
||||
--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
|
||||
--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
|
||||
);--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
|
||||
@@ -4976,3 +4976,400 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.dm-app-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-lg);
|
||||
padding: var(--space-lg);
|
||||
background: var(--surface-base);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.dm-app-shell__sidebar,
|
||||
.dm-app-shell__main {
|
||||
border-radius: var(--radius-lg);
|
||||
border: var(--border-width-hairline) solid var(--border-default);
|
||||
background: var(--surface-panel);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.dm-app-shell__sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
.dm-app-shell__title {
|
||||
font-size: var(--font-size-lg);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dm-app-shell__main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
padding: var(--space-md);
|
||||
background: var(--surface-raised);
|
||||
}
|
||||
|
||||
.dm-conversation-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.dm-conversation-list__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dm-conversation-list__title {
|
||||
font-size: var(--font-size-sm);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dm-conversation-list__items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
overflow-y: auto;
|
||||
max-height: 60vh;
|
||||
padding-right: var(--space-xs);
|
||||
}
|
||||
|
||||
.dm-conversation-list__state {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--text-muted);
|
||||
padding: var(--space-md);
|
||||
text-align: center;
|
||||
background: var(--surface-sunken);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.dm-contact-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
border-radius: var(--radius-md);
|
||||
border: var(--border-width-hairline) solid var(--border-subtle);
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dm-contact-row:hover,
|
||||
.dm-contact-row:focus-visible {
|
||||
background: var(--surface-muted);
|
||||
border-color: var(--border-strong);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dm-contact-row--active {
|
||||
background: var(--surface-sunken);
|
||||
border-color: var(--border-strong);
|
||||
}
|
||||
|
||||
.dm-contact-row__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2xs);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dm-contact-row__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.dm-contact-row__name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dm-contact-row__time {
|
||||
font-size: var(--font-size-2xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dm-contact-row__preview {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dm-contact-row__badge {
|
||||
align-self: flex-start;
|
||||
padding: var(--space-3xs) var(--space-xs);
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--surface-overlay);
|
||||
color: var(--text-inverse);
|
||||
font-size: var(--font-size-3xs);
|
||||
}
|
||||
|
||||
.dm-avatar {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--surface-sunken);
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
border: var(--border-width-hairline) solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.dm-avatar--md {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.dm-avatar--sm {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.dm-avatar__image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dm-message-thread {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.dm-message-thread__header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.dm-message-thread__title {
|
||||
font-size: var(--font-size-base);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dm-message-thread__status {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dm-message-thread__timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-sm);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface-panel);
|
||||
border: var(--border-width-hairline) solid var(--border-subtle);
|
||||
overflow-y: auto;
|
||||
min-height: 16rem;
|
||||
}
|
||||
|
||||
.dm-message-thread__state {
|
||||
padding: var(--space-md);
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
background: var(--surface-sunken);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.dm-message-bubble {
|
||||
max-width: 85%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2xs);
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
border-radius: var(--radius-lg);
|
||||
border: var(--border-width-hairline) solid var(--border-subtle);
|
||||
background: var(--surface-sunken);
|
||||
}
|
||||
|
||||
.dm-message-bubble--incoming {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.dm-message-bubble--outgoing {
|
||||
align-self: flex-end;
|
||||
background: var(--surface-overlay-panel);
|
||||
}
|
||||
|
||||
.dm-message-bubble__content {
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.dm-message-bubble__meta {
|
||||
display: flex;
|
||||
gap: var(--space-xs);
|
||||
font-size: var(--font-size-2xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dm-message-bubble__status {
|
||||
color: var(--status-danger);
|
||||
}
|
||||
|
||||
.dm-day-divider {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-size-2xs);
|
||||
}
|
||||
|
||||
.dm-day-divider__line {
|
||||
height: 1px;
|
||||
background: var(--border-subtle);
|
||||
}
|
||||
|
||||
.dm-notification-center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.dm-notification-center__empty {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dm-notification-center__item {
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
border-radius: var(--radius-md);
|
||||
border: var(--border-width-hairline) solid var(--border-subtle);
|
||||
background: var(--surface-sunken);
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
.dm-notification-center__item--info {
|
||||
background: var(--status-info-surface);
|
||||
border-color: var(--status-info-border);
|
||||
color: var(--status-info-on);
|
||||
}
|
||||
|
||||
.dm-notification-center__item--success {
|
||||
background: var(--status-success-surface);
|
||||
border-color: var(--status-success-border);
|
||||
color: var(--status-success-on);
|
||||
}
|
||||
|
||||
.dm-notification-center__item--warning {
|
||||
background: var(--status-warning-surface);
|
||||
border-color: var(--status-warning-border);
|
||||
color: var(--status-warning-on);
|
||||
}
|
||||
|
||||
.dm-notification-center__item--error {
|
||||
background: var(--status-danger-surface);
|
||||
border-color: var(--status-danger-border);
|
||||
color: var(--status-danger-on);
|
||||
}
|
||||
|
||||
.dm-composer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.dm-composer__label {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dm-composer__input {
|
||||
resize: vertical;
|
||||
border-radius: var(--radius-md);
|
||||
border: var(--border-width-hairline) solid var(--border-default);
|
||||
background: var(--surface-base);
|
||||
color: var(--text-primary);
|
||||
padding: var(--space-sm);
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.dm-composer__input:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.dm-composer__actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.dm-composer__hint {
|
||||
font-size: var(--font-size-2xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.dm-composer__send {
|
||||
border-radius: var(--radius-md);
|
||||
border: var(--border-width-hairline) solid var(--border-strong);
|
||||
background: var(--surface-overlay-panel);
|
||||
color: var(--text-primary);
|
||||
padding: var(--space-xs) var(--space-md);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dm-composer__send:hover,
|
||||
.dm-composer__send:focus-visible {
|
||||
background: var(--surface-overlay-panel-soft);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dm-composer__status {
|
||||
font-size: var(--font-size-2xs);
|
||||
color: var(--status-danger);
|
||||
min-height: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: theme("screens.md")) {
|
||||
.dm-app-shell {
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.dm-app-shell__sidebar {
|
||||
flex: 0 0 18rem;
|
||||
max-width: 20rem;
|
||||
height: calc(100vh - var(--space-lg) * 2);
|
||||
}
|
||||
|
||||
.dm-app-shell__main {
|
||||
height: calc(100vh - var(--space-lg) * 2);
|
||||
}
|
||||
|
||||
.dm-conversation-list__items {
|
||||
max-height: none;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: theme("screens.lg")) {
|
||||
.dm-app-shell__sidebar {
|
||||
flex-basis: 20rem;
|
||||
}
|
||||
|
||||
.dm-message-thread__timeline {
|
||||
padding: var(--space-md);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import { Composer } from "./Composer.js";
|
||||
import { ConversationList } from "./ConversationList.js";
|
||||
import { MessageThread } from "./MessageThread.js";
|
||||
import { NotificationCenter } from "./NotificationCenter.js";
|
||||
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
const DEFAULT_CONVERSATIONS = [
|
||||
{
|
||||
id: "conv-1",
|
||||
name: "Nia Nova",
|
||||
preview: "The relay looks stable now.",
|
||||
timestamp: "2m",
|
||||
unreadCount: 2,
|
||||
status: "Online",
|
||||
avatarSrc: "",
|
||||
},
|
||||
{
|
||||
id: "conv-2",
|
||||
name: "Orbit Ops",
|
||||
preview: "Can you share the stream link?",
|
||||
timestamp: "1h",
|
||||
unreadCount: 0,
|
||||
status: "Away",
|
||||
avatarSrc: "",
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_MESSAGES = [
|
||||
{ type: "day", label: "Today" },
|
||||
{
|
||||
id: "msg-1",
|
||||
direction: "incoming",
|
||||
body: "Hey! The new DM view looks promising.",
|
||||
timestamp: "09:12",
|
||||
},
|
||||
{
|
||||
id: "msg-2",
|
||||
direction: "outgoing",
|
||||
body: "Thanks! I'm wiring up the layout and keyboard flow now.",
|
||||
timestamp: "09:14",
|
||||
status: "sending",
|
||||
},
|
||||
];
|
||||
|
||||
export class AppShell {
|
||||
constructor({
|
||||
document: doc,
|
||||
conversations = DEFAULT_CONVERSATIONS,
|
||||
activeConversationId = DEFAULT_CONVERSATIONS[0]?.id || "",
|
||||
conversationState = "idle",
|
||||
messages = DEFAULT_MESSAGES,
|
||||
threadState = "idle",
|
||||
threadErrorType = "",
|
||||
composerState = "idle",
|
||||
notifications = [],
|
||||
onSelectConversation,
|
||||
onSendMessage,
|
||||
} = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("AppShell requires a document reference.");
|
||||
}
|
||||
|
||||
this.document = doc;
|
||||
this.root = createElement(doc, "div", "dm-app-shell");
|
||||
|
||||
const sidebar = createElement(doc, "aside", "dm-app-shell__sidebar");
|
||||
const sidebarTitle = createElement(
|
||||
doc,
|
||||
"h1",
|
||||
"dm-app-shell__title",
|
||||
"Direct Messages",
|
||||
);
|
||||
sidebar.appendChild(sidebarTitle);
|
||||
sidebar.appendChild(
|
||||
ConversationList({
|
||||
document: doc,
|
||||
conversations,
|
||||
activeId: activeConversationId,
|
||||
state: conversationState,
|
||||
onSelect: onSelectConversation,
|
||||
}),
|
||||
);
|
||||
|
||||
const main = createElement(doc, "main", "dm-app-shell__main");
|
||||
main.appendChild(
|
||||
NotificationCenter({
|
||||
document: doc,
|
||||
notices: notifications,
|
||||
}),
|
||||
);
|
||||
main.appendChild(
|
||||
MessageThread({
|
||||
document: doc,
|
||||
contact: conversations.find((conversation) => conversation.id === activeConversationId) || {},
|
||||
messages,
|
||||
state: threadState,
|
||||
errorType: threadErrorType,
|
||||
}),
|
||||
);
|
||||
main.appendChild(
|
||||
Composer({
|
||||
document: doc,
|
||||
state: composerState,
|
||||
onSend: onSendMessage,
|
||||
}),
|
||||
);
|
||||
|
||||
this.root.appendChild(sidebar);
|
||||
this.root.appendChild(main);
|
||||
}
|
||||
|
||||
getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
const DEFAULT_AVATAR_ALT = "Avatar";
|
||||
|
||||
function getInitials(label = "") {
|
||||
if (typeof label !== "string") {
|
||||
return "";
|
||||
}
|
||||
const words = label.trim().split(/\s+/).filter(Boolean);
|
||||
if (!words.length) {
|
||||
return "";
|
||||
}
|
||||
const initials = words.slice(0, 2).map((word) => word[0].toUpperCase());
|
||||
return initials.join("");
|
||||
}
|
||||
|
||||
export function Avatar({
|
||||
document: doc,
|
||||
src = "",
|
||||
alt = DEFAULT_AVATAR_ALT,
|
||||
size = "md",
|
||||
initials = "",
|
||||
status = "",
|
||||
} = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("Avatar requires a document reference.");
|
||||
}
|
||||
|
||||
const avatar = doc.createElement("div");
|
||||
avatar.className = `dm-avatar dm-avatar--${size}`;
|
||||
if (status) {
|
||||
avatar.dataset.status = status;
|
||||
}
|
||||
|
||||
const resolvedInitials = initials || getInitials(alt);
|
||||
|
||||
if (src) {
|
||||
const img = doc.createElement("img");
|
||||
img.className = "dm-avatar__image";
|
||||
img.alt = alt || DEFAULT_AVATAR_ALT;
|
||||
img.loading = "lazy";
|
||||
img.decoding = "async";
|
||||
img.src = src;
|
||||
avatar.appendChild(img);
|
||||
} else {
|
||||
const fallback = doc.createElement("span");
|
||||
fallback.className = "dm-avatar__fallback";
|
||||
fallback.textContent = resolvedInitials || "?";
|
||||
avatar.appendChild(fallback);
|
||||
}
|
||||
|
||||
return avatar;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function Composer({
|
||||
document: doc,
|
||||
placeholder = "Write a message…",
|
||||
state = "idle",
|
||||
onSend,
|
||||
} = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("Composer requires a document reference.");
|
||||
}
|
||||
|
||||
const form = createElement(doc, "form", "dm-composer");
|
||||
form.noValidate = true;
|
||||
|
||||
const label = createElement(doc, "label", "dm-composer__label", "Message");
|
||||
label.setAttribute("for", "dm-composer-input");
|
||||
|
||||
const textarea = createElement(doc, "textarea", "dm-composer__input");
|
||||
textarea.id = "dm-composer-input";
|
||||
textarea.placeholder = placeholder;
|
||||
textarea.rows = 2;
|
||||
|
||||
const actions = createElement(doc, "div", "dm-composer__actions");
|
||||
const hint = createElement(
|
||||
doc,
|
||||
"span",
|
||||
"dm-composer__hint",
|
||||
"Press Ctrl/⌘ + Enter to send",
|
||||
);
|
||||
const button = createElement(doc, "button", "dm-composer__send", "Send");
|
||||
button.type = "submit";
|
||||
actions.appendChild(hint);
|
||||
actions.appendChild(button);
|
||||
|
||||
const status = createElement(doc, "div", "dm-composer__status");
|
||||
if (state === "error") {
|
||||
status.textContent = "Send failed. Try again.";
|
||||
} else if (state === "sending") {
|
||||
status.textContent = "Sending…";
|
||||
}
|
||||
|
||||
form.appendChild(label);
|
||||
form.appendChild(textarea);
|
||||
form.appendChild(actions);
|
||||
form.appendChild(status);
|
||||
|
||||
form.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
if (typeof onSend === "function") {
|
||||
onSend(textarea.value);
|
||||
}
|
||||
});
|
||||
|
||||
textarea.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
|
||||
event.preventDefault();
|
||||
form.requestSubmit();
|
||||
}
|
||||
});
|
||||
|
||||
return form;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { Avatar } from "./Avatar.js";
|
||||
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function ContactRow({
|
||||
document: doc,
|
||||
contact = {},
|
||||
isActive = false,
|
||||
tabIndex = -1,
|
||||
onSelect,
|
||||
} = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("ContactRow requires a document reference.");
|
||||
}
|
||||
|
||||
const {
|
||||
id = "",
|
||||
name = "Unknown",
|
||||
preview = "",
|
||||
timestamp = "",
|
||||
unreadCount = 0,
|
||||
avatarSrc = "",
|
||||
status = "",
|
||||
} = contact;
|
||||
|
||||
const button = createElement(doc, "button", "dm-contact-row");
|
||||
button.type = "button";
|
||||
button.dataset.conversationId = id;
|
||||
button.setAttribute("role", "option");
|
||||
button.setAttribute("aria-selected", String(Boolean(isActive)));
|
||||
button.tabIndex = tabIndex;
|
||||
if (isActive) {
|
||||
button.classList.add("dm-contact-row--active");
|
||||
}
|
||||
|
||||
const avatar = Avatar({
|
||||
document: doc,
|
||||
src: avatarSrc,
|
||||
alt: name,
|
||||
initials: name,
|
||||
status,
|
||||
});
|
||||
button.appendChild(avatar);
|
||||
|
||||
const content = createElement(doc, "div", "dm-contact-row__content");
|
||||
const header = createElement(doc, "div", "dm-contact-row__header");
|
||||
const nameEl = createElement(doc, "span", "dm-contact-row__name", name);
|
||||
header.appendChild(nameEl);
|
||||
|
||||
const timeEl = createElement(doc, "span", "dm-contact-row__time", timestamp);
|
||||
header.appendChild(timeEl);
|
||||
content.appendChild(header);
|
||||
|
||||
const previewEl = createElement(
|
||||
doc,
|
||||
"div",
|
||||
"dm-contact-row__preview",
|
||||
preview,
|
||||
);
|
||||
content.appendChild(previewEl);
|
||||
|
||||
if (unreadCount > 0) {
|
||||
const badge = createElement(doc, "span", "dm-contact-row__badge");
|
||||
badge.textContent = `${unreadCount}`;
|
||||
badge.setAttribute("aria-label", `${unreadCount} unread messages`);
|
||||
content.appendChild(badge);
|
||||
}
|
||||
|
||||
button.appendChild(content);
|
||||
|
||||
if (typeof onSelect === "function") {
|
||||
button.addEventListener("click", () => onSelect(contact));
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { ContactRow } from "./ContactRow.js";
|
||||
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
function getFocusableItems(listEl) {
|
||||
return [...listEl.querySelectorAll("[data-conversation-id]")];
|
||||
}
|
||||
|
||||
export function ConversationList({
|
||||
document: doc,
|
||||
conversations = [],
|
||||
activeId = "",
|
||||
state = "idle",
|
||||
onSelect,
|
||||
} = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("ConversationList requires a document reference.");
|
||||
}
|
||||
|
||||
const root = createElement(doc, "section", "dm-conversation-list");
|
||||
const header = createElement(doc, "div", "dm-conversation-list__header");
|
||||
header.appendChild(createElement(doc, "h2", "dm-conversation-list__title", "Messages"));
|
||||
root.appendChild(header);
|
||||
|
||||
const list = createElement(doc, "div", "dm-conversation-list__items");
|
||||
list.setAttribute("role", "listbox");
|
||||
list.setAttribute("aria-label", "Conversation list");
|
||||
|
||||
const renderState = createElement(doc, "div", "dm-conversation-list__state");
|
||||
|
||||
if (state === "loading") {
|
||||
renderState.textContent = "Loading conversations…";
|
||||
list.appendChild(renderState);
|
||||
} else if (state === "empty") {
|
||||
renderState.textContent = "No conversations yet.";
|
||||
list.appendChild(renderState);
|
||||
} else if (state === "error") {
|
||||
renderState.textContent = "We couldn't load conversations.";
|
||||
list.appendChild(renderState);
|
||||
} else {
|
||||
const resolvedActiveId = activeId || (conversations[0] ? conversations[0].id : "");
|
||||
conversations.forEach((conversation, index) => {
|
||||
const isActive = conversation.id === resolvedActiveId;
|
||||
const row = ContactRow({
|
||||
document: doc,
|
||||
contact: conversation,
|
||||
isActive,
|
||||
tabIndex: isActive || (!resolvedActiveId && index === 0) ? 0 : -1,
|
||||
onSelect,
|
||||
});
|
||||
list.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
list.addEventListener("keydown", (event) => {
|
||||
const items = getFocusableItems(list);
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentIndex = items.indexOf(doc.activeElement);
|
||||
let nextIndex = currentIndex;
|
||||
|
||||
switch (event.key) {
|
||||
case "ArrowDown":
|
||||
nextIndex = Math.min(items.length - 1, currentIndex + 1);
|
||||
break;
|
||||
case "ArrowUp":
|
||||
nextIndex = Math.max(0, currentIndex - 1);
|
||||
break;
|
||||
case "Home":
|
||||
nextIndex = 0;
|
||||
break;
|
||||
case "End":
|
||||
nextIndex = items.length - 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
items[nextIndex].focus();
|
||||
});
|
||||
|
||||
root.appendChild(list);
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function DayDivider({ document: doc, label = "" } = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("DayDivider requires a document reference.");
|
||||
}
|
||||
|
||||
const divider = createElement(doc, "div", "dm-day-divider");
|
||||
const line = createElement(doc, "span", "dm-day-divider__line");
|
||||
const text = createElement(doc, "span", "dm-day-divider__label", label);
|
||||
divider.appendChild(line);
|
||||
divider.appendChild(text);
|
||||
divider.appendChild(line.cloneNode());
|
||||
return divider;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function MessageBubble({
|
||||
document: doc,
|
||||
message = {},
|
||||
variant = "incoming",
|
||||
} = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("MessageBubble requires a document reference.");
|
||||
}
|
||||
|
||||
const {
|
||||
body = "",
|
||||
timestamp = "",
|
||||
status = "sent",
|
||||
} = message;
|
||||
|
||||
const wrapper = createElement(doc, "div", "dm-message-bubble");
|
||||
wrapper.classList.add(`dm-message-bubble--${variant}`);
|
||||
wrapper.dataset.status = status;
|
||||
|
||||
const content = createElement(doc, "div", "dm-message-bubble__content", body);
|
||||
wrapper.appendChild(content);
|
||||
|
||||
const meta = createElement(doc, "div", "dm-message-bubble__meta");
|
||||
if (timestamp) {
|
||||
meta.appendChild(createElement(doc, "span", "dm-message-bubble__time", timestamp));
|
||||
}
|
||||
|
||||
if (status === "failed") {
|
||||
meta.appendChild(
|
||||
createElement(doc, "span", "dm-message-bubble__status", "Send failed"),
|
||||
);
|
||||
} else if (status === "sending") {
|
||||
meta.appendChild(
|
||||
createElement(doc, "span", "dm-message-bubble__status", "Sending…"),
|
||||
);
|
||||
}
|
||||
|
||||
wrapper.appendChild(meta);
|
||||
return wrapper;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { DayDivider } from "./DayDivider.js";
|
||||
import { MessageBubble } from "./MessageBubble.js";
|
||||
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function MessageThread({
|
||||
document: doc,
|
||||
contact = {},
|
||||
messages = [],
|
||||
state = "idle",
|
||||
errorType = "",
|
||||
} = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("MessageThread requires a document reference.");
|
||||
}
|
||||
|
||||
const root = createElement(doc, "section", "dm-message-thread");
|
||||
|
||||
const header = createElement(doc, "header", "dm-message-thread__header");
|
||||
header.appendChild(
|
||||
createElement(
|
||||
doc,
|
||||
"h3",
|
||||
"dm-message-thread__title",
|
||||
contact.name || "Direct Messages",
|
||||
),
|
||||
);
|
||||
if (contact.status) {
|
||||
header.appendChild(
|
||||
createElement(doc, "span", "dm-message-thread__status", contact.status),
|
||||
);
|
||||
}
|
||||
root.appendChild(header);
|
||||
|
||||
const timeline = createElement(doc, "div", "dm-message-thread__timeline");
|
||||
timeline.setAttribute("role", "log");
|
||||
timeline.setAttribute("aria-live", "polite");
|
||||
|
||||
const stateEl = createElement(doc, "div", "dm-message-thread__state");
|
||||
|
||||
if (state === "loading") {
|
||||
stateEl.textContent = "Decrypting messages…";
|
||||
timeline.appendChild(stateEl);
|
||||
} else if (state === "empty") {
|
||||
stateEl.textContent = "Say hello to start the conversation.";
|
||||
timeline.appendChild(stateEl);
|
||||
} else if (state === "error") {
|
||||
const errorMessage =
|
||||
errorType === "decrypt"
|
||||
? "We couldn't decrypt some messages."
|
||||
: "Something went wrong loading this thread.";
|
||||
stateEl.textContent = errorMessage;
|
||||
timeline.appendChild(stateEl);
|
||||
} else {
|
||||
messages.forEach((message) => {
|
||||
if (message.type === "day") {
|
||||
timeline.appendChild(DayDivider({ document: doc, label: message.label }));
|
||||
return;
|
||||
}
|
||||
const bubble = MessageBubble({
|
||||
document: doc,
|
||||
message,
|
||||
variant: message.direction === "outgoing" ? "outgoing" : "incoming",
|
||||
});
|
||||
timeline.appendChild(bubble);
|
||||
});
|
||||
}
|
||||
|
||||
root.appendChild(timeline);
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
function createElement(doc, tag, className, text) {
|
||||
const element = doc.createElement(tag);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (typeof text === "string") {
|
||||
element.textContent = text;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function NotificationCenter({ document: doc, notices = [] } = {}) {
|
||||
if (!doc) {
|
||||
throw new Error("NotificationCenter requires a document reference.");
|
||||
}
|
||||
|
||||
const root = createElement(doc, "section", "dm-notification-center");
|
||||
|
||||
if (!notices.length) {
|
||||
const placeholder = createElement(
|
||||
doc,
|
||||
"div",
|
||||
"dm-notification-center__empty",
|
||||
"You're all caught up.",
|
||||
);
|
||||
root.appendChild(placeholder);
|
||||
return root;
|
||||
}
|
||||
|
||||
notices.forEach((notice) => {
|
||||
const item = createElement(doc, "div", "dm-notification-center__item");
|
||||
if (notice.variant) {
|
||||
item.classList.add(`dm-notification-center__item--${notice.variant}`);
|
||||
}
|
||||
item.textContent = notice.message || "";
|
||||
root.appendChild(item);
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export { AppShell } from "./AppShell.js";
|
||||
export { Avatar } from "./Avatar.js";
|
||||
export { ContactRow } from "./ContactRow.js";
|
||||
export { ConversationList } from "./ConversationList.js";
|
||||
export { MessageThread } from "./MessageThread.js";
|
||||
export { MessageBubble } from "./MessageBubble.js";
|
||||
export { Composer } from "./Composer.js";
|
||||
export { NotificationCenter } from "./NotificationCenter.js";
|
||||
export { DayDivider } from "./DayDivider.js";
|
||||
Reference in new issue
Block a user