sync: UI animations, select styling, TLS verify flag via proxy second line, brand spacing
This commit is contained in:
@@ -1085,4 +1085,86 @@ header .actions {
|
|||||||
@media (max-width: 860px) {
|
@media (max-width: 860px) {
|
||||||
header .brand { margin-right: 10px; }
|
header .brand { margin-right: 10px; }
|
||||||
header .actions { margin-left: 6px; }
|
header .actions { margin-left: 6px; }
|
||||||
|
}
|
||||||
|
/* Danmaku (bullet chat) over header */
|
||||||
|
header { position: relative; }
|
||||||
|
|
||||||
|
#danmaku-layer {
|
||||||
|
position: fixed; /* покрывает весь вьюпорт, не только шапку */
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none; /* не перехватывать клики по кнопкам */
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .18s ease-in-out;
|
||||||
|
z-index: 8000; /* ниже боковых панелей/дроверов (run-drawer ~10000), выше канваса */
|
||||||
|
}
|
||||||
|
#danmaku-layer.is-on { opacity: 1; }
|
||||||
|
|
||||||
|
/* Базовый стиль «пули» */
|
||||||
|
.danmaku-bullet {
|
||||||
|
position: absolute;
|
||||||
|
left: 100%; /* старт за правым краем */
|
||||||
|
top: 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #e5e7eb;
|
||||||
|
text-shadow: 0 1px 2px rgba(0,0,0,.6);
|
||||||
|
background: rgba(15, 20, 26, .35);
|
||||||
|
border: 1px solid rgba(96,165,250,.28);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
line-height: 1.15;
|
||||||
|
transform: translateZ(0); /* чуть сгладить анимацию */
|
||||||
|
animation-name: danmaku-move;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Вариации размера/выразительности */
|
||||||
|
.danmaku-bullet.sm { font: 11px/1.15 Inter, system-ui, Arial, sans-serif; opacity: .9; }
|
||||||
|
.danmaku-bullet.md { font: 12px/1.15 Inter, system-ui, Arial, sans-serif; }
|
||||||
|
.danmaku-bullet.lg { font: 13px/1.15 Inter, system-ui, Arial, sans-serif; box-shadow: 0 0 0 2px rgba(59,130,246,.12), 0 2px 6px rgba(0,0,0,.35); }
|
||||||
|
|
||||||
|
.danmaku-bullet.tint-blue { border-color: rgba(96,165,250,.35); color:#eaf2ff; }
|
||||||
|
.danmaku-bullet.tint-green { border-color: rgba(52,211,153,.35); color:#dcfce7; }
|
||||||
|
.danmaku-bullet.tint-pink { border-color: rgba(236,72,153,.35); color:#fde2f2; }
|
||||||
|
.danmaku-bullet.tint-amber { border-color: rgba(245,158,11,.35); color:#fff3d6; }
|
||||||
|
|
||||||
|
/* Движение слева направо (через всю ширину хедера + небольшой запас) */
|
||||||
|
@keyframes danmaku-move {
|
||||||
|
from { left: 100%; }
|
||||||
|
to { left: -40%; }
|
||||||
|
}
|
||||||
|
/* Rune highlight inside danmaku bullets */
|
||||||
|
.danmaku-bullet .rune {
|
||||||
|
color: var(--accent-2); /* фирменный синий */
|
||||||
|
font-size: 1.35em; /* немного крупнее текста пули */
|
||||||
|
letter-spacing: .06em;
|
||||||
|
text-shadow:
|
||||||
|
0 0 2px rgba(96,165,250,.95),
|
||||||
|
0 0 6px rgba(96,165,250,.75),
|
||||||
|
0 0 12px rgba(96,165,250,.45),
|
||||||
|
0 0 20px rgba(96,165,250,.30);
|
||||||
|
animation: rune-glow 1.8s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes rune-glow {
|
||||||
|
0% { text-shadow: 0 0 2px rgba(96,165,250,.85), 0 0 6px rgba(96,165,250,.60), 0 0 10px rgba(96,165,250,.35); }
|
||||||
|
50% { text-shadow: 0 0 3px rgba(96,165,250,1), 0 0 10px rgba(96,165,250,.85), 0 0 18px rgba(96,165,250,.55); }
|
||||||
|
100% { text-shadow: 0 0 2px rgba(96,165,250,.90), 0 0 8px rgba(96,165,250,.70), 0 0 14px rgba(96,165,250,.45); }
|
||||||
|
}
|
||||||
|
/* Sisters highlight inside danmaku bullets (pleasant pink + gentle glow) */
|
||||||
|
.danmaku-bullet .sisters {
|
||||||
|
color: #f9a8d4; /* soft pink */
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: .02em;
|
||||||
|
text-shadow:
|
||||||
|
0 0 2px rgba(249,168,212,.95),
|
||||||
|
0 0 6px rgba(249,168,212,.70),
|
||||||
|
0 0 12px rgba(249,168,212,.40),
|
||||||
|
0 0 18px rgba(249,168,212,.28);
|
||||||
|
animation: sisters-glow 1.8s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes sisters-glow {
|
||||||
|
0% { text-shadow: 0 0 2px rgba(249,168,212,.85), 0 0 6px rgba(249,168,212,.60), 0 0 10px rgba(249,168,212,.35); }
|
||||||
|
50% { text-shadow: 0 0 3px rgba(249,168,212,1), 0 0 10px rgba(249,168,212,.85), 0 0 18px rgba(249,168,212,.55); }
|
||||||
|
100% { text-shadow: 0 0 2px rgba(249,168,212,.90), 0 0 8px rgba(249,168,212,.70), 0 0 14px rgba(249,168,212,.45); }
|
||||||
}
|
}
|
||||||
@@ -81,6 +81,8 @@
|
|||||||
<button class="chip-btn" id="btn-logs" title="Журнал HTTP запросов/ответов">ЛОГИ</button>
|
<button class="chip-btn" id="btn-logs" title="Журнал HTTP запросов/ответов">ЛОГИ</button>
|
||||||
<a class="chip-btn" href="/" role="button">ДОМОЙ</a>
|
<a class="chip-btn" href="/" role="button">ДОМОЙ</a>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Danmaku overlay layer -->
|
||||||
|
<div id="danmaku-layer" aria-hidden="true"></div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Run Drawer -->
|
<!-- Run Drawer -->
|
||||||
@@ -2392,6 +2394,142 @@ function toggleLeft() {
|
|||||||
setTimeout(applyLOD, 0);
|
setTimeout(applyLOD, 0);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
try {
|
||||||
|
const brand = document.querySelector('header .brand');
|
||||||
|
const layer = document.getElementById('danmaku-layer');
|
||||||
|
if (!brand || !layer) return;
|
||||||
|
|
||||||
|
// Пулевая лента (弾幕) — набор фраз
|
||||||
|
// Поддерживает маркеры [rune]...[/rune] для явной подсветки рун.
|
||||||
|
const msgs = [
|
||||||
|
'Сделано гпт-5 с любовью для сестричек сисунь',
|
||||||
|
'Играй и побеждай',
|
||||||
|
'[rune]ᚨᛞᚨ[/rune] ✦ ваши чаты защищены оберегом НадТаверны от слопа',
|
||||||
|
'РП‑магия заряжена ⚡',
|
||||||
|
'Пиши, как дышишь — и мир откликнется',
|
||||||
|
'Сюжет идёт по плану… или нет?',
|
||||||
|
'NPC шепчет: «/roll d20»',
|
||||||
|
// от автора
|
||||||
|
'От Roo: берегите свои миры — а я присмотрю за багами 🛡️✨',
|
||||||
|
// новые странные фразы
|
||||||
|
'Бард шепчет кружке: «ты — артефакт +1 к вдохновению» 🍺✨',
|
||||||
|
'Где-то в подвале убежал printf, ищем следы по логам… 🐾',
|
||||||
|
'[rune]ᚠᚱᛁᛞᚨ[/rune] на кости выпала — к криту и печенькам 🍪',
|
||||||
|
'Сюжет идёт по плану… пока кубик не решит иначе 🎲',
|
||||||
|
'NPC недоволен: «кто украл мою функцию?» function() {…} 🤖',
|
||||||
|
'Только для сестричек сисунь — VIP‑проход в НадТаверну',
|
||||||
|
'РП‑магия на максимуме, щиты подняты, слова летят ⚡',
|
||||||
|
'Играй сердцем, кодь с умом, логи — наш оракул 📜',
|
||||||
|
'Пиши, как дышишь, пей, как гном — но не смешивай 🍻',
|
||||||
|
'[rune]ᚨᛚᚷᛟ[/rune] говорит: «tabs vs spaces?» ␣'
|
||||||
|
];
|
||||||
|
const tints = ['tint-blue','tint-green','tint-pink','tint-amber'];
|
||||||
|
const sizes = ['sm','md','lg'];
|
||||||
|
|
||||||
|
// Автоподсветка рун: U+16A0–U+16FF (Runic)
|
||||||
|
const RUNE_RE = /[\u16A0-\u16FF]+/g;
|
||||||
|
function escHtml(s) {
|
||||||
|
return String(s ?? '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
// Оборачиваем рунические символы в уже экранированной строке
|
||||||
|
function wrapRunesEscaped(escaped) {
|
||||||
|
return escaped.replace(RUNE_RE, (m) => '<span class="rune">' + m + '</span>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Розовый хайлайт для фразы «сестричек сисунь» + случайные «девчачьи» эмодзи
|
||||||
|
const EMOJI_GIRLY = ['💖','✨','🌸','💅','👑','🩷','🌷','🦋'];
|
||||||
|
function pickTwo(arr) {
|
||||||
|
const pool = arr.slice();
|
||||||
|
const a = pool.splice((Math.random()*pool.length)|0, 1)[0] || arr[0];
|
||||||
|
const b = pool.splice((Math.random()*pool.length)|0, 1)[0] || a;
|
||||||
|
return [a, b];
|
||||||
|
}
|
||||||
|
function markSisters(src) {
|
||||||
|
try {
|
||||||
|
return String(src || '').replace(/сестричек\s+сисунь/gi, m => `[sisters]${m}[/sisters]`);
|
||||||
|
} catch { return String(src || ''); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рендер безопасного HTML, поддержка маркеров [rune]...[/rune], [sisters]...[/sisters]
|
||||||
|
function renderBulletHTML(text) {
|
||||||
|
const raw = markSisters(String(text ?? ''));
|
||||||
|
const hasMarkers = /\[(rune|sisters)\]/i.test(raw);
|
||||||
|
if (hasMarkers) {
|
||||||
|
let out = '';
|
||||||
|
let last = 0;
|
||||||
|
const re = /\[(rune|sisters)\]([\s\S]+?)\[\/(rune|sisters)\]/gi;
|
||||||
|
let m;
|
||||||
|
while ((m = re.exec(raw)) !== null) {
|
||||||
|
// незамеченная часть — экранировать и автоподсветить руны
|
||||||
|
out += wrapRunesEscaped(escHtml(raw.slice(last, m.index)));
|
||||||
|
const open = (m[1] || '').toLowerCase();
|
||||||
|
const close = (m[3] || '').toLowerCase();
|
||||||
|
const inner = String(m[2] ?? '');
|
||||||
|
if (open === close) {
|
||||||
|
if (open === 'rune') {
|
||||||
|
out += '<span class="rune">' + escHtml(inner) + '</span>';
|
||||||
|
} else if (open === 'sisters') {
|
||||||
|
const [e1, e2] = pickTwo(EMOJI_GIRLY);
|
||||||
|
out += '<span class="sisters">' + escHtml(inner) + ' ' + e1 + ' ' + e2 + '</span>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// на всякий случай — экранируем весь блок, если теги не совпали
|
||||||
|
out += wrapRunesEscaped(escHtml(m[0]));
|
||||||
|
}
|
||||||
|
last = m.index + m[0].length;
|
||||||
|
}
|
||||||
|
out += wrapRunesEscaped(escHtml(raw.slice(last)));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
// Без маркеров — экранировать и автоподсветить руны
|
||||||
|
return wrapRunesEscaped(escHtml(raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
function spawn(text) {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.className = 'danmaku-bullet ' + sizes[(Math.random()*sizes.length)|0] + ' ' + tints[(Math.random()*tints.length)|0];
|
||||||
|
// Вставляем безопасный HTML с подсветкой рун и розовым акцентом фразы «сестричек сисунь»
|
||||||
|
el.innerHTML = renderBulletHTML(text);
|
||||||
|
const vpH = (layer && layer.clientHeight) || window.innerHeight || document.documentElement.clientHeight || 600;
|
||||||
|
const top = Math.random() * Math.max(40, vpH - 40);
|
||||||
|
el.style.top = Math.max(2, Math.min(vpH - 24, top)) + 'px';
|
||||||
|
const dur = 9 + Math.random() * 7; // 9–16s
|
||||||
|
el.style.animationDuration = dur + 's';
|
||||||
|
layer.appendChild(el);
|
||||||
|
el.addEventListener('animationend', () => { try { el.remove(); } catch(_){} });
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer = null;
|
||||||
|
let last = -1;
|
||||||
|
function start() {
|
||||||
|
if (timer) return;
|
||||||
|
layer.classList.add('is-on');
|
||||||
|
// Небольшой «всплеск» при старте
|
||||||
|
for (let i=0;i<5;i++){
|
||||||
|
setTimeout(()=>{ last = (last+1) % msgs.length; spawn(msgs[last]); }, i*160);
|
||||||
|
}
|
||||||
|
timer = setInterval(()=>{ spawn(msgs[Math.floor(Math.random()*msgs.length)]); }, 700);
|
||||||
|
}
|
||||||
|
function stop() {
|
||||||
|
if (timer) { clearInterval(timer); timer=null; }
|
||||||
|
setTimeout(()=>{
|
||||||
|
layer.classList.remove('is-on');
|
||||||
|
try { Array.from(layer.querySelectorAll('.danmaku-bullet')).forEach(n=>n.remove()); } catch(_){}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
brand.addEventListener('mouseenter', start);
|
||||||
|
brand.addEventListener('mouseleave', stop);
|
||||||
|
// Тач-устройства: короткое автозакрытие
|
||||||
|
brand.addEventListener('touchstart', ()=>{ start(); setTimeout(stop, 2800); }, {passive:true});
|
||||||
|
} catch(_) {}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<!-- SSE highlight script -->
|
<!-- SSE highlight script -->
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user