// Canonical action + writing-style lists shared by the toolbar, popup, // context menus, and result modal. Values must stay in sync with the system // prompts in the background worker. // 'fix' is the user-facing id emitted by the context menu and popup; the // background normalizes it to the 'grammar' prompt. export const ACTIONS = ['fix', 'rephrase', 'shorten', 'expand', 'explain', 'prompt'] as const; export type ActionId = (typeof ACTIONS)[number]; export const ACTION_LABELS: Record = { fix: 'Fix Grammar', rephrase: 'Rephrase', shorten: 'Shorten', expand: 'Expand', explain: 'Explain', prompt: 'Make Prompt', }; // Full list, including the 'Default' pseudo-style (= no style modifier). export const WRITING_STYLES = ['Default', 'Formal', 'Casual', 'Academic', 'Creative', 'Concise'] as const; export type WritingStyle = (typeof WRITING_STYLES)[number]; // Context menus omit 'Default' — the parent action item already covers it. export const CONTEXT_MENU_STYLES = WRITING_STYLES.filter((s) => s !== 'Default'); // ─── Prompt Builder parameters (the 'prompt' action) ───────────────────────── // 'Auto' always means "let the prompt engineer decide from the input". export const PROMPT_STYLES = ['Auto', 'Instructional', 'Role-play', 'Step-by-step', 'Few-shot', 'Structured'] as const; export type PromptStyle = (typeof PROMPT_STYLES)[number]; // 'Custom…' switches the popup to a free-text persona field. export const PROMPT_PERSONAS = [ 'Auto', 'None', 'Expert Developer', 'Copywriter', 'Teacher', 'Data Analyst', 'Business Consultant', 'Researcher', 'Custom…', ] as const; export type PromptPersona = (typeof PROMPT_PERSONAS)[number]; export const PROMPT_FORMATS = ['Auto', 'Plain text', 'Markdown', 'Bulleted list', 'Numbered steps', 'JSON', 'Table'] as const; export type PromptFormat = (typeof PROMPT_FORMATS)[number]; // 'Custom…' means "use the free-text persona"; an empty custom text falls // back to Auto. Shared by the popup and the background (which resolves saved // Prompt Builder settings for toolbar/context-menu prompt requests). export function resolvePromptPersona(persona?: string, customPersona?: string): string | undefined { if (persona === 'Custom…') return customPersona?.trim() || 'Auto'; return persona; } // Minimum selection length (chars, after trim) before the floating toolbar // appears. Kept deliberately above 1-2 chars so accidental double-click // selections don't trigger the UI. export const MIN_SELECTION_LENGTH = 10; // ─── Context-menu registry ──────────────────────────────────────────────────── // Declarative id -> {action, style} mapping. Replaces the old split('-') // parsing of menuItemId, which only worked because no action or style name // contained a hyphen. Order matters: each parent precedes its children so // chrome.contextMenus.create never sees an unknown parentId. export interface ContextMenuEntry { id: string; action: ActionId; style: WritingStyle; // 'Default' entries are top-level parents; styled entries nest under them. parentId?: string; title: string; } export const CONTEXT_MENU_ENTRIES: ContextMenuEntry[] = ACTIONS.flatMap((action) => [ { id: `lexai-${action}`, action, style: 'Default' as WritingStyle, title: `⚡ LexAI: ${ACTION_LABELS[action]}`, }, // 'prompt' opens the Prompt Builder dialog in the page, which has its own // parameters — writing-style children don't apply to it. ...(action === 'prompt' ? [] : CONTEXT_MENU_STYLES.map((style) => ({ id: `lexai-${action}-${style.toLowerCase()}`, action, style, parentId: `lexai-${action}`, title: style as string, }))), ]); export function findContextMenuEntry(menuItemId: string): ContextMenuEntry | undefined { return CONTEXT_MENU_ENTRIES.find((e) => e.id === menuItemId); }