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
2 changes: 1 addition & 1 deletion apps/benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"start": "bun src/main.ts"
},
"dependencies": {
"@profullstack/hqtui": "^0.10.3"
"@profullstack/hqtui": "^0.11.0"
}
}
4 changes: 2 additions & 2 deletions apps/demo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@profullstack/hqtui-demo",
"version": "0.10.3",
"version": "0.11.0",
"description": "The HQTUI reference dashboard: a btop-grade terminal system monitor. Runs on real system metrics or a deterministic simulation.",
"license": "MIT",
"type": "module",
Expand All @@ -27,7 +27,7 @@
"audit:scroll": "bun scripts/scrollaudit.ts"
},
"dependencies": {
"@profullstack/hqtui": "^0.10.3"
"@profullstack/hqtui": "^0.11.0"
},
"publishConfig": {
"access": "public"
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function parseArgs(argv: string[]): Options {
case "-h":
case "--help": printHelp(); process.exit(0);
case "-v":
case "--version": console.log("hqtui-demo 0.10.3"); process.exit(0);
case "--version": console.log("hqtui-demo 0.11.0"); process.exit(0);
}
}
return options;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export default async function Home() {
High Quality Terminal UI for TypeScript, Rust, Go, Python, Zig and C++
</p>
<Badge variant="secondary" className="mb-5 font-mono text-xs">
v0.10.3 · {COUNT} language demos · MIT
v0.11.0 · {COUNT} language demos · MIT
</Badge>
<p className="text-balance text-3xl font-bold tracking-tight sm:text-5xl">
Terminal dashboards that
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"dependencies": {
"@base-ui/react": "1.7.0",
"@profullstack/footer": "latest",
"@profullstack/hqtui": "^0.10.3",
"@profullstack/hqtui": "^0.11.0",
"@profullstack/libsql-pg": "^0.1.3",
"@profullstack/x402-gateway": "^0.11.0",
"class-variance-authority": "0.7.1",
Expand Down
10 changes: 5 additions & 5 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"version": "0.1.0",
"type": "module",
"dependencies": {
"@profullstack/hqtui": "^0.10.3"
"@profullstack/hqtui": "^0.11.0"
}
}
27 changes: 26 additions & 1 deletion packages/hqtui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,38 @@ activity, sessions, services and the full widget catalogue.
| | |
|---|---|
| **Layout** | rows, columns, grid with spans, `"40%"`, `"2fr"`, `auto`, min/max, padding, gaps, clipping, responsive breakpoints |
| **Widgets** | panel, table, tree, list, log viewer, key/values, meter, gauge, donut, progress, sparkline, line/area/multi-series graph, histogram, heat bar, tabs, status bar, button, checkbox, toggle, radio, select, text input, modal, command palette, tooltip, badge, spinner, divider |
| **Widgets** | panel, table, data grid, tree, list, log viewer, key/values, meter, gauge, donut, progress, sparkline, line/area/multi-series graph, histogram, heat bar, tabs, status bar, button, checkbox, toggle, radio, select, text input, modal, command palette, tooltip, badge, spinner, divider |
| **Graphics** | Braille canvas (2×4 pixels per cell), block/half-block/quadrant/ASCII modes, gradients, software alpha blending |
| **Color** | 24-bit truecolor, automatic 256 and 16-colour quantization, `NO_COLOR`, monochrome and high-contrast modes |
| **Themes** | dark (default), dracula, nord, tokyo night, gruvbox, matrix, monochrome, high contrast, light — plus `defineTheme()` |
| **Input** | normalized keys with modifiers, SGR mouse (click, drag, scroll, move), bracketed paste, focus events, Tab focus traversal |
| **Testing** | headless renderer: `renderToText`, `renderToScreen`, `renderToAnsi`, `renderToHtml` — no TTY required |

## Data grid

`table` fits every column into the width. A **data grid** keeps each column readable
and scrolls on both axes instead: a frozen header, a row-number gutter, a cell cursor,
a sort marker, and arrows when columns are off to either side. It is the widget a CSV
viewer, a query result or a wide log needs.

```ts
ui.dataGrid({
columns: header.map((title) => ({ title })),
rows, // string[][], parsed however you like
order, // optional: row indices, sorted/filtered, never copied
selectedRow, selectedColumn, // the cursor; the grid scrolls to keep it on screen
sort: { column: 2, descending: true },
onSelectCell: (row, column) => { selectedRow = row; selectedColumn = column; },
onHeaderClick: (column) => toggleSort(column),
onScroll: (delta) => { selectedRow += delta * 3; },
});
```

The grid only draws: parsing, sorting and filtering stay with the caller, which keeps
the library free of dependencies. `onLayout` reports what fit (`capacity`, visible
columns) so page keys move by a real screenful. `dataGridWidths`, `dataGridLayout` and
`dataGridCellAt` are exported for callers that draw their own.

## Icons

The [OpenIcon](https://logicsrc.com/openicon) pack is built in and on by
Expand Down
2 changes: 1 addition & 1 deletion packages/hqtui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@profullstack/hqtui",
"version": "0.10.3",
"version": "0.11.0",
"description": "High Quality Terminal UI for TypeScript. btop-grade dashboards with a one-import API, dark by default, zero runtime dependencies.",
"license": "MIT",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion packages/hqtui/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { detectCapabilities } from "./capabilities.ts";
import { themeList, themes } from "./theme.ts";
import { BrailleCanvas } from "./graphics/braille.ts";

const VERSION = "0.10.3";
const VERSION = "0.11.0";

function help(): void {
console.log(`hqtui ${VERSION} — High Quality Terminal UI for TypeScript
Expand Down
2 changes: 1 addition & 1 deletion packages/hqtui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
export { App, createApp, type AppOptions, type RenderArgs, type RenderFn, type FrameStats } from "./app.ts";
export { Container, GridContainer } from "./ui.ts";
export type {
RenderContext, ContainerOptions, PanelOptions, GridOptions, CellOptions, HitRegion, HitEvent, ScrollHandlers, MarkdownCopy,
RenderContext, ContainerOptions, PanelOptions, GridOptions, CellOptions, HitRegion, HitEvent, ScrollHandlers, DataGridHandlers, MarkdownCopy,
} from "./ui.ts";
export { dispatchHit, countClicks, DOUBLE_CLICK_MS } from "./ui.ts";

Expand Down
42 changes: 42 additions & 0 deletions packages/hqtui/src/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ export interface RenderContext {
}

/** Mouse behaviour shared by every scrollable widget. */
export interface DataGridHandlers {
/** Wheel over the grid. `delta` is -1 up, 1 down. */
onScroll?: (delta: number) => void;
/** Click on a body cell: its position in the displayed order, and its column (-1 on the gutter). */
onSelectCell?: (row: number, column: number) => void;
/** Double-click on a body cell, after `onSelectCell` for the same press. */
onActivateCell?: (row: number, column: number) => void;
/** Click on a column title. */
onHeaderClick?: (column: number) => void;
onFocus?: () => void;
}

export interface ScrollHandlers {
/** Wheel over this widget. `delta` is -1 up, 1 down. */
onScroll?: (delta: number) => void;
Expand Down Expand Up @@ -533,6 +545,36 @@ export class Container {
}, this.sizeOf(options, "fill", intrinsic));
}

/**
* A data grid (widgets/data-grid.ts): cells that scroll on both axes under
* a cell cursor. Clicks are reported as cells, in the displayed order, so a
* sorted or filtered view needs no translating; a header click names the
* column, which is where a sort toggle goes.
*/
dataGrid(options: W.DataGridOptions & ContainerOptions & DataGridHandlers): this {
return this.add((s) => {
const frame = W.drawDataGrid(s, options);
const { onScroll, onSelectCell, onActivateCell, onHeaderClick, onFocus } = options;
if (!onScroll && !onSelectCell && !onActivateCell && !onHeaderClick && !onFocus) return;
this.ctx.hit({
rect: s.hitRect(),
onScroll: onScroll ? (delta) => onScroll(delta) : undefined,
onClick: (x, y, _button, clicks = 1) => {
onFocus?.();
const column = W.dataGridCellAt(frame, x);
if (y === 0) {
if (column >= 0) onHeaderClick?.(column);
return;
}
const row = frame.rowOffset + y - 1;
if (row >= frame.total) return;
onSelectCell?.(row, column);
if (clicks >= 2) onActivateCell?.(row, column);
},
});
}, this.sizeOf(options, "fill", (options.order?.length ?? options.rows.length) + 1));
}

list(options: W.ListOptions & ContainerOptions & ScrollHandlers): this {
return this.add((s) => {
W.drawList(s, options);
Expand Down
Loading
Loading