@zaly/tui 0.0.4
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/LICENSE +21 -0
- package/README.md +59 -0
- package/assets/schemas/theme.schema.json +899 -0
- package/assets/themes/LICENSE-tokyonight.txt +201 -0
- package/assets/themes/NOTICES.md +69 -0
- package/assets/themes/ayu-dark.json +119 -0
- package/assets/themes/catppuccin-frappe.json +124 -0
- package/assets/themes/catppuccin-latte.json +124 -0
- package/assets/themes/catppuccin-macchiato.json +124 -0
- package/assets/themes/catppuccin-mocha.json +124 -0
- package/assets/themes/dracula-soft.json +119 -0
- package/assets/themes/dracula.json +119 -0
- package/assets/themes/everforest-dark.json +118 -0
- package/assets/themes/everforest-light.json +118 -0
- package/assets/themes/github-dark.json +123 -0
- package/assets/themes/github-light.json +123 -0
- package/assets/themes/gruvbox-dark-medium.json +112 -0
- package/assets/themes/kanagawa-dragon.json +115 -0
- package/assets/themes/kanagawa-wave.json +115 -0
- package/assets/themes/material-theme-palenight.json +114 -0
- package/assets/themes/min-dark.json +55 -0
- package/assets/themes/min-light.json +59 -0
- package/assets/themes/monokai.json +116 -0
- package/assets/themes/night-owl.json +116 -0
- package/assets/themes/nord.json +101 -0
- package/assets/themes/one-dark-pro.json +110 -0
- package/assets/themes/one-light.json +108 -0
- package/assets/themes/poimandres.json +123 -0
- package/assets/themes/rose-pine.json +98 -0
- package/assets/themes/solarized-dark.json +116 -0
- package/assets/themes/solarized-light.json +116 -0
- package/assets/themes/synthwave-84.json +100 -0
- package/assets/themes/tokyonight-day.json +157 -0
- package/assets/themes/tokyonight-moon.json +157 -0
- package/assets/themes/tokyonight-night.json +157 -0
- package/assets/themes/tokyonight-storm.json +157 -0
- package/assets/themes/vesper.json +103 -0
- package/assets/themes/vitesse-dark.json +126 -0
- package/assets/themes/vitesse-light.json +126 -0
- package/dist/ansi-Clq0ozyA.mjs +206 -0
- package/dist/ansi-Clq0ozyA.mjs.map +1 -0
- package/dist/ansi.d.mts +48 -0
- package/dist/ansi.d.mts.map +1 -0
- package/dist/ansi.mjs +2 -0
- package/dist/autocomplete-Dfw-yQDa.d.mts +100 -0
- package/dist/autocomplete-Dfw-yQDa.d.mts.map +1 -0
- package/dist/box-DyaByEiG.mjs +386 -0
- package/dist/box-DyaByEiG.mjs.map +1 -0
- package/dist/builder-D49ECBEf.mjs +385 -0
- package/dist/builder-D49ECBEf.mjs.map +1 -0
- package/dist/callbacks-CNjGNxVw.mjs +279 -0
- package/dist/callbacks-CNjGNxVw.mjs.map +1 -0
- package/dist/client-D6C_uczF.mjs +142 -0
- package/dist/client-D6C_uczF.mjs.map +1 -0
- package/dist/clipboard.d.mts +104 -0
- package/dist/clipboard.d.mts.map +1 -0
- package/dist/clipboard.mjs +404 -0
- package/dist/clipboard.mjs.map +1 -0
- package/dist/flex-Bguo-fUe.mjs +202 -0
- package/dist/flex-Bguo-fUe.mjs.map +1 -0
- package/dist/image-Cm5T-uw-.mjs +108 -0
- package/dist/image-Cm5T-uw-.mjs.map +1 -0
- package/dist/image.d.mts +123 -0
- package/dist/image.d.mts.map +1 -0
- package/dist/image.mjs +2 -0
- package/dist/index.d.mts +64 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +222 -0
- package/dist/index.mjs.map +1 -0
- package/dist/input-B5Vy-iaa.d.mts +141 -0
- package/dist/input-B5Vy-iaa.d.mts.map +1 -0
- package/dist/inspect-D1d9t-ni.mjs +101 -0
- package/dist/inspect-D1d9t-ni.mjs.map +1 -0
- package/dist/inspect-D6FW_QUo.d.mts +67 -0
- package/dist/inspect-D6FW_QUo.d.mts.map +1 -0
- package/dist/kitty-JbCJC0iQ.mjs +673 -0
- package/dist/kitty-JbCJC0iQ.mjs.map +1 -0
- package/dist/markdown.d.mts +24 -0
- package/dist/markdown.d.mts.map +1 -0
- package/dist/markdown.mjs +2 -0
- package/dist/md.bun.d.mts +14 -0
- package/dist/md.bun.d.mts.map +1 -0
- package/dist/md.bun.mjs +28 -0
- package/dist/md.bun.mjs.map +1 -0
- package/dist/md.node.d.mts +14 -0
- package/dist/md.node.d.mts.map +1 -0
- package/dist/md.node.mjs +126 -0
- package/dist/md.node.mjs.map +1 -0
- package/dist/node-BZ7f6HzU.mjs +316 -0
- package/dist/node-BZ7f6HzU.mjs.map +1 -0
- package/dist/picker-D8OLb6kZ.d.mts +36 -0
- package/dist/picker-D8OLb6kZ.d.mts.map +1 -0
- package/dist/picker-ndVVt6qF.mjs +621 -0
- package/dist/picker-ndVVt6qF.mjs.map +1 -0
- package/dist/queries-DH3bj74K.d.mts +1963 -0
- package/dist/queries-DH3bj74K.d.mts.map +1 -0
- package/dist/reactive-f7NpAi69.mjs +623 -0
- package/dist/reactive-f7NpAi69.mjs.map +1 -0
- package/dist/registry-U654j61Y.mjs +356 -0
- package/dist/registry-U654j61Y.mjs.map +1 -0
- package/dist/render-DxVLqqB9.mjs +74 -0
- package/dist/render-DxVLqqB9.mjs.map +1 -0
- package/dist/renderer-CdLCkJB-.mjs +42 -0
- package/dist/renderer-CdLCkJB-.mjs.map +1 -0
- package/dist/renderer-CxL4xbkl.mjs +2644 -0
- package/dist/renderer-CxL4xbkl.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/search-FvGnswRl.d.mts +49 -0
- package/dist/search-FvGnswRl.d.mts.map +1 -0
- package/dist/select-CbmAZdpt.d.mts +116 -0
- package/dist/select-CbmAZdpt.d.mts.map +1 -0
- package/dist/services/logger.d.mts +2 -0
- package/dist/services/logger.mjs +71 -0
- package/dist/services/logger.mjs.map +1 -0
- package/dist/services/notifier.d.mts +17 -0
- package/dist/services/notifier.d.mts.map +1 -0
- package/dist/services/notifier.mjs +71 -0
- package/dist/services/notifier.mjs.map +1 -0
- package/dist/services/picker.d.mts +36 -0
- package/dist/services/picker.d.mts.map +1 -0
- package/dist/services/picker.mjs +137 -0
- package/dist/services/picker.mjs.map +1 -0
- package/dist/services/prompt.d.mts +30 -0
- package/dist/services/prompt.d.mts.map +1 -0
- package/dist/services/prompt.mjs +83 -0
- package/dist/services/prompt.mjs.map +1 -0
- package/dist/shiki-BrjVUkeh.mjs +423 -0
- package/dist/shiki-BrjVUkeh.mjs.map +1 -0
- package/dist/size-D8JogYLy.mjs +34 -0
- package/dist/size-D8JogYLy.mjs.map +1 -0
- package/dist/style.d.mts +10 -0
- package/dist/style.d.mts.map +1 -0
- package/dist/style.mjs +4 -0
- package/dist/text.d.mts +33 -0
- package/dist/text.d.mts.map +1 -0
- package/dist/text.mjs +111 -0
- package/dist/text.mjs.map +1 -0
- package/dist/theme.config-CU3B50HN.mjs +832 -0
- package/dist/theme.config-CU3B50HN.mjs.map +1 -0
- package/dist/themes.d.mts +113 -0
- package/dist/themes.d.mts.map +1 -0
- package/dist/themes.mjs +2 -0
- package/dist/tree-C28QOVIK.d.mts +23 -0
- package/dist/tree-C28QOVIK.d.mts.map +1 -0
- package/dist/types-B9Eh7k2v.d.mts +212 -0
- package/dist/types-B9Eh7k2v.d.mts.map +1 -0
- package/dist/types-D3jk1rtk.d.mts +140 -0
- package/dist/types-D3jk1rtk.d.mts.map +1 -0
- package/dist/utils-3M4D_AXZ.mjs +40 -0
- package/dist/utils-3M4D_AXZ.mjs.map +1 -0
- package/dist/widgets/autocomplete.d.mts +2 -0
- package/dist/widgets/autocomplete.mjs +165 -0
- package/dist/widgets/autocomplete.mjs.map +1 -0
- package/dist/widgets/box.d.mts +2 -0
- package/dist/widgets/box.mjs +2 -0
- package/dist/widgets/code.d.mts +48 -0
- package/dist/widgets/code.d.mts.map +1 -0
- package/dist/widgets/code.mjs +59 -0
- package/dist/widgets/code.mjs.map +1 -0
- package/dist/widgets/completions/actions.d.mts +41 -0
- package/dist/widgets/completions/actions.d.mts.map +1 -0
- package/dist/widgets/completions/actions.mjs +48 -0
- package/dist/widgets/completions/actions.mjs.map +1 -0
- package/dist/widgets/completions/files.d.mts +29 -0
- package/dist/widgets/completions/files.d.mts.map +1 -0
- package/dist/widgets/completions/files.mjs +42 -0
- package/dist/widgets/completions/files.mjs.map +1 -0
- package/dist/widgets/completions/github.d.mts +64 -0
- package/dist/widgets/completions/github.d.mts.map +1 -0
- package/dist/widgets/completions/github.mjs +137 -0
- package/dist/widgets/completions/github.mjs.map +1 -0
- package/dist/widgets/completions/index.d.mts +4 -0
- package/dist/widgets/completions/index.mjs +4 -0
- package/dist/widgets/diff.d.mts +57 -0
- package/dist/widgets/diff.d.mts.map +1 -0
- package/dist/widgets/diff.mjs +199 -0
- package/dist/widgets/diff.mjs.map +1 -0
- package/dist/widgets/divider.d.mts +11 -0
- package/dist/widgets/divider.d.mts.map +1 -0
- package/dist/widgets/divider.mjs +9 -0
- package/dist/widgets/divider.mjs.map +1 -0
- package/dist/widgets/image.d.mts +37 -0
- package/dist/widgets/image.d.mts.map +1 -0
- package/dist/widgets/image.mjs +77 -0
- package/dist/widgets/image.mjs.map +1 -0
- package/dist/widgets/input.d.mts +2 -0
- package/dist/widgets/input.mjs +497 -0
- package/dist/widgets/input.mjs.map +1 -0
- package/dist/widgets/log.d.mts +2 -0
- package/dist/widgets/log.mjs +130 -0
- package/dist/widgets/log.mjs.map +1 -0
- package/dist/widgets/markdown.d.mts +45 -0
- package/dist/widgets/markdown.d.mts.map +1 -0
- package/dist/widgets/markdown.mjs +124 -0
- package/dist/widgets/markdown.mjs.map +1 -0
- package/dist/widgets/overlay.d.mts +2 -0
- package/dist/widgets/overlay.mjs +18 -0
- package/dist/widgets/overlay.mjs.map +1 -0
- package/dist/widgets/picker.d.mts +2 -0
- package/dist/widgets/picker.mjs +2 -0
- package/dist/widgets/progress.d.mts +48 -0
- package/dist/widgets/progress.d.mts.map +1 -0
- package/dist/widgets/progress.mjs +48 -0
- package/dist/widgets/progress.mjs.map +1 -0
- package/dist/widgets/select.d.mts +2 -0
- package/dist/widgets/select.mjs +195 -0
- package/dist/widgets/select.mjs.map +1 -0
- package/dist/widgets/show.d.mts +55 -0
- package/dist/widgets/show.d.mts.map +1 -0
- package/dist/widgets/show.mjs +85 -0
- package/dist/widgets/show.mjs.map +1 -0
- package/dist/widgets/spinner.d.mts +65 -0
- package/dist/widgets/spinner.d.mts.map +1 -0
- package/dist/widgets/spinner.mjs +147 -0
- package/dist/widgets/spinner.mjs.map +1 -0
- package/dist/widgets/text.d.mts +2 -0
- package/dist/widgets/text.mjs +32 -0
- package/dist/widgets/text.mjs.map +1 -0
- package/dist/widgets/tree.d.mts +2 -0
- package/dist/widgets/tree.mjs +73 -0
- package/dist/widgets/tree.mjs.map +1 -0
- package/dist/widgets/widget.d.mts +49 -0
- package/dist/widgets/widget.d.mts.map +1 -0
- package/dist/widgets/widget.mjs +40 -0
- package/dist/widgets/widget.mjs.map +1 -0
- package/dist/worker-DjHuQSbY.mjs +27 -0
- package/dist/worker-DjHuQSbY.mjs.map +1 -0
- package/package.json +117 -0
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
import { S as unwrap, f as effect, h as memo, s as createProgressive, x as untrack } from "./reactive-f7NpAi69.mjs";
|
|
2
|
+
import { widget } from "./widgets/widget.mjs";
|
|
3
|
+
import { Input } from "./widgets/input.mjs";
|
|
4
|
+
import { select } from "./widgets/select.mjs";
|
|
5
|
+
import { tree } from "./widgets/tree.mjs";
|
|
6
|
+
import { sliceAnsi, stripAnsi } from "@zaly/shared/ansi";
|
|
7
|
+
import { TopK } from "@zaly/shared/minheap";
|
|
8
|
+
//#region src/search/score.ts
|
|
9
|
+
const SCORE_MATCH = 16;
|
|
10
|
+
const SCORE_GAP_START = -3;
|
|
11
|
+
const SCORE_GAP_EXTENSION = -1;
|
|
12
|
+
const BONUS_BOUNDARY = SCORE_MATCH / 2;
|
|
13
|
+
const BONUS_NONWORD = SCORE_MATCH / 2;
|
|
14
|
+
const BONUS_CAMEL_123 = BONUS_BOUNDARY - 1;
|
|
15
|
+
const BONUS_CONSECUTIVE = 4;
|
|
16
|
+
const BONUS_FIRST_CHAR_MULTIPLIER = 2;
|
|
17
|
+
const BONUS_NO_PATH_SEP = BONUS_BOUNDARY - 2;
|
|
18
|
+
const CHAR_WHITE = 0;
|
|
19
|
+
const CHAR_NONWORD = 1;
|
|
20
|
+
const CHAR_DELIMITER = 2;
|
|
21
|
+
const CHAR_LOWER = 3;
|
|
22
|
+
const CHAR_UPPER = 4;
|
|
23
|
+
const CHAR_LETTER = 5;
|
|
24
|
+
const CHAR_NUMBER = 6;
|
|
25
|
+
const PATH_SEPS = /* @__PURE__ */ new Set(["/", "\\"]);
|
|
26
|
+
const CHAR_CLASS = Array.from({ length: 256 }, (_, b) => {
|
|
27
|
+
const ch = String.fromCharCode(b);
|
|
28
|
+
if (/\s/.test(ch)) return CHAR_WHITE;
|
|
29
|
+
if (String.raw`/\,:;|`.includes(ch)) return CHAR_DELIMITER;
|
|
30
|
+
if (b >= 48 && b <= 57) return CHAR_NUMBER;
|
|
31
|
+
if (b >= 65 && b <= 90) return CHAR_UPPER;
|
|
32
|
+
if (b >= 97 && b <= 122) return CHAR_LOWER;
|
|
33
|
+
return CHAR_NONWORD;
|
|
34
|
+
});
|
|
35
|
+
function charClass(str, pos) {
|
|
36
|
+
const code = str.charCodeAt(pos);
|
|
37
|
+
return code <= 255 ? CHAR_CLASS[code] ?? CHAR_NONWORD : CHAR_LETTER;
|
|
38
|
+
}
|
|
39
|
+
var Score = class {
|
|
40
|
+
#opts;
|
|
41
|
+
#bonusMatrix = [];
|
|
42
|
+
#bonusBoundaryWhite;
|
|
43
|
+
#bonusBoundaryDelimiter;
|
|
44
|
+
#consecutive = 0;
|
|
45
|
+
#firstBonus = 0;
|
|
46
|
+
#prev;
|
|
47
|
+
#prevClass = CHAR_WHITE;
|
|
48
|
+
#score = 0;
|
|
49
|
+
#str = "";
|
|
50
|
+
isFile = false;
|
|
51
|
+
constructor(opts = {}) {
|
|
52
|
+
this.#opts = opts;
|
|
53
|
+
this.#bonusBoundaryWhite = opts.historyBonus ? BONUS_BOUNDARY : 10;
|
|
54
|
+
this.#bonusBoundaryDelimiter = opts.historyBonus ? BONUS_BOUNDARY : 9;
|
|
55
|
+
for (let prev = 0; prev <= 6; prev++) {
|
|
56
|
+
this.#bonusMatrix[prev] = [];
|
|
57
|
+
for (let curr = 0; curr <= 6; curr++) this.#bonusMatrix[prev][curr] = this.#computeBonus(prev, curr);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
get score() {
|
|
61
|
+
return this.#score;
|
|
62
|
+
}
|
|
63
|
+
isLeftBoundary(str, pos) {
|
|
64
|
+
return pos === 0 || charClass(str, pos - 1) < CHAR_LOWER;
|
|
65
|
+
}
|
|
66
|
+
isRightBoundary(str, pos) {
|
|
67
|
+
return pos === str.length - 1 || charClass(str, pos + 1) < CHAR_LOWER;
|
|
68
|
+
}
|
|
69
|
+
init(str, first) {
|
|
70
|
+
this.#str = str;
|
|
71
|
+
this.#score = 0;
|
|
72
|
+
this.#consecutive = 0;
|
|
73
|
+
this.#prevClass = first > 0 ? charClass(str, first - 1) : CHAR_WHITE;
|
|
74
|
+
this.#prev = void 0;
|
|
75
|
+
this.#firstBonus = 0;
|
|
76
|
+
if (this.isFile && this.#opts.filenameBonus && !hasPathSepAfter(str, first)) this.#score += BONUS_NO_PATH_SEP;
|
|
77
|
+
this.update(first);
|
|
78
|
+
}
|
|
79
|
+
update(pos) {
|
|
80
|
+
const cls = charClass(this.#str, pos);
|
|
81
|
+
const gap = this.#prev === void 0 ? 0 : pos - this.#prev - 1;
|
|
82
|
+
let bonus = 0;
|
|
83
|
+
if (gap > 0) {
|
|
84
|
+
this.#prevClass = charClass(this.#str, pos - 1);
|
|
85
|
+
bonus = this.#bonusMatrix[this.#prevClass]?.[cls] ?? 0;
|
|
86
|
+
this.#score += SCORE_GAP_START + (gap - 1) * SCORE_GAP_EXTENSION;
|
|
87
|
+
this.#consecutive = 0;
|
|
88
|
+
this.#firstBonus = 0;
|
|
89
|
+
} else {
|
|
90
|
+
bonus = this.#bonusMatrix[this.#prevClass]?.[cls] ?? 0;
|
|
91
|
+
if (this.#consecutive === 0) this.#firstBonus = bonus;
|
|
92
|
+
else {
|
|
93
|
+
if (bonus >= BONUS_BOUNDARY && bonus > this.#firstBonus) this.#firstBonus = bonus;
|
|
94
|
+
bonus = Math.max(bonus, this.#firstBonus, BONUS_CONSECUTIVE);
|
|
95
|
+
}
|
|
96
|
+
this.#consecutive++;
|
|
97
|
+
}
|
|
98
|
+
if (this.#prev === void 0) bonus *= BONUS_FIRST_CHAR_MULTIPLIER;
|
|
99
|
+
this.#score += SCORE_MATCH + bonus;
|
|
100
|
+
this.#prevClass = cls;
|
|
101
|
+
this.#prev = pos;
|
|
102
|
+
}
|
|
103
|
+
get(str, from, to) {
|
|
104
|
+
this.init(str, from);
|
|
105
|
+
for (let i = from + 1; i <= to; i++) this.update(i);
|
|
106
|
+
return this.#score;
|
|
107
|
+
}
|
|
108
|
+
#computeBonus(prev, curr) {
|
|
109
|
+
if (curr > CHAR_NONWORD) {
|
|
110
|
+
if (prev === CHAR_WHITE) return this.#bonusBoundaryWhite;
|
|
111
|
+
if (prev === CHAR_DELIMITER) return this.#bonusBoundaryDelimiter;
|
|
112
|
+
if (prev === CHAR_NONWORD) return BONUS_BOUNDARY;
|
|
113
|
+
}
|
|
114
|
+
if (prev === CHAR_LOWER && curr === CHAR_UPPER || prev !== CHAR_NUMBER && curr === CHAR_NUMBER) return BONUS_CAMEL_123;
|
|
115
|
+
if (curr === CHAR_NONWORD || curr === CHAR_DELIMITER) return BONUS_NONWORD;
|
|
116
|
+
if (curr === CHAR_WHITE) return 10;
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
function hasPathSepAfter(str, first) {
|
|
121
|
+
for (let i = first + 1; i < str.length; i++) if (PATH_SEPS.has(str[i])) return true;
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/search/matcher.ts
|
|
126
|
+
var Matcher = class {
|
|
127
|
+
opts;
|
|
128
|
+
score;
|
|
129
|
+
mods = [];
|
|
130
|
+
one;
|
|
131
|
+
pattern = "";
|
|
132
|
+
tick = 0;
|
|
133
|
+
constructor(opts = {}) {
|
|
134
|
+
this.opts = {
|
|
135
|
+
fuzzy: true,
|
|
136
|
+
ignorecase: true,
|
|
137
|
+
smartcase: true,
|
|
138
|
+
...opts
|
|
139
|
+
};
|
|
140
|
+
this.score = new Score(opts);
|
|
141
|
+
}
|
|
142
|
+
empty() {
|
|
143
|
+
return this.mods.length === 0;
|
|
144
|
+
}
|
|
145
|
+
init(pattern) {
|
|
146
|
+
pattern = pattern.trim();
|
|
147
|
+
if (pattern === this.pattern) return false;
|
|
148
|
+
this.pattern = pattern;
|
|
149
|
+
this.tick++;
|
|
150
|
+
this.mods = [];
|
|
151
|
+
this.one = void 0;
|
|
152
|
+
if (pattern === "") return true;
|
|
153
|
+
if (this.opts.regex) this.mods = [[this.#prepare(pattern)]];
|
|
154
|
+
else {
|
|
155
|
+
let isOr = false;
|
|
156
|
+
for (const part of pattern.split(/ +/)) {
|
|
157
|
+
if (part === "|") {
|
|
158
|
+
isOr = true;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const mod = this.#prepare(part);
|
|
162
|
+
if (mod.pattern === "") continue;
|
|
163
|
+
if (isOr && this.mods.length > 0) this.mods.at(-1)?.push(mod);
|
|
164
|
+
else this.mods.push([mod]);
|
|
165
|
+
isOr = false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
this.mods = this.mods.map((ors) => ors.toSorted((a, b) => a.entropy - b.entropy));
|
|
169
|
+
this.mods = this.mods.toSorted((a, b) => (b[0]?.entropy ?? 0) - (a[0]?.entropy ?? 0));
|
|
170
|
+
if (this.mods.length === 1 && this.mods[0]?.length === 1) this.one = this.mods[0][0];
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
match(input) {
|
|
174
|
+
if (this.empty()) return 1e3;
|
|
175
|
+
const item = typeof input === "string" ? { text: input } : input;
|
|
176
|
+
if (this.one) return this.#match(item, this.one)?.score ?? 0;
|
|
177
|
+
let score = 0;
|
|
178
|
+
for (const any of this.mods) {
|
|
179
|
+
let match;
|
|
180
|
+
for (const mod of any) {
|
|
181
|
+
match = this.#match(item, mod);
|
|
182
|
+
if (match) break;
|
|
183
|
+
}
|
|
184
|
+
if (!match) return 0;
|
|
185
|
+
score += match.score;
|
|
186
|
+
}
|
|
187
|
+
return score;
|
|
188
|
+
}
|
|
189
|
+
update(item) {
|
|
190
|
+
const score = this.match(item);
|
|
191
|
+
const it = item;
|
|
192
|
+
it.score = score;
|
|
193
|
+
return it;
|
|
194
|
+
}
|
|
195
|
+
positions(input) {
|
|
196
|
+
if (this.empty()) return [];
|
|
197
|
+
const item = typeof input === "string" ? { text: input } : input;
|
|
198
|
+
const ret = [];
|
|
199
|
+
const all = this.mods.flat();
|
|
200
|
+
for (const mod of all) {
|
|
201
|
+
const match = this.#match(item, mod);
|
|
202
|
+
if (!match) continue;
|
|
203
|
+
if (mod.fuzzy) ret.push(...this.#fuzzyPositions(match.search, mod.chars, match.from));
|
|
204
|
+
else for (let i = match.from; i <= match.to; i++) ret.push(i);
|
|
205
|
+
}
|
|
206
|
+
return [...new Set(ret)].toSorted((a, b) => a - b);
|
|
207
|
+
}
|
|
208
|
+
fields() {
|
|
209
|
+
const ret = /* @__PURE__ */ new Set();
|
|
210
|
+
for (const mod of this.mods.flat()) ret.add(mod.field ?? "text");
|
|
211
|
+
return [...ret];
|
|
212
|
+
}
|
|
213
|
+
#prepare(pattern) {
|
|
214
|
+
const mod = {
|
|
215
|
+
chars: [],
|
|
216
|
+
entropy: 0,
|
|
217
|
+
pattern
|
|
218
|
+
};
|
|
219
|
+
if (this.opts.regex) {
|
|
220
|
+
mod.regex = true;
|
|
221
|
+
const isLower = mod.pattern.toLowerCase() === mod.pattern;
|
|
222
|
+
mod.ignorecase = this.opts.smartcase ? isLower : this.opts.ignorecase;
|
|
223
|
+
try {
|
|
224
|
+
mod.re = new RegExp(mod.pattern, mod.ignorecase ? "i" : void 0);
|
|
225
|
+
} catch {
|
|
226
|
+
mod.re = void 0;
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
const file = parseFilePattern(pattern);
|
|
230
|
+
if (file) {
|
|
231
|
+
mod.field = "file";
|
|
232
|
+
mod.pattern = `${file}$`;
|
|
233
|
+
}
|
|
234
|
+
const field = mod.pattern.match(/^([\w_][\w_]+):(.*)$/);
|
|
235
|
+
if (field) {
|
|
236
|
+
mod.field = field[1];
|
|
237
|
+
mod.pattern = field[2];
|
|
238
|
+
}
|
|
239
|
+
mod.ignorecase = this.opts.ignorecase;
|
|
240
|
+
const isLower = mod.pattern.toLowerCase() === mod.pattern;
|
|
241
|
+
if (this.opts.smartcase) mod.ignorecase = isLower;
|
|
242
|
+
mod.fuzzy = this.opts.fuzzy;
|
|
243
|
+
if (!mod.fuzzy) mod.entropy += 10;
|
|
244
|
+
if (mod.pattern.startsWith("!")) {
|
|
245
|
+
mod.fuzzy = false;
|
|
246
|
+
mod.inverse = true;
|
|
247
|
+
mod.pattern = mod.pattern.slice(1);
|
|
248
|
+
mod.entropy -= 1;
|
|
249
|
+
}
|
|
250
|
+
if (mod.pattern.startsWith("'")) {
|
|
251
|
+
mod.fuzzy = false;
|
|
252
|
+
mod.pattern = mod.pattern.slice(1);
|
|
253
|
+
mod.entropy += 10;
|
|
254
|
+
if (mod.pattern.endsWith("'")) {
|
|
255
|
+
mod.word = true;
|
|
256
|
+
mod.pattern = mod.pattern.slice(0, -1);
|
|
257
|
+
mod.entropy += 10;
|
|
258
|
+
}
|
|
259
|
+
} else if (mod.pattern.startsWith("^")) {
|
|
260
|
+
mod.fuzzy = false;
|
|
261
|
+
mod.exactPrefix = true;
|
|
262
|
+
mod.pattern = mod.pattern.slice(1);
|
|
263
|
+
mod.entropy += 20;
|
|
264
|
+
}
|
|
265
|
+
if (mod.pattern.endsWith("$")) {
|
|
266
|
+
mod.fuzzy = false;
|
|
267
|
+
mod.exactSuffix = true;
|
|
268
|
+
mod.pattern = mod.pattern.slice(0, -1);
|
|
269
|
+
mod.entropy += 20;
|
|
270
|
+
}
|
|
271
|
+
const rareChars = (mod.pattern.match(/[^\w\s]/g) ?? []).length;
|
|
272
|
+
mod.entropy += Math.min(mod.pattern.length, 20) + rareChars * 2;
|
|
273
|
+
if (!mod.ignorecase && !isLower) mod.entropy *= 2;
|
|
274
|
+
if (mod.ignorecase) mod.pattern = mod.pattern.toLowerCase();
|
|
275
|
+
}
|
|
276
|
+
mod.chars = [...mod.pattern];
|
|
277
|
+
return mod;
|
|
278
|
+
}
|
|
279
|
+
#match(item, mod) {
|
|
280
|
+
this.score.isFile = itemField(item, "file") !== void 0;
|
|
281
|
+
let str = item.text;
|
|
282
|
+
if (mod.field) {
|
|
283
|
+
const value = itemField(item, mod.field);
|
|
284
|
+
if (value === void 0) return mod.inverse ? {
|
|
285
|
+
from: 0,
|
|
286
|
+
score: 1e3,
|
|
287
|
+
search: str,
|
|
288
|
+
str,
|
|
289
|
+
to: 0
|
|
290
|
+
} : void 0;
|
|
291
|
+
str = value;
|
|
292
|
+
}
|
|
293
|
+
const original = str;
|
|
294
|
+
if (mod.ignorecase) str = str.toLowerCase();
|
|
295
|
+
if (mod.regex) return this.#regex(original, mod);
|
|
296
|
+
if (mod.fuzzy) return this.#fuzzy(str, original, mod.chars);
|
|
297
|
+
let from = -1;
|
|
298
|
+
let to = -1;
|
|
299
|
+
if (mod.exactPrefix) {
|
|
300
|
+
if (str.startsWith(mod.pattern)) {
|
|
301
|
+
from = 0;
|
|
302
|
+
to = mod.pattern.length - 1;
|
|
303
|
+
}
|
|
304
|
+
} else if (mod.exactSuffix) {
|
|
305
|
+
if (str.endsWith(mod.pattern)) {
|
|
306
|
+
from = str.length - mod.pattern.length;
|
|
307
|
+
to = str.length - 1;
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
from = str.indexOf(mod.pattern);
|
|
311
|
+
to = from + mod.pattern.length - 1;
|
|
312
|
+
while (mod.word && from >= 0) {
|
|
313
|
+
if (this.score.isLeftBoundary(str, from) && this.score.isRightBoundary(str, to)) break;
|
|
314
|
+
from = str.indexOf(mod.pattern, to + 1);
|
|
315
|
+
to = from + mod.pattern.length - 1;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (mod.inverse) return from === -1 ? {
|
|
319
|
+
from: 0,
|
|
320
|
+
score: 1e3,
|
|
321
|
+
search: str,
|
|
322
|
+
str: original,
|
|
323
|
+
to: 0
|
|
324
|
+
} : void 0;
|
|
325
|
+
if (from >= 0) return {
|
|
326
|
+
from,
|
|
327
|
+
score: this.score.get(original, from, to),
|
|
328
|
+
search: str,
|
|
329
|
+
str: original,
|
|
330
|
+
to
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
#regex(str, mod) {
|
|
334
|
+
const match = mod.re?.exec(str);
|
|
335
|
+
if (!match?.[0]) return void 0;
|
|
336
|
+
const from = match.index;
|
|
337
|
+
const to = from + match[0].length - 1;
|
|
338
|
+
return {
|
|
339
|
+
from,
|
|
340
|
+
score: this.score.get(str, from, to),
|
|
341
|
+
search: str,
|
|
342
|
+
str,
|
|
343
|
+
to
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
#fuzzyFind(str, original, pattern, init = 0) {
|
|
347
|
+
const from = str.indexOf(pattern[0] ?? "", init);
|
|
348
|
+
if (from === -1) return void 0;
|
|
349
|
+
this.score.init(original, from);
|
|
350
|
+
let last = from;
|
|
351
|
+
for (let i = 1; i < pattern.length; i++) {
|
|
352
|
+
last = str.indexOf(pattern[i], last + 1);
|
|
353
|
+
if (last === -1) return void 0;
|
|
354
|
+
this.score.update(last);
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
from,
|
|
358
|
+
score: this.score.score,
|
|
359
|
+
to: last
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
#fuzzy(str, original, pattern) {
|
|
363
|
+
let best = this.#fuzzyFind(str, original, pattern);
|
|
364
|
+
if (!best) return void 0;
|
|
365
|
+
for (let next = this.#fuzzyFind(str, original, pattern, best.from + 1); next; next = this.#fuzzyFind(str, original, pattern, next.from + 1)) if (next.score > best.score) best = next;
|
|
366
|
+
return {
|
|
367
|
+
from: best.from,
|
|
368
|
+
score: best.score,
|
|
369
|
+
search: str,
|
|
370
|
+
str: original,
|
|
371
|
+
to: best.to
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
#fuzzyPositions(str, pattern, from) {
|
|
375
|
+
const ret = [from];
|
|
376
|
+
let last = from;
|
|
377
|
+
for (let i = 1; i < pattern.length; i++) {
|
|
378
|
+
last = str.indexOf(pattern[i], last + 1);
|
|
379
|
+
if (last === -1) break;
|
|
380
|
+
ret.push(last);
|
|
381
|
+
}
|
|
382
|
+
return ret;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
function itemField(item, field) {
|
|
386
|
+
const value = item[field];
|
|
387
|
+
if (value === void 0 || value === null) return void 0;
|
|
388
|
+
if (typeof value === "string") return value;
|
|
389
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
390
|
+
if (Array.isArray(value)) return value.join(" ");
|
|
391
|
+
}
|
|
392
|
+
function parseFilePattern(pattern) {
|
|
393
|
+
return pattern.match(/^(.*[/\\].*):\d*:?:?\d*$/)?.[1] ?? pattern.match(/^(.+\.[a-z_]+):\d*:?:?\d*$/)?.[1];
|
|
394
|
+
}
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/search/sort.ts
|
|
397
|
+
function parse(field) {
|
|
398
|
+
const len = field.startsWith("#");
|
|
399
|
+
return {
|
|
400
|
+
desc: field.endsWith(":desc"),
|
|
401
|
+
len,
|
|
402
|
+
name: field.replace(/^#/, "").replace(/:desc$/, "")
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function sorter(fields = [
|
|
406
|
+
"score:desc",
|
|
407
|
+
"#text",
|
|
408
|
+
"idx"
|
|
409
|
+
]) {
|
|
410
|
+
const parsed = fields.map(parse);
|
|
411
|
+
return (a, b) => {
|
|
412
|
+
for (const field of parsed) {
|
|
413
|
+
let av = a[field.name];
|
|
414
|
+
let bv = b[field.name];
|
|
415
|
+
if (av === void 0 || bv === void 0) continue;
|
|
416
|
+
if (field.len) {
|
|
417
|
+
av = typeof av === "string" || Array.isArray(av) ? av.length : 0;
|
|
418
|
+
bv = typeof bv === "string" || Array.isArray(bv) ? bv.length : 0;
|
|
419
|
+
}
|
|
420
|
+
if (av === bv) continue;
|
|
421
|
+
if (typeof av === "boolean" && typeof bv === "boolean") {
|
|
422
|
+
av = av ? 0 : 1;
|
|
423
|
+
bv = bv ? 0 : 1;
|
|
424
|
+
}
|
|
425
|
+
return compare(av, bv) * (field.desc ? -1 : 1);
|
|
426
|
+
}
|
|
427
|
+
return 0;
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function compare(a, b) {
|
|
431
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
432
|
+
if (typeof a === "string" && typeof b === "string") return a.localeCompare(b);
|
|
433
|
+
return String(a).localeCompare(String(b));
|
|
434
|
+
}
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/search/search.ts
|
|
437
|
+
const BONUS_FRECENCY = 8;
|
|
438
|
+
const SEARCH_LIMIT = 1e3;
|
|
439
|
+
const aborted = Symbol("search.aborted");
|
|
440
|
+
var Searcher = class {
|
|
441
|
+
#opts;
|
|
442
|
+
#sorter;
|
|
443
|
+
#matcher;
|
|
444
|
+
#ac;
|
|
445
|
+
constructor(opts) {
|
|
446
|
+
this.#opts = opts;
|
|
447
|
+
const sortOpts = opts.sort === true ? void 0 : opts.sort;
|
|
448
|
+
this.#sorter = sortOpts === false ? void 0 : sorter(sortOpts);
|
|
449
|
+
this.#matcher = new Matcher(opts);
|
|
450
|
+
}
|
|
451
|
+
/** Create a match function bound to the current pattern. If the
|
|
452
|
+
* matcher has been updated since the search started, match will
|
|
453
|
+
* return `0` to indicate no match, to prevent stale results */
|
|
454
|
+
createMatch(opts) {
|
|
455
|
+
const m = this.#matcher;
|
|
456
|
+
return (s) => opts.signal.aborted ? 0 : m.match(s);
|
|
457
|
+
}
|
|
458
|
+
async search(items, query = "", opts = {}) {
|
|
459
|
+
try {
|
|
460
|
+
return await this.#search(items, query, opts);
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (error === aborted) return [];
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
get limit() {
|
|
467
|
+
return this.#opts.limit ?? (this.#sorter ? SEARCH_LIMIT : Infinity);
|
|
468
|
+
}
|
|
469
|
+
async #search(items, query = "", opts = {}) {
|
|
470
|
+
this.#ac?.abort(aborted);
|
|
471
|
+
const ac = this.#ac = new AbortController();
|
|
472
|
+
const m = this.#matcher;
|
|
473
|
+
m.init(query);
|
|
474
|
+
const f = this.#opts.frecency?.();
|
|
475
|
+
const s = this.#sorter;
|
|
476
|
+
const limit = this.limit;
|
|
477
|
+
const ret = [];
|
|
478
|
+
let match = (item) => this.#matcher.update(item);
|
|
479
|
+
const topk = s && (this.#opts.sortEmpty || !m.empty()) ? new TopK(limit, (a, b) => -s(a, b)) : void 0;
|
|
480
|
+
if (typeof items === "function") {
|
|
481
|
+
match = (item) => item;
|
|
482
|
+
items = await items(query, this.createMatch({ signal: ac.signal }));
|
|
483
|
+
ac.signal.throwIfAborted();
|
|
484
|
+
}
|
|
485
|
+
if (items.length === 0) return [];
|
|
486
|
+
const results = () => topk?.sorted() ?? ret;
|
|
487
|
+
const todo = [
|
|
488
|
+
[],
|
|
489
|
+
[],
|
|
490
|
+
[]
|
|
491
|
+
];
|
|
492
|
+
let idx = 0;
|
|
493
|
+
if (!topk) todo[0] = items;
|
|
494
|
+
else for (const item of items) {
|
|
495
|
+
item.idx = idx++;
|
|
496
|
+
const it = item;
|
|
497
|
+
if (it.topk) todo[0].push(item);
|
|
498
|
+
else if (it.score) todo[1].push(item);
|
|
499
|
+
else todo[2].push(item);
|
|
500
|
+
}
|
|
501
|
+
const doFilter = !m.empty() && (this.#opts.filter ?? true);
|
|
502
|
+
let lastYield = performance.now();
|
|
503
|
+
let dirty = false;
|
|
504
|
+
let count = 0;
|
|
505
|
+
loop: for (const todos of todo) for (const item of todos) {
|
|
506
|
+
ac.signal.throwIfAborted();
|
|
507
|
+
if (count++ % 256 === 0) {
|
|
508
|
+
const now = performance.now();
|
|
509
|
+
if (now - lastYield >= (this.#opts.throttle ?? 16)) {
|
|
510
|
+
lastYield = now;
|
|
511
|
+
await new Promise((r) => setImmediate(r));
|
|
512
|
+
if (opts.progress && dirty) {
|
|
513
|
+
dirty = false;
|
|
514
|
+
await opts.progress(results());
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const si = match(item);
|
|
519
|
+
if (si.file && si.score && f) {
|
|
520
|
+
const frecency = f(si.file);
|
|
521
|
+
si.score += (1 - 1 / (1 + frecency)) * BONUS_FRECENCY;
|
|
522
|
+
}
|
|
523
|
+
if (doFilter && si.score === 0) continue;
|
|
524
|
+
if (topk) {
|
|
525
|
+
si.topk = false;
|
|
526
|
+
const ta = topk.add(si);
|
|
527
|
+
if (ta.added) {
|
|
528
|
+
si.topk = true;
|
|
529
|
+
dirty = true;
|
|
530
|
+
if (ta.evicted) ta.evicted.topk = false;
|
|
531
|
+
}
|
|
532
|
+
} else {
|
|
533
|
+
ret.push(si);
|
|
534
|
+
dirty = true;
|
|
535
|
+
if (ret.length >= limit) break loop;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return results();
|
|
539
|
+
}
|
|
540
|
+
positions(s) {
|
|
541
|
+
return this.#matcher.positions(s);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/widgets/picker.ts
|
|
546
|
+
function isTree(props) {
|
|
547
|
+
return "tree" in props;
|
|
548
|
+
}
|
|
549
|
+
const picker = widget((props) => {
|
|
550
|
+
const pattern = memo(() => {
|
|
551
|
+
if (props.pattern !== void 0) return unwrap(props.pattern);
|
|
552
|
+
return (props.input instanceof Input ? props.input : props.input?.())?.state.value ?? "";
|
|
553
|
+
});
|
|
554
|
+
let node;
|
|
555
|
+
let items;
|
|
556
|
+
if (isTree(props)) {
|
|
557
|
+
props = {
|
|
558
|
+
filter: false,
|
|
559
|
+
fuzzy: false,
|
|
560
|
+
sort: false,
|
|
561
|
+
...props
|
|
562
|
+
};
|
|
563
|
+
const t = tree(props);
|
|
564
|
+
node = t;
|
|
565
|
+
items = t.items;
|
|
566
|
+
} else {
|
|
567
|
+
node = select({
|
|
568
|
+
...props,
|
|
569
|
+
items: []
|
|
570
|
+
});
|
|
571
|
+
items = props.items;
|
|
572
|
+
}
|
|
573
|
+
const searcher = new Searcher(props);
|
|
574
|
+
node.extendRenderer((prev) => (item, ctx) => {
|
|
575
|
+
let row = prev(item, ctx);
|
|
576
|
+
if (!item.score) return row;
|
|
577
|
+
const stripped = stripAnsi(row);
|
|
578
|
+
const positions = searcher.positions(stripped).toSorted((a, b) => b - a);
|
|
579
|
+
while (positions.length > 0) {
|
|
580
|
+
const from = positions.pop();
|
|
581
|
+
let to = from;
|
|
582
|
+
while (positions.length && positions.at(-1) === to + 1) to = positions.pop();
|
|
583
|
+
row = sliceAnsi(row, 0, from) + ctx.style.accent(stripped.slice(from, to + 1)) + sliceAnsi(row, to + 1);
|
|
584
|
+
}
|
|
585
|
+
return row;
|
|
586
|
+
});
|
|
587
|
+
const results = createProgressive(async ({ set }) => await searcher.search(unwrap(items), pattern(), { progress: (r) => set(r) }), { initialValue: [] });
|
|
588
|
+
node.state.items = results;
|
|
589
|
+
if (props.input) node.bind(props.input);
|
|
590
|
+
const matches = () => unwrap(node.state.items).map((item, i) => [item, i]).filter(([item]) => item.score).map(([, i]) => i);
|
|
591
|
+
effect(() => {
|
|
592
|
+
const m = matches();
|
|
593
|
+
node.state.more = m.length >= searcher.limit;
|
|
594
|
+
if (m.length === 0 || pattern() === "") return;
|
|
595
|
+
untrack(() => {
|
|
596
|
+
if (props.filter === false) {
|
|
597
|
+
const active = node.active;
|
|
598
|
+
node.active = m.find((i) => i >= active) ?? m[0];
|
|
599
|
+
} else node.active = 0;
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
return node.withActions({
|
|
603
|
+
...props.actions,
|
|
604
|
+
"picker.next": () => {
|
|
605
|
+
const active = node.active;
|
|
606
|
+
const m = matches();
|
|
607
|
+
if (m.length === 0) return node.action("select.next");
|
|
608
|
+
node.active = node.state.reverse ? m.toReversed().find((i) => i < active) ?? m.at(-1) : m.find((i) => i > active) ?? m[0];
|
|
609
|
+
},
|
|
610
|
+
"picker.prev": () => {
|
|
611
|
+
const active = node.active;
|
|
612
|
+
const m = matches();
|
|
613
|
+
if (m.length === 0) return node.action("select.prev");
|
|
614
|
+
node.active = node.state.reverse ? m.find((i) => i > active) ?? m[0] : m.toReversed().find((i) => i < active) ?? m.at(-1);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
//#endregion
|
|
619
|
+
export { picker as t };
|
|
620
|
+
|
|
621
|
+
//# sourceMappingURL=picker-ndVVt6qF.mjs.map
|