still working on crashes
This commit is contained in:
@ -17,8 +17,10 @@ from routers import transcribe, export, ai, captions, audio
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Dev log file — frontend forwards console.error/warn here so the agent can read it
|
||||
DEV_LOG_PATH = Path(__file__).parent.parent / "webview.log"
|
||||
# Dev log file — keep outside workspace to avoid dev watcher reload loops.
|
||||
DEV_LOG_PATH = Path(
|
||||
os.environ.get("TALKEDIT_DEV_LOG_PATH", str(Path(tempfile.gettempdir()) / "talkedit-webview.log"))
|
||||
)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@ -205,6 +207,7 @@ async def dev_log(request: Request):
|
||||
if args:
|
||||
line += " " + " ".join(args)
|
||||
line += "\n"
|
||||
DEV_LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(DEV_LOG_PATH, "a") as f:
|
||||
f.write(line)
|
||||
return {"ok": True}
|
||||
return {"ok": True, "path": str(DEV_LOG_PATH)}
|
||||
|
||||
8
close
8
close
@ -3,6 +3,7 @@
|
||||
|
||||
KILLED_ANY=0
|
||||
BACKEND_PORT="${BACKEND_PORT:-8000}"
|
||||
FRONTEND_PORT="${FRONTEND_PORT:-5173}"
|
||||
|
||||
kill_port() {
|
||||
local port=$1
|
||||
@ -10,7 +11,7 @@ kill_port() {
|
||||
local pids
|
||||
pids=$(lsof -ti tcp:"$port" 2>/dev/null)
|
||||
if [[ -n "$pids" ]]; then
|
||||
echo "Stopping $name backend (port $port, PID $pids)..."
|
||||
echo "Stopping $name (port $port, PID $pids)..."
|
||||
kill "$pids" 2>/dev/null
|
||||
KILLED_ANY=1
|
||||
fi
|
||||
@ -31,8 +32,9 @@ kill_pattern() {
|
||||
# --- TalkEdit (Tauri) ---
|
||||
kill_port "$BACKEND_PORT" "TalkEdit"
|
||||
kill_pattern "tauri.*TalkEdit\|TalkEdit.*tauri\|cargo.*tauri dev\|/TalkEdit/target/debug" "TalkEdit (Tauri dev)"
|
||||
# Vite dev server for TalkEdit (port 5173)
|
||||
kill_pattern "vite.*5173\|rsbuild.*5173" "TalkEdit frontend dev server"
|
||||
# Frontend dev server: first kill by listening port, then by known process patterns.
|
||||
kill_port "$FRONTEND_PORT" "TalkEdit frontend"
|
||||
kill_pattern "vite\|rsbuild\|npm.*run dev\|pnpm.*dev\|yarn.*dev" "TalkEdit frontend dev server"
|
||||
|
||||
# --- Orphaned uvicorn workers for TalkEdit ---
|
||||
kill_pattern "uvicorn.*main:app.*--port ${BACKEND_PORT}" "leftover uvicorn workers (TalkEdit)"
|
||||
|
||||
@ -49,6 +49,7 @@ export default function App() {
|
||||
const [cutMode, setCutMode] = useState(false);
|
||||
const [muteMode, setMuteMode] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const lastVideoPathRef = useRef<string | null>(null);
|
||||
|
||||
useKeyboardShortcuts();
|
||||
|
||||
@ -74,13 +75,44 @@ export default function App() {
|
||||
// The backend URL is fixed at 127.0.0.1:8000 so we rely on the store default.
|
||||
}, [setBackendUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!import.meta.env.DEV) return;
|
||||
const previousVideoPath = lastVideoPathRef.current;
|
||||
if (previousVideoPath !== videoPath) {
|
||||
console.log('[app-state] videoPath transition', {
|
||||
from: previousVideoPath,
|
||||
to: videoPath,
|
||||
wordCount: words.length,
|
||||
isTranscribing,
|
||||
});
|
||||
if (previousVideoPath && !videoPath) {
|
||||
console.warn('[app-state] videoPath cleared and UI will show welcome screen', {
|
||||
previousVideoPath,
|
||||
wordCount: words.length,
|
||||
isTranscribing,
|
||||
});
|
||||
}
|
||||
lastVideoPathRef.current = videoPath;
|
||||
}
|
||||
}, [videoPath, words.length, isTranscribing]);
|
||||
|
||||
const handleLoadProject = async () => {
|
||||
if (!IS_DESKTOP) return;
|
||||
try {
|
||||
if (import.meta.env.DEV) console.log('[app-action] loadProject:dialogOpen');
|
||||
const projectPath = await window.desktopAPI!.openProject();
|
||||
if (import.meta.env.DEV) console.log('[app-action] loadProject:dialogResult', { projectPath });
|
||||
if (!projectPath) return;
|
||||
const content = await window.desktopAPI!.readFile(projectPath);
|
||||
const data = JSON.parse(content);
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('[app-action] loadProject:parsed', {
|
||||
projectPath,
|
||||
videoPath: data?.videoPath,
|
||||
words: Array.isArray(data?.words) ? data.words.length : null,
|
||||
segments: Array.isArray(data?.segments) ? data.segments.length : null,
|
||||
});
|
||||
}
|
||||
useEditorStore.getState().loadProject(data);
|
||||
} catch (err) {
|
||||
console.error('Failed to load project:', err);
|
||||
@ -104,8 +136,11 @@ export default function App() {
|
||||
|
||||
const handleOpenFile = async () => {
|
||||
if (IS_DESKTOP) {
|
||||
if (import.meta.env.DEV) console.log('[app-action] openFile:dialogOpen');
|
||||
const path = await window.desktopAPI!.openFile();
|
||||
if (import.meta.env.DEV) console.log('[app-action] openFile:dialogResult', { path });
|
||||
if (path) {
|
||||
if (import.meta.env.DEV) console.log('[app-action] openFile:loadVideo', { path });
|
||||
loadVideo(path);
|
||||
await transcribeVideo(path);
|
||||
}
|
||||
@ -113,6 +148,7 @@ export default function App() {
|
||||
// Browser: use the manual path input
|
||||
const path = manualPath.trim();
|
||||
if (path) {
|
||||
if (import.meta.env.DEV) console.log('[app-action] openFile:webManualPath', { path });
|
||||
loadVideo(path);
|
||||
await transcribeVideo(path);
|
||||
}
|
||||
@ -123,11 +159,13 @@ export default function App() {
|
||||
e.preventDefault();
|
||||
const path = manualPath.trim();
|
||||
if (!path) return;
|
||||
if (import.meta.env.DEV) console.log('[app-action] manualSubmit:loadVideo', { path });
|
||||
loadVideo(path);
|
||||
await transcribeVideo(path);
|
||||
};
|
||||
|
||||
const transcribeVideo = async (path: string) => {
|
||||
if (import.meta.env.DEV) console.log('[app-action] transcribe:start', { path, whisperModel });
|
||||
setTranscribing(true, 0, 'Checking model...');
|
||||
try {
|
||||
if (!window.desktopAPI?.transcribe) {
|
||||
@ -154,15 +192,25 @@ export default function App() {
|
||||
const modelLabel = MODEL_SIZES[whisperModel] ?? 'unknown size';
|
||||
setTranscribing(true, 5, `Downloading ${whisperModel} model (${modelLabel})...`);
|
||||
await window.desktopAPI.ensureModel(whisperModel);
|
||||
if (import.meta.env.DEV) console.log('[app-action] transcribe:modelReady', { whisperModel });
|
||||
|
||||
// Step 2: run transcription
|
||||
setTranscribing(true, 20, 'Transcribing audio...');
|
||||
const data = await window.desktopAPI.transcribe(path, whisperModel);
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('[app-action] transcribe:result', {
|
||||
path,
|
||||
words: Array.isArray(data?.words) ? data.words.length : null,
|
||||
segments: Array.isArray(data?.segments) ? data.segments.length : null,
|
||||
language: data?.language,
|
||||
});
|
||||
}
|
||||
setTranscription(data);
|
||||
} catch (err) {
|
||||
console.error('Transcription error:', err);
|
||||
alert(`Transcription failed. Check the console for details.\n\n${err}`);
|
||||
} finally {
|
||||
if (import.meta.env.DEV) console.log('[app-action] transcribe:finish', { path });
|
||||
setTranscribing(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,13 +1,29 @@
|
||||
/**
|
||||
* Dev-only console interceptor.
|
||||
* Forwards console.error / console.warn to the backend /dev/log endpoint,
|
||||
* which appends them to webview.log so the agent can read it.
|
||||
* which appends them to a backend-managed dev log file.
|
||||
*/
|
||||
if (import.meta.env.DEV) {
|
||||
const BACKEND = 'http://127.0.0.1:8000';
|
||||
|
||||
type ConsoleFn = (...args: unknown[]) => void;
|
||||
|
||||
const serialize = (value: unknown): string => {
|
||||
if (typeof value === 'string') return value;
|
||||
if (value instanceof Error) {
|
||||
return JSON.stringify({
|
||||
name: value.name,
|
||||
message: value.message,
|
||||
stack: value.stack,
|
||||
});
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(value);
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
};
|
||||
|
||||
const forward = (level: string, orig: ConsoleFn): ConsoleFn =>
|
||||
(...args: unknown[]) => {
|
||||
orig(...args);
|
||||
@ -15,7 +31,7 @@ if (import.meta.env.DEV) {
|
||||
fetch(`${BACKEND}/dev/log`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ level, message: String(first ?? ''), args: rest.map(String) }),
|
||||
body: JSON.stringify({ level, message: serialize(first ?? ''), args: rest.map(serialize) }),
|
||||
}).catch(() => {/* backend not running yet */});
|
||||
};
|
||||
|
||||
|
||||
@ -28,31 +28,48 @@ const EXPORT_FILTERS = [
|
||||
const BACKEND_PORT = import.meta.env.VITE_BACKEND_PORT || '8000';
|
||||
const BACKEND_URL = `http://127.0.0.1:${BACKEND_PORT}`;
|
||||
|
||||
const debugBridge = (event: string, details?: Record<string, unknown>) => {
|
||||
if (!import.meta.env.DEV) return;
|
||||
console.log('[tauri-bridge]', event, details ?? {});
|
||||
};
|
||||
|
||||
window.desktopAPI = {
|
||||
openFile: async (_options?: Record<string, unknown>): Promise<string | null> => {
|
||||
debugBridge('openFile:dialogOpen');
|
||||
const result = await open({
|
||||
multiple: false,
|
||||
filters: VIDEO_FILTERS,
|
||||
});
|
||||
return typeof result === 'string' ? result : null;
|
||||
const path = typeof result === 'string' ? result : null;
|
||||
debugBridge('openFile:dialogResult', { path });
|
||||
return path;
|
||||
},
|
||||
|
||||
saveFile: async (_options?: Record<string, unknown>): Promise<string | null> => {
|
||||
debugBridge('saveFile:dialogOpen');
|
||||
const result = await save({ filters: EXPORT_FILTERS });
|
||||
return result ?? null;
|
||||
const path = result ?? null;
|
||||
debugBridge('saveFile:dialogResult', { path });
|
||||
return path;
|
||||
},
|
||||
|
||||
openProject: async (): Promise<string | null> => {
|
||||
debugBridge('openProject:dialogOpen');
|
||||
const result = await open({
|
||||
multiple: false,
|
||||
filters: PROJECT_FILTERS,
|
||||
});
|
||||
return typeof result === 'string' ? result : null;
|
||||
const path = typeof result === 'string' ? result : null;
|
||||
debugBridge('openProject:dialogResult', { path });
|
||||
return path;
|
||||
},
|
||||
|
||||
saveProject: async (): Promise<string | null> => {
|
||||
debugBridge('saveProject:dialogOpen');
|
||||
const result = await save({ filters: PROJECT_FILTERS });
|
||||
return result ?? null;
|
||||
const path = result ?? null;
|
||||
debugBridge('saveProject:dialogResult', { path });
|
||||
return path;
|
||||
},
|
||||
|
||||
getBackendUrl: (): Promise<string> => {
|
||||
@ -77,10 +94,12 @@ window.desktopAPI = {
|
||||
},
|
||||
|
||||
readFile: (path: string): Promise<string> => {
|
||||
debugBridge('readFile', { path });
|
||||
return readTextFile(path);
|
||||
},
|
||||
|
||||
writeFile: async (path: string, content: string): Promise<boolean> => {
|
||||
debugBridge('writeFile', { path, size: content.length });
|
||||
await writeTextFile(path, content);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
// Forward console.error/warn/log to backend in dev mode so we can tail webview.log
|
||||
// Forward console.error/warn/log to backend in dev mode so we can tail the backend dev log.
|
||||
import './lib/dev-logger';
|
||||
// Must be imported before App so window.desktopAPI is patched before any component runs.
|
||||
import './lib/tauri-bridge';
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { temporal } from 'zundo';
|
||||
import type { Word, Segment, DeletedRange, CutRange, MuteRange, TranscriptionResult, ProjectFile } from '../types/project';
|
||||
|
||||
@ -84,12 +85,21 @@ const initialState: EditorState = {
|
||||
|
||||
let nextRangeId = 1;
|
||||
|
||||
const debugEditorStore = (event: string, details?: Record<string, unknown>) => {
|
||||
if (!import.meta.env.DEV) return;
|
||||
console.log('[editor-store]', event, details ?? {});
|
||||
};
|
||||
|
||||
export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
temporal(
|
||||
(set, get) => ({
|
||||
persist(
|
||||
temporal(
|
||||
(set, get) => ({
|
||||
...initialState,
|
||||
|
||||
setBackendUrl: (url) => set({ backendUrl: url }),
|
||||
setBackendUrl: (url) => {
|
||||
debugEditorStore('setBackendUrl', { url });
|
||||
set({ backendUrl: url });
|
||||
},
|
||||
|
||||
setExportedAudioPath: (path) => set({ exportedAudioPath: path }),
|
||||
|
||||
@ -123,12 +133,18 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
: `${backend}/file?path=${encodeURIComponent(filePath)}`;
|
||||
};
|
||||
const url = buildMediaUrl(path);
|
||||
debugEditorStore('loadVideo:start', {
|
||||
path,
|
||||
backend,
|
||||
previousVideoPath: get().videoPath,
|
||||
});
|
||||
set({
|
||||
...initialState,
|
||||
backendUrl: backend,
|
||||
videoPath: path,
|
||||
videoUrl: url,
|
||||
});
|
||||
debugEditorStore('loadVideo:done', { path, url });
|
||||
},
|
||||
|
||||
setTranscription: (result) => {
|
||||
@ -138,6 +154,12 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
globalIdx += seg.words.length;
|
||||
return annotated;
|
||||
});
|
||||
debugEditorStore('setTranscription', {
|
||||
wordCount: result.words?.length ?? 0,
|
||||
segmentCount: result.segments?.length ?? 0,
|
||||
language: result.language,
|
||||
currentVideoPath: get().videoPath,
|
||||
});
|
||||
set({
|
||||
words: result.words,
|
||||
segments: annotatedSegments,
|
||||
@ -310,10 +332,18 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
|
||||
loadProject: (data) => {
|
||||
const backend = get().backendUrl;
|
||||
const isWav = data.videoPath.toLowerCase().endsWith('.wav');
|
||||
const resolvedVideoPath = typeof data?.videoPath === 'string' ? data.videoPath : null;
|
||||
if (!resolvedVideoPath) {
|
||||
debugEditorStore('loadProject:invalidVideoPath', {
|
||||
videoPathType: typeof data?.videoPath,
|
||||
hasKeys: data && typeof data === 'object' ? Object.keys(data as Record<string, unknown>) : [],
|
||||
});
|
||||
throw new Error('Project file missing required videoPath string');
|
||||
}
|
||||
const isWav = resolvedVideoPath.toLowerCase().endsWith('.wav');
|
||||
const url = isWav
|
||||
? `${backend}/file?path=${encodeURIComponent(data.videoPath)}&format=mp3`
|
||||
: `${backend}/file?path=${encodeURIComponent(data.videoPath)}`;
|
||||
? `${backend}/file?path=${encodeURIComponent(resolvedVideoPath)}&format=mp3`
|
||||
: `${backend}/file?path=${encodeURIComponent(resolvedVideoPath)}`;
|
||||
|
||||
let globalIdx = 0;
|
||||
const annotatedSegments = (data.segments || []).map((seg: Segment) => {
|
||||
@ -322,10 +352,20 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
return annotated;
|
||||
});
|
||||
|
||||
debugEditorStore('loadProject:start', {
|
||||
videoPath: resolvedVideoPath,
|
||||
words: Array.isArray(data?.words) ? data.words.length : null,
|
||||
segments: Array.isArray(data?.segments) ? data.segments.length : null,
|
||||
cutRanges: Array.isArray(data?.cutRanges) ? data.cutRanges.length : null,
|
||||
muteRanges: Array.isArray(data?.muteRanges) ? data.muteRanges.length : null,
|
||||
deletedRanges: Array.isArray(data?.deletedRanges) ? data.deletedRanges.length : null,
|
||||
previousVideoPath: get().videoPath,
|
||||
});
|
||||
|
||||
set({
|
||||
...initialState,
|
||||
backendUrl: backend,
|
||||
videoPath: data.videoPath,
|
||||
videoPath: resolvedVideoPath,
|
||||
videoUrl: url,
|
||||
words: data.words || [],
|
||||
segments: annotatedSegments,
|
||||
@ -335,9 +375,21 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
language: data.language || '',
|
||||
exportedAudioPath: data.exportedAudioPath ?? null,
|
||||
});
|
||||
|
||||
debugEditorStore('loadProject:done', {
|
||||
videoPath: resolvedVideoPath,
|
||||
url,
|
||||
});
|
||||
},
|
||||
|
||||
reset: () => set(initialState),
|
||||
reset: () => {
|
||||
const stack = new Error().stack?.split('\n').slice(1, 6).join(' | ');
|
||||
debugEditorStore('reset', {
|
||||
previousVideoPath: get().videoPath,
|
||||
stack,
|
||||
});
|
||||
set(initialState);
|
||||
},
|
||||
|
||||
pauseUndo: () => {
|
||||
// Access the temporal store through the useEditorStore
|
||||
@ -354,7 +406,39 @@ export const useEditorStore = create<EditorState & EditorActions>()(
|
||||
temporalStore.getState().resume();
|
||||
}
|
||||
},
|
||||
}),
|
||||
{ limit: 100 },
|
||||
}),
|
||||
{ limit: 100 },
|
||||
),
|
||||
{
|
||||
name: 'talkedit-editor-session',
|
||||
version: 1,
|
||||
partialize: (state) => ({
|
||||
videoPath: state.videoPath,
|
||||
videoUrl: state.videoUrl,
|
||||
exportedAudioPath: state.exportedAudioPath,
|
||||
words: state.words,
|
||||
segments: state.segments,
|
||||
deletedRanges: state.deletedRanges,
|
||||
cutRanges: state.cutRanges,
|
||||
muteRanges: state.muteRanges,
|
||||
language: state.language,
|
||||
backendUrl: state.backendUrl,
|
||||
currentTime: state.currentTime,
|
||||
duration: state.duration,
|
||||
}),
|
||||
onRehydrateStorage: () => (state, error) => {
|
||||
if (error) {
|
||||
debugEditorStore('persist:rehydrate:error', { error: String(error) });
|
||||
return;
|
||||
}
|
||||
debugEditorStore('persist:rehydrate:done', {
|
||||
videoPath: state?.videoPath ?? null,
|
||||
words: state?.words?.length ?? 0,
|
||||
segments: state?.segments?.length ?? 0,
|
||||
cutRanges: state?.cutRanges?.length ?? 0,
|
||||
muteRanges: state?.muteRanges?.length ?? 0,
|
||||
});
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
15
open
15
open
@ -5,6 +5,7 @@ PROJECT_DIR="$PWD"
|
||||
|
||||
export BACKEND_PORT="${BACKEND_PORT:-8000}"
|
||||
export VITE_BACKEND_PORT="${VITE_BACKEND_PORT:-$BACKEND_PORT}"
|
||||
export TALKEDIT_DEV_LOG_PATH="${TALKEDIT_DEV_LOG_PATH:-/tmp/talkedit-webview.log}"
|
||||
BACKEND_URL="http://127.0.0.1:${BACKEND_PORT}/health"
|
||||
FRONTEND_URL="http://127.0.0.1:5173"
|
||||
|
||||
@ -18,20 +19,20 @@ else
|
||||
|
||||
# Try common terminal emulators in order
|
||||
if command -v ghostty &>/dev/null; then
|
||||
ghostty -e bash -c "cd '${BACKEND_DIR}' && '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
ghostty -e bash -c "cd '${BACKEND_DIR}' && TALKEDIT_DEV_LOG_PATH='${TALKEDIT_DEV_LOG_PATH}' '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
elif command -v kitty &>/dev/null; then
|
||||
kitty --title "TalkEdit Backend" -- bash -c "cd '${BACKEND_DIR}' && '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
kitty --title "TalkEdit Backend" -- bash -c "cd '${BACKEND_DIR}' && TALKEDIT_DEV_LOG_PATH='${TALKEDIT_DEV_LOG_PATH}' '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
elif command -v alacritty &>/dev/null; then
|
||||
alacritty --title "TalkEdit Backend" -e bash -c "cd '${BACKEND_DIR}' && '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
alacritty --title "TalkEdit Backend" -e bash -c "cd '${BACKEND_DIR}' && TALKEDIT_DEV_LOG_PATH='${TALKEDIT_DEV_LOG_PATH}' '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
elif command -v konsole &>/dev/null; then
|
||||
konsole --new-tab -e bash -c "cd '${BACKEND_DIR}' && '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
konsole --new-tab -e bash -c "cd '${BACKEND_DIR}' && TALKEDIT_DEV_LOG_PATH='${TALKEDIT_DEV_LOG_PATH}' '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
elif command -v gnome-terminal &>/dev/null; then
|
||||
gnome-terminal --title "TalkEdit Backend" -- bash -c "cd '${BACKEND_DIR}' && '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
gnome-terminal --title "TalkEdit Backend" -- bash -c "cd '${BACKEND_DIR}' && TALKEDIT_DEV_LOG_PATH='${TALKEDIT_DEV_LOG_PATH}' '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
elif command -v xterm &>/dev/null; then
|
||||
xterm -T "TalkEdit Backend" -e bash -c "cd '${BACKEND_DIR}' && '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
xterm -T "TalkEdit Backend" -e bash -c "cd '${BACKEND_DIR}' && TALKEDIT_DEV_LOG_PATH='${TALKEDIT_DEV_LOG_PATH}' '${VENV_PYTHON}' -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT}; exec bash" &
|
||||
else
|
||||
echo "No supported terminal emulator found. Starting backend in background..."
|
||||
cd "${BACKEND_DIR}" && "${VENV_PYTHON}" -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT} &
|
||||
cd "${BACKEND_DIR}" && TALKEDIT_DEV_LOG_PATH="${TALKEDIT_DEV_LOG_PATH}" "${VENV_PYTHON}" -m uvicorn main:app --host 127.0.0.1 --port ${BACKEND_PORT} &
|
||||
fi
|
||||
|
||||
# Wait up to 15s for backend to become ready
|
||||
|
||||
Reference in New Issue
Block a user