Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
50241b9
Create unstable_ansi.ts
BlackAsLight Jul 7, 2025
ff46743
Update unstable_ansi.ts
BlackAsLight Jul 7, 2025
7417e1a
fmt
BlackAsLight Jul 8, 2025
3df5988
include default setting for cursor style
BlackAsLight Jul 8, 2025
d8fe890
fix method to omit instead of calc number
BlackAsLight Jul 8, 2025
9a33735
add a million examples...
BlackAsLight Jul 8, 2025
6882b32
add export
BlackAsLight Jul 8, 2025
7c94569
fix spelling
BlackAsLight Jul 8, 2025
7aa8215
fix lint errors by adding missing jsdocs
BlackAsLight Jul 8, 2025
a610eaf
feat(cli): StaticLine
BlackAsLight Jul 9, 2025
d987f29
Merge branch 'main' into ansi
BlackAsLight Jul 9, 2025
ab34326
Merge branch 'main' into ansi_static_line
BlackAsLight Jul 9, 2025
886b852
convert(cli): Ansi class to separate functions and variables
BlackAsLight Jul 11, 2025
ae19743
Merge branch 'ansi' into ansi_static_line
BlackAsLight Jul 11, 2025
df3c7c7
update(cli): import statement to reflect ansi branch
BlackAsLight Jul 11, 2025
1eb5ab5
fix(cli): Missing Deno keyword
BlackAsLight Jul 11, 2025
4146773
switch(cli): Ansi codes due to slightly different functionality
BlackAsLight Jul 16, 2025
bf4a93b
feat(cli): StaticLine
BlackAsLight Jul 9, 2025
60c9da1
update(cli): import statement to reflect ansi branch
BlackAsLight Jul 11, 2025
e507dce
fix(cli): Missing Deno keyword
BlackAsLight Jul 11, 2025
e1a3cd0
Merge branch 'ansi_static_line' of https://github.com/BlackAsLight/st…
BlackAsLight Jul 16, 2025
da702c9
revert(cli): last changes to Ansi codes
BlackAsLight Jul 16, 2025
a95e205
Merge branch 'ansi' into ansi_static_line
BlackAsLight Jul 16, 2025
5523039
docs(cli): fixed for Ansi module
BlackAsLight Jul 17, 2025
327c117
change(cli): Ansi constants to capitalised snake case
BlackAsLight Jul 17, 2025
c96337e
Merge branch 'ansi' into ansi_static_line
BlackAsLight Jul 19, 2025
907208c
update(cli): static line code to match ansi branch
BlackAsLight Jul 19, 2025
3efac67
change(cli): param from `atTop` to `on`
BlackAsLight Jul 20, 2025
e12d57d
fix(cli): behaviour for releasing lines
BlackAsLight Jul 20, 2025
32d7d6e
fix(cli): behaviour for writing lines
BlackAsLight Jul 20, 2025
397961c
Merge branch 'main' into ansi_static_line
BlackAsLight Jul 22, 2025
1caadfb
fix(cli): error due to changes made in ansi
BlackAsLight Jul 22, 2025
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
1 change: 1 addition & 0 deletions cli/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"./unstable-prompt-select": "./unstable_prompt_select.ts",
"./unstable-prompt-multiple-select": "./unstable_prompt_multiple_select.ts",
"./unstable-spinner": "./unstable_spinner.ts",
"./unstable-static-line": "./unstable_static_line.ts",
"./unicode-width": "./unicode_width.ts"
}
}
158 changes: 158 additions & 0 deletions cli/unstable_static_line.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright 2018-2025 the Deno authors. MIT license.

import * as Ansi from "./unstable_ansi.ts";

let nextID = 0;
const linesAtTop: number[] = [];
const linesAtBottom: number[] = [];

const getRows = "Deno" in globalThis
// deno-lint-ignore no-explicit-any
? (): number => (globalThis as any).Deno.consoleSize().rows
// deno-lint-ignore no-explicit-any
: (): number => (globalThis as any).process.stderr.rows;
const write = "Deno" in globalThis
? (() => {
const encoder = new TextEncoder();
return (x: string) =>
// deno-lint-ignore no-explicit-any
(globalThis as any).Deno.stderr.write(encoder.encode(x));
})()
// deno-lint-ignore no-explicit-any
: (x: string) => (globalThis as any).process.stderr.write(x);

/**
* StaticLine is a class that assigns a line in the terminal for you to write
* to and that won't be overwritten by `console.log`.
*
* @example Usage
* ```ts ignore
* import { delay } from "@std/async/delay";
* import { StaticLine } from "@std/cli/unstable-static-line";
*
* const id = setInterval(() => console.log(Math.random()), 1000);
*
* const line = new StaticLine();
* await line.write("Hello World!");
* await delay(1500);
* await line.write("How are you?");
* await delay(1500);
* await line.write("Doing good?");
* await delay(1500);
* await line.releaseLine();
* ```
*/
export class StaticLine {
#id = ++nextID;
#atTop: boolean;
#lines: number[];
#released = false;
/**
* Constructs a new instance.
*
* @param on What side of the terminal the line should be on.
*/
constructor(on: "top" | "bottom" = "bottom") {
this.#atTop = on === "top";
this.#lines = this.#atTop ? linesAtTop : linesAtBottom;
this.#lines.push(this.#id);
const top = linesAtTop.length + 1;
const bottom = getRows() - linesAtBottom.length;
write(
(this.#atTop ? "" : Ansi.shiftUpAndInsert()) +
Ansi.setScrollableRegion(top, bottom) +
Ansi.setCursorPosition(bottom),
);
}

/**
* Overwrites the contents of the line.
*
* @param line The new content to write.
* @returns A promise that resolves when the line is written.
*
* @example Usage
* ```ts ignore
* import { delay } from "@std/async/delay";
* import { StaticLine } from "@std/cli/unstable-static-line";
*
* const id = setInterval(() => console.log(Math.random()), 1000);
*
* const line = new StaticLine();
* await line.write("Hello World!");
* await delay(1500);
* await line.write("How are you?");
* await delay(1500);
* await line.write("Doing good?");
* await delay(1500);
* await line.releaseLine();
* ```
*/
async write(line: string): Promise<void> {
if (this.#released) throw new ReferenceError("Line has been released");
await write(
Ansi.SAVE_CURSOR +
Ansi.DISABLE_ORIGIN_MODE +
Ansi.DISABLE_AUTO_WRAP +
Ansi.setCursorPosition(
this.#atTop
? linesAtTop.indexOf(this.#id) + 1
: getRows() - linesAtBottom.indexOf(this.#id),
) +
Ansi.ERASE_LINE +
line +
Ansi.RESTORE_CURSOR,
);
}

/**
* Releases the line so other content can use it.
*
* @returns A promise that resolves when the line is released.
*
* @example Usage
* ```ts ignore
* import { delay } from "@std/async/delay";
* import { StaticLine } from "@std/cli/unstable-static-line";
*
* const id = setInterval(() => console.log(Math.random()), 1000);
*
* const line = new StaticLine();
* await line.write("Hello World!");
* await delay(1500);
* await line.write("How are you?");
* await delay(1500);
* await line.write("Doing good?");
* await delay(1500);
* await line.releaseLine();
* ```
*/
async releaseLine(): Promise<void> {
if (this.#released) return;
this.#released = true;
const index = this.#lines.indexOf(this.#id);
this.#lines.splice(index, 1);
const rows = getRows();
const top = linesAtTop.length + 1;
const bottom = rows - linesAtBottom.length;
await write(
Ansi.SAVE_CURSOR +
(this.#atTop
? (top - index
? Ansi.setScrollableRegion(index + 1, top + 1) +
Ansi.shiftUpAndInsert()
: Ansi.setCursorPosition(index + 1) +
Ansi.ERASE_LINE)
: (rows - index - bottom
? Ansi.setScrollableRegion(
bottom,
rows - index,
) +
Ansi.shiftDownAndInsert()
: Ansi.setCursorPosition(rows) +
Ansi.ERASE_LINE)) +
Ansi.setScrollableRegion(top, bottom) +
Ansi.RESTORE_CURSOR,
);
}
}
Loading