← back to dennisrongo__cut-clean

Function bodies 405 total

All specs Real LLM only Function bodies
useTauriCommand function · typescript · L58-L95 (38 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriCommand.ts
export function useTauriCommand<T = unknown>(
  options: UseCommandOptions<T>
): CommandState<T> {
  const { command, args: defaultArgs, onSuccess, onError } = options;
  const [data, setData] = useState<T | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const execute = useCallback(
    async (args?: Record<string, unknown>) => {
      setIsLoading(true);
      setError(null);

      try {
        const result = await invoke<T>(command, args || defaultArgs);
        setData(result);
        onSuccess?.(result);
        return result;
      } catch (err) {
        const errorMsg = String(err);
        setError(errorMsg);
        onError?.(errorMsg);
        throw err;
      } finally {
        setIsLoading(false);
      }
    },
    [command, defaultArgs, onSuccess, onError]
  );

  const reset = useCallback(() => {
    setData(null);
    setIsLoading(false);
    setError(null);
  }, []);

  return { data, 
useTauriMutation function · typescript · L123-L152 (30 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriCommand.ts
export function useTauriMutation<T = unknown>(
  options: UseMutationOptions<T>
): MutationState<T> {
  const { command, onSuccess, onError } = options;
  const [isMutating, setIsMutating] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const execute = useCallback(
    async (args: Record<string, unknown>) => {
      setIsMutating(true);
      setError(null);

      try {
        const result = await invoke<T>(command, args);
        onSuccess?.(result);
        return result;
      } catch (err) {
        const errorMsg = String(err);
        setError(errorMsg);
        onError?.(errorMsg);
        throw err;
      } finally {
        setIsMutating(false);
      }
    },
    [command, onSuccess, onError]
  );

  return { execute, isMutating, error };
}
useTauriBatch function · typescript · L185-L213 (29 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriCommand.ts
export function useTauriBatch<T = unknown>(
  options: BatchOptions<T>
): BatchState {
  const { command, onSuccess, onError } = options;
  const [isBatching, setIsBatching] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const execute = useCallback(
    async (items: T[]) => {
      setIsBatching(true);
      setError(null);

      try {
        await invoke(command, { items });
        onSuccess?.();
      } catch (err) {
        const errorMsg = String(err);
        setError(errorMsg);
        onError?.(errorMsg);
        throw err;
      } finally {
        setIsBatching(false);
      }
    },
    [command, onSuccess, onError]
  );

  return { execute, isBatching, error };
}
useTauriEvent function · typescript · L50-L95 (46 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriEvent.ts
export function useTauriEvent<T = unknown>(
  eventName: string,
  options: UseEventOptions<T> = {}
): EventState<T> {
  const { defaultValue, onEvent, enabled = true } = options;
  const [data, setData] = useState<T | null>(defaultValue ?? null);
  const [isListening, setIsListening] = useState(false);

  const startListening = useCallback(() => {
    let unlisten: UnlistenFn | null = null;

    listen<T>(eventName, (event) => {
      setData(event.payload);
      onEvent?.(event.payload);
    })
      .then((fn) => {
        unlisten = fn;
        setIsListening(true);
      })
      .catch((err) => {
        console.error(`Failed to listen to ${eventName}:`, err);
      });

    return () => {
      unlisten?.then((fn) => fn());
      setIsListening(false);
    };
  }, [eventName, onEvent]);

  useEffect(() => {
    if (!enabled) return;

    const cleanup = startListening();
    return () => cleanup?.();
  }, [enabled, startListening]);

  const toggle = useCallback((enabled: boole
useTauriEvents function · typescript · L113-L144 (32 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriEvent.ts
export function useTauriEvents<T = unknown>(
  handlers: EventHandlers<T>,
  enabled = true
): boolean {
  const [isListening, setIsListening] = useState(false);

  useEffect(() => {
    if (!enabled) return;

    const unlisteners: Promise<UnlistenFn>[] = [];

    Object.entries(handlers).forEach(([eventName, handler]) => {
      const promise = listen<T>(eventName, (event) => {
        handler(event.payload);
      });
      unlisteners.push(promise);
    });

    Promise.all(unlisteners).then(() => {
      setIsListening(true);
    });

    return () => {
      Promise.all(unlisteners).then((fns) => {
        fns.forEach((fn) => fn());
      });
      setIsListening(false);
    };
  }, [handlers, enabled]);

  return isListening;
}
useCommandWithEvents function · typescript · L191-L243 (53 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriEvent.ts
export function useCommandWithEvents<TData = unknown, TProgress = number>(
  options: CommandWithEventOptions<TData, TProgress>
) {
  const {
    command,
    progressEvent,
    completeEvent,
    errorEvent,
    onProgress,
    onComplete,
    onError,
  } = options;

  const [isRunning, setIsRunning] = useState(false);

  // Listen to progress events
  useTauriEvent<TProgress>(progressEvent, {
    onEvent: (progress) => {
      onProgress?.(progress);
    },
  });

  // Listen to complete events
  useTauriEvent<TData>(completeEvent || "", {
    onEvent: (data) => {
      setIsRunning(false);
      onComplete?.(data);
    },
  });

  // Listen to error events
  useTauriEvent<string>(errorEvent || "", {
    onEvent: (error) => {
      setIsRunning(false);
      onError?.(error);
    },
  });

  const execute = useCallback(
    async (args?: Record<string, unknown>) => {
      setIsRunning(true);
      try {
        await invoke(command, args);
      } catch (err) {
        setIsRunning
useTauriLoad function · typescript · L48-L86 (39 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriLoad.ts
export function useTauriLoad<T = unknown>(
  options: UseLoadOptions<T>
): LoadState<T> {
  const {
    loadOnMount = true,
    defaultValue,
    onSuccess,
    onError,
    ...commandOptions
  } = options;

  const { data, isLoading, error, execute } = useTauriCommand<T>({
    ...commandOptions,
    onSuccess,
    onError,
  });

  const [isInitialLoadDone, setIsInitialLoadDone] = useState(!loadOnMount);

  useEffect(() => {
    if (loadOnMount && !isInitialLoadDone) {
      execute()
        .then(() => setIsInitialLoadDone(true))
        .catch(() => setIsInitialLoadDone(true));
    }
  }, [loadOnMount, execute, isInitialLoadDone]);

  const reload = useCallback(async () => {
    await execute();
  }, [execute]);

  return {
    data: (data ?? defaultValue) as T,
    isLoading,
    error,
    reload,
    isInitialLoadDone,
  };
}
Repobility analyzer · published findings · https://repobility.com
useTauriAutoRefresh function · typescript · L108-L134 (27 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriLoad.ts
export function useTauriAutoRefresh<T = unknown>(
  options: UseAutoRefreshOptions<T>
): LoadState<T> {
  const { eventName, ...loadOptions } = options;
  const { data, isLoading, error, reload, isInitialLoadDone } =
    useTauriLoad<T>(loadOptions);

  useEffect(() => {
    if (!eventName) return;

    const unlisten = listen(eventName, () => {
      reload();
    });

    return () => {
      unlisten.then((fn) => fn());
    };
  }, [eventName, reload]);

  return {
    data: (data ?? loadOptions.defaultValue) as T,
    isLoading,
    error,
    reload,
    isInitialLoadDone,
  };
}
useTauriMutation function · typescript · L46-L85 (40 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriMutation.ts
export function useTauriMutation<TData = unknown, TVariables = void>(
  options: UseMutationOptions<TData, TVariables>
): MutationState<TData, TVariables> {
  const { command, onSuccess, onError, onSettled } = options;
  const [isMutating, setIsMutating] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [data, setData] = useState<TData | null>(null);

  const execute = useCallback(
    async (variables: TVariables): Promise<TData> => {
      setIsMutating(true);
      setError(null);

      try {
        const result = await invoke<TData>(command, variables);
        setData(result);
        onSuccess?.(result, variables);
        onSettled?.(result, null, variables);
        return result;
      } catch (err) {
        const errorMsg = String(err);
        setError(errorMsg);
        onError?.(errorMsg, variables);
        onSettled?.(undefined, errorMsg, variables);
        throw err;
      } finally {
        setIsMutating(false);
      }
    },
 
useTauriBatchMutation function · typescript · L115-L154 (40 LOC)
.claude/skills/tauri-desktop-app/hooks/useTauriMutation.ts
export function useTauriBatchMutation<TData = unknown, TItem = unknown>(
  options: UseBatchMutationOptions<TData, TItem>
): BatchMutationState<TData, TItem> {
  const { command, onProgress, onSuccess, onError } = options;
  const [isMutating, setIsMutating] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [progress, setProgress] = useState({ completed: 0, total: 0 });

  const execute = useCallback(
    async (items: TItem[]): Promise<TData[]> => {
      setIsMutating(true);
      setError(null);
      setProgress({ completed: 0, total: items.length });

      try {
        const results: TData[] = [];

        for (const item of items) {
          const result = await invoke<TData>(command, { item });
          results.push(result);
          setProgress((prev) => ({ ...prev, completed: prev.completed + 1 }));
          onProgress?.(results.length, items.length);
        }

        onSuccess?.(results);
        return results;
      } catch (err) 
logError function · typescript · L35-L45 (11 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
async function logError(message: string, context?: string): Promise<void> {
  try {
    await invoke("log_error_command", {
      message,
      context: context || null,
      stack: new Error().stack,
    });
  } catch {
    // Silently fail if logging fails
  }
}
showToastr function · typescript · L52-L68 (17 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export async function showToastr({
  type,
  title,
  message,
  duration,
  context,
}: ToastrOptions): Promise<void> {
  // Log errors to backend
  if (type === "error") {
    const fullMessage = message ? `${title}: ${message}` : title;
    await logError(fullMessage, context);
  }

  // TODO: Replace with actual toast implementation
  // Options: toastr, react-toastify, sonner, etc.
  console.log(`[${type.toUpperCase()}] ${title}${message ? `: ${message}` : ""}`);
}
showSuccess function · typescript · L73-L75 (3 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export function showSuccess(title: string, message?: string): void {
  showToastr({ type: "success", title, message });
}
showError function · typescript · L81-L87 (7 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export function showError(
  title: string,
  message?: string,
  context?: string
): void {
  showToastr({ type: "error", title, message, context });
}
showInfo function · typescript · L92-L94 (3 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export function showInfo(title: string, message?: string): void {
  showToastr({ type: "info", title, message });
}
Repobility · code-quality intelligence platform · https://repobility.com
showWarning function · typescript · L99-L101 (3 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export function showWarning(title: string, message?: string): void {
  showToastr({ type: "warning", title, message });
}
useToastr function · typescript · L121-L129 (9 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export function useToastr() {
  return {
    showToastr: useCallback(showToastr, []),
    showSuccess: useCallback(showSuccess, []),
    showError: useCallback(showError, []),
    showInfo: useCallback(showInfo, []),
    showWarning: useCallback(showWarning, []),
  };
}
withErrorHandling function · typescript · L146-L156 (11 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export function withErrorHandling<P extends object>(
  Component: React.ComponentType<P>
): React.ComponentType<P> {
  return function WrappedComponent(props: P) {
    return (
      <ErrorBoundary>
        <Component {...props} />
      </ErrorBoundary>
    );
  };
}
useAsyncOperation function · typescript · L217-L244 (28 LOC)
.claude/skills/tauri-desktop-app/utils/toastr.tsx
export function useAsyncOperation<T>({
  operation,
  onSuccess,
  onError,
}: UseAsyncOperationOptions<T>): AsyncOperationState<T> {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  const [data, setData] = useState<T | null>(null);

  const execute = useCallback(async () => {
    setIsLoading(true);
    setError(null);

    try {
      const result = await operation();
      setData(result);
      onSuccess?.(result);
    } catch (err) {
      const error = err instanceof Error ? err : new Error(String(err));
      setError(error);
      onError?.(error);
    } finally {
      setIsLoading(false);
    }
  }, [operation, onSuccess, onError]);

  return { execute, isLoading, error, data };
}
AnalysisPanel function · typescript · L39-L249 (211 LOC)
src/components/AnalysisPanel.tsx
export function AnalysisPanel({
  clipId,
  videoPath,
  isAnalyzing,
  progress,
  stage,
  segments,
  statistics,
  error,
  onAnalyze,
  onSegmentToggle,
  onReset,
}: AnalysisPanelProps) {
  const [filterType, setFilterType] = useState<"all" | "filler" | "silence">("all");

  // Filter segments by type
  const filteredSegments = useMemo(() => {
    if (filterType === "all") return segments;
    return segments.filter((s) => {
      if (filterType === "filler") return s.type === "filler_word";
      if (filterType === "silence") return s.type === "silence";
      return true;
    });
  }, [segments, filterType]);

  // Count segments by type
  const fillerCount = useMemo(
    () => segments.filter((s) => s.type === "filler_word").length,
    [segments]
  );
  const silenceCount = useMemo(
    () => segments.filter((s) => s.type === "silence").length,
    [segments]
  );

  // Handle analyze button click
  const handleAnalyze = useCallback(() => {
    if (clipId && videoPath) {
    
ClipManager function · typescript · L21-L302 (282 LOC)
src/components/ClipManager.tsx
export function ClipManager({
  clips,
  activeClipId,
  onClipSelect,
  onClipAdd,
  onClipRemove,
  onClipReorder,
}: ClipManagerProps) {
  const [isImporting, setIsImporting] = useState(false);
  const [draggedId, setDraggedId] = useState<string | null>(null);
  const dragOverIdRef = useRef<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);

  // Handle file input change
  const handleFileInputChange = useCallback(
    async (e: React.ChangeEvent<HTMLInputElement>) => {
      const files = e.target.files;
      if (!files || files.length === 0) return;

      setIsImporting(true);
      try {
        // In Tauri, file input returns the file path
        for (let i = 0; i < files.length; i++) {
          const file = files[i];
          const filePath = (file as any).path || file.name;
          await onClipAdd(filePath);
        }
      } catch (error) {
        console.error("Failed to import video:", error);
      } finally {
        setIsImporting(false)
ClipsPanel function · typescript · L34-L312 (279 LOC)
src/components/ClipsPanel.tsx
export function ClipsPanel({
  clips,
  activeClipId,
  onClipSelect,
  onClipAdd,
  onClipRemove,
  onClipReorder,
  isVisible = true,
  onSwitchToTranscript,
}: ClipsPanelProps) {
  const [isImporting, setIsImporting] = useState(false);
  const [draggedId, setDraggedId] = useState<string | null>(null);
  const dragOverIdRef = useRef<string | null>(null);

  // Handle click on import button - use Tauri dialog
  const handleImportClick = useCallback(async () => {
    setIsImporting(true);
    try {
      const selected = await open({
        multiple: true,
        filters: [
          {
            name: 'Video',
            extensions: ['mp4', 'mov', 'avi', 'mkv', 'webm']
          }
        ]
      });

      if (selected) {
        const files = Array.isArray(selected) ? selected : [selected];
        for (const filePath of files) {
          await onClipAdd(filePath);
        }
      }
    } catch (error) {
      console.error("Failed to import video:", error);
    } finally {
   
CrashRecoveryDialog function · typescript · L29-L80 (52 LOC)
src/components/CrashRecoveryDialog.tsx
export function CrashRecoveryDialog({
  isOpen,
  autosaveTime,
  onRestore,
  onDismiss,
}: CrashRecoveryDialogProps) {
  const formattedTime = new Date(autosaveTime).toLocaleString();

  return (
    <Dialog open={isOpen} onOpenChange={(open) => !open && onDismiss()}>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <div className="flex items-center gap-2 text-amber-500">
            <AlertTriangle className="w-5 h-5" />
            <DialogTitle>Crash Recovery</DialogTitle>
          </div>
          <DialogDescription>
            The app didn't close properly. An autosaved project is available.
          </DialogDescription>
        </DialogHeader>

        <div className="py-4">
          <div className="flex items-start gap-3 p-4 rounded-lg bg-amber-500/10 border border-amber-500/20">
            <Clock className="w-5 h-5 text-amber-500 shrink-0 mt-0.5" />
            <div className="flex-1">
              <p className="text-sm font-medium text-
Want this analysis on your repo? https://repobility.com/scan/
ExportProgressModal function · typescript · L62-L234 (173 LOC)
src/components/ExportProgressModal.tsx
export function ExportProgressModal({
  open,
  progress,
  outputPath,
  fileSize,
  onCancel,
  onComplete,
}: ExportProgressModalProps) {
  const isCompleted = progress?.stage === "completed";
  const progressPercent = progress?.progress ?? 0;
  const stage = progress?.stage ?? "preparing";
  const [copied, setCopied] = useState(false);

  /**
   * Format seconds into human-readable time string.
   */
  const formatTime = (seconds: number): string => {
    if (seconds < 60) return `${Math.round(seconds)}s`;
    if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
    return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
  };

  /**
   * Format bytes into human-readable file size string.
   */
  const formatFileSize = (bytes: number): string => {
    if (bytes < 1024) return `${bytes} B`;
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
    if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024
ExportSettingsDialog function · typescript · L88-L253 (166 LOC)
src/components/ExportSettingsDialog.tsx
export function ExportSettingsDialog({
  open,
  onOpenChange,
  onStartExport,
  sourceDuration = 0,
  sourceResolution: _sourceResolution = "1080p",
}: ExportSettingsDialogProps) {
  const [settings, setSettings] = useState<ExportSettings>({
    ...DEFAULT_EXPORT_SETTINGS,
  });

  /**
   * Estimate output file size based on duration, quality bitrate, and resolution.
   * Formula: (bitrate_kbps * resolutionMultiplier * duration) / 8 / 1024 = size in MB
   * Note: bitrate is in kilobits/second (kbps), need to convert to bytes then MB
   */
  const estimateFileSize = (): string => {
    if (!sourceDuration) return "~0 MB";
    const bitrate = QUALITY_PRESETS.find(q => q.value === settings.quality)?.bitrate || 5000;
    const resolutionMult = settings.resolution === "4k" ? 1.5 : settings.resolution === "2k" ? 1.2 : 1;
    // Convert kbps to MB: (kbps * seconds) / 8 = KB, then / 1024 = MB
    const sizeMb = (bitrate * resolutionMult * sourceDuration) / 8 / 1024;
    return `~${Math.round
NewProjectDialog function · typescript · L28-L102 (75 LOC)
src/components/NewProjectDialog.tsx
export function NewProjectDialog({
  isOpen,
  onClose,
  onCreate,
}: NewProjectDialogProps) {
  const [name, setName] = useState("Untitled Project");
  const [description, setDescription] = useState("");

  const handleCreate = useCallback(() => {
    if (name.trim()) {
      onCreate(name.trim(), description.trim());
      // Reset for next time
      setDescription("");
    }
  }, [name, description, onCreate]);

  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent) => {
      if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
        handleCreate();
      }
    },
    [handleCreate]
  );

  return (
    <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <div className="flex items-center gap-2">
            <FolderOpen className="w-5 h-5 text-primary" />
            <DialogTitle>New Project</DialogTitle>
          </div>
          <DialogDescription>
            Create 
formatBytes function · typescript · L41-L47 (7 LOC)
src/components/SettingsDialog.tsx
function formatBytes(bytes: number): string {
  if (bytes === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
}
SettingsDialog function · typescript · L49-L414 (366 LOC)
src/components/SettingsDialog.tsx
export function SettingsDialog({ isOpen, onClose }: SettingsDialogProps) {
  const { theme, setTheme } = useTheme();
  const [models, setModels] = useState<ModelInfo[]>([]);
  const [activeModel, setActiveModel] = useState<string>("small");
  const [isLoading, setIsLoading] = useState(true);
  const [downloadingModel, setDownloadingModel] = useState<string | null>(null);
  const [downloadProgress, setDownloadProgress] = useState<number>(0);
  const [downloadedBytes, setDownloadedBytes] = useState<number>(0);
  const [totalBytes, setTotalBytes] = useState<number>(0);
  const [downloadStage, setDownloadStage] = useState<string>("");
  const [error, setError] = useState<string | null>(null);
  const [successMessage, setSuccessMessage] = useState<string | null>(null);

  // Load models on mount
  useEffect(() => {
    if (isOpen) {
      loadModels();
    }
  }, [isOpen]);

  // Listen for download progress
  useTauriEvent<ModelDownloadProgress>("model-download-progress", {
    onEvent: (d
Timeline function · typescript · L40-L435 (396 LOC)
src/components/Timeline.tsx
export function Timeline({
  duration,
  currentTime,
  waveform,
  segments,
  zoom,
  onSeek,
  onSegmentToggle,
  onZoomChange,
  clipBoundaries = [],
  activeClipId,
}: TimelineProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isDragging, setIsDragging] = useState(false);
  const [hoverTime, setHoverTime] = useState<number | null>(null);
  const [hoveredSegmentId, setHoveredSegmentId] = useState<string | null>(null);

  // Calculate dimensions
  const width = duration * zoom;

  // Convert time to position
  const timeToPosition = useCallback((time: number) => time * zoom, [zoom]);

  // Convert position to time
  const positionToTime = useCallback((pos: number) => pos / zoom, [zoom]);

  // Handle seek via click/drag
  const handleSeek = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      if (!containerRef.current) return;
      const rect = containerRef.current.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const time = Ma
TopBar function · typescript · L60-L322 (263 LOC)
src/components/TopBar.tsx
export function TopBar({
  projectName = "Untitled Project",
  onProjectNameChange,
  onExport,
  phase = "Phase 3: ML Integration",
  leftPanelVisible = true,
  rightPanelVisible = true,
  onToggleLeftPanel,
  onToggleRightPanel,
  onOpenSettings,
  onNewProject,
  onSaveProject,
  onOpenProject,
  recentProjects,
  onOpenRecentProject,
  videoPath = "",
  transcriptWords = [],
  deletedWordIds,
  videoDuration = 0,
  videoResolution = "1080p",
}: TopBarProps) {
  const [isEditing, setIsEditing] = useState(false);
  const [editedName, setEditedName] = useState(projectName);
  const [showExportDialog, setShowExportDialog] = useState(false);

  // Export hook integration
  const { isExporting, progress, exportOutputPath, exportFileSize, startExport, cancelExport, resetExport } = useExport();

  const handleStartEdit = useCallback(() => {
    setIsEditing(true);
    setEditedName(projectName);
  }, [projectName]);

  const handleSaveEdit = useCallback(() => {
    setIsEditing(false);
   
TranscriptEditor function · typescript · L59-L399 (341 LOC)
src/components/TranscriptEditor.tsx
export function TranscriptEditor({
  className,
  onSwitchToClips,
  currentTime = 0,
  onSeek,
  removedSegments = [],
}: TranscriptEditorProps) {
  // Select store state and actions
  const {
    words,
    paragraphs,
    deletedWordIds,
    selectedWordIds,
    editMode,
    toggleWordSelection,
    deleteWord,
    restoreWord,
    deleteSelectedWords,
    setEditMode,
    deleteParagraph,
    restoreParagraph,
    clearSelection,
  } = useTranscriptStore();

  // Group words by clip ID using useMemo for performance
  const clipsGrouped = useMemo(() => {
    const groups = new Map<string, ClipWords>();

    for (const word of words) {
      const clipId = word.id.split("-")[0]; // Extract clip ID from word ID
      const clipName = `Clip ${clipId}`; // Default clip name

      if (!groups.has(clipId)) {
        groups.set(clipId, {
          clipId,
          clipName,
          words: [],
        });
      }

      groups.get(clipId)!.words.push(word);
    }

    return Array.from
About: code-quality intelligence by Repobility · https://repobility.com
TranscriptionProgressModal function · typescript · L55-L211 (157 LOC)
src/components/TranscriptionProgressModal.tsx
export function TranscriptionProgressModal({
  open,
  onOpenChange,
  clipId,
}: TranscriptionProgressModalProps) {
  const [progress, setProgress] = useState<number>(0);
  const [stage, setStage] = useState<string>("");
  const [error, setError] = useState<string | null>(null);

  // Reset state when modal opens
  useEffect(() => {
    if (open) {
      setProgress(0);
      setStage("");
      setError(null);
    }
  }, [open]);

  // Listen for transcription progress events
  useEffect(() => {
    let unlistenProgress: UnlistenFn | null = null;
    let unlistenComplete: UnlistenFn | null = null;
    let unlistenError: UnlistenFn | null = null;

    const setupListeners = async () => {
      // Listen to progress updates
      unlistenProgress = await listen<TranscriptionProgressEvent>(
        "transcription-progress",
        (event) => {
          // Only handle events for this clip if clipId is specified
          if (clipId && event.payload.clip_id !== clipId) return;

        
TranscriptPanel function · typescript · L47-L424 (378 LOC)
src/components/TranscriptPanel.tsx
export function TranscriptPanel({
  clips = [],
  segments = [],
  currentTime = 0,
  onWordClick,
  onSegmentToggle,
  isVisible = true,
  activeClipId,
  onImportVideo,
}: TranscriptPanelProps) {
  const [searchQuery, setSearchQuery] = useState("");
  const [selectedWordId, setSelectedWordId] = useState<string | null>(null);

  // Organize transcripts by clip
  const clipTranscripts: ClipTranscript[] = useMemo(() => {
    console.log('[TranscriptPanel] clips:', clips.map(c => ({
      id: c.id,
      name: c.name,
      hasTranscript: !!c.transcript,
      wordCount: c.transcript?.words?.length || 0,
    })));

    const filtered = clips
      .filter((clip) => clip.transcript?.words && clip.transcript.words.length > 0)
      .map((clip) => ({
        clip,
        words: clip.transcript?.words || [],
      }))
      .sort((a, b) => a.clip.order - b.clip.order);

    console.log('[TranscriptPanel] clipTranscripts after filter:', filtered.length, 'clips with transcripts');

    return
Dialog function · typescript · L18-L62 (45 LOC)
src/components/ui/dialog.tsx
export function Dialog({ open, onOpenChange, children }: DialogProps) {
  const [internalOpen, setInternalOpen] = React.useState(open ?? false);

  const isControlled = open !== undefined;
  const isOpen = isControlled ? open : internalOpen;

  const handleOpenChange = React.useCallback(
    (newOpen: boolean) => {
      if (!isControlled) {
        setInternalOpen(newOpen);
      }
      onOpenChange?.(newOpen);
    },
    [isControlled, onOpenChange]
  );

  // Handle escape key
  React.useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === "Escape" && isOpen) {
        handleOpenChange(false);
      }
    };

    if (isOpen) {
      document.addEventListener("keydown", handleEscape);
      return () => document.removeEventListener("keydown", handleEscape);
    }
  }, [isOpen, handleOpenChange]);

  if (!isOpen) return null;

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center">
      {/* Backdrop */}
      <div
        c
DropdownMenu function · typescript · L130-L160 (31 LOC)
src/components/ui/dropdown-menu.tsx
export function DropdownMenu({ trigger, children, align = "end" }: DropdownMenuComponentProps) {
  const [isOpen, setIsOpen] = React.useState(false);
  const containerRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
        setIsOpen(false);
      }
    };

    if (isOpen) {
      document.addEventListener("mousedown", handleClickOutside);
    }

    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [isOpen]);

  return (
    <div ref={containerRef} className="relative inline-block">
      <div onClick={() => setIsOpen(!isOpen)}>{trigger}</div>
      {isOpen && (
        <DropdownMenuContent align={align}>
          {children}
        </DropdownMenuContent>
      )}
    </div>
  );
}
DropdownMenuSub function · typescript · L168-L218 (51 LOC)
src/components/ui/dropdown-menu.tsx
export function DropdownMenuSub({ children, onOpenChange }: DropdownMenuSubProps) {
  const [isOpen, setIsOpen] = React.useState(false);
  const containerRef = React.useRef<HTMLDivElement>(null);
  const contentRef = React.useRef<HTMLDivElement>(null);

  const handleOpenChange = React.useCallback(
    (open: boolean) => {
      setIsOpen(open);
      onOpenChange?.(open);
    },
    [onOpenChange]
  );

  React.useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
        handleOpenChange(false);
      }
    };

    if (isOpen) {
      document.addEventListener("mousedown", handleClickOutside);
      return () => document.removeEventListener("mousedown", handleClickOutside);
    }
  }, [isOpen, handleOpenChange]);

  return (
    <div ref={containerRef} className="relative">
      {React.Children.map(children, (child) => {
        if (React.isValidElement(child)) {
          i
ChevronRightIcon function · typescript · L270-L287 (18 LOC)
src/components/ui/dropdown-menu.tsx
function ChevronRightIcon({ className }: { className?: string }) {
  return (
    <svg
      className={className}
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      <polyline points="9 18 15 12 9 6" />
    </svg>
  );
}
formatTime function · typescript · L28-L32 (5 LOC)
src/components/ui/segment-header.tsx
function formatTime(seconds: number): string {
  const mins = Math.floor(seconds / 60);
  const secs = Math.floor(seconds % 60);
  return `${mins}:${secs.toString().padStart(2, "0")}`;
}
getPreviewText function · typescript · L37-L39 (3 LOC)
src/components/ui/segment-header.tsx
function getPreviewText(text: string): string {
  return text.length > 50 ? text.slice(0, 50) + "..." : text;
}
Repobility analyzer · published findings · https://repobility.com
SegmentHeader function · typescript · L62-L148 (87 LOC)
src/components/ui/segment-header.tsx
export function SegmentHeader({
  paragraph,
  isDeleted,
  onDelete,
  onRestore,
}: SegmentHeaderProps) {
  const wordCount = paragraph.wordIds.length;
  const duration = `${formatTime(paragraph.startTime)} - ${formatTime(
    paragraph.endTime
  )}`;

  return (
    <div
      className={cn(
        "flex items-center gap-2 py-2 px-3 border-b border-border/50",
        "hover:bg-muted/50 transition-colors",
        isDeleted && "opacity-60"
      )}
    >
      {/* Paragraph indicator */}
      <FileText
        className={cn(
          "w-4 h-4 shrink-0",
          isDeleted ? "text-muted-foreground" : "text-primary"
        )}
      />

      {/* Paragraph preview */}
      <span
        className={cn(
          "text-sm flex-1 truncate",
          isDeleted
            ? "text-muted-foreground line-through"
            : "text-foreground"
        )}
      >
        {getPreviewText(paragraph.text)}
      </span>

      {/* Word count badge */}
      <Badge
        variant="outline
Toaster function · typescript · L28-L57 (30 LOC)
src/components/ui/toaster.tsx
export function Toaster() {
  return (
    <SonnerToaster
      position="bottom-right"
      toastOptions={{
        duration: 4000,
        style: {
          background: "hsl(var(--card))",
          border: "1px solid hsl(var(--border))",
          color: "hsl(var(--foreground))",
          borderRadius: "calc(var(--radius) - 2px)",
          padding: "0.75rem 1rem",
          fontSize: "0.875rem",
        },
        classNames: {
          toast: "border-border bg-card text-foreground",
          title: "font-medium text-sm",
          description: "text-sm text-muted-foreground",
          actionButton: "bg-primary text-primary-foreground hover:bg-primary/90",
          cancelButton: "bg-muted text-muted-foreground hover:bg-muted/80 border border-border",
          closeButton: "bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground",
          success: "border-success/30 bg-success/10",
          error: "border-destructive/30 bg-destructive/10",
          warn
Tooltip function · typescript · L20-L100 (81 LOC)
src/components/ui/tooltip.tsx
export function Tooltip({ content, children, side = "top" }: TooltipProps) {
  const [isVisible, setIsVisible] = React.useState(false);
  const [position, setPosition] = React.useState({ top: 0, left: 0 });
  const triggerRef = React.useRef<HTMLDivElement>(null);

  const handleMouseEnter = () => {
    if (triggerRef.current) {
      const rect = triggerRef.current.getBoundingClientRect();
      let top = 0;
      let left = rect.left + rect.width / 2;

      switch (side) {
        case "top":
          top = rect.bottom + 8;
          break;
        case "bottom":
          top = rect.bottom + 8;
          break;
        case "left":
          top = rect.top + rect.height / 2;
          left = rect.left - 8;
          break;
        case "right":
          top = rect.top + rect.height / 2;
          left = rect.right + 8;
          break;
      }

      setPosition({ top, left });
      setIsVisible(true);
    }
  };

  const handleMouseLeave = () => {
    setIsVisible(false);
  };

VideoPlayer function · typescript · L24-L421 (398 LOC)
src/components/VideoPlayer.tsx
export function VideoPlayer({
  videoSrc,
  duration,
  currentTime,
  isPlaying,
  playbackSpeed,
  removedSegments = [],
  onTimeUpdate,
  onPlayPause,
  onSeek,
  onSpeedChange,
}: VideoPlayerProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const playIntentRef = useRef(false);
  const currentTimeRef = useRef(currentTime);
  const onTimeUpdateRef = useRef(onTimeUpdate);
  const onSeekRef = useRef(onSeek);
  const scrubberContainerRef = useRef<HTMLDivElement>(null);

  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setHasError] = useState(false);
  const [showControls, setShowControls] = useState(false);
  const [videoDuration, setVideoDuration] = useState(0);

  // Sync refs with props
  useEffect(() => {
    currentTimeRef.current = currentTime;
    onTimeUpdateRef.current = onTimeUpdate;
  }, [currentTime, onTimeUpdate]);
  useEffect(() => {
    onSeekRef.current = onSeek;
  }, [onSeek]);

  // Effective duration (excluding removed segments)
  co
WordSpan function · typescript · L37-L87 (51 LOC)
src/components/WordSpan.tsx
export function WordSpan({
  word,
  isDeleted,
  isSelected,
  isPlaying = false,
  onClick,
  onDoubleClick,
  onSeek,
}: WordSpanProps) {
  const handleClick = (e: React.MouseEvent) => {
    // If seek is available and not selecting, seek on click
    if (onSeek && !e.shiftKey && !isDeleted) {
      onSeek(word.startTime);
    }
    // Always trigger selection for multi-select (pass event for Shift detection)
    onClick(word, e);
  };

  const handleDoubleClick = () => {
    onDoubleClick(word);
  };

  return (
    <span
      data-word-id={word.id}
      data-start-time={word.startTime}
      data-end-time={word.endTime}
      onClick={handleClick}
      onDoubleClick={handleDoubleClick}
      className={cn(
        // Base styles
        "inline transition-colors cursor-pointer rounded px-0.5 break-words",
        // Deleted state
        isDeleted && "line-through text-muted-foreground opacity-50",
        // Playing state (highest priority visual feedback)
        !isDeleted &
ThemeProvider function · typescript · L20-L71 (52 LOC)
src/contexts/ThemeContext.tsx
export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setThemeState] = useState<ThemeMode>("dark");
  const [resolvedTheme, setResolvedTheme] = useState<"light" | "dark">("dark");

  // Load theme from Rust backend on mount
  useEffect(() => {
    invoke<string>("get_theme")
      .then((savedTheme) => {
        setThemeState(savedTheme as ThemeMode);
      })
      .catch(() => setThemeState("dark"));
  }, []);

  // Resolve system theme and apply to document
  useEffect(() => {
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");

    const updateResolvedTheme = () => {
      const isDark =
        theme === "dark" || (theme === "system" && mediaQuery.matches);
      setResolvedTheme(isDark ? "dark" : "light");

      // Apply class to document
      const root = document.documentElement;
      root.classList.remove("light", "dark");
      root.classList.add(isDark ? "dark" : "light");
    };

    updateResolvedTheme();

   
useTheme function · typescript · L73-L79 (7 LOC)
src/contexts/ThemeContext.tsx
export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error("useTheme must be used within ThemeProvider");
  }
  return context;
}
useAnalysis function · typescript · L46-L145 (100 LOC)
src/hooks/useAnalysis.ts
export function useAnalysis(options?: UseAnalysisOptions): UseAnalysisResult {
  const [isAnalyzing, setIsAnalyzing] = useState(false);
  const [progress, setProgress] = useState(0);
  const [stage, setStage] = useState("");
  const [segments, setSegments] = useState<Segment[]>([]);
  const [statistics, setStatistics] = useState<AnalysisStatistics | null>(null);
  const [error, setError] = useState<string | null>(null);

  // Listen for progress updates
  useTauriEvent<AnalysisProgress>("analysis-progress", {
    onEvent: (data) => {
      setProgress(data.progress);
      setStage(data.stage);
    },
  });

  // Listen for completion
  useTauriEvent<{
    clip_id: string;
    segments: Array<{
      id: string;
      clip_id: string;
      start_time: number;
      end_time: number;
      duration: number;
      segment_type: "filler_word" | "silence" | "speech";
      is_active: boolean;
      confidence: number;
      manually_edited: boolean;
      content: string;
    }>;
    stat
Repobility · code-quality intelligence platform · https://repobility.com
classifyError function · typescript · L114-L174 (61 LOC)
src/hooks/useExport.ts
function classifyError(error: string): ExportError {
  const lower = error.toLowerCase();

  // No active content
  if (lower.includes("no segments") || lower.includes("empty") || lower.includes("no active content")) {
    return {
      code: "NO_ACTIVE_CONTENT",
      message: "No active content to export",
      details: "All words in the transcript are marked for deletion. Restore some words before exporting.",
      recoverable: true,
    };
  }

  // Invalid path
  if (lower.includes("invalid path") || lower.includes("not found") || lower.includes("no such file")) {
    return {
      code: "INVALID_OUTPUT_PATH",
      message: "Invalid output location",
      details: "The selected output path is invalid or doesn't exist. Choose a different location.",
      recoverable: true,
    };
  }

  // Disk full
  if (lower.includes("no space") || lower.includes("disk full") || lower.includes("no space left")) {
    return {
      code: "DISK_FULL",
      message: "Not enough disk space"
serializeError function · typescript · L179-L181 (3 LOC)
src/hooks/useExport.ts
function serializeError(error: ExportError): string {
  return JSON.stringify(error);
}
deserializeError function · typescript · L186-L199 (14 LOC)
src/hooks/useExport.ts
function deserializeError(errorStr: string | null): ExportError | null {
  if (!errorStr) return null;
  try {
    return JSON.parse(errorStr) as ExportError;
  } catch {
    // If parsing fails, create a generic error
    return {
      code: "UNKNOWN_ERROR",
      message: "Export failed",
      details: errorStr,
      recoverable: true,
    };
  }
}
page 1 / 9next ›