diff --git a/.github/workflows/porter_app_staging_3155.yml b/.github/workflows/porter_app_staging_3155.yml index d55c99758..2e406bf44 100644 --- a/.github/workflows/porter_app_staging_3155.yml +++ b/.github/workflows/porter_app_staging_3155.yml @@ -4,7 +4,6 @@ - staging paths: - apps/api/** - - .github/workflows/porter_app_staging_3155.yml name: Deploy to staging jobs: porter-deploy: diff --git a/apps/web/src/components/ui/streaming/StreamingMessageCode/StreamingMessageCode.tsx b/apps/web/src/components/ui/streaming/StreamingMessageCode/StreamingMessageCode.tsx index 0a898a0d4..49b8b005c 100644 --- a/apps/web/src/components/ui/streaming/StreamingMessageCode/StreamingMessageCode.tsx +++ b/apps/web/src/components/ui/streaming/StreamingMessageCode/StreamingMessageCode.tsx @@ -2,16 +2,12 @@ import pluralize from 'pluralize'; import React, { useEffect, useMemo, useState } from 'react'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { SyntaxHighlighter } from '@/components/ui/typography/SyntaxHighlight'; import type { BusterChatMessageReasoning_file } from '@/api/asset_interfaces'; import { Text } from '@/components/ui/typography'; -import { SyntaxHighlighterLightTheme } from '@/components/ui/typography/AppCodeBlock'; import { cn } from '@/lib/classMerge'; import { FileCard } from '../../card/FileCard'; import { TextAndVersionPill } from '../../typography/TextAndVersionPill'; -import { useMount } from '@/hooks'; - -const style = SyntaxHighlighterLightTheme; type LineSegment = { type: 'text' | 'hidden'; @@ -147,7 +143,6 @@ const MemoizedSyntaxHighlighter = React.memo( ({ lineNumber, text }: { lineNumber: number; text: string }) => { return ( import('react-syntax-highlighter').then((mod) => mod.Prism), - { ssr: false } -); +import { SyntaxHighlighter } from '../SyntaxHighlight'; export const AppCodeBlock: React.FC<{ language?: string; @@ -31,9 +25,9 @@ export const AppCodeBlock: React.FC<{ wrapperClassName = '', language, showCopyButton = true, - ...rest + style } = props; - const style = lightTheme; + const code = String(children).replace(/\n$/, ''); const { openSuccessMessage } = useBusterNotifications(); @@ -65,19 +59,12 @@ export const AppCodeBlock: React.FC<{ headerButtons={headerButtons}>
- {language ? ( - - {code} - - ) : ( - - {children} - - )} + + {code} +
@@ -94,21 +81,3 @@ const CodeInlineWrapper: React.FC<{ ); }; - -const PulseLoader = React.memo(({ className, size = 4 }: { className?: string; size?: number }) => { - return ( - - {/* Pulse animation can be added here if needed */} - - ); -}); -PulseLoader.displayName = 'PulseLoader'; diff --git a/apps/web/src/components/ui/typography/AppCodeBlock/index.ts b/apps/web/src/components/ui/typography/AppCodeBlock/index.ts index 109da704c..4c7c97521 100644 --- a/apps/web/src/components/ui/typography/AppCodeBlock/index.ts +++ b/apps/web/src/components/ui/typography/AppCodeBlock/index.ts @@ -1,6 +1 @@ export * from './AppCodeBlock'; - -import SyntaxHighlighterDarkTheme from './dark'; -import SyntaxHighlighterLightTheme from './light'; - -export { SyntaxHighlighterLightTheme, SyntaxHighlighterDarkTheme }; diff --git a/apps/web/src/components/ui/typography/StreamableText/CodeBlockComponent.tsx b/apps/web/src/components/ui/typography/StreamableText/CodeBlockComponent.tsx index 5378f718e..6c7fbacdc 100644 --- a/apps/web/src/components/ui/typography/StreamableText/CodeBlockComponent.tsx +++ b/apps/web/src/components/ui/typography/StreamableText/CodeBlockComponent.tsx @@ -1,54 +1,15 @@ 'use client'; -import type { CodeToHtmlOptions } from '@llm-ui/code'; -import { - allLangs, - allLangsAlias, - codeBlockLookBack, - findCompleteCodeBlock, - findPartialCodeBlock, - loadHighlighter, - useCodeBlockToHtml -} from '@llm-ui/code'; -import { markdownLookBack } from '@llm-ui/markdown'; -import { useLLMOutput, useStreamExample, type LLMOutputComponent } from '@llm-ui/react'; -import parseHtml from 'html-react-parser'; -import ReactMarkdown from 'react-markdown'; -import remarkGfm from 'remark-gfm'; -import { getHighlighterCore } from 'shiki/core'; -import { bundledLanguagesInfo } from 'shiki/langs'; -// WARNING: Importing bundledThemes will increase your bundle size -// see: https://llm-ui.com/docs/blocks/code#bundle-size -import { bundledThemes } from 'shiki/themes'; -import getWasm from 'shiki/wasm'; - -const highlighter = loadHighlighter( - getHighlighterCore({ - langs: allLangs(bundledLanguagesInfo), - langAlias: allLangsAlias(bundledLanguagesInfo), - themes: Object.values(bundledThemes), - loadWasm: getWasm - }) -); - -const codeToHtmlOptions: CodeToHtmlOptions = { - theme: 'github-dark' -}; +import { type LLMOutputComponent } from '@llm-ui/react'; +import { AppCodeBlock } from '../AppCodeBlock'; +import React from 'react'; // Customize this component with your own styling -export const CodeBlock: LLMOutputComponent = ({ blockMatch }) => { - const { html, code } = useCodeBlockToHtml({ - markdownCodeBlock: blockMatch.output, - highlighter, - codeToHtmlOptions - }); - if (!html) { - // fallback to
 if Shiki is not loaded yet
-    return (
-      
-        {code}
-      
- ); - } - return <>{parseHtml(html)}; -}; +export const CodeBlock: LLMOutputComponent = React.memo(({ blockMatch }) => { + if (!blockMatch.visibleText) return null; + const language = blockMatch.output.match(/```(\w+)/)?.[1]; + + return {blockMatch.visibleText}; +}); + +CodeBlock.displayName = 'CodeBlock'; diff --git a/apps/web/src/components/ui/typography/StreamableText/MarkdownComponent.tsx b/apps/web/src/components/ui/typography/StreamableText/MarkdownComponent.tsx index 8851f1508..e5a19720f 100644 --- a/apps/web/src/components/ui/typography/StreamableText/MarkdownComponent.tsx +++ b/apps/web/src/components/ui/typography/StreamableText/MarkdownComponent.tsx @@ -6,5 +6,6 @@ import remarkGfm from 'remark-gfm'; export const MarkdownComponent: LLMOutputComponent = ({ blockMatch }) => { const markdown = blockMatch.output; - return {markdown}; + + return ; }; diff --git a/apps/web/src/components/ui/typography/StreamableText/StreamableText.stories.tsx b/apps/web/src/components/ui/typography/StreamableText/StreamableText.stories.tsx index a1689d842..402075712 100644 --- a/apps/web/src/components/ui/typography/StreamableText/StreamableText.stories.tsx +++ b/apps/web/src/components/ui/typography/StreamableText/StreamableText.stories.tsx @@ -37,6 +37,22 @@ print('Hello llm-ui!') \`\`\`typescript console.log('Hello llm-ui!'); \`\`\` + +## YAML + +\`\`\`yaml +name: Buster +version: 1.0.0 +test: + - item1 + - item2 + - item3: + - item4 + - item5 + +\`\`\` + +## JSON `; const randomMarkdownBlocks = [ @@ -209,12 +225,24 @@ export const Default: Story = { isStreamFinished: false }, render: (args) => { - const [currentMessage, setCurrentMessage] = useState(args.message); + const [currentMessage, setCurrentMessage] = useState( + args.message + randomMarkdownBlocks.join('\n\n---\n\n') + ); + + const { output, isStreamFinished, start } = useStreamExample(currentMessage, { + autoStart: true, + autoStartDelayMs: 0, + startIndex: 0, + delayMultiplier: 0.075 + }); const addRandomMarkdown = fn(() => { const randomBlock = randomMarkdownBlocks[Math.floor(Math.random() * randomMarkdownBlocks.length)]; setCurrentMessage((prev: string) => prev + '\n\n---\n\n' + randomBlock); + setTimeout(() => { + start(); + }, 1); }); return ( @@ -235,7 +263,8 @@ export const Default: Story = { Add Random Markdown Block - + + ); } diff --git a/apps/web/src/components/ui/typography/StreamableText/StreamableText.tsx b/apps/web/src/components/ui/typography/StreamableText/StreamableText.tsx index 17c2aef77..d942ed767 100644 --- a/apps/web/src/components/ui/typography/StreamableText/StreamableText.tsx +++ b/apps/web/src/components/ui/typography/StreamableText/StreamableText.tsx @@ -1,7 +1,7 @@ 'use client'; import { codeBlockLookBack, findCompleteCodeBlock, findPartialCodeBlock } from '@llm-ui/code'; import { markdownLookBack } from '@llm-ui/markdown'; -import { throttleBasic, useLLMOutput, useStreamExample } from '@llm-ui/react'; +import { useLLMOutput } from '@llm-ui/react'; import { MarkdownComponent } from './MarkdownComponent'; import { CodeBlock } from './CodeBlockComponent'; @@ -10,13 +10,11 @@ export interface StreamableTextProps { isStreamFinished?: boolean; } -const throttle = throttleBasic(); - export const StreamableText = ({ message: llmOutput, isStreamFinished = false }: StreamableTextProps) => { - const { blockMatches, visibleText, ...rest } = useLLMOutput({ + const { blockMatches, visibleText } = useLLMOutput({ llmOutput, fallbackBlock: { component: MarkdownComponent, @@ -30,15 +28,9 @@ export const StreamableText = ({ lookBack: codeBlockLookBack() } ], - throttle, - onFinish: () => { - console.log('finished'); - }, isStreamFinished }); - console.log(isStreamFinished, visibleText); - return (
{blockMatches.length ? ( diff --git a/apps/web/src/components/ui/typography/SyntaxHighlight/SyntaxHighlighter.tsx b/apps/web/src/components/ui/typography/SyntaxHighlight/SyntaxHighlighter.tsx new file mode 100644 index 000000000..4d1259073 --- /dev/null +++ b/apps/web/src/components/ui/typography/SyntaxHighlight/SyntaxHighlighter.tsx @@ -0,0 +1,15 @@ +import { PrismAsyncLight as SyntaxHighlighterBase } from 'react-syntax-highlighter'; +import yaml from 'react-syntax-highlighter/dist/esm/languages/prism/yaml'; +import sql from 'react-syntax-highlighter/dist/esm/languages/prism/sql'; +import lightTheme from './light'; + +SyntaxHighlighterBase.registerLanguage('yaml', yaml); +SyntaxHighlighterBase.registerLanguage('sql', sql); + +export const SyntaxHighlighter = ( + props: React.ComponentProps & { + isDarkMode?: boolean; + } +) => { + return ; +}; diff --git a/apps/web/src/components/ui/typography/AppCodeBlock/dark.ts b/apps/web/src/components/ui/typography/SyntaxHighlight/dark.ts similarity index 100% rename from apps/web/src/components/ui/typography/AppCodeBlock/dark.ts rename to apps/web/src/components/ui/typography/SyntaxHighlight/dark.ts diff --git a/apps/web/src/components/ui/typography/SyntaxHighlight/index.ts b/apps/web/src/components/ui/typography/SyntaxHighlight/index.ts new file mode 100644 index 000000000..0b9a0b886 --- /dev/null +++ b/apps/web/src/components/ui/typography/SyntaxHighlight/index.ts @@ -0,0 +1 @@ +export * from './SyntaxHighlighter'; diff --git a/apps/web/src/components/ui/typography/AppCodeBlock/light.ts b/apps/web/src/components/ui/typography/SyntaxHighlight/light.ts similarity index 100% rename from apps/web/src/components/ui/typography/AppCodeBlock/light.ts rename to apps/web/src/components/ui/typography/SyntaxHighlight/light.ts diff --git a/apps/web/src/controllers/ReasoningController/ReasoningMessages/ReasoningMessage_Files/ReasoningMessageFile.tsx b/apps/web/src/controllers/ReasoningController/ReasoningMessages/ReasoningMessage_Files/ReasoningMessageFile.tsx index 39f94d235..e9d9ec6f8 100644 --- a/apps/web/src/controllers/ReasoningController/ReasoningMessages/ReasoningMessage_Files/ReasoningMessageFile.tsx +++ b/apps/web/src/controllers/ReasoningController/ReasoningMessages/ReasoningMessage_Files/ReasoningMessageFile.tsx @@ -5,7 +5,6 @@ import { useGetChatMessage } from '@/api/buster_rest/chats'; import { StreamingMessageCode } from '@/components/ui/streaming/StreamingMessageCode'; import { ReasoningFileButtons } from './ReasoningFileButtons'; import { StreamingMessageStatus } from './StreamingMessageStatus'; -import { useMount } from '@/hooks'; export type ReasoningMessageFileProps = { chatId: string; diff --git a/apps/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatResponseMessages/ChatResponseMessage_File/ChatResponseMessage_DashboardFile.tsx b/apps/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatResponseMessages/ChatResponseMessage_File/ChatResponseMessage_DashboardFile.tsx index 090ecbc3e..cebcf3995 100644 --- a/apps/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatResponseMessages/ChatResponseMessage_File/ChatResponseMessage_DashboardFile.tsx +++ b/apps/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatResponseMessages/ChatResponseMessage_File/ChatResponseMessage_DashboardFile.tsx @@ -2,9 +2,6 @@ import type { BusterChatResponseMessage_file } from '@/api/asset_interfaces/chat import { useGetDashboard, usePrefetchGetDashboardClient } from '@/api/buster_rest/dashboards'; import React, { useMemo } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; -import { cn } from '@/lib/classMerge'; -import { FileCard } from '@/components/ui/card/FileCard'; -import { TextAndVersionPill } from '@/components/ui/typography/TextAndVersionPill'; import { itemAnimationConfig } from '@/components/ui/streaming/animationConfig'; import { BusterDashboardResponse } from '@/api/asset_interfaces/dashboard'; import { CircleSpinnerLoader } from '@/components/ui/loaders/CircleSpinnerLoader';