mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-14 16:55:13 -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,
|
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
|
// Add optional integrations for additional features
|
||||||
integrations: [
|
integrations: [
|
||||||
Sentry.captureConsoleIntegration(),
|
Sentry.captureConsoleIntegration(),
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export function ScheduleListItem({
|
|||||||
description={formatDistanceToNow(schedule.next_run_time, {
|
description={formatDistanceToNow(schedule.next_run_time, {
|
||||||
addSuffix: true,
|
addSuffix: true,
|
||||||
})}
|
})}
|
||||||
|
descriptionTitle={new Date(schedule.next_run_time).toString()}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
icon={
|
icon={
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import React from "react";
|
|||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
descriptionTitle?: string;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
@@ -16,6 +17,7 @@ interface Props {
|
|||||||
export function SidebarItemCard({
|
export function SidebarItemCard({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
descriptionTitle,
|
||||||
icon,
|
icon,
|
||||||
selected,
|
selected,
|
||||||
onClick,
|
onClick,
|
||||||
@@ -38,7 +40,11 @@ export function SidebarItemCard({
|
|||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text variant="body" className="leading-tight !text-zinc-500">
|
<Text
|
||||||
|
variant="body"
|
||||||
|
className="leading-tight !text-zinc-500"
|
||||||
|
title={descriptionTitle}
|
||||||
|
>
|
||||||
{description}
|
{description}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ export function TaskListItem({
|
|||||||
? formatDistanceToNow(run.started_at, { addSuffix: true })
|
? formatDistanceToNow(run.started_at, { addSuffix: true })
|
||||||
: "—"
|
: "—"
|
||||||
}
|
}
|
||||||
|
descriptionTitle={
|
||||||
|
run.started_at ? new Date(run.started_at).toString() : undefined
|
||||||
|
}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
actions={
|
actions={
|
||||||
|
|||||||
@@ -180,3 +180,14 @@ body[data-google-picker-open="true"] [data-dialog-content] {
|
|||||||
z-index: 1 !important;
|
z-index: 1 !important;
|
||||||
pointer-events: none !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";
|
} from "@/components/ui/tooltip";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { cjk } from "@streamdown/cjk";
|
import { cjk } from "@streamdown/cjk";
|
||||||
import { code } from "@streamdown/code";
|
import { code } from "@/lib/streamdown-code-singleton";
|
||||||
import { math } from "@streamdown/math";
|
import { math } from "@streamdown/math";
|
||||||
import { mermaid } from "@streamdown/mermaid";
|
import { mermaid } from "@streamdown/mermaid";
|
||||||
import type { UIMessage } from "ai";
|
import type { UIMessage } from "ai";
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ function renderMarkdown(
|
|||||||
table: ({ children, ...props }) => (
|
table: ({ children, ...props }) => (
|
||||||
<div className="my-4 overflow-x-auto">
|
<div className="my-4 overflow-x-auto">
|
||||||
<table
|
<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}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -235,7 +235,7 @@ function renderMarkdown(
|
|||||||
),
|
),
|
||||||
th: ({ children, ...props }) => (
|
th: ({ children, ...props }) => (
|
||||||
<th
|
<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}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -243,7 +243,7 @@ function renderMarkdown(
|
|||||||
),
|
),
|
||||||
td: ({ children, ...props }) => (
|
td: ({ children, ...props }) => (
|
||||||
<td
|
<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}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{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