mirror of
https://gitlab.com/vyla-entertainment/documentation.git
synced 2026-11-09 20:17:20 -05:00
rewrite
This commit is contained in:
1245
assets/app.js
1245
assets/app.js
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ body.is-404 .error-container {
|
||||
|
||||
body.is-404 .error-code {
|
||||
margin: 0 0 16px;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-size: clamp(74px, 14vw, 112px);
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
@@ -42,7 +42,7 @@ body.is-404 .error-code {
|
||||
|
||||
body.is-404 .error-title {
|
||||
margin: 0 0 24px;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.12em;
|
||||
|
||||
@@ -29,145 +29,6 @@
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#search-modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 100;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#search-modal.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-panel {
|
||||
position: relative;
|
||||
width: min(640px, calc(100vw - 24px));
|
||||
margin: 52px auto 0;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
background: var(--search-panel-bg) !important;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.search-input-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 18px 20px;
|
||||
}
|
||||
|
||||
.search-input-row .ico {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
#search-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
color: var(--text-heading);
|
||||
font: inherit;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#search-results {
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
padding: 0 6px 8px;
|
||||
}
|
||||
|
||||
.search-label {
|
||||
padding: 8px 14px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.sr {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.sr>.ico {
|
||||
margin-top: 3px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.sr.on {
|
||||
background: var(--hover-bg);
|
||||
}
|
||||
|
||||
.sr-body {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sr .t {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.sr .s {
|
||||
display: -webkit-box;
|
||||
margin-top: 2px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.4;
|
||||
color: var(--muted);
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.sr .p {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sr .sep {
|
||||
margin: 0 6px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.sr .hit {
|
||||
font-weight: 700;
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.search-footer {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
padding: 12px 20px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.search-footer .close-hint {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.search-footer kbd {
|
||||
margin-right: 4px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px;
|
||||
font: 11px var(--font);
|
||||
}
|
||||
|
||||
.code-wrap {
|
||||
position: relative;
|
||||
}
|
||||
@@ -254,7 +115,7 @@
|
||||
}
|
||||
|
||||
.cg-copy-btn:hover {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.code-group-panel {
|
||||
@@ -456,13 +317,11 @@ main.prose blockquote p {
|
||||
#search-trigger kbd {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-panel {
|
||||
margin-top: 50px;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.search-footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
main.prose h1,
|
||||
main.prose h2,
|
||||
main.prose h3,
|
||||
main.prose h4 {
|
||||
cursor: pointer;
|
||||
}
|
||||
262
assets/styling/features.css
Normal file
262
assets/styling/features.css
Normal file
@@ -0,0 +1,262 @@
|
||||
.bm-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: none;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease, background 0.15s ease;
|
||||
}
|
||||
|
||||
.bm-btn .ico {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.bm-btn:hover {
|
||||
background: var(--hover-bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.bm-btn.on {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.bm-btn.on .ico {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.personal-card {
|
||||
margin: 18px 0 8px;
|
||||
padding: 16px 18px;
|
||||
border-radius: 22px;
|
||||
background: var(--callout-note-bg);
|
||||
}
|
||||
|
||||
.personal-card label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.pc-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pc-row input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 36px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 100px;
|
||||
background: var(--search-bg);
|
||||
color: var(--text);
|
||||
font: inherit;
|
||||
font-size: 13.5px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.pc-row input.bad {
|
||||
border-color: #ef4444;
|
||||
}
|
||||
|
||||
.pc-btn {
|
||||
height: 36px;
|
||||
padding: 0 18px;
|
||||
border: none;
|
||||
border-radius: 100px;
|
||||
background: var(--btn-primary-bg);
|
||||
color: var(--btn-primary-text);
|
||||
font: inherit;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
main.prose .pc-hint {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.5;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.rec-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
padding: 2px 10px;
|
||||
border-radius: 100px;
|
||||
background: var(--btn-primary-bg);
|
||||
color: var(--btn-primary-text);
|
||||
font-size: 11.5px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
tbody tr.rec-row td {
|
||||
background: var(--hover-bg);
|
||||
}
|
||||
|
||||
tbody tr.rec-row td:first-child {
|
||||
padding-left: 12px;
|
||||
border-top-left-radius: 12px;
|
||||
border-bottom-left-radius: 12px;
|
||||
}
|
||||
|
||||
tbody tr.rec-row td:last-child {
|
||||
padding-right: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
|
||||
#footer-note {
|
||||
margin: 0 0 16px;
|
||||
font-size: 13.5px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.changelog,
|
||||
.contributors {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.contributors {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
main.prose .muted-line {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.cl-item {
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.cl-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 12.5px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.cl-tag {
|
||||
padding: 2px 10px;
|
||||
border-radius: 100px;
|
||||
background: var(--hover-bg);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cl-title {
|
||||
margin-top: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
main.prose .cl-notes {
|
||||
margin: 6px 0 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.cl-link {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ct-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0px 14px 0px 5px;
|
||||
border-radius: 100px;
|
||||
background: var(--hover-bg);
|
||||
color: var(--text);
|
||||
;
|
||||
font-size: 13.5px;
|
||||
margin-right: 10px;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ct-chip em {
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.ct-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-primary-bg);
|
||||
color: var(--btn-primary-text);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.ct-avatar-img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.confetti-canvas {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 3000;
|
||||
}
|
||||
|
||||
body:not(.is-home) main.prose {
|
||||
zoom: var(--text-scale, 1);
|
||||
}
|
||||
|
||||
body.no-deco .cobweb,
|
||||
body.no-deco .spider-container,
|
||||
body.no-deco .snowfall,
|
||||
body.no-deco .leaf-fall,
|
||||
body.no-deco .light-string {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.no-video .video-bg,
|
||||
body.no-video .body-blurred-video,
|
||||
body.no-video .header-video {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.no-motion *,
|
||||
body.no-motion *::before,
|
||||
body.no-motion *::after {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.rec-badge {
|
||||
margin-left: 0 !important;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
z-index: 41;
|
||||
backdrop-filter: blur(10px) saturate(1.2);
|
||||
-webkit-backdrop-filter: blur(10px) saturate(1.2);
|
||||
background: var(--bg-card);
|
||||
width: 50%;
|
||||
border-radius: 30px;
|
||||
}
|
||||
@@ -99,11 +100,11 @@
|
||||
}
|
||||
|
||||
#navbar .tabs a:hover {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#navbar .tabs a.active {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -155,19 +156,19 @@
|
||||
}
|
||||
|
||||
#sidebar li a.active {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#sidebar li a:hover {
|
||||
background: var(--hover-bg);
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#content-col {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
padding: 16px 20px 20px;
|
||||
padding: 16px max(20px, env(safe-area-inset-right)) 20px max(20px, env(safe-area-inset-left));
|
||||
gap: 92px;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
@@ -178,7 +179,7 @@
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: 720px;
|
||||
margin-top: 130px;
|
||||
margin-top: 120px;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
@@ -213,7 +214,7 @@ footer#site-footer {
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
@@ -248,7 +249,7 @@ footer#site-footer {
|
||||
}
|
||||
|
||||
.footer-socials a:hover {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.footer-icon {
|
||||
@@ -361,6 +362,7 @@ footer#site-footer {
|
||||
border-radius: 10px;
|
||||
background: none;
|
||||
color: var(--text);
|
||||
;
|
||||
font: inherit;
|
||||
font-size: 13.5px;
|
||||
cursor: pointer;
|
||||
@@ -397,11 +399,11 @@ footer#site-footer {
|
||||
|
||||
#theme-menu button.on {
|
||||
font-weight: 600;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#theme-menu button.on .ico {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#theme-menu button.on .check {
|
||||
@@ -479,6 +481,7 @@ footer#site-footer {
|
||||
border-radius: 8px;
|
||||
background: none;
|
||||
color: var(--text);
|
||||
;
|
||||
font: inherit;
|
||||
font-size: 13.5px;
|
||||
cursor: pointer;
|
||||
@@ -505,15 +508,16 @@ footer#site-footer {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--text);
|
||||
;
|
||||
}
|
||||
|
||||
#theme-seg-menu button.on span {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#theme-seg-menu button.on .ico {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#theme-seg-menu button .check {
|
||||
@@ -526,7 +530,7 @@ footer#site-footer {
|
||||
|
||||
#theme-seg-menu button.on .check {
|
||||
visibility: visible;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#theme-seg-menu button:hover {
|
||||
@@ -535,7 +539,7 @@ footer#site-footer {
|
||||
|
||||
#theme-seg-menu button.on {
|
||||
font-weight: 600;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#theme-seg-menu .check {
|
||||
@@ -564,7 +568,7 @@ footer#site-footer {
|
||||
|
||||
#theme-seg>button.on {
|
||||
background: var(--hover-bg);
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#mobile-bar {
|
||||
@@ -645,7 +649,7 @@ footer#site-footer {
|
||||
}
|
||||
|
||||
.mobile-crumbs .crumb-heading {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -667,7 +671,7 @@ footer#site-footer {
|
||||
border: none;
|
||||
background: var(--hover-bg);
|
||||
border-radius: 100px;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font: inherit;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
@@ -728,11 +732,11 @@ footer#site-footer {
|
||||
|
||||
.mt-menu .mt-item:hover {
|
||||
background: var(--hover-bg);
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.mt-menu .mt-item.on {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -754,12 +758,6 @@ footer#site-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.is-home #sidebar,
|
||||
body.is-home #toc,
|
||||
body.is-home #mobile-bar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.is-home #layout {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -805,14 +803,23 @@ body.is-home main.prose {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
body.is-home #toc,
|
||||
body.is-home #sidebar,
|
||||
body.is-home #mobile-bar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
:root {
|
||||
--header-height: 60px;
|
||||
}
|
||||
|
||||
#header-container {
|
||||
width: 91%;
|
||||
border-radius: 25px;
|
||||
width: 90%;
|
||||
border-radius: 30px;
|
||||
backdrop-filter: blur(15px) saturate(1.2);
|
||||
-webkit-backdrop-filter: blur(15px) saturate(1.2);
|
||||
top: max(6px, env(safe-area-inset-top));
|
||||
}
|
||||
|
||||
#navbar .tabs,
|
||||
@@ -865,7 +872,7 @@ body.is-home main.prose {
|
||||
}
|
||||
|
||||
#sidebar #theme-seg>button.on {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
@@ -939,6 +946,7 @@ body.is-home main.prose {
|
||||
height: 60px;
|
||||
margin: 0 -16px 12px;
|
||||
padding: 0 16px;
|
||||
margin-top: max(6px, env(safe-area-inset-top));
|
||||
}
|
||||
|
||||
#sidebar .side-head .brand {
|
||||
@@ -959,3 +967,43 @@ body.is-home main.prose {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-height: 550px) {
|
||||
:root {
|
||||
--header-height: 52px;
|
||||
}
|
||||
|
||||
#header-container {
|
||||
top: max(6px, env(safe-area-inset-top));
|
||||
width: min(600px, 88vw);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
#navbar .nav-top {
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
#navbar .tabs {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
#navbar .tabs a {
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#main-wrap {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
top: calc(var(--header-height) + 16px);
|
||||
max-height: calc(100vh - var(--header-height) - 16px);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#mobile-bar {
|
||||
padding: 6px 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
858
assets/styling/overlay.css
Normal file
858
assets/styling/overlay.css
Normal file
@@ -0,0 +1,858 @@
|
||||
#search-modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 100;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#search-modal.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-panel {
|
||||
position: relative;
|
||||
width: min(640px, calc(100vw - 24px));
|
||||
margin: 52px auto 0;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
background: var(--search-panel-bg) !important;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.search-input-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 18px 20px;
|
||||
}
|
||||
|
||||
.search-input-row .ico {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
#search-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
font: inherit;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#search-results {
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
padding: 0 6px 8px;
|
||||
}
|
||||
|
||||
.search-label {
|
||||
padding: 8px 14px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.sr {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.sr>.ico {
|
||||
margin-top: 3px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.sr.on {
|
||||
background: var(--hover-bg);
|
||||
}
|
||||
|
||||
.sr-body {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sr .t {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.sr .s {
|
||||
display: -webkit-box;
|
||||
margin-top: 2px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.4;
|
||||
color: var(--muted);
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.sr .p {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sr .sep {
|
||||
margin: 0 6px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.sr .hit {
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.search-footer {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
padding: 12px 20px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.search-footer .close-hint {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.search-footer kbd {
|
||||
margin-right: 4px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px;
|
||||
font: 11px var(--font);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.search-panel {
|
||||
margin-top: 50px;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.search-footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sheet-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.sheet-overlay.active {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.sheet-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.sheet-panel {
|
||||
position: relative;
|
||||
width: min(500px, calc(100vw - 32px));
|
||||
background: var(--bg-modal);
|
||||
border-radius: 28px;
|
||||
backdrop-filter: blur(24px);
|
||||
-webkit-backdrop-filter: blur(24px);
|
||||
padding: 32px 28px 28px;
|
||||
box-sizing: border-box;
|
||||
transform: scale(0.92) translateY(16px);
|
||||
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sheet-overlay.active .sheet-panel {
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
.sheet-handle-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sheet-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin: 0 0 10px;
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sheet-desc {
|
||||
font-size: 14.5px;
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
margin: 0 0 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sheet-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sheet-btn-primary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 22px;
|
||||
border-radius: 100px;
|
||||
background: var(--btn-primary-bg);
|
||||
color: var(--btn-primary-text) !important;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-decoration: none !important;
|
||||
transition: transform 0.15s ease, opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.sheet-btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.sheet-btn-never {
|
||||
padding: 10px 18px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--muted);
|
||||
font: inherit;
|
||||
font-size: 13.5px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
border-radius: 100px;
|
||||
transition: color 0.15s ease, background 0.15s ease;
|
||||
}
|
||||
|
||||
.sheet-btn-never:hover {
|
||||
color: var(--text);
|
||||
background: var(--hover-bg);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.sheet-overlay {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.sheet-panel {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 28px 28px 0 0;
|
||||
border-bottom: none;
|
||||
padding: 14px 22px 32px;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.45s cubic-bezier(0.32, 0.72, 0, 1);
|
||||
touch-action: none;
|
||||
max-height: 90vh;
|
||||
}
|
||||
|
||||
.sheet-panel.dragging {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.sheet-overlay.active .sheet-panel {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.sheet-handle-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 6px 0 16px;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.sheet-handle {
|
||||
width: 40px;
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: var(--muted);
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.sheet-actions {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sheet-btn-primary {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sheet-btn-never {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-height: 550px) {
|
||||
.sheet-panel {
|
||||
max-height: 94vh;
|
||||
overflow-y: auto;
|
||||
padding: 10px max(24px, env(safe-area-inset-right)) 16px max(24px, env(safe-area-inset-left));
|
||||
}
|
||||
|
||||
.sheet-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.sheet-desc {
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.sheet-actions {
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.sheet-btn-primary,
|
||||
.sheet-btn-never {
|
||||
width: auto;
|
||||
padding: 8px 18px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.image-lightbox {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2000;
|
||||
background: transparent;
|
||||
backdrop-filter: blur(0px);
|
||||
-webkit-backdrop-filter: blur(0px);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: background 0.42s cubic-bezier(0.2, 0.9, 0.3, 1),
|
||||
backdrop-filter 0.42s cubic-bezier(0.2, 0.9, 0.3, 1),
|
||||
opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.image-lightbox.active {
|
||||
background: var(--bg-modal);
|
||||
backdrop-filter: blur(24px);
|
||||
-webkit-backdrop-filter: blur(24px);
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.lightbox-controls {
|
||||
position: fixed;
|
||||
top: max(16px, env(safe-area-inset-top));
|
||||
right: max(16px, env(safe-area-inset-right));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
z-index: 2003;
|
||||
background: var(--bg-menu);
|
||||
border-radius: 100px;
|
||||
padding: 3px 6px;
|
||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
opacity: 0;
|
||||
transform: translateY(-8px) scale(0.96);
|
||||
transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1) 0.08s,
|
||||
transform 0.3s cubic-bezier(0.16, 1, 0.3, 1) 0.08s;
|
||||
}
|
||||
|
||||
.image-lightbox.active .lightbox-controls {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
|
||||
.lightbox-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
font-family: var(--font);
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.lightbox-btn:hover {
|
||||
background: var(--hover-bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.lightbox-btn svg {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
fill: currentColor;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.lightbox-img {
|
||||
position: fixed;
|
||||
object-fit: contain;
|
||||
border-radius: 16px;
|
||||
transition: top 0.44s cubic-bezier(0.2, 0.9, 0.3, 1),
|
||||
left 0.44s cubic-bezier(0.2, 0.9, 0.3, 1),
|
||||
width 0.44s cubic-bezier(0.2, 0.9, 0.3, 1),
|
||||
height 0.44s cubic-bezier(0.2, 0.9, 0.3, 1),
|
||||
border-radius 0.44s cubic-bezier(0.2, 0.9, 0.3, 1),
|
||||
transform 0.25s ease;
|
||||
z-index: 2002;
|
||||
transform-origin: center center;
|
||||
will-change: top, left, width, height, transform;
|
||||
}
|
||||
|
||||
main.prose img {
|
||||
cursor: zoom-in;
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
main.prose img:hover {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
#settings-trigger,
|
||||
.side-settings {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
background: none;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#settings-trigger .ico,
|
||||
.side-settings .ico {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
#settings-trigger:hover,
|
||||
.side-settings:hover {
|
||||
background: var(--hover-bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.settings-panel {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.st-top,
|
||||
.st-bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.st-top::before,
|
||||
.st-bottom::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
backdrop-filter: blur(18px) saturate(1.2);
|
||||
-webkit-backdrop-filter: blur(18px) saturate(1.2);
|
||||
}
|
||||
|
||||
.st-top {
|
||||
top: 0;
|
||||
padding: 26px 28px 30px;
|
||||
}
|
||||
|
||||
.st-top::before {
|
||||
background: linear-gradient(to bottom, var(--bg-menu) 40%, transparent);
|
||||
background: linear-gradient(to bottom, color-mix(in srgb, var(--bg-menu) 94%, transparent), color-mix(in srgb, var(--bg-menu) 80%, transparent) 60%, transparent);
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
|
||||
mask-image: linear-gradient(to bottom, #000 55%, transparent);
|
||||
}
|
||||
|
||||
.st-top .sheet-title {
|
||||
margin: 0 0 6px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.st-top .sheet-desc {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.st-bottom {
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 36px 28px calc(20px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.st-bottom::before {
|
||||
background: linear-gradient(to top, var(--bg-menu) 40%, transparent);
|
||||
background: linear-gradient(to top, color-mix(in srgb, var(--bg-menu) 94%, transparent), color-mix(in srgb, var(--bg-menu) 80%, transparent) 60%, transparent);
|
||||
-webkit-mask-image: linear-gradient(to top, #000 55%, transparent);
|
||||
mask-image: linear-gradient(to top, #000 55%, transparent);
|
||||
}
|
||||
|
||||
.st-bottom .sheet-btn-primary {
|
||||
border: none;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.st-bottom .sheet-btn-primary.done {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.st-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: var(--st-top, 110px) 28px var(--st-bottom, 84px);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
touch-action: pan-y;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--border) transparent;
|
||||
}
|
||||
|
||||
.st-group {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.st-heading {
|
||||
margin: 0 0 8px 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.st-card {
|
||||
border-radius: 20px;
|
||||
background: var(--hover-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.st-row,
|
||||
.st-field {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
padding: 14px 16px;
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.st-card>:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.st-row {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.st-row.stack {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
label.st-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.st-field {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.st-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.st-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.st-hint {
|
||||
font-size: 12.5px;
|
||||
line-height: 1.45;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.st-hint.err {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.st-input {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
border: none;
|
||||
border-radius: 100px;
|
||||
background: var(--search-bg);
|
||||
color: var(--text);
|
||||
font: inherit;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
transition: border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.st-input::placeholder {
|
||||
color: var(--muted);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.st-input:focus {
|
||||
border-color: var(--text);
|
||||
}
|
||||
|
||||
.st-input.bad {
|
||||
border-color: #ef4444;
|
||||
}
|
||||
|
||||
.st-swatches {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.st-swatch {
|
||||
display: block;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: var(--sw, var(--hover-bg));
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
.st-swatch:hover {
|
||||
transform: scale(1.12);
|
||||
}
|
||||
|
||||
.st-swatch.on {
|
||||
box-shadow: 0 0 0 2px var(--bg-menu), 0 0 0 4px var(--text);
|
||||
;
|
||||
}
|
||||
|
||||
.st-swatch.reset {
|
||||
background: linear-gradient(135deg, transparent 46%, var(--muted) 46%, var(--muted) 54%, transparent 54%), var(--search-bg);
|
||||
box-shadow: inset 0 0 0 1.5px var(--muted);
|
||||
}
|
||||
|
||||
.st-swatch.reset.on {
|
||||
box-shadow: inset 0 0 0 1.5px var(--muted), 0 0 0 2px var(--bg-menu), 0 0 0 4px var(--text);
|
||||
;
|
||||
}
|
||||
|
||||
.st-swatch.custom {
|
||||
background: conic-gradient(from 0deg, #ff5c5c, #f4b942, #5fd68f, #8ad1ff, #c084fc, #ff5c5c);
|
||||
}
|
||||
|
||||
.st-swatch.custom input {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
border: none;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.st-seg {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
padding: 3px;
|
||||
border-radius: 100px;
|
||||
background: var(--search-bg);
|
||||
}
|
||||
|
||||
.st-seg button {
|
||||
flex: 1;
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
border: none;
|
||||
border-radius: 100px;
|
||||
background: none;
|
||||
color: var(--muted);
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.st-seg button:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.st-seg button.on {
|
||||
background: var(--btn-primary-bg);
|
||||
color: var(--btn-primary-text);
|
||||
}
|
||||
|
||||
.st-switch {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 44px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.st-switch input {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.st-track {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 100px;
|
||||
background: var(--search-bg);
|
||||
transition: background 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.st-track::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: var(--muted);
|
||||
transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), background 0.2s ease;
|
||||
}
|
||||
|
||||
.st-switch input:checked+.st-track {
|
||||
border-color: transparent;
|
||||
background: var(--btn-primary-bg);
|
||||
}
|
||||
|
||||
.st-switch input:checked+.st-track::after {
|
||||
transform: translateX(18px);
|
||||
background: var(--btn-primary-text);
|
||||
}
|
||||
|
||||
.st-switch input:focus-visible+.st-track {
|
||||
outline: 2px solid var(--text);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (min-width: 901px) {
|
||||
.settings-panel {
|
||||
width: min(540px, calc(100vw - 32px));
|
||||
max-height: min(88vh, 780px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.st-top {
|
||||
padding: 8px 22px 28px;
|
||||
}
|
||||
|
||||
.st-scroll {
|
||||
padding-left: 22px;
|
||||
padding-right: 22px;
|
||||
}
|
||||
|
||||
.st-bottom {
|
||||
padding: 36px 22px calc(18px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.st-bottom .sheet-btn-primary {
|
||||
flex: 1;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.st-bottom .sheet-btn-never {
|
||||
flex: 0 0 auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-height: 550px) {
|
||||
.settings-panel {
|
||||
max-height: 94vh;
|
||||
}
|
||||
|
||||
.st-top {
|
||||
padding: 14px 24px 22px;
|
||||
}
|
||||
|
||||
.st-top .sheet-title {
|
||||
font-size: 17px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.st-top .sheet-desc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@ main.prose .eyebrow {
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px;
|
||||
color: var(--text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
main.prose h1 {
|
||||
@@ -18,7 +22,7 @@ main.prose h1 {
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
margin: 0 0 6px;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
main.prose .page-description {
|
||||
@@ -48,21 +52,22 @@ main.prose h3 {
|
||||
main.prose h2,
|
||||
main.prose h3 {
|
||||
scroll-margin-top: 130px;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
main.prose p,
|
||||
main.prose li {
|
||||
color: var(--text);
|
||||
color: var(--muted);
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
main.prose strong {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
main.prose a {
|
||||
color: var(--text);
|
||||
;
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -93,7 +98,7 @@ main.prose hr {
|
||||
|
||||
main.prose code {
|
||||
background: var(--code-inline-bg);
|
||||
color: var(--code-inline-text);
|
||||
color: var(--text);
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
padding: 2px 6px;
|
||||
@@ -162,6 +167,7 @@ td {
|
||||
vertical-align: top;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
color: var(--text);
|
||||
;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
@@ -246,7 +252,7 @@ aside#toc .toc-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
@@ -274,11 +280,11 @@ aside#toc li a {
|
||||
}
|
||||
|
||||
aside#toc li a:hover {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
aside#toc li a.active {
|
||||
color: var(--text-heading);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -287,3 +293,9 @@ aside#toc li a.active {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
#main-wrap {
|
||||
margin-top: max(180px, env(safe-area-inset-top)) !important;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #000000;
|
||||
--text: #f3f3f3;
|
||||
--text-heading: #ffffff;
|
||||
--text: #ffffff;
|
||||
--muted: #a3a3a3;
|
||||
--border: #2b2b2b98;
|
||||
--bg-elevated: rgba(255, 255, 255, 0.08);
|
||||
@@ -38,6 +38,9 @@
|
||||
--spider-display: none;
|
||||
--cobweb-display: none;
|
||||
--spider-thread: rgba(255, 255, 255, 0.3);
|
||||
--snow-display: none;
|
||||
--leaf-display: none;
|
||||
--lights-display: none;
|
||||
|
||||
--hl-comment: #5c6370;
|
||||
--hl-punct: #abb2bf;
|
||||
@@ -71,7 +74,7 @@
|
||||
:root[data-theme="light"] {
|
||||
--bg: #ffffff;
|
||||
--text: #0b0b0c;
|
||||
--text-heading: #111827;
|
||||
--text: #111827;
|
||||
--muted: #6b7280;
|
||||
--border: #e5e7eb;
|
||||
--bg-elevated: rgba(0, 0, 0, 0.05);
|
||||
@@ -82,7 +85,7 @@
|
||||
--code-inline-text: #0b0b0c;
|
||||
--code-text: #24292e;
|
||||
--bg-menu: #ffffff;
|
||||
--bg-modal: rgba(240, 240, 240, 0.7);
|
||||
--bg-modal: rgba(240, 240, 240);
|
||||
--hover-bg: rgba(0, 0, 0, 0.05);
|
||||
--search-bg: rgba(0, 0, 0, 0.05);
|
||||
--search-panel-bg: rgba(255, 255, 255, 0.85);
|
||||
@@ -126,7 +129,7 @@
|
||||
:root[data-theme="midnight"] {
|
||||
--bg: #070913;
|
||||
--text: #e2e8f0;
|
||||
--text-heading: #f8fafc;
|
||||
--text: #f8fafc;
|
||||
--muted: #7f8ea3;
|
||||
--border: #1a2234;
|
||||
--bg-elevated: rgba(30, 41, 59, 0.5);
|
||||
@@ -181,7 +184,7 @@
|
||||
:root[data-theme="nord"] {
|
||||
--bg: #2e3440;
|
||||
--text: #eceff4;
|
||||
--text-heading: #88c0d0;
|
||||
--text: #88c0d0;
|
||||
--muted: #d8dee9;
|
||||
--border: #3b4252;
|
||||
--bg-elevated: rgba(67, 76, 94, 0.5);
|
||||
@@ -236,7 +239,7 @@
|
||||
:root[data-theme="dracula"] {
|
||||
--bg: #282a36;
|
||||
--text: #f8f8f2;
|
||||
--text-heading: #bd93f9;
|
||||
--text: #bd93f9;
|
||||
--muted: #6272a4;
|
||||
--border: #44475a;
|
||||
--bg-elevated: rgba(68, 71, 90, 0.5);
|
||||
@@ -291,7 +294,7 @@
|
||||
:root[data-theme="forest"] {
|
||||
--bg: #0d1612;
|
||||
--text: #e2ede5;
|
||||
--text-heading: #86efac;
|
||||
--text: #86efac;
|
||||
--muted: #739481;
|
||||
--border: #1a2f24;
|
||||
--bg-elevated: rgba(34, 76, 56, 0.35);
|
||||
@@ -346,7 +349,7 @@
|
||||
:root[data-theme="sunset"] {
|
||||
--bg: #180d18;
|
||||
--text: #fce7f3;
|
||||
--text-heading: #fb7185;
|
||||
--text: #fb7185;
|
||||
--muted: #a77c98;
|
||||
--border: #3b1d36;
|
||||
--bg-elevated: rgba(80, 30, 70, 0.35);
|
||||
@@ -401,7 +404,7 @@
|
||||
:root[data-theme="cyberpunk"] {
|
||||
--bg: #050508;
|
||||
--text: #00ffcc;
|
||||
--text-heading: #ffe600;
|
||||
--text: #ffe600;
|
||||
--muted: #5e7a75;
|
||||
--border: #1a2a26;
|
||||
--bg-elevated: rgba(0, 255, 204, 0.08);
|
||||
@@ -453,61 +456,6 @@
|
||||
--hl-inserted-bg: rgba(0, 255, 204, 0.2);
|
||||
}
|
||||
|
||||
:root[data-theme="halloween"] {
|
||||
--bg: #0a0604;
|
||||
--text: #f5ebe6;
|
||||
--text-heading: #ff7518;
|
||||
--muted: #a37559;
|
||||
--border: #3d1c0c;
|
||||
--bg-elevated: rgba(255, 117, 24, 0.12);
|
||||
--bg-card: rgba(28, 14, 8, 0.6);
|
||||
--bg-card-hover: rgba(46, 21, 10, 0.85);
|
||||
--bg-code: #050201;
|
||||
--code-inline-bg: rgba(255, 117, 24, 0.15);
|
||||
--code-inline-text: #ff944d;
|
||||
--code-text: #fed7aa;
|
||||
--bg-menu: #0e0704;
|
||||
--bg-modal: rgba(10, 6, 4, 0.92);
|
||||
--hover-bg: rgba(255, 117, 24, 0.12);
|
||||
--search-bg: rgba(255, 117, 24, 0.08);
|
||||
--search-panel-bg: rgba(14, 7, 4, 0.95);
|
||||
--code-group-bg: #1c0e07;
|
||||
--code-group-panel-bg: #0a0604;
|
||||
--code-tab-active: #ff7518;
|
||||
--footer-border: #3d1c0c;
|
||||
--border-subtle: rgba(255, 117, 24, 0.15);
|
||||
--btn-primary-bg: #ff7518;
|
||||
--btn-primary-text: #000000;
|
||||
--btn-home-bg: #ff7518;
|
||||
--btn-home-text: #000000;
|
||||
--callout-note-bg: rgba(255, 117, 24, 0.14);
|
||||
--callout-info-bg: rgba(168, 85, 247, 0.15);
|
||||
--callout-warning-bg: rgba(255, 154, 77, 0.18);
|
||||
--callout-warning-text: #ffaa5e;
|
||||
--body-radial-tint: rgba(10, 6, 4, 0.85);
|
||||
--skeleton-pulse-1: rgba(255, 117, 24, 0.06);
|
||||
--skeleton-pulse-2: rgba(255, 117, 24, 0.18);
|
||||
--spider-display: block;
|
||||
--cobweb-display: block;
|
||||
--spider-thread: rgba(255, 154, 77, 0.4);
|
||||
|
||||
--hl-comment: #7c4f34;
|
||||
--hl-punct: #fed7aa;
|
||||
--hl-func: #ff944d;
|
||||
--hl-keyword: #a855f7;
|
||||
--hl-string: #84cc16;
|
||||
--hl-var: #ff7518;
|
||||
--hl-op: #a855f7;
|
||||
--hl-num: #f59e0b;
|
||||
--hl-param: #ffedd5;
|
||||
--hl-class: #c084fc;
|
||||
--hl-tag: #ff7518;
|
||||
--hl-deleted-color: #ef4444;
|
||||
--hl-deleted-bg: rgba(239, 68, 68, 0.2);
|
||||
--hl-inserted-color: #84cc16;
|
||||
--hl-inserted-bg: rgba(132, 204, 22, 0.2);
|
||||
}
|
||||
|
||||
body.banner-dismissed {
|
||||
--header-height: 109px;
|
||||
}
|
||||
@@ -526,14 +474,18 @@ html {
|
||||
|
||||
body {
|
||||
min-height: 100%;
|
||||
min-height: -webkit-fill-available;
|
||||
margin: 0;
|
||||
font-family: var(--font);
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
;
|
||||
line-height: 1.65;
|
||||
overflow-x: hidden;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar,
|
||||
@@ -565,100 +517,3 @@ a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cobweb {
|
||||
display: var(--cobweb-display);
|
||||
position: fixed;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
pointer-events: none;
|
||||
z-index: 35;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.cobweb-top-left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.cobweb-top-right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.cobweb-media {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.spider-container {
|
||||
display: var(--spider-display);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 12%;
|
||||
pointer-events: none;
|
||||
z-index: 36;
|
||||
animation: spiderDescend 10s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.spider-thread {
|
||||
width: 1.5px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--spider-thread);
|
||||
}
|
||||
|
||||
.spider-body {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-top: 100px;
|
||||
animation: spiderSway 3s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.spider-media {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
scale: 2;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
@keyframes spiderDescend {
|
||||
0% {
|
||||
transform: translateY(-80px);
|
||||
}
|
||||
|
||||
45% {
|
||||
transform: translateY(240px);
|
||||
}
|
||||
|
||||
55% {
|
||||
transform: translateY(240px);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-80px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spiderSway {
|
||||
0% {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
}
|
||||
574
assets/styling/themes.css
Normal file
574
assets/styling/themes.css
Normal file
@@ -0,0 +1,574 @@
|
||||
:root[data-theme="halloween"] {
|
||||
--bg: #0a0604;
|
||||
--text: #f5ebe6;
|
||||
--text: #ff7518;
|
||||
--muted: #a37559;
|
||||
--border: #3d1c0c;
|
||||
--bg-elevated: rgba(255, 117, 24, 0.12);
|
||||
--bg-card: rgba(28, 14, 8, 0.6);
|
||||
--bg-card-hover: rgba(46, 21, 10, 0.85);
|
||||
--bg-code: #050201;
|
||||
--code-inline-bg: rgba(255, 117, 24, 0.15);
|
||||
--code-inline-text: #ff944d;
|
||||
--code-text: #fed7aa;
|
||||
--bg-menu: #0e0704;
|
||||
--bg-modal: rgba(10, 6, 4, 0.92);
|
||||
--hover-bg: rgba(255, 117, 24, 0.12);
|
||||
--search-bg: rgba(255, 117, 24, 0.08);
|
||||
--search-panel-bg: rgba(14, 7, 4, 0.95);
|
||||
--code-group-bg: #1c0e07;
|
||||
--code-group-panel-bg: #0a0604;
|
||||
--code-tab-active: #ff7518;
|
||||
--footer-border: #3d1c0c;
|
||||
--border-subtle: rgba(255, 117, 24, 0.15);
|
||||
--btn-primary-bg: #ff7518;
|
||||
--btn-primary-text: #000000;
|
||||
--btn-home-bg: #ff7518;
|
||||
--btn-home-text: #000000;
|
||||
--callout-note-bg: rgba(255, 117, 24, 0.14);
|
||||
--callout-info-bg: rgba(168, 85, 247, 0.15);
|
||||
--callout-warning-bg: rgba(255, 154, 77, 0.18);
|
||||
--callout-warning-text: #ffaa5e;
|
||||
--body-radial-tint: rgba(10, 6, 4, 0.85);
|
||||
--skeleton-pulse-1: rgba(255, 117, 24, 0.06);
|
||||
--skeleton-pulse-2: rgba(255, 117, 24, 0.18);
|
||||
--spider-display: block;
|
||||
--cobweb-display: block;
|
||||
--spider-thread: rgba(255, 154, 77, 0.4);
|
||||
|
||||
--hl-comment: #7c4f34;
|
||||
--hl-punct: #fed7aa;
|
||||
--hl-func: #ff944d;
|
||||
--hl-keyword: #a855f7;
|
||||
--hl-string: #84cc16;
|
||||
--hl-var: #ff7518;
|
||||
--hl-op: #a855f7;
|
||||
--hl-num: #f59e0b;
|
||||
--hl-param: #ffedd5;
|
||||
--hl-class: #c084fc;
|
||||
--hl-tag: #ff7518;
|
||||
--hl-deleted-color: #ef4444;
|
||||
--hl-deleted-bg: rgba(239, 68, 68, 0.2);
|
||||
--hl-inserted-color: #84cc16;
|
||||
--hl-inserted-bg: rgba(132, 204, 22, 0.2);
|
||||
}
|
||||
|
||||
:root[data-theme="christmas"] {
|
||||
--bg: #07110c;
|
||||
--text: #f4efe6;
|
||||
--text: #ff5c5c;
|
||||
--muted: #8fa89a;
|
||||
--border: #1f3a2b;
|
||||
--bg-elevated: rgba(255, 92, 92, 0.1);
|
||||
--bg-card: rgba(10, 26, 18, 0.6);
|
||||
--bg-card-hover: rgba(18, 44, 30, 0.85);
|
||||
--bg-code: #040a07;
|
||||
--code-inline-bg: rgba(245, 196, 81, 0.14);
|
||||
--code-inline-text: #f5c451;
|
||||
--code-text: #e8f5e9;
|
||||
--bg-menu: #09150e;
|
||||
--bg-modal: rgba(7, 17, 12, 0.9);
|
||||
--hover-bg: rgba(255, 92, 92, 0.12);
|
||||
--search-bg: rgba(245, 196, 81, 0.08);
|
||||
--search-panel-bg: rgba(9, 21, 14, 0.95);
|
||||
--code-group-bg: #11261a;
|
||||
--code-group-panel-bg: #07110c;
|
||||
--code-tab-active: #ff5c5c;
|
||||
--footer-border: #1f3a2b;
|
||||
--border-subtle: rgba(245, 196, 81, 0.14);
|
||||
--btn-primary-bg: #d92d3a;
|
||||
--btn-primary-text: #ffffff;
|
||||
--btn-home-bg: #d92d3a;
|
||||
--btn-home-text: #ffffff;
|
||||
--callout-note-bg: rgba(47, 191, 113, 0.14);
|
||||
--callout-info-bg: rgba(245, 196, 81, 0.14);
|
||||
--callout-warning-bg: rgba(255, 92, 92, 0.18);
|
||||
--callout-warning-text: #ff9a9a;
|
||||
--body-radial-tint: rgba(7, 17, 12, 0.85);
|
||||
--skeleton-pulse-1: rgba(245, 196, 81, 0.06);
|
||||
--skeleton-pulse-2: rgba(245, 196, 81, 0.16);
|
||||
--snow-display: block;
|
||||
--lights-display: block;
|
||||
|
||||
--hl-comment: #5f8a72;
|
||||
--hl-punct: #e8f5e9;
|
||||
--hl-func: #f5c451;
|
||||
--hl-keyword: #ff6b6b;
|
||||
--hl-string: #5fd68f;
|
||||
--hl-var: #ff9f68;
|
||||
--hl-op: #ff6b6b;
|
||||
--hl-num: #f5c451;
|
||||
--hl-param: #fdf2d0;
|
||||
--hl-class: #7fe3b0;
|
||||
--hl-tag: #ff6b6b;
|
||||
--hl-deleted-color: #ff6b6b;
|
||||
--hl-deleted-bg: rgba(255, 107, 107, 0.18);
|
||||
--hl-inserted-color: #5fd68f;
|
||||
--hl-inserted-bg: rgba(95, 214, 143, 0.18);
|
||||
}
|
||||
|
||||
:root[data-theme="autumn"] {
|
||||
--bg: #1a0f08;
|
||||
--text: #f7ead7;
|
||||
--text: #f4b942;
|
||||
--muted: #a68a6d;
|
||||
--border: #3a2412;
|
||||
--bg-elevated: rgba(244, 185, 66, 0.1);
|
||||
--bg-card: rgba(40, 23, 11, 0.6);
|
||||
--bg-card-hover: rgba(58, 36, 18, 0.85);
|
||||
--bg-code: #0e0703;
|
||||
--code-inline-bg: rgba(244, 185, 66, 0.14);
|
||||
--code-inline-text: #f4b942;
|
||||
--code-text: #fde7c0;
|
||||
--bg-menu: #1a0f08;
|
||||
--bg-modal: rgba(26, 15, 8, 0.9);
|
||||
--hover-bg: rgba(244, 185, 66, 0.12);
|
||||
--search-bg: rgba(244, 185, 66, 0.08);
|
||||
--search-panel-bg: rgba(26, 15, 8, 0.95);
|
||||
--code-group-bg: #28170b;
|
||||
--code-group-panel-bg: #1a0f08;
|
||||
--code-tab-active: #f4b942;
|
||||
--footer-border: #3a2412;
|
||||
--border-subtle: rgba(244, 185, 66, 0.14);
|
||||
--btn-primary-bg: #f4b942;
|
||||
--btn-primary-text: #1a0f08;
|
||||
--btn-home-bg: #f4b942;
|
||||
--btn-home-text: #1a0f08;
|
||||
--callout-note-bg: rgba(200, 69, 44, 0.16);
|
||||
--callout-info-bg: rgba(132, 160, 60, 0.16);
|
||||
--callout-warning-bg: rgba(244, 185, 66, 0.18);
|
||||
--callout-warning-text: #f8cf7a;
|
||||
--body-radial-tint: rgba(26, 15, 8, 0.85);
|
||||
--skeleton-pulse-1: rgba(244, 185, 66, 0.06);
|
||||
--skeleton-pulse-2: rgba(244, 185, 66, 0.17);
|
||||
--leaf-display: block;
|
||||
|
||||
--hl-comment: #8a6a4a;
|
||||
--hl-punct: #fde7c0;
|
||||
--hl-func: #f4b942;
|
||||
--hl-keyword: #e0643f;
|
||||
--hl-string: #a3b94a;
|
||||
--hl-var: #e8913a;
|
||||
--hl-op: #e0643f;
|
||||
--hl-num: #f4b942;
|
||||
--hl-param: #fdf0d8;
|
||||
--hl-class: #d9a35b;
|
||||
--hl-tag: #e0643f;
|
||||
--hl-deleted-color: #e0643f;
|
||||
--hl-deleted-bg: rgba(224, 100, 63, 0.18);
|
||||
--hl-inserted-color: #a3b94a;
|
||||
--hl-inserted-bg: rgba(163, 185, 74, 0.18);
|
||||
}
|
||||
|
||||
:root[data-theme="winter"] {
|
||||
--bg: #0d1b2a;
|
||||
--text: #e6f0fa;
|
||||
--text: #d6ecff;
|
||||
--muted: #7f98b3;
|
||||
--border: #1b2c44;
|
||||
--bg-elevated: rgba(138, 209, 255, 0.1);
|
||||
--bg-card: rgba(16, 32, 52, 0.6);
|
||||
--bg-card-hover: rgba(26, 48, 76, 0.85);
|
||||
--bg-code: #08121e;
|
||||
--code-inline-bg: rgba(138, 209, 255, 0.14);
|
||||
--code-inline-text: #8ad1ff;
|
||||
--code-text: #cfe4f7;
|
||||
--bg-menu: #0d1b2a;
|
||||
--bg-modal: rgba(13, 27, 42, 0.88);
|
||||
--hover-bg: rgba(138, 209, 255, 0.1);
|
||||
--search-bg: rgba(138, 209, 255, 0.08);
|
||||
--search-panel-bg: rgba(13, 27, 42, 0.95);
|
||||
--code-group-bg: #132640;
|
||||
--code-group-panel-bg: #0d1b2a;
|
||||
--code-tab-active: #8ad1ff;
|
||||
--footer-border: #1b2c44;
|
||||
--border-subtle: rgba(138, 209, 255, 0.14);
|
||||
--btn-primary-bg: #8ad1ff;
|
||||
--btn-primary-text: #06101c;
|
||||
--btn-home-bg: #8ad1ff;
|
||||
--btn-home-text: #06101c;
|
||||
--callout-note-bg: rgba(138, 209, 255, 0.14);
|
||||
--callout-info-bg: rgba(185, 166, 255, 0.14);
|
||||
--callout-warning-bg: rgba(255, 214, 138, 0.16);
|
||||
--callout-warning-text: #ffd68a;
|
||||
--body-radial-tint: rgba(13, 27, 42, 0.85);
|
||||
--skeleton-pulse-1: rgba(138, 209, 255, 0.06);
|
||||
--skeleton-pulse-2: rgba(138, 209, 255, 0.16);
|
||||
--snow-display: block;
|
||||
|
||||
--hl-comment: #5b7491;
|
||||
--hl-punct: #cfe4f7;
|
||||
--hl-func: #8ad1ff;
|
||||
--hl-keyword: #b9a6ff;
|
||||
--hl-string: #9be7d4;
|
||||
--hl-var: #ffb4c7;
|
||||
--hl-op: #8ad1ff;
|
||||
--hl-num: #ffd68a;
|
||||
--hl-param: #e6f0fa;
|
||||
--hl-class: #a9e4ff;
|
||||
--hl-tag: #b9a6ff;
|
||||
--hl-deleted-color: #ff8fa3;
|
||||
--hl-deleted-bg: rgba(255, 143, 163, 0.18);
|
||||
--hl-inserted-color: #9be7d4;
|
||||
--hl-inserted-bg: rgba(155, 231, 212, 0.18);
|
||||
}
|
||||
|
||||
.cobweb {
|
||||
display: var(--cobweb-display);
|
||||
position: fixed;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
pointer-events: none;
|
||||
z-index: 35;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.cobweb-top-left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.cobweb-top-right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.cobweb-media {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.spider-container {
|
||||
display: var(--spider-display);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 12%;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
animation: spiderDescend 10s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.spider-thread {
|
||||
width: 1.5px;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--spider-thread);
|
||||
}
|
||||
|
||||
.spider-body {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-top: 100px;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
animation: spiderSway 3s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.spider-media {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
scale: 2;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
@keyframes spiderDescend {
|
||||
0% {
|
||||
transform: translateY(-80px);
|
||||
}
|
||||
|
||||
45% {
|
||||
transform: translateY(240px);
|
||||
}
|
||||
|
||||
55% {
|
||||
transform: translateY(240px);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-80px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spiderSway {
|
||||
0% {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spiderDrop {
|
||||
from {
|
||||
transform: translateY(240px);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(130vh);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--snow-tile: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cg fill='white'%3E%3Ccircle cx='22' cy='38' r='2.4' fill-opacity='.9'/%3E%3Ccircle cx='96' cy='126' r='1.8' fill-opacity='.7'/%3E%3Ccircle cx='168' cy='28' r='3' fill-opacity='.85'/%3E%3Ccircle cx='236' cy='204' r='2' fill-opacity='.75'/%3E%3Ccircle cx='272' cy='92' r='2.8' fill-opacity='.9'/%3E%3Ccircle cx='58' cy='252' r='2.2' fill-opacity='.8'/%3E%3Ccircle cx='196' cy='282' r='1.6' fill-opacity='.7'/%3E%3Ccircle cx='140' cy='190' r='2.4' fill-opacity='.85'/%3E%3C/g%3E%3Cg stroke='white' stroke-opacity='.9' stroke-width='1.3' stroke-linecap='round' fill='none'%3E%3Cpath transform='translate(126 72)' d='M0 -8V8M-6.9 -4L6.9 4M-6.9 4L6.9 -4'/%3E%3Cpath transform='translate(246 150)' d='M0 -6V6M-5.2 -3L5.2 3M-5.2 3L5.2 -3'/%3E%3Cpath transform='translate(40 160)' d='M0 -7V7M-6 -3.5L6 3.5M-6 3.5L6 -3.5'/%3E%3Cpath transform='translate(210 250)' d='M0 -6V6M-5.2 -3L5.2 3M-5.2 3L5.2 -3'/%3E%3C/g%3E%3C/svg%3E");
|
||||
--leaf-tile: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='360'%3E%3Cg transform='translate(50 60) rotate(25) scale(1.2)' fill='%23e8710a'%3E%3Cpath d='M0 -14C10 -8 12 6 0 16C-12 6 -10 -8 0 -14Z'/%3E%3Cpath d='M0 -12V14' stroke='%23000' stroke-opacity='.25' stroke-width='1'/%3E%3C/g%3E%3Cg transform='translate(190 40) rotate(-40)' fill='%23c2410c'%3E%3Cpath d='M0 -14C10 -8 12 6 0 16C-12 6 -10 -8 0 -14Z'/%3E%3Cpath d='M0 -12V14' stroke='%23000' stroke-opacity='.25' stroke-width='1'/%3E%3C/g%3E%3Cg transform='translate(300 130) rotate(70) scale(1.3)' fill='%23eab308'%3E%3Cpath d='M0 -14C10 -8 12 6 0 16C-12 6 -10 -8 0 -14Z'/%3E%3Cpath d='M0 -12V14' stroke='%23000' stroke-opacity='.25' stroke-width='1'/%3E%3C/g%3E%3Cg transform='translate(120 200) rotate(-15) scale(1.1)' fill='%23b45309'%3E%3Cpath d='M0 -14C10 -8 12 6 0 16C-12 6 -10 -8 0 -14Z'/%3E%3Cpath d='M0 -12V14' stroke='%23000' stroke-opacity='.25' stroke-width='1'/%3E%3C/g%3E%3Cg transform='translate(250 280) rotate(140) scale(1.2)' fill='%23d97706'%3E%3Cpath d='M0 -14C10 -8 12 6 0 16C-12 6 -10 -8 0 -14Z'/%3E%3Cpath d='M0 -12V14' stroke='%23000' stroke-opacity='.25' stroke-width='1'/%3E%3C/g%3E%3Cg transform='translate(40 310) rotate(-80)' fill='%23ca8a04'%3E%3Cpath d='M0 -14C10 -8 12 6 0 16C-12 6 -10 -8 0 -14Z'/%3E%3Cpath d='M0 -12V14' stroke='%23000' stroke-opacity='.25' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E");
|
||||
--lights-tile: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='56'%3E%3Cpath d='M0 4Q40 32 80 4Q120 32 160 4' fill='none' stroke='%23245c3a' stroke-width='1.6'/%3E%3Cg fill='%23222'%3E%3Crect x='18' y='13' width='4' height='4'/%3E%3Crect x='38' y='17' width='4' height='4'/%3E%3Crect x='58' y='13' width='4' height='4'/%3E%3Crect x='98' y='13' width='4' height='4'/%3E%3Crect x='118' y='17' width='4' height='4'/%3E%3Crect x='138' y='13' width='4' height='4'/%3E%3C/g%3E%3Cellipse cx='20' cy='22' rx='4' ry='5.5' fill='%23ff4d4d'/%3E%3Cellipse cx='40' cy='26' rx='4' ry='5.5' fill='%23ffd24d'/%3E%3Cellipse cx='60' cy='22' rx='4' ry='5.5' fill='%234dff88'/%3E%3Cellipse cx='100' cy='22' rx='4' ry='5.5' fill='%234da6ff'/%3E%3Cellipse cx='120' cy='26' rx='4' ry='5.5' fill='%23ff4d4d'/%3E%3Cellipse cx='140' cy='22' rx='4' ry='5.5' fill='%23ffd24d'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.snowfall,
|
||||
.leaf-fall {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.snowfall {
|
||||
display: var(--snow-display);
|
||||
}
|
||||
|
||||
.leaf-fall {
|
||||
display: var(--leaf-display);
|
||||
}
|
||||
|
||||
.snowfall::before,
|
||||
.snowfall::after,
|
||||
.leaf-fall::before,
|
||||
.leaf-fall::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background-repeat: repeat;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.snowfall::before {
|
||||
top: -600px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: var(--snow-tile);
|
||||
background-size: 300px 300px;
|
||||
opacity: 0.9;
|
||||
animation: snowFallNear 18s linear infinite;
|
||||
}
|
||||
|
||||
.snowfall::after {
|
||||
top: -500px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: var(--snow-tile);
|
||||
background-size: 500px 500px;
|
||||
opacity: 0.6;
|
||||
animation: snowFallFar 30s linear infinite;
|
||||
}
|
||||
|
||||
.leaf-fall::before {
|
||||
top: -720px;
|
||||
left: 0;
|
||||
right: -360px;
|
||||
bottom: 0;
|
||||
background-image: var(--leaf-tile);
|
||||
background-size: 360px 360px;
|
||||
opacity: 0.9;
|
||||
animation: leafFallNear 26s linear infinite;
|
||||
}
|
||||
|
||||
.leaf-fall::after {
|
||||
top: -540px;
|
||||
left: -540px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: var(--leaf-tile);
|
||||
background-size: 540px 540px;
|
||||
opacity: 0.7;
|
||||
animation: leafFallFar 40s linear infinite;
|
||||
}
|
||||
|
||||
.light-string {
|
||||
display: var(--lights-display);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
background-image: var(--lights-tile);
|
||||
background-repeat: repeat-x;
|
||||
background-size: 160px 56px;
|
||||
animation: lightTwinkle 2.6s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes snowFallNear {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(0, 600px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes snowFallFar {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(0, 500px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leafFallNear {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(-360px, 720px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leafFallFar {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(540px, 540px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lightTwinkle {
|
||||
from {
|
||||
opacity: 0.65;
|
||||
filter: drop-shadow(0 0 3px rgba(255, 214, 120, 0.5));
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
filter: drop-shadow(0 0 8px rgba(255, 214, 120, 0.9));
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
.snowfall::before,
|
||||
.snowfall::after,
|
||||
.leaf-fall::before,
|
||||
.leaf-fall::after,
|
||||
.light-string {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
#navbar .brand {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#navbar .brand::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
right: -9px;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:root[data-theme="halloween"] #navbar .brand::after {
|
||||
content: "🎃";
|
||||
}
|
||||
|
||||
:root[data-theme="autumn"] #navbar .brand::after {
|
||||
content: "🍂";
|
||||
}
|
||||
|
||||
:root[data-theme="winter"] #navbar .brand::after {
|
||||
content: "❄️";
|
||||
}
|
||||
|
||||
:root[data-theme="christmas"] #navbar .brand::after {
|
||||
content: "🎄";
|
||||
}
|
||||
|
||||
.st-themes {
|
||||
padding: 14px 16px 16px;
|
||||
}
|
||||
|
||||
.st-sub {
|
||||
margin: 0 0 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.st-theme-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.st-theme-grid:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.st-theme {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
height: 40px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 100px;
|
||||
background: var(--search-bg);
|
||||
color: var(--muted);
|
||||
font: inherit;
|
||||
font-size: 13.5px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.st-theme .ico {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.st-theme span {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.st-theme:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.st-theme.on {
|
||||
border-color: var(--text);
|
||||
background: var(--hover-bg);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -12,14 +12,14 @@ Provider availability and performance can vary based on your network environment
|
||||
|
||||
## How Providers Are Used
|
||||
|
||||
When your app calls `getStream()`—directly or through Stream API:
|
||||
When your app calls `getStream()`-directly or through Stream API:
|
||||
|
||||
1. All active (non-disabled) providers are queried **simultaneously** with per-source random jitter to stagger upstream requests
|
||||
2. Each provider has its own **timeout**, **retry count**, and **backoff delay**
|
||||
3. Every returned URL is **verified** — first via a HEAD request to the raw upstream URL, then via a proxied HLS playability check
|
||||
3. Every returned URL is **verified** - first via a HEAD request to the raw upstream URL, then via a proxied HLS playability check
|
||||
4. Only URLs that pass both verification steps appear in the response as `source` events
|
||||
|
||||
Because the endpoint is SSE, **verified sources are emitted immediately as each provider resolves** — your player receives them progressively. The stream closes once every provider has either resolved or timed out.
|
||||
Because the endpoint is SSE, **verified sources are emitted immediately as each provider resolves** - your player receives them progressively. The stream closes once every provider has either resolved or timed out.
|
||||
|
||||
:::callout-info
|
||||
Call `sdk.getSources(true)` to inspect enabled providers and `sdk.probeAllSources()` for a live availability signal. If you use Stream API, its `/health` endpoint exposes probes over HTTP.
|
||||
@@ -68,7 +68,7 @@ CACHE_TTL = 5 minutes
|
||||
All provider stream URLs are wrapped in the server proxy (unless `skipProxy: true`):
|
||||
|
||||
```
|
||||
http://localhost:7860/api?url=<encoded_stream_url>&<proxyParam>=1
|
||||
http://YOUR_API_URL/api?url=<encoded_stream_url>&<proxyParam>=1
|
||||
```
|
||||
|
||||
When your player fetches this path from your self-hosted instance, the server:
|
||||
@@ -83,5 +83,5 @@ When your player fetches this path from your self-hosted instance, the server:
|
||||
|
||||
Before a URL is emitted as a `source` event, it passes two verification steps:
|
||||
|
||||
1. **`verifyStream`** — a HEAD request to the raw upstream URL. Must return HTTP `< 400`.
|
||||
2. **`verifyPlayable`** — fetches the proxied URL, validates `#EXTM3U`, checks for `#EXTINF` or `#EXT-X-STREAM-INF`, then fetches the first segment.
|
||||
1. **`verifyStream`** - a HEAD request to the raw upstream URL. Must return HTTP `< 400`.
|
||||
2. **`verifyPlayable`** - fetches the proxied URL, validates `#EXTM3U`, checks for `#EXTINF` or `#EXT-X-STREAM-INF`, then fetches the first segment.
|
||||
78
home.txt
78
home.txt
@@ -93,19 +93,14 @@
|
||||
gap: 6px;
|
||||
padding: 10px 22px;
|
||||
border-radius: 100px;
|
||||
background: #000000;
|
||||
color: #ffffff !important;
|
||||
background: var(--btn-primary-bg);
|
||||
color: var(--btn-primary-text) !important;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
text-decoration: none !important;
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
html.dark .btn-primary {
|
||||
background: #FFFFFF;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -156,6 +151,11 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
font-size: 14.5px;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.section-head {
|
||||
margin-bottom: 22px;
|
||||
text-align: left;
|
||||
@@ -166,13 +166,13 @@
|
||||
font-size: 22px !important;
|
||||
font-weight: 700 !important;
|
||||
margin: 0 0 6px !important;
|
||||
color: #ffffff !important;
|
||||
color: var(--text) !important;
|
||||
}
|
||||
|
||||
.section-head p {
|
||||
font-size: 14.5px;
|
||||
margin: 0;
|
||||
color: #a1a1aa;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.vyla-grid {
|
||||
@@ -247,11 +247,71 @@
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.hero-greeting {
|
||||
margin: 0 0 14px;
|
||||
padding: 6px 16px;
|
||||
border-radius: 100px;
|
||||
background: var(--hover-bg);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
color: var(--text);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.hero-resume {
|
||||
max-width: min(90vw, 360px);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
#header-container {
|
||||
border-radius: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-height: 550px) {
|
||||
.home-header {
|
||||
height: auto;
|
||||
min-height: 280px;
|
||||
padding-top: calc(var(--header-height) + 24px);
|
||||
padding-bottom: 28px;
|
||||
}
|
||||
|
||||
.header-logo-badge {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 26px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
font-size: 13.5px;
|
||||
margin-bottom: 14px;
|
||||
max-width: 540px;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.vyla-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.feature-box {
|
||||
padding: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="home-container">
|
||||
|
||||
29
index.html
29
index.html
@@ -3,8 +3,11 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Vyla Entertainment — Vyla</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
|
||||
<title>Vyla Entertainment - Vyla</title>
|
||||
<meta name="description"
|
||||
content="Vyla Entertainment is a community-built, decentralized media platform and developer ecosystem. Featuring native client apps for Desktop, Mobile, and Android TV, a scraping SDK, and a self-hosted Stream API." />
|
||||
<meta name="keywords"
|
||||
@@ -15,7 +18,7 @@
|
||||
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="Vyla Entertainment — Free & Decentralized Media Platform" />
|
||||
<meta property="og:title" content="Vyla Entertainment - Free & Decentralized Media Platform" />
|
||||
<meta property="og:description"
|
||||
content="A community-built media ecosystem focused on freedom, privacy, and control. Featuring native Desktop, Android, and TV apps backed by modular developer tools." />
|
||||
<meta property="og:url" content="https://vyla.cc/" />
|
||||
@@ -26,7 +29,7 @@
|
||||
<meta property="og:image:alt" content="Vyla Entertainment Ecosystem" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="Vyla Entertainment — Free & Decentralized Media Platform" />
|
||||
<meta name="twitter:title" content="Vyla Entertainment - Free & Decentralized Media Platform" />
|
||||
<meta name="twitter:description"
|
||||
content="Explore Vyla Home, Go, TV, and the developer SDK. Built for privacy, performance, and complete streaming control." />
|
||||
<meta name="twitter:image" content="/assets/images/site_preview.png" />
|
||||
@@ -77,6 +80,9 @@
|
||||
<link rel="stylesheet" href="/assets/styling/layout.css" />
|
||||
<link rel="stylesheet" href="/assets/styling/prose.css" />
|
||||
<link rel="stylesheet" href="/assets/styling/components.css" />
|
||||
<link rel="stylesheet" href="/assets/styling/themes.css" />
|
||||
<link rel="stylesheet" href="/assets/styling/overlay.css" />
|
||||
<link rel="stylesheet" href="/assets/styling/features.css" />
|
||||
<link rel="stylesheet" href="/assets/styling/highlight.css" />
|
||||
<link rel="stylesheet" href="/assets/styling/404.css" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
@@ -87,7 +93,12 @@
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
var saved = localStorage.getItem('vyla-theme') || 'halloween';
|
||||
var saved = localStorage.getItem('vyla-theme') || 'auto';
|
||||
if (saved === 'auto') {
|
||||
var d = new Date();
|
||||
var m = d.getMonth();
|
||||
saved = m === 9 ? 'halloween' : m === 10 ? 'autumn' : m === 11 ? 'christmas' : (m === 0 || m === 1) ? 'winter' : (m === 8 && d.getDate() >= 22) ? 'autumn' : 'dark';
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme', saved);
|
||||
})();
|
||||
</script>
|
||||
@@ -106,10 +117,13 @@
|
||||
<img class="spider-media" src="https://www.pngmart.com/files/1/Spider-PNG-File.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="snowfall" aria-hidden="true"></div>
|
||||
<div class="leaf-fall" aria-hidden="true"></div>
|
||||
<div class="light-string" aria-hidden="true"></div>
|
||||
<video class="video-bg" autoplay muted loop playsinline></video>
|
||||
<div id="banner">
|
||||
<span>
|
||||
Rights holders can request source removal — <a href="/misc/project/dmca">DMCA policy</a>.
|
||||
Rights holders can request source removal - <a href="/misc/project/dmca">DMCA policy</a>.
|
||||
</span>
|
||||
<button id="banner-close" onclick="dismissBanner()" aria-label="Dismiss">✕</button>
|
||||
</div>
|
||||
@@ -120,6 +134,8 @@
|
||||
height="28" decoding="async" /></a>
|
||||
<button id="search-trigger" type="button" onclick="openSearch()" aria-label="Search"></button>
|
||||
<div class="links">
|
||||
<button id="settings-trigger" type="button" onclick="openSettings()"
|
||||
aria-label="Personalize"></button>
|
||||
<button id="theme-toggle" type="button" onclick="toggleThemeMenu(event)"
|
||||
aria-label="Theme"></button>
|
||||
</div>
|
||||
@@ -139,6 +155,7 @@
|
||||
<a id="footer-next" class="footer-link next" href="#"></a>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p id="footer-note"></p>
|
||||
<div class="footer-socials">
|
||||
<a href="https://discord.gg/5eqRhKeywG" target="_blank" rel="noopener"
|
||||
aria-label="Discord">
|
||||
|
||||
@@ -20,7 +20,7 @@ At no point does Vyla store, transcode, or re-host any media file.
|
||||
|
||||
## Content Sources
|
||||
|
||||
The following third-party sources are scraped or proxied by Vyla API. These are upstream providers — Vyla does not control, operate, or own any of them.
|
||||
The following third-party sources are scraped or proxied by Vyla API. These are upstream providers - Vyla does not control, operate, or own any of them.
|
||||
|
||||
You can find the full list of sources in the [stream-api sources directory](https://gitlab.com/vyla-entertainment/sdk/-/tree/main/src/sources).
|
||||
|
||||
|
||||
5
misc/project/changelog.md
Normal file
5
misc/project/changelog.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
<p class="page-description">Release notes for every Vyla project, pulled live from GitLab.</p>
|
||||
|
||||
<div id="changelog" class="changelog"></div>
|
||||
@@ -53,3 +53,11 @@ Massive thanks to:
|
||||
- The P-Stream contributors
|
||||
- The CinePro contributors
|
||||
- The Movieslay contributors
|
||||
|
||||
---
|
||||
|
||||
## Contributors
|
||||
|
||||
People who have committed to Vyla projects, pulled live from GitLab.
|
||||
|
||||
<div id="contributors" class="contributors"></div>
|
||||
20
misc/project/story.md
Normal file
20
misc/project/story.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Our story
|
||||
|
||||
<p class="page-description">Why Vyla exists, what we care about, and how you can help.</p>
|
||||
|
||||
Vyla started as a small web project with a simple goal: a cleaner, more private way to discover and watch movies and shows without relying on a single company or centralized service. It has since grown into apps for desktop, Android and Android TV, plus the tools that power them.
|
||||
|
||||
Vyla is software, not a streaming service. We do not host or provide any media.
|
||||
|
||||
## What we care about
|
||||
|
||||
- **Control.** You decide how much of Vyla you use and where it runs.
|
||||
- **Privacy.** Vyla stays local and self-hostable, with no unnecessary centralized dependencies.
|
||||
- **Community.** Vyla is built in the open by the people who use it.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- Pick an app: [Vyla Home](/products/home), [Vyla Go](/products/go) or [Vyla TV](/products/tv).
|
||||
- Build with Vyla: start with the [SDK quickstart](/sdk/vyla-sdk/quickstart) or the [Stream API](/stream-api/overview).
|
||||
- Help fix sources: read the [contributing guide](/sdk/vyla-sdk/contributing).
|
||||
- See who made this possible on the [credits page](/misc/project/credit).
|
||||
@@ -12,6 +12,6 @@ Vyla Go is available for Android. Get the latest version below:
|
||||
|
||||
| Platform | Download |
|
||||
| --- | --- |
|
||||
| Android | [Download .apk](https://gitlab.com/vyla-entertainment/go/-/releases) |
|
||||
| Android | [Download: v0.0.0 (.apk)](https://gitlab.com/vyla-entertainment/go/-/releases) |
|
||||
|
||||
Inside the releases page, you can find the latest version of Vyla Go for Android, find the `.apk` file, and install it on your device.
|
||||
Clicking the download button will retrieve the latest `.apk` package for your device.
|
||||
@@ -14,11 +14,11 @@ Vyla Home is available for Windows, macOS, and Linux. Get the latest version bel
|
||||
|
||||
| Platform | Download |
|
||||
| --- | --- |
|
||||
| Windows | [Download .exe](https://gitlab.com/vyla-entertainment/home/-/releases) |
|
||||
| macOS | [Download .dmg](https://gitlab.com/vyla-entertainment/home/-/releases) |
|
||||
| Windows | [Download: v0.0.0 (.exe)](https://gitlab.com/vyla-entertainment/home/-/releases) |
|
||||
| macOS | [Download: v0.0.0 (.dmg)](https://gitlab.com/vyla-entertainment/home/-/releases) |
|
||||
| Linux | [Build from source](#linux-setup) |
|
||||
|
||||
Inside the releases page, you can find the latest version of Vyla Home for each platform, find the `.exe` for windows, and `.dmg` for macOS.
|
||||
Clicking the download button will retrieve the latest installer for Windows or macOS.
|
||||
|
||||
## Linux Setup
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@ Vyla TV is available for Android TV. Get the latest version below:
|
||||
|
||||
| Platform | Download |
|
||||
| --- | --- |
|
||||
| Android TV | [Download .apk](https://gitlab.com/vyla-entertainment/TV/-/releases) |
|
||||
| Android TV | [Download: v0.0.0 (.apk)](https://gitlab.com/vyla-entertainment/TV/-/releases) |
|
||||
|
||||
Inside the releases page, you can find the latest version of Vyla TV for Android TV, find the `.apk` file, and install it on your device.
|
||||
Clicking the download button will retrieve the latest `.apk` package for your Android TV device.
|
||||
170
sdk/vyla-sdk/contributing.md
Normal file
170
sdk/vyla-sdk/contributing.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# Contributing to Sources
|
||||
|
||||
<p class="page-description">Help maintain, fix broken scrapers, and add new video or download providers to the Vyla SDK.</p>
|
||||
|
||||
Third-party streaming and scraping endpoints frequently change their DOM layout, API endpoints, tokens, and CDN protections. Community contributions are vital to keeping providers operational.
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Clone the repository
|
||||
|
||||
Clone the SDK repository to your local machine:
|
||||
|
||||
```bash
|
||||
git clone https://gitlab.com/vyla-entertainment/vyla-sdk.git
|
||||
cd vyla-sdk
|
||||
```
|
||||
|
||||
### 2. Install dependencies
|
||||
|
||||
Ensure you are running Node.js 18 or newer:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 3. Environment configuration
|
||||
|
||||
Copy the example environment file and add your TMDB API key:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Open `.env` and set your key:
|
||||
|
||||
```ini
|
||||
TMDB_API_KEY=your_actual_tmdb_api_key_here
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `src/sources/`: Scraper implementations for each provider.
|
||||
- `src/config.js`: Source registry containing timeouts, retries, headers, and proxy flags.
|
||||
- `src/downloads/downloads.js`: Downloader resolver extracting direct MP4 streams.
|
||||
- `src/subtitles.js`: Subtitle mirror aggregation endpoints.
|
||||
- `test/index.js`: Test runner for testing streams, subtitles, and downloads.
|
||||
|
||||
---
|
||||
|
||||
## Testing Sources
|
||||
|
||||
Testing is driven by `test/index.js`. Open the file and update `CONFIG` to target the source you are fixing or adding:
|
||||
|
||||
```javascript
|
||||
const CONFIG = {
|
||||
DEBUG: true,
|
||||
|
||||
MOVIE_ID: "936075",
|
||||
TV_ID: "37854",
|
||||
TV_SEASON: "1",
|
||||
TV_EPISODE: "1",
|
||||
STREAM_SOURCE: "vidrock",
|
||||
|
||||
TEST_SUBTITLE: false,
|
||||
TEST_DOWNLOAD: false,
|
||||
TEST_MOVIE: true,
|
||||
TEST_TV: true,
|
||||
|
||||
EXTRA_SDK_PARAMS: {}
|
||||
};
|
||||
```
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
When `DEBUG: true` is enabled, all outbound fetch calls, headers, request payloads, and raw responses will be logged to your terminal and saved to `test/debug-output.json`.
|
||||
|
||||
---
|
||||
|
||||
## Fixing or Adding a Source
|
||||
|
||||
### 1. Source Resolver
|
||||
|
||||
All source files are located in `src/sources/`. Each module must export an asynchronous `getStream` function:
|
||||
|
||||
```javascript
|
||||
export async function getStream({ id, s, e, clientIP, audio, sdk, config }) {
|
||||
const isTv = Boolean(s && e);
|
||||
const mediaId = id;
|
||||
|
||||
const streamUrl = "https://example-cdn.com/stream/playlist.m3u8";
|
||||
|
||||
return {
|
||||
url: streamUrl,
|
||||
quality: "auto",
|
||||
isM3U8: true,
|
||||
headers: {
|
||||
Referer: "https://example-provider.to/",
|
||||
Origin: "https://example-provider.to"
|
||||
},
|
||||
allUrls: [
|
||||
{
|
||||
url: streamUrl,
|
||||
quality: "1080p",
|
||||
isM3U8: true
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Registering in `src/config.js`
|
||||
|
||||
Add or update the source entry inside the `SOURCES` array in `src/config.js`:
|
||||
|
||||
```javascript
|
||||
{
|
||||
key: 'mysource',
|
||||
label: 'MySource',
|
||||
sourceFile: 'mysource',
|
||||
proxyParam: 'ms',
|
||||
timeout: 25000,
|
||||
jitter: 500,
|
||||
retries: 2,
|
||||
multiUrl: true,
|
||||
skipProxy: true,
|
||||
cdnHeaders: [{
|
||||
pattern: /cdn\.mysource\.to/i,
|
||||
headers: {
|
||||
Referer: 'https://mysource.to/',
|
||||
Origin: 'https://mysource.to',
|
||||
},
|
||||
}],
|
||||
}
|
||||
```
|
||||
|
||||
- `skipProxy`: Set to `true` if the stream plays directly in client players without server-side proxying.
|
||||
- `multiUrl`: Set to `true` if the scraper provides multiple resolutions or fallback mirrors.
|
||||
- `cdnHeaders`: Include custom headers required by the upstream CDN to avoid HTTP 403 Forbidden responses.
|
||||
|
||||
---
|
||||
|
||||
## Download Providers
|
||||
|
||||
Downloads are handled automatically by `src/downloads/downloads.js`. The downloader queries active sources that have `skipProxy: true` enabled and inspects returned streams:
|
||||
|
||||
- URLs ending in `.mp4` or containing `.mp4` queries are automatically extracted.
|
||||
- `.m3u8` streams are filtered out because they are HLS segment playlists rather than direct file downloads.
|
||||
- If you are implementing a provider that supplies raw MP4 video files, return them in `allUrls` or `url` with `skipProxy: true` in `src/config.js`.
|
||||
|
||||
To verify download resolution in `test/index.js`, toggle:
|
||||
|
||||
```javascript
|
||||
TEST_DOWNLOAD: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
1. Verify that your updated source passes tests for both movies (`TEST_MOVIE: true`) and TV shows (`TEST_TV: true`).
|
||||
2. Ensure you have not committed your `.env` file or personal TMDB API keys.
|
||||
3. Push your branch and open a Merge Request on GitLab.
|
||||
@@ -19,7 +19,7 @@ The SDK can be used independently of the self-hosted Stream API. If you prefer t
|
||||
- **Automated Anime Sub/Dub Routing**: Automatically configures audio track requirements (`sub` vs `dub`) based on provider naming conventions.
|
||||
- **Multi-Source Subtitle Aggregation**: Merges subtitle tracks from multiple redundant subtitle mirrors into a unified VTT/SRT format.
|
||||
- **Metadata Fallbacks**: Automatically maps TMDB IDs to external IDs (such as IMDB and AniList) for smooth stream lookups.
|
||||
- **Self-Contained**: No need to deploy a separate API server — all streaming functionality runs within your existing Node.js application.
|
||||
- **Self-Contained**: No need to deploy a separate API server - all streaming functionality runs within your existing Node.js application.
|
||||
|
||||
---
|
||||
|
||||
@@ -82,8 +82,8 @@ const geoStream = await sdk.getStream("sourcename", "155", null, null, "203.0.11
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `key` | `string` | **Yes** | — | Unique key identifier for the provider (e.g. `"sourcename"`). |
|
||||
| `id` | `string` | **Yes** | — | TMDB media ID (e.g., `"155"`). |
|
||||
| `key` | `string` | **Yes** | - | Unique key identifier for the provider (e.g. `"sourcename"`). |
|
||||
| `id` | `string` | **Yes** | - | TMDB media ID (e.g., `"155"`). |
|
||||
| `s` | `string \| number` | **No** | `null` | Season number (required for TV shows). |
|
||||
| `e` | `string \| number` | **No** | `null` | Episode number (required for TV shows). |
|
||||
| `clientIP` | `string` | **No** | `null` | End-user IP address forwarded for geo-restricted scrapers. |
|
||||
@@ -127,7 +127,7 @@ const tvSubs = await sdk.getSubtitles("1399", 1, 1);
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `id` | `string` | **Yes** | — | TMDB media ID. |
|
||||
| `id` | `string` | **Yes** | - | TMDB media ID. |
|
||||
| `s` | `string \| number` | **No** | `null` | Season number (required for TV shows). |
|
||||
| `e` | `string \| number` | **No** | `null` | Episode number (required for TV shows). |
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ GA_API_SECRET=
|
||||
| `GA_MEASUREMENT_ID` / `GA_API_SECRET` | No | Enables Google Analytics event reporting. Leave blank to disable. |
|
||||
| `PORT` | No | Port the server listens on. Defaults to `7860`. |
|
||||
| `WORKER_COUNT` | No | Number of cluster workers to fork. Defaults to `1` locally. |
|
||||
| `ENABLE_DEBUG_ROUTE` | No | Set to `true` to enable `/api/debug/:id`. Keep this `false` in production — it exposes upstream request/response headers and raw source output. |
|
||||
| `ENABLE_DEBUG_ROUTE` | No | Set to `true` to enable `/api/debug/:id`. Keep this `false` in production - it exposes upstream request/response headers and raw source output. |
|
||||
| `PROXY_STREAMS` | No | Set to `true` to route stream playback through your own server's `/api` proxy endpoint instead of direct URLs. |
|
||||
| `PROXY_URL` | No | An external proxy base URL to use instead of the built-in `/api` proxy. Only used if `PROXY_STREAMS` is enabled. |
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Use this only when HTTP is the right boundary
|
||||
|
||||
Stream API packages Vyla SDK behind a small Node.js HTTP service. It is useful when a browser, mobile app, or another process needs progressive playback candidates over HTTP. It is not required to use Vyla, and it is not the primary integration for a Node.js application—use [Vyla SDK](/sdk) directly in that case.
|
||||
Stream API packages Vyla SDK behind a small Node.js HTTP service. It is useful when a browser, mobile app, or another process needs progressive playback candidates over HTTP. It is not required to use Vyla, and it is not the primary integration for a Node.js application-use [Vyla SDK](/sdk) directly in that case.
|
||||
|
||||
The service accepts TMDB IDs, asks configured SDK providers for candidates, verifies results, and emits usable sources as they arrive. A player can start with the first source while retaining later sources as fallbacks.
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
|
||||
`GET /health` (or `/api/health`) probes active providers and returns a JSON summary. It includes a status, timestamp, TMDB-key presence, cache size, and a map of provider keys to `{ ok, ms }` results.
|
||||
|
||||
Health is a signal, not a promise of playback. Provider behavior can differ by title, client network, and the moment of the request. Use it for observability and source selection—not as a guarantee that a viewer will receive a stream.
|
||||
Health is a signal, not a promise of playback. Provider behavior can differ by title, client network, and the moment of the request. Use it for observability and source selection-not as a guarantee that a viewer will receive a stream.
|
||||
|
||||
## Test one provider
|
||||
|
||||
`GET /test/:id?source=:providerKey` resolves a single provider for a movie. Add `season` and `episode` (or `s` and `e`) for TV.
|
||||
|
||||
```bash
|
||||
curl "http://localhost:7860/test/155?source=provider-key"
|
||||
curl "http://localhost:7860/test/1396?season=1&episode=1&source=provider-key"
|
||||
curl "YOUR_API_URL/test/155?source=provider-key"
|
||||
curl "YOUR_API_URL/test/1396?season=1&episode=1&source=provider-key"
|
||||
```
|
||||
|
||||
Use source keys reported by the running deployment. A test result reports whether resolution and verification succeeded; clients should inspect the result body rather than relying only on a transport success.
|
||||
|
||||
@@ -14,8 +14,8 @@ The API supports both the short paths below and `/api`-prefixed equivalents. A m
|
||||
`id` is a TMDB ID. For example:
|
||||
|
||||
```bash
|
||||
curl -N "http://localhost:7860/movie?id=155"
|
||||
curl -N "http://localhost:7860/tv?id=1396&season=1&episode=1"
|
||||
curl -N "YOUR_API_URL/movie?id=155"
|
||||
curl -N "YOUR_API_URL/tv?id=1396&season=1&episode=1"
|
||||
```
|
||||
|
||||
## Event contract
|
||||
@@ -44,7 +44,7 @@ One event for each candidate that passes the service's verification pipeline.
|
||||
"source": {
|
||||
"source": "provider-key",
|
||||
"label": "Provider name",
|
||||
"url": "http://localhost:7860/api?url=…"
|
||||
"url": "YOUR_API_URL/api?url=…"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user