Skip to content

Commit decbec6

Browse files
authored
feat: syntax hilighting preferenses (#193)
1 parent 0ae0f11 commit decbec6

File tree

7 files changed

+143
-7
lines changed

7 files changed

+143
-7
lines changed

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@abraham/reflection": "^0.12.0",
3030
"esbuild": "^0.17.18",
3131
"inversify": "^6.0.1",
32+
"prismjs": "^1.29.0",
3233
"react": "17.0.2",
3334
"react-dom": "17.0.2",
3435
"styled-components": "^5.3.9",
@@ -43,6 +44,7 @@
4344
"@types/chrome": "^0.0.233",
4445
"@types/jest": "^27.4.0",
4546
"@types/node": "^20.6.0",
47+
"@types/prismjs": "^1.26.0",
4648
"@types/react": "^17.0.38",
4749
"@types/react-dom": "^17.0.11",
4850
"@types/react-test-renderer": "^17.0.0",

pnpm-lock.yaml

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/options/App.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import ErrorMessage from "./components/ErrorMessage";
77
const Container = styled.form`
88
padding: 2px;
99
font-family: system-ui;
10+
min-width: 480px;
11+
max-width: 90wv;
1012
`;
1113

1214
const App: React.FC = () => {
@@ -51,7 +53,6 @@ const App: React.FC = () => {
5153
<div>
5254
<TextArea
5355
name="text"
54-
spellCheck={false}
5556
onChange={onChange}
5657
onBlur={onBlur}
5758
value={jsonText}

src/options/components/TextArea.tsx

+111-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,117 @@
1+
import React from "react";
12
import styled from "styled-components";
3+
import Prism from "prismjs";
4+
import "prismjs/components/prism-json";
5+
import "prismjs/themes/prism-coy.css";
26

3-
const TextArea = styled.textarea`
4-
font-family: monospace;
5-
font-family: monospace;
7+
const Container = styled.div`
68
width: 100%;
7-
min-height: 64ex;
8-
min-width: 128ex;
9-
resize: vertical;
9+
height: 100%;
10+
resize: both;
11+
overflow: auto;
12+
border: ButtonBorder 1px solid;
13+
border-radius: 4px;
14+
&:focus-within {
15+
box-shadow: inset 0 0 3px AccentColor;
16+
}
1017
`;
1118

19+
const Content = styled.div`
20+
margin: 8px;
21+
position: relative;
22+
overflow: hidden;
23+
`;
24+
25+
const StyledPre = styled.pre`
26+
position: absolute;
27+
font-size: 14px;
28+
width: 100%;
29+
height: 100%;
30+
resize: none;
31+
margin: 0;
32+
padding: 0;
33+
`;
34+
35+
const StyledTextarea = styled.textarea`
36+
position: absolute;
37+
font-size: 14px;
38+
width: 100%;
39+
height: 100%;
40+
padding: 0;
41+
margin: 0;
42+
outline: none;
43+
border: none;
44+
resize: none;
45+
background-color: transparent;
46+
color: transparent;
47+
caret-color: CanvasText;
48+
`;
49+
50+
type Props = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
51+
52+
const TextArea: React.FC<Props> = (props) => {
53+
const highlightContainer = React.useRef<HTMLDivElement>(null);
54+
const container = React.useRef<HTMLDivElement>(null);
55+
const content = React.useRef<HTMLDivElement>(null);
56+
const textarea = React.useRef<HTMLTextAreaElement>(null);
57+
const [json, setJson] = React.useState(props.value as string);
58+
59+
React.useEffect(() => {
60+
highlight(json);
61+
}, []);
62+
63+
const onChange = React.useCallback(
64+
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
65+
setJson(e.target.value);
66+
highlight(e.target.value);
67+
autoResize();
68+
props.onChange?.(e);
69+
},
70+
[]
71+
);
72+
73+
const highlight = React.useCallback((source: string) => {
74+
if (!highlightContainer.current) {
75+
return;
76+
}
77+
78+
const highlighted = Prism.highlight(source, Prism.languages.json, "json");
79+
highlightContainer.current.innerHTML = highlighted;
80+
autoResize();
81+
}, []);
82+
83+
const autoResize = React.useCallback(() => {
84+
if (!textarea.current || !content.current) {
85+
return;
86+
}
87+
88+
// shrink scroll area to get the correct scroll area
89+
content.current.style.width = "0";
90+
content.current.style.height = "0";
91+
92+
const { scrollHeight, scrollWidth } = textarea.current;
93+
console.log(scrollHeight, scrollWidth);
94+
content.current.style.width = `${scrollWidth}px`;
95+
content.current.style.height = `${scrollHeight}px`;
96+
}, []);
97+
98+
return (
99+
<Container ref={container}>
100+
<Content ref={content}>
101+
<StyledPre>
102+
<code ref={highlightContainer} />
103+
</StyledPre>
104+
<StyledTextarea
105+
{...props}
106+
wrap="off"
107+
ref={textarea}
108+
onChange={onChange}
109+
spellCheck={false}
110+
value={json}
111+
/>
112+
</Content>
113+
</Container>
114+
);
115+
};
116+
12117
export default TextArea;

src/options/global.css

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
html {
2+
color-scheme: light dark;
3+
4+
color: CanvasText;
5+
background-color: Canvas;
6+
}
7+
8+
/* prism.js override */
9+
.token {
10+
background: none !important;
11+
}

src/options/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
<body>
77
<div id='vimmatic-settings'></div>
88
<script src='options.js'></script>
9+
<link rel='stylesheet' href='options.css'>
910
</body>
1011
</html>

src/options/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import ReactDOM from "react-dom";
33
import App from "./App";
4+
import "./global.css";
45

56
document.addEventListener("DOMContentLoaded", () => {
67
const wrapper = document.getElementById("vimmatic-settings");

0 commit comments

Comments
 (0)