| import type { TextAnalysisAPI } from '../api/GLTR_API'; |
| import type { AnalyzeResponse } from '../api/GLTR_API'; |
| import { showDialog, createCombinedContent, showAlertDialog, showConfirmDialog } from '../ui/dialog'; |
| import { createRawSnapshot } from '../utils/tokenUtils'; |
| import { ServerStorage } from '../storage/demoStorage'; |
| import { DemoStorageController } from './demoStorageController'; |
| import type { IDemoStorage } from '../storage/demoStorage'; |
| import { |
| normalizeFolderPath, |
| composeDemoFullPath, |
| getDefaultDemoName, |
| buildFolderOptions |
| } from '../utils/demoPathUtils'; |
| import { tr, trf } from '../lang/i18n-lite'; |
|
|
| |
| |
| |
| const extractSavedFileName = (saveResult: any, defaultName: string): string => { |
| if (saveResult && typeof saveResult.file === 'string' && saveResult.file.trim()) { |
| return saveResult.file.trim(); |
| } |
| const trimmed = defaultName.trim(); |
| return trimmed.toLowerCase().endsWith('.json') ? trimmed : `${trimmed}.json`; |
| }; |
|
|
| |
| |
| |
| export const showDemoNameInput = ( |
| api: TextAnalysisAPI, |
| defaultName: string |
| ): Promise<{ name: string, path: string } | null> => { |
| const LAST_SAVE_PATH_KEY = 'lastSaveDemoPath'; |
| |
| return new Promise((resolve) => { |
| |
| api.list_all_folders() |
| .then((result: { folders: string[] }) => { |
| const folders = result.folders || []; |
| |
| |
| const lastSavePath = localStorage.getItem(LAST_SAVE_PATH_KEY); |
| |
| |
| const { options: selectOptions, defaultPath } = buildFolderOptions(folders, lastSavePath); |
|
|
| |
| showDialog({ |
| title: tr('Please enter demo name:'), |
| content: createCombinedContent( |
| tr('Demo name:'), |
| defaultName, |
| tr('Save directory:'), |
| selectOptions, |
| defaultPath |
| ), |
| onConfirm: (value: { input: string; select: string }) => { |
| if (value && value.input) { |
| |
| const selectedPath = value.select || '/'; |
| localStorage.setItem(LAST_SAVE_PATH_KEY, selectedPath); |
| resolve({ name: value.input, path: selectedPath }); |
| } else { |
| resolve(null); |
| } |
| }, |
| onCancel: () => { |
| resolve(null); |
| }, |
| cancelText: tr('Cancel') |
| }); |
| }) |
| .catch((error) => { |
| console.error('加载文件夹列表失败:', error); |
| const errorMessage = error instanceof Error ? error.message : String(error); |
| showAlertDialog(tr('Error'), trf('Failed to load folder list: {message}', { message: errorMessage })); |
| resolve(null); |
| }); |
| }); |
| }; |
|
|
| export type ServerDemoSaveOptions = { |
| api: TextAnalysisAPI; |
| currentData: AnalyzeResponse | null; |
| rawApiResponse: AnalyzeResponse | null; |
| textFieldValue: string; |
| enableDemo: boolean; |
| demoManager?: { |
| loadDemoByPath: (path: string) => Promise<boolean>; |
| highlightDemo: (path: string | null) => void; |
| refresh: () => Promise<void>; |
| } | null; |
| |
| |
| |
| presetSaveInfo?: { |
| name: string; |
| path?: string | null; |
| } | null; |
| |
| |
| |
| |
| |
| showSuccessToast?: boolean; |
| |
| |
| |
| |
| serverStorage?: IDemoStorage; |
| |
| |
| |
| currentFileName?: string | null; |
| onSaveStart: () => void; |
| onSaveSuccess: (name: string) => void; |
| |
| |
| |
| onSaveComplete?: () => void; |
| onSaveError: (error: Error) => void; |
| setGlobalLoading: (loading: boolean) => void; |
| showToast: (message: string, type: 'success' | 'error') => void; |
| }; |
|
|
| |
| |
| |
| export const handleServerDemoSave = async (options: ServerDemoSaveOptions): Promise<void> => { |
| const { |
| api, |
| currentData, |
| rawApiResponse, |
| textFieldValue, |
| enableDemo, |
| demoManager, |
| presetSaveInfo = null, |
| showSuccessToast = true, |
| serverStorage: providedStorage, |
| currentFileName = null, |
| onSaveStart, |
| onSaveSuccess, |
| onSaveComplete, |
| onSaveError, |
| setGlobalLoading, |
| showToast |
| } = options; |
|
|
| if (!currentData || !rawApiResponse) { |
| showAlertDialog(tr('Info'), tr('No data to save, please analyze text first')); |
| return; |
| } |
|
|
| const LAST_SAVE_PATH_KEY = 'lastSaveDemoPath'; |
|
|
| |
| let result: { name: string; path: string } | null = null; |
| if (presetSaveInfo && presetSaveInfo.name && presetSaveInfo.name.trim()) { |
| const normalizedPath = normalizeFolderPath(presetSaveInfo.path); |
| result = { name: presetSaveInfo.name.trim(), path: normalizedPath }; |
| |
| if (normalizedPath) { |
| localStorage.setItem(LAST_SAVE_PATH_KEY, normalizedPath); |
| } |
| } else { |
| const defaultName = getDefaultDemoName(currentData, textFieldValue, currentFileName); |
| result = await showDemoNameInput(api, defaultName); |
| } |
|
|
| if (!result || !result.name || result.name.trim() === '') { |
| return; |
| } |
|
|
| |
| const savePath = result.path || '/'; |
| const storage = providedStorage || new ServerStorage(api); |
| |
| |
| const serverController = new DemoStorageController( |
| storage, |
| { |
| onStart: onSaveStart, |
| onSuccess: async (name, saveResult) => { |
| |
| onSaveSuccess(name); |
| |
| |
| |
| |
| |
| if (enableDemo && demoManager && saveResult) { |
| const savedFileName = extractSavedFileName(saveResult, name); |
| const highlightPath = composeDemoFullPath(savePath, savedFileName); |
| |
| if (highlightPath) { |
| |
| try { |
| const success = await demoManager.loadDemoByPath(highlightPath); |
| if (success) { |
| demoManager.highlightDemo(highlightPath); |
| } |
| } catch (err) { |
| console.error('自动加载保存的demo失败:', err); |
| } finally { |
| |
| await demoManager.refresh().catch(refreshErr => { |
| console.error('刷新demo列表失败:', refreshErr); |
| }); |
| } |
| } else { |
| |
| await demoManager.refresh().catch(err => { |
| console.error('刷新demo列表失败:', err); |
| }); |
| } |
| } |
| onSaveComplete?.(); |
| }, |
| onError: onSaveError, |
| setLoading: setGlobalLoading, |
| showToast, |
| showSuccessToast |
| } |
| ); |
| |
| |
| await serverController.save(rawApiResponse, { |
| name: result.name.trim(), |
| path: savePath |
| }); |
| }; |
|
|
|
|