@c2n/code-editor 0.0.10

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Nguyen Thai Vinh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @c2n/code-editor
2
+
3
+ Source-code field built with Lit on **CodeMirror 6**, which is an _optional_ peer dependency: nothing is imported until an editor mounts, and without it the element degrades to a plain `<textarea>` with the same value, events and form behaviour.
4
+
5
+ ```bash
6
+ npm install @c2n/code-editor
7
+ # the engine, installed alongside — only the grammars you need
8
+ npm install @codemirror/state @codemirror/view @codemirror/commands @codemirror/language \
9
+ @codemirror/autocomplete @codemirror/search @lezer/highlight @codemirror/lang-javascript
10
+ ```
11
+
12
+ ```html
13
+ <script type="module">
14
+ import '@c2n/code-editor'
15
+ </script>
16
+
17
+ <c2-code-editor label="Handler" language="javascript" line-numbers autocomplete value="export const answer = 42"></c2-code-editor>
18
+ ```
19
+
20
+ - **Value**: `value` is the document; assigning it replaces the text without losing scroll position or undo history, and is silent — only a user edit fires `input`, with `change` on the blur that follows, as a native control does.
21
+ - **Forms**: form-associated. `name` and `value` are submitted, `required` sets `valueMissing`, `checkValidity()` / `setCustomValidity()` work, and reset restores the `value` **attribute** — the same `defaultValue` contract a native control has.
22
+ - **Languages**: `javascript`, `typescript`, `jsx`, `tsx`, `html`, `css` and `json` have built-in loaders, each its own dynamic import. Anything else comes from the `languageLoader` property, so adding Python or SQL costs this package no dependency. An empty `language` is a plain editor with no highlighting.
23
+ - **Behaviour**: `line-numbers`, `wrap`, `autocomplete`, `tab-size`, `placeholder`, `readonly` and `disabled` are all live — each is its own CodeMirror `Compartment`, so toggling one never rebuilds the document.
24
+ - **Engine**: `ready` resolves once the engine settles and `engine` reads `codemirror` or `basic`; the `ready` event carries the same. The fallback textarea is not a second editor — it has no highlighting, no gutter and no bracket matching.
25
+ - **Events**: the editor's `contenteditable` fires native composed `input` / `beforeinput`, which are stopped at the component boundary so a consumer sees exactly one `input` per edit and none for an edit `readonly` rejected.
26
+ - **Accessibility**: the editable element carries `role="textbox"` with the label as its accessible name, plus `aria-readonly` and `aria-disabled`. The default token palette meets WCAG AA on both the surface and the active-line tint.
27
+
28
+ **Theming is plain CSS.** CodeMirror renders real DOM inside the shadow root, so there is no JavaScript theme: the frame, gutter, selection, cursor and the whole syntax palette are `--c2-code-editor__*` custom properties, and the token names (`--c2-code-editor__theme--token-keyword`, `…-string`, `…-comment`, …) match `@c2n/code-viewer`'s `css-variables` theme so one palette can drive the viewer and the editor. Dark mode is different variable values and nothing else:
29
+
30
+ ```css
31
+ .midnight {
32
+ --c2-code-editor--background: #0d1117;
33
+ --c2-code-editor--color: #c9d1d9;
34
+ --c2-code-editor--border: 1px solid #30363d;
35
+ --c2-code-editor__theme--token-keyword: #ff7b72;
36
+ --c2-code-editor__theme--token-string: #a5d6ff;
37
+ --c2-code-editor__theme--token-comment: #8b949e;
38
+ }
39
+ ```
40
+
41
+ The full list is in `custom-elements.json` and on the docs site.
@@ -0,0 +1,314 @@
1
+ import { BUILT_IN_LANGUAGES, createEditor } from "./engine.js";
2
+ import { LitElement, html, nothing, unsafeCSS } from "lit";
3
+ import { isServer } from "lit-html/is-server.js";
4
+ import { property, query, state } from "lit/decorators.js";
5
+ import { classMap } from "lit/directives/class-map.js";
6
+ import { ifDefined } from "lit/directives/if-defined.js";
7
+ import { live } from "lit/directives/live.js";
8
+ import { customElement } from "@c2n/core/element-helper.js";
9
+ //#region src/code-editor.scss?inline
10
+ var code_editor_default = "@charset \"UTF-8\";\n/* ex : var((width: 24px), width, c2-checkbox) returns var(--c2-checkbox-width, 24px) */\n:host {\n display: block;\n}\n\n:host([hidden]) {\n display: none;\n}\n\n.c2-code-editor {\n display: flex;\n flex-direction: column;\n gap: var(--c2-code-editor--gap,6px);\n}\n\n.c2-code-editor__label {\n color: var(--c2-code-editor__label--color,#18181b);\n font-size: var(--c2-code-editor__label--font-size,12px);\n font-weight: var(--c2-code-editor__label--font-weight,500);\n}\n.c2-code-editor__label[hidden] {\n display: none;\n}\n\n.c2-code-editor__surface,\n.c2-code-editor__fallback,\n.c2-code-editor__pending {\n box-sizing: border-box;\n width: 100%;\n min-height: var(--c2-code-editor--min-height,120px);\n max-height: var(--c2-code-editor--max-height,420px);\n margin: 0;\n overflow: auto;\n background: var(--c2-code-editor--background,#ffffff);\n color: var(--c2-code-editor--color,#24292e);\n border: var(--c2-code-editor--border,1px solid #bcbcc6);\n border-radius: var(--c2-code-editor--border-radius,6px);\n box-shadow: var(--c2-code-editor--box-shadow);\n font-family: var(--c2-code-editor--font-family,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);\n font-size: var(--c2-code-editor--font-size,13px);\n line-height: var(--c2-code-editor--line-height,1.6);\n}\n.c2-code-editor__surface[hidden],\n.c2-code-editor__fallback[hidden],\n.c2-code-editor__pending[hidden] {\n display: none;\n}\n\n.c2-code-editor__fallback,\n.c2-code-editor__pending {\n padding: var(--c2-code-editor--padding-block,10px) var(--c2-code-editor--padding-inline,12px);\n white-space: pre;\n tab-size: 2;\n}\n\n.c2-code-editor__fallback {\n display: block;\n resize: vertical;\n}\n.c2-code-editor__fallback:focus {\n border: var(--c2-code-editor__focus--border,var(--c2-code-editor--border,1px solid rgb(2, 101, 220)));\n outline: var(--c2-code-editor__focus--outline,2px solid rgba(2, 101, 220, 0.4));\n outline-offset: -1px;\n}\n.c2-code-editor__fallback::placeholder {\n color: var(--c2-code-editor__placeholder--color,#71717a);\n}\n\n.c2-code-editor__pending {\n color: var(--c2-code-editor__placeholder--color,#71717a);\n}\n\n.c2-code-editor.is-focused .c2-code-editor__surface {\n border: var(--c2-code-editor__focus--border,var(--c2-code-editor--border,1px solid rgb(2, 101, 220)));\n outline: var(--c2-code-editor__focus--outline,2px solid rgba(2, 101, 220, 0.4));\n outline-offset: -1px;\n}\n\n.c2-code-editor.is-invalid .c2-code-editor__surface,\n.c2-code-editor.is-invalid .c2-code-editor__fallback {\n border: var(--c2-code-editor__error--border,var(--c2-code-editor--border,1px solid #dc2626));\n}\n\n.c2-code-editor.is-disabled {\n opacity: var(--c2-code-editor__disabled--opacity,0.38);\n}\n.c2-code-editor.is-disabled .c2-code-editor__surface {\n cursor: default;\n}\n\n/**\n * CodeMirror's own DOM.\n *\n * Its base styles are injected as a `<style>` element into this shadow root, and Lit's `static styles` are an\n * adopted stylesheet — adopted sheets come last in the shadow root's cascade, so these rules win on equal\n * specificity without `!important`, and the extra `.c2-code-editor__surface` ancestor settles the rest. Only the\n * class names CodeMirror documents as stable are used.\n */\n.c2-code-editor__surface .cm-editor {\n height: 100%;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n}\n.c2-code-editor__surface .cm-editor.cm-focused {\n outline: none;\n}\n.c2-code-editor__surface .cm-scroller {\n font-family: inherit;\n line-height: inherit;\n overflow: auto;\n}\n.c2-code-editor__surface .cm-content {\n padding: var(--c2-code-editor--padding-block,10px) 0;\n caret-color: var(--c2-code-editor__cursor--color,#18181b);\n}\n.c2-code-editor__surface .cm-line {\n padding: 0 var(--c2-code-editor--padding-inline,12px);\n}\n.c2-code-editor__surface .cm-gutters {\n min-width: var(--c2-code-editor__gutter--min-width,32px);\n background: var(--c2-code-editor__gutter--background,transparent);\n color: var(--c2-code-editor__gutter--color,#71717a);\n border-right: var(--c2-code-editor__gutter--border-right,1px solid #e4e4e7);\n}\n.c2-code-editor__surface .cm-activeLineGutter {\n background: transparent;\n color: var(--c2-code-editor__gutter__active--color,#18181b);\n}\n.c2-code-editor__surface .cm-activeLine {\n background: var(--c2-code-editor__active-line--background,rgba(2, 101, 220, 0.04));\n}\n.c2-code-editor__surface .cm-selectionBackground,\n.c2-code-editor__surface .cm-content ::selection,\n.c2-code-editor__surface .cm-editor.cm-focused .cm-selectionBackground {\n background: var(--c2-code-editor__selection--background,rgba(2, 101, 220, 0.18));\n}\n.c2-code-editor__surface .cm-cursor,\n.c2-code-editor__surface .cm-dropCursor {\n border-left-color: var(--c2-code-editor__cursor--color,#18181b);\n}\n.c2-code-editor__surface .cm-matchingBracket,\n.c2-code-editor__surface .cm-editor.cm-focused .cm-matchingBracket {\n background: var(--c2-code-editor__matching-bracket--background,rgba(2, 101, 220, 0.16));\n outline: none;\n}\n.c2-code-editor__surface .cm-nonmatchingBracket {\n color: var(--c2-code-editor__error--color,#dc2626);\n}\n.c2-code-editor__surface .cm-placeholder {\n color: var(--c2-code-editor__placeholder--color,#71717a);\n}\n.c2-code-editor__surface .cm-tooltip {\n background: var(--c2-code-editor--background,#ffffff);\n color: var(--c2-code-editor--color,#24292e);\n border: var(--c2-code-editor--border,1px solid #bcbcc6);\n border-radius: var(--c2-code-editor--border-radius,6px);\n}\n.c2-code-editor__surface .cm-tooltip-autocomplete ul li[aria-selected] {\n background: var(--c2-code-editor__selection--background,rgba(2, 101, 220, 0.18));\n color: var(--c2-code-editor--color,#24292e);\n}\n\n/**\n * The syntax palette. `engine.ts` maps Lezer tags to these classes instead of to inline styles, which is what keeps\n * the whole theme in CSS: dark mode is different variable values and no JavaScript. The names match\n * `--c2-code-viewer__theme--token-*` so one palette can drive the viewer and the editor.\n */\n.c2-code-editor__surface .c2tok-keyword {\n color: var(--c2-code-editor__theme--token-keyword,#cf222e);\n}\n.c2-code-editor__surface .c2tok-string {\n color: var(--c2-code-editor__theme--token-string,#032f62);\n}\n.c2-code-editor__surface .c2tok-comment {\n color: var(--c2-code-editor__theme--token-comment,#636c76);\n font-style: italic;\n}\n.c2-code-editor__surface .c2tok-constant {\n color: var(--c2-code-editor__theme--token-constant,#0550ae);\n}\n.c2-code-editor__surface .c2tok-function {\n color: var(--c2-code-editor__theme--token-function,#6f42c1);\n}\n.c2-code-editor__surface .c2tok-type {\n color: var(--c2-code-editor__theme--token-type,#953800);\n}\n.c2-code-editor__surface .c2tok-property {\n color: var(--c2-code-editor__theme--token-property,#0550ae);\n}\n.c2-code-editor__surface .c2tok-variable {\n color: var(--c2-code-editor__theme--token-variable,#24292e);\n}\n.c2-code-editor__surface .c2tok-tag {\n color: var(--c2-code-editor__theme--token-tag,#116329);\n}\n.c2-code-editor__surface .c2tok-punctuation {\n color: var(--c2-code-editor__theme--token-punctuation,#24292e);\n}\n.c2-code-editor__surface .c2tok-link {\n color: var(--c2-code-editor__theme--token-link,#032f62);\n text-decoration: underline;\n}\n.c2-code-editor__surface .c2tok-invalid {\n color: var(--c2-code-editor__theme--token-invalid,#cf222e);\n}\n.c2-code-editor__surface .c2tok-strong {\n font-weight: 600;\n}\n.c2-code-editor__surface .c2tok-emphasis {\n font-style: italic;\n}\n\n.c2-code-editor__supporting-text {\n color: var(--c2-code-editor__supporting-text--color,#71717a);\n font-size: var(--c2-code-editor__supporting-text--font-size,12px);\n}\n.c2-code-editor__supporting-text[hidden] {\n display: none;\n}\n\n.c2-code-editor.is-invalid .c2-code-editor__supporting-text {\n color: var(--c2-code-editor__error--color,#dc2626);\n}";
11
+ //#endregion
12
+ //#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorate.js
13
+ function __decorate(decorators, target, key, desc) {
14
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
15
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
16
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
17
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
18
+ }
19
+ //#endregion
20
+ //#region src/code-editor.ts
21
+ var CodeEditor = class CodeEditor extends LitElement {
22
+ constructor(..._args) {
23
+ super(..._args);
24
+ this.internals = this.attachInternals();
25
+ this.value = "";
26
+ this.language = "";
27
+ this.languageLoader = void 0;
28
+ this.name = "";
29
+ this.label = "";
30
+ this.ariaLabel = null;
31
+ this.placeholder = "";
32
+ this.readOnly = false;
33
+ this.disabled = false;
34
+ this.required = false;
35
+ this.lineNumbers = false;
36
+ this.wrap = false;
37
+ this.autocomplete = false;
38
+ this.tabSize = 2;
39
+ this.error = false;
40
+ this.errorText = "";
41
+ this.help = "";
42
+ this.engineState = "pending";
43
+ this.focused = false;
44
+ this.customValidityMessage = "";
45
+ this.disabledByForm = false;
46
+ this.dirty = false;
47
+ this.hasLabelSlot = false;
48
+ }
49
+ static {
50
+ this.formAssociated = true;
51
+ }
52
+ static {
53
+ this.styles = unsafeCSS(code_editor_default);
54
+ }
55
+ /** Resolves once the engine has settled, so a host (or a test) can await the editor being live. */
56
+ get ready() {
57
+ return (this.mounting ?? Promise.resolve()).then(() => this.engineState);
58
+ }
59
+ /** The engine in use, or `undefined` while it is still loading. */
60
+ get engine() {
61
+ return this.engineState === "pending" ? void 0 : this.engineState === "ready" ? "codemirror" : "basic";
62
+ }
63
+ get form() {
64
+ return this.internals.form;
65
+ }
66
+ get labels() {
67
+ return this.internals.labels;
68
+ }
69
+ get validity() {
70
+ return this.internals.validity;
71
+ }
72
+ get validationMessage() {
73
+ return this.internals.validationMessage;
74
+ }
75
+ get willValidate() {
76
+ return this.internals.willValidate;
77
+ }
78
+ get effectiveDisabled() {
79
+ return this.disabled || this.disabledByForm;
80
+ }
81
+ /** What labels the editable element. CodeMirror's content node is the `textbox`, so the name has to land there. */
82
+ get accessibleName() {
83
+ return this.label || this.ariaLabel || "Code editor";
84
+ }
85
+ disconnectedCallback() {
86
+ super.disconnectedCallback();
87
+ this.editor?.destroy();
88
+ this.editor = void 0;
89
+ if (this.engineState === "ready") this.engineState = "pending";
90
+ }
91
+ /** The `value` *attribute* is the default, exactly as `defaultValue` is on a native control. */
92
+ formResetCallback() {
93
+ this.value = this.getAttribute("value") ?? "";
94
+ }
95
+ formDisabledCallback(disabled) {
96
+ this.disabledByForm = disabled && !this.hasAttribute("disabled");
97
+ }
98
+ formStateRestoreCallback(state) {
99
+ if (typeof state === "string") this.value = state;
100
+ }
101
+ checkValidity() {
102
+ return this.internals.checkValidity();
103
+ }
104
+ reportValidity() {
105
+ return this.internals.reportValidity();
106
+ }
107
+ setCustomValidity(message) {
108
+ this.customValidityMessage = message;
109
+ this.requestUpdate();
110
+ }
111
+ focus(options) {
112
+ if (this.editor) this.editor.focus();
113
+ else this.fallback?.focus(options);
114
+ }
115
+ firstUpdated() {
116
+ if (isServer) return;
117
+ this.mounting = this.mount();
118
+ }
119
+ async mount() {
120
+ const editor = await createEditor({
121
+ parent: this.surface,
122
+ root: this.renderRoot,
123
+ value: this.value,
124
+ language: this.language,
125
+ languageLoader: this.languageLoader,
126
+ readOnly: this.readOnly,
127
+ editable: !this.effectiveDisabled,
128
+ lineNumbers: this.lineNumbers,
129
+ lineWrapping: this.wrap,
130
+ autocomplete: this.autocomplete,
131
+ tabSize: this.tabSize,
132
+ placeholder: this.placeholder,
133
+ label: this.accessibleName,
134
+ onInput: (value) => this.handleEditorInput(value),
135
+ onBlur: () => this.handleBlur(),
136
+ onFocus: () => this.focused = true
137
+ });
138
+ if (!this.isConnected) {
139
+ editor?.destroy();
140
+ return;
141
+ }
142
+ this.editor = editor;
143
+ this.engineState = editor ? "ready" : "basic";
144
+ this.dispatchEvent(new CustomEvent("ready", { detail: { engine: this.engineState === "ready" ? "codemirror" : "basic" } }));
145
+ }
146
+ updated(changed) {
147
+ const editor = this.editor;
148
+ if (editor) {
149
+ if (changed.has("value")) editor.setValue(this.value);
150
+ if (changed.has("language") || changed.has("languageLoader")) editor.setLanguage(this.language, this.languageLoader);
151
+ if (changed.has("readOnly") || changed.has("disabled") || changed.has("disabledByForm")) editor.setEditing(this.readOnly, !this.effectiveDisabled);
152
+ if (changed.has("lineNumbers")) editor.setLineNumbers(this.lineNumbers);
153
+ if (changed.has("wrap")) editor.setLineWrapping(this.wrap);
154
+ if (changed.has("autocomplete")) editor.setAutocomplete(this.autocomplete);
155
+ if (changed.has("tabSize")) editor.setTabSize(this.tabSize);
156
+ if (changed.has("placeholder")) editor.setPlaceholder(this.placeholder);
157
+ if (changed.has("label") || changed.has("ariaLabel")) editor.setLabel(this.accessibleName);
158
+ }
159
+ this.internals.setFormValue(this.value, this.value);
160
+ const missing = this.required && !this.value;
161
+ const flags = this.customValidityMessage ? { customError: true } : missing ? { valueMissing: true } : {};
162
+ const message = this.customValidityMessage || (missing ? "Please enter some code." : "");
163
+ this.internals.setValidity(flags, message, this.surface);
164
+ }
165
+ /**
166
+ * CodeMirror edits a `contenteditable`, whose native `input` / `beforeinput` are composed and would escape the
167
+ * shadow root — a consumer would see one of those *and* the component's own `input` for the same keystroke, and
168
+ * would see one even for an edit `readonly` rejected. The component owns its event surface, so they stop here.
169
+ */
170
+ stopNativeEditing(event) {
171
+ event.stopPropagation();
172
+ }
173
+ handleEditorInput(value) {
174
+ if (value === this.value) return;
175
+ this.value = value;
176
+ this.dirty = true;
177
+ this.dispatchEvent(new Event("input", {
178
+ bubbles: true,
179
+ composed: true
180
+ }));
181
+ }
182
+ handleBlur() {
183
+ this.focused = false;
184
+ if (!this.dirty) return;
185
+ this.dirty = false;
186
+ this.dispatchEvent(new Event("change", {
187
+ bubbles: true,
188
+ composed: true
189
+ }));
190
+ }
191
+ /** The fallback textarea fires native `input` / `change`; only the value has to be mirrored. */
192
+ handleFallbackInput(event) {
193
+ this.value = event.target.value;
194
+ this.dispatchEvent(new Event("input", {
195
+ bubbles: true,
196
+ composed: true
197
+ }));
198
+ }
199
+ handleFallbackChange() {
200
+ this.dispatchEvent(new Event("change", {
201
+ bubbles: true,
202
+ composed: true
203
+ }));
204
+ }
205
+ renderFallback() {
206
+ return html`<textarea
207
+ class="c2-code-editor__fallback"
208
+ part="editor"
209
+ name=${ifDefined(this.name || void 0)}
210
+ aria-label=${this.accessibleName}
211
+ placeholder=${ifDefined(this.placeholder || void 0)}
212
+ spellcheck="false"
213
+ autocapitalize="off"
214
+ autocorrect="off"
215
+ ?disabled=${this.effectiveDisabled}
216
+ ?readonly=${this.readOnly}
217
+ .value=${live(this.value)}
218
+ @input=${this.handleFallbackInput}
219
+ @change=${this.handleFallbackChange}
220
+ @focus=${() => this.focused = true}
221
+ @blur=${() => this.focused = false}
222
+ ></textarea>`;
223
+ }
224
+ render() {
225
+ const invalid = this.error && !this.effectiveDisabled;
226
+ const showError = invalid && !!this.errorText;
227
+ const supporting = !!this.help || showError;
228
+ return html`
229
+ <div
230
+ class=${classMap({
231
+ "c2-code-editor": true,
232
+ "is-focused": this.focused,
233
+ "is-invalid": invalid,
234
+ "is-disabled": this.effectiveDisabled,
235
+ [`is-${this.engineState}`]: true
236
+ })}
237
+ part="container"
238
+ >
239
+ <label class="c2-code-editor__label" part="label" ?hidden=${!this.label && !this.hasLabelSlot}>
240
+ <slot name="label" @slotchange=${this.handleLabelSlotChange}>${this.label}</slot>
241
+ </label>
242
+ <!-- Always in the template, so switching engine state never recreates the node CodeMirror owns. -->
243
+ <div
244
+ class="c2-code-editor__surface"
245
+ part=${this.engineState === "ready" ? "editor" : nothing}
246
+ ?hidden=${this.engineState !== "ready"}
247
+ @beforeinput=${this.stopNativeEditing}
248
+ @input=${this.stopNativeEditing}
249
+ ></div>
250
+ ${this.engineState === "basic" ? this.renderFallback() : nothing}
251
+ ${this.engineState === "pending" ? html`<pre class="c2-code-editor__pending" aria-hidden="true">${this.value}</pre>` : nothing}
252
+ <div class="c2-code-editor__supporting-text" part="supporting-text" ?hidden=${!supporting}>
253
+ ${showError ? html`<span role="alert">${this.errorText}</span>` : html`<slot name="supporting-text">${this.help}</slot>`}
254
+ </div>
255
+ </div>
256
+ `;
257
+ }
258
+ handleLabelSlotChange(event) {
259
+ const slot = event.target;
260
+ this.hasLabelSlot = slot.assignedNodes({ flatten: true }).some((node) => node.nodeType === Node.ELEMENT_NODE || (node.textContent ?? "").trim() !== "");
261
+ }
262
+ };
263
+ __decorate([property()], CodeEditor.prototype, "value", void 0);
264
+ __decorate([property({ reflect: true })], CodeEditor.prototype, "language", void 0);
265
+ __decorate([property({ attribute: false })], CodeEditor.prototype, "languageLoader", void 0);
266
+ __decorate([property()], CodeEditor.prototype, "name", void 0);
267
+ __decorate([property()], CodeEditor.prototype, "label", void 0);
268
+ __decorate([property({ attribute: "aria-label" })], CodeEditor.prototype, "ariaLabel", void 0);
269
+ __decorate([property()], CodeEditor.prototype, "placeholder", void 0);
270
+ __decorate([property({
271
+ type: Boolean,
272
+ reflect: true,
273
+ attribute: "readonly"
274
+ })], CodeEditor.prototype, "readOnly", void 0);
275
+ __decorate([property({
276
+ type: Boolean,
277
+ reflect: true
278
+ })], CodeEditor.prototype, "disabled", void 0);
279
+ __decorate([property({
280
+ type: Boolean,
281
+ reflect: true
282
+ })], CodeEditor.prototype, "required", void 0);
283
+ __decorate([property({
284
+ type: Boolean,
285
+ reflect: true,
286
+ attribute: "line-numbers"
287
+ })], CodeEditor.prototype, "lineNumbers", void 0);
288
+ __decorate([property({
289
+ type: Boolean,
290
+ reflect: true
291
+ })], CodeEditor.prototype, "wrap", void 0);
292
+ __decorate([property({
293
+ type: Boolean,
294
+ reflect: true
295
+ })], CodeEditor.prototype, "autocomplete", void 0);
296
+ __decorate([property({
297
+ type: Number,
298
+ attribute: "tab-size"
299
+ })], CodeEditor.prototype, "tabSize", void 0);
300
+ __decorate([property({
301
+ type: Boolean,
302
+ reflect: true
303
+ })], CodeEditor.prototype, "error", void 0);
304
+ __decorate([property({ attribute: "error-text" })], CodeEditor.prototype, "errorText", void 0);
305
+ __decorate([property()], CodeEditor.prototype, "help", void 0);
306
+ __decorate([state()], CodeEditor.prototype, "engineState", void 0);
307
+ __decorate([state()], CodeEditor.prototype, "focused", void 0);
308
+ __decorate([query(".c2-code-editor__surface")], CodeEditor.prototype, "surface", void 0);
309
+ __decorate([query("textarea")], CodeEditor.prototype, "fallback", void 0);
310
+ __decorate([state()], CodeEditor.prototype, "disabledByForm", void 0);
311
+ __decorate([state()], CodeEditor.prototype, "hasLabelSlot", void 0);
312
+ CodeEditor = __decorate([customElement("c2-code-editor")], CodeEditor);
313
+ //#endregion
314
+ export { BUILT_IN_LANGUAGES, CodeEditor };
package/dist/engine.js ADDED
@@ -0,0 +1,296 @@
1
+ //#region src/engine.ts
2
+ /** Language ids with a built-in loader. Anything else needs `languageLoader`. */
3
+ var BUILT_IN_LANGUAGES = [
4
+ "javascript",
5
+ "typescript",
6
+ "jsx",
7
+ "tsx",
8
+ "html",
9
+ "css",
10
+ "json"
11
+ ];
12
+ async function importModules() {
13
+ const [state, view, commands, language, autocomplete, search, highlight] = await Promise.all([
14
+ import("@codemirror/state"),
15
+ import("@codemirror/view"),
16
+ import("@codemirror/commands"),
17
+ import("@codemirror/language"),
18
+ import("@codemirror/autocomplete"),
19
+ import("@codemirror/search"),
20
+ import("@lezer/highlight")
21
+ ]);
22
+ return {
23
+ state,
24
+ view,
25
+ commands,
26
+ language,
27
+ autocomplete,
28
+ search,
29
+ highlight
30
+ };
31
+ }
32
+ var pending;
33
+ /**
34
+ * Loads CodeMirror once per page; ten editors mounting together share one request and one module evaluation.
35
+ * Resolves to `undefined` when the peer is not installed, which is a supported state, not an error.
36
+ */
37
+ function loadCodeMirror() {
38
+ return pending ??= importModules().catch(() => void 0);
39
+ }
40
+ /**
41
+ * Token classes instead of inline colours, so the palette lives in `code-editor.scss` as
42
+ * `--c2-code-editor__theme--token-*` — the same names `@c2n/code-viewer` uses for its `css-variables` theme, so the
43
+ * two components can be given one palette.
44
+ */
45
+ function buildHighlightStyle({ language, highlight }) {
46
+ const { tags } = highlight;
47
+ return language.HighlightStyle.define([
48
+ {
49
+ tag: [
50
+ tags.keyword,
51
+ tags.modifier,
52
+ tags.controlKeyword,
53
+ tags.operatorKeyword
54
+ ],
55
+ class: "c2tok-keyword"
56
+ },
57
+ {
58
+ tag: [
59
+ tags.string,
60
+ tags.special(tags.string),
61
+ tags.regexp
62
+ ],
63
+ class: "c2tok-string"
64
+ },
65
+ {
66
+ tag: [
67
+ tags.comment,
68
+ tags.lineComment,
69
+ tags.blockComment,
70
+ tags.docComment
71
+ ],
72
+ class: "c2tok-comment"
73
+ },
74
+ {
75
+ tag: [
76
+ tags.number,
77
+ tags.bool,
78
+ tags.null,
79
+ tags.atom,
80
+ tags.constant(tags.name)
81
+ ],
82
+ class: "c2tok-constant"
83
+ },
84
+ {
85
+ tag: [
86
+ tags.function(tags.variableName),
87
+ tags.function(tags.propertyName),
88
+ tags.macroName
89
+ ],
90
+ class: "c2tok-function"
91
+ },
92
+ {
93
+ tag: [
94
+ tags.typeName,
95
+ tags.className,
96
+ tags.namespace,
97
+ tags.standard(tags.typeName)
98
+ ],
99
+ class: "c2tok-type"
100
+ },
101
+ {
102
+ tag: [tags.propertyName, tags.attributeName],
103
+ class: "c2tok-property"
104
+ },
105
+ {
106
+ tag: [
107
+ tags.variableName,
108
+ tags.definition(tags.variableName),
109
+ tags.local(tags.variableName)
110
+ ],
111
+ class: "c2tok-variable"
112
+ },
113
+ {
114
+ tag: [tags.tagName, tags.angleBracket],
115
+ class: "c2tok-tag"
116
+ },
117
+ {
118
+ tag: [
119
+ tags.operator,
120
+ tags.punctuation,
121
+ tags.separator,
122
+ tags.bracket,
123
+ tags.derefOperator
124
+ ],
125
+ class: "c2tok-punctuation"
126
+ },
127
+ {
128
+ tag: [tags.link, tags.url],
129
+ class: "c2tok-link"
130
+ },
131
+ {
132
+ tag: tags.invalid,
133
+ class: "c2tok-invalid"
134
+ },
135
+ {
136
+ tag: [tags.heading, tags.strong],
137
+ class: "c2tok-strong"
138
+ },
139
+ {
140
+ tag: tags.emphasis,
141
+ class: "c2tok-emphasis"
142
+ }
143
+ ]);
144
+ }
145
+ /** The built-in language loaders. Each grammar is its own dynamic import, so only what is used is fetched. */
146
+ async function loadBuiltInLanguage(id) {
147
+ switch (id) {
148
+ case "javascript": return import("@codemirror/lang-javascript").then((module) => module.javascript());
149
+ case "jsx": return import("@codemirror/lang-javascript").then((module) => module.javascript({ jsx: true }));
150
+ case "typescript": return import("@codemirror/lang-javascript").then((module) => module.javascript({ typescript: true }));
151
+ case "tsx": return import("@codemirror/lang-javascript").then((module) => module.javascript({
152
+ typescript: true,
153
+ jsx: true
154
+ }));
155
+ case "html": return import("@codemirror/lang-html").then((module) => module.html());
156
+ case "css": return import("@codemirror/lang-css").then((module) => module.css());
157
+ case "json": return import("@codemirror/lang-json").then((module) => module.json());
158
+ default: return;
159
+ }
160
+ }
161
+ async function resolveLanguage(id, loader) {
162
+ const normalized = id.trim().toLowerCase();
163
+ if (!normalized || normalized === "plaintext" || normalized === "text") return void 0;
164
+ if (loader) {
165
+ const custom = await loader(normalized);
166
+ if (custom) return custom;
167
+ }
168
+ return loadBuiltInLanguage(normalized).catch(() => void 0);
169
+ }
170
+ /**
171
+ * Mounts an editor, or resolves to `undefined` when CodeMirror is not installed. Each reconfigurable concern gets its
172
+ * own `Compartment`, so toggling line numbers or swapping the language never rebuilds the document or loses history.
173
+ */
174
+ async function createEditor(options) {
175
+ const modules = await loadCodeMirror();
176
+ if (!modules) return void 0;
177
+ const { state, view: viewModule, commands, language, autocomplete, search } = modules;
178
+ const { Compartment, EditorState } = state;
179
+ const { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, drawSelection, rectangularSelection, placeholder } = viewModule;
180
+ let contentState = {
181
+ label: options.label,
182
+ readOnly: options.readOnly,
183
+ editable: options.editable
184
+ };
185
+ const contentAttributes = () => ({
186
+ "aria-label": contentState.label,
187
+ "aria-readonly": contentState.readOnly ? "true" : "false",
188
+ ...contentState.editable ? {} : { "aria-disabled": "true" },
189
+ spellcheck: "false",
190
+ autocapitalize: "off",
191
+ autocorrect: "off"
192
+ });
193
+ const compartments = {
194
+ language: new Compartment(),
195
+ readOnly: new Compartment(),
196
+ editable: new Compartment(),
197
+ lineNumbers: new Compartment(),
198
+ lineWrapping: new Compartment(),
199
+ autocomplete: new Compartment(),
200
+ tabSize: new Compartment(),
201
+ placeholder: new Compartment(),
202
+ label: new Compartment()
203
+ };
204
+ let applying = false;
205
+ const view = new EditorView({
206
+ parent: options.parent,
207
+ root: options.root,
208
+ state: EditorState.create({
209
+ doc: options.value,
210
+ extensions: [
211
+ lineNumbers && compartments.lineNumbers.of(options.lineNumbers ? lineNumbers() : []),
212
+ highlightActiveLine(),
213
+ highlightActiveLineGutter(),
214
+ drawSelection(),
215
+ rectangularSelection(),
216
+ commands.history(),
217
+ language.bracketMatching(),
218
+ language.indentOnInput(),
219
+ language.foldGutter(),
220
+ autocomplete.closeBrackets(),
221
+ language.syntaxHighlighting(buildHighlightStyle(modules), { fallback: true }),
222
+ keymap.of([
223
+ ...commands.defaultKeymap,
224
+ ...commands.historyKeymap,
225
+ ...search.searchKeymap,
226
+ ...autocomplete.completionKeymap,
227
+ commands.indentWithTab
228
+ ]),
229
+ compartments.language.of([]),
230
+ compartments.autocomplete.of(options.autocomplete ? autocomplete.autocompletion() : []),
231
+ compartments.lineWrapping.of(options.lineWrapping ? EditorView.lineWrapping : []),
232
+ compartments.tabSize.of(EditorState.tabSize.of(options.tabSize)),
233
+ compartments.readOnly.of(EditorState.readOnly.of(options.readOnly)),
234
+ compartments.editable.of(EditorView.editable.of(options.editable)),
235
+ compartments.placeholder.of(options.placeholder ? placeholder(options.placeholder) : []),
236
+ compartments.label.of(EditorView.contentAttributes.of(contentAttributes())),
237
+ EditorView.updateListener.of((update) => {
238
+ if (update.docChanged && !applying) options.onInput(update.state.doc.toString());
239
+ if (update.focusChanged) (update.view.hasFocus ? options.onFocus : options.onBlur)();
240
+ })
241
+ ].filter(Boolean)
242
+ })
243
+ });
244
+ const reconfigure = (compartment, extension) => {
245
+ view.dispatch({ effects: compartment.reconfigure(extension) });
246
+ };
247
+ const handle = {
248
+ view,
249
+ getValue: () => view.state.doc.toString(),
250
+ setValue(value) {
251
+ if (value === view.state.doc.toString()) return;
252
+ applying = true;
253
+ try {
254
+ view.dispatch({ changes: {
255
+ from: 0,
256
+ to: view.state.doc.length,
257
+ insert: value
258
+ } });
259
+ } finally {
260
+ applying = false;
261
+ }
262
+ },
263
+ async setLanguage(id, loader) {
264
+ const extension = await resolveLanguage(id, loader);
265
+ reconfigure(compartments.language, extension ?? []);
266
+ },
267
+ setEditing(readOnly, editable) {
268
+ contentState = {
269
+ ...contentState,
270
+ readOnly,
271
+ editable
272
+ };
273
+ reconfigure(compartments.readOnly, EditorState.readOnly.of(readOnly));
274
+ reconfigure(compartments.editable, EditorView.editable.of(editable));
275
+ reconfigure(compartments.label, EditorView.contentAttributes.of(contentAttributes()));
276
+ },
277
+ setLineNumbers: (on) => reconfigure(compartments.lineNumbers, on ? lineNumbers() : []),
278
+ setLineWrapping: (on) => reconfigure(compartments.lineWrapping, on ? EditorView.lineWrapping : []),
279
+ setAutocomplete: (on) => reconfigure(compartments.autocomplete, on ? autocomplete.autocompletion() : []),
280
+ setTabSize: (size) => reconfigure(compartments.tabSize, EditorState.tabSize.of(size)),
281
+ setPlaceholder: (text) => reconfigure(compartments.placeholder, text ? placeholder(text) : []),
282
+ setLabel(label) {
283
+ contentState = {
284
+ ...contentState,
285
+ label
286
+ };
287
+ reconfigure(compartments.label, EditorView.contentAttributes.of(contentAttributes()));
288
+ },
289
+ focus: () => view.focus(),
290
+ destroy: () => view.destroy()
291
+ };
292
+ await handle.setLanguage(options.language, options.languageLoader);
293
+ return handle;
294
+ }
295
+ //#endregion
296
+ export { BUILT_IN_LANGUAGES, createEditor, loadCodeMirror };
package/package.json ADDED
@@ -0,0 +1,133 @@
1
+ {
2
+ "name": "@c2n/code-editor",
3
+ "version": "0.0.10",
4
+ "type": "module",
5
+ "main": "dist/code-editor.js",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./types/src/code-editor.d.ts",
9
+ "default": "./dist/code-editor.js"
10
+ },
11
+ "./react": {
12
+ "types": "./react.d.ts",
13
+ "default": "./react.js"
14
+ },
15
+ "./vue": {
16
+ "types": "./vue.d.ts",
17
+ "default": "./vue.js"
18
+ },
19
+ "./custom-elements.json": "./custom-elements.json"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "types",
24
+ "react.d.ts",
25
+ "react.js",
26
+ "vue.d.ts",
27
+ "vue.js"
28
+ ],
29
+ "keywords": [
30
+ "code-editor",
31
+ "codemirror",
32
+ "web component",
33
+ "lit"
34
+ ],
35
+ "license": "MIT",
36
+ "author": "code2nguyen@gmail.com",
37
+ "publishConfig": {
38
+ "registry": "https://registry.npmjs.org",
39
+ "access": "public"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/code2nguyen/web-components.git"
44
+ },
45
+ "scripts": {
46
+ "dev": "vite",
47
+ "build": "wireit",
48
+ "build:only": "vite build",
49
+ "type-check": "wireit"
50
+ },
51
+ "wireit": {
52
+ "type-check": {
53
+ "dependencies": [
54
+ "../../core:build"
55
+ ],
56
+ "command": "tsc -p tsconfig.lib.json --composite false"
57
+ },
58
+ "build": {
59
+ "dependencies": [
60
+ "type-check"
61
+ ],
62
+ "command": "vite build"
63
+ }
64
+ },
65
+ "dependencies": {
66
+ "@c2n/core": "0.0.10",
67
+ "lit": "3.3.3"
68
+ },
69
+ "devDependencies": {
70
+ "@c2n/config": "*",
71
+ "@codemirror/autocomplete": "^6.0.0",
72
+ "@codemirror/commands": "^6.0.0",
73
+ "@codemirror/lang-css": "^6.0.0",
74
+ "@codemirror/lang-html": "^6.0.0",
75
+ "@codemirror/lang-javascript": "^6.0.0",
76
+ "@codemirror/lang-json": "^6.0.0",
77
+ "@codemirror/language": "^6.0.0",
78
+ "@codemirror/search": "^6.0.0",
79
+ "@codemirror/state": "^6.0.0",
80
+ "@codemirror/view": "^6.0.0",
81
+ "@lezer/highlight": "^1.0.0"
82
+ },
83
+ "customElements": "custom-elements.json",
84
+ "peerDependencies": {
85
+ "@codemirror/autocomplete": "^6.0.0",
86
+ "@codemirror/commands": "^6.0.0",
87
+ "@codemirror/lang-css": "^6.0.0",
88
+ "@codemirror/lang-html": "^6.0.0",
89
+ "@codemirror/lang-javascript": "^6.0.0",
90
+ "@codemirror/lang-json": "^6.0.0",
91
+ "@codemirror/language": "^6.0.0",
92
+ "@codemirror/search": "^6.0.0",
93
+ "@codemirror/state": "^6.0.0",
94
+ "@codemirror/view": "^6.0.0",
95
+ "@lezer/highlight": "^1.0.0"
96
+ },
97
+ "peerDependenciesMeta": {
98
+ "@codemirror/autocomplete": {
99
+ "optional": true
100
+ },
101
+ "@codemirror/commands": {
102
+ "optional": true
103
+ },
104
+ "@codemirror/lang-css": {
105
+ "optional": true
106
+ },
107
+ "@codemirror/lang-html": {
108
+ "optional": true
109
+ },
110
+ "@codemirror/lang-javascript": {
111
+ "optional": true
112
+ },
113
+ "@codemirror/lang-json": {
114
+ "optional": true
115
+ },
116
+ "@codemirror/language": {
117
+ "optional": true
118
+ },
119
+ "@codemirror/search": {
120
+ "optional": true
121
+ },
122
+ "@codemirror/state": {
123
+ "optional": true
124
+ },
125
+ "@codemirror/view": {
126
+ "optional": true
127
+ },
128
+ "@lezer/highlight": {
129
+ "optional": true
130
+ }
131
+ },
132
+ "gitHead": "8d57710b3345fcb01cdb4cb15d2d43ba8bf21642"
133
+ }
package/react.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ // GENERATED by @c2n/framework-types. Do not edit by hand.
2
+ //
3
+ // JSX types for the custom elements of @c2n/code-editor. Import once, anywhere in the program:
4
+ //
5
+ // import '@c2n/code-editor/react'
6
+ //
7
+ // Register the elements at module scope before React renders, or React writes an object prop as an
8
+ // attribute and it stringifies.
9
+
10
+ import type { DetailedHTMLProps, HTMLAttributes } from 'react'
11
+ import type { CodeEditor } from '@c2n/code-editor'
12
+
13
+ /** Standard React host-element attributes plus the element's own public properties. */
14
+ type C2Props<T> = DetailedHTMLProps<HTMLAttributes<T>, T> & Partial<Omit<T, keyof HTMLElement>>
15
+
16
+ declare module 'react' {
17
+ namespace JSX {
18
+ interface IntrinsicElements {
19
+ 'c2-code-editor': C2Props<CodeEditor>
20
+ }
21
+ }
22
+ }
23
+
24
+ export {}
package/react.js ADDED
@@ -0,0 +1,3 @@
1
+ // GENERATED by @c2n/framework-types. Do not edit by hand.
2
+ // Types only — see the matching .d.ts.
3
+ export {}
@@ -0,0 +1,204 @@
1
+ import { LitElement, type PropertyValues } from 'lit';
2
+ import type { TypedAddEventListener, TypedRemoveEventListener } from '@c2n/core/event-helper.js';
3
+ import { type LanguageLoader } from './engine.js';
4
+ export { BUILT_IN_LANGUAGES } from './engine.js';
5
+ export type { BuiltInLanguage, LanguageLoader } from './engine.js';
6
+ /** How far the engine got. `basic` is the textarea fallback: CodeMirror is not installed. */
7
+ export type CodeEditorEngineState = 'pending' | 'ready' | 'basic';
8
+ /** Events fired by {@link CodeEditor}, keyed for `addEventListener`. */
9
+ export interface CodeEditorEventMap {
10
+ input: Event;
11
+ change: Event;
12
+ /** The engine settled. `detail.engine` is `codemirror` or `basic`. */
13
+ ready: CustomEvent<{
14
+ engine: 'codemirror' | 'basic';
15
+ }>;
16
+ }
17
+ export interface CodeEditor {
18
+ addEventListener: TypedAddEventListener<CodeEditor, CodeEditorEventMap>;
19
+ removeEventListener: TypedRemoveEventListener<CodeEditor, CodeEditorEventMap>;
20
+ }
21
+ /**
22
+ * Source-code field built on CodeMirror 6, which is an **optional peer dependency**: nothing is imported until an
23
+ * editor mounts, and when the peer is absent the element degrades to a plain `<textarea>` carrying the same value,
24
+ * the same events and the same form behaviour — only without highlighting. Install what you need alongside it:
25
+ *
26
+ * ```sh
27
+ * npm install @codemirror/state @codemirror/view @codemirror/commands @codemirror/language \
28
+ * @codemirror/autocomplete @codemirror/search @lezer/highlight @codemirror/lang-javascript
29
+ * ```
30
+ *
31
+ * It is form-associated: `name` and `value` take part in a `<form>`, it restores on reset, and it fires the plain
32
+ * `input` and `change` a `v-model`, a `ControlValueAccessor` or any generic two-way binding listens for.
33
+ *
34
+ * Colours are **not** a CodeMirror theme. The editor renders real DOM inside the shadow root, so every surface,
35
+ * gutter and token colour is an ordinary `--c2-code-editor__*` custom property — including the syntax palette, whose
36
+ * `--c2-code-editor__theme--token-*` names match `@c2n/code-viewer`'s `css-variables` theme so one palette can drive
37
+ * both. Dark mode is therefore just different variable values, with no JavaScript involved.
38
+ *
39
+ * Grammars for `javascript`, `typescript`, `jsx`, `tsx`, `html`, `css` and `json` are built in and loaded on demand.
40
+ * Any other language comes from `languageLoader`, so adding Python or SQL costs this package no dependency.
41
+ *
42
+ * @tag c2-code-editor
43
+ *
44
+ * @slot label - Label shown above the editor. Falls back to the `label` attribute.
45
+ * @slot supporting-text - Help text under the editor. Replaced by `error-text` while `error` is set.
46
+ *
47
+ * @event {Event} input - The document changed. Fires on every edit.
48
+ * @event {Event} change - The value was committed: the editor lost focus after an edit, as a native control does.
49
+ * @event {CustomEvent<{ engine: 'codemirror' | 'basic' }>} ready - The engine settled, so tests and hosts can wait for it. Does not bubble.
50
+ *
51
+ * @csspart container - The box around the label, editor and supporting text.
52
+ * @csspart label - The label above the editor.
53
+ * @csspart editor - The element CodeMirror renders into, or the fallback textarea.
54
+ * @csspart supporting-text - The help / error row under the editor.
55
+ *
56
+ * @cssproperty {color} [--c2-code-editor--background=#ffffff]
57
+ * @cssproperty {color} [--c2-code-editor--color=#24292e] - Foreground of text no token class matched.
58
+ * @cssproperty {border} [--c2-code-editor--border=1px solid #bcbcc6]
59
+ * @cssproperty {border} [--c2-code-editor__focus--border=1px solid rgb(2, 101, 220)]
60
+ * @cssproperty {outline} [--c2-code-editor__focus--outline=2px solid rgba(2, 101, 220, 0.4)]
61
+ * @cssproperty {border-radius} [--c2-code-editor--border-radius=6px]
62
+ * @cssproperty {box-shadow} --c2-code-editor--box-shadow
63
+ * @cssproperty {opacity} [--c2-code-editor__disabled--opacity=0.38]
64
+ *
65
+ * @cssproperty {font-family} [--c2-code-editor--font-family=ui-monospace, SFMono-Regular, Menlo, Consolas, monospace]
66
+ * @cssproperty {font-size} [--c2-code-editor--font-size=13px]
67
+ * @cssproperty {line-height} [--c2-code-editor--line-height=1.6]
68
+ * @cssproperty {pixel} [--c2-code-editor--min-height=120px]
69
+ * @cssproperty {pixel} [--c2-code-editor--max-height=420px] - The editor scrolls past this height.
70
+ * @cssproperty {padding} [--c2-code-editor--padding-block=10px]
71
+ * @cssproperty {padding} [--c2-code-editor--padding-inline=12px]
72
+ *
73
+ * @cssproperty {color} [--c2-code-editor__gutter--background=transparent]
74
+ * @cssproperty {color} [--c2-code-editor__gutter--color=#71717a]
75
+ * @cssproperty {color} [--c2-code-editor__gutter__active--color=#18181b]
76
+ * @cssproperty {border} [--c2-code-editor__gutter--border-right=1px solid #e4e4e7]
77
+ * @cssproperty {pixel} [--c2-code-editor__gutter--min-width=32px]
78
+ *
79
+ * @cssproperty {color} [--c2-code-editor__active-line--background=rgba(2, 101, 220, 0.04)]
80
+ * @cssproperty {color} [--c2-code-editor__selection--background=rgba(2, 101, 220, 0.18)]
81
+ * @cssproperty {color} [--c2-code-editor__cursor--color=#18181b]
82
+ * @cssproperty {color} [--c2-code-editor__matching-bracket--background=rgba(2, 101, 220, 0.16)]
83
+ * @cssproperty {color} [--c2-code-editor__placeholder--color=#71717a]
84
+ *
85
+ * @cssproperty {color} [--c2-code-editor__theme--token-keyword=#cf222e]
86
+ * @cssproperty {color} [--c2-code-editor__theme--token-string=#032f62]
87
+ * @cssproperty {color} [--c2-code-editor__theme--token-comment=#636c76]
88
+ * @cssproperty {color} [--c2-code-editor__theme--token-constant=#0550ae]
89
+ * @cssproperty {color} [--c2-code-editor__theme--token-function=#6f42c1]
90
+ * @cssproperty {color} [--c2-code-editor__theme--token-type=#953800]
91
+ * @cssproperty {color} [--c2-code-editor__theme--token-property=#0550ae]
92
+ * @cssproperty {color} [--c2-code-editor__theme--token-variable=#24292e]
93
+ * @cssproperty {color} [--c2-code-editor__theme--token-tag=#116329]
94
+ * @cssproperty {color} [--c2-code-editor__theme--token-punctuation=#24292e]
95
+ * @cssproperty {color} [--c2-code-editor__theme--token-link=#032f62]
96
+ * @cssproperty {color} [--c2-code-editor__theme--token-invalid=#cf222e]
97
+ *
98
+ * @cssproperty {color} [--c2-code-editor__label--color=#18181b]
99
+ * @cssproperty {font-size} [--c2-code-editor__label--font-size=12px]
100
+ * @cssproperty {font-weight} [--c2-code-editor__label--font-weight=500]
101
+ * @cssproperty {color} [--c2-code-editor__supporting-text--color=#71717a]
102
+ * @cssproperty {font-size} [--c2-code-editor__supporting-text--font-size=12px]
103
+ * @cssproperty {color} [--c2-code-editor__error--color=#dc2626]
104
+ * @cssproperty {border} [--c2-code-editor__error--border=1px solid #dc2626]
105
+ * @cssproperty {pixel} [--c2-code-editor--gap=6px] - Space between the label, the editor and the supporting text.
106
+ */
107
+ export declare class CodeEditor extends LitElement {
108
+ static formAssociated: boolean;
109
+ static styles: import("lit").CSSResult;
110
+ private readonly internals;
111
+ /** The source code. Assigning it replaces the document without losing scroll position or undo history. */
112
+ value: string;
113
+ /** Language id: one of `BUILT_IN_LANGUAGES`, anything `languageLoader` resolves, or empty for no highlighting. */
114
+ language: string;
115
+ /**
116
+ * Resolves a language id this package has no grammar for, to a CodeMirror `Extension`. Property only — it takes a
117
+ * function, so there is no attribute for it.
118
+ */
119
+ languageLoader: LanguageLoader | undefined;
120
+ /** Form field name. */
121
+ name: string;
122
+ /** Label above the editor, when the `label` slot is empty. */
123
+ label: string;
124
+ /** Accessible name when nothing visible labels the editor. */
125
+ ariaLabel: string | null;
126
+ /** Text shown while the document is empty. */
127
+ placeholder: string;
128
+ /** The document cannot be edited, but is still selectable and focusable. */
129
+ readOnly: boolean;
130
+ /** Blocks interaction entirely and dims the editor. */
131
+ disabled: boolean;
132
+ /** The form is invalid while the value is empty. */
133
+ required: boolean;
134
+ /** Show the line-number gutter. */
135
+ lineNumbers: boolean;
136
+ /** Wrap long lines instead of scrolling horizontally. */
137
+ wrap: boolean;
138
+ /** Offer completions while typing (identifiers, and whatever the grammar contributes). */
139
+ autocomplete: boolean;
140
+ /** Width of a tab stop, in characters. */
141
+ tabSize: number;
142
+ /** Renders the error styling; `error-text` replaces the supporting text. */
143
+ error: boolean;
144
+ /** Message shown in place of the supporting text while `error` is set. */
145
+ errorText: string;
146
+ /** Help text under the editor, when the `supporting-text` slot is empty. */
147
+ help: string;
148
+ /** How far the engine got: `pending`, `ready` (CodeMirror mounted) or `basic` (textarea fallback). */
149
+ private engineState;
150
+ private focused;
151
+ private surface;
152
+ private fallback?;
153
+ private editor;
154
+ private mounting;
155
+ private customValidityMessage;
156
+ private disabledByForm;
157
+ /** Set by an edit, cleared by the `change` that follows the blur — the native commit-on-blur contract. */
158
+ private dirty;
159
+ /** Resolves once the engine has settled, so a host (or a test) can await the editor being live. */
160
+ get ready(): Promise<CodeEditorEngineState>;
161
+ /** The engine in use, or `undefined` while it is still loading. */
162
+ get engine(): 'codemirror' | 'basic' | undefined;
163
+ get form(): HTMLFormElement | null;
164
+ get labels(): NodeList;
165
+ get validity(): ValidityState;
166
+ get validationMessage(): string;
167
+ get willValidate(): boolean;
168
+ private get effectiveDisabled();
169
+ /** What labels the editable element. CodeMirror's content node is the `textbox`, so the name has to land there. */
170
+ private get accessibleName();
171
+ disconnectedCallback(): void;
172
+ /** The `value` *attribute* is the default, exactly as `defaultValue` is on a native control. */
173
+ formResetCallback(): void;
174
+ formDisabledCallback(disabled: boolean): void;
175
+ formStateRestoreCallback(state: string | File | FormData | null): void;
176
+ checkValidity(): boolean;
177
+ reportValidity(): boolean;
178
+ setCustomValidity(message: string): void;
179
+ focus(options?: FocusOptions): void;
180
+ protected firstUpdated(): void;
181
+ private mount;
182
+ protected updated(changed: PropertyValues): void;
183
+ /**
184
+ * CodeMirror edits a `contenteditable`, whose native `input` / `beforeinput` are composed and would escape the
185
+ * shadow root — a consumer would see one of those *and* the component's own `input` for the same keystroke, and
186
+ * would see one even for an edit `readonly` rejected. The component owns its event surface, so they stop here.
187
+ */
188
+ private stopNativeEditing;
189
+ private handleEditorInput;
190
+ private handleBlur;
191
+ /** The fallback textarea fires native `input` / `change`; only the value has to be mirrored. */
192
+ private handleFallbackInput;
193
+ private handleFallbackChange;
194
+ private renderFallback;
195
+ render(): import("lit-html").TemplateResult<1>;
196
+ private hasLabelSlot;
197
+ private handleLabelSlotChange;
198
+ }
199
+ declare global {
200
+ interface HTMLElementTagNameMap {
201
+ 'c2-code-editor': CodeEditor;
202
+ }
203
+ }
204
+ //# sourceMappingURL=code-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.d.ts","sourceRoot":"","sources":["../../src/code-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAA;AAO/E,OAAO,KAAK,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAA;AAChG,OAAO,EAAmC,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAGlF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAElE,6FAA6F;AAC7F,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AAEjE,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,KAAK,CAAA;IACb,sEAAsE;IACtE,KAAK,EAAE,WAAW,CAAC;QAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAA;KAAE,CAAC,CAAA;CACvD;AAED,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,qBAAqB,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAA;IACvE,mBAAmB,EAAE,wBAAwB,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAA;CAC9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,MAAM,CAAC,cAAc,UAAO;IAE5B,OAAgB,MAAM,0BAAoB;IAE1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAEnD,0GAA0G;IAC9F,KAAK,SAAK;IAEtB,kHAAkH;IACrF,QAAQ,SAAK;IAE1C;;;OAGG;IAC6B,cAAc,EAAE,cAAc,GAAG,SAAS,CAAY;IAEtF,uBAAuB;IACX,IAAI,SAAK;IAErB,8DAA8D;IAClD,KAAK,SAAK;IAEtB,8DAA8D;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAO;IAE/E,8CAA8C;IAClC,WAAW,SAAK;IAE5B,4EAA4E;IACT,QAAQ,UAAQ;IAEnF,uDAAuD;IACX,QAAQ,UAAQ;IAE5D,oDAAoD;IACR,QAAQ,UAAQ;IAE5D,mCAAmC;IACoC,WAAW,UAAQ;IAE1F,yDAAyD;IACb,IAAI,UAAQ;IAExD,0FAA0F;IAC9C,YAAY,UAAQ;IAEhE,0CAA0C;IACS,OAAO,SAAI;IAE9D,4EAA4E;IAChC,KAAK,UAAQ;IAEzD,0EAA0E;IACnC,SAAS,SAAK;IAErD,4EAA4E;IAChE,IAAI,SAAK;IAErB,sGAAsG;IAC7F,OAAO,CAAC,WAAW,CAAmC;IAEtD,OAAO,CAAC,OAAO,CAAQ;IAEG,OAAO,CAAC,OAAO,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,CAAqB;IAEzD,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,qBAAqB,CAAK;IACzB,OAAO,CAAC,cAAc,CAAQ;IACvC,0GAA0G;IAC1G,OAAO,CAAC,KAAK,CAAQ;IAErB,mGAAmG;IACnG,IAAI,KAAK,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAE1C;IAED,mEAAmE;IACnE,IAAI,MAAM,IAAI,YAAY,GAAG,OAAO,GAAG,SAAS,CAE/C;IAED,IAAI,IAAI,2BAEP;IAED,IAAI,MAAM,aAET;IAED,IAAI,QAAQ,kBAEX;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,YAAY,YAEf;IAED,OAAO,KAAK,iBAAiB,GAE5B;IAED,mHAAmH;IACnH,OAAO,KAAK,cAAc,GAEzB;IAEQ,oBAAoB;IAQ7B,gGAAgG;IAChG,iBAAiB;IAIjB,oBAAoB,CAAC,QAAQ,EAAE,OAAO;IAItC,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI;IAI/D,aAAa;IAIb,cAAc;IAId,iBAAiB,CAAC,OAAO,EAAE,MAAM;IAKxB,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY;cAKlB,YAAY;YAKjB,KAAK;cA6BA,OAAO,CAAC,OAAO,EAAE,cAAc;IAoBlD;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,UAAU;IAOlB,gGAAgG;IAChG,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,cAAc;IAoBb,MAAM;IAmCN,OAAO,CAAC,YAAY,CAAQ;IAErC,OAAO,CAAC,qBAAqB;CAI9B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,UAAU,CAAA;KAC7B;CACF"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Lazy access to CodeMirror 6.
3
+ *
4
+ * Nothing here is imported by the element file at module scope: `c2-code-editor` pulls in no engine until an
5
+ * instance actually mounts, so importing the package costs nothing, the module is safe to evaluate during SSR, and
6
+ * the whole of CodeMirror stays an *optional* peer dependency — the element degrades to a plain textarea when it is
7
+ * not installed, the same contract `@c2n/chart` has with uPlot and ECharts.
8
+ *
9
+ * Every visible colour comes from the component's CSS custom properties, not from a CodeMirror theme: the editor
10
+ * renders real DOM inside the shadow root, so `code-editor.scss` can style `.cm-*` directly, and the syntax
11
+ * highlighter maps Lezer tags to `c2tok-*` classes rather than to inline styles.
12
+ */
13
+ import type { Extension } from '@codemirror/state';
14
+ import type { EditorView } from '@codemirror/view';
15
+ /** Language ids with a built-in loader. Anything else needs `languageLoader`. */
16
+ export declare const BUILT_IN_LANGUAGES: readonly ["javascript", "typescript", "jsx", "tsx", "html", "css", "json"];
17
+ export type BuiltInLanguage = (typeof BUILT_IN_LANGUAGES)[number];
18
+ /** Resolves a language id to CodeMirror extensions. Return nothing for "no highlighting". */
19
+ export type LanguageLoader = (id: string) => Promise<Extension | undefined> | Extension | undefined;
20
+ export interface EditorOptions {
21
+ parent: HTMLElement;
22
+ /** The shadow root the editor lives in. CodeMirror needs it for selection and focus tracking. */
23
+ root: ShadowRoot | Document;
24
+ value: string;
25
+ language: string;
26
+ languageLoader?: LanguageLoader;
27
+ readOnly: boolean;
28
+ editable: boolean;
29
+ lineNumbers: boolean;
30
+ lineWrapping: boolean;
31
+ autocomplete: boolean;
32
+ tabSize: number;
33
+ placeholder: string;
34
+ /** Accessible name put on the editable element itself, which is what carries `role=textbox`. */
35
+ label: string;
36
+ onInput: (value: string) => void;
37
+ onBlur: () => void;
38
+ onFocus: () => void;
39
+ }
40
+ /** What the element drives. Every setter is a live reconfiguration, never a teardown. */
41
+ export interface EditorHandle {
42
+ readonly view: EditorView;
43
+ getValue(): string;
44
+ setValue(value: string): void;
45
+ setLanguage(language: string, loader?: LanguageLoader): Promise<void>;
46
+ setEditing(readOnly: boolean, editable: boolean): void;
47
+ setLineNumbers(on: boolean): void;
48
+ setLineWrapping(on: boolean): void;
49
+ setAutocomplete(on: boolean): void;
50
+ setTabSize(size: number): void;
51
+ setPlaceholder(text: string): void;
52
+ setLabel(label: string): void;
53
+ focus(): void;
54
+ destroy(): void;
55
+ }
56
+ /** Thrown by nothing: a missing peer resolves to `undefined` so the element can fall back instead of failing. */
57
+ type Modules = Awaited<ReturnType<typeof importModules>>;
58
+ declare function importModules(): Promise<{
59
+ state: typeof import("@codemirror/state");
60
+ view: typeof import("@codemirror/view");
61
+ commands: typeof import("@codemirror/commands");
62
+ language: typeof import("@codemirror/language");
63
+ autocomplete: typeof import("@codemirror/autocomplete");
64
+ search: typeof import("@codemirror/search");
65
+ highlight: typeof import("@lezer/highlight");
66
+ }>;
67
+ /**
68
+ * Loads CodeMirror once per page; ten editors mounting together share one request and one module evaluation.
69
+ * Resolves to `undefined` when the peer is not installed, which is a supported state, not an error.
70
+ */
71
+ export declare function loadCodeMirror(): Promise<Modules | undefined>;
72
+ /**
73
+ * Mounts an editor, or resolves to `undefined` when CodeMirror is not installed. Each reconfigurable concern gets its
74
+ * own `Compartment`, so toggling line numbers or swapping the language never rebuilds the document or loses history.
75
+ */
76
+ export declare function createEditor(options: EditorOptions): Promise<EditorHandle | undefined>;
77
+ export {};
78
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,4EAA6E,CAAA;AAE5G,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEjE,6FAA6F;AAC7F,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,SAAS,GAAG,SAAS,CAAA;AAEnG,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAA;IACnB,iGAAiG;IACjG,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,OAAO,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,gGAAgG;IAChG,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,yFAAyF;AACzF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,IAAI,MAAM,CAAA;IAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAA;IACtD,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAA;IACjC,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAA;IAClC,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAA;IAClC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,KAAK,IAAI,IAAI,CAAA;IACb,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,iHAAiH;AACjH,KAAK,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAA;AAExD,iBAAe,aAAa;;;;;;;;GAW3B;AAID;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAE7D;AA4DD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAiH5F"}
package/vue.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ // GENERATED by @c2n/framework-types. Do not edit by hand.
2
+ //
3
+ // Vue template types for the custom elements of @c2n/code-editor. Import once, anywhere in the program:
4
+ //
5
+ // import '@c2n/code-editor/vue'
6
+ //
7
+ // Tell the compiler about the tags as well, or every c2-* tag is resolved as a Vue component and renders
8
+ // nothing: template.compilerOptions.isCustomElement = (tag) => tag.startsWith('c2-') in vite.config.ts.
9
+
10
+ import type { DefineComponent, HTMLAttributes } from 'vue'
11
+ import type { CodeEditor, CodeEditorEventMap } from '@c2n/code-editor'
12
+
13
+ /** The element's own public properties, plus every attribute Vue understands on a host element. */
14
+ type C2Props<T> = Partial<Omit<T, keyof HTMLElement>> & HTMLAttributes
15
+
16
+ declare module 'vue' {
17
+ interface GlobalComponents {
18
+ 'c2-code-editor': DefineComponent<
19
+ C2Props<CodeEditor> & {
20
+ 'aria-label'?: unknown
21
+ readonly?: unknown
22
+ 'line-numbers'?: unknown
23
+ 'tab-size'?: unknown
24
+ 'error-text'?: unknown
25
+ onReady?: (event: CodeEditorEventMap['ready']) => void
26
+ onInput?: (event: CodeEditorEventMap['input']) => void
27
+ onChange?: (event: CodeEditorEventMap['change']) => void
28
+ }
29
+ >
30
+ }
31
+ }
32
+
33
+ declare module '@vue/runtime-dom' {
34
+ interface HTMLAttributes {
35
+ /** Vue's own definition omits it, and slotting a plain element into a component needs it. */
36
+ slot?: string
37
+ }
38
+ }
39
+
40
+ export {}
package/vue.js ADDED
@@ -0,0 +1,3 @@
1
+ // GENERATED by @c2n/framework-types. Do not edit by hand.
2
+ // Types only — see the matching .d.ts.
3
+ export {}