2
0
This commit is contained in:
2026-04-03 13:05:01 +09:00
parent 97cca74841
commit f250ba922c
5 changed files with 239 additions and 4 deletions

Binary file not shown.

1
public/icon/font.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M349.1 114.7C343.9 103.3 332.5 96 320 96C307.5 96 296.1 103.3 290.9 114.7L123.5 480L112 480C94.3 480 80 494.3 80 512C80 529.7 94.3 544 112 544L200 544C217.7 544 232 529.7 232 512C232 494.3 217.7 480 200 480L193.9 480L215.9 432L424.2 432L446.2 480L440.1 480C422.4 480 408.1 494.3 408.1 512C408.1 529.7 422.4 544 440.1 544L528.1 544C545.8 544 560.1 529.7 560.1 512C560.1 494.3 545.8 480 528.1 480L516.6 480L349.2 114.7zM394.8 368L245.2 368L320 204.8L394.8 368z"/></svg>

After

Width:  |  Height:  |  Size: 689 B

View File

@@ -3,6 +3,12 @@ import { isLoggedIn } from '../lib/auth'
let currentNetwork = 'bsky.social' let currentNetwork = 'bsky.social'
let currentLang = localStorage.getItem('preferred-lang') || 'en' let currentLang = localStorage.getItem('preferred-lang') || 'en'
let currentFont = localStorage.getItem('preferred-font') || 'system'
const FONT_OPTIONS: { id: string; label: string }[] = [
{ id: 'system', label: 'System' },
{ id: 'aifont', label: 'aifont' },
]
export function getCurrentNetwork(): string { export function getCurrentNetwork(): string {
return currentNetwork return currentNetwork
@@ -52,17 +58,52 @@ export function renderModeTabs(handle: string, activeTab: 'blog' | 'browser' | '
return `<div class="mode-tabs"><div class="tabs-scroll">${tabs}</div>${pdsSelector}</div>` return `<div class="mode-tabs"><div class="tabs-scroll">${tabs}</div>${pdsSelector}</div>`
} }
export function getCurrentFont(): string {
return currentFont
}
export function setCurrentFont(font: string): void {
currentFont = font
localStorage.setItem('preferred-font', font)
}
function applyFont(font: string): void {
// Remove all font-* classes
for (const f of FONT_OPTIONS) {
document.body.classList.remove(`font-${f.id}`)
}
// Add selected (skip system = no class)
if (font !== 'system') {
document.body.classList.add(`font-${font}`)
}
}
// Render language selector (above content) // Render language selector (above content)
export function renderLangSelector(langs: string[]): string { export function renderLangSelector(langs: string[]): string {
if (langs.length < 2) return '' const langHtml = langs.length >= 2 ? `
return `
<div class="lang-selector" id="lang-selector"> <div class="lang-selector" id="lang-selector">
<button type="button" class="lang-btn" id="lang-tab"> <button type="button" class="lang-btn" id="lang-tab">
<img src="/icon/language.svg" alt="Lang" class="lang-icon"> <img src="/icon/language.svg" alt="Lang" class="lang-icon">
</button> </button>
<div class="lang-dropdown" id="lang-dropdown"></div> <div class="lang-dropdown" id="lang-dropdown"></div>
</div> </div>
` : ''
const fontHtml = renderFontToggle()
if (!langHtml && !fontHtml) return ''
return `<div class="content-header">${fontHtml}${langHtml}</div>`
}
// Render font selector with dropdown
export function renderFontToggle(): string {
return `
<div class="font-selector" id="font-selector">
<button type="button" class="font-btn ${currentFont !== 'system' ? 'active' : ''}" id="font-tab">
<img src="/icon/font.svg" alt="Font" class="font-icon">
</button>
<div class="font-dropdown" id="font-dropdown"></div>
</div>
` `
} }
@@ -157,9 +198,54 @@ export async function setupModeTabs(onNetworkChange: (network: string) => void,
}) })
} }
// Setup font selector
const fontTab = document.getElementById('font-tab')
const fontDropdown = document.getElementById('font-dropdown')
if (fontTab && fontDropdown) {
// Apply saved font on load
applyFont(currentFont)
// Build font options
const fontOptionsHtml = FONT_OPTIONS.map(f => {
const isSelected = f.id === currentFont
return `
<div class="font-option ${isSelected ? 'selected' : ''}" data-font="${f.id}">
<span class="font-name">${f.label}</span>
<span class="font-check">\u2713</span>
</div>
`
}).join('')
fontDropdown.innerHTML = fontOptionsHtml
// Toggle dropdown
fontTab.addEventListener('click', (e) => {
e.stopPropagation()
fontDropdown.classList.toggle('show')
})
// Handle option selection
fontDropdown.querySelectorAll('.font-option').forEach(opt => {
opt.addEventListener('click', (e) => {
e.stopPropagation()
const font = (opt as HTMLElement).dataset.font || 'system'
applyFont(font)
setCurrentFont(font)
fontTab.classList.toggle('active', font !== 'system')
fontDropdown.querySelectorAll('.font-option').forEach(o => o.classList.remove('selected'))
opt.classList.add('selected')
fontDropdown.classList.remove('show')
})
})
}
// Close dropdowns on outside click // Close dropdowns on outside click
document.addEventListener('click', () => { document.addEventListener('click', () => {
pdsDropdown?.classList.remove('show') pdsDropdown?.classList.remove('show')
langDropdown?.classList.remove('show') langDropdown?.classList.remove('show')
fontDropdown?.classList.remove('show')
}) })
} }

View File

@@ -885,6 +885,12 @@ async function render(route: Route): Promise<void> {
} }
} }
// Apply saved font preference immediately
const savedFont = localStorage.getItem('preferred-font')
if (savedFont && savedFont !== 'system') {
document.body.classList.add(`font-${savedFont}`)
}
// Initial render // Initial render
render(parseRoute()) render(parseRoute())

View File

@@ -15,7 +15,7 @@
} }
body { body {
font-family: 'aifont', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6; line-height: 1.6;
color: #1a1a1a; color: #1a1a1a;
background: #fff; background: #fff;
@@ -1506,9 +1506,151 @@ body {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
gap: 4px;
margin-bottom: 8px; margin-bottom: 8px;
} }
/* Font Toggle */
.font-selector {
display: flex;
align-items: center;
}
.font-btn {
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
border-radius: 6px;
cursor: pointer;
padding: 8px 16px;
}
.font-btn:hover {
background: #f0f0f0;
}
.font-icon {
width: 20px;
height: 20px;
opacity: 0.6;
}
.font-btn:hover .font-icon {
opacity: 0.9;
}
.font-btn.active .font-icon {
opacity: 1.0;
}
.font-selector {
position: relative;
}
.font-dropdown {
display: none;
position: absolute;
top: 100%;
right: 0;
margin-top: 4px;
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
min-width: 100px;
z-index: 100;
overflow: hidden;
}
.font-dropdown.show {
display: block;
}
.font-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
cursor: pointer;
font-size: 14px;
transition: background 0.15s;
}
.font-option:hover {
background: #f5f5f5;
}
.font-option.selected {
background: linear-gradient(135deg, #f0f7ff 0%, #e8f4ff 100%);
}
.font-name {
color: #333;
font-weight: 500;
}
.font-check {
width: 18px;
height: 18px;
border-radius: 50%;
border: 2px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
transition: all 0.2s;
}
.font-option.selected .font-check {
background: var(--btn-color);
border-color: var(--btn-color);
color: #fff;
}
.font-option:not(.selected) .font-check {
color: transparent;
}
/* Font class on body */
body.font-aifont {
font-family: 'aifont', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
letter-spacing: -0.08em;
}
body.font-aifont code,
body.font-aifont pre,
body.font-aifont kbd,
body.font-aifont samp {
letter-spacing: normal;
}
@media (prefers-color-scheme: dark) {
.font-btn:hover {
background: #2a2a2a;
}
.font-icon {
filter: invert(0.7);
}
.font-btn.active .font-icon {
filter: invert(1);
}
.font-dropdown {
background: #1a1a1a;
border-color: #333;
}
.font-option:hover {
background: #2a2a2a;
}
.font-option.selected {
background: linear-gradient(135deg, #1a2a3a 0%, #1a3040 100%);
}
.font-name {
color: #e0e0e0;
}
}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.lang-btn { .lang-btn {
background: transparent; background: transparent;