@stoplight/elements-core 7.6.5 → 7.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  import { __rest, __awaiter } from 'tslib';
2
2
  import * as React from 'react';
3
3
  import React__default, { useContext, useMemo } from 'react';
4
- import { resolveInlineRef, isPlainObject as isPlainObject$1, safeParse, safeStringify } from '@stoplight/json';
4
+ import { resolveInlineRef, hasRef, isPlainObject as isPlainObject$1, safeParse, safeStringify } from '@stoplight/json';
5
5
  import isArray from 'lodash/isArray.js';
6
- import isObject from 'lodash/isObject.js';
7
6
  import isPlainObject from 'lodash/isPlainObject.js';
8
7
  import { NodeType, HttpParamStyles } from '@stoplight/types';
9
8
  import { parse } from '@stoplight/yaml';
10
- import { isArray as isArray$1, Box, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading, Tabs, TabList, Tab, TabPanels, TabPanel, Heading, useClipboard, useBreakpoints, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
9
+ import { isArray as isArray$1, Box, Panel, CopyButton, Menu, Button, Text, Flex, Input, Icon, Select, FieldButton, Image, Link, useThemeIsDark, HStack, VStack, InvertTheme, Tooltip, Badge, LinkHeading as LinkHeading$1, NodeAnnotation, Tabs, TabList, Tab, TabPanels, TabPanel, Heading, useClipboard, useBreakpoints, useMosaicContext, Provider as Provider$1 } from '@stoplight/mosaic';
10
+ import isObject from 'lodash/isObject.js';
11
11
  import { withErrorBoundary } from '@stoplight/react-error-boundary';
12
12
  import { MarkdownViewer as MarkdownViewer$1, DefaultSMDComponents, MarkdownViewerProvider } from '@stoplight/markdown-viewer';
13
13
  export { DefaultSMDComponents } from '@stoplight/markdown-viewer';
@@ -37,9 +37,9 @@ import uniqBy from 'lodash/uniqBy.js';
37
37
  import formatXml from 'xml-formatter';
38
38
  import entries from 'lodash/entries.js';
39
39
  import keys from 'lodash/keys.js';
40
+ import { useLocation, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
40
41
  import { JsonSchemaViewer } from '@stoplight/json-schema-viewer';
41
42
  import sortBy from 'lodash/sortBy.js';
42
- import { useLocation, BrowserRouter, MemoryRouter, HashRouter, StaticRouter, Route } from 'react-router-dom';
43
43
  import { HashLink } from 'react-router-hash-link';
44
44
  import { QueryClient, useQueryClient, QueryClientProvider } from 'react-query';
45
45
  import $RefParser from '@stoplight/json-schema-ref-parser';
@@ -105,7 +105,7 @@ const isResolvedObjectProxy = (someObject) => {
105
105
  const getOriginalObject = (resolvedObject) => {
106
106
  return resolvedObject[originalObjectSymbol] || resolvedObject;
107
107
  };
108
- const isReference = (value) => isObject(value) && typeof value['$ref'] === 'string';
108
+ const isReference = hasRef;
109
109
 
110
110
  const InlineRefResolverContext = React.createContext(undefined);
111
111
  InlineRefResolverContext.displayName = 'InlineRefResolverContext';
@@ -125,6 +125,15 @@ const useResolvedObject = (currentObject) => {
125
125
  return React.useMemo(() => createResolvedObject(currentObject, { contextObject: document, resolver }), [currentObject, document, resolver]);
126
126
  };
127
127
 
128
+ const DEFAULT_CONTEXT = {};
129
+ const ElementsOptionsContext = React.createContext(DEFAULT_CONTEXT);
130
+ const useOptionsCtx = () => {
131
+ return React.useContext(ElementsOptionsContext) || DEFAULT_CONTEXT;
132
+ };
133
+ function ElementsOptionsProvider({ children, nodeHasChanged }) {
134
+ return (React.createElement(ElementsOptionsContext.Provider, { value: Object.assign({}, DEFAULT_CONTEXT, { nodeHasChanged }) }, children));
135
+ }
136
+
128
137
  function isSMDASTRoot(maybeAst) {
129
138
  return isObject(maybeAst) && maybeAst['type'] === 'root' && isArray$1(maybeAst['children']);
130
139
  }
@@ -158,6 +167,7 @@ const parserMap = {
158
167
  [NodeType.TableOfContents]: parseUnknown,
159
168
  [NodeType.SpectralRuleset]: parseUnknown,
160
169
  [NodeType.Styleguide]: parseUnknown,
170
+ [NodeType.Image]: parseUnknown,
161
171
  [NodeType.Unknown]: parseUnknown,
162
172
  };
163
173
  function parseArticleData(rawData) {
@@ -271,6 +281,11 @@ var faEye = {
271
281
  iconName: 'eye',
272
282
  icon: [576, 512, [128065], "f06e", "M279.6 160.4C282.4 160.1 285.2 160 288 160C341 160 384 202.1 384 256C384 309 341 352 288 352C234.1 352 192 309 192 256C192 253.2 192.1 250.4 192.4 247.6C201.7 252.1 212.5 256 224 256C259.3 256 288 227.3 288 192C288 180.5 284.1 169.7 279.6 160.4zM480.6 112.6C527.4 156 558.7 207.1 573.5 243.7C576.8 251.6 576.8 260.4 573.5 268.3C558.7 304 527.4 355.1 480.6 399.4C433.5 443.2 368.8 480 288 480C207.2 480 142.5 443.2 95.42 399.4C48.62 355.1 17.34 304 2.461 268.3C-.8205 260.4-.8205 251.6 2.461 243.7C17.34 207.1 48.62 156 95.42 112.6C142.5 68.84 207.2 32 288 32C368.8 32 433.5 68.84 480.6 112.6V112.6zM288 112C208.5 112 144 176.5 144 256C144 335.5 208.5 400 288 400C367.5 400 432 335.5 432 256C432 176.5 367.5 112 288 112z"]
273
283
  };
284
+ var faImage = {
285
+ prefix: 'fas',
286
+ iconName: 'image',
287
+ icon: [512, 512, [], "f03e", "M447.1 32h-384C28.64 32-.0091 60.65-.0091 96v320c0 35.35 28.65 64 63.1 64h384c35.35 0 64-28.65 64-64V96C511.1 60.65 483.3 32 447.1 32zM111.1 96c26.51 0 48 21.49 48 48S138.5 192 111.1 192s-48-21.49-48-48S85.48 96 111.1 96zM446.1 407.6C443.3 412.8 437.9 416 432 416H82.01c-6.021 0-11.53-3.379-14.26-8.75c-2.73-5.367-2.215-11.81 1.334-16.68l70-96C142.1 290.4 146.9 288 152 288s9.916 2.441 12.93 6.574l32.46 44.51l93.3-139.1C293.7 194.7 298.7 192 304 192s10.35 2.672 13.31 7.125l128 192C448.6 396 448.9 402.3 446.1 407.6z"]
288
+ };
274
289
  var faServer = {
275
290
  prefix: 'fas',
276
291
  iconName: 'server',
@@ -288,6 +303,7 @@ const NodeTypeColors = {
288
303
  table_of_contents: '',
289
304
  spectral_ruleset: '',
290
305
  styleguide: '',
306
+ image: '',
291
307
  };
292
308
  const NodeTypePrettyName = {
293
309
  http_operation: 'Endpoint',
@@ -300,6 +316,7 @@ const NodeTypePrettyName = {
300
316
  table_of_contents: '',
301
317
  spectral_ruleset: '',
302
318
  styleguide: '',
319
+ image: '',
303
320
  };
304
321
  const NodeTypeIconDefs = {
305
322
  http_operation: faCrosshairs,
@@ -312,6 +329,7 @@ const NodeTypeIconDefs = {
312
329
  table_of_contents: faQuestionCircle,
313
330
  spectral_ruleset: faQuestionCircle,
314
331
  styleguide: faQuestionCircle,
332
+ image: faImage,
315
333
  };
316
334
  const HttpMethodColors = {
317
335
  get: 'success',
@@ -439,6 +457,7 @@ const getServersToDisplay = (originalServers, mockUrl) => {
439
457
  .filter(isValidServer);
440
458
  if (mockUrl) {
441
459
  servers.push({
460
+ id: 'mock',
442
461
  description: 'Mock Server',
443
462
  url: mockUrl,
444
463
  });
@@ -628,15 +647,12 @@ const requestSampleConfigs = {
628
647
  mosaicCodeViewerLanguage: 'php',
629
648
  httpSnippetLanguage: 'php',
630
649
  libraries: {
631
- 'pecl/http 1': {
632
- httpSnippetLibrary: 'http1',
633
- },
634
- 'pecl/http 2': {
635
- httpSnippetLibrary: 'http2',
636
- },
637
650
  cURL: {
638
651
  httpSnippetLibrary: 'curl',
639
652
  },
653
+ guzzle: {
654
+ httpSnippetLibrary: 'guzzle',
655
+ },
640
656
  },
641
657
  },
642
658
  Powershell: {
@@ -999,9 +1015,12 @@ function parameterSupportsFileUpload(parameter) {
999
1015
  (((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.contentEncoding) === 'base64' ||
1000
1016
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1001
1017
  }
1018
+ function stringifyValue(value) {
1019
+ return typeof value === 'object' ? JSON.stringify(value) : escapeQuotes(String(value));
1020
+ }
1002
1021
  function exampleValue(example) {
1003
1022
  const value = 'value' in example ? example.value : example.externalValue;
1004
- return escapeQuotes(String(value));
1023
+ return stringifyValue(value);
1005
1024
  }
1006
1025
  function escapeQuotes(value) {
1007
1026
  return value.replace(/"/g, '\\"');
@@ -1022,7 +1041,7 @@ const getValueForParameter = (parameter) => {
1022
1041
  var _a, _b, _c;
1023
1042
  const defaultValue = retrieveDefaultFromSchema(parameter);
1024
1043
  if (typeof defaultValue !== 'undefined') {
1025
- return { value: String(defaultValue), isDefault: true };
1044
+ return { value: stringifyValue(defaultValue), isDefault: true };
1026
1045
  }
1027
1046
  const examples = (_a = parameter.examples) !== null && _a !== void 0 ? _a : [];
1028
1047
  if (examples.length > 0) {
@@ -1030,7 +1049,7 @@ const getValueForParameter = (parameter) => {
1030
1049
  }
1031
1050
  const enums = (_c = (_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.enum) !== null && _c !== void 0 ? _c : [];
1032
1051
  if (enums.length > 0) {
1033
- return { value: String(enums[0]) };
1052
+ return { value: stringifyValue(enums[0]) };
1034
1053
  }
1035
1054
  return { value: '' };
1036
1055
  };
@@ -1315,13 +1334,77 @@ const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, }) =>
1315
1334
  }
1316
1335
  return serverUrl;
1317
1336
  };
1337
+ const getQueryParams = ({ httpOperation, parameterValues, }) => {
1338
+ var _a;
1339
+ const query = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.query;
1340
+ if (!query)
1341
+ return [];
1342
+ return query.reduce((acc, param) => {
1343
+ var _a, _b, _c, _d, _e;
1344
+ const value = (_a = parameterValues[param.name]) !== null && _a !== void 0 ? _a : '';
1345
+ if (value.length === 0)
1346
+ return acc;
1347
+ const explode = (_b = param.explode) !== null && _b !== void 0 ? _b : true;
1348
+ if (((_c = param.schema) === null || _c === void 0 ? void 0 : _c.type) === 'object' && param.style === 'form' && value) {
1349
+ let nested;
1350
+ try {
1351
+ nested = JSON.parse(value);
1352
+ if (!(typeof nested === 'object' && nested !== null))
1353
+ throw Error();
1354
+ }
1355
+ catch (e) {
1356
+ throw new Error(`Cannot use param value "${value}". JSON object expected.`);
1357
+ }
1358
+ if (explode) {
1359
+ acc.push(...Object.entries(nested).map(([name, value]) => ({ name, value: value.toString() })));
1360
+ }
1361
+ else {
1362
+ acc.push({
1363
+ name: param.name,
1364
+ value: Object.entries(nested)
1365
+ .map(entry => entry.join(','))
1366
+ .join(','),
1367
+ });
1368
+ }
1369
+ }
1370
+ else if (((_d = param.schema) === null || _d === void 0 ? void 0 : _d.type) === 'array' && value) {
1371
+ let nested;
1372
+ try {
1373
+ nested = JSON.parse(value);
1374
+ if (!Array.isArray(nested))
1375
+ throw Error();
1376
+ }
1377
+ catch (e) {
1378
+ throw new Error(`Cannot use param value "${value}". JSON array expected.`);
1379
+ }
1380
+ if (explode) {
1381
+ acc.push(...nested.map(value => ({ name: param.name, value: value.toString() })));
1382
+ }
1383
+ else {
1384
+ const delimiter = {
1385
+ [HttpParamStyles.Form]: ',',
1386
+ [HttpParamStyles.SpaceDelimited]: ' ',
1387
+ [HttpParamStyles.PipeDelimited]: '|',
1388
+ };
1389
+ acc.push({
1390
+ name: param.name,
1391
+ value: nested.join((_e = delimiter[param.style]) !== null && _e !== void 0 ? _e : delimiter[HttpParamStyles.Form]),
1392
+ });
1393
+ }
1394
+ }
1395
+ else {
1396
+ acc.push({ name: param.name, value });
1397
+ }
1398
+ return acc;
1399
+ }, []);
1400
+ };
1318
1401
  function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
1319
- var _a, _b, _c, _d, _e, _f;
1402
+ var _a, _b, _c;
1320
1403
  return __awaiter(this, void 0, void 0, function* () {
1321
1404
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1322
1405
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1323
- const queryParams = (_c = (_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.query) === null || _b === void 0 ? void 0 : _b.map(param => { var _a; return ({ name: param.name, value: (_a = parameterValues[param.name]) !== null && _a !== void 0 ? _a : '' }); }).filter(({ value }) => value.length > 0)) !== null && _c !== void 0 ? _c : [];
1324
- const rawHeaders = filterOutAuthorizationParams((_e = (_d = httpOperation.request) === null || _d === void 0 ? void 0 : _d.headers) !== null && _e !== void 0 ? _e : [], httpOperation.security)
1406
+ const queryParams = getQueryParams({ httpOperation, parameterValues });
1407
+ const rawHeaders = filterOutAuthorizationParams((_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : [], httpOperation.security)
1325
1408
  .map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })
1326
1409
  .filter(({ value }) => value.length > 0);
1327
1410
  const [queryParamsWithAuth, headersWithAuth] = runAuthRequestEhancements(auth, queryParams, rawHeaders);
@@ -1330,7 +1413,7 @@ function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, paramet
1330
1413
  urlObject.search = new URLSearchParams(queryParamsWithAuth.map(nameAndValueObjectToPair)).toString();
1331
1414
  const body = typeof bodyInput === 'object' ? yield createRequestBody(mediaTypeContent, bodyInput) : bodyInput;
1332
1415
  const headers = Object.assign(Object.assign(Object.assign({}, ((mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== 'multipart/form-data' && {
1333
- 'Content-Type': (_f = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _f !== void 0 ? _f : 'application/json',
1416
+ 'Content-Type': (_c = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _c !== void 0 ? _c : 'application/json',
1334
1417
  })), Object.fromEntries(headersWithAuth.map(nameAndValueObjectToPair))), mockData === null || mockData === void 0 ? void 0 : mockData.header);
1335
1418
  return [
1336
1419
  urlObject.href,
@@ -1390,13 +1473,13 @@ const runAuthRequestEhancements = (auth, queryParams, headers) => {
1390
1473
  return [newQueryParams, newHeaders];
1391
1474
  };
1392
1475
  function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
1393
- var _a, _b, _c, _d, _e, _f, _g;
1476
+ var _a, _b, _c, _d;
1394
1477
  return __awaiter(this, void 0, void 0, function* () {
1395
1478
  const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
1396
1479
  const mimeType = (_a = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _a !== void 0 ? _a : 'application/json';
1397
1480
  const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase());
1398
- const queryParams = (_d = (_c = (_b = httpOperation.request) === null || _b === void 0 ? void 0 : _b.query) === null || _c === void 0 ? void 0 : _c.map(param => { var _a; return ({ name: param.name, value: (_a = parameterValues[param.name]) !== null && _a !== void 0 ? _a : '' }); }).filter(({ value }) => value.length > 0)) !== null && _d !== void 0 ? _d : [];
1399
- const headerParams = (_g = (_f = (_e = httpOperation.request) === null || _e === void 0 ? void 0 : _e.headers) === null || _f === void 0 ? void 0 : _f.map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })) !== null && _g !== void 0 ? _g : [];
1481
+ const queryParams = getQueryParams({ httpOperation, parameterValues });
1482
+ const headerParams = (_d = (_c = (_b = httpOperation.request) === null || _b === void 0 ? void 0 : _b.headers) === null || _c === void 0 ? void 0 : _c.map(header => { var _a; return ({ name: header.name, value: (_a = parameterValues[header.name]) !== null && _a !== void 0 ? _a : '' }); })) !== null && _d !== void 0 ? _d : [];
1400
1483
  if (mockData === null || mockData === void 0 ? void 0 : mockData.header) {
1401
1484
  headerParams.push({ name: 'Prefer', value: mockData.header.Prefer });
1402
1485
  }
@@ -2005,6 +2088,25 @@ ${scopes.map(([key, value]) => `- \`${key}\` - ${value}`).join('\n')}`;
2005
2088
  return description;
2006
2089
  }
2007
2090
 
2091
+ const RouterTypeContext = React.createContext(null);
2092
+ const useRouterType = () => {
2093
+ return React.useContext(RouterTypeContext);
2094
+ };
2095
+
2096
+ const LinkHeading = React.memo(function LinkHeading(props) {
2097
+ const isUsingRouter = !!useRouterType();
2098
+ const Comp = isUsingRouter ? CustomLinkHeading : LinkHeading$1;
2099
+ return React.createElement(Comp, Object.assign({}, props));
2100
+ });
2101
+ const CustomLinkHeading = React.memo(function LinkHeading(_a) {
2102
+ var { id: _id } = _a, props = __rest(_a, ["id"]);
2103
+ const { pathname } = useLocation();
2104
+ const routerKind = React.useContext(RouterTypeContext);
2105
+ const route = pathname.split('#')[0];
2106
+ const id = routerKind === 'hash' ? `${route}#${_id}` : _id;
2107
+ return React.createElement(LinkHeading$1, Object.assign({ id: id }, props));
2108
+ });
2109
+
2008
2110
  const SectionTitle = ({ title, id, size = 2, children }) => {
2009
2111
  return (React.createElement(HStack, { spacing: 6 },
2010
2112
  React.createElement(Box, { as: LinkHeading, size: size, "aria-label": title, id: id || slugify(title) }, title),
@@ -2030,6 +2132,7 @@ const Body = ({ body, onChange }) => {
2030
2132
  var _a;
2031
2133
  const refResolver = useInlineRefResolver();
2032
2134
  const [chosenContent, setChosenContent] = React.useState(0);
2135
+ const { nodeHasChanged } = useOptionsCtx();
2033
2136
  React.useEffect(() => {
2034
2137
  onChange(chosenContent);
2035
2138
  }, [chosenContent]);
@@ -2037,11 +2140,14 @@ const Body = ({ body, onChange }) => {
2037
2140
  return null;
2038
2141
  const { contents = [], description } = body;
2039
2142
  const schema = (_a = contents[chosenContent]) === null || _a === void 0 ? void 0 : _a.schema;
2143
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: body.id, attr: 'description' });
2040
2144
  return (React.createElement(VStack, { spacing: 6 },
2041
2145
  React.createElement(SectionSubtitle, { title: "Body", id: "request-body" }, contents.length > 0 && (React.createElement(Flex, { flex: 1, justify: "end" },
2042
2146
  React.createElement(Select, { "aria-label": "Request Body Content Type", value: String(chosenContent), onChange: (value) => setChosenContent(parseInt(String(value), 10)), options: contents.map((content, index) => ({ label: content.mediaType, value: index })), size: "sm" })))),
2043
- description && React.createElement(MarkdownViewer, { markdown: description }),
2044
- isJSONSchema(schema) && (React.createElement(JsonSchemaViewer, { resolveRef: refResolver, schema: getOriginalObject(schema), viewMode: "write", renderRootTreeLines: true }))));
2147
+ description && (React.createElement(Box, { pos: "relative" },
2148
+ React.createElement(MarkdownViewer, { markdown: description }),
2149
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2150
+ isJSONSchema(schema) && (React.createElement(JsonSchemaViewer, { resolveRef: refResolver, schema: getOriginalObject(schema), viewMode: "write", renderRootTreeLines: true, nodeHasChanged: nodeHasChanged }))));
2045
2151
  };
2046
2152
  Body.displayName = 'HttpOperation.Body';
2047
2153
 
@@ -2065,14 +2171,16 @@ const defaultStyle = {
2065
2171
  cookie: HttpParamStyles.Form,
2066
2172
  };
2067
2173
  const Parameters = ({ parameters, parameterType }) => {
2174
+ const { nodeHasChanged } = useOptionsCtx();
2175
+ const refResolver = useInlineRefResolver();
2068
2176
  const schema = React.useMemo(() => httpOperationParamsToSchema({ parameters, parameterType }), [parameters, parameterType]);
2069
2177
  if (!schema)
2070
2178
  return null;
2071
- return React.createElement(JsonSchemaViewer, { schema: schema, disableCrumbs: true });
2179
+ return React.createElement(JsonSchemaViewer, { resolveRef: refResolver, schema: schema, disableCrumbs: true, nodeHasChanged: nodeHasChanged });
2072
2180
  };
2073
2181
  Parameters.displayName = 'HttpOperation.Parameters';
2074
2182
  const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2075
- var _a;
2183
+ var _a, _b, _c;
2076
2184
  if (!parameters || !parameters.length)
2077
2185
  return null;
2078
2186
  const schema = {
@@ -2081,8 +2189,6 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2081
2189
  };
2082
2190
  const sortedParams = sortBy(parameters, ['required', 'name']);
2083
2191
  for (const p of sortedParams) {
2084
- if (!p.schema)
2085
- continue;
2086
2192
  const { name, description, required, deprecated, examples, style } = p;
2087
2193
  const paramExamples = (examples === null || examples === void 0 ? void 0 : examples.map(example => {
2088
2194
  if (isNodeExample(example)) {
@@ -2092,10 +2198,10 @@ const httpOperationParamsToSchema = ({ parameters, parameterType }) => {
2092
2198
  })) || [];
2093
2199
  const schemaExamples = (_a = p.schema) === null || _a === void 0 ? void 0 : _a.examples;
2094
2200
  const schemaExamplesArray = Array.isArray(schemaExamples) ? schemaExamples : [];
2095
- const paramDescription = description || p.schema.description;
2201
+ const paramDescription = description || ((_b = p.schema) === null || _b === void 0 ? void 0 : _b.description);
2096
2202
  const paramDeprecated = deprecated || p.schema.deprecated;
2097
2203
  const paramStyle = style && defaultStyle[parameterType] !== style ? readableStyles[style] || style : undefined;
2098
- schema.properties[p.name] = Object.assign(Object.assign({}, p.schema), { description: paramDescription, examples: [...paramExamples, ...schemaExamplesArray], deprecated: paramDeprecated, style: paramStyle });
2204
+ schema.properties[p.name] = Object.assign(Object.assign({}, (p.schema || {})), { description: paramDescription, examples: [...paramExamples, ...schemaExamplesArray], deprecated: paramDeprecated, style: paramStyle, 'x-stoplight': Object.assign(Object.assign({}, (((_c = p.schema) === null || _c === void 0 ? void 0 : _c['x-stoplight']) || {})), { id: p.id }) });
2099
2205
  if (required) {
2100
2206
  schema.required.push(name);
2101
2207
  }
@@ -2118,7 +2224,7 @@ const Request = ({ operation: { request, request: { path: pathParams = [], heade
2118
2224
  return null;
2119
2225
  return (React.createElement(VStack, { spacing: 8 },
2120
2226
  React.createElement(SectionTitle, { title: "Request" }),
2121
- securitySchemes.length > 0 && (React.createElement(VStack, { spacing: 3 }, securitySchemes.map((scheme, i) => (React.createElement(SecurityPanel, { key: i, scheme: scheme, includeKey: shouldIncludeKey(securitySchemes, scheme.type) }))))),
2227
+ React.createElement(SecuritySchemes$1, { schemes: securitySchemes }),
2122
2228
  pathParams.length > 0 && (React.createElement(VStack, { spacing: 5 },
2123
2229
  React.createElement(SectionSubtitle, { title: "Path Parameters" }),
2124
2230
  React.createElement(Parameters, { parameterType: "path", parameters: pathParams }))),
@@ -2139,6 +2245,15 @@ const SecurityPanel = ({ scheme, includeKey }) => {
2139
2245
  const [expandedState, setExpanded] = useAtom(schemeExpandedState);
2140
2246
  return (React.createElement(SubSectionPanel, { title: `Security: ${getReadableSecurityName(scheme, includeKey)}`, defaultIsOpen: !!expandedState[scheme.key], onChange: isOpen => setExpanded(Object.assign(Object.assign({}, expandedState), { [scheme.key]: isOpen })) },
2141
2247
  React.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) })));
2248
+ };
2249
+ const SecuritySchemes$1 = ({ schemes }) => {
2250
+ const { nodeHasChanged } = useOptionsCtx();
2251
+ if (!schemes.length) {
2252
+ return null;
2253
+ }
2254
+ return (React.createElement(VStack, { spacing: 3 }, schemes.map((scheme, i) => (React.createElement(Box, { pos: "relative", key: i },
2255
+ React.createElement(SecurityPanel, { scheme: scheme, includeKey: shouldIncludeKey(schemes, scheme.type) }),
2256
+ React.createElement(NodeAnnotation, { change: nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id }) }))))));
2142
2257
  };
2143
2258
 
2144
2259
  const Responses = ({ responses: unsortedResponses, onStatusCodeChange, onMediaTypeChange }) => {
@@ -2161,13 +2276,17 @@ const Response = ({ response, onMediaTypeChange }) => {
2161
2276
  const { contents = [], headers = [], description } = response;
2162
2277
  const [chosenContent, setChosenContent] = React.useState(0);
2163
2278
  const refResolver = useInlineRefResolver();
2279
+ const { nodeHasChanged } = useOptionsCtx();
2164
2280
  const responseContent = contents[chosenContent];
2165
2281
  const schema = responseContent === null || responseContent === void 0 ? void 0 : responseContent.schema;
2166
2282
  React.useEffect(() => {
2167
2283
  responseContent && onMediaTypeChange(responseContent.mediaType);
2168
2284
  }, [responseContent]);
2285
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: response.id, attr: 'description' });
2169
2286
  return (React.createElement(VStack, { spacing: 8, pt: 8 },
2170
- description && React.createElement(MarkdownViewer, { markdown: description }),
2287
+ description && (React.createElement(Box, { pos: "relative" },
2288
+ React.createElement(MarkdownViewer, { markdown: description }),
2289
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2171
2290
  headers.length > 0 && (React.createElement(VStack, { spacing: 5 },
2172
2291
  React.createElement(SectionSubtitle, { title: "Headers", id: "response-headers" }),
2173
2292
  React.createElement(Parameters, { parameterType: "header", parameters: headers }))),
@@ -2175,7 +2294,7 @@ const Response = ({ response, onMediaTypeChange }) => {
2175
2294
  React.createElement(SectionSubtitle, { title: "Body", id: "response-body" },
2176
2295
  React.createElement(Flex, { flex: 1, justify: "end" },
2177
2296
  React.createElement(Select, { "aria-label": "Response Body Content Type", value: String(chosenContent), onChange: (value) => setChosenContent(parseInt(String(value), 10)), options: contents.map((content, index) => ({ label: content.mediaType, value: index })), size: "sm" }))),
2178
- schema && (React.createElement(JsonSchemaViewer, { schema: getOriginalObject(schema), resolveRef: refResolver, viewMode: "read", parentCrumbs: ['responses', response.code], renderRootTreeLines: true }))))));
2297
+ schema && (React.createElement(JsonSchemaViewer, { schema: getOriginalObject(schema), resolveRef: refResolver, viewMode: "read", parentCrumbs: ['responses', response.code], renderRootTreeLines: true, nodeHasChanged: nodeHasChanged }))))));
2179
2298
  };
2180
2299
  Response.displayName = 'HttpOperation.Response';
2181
2300
  const codeToIntentVal = (code) => {
@@ -2193,6 +2312,7 @@ const codeToIntentVal = (code) => {
2193
2312
  };
2194
2313
 
2195
2314
  const HttpOperationComponent = React.memo(({ className, data: unresolvedData, layoutOptions, tryItCredentialsPolicy, tryItCorsProxy }) => {
2315
+ const { nodeHasChanged } = useOptionsCtx();
2196
2316
  const data = useResolvedObject(unresolvedData);
2197
2317
  const mocking = React.useContext(MockingContext);
2198
2318
  const isDeprecated = !!data.deprecated;
@@ -2202,15 +2322,12 @@ const HttpOperationComponent = React.memo(({ className, data: unresolvedData, la
2202
2322
  const [requestBodyIndex, setTextRequestBodyIndex] = React.useState(0);
2203
2323
  const prettyName = (data.summary || data.iid || '').trim();
2204
2324
  const hasBadges = isDeprecated || isInternal;
2205
- const header = (!(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) || hasBadges) && (React.createElement(VStack, { spacing: 5 },
2206
- React.createElement(HStack, { spacing: 5 },
2207
- !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && prettyName ? (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, prettyName)) : null,
2208
- React.createElement(HStack, { spacing: 2 },
2209
- isDeprecated && React.createElement(DeprecatedBadge, null),
2210
- isInternal && React.createElement(InternalBadge, { isHttpService: true }))),
2211
- React.createElement(MethodPath, { method: data.method, path: data.path })));
2325
+ const header = (React.createElement(OperationHeader, { id: data.id, method: data.method, path: data.path, noHeading: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading, hasBadges: hasBadges, name: prettyName, isDeprecated: isDeprecated, isInternal: isInternal }));
2326
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2212
2327
  const description = (React.createElement(VStack, { spacing: 10 },
2213
- data.description && React.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2328
+ data.description && (React.createElement(Box, { pos: "relative" },
2329
+ React.createElement(MarkdownViewer, { className: "HttpOperation__Description", markdown: data.description }),
2330
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2214
2331
  React.createElement(Request, { onChange: setTextRequestBodyIndex, operation: data }),
2215
2332
  data.responses && (React.createElement(Responses, { responses: data.responses, onMediaTypeChange: setResponseMediaType, onStatusCodeChange: setResponseStatusCode }))));
2216
2333
  const tryItPanel = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryItPanel) && (React.createElement(TryItWithRequestSamples, { httpOperation: data, responseMediaType: responseMediaType, responseStatusCode: responseStatusCode, requestBodyIndex: requestBodyIndex, hideTryIt: layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideTryIt, tryItCredentialsPolicy: tryItCredentialsPolicy, mockUrl: mocking.hideMocking ? undefined : mocking.mockUrl, corsProxy: tryItCorsProxy }));
@@ -2241,6 +2358,25 @@ function MethodPathInner({ method, path, chosenServerUrl }) {
2241
2358
  return (React.createElement(HStack, { spacing: 3, pl: 2.5, pr: 4, py: 2, bg: "canvas-50", rounded: "lg", fontFamily: "mono", display: "inline-flex", maxW: "full", title: fullUrl },
2242
2359
  React.createElement(Box, { py: 1, px: 2.5, rounded: "lg", bg: !isDark ? HttpMethodColors[method] : 'canvas-100', color: !isDark ? 'on-primary' : 'body', fontSize: "lg", fontWeight: "semibold", textTransform: "uppercase" }, method),
2243
2360
  pathElem));
2361
+ }
2362
+ function OperationHeader({ id, noHeading, hasBadges, name, isDeprecated, isInternal, method, path, }) {
2363
+ const { nodeHasChanged } = useOptionsCtx();
2364
+ if (noHeading && !hasBadges) {
2365
+ return null;
2366
+ }
2367
+ const lineOneChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['iid', 'summary', 'deprecated', 'internal'] });
2368
+ const lineTwoChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['method', 'path'] });
2369
+ return (React.createElement(VStack, { spacing: 5 },
2370
+ React.createElement(Box, { pos: "relative" },
2371
+ React.createElement(HStack, { spacing: 5 },
2372
+ !noHeading && name ? (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, name)) : null,
2373
+ React.createElement(HStack, { spacing: 2 },
2374
+ isDeprecated && React.createElement(DeprecatedBadge, null),
2375
+ isInternal && React.createElement(InternalBadge, { isHttpService: true }))),
2376
+ React.createElement(NodeAnnotation, { change: lineOneChanged })),
2377
+ React.createElement(Box, { pos: "relative" },
2378
+ React.createElement(MethodPath, { method: method, path: path }),
2379
+ React.createElement(NodeAnnotation, { change: lineTwoChanged }))));
2244
2380
  }
2245
2381
 
2246
2382
  const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) => {
@@ -2251,7 +2387,9 @@ const PoweredByLink = ({ source, pathname, packageType, layout = 'sidebar' }) =>
2251
2387
  React.createElement("strong", null, "Stoplight"))));
2252
2388
  };
2253
2389
 
2254
- const AdditionalInfo = ({ termsOfService, contact, license }) => {
2390
+ const AdditionalInfo = ({ id, termsOfService, contact, license }) => {
2391
+ const { nodeHasChanged } = useOptionsCtx();
2392
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id, attr: ['termsOfService', 'contact', 'license'] });
2255
2393
  const contactLink = (contact === null || contact === void 0 ? void 0 : contact.name) && (contact === null || contact === void 0 ? void 0 : contact.url)
2256
2394
  ? `[Contact ${contact.name}](${contact.url})`
2257
2395
  : (contact === null || contact === void 0 ? void 0 : contact.email)
@@ -2260,12 +2398,13 @@ const AdditionalInfo = ({ termsOfService, contact, license }) => {
2260
2398
  const licenseUrl = (license === null || license === void 0 ? void 0 : license.url) || `https://spdx.org/licenses/${license === null || license === void 0 ? void 0 : license.identifier}.html`;
2261
2399
  const licenseLink = (license === null || license === void 0 ? void 0 : license.name) && licenseUrl ? `[${license.name} License](${licenseUrl})` : '';
2262
2400
  const tosLink = termsOfService ? `[Terms of Service](${termsOfService})` : '';
2263
- return contactLink || licenseLink || tosLink ? (React__default.createElement(Panel, { rounded: true, isCollapsible: false },
2401
+ return contactLink || licenseLink || tosLink ? (React__default.createElement(Panel, { rounded: true, isCollapsible: false, pos: "relative" },
2264
2402
  React__default.createElement(Panel.Titlebar, { bg: "canvas-300" },
2265
2403
  React__default.createElement("span", { role: "heading" }, "Additional Information")),
2266
2404
  React__default.createElement(Panel.Content, { p: 0 },
2267
2405
  React__default.createElement(Panel.Content, null,
2268
- React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${contactLink}\n \n${licenseLink}\n \n ${tosLink}` }))))) : null;
2406
+ React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${contactLink}\n \n${licenseLink}\n \n ${tosLink}` }))),
2407
+ React__default.createElement(NodeAnnotation, { change: hasChanged }))) : null;
2269
2408
  };
2270
2409
 
2271
2410
  const ExportButton = ({ original, bundled }) => {
@@ -2287,11 +2426,14 @@ const SecuritySchemes = ({ schemes, defaultScheme, defaultCollapsed = false, })
2287
2426
  React__default.createElement(Panel.Content, { p: 0 }, sortBy(schemes, 'type').map((scheme, i) => (React__default.createElement(SecurityScheme, { key: i, scheme: scheme, defaultIsOpen: defaultScheme ? scheme.key === defaultScheme : i === 0, isCollapsible: schemes.length > 1, showSchemeKey: shouldIncludeKey(schemes, scheme.type) }))))));
2288
2427
  };
2289
2428
  const SecurityScheme = ({ scheme, defaultIsOpen, isCollapsible, showSchemeKey }) => {
2290
- return (React__default.createElement(Panel, { defaultIsOpen: defaultIsOpen, isCollapsible: isCollapsible },
2429
+ const { nodeHasChanged } = useOptionsCtx();
2430
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: scheme.id });
2431
+ return (React__default.createElement(Panel, { defaultIsOpen: defaultIsOpen, isCollapsible: isCollapsible, pos: "relative" },
2291
2432
  React__default.createElement(Panel.Titlebar, null,
2292
2433
  React__default.createElement(Box, { as: "span", role: "heading" }, getReadableSecurityName(scheme, showSchemeKey))),
2293
2434
  React__default.createElement(Panel.Content, null,
2294
- React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) }))));
2435
+ React__default.createElement(MarkdownViewer, { style: { fontSize: 12 }, markdown: `${scheme.description || ''}\n\n` + getDefaultDescription(scheme) })),
2436
+ React__default.createElement(NodeAnnotation, { change: hasChanged })));
2295
2437
  };
2296
2438
 
2297
2439
  const ServerInfo = ({ servers, mockUrl }) => {
@@ -2305,13 +2447,14 @@ const ServerInfo = ({ servers, mockUrl }) => {
2305
2447
  return (React.createElement(InvertTheme, null,
2306
2448
  React.createElement(Panel, { rounded: true, isCollapsible: false, className: "BaseURLContent", w: "full" },
2307
2449
  React.createElement(Panel.Titlebar, { whitespace: "nowrap" }, "API Base URL"),
2308
- React.createElement(Box, { overflowX: "auto" },
2309
- React.createElement(Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
2310
- React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, { key: index }))))))))));
2450
+ React.createElement(Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
2451
+ React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, { key: index })))))))));
2311
2452
  };
2312
- const ServerUrl = ({ description, url, marginBottom = true }) => {
2453
+ const ServerUrl = ({ id, description, url }) => {
2454
+ const { nodeHasChanged } = useOptionsCtx();
2313
2455
  const { onCopy, hasCopied } = useClipboard(url);
2314
- return (React.createElement(Box, { whitespace: "nowrap" },
2456
+ const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id });
2457
+ return (React.createElement(Box, { whitespace: "nowrap", pos: "relative" },
2315
2458
  React.createElement(Text, { pr: 2, fontWeight: "bold" },
2316
2459
  description,
2317
2460
  ":"),
@@ -2321,26 +2464,37 @@ const ServerUrl = ({ description, url, marginBottom = true }) => {
2321
2464
  React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'copy'] }))),
2322
2465
  hasCopied && (React.createElement(Box, { p: 1 },
2323
2466
  "Copied Server URL ",
2324
- React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'check'] }))))));
2467
+ React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'check'] })))),
2468
+ React.createElement(NodeAnnotation, { change: hasChanged, additionalLeftOffset: 16 })));
2325
2469
  };
2326
2470
 
2327
- const HttpServiceComponent = React.memo(({ data, location = {}, layoutOptions, exportProps }) => {
2471
+ const HttpServiceComponent = React.memo(({ data: unresolvedData, location = {}, layoutOptions, exportProps }) => {
2328
2472
  var _a, _b, _c, _d;
2473
+ const { nodeHasChanged } = useOptionsCtx();
2474
+ const data = useResolvedObject(unresolvedData);
2329
2475
  const { search, pathname } = location;
2330
2476
  const mocking = React.useContext(MockingContext);
2331
2477
  const query = new URLSearchParams(search);
2478
+ const nameChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'name' });
2479
+ const versionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'version' });
2480
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: data.id, attr: 'description' });
2332
2481
  return (React.createElement(Box, { mb: 10 },
2333
2482
  data.name && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && (React.createElement(Flex, { justifyContent: "between", alignItems: "center" },
2334
- React.createElement(Heading, { size: 1, mb: 4, fontWeight: "semibold" }, data.name),
2483
+ React.createElement(Box, { pos: "relative" },
2484
+ React.createElement(Heading, { size: 1, mb: 4, fontWeight: "semibold" }, data.name),
2485
+ React.createElement(NodeAnnotation, { change: nameChanged })),
2335
2486
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React.createElement(ExportButton, Object.assign({}, exportProps)))),
2336
- data.version && (React.createElement(Box, { mb: 5 },
2337
- React.createElement(VersionBadge, { value: data.version }))),
2487
+ data.version && (React.createElement(Box, { mb: 5, pos: "relative" },
2488
+ React.createElement(VersionBadge, { value: data.version }),
2489
+ React.createElement(NodeAnnotation, { change: versionChanged }))),
2338
2490
  pathname && (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.showPoweredByLink) && (React.createElement(PoweredByLink, { source: (_a = data.name) !== null && _a !== void 0 ? _a : 'no-title', pathname: pathname, packageType: "elements", layout: "stacked" })),
2339
2491
  React.createElement(VStack, { spacing: 6 },
2340
2492
  React.createElement(ServerInfo, { servers: (_b = data.servers) !== null && _b !== void 0 ? _b : [], mockUrl: mocking.mockUrl }),
2341
- React.createElement(Box, null, ((_c = data.securitySchemes) === null || _c === void 0 ? void 0 : _c.length) && (React.createElement(SecuritySchemes, { schemes: data.securitySchemes, defaultScheme: query.get('security') || undefined }))),
2342
- React.createElement(Box, null, (((_d = data.contact) === null || _d === void 0 ? void 0 : _d.email) || data.license || data.termsOfService) && (React.createElement(AdditionalInfo, { contact: data.contact, license: data.license, termsOfService: data.termsOfService })))),
2343
- data.description && React.createElement(MarkdownViewer, { className: "sl-my-5", markdown: data.description })));
2493
+ React.createElement(Box, null, ((_c = data.securitySchemes) === null || _c === void 0 ? void 0 : _c.length) ? (React.createElement(SecuritySchemes, { schemes: data.securitySchemes, defaultScheme: query.get('security') || undefined })) : null),
2494
+ React.createElement(Box, null, (((_d = data.contact) === null || _d === void 0 ? void 0 : _d.email) || data.license || data.termsOfService) && (React.createElement(AdditionalInfo, { id: data.id, contact: data.contact, license: data.license, termsOfService: data.termsOfService })))),
2495
+ data.description && (React.createElement(Box, { pos: "relative" },
2496
+ React.createElement(MarkdownViewer, { className: "sl-my-5", markdown: data.description }),
2497
+ React.createElement(NodeAnnotation, { change: descriptionChanged })))));
2344
2498
  });
2345
2499
  HttpServiceComponent.displayName = 'HttpService.Component';
2346
2500
  const HttpService = withErrorBoundary(HttpServiceComponent, { recoverableProps: ['data'] });
@@ -2362,23 +2516,31 @@ function useIsCompact(layoutOptions) {
2362
2516
  }
2363
2517
 
2364
2518
  const ModelComponent = ({ data: unresolvedData, className, nodeTitle, layoutOptions, exportProps, }) => {
2365
- var _a;
2519
+ var _a, _b;
2366
2520
  const resolveRef = useInlineRefResolver();
2367
2521
  const data = useResolvedObject(unresolvedData);
2522
+ const { nodeHasChanged } = useOptionsCtx();
2368
2523
  const { ref: layoutRef, isCompact } = useIsCompact(layoutOptions);
2369
- const title = (_a = data.title) !== null && _a !== void 0 ? _a : nodeTitle;
2524
+ const nodeId = (_a = data === null || data === void 0 ? void 0 : data['x-stoplight']) === null || _a === void 0 ? void 0 : _a.id;
2525
+ const title = (_b = data.title) !== null && _b !== void 0 ? _b : nodeTitle;
2370
2526
  const isInternal = !!data['x-internal'];
2371
2527
  const shouldDisplayHeader = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.noHeading) && (title !== undefined || (exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport)));
2528
+ const titleChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId, attr: ['title', 'internal'] });
2372
2529
  const header = (shouldDisplayHeader || isInternal) && (React.createElement(Flex, { justifyContent: "between", alignItems: "center" },
2373
- React.createElement(HStack, { spacing: 5 },
2374
- title && (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, title)),
2375
- React.createElement(HStack, { spacing: 2 }, isInternal && React.createElement(InternalBadge, null))),
2530
+ React.createElement(Box, { pos: "relative" },
2531
+ React.createElement(HStack, { spacing: 5 },
2532
+ title && (React.createElement(Heading, { size: 1, fontWeight: "semibold" }, title)),
2533
+ React.createElement(HStack, { spacing: 2 }, isInternal && React.createElement(InternalBadge, null))),
2534
+ React.createElement(NodeAnnotation, { change: titleChanged })),
2376
2535
  exportProps && !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideExport) && React.createElement(ExportButton, Object.assign({}, exportProps))));
2377
2536
  const modelExamples = !(layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.hideModelExamples) && React.createElement(ModelExamples, { data: data, isCollapsible: isCompact });
2537
+ const descriptionChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId, attr: 'description' });
2378
2538
  const description = (React.createElement(VStack, { spacing: 10 },
2379
- data.description && data.type === 'object' && React.createElement(MarkdownViewer, { role: "textbox", markdown: data.description }),
2539
+ data.description && data.type === 'object' && (React.createElement(Box, { pos: "relative" },
2540
+ React.createElement(MarkdownViewer, { role: "textbox", markdown: data.description }),
2541
+ React.createElement(NodeAnnotation, { change: descriptionChanged }))),
2380
2542
  isCompact && modelExamples,
2381
- React.createElement(JsonSchemaViewer, { resolveRef: resolveRef, schema: getOriginalObject(data) })));
2543
+ React.createElement(JsonSchemaViewer, { resolveRef: resolveRef, schema: getOriginalObject(data), nodeHasChanged: nodeHasChanged, skipTopLevelDescription: true })));
2382
2544
  return (React.createElement(TwoColumnLayout, { ref: layoutRef, className: cn('Model', className), header: header, left: description, right: !isCompact && modelExamples }));
2383
2545
  };
2384
2546
  const ModelExamples = React.memo(({ data, isCollapsible = false }) => {
@@ -2400,16 +2562,16 @@ const ModelExamples = React.memo(({ data, isCollapsible = false }) => {
2400
2562
  const Model = withErrorBoundary(ModelComponent, { recoverableProps: ['data'] });
2401
2563
 
2402
2564
  const Docs = React.memo((_a) => {
2403
- var { nodeType, nodeData, useNodeForRefResolving = false, refResolver } = _a, commonProps = __rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver"]);
2565
+ var { nodeType, nodeData, useNodeForRefResolving = false, refResolver, nodeHasChanged } = _a, commonProps = __rest(_a, ["nodeType", "nodeData", "useNodeForRefResolving", "refResolver", "nodeHasChanged"]);
2404
2566
  const parsedNode = useParsedData(nodeType, nodeData);
2405
2567
  if (!parsedNode) {
2406
2568
  return null;
2407
2569
  }
2408
- const parsedDocs = React.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2570
+ let elem = React.createElement(ParsedDocs, Object.assign({ node: parsedNode }, commonProps));
2409
2571
  if (useNodeForRefResolving) {
2410
- return (React.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, parsedDocs));
2572
+ elem = (React.createElement(InlineRefResolverProvider, { document: parsedNode.data, resolver: refResolver }, elem));
2411
2573
  }
2412
- return parsedDocs;
2574
+ return React.createElement(ElementsOptionsProvider, { nodeHasChanged: nodeHasChanged }, elem);
2413
2575
  });
2414
2576
  const ParsedDocs = (_a) => {
2415
2577
  var { node } = _a, commonProps = __rest(_a, ["node"]);
@@ -2837,16 +2999,33 @@ const useRouter = (router, basePath, staticRouterPath) => {
2837
2999
  };
2838
3000
  };
2839
3001
 
3002
+ const components = {
3003
+ a: ReactRouterMarkdownLink,
3004
+ h2: (_a) => {
3005
+ var props = __rest(_a, ["color"]);
3006
+ return React.createElement(LinkHeading, Object.assign({ size: 2 }, props));
3007
+ },
3008
+ h3: (_a) => {
3009
+ var props = __rest(_a, ["color"]);
3010
+ return React.createElement(LinkHeading, Object.assign({ size: 3 }, props));
3011
+ },
3012
+ h4: (_a) => {
3013
+ var props = __rest(_a, ["color"]);
3014
+ return React.createElement(LinkHeading, Object.assign({ size: 4 }, props));
3015
+ },
3016
+ };
2840
3017
  function withRouter(WrappedComponent) {
2841
3018
  const WithRouter = (props) => {
2842
3019
  var _a, _b, _c;
2843
3020
  const basePath = (_a = props.basePath) !== null && _a !== void 0 ? _a : '/';
2844
3021
  const staticRouterPath = (_b = props.staticRouterPath) !== null && _b !== void 0 ? _b : '';
2845
- const { Router, routerProps } = useRouter((_c = props.router) !== null && _c !== void 0 ? _c : 'history', basePath, staticRouterPath);
2846
- return (React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
2847
- React.createElement(Route, { path: "/" },
2848
- React.createElement(MarkdownComponentsProvider, { value: { a: ReactRouterMarkdownLink } },
2849
- React.createElement(WrappedComponent, Object.assign({}, props))))));
3022
+ const routerType = (_c = props.router) !== null && _c !== void 0 ? _c : 'history';
3023
+ const { Router, routerProps } = useRouter(routerType, basePath, staticRouterPath);
3024
+ return (React.createElement(RouterTypeContext.Provider, { value: routerType },
3025
+ React.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
3026
+ React.createElement(Route, { path: "/" },
3027
+ React.createElement(MarkdownComponentsProvider, { value: components },
3028
+ React.createElement(WrappedComponent, Object.assign({}, props)))))));
2850
3029
  };
2851
3030
  WithRouter.displayName = `WithRouter(${getDisplayName(WrappedComponent)})`;
2852
3031
  return WithRouter;