diff --git a/worker/src/pages/admin.ts b/worker/src/pages/admin.ts index 6d7eef9..df5ca39 100644 --- a/worker/src/pages/admin.ts +++ b/worker/src/pages/admin.ts @@ -10,7 +10,7 @@ export function renderAdminPage(): string {
Admin token
- +
@@ -38,6 +38,17 @@ export function renderAdminPage(): string { \` } + function flashSaved(btn) { + return new Promise((resolve) => { + const original = btn.textContent + btn.textContent = 'Saved' + setTimeout(() => { + btn.textContent = original + resolve() + }, 700) + }) + } + async function loadState() { const res = await fetch('/api/admin/state', { headers: { authorization: \`Bearer \${token}\` }, @@ -51,10 +62,10 @@ export function renderAdminPage(): string {
Site settings
\${fieldRow('site', 'name', 'Site name', state.site?.name)} \${fieldRow('site', 'description', 'Site description', state.site?.description, true)} - + \` - document.getElementById('save-site').addEventListener('click', async () => { + document.getElementById('save-site').addEventListener('click', async (e) => { const name = document.querySelector('[data-id="site"][data-field="name"]').value const description = document.querySelector('[data-id="site"][data-field="description"]').value await fetch('/api/admin/site', { @@ -62,6 +73,7 @@ export function renderAdminPage(): string { headers: { 'content-type': 'application/json', authorization: \`Bearer \${token}\` }, body: JSON.stringify({ name, description }), }) + await flashSaved(e.currentTarget) init() }) } @@ -73,7 +85,7 @@ export function renderAdminPage(): string {
- +
\`).join('') } @@ -92,12 +104,12 @@ export function renderAdminPage(): string { - +
Channels (any name - Next's client lists these for the user to pick)
\${channelRows(id, state.channels[id])}
- +
\` }).join('') @@ -125,6 +137,7 @@ export function renderAdminPage(): string { headers: { 'content-type': 'application/json', authorization: \`Bearer \${token}\` }, body: JSON.stringify(patch), }) + await flashSaved(btn) init() })) @@ -157,6 +170,7 @@ export function renderAdminPage(): string { alert(data.error) return } + await flashSaved(btn) init() }) }) diff --git a/worker/src/pages/browse.ts b/worker/src/pages/browse.ts index a8974e2..83de6ff 100644 --- a/worker/src/pages/browse.ts +++ b/worker/src/pages/browse.ts @@ -13,11 +13,17 @@ export function renderBrowsePage( const override = overrides[id] const version = plugin.channels.latest ?? Object.keys(plugin.versions)[0] const tagline = override?.tagline ?? plugin.description + const badges = Object.keys(plugin.channels) + .map( + channel => + `
${escapeHtml(channel.toUpperCase())}
`, + ) + .join('') return `
${plugin.icon ? escapeHtml(plugin.icon) : '\u{1F9E9}'}
-
STABLE
+
${badges}
${escapeHtml(plugin.name)}
${escapeHtml(tagline)}
diff --git a/worker/src/theme.ts b/worker/src/theme.ts index f2b4d7f..6942dd4 100644 --- a/worker/src/theme.ts +++ b/worker/src/theme.ts @@ -90,6 +90,7 @@ export const PAGE_CSS = ` border: 1px solid rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 15px; } + .badge-row { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; } .badge { background: rgba(154,140,255,0.12); color: #b6acff; border: 1px solid rgba(154,140,255,0.25); font-size: 10px; font-weight: 650; padding: 3px 8px; border-radius: 5px; @@ -98,16 +99,19 @@ export const PAGE_CSS = ` .card-desc { position: relative; font-size: 13.5px; color: rgba(255,255,255,0.55); margin-top: 6px; line-height: 1.5; } .card-foot { position: relative; display: flex; align-items: center; justify-content: space-between; margin-top: 18px; } .card-meta { font-size: 11.5px; color: rgba(255,255,255,0.4); } + button { font-family: inherit; } .btn { background: #e8e9ed; color: #0b0c10; font-size: 13px; font-weight: 650; - padding: 8px 16px; border-radius: 8px; display: inline-block; + padding: 8px 16px; border-radius: 8px; display: inline-block; border: none; cursor: pointer; transition: transform 0.2s ease, opacity 0.2s ease; } .btn:hover { transform: translateY(-2px); opacity: 0.92; } .btn-ghost { - border: 1px solid rgba(255,255,255,0.16); color: #fff; font-size: 13px; font-weight: 600; - padding: 8px 16px; border-radius: 8px; display: inline-block; + background: transparent; border: 1px solid rgba(255,255,255,0.16); color: #fff; + font-size: 13px; font-weight: 600; padding: 8px 16px; border-radius: 8px; + display: inline-block; cursor: pointer; transition: border-color 0.2s ease, background 0.2s ease; } + .btn-ghost:hover { border-color: rgba(255,255,255,0.32); background: rgba(255,255,255,0.04); } ` export const SOURCE_URL =