Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⤵️ Local download links #162

Merged
merged 2 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,26 @@
"@jupyterlab/notebook": "^4.0.0",
"@jupyterlab/rendermime": "^4.0.0",
"@jupyterlab/translation": "^4.0.0",
"@myst-theme/diagrams": "^0.3.2",
"@myst-theme/frontmatter": "^0.3.2",
"@myst-theme/providers": "^0.3.2",
"@myst-theme/diagrams": "^0.3.3",
"@myst-theme/frontmatter": "^0.3.3",
"@myst-theme/providers": "^0.3.3",
"katex": "^0.15.2",
"myst-ext-card": "^1.0.0",
"myst-ext-exercise": "^1.0.0",
"myst-ext-grid": "^1.0.0",
"myst-ext-proof": "^1.0.0",
"myst-ext-tabs": "^1.0.0",
"myst-frontmatter": "^1.0.1",
"myst-parser": "^1.0.1",
"myst-to-react": "^0.3.2",
"myst-transforms": "^1.0.1"
"myst-parser": "^1.0.2",
"myst-to-react": "^0.3.3",
"myst-transforms": "^1.0.2"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@jupyterlab/builder": "^4.0.0",
"@jupyterlab/testutils": "^4.0.0",
"@myst-theme/styles": "^0.3.2",
"@myst-theme/styles": "^0.3.3",
"@tailwindcss/typography": "^0.5.8",
"@types/jest": "^29.2.0",
"@types/json-schema": "^7.0.11",
Expand Down
2 changes: 1 addition & 1 deletion src/MySTMarkdownCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class MySTMarkdownCell
}

// The notebook update is asynchronous
renderNotebook(this.parent as StaticNotebook);
await renderNotebook(this.parent as StaticNotebook);

// Let's wait for this cell to be rendered
await this._mystWidget.renderPromise;
Expand Down
86 changes: 45 additions & 41 deletions src/links.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import React from 'react';
import type { Plugin } from 'unified';
import type { Root, Link } from 'myst-spec';
import { selectAll } from 'unist-util-select';
import { URLExt } from '@jupyterlab/coreutils';
import type { LinkProps } from '@myst-theme/providers';
import { IRenderMime } from '@jupyterlab/rendermime';
import { updateLinkTextIfEmpty } from 'myst-transforms';

/**
* Handle an anchor node.
* NOTE: This is copied from @jupyterlab/rendermime renderers.ts
* ideally this should be removed and exported from there?
* Originally from @jupyterlab/rendermime renderers.ts
*/
function handleAnchor(
async function handleAnchor(
anchor: HTMLAnchorElement,
resolver: IRenderMime.IResolver,
linkHandler: IRenderMime.ILinkHandler | undefined
Expand All @@ -24,41 +23,37 @@ function handleAnchor(
: URLExt.isLocal(href);
// Bail if it is not a file-like url.
if (!href || !isLocal) {
return Promise.resolve(undefined);
return;
}
// Remove the hash until we can handle it.
const hash = anchor.hash;
if (hash) {
// Handle internal link in the file.
if (hash === href) {
anchor.target = '_self';
return Promise.resolve(undefined);
return;
}
// For external links, remove the hash until we have hash handling.
href = href.replace(hash, '');
}
// Get the appropriate file path.
return resolver
.resolveUrl(href)
.then(urlPath => {
// decode encoded url from url to api path
const path = decodeURIComponent(urlPath);
// Handle the click override.
if (linkHandler) {
linkHandler.handleLink(anchor, path, hash);
}
// Get the appropriate file download path.
return resolver.getDownloadUrl(urlPath);
})
.then(url => {
// Set the visible anchor.
anchor.href = url + hash;
})
.catch(err => {
// If there was an error getting the url,
// just make it an empty link.
anchor.href = '';
});
try {
// Get the appropriate file path.
const urlPath = await resolver.resolveUrl(href);
// decode encoded url from url to api path
const path = decodeURIComponent(urlPath);
// Handle the click override.
if (linkHandler) {
linkHandler.handleLink(anchor, path, hash);
}
// Get the appropriate file download path.
const url = await resolver.getDownloadUrl(urlPath);
// Set the visible anchor.
anchor.href = url + hash;
} catch (error) {
// If there was an error getting the url,
// just make it an empty link.
anchor.href = '';
}
}

export const linkFactory =
Expand Down Expand Up @@ -92,17 +87,26 @@ export async function internalLinksTransform(
opts: Options
): Promise<void> {
const links = selectAll('link,linkBlock', tree) as Link[];
links.forEach(async link => {
if (!link || !link.url) return;
const resolver = opts.resolver;
const isLocal = resolver?.isLocal
? resolver.isLocal(link.url)
: URLExt.isLocal(link.url);
if (isLocal) (link as any).internal = true;
});
await Promise.all(
links.map(async link => {
if (!link || !link.url) return;
const resolver = opts.resolver;
const href = link.url;
updateLinkTextIfEmpty(link, href);
const isLocal = resolver?.isLocal
? resolver.isLocal(href)
: URLExt.isLocal(href);
if (!isLocal) return;
if (!resolver) return;
if ((link as any).static) {
// TODO: remove hash
const urlPath = await resolver.resolveUrl(href);
const url = await resolver.getDownloadUrl(urlPath);
(link as any).urlSource = href;
link.url = url;
Comment on lines +101 to +106
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now an async function, and if the link is static we update the URL directly, otherwise we let Jupyter take care of it in the renderer.

} else {
(link as any).internal = true;
}
})
);
}

export const internalLinksPlugin: Plugin<[Options], Root, Root> =
opts => tree => {
internalLinksTransform(tree, opts);
};
1 change: 1 addition & 0 deletions src/mime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class RenderedMySTMarkdown
this.node.dataset['mimeType'] = MIME_TYPE;
this.addClass('jp-RenderedMySTMarkdown');
}

async renderModel(model: IRenderMime.IMimeModel): Promise<void> {
if ((window as any).trigger) {
throw Error('triggered!');
Expand Down
18 changes: 11 additions & 7 deletions src/myst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { StaticNotebook } from '@jupyterlab/notebook';
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { IRenderMime } from '@jupyterlab/rendermime-interfaces';
import { imageUrlSourceTransform } from './images';
import { internalLinksPlugin } from './links';
import { internalLinksTransform } from './links';
import { addCiteChildrenPlugin } from './citations';
import { evalRole } from './roles';
import { IUserExpressionMetadata } from './metadata';
Expand Down Expand Up @@ -119,12 +119,12 @@ export async function processArticleMDAST(
.use(linksPlugin, { transformers: linkTransforms })
.use(footnotesPlugin)
.use(resolveReferencesPlugin, { state })
.use(internalLinksPlugin, { resolver })
.use(addCiteChildrenPlugin)
.use(keysPlugin)
.runSync(mdast as any, file);

// Go through all links and replace the source if they are local
await internalLinksTransform(mdast, { resolver });
await imageUrlSourceTransform(mdast, { resolver });

return {
Expand All @@ -143,10 +143,10 @@ export function buildNotebookMDAST(mystCells: IMySTMarkdownCell[]): any {
return { type: 'root', children: blocks };
}

export function processNotebookMDAST(
export async function processNotebookMDAST(
mdast: any,
resolver: IRenderMime.IResolver | undefined
): IMySTDocumentState {
): Promise<IMySTDocumentState> {
const linkTransforms = [
new WikiTransformer(),
new GithubTransformer(),
Expand Down Expand Up @@ -185,11 +185,12 @@ export function processNotebookMDAST(
.use(linksPlugin, { transformers: linkTransforms })
.use(footnotesPlugin)
.use(resolveReferencesPlugin, { state })
.use(internalLinksPlugin, { resolver: resolver })
.use(addCiteChildrenPlugin)
.use(keysPlugin)
.runSync(mdast as any, file);

await internalLinksTransform(mdast, { resolver });

if (file.messages.length > 0) {
// TODO: better error messages in the future
console.error(file.messages.map(m => m.message).join('\n'));
Expand All @@ -215,7 +216,7 @@ export async function processCellMDAST(
return mdast;
}

export function renderNotebook(notebook: StaticNotebook) {
export async function renderNotebook(notebook: StaticNotebook) {
const mystCells = notebook.widgets.filter(isMySTMarkdownCell).filter(
// In the future, we may want to process the code cells as well, but not now
cell => cell.fragmentMDAST !== undefined
Expand All @@ -225,7 +226,10 @@ export function renderNotebook(notebook: StaticNotebook) {
references,
frontmatter,
mdast: processedMDAST
} = processNotebookMDAST(mdast, notebook.rendermime.resolver ?? undefined);
} = await processNotebookMDAST(
mdast,
notebook.rendermime.resolver ?? undefined
);

mystCells.forEach((cell, index) => {
if (cell.rendered) {
Expand Down
Loading