docusaurus-live-brython 3.0.0-beta.9 → 3.0.1
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.
- package/.devcontainer/devcontainer.json +38 -0
- package/.prettierignore +17 -0
- package/.prettierrc +9 -8
- package/CHANGELOG.md +5 -0
- package/README.md +7 -4
- package/lib/assets/py_back_trace.py +2 -1
- package/lib/index.d.ts +1 -7
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +94 -16
- package/lib/options.d.ts +1 -0
- package/lib/options.d.ts.map +1 -0
- package/lib/options.js +2 -2
- package/lib/theme/CodeBlock/index.d.ts +3 -3
- package/lib/theme/CodeBlock/index.d.ts.map +1 -0
- package/lib/theme/CodeBlock/index.jsx +24 -14
- package/lib/theme/CodeEditor/Actions/DownloadCode.d.ts +1 -0
- package/lib/theme/CodeEditor/Actions/DownloadCode.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Actions/DownloadCode.jsx +8 -8
- package/lib/theme/CodeEditor/Actions/Reset.d.ts +1 -0
- package/lib/theme/CodeEditor/Actions/Reset.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Actions/Reset.jsx +5 -5
- package/lib/theme/CodeEditor/Actions/RunCode.d.ts +2 -1
- package/lib/theme/CodeEditor/Actions/RunCode.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Actions/RunCode.jsx +4 -4
- package/lib/theme/CodeEditor/Actions/ShowRaw.d.ts +1 -0
- package/lib/theme/CodeEditor/Actions/ShowRaw.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Actions/ShowRaw.jsx +13 -7
- package/lib/theme/CodeEditor/Actions/ShowSyncStatus.d.ts +1 -0
- package/lib/theme/CodeEditor/Actions/ShowSyncStatus.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Actions/ShowSyncStatus.jsx +21 -16
- package/lib/theme/CodeEditor/Actions/styles.module.css +2 -3
- package/lib/theme/CodeEditor/BrythonCommunicator.d.ts +2 -1
- package/lib/theme/CodeEditor/BrythonCommunicator.d.ts.map +1 -0
- package/lib/theme/CodeEditor/BrythonCommunicator.jsx +13 -9
- package/lib/theme/CodeEditor/Button/index.d.ts +3 -2
- package/lib/theme/CodeEditor/Button/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Button/index.jsx +1 -1
- package/lib/theme/CodeEditor/Button/styles.module.css +1 -1
- package/lib/theme/CodeEditor/CodeHistory/index.d.ts +1 -0
- package/lib/theme/CodeEditor/CodeHistory/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/CodeHistory/index.jsx +26 -15
- package/lib/theme/CodeEditor/CodeHistory/styles.module.css +31 -31
- package/lib/theme/CodeEditor/ContextEditor/index.d.ts +22 -0
- package/lib/theme/CodeEditor/ContextEditor/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/ContextEditor/index.jsx +36 -0
- package/lib/theme/CodeEditor/Editor/EditorAce.d.ts +2 -1
- package/lib/theme/CodeEditor/Editor/EditorAce.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/EditorAce.jsx +17 -14
- package/lib/theme/CodeEditor/Editor/Header/index.d.ts +2 -1
- package/lib/theme/CodeEditor/Editor/Header/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/Header/index.jsx +12 -12
- package/lib/theme/CodeEditor/Editor/Header/styles.module.css +7 -7
- package/lib/theme/CodeEditor/Editor/HiddenCode/index.d.ts +8 -0
- package/lib/theme/CodeEditor/Editor/HiddenCode/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/HiddenCode/index.jsx +27 -0
- package/lib/theme/CodeEditor/Editor/HiddenCode/styles.module.css +52 -0
- package/lib/theme/CodeEditor/Editor/Result/Graphics/Canvas.d.ts +1 -0
- package/lib/theme/CodeEditor/Editor/Result/Graphics/Canvas.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/Result/Graphics/Canvas.jsx +8 -9
- package/lib/theme/CodeEditor/Editor/Result/Graphics/Turtle.d.ts +1 -0
- package/lib/theme/CodeEditor/Editor/Result/Graphics/Turtle.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/Result/Graphics/Turtle.jsx +11 -11
- package/lib/theme/CodeEditor/Editor/Result/Graphics/index.d.ts +4 -3
- package/lib/theme/CodeEditor/Editor/Result/Graphics/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/Result/Graphics/index.jsx +12 -10
- package/lib/theme/CodeEditor/Editor/Result/Graphics/styles.module.css +2 -2
- package/lib/theme/CodeEditor/Editor/Result/index.d.ts +2 -3
- package/lib/theme/CodeEditor/Editor/Result/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/Result/index.jsx +6 -9
- package/lib/theme/CodeEditor/Editor/Result/styles.module.css +15 -10
- package/lib/theme/CodeEditor/Editor/index.d.ts +6 -3
- package/lib/theme/CodeEditor/Editor/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/index.jsx +34 -28
- package/lib/theme/CodeEditor/Editor/styles.module.css +15 -7
- package/lib/theme/CodeEditor/Editor/utils/checkForButtonClick.d.ts +1 -0
- package/lib/theme/CodeEditor/Editor/utils/checkForButtonClick.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/utils/saveSvg.d.ts +1 -0
- package/lib/theme/CodeEditor/Editor/utils/saveSvg.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/utils/saveSvg.js +19 -8
- package/lib/theme/CodeEditor/Editor/utils/svgWithoutAnimations.d.ts +1 -0
- package/lib/theme/CodeEditor/Editor/utils/svgWithoutAnimations.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Editor/utils/svgWithoutAnimations.js +43 -49
- package/lib/theme/CodeEditor/Icon/icons.d.ts +4 -1
- package/lib/theme/CodeEditor/Icon/icons.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Icon/icons.js +3 -1
- package/lib/theme/CodeEditor/Icon/index.d.ts +4 -2
- package/lib/theme/CodeEditor/Icon/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/Icon/index.jsx +10 -3
- package/lib/theme/CodeEditor/Icon/styles.module.css +1 -1
- package/lib/theme/CodeEditor/WithScript/ScriptContext.d.ts +8 -0
- package/lib/theme/CodeEditor/WithScript/ScriptContext.d.ts.map +1 -0
- package/lib/theme/CodeEditor/WithScript/ScriptContext.jsx +27 -0
- package/lib/theme/CodeEditor/WithScript/Storage.d.ts +2 -1
- package/lib/theme/CodeEditor/WithScript/Storage.d.ts.map +1 -0
- package/lib/theme/CodeEditor/WithScript/Types.d.ts +12 -4
- package/lib/theme/CodeEditor/WithScript/Types.d.ts.map +1 -0
- package/lib/theme/CodeEditor/WithScript/bryRunner.d.ts +3 -0
- package/lib/theme/CodeEditor/WithScript/bryRunner.d.ts.map +1 -0
- package/lib/theme/CodeEditor/WithScript/bryRunner.js +29 -0
- package/lib/theme/CodeEditor/WithScript/createStore.d.ts +4 -0
- package/lib/theme/CodeEditor/WithScript/createStore.d.ts.map +1 -0
- package/lib/theme/CodeEditor/WithScript/{Store.jsx → createStore.js} +62 -74
- package/lib/theme/CodeEditor/WithScript/helpers.d.ts +1 -4
- package/lib/theme/CodeEditor/WithScript/helpers.d.ts.map +1 -0
- package/lib/theme/CodeEditor/WithScript/helpers.js +4 -14
- package/lib/theme/CodeEditor/constants.d.ts +1 -0
- package/lib/theme/CodeEditor/constants.d.ts.map +1 -0
- package/lib/theme/CodeEditor/hooks/index.d.ts +3 -0
- package/lib/theme/CodeEditor/hooks/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/hooks/index.js +2 -0
- package/lib/theme/CodeEditor/hooks/useScript.d.ts +3 -0
- package/lib/theme/CodeEditor/hooks/useScript.d.ts.map +1 -0
- package/lib/theme/CodeEditor/hooks/useScript.js +4 -0
- package/lib/theme/CodeEditor/hooks/useStore.d.ts +3 -0
- package/lib/theme/CodeEditor/hooks/useStore.d.ts.map +1 -0
- package/lib/theme/CodeEditor/hooks/useStore.js +10 -0
- package/lib/theme/CodeEditor/index.d.ts +24 -5
- package/lib/theme/CodeEditor/index.d.ts.map +1 -0
- package/lib/theme/CodeEditor/index.jsx +17 -16
- package/lib/theme/CodeEditor/styles.module.css +28 -30
- package/og-image.md +23 -0
- package/package.json +35 -20
- package/src/assets/py_back_trace.py +2 -1
- package/src/index.ts +96 -25
- package/src/options.ts +12 -12
- package/src/theme/CodeBlock/index.tsx +44 -68
- package/src/theme/CodeEditor/Actions/DownloadCode.tsx +23 -22
- package/src/theme/CodeEditor/Actions/Reset.tsx +14 -12
- package/src/theme/CodeEditor/Actions/RunCode.tsx +14 -11
- package/src/theme/CodeEditor/Actions/ShowRaw.tsx +17 -11
- package/src/theme/CodeEditor/Actions/ShowSyncStatus.tsx +32 -27
- package/src/theme/CodeEditor/Actions/styles.module.css +2 -3
- package/src/theme/CodeEditor/BrythonCommunicator.tsx +16 -19
- package/src/theme/CodeEditor/Button/index.tsx +17 -13
- package/src/theme/CodeEditor/Button/styles.module.css +1 -1
- package/src/theme/CodeEditor/CodeHistory/index.tsx +32 -20
- package/src/theme/CodeEditor/CodeHistory/styles.module.css +31 -31
- package/src/theme/CodeEditor/ContextEditor/index.tsx +74 -0
- package/src/theme/CodeEditor/Editor/EditorAce.tsx +20 -16
- package/src/theme/CodeEditor/Editor/Header/index.tsx +13 -19
- package/src/theme/CodeEditor/Editor/Header/styles.module.css +7 -7
- package/src/theme/CodeEditor/Editor/HiddenCode/index.tsx +49 -0
- package/src/theme/CodeEditor/Editor/HiddenCode/styles.module.css +52 -0
- package/src/theme/CodeEditor/Editor/Result/Graphics/Canvas.tsx +25 -22
- package/src/theme/CodeEditor/Editor/Result/Graphics/Turtle.tsx +23 -19
- package/src/theme/CodeEditor/Editor/Result/Graphics/index.tsx +16 -16
- package/src/theme/CodeEditor/Editor/Result/Graphics/styles.module.css +2 -2
- package/src/theme/CodeEditor/Editor/Result/index.tsx +7 -13
- package/src/theme/CodeEditor/Editor/Result/styles.module.css +15 -10
- package/src/theme/CodeEditor/Editor/index.tsx +67 -65
- package/src/theme/CodeEditor/Editor/styles.module.css +15 -7
- package/src/theme/CodeEditor/Editor/utils/checkForButtonClick.ts +5 -5
- package/src/theme/CodeEditor/Editor/utils/saveSvg.ts +63 -53
- package/src/theme/CodeEditor/Editor/utils/svgWithoutAnimations.ts +182 -201
- package/src/theme/CodeEditor/Icon/icons.ts +27 -13
- package/src/theme/CodeEditor/Icon/index.tsx +31 -11
- package/src/theme/CodeEditor/Icon/styles.module.css +1 -1
- package/src/theme/CodeEditor/WithScript/ScriptContext.tsx +36 -0
- package/src/theme/CodeEditor/WithScript/Storage.ts +3 -3
- package/src/theme/CodeEditor/WithScript/Types.ts +17 -11
- package/src/theme/CodeEditor/WithScript/bryRunner.ts +39 -0
- package/src/theme/CodeEditor/WithScript/createStore.ts +276 -0
- package/src/theme/CodeEditor/WithScript/helpers.ts +16 -26
- package/src/theme/CodeEditor/constants.ts +9 -11
- package/src/theme/CodeEditor/hooks/index.ts +2 -0
- package/src/theme/CodeEditor/hooks/useScript.ts +9 -0
- package/src/theme/CodeEditor/hooks/useStore.ts +15 -0
- package/src/theme/CodeEditor/index.tsx +45 -31
- package/src/theme/CodeEditor/styles.module.css +28 -30
- package/src/typings.d.ts +11 -0
- package/lib/theme/CodeEditor/WithScript/Store.d.ts +0 -15
- package/lib/types.d.ts +0 -28
- package/lib/types.js +0 -1
- package/src/theme/CodeEditor/WithScript/Store.tsx +0 -294
- package/src/types.ts +0 -29
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import * as Icons from '
|
|
2
|
+
import * as Icons from '@theme/CodeEditor/Icon/icons';
|
|
3
3
|
import styles from './styles.module.css';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
5
|
|
|
@@ -13,10 +13,11 @@ export enum Color {
|
|
|
13
13
|
Link = 'var(--ifm-color-link)'
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
interface Props {
|
|
16
|
+
export interface Props {
|
|
17
17
|
icon: keyof typeof Icons;
|
|
18
18
|
size?: number | string;
|
|
19
19
|
spin?: boolean;
|
|
20
|
+
rotate?: number;
|
|
20
21
|
color?: string;
|
|
21
22
|
className?: string;
|
|
22
23
|
}
|
|
@@ -26,20 +27,39 @@ const Icon = (props: Props) => {
|
|
|
26
27
|
|
|
27
28
|
if (props.spin) {
|
|
28
29
|
return (
|
|
29
|
-
<svg
|
|
30
|
+
<svg
|
|
31
|
+
viewBox="0 0 24 24"
|
|
32
|
+
role="presentation"
|
|
33
|
+
style={{ width: size, height: size }}
|
|
34
|
+
className={clsx(props.className, styles.icon, styles.spin)}
|
|
35
|
+
>
|
|
30
36
|
<style>{`@keyframes spin-inverse { to { transform: rotate(-360deg) } }`}</style>
|
|
31
|
-
<g
|
|
32
|
-
|
|
37
|
+
<g
|
|
38
|
+
style={{
|
|
39
|
+
animation: '2s linear 0s infinite normal none running spin-inverse',
|
|
40
|
+
transformOrigin: 'center center'
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<path d={Icons[props.icon]} style={{ fill: props.color || 'currentcolor' }}></path>
|
|
33
44
|
</g>
|
|
34
45
|
</svg>
|
|
35
|
-
)
|
|
46
|
+
);
|
|
36
47
|
}
|
|
37
48
|
|
|
38
49
|
return (
|
|
39
|
-
<svg
|
|
40
|
-
|
|
50
|
+
<svg
|
|
51
|
+
viewBox="0 0 24 24"
|
|
52
|
+
role="presentation"
|
|
53
|
+
style={{
|
|
54
|
+
width: size,
|
|
55
|
+
height: size,
|
|
56
|
+
transform: `translateY(15%) rotate(${props.rotate || 0}deg)`
|
|
57
|
+
}}
|
|
58
|
+
className={clsx(props.className, styles.icon)}
|
|
59
|
+
>
|
|
60
|
+
<path d={Icons[props.icon]} style={{ fill: props.color || 'currentcolor' }}></path>
|
|
41
61
|
</svg>
|
|
42
|
-
)
|
|
43
|
-
}
|
|
62
|
+
);
|
|
63
|
+
};
|
|
44
64
|
|
|
45
|
-
export default Icon;
|
|
65
|
+
export default Icon;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { usePluginData } from '@docusaurus/useGlobalData';
|
|
3
|
+
import { type InitState, type Document } from '@theme/CodeEditor/WithScript/Types';
|
|
4
|
+
import { createStore } from '@theme/CodeEditor/WithScript/createStore';
|
|
5
|
+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
6
|
+
import BrowserOnly from '@docusaurus/BrowserOnly';
|
|
7
|
+
import CodeBlock from '@theme/CodeBlock';
|
|
8
|
+
export const Context = React.createContext<Document | undefined>(undefined);
|
|
9
|
+
|
|
10
|
+
const ScriptContext = (props: InitState & { children: React.ReactNode }) => {
|
|
11
|
+
const { libDir, syncMaxOnceEvery } = usePluginData('docusaurus-live-brython') as {
|
|
12
|
+
libDir: string;
|
|
13
|
+
syncMaxOnceEvery: number;
|
|
14
|
+
};
|
|
15
|
+
const [store, setStore] = React.useState<Document | null>(null);
|
|
16
|
+
const { siteConfig } = useDocusaurusContext();
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
const router = siteConfig.future.experimental_router;
|
|
19
|
+
const store = createStore(props, libDir, syncMaxOnceEvery, router);
|
|
20
|
+
setStore(store);
|
|
21
|
+
store.load();
|
|
22
|
+
}, [props.id, libDir, siteConfig]);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<BrowserOnly fallback={<CodeBlock language={props.lang}>{props.code}</CodeBlock>}>
|
|
26
|
+
{() => {
|
|
27
|
+
if (!store) {
|
|
28
|
+
return <CodeBlock language={props.lang}>{props.code}</CodeBlock>;
|
|
29
|
+
}
|
|
30
|
+
return <Context.Provider value={store}>{props.children}</Context.Provider>;
|
|
31
|
+
}}
|
|
32
|
+
</BrowserOnly>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default ScriptContext;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StorageSlot, StoredScript } from
|
|
1
|
+
import type { StorageSlot, StoredScript } from '@theme/CodeEditor/WithScript/Types';
|
|
2
2
|
|
|
3
3
|
export const getStorageScript = (storage: StorageSlot): StoredScript | undefined => {
|
|
4
4
|
const storedCode = storage.get();
|
|
@@ -14,7 +14,7 @@ export const getStorageScript = (storage: StorageSlot): StoredScript | undefined
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
return;
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
|
|
19
19
|
export const syncStorageScript = (script: StoredScript, storage: StorageSlot): boolean => {
|
|
20
20
|
try {
|
|
@@ -24,4 +24,4 @@ export const syncStorageScript = (script: StoredScript, storage: StorageSlot): b
|
|
|
24
24
|
console.warn(`Failed to save the code ${script}`, e);
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
|
-
}
|
|
27
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
export interface Version {
|
|
3
2
|
code: string;
|
|
4
3
|
createdAt: Date;
|
|
@@ -13,6 +12,7 @@ export interface StoredScript {
|
|
|
13
12
|
versions: Version[];
|
|
14
13
|
}
|
|
15
14
|
|
|
15
|
+
export type Selector<T, R> = (state: T) => R;
|
|
16
16
|
export interface Script extends StoredScript {
|
|
17
17
|
/**
|
|
18
18
|
* this is normally a uuid
|
|
@@ -22,7 +22,7 @@ export interface Script extends StoredScript {
|
|
|
22
22
|
* this is the codeId used to
|
|
23
23
|
* - identify dom elements for this block
|
|
24
24
|
* - setup the brython communicator with this id
|
|
25
|
-
* - when using the default storage, this is the key used to
|
|
25
|
+
* - when using the default storage, this is the key used to
|
|
26
26
|
* store the code to local storage
|
|
27
27
|
*/
|
|
28
28
|
codeId: string;
|
|
@@ -30,20 +30,21 @@ export interface Script extends StoredScript {
|
|
|
30
30
|
showRaw: boolean;
|
|
31
31
|
isExecuting?: boolean;
|
|
32
32
|
preCode: string;
|
|
33
|
+
postCode: string;
|
|
33
34
|
lang: 'py' | string;
|
|
34
35
|
logs: LogMessage[];
|
|
35
|
-
|
|
36
|
+
graphicsModalExecutionNr: number;
|
|
36
37
|
hasGraphicsOutput: boolean;
|
|
37
38
|
hasTurtleOutput: boolean;
|
|
38
39
|
hasCanvasOutput: boolean;
|
|
39
40
|
hasEdits: boolean;
|
|
40
41
|
/**
|
|
41
42
|
* Storage props
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
*/
|
|
44
|
+
isLoaded: boolean;
|
|
45
|
+
status: Status;
|
|
46
|
+
versionsLoaded: boolean;
|
|
47
|
+
isPasted: boolean;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export interface LogMessage {
|
|
@@ -70,23 +71,28 @@ export interface InitState {
|
|
|
70
71
|
id: string | undefined;
|
|
71
72
|
lang: 'py' | string;
|
|
72
73
|
title: string;
|
|
73
|
-
|
|
74
|
+
code: string;
|
|
75
|
+
preCode: string;
|
|
76
|
+
postCode: string;
|
|
74
77
|
readonly: boolean;
|
|
75
78
|
versioned: boolean;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
export interface
|
|
81
|
+
export interface Document<T = Script> {
|
|
79
82
|
getState: () => T;
|
|
80
83
|
setState: (fn: (state: Script) => Script) => void;
|
|
81
84
|
subscribe: (listener: () => void) => () => void;
|
|
82
85
|
saveNow: () => Promise<Status>;
|
|
83
86
|
setCode: (code: string, action?: 'insert' | 'remove' | string) => void;
|
|
84
87
|
setExecuting: (executing: boolean) => void;
|
|
85
|
-
execScript: () => void
|
|
88
|
+
execScript: () => void;
|
|
86
89
|
stopScript: () => void;
|
|
87
90
|
closeGraphicsModal: () => void;
|
|
88
91
|
addLogMessage: (log: LogMessage) => void;
|
|
89
92
|
clearLogMessages: () => void;
|
|
90
93
|
load: () => Promise<Status>;
|
|
91
94
|
loadVersions: () => Promise<void>;
|
|
95
|
+
setIsPasted: (isPasted: boolean) => void;
|
|
96
|
+
setShowRaw: (showRaw: boolean) => void;
|
|
97
|
+
setStatus: (status: Status) => void;
|
|
92
98
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RouterType } from '@docusaurus/types';
|
|
2
|
+
import { DOM_ELEMENT_IDS } from '../constants';
|
|
3
|
+
import { sanitizePyScript } from './helpers';
|
|
4
|
+
|
|
5
|
+
export const runCode = (
|
|
6
|
+
code: string,
|
|
7
|
+
preCode: string,
|
|
8
|
+
postCode: string,
|
|
9
|
+
codeId: string,
|
|
10
|
+
libDir: string,
|
|
11
|
+
router: RouterType,
|
|
12
|
+
cache: boolean = true
|
|
13
|
+
) => {
|
|
14
|
+
const lineShift = preCode
|
|
15
|
+
.trim()
|
|
16
|
+
.split(/\n/)
|
|
17
|
+
.filter((l) => l.length > 0).length;
|
|
18
|
+
const pre = lineShift > 0 ? `${preCode.trim()}\n` : '';
|
|
19
|
+
const post = postCode.trim().length > 0 ? `\n${postCode.trim()}` : '';
|
|
20
|
+
const toExec = `${pre}${code}${post}`;
|
|
21
|
+
const src = `from brython_runner import run\nrun("""${sanitizePyScript(toExec || '')}""", '${codeId}', ${lineShift})\n`;
|
|
22
|
+
if (!(window as any).__BRYTHON__) {
|
|
23
|
+
alert('Brython not loaded');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const active = document.getElementById(DOM_ELEMENT_IDS.communicator(codeId));
|
|
27
|
+
active!.setAttribute('data--start-time', `${Date.now()}`);
|
|
28
|
+
/**
|
|
29
|
+
* ensure that the script is executed after the current event loop.
|
|
30
|
+
* Otherwise, the brython script will not be able to access the graphics output.
|
|
31
|
+
*/
|
|
32
|
+
setTimeout(() => {
|
|
33
|
+
(window as any).__BRYTHON__.runPythonSource(src, {
|
|
34
|
+
pythonpath: router === 'hash' ? [] : [libDir],
|
|
35
|
+
cache: cache
|
|
36
|
+
});
|
|
37
|
+
}, 0);
|
|
38
|
+
return src;
|
|
39
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { createStorageSlot } from '@docusaurus/theme-common';
|
|
3
|
+
import { getStorageScript, syncStorageScript } from '@theme/CodeEditor/WithScript/Storage';
|
|
4
|
+
import {
|
|
5
|
+
checkCanvasOutput,
|
|
6
|
+
checkGraphicsOutput,
|
|
7
|
+
checkTurtleOutput
|
|
8
|
+
} from '@theme/CodeEditor/WithScript/helpers';
|
|
9
|
+
import {
|
|
10
|
+
type InitState,
|
|
11
|
+
type LogMessage,
|
|
12
|
+
type Script,
|
|
13
|
+
Status,
|
|
14
|
+
type Document,
|
|
15
|
+
type StoredScript,
|
|
16
|
+
type Version
|
|
17
|
+
} from '@theme/CodeEditor/WithScript/Types';
|
|
18
|
+
import { DOM_ELEMENT_IDS } from '@theme/CodeEditor/constants';
|
|
19
|
+
import throttle from 'lodash/throttle';
|
|
20
|
+
import { RouterType } from '@docusaurus/types';
|
|
21
|
+
import { runCode } from '@theme/CodeEditor/WithScript/bryRunner';
|
|
22
|
+
|
|
23
|
+
export const createStore = (
|
|
24
|
+
props: InitState,
|
|
25
|
+
libDir: string,
|
|
26
|
+
syncMaxOnceEvery: number,
|
|
27
|
+
router: RouterType
|
|
28
|
+
): Document => {
|
|
29
|
+
const canSave = !!props.id;
|
|
30
|
+
const id = props.id || uuidv4();
|
|
31
|
+
const codeId = `code.${props.title || props.lang}.${id}`.replace(/(-|\.)/g, '_');
|
|
32
|
+
const createdAt = new Date();
|
|
33
|
+
const storageKey = `code.${props.title || 'code_block'}.${id}`;
|
|
34
|
+
const storage = createStorageSlot(storageKey);
|
|
35
|
+
storage.listen((e) => {
|
|
36
|
+
if (e.key === storageKey) {
|
|
37
|
+
try {
|
|
38
|
+
if (e.newValue) {
|
|
39
|
+
const script = JSON.parse(e.newValue) as StoredScript;
|
|
40
|
+
if (new Date(script.updatedAt) > state.updatedAt) {
|
|
41
|
+
loadData(storage);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.warn(err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const loadData = (store) => {
|
|
51
|
+
setState((s) => ({ ...s, status: canSave ? Status.SYNCING : s.status }));
|
|
52
|
+
const script = getStorageScript(store);
|
|
53
|
+
const loadedCode = script?.code ? prepareCode(script.code, { codeOnly: true }) : {};
|
|
54
|
+
addVersion.cancel();
|
|
55
|
+
if (!state.isLoaded) {
|
|
56
|
+
setState((s) => ({
|
|
57
|
+
...s,
|
|
58
|
+
isLoaded: true,
|
|
59
|
+
...(script || {}),
|
|
60
|
+
...loadedCode,
|
|
61
|
+
versions: script?.versions || [],
|
|
62
|
+
versionsLoaded: true,
|
|
63
|
+
status: canSave ? Status.SUCCESS : s.status
|
|
64
|
+
}));
|
|
65
|
+
return Status.SUCCESS;
|
|
66
|
+
}
|
|
67
|
+
if (script) {
|
|
68
|
+
setState((s) => ({
|
|
69
|
+
...s,
|
|
70
|
+
...script,
|
|
71
|
+
...loadedCode,
|
|
72
|
+
status: canSave ? Status.SUCCESS : s.status,
|
|
73
|
+
versionsLoaded: true
|
|
74
|
+
}));
|
|
75
|
+
return Status.SUCCESS;
|
|
76
|
+
}
|
|
77
|
+
setState((s) => ({ ...s, status: canSave ? Status.ERROR : s.status }));
|
|
78
|
+
return Status.ERROR;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const prepareCode = (
|
|
82
|
+
code: string,
|
|
83
|
+
config: { codeOnly?: boolean; stateNotInitialized?: boolean } = {}
|
|
84
|
+
) => {
|
|
85
|
+
const hasEdits = code !== (config.stateNotInitialized ? code : state.pristineCode);
|
|
86
|
+
const updatedAt = new Date();
|
|
87
|
+
const allCode = `${props.preCode}\n${code}\n${props.postCode}`;
|
|
88
|
+
const hasCanvasOutput = checkCanvasOutput(allCode);
|
|
89
|
+
const hasTurtleOutput = checkTurtleOutput(allCode);
|
|
90
|
+
const hasGraphicsOutput = checkGraphicsOutput(allCode);
|
|
91
|
+
if (props.versioned && !config.stateNotInitialized) {
|
|
92
|
+
addVersion({
|
|
93
|
+
code: code,
|
|
94
|
+
createdAt: updatedAt,
|
|
95
|
+
version: state.versions.length + 1
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
code: code,
|
|
100
|
+
hasCanvasOutput: hasCanvasOutput,
|
|
101
|
+
hasTurtleOutput: hasTurtleOutput,
|
|
102
|
+
hasGraphicsOutput: hasGraphicsOutput,
|
|
103
|
+
hasEdits: hasEdits,
|
|
104
|
+
updatedAt: updatedAt
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const setCode = (raw: string, action?: 'insert' | 'remove' | string) => {
|
|
109
|
+
if (state.isPasted && action === 'remove') {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const data = prepareCode(raw);
|
|
113
|
+
setState((state) => ({
|
|
114
|
+
...state,
|
|
115
|
+
...data
|
|
116
|
+
}));
|
|
117
|
+
if (props.id) {
|
|
118
|
+
const toStore: StoredScript = {
|
|
119
|
+
code: data.code,
|
|
120
|
+
createdAt: state.createdAt,
|
|
121
|
+
updatedAt: data.updatedAt,
|
|
122
|
+
versions: state.versions
|
|
123
|
+
};
|
|
124
|
+
if (state.isPasted) {
|
|
125
|
+
addVersion.flush();
|
|
126
|
+
if (toStore.versions.length > 0) {
|
|
127
|
+
toStore.versions[toStore.versions.length - 1].pasted = true;
|
|
128
|
+
}
|
|
129
|
+
set(toStore);
|
|
130
|
+
set.flush();
|
|
131
|
+
state.isPasted = false;
|
|
132
|
+
} else {
|
|
133
|
+
set(toStore);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const execScript = () => {
|
|
139
|
+
setState((s) => ({
|
|
140
|
+
...s,
|
|
141
|
+
isExecuting: true,
|
|
142
|
+
graphicsModalExecutionNr: state.hasGraphicsOutput ? state.graphicsModalExecutionNr + 1 : 0
|
|
143
|
+
}));
|
|
144
|
+
runCode(state.code, state.preCode, state.postCode, codeId, libDir, router);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const load = async () => {
|
|
148
|
+
return loadData(storage);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const _set = async (script: StoredScript) => {
|
|
152
|
+
setState((s) => ({ ...s, status: canSave ? Status.SYNCING : s.status }));
|
|
153
|
+
if (syncStorageScript(script, storage)) {
|
|
154
|
+
setState((s) => ({ ...s, status: canSave ? Status.SUCCESS : s.status }));
|
|
155
|
+
return Status.SUCCESS;
|
|
156
|
+
}
|
|
157
|
+
setState((s) => ({ ...s, status: canSave ? Status.ERROR : s.status }));
|
|
158
|
+
return Status.ERROR;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const setIsPasted = (isPasted: boolean) => {
|
|
162
|
+
setState((s) => ({ ...s, isPasted: isPasted }));
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const set = throttle(_set, syncMaxOnceEvery, {
|
|
166
|
+
leading: false,
|
|
167
|
+
trailing: true
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const _addVersion = (version: Version) => {
|
|
171
|
+
if (!props.versioned || !props.id) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const versions = [...state.versions];
|
|
175
|
+
versions.push(version);
|
|
176
|
+
setState((s) => ({ ...s, versions: versions }));
|
|
177
|
+
};
|
|
178
|
+
const addVersion = throttle(_addVersion, syncMaxOnceEvery, {
|
|
179
|
+
leading: false,
|
|
180
|
+
trailing: true
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const saveNow = async () => {
|
|
184
|
+
addVersion.flush();
|
|
185
|
+
return set.flush();
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const del = async () => {
|
|
189
|
+
storage.del();
|
|
190
|
+
return Status.SUCCESS;
|
|
191
|
+
};
|
|
192
|
+
const codeData = prepareCode(props.code, { stateNotInitialized: true });
|
|
193
|
+
const setExecuting = (isExecuting: boolean) => {
|
|
194
|
+
setState((s) => ({ ...s, isExecuting: isExecuting }));
|
|
195
|
+
};
|
|
196
|
+
const addLogMessage = (log: LogMessage) => {
|
|
197
|
+
setState((s) => ({ ...s, logs: [...s.logs, log] }));
|
|
198
|
+
};
|
|
199
|
+
const clearLogMessages = () => {
|
|
200
|
+
setState((s) => ({ ...s, logs: [] }));
|
|
201
|
+
};
|
|
202
|
+
const closeGraphicsModal = () => {
|
|
203
|
+
setState((s) => ({ ...s, graphicsModalExecutionNr: 0 }));
|
|
204
|
+
};
|
|
205
|
+
const stopScript = () => {
|
|
206
|
+
const code = document.getElementById(DOM_ELEMENT_IDS.communicator(state.codeId));
|
|
207
|
+
if (code) {
|
|
208
|
+
code.removeAttribute('data--start-time');
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
let state: Script = {
|
|
212
|
+
id: id,
|
|
213
|
+
codeId: codeId,
|
|
214
|
+
lang: props.lang,
|
|
215
|
+
showRaw: false,
|
|
216
|
+
pristineCode: codeData.code,
|
|
217
|
+
isExecuting: false,
|
|
218
|
+
logs: [],
|
|
219
|
+
graphicsModalExecutionNr: 0,
|
|
220
|
+
hasEdits: false,
|
|
221
|
+
createdAt: createdAt,
|
|
222
|
+
isLoaded: false,
|
|
223
|
+
status: Status.IDLE,
|
|
224
|
+
versions: [],
|
|
225
|
+
versionsLoaded: false,
|
|
226
|
+
isPasted: false,
|
|
227
|
+
preCode: props.preCode,
|
|
228
|
+
postCode: props.postCode,
|
|
229
|
+
...codeData
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const getState = () => state;
|
|
233
|
+
const listeners = new Set<() => void>();
|
|
234
|
+
const setState = (fn: (state: Script) => Script) => {
|
|
235
|
+
state = fn(state);
|
|
236
|
+
listeners.forEach((l) => l());
|
|
237
|
+
};
|
|
238
|
+
const subscribe = (listener: () => void) => {
|
|
239
|
+
listeners.add(listener);
|
|
240
|
+
return () => listeners.delete(listener);
|
|
241
|
+
};
|
|
242
|
+
const loadVersions = async () => {
|
|
243
|
+
// noop
|
|
244
|
+
state.isLoaded = false;
|
|
245
|
+
load();
|
|
246
|
+
setState((s) => ({ ...s, versionsLoaded: true }));
|
|
247
|
+
return Promise.resolve();
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const setShowRaw = (showRaw: boolean) => {
|
|
251
|
+
setState((s) => ({ ...s, showRaw: showRaw }));
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const setStatus = (status: Status) => {
|
|
255
|
+
setState((s) => ({ ...s, status: status }));
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
getState,
|
|
260
|
+
setState,
|
|
261
|
+
subscribe,
|
|
262
|
+
saveNow,
|
|
263
|
+
addLogMessage,
|
|
264
|
+
clearLogMessages,
|
|
265
|
+
closeGraphicsModal,
|
|
266
|
+
setCode,
|
|
267
|
+
execScript,
|
|
268
|
+
setExecuting,
|
|
269
|
+
stopScript,
|
|
270
|
+
load,
|
|
271
|
+
setIsPasted,
|
|
272
|
+
setShowRaw,
|
|
273
|
+
setStatus,
|
|
274
|
+
loadVersions
|
|
275
|
+
} satisfies Document;
|
|
276
|
+
};
|
|
@@ -1,40 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const CANVAS_OUTPUT_TESTER = /^(SETUP_)?CANVAS_OUTPUT\s*=\s*(True|1)/m
|
|
1
|
+
const TURTLE_IMPORTS_TESTER = /(^from turtle import)|(^import turtle)/m;
|
|
2
|
+
const GRID_IMPORTS_TESTER = /(^from grid import)|(^import grid)/m;
|
|
3
|
+
const GRAPHICS_OUTPUT_TESTER = /^(SETUP_)?GRAPHICS_OUTPUT\s*=\s*(True|1)/m;
|
|
4
|
+
const CANVAS_OUTPUT_TESTER = /^(SETUP_)?CANVAS_OUTPUT\s*=\s*(True|1)/m;
|
|
6
5
|
|
|
7
6
|
export const checkGraphicsOutput = (raw: string): boolean => {
|
|
8
|
-
return
|
|
9
|
-
|
|
7
|
+
return (
|
|
8
|
+
CANVAS_OUTPUT_TESTER.test(raw) ||
|
|
9
|
+
GRAPHICS_OUTPUT_TESTER.test(raw) ||
|
|
10
|
+
TURTLE_IMPORTS_TESTER.test(raw) ||
|
|
11
|
+
GRID_IMPORTS_TESTER.test(raw)
|
|
12
|
+
);
|
|
13
|
+
};
|
|
10
14
|
|
|
11
15
|
export const checkTurtleOutput = (raw: string): boolean => {
|
|
12
16
|
return TURTLE_IMPORTS_TESTER.test(raw);
|
|
13
|
-
}
|
|
17
|
+
};
|
|
14
18
|
|
|
15
19
|
export const checkCanvasOutput = (raw: string): boolean => {
|
|
16
20
|
return CANVAS_OUTPUT_TESTER.test(raw) || GRID_IMPORTS_TESTER.test(raw);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const getPreCode = (rawcode: string) => {
|
|
20
|
-
const match = rawcode.match(/\n###\s*PRE.*?\n/);
|
|
21
|
-
if (match) {
|
|
22
|
-
return {
|
|
23
|
-
pre: rawcode.slice(0, match.index || 0),
|
|
24
|
-
code: rawcode.slice((match.index || 0) + match[0].length)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
pre: '',
|
|
29
|
-
code: rawcode
|
|
30
|
-
};
|
|
31
|
-
}
|
|
21
|
+
};
|
|
32
22
|
|
|
33
23
|
/**
|
|
34
24
|
* The python script is transformed to a string by embedding it with """ characters.
|
|
35
25
|
* So we must prevent the script itself to contain this sequence of characters.
|
|
36
|
-
* @param {String} script
|
|
26
|
+
* @param {String} script
|
|
37
27
|
*/
|
|
38
28
|
export const sanitizePyScript = (script: string) => {
|
|
39
|
-
return script.replace(/"{3}/g, "'''").replace(/\\n/g, '\\\\n').replace(/\\r/g, '\\\\r')
|
|
40
|
-
}
|
|
29
|
+
return script.replace(/"{3}/g, "'''").replace(/\\n/g, '\\\\n').replace(/\\r/g, '\\\\r');
|
|
30
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
const DOM_ELEMENT_IDS = {
|
|
3
2
|
communicator: (codeId: string) => `py_${codeId}`,
|
|
4
3
|
graphicsResult: (codeId: string) => `${codeId}_graphics`,
|
|
@@ -6,16 +5,15 @@ const DOM_ELEMENT_IDS = {
|
|
|
6
5
|
aceEditor: (codeId: string) => `${codeId}_editor`,
|
|
7
6
|
turtleSvgContainer: (codeId: string) => `${codeId}_svg`,
|
|
8
7
|
canvasContainer: (codeId: string) => `${codeId}_canvas`
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const BRYTHON_NOTIFICATION_EVENT = 'bry_notify'
|
|
12
|
-
const CLOSE_TURTLE_MODAL_EVENT = 'close_turtle_modal'
|
|
13
|
-
const TURTLE_IMPORTS_TESTER = /(^from turtle import)|(^import turtle)/m
|
|
14
|
-
const TURTLE3D_IMPORTS_TESTER = /(^from turtle3d import)|(^import turtle3d)/m
|
|
15
|
-
const GRID_IMPORTS_TESTER = /(^from grid import)|(^import grid)/m
|
|
16
|
-
const GRAPHICS_OUTPUT_TESTER = /^(SETUP_)?GRAPHICS_OUTPUT\s*=\s*(True|1)/m
|
|
17
|
-
const CANVAS_OUTPUT_TESTER = /^(SETUP_)?CANVAS_OUTPUT\s*=\s*(True|1)/m
|
|
8
|
+
};
|
|
18
9
|
|
|
10
|
+
const BRYTHON_NOTIFICATION_EVENT = 'bry_notify';
|
|
11
|
+
const CLOSE_TURTLE_MODAL_EVENT = 'close_turtle_modal';
|
|
12
|
+
const TURTLE_IMPORTS_TESTER = /(^from turtle import)|(^import turtle)/m;
|
|
13
|
+
const TURTLE3D_IMPORTS_TESTER = /(^from turtle3d import)|(^import turtle3d)/m;
|
|
14
|
+
const GRID_IMPORTS_TESTER = /(^from grid import)|(^import grid)/m;
|
|
15
|
+
const GRAPHICS_OUTPUT_TESTER = /^(SETUP_)?GRAPHICS_OUTPUT\s*=\s*(True|1)/m;
|
|
16
|
+
const CANVAS_OUTPUT_TESTER = /^(SETUP_)?CANVAS_OUTPUT\s*=\s*(True|1)/m;
|
|
19
17
|
|
|
20
18
|
export {
|
|
21
19
|
DOM_ELEMENT_IDS,
|
|
@@ -26,4 +24,4 @@ export {
|
|
|
26
24
|
CANVAS_OUTPUT_TESTER,
|
|
27
25
|
GRID_IMPORTS_TESTER,
|
|
28
26
|
TURTLE3D_IMPORTS_TESTER
|
|
29
|
-
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Selector, type Script, type Document } from '@theme/CodeEditor/WithScript/Types';
|
|
2
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
3
|
+
|
|
4
|
+
export const useScript = <T extends keyof Script>(store: Document, selector: T): Script[T] => {
|
|
5
|
+
return useSyncExternalStore(
|
|
6
|
+
store.subscribe,
|
|
7
|
+
useCallback(() => store.getState()[selector], [store, selector])
|
|
8
|
+
);
|
|
9
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Context } from '@theme/CodeEditor/WithScript/ScriptContext';
|
|
2
|
+
import { type Document } from '@theme/CodeEditor/WithScript/Types';
|
|
3
|
+
import { ReactContextError } from '@docusaurus/theme-common';
|
|
4
|
+
import { useContext } from 'react';
|
|
5
|
+
|
|
6
|
+
export function useStore(): Document {
|
|
7
|
+
const context = useContext(Context);
|
|
8
|
+
if (context === null) {
|
|
9
|
+
throw new ReactContextError(
|
|
10
|
+
'ScriptContextProvider',
|
|
11
|
+
'The Component must be a child of the ScriptContextProvider component'
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return context;
|
|
15
|
+
}
|