From 47d9152bda9a7e672271a6604a4633c01c1b3fbc Mon Sep 17 00:00:00 2001 From: john kevin asprec Date: Tue, 14 Jul 2026 21:35:54 +0800 Subject: [PATCH] refactor: single source for writing styles + action labels; add theme module - src/lib/actions.ts: canonical ACTIONS/ACTION_LABELS/WRITING_STYLES; context-menu styles derived (WRITING_STYLES minus Default). Replaces the three duplicated style arrays in popup, content, and background. - src/lib/theme.ts: canonical palette. Existing hex literals in the entrypoints are swapped wholesale when light/dark theming lands, so the color plumbing is only rewritten once. Co-Authored-By: Claude Fable 5 --- entrypoints/background.ts | 17 ++++------------- entrypoints/content.ts | 3 ++- entrypoints/popup/Popup.tsx | 4 +--- src/lib/actions.ts | 23 +++++++++++++++++++++++ src/lib/theme.ts | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 src/lib/actions.ts create mode 100644 src/lib/theme.ts diff --git a/entrypoints/background.ts b/entrypoints/background.ts index 14563dc..83cb32a 100644 --- a/entrypoints/background.ts +++ b/entrypoints/background.ts @@ -1,6 +1,7 @@ import { defineBackground } from 'wxt/utils/define-background'; import nacl from 'tweetnacl'; import type { AnalyzePayload, LexAIConfig, LexAIResponse } from '@lib/types'; +import { ACTIONS, ACTION_LABELS, CONTEXT_MENU_STYLES } from '@lib/actions'; // ─── Fetch with timeout ─────────────────────────────────────────────────────── @@ -431,25 +432,15 @@ export default defineBackground(() => { console.log('LexAI background service worker started'); // ─── Context menus ─────────────────────────────────────────────────────── - const CONTEXT_ACTIONS = ['fix', 'rephrase', 'shorten', 'expand', 'explain'] as const; - const CONTEXT_ACTION_LABELS: Record = { - fix: 'Fix Grammar', - rephrase: 'Rephrase', - shorten: 'Shorten', - expand: 'Expand', - explain: 'Explain', - }; - const CONTEXT_STYLES = ['Formal', 'Casual', 'Academic', 'Creative', 'Concise']; - chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.removeAll(() => { - CONTEXT_ACTIONS.forEach(action => { + ACTIONS.forEach(action => { chrome.contextMenus.create({ id: `lexai-${action}`, - title: `⚡ LexAI: ${CONTEXT_ACTION_LABELS[action]}`, + title: `⚡ LexAI: ${ACTION_LABELS[action]}`, contexts: ['selection'], }); - CONTEXT_STYLES.forEach(style => { + CONTEXT_MENU_STYLES.forEach(style => { chrome.contextMenus.create({ id: `lexai-${action}-${style.toLowerCase()}`, parentId: `lexai-${action}`, diff --git a/entrypoints/content.ts b/entrypoints/content.ts index 4423942..1f28cee 100644 --- a/entrypoints/content.ts +++ b/entrypoints/content.ts @@ -1,5 +1,6 @@ import { defineContentScript } from 'wxt/utils/define-content-script'; import { isExtensionValid, safeSendMessage } from '@lib/messaging'; +import { WRITING_STYLES } from '@lib/actions'; export default defineContentScript({ matches: [''], @@ -744,7 +745,7 @@ export default defineContentScript({ modal.appendChild(body); // ─── Style selector row ─────────────────────────────────────────────── - const STYLES = ['Default', 'Formal', 'Casual', 'Academic', 'Creative', 'Concise']; + const STYLES = WRITING_STYLES; const styleRow = document.createElement('div'); Object.assign(styleRow.style, { diff --git a/entrypoints/popup/Popup.tsx b/entrypoints/popup/Popup.tsx index cda530b..06d64ee 100644 --- a/entrypoints/popup/Popup.tsx +++ b/entrypoints/popup/Popup.tsx @@ -2,9 +2,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { createRoot } from 'react-dom/client'; import { safeSendMessage, safeStorageGet, safeStorageSet } from '@lib/messaging'; -// ─── Constants ─────────────────────────────────────────────────────────────── - -const WRITING_STYLES = ['Default', 'Formal', 'Casual', 'Academic', 'Creative', 'Concise']; +import { WRITING_STYLES } from '@lib/actions'; // ─── Types ─────────────────────────────────────────────────────────────────── diff --git a/src/lib/actions.ts b/src/lib/actions.ts new file mode 100644 index 0000000..ffc8af0 --- /dev/null +++ b/src/lib/actions.ts @@ -0,0 +1,23 @@ +// 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'] as const; +export type ActionId = (typeof ACTIONS)[number]; + +export const ACTION_LABELS: Record = { + fix: 'Fix Grammar', + rephrase: 'Rephrase', + shorten: 'Shorten', + expand: 'Expand', + explain: 'Explain', +}; + +// 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'); diff --git a/src/lib/theme.ts b/src/lib/theme.ts new file mode 100644 index 0000000..0a09bc0 --- /dev/null +++ b/src/lib/theme.ts @@ -0,0 +1,32 @@ +// Canonical LexAI palette (Catppuccin Mocha-derived). This is the single +// source of truth for UI colors; a light variant can be added here later. +// +// NOTE: the entrypoints still contain literal hex values from before this +// module existed. New/edited styles should reference THEME; the wholesale +// swap of existing literals happens together with light/dark theming so the +// color plumbing is only rewritten once. + +export const THEME = { + // Surfaces + base: '#1e1e2e', + mantle: '#181825', + surface: '#313244', + surfaceGradient: 'linear-gradient(135deg, #1e1e2e 0%, #181825 100%)', + + // Text + text: '#cdd6f4', + subtext: '#a6adc8', + muted: '#6c7086', + + // Accents + accent: '#89b4fa', // blue — primary actions, links + success: '#a6e3a1', // green — success toasts + danger: '#f38ba8', // red — errors + warning: '#f9e2af', // yellow + + // Borders + border: 'rgba(205,214,244,0.12)', + borderStrong: 'rgba(205,214,244,0.15)', +} as const; + +export type Theme = typeof THEME;