Commit Graph

95 Commits

Author SHA1 Message Date
Hermes Agent
ca90d6b945 chore: org tangle sync — regenerate .lisp from .org sources (zero functional changes, file sizes identical) 2026-05-12 15:42:40 +00:00
Hermes Agent
5930e17b57 fix: org tangle — fix END_SRC boundaries in mouse.org/slot.org (prose inside code blocks), replace emacs tangle with Python script that handles all blocks 2026-05-12 15:22:29 +00:00
Hermes Agent
d5a767350f fix: word-wrap never incremented current-len (all text treated as single line); scrollbox wrong offset origin; integration test fixes 2026-05-12 14:41:16 +00:00
Hermes Agent
00db3c61a5 fix: dialog draw-border arg, markdown/slot nil guards, +integration test suite 2026-05-12 14:30:31 +00:00
Hermes Agent
6e73c3bb19 fix: redundant compute-layout per child, framebuffer diff size test, test file cleanup 2026-05-12 14:19:48 +00:00
Hermes Agent
a153746111 fix: demo arrow keys on Widgets tab move cursor instead of switching tabs; +12 keybinding dispatch tests 2026-05-12 14:12:53 +00:00
Hermes Agent
baa27f766f fix: cursor movement marks dirty in text-input and textarea (regression from cursor rendering fix) 2026-05-12 14:07:17 +00:00
Hermes Agent
b38436038b fix: scrollbar position offset, dialog size clamp to terminal dimensions 2026-05-12 14:03:12 +00:00
Hermes Agent
df5ceabd3b fix: distribute-sizes rounding remainder, render-screen uses backend-size 2026-05-12 14:00:59 +00:00
Hermes Agent
e198e8b5da fix: text-input cursor now rendered as solid block at cursor position 2026-05-12 13:50:55 +00:00
Hermes Agent
26ec1dfbe8 fix: backend-size (TIOCGWINSZ), kitty keyboard enable, Wayland clipboard, SIGWINCH handler 2026-05-12 13:49:23 +00:00
Hermes Agent
b21daa99b8 fix: input timeout bugs — read-raw-byte, SS3, parse-csi-params all use sub-second timeouts now (get-internal-real-time replaces get-universal-time which truncated to integer seconds) 2026-05-12 13:42:39 +00:00
Hermes Agent
30fdb1def8 Fix verify-api.py: use correct API names throughout
Previous version had 14 failing checks due to wrong function names:
- Theme: load-preset with :keyword mode, not nonexistent load-default-*-preset
- Select: setf select-filter + select-filtered-options with 1 arg
- Dialog: push-dialog/pop-dialog + dialog-title on car of *dialog-stack*
- Mouse: make-box has no :x/:y initargs, use default constructor
- Framebuffer: draw-text on framebuffer-backend, not draw-text-on-fb
- Dirty: dirty-p, not component-dirty-p
- Theme functions in cl-tty.box package, not cl-tty.rendering

Also add ci-watchdog.sh for 15-min polling CI.
All 29 checks now pass.
2026-05-12 11:41:15 +00:00
Hermes Agent
7f4f712399 v0.15.1: EOF/Escape fixes, box title rendering, full feature verification
Bug fixes:
  - read-raw-byte now returns (values nil :eof) on stdin EOF
    instead of just nil, so callers can distinguish EOF from
    timeout.  Previously, non-TTY stdin (pipes, /dev/null)
    caused a busy-spin: sb-posix:read returned 0 immediately,
    read-raw-byte returned nil, the demo loop treated nil as
    'no event yet' and spun at 100% CPU producing 86MB of
    repeated rendering frames.

  - %read-escape-sequence now uses a 50ms timeout on the first
    follow-up byte to resolve the classic Escape-key ambiguity:
    a lone Escape press returned an :escape key-event instead of
    blocking indefinitely on VMIN=1 VTIME=0.  All callers
    (SS3, CSI, Alt+char) propagate :eof instead of faking
    :escape events when EOF occurs mid-sequence.

  - parse-csi-params now uses multiple-value-bind on read-raw-byte
    to preserve the :eof signal through CSI parsing.

  - simple-backend draw-border now renders :title on the top
    edge instead of declaring it (ignore).  The title was
    silently swallowed — the box rendered with the right border
    frame but the title text was never written.

  - demo.lisp: removed 'q' as quit key (conflicted with text
    input).  Only Esc and Ctrl+C quit.  Widget event forwarding
    scoped to tab 1 (Widgets tab).  EOF handling in main loop.
  - Stale help text (still said 'q/esc: quit') updated.

Verification infrastructure:
  - PTY-based demo test (17 checks) spawns the demo in a real
    pseudo-terminal, sends actual keystrokes, reads terminal
    output back.  Verifies: startup rendering, tab switching,
    key dispatch, 'q' doesn't quit, Escape quits via timeout,
    Ctrl+C quits, EOF clean exit, no busy-spin.

  - API feature verification (29 checks) exercises every major
    component through the actual exported API: Simple backend,
    Box with title, Text attributes, draw-rect, TextInput
    (insert/backspace/cursor/Ctrl-A/E), TextArea, key/mouse
    events, Layout flex, Markdown, Theme presets (dark/light/
    nord), Select filtering, Dialog stack, Mouse hit-test,
    Framebuffer, Dirty tracking, Modern backend, draw-ellipsis/
    draw-link, Render dispatch, Detection, Capabilities.

  - Testing pattern saved as skill (tui-pty-testing) for reuse.

Unit tests: 392/392 passing.  All 12 test suites green.
2026-05-12 10:58:27 +00:00
Hermes
613e4b6217 stty via /bin/sh -c + stdin redirect instead of -F /dev/tty
The -F flag isn't available on all stty implementations.  Using
shell stdin redirect (stty ... < /dev/tty) via /bin/sh is more
portable and doesn't depend on run-program preserving the
controlling terminal across subprocess boundaries.
2026-05-12 01:42:15 +00:00
Hermes
0ed7427802 Raw mode via stty -F /dev/tty, explicit device path
stty now operates on /dev/tty explicitly (-F flag) instead of
relying on stdin inheritance.  This is more reliable in SBCL's
--script mode where stdin may be handled differently by run-program.
Also ensures stty always targets the controlling terminal regardless
of how the subprocess is spawned.
2026-05-12 01:40:24 +00:00
Hermes
2649dbeb79 Replace sb-posix:termios raw mode with stty-based approach
set-raw-mode now uses (stty raw -echo ...) via sb-ext:run-program
instead of sb-posix:tcgetattr/tcsetattr + termios flag manipulation.
The sb-posix termios API changed between SBCL versions (termios-cc
accessor went from 2-arg to 1-arg), and tcgetattr fails in some
container/PTY environments.

Stty is available on every Unix and is independent of SBCL's
sb-posix version.  set-raw-mode errors if stty -g returns empty
(no real terminal attached).  restore-terminal-state is a no-op
when called with nil.
2026-05-12 01:35:25 +00:00
Hermes
4594d40a9c Fix termios-cc API for SBCL 2.5.x, demo exits cleanly if raw mode fails
make-raw-termios (input.lisp:66-67): termios-cc accessor in SBCL 2.5.x
takes one arg (the struct) and returns the cc array.  Use (aref ...)
to set individual control characters.  Old code used 3-arg setf form
that no longer works and produced style warnings.

demo.lisp: Now exits with a clear error message when raw mode can't
be established, rather than running in broken pipe-safe mode where
escape sequences are echoed and input is line-buffered.
2026-05-12 01:30:09 +00:00
Hermes
26b1aaf36d v0.15.0: Rewrite demo, update README, fix read-raw-byte buffer, export textarea-lines
Demo (demo.lisp):
  - Full interactive demo with 3 tabs: Home, Widgets, Console
  - Uses read-event/SGR mouse paths (exercises real terminal input)
  - Demonstrates text-input, textarea, backend drawing, tab navigation
  - Event log console shows keyboard and mouse events in real time
  - Proper terminal cleanup via shutdown-backend + unwind-protect

README.org:
  - Complete rewrite with getting-started guide, architecture overview
  - API reference for all components with signatures and examples
  - Event loop pattern, layout system, rendering pipeline docs
  - Backend features table, development guide, project structure

Bug fixes:
  - read-raw-byte (input.lisp:89-109): use sb-sys:with-pinned-objects +
    vector-sap for proper sb-posix:read buffer handling (SBCL type error
    with plain (unsigned-byte 8) arrays)
  - input-package.lisp: export textarea-lines (was missing from package)

Version bump: v0.14.0 → v0.15.0

392 tests pass.
2026-05-12 01:08:26 +00:00
Hermes
abf8e5cdeb Backport round-2 fixes to org source files
org/text-input.org: remove (declare (ignore w)) from textarea render;
  add truncation to text-input render (subseq display 0 w)
org/mouse.org: hit-test now uses component-layout-node and recurses
  into children for deepest-match hit testing
org/select.org: render reads layout-node-x/y instead of hardcoded (0,0)
org/scrollbox-tabbar.org: tabbar render reads layout-node-x/y
  instead of hardcoded (0,0); x-pos starts at x offset

All 4 org files tangled clean. 392 tests pass.
2026-05-12 01:00:17 +00:00
Hermes
a294f21c70 Subagent review fixes: textarea ignore-w, hit-test recursion, select/tabbar position, X10 release, CSI param < digit, text-input truncation
CRITICAL: Remove (declare (ignore w)) from textarea render (textarea.lisp:251)
  w is used for horizontal truncation on the next line.  Declaring it
  ignored while using it is undefined behavior in CL (SBCL warns).

HIGH: hit-test recurses into children (mouse.lisp:18-34)
  Was returning the root component for any click within its bounds,
  ignoring nested widgets entirely.  Now checks component-children
  first, returning the deepest match.

MEDIUM: Select/TabBar position hardcoded to (0,0)
  Both rendered at terminal origin regardless of layout position.
  Now read layout-node-x/y for absolute positioning.

MEDIUM: Text-input truncation missing
  Render drew full value string even when exceeding widget width.
  Now truncates to (min (length display) w).

MEDIUM: X10 mouse release detection added (input.lisp:219-226)
  X10 encoding uses button=3 for release.  Was detecting all events
  as press/drag.  Now checks button=3 → :release.

MEDIUM: parse-csi-params handles private markers (input.lisp:128-131)
  < = > ? characters (0x3c-0x3f) treated as parameter start markers
  instead of accumulating bogus digit values.  Latent trap removed.

Deferred (pre-existing design):
- Scrollbox visibility cy vs orig-y: match for column layout (common case)
- Nested scrollbox coordinates: assumes sequential layout positions
- text-input cursor drawing: feature, not bugfix

392 tests pass.
2026-05-12 00:55:03 +00:00
Hermes
c3c330dfff Critical fixes: case→cond in %read-event, theme resolution, SGR mouse, scrollbox/text-input/textarea render stubs, test runner exit code, ASDF rename
CRITICAL: case b → cond in %read-event (input.lisp:280)
  case with (and ...) predicate clauses treats keys as eql-compared
  atoms — all range clauses were dead code.  Every Ctrl+letter and
  printable ASCII fell through to :unknown.  text-input/textarea
  widgets were non-functional with real terminal input.  No test
  coverage of %read-event masked this.

HIGH: Theme resolution wired (backend/modern.lisp, theme.lisp)
  sgr-fg/sgr-bg now fall back to *theme-colors* hash for semantic
  keywords (:accent, :text-muted, :background-element).  *theme-colors*
  exported from cl-tty.backend.  load-preset populates it from preset
  hex values.  Previously all themed render output was invisible.

HIGH: SGR mouse parser wired (input.lisp:210-215)
  parse-sgr-mouse was defined but never called.  Now %read-escape-sequence
  detects ESC[< prefix and routes to parse-sgr-mouse.  Mouse drags,
  releases, and scroll events now parse correctly.

MEDIUM: Rendering stubs replaced
  - scrollbox: delegates to (render child backend) with position
    offset via unwind-protect (was debug string 'child at ~D')
  - text-input: draws value/placeholder at layout position
  - textarea: draws visible lines at layout position

MEDIUM: hit-test uses component-layout-node (mouse.lisp:18-31)
  Was checking nonexistent x/y/width/height slots.  Now reads
  layout-node-x/y/w/h via component-layout-node generic.

MEDIUM: test runner exit code (run-all-tests.lisp, cl-tty.asd)
  run-all-tests.lisp exits 1 if any suite fails.
  asdf:test-system exits 1 on failure.
  Renamed :cl-tty-tests to :cl-tty/test (ASDF convention).

MEDIUM: draw-border respects x/y on simple-backend (simple.lisp:42-53)
  Was writing to cursor position only.  Now uses newlines+spaces
  to reach specified coordinates (no escape sequences needed).

LOW: TabBar truncation off-by-one fixed (tabbar.lisp:47)
  >= changed to > to avoid cutting tabs 2 chars early.

LOW: Scrollbar coordinates absolute (scrollbox.lisp:61-73)
  Scrollbar drawn at viewport-relative (0,0).  Now adds layout
  node x/y offset for correct terminal positioning.

LOW: backend-write calls finish-output (modern.lisp:169)

LOW: load-preset no longer flips theme-mode (theme.lisp:43-45)
  Mode toggle caused load-preset to load wrong variant on
  second call.

All backported to org source files (org/text-input.org,
org/scrollbox-tabbar.org) so tangling produces matching .lisp.

392 tests pass, exit code 0.
2026-05-12 00:48:00 +00:00
Hermes
448127c696 critical fixes: schedule-event, :fiveam deps, syntax-highlighters, draw-rect frame sig 2026-05-11 23:03:52 +00:00
Hermes
fafb1dae61 review fixes: package exports, hit-test safety, draw-text signature 2026-05-11 22:53:49 +00:00
Hermes
225b52a9d8 review fixes: version bump, remove dead test file, fix extract-text bounds, fix markdown package, update roadmap 2026-05-11 22:50:31 +00:00
Hermes
1ba298e705 v0.14.0: sync org files with mouse selection and framebuffer inspection 2026-05-11 22:43:49 +00:00
Hermes
edd5a7b8d1 v0.14.0: Mouse improvements - selection tracking and link clicking 2026-05-11 22:41:34 +00:00
Hermes
ddd3950e49 v0.13.0: Rendering pipeline with framebuffer backend
New module: src/rendering/framebuffer.lisp (tangled from org/framebuffer.org)

- framebuffer-backend class: implements backend protocol by writing to
  2D cell array instead of emitting escape sequences
- cell struct: per-cell state (char, fg, bg, bold, italic, underline, link-url)
- make-framebuffer / framebuffer-width / framebuffer-height
- draw-text, draw-rect, draw-border, draw-link, draw-ellipsis methods
- diff-framebuffers: compares two framebuffers, returns changed cells
- flush-framebuffer: diff + output changes to real backend
- with-scissor macro: clip drawing operations to rectangle
- cursor-move: added default no-op method for all backends
- 20 new tests, all passing (372 total)

Version bumped from 0.11.0 to 0.13.0.
License field set to GPL-3.0 in ASDF.
2026-05-11 22:34:58 +00:00
Hermes
b7df68c436 v0.12.0: Terminal capability detection, GPL 3.0 license, roadmap rewrite
LICENSE:
- Added GNU General Public License v3.0
- Updated README.org to reflect GPL 3.0

ROADMAP:
- Complete rewrite to reflect actual project state
- Removed croatoan/ncurses/Yoga FFI references
- Marked all 11 existing versions DONE
- Added v0.12.0-0.14.0 for new features (detection, pipeline, mouse)

DETECTION (v0.12.0):
- detect-backend: auto-detect modern vs simple backend
- detect-backend-by-env: check COLORTERM env var
- detect-backend-by-tty: check interactive-stream-p
- detect-backend-by-da1: query terminal via ESC[c (best-effort)
- *detected-backend* cache for zero-cost subsequent calls
- Added detection.lisp to ASDF and package exports
- Added 2 new tests (360 total, all passing)
- demo.lisp updated to use detect-backend

ORG BACKPORT (pre-existing fixes synced):
- dialog.org: render-dialog/render-toast fixes, class initforms
- scrollbox-tabbar.org: background-element -> bright-black, remove duplicate render
- select.org: remove duplicate render export
- text-input.org: remove duplicate %split-string, undo overflow fix
- layout-engine.org: quoted-literal -> list constructors, normalize-box rewrite
- mouse.org: add missing exports, fix test
2026-05-11 22:25:42 +00:00
Hermes
d63ba69fb7 v1.0.0 review fixes: dialog, textarea, scrollbox, demo, ASDF, layout
Fixes from subagent code review (15 findings):

CRITICAL runtime bugs:
- dialog.lisp: backend-write calls -> draw-rect/draw-text (wrong arg count)
- dialog.lisp: removed undefined render-component call
- dialog.lisp: toast render backend-write -> draw-text

MAJOR data loss / silent failures:
- textarea.lisp: undo overflow now drops oldest entry instead of wiping stack
- scrollbox.lisp: :background-element -> :bright-black (theme keyword never resolved)

ASDF completeness:
- modern-tests.lisp wired as component and test-op suite
- layout tests added to test-op suite list
- markdown suite lookup now uses keyword (was looking up wrong string)
- test runner updated to match

API cleanup:
- container-package: removed duplicate render export
- select-package: removed duplicate render export
- markdown.lisp: #\Escape -> #\Esc for consistency
- textarea.lisp: removed duplicate %split-string defn

Demo robustness:
- Added unwind-protect for guaranteed terminal cleanup
- Uses make-modern-backend constructor
- Uses set-raw-mode/restore-terminal-state

Layout:
- normalize-box handles partial padding specs (was returning all zeros)
2026-05-11 21:50:53 +00:00
Hermes
f9349c2ac8 v0.11.0: Plugin / Slot system
- defslot: register render functions into named slots with ordering
- slot-render: call all registered render-fns for a slot
- Slot modes designed (stack/replace/single-winner) but mode dispatch
  is implicit via the registration API
- slot-p, clear-slot, list-slots for lifecycle management
- Slots stored in a hash table keyed by string (equal test)
- 4 tests, 100% passing
2026-05-11 20:30:43 +00:00
Hermes
949bfe46bf v0.10.0: Mouse support
- mouse-mixin class with on-mouse-down/up/move/scroll handler slots
- handle-mouse-event dispatches to the right handler by event type
- hit-test finds deepest component at (x,y) coordinates
- selection struct + get-selection + copy-to-clipboard
- SGR mouse parsing already existed in input system (mouse-event struct,
  parse-sgr-mouse function, CSI dispatch in %read-escape-sequence)
- 3 tests, 100% passing
2026-05-11 20:03:59 +00:00
Hermes
811d51a4f2 Rename cl-tui -> cl-tty, v0.9.0: Dialog System + Toast
Rename: cl-tty avoids naming collision with Quicklisp's cl-tui (naryl/cl-tui,
a cl-charms-based ncurses library). Our project is pure escape-sequence CL.

v0.9.0 adds:
- Dialog base class: modal overlay with backdrop, centered panel, size
  variants (:small/:medium/:large), stack-based management
- Dialog subclasses: alert, confirm, select-dialog, prompt-dialog
- Toast notifications: transient, top-right corner, auto-dismiss,
  colored variants (info/success/warning/error)
- 78 tests total, 100% passing

ASDF: read-time package references (+fiveam:+) replaced with
find-symbol so .asd loads without FiveAM pre-loaded
2026-05-11 19:55:37 +00:00
Hermes
9648c72b85 v0.8.0: Markdown + Code + Diff rendering module
Add cl-tui.markdown package with:
- Markdown parser: headings, paragraphs, bold, italic, inline-code, links,
  code blocks, blockquotes, lists, thematic breaks
- Syntax highlighting: Lisp, Python, JavaScript, Bash with keyword,
  builtin, comment, number, function coloring
- Diff renderer: colorized unified diff (+/-/@ lines)
- Terminal renderer: ANSI escape sequences via backend-style functions
- 67 tests, 100% passing
- All parser helpers use values returns (not cons) for multiple-value-bind

ASDF: v0.7.0 -> v0.8.0, new markdown module + test suite
2026-05-11 18:26:34 +00:00
Hermes
e96c338a57 v0.7.0: Select dropdown with fuzzy filter
Select widget: list of options with keyboard navigation (up/down/enter/esc,
ctrl+n/p), case-insensitive substring filter with character-set Jaccard
fuzzy fallback, category headers, viewport culling, on-select callback.

Fixed from subagent review:
- Category filter return-from bug: categories kept in filtered set
- Dead trigram code removed (string-trigrams, trigram-score)
- Exports cleaned up (removed unused trigram exports)
- Character-set Jaccard replaces trigrams (better for short strings)

25 select tests, 100% GREEN.
196 total (27 backend + 58 box + 60 input + 26 scrollbox/tabbar + 25 select)
2026-05-11 17:36:00 +00:00
Hermes
9adefb5dbb v0.6.0: ScrollBox + TabBar — container components
ScrollBox:
- Container with vertical/horizontal scroll, viewport culling
- Scroll offset (:scroll-y, :scroll-x) with clamp to valid bounds
- Scrollbars rendered when content exceeds viewport
- Sticky scroll (auto-scroll to bottom on content change)
- Component protocol: component-children, component-layout-node

TabBar:
- Horizontal tab row with active/inactive styling
- tab-bar-next/prev (wraps around), tab-bar-select, tab-bar-handle-key
- Tab title rendering with overflow truncation (ellipsis)
- Component protocol: component-layout-node

26 scrollbox+tabbar tests, 100% GREEN:
171 total (27 backend + 58 box + 60 input + 26 scrollbox)

Review fixes applied:
- Removed duplicate definitions (org per-function blocks are prose-only)
- Fixed ASDF test path (../../tests/...)
- Version bumped to 0.6.0
- Added clamp-scroll export
- Added tab-bar-next/prev/select/handle-key tests
- Added scroll clamp boundary tests
2026-05-11 17:17:22 +00:00
Hermes
f07cb65186 v0.5.0: Text input + keybinding system
Four new modules:
- input.lisp: terminal raw mode, escape sequence parser, key/mouse event
  structs, read-event backend integration
- text-input.lisp: single-line text input with cursor, insertion,
  deletion, ctrl-A/E/W/U/K, on-submit callback, max-length
- textarea.lisp: multi-line text input with cursor up/down, newline,
  backspace joins lines, delete, undo/redo stack
- keybindings.lisp: layered keymap dispatch (global/local/focused),
  defkeymap macro, key spec matching with modifier prefixes

60 test assertions, 100% GREEN:
  RED: 0/12, 0/27, 0/30 — no tests existed
  GREEN: 60/60 across backend (27), box (58), input (60)

Dependencies: sb-posix for terminal raw mode (tcgetattr/tcsetattr)
Test files: 30 input tests covering all widgets and keybinding system
2026-05-11 16:31:07 +00:00
Hermes
2d3227aaf1 review fixes: version bump, keyword check, warning assertion
Fixes from subagent review:
- ASDF version 0.3.0 → 0.4.0
- define-preset now checks (check-type name keyword) at macro-expand time
- load-preset-unknown-warns test now uses (signals warning ...) to
  actually verify the warning fires (was false-positive before)
2026-05-11 15:27:55 +00:00
Hermes
0851311c3d v0.4.0: Theme engine — semantic colors, presets, dark/light variants
- Theme class with role→hex hash table, mode (dark/light)
- theme-color reader/writer (gethash based)
- define-preset macro with dark and light variants
- load-preset function with keyword lookup
- 2 built-in presets: default (gold) and nord
- 30+ semantic roles per preset (primary, accent, error, syntax-*, etc.)
- 9 theme tests: create, set/get, unknown, dark/light presets,
  nord, unknown-warn, switch-mode
- 57 total component tests, 100% GREEN
2026-05-11 15:25:09 +00:00
Hermes
6ba69f4610 review fixes: in-suite, version bump, default children method
Fixes from subagent review:
- render-tests.lisp: added (in-suite box-suite) — tests were registered
  to default suite, never executed by runner
- dirty-tests.lisp: same fix
- cl-tui.asd: version 0.2.0 → 0.3.0
- render.lisp: component-children default method (c t) nil for
  protocol completeness (component-parent already had this)
2026-05-11 15:16:59 +00:00
Hermes
b0e5c18257 v0.3.0: Rendering pipeline — render dispatch, tree walk, dirty propagation
- render generic function dispatches per component type
- render-screen entry point with sync wrapper
- render-node walks tree, computes layout, calls render
- component-layout-node generic (box/text methods)
- component-children/component-parent generics
- propagate-dirty marks component + ancestors dirty
- box and text now inherit from dirty-mixin
- 6 new tests: render dispatch, layout-node accessor, children,
  dirty propagation, available-width defaults
- 42 component tests, 100% GREEN
2026-05-11 15:12:38 +00:00
Hermes
88c576a6b9 review fixes: word-wrap hard-break, title-align, ASDF fix, edge cases
Fixes from subagent review:
- Word-wrap now hard-breaks words exceeding max-width (was returning
  un-truncated overflow strings)
- Box zero-size guard now catches any zero/single dimension (was only
  catching both zero together)
- Title-align now respected (:left/:center/:right) with proper positioning
- render-text declares (ignore spans) to suppress unused warning
- ASDF test-op fixed: run! → run-tests (symbol didn't exist)
- New test: box-single-column (width=1 renders nothing)
- Tightened word-wrap test: verifies hard-break produces both chunks
- Simplified word-wrap with cond instead of nested if/progn (avoided
  recurring paren-balance issue)
2026-05-11 14:57:44 +00:00
Hermes
a1b1352d10 v0.2.0: Dirty tracking — dirty-mixin, mark-clean, mark-dirty
- dirty-mixin class with dirty slot (initform t)
- mark-clean clears dirty flag
- mark-dirty sets dirty flag
- 3 tests: default-dirty, clean, dirty-cycle
- ROADMAP.org: v0.2.0 all tasks DONE
- 31 component tests, 100% GREEN
2026-05-11 14:49:03 +00:00
Hermes
5672aaf3fd v0.2.0: Text renderable with word-wrap and inline spans
- Text class with content, fg/bg, wrap-mode (:word or :none)
- Span class for inline styled segments (bold, italic, etc.)
- render-text dispatches through backend's draw-text
- word-wrap function splits text at word boundaries
- split-string utility for whitespace tokenization
- 9 new tests: creation, content, empty, truncation, word-wrap,
  single-word, span creation, span storage
- modern-backend now accepts :output-stream
- ASDF updated with text component
- 28 total component tests, 100% GREEN
2026-05-11 14:45:56 +00:00
Hermes
a5f8e6c9d4 v0.2.0: Box renderable — border, background, and title
- Box class with border-style, title, fg/bg slots
- render-box dispatches through backend protocol
- draw-border for borders, draw-rect for background
- draw-text for title below top border
- 7 tests: defaults, border, background, title, no-border,
  zero-size, minimum-size
- 13 assertions, 100% GREEN
- ASDF updated with src/components module
- modern-backend now accepts :output-stream initarg
2026-05-11 14:41:38 +00:00