From bb25cde388fb76f36d606318e6bb24f53a768ee4 Mon Sep 17 00:00:00 2001 From: Nino Date: Sat, 31 Oct 2026 00:12:18 -0600 Subject: [PATCH] =?UTF-8?q?Iconos=20SVG=20en=20el=20men=C3=BA=20y=20en=20l?= =?UTF-8?q?os=20botones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Iconos en línea (estilo Lucide, sin dependencias) en el menú superior, pestañas, ficha del manga, lector, historial, descargas y ajustes. En móvil el menú muestra solo los iconos. Co-Authored-By: Claude Opus 5.5 --- app/public/app.js | 90 +++++++++++++++++++++++++++++-------------- app/public/index.html | 8 ++-- app/public/style.css | 12 ++++++ 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/app/public/app.js b/app/public/app.js index c5c72b4..f9027b3 100644 --- a/app/public/app.js +++ b/app/public/app.js @@ -1,4 +1,36 @@ const app = document.getElementById('app') +// Iconos SVG en línea (trazo estilo Lucide), heredan el color del texto. +const ICONS = { + home: '', + history: '', + download: '', + settings: '', + play: '', + book: '', + stopRead: '', + stop: '', + trash: '', + refresh: '', + search: '', + sort: '', + back: '', + prev: '', + next: '', + more: '', + check: '', + login: '', + logout: '', + userPlus: '', + flame: '', + clock: '', + grid: '', + globe: '', + key: '', +} +const icon = name => `` +// Iconos del menú superior (index.html). +document.querySelectorAll('#topbar nav a[data-icon]').forEach(a => { a.innerHTML = `${icon(a.dataset.icon)}${a.textContent}` }) + const SOURCES = { lc: 'LeerCapitulo (español)', md: 'MangaDex (multi-idioma)' } // Filtros de imagen del lector (CSS filter); el brillo se suma aparte. const FILTERS = { @@ -114,8 +146,8 @@ function viewAuth(signup) {

- - ${signup ? '' : ''} + + ${signup ? `` : ''} ` const form = $('form', app) @@ -160,15 +192,15 @@ async function viewHome(params) { app.innerHTML = `
${Object.entries(SOURCES).map(([k, v]) => ``).join('')}
- + ${q && !tag ? `

Resultados para “${esc(q)}”

` : `
- - -
`} + + + `} ${tag ? `

Categoría: ${esc(tagName)} ✕${q ? ` · “${esc(q)}”` : ''}

` : ''}
Cargando…
-
` +
` const form = $('form', app) const go = (extra = {}) => { const p = new URLSearchParams({ source, ...(form.q.value ? { q: form.q.value } : { mode }), ...(tag ? { tag, tagName } : {}), ...($('[name=any]').checked ? { any: '1' } : {}), ...extra }) @@ -230,11 +262,11 @@ async function viewManga(id, langOverride) { const first = chapters[0] let mainBtn = '' if (progress && chapters.some(c => c.id === progress.chapter_id)) { - mainBtn = `Continuar · ${progress.chapter_num ? `Cap. ${esc(progress.chapter_num)}` : 'Oneshot'}, pág. ${progress.page + 1}` + mainBtn = `${icon('play')} Continuar · ${progress.chapter_num ? `Cap. ${esc(progress.chapter_num)}` : 'Oneshot'}, pág. ${progress.page + 1}` } else if (progress) { - mainBtn = `Continuar · Cap. ${esc(progress.chapter_num ?? '?')} (${esc(LANGS[progress.lang] || progress.lang || '')}), pág. ${progress.page + 1}` + mainBtn = `${icon('play')} Continuar · Cap. ${esc(progress.chapter_num ?? '?')} (${esc(LANGS[progress.lang] || progress.lang || '')}), pág. ${progress.page + 1}` } - if (first && !mainBtn) mainBtn = `Empezar a leer` + if (first && !mainBtn) mainBtn = `${icon('book')} Empezar a leer` app.innerHTML = `
@@ -245,9 +277,9 @@ async function viewManga(id, langOverride) {
${manga.tags.map(t => (t.id ? `${esc(t.name)}` : `${esc(t.name ?? t)}`)).join('')}

${esc(manga.description)}

${mainBtn} - ${progress || reads.length ? '' : ''} + ${progress || reads.length ? `` : ''} ${isLc ? '' : ` - Buscar en LeerCapitulo`} + ${icon('search')} Buscar en LeerCapitulo`}
Fuente: ${esc(SOURCES[manga.source])}${download ? ` · Carpeta: mangas/${esc(download.folder)}` : ''}
${offline ? '

Sin conexión con la fuente: mostrando solo los capítulos descargados.

' : ''} @@ -256,7 +288,7 @@ async function viewManga(id, langOverride) {

Capítulos (${chapters.length}) - ${chapters.length > 1 ? '' : ''}

+ ${chapters.length > 1 ? `` : ''} ${chapters.length ? '
    ' : isLc ? '

    Este manga no tiene capítulos.

    ' : `

    No hay capítulos legibles en ${esc(LANGS[lang] || lang)} (MangaDex retira algunos por licencia). Prueba a buscarlo en LeerCapitulo. ${chapterLangs.length ? `Disponibles en: ${chapterLangs.map(l => `${esc(LANGS[l] || l)}`).join(' ')}` : 'No hay capítulos en ningún idioma.'}

    `}` @@ -266,12 +298,12 @@ async function viewManga(id, langOverride) { const box = $('#dl') if (!box) return const active = d && DL_ACTIVE.includes(d.status) - const delBtn = d ? `` : '' + const delBtn = d ? `` : '' const bar = d && (active || d.done < d.total) ? `
    ` : '' let html - if (!d || d.status === 'cancelled') html = `
    ${delBtn}
    ${bar}` - else if (active) html = `
    ${downloadLabel(d)} · ${Math.floor(downloadPct(d))}%${delBtn}
    ${bar}
    ${downloadDetail(d)}
    ` - else html = `
    ✓ ${downloadLabel(d)}${delBtn}
    ${bar}` + if (!d || d.status === 'cancelled') html = `
    ${delBtn}
    ${bar}` + else if (active) html = `
    ${downloadLabel(d)} · ${Math.floor(downloadPct(d))}%${delBtn}
    ${bar}
    ${downloadDetail(d)}
    ` + else html = `
    ${icon('check')} ${downloadLabel(d)}${delBtn}
    ${bar}` box.innerHTML = offline ? (d ? `${downloadLabel(d)}` : '') : html $('#dlerr').textContent = d?.status === 'error' ? d.error || '' : '' $('#dlgo')?.addEventListener('click', async () => { @@ -312,7 +344,7 @@ async function viewManga(id, langOverride) { const r = readMap.get(c.id) const done = r && r.total_pages && r.page >= r.total_pages - 1 const cls = progress?.chapter_id === c.id ? 'current' : done ? 'read' : '' - const meta = (dlSet.has(c.id) ? '⬇ ' : '') + (r ? (done ? 'Leído' : `pág. ${r.page + 1}/${r.total_pages}`) : esc(c.group)) + const meta = (dlSet.has(c.id) ? `${icon('download')} ` : '') + (r ? (done ? 'Leído' : `pág. ${r.page + 1}/${r.total_pages}`) : esc(c.group)) return `
  • ${esc(chapLabel(c))}${meta}
  • ` }).join('') } @@ -358,7 +390,7 @@ async function viewReader(mangaId, chapterId, startPage) { app.innerHTML = `
    - ← + ${icon('back')}
    ${esc(manga.title)}
    @@ -370,10 +402,10 @@ async function viewReader(mangaId, chapterId, startPage) {
    - ${prev ? `« Cap. ant.` : ''} + ${prev ? `${icon('prev')} Cap. ant.` : ''} - ${next ? `Cap. sig. »` : ''} + ${next ? `Cap. sig. ${icon('next')}` : ''}
    ` const reader = $('.reader', app) @@ -391,8 +423,8 @@ async function viewReader(mangaId, chapterId, startPage) { applyFilter() if ($('#saver')) $('#saver').checked = settings.saver const endBlock = `

    Fin del capítulo ${esc(chapter.num ?? '')}

    - ${next ? `Siguiente: ${esc(chapLabel(next))}` : '

    No hay más capítulos disponibles.

    '} - Volver a la ficha
    ` + ${next ? `${icon('next')} Siguiente: ${esc(chapLabel(next))}` : '

    No hay más capítulos disponibles.

    '} + ${icon('back')} Volver a la ficha` const setPageLabel = () => { $('#pnum').textContent = `${page + 1} / ${total}`; slider.value = page + 1 } let observer = null @@ -509,8 +541,8 @@ async function viewHistory() {
    ${continueCard(h).replace('class="cont"', 'style="display:flex;gap:12px;flex:1;min-width:0;align-items:center"')}
    - Ficha - + ${icon('book')} Ficha +
    `).join('')}` app.querySelectorAll('[data-del]').forEach(b => b.addEventListener('click', async () => { @@ -538,8 +570,8 @@ async function viewDownloads() { ${d.status === 'error' ? `
    ${esc(d.error || '')}
    ` : ''}
    - ${active ? `` : ''} - + ${active ? `` : ''} +
    ` }).join('')}` app.querySelectorAll('[data-cancel]').forEach(b => b.addEventListener('click', async () => { await api(`/api/download/${b.dataset.cancel}/cancel`, { method: 'POST' }); viewDownloads() })) @@ -560,14 +592,14 @@ function viewSettings() { app.innerHTML = `

    Ajustes

    Cuenta
    Sesión iniciada como ${esc(me.username)}
    -
    +
    Idioma preferido de los capítulos
    Cambiar contraseña
    -
    +
    ` $('#logout').addEventListener('click', async () => { await api('/api/logout', { method: 'POST' }); me = null; location.hash = '#/login'; router() }) $('#lang').addEventListener('change', async e => { await api('/api/settings', { method: 'POST', body: { lang: e.target.value } }); me.lang = e.target.value; toast('Idioma guardado') }) diff --git a/app/public/index.html b/app/public/index.html index 30b5385..1184f4d 100644 --- a/app/public/index.html +++ b/app/public/index.html @@ -12,10 +12,10 @@
    Manga Reader
    diff --git a/app/public/style.css b/app/public/style.css index f3c6feb..00671ab 100644 --- a/app/public/style.css +++ b/app/public/style.css @@ -129,3 +129,15 @@ a.tag:hover, .tag.on { background: var(--accent); color: var(--accent-ink); } .dlbar { height: 8px; border-radius: 4px; overflow: hidden; } .dlbar > i { transition: width .6s ease; } .history .cont a.info { display: block; min-width: 0; flex: 1; } + +/* Iconos */ +.ic { width: 1.15em; height: 1.15em; flex: none; vertical-align: -0.2em; } +#topbar nav a { display: inline-flex; align-items: center; gap: 6px; } +.tabs button { display: inline-flex; align-items: center; gap: 6px; } +.chapters .meta .ic { width: 1em; height: 1em; } +@media (max-width: 560px) { + #topbar nav a span { display: none; } /* en móvil solo iconos en el menú */ + #topbar nav a { padding: 8px 10px; } + #topbar nav a .ic { width: 1.35em; height: 1.35em; } + .search .btn .lbl { display: none; } +}