@potch/html-bin 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # html-bin
2
+
3
+ wip!
package/index.html ADDED
@@ -0,0 +1,35 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Document</title>
7
+ <script defer type="module">
8
+ import { createBin } from "/build/index.js";
9
+ window.bin = createBin({
10
+ container: document.body,
11
+ sources: {
12
+ js: `// js goes here
13
+ const el = document.querySelector('h1');
14
+
15
+ function update(time) {
16
+ el.innerText = time;
17
+ }
18
+
19
+ setInterval(() => {
20
+ update(Date.now());
21
+ }, 1000)`,
22
+ css: `h1, .cool, #foo {
23
+ color: #fc0;
24
+ background: hsla(120, 50%, 75%, 0.3);
25
+ }`,
26
+ html: "<h1>HI</h1>\n",
27
+ },
28
+ split: 0.6,
29
+ height: "512px",
30
+ });
31
+ </script>
32
+ <link rel="stylesheet" href="src/style.css" />
33
+ </head>
34
+ <body></body>
35
+ </html>
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@potch/html-bin",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "build": "rollup -c",
10
+ "start": "npm run build && tinyserve -p 8081 -w 'src index.html' -d . -x 'npm run build'"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "@codemirror/commands": "^6.8.1",
17
+ "@codemirror/lang-css": "^6.3.1",
18
+ "@codemirror/lang-html": "^6.4.9",
19
+ "@codemirror/lang-javascript": "^6.2.4",
20
+ "@codemirror/language": "^6.11.0",
21
+ "@codemirror/search": "^6.5.11",
22
+ "@codemirror/state": "^6.5.2",
23
+ "@codemirror/view": "^6.36.8",
24
+ "@potch/minifw": "^3.1.1",
25
+ "@rollup/plugin-terser": "^0.4.4",
26
+ "@uiw/codemirror-theme-dracula": "^4.23.12"
27
+ },
28
+ "devDependencies": {
29
+ "@potch/tinyserve": "^1.5.1",
30
+ "@rollup/plugin-node-resolve": "^16.0.1",
31
+ "rollup": "^4.40.2"
32
+ }
33
+ }
@@ -0,0 +1,12 @@
1
+ import { nodeResolve } from "@rollup/plugin-node-resolve";
2
+ import terser from "@rollup/plugin-terser";
3
+
4
+ export default {
5
+ input: "src/index.js",
6
+ output: {
7
+ sourcemap: true,
8
+ dir: "build",
9
+ format: "es",
10
+ },
11
+ plugins: [nodeResolve(), terser()],
12
+ };
package/src/index.js ADDED
@@ -0,0 +1,452 @@
1
+ import {
2
+ lineNumbers,
3
+ highlightActiveLineGutter,
4
+ highlightSpecialChars,
5
+ drawSelection,
6
+ dropCursor,
7
+ crosshairCursor,
8
+ highlightActiveLine,
9
+ keymap,
10
+ EditorView,
11
+ } from "@codemirror/view";
12
+ import { EditorState } from "@codemirror/state";
13
+ import {
14
+ indentOnInput,
15
+ syntaxHighlighting,
16
+ bracketMatching,
17
+ } from "@codemirror/language";
18
+ import { history, defaultKeymap, historyKeymap } from "@codemirror/commands";
19
+ import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
20
+
21
+ const cmSetup = [
22
+ lineNumbers(),
23
+ highlightActiveLineGutter(),
24
+ highlightSpecialChars(),
25
+ history(),
26
+ drawSelection(),
27
+ dropCursor(),
28
+ EditorState.allowMultipleSelections.of(true),
29
+ indentOnInput(),
30
+ bracketMatching(),
31
+ crosshairCursor(),
32
+ highlightActiveLine(),
33
+ highlightSelectionMatches(),
34
+ EditorView.theme(
35
+ {
36
+ ".cm-content": {
37
+ caretColor: "#fff",
38
+ },
39
+ },
40
+ { dark: true }
41
+ ),
42
+ keymap.of([...defaultKeymap, ...searchKeymap, ...historyKeymap]),
43
+ ];
44
+
45
+ import { javascript } from "@codemirror/lang-javascript";
46
+ import { html } from "@codemirror/lang-html";
47
+ import { css } from "@codemirror/lang-css";
48
+
49
+ import { classHighlighter } from "@lezer/highlight";
50
+ import {
51
+ dom as _,
52
+ signal,
53
+ computed,
54
+ effect as _effect,
55
+ on as _on,
56
+ } from "@potch/minifw";
57
+
58
+ const getLeft = (el) => (el ? el.offsetLeft + getLeft(el.offsetParent) : 0);
59
+ const getTop = (el) => (el ? el.offsetTop + getTop(el.offsetParent) : 0);
60
+
61
+ const debounceComputed = (s, ms) => {
62
+ const out = signal(s.value);
63
+ let timeout = null;
64
+ s.watch(() => {
65
+ if (timeout) {
66
+ clearTimeout(timeout);
67
+ }
68
+ timeout = setTimeout(() => {
69
+ timeout = null;
70
+ out.value = s.value;
71
+ }, ms);
72
+ });
73
+ return out;
74
+ };
75
+
76
+ const maybe = (condition, value) => (condition ? value : "");
77
+
78
+ export const createBin = ({
79
+ container,
80
+ sources: rawSources,
81
+ split,
82
+ width,
83
+ height,
84
+ }) => {
85
+ const editorSplit = signal(parseFloat(split) || 0.5);
86
+
87
+ const teardownFns = [];
88
+ const effect = (...args) => {
89
+ teardownFns.push(_effect(...args));
90
+ };
91
+ const on = (...args) => {
92
+ teardownFns.push(_on(...args));
93
+ };
94
+
95
+ const actualWidth = signal(0);
96
+ const widthObserver = new ResizeObserver((entries) => {
97
+ if (entries[0]) {
98
+ actualWidth.value = entries[0].contentRect.width;
99
+ }
100
+ });
101
+
102
+ const isMiniMode = computed(() => actualWidth.value <= 700);
103
+ const resizing = signal(false);
104
+ const splitOverride = signal(null);
105
+
106
+ const activeTab = signal("js");
107
+
108
+ const sources = {
109
+ js: signal(rawSources.js ?? "// js goes here"),
110
+ css: signal(rawSources.css ?? "/* css goes here */\n"),
111
+ html: signal(rawSources.html ?? "<!-- html goes here -->\n"),
112
+ };
113
+
114
+ // previews
115
+
116
+ const scriptURL = computed((oldURL) => {
117
+ if (oldURL) {
118
+ URL.revokeObjectURL(oldURL);
119
+ }
120
+ return URL.createObjectURL(
121
+ new Blob([sources.js.value], { type: "text/javascript" })
122
+ );
123
+ });
124
+
125
+ const cssURL = computed((oldURL) => {
126
+ if (oldURL) {
127
+ URL.revokeObjectURL(oldURL);
128
+ }
129
+ return URL.createObjectURL(
130
+ new Blob([sources.css.value], { type: "text/css" })
131
+ );
132
+ });
133
+
134
+ const previewDoc = computed(
135
+ () => `
136
+ <!DOCTYPE html>
137
+ <html>
138
+ <head>
139
+ <meta charset=utf8>
140
+ <link rel="stylesheet" href="${cssURL.value}">
141
+ </head>
142
+ <body>
143
+ ${sources.html.value}
144
+ <script>
145
+ window.onerror = function (msg, file, line, col, error) {
146
+ window.top.postMessage({
147
+ type: 'error',
148
+ msg: msg,
149
+ line: line,
150
+ col: col,
151
+ stack: error.stack.split('\\n')
152
+ }, '*');
153
+ };
154
+ </script>
155
+ <script type="module" src="${scriptURL.value}"></script>
156
+ </body>
157
+ </html>
158
+ `
159
+ );
160
+
161
+ const debouncePreviewDoc = debounceComputed(previewDoc, 1000);
162
+
163
+ const previewURL = computed((oldURL) => {
164
+ if (oldURL) {
165
+ URL.revokeObjectURL(oldURL);
166
+ }
167
+ return URL.createObjectURL(
168
+ new Blob([debouncePreviewDoc.value], { type: "text/html" })
169
+ );
170
+ });
171
+
172
+ // editor tabs
173
+
174
+ const jsEditorEl = _("div", {
175
+ className: computed(
176
+ () =>
177
+ "bin__editor bin__editor--js" +
178
+ maybe(activeTab.value === "js", " bin__editor--active")
179
+ ),
180
+ });
181
+ const cssEditorEl = _("div", {
182
+ className: computed(
183
+ () =>
184
+ "bin__editor bin__editor--css" +
185
+ maybe(activeTab.value === "css", " bin__editor--active")
186
+ ),
187
+ });
188
+ const htmlEditorEl = _("div", {
189
+ className: computed(
190
+ () =>
191
+ "bin__editor bin__editor--html" +
192
+ maybe(activeTab.value === "html", " bin__editor--active")
193
+ ),
194
+ });
195
+
196
+ const tabsForm = _(
197
+ "form",
198
+ {
199
+ className: "bin__tabs",
200
+ onsubmit: (e) => e.preventDefault(),
201
+ oninput: (e) => (activeTab.value = tabsForm.elements.tab.value),
202
+ },
203
+ _(
204
+ "label",
205
+ { className: "bin__tab bin__editor_tab" },
206
+ _("input", { type: "radio", name: "tab", value: "html" }),
207
+ "html"
208
+ ),
209
+ _(
210
+ "label",
211
+ { className: "bin__tab bin__editor_tab" },
212
+ _("input", { type: "radio", name: "tab", value: "css" }),
213
+ "css"
214
+ ),
215
+ _(
216
+ "label",
217
+ { className: "bin__tab bin__editor_tab" },
218
+ _("input", { type: "radio", name: "tab", value: "js", checked: true }),
219
+ "javascript"
220
+ ),
221
+ _(
222
+ "label",
223
+ { className: "bin__tab bin__preview_tab bin__mini_preview_tab" },
224
+ _("input", {
225
+ type: "radio",
226
+ name: "tab",
227
+ value: "preview",
228
+ disabled: computed(() => (isMiniMode.value ? false : true)),
229
+ }),
230
+ "preview",
231
+ _(
232
+ "button",
233
+ {
234
+ className: "bin__preview__refresh bin__iconbutton",
235
+ title: "reload the preview",
236
+ "aria-label": "reload the preview",
237
+ onclick: () => {
238
+ previewEl.value?.contentWindow.location.reload();
239
+ },
240
+ },
241
+ "🔁"
242
+ )
243
+ )
244
+ );
245
+
246
+ const resizerEl = _("div", {
247
+ className: "bin__resizer",
248
+ });
249
+
250
+ let previewEl = signal();
251
+
252
+ const binEl = _(
253
+ "div",
254
+ {
255
+ className: computed(
256
+ () =>
257
+ "bin" +
258
+ maybe(isMiniMode.value, " bin--mini-mode") +
259
+ maybe(resizing.value, " bin--resizing")
260
+ ),
261
+ },
262
+ _(
263
+ "div",
264
+ { className: "bin__widget" },
265
+ _(
266
+ "div",
267
+ { className: "bin__tabstrip" },
268
+ tabsForm,
269
+ _("button", {
270
+ className: "bin__editor__expand bin__iconbutton",
271
+ title: "toggle showing only the editor",
272
+ "aria-label": "toggle showing only the editor",
273
+ innerText: computed(() => (splitOverride.value === 1 ? "⏮️" : "↔️")),
274
+ onclick: () => {
275
+ splitOverride.value = splitOverride.value === 1 ? null : 1;
276
+ },
277
+ })
278
+ ),
279
+ _(
280
+ "div",
281
+ { className: "bin__menu" },
282
+ _(
283
+ "div",
284
+ { className: "bin__tab bin__preview_tab bin__tab--active" },
285
+ "preview",
286
+ _(
287
+ "button",
288
+ {
289
+ className: "bin__preview__refresh bin__iconbutton",
290
+ title: "reload the preview",
291
+ "aria-label": "reload the preview",
292
+ onclick: () => {
293
+ previewEl.value?.contentWindow.location.reload();
294
+ },
295
+ },
296
+ "🔁"
297
+ )
298
+ ),
299
+ _("button", {
300
+ className: "bin__preview__expand bin__iconbutton",
301
+ title: "toggle showing only the preview",
302
+ "aria-label": "toggle showing only the preview",
303
+ innerText: computed(() => (splitOverride.value === 0 ? "⏭️" : "↔️")),
304
+ style: computed(() => ({
305
+ order: splitOverride.value === 0 ? -1 : "unset",
306
+ })),
307
+ onclick: () => {
308
+ splitOverride.value = splitOverride.value === 0 ? null : 0;
309
+ },
310
+ })
311
+ ),
312
+ jsEditorEl,
313
+ cssEditorEl,
314
+ htmlEditorEl,
315
+ resizerEl,
316
+ _("iframe", {
317
+ ref: previewEl,
318
+ className: computed(
319
+ () =>
320
+ "bin__preview" +
321
+ maybe(activeTab.value === "preview", " bin__editor--active")
322
+ ),
323
+ src: previewURL,
324
+ }),
325
+ _("div", { className: "bin__controls" })
326
+ )
327
+ );
328
+
329
+ if (width) binEl.style.setProperty("--bin-width", width);
330
+ if (height) binEl.style.setProperty("--bin-height", height);
331
+
332
+ // resizing
333
+
334
+ const ilerp = (a, b, i) => (i - a) / (b - a);
335
+ const clamp = (a, b, n) => Math.max(a, Math.min(n, b));
336
+
337
+ const updateResize = (e) => {
338
+ if (resizing.value) {
339
+ let pos = e.clientX - resizerEl.offsetWidth / 2 - getLeft(binEl);
340
+ editorSplit.value = clamp(
341
+ 0.2,
342
+ 0.8,
343
+ ilerp(10, binEl.offsetWidth - 10, pos)
344
+ );
345
+ }
346
+ };
347
+
348
+ const startResize = (e) => {
349
+ resizing.value = true;
350
+ updateResize(e);
351
+ splitOverride.value = null;
352
+ };
353
+
354
+ const endResize = (e) => {
355
+ resizing.value = false;
356
+ updateResize(e);
357
+ };
358
+
359
+ on(resizerEl, "mousedown", startResize);
360
+ on(document.body, "mouseup", endResize);
361
+ on(document.body, "mouseleave", endResize);
362
+
363
+ on(binEl, "mousemove", (e) => {
364
+ updateResize(e);
365
+ });
366
+
367
+ effect(() => {
368
+ binEl.style.setProperty(
369
+ "--resizer-split",
370
+ splitOverride.value !== null ? splitOverride.value : editorSplit.value
371
+ );
372
+ });
373
+
374
+ // editor tabs
375
+
376
+ effect(() => {
377
+ const t = activeTab.value;
378
+ const m = isMiniMode.value;
379
+ if (t === "preview" && !m) {
380
+ tabsForm.elements.tab.value = "js";
381
+ activeTab.value = "js";
382
+ }
383
+ });
384
+
385
+ // create CM editors
386
+
387
+ const update = (s) => (v) => {
388
+ if (v.docChanged) {
389
+ // how to get the full text content from the editor
390
+ s.value = v.state.doc.toString();
391
+ }
392
+ };
393
+
394
+ const editors = {
395
+ js: new EditorView({
396
+ doc: sources.js.value,
397
+ parent: jsEditorEl,
398
+ extensions: [
399
+ cmSetup,
400
+ javascript(),
401
+ syntaxHighlighting(classHighlighter),
402
+ EditorView.updateListener.of(update(sources.js)),
403
+ ],
404
+ }),
405
+ css: new EditorView({
406
+ doc: sources.css.value,
407
+ parent: cssEditorEl,
408
+ extensions: [
409
+ cmSetup,
410
+ css(),
411
+ syntaxHighlighting(classHighlighter),
412
+ EditorView.updateListener.of(update(sources.css)),
413
+ ],
414
+ }),
415
+ html: new EditorView({
416
+ doc: sources.html.value,
417
+ parent: htmlEditorEl,
418
+ extensions: [
419
+ cmSetup,
420
+ html(),
421
+ syntaxHighlighting(classHighlighter),
422
+ EditorView.updateListener.of(update(sources.html)),
423
+ ],
424
+ }),
425
+ };
426
+
427
+ // mount bin and start
428
+ container.append(binEl);
429
+ widthObserver.observe(binEl);
430
+
431
+ // destroy / teardown
432
+ const findEffects = (node) => {
433
+ if (node._effects) {
434
+ teardownFns.push(...node._effects);
435
+ }
436
+ for (let n of node?.childNodes) findEffects(n);
437
+ };
438
+ findEffects(binEl);
439
+
440
+ const teardown = () => {
441
+ while (teardownFns.length) {
442
+ teardownFns.pop()();
443
+ }
444
+ widthObserver.disconnect();
445
+ };
446
+
447
+ return {
448
+ editors,
449
+ activeTab,
450
+ teardown,
451
+ };
452
+ };
package/src/style.css ADDED
@@ -0,0 +1,360 @@
1
+ @property --resizer-split {
2
+ initial-value: 0.5;
3
+ inherits: true;
4
+ syntax: "<number>";
5
+ }
6
+
7
+ .bin,
8
+ .bin *,
9
+ .bin *:before,
10
+ .bin *:after {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ .bin {
15
+ height: var(--bin-height);
16
+ width: var(--bin-width);
17
+ max-height: 90vh;
18
+ max-height: 90svh;
19
+
20
+ position: relative;
21
+ container-type: inline-size;
22
+ container-name: bin;
23
+
24
+ --bg: #aaa;
25
+ --resizer-split: 0.5;
26
+ --margin: 2px;
27
+ --code-bg: #282a36;
28
+ --code-highlight: color-mix(in xyz, var(--code-bg), #cceeff 5%);
29
+ --code-text: #f8f8f2;
30
+ --code-comment: #a3b0d9;
31
+ --bin-width: 100%;
32
+ --bin-height: 512px;
33
+ }
34
+
35
+ .bin__widget {
36
+ width: 100%;
37
+ height: 100%;
38
+ display: grid;
39
+ grid:
40
+ "tabs x menu" auto
41
+ "editor resizer preview" 1fr
42
+ "controls controls controls" auto
43
+ "footer footer footer" auto / calc(
44
+ var(--resizer-split) * (100% - var(--margin) * 2)
45
+ )
46
+ calc(var(--margin) * 2)
47
+ 1fr;
48
+ align-items: stretch;
49
+
50
+ margin: 5rem auto;
51
+ height: var(--bin-height);
52
+ width: var(--bin-width);
53
+ background: var(--bg);
54
+ padding: calc(var(--margin) * 2);
55
+ border-radius: 0.3rem;
56
+ box-shadow: 0 0.5rem 1rem #4444;
57
+ font-family: sans-serif;
58
+ transition: 0.1s --resizer-split ease-out;
59
+ }
60
+
61
+ .bin--resizing .bin__widget {
62
+ transition: none;
63
+ }
64
+
65
+ .bin__editor {
66
+ overflow: auto;
67
+ grid-area: editor;
68
+ background: var(--code-bg);
69
+ color: var(--code-text);
70
+ display: none;
71
+ font-size: 13px;
72
+ border-width: 0 1px 1px;
73
+ }
74
+
75
+ .bin__editor--active {
76
+ display: block;
77
+ }
78
+
79
+ .bin__iconbutton {
80
+ padding: 0;
81
+ border: 0;
82
+ background: transparent;
83
+ cursor: pointer;
84
+ border-radius: 0.125rem;
85
+ line-height: 1;
86
+ display: inline-flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ aspect-ratio: 1/1;
90
+ align-self: center;
91
+ }
92
+ .bin__iconbutton:hover {
93
+ background: #ddd;
94
+ box-shadow: 0 0 0 0.25rem #ddd;
95
+ }
96
+
97
+ .bin__editor__expand,
98
+ .bin__preview__expand {
99
+ margin-inline: 0.5rem;
100
+ }
101
+
102
+ .bin__resizer {
103
+ grid-area: resizer;
104
+ cursor: ew-resize;
105
+ position: relative;
106
+ }
107
+ .bin__resizer:after {
108
+ content: "";
109
+ position: absolute;
110
+ display: block;
111
+ top: calc(50% - 0.5rem);
112
+ left: 1px;
113
+ height: 1rem;
114
+ width: 2px;
115
+ background: #444;
116
+ }
117
+
118
+ .bin__menu {
119
+ grid-area: menu;
120
+ font-size: 13px;
121
+ display: flex;
122
+ gap: var(--margin);
123
+ overflow: hidden;
124
+ }
125
+
126
+ .bin__preview {
127
+ width: 100%;
128
+ height: 100%;
129
+ background: #fff;
130
+ grid-area: preview;
131
+ border: 0 none;
132
+ }
133
+ .bin--resizing .bin__preview {
134
+ pointer-events: none;
135
+ }
136
+
137
+ .bin__tabstrip {
138
+ grid-area: tabs;
139
+ display: flex;
140
+ background: var(--bg);
141
+ font-size: 13px;
142
+ overflow: hidden;
143
+ }
144
+ .bin__tabs {
145
+ display: flex;
146
+ gap: var(--margin);
147
+ margin-inline-end: auto;
148
+ }
149
+ .bin__tab {
150
+ border-bottom: var(--margin) solid var(--bg);
151
+ padding: 0.5rem 1rem 0.5rem;
152
+ border-radius: 0.25rem 0.25rem 0 0;
153
+ cursor: pointer;
154
+ display: flex;
155
+ gap: 0.5rem;
156
+ align-items: center;
157
+ justify-content: center;
158
+ }
159
+ .bin__tabstrip .bin__tab:hover {
160
+ text-decoration: underline;
161
+ }
162
+ .bin__editor_tab {
163
+ background: var(--code-bg);
164
+ color: var(--code-comment);
165
+ }
166
+ .bin__tab input {
167
+ width: 0;
168
+ height: 0;
169
+ position: absolute;
170
+ visibility: hidden;
171
+ }
172
+ .bin__tab--active,
173
+ .bin__tab:has(input:checked) {
174
+ border-bottom-color: transparent;
175
+ }
176
+
177
+ .bin__editor_tab:has(input:checked) {
178
+ color: var(--code-text);
179
+ }
180
+ .bin__editor_tab:hover {
181
+ background: var(--code-highlight);
182
+ }
183
+
184
+ .bin__preview_tab,
185
+ .bin__mini_preview_tab {
186
+ background: #fff;
187
+ }
188
+ .bin__tabstrip .bin__preview_tab:hover {
189
+ background: var(--code-text);
190
+ }
191
+
192
+ .bin__mini_preview_tab {
193
+ display: none;
194
+ }
195
+
196
+ .bin__controls {
197
+ grid-area: controls;
198
+ padding: 1rem;
199
+ }
200
+ .bin__controls:empty {
201
+ display: none;
202
+ }
203
+
204
+ .bin--mini-mode {
205
+ .bin__widget {
206
+ grid:
207
+ "tabs" auto
208
+ "editor" 1fr
209
+ "controls" auto
210
+ "footer" auto / 1fr;
211
+ }
212
+ .bin__preview {
213
+ grid-area: editor;
214
+ display: none;
215
+ }
216
+ .bin__preview.bin__editor--active {
217
+ display: block;
218
+ }
219
+ .bin__mini_preview_tab {
220
+ display: flex;
221
+ }
222
+ .bin__preview__expand,
223
+ .bin__editor__expand,
224
+ .bin__resizer,
225
+ .bin__menu {
226
+ display: none;
227
+ }
228
+ }
229
+
230
+ .bin .cm-editor {
231
+ height: 100%;
232
+ text-shadow: 0 1px rgba(0, 0, 0, 0.3);
233
+ }
234
+ .bin .cm-content {
235
+ caret-color: white !important;
236
+ }
237
+ .bin .cm-wrap {
238
+ height: 100%;
239
+ }
240
+ .bin .cm-scroller {
241
+ overflow: auto;
242
+ line-height: 1.5;
243
+ }
244
+ .bin .cm-gutters {
245
+ background: var(--code-bg);
246
+ color: #f8f8f2cc;
247
+ border: 0 none;
248
+ }
249
+ .bin .cm-activeLineGutter {
250
+ background: #cceeff44;
251
+ }
252
+ .bin .cm-selectionBackground {
253
+ background: #cceeff66 !important;
254
+ }
255
+
256
+ /**
257
+ * Dracula Theme originally by Zeno Rocha [@zenorocha]
258
+ * https://draculatheme.com/
259
+ *
260
+ * Ported for PrismJS by Albert Vallverdu [@byverdu]
261
+ * Adapted here for CodeMirror
262
+ */
263
+
264
+ .tok-comment,
265
+ .tok-prolog,
266
+ .tok-doctype,
267
+ .tok-cdata {
268
+ color: var(--code-comment);
269
+ }
270
+
271
+ .tok-punctuation {
272
+ color: #dbdbbd;
273
+ }
274
+
275
+ .namespace {
276
+ opacity: 0.7;
277
+ }
278
+
279
+ .tok-propertyName,
280
+ .tok-constant,
281
+ .tok-symbol,
282
+ .tok-deleted {
283
+ color: #ff79c6;
284
+ }
285
+
286
+ .tok-bool,
287
+ .tok-number,
288
+ .tok-labelName {
289
+ color: #bd93f9;
290
+ }
291
+
292
+ .tok-selector,
293
+ .tok-attr-name,
294
+ .tok-string,
295
+ .tok-char,
296
+ .tok-builtin,
297
+ .tok-tag,
298
+ .tok-typeName,
299
+ .tok-inserted {
300
+ color: #50fa7b;
301
+ }
302
+
303
+ .tok-operator,
304
+ .tok-entity,
305
+ .tok-url,
306
+ .language-css .tok-string,
307
+ .style .tok-string,
308
+ .tok-variableName {
309
+ color: #f8f8f2;
310
+ }
311
+
312
+ .tok-atrule,
313
+ .tok-attr-value,
314
+ .tok-function,
315
+ .tok-literal,
316
+ .tok-className {
317
+ color: #f1fa8c;
318
+ }
319
+
320
+ .tok-keyword {
321
+ color: #8be9fd;
322
+ }
323
+
324
+ .tok-regex,
325
+ .tok-important {
326
+ color: #ffb86c;
327
+ }
328
+
329
+ /*
330
+ .tok-link
331
+ .tok-heading
332
+ .tok-emphasis
333
+ .tok-strong
334
+ .tok-keyword
335
+ .tok-atom
336
+ .tok-bool
337
+ .tok-url
338
+ .tok-labelName
339
+ .tok-inserted
340
+ .tok-deleted
341
+ .tok-literal
342
+ .tok-string
343
+ .tok-number
344
+ .tok-string2
345
+ .tok-variableName
346
+ .tok-variableName.tok-local
347
+ .tok-variableName.tok-definition
348
+ .tok-variableName2
349
+ .tok-propertyName.tok-definition
350
+ .tok-typeName
351
+ .tok-namespace
352
+ .tok-className
353
+ .tok-macroName
354
+ .tok-propertyName
355
+ .tok-operator
356
+ .tok-comment
357
+ .tok-meta
358
+ .tok-invalid
359
+ .tok-punctuation
360
+ */