react-cheminfo 0.20.0 → 0.21.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.
Files changed (62) hide show
  1. package/lib/about/core/about.d.ts +3 -3
  2. package/lib/about/core/about.d.ts.map +1 -1
  3. package/lib/chrome/ui/SiteHeader.d.ts +20 -3
  4. package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
  5. package/lib/chrome/ui/SiteHeader.js +7 -3
  6. package/lib/chrome/ui/SiteHeader.js.map +1 -1
  7. package/lib/ecosystem/core/index.d.ts +1 -1
  8. package/lib/ecosystem/core/index.d.ts.map +1 -1
  9. package/lib/ecosystem/core/index.js.map +1 -1
  10. package/lib/ecosystem/core/lookup.d.ts +2 -2
  11. package/lib/ecosystem/core/lookup.d.ts.map +1 -1
  12. package/lib/ecosystem/core/lookup.js.map +1 -1
  13. package/lib/ecosystem/core/nameColors.d.ts +2 -2
  14. package/lib/ecosystem/core/nameColors.d.ts.map +1 -1
  15. package/lib/ecosystem/core/nameColors.js.map +1 -1
  16. package/lib/ecosystem/core/sites.d.ts +3 -3
  17. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  18. package/lib/ecosystem/core/sites.js.map +1 -1
  19. package/lib/ecosystem/core/tokens.d.ts +4 -3
  20. package/lib/ecosystem/core/tokens.d.ts.map +1 -1
  21. package/lib/ecosystem/core/tokens.js +8 -7
  22. package/lib/ecosystem/core/tokens.js.map +1 -1
  23. package/lib/ecosystem/core/types.d.ts +18 -9
  24. package/lib/ecosystem/core/types.d.ts.map +1 -1
  25. package/lib/ecosystem/ui/SiteTheme.d.ts +14 -3
  26. package/lib/ecosystem/ui/SiteTheme.d.ts.map +1 -1
  27. package/lib/ecosystem/ui/SiteTheme.js +6 -1
  28. package/lib/ecosystem/ui/SiteTheme.js.map +1 -1
  29. package/lib/ecosystem/ui/Wordmark.d.ts +2 -2
  30. package/lib/ecosystem/ui/Wordmark.d.ts.map +1 -1
  31. package/lib/ecosystem/ui/marks.d.ts +13 -4
  32. package/lib/ecosystem/ui/marks.d.ts.map +1 -1
  33. package/lib/ecosystem/ui/marks.js +11 -3
  34. package/lib/ecosystem/ui/marks.js.map +1 -1
  35. package/lib/seo/core/pageMeta.d.ts +2 -2
  36. package/lib/seo/core/pageMeta.d.ts.map +1 -1
  37. package/lib/seo/core/siteFiles.d.ts +3 -3
  38. package/lib/seo/core/siteFiles.d.ts.map +1 -1
  39. package/lib/seo/core/siteFiles.js.map +1 -1
  40. package/lib/seo/vite/ogCard.d.ts +9 -2
  41. package/lib/seo/vite/ogCard.d.ts.map +1 -1
  42. package/lib/seo/vite/ogCard.js +6 -1
  43. package/lib/seo/vite/ogCard.js.map +1 -1
  44. package/lib/seo/vite/prerender.d.ts +2 -2
  45. package/lib/seo/vite/prerender.d.ts.map +1 -1
  46. package/package.json +1 -1
  47. package/src/about/core/about.ts +3 -3
  48. package/src/chrome/ui/SiteHeader.tsx +28 -6
  49. package/src/ecosystem/core/index.ts +1 -0
  50. package/src/ecosystem/core/lookup.ts +2 -2
  51. package/src/ecosystem/core/nameColors.ts +2 -2
  52. package/src/ecosystem/core/sites.ts +3 -2
  53. package/src/ecosystem/core/tokens.ts +10 -9
  54. package/src/ecosystem/core/types.ts +19 -9
  55. package/src/ecosystem/ui/SiteTheme.tsx +19 -4
  56. package/src/ecosystem/ui/Wordmark.tsx +2 -2
  57. package/src/ecosystem/ui/marks.tsx +23 -5
  58. package/src/seo/core/noscript.ts +1 -1
  59. package/src/seo/core/pageMeta.ts +2 -2
  60. package/src/seo/core/siteFiles.ts +3 -3
  61. package/src/seo/vite/ogCard.ts +16 -3
  62. package/src/seo/vite/prerender.ts +2 -2
@@ -8,12 +8,12 @@
8
8
  * entry need. The mount is read back out of it here, so the addresses these
9
9
  * files hand out start where the site actually answers.
10
10
  */
11
- import type { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
11
+ import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
12
12
  import type { RouteMeta } from './routes.ts';
13
13
  /** What a crawler is told about the site as a whole. */
14
14
  export interface SiteFilesOptions {
15
15
  /** The site, named or passed. */
16
- site: EcosystemSite | SiteId;
16
+ site: SiteRecord | SiteId;
17
17
  /** Every address it answers. */
18
18
  routes: readonly RouteMeta[];
19
19
  /**
@@ -41,7 +41,7 @@ export declare function sitemapXml(options: SiteFilesOptions): string;
41
41
  * @param site - The site, named or passed.
42
42
  * @returns Its record.
43
43
  */
44
- export declare function resolveSite(site: EcosystemSite | SiteId): EcosystemSite;
44
+ export declare function resolveSite(site: SiteRecord | SiteId): SiteRecord;
45
45
  /**
46
46
  * Where the site is served, as an absolute address without a trailing slash.
47
47
  *
@@ -1 +1 @@
1
- {"version":3,"file":"siteFiles.d.ts","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAK3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7B,gCAAgC;IAChC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAgB5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,aAAa,CAEvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAQ1D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAE7D"}
1
+ {"version":3,"file":"siteFiles.d.ts","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAKxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,gCAAgC;IAChC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAgB5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,CAEjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAQ1D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAE7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"siteFiles.js","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAIxD,+EAA+E;AAC/E,kFAAkF;AAClF,kEAAkE;AAClE,MAAM,WAAW,GAAG,eAAe,CAAC;AAiBpC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,OAAyB;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM;SAC3B,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CACR,eAAe,UAAU,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,cAAc,CACpE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;;EAEP,OAAO;;CAER,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAA4B;IACtD,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAyB;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,sEAAsE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,OAAO,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"siteFiles.js","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAIxD,+EAA+E;AAC/E,kFAAkF;AAClF,kEAAkE;AAClE,MAAM,WAAW,GAAG,eAAe,CAAC;AAiBpC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,OAAyB;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM;SAC3B,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CACR,eAAe,UAAU,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,cAAc,CACpE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;;EAEP,OAAO;;CAER,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAyB;IACnD,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAyB;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,sEAAsE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,OAAO,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACvC,CAAC"}
@@ -9,7 +9,8 @@
9
9
  * load: this module sits in the same entry point as the prerender plugin, which
10
10
  * a site must be able to import with none of the optional peers installed.
11
11
  */
12
- import type { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
12
+ import type { ReactNode } from 'react';
13
+ import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
13
14
  /** The width every card is drawn at. */
14
15
  export declare const OG_WIDTH = 1200;
15
16
  /** The height every card is drawn at. */
@@ -17,12 +18,18 @@ export declare const OG_HEIGHT = 630;
17
18
  /** What the card says, beyond the site's own name and mark. */
18
19
  export interface OgCardOptions {
19
20
  /** The site, named or passed. */
20
- site: EcosystemSite | SiteId;
21
+ site: SiteRecord | SiteId;
21
22
  /**
22
23
  * The sentence under the name.
23
24
  * @default the site's tagline
24
25
  */
25
26
  description?: string;
27
+ /**
28
+ * The drawing on the mark's plate, for a site the shared glyph set does not
29
+ * hold — see `SiteMark`.
30
+ * @default the family's drawing for that site
31
+ */
32
+ glyph?: (accent: string) => ReactNode;
26
33
  }
27
34
  /**
28
35
  * The card, as a standalone page.
@@ -1 +1 @@
1
- {"version":3,"file":"ogCard.d.ts","sourceRoot":"","sources":["../../../src/seo/vite/ogCard.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAK3E,wCAAwC;AACxC,eAAO,MAAM,QAAQ,OAAO,CAAC;AAE7B,yCAAyC;AACzC,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAuDxE"}
1
+ {"version":3,"file":"ogCard.d.ts","sourceRoot":"","sources":["../../../src/seo/vite/ogCard.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAKxE,wCAAwC;AACxC,eAAO,MAAM,QAAQ,OAAO,CAAC;AAE7B,yCAAyC;AACzC,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC;CACvC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA4DxE"}
@@ -40,7 +40,12 @@ export async function ogCardHtml(options) {
40
40
  import('react-dom/server'),
41
41
  import("../../ecosystem/ui/marks.js"),
42
42
  ]);
43
- const mark = renderToStaticMarkup(createElement(SiteMark, { site, size: 132, colors: 'literal' }));
43
+ const mark = renderToStaticMarkup(createElement(SiteMark, {
44
+ site,
45
+ glyph: options.glyph,
46
+ size: 132,
47
+ colors: 'literal',
48
+ }));
44
49
  const dot = site.name.dot === true ? '<span class="dot">.</span>' : '';
45
50
  return `<!doctype html>
46
51
  <html lang="en">
@@ -1 +1 @@
1
- {"version":3,"file":"ogCard.js","sourceRoot":"","sources":["../../../src/seo/vite/ogCard.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,wCAAwC;AACxC,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC;AAE7B,yCAAyC;AACzC,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAa7B;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAsB;IACrD,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC;IACxD,MAAM,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,GAC/D,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,MAAM,CAAC,OAAO,CAAC;QACf,MAAM,CAAC,kBAAkB,CAAC;QAC1B,MAAM,CAAC,6BAA8B,CAAC;KACvC,CAAC,CAAC;IACL,MAAM,IAAI,GAAG,oBAAoB,CAC/B,aAAa,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAChE,CAAC;IACF,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvE,OAAO;;;;;;;;iBAQQ,QAAQ;kBACP,SAAS;;;;sBAIL,mBAAmB,CAAC,WAAW,CAAC;iBACrC,mBAAmB,CAAC,QAAQ,CAAC;;;;;;uBAMvB,MAAM,CAAC,IAAI;sBACZ,MAAM,CAAC,GAAG;sBACV,MAAM,CAAC,GAAG;qCACK,mBAAmB,CAAC,cAAc,CAAC;;;;;sBAKlD,IAAI,CAAC,QAAQ;;;;;MAK7B,IAAI;6BACmB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,qBAAqB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;;SAEzG,UAAU,CAAC,WAAW,CAAC;;;CAG/B,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"ogCard.js","sourceRoot":"","sources":["../../../src/seo/vite/ogCard.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,wCAAwC;AACxC,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC;AAE7B,yCAAyC;AACzC,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAmB7B;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAsB;IACrD,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC;IACxD,MAAM,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,GAC/D,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,MAAM,CAAC,OAAO,CAAC;QACf,MAAM,CAAC,kBAAkB,CAAC;QAC1B,MAAM,CAAC,6BAA8B,CAAC;KACvC,CAAC,CAAC;IACL,MAAM,IAAI,GAAG,oBAAoB,CAC/B,aAAa,CAAC,QAAQ,EAAE;QACtB,IAAI;QACJ,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,SAAS;KAClB,CAAC,CACH,CAAC;IACF,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvE,OAAO;;;;;;;;iBAQQ,QAAQ;kBACP,SAAS;;;;sBAIL,mBAAmB,CAAC,WAAW,CAAC;iBACrC,mBAAmB,CAAC,QAAQ,CAAC;;;;;;uBAMvB,MAAM,CAAC,IAAI;sBACZ,MAAM,CAAC,GAAG;sBACV,MAAM,CAAC,GAAG;qCACK,mBAAmB,CAAC,cAAc,CAAC;;;;;sBAKlD,IAAI,CAAC,QAAQ;;;;;MAK7B,IAAI;6BACmB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,qBAAqB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;;SAEzG,UAAU,CAAC,WAAW,CAAC;;;CAG/B,CAAC;AACF,CAAC"}
@@ -17,14 +17,14 @@
17
17
  * a name it does not have.
18
18
  */
19
19
  import type { Plugin } from 'vite';
20
- import type { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
20
+ import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
21
21
  import type { NoscriptText } from '../core/noscript.ts';
22
22
  import type { RobotsDisallow } from '../core/robots.ts';
23
23
  import type { RouteMeta } from '../core/routes.ts';
24
24
  /** What the build needs to know to write the site's addresses. */
25
25
  export interface PrerenderOptions {
26
26
  /** The site, named or passed. */
27
- site: EcosystemSite | SiteId;
27
+ site: SiteRecord | SiteId;
28
28
  /** Every address it answers, each with its title and description. */
29
29
  routes: readonly RouteMeta[];
30
30
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../../src/seo/vite/prerender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAE3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7B,qEAAqE;IACrE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACxD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CA6EnE"}
1
+ {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../../src/seo/vite/prerender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,qEAAqE;IACrE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACxD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CA6EnE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-cheminfo",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "license": "MIT",
5
5
  "description": "React components, hooks and framework-free helpers shared by the cheminfo sites: site chrome, citations, sharing, routing, pedagogy, charts, and the structure, orbital and 3D molecule viewers",
6
6
  "author": "Luc Patiny",
@@ -3,7 +3,7 @@ import type { CitedWork } from '../../citation/core/works.ts';
3
3
  import type { CreditEntry, CreditId } from '../../credits/core/credits.ts';
4
4
  import { credits } from '../../credits/core/credits.ts';
5
5
  import { siteById } from '../../ecosystem/core/lookup.ts';
6
- import type { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
6
+ import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
7
7
 
8
8
  import type { ProviderEntry, ProviderId } from './providers.ts';
9
9
  import { providers } from './providers.ts';
@@ -23,7 +23,7 @@ export interface AboutContent {
23
23
  * is deliberately not listed in the family's Tools menu and therefore not in
24
24
  * `ECOSYSTEM_SITES`.
25
25
  */
26
- siteId: SiteId | EcosystemSite;
26
+ siteId: SiteId | SiteRecord;
27
27
  /** One sentence: what this tool is. */
28
28
  what: string;
29
29
  /**
@@ -101,7 +101,7 @@ export interface AboutPerson {
101
101
  */
102
102
  export interface ResolvedAbout {
103
103
  /** The site's own record, which carries its name, tagline and colours. */
104
- site: EcosystemSite;
104
+ site: SiteRecord;
105
105
  what: string;
106
106
  /** The context paragraphs, empty when the site writes none. */
107
107
  paragraphs: readonly string[];
@@ -2,7 +2,7 @@ import type { ReactElement, ReactNode } from 'react';
2
2
  import { Fragment } from 'react';
3
3
 
4
4
  import { siteById } from '../../ecosystem/core/lookup.ts';
5
- import type { SiteId } from '../../ecosystem/core/sites.ts';
5
+ import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
6
6
  import { Wordmark } from '../../ecosystem/ui/Wordmark.tsx';
7
7
  import { SiteMark } from '../../ecosystem/ui/marks.tsx';
8
8
 
@@ -12,8 +12,24 @@ import type { NavItem } from './navItem.ts';
12
12
  import { isModifiedClick } from './navItem.ts';
13
13
 
14
14
  export interface SiteHeaderProps {
15
- /** The site the bar belongs to, which draws its mark and writes its name. */
16
- siteId: SiteId;
15
+ /**
16
+ * The site the bar belongs to, which draws its mark and writes its name. One
17
+ * of `site` and `siteId` is required.
18
+ * @default undefined
19
+ */
20
+ siteId?: SiteId;
21
+ /**
22
+ * The same site, passed rather than named — for a site that is deliberately
23
+ * not one of `ECOSYSTEM_SITES`, and so is linked from no other site's menu.
24
+ * @default undefined
25
+ */
26
+ site?: SiteRecord;
27
+ /**
28
+ * The site's own mark, for a site the shared glyph set does not hold. It
29
+ * stands where `SiteMark` would, at the size the site draws it.
30
+ * @default undefined — the family's mark for that site
31
+ */
32
+ mark?: ReactNode;
17
33
  /** The pages, in the order the bar lists them. */
18
34
  nav: readonly NavItem[];
19
35
  /**
@@ -86,10 +102,13 @@ export interface SiteHeaderProps {
86
102
  * @param props - The site, its pages, its utilities, and whether the page is
87
103
  * framed in another site.
88
104
  * @returns The bar, or nothing at all on an embedded page.
105
+ * @throws {Error} When neither `site` nor `siteId` is given.
89
106
  */
90
107
  export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
91
108
  const {
92
109
  siteId,
110
+ site: record,
111
+ mark,
93
112
  nav,
94
113
  activeId,
95
114
  actions,
@@ -104,7 +123,10 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
104
123
 
105
124
  if (embedded) return null;
106
125
 
107
- const site = siteById(siteId);
126
+ const site = record ?? (siteId === undefined ? undefined : siteById(siteId));
127
+ if (site === undefined) {
128
+ throw new Error('SiteHeader needs one of its `site` and `siteId` props');
129
+ }
108
130
 
109
131
  return (
110
132
  <header className="app-header no-print">
@@ -119,8 +141,8 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
119
141
  onHome();
120
142
  }}
121
143
  >
122
- <SiteMark siteId={siteId} size={markSize} />
123
- <Wordmark siteId={siteId} />
144
+ {mark ?? <SiteMark site={site} size={markSize} />}
145
+ <Wordmark site={site} />
124
146
  </a>
125
147
  <nav className="app-header-nav">
126
148
  {nav.map((item) => (
@@ -14,6 +14,7 @@ export type {
14
14
  SiteId,
15
15
  SiteMarkColors,
16
16
  SiteName,
17
+ SiteRecord,
17
18
  } from './sites.ts';
18
19
  export { ECOSYSTEM_SITES, siteUrl } from './sites.ts';
19
20
  export { siteTokensCss } from './tokens.ts';
@@ -1,6 +1,6 @@
1
1
  import type { SiteGroup, SiteGroupId } from './groups.ts';
2
2
  import { SITE_GROUPS } from './groups.ts';
3
- import type { EcosystemSite, SiteId } from './sites.ts';
3
+ import type { EcosystemSite, SiteId, SiteRecord } from './sites.ts';
4
4
  import { ECOSYSTEM_SITES } from './sites.ts';
5
5
 
6
6
  /** One topic and the sites written under it. */
@@ -62,7 +62,7 @@ export function siteById(id: SiteId): EcosystemSite {
62
62
  * @param site - The site being named.
63
63
  * @returns What the site is called, in one string.
64
64
  */
65
- export function siteDisplayName(site: EcosystemSite): string {
65
+ export function siteDisplayName(site: SiteRecord): string {
66
66
  return site.name.dot === true
67
67
  ? site.host
68
68
  : `${site.name.lead}${site.name.alt}`;
@@ -1,6 +1,6 @@
1
1
  import { FAMILY_TOKEN_VALUES, TOKEN } from '../../tokens/core/familyTokens.ts';
2
2
 
3
- import type { EcosystemSite } from './types.ts';
3
+ import type { SiteRecord } from './types.ts';
4
4
 
5
5
  /** The colour each half of a site's name is written in. */
6
6
  export interface SiteNameColors {
@@ -35,7 +35,7 @@ export interface SiteNameColorsOptions {
35
35
  * @returns The colour of each half, and of the dot between them.
36
36
  */
37
37
  export function siteNameColors(
38
- site: EcosystemSite,
38
+ site: SiteRecord,
39
39
  options: SiteNameColorsOptions = {},
40
40
  ): SiteNameColors {
41
41
  const literal = options.colors === 'literal';
@@ -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
  /**
@@ -295,6 +296,6 @@ export const ECOSYSTEM_SITES: readonly EcosystemSite[] = [
295
296
  * @param site - The site being linked to.
296
297
  * @returns The URL to open.
297
298
  */
298
- export function siteUrl(site: EcosystemSite): string {
299
+ export function siteUrl(site: SiteRecord): string {
299
300
  return `https://${site.host}/`;
300
301
  }
@@ -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 { EcosystemSite, SiteId } from './sites.ts';
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 id - The site whose palette is wanted.
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(id: SiteId): string {
16
- const site = siteById(id);
17
- const answering = answeringColor(site);
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: ${site.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 (site.brandAlt !== answering) {
26
- declarations.push(`--brand-alt-text: ${site.brandAlt};`);
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: EcosystemSite): string {
36
+ function answeringColor(site: SiteRecord): string {
36
37
  return site.mark.accent === site.brand ? site.mark.plate : site.mark.accent;
37
38
  }
@@ -50,10 +50,14 @@ export interface SiteName {
50
50
  ink?: 'lead' | 'alt';
51
51
  }
52
52
 
53
- /** One site of the family: its name, address, topic and two colours. */
54
- export interface EcosystemSite {
55
- /** The identifier every component and helper names the site by. */
56
- id: SiteId;
53
+ /**
54
+ * What the chrome draws a site from: its name, address and two colours. A site
55
+ * that is deliberately not one of the family writes its own, and passes it
56
+ * wherever a family site passes its id.
57
+ */
58
+ export interface SiteRecord {
59
+ /** The identifier the site is named by. */
60
+ id: string;
57
61
  /** The name, split the way the site's own wordmark splits it. */
58
62
  name: SiteName;
59
63
  /** Where the site lives, written the way it is typed. */
@@ -70,11 +74,6 @@ export interface EcosystemSite {
70
74
  publicRepository?: boolean;
71
75
  /** One line on what the site does. */
72
76
  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
77
  /**
79
78
  * The colour the name is written in — the first half, or the second when
80
79
  * `name.ink` leaves the first in the family's ink.
@@ -90,6 +89,17 @@ export interface EcosystemSite {
90
89
  mark: SiteMarkColors;
91
90
  }
92
91
 
92
+ /** One site of the family: its record, and the topic it is listed under. */
93
+ export interface EcosystemSite extends SiteRecord {
94
+ /** The identifier every component and helper names the site by. */
95
+ id: SiteId;
96
+ /**
97
+ * The topic the site is written under, which is what decides where it sits
98
+ * in the menu and in the footer.
99
+ */
100
+ group: SiteGroupId;
101
+ }
102
+
93
103
  /** The colours a site's mark is drawn in. */
94
104
  export interface SiteMarkColors {
95
105
  /** 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
- /** The site whose palette the page takes. */
9
- siteId: SiteId;
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
- return <style>{siteTokensCss(props.siteId)}</style>;
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 { EcosystemSite, SiteId } from '../core/sites.ts';
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?: EcosystemSite;
16
+ site?: SiteRecord;
17
17
  /**
18
18
  * The same site, named rather than passed, which is what a header knows.
19
19
  * @default undefined
@@ -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 { EcosystemSite, SiteId } from '../core/sites.ts';
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?: EcosystemSite;
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
- {GLYPHS[drawn.id](accent)}
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
+ }
@@ -167,7 +167,7 @@ function labelOf(route: NoscriptRoute): string {
167
167
  }
168
168
 
169
169
  function familyList(
170
- current: SiteId,
170
+ current: string,
171
171
  ecosystem: boolean | NoscriptEcosystem | undefined,
172
172
  ): string {
173
173
  if (ecosystem === undefined || ecosystem === false) return '';
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { siteDisplayName } from '../../ecosystem/core/lookup.ts';
16
- import type { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
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: EcosystemSite | SiteId;
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 { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
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: EcosystemSite | SiteId;
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: EcosystemSite | SiteId): EcosystemSite {
74
+ export function resolveSite(site: SiteRecord | SiteId): SiteRecord {
75
75
  return typeof site === 'string' ? siteById(site) : site;
76
76
  }
77
77
 
@@ -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 { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
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: EcosystemSite | SiteId;
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, { site, size: 132, colors: 'literal' }),
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