forked from Thertzlor/semantic-rainbow
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
445 lines (398 loc) · 23.2 KB
/
index.html
File metadata and controls
445 lines (398 loc) · 23.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Semantic Rainbow</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link href="https://fonts.googleapis.com/css?family=Fira+Sans:300,400,500,600" rel="stylesheet">
<link rel="icon" href="./assets/SR_favicon.ico" type="image/x-icon" />
<script src='./generator/lib/tinycolor.js'></script>
<script type="module">
/**@typedef {{foreground:string,alias?:string|boolean,bold?: boolean, italic?: boolean, underline?: boolean}} ColorRule */
/**@typedef {Record<string,string|ColorRule>} SemanticMap */
/**@typedef {{settings:{foreground:string}}[]} TextMateList*/
/**@typedef {import('../config.json')['themes'][number]} ColorConfig */
/**@typedef {import('tinycolor2')} TinyColorClass*/
/**@typedef {InstanceType<TinyColorClass>} TinyColor*/
import {generateColors, interpolate} from './generator/lib/colorGenerator.js';
/** @type {(tinycolor: TinyColorClass, config: ColorConfig) => {
semanticRules: Record<string, string | ColorRule>;
fallBackRules: {
name: string;
scope: string[];
settings: {
foreground: string;
fontStyle?: string;
};
}[];
meta: {
numColors: number;
manualColors: number;
baseNumber: number;
modNumber: number;
extraCombinationsNumber: number;
manualPercent: string;
};
}} */
var genfunc = generateColors;
/**
* @param {string} url
* @returns {Promise<string>} */
const urlFetch = url => new Promise((res, rej) => {
const Http = new XMLHttpRequest();
Http.open("GET", url);
Http.send();
Http.onreadystatechange = e => {
if (Http.readyState === 4 && Http.status === 200) res(Http.responseText);
else if (Http.readyState === 4) rej(Http.responseText);
};
});
/**
* @param {string} path
* @param {string} color
* @param {string} bg */
const colorClass = (path, color, bg) => [...document.getElementsByClassName(path.replace(/\./gm, '_').replace(/:/gm, '-'))].forEach(e => e.style[bg ? 'backgroundColor' : 'color'] = color);
(async () => {
//We need to fetch the JSON configuration file via HTTP
const {themes} = JSON.parse(await urlFetch('./generator/config.json').catch(() => '{themes:[]}'));
const frag = new DocumentFragment();
const mainTheme = themes.find(t => themes.length === 1 || t.mainTheme);
//Only using the stats from the main Theme
const {semanticRules, meta} = genfunc(tinycolor, mainTheme);
const {colorPath, path} = mainTheme;
let colorUrl = colorPath || `./colors/${path.split('/').pop()}`;
const {colors} = JSON.parse(await urlFetch(colorUrl).catch(() => '{colors:{}}'));
//Filling in variables into the text;
[...document.getElementsByClassName('text')].forEach(e => e.innerHTML = interpolate(e.innerHTML, meta));
//Creating the color table
let tableContainer;
let lastKey = '';
/** @type {Set<string>} */
const languages = new Set();
for (const k in semanticRules) {
if ((!Object.hasOwnProperty.call(semanticRules, k)) || semanticRules[k].alias || !k.includes(':')) continue;
const splitter = k.split(':');
const lang = splitter.pop();
const rule = semanticRules[k];
const origRule = semanticRules[splitter.join(':')];
if (origRule) {
const origType = typeof origRule;
const ruleType = typeof rule;
if (origType !== ruleType) {
languages.add(lang);
continue;
}
if (typeof origRule === 'string' && rule === origRule) continue;
if (typeof origRule !== 'string' && ['bold', 'foreground', 'italic', 'underline'].some(s => origRule[s] !== rule[s])) languages.add(lang);
} else languages.add(lang);
}
if (languages.size) {
const radio = frag.appendChild(document.createElement('fieldset'));
const makeRadio = val => {
const rad = radio.appendChild(document.createElement('input'));
rad.type = 'radio';
rad.id = val;
rad.value = val;
rad.name = 'lang';
rad.addEventListener('change', function () {
for (const x of ['default', ...languages]) {
const tab = document.getElementById(`tab_${x}`);
tab && (tab.style.display = x === this.value ? 'table' : 'none');
}
});
const lab = radio.appendChild(document.createElement('label'));
lab.innerHTML = val;
lab.htmlFor = val;
return rad;
};
makeRadio('default').checked = true;
languages.forEach(l => makeRadio(l));
}
const tableMaker = (lang, visible) => {
const myTable = frag.appendChild(document.createElement('table')).appendChild(document.createElement('tbody'));
myTable.parentElement.id = `tab_${lang || 'default'}`;
myTable.parentElement.style.display = visible ? 'table' : 'none';
for (const k in semanticRules) {
//We only output a demo for the main token type, not for any aliases
if ((!Object.hasOwnProperty.call(semanticRules, k)) || semanticRules[k].alias || (k.includes(':') && (!lang || !k.includes(`:${lang}`)))) continue;
if (!k.includes(':') && semanticRules[`${k}:${lang}`]) continue;
const styleElement = semanticRules[k];
const keyArray = k.split('.');
const firstKey = keyArray[0].replace(`:${lang}`, '');
//If the token type has changed, insert a new row into the table
if (lastKey !== firstKey) {
tableContainer = myTable.appendChild(document.createElement('tr'));
//The first cell contains the token name
tableContainer.appendChild(document.createElement('td')).innerHTML = firstKey;
}
lastKey = firstKey;
const colorValue = styleElement.foreground || styleElement;
const tableSquare = tableContainer.appendChild(document.createElement('td'));
//Generating the tooltip element for the individual cells
const tooltip = tableSquare.appendChild(document.createElement('div'));
tooltip.classList.add('expl');
//HTML formatting for the token description
colorClass(k, colorValue);
tooltip.innerHTML = (k.replace(/\./g, '\n<br>')).split('\n').map((e, i) => i === 0 ? e : `<i>${e}</i>`).join('\n') + `\n<p style="color:${colorValue}; ${k.includes('declaration') ? 'font-style:italic;' : ''} text-shadow:none;">${colorValue}</p>`;
tableSquare.style.background = colorValue;
tableSquare.classList.add('tableSquare');
}
for (const c in colors) if (Object.hasOwnProperty.call(colors, c)) colorClass(c, tinycolor(colors[c]).toRgbString(), true);
};
for (const l of ['default', ...languages]) tableMaker(l, l === 'default');
//Showing the table
document.getElementById("colorTable").appendChild(frag);
}
)()
</script>
<style>
#extable {
width: 97vw;
text-align: left;
position: relative;
height: 65vh;
display: block;
overflow: hidden;
}
#extable img {
max-width: 130%;
transition: transform 7s ease-out;
backface-visibility: hidden;
margin-top: -2em;
}
#extable td {
overflow: hidden;
position: relative
}
#extable::after {
box-shadow: inset -10px 0 22px 8px #14151a;
position: absolute;
content: '';
pointer-events: none;
display: block;
background: linear-gradient(transparent 80%, #14151a 98%);
top: 0;
bottom: 0;
left: 0;
right: 0
}
#extable img:hover {
transform: translateY(calc((100% - 65vh) * -1))
}
* {
box-sizing: border-box;
color: white;
text-shadow: 2px 2px 5px black, 0 0 2px black;
}
table {
margin: 0 auto;
border-spacing: .4vw;
}
fieldset {
text-align: center;
border: none;
margin: .5em
}
fieldset input+label {
margin-right: 1em;
color: lightgray;
}
fieldset input:checked+label {
color: white
}
.tableSquare {
border: .2vmax transparent solid;
position: relative;
transition: border-color .3s ease-in-out;
}
#tab_default {
width: 93vw;
}
#tab_default td:first-of-type {
width: 0;
padding-right: 1vw;
}
hr {
margin: 3em auto
}
.tableSquare .expl {
line-height: 140%;
z-index: 4000;
position: absolute;
opacity: 0;
display: block;
overflow: hidden;
transition: opacity .4s, right .2s, bottom .2s;
bottom: 40%;
right: 40%;
pointer-events: none;
background: #14151a;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.3);
padding: 10px;
border: 3px solid #595e75;
border-radius: 5px;
}
.tableSquare:hover {
border-color: white;
}
.tableSquare:hover .expl {
opacity: 1;
right: 50%;
bottom: 50%;
}
.tableSquare p {
margin: 0;
}
#extable td::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
/* background: rgba(255, 0, 0, .1); */
box-shadow: inset 0 4px 12px 4px #14151a;
display: block;
pointer-events: none;
z-index: 500000;
}
.tableSquare::after {
content: '';
display: block;
padding-top: 100%;
}
h1,
h2,
h3 {
text-align: center;
}
h1 {
margin-top: 1.5em;
}
body.bod {
padding-bottom: 3%
}
.bg {
text-shadow: none;
border-radius: 0.2em;
padding: 0 .1em;
}
body,
html {
margin: 0;
padding: 0;
font-family: 'Fira Sans', Calibri, sans-serif;
font-weight: 500;
font-size: 1.15em;
background: #14151a;
}
.text {
font-weight: 400;
margin: 0 auto;
width: 60%;
line-height: 1.6;
}
.text hr {
width: 130%;
margin-left: -15%
}
.text h2 {
text-align: left;
}
.text h3 {
font-weight: 400;
text-align: left;
}
.class {
font-weight: bold;
}
.ctx {
text-align: center;
opacity: 0.5;
}
</style>
</head>
<body class="bod">
<h1> <a href="https://github.com/Thertzlor/semantic-rainbow" style="text-decoration:none ;">Semantic Rainbow</a> </h1>
<h3 class="text"> A {manualPercent}% hand crafted Color Theme for Visual Studio Code</h3>
<hr>
<h2>Examples</h2>
<table id="extable">
<tbody>
<tr>
<th>TypeScript</th>
<th>Python</th>
<th>SCSS</th>
</tr>
<tr>
<td><a target="blank" href="./assets/ex_1.png"><img src="./assets/ex_1.png" alt="Typescript highlighted"></a></td>
<td><a target="blank" href="./assets/ex_3.png"><img src="./assets/ex_3.png" alt="SCSS highlighted"></a></td>
<td><a target="blank" href="./assets/ex_2.png"><img src="./assets/ex_2.png" alt="Python highlighted"></a>
</td>
</tr>
</tbody>
</table>
<hr>
<h3><a href="https://marketplace.visualstudio.com/items?itemName=thertzlor.semantic-rainbow">Get it on the Visual Studio Marketplace</a></h3>
<hr>
<h2>Colors</h2>
<p class="ctx">[Hover for details]</p>
<div id="colorTable"></div>
<div class="text">
<hr>
<h2 id="faq">FAQ</h2>
<p><strong>Are that many colors really necessary?</strong><br>
No, absolutely not. Some of them are in fact completely ludicrous.<br>
For example if you know any language in which <em>static async enum members</em> are a thing I would love to see it. On the bright side, that means that not every single color needs to be the most beautiful tone, and also that I don't need to worry about the contrast between some of the colors if one of them is a super weird or even impossible edge case.</p>
<p><strong>Some of those colors seem pretty close to each other, is it really possible to tell them apart?</strong><br>
It is true that some colors would be hard to distinguish in <em>isolation</em> but in the actual code tokens are literally surrounded by context. You'd be surprised how many slight differences you pick up subconsciously after a while.</p>
<p><strong>But some of them seem pretty much identical?</strong><br>
That would be the declaration styles. That's because they are mainly differentiated from other tokens by being shown in <em>italics</em>.</p>
<p><strong>Which languages are used to test this Theme?</strong><br>
I am mainly testing in TypeScript, Python, Lua, CSS/SCSS and HTML. I am not opposed to include other languages in the future.
</p>
<p><strong>I can't get the private token modifier in TypeScript to work, am I doing something wrong?</strong><br>
No, the private modifier isn't implemented in the Typescript language server yet. The token is included partly just top be future proof and partly because other language servers are already supporting it (Java for example).</p>
<hr>
<h2 id="design-philosophy">Design Philosophy</h2>
<p>First off, <a href="https://www.modal-marginalia.com/post/1-syntax-highlighting-and-focus">here is why I generally think this theme might work for you.</a></p>
<p>As for the colors themselves, I am not going to pretend that I went into this project with a definite concept and a background in color psychology. Many of these color associations emerged basically on their own as I was tweaking colors back and forth and later I went back to figure out <em>how</em> they might work.<br>
The key to making a Theme with this many colors work without things getting too confusing is to create a spectrum in which colors do not arbitrarily representi keywords but instead represent different concepts and roles in the program, and based on this spectrum colors are assigned to the different tokens.</p>
<p>The spectrum that emerged for this particular theme is the following:</p>
<h3 id="red-valuesinformation"><strong class="variable_readonly">Red:</strong> Values/Information</h3>
<h3 id="purple-keywordsflow-control"><strong class="macro_static">Purple:</strong> Keywords/Flow Control</h3>
<h3 id="blue-actiontransformation"><strong class="function">Blue:</strong> Action/Transformation</h3>
<h3 id="green-statestorage"><strong class="class">Green:</strong> State/Storage</h3>
<h3 id="yellow-definitionsprimitives"><strong class="type">Yellow:</strong> Definitions/Primitives</h3>
<p>Anything directly containing a discrete piece of information is red. This is where your <span class="variable">variables</span> and <span class="variable_readonly">constants</span> reside. They are so to speak the lifeblood flowing through the code and whenever you see red, you know information is being accessed and processed.<br>
Blue is the color for change and transformation, in other words: <span class="function">Functions</span>, since they generate and modify other information. To put it in more poetic terms information flows <em>through</em> them like water.<br>
Green is for anything that is stateful or defines a state. Here we find <span class="property">properties</span> that set the state within an object, <span class="namespace">namespaces</span> which represent groupings of information a and <span class="class">classes</span> which are of course also stateful. (Surely you could argue that variables are also "stateful" but they only represent their own singular state at the time).<br>
Yellow jumps out at you as a sort of signal, for example a <span class="type">type</span> signalling exactly what static (at least in TS) kind of data a certain parameter or variable expects. It also occurs where we see "raw" primitive data being defined such as a string representing nothing other than itself, or boolean values.</p>
<p>These are the main components and for most other tokens deciding which color to assign them is a matter of interpolation.<br>
For example <span class="method">methods</span> are midway between the blue of <span class="function">functions</span> and the green of <span class="class">classes</span>, representing them being being influenced by the specific state of their containing context.<br>
<span class="macro_static">keywords</span> like <em>return, for, var, etc</em> direct information, true but information <em>around</em> themselves depending on what <em>they</em> represent so they stand between blue transformation and red information.<br>
<span class="parameter">Parameters</span> are orange because in addition to storing values like regular <span class="variable">variables</span> they veer slightly in the direction of statefullness (green), as they represent the specific state of a single execution of a function.<br>
Numbers are slightly more greenish yellow than other primitives since they represent a very <em>discrete</em> state and <span class="enum">enums</span> are more yellow than <span class="namespace">namespaces</span> and <span class="class">classes</span> because they are basically a state solely defined by such primitives.<br>
And so on and so forth, for basically every token this kind of reasoning is applied, if only post-hoc.
</p>
<p>Up to this point everything has been about the hue of the colors. Other color properties can be used to convey even more information. The next color rule used in Semantic Rainbow is the following:</p>
<h3 id="saturation-and-vibrance-are-mapped-to-control-and-mutability"><strong>Saturation and Vibrance</strong> are mapped to control and mutability.</h3>
<p>The bolder the color the more "dynamic" the value it represents. A rewritable variable or property has a saturated and bright red and green respectively, but when something cannot be reassigned like a constant or a readonly property that color will darken, appear not as prominent, a more stable and calmly enduring entity.</p>
<p>There are of course even more ways to modify a color to be less vibrant and saturated besides dimming it towards black, it can also be bleached towards white, or generally faded using alpha. These different ways to reduce vibrance are used to represent different ways in which tokens differ. Anything belonging to a default library is shown desaturated and brightened. Being predefined, they are outside our control and their coloration reflects that indirectness. And once properly supported private properties and methods will be designated with a slight transparency, as they are ephemeral and only exist in specific contexts.</p>
<p>And finally going completely beyond color into text formatting:<br>
Any time any value is declared or initialized, the token will be displayed in italics. Also classes are shown in bold to distinguish them more effectively from namespaces and highlight their special role in object oriented programming. I am not using the underlined font style because I don't think it ever looks good.</p>
<p>The purpose of <strong>Semantic Rainbow</strong> is to extract all information your code can provide. I hope this concept shows how this wealth of information can still be handled in an intuitive manner.</p>
<hr>
<h2>Colored Highlights and Ranges</h2>
<p>In addition to the usual coloring of tokens version 1.1.0 of <strong>Semantic Rainbow</strong> introduces special coloring for highlighting and ranges used for several VSCode features. The default colors for most of these different highlights are neutral greys, making it hard to distinguish by which means a selection or highlight was created, and I felt it was in the maximalist spirit of the theme to overhaul their appearance and give each type of selection an easily identifiable color.<br>
At the same time, since most of the token colors are rather vibrant, selections and and ranges are generally assigned more restrained colors.</p>
<p><span class="bg editor_selectionBackground">Manual selections</span> are still presented in a neutral grey, while the <span class="bg editor_selectionHighlightBackground">highlighting of identical text</span> has a slightly less opaque appearance. The <span class="bg editor_inactiveSelectionBackground">selected highlighting in inactive documents</span> has a different shade, designed to pair well with the search highlight color since when switching between matches the highlighting of the current search match is technically inactive.</p>
<p><span class="bg editor_findMatchHighlightBackground">The matches of a search are highlighted</span> in green, with a more subtle and subdued green <span class="bg editor_findRangeHighlightBackground">designating the range of the search</span>, if the search is limited to a specific area. Other generic range highlights used by quick open and find features are <span class="bg editor_rangeHighlightBackground">highlighted in a paler and brighter shade of green</span>. Find matches in the peek view editor <span class="bg peekViewEditor_matchHighlightBackground">have a turquoise background.</span></p>
<p>If a specific symbol is selected, its other instances are <span class="bg editor_wordHighlightBackground">highlighted in a steely blue</span> with a more vivid coloration <span class="bg editor_wordHighlightStrongBackground">designating a write-access.</span></p>
<p>When merely <span class="bg editor_hoverHighlightBackground">hovering over a symbol</span>, a subtle orange highlight is applied, since at this point other more prominent hover actions like tooltips also appear the highlighting does not need grab attention unless it is applied to a larger range.</p>
<p><span class="bg editor_snippetTabstopHighlightBackground">The highlight color for snippet tab stops</span> is purple, a stronger color than usual as it signifies the need for user-action and the <span class="bg editor_snippetFinalTabstopHighlightBackground">highlighting of the final tab stop</span> uses the same color but slightly more intense.</p>
<p>And finally, the <span class="bg editor_symbolHighlightBackground">Symbol Highlight</span> is an eye-catching orange, chosen because it is only visible for a blink of less than a second when skipping to a symbol definition.</p>
</div>
</body>
</html>