Skip to content

Commit

Permalink
Rehydrate Widget State (#426) @dwootton
Browse files Browse the repository at this point in the history
* added types
* initial metadata join
* clean pipeing, no widgetstate in passive or active renderer
* pre rm widgetstate to tree rm
* rm merging metadata to tree
* piping render
* updated to metadata reference
* rm spacing and ts ignore
* init working
* refactor with fixed model
* updated comment
* refactor
* spacing
* state return
* hidden
  • Loading branch information
dwootton authored Jul 15, 2024
1 parent 225ffa0 commit 5619777
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 23 deletions.
7 changes: 5 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,8 @@
"npm": ">=7.0.0",
"node": ">=14.0.0"
},
"packageManager": "npm@8.10.0"
"packageManager": "npm@8.10.0",
"dependencies": {
"thebe-core": "file:../../GitHub/thebe/thebe/packages/core/thebe-core-0.4.7.tgz"
}
}
22 changes: 22 additions & 0 deletions packages/common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@ type PageFrontmatterWithDownloads = Omit<PageFrontmatter, 'downloads' | 'exports
exports?: SiteExport[];
};

export type WidgetState = {
model_module: string;
model_module_version: string;
model_name: string;
state: {
[key: string]: any;
};
};

export type Widgets = {
state: {
[key: string]: WidgetState;
};
version_major: number;
version_minor: number;
};

export type WidgetsMetaData = {
"application/vnd.jupyter.widget-state+json": Widgets
};

export type PageLoader = {
kind: SourceFileKind;
location: string;
Expand All @@ -58,6 +79,7 @@ export type PageLoader = {
project: string; // This is written in at render time in the site
frontmatter: PageFrontmatterWithDownloads;
mdast: GenericParent;
widgets?: WidgetsMetaData;
references: References;
footer?: FooterLinks;
// This may not be defined
Expand Down
16 changes: 14 additions & 2 deletions packages/jupyter/src/execute/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ interface AddNotebookPayload {
rendermime: IRenderMimeRegistry;
}

interface AddPassivePayload {
manager: any; //TODO ThebePassiveManager
rendermime: IRenderMimeRegistry;
pageSlug: string;
}

export function isPassivePayload(payload: unknown): payload is AddPassivePayload {
const maybePayload = payload as AddPassivePayload;
return typeof maybePayload.manager === 'object' && typeof maybePayload.rendermime === 'object';
}
export function isAddSessionPayload(payload: unknown): payload is AddSessionPayload {
const maybePayload = payload as AddSessionPayload;
return (
Expand All @@ -114,13 +124,15 @@ export interface ExecuteScopeAction {
| 'ADD_NOTEBOOK'
| 'ADD_SESSION'
| 'SET_FIRST_EXECUTION'
| 'SET_RENDERING_READY';
| 'SET_RENDERING_READY'
| 'ADD_PASSIVE';
payload:
| NavigatePayload
| SlugPayload
| DoubleSlugPayload
| BuildStatusPayload
| AddMdastPayload
| AddNotebookPayload
| AddSessionPayload;
| AddSessionPayload
| AddPassivePayload;
}
2 changes: 2 additions & 0 deletions packages/jupyter/src/execute/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ export function useCellExecution(id: IdOrKey, clearOutputsOnExecute = false) {
}

const ready = context.state.pages[context.slug]?.ready;
const passive = context.state.pages[context.slug]?.passive;
const kind = context.state.pages[context.slug]?.kind ?? SourceFileKind.Article;

const execute = useCallback(() => {
Expand Down Expand Up @@ -297,6 +298,7 @@ export function useCellExecution(id: IdOrKey, clearOutputsOnExecute = false) {
return {
canCompute: context.canCompute,
kind,
passive,
ready,
execute,
clear,
Expand Down
33 changes: 30 additions & 3 deletions packages/jupyter/src/execute/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Dependency } from 'myst-spec-ext';
import { SourceFileKind } from 'myst-spec-ext';
import React, { useEffect, useReducer, useRef } from 'react';
import React, { useEffect, useReducer, useRef, useState } from 'react';
import { selectAll } from 'unist-util-select';
import type { ExecuteScopeAction } from './actions.js';
import type { Computable, ExecuteScopeState, IdKeyMap } from './types.js';
Expand All @@ -13,6 +13,8 @@ import {
} from './selectors.js';
import { MdastFetcher, NotebookBuilder, ServerMonitor, SessionStarter } from './leaf.js';
import type { GenericParent } from 'myst-common';
import { WidgetsMetaData } from '../../../common/dist/types.js';
import {useThebeLoader} from 'thebe-react';

export interface ExecuteScopeType {
canCompute: boolean;
Expand All @@ -30,6 +32,7 @@ type ArticleContents = {
mdast: GenericParent;
location?: string;
dependencies?: Dependency[];
widgets?:WidgetsMetaData;
};

function useScopeNavigate({
Expand Down Expand Up @@ -106,12 +109,35 @@ export function ExecuteScopeProvider({
children,
enable,
contents,
}: React.PropsWithChildren<{ enable: boolean; contents: ArticleContents }>) {
}: React.PropsWithChildren<{ enable: boolean; contents: ArticleContents ;}>) {
// compute incoming for first render
const computables: Computable[] = listComputables(contents.mdast);

const fallbackLocation = contents.kind === SourceFileKind.Notebook ? '/fallback.ipynb' : '/';

const { core } = useThebeLoader();
const [isCoreLoaded, setIsCoreLoaded] = useState(false);
useEffect(() => {
if (core) {
setIsCoreLoaded(true);
}
}, [core]);

useEffect(() => {
if (isCoreLoaded && core) {
const rendermime = core.makeRenderMimeRegistry();
const manager = new core.ThebePassiveManager(rendermime, contents?.widgets?.["application/vnd.jupyter.widget-state+json"]);

dispatch({
type: 'ADD_PASSIVE',
payload: {
rendermime,
manager,
pageSlug:contents.slug
},
});
}
}, [isCoreLoaded, core, contents?.widgets]);

const initialState: ExecuteScopeState = {
mdast: {
[contents.slug]: { root: contents.mdast },
Expand All @@ -126,6 +152,7 @@ export function ExecuteScopeProvider({
computables,
ready: false,
scopes: {},
passive: undefined
},
},
builds: {},
Expand Down
23 changes: 23 additions & 0 deletions packages/jupyter/src/execute/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
isBuildStatusPayload,
isNavigatePayload,
isSlugPayload,
isPassivePayload,
} from './actions.js';
import type { ExecuteScopeState } from './types.js';

Expand Down Expand Up @@ -161,6 +162,28 @@ export function reducer(state: ExecuteScopeState, action: ExecuteScopeAction): E
},
};
}
case 'ADD_PASSIVE': {
if (!isPassivePayload(action.payload)) {
console.error(action.payload);
throw new Error('invalid ADD_PASSIVE payload');
}
const { rendermime, manager, pageSlug} = action.payload;

return {
...state,
pages: {
...state.pages,
[pageSlug]: {
...state.pages[pageSlug],
passive: {
rendermime: rendermime,
manager: manager,
},
},
},
};
}

case 'ADD_SESSION': {
if (!isAddSessionPayload(action.payload)) {
console.error(action.payload);
Expand Down
6 changes: 5 additions & 1 deletion packages/jupyter/src/execute/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { GenericParent } from 'myst-common';
import type { SourceFileKind, Dependency } from 'myst-spec-ext';
import type { IRenderMimeRegistry, ThebeNotebook, ThebeSession } from 'thebe-core';
import type { IRenderMimeRegistry, ThebeNotebook, ThebeSession, ThebePassiveManager } from 'thebe-core';

export type BuildStatus =
| 'pending'
Expand Down Expand Up @@ -33,6 +33,10 @@ export interface ExecuteScopeState {
scopes: {
[notebookSlug: string]: ExecutionScope;
};
passive? : {
manager: ThebePassiveManager;
rendermime: IRenderMimeRegistry
}
};
};
builds: {
Expand Down
10 changes: 6 additions & 4 deletions packages/jupyter/src/execute/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,19 @@ export function notebookFromMdast(
if (codeCell.identifier) idkmap[codeCell.identifier] = target;
if (output.identifier) idkmap[output.identifier] = target;

// TODO Future Fix: pass through metadata to sync passive and active state
const metadata = {};

return new core.ThebeCodeCell(
target.cellId,
notebook.id,
codeCell.value ?? '',
codeCell.value ?? '', //source
[block.data] ?? [{}],
config,
block.data ?? {},
metadata,
notebook.rendermime,
);
} else {
// assume content - concatenate it
// TODO inject cell metadata
const cell = new core.ThebeMarkdownCell(
block.key,
notebook.id,
Expand Down
26 changes: 16 additions & 10 deletions packages/jupyter/src/jupyter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { fetchAndEncodeOutputImages } from './convertImages.js';
import type { ThebeCore } from 'thebe-core';
import { SourceFileKind } from 'myst-spec-ext';
import { useXRefState } from '@myst-theme/providers';
import { useThebeLoader } from 'thebe-react';
import { useThebeLoader } from 'thebe-react';
import { useCellExecution } from './execute/index.js';
import { usePlaceholder } from './decoration.js';
import { MyST } from 'myst-to-react';
Expand Down Expand Up @@ -37,7 +37,7 @@ function ActiveOutputRenderer({
console.debug(`${verb} cell ${exec.cell.id} to DOM at:`, {
el: ref.current,
connected: ref.current.isConnected,
data: core?.stripWidgets(initialData) ?? initialData,
data: initialData,
});

exec.cell.attachToDOM(ref.current);
Expand Down Expand Up @@ -76,19 +76,25 @@ function PassiveOutputRenderer({
core: ThebeCore;
kind: SourceFileKind;
}) {
const rendermime = core.makeRenderMimeRegistry();
const exec = useCellExecution(id);

const cell = useRef(new core.PassiveCellRenderer(id, rendermime, undefined));
const cell = useRef<any>(null); // Initially null
const ref = useRef<HTMLDivElement>(null);

const { loaded } = usePlotlyPassively(rendermime, data);
// Use exec.passive.rendermime or fallback to core.makeRenderMimeRegistry()
const { loaded } = usePlotlyPassively(exec.passive?.rendermime ?? core.makeRenderMimeRegistry(), data);

useEffect(() => {
if (!ref.current || !loaded) return;
// eslint-disable-next-line import/no-extraneous-dependencies
cell.current.attachToDOM(ref.current ?? undefined, true);
cell.current.render(core?.stripWidgets(data) ?? data);
}, [ref, loaded]);
if (!ref.current || !loaded || !exec.passive?.rendermime) return;

// Initialize cell when exec.passive.rendermime is available
cell.current = new core.PassiveCellRenderer(id, data, exec.passive.rendermime);

cell.current.attachToDOM(ref.current ?? undefined, { appendExisting: true });

// Render regular output
cell.current.render(data);
}, [ref, loaded, exec.passive?.rendermime, id, data, core]);

return <div ref={ref} data-thebe-passive-ref="true" />;
}
Expand Down

0 comments on commit 5619777

Please sign in to comment.