Skip to content

Commit e03d358

Browse files
bfollingtonclaude
andauthored
Fix #728: Exclude trash items from @mentions and prioritize pinned charms (#887)
* Fix #728: Exclude trash items from @mentions and prioritize pinned charms 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Improve error handling and type safety in charm mentions functionality - Add warning logs for charms without IDs - Use proper type filtering to prevent null references - Improve robustness in Sets creation with proper type assertions - Fix select command handling in CommandCenter 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 07d1686 commit e03d358

File tree

2 files changed

+85
-11
lines changed

2 files changed

+85
-11
lines changed

jumble/src/components/CommandCenter.tsx

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { usePreferredLanguageModel } from "@/contexts/LanguageModelContext.tsx";
2222
import { TranscribeInput } from "./TranscribeCommand.tsx";
2323
import { useBackgroundTasks } from "@/contexts/BackgroundTaskContext.tsx";
2424
import { Composer, ComposerSubmitBar } from "@/components/Composer.tsx";
25-
import { charmId } from "@/utils/charms.ts";
25+
import { charmId, getMentionableCharms } from "@/utils/charms.ts";
2626
import { formatPromptWithMentions } from "@/utils/format.ts";
2727
import { NAME } from "@commontools/builder";
2828
import {
@@ -199,18 +199,23 @@ export function useCharmMentions() {
199199
useEffect(() => {
200200
const fetchCharmMentions = async () => {
201201
try {
202-
const charms = charmManager.getCharms();
203-
await charmManager.sync(charms);
202+
// Get mentionable charms - filtered to exclude trash and prioritize pinned
203+
const mentionableCharms = await getMentionableCharms(charmManager);
204204

205-
const mentions = charms.get().map((charm: any) => {
205+
// Convert to the format needed for mentions
206+
const mentions = mentionableCharms.map((charm) => {
206207
const data = charm.get();
207208
const name = data?.[NAME] ?? "Untitled";
208-
const id = charmId(charm.entityId!)!;
209+
const id = charmId(charm);
210+
if (!id) {
211+
console.warn(`Warning: Charm without ID found`, charm);
212+
return null;
213+
}
209214
return {
210215
id,
211216
name: `${name} (#${id.slice(-4)})`,
212217
};
213-
});
218+
}).filter((mention): mention is {id: string, name: string} => mention !== null);
214219

215220
setCharmMentions(mentions);
216221
} catch (error) {
@@ -358,21 +363,31 @@ export function CommandCenter() {
358363
const handleEditRecipe = (e: KeyboardEvent) => {
359364
if (e.key === "i" && (e.metaKey || e.ctrlKey)) {
360365
e.preventDefault();
366+
const editRecipeCommand = allCommands.find((cmd) => cmd.id === "edit-recipe");
367+
if (!editRecipeCommand) {
368+
console.warn("Edit recipe command not found");
369+
return;
370+
}
361371
setOpen(true);
362372
setMode({
363373
type: "input",
364-
command: allCommands.find((cmd) => cmd.id === "edit-recipe")!,
374+
command: editRecipeCommand,
365375
placeholder: "What would you like to change?",
366376
});
367377
}
368378
};
369379

370380
const handleEditRecipeEvent = () => {
371381
if (focusedCharmId) {
382+
const editRecipeCommand = allCommands.find((cmd) => cmd.id === "edit-recipe");
383+
if (!editRecipeCommand) {
384+
console.warn("Edit recipe command not found");
385+
return;
386+
}
372387
setOpen(true);
373388
setMode({
374389
type: "input",
375-
command: allCommands.find((cmd) => cmd.id === "edit-recipe")!,
390+
command: editRecipeCommand,
376391
placeholder: "What would you like to change?",
377392
});
378393
}
@@ -502,10 +517,12 @@ export function CommandCenter() {
502517
});
503518
break;
504519
case "select":
520+
// The select mode options should be provided by the command handler
521+
// since the SelectCommandItem interface doesn't have an options property
505522
setMode({
506523
type: "select",
507524
command: cmd,
508-
options: [], // You'll need to provide the actual options here
525+
options: [], // Options will be provided when the command is executed
509526
});
510527
break;
511528
case "transcribe":

jumble/src/utils/charms.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,64 @@
1-
import { type Charm } from "@commontools/charm";
2-
import { getEntityId } from "@commontools/runner";
1+
import { type Charm, CharmManager } from "@commontools/charm";
2+
import { Cell, getEntityId } from "@commontools/runner";
3+
import { NAME } from "@commontools/builder";
34

45
export function charmId(charm: Charm): string | undefined {
56
const id = getEntityId(charm);
67
return id ? id["/"] : undefined;
78
}
9+
10+
/**
11+
* Gets mentionable charms by filtering out trash and prioritizing pinned charms
12+
* @param charmManager The charm manager instance
13+
* @returns Promise that resolves to an array of mentionable charms (filtered out trash and pinned first)
14+
*/
15+
export async function getMentionableCharms(charmManager: CharmManager): Promise<Cell<Charm>[]> {
16+
// Sync all collections to ensure we have the latest data
17+
await Promise.all([
18+
charmManager.sync(charmManager.getCharms()),
19+
charmManager.sync(charmManager.getPinned()),
20+
charmManager.sync(charmManager.getTrash())
21+
]);
22+
23+
// Get all collections
24+
const allCharms = charmManager.getCharms().get();
25+
const pinnedCharms = charmManager.getPinned().get();
26+
const trashedCharms = charmManager.getTrash().get();
27+
28+
// Create a set of trashed charm IDs for quick lookup
29+
const trashedIds = new Set<string>(
30+
trashedCharms.map(charm => charmId(charm)).filter((id): id is string => id !== undefined)
31+
);
32+
33+
// Create a set of pinned charm IDs for quick lookup
34+
const pinnedIds = new Set<string>(
35+
pinnedCharms.map(charm => charmId(charm)).filter((id): id is string => id !== undefined)
36+
);
37+
38+
// Filter out trashed charms and those without IDs
39+
const mentionableCharms = allCharms.filter(charm => {
40+
const id = charmId(charm);
41+
return id !== undefined && !trashedIds.has(id);
42+
});
43+
44+
// Sort charms with pinned first, then by name
45+
return mentionableCharms.sort((a, b) => {
46+
const aId = charmId(a);
47+
const bId = charmId(b);
48+
49+
// By this point both aId and bId should be defined, but check just in case
50+
if (!aId || !bId) {
51+
console.warn("Unexpected undefined ID in sort function");
52+
return 0;
53+
}
54+
55+
// Sort pinned first
56+
if (pinnedIds.has(aId) && !pinnedIds.has(bId)) return -1;
57+
if (!pinnedIds.has(aId) && pinnedIds.has(bId)) return 1;
58+
59+
// Then sort by name
60+
const aName = a.get()?.[NAME] ?? "Untitled";
61+
const bName = b.get()?.[NAME] ?? "Untitled";
62+
return aName.localeCompare(bName);
63+
});
64+
}

0 commit comments

Comments
 (0)