Skip to content

Commit

Permalink
feat(playground): upgrade to codemirror 6 (#2130)
Browse files Browse the repository at this point in the history
* feat(playground): upgrade to codemirror 6

* fix: interactive editor

* fix: scrolls

* chore: update line highlight color

---------

Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
sibbng and antfu committed Jan 30, 2023
1 parent f1957bf commit 33290b6
Show file tree
Hide file tree
Showing 9 changed files with 606 additions and 212 deletions.
7 changes: 5 additions & 2 deletions package.json
Expand Up @@ -25,6 +25,10 @@
"devDependencies": {
"@antfu/eslint-config": "^0.34.0",
"@antfu/ni": "^0.18.8",
"@codemirror/lang-css": "^6.0.1",
"@codemirror/lang-html": "^6.4.1",
"@codemirror/lang-javascript": "^6.1.2",
"@codemirror/lang-xml": "^6.0.2",
"@iconify-json/carbon": "^1.1.13",
"@iconify-json/iconoir": "^1.1.17",
"@iconify-json/la": "^1.1.3",
Expand All @@ -37,7 +41,6 @@
"@iconify-json/twemoji": "^1.1.9",
"@iconify-json/uim": "^1.1.3",
"@iconify/json": "^2.2.3",
"@types/codemirror": "^5.60.6",
"@types/connect": "^3.4.35",
"@types/css-tree": "^2.0.1",
"@types/fs-extra": "^9.0.13",
Expand Down Expand Up @@ -73,7 +76,7 @@
"@vueuse/math": "^9.10.0",
"brotli-size": "^4.0.0",
"bumpp": "^8.2.1",
"codemirror": "^5.65.9",
"codemirror": "^6.0.1",
"codemirror-theme-vars": "^0.1.1",
"eslint": "8.31.0",
"esno": "^0.16.3",
Expand Down
169 changes: 61 additions & 108 deletions packages/inspector/client/components/CodeMirror.vue
@@ -1,76 +1,46 @@
<script setup lang="ts">
import type { AsyncHintFunction, HintFunction, HintFunctionResolver } from 'codemirror'
import { getMatchedPositions } from '@unocss/shared-common'
import { useCodeMirror } from '../composables/codemirror'
import { Decoration } from '@codemirror/view'
import { useEventListener, useThrottleFn } from '@vueuse/core'
import type { CompletionSource } from '@codemirror/autocomplete'
import { addMarks, filterMarks, useCodeMirror } from '../composables/codemirror'
const props = defineProps<{
modelValue: string
mode?: string
readOnly?: boolean
matched?: Set<string> | string[]
getHint?: HintFunction | AsyncHintFunction | HintFunctionResolver
getHint?: CompletionSource
}>()
const emit = defineEmits<{ (e: 'update:modelValue', payload: string): void }>()
const modeMap: Record<string, any> = {
html: 'htmlmixed',
vue: 'htmlmixed',
svelte: 'htmlmixed',
js: 'javascript',
mjs: 'javascript',
cjs: 'javascript',
ts: { name: 'javascript', typescript: true },
mts: { name: 'javascript', typescript: true },
cts: { name: 'javascript', typescript: true },
jsx: { name: 'javascript', jsx: true },
tsx: { name: 'javascript', typescript: true, jsx: true },
}
const el = ref<HTMLTextAreaElement>()
const el = ref<HTMLElement>()
const input = useVModel(props, 'modelValue', emit, { passive: true })
const mode = computed(() => modeMap[props.mode || ''] || props.mode)
const extraKeys = computed(() => (props.getHint
? {
'Ctrl-Space': 'autocomplete',
'Ctrl-.': 'autocomplete',
'Cmd-Space': 'autocomplete',
'Cmd-.': 'autocomplete',
'Tab': 'autocomplete',
}
: {}) as CodeMirror.KeyMap)
const hintOptions = computed(() => props.getHint ? { hint: props.getHint } : {})
onMounted(async () => {
const cm = useCodeMirror(el, input, reactive({
autocomplete: props.getHint,
...toRefs(props),
mode,
hintOptions,
extraKeys,
}))
cm.setSize('100%', '100%')
if (props.getHint) {
cm.on('keyup', (editor, event) => {
if (event.key.match(/^[\w:-]$/))
editor.execCommand('autocomplete')
})
}
setTimeout(() => cm.refresh(), 100)
const decorations: CodeMirror.TextMarker<CodeMirror.MarkerRange>[] = []
useEventListener(cm.contentDOM.parentElement, 'scroll', useThrottleFn(() => {
cm.requestMeasure()
}, 50, true))
function mark(start: number, end: number) {
decorations.push(cm.markText(
cm.posFromIndex(start),
cm.posFromIndex(end),
{ className: 'highlighted' },
))
const strikeMark = Decoration.mark({
class: 'highlighted',
})
cm.dispatch({
effects: addMarks.of([strikeMark.range(start, end)]),
})
}
function highlight() {
// clear previous
decorations.forEach(i => i.clear())
cm.dispatch({
effects: filterMarks.of((from: number, to: number) => to <= 0 || from >= cm.state.doc.toString().length),
})
getMatchedPositions(props.modelValue, Array.from(props.matched || []), true)
.forEach(i => mark(i[0], i[1]))
}
Expand All @@ -86,23 +56,32 @@ onMounted(async () => {

<template>
<div
ref="el"
relative
font-mono
text-sm
>
<textarea ref="el" />
</div>
data-enable-grammarly="false"
h-full
/>
</template>

<style>
.CodeMirror {
#gtx-trans,
grammarly-extension,
deepl-inline-translate,
grammarly-popups,
deepl-inline-popup,
grammarly-desktop-integration {
display: none!important;
}
.cm-editor {
height: 100% !important;
width: 100% !important;
font-family: inherit;
}
.cm-s-vars .cm-tag {
color: var(--cm-keyword);
.cm-content {
cursor: text !important;
}
:root {
Expand All @@ -111,21 +90,16 @@ onMounted(async () => {
--cm-background: #fdfdfd;
--cm-comment: #a0ada0;
--cm-string: #b56959;
--cm-literal: #2f8a89;
--cm-number: #296aa3;
--cm-variable: #59873a;
--cm-keyword: #1c6b48;
--cm-function: #6c7834;
--cm-boolean: #1c6b48;
--cm-constant: #a65e2b;
--cm-deleted: #a14f55;
--cm-class: #2993a3;
--cm-builtin: #ab5959;
--cm-property: #b58451;
--cm-namespace: #b05a78;
--cm-definition-keyword: #ab5959;
--cm-punctuation: #8e8f8b;
--cm-decorator: #bd8f8f;
--cm-regex: #ab5e3f;
--cm-json-property: #698c96;
--cm-decorator: #b07d48;
--cm-line-highlight-background: #c9c9c910;
--cm-line-highlight-border: #b0b0b030;
--cm-selection-background: #eeeeee;
/* scrollbars colors */
--cm-ttc-c-thumb: #eee;
--cm-ttc-c-track: white;
Expand All @@ -134,42 +108,35 @@ onMounted(async () => {
html.dark {
--cm-scheme: dark;
--cm-foreground: #d4cfbf80;
--cm-background: #111;
--cm-background: #121212;
--cm-comment: #758575;
--cm-string: #d48372;
--cm-literal: #429988;
--cm-keyword: #4d9375;
--cm-boolean: #1c6b48;
--cm-number: #6394bf;
--cm-variable: #c2b36e;
--cm-function: #a1b567;
--cm-deleted: #a14f55;
--cm-class: #54b1bf;
--cm-builtin: #e0a569;
--cm-property: #dd8e6e;
--cm-namespace: #db889a;
--cm-definition-keyword: #cb7676;
--cm-punctuation: #858585;
--cm-decorator: #bd8f8f;
--cm-regex: #ab5e3f;
--cm-json-property: #6b8b9e;
--cm-line-number: #888888;
--cm-decorator: #bd976a;
--cm-line-number: #dedcd530;
--cm-line-number-gutter: #eeeeee;
--cm-line-highlight-background: #444444;
--cm-selection-background: #44444450;
--cm-line-highlight-background: #4d4d4d29;
--cm-line-highlight-border: #3a3a3a80;
--cm-selection-background: #242424;
/* scrollbars colors */
--cm-ttc-c-thumb: #222;
--cm-ttc-c-track: #111;
}
.highlighted {
.highlighted, .highlighted > span {
border-bottom: 1px dashed currentColor;
}
.CodeMirror-scroll::-webkit-scrollbar,
.scrolls::-webkit-scrollbar {
.cm-scroller::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.full-scrolls .CodeMirror .CodeMirror-scroll,
.full-scrolls .cm-scroller,
.scrolls {
overflow: auto !important;
height: calc(100vh - 2px) !important;
Expand All @@ -179,42 +146,28 @@ html.dark {
.scrolls-sidebar {
height: calc(100vh - 25px - 1.5rem - 65px - 1rem - 2px) !important;
}
.overview-scrolls .CodeMirror .CodeMirror-scroll {
.overview-scrolls .cm-scroller {
--use-overview-scrolls: var(--overview-scrolls, calc(100vh - 116px - 1rem - 61px - 1rem - 2px));
height: var(--use-overview-scrolls) !important;
}
.module-scrolls .CodeMirror .CodeMirror-scroll {
.module-scrolls .cm-scroller {
--use-module-scrolls: var(--module-scrolls, calc(100vh - 41px - 2.5rem));
height: var(--use-module-scrolls) !important;
}
.rpel-scrolls .CodeMirror .CodeMirror-scroll {
--use-rpel-scrolls: var(--rpel-scrolls, calc(100vh - 41px - 2.5rem));
height: var(--use-rpel-scrolls) !important;
.repl-scrolls .cm-scroller {
--use-repl-scrolls: var(--repl-scrolls, calc(100vh - 41px - 2.5rem));
height: var(--use-repl-scrolls) !important;
}
.CodeMirror-scroll::-webkit-scrollbar-track,
.scrolls::-webkit-scrollbar-track {
.cm-scroller::-webkit-scrollbar-track {
background: var(--cm-ttc-c-track);
}
.CodeMirror-scroll::-webkit-scrollbar-thumb,
.scrolls::-webkit-scrollbar-thumb {
.cm-scroller::-webkit-scrollbar-thumb {
background-color: var(--cm-ttc-c-thumb);
border-radius: 3px;
border: 2px solid var(--cm-ttc-c-thumb);
}
.CodeMirror-scroll::-webkit-scrollbar-corner,
.scrolls::-webkit-scrollbar-corner {
.cm-scroller::-webkit-scrollbar-corner {
background-color: var(--cm-ttc-c-track);
}
.CodeMirror {
overflow: unset !important;
}
.CodeMirror-vscrollbar,
.CodeMirror-hscrollbar {
display: none !important;
}
.CodeMirror-scroll {
margin-bottom: unset !important;
margin-right: unset !important;
padding-bottom: unset !important;
}
</style>
2 changes: 1 addition & 1 deletion packages/inspector/client/components/Overview.vue
Expand Up @@ -97,7 +97,7 @@ const formatted = useCSSPrettify(computed(() => overview.value?.css), isPrettify
:model-value="formatted"
:read-only="true"
mode="css"
class="scrolls overview-scrolls"
class="overview-scrolls"
:style="style"
/>
</div>
Expand Down
36 changes: 19 additions & 17 deletions packages/inspector/client/components/ReplPlayground.vue
Expand Up @@ -3,7 +3,7 @@ import { fetchRepl } from '../composables/fetch'
import { useScrollStyle } from '../composables/useScrollStyle'
const status = ref(null)
const style = useScrollStyle(status, 'rpel-scrolls')
const style = useScrollStyle(status, 'repl-scrolls')
const input = useLocalStorage(
'unocss:inspector:repl',
Expand All @@ -16,26 +16,28 @@ const { data: result } = fetchRepl(input, isSafelistIncluded)

<template>
<div h-full grid="~ rows-[max-content_1fr]" of-hidden>
<StatusBar ref="status">
<div>
REPL Playground
</div>
<div op60>
Edit your code below to test and play UnoCSS's matching and generating.
</div>
</StatusBar>
<TitleBar border="b gray-400/20" title="">
<label>
<input v-model="isSafelistIncluded" type="checkbox">
Include safelist
</label>
</TitleBar>
<div ref="status">
<StatusBar>
<div>
REPL Playground
</div>
<div op60>
Edit your code below to test and play UnoCSS's matching and generating.
</div>
</StatusBar>
<TitleBar border="b gray-400/20" title="">
<label>
<input v-model="isSafelistIncluded" type="checkbox">
Include safelist
</label>
</TitleBar>
</div>
<div h-full of-hidden grid grid-cols-2>
<CodeMirror
v-model="input"
mode="html"
:matched="result?.matched || []"
class="scrolls rpel-scrolls"
class="scrolls repl-scrolls"
:style="style"
/>
<CodeMirror
Expand All @@ -44,7 +46,7 @@ const { data: result } = fetchRepl(input, isSafelistIncluded)
:model-value="result?.css || '/* empty */'"
:read-only="true"
mode="css"
class="scrolls rpel-scrolls"
class="scrolls repl-scrolls"
:style="style"
/>
</div>
Expand Down

0 comments on commit 33290b6

Please sign in to comment.