返回 CodeWhale
ocean.rs
1 //! Fixed caller phases over the actual Codewhale terminal water column.
2 //! Context values and completion clocks are illustrative inputs, not receipts.
3
4 use std::time::Duration;
5
6 use ratatui::{
7 buffer::Buffer,
8 layout::Rect,
9 style::Modifier,
10 text::{Line, Span},
11 widgets::{Paragraph, Widget},
12 };
13 use unicode_width::UnicodeWidthStr;
14
15 use super::Entry;
16 use crate::{
17 Depth, Heading, Message, MotionMode, Paint, Panel, Role, State, StatusMark, Theme,
18 ocean::{OceanCausticFacts, OceanColumn, OceanPaintFacts, OceanPhase, ocean_semantic_surfaces},
19 };
20
21 fn band(area: Rect, top: u16, height: u16) -> Rect {
22 let top = top.min(area.height);
23 Rect::new(
24 area.x,
25 area.y.saturating_add(top),
26 area.width,
27 height.min(area.height.saturating_sub(top)),
28 )
29 }
30
31 fn column(area: Rect, buf: &mut Buffer, theme: &Theme) {
32 let area = area.intersection(buf.area);
33 Heading::new("Codewhale / native ocean")
34 .meta("Working / fixture")
35 .paint(band(area, 0, 1), buf, theme);
36 Message::new("You", "Keep the code and the decision clear.")
37 .role(Role::Primary)
38 .paint(band(area, 2, 3), buf, theme);
39 Message::new(
40 "Codewhale",
41 "One continuous column behind the conversation.",
42 )
43 .paint(band(area, 6, 3), buf, theme);
44
45 let selected = band(area, 10, 1);
46 buf.set_style(selected, theme.bg(Role::Selected));
47 Heading::new("Selected / src/ocean.rs")
48 .sub()
49 .paint(selected, buf, theme);
50 let surface = Panel::new(Depth::Raised)
51 .title("Semantic surface / own ground")
52 .draw(band(area, 12, 4), buf, theme);
53 StatusMark::new(State::Working).paint(surface, buf, theme);
54
55 OceanColumn::new(Duration::from_millis(22_500), MotionMode::Full)
56 .phase(OceanPhase::Working)
57 .apply(area, buf, theme);
58 }
59
60 fn phases_with(area: Rect, buf: &mut Buffer, theme: &Theme, motion: MotionMode) {
61 let area = area.intersection(buf.area);
62 let phases = [
63 (OceanPhase::Working, State::Working, "Working / quiet light"),
64 (
65 OceanPhase::Verifying,
66 State::Working,
67 "Verifying / quiet light",
68 ),
69 (
70 OceanPhase::Approval,
71 State::NeedsYou,
72 "Approval / steady warm water",
73 ),
74 (OceanPhase::Failed, State::Failed, "Failed / steady tint"),
75 (
76 OceanPhase::Done,
77 State::Done,
78 if motion.animates() {
79 "Done / completion at 320 ms"
80 } else {
81 "Done / static completion"
82 },
83 ),
84 ];
85 for (index, (phase, state, label)) in phases.into_iter().enumerate() {
86 let strip = band(area, index as u16 * 4, 4);
87 Heading::new(label).paint(band(strip, 0, 1), buf, theme);
88 StatusMark::new(state).paint(band(strip, 1, 1), buf, theme);
89 OceanColumn::new(Duration::from_millis(22_500), motion)
90 .phase(phase)
91 .completion_elapsed(Duration::from_millis(320))
92 .viewport(area)
93 .apply(strip, buf, theme);
94 }
95 }
96
97 fn phases(area: Rect, buf: &mut Buffer, theme: &Theme) {
98 phases_with(area, buf, theme, MotionMode::Full);
99 }
100
101 fn reduced(area: Rect, buf: &mut Buffer, theme: &Theme) {
102 phases_with(area, buf, theme, MotionMode::Reduced);
103 }
104
105 fn context(area: Rect, buf: &mut Buffer, theme: &Theme) {
106 let area = area.intersection(buf.area);
107 for (index, percent) in [0, 50, 100].into_iter().enumerate() {
108 let left = (u32::from(area.width) * index as u32 / 3) as u16;
109 let right = (u32::from(area.width) * (index as u32 + 1) / 3) as u16;
110 let column = Rect {
111 x: area.x.saturating_add(left),
112 width: right.saturating_sub(left),
113 ..area
114 };
115 Heading::new(format!("{percent}% context")).paint(band(column, 0, 1), buf, theme);
116 Message::new("Fixture", "Fullness raises the abyss.").paint(
117 band(column, 3, column.height.saturating_sub(3)),
118 buf,
119 theme,
120 );
121 OceanColumn::new(Duration::ZERO, MotionMode::Still)
122 .context_percent(percent)
123 .apply(column, buf, theme);
124 }
125 }
126
127 // This native path supplies presentation facts from a real rendered pane.
128 // It keeps raw ink/style facts and semantic padding while sharing cached rows
129 // between the base water and caustic passes. No terminal evidence is invented.
130 fn native_guarded(area: Rect, buf: &mut Buffer, theme: &Theme) {
131 let area = area.intersection(buf.area);
132 let base = theme.bg(Role::Background);
133 let mut rows = vec![Line::from(""); 4];
134 rows.push(Line::from(vec![Span::styled(
135 "Native / guarded ocean",
136 theme.fg(Role::Foreground),
137 )]));
138 rows.push(Line::from(vec![Span::styled(
139 "[Selected] source keeps its own ground",
140 theme.bg(Role::Selected).patch(theme.fg(Role::Foreground)),
141 )]));
142 rows.push(Line::styled("", base));
143 rows.push(Line::from(vec![Span::styled(
144 "Explicit blank padding is semantic",
145 base.patch(theme.fg(Role::Foreground)),
146 )]));
147 rows.push(Line::from(vec![Span::styled(
148 "Reversed surface",
149 theme.fg(Role::Foreground).add_modifier(Modifier::REVERSED),
150 )]));
151 rows.push(Line::styled(
152 "Caller phase: Working / fixture",
153 theme.fg(Role::Dim),
154 ));
155 Paragraph::new(rows.clone()).render(area, buf);
156 let column = OceanColumn::new(Duration::from_millis(22_500), MotionMode::Full)
157 .phase(OceanPhase::Working)
158 .viewport(area);
159 let samples: Vec<_> = (area.y..area.bottom())
160 .map_while(|y| column.color_at_y(y, area, theme))
161 .collect();
162 let protected = ocean_semantic_surfaces(&rows, area, str::width);
163 let paint = OceanPaintFacts {
164 ground: base.bg.unwrap_or(ratatui::style::Color::Reset),
165 sample_top: area.y,
166 samples: &samples,
167 protected: &protected,
168 };
169 column.apply_native(area, buf, theme, &paint, |cell, _| cell.fg);
170 column.apply_caustics(
171 area,
172 buf,
173 theme,
174 &OceanCausticFacts {
175 paint,
176 elapsed: Duration::from_millis(480),
177 band_rows: 4,
178 },
179 );
180 }
181
182 pub(crate) fn entries() -> Vec<Entry> {
183 vec![
184 Entry {
185 name: "ocean-column",
186 width: 80,
187 height: 16,
188 draw: column,
189 },
190 Entry {
191 name: "ocean-phases",
192 width: 80,
193 height: 20,
194 draw: phases,
195 },
196 Entry {
197 name: "ocean-context",
198 width: 80,
199 height: 12,
200 draw: context,
201 },
202 Entry {
203 name: "ocean-reduced",
204 width: 80,
205 height: 20,
206 draw: reduced,
207 },
208 Entry {
209 name: "ocean-native-guarded",
210 width: 80,
211 height: 16,
212 draw: native_guarded,
213 },
214 ]
215 }
216
216 lines RUST