From e5756348c1b23be269ee4033628fdf8d4373b15f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 22:10:51 +0000 Subject: [PATCH 1/2] CI: also build the app for the iOS Simulator Catches changes that break the Xcode project (missing file references, compile errors in the app target), which swift test on VisuStructCore cannot see. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01Cp4EQgFQyGbM8MVLiWbJKQ --- .github/workflows/core-tests.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/core-tests.yml b/.github/workflows/core-tests.yml index b9263bb7..01bc2764 100644 --- a/.github/workflows/core-tests.yml +++ b/.github/workflows/core-tests.yml @@ -1,4 +1,4 @@ -name: VisuStructCore tests +name: CI on: push: @@ -17,3 +17,17 @@ jobs: run: swift --version - name: Build and test run: swift test + + app-build: + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + - name: Xcode version + run: xcodebuild -version + - name: Build app for iOS Simulator + run: | + xcodebuild build \ + -project VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct.xcodeproj \ + -scheme VisuStruct \ + -destination 'generic/platform=iOS Simulator' \ + CODE_SIGNING_ALLOWED=NO From f43104799dd2c2c72d091e6a0af7c6a4166b1f18 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 07:02:08 +0000 Subject: [PATCH 2/2] VoiceOver: read diagram blocks as labelled elements Each block becomes one accessibility element with its kind and text ("Anweisung: int x = 5"), button and selected traits, and an activate action that selects it. The drawn canvas, the empty background and the insert strip are hidden from VoiceOver. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01Cp4EQgFQyGbM8MVLiWbJKQ --- .../VisuStruct/Canvas/DiagramCanvasView.swift | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/Canvas/DiagramCanvasView.swift b/VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/Canvas/DiagramCanvasView.swift index ec37350b..9d1525d8 100644 --- a/VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/Canvas/DiagramCanvasView.swift +++ b/VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/Canvas/DiagramCanvasView.swift @@ -267,6 +267,7 @@ struct DiagramCanvasView: View { Color.clear .frame(width: cw, height: ch) .contentShape(Rectangle()) + .accessibilityHidden(true) .onTapGesture { selectedPath = nil // iPhone: Einfügemarke durch Tap auf leeren Bereich löschen. @@ -291,6 +292,8 @@ struct DiagramCanvasView: View { canvasView(layout: layout, cw: cw, ch: ch) .allowsHitTesting(false) + // Gezeichnete Fläche; VoiceOver liest stattdessen die Blöcke darüber. + .accessibilityHidden(true) if let firstRoot = layout.elements.first { rootInsertBeforeFirstStrip( @@ -298,6 +301,7 @@ struct DiagramCanvasView: View { canvasWidth: cw, canvasHeight: ch ) + .accessibilityHidden(true) } ForEach(flat, id: \.pathKey) { node in @@ -408,6 +412,13 @@ struct DiagramCanvasView: View { .draggable(DiagramDragPayload(raw: ElementPalette.moveDragIdentifier(path: node.idPath))) { nodeDragPreview(for: node) } + // VoiceOver: ein Element pro Block (statt der unbeschrifteten Drop-Streifen darin). + .accessibilityElement(children: .ignore) + .accessibilityLabel(accessibilityLabel(for: node)) + .accessibilityAddTraits(selectedPath == node.idPath ? [.isButton, .isSelected] : .isButton) + .accessibilityAction { + selectedPath = node.idPath + } .accessibilityHint( isPlaceholderNode ? lang.tr("a11y_placeholder_drop") @@ -415,6 +426,16 @@ struct DiagramCanvasView: View { ) } + /// Vorgelesener Name eines Blocks: Elementart und Text, z. B. „Anweisung: int x = 5“. + private func accessibilityLabel(for node: VisuStructLayout.Node) -> String { + // Schlüssel wie in der Palette: element_anweisung, element_verzweigung, … + let kind = lang.tr("element_\(node.element.type)") + let text = node.element.textLines + .joined(separator: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + return text.isEmpty ? kind : "\(kind): \(text)" + } + private func copyElement(at path: [Int]) { guard let el = try? VisuStructDocumentEditing.element(at: path, in: document) else { return } elementClipboard = el