diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c69de1d93b..edfff29ac9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,12 @@ on: - dev pull_request: workflow_dispatch: + inputs: + windows_opentui_repro: + description: "Run targeted Windows OpenTUI standalone crash repro" + required: false + type: boolean + default: false concurrency: # Keep every run on dev so cancelled checks do not pollute the default branch @@ -22,6 +28,7 @@ env: jobs: unit: + if: ${{ github.event_name != 'workflow_dispatch' || inputs.windows_opentui_repro != true }} name: unit (${{ matrix.settings.name }}) strategy: fail-fast: false @@ -80,6 +87,7 @@ jobs: run: bun run test:httpapi e2e: + if: ${{ github.event_name != 'workflow_dispatch' || inputs.windows_opentui_repro != true }} name: e2e (${{ matrix.settings.name }}) strategy: fail-fast: false @@ -149,3 +157,85 @@ jobs: path: | packages/app/e2e/test-results packages/app/e2e/playwright-report + + windows-opentui-repro: + if: ${{ github.event_name == 'workflow_dispatch' && inputs.windows_opentui_repro == true }} + name: windows opentui repro (${{ matrix.name }}, ${{ matrix.version }}, ${{ matrix.host }}) + strategy: + fail-fast: false + matrix: + include: + - version: "1.17.9" + host: windows-latest + name: baseline + markdownLoops: 1 + onlyMarkdown: false + - version: "1.17.9" + host: windows-latest + name: markdown-stress + markdownLoops: 10 + onlyMarkdown: true + - version: "1.17.10" + host: windows-latest + name: baseline + markdownLoops: 1 + onlyMarkdown: false + - version: "1.17.10" + host: windows-latest + name: markdown-stress + markdownLoops: 10 + onlyMarkdown: true + - version: "1.17.11" + host: windows-latest + name: baseline + markdownLoops: 1 + onlyMarkdown: false + - version: "1.17.11" + host: windows-latest + name: markdown-stress + markdownLoops: 10 + onlyMarkdown: true + - version: "1.17.10" + host: windows-2022 + name: baseline + markdownLoops: 1 + onlyMarkdown: false + runs-on: ${{ matrix.host }} + defaults: + run: + shell: pwsh + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "24.11.1" + + - name: Run Windows standalone repro + timeout-minutes: 30 + run: | + $reproArgs = @{ + Version = "${{ matrix.version }}" + MarkdownLoops = ${{ matrix.markdownLoops }} + } + if ("${{ matrix.onlyMarkdown }}" -eq "true") { + $reproArgs.OnlyMarkdown = $true + } + ./script/repro-windows-opentui-crash.ps1 @reproArgs + + - name: Upload Windows repro artifacts + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: windows-opentui-repro-${{ matrix.name }}-${{ matrix.version }}-${{ matrix.host }}-${{ github.run_attempt }} + if-no-files-found: ignore + retention-days: 7 + path: | + ${{ runner.temp }}/opencode-windows-opentui-${{ matrix.version }}/**/*.log + ${{ runner.temp }}/opencode-windows-opentui-${{ matrix.version }}/**/*.jsonl + ${{ runner.temp }}/*.out.log + ${{ runner.temp }}/*.err.log diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index f832174aa4..47dc19dbdd 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -1528,7 +1528,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las customBorderChars={SplitBorder.customBorderChars} borderColor={theme.error} > - {props.message.error?.data.message} + {String(props.message.error?.data.message ?? "")} diff --git a/packages/tui/src/ui/dialog-prompt.tsx b/packages/tui/src/ui/dialog-prompt.tsx index d627ea2970..63dc2ca6dd 100644 --- a/packages/tui/src/ui/dialog-prompt.tsx +++ b/packages/tui/src/ui/dialog-prompt.tsx @@ -8,7 +8,7 @@ import { useBindings, useCommandShortcut } from "../keymap" export type DialogPromptProps = { title: string - description?: () => JSX.Element + description?: JSX.Element | (() => JSX.Element) placeholder?: string value?: string busy?: boolean @@ -25,6 +25,11 @@ export function DialogPrompt(props: DialogPromptProps) { const [textareaTarget, setTextareaTarget] = createSignal() let textarea: TextareaRenderable + const description = () => { + if (typeof props.description === "function") return props.description() + return props.description + } + function confirm() { if (props.busy) return props.onConfirm?.(textarea.plainText) @@ -83,7 +88,7 @@ export function DialogPrompt(props: DialogPromptProps) { - {props.description} + {description()}