react-cheminfo 0.20.0 → 0.22.0
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/README.md +1 -1
- package/lib/about/core/about.d.ts +3 -3
- package/lib/about/core/about.d.ts.map +1 -1
- package/lib/build/vite/gitHead.d.ts.map +1 -1
- package/lib/build/vite/gitHead.js +17 -2
- package/lib/build/vite/gitHead.js.map +1 -1
- package/lib/chrome/ui/SiteHeader.d.ts +20 -3
- package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
- package/lib/chrome/ui/SiteHeader.js +7 -3
- package/lib/chrome/ui/SiteHeader.js.map +1 -1
- package/lib/clipboard/ui/ClickToCopy.d.ts +69 -0
- package/lib/clipboard/ui/ClickToCopy.d.ts.map +1 -0
- package/lib/clipboard/ui/ClickToCopy.js +66 -0
- package/lib/clipboard/ui/ClickToCopy.js.map +1 -0
- package/lib/clipboard/ui/CopyButton.d.ts +1 -1
- package/lib/clipboard/ui/CopyButton.js +1 -1
- package/lib/clipboard/ui/CopyableValue.d.ts +3 -3
- package/lib/clipboard/ui/CopyableValue.d.ts.map +1 -1
- package/lib/clipboard/ui/CopyableValue.js +5 -5
- package/lib/clipboard/ui/CopyableValue.js.map +1 -1
- package/lib/clipboard/ui/index.d.ts +2 -0
- package/lib/clipboard/ui/index.d.ts.map +1 -1
- package/lib/clipboard/ui/index.js +1 -0
- package/lib/clipboard/ui/index.js.map +1 -1
- package/lib/ecosystem/core/groups.d.ts +4 -3
- package/lib/ecosystem/core/groups.d.ts.map +1 -1
- package/lib/ecosystem/core/groups.js +8 -2
- package/lib/ecosystem/core/groups.js.map +1 -1
- package/lib/ecosystem/core/index.d.ts +1 -1
- package/lib/ecosystem/core/index.d.ts.map +1 -1
- package/lib/ecosystem/core/index.js.map +1 -1
- package/lib/ecosystem/core/lookup.d.ts +2 -2
- package/lib/ecosystem/core/lookup.d.ts.map +1 -1
- package/lib/ecosystem/core/lookup.js.map +1 -1
- package/lib/ecosystem/core/nameColors.d.ts +2 -2
- package/lib/ecosystem/core/nameColors.d.ts.map +1 -1
- package/lib/ecosystem/core/nameColors.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts +3 -3
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +39 -0
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/core/tokens.d.ts +4 -3
- package/lib/ecosystem/core/tokens.d.ts.map +1 -1
- package/lib/ecosystem/core/tokens.js +8 -7
- package/lib/ecosystem/core/tokens.js.map +1 -1
- package/lib/ecosystem/core/types.d.ts +19 -10
- package/lib/ecosystem/core/types.d.ts.map +1 -1
- package/lib/ecosystem/ui/SiteTheme.d.ts +14 -3
- package/lib/ecosystem/ui/SiteTheme.d.ts.map +1 -1
- package/lib/ecosystem/ui/SiteTheme.js +6 -1
- package/lib/ecosystem/ui/SiteTheme.js.map +1 -1
- package/lib/ecosystem/ui/Wordmark.d.ts +2 -2
- package/lib/ecosystem/ui/Wordmark.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +9 -0
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/ecosystem/ui/marks.d.ts +13 -4
- package/lib/ecosystem/ui/marks.d.ts.map +1 -1
- package/lib/ecosystem/ui/marks.js +11 -3
- package/lib/ecosystem/ui/marks.js.map +1 -1
- package/lib/error/ui/ErrorFallback.d.ts.map +1 -1
- package/lib/error/ui/ErrorFallback.js +1 -1
- package/lib/error/ui/ErrorFallback.js.map +1 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.d.ts.map +1 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.js +2 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.js.map +1 -1
- package/lib/seo/core/pageMeta.d.ts +2 -2
- package/lib/seo/core/pageMeta.d.ts.map +1 -1
- package/lib/seo/core/siteFiles.d.ts +3 -3
- package/lib/seo/core/siteFiles.d.ts.map +1 -1
- package/lib/seo/core/siteFiles.js.map +1 -1
- package/lib/seo/vite/ogCard.d.ts +9 -2
- package/lib/seo/vite/ogCard.d.ts.map +1 -1
- package/lib/seo/vite/ogCard.js +6 -1
- package/lib/seo/vite/ogCard.js.map +1 -1
- package/lib/seo/vite/prerender.d.ts +2 -2
- package/lib/seo/vite/prerender.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/about/core/about.ts +3 -3
- package/src/build/vite/gitHead.ts +17 -2
- package/src/chrome/ui/SiteHeader.tsx +28 -6
- package/src/clipboard/ui/ClickToCopy.tsx +190 -0
- package/src/clipboard/ui/CopyButton.tsx +2 -2
- package/src/clipboard/ui/CopyableValue.tsx +11 -6
- package/src/clipboard/ui/index.ts +2 -0
- package/src/ecosystem/core/groups.ts +10 -3
- package/src/ecosystem/core/index.ts +1 -0
- package/src/ecosystem/core/lookup.ts +2 -2
- package/src/ecosystem/core/nameColors.ts +2 -2
- package/src/ecosystem/core/sites.ts +42 -2
- package/src/ecosystem/core/tokens.ts +10 -9
- package/src/ecosystem/core/types.ts +23 -10
- package/src/ecosystem/ui/SiteTheme.tsx +19 -4
- package/src/ecosystem/ui/Wordmark.tsx +2 -2
- package/src/ecosystem/ui/glyphs.tsx +51 -0
- package/src/ecosystem/ui/marks.tsx +23 -5
- package/src/error/ui/ErrorFallback.tsx +3 -1
- package/src/pedagogy/ui/ReferenceSectionBlock.tsx +10 -1
- package/src/seo/core/noscript.ts +1 -1
- package/src/seo/core/pageMeta.ts +2 -2
- package/src/seo/core/siteFiles.ts +3 -3
- package/src/seo/vite/ogCard.ts +16 -3
- package/src/seo/vite/prerender.ts +2 -2
- package/styles/chrome.css +163 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// tokens-ok: file — every site's two colours are declared here.
|
|
2
|
-
import type { EcosystemSite } from './types.ts';
|
|
2
|
+
import type { EcosystemSite, SiteRecord } from './types.ts';
|
|
3
3
|
|
|
4
4
|
export type {
|
|
5
5
|
EcosystemSite,
|
|
6
6
|
SiteId,
|
|
7
7
|
SiteMarkColors,
|
|
8
8
|
SiteName,
|
|
9
|
+
SiteRecord,
|
|
9
10
|
} from './types.ts';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -288,6 +289,45 @@ export const ECOSYSTEM_SITES: readonly EcosystemSite[] = [
|
|
|
288
289
|
brandAlt: '#1d4ed8',
|
|
289
290
|
mark: { plate: '#854d0e', accent: '#60a5fa' },
|
|
290
291
|
},
|
|
292
|
+
{
|
|
293
|
+
id: 'atoms',
|
|
294
|
+
name: { lead: 'atoms', alt: 'cheminfo', dot: true },
|
|
295
|
+
host: 'atoms.cheminfo.org',
|
|
296
|
+
repository: 'https://github.com/cheminfo/atoms.cheminfo.org',
|
|
297
|
+
tagline: 'Electrons, neutrons, Lewis structures and oxidation states.',
|
|
298
|
+
group: 'practice',
|
|
299
|
+
brand: '#b91c1c',
|
|
300
|
+
// The sky the mark's electron is drawn in sits at 2.1:1 on white, so the
|
|
301
|
+
// second half of the wordmark takes this darkened form instead.
|
|
302
|
+
brandAlt: '#0369a1',
|
|
303
|
+
mark: { plate: '#b91c1c', accent: '#38bdf8' },
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
id: 'moles',
|
|
307
|
+
name: { lead: 'moles', alt: 'cheminfo', dot: true },
|
|
308
|
+
host: 'moles.cheminfo.org',
|
|
309
|
+
repository: 'https://github.com/cheminfo/moles.cheminfo.org',
|
|
310
|
+
tagline: 'Balance a reaction, and work out its mass composition.',
|
|
311
|
+
group: 'practice',
|
|
312
|
+
brand: '#334155',
|
|
313
|
+
// The amber the mark's loaded pan is drawn in sits at 2.2:1 on white, so
|
|
314
|
+
// the second half of the wordmark takes this darkened form instead.
|
|
315
|
+
brandAlt: '#b45309',
|
|
316
|
+
mark: { plate: '#334155', accent: '#f59e0b' },
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: 'inorganic',
|
|
320
|
+
name: { lead: 'inorganic', alt: 'cheminfo', dot: true },
|
|
321
|
+
host: 'inorganic.cheminfo.org',
|
|
322
|
+
repository: 'https://github.com/cheminfo/inorganic.cheminfo.org',
|
|
323
|
+
tagline: 'Name an inorganic compound, and write its formula.',
|
|
324
|
+
group: 'practice',
|
|
325
|
+
brand: '#a3620a',
|
|
326
|
+
// The cyan the mark's named ion is drawn in sits at 1.8:1 on white, so the
|
|
327
|
+
// second half of the wordmark takes this darkened form instead.
|
|
328
|
+
brandAlt: '#0e7490',
|
|
329
|
+
mark: { plate: '#a3620a', accent: '#22d3ee' },
|
|
330
|
+
},
|
|
291
331
|
];
|
|
292
332
|
|
|
293
333
|
/**
|
|
@@ -295,6 +335,6 @@ export const ECOSYSTEM_SITES: readonly EcosystemSite[] = [
|
|
|
295
335
|
* @param site - The site being linked to.
|
|
296
336
|
* @returns The URL to open.
|
|
297
337
|
*/
|
|
298
|
-
export function siteUrl(site:
|
|
338
|
+
export function siteUrl(site: SiteRecord): string {
|
|
299
339
|
return `https://${site.host}/`;
|
|
300
340
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// tokens-ok: file — the block a site renders is written here.
|
|
2
2
|
import { siteById } from './lookup.ts';
|
|
3
|
-
import type {
|
|
3
|
+
import type { SiteId, SiteRecord } from './sites.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The custom properties a site of the family sets on `:root`: the two colours
|
|
@@ -9,21 +9,22 @@ import type { EcosystemSite, SiteId } from './sites.ts';
|
|
|
9
9
|
*
|
|
10
10
|
* It is a complete rule rather than a list of declarations, so it drops into a
|
|
11
11
|
* `<style>` of a prerendered page as it is.
|
|
12
|
-
* @param
|
|
12
|
+
* @param site - The site whose palette is wanted: its id, or the record of a
|
|
13
|
+
* site that is not one of the family.
|
|
13
14
|
* @returns The `:root` rule, ending in a newline.
|
|
14
15
|
*/
|
|
15
|
-
export function siteTokensCss(
|
|
16
|
-
const
|
|
17
|
-
const answering = answeringColor(
|
|
16
|
+
export function siteTokensCss(site: SiteId | SiteRecord): string {
|
|
17
|
+
const record = typeof site === 'string' ? siteById(site) : site;
|
|
18
|
+
const answering = answeringColor(record);
|
|
18
19
|
const declarations = [
|
|
19
|
-
`--brand: ${
|
|
20
|
+
`--brand: ${record.brand};`,
|
|
20
21
|
`--brand-alt: ${answering};`,
|
|
21
22
|
];
|
|
22
23
|
|
|
23
24
|
// The answering colour of several sites is a yellow or an amber that sits
|
|
24
25
|
// around 2:1 on white, so text set in it needs the darkened form instead.
|
|
25
|
-
if (
|
|
26
|
-
declarations.push(`--brand-alt-text: ${
|
|
26
|
+
if (record.brandAlt !== answering) {
|
|
27
|
+
declarations.push(`--brand-alt-text: ${record.brandAlt};`);
|
|
27
28
|
}
|
|
28
29
|
declarations.push('--accent: var(--brand);');
|
|
29
30
|
|
|
@@ -32,6 +33,6 @@ export function siteTokensCss(id: SiteId): string {
|
|
|
32
33
|
|
|
33
34
|
// The second colour as the site's mark draws it: on the accent element, unless
|
|
34
35
|
// the mark inverts the pair and gives the plate the answering colour instead.
|
|
35
|
-
function answeringColor(site:
|
|
36
|
+
function answeringColor(site: SiteRecord): string {
|
|
36
37
|
return site.mark.accent === site.brand ? site.mark.plate : site.mark.accent;
|
|
37
38
|
}
|
|
@@ -24,7 +24,10 @@ export type SiteId =
|
|
|
24
24
|
| 'periodic-table'
|
|
25
25
|
| 'database'
|
|
26
26
|
| 'symmetry'
|
|
27
|
-
| 'osiris'
|
|
27
|
+
| 'osiris'
|
|
28
|
+
| 'atoms'
|
|
29
|
+
| 'moles'
|
|
30
|
+
| 'inorganic';
|
|
28
31
|
|
|
29
32
|
/** A site's name, split the way its own wordmark splits it. */
|
|
30
33
|
export interface SiteName {
|
|
@@ -50,10 +53,14 @@ export interface SiteName {
|
|
|
50
53
|
ink?: 'lead' | 'alt';
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* What the chrome draws a site from: its name, address and two colours. A site
|
|
58
|
+
* that is deliberately not one of the family writes its own, and passes it
|
|
59
|
+
* wherever a family site passes its id.
|
|
60
|
+
*/
|
|
61
|
+
export interface SiteRecord {
|
|
62
|
+
/** The identifier the site is named by. */
|
|
63
|
+
id: string;
|
|
57
64
|
/** The name, split the way the site's own wordmark splits it. */
|
|
58
65
|
name: SiteName;
|
|
59
66
|
/** Where the site lives, written the way it is typed. */
|
|
@@ -70,11 +77,6 @@ export interface EcosystemSite {
|
|
|
70
77
|
publicRepository?: boolean;
|
|
71
78
|
/** One line on what the site does. */
|
|
72
79
|
tagline: string;
|
|
73
|
-
/**
|
|
74
|
-
* The topic the site is written under, which is what decides where it sits
|
|
75
|
-
* in the menu and in the footer.
|
|
76
|
-
*/
|
|
77
|
-
group: SiteGroupId;
|
|
78
80
|
/**
|
|
79
81
|
* The colour the name is written in — the first half, or the second when
|
|
80
82
|
* `name.ink` leaves the first in the family's ink.
|
|
@@ -90,6 +92,17 @@ export interface EcosystemSite {
|
|
|
90
92
|
mark: SiteMarkColors;
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
/** One site of the family: its record, and the topic it is listed under. */
|
|
96
|
+
export interface EcosystemSite extends SiteRecord {
|
|
97
|
+
/** The identifier every component and helper names the site by. */
|
|
98
|
+
id: SiteId;
|
|
99
|
+
/**
|
|
100
|
+
* The topic the site is written under, which is what decides where it sits
|
|
101
|
+
* in the menu and in the footer.
|
|
102
|
+
*/
|
|
103
|
+
group: SiteGroupId;
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
/** The colours a site's mark is drawn in. */
|
|
94
107
|
export interface SiteMarkColors {
|
|
95
108
|
/** The rounded square the mark is drawn on. */
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
|
|
3
|
-
import type { SiteId } from '../core/sites.ts';
|
|
3
|
+
import type { SiteId, SiteRecord } from '../core/sites.ts';
|
|
4
4
|
import { siteTokensCss } from '../core/tokens.ts';
|
|
5
5
|
|
|
6
6
|
/** What the site palette needs. */
|
|
7
7
|
export interface SiteThemeProps {
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The site whose palette the page takes, named. One of `site` and `siteId`
|
|
10
|
+
* is required.
|
|
11
|
+
* @default undefined
|
|
12
|
+
*/
|
|
13
|
+
siteId?: SiteId;
|
|
14
|
+
/**
|
|
15
|
+
* The same site, passed rather than named — for a site that is deliberately
|
|
16
|
+
* not one of `ECOSYSTEM_SITES`.
|
|
17
|
+
* @default undefined
|
|
18
|
+
*/
|
|
19
|
+
site?: SiteRecord;
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
/**
|
|
@@ -18,7 +28,12 @@ export interface SiteThemeProps {
|
|
|
18
28
|
* in a component.
|
|
19
29
|
* @param props - The site whose palette is injected.
|
|
20
30
|
* @returns The rule, as a style element that applies wherever it is rendered.
|
|
31
|
+
* @throws {Error} When neither `site` nor `siteId` is given.
|
|
21
32
|
*/
|
|
22
33
|
export function SiteTheme(props: SiteThemeProps): ReactElement {
|
|
23
|
-
|
|
34
|
+
const site = props.site ?? props.siteId;
|
|
35
|
+
if (site === undefined) {
|
|
36
|
+
throw new Error('SiteTheme needs one of its `site` and `siteId` props');
|
|
37
|
+
}
|
|
38
|
+
return <style>{siteTokensCss(site)}</style>;
|
|
24
39
|
}
|
|
@@ -3,7 +3,7 @@ import type { CSSProperties, ReactElement } from 'react';
|
|
|
3
3
|
import { joinClassNames } from '../../shared/ui/joinClassNames.ts';
|
|
4
4
|
import { siteById } from '../core/lookup.ts';
|
|
5
5
|
import { siteNameColors } from '../core/nameColors.ts';
|
|
6
|
-
import type {
|
|
6
|
+
import type { SiteId, SiteRecord } from '../core/sites.ts';
|
|
7
7
|
|
|
8
8
|
/** What a site's written name needs. */
|
|
9
9
|
export interface WordmarkProps {
|
|
@@ -13,7 +13,7 @@ export interface WordmarkProps {
|
|
|
13
13
|
* required.
|
|
14
14
|
* @default undefined
|
|
15
15
|
*/
|
|
16
|
-
site?:
|
|
16
|
+
site?: SiteRecord;
|
|
17
17
|
/**
|
|
18
18
|
* The same site, named rather than passed, which is what a header knows.
|
|
19
19
|
* @default undefined
|
|
@@ -409,4 +409,55 @@ export const GLYPHS: Record<SiteId, (alt: string) => ReactNode> = {
|
|
|
409
409
|
/>
|
|
410
410
|
</>
|
|
411
411
|
),
|
|
412
|
+
// A nucleus with one electron on its orbit. The electron carries the
|
|
413
|
+
// answering colour because the electron is what every tool here counts.
|
|
414
|
+
atoms: (alt) => (
|
|
415
|
+
<>
|
|
416
|
+
<circle
|
|
417
|
+
cx="16"
|
|
418
|
+
cy="16"
|
|
419
|
+
r="10.5"
|
|
420
|
+
fill="none"
|
|
421
|
+
stroke="#ffffff"
|
|
422
|
+
strokeWidth="2.2"
|
|
423
|
+
/>
|
|
424
|
+
<circle cx="16" cy="16" r="3.6" fill="#ffffff" />
|
|
425
|
+
<circle cx="23.42" cy="8.58" r="3.2" fill={alt} />
|
|
426
|
+
</>
|
|
427
|
+
),
|
|
428
|
+
// A balance still tipped, its two pans holding the two sides of the
|
|
429
|
+
// calculation. The pan that carries the answer carries the colour.
|
|
430
|
+
moles: (alt) => (
|
|
431
|
+
<>
|
|
432
|
+
<path
|
|
433
|
+
d="M6.5 11.5 25.5 15.5M16 13.5V24M10.5 24h11"
|
|
434
|
+
fill="none"
|
|
435
|
+
stroke="#ffffff"
|
|
436
|
+
strokeWidth="2.2"
|
|
437
|
+
strokeLinecap="round"
|
|
438
|
+
strokeLinejoin="round"
|
|
439
|
+
/>
|
|
440
|
+
<circle cx="6.5" cy="11.5" r="3.2" fill="#ffffff" />
|
|
441
|
+
<circle cx="25.5" cy="15.5" r="3.2" fill={alt} />
|
|
442
|
+
</>
|
|
443
|
+
),
|
|
444
|
+
// One cell of an ionic lattice, cations and anions alternating: the shape of
|
|
445
|
+
// the compounds this site names. The ion being named carries the colour.
|
|
446
|
+
inorganic: (alt) => (
|
|
447
|
+
<>
|
|
448
|
+
<path
|
|
449
|
+
d="M9.5 9.5h13v13h-13Z"
|
|
450
|
+
fill="none"
|
|
451
|
+
stroke="#ffffff"
|
|
452
|
+
strokeWidth="1.6"
|
|
453
|
+
opacity="0.55"
|
|
454
|
+
/>
|
|
455
|
+
<g fill="#ffffff">
|
|
456
|
+
<circle cx="9.5" cy="9.5" r="4.3" />
|
|
457
|
+
<circle cx="22.5" cy="9.5" r="2.7" />
|
|
458
|
+
<circle cx="9.5" cy="22.5" r="2.7" />
|
|
459
|
+
</g>
|
|
460
|
+
<circle cx="22.5" cy="22.5" r="4.3" fill={alt} />
|
|
461
|
+
</>
|
|
462
|
+
),
|
|
412
463
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { CSSProperties, ReactElement } from 'react';
|
|
1
|
+
import type { CSSProperties, ReactElement, ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
import { siteById } from '../core/lookup.ts';
|
|
4
|
-
import type {
|
|
4
|
+
import type { SiteId, SiteRecord } from '../core/sites.ts';
|
|
5
5
|
|
|
6
6
|
import { GLYPHS } from './glyphs.tsx';
|
|
7
7
|
|
|
@@ -19,13 +19,21 @@ export interface SiteMarkProps {
|
|
|
19
19
|
* and `siteId` is required.
|
|
20
20
|
* @default undefined
|
|
21
21
|
*/
|
|
22
|
-
site?:
|
|
22
|
+
site?: SiteRecord;
|
|
23
23
|
/**
|
|
24
24
|
* The same site, named rather than passed, for a header that knows only
|
|
25
25
|
* which site it is.
|
|
26
26
|
* @default undefined
|
|
27
27
|
*/
|
|
28
28
|
siteId?: SiteId;
|
|
29
|
+
/**
|
|
30
|
+
* What is drawn on the plate, in the same 32×32 box, given the colour its one
|
|
31
|
+
* answering element takes. It is how a site that is not one of the family,
|
|
32
|
+
* and so has no drawing in the shared set, still gets a mark of the family's
|
|
33
|
+
* shape.
|
|
34
|
+
* @default the family's drawing for that site
|
|
35
|
+
*/
|
|
36
|
+
glyph?: (accent: string) => ReactNode;
|
|
29
37
|
/**
|
|
30
38
|
* Edge of the square the mark is drawn in, in pixels.
|
|
31
39
|
* @default 28
|
|
@@ -57,13 +65,15 @@ export interface SiteMarkProps {
|
|
|
57
65
|
* The little logo of one site of the family.
|
|
58
66
|
* @param props - Which site, how big, and where its colours come from.
|
|
59
67
|
* @returns The mark, as an inline SVG.
|
|
60
|
-
* @throws {Error} When neither `site` nor `siteId` is given
|
|
68
|
+
* @throws {Error} When neither `site` nor `siteId` is given, or when the site
|
|
69
|
+
* is not one of the family and no `glyph` is.
|
|
61
70
|
*/
|
|
62
71
|
export function SiteMark(props: SiteMarkProps): ReactElement {
|
|
63
72
|
const {
|
|
64
73
|
className,
|
|
65
74
|
site,
|
|
66
75
|
siteId,
|
|
76
|
+
glyph,
|
|
67
77
|
size = 28,
|
|
68
78
|
plate = true,
|
|
69
79
|
colors = 'literal',
|
|
@@ -73,6 +83,10 @@ export function SiteMark(props: SiteMarkProps): ReactElement {
|
|
|
73
83
|
if (drawn === undefined) {
|
|
74
84
|
throw new Error('SiteMark needs one of its `site` and `siteId` props');
|
|
75
85
|
}
|
|
86
|
+
const draw = glyph ?? familyGlyph(drawn.id);
|
|
87
|
+
if (draw === undefined) {
|
|
88
|
+
throw new Error(`SiteMark has no drawing for ${drawn.id}: pass its glyph`);
|
|
89
|
+
}
|
|
76
90
|
|
|
77
91
|
const usesTokens = colors === 'tokens';
|
|
78
92
|
const plateFill = usesTokens ? 'var(--brand)' : drawn.mark.plate;
|
|
@@ -100,7 +114,11 @@ export function SiteMark(props: SiteMarkProps): ReactElement {
|
|
|
100
114
|
stroke={edge}
|
|
101
115
|
/>
|
|
102
116
|
) : null}
|
|
103
|
-
{
|
|
117
|
+
{draw(accent)}
|
|
104
118
|
</svg>
|
|
105
119
|
);
|
|
106
120
|
}
|
|
121
|
+
|
|
122
|
+
function familyGlyph(id: string): ((accent: string) => ReactNode) | undefined {
|
|
123
|
+
return Object.hasOwn(GLYPHS, id) ? GLYPHS[id as SiteId] : undefined;
|
|
124
|
+
}
|
|
@@ -52,7 +52,9 @@ export function ErrorFallback(props: ErrorFallbackProps): ReactElement {
|
|
|
52
52
|
icon="error"
|
|
53
53
|
title={title}
|
|
54
54
|
description={
|
|
55
|
-
|
|
55
|
+
<span className="error-fallback__message">
|
|
56
|
+
{error.message === '' ? 'No message was given.' : error.message}
|
|
57
|
+
</span>
|
|
56
58
|
}
|
|
57
59
|
action={
|
|
58
60
|
onRetry === undefined ? undefined : (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CSSProperties, ReactElement } from 'react';
|
|
2
2
|
|
|
3
|
+
import { ClickToCopy } from '../../clipboard/ui/ClickToCopy.tsx';
|
|
3
4
|
import { joinClassNames } from '../../shared/ui/joinClassNames.ts';
|
|
4
5
|
import { TOKEN } from '../../tokens/core/familyTokens.ts';
|
|
5
6
|
|
|
@@ -108,7 +109,15 @@ function ReferenceRowLine(props: {
|
|
|
108
109
|
const interactive = row.tooltip !== undefined;
|
|
109
110
|
const line = (
|
|
110
111
|
<span style={interactive ? INTERACTIVE_ROW_STYLE : ROW_STYLE}>
|
|
111
|
-
<span style={syntaxStyle(width, interactive)}>
|
|
112
|
+
<span style={syntaxStyle(width, interactive)}>
|
|
113
|
+
<ClickToCopy
|
|
114
|
+
value={row.syntax}
|
|
115
|
+
label="syntax"
|
|
116
|
+
title={interactive ? '' : undefined}
|
|
117
|
+
>
|
|
118
|
+
{row.syntax}
|
|
119
|
+
</ClickToCopy>
|
|
120
|
+
</span>
|
|
112
121
|
<span style={DESCRIPTION_STYLE}>{row.description}</span>
|
|
113
122
|
</span>
|
|
114
123
|
);
|
package/src/seo/core/noscript.ts
CHANGED
|
@@ -167,7 +167,7 @@ function labelOf(route: NoscriptRoute): string {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
function familyList(
|
|
170
|
-
current:
|
|
170
|
+
current: string,
|
|
171
171
|
ecosystem: boolean | NoscriptEcosystem | undefined,
|
|
172
172
|
): string {
|
|
173
173
|
if (ecosystem === undefined || ecosystem === false) return '';
|
package/src/seo/core/pageMeta.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { siteDisplayName } from '../../ecosystem/core/lookup.ts';
|
|
16
|
-
import type {
|
|
16
|
+
import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
|
|
17
17
|
import { escapeAttribute, escapeText } from '../../share/core/escape.ts';
|
|
18
18
|
|
|
19
19
|
import type { DocumentMeta } from './documentMeta.ts';
|
|
@@ -25,7 +25,7 @@ import { PAGE_HEAD_MARKER, fill } from './template.ts';
|
|
|
25
25
|
/** Which site is being served, and what it answers. */
|
|
26
26
|
export interface PageMetaOptions {
|
|
27
27
|
/** The site, named or passed. */
|
|
28
|
-
site:
|
|
28
|
+
site: SiteRecord | SiteId;
|
|
29
29
|
/** Every address it answers, each with its title and description. */
|
|
30
30
|
routes: readonly RouteMeta[];
|
|
31
31
|
/**
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { siteById } from '../../ecosystem/core/lookup.ts';
|
|
13
|
-
import type {
|
|
13
|
+
import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
|
|
14
14
|
import { trimTrailingSlash } from '../../router/core/address.ts';
|
|
15
15
|
import { basePathOf } from '../../router/core/basePath.ts';
|
|
16
16
|
import { escapeText } from '../../share/core/escape.ts';
|
|
@@ -25,7 +25,7 @@ const HTTP_ORIGIN = /^https?:\/\//i;
|
|
|
25
25
|
/** What a crawler is told about the site as a whole. */
|
|
26
26
|
export interface SiteFilesOptions {
|
|
27
27
|
/** The site, named or passed. */
|
|
28
|
-
site:
|
|
28
|
+
site: SiteRecord | SiteId;
|
|
29
29
|
/** Every address it answers. */
|
|
30
30
|
routes: readonly RouteMeta[];
|
|
31
31
|
/**
|
|
@@ -71,7 +71,7 @@ ${entries}
|
|
|
71
71
|
* @param site - The site, named or passed.
|
|
72
72
|
* @returns Its record.
|
|
73
73
|
*/
|
|
74
|
-
export function resolveSite(site:
|
|
74
|
+
export function resolveSite(site: SiteRecord | SiteId): SiteRecord {
|
|
75
75
|
return typeof site === 'string' ? siteById(site) : site;
|
|
76
76
|
}
|
|
77
77
|
|
package/src/seo/vite/ogCard.ts
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
* a site must be able to import with none of the optional peers installed.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import type { ReactNode } from 'react';
|
|
15
|
+
|
|
14
16
|
import { siteNameColors } from '../../ecosystem/core/nameColors.ts';
|
|
15
|
-
import type {
|
|
17
|
+
import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
|
|
16
18
|
import { escapeText } from '../../share/core/escape.ts';
|
|
17
19
|
import { FAMILY_TOKEN_VALUES } from '../../tokens/core/familyTokens.ts';
|
|
18
20
|
import { resolveSite } from '../core/siteFiles.ts';
|
|
@@ -26,12 +28,18 @@ export const OG_HEIGHT = 630;
|
|
|
26
28
|
/** What the card says, beyond the site's own name and mark. */
|
|
27
29
|
export interface OgCardOptions {
|
|
28
30
|
/** The site, named or passed. */
|
|
29
|
-
site:
|
|
31
|
+
site: SiteRecord | SiteId;
|
|
30
32
|
/**
|
|
31
33
|
* The sentence under the name.
|
|
32
34
|
* @default the site's tagline
|
|
33
35
|
*/
|
|
34
36
|
description?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The drawing on the mark's plate, for a site the shared glyph set does not
|
|
39
|
+
* hold — see `SiteMark`.
|
|
40
|
+
* @default the family's drawing for that site
|
|
41
|
+
*/
|
|
42
|
+
glyph?: (accent: string) => ReactNode;
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
/**
|
|
@@ -58,7 +66,12 @@ export async function ogCardHtml(options: OgCardOptions): Promise<string> {
|
|
|
58
66
|
import('../../ecosystem/ui/marks.tsx'),
|
|
59
67
|
]);
|
|
60
68
|
const mark = renderToStaticMarkup(
|
|
61
|
-
createElement(SiteMark, {
|
|
69
|
+
createElement(SiteMark, {
|
|
70
|
+
site,
|
|
71
|
+
glyph: options.glyph,
|
|
72
|
+
size: 132,
|
|
73
|
+
colors: 'literal',
|
|
74
|
+
}),
|
|
62
75
|
);
|
|
63
76
|
const dot = site.name.dot === true ? '<span class="dot">.</span>' : '';
|
|
64
77
|
|
|
@@ -22,7 +22,7 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
22
22
|
|
|
23
23
|
import type { Logger, Plugin } from 'vite';
|
|
24
24
|
|
|
25
|
-
import type {
|
|
25
|
+
import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
|
|
26
26
|
import { trimTrailingSlash } from '../../router/core/address.ts';
|
|
27
27
|
import type { NoscriptText } from '../core/noscript.ts';
|
|
28
28
|
import { noscriptIndex } from '../core/noscript.ts';
|
|
@@ -38,7 +38,7 @@ import { PAGE_BODY_MARKER, PAGE_HEAD_MARKER, fill } from '../core/template.ts';
|
|
|
38
38
|
/** What the build needs to know to write the site's addresses. */
|
|
39
39
|
export interface PrerenderOptions {
|
|
40
40
|
/** The site, named or passed. */
|
|
41
|
-
site:
|
|
41
|
+
site: SiteRecord | SiteId;
|
|
42
42
|
/** Every address it answers, each with its title and description. */
|
|
43
43
|
routes: readonly RouteMeta[];
|
|
44
44
|
/**
|