Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [3.6.0] - 2026-06-12

### Changed

- **`classic` and `bold` redesigned as NATIVE 5-row fonts with distinct glyph
shapes — no longer the `block` font re-inked with shade characters.** `classic`
now draws cornered, open "rounded-terminal" numerals; `bold` draws heavy-slab
numerals with filled corners. Both are inked with solid `█` and distinguished
from `block` (and from each other) by GLYPH SHAPE and stroke weight, so cycling
the three solid styles with `d` shows a clear, unambiguous change. They keep the
exact 5-row × 4-col `block` footprint, so cycling never shifts the layout and
both render their own glyphs at every window size — including a minimized window
with session metadata — with no fallback to `block` and no towering.

### Fixed

- **`classic`/`bold` no longer look almost identical to `block` when cycling.** In
v3.5.0–v3.5.1 the two styles were `block` re-inked with `░` / `▓` shades, which
read too close to the full `█` block (bold especially), so alternating among the
three solid styles produced little observable change. Distinct glyph shapes
replace the shade approach, which was geometrically incapable of differentiating
three solid styles at the locked 5×4 footprint.

## [3.5.1] - 2026-06-12

### Changed
Expand Down Expand Up @@ -407,7 +430,8 @@ ANSI, zero new runtime dependencies, instant cold start.
`FLOWCLOCK_CONFIG_DIR` / `FLOWCLOCK_DATA_DIR` overrides.
- Color themes: `neon` (default), `amber`, `blue`, `mono`.

[Unreleased]: https://github.com/aedneth/flowclock-cli/compare/v3.5.1...HEAD
[Unreleased]: https://github.com/aedneth/flowclock-cli/compare/v3.6.0...HEAD
[3.6.0]: https://github.com/aedneth/flowclock-cli/compare/v3.5.1...v3.6.0
[3.5.1]: https://github.com/aedneth/flowclock-cli/compare/v3.5.0...v3.5.1
[3.5.0]: https://github.com/aedneth/flowclock-cli/compare/v3.4.1...v3.5.0
[3.4.1]: https://github.com/aedneth/flowclock-cli/compare/v3.4.0...v3.4.1
Expand Down
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ Ratio 1:4.3 focus:rest
`minimal` at every scale, including scale 1 (minimized windows).
- **`minimal`** — clean **light** line digits in box-drawing strokes (`┌─┐ │
└─┘`, airy `○` colon). Scales crisply at every size.
- **`classic`** — solid terminal numerals in a **light shade weight** (`░`),
sharing the exact 5-row × 4-col footprint of every other style.
- **`bold`** — solid terminal numerals in a **heavy shade weight** (`▓`),
- **`classic`** — solid **cornered "rounded-terminal" numerals** (a distinct,
stylized glyph shape), sharing the exact 5-row × 4-col footprint of every
other style.
- **`bold`** — solid **heavy-slab numerals** (a distinct, heavier glyph shape),
sharing the exact 5-row × 4-col footprint of every other style.

The goal sits **centered** above the counter and the focus/break metadata
Expand All @@ -147,12 +148,12 @@ Ratio 1:4.3 focus:rest
█ █ █ █ ┃ ┃ ┃ ┃ ║ ║ ║ ║
████ ████ ┗━━┛ ┗━━┛ ╚══╝ ╚══╝

minimal classic bold
┌──┐ ┌──┐ ░░░ ░░░░ ▓▓▓ ▓▓▓▓
│ │ │ │ ░ ░ ░ ░ ▓ ▓ ▓ ▓
│ │ │ │ ░░░░░ ░░░░░ ▓▓▓▓▓ ▓▓▓▓▓
└──┘ └──┘ ░ ░ ░ ░ ▓ ▓ ▓ ▓
░░░░░ ░░░░░ ▓▓▓▓▓ ▓▓▓▓▓
minimal classic bold
┌──┐ ┌──┐ ██ ██ ████ ████
│ │ │ │ █ █ █ ██ █ ██
│ │ │ │ █ █ █ █ ███
└──┘ └──┘ █ █ █ █ ██ ██
██ ███ ████ ████
```

- **Themes** — `neon` (default), `amber`, `blue`, `mono`. Set the default with
Expand Down Expand Up @@ -256,19 +257,19 @@ so they stay prominent without overshadowing the metadata:
at every scale (including scale 1 / minimized windows).
- **`minimal`** — clean **light** line digits in box-drawing strokes (`┌─┐ │`),
airy `○` colon. Scales crisply at every size; lighter feel than `simple`.
- **`classic`** — solid terminal numerals in a **light shade weight** (`░`),
sharing the exact 5-row × 4-col footprint of every other style.
- **`bold`** — solid terminal numerals in a **heavy shade weight** (`▓`),
- **`classic`** — solid **cornered "rounded-terminal" numerals** (a distinct,
stylized glyph shape), sharing the exact 5-row × 4-col footprint of every other style.
- **`bold`** — solid **heavy-slab numerals** (a distinct, heavier glyph shape),
sharing the exact 5-row × 4-col footprint of every other style.

Press **`d`** (or run `display` from the palette) to cycle
`block → simple → outline → minimal → classic → bold`, and **`t`** to cycle the
theme. Both choices are **saved to your config** as your new default — the same
as running `config set displayStyle classic` / `config set theme neon`. The
public default stays `block`; the rest are opt-in alternates. All six styles
share the same 5-row footprint — `classic`/`bold` are shade-weight variants of
`block` (`░` light / `▓` heavy), so cycling styles never changes the counter's
size or covers the session goal.
share the same 5-row footprint — `classic`/`bold` are native distinct-shape
5-row fonts (cornered "rounded-terminal" classic / heavy-slab bold), so cycling
styles never changes the counter's size or covers the session goal.

### Live session controls

Expand Down Expand Up @@ -402,7 +403,7 @@ Stored at `config.json` in your config dir (`flowclock config path`). Keys:
| Key | Default | Meaning |
| --- | --- | --- |
| `theme` | `neon` | `neon` · `amber` · `blue` · `mono` |
| `displayStyle` | `block` | Counter look: `block` solid (default), `simple` heavy line digits, `outline` hollow silhouette in double-line chars (`╔═╗`) distinct from `minimal`, `minimal` light line digits, `classic` light-shade `░` solid numerals, `bold` heavy-shade `▓` — all share the exact 5-row footprint; cycle live with `d` |
| `displayStyle` | `block` | Counter look: `block` solid (default), `simple` heavy line digits, `outline` hollow silhouette in double-line chars (`╔═╗`) distinct from `minimal`, `minimal` light line digits, `classic` cornered/rounded solid numerals (distinct shape), `bold` heavy-slab solid numerals (distinct shape) — all share the exact 5-row footprint; cycle live with `d` |
| `showControls` | `true` | show the controls footer (`--zen` overrides) |
| `dailyFocusGoalS` | `14400` | daily focus goal in seconds (drives maximization %) |
| `keybindings.{pause,break,category,reset,quit}` | `p` `b` `c` `r` `q` | in-session keys |
Expand Down Expand Up @@ -437,6 +438,7 @@ on-disk schema is **v3**; migrations are non-destructive.
| **v3.4.1** ✅ | `outline` double-line box-drawing chars (`╔═╗`) — distinct from `minimal` at every scale; uniform `classic`/`bold` footprint — height capped to 5-row reference, degrades to `block` in short windows |
| **v3.5.0** ✅ | `classic`/`bold` redesigned as 5-row shade weights (`▒`/`▓`) for exact footprint parity with all styles — no more towering, goal covering, or silent fallback to `block` |
| **v3.5.1** ✅ | `classic` lightened to `░` shade so block `█` / classic `░` / bold `▓` read as three distinct surfaces while keeping exact 5-row footprint parity |
| **v3.6.0** ✅ | `classic`/`bold` redesigned as native 5-row fonts with distinct glyph shapes (cornered `classic` / heavy-slab `bold`, both solid) — replaces the v3.5.x shade-fill approach so the three solid styles read as clearly distinct while keeping exact footprint parity |
| **next** | `flowclock sync` — push `sessions.json` to a self-hosted/cloud endpoint; recurring goals; dashboard filters |
| **later** | Per-goal analytics deep-dives, calendar heatmap, Homebrew tap |

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flowclock-cli",
"version": "3.5.1",
"version": "3.6.0",
"description": "Flowtime count-up terminal timer (HUD, not Pomodoro) with silent session logging — agent-native, like gh and vercel.",
"type": "module",
"license": "AGPL-3.0-or-later",
Expand Down
90 changes: 69 additions & 21 deletions src/lib/bigfont.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ export function computeSessionScale(
/**
* Pick the counter scale for `style`. Every display style now shares the exact
* 5-row × 4-col `block` footprint (the tall 9-row `classic`/`bold` letterforms
* were retired in favour of 5-row shade weights — see STYLE_METRICS), so a
* were retired in favour of native 5-row letterforms — see STYLE_METRICS), so a
* single scale gives ALL styles an identical rendered size. Cycling styles with
* `d` therefore never makes the counter jump in size, and no style-specific
* "taming" is needed — this is now a thin, style-aware wrapper over
Expand All @@ -339,16 +339,17 @@ export function uniformCounterScale(
}

// ---------------------------------------------------------------------------
// Solid shade-weight "classic" / "bold" fonts — terminal numerals
// Native distinct-shape "classic" / "bold" fonts — terminal numerals
// ---------------------------------------------------------------------------

/**
* Per-style glyph geometry. EVERY style shares the original 5-row, 4-wide-digit,
* 1-wide-colon footprint, so the reserve-first scaling maths and small-panel
* fallback thresholds are byte-for-byte identical across all six styles. The
* styles differ only in how each filled cell is INKED (see `renderCounter`):
* `block` full `█`, `classic`/`bold` the light `░` / heavy `▓` shade
* weights, and `simple`/`outline`/`minimal` box-drawing line-art.
* fallback thresholds are byte-for-byte identical across all six styles.
* `block`, `classic`, and `bold` are three solid fonts with distinct glyph shapes
* (all inked with `█`): block uses blunt filled rectangles, classic uses cornered/
* open terminal numerals, and bold uses heavy-slab numerals. `simple`, `outline`,
* and `minimal` use box-drawing line-art and differ by stroke weight and style.
*/
interface StyleMetrics {
rows: number;
Expand Down Expand Up @@ -382,30 +383,77 @@ export function styleWidth(style: DisplayStyle, time: string, scale = 1): number
return w * scale;
}

/** The shade glyph each solid weight inks its filled cells with. */
const CLASSIC_FILL = "░"; // light shade — the airy `classic` weight (clearly distinct from solid block)
const BOLD_FILL = "▓"; // heavy shade — the `bold` weight
// 4-wide × 5-tall CORNERED/OPEN terminal numerals (the `classic` style). A
// stylized, rounded-terminal letterform: notched corners and open interiors give
// it a softer silhouette clearly distinct from block's blunt rectangles, while
// keeping block's exact 5-row × 4-col footprint. Uses only █ so it cell-repeat
// scales as cleanly as the block font.
const CLASSIC_GLYPHS: Record<string, string[]> = {
"0": [" ██ ", "█ █", "█ █", "█ █", " ██ "],
"1": [" ██ ", " █ ", " █ ", " █ ", " ███"],
"2": [" ██ ", "█ █", " █ ", " █ ", "████"],
"3": ["███ ", " █", " ██ ", " █", "███ "],
"4": ["█ █", "█ █", " ███", " █", " ██"],
"5": ["████", "█ ", "███ ", " █", "███ "],
"6": [" ██ ", "█ ", "███ ", "█ █", " ██ "],
"7": ["███ ", " █ ", " █ ", " █ ", "█ "],
"8": [" ██ ", "█ █", " ██ ", "█ █", " ██ "],
"9": [" ██ ", "█ █", " ███", " █", " ██ "],
":": [" ", "█", " ", "█", " "],
};

// 4-wide × 5-tall HEAVY-SLAB terminal numerals (the `bold` style). The same
// terminal-numeral family as `classic` but with filled corners and extra interior
// ink, so it reads as a visibly heavier weight than both block and classic — yet
// keeps block's exact 5-row × 4-col footprint and scales cleanly (█ only).
const BOLD_GLYPHS: Record<string, string[]> = {
"0": ["████", "██ █", "█ █", "█ ██", "████"],
"1": [" ██ ", "███ ", " ██ ", " ██ ", "████"],
"2": ["████", " ██", "████", "██ ", "████"],
"3": ["████", " ██", " ███", " ██", "████"],
"4": ["█ ██", "█ ██", "████", " ██", " ██"],
"5": ["████", "██ ", "████", " ██", "████"],
"6": ["████", "██ ", "████", "█ ██", "████"],
"7": ["████", " ██", " ██ ", " ██ ", " ██ "],
"8": ["████", "█ ██", "████", "█ ██", "████"],
"9": ["████", "█ ██", "████", " ██", "████"],
":": [" ", "█", " ", "█", " "],
};

/**
* Render the solid `block` geometry with a different fill glyph. `classic` and
* `bold` are weight variants of the default `block` font: they share its EXACT
* 5-row × 4-col cell grid (so every dimension, scale step and layout reservation
* is byte-for-byte identical to block), and differ only by inking each filled
* cell with a light `░` / heavy `▓` shade instead of the full `█`. Reusing
* `renderBigLines` guarantees the footprint can never drift from block's.
* Render a 5-row solid terminal-numeral font (classic/bold) by integer
* cell-repeat — crisp at every scale, no half-block artefacts and no footprint
* drift. Each digit is 4 cols, the colon 1 col, the gap 1 col (× scale), exactly
* matching block's geometry / `styleWidth`, so the reserve-first scaling maths and
* small-window thresholds are byte-for-byte identical across all six styles. These
* letterforms have DISTINCT shapes from block (cornered `classic`, heavy `bold`),
* so cycling styles with `d` shows a clear change without altering the footprint.
*/
function renderShaded(time: string, scale: number, fill: string): string[] {
return renderBigLines(time, scale).map((line) => line.replaceAll("█", fill));
function renderSolidGlyphs(time: string, scale: number, glyphs: Record<string, string[]>): string[] {
const totalRows = BIG_ROWS * scale;
const rows = Array.from({ length: totalRows }, () => "");
const chars = [...time];
chars.forEach((ch, idx) => {
const glyph = glyphs[ch] ?? [" ", " ", " ", " ", " "];
const sep = idx < chars.length - 1 ? " ".repeat(scale) : "";
for (let r = 0; r < BIG_ROWS; r++) {
const scaledRow = [...(glyph[r] ?? "")].map((c) => c.repeat(scale)).join("");
for (let s = 0; s < scale; s++) {
rows[r * scale + s] = (rows[r * scale + s] ?? "") + scaledRow + sep;
}
}
});
return rows;
}

/** Render the `classic` style — solid LIGHT shade (`░`) terminal numerals. */
/** Render the `classic` style — solid cornered/open terminal numerals (distinct shape from block). */
export function renderClassicLines(time: string, scale = 1): string[] {
return renderShaded(time, scale, CLASSIC_FILL);
return renderSolidGlyphs(time, scale, CLASSIC_GLYPHS);
}

/** Render the `bold` style — solid HEAVY shade (`▓`) terminal numerals. */
/** Render the `bold` style — solid heavy-slab terminal numerals (distinct shape from block). */
export function renderBoldLines(time: string, scale = 1): string[] {
return renderShaded(time, scale, BOLD_FILL);
return renderSolidGlyphs(time, scale, BOLD_GLYPHS);
}

/** Render `time` in the requested display style at the given scale. */
Expand Down
4 changes: 2 additions & 2 deletions src/schemas/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export type ThemeName = z.infer<typeof ThemeNameSchema>;
* - "simple" clean heavy box-drawing seven-segment digits
* - "outline" hollow DOUBLE-LINE silhouette glyphs (╔═╗ ║ ╚╝ — distinct at every scale)
* - "minimal" clean light box-drawing seven-segment digits (airy line font)
* - "classic" solid terminal numerals in a LIGHT shade weight (░)
* - "bold" solid terminal numerals in a HEAVY shade weight (▓)
* - "classic" solid cornered/rounded terminal numerals (distinct shape, native 5-row)
* - "bold" solid heavy-slab terminal numerals (distinct shape, heavier weight)
*/
export const DisplayStyleSchema = z.enum([
"simple",
Expand Down
7 changes: 4 additions & 3 deletions src/tui/views/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ interface CounterPlan {
* centered text line.
*
* EVERY style now shares the exact 5-row × 4-col `block` footprint (`classic`/
* `bold` are shade-weight variants of block, not the old taller letterforms), so
* `bold` are native 5-row letterforms with distinct glyph shapes — cornered
* `classic` / heavy-slab `bold` — not the old taller 9-row letterforms), so
* the requested style renders at every window size the others do. There is no
* tall-font case and no fall-back to a different style: cycling styles never
* changes the counter's footprint, and classic/bold are never silently swapped
Expand Down Expand Up @@ -223,8 +224,8 @@ export function renderSession(
// "simple" clean heavy box-drawing seven-segment line digits
// "outline" double-line box-drawing silhouette digits
// "minimal" light box-drawing seven-segment line digits
// "classic" solid LIGHT shade ░ terminal numerals
// "bold" solid HEAVY shade ▓ terminal numerals
// "classic" solid cornered/rounded terminal numerals (distinct shape, █)
// "bold" solid heavy-slab terminal numerals (distinct shape, █)
const rawLines = renderCounter(plan.style, time, scale);
counterLines = rawLines.map((line) => {
const padded = padTo(line, innerW, "center");
Expand Down
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/** Kept in sync with package.json "version" (asserted by a test). */
export const VERSION = "3.5.1";
export const VERSION = "3.6.0";
Loading
Loading