@java-memory-playground/java-memory-playground 0.2.0
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/LICENSE +21 -0
- package/README.md +345 -0
- package/dist/ArrayCreationDialog.d.ts +8 -0
- package/dist/ClassSource.d.ts +15 -0
- package/dist/ConfigView.d.ts +1 -0
- package/dist/ConfirmDialog.d.ts +16 -0
- package/dist/InlineString.d.ts +14 -0
- package/dist/KeyboardShortcuts.d.ts +7 -0
- package/dist/MemoryPlayground.d.ts +74 -0
- package/dist/MemoryView.d.ts +9 -0
- package/dist/MethodCallNode.d.ts +8 -0
- package/dist/ObjectNode.d.ts +7 -0
- package/dist/ReferenceEdge.d.ts +3 -0
- package/dist/Sidebar.d.ts +11 -0
- package/dist/SimpleInputDialog.d.ts +10 -0
- package/dist/StepBar.d.ts +10 -0
- package/dist/VariableNode.d.ts +5 -0
- package/dist/ariaLabels.d.ts +11 -0
- package/dist/canonical.d.ts +19 -0
- package/dist/exportSteps.d.ts +30 -0
- package/dist/fitPadding.d.ts +13 -0
- package/dist/getEdgesAndNodes.d.ts +10 -0
- package/dist/helper.d.ts +19 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +13208 -0
- package/dist/javaSource.d.ts +17 -0
- package/dist/klassImpact.d.ts +28 -0
- package/dist/memory.d.ts +152 -0
- package/dist/palette.d.ts +29 -0
- package/dist/presets.d.ts +10 -0
- package/dist/serde.d.ts +4 -0
- package/dist/stepDiff.d.ts +18 -0
- package/dist/store.d.ts +230 -0
- package/dist/storeContext.d.ts +24 -0
- package/dist/translations.d.ts +119 -0
- package/dist/types.d.ts +23 -0
- package/dist/useDnD.d.ts +23 -0
- package/dist/useUndoRedo.d.ts +13 -0
- package/dist/utils.d.ts +8 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2026 OpenPatch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# @java-memory-playground/java-memory-playground
|
|
2
|
+
|
|
3
|
+
React components behind the [Java Memory Playground](https://jmp.openpatch.org) —
|
|
4
|
+
interactive diagrams of the Java stack and heap.
|
|
5
|
+
|
|
6
|
+
Looking to embed the playground in a page that is not a React app? Use
|
|
7
|
+
[`@java-memory-playground/web-component`](../web-component) instead.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @java-memory-playground/java-memory-playground
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`react` and `react-dom` are peer dependencies.
|
|
16
|
+
|
|
17
|
+
## Two playgrounds
|
|
18
|
+
|
|
19
|
+
`MemoryPlayground` is the student's: the whole diagram, every edit, and the steps
|
|
20
|
+
of a trace to walk through.
|
|
21
|
+
|
|
22
|
+
`MemoryPlaygroundEditor` is the teacher's: all of that, plus configuring classes
|
|
23
|
+
and options and authoring the steps.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import {
|
|
27
|
+
MemoryPlayground, // student
|
|
28
|
+
MemoryPlaygroundEditor, // teacher
|
|
29
|
+
} from "@java-memory-playground/java-memory-playground";
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Both take the same props. The split is about which tools are on screen, not
|
|
33
|
+
about what a student is allowed to touch — a student still builds objects,
|
|
34
|
+
connects references and runs the garbage collector.
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { MemoryPlayground } from "@java-memory-playground/java-memory-playground";
|
|
40
|
+
import "@java-memory-playground/java-memory-playground/index.css";
|
|
41
|
+
|
|
42
|
+
export function Example() {
|
|
43
|
+
return (
|
|
44
|
+
<div style={{ height: "600px" }}>
|
|
45
|
+
<MemoryPlayground
|
|
46
|
+
memory={{
|
|
47
|
+
klasses: { Node: { attributes: { next: "Node" } } },
|
|
48
|
+
objects: {},
|
|
49
|
+
variables: {},
|
|
50
|
+
methodCalls: {
|
|
51
|
+
1: {
|
|
52
|
+
name: "App.main",
|
|
53
|
+
index: 0,
|
|
54
|
+
localVariables: {},
|
|
55
|
+
position: { x: 0, y: 0 },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
}}
|
|
59
|
+
options={{ hideSidebar: true }}
|
|
60
|
+
language="de"
|
|
61
|
+
onChange={(memory) => console.log(memory)}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Props
|
|
69
|
+
|
|
70
|
+
| Prop | Type | Description |
|
|
71
|
+
| ------------- | ---------------------------- | -------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `memory` | `Memory \| string` | The diagram, as an object or a JSON string. Omit it to keep whatever the store already holds. |
|
|
73
|
+
| `options` | `Partial<Memory["options"]>` | Overrides applied on top of `memory.options`. |
|
|
74
|
+
| `language` | `string` | `"en"`, `"de"`, or `"auto"` to follow the browser. Defaults to the browser language. |
|
|
75
|
+
| `persistence` | `boolean` | Mirror the diagram into `location.hash`. Defaults to the value set through `setPersistence`. |
|
|
76
|
+
| `keyBindings` | `Partial<KeyBindings>` | Overrides for the default keyboard shortcuts. |
|
|
77
|
+
| `step` | `number` | The step to show, zero based. Set it to drive the diagram from the page around it. |
|
|
78
|
+
| `onStepChange`| `(step: number) => void` | Called whenever the shown step changes. |
|
|
79
|
+
| `onChange` | `(memory: Memory) => void` | Called when the user presses **Save**. |
|
|
80
|
+
| `onEdit` | `(memory: Memory) => void` | Called on every edit, not only on Save. For a host that owns the file. |
|
|
81
|
+
| `mode` | `"view" \| "edit"` | Which tools to show. Prefer picking the component; this is what it sets. |
|
|
82
|
+
|
|
83
|
+
Every `MemoryPlayground` creates its own store, so several playgrounds can live
|
|
84
|
+
on the same page without sharing state.
|
|
85
|
+
|
|
86
|
+
## State and saving
|
|
87
|
+
|
|
88
|
+
The diagram lives in the playground's store, not in React Flow's local state.
|
|
89
|
+
Every edit — dragging a node, connecting a reference, editing an attribute — is
|
|
90
|
+
in the store immediately, so nothing is lost by switching to the config view and
|
|
91
|
+
back, and when persistence is on the URL keeps up on its own.
|
|
92
|
+
|
|
93
|
+
**Save** is therefore a commit, not a rescue: it is what fires `onChange`, which
|
|
94
|
+
is how a host learns the user considers the diagram finished.
|
|
95
|
+
|
|
96
|
+
A host that owns the file and has a save of its own — the VS Code editor, say —
|
|
97
|
+
wants `onEdit` instead, which fires on every edit. Loading a `memory` prop is
|
|
98
|
+
not an edit, and neither is panning or zooming, so opening a diagram does not
|
|
99
|
+
mark it as changed.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<MemoryPlayground onEdit={(memory) => markDirty(memory)} />
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Steps
|
|
106
|
+
|
|
107
|
+
A diagram is a sequence of steps, which is what lets it show the stack doing the
|
|
108
|
+
thing that makes it a stack: a frame pushed on a call, popped on a return, and an
|
|
109
|
+
object turning into garbage the moment the last reference to it is overwritten.
|
|
110
|
+
|
|
111
|
+
A trace is authored by duplication — build a step, press **Add step**, and change
|
|
112
|
+
what the next line did. Node positions are shared across the whole diagram, so
|
|
113
|
+
dragging something moves it everywhere and the picture does not jump while
|
|
114
|
+
stepping through.
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
// Driving the diagram from the prose around it.
|
|
118
|
+
<MemoryPlayground step={step} onStepChange={setStep} />
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### What a step changed
|
|
122
|
+
|
|
123
|
+
Walking a trace is only useful if you can see what moved, so each step marks
|
|
124
|
+
itself against the one before it: a green outline for what appeared, a dashed
|
|
125
|
+
amber one for what changed, and an amber reference for one that was assigned or
|
|
126
|
+
repointed. The first step of a story marks nothing, because nothing has happened
|
|
127
|
+
yet. Set `hideStepChanges` to turn the marking off.
|
|
128
|
+
|
|
129
|
+
`diffSteps` is exported if you want the same comparison elsewhere.
|
|
130
|
+
|
|
131
|
+
A diagram with one step is just a picture, and is still saved in the shape it
|
|
132
|
+
always had, so a link to a single diagram stays readable by older versions. Set
|
|
133
|
+
`hideSteps` to hide the bar entirely.
|
|
134
|
+
|
|
135
|
+
### Exercises
|
|
136
|
+
|
|
137
|
+
A step can be marked as an exercise. The teacher authors it as the answer; a
|
|
138
|
+
student's playground starts them from the step before it and checks what they
|
|
139
|
+
build.
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{ "label": "insert at the head", "exercise": true, "objects": {}, "variables": {}, "methodCalls": {} }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The check compares the *shape* reachable from each root — the named variables
|
|
146
|
+
and each frame's locals — not the addresses, because a student who allocates an
|
|
147
|
+
object gets whatever address the playground handed out. Building the right
|
|
148
|
+
diagram passes however it was built, and the report names the root that is
|
|
149
|
+
wrong rather than only saying no. `checkAgainst` and `canonicalRoots` are
|
|
150
|
+
exported if you want to run the comparison yourself.
|
|
151
|
+
|
|
152
|
+
Saving from a student's playground writes the exercise back as authored, not
|
|
153
|
+
their attempt, so a shared link stays the exercise.
|
|
154
|
+
|
|
155
|
+
### Garbage collection
|
|
156
|
+
|
|
157
|
+
With `gcPrediction` on, the collector asks first: the student marks the objects
|
|
158
|
+
they think are unreachable, and the check scores them before sweeping. Reaching
|
|
159
|
+
for an answer before seeing it is where the learning is.
|
|
160
|
+
|
|
161
|
+
### The call stack
|
|
162
|
+
|
|
163
|
+
Only the frame on top of the stack can return; the others say so rather than
|
|
164
|
+
hiding the button, because a call having to finish before the one below it
|
|
165
|
+
resumes is the lesson. Returning takes the frame's references with it, which is
|
|
166
|
+
what leaves an object unreachable for the garbage collector to find.
|
|
167
|
+
|
|
168
|
+
## Classes from Java source
|
|
169
|
+
|
|
170
|
+
A teacher usually has the classes already — in a worksheet, in an IDE, on a
|
|
171
|
+
slide — so the config view takes them as Java rather than asking for them field
|
|
172
|
+
by field:
|
|
173
|
+
|
|
174
|
+
```java
|
|
175
|
+
class Node {
|
|
176
|
+
int value;
|
|
177
|
+
Node next;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Only the structure is read: class names, and the name and type of each field.
|
|
182
|
+
Method bodies are skipped whole, initialisers are dropped, and **nothing is
|
|
183
|
+
executed or interpreted** — the source describes the shape of the objects a
|
|
184
|
+
diagram will contain, not a program the playground runs. Comments, generics
|
|
185
|
+
(`List<Node>` is a `List`), qualified names (`java.lang.String` is a `String`),
|
|
186
|
+
arrays either way round (`int[] a` and `int a[]`), records, and several names in
|
|
187
|
+
one declaration are all understood.
|
|
188
|
+
|
|
189
|
+
Half-written source does not throw the classes away: what cannot be read is
|
|
190
|
+
reported above the editor and the last readable classes stay.
|
|
191
|
+
|
|
192
|
+
The **Class list** tab is the same classes as a table, for adding one field
|
|
193
|
+
without touching the source. `parseJavaClasses` and `toJavaSource` are exported.
|
|
194
|
+
|
|
195
|
+
### What applying them costs
|
|
196
|
+
|
|
197
|
+
Classes belong to the whole diagram, so saving them reaches every step — pasting
|
|
198
|
+
a new file over the old one can delete what the objects were holding. Save says
|
|
199
|
+
what that is first, and only when there is something to say:
|
|
200
|
+
|
|
201
|
+
- a field an object no longer has room for, and the value or reference it held
|
|
202
|
+
- objects whose class is gone, which stay in the diagram but can never be made
|
|
203
|
+
again
|
|
204
|
+
|
|
205
|
+
A field nobody has typed into is empty and goes unmentioned, even though an
|
|
206
|
+
`int` shows a `0` and a `boolean` shows a box — those are what the field starts
|
|
207
|
+
out holding, not something to lose. Adding a field, or changing classes no
|
|
208
|
+
object uses, costs nothing and saves without asking: a dialog that always
|
|
209
|
+
appears is one nobody reads.
|
|
210
|
+
|
|
211
|
+
`klassImpact` computes the same report if you want it elsewhere, and
|
|
212
|
+
`defaultValueFor` is what it counts as empty.
|
|
213
|
+
|
|
214
|
+
## Strings
|
|
215
|
+
|
|
216
|
+
A String is a reference type, so a String value lives on the heap like any other
|
|
217
|
+
object. Drawing each one would bury the point of a diagram that is about
|
|
218
|
+
something else, so they are collapsed into their owner by default and rendered
|
|
219
|
+
as an editable field in quotes.
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
// When the String is the lesson rather than the noise.
|
|
223
|
+
<MemoryPlayground options={{ inlineStrings: false }} />
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The collapsing is a display choice: the diagram stores the reference either way,
|
|
227
|
+
and a String value is an `Obj` with `klass === "String"` holding a `literal`.
|
|
228
|
+
Diagrams saved before Strings were modelled this way are converted when read, so
|
|
229
|
+
existing links keep working.
|
|
230
|
+
|
|
231
|
+
## Density
|
|
232
|
+
|
|
233
|
+
A diagram is read as a whole — a list of five nodes, a stack three frames deep
|
|
234
|
+
— so padding inside a node is diagram that has to go somewhere else. The canvas
|
|
235
|
+
is packed tightly on purpose, and how tightly is four custom properties on the
|
|
236
|
+
container rather than a number repeated down the stylesheet:
|
|
237
|
+
|
|
238
|
+
```css
|
|
239
|
+
.java-memory-playground {
|
|
240
|
+
--jmp-space: 4px;
|
|
241
|
+
--jmp-space-lg: 8px;
|
|
242
|
+
--jmp-radius: 6px;
|
|
243
|
+
--jmp-handle: 14px; /* also a drag target, so well above React Flow's 6px */
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Override them to loosen everything at once, for a projector at the back of a
|
|
248
|
+
room or a touch screen.
|
|
249
|
+
|
|
250
|
+
The palette, toolbar and step bar float over the canvas rather than sitting
|
|
251
|
+
beside it, so framing the diagram reserves the space each one occupies —
|
|
252
|
+
otherwise fitting the nodes edge to edge parks the first of them underneath a
|
|
253
|
+
panel. A hidden panel gives its side back.
|
|
254
|
+
|
|
255
|
+
## Undo and redo
|
|
256
|
+
|
|
257
|
+
Undo/redo is backed by [zundo](https://github.com/charkour/zundo). Only the
|
|
258
|
+
diagram is undoable — opening a dialog, selecting a node or switching views does
|
|
259
|
+
not consume a step, and a single drag is one step rather than one per pixel.
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
import { useUndoRedo } from "@java-memory-playground/java-memory-playground";
|
|
263
|
+
|
|
264
|
+
// Inside a MemoryPlayground subtree:
|
|
265
|
+
const { undo, redo, canUndo, canRedo, clear } = useUndoRedo();
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Keyboard shortcuts
|
|
269
|
+
|
|
270
|
+
| Shortcut | Action |
|
|
271
|
+
| -------------- | -------------------------- |
|
|
272
|
+
| `Ctrl/Cmd + S` | Save |
|
|
273
|
+
| `Ctrl/Cmd + Z` | Undo |
|
|
274
|
+
| `Ctrl/Cmd + Y` | Redo |
|
|
275
|
+
| `Ctrl/Cmd + ,` | Toggle the config view (editor only) |
|
|
276
|
+
| `Ctrl/Cmd + +` | Zoom in |
|
|
277
|
+
| `Ctrl/Cmd + -` | Zoom out |
|
|
278
|
+
| `Ctrl/Cmd + 0` | Reset zoom |
|
|
279
|
+
| `Shift + 1` | Fit the diagram to the view |
|
|
280
|
+
|
|
281
|
+
**Download all steps** writes one image with every step under its label, which
|
|
282
|
+
is what a worksheet wants — exporting the step on screen gives you the last
|
|
283
|
+
picture instead. Both exports frame the whole diagram first and crop to it, so
|
|
284
|
+
the empty canvas and the floating panels stay out of the picture.
|
|
285
|
+
|
|
286
|
+
Edges carry their stroke as an inline style rather than taking it from the
|
|
287
|
+
stylesheet. Exporting deep-clones the edge SVG and drops anything a stylesheet
|
|
288
|
+
contributed, and a reference with no stroke is an invisible one.
|
|
289
|
+
|
|
290
|
+
Shortcuts are ignored while an input has focus. Override any of them with
|
|
291
|
+
`keyBindings`:
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
<MemoryPlayground keyBindings={{ save: { key: "e", ctrl: true } }} />
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Presets
|
|
298
|
+
|
|
299
|
+
`optionPresets` names the option combinations a course moves through — a teacher
|
|
300
|
+
picks one in the config view rather than remembering which flags belong to which
|
|
301
|
+
stage.
|
|
302
|
+
|
|
303
|
+
| Preset | What it is for |
|
|
304
|
+
| ------ | -------------- |
|
|
305
|
+
| `references` | Objects and the names that point at them. No stack, no steps. |
|
|
306
|
+
| `stack` | Method calls, so the stack and stepping come with them. |
|
|
307
|
+
| `everything` | Arrays, the garbage collector, and Strings as heap objects. |
|
|
308
|
+
|
|
309
|
+
## Languages
|
|
310
|
+
|
|
311
|
+
English and German ship with the package. `language="auto"` (the default) picks
|
|
312
|
+
the browser language and falls back to English.
|
|
313
|
+
|
|
314
|
+
React Flow's own accessible text is translated too, through its
|
|
315
|
+
`ariaLabelConfig`. It ships those strings in English, so a German playground
|
|
316
|
+
used to announce "Zoom In" and "Press enter or space to select a node" beside
|
|
317
|
+
its own translated labels.
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { translations, getTranslations } from "@java-memory-playground/java-memory-playground";
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## URL persistence
|
|
324
|
+
|
|
325
|
+
The standalone app keeps the whole diagram in `location.hash`, which is what
|
|
326
|
+
makes a diagram shareable as a link. That behaviour is off by default, because
|
|
327
|
+
an embedded playground must not take over the URL of the page hosting it. Turn
|
|
328
|
+
it on once during bootstrap:
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
import { setPersistence } from "@java-memory-playground/java-memory-playground";
|
|
332
|
+
|
|
333
|
+
setPersistence(true);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Or per instance with the `persistence` prop. Writes are throttled and use
|
|
337
|
+
`history.replaceState`, so continuous syncing does not fill up the back button.
|
|
338
|
+
|
|
339
|
+
## Development
|
|
340
|
+
|
|
341
|
+
```sh
|
|
342
|
+
pnpm test # vitest
|
|
343
|
+
pnpm lint # tsc --noEmit
|
|
344
|
+
pnpm build # dist/index.js, dist/index.css and type declarations
|
|
345
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DataType } from "./memory";
|
|
2
|
+
interface ArrayCreationDialogProps {
|
|
3
|
+
onConfirm: (name: string, length: number, elementType: DataType) => void;
|
|
4
|
+
onCancel: () => void;
|
|
5
|
+
availableTypes: DataType[];
|
|
6
|
+
}
|
|
7
|
+
export declare const ArrayCreationDialog: ({ onConfirm, onCancel, availableTypes, }: ArrayCreationDialogProps) => import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Klasses } from "./memory";
|
|
2
|
+
import { Translations } from "./translations";
|
|
3
|
+
/**
|
|
4
|
+
* The classes of a diagram, written as Java.
|
|
5
|
+
*
|
|
6
|
+
* A teacher already has the classes in front of them — in a worksheet, in an
|
|
7
|
+
* IDE, on a slide — so pasting them beats rebuilding them field by field
|
|
8
|
+
* through dialogs. Only the structure is read: nothing here is executed, and
|
|
9
|
+
* method bodies are skipped whole.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ClassSource: ({ klasses, onChange, t, }: {
|
|
12
|
+
klasses: Klasses;
|
|
13
|
+
onChange: (klasses: Klasses) => void;
|
|
14
|
+
t: Translations;
|
|
15
|
+
}) => import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ConfigView: () => import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* A yes/no dialog for a change that cannot be taken back by cancelling.
|
|
4
|
+
*
|
|
5
|
+
* The confirming button says what it does rather than "OK", so the choice can
|
|
6
|
+
* be read without reading the title again.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ConfirmDialog({ title, confirmLabel, cancelLabel, destructive, onConfirm, onCancel, children, }: {
|
|
9
|
+
title: string;
|
|
10
|
+
confirmLabel: string;
|
|
11
|
+
cancelLabel: string;
|
|
12
|
+
destructive?: boolean;
|
|
13
|
+
onConfirm: () => void;
|
|
14
|
+
onCancel: () => void;
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edits the String object referenced from one attribute or local variable,
|
|
3
|
+
* showing it where the reference is rather than as its own box.
|
|
4
|
+
*
|
|
5
|
+
* Reads the store rather than React Flow, because while `inlineStrings` is on
|
|
6
|
+
* the String objects and their edges are deliberately left out of the drawing —
|
|
7
|
+
* they still exist, which is the whole point.
|
|
8
|
+
*/
|
|
9
|
+
export declare function InlineString({ nodeId, handleId, readOnly, }: {
|
|
10
|
+
nodeId: string;
|
|
11
|
+
handleId: string;
|
|
12
|
+
readOnly?: boolean;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
export default InlineString;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { KeyBindings } from "./types";
|
|
2
|
+
interface KeyboardShortcutsProps {
|
|
3
|
+
keyBindings?: Partial<KeyBindings>;
|
|
4
|
+
}
|
|
5
|
+
export declare const defaultKeyBindings: KeyBindings;
|
|
6
|
+
export declare const KeyboardShortcuts: ({ keyBindings: customKeyBindings, }: KeyboardShortcutsProps) => null;
|
|
7
|
+
export default KeyboardShortcuts;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import "@xyflow/react/dist/style.css";
|
|
2
|
+
import "./index.css";
|
|
3
|
+
import { Memory } from "./memory";
|
|
4
|
+
import { PlaygroundMode } from "./store";
|
|
5
|
+
import { KeyBindings } from "./types";
|
|
6
|
+
export interface MemoryPlaygroundProps {
|
|
7
|
+
/**
|
|
8
|
+
* The diagram to show, either as a `Memory` object or as a JSON string.
|
|
9
|
+
* Omit it to keep whatever the store already holds — the standalone app
|
|
10
|
+
* restores that from the URL.
|
|
11
|
+
*/
|
|
12
|
+
memory?: string | Memory;
|
|
13
|
+
/**
|
|
14
|
+
* Overrides for the diagram options, applied on top of the options that come
|
|
15
|
+
* with `memory`. Handy for hiding the sidebar or the garbage collector
|
|
16
|
+
* without rewriting the whole diagram.
|
|
17
|
+
*/
|
|
18
|
+
options?: Partial<Memory["options"]>;
|
|
19
|
+
/**
|
|
20
|
+
* UI language: `"en"`, `"de"`, or `"auto"` to follow the browser. Defaults to
|
|
21
|
+
* the browser language.
|
|
22
|
+
*/
|
|
23
|
+
language?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Mirror the diagram into `location.hash`. Defaults to the value set through
|
|
26
|
+
* `setPersistence`, which is off unless a host opts in.
|
|
27
|
+
*/
|
|
28
|
+
persistence?: boolean;
|
|
29
|
+
/** Overrides for the default keyboard shortcuts. */
|
|
30
|
+
keyBindings?: Partial<KeyBindings>;
|
|
31
|
+
/**
|
|
32
|
+
* The step to show, zero based. Set it to drive the diagram from the page
|
|
33
|
+
* around it — prose can walk a reader through a trace.
|
|
34
|
+
*/
|
|
35
|
+
step?: number;
|
|
36
|
+
/** Called with the step index whenever the shown step changes. */
|
|
37
|
+
onStepChange?: (step: number) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Who this playground is for. `view`, the default, is the student's: the whole
|
|
40
|
+
* diagram and every edit, but no class configuration and no step authoring.
|
|
41
|
+
* `edit` adds those. Prefer the `MemoryPlaygroundEditor` component, which is
|
|
42
|
+
* this with `mode` already set.
|
|
43
|
+
*/
|
|
44
|
+
mode?: PlaygroundMode;
|
|
45
|
+
/**
|
|
46
|
+
* Called with the full memory whenever the user saves. The web component
|
|
47
|
+
* wrapper uses this to dispatch its `change` event.
|
|
48
|
+
*/
|
|
49
|
+
onChange?: (memory: Memory) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Called with the full memory on every edit — each drag, each value typed —
|
|
52
|
+
* rather than only when the user saves.
|
|
53
|
+
*
|
|
54
|
+
* A host that owns the file and has a save of its own, like an editor with a
|
|
55
|
+
* dirty marker, needs to hear about edits as they happen; a host that only
|
|
56
|
+
* wants the finished diagram wants `onChange`. Loading a new `memory` prop is
|
|
57
|
+
* not an edit, and neither is panning or zooming, though the viewport is
|
|
58
|
+
* written along with the next real edit.
|
|
59
|
+
*/
|
|
60
|
+
onEdit?: (memory: Memory) => void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A self-contained Java memory playground.
|
|
64
|
+
*
|
|
65
|
+
* Every instance gets its own store and React Flow provider, so a page can host
|
|
66
|
+
* several playgrounds side by side without them sharing state.
|
|
67
|
+
*/
|
|
68
|
+
export declare function MemoryPlayground({ persistence, mode, ...props }: MemoryPlaygroundProps): import("react").JSX.Element;
|
|
69
|
+
/**
|
|
70
|
+
* The playground with the teacher's tools: everything a student can do, plus
|
|
71
|
+
* configuring classes and options and authoring the steps of a trace.
|
|
72
|
+
*/
|
|
73
|
+
export declare function MemoryPlaygroundEditor(props: Omit<MemoryPlaygroundProps, "mode">): import("react").JSX.Element;
|
|
74
|
+
export default MemoryPlayground;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the help button goes: the documentation page served next to the app.
|
|
3
|
+
*
|
|
4
|
+
* Relative on purpose. Naming a domain here would be guessing at where any
|
|
5
|
+
* given playground is deployed, and a guess that is wrong is a help button
|
|
6
|
+
* that 404s.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DOCUMENTATION_URL = "documentation.html";
|
|
9
|
+
export declare const MemoryView: () => import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Node, NodeProps } from "@xyflow/react";
|
|
2
|
+
import { MethodCall } from "./memory";
|
|
3
|
+
export type MethodCallNodeType = Node<MethodCall, "method-call">;
|
|
4
|
+
export declare function isMethodCallNode(node: Node): node is MethodCallNodeType;
|
|
5
|
+
declare function MethodCallNode({ id, data, onDeclareVariable, }: NodeProps<MethodCallNodeType> & {
|
|
6
|
+
onDeclareVariable?: (nodeId: string) => void;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
export default MethodCallNode;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NodeProps, Node } from "@xyflow/react";
|
|
2
|
+
import { Obj } from "./memory";
|
|
3
|
+
import { CustomNodeType } from "./types";
|
|
4
|
+
export type ObjectNodeType = Node<Obj, "object">;
|
|
5
|
+
export declare function isObjectNode(node: CustomNodeType): node is ObjectNodeType;
|
|
6
|
+
declare function ObjectNode({ id, data }: NodeProps<ObjectNodeType>): import("react").JSX.Element;
|
|
7
|
+
export default ObjectNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Memory } from "./memory";
|
|
2
|
+
interface DragGhostProps {
|
|
3
|
+
type: string | null;
|
|
4
|
+
}
|
|
5
|
+
export declare function DragGhost({ type }: DragGhostProps): import("react").JSX.Element | null;
|
|
6
|
+
export declare const Sidebar: ({ klasses, options, onNodeDrop, }: {
|
|
7
|
+
klasses: Memory["klasses"];
|
|
8
|
+
options: Memory["options"];
|
|
9
|
+
onNodeDrop: (nodeType: string, offsetX: number, offsetY: number) => void;
|
|
10
|
+
}) => import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface SimpleInputDialogProps {
|
|
2
|
+
title: string;
|
|
3
|
+
label: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
initialValue?: string;
|
|
6
|
+
onConfirm: (value: string) => void;
|
|
7
|
+
onCancel: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function SimpleInputDialog({ title, label, placeholder, initialValue, onConfirm, onCancel, }: SimpleInputDialogProps): import("react").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walks through the steps of a diagram.
|
|
3
|
+
*
|
|
4
|
+
* A single-step diagram is just a picture, so the bar only appears once there
|
|
5
|
+
* is something to walk through — or as soon as the author adds a step.
|
|
6
|
+
*/
|
|
7
|
+
export declare function StepBar({ editable }: {
|
|
8
|
+
editable?: boolean;
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
10
|
+
export default StepBar;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Node, NodeProps } from "@xyflow/react";
|
|
2
|
+
import { Variable } from "./memory";
|
|
3
|
+
export type VariableNode = Node<Variable, "variable">;
|
|
4
|
+
declare function VariableNode({ id, data }: NodeProps<VariableNode>): import("react").JSX.Element;
|
|
5
|
+
export default VariableNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReactFlowProps } from "@xyflow/react";
|
|
2
|
+
import { Translations } from "./translations";
|
|
3
|
+
/**
|
|
4
|
+
* React Flow's own accessible text, in the playground's language.
|
|
5
|
+
*
|
|
6
|
+
* It ships these strings in English, so a German playground was announcing
|
|
7
|
+
* "Zoom In" and "Press enter or space to select a node" beside its own
|
|
8
|
+
* translated labels. `ariaLabelConfig` is the documented way to replace them,
|
|
9
|
+
* and every key it does not carry keeps React Flow's default.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ariaLabelsFor: (t: Translations) => NonNullable<ReactFlowProps["ariaLabelConfig"]>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StoreStep } from "./store";
|
|
2
|
+
/**
|
|
3
|
+
* The canonical form of each root, keyed by the root's name.
|
|
4
|
+
*
|
|
5
|
+
* Comparing per root rather than as one string is what lets a check say which
|
|
6
|
+
* variable is wrong instead of only that something is.
|
|
7
|
+
*/
|
|
8
|
+
export declare const canonicalRoots: (step: StoreStep) => Record<string, string>;
|
|
9
|
+
export type ExerciseResult = {
|
|
10
|
+
correct: boolean;
|
|
11
|
+
/** Roots the solution has that the attempt got right. */
|
|
12
|
+
matched: string[];
|
|
13
|
+
/** Roots whose shape differs, or that the attempt never created. */
|
|
14
|
+
wrong: string[];
|
|
15
|
+
/** Roots the attempt invented. */
|
|
16
|
+
extra: string[];
|
|
17
|
+
};
|
|
18
|
+
/** Compares a student's diagram with the one the exercise asks for. */
|
|
19
|
+
export declare const checkAgainst: (solution: StoreStep, attempt: StoreStep) => ExerciseResult;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Viewport } from "@xyflow/react";
|
|
2
|
+
import { CustomNodeType } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* A picture of the diagram alone, cropped to the nodes.
|
|
5
|
+
*
|
|
6
|
+
* The whole flow is photographed rather than just its viewport, because the
|
|
7
|
+
* arrowheads are SVG markers defined outside the viewport and a reference
|
|
8
|
+
* without its arrowhead has lost which way it points. The empty canvas around
|
|
9
|
+
* the diagram is then cropped away.
|
|
10
|
+
*
|
|
11
|
+
* Callers are expected to have framed the diagram first — anything scrolled out
|
|
12
|
+
* of view was never photographed and cannot be cropped back in.
|
|
13
|
+
*/
|
|
14
|
+
export declare const captureDiagram: (flowElement: HTMLElement, nodes: CustomNodeType[], viewport: Viewport) => Promise<string | null>;
|
|
15
|
+
export declare const downloadStep: (flowElement: HTMLElement, nodes: CustomNodeType[], viewport: Viewport, name: string) => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* One image of a whole trace: every step stacked, each under its caption.
|
|
18
|
+
*
|
|
19
|
+
* A worksheet wants the sequence, not the last picture — which is what
|
|
20
|
+
* exporting the step on screen gives you.
|
|
21
|
+
*/
|
|
22
|
+
export declare const downloadAllSteps: ({ stepCount, labelFor, showStep, captureNow, fileName, }: {
|
|
23
|
+
stepCount: number;
|
|
24
|
+
labelFor: (index: number) => string;
|
|
25
|
+
/** Puts a step on screen, framed and measured, ready to be photographed. */
|
|
26
|
+
showStep: (index: number) => Promise<void>;
|
|
27
|
+
/** Photographs the step now on screen. */
|
|
28
|
+
captureNow: () => Promise<string | null>;
|
|
29
|
+
fileName?: string;
|
|
30
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FitViewOptions } from "@xyflow/react";
|
|
2
|
+
import { Memory } from "./memory";
|
|
3
|
+
/**
|
|
4
|
+
* Room to leave around the diagram when framing it.
|
|
5
|
+
*
|
|
6
|
+
* The palette, the toolbar and the step bar are drawn on top of the canvas
|
|
7
|
+
* rather than beside it, so framing the nodes edge to edge parks them
|
|
8
|
+
* underneath a panel — which is how the first frame of the default diagram
|
|
9
|
+
* used to lose its name. Each side is only reserved when something is
|
|
10
|
+
* actually floating there, so a diagram with the palette hidden gets its
|
|
11
|
+
* width back.
|
|
12
|
+
*/
|
|
13
|
+
export declare const fitPaddingFor: (options: Memory["options"]) => NonNullable<FitViewOptions["padding"]>;
|