@webority/ui-elements 0.29.0 → 0.31.0

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.
@@ -15140,15 +15140,45 @@ function na(e) {
15140
15140
  }
15141
15141
  ta ??= URL.createObjectURL(new Blob([Fi], { type: "text/javascript" })), jn.workerSrc = ta;
15142
15142
  }
15143
- var ra = (e) => {
15143
+ function ra(e) {
15144
+ let t = e ? e.replace(/\/?$/, "/") : null;
15145
+ return {
15146
+ isEvalSupported: !1,
15147
+ ...t && {
15148
+ cMapUrl: `${t}cmaps/`,
15149
+ cMapPacked: !0,
15150
+ standardFontDataUrl: `${t}standard_fonts/`,
15151
+ wasmUrl: `${t}wasm/`
15152
+ }
15153
+ };
15154
+ }
15155
+ async function ia(e, { workerSrc: t, assetBase: n, width: r = 640 } = {}) {
15156
+ na(t);
15157
+ let i = hr({
15158
+ ...e instanceof Blob ? { data: new Uint8Array(await e.arrayBuffer()) } : { url: e.url },
15159
+ ...ra(n)
15160
+ });
15161
+ try {
15162
+ let e = await (await i.promise).getPage(1), t = e.getViewport({ scale: r / e.getViewport({ scale: 1 }).width }), n = document.createElement("canvas");
15163
+ return n.width = Math.floor(t.width), n.height = Math.floor(t.height), await e.render({
15164
+ canvas: n,
15165
+ viewport: t
15166
+ }).promise, await new Promise((e, t) => {
15167
+ n.toBlob((n) => n ? e(n) : t(/* @__PURE__ */ Error("The first page could not be drawn.")), "image/png");
15168
+ });
15169
+ } finally {
15170
+ await i.destroy();
15171
+ }
15172
+ }
15173
+ var aa = (e) => {
15144
15174
  if (Wi.some((t) => t.key === e)) return e;
15145
15175
  let t = Number(e);
15146
15176
  return Hi.includes(t) ? t : "fit-width";
15147
- }, ia = (e) => e instanceof Element && e.closest("input, textarea, select, [contenteditable='true'], .dropdown-menu"), aa = (e) => /^(https?:|mailto:)/i.test(e), oa = class {
15177
+ }, oa = (e) => e instanceof Element && e.closest("input, textarea, select, [contenteditable='true'], .dropdown-menu"), sa = (e) => /^(https?:|mailto:)/i.test(e), ca = class {
15148
15178
  constructor(e, t) {
15149
15179
  this.shell = e, this.source = t;
15150
15180
  let n = e.prefs;
15151
- this.zoom = ra(n.zoom), this.twoPage = n.twoPage === !0, this.showThumbs = n.thumbnails === !0, this.pdf = null, this.sizes = [], this.rows = [], this.rowOf = [], this.scale = 0, this.drawnScale = 0, this.pages = /* @__PURE__ */ new Map(), this.thumbsDrawn = /* @__PURE__ */ new Set(), this.area = {
15181
+ this.zoom = aa(n.zoom), this.twoPage = n.twoPage === !0, this.showThumbs = n.thumbnails === !0, this.pdf = null, this.sizes = [], this.rows = [], this.rowOf = [], this.scale = 0, this.drawnScale = 0, this.pages = /* @__PURE__ */ new Map(), this.thumbsDrawn = /* @__PURE__ */ new Set(), this.area = {
15152
15182
  width: 0,
15153
15183
  height: 0
15154
15184
  }, this.scrollPos = {
@@ -15165,32 +15195,24 @@ var ra = (e) => {
15165
15195
  }, this.printRun = null, this.destroyed = !1, this._timers = /* @__PURE__ */ new Set();
15166
15196
  }
15167
15197
  async load() {
15168
- na(this.source.workerSrc);
15169
- let e = this.source.assetBase ? this.source.assetBase.replace(/\/?$/, "/") : null;
15170
- this.task = hr({
15198
+ na(this.source.workerSrc), this.task = hr({
15171
15199
  url: this.source.src,
15172
- isEvalSupported: !1,
15173
- ...e && {
15174
- cMapUrl: `${e}cmaps/`,
15175
- cMapPacked: !0,
15176
- standardFontDataUrl: `${e}standard_fonts/`,
15177
- wasmUrl: `${e}wasm/`
15178
- }
15200
+ ...ra(this.source.assetBase)
15179
15201
  });
15180
- let t;
15202
+ let e;
15181
15203
  try {
15182
- t = await this.task.promise;
15204
+ e = await this.task.promise;
15183
15205
  } catch (e) {
15184
15206
  this.destroyed || this.shell.fail(e);
15185
15207
  return;
15186
15208
  }
15187
15209
  if (this.destroyed) {
15188
- t.destroy();
15210
+ e.destroy();
15189
15211
  return;
15190
15212
  }
15191
- this.pdf = t;
15192
- let n = await Promise.all(Array.from({ length: t.numPages }, (e, n) => t.getPage(n + 1)));
15193
- this.destroyed || (this.sizes = n.map((e) => {
15213
+ this.pdf = e;
15214
+ let t = await Promise.all(Array.from({ length: e.numPages }, (t, n) => e.getPage(n + 1)));
15215
+ this.destroyed || (this.sizes = t.map((e) => {
15194
15216
  let t = e.getViewport({ scale: Gi });
15195
15217
  return {
15196
15218
  width: t.width,
@@ -15198,7 +15220,7 @@ var ra = (e) => {
15198
15220
  };
15199
15221
  }), this._buildToolbar(), this._buildBody(), this._layout(), this.shell.loaded({
15200
15222
  kind: "pdf",
15201
- pages: t.numPages
15223
+ pages: e.numPages
15202
15224
  }));
15203
15225
  }
15204
15226
  destroy() {
@@ -15236,7 +15258,7 @@ var ra = (e) => {
15236
15258
  checked: this.zoom === e.key
15237
15259
  }))
15238
15260
  ],
15239
- onPick: (e) => this._setZoom(ra(e))
15261
+ onPick: (e) => this._setZoom(aa(e))
15240
15262
  }), this.zoomInButton = r("zoom-in", "Zoom in"), this.zoomInButton.addEventListener("click", () => this._stepZoom(1));
15241
15263
  let c = document.createElement("span");
15242
15264
  c.className = "wui-file-viewer-divider", this.twoPageButton = r("two-page", "Two-page view"), this.twoPageButton.addEventListener("click", () => this._setTwoPage(!this.twoPage)), this.printButton = r("printer", "Print"), this.printButton.addEventListener("click", () => this.print()), i.prepend(this.searchButton, this.zoomOutButton, this.zoomButton, this.zoomInButton, c, this.twoPageButton, this.printButton), this._buildSearchRow(), this._syncToolbar();
@@ -15291,7 +15313,7 @@ var ra = (e) => {
15291
15313
  }
15292
15314
  _targetScale() {
15293
15315
  if (typeof this.zoom == "number") return this.zoom;
15294
- let e = this.cols(), t = ua(this.sizes.map((e) => e.width)), n = ua(this.sizes.map((e) => e.height)), r = (this.area.width - 2 * Ki - (e - 1) * qi) / (t * e);
15316
+ let e = this.cols(), t = fa(this.sizes.map((e) => e.width)), n = fa(this.sizes.map((e) => e.height)), r = (this.area.width - 2 * Ki - (e - 1) * qi) / (t * e);
15295
15317
  return this.zoom === "fit-page" && (r = Math.min(r, (this.area.height - 2 * Ki) / n)), Math.max(r, .1);
15296
15318
  }
15297
15319
  _layout() {
@@ -15465,7 +15487,7 @@ var ra = (e) => {
15465
15487
  async _drawLinks(e) {
15466
15488
  let t = await this.pdf.getPage(e.n), n = await t.getAnnotations({ intent: "display" });
15467
15489
  if (e.released || this.destroyed) return;
15468
- let r = n.filter((e) => e.subtype === "Link" && (e.dest || e.url && aa(e.url))), i = (await Promise.all(r.map(async (e) => e.url ? { link: e } : {
15490
+ let r = n.filter((e) => e.subtype === "Link" && (e.dest || e.url && sa(e.url))), i = (await Promise.all(r.map(async (e) => e.url ? { link: e } : {
15469
15491
  link: e,
15470
15492
  target: await this._destPage(e.dest)
15471
15493
  }))).filter(({ link: e, target: t }) => e.url || t);
@@ -15494,7 +15516,7 @@ var ra = (e) => {
15494
15516
  window.clearTimeout(this._jumpTimer), this.scroller.removeEventListener("scrollend", this._endJump), this.jumpTarget = null, this._syncToolbar();
15495
15517
  }, Math.abs(n - this.scroller.scrollTop) >= 1 && (this.jumpTarget = t, this.scroller.addEventListener("scrollend", this._endJump), this._jumpTimer = window.setTimeout(this._endJump, Zi), this._timers.add(this._jumpTimer), this.scroller.scrollTo({
15496
15518
  top: n,
15497
- behavior: da()
15519
+ behavior: pa()
15498
15520
  })), this._syncToolbar();
15499
15521
  }
15500
15522
  _commitPageInput() {
@@ -15569,7 +15591,7 @@ var ra = (e) => {
15569
15591
  _followThumb() {
15570
15592
  let e = this.current();
15571
15593
  e === this._activeThumb || !this.thumbEls || (this.thumbEls[this._activeThumb]?.classList.remove("active"), this._activeThumb = e, this.thumbEls[e]?.classList.add("active"), this.thumbEls[e]?.scrollIntoView({
15572
- behavior: da(),
15594
+ behavior: pa(),
15573
15595
  block: "nearest"
15574
15596
  }));
15575
15597
  }
@@ -15616,7 +15638,7 @@ var ra = (e) => {
15616
15638
  let r = this.search.index?.[e - 1]?.items, i = this.search.open ? this.search.byPage?.get(e) ?? [] : [], a = this.search.marked.get(e) ?? /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set();
15617
15639
  r && i.length && r.forEach((e, t) => {
15618
15640
  let r = Vi(e, i);
15619
- !r.length || !n[t] || (o.add(t), ca(n[t], r, this.search.current));
15641
+ !r.length || !n[t] || (o.add(t), ua(n[t], r, this.search.current));
15620
15642
  });
15621
15643
  for (let e of a) !o.has(e) && n[e] && n[e].replaceChildren(n[e].textContent);
15622
15644
  o.size ? this.search.marked.set(e, o) : this.search.marked.delete(e);
@@ -15650,7 +15672,7 @@ var ra = (e) => {
15650
15672
  e.preventDefault();
15651
15673
  return;
15652
15674
  }
15653
- if (e.shiftKey || ia(e.target)) return;
15675
+ if (e.shiftKey || oa(e.target)) return;
15654
15676
  let t = this.cols();
15655
15677
  if (e.key === "PageDown") this.goToPage(this.current() + t);
15656
15678
  else if (e.key === "PageUp") this.goToPage(this.current() - t);
@@ -15710,7 +15732,7 @@ var ra = (e) => {
15710
15732
  }), this.printRun !== e) throw new DOMException("Print replaced", "AbortError");
15711
15733
  }
15712
15734
  let n = document.createElement("iframe");
15713
- n.className = "wui-file-viewer-print-frame", n.title = "Document for printing", n.tabIndex = -1, n.setAttribute("aria-hidden", "true"), e.frame = n, n.srcdoc = sa(this.shell.name(), t), n.addEventListener("load", () => {
15735
+ n.className = "wui-file-viewer-print-frame", n.title = "Document for printing", n.tabIndex = -1, n.setAttribute("aria-hidden", "true"), e.frame = n, n.srcdoc = la(this.shell.name(), t), n.addEventListener("load", () => {
15714
15736
  if (this.printRun === e) {
15715
15737
  e.ready = !0, this._syncToolbar();
15716
15738
  try {
@@ -15745,12 +15767,12 @@ var ra = (e) => {
15745
15767
  console.warn(`[wui-file-viewer] ${e}:`, t?.message ?? t);
15746
15768
  }
15747
15769
  };
15748
- function sa(e, t) {
15770
+ function la(e, t) {
15749
15771
  let n = [...new Set(t.map((e) => `${e.width.toFixed(2)}mm ${e.height.toFixed(2)}mm`))], r = n.map((e, t) => `@page s${t} { size: ${e}; margin: 0; }`).join("\n"), i = t.map((e) => {
15750
15772
  let t = `s${n.indexOf(`${e.width.toFixed(2)}mm ${e.height.toFixed(2)}mm`)}`;
15751
15773
  return `<img src="${e.url}" alt="" style="page:${t};width:${e.width}mm;height:${e.height}mm">`;
15752
15774
  }).join("");
15753
- return `<!doctype html><html><head><title>${la(e)}</title><style>
15775
+ return `<!doctype html><html><head><title>${da(e)}</title><style>
15754
15776
  @page { margin: 0; }
15755
15777
  ${r}
15756
15778
  html, body { margin: 0; padding: 0; }
@@ -15758,7 +15780,7 @@ img { display: block; break-after: page; }
15758
15780
  img:last-child { break-after: auto; }
15759
15781
  </style></head><body>${i}</body></html>`;
15760
15782
  }
15761
- function ca(e, t, n) {
15783
+ function ua(e, t, n) {
15762
15784
  let r = e.textContent, i = [], a = 0;
15763
15785
  for (let { from: e, to: o, n: s } of t) {
15764
15786
  e > a && i.push(document.createTextNode(r.slice(a, e)));
@@ -15767,11 +15789,11 @@ function ca(e, t, n) {
15767
15789
  }
15768
15790
  a < r.length && i.push(document.createTextNode(r.slice(a))), e.replaceChildren(...i);
15769
15791
  }
15770
- var la = (e) => String(e ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15771
- function ua(e) {
15792
+ var da = (e) => String(e ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15793
+ function fa(e) {
15772
15794
  let t = [...e].sort((e, t) => e - t);
15773
15795
  return t[Math.floor(t.length / 2)];
15774
15796
  }
15775
- var da = () => window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth";
15797
+ var pa = () => window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth";
15776
15798
  //#endregion
15777
- export { oa as PdfView };
15799
+ export { ca as PdfView, ia as renderFirstPage };
@@ -0,0 +1,208 @@
1
+ /**
2
+ * A failed chat request. `message` is written for the person and is safe to show as-is.
3
+ */
4
+ export declare class ChatApiError extends Error {
5
+ code: string;
6
+ status: number;
7
+ resetsAtUtc: string;
8
+ /**
9
+ * @param {string} code Machine-readable reason: the server's code (`daily-limit`,
10
+ * `assistant-unavailable`, ...), "network" when no response arrived, "aborted" when the
11
+ * request was cancelled, or "unknown".
12
+ * @param {string} message The sentence to show the person.
13
+ * @param {number} [status] HTTP status, when a response arrived.
14
+ * @param {string|null} [resetsAtUtc] For `daily-limit`: when the allowance comes back, in UTC.
15
+ */
16
+ constructor(code: string, message: string, status?: number, resetsAtUtc?: string | null);
17
+ }
18
+ export type ChatHttp = {
19
+ get: (url: string, config?: object) => Promise<{
20
+ data: any;
21
+ }>;
22
+ post: (url: string, data?: any, config?: object) => Promise<{
23
+ data: any;
24
+ }>;
25
+ put: (url: string, data?: any, config?: object) => Promise<{
26
+ data: any;
27
+ }>;
28
+ delete: (url: string, config?: object) => Promise<{
29
+ data: any;
30
+ }>;
31
+ };
32
+ export type SupportAction = {
33
+ /**
34
+ * Sent back by the confirm button; nothing happens without it.
35
+ */
36
+ token: string;
37
+ kind: "RaiseTicket" | "Reply" | "Reopen";
38
+ ticketId: string | null;
39
+ ticketNumber: string | null;
40
+ category: string | null;
41
+ subject: string | null;
42
+ body: string | null;
43
+ expiresDateTimeUtc: string;
44
+ };
45
+ export type ChatTurnInput = {
46
+ /**
47
+ * The person's question.
48
+ */
49
+ message: string;
50
+ /**
51
+ * The conversation to continue; left out to start one.
52
+ */
53
+ conversationId?: string | null;
54
+ /**
55
+ * Product-defined context about the screen, passed through as-is.
56
+ */
57
+ pageContext?: any;
58
+ };
59
+ export type ChatStreamOptions = {
60
+ /**
61
+ * Cancels the request.
62
+ */
63
+ signal?: AbortSignal;
64
+ /**
65
+ * Called for each event
66
+ * before the end of the turn: `delta { text }` to append, `reset {}` to discard what was shown,
67
+ * `actions { actions }` for support cards.
68
+ */
69
+ onEvent?: (name: "delta" | "reset" | "actions", data: any) => void;
70
+ };
71
+ export type ChatTurnResult = {
72
+ conversationId: string;
73
+ /**
74
+ * The stored reply's id, for rating; null when storing failed.
75
+ */
76
+ messageId: string | null;
77
+ /**
78
+ * The reply of record. It replaces whatever was streamed.
79
+ */
80
+ reply: string;
81
+ /**
82
+ * Null when the person has no daily limit.
83
+ */
84
+ messagesLeftToday: number | null;
85
+ };
86
+ export type ConversationSummary = {
87
+ publicId: string;
88
+ title: string | null;
89
+ createdDateTimeUtc: string;
90
+ lastMessageDateTimeUtc: string;
91
+ };
92
+ export type ConversationMessage = {
93
+ publicId: string;
94
+ role: "User" | "Assistant";
95
+ content: string;
96
+ /**
97
+ * The support actions offered with an assistant reply, as a JSON array.
98
+ */
99
+ actionsJson: string | null;
100
+ rating: "Up" | "Down" | null;
101
+ ratingNote: string | null;
102
+ createdDateTimeUtc: string;
103
+ };
104
+ export type Conversation = {
105
+ publicId: string;
106
+ title: string | null;
107
+ messages: ConversationMessage[];
108
+ };
109
+ export type ChatClient = {
110
+ sendMessage: (input: ChatTurnInput, options?: ChatStreamOptions) => Promise<ChatTurnResult>;
111
+ getAllowance: () => Promise<{
112
+ messagesLeftToday: number | null;
113
+ }>;
114
+ listConversations: (query?: {
115
+ skip?: number;
116
+ take?: number;
117
+ }) => Promise<ConversationSummary[]>;
118
+ getConversation: (id: string) => Promise<Conversation>;
119
+ deleteConversation: (id: string) => Promise<void>;
120
+ rateMessage: (conversationId: string, messageId: string, input: {
121
+ rating: "Up" | "Down";
122
+ note?: string | null;
123
+ }) => Promise<void>;
124
+ };
125
+ /**
126
+ * @typedef {object} ChatHttp An axios instance (or axios-compatible) that already carries the
127
+ * bearer token, tenant headers and refresh. The streamed turn needs axios's XHR adapter (the
128
+ * browser default) to read the reply as it arrives.
129
+ * @property {(url: string, config?: object) => Promise<{ data: any }>} get
130
+ * @property {(url: string, data?: any, config?: object) => Promise<{ data: any }>} post
131
+ * @property {(url: string, data?: any, config?: object) => Promise<{ data: any }>} put
132
+ * @property {(url: string, config?: object) => Promise<{ data: any }>} delete
133
+ */
134
+ /**
135
+ * @typedef {object} SupportAction An action the assistant prepared for the person to confirm.
136
+ * @property {string} token Sent back by the confirm button; nothing happens without it.
137
+ * @property {"RaiseTicket"|"Reply"|"Reopen"} kind
138
+ * @property {string|null} ticketId
139
+ * @property {string|null} ticketNumber
140
+ * @property {string|null} category
141
+ * @property {string|null} subject
142
+ * @property {string|null} body
143
+ * @property {string} expiresDateTimeUtc
144
+ */
145
+ /**
146
+ * @typedef {object} ChatTurnInput
147
+ * @property {string} message The person's question.
148
+ * @property {string|null} [conversationId] The conversation to continue; left out to start one.
149
+ * @property {any} [pageContext] Product-defined context about the screen, passed through as-is.
150
+ */
151
+ /**
152
+ * @typedef {object} ChatStreamOptions
153
+ * @property {AbortSignal} [signal] Cancels the request.
154
+ * @property {(name: "delta"|"reset"|"actions", data: any) => void} [onEvent] Called for each event
155
+ * before the end of the turn: `delta { text }` to append, `reset {}` to discard what was shown,
156
+ * `actions { actions }` for support cards.
157
+ */
158
+ /**
159
+ * @typedef {object} ChatTurnResult The `done` event: the turn's outcome of record.
160
+ * @property {string} conversationId
161
+ * @property {string|null} messageId The stored reply's id, for rating; null when storing failed.
162
+ * @property {string} reply The reply of record. It replaces whatever was streamed.
163
+ * @property {number|null} messagesLeftToday Null when the person has no daily limit.
164
+ */
165
+ /**
166
+ * @typedef {object} ConversationSummary
167
+ * @property {string} publicId
168
+ * @property {string|null} title
169
+ * @property {string} createdDateTimeUtc
170
+ * @property {string} lastMessageDateTimeUtc
171
+ */
172
+ /**
173
+ * @typedef {object} ConversationMessage
174
+ * @property {string} publicId
175
+ * @property {"User"|"Assistant"} role
176
+ * @property {string} content
177
+ * @property {string|null} actionsJson The support actions offered with an assistant reply, as a JSON array.
178
+ * @property {"Up"|"Down"|null} rating
179
+ * @property {string|null} ratingNote
180
+ * @property {string} createdDateTimeUtc
181
+ */
182
+ /**
183
+ * @typedef {object} Conversation
184
+ * @property {string} publicId
185
+ * @property {string|null} title
186
+ * @property {ConversationMessage[]} messages
187
+ */
188
+ /**
189
+ * @typedef {object} ChatClient
190
+ * @property {(input: ChatTurnInput, options?: ChatStreamOptions) => Promise<ChatTurnResult>} sendMessage
191
+ * @property {() => Promise<{ messagesLeftToday: number|null }>} getAllowance
192
+ * @property {(query?: { skip?: number, take?: number }) => Promise<ConversationSummary[]>} listConversations
193
+ * @property {(id: string) => Promise<Conversation>} getConversation
194
+ * @property {(id: string) => Promise<void>} deleteConversation
195
+ * @property {(conversationId: string, messageId: string, input: { rating: "Up"|"Down", note?: string|null }) => Promise<void>} rateMessage
196
+ */
197
+ /**
198
+ * Binds the chat HTTP contract to the product's own authenticated axios instance. The package
199
+ * never handles auth: tokens, tenant headers and refresh stay in the instance.
200
+ *
201
+ * @param {ChatHttp} http
202
+ * @param {{ base: string }} options `base` is the prefix the API maps the chat endpoints under,
203
+ * e.g. "/api/assistant" for `POST /api/assistant/chat`.
204
+ * @returns {ChatClient}
205
+ */
206
+ export declare function createChatClient(http: ChatHttp, { base }: {
207
+ base: string;
208
+ }): ChatClient;
@@ -0,0 +1,4 @@
1
+ export { ChatApiError, createChatClient } from "./chatClient.js";
2
+ export { DEFAULT_CHAT_LABELS, mergeLabels } from "./chatLabels.js";
3
+ export { linkRoute, parseBlocks, parseInline } from "./markdownParse.js";
4
+ export { createSseParser } from "./sse.js";
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Every word the chat components show, in English. Pass a `labels` object with any subset to a
3
+ * component to change its wording; the rest keep these defaults. The few that take a value are
4
+ * functions.
5
+ */
6
+ export declare const DEFAULT_CHAT_LABELS: Readonly<{
7
+ launcher: "Ask the assistant";
8
+ panelTitle: "Assistant";
9
+ newChat: "New chat";
10
+ close: "Close";
11
+ thread: "Conversation";
12
+ you: "You";
13
+ assistant: "Assistant";
14
+ thinking: "Thinking";
15
+ emptyTitle: "How can I help?";
16
+ emptyText: "Ask a question about what you see on this page.";
17
+ composer: "Your question";
18
+ placeholder: "Ask a question";
19
+ send: "Send";
20
+ composerHint: "Press Enter to send, Shift and Enter for a new line.";
21
+ /** @type {(count: number) => string} */
22
+ allowance: (count: number) => string;
23
+ disclaimer: "Answers are written by AI and can be wrong. Check anything important.";
24
+ loadingConversation: "Loading the conversation";
25
+ rateUp: "Helpful";
26
+ rateDown: "Not helpful";
27
+ rated: "Thanks for the feedback.";
28
+ ratingNote: "What was wrong with this answer?";
29
+ ratingNoteHint: "Optional.";
30
+ ratingNoteSend: "Send";
31
+ ratingNoteSkip: "Skip";
32
+ ratingFailed: "Your rating did not save. Please try again.";
33
+ actionRaiseTicket: "New support ticket";
34
+ /** @type {(ticketNumber: string) => string} */
35
+ actionReply: (ticketNumber: string) => string;
36
+ /** @type {(ticketNumber: string) => string} */
37
+ actionReopen: (ticketNumber: string) => string;
38
+ actionNote: "Nothing is sent until you confirm.";
39
+ actionConfirm: "Confirm";
40
+ actionDismiss: "Not now";
41
+ actionDismissed: "Not sent.";
42
+ actionExpired: "This request expired and was not sent. Ask again if you still need it.";
43
+ actionFailed: "We could not send this. Please try again.";
44
+ /** @type {(ticketNumber: string) => string} */
45
+ actionDoneRaiseTicket: (ticketNumber: string) => string;
46
+ /** @type {(ticketNumber: string) => string} */
47
+ actionDoneReply: (ticketNumber: string) => string;
48
+ /** @type {(ticketNumber: string) => string} */
49
+ actionDoneReopen: (ticketNumber: string) => string;
50
+ viewTicket: "View ticket";
51
+ conversations: "Past chats";
52
+ conversationsEmptyTitle: "No past chats";
53
+ conversationsEmptyText: "Your conversations with the assistant show here.";
54
+ untitled: "Untitled chat";
55
+ deleteConversation: "Delete chat";
56
+ deleteConfirmTitle: "Delete this chat?";
57
+ deleteConfirmMessage: "The conversation is removed for good.";
58
+ deleteConfirm: "Delete";
59
+ deleteFailed: "We could not delete this chat. Please try again.";
60
+ cancel: "Cancel";
61
+ loadMore: "Show more";
62
+ loadFailed: "We could not load your chats. Please try again.";
63
+ retry: "Try again";
64
+ }>;
65
+ /**
66
+ * @param {Partial<typeof DEFAULT_CHAT_LABELS>} [labels]
67
+ * @returns {typeof DEFAULT_CHAT_LABELS}
68
+ */
69
+ export declare function mergeLabels(labels?: Partial<typeof DEFAULT_CHAT_LABELS>): typeof DEFAULT_CHAT_LABELS;
@@ -0,0 +1,77 @@
1
+ export type MarkdownBlockShape = {
2
+ type: "paragraph";
3
+ lines: string[];
4
+ } | {
5
+ type: "heading";
6
+ text: string;
7
+ } | {
8
+ type: "list";
9
+ ordered: boolean;
10
+ start: number;
11
+ items: string[];
12
+ } | {
13
+ type: "table";
14
+ header: string[];
15
+ rows: string[][];
16
+ } | {
17
+ type: "code";
18
+ text: string;
19
+ } | {
20
+ type: "rule";
21
+ };
22
+ export type MarkdownBlock = MarkdownBlockShape & {
23
+ key: number;
24
+ };
25
+ /**
26
+ * @typedef {{ type: "paragraph", lines: string[] }
27
+ * | { type: "heading", text: string }
28
+ * | { type: "list", ordered: boolean, start: number, items: string[] }
29
+ * | { type: "table", header: string[], rows: string[][] }
30
+ * | { type: "code", text: string }
31
+ * | { type: "rule" }} MarkdownBlockShape
32
+ * @typedef {MarkdownBlockShape & { key: number }} MarkdownBlock
33
+ */
34
+ /**
35
+ * Splits reply text into blocks. Each block's `key` is its position, so text arriving at the end
36
+ * of a streaming reply updates the last block in place and never remounts the ones before it.
37
+ *
38
+ * @param {string} text
39
+ * @returns {MarkdownBlock[]}
40
+ */
41
+ export declare function parseBlocks(text: string): MarkdownBlock[];
42
+ export type InlineToken = {
43
+ type: "text";
44
+ text: string;
45
+ key: number;
46
+ } | {
47
+ type: "code";
48
+ text: string;
49
+ key: number;
50
+ } | {
51
+ type: "strong" | "em";
52
+ children: InlineToken[];
53
+ key: number;
54
+ } | {
55
+ type: "link";
56
+ href: string;
57
+ children: InlineToken[];
58
+ key: number;
59
+ };
60
+ /**
61
+ * Reads bold (`**` or `__`), italics (`*` or `_`), inline code and `[label](href)` links. A marker
62
+ * with no partner yet (a reply still streaming) shows as the characters typed.
63
+ *
64
+ * @param {string} text
65
+ * @returns {InlineToken[]}
66
+ */
67
+ export declare function parseInline(text: string): InlineToken[];
68
+ /**
69
+ * The in-app route a reply link may open, or null when it may not open one. Only
70
+ * `scheme:kind/id` hrefs reach `resolveLink`, and only a route it returns that starts with a
71
+ * single "/" is used.
72
+ *
73
+ * @param {string} href
74
+ * @param {((href: string) => string|null|undefined)|undefined} resolveLink
75
+ * @returns {string|null}
76
+ */
77
+ export declare function linkRoute(href: string, resolveLink: ((href: string) => string | null | undefined) | undefined): string | null;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @typedef {object} SseEvent
3
+ * @property {string} name The `event:` field, or "message" when the block names none.
4
+ * @property {any} data The `data:` lines joined by newlines and parsed as JSON.
5
+ */
6
+ export type SseEvent = {
7
+ /**
8
+ * The `event:` field, or "message" when the block names none.
9
+ */
10
+ name: string;
11
+ /**
12
+ * The `data:` lines joined by newlines and parsed as JSON.
13
+ */
14
+ data: any;
15
+ };
16
+ /**
17
+ * An incremental reader for a `text/event-stream` body that arrives in pieces. Each `push` takes
18
+ * the next piece of text; only complete events (ended by a blank line) are handed on, and the
19
+ * incomplete tail waits for the next piece. `end` flushes a final event the stream closed
20
+ * without a blank line after.
21
+ *
22
+ * Line endings may be `\n`, `\r\n` or `\r`, and a `\r\n` pair may be split across two pieces.
23
+ * Comment lines (`:`), `id` and `retry` are ignored. A block whose data is not JSON is reported
24
+ * as an `error` event with the code `invalid-response`, so a caller ends the turn instead of
25
+ * hanging on it.
26
+ *
27
+ * @param {(event: SseEvent) => void} onEvent
28
+ * @returns {{ push: (text: string) => void, end: () => void }}
29
+ */
30
+ export declare function createSseParser(onEvent: (event: SseEvent) => void): {
31
+ push: (text: string) => void;
32
+ end: () => void;
33
+ };