Skip to content
Open
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
41 changes: 41 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@radix-ui/themes": "^3.3.0",
"blockly": "^13.2.1",
"pyodide": "^314.0.6",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"zustand": "^5.0.15"
Expand Down
42 changes: 36 additions & 6 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,36 @@
min-height: 0;
}

.workspace-card,
.code-card {
.code-column {
gap: 20px;
}

.workspace-card {
height: 100%;
}

.workspace-card,
.code-card,
.output-card {
display: flex;
flex-direction: column;
min-height: 0;
}

.workspace-card-content,
.code-card-content {
.code-card-content,
.output-card-content {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
}

.code-card,
.output-card {
flex: 1 1 0;
}

.blockly-workspace {
flex: 1;
min-height: 0;
Expand All @@ -65,7 +79,8 @@
border: 1px solid rgba(15, 23, 42, 0.08);
}

.python-preview {
.python-preview,
.python-output {
margin: 0;
flex: 1;
min-height: 0;
Expand All @@ -78,6 +93,17 @@
line-height: 1.6;
}

.python-output {
background: #111827;
color: #d1fae5;
white-space: pre-wrap;
overflow-wrap: anywhere;
}

.python-output-error {
color: #fecaca;
}

@media (max-width: 1080px) {
.hero-panel,
.workspace-layout {
Expand All @@ -91,10 +117,14 @@

.workspace-column,
.code-column,
.workspace-card,
.code-card {
.workspace-card {
min-height: 420px;
}

.code-card,
.output-card {
min-height: 300px;
}
}

@media (max-width: 720px) {
Expand Down
77 changes: 76 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,28 @@ import {

import './App.css'
import { BlocklyWorkspace } from './components/BlocklyWorkspace'
import { usePythonRunner, type PythonRunStatus } from './hooks/usePythonRunner'
import { useAppStore } from './store/useAppStore'

const statusLabels: Record<PythonRunStatus, string> = {
idle: 'Ready',
loading: 'Loading Python',
running: 'Running',
success: 'Finished',
error: 'Error',
stopped: 'Stopped',
}

function App() {
const generatedCode = useAppStore((state) => state.generatedCode)
const totalBlocks = useAppStore((state) => state.totalBlocks)
const topLevelBlocks = useAppStore((state) => state.topLevelBlocks)
const workspaceVersion = useAppStore((state) => state.workspaceVersion)
const resetWorkspace = useAppStore((state) => state.resetWorkspace)
const { clearOutput, isBusy, output, run, status, stop } = usePythonRunner()

const runButtonLabel =
status === 'loading' ? 'Stop loading' : status === 'running' ? 'Stop execution' : 'Run code'

return (
<Box className="app-shell">
Expand Down Expand Up @@ -87,7 +101,16 @@ function App() {
<section className="code-column">
<Card size="3" className="code-card">
<div className="code-card-content">
<Heading size="5">Generated Python</Heading>
<Flex align="center" justify="between" gap="3" wrap="wrap">
<Heading size="5">Generated Python</Heading>
<Button
type="button"
color={isBusy ? 'red' : 'grass'}
onClick={() => (isBusy ? stop() : run(generatedCode))}
>
{runButtonLabel}
</Button>
</Flex>
<Text as="p" size="2" color="gray" mb="3">
The code preview updates from the Zustand-backed workspace state as you
change the blocks.
Expand All @@ -97,6 +120,58 @@ function App() {
</pre>
</div>
</Card>

<Card size="3" className="output-card">
<div className="output-card-content">
<Flex align="center" justify="between" gap="3" wrap="wrap">
<Flex align="center" gap="2">
<Heading size="5">Output</Heading>
<Badge
size="2"
color={
status === 'error'
? 'red'
: status === 'success'
? 'grass'
: status === 'stopped'
? 'amber'
: 'gray'
}
variant="soft"
>
{statusLabels[status]}
</Badge>
</Flex>
<Button
type="button"
size="1"
variant="ghost"
disabled={!output || isBusy}
onClick={clearOutput}
>
Clear
</Button>
</Flex>
<Text as="p" size="2" color="gray" mb="3">
Standard output, return values, and Python errors appear here.
</Text>
<pre
className={`python-output${status === 'error' ? ' python-output-error' : ''}`}
aria-busy={isBusy}
aria-live="polite"
role="log"
>
{output ||
(status === 'loading'
? 'Preparing the Python runtime…'
: status === 'running'
? 'Running Python…'
: status === 'success'
? 'Code finished without producing output.'
: 'Run the generated Python to see its output.')}
</pre>
</div>
</Card>
</section>
</main>
</Box>
Expand Down
7 changes: 3 additions & 4 deletions src/components/blockly/blocks/generators/example.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as Blockly from "blockly";
import { pythonGenerator } from "blockly/python";
import { pythonGenerator, Order } from "blockly/python";

pythonGenerator.forBlock["example_block"] = function (block) {
pythonGenerator.forBlock["example_block"] = function () {
const code = "example_code";
return [code, pythonGenerator.ORDER_ATOMIC];
return [code, Order.ATOMIC];
};
Loading