-
Notifications
You must be signed in to change notification settings - Fork 58
feat: add subtle idle hints in Playground and Task detail #2532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
|
Finished running flow.
|
||||||||||||||||||
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis PR introduces idle-nudge UX features to encourage user interaction. PlaygroundStageInput gains idle state tracking with localStorage persistence and visual feedback on inactivity. A new EditInStudioLinkWithHint component provides reusable idle-hint functionality. The playground page implements interaction tracking and hint rendering, and task headers now use the new hint component. Changes
Sequence DiagramsequenceDiagram
participant User
participant Component as PlaygroundStageInput/<br/>EditInStudioLinkWithHint
participant Timer as Idle Timer
participant Storage as localStorage
participant UI as Hint Renderer
User->>Component: Interact (input/click/key)
Component->>Timer: Reset idle timer
Component->>Storage: Update lastInteractionAt
Component->>UI: Hide hint (if visible)
Note over Timer: idleNudgeDelayMs passes<br/>(default: varies per component)
Timer->>Component: Fire idle timeout
Component->>Storage: Check hasShownEver flag
alt First time or reset
Storage-->>Component: false
Component->>Storage: Set hasShownEver = true
Component->>UI: Fade in idle hint
Component->>UI: Apply pulsing animation
else Already shown
Storage-->>Component: true
Component->>UI: Hint remains hidden
end
User->>Component: Resume interaction
Component->>Timer: Reset idle timer
Component->>UI: Fade out hint
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces subtle idle hints to guide first-time users toward key actions in the Playground and Task detail pages. The hints appear after 8 seconds of inactivity and are shown only once per user via localStorage flags.
- Adds an idle hint in
/playgroundthat encourages users to "Run a sample app" - Adds an idle hint in
/tasks/[taskId]near "Edit in Studio" with text "Explore how this app works" - Implements persistence logic to ensure hints only appear on first visit and never after a run has been executed
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
apps/studio.giselles.ai/components/task/task-header.tsx |
Replaces inline Link component with new EditInStudioLinkWithHint component |
apps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx |
New component implementing idle hint logic for Edit in Studio button |
apps/studio.giselles.ai/app/(main)/playground/page.client.tsx |
Adds interaction tracking, localStorage persistence for run state, and renders idle hint via renderIdleHint prop |
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx |
Implements comprehensive idle hint logic with localStorage checks, interaction tracking, and visual nudge animations |
Comments suppressed due to low confidence (1)
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx:1
- The overflow property changed from 'overflow-hidden' to 'overflow-visible'. This change appears necessary for the idle hint positioning, but may have unintended side effects if content was previously being clipped intentionally. Consider adding a comment explaining why overflow-visible is needed here.
"use client";
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| className={`pointer-events-none absolute right-[-100px] top-[-12px] z-20 flex flex-col items-start text-left text-[#B8E8F4] origin-top-right rotate-[20deg] translate-y-[6px] transition-opacity duration-300 ease-out ${ | ||
| isVisible ? "opacity-100" : "opacity-0" | ||
| }`} |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Template literal with ternary for className should use clsx for consistency with the rest of the codebase. The clsx utility is already imported and used elsewhere in this file for similar conditional styling.
| className={`pointer-events-none absolute right-[-100px] top-[-12px] z-20 flex flex-col items-start text-left text-[#B8E8F4] origin-top-right rotate-[20deg] translate-y-[6px] transition-opacity duration-300 ease-out ${ | |
| isVisible ? "opacity-100" : "opacity-0" | |
| }`} | |
| className={clsx( | |
| "pointer-events-none absolute right-[-100px] top-[-12px] z-20 flex flex-col items-start text-left text-[#B8E8F4] origin-top-right rotate-[20deg] translate-y-[6px] transition-opacity duration-300 ease-out", | |
| isVisible ? "opacity-100" : "opacity-0", | |
| )} |
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/studio.giselles.ai/app/(main)/playground/page.client.tsx (1)
499-527: Unreachable code due to duplicate condition.Line 499 (
apps.length === 0) and line 523 (apps.length === 0) have the same condition. The second branch (lines 523-526) will never execute.🔎 Proposed fix
The second condition likely intended to check if the filtered apps are empty when there's a search query:
- ) : apps.length === 0 ? ( - <p className="text-sm text-muted-foreground max-w-[960px] mx-auto w-full"> - No apps match your search. - </p> + ) : apps.length === 0 && appSearchQuery.trim().length > 0 ? ( + <p className="text-sm text-muted-foreground max-w-[960px] mx-auto w-full px-4"> + No apps match your search. + </p>However, since
data.appsis checked in the first condition, you may need to restructure the logic to differentiate between "no apps at all" vs "no apps matching search".
♻️ Duplicate comments (1)
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx (1)
17-21: Duplicate localStorage key constants.These constants are also defined in
page.client.tsx. Centralizing them would improve maintainability. Already flagged in the page.client.tsx review.
🧹 Nitpick comments (5)
apps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx (3)
14-31: DuplicatedIdleHintArrowcomponent across files.This exact SVG component is also defined in
page.client.tsx(lines 37-54). Consider extracting it into a shared module to avoid duplication and ensure consistency.🔎 Suggested extraction
Create a shared component, e.g.,
apps/studio.giselles.ai/components/idle-hint-arrow.tsx:export function IdleHintArrow({ className }: { className?: string }) { return ( <svg className={className} width="77" height="127" viewBox="0 0 77 127" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" > <path d="M76.1042 19.8128C76.0142..." fill="currentColor" /> </svg> ); }Then import it in both
edit-in-studio-link-with-hint.tsxandpage.client.tsx.
17-21: Duplicated localStorage key constant.
PLAYGROUND_HAS_RUN_ANY_APP_KEYis defined inplayground-stage-input.tsx(line 21) andpage.client.tsx(line 56) with the same value. While not used in this file, the pattern suggests localStorage keys could be centralized to prevent drift.Also applies to: 56-56
44-48: Consider consolidating related boolean states.Five separate
useStatecalls for hint visibility logic (lines 44-48) increases cognitive overhead. These could be combined into a single state object or a reducer if the logic grows more complex.apps/studio.giselles.ai/app/(main)/playground/page.client.tsx (1)
56-56: Duplicate localStorage key definition.
PLAYGROUND_HAS_RUN_ANY_APP_KEYis also defined inplayground-stage-input.tsx(line 21) with the same value. Centralize these constants to avoid potential inconsistencies during future maintenance.🔎 Suggested centralization
Create a shared constants file, e.g.,
apps/studio.giselles.ai/app/(main)/playground/constants.ts:export const PLAYGROUND_HAS_RUN_ANY_APP_KEY = "giselle:playground:hasRunAnyApp"; export const PLAYGROUND_HAS_VISITED_KEY = "giselle:playground:hasVisited"; export const PLAYGROUND_HAS_SHOWN_IDLE_HINT_KEY = "giselle:playground:hasShownIdleHint";Then import in both files.
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx (1)
136-136: Redundant boolean coercion.
Boolean(hasRunAnyApp)is unnecessary sincehasRunAnyAppis already a boolean (boolean | undefined). The||operator handles falsy values correctly.🔎 Simplified condition
- if (hasRunAnyAppEver || Boolean(hasRunAnyApp)) return false; + if (hasRunAnyAppEver || hasRunAnyApp) return false;
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsxapps/studio.giselles.ai/components/task/task-header.tsx
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Favor clear, descriptive names and type annotations over clever tricks
If you need a multi-paragraph comment, refactor until intent is obvious
**/*.{ts,tsx,js,jsx}: Use async/await and proper error handling
Variables and functions should use camelCase naming
Booleans and functions should useis,has,can,shouldprefixes
Function names should clearly indicate purpose
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development-guide.mdc)
**/*.{ts,tsx}: MUST runpnpm biome check --write [filename]after EVERY code modification
All code changes must be formatted using Biome before being committed
Use Biome for formatting with tab indentation and double quotes
Follow organized imports pattern (enabled in biome.json)
Use TypeScript for type safety; avoidanytypes
Use Next.js patterns for web applications
Use async/await for asynchronous code rather than promises
Error handling: use try/catch blocks and propagate errors appropriately
Use kebab-case for all filenames
Use PascalCase for React components and classes
Use camelCase for variables, functions, and methods
Use prefixes likeis,has,can,shouldfor boolean variables (e.g.,isEnabled,hasPermission)
Use prefixes likeis,has,can,shouldfor boolean functions (e.g.,isTriggerRequiringCallsign(),hasActiveSubscription()) instead of imperative verbs
Use verbs or verb phrases for function naming that clearly indicate purpose (e.g.,calculateTotalPrice(), notprocess())Use PascalCase for React component and class names
Use TypeScript and avoid
any
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/development-guide.mdc)
Use functional components with React hooks
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/*.{js,ts,tsx,jsx,py,java,cs,cpp,c,go,rb,php,swift,kt,scala,rs,dart}
📄 CodeRabbit inference engine (.cursor/rules/language-support.mdc)
Write all code comments in English
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/*
📄 CodeRabbit inference engine (.cursor/rules/naming-guide.mdc)
Use kebab-case for file names (e.g.,
user-profile.ts,api-client.tsx)
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/naming-guide.mdc)
**/*.{js,ts,jsx,tsx}: Use camelCase for variable names, functions, and methods
Use verbs or verb phrases for function names to clearly indicate what the function does (e.g.,calculateTotalPrice(),validateUserInput())
Use nouns or noun phrases for variable names to describe what the variable represents
Use boolean prefixes (is,has,can,should) for boolean variables and functions returning boolean values (e.g.,isEnabled,hasPermission,isTriggerRequiringCallsign())
**/*.{js,ts,jsx,tsx}: Runpnpm biome check --write [filename]after every code change
All code must be formatted with Biome before commit
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/{components,pages}/**/*.{tsx,ts}
📄 CodeRabbit inference engine (AGENTS.md)
Components should use React hooks and Next.js patterns
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/*.{ts,tsx,js,jsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Files should use kebab-case naming
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (AGENTS.md)
Components should use PascalCase naming
Files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
🧠 Learnings (21)
📚 Learning: 2025-11-25T03:05:31.051Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-11-25T03:05:31.051Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/workspace-tour/workspace-tour.tsx : Update the `TourGlobalStyles` component in `workspace-tour.tsx` for animation changes
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-11-25T03:05:31.051Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-11-25T03:05:31.051Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/workspace-tour/workspace-tour.tsx : Import new images at the top of `workspace-tour.tsx` and update the `imageSrc` prop in the appropriate step component to use the new image
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-12-03T05:18:36.138Z
Learnt from: shige
Repo: giselles-ai/giselle PR: 2344
File: apps/studio.giselles.ai/app/stage/ui/navigation-rail/navigation-rail-expanded.tsx:14-20
Timestamp: 2025-12-03T05:18:36.138Z
Learning: In apps/studio.giselles.ai/app/stage/ui/navigation-rail/navigation-rail-expanded.tsx, "nav-action-history" is intentionally included in stageOnlyItemIds to hide it when the stage flag is disabled because the link destination is not yet implemented, even though it appears in the "Studio - Build Apps" section of navigation-items.ts.
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-11-25T03:07:19.740Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/new-editor/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:19.740Z
Learning: Applies to internal-packages/workflow-designer-ui/src/new-editor/**/*.{ts,tsx} : Replace context-wide reads from `src/editor/v2` with fine-grained selectors against the Zustand store
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-11-25T03:07:19.740Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/new-editor/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:19.740Z
Learning: Applies to internal-packages/workflow-designer-ui/src/new-editor/**/*.{ts,tsx} : Lift actions into the store (e.g., `updateNode`) and call them from components needing mutations instead of passing mutation callbacks as props
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-11-25T03:05:31.051Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-11-25T03:05:31.051Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/workspace-tour/assets/** : Replace GIFs and images in the `assets/` directory and update imports and references in `workspace-tour.tsx`
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsx
📚 Learning: 2025-11-25T03:07:07.498Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/editor/properties-panel/trigger-node-properties-panel/providers/github-trigger/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:07.498Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/properties-panel/trigger-node-properties-panel/providers/github-trigger/components/{callsign,labels}*.tsx : Hide the 'Back' button in `input-callsign` and `input-labels` steps during reconfiguration mode to simplify UX.
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsx
📚 Learning: 2025-11-25T03:05:31.051Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-11-25T03:05:31.051Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/workspace-tour/steps.ts : Modify the `tourSteps` array in `steps.ts` to change text, target elements, or placement for the workspace tour
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsx
📚 Learning: 2025-11-25T03:05:31.051Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-11-25T03:05:31.051Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/workspace-tour/workspace-tour.tsx : Modify CSS positioning in step components by adjusting Tailwind classes like `mt-[140px]` (margin-top) and `mr-8` (margin-right) in `workspace-tour.tsx`
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-11-25T03:05:31.051Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-11-25T03:05:31.051Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/workspace-tour/workspace-tour.tsx : Modify the `CARD_STYLES` constants in `workspace-tour.tsx` to change step styling
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsx
📚 Learning: 2025-06-23T12:31:58.286Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-06-23T12:31:58.286Z
Learning: The workspace tour feature in `internal-packages/workflow-designer-ui/src/editor/workspace-tour/` is implemented using React and relies on a `TourStep` interface that defines each step's target (CSS selector), title, content, and placement.
Applied to files:
apps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-06-23T12:31:52.270Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/development-guide.mdc:0-0
Timestamp: 2025-06-23T12:31:52.270Z
Learning: Use functional components with React hooks for building React components.
Applied to files:
apps/studio.giselles.ai/app/(main)/playground/page.client.tsx
📚 Learning: 2025-11-25T03:05:21.219Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/development-guide.mdc:0-0
Timestamp: 2025-11-25T03:05:21.219Z
Learning: Applies to **/*.tsx : Use functional components with React hooks
Applied to files:
apps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx
📚 Learning: 2025-11-25T03:06:27.023Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:06:27.023Z
Learning: Applies to **/{components,pages}/**/*.{tsx,ts} : Components should use React hooks and Next.js patterns
Applied to files:
apps/studio.giselles.ai/app/(main)/playground/page.client.tsx
📚 Learning: 2025-11-25T03:07:07.498Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/editor/properties-panel/trigger-node-properties-panel/providers/github-trigger/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:07.498Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/properties-panel/trigger-node-properties-panel/providers/github-trigger/**/*.tsx : Wrap all state updates in `startTransition` for consistent UI behavior during configuration and reconfiguration flows.
Applied to files:
apps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx
📚 Learning: 2025-11-25T03:07:19.740Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/new-editor/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:19.740Z
Learning: Applies to internal-packages/workflow-designer-ui/src/new-editor/**/*.{ts,tsx} : In selectors, avoid selecting full state; select by id (e.g., `s.nodesById[id]`) and derive small, stable shapes
Applied to files:
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx
📚 Learning: 2025-11-25T03:07:19.740Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/new-editor/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:19.740Z
Learning: Applies to internal-packages/workflow-designer-ui/src/new-editor/components/**/*.{ts,tsx} : Wrap presentational components in `React.memo` only when their props are stable and derived via selectors
Applied to files:
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
📚 Learning: 2025-08-14T02:34:56.156Z
Learnt from: gentamura
Repo: giselles-ai/giselle PR: 1590
File: internal-packages/workflow-designer-ui/src/editor/hooks/use-keyboard-shortcuts.ts:79-80
Timestamp: 2025-08-14T02:34:56.156Z
Learning: In the giselle codebase, React namespace types (React.KeyboardEvent, React.MouseEvent, React.ChangeEvent, etc.) are used consistently throughout the project without importing React itself, and verbatimModuleSyntax is not enabled in TypeScript configs. This pattern should be maintained for consistency rather than suggesting isolated changes to direct type imports.
Applied to files:
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx
📚 Learning: 2025-11-25T03:07:07.498Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/editor/properties-panel/trigger-node-properties-panel/providers/github-trigger/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:07.498Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/properties-panel/trigger-node-properties-panel/providers/github-trigger/**/*.tsx : Manage setup wizard state progression through steps: `select-event` → `select-repository` → `confirm-repository` → optionally `input-callsign` → optionally `input-labels` → `configured`.
Applied to files:
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx
📚 Learning: 2025-11-25T03:07:19.740Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: internal-packages/workflow-designer-ui/src/new-editor/AGENTS.md:0-0
Timestamp: 2025-11-25T03:07:19.740Z
Learning: Applies to internal-packages/workflow-designer-ui/src/new-editor/components/node/**/*.{ts,tsx} : In Node component, select exactly the node data, derived connection ids, and UI flags needed, providing a custom equality function that combines strict equality for references with `shallow` for arrays
Applied to files:
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx
📚 Learning: 2025-11-25T03:05:31.051Z
Learnt from: CR
Repo: giselles-ai/giselle PR: 0
File: .cursor/rules/edit-workspace-tour.mdc:0-0
Timestamp: 2025-11-25T03:05:31.051Z
Learning: Applies to internal-packages/workflow-designer-ui/src/editor/workspace-tour/{steps.ts,workspace-tour.tsx} : Add new tour steps to the `tourSteps` array in `steps.ts`, create corresponding components in `workspace-tour.tsx` following the pattern of existing steps, and add the new step to the switch statement in the main `WorkspaceTour` component
Applied to files:
apps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
🧬 Code graph analysis (3)
apps/studio.giselles.ai/components/task/task-header.tsx (1)
apps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx (1)
EditInStudioLinkWithHint(33-143)
apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx (2)
apps/studio.giselles.ai/app/(main)/playground/types.ts (1)
StageApp(8-24)apps/studio.giselles.ai/app/(main)/stores/stage-app-selection-store.ts (1)
StageAppSelectionScope(8-8)
apps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx (1)
internal-packages/ui/components/status-badge.tsx (1)
StatusBadge(42-90)
⏰ 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). (2)
- GitHub Check: Cursor Bugbot
- GitHub Check: check
🔇 Additional comments (18)
apps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx (3)
68-83: Timer may fire after expected conditions change.The timeout fires after
IDLE_HINT_DELAY_MSbut only checkslastInteractionAtinside the callback (line 71). IfhasShownOnceorhasShownEverchange during the delay, the early return on line 69 won't prevent the scheduled callback from running. However, the inner check ensures correctness. The logic is sound but slightly indirect.
95-103: Global event listeners are cleaned up correctly.The
keydownandfocusinlisteners with{ capture: true }are properly removed in the cleanup function. ThemarkInteractioncallback is stable due touseCallbackwith no dependencies.
105-143: Component renders correctly with proper accessibility attributes.The hint container uses
aria-hidden="true"andpointer-events-none, which is appropriate for decorative hints. The link includestarget="_blank"withrel="noreferrer"for security.apps/studio.giselles.ai/components/task/task-header.tsx (2)
10-10: Import added correctly.The relative import path is correct for the sibling file location.
110-116: Clean integration of the new hint component.The
EditInStudioLinkWithHintcomponent is properly integrated with the requiredworkspaceId,leftIcon, and optionalhintTextprops. The icon styling is preserved with transition classes for hover state consistency with the parent group.apps/studio.giselles.ai/app/(main)/playground/page.client.tsx (5)
264-276: Initialization and interaction tracking are correct.The
markInteractioncallback increments a tick counter to signal interactions. TheuseEffectcorrectly initializeshasRunAnyAppfrom localStorage with a fallback totrueif storage is unavailable (disabling hints gracefully).
278-326: Run submission correctly persists state and marks interaction.The handler calls
markInteraction()first, then persists to localStorage. The catch block setshasRunAnyApptotruein both success and error cases, ensuring the hint won't reappear. Dependency array correctly includesmarkInteraction.
334-339: Interaction capture events applied correctly.Using
onPointerDownCapture,onKeyDownCapture, andonFocusCaptureensures interactions are detected during the capture phase before any child handlers prevent propagation.
340-340: Minor:overflow-visibleimplied by parent, confirm intentional.The class change to
overflow-visibleon this container allows the idle hint to render outside bounds. Verify this doesn't cause unintended visual overflow in other scenarios.
363-375: Idle hint render prop implemented correctly.The
renderIdleHintfunction receivesisVisibleand returns the hint UI with appropriate transitions andaria-hidden="true".apps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx (8)
28-40: Extended props interface is well-typed.The new optional props (
interactionTick,idleNudgeDelayMs,renderIdleHint,hasRunAnyApp) are properly typed with sensible defaults. TheReactNodereturn type forrenderIdleHintprovides flexibility for the caller.
83-113: localStorage initialization with proper fallbacks.The effect correctly handles unavailable localStorage by setting conservative defaults that disable hints. Setting
hasVisitedimmediately after reading ensures the first-visit flag works correctly.
120-123: ExternalinteractionTicktriggers internal interaction mark.The effect syncs the external
interactionTickprop to the internalmarkInteractioncallback. This allows the parent component to signal interactions that occur outside this component.
125-145: Memoized conditions for idle nudge and hint visibility.
shouldShowIdleNudgeandshouldAllowIdleHintcorrectly gate when the nudge/hint can appear based on running state, input presence, first visit, and prior display. The dependency arrays are complete.
147-160: Timer correctly resets on dependency changes.The effect cleans up the previous timeout when
lastInteractionAt,shouldShowIdleNudge, oridleNudgeDelayMschange, preventing stale timers from firing.
162-180: RAF pattern for fade-in transition stability.Using
requestAnimationFramebefore settingisIdleHintVisibleensures the browser paints the initialopacity: 0state, allowing the CSS transition to animate smoothly. The localStorage update inside the RAF could theoretically be moved outside for clarity, but the current approach works correctly.
182-222: Interaction-aware handler wrappers implemented consistently.All relevant handlers (input change, key down, attachment click, submit, drop, remove file) are wrapped to call
markInteraction()before delegating. The wrapper pattern usingParameters<typeof handler>preserves type safety.
229-233: Idle nudge visual feedback applied correctly.The conditional
ring-1 ring-blue-muted/30class on the input container andmotion-safe:animate-pulseon the submit button and hint text provide subtle visual cues during idle nudge. Usingmotion-safe:respects user's reduced motion preferences.Also applies to: 286-290, 344-348
|
## 🔍 QA Testing Assistant by Giselle ### 📋 Manual QA Checklist Based on the changes in this PR, here are the key areas to test manually:
### ✨ Prompt for AI Agents Use the following prompts with Cursor or Claude Code to automate E2E testing: 📝 E2E Test Generation Prompt``` Based on the context, test scenarios, and implementation details below, please generate the necessary Playwright test code. 1. Context SummaryThe PR introduces a new feature: subtle "idle hints" on two key pages to guide new users towards their next action. These hints are non-blocking and appear only after a period of user inactivity.
2. Test ScenariosCreate two separate test files: A. Playground Page (
|
| } | ||
| }); | ||
| return () => window.cancelAnimationFrame(raf); | ||
| }, [isIdleNudgeActive, renderIdleHint, shouldAllowIdleHint]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idle hint immediately hides after showing due to circular state
The idle hint in the playground will flash briefly and immediately disappear. Inside the requestAnimationFrame callback, setHasShownIdleHintEver(true) is called, which causes shouldAllowIdleHint (computed via useMemo) to become false on the next render. Since shouldAllowIdleHint is a dependency of this effect, it re-runs immediately and the condition !shouldAllowIdleHint becomes true, triggering setIsIdleHintVisible(false). The hint is visible for only one render cycle before being hidden. The task detail component avoids this by keeping visibility state separate from the "has shown" flags.
Additional Locations (1)
shige
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, we've decided not to merge this PR.
Here are the two reasons:
- If we need to show this hint on /playground, it means users wouldn't know what to do, which indicates that the first view composition of the /playground page has failed.
- We would need to adjust the animation code that handles localStorage.
Summary
This PR adds subtle, non-blocking idle hints to help users discover next actions.
Changes
Hint conditions
/playground
Show when
localStorage: giselle:playground:hasVisitedis missing)localStorage: giselle:playground:hasRunAnyAppis missing)localStorage: giselle:playground:hasShownIdleHintis missing)Hide when
/tasks/[taskId]
Show when
localStorage: giselle:tasks:editInStudioHintShownis missing)Hide when
Files
apps/studio.giselles.ai/app/(main)/playground/page.client.tsxapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsxapps/studio.giselles.ai/components/task/task-header.tsxapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsxPR Type
Enhancement
Description
Add idle hint system to Playground showing "Run a sample app" after 8 seconds of inactivity
Add idle hint to Task detail page showing "Explore how this app works" near Edit in Studio button
Implement localStorage persistence to show hints only once per user
Add visual feedback with animated arrow SVG and subtle pulse animation on idle state
Diagram Walkthrough
flowchart LR User["User visits page"] Idle["8 seconds idle<br/>no interaction"] Check{"localStorage<br/>hint shown?"} Show["Show hint with<br/>arrow animation"] Interact["User interaction<br/>keydown/click"] Hide["Hide hint"] Persist["Mark hint as<br/>shown in storage"] User --> Idle Idle --> Check Check -->|No| Show Check -->|Yes| Hide Show --> Persist Show --> Interact Interact --> HideFile Walkthrough
playground-stage-input.tsx
Implement idle hint system with interaction trackingapps/studio.giselles.ai/app/(main)/components/stage-input/playground-stage-input.tsx
first visit, hint display, and app runs
user actions (typing, file upload, app selection, submit)
prevent visual "pop"
when idle nudge is active
page.client.tsx
Add idle hint UI and interaction propagation to Playgroundapps/studio.giselles.ai/app/(main)/playground/page.client.tsx
IdleHintArrowSVG component for visual idle hint indicatorhasRunAnyApptracking to parentcomponent
PlaygroundStageInputcomponent
interactions
positioned near input area
edit-in-studio-link-with-hint.tsx
Create Edit in Studio link with idle hint wrapperapps/studio.giselles.ai/components/task/edit-in-studio-link-with-hint.tsx
functionality
display
IdleHintArrowSVG component with rotation and mirroring forright-to-left direction
appearance
interaction
task-header.tsx
Integrate idle hint component into Task headerapps/studio.giselles.ai/components/task/task-header.tsx
EditInStudioLinkWithHintcomponent
to new component
component
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.