wayBetter for X 1.0.2
This commit is contained in:
parent
7e2515aa42
commit
467aea2e28
212 changed files with 10208 additions and 2345 deletions
|
|
@ -49,7 +49,7 @@ const fileName = (item: Item) => (item.kind === 'plugin' ? item.file! : decodeUR
|
|||
/** What each permission is called and means, in plain words (docs/PLUGINS.md → Permissions): `site.gen.permission.<id>.label|plain`. */
|
||||
const PERMISSIONS = new Set(['storage', 'ui', 'clipboard', 'account', 'users', 'commands', 'network', 'network:none']);
|
||||
const permissionKey = (p: string) => `site.gen.permission.${p === 'network:none' ? 'networkNone' : p}`;
|
||||
const CATEGORIES = new Set(['reading', 'writing', 'filters', 'focus', 'tools', 'dark', 'light', 'language']);
|
||||
const CATEGORIES = new Set(['reading', 'writing', 'filters', 'focus', 'tools', 'dark', 'light', 'language', 'icons']);
|
||||
/**
|
||||
* A description for display. A plugin header keeps at most 300 characters, so a long one reaches
|
||||
* the catalog cut mid-word: end it at the last whole word instead.
|
||||
|
|
@ -271,6 +271,7 @@ function itemHtml(page: Page, item: Item): string {
|
|||
[t('site.gen.item.file'), `<code>${esc(file)}</code>`],
|
||||
[t('site.gen.item.size'), `${kb(item.size)} <span class="it-dim">${esc(t('site.gen.item.bytes', { count: item.size.toLocaleString('en-US') }))}</span>`],
|
||||
...(updated ? [[t('site.gen.item.updated'), `<time datetime="${updated}">${esc(dateLabel(updated, lang))}</time>`] as [string, string]] : []),
|
||||
...(item.kind === 'icons' ? [[t('site.gen.item.dataOnly'), esc(t('site.gen.item.dataOnlyIcons'))] as [string, string]] : []),
|
||||
...(item.kind === 'strings' ? [
|
||||
[t('site.gen.item.language'), `<span lang="${esc(item.language ?? '')}">${esc(item.language ?? '')}</span>`],
|
||||
...(coverageOf(item) ? [[t('site.gen.item.coverage'), esc(t('site.gen.item.coverageValue', coverageOf(item)!))] as [string, string]] : []),
|
||||
|
|
@ -302,7 +303,7 @@ function itemHtml(page: Page, item: Item): string {
|
|||
return `<section class="it-top">
|
||||
<div class="it-glow" aria-hidden="true"${item.colors ? ` style="--it-accent:${esc(item.colors.accent)}"` : ''}></div>
|
||||
<nav class="it-crumbs wrap" aria-label="${esc(t('site.gen.breadcrumb'))}">
|
||||
<ol><li><a href="${rel}store/">${esc(t('site.gen.store'))}</a></li><li><a href="${rel}store/?kind=${item.kind}">${esc(t(item.kind === 'theme' ? 'site.gen.themes' : item.kind === 'strings' ? 'site.gen.strings' : 'site.gen.plugins'))}</a></li><li aria-current="page"${own(page)}>${esc(item.name)}</li></ol>
|
||||
<ol><li><a href="${rel}store/">${esc(t('site.gen.store'))}</a></li><li><a href="${rel}store/?kind=${item.kind}">${esc(t(item.kind === 'theme' ? 'site.gen.themes' : item.kind === 'strings' ? 'site.gen.strings' : item.kind === 'icons' ? 'site.gen.icons' : 'site.gen.plugins'))}</a></li><li aria-current="page"${own(page)}>${esc(item.name)}</li></ol>
|
||||
</nav>
|
||||
<div class="wrap it-grid">
|
||||
${mediaHtml(page, item)}
|
||||
|
|
@ -314,7 +315,7 @@ function itemHtml(page: Page, item: Item): string {
|
|||
${item.kind === 'plugin' ? `<ul class="gal-perms" aria-label="${esc(t('site.gen.permissions'))}">${permissionChips(page, item.permissions)}</ul>` : swatches(item)}
|
||||
<div class="it-actions" data-install-host>
|
||||
<a class="btn btn-brush" ${installAttrs(page, item)}>${esc(t('site.gen.item.install'))}</a>
|
||||
<a class="btn btn-ghost" href="${esc(fileHref)}" download="${esc(file)}" data-download>${esc(t('site.gen.item.download', { extension: item.kind === 'theme' ? '.json' : item.kind === 'strings' ? '.strings.json' : '.plugin.js' }))}</a>
|
||||
<a class="btn btn-ghost" href="${esc(fileHref)}" download="${esc(file)}" data-download>${esc(t('site.gen.item.download', { extension: item.kind === 'theme' ? '.json' : item.kind === 'strings' ? '.strings.json' : item.kind === 'icons' ? '.icons.json' : '.plugin.js' }))}</a>
|
||||
<a class="btn btn-ghost it-share" href="${esc(address)}" data-share data-share-title="${esc(`${item.name} · wayBetter Store`)}">${esc(t('site.gen.item.share'))}</a>
|
||||
</div>
|
||||
<p class="it-note">${app.oneClickSince ? th('site.gen.item.note', { app: app.name, version: app.oneClickSince }, how) : th('site.gen.item.noteAny', { app: app.name }, how)}</p>
|
||||
|
|
@ -328,7 +329,7 @@ function itemHtml(page: Page, item: Item): string {
|
|||
<div class="it-main">
|
||||
${permissions}${palette}
|
||||
<section class="it-block" aria-labelledby="it-source">
|
||||
<h2 class="it-h" id="it-source">${esc(t(item.kind === 'plugin' ? 'site.gen.item.code' : item.kind === 'strings' ? 'site.gen.item.packFile' : 'site.gen.item.themeFile'))}</h2>
|
||||
<h2 class="it-h" id="it-source">${esc(t(item.kind === 'plugin' ? 'site.gen.item.code' : item.kind === 'strings' || item.kind === 'icons' ? 'site.gen.item.packFile' : 'site.gen.item.themeFile'))}</h2>
|
||||
<details class="it-src" data-source data-source-url="${esc(fileHref)}" data-source-lang="${item.kind === 'plugin' ? 'js' : 'json'}">
|
||||
<summary><span class="it-src-t">${esc(t('site.gen.item.read', { file }))}</span><span class="it-src-m">${kb(item.size)} · ${esc(t('site.gen.item.exactBytes'))}</span></summary>
|
||||
<div class="code">
|
||||
|
|
@ -343,9 +344,9 @@ function itemHtml(page: Page, item: Item): string {
|
|||
<ol class="it-steps">
|
||||
<li>${th('site.gen.item.how.link', { kind: item.kind }, { ...bold, code1: '<code>waybetter://</code>' })}</li>
|
||||
<li>${th('site.gen.item.how.check', {}, bold)}</li>
|
||||
<li>${th(item.kind === 'plugin' ? 'site.gen.item.how.confirmPlugin' : item.kind === 'strings' ? 'site.gen.item.how.confirmStrings' : 'site.gen.item.how.confirmTheme', {}, bold)}</li>
|
||||
<li>${th(item.kind === 'plugin' ? 'site.gen.item.how.confirmPlugin' : item.kind === 'strings' ? 'site.gen.item.how.confirmStrings' : item.kind === 'icons' ? 'site.gen.item.how.confirmIcons' : 'site.gen.item.how.confirmTheme', {}, bold)}</li>
|
||||
</ol>
|
||||
<p class="it-fine">${th(item.kind === 'plugin' ? 'site.gen.item.how.finePlugin' : item.kind === 'strings' ? 'site.gen.item.how.fineStrings' : 'site.gen.item.how.fineTheme')} ${th('site.gen.item.how.vouched')}</p>
|
||||
<p class="it-fine">${th(item.kind === 'plugin' ? 'site.gen.item.how.finePlugin' : item.kind === 'strings' ? 'site.gen.item.how.fineStrings' : item.kind === 'icons' ? 'site.gen.item.how.fineIcons' : 'site.gen.item.how.fineTheme')} ${th('site.gen.item.how.vouched')}</p>
|
||||
<div class="it-manual">
|
||||
<h3>${esc(t('site.gen.item.manual.title'))}</h3>
|
||||
<p>${th(app.page && app.oneClickSince ? 'site.gen.item.manual.text' : 'site.gen.item.manual.textNoPage', { app: app.name, version: app.oneClickSince ?? '', file }, { a1: `<a href="${rel}${app.page ?? ''}">`, a1End: '</a>', a2: `<a href="${esc(fileHref)}" download="${esc(file)}">`, a2End: '</a>' })}</p>
|
||||
|
|
@ -366,7 +367,7 @@ function itemHtml(page: Page, item: Item): string {
|
|||
|
||||
${related.length ? `<section class="it-more">
|
||||
<div class="wrap">
|
||||
<header class="st-shelf-head"><h2 class="st-h">${esc(t(item.kind === 'theme' ? 'site.gen.item.moreThemes' : item.kind === 'strings' ? 'site.gen.item.moreStrings' : 'site.gen.item.morePlugins'))}</h2><p><a href="${rel}store/?kind=${item.kind}">${esc(t('site.gen.item.seeAll', { count: site.catalog.items.filter(other => other.kind === item.kind).length }))}</a></p></header>
|
||||
<header class="st-shelf-head"><h2 class="st-h">${esc(t(item.kind === 'theme' ? 'site.gen.item.moreThemes' : item.kind === 'strings' ? 'site.gen.item.moreStrings' : item.kind === 'icons' ? 'site.gen.item.moreIcons' : 'site.gen.item.morePlugins'))}</h2><p><a href="${rel}store/?kind=${item.kind}">${esc(t('site.gen.item.seeAll', { count: site.catalog.items.filter(other => other.kind === item.kind).length }))}</a></p></header>
|
||||
<div class="gal-grid">${related.map(other => card(page, other, 'h3')).join('\n')}</div>
|
||||
</div>
|
||||
</section>` : ''}`;
|
||||
|
|
@ -553,7 +554,8 @@ function gitDates(root: string): Map<string, string> {
|
|||
const relOf = (path: string) => '../'.repeat(path.split('/').length - 2) || './';
|
||||
|
||||
export function storePages(root: string): Plugin {
|
||||
const catalogPath = resolve(root, 'public/store/catalog.json');
|
||||
// catalog.v3.json is the full list (every kind, format 3), catalog.json what every app knows (format 2): the same items, so the pages read the fuller one.
|
||||
const catalogPath = resolve(root, existsSync(resolve(root, 'public/store/catalog.v3.json')) ? 'public/store/catalog.v3.json' : 'public/store/catalog.json');
|
||||
const featuredPath = resolve(root, '../../store/featured.json');
|
||||
const itemFrame = resolve(root, 'store/_item.html');
|
||||
const localesDir = resolve(root, 'locales');
|
||||
|
|
@ -566,8 +568,10 @@ export function storePages(root: string): Plugin {
|
|||
|
||||
const load = (): Loaded => {
|
||||
if (cache) return cache;
|
||||
const catalog = JSON.parse(readFileSync(catalogPath, 'utf8')) as Catalog;
|
||||
if (catalog.format !== 2) throw new Error('store catalog: unsupported format');
|
||||
const signed = JSON.parse(readFileSync(catalogPath, 'utf8')) as Omit<Catalog, 'items'> & { items: Array<Item | { kind: 'builtin' }> };
|
||||
if (signed.format !== 2 && signed.format !== 3) throw new Error('store catalog: unsupported format');
|
||||
// Built-in items (area plugins) are listed by the app's Store with their switch; the site has its own area pages (areas.ts), so they are not pages here.
|
||||
const catalog: Catalog = { ...signed, items: signed.items.filter((item): item is Item => item.kind !== 'builtin') };
|
||||
const slugs = itemSlugs(catalog.items);
|
||||
const files = gitDates(root);
|
||||
// Store files by their path below the site root ("store/themes/nord.json").
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue