jeo-code 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,7 +55,20 @@ export function renderWelcome(d: WelcomeData): string[] {
55
55
  return [ `jeo v${d.version} · ${d.model}` ];
56
56
  }
57
57
 
58
- const W = Math.min(100, cols - 2);
58
+ // The banner hugs a NATURAL hero width so it reads as a proportional box — the
59
+ // grand DNA-claw framed by even margins — instead of stretching into a mostly
60
+ // empty rectangle on wide terminals. When the terminal is SMALLER than that
61
+ // initial/native width it shrinks proportionally: the box narrows and the claw
62
+ // steps grand→compact (below) so the art keeps its shape and never clips.
63
+ const grandWidth = Math.max(...DNA_CLAW_ART_GRAND.map(l => l.length));
64
+ // Title rides ON the top border: `─── jeo v{version} · JEO forge ───`. Defined
65
+ // once here so the natural-width calc and the border render below can't drift.
66
+ const titleDashes = 3;
67
+ const titleLabel = ` jeo v${d.version} · JEO forge `;
68
+ const titleWidth = titleDashes + titleLabel.length;
69
+ // grand art (36) + a 6-col margin each side, never narrower than the title.
70
+ const naturalInner = Math.max(grandWidth + 12, titleWidth + 2);
71
+ const W = Math.min(cols - 2, naturalInner + 2);
59
72
  const inner = W - 2;
60
73
 
61
74
  const BOX_UNICODE = { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" };
@@ -67,9 +80,9 @@ export function renderWelcome(d: WelcomeData): string[] {
67
80
  const lit = useColor ? (d.accent ?? chalk.gray) : (s: string) => s;
68
81
  const shadow = useColor ? (d.accentShadow ?? ((s: string) => chalk.dim(chalk.gray(s)))) : (s: string) => s;
69
82
 
70
- // Title text: ─── jeo v{version} · JEO forge ─── (bold for contrast against the border)
71
- const dashStr = g.h.repeat(3);
72
- const titleLabel = ` jeo v${d.version} · JEO forge `;
83
+ // Title text: ─── jeo v{version} · JEO forge ─── (bold for contrast against the border).
84
+ // `titleDashes`/`titleLabel` were fixed above so width and render stay in sync.
85
+ const dashStr = g.h.repeat(titleDashes);
73
86
  const titleHead = `${dashStr}${titleLabel}`;
74
87
  let topBorderLine: string;
75
88
  if (titleHead.length + 2 > inner) {
@@ -87,7 +100,6 @@ export function renderWelcome(d: WelcomeData): string[] {
87
100
 
88
101
  // Grand symbol when the box is wide enough; compact DNA Claw otherwise.
89
102
  const colorLevel = useColor ? detectColorLevel(process.env, isTTY()) : ColorLevel.None;
90
- const grandWidth = Math.max(...DNA_CLAW_ART_GRAND.map(l => l.length));
91
103
  const grand = inner >= grandWidth;
92
104
  const artLines = renderDnaClaw({
93
105
  color: useColor,
@@ -29,6 +29,7 @@ export class Renderer {
29
29
  private cols: () => number;
30
30
  private prev: string[] = [];
31
31
  private prevCols?: number;
32
+ private prevRows?: number;
32
33
  private readonly reserve: boolean;
33
34
  // Stale rows left on screen by the previous frame after insertAbove() dropped the
34
35
  // baseline; the next render() must EL-clear any of them beyond the new frame.
@@ -45,10 +46,13 @@ export class Renderer {
45
46
 
46
47
  render(lines: string[]): void {
47
48
  const currentCols = this.cols();
48
- if (this.prevCols !== undefined && this.prevCols !== currentCols) {
49
+ const currentRows = size().rows;
50
+ if ((this.prevCols !== undefined && this.prevCols !== currentCols) ||
51
+ (this.prevRows !== undefined && this.prevRows !== currentRows)) {
49
52
  this.clear();
50
53
  }
51
54
  this.prevCols = currentCols;
55
+ this.prevRows = currentRows;
52
56
 
53
57
  const next = lines.map(line => truncate(line, currentCols));
54
58
  // Rows physically occupied by the prior frame — or recorded by reset() when the
@@ -61,7 +65,7 @@ export class Renderer {
61
65
  let cursorRow = 0;
62
66
  let out = "";
63
67
 
64
- if (this.reserve && next.length > occupied && next.length <= Math.max(1, size().rows)) {
68
+ if (this.reserve && next.length > occupied && next.length <= Math.max(1, currentRows)) {
65
69
  // The cursor rests on the frame's first row (the anchor). Walk to the last
66
70
  // currently-occupied row, emit one newline per missing row (scrolling the
67
71
  // viewport when at the bottom margin), then hop back up to the — possibly
@@ -22,6 +22,13 @@ export function clearToEnd(): string {
22
22
  return `${ESC}0J`;
23
23
  }
24
24
 
25
+ /** Full reset of the visible screen: erase the screen (2J), erase the scrollback
26
+ * buffer (3J), and home the cursor (H). This is the gjc-style "fresh start" clear —
27
+ * use it at launch and for `/clear`, NEVER mid-turn (it would flood tmux scrollback). */
28
+ export function clearScreen(): string {
29
+ return `${ESC}2J${ESC}3J${ESC}H`;
30
+ }
31
+
25
32
  export function hideCursor(): string {
26
33
  return `${ESC}?25l`;
27
34
  }