-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add thebe deps * add URL param to switch article from v2 to v3 (thebe) * fix Slider when missing autoplaySpeed props in Holme page :) * update article component with url from notebook viewer form component * manage thebe execution state via zustand * pre-cell error reporting * Errortray on top * fixes to runAll * add react codemirror * add updateCellSource function in ExecutionScope state * Create ArticleCellEditor.js * updateCellSource on codemirror change event * add source * fix binder comment * no computed objects * extended development envvar usage * expose execution count * envvar for binder is a string * fix cell errors * editing with persistent state * added function to recover binder url * top level connection status * ui tweaks * added clear saved sessions example * open in jupyter example * deriving path from url * bump thebe * better connection status and error examples * fix binderOptions and update comment on kernelName for R * ui tweak * 🎚enable clean shutdown * bump thebe-react * curvenote binder * trying to fixing dependency issue * Add default varialble in .env file and change console output * fixed shutdown callback * Update WindowEvents.js * use only env variables to set up ArticleThebeProvider --------- Co-authored-by: stevejpurves <[email protected]>
- Loading branch information
1 parent
77cc7fd
commit 55cc735
Showing
20 changed files
with
9,961 additions
and
4,574 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import React, { useEffect } from 'react' | ||
import { Container } from 'react-bootstrap' | ||
import { ArticleThebeProvider, useArticleThebe } from './ArticleThebeProvider' | ||
import SimpleArticleCell from './SimpleArticleCell' | ||
import { useNotebook } from './hooks' | ||
import ConnectionErrorBox from './ConnectionErrorBox' | ||
|
||
import ArticleExecuteToolbar from './ArticleExecuteToolbar' | ||
import { useExecutionScope } from './ExecutionScope' | ||
|
||
const Article = ({ url = '', paragraphs }) => { | ||
const { starting, connectionErrors, ready, connectAndStart, restart, session, openInJupyter } = | ||
useArticleThebe() | ||
|
||
useEffect(() => { | ||
if (!connectionErrors) return | ||
// if there is a connection error, we want to ensure that the compute UI is | ||
// disabled or in an approprate state - set a disabled flag in the execution state? | ||
}, [connectionErrors]) | ||
|
||
const attachSession = useExecutionScope((state) => state.attachSession) | ||
|
||
useEffect(() => { | ||
if (!ready) return | ||
attachSession(session) | ||
}, [ready]) | ||
|
||
console.debug('[Article]', url, 'is rendering') | ||
|
||
return ( | ||
<Container> | ||
<div style={{ paddingTop: 120 }}></div> | ||
|
||
<ArticleExecuteToolbar | ||
starting={starting} | ||
ready={ready} | ||
connectAndStart={connectAndStart} | ||
restart={restart} | ||
openInJupyter={openInJupyter} | ||
/> | ||
<ConnectionErrorBox /> | ||
{paragraphs.map((cell, idx) => { | ||
return ( | ||
<React.Fragment key={[url, idx].join('-')}> | ||
<a className="ArticleLayer_anchor"></a> | ||
<div | ||
className="ArticleLayer_paragraphWrapper" | ||
data-cell-idx={cell.idx} | ||
data-cell-layer={cell.layer} | ||
> | ||
<div className={`ArticleLayer_cellActive off`} /> | ||
<SimpleArticleCell | ||
isJavascriptTrusted={false} | ||
onNumClick={() => ({})} | ||
memoid={[url, idx].join('-')} | ||
{...cell} | ||
num={cell.num} | ||
idx={cell.idx} | ||
role={cell.role} | ||
layer={cell.layer} | ||
source={cell.source} | ||
headingLevel={cell.isHeading ? cell.heading.level : 0} | ||
windowHeight={800} | ||
ready={ready} | ||
/> | ||
</div> | ||
</React.Fragment> | ||
) | ||
})} | ||
</Container> | ||
) | ||
} | ||
|
||
function ArticleWithContent({ url, ipynb }) { | ||
const { paragraphs, executables } = useNotebook(url, ipynb) | ||
|
||
const initExecutionScope = useExecutionScope((state) => state.initialise) | ||
|
||
useEffect(() => { | ||
initExecutionScope(executables) | ||
}, [executables, initExecutionScope]) | ||
|
||
return <Article url={url} paragraphs={paragraphs} /> | ||
} | ||
|
||
function ThebeArticle({ url = '', ipynb = { cells: [], metadata: {} }, ...props }) { | ||
return ( | ||
<ArticleThebeProvider url={url} binderUrl={props.binderUrl}> | ||
<ArticleWithContent url={url} ipynb={ipynb} /> | ||
</ArticleThebeProvider> | ||
) | ||
} | ||
|
||
export default ThebeArticle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import React from 'react' | ||
import { Controlled as CodeMirror } from 'react-codemirror2' | ||
import { useExecutionScope } from './ExecutionScope' | ||
// import codemirror style | ||
import 'codemirror/lib/codemirror.css' | ||
// import codemirror dracula style | ||
import 'codemirror/theme/dracula.css' | ||
|
||
const ArticleCellEditor = ({ cellIdx = -1, options }) => { | ||
const source = useExecutionScope((state) => state.cells[cellIdx]?.source) ?? '' | ||
const [value, setValue] = React.useState(source) | ||
|
||
const updateCellSource = useExecutionScope((state) => state.updateCellSource) | ||
const onCellChangeHandler = (value) => { | ||
console.debug('[ArticleCell] onCellChangeHandler', cellIdx, { value }) | ||
updateCellSource(cellIdx, value) | ||
} | ||
|
||
return ( | ||
<CodeMirror | ||
value={value} | ||
options={{ | ||
theme: 'dracula', | ||
mode: 'python', | ||
lineNumbers: true, | ||
lineWrapping: true, | ||
styleActiveLine: true, | ||
matchBrackets: true, | ||
...options, | ||
}} | ||
onBeforeChange={(editor, data, value) => { | ||
setValue(value) | ||
}} | ||
onChange={(editor, data, value) => { | ||
console.debug('[ArticleCellEditor]', cellIdx, { value }) | ||
onCellChangeHandler(value) | ||
}} | ||
/> | ||
) | ||
} | ||
|
||
export default ArticleCellEditor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import ErrorContent from './ErrorContent' | ||
|
||
const ArticleCellError = ({ idx, errors, hideLabel = false }) => { | ||
const outputTypeClassName = `ArticleCellOutput_${errors[0].output_type}` | ||
const { t } = useTranslation() | ||
|
||
return ( | ||
<blockquote | ||
className={`${outputTypeClassName}`} | ||
style={{ backgroundColor: 'pink', borderLeftColor: 'red' }} | ||
> | ||
{hideLabel ? null : ( | ||
<div> | ||
<div className="label" style={{ color: 'red' }}> | ||
{t(outputTypeClassName)} | ||
</div> | ||
</div> | ||
)} | ||
<ErrorContent errors={errors} idx={idx} /> | ||
</blockquote> | ||
) | ||
} | ||
|
||
export default ArticleCellError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React from 'react' | ||
import { useExecutionScope } from './ExecutionScope' | ||
import ArticleCellSourceCode from '../Article/ArticleCellSourceCode' | ||
|
||
export default function ArticleCellSourceCodeWrapper(props) { | ||
const { cellIdx } = props | ||
const source = useExecutionScope((state) => state.cells[cellIdx]?.source) ?? '' | ||
return <ArticleCellSourceCode content={source} visible language="python" /> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import React, { useCallback } from 'react' | ||
import { useExecutionScope } from './ExecutionScope' | ||
import ConnectionStatusBox from './ConnectionStatusBox' | ||
import { useThebeLoader } from 'thebe-react' | ||
import { useArticleThebe } from './ArticleThebeProvider' | ||
|
||
export default function ArticleExecuteToolbar({ | ||
starting, | ||
ready, | ||
connectAndStart, | ||
restart, | ||
openInJupyter, | ||
}) { | ||
const { core } = useThebeLoader() | ||
const { shutdown } = useArticleThebe() | ||
const executing = useExecutionScope((state) => state.executing) | ||
const executeAll = useExecutionScope((state) => state.executeAll) | ||
const clearAll = useExecutionScope((state) => state.clearAll) | ||
const resetAll = useExecutionScope((state) => state.resetAll) | ||
|
||
const shutdownAndReset = useCallback(() => { | ||
resetAll() | ||
shutdown() | ||
}, [shutdown, resetAll]) | ||
|
||
const clearSavedSessions = useCallback(() => { | ||
if (!core) return | ||
core.clearAllSavedSessions() | ||
// Note: is is possible to clear the saved session only for this article | ||
// provided yo ucan supply the sotragePrefix and correct (repository) url | ||
// to core.clearSavedSession(storagePrefix, url) | ||
}, [core]) | ||
|
||
console.log('[ArticleExecuteToolbar]', { starting, ready, executing }, 'rendering') | ||
|
||
return ( | ||
<div style={{ position: 'sticky', top: 100, zIndex: 10, marginBottom: 12 }}> | ||
{!starting && !ready && ( | ||
<> | ||
<button | ||
style={{ margin: '4px', color: 'green' }} | ||
disabled={starting || ready} | ||
onClick={connectAndStart} | ||
> | ||
Start | ||
</button> | ||
<button | ||
style={{ margin: '4px', color: 'green' }} | ||
disabled={starting || ready} | ||
onClick={clearSavedSessions} | ||
title="upon successful connection to binderhub the session connection information is saved in local storage. This button will clear that information and force new servers to be started." | ||
> | ||
Clear Saved Sessions | ||
</button> | ||
</> | ||
)} | ||
{starting && ( | ||
<span | ||
style={{ | ||
display: 'inline-block', | ||
padding: 4, | ||
width: '100%', | ||
backgroundColor: 'lightgreen', | ||
}} | ||
> | ||
Starting... | ||
</span> | ||
)} | ||
{ready && ( | ||
<div | ||
style={{ | ||
display: 'flex', | ||
padding: 4, | ||
backgroundColor: 'lightgreen', | ||
width: '100%', | ||
alignItems: 'center', | ||
gap: 4, | ||
}} | ||
> | ||
{executing ? 'RUNNING...' : 'READY'} | ||
<div style={{ flexGrow: 1 }} /> | ||
<button | ||
onClick={() => { | ||
clearAll() | ||
executeAll() | ||
}} | ||
disabled={executing} | ||
> | ||
run all | ||
</button> | ||
<button onClick={clearAll} disabled={executing}> | ||
clear all | ||
</button> | ||
<button onClick={resetAll} disabled={executing}> | ||
reset all | ||
</button> | ||
<button onClick={restart} disabled={executing}> | ||
restart kernel | ||
</button> | ||
{/* TODO: feed notebook name in here if different */} | ||
<button onClick={() => openInJupyter('article.ipynb')} disabled={executing}> | ||
jupyter | ||
</button> | ||
<button onClick={shutdownAndReset} disabled={executing}> | ||
shutdown | ||
</button> | ||
</div> | ||
)} | ||
<ConnectionStatusBox /> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.