Skip to content
This repository was archived by the owner on Nov 7, 2026. It is now read-only.
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is loosely inspired by Keep a Changelog.

### Changed

- **Web 0.14.23, compact nested branches:** a two-way branch keeps both columns the same width only when neither column contains another branch or switch. Before, each nesting level doubled the width, so the drinks machine in lesson 10 step 2 (three nested branches) was about 3000 px wide and unreadable in the lesson and in the editor; now it is about half as wide, and 20 other lesson diagrams (e.g. VisuBot solutions, L17 ticket) get narrower too. Columns of a branch or switch inside a loop or another branch now always fill the box; before, the header could stay wider than its columns, so the if in lesson 14 step 3 looked like a loop (37 lesson diagrams affected). Wide diagrams in the lesson text no longer shrink below 60 % of their size; they scroll sideways instead.
- **Web 0.14.22, text case names in quotation marks:** in lesson 10 the traffic light structure chart now uses the case names `"rot"`, `"gelb"`, `"grün"` (en `"red"` …, pt `"vermelho"` …) and the calculator solution `"+"`, `"-"`, `"*"`, so it is clear they are texts and the generated Java (`case "rot" ->`) is valid. Lesson text, hint and quiz say so (de/en/pt). The simulation still accepts case names without quotes.
- **Web 0.14.21, array output like in Java:** `System.out.println(zahlen)` now prints a reference such as `[I@1e3779b1` in Simulation and Programmieren, as real Java does (`[D@`, `[Z@`, `[Ljava.lang.String;@`, `[[I@`; the same array keeps its id). `Arrays.toString(…)` and `Arrays.deepToString(…)` show the content; `Arrays` needs `import java.util.Arrays;` like javac, and the Java export adds it. The variable table still shows the content. Lesson 15 „Jetzt in Java“ explains reference and `Arrays.toString` (de/en/pt).
- **Web 0.14.18, upper and lower case, exercises „Warum läuft das nicht?“:** lesson 4 gets the new step „Groß- und Kleinschreibung“ (case-sensitive: `zahl`, `Zahl` and `ZAHL` are three names) with a structure chart exercise (`zahl` and `Zahl` both declared, `ZAHL` used), a Kompetenz item and a quiz question; glossary entry case-sensitive. Lessons 4–17 and 19 each get a Java exercise „Warum läuft das nicht?“: the start code doesn't run (misspelled variable such as `Alter`/`Grenze`/`properson`, variable used before it is created or never created, `system.out`, `.Length`, `charat`), the student reads the error message, fixes it and clicks Check (de/en/pt). The Code area now reports wrong case in Java words (`system.out.println`, `System.Out`, `Println`, `math.sqrt`, `text.Length()`, `name.charat(0)`) with the right spelling, blocks Run like javac and offers the 💡 fix; the simulation says „Unbekannte Variable: Zahl. Java unterscheidet Groß- und Kleinschreibung: Meintest du zahl?“ when the name exists in another case (all 21 languages).
Expand Down
4 changes: 2 additions & 2 deletions web/package-lock.json

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

2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "visustruct-web",
"private": true,
"version": "0.14.22",
"version": "0.14.23",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
28 changes: 24 additions & 4 deletions web/src/core/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,11 @@ export class LayoutEngine {

private stretchMultiwayNodeToWidth(node: LayoutNode, newWidth: number): LayoutNode {
const n = copy(node)
const oldW = n.frame.width
if (!(oldW > 0) || n.caseColumnFrames.length === 0) return n
if (n.caseColumnFrames.length === 0) return n
// Alte Breite aus den Spalten, nicht aus `frame.width`: Aufrufer setzen `frame.width` oft schon
// vorher auf die neue Breite. Sonst wäre scale = 1 und die Spalten blieben schmaler als der Kopf.
const oldW = n.caseColumnFrames.reduce((s, c) => s + c.width, 0)
if (!(oldW > 0)) return n
const scale = newWidth / oldW
n.frame.width = newWidth
let xCursor = n.frame.x
Expand Down Expand Up @@ -413,8 +416,15 @@ export class LayoutEngine {
totalW += colW
})

// Zweiarmige Verzweigung: beide Spalten gleich breit.
if (e.type === ElementType.verzweigung && colCount === 2 && columnFrames.length === 2) {
// Zweiarmige Verzweigung: beide Spalten gleich breit. Nicht, wenn eine Spalte selbst eine
// Verzweigung oder Fallauswahl enthält: Sonst verdoppelt sich die Breite mit jeder Ebene
// (verschachtelte else-Ketten werden unlesbar breit).
if (
e.type === ElementType.verzweigung &&
colCount === 2 &&
columnFrames.length === 2 &&
!cases.some((c) => containsMultiway(c.elements))
) {
const target = Math.max(columnFrames[0].width, columnFrames[1].width)
const xLeft = x
const xRight = x + target
Expand Down Expand Up @@ -480,6 +490,16 @@ export class LayoutEngine {
}
}

/** Steckt irgendwo (auch in Schleifen) eine Verzweigung oder Fallauswahl? */
function containsMultiway(elements: VSElement[]): boolean {
return elements.some(
(el) =>
isMultiType(el.type) ||
containsMultiway(el.loopBody ?? []) ||
(el.cases ?? []).some((c) => containsMultiway(c.elements)),
)
}

function shiftDescendantsY(node: LayoutNode, delta: number): LayoutNode {
if (Math.abs(delta) <= 0.0001) return node
const n = copy(node)
Expand Down
6 changes: 5 additions & 1 deletion web/src/ui/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1314,7 +1314,11 @@ export class App {
if (!image) return undefined
// Kleine Diagramme nicht über ihre natürliche Größe hinaus vergrößern.
const width = image.svg.getAttribute('width')
if (width) image.svg.style.maxWidth = `${width}px`
if (width) {
image.svg.style.maxWidth = `${width}px`
// Breite Diagramme nicht bis zur Unlesbarkeit verkleinern: lieber seitlich scrollen.
image.svg.style.minWidth = `${Math.round(Number(width) * 0.6)}px`
}
image.svg.removeAttribute('width')
image.svg.removeAttribute('height')
return image.svg
Expand Down
3 changes: 2 additions & 1 deletion web/src/ui/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2179,7 +2179,8 @@ button.danger-text:not(:disabled) {
width: 100%;
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
overflow-x: auto;
overflow-y: hidden;
background: #fafafa;
}
.learn-preview > svg {
Expand Down
31 changes: 31 additions & 0 deletions web/test/layoutColumns.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { expect, test } from 'vitest'
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { join } from 'node:path'
import { decodeDocument } from '../src/core/xmlCodec'
import { LayoutEngine, type LayoutNode } from '../src/core/layout'

/** Spalten einer Verzweigung/Fallauswahl müssen die ganze Breite des Kastens füllen
* (sonst bleibt rechts Kopffarbe stehen und das if sieht aus wie eine Schleife, Lektion 14). */
test('Spalten füllen die Verzweigung in allen Lektionsdiagrammen', () => {
const gaps: string[] = []
const check = (n: LayoutNode, file: string) => {
if (n.caseColumnFrames.length) {
const last = n.caseColumnFrames[n.caseColumnFrames.length - 1]
const end = last.x + last.width
if (Math.abs(end - (n.frame.x + n.frame.width)) > 1) gaps.push(`${file}: ${n.element.textLines[0]}`)
}
n.children.forEach((c) => check(c, file))
}
const walk = (dir: string) => {
for (const f of readdirSync(dir)) {
const p = join(dir, f)
if (statSync(p).isDirectory()) walk(p)
else if (p.endsWith('.visustruct')) {
const doc = decodeDocument(readFileSync(p, 'utf8'))
new LayoutEngine().layout(doc).elements.forEach((n) => check(n, p))
}
}
}
walk('lessons/diagramme')
expect(gaps).toEqual([])
})

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

2,722 changes: 1,361 additions & 1,361 deletions website/app/assets/index-DwsNaGuz.js → website/app/assets/index-0orjMs_7.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion website/app/assets/index-1DNiS0o0.css

This file was deleted.

1 change: 1 addition & 0 deletions website/app/assets/index-BbUt0G6D.css

Large diffs are not rendered by default.

Loading
Loading