Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
370 commits
Select commit Hold shift + click to select a range
3618f3c
[Phase 10D-polish + Phase 11] Briefs — ListView polish + 5 catalog wi…
crimson-knight May 27, 2026
1acdb6e
[Phase 10D-extensions] Brief v1 — charts + activity rings + map + sea…
crimson-knight May 27, 2026
a432e82
[Phase 10D / Phase 11] Widget-demonstration social contract — rubric …
crimson-knight May 27, 2026
d07b01b
[10D-polish] A: ListView polish defaults — Crystal + Overrides
crimson-knight May 27, 2026
a678806
[10D-polish] A1+A2+A3+A4: ListViewFacade — Mail-style chrome defaults
crimson-knight May 27, 2026
3b83296
[10D-polish] B1+B2+B3+B5: Voyager state + controller — pending-modal …
crimson-knight May 27, 2026
b969e49
[10D-polish] B1+B2+B3+B4+B5: Todos screen wires Alert/ActionSheet/She…
crimson-knight May 27, 2026
1de03fd
[10D-polish] iOS build green — fix strikethrough nilable
crimson-knight May 27, 2026
c91d12f
[10D-polish] Screenshot: 01_drag_handle_visible.png — Mail-style chrome
crimson-knight May 27, 2026
7020afc
[10D-polish] Add 5 polish-* capture scenarios for modal screenshots
crimson-knight May 27, 2026
c80ea7c
[10D-polish] Fix ActionSheet SIGSEGV — token setters use boxed UInt64
crimson-knight May 27, 2026
baece8c
[10D-polish] B4: Fix DatePicker seed crash — Time.utc not Time.local
crimson-knight May 27, 2026
3487e49
[10D-polish] Screenshots: all 5 catalog modals captured
crimson-knight May 27, 2026
2961e7e
[10D-polish] C0: Per-widget usage docs for 6 widgets
crimson-knight May 27, 2026
f367734
[10D-polish] C: intent-catalog + intent-backlog updates
crimson-knight May 27, 2026
e8df589
[10D-polish] C: hand-test guide phase-10-d-polish-handtest.md
crimson-knight May 27, 2026
387a590
[10D-polish polish] Fix DatePicker year offset (3995→2026) + force po…
crimson-knight May 28, 2026
772a323
[10D-polish iter2] B-ACTIONSHEET-MULTI-ACTION — ForEach actions in Co…
crimson-knight May 28, 2026
1b8619a
[10D-polish iter2] B-SHEET-INTERACTIVE-DISMISS-DISABLED — block drag-…
crimson-knight May 28, 2026
45a2ebd
[10D-polish iter2] B-DATEPICKER-STYLE-PROPERTY — compact/graphical/wh…
crimson-knight May 28, 2026
0802efa
[10D-polish iter2] B-LIST-SWIPE-TINT + B-LIST-SWIPE-LABEL-STYLE — per…
crimson-knight May 28, 2026
9e3001c
[10D-polish iter2] B-POPOVER-ANCHOR-VIEW — anchor popover to source v…
crimson-knight May 28, 2026
264f81d
[10D-polish iter2] iOS build green — all 5 fixes compile cleanly
crimson-knight May 28, 2026
3ec3e0c
[10D-polish iter2] Docs: update usage docs + intent-catalog + intent-…
crimson-knight May 28, 2026
bab1e9f
[10D-polish iter2] Screenshots: 5 iter-2 fix proofs captured
crimson-knight May 28, 2026
2cf0191
[10D-polish iter2] Docs: clear confirmation_dialog override_path_status
crimson-knight May 28, 2026
c24eed1
[Merge-readiness gate] v2 gate + lifecycle contract + harness design …
crimson-knight May 28, 2026
9d097d5
[Merge-readiness gate] Catalog coverage manifest + validator + demo-a…
crimson-knight May 28, 2026
60ed96e
[Phase 12.A] Interaction-contracts harness foundation
crimson-knight May 28, 2026
b599e24
[Phase 12.A] Marker instrumentation: ConfirmationDialog + BoolStorage
crimson-knight May 28, 2026
bcefba3
[Phase 12.A] Extend marker instrumentation + V2 spec
crimson-knight May 28, 2026
6d6cec1
[Phase 12.A] Fix Voyager bundle ID + add capture_tap_coordinates.sh
crimson-knight May 28, 2026
8a54f18
[Phase 12.A] Architect session summary — 2026-05-28
crimson-knight May 28, 2026
f3a1264
[Phase 12.A] Codex review response — fix 3 BLOCKERs + 7 CONCERNs + 1 NIT
crimson-knight May 28, 2026
b8d5ff2
[Phase 12.A] Update session summary with Wave 6 (Codex response) + ve…
crimson-knight May 28, 2026
52b15b3
[Phase 12.A] Codex verification pass — fix 1 BLOCKER NEW + 4 NOT CLEA…
crimson-knight May 28, 2026
0494752
[Phase 12.A] Final session summary — 10 commits, two Codex loops closed
crimson-knight May 28, 2026
449336c
[Phase 12.B] Sheet write-side markers + host-teardown probe
crimson-knight May 28, 2026
45dbfe8
[Phase 12.B] Worktree merge plan + dispatch Codex
crimson-knight May 28, 2026
b9d56e6
[Phase 12.B] Fix apicIntentionalDismiss + reverse XCUITest-tap claim
crimson-knight May 28, 2026
7271464
[Phase 12.B] Fix hallucinated simctl io touch tap — use cliclick c
crimson-knight May 28, 2026
aa170a0
[Phase 12.B] Merge phase-10-d-polish — V1/V2 target code lands
crimson-knight May 28, 2026
f3626f8
[Phase 12.B] Update V1/V2 specs to use real polish IDs
crimson-knight May 28, 2026
88908bd
[Phase 12.B] Codex review-v2 response — fix 4 of 5 BLOCKERs
crimson-knight May 28, 2026
6a5e4d1
[Phase 12.C] V1 lifecycle fix — Path A (renderer sweep) + Path B (exp…
crimson-knight May 29, 2026
34a87c7
[Phase 12.C] Codex iter-1 response — identity-aware sweep + Confirmat…
crimson-knight May 29, 2026
9d681e2
[Phase 12.C] Codex iter-2 response — wrapper-identity propagation + d…
crimson-knight May 29, 2026
085ac7f
[Phase 12.C] Hand-test playbook for V1 fix verification
crimson-knight May 29, 2026
ae5a950
[Phase 12.C] Codex iter-3 response — AppKit identity propagation + Po…
crimson-knight May 29, 2026
c8a12ef
[Phase 12.C iter-4] V1 fix Option A — NativeView reuse across rerenders
crimson-knight May 29, 2026
28fec39
feat(voyager): Component Gallery catalog + live-interaction wiring
crimson-knight May 29, 2026
67762ab
feat(voyager): wire all showcase gallery widgets to a live readout
crimson-knight May 29, 2026
5e8d218
test(voyager): cover Slider + Stepper interaction (Float64 callback p…
crimson-knight May 29, 2026
0a93a6f
feat(voyager): add More Controls & Display gallery section
crimson-knight May 29, 2026
d6850b5
docs(voyager): document action-sheet popover/Cancel investigation
crimson-knight May 29, 2026
996ad1d
fix(voyager): pin macOS SwiftKit lib to arch-specific build path
crimson-knight May 29, 2026
b4ad651
docs(voyager): disprove trait hypothesis for action-sheet Cancel
crimson-knight May 29, 2026
10d1373
fix(voyager): share action sheet renders as a real bottom sheet with …
crimson-knight May 29, 2026
30d8efd
feat(voyager): add Layout + Data & Indicators gallery sections
crimson-knight May 29, 2026
acbe2ab
fix(voyager): reserve ZStack height; omit Canvas/PathView (iOS render…
crimson-knight May 29, 2026
748911b
fix(voyager): macOS host wraps content in a scrolling NSScrollView
crimson-knight May 30, 2026
ae7fe58
feat(ui): implement PathView drawing on iOS (CAShapeLayer)
crimson-knight May 30, 2026
b0e6fb0
feat(ui): implement Canvas drawing on iOS (op-stream replay)
crimson-knight May 30, 2026
48b4bc6
fix(ui): SecureField now delivers typed password to FormState
crimson-knight May 31, 2026
bcb7018
docs: ingrain interactive-view Definition of Done in project spec
crimson-knight May 31, 2026
14f67f0
fix(ui): SearchField/TextArea/TextEditor deliver typed text to handler
crimson-knight May 31, 2026
4c18f54
feat(ui): reconciliation scaffold — distinguish Navigation vs Rerender
crimson-knight May 31, 2026
389632c
feat(ui): in-place reconciliation preserves text-field focus on Rerender
crimson-knight May 31, 2026
c5980c0
fix(ui): TabView + ColorPicker deliver real values to handlers
crimson-knight May 31, 2026
8e70a02
docs: consumer cross-platform integration guide
crimson-knight May 31, 2026
53fb8a1
fix(ui): ComboBox on_change delivers typed value to handler (iOS)
crimson-knight May 31, 2026
30637b7
fix(ui): wire ComboBox on_change on macOS (NSComboBox delegate)
crimson-knight Jun 1, 2026
25c75f5
fix(voyager): rebuild macOS SwiftKit on Swift source change (gallery …
crimson-knight Jun 1, 2026
282358d
fix(voyager): macOS host relinks on any required Crystal source change
crimson-knight Jun 1, 2026
5bc3d00
test(macos): Voyager AX behavior + accessibility suite (gated on disp…
crimson-knight Jun 1, 2026
03716cd
test(macos): Voyager AX suite passes green in a GUI session (4/4)
crimson-knight Jun 1, 2026
1a67799
feat(cross-platform): capability+usability guide, watchOS brief, usab…
crimson-knight Jun 1, 2026
5171d72
test(macos): reframe Sheet behavior spec to proven AX-observable outc…
crimson-knight Jun 1, 2026
4a6540c
feat(watchos): UI::Complication type-safety rail + cross-platform com…
crimson-knight Jun 1, 2026
4a8032a
feat(voyager): demonstrate ComplicationWithWebFallback in gallery; AX…
crimson-knight Jun 1, 2026
ef5c205
docs: Complication moves greenfield -> model+gate shipped (renderer P…
crimson-knight Jun 1, 2026
aef978a
feat(ui): verify iOS file-picker + animate rerender-mounted sheet pre…
crimson-knight Jun 1, 2026
0de235e
test(macos): motion-evidence triptych proving the sheet present animates
crimson-knight Jun 1, 2026
2aa4bed
docs(phase-12): preflight finding — SwiftKit facade layer is UIKit/Ap…
crimson-knight Jun 1, 2026
140cc9d
test(web): green the suite — update stale tests + fix android swipe t…
crimson-knight Jun 1, 2026
8dd659f
docs(arch): foundational output + layout model (SwiftUI-canonical + F…
crimson-knight Jun 1, 2026
8d26e01
docs(arch): incorporate Codex review — foundation now safe to build on
crimson-knight Jun 1, 2026
b45ea15
feat(layout): Phase B step 1 — UI::Fluid native (points) resolution
crimson-knight Jun 1, 2026
d20daa0
feat(layout): Phase B step 2 — native UI::Fluid resizable range (PROV…
crimson-knight Jun 1, 2026
39f2ba4
feat(voyager): migrate sign-in to a resizable UI::Fluid readable colu…
crimson-knight Jun 1, 2026
7cf9d78
revert(voyager): sign-in fluid migration — facade leaf controls don't…
crimson-knight Jun 1, 2026
9af665b
feat(layout): fluid-container leaf-fill primitive (building block; ne…
crimson-knight Jun 1, 2026
7575153
feat(layout): objc_constrain_fluid_width (cap works); document NSStac…
crimson-knight Jun 1, 2026
f73cba6
feat(voyager): Phase C — intentionally-designed Welcome/About screen …
crimson-knight Jun 1, 2026
25f9460
fix(layout): remove fluid false-pass — native fluid containers collap…
crimson-knight Jun 1, 2026
f92db8c
feat(watchos): declare .watchOS package platform + real facade-bucket…
crimson-knight Jun 1, 2026
a3050b0
feat(watchos): fix the 2 root blockers — APSKPlatformView box + host(…
crimson-knight Jun 1, 2026
8cc2f85
feat(watchos): fix the shared modifiers (CommonModifiers + MaterialSe…
crimson-knight Jun 1, 2026
2f5fc00
feat(watchos): GREEN watchOS compile — gate watch-specific facade bucket
crimson-knight Jun 1, 2026
cd2d96d
docs(phase-12): Crystal toolchain blocker — crystal-alpha can't targe…
crimson-knight Jun 1, 2026
44041dd
feat(watchos): Crystal UI lib now CROSS-COMPILES for watchOS
crimson-knight Jun 1, 2026
05eb446
docs(phase-12): mark Crystal watchOS cross-compile RESOLVED + record …
crimson-knight Jun 1, 2026
bcf1f46
docs(arch): Track 2 adaptive-layout plan (Codex-validated) — resize→r…
crimson-knight Jun 1, 2026
6bf21e1
fix(macos): size class reads active-window content rect, not nil main…
crimson-knight Jun 1, 2026
bfc9d33
docs(arch): mark Track 2 step 1 metric-contract half LANDED; live-res…
crimson-knight Jun 1, 2026
835676f
feat(ui): DeviceMetrics#responsive primitive — whole sign-in composit…
crimson-knight Jun 1, 2026
c60db0b
docs(arch): correct stretch misread; record responsive primitive (ste…
crimson-knight Jun 1, 2026
ee73a63
feat(macos): live window-resize -> rebuild (the design adapts on drag)
crimson-knight Jun 1, 2026
cea1d28
docs(arch): mark Track 2 live-resize half LANDED (proven headless)
crimson-knight Jun 1, 2026
4a1e59c
feat(voyager): Phase C welcome demo fully adaptive + reflow proof
crimson-knight Jun 1, 2026
1c6b655
docs(arch): Track 2 core COMPLETE on macOS; Phase C reflow proof reco…
crimson-knight Jun 1, 2026
09b1457
feat(watchos): APSKWatchHostView satisfies full bridge contract (kind…
crimson-knight Jun 2, 2026
1378653
docs(watchos): facade-bucket audit — 18/40 reachable, 22 compiled out
crimson-knight Jun 2, 2026
93fa204
feat(watchos): port TextField facade to watch (Bucket-2 P0)
crimson-knight Jun 2, 2026
334dd8c
feat(watchos): port ListView facade to watch (Bucket-2 P0)
crimson-knight Jun 2, 2026
db6722c
feat(watchos): port Sheet facade to watch (Bucket-2 P0 — completes al…
crimson-knight Jun 2, 2026
b051bc4
feat(watchos): port SecureField + TabView facades to watch (Bucket-2 …
crimson-knight Jun 2, 2026
7b67c3c
docs(watchos): diagnose Crystal watch cross-compile blocker to exact …
crimson-knight Jun 2, 2026
2490fa0
feat(voyager): todos screen fully adaptive via DeviceMetrics#responsi…
crimson-knight Jun 2, 2026
f4f774b
feat(voyager): settings + phase-10 hub fully adaptive + pin text to c…
crimson-knight Jun 2, 2026
4746447
feat(voyager): todo editor fully adaptive via DeviceMetrics#responsiv…
crimson-knight Jun 2, 2026
1e3a1ec
feat(voyager-ios): rebuild-on-size-change hook (iOS analog of macOS w…
crimson-knight Jun 2, 2026
5ba9f65
feat(voyager): sign-in adapts on the VERTICAL axis too (responsive_ve…
crimson-knight Jun 2, 2026
1026ff0
feat(voyager): vertical-axis adapt across todos/settings/hub/editor (…
crimson-knight Jun 2, 2026
00d1a03
feat(voyager): welcome adapts on the vertical axis too (responsive_ve…
crimson-knight Jun 2, 2026
185893a
feat(voyager): component gallery adaptive (responsive + responsive_ve…
crimson-knight Jun 2, 2026
ba7a68f
docs(voyager-ios): visual proof — app rebuilds + reflows live on devi…
crimson-knight Jun 2, 2026
72deb27
feat(watchos): port Toggle facade to watch (the hard UIView-bound case)
crimson-knight Jun 2, 2026
60ca426
feat(watchos): port Slider + Stepper facades to watch (Bucket-2 P2)
crimson-knight Jun 2, 2026
d02e55f
feat(watchos): port Picker + DatePicker + TimePicker — Bucket 2 exhau…
crimson-knight Jun 2, 2026
2b48ee4
feat(watchos): first watchOS app — SwiftKit facade catalog renders on…
crimson-knight Jun 2, 2026
4e5a4cb
feat(watchos): agent-chat surface on the wrist (the watch app's purpose)
crimson-knight Jun 2, 2026
d004731
feat(voyager): agent-chat as a Crystal UI::Screen — same design on ma…
crimson-knight Jun 2, 2026
16420ba
polish(voyager): agent-chat reads like a real messaging app (pack + w…
crimson-knight Jun 2, 2026
227d7cd
feat(voyager): reach Agent chat from the live app (Todos header -> :a…
crimson-knight Jun 2, 2026
a6d783e
fix(voyager): agent-chat renders + navigates on iOS; XCUITest proves …
crimson-knight Jun 2, 2026
e523b3c
fix(swiftkit): UI::IconButton no longer crashes on iOS (explicit tap …
crimson-knight Jun 2, 2026
dc23ec2
feat(voyager): agent-chat Send works — type a reply, it appends to th…
crimson-knight Jun 2, 2026
52beed0
feat(watchos): watch agent-chat cohesive with iOS + interactive Send
crimson-knight Jun 2, 2026
367236e
docs(dark-mode): record dark-mode gap — native app renders light when…
crimson-knight Jun 2, 2026
c955dda
fix(voyager-ios): follow system Dark Mode by default — the kit alread…
crimson-knight Jun 2, 2026
8937262
fix(voyager-macos): sync HIG_APPEARANCE so VOYAGER_APPEARANCE=dark ba…
crimson-knight Jun 2, 2026
d1e66b9
feat(watchos): gate SwiftKit bridge + sender on :watchos (WatchKit re…
crimson-knight Jun 2, 2026
822a8df
feat(watchos): SwiftKit watch stack-compose plumbing (renderer brick 2)
crimson-knight Jun 2, 2026
e4dd439
feat(watchos): UI::WatchKit::Renderer skeleton (renderer brick 3)
crimson-knight Jun 2, 2026
86252e2
feat(watchos): wire Button/Image/TextField to real facades in WatchKi…
crimson-knight Jun 2, 2026
6679de9
feat(watchos): Crystal watch bridge — voyager_watch_render entry (bri…
crimson-knight Jun 2, 2026
8af4f88
docs(watchos): scope brick 4b (on-device render) — runtime-dep cross-…
crimson-knight Jun 2, 2026
909ca21
docs(watchos): brick 4b GC blocker SOLVED — single-threaded libgc recipe
crimson-knight Jun 2, 2026
2c83ffa
feat(watchos): watch build_crystal_lib.sh — produces libvoyagerwatch.…
crimson-knight Jun 2, 2026
96d3f34
fix(watchos): skip reactive/integration/chat/live_search requires on …
crimson-knight Jun 2, 2026
349a06a
feat(watchos): brick 4b — Crystal-authored screen renders on watch sim
crimson-knight Jun 2, 2026
db05cf8
feat(watchos): wire Card/Surface/Divider/Spacer/Toggle/Slider/Stepper…
crimson-knight Jun 2, 2026
cbc4fbe
feat(watchos): wire SecureField + Picker facades; keep SegmentedContr…
crimson-knight Jun 2, 2026
49b2555
feat(watchos): render the SHARED AgentChatScreen on watch — one Cryst…
crimson-knight Jun 2, 2026
c1eb02c
fix(watchos): wrap watch content in a ScrollView so multi-line labels…
crimson-knight Jun 2, 2026
ee68f33
feat(watchos): reactive re-render loop — Crystal state changes update…
crimson-knight Jun 2, 2026
0a4d540
test(voyager): verify shared AgentChatScreen on iOS — cohesion + real…
crimson-knight Jun 2, 2026
247852b
docs(voyager): macOS agent-chat capture — three-platform cohesion com…
crimson-knight Jun 2, 2026
b0751a7
feat(ui): cross-platform fill_horizontal (flex-grow) primitive; fixes…
crimson-knight Jun 2, 2026
e1f300f
feat(ui): shared adaptive_content_width helper — every screen reflows…
crimson-knight Jun 2, 2026
846a039
feat(voyager): adopt adaptive_content_width across flow screens — sig…
crimson-knight Jun 2, 2026
e7e3e5d
feat(ui): compact_canvas? reflow signal — todos header stacks on the …
crimson-knight Jun 2, 2026
90da562
feat(voyager): todos header — large title + SF-Symbol toolbar (icons)…
crimson-knight Jun 2, 2026
d7504a8
docs(voyager): cross-platform cohesion gallery — one screen catalog, …
crimson-knight Jun 2, 2026
6de27b8
docs(voyager): verify + demonstrate dark-mode cohesion across macOS/i…
crimson-knight Jun 2, 2026
fda1a76
docs: adaptive-layout patterns guide — the reusable recipe for one sc…
crimson-knight Jun 2, 2026
6def8c4
feat(voyager): Daily Check-in screen — control-rich surface (Slider/S…
crimson-knight Jun 2, 2026
cf94b78
feat(voyager): add Focus-area Picker to Daily Check-in; fix iOS/watch…
crimson-knight Jun 2, 2026
b318b6f
style(voyager): format state.cr (blank line after checkin_focuses met…
crimson-knight Jun 2, 2026
fdf130d
docs(voyager): refresh cohesion gallery to 3 rows (agent-chat, todos …
crimson-knight Jun 2, 2026
48c7df5
feat(watchos): prove the watch is a navigable multi-screen app (Navig…
crimson-knight Jun 2, 2026
053f23a
docs(voyager): macOS window-resize adaptation artifact (design reflow…
crimson-knight Jun 2, 2026
18906ac
test(voyager): verify check-in controls render correctly in dark (fin…
crimson-knight Jun 2, 2026
264d39f
docs(voyager): add top-level sample README — the cross-platform demo,…
crimson-knight Jun 2, 2026
a8e5bce
feat(voyager): check-in reminder schedules a real local notification …
crimson-knight Jun 2, 2026
e843450
feat(voyager): the agent reaches you on the WRIST — watchOS local not…
crimson-knight Jun 2, 2026
ededd0a
feat(voyager): the agent SPEAKS its reply — UI::Speech TTS on macOS/i…
crimson-knight Jun 2, 2026
ec77244
feat(voyager): cohesion loop — agent notification fires foreground, a…
crimson-knight Jun 2, 2026
d800de5
feat(voyager): voice mute/unmute control in the Agent Chat header (ve…
crimson-knight Jun 2, 2026
b1b8ef5
feat(voyager): UI::Preferences — settings persist across relaunch (ve…
crimson-knight Jun 2, 2026
d0e6bce
docs(agent-comms): document UI::Notifications/Speech/Preferences capa…
crimson-knight Jun 2, 2026
df3a987
test(voyager): skip testCaptureMatrix when its evidence-dir env is unset
crimson-knight Jun 2, 2026
e4a9d02
feat(voyager): Voice speed preference (Slider -> UI::Speech rate, per…
crimson-knight Jun 2, 2026
143ab5e
docs(agent-comms): macOS render verification of the new voice UI (App…
crimson-knight Jun 2, 2026
02cc421
feat(voyager): lightly-contextual, varied agent replies (chat feels a…
crimson-knight Jun 2, 2026
0a0e1c4
fix(notifications): no-op (not crash) when the process has no app bundle
crimson-knight Jun 2, 2026
cf83c67
fix(voyager): editor deadline DatePicker -> TextField (fixes iOS edit…
crimson-knight Jun 3, 2026
1254353
test(voyager): isolate DatePicker crash to UI::Sheet hosting (pushed-…
crimson-knight Jun 3, 2026
01ecb1f
fix(voyager): Todos deadline rendering crashed on iOS (class-init gap…
crimson-knight Jun 3, 2026
045352b
fix(macos): window opened at screen width and locked horizontal resize
crimson-knight Jun 3, 2026
588b8df
docs(view): root_fill on macOS is a soft cap + host-pinned fill, not …
crimson-knight Jun 3, 2026
4951e15
fix(ios): overflow Popover anchored to its source view, not a bottom …
crimson-knight Jun 3, 2026
e1c1996
docs: macOS/iOS consumer setup must link the SwiftKit bridge + Swift lib
crimson-knight Jun 3, 2026
773750e
fix(macos): background-less VStack must be transparent in the live app
crimson-knight Jun 3, 2026
adb9337
fix(macos): detect capture mode by screenshot-path env, not HIG_APPEA…
crimson-knight Jun 3, 2026
8d84a61
fix(macos): UI::Toggle renders as a switch, not a checkbox
crimson-knight Jun 3, 2026
05ca64d
fix(macos): empty TextField/SecureField no longer collapses to a sliver
crimson-knight Jun 3, 2026
986c825
fix(macos): TextArea now renders its placeholder
crimson-knight Jun 3, 2026
05866da
fix(macos): default UI::Image is resizable (Fit) so it can be sized
crimson-knight Jun 3, 2026
a09aa60
feat(macos/ios): UI::Image loads absolute file paths (not just bundle…
crimson-knight Jun 3, 2026
8c7f8d4
fix(macos): ZStack children fill the container (Auto-Layout edge-pin)
crimson-knight Jun 3, 2026
c30491e
feat(macos/ios): custom fonts — runtime TTF registration + Label font…
crimson-knight Jun 3, 2026
fdee327
fix(macos/ios): UI::Button forwards font (size/weight/family) to the …
crimson-knight Jun 3, 2026
226003a
feat(macos/ios): UI::IconButton renders custom file-path/asset images…
crimson-knight Jun 3, 2026
5071a68
fix(macos): UI::Spacer actually expands in an HStack (center idiom wo…
crimson-knight Jun 3, 2026
34fb2b9
fix(macos): VStack child with fill_horizontal becomes full-bleed (mat…
crimson-knight Jun 3, 2026
772b2ed
fix(macos/ios): UI::TextField + UI::TextArea forward font (size/weigh…
crimson-knight Jun 3, 2026
3234dfd
fix(macos/ios): UI::SecureField forwards font (size/weight/family)
crimson-knight Jun 3, 2026
013e42a
fix(macos/ios): fill_horizontal Label fills + aligns text (no more ce…
crimson-knight Jun 4, 2026
cd90484
fix(macos): VStack fill_horizontal children respect the stack's horiz…
crimson-knight Jun 4, 2026
fdf572d
fix(macos/ios): fill_horizontal Button leading-aligns its label
crimson-knight Jun 4, 2026
afb3363
fix(macos/ios): UI::Button seeds foreground_color at construction
crimson-knight Jun 4, 2026
8479264
feat(macos): UI::HStack#fill_equally — equal-width cells (tab bar / b…
crimson-knight Jun 4, 2026
fd2c5a8
fix(macos/ios): fill_horizontal Label wraps multi-line text (no more …
crimson-knight Jun 4, 2026
76ec5a6
feat(macos/ios): UI::TextField underline style (bottom-rule only)
crimson-knight Jun 4, 2026
50e4025
TextField: add placeholder_color override
crimson-knight Jun 4, 2026
8703940
Slider: apply tint_color via .tint() so the track/thumb actually color
crimson-knight Jun 4, 2026
a9539bd
Button: stroke the border with the button's own corner radius
crimson-knight Jun 4, 2026
e51835e
Label: assigning text_color clears the semantic role (footgun fix)
crimson-knight Jun 4, 2026
c428c50
Button: number_of_lines for wrapping content labels (default 1 = sing…
crimson-knight Jun 4, 2026
27b6255
ScrollView (macOS): fill the viewport when content is shorter than it
crimson-knight Jun 4, 2026
c1615c3
Label: wire preferred_max_layout_width to fix wrapping-height under-r…
crimson-knight Jun 4, 2026
1bfa254
ScrollView (macOS): transparent background to layer over content
crimson-knight Jun 4, 2026
14717ff
IconButton: add `bordered` for a bare, chrome-free tappable icon
crimson-knight Jun 4, 2026
ca70dd9
Button: fill_horizontal background fills the width (not just the label)
crimson-knight Jun 4, 2026
1aadec2
ZStack (macOS): honor alignment for directional cases (fixed-size ali…
crimson-knight Jun 4, 2026
517da33
native: implement macOS ObjC collection bridge (close test-macos link…
crimson-knight Jun 4, 2026
4ee96fd
spec(native_macos): ax_geometry pends on degenerate (headless) window…
crimson-knight Jun 4, 2026
8ce6427
ax_test: App#find* expose max_depth (bound searches over large foreig…
crimson-knight Jun 4, 2026
e92b135
spec(native_macos): fix SHARD_ROOT depth so the visual probe finds hi…
crimson-knight Jun 4, 2026
aa7a8f2
uikit: ZStack children fill parent + Spacer reliably flexes (appkit p…
crimson-knight Jun 6, 2026
5a474b9
uikit: VStack child with fill_horizontal fills the stack width (appki…
crimson-knight Jun 9, 2026
2c0648c
feat(ui): discrete gesture surface — swipe (4 dirs) + long-press on U…
crimson-knight Jun 9, 2026
650bd24
feat(ui): continuing-presentation reuse — sheets survive a destructiv…
crimson-knight Jun 9, 2026
d57f615
feat(ui): IconButton non-square cover-crop via icon_width/icon_height
crimson-knight Jun 9, 2026
ded6af3
fix(uikit): cure the HStack "50/50 layout disease" + button padding i…
crimson-knight Jun 9, 2026
9ff6673
web_renderer: base reset (no body margin, no Times fallback)
crimson-knight Jun 14, 2026
412229e
web_renderer: fix three generic fidelity bugs that diverged web from …
crimson-knight Jun 14, 2026
1e00284
web_renderer: three generic fidelity fixes (border-box, touch-floor, …
crimson-knight Jun 14, 2026
5afdc63
FormFacade: default to .formStyle(.grouped) on macOS
crimson-knight Jul 2, 2026
7a721d8
AppKit renderer: honor View corner_radius + border on any layer-backe…
crimson-knight Jul 19, 2026
1a3b0c7
facade: ScrollView fill_vertical + scroll-to-bottom / at-bottom (macOS)
crimson-knight Jul 20, 2026
7a24622
facade: TextField#on_submit — Enter-to-send (SwiftUI .onSubmit)
crimson-knight Jul 20, 2026
8eb98c7
views/renderers: touch_passthrough, AsyncImage preloaded bytes, iOS f…
crimson-knight Jul 23, 2026
5f63d9a
fix(uikit): render Card as raw UIKit so interactive children receive …
crimson-knight Jul 24, 2026
115befd
Add deterministic static asset compiler
crimson-knight Aug 12, 2026
0d5fe2e
Merge SafeHTML release line before static asset release
crimson-knight Aug 12, 2026
6765988
Prepare asset_pipeline 0.37.0
crimson-knight Aug 12, 2026
99d07a1
Run core web tests on supported platforms
crimson-knight Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
89 changes: 89 additions & 0 deletions .claude/skills/apple-platform-guide/usage/action-sheet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# UI::ActionSheet

A modal sheet of choices presented at the bottom of the screen on iOS. **Tier 3 (iOS-gated).** Cross-platform code uses `UI::ActionSheetWithWebFallback` which delegates to the iOS gated class on `-Dios` builds and an accessible web fallback elsewhere.

## Default experience

- **iOS:** Routes through SwiftKit's `ConfirmationDialogFacade` which uses SwiftUI's `.confirmationDialog(titleKey:isPresented:titleVisibility:actions:message:)`. **Phase 10D-polish iter 2 (2026-05-27)** extended the facade to take parallel `actionLabels` / `actionStyles` / `actionTokens` arrays so the iOS surface honors any number of actions. The cancel-style button is automatically pinned at the bottom by SwiftUI; destructive-style buttons render in red.
- **iPadOS:** Same as iOS but presented as a popover anchored to the source view by SwiftUI default.
- **macOS:** `NSAlert(alertStyle: .informational)` with the actions as alert buttons (system-drawn).
- **web (wide / narrow):** Use `UI::ActionSheetWithWebFallback`. Wide → modal centered card; narrow → bottom-anchored sheet with vanilla-JS swipe-to-dismiss.
- **Android:** Material `BottomSheetDialogFragment`.

## Crystal API

```crystal
# Minimal invocation (iOS only — Tier 3 gate)
sheet = UI::ActionSheet.new("Title", "Optional message")
sheet.add_action("Primary") { do_thing }
sheet.add_action("Cancel", :cancel)
sheet.is_presented = true

# Cross-platform — use the with-fallback wrapper
sheet = UI::ActionSheetWithWebFallback.new("Title", "Optional message")
sheet.add_action("Primary") { do_thing }
sheet.add_action("Cancel", :cancel)
sheet.is_presented = true
```

```crystal
# Realistic Voyager invocation
# samples/initiative-cross-platform-ui-voyager/screens/todos_screen.cr:249-275
if share_id = state.pending_share_todo_id
target = state.find_todo(share_id)
sheet_title = target ? "Share \"#{target.title}\"" : "Share todo"
share_sheet = UI::ActionSheetWithWebFallback.new(sheet_title, "Choose how to share this todo.")
share_sheet.add_action("Copy to Clipboard") {
Voyager.dispatch(:copy_pending_share)
nil
}
share_sheet.add_action("Print This Todo") {
Voyager.dispatch(:print_pending_share)
nil
}
share_sheet.add_action("Cancel", :cancel) {
Voyager.dispatch(:cancel_pending)
nil
}
share_sheet.is_presented = true
share_sheet.test_id = "voyager-todos-share-sheet"
share_sheet.accessibility_label = "Share options"
root << share_sheet.as(UI::View)
end
```

## Behavior contract

- **Callbacks:** Each `Action#action : Proc(Nil)?` fires when the user taps that button. **Phase 10D-polish iter 2:** all actions fire (no longer dropped at the binary-degradation boundary).
- **Dismissal paths:**
- Tap action → dismisses, fires the chosen action's Proc.
- Tap Cancel → dismisses, fires cancel action (if any).
- iOS swipe-down from sheet handle → fires no action; the screen is responsible for clearing the pending flag.
- **Focus / keyboard:** System-managed. Destructive style highlights the action red (iOS).
- **Reduced motion:** SwiftUI handles automatically.
- **Reactivity:** `is_presented` is a plain property today (not reactive like `UI::Sheet`); the Voyager pattern is to mutate state + Rerender, which produces a new ActionSheet view tree.

## Customization knobs

| Property | Type | Default | Effect |
|----------|------|---------|--------|
| `title` | `String` | `""` | Sheet title. Empty hides the title row. |
| `message` | `String` | `""` | Secondary message. Empty hides the message row. |
| `actions` | `Array(Action)` | `[]` | Action list. Each carries `label`, `style` (`:default` / `:destructive` / `:cancel`), and optional `Proc(Nil)?`. |
| `is_presented` | `Bool` | `false` | Modal visibility. |

## Override path

**Default chrome is system-resolved.** ActionSheet inherits SwiftUI's `.confirmationDialog` chrome which is system-drawn.

**Override paths:**

- **Multi-action support:** RESOLVED by Phase 10D-polish iter 2 (`B-ACTIONSHEET-MULTI-ACTION`). The iOS facade now iterates `view.actions` natively; all actions render and fire callbacks. The action list can be any length; SwiftUI handles layout.
- **Material override:** ActionSheet does not support material override (system-drawn chrome by HIG mandate).
- **Tier 3 gating:** building without `-Dios` fails compile at any `UI::ActionSheet.new(...)` site. Use `UI::ActionSheetWithWebFallback` for cross-platform code.

## Evidence

- **Canonical example:** `samples/initiative-cross-platform-ui-voyager/screens/todos_screen.cr:249-275`
- **Screenshot:** `docs/initiative-cross-platform-ui/handoff/phase-10-d-polish-screenshots/04_actionsheet_share.png`
- **Spec coverage:** `spec/web/ui/views/action_sheet_with_web_fallback_spec.cr`
83 changes: 83 additions & 0 deletions .claude/skills/apple-platform-guide/usage/alert.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# UI::Alert

A modal alert dialog for critical confirmations — title, message, and a small set of role-differentiated action buttons. SwiftUI's `.alert(...)` is fully system-drawn; the framework forwards the title + message + buttons and lets the platform paint the chrome.

## Default experience

- **iOS:** SwiftUI `.alert(title, isPresented:, actions:, message:)`. System-drawn modal card centered over the host scene, blurred backdrop, vibrant title, secondary message, role-coloured buttons (destructive = red, cancel = bold/blue). Tapping any button dismisses the alert.
- **iPadOS:** identical to iOS.
- **macOS:** AppKit visit emits `NSAlert` (system-drawn modal sheet attached to the host window). Destructive role uses NSAlert critical style.
- **web wide:** HTML `<dialog>` overlay with backdrop; framework-rendered (no SwiftUI). Role buttons styled via `.--destructive` / `.--cancel` CSS classes.
- **web narrow:** same as web wide.
- **Android:** Material `AlertDialog`.

## Crystal API

```crystal
# Minimal invocation
alert = UI::Alert.new("Delete?", "This action cannot be undone.")
alert.add_button("Cancel", :cancel)
alert.add_button("Delete", :destructive) { state.delete }
alert.is_presented = true
```

```crystal
# Realistic Voyager invocation
# samples/initiative-cross-platform-ui-voyager/screens/todos_screen.cr:225-247
if delete_id = state.pending_delete_todo_id
target = state.find_todo(delete_id)
title_text = target ? "Delete \"#{target.title}\"?" : "Delete this todo?"
alert = UI::Alert.new(title_text, "This can't be undone.")
alert.add_button("Cancel", :cancel) {
Voyager.dispatch(:cancel_pending)
nil
}
alert.add_button("Delete", :destructive) {
Voyager.dispatch(:confirm_delete)
nil
}
alert.is_presented = true
alert.test_id = "voyager-todos-delete-alert"
alert.accessibility_label = "Confirm delete"
root << alert.as(UI::View)
end
```

## Behavior contract

- **Callbacks:** Each `AlertButton#action : Proc(Nil)?` fires synchronously when the user taps the corresponding button. No callback fires on system-dismiss (escape on macOS, tap-outside on web); the convention is to wire dismiss-equivalent to the Cancel button.
- **Dismissal paths:**
- Button tap → dismisses, fires action.
- macOS escape key → triggers the `:cancel`-roled button if any; else dismisses without action.
- iOS swipe-to-dismiss NOT applicable (system alerts are modal-only).
- **Focus / keyboard:** System-managed. Default focus on the trailing button (typically destructive or confirmation) per HIG.
- **Reduced motion:** SwiftUI / AppKit handle reduced motion automatically; alerts already use minimal animation.
- **Reactivity:** `is_presented=` is reactive — the property setter pushes through the SwiftKit bridge so post-render mutation drives the alert presentation. The Voyager pattern: mutate `state.pending_delete_todo_id` + dispatch Rerender → the screen's `build(ctx)` re-emits the alert with `is_presented = true`.

## Customization knobs

| Property | Type | Default | Effect |
|----------|------|---------|--------|
| `title` | `String` | (required) | Bold title. |
| `message` | `String` | `""` | Secondary message. Empty = no message line. |
| `buttons` | `Array(AlertButton)` | `[]` | Action buttons (label + style symbol + optional `Proc(Nil)?`). |
| `is_presented` | `Bool` | `false` | Modal visibility. Reactive setter. |
| `material_semantic` | `Symbol?` | `nil` | Per HIG, alert chrome is `:system_resolved` — this property is inert on the active SwiftUI `.alert` path; preserved for cross-platform symmetry. |

`AlertButton#style` accepts `:default`, `:cancel`, `:destructive`. SwiftUI maps these to `ButtonRole.destructive` / `.cancel` / nil.

## Override path

**Default chrome is system-resolved.** Apple HIG explicitly recommends letting the system paint alert chrome — overriding is generally inappropriate.

**Override paths:**

- **Button order:** SwiftUI controls placement based on role. To force a specific order, ship buttons in the order you want (the framework preserves the array order).
- **Custom backdrop / colors:** NOT supported. The `material_semantic` property is preserved for API symmetry but does nothing on the active `.alert` path (documented in `swift/AssetPipelineSwiftKit/Sources/AssetPipelineSwiftKit/Facades/AlertFacade.swift:7-15`). If a brand needs custom alert chrome, use `UI::Sheet` with `surface_style: :grouped_card` and a custom button row instead. **Backlog item: `B-ALERT-CUSTOM-CHROME`** — `docs/initiative-cross-platform-ui/architecture/intent-backlog.md`.
- **Confirmation alerts with destructive default focus:** ship the destructive button last; SwiftUI auto-focuses the trailing action.

## Evidence

- **Canonical example:** `samples/initiative-cross-platform-ui-voyager/screens/todos_screen.cr:225-247`
- **Screenshot:** `docs/initiative-cross-platform-ui/handoff/phase-10-d-polish-screenshots/03_alert_delete_confirm.png`
- **Spec coverage:** `spec/web/ui/views/alert_spec.cr`
84 changes: 84 additions & 0 deletions .claude/skills/apple-platform-guide/usage/date-picker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# UI::DatePicker

A calendar-based date selection control. Renders as iOS's native `UIDatePicker` chrome (compact field that opens a calendar popover on tap), `NSDatePicker` on macOS, or a native picker on Android. Date / time / datetime modes are selectable via the `mode` property.

## Default experience

- **iOS:** SwiftUI `DatePicker(label, selection: ..., displayedComponents: .date)` — renders as the compact field that taps to reveal a calendar popover (iOS 14+ default). `mode == :time` switches to `.hourAndMinute`; `mode == :datetime` to `[.date, .hourAndMinute]`.
- **iPadOS:** identical to iOS.
- **macOS:** SwiftUI `DatePicker` on macOS renders as a stepper-styled textfield with a calendar-icon affordance. Date-only mode = simple field; datetime mode = field + time stepper.
- **web wide:** HTML `<input type="date" />` / `<input type="time" />` / `<input type="datetime-local" />`.
- **web narrow:** same as web wide (mobile browsers provide native date-pickers automatically).
- **Android:** Material `DatePicker` modal.

## Crystal API

```crystal
# Minimal invocation
picker = UI::DatePicker.new(UI::DatePickerMode::Date)
picker.label = "Deadline"
picker.on_change = ->(t : Time) { ... }
```

```crystal
# Realistic Voyager invocation
# samples/initiative-cross-platform-ui-voyager/screens/todos_screen.cr:391-422
picker = UI::DatePicker.new(UI::DatePickerMode::Date)
picker.label = "Deadline"
picker.accessibility_label = "Todo deadline"
picker.test_id = "voyager-editor-sheet-deadline"
if !seed_deadline.empty?
begin
picker.selected_date = Time.parse_utc(seed_deadline, "%Y-%m-%d")
rescue Time::Format::Error
picker.selected_date = Time.utc
end
else
picker.selected_date = Time.utc
end
picker.on_change = ->(t : Time) {
d2 = Voyager.dispatcher
unless d2.nil?
d2.current_form_state.update("deadline", t.to_utc.to_s("%Y-%m-%d"))
end
nil
}
body << picker.as(UI::View)
```

## Behavior contract

- **Callbacks:** `on_change : Proc(Time, Nil)?` — fires on every value change. iOS may fire multiple times during scrubbing.
- **Dismissal paths:** The popover dismisses on tap-outside or selecting a date (system-managed). The picker itself is inline.
- **Focus / keyboard:** Field is focusable via Tab; keyboard input opens the system date picker dropdown.
- **Reduced motion:** SwiftUI handles automatically.
- **Reactivity:** `selected_date` is NOT reactive today — setting it post-render does not push to the SwiftUI side. To change the displayed date after mount, mutate domain state + Rerender.

## Customization knobs

| Property | Type | Default | Effect |
|----------|------|---------|--------|
| `selected_date` | `Time` | `Time.utc` | Initial / current date. |
| `mode` | `DatePickerMode` | `Date` | `Date` / `Time` / `DateTime`. |
| `minimum_date` | `Time?` | `nil` | Earliest selectable date (inclusive). |
| `maximum_date` | `Time?` | `nil` | Latest selectable date (inclusive). |
| `label` | `String` | `""` | Caption shown alongside the picker. |
| `on_change` | `Proc(Time, Nil)?` | `nil` | Change handler. |
| `style` | `DatePickerStyle` | `Automatic` | **Phase 10D-polish iter 2.** `Automatic` / `Compact` / `Graphical` / `Wheels`. Maps to SwiftUI `.datePickerStyle(.compact)` / `.graphical` / `.wheel`. `Automatic` lets the platform pick (iOS date mode defaults to `.graphical`; we recommend `Compact` for in-form usage so the picker shows as an inline button with a calendar popover). |

## Override path

**Public knobs cover mode + bounds + change handler + style.**

**Override paths:**

- **Picker style (compact vs graphical vs wheels):** RESOLVED by Phase 10D-polish iter 2 (`B-DATEPICKER-STYLE-PROPERTY`). Set `picker.style = UI::DatePickerStyle::Compact` / `Graphical` / `Wheels` / `Automatic`.
- **Nilable / clearable state:** today the picker has no nil value; "no date" must be represented in app state. The Voyager pattern uses a sibling `Clear` button that resets the FormState entry to empty string (`samples/initiative-cross-platform-ui-voyager/screens/todos_screen.cr:432-444`).
- **iOS class-init gap with `Time.local`:** `Time.local` segfaults on iOS due to `[[crystal-ios-class-init-gap]]`. Use `Time.utc` for seed values; date-only mode doesn't care about timezone. Backlog: `B-CRYSTAL-IOS-TIME-LOCAL` (covered by the broader iOS class-init gap remediation).
- **Date display year offset:** the Crystal-to-Swift epoch conversion path (`view.selected_date.to_unix.to_f64` → `Date(timeIntervalSince1970:)`) currently shows an incorrect year (`May 27, 3995` instead of `May 27, 2026` in the editor screenshot). **Backlog item: `B-DATEPICKER-EPOCH-CONVERSION`** — needs investigation of whether Crystal `to_unix` is returning the wrong base, or Swift is misinterpreting the Float64. Tracked in `docs/initiative-cross-platform-ui/architecture/intent-backlog.md`.

## Evidence

- **Canonical example:** `samples/initiative-cross-platform-ui-voyager/screens/todos_screen.cr:391-422`
- **Screenshot:** `docs/initiative-cross-platform-ui/handoff/phase-10-d-polish-screenshots/06_datepicker_deadline.png` (note: year display is off — backlog `B-DATEPICKER-EPOCH-CONVERSION`)
- **Spec coverage:** `spec/web/ui/views/date_picker_spec.cr`
Loading
Loading