open ocr/tts/transcribe results in dual panels from file viewer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ type FileViewerProviderProps = {
|
||||
root?: string;
|
||||
content?: string;
|
||||
onOpenFile?: (filePath: string, root: string) => void;
|
||||
onReplaceView?: (viewPath: string, viewRoot: string, ephemeralPath: string, ephemeralRoot: string) => void;
|
||||
autoPlay?: boolean;
|
||||
children: ReactNode;
|
||||
};
|
||||
@@ -22,6 +23,7 @@ export const FileViewerProvider = ({
|
||||
root = 'home',
|
||||
content: directContent,
|
||||
onOpenFile,
|
||||
onReplaceView,
|
||||
autoPlay = false,
|
||||
children,
|
||||
}: FileViewerProviderProps) => {
|
||||
@@ -71,8 +73,9 @@ export const FileViewerProvider = ({
|
||||
const handleReadAloud = async () => {
|
||||
setTtsLoading(true);
|
||||
try {
|
||||
const { audioPath, audioRoot } = await files.tts(filePath);
|
||||
onOpenFile?.(audioPath, audioRoot);
|
||||
const { audioPath, audioRoot } = await files.tts(filePath, { saveNextTo: true });
|
||||
setRefreshSignal((n) => n + 1);
|
||||
onReplaceView?.(filePath, root, audioPath, audioRoot);
|
||||
} catch {
|
||||
toast.error('Failed to generate speech audio');
|
||||
} finally {
|
||||
@@ -83,8 +86,9 @@ export const FileViewerProvider = ({
|
||||
const handleOcr = async () => {
|
||||
setOcrLoading(true);
|
||||
try {
|
||||
const { ocrPath, ocrRoot } = await files.ocr(filePath);
|
||||
onOpenFile?.(ocrPath, ocrRoot);
|
||||
const { ocrPath, ocrRoot } = await files.ocr(filePath, { saveNextTo: true });
|
||||
setRefreshSignal((n) => n + 1);
|
||||
onReplaceView?.(filePath, root, ocrPath, ocrRoot);
|
||||
} catch {
|
||||
toast.error('Failed to extract text from image');
|
||||
} finally {
|
||||
@@ -95,8 +99,9 @@ export const FileViewerProvider = ({
|
||||
const handleTranscribe = async () => {
|
||||
setTranscribeLoading(true);
|
||||
try {
|
||||
const { transcriptionPath, transcriptionRoot } = await files.transcribe(filePath);
|
||||
onOpenFile?.(transcriptionPath, transcriptionRoot);
|
||||
const { transcriptionPath, transcriptionRoot } = await files.transcribe(filePath, { saveNextTo: true });
|
||||
setRefreshSignal((n) => n + 1);
|
||||
onReplaceView?.(filePath, root, transcriptionPath, transcriptionRoot);
|
||||
} catch {
|
||||
toast.error('Failed to transcribe audio');
|
||||
} finally {
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
import { type ReactNode, useCallback } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useSearchParams } from 'react-router';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { FileViewerProvider } from '../../apps/FileViewer';
|
||||
import { EmbeddableChat } from '../../apps/Chat/EmbeddableChat';
|
||||
|
||||
type SetSearchParams = ReturnType<typeof useSearchParams>[1];
|
||||
|
||||
const onReplaceView = (setSearchParams: SetSearchParams) =>
|
||||
(viewPath: string, viewRoot: string, ephemeralPath: string, ephemeralRoot: string) => {
|
||||
setSearchParams((prev) => {
|
||||
const next = new URLSearchParams(prev);
|
||||
next.set('view', viewPath);
|
||||
next.set('ephemeral', ephemeralPath);
|
||||
next.set('ephemeralRoot', ephemeralRoot);
|
||||
next.delete('ephemeral2');
|
||||
next.delete('ephemeral2Root');
|
||||
next.delete('ephemeral2Auto');
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
export function ViewerProvider({ children }: { children: ReactNode }) {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const viewPath = searchParams.get('view') ?? '';
|
||||
@@ -19,7 +36,7 @@ export function ViewerProvider({ children }: { children: ReactNode }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<FileViewerProvider filePath={viewPath} fileName={fileName} onOpenFile={onOpenFile}>
|
||||
<FileViewerProvider filePath={viewPath} fileName={fileName} onOpenFile={onOpenFile} onReplaceView={onReplaceView(setSearchParams)}>
|
||||
{children}
|
||||
</FileViewerProvider>
|
||||
);
|
||||
@@ -42,21 +59,21 @@ export function EphemeralProvider({ children }: { children: ReactNode }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<FileViewerProvider filePath={ephemeralPath} fileName={fileName} root={ephemeralRoot} onOpenFile={onOpenFile}>
|
||||
<FileViewerProvider filePath={ephemeralPath} fileName={fileName} root={ephemeralRoot} onOpenFile={onOpenFile} onReplaceView={onReplaceView(setSearchParams)}>
|
||||
{children}
|
||||
</FileViewerProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export function Ephemeral2Provider({ children }: { children: ReactNode }) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const ephemeral2Path = searchParams.get('ephemeral2') ?? '';
|
||||
const ephemeral2Root = searchParams.get('ephemeral2Root') ?? 'home';
|
||||
const ephemeral2Auto = searchParams.get('ephemeral2Auto') === '1';
|
||||
const fileName = ephemeral2Path.split('/').pop() ?? '';
|
||||
|
||||
return (
|
||||
<FileViewerProvider filePath={ephemeral2Path} fileName={fileName} root={ephemeral2Root} autoPlay={ephemeral2Auto}>
|
||||
<FileViewerProvider filePath={ephemeral2Path} fileName={fileName} root={ephemeral2Root} autoPlay={ephemeral2Auto} onReplaceView={onReplaceView(setSearchParams)}>
|
||||
{children}
|
||||
</FileViewerProvider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user