Merge pull request 'feat(branding): brand kit architecture (Phase 1)' (#96) from feat/brand-kit into dev
Reviewed-on: #96
1
.gitignore
vendored
|
|
@ -29,6 +29,7 @@ dist.tar.gz
|
||||||
|
|
||||||
# auto-generated build file for PWA
|
# auto-generated build file for PWA
|
||||||
dev-dist/sw.js
|
dev-dist/sw.js
|
||||||
|
public/icons/
|
||||||
aio-shadcn-vite.code-workspace
|
aio-shadcn-vite.code-workspace
|
||||||
dev-dist
|
dev-dist
|
||||||
.specstory/history
|
.specstory/history
|
||||||
|
|
|
||||||
52
CLAUDE.md
|
|
@ -712,8 +712,60 @@ VITE_ADMIN_PUBKEYS='["pubkey1","pubkey2"]'
|
||||||
|
|
||||||
# Optional: Disable WebSocket if needed
|
# Optional: Disable WebSocket if needed
|
||||||
VITE_WEBSOCKET_ENABLED=true
|
VITE_WEBSOCKET_ENABLED=true
|
||||||
|
|
||||||
|
# Brand kit override (defaults to ./branding/default)
|
||||||
|
BRAND_DIR=branding/cfaun
|
||||||
|
|
||||||
|
# Per-standalone brand override (set by build pipeline, not directly)
|
||||||
|
BRAND_APP=events
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Brand kit (white-label PWA branding)
|
||||||
|
|
||||||
|
The webapp ships a brand kit architecture so the hub + every standalone
|
||||||
|
(events, wallet, chat, market, …) can be rebranded per deployment without
|
||||||
|
forking the codebase. See `branding/README.md` for the deployer contract.
|
||||||
|
|
||||||
|
**Single source of truth:** `branding/<dep>/` holds `logo.{svg,png}` +
|
||||||
|
`brand.json`. `vite-branding.ts` reads brand.json and exposes a `@brand`
|
||||||
|
import alias. `pwa-assets.config.ts` + `@vite-pwa/assets-generator` derive
|
||||||
|
the full PWA icon set from the single logo source.
|
||||||
|
|
||||||
|
**brand.json schema:** `{ name, shortName?, themeColor?, backgroundColor? }`
|
||||||
|
— `name` drives the manifest. `themeColor`/`backgroundColor` are optional
|
||||||
|
chrome overrides; when unset, each standalone's per-app accent applies.
|
||||||
|
|
||||||
|
**In-app logo:** components reference `@brand/logo.png`. Active consumers:
|
||||||
|
`Login.vue`, `LoginDemo.vue`, `AppSidebar.vue`, `MobileDrawer.vue`. The
|
||||||
|
Vite alias resolves to the active brand dir at build time.
|
||||||
|
|
||||||
|
**Generated icons:** `public/icons/` is gitignored. `brandAssetsPlugin()`
|
||||||
|
(registered first in every `vite.*.config.ts`'s plugins[]) runs the
|
||||||
|
generator once per build/dev start via `buildStart`. Outputs match the
|
||||||
|
existing filename convention (`icon-192.png`, `icon-maskable-512.png`,
|
||||||
|
…) so HTML `<link>` hrefs and VitePWA `manifest.icons` reference
|
||||||
|
`/icons/<name>` consistently across all 9 configs.
|
||||||
|
|
||||||
|
**Per-standalone override:** `branding/<dep>/icons/<app>/logo.{svg,png}`
|
||||||
|
is checked before the brand's primary logo. The standalone build sets
|
||||||
|
`BRAND_APP`; deployers just put files in the right place.
|
||||||
|
|
||||||
|
**Switching brands:**
|
||||||
|
```bash
|
||||||
|
BRAND_DIR=branding/cfaun pnpm build:events
|
||||||
|
```
|
||||||
|
|
||||||
|
**Adding a new in-app logo consumer:** use `<img src="@brand/logo.png">`
|
||||||
|
instead of `@/assets/logo.png`. The latter still works for non-brand
|
||||||
|
assets (`@/assets/bitcoin.svg`, etc.) — it's only the logo that moved.
|
||||||
|
|
||||||
|
**NixOS deployment:** brand directories will eventually live in
|
||||||
|
`deploy/server-deploy/hosts/<host>/branding/`, with each host's
|
||||||
|
`services/webapp.nix` calling `inputs.webapp.lib.mkWebapp { brandDir =
|
||||||
|
./../branding; }`. `flake.nix` exposing `lib.mkWebapp` is a follow-up
|
||||||
|
to this PR. Until it lands, server-deploy continues to build webapp
|
||||||
|
through its existing path. See aiolabs/server-deploy#8.
|
||||||
|
|
||||||
## Payment Rails Pattern
|
## Payment Rails Pattern
|
||||||
|
|
||||||
Shared primitives for modules that mix Lightning + fiat (and, future,
|
Shared primitives for modules that mix Lightning + fiat (and, future,
|
||||||
|
|
|
||||||
110
branding/README.md
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
# Brand kit
|
||||||
|
|
||||||
|
This directory holds the **white-label brand kit** that drives the PWA's icons, manifest name/colors, and in-app `<img>` logo across the hub and every standalone (events, wallet, chat, market, …).
|
||||||
|
|
||||||
|
The committed `default/` is aiolabs's brand and is what unparameterized builds use. Downstream deployers add a sibling directory (e.g. `branding/cfaun/`) and point `BRAND_DIR` at it to ship a fully rebranded build with no fork required.
|
||||||
|
|
||||||
|
## Directory layout
|
||||||
|
|
||||||
|
```
|
||||||
|
branding/
|
||||||
|
README.md
|
||||||
|
default/ # aiolabs default, committed
|
||||||
|
logo.svg # preferred source (sharp at every size)
|
||||||
|
logo.png # fallback source (≥ 1024×1024 if PNG-only)
|
||||||
|
brand.json # { name, shortName?, themeColor?, backgroundColor? }
|
||||||
|
icons/ # optional per-standalone overrides
|
||||||
|
events/logo.svg
|
||||||
|
wallet/logo.png
|
||||||
|
cfaun/ # downstream deployer's brand (gitignored or in deploy repo)
|
||||||
|
logo.svg
|
||||||
|
brand.json
|
||||||
|
```
|
||||||
|
|
||||||
|
aiolabs's `default/` currently ships PNG-only (1024×1024). Replace with `logo.svg` when a vector source becomes available — produces sharper icons at every size and unlocks `favicon.svg`.
|
||||||
|
|
||||||
|
## Source formats
|
||||||
|
|
||||||
|
**SVG strongly preferred:**
|
||||||
|
|
||||||
|
- Crisp at every output size (192 / 512 maskable / 180 apple / 48 favicon)
|
||||||
|
- Enables sharp `favicon.svg` for modern browsers
|
||||||
|
- The in-app `@brand/logo` reference can be tinted via CSS (`currentColor`, filters)
|
||||||
|
|
||||||
|
**PNG accepted with constraints:**
|
||||||
|
|
||||||
|
- **≥ 1024×1024** — smaller sources produce blurry icons on high-DPI Android install screens
|
||||||
|
- **Square aspect ratio** — PWA icon canvas is square
|
||||||
|
- **Transparent background** — the generator applies maskable/apple background colors itself
|
||||||
|
- PNG-source deployments lose the `favicon.svg` benefit and the recolorable in-app logo
|
||||||
|
|
||||||
|
When both `logo.svg` and `logo.png` are present, SVG wins.
|
||||||
|
|
||||||
|
## brand.json schema
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"name": "AIO", // required — drives PWA manifest name
|
||||||
|
"shortName": "AIO", // optional — PWA home-screen label; defaults to `name`
|
||||||
|
"themeColor": "#1f2937", // optional — PWA chrome color override (otherwise each standalone keeps its accent)
|
||||||
|
"backgroundColor": "#fff" // optional — PWA splash background
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`themeColor` and `backgroundColor` are *overrides*, not defaults. When unset, each standalone's own accent applies (wallet yellow `#eab308`, chat green `#16a34a`, …) — so the default brand kit preserves the per-app visual identity, and a deployer who wants unified chrome adds the override.
|
||||||
|
|
||||||
|
## Per-standalone overrides
|
||||||
|
|
||||||
|
Place a logo at `branding/<dep>/icons/<app>/logo.{svg,png}` to override the brand's primary logo for a single standalone build.
|
||||||
|
|
||||||
|
Resolution at build time:
|
||||||
|
|
||||||
|
1. `branding/<dep>/icons/<app>/logo.svg`
|
||||||
|
2. `branding/<dep>/icons/<app>/logo.png`
|
||||||
|
3. `branding/<dep>/logo.svg`
|
||||||
|
4. `branding/<dep>/logo.png`
|
||||||
|
5. Build fails with a clear error pointing here.
|
||||||
|
|
||||||
|
`<app>` is set via `BRAND_APP` env var (the standalone build script sets this; deployers don't touch it directly).
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
**Building with the default brand:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build # main shell
|
||||||
|
pnpm build:events # events standalone
|
||||||
|
# … one per standalone
|
||||||
|
```
|
||||||
|
|
||||||
|
**Building with a deployer's brand:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BRAND_DIR=branding/cfaun pnpm build:events
|
||||||
|
```
|
||||||
|
|
||||||
|
`BRAND_DIR` accepts relative paths (resolved from the webapp repo root) or absolute paths (used by the NixOS builder, which mounts the brand directory into the sandbox at a `/nix/store/...-branding` path).
|
||||||
|
|
||||||
|
**Regenerating icons explicitly:**
|
||||||
|
|
||||||
|
The Vite plugin auto-runs the generator on every build/dev start. To run it standalone:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm generate-pwa-assets
|
||||||
|
```
|
||||||
|
|
||||||
|
Outputs land in `public/icons/` (gitignored).
|
||||||
|
|
||||||
|
## Build pipeline
|
||||||
|
|
||||||
|
1. `BRAND_DIR` is resolved (defaults to `./branding/default`).
|
||||||
|
2. `vite-branding.ts` reads `brand.json` and exposes `@brand/<file>` alias.
|
||||||
|
3. `brandAssetsPlugin()` (registered in every `vite.*.config.ts`) runs `scripts/generate-pwa-assets.mjs` once per build via `buildStart`.
|
||||||
|
4. The script stages the source logo into `public/icons/.brand-source.{svg,png}`, runs `pwa-assets-generator`, then deletes the staged source.
|
||||||
|
5. Vite copies `public/icons/` into `dist/icons/`. Manifest references `icons/<name>.png`. HTML `<link>` tags reference `/icons/<name>.{ico,png}`.
|
||||||
|
|
||||||
|
## Integration with NixOS deployment
|
||||||
|
|
||||||
|
See [aiolabs/webapp#95](https://git.atitlan.io/aiolabs/webapp/issues/95) for the full architecture. In short: `deploy/server-deploy/hosts/<host>/branding/` will hold per-host brands, and each host's `services/webapp.nix` will call `inputs.webapp.lib.mkWebapp { brandDir = ./../branding; }` once `lib.mkWebapp` is exposed from `flake.nix` (separate follow-up issue).
|
||||||
|
|
||||||
|
The architectural payoff: brand and code become independent axes. Logo changes ship via server-deploy commits + redeploys — no webapp release, no `flake.lock` bump.
|
||||||
4
branding/default/brand.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "AIO",
|
||||||
|
"shortName": "AIO"
|
||||||
|
}
|
||||||
BIN
branding/default/logo.png
Normal file
|
After Width: | Height: | Size: 456 KiB |
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>Chat — Encrypted</title>
|
<title>Chat — Encrypted</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="Chat">
|
<meta name="apple-mobile-web-app-title" content="Chat">
|
||||||
<meta name="description" content="End-to-end encrypted Nostr chat">
|
<meta name="description" content="End-to-end encrypted Nostr chat">
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>%VITE_APP_NAME%</title>
|
<title>%VITE_APP_NAME%</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="%VITE_APP_NAME%">
|
<meta name="apple-mobile-web-app-title" content="%VITE_APP_NAME%">
|
||||||
<meta name="description" content="Discover events near you">
|
<meta name="description" content="Discover events near you">
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>Forum — Discussions</title>
|
<title>Forum — Discussions</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="Forum">
|
<meta name="apple-mobile-web-app-title" content="Forum">
|
||||||
<meta name="description" content="Decentralized link aggregator and discussion forum on Nostr">
|
<meta name="description" content="Decentralized link aggregator and discussion forum on Nostr">
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,8 @@
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<!-- <meta name="theme-color" content="#ffffff"> -->
|
<!-- <meta name="theme-color" content="#ffffff"> -->
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>%VITE_APP_NAME% Hub</title>
|
<title>%VITE_APP_NAME% Hub</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="%VITE_APP_NAME%">
|
<meta name="apple-mobile-web-app-title" content="%VITE_APP_NAME%">
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>Libra — Accounting</title>
|
<title>Libra — Accounting</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="Libra">
|
<meta name="apple-mobile-web-app-title" content="Libra">
|
||||||
<meta name="description" content="Team accounting and expense management">
|
<meta name="description" content="Team accounting and expense management">
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>Market — Nostr</title>
|
<title>Market — Nostr</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="Market">
|
<meta name="apple-mobile-web-app-title" content="Market">
|
||||||
<meta name="description" content="Decentralized marketplace on Nostr with Lightning payments">
|
<meta name="description" content="Decentralized marketplace on Nostr with Lightning payments">
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "electron/main.cjs",
|
"main": "electron/main.cjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"generate-pwa-assets": "node scripts/generate-pwa-assets.mjs",
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
"build": "vue-tsc -b && vite build",
|
"build": "vue-tsc -b && vite build",
|
||||||
"preview": "vite preview --host",
|
"preview": "vite preview --host",
|
||||||
|
|
@ -92,6 +93,7 @@
|
||||||
"@types/node": "^22.18.1",
|
"@types/node": "^22.18.1",
|
||||||
"@types/qrcode": "^1.5.5",
|
"@types/qrcode": "^1.5.5",
|
||||||
"@types/rollup-plugin-visualizer": "^4.2.3",
|
"@types/rollup-plugin-visualizer": "^4.2.3",
|
||||||
|
"@vite-pwa/assets-generator": "^1.0.2",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
|
|
|
||||||
109
pnpm-lock.yaml
generated
|
|
@ -150,6 +150,9 @@ importers:
|
||||||
'@types/rollup-plugin-visualizer':
|
'@types/rollup-plugin-visualizer':
|
||||||
specifier: ^4.2.3
|
specifier: ^4.2.3
|
||||||
version: 4.2.4
|
version: 4.2.4
|
||||||
|
'@vite-pwa/assets-generator':
|
||||||
|
specifier: ^1.0.2
|
||||||
|
version: 1.0.2
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^5.2.1
|
specifier: ^5.2.1
|
||||||
version: 5.2.4(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(vue@3.5.34(typescript@5.6.3))
|
version: 5.2.4(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(vue@3.5.34(typescript@5.6.3))
|
||||||
|
|
@ -188,7 +191,7 @@ importers:
|
||||||
version: 0.8.9(rollup@4.60.4)(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
version: 0.8.9(rollup@4.60.4)(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
||||||
vite-plugin-pwa:
|
vite-plugin-pwa:
|
||||||
specifier: ^0.21.1
|
specifier: ^0.21.1
|
||||||
version: 0.21.2(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1)
|
version: 0.21.2(@vite-pwa/assets-generator@1.0.2)(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1)
|
||||||
vue-tsc:
|
vue-tsc:
|
||||||
specifier: ^2.2.0
|
specifier: ^2.2.0
|
||||||
version: 2.2.12(typescript@5.6.3)
|
version: 2.2.12(typescript@5.6.3)
|
||||||
|
|
@ -711,6 +714,9 @@ packages:
|
||||||
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
|
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@canvas/image-data@1.1.0':
|
||||||
|
resolution: {integrity: sha512-QdObRRjRbcXGmM1tmJ+MrHcaz1MftF2+W7YI+MsphnsCrmtyfS0d5qJbk0MeSbUeyM/jCb0hmnkXPsy026L7dA==}
|
||||||
|
|
||||||
'@electron-forge/cli@7.11.2':
|
'@electron-forge/cli@7.11.2':
|
||||||
resolution: {integrity: sha512-c+C4ndLfHbxwZuCn9G8iT9wD/woLdaVkoSVjAIbj+0nJhi8UmiVsz/+Gxlj4cvhMRTzBMBxudstLU7RocMikfg==}
|
resolution: {integrity: sha512-c+C4ndLfHbxwZuCn9G8iT9wD/woLdaVkoSVjAIbj+0nJhi8UmiVsz/+Gxlj4cvhMRTzBMBxudstLU7RocMikfg==}
|
||||||
engines: {node: '>= 16.4.0'}
|
engines: {node: '>= 16.4.0'}
|
||||||
|
|
@ -1283,6 +1289,9 @@ packages:
|
||||||
'@polka/url@1.0.0-next.29':
|
'@polka/url@1.0.0-next.29':
|
||||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||||
|
|
||||||
|
'@quansync/fs@1.0.0':
|
||||||
|
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
|
||||||
|
|
||||||
'@rollup/plugin-babel@6.1.0':
|
'@rollup/plugin-babel@6.1.0':
|
||||||
resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==}
|
resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
@ -1697,6 +1706,11 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.24.0
|
zod: ^3.24.0
|
||||||
|
|
||||||
|
'@vite-pwa/assets-generator@1.0.2':
|
||||||
|
resolution: {integrity: sha512-MCbrb508JZHqe7bUibmZj/lyojdhLRnfkmyXnkrCM2zVrjTgL89U8UEfInpKTvPeTnxsw2hmyZxnhsdNR6yhwg==}
|
||||||
|
engines: {node: '>=16.14.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@vitejs/plugin-vue@5.2.4':
|
'@vitejs/plugin-vue@5.2.4':
|
||||||
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
|
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
|
@ -2121,6 +2135,10 @@ packages:
|
||||||
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
|
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
cac@6.7.14:
|
||||||
|
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
cacache@16.1.3:
|
cacache@16.1.3:
|
||||||
resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==}
|
resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==}
|
||||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||||
|
|
@ -2267,6 +2285,10 @@ packages:
|
||||||
engines: {node: ^14.13.0 || >=16.0.0}
|
engines: {node: ^14.13.0 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
consola@3.4.2:
|
||||||
|
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
|
||||||
|
engines: {node: ^14.18.0 || >=16.10.0}
|
||||||
|
|
||||||
convert-source-map@2.0.0:
|
convert-source-map@2.0.0:
|
||||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
|
|
@ -2366,6 +2388,14 @@ packages:
|
||||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
decode-bmp@0.2.1:
|
||||||
|
resolution: {integrity: sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==}
|
||||||
|
engines: {node: '>=8.6.0'}
|
||||||
|
|
||||||
|
decode-ico@0.4.1:
|
||||||
|
resolution: {integrity: sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA==}
|
||||||
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
decompress-response@6.0.0:
|
decompress-response@6.0.0:
|
||||||
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -2890,6 +2920,9 @@ packages:
|
||||||
humanize-ms@1.2.1:
|
humanize-ms@1.2.1:
|
||||||
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
||||||
|
|
||||||
|
ico-endec@0.1.6:
|
||||||
|
resolution: {integrity: sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==}
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
@ -3800,6 +3833,9 @@ packages:
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
quansync@1.0.0:
|
||||||
|
resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==}
|
||||||
|
|
||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
|
|
@ -4020,6 +4056,9 @@ packages:
|
||||||
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
sharp-ico@0.1.5:
|
||||||
|
resolution: {integrity: sha512-a3jODQl82NPp1d5OYb0wY+oFaPk7AvyxipIowCHk7pBsZCWgbe0yAkU2OOXdoH0ENyANhyOQbs9xkAiRHcF02Q==}
|
||||||
|
|
||||||
sharp@0.33.5:
|
sharp@0.33.5:
|
||||||
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
|
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
|
@ -4322,6 +4361,9 @@ packages:
|
||||||
resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
|
resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
|
||||||
engines: {node: '>=14.14'}
|
engines: {node: '>=14.14'}
|
||||||
|
|
||||||
|
to-data-view@1.1.0:
|
||||||
|
resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
|
|
@ -4397,6 +4439,12 @@ packages:
|
||||||
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
|
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
unconfig-core@7.5.0:
|
||||||
|
resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==}
|
||||||
|
|
||||||
|
unconfig@7.5.0:
|
||||||
|
resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==}
|
||||||
|
|
||||||
undici-types@6.21.0:
|
undici-types@6.21.0:
|
||||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
|
|
@ -5456,6 +5504,8 @@ snapshots:
|
||||||
'@babel/helper-string-parser': 7.27.1
|
'@babel/helper-string-parser': 7.27.1
|
||||||
'@babel/helper-validator-identifier': 7.28.5
|
'@babel/helper-validator-identifier': 7.28.5
|
||||||
|
|
||||||
|
'@canvas/image-data@1.1.0': {}
|
||||||
|
|
||||||
'@electron-forge/cli@7.11.2(encoding@0.1.13)(lightningcss@1.32.0)':
|
'@electron-forge/cli@7.11.2(encoding@0.1.13)(lightningcss@1.32.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@electron-forge/core': 7.11.2(encoding@0.1.13)(lightningcss@1.32.0)
|
'@electron-forge/core': 7.11.2(encoding@0.1.13)(lightningcss@1.32.0)
|
||||||
|
|
@ -6234,6 +6284,10 @@ snapshots:
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.29': {}
|
'@polka/url@1.0.0-next.29': {}
|
||||||
|
|
||||||
|
'@quansync/fs@1.0.0':
|
||||||
|
dependencies:
|
||||||
|
quansync: 1.0.0
|
||||||
|
|
||||||
'@rollup/plugin-babel@6.1.0(@babel/core@7.29.0)(rollup@4.60.4)':
|
'@rollup/plugin-babel@6.1.0(@babel/core@7.29.0)(rollup@4.60.4)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.0
|
'@babel/core': 7.29.0
|
||||||
|
|
@ -6559,6 +6613,15 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@vite-pwa/assets-generator@1.0.2':
|
||||||
|
dependencies:
|
||||||
|
cac: 6.7.14
|
||||||
|
colorette: 2.0.20
|
||||||
|
consola: 3.4.2
|
||||||
|
sharp: 0.33.5
|
||||||
|
sharp-ico: 0.1.5
|
||||||
|
unconfig: 7.5.0
|
||||||
|
|
||||||
'@vitejs/plugin-vue@5.2.4(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(vue@3.5.34(typescript@5.6.3))':
|
'@vitejs/plugin-vue@5.2.4(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(vue@3.5.34(typescript@5.6.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
vite: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
||||||
|
|
@ -7030,6 +7093,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
run-applescript: 7.1.0
|
run-applescript: 7.1.0
|
||||||
|
|
||||||
|
cac@6.7.14: {}
|
||||||
|
|
||||||
cacache@16.1.3:
|
cacache@16.1.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@npmcli/fs': 2.1.2
|
'@npmcli/fs': 2.1.2
|
||||||
|
|
@ -7193,6 +7258,8 @@ snapshots:
|
||||||
tree-kill: 1.2.2
|
tree-kill: 1.2.2
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
|
|
||||||
|
consola@3.4.2: {}
|
||||||
|
|
||||||
convert-source-map@2.0.0: {}
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
copy-anything@4.0.5:
|
copy-anything@4.0.5:
|
||||||
|
|
@ -7287,6 +7354,17 @@ snapshots:
|
||||||
|
|
||||||
decamelize@1.2.0: {}
|
decamelize@1.2.0: {}
|
||||||
|
|
||||||
|
decode-bmp@0.2.1:
|
||||||
|
dependencies:
|
||||||
|
'@canvas/image-data': 1.1.0
|
||||||
|
to-data-view: 1.1.0
|
||||||
|
|
||||||
|
decode-ico@0.4.1:
|
||||||
|
dependencies:
|
||||||
|
'@canvas/image-data': 1.1.0
|
||||||
|
decode-bmp: 0.2.1
|
||||||
|
to-data-view: 1.1.0
|
||||||
|
|
||||||
decompress-response@6.0.0:
|
decompress-response@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
mimic-response: 3.1.0
|
mimic-response: 3.1.0
|
||||||
|
|
@ -7973,6 +8051,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
ico-endec@0.1.6: {}
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
|
|
@ -8769,6 +8849,8 @@ snapshots:
|
||||||
pngjs: 5.0.0
|
pngjs: 5.0.0
|
||||||
yargs: 15.4.1
|
yargs: 15.4.1
|
||||||
|
|
||||||
|
quansync@1.0.0: {}
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
quick-lru@5.1.1: {}
|
quick-lru@5.1.1: {}
|
||||||
|
|
@ -9058,6 +9140,12 @@ snapshots:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.1.2
|
es-object-atoms: 1.1.2
|
||||||
|
|
||||||
|
sharp-ico@0.1.5:
|
||||||
|
dependencies:
|
||||||
|
decode-ico: 0.4.1
|
||||||
|
ico-endec: 0.1.6
|
||||||
|
sharp: 0.33.5
|
||||||
|
|
||||||
sharp@0.33.5:
|
sharp@0.33.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
color: 4.2.3
|
color: 4.2.3
|
||||||
|
|
@ -9387,6 +9475,8 @@ snapshots:
|
||||||
tmp@0.2.5:
|
tmp@0.2.5:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
to-data-view@1.1.0: {}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
|
|
@ -9462,6 +9552,19 @@ snapshots:
|
||||||
has-symbols: 1.1.0
|
has-symbols: 1.1.0
|
||||||
which-boxed-primitive: 1.1.1
|
which-boxed-primitive: 1.1.1
|
||||||
|
|
||||||
|
unconfig-core@7.5.0:
|
||||||
|
dependencies:
|
||||||
|
'@quansync/fs': 1.0.0
|
||||||
|
quansync: 1.0.0
|
||||||
|
|
||||||
|
unconfig@7.5.0:
|
||||||
|
dependencies:
|
||||||
|
'@quansync/fs': 1.0.0
|
||||||
|
defu: 6.1.7
|
||||||
|
jiti: 2.7.0
|
||||||
|
quansync: 1.0.0
|
||||||
|
unconfig-core: 7.5.0
|
||||||
|
|
||||||
undici-types@6.21.0: {}
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||||
|
|
@ -9543,7 +9646,7 @@ snapshots:
|
||||||
- rollup
|
- rollup
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
vite-plugin-pwa@0.21.2(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1):
|
vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@1.0.2)(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))(workbox-build@7.4.1)(workbox-window@7.4.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
pretty-bytes: 6.1.1
|
pretty-bytes: 6.1.1
|
||||||
|
|
@ -9551,6 +9654,8 @@ snapshots:
|
||||||
vite: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
vite: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
||||||
workbox-build: 7.4.1
|
workbox-build: 7.4.1
|
||||||
workbox-window: 7.4.1
|
workbox-window: 7.4.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@vite-pwa/assets-generator': 1.0.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
|
@ -1,3 +0,0 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M13 3L4 14h7l-2 7 9-11h-7l2-7z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 227 B |
54
pwa-assets.config.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { defineConfig } from '@vite-pwa/assets-generator/config'
|
||||||
|
import { copyFileSync, existsSync, mkdirSync } from 'node:fs'
|
||||||
|
import { join, resolve } from 'node:path'
|
||||||
|
|
||||||
|
const BRAND_DIR = process.env.BRAND_DIR ?? './branding/default'
|
||||||
|
const BRAND_APP = process.env.BRAND_APP ?? ''
|
||||||
|
|
||||||
|
const candidates: string[] = []
|
||||||
|
if (BRAND_APP) {
|
||||||
|
candidates.push(
|
||||||
|
join(BRAND_DIR, 'icons', BRAND_APP, 'logo.svg'),
|
||||||
|
join(BRAND_DIR, 'icons', BRAND_APP, 'logo.png'),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
candidates.push(
|
||||||
|
join(BRAND_DIR, 'logo.svg'),
|
||||||
|
join(BRAND_DIR, 'logo.png'),
|
||||||
|
)
|
||||||
|
|
||||||
|
const source = candidates.find((p) => existsSync(p))
|
||||||
|
if (!source) {
|
||||||
|
throw new Error(
|
||||||
|
`No brand logo found. Tried:\n ${candidates.join('\n ')}\n` +
|
||||||
|
`See branding/README.md for the brand kit contract.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CLI emits next to the source. Stage into public/icons/ so generated
|
||||||
|
// PNGs are served at /icons/<name>.png and a single .gitignore line covers
|
||||||
|
// the whole tree.
|
||||||
|
const stagingDir = resolve('public/icons')
|
||||||
|
mkdirSync(stagingDir, { recursive: true })
|
||||||
|
const sourceExt = source.toLowerCase().endsWith('.svg') ? '.svg' : '.png'
|
||||||
|
const stagedSource = join(stagingDir, `.brand-source${sourceExt}`)
|
||||||
|
copyFileSync(source, stagedSource)
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
headLinkOptions: { preset: '2023' },
|
||||||
|
preset: {
|
||||||
|
transparent: {
|
||||||
|
sizes: [192, 512],
|
||||||
|
favicons: [[48, 'favicon.ico']],
|
||||||
|
},
|
||||||
|
maskable: { sizes: [192, 512] },
|
||||||
|
apple: { sizes: [180] },
|
||||||
|
assetName: (type, size) => {
|
||||||
|
if (type === 'transparent') return `icon-${size.width}.png`
|
||||||
|
if (type === 'maskable') return `icon-maskable-${size.width}.png`
|
||||||
|
if (type === 'apple') return 'apple-touch-icon.png'
|
||||||
|
throw new Error(`Unknown asset type: ${type}`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
images: [stagedSource],
|
||||||
|
})
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>Restaurant — Order</title>
|
<title>Restaurant — Order</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="Restaurant">
|
<meta name="apple-mobile-web-app-title" content="Restaurant">
|
||||||
<meta name="description" content="Order from your local Nostr-native restaurant with Lightning payments">
|
<meta name="description" content="Order from your local Nostr-native restaurant with Lightning payments">
|
||||||
|
|
|
||||||
19
scripts/generate-pwa-assets.mjs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// Wraps pwa-assets-generator and removes the staged brand source after
|
||||||
|
// generation. pwa-assets.config.ts copies $BRAND_DIR/logo.{svg,png}
|
||||||
|
// into public/icons/.brand-source.* because the CLI emits next to the
|
||||||
|
// source. Without this cleanup the full-resolution source ships in
|
||||||
|
// dist/icons/ and is publicly served.
|
||||||
|
import { spawnSync } from 'node:child_process'
|
||||||
|
import { existsSync, rmSync } from 'node:fs'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
|
||||||
|
const cli = resolve('node_modules/.bin/pwa-assets-generator')
|
||||||
|
const { status } = spawnSync(cli, process.argv.slice(2), { stdio: 'inherit' })
|
||||||
|
if (status !== 0) process.exit(status ?? 1)
|
||||||
|
|
||||||
|
const stagingDir = resolve('public/icons')
|
||||||
|
for (const ext of ['svg', 'png']) {
|
||||||
|
const staged = resolve(stagingDir, `.brand-source.${ext}`)
|
||||||
|
if (existsSync(staged)) rmSync(staged)
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ const isActive = (href: string) => {
|
||||||
<div class="flex h-16 shrink-0 items-center">
|
<div class="flex h-16 shrink-0 items-center">
|
||||||
<router-link to="/" class="flex items-center gap-2">
|
<router-link to="/" class="flex items-center gap-2">
|
||||||
<img
|
<img
|
||||||
src="@/assets/logo.png"
|
src="@brand/logo.png"
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
class="h-8 w-8"
|
class="h-8 w-8"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ const navigateTo = (href: string) => {
|
||||||
<SheetHeader class="px-6 py-4 border-b border-border">
|
<SheetHeader class="px-6 py-4 border-b border-border">
|
||||||
<SheetTitle class="flex items-center gap-2">
|
<SheetTitle class="flex items-center gap-2">
|
||||||
<img
|
<img
|
||||||
src="@/assets/logo.png"
|
src="@brand/logo.png"
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
class="h-8 w-8"
|
class="h-8 w-8"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<!-- Logo and Title -->
|
<!-- Logo and Title -->
|
||||||
<div class="text-center space-y-6">
|
<div class="text-center space-y-6">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<img src="@/assets/logo.png" alt="Logo" class="h-24 w-24 sm:h-32 sm:w-32" />
|
<img src="@brand/logo.png" alt="Logo" class="h-24 w-24 sm:h-32 sm:w-32" />
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h1 class="text-3xl font-bold tracking-tight">Virtual Realm</h1>
|
<h1 class="text-3xl font-bold tracking-tight">Virtual Realm</h1>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<!-- Welcome Section -->
|
<!-- Welcome Section -->
|
||||||
<div class="text-center space-y-2 sm:space-y-4">
|
<div class="text-center space-y-2 sm:space-y-4">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<img src="@/assets/logo.png" alt="Logo" class="h-34 w-34 sm:h-42 sm:w-42 lg:h-50 lg:w-50" />
|
<img src="@brand/logo.png" alt="Logo" class="h-34 w-34 sm:h-42 sm:w-42 lg:h-50 lg:w-50" />
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-1 sm:space-y-3">
|
<div class="space-y-1 sm:space-y-3">
|
||||||
<h1 class="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight">Welcome to the Virtual Realm</h1>
|
<h1 class="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight">Welcome to the Virtual Realm</h1>
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>Tasks — Work Orders</title>
|
<title>Tasks — Work Orders</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="Tasks">
|
<meta name="apple-mobile-web-app-title" content="Tasks">
|
||||||
<meta name="description" content="Decentralized task management on Nostr">
|
<meta name="description" content="Decentralized task management on Nostr">
|
||||||
|
|
|
||||||
80
vite-branding.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { spawnSync } from 'node:child_process'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
import type { Plugin } from 'vite'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absolute path to the active brand kit. Deployers point this at their
|
||||||
|
* own `branding/<name>/` directory (see branding/README.md).
|
||||||
|
*
|
||||||
|
* Defaults to the committed aiolabs default brand. Used by vite configs
|
||||||
|
* for the `@brand` import alias and by pwa-assets.config.ts.
|
||||||
|
*/
|
||||||
|
export const BRAND_DIR = resolve(process.env.BRAND_DIR ?? './branding/default')
|
||||||
|
|
||||||
|
/** Fields parsed from brand.json. All but `name` are optional. */
|
||||||
|
export interface Brand {
|
||||||
|
/** Brand label — drives PWA manifest name. */
|
||||||
|
name: string
|
||||||
|
/** PWA install/home-screen short label. Defaults to `name`. */
|
||||||
|
shortName?: string
|
||||||
|
/**
|
||||||
|
* Optional PWA chrome theme color (status bar / title bar when installed).
|
||||||
|
* When unset, each standalone's vite config keeps its hardcoded accent.
|
||||||
|
*/
|
||||||
|
themeColor?: string
|
||||||
|
/**
|
||||||
|
* Optional PWA splash background. When unset, each standalone's vite
|
||||||
|
* config keeps its hardcoded value.
|
||||||
|
*/
|
||||||
|
backgroundColor?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const brand: Brand = JSON.parse(
|
||||||
|
readFileSync(resolve(BRAND_DIR, 'brand.json'), 'utf-8'),
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spread into a vite config's `resolve.alias` map. Lets components
|
||||||
|
* import deployer-provided assets via `@brand/<file>` instead of
|
||||||
|
* hardcoding `@/assets/logo.png`.
|
||||||
|
*/
|
||||||
|
export const brandAlias = {
|
||||||
|
'@brand': BRAND_DIR,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PWA manifest name for a standalone. Combines the brand name with the
|
||||||
|
* app's own label, or returns the bare brand when no label is given.
|
||||||
|
*
|
||||||
|
* Example: `brandManifestName('Wallet')` → "AIO Wallet" / "Cfaun Wallet".
|
||||||
|
* Example: `brandManifestName()` → "AIO" / "Sortir".
|
||||||
|
*/
|
||||||
|
export function brandManifestName(appLabel?: string): string {
|
||||||
|
return appLabel ? `${brand.name} ${appLabel}` : brand.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vite plugin: regenerate PWA icons under public/icons/ once per build
|
||||||
|
* / dev-server start, so vite.<app>.config.ts's includeAssets +
|
||||||
|
* manifest.icons always have something to include. Source resolution
|
||||||
|
* lives in pwa-assets.config.ts.
|
||||||
|
*/
|
||||||
|
export function brandAssetsPlugin(): Plugin {
|
||||||
|
let generated = false
|
||||||
|
return {
|
||||||
|
name: 'brand-assets-generator',
|
||||||
|
buildStart() {
|
||||||
|
if (generated) return
|
||||||
|
const { status } = spawnSync(
|
||||||
|
'node',
|
||||||
|
[resolve('scripts/generate-pwa-assets.mjs')],
|
||||||
|
{ stdio: 'inherit' },
|
||||||
|
)
|
||||||
|
if (status !== 0) {
|
||||||
|
throw new Error('pwa-assets-generator failed; see output above')
|
||||||
|
}
|
||||||
|
generated = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function chatHtmlPlugin(): Plugin {
|
function chatHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -45,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
chatHtmlPlugin(),
|
chatHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -59,20 +61,19 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Chat — Encrypted',
|
name: 'Chat — Encrypted',
|
||||||
short_name: 'Chat',
|
short_name: 'Chat',
|
||||||
description: 'End-to-end encrypted Nostr chat',
|
description: 'End-to-end encrypted Nostr chat',
|
||||||
theme_color: '#16a34a',
|
theme_color: brand.themeColor ?? '#16a34a',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -81,10 +82,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
categories: ['social', 'communication'],
|
categories: ['social', 'communication'],
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -103,6 +104,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
||||||
'@/app.config': fileURLToPath(new URL('./src/chat-app/app.config.ts', import.meta.url)),
|
'@/app.config': fileURLToPath(new URL('./src/chat-app/app.config.ts', import.meta.url)),
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig } from 'vite'
|
||||||
import Inspect from 'vite-plugin-inspect'
|
import Inspect from 'vite-plugin-inspect'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
//
|
//
|
||||||
|
|
@ -12,6 +13,11 @@ import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
// the entire origin and blocked Chrome from offering installs for the
|
// the entire origin and blocked Chrome from offering installs for the
|
||||||
// path-mounted standalones at /libra/, /market/, etc. The hub is a
|
// path-mounted standalones at /libra/, /market/, etc. The hub is a
|
||||||
// launcher page; users install the standalones they actually use.
|
// launcher page; users install the standalones they actually use.
|
||||||
|
|
||||||
|
// Brand name flows into index.html's `%VITE_APP_NAME% Hub` title via
|
||||||
|
// Vite's HTML env-var substitution.
|
||||||
|
process.env.VITE_APP_NAME = brand.name
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => ({
|
export default defineConfig(({ mode }) => ({
|
||||||
// Per-app dep cache so concurrent dev servers don't race on .vite/deps
|
// Per-app dep cache so concurrent dev servers don't race on .vite/deps
|
||||||
cacheDir: 'node_modules/.vite-hub',
|
cacheDir: 'node_modules/.vite-hub',
|
||||||
|
|
@ -20,6 +26,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
Inspect(),
|
Inspect(),
|
||||||
|
|
@ -43,6 +50,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin, brandManifestName } from './vite-branding'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to rewrite dev server requests to events.html
|
* Plugin to rewrite dev server requests to events.html
|
||||||
|
|
@ -41,12 +42,11 @@ function eventsHtmlPlugin(): Plugin {
|
||||||
* VITE_BASE_PATH=/events/ → app.ariege.io/events/ (shared auth)
|
* VITE_BASE_PATH=/events/ → app.ariege.io/events/ (shared auth)
|
||||||
* (default: /) → bouge.ariege.io (standalone subdomain)
|
* (default: /) → bouge.ariege.io (standalone subdomain)
|
||||||
*
|
*
|
||||||
* Set VITE_APP_NAME to brand the standalone (PWA name, HTML title, console
|
* Brand name resolves from brand.json under $BRAND_DIR (see
|
||||||
* logs). cfaun overrides to "Bouge" via NixOS. Defaults to "Events".
|
* vite-branding.ts and aiolabs/webapp#95). Surfaced into Vite's HTML
|
||||||
|
* env-var substitution as VITE_APP_NAME for templated titles.
|
||||||
*/
|
*/
|
||||||
const APP_NAME = process.env.VITE_APP_NAME || 'Events'
|
const APP_NAME = brandManifestName()
|
||||||
// Surface the resolved value back into env so Vite's HTML %VITE_APP_NAME%
|
|
||||||
// substitution picks up the fallback when nothing was explicitly set.
|
|
||||||
process.env.VITE_APP_NAME = APP_NAME
|
process.env.VITE_APP_NAME = APP_NAME
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => ({
|
export default defineConfig(({ mode }) => ({
|
||||||
|
|
@ -58,6 +58,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
eventsHtmlPlugin(),
|
eventsHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -75,20 +76,19 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: APP_NAME,
|
name: APP_NAME,
|
||||||
short_name: APP_NAME,
|
short_name: brand.shortName ?? APP_NAME,
|
||||||
description: 'Discover events near you',
|
description: 'Discover events near you',
|
||||||
theme_color: '#1f2937',
|
theme_color: brand.themeColor ?? '#1f2937',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -96,10 +96,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
id: 'aiolabs-events',
|
id: 'aiolabs-events',
|
||||||
categories: ['social', 'entertainment', 'lifestyle'],
|
categories: ['social', 'entertainment', 'lifestyle'],
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -118,6 +118,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function forumHtmlPlugin(): Plugin {
|
function forumHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -45,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
forumHtmlPlugin(),
|
forumHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -59,20 +61,19 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Forum — Discussions',
|
name: 'Forum — Discussions',
|
||||||
short_name: 'Forum',
|
short_name: 'Forum',
|
||||||
description: 'Decentralized link aggregator and discussion forum on Nostr',
|
description: 'Decentralized link aggregator and discussion forum on Nostr',
|
||||||
theme_color: '#2563eb',
|
theme_color: brand.themeColor ?? '#2563eb',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -81,10 +82,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
categories: ['social', 'news'],
|
categories: ['social', 'news'],
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -103,6 +104,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
||||||
'@/app.config': fileURLToPath(new URL('./src/forum-app/app.config.ts', import.meta.url)),
|
'@/app.config': fileURLToPath(new URL('./src/forum-app/app.config.ts', import.meta.url)),
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to rewrite dev server requests to libra.html
|
* Plugin to rewrite dev server requests to libra.html
|
||||||
|
|
@ -50,6 +51,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
libraHtmlPlugin(),
|
libraHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -66,20 +68,19 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Libra — Team Accounting',
|
name: 'Libra — Team Accounting',
|
||||||
short_name: 'Libra',
|
short_name: 'Libra',
|
||||||
description: 'Team accounting and expense management',
|
description: 'Team accounting and expense management',
|
||||||
theme_color: '#1f2937',
|
theme_color: brand.themeColor ?? '#1f2937',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -88,10 +89,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
categories: ['finance', 'business', 'productivity'],
|
categories: ['finance', 'business', 'productivity'],
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -110,6 +111,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
// ORDER MATTERS — @rollup/plugin-alias is first-match-wins.
|
// ORDER MATTERS — @rollup/plugin-alias is first-match-wins.
|
||||||
// The more specific @/app.config remap must precede the @ prefix
|
// The more specific @/app.config remap must precede the @ prefix
|
||||||
// alias, otherwise '@/app.config' matches '@' first and resolves
|
// alias, otherwise '@/app.config' matches '@' first and resolves
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function marketHtmlPlugin(): Plugin {
|
function marketHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -45,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
marketHtmlPlugin(),
|
marketHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -59,20 +61,19 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Market — Nostr',
|
name: 'Market — Nostr',
|
||||||
short_name: 'Market',
|
short_name: 'Market',
|
||||||
description: 'Decentralized marketplace on Nostr with Lightning payments',
|
description: 'Decentralized marketplace on Nostr with Lightning payments',
|
||||||
theme_color: '#dc2626',
|
theme_color: brand.themeColor ?? '#dc2626',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -81,10 +82,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
categories: ['shopping', 'business'],
|
categories: ['shopping', 'business'],
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -103,6 +104,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
||||||
'@/app.config': fileURLToPath(new URL('./src/market-app/app.config.ts', import.meta.url)),
|
'@/app.config': fileURLToPath(new URL('./src/market-app/app.config.ts', import.meta.url)),
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function restaurantHtmlPlugin(): Plugin {
|
function restaurantHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -50,6 +51,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
restaurantHtmlPlugin(),
|
restaurantHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -64,13 +66,12 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Restaurant — Order',
|
name: 'Restaurant — Order',
|
||||||
|
|
@ -78,8 +79,8 @@ export default defineConfig(({ mode }) => ({
|
||||||
description: 'Order from your local Nostr-native restaurant with Lightning payments',
|
description: 'Order from your local Nostr-native restaurant with Lightning payments',
|
||||||
// Green to differentiate from market red. PDF tile is purple
|
// Green to differentiate from market red. PDF tile is purple
|
||||||
// (see ~/dev/shared/extensions/restaurant/static/image/restaurant.png).
|
// (see ~/dev/shared/extensions/restaurant/static/image/restaurant.png).
|
||||||
theme_color: '#16a34a',
|
theme_color: brand.themeColor ?? '#16a34a',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -88,10 +89,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
categories: ['food', 'shopping'],
|
categories: ['food', 'shopping'],
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -110,6 +111,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
||||||
'@/app.config': fileURLToPath(new URL('./src/restaurant-app/app.config.ts', import.meta.url)),
|
'@/app.config': fileURLToPath(new URL('./src/restaurant-app/app.config.ts', import.meta.url)),
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
function tasksHtmlPlugin(): Plugin {
|
function tasksHtmlPlugin(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|
@ -45,6 +46,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
tasksHtmlPlugin(),
|
tasksHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -59,20 +61,19 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Tasks — Work Orders',
|
name: 'Tasks — Work Orders',
|
||||||
short_name: 'Tasks',
|
short_name: 'Tasks',
|
||||||
description: 'Decentralized task management on Nostr',
|
description: 'Decentralized task management on Nostr',
|
||||||
theme_color: '#4338ca',
|
theme_color: brand.themeColor ?? '#4338ca',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -81,10 +82,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
categories: ['productivity', 'business'],
|
categories: ['productivity', 'business'],
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -103,6 +104,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
||||||
'@/app.config': fileURLToPath(new URL('./src/tasks-app/app.config.ts', import.meta.url)),
|
'@/app.config': fileURLToPath(new URL('./src/tasks-app/app.config.ts', import.meta.url)),
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { defineConfig, type Plugin } from 'vite'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { brand, brandAlias, brandAssetsPlugin } from './vite-branding'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to rewrite dev server requests to wallet.html
|
* Plugin to rewrite dev server requests to wallet.html
|
||||||
|
|
@ -49,6 +50,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
brandAssetsPlugin(),
|
||||||
walletHtmlPlugin(),
|
walletHtmlPlugin(),
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
@ -65,20 +67,19 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
'favicon.ico',
|
'icons/favicon.ico',
|
||||||
'apple-touch-icon.png',
|
'icons/apple-touch-icon.png',
|
||||||
'mask-icon.svg',
|
'icons/icon-192.png',
|
||||||
'icon-192.png',
|
'icons/icon-512.png',
|
||||||
'icon-512.png',
|
'icons/icon-maskable-192.png',
|
||||||
'icon-maskable-192.png',
|
'icons/icon-maskable-512.png',
|
||||||
'icon-maskable-512.png',
|
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Wallet — Lightning',
|
name: 'Wallet — Lightning',
|
||||||
short_name: 'Wallet',
|
short_name: 'Wallet',
|
||||||
description: 'Lightning Network wallet — send, receive, and manage sats',
|
description: 'Lightning Network wallet — send, receive, and manage sats',
|
||||||
theme_color: '#eab308',
|
theme_color: brand.themeColor ?? '#eab308',
|
||||||
background_color: '#ffffff',
|
background_color: brand.backgroundColor ?? '#ffffff',
|
||||||
display: 'standalone',
|
display: 'standalone',
|
||||||
orientation: 'portrait-primary',
|
orientation: 'portrait-primary',
|
||||||
start_url: process.env.VITE_BASE_PATH || '/',
|
start_url: process.env.VITE_BASE_PATH || '/',
|
||||||
|
|
@ -87,10 +88,10 @@ export default defineConfig(({ mode }) => ({
|
||||||
categories: ['finance'],
|
categories: ['finance'],
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
icons: [
|
icons: [
|
||||||
{ src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
{ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
||||||
{ src: 'icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' },
|
||||||
{ src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
{ src: 'icons/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -109,6 +110,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
...brandAlias,
|
||||||
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
// ORDER MATTERS — @/app.config must precede @ (first-match-wins).
|
||||||
'@/app.config': fileURLToPath(new URL('./src/wallet-app/app.config.ts', import.meta.url)),
|
'@/app.config': fileURLToPath(new URL('./src/wallet-app/app.config.ts', import.meta.url)),
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/icons/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" sizes="180x180">
|
||||||
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
|
|
||||||
<title>Wallet — Lightning</title>
|
<title>Wallet — Lightning</title>
|
||||||
<meta name="apple-mobile-web-app-title" content="Wallet">
|
<meta name="apple-mobile-web-app-title" content="Wallet">
|
||||||
<meta name="description" content="Lightning Network wallet — send, receive, and manage sats">
|
<meta name="description" content="Lightning Network wallet — send, receive, and manage sats">
|
||||||
|
|
|
||||||