Skip to content

Commit 9fe3de7

Browse files
committed
refactor(firefox): tab storage handling for Firefox compatibility
1 parent f806ea8 commit 9fe3de7

File tree

9 files changed

+46
-18
lines changed

9 files changed

+46
-18
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div align="center">
22

3-
<img src="assets/icon.png" alt="NativeMind Logo" width="32" height="32">
3+
<img src="docs/images/icon.png" alt="NativeMind Logo" width="32" height="32">
44

55
# NativeMind - Private, on-device AI Assistant, no cloud dependencies
66

@@ -13,7 +13,7 @@
1313

1414
**Vote to help more people discover private, convenient AI** 🙏
1515

16-
<img src="assets/images/demo-screenshot.png" alt="NativeMind in Action" width="800">
16+
<img src="docs/images/demo-screenshot.png" alt="NativeMind in Action" width="800">
1717

1818
[![Privacy First](https://img.shields.io/badge/Privacy-First-brightgreen?style=flat-square&logo=security&logoColor=white)]()
1919
[![No Tracking](https://img.shields.io/badge/Tracking-None-success?style=flat-square&logo=shield&logoColor=white)]()
@@ -49,7 +49,7 @@ Install directly from the [Chrome Web Store](https://chromewebstore.google.com/d
4949
3. **Start Using**: Click the NativeMind icon in your browser toolbar
5050

5151
<div align="center">
52-
<img src="assets/images/extension-popup.png" alt="NativeMind Extension Interface" width="600">
52+
<img src="docs/images/extension-popup.png" alt="NativeMind Extension Interface" width="600">
5353
<br>
5454
<em>For more details, visit our official website: <strong><a href="https://nativemind.app/?utm_source=github">nativemind.app</a></strong></em>
5555
</div>

docs/images/chrome-web-store.png

3.67 KB
Loading

docs/images/demo-screenrecord.gif

1.85 MB
Loading
File renamed without changes.
File renamed without changes.

docs/images/icon.png

91.3 KB
Loading

entrypoints/background/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import '@/utils/rpc'
33

44
import { browser } from 'wxt/browser'
55
import { defineBackground } from 'wxt/utils/define-background'
6+
import { storage } from 'wxt/utils/storage'
67

78
import { INVALID_URLS } from '@/utils/constants'
89
import { CONTEXT_MENU, CONTEXT_MENU_ITEM_TRANSLATE_PAGE, ContextMenuManager } from '@/utils/context-menu'
910
import logger from '@/utils/logger'
1011
import { bgBroadcastRpc } from '@/utils/rpc'
1112
import { isTabValid } from '@/utils/tab'
13+
import { getTabKeys } from '@/utils/tab-store'
1214
import { registerDeclarativeNetRequestRule } from '@/utils/web-request'
1315

1416
export default defineBackground(() => {
@@ -61,12 +63,18 @@ export default defineBackground(() => {
6163
})
6264

6365
browser.tabs.onRemoved.addListener(async (tabId, removeInfo) => {
64-
logger.info('tab removed', { tabId })
66+
logger.info('tab removed', { tabId, removeInfo, isFirefox: import.meta.env.FIREFOX })
6567
tabsWaitingForOpen.delete(tabId)
6668
bgBroadcastRpc.emit('tabRemoved', {
6769
tabId,
6870
...removeInfo,
6971
})
72+
if (import.meta.env.FIREFOX) {
73+
// Firefox does not support session storage in content scripts, so we need to clean up the tab store
74+
const keys = getTabKeys(tabId)
75+
logger.info('Cleaning up tab store for removed tab', { tabId, keys })
76+
await storage.removeItems(keys)
77+
}
7078
})
7179

7280
browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {

utils/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const NATIVEMIND_HOMEPAGE_URL = 'https://nativemind.app'
66
export const OLLAMA_SITE_DOWNLOAD_BUTTON_CLASS = 'nativemind-ollama-download-button'
77
export const CONTEXT_MENU_STORAGE_KEY = 'local:context-menu-map_1'
88
export const MIN_SELECTION_LENGTH_TO_SHOW_WRITING_TOOLS = 10
9+
export const TAB_STORE_STORAGE_KEY_PREFIX = 'tab-store-'
910

1011
export const INVALID_URLS = [
1112
/^https:\/\/chromewebstore\.google\.com/,

utils/tab-store/index.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { customRef, ref, toRaw, watch } from 'vue'
22
import { storage } from 'wxt/utils/storage'
33

4+
import { TAB_STORE_STORAGE_KEY_PREFIX } from '../constants'
45
import { debounce } from '../debounce'
56
import { Base64ImageData } from '../image'
67
import { lazyInitialize } from '../memo'
@@ -10,12 +11,15 @@ import type { HistoryItemV1 } from './history'
1011

1112
export { HistoryItemV1 }
1213

13-
async function defineTabValue<T>(key: string, defaultValue: T) {
14-
let sessionKey = `session:tab-store-${key}` as `${'local' | 'session'}:tab-store-${string}`
15-
if (import.meta.env.FIREFOX) {
16-
// Firefox does not support session storage in content scripts
17-
sessionKey = `local:tab-store-${key}` as `local:tab-store-${string}`
18-
}
14+
function constructStorageKey(tabId: number, key: string) {
15+
const scope = import.meta.env.FIREFOX
16+
? 'local'
17+
: 'session'
18+
return `${scope}:${TAB_STORE_STORAGE_KEY_PREFIX}${tabId}-${key}` as `${'local' | 'session'}:${string}`
19+
}
20+
21+
async function defineTabValue<T>(tabId: number, key: string, defaultValue: T) {
22+
const sessionKey = constructStorageKey(tabId, key)
1923
const valueInStorageStr = await storage.getItem<string>(sessionKey)
2024
const valueInStorage = valueInStorageStr ? JSON.parse(valueInStorageStr) : defaultValue
2125
const v = ref(valueInStorage)
@@ -59,14 +63,14 @@ async function _getTabStore() {
5963
const { tabId, faviconUrl, url, title } = await c2bRpc.getTabInfo()
6064
if (!tabId) throw new Error('no tab id')
6165
return {
62-
currentTabId: await defineTabValue('currentTabId', tabId),
63-
showContainer: await defineTabValue(`showContainer-${tabId}`, false),
64-
showSetting: await defineTabValue<ShowSettingsParams>(`showSetting-${tabId}`, { show: false }),
65-
chatHistory: await defineTabValue(`chatHistory-${tabId}`, [] as ChatHistory),
66-
pageSummary: await defineTabValue(`summary-${tabId}`, { content: '', summary: '' } as PageSummary),
67-
contextTabIds: await defineTabValue(`contextTabs-${tabId}`, [tabId] as number[]),
68-
contextImages: await defineTabValue(`contextImages-${tabId}`, [] as Base64ImageData[]),
69-
tabInfo: await defineTabValue(`tabInfo-${tabId}`, {
66+
currentTabId: await defineTabValue(tabId, 'currentTabId', tabId),
67+
showContainer: await defineTabValue(tabId, `showContainer`, false),
68+
showSetting: await defineTabValue<ShowSettingsParams>(tabId, `showSetting`, { show: false }),
69+
chatHistory: await defineTabValue(tabId, `chatHistory`, [] as ChatHistory),
70+
pageSummary: await defineTabValue(tabId, `summary`, { content: '', summary: '' } as PageSummary),
71+
contextTabIds: await defineTabValue(tabId, `contextTabs`, [tabId] as number[]),
72+
contextImages: await defineTabValue(tabId, `contextImages`, [] as Base64ImageData[]),
73+
tabInfo: await defineTabValue(tabId, `tabInfo`, {
7074
url,
7175
title,
7276
faviconUrl,
@@ -75,3 +79,18 @@ async function _getTabStore() {
7579
}
7680

7781
export const getTabStore = lazyInitialize(_getTabStore)
82+
83+
// this is a workaround for firefox to clear the tab store when the tab is closed
84+
export const getTabKeys = (tabId: number) => {
85+
const keys = [
86+
'currentTabId',
87+
'showContainer',
88+
'showSetting',
89+
'chatHistory',
90+
'summary',
91+
'contextTabs',
92+
'contextImages',
93+
'tabInfo',
94+
]
95+
return keys.map((key) => constructStorageKey(tabId, key))
96+
}

0 commit comments

Comments
 (0)