@earendil-works/pi-tui 0.75.4 → 0.76.0

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.
Files changed (37) hide show
  1. package/dist/components/editor.d.ts.map +1 -1
  2. package/dist/components/editor.js +24 -83
  3. package/dist/components/editor.js.map +1 -1
  4. package/dist/components/input.d.ts.map +1 -1
  5. package/dist/components/input.js +7 -55
  6. package/dist/components/input.js.map +1 -1
  7. package/dist/components/markdown.d.ts +7 -1
  8. package/dist/components/markdown.d.ts.map +1 -1
  9. package/dist/components/markdown.js +12 -2
  10. package/dist/components/markdown.js.map +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/native-modifiers.d.ts +3 -0
  15. package/dist/native-modifiers.d.ts.map +1 -0
  16. package/dist/native-modifiers.js +53 -0
  17. package/dist/native-modifiers.js.map +1 -0
  18. package/dist/terminal-image.d.ts.map +1 -1
  19. package/dist/terminal-image.js +8 -1
  20. package/dist/terminal-image.js.map +1 -1
  21. package/dist/terminal.d.ts +2 -0
  22. package/dist/terminal.d.ts.map +1 -1
  23. package/dist/terminal.js +38 -16
  24. package/dist/terminal.js.map +1 -1
  25. package/dist/utils.d.ts +6 -1
  26. package/dist/utils.d.ts.map +1 -1
  27. package/dist/utils.js +20 -13
  28. package/dist/utils.js.map +1 -1
  29. package/dist/word-navigation.d.ts +25 -0
  30. package/dist/word-navigation.d.ts.map +1 -0
  31. package/dist/word-navigation.js +96 -0
  32. package/dist/word-navigation.js.map +1 -0
  33. package/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  34. package/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  35. package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  36. package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  37. package/package.json +3 -5
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Options for word navigation functions.
3
+ * When omitted, uses the default Intl.Segmenter word segmentation.
4
+ */
5
+ export interface WordNavigationOptions {
6
+ /** Custom segmenter returning word segments for the given text. */
7
+ segment?: (text: string) => Iterable<Intl.SegmentData>;
8
+ /** Predicate identifying atomic segments that should be treated as single units (e.g. paste markers). */
9
+ isAtomicSegment?: (segment: string) => boolean;
10
+ }
11
+ /**
12
+ * Find the cursor position after moving one word backward from `cursor` in `text`.
13
+ * Skips trailing whitespace, then stops at the next word/punctuation boundary.
14
+ *
15
+ * Pure function - does not mutate any state.
16
+ */
17
+ export declare function findWordBackward(text: string, cursor: number, options?: WordNavigationOptions): number;
18
+ /**
19
+ * Find the cursor position after moving one word forward from `cursor` in `text`.
20
+ * Skips leading whitespace, then stops at the next word/punctuation boundary.
21
+ *
22
+ * Pure function - does not mutate any state.
23
+ */
24
+ export declare function findWordForward(text: string, cursor: number, options?: WordNavigationOptions): number;
25
+ //# sourceMappingURL=word-navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"word-navigation.d.ts","sourceRoot":"","sources":["../src/word-navigation.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACrC,mEAAmE;IACnE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvD,yGAAyG;IACzG,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAgDtG;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAuCrG","sourcesContent":["import { getWordSegmenter, isWhitespaceChar, PUNCTUATION_REGEX } from \"./utils.ts\";\n\nconst wordSegmenter = getWordSegmenter();\n\n/**\n * Options for word navigation functions.\n * When omitted, uses the default Intl.Segmenter word segmentation.\n */\nexport interface WordNavigationOptions {\n\t/** Custom segmenter returning word segments for the given text. */\n\tsegment?: (text: string) => Iterable<Intl.SegmentData>;\n\t/** Predicate identifying atomic segments that should be treated as single units (e.g. paste markers). */\n\tisAtomicSegment?: (segment: string) => boolean;\n}\n\n/**\n * Find the cursor position after moving one word backward from `cursor` in `text`.\n * Skips trailing whitespace, then stops at the next word/punctuation boundary.\n *\n * Pure function - does not mutate any state.\n */\nexport function findWordBackward(text: string, cursor: number, options?: WordNavigationOptions): number {\n\tif (cursor <= 0) return 0;\n\n\tconst textBeforeCursor = text.slice(0, cursor);\n\tconst segmentFn = options?.segment;\n\tconst isAtomic = options?.isAtomicSegment;\n\tconst segments = segmentFn ? [...segmentFn(textBeforeCursor)] : [...wordSegmenter.segment(textBeforeCursor)];\n\tlet newCursor = cursor;\n\n\t// Skip trailing whitespace\n\twhile (\n\t\tsegments.length > 0 &&\n\t\t!isAtomic?.(segments[segments.length - 1]?.segment || \"\") &&\n\t\tisWhitespaceChar(segments[segments.length - 1]?.segment || \"\")\n\t) {\n\t\tnewCursor -= segments.pop()?.segment.length || 0;\n\t}\n\n\tif (segments.length === 0) return newCursor;\n\n\tconst last = segments[segments.length - 1]!;\n\n\tif (isAtomic?.(last.segment)) {\n\t\t// Skip one atomic segment.\n\t\tnewCursor -= last.segment.length;\n\t} else if (last.isWordLike) {\n\t\t// Skip inside one word-like segment, preserving ASCII punctuation boundaries.\n\t\tconst segment = last.segment;\n\t\tconst matches = [...segment.matchAll(new RegExp(PUNCTUATION_REGEX, \"g\"))];\n\t\tif (matches.length <= 0) {\n\t\t\tnewCursor -= segment.length;\n\t\t} else {\n\t\t\tconst lastMatch = matches[matches.length - 1]!;\n\t\t\tnewCursor -= segment.length - (lastMatch.index + lastMatch[0].length);\n\t\t}\n\t} else {\n\t\t// Skip non-word non-whitespace run (punctuation)\n\t\twhile (\n\t\t\tsegments.length > 0 &&\n\t\t\t!isAtomic?.(segments[segments.length - 1]?.segment || \"\") &&\n\t\t\t!segments[segments.length - 1]?.isWordLike &&\n\t\t\t!isWhitespaceChar(segments[segments.length - 1]?.segment || \"\")\n\t\t) {\n\t\t\tnewCursor -= segments.pop()?.segment.length || 0;\n\t\t}\n\t}\n\n\treturn newCursor;\n}\n\n/**\n * Find the cursor position after moving one word forward from `cursor` in `text`.\n * Skips leading whitespace, then stops at the next word/punctuation boundary.\n *\n * Pure function - does not mutate any state.\n */\nexport function findWordForward(text: string, cursor: number, options?: WordNavigationOptions): number {\n\tif (cursor >= text.length) return text.length;\n\n\tconst textAfterCursor = text.slice(cursor);\n\tconst segmentFn = options?.segment;\n\tconst isAtomic = options?.isAtomicSegment;\n\tconst segments = segmentFn ? segmentFn(textAfterCursor) : wordSegmenter.segment(textAfterCursor);\n\tconst iterator = segments[Symbol.iterator]();\n\tlet next = iterator.next();\n\tlet newCursor = cursor;\n\n\t// Skip leading whitespace\n\twhile (!next.done && !isAtomic?.(next.value.segment) && isWhitespaceChar(next.value.segment)) {\n\t\tnewCursor += next.value.segment.length;\n\t\tnext = iterator.next();\n\t}\n\n\tif (next.done) return newCursor;\n\n\tif (isAtomic?.(next.value.segment)) {\n\t\t// Skip one atomic segment.\n\t\tnewCursor += next.value.segment.length;\n\t} else if (next.value.isWordLike) {\n\t\t// Skip inside one word-like segment, preserving ASCII punctuation boundaries.\n\t\tnewCursor += PUNCTUATION_REGEX.exec(next.value.segment)?.index ?? next.value.segment.length;\n\t} else {\n\t\t// Skip non-word non-whitespace run (punctuation)\n\t\twhile (\n\t\t\t!next.done &&\n\t\t\t!isAtomic?.(next.value.segment) &&\n\t\t\t!next.value.isWordLike &&\n\t\t\t!isWhitespaceChar(next.value.segment)\n\t\t) {\n\t\t\tnewCursor += next.value.segment.length;\n\t\t\tnext = iterator.next();\n\t\t}\n\t}\n\n\treturn newCursor;\n}\n"]}
@@ -0,0 +1,96 @@
1
+ import { getWordSegmenter, isWhitespaceChar, PUNCTUATION_REGEX } from "./utils.js";
2
+ const wordSegmenter = getWordSegmenter();
3
+ /**
4
+ * Find the cursor position after moving one word backward from `cursor` in `text`.
5
+ * Skips trailing whitespace, then stops at the next word/punctuation boundary.
6
+ *
7
+ * Pure function - does not mutate any state.
8
+ */
9
+ export function findWordBackward(text, cursor, options) {
10
+ if (cursor <= 0)
11
+ return 0;
12
+ const textBeforeCursor = text.slice(0, cursor);
13
+ const segmentFn = options?.segment;
14
+ const isAtomic = options?.isAtomicSegment;
15
+ const segments = segmentFn ? [...segmentFn(textBeforeCursor)] : [...wordSegmenter.segment(textBeforeCursor)];
16
+ let newCursor = cursor;
17
+ // Skip trailing whitespace
18
+ while (segments.length > 0 &&
19
+ !isAtomic?.(segments[segments.length - 1]?.segment || "") &&
20
+ isWhitespaceChar(segments[segments.length - 1]?.segment || "")) {
21
+ newCursor -= segments.pop()?.segment.length || 0;
22
+ }
23
+ if (segments.length === 0)
24
+ return newCursor;
25
+ const last = segments[segments.length - 1];
26
+ if (isAtomic?.(last.segment)) {
27
+ // Skip one atomic segment.
28
+ newCursor -= last.segment.length;
29
+ }
30
+ else if (last.isWordLike) {
31
+ // Skip inside one word-like segment, preserving ASCII punctuation boundaries.
32
+ const segment = last.segment;
33
+ const matches = [...segment.matchAll(new RegExp(PUNCTUATION_REGEX, "g"))];
34
+ if (matches.length <= 0) {
35
+ newCursor -= segment.length;
36
+ }
37
+ else {
38
+ const lastMatch = matches[matches.length - 1];
39
+ newCursor -= segment.length - (lastMatch.index + lastMatch[0].length);
40
+ }
41
+ }
42
+ else {
43
+ // Skip non-word non-whitespace run (punctuation)
44
+ while (segments.length > 0 &&
45
+ !isAtomic?.(segments[segments.length - 1]?.segment || "") &&
46
+ !segments[segments.length - 1]?.isWordLike &&
47
+ !isWhitespaceChar(segments[segments.length - 1]?.segment || "")) {
48
+ newCursor -= segments.pop()?.segment.length || 0;
49
+ }
50
+ }
51
+ return newCursor;
52
+ }
53
+ /**
54
+ * Find the cursor position after moving one word forward from `cursor` in `text`.
55
+ * Skips leading whitespace, then stops at the next word/punctuation boundary.
56
+ *
57
+ * Pure function - does not mutate any state.
58
+ */
59
+ export function findWordForward(text, cursor, options) {
60
+ if (cursor >= text.length)
61
+ return text.length;
62
+ const textAfterCursor = text.slice(cursor);
63
+ const segmentFn = options?.segment;
64
+ const isAtomic = options?.isAtomicSegment;
65
+ const segments = segmentFn ? segmentFn(textAfterCursor) : wordSegmenter.segment(textAfterCursor);
66
+ const iterator = segments[Symbol.iterator]();
67
+ let next = iterator.next();
68
+ let newCursor = cursor;
69
+ // Skip leading whitespace
70
+ while (!next.done && !isAtomic?.(next.value.segment) && isWhitespaceChar(next.value.segment)) {
71
+ newCursor += next.value.segment.length;
72
+ next = iterator.next();
73
+ }
74
+ if (next.done)
75
+ return newCursor;
76
+ if (isAtomic?.(next.value.segment)) {
77
+ // Skip one atomic segment.
78
+ newCursor += next.value.segment.length;
79
+ }
80
+ else if (next.value.isWordLike) {
81
+ // Skip inside one word-like segment, preserving ASCII punctuation boundaries.
82
+ newCursor += PUNCTUATION_REGEX.exec(next.value.segment)?.index ?? next.value.segment.length;
83
+ }
84
+ else {
85
+ // Skip non-word non-whitespace run (punctuation)
86
+ while (!next.done &&
87
+ !isAtomic?.(next.value.segment) &&
88
+ !next.value.isWordLike &&
89
+ !isWhitespaceChar(next.value.segment)) {
90
+ newCursor += next.value.segment.length;
91
+ next = iterator.next();
92
+ }
93
+ }
94
+ return newCursor;
95
+ }
96
+ //# sourceMappingURL=word-navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"word-navigation.js","sourceRoot":"","sources":["../src/word-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { getWordSegmenter, isWhitespaceChar, PUNCTUATION_REGEX } from \"./utils.ts\";\n\nconst wordSegmenter = getWordSegmenter();\n\n/**\n * Options for word navigation functions.\n * When omitted, uses the default Intl.Segmenter word segmentation.\n */\nexport interface WordNavigationOptions {\n\t/** Custom segmenter returning word segments for the given text. */\n\tsegment?: (text: string) => Iterable<Intl.SegmentData>;\n\t/** Predicate identifying atomic segments that should be treated as single units (e.g. paste markers). */\n\tisAtomicSegment?: (segment: string) => boolean;\n}\n\n/**\n * Find the cursor position after moving one word backward from `cursor` in `text`.\n * Skips trailing whitespace, then stops at the next word/punctuation boundary.\n *\n * Pure function - does not mutate any state.\n */\nexport function findWordBackward(text: string, cursor: number, options?: WordNavigationOptions): number {\n\tif (cursor <= 0) return 0;\n\n\tconst textBeforeCursor = text.slice(0, cursor);\n\tconst segmentFn = options?.segment;\n\tconst isAtomic = options?.isAtomicSegment;\n\tconst segments = segmentFn ? [...segmentFn(textBeforeCursor)] : [...wordSegmenter.segment(textBeforeCursor)];\n\tlet newCursor = cursor;\n\n\t// Skip trailing whitespace\n\twhile (\n\t\tsegments.length > 0 &&\n\t\t!isAtomic?.(segments[segments.length - 1]?.segment || \"\") &&\n\t\tisWhitespaceChar(segments[segments.length - 1]?.segment || \"\")\n\t) {\n\t\tnewCursor -= segments.pop()?.segment.length || 0;\n\t}\n\n\tif (segments.length === 0) return newCursor;\n\n\tconst last = segments[segments.length - 1]!;\n\n\tif (isAtomic?.(last.segment)) {\n\t\t// Skip one atomic segment.\n\t\tnewCursor -= last.segment.length;\n\t} else if (last.isWordLike) {\n\t\t// Skip inside one word-like segment, preserving ASCII punctuation boundaries.\n\t\tconst segment = last.segment;\n\t\tconst matches = [...segment.matchAll(new RegExp(PUNCTUATION_REGEX, \"g\"))];\n\t\tif (matches.length <= 0) {\n\t\t\tnewCursor -= segment.length;\n\t\t} else {\n\t\t\tconst lastMatch = matches[matches.length - 1]!;\n\t\t\tnewCursor -= segment.length - (lastMatch.index + lastMatch[0].length);\n\t\t}\n\t} else {\n\t\t// Skip non-word non-whitespace run (punctuation)\n\t\twhile (\n\t\t\tsegments.length > 0 &&\n\t\t\t!isAtomic?.(segments[segments.length - 1]?.segment || \"\") &&\n\t\t\t!segments[segments.length - 1]?.isWordLike &&\n\t\t\t!isWhitespaceChar(segments[segments.length - 1]?.segment || \"\")\n\t\t) {\n\t\t\tnewCursor -= segments.pop()?.segment.length || 0;\n\t\t}\n\t}\n\n\treturn newCursor;\n}\n\n/**\n * Find the cursor position after moving one word forward from `cursor` in `text`.\n * Skips leading whitespace, then stops at the next word/punctuation boundary.\n *\n * Pure function - does not mutate any state.\n */\nexport function findWordForward(text: string, cursor: number, options?: WordNavigationOptions): number {\n\tif (cursor >= text.length) return text.length;\n\n\tconst textAfterCursor = text.slice(cursor);\n\tconst segmentFn = options?.segment;\n\tconst isAtomic = options?.isAtomicSegment;\n\tconst segments = segmentFn ? segmentFn(textAfterCursor) : wordSegmenter.segment(textAfterCursor);\n\tconst iterator = segments[Symbol.iterator]();\n\tlet next = iterator.next();\n\tlet newCursor = cursor;\n\n\t// Skip leading whitespace\n\twhile (!next.done && !isAtomic?.(next.value.segment) && isWhitespaceChar(next.value.segment)) {\n\t\tnewCursor += next.value.segment.length;\n\t\tnext = iterator.next();\n\t}\n\n\tif (next.done) return newCursor;\n\n\tif (isAtomic?.(next.value.segment)) {\n\t\t// Skip one atomic segment.\n\t\tnewCursor += next.value.segment.length;\n\t} else if (next.value.isWordLike) {\n\t\t// Skip inside one word-like segment, preserving ASCII punctuation boundaries.\n\t\tnewCursor += PUNCTUATION_REGEX.exec(next.value.segment)?.index ?? next.value.segment.length;\n\t} else {\n\t\t// Skip non-word non-whitespace run (punctuation)\n\t\twhile (\n\t\t\t!next.done &&\n\t\t\t!isAtomic?.(next.value.segment) &&\n\t\t\t!next.value.isWordLike &&\n\t\t\t!isWhitespaceChar(next.value.segment)\n\t\t) {\n\t\t\tnewCursor += next.value.segment.length;\n\t\t\tnext = iterator.next();\n\t\t}\n\t}\n\n\treturn newCursor;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-tui",
3
- "version": "0.75.4",
3
+ "version": "0.76.0",
4
4
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,6 +12,8 @@
12
12
  },
13
13
  "files": [
14
14
  "dist/**/*",
15
+ "native/win32/prebuilds/**/*.node",
16
+ "native/darwin/prebuilds/**/*.node",
15
17
  "README.md"
16
18
  ],
17
19
  "keywords": [
@@ -38,12 +40,8 @@
38
40
  "get-east-asian-width": "1.6.0",
39
41
  "marked": "15.0.12"
40
42
  },
41
- "optionalDependencies": {
42
- "koffi": "2.16.2"
43
- },
44
43
  "devDependencies": {
45
44
  "@xterm/headless": "5.5.0",
46
- "@xterm/xterm": "5.5.0",
47
45
  "chalk": "5.6.2"
48
46
  }
49
47
  }