1025 Commits

Author SHA1 Message Date
phranck 301e9afcbd Docs: Replace ASCII diagram with rendered image in KeyboardShortcuts
- Add keyboard-event-dispatch.png (light + dark) rendered from Mermaid
- Replace ASCII code block with @Image directive matching existing DocC style
2026-02-14 22:08:44 +01:00
Frank Gregor 32d72b3b85 Chore: Update test count badge to 1113 tests [skip ci] 2026-02-14 20:34:09 +00:00
phranck 9979b67d93 Fix: Resolve NSLock deadlock and re-enable localization test suites
- Fix re-entrant NSLock deadlock in string(for:) -> translationValue -> translations(for:) by restructuring to lock-free internal helpers (_translations, _translationValue)
- Fix wrong Bundle.module subdirectory path ("Localization/translations" -> "translations") that prevented translations from ever loading
- Fix didSet side effects during init() by computing language once before single Phase-1 assignment
- Remove redundant setNeedsRender() call in AppState.setLanguage()
- Add config directory injection for test isolation (LocalizationService(configDirectoryPath:))
- Re-enable LocalizationServiceTests and LocalizationKeyConsistencyTests (42 tests)
- Persistence tests now use isolated temp directories instead of real config path
2026-02-14 21:31:14 +01:00
phranck c63891d380 Disable: All localization test suites - complex setup interactions require redesign 2026-02-14 20:56:08 +01:00
phranck 604ed37f08 Fix: Remove init() from test class - create sut locally in each test instead 2026-02-14 20:52:14 +01:00
phranck fd63b6dcaa Disable: LocalizationKeyConsistencyTests - Bundle loading in init() hangs on CI 2026-02-14 20:51:36 +01:00
phranck 8ebfcc23e5 Fix: Remove File I/O from init()/deinit() and test bodies that causes hanging 2026-02-14 20:45:27 +01:00
phranck 66c6254075 Fix: Use final class instead of struct for test suites to preserve sut property 2026-02-14 20:40:00 +01:00
phranck a3add15fed Fix: Add proper init() for struct test suites instead of disabling 2026-02-14 20:36:32 +01:00
phranck 91a06fecef Fix: Temporarily disable localization test suites to debug fatalError 2026-02-14 20:35:33 +01:00
phranck bb500d26f1 Fix: Move translation loading from static to instance init to prevent runtime errors 2026-02-14 20:32:50 +01:00
phranck 2b15f46715 Fix: Refactor localization tests to prevent parallel execution conflicts
- Changed test classes from 'final class' to 'struct' (matches project pattern)
- Fixed LocalizationKeyConsistencyTests to use static lazy loading
- Disabled Persistence tests (File I/O conflicts with parallel execution)
- Create LocalizationService instances per test instead of shared
- Add cleanup after File I/O operations

Persistence tests disabled until proper serialization mechanism is available.
2026-02-14 20:26:38 +01:00
phranck d711979118 Refactor: Move i18n documentation from separate files to DocC
- Created new Localization.md article in DocC catalog
- Integrated user guide and developer guide content into single article
- Deleted separate Documentation/i18n-guide.md and i18n-developer.md
- Updated README.md to link to DocC documentation
- Updated GettingStarted.md to reference Localization guide

Documentation now follows project-wide DocC standards instead of separate Markdown files.
2026-02-14 19:56:38 +01:00
phranck 1a4c1d8782 Refactor: Convert localization tests to Swift Testing Framework
- Replace XCTest with Testing framework (@Test, #expect, @Suite)
- Use Issue.record() instead of XCTFail()
- Consistent with all other tests in the codebase
- Maintains same test coverage and functionality
2026-02-14 19:47:08 +01:00
phranck ba8f8849fe Fix: SwiftLint empty_count violation in tests
Replace count > 0 check with isEmpty for SwiftLint compliance.
2026-02-14 19:43:11 +01:00
phranck 0f7cf6c703 Docs: Update README with i18n features and examples
- Add i18n badge to metadata
- Document 5 built-in languages with type-safe constants
- Add localized string example
- Update project structure to show Localization module
- Update test count to reflect i18n tests
- Link to comprehensive i18n guides
2026-02-14 19:40:28 +01:00
phranck 035e847588 Fix: LocalizationKeyConsistencyTests bundle loading for test environment
Tests now correctly load en.json from source directory when running in test context.
2026-02-14 19:38:04 +01:00
phranck ff6c6c209c Feat: Complete i18n system with type-safe keys, tests, and documentation
Phase 3 Implementation - Complete internationalization framework with:

## New Features
- Type-safe LocalizationKey enum with 7 categories (Button, Label, Error, Placeholder, Menu, Dialog, Validation)
- All 80+ translation keys organized by category with IDE autocomplete support
- Convenient extensions on LocalizedString, Text, and LocalizationService

## Translations Expanded
- All 5 language JSON files (en, de, fr, it, es) expanded with new string categories:
  - Additional labels: page, item, items, total, from, to
  - Additional errors: timeout, file_not_found, permission_denied
  - Additional placeholders: choose_file
  - Additional menu items: new, open, save, exit
  - Additional dialogs: exit_confirmation, success, error
  - Validation category: email_invalid, password_too_short, username_taken, field_required

## Comprehensive Test Suite
- LocalizationServiceTests: Bundle loading, string resolution, fallback behavior, language switching, persistence, thread safety
- LocalizationKeyTests: Type-safe key resolution across all categories and languages
- LocalizationKeyConsistencyTests: Validates all enum keys exist in translations and vice versa

## Documentation
- i18n-guide.md: User-facing documentation for using localized strings
- i18n-developer.md: Developer guide for adding keys, languages, and understanding the architecture

## Technical Details
- All translation keys now compile-time verified via consistency tests
- Fallback chain: Current Language → English → Key itself
- XDG-compatible persistent storage (macOS: ~/Library/Application Support, Linux: ~/.config)
- Thread-safe operations with NSLock
2026-02-14 19:20:11 +01:00
phranck f54816d099 Feat: Phase 2 i18n Core API - LocalizedString View and Language Management
- Add LocalizedString View component for displaying localized strings
- Add Text(localized:) convenience initializer for Text views
- Add AppState.setLanguage(_:) method for runtime language switching
- Add AppState.currentLanguage computed property
- Integrate LocalizationService into RenderLoop environment
- Remove duplicate LocalizedString function (View takes precedence)
- All 1069 tests pass

Core i18n functionality complete. Ready for Phase 3 (String Replacement)
2026-02-14 18:28:54 +01:00
phranck 978c15993e Feat: Phase 1 i18n Infrastructure - LocalizationService and translation files
- Add LocalizationService singleton for managing language selection
- Implement XDG-compatible persistent language storage (macOS/Linux)
- Create translation JSON files for 5 languages (EN, DE, FR, IT, ES)
- Add LocalizationService to EnvironmentValues
- Support dot-notation keys for string resolution
- Implement fallback to English for missing keys
- Update Package.swift to include translation resources
- All 1069 tests pass
2026-02-14 18:15:33 +01:00
phranck cab942c1cc Refactor: Complete elimination of RenderNotifier global singleton
Replace RenderNotifier with clean AppState and RenderCache singletons:

- Add AppState.shared static singleton for global access
- Add RenderCache.shared static singleton for memoization
- Remove RenderNotifier enum completely
- Remove RenderNotifierKey from EnvironmentValues
- StateBox.didSet uses AppState.shared directly
- AppStorage.wrappedValue setter uses AppState.shared directly
- NotificationService.post() uses AppState.shared directly
- Spinner animation uses AppState.shared directly
- NotificationHostModifier animation uses AppState.shared directly
- TUIContext uses RenderCache.shared by default (injectable for tests)
- Update test infrastructure to isolate RenderCache per test

Architecture: Pure singleton pattern with no global state registry.
All 1069 tests pass, no new compiler warnings.

This completes the full elimination of RenderNotifier and provides a
clean, explicit architecture where AppState.shared is the single
source of truth for render triggers.
2026-02-14 18:03:38 +01:00
phranck 0148366bf6 Refactor: Replace RenderNotifier.current global with dependency injection
- Make RenderNotifier.current optional (nil by default) to support DI
- Add RenderNotifierKey and renderNotifier property to EnvironmentValues
- Initialize renderNotifier in RenderLoop.buildEnvironment()
- Migrate Spinner animation loop to read from environment
- Migrate NotificationHostModifier to read from environment via parameter
- Update StateBox.didSet to use optional chaining fallback
- Update AppStorage.wrappedValue setter to use optional chaining fallback
- Update NotificationService.post() to use optional chaining fallback
- Update test comments to reflect optional chaining pattern
- All 1069 tests pass, no new compiler warnings

Phase 3 implementation of P4.16 dependency injection refactor complete.
2026-02-14 17:50:14 +01:00
phranck 91891a9ea7 Chore: P4.18 concurrency documentation improvements
- Add comprehensive documentation to RenderNotifier static properties
  explaining main-thread-only access guarantees and safety model
- Add inline safety comments to Terminal memory operations
  explaining why unsafe memory rebinding is safe in context

Audit findings: All concurrency patterns are justified, documented,
and safe. No critical issues found. These are quality-of-life improvements
for code maintainability.
2026-02-14 17:38:28 +01:00
phranck cbf5b9d5b6 Refactor: Make ItemListHandler generic over SelectionValue
- Replace AnyHashable type erasure with generic SelectionValue parameter
- Remove configureSelectionBindings and assign typed bindings directly
- Change itemIDs from [AnyHashable] to [SelectionValue?] for nil-safe non-selectable rows
- Update _ListCore, _TableCore, and tests to use type-safe selection
2026-02-14 17:22:08 +01:00
phranck b404a62731 Chore: README update, Foundation cleanup, file splitting
- Update README.md with multi-module structure, new components, macOS 14+
- Remove unnecessary import Foundation from 9 source files
- Split Focus.swift into Focus, Focusable, FocusState
- Split StatusBarItem.swift into StatusBarItem, SystemStatusBarItem, StatusBarItemBuilder
- Split ASCIIConverter.swift into ASCIIConverter, +Braille, +Dithering
- Update WHATS-NEXT.md with completed cleanup tasks
2026-02-14 16:57:16 +01:00
phranck ebd8237282 Fix: Handle .space key event consistently across all interactive views
- Add explicit .space case to TextFieldHandler for space character insertion
- Change ItemListHandler from .character(" ") to .space for selection toggle
- Change RadioButtonGroupHandler from .character(" ") to .space for selection
- Update tests to use KeyEvent(key: .space) matching actual key parsing
2026-02-14 16:27:44 +01:00
Frank Gregor e64b16a442 Chore: Update test count badge to 1071 tests [skip ci] 2026-02-14 14:06:30 +00:00
phranck 04d71f42c2 Refactor: Selective RenderCache invalidation for performance
- Remove pulse-triggered cache clearing (was destroying entire cache
  ~7x/sec during focus animation, yielding 0% hit rate)
- Add RenderCache.clearAffected(by:) for identity-aware invalidation
- Wire StateBox to its ViewIdentity for targeted cache clearing on
  @State changes (sibling subtrees retain their cached buffers)
- Add 5 new tests for clearAffected covering ancestor, descendant,
  sibling preservation, exact match, and empty cache scenarios
- Update EquatableView documentation with pulse/focus guidance
2026-02-14 15:03:51 +01:00
phranck 3835ed87e3 Refactor: Dissolve Core/ and Styling/ directories in TUIkit module
- Move KeyEvent.swift from Core/ to Focus/
- Move ListRowExtractor.swift, SelectableListRow.swift from Core/ to Views/
- Move 5 styling environment files from Styling/ to Environment/
- Move ViewConstants+EdgeInsets.swift from Styling/ to Extensions/
- Remove empty PrimitiveTypes+Extensions.swift stub
2026-02-14 14:35:47 +01:00
phranck a945ae3b36 Fix: Add FoundationNetworking import for Linux compatibility
- Import FoundationNetworking conditionally on Linux where URLRequest,
  URLSession and URLSessionDataTask are not part of Foundation
2026-02-14 14:17:05 +01:00
phranck d0627bafdc Refactor: Extract TUIkitView module and organize sub-module directories
- Extract View system foundation into new TUIkitView module (Layer 1)
  - View, ViewBuilder, TupleViews, ViewModifier, PrimitiveViews, EquatableView
  - Renderable, RenderContext, RenderCache, ChildInfo, SpacerProtocol
  - State, StateStorage, StateRegistration, HydrationContext
  - EnvironmentValues, EnvironmentModifier, ViewServiceEnvironment
- Introduce SpacerProtocol to decouple ChildInfo from concrete Spacer type
- Split ServiceEnvironment.swift (StateStorageKey/RenderCacheKey to TUIkitView)
- Add @_exported import TUIkitView in Exports.swift for backward compatibility
- Make internal types public for cross-module visibility (Renderable, Layoutable,
  renderToBuffer, ChildView, ChildInfo, ModifiedView, StateStorage, RenderCache)
- Organize TUIkitCore into Rendering/, Environment/, Input/, Extensions/, Concurrency/
- Organize TUIkitStyling into Color/, Theme/, Styles/
2026-02-14 14:11:09 +01:00
phranck 3fb4944472 Refactor: Move runtime services from RenderContext to EnvironmentValues
- Add ServiceEnvironment.swift with 9 EnvironmentKeys for runtime services
  (stateStorage, lifecycle, keyEventDispatcher, renderCache, preferenceStorage,
  pulsePhase, cursorTimer, focusIndicatorColor, activeFocusSectionID)
- Remove tuiContext, pulsePhase, cursorTimer, focusIndicatorColor, and
  activeFocusSectionID as direct RenderContext properties
- Inject all services through EnvironmentValues in RenderLoop.buildEnvironment()
- Add convenience RenderContext init that accepts TUIContext and auto-injects
  services into the environment
- Simplify isolatedForBackground() to only swap environment values
- Migrate ~49 access sites in ~25 source files from context.tuiContext.X and
  context.pulsePhase/cursorTimer to context.environment.X
- Update 38 test files to use the new convenience init
2026-02-14 13:13:24 +01:00
phranck d39e02722b Refactor: Extract TUIkitCore micro-kernel module
- Move pure value types and protocols to new TUIkitCore target (zero deps)
- Move whole files: FrameBuffer, ViewIdentity, Lock, TerminalSymbols
- Split types from mixed files: LayoutTypes (from Renderable), KeyEvent
  (from KeyEventDispatcher), EnvironmentKey (from Environment),
  PreferenceKey (from Preferences), String+TerminalWidth (from String+ANSI)
- Inline ANSIRenderer.reset as private constant in FrameBuffer
- Promote internal types to public for cross-module visibility
- Add @_exported import TUIkitCore to Exports.swift
2026-02-14 04:23:50 +01:00
phranck ce850e1b29 Refactor: Extract TUIkitStyling and TUIkitImage modules
- Extract 12 pure type definitions (Color, Palette, Appearance, BorderStyle, etc.) into TUIkitStyling module with no dependencies
- Extract 3 image processing files (RGBAImage, ImageLoader, ASCIIConverter) into TUIkitImage module (deps: CSTBImage, TUIkitStyling)
- Split 6 mixed files into type definitions (TUIkitStyling) and environment glue (TUIkit/Styling/)
- Decouple ThemeManager from AppState via renderTrigger closure
- Decouple ASCIIConverter from ANSIRenderer via local ANSIEscape constants
- Add @_exported imports in Exports.swift for backward compatibility
- All 1064 tests pass, no breaking API changes
2026-02-14 03:14:14 +01:00
phranck be19689b84 Feat: Add image size limits and URL timeout configuration (P4.19)
- Add .imageMaxPixelCount(_:) modifier to reject oversized images
- Add .imageURLTimeout(_:) modifier with configurable timeout (default: 30s)
- Replace Data(contentsOf:) with URLSession.dataTask for proper timeout support
- Add ImageLoadError.imageTooLarge error case
- Wire environment values through _ImageCore to PlatformImageLoader
2026-02-14 02:17:26 +01:00
phranck e214215610 Refactor: Replace MainActor.assumeIsolated with @preconcurrency Equatable
- Migrate 20 Equatable conformances across 17 files from
  nonisolated + MainActor.assumeIsolated to @preconcurrency Equatable (SE-0423)
- Remove unnecessary import Foundation from 29 source files
- Extract TextFieldHandler clipboard ops into TextFieldHandler+Clipboard.swift
- Extract RenderContext into RenderContext.swift (Renderable.swift 553 -> 279 lines)
- Extract ANSIColor enum into ANSIColor.swift (Color.swift 600 -> 533 lines)
- Add deprecation timeline note for progressBarStyle(_:)
- Migrate test usages from progressBarStyle to trackStyle
2026-02-14 02:10:26 +01:00
phranck 02d1921bf4 Refactor: Apply project analysis improvements (P1-P3)
- Extract scattered opacity magic numbers into ViewConstants enum (15+ files)
- Extract "No items" string literals into ViewConstants.emptyListPlaceholder
- Add EdgeInsets.containerDefault and .dialogDefault named constants
- Standardize file headers to emoji format across 10 files
- Extract shared selection binding helper into ItemListHandler
- Rename unclear short variables (p, w, h, v, i) to descriptive names
- Split StatusBarItem.swift: extract Shortcut enum into Shortcut.swift
- Add sanitizedForTerminal property for ANSI escape sequence defense
- Replace Mirror-based button extraction in Alert with ButtonProvider protocol
- Add process name sanitization in file storage paths
- Add project analysis improvement plan with full checklist
2026-02-14 01:29:46 +01:00
Frank Gregor 41dfb6f02d Chore: Update test count badge to 1066 tests [skip ci] 2026-02-13 23:53:56 +00:00
phranck 3a30229a3b Merge pull request #90 from phranck/feat/image-view
Feat: Add Image view with ASCII art rendering
2026-02-14 00:51:17 +01:00
phranck 7682efc516 Chore: Fix SwiftLint CI failures
- Add _ImageCore to type_name exclusion list in .swiftlint.yml
- Suppress false-positive empty_count on integer counter variable
2026-02-14 00:48:43 +01:00
phranck b3d563040a Feat: Add Image view with ASCII art rendering, bracketed paste, and input filtering
- Add Image view rendering local files and URLs as colored ASCII art
- Add CSTBImage C target wrapping stb_image for cross-platform image decoding
- Add ASCIIConverter with block, ASCII, and braille character sets
- Support trueColor, ANSI-256, grayscale, and mono color modes
- Add Floyd-Steinberg dithering for improved visual quality
- Add async image loading with URLImageCache for URL sources
- Add bracketed paste mode for bulk text insertion in text fields
- Add TextContentType modifier for input character filtering
- Add ContentMode enum and aspectRatio(_:contentMode:) View modifier
- Add text-input priority in key dispatch to prevent shortcut conflicts
- Add Image (File) and Image (URL) demo pages to example app
- Update DocC documentation with new symbols and layout table
2026-02-14 00:43:22 +01:00
phranck c75168a314 Chore: Remove obsolete to-dos.md files
- Delete to-dos.md and .claude/to-dos.md (replaced by WHATS-NEXT.md)
- Update global CLAUDE.md: remove to-dos.md section, fix references to WHATS-NEXT.md
- Add session-state.json to per-project files documentation
2026-02-13 21:31:44 +01:00
phranck 54aff891a0 Chore: Add TUIkitVersion constant (0.3.0) v0.3.0 2026-02-13 20:39:16 +01:00
phranck 1e65440a94 Chore: Add export-ignore to reduce SPM download size
- Mark non-essential files/dirs with export-ignore in .gitattributes
- Source archives (GitHub releases, package registries) now exclude:
  .claude/, .cursor/, .github/, .serena/, .vscode/, project-template/,
  AGENTS.md, CONTRIBUTING.md, WHATS-NEXT.md, to-dos.md, gource-swift.sh,
  .gource.conf, .windsurfrules, .swift-format, .swiftlint.yml
- Files remain tracked in git (history preserved)
- SPM consumers only receive Sources/, Tests/, Package.swift, README, LICENSE
2026-02-13 20:37:17 +01:00
phranck 70f852222c Docs: Fix all 217 DocC warnings and clean up project
- Fix disambiguation suffix (-2f9gk) in statusBarItems reference
- Fix missing parameter docs in alert() overloads (borderStyle, borderColor, titleColor)
- Replace ``InternalType`` DocC links with `InternalType` inline code across ~30 source files and ~8 DocC articles
- Remove internal types (FocusManager, ThemeManager) from DocC Topics sections
- Fix ambiguous statusBarItems(_:) link in StatusBarItemBuilder
- Update DocC articles: FocusSystem, Architecture, ListAndTable, LayoutSystem (new), RenderCycle, AppLifecycle
- Update TUIkit.md Topics with all current public types
- Add papers/ and .gource/ to .gitignore, remove from tracking
- Delete stale files: output.log, palette-preview.html, session.md, backup files
- Move completed plans (codebase-refactoring, REFACTORING_ROADMAP) to done/
- Remove empty .claude/commands/ directory
2026-02-13 20:05:12 +01:00
phranck 0cf41dc29a Chore: Add GitHub Pages routing support for DocC
- Copy index.html as 404.html for SPA deep-link routing
- Replace root index.html with redirect to /documentation/tuikit/
2026-02-13 19:28:21 +01:00
phranck 1f08efade5 Chore: Move landing page to phranck/tuikit.dev repo
- Remove docs/ directory (now in phranck/tuikit.dev)
- Remove scheduled workflows (update-plans-data, update-weekly-activity, update-social-cache)
- Remove build-landing and deploy-landing CI jobs
- Remove pages/id-token permissions (no longer needed)
- Keep: build (lint/test), update-badge, deploy-docs (DocC -> docs.tuikit.dev)
2026-02-13 18:55:59 +01:00
github-actions[bot] 73ac66b702 chore: update plans data [skip ci] 2026-02-13 17:37:29 +00:00
phranck 756390dce3 Chore: Split CI into separate Landing Page and DocC deployments
- Landing page (Astro) deploys to tuikit.dev via main repo GitHub Pages
- DocC documentation deploys to docs.tuikit.dev via phranck/tuikit-docs
- Use peaceiris/actions-gh-pages for cross-repo DocC deployment
- Update documentation URLs to docs.tuikit.dev
2026-02-13 18:12:06 +01:00
phranck 021c62edcf Chore: Migrate custom domain from tuikit.layered.work to tuikit.dev
- Update CNAME in CI workflow
- Update Astro site URL, JSON-LD structured data
- Update robots.txt, sitemap.xml, install-template.sh
- Update documentation links in project-template README
2026-02-13 17:52:03 +01:00