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

feature/thebe #560

Merged
merged 55 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ed34f9f
add thebe deps
danieleguido Jun 20, 2023
33c502c
add article version 3
danieleguido Jun 20, 2023
ed3fc47
add provider wrapper to reduce complexity and useArticleThebe hook fo…
danieleguido Jun 20, 2023
4243627
Create Article.js
danieleguido Jun 20, 2023
de2340b
Create index.js
danieleguido Jun 20, 2023
04326a0
Update ArticleThebeProvider.js
danieleguido Jun 20, 2023
3148792
Merge branch 'develop' into feature/thebe
danieleguido Jun 22, 2023
6928622
Merge branch 'develop' into feature/thebe
danieleguido Jun 28, 2023
be24aaf
Merge branch 'develop' into feature/thebe
danieleguido Aug 31, 2023
14a4c7c
Merge branch 'develop' into feature/thebe
danieleguido Sep 12, 2023
72a1fa5
fix error on not found functions
danieleguido Sep 12, 2023
99e8654
fix Slider when missing autoplaySpeed props
danieleguido Sep 13, 2023
a7c3261
Merge branch 'develop' into feature/thebe
danieleguido Nov 6, 2023
984a9d9
update article component with url from notebook viewer form component
danieleguido Nov 7, 2023
72ba834
moved to latest thebe version
stevejpurves Nov 13, 2023
3236656
minimal provider and cell hookup
stevejpurves Nov 13, 2023
5b9557e
Merge pull request #588 from C2DH/feat/initial-thebe-controls
stevejpurves Nov 14, 2023
8872d0b
using raw json outputs with existing jdh rendering, some error integr…
stevejpurves Nov 14, 2023
fb0c463
better naming
stevejpurves Nov 14, 2023
8609164
execution state via zustand
stevejpurves Nov 15, 2023
17cc6cf
Merge pull request #589 from C2DH/feature/thebe-raw-outputs
stevejpurves Nov 15, 2023
087ebfe
pre-cell error reporting
stevejpurves Nov 16, 2023
1ef3136
Errortray on top
stevejpurves Nov 16, 2023
34f8c44
fixes to runAll
stevejpurves Nov 16, 2023
9c38eff
add react codemirror
danieleguido Nov 16, 2023
0906d7f
add updateCellSource function in ExecutionScope state
danieleguido Nov 16, 2023
4a4dfc9
Create ArticleCellEditor.js
danieleguido Nov 16, 2023
3fbdf19
updateCellSource on codemirror change event
danieleguido Nov 16, 2023
2b8a457
add source
danieleguido Nov 16, 2023
b52439c
fix binder comment
stevejpurves Nov 17, 2023
ff03bfb
no computed objects
stevejpurves Nov 17, 2023
45e0067
extended development envvar usage
stevejpurves Nov 20, 2023
babf797
expose execution count
stevejpurves Nov 20, 2023
0d68acd
envvar for binder is a string
stevejpurves Nov 20, 2023
bcb1b33
fix cell errors
stevejpurves Nov 20, 2023
cc4b465
editing with persistent state
stevejpurves Nov 20, 2023
1e28298
added function to recover binder url
stevejpurves Nov 20, 2023
51a42ab
top level connection status
stevejpurves Nov 22, 2023
ee1c621
ui tweaks
stevejpurves Nov 22, 2023
af9d02d
added clear saved sessions example
stevejpurves Nov 22, 2023
4ea7bcb
open in jupyter example
stevejpurves Nov 22, 2023
c8df614
deriving path from url
stevejpurves Nov 22, 2023
f0f19ce
bump thebe
stevejpurves Nov 22, 2023
ea8bfe7
better connection status and error examples
stevejpurves Nov 22, 2023
507bf7b
fix binderOptions and update comment on kernelName for R
stevejpurves Nov 23, 2023
e9bfc7f
ui tweak
stevejpurves Nov 23, 2023
1c8c4ca
🎚enable clean shutdown
stevejpurves Nov 23, 2023
12f3398
bump thebe-react
stevejpurves Nov 23, 2023
24bb7f2
curvenote binder
stevejpurves Nov 23, 2023
6d8686a
trying to fixing dependency issue
stevejpurves Nov 23, 2023
e4c2882
Add default varialble in .env file and change console output
danieleguido Nov 24, 2023
d72992d
fixed shutdown callback
stevejpurves Nov 27, 2023
6d91934
Merge branch 'develop' into feature/thebe
danieleguido Mar 18, 2024
34f399f
Update WindowEvents.js
danieleguido Mar 18, 2024
b655f90
use only env variables to set up ArticleThebeProvider
danieleguido Mar 18, 2024
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
8 changes: 7 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ REACT_APP_TAG_EDITORIAL=editorial
REACT_APP_ENABLE_AUTH_0=false
REACT_APP_TWITTER=Journal_DigHist
REACT_APP_FACEBOOK=journalofdigitalhistory
REACT_APP_THEBE_TOKEN=********
REACT_APP_THEBE_DEV_BINDER=false
REACT_APP_THEBE_JUPYTER_URL=http://localhost:8888
REACT_APP_THEBE_BINDER_URL=https://mybinder.org
REACT_APP_GITHUB=https://github.com/C2DH/journal-of-digital-history
REACT_APP_GITHUB_RELEASES_API_ENDPOINT=https://api.github.com/repos/c2dh/journal-of-digital-history/releases

Expand All @@ -32,4 +36,6 @@ REACT_APP_MATOMO_SITEID=1
REACT_APP_MATOMO_URLBASE=https://journalofdigitalhistory.matomo.cloud/
REACT_APP_WIKI_GUIDELINES=/proxy-githubusercontent/wiki/c2dh/journal-of-digital-history/Guidelines.md
REACT_APP_LAUNCH_BINDER_BADGE_URL="https://img.shields.io/badge/-binder-579ACA.svg?logo=data:image/png;base64,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"
REACT_APP_STORYBOOK_BASE_64_PNG_SRC="iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"
REACT_APP_STORYBOOK_BASE_64_PNG_SRC="iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"

GENERATE_SOURCEMAP=false
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ node_modules
.development.env
.env.development
.vscode/settings.json
.yalc/*
yalc.lock
storybook-static
# _redirects
journal-of-digital-history.code-workspace
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {
"@auth0/auth0-react": "^1.1.0",
"@c2dh/react-facets": "^1.1.0",
"@curvenote/ansi-to-react": "^7.0.0",
"@gerhobbelt/markdown-it-attrs": "^3.0.3-20",
"@jonkoops/matomo-tracker-react": "^0.7.0",
"@react-spring/web": "^9.7.3",
Expand All @@ -25,6 +26,7 @@
"axios": "^1.6.7",
"bootstrap": "^5.2.3",
"citation-js": "0.6.4",
"codemirror": "5",
"d3-array": "^2.9.1",
"d3-scale": "^3.2.3",
"deepdash-es": "^5.3.0",
Expand All @@ -51,6 +53,7 @@
"query-string": "^7.0.1",
"react": "^18.2.0",
"react-bootstrap": "^2.7.2",
"react-codemirror2": "^7.3.0",
"react-copy-to-clipboard": "^5.0.4",
"react-device-detect": "^1.14.0",
"react-dom": "^18.2.0",
Expand All @@ -70,6 +73,8 @@
"react-window": "^1.8.5",
"sass": "^1.39.2",
"source-map-explorer": "^2.4.2",
"thebe-core": "^0.4.2",
"thebe-react": "^0.4.2",
"typescript": "^3.9.7",
"universal-cookie": "^4.0.4",
"use-query-params": "^1.2.2",
Expand Down Expand Up @@ -124,6 +129,9 @@
"storybook-react-i18next": "^1.1.2",
"webpack": "5"
},
"resolutions": {
"@lumino/algorithm": "2.0.1"
},
"eslintConfig": {
"overrides": [
{
Expand Down
94 changes: 94 additions & 0 deletions src/components/ArticleV3/Article.js
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
42 changes: 42 additions & 0 deletions src/components/ArticleV3/ArticleCellEditor.js
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
26 changes: 26 additions & 0 deletions src/components/ArticleV3/ArticleCellError.js
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
9 changes: 9 additions & 0 deletions src/components/ArticleV3/ArticleCellSourceCodeWrapper.js
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" />
}
112 changes: 112 additions & 0 deletions src/components/ArticleV3/ArticleExecuteToolbar.js
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>
)
}
Loading