chore: initialize git repository and create docs/ with ARCHITECTURE.md and UI_DESIGN.md
- Capture key decisions (single BASE cycle with Tab to reserve A-F, decimal math + display formatting only, CERR policy on non-integers, two-row galculator-inspired display, gostations lipgloss patterns). - Establish docs/ as home for architecture and design notes (per request). - Include references to spec.md and gostations patterns. - Paper trail begins here for backtracking.
This commit is contained in:
commit
0c7473943d
50
.gitignore
vendored
Normal file
50
.gitignore
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool
|
||||
*.out
|
||||
coverage.html
|
||||
coverage.out
|
||||
|
||||
# Dependency directories (vendor)
|
||||
vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# Build output (following gostations convention)
|
||||
build/
|
||||
|
||||
# The compiled binary for this project
|
||||
gralculator
|
||||
gralculator.exe
|
||||
|
||||
# IDE and editor
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*~
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Logs and local env
|
||||
*.log
|
||||
.env
|
||||
.env.local
|
||||
55
docs/ARCHITECTURE.md
Normal file
55
docs/ARCHITECTURE.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Gralculator Architecture
|
||||
|
||||
## Overview
|
||||
Gralculator is a terminal UI calculator inspired by the visual density and layout of galculator (GTK), but adapted for Bubble Tea + lipgloss in a keyboard-first, content-centered style consistent with the gostations project.
|
||||
|
||||
Core philosophy for v0.1 (MVP):
|
||||
- All math is performed in decimal (float64 for simplicity).
|
||||
- Display bases (DEC/HEX/BIN/OCT) are **purely for formatting** the current value in the large display area.
|
||||
- A single "BASE" action (on-screen button + `Tab` key) cycles the display format: DEC → HEX → BIN → OCT → DEC.
|
||||
- Non-integer values cannot be displayed in non-DEC bases. Pressing BASE on a fractional result triggers a brief "CERR" (conversion error) flash in the large display and leaves the base unchanged. This is an explicit design choice over galculator's silent truncation.
|
||||
- Future iterations will add full hex digit entry (A-F), at which point letter keys will be reserved. This is why `Tab` was chosen for BASE cycling instead of a letter key.
|
||||
|
||||
## Package Structure
|
||||
- `internal/calc` — Pure Go calculation engine. No TUI dependencies. Handles value state, pending operations, base cycling with integer checks, formatting, and arithmetic (+ - * / MOD). Fully unit-testable.
|
||||
- `internal/ui` — Bubble Tea model, view, and update logic. Renders the two-row display (large number + small current-base indicator) and keypad grid. Reuses lipgloss patterns, centering (`Place`), subtle borders, flash animations (tea.Tick + 140ms color 63 style), and content-sized card layout refined in gostations.
|
||||
- `internal/version` — Version string injection (ldflags), matching gostations.
|
||||
- `main.go` — Entry point, flags (-v), tea.NewProgram wiring, pre-flight checks if any.
|
||||
- Root: `spec.md` (living high-level spec), `docs/` (detailed architecture and design notes), `Makefile`, `.gitignore`.
|
||||
|
||||
## Key Design Decisions (v0.1)
|
||||
- **Internal representation**: `float64` + pending operator state (classic four-function calculator model). Simpler and sufficient for MVP. Floating-point MOD via `math.Mod`.
|
||||
- **Base handling**: `CycleBase()` returns error/sentinel on non-integer to drive UI "CERR" flash. `CurrentBase()` and `FormatForDisplay()` for the view. No base-aware digit entry yet (reserved for future hex support).
|
||||
- **Error on conversion**: When value has fractional part and BASE is pressed, large display shows "CERR" briefly (~600ms with flash styling), base does not change, then reverts to previous formatted value.
|
||||
- **Single BASE key**: Reduces rendered keys. On-screen button labeled "BASE". Direct key is `Tab` (chosen to leave A-F free for future hex digits).
|
||||
- **Display layout**:
|
||||
- Large top area inside bordered panel for the number (tall padding + bold/high-visibility styling for "large font" weight).
|
||||
- Small bottom row inside the same panel showing only the current base ("DEC", "HEX", "BIN", or "OCT"), highlighted when active.
|
||||
- **Keypad**: Sparse 4-5 column grid for MVP (digits, operators, MOD, C, AC, single BASE button). Uses the same `makeButton` + `Width().Align(Center)` lipgloss idiom as gostations playback controls.
|
||||
- **Card layout**: Entire UI is a content-sized, centered card (lipgloss.Place + rounded outer border color 63). Matches gostations Winamp-style player polish (subtle inner 238 borders, no stretching).
|
||||
- **Feedback**: 140ms timed flashes (color 63 bg + white fg bold) on key actions, exactly as tuned for volume/skip/stop in gostations.
|
||||
- **Hint row**: Minimal, non-wrapping, full-width (like final gostations player).
|
||||
- **No persistence / config in v0.1**: Session-only current base.
|
||||
- **Testing**: Engine has high-value unit tests (especially the CERR path). UI tests will focus on rendering and key-driven state transitions.
|
||||
- **Future-proofing notes**:
|
||||
- Hex entry (A-F) planned for later release → letter keys kept free.
|
||||
- Scientific functions, memory, %, bitwise, word size, RPN, history, etc. explicitly deferred.
|
||||
- Potential later addition of clipboard, persistent settings (XDG), scripting subcommand.
|
||||
|
||||
## Build & Integration
|
||||
- Matches gostations conventions: `make build`, `make install` (to `~/.local/bin/gralculator`), cross-compile support, ldflags for version.
|
||||
- Binary name: `gralculator`
|
||||
- Will eventually be wired into the personal REXX `~/.local/bin/apps` launcher (likely a new letter or replacement for the current "l. Terminal Calculator" / tcalc entry).
|
||||
- Go 1.24+ (consistent with other apps/ projects).
|
||||
|
||||
## References
|
||||
- Root `spec.md` — original living specification and iteration history.
|
||||
- gostations (`apps/gostations`) — primary source for TUI patterns, lipgloss idioms, centering, flashes, Makefile, release hygiene, todo/ process (to be adopted later).
|
||||
- galculator GTK screenshot — primary visual reference for the "large number + small mode row" display density.
|
||||
|
||||
See also:
|
||||
- `docs/UI_DESIGN.md` (detailed two-row display and keypad rendering notes)
|
||||
- `docs/KEYBOARD.md` (key binding rationale and future hex entry considerations)
|
||||
|
||||
---
|
||||
*Initialized during v0.1 skeleton work, 2026.*
|
||||
74
docs/UI_DESIGN.md
Normal file
74
docs/UI_DESIGN.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Gralculator UI Design
|
||||
|
||||
## Display (Two-Row Layout)
|
||||
Directly inspired by the galculator GTK reference screenshot, simplified for terminal realities and single-BASE cycling.
|
||||
|
||||
Inside a single bordered "LCD" panel (dark background ~235, green/high-visibility foreground ~46 for the number, subtle inner NormalBorder color 238):
|
||||
|
||||
- **Large top area**: The current numeric value. Strong visual weight via extra vertical padding/height (e.g. 3-5 lines effective), bold styling, generous font-like sizing through lipgloss. Right-aligned or centered as appropriate for the base (DEC often right, hex/bin may vary). Large enough to feel like the "big number" in galculator.
|
||||
|
||||
- **Small bottom row**: Directly under the number, still within the same panel. Shows only the *current* base label: `DEC`, `HEX`, `BIN`, or `OCT`. The active one is highlighted (bold + background color 63 or accent green, matching gostations key badges and outer border color). No full row of four selectable bases — the single BASE action handles cycling.
|
||||
|
||||
Example (ASCII approximation of rendered view):
|
||||
|
||||
```
|
||||
+--------------------------------------------------+
|
||||
| |
|
||||
| 1234.56 | <-- large number area
|
||||
| |
|
||||
| DEC | <-- small current-base row (highlighted)
|
||||
+--------------------------------------------------+
|
||||
```
|
||||
|
||||
When a non-integer conversion is attempted (e.g. 3.833... + BASE), the *large number area* temporarily renders "CERR" (with the 140ms + color 63 flash/blink style used for volume/skip/stop feedback in gostations). After the flash duration (~600ms), it reverts to the previous value in the unchanged base. The small row never changes on error.
|
||||
|
||||
## Keypad Grid
|
||||
Sparse for MVP (no memory, no %, no bitwise, no scientific, no A-F yet).
|
||||
|
||||
Uses the same button-cell rendering idiom as gostations `renderPlayback` / `makeButton`:
|
||||
|
||||
- `lipgloss.NewStyle().Width(5).Align(lipgloss.Center).Padding(0,1).Render(label)`
|
||||
- Active/pressed state uses green accent or the flashStyle (white on 63 bg, bold).
|
||||
- Flash on every key action using `tea.Tick` + clearFlashMsg (140ms), exactly as tuned in gostations player.
|
||||
|
||||
Layout sketch (subject to refinement during spike; 4-5 columns typical):
|
||||
|
||||
```
|
||||
7 8 9 / MOD
|
||||
4 5 6 * C
|
||||
1 2 3 - AC
|
||||
0 . +/- + =
|
||||
BASE
|
||||
```
|
||||
|
||||
(Or tighter grouping. BASE is prominent because it is the distinctive "programmer display" feature.)
|
||||
|
||||
All important actions have direct keys (digits, operators, =/Enter, Backspace, Tab for BASE, c/C, etc.). The on-screen buttons are primarily visual + mouse targets.
|
||||
|
||||
## Overall Card & Polish
|
||||
- The entire calculator is wrapped in a **content-sized centered card**:
|
||||
- Outer rounded border (color 63, matching gostations label/border).
|
||||
- `lipgloss.Place(width, height, lipgloss.Center, lipgloss.Center, card)`
|
||||
- No stretching to full terminal width (important lesson from gostations player polish).
|
||||
- Subtle inner borders on panels where depth helps.
|
||||
- Minimal non-wrapping hint row at bottom of window (lipgloss bar with key badges in 63 bg, like final gostations player): e.g. "Tab:BASE q:quit etc."
|
||||
- Resize handling: card recenters; display and grid adapt gracefully (minimum sensible width enforced with a message if too narrow).
|
||||
- Consistent dark theme, green LCD accents, flash feedback.
|
||||
|
||||
## Rendering & State
|
||||
- UI owns transient state: current flashes, window size, focus hints.
|
||||
- Engine (`internal/calc`) is queried for `FormatForDisplay()`, `CurrentBase()`, and mutated via `CycleBase()`, digit entry, operators, etc.
|
||||
- View is mostly custom (JoinVertical/JoinHorizontal + lipgloss styles). Minimal use of bubbles components for MVP (possible list or table later if keypad grows).
|
||||
- Update dispatches keys → engine calls + flash triggers. Tab always cycles (global, like q/quit).
|
||||
|
||||
## Mouse (Bonus)
|
||||
Bubble Tea mouse support will be wired so clicking a rendered button cell triggers the same action as its key. Not required for first working spike.
|
||||
|
||||
## References & Constraints
|
||||
- Must feel like the top half of the provided galculator screenshot (large number + small mode indicator) while staying practical in 80-120 col terminals.
|
||||
- Reuse every hard-won gostations pattern: lipgloss.Place centering, inner 238 borders, makeButton cells, 140ms color-63 flashes, hint bar, content-sized card, tea.Tick clear messages.
|
||||
- Keep the TUI responsive and fun even in the minimal keypad state.
|
||||
|
||||
See `spec.md` (root) for full scope and `ARCHITECTURE.md` for package and decision rationale.
|
||||
---
|
||||
*Created during phase 1 skeleton, 2026.*
|
||||
246
spec.md
Normal file
246
spec.md
Normal file
@ -0,0 +1,246 @@
|
||||
# Gralculator
|
||||
|
||||
**Gralculator** — Greg's Calculator. A terminal UI calculator.
|
||||
|
||||
Portmanteau of "Greg" + "calculator". Goal: a fast, keyboard-centric TUI whose display layout closely follows galculator (one large top display area for the number + one small row directly below it showing the current display base), while performing all calculations in decimal. A single "BASE" key cycles the display formatting (DEC → HEX → BIN → OCT). Bases are purely for formatting the displayed value.
|
||||
|
||||
## Goals
|
||||
|
||||
- Display layout that matches the galculator spirit: one large prominent area for the number (big visual weight) + one small bottom row inside or immediately under the display showing the **current base** ("DEC", "HEX", "BIN", or "OCT"), with the active one highlighted.
|
||||
- A single "BASE" button/key that cycles the display base. This reduces the number of keys that need to be laid out and rendered in the keypad.
|
||||
- Core arithmetic for MVP: `+`, `-`, `*`, `/`, and `MOD` (modulo). Plus `C` (clear entry) and `AC` (all clear).
|
||||
- All internal calculations are performed in decimal. The current base only changes how the result/value is formatted and shown in the large display (with CERR flash on non-integer values when BASE is pressed). No base-specific digit entry or bitwise math in v1.
|
||||
- Clean, keyboard-primary TUI that feels immediate.
|
||||
- Single static Go binary that launches from the REXX ncurses `~/.local/bin/apps` launcher (exactly like `gostations`).
|
||||
- Reuse the proven TUI patterns and aesthetics from gostations (lipgloss-centered content-sized card, subtle inner borders, LCD-style display treatment, button cells, action flashes via 140 ms ticks + color 63, minimal non-wrapping hint row, proper centering on resize).
|
||||
- Small, pure, testable calculation engine separate from the Bubble Tea UI layer.
|
||||
- Project hygiene matching the rest of `apps/` (gostations/grokkit): Makefile with build/install/test, easy `make install` to `~/.local/bin/gralculator`.
|
||||
|
||||
## Reference
|
||||
|
||||
Primary visual reference: galculator GTK (screenshot provided during design discussion).
|
||||
|
||||
Key elements observed and targeted for the TUI port:
|
||||
- Large, prominent top display area showing the current value (right-aligned in the reference).
|
||||
- Small base indicator row tightly associated with the display (just the current base name, e.g. "DEC"), since a single BASE key cycles through them. Current base is obvious at a glance.
|
||||
- Additional mode indicators (DEG/RAD/GRAD, ALG/RPN/FORM) — these are lower priority for the initial programmer focus.
|
||||
- C / AC (clear) and backspace controls near the display.
|
||||
- Wide, dense button grid that interleaves:
|
||||
- Scientific functions on the left (many can be deferred).
|
||||
- Bitwise/logic operators (AND, OR, XOR, CMP/NOT, LSH, RSH, MOD) mixed into the grid.
|
||||
- Hex digits A–F placed directly in the numeric flow.
|
||||
- Memory operations (MS, MR, M+ with dropdown affordance in GUI).
|
||||
- Standard numeric keypad + arithmetic operators on the right.
|
||||
- Dark theme with clear visual hierarchy.
|
||||
- Everything important is visible without deep menus or mode switches.
|
||||
|
||||
In a TUI we cannot (and should not) do a pixel-perfect 1:1. We will capture the **density and immediacy** using character-cell grids, bordered panels, and highlighting.
|
||||
|
||||
## MVP Scope (Target for First Usable Release)
|
||||
|
||||
**Must have for v0.1 (narrowed per design discussion):**
|
||||
|
||||
- **Display layout** (directly from the galculator reference, simplified):
|
||||
- One large top area inside the display panel with prominent / large visual weight for the current number.
|
||||
- One small bottom row (inside or tightly coupled to the display) that simply shows the **current base**: "DEC", "HEX", "BIN", or "OCT". The current one is visually distinct (highlighted / bold / accent color).
|
||||
- All calculations are performed internally in decimal. The display base only affects **formatting** of the current value for the large number area.
|
||||
|
||||
- **Display base conversion rule (non-integer handling)** — this is the explicit policy chosen over galculator's "truncate to int" behavior:
|
||||
- There is a single "BASE" key (on-screen button labeled BASE + a direct keyboard key). Pressing it **cycles** the display base: DEC → HEX → BIN → OCT → DEC.
|
||||
- When the user presses the BASE key (on-screen "BASE" button or `Tab`), the engine first checks if the current decimal value is effectively an integer.
|
||||
- Check: use `math.Modf` (or equivalent); if the fractional part is 0 (or within a tiny epsilon like 1e-10 to tolerate floating-point noise from operations like 1/3), treat it as integer.
|
||||
- If it **is** an integer: cycle to the next base and immediately reformat the (integer) value for the large display area in the new base. The small mode row updates to show the new current base.
|
||||
- If it has a **fractional part**: refuse the cycle. Do not change the display base. Instead, trigger a short error indication using the existing flash mechanism:
|
||||
- The large number area briefly shows "CERR" (for ~600 ms, with the color 63 flash style / blink for visibility).
|
||||
- After the flash, the display reverts to showing the value in the previous base (small row unchanged).
|
||||
- Example: `23 / 6` → `3.833333...` (DEC). Press BASE → large display blinks "CERR", small row stays on DEC, then number returns.
|
||||
- This gives clear feedback without silently losing precision, which the user prefers over galculator's truncation. A single BASE key also reduces the number of keys that need to be rendered in the keypad grid.
|
||||
- Core operations:
|
||||
- `+` `-` `*` `/`
|
||||
- `MOD` (modulo / remainder)
|
||||
- Clear controls: `[C]` (clear current entry / value) and `[AC]` (all clear).
|
||||
- Standard supporting entry: decimal digits `0-9`, decimal point `.` (for fractional results), `+/-` (sign change), backspace, and `=` / Enter to commit the pending operation.
|
||||
- Keyboard primary. Direct keys for everything above + `Tab` for the single BASE key (cycles DEC→HEX→BIN→OCT). On-screen "BASE" button is for visual reference and optional mouse support. Using a single BASE button (instead of four mode buttons) keeps the keypad sparse. Tab was chosen specifically to leave `A`–`F` free for future hex digit entry.
|
||||
- Content-sized + centered main calculator card (lipgloss.Place + rounded outer border). Use the same subtle inner NormalBorder (color 238) treatment and "LCD" styling (dark bg ~235 + green ~46 or high-visibility fg) that was refined in gostations.
|
||||
- Action feedback: short flashes (140 ms ticks + color 63 highlight) on button/key actions, matching the volume/skip/stop flashes from the gostations player view.
|
||||
- Minimal non-wrapping hint row at the very bottom (same style as gostations).
|
||||
- `gralculator -v` / `--version` and clean exit behavior.
|
||||
- `make install` target that drops the binary into `~/.local/bin/gralculator`.
|
||||
|
||||
**Explicitly deferred (second major release or later):**
|
||||
|
||||
- `%` (percent)
|
||||
- Bitwise / logic: AND, OR, XOR, CMP (NOT / complement)
|
||||
- Memory (MS / MR / M+ / MC)
|
||||
- Any hex-aware entry or A-F digits (because we stay in decimal internally for v1)
|
||||
- Word size / bit masking
|
||||
- Scientific functions (the left side of the galculator grid)
|
||||
- RPN or other notations
|
||||
- History tape, clipboard, persistence of last base, etc.
|
||||
|
||||
This is intentionally a much smaller MVP than the initial broad spec. The goal is a working, polished-feeling decimal calculator with nice base *display* modes that looks and feels like the top half of the galculator window.
|
||||
|
||||
## Proposed UI Layout (Updated per design discussion)
|
||||
|
||||
The display itself follows the galculator reference very closely, but simplified to a single cycling BASE:
|
||||
|
||||
- One **large top area** (the main number display) with strong visual weight — taller, more padding, bolder/larger effective "font" treatment via lipgloss height + styling.
|
||||
- One **small bottom row** directly under the number (still inside the same bordered display panel or tightly coupled) that simply shows the current base: `DEC` (or `HEX` / `BIN` / `OCT`). The current one is highlighted (e.g. bold + background color 63 or the green accent). No need to show all four options at once since BASE cycles them.
|
||||
|
||||
Example sketch of the display portion only:
|
||||
|
||||
```
|
||||
+-----------------------------------------------+
|
||||
| |
|
||||
| 1234.56 | <-- large / prominent number area
|
||||
| |
|
||||
| DEC | <-- small row: just the current base
|
||||
+-----------------------------------------------+
|
||||
```
|
||||
|
||||
(The keypad grid below will contain a single "BASE" button cell instead of four separate mode buttons. This directly addresses the request to reduce the number of keys we need to render.)
|
||||
|
||||
Below the display panel comes the keypad grid of button cells (using the same `makeButton` + `Width(5).Align(Center)` pattern that worked well for the control symbols in gostations playback).
|
||||
|
||||
The whole calculator is wrapped in a content-sized centered card (outer rounded border color 63, `lipgloss.Place` centering) exactly like the Winamp-style player view.
|
||||
|
||||
Keypad for MVP will be much sparser than the full galculator grid because we have removed memory, %, bitwise, hex letters, and scientific functions for v1. Something like a clean 4- or 5-column arithmetic keypad with MOD, C, AC, and a single "BASE" button (the direct key for BASE cycles the display formatting).
|
||||
|
||||
The entire view uses the gostations rendering idioms: `lipgloss.JoinVertical` / `JoinHorizontal`, inner subtle borders where helpful, flash styles on action, and a minimal hint bar at the very bottom of the window.
|
||||
|
||||
Display styling target (same LCD treatment refined in gostations):
|
||||
- Display panel background ~235, number foreground ~46 (classic green) or bright for visibility.
|
||||
- Subtle inner border (238).
|
||||
- The small mode row uses a dimmer style with the active base popped (bold or colored background).
|
||||
|
||||
## Core State & Engine (Pure, Testable) — Simplified for this MVP
|
||||
|
||||
Because all calculations stay in decimal and bases are display-only:
|
||||
|
||||
- Internal value: a simple `float64` (or a small wrapper type) for the current result / accumulator. This keeps the engine tiny and familiar for basic arithmetic + modulo.
|
||||
- Pending operation state (the classic "lastOperator + lastValue" pattern used by most four-function calculators).
|
||||
- Current display base (an enum or string: "dec", "hex", "oct", "bin"). This is UI + formatting state, not math state.
|
||||
- No word size, no bit masks, no base-aware parsing for v1.
|
||||
|
||||
Separate the engine:
|
||||
- `internal/calc` package (pure Go, no Bubble Tea, easy to unit test).
|
||||
- Main types: current value (float64), pending op, displayBase.
|
||||
- Key methods: `EnterDigit(d rune)`, `EnterDecimalPoint()`, `SetOperator(op string)`, `Equals()`, `ApplyMod()`, `ClearEntry()`, `AllClear()`, `ChangeSign()`, `Backspace()`, `SetDisplayBase(base)`.
|
||||
- `FormatForDisplay() string` — this is where the magic happens. It takes the internal decimal float and produces the string to show in the large display area according to the current base. For non-integer values in HEX/OCT/BIN we will need a clear policy (e.g. show integer part only + a note, or fall back to decimal, or use a fixed-point representation). This policy should be decided before the first spike.
|
||||
- The engine never cares what the current display base is until `FormatForDisplay` is called.
|
||||
|
||||
The Bubble Tea model (in `internal/ui`) holds:
|
||||
- A `*calc.Engine` (or the state it exposes).
|
||||
- Transient UI state only: current base for the small row, flash flags for recent key presses (using the same `tea.Tick` + clearMsg pattern as gostations), window size for centering the card. (Only one BASE button to track instead of four separate modes.)
|
||||
- The view renders the big display by calling `engine.FormatForDisplay()` and then a small mode row underneath it.
|
||||
|
||||
This is dramatically simpler than the original broad programmer-calc vision. The engine should be implementable in a few dozen lines plus tests.
|
||||
|
||||
The engine will expose something like:
|
||||
- `CycleBase() error` — advances to the next base in the cycle (DEC→HEX→BIN→OCT). Returns a special "conversion not possible" sentinel (or typed error) when the current value is non-integer. The UI layer uses the error to trigger the "CERR" flash in the big display and leaves the base unchanged.
|
||||
- `FormatForDisplay() string`
|
||||
- `IsInteger() bool` (helper, with the epsilon tolerance).
|
||||
- `CurrentBase() string` — returns "DEC", "HEX", "BIN", or "OCT" for the small row in the display.
|
||||
|
||||
MOD will use Go's `math.Mod` (floating-point modulo) because it is the simplest to implement initially; the user confirmed this is acceptable.
|
||||
|
||||
## Keyboard Bindings (Initial Proposal — To Be Refined)
|
||||
|
||||
Global:
|
||||
- `q` / `Ctrl+C` / `Esc` — quit
|
||||
- `Tab` — BASE: cycle the display base (DEC → HEX → BIN → OCT → DEC). The small row updates to the new current base (or flashes "CERR" + stays put if the value is non-integer).
|
||||
- Tab was deliberately chosen (instead of a letter key) to reserve `A`–`F` for future full hex digit entry support in a later release.
|
||||
|
||||
Entry:
|
||||
- `0-9` — digits (always decimal)
|
||||
- `.` — decimal point
|
||||
- `Backspace` / `Delete` — backspace last digit
|
||||
- `c` / `C` — clear entry (C button)
|
||||
- `Ctrl+L` or dedicated AC key — all clear (AC button)
|
||||
- `+/-` or `s` / `S` or `n` / `N` — change sign (we can pick the most natural during implementation)
|
||||
|
||||
Operations:
|
||||
- `+ - * /`
|
||||
- `=` or `Enter` — commit / equals
|
||||
- `m` or `M` or `%` wait no — `mod` key or `;` or a clear mnemonic for MOD (to be chosen; on-screen label will be "MOD")
|
||||
- `Ctrl+C` is already AC, so careful with overlap
|
||||
|
||||
The small bottom row simply shows the current base (e.g. "DEC"). The single "BASE" button in the keypad grid (plus the direct key) is what cycles it. The row does not need to be interactive since we only have one cycle action.
|
||||
|
||||
We will keep the hint row minimal and non-wrapping, exactly like the final gostations player hint.
|
||||
|
||||
Mouse: supported as a bonus via Bubble Tea mouse messages mapping clicks on the rendered button cells. Not required for first working version.
|
||||
|
||||
## Project Structure (Minimal at Start, Grow to Match gostations)
|
||||
|
||||
```
|
||||
apps/gralculator/
|
||||
├── .gitignore
|
||||
├── spec.md # this file
|
||||
├── go.mod
|
||||
├── main.go # entry, flags, tea.NewProgram
|
||||
├── Makefile # build, install, test, cross (copy/adapt from gostations)
|
||||
├── internal/
|
||||
│ ├── calc/
|
||||
│ │ ├── calc.go # engine
|
||||
│ │ └── calc_test.go
|
||||
│ ├── ui/
|
||||
│ │ ├── ui.go # bubbletea model + view + update (heavy custom render)
|
||||
│ │ └── ui_test.go
|
||||
│ └── version/
|
||||
│ └── version.go
|
||||
├── build/ # (gitignored) dev builds
|
||||
└── (later: README.md, CHANGELOG.md, todo/, release.sh, assets/, .gitea/...)
|
||||
```
|
||||
|
||||
Follow the gostations pattern for:
|
||||
- Version injection via ldflags (Makefile + release workflow when we get there).
|
||||
- `internal/version`.
|
||||
- Precheck / nice errors if needed (none for a pure calc).
|
||||
- Tests from day one for the engine.
|
||||
|
||||
## Integration Points
|
||||
|
||||
- Binary name: `gralculator`
|
||||
- Launcher: once usable, add an entry in `~/.local/bin/apps` (ProcessSelection and the two parallel arrays). We can choose a good letter later (current `l` is still `tcalc`).
|
||||
- Install target: `~/.local/bin/gralculator` (exactly like gostations).
|
||||
- No external runtime dependencies beyond a modern terminal (kitty, wezterm, ghostty, etc. recommended for Unicode and 256 color, same as gostations).
|
||||
|
||||
## Open Questions & Decisions Needed (Current as of this update)
|
||||
The two most important policy questions have now been answered:
|
||||
|
||||
**Resolved (per your latest message):**
|
||||
- **Non-integer base conversion**: When the current decimal value has a fractional part, pressing the single BASE key (cycle) must **not** silently truncate (unlike galculator). Instead: keep the previous base, briefly flash "CERR" in the large display area (using the flash/tick + color 63 mechanism, held for ~600 ms), then revert to the number in the old base. Full rule (including the single BASE cycle) is documented in the "Display base conversion rule" subsection under MVP Scope.
|
||||
- **MOD on floats**: Use floating-point modulo (`math.Mod`) for the initial implementation — whichever is easiest.
|
||||
|
||||
**Still open (ranked roughly by what we need for the first code):**
|
||||
|
||||
1. **Classic entry model** — "In-progress entry buffer" (you see the digits as you type them before committing with an operator or =) vs. immediately applying each digit to the accumulator? Classic four-function calculators usually use the buffer approach.
|
||||
|
||||
2. **Base change with a pending operation** — If the display shows something like `123 +` (or the pending left value) and you switch base, should we reformat the pending operand in the new base? (Almost certainly yes — it's just a view change.)
|
||||
|
||||
3. **Key for MOD** — Natural key (or shifted key) for the MOD button? The on-screen label will say "MOD".
|
||||
|
||||
4. **Large display "big font" treatment** — How do we give the number area strong visual weight? (Extra-tall with padding + bold green text, multiple lines, etc.)
|
||||
|
||||
5. **Spike order** — Write the pure `internal/calc` engine first (including `IsInteger()` / epsilon check, `TrySetBase` that can return the CERR condition, and `FormatForDisplay`), or start with a quick single-file TUI rendering spike of the exact large-number + small-mode-row display (hard-coded values) so we can see the galculator layout live in the terminal immediately?
|
||||
|
||||
6. **When to scaffold the project** — Add `go.mod`, `internal/calc`, `internal/ui`, Makefile, etc. now, or keep it to the absolute minimum source files until we have something running and pretty?
|
||||
|
||||
Minor tunables (exact epsilon for "is integer", whether "CERR" is the final string, flash duration, whether a tiny fractional indicator appears somewhere) can be adjusted during implementation.
|
||||
## Next Steps (Ready when you are)
|
||||
|
||||
With the display layout and MVP scope now pinned down, the logical next moves are:
|
||||
|
||||
- Agree on the answers to the remaining open questions (especially entry model, spike order, and when to add go.mod + skeleton). The key decisions (CERR non-integer policy and floating-point MOD) are already documented in the MVP Scope "Display base conversion rule" subsection.
|
||||
- Stand up the minimal Go module + directory layout (`internal/calc`, `internal/ui`, `main.go`).
|
||||
- Implement the calc engine (very small for this scope) with good tests.
|
||||
- Build the first visual spike focused on the exact "large number + small mode row" display the user described, plus a basic keypad grid, using gostations lipgloss idioms + centering.
|
||||
- Wire just enough keys to do a full add/sub/mul/div/mod cycle and see bases switch the display.
|
||||
|
||||
Once that feels right, we can talk about wiring it into the REXX launcher (probably under a new letter or eventually `l`).
|
||||
|
||||
The spec has been updated in place to match exactly what you described in this message. Let me know the answers to any of the remaining questions (or changes), and whether you want me to create the Go skeleton + first engine or rendering spike next.
|
||||
|
||||
---
|
||||
*Spec updated 2026 to reflect narrowed MVP and galculator two-row display.*
|
||||
Loading…
Reference in New Issue
Block a user