react-cheminfo 0.14.0 → 0.16.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 +175 -21
- package/lib/about/core/about.d.ts +13 -7
- package/lib/about/core/about.d.ts.map +1 -1
- package/lib/about/core/about.js +4 -2
- package/lib/about/core/about.js.map +1 -1
- package/lib/about/ui/AboutBuild.d.ts +20 -0
- package/lib/about/ui/AboutBuild.d.ts.map +1 -0
- package/lib/about/ui/AboutBuild.js +38 -0
- package/lib/about/ui/AboutBuild.js.map +1 -0
- package/lib/about/ui/AboutPage.d.ts +7 -0
- package/lib/about/ui/AboutPage.d.ts.map +1 -1
- package/lib/about/ui/AboutPage.js +5 -2
- package/lib/about/ui/AboutPage.js.map +1 -1
- package/lib/about/ui/index.d.ts +2 -0
- package/lib/about/ui/index.d.ts.map +1 -1
- package/lib/about/ui/index.js +1 -0
- package/lib/about/ui/index.js.map +1 -1
- package/lib/build/core/buildInfo.d.ts +39 -0
- package/lib/build/core/buildInfo.d.ts.map +1 -0
- package/lib/build/core/buildInfo.js +20 -0
- package/lib/build/core/buildInfo.js.map +1 -0
- package/lib/build/core/current.d.ts +12 -0
- package/lib/build/core/current.d.ts.map +1 -0
- package/lib/build/core/current.js +11 -0
- package/lib/build/core/current.js.map +1 -0
- package/lib/build/core/index.d.ts +3 -0
- package/lib/build/core/index.d.ts.map +1 -0
- package/lib/build/core/index.js +2 -0
- package/lib/build/core/index.js.map +1 -0
- package/lib/build/vite/buildInfo.d.ts +57 -0
- package/lib/build/vite/buildInfo.d.ts.map +1 -0
- package/lib/build/vite/buildInfo.js +151 -0
- package/lib/build/vite/buildInfo.js.map +1 -0
- package/lib/build/vite/gitHead.d.ts +29 -0
- package/lib/build/vite/gitHead.d.ts.map +1 -0
- package/lib/build/vite/gitHead.js +102 -0
- package/lib/build/vite/gitHead.js.map +1 -0
- package/lib/build/vite/index.d.ts +4 -0
- package/lib/build/vite/index.d.ts.map +1 -0
- package/lib/build/vite/index.js +3 -0
- package/lib/build/vite/index.js.map +1 -0
- package/lib/build-info.d.ts +3 -0
- package/lib/build-info.d.ts.map +1 -0
- package/lib/build-info.js +2 -0
- package/lib/build-info.js.map +1 -0
- package/lib/core.d.ts +2 -0
- package/lib/core.d.ts.map +1 -1
- package/lib/core.js +2 -0
- package/lib/core.js.map +1 -1
- package/lib/credits/core/credits.d.ts +35 -0
- package/lib/credits/core/credits.d.ts.map +1 -1
- package/lib/credits/core/credits.js +41 -0
- package/lib/credits/core/credits.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +40 -0
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/core/types.d.ts +1 -1
- package/lib/ecosystem/core/types.d.ts.map +1 -1
- package/lib/ecosystem/ui/Wordmark.d.ts +14 -3
- package/lib/ecosystem/ui/Wordmark.d.ts.map +1 -1
- package/lib/ecosystem/ui/Wordmark.js +8 -4
- package/lib/ecosystem/ui/Wordmark.js.map +1 -1
- package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +11 -0
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/parallel/core/index.d.ts +27 -0
- package/lib/parallel/core/index.d.ts.map +1 -0
- package/lib/parallel/core/index.js +20 -0
- package/lib/parallel/core/index.js.map +1 -0
- package/lib/parallel/core/parallelAxes.d.ts +103 -0
- package/lib/parallel/core/parallelAxes.d.ts.map +1 -0
- package/lib/parallel/core/parallelAxes.js +146 -0
- package/lib/parallel/core/parallelAxes.js.map +1 -0
- package/lib/parallel/core/parallelBrush.d.ts +79 -0
- package/lib/parallel/core/parallelBrush.d.ts.map +1 -0
- package/lib/parallel/core/parallelBrush.js +99 -0
- package/lib/parallel/core/parallelBrush.js.map +1 -0
- package/lib/parallel/core/parallelExtent.d.ts +28 -0
- package/lib/parallel/core/parallelExtent.d.ts.map +1 -0
- package/lib/parallel/core/parallelExtent.js +47 -0
- package/lib/parallel/core/parallelExtent.js.map +1 -0
- package/lib/parallel/core/parallelFilter.d.ts +36 -0
- package/lib/parallel/core/parallelFilter.d.ts.map +1 -0
- package/lib/parallel/core/parallelFilter.js +74 -0
- package/lib/parallel/core/parallelFilter.js.map +1 -0
- package/lib/parallel/core/parallelHit.d.ts +47 -0
- package/lib/parallel/core/parallelHit.d.ts.map +1 -0
- package/lib/parallel/core/parallelHit.js +81 -0
- package/lib/parallel/core/parallelHit.js.map +1 -0
- package/lib/parallel/core/parallelPaint.d.ts +104 -0
- package/lib/parallel/core/parallelPaint.d.ts.map +1 -0
- package/lib/parallel/core/parallelPaint.js +160 -0
- package/lib/parallel/core/parallelPaint.js.map +1 -0
- package/lib/parallel/core/parallelPalette.d.ts +31 -0
- package/lib/parallel/core/parallelPalette.d.ts.map +1 -0
- package/lib/parallel/core/parallelPalette.js +68 -0
- package/lib/parallel/core/parallelPalette.js.map +1 -0
- package/lib/parallel/core/parallelTicks.d.ts +34 -0
- package/lib/parallel/core/parallelTicks.d.ts.map +1 -0
- package/lib/parallel/core/parallelTicks.js +116 -0
- package/lib/parallel/core/parallelTicks.js.map +1 -0
- package/lib/parallel/core/parallelTypes.d.ts +129 -0
- package/lib/parallel/core/parallelTypes.d.ts.map +1 -0
- package/lib/parallel/core/parallelTypes.js +29 -0
- package/lib/parallel/core/parallelTypes.js.map +1 -0
- package/lib/parallel/ui/ParallelAxesLayer.d.ts +28 -0
- package/lib/parallel/ui/ParallelAxesLayer.d.ts.map +1 -0
- package/lib/parallel/ui/ParallelAxesLayer.js +29 -0
- package/lib/parallel/ui/ParallelAxesLayer.js.map +1 -0
- package/lib/parallel/ui/ParallelCoordinates.d.ts +21 -0
- package/lib/parallel/ui/ParallelCoordinates.d.ts.map +1 -0
- package/lib/parallel/ui/ParallelCoordinates.js +109 -0
- package/lib/parallel/ui/ParallelCoordinates.js.map +1 -0
- package/lib/parallel/ui/ParallelLabels.d.ts +26 -0
- package/lib/parallel/ui/ParallelLabels.d.ts.map +1 -0
- package/lib/parallel/ui/ParallelLabels.js +25 -0
- package/lib/parallel/ui/ParallelLabels.js.map +1 -0
- package/lib/parallel/ui/ParallelTooltip.d.ts +26 -0
- package/lib/parallel/ui/ParallelTooltip.d.ts.map +1 -0
- package/lib/parallel/ui/ParallelTooltip.js +30 -0
- package/lib/parallel/ui/ParallelTooltip.js.map +1 -0
- package/lib/parallel/ui/index.d.ts +25 -0
- package/lib/parallel/ui/index.d.ts.map +1 -0
- package/lib/parallel/ui/index.js +18 -0
- package/lib/parallel/ui/index.js.map +1 -0
- package/lib/parallel/ui/parallelCoordinatesProps.d.ts +185 -0
- package/lib/parallel/ui/parallelCoordinatesProps.d.ts.map +1 -0
- package/lib/parallel/ui/parallelCoordinatesProps.js +11 -0
- package/lib/parallel/ui/parallelCoordinatesProps.js.map +1 -0
- package/lib/parallel/ui/parallelGestureModel.d.ts +52 -0
- package/lib/parallel/ui/parallelGestureModel.d.ts.map +1 -0
- package/lib/parallel/ui/parallelGestureModel.js +80 -0
- package/lib/parallel/ui/parallelGestureModel.js.map +1 -0
- package/lib/parallel/ui/parallelInk.d.ts +76 -0
- package/lib/parallel/ui/parallelInk.d.ts.map +1 -0
- package/lib/parallel/ui/parallelInk.js +113 -0
- package/lib/parallel/ui/parallelInk.js.map +1 -0
- package/lib/parallel/ui/parallelPlotModel.d.ts +55 -0
- package/lib/parallel/ui/parallelPlotModel.d.ts.map +1 -0
- package/lib/parallel/ui/parallelPlotModel.js +113 -0
- package/lib/parallel/ui/parallelPlotModel.js.map +1 -0
- package/lib/parallel/ui/parallelStyles.d.ts +83 -0
- package/lib/parallel/ui/parallelStyles.d.ts.map +1 -0
- package/lib/parallel/ui/parallelStyles.js +131 -0
- package/lib/parallel/ui/parallelStyles.js.map +1 -0
- package/lib/parallel/ui/useParallelCanvas.d.ts +49 -0
- package/lib/parallel/ui/useParallelCanvas.d.ts.map +1 -0
- package/lib/parallel/ui/useParallelCanvas.js +58 -0
- package/lib/parallel/ui/useParallelCanvas.js.map +1 -0
- package/lib/parallel/ui/useParallelGesture.d.ts +67 -0
- package/lib/parallel/ui/useParallelGesture.d.ts.map +1 -0
- package/lib/parallel/ui/useParallelGesture.js +139 -0
- package/lib/parallel/ui/useParallelGesture.js.map +1 -0
- package/lib/structure/ui/useToolbarFloor.d.ts.map +1 -1
- package/lib/structure/ui/useToolbarFloor.js +29 -22
- package/lib/structure/ui/useToolbarFloor.js.map +1 -1
- package/lib/ui.d.ts +1 -0
- package/lib/ui.d.ts.map +1 -1
- package/lib/ui.js +1 -0
- package/lib/ui.js.map +1 -1
- package/lib/vite.d.ts +1 -0
- package/lib/vite.d.ts.map +1 -1
- package/lib/vite.js +1 -0
- package/lib/vite.js.map +1 -1
- package/package.json +2 -1
- package/src/about/core/about.ts +18 -9
- package/src/about/ui/AboutBuild.tsx +83 -0
- package/src/about/ui/AboutPage.tsx +15 -7
- package/src/about/ui/index.ts +2 -0
- package/src/build/core/buildInfo.ts +48 -0
- package/src/build/core/current.ts +12 -0
- package/src/build/core/index.ts +2 -0
- package/src/build/vite/buildInfo.ts +178 -0
- package/src/build/vite/gitHead.ts +114 -0
- package/src/build/vite/index.ts +8 -0
- package/src/build-info.ts +2 -0
- package/src/core.ts +2 -0
- package/src/credits/core/credits.ts +42 -0
- package/src/ecosystem/core/sites.ts +40 -0
- package/src/ecosystem/core/types.ts +4 -1
- package/src/ecosystem/ui/Wordmark.tsx +22 -7
- package/src/ecosystem/ui/glyphs.tsx +46 -0
- package/src/parallel/core/index.ts +80 -0
- package/src/parallel/core/parallelAxes.ts +207 -0
- package/src/parallel/core/parallelBrush.ts +145 -0
- package/src/parallel/core/parallelExtent.ts +56 -0
- package/src/parallel/core/parallelFilter.ts +94 -0
- package/src/parallel/core/parallelHit.ts +106 -0
- package/src/parallel/core/parallelPaint.ts +231 -0
- package/src/parallel/core/parallelPalette.ts +80 -0
- package/src/parallel/core/parallelTicks.ts +164 -0
- package/src/parallel/core/parallelTypes.ts +147 -0
- package/src/parallel/ui/ParallelAxesLayer.tsx +123 -0
- package/src/parallel/ui/ParallelCoordinates.tsx +233 -0
- package/src/parallel/ui/ParallelLabels.tsx +56 -0
- package/src/parallel/ui/ParallelTooltip.tsx +55 -0
- package/src/parallel/ui/index.ts +50 -0
- package/src/parallel/ui/parallelCoordinatesProps.ts +199 -0
- package/src/parallel/ui/parallelGestureModel.ts +109 -0
- package/src/parallel/ui/parallelInk.ts +182 -0
- package/src/parallel/ui/parallelPlotModel.ts +130 -0
- package/src/parallel/ui/parallelStyles.ts +146 -0
- package/src/parallel/ui/useParallelCanvas.ts +104 -0
- package/src/parallel/ui/useParallelGesture.ts +227 -0
- package/src/structure/ui/useToolbarFloor.ts +27 -22
- package/src/ui.ts +1 -0
- package/src/vite.ts +1 -0
package/src/about/core/about.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BuildInfo } from '../../build/core/buildInfo.ts';
|
|
1
2
|
import type { CitedWork } from '../../citation/core/works.ts';
|
|
2
3
|
import type { CreditEntry, CreditId } from '../../credits/core/credits.ts';
|
|
3
4
|
import { credits } from '../../credits/core/credits.ts';
|
|
@@ -17,8 +18,12 @@ import { providers } from './providers.ts';
|
|
|
17
18
|
* each crediting a different half of what it runs on.
|
|
18
19
|
*/
|
|
19
20
|
export interface AboutContent {
|
|
20
|
-
/**
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The site the page belongs to: its id, or the record itself for a site that
|
|
23
|
+
* is deliberately not listed in the family's Tools menu and therefore not in
|
|
24
|
+
* `ECOSYSTEM_SITES`.
|
|
25
|
+
*/
|
|
26
|
+
siteId: SiteId | EcosystemSite;
|
|
22
27
|
/** One sentence: what this tool is. */
|
|
23
28
|
what: string;
|
|
24
29
|
/**
|
|
@@ -59,10 +64,11 @@ export interface AboutContent {
|
|
|
59
64
|
*/
|
|
60
65
|
repository?: string;
|
|
61
66
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
67
|
+
* Which build is running, from `virtual:cheminfo-build-info` — never written
|
|
68
|
+
* by hand, which is what keeps it true after the next release.
|
|
69
|
+
* @default undefined — the site's build does not publish one
|
|
64
70
|
*/
|
|
65
|
-
|
|
71
|
+
build?: BuildInfo;
|
|
66
72
|
/**
|
|
67
73
|
* Where a problem is reported.
|
|
68
74
|
* @default the repository's /issues
|
|
@@ -102,8 +108,8 @@ export interface ResolvedAbout {
|
|
|
102
108
|
providedBy: ProviderEntry[];
|
|
103
109
|
license: string;
|
|
104
110
|
repository: string;
|
|
105
|
-
/**
|
|
106
|
-
|
|
111
|
+
/** Which build is running, or `undefined` when the site does not say. */
|
|
112
|
+
build: BuildInfo | undefined;
|
|
107
113
|
issues: string;
|
|
108
114
|
}
|
|
109
115
|
|
|
@@ -116,7 +122,10 @@ export interface ResolvedAbout {
|
|
|
116
122
|
* credits nobody.
|
|
117
123
|
*/
|
|
118
124
|
export function resolveAbout(content: AboutContent): ResolvedAbout {
|
|
119
|
-
const site =
|
|
125
|
+
const site =
|
|
126
|
+
typeof content.siteId === 'string'
|
|
127
|
+
? siteById(content.siteId)
|
|
128
|
+
: content.siteId;
|
|
120
129
|
const repository = content.repository ?? site.repository;
|
|
121
130
|
|
|
122
131
|
return {
|
|
@@ -130,7 +139,7 @@ export function resolveAbout(content: AboutContent): ResolvedAbout {
|
|
|
130
139
|
providedBy: providers(content.providedBy ?? []),
|
|
131
140
|
license: content.license ?? DEFAULT_LICENSE,
|
|
132
141
|
repository,
|
|
133
|
-
|
|
142
|
+
build: content.build,
|
|
134
143
|
issues: content.issues ?? `${withoutTrailingSlash(repository)}/issues`,
|
|
135
144
|
};
|
|
136
145
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { CSSProperties, ReactElement, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { BuildInfo } from '../../build/core/buildInfo.ts';
|
|
4
|
+
import { formatBuiltAt, shortCommit } from '../../build/core/buildInfo.ts';
|
|
5
|
+
|
|
6
|
+
export interface AboutBuildProps {
|
|
7
|
+
/** What the build published about itself. */
|
|
8
|
+
build: BuildInfo;
|
|
9
|
+
/** Where the sources live, which is what the version and commit link into. */
|
|
10
|
+
repository: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The one line that says which build of the site a visitor has open.
|
|
15
|
+
*
|
|
16
|
+
* A report of something going wrong is worth answering only when we know what
|
|
17
|
+
* was running, and a visitor cannot read that off the page otherwise. The
|
|
18
|
+
* version and the commit link into the repository, so the change that caused a
|
|
19
|
+
* problem is one click from the page that shows it.
|
|
20
|
+
* @param props - See {@link AboutBuildProps}.
|
|
21
|
+
* @returns The line.
|
|
22
|
+
*/
|
|
23
|
+
export function AboutBuild(props: AboutBuildProps): ReactElement {
|
|
24
|
+
const { build, repository } = props;
|
|
25
|
+
const sources = repository.endsWith('/')
|
|
26
|
+
? repository.slice(0, -1)
|
|
27
|
+
: repository;
|
|
28
|
+
const onGitHub = sources.startsWith(GITHUB);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<p style={PARAGRAPH_STYLE}>
|
|
32
|
+
Running version{' '}
|
|
33
|
+
<Reference
|
|
34
|
+
href={
|
|
35
|
+
onGitHub ? `${sources}/releases/tag/v${build.version}` : undefined
|
|
36
|
+
}
|
|
37
|
+
>
|
|
38
|
+
{build.version}
|
|
39
|
+
</Reference>
|
|
40
|
+
, built {formatBuiltAt(build.builtAt)}
|
|
41
|
+
{build.commit === undefined ? null : (
|
|
42
|
+
<>
|
|
43
|
+
{' from commit '}
|
|
44
|
+
<Reference
|
|
45
|
+
href={onGitHub ? `${sources}/commit/${build.commit}` : undefined}
|
|
46
|
+
>
|
|
47
|
+
{shortCommit(build.commit)}
|
|
48
|
+
</Reference>
|
|
49
|
+
</>
|
|
50
|
+
)}
|
|
51
|
+
.
|
|
52
|
+
</p>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function Reference(props: {
|
|
57
|
+
href: string | undefined;
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}): ReactElement {
|
|
60
|
+
const { href, children } = props;
|
|
61
|
+
if (href === undefined) return <code style={CODE_STYLE}>{children}</code>;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<a href={href} target="_blank" rel="noopener noreferrer" style={LINK_STYLE}>
|
|
65
|
+
<code style={CODE_STYLE}>{children}</code>
|
|
66
|
+
</a>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const GITHUB = 'https://github.com/';
|
|
71
|
+
|
|
72
|
+
const PARAGRAPH_STYLE = {
|
|
73
|
+
margin: '8px 0 0',
|
|
74
|
+
color: 'var(--text-muted)',
|
|
75
|
+
fontSize: 13,
|
|
76
|
+
} as const satisfies CSSProperties;
|
|
77
|
+
|
|
78
|
+
const CODE_STYLE = {
|
|
79
|
+
fontFamily: 'var(--font-mono, ui-monospace, monospace)',
|
|
80
|
+
fontSize: 13,
|
|
81
|
+
} as const satisfies CSSProperties;
|
|
82
|
+
|
|
83
|
+
const LINK_STYLE = { color: 'var(--accent)' } as const satisfies CSSProperties;
|
|
@@ -6,6 +6,7 @@ import { SiteMark } from '../../ecosystem/ui/marks.tsx';
|
|
|
6
6
|
import type { AboutContent } from '../core/about.ts';
|
|
7
7
|
import { resolveAbout } from '../core/about.ts';
|
|
8
8
|
|
|
9
|
+
import { AboutBuild } from './AboutBuild.tsx';
|
|
9
10
|
import { AboutCitations } from './AboutCitations.tsx';
|
|
10
11
|
import { AboutProvidedBy } from './AboutProvidedBy.tsx';
|
|
11
12
|
import { AboutSection } from './AboutSection.tsx';
|
|
@@ -25,6 +26,13 @@ export interface AboutPageProps {
|
|
|
25
26
|
* @default undefined
|
|
26
27
|
*/
|
|
27
28
|
children?: ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* The site's own mark, for a site the shared glyph set does not hold —
|
|
31
|
+
* one that is deliberately not listed in `ECOSYSTEM_SITES`. It stands where
|
|
32
|
+
* `SiteMark` would, and the hero is otherwise unchanged.
|
|
33
|
+
* @default undefined — the family's mark for that site
|
|
34
|
+
*/
|
|
35
|
+
mark?: ReactNode;
|
|
28
36
|
/**
|
|
29
37
|
* The site's own drawn lockup, for a site that has one. It stands in the
|
|
30
38
|
* hero in place of the mark and the written name, above the tagline, and
|
|
@@ -48,7 +56,7 @@ export interface AboutPageProps {
|
|
|
48
56
|
* @throws {Error} When the record names a site or a credit that does not exist.
|
|
49
57
|
*/
|
|
50
58
|
export function AboutPage(props: AboutPageProps): ReactElement {
|
|
51
|
-
const { content, className, children, logo } = props;
|
|
59
|
+
const { content, className, children, logo, mark } = props;
|
|
52
60
|
const about = resolveAbout(content);
|
|
53
61
|
const site = about.site;
|
|
54
62
|
|
|
@@ -63,10 +71,12 @@ export function AboutPage(props: AboutPageProps): ReactElement {
|
|
|
63
71
|
className="about-hero"
|
|
64
72
|
style={logo === undefined ? HERO_STYLE : LOGO_HERO_STYLE}
|
|
65
73
|
>
|
|
66
|
-
{logo === undefined
|
|
74
|
+
{logo === undefined
|
|
75
|
+
? (mark ?? <SiteMark site={site} size={56} />)
|
|
76
|
+
: null}
|
|
67
77
|
<div>
|
|
68
78
|
<h1 style={logo === undefined ? NAME_STYLE : LOGO_NAME_STYLE}>
|
|
69
|
-
{logo ?? <Wordmark
|
|
79
|
+
{logo ?? <Wordmark site={site} size={26} />}
|
|
70
80
|
</h1>
|
|
71
81
|
<p style={TAGLINE_STYLE}>{site.tagline}</p>
|
|
72
82
|
<p style={WHAT_STYLE}>{about.what}</p>
|
|
@@ -118,10 +128,8 @@ export function AboutPage(props: AboutPageProps): ReactElement {
|
|
|
118
128
|
piece of it into something else. The sources are at{' '}
|
|
119
129
|
<ExternalLink href={about.repository} />.
|
|
120
130
|
</p>
|
|
121
|
-
{about.
|
|
122
|
-
<
|
|
123
|
-
This page is running version {about.version}.
|
|
124
|
-
</p>
|
|
131
|
+
{about.build === undefined ? null : (
|
|
132
|
+
<AboutBuild build={about.build} repository={about.repository} />
|
|
125
133
|
)}
|
|
126
134
|
</AboutSection>
|
|
127
135
|
|
package/src/about/ui/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type { AboutBuildProps } from './AboutBuild.tsx';
|
|
2
|
+
export { AboutBuild } from './AboutBuild.tsx';
|
|
1
3
|
export type { AboutCitationsProps } from './AboutCitations.tsx';
|
|
2
4
|
export { AboutCitations } from './AboutCitations.tsx';
|
|
3
5
|
export type { AboutPageProps } from './AboutPage.tsx';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a deployed page can say about itself: which release it is, when it was
|
|
3
|
+
* built, and from which commit.
|
|
4
|
+
*
|
|
5
|
+
* A site of ours is a link somebody handed out in a course, so the first
|
|
6
|
+
* question a report raises is *which one of these is running*. The record is
|
|
7
|
+
* filled by the build rather than written by hand — a version a human types
|
|
8
|
+
* into a source file is a version that is wrong by the next release.
|
|
9
|
+
*/
|
|
10
|
+
export interface BuildInfo {
|
|
11
|
+
/** The released version, from the package.json at the repository root. */
|
|
12
|
+
version: string;
|
|
13
|
+
/** When the build ran, as an ISO instant to the second: `2026-09-16T09:41:07Z`. */
|
|
14
|
+
builtAt: string;
|
|
15
|
+
/**
|
|
16
|
+
* The full commit the build was made from.
|
|
17
|
+
* @default undefined — the build could not see the repository
|
|
18
|
+
*/
|
|
19
|
+
commit?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The version of `react-cheminfo` the site is built against, which is what
|
|
22
|
+
* says whether a site is running the current chrome.
|
|
23
|
+
* @default undefined — the build could not read it
|
|
24
|
+
*/
|
|
25
|
+
reactCheminfo?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The commit as it is written for a reader.
|
|
30
|
+
* @param commit - The full hash.
|
|
31
|
+
* @returns Its first seven characters, which is what git itself shows.
|
|
32
|
+
*/
|
|
33
|
+
export function shortCommit(commit: string): string {
|
|
34
|
+
return commit.slice(0, SHORT_COMMIT_LENGTH);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The build instant as it is read on the page.
|
|
39
|
+
* @param builtAt - The ISO instant the build wrote.
|
|
40
|
+
* @returns The day and the time, in UTC, e.g. `2026-09-16 09:41:07 UTC`.
|
|
41
|
+
*/
|
|
42
|
+
export function formatBuiltAt(builtAt: string): string {
|
|
43
|
+
const seconds = builtAt.slice(0, SECOND_LENGTH).replace('T', ' ');
|
|
44
|
+
return `${seconds} UTC`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const SHORT_COMMIT_LENGTH = 7;
|
|
48
|
+
const SECOND_LENGTH = 19;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BuildInfo } from './buildInfo.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Which build of the site is running, or `undefined` when nothing filled it in.
|
|
5
|
+
*
|
|
6
|
+
* The module is replaced at build time by `cheminfoBuildInfo`, which is what
|
|
7
|
+
* puts the real version, instant and commit here. It stays a real module rather
|
|
8
|
+
* than a virtual one so that a test, a Playwright spec or a backend can import
|
|
9
|
+
* the About record in plain Node: outside a build there is simply no record,
|
|
10
|
+
* and the About page leaves the line out.
|
|
11
|
+
*/
|
|
12
|
+
export const BUILD_INFO: BuildInfo | undefined = undefined;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tell a site which build of itself it is running.
|
|
3
|
+
*
|
|
4
|
+
* Every site of the family carries an About page, and the one thing that page
|
|
5
|
+
* cannot state from a hand-written record is the release it is serving. So the
|
|
6
|
+
* build states it: the plugin resolves the version, the day and the commit
|
|
7
|
+
* once, hands them to the page through a virtual module, and writes the same
|
|
8
|
+
* record to `build-info.json` so the whole family can be asked what it is
|
|
9
|
+
* running with one request each.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { dirname, join, resolve } from 'node:path';
|
|
14
|
+
|
|
15
|
+
import type { Plugin } from 'vite';
|
|
16
|
+
|
|
17
|
+
import type { BuildInfo } from '../core/buildInfo.ts';
|
|
18
|
+
|
|
19
|
+
import { commitFromEnvironment, readGitHead } from './gitHead.ts';
|
|
20
|
+
|
|
21
|
+
/** The module a site imports its build record from. */
|
|
22
|
+
export const BUILD_INFO_MODULE = 'react-cheminfo/build-info';
|
|
23
|
+
|
|
24
|
+
/** What the build writes about itself. */
|
|
25
|
+
export interface BuildInfoOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Where the record is written in the build, so a deployment can be asked
|
|
28
|
+
* what it is running. `false` writes none.
|
|
29
|
+
* @default 'build-info.json'
|
|
30
|
+
*/
|
|
31
|
+
fileName?: string | false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the build's own version, instant and commit, and publish them.
|
|
36
|
+
*
|
|
37
|
+
* The site reads them from the module the plugin fills in:
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { BUILD_INFO } from 'react-cheminfo/build-info';
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* That module exists on disk and exports `undefined`, so a Playwright spec or
|
|
44
|
+
* a unit test importing the same About record in plain Node still resolves it —
|
|
45
|
+
* a virtual specifier does not, and the whole suite dies on the import.
|
|
46
|
+
* @param options - See {@link BuildInfoOptions}.
|
|
47
|
+
* @returns The Vite plugin.
|
|
48
|
+
*/
|
|
49
|
+
export function cheminfoBuildInfo(options: BuildInfoOptions = {}): Plugin {
|
|
50
|
+
const { fileName = 'build-info.json' } = options;
|
|
51
|
+
let root = process.cwd();
|
|
52
|
+
let outDir = 'dist';
|
|
53
|
+
let info: BuildInfo = resolveBuildInfo(root);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
name: 'cheminfo-build-info',
|
|
57
|
+
// Vite's own resolver runs first and would hand back the module on disk,
|
|
58
|
+
// which exports `undefined`; and the dependency optimizer pre-bundles a
|
|
59
|
+
// bare specifier without asking a plugin at all.
|
|
60
|
+
enforce: 'pre',
|
|
61
|
+
config() {
|
|
62
|
+
return { optimizeDeps: { exclude: [BUILD_INFO_MODULE] } };
|
|
63
|
+
},
|
|
64
|
+
configResolved(config) {
|
|
65
|
+
root = config.root;
|
|
66
|
+
outDir = config.build.outDir;
|
|
67
|
+
info = resolveBuildInfo(root);
|
|
68
|
+
},
|
|
69
|
+
resolveId(id) {
|
|
70
|
+
return id === BUILD_INFO_MODULE ? RESOLVED_MODULE : undefined;
|
|
71
|
+
},
|
|
72
|
+
load(id) {
|
|
73
|
+
if (id !== RESOLVED_MODULE) return undefined;
|
|
74
|
+
return `export const BUILD_INFO = ${JSON.stringify(info)};\n`;
|
|
75
|
+
},
|
|
76
|
+
closeBundle() {
|
|
77
|
+
if (fileName === false) return;
|
|
78
|
+
const target = resolve(root, outDir, fileName);
|
|
79
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
80
|
+
writeFileSync(target, `${JSON.stringify(info, null, 2)}\n`);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* What the build knows about itself, read off the checkout it runs in.
|
|
87
|
+
* @param root - The project Vite is building, which may sit under the
|
|
88
|
+
* repository root as `frontend` does.
|
|
89
|
+
* @returns The record, with whatever it could not resolve left out.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveBuildInfo(root: string): BuildInfo {
|
|
92
|
+
const repository = findRepositoryRoot(root);
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
version: readVersion(repository) ?? readVersion(root) ?? UNRELEASED,
|
|
96
|
+
builtAt: buildInstant(),
|
|
97
|
+
commit: readGitHead(repository) ?? commitFromEnvironment(),
|
|
98
|
+
reactCheminfo: readOwnVersion(),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The topmost directory of the run of ancestors that all hold a `package.json`.
|
|
104
|
+
*
|
|
105
|
+
* It is the repository root, and it is where the released version lives:
|
|
106
|
+
* release-please bumps the root `package.json`, while the `frontend` workspace
|
|
107
|
+
* of a site that has one keeps the `0.0.0` it was scaffolded with.
|
|
108
|
+
* @param start - Where to start climbing.
|
|
109
|
+
* @returns The highest such directory, or `start` when it holds none itself.
|
|
110
|
+
*/
|
|
111
|
+
export function findRepositoryRoot(start: string): string {
|
|
112
|
+
let directory = resolve(start);
|
|
113
|
+
let highest = directory;
|
|
114
|
+
|
|
115
|
+
for (;;) {
|
|
116
|
+
if (readPackage(directory) !== undefined) highest = directory;
|
|
117
|
+
const parent = dirname(directory);
|
|
118
|
+
// The run has ended: a directory with no package.json is above the project,
|
|
119
|
+
// and so is everything above it.
|
|
120
|
+
if (parent === directory || readPackage(parent) === undefined) {
|
|
121
|
+
return highest;
|
|
122
|
+
}
|
|
123
|
+
directory = parent;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function readVersion(directory: string): string | undefined {
|
|
128
|
+
const version = readPackage(directory)?.version;
|
|
129
|
+
return typeof version === 'string' && version.length > 0
|
|
130
|
+
? version
|
|
131
|
+
: undefined;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The version of this package, so a site can be asked which chrome it runs.
|
|
136
|
+
* @returns The version, or `undefined` when the manifest is out of reach.
|
|
137
|
+
*/
|
|
138
|
+
function readOwnVersion(): string | undefined {
|
|
139
|
+
let directory = import.meta.dirname;
|
|
140
|
+
|
|
141
|
+
for (;;) {
|
|
142
|
+
const manifest = readPackage(directory);
|
|
143
|
+
if (manifest?.name === OWN_NAME && typeof manifest.version === 'string') {
|
|
144
|
+
return manifest.version;
|
|
145
|
+
}
|
|
146
|
+
const parent = dirname(directory);
|
|
147
|
+
if (parent === directory) return undefined;
|
|
148
|
+
directory = parent;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readPackage(
|
|
153
|
+
directory: string,
|
|
154
|
+
): { name?: unknown; version?: unknown } | undefined {
|
|
155
|
+
try {
|
|
156
|
+
return JSON.parse(
|
|
157
|
+
readFileSync(join(directory, 'package.json'), 'utf8'),
|
|
158
|
+
) as Record<string, unknown>;
|
|
159
|
+
} catch {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* When the build ran, honouring `SOURCE_DATE_EPOCH` so a reproducible build
|
|
166
|
+
* stays reproducible.
|
|
167
|
+
* @returns The instant, to the second, as `2026-09-16T09:41:07Z`.
|
|
168
|
+
*/
|
|
169
|
+
function buildInstant(): string {
|
|
170
|
+
const epoch = Number(process.env.SOURCE_DATE_EPOCH);
|
|
171
|
+
const when = Number.isFinite(epoch) && epoch > 0 ? epoch * 1000 : Date.now();
|
|
172
|
+
return `${new Date(when).toISOString().slice(0, SECOND_LENGTH)}Z`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const RESOLVED_MODULE = `\0${BUILD_INFO_MODULE}`;
|
|
176
|
+
const OWN_NAME = 'react-cheminfo';
|
|
177
|
+
const UNRELEASED = '0.0.0';
|
|
178
|
+
const SECOND_LENGTH = 19;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the commit a build is made from, without running git.
|
|
3
|
+
*
|
|
4
|
+
* The image build cannot run git: `.dockerignore` keeps `.git` out of the
|
|
5
|
+
* context and `node:alpine` ships no git binary, while the shared
|
|
6
|
+
* `docker-image` workflow passes no build argument a commit could arrive in.
|
|
7
|
+
* What a site does instead is let three paths back into the context —
|
|
8
|
+
* `.git/HEAD`, `.git/refs` and `.git/packed-refs`, a few kilobytes — and those
|
|
9
|
+
* three are enough to resolve the hash by reading files.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The commit `HEAD` names, read from the repository above a directory.
|
|
17
|
+
* @param start - Where to start looking; every ancestor is tried in turn.
|
|
18
|
+
* @returns The full hash, or `undefined` when the repository is out of reach —
|
|
19
|
+
* which is what a build sees when the three paths were not let back in.
|
|
20
|
+
*/
|
|
21
|
+
export function readGitHead(start: string): string | undefined {
|
|
22
|
+
const gitDir = findGitDir(start);
|
|
23
|
+
if (gitDir === undefined) return undefined;
|
|
24
|
+
|
|
25
|
+
const head = readText(join(gitDir, 'HEAD'));
|
|
26
|
+
if (head === undefined) return undefined;
|
|
27
|
+
|
|
28
|
+
// A checkout made by `actions/checkout` is detached, so `HEAD` is the hash
|
|
29
|
+
// itself and neither `refs` nor `packed-refs` is read at all.
|
|
30
|
+
if (!head.startsWith('ref:')) return asCommit(head);
|
|
31
|
+
|
|
32
|
+
const ref = head.slice('ref:'.length).trim();
|
|
33
|
+
const loose = readText(join(gitDir, ref));
|
|
34
|
+
return (
|
|
35
|
+
asCommit(loose) ??
|
|
36
|
+
commitInPackedRefs(readText(join(gitDir, 'packed-refs')), ref)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The commit named by the environment, for a build that cannot see `.git`.
|
|
42
|
+
* @returns The hash, or `undefined` when no variable names one.
|
|
43
|
+
*/
|
|
44
|
+
export function commitFromEnvironment(): string | undefined {
|
|
45
|
+
return (
|
|
46
|
+
asCommit(process.env.GITHUB_SHA) ??
|
|
47
|
+
asCommit(process.env.SOURCE_COMMIT) ??
|
|
48
|
+
asCommit(process.env.GIT_COMMIT)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The `.git` directory governing a path.
|
|
54
|
+
* @param start - Where to start looking.
|
|
55
|
+
* @returns Its absolute path, or `undefined` when there is none above `start`.
|
|
56
|
+
*/
|
|
57
|
+
export function findGitDir(start: string): string | undefined {
|
|
58
|
+
let directory = resolve(start);
|
|
59
|
+
|
|
60
|
+
for (;;) {
|
|
61
|
+
const candidate = join(directory, '.git');
|
|
62
|
+
const text = readText(candidate);
|
|
63
|
+
// A worktree and a submodule write a file naming the real directory; a
|
|
64
|
+
// plain repository has the directory itself, which reads as `undefined`.
|
|
65
|
+
if (text?.startsWith('gitdir:')) {
|
|
66
|
+
const target = text.slice('gitdir:'.length).trim();
|
|
67
|
+
return isAbsolute(target) ? target : resolve(directory, target);
|
|
68
|
+
}
|
|
69
|
+
if (readText(join(candidate, 'HEAD')) !== undefined) return candidate;
|
|
70
|
+
|
|
71
|
+
const parent = dirname(directory);
|
|
72
|
+
if (parent === directory) return undefined;
|
|
73
|
+
directory = parent;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The hash a packed `refs` file gives a ref.
|
|
79
|
+
* @param packed - The contents of `packed-refs`, when there is one.
|
|
80
|
+
* @param ref - The ref to look up, e.g. `refs/heads/main`.
|
|
81
|
+
* @returns The hash, or `undefined` when the file does not carry the ref.
|
|
82
|
+
*/
|
|
83
|
+
function commitInPackedRefs(
|
|
84
|
+
packed: string | undefined,
|
|
85
|
+
ref: string,
|
|
86
|
+
): string | undefined {
|
|
87
|
+
if (packed === undefined) return undefined;
|
|
88
|
+
|
|
89
|
+
for (const line of packed.split('\n')) {
|
|
90
|
+
// `#` is the header and `^` the object a tag points at: neither is a ref.
|
|
91
|
+
if (line.startsWith('#') || line.startsWith('^')) continue;
|
|
92
|
+
const space = line.indexOf(' ');
|
|
93
|
+
if (space === -1 || line.slice(space + 1).trim() !== ref) continue;
|
|
94
|
+
return asCommit(line.slice(0, space));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function asCommit(value: string | undefined): string | undefined {
|
|
101
|
+
const text = value?.trim();
|
|
102
|
+
return text !== undefined && COMMIT.test(text) ? text : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function readText(path: string): string | undefined {
|
|
106
|
+
try {
|
|
107
|
+
return readFileSync(path, 'utf8').trim();
|
|
108
|
+
} catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Forty hexadecimal characters, or sixty-four in a repository on sha256.
|
|
114
|
+
const COMMIT = /^[\da-f]{40}(?:[\da-f]{24})?$/;
|
package/src/core.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './about/core/index.ts';
|
|
2
|
+
export * from './build/core/index.ts';
|
|
2
3
|
export * from './chart/core/index.ts';
|
|
3
4
|
export * from './citation/core/index.ts';
|
|
4
5
|
export * from './clipboard/core/index.ts';
|
|
@@ -11,6 +12,7 @@ export * from './error/core/index.ts';
|
|
|
11
12
|
export * from './format/core/index.ts';
|
|
12
13
|
export * from './orbital/core/index.ts';
|
|
13
14
|
export * from './overlay/core/index.ts';
|
|
15
|
+
export * from './parallel/core/index.ts';
|
|
14
16
|
export * from './pedagogy/core/index.ts';
|
|
15
17
|
export * from './periodic/core/index.ts';
|
|
16
18
|
export * from './projection/core/index.ts';
|
|
@@ -77,6 +77,13 @@ export const CREDITS = [
|
|
|
77
77
|
description: 'the structure helpers built on top of OpenChemLib.',
|
|
78
78
|
license: 'MIT',
|
|
79
79
|
},
|
|
80
|
+
{
|
|
81
|
+
id: 'osiris-property-explorer',
|
|
82
|
+
name: 'OSIRIS Property Explorer',
|
|
83
|
+
href: 'https://www.organic-chemistry.org/prog/peo/',
|
|
84
|
+
description:
|
|
85
|
+
'the original toxicity and property predictions, by Thomas Sander at Actelion, now Idorsia.',
|
|
86
|
+
},
|
|
80
87
|
{
|
|
81
88
|
id: 'react-ocl',
|
|
82
89
|
name: 'react-ocl',
|
|
@@ -115,6 +122,20 @@ export const CREDITS = [
|
|
|
115
122
|
description: 'the linear algebra behind the numbers on the page.',
|
|
116
123
|
license: 'MIT',
|
|
117
124
|
},
|
|
125
|
+
{
|
|
126
|
+
id: 'ml-xsadd',
|
|
127
|
+
name: 'ml-xsadd',
|
|
128
|
+
href: 'https://github.com/mljs/xsadd',
|
|
129
|
+
description: 'the seeded generator a reproducible series is drawn from.',
|
|
130
|
+
license: 'MIT',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'cif-to-json',
|
|
134
|
+
name: 'cif-to-json',
|
|
135
|
+
href: 'https://github.com/cheminfo/cif-to-json',
|
|
136
|
+
description: 'the reader that turns a crystallographic file into data.',
|
|
137
|
+
license: 'MIT',
|
|
138
|
+
},
|
|
118
139
|
{
|
|
119
140
|
id: 'ml-spectra-processing',
|
|
120
141
|
name: 'ml-spectra-processing',
|
|
@@ -194,6 +215,27 @@ export const CREDITS = [
|
|
|
194
215
|
description: 'the dragging that arranges things into an order.',
|
|
195
216
|
license: 'MIT',
|
|
196
217
|
},
|
|
218
|
+
{
|
|
219
|
+
id: 'react-roi',
|
|
220
|
+
name: 'react-roi',
|
|
221
|
+
href: 'https://github.com/zakodium-oss/react-roi',
|
|
222
|
+
description: 'the box a region of an image is drawn with.',
|
|
223
|
+
license: 'MIT',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: 'file-collection',
|
|
227
|
+
name: 'file-collection',
|
|
228
|
+
href: 'https://github.com/cheminfo/file-collection',
|
|
229
|
+
description: 'the reader that takes dropped files, folders and archives.',
|
|
230
|
+
license: 'MIT',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'zip-js',
|
|
234
|
+
name: 'zip.js',
|
|
235
|
+
href: 'https://github.com/gildas-lormeau/zip.js',
|
|
236
|
+
description: 'the ZIP archives that are read and written in the browser.',
|
|
237
|
+
license: 'BSD-3-Clause',
|
|
238
|
+
},
|
|
197
239
|
{
|
|
198
240
|
id: 'preact-signals',
|
|
199
241
|
name: 'Preact Signals',
|