Skip to content

Commit dca02a1

Browse files
authored
Merge pull request #4 from NativeMindBrowser/dev
Merge: merge dev into main (1.2.0)
2 parents 8cf9af9 + e8c9982 commit dca02a1

File tree

13 files changed

+359
-46
lines changed

13 files changed

+359
-46
lines changed

CHANGELOG.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,46 @@
11
# Changelog
22

33

4+
## v1.2.0-beta.16
5+
6+
[compare changes](https://github.com/NativeMindBrowser/NativeMindExtension/compare/v1.2.0-beta.15...v1.2.0-beta.16)
7+
8+
### 🚀 Enhancements
9+
10+
- **background:** Inject content script on installation ([a9b410d](https://github.com/NativeMindBrowser/NativeMindExtension/commit/a9b410d))
11+
12+
### ❤️ Contributors
13+
14+
- Tony Hu ([@tonyhu-012](http://github.com/tonyhu-012))
15+
16+
## v1.2.0-beta.15
17+
18+
[compare changes](https://github.com/NativeMindBrowser/NativeMindExtension/compare/v1.2.0-beta.14...v1.2.0-beta.15)
19+
20+
### 🚀 Enhancements
21+
22+
- **wxt:** Add module to expose web resources and update config ([93c5d03](https://github.com/NativeMindBrowser/NativeMindExtension/commit/93c5d03))
23+
24+
### 🩹 Fixes
25+
26+
- **styles:** Enhance style injection and loading mechanism for shadow DOM ([e947697](https://github.com/NativeMindBrowser/NativeMindExtension/commit/e947697))
27+
28+
### ❤️ Contributors
29+
30+
- Tony Hu ([@tonyhu-012](http://github.com/tonyhu-012))
31+
32+
## v1.2.0-beta.14
33+
34+
[compare changes](https://github.com/NativeMindBrowser/NativeMindExtension/compare/v1.2.0-beta.13...v1.2.0-beta.14)
35+
36+
### 🩹 Fixes
37+
38+
- **translator:** Ensure translation menu updates only when document is visible and improve context menu handling ([2f8ad72](https://github.com/NativeMindBrowser/NativeMindExtension/commit/2f8ad72))
39+
40+
### ❤️ Contributors
41+
42+
- Tony Hu ([@tonyhu-012](http://github.com/tonyhu-012))
43+
444
## v1.2.0-beta.13
545

646
[compare changes](https://github.com/NativeMindBrowser/NativeMindExtension/compare/v1.2.0-beta.12...v1.2.0-beta.13)

composables/useInjectStyle.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { onScopeDispose } from 'vue'
22

3-
export function useInjectStyle(inlineCss: string) {
3+
export function useInjectStyle(inlineCss: string, attachedElement?: HTMLElement) {
44
const styleElement = document.createElement('style')
55
styleElement.textContent = inlineCss
66
styleElement.setAttribute('data-nativemind-style', 'true')
77
styleElement.setAttribute('data-nativemind-style-injected', 'true')
88

99
// Append the style element to the head
10-
document.head.appendChild(styleElement)
10+
;(attachedElement ?? document.head).appendChild(styleElement)
1111

1212
onScopeDispose(() => {
1313
styleElement.remove()

entrypoints/background.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { INVALID_URLS } from '@/utils/constants'
88
import { CONTEXT_MENU, CONTEXT_MENU_ITEM_TRANSLATE_PAGE } from '@/utils/context-menu'
99
import logger from '@/utils/logger'
1010
import { bgBroadcastRpc } from '@/utils/rpc'
11-
import { registerBackgroundRpcEvent } from '@/utils/rpc/background-fns'
1211
import { isTabValid } from '@/utils/tab'
1312
import { registerDeclarativeNetRequestRule } from '@/utils/web-request'
1413

@@ -37,8 +36,7 @@ export default defineBackground(() => {
3736
})
3837

3938
const setPopupStatusBasedOnUrl = async (tabId: number, url: string) => {
40-
const isValidUrl = /https?:\/\//.test(url ?? '')
41-
if (!isValidUrl || unAttachedTabs.has(tabId) || INVALID_URLS.some((regex) => regex.test(url))) {
39+
if (INVALID_URLS.some((regex) => regex.test(url))) {
4240
await browser.action.setPopup({ popup: 'popup.html' })
4341
}
4442
else {
@@ -84,14 +82,8 @@ export default defineBackground(() => {
8482
})
8583
})
8684

87-
const unAttachedTabs = new Set<number>()
88-
8985
browser.runtime.onInstalled.addListener(async () => {
9086
logger.debug('Extension Installed')
91-
const tabs = await browser.tabs.query({ currentWindow: true })
92-
for (const tab of tabs) {
93-
tab.id && unAttachedTabs.add(tab.id)
94-
}
9587
await browser.contextMenus.removeAll()
9688
for (const menu of CONTEXT_MENU) {
9789
browser.contextMenus.create({
@@ -100,6 +92,23 @@ export default defineBackground(() => {
10092
contexts: menu.contexts,
10193
})
10294
}
95+
// inject content script into all tabs which are opened before the extension is installed
96+
const tabs = await browser.tabs.query({})
97+
for (const tab of tabs) {
98+
if (tab.id && tab.url) {
99+
const tabUrl = tab.url
100+
if (INVALID_URLS.some((regex) => regex.test(tabUrl))) continue
101+
await browser.scripting.executeScript({
102+
files: ['/content-scripts/content.js'],
103+
target: { tabId: tab.id },
104+
world: 'ISOLATED',
105+
}).then(() => {
106+
logger.info('Content script injected', { tabId: tab.id })
107+
}).catch((error) => {
108+
logger.error('Failed to inject content script', { tabId: tab.id, error })
109+
})
110+
}
111+
}
103112
})
104113

105114
browser.contextMenus.onClicked.addListener(async (info, tab) => {
@@ -112,9 +121,5 @@ export default defineBackground(() => {
112121
}
113122
})
114123

115-
registerBackgroundRpcEvent('ready', (tabId) => {
116-
unAttachedTabs.delete(tabId)
117-
})
118-
119124
logger.info('Hello background!', { id: browser.runtime.id })
120125
})

entrypoints/content/composables/useTranslator.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,35 +61,51 @@ async function _useTranslator() {
6161
})
6262

6363
watch(enabled, async (newVal) => {
64-
await setTranslationMenuTargetLanguage(newVal, targetLocale.value)
64+
if (document.visibilityState === 'visible') {
65+
await setTranslationMenuTargetLanguage(newVal, targetLocale.value)
66+
}
6567
})
6668

6769
watch(targetLocale, async (newVal) => {
68-
await setTranslationMenuTargetLanguage(enabled.value, newVal)
70+
if (document.visibilityState === 'visible') {
71+
await setTranslationMenuTargetLanguage(enabled.value, newVal)
72+
}
6973
})
7074

75+
let isWaiting = false
76+
7177
registerContentScriptRpcEvent('contextMenuClicked', async (e) => {
72-
if (!enabled.value && userConfig.llm.endpointType.get() === 'ollama') {
73-
if (!(await ollamaStatusStore.updateConnectionStatus())) {
74-
toast('Failed to connect to Ollama server, please check your Ollama connection', { duration: 2000 })
75-
showSettings(true, 'server-address-section')
76-
return
78+
if (isWaiting) return
79+
isWaiting = true
80+
try {
81+
if (!enabled.value && userConfig.llm.endpointType.get() === 'ollama') {
82+
if (!(await ollamaStatusStore.updateConnectionStatus())) {
83+
toast('Failed to connect to Ollama server, please check your Ollama connection', { duration: 2000 })
84+
showSettings(true, 'server-address-section')
85+
return
86+
}
87+
else if ((await ollamaStatusStore.updateModelList()).length === 0) {
88+
toast('No model found, please download a model.', { duration: 2000 })
89+
showSettings(true, 'model-download-section')
90+
return
91+
}
92+
}
93+
if (e.menuItemId === 'native-mind-page-translate') {
94+
await onInit()
95+
enabled.value = toggleTranslation(!enabled.value)
7796
}
78-
else if ((await ollamaStatusStore.updateModelList()).length === 0) {
79-
toast('No model found, please download a model.', { duration: 2000 })
80-
showSettings(true, 'model-download-section')
81-
return
97+
else if (e.menuItemId === 'native-mind-selection-translate') {
98+
await onInit()
99+
const selection = window.getSelection()
100+
const commonAncestor = getCommonAncestorElement(selection)
101+
commonAncestor && translation.translateElement(commonAncestor)
82102
}
83103
}
84-
if (e.menuItemId === 'native-mind-page-translate') {
85-
await onInit()
86-
enabled.value = toggleTranslation(!enabled.value)
104+
catch (error) {
105+
logger.error('Error handling context menu click', error)
87106
}
88-
else if (e.menuItemId === 'native-mind-selection-translate') {
89-
await onInit()
90-
const selection = window.getSelection()
91-
const commonAncestor = getCommonAncestorElement(selection)
92-
commonAncestor && translation.translateElement(commonAncestor)
107+
finally {
108+
isWaiting = false
93109
}
94110
})
95111

entrypoints/content/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { createShadowRootOverlay } from './ui'
1212

1313
export default defineContentScript({
1414
matches: ['*://*/*'],
15-
cssInjectionMode: 'ui',
15+
cssInjectionMode: 'manual',
1616
runAt: 'document_start',
1717
async main(ctx) {
1818
const ui = await createShadowRootOverlay(ctx, ({ rootElement }) => {

entrypoints/content/ui.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
11
import { createPinia } from 'pinia'
22
import type { Component } from 'vue'
33
import { createApp } from 'vue'
4+
import { browser } from 'wxt/browser'
45
import { ContentScriptContext } from 'wxt/utils/content-script-context'
56
import { createShadowRootUi } from 'wxt/utils/content-script-ui/shadow-root'
7+
import { splitShadowRootCss } from 'wxt/utils/split-shadow-root-css'
68

79
import { initToast } from '@/composables/useToast'
10+
import { FONT_FACE_CSS } from '@/utils/constants'
11+
import { convertPropertiesIntoSimpleVariables, createStyleSheetByCssText, loadContentScriptCss, replaceFontFaceUrl, scopeStyleIntoShadowRoot } from '@/utils/css'
812
import { i18n } from '@/utils/i18n'
913

14+
async function loadStyleSheet(shadowRoot: ShadowRoot) {
15+
const contentScriptCss = await loadContentScriptCss(import.meta.env.ENTRYPOINT)
16+
const fontFaceCss = await loadContentScriptCss(FONT_FACE_CSS)
17+
const { shadowCss, documentCss } = splitShadowRootCss(contentScriptCss)
18+
shadowRoot.adoptedStyleSheets.push(scopeStyleIntoShadowRoot(shadowCss))
19+
shadowRoot.adoptedStyleSheets.push(convertPropertiesIntoSimpleVariables(scopeStyleIntoShadowRoot(documentCss), true))
20+
// font-face can only be applied to the document, not the shadow root
21+
document.adoptedStyleSheets.push(replaceFontFaceUrl(createStyleSheetByCssText(fontFaceCss), (url) => browser.runtime.getURL(url as Parameters<typeof browser.runtime.getURL>[0])))
22+
}
23+
1024
export async function createShadowRootOverlay(ctx: ContentScriptContext, component: Component<{ rootElement: HTMLDivElement }>) {
1125
const ui = await createShadowRootUi(ctx, {
1226
name: 'nativemind-container',
1327
position: 'overlay',
1428
isolateEvents: true,
1529
mode: 'open',
1630
anchor: 'html',
17-
onMount(uiContainer, _shadow, shadowHost) {
31+
async onMount(uiContainer, shadowRoot, shadowHost) {
32+
await loadStyleSheet(shadowRoot)
1833
const rootElement = document.createElement('div')
1934
const toastRoot = document.createElement('div')
2035
uiContainer.appendChild(rootElement)
@@ -31,8 +46,8 @@ export async function createShadowRootOverlay(ctx: ContentScriptContext, compone
3146
app.mount(rootElement)
3247
return app
3348
},
34-
onRemove(app) {
35-
app?.unmount()
49+
async onRemove(app) {
50+
(await app)?.unmount()
3651
},
3752
})
3853
return ui

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nativemind-extension",
3-
"version": "1.2.0-beta.13",
3+
"version": "1.2.0-beta.16",
44
"private": false,
55
"author": "NativeMind",
66
"keywords": [
@@ -100,6 +100,7 @@
100100
"eslint": "^9.26.0",
101101
"eslint-plugin-simple-import-sort": "^12.1.1",
102102
"fs-extra": "^11.3.0",
103+
"glob": "^11.0.3",
103104
"globals": "^16.1.0",
104105
"husky": "^9.1.7",
105106
"prettier-plugin-tailwindcss": "^0.6.11",

0 commit comments

Comments
 (0)