mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-14 08:45:12 -05:00
Compare commits
5 Commits
ntindle/go
...
fix/copilo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
149c3b5ce4 | ||
|
|
9f51f9f034 | ||
|
|
e8c50b96d1 | ||
|
|
30e854569a | ||
|
|
301d7cbada |
@@ -22,6 +22,11 @@ Sentry.init({
|
||||
|
||||
enabled: shouldEnable,
|
||||
|
||||
// Suppress cross-origin stylesheet errors from Sentry Replay (rrweb)
|
||||
// serializing DOM snapshots with cross-origin stylesheets
|
||||
// (e.g., from browser extensions or CDN-loaded CSS)
|
||||
ignoreErrors: [/Not allowed to access cross-origin stylesheet/],
|
||||
|
||||
// Add optional integrations for additional features
|
||||
integrations: [
|
||||
Sentry.captureConsoleIntegration(),
|
||||
|
||||
@@ -29,6 +29,7 @@ export function ScheduleListItem({
|
||||
description={formatDistanceToNow(schedule.next_run_time, {
|
||||
addSuffix: true,
|
||||
})}
|
||||
descriptionTitle={new Date(schedule.next_run_time).toString()}
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
icon={
|
||||
|
||||
@@ -7,6 +7,7 @@ import React from "react";
|
||||
interface Props {
|
||||
title: string;
|
||||
description?: string;
|
||||
descriptionTitle?: string;
|
||||
icon?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
onClick?: () => void;
|
||||
@@ -16,6 +17,7 @@ interface Props {
|
||||
export function SidebarItemCard({
|
||||
title,
|
||||
description,
|
||||
descriptionTitle,
|
||||
icon,
|
||||
selected,
|
||||
onClick,
|
||||
@@ -38,7 +40,11 @@ export function SidebarItemCard({
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
<Text variant="body" className="leading-tight !text-zinc-500">
|
||||
<Text
|
||||
variant="body"
|
||||
className="leading-tight !text-zinc-500"
|
||||
title={descriptionTitle}
|
||||
>
|
||||
{description}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
@@ -81,6 +81,9 @@ export function TaskListItem({
|
||||
? formatDistanceToNow(run.started_at, { addSuffix: true })
|
||||
: "—"
|
||||
}
|
||||
descriptionTitle={
|
||||
run.started_at ? new Date(run.started_at).toString() : undefined
|
||||
}
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
actions={
|
||||
|
||||
@@ -180,3 +180,14 @@ body[data-google-picker-open="true"] [data-dialog-content] {
|
||||
z-index: 1 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/* CoPilot chat table styling — remove left/right borders, increase padding */
|
||||
[data-streamdown="table-wrapper"] table {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
[data-streamdown="table-wrapper"] th,
|
||||
[data-streamdown="table-wrapper"] td {
|
||||
padding: 0.875rem 1rem; /* py-3.5 px-4 */
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cjk } from "@streamdown/cjk";
|
||||
import { code } from "@streamdown/code";
|
||||
import { code } from "@/lib/streamdown-code-singleton";
|
||||
import { math } from "@streamdown/math";
|
||||
import { mermaid } from "@streamdown/mermaid";
|
||||
import type { UIMessage } from "ai";
|
||||
|
||||
@@ -226,7 +226,7 @@ function renderMarkdown(
|
||||
table: ({ children, ...props }) => (
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table
|
||||
className="min-w-full divide-y divide-gray-200 rounded-lg border border-gray-200 dark:divide-gray-700 dark:border-gray-700"
|
||||
className="min-w-full divide-y divide-gray-200 border-y border-gray-200 dark:divide-gray-700 dark:border-gray-700"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -235,7 +235,7 @@ function renderMarkdown(
|
||||
),
|
||||
th: ({ children, ...props }) => (
|
||||
<th
|
||||
className="bg-gray-50 px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
||||
className="bg-gray-50 px-4 py-3.5 text-left text-xs font-semibold uppercase tracking-wider text-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -243,7 +243,7 @@ function renderMarkdown(
|
||||
),
|
||||
td: ({ children, ...props }) => (
|
||||
<td
|
||||
className="border-t border-gray-200 px-4 py-3 text-sm text-gray-600 dark:border-gray-700 dark:text-gray-400"
|
||||
className="border-t border-gray-200 px-4 py-3.5 text-sm text-gray-600 dark:border-gray-700 dark:text-gray-400"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
237
autogpt_platform/frontend/src/lib/streamdown-code-singleton.ts
Normal file
237
autogpt_platform/frontend/src/lib/streamdown-code-singleton.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Custom Streamdown code plugin with proper shiki singleton.
|
||||
*
|
||||
* Fixes SENTRY-1051: "@streamdown/code creates a new shiki highlighter per language,
|
||||
* causing "10 instances created" warnings and memory bloat.
|
||||
*
|
||||
* This plugin creates ONE highlighter and loads languages dynamically.
|
||||
*/
|
||||
|
||||
import {
|
||||
createHighlighter,
|
||||
bundledLanguages,
|
||||
type Highlighter,
|
||||
type BundledLanguage,
|
||||
type BundledTheme,
|
||||
} from "shiki";
|
||||
|
||||
// Types matching streamdown's expected interface
|
||||
interface HighlightToken {
|
||||
content: string;
|
||||
color?: string;
|
||||
bgColor?: string;
|
||||
htmlStyle?: Record<string, string>;
|
||||
htmlAttrs?: Record<string, string>;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
interface HighlightResult {
|
||||
tokens: HighlightToken[][];
|
||||
fg?: string;
|
||||
bg?: string;
|
||||
}
|
||||
|
||||
interface HighlightOptions {
|
||||
code: string;
|
||||
language: BundledLanguage;
|
||||
themes: [string, string];
|
||||
}
|
||||
|
||||
interface CodeHighlighterPlugin {
|
||||
name: "shiki";
|
||||
type: "code-highlighter";
|
||||
highlight: (
|
||||
options: HighlightOptions,
|
||||
callback?: (result: HighlightResult) => void
|
||||
) => HighlightResult | null;
|
||||
supportsLanguage: (language: BundledLanguage) => boolean;
|
||||
getSupportedLanguages: () => BundledLanguage[];
|
||||
getThemes: () => [BundledTheme, BundledTheme];
|
||||
}
|
||||
|
||||
// Singleton state
|
||||
let highlighterPromise: Promise<Highlighter> | null = null;
|
||||
let highlighterInstance: Highlighter | null = null;
|
||||
const loadedLanguages = new Set<string>();
|
||||
const pendingLanguages = new Map<string, Promise<void>>();
|
||||
|
||||
// Result cache (same as @streamdown/code)
|
||||
const resultCache = new Map<string, HighlightResult>();
|
||||
const pendingCallbacks = new Map<string, Set<(result: HighlightResult) => void>>();
|
||||
|
||||
// All supported languages
|
||||
const supportedLanguages = new Set(Object.keys(bundledLanguages));
|
||||
|
||||
// Cache key for results
|
||||
function getCacheKey(code: string, language: string, themes: [string, string]): string {
|
||||
const prefix = code.slice(0, 100);
|
||||
const suffix = code.length > 100 ? code.slice(-100) : "";
|
||||
return `${language}:${themes[0]}:${themes[1]}:${code.length}:${prefix}:${suffix}`;
|
||||
}
|
||||
|
||||
// Get or create the singleton highlighter
|
||||
async function getHighlighter(themes: [string, string]): Promise<Highlighter> {
|
||||
if (highlighterInstance) {
|
||||
return highlighterInstance;
|
||||
}
|
||||
|
||||
if (!highlighterPromise) {
|
||||
highlighterPromise = createHighlighter({
|
||||
themes: themes as [BundledTheme, BundledTheme],
|
||||
// Start with common languages pre-loaded for faster first render
|
||||
langs: ["javascript", "typescript", "python", "json", "html", "css", "bash", "markdown"],
|
||||
}).then((h: Highlighter) => {
|
||||
highlighterInstance = h;
|
||||
["javascript", "typescript", "python", "json", "html", "css", "bash", "markdown"].forEach(
|
||||
(l) => loadedLanguages.add(l)
|
||||
);
|
||||
return h;
|
||||
});
|
||||
}
|
||||
|
||||
return highlighterPromise;
|
||||
}
|
||||
|
||||
// Load a language dynamically
|
||||
async function ensureLanguageLoaded(
|
||||
highlighter: Highlighter,
|
||||
language: string
|
||||
): Promise<void> {
|
||||
if (loadedLanguages.has(language)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingLanguages.has(language)) {
|
||||
return pendingLanguages.get(language);
|
||||
}
|
||||
|
||||
const loadPromise = highlighter
|
||||
.loadLanguage(language as BundledLanguage)
|
||||
.then(() => {
|
||||
loadedLanguages.add(language);
|
||||
pendingLanguages.delete(language);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
console.warn(`[streamdown-code-singleton] Failed to load language: ${language}`, err);
|
||||
pendingLanguages.delete(language);
|
||||
});
|
||||
|
||||
pendingLanguages.set(language, loadPromise);
|
||||
return loadPromise;
|
||||
}
|
||||
|
||||
// Shiki token types
|
||||
interface ShikiToken {
|
||||
content: string;
|
||||
color?: string;
|
||||
htmlStyle?: Record<string, string>;
|
||||
}
|
||||
|
||||
// Convert shiki tokens to streamdown format
|
||||
function convertTokens(
|
||||
shikiResult: ReturnType<Highlighter["codeToTokens"]>
|
||||
): HighlightResult {
|
||||
return {
|
||||
tokens: shikiResult.tokens.map((line: ShikiToken[]) =>
|
||||
line.map((token: ShikiToken) => ({
|
||||
content: token.content,
|
||||
color: token.color,
|
||||
htmlStyle: token.htmlStyle,
|
||||
}))
|
||||
),
|
||||
fg: shikiResult.fg,
|
||||
bg: shikiResult.bg,
|
||||
};
|
||||
}
|
||||
|
||||
export interface CodePluginOptions {
|
||||
themes?: [BundledTheme, BundledTheme];
|
||||
}
|
||||
|
||||
export function createCodePlugin(
|
||||
options: CodePluginOptions = {}
|
||||
): CodeHighlighterPlugin {
|
||||
const themes = options.themes ?? ["github-light", "github-dark"];
|
||||
|
||||
return {
|
||||
name: "shiki",
|
||||
type: "code-highlighter",
|
||||
|
||||
supportsLanguage(language: BundledLanguage): boolean {
|
||||
return supportedLanguages.has(language);
|
||||
},
|
||||
|
||||
getSupportedLanguages(): BundledLanguage[] {
|
||||
return Array.from(supportedLanguages) as BundledLanguage[];
|
||||
},
|
||||
|
||||
getThemes(): [BundledTheme, BundledTheme] {
|
||||
return themes as [BundledTheme, BundledTheme];
|
||||
},
|
||||
|
||||
highlight(
|
||||
{ code, language, themes: highlightThemes }: HighlightOptions,
|
||||
callback?: (result: HighlightResult) => void
|
||||
): HighlightResult | null {
|
||||
const cacheKey = getCacheKey(code, language, highlightThemes);
|
||||
|
||||
// Return cached result if available
|
||||
if (resultCache.has(cacheKey)) {
|
||||
return resultCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
// Register callback for async result
|
||||
if (callback) {
|
||||
if (!pendingCallbacks.has(cacheKey)) {
|
||||
pendingCallbacks.set(cacheKey, new Set());
|
||||
}
|
||||
pendingCallbacks.get(cacheKey)!.add(callback);
|
||||
}
|
||||
|
||||
// Start async highlighting
|
||||
getHighlighter(highlightThemes)
|
||||
.then(async (highlighter) => {
|
||||
// Ensure language is loaded
|
||||
const lang = supportedLanguages.has(language) ? language : "text";
|
||||
if (lang !== "text") {
|
||||
await ensureLanguageLoaded(highlighter, lang);
|
||||
}
|
||||
|
||||
// Highlight the code
|
||||
const effectiveLang = highlighter.getLoadedLanguages().includes(lang)
|
||||
? lang
|
||||
: "text";
|
||||
|
||||
const shikiResult = highlighter.codeToTokens(code, {
|
||||
lang: effectiveLang,
|
||||
themes: {
|
||||
light: highlightThemes[0] as BundledTheme,
|
||||
dark: highlightThemes[1] as BundledTheme,
|
||||
},
|
||||
});
|
||||
|
||||
const result = convertTokens(shikiResult);
|
||||
resultCache.set(cacheKey, result);
|
||||
|
||||
// Notify all pending callbacks
|
||||
const callbacks = pendingCallbacks.get(cacheKey);
|
||||
if (callbacks) {
|
||||
for (const cb of callbacks) {
|
||||
cb(result);
|
||||
}
|
||||
pendingCallbacks.delete(cacheKey);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("[streamdown-code-singleton] Failed to highlight code:", err);
|
||||
pendingCallbacks.delete(cacheKey);
|
||||
});
|
||||
|
||||
// Return null while async loading
|
||||
return null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Pre-configured plugin with default settings (drop-in replacement for @streamdown/code)
|
||||
export const code = createCodePlugin();
|
||||
Reference in New Issue
Block a user