askeroo
Version:
A modern CLI prompt library with flow control, history navigation, and conditional prompts
136 lines • 5.28 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { render } from "ink";
import { PromptApp } from "../components/PromptApp.js";
import { PromptStateProvider } from "./plugin-state-context.js";
import { debugLogger } from "../utils/logging.js";
import { globalRegistry } from "./registry.js";
// Generate stable IDs for prompts based on content and context
const generatePromptId = (type, label, groupName) => {
const parts = [type, label];
if (groupName)
parts.push(`group:${groupName}`);
return parts.join("|");
};
let appInstance = {};
function ensureApp() {
return new Promise((resolve) => {
if (appInstance.promptFn) {
resolve(appInstance.promptFn);
return;
}
const { unmount } = render(_jsx(PromptStateProvider, { children: _jsx(PromptApp, { onReady: (promptFn) => {
appInstance.promptFn = promptFn;
appInstance.unmount = unmount;
resolve(promptFn);
}, runtime: currentRuntime }) }), {
exitOnCtrlC: false, // Prevent Ink from exiting immediately on Ctrl+C
});
});
}
// Store reference to the runtime for re-discovery and tree access
let currentRuntime = null;
// Create a dynamic UI object that includes plugin handlers
function createUI() {
const ui = {
/**
* Show a group in the UI (called by runtime when executing group plugin)
*/
async showGroup(label, flow, id, discoveredFields, enableArrowNavigation, depth, parentGroup, groupOptions // Accept all group options including hideOnCompletion
) {
const groupId = id || generatePromptId("group", label || "group");
appInstance.currentGroup = groupId;
const promptFn = await ensureApp();
// Extract additional options while preserving core properties
const { label: _, flow: __, id: ___, enableArrowNavigation: ____, ...additionalOptions } = groupOptions || {};
await promptFn({
type: "group",
id: groupId,
label: label,
flow,
discoveredFields,
enableArrowNavigation,
depth,
groupName: parentGroup, // Pass parent group for proper nesting
...additionalOptions, // Spread additional options like hideOnCompletion
});
},
clearGroup() {
appInstance.currentGroup = undefined;
},
cleanup() {
if (appInstance.unmount) {
debugLogger.log("UI_CLEANUP", "UI cleanup triggered");
appInstance.unmount();
appInstance.promptFn = undefined;
appInstance.unmount = undefined;
appInstance.currentGroup = undefined;
debugLogger.cleanup();
}
},
setRuntime(runtime) {
currentRuntime = runtime;
},
getTreeManager() {
// NEW: Expose runtime's tree manager to UI
return currentRuntime?.getTree?.() || null;
},
async rediscoverStaticGroup(groupId) {
if (currentRuntime?.rediscoverStaticGroupFields) {
return await currentRuntime.rediscoverStaticGroupFields(groupId);
}
return null;
},
async completeFlow() {
const promptFn = await ensureApp();
await promptFn({
type: "completeFlow",
id: "flow-completion",
});
},
onGroupCompleted(groupId) {
// Trigger a re-render by sending a UI update event
ensureApp().then((promptFn) => {
promptFn({
type: "groupCompleted",
id: groupId,
});
});
},
};
// Create plugin handlers upfront (no Proxy)
for (const plugin of globalRegistry.getAll()) {
ui[plugin.type] = async function (opts, currentGroup, id) {
// Always update currentGroup, even if it's undefined
// This ensures fields outside groups don't inherit the previous group
appInstance.currentGroup =
typeof currentGroup === "string" ? currentGroup : undefined;
const promptFn = await ensureApp();
// Create the request object with all options spread in
const request = {
type: plugin.type,
id: id ||
generatePromptId(plugin.type, opts.label || `${plugin.type} field`),
groupName: appInstance.currentGroup,
...opts, // Spread all options from the plugin
};
return promptFn(request);
};
}
return ui;
}
// Lazy UI creation to support dynamic plugin registration
let uiInstance = null;
function ensureUI() {
if (!uiInstance) {
uiInstance = createUI();
}
return uiInstance;
}
// Export a lightweight Proxy that lazily creates the UI on first access
// This allows plugins to register before the UI is created
export const ui = new Proxy({}, {
get(_target, prop) {
return ensureUI()[prop];
},
});
//# sourceMappingURL=ui.js.map