@miso.ai/client-sdk-ui 1.14.0-beta.2 → 1.14.0-beta.4
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/cjs/asset/svgs.js +4 -2
- package/dist/cjs/constants.js +2 -0
- package/dist/cjs/defaults/ask/index.js +2 -1
- package/dist/cjs/defaults/ask/templates.js +2 -1
- package/dist/cjs/defaults/hybrid-search/index.js +2 -1
- package/dist/cjs/defaults/hybrid-search/templates.js +2 -1
- package/dist/cjs/defaults/index.js +2 -1
- package/dist/cjs/defaults.js +107 -10
- package/dist/cjs/element/container/index.js +14 -0
- package/dist/cjs/element/container/miso-message-item.js +91 -0
- package/dist/cjs/element/container/miso-thread-item.js +91 -0
- package/dist/cjs/element/index.js +2 -1
- package/dist/cjs/element/role/index.js +7 -0
- package/dist/cjs/element/role/miso-delete.js +20 -0
- package/dist/cjs/layout/button/button.js +45 -5
- package/dist/cjs/layout/button/checkbox.js +2 -1
- package/dist/cjs/layout/container/item-container.js +99 -0
- package/dist/cjs/layout/index.js +14 -0
- package/dist/cjs/layout/input/search-box.js +9 -4
- package/dist/cjs/layout/list/collection.js +21 -4
- package/dist/cjs/layout/list/messages.js +261 -224
- package/dist/cjs/layout/list/threads.js +106 -150
- package/dist/cjs/layout/templates.js +98 -38
- package/dist/cjs/layout/text/question.js +71 -0
- package/dist/cjs/layout/text/typewriter/index.js +15 -21
- package/dist/cjs/plugin.js +6 -3
- package/dist/cjs/util/dom.js +21 -0
- package/dist/cjs/util/notify.js +74 -0
- package/dist/cjs/version.js +1 -1
- package/package.json +3 -3
- package/src/CLAUDE.md +7 -2
- package/src/asset/svgs.js +3 -1
- package/src/constants.js +2 -0
- package/src/defaults.js +75 -5
- package/src/element/container/index.js +2 -0
- package/src/element/container/miso-message-item.js +94 -0
- package/src/element/container/miso-thread-item.js +94 -0
- package/src/element/role/index.js +1 -0
- package/src/element/role/miso-delete.js +16 -0
- package/src/layout/button/button.js +40 -4
- package/src/layout/button/checkbox.js +2 -1
- package/src/layout/container/item-container.js +99 -0
- package/src/layout/index.js +2 -0
- package/src/layout/input/search-box.js +4 -2
- package/src/layout/list/collection.js +22 -5
- package/src/layout/list/messages.js +231 -201
- package/src/layout/list/threads.js +107 -128
- package/src/layout/templates.js +100 -38
- package/src/layout/text/question.js +58 -0
- package/src/layout/text/typewriter/index.js +11 -15
- package/src/plugin.js +3 -2
- package/src/util/dom.js +16 -0
- package/src/util/notify.js +68 -0
- package/src/version.js +1 -1
|
@@ -7,42 +7,47 @@ exports.default = void 0;
|
|
|
7
7
|
var _clientSdkWorkflow = require("@miso.ai/client-sdk-workflow");
|
|
8
8
|
var _constants = require("../../constants.js");
|
|
9
9
|
var _collection = _interopRequireDefault(require("./collection.js"));
|
|
10
|
+
var _templates = require("../templates.js");
|
|
10
11
|
var _dom = require("../../util/dom.js");
|
|
11
|
-
var _confirm = _interopRequireDefault(require("../../util/confirm.js"));
|
|
12
|
-
var _prompt = _interopRequireDefault(require("../../util/prompt.js"));
|
|
13
12
|
const _excluded = ["className", "templates"];
|
|
14
13
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
14
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
16
15
|
const TYPE = _constants.LAYOUT_TYPE.THREADS;
|
|
17
16
|
const DEFAULT_CLASSNAME = 'miso-threads';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} = layout;
|
|
26
|
-
return templates[options.itemType](layout, state, value, {
|
|
27
|
-
index
|
|
28
|
-
});
|
|
17
|
+
const DEFAULT_TEMPLATES = Object.freeze({
|
|
18
|
+
[_constants.STATUS.ERRONEOUS]: _templates.listError,
|
|
19
|
+
errorMessage
|
|
20
|
+
});
|
|
21
|
+
const INHERITED_DEFAULT_TEMPLATES = Object.freeze(Object.assign({}, _collection.default.defaultTemplates, DEFAULT_TEMPLATES));
|
|
22
|
+
function errorMessage(layout, error) {
|
|
23
|
+
return (0, _templates.isSessionExpired)(error) ? _templates.SESSION_EXPIRED_MESSAGE : 'Your conversations could not be loaded.';
|
|
29
24
|
}
|
|
30
25
|
|
|
31
26
|
/**
|
|
32
|
-
* The thread list of the chat history interface: a
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
27
|
+
* The thread list of the chat history interface: a shell that renders one
|
|
28
|
+
* <miso-thread-item> container element per thread item, incrementally —
|
|
29
|
+
* fresh items land at either end of the list: a newly created thread
|
|
30
|
+
* renders as a prepended item (the list is newest-first), while an older
|
|
31
|
+
* page fetched by the more flow (infinite scroll: the collection layout's
|
|
32
|
+
* stock trigger, viewable at the list's bottom, fires the `more` hub
|
|
33
|
+
* field) renders as appended items — told apart by which boundary the
|
|
34
|
+
* already-rendered records line up with. The content
|
|
35
|
+
* of an item is not rendered here: each <miso-thread-item> is assigned its item
|
|
36
|
+
* subworkflow (workflow._getThreadWorkflow, off the item binding) in the
|
|
37
|
+
* post-render sync pass, and the role elements inside (the title, and the
|
|
38
|
+
* context menu's rename/delete buttons) render through that workflow's own
|
|
39
|
+
* layouts — so a record change re-renders the affected item's roles alone.
|
|
41
40
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
41
|
+
* The shell keeps the list-level behaviors. Selection comes with the data:
|
|
42
|
+
* the history workflow stamps `selectedThreadId` into its value and
|
|
43
|
+
* decorates each thread record with a `selected` flag, so any selection
|
|
44
|
+
* change flows down the regular data path; in-place state changes on
|
|
45
|
+
* existing items (selected, unread, the thread identity of a settled
|
|
46
|
+
* placeholder) are applied by `_syncItems` after each render, reading the
|
|
47
|
+
* fresh values off the item bindings — no item re-render needed. Each item
|
|
48
|
+
* carries a context menu (vertical dots), but that is the thread workflow's
|
|
49
|
+
* too: its item-container layout inserts and toggles it — the shell only
|
|
50
|
+
* keeps menu clicks (by their item-menu data-roles) from selecting.
|
|
46
51
|
*/
|
|
47
52
|
class ThreadsLayout extends _collection.default {
|
|
48
53
|
static get type() {
|
|
@@ -51,6 +56,9 @@ class ThreadsLayout extends _collection.default {
|
|
|
51
56
|
static get defaultClassName() {
|
|
52
57
|
return DEFAULT_CLASSNAME;
|
|
53
58
|
}
|
|
59
|
+
static get defaultTemplates() {
|
|
60
|
+
return INHERITED_DEFAULT_TEMPLATES;
|
|
61
|
+
}
|
|
54
62
|
constructor(_ref = {}) {
|
|
55
63
|
let {
|
|
56
64
|
className = DEFAULT_CLASSNAME,
|
|
@@ -59,37 +67,62 @@ class ThreadsLayout extends _collection.default {
|
|
|
59
67
|
options = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
60
68
|
super(Object.assign({
|
|
61
69
|
className,
|
|
62
|
-
templates: Object.assign({
|
|
63
|
-
item
|
|
64
|
-
}, templates)
|
|
70
|
+
templates: Object.assign({}, DEFAULT_TEMPLATES, templates)
|
|
65
71
|
}, options));
|
|
66
72
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
|
|
74
|
+
// item identity: the thread id, or the local placeholder id standing in
|
|
75
|
+
// for it while a thread is being created — the workflow adopts the thread
|
|
76
|
+
// id at the settle, so the element and its workflow survive it
|
|
77
|
+
_getItemKey(thread) {
|
|
78
|
+
return thread.thread_id || thread.placeholder_id || thread;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// incremental renders cover a pure prepend (a created thread) or a pure
|
|
82
|
+
// append (an older page) — anything else re-renders in full
|
|
83
|
+
_shallRenderIncrementally(state, rendered) {
|
|
84
|
+
if (!super._shallRenderIncrementally(state, rendered)) {
|
|
85
|
+
return false;
|
|
79
86
|
}
|
|
87
|
+
const values = this._getItems(state) || [];
|
|
88
|
+
const renderedValues = rendered.value;
|
|
89
|
+
const count = values.length - renderedValues.length;
|
|
90
|
+
return count === 0 ||
|
|
91
|
+
// in-place changes only
|
|
92
|
+
this._getItemKey(values[0]) === this._getItemKey(renderedValues[0]) ||
|
|
93
|
+
// appended
|
|
94
|
+
this._getItemKey(values[count]) === this._getItemKey(renderedValues[0]); // prepended
|
|
80
95
|
}
|
|
81
96
|
|
|
82
|
-
// the
|
|
83
|
-
//
|
|
97
|
+
// the fresh items land at the head (a newly created thread — the list is
|
|
98
|
+
// newest-first) or at the tail (an older page fetched by the more flow):
|
|
99
|
+
// told apart by which boundary the already-rendered records line up with
|
|
84
100
|
_html(state, rendered, incremental) {
|
|
85
|
-
if (incremental) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
if (!incremental) {
|
|
102
|
+
return this.templates.root(this, state);
|
|
103
|
+
}
|
|
104
|
+
const values = this._getItems(state) || [];
|
|
105
|
+
const renderedValues = rendered.value;
|
|
106
|
+
const count = values.length - renderedValues.length;
|
|
107
|
+
if (count === 0) {
|
|
108
|
+
return '';
|
|
109
|
+
}
|
|
110
|
+
if (this._getItemKey(values[0]) === this._getItemKey(renderedValues[0])) {
|
|
111
|
+
const fresh = values.slice(renderedValues.length);
|
|
112
|
+
return {
|
|
113
|
+
position: 'beforeend',
|
|
114
|
+
items: this.templates.items(this, state, fresh, {
|
|
115
|
+
offset: renderedValues.length
|
|
116
|
+
})
|
|
117
|
+
};
|
|
91
118
|
}
|
|
92
|
-
|
|
119
|
+
const fresh = values.slice(0, count);
|
|
120
|
+
return {
|
|
121
|
+
position: 'afterbegin',
|
|
122
|
+
items: this.templates.items(this, state, fresh, {
|
|
123
|
+
offset: 0
|
|
124
|
+
})
|
|
125
|
+
};
|
|
93
126
|
}
|
|
94
127
|
_render(element, {
|
|
95
128
|
state
|
|
@@ -102,7 +135,7 @@ class ThreadsLayout extends _collection.default {
|
|
|
102
135
|
} = state;
|
|
103
136
|
if (incremental) {
|
|
104
137
|
if (html) {
|
|
105
|
-
this._getListElement(element).insertAdjacentHTML(
|
|
138
|
+
this._getListElement(element).insertAdjacentHTML(html.position, html.items);
|
|
106
139
|
} else {
|
|
107
140
|
notifyUpdate(false);
|
|
108
141
|
}
|
|
@@ -112,12 +145,28 @@ class ThreadsLayout extends _collection.default {
|
|
|
112
145
|
}
|
|
113
146
|
_afterRender(element, state) {
|
|
114
147
|
super._afterRender(element, state); // syncs bindings to the latest values
|
|
148
|
+
this._syncWorkflows(element);
|
|
115
149
|
this._syncItems(element);
|
|
116
150
|
}
|
|
117
151
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
152
|
+
// assign each <miso-thread-item> its item subworkflow, off the item binding
|
|
153
|
+
_syncWorkflows(element) {
|
|
154
|
+
const workflow = this._view && this._view.workflow;
|
|
155
|
+
if (!workflow || typeof workflow._getThreadWorkflow !== 'function') {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
for (const item of this._getItemElements(element)) {
|
|
159
|
+
const binding = this._bindings.get(item);
|
|
160
|
+
if (binding && item.isContainer) {
|
|
161
|
+
item.workflow = workflow._getThreadWorkflow(binding.value);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// sync in-place item state changes from the bound values onto the existing
|
|
167
|
+
// item elements: selection/unread state, and the thread identity of a
|
|
168
|
+
// placeholder that settled into its real record — the item contents render
|
|
169
|
+
// through the thread workflows, outside this layout's render cycle
|
|
121
170
|
_syncItems(element) {
|
|
122
171
|
for (const item of this._getItemElements(element)) {
|
|
123
172
|
const binding = this._bindings.get(item);
|
|
@@ -129,38 +178,16 @@ class ThreadsLayout extends _collection.default {
|
|
|
129
178
|
} = binding;
|
|
130
179
|
(0, _dom.setOrRemoveAttribute)(item, 'data-selected', value.selected ? '' : undefined);
|
|
131
180
|
(0, _dom.setOrRemoveAttribute)(item, 'data-unread', (0, _clientSdkWorkflow.isThreadUnread)(value) ? '' : undefined);
|
|
132
|
-
(0, _dom.setOrRemoveAttribute)(item, 'data-thread-id',
|
|
133
|
-
const titleElement = item.querySelector(`.${this.className}__title`);
|
|
134
|
-
const title = value.title || 'Untitled';
|
|
135
|
-
if (titleElement && titleElement.textContent !== title) {
|
|
136
|
-
titleElement.textContent = title;
|
|
137
|
-
}
|
|
181
|
+
(0, _dom.setOrRemoveAttribute)(item, 'data-thread-id', value.thread_id || undefined);
|
|
138
182
|
}
|
|
139
183
|
}
|
|
140
184
|
|
|
141
185
|
// a click on a thread item means selection — a navigation action, not a
|
|
142
186
|
// content-engagement click: emit a select view event and skip click tracking
|
|
143
187
|
_onClick(event) {
|
|
144
|
-
if (event.target.closest(`[data-role="
|
|
145
|
-
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (event.target.closest(`[data-role="thread-rename"]`)) {
|
|
149
|
-
this._closeMenus();
|
|
150
|
-
const binding = this._bindings.get(event.target.closest(`[data-role="item"]`));
|
|
151
|
-
binding && this._requestRename(binding);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (event.target.closest(`[data-role="thread-delete"]`)) {
|
|
155
|
-
this._closeMenus();
|
|
156
|
-
const binding = this._bindings.get(event.target.closest(`[data-role="item"]`));
|
|
157
|
-
binding && this._requestDelete(binding);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
if (event.target.closest(`[data-role="thread-menu"]`)) {
|
|
161
|
-
return; // other clicks inside the menu don't select
|
|
188
|
+
if (event.target.closest(`[data-role="item-menu-button"], [data-role="item-menu"]`)) {
|
|
189
|
+
return; // the item's own context menu, owned by its container layout
|
|
162
190
|
}
|
|
163
|
-
this._closeMenus();
|
|
164
191
|
const element = event.target.closest(`[data-role="item"]`);
|
|
165
192
|
if (!element) {
|
|
166
193
|
return;
|
|
@@ -182,76 +209,5 @@ class ThreadsLayout extends _collection.default {
|
|
|
182
209
|
domEvent: event
|
|
183
210
|
});
|
|
184
211
|
}
|
|
185
|
-
|
|
186
|
-
// context menu //
|
|
187
|
-
_toggleMenu(item) {
|
|
188
|
-
const menu = item && item.querySelector(`[data-role="thread-menu"]`);
|
|
189
|
-
if (!menu) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const open = menu.hidden;
|
|
193
|
-
this._closeMenus();
|
|
194
|
-
menu.hidden = !open;
|
|
195
|
-
}
|
|
196
|
-
_closeMenus() {
|
|
197
|
-
const element = this._element;
|
|
198
|
-
if (!element) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
for (const menu of element.querySelectorAll(`[data-role="thread-menu"]:not([hidden])`)) {
|
|
202
|
-
menu.hidden = true;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// rename //
|
|
207
|
-
async _requestRename(binding) {
|
|
208
|
-
const {
|
|
209
|
-
value,
|
|
210
|
-
element
|
|
211
|
-
} = binding;
|
|
212
|
-
const title = await (0, _prompt.default)({
|
|
213
|
-
title: 'Rename thread',
|
|
214
|
-
value: value.title || '',
|
|
215
|
-
placeholder: 'Thread name',
|
|
216
|
-
confirmText: 'Rename'
|
|
217
|
-
});
|
|
218
|
-
if (!title || title === value.title) {
|
|
219
|
-
return; // cancelled, cleared, or unchanged
|
|
220
|
-
}
|
|
221
|
-
const {
|
|
222
|
-
session
|
|
223
|
-
} = this._view._state;
|
|
224
|
-
this._view._emit('rename', {
|
|
225
|
-
session,
|
|
226
|
-
value,
|
|
227
|
-
element,
|
|
228
|
-
title
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// delete //
|
|
233
|
-
async _requestDelete(binding) {
|
|
234
|
-
const {
|
|
235
|
-
value,
|
|
236
|
-
element
|
|
237
|
-
} = binding;
|
|
238
|
-
const confirmed = await (0, _confirm.default)({
|
|
239
|
-
title: 'Delete thread',
|
|
240
|
-
message: `Are you sure you want to delete "${value.title || 'this thread'}"? This cannot be undone.`,
|
|
241
|
-
confirmText: 'Delete',
|
|
242
|
-
danger: true
|
|
243
|
-
});
|
|
244
|
-
if (!confirmed) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
const {
|
|
248
|
-
session
|
|
249
|
-
} = this._view._state;
|
|
250
|
-
this._view._emit('delete', {
|
|
251
|
-
session,
|
|
252
|
-
value,
|
|
253
|
-
element
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
212
|
}
|
|
257
213
|
exports.default = ThreadsLayout;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.SESSION_EXPIRED_MESSAGE = void 0;
|
|
6
7
|
exports.affiliation = affiliation;
|
|
7
8
|
exports.affiliationInfoBlock = affiliationInfoBlock;
|
|
8
9
|
exports.article = article;
|
|
@@ -22,16 +23,20 @@ exports.image = image;
|
|
|
22
23
|
exports.imageBlock = imageBlock;
|
|
23
24
|
exports.imageInfoBlock = imageInfoBlock;
|
|
24
25
|
exports.indexBlock = indexBlock;
|
|
26
|
+
exports.isSessionExpired = isSessionExpired;
|
|
27
|
+
exports.listError = listError;
|
|
25
28
|
exports.message = message;
|
|
26
|
-
exports.
|
|
27
|
-
exports.
|
|
28
|
-
exports.
|
|
29
|
+
exports.messageBody = messageBody;
|
|
30
|
+
exports.messageErrorBlock = messageErrorBlock;
|
|
31
|
+
exports.messageErrorMessage = messageErrorMessage;
|
|
32
|
+
exports.messageSourcesBlock = messageSourcesBlock;
|
|
29
33
|
exports.priceBlock = priceBlock;
|
|
30
34
|
exports.product = product;
|
|
31
35
|
exports.productInfoBlock = productInfoBlock;
|
|
32
36
|
exports.question = question;
|
|
33
37
|
exports.requiresImplementation = requiresImplementation;
|
|
34
38
|
exports.thread = thread;
|
|
39
|
+
exports.threadBody = threadBody;
|
|
35
40
|
exports.threadMenuBlock = threadMenuBlock;
|
|
36
41
|
exports.titleBlock = titleBlock;
|
|
37
42
|
exports.unimplemented = unimplemented;
|
|
@@ -64,67 +69,122 @@ function question(layout, state, data) {
|
|
|
64
69
|
return `${openTag}${data.value || data.text || data}${closeTag}`;
|
|
65
70
|
}
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
/**
|
|
73
|
+
* A thread item is a <miso-thread-item> container element hosting the thread
|
|
74
|
+
* item subworkflow — the threads layout is a shell: it assigns the workflow
|
|
75
|
+
* (and syncs the item-level attributes) in its post-render pass, and the
|
|
76
|
+
* content renders through the thread workflow's own layouts: the title role
|
|
77
|
+
* element in the body, plus the context menu (with the
|
|
78
|
+
* rename/subscription/delete role elements) inserted as chrome by the workflow's item-container layout. The
|
|
79
|
+
* item state renders as attributes right in the template (and stays in
|
|
80
|
+
* sync thereafter): the thread identity, the selection, and the unread
|
|
81
|
+
* mark.
|
|
82
|
+
*/
|
|
70
83
|
function thread(layout, state, data) {
|
|
71
84
|
const {
|
|
72
85
|
className,
|
|
73
86
|
templates
|
|
74
87
|
} = layout;
|
|
75
|
-
const threadId =
|
|
88
|
+
const threadId = data.thread_id;
|
|
76
89
|
const threadIdAttr = threadId ? ` data-thread-id="${threadId}"` : '';
|
|
77
90
|
const unreadAttr = (0, _clientSdkWorkflow.isThreadUnread)(data) ? ' data-unread' : '';
|
|
78
91
|
const selectedAttr = data.selected ? ' data-selected' : '';
|
|
79
|
-
|
|
80
|
-
|
|
92
|
+
return [`<miso-thread-item class="${className}__item-body" data-role="item"${threadIdAttr}${unreadAttr}${selectedAttr}>`, (templates.threadBody || threadBody)(layout, data), `</miso-thread-item>`].join('');
|
|
93
|
+
}
|
|
94
|
+
function threadBody(layout, data) {
|
|
95
|
+
const {
|
|
96
|
+
className,
|
|
97
|
+
templates
|
|
98
|
+
} = layout;
|
|
99
|
+
return `<miso-title class="${className}__title"></miso-title>` + (templates.threadMenuBlock || threadMenuBlock)(layout, data);
|
|
81
100
|
}
|
|
101
|
+
|
|
102
|
+
// the item-menu data-roles are the contract with the thread workflow's
|
|
103
|
+
// item-container layout, which drives the menu's open/close behavior; the
|
|
104
|
+
// menu items are the thread workflow's own controls, rendered by the
|
|
105
|
+
// generic button layout with a prompt (rename) or a confirm (delete) dialog,
|
|
106
|
+
// and by the checkbox layout (the subscription toggle)
|
|
82
107
|
function threadMenuBlock({
|
|
83
108
|
className
|
|
84
109
|
}) {
|
|
85
|
-
return `<button type="button" class="${className}__menu-button" data-role="
|
|
110
|
+
return `<button type="button" class="${className}__menu-button" data-role="item-menu-button" aria-label="Thread actions" aria-haspopup="menu">${(0, _svgs.getIcon)('dots-vertical')}</button>` + `<div class="${className}__menu" data-role="item-menu" role="menu" hidden>` + `<miso-rename class="${className}__menu-item"></miso-rename>` + `<miso-subscription class="${className}__menu-item"></miso-subscription>` + `<miso-delete class="${className}__menu-item ${className}__menu-item--danger"></miso-delete>` + `</div>`;
|
|
86
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A message item is a <miso-message-item> container element hosting the message
|
|
115
|
+
* item subworkflow — the messages layout is a shell: it assigns the workflow
|
|
116
|
+
* (and the item-level attributes) in its post-render sync pass, and the role
|
|
117
|
+
* elements inside render the actual content through the message workflow's
|
|
118
|
+
* own layouts. The item state renders as an attribute right in the template
|
|
119
|
+
* (and stays in sync thereafter): the live mark, for a message generated in
|
|
120
|
+
* this session.
|
|
121
|
+
*/
|
|
87
122
|
function message(layout, state, data) {
|
|
88
123
|
const {
|
|
89
124
|
className,
|
|
90
125
|
templates
|
|
91
126
|
} = layout;
|
|
92
|
-
|
|
127
|
+
const liveAttr = data.live ? ' data-live' : '';
|
|
128
|
+
// miso-circled-citation-index covers both the citation links in the answer
|
|
129
|
+
// and the indexes on the source cards, as the hybrid-search UI scopes it
|
|
130
|
+
return [`<miso-message-item class="${className}__item-body miso-circled-citation-index" data-role="item"${liveAttr}>`, (templates.messageBody || messageBody)(layout, data), `</miso-message-item>`].join('');
|
|
93
131
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
132
|
+
|
|
133
|
+
// the regular content is packed together, so the erroneous presentation
|
|
134
|
+
// swaps it out wholesale for the error notice (the content wrapper hides by
|
|
135
|
+
// a class rule off the item's status — visible-when suits the notice, but
|
|
136
|
+
// not a wrapper: it forces `display: block !important` and only negates in
|
|
137
|
+
// combination with positive terms)
|
|
138
|
+
function messageBody(layout) {
|
|
97
139
|
const {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// always rendered so it can be filled in place when the question text
|
|
103
|
-
// arrives later (the list renders incrementally); the authorship attrs
|
|
104
|
-
// drive the author label and the bubble color
|
|
105
|
-
const attrs = `${author ? ` data-author="${(0, _commons.escapeHtml)(author)}"` : ''}${generatedBy ? ` data-generated-by="${(0, _commons.escapeHtml)(generatedBy)}"` : ''}`;
|
|
106
|
-
return `<div class="${className}__question" data-role="question"${attrs}${question ? '' : ' hidden'}>${question ? (0, _commons.escapeHtml)(question) : ''}</div>`;
|
|
140
|
+
className,
|
|
141
|
+
templates
|
|
142
|
+
} = layout;
|
|
143
|
+
return `<div class="${className}__content">` + `<miso-question class="${className}__question" hidden></miso-question>` + `<miso-answer class="${className}__answer"></miso-answer>` + `<miso-feedback class="${className}__feedback"></miso-feedback>` + (templates.messageSourcesBlock || messageSourcesBlock)(layout) + `</div>` + (templates.messageErrorBlock || messageErrorBlock)(layout);
|
|
107
144
|
}
|
|
108
145
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return
|
|
146
|
+
// the sources with their heading, as the hybrid-search UI presents them —
|
|
147
|
+
// shown once the item carries content (visible-when nonempty), so no
|
|
148
|
+
// orphan heading sits over a message still loading
|
|
149
|
+
function messageSourcesBlock({
|
|
150
|
+
className
|
|
151
|
+
}) {
|
|
152
|
+
return `<div class="${className}__sources-container" visible-when="nonempty">` + `<h3 class="${className}__sources-phrase">Sources</h3>` + `<miso-sources class="${className}__sources"></miso-sources>` + `</div>`;
|
|
116
153
|
}
|
|
117
|
-
|
|
154
|
+
|
|
155
|
+
// shown by the item's own status (visible-when): a message whose content
|
|
156
|
+
// fetch, or (a live one's) posting or answer polling, failed for good
|
|
157
|
+
// presents this in place of its content — the text through the item's own
|
|
158
|
+
// error role element, off the record's error mark (messageErrorMessage)
|
|
159
|
+
function messageErrorBlock({
|
|
118
160
|
className
|
|
119
|
-
}, {
|
|
120
|
-
answer
|
|
121
161
|
}) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
162
|
+
return `<div class="${className}__error" visible-when="erroneous"><miso-error></miso-error></div>`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// the error layout's message template for a message item (seeded in the
|
|
166
|
+
// 'conversation' layout defaults): an expired session reads apart from other
|
|
167
|
+
// failures
|
|
168
|
+
function messageErrorMessage(layout, error) {
|
|
169
|
+
return isSessionExpired(error) ? SESSION_EXPIRED_MESSAGE : 'Something went wrong with this message.';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// the list-level error notice of the chat history interface, in place of
|
|
173
|
+
// the list when its load failed (the thread list, or the thread on display);
|
|
174
|
+
// the text comes from the layout's errorMessage template, given the failure
|
|
175
|
+
function listError(layout, state) {
|
|
176
|
+
const {
|
|
177
|
+
className,
|
|
178
|
+
templates
|
|
179
|
+
} = layout;
|
|
180
|
+
return `<div class="${className}__list-error" data-role="error">${templates.errorMessage(layout, state.error)}</div>`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// an expired session (e.g. an expired JWT), to be told apart from other failures
|
|
184
|
+
function isSessionExpired(error) {
|
|
185
|
+
return !!error && error.status === 401;
|
|
127
186
|
}
|
|
187
|
+
const SESSION_EXPIRED_MESSAGE = exports.SESSION_EXPIRED_MESSAGE = 'Your session has expired. Please sign in again.';
|
|
128
188
|
function article(layout, state, data, meta) {
|
|
129
189
|
const {
|
|
130
190
|
templates
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _commons = require("@miso.ai/commons");
|
|
8
|
+
var _clientSdkWorkflow = require("@miso.ai/client-sdk-workflow");
|
|
9
|
+
var _constants = require("../../constants.js");
|
|
10
|
+
var _dom = require("../../util/dom.js");
|
|
11
|
+
var _text = _interopRequireDefault(require("./text.js"));
|
|
12
|
+
const _excluded = ["className", "tag", "author", "templates"];
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
15
|
+
const TYPE = _constants.LAYOUT_TYPE.QUESTION;
|
|
16
|
+
const DEFAULT_CLASSNAME = 'miso-question';
|
|
17
|
+
|
|
18
|
+
// the author label: only a question written by the answer-updates monitor is
|
|
19
|
+
// labeled — the user's own questions carry none
|
|
20
|
+
function defaultAuthor(message) {
|
|
21
|
+
return (0, _clientSdkWorkflow.isUpdateMessage)(message) ? 'Written by Miso' : undefined;
|
|
22
|
+
}
|
|
23
|
+
function content(layout, {
|
|
24
|
+
value
|
|
25
|
+
}) {
|
|
26
|
+
const question = value && value.question;
|
|
27
|
+
return question ? (0, _commons.escapeHtml)(question) : '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The question of a message: a text layout taking the whole message record —
|
|
32
|
+
* the text is the `question` field, and the authorship is stamped as data
|
|
33
|
+
* attributes on the host element (`data-author` drives the CSS label badge,
|
|
34
|
+
* `data-generated-by` the bubble color), re-applied on every render, since
|
|
35
|
+
* the metadata arrives with the answers response, after the stub render. The
|
|
36
|
+
* host element hides while there is no question text to show. The author
|
|
37
|
+
* label is configurable via the `author` option (message -> label text).
|
|
38
|
+
*/
|
|
39
|
+
class QuestionLayout extends _text.default {
|
|
40
|
+
static get type() {
|
|
41
|
+
return TYPE;
|
|
42
|
+
}
|
|
43
|
+
static get defaultClassName() {
|
|
44
|
+
return DEFAULT_CLASSNAME;
|
|
45
|
+
}
|
|
46
|
+
constructor(_ref = {}) {
|
|
47
|
+
let {
|
|
48
|
+
className = DEFAULT_CLASSNAME,
|
|
49
|
+
tag = 'div',
|
|
50
|
+
author = defaultAuthor,
|
|
51
|
+
templates
|
|
52
|
+
} = _ref,
|
|
53
|
+
options = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
54
|
+
super(Object.assign({
|
|
55
|
+
className,
|
|
56
|
+
tag,
|
|
57
|
+
author,
|
|
58
|
+
templates: Object.assign({
|
|
59
|
+
content
|
|
60
|
+
}, templates)
|
|
61
|
+
}, options));
|
|
62
|
+
}
|
|
63
|
+
_afterRender(element, state) {
|
|
64
|
+
super._afterRender(element, state);
|
|
65
|
+
const message = state.value || {};
|
|
66
|
+
element.hidden = !message.question;
|
|
67
|
+
(0, _dom.setOrRemoveAttribute)(element, 'data-author', this.options.author(message));
|
|
68
|
+
(0, _dom.setOrRemoveAttribute)(element, 'data-generated-by', message.metadata && message.metadata.miso_generated_by || undefined);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.default = QuestionLayout;
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _commons = require("@miso.ai/commons");
|
|
8
8
|
var _constants = require("../../../constants.js");
|
|
9
|
+
var _dom = require("../../../util/dom.js");
|
|
9
10
|
var _progressive = _interopRequireDefault(require("../../progressive.js"));
|
|
10
11
|
var _plaintext = _interopRequireDefault(require("./plaintext.js"));
|
|
11
12
|
var _utils = require("./utils.js");
|
|
@@ -160,6 +161,18 @@ class TypewriterLayout extends _progressive.default {
|
|
|
160
161
|
// new typing streak
|
|
161
162
|
result = this._renderer.clear(container, rendered);
|
|
162
163
|
} else {
|
|
164
|
+
result = rendered;
|
|
165
|
+
}
|
|
166
|
+
if (this.options.instant && !result.cursor) {
|
|
167
|
+
// instant mode: while nothing is typed yet, the cursor starts at the max
|
|
168
|
+
// available position (the renderer clamps it to the safe right bound) —
|
|
169
|
+
// content that arrived complete (e.g. a loaded conversation message)
|
|
170
|
+
// renders in one shot; ongoing content shows what is there so far at
|
|
171
|
+
// once, and only the part streaming in after that types out
|
|
172
|
+
result = this._renderer.update(container, result, Object.assign({}, state, {
|
|
173
|
+
cursor: Infinity
|
|
174
|
+
}));
|
|
175
|
+
} else if (!newStreak) {
|
|
163
176
|
const cursor = this._getNextCursor(rendered.cursor, state.doneAt, rendered.timestamp, state.timestamp);
|
|
164
177
|
result = this._renderer.update(container, rendered, Object.assign({}, state, {
|
|
165
178
|
cursor
|
|
@@ -228,7 +241,7 @@ class TypewriterLayout extends _progressive.default {
|
|
|
228
241
|
url: href,
|
|
229
242
|
text: innerText ? innerText.trim() : '',
|
|
230
243
|
className: className || '',
|
|
231
|
-
attributes: dumpElementAttributes(anchorElement)
|
|
244
|
+
attributes: (0, _dom.dumpElementAttributes)(anchorElement)
|
|
232
245
|
}));
|
|
233
246
|
}
|
|
234
247
|
}
|
|
@@ -236,23 +249,4 @@ class TypewriterLayout extends _progressive.default {
|
|
|
236
249
|
}
|
|
237
250
|
}
|
|
238
251
|
}
|
|
239
|
-
exports.default = TypewriterLayout;
|
|
240
|
-
function dumpElementAttributes(element) {
|
|
241
|
-
const {
|
|
242
|
-
attributes
|
|
243
|
-
} = element;
|
|
244
|
-
const attrs = {};
|
|
245
|
-
for (const {
|
|
246
|
-
name,
|
|
247
|
-
value
|
|
248
|
-
} of attributes) {
|
|
249
|
-
if (name.startsWith('data-')) {
|
|
250
|
-
attrs[name] = value;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
try {
|
|
254
|
-
return JSON.stringify(attrs);
|
|
255
|
-
} catch (e) {
|
|
256
|
-
return '{}';
|
|
257
|
-
}
|
|
258
|
-
}
|
|
252
|
+
exports.default = TypewriterLayout;
|