diff --git a/.gitignore b/.gitignore
index 66cf19215..7b7a88b30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,6 +50,7 @@ bruno.iml
.vscode
.cursor
.claude
+.codex
# Playwright
/blob-report/
diff --git a/package-lock.json b/package-lock.json
index 6b6f4c0c1..0c24a8003 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6513,6 +6513,18 @@
"node": ">=16.9"
}
},
+ "node_modules/@profoundlogic/hogan": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@profoundlogic/hogan/-/hogan-3.0.4.tgz",
+ "integrity": "sha512-pmNVGuooS30Mm7YbZd5T7E5zYVO6D5Ct91sn4T39mUvMUc3sCGridcnhAufL1/Bz2QzAtzEn0agNrdk3+5yWzw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "nopt": "1.0.10"
+ },
+ "bin": {
+ "hulk": "bin/hulk"
+ }
+ },
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -10970,8 +10982,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
- "license": "ISC",
- "optional": true
+ "license": "ISC"
},
"node_modules/abort-controller": {
"version": "3.0.0",
@@ -14734,6 +14745,41 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/diff2html": {
+ "version": "3.4.56",
+ "resolved": "https://registry.npmjs.org/diff2html/-/diff2html-3.4.56.tgz",
+ "integrity": "sha512-u9gfn+BlbHcyO7vItCIC4z49LJDUt31tODzOfAuJ5R1E7IdlRL6KjugcB9zOpejD+XiR+dDZbsnHSQ3g6A/u8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@profoundlogic/hogan": "^3.0.4",
+ "diff": "^8.0.3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "highlight.js": "11.11.1"
+ }
+ },
+ "node_modules/diff2html/node_modules/diff": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
+ "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/diff2html/node_modules/highlight.js": {
+ "version": "11.11.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
+ "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/diffie-hellman": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@@ -22016,6 +22062,21 @@
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"license": "MIT"
},
+ "node_modules/nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
+ "license": "MIT",
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -30114,6 +30175,7 @@
"codemirror": "5.65.2",
"codemirror-graphql": "2.1.1",
"cookie": "0.7.1",
+ "diff2html": "^3.4.47",
"dompurify": "^3.2.4",
"escape-html": "^1.0.3",
"fast-fuzzy": "^1.12.0",
@@ -33470,6 +33532,7 @@
"chokidar": "^3.5.3",
"content-disposition": "^0.5.4",
"decomment": "^0.9.5",
+ "diff": "^8.0.3",
"dotenv": "^16.0.3",
"electron-is-dev": "^2.0.0",
"electron-notarize": "^1.2.2",
@@ -34845,6 +34908,15 @@
}
}
},
+ "packages/bruno-electron/node_modules/diff": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
+ "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"packages/bruno-electron/node_modules/dir-compare": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz",
diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json
index e7637dd11..e5c95f83f 100644
--- a/packages/bruno-app/package.json
+++ b/packages/bruno-app/package.json
@@ -27,6 +27,7 @@
"codemirror": "5.65.2",
"codemirror-graphql": "2.1.1",
"cookie": "0.7.1",
+ "diff2html": "^3.4.47",
"dompurify": "^3.2.4",
"escape-html": "^1.0.3",
"fast-fuzzy": "^1.12.0",
diff --git a/packages/bruno-app/public/static/diff2Html.js b/packages/bruno-app/public/static/diff2Html.js
new file mode 100644
index 000000000..15acb0d47
--- /dev/null
+++ b/packages/bruno-app/public/static/diff2Html.js
@@ -0,0 +1,3213 @@
+!(function (e, t) {
+ 'object' == typeof exports && 'object' == typeof module
+ ? (module.exports = t())
+ : 'function' == typeof define && define.amd
+ ? define('Diff2Html', [], t)
+ : 'object' == typeof exports
+ ? (exports.Diff2Html = t())
+ : (e.Diff2Html = t());
+})(this, () => {
+ return (
+ (e = {
+ 696: (e, t) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.convertChangesToDMP = function (e) {
+ for (var t, n, i = [], r = 0; r < e.length; r++)
+ (n = (t = e[r]).added ? 1 : t.removed ? -1 : 0), i.push([n, t.value]);
+ return i;
+ });
+ },
+ 826: (e, t) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.convertChangesToXML = function (e) {
+ for (var t = [], n = 0; n < e.length; n++) {
+ var i = e[n];
+ i.added ? t.push('') : i.removed && t.push(''),
+ t.push(
+ i.value.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"')
+ ),
+ i.added ? t.push('') : i.removed && t.push('');
+ }
+ return t.join('');
+ });
+ },
+ 976: (e, t, n) => {
+ 'use strict';
+ var i;
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffArrays = function (e, t, n) {
+ return r.diff(e, t, n);
+ }),
+ (t.arrayDiff = void 0);
+ var r = new ((i = n(913)) && i.__esModule ? i : { default: i }).default();
+ (t.arrayDiff = r),
+ (r.tokenize = function (e) {
+ return e.slice();
+ }),
+ (r.join = r.removeEmpty =
+ function (e) {
+ return e;
+ });
+ },
+ 913: (e, t) => {
+ 'use strict';
+ function n() {}
+ function i(e, t, n, i, r) {
+ for (var s = 0, o = t.length, a = 0, l = 0; s < o; s++) {
+ var c = t[s];
+ if (c.removed) {
+ if (((c.value = e.join(i.slice(l, l + c.count))), (l += c.count), s && t[s - 1].added)) {
+ var d = t[s - 1];
+ (t[s - 1] = t[s]), (t[s] = d);
+ }
+ } else {
+ if (!c.added && r) {
+ var f = n.slice(a, a + c.count);
+ (f = f.map(function (e, t) {
+ var n = i[l + t];
+ return n.length > e.length ? n : e;
+ })),
+ (c.value = e.join(f));
+ } else c.value = e.join(n.slice(a, a + c.count));
+ (a += c.count), c.added || (l += c.count);
+ }
+ }
+ var u = t[o - 1];
+ return (
+ o > 1 &&
+ 'string' == typeof u.value &&
+ (u.added || u.removed) &&
+ e.equals('', u.value) &&
+ ((t[o - 2].value += u.value), t.pop()),
+ t
+ );
+ }
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.default = n),
+ (n.prototype = {
+ diff: function (e, t) {
+ var n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {},
+ r = n.callback;
+ 'function' == typeof n && ((r = n), (n = {})), (this.options = n);
+ var s = this;
+ function o(e) {
+ return r
+ ? (setTimeout(function () {
+ r(void 0, e);
+ }, 0),
+ !0)
+ : e;
+ }
+ (e = this.castInput(e)), (t = this.castInput(t)), (e = this.removeEmpty(this.tokenize(e)));
+ var a = (t = this.removeEmpty(this.tokenize(t))).length,
+ l = e.length,
+ c = 1,
+ d = a + l;
+ n.maxEditLength && (d = Math.min(d, n.maxEditLength));
+ var f = [{ newPos: -1, components: [] }],
+ u = this.extractCommon(f[0], t, e, 0);
+ if (f[0].newPos + 1 >= a && u + 1 >= l) return o([{ value: this.join(t), count: t.length }]);
+ function h() {
+ for (var n = -1 * c; n <= c; n += 2) {
+ var r = void 0,
+ d = f[n - 1],
+ u = f[n + 1],
+ h = (u ? u.newPos : 0) - n;
+ d && (f[n - 1] = void 0);
+ var p = d && d.newPos + 1 < a,
+ b = u && 0 <= h && h < l;
+ if (p || b) {
+ if (
+ (!p || (b && d.newPos < u.newPos)
+ ? ((r = { newPos: (g = u).newPos, components: g.components.slice(0) }),
+ s.pushComponent(r.components, void 0, !0))
+ : ((r = d).newPos++, s.pushComponent(r.components, !0, void 0)),
+ (h = s.extractCommon(r, t, e, n)),
+ r.newPos + 1 >= a && h + 1 >= l)
+ )
+ return o(i(s, r.components, t, e, s.useLongestToken));
+ f[n] = r;
+ } else f[n] = void 0;
+ }
+ var g;
+ c++;
+ }
+ if (r)
+ !(function e() {
+ setTimeout(function () {
+ if (c > d) return r();
+ h() || e();
+ }, 0);
+ })();
+ else
+ for (; c <= d; ) {
+ var p = h();
+ if (p) return p;
+ }
+ },
+ pushComponent: function (e, t, n) {
+ var i = e[e.length - 1];
+ i && i.added === t && i.removed === n
+ ? (e[e.length - 1] = { count: i.count + 1, added: t, removed: n })
+ : e.push({ count: 1, added: t, removed: n });
+ },
+ extractCommon: function (e, t, n, i) {
+ for (
+ var r = t.length, s = n.length, o = e.newPos, a = o - i, l = 0;
+ o + 1 < r && a + 1 < s && this.equals(t[o + 1], n[a + 1]);
+
+ )
+ o++, a++, l++;
+ return l && e.components.push({ count: l }), (e.newPos = o), a;
+ },
+ equals: function (e, t) {
+ return this.options.comparator
+ ? this.options.comparator(e, t)
+ : e === t || (this.options.ignoreCase && e.toLowerCase() === t.toLowerCase());
+ },
+ removeEmpty: function (e) {
+ for (var t = [], n = 0; n < e.length; n++) e[n] && t.push(e[n]);
+ return t;
+ },
+ castInput: function (e) {
+ return e;
+ },
+ tokenize: function (e) {
+ return e.split('');
+ },
+ join: function (e) {
+ return e.join('');
+ }
+ });
+ },
+ 630: (e, t, n) => {
+ 'use strict';
+ var i;
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffChars = function (e, t, n) {
+ return r.diff(e, t, n);
+ }),
+ (t.characterDiff = void 0);
+ var r = new ((i = n(913)) && i.__esModule ? i : { default: i }).default();
+ t.characterDiff = r;
+ },
+ 852: (e, t, n) => {
+ 'use strict';
+ var i;
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffCss = function (e, t, n) {
+ return r.diff(e, t, n);
+ }),
+ (t.cssDiff = void 0);
+ var r = new ((i = n(913)) && i.__esModule ? i : { default: i }).default();
+ (t.cssDiff = r),
+ (r.tokenize = function (e) {
+ return e.split(/([{}:;,]|\s+)/);
+ });
+ },
+ 276: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffJson = function (e, t, n) {
+ return l.diff(e, t, n);
+ }),
+ (t.canonicalize = c),
+ (t.jsonDiff = void 0);
+ var i,
+ r = (i = n(913)) && i.__esModule ? i : { default: i },
+ s = n(187);
+ function o(e) {
+ return (
+ (o =
+ 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator
+ ? function (e) {
+ return typeof e;
+ }
+ : function (e) {
+ return e && 'function' == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype
+ ? 'symbol'
+ : typeof e;
+ }),
+ o(e)
+ );
+ }
+ var a = Object.prototype.toString,
+ l = new r.default();
+ function c(e, t, n, i, r) {
+ var s, l;
+ for (t = t || [], n = n || [], i && (e = i(r, e)), s = 0; s < t.length; s += 1) if (t[s] === e) return n[s];
+ if ('[object Array]' === a.call(e)) {
+ for (t.push(e), l = new Array(e.length), n.push(l), s = 0; s < e.length; s += 1) l[s] = c(e[s], t, n, i, r);
+ return t.pop(), n.pop(), l;
+ }
+ if ((e && e.toJSON && (e = e.toJSON()), 'object' === o(e) && null !== e)) {
+ t.push(e), (l = {}), n.push(l);
+ var d,
+ f = [];
+ for (d in e) e.hasOwnProperty(d) && f.push(d);
+ for (f.sort(), s = 0; s < f.length; s += 1) l[(d = f[s])] = c(e[d], t, n, i, d);
+ t.pop(), n.pop();
+ } else l = e;
+ return l;
+ }
+ (t.jsonDiff = l),
+ (l.useLongestToken = !0),
+ (l.tokenize = s.lineDiff.tokenize),
+ (l.castInput = function (e) {
+ var t = this.options,
+ n = t.undefinedReplacement,
+ i = t.stringifyReplacer,
+ r =
+ void 0 === i
+ ? function (e, t) {
+ return void 0 === t ? n : t;
+ }
+ : i;
+ return 'string' == typeof e ? e : JSON.stringify(c(e, null, null, r), r, ' ');
+ }),
+ (l.equals = function (e, t) {
+ return r.default.prototype.equals.call(l, e.replace(/,([\r\n])/g, '$1'), t.replace(/,([\r\n])/g, '$1'));
+ });
+ },
+ 187: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffLines = function (e, t, n) {
+ return o.diff(e, t, n);
+ }),
+ (t.diffTrimmedLines = function (e, t, n) {
+ var i = (0, s.generateOptions)(n, { ignoreWhitespace: !0 });
+ return o.diff(e, t, i);
+ }),
+ (t.lineDiff = void 0);
+ var i,
+ r = (i = n(913)) && i.__esModule ? i : { default: i },
+ s = n(9),
+ o = new r.default();
+ (t.lineDiff = o),
+ (o.tokenize = function (e) {
+ var t = [],
+ n = e.split(/(\n|\r\n)/);
+ n[n.length - 1] || n.pop();
+ for (var i = 0; i < n.length; i++) {
+ var r = n[i];
+ i % 2 && !this.options.newlineIsToken
+ ? (t[t.length - 1] += r)
+ : (this.options.ignoreWhitespace && (r = r.trim()), t.push(r));
+ }
+ return t;
+ });
+ },
+ 146: (e, t, n) => {
+ 'use strict';
+ var i;
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffSentences = function (e, t, n) {
+ return r.diff(e, t, n);
+ }),
+ (t.sentenceDiff = void 0);
+ var r = new ((i = n(913)) && i.__esModule ? i : { default: i }).default();
+ (t.sentenceDiff = r),
+ (r.tokenize = function (e) {
+ return e.split(/(\S.+?[.!?])(?=\s+|$)/);
+ });
+ },
+ 303: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffWords = function (e, t, n) {
+ return (n = (0, s.generateOptions)(n, { ignoreWhitespace: !0 })), l.diff(e, t, n);
+ }),
+ (t.diffWordsWithSpace = function (e, t, n) {
+ return l.diff(e, t, n);
+ }),
+ (t.wordDiff = void 0);
+ var i,
+ r = (i = n(913)) && i.__esModule ? i : { default: i },
+ s = n(9),
+ o = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/,
+ a = /\S/,
+ l = new r.default();
+ (t.wordDiff = l),
+ (l.equals = function (e, t) {
+ return (
+ this.options.ignoreCase && ((e = e.toLowerCase()), (t = t.toLowerCase())),
+ e === t || (this.options.ignoreWhitespace && !a.test(e) && !a.test(t))
+ );
+ }),
+ (l.tokenize = function (e) {
+ for (var t = e.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/), n = 0; n < t.length - 1; n++)
+ !t[n + 1] &&
+ t[n + 2] &&
+ o.test(t[n]) &&
+ o.test(t[n + 2]) &&
+ ((t[n] += t[n + 2]), t.splice(n + 1, 2), n--);
+ return t;
+ });
+ },
+ 785: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ Object.defineProperty(t, 'Diff', {
+ enumerable: !0,
+ get: function () {
+ return r.default;
+ }
+ }),
+ Object.defineProperty(t, 'diffChars', {
+ enumerable: !0,
+ get: function () {
+ return s.diffChars;
+ }
+ }),
+ Object.defineProperty(t, 'diffWords', {
+ enumerable: !0,
+ get: function () {
+ return o.diffWords;
+ }
+ }),
+ Object.defineProperty(t, 'diffWordsWithSpace', {
+ enumerable: !0,
+ get: function () {
+ return o.diffWordsWithSpace;
+ }
+ }),
+ Object.defineProperty(t, 'diffLines', {
+ enumerable: !0,
+ get: function () {
+ return a.diffLines;
+ }
+ }),
+ Object.defineProperty(t, 'diffTrimmedLines', {
+ enumerable: !0,
+ get: function () {
+ return a.diffTrimmedLines;
+ }
+ }),
+ Object.defineProperty(t, 'diffSentences', {
+ enumerable: !0,
+ get: function () {
+ return l.diffSentences;
+ }
+ }),
+ Object.defineProperty(t, 'diffCss', {
+ enumerable: !0,
+ get: function () {
+ return c.diffCss;
+ }
+ }),
+ Object.defineProperty(t, 'diffJson', {
+ enumerable: !0,
+ get: function () {
+ return d.diffJson;
+ }
+ }),
+ Object.defineProperty(t, 'canonicalize', {
+ enumerable: !0,
+ get: function () {
+ return d.canonicalize;
+ }
+ }),
+ Object.defineProperty(t, 'diffArrays', {
+ enumerable: !0,
+ get: function () {
+ return f.diffArrays;
+ }
+ }),
+ Object.defineProperty(t, 'applyPatch', {
+ enumerable: !0,
+ get: function () {
+ return u.applyPatch;
+ }
+ }),
+ Object.defineProperty(t, 'applyPatches', {
+ enumerable: !0,
+ get: function () {
+ return u.applyPatches;
+ }
+ }),
+ Object.defineProperty(t, 'parsePatch', {
+ enumerable: !0,
+ get: function () {
+ return h.parsePatch;
+ }
+ }),
+ Object.defineProperty(t, 'merge', {
+ enumerable: !0,
+ get: function () {
+ return p.merge;
+ }
+ }),
+ Object.defineProperty(t, 'structuredPatch', {
+ enumerable: !0,
+ get: function () {
+ return b.structuredPatch;
+ }
+ }),
+ Object.defineProperty(t, 'createTwoFilesPatch', {
+ enumerable: !0,
+ get: function () {
+ return b.createTwoFilesPatch;
+ }
+ }),
+ Object.defineProperty(t, 'createPatch', {
+ enumerable: !0,
+ get: function () {
+ return b.createPatch;
+ }
+ }),
+ Object.defineProperty(t, 'convertChangesToDMP', {
+ enumerable: !0,
+ get: function () {
+ return g.convertChangesToDMP;
+ }
+ }),
+ Object.defineProperty(t, 'convertChangesToXML', {
+ enumerable: !0,
+ get: function () {
+ return m.convertChangesToXML;
+ }
+ });
+ var i,
+ r = (i = n(913)) && i.__esModule ? i : { default: i },
+ s = n(630),
+ o = n(303),
+ a = n(187),
+ l = n(146),
+ c = n(852),
+ d = n(276),
+ f = n(976),
+ u = n(690),
+ h = n(719),
+ p = n(51),
+ b = n(286),
+ g = n(696),
+ m = n(826);
+ },
+ 690: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.applyPatch = o),
+ (t.applyPatches = function (e, t) {
+ 'string' == typeof e && (e = (0, r.parsePatch)(e));
+ var n = 0;
+ !(function i() {
+ var r = e[n++];
+ if (!r) return t.complete();
+ t.loadFile(r, function (e, n) {
+ if (e) return t.complete(e);
+ var s = o(n, r, t);
+ t.patched(r, s, function (e) {
+ if (e) return t.complete(e);
+ i();
+ });
+ });
+ })();
+ });
+ var i,
+ r = n(719),
+ s = (i = n(169)) && i.__esModule ? i : { default: i };
+ function o(e, t) {
+ var n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {};
+ if (('string' == typeof t && (t = (0, r.parsePatch)(t)), Array.isArray(t))) {
+ if (t.length > 1) throw new Error('applyPatch only works with a single input.');
+ t = t[0];
+ }
+ var i,
+ o,
+ a = e.split(/\r\n|[\n\v\f\r\x85]/),
+ l = e.match(/\r\n|[\n\v\f\r\x85]/g) || [],
+ c = t.hunks,
+ d =
+ n.compareLine ||
+ function (e, t, n, i) {
+ return t === i;
+ },
+ f = 0,
+ u = n.fuzzFactor || 0,
+ h = 0,
+ p = 0;
+ function b(e, t) {
+ for (var n = 0; n < e.lines.length; n++) {
+ var i = e.lines[n],
+ r = i.length > 0 ? i[0] : ' ',
+ s = i.length > 0 ? i.substr(1) : i;
+ if (' ' === r || '-' === r) {
+ if (!d(t + 1, a[t], r, s) && ++f > u) return !1;
+ t++;
+ }
+ }
+ return !0;
+ }
+ for (var g = 0; g < c.length; g++) {
+ for (
+ var m = c[g], v = a.length - m.oldLines, y = 0, w = p + m.oldStart - 1, S = (0, s.default)(w, h, v);
+ void 0 !== y;
+ y = S()
+ )
+ if (b(m, w + y)) {
+ m.offset = p += y;
+ break;
+ }
+ if (void 0 === y) return !1;
+ h = m.offset + m.oldStart + m.oldLines;
+ }
+ for (var L = 0, C = 0; C < c.length; C++) {
+ var x = c[C],
+ O = x.oldStart + x.offset + L - 1;
+ L += x.newLines - x.oldLines;
+ for (var T = 0; T < x.lines.length; T++) {
+ var j = x.lines[T],
+ _ = j.length > 0 ? j[0] : ' ',
+ N = j.length > 0 ? j.substr(1) : j,
+ P = x.linedelimiters[T];
+ if (' ' === _) O++;
+ else if ('-' === _) a.splice(O, 1), l.splice(O, 1);
+ else if ('+' === _) a.splice(O, 0, N), l.splice(O, 0, P), O++;
+ else if ('\\' === _) {
+ var E = x.lines[T - 1] ? x.lines[T - 1][0] : null;
+ '+' === E ? (i = !0) : '-' === E && (o = !0);
+ }
+ }
+ }
+ if (i) for (; !a[a.length - 1]; ) a.pop(), l.pop();
+ else o && (a.push(''), l.push('\n'));
+ for (var M = 0; M < a.length - 1; M++) a[M] = a[M] + l[M];
+ return a.join('');
+ }
+ },
+ 286: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.structuredPatch = o),
+ (t.formatPatch = a),
+ (t.createTwoFilesPatch = l),
+ (t.createPatch = function (e, t, n, i, r, s) {
+ return l(e, e, t, n, i, r, s);
+ });
+ var i = n(187);
+ function r(e) {
+ return (
+ (function (e) {
+ if (Array.isArray(e)) return s(e);
+ })(e) ||
+ (function (e) {
+ if ('undefined' != typeof Symbol && Symbol.iterator in Object(e)) return Array.from(e);
+ })(e) ||
+ (function (e, t) {
+ if (e) {
+ if ('string' == typeof e) return s(e, t);
+ var n = Object.prototype.toString.call(e).slice(8, -1);
+ return (
+ 'Object' === n && e.constructor && (n = e.constructor.name),
+ 'Map' === n || 'Set' === n
+ ? Array.from(e)
+ : 'Arguments' === n || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)
+ ? s(e, t)
+ : void 0
+ );
+ }
+ })(e) ||
+ (function () {
+ throw new TypeError(
+ 'Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.'
+ );
+ })()
+ );
+ }
+ function s(e, t) {
+ (null == t || t > e.length) && (t = e.length);
+ for (var n = 0, i = new Array(t); n < t; n++) i[n] = e[n];
+ return i;
+ }
+ function o(e, t, n, s, o, a, l) {
+ l || (l = {}), void 0 === l.context && (l.context = 4);
+ var c = (0, i.diffLines)(n, s, l);
+ if (c) {
+ c.push({ value: '', lines: [] });
+ for (
+ var d = [],
+ f = 0,
+ u = 0,
+ h = [],
+ p = 1,
+ b = 1,
+ g = function (e) {
+ var t = c[e],
+ i = t.lines || t.value.replace(/\n$/, '').split('\n');
+ if (((t.lines = i), t.added || t.removed)) {
+ var o;
+ if (!f) {
+ var a = c[e - 1];
+ (f = p),
+ (u = b),
+ a &&
+ ((h = l.context > 0 ? v(a.lines.slice(-l.context)) : []), (f -= h.length), (u -= h.length));
+ }
+ (o = h).push.apply(
+ o,
+ r(
+ i.map(function (e) {
+ return (t.added ? '+' : '-') + e;
+ })
+ )
+ ),
+ t.added ? (b += i.length) : (p += i.length);
+ } else {
+ if (f)
+ if (i.length <= 2 * l.context && e < c.length - 2) {
+ var g;
+ (g = h).push.apply(g, r(v(i)));
+ } else {
+ var m,
+ y = Math.min(i.length, l.context);
+ (m = h).push.apply(m, r(v(i.slice(0, y))));
+ var w = { oldStart: f, oldLines: p - f + y, newStart: u, newLines: b - u + y, lines: h };
+ if (e >= c.length - 2 && i.length <= l.context) {
+ var S = /\n$/.test(n),
+ L = /\n$/.test(s),
+ C = 0 == i.length && h.length > w.oldLines;
+ !S && C && n.length > 0 && h.splice(w.oldLines, 0, '\\ No newline at end of file'),
+ ((S || C) && L) || h.push('\\ No newline at end of file');
+ }
+ d.push(w), (f = 0), (u = 0), (h = []);
+ }
+ (p += i.length), (b += i.length);
+ }
+ },
+ m = 0;
+ m < c.length;
+ m++
+ )
+ g(m);
+ return { oldFileName: e, newFileName: t, oldHeader: o, newHeader: a, hunks: d };
+ }
+ function v(e) {
+ return e.map(function (e) {
+ return ' ' + e;
+ });
+ }
+ }
+ function a(e) {
+ var t = [];
+ e.oldFileName == e.newFileName && t.push('Index: ' + e.oldFileName),
+ t.push('==================================================================='),
+ t.push('--- ' + e.oldFileName + (void 0 === e.oldHeader ? '' : '\t' + e.oldHeader)),
+ t.push('+++ ' + e.newFileName + (void 0 === e.newHeader ? '' : '\t' + e.newHeader));
+ for (var n = 0; n < e.hunks.length; n++) {
+ var i = e.hunks[n];
+ 0 === i.oldLines && (i.oldStart -= 1),
+ 0 === i.newLines && (i.newStart -= 1),
+ t.push('@@ -' + i.oldStart + ',' + i.oldLines + ' +' + i.newStart + ',' + i.newLines + ' @@'),
+ t.push.apply(t, i.lines);
+ }
+ return t.join('\n') + '\n';
+ }
+ function l(e, t, n, i, r, s, l) {
+ return a(o(e, t, n, i, r, s, l));
+ }
+ },
+ 51: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.calcLineCount = l),
+ (t.merge = function (e, t, n) {
+ (e = c(e, n)), (t = c(t, n));
+ var i = {};
+ (e.index || t.index) && (i.index = e.index || t.index),
+ (e.newFileName || t.newFileName) &&
+ (d(e)
+ ? d(t)
+ ? ((i.oldFileName = f(i, e.oldFileName, t.oldFileName)),
+ (i.newFileName = f(i, e.newFileName, t.newFileName)),
+ (i.oldHeader = f(i, e.oldHeader, t.oldHeader)),
+ (i.newHeader = f(i, e.newHeader, t.newHeader)))
+ : ((i.oldFileName = e.oldFileName),
+ (i.newFileName = e.newFileName),
+ (i.oldHeader = e.oldHeader),
+ (i.newHeader = e.newHeader))
+ : ((i.oldFileName = t.oldFileName || e.oldFileName),
+ (i.newFileName = t.newFileName || e.newFileName),
+ (i.oldHeader = t.oldHeader || e.oldHeader),
+ (i.newHeader = t.newHeader || e.newHeader))),
+ (i.hunks = []);
+ for (var r = 0, s = 0, o = 0, a = 0; r < e.hunks.length || s < t.hunks.length; ) {
+ var l = e.hunks[r] || { oldStart: 1 / 0 },
+ b = t.hunks[s] || { oldStart: 1 / 0 };
+ if (u(l, b)) i.hunks.push(h(l, o)), r++, (a += l.newLines - l.oldLines);
+ else if (u(b, l)) i.hunks.push(h(b, a)), s++, (o += b.newLines - b.oldLines);
+ else {
+ var g = {
+ oldStart: Math.min(l.oldStart, b.oldStart),
+ oldLines: 0,
+ newStart: Math.min(l.newStart + o, b.oldStart + a),
+ newLines: 0,
+ lines: []
+ };
+ p(g, l.oldStart, l.lines, b.oldStart, b.lines), s++, r++, i.hunks.push(g);
+ }
+ }
+ return i;
+ });
+ var i = n(286),
+ r = n(719),
+ s = n(780);
+ function o(e) {
+ return (
+ (function (e) {
+ if (Array.isArray(e)) return a(e);
+ })(e) ||
+ (function (e) {
+ if ('undefined' != typeof Symbol && Symbol.iterator in Object(e)) return Array.from(e);
+ })(e) ||
+ (function (e, t) {
+ if (e) {
+ if ('string' == typeof e) return a(e, t);
+ var n = Object.prototype.toString.call(e).slice(8, -1);
+ return (
+ 'Object' === n && e.constructor && (n = e.constructor.name),
+ 'Map' === n || 'Set' === n
+ ? Array.from(e)
+ : 'Arguments' === n || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)
+ ? a(e, t)
+ : void 0
+ );
+ }
+ })(e) ||
+ (function () {
+ throw new TypeError(
+ 'Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.'
+ );
+ })()
+ );
+ }
+ function a(e, t) {
+ (null == t || t > e.length) && (t = e.length);
+ for (var n = 0, i = new Array(t); n < t; n++) i[n] = e[n];
+ return i;
+ }
+ function l(e) {
+ var t = C(e.lines),
+ n = t.oldLines,
+ i = t.newLines;
+ void 0 !== n ? (e.oldLines = n) : delete e.oldLines, void 0 !== i ? (e.newLines = i) : delete e.newLines;
+ }
+ function c(e, t) {
+ if ('string' == typeof e) {
+ if (/^@@/m.test(e) || /^Index:/m.test(e)) return (0, r.parsePatch)(e)[0];
+ if (!t) throw new Error('Must provide a base reference or pass in a patch');
+ return (0, i.structuredPatch)(void 0, void 0, t, e);
+ }
+ return e;
+ }
+ function d(e) {
+ return e.newFileName && e.newFileName !== e.oldFileName;
+ }
+ function f(e, t, n) {
+ return t === n ? t : ((e.conflict = !0), { mine: t, theirs: n });
+ }
+ function u(e, t) {
+ return e.oldStart < t.oldStart && e.oldStart + e.oldLines < t.oldStart;
+ }
+ function h(e, t) {
+ return {
+ oldStart: e.oldStart,
+ oldLines: e.oldLines,
+ newStart: e.newStart + t,
+ newLines: e.newLines,
+ lines: e.lines
+ };
+ }
+ function p(e, t, n, i, r) {
+ var s = { offset: t, lines: n, index: 0 },
+ a = { offset: i, lines: r, index: 0 };
+ for (v(e, s, a), v(e, a, s); s.index < s.lines.length && a.index < a.lines.length; ) {
+ var c = s.lines[s.index],
+ d = a.lines[a.index];
+ if (('-' !== c[0] && '+' !== c[0]) || ('-' !== d[0] && '+' !== d[0]))
+ if ('+' === c[0] && ' ' === d[0]) {
+ var f;
+ (f = e.lines).push.apply(f, o(w(s)));
+ } else if ('+' === d[0] && ' ' === c[0]) {
+ var u;
+ (u = e.lines).push.apply(u, o(w(a)));
+ } else
+ '-' === c[0] && ' ' === d[0]
+ ? g(e, s, a)
+ : '-' === d[0] && ' ' === c[0]
+ ? g(e, a, s, !0)
+ : c === d
+ ? (e.lines.push(c), s.index++, a.index++)
+ : m(e, w(s), w(a));
+ else b(e, s, a);
+ }
+ y(e, s), y(e, a), l(e);
+ }
+ function b(e, t, n) {
+ var i = w(t),
+ r = w(n);
+ if (S(i) && S(r)) {
+ var a, l;
+ if ((0, s.arrayStartsWith)(i, r) && L(n, i, i.length - r.length))
+ return void (a = e.lines).push.apply(a, o(i));
+ if ((0, s.arrayStartsWith)(r, i) && L(t, r, r.length - i.length))
+ return void (l = e.lines).push.apply(l, o(r));
+ } else if ((0, s.arrayEqual)(i, r)) {
+ var c;
+ return void (c = e.lines).push.apply(c, o(i));
+ }
+ m(e, i, r);
+ }
+ function g(e, t, n, i) {
+ var r,
+ s = w(t),
+ a = (function (e, t) {
+ for (var n = [], i = [], r = 0, s = !1, o = !1; r < t.length && e.index < e.lines.length; ) {
+ var a = e.lines[e.index],
+ l = t[r];
+ if ('+' === l[0]) break;
+ if (((s = s || ' ' !== a[0]), i.push(l), r++, '+' === a[0]))
+ for (o = !0; '+' === a[0]; ) n.push(a), (a = e.lines[++e.index]);
+ l.substr(1) === a.substr(1) ? (n.push(a), e.index++) : (o = !0);
+ }
+ if (('+' === (t[r] || '')[0] && s && (o = !0), o)) return n;
+ for (; r < t.length; ) i.push(t[r++]);
+ return { merged: i, changes: n };
+ })(n, s);
+ a.merged ? (r = e.lines).push.apply(r, o(a.merged)) : m(e, i ? a : s, i ? s : a);
+ }
+ function m(e, t, n) {
+ (e.conflict = !0), e.lines.push({ conflict: !0, mine: t, theirs: n });
+ }
+ function v(e, t, n) {
+ for (; t.offset < n.offset && t.index < t.lines.length; ) {
+ var i = t.lines[t.index++];
+ e.lines.push(i), t.offset++;
+ }
+ }
+ function y(e, t) {
+ for (; t.index < t.lines.length; ) {
+ var n = t.lines[t.index++];
+ e.lines.push(n);
+ }
+ }
+ function w(e) {
+ for (var t = [], n = e.lines[e.index][0]; e.index < e.lines.length; ) {
+ var i = e.lines[e.index];
+ if (('-' === n && '+' === i[0] && (n = '+'), n !== i[0])) break;
+ t.push(i), e.index++;
+ }
+ return t;
+ }
+ function S(e) {
+ return e.reduce(function (e, t) {
+ return e && '-' === t[0];
+ }, !0);
+ }
+ function L(e, t, n) {
+ for (var i = 0; i < n; i++) {
+ var r = t[t.length - n + i].substr(1);
+ if (e.lines[e.index + i] !== ' ' + r) return !1;
+ }
+ return (e.index += n), !0;
+ }
+ function C(e) {
+ var t = 0,
+ n = 0;
+ return (
+ e.forEach(function (e) {
+ if ('string' != typeof e) {
+ var i = C(e.mine),
+ r = C(e.theirs);
+ void 0 !== t && (i.oldLines === r.oldLines ? (t += i.oldLines) : (t = void 0)),
+ void 0 !== n && (i.newLines === r.newLines ? (n += i.newLines) : (n = void 0));
+ } else void 0 === n || ('+' !== e[0] && ' ' !== e[0]) || n++, void 0 === t || ('-' !== e[0] && ' ' !== e[0]) || t++;
+ }),
+ { oldLines: t, newLines: n }
+ );
+ }
+ },
+ 719: (e, t) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.parsePatch = function (e) {
+ var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {},
+ n = e.split(/\r\n|[\n\v\f\r\x85]/),
+ i = e.match(/\r\n|[\n\v\f\r\x85]/g) || [],
+ r = [],
+ s = 0;
+ function o() {
+ var e = {};
+ for (r.push(e); s < n.length; ) {
+ var i = n[s];
+ if (/^(\-\-\-|\+\+\+|@@)\s/.test(i)) break;
+ var o = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(i);
+ o && (e.index = o[1]), s++;
+ }
+ for (a(e), a(e), e.hunks = []; s < n.length; ) {
+ var c = n[s];
+ if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(c)) break;
+ if (/^@@/.test(c)) e.hunks.push(l());
+ else {
+ if (c && t.strict) throw new Error('Unknown line ' + (s + 1) + ' ' + JSON.stringify(c));
+ s++;
+ }
+ }
+ }
+ function a(e) {
+ var t = /^(---|\+\+\+)\s+(.*)$/.exec(n[s]);
+ if (t) {
+ var i = '---' === t[1] ? 'old' : 'new',
+ r = t[2].split('\t', 2),
+ o = r[0].replace(/\\\\/g, '\\');
+ /^".*"$/.test(o) && (o = o.substr(1, o.length - 2)),
+ (e[i + 'FileName'] = o),
+ (e[i + 'Header'] = (r[1] || '').trim()),
+ s++;
+ }
+ }
+ function l() {
+ var e = s,
+ r = n[s++].split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/),
+ o = {
+ oldStart: +r[1],
+ oldLines: void 0 === r[2] ? 1 : +r[2],
+ newStart: +r[3],
+ newLines: void 0 === r[4] ? 1 : +r[4],
+ lines: [],
+ linedelimiters: []
+ };
+ 0 === o.oldLines && (o.oldStart += 1), 0 === o.newLines && (o.newStart += 1);
+ for (
+ var a = 0, l = 0;
+ s < n.length &&
+ !(
+ 0 === n[s].indexOf('--- ') &&
+ s + 2 < n.length &&
+ 0 === n[s + 1].indexOf('+++ ') &&
+ 0 === n[s + 2].indexOf('@@')
+ );
+ s++
+ ) {
+ var c = 0 == n[s].length && s != n.length - 1 ? ' ' : n[s][0];
+ if ('+' !== c && '-' !== c && ' ' !== c && '\\' !== c) break;
+ o.lines.push(n[s]),
+ o.linedelimiters.push(i[s] || '\n'),
+ '+' === c ? a++ : '-' === c ? l++ : ' ' === c && (a++, l++);
+ }
+ if ((a || 1 !== o.newLines || (o.newLines = 0), l || 1 !== o.oldLines || (o.oldLines = 0), t.strict)) {
+ if (a !== o.newLines) throw new Error('Added line count did not match for hunk at line ' + (e + 1));
+ if (l !== o.oldLines) throw new Error('Removed line count did not match for hunk at line ' + (e + 1));
+ }
+ return o;
+ }
+ for (; s < n.length; ) o();
+ return r;
+ });
+ },
+ 780: (e, t) => {
+ 'use strict';
+ function n(e, t) {
+ if (t.length > e.length) return !1;
+ for (var n = 0; n < t.length; n++) if (t[n] !== e[n]) return !1;
+ return !0;
+ }
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.arrayEqual = function (e, t) {
+ return e.length === t.length && n(e, t);
+ }),
+ (t.arrayStartsWith = n);
+ },
+ 169: (e, t) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.default = function (e, t, n) {
+ var i = !0,
+ r = !1,
+ s = !1,
+ o = 1;
+ return function a() {
+ if (i && !s) {
+ if ((r ? o++ : (i = !1), e + o <= n)) return o;
+ s = !0;
+ }
+ if (!r) return s || (i = !0), t <= e - o ? -o++ : ((r = !0), a());
+ };
+ });
+ },
+ 9: (e, t) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.generateOptions = function (e, t) {
+ if ('function' == typeof e) t.callback = e;
+ else if (e) for (var n in e) e.hasOwnProperty(n) && (t[n] = e[n]);
+ return t;
+ });
+ },
+ 397: (e, t) => {
+ !(function (e) {
+ var t = /\S/,
+ n = /\"/g,
+ i = /\n/g,
+ r = /\r/g,
+ s = /\\/g,
+ o = /\u2028/,
+ a = /\u2029/;
+ function l(e) {
+ return e.trim ? e.trim() : e.replace(/^\s*|\s*$/g, '');
+ }
+ function c(e, t, n) {
+ if (t.charAt(n) != e.charAt(0)) return !1;
+ for (var i = 1, r = e.length; i < r; i++) if (t.charAt(n + i) != e.charAt(i)) return !1;
+ return !0;
+ }
+ (e.tags = { '#': 1, '^': 2, '<': 3, $: 4, '/': 5, '!': 6, '>': 7, '=': 8, _v: 9, '{': 10, '&': 11, _t: 12 }),
+ (e.scan = function (n, i) {
+ var r,
+ s = n.length,
+ o = 0,
+ a = null,
+ d = null,
+ f = '',
+ u = [],
+ h = !1,
+ p = 0,
+ b = 0,
+ g = '{{',
+ m = '}}';
+ function v() {
+ f.length > 0 && (u.push({ tag: '_t', text: new String(f) }), (f = ''));
+ }
+ function y(n, i) {
+ if (
+ (v(),
+ n &&
+ (function () {
+ for (var n = !0, i = b; i < u.length; i++)
+ if (!(n = e.tags[u[i].tag] < e.tags._v || ('_t' == u[i].tag && null === u[i].text.match(t))))
+ return !1;
+ return n;
+ })())
+ )
+ for (var r, s = b; s < u.length; s++)
+ u[s].text && ((r = u[s + 1]) && '>' == r.tag && (r.indent = u[s].text.toString()), u.splice(s, 1));
+ else i || u.push({ tag: '\n' });
+ (h = !1), (b = u.length);
+ }
+ function w(e, t) {
+ var n = '=' + m,
+ i = e.indexOf(n, t),
+ r = l(e.substring(e.indexOf('=', t) + 1, i)).split(' ');
+ return (g = r[0]), (m = r[r.length - 1]), i + n.length - 1;
+ }
+ for (i && ((i = i.split(' ')), (g = i[0]), (m = i[1])), p = 0; p < s; p++)
+ 0 == o
+ ? c(g, n, p)
+ ? (--p, v(), (o = 1))
+ : '\n' == n.charAt(p)
+ ? y(h)
+ : (f += n.charAt(p))
+ : 1 == o
+ ? ((p += g.length - 1),
+ '=' == (a = (d = e.tags[n.charAt(p + 1)]) ? n.charAt(p + 1) : '_v')
+ ? ((p = w(n, p)), (o = 0))
+ : (d && p++, (o = 2)),
+ (h = p))
+ : c(m, n, p)
+ ? (u.push({ tag: a, n: l(f), otag: g, ctag: m, i: '/' == a ? h - g.length : p + m.length }),
+ (f = ''),
+ (p += m.length - 1),
+ (o = 0),
+ '{' == a &&
+ ('}}' == m
+ ? p++
+ : '}' === (r = u[u.length - 1]).n.substr(r.n.length - 1) &&
+ (r.n = r.n.substring(0, r.n.length - 1))))
+ : (f += n.charAt(p));
+ return y(h, !0), u;
+ });
+ var d = { _t: !0, '\n': !0, $: !0, '/': !0 };
+ function f(t, n, i, r) {
+ var s,
+ o = [],
+ a = null,
+ l = null;
+ for (s = i[i.length - 1]; t.length > 0; ) {
+ if (((l = t.shift()), s && '<' == s.tag && !(l.tag in d)))
+ throw new Error('Illegal content in < super tag.');
+ if (e.tags[l.tag] <= e.tags.$ || u(l, r)) i.push(l), (l.nodes = f(t, l.tag, i, r));
+ else {
+ if ('/' == l.tag) {
+ if (0 === i.length) throw new Error('Closing tag without opener: /' + l.n);
+ if (((a = i.pop()), l.n != a.n && !h(l.n, a.n, r)))
+ throw new Error('Nesting error: ' + a.n + ' vs. ' + l.n);
+ return (a.end = l.i), o;
+ }
+ '\n' == l.tag && (l.last = 0 == t.length || '\n' == t[0].tag);
+ }
+ o.push(l);
+ }
+ if (i.length > 0) throw new Error('missing closing tag: ' + i.pop().n);
+ return o;
+ }
+ function u(e, t) {
+ for (var n = 0, i = t.length; n < i; n++) if (t[n].o == e.n) return (e.tag = '#'), !0;
+ }
+ function h(e, t, n) {
+ for (var i = 0, r = n.length; i < r; i++) if (n[i].c == e && n[i].o == t) return !0;
+ }
+ function p(e) {
+ var t = [];
+ for (var n in e.partials)
+ t.push('"' + g(n) + '":{name:"' + g(e.partials[n].name) + '", ' + p(e.partials[n]) + '}');
+ return (
+ 'partials: {' +
+ t.join(',') +
+ '}, subs: ' +
+ (function (e) {
+ var t = [];
+ for (var n in e) t.push('"' + g(n) + '": function(c,p,t,i) {' + e[n] + '}');
+ return '{ ' + t.join(',') + ' }';
+ })(e.subs)
+ );
+ }
+ e.stringify = function (t, n, i) {
+ return '{code: function (c,p,i) { ' + e.wrapMain(t.code) + ' },' + p(t) + '}';
+ };
+ var b = 0;
+ function g(e) {
+ return e
+ .replace(s, '\\\\')
+ .replace(n, '\\"')
+ .replace(i, '\\n')
+ .replace(r, '\\r')
+ .replace(o, '\\u2028')
+ .replace(a, '\\u2029');
+ }
+ function m(e) {
+ return ~e.indexOf('.') ? 'd' : 'f';
+ }
+ function v(e, t) {
+ var n = '<' + (t.prefix || '') + e.n + b++;
+ return (
+ (t.partials[n] = { name: e.n, partials: {} }),
+ (t.code += 't.b(t.rp("' + g(n) + '",c,p,"' + (e.indent || '') + '"));'),
+ n
+ );
+ }
+ function y(e, t) {
+ t.code += 't.b(t.t(t.' + m(e.n) + '("' + g(e.n) + '",c,p,0)));';
+ }
+ function w(e) {
+ return 't.b(' + e + ');';
+ }
+ (e.generate = function (t, n, i) {
+ b = 0;
+ var r = { code: '', subs: {}, partials: {} };
+ return e.walk(t, r), i.asString ? this.stringify(r, n, i) : this.makeTemplate(r, n, i);
+ }),
+ (e.wrapMain = function (e) {
+ return 'var t=this;t.b(i=i||"");' + e + 'return t.fl();';
+ }),
+ (e.template = e.Template),
+ (e.makeTemplate = function (e, t, n) {
+ var i = this.makePartials(e);
+ return (i.code = new Function('c', 'p', 'i', this.wrapMain(e.code))), new this.template(i, t, this, n);
+ }),
+ (e.makePartials = function (e) {
+ var t,
+ n = { subs: {}, partials: e.partials, name: e.name };
+ for (t in n.partials) n.partials[t] = this.makePartials(n.partials[t]);
+ for (t in e.subs) n.subs[t] = new Function('c', 'p', 't', 'i', e.subs[t]);
+ return n;
+ }),
+ (e.codegen = {
+ '#': function (t, n) {
+ (n.code +=
+ 'if(t.s(t.' +
+ m(t.n) +
+ '("' +
+ g(t.n) +
+ '",c,p,1),c,p,0,' +
+ t.i +
+ ',' +
+ t.end +
+ ',"' +
+ t.otag +
+ ' ' +
+ t.ctag +
+ '")){t.rs(c,p,function(c,p,t){'),
+ e.walk(t.nodes, n),
+ (n.code += '});c.pop();}');
+ },
+ '^': function (t, n) {
+ (n.code += 'if(!t.s(t.' + m(t.n) + '("' + g(t.n) + '",c,p,1),c,p,1,0,0,"")){'),
+ e.walk(t.nodes, n),
+ (n.code += '};');
+ },
+ '>': v,
+ '<': function (t, n) {
+ var i = { partials: {}, code: '', subs: {}, inPartial: !0 };
+ e.walk(t.nodes, i);
+ var r = n.partials[v(t, n)];
+ (r.subs = i.subs), (r.partials = i.partials);
+ },
+ $: function (t, n) {
+ var i = { subs: {}, code: '', partials: n.partials, prefix: t.n };
+ e.walk(t.nodes, i), (n.subs[t.n] = i.code), n.inPartial || (n.code += 't.sub("' + g(t.n) + '",c,p,i);');
+ },
+ '\n': function (e, t) {
+ t.code += w('"\\n"' + (e.last ? '' : ' + i'));
+ },
+ _v: function (e, t) {
+ t.code += 't.b(t.v(t.' + m(e.n) + '("' + g(e.n) + '",c,p,0)));';
+ },
+ _t: function (e, t) {
+ t.code += w('"' + g(e.text) + '"');
+ },
+ '{': y,
+ '&': y
+ }),
+ (e.walk = function (t, n) {
+ for (var i, r = 0, s = t.length; r < s; r++) (i = e.codegen[t[r].tag]) && i(t[r], n);
+ return n;
+ }),
+ (e.parse = function (e, t, n) {
+ return f(e, 0, [], (n = n || {}).sectionTags || []);
+ }),
+ (e.cache = {}),
+ (e.cacheKey = function (e, t) {
+ return [e, !!t.asString, !!t.disableLambda, t.delimiters, !!t.modelGet].join('||');
+ }),
+ (e.compile = function (t, n) {
+ n = n || {};
+ var i = e.cacheKey(t, n),
+ r = this.cache[i];
+ if (r) {
+ var s = r.partials;
+ for (var o in s) delete s[o].instance;
+ return r;
+ }
+ return (r = this.generate(this.parse(this.scan(t, n.delimiters), t, n), t, n)), (this.cache[i] = r);
+ });
+ })(t);
+ },
+ 485: (e, t, n) => {
+ var i = n(397);
+ (i.Template = n(882).Template), (i.template = i.Template), (e.exports = i);
+ },
+ 882: (e, t) => {
+ !(function (e) {
+ function t(e, t, n) {
+ var i;
+ return (
+ t &&
+ 'object' == typeof t &&
+ (void 0 !== t[e] ? (i = t[e]) : n && t.get && 'function' == typeof t.get && (i = t.get(e))),
+ i
+ );
+ }
+ (e.Template = function (e, t, n, i) {
+ (e = e || {}),
+ (this.r = e.code || this.r),
+ (this.c = n),
+ (this.options = i || {}),
+ (this.text = t || ''),
+ (this.partials = e.partials || {}),
+ (this.subs = e.subs || {}),
+ (this.buf = '');
+ }),
+ (e.Template.prototype = {
+ r: function (e, t, n) {
+ return '';
+ },
+ v: function (e) {
+ return (
+ (e = l(e)),
+ a.test(e)
+ ? e
+ .replace(n, '&')
+ .replace(i, '<')
+ .replace(r, '>')
+ .replace(s, ''')
+ .replace(o, '"')
+ : e
+ );
+ },
+ t: l,
+ render: function (e, t, n) {
+ return this.ri([e], t || {}, n);
+ },
+ ri: function (e, t, n) {
+ return this.r(e, t, n);
+ },
+ ep: function (e, t) {
+ var n = this.partials[e],
+ i = t[n.name];
+ if (n.instance && n.base == i) return n.instance;
+ if ('string' == typeof i) {
+ if (!this.c) throw new Error('No compiler available.');
+ i = this.c.compile(i, this.options);
+ }
+ if (!i) return null;
+ if (((this.partials[e].base = i), n.subs)) {
+ for (key in (t.stackText || (t.stackText = {}), n.subs))
+ t.stackText[key] ||
+ (t.stackText[key] =
+ void 0 !== this.activeSub && t.stackText[this.activeSub]
+ ? t.stackText[this.activeSub]
+ : this.text);
+ i = (function (e, t, n, i, r, s) {
+ function o() {}
+ function a() {}
+ var l;
+ (o.prototype = e), (a.prototype = e.subs);
+ var c = new o();
+ for (l in ((c.subs = new a()),
+ (c.subsText = {}),
+ (c.buf = ''),
+ (i = i || {}),
+ (c.stackSubs = i),
+ (c.subsText = s),
+ t))
+ i[l] || (i[l] = t[l]);
+ for (l in i) c.subs[l] = i[l];
+ for (l in ((r = r || {}), (c.stackPartials = r), n)) r[l] || (r[l] = n[l]);
+ for (l in r) c.partials[l] = r[l];
+ return c;
+ })(i, n.subs, n.partials, this.stackSubs, this.stackPartials, t.stackText);
+ }
+ return (this.partials[e].instance = i), i;
+ },
+ rp: function (e, t, n, i) {
+ var r = this.ep(e, n);
+ return r ? r.ri(t, n, i) : '';
+ },
+ rs: function (e, t, n) {
+ var i = e[e.length - 1];
+ if (c(i)) for (var r = 0; r < i.length; r++) e.push(i[r]), n(e, t, this), e.pop();
+ else n(e, t, this);
+ },
+ s: function (e, t, n, i, r, s, o) {
+ var a;
+ return (
+ (!c(e) || 0 !== e.length) &&
+ ('function' == typeof e && (e = this.ms(e, t, n, i, r, s, o)),
+ (a = !!e),
+ !i && a && t && t.push('object' == typeof e ? e : t[t.length - 1]),
+ a)
+ );
+ },
+ d: function (e, n, i, r) {
+ var s,
+ o = e.split('.'),
+ a = this.f(o[0], n, i, r),
+ l = this.options.modelGet,
+ d = null;
+ if ('.' === e && c(n[n.length - 2])) a = n[n.length - 1];
+ else for (var f = 1; f < o.length; f++) void 0 !== (s = t(o[f], a, l)) ? ((d = a), (a = s)) : (a = '');
+ return !(r && !a) && (r || 'function' != typeof a || (n.push(d), (a = this.mv(a, n, i)), n.pop()), a);
+ },
+ f: function (e, n, i, r) {
+ for (var s = !1, o = !1, a = this.options.modelGet, l = n.length - 1; l >= 0; l--)
+ if (void 0 !== (s = t(e, n[l], a))) {
+ o = !0;
+ break;
+ }
+ return o ? (r || 'function' != typeof s || (s = this.mv(s, n, i)), s) : !r && '';
+ },
+ ls: function (e, t, n, i, r) {
+ var s = this.options.delimiters;
+ return (
+ (this.options.delimiters = r),
+ this.b(this.ct(l(e.call(t, i)), t, n)),
+ (this.options.delimiters = s),
+ !1
+ );
+ },
+ ct: function (e, t, n) {
+ if (this.options.disableLambda) throw new Error('Lambda features disabled.');
+ return this.c.compile(e, this.options).render(t, n);
+ },
+ b: function (e) {
+ this.buf += e;
+ },
+ fl: function () {
+ var e = this.buf;
+ return (this.buf = ''), e;
+ },
+ ms: function (e, t, n, i, r, s, o) {
+ var a,
+ l = t[t.length - 1],
+ c = e.call(l);
+ return 'function' == typeof c
+ ? !!i ||
+ ((a =
+ this.activeSub && this.subsText && this.subsText[this.activeSub]
+ ? this.subsText[this.activeSub]
+ : this.text),
+ this.ls(c, l, n, a.substring(r, s), o))
+ : c;
+ },
+ mv: function (e, t, n) {
+ var i = t[t.length - 1],
+ r = e.call(i);
+ return 'function' == typeof r ? this.ct(l(r.call(i)), i, n) : r;
+ },
+ sub: function (e, t, n, i) {
+ var r = this.subs[e];
+ r && ((this.activeSub = e), r(t, n, this, i), (this.activeSub = !1));
+ }
+ });
+ var n = /&/g,
+ i = //g,
+ s = /\'/g,
+ o = /\"/g,
+ a = /[&<>\"\']/;
+ function l(e) {
+ return String(null == e ? '' : e);
+ }
+ var c =
+ Array.isArray ||
+ function (e) {
+ return '[object Array]' === Object.prototype.toString.call(e);
+ };
+ })(t);
+ },
+ 468: (e, t, n) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }), (t.parse = void 0);
+ const i = n(699),
+ r = n(593);
+ function s(e, t) {
+ const n = e.split('.');
+ return n.length > 1 ? n[n.length - 1] : t;
+ }
+ function o(e, t) {
+ return t.reduce((t, n) => t || e.startsWith(n), !1);
+ }
+ const a = ['a/', 'b/', 'i/', 'w/', 'c/', 'o/'];
+ function l(e, t, n) {
+ const i = void 0 !== n ? [...a, n] : a,
+ s = t ? new RegExp(`^${(0, r.escapeForRegExp)(t)} "?(.+?)"?$`) : new RegExp('^"?(.+?)"?$'),
+ [, o = ''] = s.exec(e) || [],
+ l = i.find((e) => 0 === o.indexOf(e));
+ return (l ? o.slice(l.length) : o).replace(
+ /\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)? [+-]\d{4}.*$/,
+ ''
+ );
+ }
+ t.parse = function (e, t = {}) {
+ const n = [];
+ let r = null,
+ a = null,
+ c = null,
+ d = null,
+ f = null,
+ u = null,
+ h = null;
+ const p = '--- ',
+ b = '+++ ',
+ g = '@@',
+ m = /^old mode (\d{6})/,
+ v = /^new mode (\d{6})/,
+ y = /^deleted file mode (\d{6})/,
+ w = /^new file mode (\d{6})/,
+ S = /^copy from "?(.+)"?/,
+ L = /^copy to "?(.+)"?/,
+ C = /^rename from "?(.+)"?/,
+ x = /^rename to "?(.+)"?/,
+ O = /^similarity index (\d+)%/,
+ T = /^dissimilarity index (\d+)%/,
+ j = /^index ([\da-z]+)\.\.([\da-z]+)\s*(\d{6})?/,
+ _ = /^Binary files (.*) and (.*) differ/,
+ N = /^GIT binary patch/,
+ P = /^index ([\da-z]+),([\da-z]+)\.\.([\da-z]+)/,
+ E = /^mode (\d{6}),(\d{6})\.\.(\d{6})/,
+ M = /^new file mode (\d{6})/,
+ H = /^deleted file mode (\d{6}),(\d{6})/,
+ k = e
+ .replace(/\\ No newline at end of file/g, '')
+ .replace(/\r\n?/g, '\n')
+ .split('\n');
+ function D() {
+ null !== a && null !== r && (r.blocks.push(a), (a = null));
+ }
+ function F() {
+ null !== r &&
+ (r.oldName || null === u || (r.oldName = u),
+ r.newName || null === h || (r.newName = h),
+ r.newName && (n.push(r), (r = null))),
+ (u = null),
+ (h = null);
+ }
+ function I() {
+ D(), F(), (r = { blocks: [], deletedLines: 0, addedLines: 0 });
+ }
+ function A(e) {
+ let t;
+ D(),
+ null !== r &&
+ ((t = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@.*/.exec(e))
+ ? ((r.isCombined = !1), (c = parseInt(t[1], 10)), (f = parseInt(t[2], 10)))
+ : (t = /^@@@ -(\d+)(?:,\d+)? -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@@.*/.exec(e))
+ ? ((r.isCombined = !0), (c = parseInt(t[1], 10)), (d = parseInt(t[2], 10)), (f = parseInt(t[3], 10)))
+ : (e.startsWith(g) && console.error('Failed to parse lines, starting in 0!'),
+ (c = 0),
+ (f = 0),
+ (r.isCombined = !1))),
+ (a = { lines: [], oldStartLine: c, oldStartLine2: d, newStartLine: f, header: e });
+ }
+ return (
+ k.forEach((e, d) => {
+ if (!e || e.startsWith('*')) return;
+ let D;
+ const F = k[d - 1],
+ R = k[d + 1],
+ W = k[d + 2];
+ if (e.startsWith('diff --git') || e.startsWith('diff --combined')) {
+ if (
+ (I(),
+ (D = /^diff --git "?([a-ciow]\/.+)"? "?([a-ciow]\/.+)"?/.exec(e)) &&
+ ((u = l(D[1], void 0, t.dstPrefix)), (h = l(D[2], void 0, t.srcPrefix))),
+ null === r)
+ )
+ throw new Error('Where is my file !!!');
+ return void (r.isGitDiff = !0);
+ }
+ if (e.startsWith('Binary files') && !(null == r ? void 0 : r.isGitDiff)) {
+ if (
+ (I(),
+ (D = /^Binary files "?([a-ciow]\/.+)"? and "?([a-ciow]\/.+)"? differ/.exec(e)) &&
+ ((u = l(D[1], void 0, t.dstPrefix)), (h = l(D[2], void 0, t.srcPrefix))),
+ null === r)
+ )
+ throw new Error('Where is my file !!!');
+ return void (r.isBinary = !0);
+ }
+ if (
+ ((!r || (!r.isGitDiff && r && e.startsWith(p) && R.startsWith(b) && W.startsWith(g))) && I(),
+ null == r ? void 0 : r.isTooBig)
+ )
+ return;
+ if (
+ r &&
+ (('number' == typeof t.diffMaxChanges && r.addedLines + r.deletedLines > t.diffMaxChanges) ||
+ ('number' == typeof t.diffMaxLineLength && e.length > t.diffMaxLineLength))
+ )
+ return (
+ (r.isTooBig = !0),
+ (r.addedLines = 0),
+ (r.deletedLines = 0),
+ (r.blocks = []),
+ (a = null),
+ void A(
+ 'function' == typeof t.diffTooBigMessage
+ ? t.diffTooBigMessage(n.length)
+ : 'Diff too big to be displayed'
+ )
+ );
+ if ((e.startsWith(p) && R.startsWith(b)) || (e.startsWith(b) && F.startsWith(p))) {
+ if (
+ r &&
+ !r.oldName &&
+ e.startsWith('--- ') &&
+ (D = (function (e, t) {
+ return l(e, '---', t);
+ })(e, t.srcPrefix))
+ )
+ return (r.oldName = D), void (r.language = s(r.oldName, r.language));
+ if (
+ r &&
+ !r.newName &&
+ e.startsWith('+++ ') &&
+ (D = (function (e, t) {
+ return l(e, '+++', t);
+ })(e, t.dstPrefix))
+ )
+ return (r.newName = D), void (r.language = s(r.newName, r.language));
+ }
+ if (r && (e.startsWith(g) || (r.isGitDiff && r.oldName && r.newName && !a))) return void A(e);
+ if (a && (e.startsWith('+') || e.startsWith('-') || e.startsWith(' ')))
+ return void (function (e) {
+ if (null === r || null === a || null === c || null === f) return;
+ const t = { content: e },
+ n = r.isCombined ? ['+ ', ' +', '++'] : ['+'],
+ s = r.isCombined ? ['- ', ' -', '--'] : ['-'];
+ o(e, n)
+ ? (r.addedLines++, (t.type = i.LineType.INSERT), (t.oldNumber = void 0), (t.newNumber = f++))
+ : o(e, s)
+ ? (r.deletedLines++, (t.type = i.LineType.DELETE), (t.oldNumber = c++), (t.newNumber = void 0))
+ : ((t.type = i.LineType.CONTEXT), (t.oldNumber = c++), (t.newNumber = f++)),
+ a.lines.push(t);
+ })(e);
+ const B = !(function (e, t) {
+ let n = t;
+ for (; n < k.length - 3; ) {
+ if (e.startsWith('diff')) return !1;
+ if (k[n].startsWith(p) && k[n + 1].startsWith(b) && k[n + 2].startsWith(g)) return !0;
+ n++;
+ }
+ return !1;
+ })(e, d);
+ if (null === r) throw new Error('Where is my file !!!');
+ (D = m.exec(e))
+ ? (r.oldMode = D[1])
+ : (D = v.exec(e))
+ ? (r.newMode = D[1])
+ : (D = y.exec(e))
+ ? ((r.deletedFileMode = D[1]), (r.isDeleted = !0))
+ : (D = w.exec(e))
+ ? ((r.newFileMode = D[1]), (r.isNew = !0))
+ : (D = S.exec(e))
+ ? (B && (r.oldName = D[1]), (r.isCopy = !0))
+ : (D = L.exec(e))
+ ? (B && (r.newName = D[1]), (r.isCopy = !0))
+ : (D = C.exec(e))
+ ? (B && (r.oldName = D[1]), (r.isRename = !0))
+ : (D = x.exec(e))
+ ? (B && (r.newName = D[1]), (r.isRename = !0))
+ : (D = _.exec(e))
+ ? ((r.isBinary = !0),
+ (r.oldName = l(D[1], void 0, t.srcPrefix)),
+ (r.newName = l(D[2], void 0, t.dstPrefix)),
+ A('Binary file'))
+ : N.test(e)
+ ? ((r.isBinary = !0), A(e))
+ : (D = O.exec(e))
+ ? (r.unchangedPercentage = parseInt(D[1], 10))
+ : (D = T.exec(e))
+ ? (r.changedPercentage = parseInt(D[1], 10))
+ : (D = j.exec(e))
+ ? ((r.checksumBefore = D[1]), (r.checksumAfter = D[2]), D[3] && (r.mode = D[3]))
+ : (D = P.exec(e))
+ ? ((r.checksumBefore = [D[2], D[3]]), (r.checksumAfter = D[1]))
+ : (D = E.exec(e))
+ ? ((r.oldMode = [D[2], D[3]]), (r.newMode = D[1]))
+ : (D = M.exec(e))
+ ? ((r.newFileMode = D[1]), (r.isNew = !0))
+ : (D = H.exec(e)) && ((r.deletedFileMode = D[1]), (r.isDeleted = !0));
+ }),
+ D(),
+ F(),
+ n
+ );
+ };
+ },
+ 979: function (e, t, n) {
+ 'use strict';
+ var i =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (e, t, n, i) {
+ void 0 === i && (i = n);
+ var r = Object.getOwnPropertyDescriptor(t, n);
+ (r && !('get' in r ? !t.__esModule : r.writable || r.configurable)) ||
+ (r = {
+ enumerable: !0,
+ get: function () {
+ return t[n];
+ }
+ }),
+ Object.defineProperty(e, i, r);
+ }
+ : function (e, t, n, i) {
+ void 0 === i && (i = n), (e[i] = t[n]);
+ }),
+ r =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (e, t) {
+ Object.defineProperty(e, 'default', { enumerable: !0, value: t });
+ }
+ : function (e, t) {
+ e.default = t;
+ }),
+ s =
+ (this && this.__importStar) ||
+ function (e) {
+ if (e && e.__esModule) return e;
+ var t = {};
+ if (null != e)
+ for (var n in e) 'default' !== n && Object.prototype.hasOwnProperty.call(e, n) && i(t, e, n);
+ return r(t, e), t;
+ };
+ Object.defineProperty(t, '__esModule', { value: !0 }), (t.defaultTemplates = void 0);
+ const o = s(n(485));
+ (t.defaultTemplates = {}),
+ (t.defaultTemplates['file-summary-line'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(i.rp(''),
+ i.b(i.v(i.f('fileName', e, t, 0))),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.v(i.f('addedLines', e, t, 0))),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.v(i.f('deletedLines', e, t, 0))),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(''),
+ i.fl()
+ );
+ },
+ partials: { ''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.t(i.f('files', e, t, 0))),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b(''),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['generic-block-header'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' | '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.s(i.f('blockHeader', e, t, 1), e, t, 0, 156, 173, '{{ }}') &&
+ (i.rs(e, t, function (e, t, n) {
+ n.b(n.t(n.f('blockHeader', e, t, 0)));
+ }),
+ e.pop()),
+ i.s(i.f('blockHeader', e, t, 1), e, t, 1, 0, 0, '') || i.b(' '),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' | '),
+ i.b('\n' + n),
+ i.b('
'),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['generic-empty-diff'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' | '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' File without changes'),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' | '),
+ i.b('\n' + n),
+ i.b('
'),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['generic-file-path'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(i.rp(''),
+ i.b(i.v(i.f('fileDiffName', e, t, 0))),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(i.rp(''),
+ i.b('\n' + n),
+ i.b(''),
+ i.fl()
+ );
+ },
+ partials: {
+ ''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.t(i.f('lineNumber', e, t, 0))),
+ i.b('\n' + n),
+ i.b(' | '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.s(i.f('prefix', e, t, 1), e, t, 0, 162, 238, '{{ }}') &&
+ (i.rs(e, t, function (e, t, i) {
+ i.b(' '),
+ i.b(i.t(i.f('prefix', e, t, 0))),
+ i.b(''),
+ i.b('\n' + n);
+ }),
+ e.pop()),
+ i.s(i.f('prefix', e, t, 1), e, t, 1, 0, 0, '') ||
+ (i.b(' '), i.b('\n' + n)),
+ i.s(i.f('content', e, t, 1), e, t, 0, 371, 445, '{{ }}') &&
+ (i.rs(e, t, function (e, t, i) {
+ i.b(' '),
+ i.b(i.t(i.f('content', e, t, 0))),
+ i.b(''),
+ i.b('\n' + n);
+ }),
+ e.pop()),
+ i.s(i.f('content', e, t, 1), e, t, 1, 0, 0, '') ||
+ (i.b(' '), i.b('\n' + n)),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' | '),
+ i.b('\n' + n),
+ i.b(''),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['generic-wrapper'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.t(i.f('content', e, t, 0))),
+ i.b('\n' + n),
+ i.b('
'),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['icon-file-added'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(
+ ''),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['icon-file-changed'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['icon-file-deleted'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['icon-file-renamed'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['icon-file'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(
+ ''),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['line-by-line-file-diff'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.t(i.f('diffs', e, t, 0))),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['line-by-line-numbers'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.b(i.v(i.f('oldNumber', e, t, 0))),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b(''),
+ i.b(i.v(i.f('newNumber', e, t, 0))),
+ i.b('
'),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['side-by-side-file-diff'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')),
+ i.b(''),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.t(i.d('diffs.left', e, t, 0))),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b(i.t(i.d('diffs.right', e, t, 0))),
+ i.b('\n' + n),
+ i.b(' '),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.b('\n' + n),
+ i.b('
'),
+ i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['tag-file-added'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return i.b((n = n || '')), i.b('ADDED'), i.fl();
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['tag-file-changed'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')), i.b('CHANGED'), i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['tag-file-deleted'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return (
+ i.b((n = n || '')), i.b('DELETED'), i.fl()
+ );
+ },
+ partials: {},
+ subs: {}
+ })),
+ (t.defaultTemplates['tag-file-renamed'] = new o.Template({
+ code: function (e, t, n) {
+ var i = this;
+ return i.b((n = n || '')), i.b('RENAMED'), i.fl();
+ },
+ partials: {},
+ subs: {}
+ }));
+ },
+ 834: function (e, t, n) {
+ 'use strict';
+ var i =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (e, t, n, i) {
+ void 0 === i && (i = n);
+ var r = Object.getOwnPropertyDescriptor(t, n);
+ (r && !('get' in r ? !t.__esModule : r.writable || r.configurable)) ||
+ (r = {
+ enumerable: !0,
+ get: function () {
+ return t[n];
+ }
+ }),
+ Object.defineProperty(e, i, r);
+ }
+ : function (e, t, n, i) {
+ void 0 === i && (i = n), (e[i] = t[n]);
+ }),
+ r =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (e, t) {
+ Object.defineProperty(e, 'default', { enumerable: !0, value: t });
+ }
+ : function (e, t) {
+ e.default = t;
+ }),
+ s =
+ (this && this.__importStar) ||
+ function (e) {
+ if (e && e.__esModule) return e;
+ var t = {};
+ if (null != e)
+ for (var n in e) 'default' !== n && Object.prototype.hasOwnProperty.call(e, n) && i(t, e, n);
+ return r(t, e), t;
+ },
+ o =
+ (this && this.__importDefault) ||
+ function (e) {
+ return e && e.__esModule ? e : { default: e };
+ };
+ Object.defineProperty(t, '__esModule', { value: !0 }), (t.html = t.parse = t.defaultDiff2HtmlConfig = void 0);
+ const a = s(n(468)),
+ l = n(479),
+ c = s(n(378)),
+ d = s(n(170)),
+ f = n(699),
+ u = o(n(63));
+ (t.defaultDiff2HtmlConfig = Object.assign(
+ Object.assign(Object.assign({}, c.defaultLineByLineRendererConfig), d.defaultSideBySideRendererConfig),
+ { outputFormat: f.OutputFormatType.LINE_BY_LINE, drawFileList: !0 }
+ )),
+ (t.parse = function (e, n = {}) {
+ return a.parse(e, Object.assign(Object.assign({}, t.defaultDiff2HtmlConfig), n));
+ }),
+ (t.html = function (e, n = {}) {
+ const i = Object.assign(Object.assign({}, t.defaultDiff2HtmlConfig), n),
+ r = 'string' == typeof e ? a.parse(e, i) : e,
+ s = new u.default(i),
+ { colorScheme: o } = i,
+ f = { colorScheme: o };
+ return (
+ (i.drawFileList ? new l.FileListRenderer(s, f).render(r) : '') +
+ ('side-by-side' === i.outputFormat ? new d.default(s, i).render(r) : new c.default(s, i).render(r))
+ );
+ });
+ },
+ 479: function (e, t, n) {
+ 'use strict';
+ var i =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (e, t, n, i) {
+ void 0 === i && (i = n);
+ var r = Object.getOwnPropertyDescriptor(t, n);
+ (r && !('get' in r ? !t.__esModule : r.writable || r.configurable)) ||
+ (r = {
+ enumerable: !0,
+ get: function () {
+ return t[n];
+ }
+ }),
+ Object.defineProperty(e, i, r);
+ }
+ : function (e, t, n, i) {
+ void 0 === i && (i = n), (e[i] = t[n]);
+ }),
+ r =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (e, t) {
+ Object.defineProperty(e, 'default', { enumerable: !0, value: t });
+ }
+ : function (e, t) {
+ e.default = t;
+ }),
+ s =
+ (this && this.__importStar) ||
+ function (e) {
+ if (e && e.__esModule) return e;
+ var t = {};
+ if (null != e)
+ for (var n in e) 'default' !== n && Object.prototype.hasOwnProperty.call(e, n) && i(t, e, n);
+ return r(t, e), t;
+ };
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.FileListRenderer = t.defaultFileListRendererConfig = void 0);
+ const o = s(n(741)),
+ a = 'file-summary';
+ (t.defaultFileListRendererConfig = { colorScheme: o.defaultRenderConfig.colorScheme }),
+ (t.FileListRenderer = class {
+ constructor(e, n = {}) {
+ (this.hoganUtils = e),
+ (this.config = Object.assign(Object.assign({}, t.defaultFileListRendererConfig), n));
+ }
+ render(e) {
+ const t = e
+ .map((e) =>
+ this.hoganUtils.render(
+ a,
+ 'line',
+ {
+ fileHtmlId: o.getHtmlId(e),
+ oldName: e.oldName,
+ newName: e.newName,
+ fileName: o.filenameDiff(e),
+ deletedLines: '-' + e.deletedLines,
+ addedLines: '+' + e.addedLines
+ },
+ { fileIcon: this.hoganUtils.template('icon', o.getFileIcon(e)) }
+ )
+ )
+ .join('\n');
+ return this.hoganUtils.render(a, 'wrapper', {
+ colorScheme: o.colorSchemeToCss(this.config.colorScheme),
+ filesNumber: e.length,
+ files: t
+ });
+ }
+ });
+ },
+ 63: function (e, t, n) {
+ 'use strict';
+ var i =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (e, t, n, i) {
+ void 0 === i && (i = n);
+ var r = Object.getOwnPropertyDescriptor(t, n);
+ (r && !('get' in r ? !t.__esModule : r.writable || r.configurable)) ||
+ (r = {
+ enumerable: !0,
+ get: function () {
+ return t[n];
+ }
+ }),
+ Object.defineProperty(e, i, r);
+ }
+ : function (e, t, n, i) {
+ void 0 === i && (i = n), (e[i] = t[n]);
+ }),
+ r =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (e, t) {
+ Object.defineProperty(e, 'default', { enumerable: !0, value: t });
+ }
+ : function (e, t) {
+ e.default = t;
+ }),
+ s =
+ (this && this.__importStar) ||
+ function (e) {
+ if (e && e.__esModule) return e;
+ var t = {};
+ if (null != e)
+ for (var n in e) 'default' !== n && Object.prototype.hasOwnProperty.call(e, n) && i(t, e, n);
+ return r(t, e), t;
+ };
+ Object.defineProperty(t, '__esModule', { value: !0 });
+ const o = s(n(485)),
+ a = n(979);
+ t.default = class {
+ constructor({ compiledTemplates: e = {}, rawTemplates: t = {} }) {
+ const n = Object.entries(t).reduce((e, [t, n]) => {
+ const i = o.compile(n, { asString: !1 });
+ return Object.assign(Object.assign({}, e), { [t]: i });
+ }, {});
+ this.preCompiledTemplates = Object.assign(Object.assign(Object.assign({}, a.defaultTemplates), e), n);
+ }
+ static compile(e) {
+ return o.compile(e, { asString: !1 });
+ }
+ render(e, t, n, i, r) {
+ const s = this.templateKey(e, t);
+ try {
+ return this.preCompiledTemplates[s].render(n, i, r);
+ } catch (e) {
+ throw new Error(`Could not find template to render '${s}'`);
+ }
+ }
+ template(e, t) {
+ return this.preCompiledTemplates[this.templateKey(e, t)];
+ }
+ templateKey(e, t) {
+ return `${e}-${t}`;
+ }
+ };
+ },
+ 378: function (e, t, n) {
+ 'use strict';
+ var i =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (e, t, n, i) {
+ void 0 === i && (i = n);
+ var r = Object.getOwnPropertyDescriptor(t, n);
+ (r && !('get' in r ? !t.__esModule : r.writable || r.configurable)) ||
+ (r = {
+ enumerable: !0,
+ get: function () {
+ return t[n];
+ }
+ }),
+ Object.defineProperty(e, i, r);
+ }
+ : function (e, t, n, i) {
+ void 0 === i && (i = n), (e[i] = t[n]);
+ }),
+ r =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (e, t) {
+ Object.defineProperty(e, 'default', { enumerable: !0, value: t });
+ }
+ : function (e, t) {
+ e.default = t;
+ }),
+ s =
+ (this && this.__importStar) ||
+ function (e) {
+ if (e && e.__esModule) return e;
+ var t = {};
+ if (null != e)
+ for (var n in e) 'default' !== n && Object.prototype.hasOwnProperty.call(e, n) && i(t, e, n);
+ return r(t, e), t;
+ };
+ Object.defineProperty(t, '__esModule', { value: !0 }), (t.defaultLineByLineRendererConfig = void 0);
+ const o = s(n(483)),
+ a = s(n(741)),
+ l = n(699);
+ t.defaultLineByLineRendererConfig = Object.assign(Object.assign({}, a.defaultRenderConfig), {
+ renderNothingWhenEmpty: !1,
+ matchingMaxComparisons: 2500,
+ maxLineSizeInBlockForComparison: 200
+ });
+ const c = 'generic',
+ d = 'line-by-line';
+ t.default = class {
+ constructor(e, n = {}) {
+ (this.hoganUtils = e),
+ (this.config = Object.assign(Object.assign({}, t.defaultLineByLineRendererConfig), n));
+ }
+ render(e) {
+ const t = e
+ .map((e) => {
+ let t;
+ return (
+ (t = e.blocks.length ? this.generateFileHtml(e) : this.generateEmptyDiff()),
+ this.makeFileDiffHtml(e, t)
+ );
+ })
+ .join('\n');
+ return this.hoganUtils.render(c, 'wrapper', {
+ colorScheme: a.colorSchemeToCss(this.config.colorScheme),
+ content: t
+ });
+ }
+ makeFileDiffHtml(e, t) {
+ if (this.config.renderNothingWhenEmpty && Array.isArray(e.blocks) && 0 === e.blocks.length) return '';
+ const n = this.hoganUtils.template(d, 'file-diff'),
+ i = this.hoganUtils.template(c, 'file-path'),
+ r = this.hoganUtils.template('icon', 'file'),
+ s = this.hoganUtils.template('tag', a.getFileIcon(e));
+ return n.render({
+ file: e,
+ fileHtmlId: a.getHtmlId(e),
+ diffs: t,
+ filePath: i.render({ fileDiffName: a.filenameDiff(e) }, { fileIcon: r, fileTag: s })
+ });
+ }
+ generateEmptyDiff() {
+ return this.hoganUtils.render(c, 'empty-diff', {
+ contentClass: 'd2h-code-line',
+ CSSLineClass: a.CSSLineClass
+ });
+ }
+ generateFileHtml(e) {
+ const t = o.newMatcherFn(o.newDistanceFn((t) => a.deconstructLine(t.content, e.isCombined).content));
+ return e.blocks
+ .map((n) => {
+ let i = this.hoganUtils.render(c, 'block-header', {
+ CSSLineClass: a.CSSLineClass,
+ blockHeader: e.isTooBig ? n.header : a.escapeForHtml(n.header),
+ lineClass: 'd2h-code-linenumber',
+ contentClass: 'd2h-code-line'
+ });
+ return (
+ this.applyLineGroupping(n).forEach(([n, r, s]) => {
+ if (r.length && s.length && !n.length)
+ this.applyRematchMatching(r, s, t).map(([t, n]) => {
+ const { left: r, right: s } = this.processChangedLines(e, e.isCombined, t, n);
+ (i += r), (i += s);
+ });
+ else if (n.length)
+ n.forEach((t) => {
+ const { prefix: n, content: r } = a.deconstructLine(t.content, e.isCombined);
+ i += this.generateSingleLineHtml(e, {
+ type: a.CSSLineClass.CONTEXT,
+ prefix: n,
+ content: r,
+ oldNumber: t.oldNumber,
+ newNumber: t.newNumber
+ });
+ });
+ else if (r.length || s.length) {
+ const { left: t, right: n } = this.processChangedLines(e, e.isCombined, r, s);
+ (i += t), (i += n);
+ } else console.error('Unknown state reached while processing groups of lines', n, r, s);
+ }),
+ i
+ );
+ })
+ .join('\n');
+ }
+ applyLineGroupping(e) {
+ const t = [];
+ let n = [],
+ i = [];
+ for (let r = 0; r < e.lines.length; r++) {
+ const s = e.lines[r];
+ ((s.type !== l.LineType.INSERT && i.length) || (s.type === l.LineType.CONTEXT && n.length > 0)) &&
+ (t.push([[], n, i]), (n = []), (i = [])),
+ s.type === l.LineType.CONTEXT
+ ? t.push([[s], [], []])
+ : s.type === l.LineType.INSERT && 0 === n.length
+ ? t.push([[], [], [s]])
+ : s.type === l.LineType.INSERT && n.length > 0
+ ? i.push(s)
+ : s.type === l.LineType.DELETE && n.push(s);
+ }
+ return (n.length || i.length) && (t.push([[], n, i]), (n = []), (i = [])), t;
+ }
+ applyRematchMatching(e, t, n) {
+ const i = e.length * t.length,
+ r = Math.max.apply(null, [0].concat(e.concat(t).map((e) => e.content.length)));
+ return i < this.config.matchingMaxComparisons &&
+ r < this.config.maxLineSizeInBlockForComparison &&
+ ('lines' === this.config.matching || 'words' === this.config.matching)
+ ? n(e, t)
+ : [[e, t]];
+ }
+ processChangedLines(e, t, n, i) {
+ const r = { right: '', left: '' },
+ s = Math.max(n.length, i.length);
+ for (let o = 0; o < s; o++) {
+ const s = n[o],
+ l = i[o],
+ c = void 0 !== s && void 0 !== l ? a.diffHighlight(s.content, l.content, t, this.config) : void 0,
+ d =
+ void 0 !== s && void 0 !== s.oldNumber
+ ? Object.assign(
+ Object.assign(
+ {},
+ void 0 !== c
+ ? {
+ prefix: c.oldLine.prefix,
+ content: c.oldLine.content,
+ type: a.CSSLineClass.DELETE_CHANGES
+ }
+ : Object.assign(Object.assign({}, a.deconstructLine(s.content, t)), {
+ type: a.toCSSClass(s.type)
+ })
+ ),
+ { oldNumber: s.oldNumber, newNumber: s.newNumber }
+ )
+ : void 0,
+ f =
+ void 0 !== l && void 0 !== l.newNumber
+ ? Object.assign(
+ Object.assign(
+ {},
+ void 0 !== c
+ ? {
+ prefix: c.newLine.prefix,
+ content: c.newLine.content,
+ type: a.CSSLineClass.INSERT_CHANGES
+ }
+ : Object.assign(Object.assign({}, a.deconstructLine(l.content, t)), {
+ type: a.toCSSClass(l.type)
+ })
+ ),
+ { oldNumber: l.oldNumber, newNumber: l.newNumber }
+ )
+ : void 0,
+ { left: u, right: h } = this.generateLineHtml(e, d, f);
+ (r.left += u), (r.right += h);
+ }
+ return r;
+ }
+ generateLineHtml(e, t, n) {
+ return { left: this.generateSingleLineHtml(e, t), right: this.generateSingleLineHtml(e, n) };
+ }
+ generateSingleLineHtml(e, t) {
+ if (void 0 === t) return '';
+ const n = this.hoganUtils.render(d, 'numbers', {
+ oldNumber: t.oldNumber || '',
+ newNumber: t.newNumber || ''
+ });
+ return this.hoganUtils.render(c, 'line', {
+ type: t.type,
+ lineClass: 'd2h-code-linenumber',
+ contentClass: 'd2h-code-line',
+ prefix: ' ' === t.prefix ? ' ' : t.prefix,
+ content: t.content,
+ lineNumber: n,
+ line: t,
+ file: e
+ });
+ }
+ };
+ },
+ 483: (e, t) => {
+ 'use strict';
+ function n(e, t) {
+ if (0 === e.length) return t.length;
+ if (0 === t.length) return e.length;
+ const n = [];
+ let i, r;
+ for (i = 0; i <= t.length; i++) n[i] = [i];
+ for (r = 0; r <= e.length; r++) n[0][r] = r;
+ for (i = 1; i <= t.length; i++)
+ for (r = 1; r <= e.length; r++)
+ t.charAt(i - 1) === e.charAt(r - 1)
+ ? (n[i][r] = n[i - 1][r - 1])
+ : (n[i][r] = Math.min(n[i - 1][r - 1] + 1, Math.min(n[i][r - 1] + 1, n[i - 1][r] + 1)));
+ return n[t.length][e.length];
+ }
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.newMatcherFn = t.newDistanceFn = t.levenshtein = void 0),
+ (t.levenshtein = n),
+ (t.newDistanceFn = function (e) {
+ return (t, i) => {
+ const r = e(t).trim(),
+ s = e(i).trim();
+ return n(r, s) / (r.length + s.length);
+ };
+ }),
+ (t.newMatcherFn = function (e) {
+ return function t(n, i, r = 0, s = new Map()) {
+ const o = (function (t, n, i = new Map()) {
+ let r,
+ s = 1 / 0;
+ for (let o = 0; o < t.length; ++o)
+ for (let a = 0; a < n.length; ++a) {
+ const l = JSON.stringify([t[o], n[a]]);
+ let c;
+ (i.has(l) && (c = i.get(l))) || ((c = e(t[o], n[a])), i.set(l, c)),
+ c < s && ((s = c), (r = { indexA: o, indexB: a, score: s }));
+ }
+ return r;
+ })(n, i, s);
+ if (!o || n.length + i.length < 3) return [[n, i]];
+ const a = n.slice(0, o.indexA),
+ l = i.slice(0, o.indexB),
+ c = [n[o.indexA]],
+ d = [i[o.indexB]],
+ f = o.indexA + 1,
+ u = o.indexB + 1,
+ h = n.slice(f),
+ p = i.slice(u),
+ b = t(a, l, r + 1, s),
+ g = t(c, d, r + 1, s),
+ m = t(h, p, r + 1, s);
+ let v = g;
+ return (
+ (o.indexA > 0 || o.indexB > 0) && (v = b.concat(v)),
+ (n.length > f || i.length > u) && (v = v.concat(m)),
+ v
+ );
+ };
+ });
+ },
+ 741: function (e, t, n) {
+ 'use strict';
+ var i =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (e, t, n, i) {
+ void 0 === i && (i = n);
+ var r = Object.getOwnPropertyDescriptor(t, n);
+ (r && !('get' in r ? !t.__esModule : r.writable || r.configurable)) ||
+ (r = {
+ enumerable: !0,
+ get: function () {
+ return t[n];
+ }
+ }),
+ Object.defineProperty(e, i, r);
+ }
+ : function (e, t, n, i) {
+ void 0 === i && (i = n), (e[i] = t[n]);
+ }),
+ r =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (e, t) {
+ Object.defineProperty(e, 'default', { enumerable: !0, value: t });
+ }
+ : function (e, t) {
+ e.default = t;
+ }),
+ s =
+ (this && this.__importStar) ||
+ function (e) {
+ if (e && e.__esModule) return e;
+ var t = {};
+ if (null != e)
+ for (var n in e) 'default' !== n && Object.prototype.hasOwnProperty.call(e, n) && i(t, e, n);
+ return r(t, e), t;
+ };
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.diffHighlight =
+ t.getFileIcon =
+ t.getHtmlId =
+ t.filenameDiff =
+ t.deconstructLine =
+ t.escapeForHtml =
+ t.colorSchemeToCss =
+ t.toCSSClass =
+ t.defaultRenderConfig =
+ t.CSSLineClass =
+ void 0);
+ const o = s(n(785)),
+ a = n(593),
+ l = s(n(483)),
+ c = n(699);
+ (t.CSSLineClass = {
+ INSERTS: 'd2h-ins',
+ DELETES: 'd2h-del',
+ CONTEXT: 'd2h-cntx',
+ INFO: 'd2h-info',
+ INSERT_CHANGES: 'd2h-ins d2h-change',
+ DELETE_CHANGES: 'd2h-del d2h-change'
+ }),
+ (t.defaultRenderConfig = {
+ matching: c.LineMatchingType.NONE,
+ matchWordsThreshold: 0.25,
+ maxLineLengthHighlight: 1e4,
+ diffStyle: c.DiffStyleType.WORD,
+ colorScheme: c.ColorSchemeType.LIGHT
+ });
+ const d = '/',
+ f = l.newDistanceFn((e) => e.value),
+ u = l.newMatcherFn(f);
+ function h(e) {
+ return -1 !== e.indexOf('dev/null');
+ }
+ function p(e) {
+ return e.replace(/(]*>((.|\n)*?)<\/del>)/g, '');
+ }
+ function b(e) {
+ return e
+ .slice(0)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(/\//g, '/');
+ }
+ function g(e, t, n = !0) {
+ const i = (function (e) {
+ return e ? 2 : 1;
+ })(t);
+ return { prefix: e.substring(0, i), content: n ? b(e.substring(i)) : e.substring(i) };
+ }
+ function m(e) {
+ const t = (0, a.unifyPath)(e.oldName),
+ n = (0, a.unifyPath)(e.newName);
+ if (t === n || h(t) || h(n)) return h(n) ? t : n;
+ {
+ const e = [],
+ i = [],
+ r = t.split(d),
+ s = n.split(d);
+ let o = 0,
+ a = r.length - 1,
+ l = s.length - 1;
+ for (; o < a && o < l && r[o] === s[o]; ) e.push(s[o]), (o += 1);
+ for (; a > o && l > o && r[a] === s[l]; ) i.unshift(s[l]), (a -= 1), (l -= 1);
+ const c = e.join(d),
+ f = i.join(d),
+ u = r.slice(o, a + 1).join(d),
+ h = s.slice(o, l + 1).join(d);
+ return c.length && f.length
+ ? c + d + '{' + u + ' → ' + h + '}' + d + f
+ : c.length
+ ? c + d + '{' + u + ' → ' + h + '}'
+ : f.length
+ ? '{' + u + ' → ' + h + '}' + d + f
+ : t + ' → ' + n;
+ }
+ }
+ (t.toCSSClass = function (e) {
+ switch (e) {
+ case c.LineType.CONTEXT:
+ return t.CSSLineClass.CONTEXT;
+ case c.LineType.INSERT:
+ return t.CSSLineClass.INSERTS;
+ case c.LineType.DELETE:
+ return t.CSSLineClass.DELETES;
+ }
+ }),
+ (t.colorSchemeToCss = function (e) {
+ switch (e) {
+ case c.ColorSchemeType.DARK:
+ return 'd2h-dark-color-scheme';
+ case c.ColorSchemeType.AUTO:
+ return 'd2h-auto-color-scheme';
+ case c.ColorSchemeType.LIGHT:
+ default:
+ return 'd2h-light-color-scheme';
+ }
+ }),
+ (t.escapeForHtml = b),
+ (t.deconstructLine = g),
+ (t.filenameDiff = m),
+ (t.getHtmlId = function (e) {
+ return `d2h-${(0, a.hashCode)(m(e)).toString().slice(-6)}`;
+ }),
+ (t.getFileIcon = function (e) {
+ let t = 'file-changed';
+ return (
+ e.isRename || e.isCopy
+ ? (t = 'file-renamed')
+ : e.isNew
+ ? (t = 'file-added')
+ : e.isDeleted
+ ? (t = 'file-deleted')
+ : e.newName !== e.oldName && (t = 'file-renamed'),
+ t
+ );
+ }),
+ (t.diffHighlight = function (e, n, i, r = {}) {
+ const {
+ matching: s,
+ maxLineLengthHighlight: a,
+ matchWordsThreshold: l,
+ diffStyle: c
+ } = Object.assign(Object.assign({}, t.defaultRenderConfig), r),
+ d = g(e, i, !1),
+ h = g(n, i, !1);
+ if (d.content.length > a || h.content.length > a)
+ return {
+ oldLine: { prefix: d.prefix, content: b(d.content) },
+ newLine: { prefix: h.prefix, content: b(h.content) }
+ };
+ const m = 'char' === c ? o.diffChars(d.content, h.content) : o.diffWordsWithSpace(d.content, h.content),
+ v = [];
+ if ('word' === c && 'words' === s) {
+ const e = m.filter((e) => e.removed),
+ t = m.filter((e) => e.added);
+ u(t, e).forEach((e) => {
+ 1 === e[0].length && 1 === e[1].length && f(e[0][0], e[1][0]) < l && (v.push(e[0][0]), v.push(e[1][0]));
+ });
+ }
+ const y = m.reduce((e, t) => {
+ const n = t.added ? 'ins' : t.removed ? 'del' : null,
+ i = v.indexOf(t) > -1 ? ' class="d2h-change"' : '',
+ r = b(t.value);
+ return null !== n ? `${e}<${n}${i}>${r}${n}>` : `${e}${r}`;
+ }, '');
+ return {
+ oldLine: { prefix: d.prefix, content: ((w = y), w.replace(/(]*>((.|\n)*?)<\/ins>)/g, '')) },
+ newLine: { prefix: h.prefix, content: p(y) }
+ };
+ var w;
+ });
+ },
+ 170: function (e, t, n) {
+ 'use strict';
+ var i =
+ (this && this.__createBinding) ||
+ (Object.create
+ ? function (e, t, n, i) {
+ void 0 === i && (i = n);
+ var r = Object.getOwnPropertyDescriptor(t, n);
+ (r && !('get' in r ? !t.__esModule : r.writable || r.configurable)) ||
+ (r = {
+ enumerable: !0,
+ get: function () {
+ return t[n];
+ }
+ }),
+ Object.defineProperty(e, i, r);
+ }
+ : function (e, t, n, i) {
+ void 0 === i && (i = n), (e[i] = t[n]);
+ }),
+ r =
+ (this && this.__setModuleDefault) ||
+ (Object.create
+ ? function (e, t) {
+ Object.defineProperty(e, 'default', { enumerable: !0, value: t });
+ }
+ : function (e, t) {
+ e.default = t;
+ }),
+ s =
+ (this && this.__importStar) ||
+ function (e) {
+ if (e && e.__esModule) return e;
+ var t = {};
+ if (null != e)
+ for (var n in e) 'default' !== n && Object.prototype.hasOwnProperty.call(e, n) && i(t, e, n);
+ return r(t, e), t;
+ };
+ Object.defineProperty(t, '__esModule', { value: !0 }), (t.defaultSideBySideRendererConfig = void 0);
+ const o = s(n(483)),
+ a = s(n(741)),
+ l = n(699);
+ t.defaultSideBySideRendererConfig = Object.assign(Object.assign({}, a.defaultRenderConfig), {
+ renderNothingWhenEmpty: !1,
+ matchingMaxComparisons: 2500,
+ maxLineSizeInBlockForComparison: 200
+ });
+ const c = 'generic';
+ t.default = class {
+ constructor(e, n = {}) {
+ (this.hoganUtils = e),
+ (this.config = Object.assign(Object.assign({}, t.defaultSideBySideRendererConfig), n));
+ }
+ render(e) {
+ const t = e
+ .map((e) => {
+ let t;
+ return (
+ (t = e.blocks.length ? this.generateFileHtml(e) : this.generateEmptyDiff()),
+ this.makeFileDiffHtml(e, t)
+ );
+ })
+ .join('\n');
+ return this.hoganUtils.render(c, 'wrapper', {
+ colorScheme: a.colorSchemeToCss(this.config.colorScheme),
+ content: t
+ });
+ }
+ makeFileDiffHtml(e, t) {
+ if (this.config.renderNothingWhenEmpty && Array.isArray(e.blocks) && 0 === e.blocks.length) return '';
+ const n = this.hoganUtils.template('side-by-side', 'file-diff'),
+ i = this.hoganUtils.template(c, 'file-path'),
+ r = this.hoganUtils.template('icon', 'file'),
+ s = this.hoganUtils.template('tag', a.getFileIcon(e));
+ return n.render({
+ file: e,
+ fileHtmlId: a.getHtmlId(e),
+ diffs: t,
+ filePath: i.render({ fileDiffName: a.filenameDiff(e) }, { fileIcon: r, fileTag: s })
+ });
+ }
+ generateEmptyDiff() {
+ return {
+ right: '',
+ left: this.hoganUtils.render(c, 'empty-diff', {
+ contentClass: 'd2h-code-side-line',
+ CSSLineClass: a.CSSLineClass
+ })
+ };
+ }
+ generateFileHtml(e) {
+ const t = o.newMatcherFn(o.newDistanceFn((t) => a.deconstructLine(t.content, e.isCombined).content));
+ return e.blocks
+ .map((n) => {
+ const i = { left: this.makeHeaderHtml(n.header, e), right: this.makeHeaderHtml('') };
+ return (
+ this.applyLineGroupping(n).forEach(([n, r, s]) => {
+ if (r.length && s.length && !n.length)
+ this.applyRematchMatching(r, s, t).map(([t, n]) => {
+ const { left: r, right: s } = this.processChangedLines(e.isCombined, t, n);
+ (i.left += r), (i.right += s);
+ });
+ else if (n.length)
+ n.forEach((t) => {
+ const { prefix: n, content: r } = a.deconstructLine(t.content, e.isCombined),
+ { left: s, right: o } = this.generateLineHtml(
+ { type: a.CSSLineClass.CONTEXT, prefix: n, content: r, number: t.oldNumber },
+ { type: a.CSSLineClass.CONTEXT, prefix: n, content: r, number: t.newNumber }
+ );
+ (i.left += s), (i.right += o);
+ });
+ else if (r.length || s.length) {
+ const { left: t, right: n } = this.processChangedLines(e.isCombined, r, s);
+ (i.left += t), (i.right += n);
+ } else console.error('Unknown state reached while processing groups of lines', n, r, s);
+ }),
+ i
+ );
+ })
+ .reduce((e, t) => ({ left: e.left + t.left, right: e.right + t.right }), { left: '', right: '' });
+ }
+ applyLineGroupping(e) {
+ const t = [];
+ let n = [],
+ i = [];
+ for (let r = 0; r < e.lines.length; r++) {
+ const s = e.lines[r];
+ ((s.type !== l.LineType.INSERT && i.length) || (s.type === l.LineType.CONTEXT && n.length > 0)) &&
+ (t.push([[], n, i]), (n = []), (i = [])),
+ s.type === l.LineType.CONTEXT
+ ? t.push([[s], [], []])
+ : s.type === l.LineType.INSERT && 0 === n.length
+ ? t.push([[], [], [s]])
+ : s.type === l.LineType.INSERT && n.length > 0
+ ? i.push(s)
+ : s.type === l.LineType.DELETE && n.push(s);
+ }
+ return (n.length || i.length) && (t.push([[], n, i]), (n = []), (i = [])), t;
+ }
+ applyRematchMatching(e, t, n) {
+ const i = e.length * t.length,
+ r = Math.max.apply(null, [0].concat(e.concat(t).map((e) => e.content.length)));
+ return i < this.config.matchingMaxComparisons &&
+ r < this.config.maxLineSizeInBlockForComparison &&
+ ('lines' === this.config.matching || 'words' === this.config.matching)
+ ? n(e, t)
+ : [[e, t]];
+ }
+ makeHeaderHtml(e, t) {
+ return this.hoganUtils.render(c, 'block-header', {
+ CSSLineClass: a.CSSLineClass,
+ blockHeader: (null == t ? void 0 : t.isTooBig) ? e : a.escapeForHtml(e),
+ lineClass: 'd2h-code-side-linenumber',
+ contentClass: 'd2h-code-side-line'
+ });
+ }
+ processChangedLines(e, t, n) {
+ const i = { right: '', left: '' },
+ r = Math.max(t.length, n.length);
+ for (let s = 0; s < r; s++) {
+ const r = t[s],
+ o = n[s],
+ l = void 0 !== r && void 0 !== o ? a.diffHighlight(r.content, o.content, e, this.config) : void 0,
+ c =
+ void 0 !== r && void 0 !== r.oldNumber
+ ? Object.assign(
+ Object.assign(
+ {},
+ void 0 !== l
+ ? {
+ prefix: l.oldLine.prefix,
+ content: l.oldLine.content,
+ type: a.CSSLineClass.DELETE_CHANGES
+ }
+ : Object.assign(Object.assign({}, a.deconstructLine(r.content, e)), {
+ type: a.toCSSClass(r.type)
+ })
+ ),
+ { number: r.oldNumber }
+ )
+ : void 0,
+ d =
+ void 0 !== o && void 0 !== o.newNumber
+ ? Object.assign(
+ Object.assign(
+ {},
+ void 0 !== l
+ ? {
+ prefix: l.newLine.prefix,
+ content: l.newLine.content,
+ type: a.CSSLineClass.INSERT_CHANGES
+ }
+ : Object.assign(Object.assign({}, a.deconstructLine(o.content, e)), {
+ type: a.toCSSClass(o.type)
+ })
+ ),
+ { number: o.newNumber }
+ )
+ : void 0,
+ { left: f, right: u } = this.generateLineHtml(c, d);
+ (i.left += f), (i.right += u);
+ }
+ return i;
+ }
+ generateLineHtml(e, t) {
+ return { left: this.generateSingleHtml(e), right: this.generateSingleHtml(t) };
+ }
+ generateSingleHtml(e) {
+ const t = 'd2h-code-side-linenumber',
+ n = 'd2h-code-side-line';
+ return this.hoganUtils.render(c, 'line', {
+ type: (null == e ? void 0 : e.type) || `${a.CSSLineClass.CONTEXT} d2h-emptyplaceholder`,
+ lineClass: void 0 !== e ? t : `${t} d2h-code-side-emptyplaceholder`,
+ contentClass: void 0 !== e ? n : `${n} d2h-code-side-emptyplaceholder`,
+ prefix: ' ' === (null == e ? void 0 : e.prefix) ? ' ' : null == e ? void 0 : e.prefix,
+ content: null == e ? void 0 : e.content,
+ lineNumber: null == e ? void 0 : e.number
+ });
+ }
+ };
+ },
+ 699: (e, t) => {
+ 'use strict';
+ var n, i;
+ Object.defineProperty(t, '__esModule', { value: !0 }),
+ (t.ColorSchemeType = t.DiffStyleType = t.LineMatchingType = t.OutputFormatType = t.LineType = void 0),
+ (function (e) {
+ (e.INSERT = 'insert'), (e.DELETE = 'delete'), (e.CONTEXT = 'context');
+ })(n || (t.LineType = n = {})),
+ (t.OutputFormatType = { LINE_BY_LINE: 'line-by-line', SIDE_BY_SIDE: 'side-by-side' }),
+ (t.LineMatchingType = { LINES: 'lines', WORDS: 'words', NONE: 'none' }),
+ (t.DiffStyleType = { WORD: 'word', CHAR: 'char' }),
+ (function (e) {
+ (e.AUTO = 'auto'), (e.DARK = 'dark'), (e.LIGHT = 'light');
+ })(i || (t.ColorSchemeType = i = {}));
+ },
+ 593: (e, t) => {
+ 'use strict';
+ Object.defineProperty(t, '__esModule', { value: !0 }), (t.hashCode = t.unifyPath = t.escapeForRegExp = void 0);
+ const n = RegExp(
+ '[' + ['-', '[', ']', '/', '{', '}', '(', ')', '*', '+', '?', '.', '\\', '^', '$', '|'].join('\\') + ']',
+ 'g'
+ );
+ (t.escapeForRegExp = function (e) {
+ return e.replace(n, '\\$&');
+ }),
+ (t.unifyPath = function (e) {
+ return e ? e.replace(/\\/g, '/') : e;
+ }),
+ (t.hashCode = function (e) {
+ let t,
+ n,
+ i,
+ r = 0;
+ for (t = 0, i = e.length; t < i; t++) (n = e.charCodeAt(t)), (r = (r << 5) - r + n), (r |= 0);
+ return r;
+ });
+ }
+ }),
+ (t = {}),
+ (function n(i) {
+ var r = t[i];
+ if (void 0 !== r) return r.exports;
+ var s = (t[i] = { exports: {} });
+ return e[i].call(s.exports, s, s.exports, n), s.exports;
+ })(834)
+ );
+ var e, t;
+});
diff --git a/packages/bruno-app/public/static/diff2Html.min.css b/packages/bruno-app/public/static/diff2Html.min.css
new file mode 100644
index 000000000..d793f308a
--- /dev/null
+++ b/packages/bruno-app/public/static/diff2Html.min.css
@@ -0,0 +1,713 @@
+:host,
+:root {
+ --d2h-bg-color: #fff;
+ --d2h-border-color: #ddd;
+ --d2h-dim-color: rgba(0, 0, 0, 0.3);
+ --d2h-line-border-color: #eee;
+ --d2h-file-header-bg-color: #f7f7f7;
+ --d2h-file-header-border-color: #d8d8d8;
+ --d2h-empty-placeholder-bg-color: #f1f1f1;
+ --d2h-empty-placeholder-border-color: #e1e1e1;
+ --d2h-selected-color: #c8e1ff;
+ --d2h-ins-bg-color: #dfd;
+ --d2h-ins-border-color: #b4e2b4;
+ --d2h-ins-highlight-bg-color: #97f295;
+ --d2h-ins-label-color: #399839;
+ --d2h-del-bg-color: #fee8e9;
+ --d2h-del-border-color: #e9aeae;
+ --d2h-del-highlight-bg-color: #ffb6ba;
+ --d2h-del-label-color: #c33;
+ --d2h-change-del-color: #fdf2d0;
+ --d2h-change-ins-color: #ded;
+ --d2h-info-bg-color: #f8fafd;
+ --d2h-info-border-color: #d5e4f2;
+ --d2h-change-label-color: #d0b44c;
+ --d2h-moved-label-color: #3572b0;
+ --d2h-dark-color: #e6edf3;
+ --d2h-dark-bg-color: #0d1117;
+ --d2h-dark-border-color: #30363d;
+ --d2h-dark-dim-color: #6e7681;
+ --d2h-dark-line-border-color: #21262d;
+ --d2h-dark-file-header-bg-color: #161b22;
+ --d2h-dark-file-header-border-color: #30363d;
+ --d2h-dark-empty-placeholder-bg-color: hsla(215, 8%, 47%, 0.1);
+ --d2h-dark-empty-placeholder-border-color: #30363d;
+ --d2h-dark-selected-color: rgba(56, 139, 253, 0.1);
+ --d2h-dark-ins-bg-color: rgba(46, 160, 67, 0.15);
+ --d2h-dark-ins-border-color: rgba(46, 160, 67, 0.4);
+ --d2h-dark-ins-highlight-bg-color: rgba(46, 160, 67, 0.4);
+ --d2h-dark-ins-label-color: #3fb950;
+ --d2h-dark-del-bg-color: rgba(248, 81, 73, 0.1);
+ --d2h-dark-del-border-color: rgba(248, 81, 73, 0.4);
+ --d2h-dark-del-highlight-bg-color: rgba(248, 81, 73, 0.4);
+ --d2h-dark-del-label-color: #f85149;
+ --d2h-dark-change-del-color: rgba(210, 153, 34, 0.2);
+ --d2h-dark-change-ins-color: rgba(46, 160, 67, 0.25);
+ --d2h-dark-info-bg-color: rgba(56, 139, 253, 0.1);
+ --d2h-dark-info-border-color: rgba(56, 139, 253, 0.4);
+ --d2h-dark-change-label-color: #d29922;
+ --d2h-dark-moved-label-color: #3572b0;
+}
+.d2h-wrapper {
+ text-align: left;
+}
+.d2h-file-header {
+ background-color: #f7f7f7;
+ background-color: var(--d2h-file-header-bg-color);
+ border-bottom: 1px solid #d8d8d8;
+ border-bottom: 1px solid var(--d2h-file-header-border-color);
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ font-family: Source Sans Pro, Helvetica Neue, Helvetica, Arial, sans-serif;
+ height: 35px;
+ padding: 5px 10px;
+}
+.d2h-file-header.d2h-sticky-header {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+}
+.d2h-file-stats {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ font-size: 14px;
+ margin-left: auto;
+}
+.d2h-lines-added {
+ border: 1px solid #b4e2b4;
+ border: 1px solid var(--d2h-ins-border-color);
+ border-radius: 5px 0 0 5px;
+ color: #399839;
+ color: var(--d2h-ins-label-color);
+ padding: 2px;
+ text-align: right;
+ vertical-align: middle;
+}
+.d2h-lines-deleted {
+ border: 1px solid #e9aeae;
+ border: 1px solid var(--d2h-del-border-color);
+ border-radius: 0 5px 5px 0;
+ color: #c33;
+ color: var(--d2h-del-label-color);
+ margin-left: 1px;
+ padding: 2px;
+ text-align: left;
+ vertical-align: middle;
+}
+.d2h-file-name-wrapper {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ font-size: 15px;
+ width: 100%;
+}
+.d2h-file-name {
+ overflow-x: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.d2h-file-wrapper {
+ border: 1px solid #ddd;
+ border: 1px solid var(--d2h-border-color);
+ border-radius: 3px;
+ margin-bottom: 1em;
+}
+.d2h-file-collapse {
+ -webkit-box-pack: end;
+ -ms-flex-pack: end;
+ cursor: pointer;
+ display: none;
+ font-size: 12px;
+ justify-content: flex-end;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ border: 1px solid #ddd;
+ border: 1px solid var(--d2h-border-color);
+ border-radius: 3px;
+ padding: 4px 8px;
+}
+.d2h-file-collapse.d2h-selected {
+ background-color: #c8e1ff;
+ background-color: var(--d2h-selected-color);
+}
+.d2h-file-collapse-input {
+ margin: 0 4px 0 0;
+}
+.d2h-diff-table {
+ border-collapse: collapse;
+ font-family: Menlo, Consolas, monospace;
+ font-size: 13px;
+ width: 100%;
+}
+.d2h-files-diff {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ width: 100%;
+}
+.d2h-file-diff {
+ overflow-y: hidden;
+}
+.d2h-file-diff.d2h-d-none,
+.d2h-files-diff.d2h-d-none {
+ display: none;
+}
+.d2h-file-side-diff {
+ display: inline-block;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ width: 50%;
+}
+.d2h-code-line {
+ padding: 0 8em;
+ width: calc(100% - 16em);
+}
+.d2h-code-line,
+.d2h-code-side-line {
+ display: inline-block;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ white-space: nowrap;
+}
+.d2h-code-side-line {
+ padding: 0 4.5em;
+ width: calc(100% - 9em);
+}
+.d2h-code-line-ctn {
+ background: none;
+ display: inline-block;
+ padding: 0;
+ word-wrap: normal;
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ -ms-user-select: text;
+ user-select: text;
+ vertical-align: middle;
+ white-space: pre;
+ width: 100%;
+}
+.d2h-code-line del,
+.d2h-code-side-line del {
+ background-color: #ffb6ba;
+ background-color: var(--d2h-del-highlight-bg-color);
+}
+.d2h-code-line del,
+.d2h-code-line ins,
+.d2h-code-side-line del,
+.d2h-code-side-line ins {
+ border-radius: 0.2em;
+ display: inline-block;
+ margin-top: -1px;
+ -webkit-text-decoration: none;
+ text-decoration: none;
+}
+.d2h-code-line ins,
+.d2h-code-side-line ins {
+ background-color: #97f295;
+ background-color: var(--d2h-ins-highlight-bg-color);
+ text-align: left;
+}
+.d2h-code-line-prefix {
+ background: none;
+ display: inline;
+ padding: 0;
+ word-wrap: normal;
+ white-space: pre;
+}
+.line-num1 {
+ float: left;
+}
+.line-num1,
+.line-num2 {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ overflow: hidden;
+ padding: 0 0.5em;
+ text-overflow: ellipsis;
+ width: 3.5em;
+}
+.line-num2 {
+ float: right;
+}
+.d2h-code-linenumber {
+ background-color: #fff;
+ background-color: var(--d2h-bg-color);
+ border: solid #eee;
+ border: solid var(--d2h-line-border-color);
+ border-width: 0 1px;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ color: rgba(0, 0, 0, 0.3);
+ color: var(--d2h-dim-color);
+ cursor: pointer;
+ display: inline-block;
+ position: absolute;
+ text-align: right;
+ width: 7.5em;
+}
+.d2h-code-linenumber:after {
+ content: '\200b';
+}
+.d2h-code-side-linenumber {
+ background-color: #fff;
+ background-color: var(--d2h-bg-color);
+ border: solid #eee;
+ border: solid var(--d2h-line-border-color);
+ border-width: 0 1px;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ color: rgba(0, 0, 0, 0.3);
+ color: var(--d2h-dim-color);
+ cursor: pointer;
+ display: inline-block;
+ overflow: hidden;
+ padding: 0 0.5em;
+ position: absolute;
+ text-align: right;
+ text-overflow: ellipsis;
+ width: 4em;
+}
+.d2h-code-side-linenumber:after {
+ content: '\200b';
+}
+.d2h-code-side-emptyplaceholder,
+.d2h-emptyplaceholder {
+ background-color: #f1f1f1;
+ background-color: var(--d2h-empty-placeholder-bg-color);
+ border-color: #e1e1e1;
+ border-color: var(--d2h-empty-placeholder-border-color);
+}
+.d2h-code-line-prefix,
+.d2h-code-linenumber,
+.d2h-code-side-linenumber,
+.d2h-emptyplaceholder {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+.d2h-code-linenumber,
+.d2h-code-side-linenumber {
+ direction: rtl;
+}
+.d2h-del {
+ background-color: #fee8e9;
+ background-color: var(--d2h-del-bg-color);
+ border-color: #e9aeae;
+ border-color: var(--d2h-del-border-color);
+}
+.d2h-ins {
+ background-color: #dfd;
+ background-color: var(--d2h-ins-bg-color);
+ border-color: #b4e2b4;
+ border-color: var(--d2h-ins-border-color);
+}
+.d2h-info {
+ background-color: #f8fafd;
+ background-color: var(--d2h-info-bg-color);
+ border-color: #d5e4f2;
+ border-color: var(--d2h-info-border-color);
+ color: rgba(0, 0, 0, 0.3);
+ color: var(--d2h-dim-color);
+}
+.d2h-file-diff .d2h-del.d2h-change {
+ background-color: #fdf2d0;
+ background-color: var(--d2h-change-del-color);
+}
+.d2h-file-diff .d2h-ins.d2h-change {
+ background-color: #ded;
+ background-color: var(--d2h-change-ins-color);
+}
+.d2h-file-list-wrapper {
+ margin-bottom: 10px;
+}
+.d2h-file-list-wrapper a {
+ -webkit-text-decoration: none;
+ text-decoration: none;
+}
+.d2h-file-list-wrapper a,
+.d2h-file-list-wrapper a:visited {
+ color: #3572b0;
+ color: var(--d2h-moved-label-color);
+}
+.d2h-file-list-header {
+ text-align: left;
+}
+.d2h-file-list-title {
+ font-weight: 700;
+}
+.d2h-file-list-line {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ text-align: left;
+}
+.d2h-file-list {
+ display: block;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.d2h-file-list > li {
+ border-bottom: 1px solid #ddd;
+ border-bottom: 1px solid var(--d2h-border-color);
+ margin: 0;
+ padding: 5px 10px;
+}
+.d2h-file-list > li:last-child {
+ border-bottom: none;
+}
+.d2h-file-switch {
+ cursor: pointer;
+ display: none;
+ font-size: 10px;
+}
+.d2h-icon {
+ margin-right: 10px;
+ vertical-align: middle;
+ fill: currentColor;
+}
+.d2h-deleted {
+ color: #c33;
+ color: var(--d2h-del-label-color);
+}
+.d2h-added {
+ color: #399839;
+ color: var(--d2h-ins-label-color);
+}
+.d2h-changed {
+ color: #d0b44c;
+ color: var(--d2h-change-label-color);
+}
+.d2h-moved {
+ color: #3572b0;
+ color: var(--d2h-moved-label-color);
+}
+.d2h-tag {
+ background-color: #fff;
+ background-color: var(--d2h-bg-color);
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ font-size: 10px;
+ margin-left: 5px;
+ padding: 0 2px;
+}
+.d2h-deleted-tag {
+ border: 1px solid #c33;
+ border: 1px solid var(--d2h-del-label-color);
+}
+.d2h-added-tag {
+ border: 1px solid #399839;
+ border: 1px solid var(--d2h-ins-label-color);
+}
+.d2h-changed-tag {
+ border: 1px solid #d0b44c;
+ border: 1px solid var(--d2h-change-label-color);
+}
+.d2h-moved-tag {
+ border: 1px solid #3572b0;
+ border: 1px solid var(--d2h-moved-label-color);
+}
+.d2h-dark-color-scheme {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+ color: #e6edf3;
+ color: var(--d2h-dark-color);
+}
+.d2h-dark-color-scheme .d2h-file-header {
+ background-color: #161b22;
+ background-color: var(--d2h-dark-file-header-bg-color);
+ border-bottom: #30363d;
+ border-bottom: var(--d2h-dark-file-header-border-color);
+}
+.d2h-dark-color-scheme .d2h-lines-added {
+ border: 1px solid rgba(46, 160, 67, 0.4);
+ border: 1px solid var(--d2h-dark-ins-border-color);
+ color: #3fb950;
+ color: var(--d2h-dark-ins-label-color);
+}
+.d2h-dark-color-scheme .d2h-lines-deleted {
+ border: 1px solid rgba(248, 81, 73, 0.4);
+ border: 1px solid var(--d2h-dark-del-border-color);
+ color: #f85149;
+ color: var(--d2h-dark-del-label-color);
+}
+.d2h-dark-color-scheme .d2h-code-line del,
+.d2h-dark-color-scheme .d2h-code-side-line del {
+ background-color: rgba(248, 81, 73, 0.4);
+ background-color: var(--d2h-dark-del-highlight-bg-color);
+}
+.d2h-dark-color-scheme .d2h-code-line ins,
+.d2h-dark-color-scheme .d2h-code-side-line ins {
+ background-color: rgba(46, 160, 67, 0.4);
+ background-color: var(--d2h-dark-ins-highlight-bg-color);
+}
+.d2h-dark-color-scheme .d2h-diff-tbody {
+ border-color: #30363d;
+ border-color: var(--d2h-dark-border-color);
+}
+.d2h-dark-color-scheme .d2h-code-side-linenumber {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+ border-color: #21262d;
+ border-color: var(--d2h-dark-line-border-color);
+ color: #6e7681;
+ color: var(--d2h-dark-dim-color);
+}
+.d2h-dark-color-scheme .d2h-files-diff .d2h-code-side-emptyplaceholder,
+.d2h-dark-color-scheme .d2h-files-diff .d2h-emptyplaceholder {
+ background-color: hsla(215, 8%, 47%, 0.1);
+ background-color: var(--d2h-dark-empty-placeholder-bg-color);
+ border-color: #30363d;
+ border-color: var(--d2h-dark-empty-placeholder-border-color);
+}
+.d2h-dark-color-scheme .d2h-code-linenumber {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+ border-color: #21262d;
+ border-color: var(--d2h-dark-line-border-color);
+ color: #6e7681;
+ color: var(--d2h-dark-dim-color);
+}
+.d2h-dark-color-scheme .d2h-del {
+ background-color: rgba(248, 81, 73, 0.1);
+ background-color: var(--d2h-dark-del-bg-color);
+ border-color: rgba(248, 81, 73, 0.4);
+ border-color: var(--d2h-dark-del-border-color);
+}
+.d2h-dark-color-scheme .d2h-ins {
+ background-color: rgba(46, 160, 67, 0.15);
+ background-color: var(--d2h-dark-ins-bg-color);
+ border-color: rgba(46, 160, 67, 0.4);
+ border-color: var(--d2h-dark-ins-border-color);
+}
+.d2h-dark-color-scheme .d2h-info {
+ background-color: rgba(56, 139, 253, 0.1);
+ background-color: var(--d2h-dark-info-bg-color);
+ border-color: rgba(56, 139, 253, 0.4);
+ border-color: var(--d2h-dark-info-border-color);
+ color: #6e7681;
+ color: var(--d2h-dark-dim-color);
+}
+.d2h-dark-color-scheme .d2h-file-diff .d2h-del.d2h-change {
+ background-color: rgba(210, 153, 34, 0.2);
+ background-color: var(--d2h-dark-change-del-color);
+}
+.d2h-dark-color-scheme .d2h-file-diff .d2h-ins.d2h-change {
+ background-color: rgba(46, 160, 67, 0.25);
+ background-color: var(--d2h-dark-change-ins-color);
+}
+.d2h-dark-color-scheme .d2h-file-wrapper {
+ border: 1px solid #30363d;
+ border: 1px solid var(--d2h-dark-border-color);
+}
+.d2h-dark-color-scheme .d2h-file-collapse {
+ border: 1px solid #0d1117;
+ border: 1px solid var(--d2h-dark-bg-color);
+}
+.d2h-dark-color-scheme .d2h-file-collapse.d2h-selected {
+ background-color: rgba(56, 139, 253, 0.1);
+ background-color: var(--d2h-dark-selected-color);
+}
+.d2h-dark-color-scheme .d2h-file-list-wrapper a,
+.d2h-dark-color-scheme .d2h-file-list-wrapper a:visited {
+ color: #3572b0;
+ color: var(--d2h-dark-moved-label-color);
+}
+.d2h-dark-color-scheme .d2h-file-list > li {
+ border-bottom: 1px solid #0d1117;
+ border-bottom: 1px solid var(--d2h-dark-bg-color);
+}
+.d2h-dark-color-scheme .d2h-deleted {
+ color: #f85149;
+ color: var(--d2h-dark-del-label-color);
+}
+.d2h-dark-color-scheme .d2h-added {
+ color: #3fb950;
+ color: var(--d2h-dark-ins-label-color);
+}
+.d2h-dark-color-scheme .d2h-changed {
+ color: #d29922;
+ color: var(--d2h-dark-change-label-color);
+}
+.d2h-dark-color-scheme .d2h-moved {
+ color: #3572b0;
+ color: var(--d2h-dark-moved-label-color);
+}
+.d2h-dark-color-scheme .d2h-tag {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+}
+.d2h-dark-color-scheme .d2h-deleted-tag {
+ border: 1px solid #f85149;
+ border: 1px solid var(--d2h-dark-del-label-color);
+}
+.d2h-dark-color-scheme .d2h-added-tag {
+ border: 1px solid #3fb950;
+ border: 1px solid var(--d2h-dark-ins-label-color);
+}
+.d2h-dark-color-scheme .d2h-changed-tag {
+ border: 1px solid #d29922;
+ border: 1px solid var(--d2h-dark-change-label-color);
+}
+.d2h-dark-color-scheme .d2h-moved-tag {
+ border: 1px solid #3572b0;
+ border: 1px solid var(--d2h-dark-moved-label-color);
+}
+@media (prefers-color-scheme: dark) {
+ .d2h-auto-color-scheme {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+ color: #e6edf3;
+ color: var(--d2h-dark-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-header {
+ background-color: #161b22;
+ background-color: var(--d2h-dark-file-header-bg-color);
+ border-bottom: #30363d;
+ border-bottom: var(--d2h-dark-file-header-border-color);
+ }
+ .d2h-auto-color-scheme .d2h-lines-added {
+ border: 1px solid rgba(46, 160, 67, 0.4);
+ border: 1px solid var(--d2h-dark-ins-border-color);
+ color: #3fb950;
+ color: var(--d2h-dark-ins-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-lines-deleted {
+ border: 1px solid rgba(248, 81, 73, 0.4);
+ border: 1px solid var(--d2h-dark-del-border-color);
+ color: #f85149;
+ color: var(--d2h-dark-del-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-code-line del,
+ .d2h-auto-color-scheme .d2h-code-side-line del {
+ background-color: rgba(248, 81, 73, 0.4);
+ background-color: var(--d2h-dark-del-highlight-bg-color);
+ }
+ .d2h-auto-color-scheme .d2h-code-line ins,
+ .d2h-auto-color-scheme .d2h-code-side-line ins {
+ background-color: rgba(46, 160, 67, 0.4);
+ background-color: var(--d2h-dark-ins-highlight-bg-color);
+ }
+ .d2h-auto-color-scheme .d2h-diff-tbody {
+ border-color: #30363d;
+ border-color: var(--d2h-dark-border-color);
+ }
+ .d2h-auto-color-scheme .d2h-code-side-linenumber {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+ border-color: #21262d;
+ border-color: var(--d2h-dark-line-border-color);
+ color: #6e7681;
+ color: var(--d2h-dark-dim-color);
+ }
+ .d2h-auto-color-scheme .d2h-files-diff .d2h-code-side-emptyplaceholder,
+ .d2h-auto-color-scheme .d2h-files-diff .d2h-emptyplaceholder {
+ background-color: hsla(215, 8%, 47%, 0.1);
+ background-color: var(--d2h-dark-empty-placeholder-bg-color);
+ border-color: #30363d;
+ border-color: var(--d2h-dark-empty-placeholder-border-color);
+ }
+ .d2h-auto-color-scheme .d2h-code-linenumber {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+ border-color: #21262d;
+ border-color: var(--d2h-dark-line-border-color);
+ color: #6e7681;
+ color: var(--d2h-dark-dim-color);
+ }
+ .d2h-auto-color-scheme .d2h-del {
+ background-color: rgba(248, 81, 73, 0.1);
+ background-color: var(--d2h-dark-del-bg-color);
+ border-color: rgba(248, 81, 73, 0.4);
+ border-color: var(--d2h-dark-del-border-color);
+ }
+ .d2h-auto-color-scheme .d2h-ins {
+ background-color: rgba(46, 160, 67, 0.15);
+ background-color: var(--d2h-dark-ins-bg-color);
+ border-color: rgba(46, 160, 67, 0.4);
+ border-color: var(--d2h-dark-ins-border-color);
+ }
+ .d2h-auto-color-scheme .d2h-info {
+ background-color: rgba(56, 139, 253, 0.1);
+ background-color: var(--d2h-dark-info-bg-color);
+ border-color: rgba(56, 139, 253, 0.4);
+ border-color: var(--d2h-dark-info-border-color);
+ color: #6e7681;
+ color: var(--d2h-dark-dim-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-diff .d2h-del.d2h-change {
+ background-color: rgba(210, 153, 34, 0.2);
+ background-color: var(--d2h-dark-change-del-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-diff .d2h-ins.d2h-change {
+ background-color: rgba(46, 160, 67, 0.25);
+ background-color: var(--d2h-dark-change-ins-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-wrapper {
+ border: 1px solid #30363d;
+ border: 1px solid var(--d2h-dark-border-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-collapse {
+ border: 1px solid #0d1117;
+ border: 1px solid var(--d2h-dark-bg-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-collapse.d2h-selected {
+ background-color: rgba(56, 139, 253, 0.1);
+ background-color: var(--d2h-dark-selected-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-list-wrapper a,
+ .d2h-auto-color-scheme .d2h-file-list-wrapper a:visited {
+ color: #3572b0;
+ color: var(--d2h-dark-moved-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-file-list > li {
+ border-bottom: 1px solid #0d1117;
+ border-bottom: 1px solid var(--d2h-dark-bg-color);
+ }
+ .d2h-dark-color-scheme .d2h-deleted {
+ color: #f85149;
+ color: var(--d2h-dark-del-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-added {
+ color: #3fb950;
+ color: var(--d2h-dark-ins-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-changed {
+ color: #d29922;
+ color: var(--d2h-dark-change-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-moved {
+ color: #3572b0;
+ color: var(--d2h-dark-moved-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-tag {
+ background-color: #0d1117;
+ background-color: var(--d2h-dark-bg-color);
+ }
+ .d2h-auto-color-scheme .d2h-deleted-tag {
+ border: 1px solid #f85149;
+ border: 1px solid var(--d2h-dark-del-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-added-tag {
+ border: 1px solid #3fb950;
+ border: 1px solid var(--d2h-dark-ins-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-changed-tag {
+ border: 1px solid #d29922;
+ border: 1px solid var(--d2h-dark-change-label-color);
+ }
+ .d2h-auto-color-scheme .d2h-moved-tag {
+ border: 1px solid #3572b0;
+ border: 1px solid var(--d2h-dark-moved-label-color);
+ }
+}
diff --git a/packages/bruno-app/src/components/ApiSpecPanel/FileEditor/CodeEditor/StyledWrapper.js b/packages/bruno-app/src/components/ApiSpecPanel/FileEditor/CodeEditor/StyledWrapper.js
index 57be41098..5f60a144a 100644
--- a/packages/bruno-app/src/components/ApiSpecPanel/FileEditor/CodeEditor/StyledWrapper.js
+++ b/packages/bruno-app/src/components/ApiSpecPanel/FileEditor/CodeEditor/StyledWrapper.js
@@ -2,10 +2,11 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
div.CodeMirror {
- height: calc(100vh - 4rem);
+ height: calc(100vh - 9rem);
background: ${(props) => props.theme.codemirror.bg};
border: solid 1px ${(props) => props.theme.codemirror.border};
font-family: ${(props) => (props.font ? props.font : 'default')};
+ font-size: ${(props) => props.theme.font.size.base};
line-break: anywhere;
}
diff --git a/packages/bruno-app/src/components/ApiSpecPanel/FileEditor/index.js b/packages/bruno-app/src/components/ApiSpecPanel/FileEditor/index.js
deleted file mode 100644
index 4d27290f4..000000000
--- a/packages/bruno-app/src/components/ApiSpecPanel/FileEditor/index.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import get from 'lodash/get';
-import { useTheme } from 'providers/Theme';
-import { useDispatch, useSelector } from 'react-redux';
-import CodeEditor from './CodeEditor/index';
-import { IconDeviceFloppy } from '@tabler/icons';
-import { saveApiSpecToFile } from 'providers/ReduxStore/slices/apiSpec';
-import { useState } from 'react';
-
-const FileEditor = ({ apiSpec }) => {
- const dispatch = useDispatch();
- const { displayedTheme, theme } = useTheme();
- const preferences = useSelector((state) => state.app.preferences);
-
- const [content, setContent] = useState(apiSpec?.raw);
-
- const onEdit = (value) => {
- setContent(value);
- };
-
- const onSave = () => {
- dispatch(saveApiSpecToFile({ uid: apiSpec?.uid, content }));
- };
-
- const hasChanges = Boolean(content != apiSpec?.raw);
-
- const editorMode = 'yaml';
-
- return (
-
-
-
-
- );
-};
-
-export default FileEditor;
diff --git a/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/StyledWrapper.js b/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/StyledWrapper.js
index 21dd2882d..ac9233da6 100644
--- a/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/StyledWrapper.js
+++ b/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/StyledWrapper.js
@@ -2,15 +2,868 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
.swagger-root {
- height: calc(100vh - 4rem);
- border: solid 1px ${(props) => props.theme.codemirror.border};
+ height: calc(100vh - 7rem);
+ border-left: solid 1px ${(props) => props.theme.border.border1};
+ overflow-y: auto;
+ background: ${(props) => props.theme.bg};
+ padding-bottom: 20px;
- &.dark {
- .swagger-ui {
- filter: invert(88%) hue-rotate(180deg);
+ /* ── Global reset ── */
+ .swagger-ui {
+ font-family: inherit;
+ font-size: ${(props) => props.theme.font.size.base};
+ color: ${(props) => props.theme.text};
+
+ * {
+ border-color: ${(props) => props.theme.border.border1};
}
- .swagger-ui .microlight {
- filter: invert(100%) hue-rotate(180deg);
+
+ .auth-container {
+ padding: 0;
+ }
+
+ select {
+ box-shadow: none !important;
+ }
+
+ .wrapper {
+ padding: 0 20px;
+ max-width: none;
+ }
+
+ /* ── Info section ── */
+ .info {
+ margin: 16px 0 12px;
+
+ hgroup.main {
+ margin: 0;
+ }
+
+ .title {
+ font-size: 16px;
+ font-weight: 600;
+ color: ${(props) => props.theme.text};
+
+ small {
+ padding: 2px 6px !important;
+ font-size: 10px;
+ vertical-align: middle;
+ border-radius: 3px;
+
+ pre {
+ color: ${(props) => props.theme.text} !important;
+ font-size: 10px;
+ }
+ }
+ }
+
+ .base-url {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .description {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+
+ p, li {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+ margin: 3px 0;
+ line-height: 1.5;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ color: ${(props) => props.theme.text};
+ }
+
+ a {
+ color: ${(props) => props.theme.textLink};
+ }
+ }
+ }
+
+ /* Version / OAS badges */
+ .version-stamp span.version {
+ background: ${(props) => props.theme.border.border1} !important;
+ border: 1px solid ${(props) => props.theme.colors.text.muted} !important;
+ color: ${(props) => props.theme.text} !important;
+ font-size: 9px;
+ padding: 2px 6px;
+ border-radius: 3px;
+ }
+
+ .version-pragma {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ /* ── Tag section headings ── */
+ .opblock-tag-section {
+ .opblock-tag {
+ font-size: ${(props) => props.theme.font.size.md};
+ color: ${(props) => props.theme.text};
+ border-bottom: none;
+ padding: 0;
+
+ &:hover {
+ background: ${(props) => props.theme.background.mantle};
+ }
+
+ a {
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ small {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+ padding: 0 10px;
+ }
+ }
+ }
+
+ /* ── Operation blocks (GET, POST, PUT, DELETE, PATCH) ── */
+ .opblock {
+ margin: 0 0 8px;
+ border-radius: 4px;
+ border: 1px solid ${(props) => props.theme.border.border1} !important;
+ background: ${(props) => props.theme.bg} !important;
+ box-shadow: none !important;
+
+ .opblock-summary {
+ padding: 6px 10px;
+ border: none !important;
+ background: transparent !important;
+
+ .opblock-summary-method {
+ font-size: 10px;
+ font-weight: 700;
+ padding: 3px 8px;
+ min-width: 50px;
+ text-align: center;
+ border-radius: 3px;
+ }
+
+ .opblock-summary-path {
+ font-size: ${(props) => props.theme.font.size.sm};
+
+ a, span {
+ color: ${(props) => props.theme.text} !important;
+ }
+ }
+
+ .opblock-summary-description {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .opblock-summary-control {
+ svg {
+ fill: ${(props) => props.theme.colors.text.muted};
+ width: 14px;
+ height: 14px;
+ }
+ }
+ }
+
+ .opblock-body {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+ background: ${(props) => props.theme.bg};
+ border-top: 1px solid ${(props) => props.theme.border.border1};
+
+ .opblock-description-wrapper,
+ .opblock-section {
+ p {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: ${(props) => props.theme.font.size.sm};
+ }
+ }
+
+ .tab-header .tab-item {
+ color: ${(props) => props.theme.colors.text.muted};
+
+ &.active {
+ color: ${(props) => props.theme.text};
+ }
+ }
+
+ select {
+ color: ${(props) => props.theme.text};
+ background: ${(props) => props.theme.bg};
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: 3px;
+ font-size: ${(props) => props.theme.font.size.xs};
+ padding: 2px 6px;
+ }
+
+ input[type="text"] {
+ color: ${(props) => props.theme.text};
+ background: ${(props) => props.theme.bg};
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: 3px;
+ font-size: ${(props) => props.theme.font.size.sm};
+ }
+ }
+ }
+
+ /* Method badge colors — keep them but tone down */
+ .opblock.opblock-get .opblock-summary-method { background: #61affe; color: #fff; }
+ .opblock.opblock-post .opblock-summary-method { background: #49cc90; color: #fff; }
+ .opblock.opblock-put .opblock-summary-method { background: #fca130; color: #fff; }
+ .opblock.opblock-delete .opblock-summary-method { background: #f93e3e; color: #fff; }
+ .opblock.opblock-patch .opblock-summary-method { background: #50e3c2; color: #000; }
+
+ /* Lock / authorization icons */
+ .authorization__btn {
+
+ svg {
+ fill: ${(props) => props.theme.colors.text.muted};
+ width: 14px;
+ height: 14px;
+ }
+ }
+
+ /* ── Tables ── */
+ table {
+ font-size: ${(props) => props.theme.font.size.sm};
+
+ thead {
+ tr {
+ th {
+ font-size: ${(props) => props.theme.font.size.xs} !important;
+ color: ${(props) => props.theme.colors.text.muted} !important;
+ border-bottom: 1px solid ${(props) => props.theme.border.border1} !important;
+ padding: 6px 0;
+ }
+ }
+ }
+
+ td {
+ padding: 6px 0;
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+ color: ${(props) => props.theme.text};
+ }
+ }
+
+ .parameter__name {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+
+ &.required::after {
+ color: ${(props) => props.theme.colors.text.danger || '#c0392b'};
+ font-size: ${(props) => props.theme.font.size.xs};
+ }
+ }
+
+ .parameter__type {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .parameter__in {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ /* ── Models / Schemas ── */
+ section.models {
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: 4px;
+ background: ${(props) => props.theme.bg};
+ padding-bottom: 0px;
+ margin-bottom: 40px;
+ margin-top: 8px;
+
+ h4 {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+ border-bottom: none;
+ padding: 6px 10px;
+ margin: 0;
+
+ svg {
+ fill: ${(props) => props.theme.colors.text.muted};
+ width: 16px;
+ height: 16px;
+ }
+ }
+
+ .model-container {
+ background: ${(props) => props.theme.bg} !important;
+ margin: 0;
+ padding: 4px 8px;
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .model-box {
+ background: ${(props) => props.theme.bg} !important;
+ padding: 2px 0;
+ }
+ }
+ }
+
+ .model {
+ font-size: 11px;
+ color: ${(props) => props.theme.text};
+ line-height: 1.4;
+
+ .prop-type {
+ color: ${(props) => props.theme.textLink};
+ font-size: 11px;
+ }
+
+ .prop-format {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: 10px;
+ }
+
+ span.prop-enum {
+ display: block;
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: 10px;
+ }
+ }
+
+ .model-example {
+
+ .tab li {
+ color: ${(props) => props.theme.colors.text.muted} !important;
+ }
+ }
+
+ /* Model expand/collapse toggle */
+ .model-toggle {
+ cursor: pointer;
+ font-size: 10px;
+ color: ${(props) => props.theme.colors.text.muted};
+
+ &::after {
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+ }
+
+ /* Model box inner styling */
+ .model-box {
+ background: ${(props) => props.theme.bg} !important;
+ color: ${(props) => props.theme.text};
+ }
+
+ /* Inner model details */
+ .inner-object {
+ color: ${(props) => props.theme.text};
+ }
+
+ /* Model title (schema name) */
+ .model-title {
+ color: ${(props) => props.theme.text};
+ font-size: 12px;
+ font-weight: 600;
+ }
+
+ /* ── JSON Schema 2020-12 (OpenAPI 3.1) schema overrides ── */
+ .json-schema-2020-12-accordion,
+ .json-schema-2020-12-expand-deep-button,
+ section.models h4 button,
+ .model-box button,
+ .models-control,
+ .opblock-summary,
+ .opblock-summary-control,
+ .opblock-tag {
+ outline: none !important;
+ box-shadow: none !important;
+ }
+
+ button:focus-visible,
+ .opblock-summary:focus-visible,
+ .opblock-tag:focus-visible,
+ .models-control:focus-visible {
+ outline: 2px solid ${(props) => props.theme.textLink} !important;
+ outline-offset: 2px;
+ }
+
+ .json-schema-2020-12__title {
+ font-size: 12px !important;
+ font-weight: 600;
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ .json-schema-2020-12-head {
+ padding: 4px 8px !important;
+ background: ${(props) => props.theme.bg} !important;
+
+ .json-schema-2020-12-accordion {
+ padding: 0 !important;
+ color: ${(props) => props.theme.text} !important;
+ background: transparent !important;
+ }
+
+ /* chevron / arrow icon */
+ .json-schema-2020-12-accordion__icon {
+ fill: ${(props) => props.theme.colors.text.muted} !important;
+ }
+
+ button.json-schema-2020-12-expand-deep-button {
+ font-size: 10px !important;
+ color: ${(props) => props.theme.colors.text.muted} !important;
+ background: transparent !important;
+ padding: 0 4px !important;
+ }
+
+ strong.json-schema-2020-12__attribute--primary {
+ font-size: 11px !important;
+ color: ${(props) => props.theme.textLink} !important;
+ font-weight: normal;
+ }
+ }
+
+ .json-schema-2020-12-body {
+ font-size: 11px !important;
+ margin-left: 16px;
+ color: ${(props) => props.theme.text} !important;
+
+ .json-schema-2020-12-property {
+ margin-left: 8px;
+ color: ${(props) => props.theme.text} !important;
+ border-color: ${(props) => props.theme.border.border1} !important;
+ }
+
+ /* property names */
+ .json-schema-2020-12__title {
+ font-size: 11px !important;
+ font-weight: normal;
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ /* type badges inside expanded schema */
+ strong.json-schema-2020-12__attribute--primary {
+ font-size: 10px !important;
+ color: ${(props) => props.theme.textLink} !important;
+ font-weight: normal;
+ }
+
+ strong.json-schema-2020-12__attribute {
+ font-size: 10px !important;
+ color: ${(props) => props.theme.colors.text.muted} !important;
+ font-weight: normal;
+ }
+ }
+
+ .json-schema-2020-12 {
+ font-size: 11px !important;
+ margin: 0 !important;
+ width: 100%;
+ height: 100%;
+ color: ${(props) => props.theme.text} !important;
+ background: ${(props) => props.theme.bg} !important;
+ }
+
+ /* JSON viewer (Examples section inside schema properties) */
+ .json-schema-2020-12-json-viewer {
+ background: transparent !important;
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ .json-schema-2020-12-json-viewer__name {
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ .json-schema-2020-12-json-viewer__name--secondary {
+ color: ${(props) => props.theme.colors.text.muted} !important;
+ font-weight: normal !important;
+ }
+
+ .json-schema-2020-12-json-viewer__value {
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ .json-schema-2020-12-json-viewer__value--secondary {
+ color: ${(props) => props.theme.colors.text.subtext0} !important;
+ }
+
+ .json-schema-2020-12-json-viewer__value--string,
+ .json-schema-2020-12-json-viewer__value--string.json-schema-2020-12-json-viewer__value--secondary {
+ color: ${(props) => props.theme.colors.text.green} !important;
+ }
+
+ .json-schema-2020-12-json-viewer__value--number,
+ .json-schema-2020-12-json-viewer__value--bigint,
+ .json-schema-2020-12-json-viewer__value--number.json-schema-2020-12-json-viewer__value--secondary,
+ .json-schema-2020-12-json-viewer__value--bigint.json-schema-2020-12-json-viewer__value--secondary {
+ color: ${(props) => props.theme.textLink} !important;
+ }
+
+ .json-schema-2020-12-json-viewer__value--boolean,
+ .json-schema-2020-12-json-viewer__value--boolean.json-schema-2020-12-json-viewer__value--secondary {
+ color: ${(props) => props.theme.colors.text.warning} !important;
+ }
+
+ .json-schema-2020-12-json-viewer__value--null,
+ .json-schema-2020-12-json-viewer__value--undefined {
+ color: ${(props) => props.theme.colors.text.muted} !important;
+ }
+
+ /* enum/keyword example values container */
+ .json-schema-2020-12-keyword--examples,
+ [data-json-schema-keyword="examples"] {
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ /* Model collapse/expand all link */
+ span.model-toggle {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: 10px;
+ }
+
+ /* Brace styling in models */
+ .brace-open, .brace-close {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: 11px;
+ }
+
+ /* ── Code / Response blocks ── */
+ .microlight {
+ background: ${(props) => props.theme.codemirror.bg} !important;
+ color: ${(props) => props.theme.text} !important;
+ font-size: ${(props) => props.theme.font.size.xs};
+ border-radius: 4px;
+ padding: 8px;
+ border: 1px solid ${(props) => props.theme.border.border1};
+ }
+
+ .highlight-code {
+ background: ${(props) => props.theme.codemirror.bg} !important;
+
+ > .microlight {
+ border: none;
+ }
+ }
+
+ pre {
+ color: ${(props) => props.theme.text};
+ font-size: ${(props) => props.theme.font.size.xs};
+ border-radius: 4px;
+ }
+
+ .response-col_status {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+ }
+
+ .response-col_description {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .responses-inner {
+ h4, h5 {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+ }
+ }
+
+ /* ── Buttons ── */
+ .btn {
+ font-size: ${(props) => props.theme.font.size.xs};
+ border-radius: 4px;
+ box-shadow: none !important;
+ color: ${(props) => props.theme.text};
+ border-color: ${(props) => props.theme.border.border1};
+ background: transparent;
+ }
+
+ .btn.authorize {
+ color: ${(props) => props.theme.text};
+ border-color: ${(props) => props.theme.border.border1};
+ background: transparent;
+
+ svg {
+ fill: ${(props) => props.theme.text};
+ }
+
+ span {
+ color: ${(props) => props.theme.text};
+ }
+ }
+
+ .btn.execute {
+ background: ${(props) => props.theme.primary?.solid || props.theme.textLink};
+ color: #fff;
+ border-color: transparent;
+ }
+
+ .btn-group {
+ .btn {
+ background: ${(props) => props.theme.bg};
+ color: ${(props) => props.theme.text};
+ }
+ }
+
+ /* ── Links ── */
+ a {
+ color: ${(props) => props.theme.textLink};
+ }
+
+ /* ── Servers / Scheme container ── */
+ .scheme-container {
+ background: ${(props) => props.theme.background.mantle} !important;
+ border-top: 1px solid ${(props) => props.theme.border.border1};
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+ padding: 10px;
+ box-shadow: none !important;
+
+ .schemes-title {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ label {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ select {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+ background: ${(props) => props.theme.bg};
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: 4px;
+ padding: 4px 8px;
+ }
+ }
+
+ /* ── SVGs / icons ── */
+ svg {
+ fill: ${(props) => props.theme.colors.text.muted};
+ }
+
+ svg.arrow {
+ fill: ${(props) => props.theme.text};
+ width: 12px;
+ height: 12px;
+ margin-left: 4px;
+ }
+
+ .expand-operation svg {
+ fill: ${(props) => props.theme.colors.text.muted};
+ width: 14px;
+ height: 14px;
+ }
+
+ /* ── Misc / catch-all ── */
+ .loading-container .loading::after {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: ${(props) => props.theme.font.size.sm};
+ }
+
+ .renderedMarkdown p {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: ${(props) => props.theme.font.size.sm};
+ }
+
+ .opblock-section-header {
+ background: ${(props) => props.theme.background.mantle} !important;
+ box-shadow: none !important;
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+ padding: 6px 10px;
+
+ h4 {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+ }
+
+ label {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+ }
+
+ .copy-to-clipboard {
+ button {
+ background: ${(props) => props.theme.background.mantle};
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: 3px;
+ }
+ }
+
+ /* Dialog / modal overrides */
+ .dialog-ux {
+ .modal-ux {
+ background: ${(props) => props.theme.bg};
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: 6px;
+ color: ${(props) => props.theme.text};
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
+
+ .modal-ux-header {
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+ padding: 12px 0px;
+
+ h3 {
+ font-size: ${(props) => props.theme.font.size.md};
+ font-weight: 600;
+ color: ${(props) => props.theme.text};
+ }
+
+ .close-modal {
+ opacity: 0.6;
+ &:hover { opacity: 1; }
+ svg { fill: ${(props) => props.theme.text}; }
+ }
+ }
+
+ .modal-ux-content {
+ color: ${(props) => props.theme.text};
+ padding: 12px 16px;
+
+ p {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ /* Section headings like "api_key (apiKey)" */
+ h4, h5, h6 {
+ font-size: ${(props) => props.theme.font.size.sm};
+ font-weight: 600;
+ color: ${(props) => props.theme.textLink};
+ margin: 12px 0 6px;
+ }
+
+ /* Labels: "Name:", "In:", "Flow:", "Value:", etc. */
+ label {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+
+ > span {
+ font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+ }
+
+ /* "Scopes:" heading */
+ .scopes h2 {
+ font-size: ${(props) => props.theme.font.size.sm} !important;
+ font-weight: 500;
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ /* Scope item name + description */
+ .scopes .checkbox {
+ p.name {
+ font-size: ${(props) => props.theme.font.size.sm} !important;
+ color: ${(props) => props.theme.text} !important;
+ font-weight: 500;
+ margin: 0;
+ }
+
+ p.description {
+ font-size: ${(props) => props.theme.font.size.xs} !important;
+ color: ${(props) => props.theme.colors.text.muted} !important;
+ margin: 0;
+ }
+ }
+
+ /* Text inputs */
+ input[type="text"],
+ input[type="password"],
+ input[type="email"] {
+ background: ${(props) => props.theme.background.mantle} !important;
+ color: ${(props) => props.theme.text} !important;
+ border: 1px solid ${(props) => props.theme.border.border1} !important;
+ border-radius: 4px !important;
+ font-size: ${(props) => props.theme.font.size.sm} !important;
+ padding: 6px 10px !important;
+ outline: none !important;
+ box-shadow: none !important;
+
+ &:focus {
+ border-color: ${(props) => props.theme.textLink} !important;
+ outline: none !important;
+ box-shadow: none !important;
+ }
+ }
+
+ /* Checkboxes — custom styled to match theme */
+ input[type="checkbox"] {
+ appearance: none !important;
+ -webkit-appearance: none !important;
+ width: 14px !important;
+ height: 14px !important;
+ min-width: 14px;
+ border: 1px solid ${(props) => props.theme.border.border1} !important;
+ border-radius: 3px !important;
+ background: ${(props) => props.theme.background.mantle} !important;
+ cursor: pointer;
+ position: relative;
+ vertical-align: middle;
+
+ &:checked {
+ background: ${(props) => props.theme.textLink} !important;
+ border-color: ${(props) => props.theme.textLink} !important;
+
+ &::after {
+ content: '';
+ position: absolute;
+ left: 3px;
+ top: 1px;
+ width: 5px;
+ height: 8px;
+ border: 2px solid #fff;
+ border-top: none;
+ border-left: none;
+ transform: rotate(45deg);
+ }
+ }
+ }
+
+ /* "select all / select none" links */
+ a {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.textLink};
+ }
+
+ /* Dividers between auth sections */
+ hr {
+ border-color: ${(props) => props.theme.border.border1};
+ margin: 12px 0;
+ }
+
+ /* Authorize / Close buttons */
+ .btn-done,
+ .auth-btn-wrapper .btn {
+ font-size: ${(props) => props.theme.font.size.sm};
+ border-radius: 4px;
+ padding: 6px 16px;
+ border: 1px solid ${(props) => props.theme.border.border1};
+ background: transparent;
+ color: ${(props) => props.theme.text};
+ cursor: pointer;
+ outline: none !important;
+ box-shadow: none !important;
+
+ &:hover {
+ background: ${(props) => props.theme.background.mantle};
+ }
+
+ &.modal-btn-operation {
+ background: ${(props) => props.theme.textLink};
+ color: #fff;
+ border-color: transparent;
+
+ &:hover {
+ opacity: 0.9;
+ }
+ }
+ }
+ }
+ }
+
+ .backdrop-ux {
+ background: rgba(0, 0, 0, 0.5);
+ }
}
}
}
diff --git a/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/index.js b/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/index.js
index 3e59f3bc5..d37b6b63c 100644
--- a/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/index.js
+++ b/packages/bruno-app/src/components/ApiSpecPanel/Renderers/Swagger/index.js
@@ -1,16 +1,11 @@
import SwaggerUI from 'swagger-ui-react';
import StyledWrapper from './StyledWrapper';
-import { useTheme } from 'providers/Theme';
-
-const Swagger = ({ string }) => {
- const { displayedTheme } = useTheme();
-
- console.log('string', string);
+const Swagger = ({ spec }) => {
return (
-
-
+
+
);
diff --git a/packages/bruno-app/src/components/ApiSpecPanel/SpecViewer.js b/packages/bruno-app/src/components/ApiSpecPanel/SpecViewer.js
new file mode 100644
index 000000000..97d14c65e
--- /dev/null
+++ b/packages/bruno-app/src/components/ApiSpecPanel/SpecViewer.js
@@ -0,0 +1,71 @@
+import React, { useState, useEffect, Suspense } from 'react';
+import get from 'lodash/get';
+import { useTheme } from 'providers/Theme';
+import { useSelector } from 'react-redux';
+import { IconDeviceFloppy } from '@tabler/icons';
+import CodeEditor from './FileEditor/CodeEditor/index';
+import Swagger from './Renderers/Swagger';
+
+/**
+ * Shared split-pane spec viewer: CodeEditor (left) + Swagger preview (right).
+ *
+ * Props:
+ * - content (string) The spec content (YAML/JSON string)
+ * - readOnly (boolean) If true, editor is not editable and save icon is hidden
+ * - onSave (function) Called with current editor content on save (editable mode only)
+ */
+const SpecViewer = ({ content, readOnly, onSave }) => {
+ const { displayedTheme, theme } = useTheme();
+ const preferences = useSelector((state) => state.app.preferences);
+
+ const [editorContent, setEditorContent] = useState(content);
+
+ // Sync editor when saved content changes from outside (e.g. after save completes)
+ useEffect(() => {
+ setEditorContent(content);
+ }, [content]);
+
+ const hasChanges = !readOnly && editorContent !== content;
+
+ const handleSave = () => {
+ if (onSave) onSave(editorContent);
+ };
+
+ return (
+
+
+
+
+ setEditorContent(val)}
+ onSave={readOnly ? undefined : handleSave}
+ mode="yaml"
+ font={get(preferences, 'font.codeFont', 'default')}
+ />
+ {!readOnly && onSave && (
+
+ )}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default SpecViewer;
diff --git a/packages/bruno-app/src/components/ApiSpecPanel/index.js b/packages/bruno-app/src/components/ApiSpecPanel/index.js
index f6359ffdb..35f574000 100644
--- a/packages/bruno-app/src/components/ApiSpecPanel/index.js
+++ b/packages/bruno-app/src/components/ApiSpecPanel/index.js
@@ -3,13 +3,11 @@ import find from 'lodash/find';
import { useSelector, useDispatch } from 'react-redux';
import { IconFileCode, IconDots } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
-import FileEditor from './FileEditor';
+import SpecViewer from './SpecViewer';
import Dropdown from 'components/Dropdown';
-import { openApiSpec } from 'providers/ReduxStore/slices/apiSpec';
+import { openApiSpec, saveApiSpecToFile } from 'providers/ReduxStore/slices/apiSpec';
import { useState } from 'react';
import CreateApiSpec from 'components/Sidebar/ApiSpecs/CreateApiSpec';
-import { Suspense } from 'react';
-import Swagger from './Renderers/Swagger';
import toast from 'react-hot-toast';
const ApiSpecPanel = () => {
@@ -78,18 +76,10 @@ const ApiSpecPanel = () => {
-
+ dispatch(saveApiSpecToFile({ uid, content }))}
+ />
);
};
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/CollapsibleDiffRow.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/CollapsibleDiffRow.js
new file mode 100644
index 000000000..13344949f
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/CollapsibleDiffRow.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import { IconChevronDown, IconChevronRight } from '@tabler/icons';
+
+const CollapsibleDiffRow = ({ title, isCollapsed, onToggle, oldContent, newContent, hasOldContent, hasNewContent }) => {
+ if (!hasOldContent && !hasNewContent) {
+ return null;
+ }
+
+ return (
+
+
+
+ {isCollapsed ? (
+
+ ) : (
+
+ )}
+
+ {title}
+
+ {!isCollapsed && (
+
+
+ {hasOldContent ? oldContent :
}
+
+
+ {hasNewContent ? newContent :
}
+
+
+ )}
+
+ );
+};
+
+export default CollapsibleDiffRow;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffAuth.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffAuth.js
new file mode 100644
index 000000000..26a9eb904
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffAuth.js
@@ -0,0 +1,199 @@
+import React, { useMemo } from 'react';
+import get from 'lodash/get';
+import isEqual from 'lodash/isEqual';
+
+const AUTH_TYPE_LABELS = {
+ awsv4: 'AWS Signature v4',
+ basic: 'Basic Auth',
+ bearer: 'Bearer Token',
+ digest: 'Digest Auth',
+ ntlm: 'NTLM',
+ oauth2: 'OAuth 2.0',
+ wsse: 'WSSE',
+ apikey: 'API Key'
+};
+
+const AUTH_FIELD_LABELS = {
+ // AWS v4
+ accessKeyId: 'Access Key ID',
+ secretAccessKey: 'Secret Access Key',
+ sessionToken: 'Session Token',
+ service: 'Service',
+ region: 'Region',
+ profileName: 'Profile Name',
+ // Basic/Digest/NTLM/WSSE
+ username: 'Username',
+ password: 'Password',
+ domain: 'Domain',
+ // Bearer
+ token: 'Token',
+ // API Key
+ key: 'Key',
+ value: 'Value',
+ placement: 'Placement',
+ // OAuth2
+ grantType: 'Grant Type',
+ callbackUrl: 'Callback URL',
+ authorizationUrl: 'Authorization URL',
+ accessTokenUrl: 'Access Token URL',
+ refreshTokenUrl: 'Refresh Token URL',
+ clientId: 'Client ID',
+ clientSecret: 'Client Secret',
+ scope: 'Scope',
+ state: 'State',
+ pkce: 'PKCE',
+ credentialsPlacement: 'Credentials Placement',
+ credentialsId: 'Credentials ID',
+ tokenPlacement: 'Token Placement',
+ tokenHeaderPrefix: 'Token Header Prefix',
+ tokenQueryKey: 'Token Query Key',
+ autoFetchToken: 'Auto Fetch Token',
+ autoRefreshToken: 'Auto Refresh Token'
+};
+
+const VisualDiffAuth = ({ oldData, newData, showSide }) => {
+ const oldAuth = get(oldData, 'request.auth', {});
+ const newAuth = get(newData, 'request.auth', {});
+
+ const currentAuth = showSide === 'old' ? oldAuth : newAuth;
+ const otherAuth = showSide === 'old' ? newAuth : oldAuth;
+
+ const authTypes = useMemo(() => {
+ const types = new Set([...Object.keys(currentAuth), ...Object.keys(otherAuth)]);
+ types.delete('mode');
+ return Array.from(types);
+ }, [currentAuth, otherAuth]);
+
+ const authSections = useMemo(() => {
+ return authTypes.map((authType) => {
+ const rawCurrentConfig = currentAuth[authType];
+ const rawOtherConfig = otherAuth[authType];
+ const currentConfig = (typeof rawCurrentConfig === 'object' && rawCurrentConfig !== null) ? rawCurrentConfig : {};
+ const otherConfig = (typeof rawOtherConfig === 'object' && rawOtherConfig !== null) ? rawOtherConfig : {};
+
+ if (Object.keys(currentConfig).length === 0 && showSide === 'old') {
+ return null;
+ }
+ if (Object.keys(currentConfig).length === 0 && showSide === 'new') {
+ return null;
+ }
+
+ let sectionStatus = 'unchanged';
+ if (Object.keys(otherConfig).length === 0) {
+ sectionStatus = showSide === 'old' ? 'deleted' : 'added';
+ } else if (!isEqual(currentConfig, otherConfig)) {
+ sectionStatus = 'modified';
+ }
+
+ const allFields = new Set([...Object.keys(currentConfig), ...Object.keys(otherConfig)]);
+ const fields = Array.from(allFields).map((field) => {
+ const currentValue = currentConfig[field];
+ const otherValue = otherConfig[field];
+
+ let status = 'unchanged';
+ if (otherValue === undefined) {
+ status = showSide === 'old' ? 'deleted' : 'added';
+ } else if (currentValue !== otherValue) {
+ status = 'modified';
+ }
+
+ let displayValue = currentValue;
+ if (typeof displayValue === 'boolean') {
+ displayValue = displayValue ? 'true' : 'false';
+ } else if (displayValue === undefined || displayValue === null) {
+ displayValue = '';
+ }
+
+ return {
+ key: AUTH_FIELD_LABELS[field] || field,
+ value: String(displayValue),
+ status
+ };
+ });
+
+ return {
+ type: authType,
+ label: AUTH_TYPE_LABELS[authType] || authType,
+ status: sectionStatus,
+ fields
+ };
+ }).filter(Boolean);
+ }, [authTypes, currentAuth, otherAuth, showSide]);
+
+ const currentMode = currentAuth.mode;
+ const otherMode = otherAuth.mode;
+ const modeStatus = currentMode !== otherMode ? (otherMode === undefined ? (showSide === 'old' ? 'deleted' : 'added') : 'modified') : 'unchanged';
+
+ if (authSections.length === 0 && !currentMode) {
+ return null;
+ }
+
+ return (
+ <>
+ {currentMode && (
+
+
+
+
+ |
+ Field |
+ Value |
+
+
+
+
+ |
+ {modeStatus !== 'unchanged' && (
+
+ {modeStatus === 'added' ? 'A' : modeStatus === 'deleted' ? 'D' : 'M'}
+
+ )}
+ |
+ Auth Mode |
+ {AUTH_TYPE_LABELS[currentMode] || currentMode} |
+
+
+
+
+ )}
+ {authSections.map((section) => (
+
+
+ {section.label}
+ {section.status !== 'unchanged' && (
+
+ {section.status === 'added' ? 'A' : section.status === 'deleted' ? 'D' : 'M'}
+
+ )}
+
+
+
+
+ |
+ Field |
+ Value |
+
+
+
+ {section.fields.map((field, index) => (
+
+ |
+ {field.status !== 'unchanged' && (
+
+ {field.status === 'added' ? 'A' : field.status === 'deleted' ? 'D' : 'M'}
+
+ )}
+ |
+ {field.key} |
+ {field.value} |
+
+ ))}
+
+
+
+ ))}
+ >
+ );
+};
+
+export default VisualDiffAuth;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffBody.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffBody.js
new file mode 100644
index 000000000..b4ac5700e
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffBody.js
@@ -0,0 +1,353 @@
+import React, { useMemo } from 'react';
+import get from 'lodash/get';
+import isEqual from 'lodash/isEqual';
+import { computeLineDiffForOld, computeLineDiffForNew } from './utils/diffUtils';
+
+const BODY_TYPE_LABELS = {
+ json: 'JSON',
+ text: 'Text',
+ xml: 'XML',
+ sparql: 'SPARQL',
+ graphql: 'GraphQL',
+ formUrlEncoded: 'Form URL Encoded',
+ multipartForm: 'Multipart Form',
+ file: 'File',
+ grpc: 'gRPC',
+ ws: 'WebSocket'
+};
+
+const TEXT_BODY_TYPES = ['json', 'text', 'xml', 'sparql'];
+const FORM_BODY_TYPES = ['formUrlEncoded', 'multipartForm'];
+const ALL_BODY_TYPES = Object.keys(BODY_TYPE_LABELS);
+
+const VisualDiffBody = ({ oldData, newData, showSide }) => {
+ const oldBody = get(oldData, 'request.body', {});
+ const newBody = get(newData, 'request.body', {});
+
+ const currentBody = showSide === 'old' ? oldBody : newBody;
+ const otherBody = showSide === 'old' ? newBody : oldBody;
+
+ const bodyTypes = useMemo(() => {
+ const currentMode = currentBody.mode;
+ const otherMode = otherBody.mode;
+
+ // Collect body types that match either side's active mode
+ const relevantTypes = new Set();
+ if (currentMode && currentMode !== 'none') {
+ relevantTypes.add(currentMode);
+ }
+ if (otherMode && otherMode !== 'none') {
+ relevantTypes.add(otherMode);
+ }
+
+ // If neither side has a mode (legacy data), fall back to showing all defined types
+ if (relevantTypes.size === 0) {
+ return ALL_BODY_TYPES.filter((type) => {
+ const currentVal = currentBody[type];
+ const otherVal = otherBody[type];
+ return currentVal !== undefined || otherVal !== undefined;
+ });
+ }
+
+ // Only show body types that match the active mode on either side
+ return ALL_BODY_TYPES.filter((type) => {
+ if (!relevantTypes.has(type)) return false;
+ const currentVal = currentBody[type];
+ const otherVal = otherBody[type];
+ return currentVal !== undefined || otherVal !== undefined;
+ });
+ }, [currentBody, otherBody]);
+
+ const renderLineDiff = (segments) => {
+ return segments.map((segment, index) => (
+
+ {segment.text || '\u00A0'}
+
+ ));
+ };
+
+ const renderFormData = (items, otherItems) => {
+ if (!items || items.length === 0) return null;
+
+ const otherItemMap = new Map();
+ (otherItems || []).forEach((item) => {
+ otherItemMap.set(item.name, item);
+ });
+
+ return (
+
+ );
+ };
+
+ const renderFileBody = (files, otherFiles) => {
+ if (!files || files.length === 0) return null;
+
+ const otherFileMap = new Map();
+ (otherFiles || []).forEach((f, idx) => {
+ otherFileMap.set(f.filePath || idx, f);
+ });
+
+ return (
+
+ );
+ };
+
+ const renderMessageBody = (messages, otherMessages, typeLabel) => {
+ if (!messages || messages.length === 0) return null;
+
+ return messages.map((msg, index) => {
+ const otherMsg = (otherMessages || [])[index];
+ const contentDiff = showSide === 'old'
+ ? computeLineDiffForOld(msg.content || '', otherMsg?.content || '')
+ : computeLineDiffForNew(otherMsg?.content || '', msg.content || '');
+
+ let msgStatus = 'unchanged';
+ if (!otherMsg) {
+ msgStatus = showSide === 'old' ? 'deleted' : 'added';
+ } else if (msg.name !== otherMsg.name || msg.type !== otherMsg.type) {
+ msgStatus = 'modified';
+ }
+
+ return (
+
+
+ {typeLabel}: {msg.name || `Message ${index + 1}`}{msg.type ? ` (${msg.type})` : ''}
+ {msgStatus !== 'unchanged' && (
+
+ {msgStatus === 'added' ? 'A' : msgStatus === 'deleted' ? 'D' : 'M'}
+
+ )}
+
+
{renderLineDiff(contentDiff)}
+
+ );
+ });
+ };
+
+ const renderGraphqlBody = (graphql, otherGraphql) => {
+ const currentQuery = graphql?.query || '';
+ const otherQuery = otherGraphql?.query || '';
+ const currentVariables = graphql?.variables || '';
+ const otherVariables = otherGraphql?.variables || '';
+
+ const queryDiff = showSide === 'old'
+ ? computeLineDiffForOld(currentQuery, otherQuery)
+ : computeLineDiffForNew(otherQuery, currentQuery);
+
+ const variablesDiff = showSide === 'old'
+ ? computeLineDiffForOld(currentVariables, otherVariables)
+ : computeLineDiffForNew(otherVariables, currentVariables);
+
+ return (
+ <>
+ {(currentQuery || otherQuery) && (
+
+
Query
+
{renderLineDiff(queryDiff)}
+
+ )}
+ {(currentVariables || otherVariables) && (
+
+
Variables
+
{renderLineDiff(variablesDiff)}
+
+ )}
+ >
+ );
+ };
+
+ const renderTextBody = (currentContent, otherContent) => {
+ const diffSegments = showSide === 'old'
+ ? computeLineDiffForOld(currentContent || '', otherContent || '')
+ : computeLineDiffForNew(otherContent || '', currentContent || '');
+
+ return (
+
+ {renderLineDiff(diffSegments)}
+
+ );
+ };
+
+ const renderBodyType = (type) => {
+ const currentVal = currentBody[type];
+ const otherVal = otherBody[type];
+
+ if (currentVal === undefined && otherVal === undefined) return null;
+
+ // For text-based body types
+ if (TEXT_BODY_TYPES.includes(type)) {
+ if (!currentVal) return null;
+ return renderTextBody(currentVal, otherVal);
+ }
+
+ // For form data types
+ if (FORM_BODY_TYPES.includes(type)) {
+ return renderFormData(currentVal, otherVal);
+ }
+
+ // GraphQL
+ if (type === 'graphql') {
+ return renderGraphqlBody(currentVal, otherVal);
+ }
+
+ // File
+ if (type === 'file') {
+ return renderFileBody(currentVal, otherVal);
+ }
+
+ // gRPC
+ if (type === 'grpc') {
+ return renderMessageBody(currentVal, otherVal, 'gRPC');
+ }
+
+ // WebSocket
+ if (type === 'ws') {
+ return renderMessageBody(currentVal, otherVal, 'WebSocket');
+ }
+
+ return null;
+ };
+
+ // Show body mode if present
+ const currentMode = currentBody.mode;
+ const otherMode = otherBody.mode;
+ const modeStatus = currentMode !== otherMode ? (otherMode === undefined ? (showSide === 'old' ? 'deleted' : 'added') : 'modified') : 'unchanged';
+
+ if (bodyTypes.length === 0 && !currentMode) {
+ return null;
+ }
+
+ return (
+ <>
+ {currentMode && (
+
+
+
+
+ |
+ Field |
+ Value |
+
+
+
+
+ |
+ {modeStatus !== 'unchanged' && (
+
+ {modeStatus === 'added' ? 'A' : modeStatus === 'deleted' ? 'D' : 'M'}
+
+ )}
+ |
+ Body Mode |
+ {BODY_TYPE_LABELS[currentMode] || currentMode} |
+
+
+
+
+ )}
+ {bodyTypes.map((type) => {
+ const content = renderBodyType(type);
+ if (!content) return null;
+
+ const currentVal = currentBody[type];
+ const otherVal = otherBody[type];
+ const hasChanges = !isEqual(currentVal, otherVal);
+
+ return (
+
+
+ {BODY_TYPE_LABELS[type] || type}
+ {hasChanges && (
+
+ {otherVal === undefined ? (showSide === 'old' ? 'D' : 'A') : 'M'}
+
+ )}
+
+ {content}
+
+ );
+ })}
+ >
+ );
+};
+
+export default VisualDiffBody;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffContent/StyledWrapper.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffContent/StyledWrapper.js
new file mode 100644
index 000000000..b874a7aa7
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffContent/StyledWrapper.js
@@ -0,0 +1,443 @@
+import styled from 'styled-components';
+
+const StyledWrapper = styled.div`
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+
+ .visual-diff-content {
+ flex: 1;
+ overflow: auto;
+ }
+
+ .diff-header-row {
+ display: flex;
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: ${(props) => props.theme.border.radius.base};
+ margin-bottom: 1rem;
+ }
+
+ .diff-header-pane {
+ flex: 1;
+ padding: 0.5rem 0.75rem;
+ font-size: ${(props) => props.theme.font.size.xs};
+ font-weight: 600;
+ color: ${(props) => props.theme.colors.text.muted};
+ text-transform: uppercase;
+
+ &.old {
+ border-right: 1px solid ${(props) => props.theme.border.border1};
+ }
+ }
+
+ .diff-sections {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ .diff-row {
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: ${(props) => props.theme.border.radius.base};
+ overflow: hidden;
+ }
+
+ .diff-row-header {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.5rem 0.75rem;
+ background: ${(props) => props.theme.sidebar.bg};
+ cursor: pointer;
+ user-select: none;
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+
+ &:hover {
+ background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
+ }
+ }
+
+ .collapse-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .diff-row-title {
+ font-size: ${(props) => props.theme.font.size.sm};
+ font-weight: 500;
+ color: ${(props) => props.theme.text};
+ }
+
+ .diff-row-content {
+ display: flex;
+ gap: 1rem;
+ padding: 0.75rem;
+ background: ${(props) => props.theme.background.base};
+ }
+
+ .diff-row-pane {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+
+ &.old {
+ border-left: 2px solid ${(props) => props.theme.colors.text.danger}20;
+ padding-left: 0.5rem;
+ }
+
+ &.new {
+ border-left: 2px solid ${(props) => props.theme.colors.text.green}20;
+ padding-left: 0.5rem;
+ }
+ }
+
+ .empty-placeholder {
+ flex: 1;
+ min-height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: ${(props) => props.theme.sidebar.bg};
+ border: 1px dashed ${(props) => props.theme.border.border1};
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: ${(props) => props.theme.font.size.xs};
+ }
+
+ .empty-placeholder::after {
+ content: 'No content';
+ }
+
+ .diff-section {
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ overflow: hidden;
+
+ &.added {
+ border-color: ${(props) => props.theme.colors.text.green};
+ }
+
+ &.deleted {
+ border-color: ${(props) => props.theme.colors.text.danger};
+ }
+ }
+
+ .diff-section-header {
+ padding: 0.375rem 0.5rem;
+ font-size: ${(props) => props.theme.font.size.xs};
+ font-weight: 500;
+ background: ${(props) => props.theme.sidebar.bg};
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ .diff-section-content {
+ padding: 0.5rem;
+ }
+
+ .url-bar {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.375rem 0.5rem;
+
+ .method {
+ font-weight: 600;
+ font-size: ${(props) => props.theme.font.size.xs};
+ text-transform: uppercase;
+ padding: 0.125rem 0.375rem;
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ background: ${(props) => props.theme.brand}15;
+ color: ${(props) => props.theme.brand};
+ }
+
+ .url {
+ flex: 1;
+ font-family: 'Fira Code', monospace;
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.text};
+ word-break: break-all;
+
+ &.changed {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.warning} 20%, transparent);
+ padding: 0.125rem 0.25rem;
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ }
+ }
+
+ .method.changed {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.warning} 30%, transparent);
+ color: ${(props) => props.theme.colors.text.warning};
+ }
+ }
+
+ .diff-inline {
+ padding: 0.125rem 0.25rem;
+ border-radius: 2px;
+
+ &.added {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.green} 25%, transparent);
+ color: ${(props) => props.theme.colors.text.green};
+ }
+
+ &.deleted {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.danger} 25%, transparent);
+ color: ${(props) => props.theme.colors.text.danger};
+ text-decoration: line-through;
+ }
+
+ &.modified {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.warning} 25%, transparent);
+ color: ${(props) => props.theme.colors.text.warning};
+ }
+ }
+
+
+
+ .diff-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: ${(props) => props.theme.font.size.xs};
+
+ th, td {
+ padding: 0.375rem 0.5rem;
+ text-align: left;
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+ }
+
+ th {
+ font-weight: 500;
+ color: ${(props) => props.theme.colors.text.muted};
+ background: ${(props) => props.theme.sidebar.bg};
+ }
+
+ tr.added {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.green} 10%, transparent);
+ }
+
+ tr.deleted {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.danger} 10%, transparent);
+ }
+
+ tr.modified {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.warning} 10%, transparent);
+ }
+
+ .checkbox-cell {
+ width: 24px;
+ text-align: center;
+
+ input[type='checkbox'] {
+ cursor: default;
+ width: 12px;
+ height: 12px;
+ accent-color: ${(props) => props.theme.colors.accent};
+ vertical-align: middle;
+ margin: 0;
+ }
+ }
+
+ .key-cell {
+ font-family: 'Fira Code', monospace;
+ color: ${(props) => props.theme.text};
+ }
+
+ .value-cell {
+ font-family: 'Fira Code', monospace;
+ color: ${(props) => props.theme.colors.text.muted};
+ word-break: break-all;
+ }
+
+ .status-badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 0.875rem;
+ height: 0.875rem;
+ border-radius: 2px;
+ font-size: 8px;
+ font-weight: 600;
+
+ &.added {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.green} 13%, transparent);
+ color: ${(props) => props.theme.colors.text.green};
+ }
+
+ &.deleted {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.danger} 13%, transparent);
+ color: ${(props) => props.theme.colors.text.danger};
+ }
+
+ &.modified {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.warning} 13%, transparent);
+ color: ${(props) => props.theme.colors.text.warning};
+ }
+ }
+ }
+
+ .code-diff-content {
+ max-height: 250px;
+ overflow: auto;
+ font-family: 'Fira Code', monospace;
+ font-size: ${(props) => props.theme.font.size.xs};
+ line-height: 1.5;
+
+ .diff-line {
+ padding: 0 0.5rem;
+ white-space: pre-wrap;
+ word-break: break-word;
+
+ &.unchanged {
+ color: ${(props) => props.theme.text};
+ }
+
+ &.added {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.green} 15%, transparent);
+ color: ${(props) => props.theme.colors.text.green};
+ }
+
+ &.deleted {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.danger} 15%, transparent);
+ color: ${(props) => props.theme.colors.text.danger};
+ text-decoration: line-through;
+ }
+ }
+ }
+
+ .example-content {
+ padding: 0.5rem;
+ }
+
+ .example-block {
+ margin-bottom: 0.5rem;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ .example-block-header {
+ font-size: ${(props) => props.theme.font.size.xs};
+ font-weight: 600;
+ color: ${(props) => props.theme.colors.text.muted};
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ padding: 0.25rem 0.5rem;
+ background: ${(props) => props.theme.sidebar.bg};
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ margin-bottom: 0.375rem;
+ }
+
+ .example-subsection {
+ margin-bottom: 0.375rem;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ .example-subsection-title {
+ font-size: ${(props) => props.theme.font.size.xs};
+ font-weight: 500;
+ color: ${(props) => props.theme.colors.text.muted};
+ padding: 0.25rem 0.5rem;
+ margin-bottom: 0.25rem;
+ }
+
+ .example-description {
+ font-weight: 400;
+ color: ${(props) => props.theme.colors.text.muted};
+ font-style: italic;
+ }
+
+ .status-display {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.25rem 0.5rem;
+ font-family: 'Fira Code', monospace;
+ font-size: ${(props) => props.theme.font.size.xs};
+
+ .status-code {
+ font-weight: 600;
+ padding: 0.125rem 0.375rem;
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ background: ${(props) => props.theme.sidebar.bg};
+
+ &.changed {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.warning} 20%, transparent);
+ color: ${(props) => props.theme.colors.text.warning};
+ }
+ }
+
+ .status-text {
+ color: ${(props) => props.theme.colors.text.muted};
+
+ &.changed {
+ color: ${(props) => props.theme.colors.text.warning};
+ }
+ }
+ }
+
+ .example-subsection .diff-table {
+ margin: 0;
+ }
+
+ .example-subsection .code-diff-content {
+ max-height: 150px;
+ border: 1px solid ${(props) => props.theme.border.border1};
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ }
+
+ .empty-state {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 2rem;
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: ${(props) => props.theme.font.size.sm};
+ }
+
+ .tags-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.25rem;
+ }
+
+ .tag-badge {
+ display: inline-block;
+ padding: 0.125rem 0.375rem;
+ font-size: ${(props) => props.theme.font.size.xs};
+ font-family: 'Fira Code', monospace;
+ border-radius: ${(props) => props.theme.border.radius.sm};
+ background: ${(props) => props.theme.sidebar.bg};
+ border: 1px solid ${(props) => props.theme.border.border1};
+
+ &.added {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.green} 15%, transparent);
+ border-color: ${(props) => props.theme.colors.text.green};
+ color: ${(props) => props.theme.colors.text.green};
+ }
+
+ &.deleted {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.danger} 15%, transparent);
+ border-color: ${(props) => props.theme.colors.text.danger};
+ color: ${(props) => props.theme.colors.text.danger};
+ text-decoration: line-through;
+ }
+
+ &.modified {
+ background: color-mix(in srgb, ${(props) => props.theme.colors.text.warning} 15%, transparent);
+ border-color: ${(props) => props.theme.colors.text.warning};
+ color: ${(props) => props.theme.colors.text.warning};
+ }
+ }
+`;
+
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffContent/index.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffContent/index.js
new file mode 100644
index 000000000..089a59284
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffContent/index.js
@@ -0,0 +1,109 @@
+import React, { useState, useEffect } from 'react';
+import CollapsibleDiffRow from '../CollapsibleDiffRow';
+import StyledWrapper from './StyledWrapper';
+
+/**
+ * VisualDiffContent - Presentational component for rendering visual diffs
+ *
+ * This is a reusable component that renders the visual diff UI.
+ * It can be used by:
+ * - Git VisualDiffViewer (for git diffs)
+ * - OpenAPI ChangeSection (for spec diffs)
+ *
+ * Props:
+ * - oldData: The "before" data
+ * - newData: The "after" data
+ * - sections: Array of section configs { key, title, Component, hasContent }
+ * - sectionHasChanges: Function (sectionKey, oldData, newData) => boolean
+ * - oldLabel: Label for the left/old pane (default: "Before")
+ * - newLabel: Label for the right/new pane (default: "After")
+ * - hideUnchanged: Hide sections without changes entirely (default: false)
+ */
+const VisualDiffContent = ({
+ oldData,
+ newData,
+ sections,
+ sectionHasChanges,
+ oldLabel = 'Before',
+ newLabel = 'After',
+ hideUnchanged = false
+}) => {
+ const [collapsedSections, setCollapsedSections] = useState({});
+
+ const toggleSection = (sectionKey) => {
+ setCollapsedSections((prev) => ({
+ ...prev,
+ [sectionKey]: !prev[sectionKey]
+ }));
+ };
+
+ // Auto-collapse unchanged sections (collapsed but still visible)
+ useEffect(() => {
+ if (!sectionHasChanges || (!oldData && !newData)) return;
+
+ const initialCollapsed = {};
+ sections.forEach(({ key }) => {
+ const hasChanges = sectionHasChanges(key, oldData, newData);
+ initialCollapsed[key] = !hasChanges;
+ });
+
+ setCollapsedSections(initialCollapsed);
+ }, [oldData, newData, sections, sectionHasChanges]);
+
+ if (!oldData && !newData) {
+ return (
+
+
+ No content to display
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
{oldLabel}
+
{newLabel}
+
+
+
+ {sections.map(({ key, title, Component, hasContent: checkContent }) => {
+ const hasOld = oldData && checkContent(oldData);
+ const hasNew = newData && checkContent(newData);
+
+ if (!hasOld && !hasNew) {
+ return null;
+ }
+
+ // Hide sections without changes entirely when hideUnchanged is enabled
+ if (hideUnchanged && sectionHasChanges && !sectionHasChanges(key, oldData, newData)) {
+ return null;
+ }
+
+ return (
+ toggleSection(key)}
+ hasOldContent={hasOld}
+ hasNewContent={hasNew}
+ oldContent={
+
+ }
+ newContent={
+
+ }
+ />
+ );
+ })}
+
+
+
+ );
+};
+
+export default VisualDiffContent;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffHeaders.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffHeaders.js
new file mode 100644
index 000000000..47b41d4e9
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffHeaders.js
@@ -0,0 +1,74 @@
+import React, { useMemo } from 'react';
+import get from 'lodash/get';
+
+const VisualDiffHeaders = ({ oldData, newData, showSide }) => {
+ const oldHeaders = get(oldData, 'request.headers', []);
+ const newHeaders = get(newData, 'request.headers', []);
+
+ const currentHeaders = showSide === 'old' ? oldHeaders : newHeaders;
+ const otherHeaders = showSide === 'old' ? newHeaders : oldHeaders;
+
+ const headersWithStatus = useMemo(() => {
+ const otherHeaderMap = new Map();
+ otherHeaders.forEach((h) => {
+ otherHeaderMap.set(h.name, h);
+ });
+
+ return currentHeaders.map((header) => {
+ const otherHeader = otherHeaderMap.get(header.name);
+
+ let status = 'unchanged';
+ if (!otherHeader) {
+ status = showSide === 'old' ? 'deleted' : 'added';
+ } else if (header.value !== otherHeader.value || header.enabled !== otherHeader.enabled) {
+ status = 'modified';
+ }
+
+ return { ...header, status };
+ });
+ }, [currentHeaders, otherHeaders, showSide]);
+
+ if (headersWithStatus.length === 0) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export default VisualDiffHeaders;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffParams.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffParams.js
new file mode 100644
index 000000000..3a0078960
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffParams.js
@@ -0,0 +1,89 @@
+import React, { useMemo } from 'react';
+import get from 'lodash/get';
+
+const VisualDiffParams = ({ oldData, newData, showSide }) => {
+ const oldParams = get(oldData, 'request.params', []);
+ const newParams = get(newData, 'request.params', []);
+
+ const currentParams = showSide === 'old' ? oldParams : newParams;
+ const otherParams = showSide === 'old' ? newParams : oldParams;
+
+ const paramsWithStatus = useMemo(() => {
+ const otherParamMap = new Map();
+ otherParams.forEach((p) => {
+ otherParamMap.set(p.name, p);
+ });
+
+ return currentParams.map((param) => {
+ const otherParam = otherParamMap.get(param.name);
+
+ let status = 'unchanged';
+ if (!otherParam) {
+ status = showSide === 'old' ? 'deleted' : 'added';
+ } else if (param.value !== otherParam.value || param.enabled !== otherParam.enabled) {
+ status = 'modified';
+ }
+
+ return { ...param, status };
+ });
+ }, [currentParams, otherParams, showSide]);
+
+ const queryParams = paramsWithStatus.filter((p) => p.type === 'query');
+ const pathParams = paramsWithStatus.filter((p) => p.type === 'path');
+
+ if (queryParams.length === 0 && pathParams.length === 0) {
+ return null;
+ }
+
+ const renderTable = (params, title) => {
+ if (params.length === 0) return null;
+
+ return (
+
+ );
+ };
+
+ return (
+ <>
+ {renderTable(queryParams, 'Query Parameters')}
+ {renderTable(pathParams, 'Path Parameters')}
+ >
+ );
+};
+
+export default VisualDiffParams;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffUrlBar.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffUrlBar.js
new file mode 100644
index 000000000..102fd7637
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/VisualDiffUrlBar.js
@@ -0,0 +1,55 @@
+import React, { useMemo } from 'react';
+import { computeWordDiffForOld, computeWordDiffForNew } from './utils/diffUtils';
+import { getMethod, getUrl } from './utils/bruUtils';
+
+const VisualDiffUrlBar = ({ oldData, newData, showSide }) => {
+ const oldMethod = getMethod(oldData);
+ const newMethod = getMethod(newData);
+ const oldUrl = getUrl(oldData);
+ const newUrl = getUrl(newData);
+
+ const currentMethod = showSide === 'old' ? oldMethod : newMethod;
+
+ const urlDiffSegments = useMemo(() => {
+ if (showSide === 'old') {
+ return computeWordDiffForOld(oldUrl, newUrl);
+ } else {
+ return computeWordDiffForNew(oldUrl, newUrl);
+ }
+ }, [oldUrl, newUrl, showSide]);
+
+ const methodChanged = oldMethod !== newMethod;
+ const methodStatus = useMemo(() => {
+ if (!methodChanged) return 'unchanged';
+ if (showSide === 'old') return 'deleted';
+ return 'added';
+ }, [methodChanged, showSide]);
+
+ const renderDiffSegments = (segments) => {
+ return segments.map((segment, index) => {
+ if (segment.status === 'unchanged') {
+ return {segment.text};
+ }
+ return (
+
+ {segment.text}
+
+ );
+ });
+ };
+
+ return (
+
+
+
+ {currentMethod?.toUpperCase() || 'GET'}
+
+
+ {renderDiffSegments(urlDiffSegments)}
+
+
+
+ );
+};
+
+export default VisualDiffUrlBar;
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/bruUtils.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/bruUtils.js
new file mode 100644
index 000000000..f33bef817
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/bruUtils.js
@@ -0,0 +1,53 @@
+import get from 'lodash/get';
+
+export const DIFF_STATUS = Object.freeze({
+ ADDED: 'added',
+ DELETED: 'deleted',
+ MODIFIED: 'modified',
+ UNCHANGED: 'unchanged'
+});
+
+export const getBodyContent = (body) => {
+ if (!body) return '';
+ if (body.json) return body.json;
+ if (body.text) return body.text;
+ if (body.xml) return body.xml;
+ if (body.sparql) return body.sparql;
+ if (body.graphql?.query) return body.graphql.query;
+ if (body.content) return body.content;
+ return '';
+};
+
+export const getBodyMode = (body) => {
+ if (!body) return 'none';
+ if (body.json !== undefined) return 'json';
+ if (body.text !== undefined) return 'text';
+ if (body.xml !== undefined) return 'xml';
+ if (body.sparql !== undefined) return 'sparql';
+ if (body.graphql) return 'graphql';
+ if (body.formUrlEncoded) return 'formUrlEncoded';
+ if (body.multipartForm) return 'multipartForm';
+ if (body.file) return 'file';
+ if (body.grpc) return 'grpc';
+ if (body.ws) return 'ws';
+ if (body.mode === 'none') return 'none';
+ return 'none';
+};
+
+export const getMethod = (data) => {
+ return get(data, 'request.method', 'GET');
+};
+
+export const getUrl = (data) => {
+ return get(data, 'request.url', '');
+};
+
+export const computeItemDiffStatus = (currentItem, otherItem, showSide) => {
+ if (!otherItem) {
+ return showSide === 'old' ? DIFF_STATUS.DELETED : DIFF_STATUS.ADDED;
+ }
+ if (currentItem.value !== otherItem.value || currentItem.enabled !== otherItem.enabled) {
+ return DIFF_STATUS.MODIFIED;
+ }
+ return DIFF_STATUS.UNCHANGED;
+};
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/bruUtils.spec.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/bruUtils.spec.js
new file mode 100644
index 000000000..fc62d4307
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/bruUtils.spec.js
@@ -0,0 +1,194 @@
+const { describe, it, expect } = require('@jest/globals');
+
+import {
+ getBodyContent,
+ getBodyMode,
+ getMethod,
+ getUrl,
+ computeItemDiffStatus
+} from './bruUtils';
+
+describe('bruUtils', () => {
+ describe('getBodyContent', () => {
+ it('should return empty string for null or undefined body', () => {
+ expect(getBodyContent(null)).toBe('');
+ expect(getBodyContent(undefined)).toBe('');
+ });
+
+ it('should return empty string for empty body', () => {
+ expect(getBodyContent({})).toBe('');
+ });
+
+ it('should return json content', () => {
+ expect(getBodyContent({ json: '{"key": "value"}' })).toBe('{"key": "value"}');
+ });
+
+ it('should return text content', () => {
+ expect(getBodyContent({ text: 'plain text content' })).toBe('plain text content');
+ });
+
+ it('should return xml content', () => {
+ expect(getBodyContent({ xml: '- value
' })).toBe('- value
');
+ });
+
+ it('should return sparql content', () => {
+ expect(getBodyContent({ sparql: 'SELECT * WHERE { ?s ?p ?o }' })).toBe('SELECT * WHERE { ?s ?p ?o }');
+ });
+
+ it('should return graphql query content', () => {
+ expect(getBodyContent({ graphql: { query: 'query { users { id } }' } })).toBe('query { users { id } }');
+ });
+
+ it('should return generic content', () => {
+ expect(getBodyContent({ content: 'generic content' })).toBe('generic content');
+ });
+
+ it('should return empty string for graphql without query', () => {
+ expect(getBodyContent({ graphql: {} })).toBe('');
+ expect(getBodyContent({ graphql: { variables: '{}' } })).toBe('');
+ });
+
+ it('should prioritize json over other types', () => {
+ expect(getBodyContent({ json: '{"a":1}', text: 'text' })).toBe('{"a":1}');
+ });
+ });
+
+ describe('getBodyMode', () => {
+ it('should return none for null or undefined body', () => {
+ expect(getBodyMode(null)).toBe('none');
+ expect(getBodyMode(undefined)).toBe('none');
+ });
+
+ it('should return none for empty body', () => {
+ expect(getBodyMode({})).toBe('none');
+ });
+
+ it('should return json mode', () => {
+ expect(getBodyMode({ json: '{}' })).toBe('json');
+ expect(getBodyMode({ json: '' })).toBe('json');
+ });
+
+ it('should return text mode', () => {
+ expect(getBodyMode({ text: 'content' })).toBe('text');
+ expect(getBodyMode({ text: '' })).toBe('text');
+ });
+
+ it('should return xml mode', () => {
+ expect(getBodyMode({ xml: '' })).toBe('xml');
+ });
+
+ it('should return sparql mode', () => {
+ expect(getBodyMode({ sparql: 'SELECT *' })).toBe('sparql');
+ });
+
+ it('should return graphql mode', () => {
+ expect(getBodyMode({ graphql: { query: '' } })).toBe('graphql');
+ });
+
+ it('should return formUrlEncoded mode', () => {
+ expect(getBodyMode({ formUrlEncoded: [] })).toBe('formUrlEncoded');
+ expect(getBodyMode({ formUrlEncoded: [{ name: 'key', value: 'val' }] })).toBe('formUrlEncoded');
+ });
+
+ it('should return multipartForm mode', () => {
+ expect(getBodyMode({ multipartForm: [] })).toBe('multipartForm');
+ });
+
+ it('should return file mode', () => {
+ expect(getBodyMode({ file: [] })).toBe('file');
+ });
+
+ it('should return grpc mode', () => {
+ expect(getBodyMode({ grpc: [] })).toBe('grpc');
+ });
+
+ it('should return ws mode', () => {
+ expect(getBodyMode({ ws: [] })).toBe('ws');
+ });
+
+ it('should return none for explicit none mode', () => {
+ expect(getBodyMode({ mode: 'none' })).toBe('none');
+ });
+
+ it('should prioritize json over other modes', () => {
+ expect(getBodyMode({ json: '{}', text: 'text' })).toBe('json');
+ });
+ });
+
+ describe('getMethod', () => {
+ it('should return GET as default', () => {
+ expect(getMethod(null)).toBe('GET');
+ expect(getMethod(undefined)).toBe('GET');
+ expect(getMethod({})).toBe('GET');
+ });
+
+ it('should return request method', () => {
+ expect(getMethod({ request: { method: 'POST' } })).toBe('POST');
+ expect(getMethod({ request: { method: 'PUT' } })).toBe('PUT');
+ expect(getMethod({ request: { method: 'DELETE' } })).toBe('DELETE');
+ });
+
+ it('should return GET when request exists but method is missing', () => {
+ expect(getMethod({ request: {} })).toBe('GET');
+ });
+ });
+
+ describe('getUrl', () => {
+ it('should return empty string as default', () => {
+ expect(getUrl(null)).toBe('');
+ expect(getUrl(undefined)).toBe('');
+ expect(getUrl({})).toBe('');
+ });
+
+ it('should return request url', () => {
+ expect(getUrl({ request: { url: 'https://api.example.com/users' } })).toBe('https://api.example.com/users');
+ });
+
+ it('should return empty string when request exists but url is missing', () => {
+ expect(getUrl({ request: {} })).toBe('');
+ });
+
+ it('should return url with different protocols', () => {
+ expect(getUrl({ request: { url: 'http://localhost:3000' } })).toBe('http://localhost:3000');
+ expect(getUrl({ request: { url: 'ws://localhost:8080' } })).toBe('ws://localhost:8080');
+ expect(getUrl({ request: { url: 'grpc://localhost:50051' } })).toBe('grpc://localhost:50051');
+ });
+ });
+
+ describe('computeItemDiffStatus', () => {
+ it('should return deleted when other item is missing and showing old side', () => {
+ expect(computeItemDiffStatus({ name: 'key', value: 'val' }, null, 'old')).toBe('deleted');
+ expect(computeItemDiffStatus({ name: 'key', value: 'val' }, undefined, 'old')).toBe('deleted');
+ });
+
+ it('should return added when other item is missing and showing new side', () => {
+ expect(computeItemDiffStatus({ name: 'key', value: 'val' }, null, 'new')).toBe('added');
+ expect(computeItemDiffStatus({ name: 'key', value: 'val' }, undefined, 'new')).toBe('added');
+ });
+
+ it('should return unchanged when items are equal', () => {
+ const item = { name: 'key', value: 'val', enabled: true };
+ const otherItem = { name: 'key', value: 'val', enabled: true };
+ expect(computeItemDiffStatus(item, otherItem, 'old')).toBe('unchanged');
+ expect(computeItemDiffStatus(item, otherItem, 'new')).toBe('unchanged');
+ });
+
+ it('should return modified when values differ', () => {
+ const item = { name: 'key', value: 'val1', enabled: true };
+ const otherItem = { name: 'key', value: 'val2', enabled: true };
+ expect(computeItemDiffStatus(item, otherItem, 'old')).toBe('modified');
+ });
+
+ it('should return modified when enabled status differs', () => {
+ const item = { name: 'key', value: 'val', enabled: true };
+ const otherItem = { name: 'key', value: 'val', enabled: false };
+ expect(computeItemDiffStatus(item, otherItem, 'old')).toBe('modified');
+ });
+
+ it('should handle undefined enabled as different from explicit false', () => {
+ const item = { name: 'key', value: 'val' };
+ const otherItem = { name: 'key', value: 'val', enabled: false };
+ expect(computeItemDiffStatus(item, otherItem, 'old')).toBe('modified');
+ });
+ });
+});
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/diffUtils.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/diffUtils.js
new file mode 100644
index 000000000..5f77b13bb
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/diffUtils.js
@@ -0,0 +1,202 @@
+// Matches word-boundary separators: whitespace, slashes, query/path delimiters (?&=), dots, hyphens, underscores, colons, @
+const WORD_SEPARATOR = /[\s\/\?\&\=\.\-\_\:\@]/;
+
+const splitWithSeparators = (str) => {
+ const result = [];
+ let current = '';
+ for (const char of str) {
+ if (WORD_SEPARATOR.test(char)) {
+ if (current) {
+ result.push(current);
+ current = '';
+ }
+ result.push(char);
+ } else {
+ current += char;
+ }
+ }
+ if (current) {
+ result.push(current);
+ }
+ return result;
+};
+
+export const computeWordDiffForOld = (oldStr, newStr) => {
+ if (oldStr === newStr) {
+ return [{ text: oldStr, status: 'unchanged' }];
+ }
+
+ if (!oldStr) {
+ return [];
+ }
+
+ if (!newStr) {
+ return [{ text: oldStr, status: 'deleted' }];
+ }
+
+ const oldWords = splitWithSeparators(oldStr);
+ const newWords = splitWithSeparators(newStr);
+ const lcs = computeLCS(oldWords, newWords);
+
+ const segments = [];
+ let oldIdx = 0;
+ let lcsIdx = 0;
+
+ while (oldIdx < oldWords.length) {
+ if (lcsIdx < lcs.length && oldIdx === lcs[lcsIdx].oldIndex) {
+ segments.push({ text: oldWords[oldIdx], status: 'unchanged' });
+ lcsIdx++;
+ } else {
+ segments.push({ text: oldWords[oldIdx], status: 'deleted' });
+ }
+ oldIdx++;
+ }
+
+ return mergeSegments(segments);
+};
+
+export const computeWordDiffForNew = (oldStr, newStr) => {
+ if (oldStr === newStr) {
+ return [{ text: newStr, status: 'unchanged' }];
+ }
+
+ if (!newStr) {
+ return [];
+ }
+
+ if (!oldStr) {
+ return [{ text: newStr, status: 'added' }];
+ }
+
+ const oldWords = splitWithSeparators(oldStr);
+ const newWords = splitWithSeparators(newStr);
+ const lcs = computeLCS(oldWords, newWords);
+
+ const segments = [];
+ let newIdx = 0;
+ let lcsIdx = 0;
+
+ while (newIdx < newWords.length) {
+ if (lcsIdx < lcs.length && newIdx === lcs[lcsIdx].newIndex) {
+ segments.push({ text: newWords[newIdx], status: 'unchanged' });
+ lcsIdx++;
+ } else {
+ segments.push({ text: newWords[newIdx], status: 'added' });
+ }
+ newIdx++;
+ }
+
+ return mergeSegments(segments);
+};
+
+const mergeSegments = (segments) => {
+ const merged = [];
+ for (const segment of segments) {
+ if (merged.length > 0 && merged[merged.length - 1].status === segment.status) {
+ merged[merged.length - 1].text += segment.text;
+ } else {
+ merged.push({ ...segment });
+ }
+ }
+ return merged;
+};
+
+const computeLCS = (arr1, arr2) => {
+ const m = arr1.length;
+ const n = arr2.length;
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
+
+ for (let i = 1; i <= m; i++) {
+ for (let j = 1; j <= n; j++) {
+ if (arr1[i - 1] === arr2[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+
+ const lcs = [];
+ let i = m, j = n;
+ while (i > 0 && j > 0) {
+ if (arr1[i - 1] === arr2[j - 1]) {
+ lcs.unshift({ value: arr1[i - 1], oldIndex: i - 1, newIndex: j - 1 });
+ i--;
+ j--;
+ } else if (dp[i - 1][j] > dp[i][j - 1]) {
+ i--;
+ } else {
+ j--;
+ }
+ }
+
+ return lcs;
+};
+
+export const computeLineDiffForOld = (oldStr, newStr) => {
+ if (oldStr === newStr) {
+ return (oldStr || '').split('\n').map((line) => ({ text: line, status: 'unchanged' }));
+ }
+
+ if (!oldStr) {
+ return [];
+ }
+
+ if (!newStr) {
+ return oldStr.split('\n').map((line) => ({ text: line, status: 'deleted' }));
+ }
+
+ const oldLines = oldStr.split('\n');
+ const newLines = newStr.split('\n');
+ const lcs = computeLCS(oldLines, newLines);
+
+ const segments = [];
+ let oldIdx = 0;
+ let lcsIdx = 0;
+
+ while (oldIdx < oldLines.length) {
+ if (lcsIdx < lcs.length && oldIdx === lcs[lcsIdx].oldIndex) {
+ segments.push({ text: oldLines[oldIdx], status: 'unchanged' });
+ lcsIdx++;
+ } else {
+ segments.push({ text: oldLines[oldIdx], status: 'deleted' });
+ }
+ oldIdx++;
+ }
+
+ return segments;
+};
+
+export const computeLineDiffForNew = (oldStr, newStr) => {
+ if (oldStr === newStr) {
+ return (newStr || '').split('\n').map((line) => ({ text: line, status: 'unchanged' }));
+ }
+
+ if (!newStr) {
+ return [];
+ }
+
+ if (!oldStr) {
+ return newStr.split('\n').map((line) => ({ text: line, status: 'added' }));
+ }
+
+ const oldLines = oldStr.split('\n');
+ const newLines = newStr.split('\n');
+ const lcs = computeLCS(oldLines, newLines);
+
+ const segments = [];
+ let newIdx = 0;
+ let lcsIdx = 0;
+
+ while (newIdx < newLines.length) {
+ if (lcsIdx < lcs.length && newIdx === lcs[lcsIdx].newIndex) {
+ segments.push({ text: newLines[newIdx], status: 'unchanged' });
+ lcsIdx++;
+ } else {
+ segments.push({ text: newLines[newIdx], status: 'added' });
+ }
+ newIdx++;
+ }
+
+ return segments;
+};
diff --git a/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/diffUtils.spec.js b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/diffUtils.spec.js
new file mode 100644
index 000000000..1891a8f8a
--- /dev/null
+++ b/packages/bruno-app/src/components/Git/VisualDiffViewer/utils/diffUtils.spec.js
@@ -0,0 +1,198 @@
+const { describe, it, expect } = require('@jest/globals');
+
+import {
+ computeWordDiffForOld,
+ computeWordDiffForNew,
+ computeLineDiffForOld,
+ computeLineDiffForNew
+} from './diffUtils';
+
+describe('diffUtils', () => {
+ describe('computeWordDiffForOld', () => {
+ it('should return unchanged for identical strings', () => {
+ expect(computeWordDiffForOld('hello world', 'hello world')).toEqual([
+ { text: 'hello world', status: 'unchanged' }
+ ]);
+ });
+
+ it('should return empty array for empty old string', () => {
+ expect(computeWordDiffForOld('', 'new text')).toEqual([]);
+ expect(computeWordDiffForOld(null, 'new text')).toEqual([]);
+ expect(computeWordDiffForOld(undefined, 'new text')).toEqual([]);
+ });
+
+ it('should return deleted for entire old string when new is empty', () => {
+ expect(computeWordDiffForOld('old text', '')).toEqual([
+ { text: 'old text', status: 'deleted' }
+ ]);
+ expect(computeWordDiffForOld('old text', null)).toEqual([
+ { text: 'old text', status: 'deleted' }
+ ]);
+ });
+
+ it('should detect deleted words', () => {
+ const result = computeWordDiffForOld('hello world', 'hello');
+ expect(result).toContainEqual({ text: 'hello', status: 'unchanged' });
+ expect(result.some((s) => s.status === 'deleted' && s.text.includes('world'))).toBe(true);
+ });
+
+ it('should handle URL paths', () => {
+ const result = computeWordDiffForOld(
+ 'https://api.example.com/users/123',
+ 'https://api.example.com/users/456'
+ );
+ expect(result.some((s) => s.status === 'unchanged')).toBe(true);
+ expect(result.some((s) => s.status === 'deleted')).toBe(true);
+ });
+
+ it('should preserve separators', () => {
+ const result = computeWordDiffForOld('a/b/c', 'a/b/c');
+ expect(result).toEqual([{ text: 'a/b/c', status: 'unchanged' }]);
+ });
+ });
+
+ describe('computeWordDiffForNew', () => {
+ it('should return unchanged for identical strings', () => {
+ expect(computeWordDiffForNew('hello world', 'hello world')).toEqual([
+ { text: 'hello world', status: 'unchanged' }
+ ]);
+ });
+
+ it('should return empty array for empty new string', () => {
+ expect(computeWordDiffForNew('old text', '')).toEqual([]);
+ expect(computeWordDiffForNew('old text', null)).toEqual([]);
+ expect(computeWordDiffForNew('old text', undefined)).toEqual([]);
+ });
+
+ it('should return added for entire new string when old is empty', () => {
+ expect(computeWordDiffForNew('', 'new text')).toEqual([
+ { text: 'new text', status: 'added' }
+ ]);
+ expect(computeWordDiffForNew(null, 'new text')).toEqual([
+ { text: 'new text', status: 'added' }
+ ]);
+ });
+
+ it('should detect added words', () => {
+ const result = computeWordDiffForNew('hello', 'hello world');
+ expect(result).toContainEqual({ text: 'hello', status: 'unchanged' });
+ expect(result.some((s) => s.status === 'added' && s.text.includes('world'))).toBe(true);
+ });
+
+ it('should handle URL paths', () => {
+ const result = computeWordDiffForNew(
+ 'https://api.example.com/users/123',
+ 'https://api.example.com/users/456'
+ );
+ expect(result.some((s) => s.status === 'unchanged')).toBe(true);
+ expect(result.some((s) => s.status === 'added')).toBe(true);
+ });
+ });
+
+ describe('computeLineDiffForOld', () => {
+ it('should return unchanged for identical multiline strings', () => {
+ const text = 'line1\nline2\nline3';
+ expect(computeLineDiffForOld(text, text)).toEqual([
+ { text: 'line1', status: 'unchanged' },
+ { text: 'line2', status: 'unchanged' },
+ { text: 'line3', status: 'unchanged' }
+ ]);
+ });
+
+ it('should return empty array for empty old string', () => {
+ expect(computeLineDiffForOld('', 'new\ntext')).toEqual([]);
+ expect(computeLineDiffForOld(null, 'new\ntext')).toEqual([]);
+ });
+
+ it('should return deleted for all lines when new is empty', () => {
+ expect(computeLineDiffForOld('line1\nline2', '')).toEqual([
+ { text: 'line1', status: 'deleted' },
+ { text: 'line2', status: 'deleted' }
+ ]);
+ });
+
+ it('should detect deleted lines', () => {
+ const result = computeLineDiffForOld('line1\nline2\nline3', 'line1\nline3');
+ expect(result).toContainEqual({ text: 'line1', status: 'unchanged' });
+ expect(result).toContainEqual({ text: 'line2', status: 'deleted' });
+ expect(result).toContainEqual({ text: 'line3', status: 'unchanged' });
+ });
+
+ it('should handle single line strings', () => {
+ expect(computeLineDiffForOld('single line', 'single line')).toEqual([
+ { text: 'single line', status: 'unchanged' }
+ ]);
+ });
+
+ it('should handle code blocks', () => {
+ const oldCode = 'function foo() {\n return 1;\n}';
+ const newCode = 'function foo() {\n return 2;\n}';
+ const result = computeLineDiffForOld(oldCode, newCode);
+ expect(result).toContainEqual({ text: 'function foo() {', status: 'unchanged' });
+ expect(result).toContainEqual({ text: ' return 1;', status: 'deleted' });
+ expect(result).toContainEqual({ text: '}', status: 'unchanged' });
+ });
+ });
+
+ describe('computeLineDiffForNew', () => {
+ it('should return unchanged for identical multiline strings', () => {
+ const text = 'line1\nline2\nline3';
+ expect(computeLineDiffForNew(text, text)).toEqual([
+ { text: 'line1', status: 'unchanged' },
+ { text: 'line2', status: 'unchanged' },
+ { text: 'line3', status: 'unchanged' }
+ ]);
+ });
+
+ it('should return empty array for empty new string', () => {
+ expect(computeLineDiffForNew('old\ntext', '')).toEqual([]);
+ expect(computeLineDiffForNew('old\ntext', null)).toEqual([]);
+ });
+
+ it('should return added for all lines when old is empty', () => {
+ expect(computeLineDiffForNew('', 'line1\nline2')).toEqual([
+ { text: 'line1', status: 'added' },
+ { text: 'line2', status: 'added' }
+ ]);
+ });
+
+ it('should detect added lines', () => {
+ const result = computeLineDiffForNew('line1\nline3', 'line1\nline2\nline3');
+ expect(result).toContainEqual({ text: 'line1', status: 'unchanged' });
+ expect(result).toContainEqual({ text: 'line2', status: 'added' });
+ expect(result).toContainEqual({ text: 'line3', status: 'unchanged' });
+ });
+
+ it('should handle code blocks', () => {
+ const oldCode = 'function foo() {\n return 1;\n}';
+ const newCode = 'function foo() {\n return 2;\n}';
+ const result = computeLineDiffForNew(oldCode, newCode);
+ expect(result).toContainEqual({ text: 'function foo() {', status: 'unchanged' });
+ expect(result).toContainEqual({ text: ' return 2;', status: 'added' });
+ expect(result).toContainEqual({ text: '}', status: 'unchanged' });
+ });
+ });
+
+ describe('edge cases', () => {
+ it('should handle empty strings', () => {
+ expect(computeWordDiffForOld('', '')).toEqual([{ text: '', status: 'unchanged' }]);
+ expect(computeWordDiffForNew('', '')).toEqual([{ text: '', status: 'unchanged' }]);
+ });
+
+ it('should handle strings with only whitespace', () => {
+ const result = computeWordDiffForOld(' ', ' ');
+ expect(result).toEqual([{ text: ' ', status: 'unchanged' }]);
+ });
+
+ it('should handle special characters in URLs', () => {
+ const url = 'https://api.example.com/users?id=123&name=test';
+ expect(computeWordDiffForOld(url, url)).toEqual([{ text: url, status: 'unchanged' }]);
+ });
+
+ it('should handle JSON-like content', () => {
+ const json = '{"key": "value", "number": 123}';
+ const result = computeLineDiffForOld(json, json);
+ expect(result).toEqual([{ text: json, status: 'unchanged' }]);
+ });
+ });
+});
diff --git a/packages/bruno-app/src/components/Help/StyledWrapper.js b/packages/bruno-app/src/components/Help/StyledWrapper.js
index 710f2c80b..5ce61cb63 100644
--- a/packages/bruno-app/src/components/Help/StyledWrapper.js
+++ b/packages/bruno-app/src/components/Help/StyledWrapper.js
@@ -3,6 +3,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
font-weight: 400;
font-size: ${(props) => props.theme.font.size.sm};
+ color: ${(props) => props.theme.text};
+ white-space: normal;
background-color: ${(props) => props.theme.infoTip.bg};
border: 1px solid ${(props) => props.theme.infoTip.border};
box-shadow: ${(props) => props.theme.infoTip.boxShadow};
diff --git a/packages/bruno-app/src/components/Help/index.js b/packages/bruno-app/src/components/Help/index.js
index f8ff625a5..a22f8540d 100644
--- a/packages/bruno-app/src/components/Help/index.js
+++ b/packages/bruno-app/src/components/Help/index.js
@@ -4,62 +4,84 @@
* We should allow icon and placement props to be passed in
*/
-import React, { useState } from 'react';
-import HelpIcon from 'components/Icons/Help';
+import React, { useState, useRef, useCallback } from 'react';
+import { createPortal } from 'react-dom';
+import QuestionCircle from 'components/Icons/QuestionCircle';
+import InfoCircle from 'components/Icons/InfoCircle';
import StyledWrapper from './StyledWrapper';
-const getPlacementStyles = (placement) => {
+const GAP = 8;
+
+const getPortalPosition = (rect, placement, width) => {
switch (placement) {
case 'top':
return {
- bottom: 'calc(100% + 8px)',
- left: '50%',
- transform: 'translateX(-50%)'
+ top: rect.top - GAP,
+ left: rect.left + rect.width / 2 - width / 2,
+ transform: 'translateY(-100%)'
};
case 'bottom':
return {
- top: 'calc(100% + 8px)',
- left: '50%',
- transform: 'translateX(-50%)'
+ top: rect.bottom + GAP,
+ left: rect.left + rect.width / 2 - width / 2
};
case 'left':
return {
- top: '50%',
- right: 'calc(100% + 8px)',
+ top: rect.top + rect.height / 2,
+ left: rect.left - GAP - width,
transform: 'translateY(-50%)'
};
case 'right':
default:
return {
- top: '50%',
- left: 'calc(100% + 8px)',
+ top: rect.top + rect.height / 2,
+ left: rect.right + GAP,
transform: 'translateY(-50%)'
};
}
};
-const Help = ({ children, width = 200, placement = 'right' }) => {
+const iconMap = {
+ question: QuestionCircle,
+ info: InfoCircle
+};
+
+const Help = ({ children, width = 200, placement = 'right', icon = 'question', iconComponent: IconComponent, size = 14 }) => {
const [showTooltip, setShowTooltip] = useState(false);
+ const [position, setPosition] = useState(null);
+ const iconRef = useRef(null);
+ const ResolvedIcon = IconComponent || iconMap[icon] || QuestionCircle;
+
+ const handleMouseEnter = useCallback(() => {
+ if (iconRef.current) {
+ const rect = iconRef.current.getBoundingClientRect();
+ setPosition(getPortalPosition(rect, placement, width));
+ }
+ setShowTooltip(true);
+ }, [placement, width]);
return (
-
+
setShowTooltip(true)}
+ onMouseEnter={handleMouseEnter}
onMouseLeave={() => setShowTooltip(false)}
>
-
+
- {showTooltip && (
+ {showTooltip && position && createPortal(
{children}
-
+ ,
+ document.body
)}
);
diff --git a/packages/bruno-app/src/components/Icons/InfoCircle/index.js b/packages/bruno-app/src/components/Icons/InfoCircle/index.js
new file mode 100644
index 000000000..140b41cb5
--- /dev/null
+++ b/packages/bruno-app/src/components/Icons/InfoCircle/index.js
@@ -0,0 +1,20 @@
+import React from 'react';
+
+const InfoCircle = ({ size = 14 }) => {
+ return (
+
+ );
+};
+
+export default InfoCircle;
diff --git a/packages/bruno-app/src/components/Icons/OpenAPISync/index.js b/packages/bruno-app/src/components/Icons/OpenAPISync/index.js
new file mode 100644
index 000000000..b27d38a3a
--- /dev/null
+++ b/packages/bruno-app/src/components/Icons/OpenAPISync/index.js
@@ -0,0 +1,22 @@
+import React from 'react';
+
+const OpenAPISyncIcon = ({ size = 16, color = 'currentColor', ...props }) => {
+ return (
+
+ );
+};
+
+export default OpenAPISyncIcon;
diff --git a/packages/bruno-app/src/components/Icons/Help/index.js b/packages/bruno-app/src/components/Icons/QuestionCircle/index.js
similarity index 92%
rename from packages/bruno-app/src/components/Icons/Help/index.js
rename to packages/bruno-app/src/components/Icons/QuestionCircle/index.js
index b96e02ba8..32844b538 100644
--- a/packages/bruno-app/src/components/Icons/Help/index.js
+++ b/packages/bruno-app/src/components/Icons/QuestionCircle/index.js
@@ -1,6 +1,6 @@
import React from 'react';
-const HelpIcon = ({ size = 14 }) => {
+const QuestionCircle = ({ size = 14 }) => {
return (