@myst-theme/frontmatter 0.13.3 → 0.13.5
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PageFrontmatter } from 'myst-frontmatter';
|
|
1
|
+
import type { ExpandedThebeFrontmatter, PageFrontmatter } from 'myst-frontmatter';
|
|
2
2
|
import { SourceFileKind } from 'myst-spec-ext';
|
|
3
3
|
export declare function DoiText({ doi: possibleLink, className }: {
|
|
4
4
|
doi?: string;
|
|
@@ -28,12 +28,14 @@ export declare function Journal({ venue, volume, issue, className, }: {
|
|
|
28
28
|
issue?: Required<PageFrontmatter>['issue'];
|
|
29
29
|
className?: string;
|
|
30
30
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
31
|
-
export declare function FrontmatterBlock({ frontmatter, kind, authorStyle, hideBadges, hideExports, className, }: {
|
|
31
|
+
export declare function FrontmatterBlock({ frontmatter, kind, authorStyle, hideBadges, hideExports, className, thebe, location, }: {
|
|
32
32
|
frontmatter: Omit<PageFrontmatter, 'parts'>;
|
|
33
33
|
kind?: SourceFileKind;
|
|
34
34
|
authorStyle?: 'block' | 'list';
|
|
35
35
|
hideBadges?: boolean;
|
|
36
36
|
hideExports?: boolean;
|
|
37
37
|
className?: string;
|
|
38
|
+
thebe?: ExpandedThebeFrontmatter;
|
|
39
|
+
location?: string;
|
|
38
40
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
39
41
|
//# sourceMappingURL=FrontmatterBlock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FrontmatterBlock.d.ts","sourceRoot":"","sources":["../src/FrontmatterBlock.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"FrontmatterBlock.d.ts","sourceRoot":"","sources":["../src/FrontmatterBlock.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAyB/C,wBAAgB,OAAO,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,kDAe7F;AAED,wBAAgB,QAAQ,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,kDAgB9F;AAED,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,MAIC,EACD,MAAM,GACP,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,kDAiBA;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,kDAkB1E;AAED,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,kDAkBvE;AAED,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,kDAiBzE;AAED,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,MAAM,EACN,KAAK,EACL,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC7C,KAAK,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,kDAyBA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,WAAW,EACX,IAA6B,EAC7B,WAAqB,EACrB,UAAU,EACV,WAAW,EACX,SAAS,EACT,KAAK,EACL,QAAQ,GACT,EAAE;IACD,WAAW,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,kDA0FA"}
|
package/dist/FrontmatterBlock.js
CHANGED
|
@@ -5,6 +5,7 @@ import { JupyterIcon, OpenAccessIcon, GithubIcon, TwitterIcon } from '@scienceic
|
|
|
5
5
|
import { LicenseBadges } from './licenses.js';
|
|
6
6
|
import { DownloadsDropdown } from './downloads.js';
|
|
7
7
|
import { AuthorAndAffiliations, AuthorsList } from './Authors.js';
|
|
8
|
+
import { LaunchButton } from './LaunchButton.js';
|
|
8
9
|
function ExternalOrInternalLink({ to, className, title, children, }) {
|
|
9
10
|
return (_jsx("a", { href: to, className: className, title: title, children: children }));
|
|
10
11
|
}
|
|
@@ -64,7 +65,7 @@ export function Journal({ venue, volume, issue, className, }) {
|
|
|
64
65
|
return null;
|
|
65
66
|
return (_jsxs("div", { className: classNames('flex-none mr-2', className), children: [url ? (_jsx(ExternalOrInternalLink, { className: "font-semibold no-underline smallcaps", to: url, title: title, children: title })) : (_jsx("span", { className: "font-semibold smallcaps", children: title })), volume != null && (_jsxs("span", { className: "pl-2 ml-2 border-l", children: ["Volume ", volume.title, issue != null && _jsxs(_Fragment, { children: [", Issue ", issue.title] })] }))] }));
|
|
66
67
|
}
|
|
67
|
-
export function FrontmatterBlock({ frontmatter, kind = SourceFileKind.Article, authorStyle = 'block', hideBadges, hideExports, className, }) {
|
|
68
|
+
export function FrontmatterBlock({ frontmatter, kind = SourceFileKind.Article, authorStyle = 'block', hideBadges, hideExports, className, thebe, location, }) {
|
|
68
69
|
if (!frontmatter)
|
|
69
70
|
return null;
|
|
70
71
|
const { title, subtitle, subject, doi, open_access, license, github, venue, volume, issue, exports, downloads, date, authors, } = frontmatter;
|
|
@@ -75,11 +76,12 @@ export function FrontmatterBlock({ frontmatter, kind = SourceFileKind.Article, a
|
|
|
75
76
|
const hasHeaders = !!subject || !!venue || !!volume || !!issue;
|
|
76
77
|
const hasDateOrDoi = !!doi || !!date;
|
|
77
78
|
const showHeaderBlock = hasHeaders || (hasBadges && !hideBadges) || (hasExports && !hideExports);
|
|
79
|
+
const hideLaunch = false;
|
|
78
80
|
if (!title && !subtitle && !showHeaderBlock && !hasAuthors && !hasDateOrDoi) {
|
|
79
81
|
// Nothing to show!
|
|
80
82
|
return null;
|
|
81
83
|
}
|
|
82
84
|
return (_jsxs("div", { id: "skip-to-frontmatter", "aria-label": "article frontmatter", className: classNames(className), children: [showHeaderBlock && (_jsxs("div", { className: "flex items-center h-6 mb-5 text-sm font-light", children: [subject && (_jsx("div", { className: classNames('flex-none pr-2 smallcaps', {
|
|
83
85
|
'border-r mr-2': venue,
|
|
84
|
-
}), children: subject })), _jsx(Journal, { venue: venue, volume: volume, issue: issue }), _jsx("div", { className: "flex-grow" }), !hideBadges && (_jsxs(_Fragment, { children: [_jsx(LicenseBadges, { license: license }), _jsx(OpenAccessBadge, { open_access: open_access }), _jsx(GitHubLink, { github: github }), isJupyter && (_jsx("div", { className: "inline-block mr-1", children: _jsx(JupyterIcon, { width: "1.25rem", height: "1.25rem", className: "inline-block", title: "Jupyter Notebook" }) }))] })), !hideExports && _jsx(DownloadsDropdown, { exports: (downloads !== null && downloads !== void 0 ? downloads : exports) })] })), title && _jsx("h1", { className: "mb-0", children: title }), subtitle && _jsx("p", { className: "mt-2 mb-0 lead text-zinc-600 dark:text-zinc-400", children: subtitle }), hasAuthors && authorStyle === 'list' && (_jsx(AuthorsList, { authors: frontmatter.authors, affiliations: frontmatter.affiliations })), hasAuthors && authorStyle === 'block' && (_jsx(AuthorAndAffiliations, { authors: frontmatter.authors, affiliations: frontmatter.affiliations })), hasDateOrDoi && (_jsxs("div", { className: "flex mt-2 text-sm font-light", children: [_jsx(DateString, { date: date, spacer: !!doi }), _jsx(DoiBadge, { doi: doi })] }))] }));
|
|
86
|
+
}), children: subject })), _jsx(Journal, { venue: venue, volume: volume, issue: issue }), _jsx("div", { className: "flex-grow" }), !hideBadges && (_jsxs(_Fragment, { children: [_jsx(LicenseBadges, { license: license }), _jsx(OpenAccessBadge, { open_access: open_access }), _jsx(GitHubLink, { github: github }), isJupyter && (_jsx("div", { className: "inline-block mr-1", children: _jsx(JupyterIcon, { width: "1.25rem", height: "1.25rem", className: "inline-block", title: "Jupyter Notebook" }) }))] })), !hideExports && _jsx(DownloadsDropdown, { exports: (downloads !== null && downloads !== void 0 ? downloads : exports) }), !hideLaunch && thebe && location && _jsx(LaunchButton, { thebe: thebe, location: location })] })), title && _jsx("h1", { className: "mb-0", children: title }), subtitle && _jsx("p", { className: "mt-2 mb-0 lead text-zinc-600 dark:text-zinc-400", children: subtitle }), hasAuthors && authorStyle === 'list' && (_jsx(AuthorsList, { authors: frontmatter.authors, affiliations: frontmatter.affiliations })), hasAuthors && authorStyle === 'block' && (_jsx(AuthorAndAffiliations, { authors: frontmatter.authors, affiliations: frontmatter.affiliations })), hasDateOrDoi && (_jsxs("div", { className: "flex mt-2 text-sm font-light", children: [_jsx(DateString, { date: date, spacer: !!doi }), _jsx(DoiBadge, { doi: doi })] }))] }));
|
|
85
87
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExpandedThebeFrontmatter } from 'myst-frontmatter';
|
|
2
|
+
export type LaunchProps = {
|
|
3
|
+
thebe: ExpandedThebeFrontmatter;
|
|
4
|
+
location: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function LaunchButton(props: LaunchProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=LaunchButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LaunchButton.d.ts","sourceRoot":"","sources":["../src/LaunchButton.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,wBAAwB,EAAoB,MAAM,kBAAkB,CAAC;AAiDnF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,wBAAwB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAmVF,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,2CAyD9C"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useCallback, useState } from 'react';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import * as Popover from '@radix-ui/react-popover';
|
|
5
|
+
import { RocketIcon, Cross2Icon, ClipboardCopyIcon, ExternalLinkIcon } from '@radix-ui/react-icons';
|
|
6
|
+
import * as Tabs from '@radix-ui/react-tabs';
|
|
7
|
+
import * as Form from '@radix-ui/react-form';
|
|
8
|
+
const GITHUB_USERNAME_REPO_REGEX = /^(?:https?:\/\/github.com\/)?([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(?:.git)?\/?$/;
|
|
9
|
+
const GITLAB_USERNAME_REPO_REGEX = /^(?:https?:\/\/gitlab.com\/)?([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(?:.git)?\/?$/;
|
|
10
|
+
const GIST_USERNAME_REPO_REGEX = /^(?:https?:\/\/gist.github.com\/)?([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(?:.git)?\/?$/;
|
|
11
|
+
function CopyButton(props) {
|
|
12
|
+
const { className, defaultMessage, alternateMessage, buildLink, timeout } = props;
|
|
13
|
+
const [message, setMessage] = useState(defaultMessage);
|
|
14
|
+
const copyLink = useCallback(() => {
|
|
15
|
+
// Build the link for the clipboard
|
|
16
|
+
const link = props.buildLink();
|
|
17
|
+
// In secure links, we can copy it!
|
|
18
|
+
if (window.isSecureContext) {
|
|
19
|
+
// Write to clipboard
|
|
20
|
+
window.navigator.clipboard.writeText(link !== null && link !== void 0 ? link : '<invalid link>');
|
|
21
|
+
// Update UI
|
|
22
|
+
setMessage(alternateMessage !== null && alternateMessage !== void 0 ? alternateMessage : defaultMessage);
|
|
23
|
+
// Set callback to restore message
|
|
24
|
+
setTimeout(() => {
|
|
25
|
+
setMessage(defaultMessage);
|
|
26
|
+
}, timeout !== null && timeout !== void 0 ? timeout : 1000);
|
|
27
|
+
}
|
|
28
|
+
}, [defaultMessage, alternateMessage, buildLink, timeout, setMessage]);
|
|
29
|
+
return (_jsxs("button", { type: "button", className: classNames(className, 'flex flex-row items-center gap-1'), onClick: copyLink, children: [message, " ", _jsx(ClipboardCopyIcon, { className: "inline-block" })] }));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Ensure URL of for http://foo.com/bar?baz
|
|
33
|
+
* has the form http://foo.com/bar/
|
|
34
|
+
*
|
|
35
|
+
* @param url - URL to parse
|
|
36
|
+
*/
|
|
37
|
+
function ensureBasename(url) {
|
|
38
|
+
// Parse input URL (or fallback)
|
|
39
|
+
const parsedURL = new URL(url);
|
|
40
|
+
// Drop any fragments
|
|
41
|
+
let baseURL = `${parsedURL.origin}${parsedURL.pathname}`;
|
|
42
|
+
// Ensure a trailing fragment
|
|
43
|
+
if (!baseURL.endsWith('/')) {
|
|
44
|
+
baseURL = `${baseURL}/`;
|
|
45
|
+
}
|
|
46
|
+
return baseURL;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Equivalent to Python's `urllib.parse.urlencode` function
|
|
50
|
+
*
|
|
51
|
+
* @param params - mapping from parameter name to string value
|
|
52
|
+
*/
|
|
53
|
+
function encodeURLParams(params) {
|
|
54
|
+
return Object.entries(params)
|
|
55
|
+
.filter(([key, value]) => value !== undefined)
|
|
56
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
57
|
+
.join('&');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Make a binder url for supported providers
|
|
61
|
+
*
|
|
62
|
+
* - trim gitlab.com from repo
|
|
63
|
+
* - trim trailing or leading '/' on repo
|
|
64
|
+
* - convert to URL acceptable string. Required for gitlab
|
|
65
|
+
* - trailing / on binderUrl
|
|
66
|
+
*
|
|
67
|
+
* Copied from thebe-core
|
|
68
|
+
*
|
|
69
|
+
* @param opts BinderOptions
|
|
70
|
+
* @returns a binder compatible url
|
|
71
|
+
*/
|
|
72
|
+
function makeBinderURL(options, location, version = 'v2') {
|
|
73
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
74
|
+
let stub;
|
|
75
|
+
if (!options.repo || !options.url) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
switch (options.provider) {
|
|
79
|
+
case 'git': {
|
|
80
|
+
const encodedRepo = encodeURIComponent(options.repo);
|
|
81
|
+
const encodedRef = encodeURIComponent((_a = options.ref) !== null && _a !== void 0 ? _a : 'HEAD');
|
|
82
|
+
stub = `git/${encodedRepo}/${encodedRef}`;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case 'gitlab': {
|
|
86
|
+
const [, org, repo] = (_b = options.repo.match(GITLAB_USERNAME_REPO_REGEX)) !== null && _b !== void 0 ? _b : [];
|
|
87
|
+
if (!org) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const encodedRef = encodeURIComponent((_c = options.ref) !== null && _c !== void 0 ? _c : 'HEAD');
|
|
91
|
+
stub = `gl/${org}/${repo}/${encodedRef}`;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'github': {
|
|
95
|
+
const [, org, repo] = (_d = options.repo.match(GITHUB_USERNAME_REPO_REGEX)) !== null && _d !== void 0 ? _d : [];
|
|
96
|
+
if (!org) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const encodedRef = encodeURIComponent((_e = options.ref) !== null && _e !== void 0 ? _e : 'HEAD');
|
|
100
|
+
stub = `gh/${org}/${repo}/${encodedRef}`;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case 'gist': {
|
|
104
|
+
const [, org, repo] = (_f = options.repo.match(GIST_USERNAME_REPO_REGEX)) !== null && _f !== void 0 ? _f : [];
|
|
105
|
+
if (!org) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const encodedRef = encodeURIComponent((_g = options.ref) !== null && _g !== void 0 ? _g : 'HEAD');
|
|
109
|
+
stub = `gist/${org}/${repo}/${encodedRef}`;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
default: {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Build binder URL path
|
|
117
|
+
const query = encodeURLParams({ urlpath: `/lab/tree/${location}` });
|
|
118
|
+
const binderURL = ensureBasename(options.url);
|
|
119
|
+
return `${binderURL}${version}/${stub}?${query}`;
|
|
120
|
+
}
|
|
121
|
+
function cloneNameFromRepo(repo) {
|
|
122
|
+
const url = new URL(repo);
|
|
123
|
+
const parts = url.pathname.slice(1).split('/');
|
|
124
|
+
return parts[parts.length - 1] || url.hostname;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Make an nbgitpuller url for supported providers
|
|
128
|
+
*
|
|
129
|
+
* - trim gitlab.com from repo
|
|
130
|
+
* - trim trailing or leading '/' on repo
|
|
131
|
+
* - convert to URL acceptable string. Required for gitlab
|
|
132
|
+
* - trailing / on binderUrl
|
|
133
|
+
*
|
|
134
|
+
* Copied from thebe-core
|
|
135
|
+
*
|
|
136
|
+
* @param opts BinderOptions
|
|
137
|
+
* @returns a binder compatible url
|
|
138
|
+
*/
|
|
139
|
+
function makeNbgitpullerURL(options, location) {
|
|
140
|
+
var _a, _b, _c;
|
|
141
|
+
if (!options.repo || !options.url) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
const { ref } = options;
|
|
145
|
+
let repo;
|
|
146
|
+
let cloneName;
|
|
147
|
+
switch (options.provider) {
|
|
148
|
+
case 'git': {
|
|
149
|
+
repo = options.repo;
|
|
150
|
+
cloneName = cloneNameFromRepo(repo);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 'gitlab': {
|
|
154
|
+
const [, org, name] = (_a = options.repo.match(GITLAB_USERNAME_REPO_REGEX)) !== null && _a !== void 0 ? _a : [];
|
|
155
|
+
repo = `https://gitlab.com/${org}/${name}`;
|
|
156
|
+
cloneName = name;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case 'github': {
|
|
160
|
+
const [, org, name] = (_b = options.repo.match(GITHUB_USERNAME_REPO_REGEX)) !== null && _b !== void 0 ? _b : [];
|
|
161
|
+
repo = `https://github.com/${org}/${name}`;
|
|
162
|
+
cloneName = name;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case 'gist': {
|
|
166
|
+
const [, , rev] = (_c = options.repo.match(GIST_USERNAME_REPO_REGEX)) !== null && _c !== void 0 ? _c : [];
|
|
167
|
+
repo = `https://gist.github.com/${rev}`;
|
|
168
|
+
cloneName = rev;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
default: {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Build binder URL path
|
|
176
|
+
const query = encodeURLParams({
|
|
177
|
+
repo,
|
|
178
|
+
// Need a valid branch name, not a rev
|
|
179
|
+
// branch: ref,
|
|
180
|
+
urlpath: `/lab/tree/${cloneName}${location}`,
|
|
181
|
+
});
|
|
182
|
+
return `git-pull?${query}`;
|
|
183
|
+
}
|
|
184
|
+
function BinderLaunchContent(props) {
|
|
185
|
+
var _a;
|
|
186
|
+
const { thebe, location, onLaunch } = props;
|
|
187
|
+
const { binder } = thebe;
|
|
188
|
+
const defaultBinderBaseURL = (_a = binder === null || binder === void 0 ? void 0 : binder.url) !== null && _a !== void 0 ? _a : 'https://mybinder.org';
|
|
189
|
+
const formRef = useRef(null);
|
|
190
|
+
const buildLink = useCallback(() => {
|
|
191
|
+
const form = formRef.current;
|
|
192
|
+
if (!form) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const data = Object.fromEntries(new FormData(form));
|
|
196
|
+
return makeBinderURL(Object.assign(Object.assign({}, (binder !== null && binder !== void 0 ? binder : {})), { url: data.url || defaultBinderBaseURL }), location);
|
|
197
|
+
}, [formRef, location, binder]);
|
|
198
|
+
// FIXME: use ValidityState from radix-ui once passing-by-name is fixed
|
|
199
|
+
const urlRef = useRef(null);
|
|
200
|
+
const buildValidLink = useCallback(() => {
|
|
201
|
+
var _a;
|
|
202
|
+
if (((_a = urlRef.current) === null || _a === void 0 ? void 0 : _a.dataset.invalid) === 'true') {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
return buildLink();
|
|
207
|
+
}
|
|
208
|
+
}, [buildLink, urlRef]);
|
|
209
|
+
const handleSubmit = useCallback((event) => {
|
|
210
|
+
var _a;
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
const link = buildLink();
|
|
213
|
+
// Link should exist, but guard anyway
|
|
214
|
+
if (link) {
|
|
215
|
+
(_a = window === null || window === void 0 ? void 0 : window.open(link, '_blank')) === null || _a === void 0 ? void 0 : _a.focus();
|
|
216
|
+
}
|
|
217
|
+
onLaunch === null || onLaunch === void 0 ? void 0 : onLaunch();
|
|
218
|
+
}, [defaultBinderBaseURL, buildLink, onLaunch]);
|
|
219
|
+
return (_jsxs(Form.Root, { className: "w-[260px]", onSubmit: handleSubmit, ref: formRef, children: [_jsxs("p", { className: "mb-2.5 text-[15px] font-medium leading-[19px]", children: ["Launch on a BinderHub e.g. ", _jsx("a", { href: "https://mybinder.org", children: "mybinder.org" })] }), _jsxs(Form.Field, { className: "mb-2.5 grid", name: "url", children: [_jsxs("div", { className: "flex items-baseline justify-between", children: [_jsx(Form.Label, { className: "text-[15px] font-medium leading-[35px]", children: "BinderHub URL" }), _jsx(Form.Message, { className: "text-[13px] opacity-80", match: "typeMismatch", children: "Please provide a valid URL that starts with http(s)." })] }), _jsx(Form.Control, { asChild: true, children: _jsx("input", { className: "box-border inline-flex h-[35px] w-full appearance-none items-center justify-center rounded px-2.5 text-[15px] leading-none shadow-[0_0_0_1px] shadow-slate-400 outline-none bg-gray-50 dark:bg-gray-700 hover:shadow-[0_0_0_1px_black] focus:shadow-[0_0_0_2px_black]", type: "url", placeholder: defaultBinderBaseURL, ref: urlRef }) })] }), _jsxs("div", { className: "flex flex-row justify-between", children: [_jsx(Form.Submit, { asChild: true, children: _jsxs("button", { className: "inline-flex flex-row gap-1 h-[35px] items-center justify-center rounded px-[15px] font-medium leading-none bg-orange-500 hover:bg-orange-600 outline-none text-white focus:shadow-[0_0_0_2px] focus:shadow-black focus:outline-none", children: [_jsx("span", { children: "Launch" }), " ", _jsx(ExternalLinkIcon, { className: "inline-block" })] }) }), _jsx(CopyButton, { className: "inline-flex h-[35px] items-center justify-center rounded px-[15px] font-medium leading-none bg-gray-400 hover:bg-gray-500 outline-none text-white focus:shadow-[0_0_0_2px] focus:shadow-black focus:outline-none", defaultMessage: "Copy Link", alternateMessage: "Copied Link", buildLink: buildValidLink })] })] }));
|
|
220
|
+
}
|
|
221
|
+
function JupyterHubLaunchContent(props) {
|
|
222
|
+
const { onLaunch, location, thebe } = props;
|
|
223
|
+
const { binder } = thebe;
|
|
224
|
+
const defaultHubBaseURL = '';
|
|
225
|
+
const formRef = useRef(null);
|
|
226
|
+
const buildLink = useCallback(() => {
|
|
227
|
+
const form = formRef.current;
|
|
228
|
+
if (!form) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const data = Object.fromEntries(new FormData(form));
|
|
232
|
+
const rawHubBaseURL = data.url;
|
|
233
|
+
if (!rawHubBaseURL) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const gitPullURL = makeNbgitpullerURL(binder !== null && binder !== void 0 ? binder : {}, location);
|
|
237
|
+
const hubURL = ensureBasename(rawHubBaseURL);
|
|
238
|
+
return `${hubURL}hub/user-redirect/${gitPullURL}`;
|
|
239
|
+
}, [formRef, location, binder]);
|
|
240
|
+
// FIXME: use ValidityState from radix-ui once passing-by-name is fixed
|
|
241
|
+
const urlRef = useRef(null);
|
|
242
|
+
const buildValidLink = useCallback(() => {
|
|
243
|
+
var _a;
|
|
244
|
+
if (((_a = urlRef.current) === null || _a === void 0 ? void 0 : _a.dataset.invalid) === 'true') {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
return buildLink();
|
|
249
|
+
}
|
|
250
|
+
}, [buildLink, urlRef]);
|
|
251
|
+
const handleSubmit = useCallback((event) => {
|
|
252
|
+
var _a;
|
|
253
|
+
event.preventDefault();
|
|
254
|
+
const link = buildLink();
|
|
255
|
+
// Link should exist, but guard anyway
|
|
256
|
+
if (link) {
|
|
257
|
+
(_a = window === null || window === void 0 ? void 0 : window.open(link, '_blank')) === null || _a === void 0 ? void 0 : _a.focus();
|
|
258
|
+
}
|
|
259
|
+
onLaunch === null || onLaunch === void 0 ? void 0 : onLaunch();
|
|
260
|
+
}, [defaultHubBaseURL, buildLink, onLaunch]);
|
|
261
|
+
return (_jsxs(Form.Root, { className: "w-[260px]", onSubmit: handleSubmit, ref: formRef, children: [_jsx("p", { className: "mb-2.5 text-[15px] font-medium leading-[19px]", children: "Launch on a JupyterHub" }), _jsxs(Form.Field, { className: "mb-2.5 grid", name: "url", children: [_jsxs("div", { className: "flex items-baseline justify-between", children: [_jsx(Form.Label, { className: "text-[15px] font-medium leading-[35px]", children: "JupyterHub URL" }), _jsx(Form.Message, { className: "text-[13px] opacity-80", match: "valueMissing", children: "Please provide a URL." }), _jsx(Form.Message, { className: "text-[13px] opacity-80", match: "typeMismatch", children: "Please provide a valid URL that starts with http(s)." })] }), _jsx(Form.Control, { asChild: true, children: _jsx("input", { className: "box-border inline-flex h-[35px] w-full appearance-none items-center justify-center rounded px-2.5 text-[15px] leading-none shadow-[0_0_0_1px] shadow-slate-400 outline-none bg-gray-50 dark:bg-gray-700 hover:shadow-[0_0_0_1px_black] focus:shadow-[0_0_0_2px_black]", type: "url", required: true, ref: urlRef }) })] }), _jsxs("div", { className: "flex flex-row justify-between", children: [_jsx(Form.Submit, { asChild: true, children: _jsxs("button", { className: "inline-flex flex-row gap-1 h-[35px] items-center justify-center rounded px-[15px] font-medium leading-none bg-orange-500 hover:bg-orange-600 outline-none text-white focus:shadow-[0_0_0_2px] focus:shadow-black focus:outline-none", children: [_jsx("span", { children: "Launch" }), " ", _jsx(ExternalLinkIcon, { className: "inline-block" })] }) }), _jsx(CopyButton, { className: "inline-flex h-[35px] items-center justify-center rounded px-[15px] font-medium leading-none bg-gray-400 hover:bg-gray-500 outline-none text-white focus:shadow-[0_0_0_2px] focus:shadow-black focus:outline-none", defaultMessage: "Copy Link", alternateMessage: "Copied Link", buildLink: buildValidLink })] })] }));
|
|
262
|
+
}
|
|
263
|
+
export function LaunchButton(props) {
|
|
264
|
+
const closeRef = useRef(null);
|
|
265
|
+
const closePopover = useCallback(() => {
|
|
266
|
+
var _a, _b;
|
|
267
|
+
(_b = (_a = closeRef.current) === null || _a === void 0 ? void 0 : _a.click) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
268
|
+
}, []);
|
|
269
|
+
return (_jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx("button", { className: "inline-flex size-[24px] hover:text-[#E18435] items-center justify-center", "aria-label": "Launch in external computing interface", children: _jsx(RocketIcon, {}) }) }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { className: "z-30 text-gray-700 dark:text-white bg-white dark:bg-stone-800 p-5 rounded shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)]", sideOffset: 5, children: [_jsxs(Tabs.Root, { className: "flex w-[300px] flex-col", defaultValue: "binder", children: [_jsxs(Tabs.List, { className: "flex shrink-0 border-b divide-x border-gray-200 dark:border-gray-400", "aria-label": "Launch into computing interface", children: [_jsx(Tabs.Trigger, { className: "flex h-[45px] flex-1 cursor-default items-center justify-center px-5 text-[15px] outline-none data-[state=active]:underline border-gray-200 dark:border-gray-400", value: "binder", children: "Binder" }), _jsx(Tabs.Trigger, { className: "flex h-[45px] flex-1 cursor-default items-center justify-center px-5 text-[15px] outline-none data-[state=active]:underline border-gray-200 dark:border-gray-400", value: "jupyterhub", children: "JupyterHub" })] }), _jsx(Tabs.Content, { className: "grow rounded-b-md p-5 outline-none", value: "binder", children: _jsx(BinderLaunchContent, Object.assign({}, props, { onLaunch: closePopover })) }), _jsx(Tabs.Content, { className: "grow rounded-b-md p-5 outline-none", value: "jupyterhub", children: _jsx(JupyterHubLaunchContent, Object.assign({}, props, { onLaunch: closePopover })) })] }), _jsx(Popover.Close, { className: "absolute right-[5px] top-[5px] inline-flex size-[25px] items-center justify-center rounded-full", "aria-label": "Close", ref: closeRef, children: _jsx(Cross2Icon, {}) }), _jsx(Popover.Arrow, { className: "fill-white" })] }) })] }));
|
|
270
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myst-theme/frontmatter",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@headlessui/react": "^1.7.15",
|
|
24
24
|
"@heroicons/react": "^2.0.18",
|
|
25
|
-
"@radix-ui/react-
|
|
25
|
+
"@radix-ui/react-form": "^0.1.0",
|
|
26
|
+
"@radix-ui/react-icons": "^1.3.2",
|
|
27
|
+
"@radix-ui/react-popover": "^1.1.2",
|
|
28
|
+
"@radix-ui/react-tabs": "^1.1.1",
|
|
26
29
|
"@scienceicons/react": "^0.0.6",
|
|
27
30
|
"classnames": "^2.3.2",
|
|
28
31
|
"myst-common": "*",
|