Skip to content

Conversation

@AdityaHegde
Copy link
Collaborator

@AdityaHegde AdityaHegde commented Oct 31, 2025

Adds inline mention-like chat context support.
Also includes measure quick explanation feature.

This is behind a feature flag: dashboard_chat

PRD: https://www.notion.so/rilldata/AI-Chat-experience-2a2ba33c8f578036802fd3e20c7029ec?source=copy_link#2a2ba33c8f578154a5d2ca6c88ee2113
UXQA https://www.notion.so/rilldata/Inline-chat-context-UXQA-2b0ba33c8f57808999a8d37cc0da573e#2b5ba33c8f5780fc8966e48409b3bb43

Closes APP-439 APP-432

Checklist:

  • Covered by tests
  • Ran it and it works as intended
  • Reviewed the diff before requesting a review
  • Checked for unhandled edge cases
  • Linked the issues it closes
  • Checked if the docs need to be updated. If so, create a separate Linear DOCS issue
  • Intend to cherry-pick into the release branch
  • I'm proud of this work!
Copy link

@Di7design Di7design left a comment

Choose a reason for hiding this comment

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

Thanks, Aditya!
After testing, I noticed the current hover action feels a bit slow if I click the tag without waiting. Can we display the list with the click behavior as well — when clicking the tag, it should both apply the filter and open the list, and clicking anywhere else will close the list. The remove action should only be triggered by the close icon, not by clicking the tag itself.

I also noticed that when I use the Explain feature, it doesn’t update the measure filter in the tag — is this intentional, or is the measure filter missing in this case? Also, when I add a measure filter manually, it doesn’t update in the tag either.
Screenshot 2025-10-31 at 9 23 30 AM

@AdityaHegde
Copy link
Collaborator Author

Thanks @Di7design . Measure name being in the filter will be a future enhancement. For now we have opted to skip things in the chat being identified as a context item.

@AdityaHegde AdityaHegde force-pushed the feat/dashboard-chat-context branch from 34356ca to 032e99d Compare November 3, 2025 15:05
Copy link

@Di7design Di7design left a comment

Choose a reason for hiding this comment

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

Context in chat box looks good. Here is design input for additional context in chat history: Figma
Screenshot for preview
Screenshot 2025-11-03 at 3 06 05 PM

@AdityaHegde AdityaHegde marked this pull request as ready for review November 4, 2025 13:27
@AdityaHegde AdityaHegde requested a review from Di7design November 4, 2025 13:27
Copy link
Contributor

@ericpgreen2 ericpgreen2 left a comment

Choose a reason for hiding this comment

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

UXQA:

  1. I asked a question on Dashboard A, kept the chat open, and I navigated to Dashboard B. I hit this error that crashed the app:
    Image
  2. The new designs (as of Friday) no longer have the "scoped to current dashboard" message & constraint. The AI should be able to query other metrics views. (Similar to how Cursor knows the context of your current open file, yet it's still able to query other files that are not open.)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move these formatters to be colocated with their respective state management? For example:

  • The time range formatter should live in features/dashboards/time-controls
  • The filter formatter should live in features/dashboards/filters
@AdityaHegde AdityaHegde force-pushed the feat/dashboard-chat-context branch 2 times, most recently from 114a431 to fd88c9c Compare November 20, 2025 14:17
@AdityaHegde AdityaHegde force-pushed the feat/dashboard-chat-context branch from fd88c9c to 7119b31 Compare November 20, 2025 14:27
@AdityaHegde AdityaHegde force-pushed the feat/dashboard-chat-context branch from 7fd9119 to e6a4de0 Compare November 24, 2025 13:14
@AdityaHegde AdityaHegde force-pushed the feat/dashboard-chat-context branch from e1f9a08 to 3dd529a Compare November 24, 2025 16:05
@ericpgreen2
Copy link
Contributor

Question: ChatInputTextAreaManager Complexity & Long-term Maintenance

I want to discuss the hand-rolled ChatInputTextAreaManager implementation (chat-input-textarea-manager.ts:1-279). While it's functionally working, I have concerns about the architectural complexity and long-term maintainability.

Technical Concerns

1. Race Conditions from setTimeout Dependencies

The implementation relies on multiple setTimeout calls to work around DOM timing issues:

// chat-input-textarea-manager.ts:88-90
setTimeout(() => {
  this.handleContextMode();
});

// chat-input-textarea-manager.ts:99-101
setTimeout(() => {
  this.handleContextStarted();
});

This creates potential race conditions when:

  • Users type quickly (e.g., @@ triggers two queued handleContextStarted() calls)
  • Multiple setPrompt() calls happen in succession
  • Keyboard events fire while components are being created/destroyed

2. Manual Component Lifecycle Management

The Svelte component lifecycle is managed manually alongside DOM operations (chat-input-textarea-manager.ts:40-72):

public setPrompt(html: string) {
  this.editorElement.innerHTML = html; // Nukes DOM
  
  setTimeout(() => {
    // Destroy old components
    this.elementToContextComponent.values().forEach((c) => c.$destroy());
    
    // Recreate by parsing HTML
    const comp = new InlineChatContextComponent({ ... });
    this.elementToContextComponent.set(
      (node as any).previousElementSibling, // Fragile mapping
      comp,
    );
  });
}

Concerns:

  • Components could be orphaned if setPrompt() is called during the setTimeout
  • The previousElementSibling mapping assumes stable DOM structure
  • Multiple as any type casts (chat-input-textarea-manager.ts:54, 63, 220, 235)

3. Direct Selection API Manipulation

Manual cursor management (chat-input-textarea-manager.ts:203-244) doesn't handle:

  • Edge cases (start of line, RTL text, IME composition)
  • Mobile browser differences
  • Browser inconsistencies between Safari/Chrome/Firefox

4. Bidirectional Sync Guards

The need for infinite-loop prevention (ChatInput.svelte:52-56) suggests we're fighting the framework:

function syncPrompts(newPrompt: string) {
  if (lastPrompt === newPrompt) return; // Prevent infinite loop
  // ...
}

Future Complexity

This approach will make it harder to add features like:

  • Copy/paste with chip preservation
  • Undo/redo
  • Multi-chip selection and editing
  • Full accessibility support

Each will require additional complexity layered on this foundation.


Alternative Approach: Use a Rich Text Editor Library

After researching options, we recommend TipTap as an alternative approach. It's built on ProseMirror and designed specifically for cases like this (inline custom components in text).

TipTap example:

import { Editor } from '@tiptap/core';
import { Mention } from '@tiptap/extension-mention';

const editor = new Editor({
  extensions: [
    StarterKit,
    Mention.configure({
      suggestion: {
        char: '@',
        items: ({ query }) => getFilteredOptions(query),
        render: () => renderSuggestionDropdown(),
      },
    }),
  ],
});

Why we recommend TipTap:

  • Has a Mention extension built for exactly this use case
  • Handles cursor management, browser quirks, and component lifecycle
  • Gets us copy/paste, undo/redo, and accessibility for free
  • Svelte-compatible with official bindings
  • Used in production by Substack, GitLab, Apostrophe CMS
  • Can serialize to our existing <inline> format via custom serializer
  • Would allow keeping existing types, dropdown component, and backend format

Trade-offs:

  • Additional dependency (~50-100kb)
  • Learning curve for the library's architecture
  • May be overkill if we don't need other editor features

Discussion

I'd like to understand your thinking on the hand-rolled approach:

  1. Did you evaluate existing libraries? If so, what made you decide against them?
  2. Are there specific requirements that make a library approach not work?
  3. How do you see us handling copy/paste, undo/redo, and the other missing features?
  4. Do you have ideas for simplifying the current implementation to reduce the race conditions and lifecycle complexity?

I'm genuinely curious about your perspective here - there may be constraints I'm not aware of that make the library approach unsuitable. Would love to discuss alternatives or ways to strengthen the current implementation.


Developed in collaboration with Claude Code and Eric

Copy link
Contributor

@ericpgreen2 ericpgreen2 left a comment

Choose a reason for hiding this comment

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

Adding to the above feedback, see my UXQA findings here, which map to the difficulty of hand-rolling the chat input.

Below are a few smaller comments.

Comment on lines 49 to 51
{@html DOMPurify.sanitize(
convertPromptWithInlineContextToHTML(content, $contextMetadataStore),
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps this should live in its own UserMessage component?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Isnt TextMessage the user message component? Or you mean similar to Markdown component?

Copy link
Contributor

Choose a reason for hiding this comment

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

The TextMessage component currently handles some assistant messages too.

Given how the inline context makes the user message rendering more complex, it could warrant encapsulating that complexity in its own UserMessage component.

Copy link

@Di7design Di7design left a comment

Choose a reason for hiding this comment

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

Adding my reviews to the Notion doc as well. See here.

@AdityaHegde
Copy link
Collaborator Author

Hmmm I did take a look at tiptap. The link to the mention component is tagged as started plan here: https://tiptap.dev/docs/ui-components/components/mention-dropdown-menu
Looks like the plugin is separate which is open source. Lemme try to prototype something and see if it is simple. Note that component lifecycle management will probably have to be done by us.

I was honestly putting my effort into implementing different changes to the design/requirement. I wrote the editor at the beginning and didnt touch it too much.

@ericpgreen2
Copy link
Contributor

Code Review - PR #8217

Nice work on the inline chat context feature! The Tiptap integration is clean and the separation of concerns is well done. A few items to address before merge:

High Priority

  1. Dropdown positioning may render off-screen (AddInlineChatDropdown.svelte)

    The dropdown uses fixed pixel positioning calculated from getBoundingClientRect(). If the chat input is near viewport edges, the dropdown could render partially or fully off-screen. Consider adding boundary detection or using a positioning library like Floating UI.

  2. Potential memory leak in node view (chat-plugins.ts:157-193)

    Svelte components are created in addNodeView() but if Tiptap doesn't properly call destroy() on node replacement/deletion, components may accumulate. Worth verifying the cleanup path is working correctly.

Medium Priority

  1. Duplicate regex patterns (nav-utils.ts:8-9)

    const exploreRouteRegex = /\/explore\/(?:\[name]|\[dashboard])/;
    const canvasRouteRegex = /\/explore\/(?:\[name]|\[dashboard])/;

    Both regexes are identical - looks like a copy-paste error. Should canvasRouteRegex match canvas routes instead?

  2. Missing values in equality check (inline-context.ts:40-49)

    inlineChatContextsAreEqual() doesn't compare the values property, which could cause incorrect equality results for DimensionValues context types.

  3. Type safety (chat-plugins.ts:134)

    conversationManager: (this.options as any).manager,

    Consider properly extending MentionOptions to include manager in a type-safe way rather than using as any.

  4. Hardcoded prompt template (measure-selection.ts:45-52)

    The explanation prompt is hardcoded inline. Consider extracting to a constants file for easier maintenance.


Developed in collaboration with Claude Code

Copy link
Contributor

@ericpgreen2 ericpgreen2 left a comment

Choose a reason for hiding this comment

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

  1. I left a few more UXQA findings here.
  2. Generally, I find the context feature feels broken without keyboard shortcuts for selecting items. I'd expect the primary way to select context will be keyboard-only, not point-and-click.
  3. Can you please add e2e tests for the "explain this" feature and chatting w/ user-provided context? (Though, I'm open to these tests being added when we remove the feature flag.)
  4. See the Claude Code feedback above for some detailed feedback.
Copy link
Contributor

@ericpgreen2 ericpgreen2 Nov 25, 2025

Choose a reason for hiding this comment

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

Naming nit: InlineContextSpan.svelte could be cleaner. It'd help imply there could be multiple instances of the component within the Chat Input.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why do you think Context doesnt imply multiple instances? Also Span feels out of place, so would be good to stuck with Context

Comment on lines 13 to 17
export const INLINE_CHAT_TYPE_ATTR = "data-type";
export const INLINE_CHAT_METRICS_VIEW_ATTR = "data-metrics-view";
export const INLINE_CHAT_MEASURE_ATTR = "data-measure";
export const INLINE_CHAT_DIMENSION_ATTR = "data-dimension";
export const INLINE_CHAT_TIME_RANGE_ATTR = "data-time-range";
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly, here's a proposal to make these tags cleaner for the LLM:

Image
Copy link
Contributor

Choose a reason for hiding this comment

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

Somewhere in the code, like at the top of this file, it'd be helpful to have a clear explanatory comment that shows a couple example serializations.

Copy link
Collaborator Author

@AdityaHegde AdityaHegde Nov 26, 2025

Choose a reason for hiding this comment

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

Converting to html is not needed right now, LLM doesnt get the attributes.

There are tests for these methods for serialisation examples. Lemme know if they are not enough.

@ericpgreen2
Copy link
Contributor

Naming Inconsistencies

Developed in collaboration with Claude Code.

I've reviewed the naming patterns throughout this PR and found several inconsistencies that could create confusion for future maintainers.

1. Inconsistent Terminology: "inline" and "chat" Qualifiers

The codebase uses inconsistent combinations of "inline" and "chat" to describe the same concept:

Pattern 1: "InlineChatContext" (both qualifiers)

// Component names
InlineChatContext.svelte
InlineChatContextPicker.svelte
InlineChatMetricsViewContextPicker.svelte

// Type definition
type InlineChatContext = { ... }

// Functions
function normalizeInlineChatContext() { ... }
function getInlineChatContextMetadata() { ... }

Pattern 2: "ChatContext" (no "inline")

// Enum
enum ChatContextEntryType { ... }

// Variables
selectedChatContext: InlineChatContext
const chatElement: HTMLSpanElement

Pattern 3: "inline-context" (no "chat")

// File names
inline-context.ts
inline-context-data.ts
inline-context-highlight-manager.ts

Pattern 4: "inline-chat-context" (both qualifiers, different order)

// CSS classes
.inline-chat-context
.inline-chat-context-value
.inline-chat-context-dropdown

Pattern 5: "chat-reference" (different term entirely)

// HTML tag
export const INLINE_CHAT_CONTEXT_TAG = "chat-reference";
// Renders as: <chat-reference>...</chat-reference>

The Problem

When do we say "inline" and when do we omit it? When do we include "chat" and when do we not?

This creates confusion:

  • Is ChatContextEntryType the same as InlineChatContext? (Yes, but naming suggests otherwise)
  • Why is it inline-context.ts but InlineChatContext type?
  • Variables like selectedChatContext drop "inline" - is this intentional or inconsistent?
  • The HTML tag uses a completely different term ("reference") than everything else

Developer impact:

  • Searching for "InlineChatContext" won't find inline-context.ts
  • Searching for "ChatContext" won't find the highlight manager
  • The mental model is unclear - are these different things or the same thing?

Discussion: What should the canonical name be?

The core question: What is this feature called?

Option A: "ChatContext" (drop "inline" everywhere)

  • Rationale: The "inline" refers to the UI mechanism (@-mention), not the conceptual feature
  • Rationale: Once the context is selected, it's just "chat context" being sent to the API
  • Changes:
    • Rename types/files to remove "inline"
    • ChatContext.svelte, ChatContextPicker.svelte
    • chat-context.ts, chat-context-data.ts
  • Pro: Shorter, focuses on what it IS not how it's entered
  • Con: Loses the distinction from other types of context (like explore-context.ts)

Option B: "InlineChatContext" (add "inline" everywhere)

  • Rationale: This is specifically context added inline via mentions (vs. implicit/automatic context)
  • Changes:
    • Add "inline" to enum: InlineChatContextType
    • Rename files: inline-chat-context.ts, inline-chat-context-data.ts
    • Keep components as-is
  • Pro: Distinguishes this from other context types
  • Pro: Describes both what it is AND how it's provided
  • Con: Most verbose option

Option C: "InlineContext" (drop "chat", add "inline" everywhere)

  • Rationale: We're in features/chat/ already, "chat" is redundant
  • Changes:
    • Rename types: InlineContext, InlineContextType
    • Keep files as-is: inline-context.ts
    • Rename components: InlineContext.svelte, InlineContextPicker.svelte
  • Pro: Shorter but still specific
  • Pro: File names already match this pattern
  • Pro: Namespace efficiency (already in chat feature)
  • Con: Less explicit about the feature domain

Recommendation

I recommend Option C: "InlineContext" because:

  1. Namespace efficiency: We're already in features/chat/core/context/ - saying "chat" again is redundant
  2. Consistency with existing files: File names already use this pattern (inline-context.ts)
  3. Clarity: "Inline" distinguishes this from automatic/implicit context
  4. Follows codebase patterns: Similar to how pivot/ directory has pivot-*.ts files

Proposed changes:

// Types
- type InlineChatContext = { ... }
+ type InlineContext = { ... }

// Enum
- enum ChatContextEntryType { ... }
+ enum InlineContextType { ... }

// Files (already correct!)
inline-context.ts ✓
inline-context-data.ts ✓

// Components
- InlineChatContext.svelte
+ InlineContext.svelte

- InlineChatContextPicker.svelte
+ InlineContextPicker.svelte

// Add missing prefixes
- chat-plugins.ts
+ inline-context-plugins.ts

- convertors.ts
+ inline-context-convertors.ts

// HTML tag - can keep as-is with comment
export const INLINE_CONTEXT_TAG = "chat-reference"; // Using "reference" in HTML for semantic clarity

Alternative: If you prefer keeping "InlineChatContext", then files need to be renamed to match:

inline-chat-context.ts (not inline-context.ts)
inline-chat-context-data.ts
etc.

What's your take? The key decision is whether to:

  1. Standardize on "InlineContext" (shorter, matches file names)
  2. Standardize on "InlineChatContext" (more explicit, matches current types)
  3. Standardize on "ChatContext" (shortest, but loses distinction)

2. Component Naming - Container vs Item Pattern Not Clear

There are two "Picker" components with unclear boundaries:

InlineChatContextPicker.svelte (lines 68-76):

  • Role: Container/orchestrator for the entire dropdown menu
  • Responsibilities: Global keyboard navigation, search filtering, highlight management
  • Renders: The dropdown shell that loops over metrics views

InlineChatMetricsViewContextPicker.svelte:

  • Role: Individual collapsible section for ONE metrics view
  • Responsibilities: Renders a single metrics view with its measures/dimensions
  • Renders: A collapsible list item with checkboxes and icons

The Architecture

InlineChatContextPicker (Dropdown Container)
│
├─ InlineChatMetricsViewContextPicker (Metrics View: "Sales")
│  ├─ Revenue (measure)
│  ├─ Orders (measure)
│  └─ Country (dimension)
│
├─ InlineChatMetricsViewContextPicker (Metrics View: "Users")
│  ├─ Active Users (measure)
│  └─ Region (dimension)

The Problem

Both are called "Picker" but serve different roles:

  • The first "picks" from a dropdown menu (it IS the dropdown)
  • The second doesn't "pick" metrics views—it displays a metrics view's contents as a collapsible group

The parent/child relationship isn't obvious from the names.

Recommendation

Option A: Make the hierarchy explicit

ContextPickerDropdown.svelte          # The container
ContextPickerMetricsViewItem.svelte   # The collapsible item

Option B: Use semantic component names (recommended)

ContextPicker.svelte                  # The picker dropdown
MetricsViewGroup.svelte               # One collapsible group

Option B is clearer because:

  • Accurately describes what each component does
  • "MetricsViewGroup" clearly indicates it's a grouping component
  • The parent/child relationship is obvious
  • Shorter, more semantic names

Impact

These naming inconsistencies make the codebase harder to:

  • Search - Need multiple search terms for the same feature
  • Navigate - Unclear component relationships and file organization
  • Understand - Conceptual mismatches between layers
  • Maintain - Future developers will spend time resolving this confusion

Recommendation: Address these naming issues before merge. The terminology decision (section 1) is foundational and should be resolved first, as it informs all other naming choices.

Copy link
Contributor

@ericpgreen2 ericpgreen2 left a comment

Choose a reason for hiding this comment

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

The feature feels much better with the keyboard controls! Overall, code is nicely readable and well-documented!

  1. I've added a couple more items to the UXQA doc
  2. See below for a few specific code comments
  3. See above for a Claude Code comment about naming

Otherwise looks good to merge 👍

Comment on lines 49 to 51
{@html DOMPurify.sanitize(
convertPromptWithInlineContextToHTML(content, $contextMetadataStore),
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

The TextMessage component currently handles some assistant messages too.

Given how the inline context makes the user message rendering more complex, it could warrant encapsulating that complexity in its own UserMessage component.

@AdityaHegde AdityaHegde merged commit 4907580 into main Nov 27, 2025
12 checks passed
@AdityaHegde AdityaHegde deleted the feat/dashboard-chat-context branch November 27, 2025 04:52
AdityaHegde added a commit that referenced this pull request Nov 27, 2025
* feat: add dashboard chat context support

* Some fixes

* Update context colors

* Fix lint

* Add basic rich text input

* Add inline context add/edit support

* Update inline context in chat history

* Integrate explain anomaly with new context

* Add dimension value selection support

* Improve add/remove

* Fix backspace not removing elements accurately

* Disable context in non-explore context

* Cleanup

* Add shortcuts for dimension/measures

* Update to the latest design

* Update explain anomaly feature for new design

* Add current/recently used metrics view

* Cleanup

* Fix edge cases with prompt synching

* Move to using tiptap

* Add keyboard navigation for dropdown

* Add explicit add mention button

* Fix lint

* Final PR comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants