// Promise-based confirm dialog state, host-side mirror of // `packages/plugins/shared/components/confirm.ts`. Mirrored on purpose: recipe-book (that // copy's consumer) is a deliberately gui-chat-protocol-only sample, so sharing via // @mulmoclaude/core would force a core dep onto the sample + scaffold — unify only if that // dependency-minimalism policy changes. (The ConfirmModal.vue components' vue-i18n-vs-useRuntime // locale split is a separate constraint on the components, not on this file.) import { ref } from "vue"; export interface ConfirmOptions { title?: string; message: string; confirmText?: string; cancelText?: string; variant?: "primary" | "success" | "danger"; } export interface ConfirmState { isOpen: boolean; title: string; message: string; confirmText: string; cancelText: string; variant: "primary" | "success" | "danger"; resolve: ((value: boolean) => void) | null; } export const confirmState = ref({ isOpen: false, title: "", message: "", confirmText: "", cancelText: "", variant: "primary", resolve: null, }); export function useConfirm() { function openConfirm(options: ConfirmOptions | string): Promise { const opts = typeof options === "string" ? { message: options } : options; return new Promise((resolve) => { // If a previous confirm is still pending, settle it as // "cancelled" before replacing the state. Without this the // earlier `Promise` would hang forever and any // caller `await`ing it would deadlock. const previous = confirmState.value.resolve; if (previous) previous(false); confirmState.value = { isOpen: true, title: opts.title || "", message: opts.message, confirmText: opts.confirmText || "", cancelText: opts.cancelText || "", variant: opts.variant || "primary", resolve, }; }); } function handleConfirm(value: boolean): void { if (confirmState.value.resolve) { confirmState.value.resolve(value); } confirmState.value.isOpen = false; confirmState.value.resolve = null; } return { confirmState, openConfirm, handleConfirm }; }