Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/web-ui/src/app/components/AboutDialog/AboutDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const AboutDialog: React.FC<AboutDialogProps> = ({
onClose={onClose}
title={t('header.about')}
showCloseButton={true}
size="small"
size="medium"
>
<div className="bitfun-about-dialog__content">
{/* Hero section - product info */}
Expand Down
6 changes: 1 addition & 5 deletions src/web-ui/src/flow_chat/components/ChatInput.scss
Original file line number Diff line number Diff line change
Expand Up @@ -758,11 +758,7 @@
max-height: 280px;
display: flex;
flex-direction: column;
background: linear-gradient(
135deg,
var(--color-bg-elevated) 0%,
var(--color-bg-tertiary) 100%
);
background: var(--color-bg-elevated);
border: 1px solid var(--border-subtle);
border-radius: 8px;
box-shadow:
Expand Down
34 changes: 33 additions & 1 deletion src/web-ui/src/flow_chat/components/modern/ExportImageButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { FlowToolCard } from '../FlowToolCard';
import { Tooltip } from '@/component-library';
import type { DialogTurn, FlowTextItem, FlowToolItem } from '../../types/flow-chat';
import { i18nService } from '@/infrastructure/i18n';
import { workspaceAPI } from '@/infrastructure/api';
import { createLogger } from '@/shared/utils/logger';
import { downloadDir, join } from '@tauri-apps/api/path';
import { writeFile } from '@tauri-apps/plugin-fs';
Expand Down Expand Up @@ -256,7 +257,38 @@ export const ExportImageButton: React.FC<ExportImageButtonProps> = ({
const arrayBuffer = await blob.arrayBuffer();
await writeFile(filePath, new Uint8Array(arrayBuffer));

notificationService.success(i18nService.t('flow-chat:exportImage.exportSuccess', { filePath }));
const plainSuccessMessage = i18nService.t('flow-chat:exportImage.exportSuccess', { filePath });
const successPrefix = i18nService.t('flow-chat:exportImage.exportSuccessPrefix');

const revealExportedFile = async () => {
if (typeof window === 'undefined' || !('__TAURI__' in window)) {
return;
}
try {
await workspaceAPI.revealInExplorer(filePath);
} catch (error) {
log.error('Failed to reveal export path in file manager', { filePath, error });
}
};

notificationService.success(plainSuccessMessage, {
messageNode: (
<>
{successPrefix}
<button
type="button"
className="notification-item__path-link"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
void revealExportedFile();
}}
>
{filePath}
</button>
</>
),
});
} catch (error) {
log.error('Export failed', error);
notificationService.error(i18nService.t('flow-chat:exportImage.exportFailed'));
Expand Down
24 changes: 0 additions & 24 deletions src/web-ui/src/flow_chat/components/modern/ModelRoundItem.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,6 @@
margin-top: 0.5rem;
}

.model-round-item__feedback-group {
display: flex;
align-items: center;
gap: 0.25rem;
padding-left: 0.5rem;
border-left: 1px solid rgba(255, 255, 255, 0.1);
}

.model-round-item__action-btn {
display: flex;
align-items: center;
Expand Down Expand Up @@ -112,22 +104,6 @@
}
}

// Feedback buttons
.model-round-item__feedback-btn {
&:hover:has(svg[data-lucide="thumbs-up"]) {
color: #22c55e;
}

&:hover:has(svg[data-lucide="thumbs-down"]) {
color: #ef4444;
}

&:hover:has(svg[data-lucide="alert-triangle"]) {
color: #f59e0b;
}
}


@keyframes fadeIn {
from {
opacity: 0;
Expand Down
38 changes: 1 addition & 37 deletions src/web-ui/src/flow_chat/components/modern/ModelRoundItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import React, { useMemo, useState, useCallback, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Copy, Check, ThumbsUp, ThumbsDown, AlertTriangle } from 'lucide-react';
import { Copy, Check } from 'lucide-react';
import type { ModelRound, FlowItem, FlowTextItem, FlowToolItem, FlowThinkingItem } from '../../types/flow-chat';
import { FlowTextBlock } from '../FlowTextBlock';
import { FlowToolCard } from '../FlowToolCard';
Expand All @@ -17,7 +17,6 @@ import { isCollapsibleTool } from '../../tool-cards';
import { useFlowChatContext } from './FlowChatContext';
import { FlowChatStore } from '../../store/FlowChatStore';
import { taskCollapseStateManager } from '../../store/TaskCollapseStateManager';
import { notificationService } from '../../../shared/notification-system/services/NotificationService';
import { ExportImageButton } from './ExportImageButton';
import { Tooltip } from '@/component-library';
import { createLogger } from '@/shared/utils/logger';
Expand Down Expand Up @@ -62,12 +61,6 @@ export const ModelRoundItem = React.memo<ModelRoundItemProps>(
};
}, [copied]);

const handleFeedback = useCallback((_type: 'like' | 'dislike' | 'report') => {
notificationService.info(t('modelRound.feedbackThanks'), {
title: t('modelRound.feedbackDevVersion')
});
}, [t]);

// Keep insertion order; do not sort by timestamp.
// Subagent ordering is controlled by insertModelRoundItemAfterTool.
// FlowChatStore uses immutable updates, so rely on round.items reference.
Expand Down Expand Up @@ -354,35 +347,6 @@ export const ModelRoundItem = React.memo<ModelRoundItemProps>(
</Tooltip>

<ExportImageButton turnId={turnId} />

<div className="model-round-item__feedback-group">
<Tooltip content={t('modelRound.like')}>
<button
className="model-round-item__action-btn model-round-item__feedback-btn"
onClick={() => handleFeedback('like')}
>
<ThumbsUp size={14} />
</button>
</Tooltip>

<Tooltip content={t('modelRound.dislike')}>
<button
className="model-round-item__action-btn model-round-item__feedback-btn"
onClick={() => handleFeedback('dislike')}
>
<ThumbsDown size={14} />
</button>
</Tooltip>

<Tooltip content={t('modelRound.report')}>
<button
className="model-round-item__action-btn model-round-item__feedback-btn"
onClick={() => handleFeedback('report')}
>
<AlertTriangle size={14} />
</button>
</Tooltip>
</div>
</div>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions src/web-ui/src/locales/en-US/flow-chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@
"generateFailed": "Failed to generate image",
"fileNamePrefix": "BitFun-Chat",
"exportSuccess": "Image exported: {{filePath}}",
"exportSuccessPrefix": "Image exported: ",
"exportFailed": "Export failed, please try again",
"exporting": "Exporting...",
"exportToImage": "Export as Image"
Expand Down
1 change: 1 addition & 0 deletions src/web-ui/src/locales/zh-CN/flow-chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@
"generateFailed": "生成图片失败",
"fileNamePrefix": "BitFun对话",
"exportSuccess": "图片已导出:{{filePath}}",
"exportSuccessPrefix": "图片已导出:",
"exportFailed": "导出图片失败,请重试",
"exporting": "正在导出...",
"exportToImage": "导出为图片"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,11 +347,11 @@

&__item-badge {
flex-shrink: 0;
width: 6px;
height: 6px;
background: var(--color-error);
width: 10px;
height: 10px;
background: var(--color-error);
border-radius: 50%;
border: 1px solid $color-bg-primary;
border: none;
margin-left: auto;
margin-right: $size-gap-1;
align-self: center;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export const NotificationCenter: React.FC = () => {
)}
</div>
<div className="notification-center__active-task-message">
{isProgress && notification.progressText ? notification.progressText : notification.message}
{isProgress && notification.progressText ? notification.progressText : (notification.messageNode ?? notification.message)}
</div>

{isProgress && (() => {
Expand Down Expand Up @@ -280,7 +280,7 @@ export const NotificationCenter: React.FC = () => {
})()}
</div>
<div className="notification-center__item-message">
{(isProgress && notification.progressText) ? notification.progressText : notification.message}
{(isProgress && notification.progressText) ? notification.progressText : (notification.messageNode ?? notification.message)}
</div>

{isProgress && (() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@
word-break: break-word;
}

&__path-link {
display: inline;
margin: 0;
padding: 0;
border: none;
background: none;
font: inherit;
color: var(--color-primary);
text-decoration: underline;
text-align: left;
cursor: pointer;
word-break: break-all;

&:hover {
color: var(--color-accent, var(--color-primary));
}
}


&__actions {
display: flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface NotificationItemProps {
}

export const NotificationItem: React.FC<NotificationItemProps> = ({ notification }) => {
const { id, type, title, message, closable, actions } = notification;
const { id, type, title, message, messageNode, closable, actions } = notification;
const { t } = useI18n('common');


Expand Down Expand Up @@ -54,7 +54,7 @@ export const NotificationItem: React.FC<NotificationItemProps> = ({ notification

<div className="notification-item__content">
<div className="notification-item__title">{title}</div>
<div className="notification-item__message">{message}</div>
<div className="notification-item__message">{messageNode ?? message}</div>


{actions && actions.length > 0 && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class NotificationService {
variant: 'toast',
title: options?.title || this.getDefaultTitle(type),
message,
messageNode: options?.messageNode,
timestamp: Date.now(),
duration: options?.duration ?? state.config.defaultDuration,
closable: options?.closable ?? true,
Expand Down
7 changes: 7 additions & 0 deletions src/web-ui/src/shared/notification-system/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*
* Shared by the notification store, service, and UI components.
*/
import type { ReactNode } from 'react';

export type NotificationType = 'success' | 'error' | 'warning' | 'info';


Expand Down Expand Up @@ -43,6 +45,9 @@ export interface Notification {
title: string;

message: string;

/** When set, toast/history render this instead of plain `message` (keep `message` for search/plain fallback). */
messageNode?: ReactNode;

timestamp: number;

Expand Down Expand Up @@ -103,6 +108,8 @@ export interface ToastOptions {
closable?: boolean;

actions?: NotificationAction[];

messageNode?: ReactNode;

metadata?: Record<string, any>;
}
Expand Down
Loading