jeopi-tui 16.2.24 → 16.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/types/autocomplete.d.ts +2 -0
- package/package.json +3 -3
- package/src/autocomplete.ts +64 -51
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.2.25] - 2026-07-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `priority?: number` to `SlashCommand` (ported from gajae-code): `CombinedAutocompleteProvider`'s slash-command ranking now breaks same-score ties by `priority` (higher first) instead of falling through only to registry order. Text-match quality (exact/prefix/contains/subsequence) still ranks first — `priority` only decides ties within the same score tier, most visibly the bare `/` menu where every command scores 1.
|
|
10
|
+
|
|
5
11
|
## [16.2.14] - 2026-07-02
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
@@ -18,6 +18,8 @@ export interface SlashCommand {
|
|
|
18
18
|
name: string;
|
|
19
19
|
aliases?: string[];
|
|
20
20
|
description?: string;
|
|
21
|
+
/** Ranking boost for the empty-prefix `/` menu and tied fuzzy matches; higher shows first. Commands without a priority default to 0. */
|
|
22
|
+
priority?: number;
|
|
21
23
|
argumentHint?: string;
|
|
22
24
|
/** Dynamic display-only description for slash-command autocomplete. Must be synchronous and side-effect free. */
|
|
23
25
|
getAutocompleteDescription?: () => string | undefined;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-tui",
|
|
4
|
-
"version": "16.2.
|
|
4
|
+
"version": "16.2.26",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://github.com/akillness/jeopi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"fmt": "biome format --write ."
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"jeopi-natives": "16.2.
|
|
41
|
-
"jeopi-utils": "16.2.
|
|
40
|
+
"jeopi-natives": "16.2.26",
|
|
41
|
+
"jeopi-utils": "16.2.26",
|
|
42
42
|
"lru-cache": "11.5.1",
|
|
43
43
|
"marked": "^18.0.5"
|
|
44
44
|
},
|
package/src/autocomplete.ts
CHANGED
|
@@ -181,6 +181,8 @@ export interface SlashCommand {
|
|
|
181
181
|
name: string;
|
|
182
182
|
aliases?: string[];
|
|
183
183
|
description?: string;
|
|
184
|
+
/** Ranking boost for the empty-prefix `/` menu and tied fuzzy matches; higher shows first. Commands without a priority default to 0. */
|
|
185
|
+
priority?: number;
|
|
184
186
|
argumentHint?: string;
|
|
185
187
|
/** Dynamic display-only description for slash-command autocomplete. Must be synchronous and side-effect free. */
|
|
186
188
|
getAutocompleteDescription?: () => string | undefined;
|
|
@@ -286,63 +288,74 @@ function scoreCommandTextMatch(lowerPrefix: string, lowerTarget: string): number
|
|
|
286
288
|
}
|
|
287
289
|
|
|
288
290
|
function buildSlashCommandCompletions(commands: CommandEntry[], lowerPrefix: string): AutocompleteItem[] {
|
|
289
|
-
return
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
score: primaryScore,
|
|
323
|
-
...(fullDesc && { description: fullDesc }),
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (lowerPrefix.length > 0) {
|
|
328
|
-
for (const alias of getCommandAliases(cmd)) {
|
|
329
|
-
if (alias === name) continue;
|
|
330
|
-
const aliasScore = scoreCommandTextMatch(lowerPrefix, alias.toLowerCase());
|
|
331
|
-
if (aliasScore === 0) continue;
|
|
291
|
+
return (
|
|
292
|
+
commands
|
|
293
|
+
.flatMap(cmd => {
|
|
294
|
+
const name = getCommandName(cmd);
|
|
295
|
+
if (!name) return [];
|
|
296
|
+
const hint = "argumentHint" in cmd && cmd.argumentHint ? cmd.argumentHint : undefined;
|
|
297
|
+
const staticDesc = getStaticCommandDescription(cmd);
|
|
298
|
+
let fullDescMemo: string | undefined;
|
|
299
|
+
let fullDescComputed = false;
|
|
300
|
+
// Resolve the (possibly live) display description lazily, only once a
|
|
301
|
+
// candidate actually matches — getAutocompleteDescription reads live
|
|
302
|
+
// session state and must not run for every command on each keystroke.
|
|
303
|
+
const resolveFullDesc = (): string | undefined => {
|
|
304
|
+
if (!fullDescComputed) {
|
|
305
|
+
const displayDesc = getAutocompleteCommandDescription(cmd);
|
|
306
|
+
fullDescMemo = hint ? (displayDesc ? `${hint} - ${displayDesc}` : hint) : displayDesc;
|
|
307
|
+
fullDescComputed = true;
|
|
308
|
+
}
|
|
309
|
+
return fullDescMemo;
|
|
310
|
+
};
|
|
311
|
+
const candidates: Array<AutocompleteItem & { score: number; priority: number }> = [];
|
|
312
|
+
const priority = "priority" in cmd && typeof cmd.priority === "number" ? cmd.priority : 0;
|
|
313
|
+
|
|
314
|
+
const isSkillCommand = name.startsWith("skill:");
|
|
315
|
+
const nameScore =
|
|
316
|
+
lowerPrefix.length === 0 && isSkillCommand
|
|
317
|
+
? 950
|
|
318
|
+
: scoreCommandTextMatch(lowerPrefix, name.toLowerCase());
|
|
319
|
+
const lowerDesc = staticDesc.toLowerCase();
|
|
320
|
+
const descScore =
|
|
321
|
+
lowerDesc && fuzzyMatch(lowerPrefix, lowerDesc) ? fuzzyScore(lowerPrefix, lowerDesc) * 0.5 : 0;
|
|
322
|
+
const primaryScore = Math.max(nameScore, descScore);
|
|
323
|
+
if (primaryScore > 0) {
|
|
332
324
|
const fullDesc = resolveFullDesc();
|
|
333
325
|
candidates.push({
|
|
334
|
-
value:
|
|
335
|
-
label:
|
|
336
|
-
score:
|
|
326
|
+
value: name,
|
|
327
|
+
label: "name" in cmd ? cmd.name : cmd.label,
|
|
328
|
+
score: primaryScore,
|
|
329
|
+
priority,
|
|
337
330
|
...(fullDesc && { description: fullDesc }),
|
|
338
331
|
});
|
|
339
332
|
}
|
|
340
|
-
}
|
|
341
333
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
334
|
+
if (lowerPrefix.length > 0) {
|
|
335
|
+
for (const alias of getCommandAliases(cmd)) {
|
|
336
|
+
if (alias === name) continue;
|
|
337
|
+
const aliasScore = scoreCommandTextMatch(lowerPrefix, alias.toLowerCase());
|
|
338
|
+
if (aliasScore === 0) continue;
|
|
339
|
+
const fullDesc = resolveFullDesc();
|
|
340
|
+
candidates.push({
|
|
341
|
+
value: alias,
|
|
342
|
+
label: alias,
|
|
343
|
+
score: aliasScore,
|
|
344
|
+
priority,
|
|
345
|
+
...(fullDesc && { description: fullDesc }),
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return candidates;
|
|
351
|
+
})
|
|
352
|
+
// Score ranks fuzzy-match quality first (exact/prefix/contains/subsequence);
|
|
353
|
+
// `priority` only breaks ties within the same score tier — most visibly the
|
|
354
|
+
// empty-prefix `/` menu, where every command scores 1 and priority alone
|
|
355
|
+
// decides display order (e.g. `/help`, `/new`, `/resume` surface first).
|
|
356
|
+
.sort((a, b) => b.score - a.score || b.priority - a.priority)
|
|
357
|
+
.map(({ score: _score, priority: _priority, ...rest }) => rest)
|
|
358
|
+
);
|
|
346
359
|
}
|
|
347
360
|
|
|
348
361
|
function hasPromptTextBeforeSlash(
|