Skip to content

Feature/alive onboarding demo - #23

Merged
dfed25 merged 10 commits into
mainfrom
feature/alive-onboarding-demo
Apr 26, 2026
Merged

dfed25 merged 10 commits into
mainfrom
feature/alive-onboarding-demo

Conversation

@dfed25

@dfed25 dfed25 commented Apr 26, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Interactive guided onboarding tour with visual highlighting and step-by-step instructions.
    • New "Guide me" and "What should I do next?" assistant buttons for contextual guidance.
    • Session-persistent progress tracking for onboarding workflows.
  • Bug Fixes

    • Improved error handling and fallback messages from chat responses.

dfed25 added 7 commits April 25, 2026 21:18
Add a proactive nudge, guided tour overlays, local interactive demo state changes, and widget-to-page context syncing so users get clear next-step onboarding on /embed-demo.

Made-with: Cursor
Convert the /embed-demo tour from passive next-clicks into required-action auto-advance with live checklist/app-state updates, and pass app state into embed chat so next-step guidance stays page-first and reliable.

Made-with: Cursor
Stabilize the dynamic header knowledge label across SSR/CSR and switch guided tour steps to explicit card-level targets with clearer highlight visuals so onboarding cues land on the right controls.

Made-with: Cursor
Allow the walkthrough to resume at the first incomplete step, add explicit demo reset controls, reduce overlay dimming, and stop external suggestion events from auto-opening the widget while preserving live status updates.

Made-with: Cursor
Move embed-demo initialization to deterministic SSR-safe defaults with deferred client hydration, and forward card attributes so explicit tour targets render consistently and the walkthrough continues past the first action.

Made-with: Cursor
Emit structured UI actions from embed chat responses and execute them on /embed-demo so guide and next-step intents can drive highlights and tour step entry points in a reliable, state-aware flow.

Made-with: Cursor
Add a shared next-action engine, intelligent intent guards, live widget status/events, activity feed updates, and local assistant fallbacks so the onboarding demo feels proactive and remains reliable without LLM responses.

Made-with: Cursor
@coderabbitai

coderabbitai Bot commented Apr 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@dfed25 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 53 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 43 minutes and 53 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d0ab33a-b403-4bea-a504-7322c38821ae

📥 Commits

Reviewing files that changed from the base of the PR and between bfa5a92 and e875ad4.

📒 Files selected for processing (8)
  • src/app/api/embed/chat/route.ts
  • src/app/embed-demo/layout.tsx
  • src/app/embed-demo/page.tsx
  • src/components/EmbeddedRunbookAssistant.tsx
  • src/components/demo/FeatureCard.tsx
  • src/lib/embedDemoKnowledge.ts
  • src/lib/embedDemoNextAction.ts
  • src/lib/embedNorthstarChat.ts
📝 Walkthrough

Walkthrough

This PR implements an interactive onboarding flow for the embedded runbook demo by adding client-side app state tracking (GitHub integration, API key, workflow, deployment status), server-side UI action derivation from user intent patterns and app state, localStorage persistence, a guided tour with progress gating, and context-aware assistance responses tailored to user prerequisites.

Changes

Cohort / File(s) Summary
Chat API and App State Infrastructure
src/app/api/embed/chat/route.ts, src/lib/embedDemoNextAction.ts
Adds optional appState input to chat API; derives uiAction from message patterns and state flags (github/api/workflow/deployed); normalizes page context with stringified app state; new state/action types for demo onboarding pipeline.
Demo Page Transformation
src/app/embed-demo/page.tsx
Major refactor: adds app state for integration/API key/workflow/deployment; persists to localStorage; implements multi-step guided tour (TOUR_STEPS) with overlay, scroll-into-view, completion gating, and success feedback; adds hover-driven feature detection via data-runbook-* attributes; replaces static sections with interactive onboarding checklist and activity feed; adds interrupt warnings for out-of-order actions.
Demo Layout and State Initialization
src/app/embed-demo/layout.tsx
Introduces firstSentence utility for truncating explanations; changes initialization from URL/storage-driven lazy setup to hardcoded defaults with deferred useEffect refresh; adds suppressHydrationWarning to reduce hydration mismatch noise.
Assistant Component and Event Integration
src/components/EmbeddedRunbookAssistant.tsx
Subscribes to multiple window events (active feature, suggestions, status, widget open, pulse); forwards appState and externalFeature to backend; dispatches runbook-ui-action events on response; adds "Guide me" and "What should I do next?" flows with suggestion routing; implements launcher button pulse animation; enhances error fallback with structured content.
Feature Card and Demo Knowledge
src/components/demo/FeatureCard.tsx, src/lib/embedDemoKnowledge.ts
FeatureCard now accepts arbitrary React.HTMLAttributes<HTMLElement> for flexible prop forwarding; embedDemoKnowledge adds context-aware next-step inference to tailor onboarding responses based on GitHub/API key/deployment status and dedicated command handlers for "connect github", "build workflow", and "deploy".

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant DemoPage as Demo Page<br/>(Frontend)
    participant ChatAPI as Chat API<br/>(Backend)
    participant Assistant as Assistant<br/>Component
    participant WindowEvents as Window Events<br/>(Pub/Sub)

    User->>DemoPage: Interacts with feature/button
    DemoPage->>DemoPage: Update appState<br/>(github/api/workflow/deployed)
    DemoPage->>DemoPage: Persist to localStorage
    DemoPage->>WindowEvents: Emit feature/status events
    
    User->>Assistant: Types message or clicks "Guide me"
    Assistant->>Assistant: Collect appState from DemoPage<br/>via window.__runbookAppState
    Assistant->>ChatAPI: Send message + appState<br/>+ externalFeature context
    
    ChatAPI->>ChatAPI: Regex pattern match on message
    ChatAPI->>ChatAPI: Inspect appState flags<br/>(github/api/workflow/deployed)
    ChatAPI->>ChatAPI: Derive uiAction<br/>{type, stepId, feature}
    ChatAPI-->>Assistant: Return response + uiAction
    
    Assistant->>WindowEvents: Dispatch runbook-ui-action<br/>event if uiAction present
    WindowEvents->>DemoPage: Receive uiAction event
    DemoPage->>DemoPage: Highlight/focus target step
    DemoPage->>DemoPage: Mark step complete if matched
    Assistant-->>User: Display chat response
Loading
sequenceDiagram
    participant Tour as Guided Tour<br/>(DemoPage)
    actor User
    participant Step as Tour Step<br/>(Overlay + Action)
    participant AppState as App State<br/>Tracker

    loop For each TOUR_STEP
        Tour->>Tour: Find target element<br/>by selector
        Tour->>Tour: Scroll into view +<br/>show overlay rectangle
        Tour->>Step: Display instructions &<br/>completion requirements
        
        User->>Step: Click action button<br/>(or performs prerequisite)
        
        alt Prerequisites not met
            Step-->>User: Show interrupt warning<br/>+ next required step
        else Prerequisites met
            Step->>AppState: Update state flag<br/>(e.g., githubConnected=true)
            AppState->>Tour: Emit completion event
            Tour->>Tour: Mark step complete<br/>+ show success feedback
        end
    end
    
    Tour->>Tour: All steps complete?
    alt Yes
        Tour-->>User: Display completion banner<br/>+ next action guidance
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #18: Modifies chat intent handling and page-context enrichment with overlapping changes to chat route and demo knowledge/intent detection logic.
  • PR #19: Updates embed chat contract and response shaping in the chat route to include structured response fields; main PR's uiAction/appState additions directly extend this contract.
  • PR #22: Overlaps across multiple embed/demo files (route.ts, layout, page, assistant, knowledge) with shared code-level changes to UX and context handling.

Poem

🐰 Hops through app state with glee,
Tour steps light the path to be,
Windows whisper, actions sing,
Onboarding's now a guided thing!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feature/alive onboarding demo' is vague and uses generic phrasing that doesn't clearly convey what the changeset accomplishes. Replace with a more descriptive title that captures the main feature: e.g., 'Add interactive onboarding flow to embed demo with guided tour and app state tracking' or similar.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/alive-onboarding-demo

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.

Comment thread src/app/embed-demo/page.tsx Fixed
Comment thread src/lib/embedDemoKnowledge.ts Fixed
dfed25 added 2 commits April 25, 2026 22:32
Remove sync setState calls from effects and derive the activity status line from current demo state so eslint react-hooks set-state-in-effect checks pass in CI.

Made-with: Cursor
Use crypto.getRandomValues for demo API key token suffixes and replace regex-based next-action context matching with safe string includes to avoid insecure randomness and potential ReDoS alerts.

Made-with: Cursor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (9)
src/app/embed-demo/page.tsx (2)

102-103: viewportHeight is captured once and not recomputed on resize.

viewportHeight = typeof window !== "undefined" ? window.innerHeight : 900 runs only at render time of the first mount and is reused at line 814 to position the tour panel. Resizing or rotating the device leaves the panel anchored to the original height. Either move this read into updateTourTarget (which already runs on resize) or track viewport size with a small effect.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/embed-demo/page.tsx` around lines 102 - 103, viewportHeight is read
once at mount and never updated, so the tour panel positioning (used with
currentTourStep/TOUR_STEPS) breaks on resize; fix by reading window.innerHeight
where positioning happens (move the read into updateTourTarget which already
runs on "resize") or by tracking viewportHeight in state via a small effect that
listens to window "resize" and updates the value; update references to
viewportHeight in the tour positioning logic (e.g., inside updateTourTarget and
any usage that positions the tour panel) so the latest height is used when
computing layout.

397-452: else if (… stepComplete) branches read stale appState.

In connectGithub/createApiKey/createWorkflow/deployStaging, the call to setAppState((s) => ({ ...s, X: true })) is asynchronous, but stepComplete was computed by useMemo against the previous render's appState. The else if (tourActive && currentTourStep && stepComplete) fallback therefore runs against pre-update state and effectively never matches when this handler is what just made the step complete. It does no harm today because the first branch handles every concrete id, but the dead fallback is misleading and will mask bugs if step ids ever drift.

Either remove the else if branch or compute completeness directly from the post-update flag (e.g. recompute stepComplete from a local next state object).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/embed-demo/page.tsx` around lines 397 - 452, The fallback else-if
checks using the memoized stepComplete are stale after you call setAppState in
the handlers (connectGithub, createApiKey, createWorkflow, deployStaging);
either remove the redundant else-if branch entirely or compute completeness from
the post-update state before calling setAppState (e.g., build a local nextState
object with the updated flag, derive stepCompleteNext from that and use it
instead of the memoized stepComplete), then call
advanceTourAfterSuccess(currentTourStep) based on that local computation; apply
the same change to connectGithub, createApiKey, createWorkflow and the inner
timeout callback in deployStaging and keep the explicit id check
(currentTourStep?.id === "...") as-is.
src/lib/embedDemoKnowledge.ts (1)

366-373: Overly broad deploy/staging matcher.

m.includes("deploy") || m.includes("staging") catches unrelated questions like "explain staging health" or "how does deploy rollback work?", which would otherwise fall through to the default journey response. Consider tightening to verbs/intents (e.g. \b(deploy|deploy now|push to staging)\b) or moving this branch below the explanatory checks so generic mentions don't hijack the response.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/embedDemoKnowledge.ts` around lines 366 - 373, The current branch
using m.includes("deploy") || m.includes("staging") is too broad and hijacks
queries; update the condition in the block that returns createResult(...) to
either use a stricter regex on m (e.g., match verbs/intents with word boundaries
like \b(deploy to staging|deploy now|push to staging)\b) or move this branch
below the explanatory/health-check branches so questions like "explain staging
health" or "rollback" fall through; reference the same variables (m,
createResult, product, excerptFromContent) when changing the matcher so the
returned answer/sources/steps logic remains intact.
src/app/api/embed/chat/route.ts (3)

181-184: Direct-command regexes don’t respect word boundaries.

/deploy|staging/ and /api key|create key|generate key/ will fire on questions like "what is staging?" or "explain api keys", returning a start_step uiAction that activates the guided tour for an unrelated query. Tighten to verb-led intents (e.g. \b(deploy now|push to staging)\b, \b(create|generate)\s+(an?\s+)?api\s+key\b) or require an imperative cue (how do i …, start …) before emitting start_step.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/embed/chat/route.ts` around lines 181 - 184, The current
intent-matching for variable m in src/app/api/embed/chat/route.ts uses loose
regexes that match substrings (e.g., the checks that return start_step for
stepId "deploy-staging" and "create-api-key"), causing false positives; update
the regexes used in that message-to-action mapping to require word boundaries
and verb-led or imperative cues (e.g., wrap patterns with \b...\b, use groups
like \b(create|generate)\s+(an?\s+)?api\s+key\b for the create-api-key branch
and \b(deploy(?:\s+now)?|push\s+to\s+staging)\b or require an imperative prefix
like ^(how do i|start|deploy)\b for the deploy-staging branch) so only explicit
commands trigger start_step UI actions.

158-186: Consider validating appState shape and reusing getNextAction.

firstIncompleteFeature re-implements the same prerequisite chain as getNextAction in src/lib/embedDemoNextAction.ts, but uses different feature/step keys (workflow-builder vs create-workflow). Sharing one function (and the DemoAppState type) would prevent the two halves of the system from drifting — the targetFeature mismatch noted in embedDemoNextAction.ts is a direct symptom.

Also, since appState is typed as unknown, a tiny guard like (state as Partial<DemoAppState>) keeps Boolean(...) honest while documenting the contract from ChatBody.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/embed/chat/route.ts` around lines 158 - 186, Replace the ad-hoc
firstIncompleteFeature logic with a call to the shared getNextAction and type
the incoming state as Partial<DemoAppState> to validate shape: update
deriveUiAction to cast appState to Partial<DemoAppState> (or guard it) and call
getNextAction(...) to obtain feature/stepId instead of duplicating logic in
firstIncompleteFeature; remove or collapse firstIncompleteFeature and ensure
derived step/feature keys match getNextAction (e.g., "create-workflow" vs
"workflow-builder") so both functions reuse the same enum/strings and stay in
sync.

123-127: Dead conditional — both branches now produce the same string.

After the refactor, the if/fallback paths return the identical [pageSection, appStateSection, hoverSection].join("\n\n"). The if (typeof body.pageContext === "string" && body.pageContext.trim()) check is no longer doing anything.

♻️ Simplify
-  if (typeof body.pageContext === "string" && body.pageContext.trim()) {
-    return [pageSection, appStateSection, hoverSection].join("\n\n");
-  }
   return [pageSection, appStateSection, hoverSection].join("\n\n");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/embed/chat/route.ts` around lines 123 - 127, The conditional
checking `if (typeof body.pageContext === "string" && body.pageContext.trim())`
is dead because both branches return the same value; remove the if/else and
return the joined sections directly. Update the function in route.ts (the block
that builds/returns the response using pageSection, appStateSection,
hoverSection) to simply `return [pageSection, appStateSection,
hoverSection].join("\n\n");`, eliminating the redundant `body.pageContext` check
and any unreachable branch code.
src/components/demo/FeatureCard.tsx (1)

19-29: Place {...rest} before the explicit data-runbook-* attributes.

Spreading rest after the explicit attributes lets a caller silently override data-runbook-feature / -title / -description (and even className, except className is destructured) by passing the same key. Putting the spread first preserves the component's invariant that feature/title/description are authoritative.

♻️ Proposed change
     <section
+      {...rest}
       className={className}
       data-runbook-feature={feature}
       data-runbook-title={title}
       data-runbook-description={description}
-      {...rest}
     >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/demo/FeatureCard.tsx` around lines 19 - 29, The JSX in the
FeatureCard component allows callers to override the component's authoritative
data attributes because {...rest} is spread after the explicit
data-runbook-feature/title/description props; move the {...rest} spread before
those explicit attributes in the returned <section> so feature, title, and
description passed to FeatureCard cannot be silently overridden (refer to the
FeatureCard component's return JSX and the data-runbook-feature /
data-runbook-title / data-runbook-description attributes).
src/components/EmbeddedRunbookAssistant.tsx (1)

388-416: triggerWhatNext duplicates the server-side prerequisite chain.

The same boolean ladder lives in three places now: getNextAction (src/lib/embedDemoNextAction.ts), firstIncompleteFeature (src/app/api/embed/chat/route.ts), and here. They already disagree on the feature key for the build-workflow step (see comment in embedDemoNextAction.ts), and any future change has to be made in lock-step. Consider exporting a single helper from embedDemoNextAction.ts and consuming it client-side as well.

Additionally, the bullets ["It moves setup forward", "It unlocks the next step", "I highlighted where to click"] are unconditional — they read oddly when the answer is "You are ready to launch." Consider making them state-aware.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/EmbeddedRunbookAssistant.tsx` around lines 388 - 416,
triggerWhatNext duplicates the prerequisite boolean ladder that already exists
in getNextAction and firstIncompleteFeature; export the shared helper (e.g.,
getNextAction or a new computeNextAction helper) from
src/lib/embedDemoNextAction.ts and import it into EmbeddedRunbookAssistant.tsx
to compute githubConnected/apiKeyCreated/workflowCreated/deployed and the final
answer so all callers use a single source of truth (ensure the exported helper
uses the canonical feature key for the build-workflow step). Also make the
bullets in triggerWhatNext state-aware: return contextual bullets only for
actionable states and use an alternative (or none) when the helper returns the
terminal "ready to launch" state so the assistant text reads correctly.
src/app/embed-demo/layout.tsx (1)

93-97: firstSentence will truncate at common abbreviations.

/^.*?[.!?](?:\s|$)/ stops at the first period followed by whitespace, so inputs like "e.g. connect GitHub. then deploy." collapse to "e.g.". Given inputs come from compactSentence(bestSentence) derived from imported repo content, this is a realistic foot-gun for tooltips. Consider requiring at least a few words before the terminator, or skipping single-letter / common-abbreviation cases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/embed-demo/layout.tsx` around lines 93 - 97, The current
firstSentence function truncates at abbreviations like "e.g." because its regex
/^.*?[.!?](?:\s|$)/ stops at the first period; update firstSentence to require a
more realistic sentence terminator by either (a) using a regex that only matches
a terminator if there are at least N words/characters before it (e.g., require
2–3 words before the punctuation) or (b) post-process the initial match to
detect and skip common abbreviations (e.g., "e.g.", "i.e.", "Mr.", "Dr.") and
continue searching for the next terminator; make this change in the
firstSentence function (which is fed compactSentence(bestSentence)) so tooltips
don't collapse on common abbreviations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/embed-demo/page.tsx`:
- Around line 783-798: The modal controlled by showWorkflowModal lacks any
dismiss mechanism; add a Cancel button that calls setShowWorkflowModal(false)
alongside the existing Create workflow button, make the backdrop div (the
element rendered when showWorkflowModal is true) handle clicks to close the
modal by calling setShowWorkflowModal(false) when the click is outside the modal
content, and register a keydown listener (mounted when showWorkflowModal is true
and cleaned up) to close on Escape; ensure createWorkflow remains unchanged and
that event propagation is stopped on the inner modal container so clicking the
form/buttons doesn't trigger backdrop close.

In `@src/components/EmbeddedRunbookAssistant.tsx`:
- Around line 172-178: The onPulse handler currently calls window.setTimeout
without tracking the timer; change it to store the timeout id (e.g.,
pulseTimeoutRef) when calling window.setTimeout in onPulse, clear any existing
timeout before creating a new one, and call
clearTimeout(pulseTimeoutRef.current) in a cleanup (useEffect return) so
setPulseLaunch(false) cannot run after the component unmounts or when pulses
stack; update references to onPulse/setPulseLaunch and ensure the ref is cleared
after clearing the timeout.

In `@src/lib/embedDemoKnowledge.ts`:
- Around line 129-157: inferNextActionFromContext currently inspects a
serialized page string with fragile regexes that match JSON keys and UI chrome;
change it to accept the typed appState booleans instead and use those booleans
for branching. Update inferNextActionFromContext signature to take an object
(e.g., { githubConnected, apiKeyCreated, stagingHealthy } or the exact appState
type), replace regex checks (/github.*connected/i, /api key|api keys/i,
/deploy|deployment/i and the rk_live_/staging checks) with direct boolean logic,
and adjust callers (buildNorthstarDemoResponse and the route that calls it) to
pass the parsed appState through so the function mirrors
firstIncompleteFeature/getNextAction semantics. Ensure return shape ({ answer,
feature, step }) stays the same.

In `@src/lib/embedDemoNextAction.ts`:
- Around line 8-42: The build-workflow step in getNextAction uses the wrong
targetFeature string ("create-workflow") which breaks feature lookups; update
the DemoNextAction.targetFeature union and the getNextAction return for the
"build-workflow" branch (function getNextAction, type DemoNextAction) to use
"workflow-builder" instead of "create-workflow" (and ensure both occurrences are
changed), or better yet replace the literal with a shared feature-id
enum/constant used across the codebase so the build-workflow step consistently
returns targetFeature: "workflow-builder".

---

Nitpick comments:
In `@src/app/api/embed/chat/route.ts`:
- Around line 181-184: The current intent-matching for variable m in
src/app/api/embed/chat/route.ts uses loose regexes that match substrings (e.g.,
the checks that return start_step for stepId "deploy-staging" and
"create-api-key"), causing false positives; update the regexes used in that
message-to-action mapping to require word boundaries and verb-led or imperative
cues (e.g., wrap patterns with \b...\b, use groups like
\b(create|generate)\s+(an?\s+)?api\s+key\b for the create-api-key branch and
\b(deploy(?:\s+now)?|push\s+to\s+staging)\b or require an imperative prefix like
^(how do i|start|deploy)\b for the deploy-staging branch) so only explicit
commands trigger start_step UI actions.
- Around line 158-186: Replace the ad-hoc firstIncompleteFeature logic with a
call to the shared getNextAction and type the incoming state as
Partial<DemoAppState> to validate shape: update deriveUiAction to cast appState
to Partial<DemoAppState> (or guard it) and call getNextAction(...) to obtain
feature/stepId instead of duplicating logic in firstIncompleteFeature; remove or
collapse firstIncompleteFeature and ensure derived step/feature keys match
getNextAction (e.g., "create-workflow" vs "workflow-builder") so both functions
reuse the same enum/strings and stay in sync.
- Around line 123-127: The conditional checking `if (typeof body.pageContext ===
"string" && body.pageContext.trim())` is dead because both branches return the
same value; remove the if/else and return the joined sections directly. Update
the function in route.ts (the block that builds/returns the response using
pageSection, appStateSection, hoverSection) to simply `return [pageSection,
appStateSection, hoverSection].join("\n\n");`, eliminating the redundant
`body.pageContext` check and any unreachable branch code.

In `@src/app/embed-demo/layout.tsx`:
- Around line 93-97: The current firstSentence function truncates at
abbreviations like "e.g." because its regex /^.*?[.!?](?:\s|$)/ stops at the
first period; update firstSentence to require a more realistic sentence
terminator by either (a) using a regex that only matches a terminator if there
are at least N words/characters before it (e.g., require 2–3 words before the
punctuation) or (b) post-process the initial match to detect and skip common
abbreviations (e.g., "e.g.", "i.e.", "Mr.", "Dr.") and continue searching for
the next terminator; make this change in the firstSentence function (which is
fed compactSentence(bestSentence)) so tooltips don't collapse on common
abbreviations.

In `@src/app/embed-demo/page.tsx`:
- Around line 102-103: viewportHeight is read once at mount and never updated,
so the tour panel positioning (used with currentTourStep/TOUR_STEPS) breaks on
resize; fix by reading window.innerHeight where positioning happens (move the
read into updateTourTarget which already runs on "resize") or by tracking
viewportHeight in state via a small effect that listens to window "resize" and
updates the value; update references to viewportHeight in the tour positioning
logic (e.g., inside updateTourTarget and any usage that positions the tour
panel) so the latest height is used when computing layout.
- Around line 397-452: The fallback else-if checks using the memoized
stepComplete are stale after you call setAppState in the handlers
(connectGithub, createApiKey, createWorkflow, deployStaging); either remove the
redundant else-if branch entirely or compute completeness from the post-update
state before calling setAppState (e.g., build a local nextState object with the
updated flag, derive stepCompleteNext from that and use it instead of the
memoized stepComplete), then call advanceTourAfterSuccess(currentTourStep) based
on that local computation; apply the same change to connectGithub, createApiKey,
createWorkflow and the inner timeout callback in deployStaging and keep the
explicit id check (currentTourStep?.id === "...") as-is.

In `@src/components/demo/FeatureCard.tsx`:
- Around line 19-29: The JSX in the FeatureCard component allows callers to
override the component's authoritative data attributes because {...rest} is
spread after the explicit data-runbook-feature/title/description props; move the
{...rest} spread before those explicit attributes in the returned <section> so
feature, title, and description passed to FeatureCard cannot be silently
overridden (refer to the FeatureCard component's return JSX and the
data-runbook-feature / data-runbook-title / data-runbook-description
attributes).

In `@src/components/EmbeddedRunbookAssistant.tsx`:
- Around line 388-416: triggerWhatNext duplicates the prerequisite boolean
ladder that already exists in getNextAction and firstIncompleteFeature; export
the shared helper (e.g., getNextAction or a new computeNextAction helper) from
src/lib/embedDemoNextAction.ts and import it into EmbeddedRunbookAssistant.tsx
to compute githubConnected/apiKeyCreated/workflowCreated/deployed and the final
answer so all callers use a single source of truth (ensure the exported helper
uses the canonical feature key for the build-workflow step). Also make the
bullets in triggerWhatNext state-aware: return contextual bullets only for
actionable states and use an alternative (or none) when the helper returns the
terminal "ready to launch" state so the assistant text reads correctly.

In `@src/lib/embedDemoKnowledge.ts`:
- Around line 366-373: The current branch using m.includes("deploy") ||
m.includes("staging") is too broad and hijacks queries; update the condition in
the block that returns createResult(...) to either use a stricter regex on m
(e.g., match verbs/intents with word boundaries like \b(deploy to staging|deploy
now|push to staging)\b) or move this branch below the explanatory/health-check
branches so questions like "explain staging health" or "rollback" fall through;
reference the same variables (m, createResult, product, excerptFromContent) when
changing the matcher so the returned answer/sources/steps logic remains intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e51ede2a-fab5-4f77-915e-47b1a0f48e15

📥 Commits

Reviewing files that changed from the base of the PR and between 94b3dd8 and bfa5a92.

📒 Files selected for processing (7)
  • src/app/api/embed/chat/route.ts
  • src/app/embed-demo/layout.tsx
  • src/app/embed-demo/page.tsx
  • src/components/EmbeddedRunbookAssistant.tsx
  • src/components/demo/FeatureCard.tsx
  • src/lib/embedDemoKnowledge.ts
  • src/lib/embedDemoNextAction.ts

Comment thread src/app/embed-demo/page.tsx
Comment thread src/components/EmbeddedRunbookAssistant.tsx
Comment thread src/lib/embedDemoKnowledge.ts Outdated
Comment thread src/lib/embedDemoNextAction.ts
Harden modal/resize behavior, remove stale tour completion branches, unify next-action derivation across page/widget/API, tighten uiAction intent matching, and improve fallback sentence/next-step parsing consistency.

Made-with: Cursor
@dfed25
dfed25 merged commit c2ebe46 into main Apr 26, 2026
5 checks passed
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.

2 participants