Skip to content
Closed
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
63 changes: 56 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ifeq ($(UNAME_S),Linux)
endif
CFLAGS ?= -std=c99 $(POSIX_CFLAGS) -Wall -Wextra -Wpedantic -O2 -pthread
TESTCFLAGS ?= -std=c99 $(POSIX_CFLAGS) -Wall -Wextra -Wpedantic -O0 -g -pthread
SAN ?= address
CONPTY_WIN_CC ?= x86_64-w64-mingw32-gcc
CONPTY_WIN_CFLAGS ?= -std=c99 -Wall -Wextra -Wpedantic -Werror -D_WIN32_WINNT=0x0A00

Expand All @@ -40,7 +41,7 @@ LIB_SECTIONS := $(wildcard $(SRCDIR)/timui_*.c) $(SRCDIR)/timui_int.h \
# link flags, so it is built by a dedicated rule below — keep it out of the
# generic single-file example pattern.
EXAMPLES := $(filter-out $(BLDDIR)/radio,$(patsubst $(EXADIR)/%.c,$(BLDDIR)/%,$(wildcard $(EXADIR)/*.c)))
TEST_SRCS := $(SRCDIR)/timui.c $(TSTDIR)/test_main.c $(TSTDIR)/test_rect.c $(TSTDIR)/test_result.c $(TSTDIR)/test_arena.c $(TSTDIR)/test_strings.c $(TSTDIR)/test_id_stack.c $(TSTDIR)/test_msgq.c $(TSTDIR)/test_mpsc.c $(TSTDIR)/test_transport.c $(TSTDIR)/test_screen.c $(TSTDIR)/test_input.c $(TSTDIR)/test_mouse.c $(TSTDIR)/test_termios.c $(TSTDIR)/test_size.c $(TSTDIR)/test_caps.c $(TSTDIR)/test_kitty.c $(TSTDIR)/test_sync.c $(TSTDIR)/test_cells.c $(TSTDIR)/test_utf8.c $(TSTDIR)/test_draw.c $(TSTDIR)/test_render.c $(TSTDIR)/test_cursor.c $(TSTDIR)/test_frame.c $(TSTDIR)/test_interact.c $(TSTDIR)/test_theme.c $(TSTDIR)/test_stylesheet.c $(TSTDIR)/test_button.c $(TSTDIR)/test_widgets.c $(TSTDIR)/test_input_widget.c $(TSTDIR)/test_listbox.c $(TSTDIR)/test_grid_widget.c $(TSTDIR)/test_dialog.c $(TSTDIR)/test_fuzz.c $(TSTDIR)/test_clip.c $(TSTDIR)/test_menus.c $(TSTDIR)/test_modal.c $(TSTDIR)/test_hyperlink.c $(TSTDIR)/test_esc_timeout.c $(TSTDIR)/test_scroll.c $(TSTDIR)/test_v02_batch.c $(TSTDIR)/test_v02_widgets.c $(TSTDIR)/test_v02_more.c $(TSTDIR)/test_images_pty.c $(TSTDIR)/test_review_critical.c $(TSTDIR)/test_snapshot.c $(TSTDIR)/test_coverage_z7.c $(TSTDIR)/test_render_stream.c
TEST_SRCS := $(SRCDIR)/timui.c $(TSTDIR)/test_main.c $(TSTDIR)/test_rect.c $(TSTDIR)/test_result.c $(TSTDIR)/test_arena.c $(TSTDIR)/test_strings.c $(TSTDIR)/test_id_stack.c $(TSTDIR)/test_msgq.c $(TSTDIR)/test_mpsc.c $(TSTDIR)/test_transport.c $(TSTDIR)/test_screen.c $(TSTDIR)/test_input.c $(TSTDIR)/test_mouse.c $(TSTDIR)/test_termios.c $(TSTDIR)/test_size.c $(TSTDIR)/test_caps.c $(TSTDIR)/test_kitty.c $(TSTDIR)/test_sync.c $(TSTDIR)/test_cells.c $(TSTDIR)/test_utf8.c $(TSTDIR)/test_draw.c $(TSTDIR)/test_render.c $(TSTDIR)/test_cursor.c $(TSTDIR)/test_frame.c $(TSTDIR)/test_interact.c $(TSTDIR)/test_theme.c $(TSTDIR)/test_stylesheet.c $(TSTDIR)/test_button.c $(TSTDIR)/test_widgets.c $(TSTDIR)/test_input_widget.c $(TSTDIR)/test_listbox.c $(TSTDIR)/test_grid_widget.c $(TSTDIR)/test_dialog.c $(TSTDIR)/test_fuzz.c $(TSTDIR)/test_clip.c $(TSTDIR)/test_menus.c $(TSTDIR)/test_modal.c $(TSTDIR)/test_hyperlink.c $(TSTDIR)/test_esc_timeout.c $(TSTDIR)/test_scroll.c $(TSTDIR)/test_v02_batch.c $(TSTDIR)/test_v02_widgets.c $(TSTDIR)/test_v02_more.c $(TSTDIR)/test_images_pty.c $(TSTDIR)/test_review_critical.c $(TSTDIR)/test_snapshot.c $(TSTDIR)/test_coverage_z7.c $(TSTDIR)/test_render_stream.c $(TSTDIR)/test_async_scan.c
TEST_BIN := $(BLDDIR)/test_unit
GOLDEN_BIN := $(BLDDIR)/gen_golden

Expand Down Expand Up @@ -134,7 +135,7 @@ endif
# 2. BUILD RULES — help/build · example pattern rule · test & tool binaries · subsystem objects
# ============================================================================

.PHONY: help build test test-san run www check-www check-www-assets amalgamate release-check fmt check clean goldens vt-test check-no-images check-conpty check-conpty-posix check-conpty-win32-compile check-conpty-win32-smoke-compile check-chat-highlight check-chat-text man install-man check-chat-text-sheenbidi check-radio smoke-radio run-radio check-sqlite-tui run-sqlite-tui smoke-sqlite-tui check-grid check-layout check-tabs check-chart check-syntax run-gallery smoke-gallery check-image-smoke smoke-image-live smoke-image-live-auto smoke-image-live-kitty smoke-image-live-sixel smoke-image-live-iterm2 smoke-image-live-none smoke-conpty-win32 check-irc run-irc smoke-irc
.PHONY: help build test test-san run www check-www check-www-assets check-phase1-5-docs check-hosted-visual-windows check-conpty-evidence-artifacts verify-conpty-evidence amalgamate release-check fmt check clean goldens vt-test check-no-images check-conpty check-conpty-posix check-conpty-smoke-tool check-conpty-source-order check-conpty-win32-compile check-conpty-win32-smoke-compile check-chat-highlight check-chat-text man install-man check-chat-text-sheenbidi check-radio smoke-radio run-radio check-sqlite-tui run-sqlite-tui smoke-sqlite-tui check-grid check-layout check-tabs check-chart check-syntax run-gallery smoke-gallery check-image-smoke smoke-image-live smoke-image-live-auto smoke-image-live-kitty smoke-image-live-sixel smoke-image-live-iterm2 smoke-image-live-none smoke-conpty-win32 check-irc run-irc smoke-irc
.PHONY: accept check-vt-gif check-vt-gif-glyphs check-vt-gif-cjk check-vt-gif-emoji check-vt-gif-output check-vt-gif-golden gen-golden-vtgif check-vt-gif-style check-vt-gif-all
.PHONY: run-chat-demo rec-chat-demo gif-chat-demo webp-chat-demo gen-font-ttf gen-emoji gen-cjk

Expand Down Expand Up @@ -292,7 +293,7 @@ rec-chat-demo: $(BLDDIR)/chat ## Screen-record hint, then autoplay the chat demo
# 5. CHECK — unit tests · goldens · acceptance · per-subsystem standalone checks
# ============================================================================

check: build test check-no-images check-conpty-win32-compile check-conpty-win32-smoke-compile check-www ## Build + test gate
check: build test check-no-images check-conpty-smoke-tool check-conpty-source-order check-conpty-win32-compile check-conpty-win32-smoke-compile check-hosted-visual-windows check-conpty-evidence-artifacts check-www check-phase1-5-docs ## Build + test gate
@printf "$(C_GREEN)✓ check passed$(C_RESET)\n"

test: $(TEST_BIN) ## Compile and run the unit tests
Expand All @@ -309,6 +310,43 @@ vt-test: build ## Compile + run unit tests WITH vterm round-trip tests (needs li
@printf "$(C_YELL)▶ running vt-tests$(C_RESET)\n"
@./$(VT_BIN)

check-phase1-5-docs: ## Verify Phase 1.5 evidence docs agree
@awk '{$$1=$$1; printf "%s ", $$0}' docs/runbooks/phase1-5-live-evidence.md | grep -Fq -- 'Run `28986249841` is the accepted hosted macOS iTerm2 baseline'
@grep -Fq -- 'Hosted macOS iTerm2 now has accepted timui OSC 1337 visual evidence.' docs/handoff/2026-07-09-hosted-visual-probe.md
@awk '{$$1=$$1; printf "%s ", $$0}' docs/backlog.md | grep -Fq -- '- [x] iTerm2 terminal evidence via hosted macOS iTerm2 run `28986249841`.'
@! grep -Fq -- '- [ ] iTerm2 terminal evidence.' docs/backlog.md
@awk '{$$1=$$1; printf "%s ", $$0}' docs/gaps.md | grep -Fq -- 'Image protocol live evidence is accepted for Windows Terminal Sixel and hosted macOS iTerm2.'
@grep -Fq -- 'DONE: iTerm2 terminal evidence via hosted macOS iTerm2 run `28986249841`.' docs/goals/phase1_5-platform-widgets-style-text-image.goal.txt
@grep -Fq -- 'DONE: hosted Windows ConPTY smoke evidence via run `29226547099`' docs/goals/phase1_5-platform-widgets-style-text-image.goal.txt
@awk '{$$1=$$1; printf "%s ", $$0}' docs/runbooks/phase1-5-live-evidence.md | grep -Fq -- 'Run `29226547099` is the accepted hosted Windows ConPTY smoke baseline'
@awk '{$$1=$$1; printf "%s ", $$0}' docs/backlog.md | grep -Fq -- '- [x] Hosted Windows ConPTY smoke evidence via run `29226547099`.'
@awk '{$$1=$$1; printf "%s ", $$0}' docs/gaps.md | grep -Fq -- 'Windows ConPTY live evidence is accepted in hosted run `29226547099`'
@awk '{$$1=$$1; printf "%s ", $$0}' docs/TERMINAL_PROTOCOLS.md | grep -Fq -- 'Hosted Windows Terminal Sixel evidence is accepted in run `28982641529`; hosted macOS iTerm2 evidence is accepted in run `28986249841`.'
@awk '{$$1=$$1; printf "%s ", $$0}' docs/TERMINAL_PROTOCOLS.md | grep -Fq -- 'Hosted Windows ConPTY smoke evidence is accepted in run `29226547099`'
@grep -Fq -- 'make verify-conpty-evidence ARTIFACT_DIR=' docs/runbooks/phase1-5-live-evidence.md
@printf "$(C_GREEN)✓ Phase 1.5 evidence docs$(C_RESET)\n"

check-hosted-visual-windows: tools/ci/hosted_visual_windows.ps1 ## Verify hosted Windows probe emits ConPTY acceptance artifacts
@grep -Fq -- 'conpty-acceptance.json' tools/ci/hosted_visual_windows.ps1
@grep -Fq -- 'conpty-smoke.command.txt' tools/ci/hosted_visual_windows.ps1
@grep -Fq -- 'conpty-smoke.meta.txt' tools/ci/hosted_visual_windows.ps1
@grep -Fq -- 'passTokenPresent' tools/ci/hosted_visual_windows.ps1
@grep -Fq -- 'accepted' tools/ci/hosted_visual_windows.ps1
@grep -Fq -- 'PASS conpty smoke: observed TIMUI_CONPTY_SMOKE' tools/ci/hosted_visual_windows.ps1
@grep -Fq -- 'ConvertTo-Json' tools/ci/hosted_visual_windows.ps1
@grep -Fq -- 'return (Invoke-Captured $$Name' tools/ci/hosted_visual_windows.ps1
@printf "$(C_GREEN)✓ hosted Windows ConPTY evidence manifest$(C_RESET)\n"

check-conpty-evidence-artifacts: tests/test_conpty_evidence.py tools/verify_conpty_evidence.py ## Verify hosted ConPTY artifact acceptance predicates
@TIMUI_TEST_COMMIT=$$(git rev-parse HEAD) python3 tests/test_conpty_evidence.py
@printf "$(C_GREEN)✓ hosted ConPTY evidence artifact verifier$(C_RESET)\n"

verify-conpty-evidence: tools/verify_conpty_evidence.py ## Validate downloaded hosted ConPTY evidence (ARTIFACT_DIR=... [COMMIT=...])
@[ -n "$(ARTIFACT_DIR)" ] || { printf "$(C_YELL)ARTIFACT_DIR is required$(C_RESET)\n"; exit 2; }
@commit="$(COMMIT)"; \
if [ -z "$$commit" ]; then commit=$$(git rev-parse HEAD); fi; \
python3 tools/verify_conpty_evidence.py --artifact-dir "$(ARTIFACT_DIR)" --commit "$$commit"

check-no-images: $(TSTDIR)/test_no_images.c $(HEADER) $(LIB_SECTIONS) ## Test TIMUI_NO_IMAGES keeps API but disables terminal image escapes
@mkdir -p $(BLDDIR)
@printf "$(C_CYAN)build$(C_RESET) no-images test\n"
Expand All @@ -321,6 +359,16 @@ check-conpty-posix: $(TEST_BIN) ## Run POSIX ConPTY fallback/helper coverage
@printf "$(C_YELL)▶ running ConPTY POSIX helper tests$(C_RESET)\n"
@./$(TEST_BIN)

check-conpty-smoke-tool: $(TSTDIR)/test_conpty_smoke_tool.c $(TOOLDIR)/conpty_smoke_win32.c $(HEADER) $(LIB_SECTIONS) ## Test portable ConPTY smoke-tool helpers
@mkdir -p $(BLDDIR)
@printf "$(C_CYAN)build$(C_RESET) ConPTY smoke helper tests\n"
@$(CC) $(CFLAGS) -I$(INCDIR) $(TSTDIR)/test_conpty_smoke_tool.c -o $(BLDDIR)/test_conpty_smoke_tool
@./$(BLDDIR)/test_conpty_smoke_tool

check-conpty-source-order: $(TSTDIR)/test_conpty_source.py $(SRCDIR)/timui_conpty.c ## Verify ConPTY handle lifetime ordering
@python3 $(TSTDIR)/test_conpty_source.py
@printf "$(C_GREEN)✓ ConPTY source ordering$(C_RESET)\n"

check-conpty-win32-compile: ## Cross-compile the isolated Win32 ConPTY backend when mingw is available
@mkdir -p $(BLDDIR)
@if ! command -v $(CONPTY_WIN_CC) >/dev/null 2>&1; then \
Expand All @@ -341,7 +389,7 @@ check-conpty-win32-smoke-compile: ## Cross-compile the operator Win32 ConPTY smo
@$(CONPTY_WIN_CC) $(CONPTY_WIN_CFLAGS) -I$(INCDIR) $(TOOLDIR)/conpty_smoke_win32.c -o $(BLDDIR)/conpty_smoke_win32.exe
@printf "$(C_GREEN)✓ Win32 ConPTY operator smoke runner compiles$(C_RESET)\n"

check-conpty: check-conpty-posix check-conpty-win32-compile ## Run ConPTY POSIX helper + optional Win32 compile checks
check-conpty: check-conpty-posix check-conpty-smoke-tool check-conpty-source-order check-conpty-win32-compile ## Run ConPTY helper + optional Win32 compile checks

goldens: $(GOLDEN_BIN) ## Regenerate tests/golden/*.txt snapshots
@mkdir -p tests/golden
Expand Down Expand Up @@ -674,11 +722,11 @@ smoke-image-live-iterm2: smoke-image-live ## Live image smoke forcing iTerm2 inl
smoke-image-live-none: PROTO=none
smoke-image-live-none: smoke-image-live ## Live image smoke forcing text placeholders

smoke-conpty-win32: check-conpty-win32-smoke-compile ## Run the ConPTY smoke runner on Windows Terminal only
smoke-conpty-win32: check-conpty-win32-smoke-compile ## Run the ConPTY smoke runner on Windows only
@if [ "$${OS:-}" = "Windows_NT" ]; then \
./$(BLDDIR)/conpty_smoke_win32.exe; \
else \
printf "$(C_YELL)SKIP$(C_RESET) smoke-conpty-win32 must run inside Windows Terminal on Windows\n"; \
printf "$(C_YELL)SKIP$(C_RESET) smoke-conpty-win32 must run on Windows\n"; \
fi

# ============================================================================
Expand Down Expand Up @@ -744,13 +792,14 @@ www: amalgamate ## Refresh static website assets under www/
@$(MAKE) check-www-assets
@printf "$(C_GREEN)✓ refreshed $(WWW_HEADER) and $(WWW_LICENSE)$(C_RESET)\n"

check-www: check-www-assets ## Verify static website license, agent links, and assets
check-www: amalgamate check-www-assets ## Verify static website license, agent links, header freshness, and assets
@grep -q '<h2>LICENSE</h2>' $(WWWDIR)/index.html
@grep -q 'href="LICENSE"' $(WWWDIR)/index.html
@grep -q 'Apache-2.0' $(WWWDIR)/index.html
@grep -q '^## License$$' $(WWWDIR)/llms.txt
@grep -q 'https://timui.dev/LICENSE' $(WWWDIR)/llms.txt
@grep -q 'SPDX-License-Identifier: Apache-2.0' $(WWW_HEADER)
@cmp -s $(RELDIR)/timui.h $(WWW_HEADER) || { printf "$(C_YELL)✗ www$(C_RESET) timui.h is stale; run make www\n"; exit 1; }
@cmp -s LICENSE $(WWW_LICENSE)
@printf "$(C_GREEN)✓ website license links$(C_RESET)\n"

Expand Down
4 changes: 2 additions & 2 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ calling `ResizePseudoConsole`.

On non-Windows builds, ConPTY APIs return `TIMUI_ERR_UNSUPPORTED` after
clearing output handles. The Win32 path is enforced by the MinGW compile seam in
`make check`; live Windows Terminal smoke evidence is still required before
documenting Windows as supported.
`make check`; hosted Windows ConPTY smoke evidence is accepted in run
`29226547099` at commit `44bb495b1f7937357f117b42563b50ba08c08e08`.

## Layout

Expand Down
13 changes: 7 additions & 6 deletions docs/TERMINAL_PROTOCOLS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,17 @@ set for tests or user overrides. With `TIMUI_NO_IMAGES`, image caps are stripped
after detection and force masks; protocol selection always returns
`TIMUI_IMAGE_PROTOCOL_NONE`.

## Planned
## Platform Evidence

- **Windows ConPTY**: implemented as a `_WIN32` transport/lifecycle backend
with runtime symbol probing and MinGW compile coverage in `make check`. It
belongs below the protocol layer, not as a graphics abstraction. Live Windows
Terminal smoke evidence is still pending before claiming supported Windows
operation.
belongs below the protocol layer, not as a graphics abstraction. Hosted
Windows ConPTY smoke evidence is accepted in run `29226547099` at commit
`44bb495b1f7937357f117b42563b50ba08c08e08`.
- **Sixel parity**: the RGBA emitter is implemented with exact palettes,
bounded 16-colour quantization, clipping, and scaling when terminal
cell-pixel geometry is known. Plain PNG images are decoded lazily through the
bounded PNG-only `stb_image` path for Sixel, while `timui_image_from_png_rgba`
lets applications provide exact decoded pixels directly. Deferred work:
real-terminal evidence.
lets applications provide exact decoded pixels directly. Hosted Windows
Terminal Sixel evidence is accepted in run `28982641529`; hosted macOS iTerm2
evidence is accepted in run `28986249841`.
16 changes: 10 additions & 6 deletions docs/backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ I/O via `timui_post`, offline `--demo`/`--replay`, `make check-irc`, and
This is the next planned sweep. It is library/platform work, not a large app
push. Detailed pickup prompt: `docs/goals/phase1_5-platform-widgets-style-text-image.goal.txt`.

- [ ] **Windows ConPTY backend** - replace the unsupported stub with a real
- [x] **Windows ConPTY backend** - replace the unsupported stub with a real
`CreatePseudoConsole` transport: pipes, process lifetime, resize, close,
runtime symbol probing, short-write/read handling, and `DWORD` chunking.
Do not promise graphics as part of ConPTY itself; graphics are selected by
terminal image protocol capability above the transport.
- [x] `_WIN32` backend implementation, resize API, POSIX fallback tests,
and MinGW compile seam in `make check`.
- [ ] Real Windows Terminal smoke run and captured operator evidence.
- [x] Hosted Windows ConPTY smoke evidence via run `29226547099`.
- [x] **Submit-capable multi-line text area** - add a result-returning,
controlled API plus `_mut` convenience wrapper, preserving the current
void `timui_text_area` compatibility wrapper. Refactor `examples/chat.c`
Expand All @@ -57,7 +57,7 @@ push. Detailed pickup prompt: `docs/goals/phase1_5-platform-widgets-style-text-i
- [x] **Grapheme clustering foundation** - next/prev/width helpers integrated
into editing and truncation for ZWJ emoji, skin tones, regional
indicators, combining marks, VS16, and CRLF.
- [ ] **Protocol-neutral image layer + Sixel + iTerm2** - keep Kitty support,
- [x] **Protocol-neutral image layer + Sixel + iTerm2** - keep Kitty support,
but refactor image emission behind protocol capabilities. Add Sixel first
(including Windows Terminal usefulness), then iTerm2 inline images, with
fallback placeholders preserved.
Expand All @@ -81,11 +81,15 @@ push. Detailed pickup prompt: `docs/goals/phase1_5-platform-widgets-style-text-i
- [x] Operator smoke harnesses exist for live image protocols
(`nix develop -c make smoke-image-live PROTO=... FRAMES=N`) and
Windows ConPTY (`nix develop -c make smoke-conpty-win32`);
compile/headless harness checks are wired, but live evidence is
still pending. Use `docs/runbooks/phase1-5-live-evidence.md`.
compile/headless harness checks are wired. Use
`docs/runbooks/phase1-5-live-evidence.md` for protocol recapture
and evidence audits.
- [x] Sixel terminal evidence via hosted Windows Terminal run
`28982641529`.
- [ ] iTerm2 terminal evidence.
- [x] iTerm2 terminal evidence via hosted macOS iTerm2 run
`28986249841`.
- [x] Windows ConPTY smoke evidence via hosted Windows run
`29226547099`.

## Phase 2 backlog - architecture and applications

Expand Down
Loading
Loading