aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
authorJoanne <zehan_ding@brown.edu>2025-06-22 23:12:46 -0400
committerJoanne <zehan_ding@brown.edu>2025-06-22 23:12:46 -0400
commit17ec2a19b2d2dc5ba3f99c43d86c27946de2ac71 (patch)
treeefb55f2b186f0636caefc8dd8680f918d1ddbb25 /src/client/views
parent61787b3c1cf53c0230f6142bee0df30c65971012 (diff)
successfully merged documentationtext functionality with new version of agent, however still minor issues with the agent not selecting the proper tool for documentation generation without the additional context from the topbar
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx3
-rw-r--r--src/client/views/nodes/chatbot/agentsystem/Agent.ts12
-rw-r--r--src/client/views/nodes/chatbot/agentsystem/prompts.ts16
-rw-r--r--src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx60
-rw-r--r--src/client/views/nodes/chatbot/tools/TutorialTool.ts39
-rw-r--r--src/client/views/topbar/TopBar.tsx15
6 files changed, 87 insertions, 58 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 595bbf2e9..5a54553b1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -9,6 +9,9 @@ import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksBu
import { TopBar } from '../../topbar/TopBar';
import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
import { CollectionFreeFormView } from './CollectionFreeFormView';
+import { Button } from '@dash/components';
+import { ButtonType } from '../../nodes/FontIconBox/FontIconBox';
+
import './CollectionFreeFormView.scss';
export interface CollectionFreeFormInfoUIProps {
diff --git a/src/client/views/nodes/chatbot/agentsystem/Agent.ts b/src/client/views/nodes/chatbot/agentsystem/Agent.ts
index 361c5eb2b..a16794e10 100644
--- a/src/client/views/nodes/chatbot/agentsystem/Agent.ts
+++ b/src/client/views/nodes/chatbot/agentsystem/Agent.ts
@@ -27,7 +27,7 @@ import { CodebaseSummarySearchTool } from '../tools/CodebaseSummarySearchTool';
import { FileContentTool } from '../tools/FileContentTool';
import { FileNamesTool } from '../tools/FileNamesTool';
import { CreateNewTool } from '../tools/CreateNewTool';
-//import { CreateTextDocTool } from '../tools/CreateTextDocumentTool';
+import { GPTTutorialTool } from '../tools/TutorialTool';
dotenv.config();
@@ -50,6 +50,7 @@ export class Agent {
private streamedAnswerParser: StreamedAnswerParser = new StreamedAnswerParser();
private tools: Record<string, BaseTool<ReadonlyArray<Parameter>>>;
private _docManager: AgentDocumentManager;
+ private is_dash_doc_assistant: boolean;
// Dynamic tool registry for tools created at runtime
private dynamicToolRegistry: Map<string, BaseTool<ReadonlyArray<Parameter>>> = new Map();
// Callback for notifying when tools are created and need reload
@@ -74,7 +75,8 @@ export class Agent {
csvData: () => { filename: string; id: string; text: string }[],
createImage: (result: Upload.FileInformation & Upload.InspectionResults, options: DocumentOptions) => void,
createCSVInDash: (url: string, title: string, id: string, data: string) => void,
- docManager: AgentDocumentManager
+ docManager: AgentDocumentManager,
+ isDashDocAssistant: boolean
) {
// Initialize OpenAI client with API key from environment
this.client = new OpenAI({ apiKey: process.env.OPENAI_KEY, dangerouslyAllowBrowser: true });
@@ -82,6 +84,7 @@ export class Agent {
this._history = history;
this._csvData = csvData;
this._docManager = docManager;
+ this.is_dash_doc_assistant = isDashDocAssistant;
// Initialize dynamic tool registry
this.dynamicToolRegistry = new Map();
@@ -100,6 +103,7 @@ export class Agent {
codebaseSummarySearch: new CodebaseSummarySearchTool(this.vectorstore),
fileContent: new FileContentTool(this.vectorstore),
fileNames: new FileNamesTool(this.vectorstore),
+ generateTutorialNode: new GPTTutorialTool(this._docManager),
};
// Add the createNewTool after other tools are defined
@@ -139,7 +143,7 @@ export class Agent {
const instance: BaseTool<ReadonlyArray<Parameter>> = new ToolClass();
- // Prefer the tool’s self-declared name (matches <action> tag)
+ // Prefer the tool's self-declared name (matches <action> tag)
const key = (instance.name || '').trim() || legacyKey;
// Check for duplicates
@@ -756,7 +760,7 @@ export class Agent {
const docSummaries = () => JSON.stringify(this._docManager.listDocs);
const chatHistory = this._history();
- return getReactPrompt(allTools, docSummaries, chatHistory);
+ return getReactPrompt(allTools, docSummaries, chatHistory, this.is_dash_doc_assistant);
}
/**
diff --git a/src/client/views/nodes/chatbot/agentsystem/prompts.ts b/src/client/views/nodes/chatbot/agentsystem/prompts.ts
index fcb4ab450..b7678bd08 100644
--- a/src/client/views/nodes/chatbot/agentsystem/prompts.ts
+++ b/src/client/views/nodes/chatbot/agentsystem/prompts.ts
@@ -10,7 +10,7 @@
import { BaseTool } from '../tools/BaseTool';
import { Parameter } from '../types/tool_types';
-export function getReactPrompt(tools: BaseTool<ReadonlyArray<Parameter>>[], summaries: () => string, chatHistory: string): string {
+export function getReactPrompt(tools: BaseTool<ReadonlyArray<Parameter>>[], summaries: () => string, chatHistory: string, isDashDocAssistant?: boolean): string {
const toolDescriptions = tools
.map(
tool => `
@@ -21,11 +21,21 @@ export function getReactPrompt(tools: BaseTool<ReadonlyArray<Parameter>>[], summ
)
.join('\n');
+ const dashDocContext = isDashDocAssistant
+ ? `
+ <dash_doc_assistant_context>
+ <point>You are acting as a help assistant for a software application called Dash.</point>
+ <point>All user queries, unless otherwise specified, should be interpreted as questions about how to use Dash or about Dash's functionality.</point>
+ <point>You should prioritize using the 'generateTutorialNode' tool to answer user questions about Dash.</point>
+ </dash_doc_assistant_context>
+ `
+ : '';
+
return `<system_message>
<task>
You are an advanced AI assistant equipped with tools to answer user queries efficiently. You operate in a loop that is RIGIDLY structured and requires the use of specific tags and formats for your responses. Your goal is to provide accurate and well-structured answers to user queries. Below are the guidelines and information you can use to structure your approach to accomplishing this task.
</task>
-
+ ${dashDocContext}
<critical_points>
<point>**STRUCTURE**: Always use the correct stage tags (e.g., <stage number="2" role="assistant">) for every response. Use only even-numbered assisntant stages for your responses.</point>
<point>**STOP after every stage and wait for input. Do not combine multiple stages in one response.**</point>
@@ -189,7 +199,7 @@ export function getReactPrompt(tools: BaseTool<ReadonlyArray<Parameter>>[], summ
<action_input>
<action_input_description>Getting information from the relevant websites about Qatar's tourism impact during the World Cup.</action_input_description>
<inputs>
- <urls>[***URLS to search elided, but they will be comma seperated double quoted strings"]</urls>
+ <chunk_ids>[***CHUNK IDS to search elided, but they will be comma separated double quoted strings"]</chunk_ids>
</inputs>
</action_input>
</stage>
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index db01b7c88..18d0266af 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -7,23 +7,21 @@
* with support for follow-up questions and citation management.
*/
-import dotenv from 'dotenv';
import { ObservableSet, action, computed, makeObservable, observable, observe, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import OpenAI, { ClientOptions } from 'openai';
import * as React from 'react';
import { v4 as uuidv4 } from 'uuid';
import { ClientUtils, OmitKeys } from '../../../../../ClientUtils';
-import { Doc, DocListCast, Opt } from '../../../../../fields/Doc';
-import { DocData, DocLayout, DocViews } from '../../../../../fields/DocSymbols';
+import { Doc, Opt } from '../../../../../fields/Doc';
+import { DocViews } from '../../../../../fields/DocSymbols';
import { Id } from '../../../../../fields/FieldSymbols';
import { RichTextField } from '../../../../../fields/RichTextField';
import { ScriptField } from '../../../../../fields/ScriptField';
-import { CsvCast, DocCast, NumCast, PDFCast, RTFCast, StrCast, VideoCast, AudioCast } from '../../../../../fields/Types';
+import { CsvCast, DocCast, PDFCast, RTFCast, StrCast, VideoCast, AudioCast } from '../../../../../fields/Types';
import { DocUtils } from '../../../../documents/DocUtils';
import { CollectionViewType, DocumentType } from '../../../../documents/DocumentTypes';
import { Docs, DocumentOptions } from '../../../../documents/Documents';
-import { DocServer } from '../../../../DocServer';
import { DocumentManager } from '../../../../util/DocumentManager';
import { ImageUtils } from '../../../../util/Import & Export/ImageUtils';
import { LinkManager } from '../../../../util/LinkManager';
@@ -44,11 +42,8 @@ import './ChatBox.scss';
import MessageComponentBox from './MessageComponent';
import { OpenWhere } from '../../OpenWhere';
import { Upload } from '../../../../../server/SharedMediaTypes';
-import { DocumentMetadataTool } from '../tools/DocumentMetadataTool';
import { AgentDocumentManager } from '../utils/AgentDocumentManager';
-dotenv.config();
-
export type parsedDocData = {
doc_type: string;
data: unknown;
@@ -58,6 +53,7 @@ export type parsedDocData = {
data_useCors?: boolean;
};
export type parsedDoc = DocumentOptions & parsedDocData;
+
/**
* ChatBox is the main class responsible for managing the interaction between the user and the assistant,
* handling documents, and integrating with OpenAI for tasks such as document analysis, chat functionality,
@@ -124,7 +120,15 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.vectorstore = new Vectorstore(this.vectorstore_id, this.docManager);
// Create an agent with the vectorstore
- this.agent = new Agent(this.vectorstore, this.retrieveFormattedHistory.bind(this), this.retrieveCSVData.bind(this), this.createImageInDash.bind(this), this.createCSVInDash.bind(this), this.docManager);
+ this.agent = new Agent(
+ this.vectorstore,
+ this.retrieveFormattedHistory.bind(this),
+ this.retrieveCSVData.bind(this),
+ this.createImageInDash.bind(this),
+ this.createCSVInDash.bind(this),
+ this.docManager,
+ this.dataDoc.is_dash_doc_assistant === 'true'
+ );
// Set up the tool created callback
this.agent.setToolCreatedCallback(this.handleToolCreated);
@@ -388,7 +392,11 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
// Get the response from the agent
- const response = await this.agent.askAgent(trimmedText, onProcessingUpdate, onAnswerUpdate);
+ let userQuery = trimmedText;
+ if (this.dataDoc.is_dash_doc_assistant) {
+ userQuery = `The user is asking a question about Dash functionality. Their question is: "${trimmedText}". You should use the generateTutorialNode tool to answer this question.`;
+ }
+ const response = await this.agent.askAgent(userQuery, onProcessingUpdate, onAnswerUpdate);
// Push the final message to history
runInAction(() => {
@@ -476,7 +484,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const data = (doc as parsedDocData).data;
const ndoc = (() => {
switch (doc.doc_type) {
- default:
+ default:
case supportedDocTypes.note: return Docs.Create.TextDocument(data as string, options);
case supportedDocTypes.comparison: return this.createComparison(JSON.parse(data as string) as parsedDoc[], options);
case supportedDocTypes.flashcard: return this.createFlashcard(JSON.parse(data as string) as parsedDoc[], options);
@@ -487,22 +495,22 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
case supportedDocTypes.web:
// Create web document with enhanced safety options
const webOptions = {
- ...options,
+ ...options,
data_useCors: true
};
-
+
// If iframe_sandbox was passed from AgentDocumentManager, add it to the options
if ('_iframe_sandbox' in options) {
(webOptions as any)._iframe_sandbox = options._iframe_sandbox;
}
-
+
return Docs.Create.WebDocument(data as string, webOptions);
case supportedDocTypes.dataviz: return Docs.Create.DataVizDocument('/users/rz/Downloads/addresses.csv', options);
case supportedDocTypes.pdf: return Docs.Create.PdfDocument(data as string, options);
case supportedDocTypes.video: return Docs.Create.VideoDocument(data as string, options);
case supportedDocTypes.diagram: return Docs.Create.DiagramDocument(undefined, { text: data as unknown as RichTextField, ...options}); // text: can take a string or RichTextField but it's typed for RichTextField.
-
- // case supportedDocumentTypes.dataviz:
+
+ // case supportedDocumentTypes.dataviz:
// {
// const { fileUrl, id } = await Networking.PostToServer('/createCSV', {
// filename: (options.title as string).replace(/\s+/g, '') + '.csv',
@@ -527,7 +535,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const arr = this.createCollectionWithChildren(JSON.parse(data as string) as parsedDoc[], true).filter(d=>d).map(d => d!);
const collOpts = { _width:300, _height: 300, _layout_fitWidth: true, _freeform_backgroundGrid: true, ...options, };
return (() => {
- switch (options.type_collection) {
+ switch (options.type_collection) {
case CollectionViewType.Tree: return Docs.Create.TreeDocument(arr, collOpts);
case CollectionViewType.Stacking: return Docs.Create.StackingDocument(arr, collOpts);
case CollectionViewType.Masonry: return Docs.Create.MasonryDocument(arr, collOpts);
@@ -536,9 +544,9 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
case CollectionViewType.Carousel3D: return Docs.Create.Carousel3DDocument(arr, collOpts);
case CollectionViewType.Multicolumn: return Docs.Create.CarouselDocument(arr, collOpts);
default: return Docs.Create.FreeformDocument(arr, collOpts);
- }
- })();
- }
+ }
+ })();
+ }
// case supportedDocumentTypes.map: return Docs.Create.MapDocument([], options);
// case supportedDocumentTypes.button: return Docs.Create.ButtonDocument(options);
// case supportedDocumentTypes.trail: return Docs.Create.PresDocument(options);
@@ -646,8 +654,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
} else {
console.warn(`Chunk not found for chunk ID: ${chunkId}`);
}
- return;
- }
+ return;
+ }
console.log(`Found chunk in document:`, foundChunk);
@@ -656,7 +664,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const directMatchSegmentStart = this.getDirectMatchingSegmentStart(doc, citation.direct_text || '', foundChunk.indexes || []);
if (directMatchSegmentStart) {
await this.goToMediaTimestamp(doc, directMatchSegmentStart, foundChunk.chunkType);
- } else {
+ } else {
console.error('No direct matching segment found for the citation.');
}
} else if (foundChunk.chunkType === CHUNK_TYPE.TABLE || foundChunk.chunkType === CHUNK_TYPE.IMAGE) {
@@ -911,7 +919,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
console.error(`Maximum verification attempts (${attempt}) reached for document ${doc.id}`);
// Last resort: force re-creation of the document view
- if (isPDF) {
+ if (isPDF) {
console.log('Forcing document recreation as last resort');
DocumentManager.Instance.showDocument(doc, {
willZoomCentered: true,
@@ -939,7 +947,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return;
}
- this.processPDFDocumentView(doc, isPDF, citation, foundChunk);
+ this.processPDFDocumentView(doc, isPDF, citation, foundChunk);
} catch (error) {
console.error(`Error on verification attempt ${attempt}:`, error);
if (attempt < 5) {
@@ -1395,7 +1403,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
</div>
)}
<div className="chat-header">
- <h2>{this.userName()}&apos;s AI Assistant</h2>
+ <h2>{StrCast(this.dataDoc.title) || `${this.userName()}'s AI Assistant`}</h2>
<div className="font-size-control" onClick={this.toggleFontSizeModal}>
{this.renderFontSizeIcon()}
</div>
diff --git a/src/client/views/nodes/chatbot/tools/TutorialTool.ts b/src/client/views/nodes/chatbot/tools/TutorialTool.ts
index 08e4e1409..1624f0439 100644
--- a/src/client/views/nodes/chatbot/tools/TutorialTool.ts
+++ b/src/client/views/nodes/chatbot/tools/TutorialTool.ts
@@ -11,7 +11,9 @@ import { RichTextField } from '../../../../../fields/RichTextField';
import { DocumentViewInternal } from '../../DocumentView';
import { Docs } from '../../../../documents/Documents';
import { OpenWhere } from '../../OpenWhere';
-import { CollectionFreeFormView } from '../../../collections/collectionFreeForm';
+import { CollectionFreeFormView } from '../../../collections/collectionFreeForm/CollectionFreeFormView';
+import { AgentDocumentManager } from '../utils/AgentDocumentManager';
+import { Node as ProseMirrorNode } from 'prosemirror-model';
const generateTutorialNodeToolParams = [
{
@@ -28,20 +30,26 @@ const generateTutorialNodeToolInfo: ToolInfo<typeof generateTutorialNodeToolPara
parameterRules: generateTutorialNodeToolParams,
citationRules: "No citation needed for this tool's output.",
};
-const applyFormatting = (markdownText: string): { doc: any; plainText: string } => {
+
+interface FormattedDocument {
+ doc: ProseMirrorNode;
+ plainText: string;
+}
+
+const applyFormatting = (markdownText: string): FormattedDocument => {
const lines = markdownText.split('\n');
- const nodes: any[] = [];
+ const nodes: ProseMirrorNode[] = [];
let plainText = '';
let i = 0;
- let currentListItems: any[] = [];
- let currentParagraph: any[] = [];
- let currentOrderedListItems: any[] = [];
+ let currentListItems: ProseMirrorNode[] = [];
+ let currentParagraph: ProseMirrorNode[] = [];
+ let currentOrderedListItems: ProseMirrorNode[] = [];
let inOrderedList = false;
let inBulletList = false;
- const processBoldText = (text: string) => {
+ const processBoldText = (text: string): ProseMirrorNode[] => {
const boldRegex = /\*\*(.*?)\*\*/g;
- const parts = [];
+ const parts: ProseMirrorNode[] = [];
let lastIndex = 0;
let match;
@@ -58,7 +66,7 @@ const applyFormatting = (markdownText: string): { doc: any; plainText: string }
return parts.length > 0 ? parts : [schema.text(text)];
};
- const flushListItems = () => {
+ const flushListItems = (): void => {
if (currentListItems.length > 0) {
nodes.push(schema.nodes.ordered_list.create({ mapStyle: 'bullet' }, currentListItems));
nodes.push(schema.nodes.paragraph.create());
@@ -73,14 +81,14 @@ const applyFormatting = (markdownText: string): { doc: any; plainText: string }
}
};
- const flushParagraph = () => {
+ const flushParagraph = (): void => {
if (currentParagraph.length > 0) {
nodes.push(schema.nodes.paragraph.create({}, currentParagraph));
currentParagraph = [];
}
};
- const processHeader = (line: string) => {
+ const processHeader = (line: string): boolean => {
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
if (headerMatch) {
const level = Math.min(headerMatch[1].length, 6); // Cap at h6
@@ -138,12 +146,11 @@ const applyFormatting = (markdownText: string): { doc: any; plainText: string }
};
export class GPTTutorialTool extends BaseTool<typeof generateTutorialNodeToolParams> {
- private _createDocInDash: (doc: parsedDoc) => Doc | undefined;
+ private _docManager: AgentDocumentManager;
- constructor(createDocInDash: (doc: parsedDoc) => Doc | undefined) {
+ constructor(docManager: AgentDocumentManager) {
super(generateTutorialNodeToolInfo);
-
- this._createDocInDash = createDocInDash;
+ this._docManager = docManager;
}
async execute(args: ParametersType<typeof generateTutorialNodeToolParams>): Promise<Observation[]> {
@@ -158,7 +165,7 @@ export class GPTTutorialTool extends BaseTool<typeof generateTutorialNodeToolPar
// Build the ProseMirror‐in‐JSON + plain-text for RichTextField
const rtfData = {
- doc: (doc as any).toJSON ? (doc as any).toJSON() : doc,
+ doc: doc.toJSON ? doc.toJSON() : doc,
selection: { type: 'text', anchor: 0, head: 0 },
storedMarks: [],
};
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 5d8583873..9b24219cf 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -3,10 +3,9 @@ import { Button, Dropdown, DropdownType, IconButton, isDark, Size, Type } from '
import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Flip } from 'react-awesome-reveal';
import { FaBug } from 'react-icons/fa';
-import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
-import { Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc';
+import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
+import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc';
import { AclAdmin, DashVersion } from '../../../fields/DocSymbols';
import { StrCast } from '../../../fields/Types';
import { GetEffectiveAcl } from '../../../fields/util';
@@ -28,10 +27,6 @@ import { ObservableReactComponent } from '../ObservableReactComponent';
import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
import './TopBar.scss';
import { OpenWhere } from '../nodes/OpenWhere';
-import { ChatBox } from '../nodes/chatbot/chatboxcomponents/ChatBox';
-import { FieldViewProps } from '../nodes/FieldView';
-import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { PinProps } from '../PinFuncs';
import { Docs } from '../../documents/Documents';
/**
@@ -90,7 +85,7 @@ export class TopBar extends ObservableReactComponent<object> {
{Doc.ActiveDashboard ? (
<IconButton
onClick={this.navigateToHome}
- icon={<FontAwesomeIcon icon={DocListCast(Doc.MySharedDocs.data_dashboards).some(dash => !DocListCast(Doc.MySharedDocs.viewed).includes(dash)) ? 'portrait' : 'home'} />}
+ icon={<FontAwesomeIcon icon={DocListCast(Doc.MySharedDocs?.data_dashboards)?.some(dash => !DocListCast(Doc.MySharedDocs?.viewed)?.includes(dash)) ? 'portrait' : 'home'} />}
color={this.color}
background={this.backgroundColor}
/>
@@ -231,9 +226,11 @@ export class TopBar extends ObservableReactComponent<object> {
val: 'tutorialagent',
text: 'Ask AI!',
onClick: () => {
+ const userEmail = ClientUtils.CurrentUserEmail();
+ const userName = userEmail.split('@')[0];
const doc = Docs.Create.ChatDocument({
chat: 'Welcome to your help assistant for Dash. Ask any Dash-related questions to get started.',
- title: 'Dash Documentation Assistant',
+ title: `${userName}'s Dash Help Assistant`,
is_dash_doc_assistant: 'true',
});
DocumentViewInternal.addDocTabFunc(doc, OpenWhere.addRight);