mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-10 09:25:57 +00:00
644229f968
## New Features - Add `CitationLink` shared component for rendering citation hover cards - Add `CitationsLoadingIndicator` component for showing loading state - Add `removeAllCitations` utility to strip all citations from content - Add backend support for removing citations when downloading markdown files - Add i18n support for citation loading messages (en-US, zh-CN) ## Code Optimizations - Remove duplicate `ExternalLinkBadge` component, reuse `CitationLink` instead - Consolidate `remarkPlugins` config in `streamdownPlugins` to avoid duplication - Remove unused imports: `Citation`, `buildCitationMap`, `extractDomainFromUrl`, etc. - Remove unused `messages` parameter from `ToolCall` component - Remove unused `isWriteFile` parameter from `ArtifactFilePreview` component - Remove unused `useI18n` hook from `MessageContent` component ## Bug Fixes - Fix `remarkGfm` plugin configuration that prevented table rendering - Fix React Hooks rule violation: move `useMemo` to component top level - Replace `||` with `??` for nullish coalescing in clipboard data ## Code Cleanup - Remove debug console.log/info statements from: - `threads/hooks.ts` - `notification/hooks.ts` - `memory-settings-page.tsx` - Fix import order in `message-group.tsx` Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
2.5 KiB
TypeScript
99 lines
2.5 KiB
TypeScript
import { useState, useEffect, useCallback, useRef } from "react";
|
|
|
|
import { useLocalSettings } from "../settings";
|
|
|
|
interface NotificationOptions {
|
|
body?: string;
|
|
icon?: string;
|
|
badge?: string;
|
|
tag?: string;
|
|
data?: unknown;
|
|
requireInteraction?: boolean;
|
|
silent?: boolean;
|
|
}
|
|
|
|
interface UseNotificationReturn {
|
|
permission: NotificationPermission;
|
|
isSupported: boolean;
|
|
requestPermission: () => Promise<NotificationPermission>;
|
|
showNotification: (title: string, options?: NotificationOptions) => void;
|
|
}
|
|
|
|
export function useNotification(): UseNotificationReturn {
|
|
const [permission, setPermission] =
|
|
useState<NotificationPermission>("default");
|
|
const [isSupported, setIsSupported] = useState(false);
|
|
|
|
const lastNotificationTime = useRef<Date>(new Date());
|
|
|
|
useEffect(() => {
|
|
// Check if browser supports Notification API
|
|
if ("Notification" in window) {
|
|
setIsSupported(true);
|
|
setPermission(Notification.permission);
|
|
}
|
|
}, []);
|
|
|
|
const requestPermission =
|
|
useCallback(async (): Promise<NotificationPermission> => {
|
|
if (!isSupported) {
|
|
console.warn("Notification API is not supported in this browser");
|
|
return "denied";
|
|
}
|
|
|
|
const result = await Notification.requestPermission();
|
|
setPermission(result);
|
|
return result;
|
|
}, [isSupported]);
|
|
|
|
const [settings] = useLocalSettings();
|
|
|
|
const showNotification = useCallback(
|
|
(title: string, options?: NotificationOptions) => {
|
|
if (!isSupported) {
|
|
console.warn("Notification API is not supported");
|
|
return;
|
|
}
|
|
|
|
if (!settings.notification.enabled) {
|
|
console.warn("Notification is disabled");
|
|
return;
|
|
}
|
|
|
|
if (
|
|
new Date().getTime() - lastNotificationTime.current.getTime() <
|
|
1000
|
|
) {
|
|
console.warn("Notification sent too soon");
|
|
return;
|
|
}
|
|
lastNotificationTime.current = new Date();
|
|
|
|
if (permission !== "granted") {
|
|
console.warn("Notification permission not granted");
|
|
return;
|
|
}
|
|
|
|
const notification = new Notification(title, options);
|
|
|
|
// Optional: Add event listeners
|
|
notification.onclick = () => {
|
|
window.focus();
|
|
notification.close();
|
|
};
|
|
|
|
notification.onerror = (error) => {
|
|
console.error("Notification error:", error);
|
|
};
|
|
},
|
|
[isSupported, settings.notification.enabled, permission],
|
|
);
|
|
|
|
return {
|
|
permission,
|
|
isSupported,
|
|
requestPermission,
|
|
showNotification,
|
|
};
|
|
}
|