///
/**
* Example: Source charm for charm linking.
* Referenced in: docs/common/CHARM_LINKING.md
*
* @reviewed 2025-12-10 docs-rationalization
*/
import { Cell, Default, handler, lift, NAME, pattern, UI } from "commontools";
interface Stats {
average: number;
count: number;
min: number;
max: number;
}
/** GPA Stats Source */
interface Input {
name: Default;
rawData: Default;
}
interface Output {
name: string;
rawData: string;
gpaStats: Stats | null;
}
const parseGpas = lift((raw: string): number[] => {
if (!raw.trim()) return [];
return raw.split("\n")
.map((line) => parseFloat(line.trim()))
.filter((n) => !isNaN(n));
});
const calculateStats = lift((values: number[]): Stats | null => {
if (values.length === 0) return null;
const sorted = [...values].sort((a, b) => a - b);
const sum = sorted.reduce((a, b) => a + b, 0);
return {
average: sum / sorted.length,
count: sorted.length,
min: sorted[0],
max: sorted[sorted.length - 1],
};
});
const updateData = handler<
{ target: { value: string } },
{ rawData: Cell }
>((event, { rawData }) => {
rawData.set(event.target.value);
});
export default pattern(({ name, rawData }) => {
const gpas = parseGpas(rawData);
const gpaStats = calculateStats(gpas);
return {
[NAME]: "GPA Source",
[UI]: (
GPA Data Entry
),
name,
rawData,
gpaStats,
};
});