mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-01 13:37:47 +00:00
Add code editor to YAML editor (#2609)
* Add code editor to YAML editor * Add code editor to raw config editor * Remove hui-yaml-editor * Update src/panels/lovelace/components/hui-code-editor.ts Co-Authored-By: bramkragten <mail@bramkragten.nl> * Update src/panels/lovelace/components/hui-code-editor.ts Co-Authored-By: bramkragten <mail@bramkragten.nl> * Rename to hui-yaml-editor * Lint and tab * Fix empty editor * Lint * Use codemirror for comment and edit detection + some styling * Add save action (ctrl+s/cmd+s) to card editor * Move save to the instance * Delete save for now * Remove yaml-change event on init
This commit is contained in:
commit
7cb2b743fa
@ -68,6 +68,7 @@
|
||||
"@webcomponents/webcomponentsjs": "^2.2.0",
|
||||
"chart.js": "~2.7.2",
|
||||
"chartjs-chart-timeline": "^0.2.1",
|
||||
"codemirror": "^5.43.0",
|
||||
"deep-clone-simple": "^1.1.1",
|
||||
"es6-object-assign": "^1.1.0",
|
||||
"eslint-import-resolver-webpack": "^0.10.1",
|
||||
@ -102,6 +103,7 @@
|
||||
"@babel/preset-typescript": "^7.1.0",
|
||||
"@gfx/zopfli": "^1.0.9",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/codemirror": "^0.0.71",
|
||||
"@types/mocha": "^5.2.5",
|
||||
"babel-eslint": "^10",
|
||||
"babel-loader": "^8.0.4",
|
||||
|
93
src/panels/lovelace/components/hui-yaml-editor.ts
Normal file
93
src/panels/lovelace/components/hui-yaml-editor.ts
Normal file
@ -0,0 +1,93 @@
|
||||
// @ts-ignore
|
||||
import CodeMirror from "codemirror";
|
||||
import "codemirror/mode/yaml/yaml";
|
||||
// @ts-ignore
|
||||
import codeMirrorCSS from "codemirror/lib/codemirror.css";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"yaml-changed": {
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class HuiYamlEditor extends HTMLElement {
|
||||
public codemirror: CodeMirror;
|
||||
private _value: string;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
this._value = "";
|
||||
const shadowRoot = this.attachShadow({ mode: "open" });
|
||||
shadowRoot.innerHTML = `
|
||||
<style>
|
||||
${codeMirrorCSS}
|
||||
.CodeMirror {
|
||||
height: var(--code-mirror-height, 300px);
|
||||
direction: var(--code-mirror-direction, ltr);
|
||||
}
|
||||
.CodeMirror-gutters {
|
||||
border-right: 1px solid var(--paper-input-container-color, var(--secondary-text-color));
|
||||
background-color: var(--paper-dialog-background-color, var(--primary-background-color));
|
||||
transition: 0.2s ease border-right;
|
||||
}
|
||||
.CodeMirror-focused .CodeMirror-gutters {
|
||||
border-right: 2px solid var(--paper-input-container-focus-color, var(--primary-color));;
|
||||
}
|
||||
.CodeMirror-linenumber {
|
||||
color: var(--paper-dialog-color, var(--primary-text-color));
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
set value(value: string) {
|
||||
if (this.codemirror) {
|
||||
if (value !== this.codemirror.getValue()) {
|
||||
this.codemirror.setValue(value);
|
||||
}
|
||||
}
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.codemirror.getValue();
|
||||
}
|
||||
|
||||
get hasComments(): boolean {
|
||||
return this.shadowRoot!.querySelector("span.cm-comment") ? true : false;
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
if (!this.codemirror) {
|
||||
this.codemirror = CodeMirror(this.shadowRoot, {
|
||||
value: this._value,
|
||||
lineNumbers: true,
|
||||
mode: "yaml",
|
||||
tabSize: 2,
|
||||
autofocus: true,
|
||||
extraKeys: {
|
||||
Tab: (cm: CodeMirror) => {
|
||||
const spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
|
||||
cm.replaceSelection(spaces);
|
||||
},
|
||||
},
|
||||
});
|
||||
this.codemirror.on("changes", () => this._onChange());
|
||||
} else {
|
||||
this.codemirror.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private _onChange(): void {
|
||||
fireEvent(this, "yaml-changed", { value: this.codemirror.getValue() });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-yaml-editor": HuiYamlEditor;
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define("hui-yaml-editor", HuiYamlEditor);
|
@ -23,23 +23,22 @@ import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
import "./hui-yaml-editor";
|
||||
import "../../components/hui-yaml-editor";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HuiYamlEditor } from "../../components/hui-yaml-editor";
|
||||
import "./hui-card-preview";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HuiCardPreview } from "./hui-card-preview";
|
||||
import { LovelaceCardEditor, Lovelace } from "../../types";
|
||||
import { YamlChangedEvent, ConfigValue, ConfigError } from "../types";
|
||||
import { extYamlSchema } from "../yaml-ext-schema";
|
||||
import { ConfigValue, ConfigError } from "../types";
|
||||
import { EntityConfig } from "../../entity-rows/types";
|
||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||
import { addCard, replaceCard } from "../config-util";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"yaml-changed": {
|
||||
yaml: string;
|
||||
};
|
||||
"entities-changed": {
|
||||
entities: EntityConfig[];
|
||||
};
|
||||
@ -120,8 +119,7 @@ export class HuiEditCard extends LitElement {
|
||||
? this._configElement
|
||||
: html`
|
||||
<hui-yaml-editor
|
||||
.hass="${this.hass}"
|
||||
.yaml="${this._configValue!.value}"
|
||||
.value="${this._configValue!.value}"
|
||||
@yaml-changed="${this._handleYamlChanged}"
|
||||
></hui-yaml-editor>
|
||||
`}
|
||||
@ -195,6 +193,11 @@ export class HuiEditCard extends LitElement {
|
||||
await this.updateComplete;
|
||||
this._loading = false;
|
||||
this._resizeDialog();
|
||||
if (!this._uiEditor) {
|
||||
setTimeout(() => {
|
||||
this.yamlEditor.codemirror.refresh();
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private async _resizeDialog(): Promise<void> {
|
||||
@ -217,9 +220,7 @@ export class HuiEditCard extends LitElement {
|
||||
|
||||
const cardConf: LovelaceCardConfig =
|
||||
this._configValue!.format === "yaml"
|
||||
? yaml.safeLoad(this._configValue!.value!, {
|
||||
schema: extYamlSchema,
|
||||
})
|
||||
? yaml.safeLoad(this._configValue!.value!)
|
||||
: this._configValue!.value!;
|
||||
|
||||
try {
|
||||
@ -241,12 +242,12 @@ export class HuiEditCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _handleYamlChanged(ev: YamlChangedEvent): void {
|
||||
this._configValue = { format: "yaml", value: ev.detail.yaml };
|
||||
private _handleYamlChanged(ev: CustomEvent): void {
|
||||
this._configValue = { format: "yaml", value: ev.detail.value };
|
||||
try {
|
||||
const config = yaml.safeLoad(this._configValue.value, {
|
||||
schema: extYamlSchema,
|
||||
}) as LovelaceCardConfig;
|
||||
const config = yaml.safeLoad(
|
||||
this._configValue.value
|
||||
) as LovelaceCardConfig;
|
||||
this._updatePreview(config);
|
||||
this._configState = "OK";
|
||||
} catch (err) {
|
||||
@ -263,7 +264,9 @@ export class HuiEditCard extends LitElement {
|
||||
this._updatePreview(value);
|
||||
}
|
||||
|
||||
private _updatePreview(config: LovelaceCardConfig) {
|
||||
private async _updatePreview(config: LovelaceCardConfig) {
|
||||
await this.updateComplete;
|
||||
|
||||
if (!this._previewEl) {
|
||||
return;
|
||||
}
|
||||
@ -295,9 +298,7 @@ export class HuiEditCard extends LitElement {
|
||||
this._uiEditor = !this._uiEditor;
|
||||
} else if (this._configElement && this._configValue!.format === "yaml") {
|
||||
const yamlConfig = this._configValue!.value;
|
||||
const cardConfig = yaml.safeLoad(yamlConfig, {
|
||||
schema: extYamlSchema,
|
||||
}) as LovelaceCardConfig;
|
||||
const cardConfig = yaml.safeLoad(yamlConfig) as LovelaceCardConfig;
|
||||
this._uiEditor = !this._uiEditor;
|
||||
if (cardConfig.type !== this._cardType) {
|
||||
const succes = await this._loadConfigElement(cardConfig);
|
||||
@ -356,6 +357,7 @@ export class HuiEditCard extends LitElement {
|
||||
configElement = await elClass.getConfigElement();
|
||||
} else {
|
||||
this._configValue = { format: "yaml", value: yaml.safeDump(conf) };
|
||||
this._updatePreview(conf);
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return false;
|
||||
@ -372,6 +374,7 @@ export class HuiEditCard extends LitElement {
|
||||
format: "yaml",
|
||||
value: yaml.safeDump(conf),
|
||||
};
|
||||
this._updatePreview(conf);
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return false;
|
||||
@ -398,6 +401,10 @@ export class HuiEditCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private get yamlEditor(): HuiYamlEditor {
|
||||
return this.shadowRoot!.querySelector("hui-yaml-editor")!;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyleDialog,
|
||||
|
@ -1,64 +0,0 @@
|
||||
import {
|
||||
html,
|
||||
LitElement,
|
||||
PropertyDeclarations,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
export class HuiYAMLEditor extends LitElement {
|
||||
protected hass?: HomeAssistant;
|
||||
private _yaml?: string;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return { _yaml: {} };
|
||||
}
|
||||
|
||||
set yaml(yaml: string) {
|
||||
if (yaml === undefined) {
|
||||
return;
|
||||
} else {
|
||||
this._yaml = yaml;
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<paper-textarea
|
||||
max-rows="10"
|
||||
.value="${this._yaml}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-textarea>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderStyle(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
paper-textarea {
|
||||
--paper-input-container-shared-input-style_-_font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: Event): void {
|
||||
const target = ev.target! as any;
|
||||
this._yaml = target.value;
|
||||
fireEvent(this, "yaml-changed", {
|
||||
yaml: target.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-yaml-editor": HuiYAMLEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-yaml-editor", HuiYAMLEditor);
|
@ -1,22 +0,0 @@
|
||||
import yaml from "js-yaml";
|
||||
|
||||
const secretYamlType = new yaml.Type("!secret", {
|
||||
kind: "scalar",
|
||||
construct(data) {
|
||||
data = data || "";
|
||||
return "!secret " + data;
|
||||
},
|
||||
});
|
||||
|
||||
const includeYamlType = new yaml.Type("!include", {
|
||||
kind: "scalar",
|
||||
construct(data) {
|
||||
data = data || "";
|
||||
return "!include " + data;
|
||||
},
|
||||
});
|
||||
|
||||
export const extYamlSchema = yaml.Schema.create([
|
||||
secretYamlType,
|
||||
includeYamlType,
|
||||
]);
|
@ -14,8 +14,10 @@ import { Lovelace } from "./types";
|
||||
|
||||
import "../../components/ha-icon";
|
||||
import { haStyle } from "../../resources/ha-style";
|
||||
|
||||
const TAB_INSERT = " ";
|
||||
import "./components/hui-yaml-editor";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HuiYamlEditor } from "./components/hui-yaml-editor";
|
||||
|
||||
const lovelaceStruct = struct.interface({
|
||||
title: "string?",
|
||||
@ -28,16 +30,13 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
public closeEditor?: () => void;
|
||||
private _saving?: boolean;
|
||||
private _changed?: boolean;
|
||||
private _hashAdded?: boolean;
|
||||
private _hash?: boolean;
|
||||
private _generation?: number;
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
lovelace: {},
|
||||
_saving: {},
|
||||
_changed: {},
|
||||
_hashAdded: {},
|
||||
_hash: {},
|
||||
};
|
||||
}
|
||||
|
||||
@ -51,11 +50,6 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
@click="${this._closeEditor}"
|
||||
></paper-icon-button>
|
||||
<div main-title>Edit Config</div>
|
||||
${this._hash
|
||||
? html`
|
||||
<span class="comments">Comments will be not be saved!</span>
|
||||
`
|
||||
: ""}
|
||||
<paper-button @click="${this._handleSave}">Save</paper-button>
|
||||
<ha-icon
|
||||
class="save-button
|
||||
@ -67,52 +61,27 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div class="content">
|
||||
<textarea
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
@input="${this._yamlChanged}"
|
||||
></textarea>
|
||||
<hui-yaml-editor @yaml-changed="${this._yamlChanged}">
|
||||
</hui-yaml-editor>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
const textArea = this.textArea;
|
||||
textArea.value = yaml.safeDump(this.lovelace!.config);
|
||||
textArea.addEventListener("keydown", (e) => {
|
||||
if (e.keyCode === 51) {
|
||||
this._hashAdded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode !== 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// tab was pressed, get caret position/selection
|
||||
const val = textArea.value;
|
||||
const start = textArea.selectionStart;
|
||||
const end = textArea.selectionEnd;
|
||||
|
||||
// set textarea value to: text before caret + tab + text after caret
|
||||
textArea.value =
|
||||
val.substring(0, start) + TAB_INSERT + val.substring(end);
|
||||
|
||||
// put caret at right position again
|
||||
textArea.selectionStart = textArea.selectionEnd =
|
||||
start + TAB_INSERT.length;
|
||||
});
|
||||
this.yamlEditor.value = yaml.safeDump(this.lovelace!.config);
|
||||
this.yamlEditor.codemirror.clearHistory();
|
||||
this._generation = this.yamlEditor.codemirror.changeGeneration(true);
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
--code-mirror-height: 100%;
|
||||
}
|
||||
|
||||
app-header-layout {
|
||||
height: 100vh;
|
||||
}
|
||||
@ -132,16 +101,8 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
height: calc(100vh - 68px);
|
||||
}
|
||||
|
||||
textarea {
|
||||
box-sizing: border-box;
|
||||
hui-code-editor {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
resize: none;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 12pt;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
@ -158,6 +119,20 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
];
|
||||
}
|
||||
|
||||
private _yamlChanged() {
|
||||
if (!this._generation) {
|
||||
return;
|
||||
}
|
||||
this._changed = !this.yamlEditor.codemirror.isClean(this._generation);
|
||||
if (this._changed && !window.onbeforeunload) {
|
||||
window.onbeforeunload = () => {
|
||||
return true;
|
||||
};
|
||||
} else if (!this._changed && window.onbeforeunload) {
|
||||
window.onbeforeunload = null;
|
||||
}
|
||||
}
|
||||
|
||||
private _closeEditor() {
|
||||
if (this._changed) {
|
||||
if (
|
||||
@ -173,10 +148,10 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
private async _handleSave() {
|
||||
this._saving = true;
|
||||
|
||||
if (this._hashAdded) {
|
||||
if (this.yamlEditor.hasComments) {
|
||||
if (
|
||||
!confirm(
|
||||
"Your config might contain comments, these will not be saved. Do you want to continue?"
|
||||
"Your config contains comment(s), these will not be saved. Do you want to continue?"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
@ -185,7 +160,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
|
||||
let value;
|
||||
try {
|
||||
value = yaml.safeLoad(this.textArea.value);
|
||||
value = yaml.safeLoad(this.yamlEditor.value);
|
||||
} catch (err) {
|
||||
alert(`Unable to parse YAML: ${err}`);
|
||||
this._saving = false;
|
||||
@ -202,25 +177,14 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
} catch (err) {
|
||||
alert(`Unable to save YAML: ${err}`);
|
||||
}
|
||||
this._generation = this.yamlEditor.codemirror.changeGeneration(true);
|
||||
window.onbeforeunload = null;
|
||||
this._saving = false;
|
||||
this._changed = false;
|
||||
this._hashAdded = false;
|
||||
}
|
||||
|
||||
private _yamlChanged() {
|
||||
this._hash = this._hashAdded || this.textArea.value.includes("#");
|
||||
if (this._changed) {
|
||||
return;
|
||||
}
|
||||
window.onbeforeunload = () => {
|
||||
return true;
|
||||
};
|
||||
this._changed = true;
|
||||
}
|
||||
|
||||
private get textArea(): HTMLTextAreaElement {
|
||||
return this.shadowRoot!.querySelector("textarea")!;
|
||||
private get yamlEditor(): HuiYamlEditor {
|
||||
return this.shadowRoot!.querySelector("hui-yaml-editor")!;
|
||||
}
|
||||
}
|
||||
|
||||
|
21
yarn.lock
21
yarn.lock
@ -1436,6 +1436,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/clone/-/clone-0.1.30.tgz#e7365648c1b42136a59c7d5040637b3b5c83b614"
|
||||
integrity sha1-5zZWSMG0ITalnH1QQGN7O1yDthQ=
|
||||
|
||||
"@types/codemirror@^0.0.71":
|
||||
version "0.0.71"
|
||||
resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.71.tgz#861f1bcb3100c0a064567c5400f2981cf4ae8ca7"
|
||||
integrity sha512-b2oEEnno1LIGKMR7uBEsr40al1UijF1HEpRn0+Yf1xOLl24iQgB7DBpZVMM7y54G5wCNoclDrRO65E6KHPNO2w==
|
||||
dependencies:
|
||||
"@types/tern" "*"
|
||||
|
||||
"@types/compression@^0.0.33":
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/compression/-/compression-0.0.33.tgz#95dc733a2339aa846381d7f1377792d2553dc27d"
|
||||
@ -1477,7 +1484,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-0.0.20.tgz#cae698714dd61ebee5ab3f2aeb9a34ba1011735a"
|
||||
integrity sha512-6dhZJLbA7aOwkYB2GDGdIqJ20wmHnkDzaxV9PJXe7O02I2dSFTERzRB6JrX6cWKaS+VqhhY7cQUMCbO5kloFUw==
|
||||
|
||||
"@types/estree@0.0.39":
|
||||
"@types/estree@*", "@types/estree@0.0.39":
|
||||
version "0.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||
@ -1850,6 +1857,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/tern@*":
|
||||
version "0.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.22.1.tgz#d96467553128794f42fbe7ba8f60b520acffb817"
|
||||
integrity sha512-CRzPRkg8hYLwunsj61r+rqPJQbiCIEQqlMMY/0k7krgIsoSaFgGg1ZH2f9qaR1YpenaMl6PnlTtUkCbNH/uo+A==
|
||||
dependencies:
|
||||
"@types/estree" "*"
|
||||
|
||||
"@types/through@*":
|
||||
version "0.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.29.tgz#72943aac922e179339c651fa34a4428a4d722f93"
|
||||
@ -4281,6 +4295,11 @@ code-point-at@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||
|
||||
codemirror@^5.43.0:
|
||||
version "5.43.0"
|
||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.43.0.tgz#2454b5e0f7005dc9945ab7b0d9594ccf233da040"
|
||||
integrity sha512-mljwQWUaWIf85I7QwTBryF2ASaIvmYAL4s5UCanCJFfKeXOKhrqdHWdHiZWAMNT+hjLTCnVx2S/SYTORIgxsgA==
|
||||
|
||||
collection-visit@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user