Skip to content

Commit 895118b

Browse files
committed
feat: highlight issues in editor
1 parent c99297d commit 895118b

File tree

2 files changed

+121
-55
lines changed

2 files changed

+121
-55
lines changed

website/src/index.html

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ <h2>Install</h2>
3333
</div>
3434
<h2>Try it</h2>
3535
<p>Paste or type some text below.</p>
36-
<div>
36+
<div id="editor">
3737
<textarea id="input" rows="8">
3838
This is very unique and literally perfect.</textarea
3939
>
40+
<!-- prettier-ignore -->
41+
<div id="preview">This is <span class="highlight" title="Comparison of an uncomparable: 'very unique' is not comparable.">very unique</span> and literally perfect.</div>
4042
</div>
4143

4244
<h3>Issues</h3>
@@ -56,9 +58,39 @@ <h3>Issues</h3>
5658
<%- include('partials/footer.ejs') %>
5759

5860
<script>
61+
const baseApiURL = "<%= apiURL %>";
62+
5963
const input = document.getElementById("input");
6064
const issues = document.getElementById("issues");
61-
const baseApiURL = "<%= apiURL %>";
65+
const preview = document.getElementById("preview");
66+
67+
function highlight(text, issues) {
68+
const spans = [...issues].sort((a, b) => a.span[0] - b.span[0]);
69+
70+
let result = "";
71+
let cursor = 0;
72+
73+
for (const { span, message } of spans) {
74+
let [start, end] = span;
75+
76+
start--;
77+
end--;
78+
79+
if (start < cursor) {
80+
continue;
81+
}
82+
83+
result += text.slice(cursor, start);
84+
result += `<span class="highlight" title="${message}">`;
85+
result += text.slice(start, end);
86+
result += `</span>`;
87+
88+
cursor = end;
89+
}
90+
91+
result += text.slice(cursor);
92+
return result;
93+
}
6294

6395
function debounce(fn, delay) {
6496
let timer;
@@ -72,6 +104,8 @@ <h3>Issues</h3>
72104
async function check() {
73105
if (input.value.trim() === "") {
74106
issues.innerHTML = "<li>No issues found.</li>";
107+
preview.textContent = input.value;
108+
75109
return;
76110
}
77111

@@ -100,8 +134,14 @@ <h3>Issues</h3>
100134
li.innerHTML = `<em>${issue.check_path}</em> ${issue.message}`;
101135
issues.appendChild(li);
102136
}
137+
138+
preview.innerHTML = highlight(input.value, json.data);
103139
}
104140

141+
input.addEventListener(
142+
"input",
143+
() => (preview.textContent = input.value),
144+
);
105145
input.addEventListener("input", debounce(check, 300));
106146
</script>
107147
</body>

website/src/styles.css

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,41 @@
11
@import "./reset.css";
22

3-
body {
4-
font-family:
5-
"Georgia", "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
6-
line-height: 1.65;
7-
color: #171717;
8-
background-color: #fafafa;
3+
:root {
4+
--bg: #fafafa;
5+
--fg: #171717;
6+
--serif:
7+
Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
8+
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
9+
--focus-ring: 0 0 0 2px #4f46e5;
910
}
1011

11-
h1,
12-
h2,
13-
h3 {
14-
font-family: "Merriweather", "Georgia", serif;
12+
body {
13+
font-family: var(--serif);
14+
line-height: 1.65;
15+
color: var(--fg);
16+
background-color: var(--bg);
1517
}
1618

17-
main {
19+
main,
20+
footer {
1821
max-width: 42rem;
1922
margin: 4rem auto;
2023
padding: 0 1.5rem;
2124
}
2225

26+
footer {
27+
margin-top: 0;
28+
padding-bottom: 2rem;
29+
text-align: center;
30+
}
31+
32+
h1,
33+
h2,
34+
h3 {
35+
font-family: "Merriweather", Georgia, serif;
36+
font-weight: 600;
37+
}
38+
2339
h1 {
2440
font-size: 2.25rem;
2541
line-height: 2.5rem;
@@ -28,19 +44,12 @@ h1 {
2844

2945
h2 {
3046
font-size: 1.5rem;
31-
font-weight: 600;
32-
}
33-
34-
h3 {
35-
font-weight: 600;
3647
}
3748

3849
p {
3950
font-size: 1.125rem;
4051
color: #404040;
41-
42-
margin-top: 0.3rem !important;
43-
margin-bottom: 0.8rem !important;
52+
margin: 0.3rem 0 0.8rem !important;
4453
}
4554

4655
p.sm {
@@ -54,55 +63,43 @@ a {
5463
}
5564

5665
a:hover {
57-
color: #171717;
66+
color: var(--fg);
67+
}
68+
69+
em {
70+
font-style: italic;
71+
color: #2c2c2c;
72+
margin-right: 0.05rem;
5873
}
5974

6075
pre {
6176
font-size: 0.875rem;
6277
margin-top: 0.75rem;
78+
padding: 1rem;
6379
background-color: #fff;
6480
border: 1px solid #d4d4d4;
65-
color: #171717;
66-
padding: 1rem;
6781
border-radius: 0.5rem;
68-
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
6982
line-height: 1.5rem;
7083
white-space: pre-wrap;
7184
word-break: break-word;
7285
cursor: pointer;
7386
transition: border-color 0.2s;
87+
color: var(--fg);
88+
font-family: var(--mono);
7489
}
7590

7691
pre:hover {
7792
border-color: #a3a3a3;
7893
}
7994

80-
pre:focus {
81-
outline: none;
82-
box-shadow: 0 0 0 2px #4f46e5;
83-
}
84-
85-
pre:hover {
86-
border-color: #a3a3a3;
87-
}
88-
89-
pre:focus {
90-
outline: none;
91-
box-shadow: 0 0 0 2px #4f46e5;
92-
}
93-
9495
code:not(pre code) {
9596
padding: 0.1em 0.2em;
9697
border-radius: 4px;
97-
font-family: monospace;
9898
font-size: 0.75em;
9999
background-color: #fff;
100100
border: 1px solid #d4d4d4;
101-
color: #171717;
102-
}
103-
104-
code {
105-
font-family: inherit;
101+
font-family: var(--mono);
102+
color: var(--fg);
106103
}
107104

108105
code .blue {
@@ -126,23 +123,52 @@ code .amber {
126123
color: #f59e0b;
127124
}
128125

129-
textarea {
126+
textarea,
127+
#preview {
130128
width: 100%;
131-
border: 1px solid #d4d4d4;
132-
border-radius: 0.5rem;
133129
padding: 1rem;
134-
font-family: Georgia, Cambria, "Times New Roman", Times, serif;
135130
line-height: 1.5rem;
131+
font-size: 1rem;
132+
border-radius: 0.5rem;
133+
white-space: pre-wrap;
134+
word-wrap: break-word;
135+
overflow-wrap: break-word;
136+
font-family: var(--serif);
136137
}
137138

139+
textarea {
140+
border: 1px solid #d4d4d4;
141+
resize: vertical;
142+
caret-color: black;
143+
color: transparent;
144+
z-index: 100;
145+
}
146+
147+
textarea::selection {
148+
background: rgba(251, 255, 36, 0.6);
149+
color: transparent;
150+
}
151+
152+
pre:focus,
138153
textarea:focus {
139154
outline: none;
140-
box-shadow: 0 0 0 2px #4f46e5;
155+
box-shadow: var(--focus-ring);
141156
}
142157

143-
footer {
144-
max-width: 42rem;
145-
margin: 0 auto;
146-
padding: 0 1.5rem 2rem;
147-
text-align: center;
158+
#editor {
159+
position: relative;
160+
width: 100%;
161+
}
162+
163+
#preview {
164+
position: absolute;
165+
top: 0;
166+
left: 0;
167+
border: 1px solid transparent;
168+
pointer-events: none;
169+
}
170+
171+
.highlight {
172+
background-color: #fbff24;
173+
color: var(--fg);
148174
}

0 commit comments

Comments
 (0)