Skip to content

Commit

Permalink
UX Refresh of Explore Assistant (#68)
Browse files Browse the repository at this point in the history
* initial new layout

* add the title and examples

* reset the chat, submit the query

* start reconnecting everything

* fix the message display order

* improve the prompt input

* remove unused-pages

* improve layout

* remove un-used files

* improve message styling

* Improve the spinner

* improve UX

* fix summarization

* load only when ready

* connect chat to history

* persist the redux store

* clear history

* don't persist dimension and measures

* use the data toggle

* Docs(updating readme with new ui/ux gif)

---------

Co-authored-by: Luka Fontanilla <[email protected]>
  • Loading branch information
waziers and Luka Fontanilla authored Jul 31, 2024
1 parent aae2c2c commit c986fe3
Show file tree
Hide file tree
Showing 32 changed files with 5,150 additions and 1,426 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This is an extension or API plugin for Looker that integrates LLM's hosted on Vertex AI into a natural language experience powered by Looker's modeling layer.

![explore assistant](./static/Explore%20Assistant.gif)
![explore assistant](./static/explore-assistant.gif)

## Description

Expand Down
4,381 changes: 4,087 additions & 294 deletions explore-assistant-extension/package-lock.json

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions explore-assistant-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,40 @@
"node": ">=14 <17"
},
"dependencies": {
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@looker/components": "^4.1.3",
"@looker/components-providers": "1.5.31",
"@looker/embed-sdk": "^1.6.1",
"@looker/extension-sdk": "^24.2.0",
"@looker/extension-sdk-react": "^24.2.0",
"@looker/sdk": "^24.2.0",
"@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3",
"@mui/icons-material": "^5.16.5",
"@mui/material": "^5.16.5",
"@reduxjs/toolkit": "^2.2.2",
"@types/crypto-js": "^4.2.2",
"@types/react": "^17.0.80",
"clsx": "^2.1.1",
"crypto-js": "^4.2.0",
"highlight.js": "^11.9.0",
"highlightjs-lookml": "^1.0.2",
"install": "^0.13.0",
"marked": "^12.0.1",
"marked-highlight": "^2.1.1",
"npm": "^10.8.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-error-boundary": "^4.0.13",
"react-is": "^16.13.1",
"react-redux": "^8.1.3",
"react-router-dom": "^5.3.4",
"redux-persist": "^6.0.0",
"sass": "^1.72.0",
"sass-loader": "^14.1.1",
"styled-components": "^5.3.11"
"styled-components": "^5.3.11",
"tailwindcss": "^3.4.7"
},
"devDependencies": {
"@babel/cli": "^7.16.0",
Expand All @@ -53,19 +64,21 @@
"@babel/preset-typescript": "^7.16.0",
"@babel/runtime": "^7.12.5",
"@types/node": "^14.14.12",
"@types/react": "^16.14.46",
"@types/react-dom": "^16.9.19",
"@types/react-router-dom": "^5.1.5",
"@types/readable-stream": "^2.3.5",
"@types/styled-components": "^5.1.28",
"@types/styled-system": "^5.1.18",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"autoprefixer": "^10.4.19",
"babel-loader": "^8.2.2",
"babel-preset-nano-react-app": "^0.1.0",
"css-loader": "^6.8.1",
"dotenv": "^8.6.0",
"eslint": "^8.13.0",
"file-loader": "^6.2.0",
"postcss": "^8.4.40",
"postcss-loader": "^8.1.1",
"prettier": "^2.2.1",
"react-hot-loader": "^4.12.20",
"style-loader": "^3.3.3",
Expand Down
7 changes: 7 additions & 0 deletions explore-assistant-extension/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

12 changes: 2 additions & 10 deletions explore-assistant-extension/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import React, { useEffect } from 'react'
import { hot } from 'react-hot-loader/root'
import { Route, Switch, Redirect } from 'react-router-dom'
import LandingPage from './pages/LandingPage'
import ExploreAssistantPage from './pages/ExploreAssistantPage'
import ExploreChatPage from './pages/ExploreChatPage'
import { useDispatch } from 'react-redux'
import {
setExploreId,
Expand All @@ -12,6 +9,7 @@ import {
} from './slices/assistantSlice'
import { useLookerFields } from './hooks/useLookerFields'
import { useBigQueryExamples } from './hooks/useBigQueryExamples'
import AgentPage from './pages/AgentPage'

const ExploreApp = () => {
const dispatch = useDispatch()
Expand All @@ -32,13 +30,7 @@ const ExploreApp = () => {
<>
<Switch>
<Route path="/index" exact>
<LandingPage />
</Route>
<Route path="/assistant">
<ExploreAssistantPage />
</Route>
<Route path="/chat">
<ExploreChatPage />
<AgentPage />
</Route>
<Route>
<Redirect to="/index" />
Expand Down
64 changes: 32 additions & 32 deletions explore-assistant-extension/src/components/Chat/ExploreMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
import React from 'react'

import Message from './Message'
import {
Box,
Chip,
Icon,
Link,
Paragraph,
Section,
Space,
Tooltip,
} from '@looker/components'
import { Link } from '@looker/components'
import { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { useSelector } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../store'
import { Info } from '@material-ui/icons'
import {
openSidePanel,
setSidePanelExploreUrl,
} from '../../slices/assistantSlice'
import { Explore, OpenInNew, Share } from '@material-ui/icons'

interface ExploreMessageProps {
prompt: string
queryArgs: string
}

const ExploreMessage = ({ prompt, queryArgs }: ExploreMessageProps) => {
const dispatch = useDispatch()
const { exploreId } = useSelector((state: RootState) => state.assistant)
const { extensionSDK } = useContext(ExtensionContext)
const exploreHref = `/explore/${exploreId}?${queryArgs}`
const openExplore = () => {
extensionSDK.openBrowserWindow(exploreHref, '_blank')
}

const openSidePanelExplore = () => {
dispatch(setSidePanelExploreUrl(queryArgs))
dispatch(openSidePanel())
}

return (
<>
<Message actor="system" createdAt={Date.now()}>
<Box my={'u2'}>
<Space between style={{ position: 'relative'}}>
<Chip disabled>Explore</Chip>
{prompt && (
<Box position="absolute" right="-10px" top="0px" cursor='pointer'>
<Tooltip content={prompt}>
<Icon color={'ui3'} size="xxsmall" icon={<Info />} />
</Tooltip>
</Box>
)}
</Space>
</Box>
<Section>
<Paragraph>Here is the link to your explore:</Paragraph>
<Box mb="u4">
<Link href="#" onClick={openExplore} isExternal>
Visit Explore
</Link>
</Box>
</Section>
<div>
<div className="mb-2">Here is the explore we generated.</div>
<div
className="bg-gray-400 text-white rounded-md p-4 my-2 shadow-lg hover:bg-gray-500 cursor-pointer"
onClick={openSidePanelExplore}
>
<div className="flex flex-row text-md font-semibold">
<div className="flex-grow">Explore</div>
</div>
<div className="text-xs mt-2 line-clamp-3">{prompt}</div>
</div>
<div
className="cursor-pointer hover:underline text-sm text-blue-500 flex flex-col justify-center items-end"
onClick={openExplore}
>
<div>visit <OpenInNew fontSize={'small'} /></div>
</div>
</div>
</Message>
</>
)
Expand Down
54 changes: 51 additions & 3 deletions explore-assistant-extension/src/components/Chat/MarkdownText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,73 @@ const marked = new Marked(
markedHighlight({
langPrefix: 'hljs border rounded shadow syntax-highlighter language-',
highlight(code, lang) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext'
const language = hljs.getLanguage(lang) ? lang : 'markdown'
return hljs.highlight(code, { language }).value
},
}),
)

// Custom renderer to add Tailwind CSS classes
const renderer = new marked.Renderer()

renderer.heading = (text, level) => {
const tag = `h${level}`
let classes = 'font-bold text-gray-800'

switch (level) {
case 1:
classes += ' text-3xl'
break
case 2:
classes += ' text-2xl'
break
case 3:
classes += ' text-xl'
break
case 4:
classes += ' text-lg'
break
case 5:
classes += ' text-base'
break
case 6:
classes += ' text-base'
break
default:
classes += ' text-base'
break
}

return `<${tag} class="${classes}">${text}</${tag}>`
}

renderer.paragraph = (text) => {
return `<p class="mb-4">${text}</p>`
}

renderer.list = (body, ordered) => {
const tag = ordered ? 'ol' : 'ul'
const classes = 'list-disc list-inside mb-4'
return `<${tag} class="${classes}">${body}</${tag}>`
}

renderer.listitem = (text) => {
return `<li class="mb-2">${text}</li>`
}

const processText = (text: string) => {
if (!text) {
return text
}
const modifiedText = marked.parse(text, {
renderer,
gfm: true,
breaks: true,
})
return modifiedText
}

const MarkdownText = ({ text }: { text: string }) => {

return (
<div
dangerouslySetInnerHTML={{
Expand All @@ -38,4 +86,4 @@ const MarkdownText = ({ text }: { text: string }) => {
)
}

export default MarkdownText
export default MarkdownText
37 changes: 19 additions & 18 deletions explore-assistant-extension/src/components/Chat/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Card, CardContent, Paragraph, Section } from '@looker/components'
import React from 'react'

import styles from './style.module.scss'
import MarkdownText from './MarkdownText'
import clsx from 'clsx'

export const getRelativeTimeString = (dateStr: string | Date) => {
const date = new Date(dateStr)
Expand Down Expand Up @@ -74,23 +73,25 @@ interface MessageProps {
}

const Message = ({ message, actor, children }: MessageProps) => (
<Card border={'none'} width={'100%'}>
<CardContent p={0}>
<Paragraph fontSize="xsmall" color="text1" mb="u2">
{actor == 'system' && 'Gemini'}
</Paragraph>

<Section
fontSize={'small'}
className={styles.chatBubble + ' ' + styles[actor]}
<div
className={`flex ${
actor === 'user' ? 'justify-end' : 'justify-start'
} mb-4`}
>
<div className={`max-w-[70%] ${actor === 'user' ? 'order-2' : 'order-1'}`}>
<div
className={clsx(
'rounded-lg p-3 max-w-xl',
actor === 'user'
? 'bg-[rgb(237,243,253)] text-gray-800'
: 'bg-[rgb(242,242,242)] text-gray-800',
)}
>
<div className={styles.chatBubbleContent}>
{message && (<MarkdownText text={message} />)}
{children && <div>{children}</div>}
</div>
</Section>
</CardContent>
</Card>
{message && <MarkdownText text={message} />}
{children && <div>{children}</div>}
</div>
</div>
</div>
)

export default Message
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ const SummaryMessage = ({ queryArgs }: SummaryMessageProps) => {
useEffect(() => {
const fetchSummary = async () => {
const response = await summarizeExplore(queryArgs)
setSummary(response)
if (!response) {
setSummary('There was an error summarizing the data')
} else {
setSummary(response)
}
setLoading(false)
}
fetchSummary()
}, [])

console.log(summary)
return (
<Message actor="system" createdAt={Date.now()}>
<Section my={'u2'}>
Expand All @@ -31,9 +35,9 @@ const SummaryMessage = ({ queryArgs }: SummaryMessageProps) => {
<Spinner size={20} my={'u2'} />
) : (
<>
<Section maxHeight={150} scrolling={'auto'} my={'u2'}>
<div className="">
<MarkdownText text={summary} />
</Section>
</div>
</>
)}
</Section>
Expand Down
Loading

0 comments on commit c986fe3

Please sign in to comment.