@lenylvt/pi-tui 0.64.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 (127) hide show
  1. package/README.md +767 -0
  2. package/dist/autocomplete.d.ts +50 -0
  3. package/dist/autocomplete.d.ts.map +1 -0
  4. package/dist/autocomplete.js +623 -0
  5. package/dist/autocomplete.js.map +1 -0
  6. package/dist/components/box.d.ts +22 -0
  7. package/dist/components/box.d.ts.map +1 -0
  8. package/dist/components/box.js +104 -0
  9. package/dist/components/box.js.map +1 -0
  10. package/dist/components/cancellable-loader.d.ts +22 -0
  11. package/dist/components/cancellable-loader.d.ts.map +1 -0
  12. package/dist/components/cancellable-loader.js +35 -0
  13. package/dist/components/cancellable-loader.js.map +1 -0
  14. package/dist/components/editor.d.ts +244 -0
  15. package/dist/components/editor.d.ts.map +1 -0
  16. package/dist/components/editor.js +1861 -0
  17. package/dist/components/editor.js.map +1 -0
  18. package/dist/components/image.d.ts +28 -0
  19. package/dist/components/image.d.ts.map +1 -0
  20. package/dist/components/image.js +69 -0
  21. package/dist/components/image.js.map +1 -0
  22. package/dist/components/input.d.ts +37 -0
  23. package/dist/components/input.d.ts.map +1 -0
  24. package/dist/components/input.js +426 -0
  25. package/dist/components/input.js.map +1 -0
  26. package/dist/components/loader.d.ts +21 -0
  27. package/dist/components/loader.d.ts.map +1 -0
  28. package/dist/components/loader.js +49 -0
  29. package/dist/components/loader.js.map +1 -0
  30. package/dist/components/markdown.d.ts +95 -0
  31. package/dist/components/markdown.d.ts.map +1 -0
  32. package/dist/components/markdown.js +660 -0
  33. package/dist/components/markdown.js.map +1 -0
  34. package/dist/components/select-list.d.ts +50 -0
  35. package/dist/components/select-list.d.ts.map +1 -0
  36. package/dist/components/select-list.js +159 -0
  37. package/dist/components/select-list.js.map +1 -0
  38. package/dist/components/settings-list.d.ts +50 -0
  39. package/dist/components/settings-list.d.ts.map +1 -0
  40. package/dist/components/settings-list.js +185 -0
  41. package/dist/components/settings-list.js.map +1 -0
  42. package/dist/components/spacer.d.ts +12 -0
  43. package/dist/components/spacer.d.ts.map +1 -0
  44. package/dist/components/spacer.js +23 -0
  45. package/dist/components/spacer.js.map +1 -0
  46. package/dist/components/text.d.ts +19 -0
  47. package/dist/components/text.d.ts.map +1 -0
  48. package/dist/components/text.js +89 -0
  49. package/dist/components/text.js.map +1 -0
  50. package/dist/components/truncated-text.d.ts +13 -0
  51. package/dist/components/truncated-text.d.ts.map +1 -0
  52. package/dist/components/truncated-text.js +51 -0
  53. package/dist/components/truncated-text.js.map +1 -0
  54. package/dist/editor-component.d.ts +39 -0
  55. package/dist/editor-component.d.ts.map +1 -0
  56. package/dist/editor-component.js +2 -0
  57. package/dist/editor-component.js.map +1 -0
  58. package/dist/fuzzy.d.ts +16 -0
  59. package/dist/fuzzy.d.ts.map +1 -0
  60. package/dist/fuzzy.js +107 -0
  61. package/dist/fuzzy.js.map +1 -0
  62. package/dist/index.d.ts +23 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +32 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/keybindings.d.ts +193 -0
  67. package/dist/keybindings.d.ts.map +1 -0
  68. package/dist/keybindings.js +174 -0
  69. package/dist/keybindings.js.map +1 -0
  70. package/dist/keys.d.ts +170 -0
  71. package/dist/keys.d.ts.map +1 -0
  72. package/dist/keys.js +1124 -0
  73. package/dist/keys.js.map +1 -0
  74. package/dist/kill-ring.d.ts +28 -0
  75. package/dist/kill-ring.d.ts.map +1 -0
  76. package/dist/kill-ring.js +44 -0
  77. package/dist/kill-ring.js.map +1 -0
  78. package/dist/stdin-buffer.d.ts +48 -0
  79. package/dist/stdin-buffer.d.ts.map +1 -0
  80. package/dist/stdin-buffer.js +317 -0
  81. package/dist/stdin-buffer.js.map +1 -0
  82. package/dist/terminal-image.d.ts +68 -0
  83. package/dist/terminal-image.d.ts.map +1 -0
  84. package/dist/terminal-image.js +288 -0
  85. package/dist/terminal-image.js.map +1 -0
  86. package/dist/terminal.d.ts +84 -0
  87. package/dist/terminal.d.ts.map +1 -0
  88. package/dist/terminal.js +285 -0
  89. package/dist/terminal.js.map +1 -0
  90. package/dist/tui.d.ts +218 -0
  91. package/dist/tui.d.ts.map +1 -0
  92. package/dist/tui.js +966 -0
  93. package/dist/tui.js.map +1 -0
  94. package/dist/undo-stack.d.ts +17 -0
  95. package/dist/undo-stack.d.ts.map +1 -0
  96. package/dist/undo-stack.js +25 -0
  97. package/dist/undo-stack.js.map +1 -0
  98. package/dist/utils.d.ts +78 -0
  99. package/dist/utils.d.ts.map +1 -0
  100. package/dist/utils.js +960 -0
  101. package/dist/utils.js.map +1 -0
  102. package/package.json +55 -0
  103. package/src/autocomplete.ts +771 -0
  104. package/src/components/box.ts +137 -0
  105. package/src/components/cancellable-loader.ts +40 -0
  106. package/src/components/editor.ts +2230 -0
  107. package/src/components/image.ts +104 -0
  108. package/src/components/input.ts +503 -0
  109. package/src/components/loader.ts +55 -0
  110. package/src/components/markdown.ts +820 -0
  111. package/src/components/select-list.ts +229 -0
  112. package/src/components/settings-list.ts +250 -0
  113. package/src/components/spacer.ts +28 -0
  114. package/src/components/text.ts +106 -0
  115. package/src/components/truncated-text.ts +65 -0
  116. package/src/editor-component.ts +74 -0
  117. package/src/fuzzy.ts +133 -0
  118. package/src/index.ts +104 -0
  119. package/src/keybindings.ts +244 -0
  120. package/src/keys.ts +1356 -0
  121. package/src/kill-ring.ts +46 -0
  122. package/src/stdin-buffer.ts +386 -0
  123. package/src/terminal-image.ts +381 -0
  124. package/src/terminal.ts +360 -0
  125. package/src/tui.ts +1200 -0
  126. package/src/undo-stack.ts +28 -0
  127. package/src/utils.ts +1068 -0
package/dist/fuzzy.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Fuzzy matching utilities.
3
+ * Matches if all query characters appear in order (not necessarily consecutive).
4
+ * Lower score = better match.
5
+ */
6
+ export function fuzzyMatch(query, text) {
7
+ const queryLower = query.toLowerCase();
8
+ const textLower = text.toLowerCase();
9
+ const matchQuery = (normalizedQuery) => {
10
+ if (normalizedQuery.length === 0) {
11
+ return { matches: true, score: 0 };
12
+ }
13
+ if (normalizedQuery.length > textLower.length) {
14
+ return { matches: false, score: 0 };
15
+ }
16
+ let queryIndex = 0;
17
+ let score = 0;
18
+ let lastMatchIndex = -1;
19
+ let consecutiveMatches = 0;
20
+ for (let i = 0; i < textLower.length && queryIndex < normalizedQuery.length; i++) {
21
+ if (textLower[i] === normalizedQuery[queryIndex]) {
22
+ const isWordBoundary = i === 0 || /[\s\-_./:]/.test(textLower[i - 1]);
23
+ // Reward consecutive matches
24
+ if (lastMatchIndex === i - 1) {
25
+ consecutiveMatches++;
26
+ score -= consecutiveMatches * 5;
27
+ }
28
+ else {
29
+ consecutiveMatches = 0;
30
+ // Penalize gaps
31
+ if (lastMatchIndex >= 0) {
32
+ score += (i - lastMatchIndex - 1) * 2;
33
+ }
34
+ }
35
+ // Reward word boundary matches
36
+ if (isWordBoundary) {
37
+ score -= 10;
38
+ }
39
+ // Slight penalty for later matches
40
+ score += i * 0.1;
41
+ lastMatchIndex = i;
42
+ queryIndex++;
43
+ }
44
+ }
45
+ if (queryIndex < normalizedQuery.length) {
46
+ return { matches: false, score: 0 };
47
+ }
48
+ return { matches: true, score };
49
+ };
50
+ const primaryMatch = matchQuery(queryLower);
51
+ if (primaryMatch.matches) {
52
+ return primaryMatch;
53
+ }
54
+ const alphaNumericMatch = queryLower.match(/^(?<letters>[a-z]+)(?<digits>[0-9]+)$/);
55
+ const numericAlphaMatch = queryLower.match(/^(?<digits>[0-9]+)(?<letters>[a-z]+)$/);
56
+ const swappedQuery = alphaNumericMatch
57
+ ? `${alphaNumericMatch.groups?.digits ?? ""}${alphaNumericMatch.groups?.letters ?? ""}`
58
+ : numericAlphaMatch
59
+ ? `${numericAlphaMatch.groups?.letters ?? ""}${numericAlphaMatch.groups?.digits ?? ""}`
60
+ : "";
61
+ if (!swappedQuery) {
62
+ return primaryMatch;
63
+ }
64
+ const swappedMatch = matchQuery(swappedQuery);
65
+ if (!swappedMatch.matches) {
66
+ return primaryMatch;
67
+ }
68
+ return { matches: true, score: swappedMatch.score + 5 };
69
+ }
70
+ /**
71
+ * Filter and sort items by fuzzy match quality (best matches first).
72
+ * Supports space-separated tokens: all tokens must match.
73
+ */
74
+ export function fuzzyFilter(items, query, getText) {
75
+ if (!query.trim()) {
76
+ return items;
77
+ }
78
+ const tokens = query
79
+ .trim()
80
+ .split(/\s+/)
81
+ .filter((t) => t.length > 0);
82
+ if (tokens.length === 0) {
83
+ return items;
84
+ }
85
+ const results = [];
86
+ for (const item of items) {
87
+ const text = getText(item);
88
+ let totalScore = 0;
89
+ let allMatch = true;
90
+ for (const token of tokens) {
91
+ const match = fuzzyMatch(token, text);
92
+ if (match.matches) {
93
+ totalScore += match.score;
94
+ }
95
+ else {
96
+ allMatch = false;
97
+ break;
98
+ }
99
+ }
100
+ if (allMatch) {
101
+ results.push({ item, totalScore });
102
+ }
103
+ }
104
+ results.sort((a, b) => a.totalScore - b.totalScore);
105
+ return results.map((r) => r.item);
106
+ }
107
+ //# sourceMappingURL=fuzzy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzy.js","sourceRoot":"","sources":["../src/fuzzy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fuzzy matching utilities.\n * Matches if all query characters appear in order (not necessarily consecutive).\n * Lower score = better match.\n */\n\nexport interface FuzzyMatch {\n\tmatches: boolean;\n\tscore: number;\n}\n\nexport function fuzzyMatch(query: string, text: string): FuzzyMatch {\n\tconst queryLower = query.toLowerCase();\n\tconst textLower = text.toLowerCase();\n\n\tconst matchQuery = (normalizedQuery: string): FuzzyMatch => {\n\t\tif (normalizedQuery.length === 0) {\n\t\t\treturn { matches: true, score: 0 };\n\t\t}\n\n\t\tif (normalizedQuery.length > textLower.length) {\n\t\t\treturn { matches: false, score: 0 };\n\t\t}\n\n\t\tlet queryIndex = 0;\n\t\tlet score = 0;\n\t\tlet lastMatchIndex = -1;\n\t\tlet consecutiveMatches = 0;\n\n\t\tfor (let i = 0; i < textLower.length && queryIndex < normalizedQuery.length; i++) {\n\t\t\tif (textLower[i] === normalizedQuery[queryIndex]) {\n\t\t\t\tconst isWordBoundary = i === 0 || /[\\s\\-_./:]/.test(textLower[i - 1]!);\n\n\t\t\t\t// Reward consecutive matches\n\t\t\t\tif (lastMatchIndex === i - 1) {\n\t\t\t\t\tconsecutiveMatches++;\n\t\t\t\t\tscore -= consecutiveMatches * 5;\n\t\t\t\t} else {\n\t\t\t\t\tconsecutiveMatches = 0;\n\t\t\t\t\t// Penalize gaps\n\t\t\t\t\tif (lastMatchIndex >= 0) {\n\t\t\t\t\t\tscore += (i - lastMatchIndex - 1) * 2;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Reward word boundary matches\n\t\t\t\tif (isWordBoundary) {\n\t\t\t\t\tscore -= 10;\n\t\t\t\t}\n\n\t\t\t\t// Slight penalty for later matches\n\t\t\t\tscore += i * 0.1;\n\n\t\t\t\tlastMatchIndex = i;\n\t\t\t\tqueryIndex++;\n\t\t\t}\n\t\t}\n\n\t\tif (queryIndex < normalizedQuery.length) {\n\t\t\treturn { matches: false, score: 0 };\n\t\t}\n\n\t\treturn { matches: true, score };\n\t};\n\n\tconst primaryMatch = matchQuery(queryLower);\n\tif (primaryMatch.matches) {\n\t\treturn primaryMatch;\n\t}\n\n\tconst alphaNumericMatch = queryLower.match(/^(?<letters>[a-z]+)(?<digits>[0-9]+)$/);\n\tconst numericAlphaMatch = queryLower.match(/^(?<digits>[0-9]+)(?<letters>[a-z]+)$/);\n\tconst swappedQuery = alphaNumericMatch\n\t\t? `${alphaNumericMatch.groups?.digits ?? \"\"}${alphaNumericMatch.groups?.letters ?? \"\"}`\n\t\t: numericAlphaMatch\n\t\t\t? `${numericAlphaMatch.groups?.letters ?? \"\"}${numericAlphaMatch.groups?.digits ?? \"\"}`\n\t\t\t: \"\";\n\n\tif (!swappedQuery) {\n\t\treturn primaryMatch;\n\t}\n\n\tconst swappedMatch = matchQuery(swappedQuery);\n\tif (!swappedMatch.matches) {\n\t\treturn primaryMatch;\n\t}\n\n\treturn { matches: true, score: swappedMatch.score + 5 };\n}\n\n/**\n * Filter and sort items by fuzzy match quality (best matches first).\n * Supports space-separated tokens: all tokens must match.\n */\nexport function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) => string): T[] {\n\tif (!query.trim()) {\n\t\treturn items;\n\t}\n\n\tconst tokens = query\n\t\t.trim()\n\t\t.split(/\\s+/)\n\t\t.filter((t) => t.length > 0);\n\n\tif (tokens.length === 0) {\n\t\treturn items;\n\t}\n\n\tconst results: { item: T; totalScore: number }[] = [];\n\n\tfor (const item of items) {\n\t\tconst text = getText(item);\n\t\tlet totalScore = 0;\n\t\tlet allMatch = true;\n\n\t\tfor (const token of tokens) {\n\t\t\tconst match = fuzzyMatch(token, text);\n\t\t\tif (match.matches) {\n\t\t\t\ttotalScore += match.score;\n\t\t\t} else {\n\t\t\t\tallMatch = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (allMatch) {\n\t\t\tresults.push({ item, totalScore });\n\t\t}\n\t}\n\n\tresults.sort((a, b) => a.totalScore - b.totalScore);\n\treturn results.map((r) => r.item);\n}\n"]}
@@ -0,0 +1,23 @@
1
+ export { type AutocompleteItem, type AutocompleteProvider, type AutocompleteSuggestions, CombinedAutocompleteProvider, type SlashCommand, } from "./autocomplete.js";
2
+ export { Box } from "./components/box.js";
3
+ export { CancellableLoader } from "./components/cancellable-loader.js";
4
+ export { Editor, type EditorOptions, type EditorTheme } from "./components/editor.js";
5
+ export { Image, type ImageOptions, type ImageTheme } from "./components/image.js";
6
+ export { Input } from "./components/input.js";
7
+ export { Loader } from "./components/loader.js";
8
+ export { type DefaultTextStyle, Markdown, type MarkdownTheme } from "./components/markdown.js";
9
+ export { type SelectItem, SelectList, type SelectListLayoutOptions, type SelectListTheme, type SelectListTruncatePrimaryContext, } from "./components/select-list.js";
10
+ export { type SettingItem, SettingsList, type SettingsListTheme } from "./components/settings-list.js";
11
+ export { Spacer } from "./components/spacer.js";
12
+ export { Text } from "./components/text.js";
13
+ export { TruncatedText } from "./components/truncated-text.js";
14
+ export type { EditorComponent } from "./editor-component.js";
15
+ export { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from "./fuzzy.js";
16
+ export { getKeybindings, type Keybinding, type KeybindingConflict, type KeybindingDefinition, type KeybindingDefinitions, type Keybindings, type KeybindingsConfig, KeybindingsManager, setKeybindings, TUI_KEYBINDINGS, } from "./keybindings.js";
17
+ export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, Key, type KeyEventType, type KeyId, matchesKey, parseKey, setKittyProtocolActive, } from "./keys.js";
18
+ export { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from "./stdin-buffer.js";
19
+ export { ProcessTerminal, type Terminal } from "./terminal.js";
20
+ export { allocateImageId, type CellDimensions, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, type ImageDimensions, type ImageProtocol, type ImageRenderOptions, imageFallback, renderImage, resetCapabilitiesCache, setCellDimensions, type TerminalCapabilities, } from "./terminal-image.js";
21
+ export { type Component, Container, CURSOR_MARKER, type Focusable, isFocusable, type OverlayAnchor, type OverlayHandle, type OverlayMargin, type OverlayOptions, type SizeValue, TUI, } from "./tui.js";
22
+ export { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,YAAY,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,KAAK,gBAAgB,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EACN,KAAK,UAAU,EACf,UAAU,EACV,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,gCAAgC,GACrC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EACN,cAAc,EACd,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EACH,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEnG,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,EACN,eAAe,EACf,KAAK,cAAc,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,oBAAoB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,aAAa,EACb,KAAK,SAAS,EACd,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,GAAG,GACH,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.js\";\n// Components\nexport { Box } from \"./components/box.js\";\nexport { CancellableLoader } from \"./components/cancellable-loader.js\";\nexport { Editor, type EditorOptions, type EditorTheme } from \"./components/editor.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { Input } from \"./components/input.js\";\nexport { Loader } from \"./components/loader.js\";\nexport { type DefaultTextStyle, Markdown, type MarkdownTheme } from \"./components/markdown.js\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.js\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.js\";\nexport { Spacer } from \"./components/spacer.js\";\nexport { Text } from \"./components/text.js\";\nexport { TruncatedText } from \"./components/truncated-text.js\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.js\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.js\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.js\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.js\";\n// Input buffering for batch splitting\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.js\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.js\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.js\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype SizeValue,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ // Core TUI interfaces and classes
2
+ // Autocomplete support
3
+ export { CombinedAutocompleteProvider, } from "./autocomplete.js";
4
+ // Components
5
+ export { Box } from "./components/box.js";
6
+ export { CancellableLoader } from "./components/cancellable-loader.js";
7
+ export { Editor } from "./components/editor.js";
8
+ export { Image } from "./components/image.js";
9
+ export { Input } from "./components/input.js";
10
+ export { Loader } from "./components/loader.js";
11
+ export { Markdown } from "./components/markdown.js";
12
+ export { SelectList, } from "./components/select-list.js";
13
+ export { SettingsList } from "./components/settings-list.js";
14
+ export { Spacer } from "./components/spacer.js";
15
+ export { Text } from "./components/text.js";
16
+ export { TruncatedText } from "./components/truncated-text.js";
17
+ // Fuzzy matching
18
+ export { fuzzyFilter, fuzzyMatch } from "./fuzzy.js";
19
+ // Keybindings
20
+ export { getKeybindings, KeybindingsManager, setKeybindings, TUI_KEYBINDINGS, } from "./keybindings.js";
21
+ // Keyboard input handling
22
+ export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, Key, matchesKey, parseKey, setKittyProtocolActive, } from "./keys.js";
23
+ // Input buffering for batch splitting
24
+ export { StdinBuffer } from "./stdin-buffer.js";
25
+ // Terminal interface and implementations
26
+ export { ProcessTerminal } from "./terminal.js";
27
+ // Terminal image support
28
+ export { allocateImageId, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, imageFallback, renderImage, resetCapabilitiesCache, setCellDimensions, } from "./terminal-image.js";
29
+ export { Container, CURSOR_MARKER, isFocusable, TUI, } from "./tui.js";
30
+ // Utilities
31
+ export { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,uBAAuB;AACvB,OAAO,EAIN,4BAA4B,GAE5B,MAAM,mBAAmB,CAAC;AAC3B,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAwC,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAyB,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAEN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,YAAY,EAA0B,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,iBAAiB;AACjB,OAAO,EAAmB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,cAAc;AACd,OAAO,EACN,cAAc,EAOd,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,0BAA0B;AAC1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EAGH,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AACnB,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,yBAAyB;AACzB,OAAO,EACN,eAAe,EAEf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAIjB,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,iBAAiB,GAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEN,SAAS,EACT,aAAa,EAEb,WAAW,EAMX,GAAG,GACH,MAAM,UAAU,CAAC;AAClB,YAAY;AACZ,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.js\";\n// Components\nexport { Box } from \"./components/box.js\";\nexport { CancellableLoader } from \"./components/cancellable-loader.js\";\nexport { Editor, type EditorOptions, type EditorTheme } from \"./components/editor.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { Input } from \"./components/input.js\";\nexport { Loader } from \"./components/loader.js\";\nexport { type DefaultTextStyle, Markdown, type MarkdownTheme } from \"./components/markdown.js\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.js\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.js\";\nexport { Spacer } from \"./components/spacer.js\";\nexport { Text } from \"./components/text.js\";\nexport { TruncatedText } from \"./components/truncated-text.js\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.js\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.js\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.js\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.js\";\n// Input buffering for batch splitting\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.js\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.js\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.js\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype SizeValue,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
@@ -0,0 +1,193 @@
1
+ import { type KeyId } from "./keys.js";
2
+ /**
3
+ * Global keybinding registry.
4
+ * Downstream packages can add keybindings via declaration merging.
5
+ */
6
+ export interface Keybindings {
7
+ "tui.editor.cursorUp": true;
8
+ "tui.editor.cursorDown": true;
9
+ "tui.editor.cursorLeft": true;
10
+ "tui.editor.cursorRight": true;
11
+ "tui.editor.cursorWordLeft": true;
12
+ "tui.editor.cursorWordRight": true;
13
+ "tui.editor.cursorLineStart": true;
14
+ "tui.editor.cursorLineEnd": true;
15
+ "tui.editor.jumpForward": true;
16
+ "tui.editor.jumpBackward": true;
17
+ "tui.editor.pageUp": true;
18
+ "tui.editor.pageDown": true;
19
+ "tui.editor.deleteCharBackward": true;
20
+ "tui.editor.deleteCharForward": true;
21
+ "tui.editor.deleteWordBackward": true;
22
+ "tui.editor.deleteWordForward": true;
23
+ "tui.editor.deleteToLineStart": true;
24
+ "tui.editor.deleteToLineEnd": true;
25
+ "tui.editor.yank": true;
26
+ "tui.editor.yankPop": true;
27
+ "tui.editor.undo": true;
28
+ "tui.input.newLine": true;
29
+ "tui.input.submit": true;
30
+ "tui.input.tab": true;
31
+ "tui.input.copy": true;
32
+ "tui.select.up": true;
33
+ "tui.select.down": true;
34
+ "tui.select.pageUp": true;
35
+ "tui.select.pageDown": true;
36
+ "tui.select.confirm": true;
37
+ "tui.select.cancel": true;
38
+ }
39
+ export type Keybinding = keyof Keybindings;
40
+ export interface KeybindingDefinition {
41
+ defaultKeys: KeyId | KeyId[];
42
+ description?: string;
43
+ }
44
+ export type KeybindingDefinitions = Record<string, KeybindingDefinition>;
45
+ export type KeybindingsConfig = Record<string, KeyId | KeyId[] | undefined>;
46
+ export declare const TUI_KEYBINDINGS: {
47
+ readonly "tui.editor.cursorUp": {
48
+ readonly defaultKeys: "up";
49
+ readonly description: "Move cursor up";
50
+ };
51
+ readonly "tui.editor.cursorDown": {
52
+ readonly defaultKeys: "down";
53
+ readonly description: "Move cursor down";
54
+ };
55
+ readonly "tui.editor.cursorLeft": {
56
+ readonly defaultKeys: ["left", "ctrl+b"];
57
+ readonly description: "Move cursor left";
58
+ };
59
+ readonly "tui.editor.cursorRight": {
60
+ readonly defaultKeys: ["right", "ctrl+f"];
61
+ readonly description: "Move cursor right";
62
+ };
63
+ readonly "tui.editor.cursorWordLeft": {
64
+ readonly defaultKeys: ["alt+left", "ctrl+left", "alt+b"];
65
+ readonly description: "Move cursor word left";
66
+ };
67
+ readonly "tui.editor.cursorWordRight": {
68
+ readonly defaultKeys: ["alt+right", "ctrl+right", "alt+f"];
69
+ readonly description: "Move cursor word right";
70
+ };
71
+ readonly "tui.editor.cursorLineStart": {
72
+ readonly defaultKeys: ["home", "ctrl+a"];
73
+ readonly description: "Move to line start";
74
+ };
75
+ readonly "tui.editor.cursorLineEnd": {
76
+ readonly defaultKeys: ["end", "ctrl+e"];
77
+ readonly description: "Move to line end";
78
+ };
79
+ readonly "tui.editor.jumpForward": {
80
+ readonly defaultKeys: "ctrl+]";
81
+ readonly description: "Jump forward to character";
82
+ };
83
+ readonly "tui.editor.jumpBackward": {
84
+ readonly defaultKeys: "ctrl+alt+]";
85
+ readonly description: "Jump backward to character";
86
+ };
87
+ readonly "tui.editor.pageUp": {
88
+ readonly defaultKeys: "pageUp";
89
+ readonly description: "Page up";
90
+ };
91
+ readonly "tui.editor.pageDown": {
92
+ readonly defaultKeys: "pageDown";
93
+ readonly description: "Page down";
94
+ };
95
+ readonly "tui.editor.deleteCharBackward": {
96
+ readonly defaultKeys: "backspace";
97
+ readonly description: "Delete character backward";
98
+ };
99
+ readonly "tui.editor.deleteCharForward": {
100
+ readonly defaultKeys: ["delete", "ctrl+d"];
101
+ readonly description: "Delete character forward";
102
+ };
103
+ readonly "tui.editor.deleteWordBackward": {
104
+ readonly defaultKeys: ["ctrl+w", "alt+backspace"];
105
+ readonly description: "Delete word backward";
106
+ };
107
+ readonly "tui.editor.deleteWordForward": {
108
+ readonly defaultKeys: ["alt+d", "alt+delete"];
109
+ readonly description: "Delete word forward";
110
+ };
111
+ readonly "tui.editor.deleteToLineStart": {
112
+ readonly defaultKeys: "ctrl+u";
113
+ readonly description: "Delete to line start";
114
+ };
115
+ readonly "tui.editor.deleteToLineEnd": {
116
+ readonly defaultKeys: "ctrl+k";
117
+ readonly description: "Delete to line end";
118
+ };
119
+ readonly "tui.editor.yank": {
120
+ readonly defaultKeys: "ctrl+y";
121
+ readonly description: "Yank";
122
+ };
123
+ readonly "tui.editor.yankPop": {
124
+ readonly defaultKeys: "alt+y";
125
+ readonly description: "Yank pop";
126
+ };
127
+ readonly "tui.editor.undo": {
128
+ readonly defaultKeys: "ctrl+-";
129
+ readonly description: "Undo";
130
+ };
131
+ readonly "tui.input.newLine": {
132
+ readonly defaultKeys: "shift+enter";
133
+ readonly description: "Insert newline";
134
+ };
135
+ readonly "tui.input.submit": {
136
+ readonly defaultKeys: "enter";
137
+ readonly description: "Submit input";
138
+ };
139
+ readonly "tui.input.tab": {
140
+ readonly defaultKeys: "tab";
141
+ readonly description: "Tab / autocomplete";
142
+ };
143
+ readonly "tui.input.copy": {
144
+ readonly defaultKeys: "ctrl+c";
145
+ readonly description: "Copy selection";
146
+ };
147
+ readonly "tui.select.up": {
148
+ readonly defaultKeys: "up";
149
+ readonly description: "Move selection up";
150
+ };
151
+ readonly "tui.select.down": {
152
+ readonly defaultKeys: "down";
153
+ readonly description: "Move selection down";
154
+ };
155
+ readonly "tui.select.pageUp": {
156
+ readonly defaultKeys: "pageUp";
157
+ readonly description: "Selection page up";
158
+ };
159
+ readonly "tui.select.pageDown": {
160
+ readonly defaultKeys: "pageDown";
161
+ readonly description: "Selection page down";
162
+ };
163
+ readonly "tui.select.confirm": {
164
+ readonly defaultKeys: "enter";
165
+ readonly description: "Confirm selection";
166
+ };
167
+ readonly "tui.select.cancel": {
168
+ readonly defaultKeys: ["escape", "ctrl+c"];
169
+ readonly description: "Cancel selection";
170
+ };
171
+ };
172
+ export interface KeybindingConflict {
173
+ key: KeyId;
174
+ keybindings: string[];
175
+ }
176
+ export declare class KeybindingsManager {
177
+ private definitions;
178
+ private userBindings;
179
+ private keysById;
180
+ private conflicts;
181
+ constructor(definitions: KeybindingDefinitions, userBindings?: KeybindingsConfig);
182
+ private rebuild;
183
+ matches(data: string, keybinding: Keybinding): boolean;
184
+ getKeys(keybinding: Keybinding): KeyId[];
185
+ getDefinition(keybinding: Keybinding): KeybindingDefinition;
186
+ getConflicts(): KeybindingConflict[];
187
+ setUserBindings(userBindings: KeybindingsConfig): void;
188
+ getUserBindings(): KeybindingsConfig;
189
+ getResolvedBindings(): KeybindingsConfig;
190
+ }
191
+ export declare function setKeybindings(keybindings: KeybindingsManager): void;
192
+ export declare function getKeybindings(): KeybindingsManager;
193
+ //# sourceMappingURL=keybindings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keybindings.d.ts","sourceRoot":"","sources":["../src/keybindings.ts"],"names":[],"mappings":"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","sourcesContent":["import { type KeyId, matchesKey } from \"./keys.js\";\n\n/**\n * Global keybinding registry.\n * Downstream packages can add keybindings via declaration merging.\n */\nexport interface Keybindings {\n\t// Editor navigation and editing\n\t\"tui.editor.cursorUp\": true;\n\t\"tui.editor.cursorDown\": true;\n\t\"tui.editor.cursorLeft\": true;\n\t\"tui.editor.cursorRight\": true;\n\t\"tui.editor.cursorWordLeft\": true;\n\t\"tui.editor.cursorWordRight\": true;\n\t\"tui.editor.cursorLineStart\": true;\n\t\"tui.editor.cursorLineEnd\": true;\n\t\"tui.editor.jumpForward\": true;\n\t\"tui.editor.jumpBackward\": true;\n\t\"tui.editor.pageUp\": true;\n\t\"tui.editor.pageDown\": true;\n\t\"tui.editor.deleteCharBackward\": true;\n\t\"tui.editor.deleteCharForward\": true;\n\t\"tui.editor.deleteWordBackward\": true;\n\t\"tui.editor.deleteWordForward\": true;\n\t\"tui.editor.deleteToLineStart\": true;\n\t\"tui.editor.deleteToLineEnd\": true;\n\t\"tui.editor.yank\": true;\n\t\"tui.editor.yankPop\": true;\n\t\"tui.editor.undo\": true;\n\t// Generic input actions\n\t\"tui.input.newLine\": true;\n\t\"tui.input.submit\": true;\n\t\"tui.input.tab\": true;\n\t\"tui.input.copy\": true;\n\t// Generic selection actions\n\t\"tui.select.up\": true;\n\t\"tui.select.down\": true;\n\t\"tui.select.pageUp\": true;\n\t\"tui.select.pageDown\": true;\n\t\"tui.select.confirm\": true;\n\t\"tui.select.cancel\": true;\n}\n\nexport type Keybinding = keyof Keybindings;\n\nexport interface KeybindingDefinition {\n\tdefaultKeys: KeyId | KeyId[];\n\tdescription?: string;\n}\n\nexport type KeybindingDefinitions = Record<string, KeybindingDefinition>;\nexport type KeybindingsConfig = Record<string, KeyId | KeyId[] | undefined>;\n\nexport const TUI_KEYBINDINGS = {\n\t\"tui.editor.cursorUp\": { defaultKeys: \"up\", description: \"Move cursor up\" },\n\t\"tui.editor.cursorDown\": { defaultKeys: \"down\", description: \"Move cursor down\" },\n\t\"tui.editor.cursorLeft\": {\n\t\tdefaultKeys: [\"left\", \"ctrl+b\"],\n\t\tdescription: \"Move cursor left\",\n\t},\n\t\"tui.editor.cursorRight\": {\n\t\tdefaultKeys: [\"right\", \"ctrl+f\"],\n\t\tdescription: \"Move cursor right\",\n\t},\n\t\"tui.editor.cursorWordLeft\": {\n\t\tdefaultKeys: [\"alt+left\", \"ctrl+left\", \"alt+b\"],\n\t\tdescription: \"Move cursor word left\",\n\t},\n\t\"tui.editor.cursorWordRight\": {\n\t\tdefaultKeys: [\"alt+right\", \"ctrl+right\", \"alt+f\"],\n\t\tdescription: \"Move cursor word right\",\n\t},\n\t\"tui.editor.cursorLineStart\": {\n\t\tdefaultKeys: [\"home\", \"ctrl+a\"],\n\t\tdescription: \"Move to line start\",\n\t},\n\t\"tui.editor.cursorLineEnd\": {\n\t\tdefaultKeys: [\"end\", \"ctrl+e\"],\n\t\tdescription: \"Move to line end\",\n\t},\n\t\"tui.editor.jumpForward\": {\n\t\tdefaultKeys: \"ctrl+]\",\n\t\tdescription: \"Jump forward to character\",\n\t},\n\t\"tui.editor.jumpBackward\": {\n\t\tdefaultKeys: \"ctrl+alt+]\",\n\t\tdescription: \"Jump backward to character\",\n\t},\n\t\"tui.editor.pageUp\": { defaultKeys: \"pageUp\", description: \"Page up\" },\n\t\"tui.editor.pageDown\": { defaultKeys: \"pageDown\", description: \"Page down\" },\n\t\"tui.editor.deleteCharBackward\": {\n\t\tdefaultKeys: \"backspace\",\n\t\tdescription: \"Delete character backward\",\n\t},\n\t\"tui.editor.deleteCharForward\": {\n\t\tdefaultKeys: [\"delete\", \"ctrl+d\"],\n\t\tdescription: \"Delete character forward\",\n\t},\n\t\"tui.editor.deleteWordBackward\": {\n\t\tdefaultKeys: [\"ctrl+w\", \"alt+backspace\"],\n\t\tdescription: \"Delete word backward\",\n\t},\n\t\"tui.editor.deleteWordForward\": {\n\t\tdefaultKeys: [\"alt+d\", \"alt+delete\"],\n\t\tdescription: \"Delete word forward\",\n\t},\n\t\"tui.editor.deleteToLineStart\": {\n\t\tdefaultKeys: \"ctrl+u\",\n\t\tdescription: \"Delete to line start\",\n\t},\n\t\"tui.editor.deleteToLineEnd\": {\n\t\tdefaultKeys: \"ctrl+k\",\n\t\tdescription: \"Delete to line end\",\n\t},\n\t\"tui.editor.yank\": { defaultKeys: \"ctrl+y\", description: \"Yank\" },\n\t\"tui.editor.yankPop\": { defaultKeys: \"alt+y\", description: \"Yank pop\" },\n\t\"tui.editor.undo\": { defaultKeys: \"ctrl+-\", description: \"Undo\" },\n\t\"tui.input.newLine\": { defaultKeys: \"shift+enter\", description: \"Insert newline\" },\n\t\"tui.input.submit\": { defaultKeys: \"enter\", description: \"Submit input\" },\n\t\"tui.input.tab\": { defaultKeys: \"tab\", description: \"Tab / autocomplete\" },\n\t\"tui.input.copy\": { defaultKeys: \"ctrl+c\", description: \"Copy selection\" },\n\t\"tui.select.up\": { defaultKeys: \"up\", description: \"Move selection up\" },\n\t\"tui.select.down\": { defaultKeys: \"down\", description: \"Move selection down\" },\n\t\"tui.select.pageUp\": { defaultKeys: \"pageUp\", description: \"Selection page up\" },\n\t\"tui.select.pageDown\": {\n\t\tdefaultKeys: \"pageDown\",\n\t\tdescription: \"Selection page down\",\n\t},\n\t\"tui.select.confirm\": { defaultKeys: \"enter\", description: \"Confirm selection\" },\n\t\"tui.select.cancel\": {\n\t\tdefaultKeys: [\"escape\", \"ctrl+c\"],\n\t\tdescription: \"Cancel selection\",\n\t},\n} as const satisfies KeybindingDefinitions;\n\nexport interface KeybindingConflict {\n\tkey: KeyId;\n\tkeybindings: string[];\n}\n\nfunction normalizeKeys(keys: KeyId | KeyId[] | undefined): KeyId[] {\n\tif (keys === undefined) return [];\n\tconst keyList = Array.isArray(keys) ? keys : [keys];\n\tconst seen = new Set<KeyId>();\n\tconst result: KeyId[] = [];\n\tfor (const key of keyList) {\n\t\tif (!seen.has(key)) {\n\t\t\tseen.add(key);\n\t\t\tresult.push(key);\n\t\t}\n\t}\n\treturn result;\n}\n\nexport class KeybindingsManager {\n\tprivate definitions: KeybindingDefinitions;\n\tprivate userBindings: KeybindingsConfig;\n\tprivate keysById = new Map<Keybinding, KeyId[]>();\n\tprivate conflicts: KeybindingConflict[] = [];\n\n\tconstructor(definitions: KeybindingDefinitions, userBindings: KeybindingsConfig = {}) {\n\t\tthis.definitions = definitions;\n\t\tthis.userBindings = userBindings;\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.keysById.clear();\n\t\tthis.conflicts = [];\n\n\t\tconst userClaims = new Map<KeyId, Set<Keybinding>>();\n\t\tfor (const [keybinding, keys] of Object.entries(this.userBindings)) {\n\t\t\tif (!(keybinding in this.definitions)) continue;\n\t\t\tfor (const key of normalizeKeys(keys)) {\n\t\t\t\tconst claimants = userClaims.get(key) ?? new Set<Keybinding>();\n\t\t\t\tclaimants.add(keybinding as Keybinding);\n\t\t\t\tuserClaims.set(key, claimants);\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, keybindings] of userClaims) {\n\t\t\tif (keybindings.size > 1) {\n\t\t\t\tthis.conflicts.push({ key, keybindings: [...keybindings] });\n\t\t\t}\n\t\t}\n\n\t\tfor (const [id, definition] of Object.entries(this.definitions)) {\n\t\t\tconst userKeys = this.userBindings[id];\n\t\t\tconst keys = userKeys === undefined ? normalizeKeys(definition.defaultKeys) : normalizeKeys(userKeys);\n\t\t\tthis.keysById.set(id as Keybinding, keys);\n\t\t}\n\t}\n\n\tmatches(data: string, keybinding: Keybinding): boolean {\n\t\tconst keys = this.keysById.get(keybinding) ?? [];\n\t\tfor (const key of keys) {\n\t\t\tif (matchesKey(data, key)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tgetKeys(keybinding: Keybinding): KeyId[] {\n\t\treturn [...(this.keysById.get(keybinding) ?? [])];\n\t}\n\n\tgetDefinition(keybinding: Keybinding): KeybindingDefinition {\n\t\treturn this.definitions[keybinding];\n\t}\n\n\tgetConflicts(): KeybindingConflict[] {\n\t\treturn this.conflicts.map((conflict) => ({ ...conflict, keybindings: [...conflict.keybindings] }));\n\t}\n\n\tsetUserBindings(userBindings: KeybindingsConfig): void {\n\t\tthis.userBindings = userBindings;\n\t\tthis.rebuild();\n\t}\n\n\tgetUserBindings(): KeybindingsConfig {\n\t\treturn { ...this.userBindings };\n\t}\n\n\tgetResolvedBindings(): KeybindingsConfig {\n\t\tconst resolved: KeybindingsConfig = {};\n\t\tfor (const id of Object.keys(this.definitions)) {\n\t\t\tconst keys = this.keysById.get(id as Keybinding) ?? [];\n\t\t\tresolved[id] = keys.length === 1 ? keys[0]! : [...keys];\n\t\t}\n\t\treturn resolved;\n\t}\n}\n\nlet globalKeybindings: KeybindingsManager | null = null;\n\nexport function setKeybindings(keybindings: KeybindingsManager): void {\n\tglobalKeybindings = keybindings;\n}\n\nexport function getKeybindings(): KeybindingsManager {\n\tif (!globalKeybindings) {\n\t\tglobalKeybindings = new KeybindingsManager(TUI_KEYBINDINGS);\n\t}\n\treturn globalKeybindings;\n}\n"]}
@@ -0,0 +1,174 @@
1
+ import { matchesKey } from "./keys.js";
2
+ export const TUI_KEYBINDINGS = {
3
+ "tui.editor.cursorUp": { defaultKeys: "up", description: "Move cursor up" },
4
+ "tui.editor.cursorDown": { defaultKeys: "down", description: "Move cursor down" },
5
+ "tui.editor.cursorLeft": {
6
+ defaultKeys: ["left", "ctrl+b"],
7
+ description: "Move cursor left",
8
+ },
9
+ "tui.editor.cursorRight": {
10
+ defaultKeys: ["right", "ctrl+f"],
11
+ description: "Move cursor right",
12
+ },
13
+ "tui.editor.cursorWordLeft": {
14
+ defaultKeys: ["alt+left", "ctrl+left", "alt+b"],
15
+ description: "Move cursor word left",
16
+ },
17
+ "tui.editor.cursorWordRight": {
18
+ defaultKeys: ["alt+right", "ctrl+right", "alt+f"],
19
+ description: "Move cursor word right",
20
+ },
21
+ "tui.editor.cursorLineStart": {
22
+ defaultKeys: ["home", "ctrl+a"],
23
+ description: "Move to line start",
24
+ },
25
+ "tui.editor.cursorLineEnd": {
26
+ defaultKeys: ["end", "ctrl+e"],
27
+ description: "Move to line end",
28
+ },
29
+ "tui.editor.jumpForward": {
30
+ defaultKeys: "ctrl+]",
31
+ description: "Jump forward to character",
32
+ },
33
+ "tui.editor.jumpBackward": {
34
+ defaultKeys: "ctrl+alt+]",
35
+ description: "Jump backward to character",
36
+ },
37
+ "tui.editor.pageUp": { defaultKeys: "pageUp", description: "Page up" },
38
+ "tui.editor.pageDown": { defaultKeys: "pageDown", description: "Page down" },
39
+ "tui.editor.deleteCharBackward": {
40
+ defaultKeys: "backspace",
41
+ description: "Delete character backward",
42
+ },
43
+ "tui.editor.deleteCharForward": {
44
+ defaultKeys: ["delete", "ctrl+d"],
45
+ description: "Delete character forward",
46
+ },
47
+ "tui.editor.deleteWordBackward": {
48
+ defaultKeys: ["ctrl+w", "alt+backspace"],
49
+ description: "Delete word backward",
50
+ },
51
+ "tui.editor.deleteWordForward": {
52
+ defaultKeys: ["alt+d", "alt+delete"],
53
+ description: "Delete word forward",
54
+ },
55
+ "tui.editor.deleteToLineStart": {
56
+ defaultKeys: "ctrl+u",
57
+ description: "Delete to line start",
58
+ },
59
+ "tui.editor.deleteToLineEnd": {
60
+ defaultKeys: "ctrl+k",
61
+ description: "Delete to line end",
62
+ },
63
+ "tui.editor.yank": { defaultKeys: "ctrl+y", description: "Yank" },
64
+ "tui.editor.yankPop": { defaultKeys: "alt+y", description: "Yank pop" },
65
+ "tui.editor.undo": { defaultKeys: "ctrl+-", description: "Undo" },
66
+ "tui.input.newLine": { defaultKeys: "shift+enter", description: "Insert newline" },
67
+ "tui.input.submit": { defaultKeys: "enter", description: "Submit input" },
68
+ "tui.input.tab": { defaultKeys: "tab", description: "Tab / autocomplete" },
69
+ "tui.input.copy": { defaultKeys: "ctrl+c", description: "Copy selection" },
70
+ "tui.select.up": { defaultKeys: "up", description: "Move selection up" },
71
+ "tui.select.down": { defaultKeys: "down", description: "Move selection down" },
72
+ "tui.select.pageUp": { defaultKeys: "pageUp", description: "Selection page up" },
73
+ "tui.select.pageDown": {
74
+ defaultKeys: "pageDown",
75
+ description: "Selection page down",
76
+ },
77
+ "tui.select.confirm": { defaultKeys: "enter", description: "Confirm selection" },
78
+ "tui.select.cancel": {
79
+ defaultKeys: ["escape", "ctrl+c"],
80
+ description: "Cancel selection",
81
+ },
82
+ };
83
+ function normalizeKeys(keys) {
84
+ if (keys === undefined)
85
+ return [];
86
+ const keyList = Array.isArray(keys) ? keys : [keys];
87
+ const seen = new Set();
88
+ const result = [];
89
+ for (const key of keyList) {
90
+ if (!seen.has(key)) {
91
+ seen.add(key);
92
+ result.push(key);
93
+ }
94
+ }
95
+ return result;
96
+ }
97
+ export class KeybindingsManager {
98
+ definitions;
99
+ userBindings;
100
+ keysById = new Map();
101
+ conflicts = [];
102
+ constructor(definitions, userBindings = {}) {
103
+ this.definitions = definitions;
104
+ this.userBindings = userBindings;
105
+ this.rebuild();
106
+ }
107
+ rebuild() {
108
+ this.keysById.clear();
109
+ this.conflicts = [];
110
+ const userClaims = new Map();
111
+ for (const [keybinding, keys] of Object.entries(this.userBindings)) {
112
+ if (!(keybinding in this.definitions))
113
+ continue;
114
+ for (const key of normalizeKeys(keys)) {
115
+ const claimants = userClaims.get(key) ?? new Set();
116
+ claimants.add(keybinding);
117
+ userClaims.set(key, claimants);
118
+ }
119
+ }
120
+ for (const [key, keybindings] of userClaims) {
121
+ if (keybindings.size > 1) {
122
+ this.conflicts.push({ key, keybindings: [...keybindings] });
123
+ }
124
+ }
125
+ for (const [id, definition] of Object.entries(this.definitions)) {
126
+ const userKeys = this.userBindings[id];
127
+ const keys = userKeys === undefined ? normalizeKeys(definition.defaultKeys) : normalizeKeys(userKeys);
128
+ this.keysById.set(id, keys);
129
+ }
130
+ }
131
+ matches(data, keybinding) {
132
+ const keys = this.keysById.get(keybinding) ?? [];
133
+ for (const key of keys) {
134
+ if (matchesKey(data, key))
135
+ return true;
136
+ }
137
+ return false;
138
+ }
139
+ getKeys(keybinding) {
140
+ return [...(this.keysById.get(keybinding) ?? [])];
141
+ }
142
+ getDefinition(keybinding) {
143
+ return this.definitions[keybinding];
144
+ }
145
+ getConflicts() {
146
+ return this.conflicts.map((conflict) => ({ ...conflict, keybindings: [...conflict.keybindings] }));
147
+ }
148
+ setUserBindings(userBindings) {
149
+ this.userBindings = userBindings;
150
+ this.rebuild();
151
+ }
152
+ getUserBindings() {
153
+ return { ...this.userBindings };
154
+ }
155
+ getResolvedBindings() {
156
+ const resolved = {};
157
+ for (const id of Object.keys(this.definitions)) {
158
+ const keys = this.keysById.get(id) ?? [];
159
+ resolved[id] = keys.length === 1 ? keys[0] : [...keys];
160
+ }
161
+ return resolved;
162
+ }
163
+ }
164
+ let globalKeybindings = null;
165
+ export function setKeybindings(keybindings) {
166
+ globalKeybindings = keybindings;
167
+ }
168
+ export function getKeybindings() {
169
+ if (!globalKeybindings) {
170
+ globalKeybindings = new KeybindingsManager(TUI_KEYBINDINGS);
171
+ }
172
+ return globalKeybindings;
173
+ }
174
+ //# sourceMappingURL=keybindings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keybindings.js","sourceRoot":"","sources":["../src/keybindings.ts"],"names":[],"mappings":"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","sourcesContent":["import { type KeyId, matchesKey } from \"./keys.js\";\n\n/**\n * Global keybinding registry.\n * Downstream packages can add keybindings via declaration merging.\n */\nexport interface Keybindings {\n\t// Editor navigation and editing\n\t\"tui.editor.cursorUp\": true;\n\t\"tui.editor.cursorDown\": true;\n\t\"tui.editor.cursorLeft\": true;\n\t\"tui.editor.cursorRight\": true;\n\t\"tui.editor.cursorWordLeft\": true;\n\t\"tui.editor.cursorWordRight\": true;\n\t\"tui.editor.cursorLineStart\": true;\n\t\"tui.editor.cursorLineEnd\": true;\n\t\"tui.editor.jumpForward\": true;\n\t\"tui.editor.jumpBackward\": true;\n\t\"tui.editor.pageUp\": true;\n\t\"tui.editor.pageDown\": true;\n\t\"tui.editor.deleteCharBackward\": true;\n\t\"tui.editor.deleteCharForward\": true;\n\t\"tui.editor.deleteWordBackward\": true;\n\t\"tui.editor.deleteWordForward\": true;\n\t\"tui.editor.deleteToLineStart\": true;\n\t\"tui.editor.deleteToLineEnd\": true;\n\t\"tui.editor.yank\": true;\n\t\"tui.editor.yankPop\": true;\n\t\"tui.editor.undo\": true;\n\t// Generic input actions\n\t\"tui.input.newLine\": true;\n\t\"tui.input.submit\": true;\n\t\"tui.input.tab\": true;\n\t\"tui.input.copy\": true;\n\t// Generic selection actions\n\t\"tui.select.up\": true;\n\t\"tui.select.down\": true;\n\t\"tui.select.pageUp\": true;\n\t\"tui.select.pageDown\": true;\n\t\"tui.select.confirm\": true;\n\t\"tui.select.cancel\": true;\n}\n\nexport type Keybinding = keyof Keybindings;\n\nexport interface KeybindingDefinition {\n\tdefaultKeys: KeyId | KeyId[];\n\tdescription?: string;\n}\n\nexport type KeybindingDefinitions = Record<string, KeybindingDefinition>;\nexport type KeybindingsConfig = Record<string, KeyId | KeyId[] | undefined>;\n\nexport const TUI_KEYBINDINGS = {\n\t\"tui.editor.cursorUp\": { defaultKeys: \"up\", description: \"Move cursor up\" },\n\t\"tui.editor.cursorDown\": { defaultKeys: \"down\", description: \"Move cursor down\" },\n\t\"tui.editor.cursorLeft\": {\n\t\tdefaultKeys: [\"left\", \"ctrl+b\"],\n\t\tdescription: \"Move cursor left\",\n\t},\n\t\"tui.editor.cursorRight\": {\n\t\tdefaultKeys: [\"right\", \"ctrl+f\"],\n\t\tdescription: \"Move cursor right\",\n\t},\n\t\"tui.editor.cursorWordLeft\": {\n\t\tdefaultKeys: [\"alt+left\", \"ctrl+left\", \"alt+b\"],\n\t\tdescription: \"Move cursor word left\",\n\t},\n\t\"tui.editor.cursorWordRight\": {\n\t\tdefaultKeys: [\"alt+right\", \"ctrl+right\", \"alt+f\"],\n\t\tdescription: \"Move cursor word right\",\n\t},\n\t\"tui.editor.cursorLineStart\": {\n\t\tdefaultKeys: [\"home\", \"ctrl+a\"],\n\t\tdescription: \"Move to line start\",\n\t},\n\t\"tui.editor.cursorLineEnd\": {\n\t\tdefaultKeys: [\"end\", \"ctrl+e\"],\n\t\tdescription: \"Move to line end\",\n\t},\n\t\"tui.editor.jumpForward\": {\n\t\tdefaultKeys: \"ctrl+]\",\n\t\tdescription: \"Jump forward to character\",\n\t},\n\t\"tui.editor.jumpBackward\": {\n\t\tdefaultKeys: \"ctrl+alt+]\",\n\t\tdescription: \"Jump backward to character\",\n\t},\n\t\"tui.editor.pageUp\": { defaultKeys: \"pageUp\", description: \"Page up\" },\n\t\"tui.editor.pageDown\": { defaultKeys: \"pageDown\", description: \"Page down\" },\n\t\"tui.editor.deleteCharBackward\": {\n\t\tdefaultKeys: \"backspace\",\n\t\tdescription: \"Delete character backward\",\n\t},\n\t\"tui.editor.deleteCharForward\": {\n\t\tdefaultKeys: [\"delete\", \"ctrl+d\"],\n\t\tdescription: \"Delete character forward\",\n\t},\n\t\"tui.editor.deleteWordBackward\": {\n\t\tdefaultKeys: [\"ctrl+w\", \"alt+backspace\"],\n\t\tdescription: \"Delete word backward\",\n\t},\n\t\"tui.editor.deleteWordForward\": {\n\t\tdefaultKeys: [\"alt+d\", \"alt+delete\"],\n\t\tdescription: \"Delete word forward\",\n\t},\n\t\"tui.editor.deleteToLineStart\": {\n\t\tdefaultKeys: \"ctrl+u\",\n\t\tdescription: \"Delete to line start\",\n\t},\n\t\"tui.editor.deleteToLineEnd\": {\n\t\tdefaultKeys: \"ctrl+k\",\n\t\tdescription: \"Delete to line end\",\n\t},\n\t\"tui.editor.yank\": { defaultKeys: \"ctrl+y\", description: \"Yank\" },\n\t\"tui.editor.yankPop\": { defaultKeys: \"alt+y\", description: \"Yank pop\" },\n\t\"tui.editor.undo\": { defaultKeys: \"ctrl+-\", description: \"Undo\" },\n\t\"tui.input.newLine\": { defaultKeys: \"shift+enter\", description: \"Insert newline\" },\n\t\"tui.input.submit\": { defaultKeys: \"enter\", description: \"Submit input\" },\n\t\"tui.input.tab\": { defaultKeys: \"tab\", description: \"Tab / autocomplete\" },\n\t\"tui.input.copy\": { defaultKeys: \"ctrl+c\", description: \"Copy selection\" },\n\t\"tui.select.up\": { defaultKeys: \"up\", description: \"Move selection up\" },\n\t\"tui.select.down\": { defaultKeys: \"down\", description: \"Move selection down\" },\n\t\"tui.select.pageUp\": { defaultKeys: \"pageUp\", description: \"Selection page up\" },\n\t\"tui.select.pageDown\": {\n\t\tdefaultKeys: \"pageDown\",\n\t\tdescription: \"Selection page down\",\n\t},\n\t\"tui.select.confirm\": { defaultKeys: \"enter\", description: \"Confirm selection\" },\n\t\"tui.select.cancel\": {\n\t\tdefaultKeys: [\"escape\", \"ctrl+c\"],\n\t\tdescription: \"Cancel selection\",\n\t},\n} as const satisfies KeybindingDefinitions;\n\nexport interface KeybindingConflict {\n\tkey: KeyId;\n\tkeybindings: string[];\n}\n\nfunction normalizeKeys(keys: KeyId | KeyId[] | undefined): KeyId[] {\n\tif (keys === undefined) return [];\n\tconst keyList = Array.isArray(keys) ? keys : [keys];\n\tconst seen = new Set<KeyId>();\n\tconst result: KeyId[] = [];\n\tfor (const key of keyList) {\n\t\tif (!seen.has(key)) {\n\t\t\tseen.add(key);\n\t\t\tresult.push(key);\n\t\t}\n\t}\n\treturn result;\n}\n\nexport class KeybindingsManager {\n\tprivate definitions: KeybindingDefinitions;\n\tprivate userBindings: KeybindingsConfig;\n\tprivate keysById = new Map<Keybinding, KeyId[]>();\n\tprivate conflicts: KeybindingConflict[] = [];\n\n\tconstructor(definitions: KeybindingDefinitions, userBindings: KeybindingsConfig = {}) {\n\t\tthis.definitions = definitions;\n\t\tthis.userBindings = userBindings;\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.keysById.clear();\n\t\tthis.conflicts = [];\n\n\t\tconst userClaims = new Map<KeyId, Set<Keybinding>>();\n\t\tfor (const [keybinding, keys] of Object.entries(this.userBindings)) {\n\t\t\tif (!(keybinding in this.definitions)) continue;\n\t\t\tfor (const key of normalizeKeys(keys)) {\n\t\t\t\tconst claimants = userClaims.get(key) ?? new Set<Keybinding>();\n\t\t\t\tclaimants.add(keybinding as Keybinding);\n\t\t\t\tuserClaims.set(key, claimants);\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, keybindings] of userClaims) {\n\t\t\tif (keybindings.size > 1) {\n\t\t\t\tthis.conflicts.push({ key, keybindings: [...keybindings] });\n\t\t\t}\n\t\t}\n\n\t\tfor (const [id, definition] of Object.entries(this.definitions)) {\n\t\t\tconst userKeys = this.userBindings[id];\n\t\t\tconst keys = userKeys === undefined ? normalizeKeys(definition.defaultKeys) : normalizeKeys(userKeys);\n\t\t\tthis.keysById.set(id as Keybinding, keys);\n\t\t}\n\t}\n\n\tmatches(data: string, keybinding: Keybinding): boolean {\n\t\tconst keys = this.keysById.get(keybinding) ?? [];\n\t\tfor (const key of keys) {\n\t\t\tif (matchesKey(data, key)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tgetKeys(keybinding: Keybinding): KeyId[] {\n\t\treturn [...(this.keysById.get(keybinding) ?? [])];\n\t}\n\n\tgetDefinition(keybinding: Keybinding): KeybindingDefinition {\n\t\treturn this.definitions[keybinding];\n\t}\n\n\tgetConflicts(): KeybindingConflict[] {\n\t\treturn this.conflicts.map((conflict) => ({ ...conflict, keybindings: [...conflict.keybindings] }));\n\t}\n\n\tsetUserBindings(userBindings: KeybindingsConfig): void {\n\t\tthis.userBindings = userBindings;\n\t\tthis.rebuild();\n\t}\n\n\tgetUserBindings(): KeybindingsConfig {\n\t\treturn { ...this.userBindings };\n\t}\n\n\tgetResolvedBindings(): KeybindingsConfig {\n\t\tconst resolved: KeybindingsConfig = {};\n\t\tfor (const id of Object.keys(this.definitions)) {\n\t\t\tconst keys = this.keysById.get(id as Keybinding) ?? [];\n\t\t\tresolved[id] = keys.length === 1 ? keys[0]! : [...keys];\n\t\t}\n\t\treturn resolved;\n\t}\n}\n\nlet globalKeybindings: KeybindingsManager | null = null;\n\nexport function setKeybindings(keybindings: KeybindingsManager): void {\n\tglobalKeybindings = keybindings;\n}\n\nexport function getKeybindings(): KeybindingsManager {\n\tif (!globalKeybindings) {\n\t\tglobalKeybindings = new KeybindingsManager(TUI_KEYBINDINGS);\n\t}\n\treturn globalKeybindings;\n}\n"]}