UNPKG

trellis

Version:

Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications

137 lines (134 loc) 3.71 kB
import { fuzzyMatch } from "./chunk-EEQNVCWD.js"; // src/palette/core/index.ts function defaultFilter(query, items) { const scored = []; for (const item of items) { const score = fuzzyMatch(query, [item.label, ...item.keywords ?? []]); if (score > 0) scored.push({ item, score }); } scored.sort((a, b) => b.score - a.score); return scored.map(({ item }) => item); } function groupItems(items) { const groups = []; const byId = /* @__PURE__ */ new Map(); for (const item of items) { const key = item.group ?? ""; let group = byId.get(key); if (!group) { group = { id: key, title: key || "All", items: [] }; byId.set(key, group); groups.push(group); } group.items.push(item); } return groups; } function createPaletteCore(config) { const { items = [], onSelect, closeOnSelect = true, wrap = true } = config; const filter = config.filter ?? defaultFilter; const baseState = { open: false, query: "", items, loading: false }; let state = deriveState({ ...baseState, results: items, selectedIndex: 0 }); const subscribers = /* @__PURE__ */ new Set(); const notify = () => subscribers.forEach((fn) => fn()); function deriveState(partial) { const results = filter(partial.query, partial.items); let selectedIndex = partial.selectedIndex; if (results.length === 0) { selectedIndex = 0; } else if (selectedIndex >= results.length) { selectedIndex = wrap ? 0 : results.length - 1; } else if (selectedIndex < 0) { selectedIndex = wrap ? results.length - 1 : 0; } return { ...partial, results, selectedIndex, empty: partial.open && !partial.loading && results.length === 0, groups: groupItems(results) }; } const actions = { open: () => { state = deriveState({ ...state, open: true }); notify(); }, close: () => { state = deriveState({ ...state, open: false, query: "" }); notify(); }, toggle: () => { state = deriveState({ ...state, open: !state.open, query: !state.open ? "" : state.query }); notify(); }, setQuery: (query) => { state = deriveState({ ...state, query, selectedIndex: 0 }); notify(); }, moveSelection: (delta) => { if (state.results.length === 0) return; const next = state.selectedIndex + delta; let selectedIndex; if (wrap) { selectedIndex = (next % state.results.length + state.results.length) % state.results.length; } else { selectedIndex = Math.min(Math.max(next, 0), state.results.length - 1); } state = deriveState({ ...state, selectedIndex }); notify(); }, setSelectedIndex: (index) => { state = deriveState({ ...state, selectedIndex: index }); notify(); }, select: () => { const item = state.results[state.selectedIndex]; if (!item || item.disabled) return null; if (onSelect) onSelect(item); if (closeOnSelect) { state = deriveState({ ...state, open: false, query: "" }); notify(); } return item; }, setLoading: (loading) => { state = deriveState({ ...state, loading }); notify(); }, setItems: (nextItems) => { state = deriveState({ ...state, items: nextItems, selectedIndex: 0, loading: false }); notify(); } }; const core = { get state() { return state; }, actions, subscribe: (listener) => { subscribers.add(listener); return () => subscribers.delete(listener); } }; return core; } export { createPaletteCore };