Skip to content
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ wterm ("dub-term") renders to the DOM — native text selection, copy/paste, fin
| [`@wterm/react`](packages/@wterm/react) | React component + `useTerminal` hook (TypeScript) |
| [`@wterm/just-bash`](packages/@wterm/just-bash) | In-browser Bash shell powered by just-bash |
| [`@wterm/markdown`](packages/@wterm/markdown) | Render Markdown in the terminal |
| [`@wterm/search`](packages/@wterm/search) | Grid and scrollback search |

## Features

Expand Down
65 changes: 65 additions & 0 deletions apps/docs/src/app/api-reference/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,68 @@ interface CursorState {
visible: boolean;
}
```

## @wterm/search

Search across both terminal grid rows and scrollback rows.

### `Search`

```ts
import { Search } from "@wterm/search";

const search = new Search(term);
const match = search.findNext("error");
```

<table>
<thead>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>new Search(term)</code></td>
<td>Create a search instance for an initialized <code>WTerm</code></td>
</tr>
<tr>
<td><code>findNext(query, opts?)</code></td>
<td>Find the next match after the internal cursor</td>
</tr>
<tr>
<td><code>findPrevious(query, opts?)</code></td>
<td>Find the previous match before the internal cursor</td>
</tr>
<tr>
<td><code>findAll(query, opts?)</code></td>
<td>Return all matches in oldest-first order (scrollback then grid)</td>
</tr>
<tr>
<td><code>reset()</code></td>
<td>Clear the internal cursor so the next <code>findNext</code> starts from the top</td>
</tr>
</tbody>
</table>

### `SearchOptions`

```ts
interface SearchOptions {
caseSensitive?: boolean;
regex?: boolean;
wholeWord?: boolean;
}
```

### `SearchMatch`

```ts
interface SearchMatch {
row: number; // negative for scrollback, 0+ for grid
col: number;
length: number;
text: string;
}
```
7 changes: 7 additions & 0 deletions apps/docs/src/app/search/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { pageMetadata } from "@/lib/page-metadata";

export const metadata = pageMetadata("search");

export default function Layout({ children }: { children: React.ReactNode }) {
return children;
}
142 changes: 142 additions & 0 deletions apps/docs/src/app/search/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Search

`@wterm/search` adds grid and scrollback search to wterm terminals. Browser `Ctrl+F` can only search visible DOM rows, but most terminal history lives in WASM scrollback. This package searches both.

## Why

Terminal apps often emit errors above the visible viewport. The browser can only find text already rendered in the DOM, so searching misses history still in scrollback. `@wterm/search` scans scrollback and grid together, in buffer order.

## Install

```bash
npm install @wterm/search
```

## Quick Start

### Vanilla JS

```js
import { WTerm } from "@wterm/dom";
import { Search } from "@wterm/search";
import "@wterm/dom/css";

const term = new WTerm(document.getElementById("terminal"));
await term.init();

term.write("error: db unavailable\\r\\n");
term.write("info: retrying\\r\\n");

const search = new Search(term);
const match = search.findNext("error");

if (match) {
console.log(match.row, match.col, match.text);
}
```

## API Reference

### SearchOptions

<table>
<thead>
<tr>
<th>Option</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>caseSensitive</code></td>
<td><code>boolean</code></td>
<td><code>false</code></td>
<td>Match case exactly</td>
</tr>
<tr>
<td><code>regex</code></td>
<td><code>boolean</code></td>
<td><code>false</code></td>
<td>Treat <code>query</code> as a regular expression</td>
</tr>
<tr>
<td><code>wholeWord</code></td>
<td><code>boolean</code></td>
<td><code>false</code></td>
<td>Match only whole words</td>
</tr>
</tbody>
</table>

### SearchMatch

<table>
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>row</code></td>
<td><code>number</code></td>
<td>Match row (<code>-1</code> is most recent scrollback line, <code>0+</code> is grid row)</td>
</tr>
<tr>
<td><code>col</code></td>
<td><code>number</code></td>
<td>Zero-based column of the match</td>
</tr>
<tr>
<td><code>length</code></td>
<td><code>number</code></td>
<td>Match length in characters</td>
</tr>
<tr>
<td><code>text</code></td>
<td><code>string</code></td>
<td>Matched text</td>
</tr>
</tbody>
</table>

### Search

<table>
<thead>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>new Search(term)</code></td>
<td>Create a search instance for an initialized terminal</td>
</tr>
<tr>
<td><code>findNext(query, opts?)</code></td>
<td>Find the next match after the current cursor position</td>
</tr>
<tr>
<td><code>findPrevious(query, opts?)</code></td>
<td>Find the previous match before the current cursor position</td>
</tr>
<tr>
<td><code>findAll(query, opts?)</code></td>
<td>Return all matches in oldest-first order (scrollback then grid)</td>
</tr>
<tr>
<td><code>reset()</code></td>
<td>Reset the internal cursor so the next <code>findNext</code> starts from the top</td>
</tr>
</tbody>
</table>

## Limitations

`@wterm/search` returns match positions and text only. Visual highlighting through the CSS Highlight API is a follow-up PR.
1 change: 1 addition & 0 deletions apps/docs/src/lib/docs-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const navGroups: NavGroup[] = [
items: [
{ name: "Just Bash", href: "/just-bash" },
{ name: "Markdown", href: "/markdown" },
{ name: "Search", href: "/search" },
{ name: "Core / Advanced", href: "/core" },
],
},
Expand Down
1 change: 1 addition & 0 deletions apps/docs/src/lib/page-titles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const PAGE_TITLES: Record<string, string> = {
vanilla: "Vanilla JS",
"just-bash": "Just Bash",
markdown: "Markdown",
search: "Search",
core: "Core / Advanced",
"api-reference": "API Reference",
};
Expand Down
1 change: 1 addition & 0 deletions packages/@wterm/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Headless terminal emulator core for [wterm](https://github.com/vercel-labs/wterm
| [`@wterm/react`](https://www.npmjs.com/package/@wterm/react) | React component + `useTerminal` hook |
| [`@wterm/just-bash`](https://www.npmjs.com/package/@wterm/just-bash) | In-browser Bash shell powered by just-bash |
| [`@wterm/markdown`](https://www.npmjs.com/package/@wterm/markdown) | Streaming Markdown-to-ANSI renderer for terminals |
| [`@wterm/search`](https://www.npmjs.com/package/@wterm/search) | Grid and scrollback search |

## Install

Expand Down
71 changes: 71 additions & 0 deletions packages/@wterm/search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# @wterm/search

Grid and scrollback search for [wterm](https://github.com/vercel-labs/wterm) terminals.

## Search

Search across both visible rows and WASM-backed scrollback.

## Install

```bash
npm install @wterm/search
```

## Quick Start

```ts
import { WTerm } from "@wterm/dom";
import { Search } from "@wterm/search";
import "@wterm/dom/css";

const term = new WTerm(document.getElementById("terminal"));
await term.init();

term.write("error: failed to connect\\r\\n");
term.write("warning: retrying\\r\\n");

const search = new Search(term);
const match = search.findNext("error");

if (match) {
console.log(match.row, match.col, match.text);
}
```

## API

### `SearchOptions`

| Option | Type | Default | Description |
|---|---|---|---|
| `caseSensitive` | `boolean` | `false` | Match case exactly |
| `regex` | `boolean` | `false` | Treat `query` as a regular expression |
| `wholeWord` | `boolean` | `false` | Match only whole words |

### `SearchMatch`

| Field | Type | Description |
|---|---|---|
| `row` | `number` | Match row (`-1` is most recent scrollback line, `0+` is grid row) |
| `col` | `number` | Zero-based column of the match |
| `length` | `number` | Match length in characters |
| `text` | `string` | Matched text |

### `Search`

| Method | Description |
|---|---|
| `new Search(term)` | Create a search instance for an initialized `WTerm` |
| `findNext(query, opts?)` | Find the next match after the internal cursor |
| `findPrevious(query, opts?)` | Find the previous match before the internal cursor |
| `findAll(query, opts?)` | Return all matches in oldest-first buffer order |
| `reset()` | Clear the internal cursor |

## Limitations

`@wterm/search` returns match positions and text. Visual highlighting (for example via the CSS Highlight API) is planned as a follow-up PR.

## License

Apache-2.0
46 changes: 46 additions & 0 deletions packages/@wterm/search/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@wterm/search",
"version": "0.1.8",
"description": "Grid and scrollback search for wterm terminals",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"prepublishOnly": "pnpm build",
"test": "vitest run",
"type-check": "tsc --noEmit"
},
"peerDependencies": {
"@wterm/core": "workspace:*",
"@wterm/dom": "workspace:*"
},
"devDependencies": {
"@internal/ts": "workspace:*",
"typescript": "^6.0.2"
},
"keywords": [
"terminal",
"wterm",
"search",
"scrollback",
"find"
],
"license": "Apache-2.0",
"homepage": "https://wterm.dev",
"repository": {
"type": "git",
"url": "https://github.com/vercel-labs/wterm",
"directory": "packages/@wterm/search"
}
}
Loading