diff --git a/docs/configure-coderabbit.md b/docs/configure-coderabbit.md
index a2b35f10..e44c3936 100644
--- a/docs/configure-coderabbit.md
+++ b/docs/configure-coderabbit.md
@@ -10,6 +10,7 @@ sidebar_position: 3
```mdx-code-block
import SchemaViewer from "@site/src/components/SchemaViewer";
+import YamlEditor from "/src/components/YamlEditor/YamlEditor";
```
CodeRabbit offers various configuration options to tailor the reviews to your
@@ -56,6 +57,12 @@ chat:
auto_reply: true
```
+Write your configuration file in the below editor to validate:
+
+```mdx-code-block
+
+```
+
The configuration file can be used to set the following options:
```mdx-code-block
diff --git a/package-lock.json b/package-lock.json
index b7c323c4..a0f1c019 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,7 @@
"postcss": "^8.4.32",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
+ "react-ace": "^12.0.0",
"react-dom": "^18.0.0",
"tailwindcss": "^3.4.0"
},
@@ -3988,6 +3989,12 @@
"node": ">= 0.6"
}
},
+ "node_modules/ace-builds": {
+ "version": "1.35.4",
+ "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.35.4.tgz",
+ "integrity": "sha512-r0KQclhZ/uk5a4zOqRYQkJuQuu4vFMiA6VTj54Tk4nI1TUR3iEMMppZkWbNoWEgWwv4ciDloObb9Rf4V55Qgjw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/acorn": {
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
@@ -5747,6 +5754,12 @@
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
+ "node_modules/diff-match-patch": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
+ "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==",
+ "license": "Apache-2.0"
+ },
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -8316,6 +8329,18 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
+ "node_modules/lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "license": "MIT"
+ },
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -12216,6 +12241,23 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-ace": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-12.0.0.tgz",
+ "integrity": "sha512-PstU6CSMfYIJknb4su2Fa0WgLXzq2ufQgR6fjcSWuGT1hGTHkBzuKw+SncV8PuLCdSJBJc1VehPhyeXlWByG/g==",
+ "license": "MIT",
+ "dependencies": {
+ "ace-builds": "^1.32.8",
+ "diff-match-patch": "^1.0.5",
+ "lodash.get": "^4.4.2",
+ "lodash.isequal": "^4.5.0",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
diff --git a/package.json b/package.json
index 3c82d516..4f257c51 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"postcss": "^8.4.32",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
+ "react-ace": "^12.0.0",
"react-dom": "^18.0.0",
"tailwindcss": "^3.4.0"
},
diff --git a/src/components/YamlEditor/YamlEditor.jsx b/src/components/YamlEditor/YamlEditor.jsx
new file mode 100644
index 00000000..a60564ba
--- /dev/null
+++ b/src/components/YamlEditor/YamlEditor.jsx
@@ -0,0 +1,103 @@
+import { React, useState } from "react";
+
+import AceEditor from "react-ace";
+import "ace-builds/src-noconflict/theme-github";
+import "ace-builds/src-noconflict/ext-language_tools";
+
+import "ace-builds/webpack-resolver";
+import "ace-builds/src-noconflict/mode-yaml";
+
+import jsYaml from "js-yaml";
+
+import Ajv from "ajv";
+const ajv = new Ajv({ allErrors: true });
+
+import Schema from "../../../static/schema/schema.v2.json";
+
+export default function YamlEditor() {
+ const [annotations, setAnnotations] = useState([]);
+ const [value, setValue] = useState(
+ "# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json\n"
+ );
+ const validate = ajv.compile(Schema.definitions.schema);
+ function getRowFromPath(path) {
+ // Convert path to row number (0-based)
+ return path.split("/").length - 1;
+ }
+ function getLineNumber(yaml, path) {
+ const lines = yaml.split("\n");
+ const pathParts = path.split("/").filter(Boolean);
+ let currentObj = jsYaml.load(yaml);
+ let lineNumber = 0;
+
+ for (const part of pathParts) {
+ for (let i = lineNumber; i < lines.length; i++) {
+ if (lines[i].trim().startsWith(part + ":")) {
+ lineNumber = i;
+ break;
+ }
+ }
+ currentObj = currentObj[part];
+ }
+
+ return lineNumber;
+ }
+ function onChange(newValue) {
+ setValue(newValue);
+ try {
+ const doc = jsYaml.load(newValue, { strict: true });
+ const valid = validate(doc);
+
+ if (!valid && validate.errors) {
+ setAnnotations(
+ validate.errors.map((err) => ({
+ row: err.instancePath
+ ? getLineNumber(newValue, err.instancePath)
+ : 0,
+ column: 0,
+ text: `${err.keyword}: ${err.message} ${
+ err?.params?.allowedValues
+ ? `Allowed values: ${err.params.allowedValues.join(", ")}`
+ : ""
+ }`,
+ type: "error",
+ }))
+ );
+ } else {
+ setAnnotations([]);
+ }
+ } catch (err) {
+ setAnnotations([
+ {
+ row: err.instancePath ? getLineNumber(newValue, err.instancePath) : 0,
+ column: 0,
+ text:
+ `${err.keyword}: ${err.message} ${
+ err?.params?.allowedValues
+ ? `Allowed values: ${err.params.allowedValues.join(", ")}`
+ : ""
+ }` || "YAML parsing error",
+ type: "error",
+ },
+ ]);
+ }
+ }
+ return (
+
+ );
+}