58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import type { PieceSummary } from '../api';
|
|
|
|
export interface SplitPieces {
|
|
defaults: PieceSummary[];
|
|
customs: PieceSummary[];
|
|
}
|
|
|
|
/**
|
|
* Split a flat list of PieceSummary into Default (built-in) and Custom sections.
|
|
* Pieces with source === 'builtin' go to defaults; all others go to customs.
|
|
*/
|
|
export function splitPieces(pieces: PieceSummary[]): SplitPieces {
|
|
const defaults: PieceSummary[] = [];
|
|
const customs: PieceSummary[] = [];
|
|
for (const p of pieces) {
|
|
if (p.source === 'builtin') {
|
|
defaults.push(p);
|
|
} else {
|
|
customs.push(p);
|
|
}
|
|
}
|
|
return { defaults, customs };
|
|
}
|
|
|
|
/**
|
|
* De-duplicate a flat list of PieceSummary by name, keeping the highest-priority
|
|
* source for each name — mirroring the executor's resolution order:
|
|
* user-custom > global-custom > builtin
|
|
*
|
|
* Use this wherever a piece is SELECTED TO RUN (task creation, scheduled tasks,
|
|
* continue-with-piece dialogs). The result matches what the executor will actually
|
|
* run, so the user sees exactly one "chat" entry rather than two.
|
|
*
|
|
* Do NOT use this in the management/PiecesPage view — that view intentionally
|
|
* shows both the builtin and any same-named custom side-by-side.
|
|
*/
|
|
export function resolvePieceOptions(pieces: PieceSummary[]): PieceSummary[] {
|
|
const PRIORITY: Record<string, number> = {
|
|
'user-custom': 0,
|
|
'global-custom': 1,
|
|
builtin: 2,
|
|
};
|
|
const seen = new Map<string, PieceSummary>();
|
|
for (const p of pieces) {
|
|
const existing = seen.get(p.name);
|
|
const pPrio = PRIORITY[p.source ?? 'builtin'] ?? 2;
|
|
if (!existing) {
|
|
seen.set(p.name, p);
|
|
} else {
|
|
const existingPrio = PRIORITY[existing.source ?? 'builtin'] ?? 2;
|
|
if (pPrio < existingPrio) {
|
|
seen.set(p.name, p);
|
|
}
|
|
}
|
|
}
|
|
return Array.from(seen.values());
|
|
}
|