@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.
Files changed (78) hide show
  1. package/README.md +139 -43
  2. package/contract/conformance.js +6 -4
  3. package/contract/index.d.ts +54 -4
  4. package/contract/index.js +12 -0
  5. package/index.d.ts +23 -3
  6. package/index.js +238 -14
  7. package/package.json +19 -4
  8. package/react.d.ts +75 -17
  9. package/react.js +245 -42
  10. package/window.js +381 -0
  11. package/remote/assets/AgentOptionControl-BnqfB5W3.js +0 -1
  12. package/remote/assets/CsvPreview-DD2VELWa.js +0 -10
  13. package/remote/assets/ErrorCard-Bu_0QAJ9.js +0 -1
  14. package/remote/assets/HighlightedCode-DpxGYHfF.js +0 -1
  15. package/remote/assets/HistorySearchDialog-q4cvx5jy.js +0 -41
  16. package/remote/assets/HostBridgeProvider-BV-UGU3Q.js +0 -1
  17. package/remote/assets/HtmlArtifactRenderer-EO_cwaEm.js +0 -1
  18. package/remote/assets/I18nContext-BlQKoDmv.js +0 -24
  19. package/remote/assets/PreviewState-VcIZyCXJ.js +0 -1
  20. package/remote/assets/SourceCitations-DvApeC9N.js +0 -1
  21. package/remote/assets/SourcesArtifactRenderer-BqnIOctm.js +0 -1
  22. package/remote/assets/TableArtifactRenderer-BLZLQMxU.js +0 -2
  23. package/remote/assets/TiptapComposerInput-CmxEiBlE.js +0 -132
  24. package/remote/assets/UnsupportedArtifact-CeOCu_J6.js +0 -1
  25. package/remote/assets/_virtual_mf-localSharedImportMap___mfe_internal__nexusChat__mf_owner__1-CObPUVU6.js +0 -1
  26. package/remote/assets/ar-BV81JAir.js +0 -1
  27. package/remote/assets/button-CO0tH6F2.js +0 -1
  28. package/remote/assets/client-Bv7EbCrM.js +0 -2
  29. package/remote/assets/composite-giRhJC3d.js +0 -1
  30. package/remote/assets/conformance-entry-uotihsU2.js +0 -2
  31. package/remote/assets/createLucideIcon-D1A0JYKN.js +0 -1
  32. package/remote/assets/de-BXJlhmC2.js +0 -1
  33. package/remote/assets/defineProperty-BbfpZ9Tg.js +0 -1
  34. package/remote/assets/dist-MaNcI-Om.js +0 -4
  35. package/remote/assets/errorReporter-hCgThzof.js +0 -1
  36. package/remote/assets/es-nnw68YFM.js +0 -1
  37. package/remote/assets/federation-entry-B8pojL73.css +0 -2
  38. package/remote/assets/federation-entry-DL02k5Y5.js +0 -65
  39. package/remote/assets/floating-ui.dom-B99QSfPH.js +0 -1
  40. package/remote/assets/floating-ui.utils.dom-ObnfY4F6.js +0 -1
  41. package/remote/assets/fr-CJIAbbzn.js +0 -1
  42. package/remote/assets/highlighter-C37NKSjx.js +0 -19
  43. package/remote/assets/hostInit-YMtJ2r-g.js +0 -1
  44. package/remote/assets/it-D3TiEKfy.js +0 -1
  45. package/remote/assets/ja-ZB0L4Lsc.js +0 -1
  46. package/remote/assets/jsx-runtime-DE3RlOCf.js +0 -1
  47. package/remote/assets/lib-CjT9YRPC.js +0 -1
  48. package/remote/assets/modelProviderIcons-fiyx4SaW.js +0 -1
  49. package/remote/assets/react-dom-FxOPXJ08.js +0 -1
  50. package/remote/assets/rehype-raw-CNOzW4vu.js +0 -2
  51. package/remote/assets/roboto-latin-400-italic-CSuqwwKr.woff2 +0 -0
  52. package/remote/assets/roboto-latin-400-normal-BqEyEoaF.woff2 +0 -0
  53. package/remote/assets/roboto-latin-500-italic-DOe3GFcv.woff2 +0 -0
  54. package/remote/assets/roboto-latin-500-normal-7RbcRiD8.woff2 +0 -0
  55. package/remote/assets/roboto-latin-700-italic-Cb9agdGy.woff2 +0 -0
  56. package/remote/assets/roboto-latin-700-normal-BZpUvMxY.woff2 +0 -0
  57. package/remote/assets/roboto-latin-ext-400-italic-BilqUbDw.woff2 +0 -0
  58. package/remote/assets/roboto-latin-ext-400-normal-C3tdtHj3.woff2 +0 -0
  59. package/remote/assets/roboto-latin-ext-500-italic-B5teBpxj.woff2 +0 -0
  60. package/remote/assets/roboto-latin-ext-500-normal-pMCM9Ixg.woff2 +0 -0
  61. package/remote/assets/roboto-latin-ext-700-italic-CBdFfgzf.woff2 +0 -0
  62. package/remote/assets/roboto-latin-ext-700-normal-DSBUz0N1.woff2 +0 -0
  63. package/remote/assets/rolldown-runtime-hePW80VL.js +0 -1
  64. package/remote/assets/sessionTitle-C9LhAN0r.js +0 -1
  65. package/remote/assets/spinner-eqRx6Fq-.js +0 -1
  66. package/remote/assets/sqlFormatting-BjJIfUN_.js +0 -7
  67. package/remote/assets/src-CIHHGjAO.js +0 -1
  68. package/remote/assets/store-FaSYSP4_.js +0 -1
  69. package/remote/assets/useBaseUiId-Dpqq0brK.js +0 -1
  70. package/remote/assets/usePositioner-BhCjFmeW.js +0 -1
  71. package/remote/assets/virtualExposes-DgWzju8R.js +0 -1
  72. package/remote/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__nexusChat__remoteEntry_js-BCqsZDEx.js +0 -1
  73. package/remote/assets/virtual_mf-exposes-ssr___mfe_internal__nexusChat__remoteEntry_js-nszk7xTq.js +0 -2
  74. package/remote/assets/virtual_mf-exposes___mfe_internal__nexusChat__remoteEntry_js-BkoNn4Ee.js +0 -2
  75. package/remote/assets/vite-preload-helper-B7qeedMF.js +0 -1
  76. package/remote/assets/zh-De2Ilp0M.js +0 -1
  77. package/remote/remoteEntry.js +0 -1
  78. 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. One package holds:
4
-
5
- | Import | What |
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. Importing the connector adds about 2 KB to an app; `remote/` stays on
17
- disk unless the app serves it.
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={{ client: 'general', copilotUrl: '/api/ai', credentials: 'same-origin' }}
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. `theme`, `locale` (default: the browser's), `direction`,
33
- `tenantId`, `user` and `window` update the running chat; `connection`, `chat`, `actions`, `events`,
34
- `instanceId`, `assetsUrl` and `timeoutMs` are read once at mount. `window` (`docked`, `canDock`,
35
- `setDocked`, `close`) hands the chat's window to the app: the chat shows a dock toggle and a close
36
- button. `onReady` receives the handle (`ask`, `attach`).
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. Mount in the browser
56
- only, never during SSR.
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); a failed load is forgotten so the next
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 | Options | UI version | App setup |
65
- | ------------------------- | ----------------------------------------------------------------------------- | ----------------------------- | ----------------------------------------------- |
66
- | Always updated (default) | `connection.copilotUrl` only | what that Copilot runs | none: Copilot serves `<copilotUrl>/nexus-chat/` |
67
- | Pinned, served by the app | `assetsUrl: '/nexus-chat/'` | the installed package version | serve this package's `remote/` at that path |
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
- An app with a server serves `remote/` straight from the installed package:
167
+ A pinned copy serves `remoteEntry.js` with revalidation (`no-cache`) and `assets/` as immutable.
71
168
 
72
- ```ts
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
- A static app copies it instead:
84
- `"postinstall": "cp -R node_modules/@incorta/nexus-chat/remote public/nexus-chat"`.
171
+ ## Everything in the package
85
172
 
86
- A pinned UI keeps talking to a Copilot that keeps moving; the default mode keeps the two in step.
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
 
@@ -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 "file" or a source with a loader', () => {
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 === 'file')
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) => {
@@ -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
- /** `'file'` is the chat's own upload browser; every other kind is a host source. */
84
- export type ContextSpec = 'file' | ContextSource;
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`, and a close button. Absent: the chat's own fullscreen toggle,
350
- * and no close button.
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 { NexusChatHandle, NexusChatMount, NexusChatOptions } from './contract/index.js';
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): the app's own copy of
10
- * this package's `remote/` folder, or a CDN URL. Absent: from Copilot.
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 };