diff --git a/assets/icons/copy.svg b/assets/icons/copy.svg
new file mode 100644
index 00000000..7577d583
--- /dev/null
+++ b/assets/icons/copy.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/assets/icons/done.svg b/assets/icons/done.svg
new file mode 100644
index 00000000..4b295c40
--- /dev/null
+++ b/assets/icons/done.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/icons/language.svg b/assets/icons/language.svg
new file mode 100644
index 00000000..e27f08db
--- /dev/null
+++ b/assets/icons/language.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/assets/icons/logo.svg b/assets/icons/logo.svg
deleted file mode 100644
index ec6c3c68..00000000
--- a/assets/icons/logo.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
\ No newline at end of file
diff --git a/assets/icons/regenerate.svg b/assets/icons/regenerate.svg
new file mode 100644
index 00000000..34898a77
--- /dev/null
+++ b/assets/icons/regenerate.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/assets/icons/reply-suggestion.svg b/assets/icons/reply-suggestion.svg
new file mode 100644
index 00000000..502c9f73
--- /dev/null
+++ b/assets/icons/reply-suggestion.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/assets/icons/settings-email.svg b/assets/icons/settings-email.svg
new file mode 100644
index 00000000..9800cf54
--- /dev/null
+++ b/assets/icons/settings-email.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/assets/icons/writing-style.svg b/assets/icons/writing-style.svg
new file mode 100644
index 00000000..ccb55777
--- /dev/null
+++ b/assets/icons/writing-style.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/components/IconSelector.vue b/components/IconSelector.vue
new file mode 100644
index 00000000..54d7aadf
--- /dev/null
+++ b/components/IconSelector.vue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+
+ {{ placeholder }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ props.emptyPlaceholder }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/entrypoints/background/index.ts b/entrypoints/background/index.ts
index d762c414..98d0bbe4 100644
--- a/entrypoints/background/index.ts
+++ b/entrypoints/background/index.ts
@@ -17,6 +17,7 @@ import { registerDeclarativeNetRequestRule } from '@/utils/web-request'
import { BackgroundDatabaseManager } from './database'
import { BackgroundCacheServiceManager } from './services/cache-service'
import { BackgroundChatHistoryServiceManager } from './services/chat-history-service'
+import { BackgroundWindowManager } from './services/window-manager'
import { waitUntilSidepanelLoaded } from './utils'
export default defineBackground(() => {
@@ -169,6 +170,10 @@ export default defineBackground(() => {
await translationCache.initialize()
logger.debug('Translation cache initialized successfully')
+ // Initialize window manager service
+ await BackgroundWindowManager.initialize()
+ logger.debug('Window manager service initialized successfully')
+
// ================================
// Debug Code
// ================================
diff --git a/entrypoints/background/services/window-manager.ts b/entrypoints/background/services/window-manager.ts
new file mode 100644
index 00000000..ef9969dd
--- /dev/null
+++ b/entrypoints/background/services/window-manager.ts
@@ -0,0 +1,112 @@
+import { Browser, browser } from 'wxt/browser'
+
+import logger from '@/utils/logger'
+
+/**
+ * Window Manager Service for tracking active window IDs
+ * This service maintains a cache of the current active window ID to avoid async calls before sidePanel.open()
+ */
+class WindowManagerService {
+ private currentWindowId: number | null = null
+ private initialized = false
+
+ /**
+ * Initialize the window manager service
+ */
+ async initialize(): Promise {
+ if (this.initialized) {
+ return
+ }
+
+ try {
+ // Get initial current window
+ const currentWindow = await browser.windows.getCurrent()
+ this.currentWindowId = currentWindow.id || null
+ logger.debug('Window Manager initialized with window ID:', this.currentWindowId)
+
+ // Listen for window focus changes
+ browser.windows.onFocusChanged.addListener(this.handleWindowFocusChanged.bind(this))
+
+ // Listen for window created/removed events
+ browser.windows.onCreated.addListener(this.handleWindowCreated.bind(this))
+ browser.windows.onRemoved.addListener(this.handleWindowRemoved.bind(this))
+
+ this.initialized = true
+ }
+ catch (error) {
+ logger.error('Failed to initialize Window Manager:', error)
+ throw error
+ }
+ }
+
+ /**
+ * Get the current cached window ID (synchronous)
+ */
+ getCurrentWindowId(): number | null {
+ return this.currentWindowId
+ }
+
+ /**
+ * Handle window focus change events
+ */
+ private handleWindowFocusChanged(windowId: number): void {
+ if (windowId !== browser.windows.WINDOW_ID_NONE) {
+ this.currentWindowId = windowId
+ logger.debug('Active window changed to:', windowId)
+ }
+ }
+
+ /**
+ * Handle window created events
+ */
+ private handleWindowCreated(window: Browser.windows.Window): void {
+ if (window.focused && window.id) {
+ this.currentWindowId = window.id
+ logger.debug('New focused window created:', window.id)
+ }
+ }
+
+ /**
+ * Handle window removed events
+ */
+ private handleWindowRemoved(windowId: number): void {
+ if (windowId === this.currentWindowId) {
+ // Current window was closed, try to get a new active window
+ this.updateCurrentWindow()
+ }
+ }
+
+ /**
+ * Update current window by querying browser (fallback method)
+ */
+ private async updateCurrentWindow(): Promise {
+ try {
+ const currentWindow = await browser.windows.getCurrent()
+ this.currentWindowId = currentWindow.id || null
+ logger.debug('Updated current window ID:', this.currentWindowId)
+ }
+ catch (error) {
+ logger.warn('Failed to update current window:', error)
+ this.currentWindowId = null
+ }
+ }
+
+ /**
+ * Cleanup listeners on service shutdown
+ */
+ cleanup(): void {
+ if (browser.windows.onFocusChanged.hasListener(this.handleWindowFocusChanged)) {
+ browser.windows.onFocusChanged.removeListener(this.handleWindowFocusChanged)
+ }
+ if (browser.windows.onCreated.hasListener(this.handleWindowCreated)) {
+ browser.windows.onCreated.removeListener(this.handleWindowCreated)
+ }
+ if (browser.windows.onRemoved.hasListener(this.handleWindowRemoved)) {
+ browser.windows.onRemoved.removeListener(this.handleWindowRemoved)
+ }
+ this.initialized = false
+ logger.debug('Window Manager service cleaned up')
+ }
+}
+
+export const BackgroundWindowManager = new WindowManagerService()
diff --git a/entrypoints/content/App.vue b/entrypoints/content/App.vue
index 0f76b027..106854c4 100644
--- a/entrypoints/content/App.vue
+++ b/entrypoints/content/App.vue
@@ -2,20 +2,26 @@
+
+
+
diff --git a/entrypoints/content/components/GmailTools/GmailReplyCard.vue b/entrypoints/content/components/GmailTools/GmailReplyCard.vue
new file mode 100644
index 00000000..f1f42d8a
--- /dev/null
+++ b/entrypoints/content/components/GmailTools/GmailReplyCard.vue
@@ -0,0 +1,444 @@
+
+