@ahmedramez-org/nexus-chat 1.0.0-beta.1 → 1.0.0-beta.10
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/README.md +139 -43
- package/contract/conformance.js +6 -4
- package/contract/index.d.ts +54 -4
- package/contract/index.js +12 -0
- package/index.d.ts +23 -3
- package/index.js +238 -14
- package/package.json +19 -4
- package/react.d.ts +75 -17
- package/react.js +245 -42
- package/window.js +381 -0
- package/remote/assets/AgentOptionControl-BnqfB5W3.js +0 -1
- package/remote/assets/CsvPreview-DD2VELWa.js +0 -10
- package/remote/assets/ErrorCard-Bu_0QAJ9.js +0 -1
- package/remote/assets/HighlightedCode-DpxGYHfF.js +0 -1
- package/remote/assets/HistorySearchDialog-q4cvx5jy.js +0 -41
- package/remote/assets/HostBridgeProvider-BV-UGU3Q.js +0 -1
- package/remote/assets/HtmlArtifactRenderer-EO_cwaEm.js +0 -1
- package/remote/assets/I18nContext-BlQKoDmv.js +0 -24
- package/remote/assets/PreviewState-VcIZyCXJ.js +0 -1
- package/remote/assets/SourceCitations-DvApeC9N.js +0 -1
- package/remote/assets/SourcesArtifactRenderer-BqnIOctm.js +0 -1
- package/remote/assets/TableArtifactRenderer-BLZLQMxU.js +0 -2
- package/remote/assets/TiptapComposerInput-CmxEiBlE.js +0 -132
- package/remote/assets/UnsupportedArtifact-CeOCu_J6.js +0 -1
- package/remote/assets/_virtual_mf-localSharedImportMap___mfe_internal__nexusChat__mf_owner__1-CObPUVU6.js +0 -1
- package/remote/assets/ar-BV81JAir.js +0 -1
- package/remote/assets/button-CO0tH6F2.js +0 -1
- package/remote/assets/client-Bv7EbCrM.js +0 -2
- package/remote/assets/composite-giRhJC3d.js +0 -1
- package/remote/assets/conformance-entry-uotihsU2.js +0 -2
- package/remote/assets/createLucideIcon-D1A0JYKN.js +0 -1
- package/remote/assets/de-BXJlhmC2.js +0 -1
- package/remote/assets/defineProperty-BbfpZ9Tg.js +0 -1
- package/remote/assets/dist-MaNcI-Om.js +0 -4
- package/remote/assets/errorReporter-hCgThzof.js +0 -1
- package/remote/assets/es-nnw68YFM.js +0 -1
- package/remote/assets/federation-entry-B8pojL73.css +0 -2
- package/remote/assets/federation-entry-DL02k5Y5.js +0 -65
- package/remote/assets/floating-ui.dom-B99QSfPH.js +0 -1
- package/remote/assets/floating-ui.utils.dom-ObnfY4F6.js +0 -1
- package/remote/assets/fr-CJIAbbzn.js +0 -1
- package/remote/assets/highlighter-C37NKSjx.js +0 -19
- package/remote/assets/hostInit-YMtJ2r-g.js +0 -1
- package/remote/assets/it-D3TiEKfy.js +0 -1
- package/remote/assets/ja-ZB0L4Lsc.js +0 -1
- package/remote/assets/jsx-runtime-DE3RlOCf.js +0 -1
- package/remote/assets/lib-CjT9YRPC.js +0 -1
- package/remote/assets/modelProviderIcons-fiyx4SaW.js +0 -1
- package/remote/assets/react-dom-FxOPXJ08.js +0 -1
- package/remote/assets/rehype-raw-CNOzW4vu.js +0 -2
- package/remote/assets/roboto-latin-400-italic-CSuqwwKr.woff2 +0 -0
- package/remote/assets/roboto-latin-400-normal-BqEyEoaF.woff2 +0 -0
- package/remote/assets/roboto-latin-500-italic-DOe3GFcv.woff2 +0 -0
- package/remote/assets/roboto-latin-500-normal-7RbcRiD8.woff2 +0 -0
- package/remote/assets/roboto-latin-700-italic-Cb9agdGy.woff2 +0 -0
- package/remote/assets/roboto-latin-700-normal-BZpUvMxY.woff2 +0 -0
- package/remote/assets/roboto-latin-ext-400-italic-BilqUbDw.woff2 +0 -0
- package/remote/assets/roboto-latin-ext-400-normal-C3tdtHj3.woff2 +0 -0
- package/remote/assets/roboto-latin-ext-500-italic-B5teBpxj.woff2 +0 -0
- package/remote/assets/roboto-latin-ext-500-normal-pMCM9Ixg.woff2 +0 -0
- package/remote/assets/roboto-latin-ext-700-italic-CBdFfgzf.woff2 +0 -0
- package/remote/assets/roboto-latin-ext-700-normal-DSBUz0N1.woff2 +0 -0
- package/remote/assets/rolldown-runtime-hePW80VL.js +0 -1
- package/remote/assets/sessionTitle-C9LhAN0r.js +0 -1
- package/remote/assets/spinner-eqRx6Fq-.js +0 -1
- package/remote/assets/sqlFormatting-BjJIfUN_.js +0 -7
- package/remote/assets/src-CIHHGjAO.js +0 -1
- package/remote/assets/store-FaSYSP4_.js +0 -1
- package/remote/assets/useBaseUiId-Dpqq0brK.js +0 -1
- package/remote/assets/usePositioner-BhCjFmeW.js +0 -1
- package/remote/assets/virtualExposes-DgWzju8R.js +0 -1
- package/remote/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__nexusChat__remoteEntry_js-BCqsZDEx.js +0 -1
- package/remote/assets/virtual_mf-exposes-ssr___mfe_internal__nexusChat__remoteEntry_js-nszk7xTq.js +0 -2
- package/remote/assets/virtual_mf-exposes___mfe_internal__nexusChat__remoteEntry_js-BkoNn4Ee.js +0 -2
- package/remote/assets/vite-preload-helper-B7qeedMF.js +0 -1
- package/remote/assets/zh-De2Ilp0M.js +0 -1
- package/remote/remoteEntry.js +0 -1
- package/remote/remoteEntry.ssr.js +0 -1
package/README.md
CHANGED
|
@@ -1,39 +1,127 @@
|
|
|
1
1
|
# @incorta/nexus-chat
|
|
2
2
|
|
|
3
|
-
NexusChat, the IncortaCopilot chat UI, for any app on any framework
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
| `@incorta/nexus-chat` | the connector (`mountNexusChat`, `loadNexusChat`) and types |
|
|
8
|
-
| `@incorta/nexus-chat/react` | `<NexusChat />` for React 18 and 19 apps |
|
|
9
|
-
| `@incorta/nexus-chat/contract` | the host contract's values (`NEXUS_CHAT_CONTRACT_VERSION`…) |
|
|
10
|
-
| `@incorta/nexus-chat/conformance` | checks a host bridge against the contract |
|
|
11
|
-
| `@incorta/nexus-chat/host` | host helpers (`createElementRootAdapter`…) |
|
|
12
|
-
| `node_modules/@incorta/nexus-chat/remote/` | the prebuilt chat, for apps that serve a pinned copy |
|
|
13
|
-
|
|
14
|
-
The chat always runs as that prebuilt Module Federation bundle with its own React and isolated
|
|
3
|
+
NexusChat, the IncortaCopilot chat UI, for any app on any framework: one `<script>` tag for any
|
|
4
|
+
site, `<NexusChat />` for React, `mountNexusChat()` for everything else.
|
|
5
|
+
|
|
6
|
+
The chat always runs as a prebuilt Module Federation bundle with its own React and isolated
|
|
15
7
|
styles, never re-compiled by the app's bundler, so it never collides with the app's React, its
|
|
16
|
-
dependencies or its CSS.
|
|
17
|
-
|
|
8
|
+
dependencies or its CSS. The connector adds about 6 KB gzipped to an app (half of it the Incorta
|
|
9
|
+
Intelligence mark its loading skeleton draws), and the React component about 7 KB more. The chat
|
|
10
|
+
build itself is not in this package: Copilot serves it, and each GitHub Release attaches it as
|
|
11
|
+
`nexus-chat-dist.tar.gz`.
|
|
12
|
+
|
|
13
|
+
## Any site: one script
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<script src="https://<incorta>/incorta/copilot/nexus-chat/widget.js" defer></script>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Incorta serves this script next to `embed.html`; it is not in this package and needs no build. It
|
|
20
|
+
draws the Incorta Intelligence launcher at the bottom right and opens the embedded chat (below)
|
|
21
|
+
floating, docked or full screen, on the user's Incorta session, so the page holds no token. It is
|
|
22
|
+
one classic script with no imports, so a page on any origin loads it without CORS.
|
|
23
|
+
|
|
24
|
+
Optional `data-` attributes set it up: `data-theme="dark"`, `data-modes="floating,docked"` (the
|
|
25
|
+
windows allowed, in that order; default all three), `data-default-mode="docked"` (open on load;
|
|
26
|
+
default only the launcher) and `data-locale="fr"`. The page drives it afterwards through
|
|
27
|
+
`window.NexusChatWidget`: `open()`, `close()`, `setMode('docked')`, `setTheme('dark')`.
|
|
18
28
|
|
|
19
29
|
## React
|
|
20
30
|
|
|
31
|
+
Two ways in: **direct**, where the chat mounts into the page and shares its session (Incorta's own
|
|
32
|
+
UIs), and **embedded**, where it runs in an iframe served by Incorta on the user's Incorta session,
|
|
33
|
+
so the app never holds a token (generated apps, other sites).
|
|
34
|
+
|
|
35
|
+
### Embedded
|
|
36
|
+
|
|
21
37
|
```tsx
|
|
22
38
|
import { NexusChat } from '@incorta/nexus-chat/react';
|
|
23
39
|
|
|
40
|
+
<NexusChat embedUrl={`${incortaUrl}/copilot/nexus-chat/embed.html`} theme="dark" />;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
It draws its own window: an Incorta Intelligence launcher at the bottom right opens the chat
|
|
44
|
+
floating (a card lifted off the page, opening out of the launcher, which stays under it to hide
|
|
45
|
+
it) or docked flush on the right (the page makes room). The chat's own top bar switches between
|
|
46
|
+
the windows (Float, Dock to the side, Enter full screen: every allowed one but the current) and
|
|
47
|
+
closes it back to the launcher; nothing sits above the chat. `modes` limits the windows (default
|
|
48
|
+
all three, in that order) and `defaultMode` opens one on load. Below 768px wide the chat covers
|
|
49
|
+
the page. The chat is drawn smaller in a small window, and a skeleton holds the window while the
|
|
50
|
+
chat loads. `theme` is followed live; `locale` is read when the chat first opens.
|
|
51
|
+
|
|
52
|
+
The Incorta cluster needs `iframe.inclusion.enabled` (and `frame-ancestors` set to the app's
|
|
53
|
+
domains). Where the browser keeps the Incorta session from the iframe (Safari, Firefox, private
|
|
54
|
+
windows), the chat shows Continue with Incorta: the browser asks the user to allow it once, and
|
|
55
|
+
the chat starts on their Incorta session. A user not signed in to Incorta signs in through a small
|
|
56
|
+
Incorta window that closes itself afterwards. No browser setting is needed.
|
|
57
|
+
|
|
58
|
+
While the user clicks or types in the app, the component tells the chat at most every ten seconds,
|
|
59
|
+
so Incorta's own idle timeout does not end the chat's session while the app is in use. A page that
|
|
60
|
+
places the iframe itself can do the same: `iframe.contentWindow.postMessage({ nexusChat: 'active' },
|
|
61
|
+
'<incorta origin>')`. If the session ends anyway, the chat shows Sign in to Incorta.
|
|
62
|
+
|
|
63
|
+
### Direct
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// Copilot answers 400 without the signed-in user's token: read it from Incorta's
|
|
67
|
+
// session, and read it again after a 401 or 403.
|
|
68
|
+
let session;
|
|
69
|
+
const incortaHeaders = () =>
|
|
70
|
+
(session ??= fetch('/incorta/service/user/isLoggedIn', { credentials: 'include' }).then(
|
|
71
|
+
async (response) => ({
|
|
72
|
+
Authorization: `Bearer ${(await response.json()).accessToken}`,
|
|
73
|
+
'X-XSRF-TOKEN': response.headers.get('X-XSRF-TOKEN') ?? '',
|
|
74
|
+
}),
|
|
75
|
+
));
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
24
79
|
<NexusChat
|
|
25
|
-
connection={{
|
|
80
|
+
connection={{
|
|
81
|
+
client: 'general',
|
|
82
|
+
copilotUrl: '/incorta/copilot',
|
|
83
|
+
credentials: 'include',
|
|
84
|
+
headers: incortaHeaders, // read before each request
|
|
85
|
+
refreshAuth: async () => ((session = undefined), true), // after a 401 or 403: read again, retry
|
|
86
|
+
}}
|
|
26
87
|
theme="dark"
|
|
27
88
|
user={{ id: user.sub, name: user.name }}
|
|
28
89
|
onLoadError={(error) => setError(error.message)}
|
|
29
|
-
|
|
90
|
+
/>
|
|
30
91
|
```
|
|
31
92
|
|
|
32
|
-
The chat fills the component's box.
|
|
33
|
-
`tenantId`, `user` and `window` update the running chat;
|
|
34
|
-
`instanceId`, `assetsUrl` and `timeoutMs` are read once
|
|
35
|
-
`setDocked`, `close`) hands the chat's window to the app:
|
|
36
|
-
|
|
93
|
+
The chat fills the component's box; the app draws any frame around it. Until the chat is mounted, a skeleton holds the box (the embedded window's loading face: the Incorta Intelligence mark and two lines, after 300 ms), and it fades once the chat mounts, or leaves at once when `onLoadError` fires, so the app's own failure takes the box. `theme`, `locale` (default:
|
|
94
|
+
the browser's), `direction`, `tenantId`, `user` and `window` update the running chat;
|
|
95
|
+
`connection`, `chat`, `actions`, `events`, `instanceId`, `assetsUrl` and `timeoutMs` are read once
|
|
96
|
+
at mount. `window` (`docked`, `canDock`, `setDocked`, `close`) hands the chat's window to the app:
|
|
97
|
+
the chat shows a dock toggle and a close button; with `mode`, `modes` and `setMode` (read at
|
|
98
|
+
mount) it shows a button per other mode instead of the toggle. An app that already builds the contract's
|
|
99
|
+
`environment` or `window` passes it as is. `onReady` receives the handle (`ask`, `attach`), and
|
|
100
|
+
`null` once the chat unmounts; `loadedNexusChatVersion()` names the loaded build for error reports.
|
|
101
|
+
|
|
102
|
+
### Direct, in a window
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<NexusChat
|
|
106
|
+
connection={{
|
|
107
|
+
client: 'general',
|
|
108
|
+
copilotUrl: '/incorta/copilot',
|
|
109
|
+
credentials: 'include',
|
|
110
|
+
headers: incortaHeaders, // as above
|
|
111
|
+
refreshAuth: async () => ((session = undefined), true),
|
|
112
|
+
}}
|
|
113
|
+
theme="dark"
|
|
114
|
+
modes={['floating', 'docked', 'fullscreen']}
|
|
115
|
+
defaultMode="floating"
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`modes` or `defaultMode` instead of `window` gives the direct chat the embedded window: the
|
|
120
|
+
launcher, floating, docked (the page makes room) and full-screen windows, full screen below 768px.
|
|
121
|
+
The chat's own top bar switches windows and closes it to the launcher, where the conversation
|
|
122
|
+
stays. `modes` limits the windows (default all three, in that order); `defaultMode` opens one on
|
|
123
|
+
load, else only the launcher shows. The other props are the direct ones, and an app passing
|
|
124
|
+
`window` keeps drawing its own frame.
|
|
37
125
|
|
|
38
126
|
## Any framework
|
|
39
127
|
|
|
@@ -52,38 +140,46 @@ chat.unmount();
|
|
|
52
140
|
```
|
|
53
141
|
|
|
54
142
|
The options are the host contract: `connection` and `environment` are required; `chat`, `actions`,
|
|
55
|
-
`events` and `window` are optional, and the chat hides what a host leaves out.
|
|
56
|
-
|
|
143
|
+
`events` and `window` are optional, and the chat hides what a host leaves out. The chat fills
|
|
144
|
+
`element`. Until it is mounted the React component's skeleton holds the element (after 300 ms, in
|
|
145
|
+
the `environment`'s theme), and it leaves at once if the chat cannot load, so the app's own failure
|
|
146
|
+
takes its place. Mount in the browser only, never during SSR.
|
|
147
|
+
|
|
148
|
+
`chat={{ contexts: ['business-view', 'ai-container', 'file'] }}` lets users attach contexts, in
|
|
149
|
+
that menu order: `'file'` uploads, and each other built-in kind (`business-view`, `physical-table`,
|
|
150
|
+
`ai-container`, `knowledge-store`) is loaded by the chat from Incorta through
|
|
151
|
+
`connection.incortaApi` (for example, `{ type: 'bff', baseUrl: '/incorta/bff/v1' }`). Without
|
|
152
|
+
`incortaApi`, a bare kind is left out with a console warning; the public API has no Knowledge
|
|
153
|
+
Stores. A `ContextSource` of a kind replaces the chat's loader for it.
|
|
57
154
|
|
|
58
155
|
`loadNexusChat(source)` only loads the chat and resolves to its `mount`. It loads once per source,
|
|
59
|
-
retries once, and gives up after `timeoutMs` (default 20 s
|
|
156
|
+
retries once, and gives up after `timeoutMs` (default 20 s, counted while the page is visible: a
|
|
157
|
+
hidden tab gets a fresh deadline once it shows); a failed load is forgotten so the next
|
|
60
158
|
call tries again. Call it when the page is idle to make the first open instant.
|
|
61
159
|
|
|
62
160
|
## Where the chat comes from
|
|
63
161
|
|
|
64
|
-
| Mode
|
|
65
|
-
|
|
|
66
|
-
| Always updated (default)
|
|
67
|
-
| Pinned
|
|
68
|
-
| Pinned, served from a CDN | `assetsUrl: 'https://cdn.jsdelivr.net/npm/@incorta/nexus-chat@1.4.2/remote/'` | the version in the URL | none; the page's CSP must allow that origin |
|
|
162
|
+
| Mode | Options | UI version | App setup |
|
|
163
|
+
| ------------------------ | ---------------------------- | ------------------------- | ---------------------------------------------------------------------- |
|
|
164
|
+
| Always updated (default) | `connection.copilotUrl` only | what that Copilot runs | none: Copilot serves `<copilotUrl>/nexus-chat/` |
|
|
165
|
+
| Pinned | `assetsUrl: '/nexus-chat/'` | the release you extracted | serve that release's `nexus-chat-dist.tar.gz`, extracted, at that path |
|
|
69
166
|
|
|
70
|
-
|
|
167
|
+
A pinned copy serves `remoteEntry.js` with revalidation (`no-cache`) and `assets/` as immutable.
|
|
71
168
|
|
|
72
|
-
|
|
73
|
-
import { createRequire } from 'node:module';
|
|
74
|
-
import path from 'node:path';
|
|
75
|
-
|
|
76
|
-
const remoteDir = path.join(
|
|
77
|
-
path.dirname(createRequire(import.meta.url).resolve('@incorta/nexus-chat/package.json')),
|
|
78
|
-
'remote',
|
|
79
|
-
);
|
|
80
|
-
// serve remoteDir at /nexus-chat/: remoteEntry.js revalidates (no-cache), assets/ is immutable
|
|
81
|
-
```
|
|
169
|
+
A pinned UI keeps talking to a Copilot that keeps moving; the default mode keeps the two in step.
|
|
82
170
|
|
|
83
|
-
|
|
84
|
-
`"postinstall": "cp -R node_modules/@incorta/nexus-chat/remote public/nexus-chat"`.
|
|
171
|
+
## Everything in the package
|
|
85
172
|
|
|
86
|
-
|
|
173
|
+
React and other apps need only the first two imports; the rest serve hosts that federate the chat
|
|
174
|
+
themselves (web-react, Hikari).
|
|
175
|
+
|
|
176
|
+
| Import | What |
|
|
177
|
+
| --------------------------------- | ----------------------------------------------------------- |
|
|
178
|
+
| `@incorta/nexus-chat` | the connector (`mountNexusChat`, `loadNexusChat`) and types |
|
|
179
|
+
| `@incorta/nexus-chat/react` | `<NexusChat />` for React 18 and 19 apps |
|
|
180
|
+
| `@incorta/nexus-chat/contract` | the host contract's values (`NEXUS_CHAT_CONTRACT_VERSION`…) |
|
|
181
|
+
| `@incorta/nexus-chat/conformance` | checks a host bridge against the contract |
|
|
182
|
+
| `@incorta/nexus-chat/host` | host helpers (`createElementRootAdapter`…) |
|
|
87
183
|
|
|
88
184
|
## Versions
|
|
89
185
|
|
package/contract/conformance.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NEXUS_CHAT_CONTRACT_VERSION, } from './index.js';
|
|
1
|
+
import { BUILT_IN_CONTEXT_KINDS, NEXUS_CHAT_CONTRACT_VERSION, } from './index.js';
|
|
2
2
|
class CheckCollector {
|
|
3
3
|
results = [];
|
|
4
4
|
async run(id, title, check) {
|
|
@@ -255,15 +255,17 @@ export async function checkHostContract(options, checkOptions = {}) {
|
|
|
255
255
|
ensure(notifications === before, 'unsubscribe did not detach the listener.');
|
|
256
256
|
}
|
|
257
257
|
});
|
|
258
|
-
await checks.run('chat.contexts', 'each context is
|
|
258
|
+
await checks.run('chat.contexts', 'each context is a built-in kind or a source with a loader', () => {
|
|
259
259
|
const kinds = (chat?.contexts ?? []).map((spec) => {
|
|
260
|
-
if (spec === '
|
|
260
|
+
if (typeof spec === 'string') {
|
|
261
|
+
ensure(BUILT_IN_CONTEXT_KINDS.includes(spec), `unknown context ${JSON.stringify(spec)}; a bare string is one of the built-in kinds: ${BUILT_IN_CONTEXT_KINDS.join(', ')}.`);
|
|
261
262
|
return spec;
|
|
263
|
+
}
|
|
262
264
|
ensure(CONTEXT_KINDS.includes(spec.kind), `unknown context kind ${JSON.stringify(spec.kind)}.`);
|
|
263
265
|
ensure(typeof spec.load === 'function', `the ${spec.kind} source has no load().`);
|
|
264
266
|
return spec.kind;
|
|
265
267
|
});
|
|
266
|
-
ensure(new Set(kinds).size === kinds.length, 'a context kind is listed twice.');
|
|
268
|
+
ensure(new Set(kinds).size === kinds.length, 'a context kind is listed twice (a source replaces its bare kind: list only the source).');
|
|
267
269
|
});
|
|
268
270
|
await checks.run('chat.triggers', 'each trigger is @ or # with a search', () => {
|
|
269
271
|
const chars = (chat?.triggers ?? []).map((trigger) => {
|
package/contract/index.d.ts
CHANGED
|
@@ -22,6 +22,17 @@ export interface NexusChatConnection {
|
|
|
22
22
|
* Absent: the failure surfaces to the user.
|
|
23
23
|
*/
|
|
24
24
|
refreshAuth?(): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* How the chat reaches the Incorta API for built-in kinds other than `'file'` in
|
|
27
|
+
* `chat.contexts`, under the headers, credentials and `refreshAuth` above.
|
|
28
|
+
* - `'bff'`: Incorta's internal BFF (web-react: `baseUrl: '/incorta/bff/v1'`;
|
|
29
|
+
* the session headers carry the user).
|
|
30
|
+
* - `'public'`: the public API v2, rooted at `/api/v2/{tenant}`.
|
|
31
|
+
*/
|
|
32
|
+
incortaApi?: {
|
|
33
|
+
type: 'bff' | 'public';
|
|
34
|
+
baseUrl: string;
|
|
35
|
+
};
|
|
25
36
|
/** Poll tuning; hosts normally omit both (defaults 1500 ms / 1000 ms). */
|
|
26
37
|
pollIntervalMs?: number;
|
|
27
38
|
initialPollDelayMs?: number;
|
|
@@ -80,8 +91,34 @@ export interface ContextSource {
|
|
|
80
91
|
*/
|
|
81
92
|
resolve?(dataset: Readonly<Record<string, unknown>>, signal: AbortSignal): Promise<Readonly<Record<string, unknown>>>;
|
|
82
93
|
}
|
|
83
|
-
/**
|
|
84
|
-
|
|
94
|
+
/**
|
|
95
|
+
* The built-in kinds the chat handles itself: its upload browser and contexts
|
|
96
|
+
* loaded through `connection.incortaApi`. `'insight'` is not one: insights come
|
|
97
|
+
* from the dashboard the host has open, so only a host source can list them.
|
|
98
|
+
*/
|
|
99
|
+
export declare const BUILT_IN_CONTEXT_KINDS: readonly ["file", "business-view", "physical-table", "ai-container", "knowledge-store"];
|
|
100
|
+
export type BuiltInContextKind = (typeof BUILT_IN_CONTEXT_KINDS)[number];
|
|
101
|
+
/**
|
|
102
|
+
* One entry of the add-context menu; `chat.contexts` lists them in menu order.
|
|
103
|
+
*
|
|
104
|
+
* - `'file'`: the chat's own upload browser.
|
|
105
|
+
* - Another built-in kind (`'business-view'`, …): the chat loads it from
|
|
106
|
+
* `connection.incortaApi`. Without `incortaApi`, or for a kind its API does not
|
|
107
|
+
* offer (`'knowledge-store'` has no public API), the kind is left out of
|
|
108
|
+
* the menu with a console warning.
|
|
109
|
+
* - A `ContextSource`: the host loads the rows. A source of a built-in kind
|
|
110
|
+
* replaces the chat's loader for that kind.
|
|
111
|
+
*
|
|
112
|
+
* Name each kind once. A host that lists a bare kind and a source of the same
|
|
113
|
+
* kind fails conformance; the chat still shows the kind once, as the host's
|
|
114
|
+
* source, in the source's place. Any other string is dropped.
|
|
115
|
+
*
|
|
116
|
+
* Migrating: code that told a source apart with `spec === 'file'` no longer
|
|
117
|
+
* compiles, since a bare string may now be a kind; narrow with
|
|
118
|
+
* `typeof spec === 'string'`. Pass bare kinds only to a chat that has them
|
|
119
|
+
* (this release or later): an older chat cannot render them.
|
|
120
|
+
*/
|
|
121
|
+
export type ContextSpec = BuiltInContextKind | ContextSource;
|
|
85
122
|
export interface ColumnSummary {
|
|
86
123
|
id: string;
|
|
87
124
|
name: string;
|
|
@@ -346,16 +383,27 @@ export interface NexusChatOptions {
|
|
|
346
383
|
* The host sizes the mount and owns its window (a page-covering overlay, a
|
|
347
384
|
* docked side panel) and draws its frame. Present: the chat hides its own
|
|
348
385
|
* fullscreen toggle and its edge border, and shows a dock toggle while
|
|
349
|
-
* `canDock
|
|
350
|
-
* and
|
|
386
|
+
* `canDock` (or, with `mode`, `modes` and `setMode`, one button per other
|
|
387
|
+
* mode), and a close button. Absent: the chat's own fullscreen toggle, and
|
|
388
|
+
* no close button.
|
|
351
389
|
*/
|
|
352
390
|
window?: NexusChatWindow;
|
|
353
391
|
}
|
|
392
|
+
/** The windows a host can show the chat in. */
|
|
393
|
+
export type NexusChatWindowMode = 'floating' | 'docked' | 'fullscreen';
|
|
354
394
|
export interface NexusChatWindowState {
|
|
355
395
|
/** The mount is the docked side panel (the toggle shows pressed). */
|
|
356
396
|
docked: boolean;
|
|
357
397
|
/** The host page allows docking now; `false` hides the dock toggle. */
|
|
358
398
|
canDock: boolean;
|
|
399
|
+
/**
|
|
400
|
+
* The window shown now. With `modes` and `setMode`, the header offers every
|
|
401
|
+
* mode in `modes` but this one (float, dock, full screen, in that order)
|
|
402
|
+
* instead of the dock toggle.
|
|
403
|
+
*/
|
|
404
|
+
mode?: NexusChatWindowMode;
|
|
405
|
+
/** The modes the host can switch to now, the current one included. */
|
|
406
|
+
modes?: readonly NexusChatWindowMode[];
|
|
359
407
|
}
|
|
360
408
|
export interface NexusChatWindow {
|
|
361
409
|
/** May return a fresh object on every call: the chat compares the fields. */
|
|
@@ -363,6 +411,8 @@ export interface NexusChatWindow {
|
|
|
363
411
|
/** Call `onChange` whenever `get` may return something different. */
|
|
364
412
|
subscribe(onChange: () => void): () => void;
|
|
365
413
|
setDocked(docked: boolean): void;
|
|
414
|
+
/** Shows the chat in another of `modes`; the conversation and draft stay. */
|
|
415
|
+
setMode?(mode: NexusChatWindowMode): void;
|
|
366
416
|
close(): void;
|
|
367
417
|
}
|
|
368
418
|
/** What `mount` returns: the host's handle on one running chat. */
|
package/contract/index.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
export const NEXUS_CHAT_CONTRACT_VERSION = 2;
|
|
2
|
+
/**
|
|
3
|
+
* The built-in kinds the chat handles itself: its upload browser and contexts
|
|
4
|
+
* loaded through `connection.incortaApi`. `'insight'` is not one: insights come
|
|
5
|
+
* from the dashboard the host has open, so only a host source can list them.
|
|
6
|
+
*/
|
|
7
|
+
export const BUILT_IN_CONTEXT_KINDS = [
|
|
8
|
+
'file',
|
|
9
|
+
'business-view',
|
|
10
|
+
'physical-table',
|
|
11
|
+
'ai-container',
|
|
12
|
+
'knowledge-store',
|
|
13
|
+
];
|
|
2
14
|
/**
|
|
3
15
|
* Reads the namespaced-kind convention. Returns null for bare legacy kinds.
|
|
4
16
|
* Framework-free on purpose: hosts use this inside `canRender` to route by
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
HostTheme,
|
|
3
|
+
NexusChatHandle,
|
|
4
|
+
NexusChatMount,
|
|
5
|
+
NexusChatOptions,
|
|
6
|
+
} from './contract/index.js';
|
|
2
7
|
|
|
3
8
|
export type * from './contract/index.js';
|
|
4
9
|
|
|
@@ -6,8 +11,8 @@ export interface NexusChatSource {
|
|
|
6
11
|
/** IncortaCopilot's root as this page reaches it; the chat loads from `<copilotUrl>/nexus-chat/`. */
|
|
7
12
|
copilotUrl?: string;
|
|
8
13
|
/**
|
|
9
|
-
* Where the chat files are served instead (pinned):
|
|
10
|
-
*
|
|
14
|
+
* Where the chat files are served instead (pinned): any URL that serves a
|
|
15
|
+
* release's extracted `nexus-chat-dist.tar.gz`. Absent: from Copilot.
|
|
11
16
|
*/
|
|
12
17
|
assetsUrl?: string;
|
|
13
18
|
/** Deadline for loading the chat, retry included. Default 20 000 ms. */
|
|
@@ -24,8 +29,23 @@ export const CONTRACT_VERSION: 2;
|
|
|
24
29
|
/** Loads the chat once per source (one retry, one deadline) and resolves to its `mount`. */
|
|
25
30
|
export function loadNexusChat(source: NexusChatSource): Promise<NexusChatMount>;
|
|
26
31
|
|
|
32
|
+
/** Forgets a loaded chat, so the next load fetches it again (after it failed while running). */
|
|
33
|
+
export function forgetNexusChat(source: NexusChatSource): void;
|
|
34
|
+
|
|
35
|
+
/** The version the last loaded chat reports (for error reports); undefined before a load. */
|
|
36
|
+
export function loadedNexusChatVersion(): string | undefined;
|
|
37
|
+
|
|
27
38
|
/** Loads the chat and mounts it into `element`. */
|
|
28
39
|
export function mountNexusChat(
|
|
29
40
|
element: HTMLElement,
|
|
30
41
|
options: MountNexusChatOptions,
|
|
31
42
|
): Promise<NexusChatHandle>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @internal The skeleton `mountNexusChat` and `<NexusChat>` draw over a box
|
|
46
|
+
* while the chat loads. Not part of the package's API.
|
|
47
|
+
*/
|
|
48
|
+
export function showNexusChatSkeleton(
|
|
49
|
+
box: HTMLElement,
|
|
50
|
+
theme?: HostTheme,
|
|
51
|
+
): { setTheme(theme?: HostTheme): void; done(): void; remove(): void };
|