initial completed version

This commit is contained in:
Liu Xiaoyi 2024-03-31 01:12:25 +08:00
parent 92ce69c076
commit 5e7f81f61f
No known key found for this signature in database
GPG Key ID: A04E02BF7E977471
13 changed files with 558 additions and 153 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,9 @@
<div class="row">
<h3 id="mirror-title">{% fa_svg fas.fa-cube %} 镜像列表 </h3>
{% unless page.legacy %}
<input type="search" v-model.trim="filter" id="search" ref="search" placeholder="按 / 搜索" autocomplete="off">
<span class="input-wrapper input-wrapper-search">
<input type="search" v-model.trim="filter" id="search" ref="search" placeholder="按 / 搜索" autocomplete="off">
</span>
{% endunless %}
</div>
<table class="table" v-if="mirrorList.length">
@ -69,7 +71,7 @@
{% endraw %}
{% else %}
{% raw %}
<tr v-for="mir in filteredMirrorList" :class="['row', 'status-'+mir.status]" :key="mir.name">
<tr v-for="mir in mappedMirrorList" :class="['row', 'status-'+mir.status]" :key="mir.name" :display="mir.shown">
<td class="col-md-8">
<a class="mirror-item-label" data-toggle="popover" data-trigger="hover" data-placement="right"
:data-content="mir.description" :href="getURL(mir)" :aria-label="mir.name + ', ' + mir.description">
@ -128,9 +130,11 @@
<div id="download-link">
<h4>{% fa_svg far.fa-file-zipper %} 下载链接 </h4>
<p>常用发行版 iso 和应用工具安装包直接下载</p>
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#isoModal">
获取下载链接
</button>
<span class="input-wrapper" style="border-radius: 4px">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#isoModal">
获取下载链接
</button>
</span>
</div>
{% endunless %}
<div class="thuhidden {%if site.issue%}col-md-3{%endif%}">

View File

@ -7,26 +7,26 @@ BOOTSTRAP MODIFICATIONS & TWEAKS
}
.nav > li > a:hover, .nav > li > a:focus{
@include dark{
@include dark(&) {
background-color: #111;
}
}
pre{
@include dark{
@include dark(&) {
color: #ccc;
background-color: #272822;
}
}
code{
@include dark{
@include dark(&) {
background-color: #060d0b;
}
}
.navbar-default {
@include dark{
@include dark(&) {
background-color: $color_bg_dark;
}
@include noissue {
@ -37,13 +37,13 @@ code{
.navbar-nav > li.active > a {
color: $color_thu_purple;
border-bottom: 2px solid $color_thu_purple;
@include dark{
@include dark(&) {
color: $color_thu_purple_dark;
border-bottom-color: $color_thu_purple_dark;
}
}
.navbar-nav > li > a {
@include dark{
@include dark(&) {
color: $color_secondary_dark;
}
color: $color_secondary;
@ -54,7 +54,7 @@ code{
&:hover, &:focus {
color: $color_primary !important;
background: transparent !important;
@include dark{
@include dark(&) {
color: $color_primary_dark !important;
}
}
@ -73,7 +73,7 @@ code{
.navbar-header .navbar-brand {
color: $color_secondary;
@include dark{
@include dark(&) {
color: $color_secondary_dark;
&:hover {
color: #fff;
@ -82,7 +82,7 @@ code{
}
.dropdown-menu {
@include dark{
@include dark(&) {
background: $color_secondary_dark;
}
background: $color_secondary ;
@ -92,7 +92,7 @@ code{
@include noissue {
color: white;
}
@include dark{
@include dark(&) {
color: $color_bg_dark;
}
font-weight: 700;
@ -127,7 +127,7 @@ table .row{
}
}
@include dark{
@include dark(&) {
.popover{
background-color: #282828;
border-color: rgba(255, 255, 255, 0.2);

View File

@ -52,11 +52,20 @@
animation-duration: 0.2s;
animation-timing-function: ease-in;
animation-name: icon-exit;
&::after {
font-size: 12px;
position: absolute;
right: 100%;
bottom: 50%;
opacity: 0;
}
}
@each $mode in "light", "dark", "darker", "lighter" {
&[data-mode="#{$mode}"] {
#{$this}-icon[data-active="#{$mode}"] {
position: relative;
transition: transform .2s ease-out, opacity .2s ease-out;
transition-delay: .15s;
opacity: .8;
@ -81,6 +90,30 @@
&:hover {
opacity: 1;
}
@keyframes hint-reveal {
0% {
transform: translate(0, 50%);
opacity: 1;
}
100% {
transform: translate(10px, 50%);
opacity: 0;
}
}
&::after {
content: "#{$mode}";
animation-fill-mode: both;
animation-iteration-count: 1;
animation-name: hint-reveal;
animation-duration: .2s;
animation-delay: 2s;
@if $mode == "darker" {
animation-delay: 20s;
}
}
}
}
}
@ -109,9 +142,34 @@
z-index: 100000;
pointer-events: none;
mix-blend-mode: multiply;
body.darker-cleanup & {
transition: opacity 1s ease-in;
opacity: 0;
}
}
.label-status {
.darker-engage {
opacity: 0;
pointer-events: none;
transition: opacity 1s ease-in;
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
z-index: 100001;
background: black;
body.darker-engaging & {
transition: none;
opacity: 1;
pointer-events: all;
}
}
.label-status, .label-new {
position: relative;
}
@ -126,6 +184,94 @@
pointer-events: none;
}
.input-wrapper {
display: inline-block;
position: relative;
&-search {
margin: 20px 10px 0 0;
float: right;
border-radius: 1px;
input#search {
margin: 0;
}
}
}
.popover {
position: relative;
}
#mirror-list tbody .row:not([display="true"]) {
display: none;
}
.brand-img-text {
fill: #2f4b68;
@include dark(&) {
fill: white;
}
}
@keyframes flame-jump {
0% {
transform: translate(-50%, -50%) scale(1.5);
}
50% {
transform: translate(-50%, -50%) scale(1.7);
}
100% {
transform: translate(-50%, -50%) scale(1.5);
}
}
.flames {
position: absolute;
pointer-events: none;
top: 0;
left: 0;
svg {
position: absolute;
transform: translate(-50%, -50%);
animation: flame-jump .2s infinite linear;
use {
fill: rgb(240, 100, 24);
}
}
}
.dark-switch-hint {
width: 200px;
position: absolute;
right: 18px;
top: 100%;
transform: translate(50%, 0px);
border-radius: 8px;
box-shadow: rgba(0,0,0,.3) 0 4px 12px;
text-align: center;
padding: 10px 20px;
font-size: 12px;
z-index: 10;
background: white;
cursor: pointer;
transition: opacity .2s ease, transform .2s ease;
@include dark(&) {
background: $color_bg_dark;
}
&-ack {
transform: translate(50%, 20px);
opacity: 0;
pointer-events: none;
}
}
@font-face {
font-family: 'Source Han Sans SC';
src: url('../fonts/sss-regular.otf') format('otf'),

View File

@ -11,7 +11,7 @@ body {
margin: 0;
/* height: 100%; */
color: $color_secondary;
@include dark{
@include dark(&) {
color: $color_secondary_dark;
}
font-family: $font_default;
@ -33,7 +33,7 @@ p {
font-size: 14px;
line-height: 24px;
color: $color_secondary ;
@include dark{
@include dark(&) {
color: $color_secondary_dark;
}
margin-top: 10px;
@ -46,7 +46,7 @@ img {
a {
color:$color_link;
@include dark{
@include dark(&) {
color: $color_link_dark;
}
padding: 0;
@ -68,7 +68,7 @@ a:hover, a:focus {
color: #fff;
text-shadow:none;
background:$color_secondary;
@include dark{
@include dark(&) {
background: $color_secondary_dark;
}
}
@ -76,7 +76,7 @@ a:hover, a:focus {
color: #fff;
text-shadow:none;
background:$color_secondary;
@include dark{
@include dark(&) {
background: white;
color: $color_secondary;
}

View File

@ -84,7 +84,7 @@
.hljs-tag { color: #000080 } // Name.Tag
.hljs-variable { color: #008080 } // Name.Variable
@include dark{
@include dark(&) {
.highlight {
.hll { background-color: #272822; }
.c { color: #75715e } /* Comment */

View File

@ -45,11 +45,19 @@ $color_bg_dark: #222;
$color_primary: $color_thu_purple;
$color_primary_dark: $color_thu_purple_dark;
@mixin dark {
@mixin dark($cur) {
{% unless site.issue %}
@each $br in $cur {
@media (prefers-color-scheme: dark) {
@at-root html:not(.forced-light) #{$br} {
@content
}
}
@at-root html.forced-dark #{$br} {
@content
}
}
{% else %}
@content
{% endunless %}
@ -97,7 +105,7 @@ html {
body {
position: relative;
min-height: 100%;
@include dark{
@include dark(&) {
background: $color_bg_dark;
}
}
@ -122,18 +130,18 @@ body {
.status-fail, .status-failed, .status-paused {
background-color: #fff4e3;
@include dark{
@include dark(&) {
background-color: #524841;
}
}
.status-syncing {
background-color: #e3fffd;
@include dark{
@include dark(&) {
background-color: #254059;
}
}
.sk-wave .sk-rect {
@include dark{
@include dark(&) {
background-color: $color_secondary_dark;
}
}
@ -142,7 +150,7 @@ body {
tbody {
tr:hover {
background-color: $color_row_hover;
@include dark{
@include dark(&) {
background-color: $color_row_hover_dark;
}
}
@ -164,7 +172,7 @@ body {
}
.question-circle {
color: #234961;
@include dark{
@include dark(&) {
color: #6e9cdb;
}
}
@ -172,7 +180,7 @@ body {
#news {
li > a {
color: $color_secondary;
@include dark{
@include dark(&) {
color: $color_secondary_dark;
}
}
@ -194,7 +202,7 @@ body {
#mirror-list {
tr:hover {
background-color: $color_row_hover;
@include dark{
@include dark(&) {
background-color: $color_row_hover_dark;
}
}
@ -210,7 +218,7 @@ body {
article {
.meta {
color: $color_grey;
@include dark{
@include dark(&) {
color: $color_grey_dark;
}
}
@ -402,7 +410,7 @@ body {
url("/static/img/missing@3x.png") 3x,
url("/static/img/missing@4x.png") 4x);
}
@include dark {
@include dark(&) {
opacity: 1;
filter: none;
background-image: url(/static/img/missing-dark.png);

View File

View File

@ -36,10 +36,15 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
};
console.log("喵呜喵呜喵");
var modes = ["light", "dark", "darker", "lighter"];
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
modes[0] = 'dark';
modes[1] = 'light';
var toggle = document.getElementsByClassName('dark-switch')[0];
toggle.setAttribute('data-mode', 'dark');
}
var tmpCtx = document.createElement('canvas').getContext('2d');
var vertShaderSrc = "\nattribute vec4 a_pos;\nuniform vec2 u_screen;\nuniform vec2 u_mouse;\nuniform vec2 u_offset;\n\nvarying float v_opacity;\n\nvoid main() {\n vec2 pos_screen = a_pos.xy + u_offset;\n vec2 pos_translated;\n vec2 diff = pos_screen - u_mouse;\n pos_translated.x = pos_screen.x + diff.x * a_pos.z;\n pos_translated.y = pos_screen.y + diff.y * a_pos.z;\n gl_Position.x = pos_translated.x / u_screen.x * 2.0 - 1.0;\n gl_Position.y = - (pos_translated.y / u_screen.y * 2.0 - 1.0);\n gl_Position.z = 0.0;\n gl_Position.w = 1.0;\n\n float dist = sqrt(diff.x * diff.x + diff.y * diff.y);\n // 30 - 50px\n v_opacity = clamp((dist - 30.0) / 20.0, 0.0, 1.0);\n}\n";
var fragShaderSrc = "\nprecision mediump float;\nvarying float v_opacity;\n\nvoid main() {\n gl_FragColor = vec4(0.0, 0.0, 0.0, v_opacity);\n}\n";
var radialVertShaderSrc = "\n\n";
var radialFragShaderSrc = "\n";
function loadFont(fn) {
return __awaiter(this, void 0, void 0, function () {
var url, req, resp;
@ -157,14 +162,53 @@ function discretize(path) {
return loop;
}
function applyMode(m) {
if (m === 'darker') {
ensureCanvas();
rescanAt(document.body);
// TODO: async reassemble
reassemble();
ensureObs();
renderLoop();
}
return __awaiter(this, void 0, void 0, function () {
var tmpl_1, flames_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
document.body.parentElement.classList.remove('forced-light');
document.body.parentElement.classList.remove('forced-dark');
if (!(m === 'light' || m === 'dark')) return [3 /*break*/, 1];
document.body.parentElement.classList.add("forced-".concat(m));
return [3 /*break*/, 4];
case 1:
if (!(m === 'darker')) return [3 /*break*/, 3];
document.body.parentElement.classList.add('forced-light');
document.body.classList.add('darker-engaging');
return [4 /*yield*/, allFonts];
case 2:
_a.sent();
setTimeout(function () {
ensureCanvas();
rescanAt(document.body);
// TODO: async reassemble
reassemble();
ensureObs();
renderLoop();
document.body.classList.remove('darker-engaging');
}, 100);
return [3 /*break*/, 4];
case 3:
tmpl_1 = document.querySelector('.dark-switch-icon[data-active="lighter"] svg').cloneNode(true);
flames_1 = document.querySelector('.flames');
renderStopped = true;
document.body.classList.add('darker-cleanup');
setTimeout(function () {
document.body.addEventListener('click', function (e) {
e.target.remove();
e.preventDefault();
var inserted = tmpl_1.cloneNode(true);
inserted.style.top = e.pageY + 'px';
inserted.style.left = e.pageX + 'px';
flames_1.appendChild(inserted);
});
});
_a.label = 4;
case 4: return [2 /*return*/];
}
});
});
}
var mx = 0;
var my = 0;
@ -186,20 +230,24 @@ function rescan(mutations, obs) {
var n = _b[_a];
rescanAt(n);
}
reassemble();
}
setTimeout(function () { return reassemble(); });
}
// TODO: allow scaning arbitrary HTML-side nodes
function rescanAt(el) {
var _a;
var _a, _b, _c, _d, _e, _f;
if ((_a = el.classList) === null || _a === void 0 ? void 0 : _a.contains('sr-only'))
return;
if ((_b = el.classList) === null || _b === void 0 ? void 0 : _b.contains('dark-switch-hint'))
return;
// Check if is svg
if (el.tagName === 'svg') {
rescanSVG(el);
return;
}
if ((_a = el.classList) === null || _a === void 0 ? void 0 : _a.contains('label-status')) {
if (((_c = el.classList) === null || _c === void 0 ? void 0 : _c.contains('label-status')) || ((_d = el.classList) === null || _d === void 0 ? void 0 : _d.contains('label-new')) || ((_e = el.classList) === null || _e === void 0 ? void 0 : _e.contains('input-wrapper')) || ((_f = el.classList) === null || _f === void 0 ? void 0 : _f.contains('popover'))) {
var r = parseFloat(window.getComputedStyle(el).borderRadius.match(/^[0-9.]+/)[0]);
var _b = el.getBoundingClientRect(), width = _b.width, height = _b.height;
var _g = el.getBoundingClientRect(), width = _g.width, height = _g.height;
var d = "\n M 0 ".concat(r, " A ").concat(r, " ").concat(r, " 0 0 1 ").concat(r, " 0\n L ").concat(width - r, " 0 A ").concat(r, " ").concat(r, " 0 0 1 ").concat(width, " ").concat(r, "\n L ").concat(width, " ").concat(height - r, " A ").concat(r, " ").concat(r, " 0 0 1 ").concat(width - r, " ").concat(height, "\n L ").concat(r, " ").concat(height, " A ").concat(r, " ").concat(r, " 0 0 1 0 ").concat(height - r, "\n z\n ");
var svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
svg.setAttribute('viewbox', "0 0 ".concat(width, " ").concat(height));
@ -213,10 +261,11 @@ function rescanAt(el) {
svg.appendChild(path);
el.appendChild(svg);
svg.classList.add('darker-traced');
svg.classList.add('darker-traced-misc');
rescanAt(svg);
}
for (var _i = 0, _c = el.childNodes; _i < _c.length; _i++) {
var child = _c[_i];
for (var _i = 0, _h = el.childNodes; _i < _h.length; _i++) {
var child = _h[_i];
if (child.nodeType === Node.TEXT_NODE) {
var inner = child.nodeValue;
if (!inner)
@ -307,22 +356,28 @@ function splitPathSegs(path) {
var seg = segs_1[_i];
var _a = seg.match(/^[mM] *(-?[.0-9]+) *(-?[.0-9]+)/), firstMove = _a[0], mx_1 = _a[1], my_1 = _a[2];
// console.log(firstMove);
var d_1 = firstMove[0] === 'M' ? seg : "M ".concat(last.x + parseFloat(mx_1), " ").concat(last.y + parseFloat(my_1), " ").concat(seg.substring(firstMove.length));
var bx = firstMove[0] === 'M' ? parseFloat(mx_1) : last.x + parseFloat(mx_1);
var by = firstMove[0] === 'M' ? parseFloat(my_1) : last.y + parseFloat(my_1);
var d_1 = "M ".concat(bx, " ").concat(by, " ").concat(seg.substring(firstMove.length).trim());
var path_1 = document.createElementNS("http://www.w3.org/2000/svg", 'path');
path_1.setAttribute('d', d_1);
path_1.classList.add('darker-processed');
path_1.classList.add('darker-surrogate');
last = path_1.getPointAtLength(path_1.getTotalLength());
paths.push([path_1, new Path2D(d_1)]);
if (d_1[d_1.length - 1] === 'z' || d_1[d_1.length - 1] === 'Z') {
last = { x: bx, y: by };
}
else {
console.error("Cannot find path end: ".concat(d_1));
last = { x: bx, y: by };
}
paths.push([path_1, { x: bx, y: by }, new Path2D(d_1)]);
}
var tmpCtx = document.createElement('canvas').getContext('2d');
var outerPaths = [];
for (var _b = 0, paths_1 = paths; _b < paths_1.length; _b++) {
var _c = paths_1[_b], path_2 = _c[0], _ = _c[1];
var starting = path_2.getPointAtLength(0);
var _c = paths_1[_b], path_2 = _c[0], starting = _c[1], _ = _c[2];
var outer = true;
for (var _d = 0, paths_2 = paths; _d < paths_2.length; _d++) {
var _e = paths_2[_d], another = _e[0], repr = _e[1];
var _e = paths_2[_d], another = _e[0], _1 = _e[1], repr = _e[2];
if (another !== path_2) {
if (tmpCtx.isPointInPath(repr, starting.x, starting.y)) {
outer = false;
@ -448,11 +503,11 @@ function ensureObs() {
}
}
var textCache = new WeakMap();
var assembleCache = new WeakMap();
function reassemble() {
var traced = document.getElementsByClassName('darker-traced');
var assembledBuffer = [];
var trig = 0;
function assemblePath(paths, sx, sy, scale) {
var buf = [];
for (var _i = 0, paths_3 = paths; _i < paths_3.length; _i++) {
var path = paths_3[_i];
var dpath = discretize(path);
@ -464,21 +519,27 @@ function reassemble() {
var nx = dpath[(i + 1) % dpath.length].x * scale + sx;
var ny = dpath[(i + 1) % dpath.length].y * scale + sy;
// Expand a little bit
assembledBuffer.push(cx, cy, 0, nx, ny, 0, cx, cy, 100, cx, cy, 100, nx, ny, 100, nx, ny, 0);
trig += 2;
buf.push(cx, cy, -0.01, nx, ny, -0.01, cx, cy, 100, cx, cy, 100, nx, ny, 100, nx, ny, -0.01);
}
}
return buf;
}
var total = [];
for (var _i = 0, traced_1 = traced; _i < traced_1.length; _i++) {
var trace = traced_1[_i];
if (assembleCache.has(trace)) {
var cached = assembleCache.get(trace);
total.push.apply(total, cached);
continue;
}
var _a = trace.getBoundingClientRect(), x = _a.x, y = _a.y, width = _a.width, height = _a.height;
var scale = 1;
var populated = [];
if (trace.tagName === 'use') {
var sym = document.getElementById(trace.getAttribute('xlink:href').substring(1));
var vbox = sym.viewBox.baseVal;
// TODO: handle browsers without baseVal
// TODO: handle origins other than 0,0
var scale_1 = width / vbox.width;
var scale = width / vbox.width;
var vscale = height / vbox.height;
// if(scale > vscale * 1.01 || scale < vscale * 0.99)
// console.warn(`incompatible scales: ${scale}, ${vscale}`);
@ -487,15 +548,21 @@ function reassemble() {
console.warn("Symbol not in cache: ".concat(sym.id));
continue;
}
assemblePath(paths, x, y + window.scrollY, scale_1);
populated = assemblePath(paths, x, y + window.scrollY, scale);
}
else if (trace.tagName === 'svg') {
var scale = 1;
var vb = trace.getAttribute('viewBox');
if (vb) {
var _b = vb.split(' ').map(function (e) { return parseFloat(e); }), _ = _b[0], __ = _b[1], vboxw = _b[2], vboxh = _b[3];
scale = width / vboxw;
}
var paths = svgCache[trace.id];
if (paths === undefined) {
console.warn("SVG not in cache: ".concat(trace.id));
continue;
}
assemblePath(paths, x, y + window.scrollY, scale);
populated = assemblePath(paths, x, y + window.scrollY, scale);
}
else if (trace.classList.contains('darker-text')) {
var cached = textCache.get(trace);
@ -505,17 +572,21 @@ function reassemble() {
cached = paths.map(function (e) { return e.getAttribute('d'); });
textCache.set(trace, cached);
}
assemblePath(cached, x, y + window.scrollY, scale);
populated = assemblePath(cached, x, y + window.scrollY, 1);
}
assembleCache.set(trace, populated);
total.push.apply(total, populated);
}
// TODO: error on me
if (!shaderCtx.gl)
return;
shaderCtx.gl.bufferData(shaderCtx.gl.ARRAY_BUFFER, new Float32Array(assembledBuffer), shaderCtx.gl.STATIC_DRAW);
shaderCtx.triangleCnt = trig * 3;
shaderCtx.gl.bufferData(shaderCtx.gl.ARRAY_BUFFER, new Float32Array(total), shaderCtx.gl.STATIC_DRAW);
shaderCtx.triangleCnt = total.length / 3;
}
var renderStopped = false;
function renderLoop() {
if (renderStopped)
return;
if (!canvas || !backdrop || !overlay || !shaderCtx.gl)
return;
canvas.width = window.innerWidth;
@ -525,7 +596,7 @@ function renderLoop() {
backdrop.height = window.innerHeight;
var ctx = backdrop.getContext('2d');
var grad = ctx.createRadialGradient(mx, my, 100, mx, my, 600);
grad.addColorStop(0, "#333");
grad.addColorStop(0, "#222");
grad.addColorStop(1, "#111");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, backdrop.width, backdrop.height);
@ -557,13 +628,26 @@ function renderLoop() {
}
document.addEventListener('DOMContentLoaded', function () {
var dark = document.getElementsByClassName('dark-switch-inner')[0];
var hint = document.querySelector('.dark-switch-hint');
dark.addEventListener('click', function () {
hint === null || hint === void 0 ? void 0 : hint.classList.add('dark-switch-hint-ack');
window.localStorage.setItem('2024-april-fools', 'meow');
var parent = dark.parentNode;
var cur = parent.getAttribute("data-mode");
if (cur === 'lighter')
return;
var idx = (modes.findIndex(function (e) { return e === cur; }) + 1) % modes.length;
var next = modes[idx];
applyMode(next);
parent.setAttribute('data-mode', next);
});
if (hint) {
hint.addEventListener('click', function (e) {
hint.classList.add('dark-switch-hint-ack');
window.localStorage.setItem('2024-april-fools', 'meow');
});
if (window.localStorage.getItem('2024-april-fools') === null)
hint.classList.remove('dark-switch-hint-ack');
}
document.addEventListener('mousemove', tracker);
});

View File

@ -1,9 +1,16 @@
console.log("喵呜喵呜喵");
const modes = ["light", "dark", "darker", "lighter"] as const;
type Mode = typeof modes[number];
const modes = ["light", "dark", "darker", "lighter"];
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
modes[0] = 'dark';
modes[1] = 'light';
const toggle = document.getElementsByClassName('dark-switch')[0];
toggle.setAttribute('data-mode', 'dark');
}
type DiscreteLoop = { x: number, y: number }[];
const tmpCtx = document.createElement('canvas').getContext('2d')!;
const vertShaderSrc = `
attribute vec4 a_pos;
@ -39,13 +46,6 @@ void main() {
}
`;
const radialVertShaderSrc = `
`
const radialFragShaderSrc = `
`
async function loadFont(fn: string): Promise<any> {
const url = `/static/fonts/${fn}`;
const req = await fetch(url);
@ -147,14 +147,41 @@ function discretize(path: string): DiscreteLoop {
return loop;
}
function applyMode(m: Mode) {
if(m === 'darker') {
ensureCanvas();
rescanAt(document.body);
// TODO: async reassemble
reassemble();
ensureObs();
renderLoop();
async function applyMode(m: string) {
(document.body.parentElement as HTMLElement).classList.remove('forced-light');
(document.body.parentElement as HTMLElement).classList.remove('forced-dark');
if(m === 'light' || m === 'dark') {
(document.body.parentElement as HTMLElement).classList.add(`forced-${m}`);
} else if(m === 'darker') {
(document.body.parentElement as HTMLElement).classList.add('forced-light');
document.body.classList.add('darker-engaging');
await allFonts;
setTimeout(() => {
ensureCanvas();
rescanAt(document.body);
// TODO: async reassemble
reassemble();
ensureObs();
renderLoop();
document.body.classList.remove('darker-engaging');
}, 100);
} else {
const tmpl = document.querySelector('.dark-switch-icon[data-active="lighter"] svg')!.cloneNode(true);
const flames = document.querySelector('.flames')!;
renderStopped = true;
document.body.classList.add('darker-cleanup');
setTimeout(() => {
document.body.addEventListener('click', e => {
(e.target as HTMLElement).remove();
e.preventDefault();
const inserted = tmpl.cloneNode(true) as HTMLElement;
inserted.style.top = e.pageY + 'px';
inserted.style.left = e.pageX + 'px';
flames.appendChild(inserted);
})
});
}
}
@ -175,19 +202,22 @@ function rescan(mutations: MutationRecord[], obs: MutationObserver) {
}
for(const n of m.addedNodes) rescanAt(n as HTMLElement);
reassemble();
}
setTimeout(() => reassemble());
}
// TODO: allow scaning arbitrary HTML-side nodes
function rescanAt(el: HTMLElement) {
if(el.classList?.contains('sr-only')) return;
if(el.classList?.contains('dark-switch-hint')) return;
// Check if is svg
if(el.tagName === 'svg') {
rescanSVG(el as unknown as SVGElement);
return;
}
if(el.classList?.contains('label-status')) {
if(el.classList?.contains('label-status') || el.classList?.contains('label-new') || el.classList?.contains('input-wrapper') || el.classList?.contains('popover')) {
const r = parseFloat(window.getComputedStyle(el).borderRadius.match(/^[0-9.]+/)![0]);
const { width, height } = el.getBoundingClientRect();
const d = `
@ -213,6 +243,7 @@ function rescanAt(el: HTMLElement) {
el.appendChild(svg);
svg.classList.add('darker-traced');
svg.classList.add('darker-traced-misc');
rescanAt(svg as unknown as HTMLElement);
}
@ -316,26 +347,31 @@ function splitPathSegs(path: string): SVGPathElement[] {
}
let last = { x: 0, y: 0 };
const paths: [SVGPathElement, Path2D][] = [];
const paths: [SVGPathElement, { x: number, y: number }, Path2D][] = [];
for(const seg of segs) {
const [firstMove, mx, my] = seg.match(/^[mM] *(-?[.0-9]+) *(-?[.0-9]+)/)!;
// console.log(firstMove);
const d = firstMove[0] === 'M' ? seg : `M ${last.x + parseFloat(mx)} ${last.y + parseFloat(my)} ${seg.substring(firstMove.length)}`;
const bx = firstMove[0] === 'M' ? parseFloat(mx) : last.x + parseFloat(mx);
const by = firstMove[0] === 'M' ? parseFloat(my) : last.y + parseFloat(my);
const d = `M ${bx} ${by} ${seg.substring(firstMove.length).trim()}`;
const path = document.createElementNS("http://www.w3.org/2000/svg", 'path');
path.setAttribute('d', d);
path.classList.add('darker-processed');
path.classList.add('darker-surrogate');
last = path.getPointAtLength(path.getTotalLength());
paths.push([path, new Path2D(d)]);
if(d[d.length-1] === 'z' || d[d.length-1] === 'Z') {
last = { x: bx, y: by };
} else {
console.error(`Cannot find path end: ${d}`);
last = { x: bx, y: by };
}
paths.push([path, { x: bx, y: by }, new Path2D(d)]);
}
const tmpCtx = document.createElement('canvas').getContext('2d')!;
const outerPaths: SVGPathElement[] = [];
for(const [path, _] of paths) {
const starting = path.getPointAtLength(0);
for(const [path, starting, _] of paths) {
let outer = true;
for(const [another, repr] of paths) if(another !== path) {
for(const [another, _, repr] of paths) if(another !== path) {
if(tmpCtx.isPointInPath(repr, starting.x, starting.y)) {
outer = false;
break;
@ -466,13 +502,13 @@ function ensureObs() {
}
const textCache: WeakMap<Element, string[]> = new WeakMap();
const assembleCache: WeakMap<Element, number[]> = new WeakMap();
function reassemble() {
const traced = document.getElementsByClassName('darker-traced');
const assembledBuffer: number[] = [];
let trig = 0;
function assemblePath(paths: string[], sx: number, sy: number, scale: number) {
function assemblePath(paths: string[], sx: number, sy: number, scale: number): number[] {
const buf: number[] = [];
for(const path of paths) {
const dpath = discretize(path);
if(dpath.length === 1) continue;
@ -485,24 +521,31 @@ function reassemble() {
let ny = dpath[(i + 1) % dpath.length].y * scale + sy;
// Expand a little bit
assembledBuffer.push(
cx, cy, 0,
nx, ny, 0,
buf.push(
cx, cy, -0.01,
nx, ny, -0.01,
cx, cy, 100,
cx, cy, 100,
nx, ny, 100,
nx, ny, 0,
nx, ny, -0.01,
);
trig += 2;
}
}
return buf;
}
const total: number[] = [];
for(const trace of traced) {
if(assembleCache.has(trace)) {
const cached = assembleCache.get(trace)!;
total.push(...cached);
continue;
}
const { x, y, width, height } = trace.getBoundingClientRect();
let scale = 1;
let populated: number[] = [];
if(trace.tagName === 'use') {
const sym = document.getElementById(trace.getAttribute('xlink:href')!.substring(1)) as unknown as SVGSymbolElement;
@ -520,14 +563,20 @@ function reassemble() {
continue;
}
assemblePath(paths, x, y + window.scrollY, scale);
populated = assemblePath(paths, x, y + window.scrollY, scale);
} else if(trace.tagName === 'svg') {
let scale = 1;
const vb = trace.getAttribute('viewBox');
if(vb) {
const [_, __, vboxw, vboxh] = vb.split(' ').map(e => parseFloat(e))!;
scale = width / vboxw;
}
const paths: string[] | undefined = svgCache[trace.id];
if(paths === undefined) {
console.warn(`SVG not in cache: ${trace.id}`);
continue;
}
assemblePath(paths, x, y + window.scrollY, scale);
populated = assemblePath(paths, x, y + window.scrollY, scale);
} else if(trace.classList.contains('darker-text')) {
let cached = textCache.get(trace);
if(!cached) {
@ -536,19 +585,23 @@ function reassemble() {
cached = paths.map(e => e.getAttribute('d')!);
textCache.set(trace, cached);
}
assemblePath(cached, x, y + window.scrollY, scale);
populated = assemblePath(cached, x, y + window.scrollY, 1);
}
assembleCache.set(trace, populated);
total.push(...populated);
}
// TODO: error on me
if(!shaderCtx.gl) return;
shaderCtx.gl.bufferData(shaderCtx.gl.ARRAY_BUFFER, new Float32Array(assembledBuffer), shaderCtx.gl.STATIC_DRAW);
shaderCtx.triangleCnt = trig * 3;
shaderCtx.gl.bufferData(shaderCtx.gl.ARRAY_BUFFER, new Float32Array(total), shaderCtx.gl.STATIC_DRAW);
shaderCtx.triangleCnt = total.length / 3;
}
let renderStopped = false;
function renderLoop() {
if(renderStopped) return;
if(!canvas || !backdrop || !overlay || !shaderCtx.gl) return;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
@ -558,7 +611,7 @@ function renderLoop() {
backdrop.height = window.innerHeight;
const ctx = backdrop.getContext('2d')!;
const grad = ctx.createRadialGradient(mx, my, 100, mx, my, 600);
grad.addColorStop(0, "#333");
grad.addColorStop(0, "#222");
grad.addColorStop(1, "#111");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, backdrop.width, backdrop.height);
@ -597,14 +650,30 @@ function renderLoop() {
document.addEventListener('DOMContentLoaded', () => {
const dark = document.getElementsByClassName('dark-switch-inner')[0];
const hint = document.querySelector('.dark-switch-hint');
dark.addEventListener('click', () => {
hint?.classList.add('dark-switch-hint-ack');
window.localStorage.setItem('2024-april-fools', 'meow');
const parent = dark.parentNode as HTMLElement;
const cur = parent.getAttribute("data-mode");
if(cur === 'lighter') return;
const idx = (modes.findIndex(e => e === cur) + 1) % modes.length;
const next = modes[idx];
applyMode(next);
parent.setAttribute('data-mode', next);
})
if(hint) {
hint.addEventListener('click', e => {
hint.classList.add('dark-switch-hint-ack');
window.localStorage.setItem('2024-april-fools', 'meow');
})
if(window.localStorage.getItem('2024-april-fools') === null)
hint.classList.remove('dark-switch-hint-ack');
}
document.addEventListener('mousemove', tracker);
})

View File

@ -64,10 +64,13 @@ const vmMirList = new Vue({
}
return result;
},
filteredMirrorList: function() {
mappedMirrorList: function() {
var filter = this.filter.toLowerCase();
return this.mirrorList.filter((m) => {
return m.is_master && m.name.toLowerCase().indexOf(filter) !== -1;
return this.mirrorList.map((m) => {
return {
...m,
shown: m.is_master && m.name.toLowerCase().indexOf(filter) !== -1,
};
});
},
},

View File

@ -1,13 +0,0 @@
attribute vec3 a_pos;
uniform vec2 u_screen;
uniform vec2 u_mouse;
uniform vec2 u_offset;
void main() {
vec2 pos_screen = a_pos.xy - u_offset;
vec2 pos_translated;
pos_translated.x = pos_screen.x + (pos_screen.x - u_mouse.x) * a_pos.z * 1000;
pos_translated.y = pos_screen.y + (pos_screen.y - u_mouse.y) * a_pos.z * 1000;
gl_Position.x = pos_translated.x / u_screen * 2 - 1;
gl_Position.y = pos_translated.y / u_screen * 2 - 1;
}