Skip to content

feat(web): add progress bar with auto-cycling and scroll-into-view for showcase sections#1983

Closed
ComputelessComputer wants to merge 2 commits intomainfrom
devin/1764382504-progress-bar-showcase
Closed

feat(web): add progress bar with auto-cycling and scroll-into-view for showcase sections#1983
ComputelessComputer wants to merge 2 commits intomainfrom
devin/1764382504-progress-bar-showcase

Conversation

@ComputelessComputer
Copy link
Collaborator

@ComputelessComputer ComputelessComputer commented Nov 29, 2025

feat(web): add auto-cycling progress bar for showcase sections

Summary

Adds an auto-cycling progress bar component to showcase multiple elements across three sections on the marketing website:

  • FloatingPanelSection in /product/ai-notetaking (mobile, tablet, desktop)
  • MainFeaturesSection on the homepage (mobile carousel)
  • DetailsSection on the homepage (mobile, tablet, desktop)

The progress bar auto-advances every 5 seconds with a visual fill animation. It pauses on hover (desktop) or touch (mobile) and resumes when interaction ends. Clicking a segment navigates directly to that item with scroll-into-view behavior.

Review & Testing Checklist for Human

  • Visual verification: Test all three sections at mobile (<800px), tablet (800-1200px), and desktop (>1200px) viewport widths
  • Auto-cycling: Verify items advance every 5 seconds and progress bar fills smoothly
  • Pause/resume: Test hover pause on desktop, touch pause on mobile - ensure cycling resumes correctly
  • Navigation: Click progress bar segments and verify correct item is shown with smooth scroll
  • Scroll-into-view: On tablet/desktop views, verify the selected tab scrolls into view when auto-advancing

Recommended test plan:

  1. Run pnpm -F web dev and navigate to both / and /product/ai-notetaking
  2. Resize browser to test all three breakpoints
  3. Let the carousels auto-cycle through all items
  4. Interact with progress bars and verify pause/resume behavior

Notes

  • The useAutoProgress hook and ProgressBar component are duplicated in both files - could be extracted to a shared location in a follow-up
  • Uses setInterval at 50ms for smooth progress animation

Link to Devin run: https://app.devin.ai/sessions/71adb294caef488c9247fd16b9c1b8af
Requested by: john@hyprnote.com (@ComputelessComputer)

…r showcase sections

- Add useAutoProgress hook for auto-cycling through items every 5 seconds
- Add ProgressBar component with visual progress fill animation
- Implement progress bar for FloatingPanelSection in ai-notetaking.tsx (mobile, tablet, desktop)
- Implement progress bar for MainFeaturesSection in index.tsx (mobile carousel)
- Implement progress bar for DetailsSection in index.tsx (mobile, tablet, desktop)
- Add scroll-into-view functionality when showcasing elements
- Add pause on hover/touch and resume on mouse leave/touch end

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Nov 29, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit ee72fd0
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/692a5b3d302f5a000864034d
😎 Deploy Preview https://deploy-preview-1983--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 29, 2025

📝 Walkthrough

Walkthrough

Added an auto-progress system (internal useAutoProgress hook + ProgressBar component) and replaced direct scroll callbacks with progress-driven APIs and pause/resume handlers across feature and detail carousels and floating panels.

Changes

Cohort / File(s) Summary
Main View / Carousels
apps/web/src/routes/_view/index.tsx
Introduced useAutoProgress usage and integrated ProgressBar. Replaced scrollToFeature/scrollToDetail callbacks with progress, onSelect, onPause, onResume props on MainFeaturesSection, DetailsSection, and their mobile/tablet/desktop subcomponents. Implemented hover/touch pause and resume behaviors and progress-driven auto-advance.
Product — Floating Panel (ai-notetaking)
apps/web/src/routes/_view/product/ai-notetaking.tsx
Added useAutoProgress hook, ProgressBar component, and PROGRESS_DURATION constant. Refactored FloatingPanel variants (FloatingPanelMobile, FloatingPanelTablet, FloatingPanelDesktop) to accept progress, onSelect, onPause, onResume and centralized auto-advance/tab navigation via progress. Extended React imports (useCallback).
VS Route
apps/web/src/routes/_view/vs/$slug.tsx
Added internal useAutoProgress implementation and wired two independent progress managers (features and details). Replaced direct scroll callbacks with progress-driven handlers (onSelect, onPause, onResume) and preserved existing UI structure.

Sequence Diagram

sequenceDiagram
    participant User
    participant View as Carousel / Panel View
    participant Hook as useAutoProgress
    participant ProgressBar
    participant Parent as Parent Component State

    Note over Hook: Tracks progress, pause/resume, auto-advance
    Hook->>Hook: start timer / increment progress
    Hook->>View: emit progress updates

    loop while active
        View->>ProgressBar: render progress for items
        User->>View: hover / touchstart
        View->>Hook: onPause()
        Hook->>Hook: pause timer
        User->>View: hover end / touchend
        View->>Hook: onResume()
        Hook->>Hook: resume timer
        Hook->>Hook: progress reaches 100%
        Hook->>Parent: onSelect(nextIndex)
        Parent->>View: update selected index
        Hook->>Hook: reset progress
    end

    alt user clicks progress bar
        User->>ProgressBar: click index
        ProgressBar->>Parent: onSelect(targetIndex)
        Parent->>View: update selected index
        Parent->>Hook: restart progress for new index
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review timer/interval management and cleanup in useAutoProgress implementations for memory leaks and double-timers.
  • Verify pause/resume accuracy (preserving elapsed time) and edge cases when selected index changes rapidly.
  • Ensure all component signature updates are consistent across mobile/tablet/desktop variants and callers.
  • Validate ProgressBar click handling and accessibility/keyboard interactions if required.

Suggested reviewers

  • yujonglee

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(web): add progress bar with auto-cycling and scroll-into-view for showcase sections' clearly and specifically describes the main changes: adding a progress bar component with auto-cycling and scroll-into-view functionality across showcase sections.
Description check ✅ Passed The description is well-related to the changeset, providing a clear summary of the new auto-cycling progress bar component, the sections where it's implemented, its behavior (auto-advance, pause/resume, navigation), and testing guidance.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1764382504-progress-bar-showcase

Comment @coderabbitai help to get the list of available commands and usage tips.

@netlify
Copy link

netlify bot commented Nov 29, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit ee72fd0
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/692a5b3d77f8c70008db5e32
😎 Deploy Preview https://deploy-preview-1983--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
apps/web/src/routes/_view/index.tsx (3)

66-91: Potential race condition between the two useEffect hooks.

The first useEffect (lines 66-85) calls onSelect(nextIndex) when progress reaches 1, which changes selectedIndex. The second useEffect (lines 87-91) resets progress when selectedIndex changes. However, the first effect also resets progress internally (line 77-79), potentially causing a double reset and visual flicker.

Consider removing the duplicate reset logic:

       if (newProgress >= 1) {
         const nextIndex = (selectedIndex + 1) % itemCount;
         onSelect(nextIndex);
-        setProgress(0);
-        startTimeRef.current = Date.now();
-        pausedProgressRef.current = 0;
       }

The second useEffect will handle the reset when selectedIndex changes.


109-131: Consider enhancing accessibility for the progress bar.

The buttons have aria-label which is good, but for a progress indicator pattern, consider adding ARIA attributes to convey progress state to assistive technologies:

     <div className={cn("flex justify-center gap-2", className)}>
-      {Array.from({ length: itemCount }).map((_, index) => (
+      {Array.from({ length: itemCount }).map((_, index) => {
+        const isActive = index === selectedIndex;
+        const isComplete = index < selectedIndex;
+        return (
         <button
           key={index}
           onClick={() => onSelect(index)}
           className="h-1 w-8 rounded-full bg-neutral-200 overflow-hidden cursor-pointer"
           aria-label={`Go to item ${index + 1}`}
+          aria-current={isActive ? "step" : undefined}
         >

1596-1608: Fragile DOM traversal for scroll-into-view.

The code accesses container.children[0]?.children[selectedDetail] which depends on the exact DOM structure. A safer approach would be to use data-* attributes or query selectors:

   useEffect(() => {
     if (tabsContainerRef.current) {
       const container = tabsContainerRef.current;
-      const selectedButton = container.children[0]?.children[
-        selectedDetail
-      ] as HTMLElement;
+      const selectedButton = container.querySelector(
+        `[data-index="${selectedDetail}"]`
+      ) as HTMLElement;
       if (selectedButton) {
         selectedButton.scrollIntoView({
           behavior: "smooth",
           block: "nearest",
           inline: "center",
         });
       }
     }
   }, [selectedDetail]);

Then add data-index={index} to each button in the render.

apps/web/src/routes/_view/product/ai-notetaking.tsx (1)

2304-2318: Same fragile DOM traversal pattern.

This has the same issue as noted in index.tsx - the container.children[0]?.children[selectedTab] traversal is fragile. Consider using data attributes or query selectors for robustness.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06e80fb and abb69b9.

📒 Files selected for processing (2)
  • apps/web/src/routes/_view/index.tsx (13 hunks)
  • apps/web/src/routes/_view/product/ai-notetaking.tsx (7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/product/ai-notetaking.tsx
  • apps/web/src/routes/_view/index.tsx
🧠 Learnings (1)
📚 Learning: 2025-11-24T16:32:19.706Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:19.706Z
Learning: Applies to **/*.{ts,tsx} : Use `motion/react` instead of `framer-motion`.

Applied to files:

  • apps/web/src/routes/_view/product/ai-notetaking.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/index.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
🔇 Additional comments (6)
apps/web/src/routes/_view/index.tsx (3)

224-240: LGTM on the scroll callbacks.

The scrollToDetail and scrollToFeature callbacks are correctly memoized with empty dependency arrays since they only use refs and setters which are stable.


1282-1296: Touch events for pause/resume are good for mobile UX.

The onTouchStart and onTouchEnd handlers correctly pause and resume auto-progress during touch interactions, allowing users to manually swipe through items without the timer fighting them.


1714-1719: Desktop view correctly pauses on hover.

The onMouseEnter={onPause} and onMouseLeave={onResume} pattern is appropriate for desktop users, allowing them to interact with the tabs without the auto-advance interfering.

apps/web/src/routes/_view/product/ai-notetaking.tsx (3)

2238-2258: LGTM on the progress integration.

The FloatingPanelContent correctly integrates useAutoProgress and properly threads the progress state and control callbacks to child components.


2474-2478: Touch event handling is consistent with mobile UX patterns.

The touch start/end handlers for pausing and resuming are correctly implemented, matching the pattern in index.tsx.


9-10: Correct motion import.

Using motion/react instead of framer-motion as per coding guidelines.

Comment on lines +2097 to +2202
const PROGRESS_DURATION = 5000;

function useAutoProgress({
itemCount,
selectedIndex,
onSelect,
duration = PROGRESS_DURATION,
}: {
itemCount: number;
selectedIndex: number;
onSelect: (index: number) => void;
duration?: number;
}) {
const [progress, setProgress] = useState(0);
const [isPaused, setIsPaused] = useState(false);
const startTimeRef = useRef<number>(Date.now());
const pausedProgressRef = useRef<number>(0);

const pause = useCallback(() => {
pausedProgressRef.current = progress;
setIsPaused(true);
}, [progress]);

const resume = useCallback(() => {
startTimeRef.current = Date.now() - pausedProgressRef.current * duration;
setIsPaused(false);
}, [duration]);

const goToIndex = useCallback(
(index: number) => {
onSelect(index);
setProgress(0);
startTimeRef.current = Date.now();
pausedProgressRef.current = 0;
},
[onSelect],
);

useEffect(() => {
if (isPaused) return;

const animate = () => {
const elapsed = Date.now() - startTimeRef.current;
const newProgress = Math.min(elapsed / duration, 1);
setProgress(newProgress);

if (newProgress >= 1) {
const nextIndex = (selectedIndex + 1) % itemCount;
onSelect(nextIndex);
setProgress(0);
startTimeRef.current = Date.now();
pausedProgressRef.current = 0;
}
};

const intervalId = setInterval(animate, 50);
return () => clearInterval(intervalId);
}, [isPaused, selectedIndex, itemCount, onSelect, duration]);

useEffect(() => {
setProgress(0);
startTimeRef.current = Date.now();
pausedProgressRef.current = 0;
}, [selectedIndex]);

return { progress, isPaused, pause, resume, goToIndex };
}

function ProgressBar({
itemCount,
selectedIndex,
progress,
onSelect,
className,
}: {
itemCount: number;
selectedIndex: number;
progress: number;
onSelect: (index: number) => void;
className?: string;
}) {
return (
<div className={cn("flex justify-center gap-2", className)}>
{Array.from({ length: itemCount }).map((_, index) => (
<button
key={index}
onClick={() => onSelect(index)}
className="h-1 w-8 rounded-full bg-neutral-200 overflow-hidden cursor-pointer"
aria-label={`Go to item ${index + 1}`}
>
<div
className="h-full bg-stone-600 transition-all duration-100"
style={{
width:
index < selectedIndex
? "100%"
: index === selectedIndex
? `${progress * 100}%`
: "0%",
}}
/>
</button>
))}
</div>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated useAutoProgress hook and ProgressBar component to shared module.

The useAutoProgress hook (lines 2099-2163) and ProgressBar component (lines 2165-2202) are duplicated verbatim from apps/web/src/routes/_view/index.tsx. This violates DRY and will make future maintenance difficult.

Consider extracting to a shared location:

// apps/web/src/hooks/use-auto-progress.ts
export function useAutoProgress({ ... }) { ... }

// apps/web/src/components/progress-bar.tsx
export function ProgressBar({ ... }) { ... }

Then import in both files:

import { useAutoProgress } from "@/hooks/use-auto-progress";
import { ProgressBar } from "@/components/progress-bar";
🤖 Prompt for AI Agents
In apps/web/src/routes/_view/product/ai-notetaking.tsx around lines 2097-2202
the useAutoProgress hook and ProgressBar component are duplicated from
apps/web/src/routes/_view/index.tsx; extract both into shared files and replace
the inline definitions with imports. Create
apps/web/src/hooks/use-auto-progress.ts exporting the useAutoProgress hook
(preserve props, behavior and defaults) and
apps/web/src/components/progress-bar.tsx exporting the ProgressBar component
(preserve props and markup), update both original files to import those modules,
and run a quick typecheck to ensure imports/exports and relative paths are
correct.

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
@argos-ci
Copy link

argos-ci bot commented Nov 29, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
web (Inspect) ⚠️ Changes detected (Review) 2 changed Nov 29, 2025, 2:35 AM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/src/routes/_view/product/ai-notetaking.tsx (1)

2064-2169: useAutoProgress + ProgressBar duplication should be extracted to a shared module

This hook and ProgressBar are still duplicated verbatim with the versions in apps/web/src/routes/_view/index.tsx, and now the same hook logic also appears in apps/web/src/routes/_view/vs/$slug.tsx. Centralizing them (e.g., @/hooks/use-auto-progress and @/components/progress-bar) would reduce maintenance overhead and keep behavior consistent across pages.

The existing prior review already called this out; this just reiterates it now that a third copy exists.

-const PROGRESS_DURATION = 5000;
-
-function useAutoProgress({ ... }) { ... }
-
-function ProgressBar({ ... }) { ... }
+// TODO: import from shared modules once extracted
+// import { PROGRESS_DURATION, useAutoProgress } from "@/hooks/use-auto-progress";
+// import { ProgressBar } from "@/components/progress-bar";
🧹 Nitpick comments (1)
apps/web/src/routes/_view/vs/$slug.tsx (1)

123-161: Avoid double-calling scroll handlers and stabilize callbacks

handleFeatureSelect / handleDetailSelect currently invoke both goToIndex(index) and scrollToFeature / scrollToDetail directly. Since goToIndex already calls onSelect (which is scrollToFeature / scrollToDetail), this results in duplicate setState + scrollTo work and also ties the hook more tightly to non-memoized callbacks.

You can simplify and reduce unnecessary work like this:

-  const scrollToDetail = (index: number) => {
+  const scrollToDetail = useCallback((index: number) => {
     setSelectedDetail(index);
     if (detailsScrollRef.current) {
       const container = detailsScrollRef.current;
       const scrollLeft = container.offsetWidth * index;
       container.scrollTo({ left: scrollLeft, behavior: "smooth" });
     }
-  };
+  }, []);
 
-  const scrollToFeature = (index: number) => {
+  const scrollToFeature = useCallback((index: number) => {
     setSelectedFeature(index);
     if (featuresScrollRef.current) {
       const container = featuresScrollRef.current;
       const scrollLeft = container.offsetWidth * index;
       container.scrollTo({ left: scrollLeft, behavior: "smooth" });
     }
-  };
+  }, []);
 
   const featuresProgress = useAutoProgress({
     itemCount: 5,
     selectedIndex: selectedFeature,
     onSelect: scrollToFeature,
   });
 
   const detailsProgress = useAutoProgress({
     itemCount: 5,
     selectedIndex: selectedDetail,
     onSelect: scrollToDetail,
   });
 
   const handleFeatureSelect = (index: number) => {
     featuresProgress.goToIndex(index);
-    scrollToFeature(index);
   };
 
   const handleDetailSelect = (index: number) => {
     detailsProgress.goToIndex(index);
-    scrollToDetail(index);
   };

This keeps useAutoProgress as the single source of truth for progression + scrolling and prevents the interval effect from restarting on every render due to onSelect identity changes.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abb69b9 and ee72fd0.

📒 Files selected for processing (2)
  • apps/web/src/routes/_view/product/ai-notetaking.tsx (7 hunks)
  • apps/web/src/routes/_view/vs/$slug.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/vs/$slug.tsx
  • apps/web/src/routes/_view/product/ai-notetaking.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-24T16:32:19.706Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:19.706Z
Learning: Applies to **/*.{ts,tsx} : Use `motion/react` instead of `framer-motion`.

Applied to files:

  • apps/web/src/routes/_view/vs/$slug.tsx
  • apps/web/src/routes/_view/product/ai-notetaking.tsx
📚 Learning: 2025-11-24T16:32:19.706Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:19.706Z
Learning: Applies to **/*.{ts,tsx} : Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props. Just inline them.

Applied to files:

  • apps/web/src/routes/_view/product/ai-notetaking.tsx
📚 Learning: 2025-11-24T16:32:23.055Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:23.055Z
Learning: Applies to **/*.{ts,tsx} : Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.

Applied to files:

  • apps/web/src/routes/_view/product/ai-notetaking.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/product/ai-notetaking.tsx (3)
crates/whisper-local/src/model/mod.rs (1)
  • duration (38-40)
owhisper/owhisper-server/src/commands/run/state.rs (1)
  • elapsed (117-119)
packages/utils/src/cn.ts (1)
  • cn (20-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt
🔇 Additional comments (7)
apps/web/src/routes/_view/vs/$slug.tsx (2)

17-83: Auto-progress hook is internally consistent and matches other usages

The timing logic, pause/resume math, and wraparound (selectedIndex + 1) % itemCount all look correct and align with the implementation used elsewhere in the app. No functional issues from this hook as written.


180-197: Progress wiring into sections looks correct

Passing progress, onSelect, onPause, and onResume into MainFeaturesSection and DetailsSection while still supplying selected* and setters gives those components everything they need for both controlled selection and auto-cycling. Integration here looks sound assuming the section components use the new handlers as intended.

apps/web/src/routes/_view/product/ai-notetaking.tsx (5)

10-10: Expanded React imports are appropriate

Including memo, useCallback, useEffect, useRef, and useState matches the hooks/components used throughout this file; nothing extraneous here.


2205-2251: Floating panel auto-progress wiring is coherent

FloatingPanelContent centralizes tab selection via scrollToTab and useAutoProgress, then exposes progress, onSelect, onPause, and onResume to the three layout variants. Using a single hook instance driven by selectedTab keeps behavior consistent regardless of breakpoint and ensures that clicking via handleSelect always resets timing via goToIndex. This looks correct.


2256-2335: Tablet layout: scroll-into-view + hover pause are implemented cleanly

The tablet variant’s tabsContainerRef + useEffect keep the active tab button centered when selectedTab changes, and the wrapper-level onMouseEnter / onMouseLeave correctly delegate to onPause / onResume. Adding the ProgressBar under the image ties visual progress to the hook state with no extra state duplication. No issues here.


2337-2421: Desktop layout: synchronized list, preview, and progress bar

On desktop, the left column list uses onClick={() => onSelect(index)} to funnel interactions through the same auto-progress path, while the right column image and ProgressBar are driven solely by selectedTab and progress. The scroll-into-view effect on the left (tabsContainerRef) is a nice touch for long lists. Behavior here is consistent with tablet/mobile.


2423-2488: Mobile layout: touch pause and snapping work with auto-progress

On mobile, onTouchStart={onPause} / onTouchEnd={onResume} at the container level pause auto-advancement while the user is interacting, and the horizontal snap scroller updates selectedTab from scrollLeft. The ProgressBar delegates clicks through onSelect, which routes back to goToIndex and scrollToTab. The data flow is clean and doesn’t introduce extra state.

@yujonglee yujonglee force-pushed the main branch 4 times, most recently from cd99174 to cb527d5 Compare November 29, 2025 13:01
@ComputelessComputer ComputelessComputer deleted the devin/1764382504-progress-bar-showcase branch December 14, 2025 15:20
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

Comments