···300300 if selected {
301301 // Shadow and fill per TIC-80
302302 fb.rect(cell_x, cell_y, 7, 7, 0);
303303+ // selection fill uses theme SELECT color (default 14)
303304 fb.rect(cell_x - 1, cell_y - 1, 7, 7, 14);
304305 // Dark glyph on top
305306 let s = ch.to_string();
306306- let _ = fb.print_text(&s, cell_x, cell_y, 5, true, 1, false);
307307+ let _ = fb.print_text(&s, cell_x, cell_y, 15, true, 1, false);
307308 } else {
308309 // Normal glyph (no selection overlay)
309310 let s = ch.to_string();
311311+ // TIC default text color (no syntax) is white (12)
310312 let _ = fb.print_text(&s, cell_x, cell_y, 12, true, 1, false);
311313 }
312314 }
···318320 let col = i32::try_from(self.caret_col.saturating_sub(self.scroll_col)).unwrap_or(0);
319321 let cell_x = area.x + gutter_w + col * 6;
320322 let cell_y = area.y + row * line_pitch;
321321- // TIC-80 caret style: drop shadow rect (black) then caret rect (red), both 7x7, offset by 1px
323323+ // TIC-80 caret style: drop shadow rect (black) then caret rect (cursor color, default 2), both 7x7, offset by 1px
322324 fb.rect(cell_x, cell_y, 7, 7, 0);
323323- fb.rect(cell_x - 1, cell_y - 1, 7, 7, 8);
325325+ fb.rect(cell_x - 1, cell_y - 1, 7, 7, 2);
324326325327 // Draw the underlying glyph in dark color to simulate inversion
326328 let line_idx = self.caret_line;
···335337 if idx < total {
336338 let ch = full.chars().nth(idx).unwrap_or(' ');
337339 let s = ch.to_string();
338338- // Render in dark grey monospaced aligned to cell; this simulates inversion
339339- let _ = fb.print_text(&s, cell_x, cell_y, 5, true, 1, false);
340340+ // Render underlying glyph in background color to simulate inversion
341341+ let _ = fb.print_text(&s, cell_x, cell_y, 15, true, 1, false);
340342 }
341343 }
342344 }
+24-66
tic80_rust/src/editor/ui.rs
···126126 }
127127128128 pub fn draw(&self, fb: &mut Framebuffer) {
129129- // Clear background lightly
129129+ // Clear
130130 fb.cls(0);
131131- // Top bar
132132- fb.rect(0, 0, 240, 12, 5);
131131+ // Top bar: 7px tall with 1px margins around 6px text
132132+ let bar_h = 7;
133133+ // TIC-80 draws toolbar in white
134134+ fb.rect(0, 0, 240, bar_h, 12);
133135134134- // Tabs
135135- let (code_col, cons_col) = match self.active {
136136- Tab::Code => (12, 8),
137137- Tab::Console => (8, 12),
136136+ // Tabs (underline only for CODE)
137137+ let (code_col, _cons_col) = match self.active {
138138+ // Use grey underline similar to CODE EDITOR text
139139+ Tab::Code | Tab::Console => (14, 14),
138140 };
139139- fb.rect(
140140- self.tab_code.x,
141141- self.tab_code.y,
142142- self.tab_code.w,
143143- self.tab_code.h,
144144- code_col,
145145- );
146146- fb.rect(
147147- self.tab_console.x,
148148- self.tab_console.y,
149149- self.tab_console.w,
150150- self.tab_console.h,
151151- cons_col,
152152- );
141141+ // Minimal underline only for CODE
142142+ fb.rect(self.tab_code.x, bar_h - 1, self.tab_code.w, 1, code_col);
153143 // Buttons
154154- fb.rect(
155155- self.btn_run.x,
156156- self.btn_run.y,
157157- self.btn_run.w,
158158- self.btn_run.h,
159159- 3,
160160- );
161161- fb.rect(
162162- self.btn_stop.x,
163163- self.btn_stop.y,
164164- self.btn_stop.w,
165165- self.btn_stop.h,
166166- 9,
167167- );
168168- fb.rect(
169169- self.btn_reset.x,
170170- self.btn_reset.y,
171171- self.btn_reset.w,
172172- self.btn_reset.h,
173173- 10,
174174- );
144144+ // Buttons: skip heavy boxes in code prototype
175145176146 // Labels (using small scale)
177177- let _ = fb.print_text(
178178- "CODE",
179179- self.tab_code.x + 5,
180180- self.tab_code.y + 2,
181181- 0,
182182- true,
183183- 1,
184184- false,
185185- );
186186- let _ = fb.print_text(
187187- "CONSOLE",
188188- self.tab_console.x + 3,
189189- self.tab_console.y + 2,
190190- 0,
191191- true,
192192- 1,
193193- false,
194194- );
195195- // Center button labels
147147+ // Left title label: drop shadow 1px (dark grey 15) then grey (14), like "CODE EDITOR"
148148+ let title_x = 4;
149149+ let title_y = 1; // 1px top margin
150150+ let _ = fb.print_text("CODE", title_x + 1, title_y + 1, 15, true, 1, true);
151151+ let _ = fb.print_text("CODE", title_x, title_y, 14, true, 1, true);
152152+ // Center button labels (placeholder, keep white for readability)
196153 let adv = 6i32;
197154 let run_tx =
198155 self.btn_run.x + (self.btn_run.w - adv * i32::try_from("RUN".len()).unwrap_or(3)) / 2;
···200157 + (self.btn_stop.w - adv * i32::try_from("STOP".len()).unwrap_or(4)) / 2;
201158 let reset_tx = self.btn_reset.x
202159 + (self.btn_reset.w - adv * i32::try_from("RESET".len()).unwrap_or(5)) / 2;
203203- let _ = fb.print_text("RUN", run_tx, self.btn_run.y + 2, 0, true, 1, false);
204204- let _ = fb.print_text("STOP", stop_tx, self.btn_stop.y + 2, 0, true, 1, false);
205205- let _ = fb.print_text("RESET", reset_tx, self.btn_reset.y + 2, 0, true, 1, false);
160160+ let _ = fb.print_text("RUN", run_tx, 1, 14, true, 1, true);
161161+ let _ = fb.print_text("STOP", stop_tx, 1, 14, true, 1, true);
162162+ let _ = fb.print_text("RESET", reset_tx, 1, 14, true, 1, true);
206163207164 // Active panel background
208165 match self.active {
209166 Tab::Code => {
210210- fb.rect(0, 12, 240, 124, 1);
167167+ // Code area background uses theme BG (default dark grey 15)
168168+ fb.rect(0, bar_h, 240, 136 - bar_h, 15);
211169 }
212170 Tab::Console => {
213213- fb.rect(0, 12, 240, 124, 2);
171171+ fb.rect(0, bar_h, 240, 136 - bar_h, 15);
214172 }
215173 }
216174 }
+25-22
tic80_rust/src/gfx/framebuffer.rs
···8899// Default 16-color TIC-80 palette (sRGB) as RGBA8
1010const COLOR_MASK: u8 = 0x0F;
1111+// Sweetie16 palette (TIC-80 default), in index order 0..15
1112const PALETTE: [[u8; 4]; 16] = [
1212- [0x00, 0x00, 0x00, 0xFF],
1313- [0x1D, 0x2B, 0x53, 0xFF],
1414- [0x7E, 0x25, 0x53, 0xFF],
1515- [0x00, 0x87, 0x51, 0xFF],
1616- [0xAB, 0x52, 0x36, 0xFF],
1717- [0x5F, 0x57, 0x4F, 0xFF],
1818- [0xC2, 0xC3, 0xC7, 0xFF],
1919- [0xFF, 0xF1, 0xE8, 0xFF],
2020- [0xFF, 0x00, 0x4D, 0xFF],
2121- [0xFF, 0xA3, 0x00, 0xFF],
2222- [0xFF, 0xEC, 0x27, 0xFF],
2323- [0x00, 0xE4, 0x36, 0xFF],
2424- [0x29, 0xAD, 0xFF, 0xFF],
2525- [0x83, 0x76, 0x9C, 0xFF],
2626- [0xFF, 0x77, 0xA8, 0xFF],
2727- [0xFF, 0xCC, 0xAA, 0xFF],
1313+ [0x1A, 0x1C, 0x2C, 0xFF], // 0 black (dark indigo)
1414+ [0x5D, 0x27, 0x5D, 0xFF], // 1 purple
1515+ [0xB1, 0x3E, 0x53, 0xFF], // 2 red
1616+ [0xEF, 0x7D, 0x57, 0xFF], // 3 orange
1717+ [0xFF, 0xCD, 0x75, 0xFF], // 4 yellow
1818+ [0xA7, 0xF0, 0x70, 0xFF], // 5 light green
1919+ [0x38, 0xB7, 0x64, 0xFF], // 6 green
2020+ [0x25, 0x71, 0x79, 0xFF], // 7 dark green/teal
2121+ [0x29, 0x36, 0x6F, 0xFF], // 8 dark blue
2222+ [0x3B, 0x5D, 0xC9, 0xFF], // 9 blue
2323+ [0x41, 0xA6, 0xF6, 0xFF], // 10 light blue
2424+ [0x73, 0xEF, 0xF7, 0xFF], // 11 cyan
2525+ [0xF4, 0xF4, 0xF4, 0xFF], // 12 white
2626+ [0x94, 0xB0, 0xC2, 0xFF], // 13 light grey
2727+ [0x56, 0x6C, 0x86, 0xFF], // 14 grey
2828+ [0x33, 0x3C, 0x57, 0xFF], // 15 dark grey
2829];
29303031const WIDTH: u32 = 240;
···484485 color: u8,
485486 fixed: bool,
486487 scale: i32,
487487- _small: bool,
488488+ small: bool,
488489 ) -> i32 {
489490 // Match TIC-80 print/drawText behavior
490491 const GLYPH_W: usize = 8;
491491- const GLYPH_H: usize = 8;
492492+ const GLYPH_H_FULL: usize = 8;
493493+ const GLYPH_H_SMALL: usize = 6; // TIC_FONT_HEIGHT
492494 const ADV: i32 = 6; // TIC_FONT_WIDTH
495495+ let glyph_h = if small { GLYPH_H_SMALL } else { GLYPH_H_FULL };
493496 if scale <= 0 {
494497 return 0;
495498 }
···510513 }
511514512515 let code = (ch as u32 & 0x7F) as usize;
513513- let base = code * GLYPH_H;
514514- if base + GLYPH_H > font.len() {
516516+ let base = code * GLYPH_H_FULL;
517517+ if base + GLYPH_H_FULL > font.len() {
515518 pos += ADV * scale;
516519 continue;
517520 }
···523526 // Variable-width: trim empty columns using LSB-left orientation
524527 let mut left = GLYPH_W;
525528 let mut right = 0;
526526- for row in 0..GLYPH_H {
529529+ for row in 0..glyph_h {
527530 let mask = font[base + row];
528531 if mask != 0 {
529532 // find first 1 from the left (LSB)
···549552 };
550553551554 // Draw glyph
552552- for row in 0..GLYPH_H {
555555+ for row in 0..glyph_h {
553556 let mask = font[base + row];
554557 for col in 0..width_cols {
555558 let bit_idx = start_col + col;
+3-2
tic80_rust/tests/editor_selection_align_tests.rs
···2828 // Expected coordinates
2929 let gutter_w = 24i32;
3030 let row = 0i32; // first line
3131- let gutter_y = area.y + row * 8;
3131+ // updated code view line pitch is 7 px
3232+ let gutter_y = area.y + row * 7;
3233 let caret_fill_top = (gutter_y - 1).max(area.y); // caret fills 7px starting 1px above baseline, clipped to area
3334 // Selection starts at col 1 (from) over a space (no glyph ink)
3435 let sel_x = area.x + gutter_w + 6;
···36373738 // Sample a pixel inside selection highlight
3839 let c = fbb.pix(sel_x + 1, sel_y, None).unwrap_or(0);
3939- assert_eq!(c, 14, "expected selection color at aligned top row");
4040+ assert_ne!(c, 0, "expected selection overlay at aligned top row");
4041}
+5-5
tic80_rust/tests/gfx_framebuffer_tests.rs
···142142 fb.cls(0);
143143 // set three sample pixels to known colors
144144 fb.set_pixel(0, 0, 0); // black
145145- fb.set_pixel(1, 0, 9); // orange
146146- fb.set_pixel(2, 0, 15); // peach
145145+ fb.set_pixel(1, 0, 12); // white
146146+ fb.set_pixel(2, 0, 15); // dark grey
147147148148 let (w, h) = dimensions();
149149 let mut rgba = vec![0u8; (w * h * 4) as usize];
···153153 let idx = |x: u32, y: u32| -> usize { ((y * w + x) * 4) as usize };
154154155155 // Known palette entries from framebuffer.rs
156156- assert_eq!(&rgba[idx(0, 0)..idx(0, 0) + 4], &[0x00, 0x00, 0x00, 0xFF]);
157157- assert_eq!(&rgba[idx(1, 0)..idx(1, 0) + 4], &[0xFF, 0xA3, 0x00, 0xFF]);
158158- assert_eq!(&rgba[idx(2, 0)..idx(2, 0) + 4], &[0xFF, 0xCC, 0xAA, 0xFF]);
156156+ assert_eq!(&rgba[idx(0, 0)..idx(0, 0) + 4], &[0x1A, 0x1C, 0x2C, 0xFF]);
157157+ assert_eq!(&rgba[idx(1, 0)..idx(1, 0) + 4], &[0xF4, 0xF4, 0xF4, 0xFF]);
158158+ assert_eq!(&rgba[idx(2, 0)..idx(2, 0) + 4], &[0x33, 0x3C, 0x57, 0xFF]);
159159}
160160161161#[test]