Skip to content

Commit 7835da8

Browse files
committed
Allow multiline stdin in the UI
1 parent b5e6cb5 commit 7835da8

File tree

2 files changed

+60
-20
lines changed

2 files changed

+60
-20
lines changed

ui/frontend/Stdin.module.css

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
11
.form {
22
display: flex;
33
gap: 0.25em;
4+
align-items: flex-start;
45
}
56

6-
.text {
7+
.multiLine {
78
flex: 1;
9+
display: grid;
10+
}
11+
12+
.text,
13+
.sizer {
14+
margin: 0;
15+
padding: 0.25em;
16+
grid-area: 1 / 1 / 2 /2;
17+
max-height: 3.5lh;
18+
}
19+
20+
.text {
21+
resize: none;
22+
}
23+
24+
.sizer {
25+
visibility: hidden;
26+
white-space: pre-wrap;
827
}

ui/frontend/Stdin.tsx

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { FormEvent, useCallback } from 'react';
1+
import React, { ChangeEvent, FormEvent, KeyboardEvent, useCallback, useRef, useState } from 'react';
22
import { useDispatch, useSelector } from 'react-redux';
33

44
import { wsExecuteStdin } from './reducers/output/execute';
@@ -10,33 +10,54 @@ const Stdin: React.FC = () => {
1010
const dispatch = useDispatch();
1111
const disabled = !useSelector(enableStdinSelector);
1212

13+
const [content, setContent] = useState('');
14+
15+
const form = useRef<HTMLFormElement>(null);
16+
17+
const handleKeyDown = useCallback(
18+
(e: KeyboardEvent<HTMLTextAreaElement>) => {
19+
if (e.key === 'Enter' && !e.shiftKey) {
20+
e.preventDefault();
21+
form.current?.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
22+
}
23+
},
24+
[dispatch, form, content],
25+
);
26+
27+
const handleChange = useCallback(
28+
(e: ChangeEvent<HTMLTextAreaElement>) => {
29+
setContent(e.currentTarget.value);
30+
},
31+
[setContent],
32+
);
33+
1334
const handleSubmit = useCallback(
1435
(e: FormEvent<HTMLFormElement>) => {
1536
e.preventDefault();
1637

17-
const form = e.currentTarget;
18-
const formData = new FormData(form);
19-
20-
const content = formData.get('content')?.valueOf();
21-
22-
if (content && typeof content === 'string') {
23-
dispatch(wsExecuteStdin(content + '\n'));
24-
}
38+
dispatch(wsExecuteStdin(content + '\n'));
2539

26-
form.reset();
40+
setContent('');
2741
},
28-
[dispatch],
42+
[dispatch, content, setContent],
2943
);
3044

3145
return (
32-
<form onSubmit={handleSubmit} className={styles.form} data-test-id="stdin">
33-
<input
34-
type="text"
35-
name="content"
36-
autoComplete="off"
37-
className={styles.text}
38-
disabled={disabled}
39-
></input>
46+
<form onSubmit={handleSubmit} className={styles.form} data-test-id="stdin" ref={form}>
47+
<div className={styles.multiLine}>
48+
<textarea
49+
rows={1}
50+
onKeyDown={handleKeyDown}
51+
onChange={handleChange}
52+
name="content"
53+
autoComplete="off"
54+
spellCheck="false"
55+
className={styles.text}
56+
value={content}
57+
disabled={disabled}
58+
></textarea>
59+
<p className={styles.sizer}>{content} </p>
60+
</div>
4061
<button type="submit" disabled={disabled}>
4162
Send
4263
</button>

0 commit comments

Comments
 (0)