pi-multimodal-proxy 1.6.0 → 1.10.1

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.
@@ -0,0 +1,137 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import {
4
+ applyRecallCompletion,
5
+ buildRecallItems,
6
+ collectRecallCandidates,
7
+ extractRecallToken,
8
+ parseRecallItemValue,
9
+ RECALL_AC_MAX_ITEMS,
10
+ RECALL_AC_VALUE_PREFIX,
11
+ type RecallCandidate,
12
+ } from "../internal.ts";
13
+
14
+ describe("extractRecallToken", () => {
15
+ it("matches a bare # at the start of the line", () => {
16
+ assert.deepEqual(extractRecallToken(["#"], 0, 1), { query: "", prefix: "#" });
17
+ });
18
+
19
+ it("matches a # token after whitespace with a partial query", () => {
20
+ assert.deepEqual(extractRecallToken(["zoom into #shot"], 0, 15), {
21
+ query: "shot",
22
+ prefix: "#shot",
23
+ });
24
+ });
25
+
26
+ it("only considers text before the cursor", () => {
27
+ assert.deepEqual(extractRecallToken(["#abc tail"], 0, 4), { query: "abc", prefix: "#abc" });
28
+ });
29
+
30
+ it("returns null mid-word (no token boundary)", () => {
31
+ assert.equal(extractRecallToken(["issue#42"], 0, 8), null);
32
+ });
33
+
34
+ it("returns null when the cursor is not in a # token", () => {
35
+ assert.equal(extractRecallToken(["plain text"], 0, 5), null);
36
+ assert.equal(extractRecallToken(["# spaced"], 0, 8), null);
37
+ });
38
+
39
+ it("uses the cursor line in multi-line input", () => {
40
+ assert.deepEqual(extractRecallToken(["first", "see #x"], 1, 6), { query: "x", prefix: "#x" });
41
+ });
42
+ });
43
+
44
+ describe("collectRecallCandidates", () => {
45
+ it("orders newest first and attaches in-memory filenames", () => {
46
+ const descriptions = new Map([
47
+ ["h1", "first image"],
48
+ ["h2", "second image"],
49
+ ]);
50
+ const meta = new Map([["h2", { width: 1, height: 1, filename: "b.png" }]]);
51
+ const out = collectRecallCandidates(descriptions, (h) => meta.get(h));
52
+ assert.deepEqual(out, [
53
+ { hash: "h2", description: "second image", filename: "b.png" },
54
+ { hash: "h1", description: "first image", filename: undefined },
55
+ ]);
56
+ });
57
+ });
58
+
59
+ describe("buildRecallItems", () => {
60
+ const candidates: RecallCandidate[] = [
61
+ { hash: "a".repeat(32), filename: "screenshot.png", description: "a login form" },
62
+ { hash: "b".repeat(32), description: "an error dialog with a stack trace" },
63
+ ];
64
+
65
+ it("lists all candidates for an empty query", () => {
66
+ const items = buildRecallItems(candidates, "");
67
+ assert.equal(items.length, 2);
68
+ assert.equal(items[0]!.value, `${RECALL_AC_VALUE_PREFIX}${"a".repeat(32)}`);
69
+ assert.equal(items[0]!.label, "screenshot.png");
70
+ assert.equal(items[1]!.label, `${"b".repeat(12)}…`);
71
+ });
72
+
73
+ it("fuzzy-matches filename and description", () => {
74
+ assert.equal(buildRecallItems(candidates, "shot")[0]!.label, "screenshot.png");
75
+ const byDesc = buildRecallItems(candidates, "stack trace");
76
+ assert.equal(byDesc.length, 1);
77
+ assert.ok(byDesc[0]!.value.endsWith("b".repeat(32)));
78
+ assert.equal(buildRecallItems(candidates, "zzz-no-match").length, 0);
79
+ });
80
+
81
+ it("matches the hash by prefix only, not by fuzzy subsequence", () => {
82
+ // "b" is a prefix of the second hash but no subsequence match on the
83
+ // first candidate's filename/description — hex letters must not match
84
+ // every image via the 32-hex hash.
85
+ const byPrefix = buildRecallItems(candidates, "bbbb");
86
+ assert.equal(byPrefix.length, 1);
87
+ assert.ok(byPrefix[0]!.value.endsWith("b".repeat(32)));
88
+ const q = buildRecallItems(candidates, "eeee");
89
+ assert.equal(q.length, 0, "hex query matching no prefix and no text must return nothing");
90
+ });
91
+
92
+ it("truncates dropdown descriptions to a single short line", () => {
93
+ const long = [{ hash: "c".repeat(32), description: `x${"y z".repeat(80)}` }];
94
+ const item = buildRecallItems(long, "")[0]!;
95
+ assert.ok(item.description!.length <= 61);
96
+ assert.ok(item.description!.endsWith("…"));
97
+ });
98
+
99
+ it("caps the item count", () => {
100
+ const many: RecallCandidate[] = Array.from({ length: RECALL_AC_MAX_ITEMS + 5 }, (_, i) => ({
101
+ hash: `${i}`.padStart(32, "0"),
102
+ description: `img ${i}`,
103
+ }));
104
+ assert.equal(buildRecallItems(many, "").length, RECALL_AC_MAX_ITEMS);
105
+ });
106
+ });
107
+
108
+ describe("parseRecallItemValue", () => {
109
+ it("round-trips recall values and rejects foreign ones", () => {
110
+ assert.equal(parseRecallItemValue(`${RECALL_AC_VALUE_PREFIX}deadbeef`), "deadbeef");
111
+ assert.equal(parseRecallItemValue("/some/file.png"), null);
112
+ assert.equal(parseRecallItemValue("deadbeef"), null);
113
+ });
114
+ });
115
+
116
+ describe("applyRecallCompletion", () => {
117
+ const hash = "f".repeat(32);
118
+
119
+ it("replaces the # token with the fence-style id and moves the cursor", () => {
120
+ const out = applyRecallCompletion(["zoom into #shot"], 0, 15, hash, "#shot");
121
+ assert.equal(out.lines[0], `zoom into image="${hash}" `);
122
+ assert.equal(out.cursorLine, 0);
123
+ assert.equal(out.cursorCol, out.lines[0]!.length);
124
+ });
125
+
126
+ it("preserves text after the cursor", () => {
127
+ const out = applyRecallCompletion(["#ab and crop it"], 0, 3, hash, "#ab");
128
+ assert.equal(out.lines[0], `image="${hash}" and crop it`);
129
+ assert.equal(out.cursorCol, `image="${hash}" `.length);
130
+ });
131
+
132
+ it("only touches the cursor line", () => {
133
+ const out = applyRecallCompletion(["keep", "#x"], 1, 2, hash, "#x");
134
+ assert.equal(out.lines[0], "keep");
135
+ assert.equal(out.lines[1], `image="${hash}" `);
136
+ });
137
+ });