add app and apps icons

This commit is contained in:
olcxja 2026-09-25 18:42:03 +02:00
commit 7508811374
16 changed files with 554 additions and 7 deletions

View file

@ -262,6 +262,31 @@ function wineLabel(id) {
return w ? w.label : (id === 'system' ? t('wines.system') : id)
}
function iconUrl(id) {
return `plgl-icon://${encodeURIComponent(id)}/icon`
}
function fmtPlaytime(seconds) {
const s = Math.round(seconds || 0)
if (s < 60) return t('library.playtimeSeconds', s)
const h = Math.floor(s / 3600)
const m = Math.floor((s % 3600) / 60)
if (h > 0) return t('library.playtimeHM', h, m)
return t('library.playtimeMin', m)
}
async function attachCardIcon(card, game) {
const p = await window.plgl.gamesIconPath(game.id)
if (!p) return
const img = document.createElement('img')
img.className = 'game-icon'
img.alt = ''
img.onerror = () => img.remove()
img.onload = () => card.prepend(img)
img.src = iconUrl(game.id)
if (img.complete && img.naturalWidth > 0) card.prepend(img)
}
function renderLibrary() {
const list = qs('#game-list')
list.innerHTML = ''
@ -280,6 +305,7 @@ function renderLibrary() {
<span class="badge">${wineLabel(game.wine)}</span>
</div>
<div class="meta">${t('library.lastPlayed', last)}</div>
<div class="meta">${t('library.playtime', fmtPlaytime(game.playSeconds || 0))}</div>
<div class="actions">
<button class="button primary small" data-act="play">${t('library.play')}</button>
<button class="button small danger" data-act="kill" style="display:none">${t('library.stop')}</button>
@ -321,6 +347,7 @@ function renderLibrary() {
renderLibrary()
}
list.appendChild(card)
attachCardIcon(card, game)
})
}
@ -571,6 +598,31 @@ function renderPrefixOptions(selectId, selected) {
else selects[selectId].update(opts, selected || '')
}
async function refreshConfigIcon() {
const game = state.currentGame
if (!game) return
const p = await window.plgl.gamesIconPath(game.id)
const img = qs('#gc-icon')
const ph = qs('#gc-icon-placeholder')
img.onerror = () => {
img.style.display = 'none'
img.removeAttribute('src')
ph.style.display = 'flex'
}
if (p) {
img.onload = () => {
img.style.display = ''
ph.style.display = 'none'
}
img.src = iconUrl(game.id) + '?t=' + Date.now()
if (img.complete && img.naturalWidth > 0) img.onload()
} else {
img.style.display = 'none'
img.removeAttribute('src')
ph.style.display = 'flex'
}
}
async function openGameConfig(id) {
const game = state.games.find(g => g.id === id)
if (!game) return
@ -578,6 +630,7 @@ async function openGameConfig(id) {
showView('gameconfig')
qs('#gc-title').textContent = game.name
qs('#gc-name').value = game.name
refreshConfigIcon()
qs('#gc-exe').value = game.exePath
qs('#gc-workdir').value = game.workdir
qs('#gc-args').value = game.args || ''
@ -744,6 +797,46 @@ function wireEvents() {
if (p) qs('#gc-workdir').value = p
})
qs('#gc-tool-run').addEventListener('click', runCustomTool)
qs('#gc-icon-auto').addEventListener('click', async () => {
const res = await window.plgl.gamesAutoIcon(state.currentGame.id)
if (res.ok) {
toast(t('config.icon.set'), 'success')
refreshConfigIcon()
} else {
toast(t('config.icon.noIcon'), 'error')
}
})
qs('#gc-icon-browse').addEventListener('click', async () => {
const p = await window.plgl.dialogPickFile({
properties: ['openFile'],
title: t('config.icon.pick'),
filters: [{ name: t('config.icon.pick'), extensions: ['png', 'jpg', 'jpeg', 'svg', 'ico', 'webp', 'gif', 'bmp', 'exe'] }]
})
if (!p) return
let res
if (p.toLowerCase().endsWith('.exe')) {
res = await window.plgl.gamesSetIconFile(state.currentGame.id, p)
if (res.ok) toast(t('config.icon.set'), 'success')
else toast(t('config.icon.noIcon'), 'error')
} else {
res = await window.plgl.gamesSetIconFile(state.currentGame.id, p)
if (res.ok) toast(t('config.icon.set'), 'success')
else toast(t('config.icon.noIcon'), 'error')
}
refreshConfigIcon()
})
qs('#gc-icon-reset').addEventListener('click', async () => {
await window.plgl.gamesResetIcon(state.currentGame.id)
refreshConfigIcon()
})
qs('#btn-gc-clear-playtime').addEventListener('click', async () => {
const ok = await confirmModal(t('config.clearPlaytimeConfirm'), t('config.clearPlaytime'))
if (!ok) return
await window.plgl.gamesClearPlaytime(state.currentGame.id)
state.games = await window.plgl.gamesList()
state.currentGame = state.games.find(g => g.id === state.currentGame.id)
toast(t('config.playtimeCleared'), 'success')
})
qs('#modal-overlay').addEventListener('click', e => {
if (e.target === qs('#modal-overlay')) closeModal()
})