Official
palette
Built-in command palette (⌘⇧P) and chat search (⌘P).
The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/palette@0.2.0
Permissions in 0.2.0
Files
model.test.ts5.4 KB
// Preserve the palette matching, ranking, and navigation assertions.
// These tests import the pure model directly, using the same cases as the JavaScript suite.
import assert from "node:assert/strict";
import test from "node:test";
import { RECENT_LIMIT, commandLabel, fuzzy, modeOf, move, rankChats, rankCommands, remember, window } from "./model.ts";
const command = (
id: string,
title: string,
extra: { category?: string; enabled?: boolean; registered?: boolean } = {},
) => ({ id, title, category: extra.category, enabled: true, registered: true, ...extra });
test("fuzzy matches every character in order, whatever the case", () => {
assert.ok(fuzzy("nc", "New Chat"));
assert.ok(fuzzy("NEWCH", "new chat"));
assert.equal(fuzzy("cn", "New Chat"), null, "order counts");
assert.equal(fuzzy("xyz", "New Chat"), null);
assert.deepEqual(fuzzy("", "anything"), { score: 0, positions: [] });
// A word start that leads nowhere does not hide a match.
assert.ok(fuzzy("ta", "xtay t"));
// Letters strewn through the text are no match.
assert.equal(fuzzy("git", "Divergence: Quit Divergence"), null);
});
test("word starts, runs and prefixes score higher", () => {
const score = (query: string, text: string) => {
const match = fuzzy(query, text);
assert.ok(match);
return match.score;
};
assert.ok(score("nc", "New Chat") > score("nc", "income"), "word starts beat letters inside words");
assert.ok(score("new", "New Chat") > score("new", "Renew Chat"), "a prefix beats a match inside");
assert.ok(score("chat", "Chat") > score("chat", "Chat with a very long title"), "the shorter text wins a tie");
const match = fuzzy("nc", "New Chat");
assert.ok(match);
assert.deepEqual(match.positions, [0, 4]);
});
test("commands: hidden when their when is false or their plugin does not run", () => {
const commands = [
command("a.one", "Open", { category: "Alpha" }),
command("b.two", "Open Later", { enabled: false }),
command("c.three", "Open Other", { registered: false }),
];
assert.deepEqual(
rankCommands(commands, "open").map((r) => r.command.id),
["a.one"],
);
assert.deepEqual(
rankCommands(commands, "").map((r) => r.command.id),
["a.one"],
);
});
test("commands: fuzzy on the title and the category, recently run first", () => {
const commands = [
command("git.toggle", "Show Source Control", { category: "Git" }),
command("sidebar.newChat", "New Chat", { category: "Sidebar" }),
command("chat.switchNext", "Switch to the Previous Chat", { category: "Chat" }),
command("kernel.quit", "Quit Divergence", { category: "Divergence" }),
];
assert.deepEqual(
rankCommands(commands, "git").map((r) => r.command.id),
["git.toggle"],
"the category is searched",
);
assert.equal(rankCommands(commands, "new chat")[0].command.id, "sidebar.newChat");
// With no query: recent first, then by label.
const listed = rankCommands(commands, "", ["kernel.quit", "chat.switchNext"]).map((r) => r.command.id);
assert.deepEqual(listed, ["kernel.quit", "chat.switchNext", "git.toggle", "sidebar.newChat"]);
// A recent command wins a close match.
const ranked = rankCommands([command("a.x", "Toggle A"), command("b.x", "Toggle B")], "toggle", ["b.x"]);
assert.equal(ranked[0].command.id, "b.x");
assert.equal(commandLabel(commands[0]), "Git: Show Source Control");
assert.equal(commandLabel({ title: "Plain" }), "Plain");
});
test("chats: by title and workspace, newest first, archived ones never", () => {
const workspaces = { w1: { name: "nexus" }, w2: { name: "landing" } };
const chats = [
{ id: "a", title: "Fix the palette", workspaceId: "w1", updatedAt: "2026-09-20" },
{ id: "b", title: "Hero section", workspaceId: "w2", updatedAt: "2026-09-25" },
{ id: "c", title: "Old palette notes", workspaceId: "w1", updatedAt: "2026-09-01", archived: true },
];
assert.deepEqual(
rankChats(chats, workspaces, "").map((r) => r.chat.id),
["b", "a"],
);
assert.deepEqual(
rankChats(chats, workspaces, "palette").map((r) => r.chat.id),
["a"],
);
assert.deepEqual(
rankChats(chats, workspaces, "landing").map((r) => r.chat.id),
["b"],
"the workspace name is searched",
);
assert.deepEqual(rankChats(chats, workspaces, "zzz"), []);
});
test("the chat search switches mode by prefix", () => {
const providers = [
{ plugin: "files", id: "files", prefix: "@" },
{ plugin: "x", id: "long", prefix: "@@" },
];
assert.deepEqual(modeOf("> new", providers), { mode: "commands", query: "new" });
const mode = modeOf("@@deep", providers);
assert.equal(mode.mode, "provider");
assert.ok(mode.mode === "provider");
assert.equal(mode.provider.id, "long", "the longest prefix wins");
assert.deepEqual(modeOf("@main.rs", providers), { mode: "provider", provider: providers[0], query: "main.rs" });
assert.deepEqual(modeOf("hello", providers), { mode: "chats", query: "hello" });
});
test("the selection wraps and stays in sight; recent commands are bounded", () => {
assert.equal(move(0, 3, -1), 2);
assert.equal(move(2, 3, 1), 0);
assert.equal(move(0, 0, 1), 0);
assert.deepEqual(window(5, 3, 12), { from: 0, to: 5 });
assert.deepEqual(window(40, 30, 12), { from: 24, to: 36 });
assert.deepEqual(window(40, 39, 12), { from: 28, to: 40 });
let recent: string[] = [];
for (let i = 0; i < RECENT_LIMIT + 5; i++) recent = remember(recent, `c${i}`);
assert.equal(recent.length, RECENT_LIMIT);
assert.equal(remember(recent, recent[3])[0], recent[3]);
});Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 11.2 KB | 3 permissions | Listed |
No comments yet.