diff --git a/analytics/husky.analytics.ts b/analytics/husky.analytics.ts index af63747d..2983b854 100644 --- a/analytics/husky.analytics.ts +++ b/analytics/husky.analytics.ts @@ -59,8 +59,8 @@ export const useHuskyAnalytics = () => { captureEvent(events.husky_action_card_clicked, {...action}) } - function trackSharedBlog(blogId: string, mode: string) { - captureEvent(events.husky_open_shared_blog, {blogId, mode }); + function trackSharedBlog(blogId: string, mode: string, question: string) { + captureEvent(events.husky_open_shared_blog, {blogId, mode, question }); } function trackFollowupQuestionClick(mode: string, question: string, blogId?: string | null) { @@ -75,12 +75,12 @@ export const useHuskyAnalytics = () => { captureEvent(events.husky_user_feedback_clicked, { question, answer }); } - function trackFeedbackStatus( status: string) { - captureEvent(events.husky_user_feedback_status, { status }); + function trackFeedbackStatus( status: string, rating: string, question: string) { + captureEvent(events.husky_user_feedback_status, { feedbackStatus: status, rating, question }); } - function trackAiResponse(status: string, mode: string) { - captureEvent(events.husky_ai_response, { status, mode, }); + function trackAiResponse(status: string, mode: string, isBlog: boolean, question: string) { + captureEvent(events.husky_ai_response, { huskyResponse: status, mode, isBlog, question }); } function trackRegenerate() { diff --git a/components/core/husky/husky-ai.tsx b/components/core/husky/husky-ai.tsx index 86145829..df262ec6 100644 --- a/components/core/husky/husky-ai.tsx +++ b/components/core/husky/husky-ai.tsx @@ -107,7 +107,7 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose const onPromptClicked = async (question: string) => { try { - const { authToken } = await getUserCredentials(); + const { authToken, userInfo } = await getUserCredentials(); if (!authToken) { return; } @@ -116,19 +116,19 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose setAnswerLoadingStatus(true); setActiveTab(DEFAULT_TAB_ITEMS[0].key); setChats([]); - trackAiResponse('initiated', 'prompt'); - const result = await getHuskyResponse(authToken, question, selectedSource, chatUid, null, null, mode === 'blog'); // Fixed function name + trackAiResponse('initiated', 'prompt', mode === 'blog', question); + const result = await getHuskyResponse(userInfo, authToken, question, selectedSource, chatUid, null, null, mode === 'blog'); // Fixed function name setAskingQuestion(''); setAnswerLoadingStatus(false); if (result.isError) { - trackAiResponse('error', 'prompt'); + trackAiResponse('error', 'prompt', mode === 'blog', question); setChats((prevChats) => [...prevChats, { question, answer: '', isError: true }]); return; } - trackAiResponse('success', 'prompt'); + trackAiResponse('success', 'prompt', mode === 'blog', question); setChats(result.data ? [{ ...result.data, isError: false }] : []); } catch (error) { - trackAiResponse('error', 'prompt'); + trackAiResponse('error', 'prompt', mode === 'blog', question); } }; @@ -149,7 +149,7 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose const onFollowupClicked = async (question: string) => { try { - const { authToken } = await getUserCredentials(); + const { authToken, userInfo } = await getUserCredentials(); if (!authToken) { return; } @@ -158,23 +158,23 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose return; } trackFollowupQuestionClick(mode, question, blogId); - trackAiResponse('initiated', 'followup'); + trackAiResponse('initiated', 'followup', mode === 'blog', question); const chatUid = checkAndSetPromptId(); setAskingQuestion(question); setAnswerLoadingStatus(true); - const result = await getHuskyResponse(authToken, question, selectedSource, chatUid, mode === 'blog' && chats.length === 1 ? chats[0].question : null, mode === 'blog' && chats.length === 1 ? chats[0].answer : null, mode === 'blog'); // Fixed function name + const result = await getHuskyResponse(userInfo, authToken, question, selectedSource, chatUid, mode === 'blog' && chats.length === 1 ? chats[0].question : null, mode === 'blog' && chats.length === 1 ? chats[0].answer : null, mode === 'blog'); // Fixed function name setAskingQuestion(''); setAnswerLoadingStatus(false); if (result.isError) { - trackAiResponse('error', 'followup'); + trackAiResponse('error', 'followup', mode === 'blog', question); setChats((prevChats) => [...prevChats, { question, answer: '', isError: true }]); return; } - trackAiResponse('success', 'followup'); + trackAiResponse('success', 'followup', mode === 'blog', question); setChats((prevChats) => result.data ? [...prevChats, { ...result.data, isError: false }] : prevChats); } catch (error) { - trackAiResponse('error', 'followup'); + trackAiResponse('error', 'followup', mode === 'blog', question); } }; @@ -199,7 +199,7 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose const onHuskyInput = async (query: string) => { try { - const { authToken } = await getUserCredentials(); + const { authToken, userInfo } = await getUserCredentials(); if (!authToken) { return; } @@ -216,19 +216,19 @@ function HuskyAi({ mode = 'chat', initialChats = [], isLoggedIn, blogId, onClose trackUserPrompt(query); setAskingQuestion(query); setAnswerLoadingStatus(true); - trackAiResponse('initiated', 'user-input'); - const result = await getHuskyResponse(authToken, query, selectedSource, chatUid); + trackAiResponse('initiated', 'user-input', mode === 'blog', query); + const result = await getHuskyResponse(userInfo, authToken, query, selectedSource, chatUid); setAskingQuestion(''); setAnswerLoadingStatus(false); if (result.isError) { - trackAiResponse('error', 'user-input'); + trackAiResponse('error', 'user-input', mode === 'blog', query); setChats((prevChats) => [...prevChats, { question: query, answer: '', isError: true }]); return; } - trackAiResponse('success', 'user-input'); + trackAiResponse('success', 'user-input', mode === 'blog', query); setChats((prevChats) => result.data ? [...prevChats, { ...result.data, isError: false }] : prevChats); } catch (error) { - trackAiResponse('error', 'user-input'); + trackAiResponse('error', 'user-input', mode === 'blog', query); } }; diff --git a/components/core/husky/husky-chat-answer.tsx b/components/core/husky/husky-chat-answer.tsx index 489e2552..48ff054a 100644 --- a/components/core/husky/husky-chat-answer.tsx +++ b/components/core/husky/husky-chat-answer.tsx @@ -1,7 +1,6 @@ import Markdown from 'markdown-to-jsx'; -import { useRef, useState } from 'react'; - import CopyText from '../copy-text'; +import HuskyCodeBlock from './husky-code-block'; interface HuskyChatAnswerProps { mode: 'blog' | 'chat'; @@ -29,22 +28,24 @@ function HuskyChatAnswer({ mode, answer, isLastIndex, question, onCopyAnswer, on
{mode !== 'blog' && (

- + Answer Answer

)}
@@ -95,6 +96,11 @@ function HuskyChatAnswer({ mode, answer, isLastIndex, question, onCopyAnswer, on gap: 8px; padding: 14px; border-radius: 8px; + max-width: 100%; + } + + .chat__ans__text div:first-child { + max-width: 100%; } .chat__ans__text--blog { @@ -138,4 +144,4 @@ function HuskyChatAnswer({ mode, answer, isLastIndex, question, onCopyAnswer, on ); } -export default HuskyChatAnswer; +export default HuskyChatAnswer; \ No newline at end of file diff --git a/components/core/husky/husky-code-block.tsx b/components/core/husky/husky-code-block.tsx new file mode 100644 index 00000000..67c88498 --- /dev/null +++ b/components/core/husky/husky-code-block.tsx @@ -0,0 +1,354 @@ +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { a11yDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import CopyText from '../copy-text'; + +function HuskyCodeBlock(props: any) { + const languages = new Set([ + 'abap', + 'abnf', + 'actionscript', + 'ada', + 'agda', + 'al', + 'antlr4', + 'apacheconf', + 'apex', + 'apl', + 'applescript', + 'aql', + 'arduino', + 'arff', + 'asciidoc', + 'asm6502', + 'asmatmel', + 'aspnet', + 'autohotkey', + 'autoit', + 'avisynth', + 'avroIdl', + 'bash', + 'basic', + 'batch', + 'bbcode', + 'bicep', + 'birb', + 'bison', + 'bnf', + 'brainfuck', + 'brightscript', + 'bro', + 'bsl', + 'c', + 'cfscript', + 'chaiscript', + 'cil', + 'clike', + 'clojure', + 'cmake', + 'cobol', + 'coffeescript', + 'concurnas', + 'coq', + 'cpp', + 'crystal', + 'csharp', + 'cshtml', + 'csp', + 'cssExtras', + 'css', + 'csv', + 'cypher', + 'd', + 'dart', + 'dataweave', + 'dax', + 'dhall', + 'diff', + 'django', + 'dnsZoneFile', + 'docker', + 'dot', + 'ebnf', + 'editorconfig', + 'eiffel', + 'ejs', + 'elixir', + 'elm', + 'erb', + 'erlang', + 'etlua', + 'excelFormula', + 'factor', + 'falselang', + 'firestoreSecurityRules', + 'flow', + 'fortran', + 'fsharp', + 'ftl', + 'gap', + 'gcode', + 'gdscript', + 'gedcom', + 'gherkin', + 'git', + 'glsl', + 'gml', + 'gn', + 'goModule', + 'go', + 'graphql', + 'groovy', + 'haml', + 'handlebars', + 'haskell', + 'haxe', + 'hcl', + 'hlsl', + 'hoon', + 'hpkp', + 'hsts', + 'http', + 'ichigojam', + 'icon', + 'icuMessageFormat', + 'idris', + 'iecst', + 'ignore', + 'inform7', + 'ini', + 'io', + 'j', + 'java', + 'javadoc', + 'javadoclike', + 'javascript', + 'javastacktrace', + 'jexl', + 'jolie', + 'jq', + 'jsExtras', + 'jsTemplates', + 'jsdoc', + 'json', + 'json5', + 'jsonp', + 'jsstacktrace', + 'jsx', + 'julia', + 'keepalived', + 'keyman', + 'kotlin', + 'kumir', + 'kusto', + 'latex', + 'latte', + 'less', + 'lilypond', + 'liquid', + 'lisp', + 'livescript', + 'llvm', + 'log', + 'lolcode', + 'lua', + 'magma', + 'makefile', + 'markdown', + 'markupTemplating', + 'markup', + 'matlab', + 'maxscript', + 'mel', + 'mermaid', + 'mizar', + 'mongodb', + 'monkey', + 'moonscript', + 'n1ql', + 'n4js', + 'nand2tetrisHdl', + 'naniscript', + 'nasm', + 'neon', + 'nevod', + 'nginx', + 'nim', + 'nix', + 'nsis', + 'objectivec', + 'ocaml', + 'opencl', + 'openqasm', + 'oz', + 'parigp', + 'parser', + 'pascal', + 'pascaligo', + 'pcaxis', + 'peoplecode', + 'perl', + 'phpExtras', + 'php', + 'phpdoc', + 'plsql', + 'powerquery', + 'powershell', + 'processing', + 'prolog', + 'promql', + 'properties', + 'protobuf', + 'psl', + 'pug', + 'puppet', + 'pure', + 'purebasic', + 'purescript', + 'python', + 'q', + 'qml', + 'qore', + 'qsharp', + 'r', + 'racket', + 'reason', + 'regex', + 'rego', + 'renpy', + 'rest', + 'rip', + 'roboconf', + 'robotframework', + 'ruby', + 'rust', + 'sas', + 'sass', + 'scala', + 'scheme', + 'scss', + 'shellSession', + 'smali', + 'smalltalk', + 'smarty', + 'sml', + 'solidity', + 'solutionFile', + 'soy', + 'sparql', + 'splunkSpl', + 'sqf', + 'sql', + 'squirrel', + 'stan', + 'stylus', + 'swift', + 'systemd', + 't4Cs', + 't4Templating', + 't4Vb', + 'tap', + 'tcl', + 'textile', + 'toml', + 'tremor', + 'tsx', + 'tt2', + 'turtle', + 'twig', + 'typescript', + 'typoscript', + 'unrealscript', + 'uorazor', + 'uri', + 'v', + 'vala', + 'vbnet', + 'velocity', + 'verilog', + 'vhdl', + 'vim', + 'visualBasic', + 'warpscript', + 'wasm', + 'webIdl', + 'wiki', + 'wolfram', + 'wren', + 'xeora', + 'xmlDoc', + 'xojo', + 'xquery', + 'yaml', + 'yang', + 'zig', + ]); + + let codeText = props.children.trim(); + let language = 'bash'; + + const firstLineEndIndex = codeText.indexOf('\n'); + if (firstLineEndIndex !== -1) { + const firstWord = codeText.substring(0, firstLineEndIndex).trim(); + if (languages.has(firstWord)) { + language = firstWord; + codeText = codeText.substring(firstLineEndIndex + 1).trim(); + } + } + + return ( + <> +
+
+

{language}

+ + + Copy Code Copy Code + + +
+ + {codeText} + +
+ + + ); +} + +export default HuskyCodeBlock \ No newline at end of file diff --git a/components/core/husky/husky-empty-chat.tsx b/components/core/husky/husky-empty-chat.tsx index ec41437b..119b1160 100644 --- a/components/core/husky/husky-empty-chat.tsx +++ b/components/core/husky/husky-empty-chat.tsx @@ -12,6 +12,10 @@ function HuskyEmptyChat({ onPromptClicked }: HuskyEmptyChatProps) { { text: 'What initiatives or programs does Protocol Labs offer to foster innovation in decentralized technologies?', icon: '/icons/send-black.svg' }, ]; + const isMobileDevice = () => { + return /Mobi|Android/i.test(navigator.userAgent); + }; + const { trackExplorationPromptSelection } = useHuskyAnalytics(); const textareaRef = useRef(null); @@ -33,12 +37,22 @@ function HuskyEmptyChat({ onPromptClicked }: HuskyEmptyChatProps) { await onPromptClicked(ques); }; + const handleKeyDown = (e: React.KeyboardEvent) => { + const isMobileOrTablet = /Mobi|Android|iPad|iPhone/i.test(navigator.userAgent); + if (!isMobileOrTablet && window.innerWidth >= 1024) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); // Prevents adding a new line + handlePromptSubmission(); // Submits the form + } + } + }; + return ( <>
- + Husky Bone Blue

What is Husky?

@@ -49,26 +63,38 @@ function HuskyEmptyChat({ onPromptClicked }: HuskyEmptyChatProps) {

- + Husky Line Logo

Traverse the Protocol Labs with Husky

- -