Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5a53eb1
feat: implement event handling for cookie clicker extension
lebalz Oct 28, 2026
dc70c5a
feat: update on_click handler to support generic event sources for ex…
lebalz Oct 28, 2026
995633f
feat: add keyboard event handling for cookie component and update pro…
lebalz Oct 29, 2026
735da5b
feat: enhance event bridge protocol with additional type definitions
lebalz Oct 29, 2026
f335120
feat: add cookie clicker example with event handling and interaction
lebalz Oct 29, 2026
f8114f8
feat: implement on_event handler for multiple event sources and impro…
lebalz Oct 29, 2026
543e012
feat: add color customization to cookie component and update event ha…
lebalz Oct 29, 2026
9a00928
use flex
lebalz Oct 29, 2026
55f0a2f
feat: update cookie component ID from 'cookie-demo' to 'cookie' for c…
lebalz Oct 29, 2026
205feea
feat: rename logging function to improve clarity and print event details
lebalz Oct 29, 2026
d01042b
feat: enhance README with detailed event handling and cookie control …
lebalz Oct 29, 2026
24e581f
feat: add MDX documentation guidelines for Teaching Dev conventions
lebalz Oct 29, 2026
b7e213c
feat: enhance cookie-klicker documentation with additional examples a…
lebalz Oct 29, 2026
645a1a8
feat: add collapseCode property to CodeShowcase for better UI in cook…
lebalz Oct 29, 2026
b664898
feat: add guideline to declare class fields explicitly in TypeScript
lebalz Oct 29, 2026
56774d9
feat: refactor CookieStore constructor for improved readability
lebalz Oct 29, 2026
0340a7d
feat: Refactor Pyodide extension handling and introduce new clock and…
lebalz Oct 29, 2026
b04a222
feat: Enhance Pyodide and cookie-klicker extensions with improved str…
lebalz Oct 29, 2026
73a42f4
feat: Add execution phase handling and UI updates for code execution …
lebalz Oct 29, 2026
3c8941a
feat: Remove Clock component from README and add dedicated clock docu…
lebalz Oct 29, 2026
f53fd5c
feat: Add documentation for webio, interactive clock, and cookie-clic…
lebalz Oct 29, 2026
9acebf4
feat: add snowfall game extension with game loop and event handling
lebalz Oct 29, 2026
bfa2006
feat: enhance snowfall game with dot interaction and improved visuals
lebalz Oct 29, 2026
ee4283f
feat: add collapseCode property to CodeShowcase in snowfall game docu…
lebalz Oct 29, 2026
ebb5a9c
feat: add guidelines for organizing TypeScript imports in SKILL.md
lebalz Oct 29, 2026
a6fc52c
feat: reorganize imports and enhance code structure across multiple f…
lebalz Oct 29, 2026
2eac410
feat: implement handleExtensionMessage for improved error handling in…
lebalz Oct 29, 2026
3a4166b
feat: add connection check in handleKeyDown for improved key event ha…
lebalz Oct 29, 2026
a71134a
feat(grid): add grid extension with Python integration and rendering …
lebalz Oct 29, 2026
b1eaa23
feat: Implement CookieStore and GridStore for managing game state
lebalz Oct 29, 2026
9023b3d
feat: update default scene height to improve game layout
lebalz Nov 1, 2026
4fb6bd0
feat: refactor idle runtime management in PyodideStore for improved e…
lebalz Nov 1, 2026
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
15 changes: 15 additions & 0 deletions .agents/skills/code-style/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ details only when needed.
- Normally, only one component, model or store is defined per file. Multiple components per file are ok when they are small, closely related, not exported and don't have logic of their own (so no hooks, no state, no side effects). Otherwise split them into separate files and import them.
- When a component needs to import dedicated subcomponents, put them in a folder named after the component, with an `index.tsx` and a `styles.module.scss` file.
- When shared styles are needed, import them from the parent folder's `styles.module.scss` file. Avoid global CSS and inline styles.
- Declare class fields explicitly. Do not use TypeScript constructor parameter properties (public, protected, private, or readonly on constructor parameters). Declare each field in the class body and assign it explicitly in the constructor.
- Use `clsx` for conditional class names. Avoid inline styles and global CSS.
- private methods should be located after public methods at the bottom of the class.

Expand Down Expand Up @@ -72,3 +73,17 @@ createOrUpdateUserPermission(documentRootId: string, user: User, access: Access)
## Readability over micro-optimizations

Unless the code is on a hot path where performance matters, favor readability over micro-optimizations and clever one-liners. Iterating over a list several times with well-named steps is fine when the list is small.

## TypeScript imports

After editing `.ts` or `.tsx` files:
- Run TypeScript's Organize Imports operation on every changed file.
- Use the project's TypeScript version, tsconfig, and the import-organizing
preferences and formatting options used by VS Code.
- Prefer an existing repository command that invokes this operation.
Otherwise, use a helper backed by the TypeScript language service's
`organizeImports` API.
- Apply the returned edits; do not approximate the result by hand or
substitute another import sorter.
- Verify that running the organizer again produces no further edits.
- If the operation cannot be run, report that verification is incomplete.
86 changes: 86 additions & 0 deletions .agents/skills/mdx-docs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
name: mdx-docs
description: Write or edit repository Markdown and MDX documentation using Teaching Dev conventions, components, and remark syntax. Read before changing .md or .mdx documentation.
---

# MDX Documentation

Apply these rules to `.md` and `.mdx` documentation, not source code. Keep edits focused on the requested feature or page.

## Language and content

- Write for computer science teachers at upper secondary schools (Gymnasium). Assume programming knowledge, but explain Teaching Dev concepts and setup requirements. Use classroom-relevant examples and focus on what teachers need to create and use teaching materials.
- Write framework documentation in Swiss Standard German: use ss instead of ß and proper umlauts (ä, ö, ü). Comments in embedded code examples may also be German. Preserve identifiers, file paths, commands, and quoted output.
- Lead usage documentation with a minimal working example. Explain prerequisites, surprising behaviour, constraints, and consequences the example does not reveal. Do not narrate each line or repeat API definitions in surrounding prose.
- Use headings for topics, ordered lists for sequences, and unordered lists for parallel items. Keep workflows and narrative explanations in prose. Reserve tables for comparisons across several dimensions.

## Location and identity

- Keep package usage and API references in a root `README.mdx` or a `docs/` directory with `README.mdx` or `index.mdx` and optional subpages. Keep related assets alongside the documentation, following the package's existing layout.
- Edit package sources under `packages/`, not synchronized copies under `tdev-website/docs/packages/`. The synchronization logic is in [updateSync/packageDocsSync](../../../updateSync/packageDocsSync).
- Package docs are the reference. Teacher-facing pages under `tdev-website/docs/` demonstrate classroom use and link to the reference. Add or update a gallery page when relevant to the requested feature; keep overlapping examples consistent without duplicating the full API reference.
- Preserve existing frontmatter, including `page_id`, when editing or moving pages. Preserve persisted component IDs in existing examples.
- Whenever a component needs a new `id` attribute or an MDX page needs a new `page_id`, generate a fresh, random, valid UUIDv4 using a UUID generator, such as `crypto.randomUUID()` or Python's `uuid.uuid4()`. Do not invent UUIDs, copy them from other examples, or reuse an existing ID for a new component or page.

## API definitions

Use definition lists for attributes, props, options, parameters, API functions, and terminology. Include types, defaults, and constraints when useful. Put each term on its own line, followed by one or more `: ` definitions:

```md
`option`
: Description.
: Standard: `false`.
`otherOption`
: Description of the next option.
```

For code blocks, lists, or other block content within a definition, use `:::dd`. See the [definition-list gallery](../../../tdev-website/docs/gallery/markdown/remark-deflist/index.mdx) for examples.

## Examples and instructions

Use `CodeShowcase` when readers benefit from seeing source alongside its rendered or interactive result. Use ordinary fenced blocks for commands, configuration, and standalone snippets. Keep existing runnable code-fence metadata when editing live examples.

Import components explicitly. For Markdown demonstrations, set `lang="md"`. Use `imports` to include the imports readers need in the displayed source:

```mdx
import CodeShowcase from '@tdev-components/CodeShowcase';
import Steps from '@tdev-components/Steps';

<CodeShowcase lang="md" imports={[`import Steps from '@tdev-components/Steps';`]}>
<Steps>

1. Öffne die Einstellungen.
2. Speichere die Änderungen.

</Steps>
</CodeShowcase>
```

Wrap ordered lists describing user actions in `Steps`; use ordinary lists for other enumerations. See the [Steps gallery](../../../tdev-website/docs/gallery/display/steps/index.mdx).

## Repository syntax

Use the relevant gallery page for detailed syntax rather than inventing JSX or copying plugin setup into usage guides:

- [Cards and flex](../../../tdev-website/docs/gallery/markdown/remark-flex-cards/index.mdx): `:::cards` or `:::flex`, with `::br` between items. When nesting directives, use longer outer fences, such as `::::flex` around `:::cards`.
- [Details](../../../tdev-website/docs/gallery/markdown/remark-details/index.mdx): use a container with a summary:

```md
:::details[Weitere Informationen]
Optionaler Inhalt.
:::
```

- [Admonitions](../../../tdev-website/docs/gallery/markdown/admonitions/index.mdx): use notes, tips, and warnings for information that needs attention, not routine prose.
- [Keyboard shortcuts](../../../tdev-website/docs/gallery/markdown/remark-kbd/index.mdx): `[[Ctrl]] + [[C]]`.
- [Inline icons](../../../tdev-website/docs/gallery/markdown/remark-mdi/index.mdx): `:mdi[skateboarding]`.
- [Boxed emphasis](../../../tdev-website/docs/gallery/markdown/remark-strong/index.mdx): `__path/to/file__` highlights a path using the repository's boxed style. Use it when highlighting a file or directory helps; use `**text**` for ordinary bold and backticks for identifiers and commands.
- [Images](../../../tdev-website/docs/gallery/markdown/remark-image/index.mdx): alt text becomes the caption. Consult the gallery for size options, attribution JSON, and inline images.
- Diagrams and formulas: consult [Mermaid](../../../tdev-website/docs/gallery/markdown/mermaid/index.mdx), [Graphviz](../../../tdev-website/docs/gallery/markdown/remark-graphviz/index.mdx), or [math](../../../tdev-website/docs/gallery/markdown/remark-math/index.mdx) when they clarify the content.

## Validation

- Check documented APIs, defaults, and constraints against the implementation. Verify local links and imports, including relative paths after moving a page.
- After structural MDX changes, compile the affected pages with the relevant remark plugins. Preview changes involving layout or interaction.
- Markdown and MDX are excluded from repository-wide Prettier checks; `format:check` does not validate them. Review their formatting by hand.

2 changes: 1 addition & 1 deletion packages/tdev/pyodide-code/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const Header = observer((props: Props) => {
return (
<Container code={code} ignoreSlim>
{!code.meta.slim && <Content code={code} />}
{code.canExecute && <RunCode code={code} onExecute={handleRun} />}
{code.canExecute && <RunCode code={code} onExecute={handleRun} isPreparing={code.isPreparing} />}
{code.isExecuting && <Button icon={mdiClose} title="Ausführung abbrechen" onClick={handleStop} />}
</Container>
);
Expand Down
74 changes: 74 additions & 0 deletions packages/tdev/pyodide-code/docs/02-webio.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
page_id: 3122a420-0743-4281-b7c0-817759055e9e
---

# webio: Ereignisse verarbeiten

Mit `webio` verarbeitet Python Browser-Ereignisse von Erweiterungen. Das Modul gehört zum Pyodide-Package. Der [Cookie-Klicker](./04-cookie-klicker.mdx) zeigt Ereignis-Handler; das [Schneefall-Spiel](./05-snowfall-game.mdx) verbindet Eingaben mit einer regelmässigen Spielschleife.

## Handler registrieren

Die Decorators aus `webio` funktionieren mit allen Erweiterungs-Handles, die eine Ereignisquelle als `events` bereitstellen.

`@on_click(target)`
: Registriert einen Klick-Handler; entspricht `@on_event('click', source=target.events)`.
`@on_key(target, key=None)`
: Registriert einen Tastatur-Handler. Ein Tastenname wie `'ArrowLeft'` filtert die Ereignisse; `None` und `'*'` empfangen alle Tasten. Der Tastenname steht in `event.payload['key']`.
`@on_event(event_type=None, *, source)`
: Registriert einen Handler für eine Quelle oder eine Liste von Quellen. `source` ist erforderlich und muss als benanntes Argument übergeben werden. Ohne Ereignistyp sowie mit `None` oder `'*'` werden alle Ereignistypen empfangen.

## Spielschleife

`@gameloop(fps=30, sources=[scene])`
: Registriert die Aktualisierungsfunktion eines Spiels. Pro Ausführung ist eine Spielschleife erlaubt. Die Registrierung erfolgt vor `await run()`.
`fps`
: Angestrebte Bilder pro Sekunde als endliche Zahl zwischen `1` und `120`. Langsame Aktualisierungen verringern die tatsächlich erreichte Bildrate.
`sources`
: Mindestens eine Ereignisquelle oder ein Erweiterungs-Handle mit `events`. Nur Ereignisse dieser Quellen erscheinen in `ctx.events`.

Die Aktualisierungsfunktion erhält einen Kontext:

`ctx.time`
: Monoton verstrichene Zeit seit Beginn der Spielschleife in Millisekunden.
`ctx.dt`
: Tatsächlich vergangene Zeit seit dem vorherigen Bild in Sekunden. Im ersten Bild wird seit Beginn der Spielschleife gemessen.
`ctx.frame`
: Bildnummer, beginnend bei `1`.
`ctx.events`
: Tupel der seit dem vorherigen Bild empfangenen Ereignisse. Es bildet eine Momentaufnahme der Ereigniswarteschlange.

Registrierte Ereignis-Handler laufen vor der Aktualisierungsfunktion. Dasselbe Ereignis bleibt auch in `ctx.events` enthalten. Verarbeite eine Spielaktion deshalb entweder im Decorator-Handler oder in der Schleife über `ctx.events`.

Verspätete Bilder werden nicht nachgeholt. Eine synchrone oder asynchrone Aktualisierungsfunktion wird vollständig abgeschlossen, bevor das nächste Bild beginnt.

## Empfang starten und beenden

`await run()`
: Startet den Ereignisempfang und eine registrierte Spielschleife. Pro Ausführung ist nur ein Aufruf erlaubt.
`webio.stop()`
: Beendet den Empfang nach dem aktuellen Handler oder der aktuellen Spielaktualisierung. Ein wartender Timer wird abgebrochen. Alternativ beendet **Abbrechen** im Editor die Ausführung sofort.

## Ereignisattribute

Jeder Handler erhält ein Ereignis mit folgenden Attributen:

`type`
: Ereignistyp, etwa `'click'` oder `'key'`.
`extension`
: Konfigurierter Name der Erweiterung, beispielsweise `'cookie_klicker'`.
`target_id`
: Ziel-ID der Ereignisquelle.
`timestamp`
: Zeitpunkt des Ereignisses in Millisekunden seit der Unix-Epoche.
`payload`
: Ereignisspezifische Daten der Erweiterung; beim Cookie `None` für Klicks und `{'key': ...}` für Tastatureingaben.
`x`, `y`
: Zusätzliche Koordinatenattribute, wenn der Payload `x` beziehungsweise `y` enthält. Die Schneefall-Szene liefert damit logische Szenenkoordinaten.

## Ablauf und Grenzen

Eine Ereignisquelle kann gleichzeitig nur von einem laufenden Programm abonniert werden. Ein zweites Programm erhält eine Fehlermeldung. Nach Ende oder Abbruch werden die Abonnements entfernt; alte Ereignisse werden nicht an einen Neustart weitergegeben.

Handler laufen nacheinander. Für absichtliche Wartezeiten kann ein Handler `async def` verwenden und `await asyncio.sleep(0.1)` aufrufen. Währenddessen können weitere Ereignisse empfangen werden; ihre Handler starten nach Abschluss des aktuellen Handlers. `time.sleep()`, synchrone Eingaben mit `input()` und lange Berechnungen blockieren dagegen den Empfang im Worker.

Fehler in Handlern und überfüllte Ereigniswarteschlangen beenden die betroffene Ausführung mit einer Fehlermeldung.
117 changes: 117 additions & 0 deletions packages/tdev/pyodide-code/docs/03-clock.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
page_id: 9a511817-a51f-4195-8ce1-ecfe9ae8b541
---

import CodeShowcase from '@tdev-components/CodeShowcase';
import Steps from '@tdev-components/Steps';
import Clock from '@tdev/pyodide/clock/src/components/Clock';
import CodeBlock from '@theme/CodeBlock';

# Interaktive Uhr

Python steuert die Zeiger einer React-Komponente. Alle Winkel sind in Grad angegeben.

<CodeShowcase lang="md" imports={[`import Clock from '@tdev/pyodide/clock/src/components/Clock';`]}>
<Clock clockId="sbb-uhr" />

```py live_pyo id=e63340b1-3821-4b70-99af-602272a48d8d title=Interaktive_Uhr
from clock import use_clock
from time import sleep
uhr = use_clock("sbb-uhr")

while True:
uhr.set_seconds(uhr.seconds + 1)
if uhr.seconds % 360 == 0:
sleep(0.99)
uhr.set_minutes(uhr.minutes + 6)
if uhr.minutes % 360 == 0:
uhr.set_hours(uhr.hours + 30)
sleep(0.01)
```
</CodeShowcase>

## Auf der eigenen Website einrichten

Voraussetzung ist das [installierte Pyodide-Package](./README.mdx#installation).

<Steps>

1. Kopiere den vollständigen Ordner [clock](https://github.com/GBSL-Informatik/teaching-dev/tree/main/tdev-website/website/pyodide/clock) nach __website/pyodide/clock__. Er enthält Komponenten, Styles, Browser-Store und Python-Modul.
2. Ergänze den Ordner in der bestehenden `extensions`-Liste des [Pyodide-Plugins](./README.mdx#erweiterungen-registrieren):

```ts title="siteConfig.ts"
extensions: [
'./website/pyodide/clock'
]
```

3. Starte den Entwicklungsserver neu. Danach kannst du das MDX-Beispiel oben verwenden.

</Steps>

Die __extension.json__ im kopierten Ordner legt den Namen und die Einstiegspunkte fest. Der Browser-Store wird automatisch initialisiert; der `SiteStore` deiner Website benötigt keine Ergänzungen.

## Aufbau der Erweiterung

Die Uhr verwendet die allgemeine [Erweiterungs-API](./README.mdx#eigene-erweiterungen-weitergeben). Nachrichtentypen, Worker, Browser-Store und React-Komponente liegen gemeinsam im kopierbaren Ordner.

### Gemeinsamer Nachrichtentyp

```ts title="website/pyodide/clock/protocol.ts"
export interface ClockTime {
hours: number;
minutes: number;
seconds: number;
}

export type ClockUpdate = Partial<ClockTime>;
```

Eine Nachricht kann einzelne Zeiger oder die ganze Uhr ändern. Worker und Browser-Handler verwenden `ClockUpdate` als Typparameter.

### Python-Modul im Worker

import clockWorkerSource from '!!raw-loader!@tdev/pyodide/clock/worker.ts';

<CodeBlock language="ts" title="website/pyodide/clock/worker.ts">
{clockWorkerSource}
</CodeBlock>

### Browser-Store und React

import clockClientSource from '!!raw-loader!@tdev/pyodide/clock/client.ts';

<CodeBlock language="ts" title="website/pyodide/clock/client.ts">
{clockClientSource}
</CodeBlock>

Der Handler aktualisiert den [ClockStore](https://github.com/GBSL-Informatik/teaching-dev/blob/main/tdev-website/website/pyodide/clock/src/store/ClockStore.ts) und dessen [Clock-Modelle](https://github.com/GBSL-Informatik/teaching-dev/blob/main/tdev-website/website/pyodide/clock/src/models/Clock.ts). Die [React-Komponente](https://github.com/GBSL-Informatik/teaching-dev/blob/main/tdev-website/website/pyodide/clock/src/components/Clock/index.tsx) greift innerhalb ihrer Funktion auf denselben Store zu:

```tsx title="website/pyodide/clock/src/components/Clock/index.tsx"
import { useExtensionStore } from '@tdev/pyodide-code/hooks/useExtensionStore';
import clockExtension from '../../../client';

const clockStore = useExtensionStore(clockExtension);
const clock = clockStore.useClock(clockId);
```

## Python-Handle

`use_clock(id)`
: Verbindet den Handle mit der `clockId` der React-Komponente. Jeder Aufruf erzeugt einen eigenen Handle mit lokalen Nullwerten.
`hours`, `minutes`, `seconds`
: Lesen die letzten Winkel dieses Handles, nicht den Browser-Store oder andere Handles.
`set_hours(winkel)`, `set_minutes(winkel)`, `set_seconds(winkel)`
: Ändern jeweils nur den zugehörigen Zeiger im Browser.
`set_time(stunden, minuten, sekunden)`
: Setzt alle drei Zeiger. Die Argumente sind Winkel in Grad.
`reset()`
: Setzt alle Zeiger auf null.

## Zustand und Lebensdauer

Mehrere Komponenten mit derselben `clockId` teilen den Zustand im Browser-Store. Dieser bleibt nach Ende oder Abbruch eines Skripts erhalten; ein Neustart setzt ihn nicht automatisch zurück. Ein unbekanntes Ziel wird beim ersten Update im `ClockStore` angelegt, auch wenn noch keine React-Komponente dafür angezeigt wird.

Ein neuer Python-Handle beginnt dagegen wieder bei null und verändert die Anzeige erst mit seinem ersten Update. Die Erweiterung bietet keine Rückfragen vom Browser an Python.

Für Hilfsmodule, die Handles oder Factory-Referenzen speichern, gelten die Hinweise zur [Wiederverwendung von Workern](./README.mdx#zustand-und-lebensdauer).
Loading
Loading