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
JavaScript
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
};