moonshot

technical

skrift

skrift is moonshot's text editor. it runs inside a kakel window and reads and writes files through jakel. type edit (filename) from skalman to open a file. the editor takes over the keyboard. no shell prompt, no echo, just a buffer and a cursor.

modes

skrift has two modes. insert mode is home, you open a file and start typing. escape enters command mode, where single keys trigger actions. this is not vim-style modality. there is no normal mode, no composition language. just typing, and a small set of commands behind one key.

insert mode (default)
typing inserts characters at the cursor. backspace deletes backward. ctrl-d deletes the character under the cursor (forward delete, same as command-mode x). enter inserts a newline. tab advances to the next 4-column stop. arrow keys move in any direction, left and right within a line, up and down between lines (preserving the remembered column so vertical movement feels natural). escape enters command mode.
command mode
i returns to insert mode. h shows a key reference in the status bar. q quits and saves the buffer back to jakel. x deletes the character under the cursor. dd (press d twice) deletes the entire current line. cc (press c twice) changes (deletes) the current line and enters insert mode. cw (c then w) changes from the cursor to the end of the current word and enters insert mode. yy (press y twice) copies (yanks) the current line. any of dd, yy, or cc can be prefixed with a digit count, so 3dd deletes three lines, 5yy yanks five lines. the same digit accumulator that powers 5G (go to line 5) now also feeds repeat counts. u undoes the last buffer modification, whether an insert, a deletion, a paste, or a change operation. undo is single-level, so pressing u again does not redo. any new modification after undo saves a fresh undo state. gg (press g twice) jumps to the first line. G jumps to the last line, or, with a digit prefix, to a specific line (5G goes to line 5). e evaluates the current line as a c0 expression through the repl and shows the result in the status bar. E (shift-e) does the same but inserts the result as a // => N comment on the next line. r runs every line in the buffer through the repl interpreter. output scrolls in the editor window, with a ran N ln summary afterward. escape also returns to insert mode. arrow keys still move the cursor.

ctrl chords

ctrl chords work in both modes. ctrl-a jumps to the start of the current line. ctrl-e jumps to the end. ctrl-k kills (deletes) everything from the cursor to the end of the line, or, if the cursor is already at the end, kills the newline to merge with the next line. ctrl-d deletes the character under the cursor (forward delete). the keyboard handler tracks the left ctrl key (scancode 0x1D) as a held-state modifier, exactly like shift and alt.

how it works

the editor runs directly in the keyboard interrupt handler, the same context that echoes keystrokes to the screen when the shell is active. when skrift is active the keyboard handler routes every keypress to it instead of echoing and enqueuing for the shell. this is the same pattern skalman v1 used before it became a scheduled task.

the buffer is one flat, fixed-size heap allocation. inserting a character shifts every byte after the cursor right by one position and deleting shifts them left. this is the simplest possible buffer and it is fast enough for files of a few thousand lines. the use case is writing c0 source code for the repl's compile-and-execute mode, not editing a novel. a gap buffer or piece table can replace it later when the profile demands it.

rendering is a full redraw on every keystroke. it clears the window, walks the buffer line by line, draws each character into the visible viewport, draws a status bar at the bottom and draws an inverted-colour cell at the cursor position. the cursor is a full-cell invert (the character under the cursor is drawn in the window's background colour on a bright foreground background) rather than punkt's underline, because an editor cursor should be unambiguous.

file i/o is simple. skrift_start reads the file from jakel through the normal path-resolution and read apis. skrift_quit writes the buffer back, creating the file if it does not exist. saving does not call jakel_sync, because disk i/o is too slow for interrupt context, so the user runs sync from the shell after quitting, the same pattern as the shell's own write builtin.

status bar

the bottom row of the editor window shows the current mode, the filename, a * when the buffer has unsaved changes, the cursor's line and column (1-indexed), and the available commands for the current mode. in insert mode the right-hand hint reads esc:cmd and in command mode it shows c:chg dd:del y:yank and so on. pressing h overlays a full key reference.

arrow keys

the ps/2 keyboard sends arrow keys as two-byte sequences, an 0xE0 prefix byte followed by the arrow's scancode. the keyboard handler gained an e0_prefix flag that captures this. when 0xE0 arrives it sets the flag and returns from the interrupt after acknowledging the pic. the next interrupt picks up the flag and passes it to the active key consumer. the editor maps the four arrow make codes, up (72), left (75), right (77), and down (80), to cursor movement.

visual selection

hold shift while pressing arrow keys to select text. the selection spans from the anchor (where shift was first held) to the cursor. selected characters are drawn with inverted colours, the same visual as the editor cursor, so the selection is immediately obvious. releasing shift and pressing an arrow key clears the selection. the selection survives mode switches (escape to toggle between insert and command mode) so you can select in insert mode and operate in command mode.

once a region is selected, operating on it works as you would expect.

d in command mode
deletes the selection. if no selection is active, d pends for dd (delete current line) as before.
y in command mode
copies (yanks) the selection into the yank buffer. without a selection, y pends for yy (yank current line).
x in command mode
deletes the selection. without a selection, deletes the character under the cursor.
e in command mode
evaluates the selected text as a c0 expression through the repl and shows the result in the status bar. without a selection, evaluates the current line.
typing in insert mode
any printable character, enter, backspace, or tab deletes the selection first and then performs the normal action. this is type-to-replace, the way every modern editor works. ctrl-k and ctrl-d also delete the selection when one is active.

the status bar shows sel:N when a selection is active, where N is the number of selected bytes. the keyboard handler passes the shift held-state to the editor as a fifth argument alongside the make code, ascii, e0 flag, and ctrl flag it was already passing.