Skip to content

Commit 5e98414

Browse files
authored
Merge pull request #330 from ujiro99/feat/fix-ui
Feat/fix UI
2 parents 80e6f02 + a89d473 commit 5e98414

7 files changed

Lines changed: 69 additions & 119 deletions

File tree

packages/extension/src/background_script.ts

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const getActiveTabId = (
6060
return true
6161
}
6262

63-
const onConnect = async function (port: chrome.runtime.Port) {
63+
const onConnect = async function(port: chrome.runtime.Port) {
6464
if (port.name !== CONNECTION_APP) return
6565
port.onDisconnect.addListener(() => onDisconnect(port))
6666
const tabId = port.sender?.tab?.id
@@ -73,7 +73,7 @@ const onConnect = async function (port: chrome.runtime.Port) {
7373
await PageActionBackground.handleSidePanelConnect(port)
7474
}
7575
}
76-
const onDisconnect = async function (port: chrome.runtime.Port) {
76+
const onDisconnect = async function(port: chrome.runtime.Port) {
7777
if (port.name !== CONNECTION_APP) return
7878
if (chrome.runtime.lastError) {
7979
if (
@@ -170,24 +170,24 @@ const commandFuncs = {
170170

171171
const cmd = isSearch
172172
? {
173+
id: params.id,
174+
title: params.title,
175+
searchUrl: params.searchUrl,
176+
iconUrl: params.iconUrl,
177+
openMode: params.openMode,
178+
openModeSecondary: params.openModeSecondary,
179+
spaceEncoding: params.spaceEncoding,
180+
popupOption: PopupOption,
181+
}
182+
: isPageAction
183+
? {
173184
id: params.id,
174185
title: params.title,
175-
searchUrl: params.searchUrl,
176186
iconUrl: params.iconUrl,
177187
openMode: params.openMode,
178-
openModeSecondary: params.openModeSecondary,
179-
spaceEncoding: params.spaceEncoding,
188+
pageActionOption: params.pageActionOption,
180189
popupOption: PopupOption,
181190
}
182-
: isPageAction
183-
? {
184-
id: params.id,
185-
title: params.title,
186-
iconUrl: params.iconUrl,
187-
openMode: params.openMode,
188-
pageActionOption: params.pageActionOption,
189-
popupOption: PopupOption,
190-
}
191191
: null
192192

193193
if (!cmd) {
@@ -229,6 +229,7 @@ const commandFuncs = {
229229
) => {
230230
const handleOpenInTab = async () => {
231231
let w: WindowType | undefined
232+
const targetUrl = sender.tab?.url ?? sender.url
232233

233234
const stack = await WindowStackManager.getStack()
234235
for (const layer of stack) {
@@ -241,7 +242,7 @@ const commandFuncs = {
241242
}
242243
if (!w || w.srcWindowId == null) {
243244
console.warn("window not found", sender.tab?.windowId)
244-
chrome.tabs.create({ url: sender.url })
245+
chrome.tabs.create({ url: targetUrl })
245246
await closeWindow(sender.tab?.windowId as number, "openInTab")
246247
await WindowStackManager.removeWindow(sender.tab?.windowId as number)
247248
response(true)
@@ -261,10 +262,7 @@ const commandFuncs = {
261262
}
262263

263264
if (targetId) {
264-
chrome.tabs.create({
265-
url: sender.url,
266-
windowId: targetId,
267-
})
265+
chrome.tabs.create({ url: targetUrl, windowId: targetId })
268266
await closeWindow(sender.tab?.windowId as number, "openInTab")
269267
await WindowStackManager.removeWindow(sender.tab?.windowId as number)
270268
response(true)
@@ -547,17 +545,17 @@ const checkAndPerformLegacyBackup = async () => {
547545
}
548546
}
549547

550-
// Initialize commandIdObj and register listener at top-level
551-
// to ensure they are available when service worker restarts
552-
;(async () => {
553-
try {
554-
await ContextMenu.syncCommandIdObj()
555-
chrome.contextMenus.onClicked.addListener(ContextMenu.onClicked)
556-
} catch (error) {
557-
// Ignore errors during initialization (e.g., in test environment)
558-
console.debug("Failed to initialize context menu listener:", error)
559-
}
560-
})()
548+
// Initialize commandIdObj and register listener at top-level
549+
// to ensure they are available when service worker restarts
550+
; (async () => {
551+
try {
552+
await ContextMenu.syncCommandIdObj()
553+
chrome.contextMenus.onClicked.addListener(ContextMenu.onClicked)
554+
} catch (error) {
555+
// Ignore errors during initialization (e.g., in test environment)
556+
console.debug("Failed to initialize context menu listener:", error)
557+
}
558+
})()
561559

562560
Settings.addChangedListener(() => ContextMenu.init())
563561

packages/extension/src/const.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ export const COMMAND_TYPE_GROUPS = [
109109
titleKey: "commandGroup_webPage_title",
110110
types: [
111111
COMMAND_TYPE.SEARCH,
112-
COMMAND_TYPE.PAGE_ACTION,
113112
COMMAND_TYPE.AI_PROMPT,
113+
COMMAND_TYPE.PAGE_ACTION,
114114
],
115115
},
116116
{

packages/extension/src/services/aiPrompt.ts

Lines changed: 11 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -19,76 +19,13 @@ export type AiService = {
1919
/** External endpoint URL for AI service config data. */
2020
const AI_SERVICES_URL = `${HUB_URL}/data/ai-services.json`
2121

22-
/**
23-
* List of supported AI services with their DOM selectors.
24-
* Used as fallback when the external fetch fails and no cache is available.
25-
* Selector arrays are tried in order, using the first one that matches.
26-
*/
27-
const AI_SERVICES_FALLBACK: AiService[] = [
28-
{
29-
id: "chatgpt",
30-
name: "ChatGPT",
31-
url: "https://chatgpt.com",
32-
faviconUrl: "https://chatgpt.com/favicon.ico",
33-
inputSelectors: ["#prompt-textarea", "[data-testid='prompt-textarea']"],
34-
submitSelectors: [
35-
"form button.composer-submit-button-color",
36-
"button#composer-submit-button",
37-
"button[data-testid='composer-speech-button']",
38-
],
39-
selectorType: SelectorType.css,
40-
},
41-
{
42-
id: "gemini",
43-
name: "Gemini",
44-
url: "https://gemini.google.com/app",
45-
faviconUrl:
46-
"https://www.gstatic.com/lamda/images/gemini_sparkle_aurora_33f86dc0c0257da337c63.svg",
47-
inputSelectors: [".ql-editor[contenteditable='true']"],
48-
submitSelectors: ["button.send-button", "button mat-icon[fonticon='send']"],
49-
selectorType: SelectorType.css,
50-
},
51-
{
52-
id: "claude",
53-
name: "Claude",
54-
url: "https://claude.ai/new",
55-
faviconUrl: "https://claude.ai/favicon.ico",
56-
inputSelectors: [
57-
"div[contenteditable='true'][aria-label]",
58-
"div[contenteditable='true'].ProseMirror",
59-
],
60-
submitSelectors: [
61-
"#main-content button.Button_claude__c_hZy",
62-
"#main-content button[data-state='closed']",
63-
],
64-
selectorType: SelectorType.css,
65-
},
66-
{
67-
id: "perplexity",
68-
name: "Perplexity",
69-
url: "https://www.perplexity.ai",
70-
faviconUrl: "https://favicon.im/perplexity.ai",
71-
inputSelectors: [
72-
"div#ask-input",
73-
"div[contenteditable='true'][role='textbox']",
74-
],
75-
submitSelectors: [
76-
"button[type='button']:has(use[*|href='#pplx-icon-custom-perplexity-v2v'])",
77-
"button[type='button']:has(use[*|href='#pplx-icon-arrow-up'])",
78-
"button[type='button']:has(use[*|href^='#pplx-icon-arrow-right'])",
79-
"button[aria-label='Submit']",
80-
],
81-
selectorType: SelectorType.css,
82-
},
83-
]
84-
8522
/** Today's date string "YYYY-MM-DD" used as cache TTL key. */
8623
const todayStr = (): string => new Date().toISOString().slice(0, 10)
8724

8825
/**
8926
* Normalize raw JSON data fetched from the external endpoint into AiService[].
90-
* Items that are missing required fields (id, url, inputSelectors, submitSelectors)
91-
* are silently skipped. The external JSON may omit `selectorType`, defaulting to CSS.
27+
* - Items that are missing required fields (id, url, inputSelectors, submitSelectors) are silently skipped.
28+
* - The external JSON may omit `selectorType`, defaulting to `CSS`.
9229
*/
9330
const normalizeServices = (raw: unknown[]): AiService[] => {
9431
const results: AiService[] = []
@@ -118,6 +55,15 @@ const normalizeServices = (raw: unknown[]): AiService[] => {
11855
return results
11956
}
12057

58+
/**
59+
* List of supported AI services with their DOM selectors.
60+
* Built from the hub's ai-services.json at compile time.
61+
* Used as fallback when the external fetch fails and no cache is available.
62+
* Selector arrays are tried in order, using the first one that matches.
63+
*/
64+
const AI_SERVICES_FALLBACK: AiService[] =
65+
normalizeServices(__AI_SERVICES_JSON__)
66+
12167
/**
12268
* Retrieve AI service definitions.
12369
* Strategy:
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
/// <reference types="vite/client" />
22

33
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
4-
interface ImportMetaEnv {}
4+
interface ImportMetaEnv { }
55

66
interface ImportMeta {
77
readonly env: ImportMetaEnv
88
}
99

1010
declare const __APP_VERSION__: string
1111
declare const __APP_NAME__: string
12+
declare const __AI_SERVICES_JSON__: unknown[]

packages/extension/vite.config.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from "fs"
12
import path from "path"
23
import { defineConfig, loadEnv } from "vite"
34
import react from "@vitejs/plugin-react"
@@ -106,6 +107,10 @@ export default defineConfig(({ mode }) => {
106107
define: {
107108
__APP_NAME__: JSON.stringify(packageJson.name),
108109
__APP_VERSION__: JSON.stringify(packageJson.version),
110+
__AI_SERVICES_JSON__: fs.readFileSync(
111+
path.resolve(__dirname, "../hub/public/data/ai-services.json"),
112+
"utf-8",
113+
),
109114
},
110115
resolve: {
111116
alias: {
@@ -118,20 +123,20 @@ export default defineConfig(({ mode }) => {
118123
pure:
119124
mode === "production"
120125
? [
121-
"console.log",
122-
"console.debug",
123-
"console.info",
124-
"console.trace",
125-
"console.dir",
126-
"console.count",
127-
"console.countReset",
128-
"console.group",
129-
"console.groupCollapsed",
130-
"console.groupEnd",
131-
"console.time",
132-
"console.timeEnd",
133-
"console.timeLog",
134-
]
126+
"console.log",
127+
"console.debug",
128+
"console.info",
129+
"console.trace",
130+
"console.dir",
131+
"console.count",
132+
"console.countReset",
133+
"console.group",
134+
"console.groupCollapsed",
135+
"console.groupEnd",
136+
"console.time",
137+
"console.timeEnd",
138+
"console.timeLog",
139+
]
135140
: [],
136141
},
137142
build: {

packages/extension/vitest.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from "fs"
12
import { defineConfig, mergeConfig } from "vitest/config"
23
import { resolve } from "path"
34
import packageJson from "./package.json"
@@ -13,6 +14,10 @@ export default mergeConfig(
1314
define: {
1415
__APP_NAME__: JSON.stringify(packageJson.name),
1516
__APP_VERSION__: JSON.stringify(packageJson.version),
17+
__AI_SERVICES_JSON__: fs.readFileSync(
18+
resolve(__dirname, "../hub/public/data/ai-services.json"),
19+
"utf-8",
20+
),
1621
},
1722
resolve: {
1823
alias: {

packages/hub/public/data/ai-services.json

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
"name": "ChatGPT",
55
"url": "https://chatgpt.com",
66
"faviconUrl": "https://chatgpt.com/favicon.ico",
7-
"inputSelectors": [
8-
"#prompt-textarea",
9-
"[data-testid='prompt-textarea']"
10-
],
7+
"inputSelectors": ["#prompt-textarea", "[data-testid='prompt-textarea']"],
118
"submitSelectors": [
129
"form button.composer-submit-button-color",
1310
"button#composer-submit-button",
@@ -19,9 +16,7 @@
1916
"name": "Gemini",
2017
"url": "https://gemini.google.com/app",
2118
"faviconUrl": "https://www.gstatic.com/lamda/images/gemini_sparkle_aurora_33f86dc0c0257da337c63.svg",
22-
"inputSelectors": [
23-
".ql-editor[contenteditable='true']"
24-
],
19+
"inputSelectors": [".ql-editor[contenteditable='true']"],
2520
"submitSelectors": [
2621
"button.send-button",
2722
"button mat-icon[fonticon='send']"
@@ -31,7 +26,7 @@
3126
"id": "claude",
3227
"name": "Claude",
3328
"url": "https://claude.ai/new",
34-
"faviconUrl": "https://claude.ai/favicon.ico",
29+
"faviconUrl": "https://favicon.im/claude.ai",
3530
"inputSelectors": [
3631
"div[contenteditable='true'][aria-label]",
3732
"div[contenteditable='true'].ProseMirror"

0 commit comments

Comments
 (0)