@oh-my-pi/pi-tui 17.4.1 → 17.4.2
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.
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.4.2] - 2026-08-21
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Editor atom table: `insertAtom`/`registerAtom` stage compact atomic tokens whose registered expansion is emitted on submit (`getExpandedText`), alongside the existing paste-marker store.
|
|
10
|
+
|
|
5
11
|
## [17.4.1] - 2026-08-21
|
|
6
12
|
|
|
7
13
|
### Added
|
|
@@ -120,6 +120,15 @@ export declare class Editor implements Component, Focusable {
|
|
|
120
120
|
/** Whether the buffer text equals `value`, without `getText()`'s full join —
|
|
121
121
|
* O(1) for the hot per-keystroke probes against short single-line values. */
|
|
122
122
|
textEquals(value: string): boolean;
|
|
123
|
+
/** Register `label` as a collapsed atom expanding to `expansion` on submit, without inserting
|
|
124
|
+
* it — for hosts that re-collapse restored draft text via {@link setText}. */
|
|
125
|
+
registerAtom(label: string, expansion: string): void;
|
|
126
|
+
/** Insert `label` (plus a trailing space) at the cursor and register it as an atom expanding
|
|
127
|
+
* to `expansion` on submit. Pair with {@link atomicTokenPattern} so the label deletes as a
|
|
128
|
+
* unit. */
|
|
129
|
+
insertAtom(label: string, expansion: string): void;
|
|
130
|
+
/** Drop every registered atom expansion (draft cleared or replaced by the host). */
|
|
131
|
+
clearAtoms(): void;
|
|
123
132
|
/**
|
|
124
133
|
* Get text with paste markers expanded to their actual content.
|
|
125
134
|
* Use this when you need the full content (e.g., for external editor).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-tui",
|
|
4
|
-
"version": "17.4.
|
|
4
|
+
"version": "17.4.2",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Stencil Labs, Inc.",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"fmt": "biome format --write ."
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@oh-my-pi/pi-natives": "17.4.
|
|
41
|
-
"@oh-my-pi/pi-utils": "17.4.
|
|
40
|
+
"@oh-my-pi/pi-natives": "17.4.2",
|
|
41
|
+
"@oh-my-pi/pi-utils": "17.4.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"ghostty-web": "^0.4.0"
|
package/src/components/editor.ts
CHANGED
|
@@ -484,6 +484,9 @@ export class Editor implements Component, Focusable {
|
|
|
484
484
|
#pastes: Map<number, string> = new Map();
|
|
485
485
|
#pasteCounter: number = 0;
|
|
486
486
|
|
|
487
|
+
// Host-registered atomic chip tokens: exact buffer label → expansion emitted on submit.
|
|
488
|
+
#atoms: Map<string, string> = new Map();
|
|
489
|
+
|
|
487
490
|
/** Optional pattern matching atomic placeholder tokens (e.g. `[Image #1, 800x600]` or
|
|
488
491
|
* `[Paste #2, +30 lines]`) that the editor treats as indivisible: a backspace or forward-delete
|
|
489
492
|
* landing on any character of a token removes the whole token instead of corrupting it into
|
|
@@ -1752,13 +1755,48 @@ export class Editor implements Component, Focusable {
|
|
|
1752
1755
|
return this.getText() === value;
|
|
1753
1756
|
}
|
|
1754
1757
|
|
|
1758
|
+
/** Expand collapsed markers — `[Paste #N]` tokens and registered atom labels — into their
|
|
1759
|
+
* stored content. Single pass so replaced content is never rescanned (a pasted body that
|
|
1760
|
+
* happens to contain another token's label must survive verbatim). Longer atom labels are
|
|
1761
|
+
* tried first so `#1` never shadows `#10`. */
|
|
1755
1762
|
#expandPasteMarkers(text: string): string {
|
|
1756
|
-
|
|
1757
|
-
for (const
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1763
|
+
const sources: string[] = [];
|
|
1764
|
+
for (const pasteId of this.#pastes.keys()) {
|
|
1765
|
+
sources.push(`\\[Paste #${pasteId}(?:, (?:\\+\\d+ lines|\\d+ chars))?\\]`);
|
|
1766
|
+
}
|
|
1767
|
+
const labels = [...this.#atoms.keys()].sort((a, b) => b.length - a.length);
|
|
1768
|
+
for (const label of labels) sources.push(RegExp.escape(label));
|
|
1769
|
+
if (sources.length === 0) return text;
|
|
1770
|
+
const markerRegex = new RegExp(sources.join("|"), "g");
|
|
1771
|
+
return text.replace(markerRegex, match => {
|
|
1772
|
+
const paste = /^\[Paste #(\d+)/.exec(match);
|
|
1773
|
+
if (paste) return this.#pastes.get(Number(paste[1])) ?? match;
|
|
1774
|
+
return this.#atoms.get(match) ?? match;
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
/** Register `label` as a collapsed atom expanding to `expansion` on submit, without inserting
|
|
1779
|
+
* it — for hosts that re-collapse restored draft text via {@link setText}. */
|
|
1780
|
+
registerAtom(label: string, expansion: string): void {
|
|
1781
|
+
this.#atoms.set(label, expansion);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
/** Insert `label` (plus a trailing space) at the cursor and register it as an atom expanding
|
|
1785
|
+
* to `expansion` on submit. Pair with {@link atomicTokenPattern} so the label deletes as a
|
|
1786
|
+
* unit. */
|
|
1787
|
+
insertAtom(label: string, expansion: string): void {
|
|
1788
|
+
this.#historyIndex = -1;
|
|
1789
|
+
this.#resetKillSequence();
|
|
1790
|
+
this.#recordUndoState();
|
|
1791
|
+
this.registerAtom(label, expansion);
|
|
1792
|
+
this.#withUndoSuspended(() => {
|
|
1793
|
+
this.#insertTextAtCursor(`${label} `);
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
/** Drop every registered atom expansion (draft cleared or replaced by the host). */
|
|
1798
|
+
clearAtoms(): void {
|
|
1799
|
+
this.#atoms.clear();
|
|
1762
1800
|
}
|
|
1763
1801
|
|
|
1764
1802
|
/**
|
|
@@ -2213,6 +2251,7 @@ export class Editor implements Component, Focusable {
|
|
|
2213
2251
|
this.#state = { lines: [""], cursorLine: 0, cursorCol: 0 };
|
|
2214
2252
|
this.#pastes.clear();
|
|
2215
2253
|
this.#pasteCounter = 0;
|
|
2254
|
+
this.#atoms.clear();
|
|
2216
2255
|
this.#historyIndex = -1;
|
|
2217
2256
|
this.#scrollOffset = 0;
|
|
2218
2257
|
this.#undoStack.length = 0;
|