Skip to content

Commit a440f3e

Browse files
lcompleteclaude
andcommitted
feat(extension): improve server connection handling and add sponsor
- Add graceful handling when Huntly server connection fails, showing warning and allowing AI features to work with external providers - Hide Huntly AI option in toolbar when server is not connected - Fix shortcuts_preview message being sent twice by adding skipPreview flag when preview is already open - Bump extension version to 0.5.2 - Add jtsang4 to sponsors list Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 84f1102 commit a440f3e

File tree

7 files changed

+87
-41
lines changed

7 files changed

+87
-41
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,6 @@ Thank you to all our sponsors for your generous support!
149149
<td align="center"><a href="https://www.jetbrains.com/">jetbrains</a></td>
150150
<td align="center"><a href="https://taresky.com/">TARESKY</a></td>
151151
<td align="center"><a href="https://x.com/Db9el25LULCBrcn">抹茶</a></td>
152+
<td align="center"><a href="https://github.com/jtsang4">jtsang4</a></td>
152153
</tr>
153154
</table>

README.zh.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,6 @@ sudo xattr -r -d com.apple.quarantine /YOUR_PATH/Huntly.app
149149
<td align="center"><a href="https://www.jetbrains.com/">jetbrains</a></td>
150150
<td align="center"><a href="https://taresky.com/">TARESKY</a></td>
151151
<td align="center"><a href="https://x.com/Db9el25LULCBrcn">抹茶</a></td>
152+
<td align="center"><a href="https://github.com/jtsang4">jtsang4</a></td>
152153
</tr>
153154
</table>

app/extension/public/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"manifest_version": 3,
33
"name": "Huntly",
4-
"description": "AI shortcuts (summarization, bilingual translation, key points extraction, etc.), auto-saves web pages and tweets, and provides immersive article preview.",
5-
"version": "0.5.1",
4+
"description": "Huntly - AI shortcuts (summarization, bilingual translation, key points extraction, etc.), auto-saves web pages and tweets, and provides immersive article preview.",
5+
"version": "0.5.2",
66
"options_ui": {
77
"page": "options.html",
88
"open_in_tab": true

app/extension/src/background.ts

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,11 @@ function cancelVercelAITask(taskId: string): boolean {
2727
return false;
2828
}
2929

30-
function startProcessingWithShortcuts(task: any, shortcuts: any[]) {
30+
function startProcessingWithShortcuts(task: any, shortcuts: any[], skipPreview: boolean = false) {
3131
if (!task) return;
32-
33-
chrome.tabs.sendMessage(task.tabId, {
34-
type: 'shortcuts_preview',
35-
payload: {
36-
shortcuts: shortcuts,
37-
taskId: task.taskId,
38-
page: {
39-
title: task.title,
40-
content: task.content,
41-
url: task.url,
42-
description: "",
43-
thumbUrl: "",
44-
author: "",
45-
siteName: "",
46-
language: "",
47-
category: "",
48-
isLiked: false,
49-
isFavorite: false,
50-
domain: "",
51-
faviconUrl: "",
52-
contentType: task.contentType, // Pass contentType for snippet mode
53-
}
54-
}
55-
}, function(response) {
32+
33+
// Function to start the actual processing
34+
const startProcessing = () => {
5635
// 发送处理开始的消息
5736
chrome.tabs.sendMessage(task.tabId, {
5837
type: 'shortcuts_processing_start',
@@ -62,22 +41,22 @@ function startProcessingWithShortcuts(task: any, shortcuts: any[]) {
6241
taskId: task.taskId
6342
}
6443
});
65-
44+
6645
let accumulatedContent = "";
67-
46+
6847
// 使用新的 SSERequestManager 处理文章内容
6948
sseRequestManager.processContentWithShortcutStream(
7049
task.taskId,
7150
task.tabId,
72-
task.content,
51+
task.content,
7352
task.shortcutId,
7453
task.shortcutName,
7554
task.url,
7655
task.title, // 传递文章标题
7756
// onData callback - 接收流式数据
7857
(data: string, taskId: string) => {
7958
accumulatedContent += data;
80-
59+
8160
// 发送流式数据到预览页面
8261
try {
8362
chrome.tabs.sendMessage(task.tabId, {
@@ -114,7 +93,7 @@ function startProcessingWithShortcuts(task: any, shortcuts: any[]) {
11493
// onError callback - 处理错误
11594
(error: any, taskId: string) => {
11695
console.error("Error processing with shortcut for task:", taskId, error);
117-
96+
11897
try {
11998
chrome.tabs.sendMessage(task.tabId, {
12099
type: 'shortcuts_process_error',
@@ -129,6 +108,39 @@ function startProcessingWithShortcuts(task: any, shortcuts: any[]) {
129108
}
130109
}
131110
);
111+
};
112+
113+
// If preview is already open, skip sending shortcuts_preview message
114+
if (skipPreview) {
115+
startProcessing();
116+
return;
117+
}
118+
119+
// Send shortcuts_preview to open the preview window first
120+
chrome.tabs.sendMessage(task.tabId, {
121+
type: 'shortcuts_preview',
122+
payload: {
123+
shortcuts: shortcuts,
124+
taskId: task.taskId,
125+
page: {
126+
title: task.title,
127+
content: task.content,
128+
url: task.url,
129+
description: "",
130+
thumbUrl: "",
131+
author: "",
132+
siteName: "",
133+
language: "",
134+
category: "",
135+
isLiked: false,
136+
isFavorite: false,
137+
domain: "",
138+
faviconUrl: "",
139+
contentType: task.contentType, // Pass contentType for snippet mode
140+
}
141+
}
142+
}, function(response) {
143+
startProcessing();
132144
});
133145
}
134146

@@ -304,7 +316,8 @@ chrome.runtime.onMessage.addListener(function (msg: Message, sender, sendRespons
304316
contentType: msg.payload.contentType,
305317
};
306318
const shortcuts = msg.payload.shortcuts || [];
307-
startProcessingWithShortcuts(task, shortcuts);
319+
const skipPreview = msg.payload.skipPreview || false;
320+
startProcessingWithShortcuts(task, shortcuts, skipPreview);
308321
} else {
309322
// Use Vercel AI SDK for other providers
310323
const task = {

app/extension/src/components/AIToolbar.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ export interface AIToolbarProps {
7676
externalModels?: ExternalModelsData;
7777
/** Initial selected model (used when auto-executing from popup) */
7878
initialSelectedModel?: ModelItem | null;
79+
/** Hide Huntly AI option (e.g., when server is not connected) */
80+
hideHuntlyAI?: boolean;
7981
}
8082

8183
// Gradient definition for AI icon
@@ -98,6 +100,7 @@ export const AIToolbar: React.FC<AIToolbarProps> = ({
98100
externalShortcuts,
99101
externalModels,
100102
initialSelectedModel,
103+
hideHuntlyAI = false,
101104
}) => {
102105
// Determine if using external data
103106
const useExternalShortcuts = !!externalShortcuts;
@@ -152,6 +155,18 @@ export const AIToolbar: React.FC<AIToolbarProps> = ({
152155
}
153156
}, [initialSelectedModel]);
154157

158+
// Switch away from Huntly AI if it's hidden
159+
useEffect(() => {
160+
if (hideHuntlyAI && selectedModel?.provider === 'huntly-server') {
161+
const nonHuntlyModel = models.find(m => m.provider !== 'huntly-server');
162+
if (nonHuntlyModel) {
163+
setSelectedModel(nonHuntlyModel);
164+
} else {
165+
setSelectedModel(null);
166+
}
167+
}
168+
}, [hideHuntlyAI, selectedModel, models]);
169+
155170
// Menu state
156171
const [shortcutAnchorEl, setShortcutAnchorEl] = useState<null | HTMLElement>(null);
157172
const [modelAnchorEl, setModelAnchorEl] = useState<null | HTMLElement>(null);
@@ -343,8 +358,8 @@ export const AIToolbar: React.FC<AIToolbarProps> = ({
343358
(showUserSystemPrompts && (userPrompts.length > 0 || systemPrompts.length > 0));
344359
const hasModels = models.length > 0;
345360

346-
// Group models by provider - only show Huntly AI when huntlyShortcutsEnabled is true
347-
const huntlyModels = huntlyShortcutsEnabled
361+
// Group models by provider - only show Huntly AI when huntlyShortcutsEnabled is true and not hidden
362+
const huntlyModels = (huntlyShortcutsEnabled && !hideHuntlyAI)
348363
? models.filter(m => m.provider === 'huntly-server')
349364
: [];
350365
const otherModels = models.filter(m => m.provider !== 'huntly-server');

app/extension/src/components/ArticlePreview.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ export const ArticlePreview: React.FC<ArticlePreviewProps> = ({
178178
title: isSnippetMode ? "" : page.title,
179179
contentType: isSnippetMode ? 4 : undefined,
180180
selectedModel: selectedModel,
181+
skipPreview: true, // Preview is already open, skip shortcuts_preview message
181182
},
182183
});
183184
}, [isProcessing, currentTaskId, page, isSnippetMode]);

app/extension/src/popup.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const Popup = () => {
7575
const [storageSettings, setStorageSettings] = useState<StorageSettings>(null);
7676
const [username, setUsername] = useState<string>(null);
7777
const [loadingUser, setLoadingUser] = useState(true);
78+
const [serverConnectionFailed, setServerConnectionFailed] = useState(false);
7879
const [page, setPage] = useState<PageModel>(null);
7980
const [autoSavedPageId, setAutoSavedPageId] = useState<number>(0);
8081
const [articleOperateResult, setArticleOperateResult] = useState<PageOperateResult>(null);
@@ -151,16 +152,21 @@ const Popup = () => {
151152
if (!settings.serverUrl) {
152153
// No server enabled - still load page info for parsing, but skip server-related operations
153154
setLoadingUser(false);
155+
setServerConnectionFailed(false);
154156
loadPageInfoOnly();
155157
} else {
156158
setLoadingUser(true);
159+
setServerConnectionFailed(false);
157160
getLoginUserInfo().then((data) => {
158161
const result = JSON.parse(data);
159162
setUsername(result.username);
160163

161164
loadPageInfo();
162165
}).catch(() => {
163166
setUsername(null);
167+
// Server connection failed - still show article preview in read-only mode
168+
setServerConnectionFailed(true);
169+
loadPageInfoOnly();
164170
}).finally(() => {
165171
setLoadingUser(false);
166172
});
@@ -551,9 +557,9 @@ const Popup = () => {
551557
<CircularProgress/>
552558
</div>
553559
}
554-
{/* Server configured but not signed in */}
560+
{/* Server configured but not signed in and server is reachable */}
555561
{
556-
!loadingUser && storageSettings?.serverUrl && !username && <div>
562+
!loadingUser && storageSettings?.serverUrl && !username && !serverConnectionFailed && <div>
557563
<div className={'mt-5'}>
558564
<Alert severity={'info'}>Please sign in to start.</Alert>
559565
</div>
@@ -562,9 +568,17 @@ const Popup = () => {
562568
</div>
563569
</div>
564570
}
565-
{/* Server configured and signed in, or no server configured (read-only mode) */}
571+
{/* Server configured and signed in, no server configured (read-only mode), or server connection failed */}
566572
{
567-
!loadingUser && (username || !storageSettings?.serverUrl) && <div>
573+
!loadingUser && (username || !storageSettings?.serverUrl || serverConnectionFailed) && <div>
574+
{/* Server connection failed warning */}
575+
{serverConnectionFailed && (
576+
<div className={'mb-2'}>
577+
<Alert severity={'warning'}>
578+
Cannot connect to Huntly server.
579+
</Alert>
580+
</div>
581+
)}
568582
{/* RSS Feed Subscription Interface */}
569583
{checkingRssFeed && (
570584
<div className={'flex justify-center items-center h-[120px]'}>
@@ -662,8 +676,8 @@ const Popup = () => {
662676
<div>
663677
<div className={'flex items-center'}>
664678
<TextField value={activePage.url} size={"small"} fullWidth={true} disabled={true}/>
665-
{/* Only show action buttons when server is configured and not on Huntly site */}
666-
{storageSettings?.serverUrl && !isHuntlySite && <div className={'grow shrink-0 pl-2'}>
679+
{/* Only show action buttons when server is configured, connected, and not on Huntly site */}
680+
{storageSettings?.serverUrl && !isHuntlySite && !serverConnectionFailed && <div className={'grow shrink-0 pl-2'}>
667681
{
668682
activeOperateResult?.readLater ? (
669683
<Tooltip title={"Remove from read later"}>
@@ -784,6 +798,7 @@ const Popup = () => {
784798
onShortcutClick={handleAIShortcutClick}
785799
isProcessing={processingShortcut}
786800
compact={true}
801+
hideHuntlyAI={serverConnectionFailed}
787802
/>
788803
</div>
789804
</div>

0 commit comments

Comments
 (0)