+ <>
+ {!isChartsExpanded ? (
+ // Default Single-Column Layout with Drawer
+
+ {/* Page Header */}
+
+
+ {/* Left Panel - Chat (393px) */}
+
+ {renderChat()}
+
- {/* Right Panel - Financial Charts Analysis */}
-
-
-
-
- Progress Agentic RAG Financial Charts Analysis
-
-
- {/* Dynamic Charts from Progress Agentic RAG answer (up to 3) */}
-
- {selectedCharts.length === 0 && (
-
- No charts available for the latest answer.
+ {/* Right Panel - Charts (flex-1) */}
+
+ {/* Glassmorphism Card */}
+
+ {/* Close Button */}
+
+
+ {/* Charts Display */}
+
+ {selectedCharts.slice(0, 3).map((chart, idx) => (
+
+
+ {chart.title}
- )}
- {selectedCharts.length > 0 && (
-
- {selectedCharts.slice(0, 3).map((chart, idx) => (
-
-
- {chart.title}
-
-
-
-
-
-
-
-
-
- {chart.series.map((series) => (
-
- ))}
-
-
-
- ))}
-
- )}
-
+
+
+
+
+
+
+
+
+ {chart.series.map((series) => (
+
+ ))}
+
+
+
+ ))}
-
+
+
-
-
+ )}
+ >
);
}
diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx
index 553b3f0..83f8fa5 100644
--- a/client/src/pages/Home.tsx
+++ b/client/src/pages/Home.tsx
@@ -1,43 +1,139 @@
-import { Button } from '@progress/kendo-react-buttons';
-import { sparklesIcon } from '@progress/kendo-svg-icons';
-import { useNavigate } from 'react-router-dom';
+import React from "react";
+import { type TextAreaChangeEvent } from "@progress/kendo-react-inputs";
+import { SearchInput } from "../components/SearchInput";
+import { useNavigate } from "react-router-dom";
+
+// Figma design asset URLs
+const imgBot = `${import.meta.env.BASE_URL}bot.svg`;
+const imgChartArea = `${import.meta.env.BASE_URL}chart-area.svg`;
+const imgScanSearch = `${import.meta.env.BASE_URL}scan-search.svg`;
+const imgSparkles = `${import.meta.env.BASE_URL}sparkles.svg`;
+const imgBackground = `${import.meta.env.BASE_URL}background.svg`;
export default function Home() {
const navigate = useNavigate();
+ const [searchQuery, setSearchQuery] = React.useState("");
+
+ const handleSearchChange = (event: TextAreaChangeEvent) => {
+ setSearchQuery(String(event.target.value || ""));
+ };
+
+ const handleSearchSubmit = () => {
+ if (searchQuery.trim()) {
+ navigate("/ai-search", { state: { query: searchQuery.trim() } });
+ }
+ };
- const handleExploreClick = () => {
- navigate('/knowledge-assistant');
+ const handleKeyPress = (event: React.KeyboardEvent) => {
+ if (event.key === "Enter" && searchQuery.trim()) {
+ handleSearchSubmit();
+ }
+ };
+
+ const demos = [
+ {
+ name: "Intelligent Search",
+ icon: imgScanSearch,
+ path: "/ai-search",
+ },
+ {
+ name: "Financial Analysis",
+ icon: imgChartArea,
+ path: "/finance-analysis",
+ },
+ {
+ name: "Knowledge Assistant",
+ icon: imgBot,
+ path: "/knowledge-assistant",
+ },
+ {
+ name: "Agentic RAG Value",
+ icon: imgSparkles,
+ path: "/value-proposition",
+ },
+ ];
+
+ const handleDemoClick = (path: string) => {
+ navigate(path);
};
return (
-
-
-
-
Progress Agentic RAG + Telerik DevTools
-
Supercharging AI-Powered Applications
-
-
-
Create AI-driven applications that are not only visually compelling and easy to use but also grounded in the most precise, reliable data context - delivering value where it matters most.
-
- - Seamlessly embed AI-powered search and generative answers directly into your applications.
- - Enhance user workflows with intuitive UI components paired with context-rich, reliable insights.
- - Accelerate development cycles by combining enterprise-grade UI with enterprise-ready AI retrieval.
- - Unlock business outcomes by transforming unstructured data into meaningful knowledge, surfaced through beautiful and functional interfaces.
-
-
+
+ {/* Gradient background - positioned fixed to viewport */}
+
+
+
+
+
+
+ {/* Main heading + Subtitle */}
+
+
+
+
+ Supercharging
+
+ AI-Powered Applications
+
+
+ Build AI-powered apps that look great, are easy to use, and rely
+ on accurate, trustworthy data—
+ so they deliver real value where it counts.
+
+
+
+
+
+ {/* Search bar */}
+
+
+
+ {/* Explore Demos section */}
+
+
+ Explore Demos
+
+
+ {demos.map((demo) => (
+
handleDemoClick(demo.path)}
+ >
+
+

+
+
{demo.name}
+
+ ))}
+
+
+
+
+
+ {/* Footer */}
+
);
}
diff --git a/client/src/pages/KnowledgeAssistant.tsx b/client/src/pages/KnowledgeAssistant.tsx
index 1661fe5..93aa0a8 100644
--- a/client/src/pages/KnowledgeAssistant.tsx
+++ b/client/src/pages/KnowledgeAssistant.tsx
@@ -1,10 +1,13 @@
import { Chat, type ChatSuggestion } from "@progress/kendo-react-conversational-ui";
import ChatMessage from "../components/ChatMessage";
import { useChatBot } from '../hooks/useChatBot';
-import { SvgIcon } from "@progress/kendo-react-common";
-import { searchIcon } from "@progress/kendo-svg-icons";
+import DrawerComponent from "../components/DrawerComponent";
+import { VectorsBackground } from "../components/VectorsBackground";
+import ChatMessageBox from '../components/ChatMessageBox';
+import ChatHeaderTemplate from '../components/ChatHeader';
const KnowledgeAssistant = () => {
+
// Predefined suggestions related to Kendo React
const kendoSuggestions: ChatSuggestion[] = [
{
@@ -26,37 +29,74 @@ const KnowledgeAssistant = () => {
const chatBot = useChatBot({
botName: 'Progress Agentic RAG Assistant',
- initialMessage: 'Hello! I\'m your Progress Agentic RAG AI assistant. I can help you with KendoReact questions and documentation. Try one of the suggestions below, or ask me anything about KendoReact components, theming, data visualization, and more!',
+ initialMessage: '👋 Hello! I\'m your Progress Agentic RAG AI assistant. I can help you with KendoReact questions and documentation. Try one of the suggestions below, or ask me anything about KendoReact like:\n- components\n- theming\n- data visualization\n- and more!',
apiEndpoint: '/api/ask',
placeholder: 'Try a suggestion or ask about KendoReact...',
suggestions: kendoSuggestions
});
+ // Check if conversation has started (more than initial message)
+ const hasConversationStarted = chatBot.messages.length > 1;
+
return (
-
-
-
-
-
-
Progress Agentic RAG Knowledge Assistant
+
+
+
+ {/* Background Illustration with Vectors - Only show in idle state */}
+
+
+ {/* Hero Section - Only visible in idle state */}
+ {!hasConversationStarted && (
+
+
+
+ Progress Agentic RAG Knowledge Assistant
+
+
+ Use AI search to quickly find accurate, relevant information about Progress Agentic RAG—its features, capabilities, and best practices.
+
+
+
+ )}
+ {hasConversationStarted &&
}
+ {/* Chat Component */}
+
+
+ null }
+ showUsername={false}
+ messageWidthMode="full"
+ messageBox={(props) => (
+ {
+ chatBot.addNewMessage({
+ message: {
+ id: Date.now(),
+ author: chatBot.user,
+ timestamp: new Date(),
+ text
+ }
+ });
+ }}
+ />
+ )}
+ />
-
Explore the comprehensive Progress Agentic RAG knowledge base with AI-powered intelligent search for precise, contextual results about Progress Agentic RAG features, capabilities, and best practices
-
-
-
-
+
);
}
diff --git a/client/src/pages/ValueProposition.tsx b/client/src/pages/ValueProposition.tsx
new file mode 100644
index 0000000..b46edf0
--- /dev/null
+++ b/client/src/pages/ValueProposition.tsx
@@ -0,0 +1,290 @@
+import React from "react";
+import { Button } from "@progress/kendo-react-buttons";
+import { TextArea } from "@progress/kendo-react-inputs";
+import { buildApiUrl } from '../config/api';
+import { renderMarkdown } from '../utils/markdownRenderer';
+import { sparklesIcon } from "@progress/kendo-svg-icons";
+import { VectorsBackground } from '../components/VectorsBackground';
+import { GradientLoader } from '../components/GradientLoader';
+
+export default function ValueProposition() {
+ const [industry, setIndustry] = React.useState
('');
+ const [companySize, setCompanySize] = React.useState('');
+ const [dataTypes, setDataTypes] = React.useState([]);
+ const [useCase, setUseCase] = React.useState('');
+ const [additionalDetails, setAdditionalDetails] = React.useState('');
+ const [isLoading, setIsLoading] = React.useState(false);
+ const [valueProposition, setValueProposition] = React.useState('');
+
+ const toggleDataType = React.useCallback((dataType: string) => {
+ setDataTypes(prev =>
+ prev.includes(dataType)
+ ? prev.filter(dt => dt !== dataType)
+ : [...prev, dataType]
+ );
+ }, []);
+
+ const generateQuestion = React.useCallback(() => {
+ const parts: string[] = [];
+
+ parts.push("Generate a compelling value proposition for Progress Agentic RAG and Telerik DevTools");
+
+ if (industry) {
+ parts.push(`for a company in the ${industry} industry`);
+ }
+
+ if (companySize) {
+ parts.push(`with ${companySize} employees`);
+ }
+
+ if (dataTypes.length > 0) {
+ parts.push(`that works with ${dataTypes.join(', ')}`);
+ }
+
+ if (useCase) {
+ parts.push(`focusing on ${useCase}`);
+ }
+
+ if (additionalDetails) {
+ parts.push(`Additional context: ${additionalDetails}`);
+ }
+
+ parts.push("Highlight the benefits of combining AI-powered search with enterprise-grade UI components.");
+
+ return parts.join(' ');
+ }, [industry, companySize, dataTypes, useCase, additionalDetails]);
+
+ const handleGenerate = React.useCallback(async () => {
+ const question = generateQuestion();
+
+ setIsLoading(true);
+ setValueProposition('');
+
+ try {
+ const res = await fetch(buildApiUrl('/api/ask-nuclia'), {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ question })
+ });
+
+ if (!res.ok || !res.body) {
+ throw new Error('Request failed');
+ }
+
+ const reader = res.body.getReader();
+ const decoder = new TextDecoder('utf-8');
+ let buffer = '';
+ let currentAnswer = '';
+
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+
+ buffer += decoder.decode(value, { stream: true });
+ const parts = buffer.split('\n\n');
+ buffer = parts.pop() || '';
+
+ for (const part of parts) {
+ const lines = part.split('\n').filter(Boolean);
+ const dataLine = lines.find(l => l.startsWith('data: '));
+ const isError = lines.some(l => l.startsWith('event: error'));
+
+ if (isError) {
+ if (dataLine) {
+ try {
+ const payload = JSON.parse(dataLine.replace(/^data: /, ''));
+ throw new Error(payload.error || 'Error');
+ } catch {
+ throw new Error('Error processing request');
+ }
+ } else {
+ throw new Error('Error processing request');
+ }
+ }
+
+ if (dataLine) {
+ try {
+ const payload = JSON.parse(dataLine.replace(/^data: /, ''));
+ if (payload.answer) {
+ currentAnswer = payload.answer;
+ setValueProposition(currentAnswer);
+ }
+ } catch (e) {
+ console.warn('Failed to parse SSE chunk', e, part);
+ }
+ }
+ }
+ }
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'Network error';
+ setValueProposition(`Sorry, I encountered an error: ${errorMessage}`);
+ } finally {
+ setIsLoading(false);
+ }
+ }, [generateQuestion]);
+
+ const hasResults = (valueProposition || isLoading);
+
+ return (
+
+ {!hasResults ? (
+ <>
+
+ {/* Hero Section */}
+
+
+
+ Progress Agentic RAG Value
+
+
+ Generate a customized value proposition showing exactly how Nuclia delivers ROI for your specific industry, company size, and use case.
+
+
+
+
+
+
Configure Your Scenario
+
Tell us about your organization and we'll create a tailored value proposition
+
+
+
+
+
Industry *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Company Size *
+
+
+
+
+
+
+
+
+
Data Types * (Select all that apply)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Primary Use Case *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Additional Details (Optional)
+
+
+
+
+
+ >
+ ) : (
+
+ {/* Hero section with title and summary */}
+
+ {isLoading && (
+
+ )}
+
+ {!isLoading && (
+
+ Custom Value Proposition
+
+ )}
+
+
+
+
+
Industry & Size:
+
{industry}
+
{companySize}
+
+
+
Primary Use Case:
+
{useCase}
+
+
+
+
+
Data Types:
+
{dataTypes.join(', ')}
+
+ {additionalDetails && (
+
+
Additional Details:
+
{additionalDetails}
+
+ )}
+
+
+
+
+ {/* Results content */}
+ {!isLoading && valueProposition && (
+
+
+ {renderMarkdown(valueProposition)}
+
+
+ )}
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/styles/styles.css b/client/src/styles/styles.css
new file mode 100644
index 0000000..867ba28
--- /dev/null
+++ b/client/src/styles/styles.css
@@ -0,0 +1,1313 @@
+@import 'https://fonts.googleapis.com/css2?family=Nunito';
+
+/* Prevent body scroll - only page content should scroll */
+html, body, #root {
+ height: 100%;
+ overflow: hidden;
+}
+
+/* Metric Font Family */
+@font-face {
+ font-family: 'Metric';
+ src: url('/fonts/Metric-Light.woff2') format('woff2');
+ font-weight: 300;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Metric';
+ src: url('/fonts/Metric-Regular.woff2') format('woff2');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Metric';
+ src: url('/fonts/Metric-Medium.woff2') format('woff2');
+ font-weight: 500;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Metric';
+ src: url('/fonts/Metric-Semibold.woff2') format('woff2');
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+}
+
+:root {
+ /* Primary Colors */
+ --kendo-color-primary: #225eff;
+ --kendo-color-subtle: #515C7C;
+
+ /* Border Colors */
+ --kendo-color-border: #dee2e6;
+ --kendo-color-border-alt: #ced4da;
+
+
+ /* Custom colors for gradient and accents (from Home.css) */
+ --overlay-30: rgba(255, 255, 255, 0.3);
+ --overlay-95: rgba(255, 255, 255, 0.95);
+
+ /* Typography */
+ --kendo-font-family: "Metric", "Segoe UI", Roboto,
+ "Helvetica Neue", Arial, sans-serif;
+ --kendo-font-size: 16px;
+ --kendo-line-height: 1;
+
+ --kendo-h1-font-size: 56px;
+ --kendo-h1-line-height: 1;
+ --kendo-h2-font-size: 36px;
+ --kendo-h2-line-height: 1;
+ --kendo-h3-font-size: 24px;
+ --kendo-h3-line-height: 1.2;
+}
+
+.hero {
+ background: linear-gradient(180deg, rgba(250, 250, 250, 0.80) 85%, rgba(236, 236, 236, 0.80) 100%);
+}
+
+.financial-analysis .hero,
+.value-proposition .hero,
+.knowledge-assistant-hero-wrapper {
+ background: transparent;
+}
+
+/* Search Input */
+.search-input {
+ border-width: 2px;
+ border-color: #A1B0C7;
+ color: #A1B0C7;
+ box-shadow: var(--kendo-elevation-2);
+ border-radius: 24px;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.search-input .k-input-inner {
+ overflow: hidden;
+}
+
+/* Target the input inside the TextBox wrapper */
+.search-input input::placeholder,
+.search-input .k-input-inner::placeholder,
+.search-input.k-textbox input::placeholder {
+ color: #A1B0C7;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: normal;
+ vertical-align: middle;
+}
+
+.search-input.k-focus,
+.search-input:focus,
+.search-input:focus-within {
+ position: relative;
+ border: 2px solid transparent !important;
+ background: linear-gradient(white, white) padding-box,
+ linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%) border-box !important;
+ box-shadow: var(--kendo-elevation-4);
+}
+
+.search-input .send-button {
+ border: none;
+
+}
+
+
+.search-input.k-focus .send-button {
+ background: linear-gradient(143deg, #C158E4 19.85%, #001DFF 83.02%);
+ color: #fff;
+ border: none;
+
+}
+
+/* Search Pills */
+.search-pill {
+ background-color: rgba(255, 255, 255, 0.7);
+ border: 1px solid white;
+ border-radius: 16px;
+ padding: var(--kendo-spacing-2\.5);
+ font-size: 14px;
+ font-weight: 500;
+ color: #000000;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+ line-height: 1;
+ box-shadow: var(--kendo-elevation-2);
+ font-weight: var(--kendo-font-weight-medium);
+ text-align: center;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ hyphens: auto;
+}
+
+.search-pill:hover {
+ color: var(--kendo-color-primary);
+ border-color: var(--kendo-color-primary);
+ box-shadow: var(--kendo-elevation-4);
+}
+
+.search-pill:hover:not(:disabled) {
+ background-color: rgba(255, 255, 255, 0.9);
+}
+
+.search-pill:disabled {
+ cursor: not-allowed;
+ opacity: 0.6;
+}
+
+.gradient-heading {
+ background: linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ font-size: 96px;
+ font-weight: 500;
+ line-height: 0.9;
+ letter-spacing: -3px;
+ margin: 0;
+}
+
+
+
+.drawer-navigation .k-drawer-wrapper {
+ height: 100%;
+ border-right: 1px solid var(--kendo-color-border, rgba(0, 0, 0, 0.08));
+ background: linear-gradient(
+ 90deg,
+ rgba(250, 250, 250, 0.8) 85%,
+ rgba(236, 236, 236, 0.8) 100%
+ );
+ padding: 8px;
+ position: relative;
+ overflow: hidden;
+}
+
+.k-drawer-wrapper::after,
+.preview .charts-preview:before {
+ content: "";
+ display: block;
+ height: 100%;
+ width: 63.344px;
+ position: absolute;
+ background: conic-gradient(from 180deg at 49.93% 47.29%, rgba(0, 119, 255, 0.6) 0deg, rgba(0, 200, 255, 0.3) 180deg, rgba(255, 0, 251, 0.6) 360deg);
+ filter: blur(60px);
+ opacity: 0.6;
+ bottom: 0;
+ transform-origin: center;
+ border-radius: 815px;
+ pointer-events: none;
+}
+.k-drawer-wrapper::after {
+ right: 0;
+}
+.preview .charts-preview:before {
+ left: -60px;
+}
+
+.user-selection-wrapper:after,
+.gradient-wrapper:after {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 63.344px;
+ position: absolute;
+ filter: blur(60px);
+ opacity: 0.6;
+ bottom: 0;
+ left: 0;
+ bottom: -40px;
+ transform-origin: center;
+ border-radius: 815px;
+ pointer-events: none;
+
+}
+
+.user-selection-wrapper:after {
+ background: conic-gradient(from 180deg at 49.93% 47.29%, rgba(0, 119, 255, 0.6) 0deg, rgba(0, 200, 255, 0.3) 180deg, rgba(255, 0, 251, 0.6) 360deg);
+}
+.gradient-wrapper:after {
+ background: conic-gradient(from 180deg at 49.93% 47.29%, rgba(255, 0, 251, 0.6) 0deg, rgba(0, 200, 255, 0.3) 180deg, rgba(0, 119, 255, 0.6) 360deg);
+}
+
+.user-selection-wrapper-loading:after {
+ display: none;
+}
+
+.user-selection-wrapper-loading:before {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 63.344px;
+ position: absolute;
+ filter: blur(60px);
+ opacity: 0.6;
+ bottom: 0;
+ left: 0;
+ top: -40px;
+ transform-origin: center;
+ border-radius: 815px;
+ pointer-events: none;
+ background: conic-gradient(from 180deg at 49.93% 47.29%, rgba(0, 119, 255, 0.6) 0deg, rgba(0, 200, 255, 0.3) 180deg, rgba(255, 0, 251, 0.6) 360deg);
+}
+
+/* Financial Analysis gradient - positioned higher in the content, only shown before conversation starts */
+.financial-analysis .chat-wrapper.show-gradient::after {
+ content: "";
+ display: block;
+ width: 815px;
+ height: 63.344px;
+ position: absolute;
+ background: conic-gradient(
+ from 270deg at 47.29% 49.93%,
+ rgba(255, 0, 251, 0.6) 0deg,
+ rgba(0, 200, 255, 0.3) 180deg,
+ rgba(0, 119, 255, 0.6) 360deg
+ );
+ filter: blur(75px);
+ opacity: 0.6;
+ left: 50%;
+ transform: translateX(-50%);
+ bottom: 115px;
+ border-radius: 815px;
+ pointer-events: none;
+ z-index: 0;
+}
+
+/* Knowledge Assistant gradient - positioned lower in the content, only shown before conversation starts */
+.chat-content-wrapper.show-gradient::after {
+ content: "";
+ display: block;
+ width: 815px;
+ height: 63.344px;
+ position: absolute;
+ background: conic-gradient(
+ from 270deg at 47.29% 49.93%,
+ rgba(255, 0, 251, 0.6) 0deg,
+ rgba(0, 200, 255, 0.3) 180deg,
+ rgba(0, 119, 255, 0.6) 360deg
+ );
+ filter: blur(75px);
+ opacity: 0.6;
+ left: 50%;
+ bottom: 115px;
+ transform: translateX(-50%);
+ border-radius: 815px;
+ pointer-events: none;
+ z-index: 0;
+}
+
+.k-drawer-item {
+ border-radius: 12px;
+ border: 1px solid transparent;
+}
+
+.k-drawer-item.k-hover,
+.k-drawer-item:hover {
+ border-radius: 8px;
+ border-color: #bacae3;
+ background-color: transparent;
+}
+
+.k-drawer-item.k-selected {
+ border-color: #ffffff;
+ background-color: rgba(255, 255, 255, 0.8);
+ box-shadow: var(--kendo-elevation-2);
+ color: currentColor;
+}
+
+.k-drawer-item.k-selected.k-hover,
+.k-drawer-item.k-selected:hover {
+ border-color: #bacae3;
+ background-color: transparent;
+ color: currentColor;
+}
+
+.k-chat {
+ background: transparent;
+ max-width: 770px;
+ min-height: auto;
+}
+
+.k-chat .k-message-list-content .k-timestamp:empty {
+ display: none;
+}
+
+.k-chat .k-message-list {
+ justify-content: flex-end;
+
+}
+
+
+.finance-analysis-chat-default .k-message-list {
+ padding-block: 64px;
+}
+
+.finance-analysis-chat-conversation .k-message-list {
+ padding-block: 32px;
+}
+
+/* Agentic RAG Value */
+.value-proposition .k-button {
+ background-color: #fff;
+ color: rgba(0, 0, 0, 0.6);
+ border-width: 2px;
+ border-color: #bacae3;
+ border-radius: 12px;
+}
+
+.value-proposition .k-button.k-selected {
+ color: var(--kendo-color-primary);
+ border-color: var(--kendo-color-primary);
+ box-shadow: var(--kendo-elevation-3);
+}
+
+.value-proposition .k-button.k-selected .k-button-text {
+ transform: scale(1.05);
+}
+
+/* Loader gradient styling */
+.gradient-loader {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ position: relative;
+ background: conic-gradient(from 0deg, #96E5FF 0deg, #82DCFF 30deg, #70C8FF 60deg, #5DBDFF 90deg, #4AABFF 120deg, #4798FF 150deg, #4580FF 180deg, #4570FF 210deg, #4860FF 240deg, #4C50FF 270deg, #5845FF 300deg, #6840FF 330deg, #96E5FF 360deg);
+ -webkit-mask: radial-gradient(circle, transparent 0, transparent 34px, black 34px, black 40px);
+ mask: radial-gradient(circle, transparent 0, transparent 34px, black 34px, black 40px);
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.value-proposition .generate-button {
+ border-radius: 9999px;
+ padding: 16px 24px;
+ box-shadow: 0px 6px 13px 0px rgba(0, 0, 0, 0.13);
+ background: linear-gradient(142deg, #c158e4 0.08%, #001dff 110.93%);
+ color: #fff;
+ border-width: 0;
+ font-weight: var(--kendo-font-weight-semibold);
+ font-size: var(--kendo-font-size-lg);
+}
+
+.value-proposition .generate-button:hover {
+ background: linear-gradient(0deg, #000, #000);
+}
+
+.nav-link {
+ font-size: 16px;
+ line-height: 24px;
+ color: #a1b0c7;
+ font-weight: 400;
+ cursor: pointer;
+ text-decoration: none;
+ white-space: nowrap;
+ position: relative;
+}
+
+.nav-link::after {
+ content: "";
+ display: block;
+ position: absolute;
+ bottom: -15px;
+ left: 0;
+ width: 100%;
+ height: 3px;
+ background-color: var(--kendo-color-primary, #225eff);
+ transform: scaleX(0);
+ transform-origin: left;
+ transition: transform 0.3s ease-in-out;
+}
+
+.nav-link:hover {
+ color: #000000;
+}
+
+.nav-link.k-active {
+ color: #000000;
+ font-weight: 600;
+}
+
+.nav-link.k-active::after {
+ transform: scaleX(1);
+}
+
+.k-chat .k-message-list {
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+}
+
+.k-chat .k-message-list::-webkit-scrollbar {
+ display: none;
+}
+
+/* Demo cards (from Home.css) */
+.demo-card {
+ background-color: rgba(255, 255, 255, 0.7);
+ border: 2px solid white;
+ border-radius: 16px;
+ padding: 12px;
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ box-sizing: border-box;
+ min-width: 316px;
+}
+
+.demo-card:hover {
+ position: relative;
+ border: 2px solid transparent;
+ background: linear-gradient(white, white) padding-box,
+ linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%) border-box;
+ box-shadow: var(--kendo-elevation-4);
+}
+
+.demo-card-icon {
+ width: 48px;
+ height: 48px;
+ border-radius: 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* Background layers (from Home.css) */
+.home-background {
+ position: absolute;
+ inset: 0;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.bg-layer-base {
+ position: absolute;
+ inset: 0;
+ background-color: #ffffff;
+}
+
+.bg-layer-image {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ opacity: 0.3;
+ object-fit: cover;
+ object-position: center;
+}
+
+.bg-layer-overlay {
+ position: absolute;
+ inset: 0;
+ background-color: rgba(255, 255, 255, 0.95);
+}
+
+/* Decorative circle (from Home.css) */
+.decorative-circle {
+ position: absolute;
+ left: 50%;
+ top: calc(50% + 139.5px);
+ transform: translate(-50%, -50%);
+ width: 1266px;
+ height: 331px;
+ opacity: 0.6;
+ pointer-events: none;
+}
+
+/* Base styles - mobile first */
+.hero-wrapper {
+ margin-block-start: 48px;
+}
+
+.rag-hero-wrapper {
+ padding: 48px;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.search-input-wrapper {
+ padding: 0 32px;
+ margin: 0 auto;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.knowledge-assistant-hero-wrapper {
+ padding: 48px;
+}
+
+.results-section {
+ padding: 64px 24px;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.results-content {
+ padding: 0 8px;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.results-content * {
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.conversation-container {
+ padding: 24px;
+}
+
+.footer {
+ margin-block-end: var(--kendo-spacing-20);
+}
+
+.heading-wrapper > .k-flex-column {
+ max-width: 100%;
+}
+
+/* Mobile: Pills stack vertically */
+@media (max-width: 767px) {
+ .suggestions-container {
+ flex-direction: column;
+ }
+
+ .search-pill {
+ width: 100%;
+ white-space: normal;
+ }
+
+ .demo-card {
+ flex: 1;
+ }
+}
+
+/* Medium (md) - 768px and up */
+@media (min-width: 768px) {
+ .search-input {
+ border-radius: var(--kendo-border-radius-full);
+ }
+
+ .heading-wrapper {
+ padding-block: 128px;
+ padding-left: 128px;
+ padding-right: 512px;
+ }
+
+ .heading-wrapper > .k-flex-column {
+ max-width: 800px;
+ }
+
+ .value-proposition > .k-d-flex > section {
+ padding-block: 64px;
+ padding-inline: 32px;
+ }
+
+ .hero-wrapper {
+ margin-block-start: 62px;
+ }
+
+ .rag-hero-wrapper {
+ padding: 64px 32px;
+ }
+
+ .search-input-wrapper {
+ padding: 0 32px;
+ }
+
+ .knowledge-assistant-hero-wrapper {
+ padding: 64px;
+ }
+
+ .results-section {
+ padding: 64px 24px;
+ }
+
+ .results-content {
+ padding: 0 8px;
+ }
+
+ .conversation-container {
+ padding: 64px;
+ }
+
+ /* Desktop: Pills wrap horizontally, don't override flex-direction */
+ .search-pill {
+ white-space: nowrap;
+ flex-shrink: 0;
+ width: auto;
+ }
+
+ .financial-analysis .hero,
+ .financial-analysis .chat-wrapper {
+ padding: 64px;
+ }
+}
+
+/* Large (lg) - 992px and up */
+@media (min-width: 992px) {
+ /* Add lg breakpoint styles here */
+
+
+ .financial-analysis .hero {
+ max-width: 540px;
+ }
+}
+
+/* Extra Large (xl) - 1200px and up */
+@media (min-width: 1200px) {
+ .hero-wrapper {
+ margin-block-start: 120px;
+ }
+
+ .rag-hero-wrapper {
+ padding: 128px 512px 64px 128px;
+ }
+
+ .search-input-wrapper {
+ padding: 0;
+ }
+
+ .knowledge-assistant-hero-wrapper {
+ padding: 96px 512px 96px 128px;
+ }
+
+ .results-section {
+ padding: 64px 24px;
+ }
+
+ .results-content {
+ padding: 0;
+ }
+
+ .conversation-container {
+ padding: 0px;
+ }
+
+ .footer {
+ margin-block-end: var(--kendo-spacing-8);
+ }
+
+ .gradient-heading {
+ font-size: 72px;
+ }
+
+ .preview-wrapper > .preview {
+ grid-template-columns: 393px 1fr;
+ padding: 32px;
+ gap: 36px;
+ }
+
+ .charts-preview {
+ border-radius: 20px;
+ }
+}
+
+
+/* Code Block Styles */
+.code-block-wrapper {
+ margin-bottom: 12px;
+}
+
+.code-block-container {
+ background-color: rgba(250, 250, 250, 0.8);
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+}
+
+.code-block-pre {
+ margin: 0;
+ font-family: Menlo, Monaco, "Courier New", monospace;
+ font-size: 14px;
+ line-height: 20px;
+ color: #495057;
+ white-space: pre;
+}
+
+.code-copy-button {
+ min-height: 24px;
+ min-width: 24px;
+ width: 24px;
+ height: 24px;
+ padding: 4px;
+ flex-shrink: 0;
+ margin-left: 8px;
+}
+
+.code-copy-button.copied {
+ min-width: auto;
+ width: auto;
+ padding: 4px 8px;
+}
+
+.code-copy-text {
+ margin-left: 4px;
+ font-size: 12px;
+}
+
+.gradient-loader-heading {
+ font-size: 36px;
+ line-height: 1;
+ font-weight: 500;
+ letter-spacing: normal;
+}
+
+.gradient-loader-subtitle {
+ font-size: 16px;
+ line-height: 1.5;
+ color: #323130;
+}
+
+/* Search Input Buttons */
+.k-button-lg.search-input-button,
+.k-button-lg.send-button {
+ padding: 11px;
+}
+
+.search-input .send-button {
+ color: #fff;
+ background-color: #A1B0C7;
+}
+
+/* Vectors Background */
+.vectors-background {
+ right: 0;
+ top: 0;
+ width: 472px;
+ height: 523px;
+ pointer-events: none;
+ z-index: 1;
+}
+
+.vectors-background-image {
+ right: 0;
+ top: 0;
+}
+
+/* Chat Header */
+.chat-header {
+ font-size: 16px;
+ line-height: 1.5;
+ background-color: rgba(255, 255, 255, 0.8);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+}
+
+/* Chat Message */
+.chat-message-bot-content {
+ font-size: 20px;
+ line-height: 1.2;
+}
+
+.chat-message-user-bubble {
+ background-color: #A1B0C7;
+ color: #ffffff;
+ border-radius: 12px 12px 2px 12px;
+ max-width: 284px;
+ font-size: 16px;
+ line-height: 1.5;
+}
+
+.chat-message-user-text {
+ margin: 0;
+ font-size: 16px;
+ line-height: 1.5;
+ font-family: var(--kendo-font-family, 'Metric', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif);
+ color: #ffffff;
+}
+
+/* AppBar */
+.app-bar {
+ background: #ffffff;
+ border-bottom: none;
+ box-shadow: 0px 2px 7px 0px rgba(0, 0, 0, 0.08);
+ padding: 15px 32px;
+}
+
+.app-bar-mobile {
+ padding: 15px 12px;
+}
+
+.app-bar-logo-container {
+ height: 24px;
+ width: 102px;
+ cursor: pointer;
+}
+
+.app-bar-logo-image {
+ max-width: none;
+}
+
+.app-bar-title {
+ font-size: 20px;
+ line-height: 1;
+ color: #000000;
+ letter-spacing: var(--kendo-letter-spacing, 0px);
+}
+
+.app-bar-logo-wrapper {
+ cursor: pointer;
+}
+
+.app-bar-logo-compact-container {
+ height: 24px;
+ width: 24px;
+}
+
+.app-bar-logo-full-container {
+ height: 24px;
+ width: 102px;
+}
+
+.app-bar-title-mobile {
+ font-size: 14px;
+ line-height: 1;
+ color: #000000;
+ letter-spacing: var(--kendo-letter-spacing, 0px);
+}
+
+.app-bar-title-desktop {
+ font-size: 20px;
+ line-height: 24px;
+ color: #000000;
+ letter-spacing: var(--kendo-letter-spacing, 0px);
+}
+
+.app-bar-menu-button {
+ cursor: pointer;
+ font-size: 16px;
+ font-weight: 400;
+ color: #000000;
+}
+
+.mobile-menu-popup-content {
+ background-color: #ffffff;
+ box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ padding: 8px;
+ min-width: 200px;
+ margin-top: 8px;
+}
+
+.mobile-menu-item {
+ padding: 12px 16px;
+ cursor: pointer;
+ border-radius: 4px;
+ font-size: 16px;
+ font-weight: 400;
+ color: #A1B0C7;
+ transition: all 0.2s ease;
+}
+
+.mobile-menu-item.active {
+ font-weight: 600;
+ color: #000000;
+}
+
+.mobile-menu-item:hover {
+ background-color: #f5f5f5;
+ color: #000000;
+}
+
+/* AI Search Page */
+.ai-search-container {
+ height: calc(100vh - 54px);
+ background-color: rgba(255, 255, 255, 0.6);
+ max-width: 100vw;
+ box-sizing: border-box;
+}
+
+.ai-search-content {
+ z-index: 1;
+ max-width: 100%;
+}
+
+.ai-search-decorative-circle {
+ left: 50%;
+ top: 545px;
+ transform: translateX(-50%);
+ width: 930px;
+ height: 169px;
+ opacity: 0.6;
+ pointer-events: none;
+ z-index: 0;
+}
+
+.ai-search-decorative-inner {
+ inset: -177.57% -32.26%;
+}
+
+.ai-search-gradient-bg {
+ background: radial-gradient(ellipse at center, rgba(193, 88, 228, 0.15) 0%, rgba(0, 187, 255, 0.1) 50%, transparent 70%);
+}
+
+.ai-search-title {
+ background: linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.ai-search-description {
+ font-size: 24px;
+ line-height: 1.2;
+ color: #535B6A;
+}
+
+.ai-search-input-wrapper {
+ max-width: 770px;
+}
+
+.ai-search-popular-label {
+ font-size: 14px;
+ line-height: 1.5;
+ color: #212529;
+ text-align: left;
+}
+
+.ai-search-results-hero {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+ padding: 64px 32px;
+ isolation: isolate;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.ai-search-results-decorative-container {
+ inset: 0;
+ pointer-events: none;
+ z-index: 0;
+}
+
+.ai-search-results-gradient-circle {
+ left: 50%;
+ top: 162px;
+ transform: translateX(-50%) rotate(180deg);
+ width: 815px;
+ height: 63px;
+ opacity: 0.6;
+}
+
+.ai-search-results-gradient-inner {
+ inset: -236.8% -18.4%;
+}
+
+.ai-search-results-shadow {
+ left: 50%;
+ top: 207px;
+ transform: translateX(-50%) rotate(90deg) scaleY(-1);
+ width: 32px;
+ height: 1440px;
+ opacity: 0.3;
+ background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.05) 50%, rgba(0, 0, 0, 0) 100%);
+}
+
+.ai-search-results-title {
+ font-size: 36px;
+ font-weight: 500;
+ line-height: 1.3;
+ background: linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ max-width: 800px;
+ margin: 0 auto;
+ z-index: 1;
+ padding-bottom: 4px;
+}
+
+.ai-search-results-input-wrapper {
+ max-width: 770px;
+ margin: 0 auto;
+ z-index: 1;
+}
+
+.ai-search-results-section {
+ background-color: rgba(255, 255, 255, 0.7);
+}
+
+.ai-search-results-content-wrapper {
+ max-width: 770px;
+ margin: 0 auto;
+ min-width: 0;
+}
+
+.ai-search-answer-text {
+ font-size: 16px;
+ line-height: 1.5;
+ color: #000000;
+ overflow-wrap: break-word;
+ word-break: break-word;
+}
+
+/* Finance Analysis Page */
+.finance-analysis-container {
+ background-color: rgba(255, 255, 255, 0.6);
+}
+
+.finance-analysis-title {
+ background: linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.finance-analysis-description {
+ color: #535B6A;
+ font-size: 24px;
+ line-height: 1.2;
+ font-family: var(--kendo-font-family, 'Metric', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif);
+}
+
+.finance-analysis-chat-wrapper {
+ position: relative;
+}
+
+.finance-analysis-chat {
+ min-height: auto;
+ width: 100%;
+}
+
+.finance-preview-wrapper {
+ height: calc(100vh - 54px);
+}
+
+.finance-charts-preview {
+ border: 1px solid #FFF;
+ background: rgba(255, 255, 255, 0.50);
+ box-shadow: 0 4px 12px 0 rgba(13, 10, 44, 0.08);
+ backdrop-filter: blur(2px);
+}
+
+.finance-charts-container {
+ max-width: 679px;
+ gap: 20px;
+}
+
+.finance-close-button {
+ top: 16px;
+ right: 16px;
+}
+
+.chart-thumbnail {
+ pointer-events: none;
+}
+
+.chart-thumbnail svg {
+ pointer-events: auto;
+ cursor: pointer;
+}
+
+/* Home Page */
+.home-container {
+ height: calc(100vh - 54px);
+}
+
+.home-background-fixed {
+ top: 54px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ pointer-events: none;
+ z-index: 0;
+}
+
+.home-background-gradient {
+ width: 1266px;
+ height: 331px;
+ left: 50%;
+ top: calc(50% + 139.5px);
+ transform: translate(-50%, -50%);
+ opacity: 0.6;
+}
+
+.home-background-gradient-inner {
+ top: -90.63%;
+ right: -23.7%;
+ bottom: -90.63%;
+ left: -23.7%;
+}
+
+.home-background-image {
+ max-width: none;
+}
+
+.home-section {
+ z-index: 1;
+}
+
+.home-heading-padding {
+ padding: 10px;
+}
+
+.home-demo-icon {
+ width: 48px;
+ height: 48px;
+}
+
+/* Knowledge Assistant Page */
+.knowledge-assistant-container {
+ background-color: rgba(255, 255, 255, 0.6);
+}
+
+.knowledge-assistant-hero {
+ z-index: 1;
+}
+
+.knowledge-assistant-hero-content {
+ max-width: 770px;
+}
+
+.knowledge-assistant-title {
+ background: linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.knowledge-assistant-description {
+ color: #535B6A;
+ font-size: 24px;
+ line-height: 1.2;
+ font-family: var(--kendo-font-family, 'Metric', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif);
+}
+
+.knowledge-assistant-conversation {
+ box-sizing: border-box;
+}
+
+.knowledge-assistant-conversation-started {
+ padding: 24px;
+}
+
+.knowledge-assistant-chat-wrapper {
+ max-width: 770px;
+ box-sizing: border-box;
+}
+
+.knowledge-assistant-chat-flex-full {
+ flex: 1;
+}
+
+.knowledge-assistant-chat-flex-none {
+ flex: none;
+}
+
+/* Value Proposition Page */
+.value-proposition-container {
+ height: calc(100vh - 54px);
+}
+
+.value-proposition-title {
+ background: linear-gradient(105deg, #C158E4 11.99%, #0BF 49.33%, #001DFF 88.12%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.value-proposition-description {
+ color: #535B6A;
+ font-size: 24px;
+ line-height: 1.2;
+ font-family: var(--kendo-font-family, 'Metric', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif);
+}
+
+.value-proposition-card {
+ max-width: 900px;
+ border-radius: 28px;
+ box-shadow: 0 2px 6px 0 rgba(13, 10, 44, 0.08);
+ backdrop-filter: blur(2px);
+ background-color: rgba(255, 255, 255, 0.5);
+ border: 1px solid white;
+}
+
+.value-proposition-textarea {
+ border-radius: 12px;
+ border: 2px solid #bacae3;
+ padding: 24px;
+}
+
+.value-proposition-results-container {
+ background-color: rgba(255, 255, 255, 0.6);
+}
+
+.value-proposition-results-hero {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+ background: linear-gradient(180deg, rgba(250, 250, 250, 0.80) 85%, rgba(236, 236, 236, 0.80) 100%);
+}
+
+.value-proposition-results-title {
+ font-size: 36px;
+ line-height: 1;
+ font-weight: 500;
+ letter-spacing: normal;
+ max-width: 800px;
+}
+
+.value-proposition-user-selection {
+ max-width: 770px;
+ border-radius: 20px;
+ background-color: rgba(255, 255, 255, 0.5);
+ border: 1px solid white;
+ box-shadow: 0 2px 6px 0 rgba(13, 10, 44, 0.08);
+ backdrop-filter: blur(2px);
+}
+
+.value-proposition-label {
+ font-size: 16px;
+ opacity: 0.5;
+ line-height: 1.5;
+}
+
+.value-proposition-value {
+ font-size: 20px;
+ font-weight: 600;
+ line-height: 1.5;
+}
+
+.value-proposition-value-compact {
+ font-size: 20px;
+ font-weight: 600;
+ line-height: 1.2;
+}
+
+.value-proposition-additional-label {
+ font-size: 16px;
+ opacity: 0.5;
+ margin-bottom: 4px;
+}
+
+.value-proposition-results-section {
+ background-color: rgba(255, 255, 255, 0.7);
+}
+
+.value-proposition-results-content {
+ max-width: 770px;
+ font-size: 16px;
+ line-height: 1.5;
+ color: #323130;
+}
+
+/* Mobile and small screens */
+@media (max-width: 768px) {
+ .decorative-circle {
+ width: 800px;
+ height: 220px;
+ }
+
+ .gradient-heading {
+ font-size: 48px;
+ }
+}
diff --git a/client/src/utils/markdownRenderer.tsx b/client/src/utils/markdownRenderer.tsx
new file mode 100644
index 0000000..0fd4dae
--- /dev/null
+++ b/client/src/utils/markdownRenderer.tsx
@@ -0,0 +1,201 @@
+import React from 'react';
+import CodeBlockWithCopy from '../components/CodeBlockWithCopy';
+
+/**
+ * Simple markdown renderer for basic formatting
+ * Handles: bold, italic, code blocks, inline code, headings, lists
+ */
+export const renderMarkdown = (text: string): React.ReactNode[] => {
+ if (!text) return [];
+
+ const lines = text.split('\n');
+ const elements: React.ReactNode[] = [];
+ let currentCodeBlock: string[] = [];
+ let inCodeBlock = false;
+ let currentList: { content: string; type: 'unordered' | 'ordered' }[] = [];
+
+ const flushList = () => {
+ if (currentList.length === 0) return;
+
+ const isOrdered = currentList[0].type === 'ordered';
+ const ListTag = isOrdered ? 'ol' : 'ul';
+
+ elements.push(
+ React.createElement(
+ ListTag,
+ {
+ key: `list-${elements.length}`,
+ style: {
+ paddingLeft: '24px',
+ marginBottom: '8px',
+ listStyleType: isOrdered ? 'decimal' : 'disc'
+ }
+ },
+ currentList.map((item, i) => (
+
+ {processInlineMarkdown(item.content)}
+
+ ))
+ )
+ );
+
+ currentList = [];
+ };
+
+ lines.forEach((line, lineIndex) => {
+ // Check for code block start/end
+ if (line.trim().startsWith('```')) {
+ flushList();
+ if (!inCodeBlock) {
+ // Start of code block
+ inCodeBlock = true;
+ } else {
+ // End of code block
+ inCodeBlock = false;
+ elements.push(
+
+ );
+ currentCodeBlock = [];
+ }
+ return;
+ }
+
+ // If in code block, accumulate lines
+ if (inCodeBlock) {
+ currentCodeBlock.push(line);
+ return;
+ }
+
+ // Process regular lines
+ const processedLine = processInlineMarkdown(line);
+
+ // Handle headings
+ if (line.startsWith('### ')) {
+ flushList();
+ elements.push(
+
+ {processInlineMarkdown(line.substring(4))}
+
+ );
+ } else if (line.startsWith('## ')) {
+ flushList();
+ elements.push(
+
+ {processInlineMarkdown(line.substring(3))}
+
+ );
+ } else if (line.startsWith('# ')) {
+ flushList();
+ elements.push(
+
+ {processInlineMarkdown(line.substring(2))}
+
+ );
+ }
+ // Handle unordered lists
+ else if (line.trim().match(/^[-*+]\s/)) {
+ const content = line.trim().substring(2);
+ currentList.push({ content, type: 'unordered' });
+ }
+ // Handle ordered lists
+ else if (line.trim().match(/^\d+\.\s/)) {
+ const content = line.trim().replace(/^\d+\.\s/, '');
+ currentList.push({ content, type: 'ordered' });
+ }
+ // Handle empty lines
+ else if (line.trim() === '') {
+ flushList();
+ elements.push(
);
+ }
+ // Regular paragraph
+ else {
+ flushList();
+ elements.push(
+
+ {processedLine}
+
+ );
+ }
+ });
+
+ // Flush any remaining list
+ flushList();
+
+ return elements;
+};
+
+/**
+ * Process inline markdown: bold, italic, inline code, links
+ */
+const processInlineMarkdown = (text: string): React.ReactNode[] => {
+ const parts: React.ReactNode[] = [];
+ const currentText = text;
+ let key = 0;
+
+ // Combined regex to match bold, italic, inline code, and links
+ const regex = /(\*\*([^*]+)\*\*)|(\*([^*]+)\*)|(`([^`]+)`)|\[([^\]]+)\]\(([^)]+)\)/g;
+ let lastIndex = 0;
+ let match;
+
+ while ((match = regex.exec(currentText)) !== null) {
+ // Add text before the match
+ if (match.index > lastIndex) {
+ parts.push(currentText.slice(lastIndex, match.index));
+ }
+
+ // Determine which pattern matched
+ if (match[1]) {
+ // Bold: **text**
+ parts.push(
+ {match[2]}
+ );
+ } else if (match[3]) {
+ // Italic: *text*
+ parts.push(
+ {match[4]}
+ );
+ } else if (match[5]) {
+ // Inline code: `code`
+ parts.push(
+
+ {match[6]}
+
+ );
+ } else if (match[7]) {
+ // Link: [text](url)
+ parts.push(
+
+ {match[7]}
+
+ );
+ }
+
+ lastIndex = match.index + match[0].length;
+ }
+
+ // Add remaining text
+ if (lastIndex < currentText.length) {
+ parts.push(currentText.slice(lastIndex));
+ }
+
+ return parts.length > 0 ? parts : [currentText];
+};
diff --git a/package-lock.json b/package-lock.json
index 0d7ca1b..e1f8993 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,7 +7,7 @@
"": {
"name": "nuclia-demo",
"version": "1.0.0",
- "license": "ISC",
+ "license": "Apache-2.0",
"devDependencies": {
"concurrently": "^9.2.1",
"nodemon": "^3.0.0"
diff --git a/server/package-lock.json b/server/package-lock.json
index f75b079..0ec1e58 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -7,7 +7,7 @@
"": {
"name": "server",
"version": "1.0.0",
- "license": "ISC",
+ "license": "Apache-2.0",
"dependencies": {
"@nuclia/core": "^1.27.0",
"cors": "^2.8.5",
diff --git a/server/src/index.ts b/server/src/index.ts
index 055ec9f..9824ab6 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -25,6 +25,13 @@ const nuclia_fin_charts: Nuclia = new Nuclia({
apiKey: process.env.NUCLIA_FIN_API_KEY,
});
+const nuclia_verse: Nuclia = new Nuclia({
+ backend: 'https://nuclia.cloud/api',
+ zone: 'aws-us-east-2-1',
+ knowledgeBox: process.env.NUCLIA_VERSE_KB,
+ apiKey: process.env.NUCLIA_VERSE_API_KEY,
+});
+
app.get("/api/health", (_req: express.Request, res: express.Response) => {
res.json({ status: "ok" });
});
@@ -129,5 +136,9 @@ app.post("/api/ask-charts", (req: express.Request, res: express.Response) => {
return askHandler(req, res, nuclia_fin_charts.knowledgeBox, req.body.question, chatOptions);
});
+app.post("/api/ask-nuclia", (req: express.Request, res: express.Response) => {
+ return askHandler(req, res, nuclia_verse.knowledgeBox, req.body.question);
+});
+
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`API listening on http://localhost:${PORT}`));