cmux/tests
Ismail Pelaseyed d72b014d6d
feat: add markdown viewer panel with live file watching (#883)
* Add markdown viewer panel with live file watching

Introduce a new PanelType.markdown that renders .md files in a dedicated
panel using MarkdownUI (SwiftUI), with live file watching via DispatchSource
so content auto-updates when the file changes on disk.

- New MarkdownPanel class with file system watcher (write/delete/rename/extend)
- New MarkdownPanelView with custom cmux theme (headings, code blocks, tables,
  blockquotes, inline code, lists, horizontal rules, light/dark mode)
- Full workspace integration: SurfaceKind, creation methods, tab subscription
- Session persistence: snapshot/restore across app restarts
- V2 socket command: markdown.open (validates path, resolves workspace, splits)
- CLI command: cmux markdown open <path> with routing flags and help text
- Agent skill: skills/cmux-markdown/ with SKILL.md, openai.yaml, and references
- Cross-link from skills/cmux/SKILL.md to the new markdown skill
- SPM dependency: gonzalezreal/swift-markdown-ui 2.4.1

* Fix unreachable guard in markdown subcommand dispatch

Use looksLikePath() to distinguish subcommands from path arguments
so the guard can catch unknown subcommands and future subcommands
are parsed correctly.

* Use .isoLatin1 fallback instead of .ascii for encoding recovery

ASCII is a strict subset of UTF-8, so falling back to .ascii after
UTF-8 fails is dead code. Use .isoLatin1 which accepts all 256 byte
values and covers legacy encodings like Windows-1252.

* Mark fileWatchSource as nonisolated(unsafe) for deinit safety

deinit is not guaranteed to run on the main actor, so accessing
@MainActor-isolated storage is a data race under strict concurrency.
DispatchSource.cancel() is thread-safe, so nonisolated(unsafe) is
sufficient with a documented invariant that writes only occur on main.

* Fix file watcher reattach: retry loop with cancellation guard

- Replace one-shot 500ms retry with up to 6 attempts (3s total window)
  so files that reappear after a slow atomic replace are picked up
- Add isClosed flag checked before each retry to prevent restarting
  the watcher after close()/deinit

* Harden path validation in markdown.open command

Reject directories and non-absolute paths before panel creation
to prevent ambiguous behavior and generic downstream failures.

* Always reattach file watcher on delete/rename events

After an atomic save (delete old + create new), the DispatchSource still
points to the old inode. Previously we only reattached when the file was
unreadable, so successful atomic saves left the watcher on a stale inode
and live updates silently stopped. Now we always stop and reattach:
immediately if the new file is readable, via retry loop if not.

* Restore markdown panels even when file is missing at launch

MarkdownPanel already handles unavailable files gracefully (shows
'file unavailable' UI and retries via the reattach loop). Dropping
the panel on restore lost the user's layout for files that may
reappear shortly after (network drives, build artifacts, etc.).

* Harden markdown CLI parsing and startup reconnect behavior

---------

Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
2026-03-04 17:48:28 -08:00
..
cmux.py feat(sidebar): add markdown blocks, provider labels, and fine-grained toggles 2026-02-24 20:38:07 -08:00
test_app_keystrokes.sh Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_blank_screen.py Bump version to 1.25.0 (#33) 2026-02-11 16:24:31 -08:00
test_bonsplit_tab_drag_overlay_gate.py Restrict portal pass-through to active drag-motion events 2026-02-20 20:07:54 -08:00
test_browser_back_forward.py Cmd+[/] browser back/forward, cmd+click open in new tab, right-click open in new tab 2026-02-15 18:35:58 -08:00
test_browser_chrome_contrast_regression.py Fix browser chrome contrast for mixed light/dark themes 2026-02-24 14:36:13 -08:00
test_browser_console_errors_cli_output_regression.py Print browser console/errors in non-JSON mode 2026-02-24 14:57:55 -08:00
test_browser_custom_keybinds.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_browser_devtools_portal_regressions.py Fix browser DevTools retry and portal visibility follow-ups 2026-02-20 18:00:20 -08:00
test_browser_eval_async_wrapper_regression.py Fix browser eval: await promises, always-on console hooks, undefined detection (#613) 2026-02-27 01:42:27 -08:00
test_browser_eval_cli_output_regression.py Fix browser eval: await promises, always-on console hooks, undefined detection (#613) 2026-02-27 01:42:27 -08:00
test_browser_favicon_navigation_regression.py Address review: allow favicon refresh across history URL changes 2026-02-20 20:23:16 -08:00
test_browser_goto_split.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_browser_new_tab_surface_focus_omnibar.py Cmd+P: show workspaces only (#844) 2026-03-04 00:19:01 -08:00
test_browser_omnibar_compact_layout_regression.py Make browser omnibar squircle 2026-02-22 18:36:27 -08:00
test_browser_panel_stability.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_browser_portal_lifecycle_architecture.py Fix browser panel lifecycle after WebContent process termination (#892) 2026-03-04 16:23:22 -08:00
test_ci_create_dmg_pinned.sh Pin create-dmg version in signed build workflows (#401) 2026-02-25 19:30:10 -08:00
test_ci_scheme_testaction_debug.sh Fix Xcode Cloud UI tests by running TestAction in Debug (#672) 2026-02-28 01:48:49 -08:00
test_ci_self_hosted_guard.sh Split CI: GitHub runners for tests, Depot for perf regression (#773) 2026-03-02 21:31:39 -08:00
test_ci_unit_test_spm_retry.sh Harden CI unit tests against SwiftPM artifact flakes (#682) 2026-02-28 00:38:46 -08:00
test_claude_hook_missing_socket_error.py Broaden CLI socket telemetry and add restart listener command 2026-02-24 20:38:05 -08:00
test_claude_hook_session_mapping.py add Claude hook session mapping test 2026-02-15 21:50:06 -08:00
test_cli_sigpipe_ignore.py Fix CLI SIGPIPE exit on broken stdout pipes (#581) 2026-02-26 15:17:50 -08:00
test_cli_socket_sentry_scope.py Broaden CLI socket telemetry and add restart listener command 2026-02-24 20:38:05 -08:00
test_cli_subcommand_help_regressions.py Fix --help flag executing commands instead of showing help (#657) 2026-02-27 17:38:33 -08:00
test_cli_tree_command.py Add cmux tree command for full hierarchy view (#592) 2026-02-26 20:04:09 -08:00
test_cli_version_commit_metadata.py Include commit metadata in cmux --version output 2026-02-24 20:51:43 -08:00
test_cli_version_flag.py Add CLI --version output and regression test 2026-02-22 00:16:01 -08:00
test_close_surface_selection.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_close_workspace_selection.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_cmd_option_t_close_other_tabs_in_pane.py Add Cmd+Option+W to close other pane tabs with confirmation (#475) 2026-02-25 03:54:51 -08:00
test_command_palette_socket_restart_command.py Auto-heal missing CLI listener socket (#679) 2026-02-28 01:19:38 -08:00
test_command_palette_update_commands.py Hide apply-update command until update is available 2026-02-23 04:56:48 -08:00
test_cpu_notifications.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_cpu_usage.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_ctrl_enter_keybind.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_ctrl_interactive.py Rename to cmux and add About panel 2026-01-26 03:05:03 -08:00
test_ctrl_signals.sh Rename to cmux and add About panel 2026-01-26 03:05:03 -08:00
test_ctrl_socket.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_file_drop_paths.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_file_drop_split_targeting.py Add file drop support from Finder into terminal splits 2026-02-17 21:55:31 -08:00
test_focus_notification_dismiss.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_focus_panel_reentrant_guard_regression.py Guard terminal onFocus from re-entrant focus loops 2026-02-24 14:35:10 -08:00
test_homebrew_sha.sh Fix homebrew SHA mismatch race condition (#111) 2026-02-19 17:44:00 -08:00
test_initial_terminal_interactive_and_rendering.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_issue_464_cmdw_close_terminal_browser_split.py Fix Cmd+W terminal close in terminal+browser split 2026-02-24 21:54:25 -08:00
test_issue_494_sleep_wake_git_branch_recovery.py Fix sidebar git branch recovery after sleep/wake (#494) 2026-02-25 15:36:51 -08:00
test_issue_582_sidebar_git_branch_fast_path.py fix: avoid blocking git branch socket updates on main thread 2026-02-26 15:21:27 -08:00
test_issue_734_shell_integration_none_respected.py Respect shell-integration=none in zsh wrapper (#816) 2026-03-03 16:22:38 -08:00
test_lint_swiftui_patterns.py Command palette caret uses white tint (#361) 2026-02-23 04:08:01 -08:00
test_markdown_open_regressions.py feat: add markdown viewer panel with live file watching (#883) 2026-03-04 17:48:28 -08:00
test_microphone_access_metadata.py Add microphone permission metadata for voice transcription 2026-02-25 18:20:10 -08:00
test_multi_workspace_focus.py Release v1.36.0 (#47) 2026-02-17 04:04:29 -08:00
test_nested_split_does_not_disappear.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_nested_split_no_arranged_subview_underflow.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_nested_split_no_detach_during_update.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_nested_split_panel_routing.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_nested_split_preserves_existing_split.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_new_tab_interactive_after_splits.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_new_tab_render_after_splits.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_notifications.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_omnibar_focus_cpu.py Fix omnibar Cmd+L infinite focus loop causing 100% CPU (#97) 2026-02-18 23:18:08 -08:00
test_open_wrapper.py Add external URL bypass rules for embedded browser opens (#768) 2026-03-02 17:50:34 -08:00
test_real_click_overlay_forwarding.py Fix issue #185 overlay recursion hardening and scroll regression (#193) 2026-02-20 14:51:44 -08:00
test_real_click_tabtransfer_pasteboard.py Animate terminal drop overlay and add stale tabtransfer click regression 2026-02-19 15:22:30 -08:00
test_session_restore_unfocused_workspace_multi_window_cycle.py Implement session persistence pass 1 with multi-window restore 2026-02-22 15:39:59 -08:00
test_session_restore_unfocused_workspace_relaunch_cycle.py Implement session persistence pass 1 with multi-window restore 2026-02-22 15:39:59 -08:00
test_shell_histfile_ghostty_zdotdir_regression.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_shell_scrollback_restore_color_replay.py Implement session persistence pass 1 with multi-window restore 2026-02-22 15:39:59 -08:00
test_shell_scrollback_restore_replay_path_regression.py Implement session persistence pass 1 with multi-window restore 2026-02-22 15:39:59 -08:00
test_shell_zdotdir_user_override.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_shell_zdotdir_wrapper.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_sidebar_cwd_git.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_sidebar_indicator_default.py Set default workspace indicator style to left rail (#332) 2026-02-22 18:25:07 -08:00
test_sidebar_meta.py feat(sidebar): add generic metadata rows and commands 2026-02-24 20:19:38 -08:00
test_sidebar_meta_block.py feat(sidebar): add markdown blocks, provider labels, and fine-grained toggles 2026-02-24 20:38:07 -08:00
test_sidebar_ports.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_sidebar_pr.py feat(sidebar): add markdown blocks, provider labels, and fine-grained toggles 2026-02-24 20:38:07 -08:00
test_signals_auto.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_socket_access.py Fix CLI exit code on v1 auth errors 2026-02-22 01:37:42 -08:00
test_split_flash_and_layout.py Fix split blackout race and stabilize focus handoff 2026-02-19 17:10:27 -08:00
test_tab_dragging.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_terminal_drop_overlay_animation_probe.py Harden drag overlay routing and add terminal overlay regression probes 2026-02-20 19:58:58 -08:00
test_terminal_focus_routing.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_terminal_input_render_report.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_terminal_portal_hosting.py Improve terminal hosting depth and workspace mount policy 2026-02-18 19:55:41 -08:00
test_terminal_resize_portal_regressions.py Fix terminal pane render loss during tab drag reorder 2026-02-25 17:54:28 -08:00
test_terminfo_bright_colors.py Release v1.23.0 (#31) 2026-02-09 15:30:43 -08:00
test_update_timing.py Improve update UI error details 2026-01-28 01:49:02 -08:00
test_visual_screenshots.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_visual_typing_char_by_char.py Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
test_workspace_churn_up_arrow_lag.py Add workspace-churn typing lag regression and fix (#767) 2026-03-02 19:06:50 -08:00