Skip to content

Commit 972751b

Browse files
authored
Merge pull request #451 from jaseci-labs/Syntax_Highlighter
Reducing latency for Syntax loading
2 parents e69df4e + 533ff21 commit 972751b

6 files changed

Lines changed: 227 additions & 171 deletions

File tree

jac-gpt-fullstack/components/ChatMessage.cl.jac

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import from "@tabler/icons-react" {
1919
# Monaco Editor - React wrapper
2020
import from "@monaco-editor/react" { Editor }
2121

22-
# Syntax highlighting utilities
23-
import from "..utils/syntax_highlighting" { loadSyntax, configureTheme }
22+
# Monaco initialization utilities
23+
import from "..utils/monaco_initializer" { setupMonacoEditor, defineJacTheme }
2424

2525

2626
"""Generic code block component with copy functionality (for non-Jac languages)."""
@@ -112,34 +112,50 @@ def:pub JacMonacoBlock(code: str) -> any {
112112
setTimeout(lambda { copied = false; }, 2000);
113113
}
114114

115-
# Handle Monaco mount - load Jac syntax and calculate height
115+
# Handle before mount - define theme before editor is created
116+
async def handleBeforeMount(monaco: any) -> None {
117+
try {
118+
# Define the centralized Jac theme before the editor mounts
119+
defineJacTheme(monaco);
120+
121+
# Register Jac language if not already registered
122+
languages = monaco.languages.getLanguages();
123+
isJacRegistered = languages.some(lambda lang: any { return lang.id == "jac"; });
124+
125+
if not isJacRegistered {
126+
monaco.languages.register({ id: "jac" });
127+
}
128+
} except Exception as error {
129+
}
130+
}
131+
132+
# Handle Monaco mount - setup editor instance with pre-loaded syntax
116133
async def handleOnMount(editor: any, monaco: any) -> None {
117134
editorRef.current = editor;
118135
monacoRef.current = monaco;
119-
136+
120137
try {
121-
# Load TextMate grammar
122-
grammarConfig = await loadSyntax(monaco, editor);
123-
138+
grammarConfig = await setupMonacoEditor(monaco, editor);
139+
124140
if not grammarConfig {
125141
return;
126142
}
127-
128-
# Apply Jaseci Orange theme
129-
await configureTheme(monaco, grammarConfig, true);
130-
131-
# Wait a bit for theme to apply
132-
await Reflect.construct(Promise, [lambda resolve: any { setTimeout(resolve, 200); }]);
133-
143+
144+
if grammarConfig {
145+
monaco.languages.setLanguageConfiguration("jac", grammarConfig);
146+
}
147+
148+
await Reflect.construct(Promise, [lambda resolve: any { setTimeout(resolve, 100); }]);
149+
134150
# Get Monaco's calculated content height
135151
if editorRef.current {
136152
height = editorRef.current.getContentHeight();
137153
contentHeight = height;
138154
editorRef.current.updateOptions({ readOnly: true });
139155
}
140-
156+
141157
themeApplied = true;
142-
158+
143159
} except Exception as error {
144160
}
145161
}
@@ -190,7 +206,8 @@ def:pub JacMonacoBlock(code: str) -> any {
190206
width="100%"
191207
language="jac"
192208
value={code}
193-
theme="vs-dark"
209+
theme="jac-theme"
210+
beforeMount={handleBeforeMount}
194211
onMount={handleOnMount}
195212
loading="Loading editor..."
196213
options={{

jac-gpt-fullstack/frontend.cl.jac

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Main entry point for Jac GPT Client application."""
2-
2+
import from react { useEffect }
33
import from "@jac/runtime" { Router, Routes, Route }
44
import from "@mantine/core" { MantineProvider, createTheme }
55

@@ -9,11 +9,23 @@ import from .pages.RegisterPage { RegisterPage }
99
import from .pages.ForgotPasswordPage { ForgotPasswordPage }
1010
import from .pages.ResetPasswordPage { ResetPasswordPage }
1111

12+
import from ".utils.monaco_initializer" { initializeMonacoGlobally }
13+
1214
import "@mantine/core/styles.css";
1315
import ".global.css";
1416

1517
"""Main app component with routing."""
1618
def:pub app() -> any {
19+
useEffect(lambda {
20+
async def initMonaco() -> None {
21+
try {
22+
await initializeMonacoGlobally();
23+
} except Exception {
24+
}
25+
}
26+
27+
initMonaco();
28+
}, []);
1729
# Custom Mantine theme for dark mode
1830
theme = createTheme({
1931
"primaryColor": "violet",

jac-gpt-fullstack/hooks/useChat.cl.jac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,4 @@ def:pub useChat() -> any {
274274
"handleDeleteSession": handleDeleteSession,
275275
"handleStopGeneration": handleStopGeneration
276276
};
277-
}
277+
}

jac-gpt-fullstack/pages/JacChatbot.cl.jac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,4 @@ def:pub JacChatbot() -> any {
373373
{docPanelElement}
374374
</div>
375375
</div>;
376-
}
376+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
"""Global Monaco initialization - ensures syntax highlighting is loaded once."""
2+
3+
import from "monaco-textmate" { Registry }
4+
import from "monaco-editor-textmate" { wireTmGrammars }
5+
import from "onigasm" { loadWASM }
6+
7+
# CORRECTED PATHS - Use absolute paths from web root
8+
glob ONIG_WASM_PATH = "/static/assets/onigasm.wasm";
9+
glob LANGUAGE_CONFIG_PATH = "/static/assets/language-configuration.json";
10+
11+
# Global initialization state
12+
glob monacoInitialized = false;
13+
glob initializationPromise = null;
14+
glob cachedGrammarConfig = null;
15+
glob cachedRegistry = null;
16+
glob cachedGrammarMap = null;
17+
18+
"""Internal function to perform the actual initialization."""
19+
async def _performInitialization() -> any {
20+
try {
21+
# Load WASM
22+
await loadWASM(ONIG_WASM_PATH);
23+
24+
# Fetch Jac grammar from GitHub
25+
response = await fetch("https://raw.githubusercontent.com/jaseci-labs/jac-vscode/main/syntaxes/jac.tmLanguage.json");
26+
27+
if not response.ok {
28+
raise Reflect.construct(Error, [f"Failed to fetch grammar: {response.status}"]);
29+
}
30+
31+
grammarConfigRes = await fetch(LANGUAGE_CONFIG_PATH);
32+
33+
if not grammarConfigRes.ok {
34+
raise Reflect.construct(Error, [f"Failed to fetch language config: {grammarConfigRes.status}"]);
35+
}
36+
37+
jacGrammar = await response.json();
38+
cachedGrammarConfig = await grammarConfigRes.json();
39+
40+
# Create TextMate registry
41+
registry = Reflect.construct(Registry, [{
42+
getGrammarDefinition: lambda {
43+
return {
44+
format: "json",
45+
content: JSON.stringify(jacGrammar)
46+
};
47+
}
48+
}]);
49+
50+
cachedRegistry = registry;
51+
52+
# Create grammar map
53+
grammar = Reflect.construct(Map, []);
54+
grammar.set("jac", "source.jac");
55+
cachedGrammarMap = grammar;
56+
57+
monacoInitialized = true;
58+
59+
return cachedGrammarConfig;
60+
61+
} except Exception as error {
62+
initializationPromise = null; # Reset on error to allow retry
63+
return null;
64+
}
65+
}
66+
67+
"""Initialize Monaco globally - call this once when the app loads."""
68+
async def:pub initializeMonacoGlobally() -> any {
69+
# If already initialized or in progress, return the existing promise
70+
if monacoInitialized {
71+
return cachedGrammarConfig;
72+
}
73+
74+
if initializationPromise {
75+
return await initializationPromise;
76+
}
77+
78+
# Create initialization promise by calling the async function
79+
initializationPromise = _performInitialization();
80+
81+
return await initializationPromise;
82+
}
83+
84+
"""Setup Monaco editor instance with Jac syntax highlighting."""
85+
async def:pub setupMonacoEditor(monaco: any, editor: any) -> any {
86+
try {
87+
# Ensure global initialization is complete
88+
if not monacoInitialized {
89+
await initializeMonacoGlobally();
90+
}
91+
92+
# Register language if not already registered
93+
languages = monaco.languages.getLanguages();
94+
isJacRegistered = languages.some(lambda lang: any { return lang.id == "jac"; });
95+
96+
if not isJacRegistered {
97+
monaco.languages.register({ id: "jac" });
98+
}
99+
100+
# Wire grammar to this specific editor instance (lightweight operation)
101+
if editor and cachedRegistry and cachedGrammarMap {
102+
await wireTmGrammars(monaco, cachedRegistry, cachedGrammarMap, editor);
103+
}
104+
105+
return cachedGrammarConfig;
106+
107+
} except Exception as error {
108+
return null;
109+
}
110+
}
111+
112+
"""Configure Monaco theme - only needs to be called once per Monaco instance."""
113+
def:pub defineJacTheme(monaco: any) -> None {
114+
try {
115+
monaco.editor.defineTheme("jac-theme", {
116+
base: "vs-dark",
117+
inherit: true,
118+
rules: [
119+
# Keywords - Orange
120+
{ token: "storage.type.class.jac", foreground: "f97316", fontStyle: "bold" },
121+
{ token: "storage.type.function.jac", foreground: "f97316", fontStyle: "bold" },
122+
{ token: "keyword.control.flow.jac", foreground: "f97316", fontStyle: "bold" },
123+
{ token: "keyword.control.import.jac", foreground: "f97316" },
124+
{ token: "storage.type.has.jac", foreground: "f97316" },
125+
{ token: "storage.modifier.declaration.jac", foreground: "f97316" },
126+
127+
# Class/Function Names - Amber
128+
{ token: "entity.name.type.class.jac", foreground: "fbbf24" },
129+
{ token: "entity.name.function.jac", foreground: "fbbf24" },
130+
131+
# Types - Light Orange
132+
{ token: "support.type.jac", foreground: "fb923c" },
133+
{ token: "constant.language.jac", foreground: "fb923c" },
134+
135+
# Strings - Green
136+
{ token: "string.quoted.single.jac", foreground: "86efac" },
137+
{ token: "string.quoted.double.jac", foreground: "86efac" },
138+
{ token: "string.interpolated.jac", foreground: "86efac" },
139+
140+
# Numbers - Purple
141+
{ token: "constant.numeric.jac", foreground: "c084fc" },
142+
143+
# Comments - Gray
144+
{ token: "comment.line.jac", foreground: "6b7280", fontStyle: "italic" },
145+
{ token: "comment.block.jac", foreground: "6b7280", fontStyle: "italic" },
146+
147+
# Variables - Light Blue
148+
{ token: "variable.parameter.jac", foreground: "93c5fd" },
149+
{ token: "variable.language.special.self.jac", foreground: "93c5fd" }
150+
],
151+
colors: {
152+
"editor.background": "#1a1b1e",
153+
"editor.foreground": "#e4e4e7",
154+
"editorLineNumber.foreground": "#6b7280",
155+
"editorLineNumber.activeForeground": "#f97316",
156+
"editorCursor.foreground": "#f97316",
157+
"editor.selectionBackground": "#f9731633"
158+
}
159+
});
160+
} except Exception as error {
161+
}
162+
}
163+
164+
"""Configure Monaco theme - only needs to be called once per Monaco instance."""
165+
def:pub configureMonacoTheme(monaco: any, grammarConfig: any) -> None {
166+
try {
167+
# Set language configuration
168+
if grammarConfig {
169+
monaco.languages.setLanguageConfiguration("jac", grammarConfig);
170+
}
171+
172+
# Define and apply the theme
173+
defineJacTheme(monaco);
174+
monaco.editor.setTheme("jac-theme");
175+
176+
} except Exception as error {
177+
}
178+
}

0 commit comments

Comments
 (0)