@myst-theme/frontmatter 0.13.4 → 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;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAwB/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,GACV,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;CACpB,kDAuFA"}
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"}
@@ -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.4",
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-popover": "^1.0.6",
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": "*",