Skip to content

refactor: extract useAutoReset hook and fix selector stability#10

Open
randroid88 wants to merge 7 commits into
DataAnts-AI:mainfrom
Randroids-Dojo:upstream/auto-reset-hook
Open

refactor: extract useAutoReset hook and fix selector stability#10
randroid88 wants to merge 7 commits into
DataAnts-AI:mainfrom
Randroids-Dojo:upstream/auto-reset-hook

Conversation

@randroid88

Copy link
Copy Markdown

Summary

  • Extract useAutoReset hook — replaces 4 identical useState + useEffect + setTimeout patterns across components
  • Fix temporal selector stability — wrap useStore(temporal) with useShallow to prevent new-object-on-every-update re-renders

Depends on: #8 (browser file ops — modifies AIPanel, ExportDialog) and #9 (undo/redo — introduces temporal subscription in App.tsx)

useAutoReset hook

const [copied, setCopied] = useAutoReset(false, 2000);
// setCopied(true) → auto-resets to false after 2s

Replaces this pattern that was duplicated in 4 places:

const [copied, setCopied] = useState(false);
useEffect(() => {
  if (!copied) return;
  const timer = setTimeout(() => setCopied(false), 2000);
  return () => clearTimeout(timer);
}, [copied]);

Components updated

Component State replaced
TranscriptEditor copied (2s)
ExportDialog exportSuccess (3s), exportError (3s)
AIPanel exportedClipIndex (3s), exportClipError (3s)

Selector stability

The temporal store subscription in App.tsx creates a new object { pastStates, futureStates } on every store update, causing unnecessary re-renders. Wrapping with useShallow performs shallow equality comparison to prevent this.

Files changed

  • frontend/src/hooks/useAutoReset.ts — New: reusable auto-resetting state hook
  • frontend/src/App.tsx — Add useShallow to temporal selector
  • frontend/src/components/AIPanel.tsx — Use useAutoReset for clip export feedback
  • frontend/src/components/ExportDialog.tsx — Use useAutoReset for export feedback
  • frontend/src/components/TranscriptEditor.tsx — Use useAutoReset for copy feedback

Test plan

  • Copy transcript → "Copied!" resets after 2 seconds
  • Export video → "Saved!" resets after 3 seconds
  • Export AI clip → "Saved!" resets after 3 seconds
  • Export error → error message resets after 3 seconds
  • Verify no unnecessary re-renders (React DevTools profiler)

Add a live backend connection status indicator (green/red/yellow dot)
to the toolbar with adaptive health polling (1s retry when offline,
5s when online). Include restart/start controls in both Electron
(IPC-based) and browser mode (Vite dev middleware).

- New BackendStatusDot component with color-coded status
- Extract IS_ELECTRON and startBackend() to shared utils/env.ts
- Add BackendStatus type to project types
- Backend section in Settings panel with status + restart button
- Vite plugin to spawn backend in browser dev mode
- Electron IPC handler for backend:restart
Add bulk transcript editing: copy the transcript, edit it externally,
and paste it back to apply all deletions at once.

Workflow:
1. Click "Copy" in the transcript toolbar to copy all non-deleted words
2. Edit the text in any external editor (delete unwanted words)
3. Click "Paste edits" to open a diff preview dialog
4. Review the preview (word count, cut count, warnings) and click "Apply"

The diff algorithm uses Longest Common Subsequence (LCS) to detect only
deletions — modified words are ignored. Uses Uint32Array for the DP
table to support transcripts beyond 65k words. The diff computation is
debounced to 300ms to avoid running on every keystroke.

Also extracts buildDeletedSet() utility to eliminate duplicate set-
building loops across components.
Replace the manual path input in browser mode with the File System
Access API for both opening and saving files:

- New POST /upload endpoint accepts file uploads for browser-mode
  transcription
- Browser file open uses showOpenFilePicker() with upload progress
- Browser export uses showSaveFilePicker() and streams the response
  directly to disk via FileResponse
- AI clip exports now intersect keep-segments with clip time range,
  honoring word-level cuts and auto-selecting re-encode mode when needed
- Inline "Saved!" / error feedback replaces alert() calls
- Export loading spinner deferred until after user confirms save picker
- Fix TOCTOU race condition in export (replace os.path.exists() guard
  with try/except)
Add visible Undo/Redo buttons to the toolbar with proper disabled state,
wired to the zundo temporal store.

Fix the temporal middleware's partialize config to only snapshot editing
state (words, deletedRanges, segments, language). Previously, all state
including playhead position, hover state, and selection was captured,
causing spurious undo steps when the user simply moved the playhead.
Extract a reusable useAutoReset<T>(initial, delayMs) hook that replaces
4 identical useState + useEffect + setTimeout patterns across components
(AIPanel, ExportDialog, TranscriptEditor).

Fix selector stability in App.tsx by wrapping the temporal store
subscription with useShallow to prevent unnecessary re-renders from
new-object-on-every-update.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant