Skip to content

Commit 8a02b88

Browse files
authored
feat: Syntax Highlighting for Multiline Editor (#1919)
## Description Adds the option for syntax highlighting to the multiline texteditor via the new `highlightSyntax` prop. When `true` it will render a select box for the user to choose a syntax language for highlighting purposes. If the user selects `plaintext` the current vanilla `TextArea` component will render. If the user selects one of the provided code languages the Monaco Editor will render inside the dialog with syntax highlighting appropriate for that language. This feature is currently implemented in all instances of the `MultilineTextInputDialog` though it is debateable if it has use for the Annotation Editor. ## Related Issue and Pull requests Progresses #1891 Progresses Shopify/oasis-frontend#391 <!-- Link to any related issues using the format #<issue-number> --> ## Type of Change <!-- Please delete options that are not relevant --> - [x] Improvement ## Checklist <!-- Please ensure the following are completed before submitting the PR --> - [ ] I have tested this does not break current pipelines / runs functionality - [ ] I have tested the changes on staging ## Screenshots (if applicable) <!-- Include any screenshots that might help explain the changes or provide visual context --> Before ![image.png](https://app.graphite.com/user-attachments/assets/1566c663-63fa-4c50-8376-3cfd0d50ccf8.png) After ![image.png](https://app.graphite.com/user-attachments/assets/3a70519d-5323-44e6-93f9-b3720e710ccb.png) ![image.png](https://app.graphite.com/user-attachments/assets/c78ead58-5573-416d-a2c9-cab167032a25.png) Demo: [syntax-highlighting-opt1.mov <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.com/user-attachments/thumbnails/7b8f965b-d621-46b3-83a1-dfb3d0619b19.mov" />](https://app.graphite.com/user-attachments/video/7b8f965b-d621-46b3-83a1-dfb3d0619b19.mov) ## Test Instructions Confirm that the multiline text editor now renders a select box and that changing the language renders the code viewer and appropriately highlights syntax. <!-- Detail steps and prerequisites for testing the changes in this PR --> ## Additional Comments <!-- Add any additional context or information that reviewers might need to know regarding this PR -->
1 parent 28d89a1 commit 8a02b88

6 files changed

Lines changed: 97 additions & 12 deletions

File tree

react-compiler.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const REACT_COMPILER_ENABLED_DIRS = [
5555
"src/components/Editor/IOEditor/IOZIndexEditor.tsx",
5656
"src/components/shared/ReactFlow/FlowCanvas/TaskNode/ArgumentsEditor/DynamicDataDropdown.tsx",
5757
"src/components/shared/ReactFlow/FlowCanvas/Multiselect",
58+
"src/components/shared/CodeViewer/CodeEditor.tsx",
5859
"src/components/shared/HighlightText.tsx",
5960
"src/components/shared/AnnouncementBanners.tsx",
6061

src/components/Editor/IOEditor/InputValueEditor/InputValueDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const InputValueDialog = ({
3838
open={open}
3939
onCancel={onCancel}
4040
onConfirm={onConfirm}
41+
highlightSyntax
4142
/>
4243
);
4344
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import MonacoEditor from "@monaco-editor/react";
2+
3+
interface CodeEditorProps {
4+
value: string;
5+
language: string;
6+
onChange: (value: string) => void;
7+
}
8+
9+
function CodeEditor({ value, language, onChange }: CodeEditorProps) {
10+
return (
11+
<MonacoEditor
12+
language={language}
13+
theme="vs-dark"
14+
value={value}
15+
onChange={(v) => onChange(v ?? "")}
16+
options={{
17+
minimap: { enabled: false },
18+
scrollBeyondLastLine: false,
19+
lineNumbers: "on",
20+
wordWrap: "on",
21+
automaticLayout: true,
22+
}}
23+
/>
24+
);
25+
}
26+
27+
export default CodeEditor;

src/components/shared/Dialogs/MultilineTextInputDialog.tsx

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,28 @@ import {
99
DialogTitle,
1010
} from "@/components/ui/dialog";
1111
import { InlineStack } from "@/components/ui/layout";
12+
import {
13+
Select,
14+
SelectContent,
15+
SelectItem,
16+
SelectTrigger,
17+
SelectValue,
18+
} from "@/components/ui/select";
1219
import { Textarea } from "@/components/ui/textarea";
1320
import { Paragraph } from "@/components/ui/typography";
1421
import { cn } from "@/lib/utils";
1522

23+
import CodeEditor from "../CodeViewer/CodeEditor";
24+
25+
const LANGUAGE_OPTIONS = [
26+
{ value: "plaintext", label: "Plain Text" },
27+
{ value: "yaml", label: "YAML" },
28+
{ value: "python", label: "Python" },
29+
{ value: "javascript", label: "JavaScript" },
30+
{ value: "json", label: "JSON" },
31+
{ value: "sql", label: "SQL" },
32+
];
33+
1634
interface MultilineTextInputDialogProps {
1735
title: ReactNode;
1836
description?: string;
@@ -21,6 +39,7 @@ interface MultilineTextInputDialogProps {
2139
open: boolean;
2240
required?: boolean;
2341
maxLength?: number;
42+
highlightSyntax?: boolean;
2443
onCancel: () => void;
2544
onConfirm: (value: string) => void;
2645
}
@@ -33,10 +52,12 @@ export const MultilineTextInputDialog = ({
3352
open,
3453
required = false,
3554
maxLength,
55+
highlightSyntax,
3656
onCancel,
3757
onConfirm,
3858
}: MultilineTextInputDialogProps) => {
3959
const [value, setValue] = useState(initialValue);
60+
const [selectedLanguage, setSelectedLanguage] = useState("plaintext");
4061

4162
const handleConfirm = useCallback(() => {
4263
onConfirm(value);
@@ -61,22 +82,55 @@ export const MultilineTextInputDialog = ({
6182
setValue(initialValue);
6283
}, [initialValue]);
6384

85+
useEffect(() => {
86+
setSelectedLanguage("plaintext");
87+
}, [highlightSyntax]);
88+
6489
return (
6590
<Dialog open={open} onOpenChange={onCancel}>
6691
<DialogContent>
6792
<DialogTitle>{title}</DialogTitle>
68-
<DialogDescription className={cn(!description ? "hidden" : "")}>
69-
{description ?? title}
70-
</DialogDescription>
71-
<Textarea
72-
ref={setCursorToEnd}
73-
value={value}
74-
onChange={(e) => setValue(e.target.value)}
75-
placeholder={placeholder}
76-
className="min-h-32 max-h-[80vh]"
77-
required={required}
78-
maxLength={maxLength}
79-
/>
93+
<InlineStack gap="2" align="space-between" wrap="nowrap" fill>
94+
<DialogDescription className={cn(!description ? "hidden" : "")}>
95+
{description ?? title}
96+
</DialogDescription>
97+
{highlightSyntax && (
98+
<Select
99+
value={selectedLanguage}
100+
onValueChange={setSelectedLanguage}
101+
>
102+
<SelectTrigger className="w-40">
103+
<SelectValue />
104+
</SelectTrigger>
105+
<SelectContent>
106+
{LANGUAGE_OPTIONS.map((opt) => (
107+
<SelectItem key={opt.value} value={opt.value}>
108+
{opt.label}
109+
</SelectItem>
110+
))}
111+
</SelectContent>
112+
</Select>
113+
)}
114+
</InlineStack>
115+
{highlightSyntax && selectedLanguage !== "plaintext" ? (
116+
<div className="h-64">
117+
<CodeEditor
118+
value={value}
119+
language={selectedLanguage}
120+
onChange={setValue}
121+
/>
122+
</div>
123+
) : (
124+
<Textarea
125+
ref={setCursorToEnd}
126+
value={value}
127+
onChange={(e) => setValue(e.target.value)}
128+
placeholder={placeholder}
129+
className="min-h-32 max-h-[80vh]"
130+
required={required}
131+
maxLength={maxLength}
132+
/>
133+
)}
80134
<DialogFooter>
81135
<InlineStack gap="2" align="space-between" className="w-full">
82136
{maxLength && value.length >= maxLength && (

src/components/shared/ReactFlow/FlowCanvas/TaskNode/AnnotationsEditor/AnnotationsInput.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ export const AnnotationsInput = ({
409409
onConfirm={handleDialogConfirm}
410410
maxLength={config?.max}
411411
required={config?.required}
412+
highlightSyntax={inputType === "json"}
412413
/>
413414
)}
414415
</>

src/components/shared/ReactFlow/FlowCanvas/TaskNode/ArgumentsEditor/ArgumentInputDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const ArgumentInputDialog = ({
4141
open={open}
4242
onCancel={onCancel}
4343
onConfirm={onConfirm}
44+
highlightSyntax
4445
/>
4546
);
4647
};

0 commit comments

Comments
 (0)