Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 169 additions & 5 deletions css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -1570,9 +1570,20 @@ section {
}
}

@media (max-width: 560px) {
.logo span {
display: none;
}
}

@media (max-width: 720px) {
#nav {
padding-inline: 18px;
padding-inline: 14px;
gap: 10px;
}

.nav-actions {
gap: 8px;
}

#hero {
Expand Down Expand Up @@ -1726,14 +1737,16 @@ section {
display: inline-flex;
align-items: center;
gap: 12px;
flex: none;
}

.gh-link {
display: inline-flex;
align-items: center;
justify-content: center;
align-self: stretch; /* match the language pill's height exactly */
aspect-ratio: 1 / 1; /* keep a perfect circle at that height */
flex: none;
width: 40px;
height: 40px;
border-radius: 999px;
color: var(--ink, #1e1914);
border: 1px solid rgba(30, 25, 20, 0.16);
Expand All @@ -1755,6 +1768,157 @@ section {
display: block;
}

.qq-group {
position: relative;
display: inline-flex;
align-self: stretch;
}

.qq-group-btn {
display: inline-flex;
align-items: center;
justify-content: center;
flex: none;
height: 40px;
min-width: 44px;
padding: 0 12px;
white-space: nowrap;
border-radius: 999px;
border: 1px solid rgba(30, 25, 20, 0.16);
background-color: rgba(255, 251, 246, 0.72);
color: var(--ink, #1e1914);
font-size: 0.86rem;
font-weight: 650;
letter-spacing: 0.02em;
transition: border-color 0.18s ease, background-color 0.18s ease, color 0.18s ease;
}

.qq-group-btn:hover,
.qq-group-btn[aria-expanded="true"] {
border-color: rgba(30, 25, 20, 0.32);
color: var(--accent, #c97953);
}

.qq-group-btn:focus-visible {
outline: 2px solid var(--accent, #c97953);
outline-offset: 2px;
}

.qq-group-panel {
position: absolute;
top: calc(100% + 10px);
inset-inline-end: 0;
z-index: 30;
width: min(300px, calc(100vw - 28px));
padding: 16px 16px 14px;
border-radius: 14px;
border: 1px solid rgba(30, 25, 20, 0.1);
background: var(--paper-strong, #fbf8f3);
box-shadow: 0 16px 40px rgba(30, 25, 20, 0.16);
}

.qq-group-close {
position: absolute;
top: 6px;
inset-inline-end: 8px;
width: 32px;
height: 32px;
font-size: 1.3rem;
line-height: 1;
color: rgba(30, 25, 20, 0.5);
}

.qq-group-close:hover {
color: var(--ink, #1e1914);
}

.qq-group-title {
margin: 0;
margin-inline-end: 28px;
font-weight: 700;
font-size: 1rem;
line-height: 1.35;
}

.qq-group-lead {
margin: 8px 0 0;
color: var(--ink-soft, rgba(30, 25, 20, 0.72));
font-size: 0.88rem;
line-height: 1.45;
}

.qq-group-number-row {
display: flex;
align-items: baseline;
gap: 8px;
margin: 12px 0 0;
color: var(--ink-soft, rgba(30, 25, 20, 0.72));
font-size: 0.88rem;
}

.qq-group-number-row strong {
color: var(--ink, #1e1914);
font-size: 1.15rem;
font-weight: 700;
letter-spacing: 0.04em;
font-variant-numeric: tabular-nums;
user-select: all;
}

.qq-group-actions {
display: flex;
gap: 8px;
margin-top: 14px;
}

.qq-group-join,
.qq-group-copy {
flex: 1;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 40px;
padding: 8px 10px;
border-radius: 10px;
font-size: 0.88rem;
font-weight: 650;
text-align: center;
}

.qq-group-join {
background: var(--ink, #1e1914);
color: #fffaf4;
}

.qq-group-join:hover {
background: #2e2620;
}

.qq-group-copy {
border: 1px solid rgba(30, 25, 20, 0.16);
background: rgba(255, 251, 246, 0.8);
}

.qq-group-copy:hover {
border-color: rgba(30, 25, 20, 0.32);
}

.qq-group-status {
margin: 8px 0 0;
min-height: 0;
font-size: 0.82rem;
line-height: 1.4;
color: var(--ink-soft, rgba(30, 25, 20, 0.72));
}

.qq-group-status:empty {
display: none;
}

.qq-group-status.is-failed {
color: #8a4b2d;
}

#lang-select {
appearance: none;
-webkit-appearance: none;
Expand Down Expand Up @@ -2340,8 +2504,8 @@ section {
display: inline-flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
width: 40px;
height: 40px;
flex: none;
border-radius: 999px;
color: var(--ink, #1e1914);
Expand Down
31 changes: 26 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Noto+Serif+SC:wght@400;500;600;700;900&family=Spectral:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css?v=20260914-tagfix1">
<script src="js/i18n.js?v=20260914-tagfix1"></script>
<script src="js/i18n.strings.js?v=20260914-tagfix1"></script>
<link rel="stylesheet" href="css/style.css?v=20261008-qq2">
<script src="js/i18n.js?v=20261008-qq2"></script>
<script src="js/i18n.strings.js?v=20261008-qq2"></script>
</head>
<body>
<header id="nav">
Expand All @@ -39,6 +39,27 @@
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>
</a>
<div class="qq-group">
<button id="qq-group-btn" class="qq-group-btn" type="button"
aria-haspopup="dialog" aria-expanded="false" aria-controls="qq-group-panel"
data-i18n-aria-label="nav.qq" data-i18n-title="nav.qq" aria-label="QQ group" title="QQ group">
<span data-i18n="nav.qqShort">Q群</span>
</button>
<div id="qq-group-panel" class="qq-group-panel hidden" role="dialog" aria-labelledby="qq-group-title">
<button id="qq-group-close" class="qq-group-close" type="button" data-i18n-aria-label="nav.qqClose" aria-label="Close">×</button>
<p id="qq-group-title" class="qq-group-title">WebToApp 网页版</p>
<p class="qq-group-lead" data-i18n="nav.qqLead">点击链接加入群聊【WebToApp 网页版】</p>
<p class="qq-group-number-row">
<span data-i18n="nav.qqNumber">群号</span>
<strong id="qq-group-number">947508787</strong>
</p>
<div class="qq-group-actions">
<a class="qq-group-join" href="https://qm.qq.com/q/F9BHHJNvtm" target="_blank" rel="noopener noreferrer" data-i18n="nav.qqJoin">加入群聊</a>
<button id="qq-group-copy" class="qq-group-copy" type="button" data-i18n="nav.qqCopy">复制群号</button>
</div>
<p id="qq-group-status" class="qq-group-status" role="status"></p>
</div>
</div>
</div>
</header>

Expand Down Expand Up @@ -540,7 +561,7 @@ <h2 data-i18n="market.title">Public app market</h2>
</div>
</div>

<script src="js/mdmini.js?v=20260914-tagfix1"></script>
<script src="js/app.v5.js?v=20260914-tagfix1"></script>
<script src="js/mdmini.js?v=20261008-qq2"></script>
<script src="js/app.v5.js?v=20261008-qq2"></script>
</body>
</html>
112 changes: 112 additions & 0 deletions js/app.v5.js
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,117 @@
}
}

function copyPlainText(text) {
const legacy = () => new Promise((resolve, reject) => {
const area = document.createElement('textarea');
area.value = text;
area.setAttribute('readonly', '');
area.style.position = 'fixed';
area.style.insetInlineStart = '-9999px';
document.body.appendChild(area);
area.select();
try {
const ok = document.execCommand('copy');
area.remove();
if (ok) resolve();
else reject(new Error('copy failed'));
} catch (err) {
area.remove();
reject(err);
}
});
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text).catch(() => legacy());
}
return legacy();
}

function initQqGroup() {
const btn = document.getElementById('qq-group-btn');
const panel = document.getElementById('qq-group-panel');
const closeBtn = document.getElementById('qq-group-close');
const copyBtn = document.getElementById('qq-group-copy');
const numberEl = document.getElementById('qq-group-number');
const statusEl = document.getElementById('qq-group-status');
if (!btn || !panel || !closeBtn || !copyBtn || !numberEl || !statusEl) return;

let statusTimer = 0;
const isOpen = () => !panel.classList.contains('hidden');

function clearStatus() {
window.clearTimeout(statusTimer);
statusTimer = 0;
statusEl.textContent = '';
statusEl.classList.remove('is-failed');
}

function showStatus(message, failed) {
window.clearTimeout(statusTimer);
statusEl.textContent = message;
statusEl.classList.toggle('is-failed', !!failed);
statusTimer = window.setTimeout(clearStatus, 2000);
}

function openPanel() {
panel.classList.remove('hidden');
btn.setAttribute('aria-expanded', 'true');
const join = panel.querySelector('.qq-group-join');
if (join) join.focus();
}

function closePanel(restoreFocus) {
if (!isOpen()) return;
panel.classList.add('hidden');
btn.setAttribute('aria-expanded', 'false');
clearStatus();
if (restoreFocus) btn.focus();
}

btn.addEventListener('click', (event) => {
event.stopPropagation();
if (isOpen()) closePanel(false);
else openPanel();
});

closeBtn.addEventListener('click', () => closePanel(true));

document.addEventListener('click', (event) => {
if (!isOpen()) return;
if (panel.contains(event.target) || btn.contains(event.target)) return;
closePanel(false);
});

document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && isOpen()) {
event.preventDefault();
closePanel(true);
}
});

panel.addEventListener('focusout', (event) => {
if (!isOpen()) return;
const next = event.relatedTarget;
if (!next || panel.contains(next) || next === btn) return;
closePanel(false);
});

copyBtn.addEventListener('click', () => {
const number = (numberEl.textContent || '').trim();
copyPlainText(number).then(() => {
showStatus(t('nav.qqCopied'), false);
}).catch(() => {
showStatus(t('nav.qqCopyFailed'), true);
});
});

window.addEventListener('i18n:changed', () => {
if (!statusEl.textContent) return;
statusEl.textContent = statusEl.classList.contains('is-failed')
? t('nav.qqCopyFailed')
: t('nav.qqCopied');
});
}

function initProfile() {
// Paint the cached avatar instantly, then refresh in the background —
// otherwise the nav button shows the generic icon on every page load
Expand All @@ -907,6 +1018,7 @@
paintNavAvatar(d.profile);
}
}).catch(() => {});
initQqGroup();
profileBtn.addEventListener('click', () => openProfile(null));
profileClose.addEventListener('click', () => closeProfileModal());
profileModal.addEventListener('click', (e) => {
Expand Down
Loading
Loading