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: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# A generated copy of ports/c, ports/cpp and ports/bindings (ports/nim/tools/vendor.sh).
ports/nim/src/hqtui/engine/** linguist-generated=true
48 changes: 48 additions & 0 deletions .github/workflows/bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,54 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
# What `nimble install hqtui` gets: the vendored engine must match ports/c,
# ports/cpp and ports/bindings, `nimble test` must pass, and an installed copy
# must build a program with no checkout or shared library around it.
nim-package:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: jdx/mise-action@v5.0.0
with:
install: false
- name: Install Nim
run: mise install nim@2.2.12
- name: Vendored engine matches its sources
shell: bash
run: |
sh ports/nim/tools/vendor.sh
changes=$(git status --porcelain --untracked-files=all -- ports/nim/src/hqtui/engine)
if [ -n "$changes" ]; then
printf '%s\n' "$changes"
git --no-pager diff --stat -- ports/nim/src/hqtui/engine
echo "::error::ports/nim/src/hqtui/engine is stale. Run sh ports/nim/tools/vendor.sh and commit the result."
exit 1
fi
- name: nimble test
working-directory: ports/nim
run: mise exec nim@2.2.12 -- nimble test
- name: nimble install, then build against the installed package
shell: bash
run: |
scratch="$RUNNER_TEMP/nimble-install"
mkdir -p "$scratch/app"
(cd ports/nim && mise exec nim@2.2.12 -- nimble install -y --nimbleDir:"$scratch/nimble")
cat > "$scratch/app/hello.nim" <<'EOF'
import std/strutils, hqtui
let ui = newUI()
ui.panel("Installed", proc(p: UI) = p.meter(0.5, %*{"label": "CPU"}))
var scene = newScene(30, 4)
scene.set(ui)
let frame = scene.render()
doAssert "Installed" in frame and "CPU" in frame, frame
echo frame
EOF
cd "$scratch/app"
mise exec nim@2.2.12 -- nim c -r --hints:off --nimblePath:"$scratch/nimble/pkgs2" hello.nim
bindings:
strategy:
fail-fast: false
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,9 @@ cd ports/python && python examples/dashboard.py # Python
cd ports/zig && zig build run-dashboard # Zig
```

C++, Ruby, PHP, Perl and Nim build a native library first; their READMEs have the
CMake invocation.
C++, Ruby, PHP and Perl build a native library first; their READMEs have the
CMake invocation. Nim compiles the engine into the program itself:
`nim c -r ports/nim/examples/dashboard.nim`.

### Traffic — every protocol in and out of the host

Expand Down Expand Up @@ -430,7 +431,7 @@ and the same escape bytes, byte for byte.
| [C++](ports/cpp/) | native demo on the C core; library API experimental | none | [README](ports/cpp/README.md) |
| [Ruby](ports/bindings/) | binding over the shared C ABI | the native engine | [README](ports/bindings/README.md) |
| [PHP](ports/bindings/) | binding over the shared C ABI | the native engine | [README](ports/bindings/README.md) |
| [Nim](ports/nim/) | binding over the shared C ABI | the native engine | [README](ports/nim/README.md) |
| [Nim](ports/nim/) | binding over the shared C ABI, engine compiled in; `nimble install https://github.com/profullstack/hqtui?subdir=ports/nim` | none (vendored engine) | [README](ports/nim/README.md) |
| [Perl](ports/bindings/) | binding over the shared C ABI | the native engine | [README](ports/bindings/README.md) |

The four native ports are ports, not bindings: no Node is in the picture at
Expand Down
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.7.0"
"@profullstack/hqtui": "^0.7.1"
}
}
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.7.0",
"version": "0.7.1",
"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.7.0"
"@profullstack/hqtui": "^0.7.1"
},
"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.7.0"); process.exit(0);
case "--version": console.log("hqtui-demo 0.7.1"); process.exit(0);
}
}
return options;
Expand Down
5 changes: 3 additions & 2 deletions apps/web/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,12 @@ export default async function Docs() {
<P>
Ruby, PHP, Perl and Nim are bindings, not independent renderer ports. Their widget
APIs submit batched scene updates; the shared ten-screen demo, collectors and
terminal loop execute inside the calling runtime. They need GCC/Clang and CMake.
terminal loop execute inside the calling runtime. They need GCC/Clang, and Ruby,
PHP and Perl also need CMake.
Ruby needs Fiddle; Perl needs FFI::Platypus (the launcher installs it into its
private cache using cpanm if missing). PHP uses a small native adapter built
with matching php-config/development headers, or FFI when available.
Nim uses its built-in C FFI and needs Nim 2.2.4 or newer.
Nim compiles the engine into the program from source and needs Nim 2.2.4 or newer.
Mise supplies pinned Ruby/Perl/Nim, prebuilt PHP via its Conda backend, and CMake.
No system packages or global gems/CPAN modules are installed by the launcher.
</P>
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 @@ -158,7 +158,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.7.0 · {COUNT} language demos · MIT
v0.7.1 · {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 @@ -9,7 +9,7 @@
},
"dependencies": {
"@base-ui/react": "1.7.0",
"@profullstack/hqtui": "^0.7.0",
"@profullstack/hqtui": "^0.7.1",
"@profullstack/libsql-pg": "^0.1.3",
"class-variance-authority": "0.7.1",
"clsx": "2.1.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.7.0"
"@profullstack/hqtui": "^0.7.1"
}
}
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.7.0",
"version": "0.7.1",
"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.7.0";
const VERSION = "0.7.1";

function help(): void {
console.log(`hqtui ${VERSION} — High Quality Terminal UI for TypeScript
Expand Down
11 changes: 11 additions & 0 deletions packages/hqtui/test/version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ test("every version badge on the site matches the library", () => {
for (const v of found) assert.equal(v, versionOf("packages", "hqtui"), `page.tsx has ${v}`);
});

test("the Nim package is versioned with the library", () => {
// Nimble resolves `nimble install hqtui` to the newest vX.Y.Z tag of this
// repository and reads ports/nim/hqtui.nimble there, so the two must agree
// or the registry installs a package that reports another version.
const library = versionOf("packages", "hqtui");
const nimble = allMatches(read("ports", "nim", "hqtui.nimble"), /^version\s*=\s*"([^"]+)"/gm);
assert.deepEqual(nimble, [library], `hqtui.nimble has ${nimble.join(", ")}`);
const source = allMatches(read("ports", "nim", "src", "hqtui.nim"), /^const version\*\s*=\s*"([^"]+)"/gm);
assert.deepEqual(source, [library], `hqtui.nim has ${source.join(", ")}`);
});

/**
* Whether `version` satisfies `range`, for the range forms this repo uses.
* Returns null for anything else so the caller fails loudly rather than
Expand Down
11 changes: 6 additions & 5 deletions ports/bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ uses the C++ layout/widgets plus the C11 framebuffer/diff renderer. The JSON tre
is retained and only reparsed on `set`, not on every render. No per-cell FFI calls,
JSON subprocess pipe, JavaScript runtime or child demo executable is involved.

Nim uses its built-in C FFI and requires Nim 2.2.4+; see [Nim](../nim/README.md).
Nim compiles the engine from source into the program (no shared library) and
requires Nim 2.2.4+; it installs with Nimble, see [Nim](../nim/README.md).
Ruby uses Fiddle. Perl uses FFI::Platypus. PHP prefers a tiny compiled Zend adapter
to the same C ABI (so FFI need not be enabled); an FFI path is available otherwise.
The full ten-screen example calls the shared C++ demo **inside the language VM**,
Expand Down Expand Up @@ -57,10 +58,10 @@ matching the PHP binary, then build target `hqtui_php`. Run PHP with
to the current invocation and does not edit php.ini. Without the adapter, PHP
must provide its FFI extension and permit CLI FFI calls.

The loaders find this conventional checkout build or use `HQTUI_NATIVE_LIB` to
select an explicitly installed shared library. Copying a binding package alone
does not install the engine. RubyGems/Composer/CPAN metadata is included, but these
new packages have **not** been published to those registries.
The Ruby, PHP and Perl loaders find this conventional checkout build or use
`HQTUI_NATIVE_LIB` to select an explicitly installed shared library. Copying one of
those packages alone does not install the engine. RubyGems/Composer/CPAN metadata is
included, but those packages have **not** been published to their registries.

## API and tests

Expand Down
38 changes: 26 additions & 12 deletions ports/nim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,36 @@ Nim 2.2.4+ bindings to the shared C/C++ engine, with no Nim package dependencies
Build custom widget trees in Nim or run the full ten-screen dashboard in the same
process. Linux and macOS are supported; live metrics require Linux.

## Build and run
## Install

From the repository root, with Nim, a C/C++17 compiler and CMake 3.20+ installed:
```sh
nimble install https://github.com/profullstack/hqtui?subdir=ports/nim
```

`nimble install hqtui` works the same way once the package is listed in
[nim-lang/packages](https://github.com/nim-lang/packages). Nimble picks the
newest `vX.Y.Z` tag, and the package version follows the hqtui release.

The engine is compiled from source into your program: the package carries a
copy of `ports/c`, `ports/cpp` and `ports/bindings` in `src/hqtui/engine`, built
through Nim's `compile` pragma. You need Nim 2.2.4+ and a C11/C++17 compiler
(GCC or Clang); there is no CMake step, no shared library and nothing to set at
run time. The first build of a program compiles the engine (about 20 seconds);
later builds reuse the objects in its nimcache.

## Build and run from a checkout

```sh
cmake -S ports/cpp -B ports/cpp/build-bindings \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF -DHQTUI_BUILD_BINDINGS=ON
cmake --build ports/cpp/build-bindings --target hqtui_bindings --parallel 2
nim c -r ports/nim/examples/hello.nim
nim c -r -d:release ports/nim/examples/dashboard.nim --sim
nim c -r ports/nim/examples/widgets.nim
cd ports/nim && nimble test
```

The loader uses that checkout build by default. Set `HQTUI_NATIVE_LIB` to the
absolute path of `libhqtui_bindings.so` (Linux) or `libhqtui_bindings.dylib`
(macOS) when installing or moving the executable. The library must remain
available at runtime. Nimble metadata is included; this package is not published.
`src/hqtui/engine` is generated. After changing the C, C++ or binding sources,
run `sh ports/nim/tools/vendor.sh`; CI fails when the copy differs.

Once this change is on main, the latest-source launcher supports both forms:
The latest-source launcher runs the demo without a checkout:

```sh
curl -fsSL https://hqtui.com/demo.sh | sh -s -- --system nim
Expand All @@ -49,7 +59,7 @@ stdout.write scene.render()
scene.close()
```

Compile with `--path:ports/nim/src` when using the checkout. Each widget accepts
Compile with `--path:ports/nim/src` when using the checkout instead of Nimble. Each widget accepts
an optional JSON object for options, with the same fields as the
[shared protocol](../bindings/PROTOCOL.md). Arrays and records use Nim's `%*`
JSON literal macro, exported by `hqtui`. Named procedures cover every widget in
Expand Down Expand Up @@ -80,6 +90,10 @@ headless output and the full interactive demo. It does not implement the larger
TypeScript API independently: modal content uses protocol fields rather than
arbitrary nested widgets, and input is not a high-level event/callback API.

`nimble test` runs the API suite and replays the `demo-parity` conformance
group (240 TypeScript reference frames). The shared binding runner checks the
same frames across Ruby, PHP, Perl and Nim, plus interactive behaviour:

```sh
sh ports/nim/tests/build.sh
export HQTUI_NATIVE_LIB="$PWD/ports/cpp/build-bindings/libhqtui_bindings.so"
Expand Down
22 changes: 15 additions & 7 deletions ports/nim/hqtui.nimble
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
version = "0.1.0"
author = "HQTUI contributors"
description = "HQTUI widgets and terminal applications over the shared native engine"
license = "MIT"
srcDir = "src"
# Package

version = "0.7.1"
author = "HQTUI contributors"
description = "Terminal UI widgets and the ten-screen HQTUI dashboard for Nim, over HQTUI's shared native engine (compiled from source)"
license = "MIT"
srcDir = "src"

# Dependencies

requires "nim >= 2.2.4"

task test, "Run binding API tests (requires native engine)":
exec "nim c -r --hints:off tests/core.nim"
# Tasks

task test, "Run the API tests and replay the demo-parity conformance fixtures":
exec "nim c -r --hints:off --nimcache:build/nimcache --out:build/core tests/core.nim"
exec "nim c -r --hints:off --nimcache:build/nimcache --out:build/parity tests/parity.nim"
Loading
Loading