Files
deer-flow/frontend/src/core/notification/hooks.ts
T
LofiSu 644229f968 feat(citations): add shared citation components and optimize code
## 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>
2026-02-04 11:56:10 +08:00

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,
};
}