Skip to content

Conversation

@ComputelessComputer
Copy link
Collaborator

@ComputelessComputer ComputelessComputer commented Dec 17, 2025

Description

  • Added close button with finalizing state for finalizing states
  • Enhanced tab interaction with new UI elements

This is part 1 of 3 in a stack:

@netlify
Copy link

netlify bot commented Dec 17, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit 0b187fb
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/694767c432fc5a0007e9c4fb
😎 Deploy Preview https://deploy-preview-2359--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.

@netlify
Copy link

netlify bot commented Dec 17, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 0b187fb
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/694767c5d903180008e67064
😎 Deploy Preview https://deploy-preview-2359--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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

📝 Walkthrough

Walkthrough

Adds a read-only hook to detect when a session's enhanced notes are being generated and surfaces that state as a "finalizing" spinner in session tabs and timeline items by passing a new finalizing prop and adjusting rendering/layout to show a Spinner when appropriate.

Changes

Cohort / File(s) Change Summary
Enhanced notes status hook
apps/desktop/src/hooks/useEnhancedNotes.ts
Added export function useIsSessionEnhancing(sessionId: string): boolean which maps enhanced-note IDs to AI task IDs and returns true if any corresponding useAITask status is "generating".
Session tab wiring
apps/desktop/src/components/main/body/sessions/index.tsx
Imported useIsSessionEnhancing, derived isEnhancing, computed isFinalizing from sessionMode, combined into showSpinner, and passed finalizing={showSpinner} to TabItemBase.
Tab item rendering
apps/desktop/src/components/main/body/shared.tsx
Added finalizing?: boolean to TabItemBaseProps and finalizing = false default. When finalizing is true, render Spinner in the icon area; removed isEmptyTab prop and adjusted icon-area control flow.
Timeline item layout
apps/desktop/src/components/main/sidebar/timeline/item.tsx
Imported Spinner, useListener, and useIsSessionEnhancing. Computed sessionMode, isEnhancing, isFinalizing, and showSpinner. Reworked layout to conditionally show a left-aligned Spinner and adjusted title/timestamp stacking to allow truncation.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant SessionsIndex as SessionsIndex (render)
participant Hook as useIsSessionEnhancing
participant AITaskStore as useAITask / TaskStore
participant TabItem as TabItemBase / TimelineItem

SessionsIndex->>Hook: call useIsSessionEnhancing(sessionId)
Hook->>AITaskStore: derive task IDs -> query statuses
AITaskStore-->>Hook: return statuses (generating/...)
Hook-->>SessionsIndex: return isEnhancing (true/false)
SessionsIndex->>TabItem: pass finalizing={sessionMode==="finalizing" || isEnhancing}
TabItem->>TabItem: render icon area (Spinner if finalizing else prior visuals)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Focus review on: correctness of enhanced-note -> task ID mapping and status checks in useIsSessionEnhancing; consistent typing and default for new finalizing prop; UI layout/truncation and keyboard/accessibility when spinner is present.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description mentions a close button and finalizing state but doesn't align with the actual changes, which focus on displaying a spinner during session enhancement/finalization. Update the description to accurately reflect that the PR adds a finalizing spinner indicator to tabs and sidebar timeline items when sessions are enhancing or finalizing.
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.
Title check ❓ Inconclusive The title 'feat/tab-close-button' is vague and doesn't clearly communicate the main changes; the PR actually adds a finalizing spinner state to tabs, not just a close button. Consider a more descriptive title that reflects the primary change, such as 'Add finalizing spinner state to tabs' or 'Display session enhancement status in tabs'.
✨ 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 feat/tab-close-button

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

🧹 Nitpick comments (1)
apps/desktop/src/components/main/body/sessions/index.tsx (1)

39-43: Consider extracting spinner state logic to a shared hook.

The pattern of computing isEnhancing, isFinalizing, and showSpinner is duplicated in timeline/item.tsx (lines 42-48). Extracting this to a reusable hook like useSessionSpinnerState(sessionId) would reduce duplication and improve maintainability.

Example implementation:

// In a shared hooks file
export function useSessionSpinnerState(sessionId: string | null) {
  const sessionMode = useListener((state) =>
    sessionId ? state.getSessionMode(sessionId) : "inactive"
  );
  const isEnhancing = useIsSessionEnhancing(sessionId ?? "");
  const isFinalizing = sessionMode === "finalizing";
  const showSpinner = isFinalizing || isEnhancing;
  
  return { isEnhancing, isFinalizing, showSpinner };
}
📜 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 d795e36 and 73a2372.

📒 Files selected for processing (4)
  • apps/desktop/src/components/main/body/sessions/index.tsx (2 hunks)
  • apps/desktop/src/components/main/body/shared.tsx (4 hunks)
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx (3 hunks)
  • apps/desktop/src/hooks/useEnhancedNotes.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/desktop/src/hooks/useEnhancedNotes.ts
**/*

📄 CodeRabbit inference engine (AGENTS.md)

Format using dprint fmt from the root. Do not use cargo fmt.

Files:

  • apps/desktop/src/hooks/useEnhancedNotes.ts
  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/components/main/body/sessions/index.tsx
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
**/*.{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.
Never do manual state management for form/mutation. Use useForm from tanstack-form and useQuery/useMutation from tanstack-query for 99% cases.

Files:

  • apps/desktop/src/hooks/useEnhancedNotes.ts
  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/components/main/body/sessions/index.tsx
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
**/*.{ts,tsx,rs,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

By default, avoid writing comments at all. If you write one, it should be about 'Why', not 'What'.

Files:

  • apps/desktop/src/hooks/useEnhancedNotes.ts
  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/components/main/body/sessions/index.tsx
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: If there are many classNames with conditional logic, use cn (import from @hypr/utils). Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/desktop/src/hooks/useEnhancedNotes.ts
  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/components/main/body/sessions/index.tsx
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
🧬 Code graph analysis (4)
apps/desktop/src/hooks/useEnhancedNotes.ts (2)
apps/desktop/src/store/zustand/ai-task/task-configs/index.ts (1)
  • createTaskId (60-65)
apps/desktop/src/contexts/ai-task.tsx (1)
  • useAITask (49-60)
apps/desktop/src/components/main/body/shared.tsx (1)
extensions/shared/types/hypr-extension.d.ts (1)
  • Spinner (425-425)
apps/desktop/src/components/main/body/sessions/index.tsx (3)
apps/desktop/src/hooks/useEnhancedNotes.ts (1)
  • useIsSessionEnhancing (176-195)
apps/desktop/src/components/main/body/shared.tsx (1)
  • TabItemBase (38-162)
extensions/calendar/components/icons.tsx (1)
  • StickyNoteIcon (86-104)
apps/desktop/src/components/main/sidebar/timeline/item.tsx (3)
apps/desktop/src/contexts/listener.tsx (1)
  • useListener (37-49)
apps/desktop/src/hooks/useEnhancedNotes.ts (1)
  • useIsSessionEnhancing (176-195)
extensions/shared/types/hypr-extension.d.ts (1)
  • Spinner (425-425)
⏰ 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). (6)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: desktop_ci (linux, depot-ubuntu-24.04-8)
  • GitHub Check: desktop_ci (linux, depot-ubuntu-22.04-8)
  • GitHub Check: fmt
🔇 Additional comments (3)
apps/desktop/src/hooks/useEnhancedNotes.ts (1)

176-195: LGTM! Clean implementation of enhancement status check.

The function correctly aggregates AI task statuses for all enhanced notes in a session. The use of useMemo to derive task IDs is appropriate for performance, and the logic properly handles edge cases (null/empty arrays).

apps/desktop/src/components/main/body/shared.tsx (1)

110-112: LGTM! Clean conditional rendering for finalizing state.

The three-way conditional properly handles finalizing, active, and default icon states. The spinner provides clear visual feedback during enhancement operations.

apps/desktop/src/components/main/sidebar/timeline/item.tsx (1)

185-197: LGTM! Layout properly accommodates the loading spinner.

The horizontal flex layout with flex-shrink-0 on the spinner ensures proper spacing and alignment. The min-w-0 on the content container correctly enables text truncation when space is constrained.

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

🧹 Nitpick comments (1)
apps/desktop/src/components/main/body/shared.tsx (1)

110-119: Consider: Close button behavior during finalization.

The current implementation allows users to close tabs while they're finalizing (close button appears on hover even when finalizing={true}). If this is intentional to give users control, the implementation is correct.

However, if finalization should prevent closing, you may want to conditionally disable the close button:

 <div
   className={cn([
     "absolute inset-0 flex items-center justify-center transition-opacity duration-200",
-    isHovered ? "opacity-100" : "opacity-0",
+    isHovered && !finalizing ? "opacity-100" : "opacity-0",
   ])}
 >
📜 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 73a2372 and c9ab57d.

📒 Files selected for processing (4)
  • apps/desktop/src/components/main/body/sessions/index.tsx (2 hunks)
  • apps/desktop/src/components/main/body/shared.tsx (4 hunks)
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx (3 hunks)
  • apps/desktop/src/hooks/useEnhancedNotes.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
  • apps/desktop/src/components/main/body/sessions/index.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

Format using dprint fmt from the root. Do not use cargo fmt.

Files:

  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/hooks/useEnhancedNotes.ts
**/*.{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.
Never do manual state management for form/mutation. Use useForm from tanstack-form and useQuery/useMutation from tanstack-query for 99% cases.

Files:

  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/hooks/useEnhancedNotes.ts
**/*.{ts,tsx,rs,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

By default, avoid writing comments at all. If you write one, it should be about 'Why', not 'What'.

Files:

  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/hooks/useEnhancedNotes.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: If there are many classNames with conditional logic, use cn (import from @hypr/utils). Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/desktop/src/components/main/body/shared.tsx
  • apps/desktop/src/hooks/useEnhancedNotes.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/desktop/src/hooks/useEnhancedNotes.ts
🧬 Code graph analysis (2)
apps/desktop/src/components/main/body/shared.tsx (1)
extensions/shared/types/hypr-extension.d.ts (1)
  • Spinner (425-425)
apps/desktop/src/hooks/useEnhancedNotes.ts (2)
apps/desktop/src/store/zustand/ai-task/task-configs/index.ts (1)
  • createTaskId (60-65)
apps/desktop/src/contexts/ai-task.tsx (1)
  • useAITask (49-60)
⏰ 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). (6)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
  • GitHub Check: desktop_ci (linux, depot-ubuntu-24.04-8)
  • GitHub Check: desktop_ci (linux, depot-ubuntu-22.04-8)
🔇 Additional comments (1)
apps/desktop/src/hooks/useEnhancedNotes.ts (1)

176-195: LGTM! Clean implementation of the enhancing state hook.

The hook correctly:

  • Retrieves enhanced note IDs for the session
  • Maps them to task IDs using the established pattern
  • Uses useMemo to optimize the task ID array computation
  • Leverages useAITask to check if any enhancement task is generating
  • Handles undefined enhancedNoteIds with the || [] fallback

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/components/main/body/shared.tsx (1)

60-72: Critical: Undefined variable reference.

Line 61 references isEmptyTab, but this prop was removed in the refactoring. This will cause a ReferenceError at runtime and break the context menu functionality.

🔎 Proposed fix

If the isEmptyTab check is no longer needed, remove it:

  const contextMenu = !active
-    ? selected && !isEmptyTab
+    ? selected
      ? [{ id: "close-tab", text: "Close", action: handleCloseThis }]
      : [
          { id: "close-tab", text: "Close", action: handleCloseThis },

Alternatively, if different logic is needed based on the finalizing state or another condition, please clarify the intended behavior.

🧹 Nitpick comments (1)
apps/desktop/src/components/main/sidebar/timeline/item.tsx (1)

4-4: Remove unused import.

ContextMenuItem is imported but never used in this file.

🔎 Proposed fix
-import { ContextMenuItem } from "@hypr/ui/components/ui/context-menu";
📜 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 c9ab57d and 0b187fb.

📒 Files selected for processing (2)
  • apps/desktop/src/components/main/body/shared.tsx (4 hunks)
  • apps/desktop/src/components/main/sidebar/timeline/item.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

Format using dprint fmt from the root. Do not use cargo fmt.

Files:

  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
  • apps/desktop/src/components/main/body/shared.tsx
**/*.{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.
Never do manual state management for form/mutation. Use useForm from tanstack-form and useQuery/useMutation from tanstack-query for 99% cases.

Files:

  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
  • apps/desktop/src/components/main/body/shared.tsx
**/*.{ts,tsx,rs,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

By default, avoid writing comments at all. If you write one, it should be about 'Why', not 'What'.

Files:

  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
  • apps/desktop/src/components/main/body/shared.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: If there are many classNames with conditional logic, use cn (import from @hypr/utils). Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/desktop/src/components/main/sidebar/timeline/item.tsx
  • apps/desktop/src/components/main/body/shared.tsx
🧬 Code graph analysis (1)
apps/desktop/src/components/main/body/shared.tsx (1)
extensions/shared/types/hypr-extension.d.ts (1)
  • Spinner (425-425)
⏰ 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 (3)
apps/desktop/src/components/main/sidebar/timeline/item.tsx (1)

177-189: LGTM!

The layout refactoring correctly implements the spinner display with proper flex behaviors. The min-w-0 on the content wrapper enables text truncation within the flex container, and flex-shrink-0 prevents the spinner from being compressed.

apps/desktop/src/components/main/body/shared.tsx (2)

24-24: LGTM!

The prop rename from isEmptyTab to finalizing is clear and aligns well with the new functionality.

Also applies to: 42-42


114-123: LGTM!

The rendering logic correctly prioritizes the finalizing state over the active state, and the spinner size matches the icon container dimensions.

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.

3 participants