@scalar/use-codemirror 0.4.0 → 0.5.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/dist/style.css ADDED
@@ -0,0 +1,37 @@
1
+
2
+ /** Basics */
3
+ .scalar-api-client__codemirror__wrapper {
4
+ width: 100%;
5
+ display: flex;
6
+ align-items: stretch;
7
+ }
8
+ .scalar-api-client__codemirror {
9
+ flex-grow: 1;
10
+ max-width: 100%;
11
+
12
+ font-size: var(--theme-small);
13
+ }
14
+
15
+ /* .scalar-api-client__codemirror.ͼw {
16
+ background-color: var(--theme-background-1);
17
+ }
18
+
19
+ .scalar-api-client__codemirror--read-only.ͼw {
20
+ background-color: var(--theme-background-2);
21
+ } */
22
+
23
+ /** URL input */
24
+ .scalar-api-client__url-input {
25
+ font-weight: var(--theme-semibold);
26
+ }
27
+
28
+ /* .scalar-api-client__url-input .cm-scroller {
29
+ padding-left: 6px;
30
+ }
31
+
32
+ .scalar-api-client__url-input .ͼ1 .cm-scroller {
33
+ align-items: center !important;
34
+ } */
35
+ .scalar-api-client__variable {
36
+ color: var(--scalar-api-client-color);
37
+ }
@@ -0,0 +1,3 @@
1
+ export declare const lightTheme: import("@codemirror/state").Extension;
2
+ export declare const darkTheme: import("@codemirror/state").Extension;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/themes/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,uCAqCrB,CAAA;AAEF,eAAO,MAAM,SAAS,uCAkCpB,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,23 @@
1
1
  {
2
2
  "name": "@scalar/use-codemirror",
3
- "version": "0.4.0",
4
- "author": "Scalar",
3
+ "description": "CodeMirror for Vue",
4
+ "keywords": [
5
+ "vue",
6
+ "vue3",
7
+ "composable",
8
+ "codemirror"
9
+ ],
10
+ "version": "0.5.0",
11
+ "author": "Scalar (https://github.com/scalar)",
12
+ "homepage": "https://github.com/scalar/scalar",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/scalar/scalar.git",
16
+ "directory": "packages/use-codemirror"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/scalar/scalar/issues/new"
20
+ },
5
21
  "license": "MIT",
6
22
  "engines": {
7
23
  "node": ">=18"
@@ -18,14 +34,20 @@
18
34
  "import": "./dist/index.js"
19
35
  },
20
36
  "dependencies": {
21
- "@codemirror/lang-javascript": "6.1.9",
37
+ "@codemirror/lang-java": "^6.0.1",
38
+ "@codemirror/lang-javascript": "6.2.1",
22
39
  "@codemirror/lang-json": "6.0.1",
40
+ "@codemirror/lang-python": "6.1.3",
41
+ "@codemirror/language": "6.9.0",
42
+ "@codemirror/legacy-modes": "6.3.3",
23
43
  "@codemirror/state": "6.2.1",
24
- "@codemirror/view": "6.16.0",
44
+ "@codemirror/view": "6.18.0",
25
45
  "@headlessui/vue": "1.7.14",
46
+ "@lezer/common": "1.0.3",
47
+ "@lezer/lr": "1.3.10",
26
48
  "@lezer/highlight": "1.1.6",
27
- "@uiw/codemirror-theme-duotone": "4.21.3",
28
- "@uiw/codemirror-themes": "4.21.3",
49
+ "@uiw/codemirror-theme-duotone": "4.21.13",
50
+ "@uiw/codemirror-themes": "4.21.13",
29
51
  "@vueuse/core": "10.1.2",
30
52
  "axios": "1.4.0",
31
53
  "codemirror": "6.0.1",
@@ -0,0 +1,249 @@
1
+ <script lang="ts" setup>
2
+ import { java } from '@codemirror/lang-java'
3
+ import { javascript } from '@codemirror/lang-javascript'
4
+ import { json } from '@codemirror/lang-json'
5
+ import { python } from '@codemirror/lang-python'
6
+ import { type LanguageSupport, StreamLanguage } from '@codemirror/language'
7
+ import {
8
+ c,
9
+ csharp,
10
+ kotlin,
11
+ objectiveC,
12
+ } from '@codemirror/legacy-modes/mode/clike'
13
+ import { clojure } from '@codemirror/legacy-modes/mode/clojure'
14
+ import { go } from '@codemirror/legacy-modes/mode/go'
15
+ import { http } from '@codemirror/legacy-modes/mode/http'
16
+ import { oCaml } from '@codemirror/legacy-modes/mode/mllike'
17
+ import { powerShell } from '@codemirror/legacy-modes/mode/powershell'
18
+ import { r } from '@codemirror/legacy-modes/mode/r'
19
+ import { ruby } from '@codemirror/legacy-modes/mode/ruby'
20
+ import { shell } from '@codemirror/legacy-modes/mode/shell'
21
+ import { swift } from '@codemirror/legacy-modes/mode/swift'
22
+ import { type Extension } from '@codemirror/state'
23
+ import {
24
+ EditorView,
25
+ type ViewUpdate,
26
+ keymap,
27
+ lineNumbers as lineNumbersExtension,
28
+ } from '@codemirror/view'
29
+ import { watch } from 'vue'
30
+
31
+ import { useCodeMirror } from '../../hooks'
32
+ import { variables } from './extensions/variables'
33
+
34
+ const props = withDefaults(
35
+ defineProps<{
36
+ extensions?: Extension[]
37
+ content?: string
38
+ readOnly?: boolean
39
+ languages?: Language[]
40
+ withVariables?: boolean
41
+ lineNumbers?: boolean
42
+ withoutTheme?: boolean
43
+ disableEnter?: boolean
44
+ forceDarkMode?: boolean
45
+ }>(),
46
+ {
47
+ disableEnter: false,
48
+ forceDarkMode: false,
49
+ },
50
+ )
51
+
52
+ const emit = defineEmits<{
53
+ (e: 'change', value: string): void
54
+ }>()
55
+
56
+ // TODO: Add 'php' and 'laravel'
57
+ const syntaxHighlighting: Partial<
58
+ Record<Language, LanguageSupport | StreamLanguage<any>>
59
+ > = {
60
+ axios: javascript(),
61
+ c: StreamLanguage.define(c),
62
+ clojure: StreamLanguage.define(clojure),
63
+ csharp: StreamLanguage.define(csharp),
64
+ go: StreamLanguage.define(go),
65
+ http: StreamLanguage.define(http),
66
+ java: java(),
67
+ javascript: javascript(),
68
+ json: json(),
69
+ kotlin: StreamLanguage.define(kotlin),
70
+ node: javascript(),
71
+ objc: StreamLanguage.define(objectiveC),
72
+ ocaml: StreamLanguage.define(oCaml),
73
+ powershell: StreamLanguage.define(powerShell),
74
+ python: python(),
75
+ r: StreamLanguage.define(r),
76
+ ruby: StreamLanguage.define(ruby),
77
+ shell: StreamLanguage.define(shell),
78
+ swift: StreamLanguage.define(swift),
79
+ }
80
+
81
+ type Language =
82
+ | 'axios'
83
+ | 'c'
84
+ | 'clojure'
85
+ | 'csharp'
86
+ | 'go'
87
+ | 'http'
88
+ | 'java'
89
+ | 'javascript'
90
+ | 'json'
91
+ | 'kotlin'
92
+ | 'node'
93
+ | 'objc'
94
+ | 'ocaml'
95
+ | 'powershell'
96
+ | 'python'
97
+ | 'r'
98
+ | 'ruby'
99
+ | 'shell'
100
+ | 'swift'
101
+ | 'php'
102
+
103
+ // CSS Class
104
+ const classes = ['scalar-api-client__codemirror']
105
+
106
+ if (props.readOnly) {
107
+ classes.push('scalar-api-client__codemirror--read-only')
108
+ }
109
+
110
+ const getCodeMirrorExtensions = () => {
111
+ const extensions: Extension[] = []
112
+
113
+ extensions.push(EditorView.editorAttributes.of({ class: classes.join(' ') }))
114
+
115
+ // Custom extensions
116
+ if (props.extensions) {
117
+ props.extensions.forEach((extension) => {
118
+ extensions.push(extension)
119
+ })
120
+ }
121
+
122
+ // Read only
123
+ if (props.readOnly) {
124
+ extensions.push(EditorView.editable.of(false))
125
+ }
126
+
127
+ // Syntax highlighting
128
+ if (props.languages) {
129
+ props.languages
130
+ .filter((language) => typeof syntaxHighlighting[language] !== 'undefined')
131
+ .forEach((language) => {
132
+ extensions.push(syntaxHighlighting[language] as Extension)
133
+ })
134
+ }
135
+
136
+ // Line numbers
137
+ if (props.lineNumbers) {
138
+ extensions.push(lineNumbersExtension())
139
+ }
140
+
141
+ // Highlight variables
142
+ if (props.withVariables) {
143
+ extensions.push(variables())
144
+ }
145
+
146
+ if (props.disableEnter) {
147
+ extensions.push(
148
+ keymap.of([
149
+ {
150
+ key: 'Enter',
151
+ run: () => {
152
+ return true
153
+ },
154
+ },
155
+ {
156
+ key: 'Ctrl-Enter',
157
+ mac: 'Cmd-Enter',
158
+ run: () => {
159
+ return true
160
+ },
161
+ },
162
+ {
163
+ key: 'Shift-Enter',
164
+ run: () => {
165
+ return true
166
+ },
167
+ },
168
+ ]),
169
+ )
170
+ }
171
+
172
+ // Listen to updates
173
+ extensions.push(
174
+ EditorView.updateListener.of((v: ViewUpdate) => {
175
+ if (!v.docChanged) {
176
+ return
177
+ }
178
+
179
+ emit('change', v.state.doc.toString())
180
+ }),
181
+ )
182
+
183
+ return extensions
184
+ }
185
+
186
+ const { codeMirrorRef, setCodeMirrorContent, reconfigureCodeMirror } =
187
+ useCodeMirror({
188
+ content: props.content ?? '',
189
+ extensions: getCodeMirrorExtensions(),
190
+ withoutTheme: props.withoutTheme,
191
+ forceDarkMode: props.forceDarkMode,
192
+ })
193
+
194
+ watch(props, () => {
195
+ setCodeMirrorContent(props.content ?? '')
196
+ reconfigureCodeMirror(getCodeMirrorExtensions())
197
+ })
198
+
199
+ defineExpose({
200
+ setCodeMirrorContent,
201
+ })
202
+ </script>
203
+
204
+ <template>
205
+ <div
206
+ ref="codeMirrorRef"
207
+ class="scalar-api-client__codemirror__wrapper" />
208
+ </template>
209
+
210
+ <style>
211
+ /** Basics */
212
+ .scalar-api-client__codemirror__wrapper {
213
+ width: 100%;
214
+ display: flex;
215
+ align-items: stretch;
216
+ }
217
+
218
+ .scalar-api-client__codemirror {
219
+ flex-grow: 1;
220
+ max-width: 100%;
221
+
222
+ font-size: var(--theme-small);
223
+ }
224
+
225
+ /* .scalar-api-client__codemirror.ͼw {
226
+ background-color: var(--theme-background-1);
227
+ }
228
+
229
+ .scalar-api-client__codemirror--read-only.ͼw {
230
+ background-color: var(--theme-background-2);
231
+ } */
232
+
233
+ /** URL input */
234
+ .scalar-api-client__url-input {
235
+ font-weight: var(--theme-semibold);
236
+ }
237
+
238
+ /* .scalar-api-client__url-input .cm-scroller {
239
+ padding-left: 6px;
240
+ }
241
+
242
+ .scalar-api-client__url-input .ͼ1 .cm-scroller {
243
+ align-items: center !important;
244
+ } */
245
+
246
+ .scalar-api-client__variable {
247
+ color: var(--scalar-api-client-color);
248
+ }
249
+ </style>
@@ -0,0 +1,41 @@
1
+ import {
2
+ Decoration,
3
+ type DecorationSet,
4
+ EditorView,
5
+ MatchDecorator,
6
+ ViewPlugin,
7
+ type ViewUpdate,
8
+ } from '@codemirror/view'
9
+
10
+ const variableHighlighterDecoration = new MatchDecorator({
11
+ regexp: /(\{[^}]+\})/g,
12
+ decoration: () =>
13
+ Decoration.mark({
14
+ attributes: {
15
+ class: 'scalar-api-client__variable',
16
+ },
17
+ }),
18
+ })
19
+
20
+ export const variables = () =>
21
+ ViewPlugin.fromClass(
22
+ class {
23
+ variables: DecorationSet
24
+ constructor(view: EditorView) {
25
+ this.variables = variableHighlighterDecoration.createDeco(view)
26
+ }
27
+ update(update: ViewUpdate) {
28
+ this.variables = variableHighlighterDecoration.updateDeco(
29
+ update,
30
+ this.variables,
31
+ )
32
+ }
33
+ },
34
+ {
35
+ decorations: (instance) => instance.variables,
36
+ provide: (plugin) =>
37
+ EditorView.atomicRanges.of(
38
+ (view) => view.plugin(plugin)?.variables || Decoration.none,
39
+ ),
40
+ },
41
+ )
@@ -0,0 +1 @@
1
+ export { default as CodeMirror } from './CodeMirror.vue'
@@ -0,0 +1 @@
1
+ export * from './useCodeMirror'
@@ -1,9 +1,10 @@
1
1
  import { type Extension, StateEffect } from '@codemirror/state'
2
2
  import { type EditorViewConfig } from '@codemirror/view'
3
- import { duotoneDark, duotoneLight } from '@uiw/codemirror-theme-duotone'
4
3
  import { EditorView } from 'codemirror'
5
4
  import { type Ref, ref, watch } from 'vue'
6
5
 
6
+ import { darkTheme, lightTheme } from '../themes'
7
+
7
8
  /** TODO: This is a static value, make it work with a dynamic parameter. */
8
9
  const isDark = ref(false)
9
10
 
@@ -17,9 +18,13 @@ type UseCodeMirrorParameters = {
17
18
  */
18
19
  content?: string
19
20
  /**
20
- * Inverse the dark mode.
21
+ * Force the dark mode.
21
22
  */
22
23
  forceDarkMode?: boolean
24
+ /**
25
+ * Force the light mode.
26
+ */
27
+ forceLightMode?: boolean
23
28
  /**
24
29
  * Whether to load a theme.
25
30
  */
@@ -36,7 +41,8 @@ export const useCodeMirror = (
36
41
  reconfigureCodeMirror: (newExtensions: Extension[]) => void
37
42
  restartCodeMirror: (newExtensions: Extension[]) => void
38
43
  } => {
39
- const { extensions, content, forceDarkMode, withoutTheme } = parameters
44
+ const { extensions, content, forceDarkMode, forceLightMode, withoutTheme } =
45
+ parameters
40
46
  const value = ref(content ?? '')
41
47
  const codeMirrorRef = ref<HTMLDivElement | null>(null)
42
48
  const codeMirror = ref<EditorView | null>(null)
@@ -64,19 +70,32 @@ export const useCodeMirror = (
64
70
  }
65
71
  }
66
72
 
73
+ const getCurrentTheme = () => {
74
+ if (withoutTheme) {
75
+ return null
76
+ }
77
+
78
+ if (forceDarkMode) {
79
+ return darkTheme
80
+ }
81
+
82
+ if (forceLightMode) {
83
+ return lightTheme
84
+ }
85
+
86
+ if (isDark.value) {
87
+ return darkTheme
88
+ }
89
+
90
+ return lightTheme
91
+ }
92
+
67
93
  // Extend the given extension list with a dark/light theme.
68
94
  const addDefaultExtensions = (newExtensions: Extension[] = []) => {
69
- const theme = withoutTheme
70
- ? null
71
- : forceDarkMode
72
- ? duotoneLight
73
- : isDark.value
74
- ? duotoneDark
75
- : duotoneLight
76
95
  // Themes
77
96
  const defaultExtensions: Extension[] = [
78
97
  EditorView.theme({}, { dark: forceDarkMode ? false : isDark.value }),
79
- theme,
98
+ getCurrentTheme(),
80
99
  ].filter((extension) => extension !== null) as Extension[]
81
100
 
82
101
  return [...defaultExtensions, newExtensions]
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
- export * from './useCodeMirror'
1
+ export { CodeMirror } from './components/CodeMirror'
2
+ export * from './hooks/useCodeMirror'
@@ -0,0 +1,77 @@
1
+ import { tags as t } from '@lezer/highlight'
2
+ import { createTheme } from '@uiw/codemirror-themes'
3
+
4
+ export const lightTheme = createTheme({
5
+ theme: 'light',
6
+ settings: {
7
+ background: 'var(--theme-background-2)',
8
+ foreground: 'var(--theme-color-1)',
9
+ caret: '#93abdc',
10
+ selection: '#e3dcce',
11
+ selectionMatch: '#e3dcce',
12
+ gutterBackground: 'var(--theme-background-2)',
13
+ gutterForeground: 'var(--theme-color-2)',
14
+ gutterBorder: 'transparent',
15
+ lineHighlight: '#EFEFEF',
16
+ fontFamily: 'var(--theme-font-code)',
17
+ },
18
+ styles: [
19
+ { tag: [t.standard(t.tagName), t.tagName], color: '#116329' },
20
+ { tag: [t.comment, t.bracket], color: '#6a737d' },
21
+ { tag: [t.className, t.propertyName], color: '#6f42c1' },
22
+ {
23
+ tag: [t.variableName, t.attributeName, t.number, t.operator],
24
+ color: '#005cc5',
25
+ },
26
+ {
27
+ tag: [t.keyword, t.typeName, t.typeOperator, t.typeName],
28
+ color: '#d73a49',
29
+ },
30
+ { tag: [t.string, t.meta, t.regexp], color: '#032f62' },
31
+ { tag: [t.name, t.quote], color: '#22863a' },
32
+ { tag: [t.heading], color: '#24292e', fontWeight: 'bold' },
33
+ { tag: [t.emphasis], color: '#24292e', fontStyle: 'italic' },
34
+ { tag: [t.deleted], color: '#b31d28', backgroundColor: 'ffeef0' },
35
+ { tag: [t.atom, t.bool, t.special(t.variableName)], color: '#e36209' },
36
+ { tag: [t.url, t.escape, t.regexp, t.link], color: '#032f62' },
37
+ { tag: t.link, textDecoration: 'underline' },
38
+ { tag: t.strikethrough, textDecoration: 'line-through' },
39
+ { tag: t.invalid, color: '#cb2431' },
40
+ ],
41
+ })
42
+
43
+ export const darkTheme = createTheme({
44
+ theme: 'dark',
45
+ settings: {
46
+ background: 'var(--theme-background-2)',
47
+ foreground: 'var(--theme-color-1)',
48
+ caret: '#ffad5c',
49
+ selection: 'rgba(255, 255, 255, 0.1)',
50
+ gutterBackground: 'var(--theme-background-2)',
51
+ gutterForeground: 'var(--theme-color-2)',
52
+ lineHighlight: '#36334280',
53
+ fontFamily: 'var(--theme-font-code)',
54
+ },
55
+ styles: [
56
+ { tag: [t.standard(t.tagName), t.tagName], color: '#7ee787' },
57
+ { tag: [t.comment, t.bracket], color: '#8b949e' },
58
+ { tag: [t.className, t.propertyName], color: '#d2a8ff' },
59
+ {
60
+ tag: [t.variableName, t.attributeName, t.number, t.operator],
61
+ color: '#79c0ff',
62
+ },
63
+ {
64
+ tag: [t.keyword, t.typeName, t.typeOperator, t.typeName],
65
+ color: '#ff7b72',
66
+ },
67
+ { tag: [t.string, t.meta, t.regexp], color: '#a5d6ff' },
68
+ { tag: [t.name, t.quote], color: '#7ee787' },
69
+ { tag: [t.heading], color: '#d2a8ff', fontWeight: 'bold' },
70
+ { tag: [t.emphasis], color: '#d2a8ff', fontStyle: 'italic' },
71
+ { tag: [t.deleted], color: '#ffdcd7', backgroundColor: 'ffeef0' },
72
+ { tag: [t.atom, t.bool, t.special(t.variableName)], color: '#ffab70' },
73
+ { tag: t.link, textDecoration: 'underline' },
74
+ { tag: t.strikethrough, textDecoration: 'line-through' },
75
+ { tag: t.invalid, color: '#f97583' },
76
+ ],
77
+ })
@@ -1 +0,0 @@
1
- {"version":3,"file":"useCodeMirror.d.ts","sourceRoot":"","sources":["../src/useCodeMirror.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,mBAAmB,CAAA;AAG/D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,KAAK,GAAG,EAAc,MAAM,KAAK,CAAA;AAK1C,KAAK,uBAAuB,GAAG;IAC7B;;OAEG;IACH,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,aAAa,eACZ,uBAAuB;WAE5B,IAAI,MAAM,CAAC;mBACH,IAAI,cAAc,GAAG,IAAI,CAAC;gBAC7B,IAAI,UAAU,GAAG,IAAI,CAAC;oCACF,MAAM,KAAK,IAAI;2CACR,SAAS,EAAE,KAAK,IAAI;uCACxB,SAAS,EAAE,KAAK,IAAI;CA6HxD,CAAA"}