diff --git a/modules/home/code-line.tsx b/modules/home/code-line.tsx
index 06da7478..62b8dff7 100644
--- a/modules/home/code-line.tsx
+++ b/modules/home/code-line.tsx
@@ -1,40 +1,62 @@
-
"use client";
import React from 'react';
-export const CodeLine = ({ line }: { line: string }) => {
- // Basic replacements to simulate syntax highlighting
- // Note: This is a very simplistic implementation and should be replaced with a proper library like prismjs or shiki in production
-
- const highlight = (text: string) => {
- // We use a series of replacements. Order matters to avoid replacing inside already replaced spans.
- // A better approach for robust highlighting is tokenization.
-
- const highlighted = text;
-
- // Comments (simple // for now)
- if (highlighted.includes('//')) {
- const parts = highlighted.split('//');
- return <>{'//' + parts[1]}>;
- }
+const escapeHtml = (text: string) => {
+ return text
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+};
- return ;
+const highlightCode = (code: string) => {
+ // Use placeholders for span tags to preserve them during escaping
+ const placeholderPrefix = "__SPAN_";
+ const placeholderSuffix = "__";
+ let placeholderIndex = 0;
+ const placeholders: string[] = [];
+
+ const replaceWithPlaceholder = (match: string) => {
+ const placeholder = `${placeholderPrefix}${placeholderIndex++}${placeholderSuffix}`;
+ placeholders.push(match);
+ return placeholder;
};
- const highlightCode = (code: string) => {
- return code
- .replace(/import|from|export|default|return|const|new/g, '$&')
- .replace(/'[^']*'/g, '$&')
- .replace(/"[^"]*"/g, '$&')
- .replace(/Editron|console|editor/g, '$&');
- }
+ // Apply syntax highlighting on raw code first
+ let highlighted = code
+ .replace(/\b(?:import|from|export|default|return|const|new)\b/g, (match) => replaceWithPlaceholder(`${escapeHtml(match)}`))
+ .replace(/'[^']*'/g, (match) => replaceWithPlaceholder(`${escapeHtml(match)}`))
+ .replace(/\"[^\"]*\"/g, (match) => replaceWithPlaceholder(`${escapeHtml(match)}`))
+ .replace(/\b(?:Editron|console|editor)\b/g, (match) => replaceWithPlaceholder(`${escapeHtml(match)}`));
- const [highlighted, setHighlighted] = React.useState(line);
+ // Escape HTML entities in the non-span content
+ highlighted = escapeHtml(highlighted);
- React.useEffect(() => {
- setHighlighted(highlight(line));
-// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [line]);
+ // Restore the span tags from placeholders
+ placeholders.forEach((span, index) => {
+ highlighted = highlighted.replace(`${placeholderPrefix}${index}${placeholderSuffix}`, span);
+ });
return highlighted;
};
+
+const highlight = (text: string) => {
+ if (text.includes('//')) {
+ const commentStart = text.indexOf('//');
+ const codePart = text.slice(0, commentStart);
+ const commentPart = text.slice(commentStart);
+ return (
+ <>
+
+ {commentPart}
+ >
+ );
+ }
+ return ;
+};
+
+export const CodeLine = ({ line }: { line: string }) => {
+ const highlighted = React.useMemo(() => highlight(line), [line]);
+ return highlighted;
+};
\ No newline at end of file
diff --git a/modules/playground/components/playground-editor.tsx b/modules/playground/components/playground-editor.tsx
index 1434060a..6d4735b5 100644
--- a/modules/playground/components/playground-editor.tsx
+++ b/modules/playground/components/playground-editor.tsx
@@ -1,7 +1,7 @@
"use client";
import { EDITOR_CONFIG } from "@/lib/constants/config";
-import { useRef, useEffect, useState } from "react";
+import { useRef, useEffect, useState, useCallback } from "react";
import Editor, { type Monaco } from "@monaco-editor/react";
import type { editor as MonacoEditor } from "monaco-editor";
import {
@@ -253,7 +253,7 @@ const PlaygroundEditor = ({
);
};
- const updateEditorLanguage = () => {
+ const updateEditorLanguage = useCallback(() => {
if (!activeFile || !monacoRef.current || !editorRef.current) return;
const model = editorRef.current.getModel();
if (!model) return;
@@ -264,12 +264,11 @@ const PlaygroundEditor = ({
} catch (error) {
console.warn("Failed to set editor language:", error);
}
- };
+ }, [activeFile]);
useEffect(() => {
updateEditorLanguage();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [activeFile]);
+ }, [updateEditorLanguage]);
// Bind Yjs to Monaco
useEffect(() => {
diff --git a/modules/webcontainers/components/terminal.tsx b/modules/webcontainers/components/terminal.tsx
index 743a32fd..4154e015 100644
--- a/modules/webcontainers/components/terminal.tsx
+++ b/modules/webcontainers/components/terminal.tsx
@@ -325,7 +325,6 @@ const
writePrompt();
return terminal;
-// eslint-disable-next-line react-hooks/exhaustive-deps
}, [theme, handleTerminalInput, writePrompt]);
const connectToWebContainer = useCallback(async () => {
@@ -420,7 +419,6 @@ const
currentProcess.current.kill();
}
if (shellProcess.current) {
-// eslint-disable-next-line react-hooks/exhaustive-deps
shellProcess.current.kill();
}
if (term.current) {