Skip to content

Commit 79ed441

Browse files
authored
fix: charm reload on charm-list change (#1040)
1 parent 2fd54c4 commit 79ed441

File tree

2 files changed

+35
-22
lines changed

2 files changed

+35
-22
lines changed

jumble/src/components/CharmRunner.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import React, { useRef } from "react";
2-
import { render } from "@commontools/html";
1+
import React, { useMemo, useRef } from "react";
2+
import { render, type VNode } from "@commontools/html";
33
import { UI } from "@commontools/builder";
44
import { charmSchema, fixItCharm } from "@commontools/charm";
55
import { useCharmManager } from "@/contexts/CharmManagerContext.tsx";
66
import { useNavigate } from "react-router-dom";
77
import { LuX } from "react-icons/lu";
88
import { DitheredCube } from "@/components/DitherCube.tsx";
99
import { createPath } from "@/routes.ts";
10-
import { charmId } from "@/utils/charms.ts";
10+
import { Cell, Charm, charmId } from "@/utils/charms.ts";
11+
1112
interface CharmLoaderProps {
1213
charmImport: () => Promise<any>;
1314
argument?: any;
@@ -16,7 +17,7 @@ interface CharmLoaderProps {
1617
}
1718

1819
interface CharmRendererProps {
19-
charm: any;
20+
charm: Cell<Charm>;
2021
argument?: any;
2122
className?: string;
2223
}
@@ -81,18 +82,19 @@ function RawCharmRenderer({ charm, className = "" }: CharmRendererProps) {
8182
const [runtimeError, setRuntimeError] = React.useState<Error | null>(null);
8283
const [isFixing, setIsFixing] = React.useState(false);
8384
const { charmManager, currentReplica } = useCharmManager();
85+
const id = useMemo(() => charmId(charm), [charm]);
8486
const navigate = useNavigate();
8587

8688
// Store a reference to the current charm to detect changes
87-
const prevCharmRef = useRef(charm);
89+
const prevCharmRef = useRef<Charm | null>(null);
8890

8991
// Clear error when charm changes
9092
React.useEffect(() => {
9193
if (prevCharmRef.current !== charm) {
9294
setRuntimeError(null);
9395
prevCharmRef.current = charm;
9496
}
95-
}, [charm]);
97+
}, [id]);
9698

9799
const handleFixIt = React.useCallback(async () => {
98100
if (!runtimeError || isFixing) return;
@@ -111,7 +113,7 @@ function RawCharmRenderer({ charm, className = "" }: CharmRendererProps) {
111113
} finally {
112114
setIsFixing(false);
113115
}
114-
}, [runtimeError, isFixing, charmManager, charm, currentReplica, navigate]);
116+
}, [runtimeError, isFixing, charmManager, id, currentReplica, navigate]);
115117

116118
React.useEffect(() => {
117119
const container = containerRef.current;
@@ -127,7 +129,10 @@ function RawCharmRenderer({ charm, className = "" }: CharmRendererProps) {
127129

128130
container.addEventListener("common-iframe-error", handleIframeError);
129131

130-
const cleanup = render(container, charm.asSchema(charmSchema).key(UI));
132+
const cleanup = render(
133+
container,
134+
charm.asSchema(charmSchema).key(UI) as Cell<VNode>,
135+
);
131136

132137
return () => {
133138
cleanup();
@@ -136,7 +141,7 @@ function RawCharmRenderer({ charm, className = "" }: CharmRendererProps) {
136141
container.innerHTML = "";
137142
}
138143
};
139-
}, [charm]);
144+
}, [id]);
140145

141146
return (
142147
<>

jumble/src/utils/charms.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { type Charm, CharmManager } from "@commontools/charm";
22
import { Cell, getEntityId } from "@commontools/runner";
33
import { NAME } from "@commontools/builder";
44

5+
export type { Cell, Charm };
6+
57
export function charmId(charm: Charm): string | undefined {
68
const id = getEntityId(charm);
79
return id ? id["/"] : undefined;
@@ -12,50 +14,56 @@ export function charmId(charm: Charm): string | undefined {
1214
* @param charmManager The charm manager instance
1315
* @returns Promise that resolves to an array of mentionable charms (filtered out trash and pinned first)
1416
*/
15-
export async function getMentionableCharms(charmManager: CharmManager): Promise<Cell<Charm>[]> {
17+
export async function getMentionableCharms(
18+
charmManager: CharmManager,
19+
): Promise<Cell<Charm>[]> {
1620
// Sync all collections to ensure we have the latest data
1721
await Promise.all([
1822
charmManager.sync(charmManager.getCharms()),
1923
charmManager.sync(charmManager.getPinned()),
20-
charmManager.sync(charmManager.getTrash())
24+
charmManager.sync(charmManager.getTrash()),
2125
]);
22-
26+
2327
// Get all collections
2428
const allCharms = charmManager.getCharms().get();
2529
const pinnedCharms = charmManager.getPinned().get();
2630
const trashedCharms = charmManager.getTrash().get();
27-
31+
2832
// Create a set of trashed charm IDs for quick lookup
2933
const trashedIds = new Set<string>(
30-
trashedCharms.map(charm => charmId(charm)).filter((id): id is string => id !== undefined)
34+
trashedCharms.map((charm) => charmId(charm)).filter((id): id is string =>
35+
id !== undefined
36+
),
3137
);
32-
38+
3339
// Create a set of pinned charm IDs for quick lookup
3440
const pinnedIds = new Set<string>(
35-
pinnedCharms.map(charm => charmId(charm)).filter((id): id is string => id !== undefined)
41+
pinnedCharms.map((charm) => charmId(charm)).filter((id): id is string =>
42+
id !== undefined
43+
),
3644
);
37-
45+
3846
// Filter out trashed charms and those without IDs
39-
const mentionableCharms = allCharms.filter(charm => {
47+
const mentionableCharms = allCharms.filter((charm) => {
4048
const id = charmId(charm);
4149
return id !== undefined && !trashedIds.has(id);
4250
});
43-
51+
4452
// Sort charms with pinned first, then by name
4553
return mentionableCharms.sort((a, b) => {
4654
const aId = charmId(a);
4755
const bId = charmId(b);
48-
56+
4957
// By this point both aId and bId should be defined, but check just in case
5058
if (!aId || !bId) {
5159
console.warn("Unexpected undefined ID in sort function");
5260
return 0;
5361
}
54-
62+
5563
// Sort pinned first
5664
if (pinnedIds.has(aId) && !pinnedIds.has(bId)) return -1;
5765
if (!pinnedIds.has(aId) && pinnedIds.has(bId)) return 1;
58-
66+
5967
// Then sort by name
6068
const aName = a.get()?.[NAME] ?? "Untitled";
6169
const bName = b.get()?.[NAME] ?? "Untitled";

0 commit comments

Comments
 (0)