react-cheminfo 0.59.0 → 0.60.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 (125) hide show
  1. package/README.md +7 -2
  2. package/lib/about/ui/AboutBuildDetails.d.ts +15 -0
  3. package/lib/about/ui/AboutBuildDetails.d.ts.map +1 -0
  4. package/lib/about/ui/AboutBuildDetails.js +17 -0
  5. package/lib/about/ui/AboutBuildDetails.js.map +1 -0
  6. package/lib/about/ui/AboutPage.d.ts +8 -0
  7. package/lib/about/ui/AboutPage.d.ts.map +1 -1
  8. package/lib/about/ui/AboutPage.js +12 -3
  9. package/lib/about/ui/AboutPage.js.map +1 -1
  10. package/lib/about/ui/AboutSignIn.d.ts +33 -0
  11. package/lib/about/ui/AboutSignIn.d.ts.map +1 -0
  12. package/lib/about/ui/AboutSignIn.js +28 -0
  13. package/lib/about/ui/AboutSignIn.js.map +1 -0
  14. package/lib/about/ui/AboutVersion.d.ts +4 -2
  15. package/lib/about/ui/AboutVersion.d.ts.map +1 -1
  16. package/lib/about/ui/AboutVersion.js +21 -3
  17. package/lib/about/ui/AboutVersion.js.map +1 -1
  18. package/lib/about/ui/index.d.ts +2 -2
  19. package/lib/about/ui/index.d.ts.map +1 -1
  20. package/lib/about/ui/index.js +1 -1
  21. package/lib/about/ui/index.js.map +1 -1
  22. package/lib/chrome/ui/NavEntry.d.ts +16 -0
  23. package/lib/chrome/ui/NavEntry.d.ts.map +1 -0
  24. package/lib/chrome/ui/NavEntry.js +27 -0
  25. package/lib/chrome/ui/NavEntry.js.map +1 -0
  26. package/lib/chrome/ui/SiteHeader.d.ts +11 -4
  27. package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
  28. package/lib/chrome/ui/SiteHeader.js +4 -26
  29. package/lib/chrome/ui/SiteHeader.js.map +1 -1
  30. package/lib/core.d.ts +1 -0
  31. package/lib/core.d.ts.map +1 -1
  32. package/lib/core.js +1 -0
  33. package/lib/core.js.map +1 -1
  34. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  35. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  36. package/lib/locales/de.json +11 -3
  37. package/lib/locales/en.json +11 -3
  38. package/lib/locales/es.json +11 -3
  39. package/lib/locales/fr.json +11 -3
  40. package/lib/locales/it.json +11 -3
  41. package/lib/moleculesSources/core/index.d.ts +3 -0
  42. package/lib/moleculesSources/core/index.d.ts.map +1 -0
  43. package/lib/moleculesSources/core/index.js +2 -0
  44. package/lib/moleculesSources/core/index.js.map +1 -0
  45. package/lib/moleculesSources/core/moleculesSources.d.ts +55 -0
  46. package/lib/moleculesSources/core/moleculesSources.d.ts.map +1 -0
  47. package/lib/moleculesSources/core/moleculesSources.js +63 -0
  48. package/lib/moleculesSources/core/moleculesSources.js.map +1 -0
  49. package/lib/moleculesSources/ui/MoleculesSourceBadge.d.ts +18 -0
  50. package/lib/moleculesSources/ui/MoleculesSourceBadge.d.ts.map +1 -0
  51. package/lib/moleculesSources/ui/MoleculesSourceBadge.js +22 -0
  52. package/lib/moleculesSources/ui/MoleculesSourceBadge.js.map +1 -0
  53. package/lib/moleculesSources/ui/SourceBadge.d.ts +57 -0
  54. package/lib/moleculesSources/ui/SourceBadge.d.ts.map +1 -0
  55. package/lib/moleculesSources/ui/SourceBadge.js +29 -0
  56. package/lib/moleculesSources/ui/SourceBadge.js.map +1 -0
  57. package/lib/moleculesSources/ui/index.d.ts +7 -0
  58. package/lib/moleculesSources/ui/index.d.ts.map +1 -0
  59. package/lib/moleculesSources/ui/index.js +4 -0
  60. package/lib/moleculesSources/ui/index.js.map +1 -0
  61. package/lib/moleculesSources/ui/useSourceLooks.d.ts +24 -0
  62. package/lib/moleculesSources/ui/useSourceLooks.d.ts.map +1 -0
  63. package/lib/moleculesSources/ui/useSourceLooks.js +53 -0
  64. package/lib/moleculesSources/ui/useSourceLooks.js.map +1 -0
  65. package/lib/moleculesToken/core/index.d.ts +1 -0
  66. package/lib/moleculesToken/core/index.d.ts.map +1 -1
  67. package/lib/moleculesToken/core/index.js +1 -0
  68. package/lib/moleculesToken/core/index.js.map +1 -1
  69. package/lib/moleculesToken/core/tokenFix.d.ts +18 -0
  70. package/lib/moleculesToken/core/tokenFix.d.ts.map +1 -0
  71. package/lib/moleculesToken/core/tokenFix.js +27 -0
  72. package/lib/moleculesToken/core/tokenFix.js.map +1 -0
  73. package/lib/moleculesToken/ui/MoleculesTokenFix.d.ts +22 -0
  74. package/lib/moleculesToken/ui/MoleculesTokenFix.d.ts.map +1 -0
  75. package/lib/moleculesToken/ui/MoleculesTokenFix.js +35 -0
  76. package/lib/moleculesToken/ui/MoleculesTokenFix.js.map +1 -0
  77. package/lib/moleculesToken/ui/MoleculesTokenTable.d.ts +6 -1
  78. package/lib/moleculesToken/ui/MoleculesTokenTable.d.ts.map +1 -1
  79. package/lib/moleculesToken/ui/MoleculesTokenTable.js +7 -75
  80. package/lib/moleculesToken/ui/MoleculesTokenTable.js.map +1 -1
  81. package/lib/moleculesToken/ui/index.d.ts +2 -0
  82. package/lib/moleculesToken/ui/index.d.ts.map +1 -1
  83. package/lib/moleculesToken/ui/index.js +1 -0
  84. package/lib/moleculesToken/ui/index.js.map +1 -1
  85. package/lib/moleculesToken/ui/tokenDetails.d.ts +13 -0
  86. package/lib/moleculesToken/ui/tokenDetails.d.ts.map +1 -0
  87. package/lib/moleculesToken/ui/tokenDetails.js +73 -0
  88. package/lib/moleculesToken/ui/tokenDetails.js.map +1 -0
  89. package/lib/ui.d.ts +1 -0
  90. package/lib/ui.d.ts.map +1 -1
  91. package/lib/ui.js +1 -0
  92. package/lib/ui.js.map +1 -1
  93. package/package.json +1 -1
  94. package/src/about/ui/AboutBuildDetails.tsx +45 -0
  95. package/src/about/ui/AboutPage.tsx +27 -13
  96. package/src/about/ui/AboutSignIn.tsx +63 -0
  97. package/src/about/ui/AboutVersion.tsx +27 -9
  98. package/src/about/ui/index.ts +2 -2
  99. package/src/chrome/ui/NavEntry.tsx +41 -0
  100. package/src/chrome/ui/SiteHeader.tsx +15 -41
  101. package/src/core.ts +1 -0
  102. package/src/locales/de.json +11 -3
  103. package/src/locales/en.json +11 -3
  104. package/src/locales/es.json +11 -3
  105. package/src/locales/fr.json +11 -3
  106. package/src/locales/it.json +11 -3
  107. package/src/moleculesSources/core/index.ts +11 -0
  108. package/src/moleculesSources/core/moleculesSources.ts +102 -0
  109. package/src/moleculesSources/ui/MoleculesSourceBadge.tsx +44 -0
  110. package/src/moleculesSources/ui/SourceBadge.tsx +117 -0
  111. package/src/moleculesSources/ui/index.ts +6 -0
  112. package/src/moleculesSources/ui/useSourceLooks.ts +70 -0
  113. package/src/moleculesToken/core/index.ts +1 -0
  114. package/src/moleculesToken/core/tokenFix.ts +37 -0
  115. package/src/moleculesToken/ui/MoleculesTokenFix.tsx +95 -0
  116. package/src/moleculesToken/ui/MoleculesTokenTable.tsx +20 -94
  117. package/src/moleculesToken/ui/index.ts +2 -0
  118. package/src/moleculesToken/ui/tokenDetails.tsx +91 -0
  119. package/src/ui.ts +1 -0
  120. package/styles/chrome.css +159 -0
  121. package/lib/about/ui/AboutBuild.d.ts +0 -18
  122. package/lib/about/ui/AboutBuild.d.ts.map +0 -1
  123. package/lib/about/ui/AboutBuild.js +0 -37
  124. package/lib/about/ui/AboutBuild.js.map +0 -1
  125. package/src/about/ui/AboutBuild.tsx +0 -54
@@ -1,5 +1,6 @@
1
1
  import type { CSSProperties, ReactElement, ReactNode } from 'react';
2
2
 
3
+ import { buildLabel } from '../../build/core/buildInfo.ts';
3
4
  import { CreditsList } from '../../credits/ui/CreditsList.tsx';
4
5
  import { Wordmark } from '../../ecosystem/ui/Wordmark.tsx';
5
6
  import { SiteMark } from '../../ecosystem/ui/marks.tsx';
@@ -7,10 +8,11 @@ import { useChromeT } from '../../i18n/ui/useT.ts';
7
8
  import type { AboutContent } from '../core/about.ts';
8
9
  import { resolveAbout } from '../core/about.ts';
9
10
 
10
- import { AboutBuild } from './AboutBuild.tsx';
11
11
  import { AboutCitations } from './AboutCitations.tsx';
12
12
  import { AboutProvidedBy } from './AboutProvidedBy.tsx';
13
13
  import { AboutSection } from './AboutSection.tsx';
14
+ import type { AboutSignInProps } from './AboutSignIn.tsx';
15
+ import { AboutSignIn } from './AboutSignIn.tsx';
14
16
  import { AboutVersion } from './AboutVersion.tsx';
15
17
 
16
18
  export interface AboutPageProps {
@@ -43,6 +45,13 @@ export interface AboutPageProps {
43
45
  * @default undefined
44
46
  */
45
47
  logo?: ReactNode;
48
+ /**
49
+ * Where the people who run the site sign in, drawn under the build badge in
50
+ * the corner of the hero. A site passes it while nobody is signed in, and
51
+ * leaves it out once somebody is: the account is then in the bar.
52
+ * @default undefined — the site has no account to sign in to
53
+ */
54
+ signIn?: AboutSignInProps;
46
55
  }
47
56
 
48
57
  /**
@@ -58,10 +67,12 @@ export interface AboutPageProps {
58
67
  * @throws {Error} When the record names a site or a credit that does not exist.
59
68
  */
60
69
  export function AboutPage(props: AboutPageProps): ReactElement {
61
- const { content, className, children, logo, mark } = props;
70
+ const { content, className, children, logo, mark, signIn } = props;
62
71
  const t = useChromeT();
63
72
  const about = resolveAbout(content);
64
73
  const site = about.site;
74
+ const hasCorner =
75
+ buildLabel(about.build) !== undefined || signIn !== undefined;
65
76
 
66
77
  return (
67
78
  <div
@@ -83,7 +94,12 @@ export function AboutPage(props: AboutPageProps): ReactElement {
83
94
  </p>
84
95
  <p style={WHAT_STYLE}>{about.what}</p>
85
96
  </div>
86
- <AboutVersion build={about.build} />
97
+ {hasCorner ? (
98
+ <div className="about-corner" style={CORNER_STYLE}>
99
+ <AboutVersion build={about.build} />
100
+ {signIn === undefined ? null : <AboutSignIn {...signIn} />}
101
+ </div>
102
+ ) : null}
87
103
  </header>
88
104
 
89
105
  {about.people.length === 0 && about.providedBy.length === 0 ? null : (
@@ -127,16 +143,6 @@ export function AboutPage(props: AboutPageProps): ReactElement {
127
143
  <AboutCitations works={about.cite} />
128
144
  </AboutSection>
129
145
  )}
130
-
131
- {/*
132
- The build the reader has open, last and under no heading: it is what a
133
- report has to name, not something anybody came to the page to read.
134
- */}
135
- {about.build === undefined ? null : (
136
- <AboutSection className="about-build">
137
- <AboutBuild build={about.build} />
138
- </AboutSection>
139
- )}
140
146
  </div>
141
147
  );
142
148
  }
@@ -166,6 +172,14 @@ const HERO_STYLE = {
166
172
  lineHeight: 1.55,
167
173
  } as const satisfies CSSProperties;
168
174
 
175
+ const CORNER_STYLE = {
176
+ display: 'flex',
177
+ flex: '0 0 auto',
178
+ flexDirection: 'column',
179
+ alignItems: 'flex-end',
180
+ gap: 2,
181
+ } as const satisfies CSSProperties;
182
+
169
183
  const NAME_STYLE = {
170
184
  margin: 0,
171
185
  fontSize: 26,
@@ -0,0 +1,63 @@
1
+ import type { ReactElement } from 'react';
2
+
3
+ import { isModifiedClick } from '../../chrome/ui/navItem.ts';
4
+ import { useChromeT } from '../../i18n/ui/useT.ts';
5
+
6
+ export interface AboutSignInProps {
7
+ /**
8
+ * The address signing in happens at. Writing it keeps the entry a real link,
9
+ * so a middle click opens a tab.
10
+ * @default undefined
11
+ */
12
+ href?: string;
13
+ /**
14
+ * What the site does when the entry is picked — route to that page, or open
15
+ * a credentials dialog for a site that has no page. A modified click is left
16
+ * to the browser.
17
+ * @default undefined
18
+ */
19
+ onSignIn?: () => void;
20
+ /**
21
+ * What the entry reads, for a site whose accounts are for one kind of person.
22
+ * @default the chrome's own word for it, in the language of the page
23
+ */
24
+ label?: string;
25
+ }
26
+
27
+ /**
28
+ * Where the people who run a site sign in: one quiet line under the build
29
+ * badge, in the corner of the About.
30
+ *
31
+ * A visitor has nothing to sign in to, so the bar carries no invitation; the
32
+ * About is the page the operators already know, and the corner is where they
33
+ * already look for the build they run.
34
+ * @param props - See {@link AboutSignInProps}.
35
+ * @returns The entry.
36
+ */
37
+ export function AboutSignIn(props: AboutSignInProps): ReactElement {
38
+ const { href, onSignIn, label } = props;
39
+ const t = useChromeT();
40
+ const text = label ?? t('account.signIn');
41
+
42
+ if (href === undefined) {
43
+ return (
44
+ <button type="button" className="about-sign-in" onClick={onSignIn}>
45
+ {text}
46
+ </button>
47
+ );
48
+ }
49
+
50
+ return (
51
+ <a
52
+ className="about-sign-in"
53
+ href={href}
54
+ onClick={(event) => {
55
+ if (onSignIn === undefined || isModifiedClick(event)) return;
56
+ event.preventDefault();
57
+ onSignIn();
58
+ }}
59
+ >
60
+ {text}
61
+ </a>
62
+ );
63
+ }
@@ -4,18 +4,22 @@
4
4
  * A report of something going wrong is worth answering only when we know what
5
5
  * was running, and that is the one thing an About page cannot say from a
6
6
  * hand-written record. It sits at the top of the page rather than at the
7
- * bottom because it is the one line a reader is asked to quote back.
7
+ * bottom because it is the one line a reader is asked to quote back, and it is
8
+ * said once: nothing further down the page repeats it.
8
9
  */
9
10
 
11
+ import { Tooltip } from '@blueprintjs/core';
10
12
  import type { CSSProperties, ReactElement } from 'react';
11
13
 
12
14
  import type { BuildInfo } from '../../build/core/buildInfo.ts';
13
15
  import {
14
16
  buildLabel,
15
17
  buildStamp,
16
- buildSummary,
18
+ releasedVersion,
17
19
  } from '../../build/core/buildInfo.ts';
18
20
 
21
+ import { AboutBuildDetails } from './AboutBuildDetails.tsx';
22
+
19
23
  export interface AboutVersionProps {
20
24
  /** What the build published about itself, or `undefined` when it published none. */
21
25
  build: BuildInfo | undefined;
@@ -23,7 +27,8 @@ export interface AboutVersionProps {
23
27
 
24
28
  /**
25
29
  * The build badge: the release a site is serving, or the commit it was built
26
- * from while it has no release yet, and when it was made.
30
+ * from while it has no release yet, and when it was made. A release names its
31
+ * commit and the exact instant in the card its hover opens.
27
32
  * @param props - See {@link AboutVersionProps}.
28
33
  * @returns The badge, or `null` when the build says nothing worth showing.
29
34
  */
@@ -34,15 +39,28 @@ export function AboutVersion(props: AboutVersionProps): ReactElement | null {
34
39
  // The instant belongs on the badge rather than in its hover: a version
35
40
  // nobody can date says nothing about how old the page they read is.
36
41
  const badge = `${label} · ${buildStamp(build.builtAt)}`;
42
+ const version = releasedVersion(build);
43
+
44
+ if (version === undefined) {
45
+ return (
46
+ <span style={BADGE_STYLE} className="about-version">
47
+ {badge}
48
+ </span>
49
+ );
50
+ }
37
51
 
38
52
  return (
39
- <span
40
- style={BADGE_STYLE}
41
- className="about-version"
42
- title={buildSummary(build)}
53
+ <Tooltip
54
+ content={<AboutBuildDetails build={build} version={version} />}
55
+ // To the left, so the card never covers the way to sign in under it.
56
+ placement="left-start"
57
+ popoverClassName="help-tooltip build-tooltip"
58
+ hoverOpenDelay={150}
43
59
  >
44
- {badge}
45
- </span>
60
+ <span style={BADGE_STYLE} className="about-version" tabIndex={0}>
61
+ {badge}
62
+ </span>
63
+ </Tooltip>
46
64
  );
47
65
  }
48
66
 
@@ -1,5 +1,3 @@
1
- export type { AboutBuildProps } from './AboutBuild.tsx';
2
- export { AboutBuild } from './AboutBuild.tsx';
3
1
  export type { AboutVersionProps } from './AboutVersion.tsx';
4
2
  export { AboutVersion } from './AboutVersion.tsx';
5
3
  export type { AboutCitationsProps } from './AboutCitations.tsx';
@@ -10,3 +8,5 @@ export type { AboutProvidedByProps } from './AboutProvidedBy.tsx';
10
8
  export { AboutProvidedBy } from './AboutProvidedBy.tsx';
11
9
  export type { AboutSectionProps } from './AboutSection.tsx';
12
10
  export { AboutSection } from './AboutSection.tsx';
11
+ export type { AboutSignInProps } from './AboutSignIn.tsx';
12
+ export { AboutSignIn } from './AboutSignIn.tsx';
@@ -0,0 +1,41 @@
1
+ import type { ReactElement } from 'react';
2
+
3
+ import { NavLink } from './NavLink.tsx';
4
+ import { NavMenuButton } from './NavMenuButton.tsx';
5
+ import type { NavItem } from './navItem.ts';
6
+
7
+ /**
8
+ * One page of the bar: a link, or the menu it opens when the entry folds pages
9
+ * of its own. A menu stands where its pages would, among them and not among the
10
+ * utilities, because picking one of them changes what the page shows.
11
+ * @param props - The entry and the page on show.
12
+ * @param props.item - The entry.
13
+ * @param props.activeId - The page on show.
14
+ * @returns The entry.
15
+ */
16
+ export function NavEntry(props: {
17
+ item: NavItem;
18
+ activeId: string | undefined;
19
+ }): ReactElement {
20
+ const { item, activeId } = props;
21
+ if (item.items === undefined) {
22
+ return <NavLink item={item} active={item.id === activeId} />;
23
+ }
24
+
25
+ return (
26
+ <NavMenuButton
27
+ label={menuLabel(item)}
28
+ icon={typeof item.icon === 'string' ? item.icon : undefined}
29
+ items={item.items}
30
+ activeId={activeId}
31
+ placement="bottom-start"
32
+ />
33
+ );
34
+ }
35
+
36
+ // The trigger names itself to a screen reader, so an entry drawn as a glyph
37
+ // alone says what it opens through its title rather than through its id.
38
+ function menuLabel(item: NavItem): string {
39
+ if (typeof item.label === 'string') return item.label;
40
+ return item.title ?? item.id;
41
+ }
@@ -10,7 +10,7 @@ import { SiteMark } from '../../ecosystem/ui/marks.tsx';
10
10
  import { useLanguage } from '../../i18n/ui/useLanguage.ts';
11
11
  import { useChromeT } from '../../i18n/ui/useT.ts';
12
12
 
13
- import { NavLink } from './NavLink.tsx';
13
+ import { NavEntry } from './NavEntry.tsx';
14
14
  import { NavMenuButton } from './NavMenuButton.tsx';
15
15
  import type { NavItem } from './navItem.ts';
16
16
  import { isModifiedClick } from './navItem.ts';
@@ -42,13 +42,20 @@ export interface SiteHeaderProps {
42
42
  */
43
43
  activeId?: string;
44
44
  /**
45
- * The utilities pushed to the right edge — About, Cite, Tools, the language,
46
- * sign in; Share has a prop of its own, so it always comes last. They
47
- * arrive dressed as bar items, so a plain `nav-link` and a `CiteButton` read
48
- * alike beside each other.
45
+ * The utilities pushed to the right edge — About, Cite, Tools, the language;
46
+ * Share and the account have props of their own, so they always come last.
47
+ * They arrive dressed as bar items, so a plain `nav-link` and a `CiteButton`
48
+ * read alike beside each other.
49
49
  * @default undefined
50
50
  */
51
51
  actions?: ReactNode;
52
+ /**
53
+ * Who is signed in, drawn at the extreme right, after Share — usually an
54
+ * `AccountButton`. A site passes it only while somebody is signed in: signing
55
+ * in starts on the About (`AboutPage`'s `signIn`), never in the bar.
56
+ * @default undefined
57
+ */
58
+ account?: ReactNode;
52
59
  /**
53
60
  * The Share entry, which closes the utilities on every site, after the
54
61
  * Contact entry. A site passes its own when it wraps the shared `ShareButton`
@@ -133,6 +140,7 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
133
140
  activeId,
134
141
  actions,
135
142
  share,
143
+ account,
136
144
  contact,
137
145
  renderNavItem,
138
146
  embedded = false,
@@ -195,6 +203,7 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
195
203
  <span className="spacer" />
196
204
  {actions === undefined &&
197
205
  share === undefined &&
206
+ account === undefined &&
198
207
  contactForm === '' ? null : (
199
208
  <div className="app-header-actions">
200
209
  {actions}
@@ -202,6 +211,7 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
202
211
  <ContactButton siteId={site.id} formUrl={contactForm} />
203
212
  )}
204
213
  {share}
214
+ {account}
205
215
  </div>
206
216
  )}
207
217
  </div>
@@ -209,42 +219,6 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
209
219
  );
210
220
  }
211
221
 
212
- /**
213
- * One page of the bar: a link, or the menu it opens when the entry folds pages
214
- * of its own. A menu stands where its pages would, among them and not among the
215
- * utilities, because picking one of them changes what the page shows.
216
- * @param props - The entry and the page on show.
217
- * @param props.item - The entry.
218
- * @param props.activeId - The page on show.
219
- * @returns The entry.
220
- */
221
- function NavEntry(props: {
222
- item: NavItem;
223
- activeId: string | undefined;
224
- }): ReactElement {
225
- const { item, activeId } = props;
226
- if (item.items === undefined) {
227
- return <NavLink item={item} active={item.id === activeId} />;
228
- }
229
-
230
- return (
231
- <NavMenuButton
232
- label={menuLabel(item)}
233
- icon={typeof item.icon === 'string' ? item.icon : undefined}
234
- items={item.items}
235
- activeId={activeId}
236
- placement="bottom-start"
237
- />
238
- );
239
- }
240
-
241
- // The trigger names itself to a screen reader, so an entry drawn as a glyph
242
- // alone says what it opens through its title rather than through its id.
243
- function menuLabel(item: NavItem): string {
244
- if (typeof item.label === 'string') return item.label;
245
- return item.title ?? item.id;
246
- }
247
-
248
222
  /** The two widths the bar's contents run at, as the classes that set them. */
249
223
  const INNER_CLASS = {
250
224
  page: 'app-header__inner',
package/src/core.ts CHANGED
@@ -14,6 +14,7 @@ export * from './error/core/index.ts';
14
14
  export * from './format/core/index.ts';
15
15
  export * from './i18n/core/index.ts';
16
16
  export * from './language/core/index.ts';
17
+ export * from './moleculesSources/core/index.ts';
17
18
  export * from './moleculesToken/core/index.ts';
18
19
  export * from './number/core/index.ts';
19
20
  export * from './orbital/core/index.ts';
@@ -107,10 +107,11 @@
107
107
  "about.providedBy": "Bereitgestellt von",
108
108
  "about.whatYouCanDo": "Was Sie hier tun können",
109
109
  "about.builtOn": "Aufgebaut auf",
110
+ "about.buildVersion": "Version {version}",
111
+ "about.buildBuilt": "Erstellt",
112
+ "about.buildCommit": "Commit",
110
113
  "about.howToCite": "Wie zu zitieren",
111
114
  "about.citeWork": "{what} zitieren",
112
- "about.built": "Erstellt {builtAt}.",
113
- "about.builtFromCommit": "Erstellt {builtAt} aus Commit {commit}.",
114
115
  "about.nameList": "{names} und {last}",
115
116
  "help.label": "Hilfe",
116
117
  "help.learnMore": "Mehr erfahren",
@@ -618,5 +619,12 @@
618
619
  "moleculesToken.refusedDetail": "nie ausgestellt oder widerrufen",
619
620
  "moleculesToken.unsetDetail": "Setzen Sie {variable} in der .env des Servers.",
620
621
  "moleculesToken.uncheckableDetail": "Dieses molecules.cheminfo.org kann noch keine Tokens prüfen.",
621
- "moleculesToken.howToChange": "Ein Token wird in der .env des Servers geändert und beim Neustart des Dienstes neu gelesen. molecules.cheminfo.org stellt einen neuen aus mit"
622
+ "moleculesToken.howToChange": "Ein Token wird in der .env des Servers geändert und beim Neustart des Dienstes neu gelesen. molecules.cheminfo.org stellt einen neuen aus mit",
623
+ "moleculesToken.fix.title": "Setzen Sie ein gültiges Token in {variables}",
624
+ "moleculesToken.fix.issueOne": "Stellen Sie ein Token auf der Admin-Seite von molecules.cheminfo.org aus:",
625
+ "moleculesToken.fix.issueMany": "Stellen Sie ein Token pro Quelle auf der Admin-Seite von molecules.cheminfo.org aus:",
626
+ "moleculesToken.fix.issueOnServer": "Oder auf dem Server von molecules.cheminfo.org:",
627
+ "moleculesToken.fix.envOne": "Tragen Sie es in die .env des Servers dieser Website ein:",
628
+ "moleculesToken.fix.envMany": "Tragen Sie sie in die .env des Servers dieser Website ein:",
629
+ "moleculesToken.fix.restart": "Starten Sie die Dienste neu, da sie die .env nur beim Start lesen:"
622
630
  }
@@ -107,10 +107,11 @@
107
107
  "about.providedBy": "Provided by",
108
108
  "about.whatYouCanDo": "What you can do here",
109
109
  "about.builtOn": "Built on",
110
+ "about.buildVersion": "Version {version}",
111
+ "about.buildBuilt": "Built",
112
+ "about.buildCommit": "Commit",
110
113
  "about.howToCite": "How to cite",
111
114
  "about.citeWork": "Cite {what}",
112
- "about.built": "Built {builtAt}.",
113
- "about.builtFromCommit": "Built {builtAt} from commit {commit}.",
114
115
  "about.nameList": "{names} and {last}",
115
116
  "credits.openbabel.description": "reads and writes the chemical file formats, and does the conversion itself.",
116
117
  "credits.openchemlib.description": "reads and writes structures, and computes their properties in the browser.",
@@ -618,5 +619,12 @@
618
619
  "moleculesToken.refusedDetail": "never issued, or revoked",
619
620
  "moleculesToken.unsetDetail": "Set {variable} in the server's .env.",
620
621
  "moleculesToken.uncheckableDetail": "This molecules.cheminfo.org cannot check a token yet.",
621
- "moleculesToken.howToChange": "A token is changed in the server's .env and read again when the service restarts. molecules.cheminfo.org issues a new one with"
622
+ "moleculesToken.howToChange": "A token is changed in the server's .env and read again when the service restarts. molecules.cheminfo.org issues a new one with",
623
+ "moleculesToken.fix.title": "Set a valid token in {variables}",
624
+ "moleculesToken.fix.issueOne": "Issue a token on the Admin page of molecules.cheminfo.org:",
625
+ "moleculesToken.fix.issueMany": "Issue one token per source on the Admin page of molecules.cheminfo.org:",
626
+ "moleculesToken.fix.issueOnServer": "Or, on the server of molecules.cheminfo.org:",
627
+ "moleculesToken.fix.envOne": "Write it in the .env of this site's server:",
628
+ "moleculesToken.fix.envMany": "Write them in the .env of this site's server:",
629
+ "moleculesToken.fix.restart": "Restart the services, which read the .env only when they start:"
622
630
  }
@@ -107,10 +107,11 @@
107
107
  "about.providedBy": "Ofrecido por",
108
108
  "about.whatYouCanDo": "Lo que puede hacer aquí",
109
109
  "about.builtOn": "Construido sobre",
110
+ "about.buildVersion": "Versión {version}",
111
+ "about.buildBuilt": "Compilado",
112
+ "about.buildCommit": "Commit",
110
113
  "about.howToCite": "Cómo citar",
111
114
  "about.citeWork": "Citar {what}",
112
- "about.built": "Compilado {builtAt}.",
113
- "about.builtFromCommit": "Compilado {builtAt} desde el commit {commit}.",
114
115
  "about.nameList": "{names} y {last}",
115
116
  "help.label": "Ayuda",
116
117
  "help.learnMore": "Saber más",
@@ -618,5 +619,12 @@
618
619
  "moleculesToken.refusedDetail": "nunca emitido, o revocado",
619
620
  "moleculesToken.unsetDetail": "Defina {variable} en el .env del servidor.",
620
621
  "moleculesToken.uncheckableDetail": "Este molecules.cheminfo.org aún no puede comprobar un token.",
621
- "moleculesToken.howToChange": "Un token se cambia en el .env del servidor y se vuelve a leer cuando el servicio se reinicia. molecules.cheminfo.org emite uno nuevo con"
622
+ "moleculesToken.howToChange": "Un token se cambia en el .env del servidor y se vuelve a leer cuando el servicio se reinicia. molecules.cheminfo.org emite uno nuevo con",
623
+ "moleculesToken.fix.title": "Defina un token válido en {variables}",
624
+ "moleculesToken.fix.issueOne": "Emita un token en la página Admin de molecules.cheminfo.org:",
625
+ "moleculesToken.fix.issueMany": "Emita un token por fuente en la página Admin de molecules.cheminfo.org:",
626
+ "moleculesToken.fix.issueOnServer": "O, en el servidor de molecules.cheminfo.org:",
627
+ "moleculesToken.fix.envOne": "Escríbalo en el .env del servidor de este sitio:",
628
+ "moleculesToken.fix.envMany": "Escríbalos en el .env del servidor de este sitio:",
629
+ "moleculesToken.fix.restart": "Reinicie los servicios, que solo leen el .env al arrancar:"
622
630
  }
@@ -107,10 +107,11 @@
107
107
  "about.providedBy": "Proposé par",
108
108
  "about.whatYouCanDo": "Ce que vous pouvez faire ici",
109
109
  "about.builtOn": "Construit sur",
110
+ "about.buildVersion": "Version {version}",
111
+ "about.buildBuilt": "Compilé",
112
+ "about.buildCommit": "Commit",
110
113
  "about.howToCite": "Comment citer",
111
114
  "about.citeWork": "Citer {what}",
112
- "about.built": "Compilé {builtAt}.",
113
- "about.builtFromCommit": "Compilé {builtAt} depuis le commit {commit}.",
114
115
  "about.nameList": "{names} et {last}",
115
116
  "help.label": "Aide",
116
117
  "help.learnMore": "En savoir plus",
@@ -618,5 +619,12 @@
618
619
  "moleculesToken.refusedDetail": "jamais émis, ou révoqué",
619
620
  "moleculesToken.unsetDetail": "Définissez {variable} dans le fichier .env du serveur.",
620
621
  "moleculesToken.uncheckableDetail": "Ce molecules.cheminfo.org ne sait pas encore vérifier un jeton.",
621
- "moleculesToken.howToChange": "Un jeton se change dans le fichier .env du serveur, et est relu au redémarrage du service. molecules.cheminfo.org en émet un nouveau avec"
622
+ "moleculesToken.howToChange": "Un jeton se change dans le fichier .env du serveur, et est relu au redémarrage du service. molecules.cheminfo.org en émet un nouveau avec",
623
+ "moleculesToken.fix.title": "Définissez un jeton valide dans {variables}",
624
+ "moleculesToken.fix.issueOne": "Émettez un jeton sur la page Admin de molecules.cheminfo.org :",
625
+ "moleculesToken.fix.issueMany": "Émettez un jeton par source sur la page Admin de molecules.cheminfo.org :",
626
+ "moleculesToken.fix.issueOnServer": "Ou, sur le serveur de molecules.cheminfo.org :",
627
+ "moleculesToken.fix.envOne": "Écrivez-le dans le fichier .env du serveur de ce site :",
628
+ "moleculesToken.fix.envMany": "Écrivez-les dans le fichier .env du serveur de ce site :",
629
+ "moleculesToken.fix.restart": "Redémarrez les services, qui ne lisent le fichier .env qu'à leur démarrage :"
622
630
  }
@@ -107,10 +107,11 @@
107
107
  "about.providedBy": "Offerto da",
108
108
  "about.whatYouCanDo": "Cosa si può fare qui",
109
109
  "about.builtOn": "Costruito su",
110
+ "about.buildVersion": "Versione {version}",
111
+ "about.buildBuilt": "Compilato",
112
+ "about.buildCommit": "Commit",
110
113
  "about.howToCite": "Come citare",
111
114
  "about.citeWork": "Citare {what}",
112
- "about.built": "Compilato {builtAt}.",
113
- "about.builtFromCommit": "Compilato {builtAt} dal commit {commit}.",
114
115
  "about.nameList": "{names} e {last}",
115
116
  "credits.openbabel.description": "legge e scrive i formati di file chimici, ed esegue la conversione stessa.",
116
117
  "credits.openchemlib.description": "legge e scrive strutture, e ne calcola le proprietà nel browser.",
@@ -618,5 +619,12 @@
618
619
  "moleculesToken.refusedDetail": "mai emesso, o revocato",
619
620
  "moleculesToken.unsetDetail": "Impostare {variable} nel file .env del server.",
620
621
  "moleculesToken.uncheckableDetail": "Questo molecules.cheminfo.org non sa ancora verificare un token.",
621
- "moleculesToken.howToChange": "Un token si cambia nel file .env del server e viene riletto al riavvio del servizio. molecules.cheminfo.org ne emette uno nuovo con"
622
+ "moleculesToken.howToChange": "Un token si cambia nel file .env del server e viene riletto al riavvio del servizio. molecules.cheminfo.org ne emette uno nuovo con",
623
+ "moleculesToken.fix.title": "Impostare un token valido in {variables}",
624
+ "moleculesToken.fix.issueOne": "Emettere un token nella pagina Admin di molecules.cheminfo.org:",
625
+ "moleculesToken.fix.issueMany": "Emettere un token per fonte nella pagina Admin di molecules.cheminfo.org:",
626
+ "moleculesToken.fix.issueOnServer": "Oppure, sul server di molecules.cheminfo.org:",
627
+ "moleculesToken.fix.envOne": "Scriverlo nel file .env del server di questo sito:",
628
+ "moleculesToken.fix.envMany": "Scriverli nel file .env del server di questo sito:",
629
+ "moleculesToken.fix.restart": "Riavviare i servizi, che leggono il file .env solo all'avvio:"
622
630
  }
@@ -0,0 +1,11 @@
1
+ export type {
2
+ FetchSourceLooksOptions,
3
+ SourceLook,
4
+ SourceLooks,
5
+ } from './moleculesSources.ts';
6
+ export {
7
+ MOLECULES_URL,
8
+ fetchSourceLooks,
9
+ readSourceLooks,
10
+ sourceLook,
11
+ } from './moleculesSources.ts';
@@ -0,0 +1,102 @@
1
+ /** Where molecules.cheminfo.org answers, which holds the family's sources. */
2
+ export const MOLECULES_URL = 'https://molecules.cheminfo.org';
3
+
4
+ /**
5
+ * How a source is drawn wherever the family names it — PubChem, ChEBI, COCONUT
6
+ * — as molecules.cheminfo.org publishes it. molecules is where a source's
7
+ * colour and logo are kept and edited, so every site reads them from there
8
+ * rather than keeping a copy that drifts the first time one is changed.
9
+ */
10
+ export interface SourceLook {
11
+ /** What the source is called, as molecules writes it: `ChEBI`. */
12
+ name: string;
13
+ /** The colour its badges are drawn in, as `#rrggbb`; null for none. */
14
+ color: string | null;
15
+ /** The absolute address of its little logo; null when it has none. */
16
+ logo: string | null;
17
+ }
18
+
19
+ /** Every source molecules publishes, by its name in lower case. */
20
+ export type SourceLooks = ReadonlyMap<string, SourceLook>;
21
+
22
+ /**
23
+ * The sources a `/v1/sources` answer describes, each logo made absolute
24
+ * against the address it was read from, so the badge draws it on any site.
25
+ * Anything not shaped like a source is left out rather than trusted.
26
+ * @param body - The parsed answer of `/v1/sources`.
27
+ * @param baseUrl - Where it was read from.
28
+ * @returns The looks, by lower-case name.
29
+ */
30
+ export function readSourceLooks(body: unknown, baseUrl: string): SourceLooks {
31
+ const looks = new Map<string, SourceLook>();
32
+ const sources = isRecord(body) ? body.sources : undefined;
33
+ if (!Array.isArray(sources)) return looks;
34
+ for (const source of sources) {
35
+ if (!isRecord(source) || typeof source.name !== 'string') continue;
36
+ const { name, color, logo } = source;
37
+ looks.set(name.toLowerCase(), {
38
+ name,
39
+ color: typeof color === 'string' && HEX_COLOR.test(color) ? color : null,
40
+ logo: typeof logo === 'string' ? absolute(logo, baseUrl) : null,
41
+ });
42
+ }
43
+ return looks;
44
+ }
45
+
46
+ /**
47
+ * The look of one source, named in any case.
48
+ * @param looks - What molecules publishes, or null while it is being read.
49
+ * @param name - The source's name or key: `chebi`, `ChEBI`.
50
+ * @returns Its look, or `undefined` when molecules does not know it (yet).
51
+ */
52
+ export function sourceLook(
53
+ looks: SourceLooks | null,
54
+ name: string,
55
+ ): SourceLook | undefined {
56
+ return looks?.get(name.toLowerCase());
57
+ }
58
+
59
+ /** Where and how {@link fetchSourceLooks} reads. */
60
+ export interface FetchSourceLooksOptions {
61
+ /**
62
+ * Where molecules answers.
63
+ * @default MOLECULES_URL
64
+ */
65
+ url?: string;
66
+ /**
67
+ * Aborts the read.
68
+ * @default undefined
69
+ */
70
+ signal?: AbortSignal;
71
+ }
72
+
73
+ /**
74
+ * Read every source's look from molecules.cheminfo.org.
75
+ * @param options - See {@link FetchSourceLooksOptions}.
76
+ * @returns The looks, by lower-case name.
77
+ * @throws {Error} When molecules does not answer, or answers with an error.
78
+ */
79
+ export async function fetchSourceLooks(
80
+ options: FetchSourceLooksOptions = {},
81
+ ): Promise<SourceLooks> {
82
+ const { url = MOLECULES_URL, signal } = options;
83
+ const response = await fetch(`${url}/v1/sources`, { signal });
84
+ if (!response.ok) {
85
+ throw new Error(`${url} answered ${response.status} for its sources`);
86
+ }
87
+ return readSourceLooks(await response.json(), url);
88
+ }
89
+
90
+ const HEX_COLOR = /^#[\da-f]{6}$/i;
91
+
92
+ function absolute(address: string, baseUrl: string): string | null {
93
+ try {
94
+ return new URL(address, baseUrl).href;
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+
100
+ function isRecord(value: unknown): value is Record<string, unknown> {
101
+ return typeof value === 'object' && value !== null;
102
+ }