LLMStream is an iOS/macOS Swift package displaying streamed responses from LLMs!
Under the hood, LLMStream uses a customizable WebView component to render rich text content. We built LLMStream while developing Onit. The full blog post is here.
- π€ Thinking State - Elegant loading state when response contains
<think>
tag

- π Markdown Support - Complete Markdown rendering with inline formatting and nested lists
- π» Code Highlighting - Syntax highlighting for multiple programming languages with convenient copy functionality

- β‘ LaTeX Integration - Seamless rendering of mathematical expressions:
- Inline expressions using
$...$
- Display math using
\[...\]
- Partial document rendering
- Inline expressions using

- π Real-time Updates - Smooth content updates without flickering
- π¨ Customizable Styling - Flexible appearance configuration
- Markdown Processing:
markdown-it
for robust Markdown parsing - Code Highlighting:
highlight.js
for syntax highlighting - Math Rendering:
Mathjax
for LaTeX processing - Platform: Optimized for macOS
- iOS 16.0 or later
- macOS 13.0 or later
- Swift 5.7+
- Xcode 14+
- Add the following dependency to your
Package.swift
:
dependencies: [
.package(url: "https://github.com/synth-inc/LLMStream", branch: "main")
]
- Add required entitlements to your project:
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.web</key>
<true/>
- Import the package in your Swift file:
import LLMStream
- Implement the view in your SwiftUI code:
struct ContentView: View {
@State var height: CGFloat = 0
var body: some View {
LLMStreamView(
text: "# Hello **Markdown** & $\\LaTeX$"
)
}
}
LLMStream offers extensive customization options through LLMStreamConfiguration
. You can customize every aspect of the rendering:
let fontConfig = FontConfiguration(
size: 14.0, // Base font size
lineHeight: 1.4, // Line height multiplier
family: "-apple-system...", // Main font family
codeFontFamily: "SF Mono...", // Code blocks font
tableFontFamily: "system-ui...",// Table font
mathFontFamily: "STIX Two Math..." // Math expressions font
)
let colorConfig = ColorConfiguration(
textColor: .white,
backgroundColor: .clear,
codeBackgroundColor: Color(red: 0.15, green: 0.15, blue: 0.15),
codeBorderColor: Color(white: 0.24),
linkColor: Color(red: 0.29, green: 0.60, blue: 1.0),
thoughtBackgroundColor: Color.gray.opacity(0.1),
tableHeaderBackgroundColor: Color(white: 0.24),
tableBorderColor: Color(white: 0.4),
tableRowEvenColor: Color(white: 0.2).opacity(0.2),
tableRowHoverColor: Color(white: 0.27).opacity(0.3),
theoremBorderColor: Color(red: 0.29, green: 0.60, blue: 1.0),
proofBorderColor: Color(white: 0.47)
)
let layoutConfig = LayoutConfiguration(
contentPadding: EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0),
codePadding: EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16),
thoughtPadding: EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8),
tablePadding: EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8),
spacing: 8,
cornerRadius: 8,
tableCornerRadius: 5,
theoremCornerRadius: 4
)
let codeConfig = CodeBlockConfiguration(
showLanguage: true, // Show language label
showCopyButton: true, // Show copy button
showActionButton: true, // Show action button
languageTextSize: 13.0, // Language label size
copyButtonSize: 16, // Copy button size
actionButtonSize: 16, // Action button size
copyButtonOpacity: 0.5, // Copy button normal opacity
copyButtonHoverOpacity: 1.0, // Copy button hover opacity
actionButtonOpacity: 0.5, // Action button normal opacity
actionButtonHoverOpacity: 1.0,// Action button hover opacity
actionButtonIcon: "play.circle", // SF Symbol name for action button
actionButtonTooltip: "Execute" // Action button tooltip
)
You can add a custom action button to code blocks that triggers a callback with the code content:
LLMStreamView(
text: "Your content here",
configuration: LLMStreamConfiguration(
codeBlock: CodeBlockConfiguration(
showActionButton: true,
actionButtonIcon: "play.circle",
actionButtonTooltip: "Run this code"
)
),
onCodeAction: { code in
// Handle the code execution here
print("Code to execute:", code)
}
)
let tableConfig = TableConfiguration(
showCaption: true,
captionStyle: TableConfiguration.CaptionStyle(
fontSize: 0.9,
textColor: Color(white: 0.8)
),
headerStyle: TableConfiguration.HeaderStyle(
fontWeight: .bold,
textAlignment: .center,
borderWidth: 2.0
),
enableHover: true,
enableZebraStripes: true
)
let thoughtConfig = ThoughtConfiguration(
icon: Image(systemName: "brain"), // Optional icon
iconSize: 16,
thinkingTitle: "Thinking...", // Loading state title
thoughtTitle: "Thought-process", // Normal state title
showExpandButton: true
)
let animationConfig = AnimationConfiguration(
thoughtExpandAnimation: .spring(response: 0.35, dampingFraction: 0.85),
shimmerAnimation: .linear(duration: 1.5).delay(0.25).repeatForever(autoreverses: false),
shimmerGradient: Gradient(colors: [
.black.opacity(0.3),
.black,
.black.opacity(0.3)
])
)
let config = LLMStreamConfiguration(
font: fontConfig,
colors: colorConfig,
layout: layoutConfig,
thought: thoughtConfig,
animation: animationConfig,
codeBlock: codeConfig,
table: tableConfig
)
LLMStreamView(
text: "Your content here",
configuration: config
)
# Integration Example
This is a **bold** text with an inline equation: $E = mc^2$
## Display Math
\[
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
\]
Contributions are welcome! Feel free to submit issues and pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.
- markdown-it for Markdown parsing
- highlight.js for code syntax highlighting
- MathJax for LaTeX rendering