@eventcatalog/core 5.5.2 → 5.6.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/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-5HSYALWK.js → chunk-2NALBBWH.js} +1 -1
- package/dist/{chunk-WG6ZK6L3.js → chunk-L63ED43X.js} +1 -1
- package/dist/{chunk-CNN747RO.js → chunk-LVOGLB6C.js} +1 -1
- package/dist/{chunk-7ZEKF2LA.js → chunk-N7LDFUEB.js} +1 -1
- package/dist/{chunk-MC4F3VVM.js → chunk-QJXVAM76.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/development/customization/05-resource-sidebar.md +1 -1
- package/dist/docs/development/guides/domains/09-reference.md +5 -1
- package/dist/docs/development/guides/resources/schemas/03-add-specifications-to-services/04-add-generic-specifications.md +139 -0
- package/dist/docs/development/guides/resources/schemas/03-add-specifications-to-services/_category_.json +1 -1
- package/dist/docs/development/guides/resources/services/08-reference.md +6 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/public/icons/specification.svg +1 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +51 -25
- package/eventcatalog/src/components/Grids/specification-utils.ts +7 -1
- package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +1 -1
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeBlock.tsx +58 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -1
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +38 -76
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +15 -11
- package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +171 -142
- package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +5 -8
- package/eventcatalog/src/components/SchemaExplorer/types.ts +2 -0
- package/eventcatalog/src/components/SchemaExplorer/utils.ts +70 -1
- package/eventcatalog/src/content.config.ts +3 -1
- package/eventcatalog/src/features/ai/chat-api.ts +6 -1
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +1 -1
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +1 -1
- package/eventcatalog/src/features/tools/catalog-tools.ts +8 -3
- package/eventcatalog/src/pages/.well-known/api-catalog.ts +1 -11
- package/eventcatalog/src/pages/api-catalog/specifications/[collection]/[id]/[version]/[specification].ts +1 -11
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/generic/[filename].astro +137 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/generic/_SpecificationSource.tsx +44 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/generic/_[filename].data.ts +75 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +14 -1
- package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +10 -1
- package/eventcatalog/src/stores/sidebar-store/builders/service.ts +8 -1
- package/eventcatalog/src/stores/sidebar-store/custom-sidebar.ts +3 -1
- package/eventcatalog/src/utils/collections/util.ts +38 -1
- package/eventcatalog/src/utils/icon.ts +8 -0
- package/eventcatalog/src/utils/node-graphs/utils/utils.ts +5 -2
- package/eventcatalog/src/utils/schema-explorer.ts +7 -2
- package/package.json +6 -6
|
@@ -169,7 +169,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
169
169
|
var import_os = __toESM(require("os"), 1);
|
|
170
170
|
|
|
171
171
|
// package.json
|
|
172
|
-
var version = "5.
|
|
172
|
+
var version = "5.6.1";
|
|
173
173
|
|
|
174
174
|
// src/constants.ts
|
|
175
175
|
var VERSION = version;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-LVOGLB6C.js";
|
|
4
4
|
import "../chunk-AD7CL772.js";
|
|
5
|
-
import "../chunk-
|
|
5
|
+
import "../chunk-2NALBBWH.js";
|
|
6
6
|
import "../chunk-YRZK7HKX.js";
|
|
7
7
|
import "../chunk-CA6WCQ2T.js";
|
|
8
8
|
import "../chunk-CPW6G62V.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-L63ED43X.js";
|
|
10
10
|
import "../chunk-4G7FSRPN.js";
|
|
11
11
|
export {
|
|
12
12
|
log_build_default as default
|
package/dist/constants.cjs
CHANGED
package/dist/constants.js
CHANGED
|
@@ -89,7 +89,7 @@ Inside a custom group, each entry in `pages` is one of:
|
|
|
89
89
|
| Section token | `"$inbound-messages"` | The **items** of a predefined section, spliced into your group — this is how you extend a generated section with your own pages |
|
|
90
90
|
| Resource reference | `"[[service\|payment-api]]"` | The resource, with its own expandable nested sidebar, at its latest version. Messages can pin a version: `"[[event\|payment-captured@1.0.0]]"` — other types always reference the latest |
|
|
91
91
|
| Documentation reference | `"[[doc\|guides/onboarding]]"` | One of this resource's [documentation pages](/docs/development/bring-your-own-documentation/resource-docs/adding-resource-docs), addressed as `<type>/<id>` |
|
|
92
|
-
| Specification reference | `"[[spec\|openapi.yml]]"` | A specification page, with the OpenAPI/AsyncAPI/GraphQL logo and the spec's name |
|
|
92
|
+
| Specification reference | `"[[spec\|openapi.yml]]"` | A specification page, with the OpenAPI/AsyncAPI/GraphQL logo (or a generic specification's icon) and the spec's name |
|
|
93
93
|
| Schema reference | `"[[schema\|payment-captured]]"` | A message's schema page |
|
|
94
94
|
| Link | `{ "title": "Runbook", "href": "https://..." }` | A plain link. External links (any protocol) open in a new tab and show an external-link icon |
|
|
95
95
|
| Nested group | `{ "title": "Internal", "collapsed": true, "pages": [...] }` | A subsection. Groups nest to any depth |
|
|
@@ -425,14 +425,18 @@ specifications:
|
|
|
425
425
|
- type: graphql
|
|
426
426
|
path: graphql.yml
|
|
427
427
|
name: GraphQL Specification
|
|
428
|
+
- type: generic
|
|
429
|
+
path: carrier-sla.yaml
|
|
430
|
+
name: Carrier Service Levels
|
|
428
431
|
---
|
|
429
432
|
```
|
|
430
433
|
|
|
431
434
|
| Property | Type | Required | Description |
|
|
432
435
|
| -------- | ---- | -------- | ----------- |
|
|
433
|
-
| `type` | `string` | Yes | The type of specification. |
|
|
436
|
+
| `type` | `string` | Yes | The type of specification: `openapi`, `asyncapi`, `graphql` or `generic` ([any other file](/docs/development/guides/resources/schemas/add-specifications-to-services/add-generic-specifications), such as a WSDL, an XML Schema, or a JSON or YAML contract). |
|
|
434
437
|
| `path` | `string` | Yes | The path to the specification file. |
|
|
435
438
|
| `name` | `string` | No | Optional friendly name of the specification, rendered in the UI. |
|
|
439
|
+
| `icon` | `string` | No | Icon for a `generic` specification: a path in your `public` folder or a URL. |
|
|
436
440
|
|
|
437
441
|
**Older versions of EventCatalog (< 2.39.0)**
|
|
438
442
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
keywords:
|
|
3
|
+
- EventCatalog schemas
|
|
4
|
+
- WSDL
|
|
5
|
+
- XML Schema
|
|
6
|
+
- XSD
|
|
7
|
+
- generic specifications
|
|
8
|
+
sidebar_position: 4
|
|
9
|
+
sidebar_label: Other formats (XML, JSON, YAML)
|
|
10
|
+
title: Add other specifications (XML, JSON, YAML)
|
|
11
|
+
description: Attach any specification file, such as a WSDL, an XML Schema, or a JSON or YAML contract, to a service or domain
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
import AddedIn from '@site/src/components/MDX/AddedIn';
|
|
15
|
+
import ProjectTree from '@site/src/components/MDX/ProjectTree';
|
|
16
|
+
|
|
17
|
+
<AddedIn version="5.6.0" />
|
|
18
|
+
|
|
19
|
+
Not every API is described with OpenAPI, AsyncAPI or GraphQL. A SOAP service has a WSDL, a partner integration may share an XML Schema, and a team may keep its contract in its own JSON or YAML format.
|
|
20
|
+
|
|
21
|
+
Use `type: generic` to attach any of these files to a service or domain. EventCatalog adds a page for the file and lists it with your other specifications.
|
|
22
|
+
|
|
23
|
+
Generic specifications are shown as source code. EventCatalog doesn't parse them, so any file works. These files get syntax highlighting:
|
|
24
|
+
|
|
25
|
+
| Format | File extensions |
|
|
26
|
+
| ------ | --------------- |
|
|
27
|
+
| XML | `.xml`, `.xsd`, `.wsdl`, `.xsl`, `.xslt` |
|
|
28
|
+
| JSON | `.json` |
|
|
29
|
+
| YAML | `.yaml`, `.yml` |
|
|
30
|
+
|
|
31
|
+
Other files are shown as plain text.
|
|
32
|
+
|
|
33
|
+
## Add a generic specification
|
|
34
|
+
|
|
35
|
+
Put the file inside the service (or domain) folder.
|
|
36
|
+
|
|
37
|
+
<ProjectTree
|
|
38
|
+
items={[
|
|
39
|
+
{
|
|
40
|
+
name: 'services',
|
|
41
|
+
type: 'folder',
|
|
42
|
+
defaultOpen: true,
|
|
43
|
+
children: [
|
|
44
|
+
{
|
|
45
|
+
name: 'CarrierAdapter',
|
|
46
|
+
type: 'folder',
|
|
47
|
+
defaultOpen: true,
|
|
48
|
+
children: [
|
|
49
|
+
{ name: 'index.mdx' },
|
|
50
|
+
{
|
|
51
|
+
name: 'specs',
|
|
52
|
+
type: 'folder',
|
|
53
|
+
defaultOpen: true,
|
|
54
|
+
children: [
|
|
55
|
+
{ name: 'carrier-shipping.wsdl', highlight: true },
|
|
56
|
+
{ name: 'shipment.xsd', highlight: true },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
]}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
Then reference the file in the frontmatter.
|
|
67
|
+
|
|
68
|
+
```md title="example: /services/CarrierAdapter/index.mdx"
|
|
69
|
+
---
|
|
70
|
+
specifications:
|
|
71
|
+
- type: generic
|
|
72
|
+
# Path to the file, relative to the service folder
|
|
73
|
+
path: specs/carrier-shipping.wsdl
|
|
74
|
+
# Friendly name for the specification (defaults to "Specification")
|
|
75
|
+
name: Carrier Shipping API (WSDL)
|
|
76
|
+
- type: generic
|
|
77
|
+
path: specs/shipment.xsd
|
|
78
|
+
name: Shipment XML Schema
|
|
79
|
+
---
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Each generic specification gets a page at `/docs/services/{id}/{version}/generic/{filename}`, where `{filename}` is the file name without its extension. It is also listed:
|
|
83
|
+
|
|
84
|
+
- under **API & Contracts** in the sidebar
|
|
85
|
+
- in the badges on the service or domain page
|
|
86
|
+
- in the [Schema Explorer](/docs/development/guides/resources/schemas/explore-schemas/schema-explorer), where you can filter by the **Specs** type and by format (XML, JSON, YAML)
|
|
87
|
+
- in the node's menu in the visualiser
|
|
88
|
+
|
|
89
|
+
:::info
|
|
90
|
+
The page URL uses the file name without its extension, so give each specification on a resource its own file name (for example, don't use both `contract.json` and `contract.yaml`).
|
|
91
|
+
:::
|
|
92
|
+
|
|
93
|
+
## Use a custom icon
|
|
94
|
+
|
|
95
|
+
Generic specifications use a default specification icon. Use `icon` to set your own. It can be a path to a file in your catalog's `public` folder, or a URL.
|
|
96
|
+
|
|
97
|
+
```md title="example: /services/CarrierAdapter/index.mdx"
|
|
98
|
+
---
|
|
99
|
+
specifications:
|
|
100
|
+
- type: generic
|
|
101
|
+
path: specs/carrier-shipping.wsdl
|
|
102
|
+
name: Carrier Shipping API (WSDL)
|
|
103
|
+
# /public/icons/soap.svg in your catalog
|
|
104
|
+
icon: /icons/soap.svg
|
|
105
|
+
---
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The icon shows on the specification page, in the sidebar, on the resource page and in the Schema Explorer. `icon` only applies to generic specifications.
|
|
109
|
+
|
|
110
|
+
## Reference a file from a remote URL
|
|
111
|
+
|
|
112
|
+
You can also use a URL as the `path`. EventCatalog fetches the file when it builds your catalog, so the URL must be reachable from the build machine. Rebuild your catalog to show changes to the file.
|
|
113
|
+
|
|
114
|
+
```md title="example: /services/CarrierAdapter/index.mdx"
|
|
115
|
+
---
|
|
116
|
+
specifications:
|
|
117
|
+
- type: generic
|
|
118
|
+
path: https://raw.githubusercontent.com/acme/carrier-contracts/main/carrier-shipping.wsdl
|
|
119
|
+
name: Carrier Shipping API (WSDL)
|
|
120
|
+
# Optional: headers for private files. Only variables that start with EVENTCATALOG_ or EC_ are used.
|
|
121
|
+
headers:
|
|
122
|
+
Authorization: 'Bearer ${EC_GITHUB_TOKEN}'
|
|
123
|
+
---
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Remote files are shown on their specification page. The Schema Explorer only lists files in your catalog.
|
|
127
|
+
|
|
128
|
+
## Add generic specifications to a domain
|
|
129
|
+
|
|
130
|
+
Domains support generic specifications in the same way. Put the file in the domain folder and add it to the domain's `specifications`.
|
|
131
|
+
|
|
132
|
+
```md title="example: /domains/Fulfilment/index.mdx"
|
|
133
|
+
---
|
|
134
|
+
specifications:
|
|
135
|
+
- type: generic
|
|
136
|
+
path: carrier-sla.yaml
|
|
137
|
+
name: Carrier Service Levels
|
|
138
|
+
---
|
|
139
|
+
```
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
"link": {
|
|
7
7
|
"type": "generated-index",
|
|
8
8
|
"slug": "/schemas/add-specifications-to-services",
|
|
9
|
-
"description": "Attach OpenAPI, AsyncAPI, and
|
|
9
|
+
"description": "Attach OpenAPI, AsyncAPI, GraphQL and other specifications (XML, JSON, YAML) to services."
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -213,7 +213,7 @@ externalSystem: true
|
|
|
213
213
|
|
|
214
214
|
- Type: `object` or `array`
|
|
215
215
|
|
|
216
|
-
OpenAPI, AsyncAPI, or GraphQL specifications for the service.
|
|
216
|
+
OpenAPI, AsyncAPI, or GraphQL specifications for the service. Use `type: generic` for any other specification file, such as a WSDL, an XML Schema, or a JSON or YAML contract ([read more](/docs/development/guides/resources/schemas/add-specifications-to-services/add-generic-specifications)).
|
|
217
217
|
|
|
218
218
|
```md title="Example"
|
|
219
219
|
---
|
|
@@ -223,6 +223,11 @@ specifications:
|
|
|
223
223
|
name: Public API
|
|
224
224
|
- type: asyncapi
|
|
225
225
|
path: asyncapi.yml
|
|
226
|
+
- type: generic
|
|
227
|
+
path: carrier-shipping.wsdl
|
|
228
|
+
name: Carrier Shipping API (WSDL)
|
|
229
|
+
# Optional, generic specifications only
|
|
230
|
+
icon: /icons/soap.svg
|
|
226
231
|
---
|
|
227
232
|
```
|
|
228
233
|
|
package/dist/eventcatalog.cjs
CHANGED
|
@@ -174,7 +174,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
174
174
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
175
175
|
|
|
176
176
|
// package.json
|
|
177
|
-
var version = "5.
|
|
177
|
+
var version = "5.6.1";
|
|
178
178
|
|
|
179
179
|
// src/constants.ts
|
|
180
180
|
var VERSION = version;
|
package/dist/eventcatalog.js
CHANGED
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
import "./chunk-CA4U2JP7.js";
|
|
8
8
|
import {
|
|
9
9
|
log_build_default
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LVOGLB6C.js";
|
|
11
11
|
import {
|
|
12
12
|
getLicenseStatus,
|
|
13
13
|
printLicenseStatus
|
|
14
14
|
} from "./chunk-AD7CL772.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-2NALBBWH.js";
|
|
16
16
|
import "./chunk-YRZK7HKX.js";
|
|
17
17
|
import "./chunk-CA6WCQ2T.js";
|
|
18
18
|
import "./chunk-CPW6G62V.js";
|
|
@@ -39,10 +39,10 @@ import {
|
|
|
39
39
|
} from "./chunk-NKNOXQ62.js";
|
|
40
40
|
import {
|
|
41
41
|
generate
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-N7LDFUEB.js";
|
|
43
43
|
import {
|
|
44
44
|
logger
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-QJXVAM76.js";
|
|
46
46
|
import {
|
|
47
47
|
resolve_catalog_dependencies_default
|
|
48
48
|
} from "./chunk-PD7KTSYN.js";
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
} from "./chunk-LFQJXLHG.js";
|
|
80
80
|
import {
|
|
81
81
|
VERSION
|
|
82
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-L63ED43X.js";
|
|
83
83
|
import {
|
|
84
84
|
getEventCatalogConfigFile,
|
|
85
85
|
verifyRequiredFieldsAreInCatalogConfigFile
|
package/dist/generate.cjs
CHANGED
|
@@ -105,7 +105,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
|
|
|
105
105
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
106
106
|
|
|
107
107
|
// package.json
|
|
108
|
-
var version = "5.
|
|
108
|
+
var version = "5.6.1";
|
|
109
109
|
|
|
110
110
|
// src/constants.ts
|
|
111
111
|
var VERSION = version;
|
package/dist/generate.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generate
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-N7LDFUEB.js";
|
|
4
|
+
import "./chunk-QJXVAM76.js";
|
|
5
|
+
import "./chunk-L63ED43X.js";
|
|
6
6
|
import "./chunk-4G7FSRPN.js";
|
|
7
7
|
export {
|
|
8
8
|
generate
|
package/dist/utils/cli-logger.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="m10 13-2 2 2 2"/><path d="m14 17 2-2-2-2"/></svg>
|
|
@@ -408,10 +408,16 @@ const fadeInStyles = {
|
|
|
408
408
|
};
|
|
409
409
|
|
|
410
410
|
// Preprocess markdown to fix common formatting issues
|
|
411
|
-
const preprocessMarkdown = (text: string): string => {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
411
|
+
export const preprocessMarkdown = (text: string): string => {
|
|
412
|
+
return (
|
|
413
|
+
text
|
|
414
|
+
// Add newlines before headings if they're directly after text (no newline)
|
|
415
|
+
// This fixes cases like "some text.## Heading" → "some text.\n\n## Heading"
|
|
416
|
+
.replace(/([^\n])(#{1,6}\s)/g, '$1\n\n$2')
|
|
417
|
+
// Show a link still streaming in ("[Order Service](/docs/serv") as its text: shown raw, its URL wraps onto more
|
|
418
|
+
// lines until the closing bracket arrives, then the answer jumps back as it collapses into a link
|
|
419
|
+
.replace(/\[([^\]\n]*)\]\([^)\s]*$/, '$1')
|
|
420
|
+
);
|
|
415
421
|
};
|
|
416
422
|
|
|
417
423
|
// Helper to extract text content from message parts
|
|
@@ -564,9 +570,12 @@ const modalMarkdownComponents = {
|
|
|
564
570
|
const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
565
571
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
566
572
|
const modalInputRef = useRef<HTMLTextAreaElement>(null);
|
|
567
|
-
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
568
|
-
const modalMessagesEndRef = useRef<HTMLDivElement>(null);
|
|
569
573
|
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
|
574
|
+
const modalMessagesContainerRef = useRef<HTMLDivElement>(null);
|
|
575
|
+
// Follow the answer as it streams, until the person scrolls up to read: sending a message or the scroll button
|
|
576
|
+
// follows it again
|
|
577
|
+
const followLatest = useRef(true);
|
|
578
|
+
const lastScrollTop = useRef(new WeakMap<HTMLElement, number>());
|
|
570
579
|
const [inputValue, setInputValue] = useState('');
|
|
571
580
|
const [isSetupThinking, setIsSetupThinking] = useState(false);
|
|
572
581
|
const setupReplyTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -647,10 +656,14 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
647
656
|
return null;
|
|
648
657
|
}, [pathname]);
|
|
649
658
|
|
|
650
|
-
//
|
|
659
|
+
// Stop following the answer when the person scrolls up, and follow it again when they're back at the bottom.
|
|
660
|
+
// Only a move up stops it: content growing below doesn't scroll, and a scroll down is heading for the latest.
|
|
651
661
|
const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
|
|
652
|
-
const target = e.
|
|
662
|
+
const target = e.currentTarget;
|
|
653
663
|
const isNearBottom = target.scrollHeight - target.scrollTop - target.clientHeight < 100;
|
|
664
|
+
if (isNearBottom) followLatest.current = true;
|
|
665
|
+
else if (target.scrollTop < (lastScrollTop.current.get(target) ?? 0)) followLatest.current = false;
|
|
666
|
+
lastScrollTop.current.set(target, target.scrollTop);
|
|
654
667
|
setShowScrollButton(!isNearBottom);
|
|
655
668
|
}, []);
|
|
656
669
|
|
|
@@ -685,16 +698,24 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
685
698
|
// Get currently running tools from the last assistant message
|
|
686
699
|
const runningTools = useMemo(() => (lastAssistantMessage ? getRunningTools(lastAssistantMessage) : []), [lastAssistantMessage]);
|
|
687
700
|
|
|
688
|
-
//
|
|
689
|
-
|
|
690
|
-
|
|
701
|
+
// Jump the messages (in the panel and the fullscreen modal) to the latest. Instantly: a smooth scroll restarted on
|
|
702
|
+
// every streamed token lands short of the growing answer and scrolls back and forth.
|
|
703
|
+
const scrollToLatest = useCallback(() => {
|
|
704
|
+
for (const container of [messagesContainerRef.current, modalMessagesContainerRef.current]) {
|
|
705
|
+
if (!container) continue;
|
|
706
|
+
container.scrollTop = container.scrollHeight;
|
|
707
|
+
lastScrollTop.current.set(container, container.scrollTop);
|
|
708
|
+
}
|
|
691
709
|
}, []);
|
|
692
710
|
|
|
711
|
+
const scrollToBottom = useCallback(() => {
|
|
712
|
+
followLatest.current = true;
|
|
713
|
+
scrollToLatest();
|
|
714
|
+
}, [scrollToLatest]);
|
|
715
|
+
|
|
693
716
|
useEffect(() => {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
modalMessagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
697
|
-
}, [messages, scrollToBottom]);
|
|
717
|
+
if (followLatest.current) scrollToLatest();
|
|
718
|
+
}, [messages, scrollToLatest]);
|
|
698
719
|
|
|
699
720
|
// Focus modal input when fullscreen opens
|
|
700
721
|
useEffect(() => {
|
|
@@ -734,15 +755,19 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
734
755
|
|
|
735
756
|
// Add/remove padding to main application and header when sidebar panel is open
|
|
736
757
|
useEffect(() => {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
758
|
+
// Looked up each time: the panel stays open across ClientRouter navigations (the header persists), but each one
|
|
759
|
+
// swaps in a new page that needs pushing over too
|
|
760
|
+
const getElements = () => ({
|
|
761
|
+
elements: [document.getElementById('eventcatalog-application'), document.getElementById('eventcatalog-header')].filter(
|
|
762
|
+
Boolean
|
|
763
|
+
) as HTMLElement[],
|
|
764
|
+
docsSidebarEl: document.getElementById('eventcatalog-docs-sidebar'),
|
|
765
|
+
});
|
|
742
766
|
|
|
743
767
|
const desktop = window.matchMedia('(min-width: 1024px)');
|
|
744
768
|
const updateLayout = () => {
|
|
745
769
|
const pushContent = desktop.matches && isOpen && !isFullscreen;
|
|
770
|
+
const { elements, docsSidebarEl } = getElements();
|
|
746
771
|
elements.forEach((el) => {
|
|
747
772
|
if (!el.style.transition) {
|
|
748
773
|
el.style.transition = 'padding-right 420ms cubic-bezier(0.16, 1, 0.3, 1)';
|
|
@@ -753,10 +778,14 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
753
778
|
};
|
|
754
779
|
updateLayout();
|
|
755
780
|
desktop.addEventListener('change', updateLayout);
|
|
781
|
+
// The new page is in place but not yet shown, so it opens already pushed over
|
|
782
|
+
document.addEventListener('astro:after-swap', updateLayout);
|
|
756
783
|
|
|
757
784
|
// Cleanup on unmount
|
|
758
785
|
return () => {
|
|
759
786
|
desktop.removeEventListener('change', updateLayout);
|
|
787
|
+
document.removeEventListener('astro:after-swap', updateLayout);
|
|
788
|
+
const { elements, docsSidebarEl } = getElements();
|
|
760
789
|
elements.forEach((el) => {
|
|
761
790
|
el.style.paddingRight = '0';
|
|
762
791
|
});
|
|
@@ -771,6 +800,7 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
771
800
|
(text: string) => {
|
|
772
801
|
if (!text.trim() || isLoading) return;
|
|
773
802
|
setInputValue('');
|
|
803
|
+
followLatest.current = true;
|
|
774
804
|
if (!configured) {
|
|
775
805
|
setMessages((previous) => [...previous, { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text }] }]);
|
|
776
806
|
setIsSetupThinking(true);
|
|
@@ -1075,8 +1105,6 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
1075
1105
|
</div>
|
|
1076
1106
|
</div>
|
|
1077
1107
|
))}
|
|
1078
|
-
|
|
1079
|
-
<div ref={messagesEndRef} />
|
|
1080
1108
|
</div>
|
|
1081
1109
|
)}
|
|
1082
1110
|
</div>
|
|
@@ -1265,7 +1293,7 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
1265
1293
|
</div>
|
|
1266
1294
|
|
|
1267
1295
|
{/* Modal Content */}
|
|
1268
|
-
<div className="flex-1 overflow-y-auto px-6 py-6">
|
|
1296
|
+
<div ref={modalMessagesContainerRef} onScroll={handleScroll} className="flex-1 overflow-y-auto px-6 py-6">
|
|
1269
1297
|
{!hasMessages ? (
|
|
1270
1298
|
/* Welcome area - Clean design */
|
|
1271
1299
|
<div className="flex flex-col h-full max-w-2xl mx-auto">
|
|
@@ -1383,8 +1411,6 @@ const ChatPanel = ({ isOpen, onClose, configured = false }: ChatPanelProps) => {
|
|
|
1383
1411
|
</div>
|
|
1384
1412
|
</div>
|
|
1385
1413
|
))}
|
|
1386
|
-
|
|
1387
|
-
<div ref={modalMessagesEndRef} />
|
|
1388
1414
|
</div>
|
|
1389
1415
|
)}
|
|
1390
1416
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildUrl } from '@utils/url-builder';
|
|
2
2
|
|
|
3
|
-
export type SpecificationType = 'openapi' | 'asyncapi' | 'graphql';
|
|
3
|
+
export type SpecificationType = 'openapi' | 'asyncapi' | 'graphql' | 'generic';
|
|
4
4
|
|
|
5
5
|
export interface Specification {
|
|
6
6
|
type: SpecificationType;
|
|
@@ -18,6 +18,8 @@ export const getSpecUrl = (spec: Specification, serviceId: string, serviceVersio
|
|
|
18
18
|
return buildUrl(`/docs/services/${serviceId}/${serviceVersion}/asyncapi/${spec.filenameWithoutExtension}`);
|
|
19
19
|
case 'graphql':
|
|
20
20
|
return buildUrl(`/docs/services/${serviceId}/${serviceVersion}/graphql/${spec.filenameWithoutExtension}`);
|
|
21
|
+
case 'generic':
|
|
22
|
+
return buildUrl(`/docs/services/${serviceId}/${serviceVersion}/generic/${spec.filenameWithoutExtension}`);
|
|
21
23
|
default:
|
|
22
24
|
return '#';
|
|
23
25
|
}
|
|
@@ -31,6 +33,8 @@ export const getSpecIcon = (type: string): string => {
|
|
|
31
33
|
return 'asyncapi';
|
|
32
34
|
case 'graphql':
|
|
33
35
|
return 'graphql';
|
|
36
|
+
case 'generic':
|
|
37
|
+
return 'specification';
|
|
34
38
|
default:
|
|
35
39
|
return 'json-schema';
|
|
36
40
|
}
|
|
@@ -44,6 +48,8 @@ export const getSpecLabel = (type: string): string => {
|
|
|
44
48
|
return 'AsyncAPI';
|
|
45
49
|
case 'graphql':
|
|
46
50
|
return 'GraphQL';
|
|
51
|
+
case 'generic':
|
|
52
|
+
return 'Specification';
|
|
47
53
|
default:
|
|
48
54
|
return type;
|
|
49
55
|
}
|
|
@@ -390,7 +390,7 @@ const tooltipId = `ref-tooltip-${Math.random().toString(36).slice(2, 9)}`;
|
|
|
390
390
|
href={getSpecUrl(spec, resourceId, resource?.data?.version)}
|
|
391
391
|
class="hover:underline hover:text-[rgb(var(--ec-accent))]"
|
|
392
392
|
>
|
|
393
|
-
{getSpecLabel(spec.type)}
|
|
393
|
+
{spec.type === 'generic' && spec.name ? spec.name : getSpecLabel(spec.type)}
|
|
394
394
|
</a>
|
|
395
395
|
{idx < specifications.length - 1 && ', '}
|
|
396
396
|
</>
|