Skip to content

fix: improve embed location intent guidance and highlighting - #18

Merged
ZhuBryan merged 2 commits into
mainfrom
feature/embed-location-fixes
Apr 26, 2026
Merged

ZhuBryan merged 2 commits into
mainfrom
feature/embed-location-fixes

Conversation

@ZhuBryan

@ZhuBryan ZhuBryan commented Apr 26, 2026

Copy link
Copy Markdown
Collaborator

Improve demo embed chat handling for location-intent prompts, enrich page context, and harden UI highlight targeting so create-account and sign-up queries produce actionable guidance instead of generic fallback text.

Summary by CodeRabbit

  • New Features

    • Interactive UI highlighting for location and action-oriented questions with visual guidance and tooltips.
    • Enhanced onboarding guidance with inferred user flows and step-by-step instructions.
    • Demo mode responses for public requests.
  • Improvements

    • Better document retrieval with expanded query matching for onboarding-related questions.
    • Improved error handling with fallback instructions when sources are unavailable.
    • Safety checks for external links.

Improve demo embed chat handling for location-intent prompts, enrich page context, and harden UI highlight targeting so create-account and sign-up queries produce actionable guidance instead of generic fallback text.

Made-with: Cursor
@vercel

vercel Bot commented Apr 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
run-book Ready Ready Preview, Comment Apr 26, 2026 1:37am
runbook Ready Ready Preview, Comment Apr 26, 2026 1:37am

@coderabbitai

coderabbitai Bot commented Apr 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@ZhuBryan has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 12 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 46 minutes and 12 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: a73446e7-0d0f-4e1c-812a-2dbef6538665

📥 Commits

Reviewing files that changed from the base of the PR and between 6fd7a61 and 73a3b88.

📒 Files selected for processing (6)
  • public/runbook-embed.js
  • src/app/api/embed/chat/route.ts
  • src/components/EmbeddedRunbookAssistant.tsx
  • src/lib/embedDemoKnowledge.ts
  • src/lib/embedRetrieval.ts
  • test-embed.html
📝 Walkthrough

Walkthrough

This pull request adds client-side UI highlighting and intent detection for navigation-style questions across the embedded runbook assistant. Changes include element scoring and visual overlay logic in the client, enhanced retrieval with multiple embedding queries, extended system prompts for onboarding/location inference, demo mode support in API responses, and improved error handling with fallback instructions.

Changes

Cohort / File(s) Summary
Client-side highlighting and intent detection
public/runbook-embed.js
Adds intent phrase extraction for "where/find/click/open/how do I/create account" queries. Implements heuristic element scoring, DOM manipulation with CSS injection, animated pulse overlay, and tooltip rendering. Detects unindexed/missing-codebase state via mode, text patterns, or zero sources, injecting warning banners. Validates source URLs with safe URL helper before rendering external links. Enhances page context with truncated document body text (12k chars). Triggers highlight workflow post-response.
Response handling and highlight management
src/components/EmbeddedRunbookAssistant.tsx
Adds useRef-based highlight cleanup tracking. Extends ChatResponse with optional mode field. Derives missing-index signal from demo mode, text patterns, or empty sources, prepending assistant guidance messages. Implements highlight overlay lifecycle with DOM element scoring, scrolling, and conditional fallback prompts. Clears previous highlights before new overlay injection.
API route authorization and fallback handling
src/app/api/embed/chat/route.ts
Strengthens hostname validation with strict URL parsing and equality checks. Augments demo/public responses with mode: "demo". Adds mode: "fallback" for unconfigured AI and mode: "live" on success. Returns fallback onboarding-style answers using baseSources on runtime errors instead of 503 responses.
Retrieval and query expansion
src/lib/embedRetrieval.ts, src/lib/embedDemoKnowledge.ts
Builds multiple embedding queries from input (with onboarding/flow-specific expansions) instead of single query. Deduplicates documents across queries by ID, retaining highest similarity. Logs and skips RPC errors instead of aborting. Recognizes "where/find/locate/click/open/go to" intents and returns dedicated guidance flow with conditionally-attached sources.
System prompts and chat logic
src/lib/embedNorthstarChat.ts, src/lib/prompts.ts
Updates NORTHSTAR_SYSTEM and EMBED_CHAT_SYSTEM_PROMPT to instruct model to infer UI flow details (entry points, user actions, verification signals) for onboarding/how-to queries. Directs model to provide concise, action-first responses for location/login/sign-up intents to support immediate highlighting.
Test harness
test-embed.html
Introduces standalone HTML test page with account onboarding and login flow sections, labeled interactive elements, example prompts, and configured runbook embed script loading from local dev server.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client Browser
    participant Embed as runbook-embed.js
    participant Component as EmbeddedRunbookAssistant
    participant API as /embed/chat Route
    participant Retrieval as Retrieval & Embedding
    participant LLM as Claude API

    Client->>Embed: Load page + ask navigation question
    Embed->>Embed: Detect intent (where/find/click/open)
    Embed->>Component: Send question with page context
    Component->>API: POST /embed/chat
    API->>Retrieval: Retrieve docs (multiple queries + dedup)
    Retrieval->>LLM: Send context + extended system prompt
    LLM->>API: Return response + mode flag
    API->>Component: Return ChatResponse {answer, sources, mode}
    Component->>Component: Check for missing-index signal<br/>(mode=fallback or text patterns or empty sources)
    alt Missing Index Detected
        Component->>Component: Prepend assistant message<br/>with index guidance
    end
    Component->>Embed: Trigger maybeHighlight(question, response)
    Embed->>Embed: Extract target label from response
    Embed->>Embed: Score page elements against label
    Embed->>Embed: Scroll to best-match element
    alt Match Found
        Embed->>Client: Show animated pulse overlay + tooltip
    else No Match Found
        Embed->>Component: Emit assistant message<br/>requesting clarification
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • feat: ship interactive Runbook embed demo flow #16 — Modifies the same embed client logic, component handlers, API route, and retrieval/prompt functions; directly overlaps in implementation targets.
  • Chrome widget #15 — Adds related page-element locating and highlighting workflows with DOM scoring and overlay logic for location-based queries.
  • Enterprise integrations #8 — Enhances the vector embedding/retrieval pipeline integration; this PR builds on that foundation with multi-query expansion and deduplication.

Poem

🐰 A quest for the "where," the "how," and the "why,"
With highlights that dance and tooltips that fly!
Intent meets the page in a pulse and a gleam,
Smart queries and onboarding—a developer's dream! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: improving embed location intent guidance and highlighting, which directly aligns with the PR objectives and the substantial changes across multiple files.
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/embed-location-fixes

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/lib/embedDemoKnowledge.ts Fixed
Comment thread src/lib/embedDemoKnowledge.ts Fixed

@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: 7

♻️ Duplicate comments (1)
src/components/EmbeddedRunbookAssistant.tsx (1)

113-128: ⚠️ Potential issue | 🟡 Minor

Same mode === "fallback" conflation as the route handler.

This shares the issue flagged on src/app/api/embed/chat/route.ts lines 234-249: a transient AI failure now triggers the “Codebase not indexed yet. Connect your GitHub repo in Studio…” banner even when the index is healthy. The fix is best applied at the root cause (distinct mode for AI errors vs. unindexed) so this branch can stay narrow.

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

In `@src/components/EmbeddedRunbookAssistant.tsx` around lines 113 - 128, The
banner is incorrectly shown when data.mode === "fallback" (AI transient
failure); remove that check from the missingIndexSignal and instead only treat
the response as "unindexed" when there's an explicit unindexed indicator or
sourceCount === 0 (or when data.mode === "unindexed" if you introduce that
server-side), i.e., change the missingIndexSignal computation to NOT include
data.mode === "fallback" and rely on sourceCount === 0 or a distinct
data.mode/value that represents an actually unindexed repo before pushing the
"Codebase not indexed yet..." assistant message via setMessages.
🧹 Nitpick comments (6)
src/lib/embedRetrieval.ts (2)

57-57: match_count: TOP_K * 8 is large when multiplied across 3 queries.

With TOP_K = 6, each call asks for up to 48 rows; for flow-intent questions that's 144 rows fetched, marshalled, and post-filtered just to keep the top 6. Consider trimming the per-query fan-out (e.g., TOP_K * 4) now that the multi-query merge is expanding recall on its own.

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

In `@src/lib/embedRetrieval.ts` at line 57, Reduce the per-query fan-out by
changing the match_count expression from TOP_K * 8 to a smaller multiplier
(e.g., TOP_K * 4) in src/lib/embedRetrieval.ts; update the occurrence where
match_count is set (the line currently using "match_count: TOP_K * 8") so the
code asks for fewer rows per query while leaving the TOP_K constant and the
multi-query merge logic unchanged. Ensure any tests or callers that assume the
larger fan-out still pass or adjust assertions accordingly.

47-71: Run embedding queries in parallel to avoid latency tripling.

For flow-intent questions, buildEmbedQueries returns 3 strings. The current loop awaits each generateEmbedding(...) and each supabaseAdmin.rpc("match_documents", …) in sequence, so a single chat request now incurs roughly 3× the embedding+RPC round trips it used to. On a hot path served from a request thread this is a meaningful slowdown.

Running the queries concurrently keeps the same merge semantics with one round-trip’s worth of latency:

♻️ Suggested parallelization
-  for (const query of queries) {
-    const embedding = await generateEmbedding(query);
-    if (!embedding) continue;
-
-    const { data: documents, error } = await supabaseAdmin.rpc("match_documents", {
-      query_embedding: `[${embedding.join(",")}]`,
-      match_threshold: DEFAULT_MATCH_THRESHOLD,
-      match_count: TOP_K * 8
-    });
-
-    if (error) {
-      console.error("Embed vector search error:", error);
-      continue;
-    }
-
-    const batch = (documents || []) as MatchedDocument[];
-    for (const doc of batch) {
-      if (!matchesEmbedScope(doc.content, projectId)) continue;
-      const prev = merged.get(doc.id);
-      if (!prev || doc.similarity > prev.similarity) merged.set(doc.id, doc);
-    }
-  }
+  const results = await Promise.all(
+    queries.map(async (query) => {
+      const embedding = await generateEmbedding(query);
+      if (!embedding) return [] as MatchedDocument[];
+      const { data, error } = await supabaseAdmin.rpc("match_documents", {
+        query_embedding: `[${embedding.join(",")}]`,
+        match_threshold: DEFAULT_MATCH_THRESHOLD,
+        match_count: TOP_K * 8
+      });
+      if (error) {
+        console.error("Embed vector search error:", error);
+        return [] as MatchedDocument[];
+      }
+      return (data || []) as MatchedDocument[];
+    })
+  );
+
+  for (const batch of results) {
+    for (const doc of batch) {
+      if (!matchesEmbedScope(doc.content, projectId)) continue;
+      const prev = merged.get(doc.id);
+      if (!prev || doc.similarity > prev.similarity) merged.set(doc.id, doc);
+    }
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/embedRetrieval.ts` around lines 47 - 71, The loop is serially
awaiting generateEmbedding and supabaseAdmin.rpc for each query; refactor embed
retrieval to run per-query work in parallel by mapping
buildEmbedQueries(question) to an array of promises that concurrently call
generateEmbedding(query) then, if embedding exists, call
supabaseAdmin.rpc("match_documents", ...) and return the batch (or an error
marker), then await Promise.all on those promises, iterate the resolved results
and apply the same filtering/merge logic (use matchesEmbedScope(doc.content,
projectId) and the merged Map to keep the highest similarity per doc id).
Preserve existing error handling by logging per-promise RPC errors and skip
those results, and ensure the final merged Map semantics remain identical to the
original sequential code.
test-embed.html (1)

76-83: Optional: use real <label> elements for inputs.

The fields use <p>Email</p> / <p>Password</p> plus aria-label, which works for screen readers but loses <label for="…">/<input id="…"> association (clicking the visible text won’t focus the input). Since this harness exists to exercise the embed’s element-targeting heuristics (which include label in its selector), a real <label> would also give the highlighter a richer surface to score against.

-      <p>Email</p>
-      <input aria-label="Email address" placeholder="name@company.com" />
-      <p style="margin-top: 12px">Password</p>
-      <input aria-label="Password" placeholder="Password" type="password" />
+      <label for="email-field">Email</label>
+      <input id="email-field" aria-label="Email address" placeholder="name@company.com" />
+      <label for="password-field" style="margin-top: 12px">Password</label>
+      <input id="password-field" aria-label="Password" placeholder="Password" type="password" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test-embed.html` around lines 76 - 83, Replace the decorative <p> elements
with real <label> elements and associate them to the inputs by adding unique id
attributes to each input (e.g., id="email-input" and id="password-input") and
using <label for="email-input">Email</label> and <label
for="password-input">Password</label>; keep the existing aria-label and
placeholder attributes and the existing buttons (ids "log-in-btn" and
"reset-password-btn") unchanged so the embed/highlighter can match labels as
well as aria attributes.
public/runbook-embed.js (1)

213-344: Highlight engine is duplicated verbatim with the React component.

ensurePageHighlightStyles, clearPageHighlight, tokenize, compact, extractIntentPhrase, scoreCandidate/findBestElement, showPageOverlay, maybeHighlight, and isLocationIntent are reproduced almost line-for-line in src/components/EmbeddedRunbookAssistant.tsx (lines 375-532). They share the same stylesheet id rb-page-highlight-style and the same rb-highlight-target class, so if both ever load on the same page they will race on cleanup and potentially clobber each other's overlay/timeout state.

Recommend extracting the shared logic into a single module (e.g., src/lib/embedHighlight.ts) and consuming it from both the IIFE and the React component, or — at minimum — keeping the regexes/scoring constants in one canonical place. This will also stop the intent regex from drifting between the three files where it currently appears (embedDemoKnowledge.ts, runbook-embed.js, EmbeddedRunbookAssistant.tsx).

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

In `@public/runbook-embed.js` around lines 213 - 344, The highlight engine is
duplicated across files causing races and drift; extract the shared functions
(ensurePageHighlightStyles, clearPageHighlight, tokenize, compact,
extractIntentPhrase, scoreCandidate, findBestElement, showPageOverlay,
maybeHighlight, and isLocationIntent) into a single module (e.g.,
src/lib/embedHighlight.ts) that exports these functions and any scoring/regex
constants, update EmbeddedRunbookAssistant.tsx and public/runbook-embed.js to
import/use that module (and also update embedDemoKnowledge.ts to use the same
isLocationIntent/regex), and ensure the single stylesheet id
("rb-page-highlight-style"), class names
("rb-highlight-target","rb-highlight-overlay") and shared overlay/timeout state
are managed from that module so both consumers call the same cleanup and avoid
clobbering each other.
src/app/api/embed/chat/route.ts (1)

178-179: Onboarding instruction overlaps with the system prompt.

The new sentence at line 178 largely restates EMBED_CHAT_SYSTEM_PROMPT lines 4-9 in src/lib/prompts.ts. Keeping it in both places risks the two instructions drifting out of sync. Consider relying solely on the system prompt for onboarding behavior and keeping the user-prompt suffix focused on the RUNBOOK_STEPS_JSON: contract on line 179.

🤖 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 178 - 179, Remove the
duplicated onboarding sentence from the user prompt in the chat route and rely
on the existing EMBED_CHAT_SYSTEM_PROMPT for onboarding behavior; update the
user-prompt suffix to only enforce the RUNBOOK_STEPS_JSON contract (i.e., append
the JSON steps line exactly as specified) and ensure the route’s prompt
construction uses EMBED_CHAT_SYSTEM_PROMPT as the single source of truth for
onboarding instructions to avoid drift between prompts.
src/components/EmbeddedRunbookAssistant.tsx (1)

418-507: findBestTarget and findBestTargetWithIntent overlap heavily; consolidate.

The two helpers iterate the same node set with the same selector, the same tokens loop, and the same threshold. The only differences are the intent-phrase bonus and the “create/sign up/register/get started/start/continue/next” keyword bonus. maybeHighlightElementForQuestion already calls findBestTargetWithIntent first and falls back to findBestTarget, but findBestTargetWithIntent is a strict superset of findBestTarget's scoring (the intent/keyword bonuses can only add), so the fallback can never win. You can drop findBestTarget and pass an empty intentCompact/skip the keyword bonus when not desired.

Also note this drifts from public/runbook-embed.js's findBestElement, which always applies the intent bonus — so identical inputs may pick different targets between the React preview and the embedded script.

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

In `@src/components/EmbeddedRunbookAssistant.tsx` around lines 418 - 507, The two
functions findBestTarget and findBestTargetWithIntent are redundant because the
latter is a strict superset; remove findBestTarget and consolidate into a single
function (keep the name findBestTargetWithIntent or rename to findBestTarget)
that accepts an optional intentCompact (string) and an options flag (e.g., {
applyKeywordBonus: boolean }) so callers can pass an empty intentCompact or set
applyKeywordBonus=false to mimic the old behavior; update
maybeHighlightElementForQuestion to call the unified function accordingly, keep
the same node selector, token loop, scoring thresholds, and scoring bonuses, and
ensure the unified implementation mirrors public/runbook-embed.js's
findBestElement behavior for parity (always apply intent bonus there or make
behavior configurable) so identical inputs produce the same target across both
contexts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@public/runbook-embed.js`:
- Around line 352-361: The current capture of document.body.innerText into the
bodyText variable exposes potential PII; change this to opt-in and safer
extraction: only collect body text when the embedding script has a
data-include-body-text attribute (check document.currentScript or
querySelector('script[data-include-body-text]')), otherwise leave bodyText empty
and return only title/meta/heading landmarks; when opt-in, sanitize by removing
values of form controls and text from <input> and <textarea> elements before
building bodyText (strip or replace their values), limit length as before, and
ensure the return that joins [u, t, meta, bodyText] uses the sanitized/opt-in
bodyText variable so default behavior never sends arbitrary page content.

In `@src/app/api/embed/chat/route.ts`:
- Around line 234-249: The catch block that handles errors from
generateFromGemini is returning mode: "fallback" which conflates transient AI
failures with an unindexed codebase; change the response in that catch (the
NextResponse.json that's returning answer, sources: baseSources, mode, steps) to
use mode: "error" (or another distinct value) for AI/API failures, keep mode:
"fallback" reserved for the unindexed/missing-index path, and update client-side
checks (the logic that inspects mode to show the “connect your repo / re-index”
banner) to only treat mode === "fallback" as the missing-index case while
ignoring mode === "error".
- Around line 28-34: The originAllowed logic currently enforces strict equality
between allowed.hostname and actual.hostname which blocks valid subdomains;
update the check in the try block (the code that creates `allowed` and `actual`
from `projectSite` and `origin`) to allow either exact hostname match OR a
subdomain match — e.g. return true if `actual.hostname === allowed.hostname ||
actual.hostname.endsWith('.' + allowed.hostname)` — keeping the existing URL
parsing and try/catch structure intact so malformed URLs still return false.

In `@src/components/EmbeddedRunbookAssistant.tsx`:
- Line 60: The highlight cleanup stored in highlightCleanupRef is never invoked
on component unmount, so add a useEffect(() => () => {
highlightCleanupRef.current?.(); }) to EmbeddedRunbookAssistant to call
highlightCleanupRef.current() during cleanup; ensure you reference the existing
highlightCleanupRef and that maybeHighlightElementForQuestion’s assigned cleanup
is invoked so the rb-highlight-target class and overlay are removed when the
component unmounts (also apply same cleanup pattern where highlightCleanupRef is
used around maybeHighlightElementForQuestion).
- Around line 138-150: The fallback assistant bubble from
maybeHighlightElementForQuestion is being appended too aggressively even when
the model already provided a target or when step data exists; change the logic
that currently sets the fallback message in the block handling
maybeHighlightElementForQuestion so you only append that extra assistant message
when there are no data.steps AND the latest assistant/model answer does not
already include a target label (i.e., inspect the last assistant message text
from messages or the model response for phrases/labels), otherwise skip adding
the extra message and just set highlightCleanupRef.current = () => undefined;
additionally consider gating this only for location intents resolved by
findBestTargetWithIntent/findBestTarget so non-location answers never trigger
the fallback.

In `@src/lib/embedDemoKnowledge.ts`:
- Around line 48-63: The response for location intents in the isLocationIntent
branch currently hard-codes "account creation flow"; change the answer to use
the extracted target (from extractLocationTarget(message) assigned to target) or
a neutral phrase so it reads correctly for any intent—update the returned object
in the isLocationIntent block (where target, product, and excerptFromContent are
used) to replace the account-specific sentence with something like a template
referencing ${target} or a generic "that action" wording and adjust any step
text if it references account creation.
- Around line 20-24: The isLocationIntent function's regex is too broad (it
includes "how do i" and lacks word boundaries), causing demo-specific prompts to
be misclassified; tighten it by removing the bare "how do i" alternative, add
word boundaries (\b) around single-word triggers like
find/register/create/account/signup/login/log in, and restrict phrase triggers
to genuinely UI-locator forms such as "where", "where is", "where can I",
"locate", "click", "open", "go to", or "get started"; update the regex inside
isLocationIntent to use these bounded/phrased terms so existing
GitHub/local-setup/first-week branches are no longer short-circuited.

---

Duplicate comments:
In `@src/components/EmbeddedRunbookAssistant.tsx`:
- Around line 113-128: The banner is incorrectly shown when data.mode ===
"fallback" (AI transient failure); remove that check from the missingIndexSignal
and instead only treat the response as "unindexed" when there's an explicit
unindexed indicator or sourceCount === 0 (or when data.mode === "unindexed" if
you introduce that server-side), i.e., change the missingIndexSignal computation
to NOT include data.mode === "fallback" and rely on sourceCount === 0 or a
distinct data.mode/value that represents an actually unindexed repo before
pushing the "Codebase not indexed yet..." assistant message via setMessages.

---

Nitpick comments:
In `@public/runbook-embed.js`:
- Around line 213-344: The highlight engine is duplicated across files causing
races and drift; extract the shared functions (ensurePageHighlightStyles,
clearPageHighlight, tokenize, compact, extractIntentPhrase, scoreCandidate,
findBestElement, showPageOverlay, maybeHighlight, and isLocationIntent) into a
single module (e.g., src/lib/embedHighlight.ts) that exports these functions and
any scoring/regex constants, update EmbeddedRunbookAssistant.tsx and
public/runbook-embed.js to import/use that module (and also update
embedDemoKnowledge.ts to use the same isLocationIntent/regex), and ensure the
single stylesheet id ("rb-page-highlight-style"), class names
("rb-highlight-target","rb-highlight-overlay") and shared overlay/timeout state
are managed from that module so both consumers call the same cleanup and avoid
clobbering each other.

In `@src/app/api/embed/chat/route.ts`:
- Around line 178-179: Remove the duplicated onboarding sentence from the user
prompt in the chat route and rely on the existing EMBED_CHAT_SYSTEM_PROMPT for
onboarding behavior; update the user-prompt suffix to only enforce the
RUNBOOK_STEPS_JSON contract (i.e., append the JSON steps line exactly as
specified) and ensure the route’s prompt construction uses
EMBED_CHAT_SYSTEM_PROMPT as the single source of truth for onboarding
instructions to avoid drift between prompts.

In `@src/components/EmbeddedRunbookAssistant.tsx`:
- Around line 418-507: The two functions findBestTarget and
findBestTargetWithIntent are redundant because the latter is a strict superset;
remove findBestTarget and consolidate into a single function (keep the name
findBestTargetWithIntent or rename to findBestTarget) that accepts an optional
intentCompact (string) and an options flag (e.g., { applyKeywordBonus: boolean
}) so callers can pass an empty intentCompact or set applyKeywordBonus=false to
mimic the old behavior; update maybeHighlightElementForQuestion to call the
unified function accordingly, keep the same node selector, token loop, scoring
thresholds, and scoring bonuses, and ensure the unified implementation mirrors
public/runbook-embed.js's findBestElement behavior for parity (always apply
intent bonus there or make behavior configurable) so identical inputs produce
the same target across both contexts.

In `@src/lib/embedRetrieval.ts`:
- Line 57: Reduce the per-query fan-out by changing the match_count expression
from TOP_K * 8 to a smaller multiplier (e.g., TOP_K * 4) in
src/lib/embedRetrieval.ts; update the occurrence where match_count is set (the
line currently using "match_count: TOP_K * 8") so the code asks for fewer rows
per query while leaving the TOP_K constant and the multi-query merge logic
unchanged. Ensure any tests or callers that assume the larger fan-out still pass
or adjust assertions accordingly.
- Around line 47-71: The loop is serially awaiting generateEmbedding and
supabaseAdmin.rpc for each query; refactor embed retrieval to run per-query work
in parallel by mapping buildEmbedQueries(question) to an array of promises that
concurrently call generateEmbedding(query) then, if embedding exists, call
supabaseAdmin.rpc("match_documents", ...) and return the batch (or an error
marker), then await Promise.all on those promises, iterate the resolved results
and apply the same filtering/merge logic (use matchesEmbedScope(doc.content,
projectId) and the merged Map to keep the highest similarity per doc id).
Preserve existing error handling by logging per-promise RPC errors and skip
those results, and ensure the final merged Map semantics remain identical to the
original sequential code.

In `@test-embed.html`:
- Around line 76-83: Replace the decorative <p> elements with real <label>
elements and associate them to the inputs by adding unique id attributes to each
input (e.g., id="email-input" and id="password-input") and using <label
for="email-input">Email</label> and <label
for="password-input">Password</label>; keep the existing aria-label and
placeholder attributes and the existing buttons (ids "log-in-btn" and
"reset-password-btn") unchanged so the embed/highlighter can match labels as
well as aria attributes.
🪄 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: 14171f7e-8828-453f-8e44-486e5fb7ae41

📥 Commits

Reviewing files that changed from the base of the PR and between efe0e99 and 6fd7a61.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • public/runbook-embed.js
  • src/app/api/embed/chat/route.ts
  • src/components/EmbeddedRunbookAssistant.tsx
  • src/lib/embedDemoKnowledge.ts
  • src/lib/embedNorthstarChat.ts
  • src/lib/embedRetrieval.ts
  • src/lib/prompts.ts
  • test-embed.html

Comment thread public/runbook-embed.js Outdated
Comment thread src/app/api/embed/chat/route.ts
Comment thread src/app/api/embed/chat/route.ts
Comment thread src/components/EmbeddedRunbookAssistant.tsx
Comment thread src/components/EmbeddedRunbookAssistant.tsx
Comment thread src/lib/embedDemoKnowledge.ts
Comment thread src/lib/embedDemoKnowledge.ts
Harden embed privacy and error semantics, tighten location-intent handling, improve highlight cleanup behavior, parallelize embed retrieval queries, and update the test harness accessibility labels.

Made-with: Cursor
@vercel

vercel Bot commented Apr 26, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/domenic-federicos-projects?upgradeToPro=build-rate-limit

@ZhuBryan
ZhuBryan merged commit 5a1a318 into main Apr 26, 2026
5 of 7 checks passed
@ZhuBryan
ZhuBryan deleted the feature/embed-location-fixes branch April 26, 2026 01:51
@ZhuBryan
ZhuBryan restored the feature/embed-location-fixes branch April 26, 2026 01:51
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