@webjsdev/cli 0.10.67 → 0.10.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/app-icon.js +55 -0
- package/lib/create.js +14 -9
- package/lib/doctor/codes.js +1 -0
- package/lib/doctor/probes/app-icon.js +36 -0
- package/lib/doctor/runner.js +2 -0
- package/lib/gallery-shell-files.js +1 -1
- package/package.json +2 -2
- package/templates/.agents/skills/webjs/SKILL.md +2 -1
- package/templates/.agents/skills/webjs/references/built-ins.md +2 -0
- package/templates/.agents/skills/webjs/references/routing-and-pages.md +18 -4
- package/templates/gallery/app/icon.ts +8 -6
- package/templates/gallery/app/manifest.ts +4 -1
- package/templates/scripts/clear-gallery.mjs +3 -3
- package/templates/public/favicon.svg +0 -12
package/lib/app-icon.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app icon a new scaffold starts with, and how `webjs doctor` recognises
|
|
3
|
+
* an icon that was never replaced.
|
|
4
|
+
*
|
|
5
|
+
* A scaffold ships `app/icon.svg` (auto-linked as the favicon, served at
|
|
6
|
+
* /icon.svg and as the /favicon.ico fallback) and `app/manifest.webmanifest`
|
|
7
|
+
* (auto-linked as the web app manifest). The icon is a deliberately NEUTRAL
|
|
8
|
+
* placeholder: a grey tile with a dashed frame, so a tab strip shows at a
|
|
9
|
+
* glance that the app has no icon of its own yet, and no app ever ships
|
|
10
|
+
* looking like a WebJs demo. It carries `data-webjs-placeholder` so a check
|
|
11
|
+
* (webjs doctor, or any agent's own tester) can tell it apart from a real
|
|
12
|
+
* icon without comparing bytes.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** The attribute that marks the scaffold's placeholder icon. */
|
|
16
|
+
export const PLACEHOLDER_MARKER = 'data-webjs-placeholder';
|
|
17
|
+
|
|
18
|
+
/** The placeholder `app/icon.svg`. Replace it with the app's own mark. */
|
|
19
|
+
export const PLACEHOLDER_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" ${PLACEHOLDER_MARKER}="icon">
|
|
20
|
+
<!-- PLACEHOLDER app icon from \`webjs create\`. Replace this file with the
|
|
21
|
+
app's own icon: a simple symbol for what the app is, in its colours,
|
|
22
|
+
legible at 16px. Keep the file name (app/icon.svg) and the framework
|
|
23
|
+
links it, serves it and answers /favicon.ico with it. -->
|
|
24
|
+
<rect width="32" height="32" rx="8" fill="#d4d4d8"/>
|
|
25
|
+
<rect x="7" y="7" width="18" height="18" rx="3" fill="none" stroke="#71717a" stroke-width="2" stroke-dasharray="3 2.4"/>
|
|
26
|
+
</svg>
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The `app/manifest.webmanifest` a scaffold starts with: the app's name, the
|
|
31
|
+
* neutral colours of the scaffold palette, and the icon. Grow it with the app
|
|
32
|
+
* (its real theme colour, a 192 and 512 PNG for installability).
|
|
33
|
+
* @param {string} name the app's display name
|
|
34
|
+
*/
|
|
35
|
+
export function appManifest(name) {
|
|
36
|
+
return JSON.stringify({
|
|
37
|
+
name,
|
|
38
|
+
short_name: name,
|
|
39
|
+
start_url: '/',
|
|
40
|
+
display: 'standalone',
|
|
41
|
+
background_color: '#ffffff',
|
|
42
|
+
theme_color: '#ffffff',
|
|
43
|
+
icons: [{ src: '/icon.svg', sizes: 'any', type: 'image/svg+xml' }],
|
|
44
|
+
}, null, 2) + '\n';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether an SVG is the WebJs brand mark earlier scaffolds shipped as
|
|
49
|
+
* `public/favicon.svg` (a rounded square with the gallery's grey gradient).
|
|
50
|
+
* Apps made before the placeholder still serve it as their favicon.
|
|
51
|
+
* @param {string} svg
|
|
52
|
+
*/
|
|
53
|
+
export function isLegacyBrandFavicon(svg) {
|
|
54
|
+
return /aria-label="WebJs"/.test(svg) && /<linearGradient id="wj"/.test(svg);
|
|
55
|
+
}
|
package/lib/create.js
CHANGED
|
@@ -21,6 +21,7 @@ import { bunifyProse, bunifyDockerfile, bunifyCompose, bunifyCi, bunifyEnvExampl
|
|
|
21
21
|
import { postgresCompose, postgresCi } from './db-rewrite.js';
|
|
22
22
|
import { assertValidAppName, toDatabaseName } from './app-name.js';
|
|
23
23
|
import { isGalleryAppShellFile } from './gallery-shell-files.js';
|
|
24
|
+
import { PLACEHOLDER_ICON_SVG, appManifest } from './app-icon.js';
|
|
24
25
|
import { detectPackageManager } from './package-manager.js';
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -1246,10 +1247,15 @@ export type ActionResult<T> =
|
|
|
1246
1247
|
const swSrc = join(TEMPLATES, 'public', swFile);
|
|
1247
1248
|
if (existsSync(swSrc)) await cp(swSrc, join(publicDir, swFile));
|
|
1248
1249
|
}
|
|
1249
|
-
//
|
|
1250
|
-
// the
|
|
1251
|
-
|
|
1252
|
-
|
|
1250
|
+
// The app icon and web app manifest: `app/icon.svg` (a neutral PLACEHOLDER
|
|
1251
|
+
// the agent replaces with the app's own icon; `webjs doctor` warns while it
|
|
1252
|
+
// is still there) and `app/manifest.webmanifest` (the app's name). The
|
|
1253
|
+
// framework links both into <head> and answers /favicon.ico with the icon,
|
|
1254
|
+
// so the layout declares nothing. They ship with the app, not the gallery,
|
|
1255
|
+
// so they survive `gallery:clear`.
|
|
1256
|
+
await mkdir(join(appDir, 'app'), { recursive: true });
|
|
1257
|
+
await writeFile(join(appDir, 'app', 'icon.svg'), PLACEHOLDER_ICON_SVG);
|
|
1258
|
+
await writeFile(join(appDir, 'app', 'manifest.webmanifest'), appManifest(displayName));
|
|
1253
1259
|
|
|
1254
1260
|
// The gallery-reset script (wired as `gallery:clear`). Only UI templates have
|
|
1255
1261
|
// a gallery, so it ships here (NOT in the flat templateFiles list, which would
|
|
@@ -1355,11 +1361,10 @@ import '#components/theme-toggle.ts';
|
|
|
1355
1361
|
* text-foreground, bg-card, bg-primary, and border-border all work.
|
|
1356
1362
|
*/
|
|
1357
1363
|
|
|
1358
|
-
//
|
|
1359
|
-
//
|
|
1360
|
-
//
|
|
1361
|
-
//
|
|
1362
|
-
export const metadata = { icons: '/public/favicon.svg' };
|
|
1364
|
+
// The favicon and manifest are app/icon.svg and app/manifest.webmanifest: the
|
|
1365
|
+
// framework links them into <head> itself, so nothing is declared here. Replace
|
|
1366
|
+
// the placeholder icon with this app's own (see the skill's routing-and-pages
|
|
1367
|
+
// reference, "App icon").
|
|
1363
1368
|
|
|
1364
1369
|
// LayoutProps types every layout argument (children, params, searchParams,
|
|
1365
1370
|
// url) from the framework, so children is a TemplateResult rather than an
|
package/lib/doctor/codes.js
CHANGED
|
@@ -50,6 +50,7 @@ export const DOCTOR_CODES = {
|
|
|
50
50
|
'Static build outputs (dev.regenerate freshness)': 'STATIC_ASSET_FRESHNESS',
|
|
51
51
|
'Asset urls (unmarked stylesheet links)': 'UNMARKED_ASSET_LINKS',
|
|
52
52
|
'workspace-overrides': 'WORKSPACE_OVERRIDES',
|
|
53
|
+
'app-icon': 'APP_ICON',
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
/**
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { PLACEHOLDER_MARKER, isLegacyBrandFavicon } from '../../app-icon.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {import('../codes.js').DoctorResult} DoctorResult
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** @param {string} file */
|
|
10
|
+
function read(file) {
|
|
11
|
+
try { return readFileSync(file, 'utf8'); } catch { return ''; }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Warn when the app's favicon is still the one `webjs create` shipped: the
|
|
16
|
+
* neutral `app/icon.svg` placeholder, or the WebJs brand mark earlier
|
|
17
|
+
* scaffolds put at `public/favicon.svg`. Either way every app made from the
|
|
18
|
+
* scaffold shows the same tab icon, which reads as a demo rather than a
|
|
19
|
+
* product. An app with no `app/` directory (a library, the api template with
|
|
20
|
+
* no pages) passes.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} appDir
|
|
23
|
+
* @returns {DoctorResult}
|
|
24
|
+
*/
|
|
25
|
+
export function checkAppIcon(appDir) {
|
|
26
|
+
const name = 'app-icon';
|
|
27
|
+
if (!existsSync(join(appDir, 'app'))) return { name, status: 'pass', message: 'no app/ directory to analyse' };
|
|
28
|
+
const fix = 'Replace app/icon.svg with an icon for this app (a simple symbol for what it is, in its own colours, legible at 16px), and delete public/favicon.svg plus any metadata.icons that still points at it.';
|
|
29
|
+
if (read(join(appDir, 'app', 'icon.svg')).includes(PLACEHOLDER_MARKER)) {
|
|
30
|
+
return { name, status: 'warn', message: 'app/icon.svg is still the scaffold placeholder icon', fix };
|
|
31
|
+
}
|
|
32
|
+
if (isLegacyBrandFavicon(read(join(appDir, 'public', 'favicon.svg')))) {
|
|
33
|
+
return { name, status: 'warn', message: 'public/favicon.svg is still the WebJs mark an earlier scaffold shipped', fix };
|
|
34
|
+
}
|
|
35
|
+
return { name, status: 'pass', message: 'the app has its own icon (or declares none)' };
|
|
36
|
+
}
|
package/lib/doctor/runner.js
CHANGED
|
@@ -12,6 +12,7 @@ import { checkStaticAssetFreshness } from './probes/static-asset-freshness.js';
|
|
|
12
12
|
import { checkUnmarkedAssetLinks } from './probes/unmarked-asset-links.js';
|
|
13
13
|
import { checkFrameworkResolves, checkFrameworkLinks } from './probes/framework-resolves.js';
|
|
14
14
|
import { checkWorkspaceOverrides } from './probes/workspace-overrides.js';
|
|
15
|
+
import { checkAppIcon } from './probes/app-icon.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* @typedef {import('./codes.js').DoctorResult} DoctorResult
|
|
@@ -66,6 +67,7 @@ export async function runDoctorChecks(appDir, opts = {}) {
|
|
|
66
67
|
checkStaticAssetFreshness(appDir),
|
|
67
68
|
checkUnmarkedAssetLinks(appDir),
|
|
68
69
|
Promise.resolve(checkWorkspaceOverrides(appDir)),
|
|
70
|
+
Promise.resolve(checkAppIcon(appDir)),
|
|
69
71
|
]);
|
|
70
72
|
// Attach the stable machine code to every result (#975). Centralized here so
|
|
71
73
|
// each check function stays free of the code-contract concern.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* live app deployed on its own, so it needs a root layout, a home page, a theme
|
|
7
7
|
* toggle, and the `cn()` helper. The scaffold writes all four itself, with
|
|
8
8
|
* things the gallery's copies cannot carry: the app's `displayName`, the
|
|
9
|
-
* `cspNonce()` wiring, `LayoutProps` typing,
|
|
9
|
+
* `cspNonce()` wiring, `LayoutProps` typing, and
|
|
10
10
|
* a `cn.ts` read verbatim from the `@webjsdev/ui` registry so `webjs ui add`
|
|
11
11
|
* stays in lockstep with the kit.
|
|
12
12
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjsdev/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.68",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The CLI for WebJs, a full-stack JavaScript framework built on web components with server-side rendering and no build step. Runs the dev and production servers, scaffolds apps, validates conventions, and drives the database. Node 24+ or Bun.",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@webjsdev/mcp": "^0.1.0",
|
|
21
|
-
"@webjsdev/server": "^0.8.
|
|
21
|
+
"@webjsdev/server": "^0.8.86",
|
|
22
22
|
"@webjsdev/ui": "^0.3.15"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
@@ -44,6 +44,7 @@ Rows point rather than explain. The reference is the authority on the rule, and
|
|
|
44
44
|
| add a URL, static or with a dynamic segment | a file at `app/<path>/page.ts`, `[id]` for a param | registering the route in a table or config | `references/routing-and-pages.md` | `app/features/routing` |
|
|
45
45
|
| abandon a render because something is missing or not allowed | throw `notFound()` / `forbidden()` / `unauthorized()` | returning an error object and branching in the template | `references/routing-and-pages.md` | `app/features/boundaries` |
|
|
46
46
|
| set a page's title, description, or social preview | `export const metadata` or `generateMetadata()` | writing `<head>` tags in the page | `references/routing-and-pages.md` | `app/features/metadata` |
|
|
47
|
+
| give the app its own favicon, home-screen icon and manifest | replace the placeholder `app/icon.svg` with a simple symbol for the app in its colours, add `app/apple-icon.png`, edit `app/manifest.webmanifest` | leaving the scaffold placeholder, or a hand-written `<link rel="icon">` | `references/routing-and-pages.md` (App icon and manifest) | `app/icon.ts` |
|
|
47
48
|
| make part of the page respond to a click or hold state | a `WebComponent` custom element | expecting the page's own markup to hydrate | `references/components.md` | `app/features/components` |
|
|
48
49
|
| render a keyed list, or swap one node when state changes | `repeat()` / `watch()` from `/directives` | re-rendering the component or diffing by hand | `references/components.md` | `app/features/directives` |
|
|
49
50
|
| get server data into a component's first paint | `async render()` awaiting an action | fetching in `connectedCallback`, which SSR never calls | `references/components.md` | `app/features/async-render` |
|
|
@@ -174,7 +175,7 @@ Find the right export fast. Load the linked reference for full examples.
|
|
|
174
175
|
|
|
175
176
|
### File conventions
|
|
176
177
|
|
|
177
|
-
`page.ts` (server-only fn), `layout.ts` (embeds `children`), `route.ts` (HTTP handler), `middleware.ts`, `*.server.ts` (server boundary), `error.ts` / `loading.ts` / `not-found.ts` / `forbidden.ts` / `unauthorized.ts` (boundaries), metadata routes (`sitemap.ts`, `robots.ts`, `manifest.ts`, `icon.ts`, `opengraph-image.ts`).
|
|
178
|
+
`page.ts` (server-only fn), `layout.ts` (embeds `children`), `route.ts` (HTTP handler), `middleware.ts`, `*.server.ts` (server boundary), `error.ts` / `loading.ts` / `not-found.ts` / `forbidden.ts` / `unauthorized.ts` (boundaries), metadata routes (`sitemap.ts`, `robots.ts`, `manifest.ts`, `icon.ts`, `opengraph-image.ts`), and the static app-root icon files (`icon.svg`, `apple-icon.png`, `manifest.webmanifest`, `favicon.ico`), auto-linked into `<head>`. The scaffold's `app/icon.svg` is a placeholder: replace it with the app's own icon.
|
|
178
179
|
|
|
179
180
|
## Canonical Patterns
|
|
180
181
|
|
|
@@ -296,6 +296,8 @@ Three levels, the same scale ESLint uses: `error` fails the exit, `warn` reports
|
|
|
296
296
|
|
|
297
297
|
Two guarantees worth knowing. A result that could not check (a network or toolchain outage) is capped at `warn` and can never be escalated, so a jspm or npm outage cannot red your CI. And a malformed gate exits 1 naming the offender rather than being ignored, so a typo cannot silently un-gate the build. That covers an unknown code, a bad severity, a wrong shape (a non-object `doctor` or `gate`), and a misspelled sibling of `gate` such as `gates`, since every one of those would otherwise leave the build un-gated while the `package.json` looks gated. Under `--json` the offenders come back as a `configErrors` array alongside an empty `results`, each entry a `{ kind }` of `malformed` / `unknown-key` / `unknown-code` / `bad-severity`. Wire it up with one workflow step, `npm run doctor`, and change what is fatal in `package.json` rather than in the workflow.
|
|
298
298
|
|
|
299
|
+
`APP_ICON` warns while the favicon is still the scaffold's: the placeholder `app/icon.svg` (marked `data-webjs-placeholder`) or the WebJs mark older scaffolds shipped at `public/favicon.svg`. Replace it with the app's own icon (`references/routing-and-pages.md`, "App icon and manifest").
|
|
300
|
+
|
|
299
301
|
### Dependency audit allowlist
|
|
300
302
|
|
|
301
303
|
`webjs audit` runs `npm audit` or `bun audit` (by the nearest lockfile, so a workspace member uses the root's) and fails on any advisory at or above `webjs.audit.level` (default `high`) that `webjs.audit.ignore` does not list. The scaffold's `Security: dependency audit` CI step runs it.
|
|
@@ -278,12 +278,26 @@ export default function robots({ siteUrl }: MetadataRouteContext) {
|
|
|
278
278
|
|
|
279
279
|
The IMAGE metadata routes (`icon`, `apple-icon`, `opengraph-image`, `twitter-image`) default-export a function returning a `Response` with an explicit `content-type`, so an inline SVG needs no asset file (buildless).
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
### App icon and manifest (replace the placeholder)
|
|
282
282
|
|
|
283
|
-
|
|
283
|
+
**Every app needs its OWN icon.** `webjs create` ships `app/icon.svg` as a neutral PLACEHOLDER (a grey tile with a dashed frame, marked `data-webjs-placeholder="icon"`) and `app/manifest.webmanifest` with the app's name. Replacing the placeholder is part of building the app, not polish: until you do, the tab, the bookmark and the home-screen icon look like every other unfinished app, and `webjs doctor` warns (`APP_ICON`). Draw a simple symbol for what the app IS (a grid for a tic-tac-toe game, a cup for a cafe, a check for a task list), in the app's own colours, on a 32x32 or 24x24 `viewBox`: a filled rounded tile in the primary colour with one bold shape in its foreground colour reads at 16px. Avoid thin strokes (under 2px at 32px), text longer than one letter, and detail that blurs at tab size. Then set `name`, `short_name`, `theme_color` and `background_color` in `app/manifest.webmanifest` to match.
|
|
284
|
+
|
|
285
|
+
The icon conventions, all at the app ROOT and all auto-linked into `<head>` when the app declares no `metadata.icons`:
|
|
286
|
+
|
|
287
|
+
| File | Served at | Linked as |
|
|
288
|
+
|---|---|---|
|
|
289
|
+
| `app/icon.svg` / `icon.png` / `icon.ico` | `/icon.svg` ... | `<link rel="icon">` with `type`, `sizes="any"` (SVG) or the PNG's real pixel size |
|
|
290
|
+
| `app/apple-icon.png` (180x180) | `/apple-icon.png` | `<link rel="apple-touch-icon" sizes="180x180">` (iOS needs PNG, not SVG) |
|
|
291
|
+
| `app/icon.ts` / `apple-icon.ts` | `/icon`, `/apple-icon` | bare link (the route picks its content type per request) |
|
|
292
|
+
| `app/manifest.webmanifest` / `manifest.json` / `manifest.ts` | `/manifest.webmanifest`, `/manifest.json` | `<link rel="manifest">` (`metadata.manifest` wins; `manifest: null` opts out) |
|
|
293
|
+
| `app/favicon.ico` | `/favicon.ico` | not linked; browsers request it themselves |
|
|
294
|
+
|
|
295
|
+
`/favicon.ico` always answers: `public/favicon.ico`, else `app/favicon.ico`, else the app's icon (raster preferred over SVG, then the `icon.ts` route), so a crawler or feed reader that reads no markup gets the same icon. A static icon file wins the link over an icon route when both exist (the route still serves at its URL). Raster icons are linked before SVG, because Google's favicon crawler takes the first usable icon and wants a square raster. Use `icon.ts` only when the mark must be computed per request (per theme, per tenant); a route can render a PNG for `apple-icon.ts` the same way.
|
|
296
|
+
|
|
297
|
+
Declaring `metadata.icons` **suppresses** all of the above rather than merging with them, which is what Next does with its static icon files. So name icons explicitly only when they live elsewhere (a CDN, `public/`):
|
|
284
298
|
|
|
285
299
|
```ts
|
|
286
|
-
// app/layout.ts -> these win; /icon and /apple-icon are no longer linked
|
|
300
|
+
// app/layout.ts -> these win; app/icon.* and app/apple-icon.* are no longer linked
|
|
287
301
|
export const metadata = {
|
|
288
302
|
icons: {
|
|
289
303
|
icon: [
|
|
@@ -295,7 +309,7 @@ export const metadata = {
|
|
|
295
309
|
};
|
|
296
310
|
```
|
|
297
311
|
|
|
298
|
-
|
|
312
|
+
Never write a favicon as a hand-written `<link rel="icon">`: only the root layout may write a shell at all (invariant 8), so a hand-written tag is unavailable to every other layout.
|
|
299
313
|
|
|
300
314
|
`opengraph-image` and `twitter-image` are LINKED too, Next's behaviour: a page that declares no `openGraph.images` gets `og:image` pointing at the nearest `opengraph-image` route above it (absolute against the site URL), and likewise `twitter:image`. A page that declares its own image keeps it.
|
|
301
315
|
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
// content type, so an inline SVG needs no asset file. Generate it dynamically
|
|
4
4
|
// (per-theme, per-tenant) when the mark must be computed at request time.
|
|
5
5
|
//
|
|
6
|
-
// This is the DEMO of that surface, not the gallery's own favicon.
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
6
|
+
// This is the DEMO of that surface, not the gallery's own favicon. With no
|
|
7
|
+
// metadata.icons declared, the framework auto-links an app-root icon: a STATIC
|
|
8
|
+
// file (app/icon.svg, app/icon.png) wins that link over this route, and a
|
|
9
|
+
// declared metadata.icons wins over both. The gallery declares its WebJs brand
|
|
10
|
+
// mark in app/layout.ts, so this route stays browsable at /icon without being
|
|
11
|
+
// the tab icon. For an icon that never changes, write app/icon.svg instead
|
|
12
|
+
// (what `webjs create` ships); keep a route like this only when the mark must
|
|
13
|
+
// be computed at request time (per theme, per tenant).
|
|
12
14
|
export default function Icon() {
|
|
13
15
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
14
16
|
<rect width="32" height="32" rx="7" fill="#1e2226"/>
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
// icons to your app; pair it with the opt-in service worker for an installable
|
|
4
4
|
// PWA. See agent-docs/service-worker.md. (Gallery files are copied verbatim, so
|
|
5
5
|
// set the real app name here by hand rather than expecting substitution.)
|
|
6
|
+
// The framework links an app-root manifest into <head> by itself. A static
|
|
7
|
+
// app/manifest.webmanifest (what `webjs create` ships) wins that link over this
|
|
8
|
+
// route; write a route like this only when a value must be computed.
|
|
6
9
|
export default function Manifest() {
|
|
7
10
|
return {
|
|
8
11
|
name: 'webjs app',
|
|
@@ -12,7 +15,7 @@ export default function Manifest() {
|
|
|
12
15
|
background_color: '#ffffff',
|
|
13
16
|
theme_color: '#1e2226',
|
|
14
17
|
icons: [
|
|
15
|
-
{ src: '/
|
|
18
|
+
{ src: '/icon', sizes: 'any', type: 'image/svg+xml' },
|
|
16
19
|
],
|
|
17
20
|
};
|
|
18
21
|
}
|
|
@@ -191,9 +191,9 @@ import type { LayoutProps } from '@webjsdev/core';
|
|
|
191
191
|
* to pull primitives, then theme them here.
|
|
192
192
|
*/
|
|
193
193
|
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
|
|
194
|
+
// The favicon is app/icon.svg (and the manifest app/manifest.webmanifest): the
|
|
195
|
+
// framework links both into <head>, so nothing is declared here. Replace the
|
|
196
|
+
// placeholder icon with this app's own.
|
|
197
197
|
|
|
198
198
|
export default function RootLayout({ children }: LayoutProps) {
|
|
199
199
|
return html\`
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" role="img" aria-label="WebJs">
|
|
2
|
-
<!-- Matches the gallery navbar brand mark (a rounded square with a
|
|
3
|
-
foreground -> muted-foreground gradient), using the DARK-theme token
|
|
4
|
-
values (#dee2e6 -> #94989c) so it reads on a dark browser tab bar. -->
|
|
5
|
-
<defs>
|
|
6
|
-
<linearGradient id="wj" x1="0" y1="0" x2="1" y2="1">
|
|
7
|
-
<stop offset="0" stop-color="#dee2e6"/>
|
|
8
|
-
<stop offset="1" stop-color="#94989c"/>
|
|
9
|
-
</linearGradient>
|
|
10
|
-
</defs>
|
|
11
|
-
<rect width="32" height="32" rx="10" fill="url(#wj)"/>
|
|
12
|
-
</svg>
|