@genesislcap/ai-assistant 15.58.2 → 15.60.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.
- package/dist/ai-assistant.api.json +10096 -6986
- package/dist/ai-assistant.d.ts +562 -101
- package/dist/chat-driver.cjs +624 -207
- package/dist/chat-driver.cjs.map +4 -4
- package/dist/chat-driver.mjs +624 -207
- package/dist/chat-driver.mjs.map +4 -4
- package/dist/custom-elements.json +5426 -3570
- package/dist/dts/components/agent-session/agent-session.d.ts +19 -3
- package/dist/dts/components/agent-session/agent-session.d.ts.map +1 -1
- package/dist/dts/components/ai-driver/ai-driver.d.ts +120 -10
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
- package/dist/dts/components/ai-driver/base-driver-decorator.d.ts +103 -0
- package/dist/dts/components/ai-driver/base-driver-decorator.d.ts.map +1 -0
- package/dist/dts/components/ai-driver/index.d.ts +3 -1
- package/dist/dts/components/ai-driver/index.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +84 -8
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/interaction-controller.d.ts +37 -3
- package/dist/dts/components/chat-driver/interaction-controller.d.ts.map +1 -1
- package/dist/dts/components/message-feedback/index.d.ts +2 -0
- package/dist/dts/components/message-feedback/index.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.d.ts +61 -0
- package/dist/dts/components/message-feedback/message-feedback.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.styles.d.ts +2 -0
- package/dist/dts/components/message-feedback/message-feedback.styles.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.template.d.ts +4 -0
- package/dist/dts/components/message-feedback/message-feedback.template.d.ts.map +1 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +30 -93
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/index.d.ts +4 -1
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +21 -5
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/react.d.ts +10 -0
- package/dist/dts/state/debug-event-log.d.ts +1 -1
- package/dist/dts/state/debug-event-log.d.ts.map +1 -1
- package/dist/dts/state/interaction-context.d.ts +7 -1
- package/dist/dts/state/interaction-context.d.ts.map +1 -1
- package/dist/dts/state/persistence/session-persister.d.ts.map +1 -1
- package/dist/dts/suggestions/generate-suggestions.d.ts.map +1 -1
- package/dist/dts/types/handler-capability.d.ts +28 -0
- package/dist/dts/types/handler-capability.d.ts.map +1 -0
- package/dist/dts/types/interaction-context.d.ts +16 -0
- package/dist/dts/types/interaction-context.d.ts.map +1 -1
- package/dist/dts/types/interaction-interceptor.d.ts +109 -0
- package/dist/dts/types/interaction-interceptor.d.ts.map +1 -0
- package/dist/dts/utils/delegated-message.d.ts +52 -0
- package/dist/dts/utils/delegated-message.d.ts.map +1 -0
- package/dist/dts/utils/history-transform.d.ts.map +1 -1
- package/dist/esm/components/agent-session/agent-session.js +17 -0
- package/dist/esm/components/ai-driver/ai-driver.js +21 -1
- package/dist/esm/components/ai-driver/base-driver-decorator.js +211 -0
- package/dist/esm/components/ai-driver/index.js +2 -1
- package/dist/esm/components/chat-driver/chat-driver.js +195 -14
- package/dist/esm/components/chat-driver/interaction-controller.js +168 -14
- package/dist/esm/components/message-feedback/index.js +1 -0
- package/dist/esm/components/message-feedback/message-feedback.js +241 -0
- package/dist/esm/components/message-feedback/message-feedback.styles.js +94 -0
- package/dist/esm/components/message-feedback/message-feedback.template.js +80 -0
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +72 -196
- package/dist/esm/index.js +1 -0
- package/dist/esm/main/main.js +123 -78
- package/dist/esm/main/main.styles.js +27 -0
- package/dist/esm/main/main.template.js +13 -0
- package/dist/esm/state/debug-event-log.js +7 -2
- package/dist/esm/state/interaction-context.js +11 -0
- package/dist/esm/state/persistence/session-persister.js +5 -6
- package/dist/esm/suggestions/generate-suggestions.js +8 -1
- package/dist/esm/types/handler-capability.js +1 -0
- package/dist/esm/types/interaction-interceptor.js +1 -0
- package/dist/esm/utils/delegated-message.js +25 -0
- package/dist/esm/utils/history-transform.js +12 -1
- package/dist/react.cjs +7 -0
- package/dist/react.mjs +6 -0
- package/package.json +17 -17
- package/sandbox/README.md +1 -1
- package/sandbox/sandbox.ts +35 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { __awaiter, __decorate } from "tslib";
|
|
2
|
+
import { attr, customElement, GenesisElement, html, observable, volatile, } from '@genesislcap/web-core';
|
|
3
|
+
import { logger } from '../../utils/logger';
|
|
4
|
+
import { normalizeDebugLogNote } from '../../utils/resolve-debug-log-note';
|
|
5
|
+
import { styles } from './message-feedback.styles';
|
|
6
|
+
import { MessageFeedbackTemplate } from './message-feedback.template';
|
|
7
|
+
/**
|
|
8
|
+
* A final assistant reply, including the assistant's own notices: not reasoning, narration, a
|
|
9
|
+
* tool call, a tool result or a widget. Agrees with `filterVisibleMessages` on what is shown.
|
|
10
|
+
*/
|
|
11
|
+
export const isRateableReply = (m) => {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
return m.role === 'assistant' &&
|
|
14
|
+
!!((_a = m.content) === null || _a === void 0 ? void 0 : _a.trim()) &&
|
|
15
|
+
!((_b = m.toolCalls) === null || _b === void 0 ? void 0 : _b.length) &&
|
|
16
|
+
!m.interaction &&
|
|
17
|
+
!m.thinking &&
|
|
18
|
+
!m.category;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Survives the message object being replaced and the session being reloaded, which object
|
|
22
|
+
* identity does not. Replies saved before timestamps existed fall back to their content.
|
|
23
|
+
*/
|
|
24
|
+
export const replyKey = (m) => { var _a, _b; return `${(_a = m.timestamp) !== null && _a !== void 0 ? _a : m.content}|${(_b = m.agentName) !== null && _b !== void 0 ? _b : ''}`; };
|
|
25
|
+
/** Forwarded by the assistant so `::part()` reaches inside from the host app. */
|
|
26
|
+
export const MESSAGE_FEEDBACK_EXPORTPARTS = [
|
|
27
|
+
'message-feedback-thumbs',
|
|
28
|
+
'message-feedback-button',
|
|
29
|
+
'message-feedback-positive',
|
|
30
|
+
'message-feedback-negative',
|
|
31
|
+
'message-feedback-panel',
|
|
32
|
+
'message-feedback-title',
|
|
33
|
+
'message-feedback-note',
|
|
34
|
+
'message-feedback-hint',
|
|
35
|
+
'message-feedback-cancel-button',
|
|
36
|
+
'message-feedback-send-button',
|
|
37
|
+
].join(', ');
|
|
38
|
+
/**
|
|
39
|
+
* Keyed on the reply, not the element: the transcript's `repeat` recycles rows, so a rating held
|
|
40
|
+
* on the element would jump to whichever reply the row is rebound to.
|
|
41
|
+
*/
|
|
42
|
+
const sentRatings = new Map();
|
|
43
|
+
const focusTextArea = (el) => {
|
|
44
|
+
var _a, _b, _c;
|
|
45
|
+
(_c = ((_b = (_a = el === null || el === void 0 ? void 0 : el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('textarea')) !== null && _b !== void 0 ? _b : el)) === null || _c === void 0 ? void 0 : _c.focus();
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Thumbs up / thumbs down under one assistant reply, with a note panel for the pressed thumb.
|
|
49
|
+
* Rendered by the assistant only when `chatConfig.ui.messageFeedback` is set.
|
|
50
|
+
*
|
|
51
|
+
* @beta
|
|
52
|
+
*/
|
|
53
|
+
let MessageFeedback = class MessageFeedback extends GenesisElement {
|
|
54
|
+
constructor() {
|
|
55
|
+
super(...arguments);
|
|
56
|
+
this.designSystemPrefix = 'rapid';
|
|
57
|
+
this.description = '';
|
|
58
|
+
/** Reflected so the assistant's reveal CSS can keep an open panel visible. */
|
|
59
|
+
this.open = false;
|
|
60
|
+
/** The thumb the open panel belongs to. */
|
|
61
|
+
this.draftRating = null;
|
|
62
|
+
/** The rating last sent for this reply; reflected so a rated reply keeps its thumbs visible. */
|
|
63
|
+
this.sentRating = null;
|
|
64
|
+
this.note = '';
|
|
65
|
+
this.status = 'idle';
|
|
66
|
+
this.announcement = '';
|
|
67
|
+
/** Bumped to disown an in-flight send: on Cancel, and when the row is rebound. */
|
|
68
|
+
this.sendGeneration = 0;
|
|
69
|
+
}
|
|
70
|
+
messageChanged(prev, next) {
|
|
71
|
+
var _a;
|
|
72
|
+
if (prev && next && replyKey(prev) === replyKey(next))
|
|
73
|
+
return;
|
|
74
|
+
this.sendGeneration += 1;
|
|
75
|
+
this.open = false;
|
|
76
|
+
this.draftRating = null;
|
|
77
|
+
this.note = '';
|
|
78
|
+
this.status = 'idle';
|
|
79
|
+
this.announcement = '';
|
|
80
|
+
this.sentRating = next ? ((_a = sentRatings.get(replyKey(next))) !== null && _a !== void 0 ? _a : null) : null;
|
|
81
|
+
}
|
|
82
|
+
/** The thumb that reads as pressed: the open panel's, else the one already sent. */
|
|
83
|
+
get activeRating() {
|
|
84
|
+
return this.open ? this.draftRating : this.sentRating;
|
|
85
|
+
}
|
|
86
|
+
toggle(rating) {
|
|
87
|
+
if (this.status === 'sending')
|
|
88
|
+
return;
|
|
89
|
+
if (this.open && this.draftRating === rating) {
|
|
90
|
+
this.cancel();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.draftRating = rating;
|
|
94
|
+
this.status = 'idle';
|
|
95
|
+
this.open = true;
|
|
96
|
+
requestAnimationFrame(() => {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
if (!this.open || !this.isConnected)
|
|
99
|
+
return;
|
|
100
|
+
// Focusing the design-system text-area does not scroll the transcript to it.
|
|
101
|
+
(_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.panel')) === null || _b === void 0 ? void 0 : _b.scrollIntoView({ block: 'nearest' });
|
|
102
|
+
focusTextArea(this.noteEl);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Also abandons an in-flight send, so a hung transport cannot trap the user in the panel. */
|
|
106
|
+
cancel() {
|
|
107
|
+
this.sendGeneration += 1;
|
|
108
|
+
const rating = this.draftRating;
|
|
109
|
+
this.open = false;
|
|
110
|
+
this.draftRating = null;
|
|
111
|
+
this.note = '';
|
|
112
|
+
this.status = 'idle';
|
|
113
|
+
this.restoreThumbFocus(rating);
|
|
114
|
+
}
|
|
115
|
+
noteInput(value) {
|
|
116
|
+
this.note = value;
|
|
117
|
+
if (this.status === 'error')
|
|
118
|
+
this.status = 'idle';
|
|
119
|
+
}
|
|
120
|
+
send() {
|
|
121
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
const { message, draftRating: rating, submit } = this;
|
|
123
|
+
if (!message || !rating || !submit || this.status === 'sending')
|
|
124
|
+
return;
|
|
125
|
+
const key = replyKey(message);
|
|
126
|
+
this.sendGeneration += 1;
|
|
127
|
+
const generation = this.sendGeneration;
|
|
128
|
+
this.status = 'sending';
|
|
129
|
+
// The live region only speaks on a change, so a repeat outcome must follow an empty one.
|
|
130
|
+
this.announcement = '';
|
|
131
|
+
try {
|
|
132
|
+
yield submit(message, rating, normalizeDebugLogNote(this.note));
|
|
133
|
+
// Recorded even when abandoned: it was delivered, and an unrated row invites a duplicate.
|
|
134
|
+
sentRatings.set(key, rating);
|
|
135
|
+
if (this.message && replyKey(this.message) === key)
|
|
136
|
+
this.sentRating = rating;
|
|
137
|
+
if (generation !== this.sendGeneration)
|
|
138
|
+
return;
|
|
139
|
+
this.open = false;
|
|
140
|
+
this.draftRating = null;
|
|
141
|
+
this.note = '';
|
|
142
|
+
this.status = 'idle';
|
|
143
|
+
this.announcement = 'Thanks for your feedback.';
|
|
144
|
+
this.restoreThumbFocus(rating);
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
logger.error('Sending message feedback failed:', e);
|
|
148
|
+
if (generation !== this.sendGeneration)
|
|
149
|
+
return;
|
|
150
|
+
this.status = 'error';
|
|
151
|
+
this.announcement = "Couldn't send your feedback.";
|
|
152
|
+
requestAnimationFrame(() => {
|
|
153
|
+
if (this.open && this.isConnected && this.ownsFocus())
|
|
154
|
+
focusTextArea(this.noteEl);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/** @internal */
|
|
160
|
+
handleKeydown(event) {
|
|
161
|
+
if (event.key === 'Escape' && this.open) {
|
|
162
|
+
// Handled here; a host container that closes on Escape must not close as well.
|
|
163
|
+
event.stopPropagation();
|
|
164
|
+
this.cancel();
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
if (event.key === 'Enter' && !event.shiftKey && this.open && event.target === this.noteEl) {
|
|
168
|
+
void this.send();
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
/** Closing the panel unmounts the focused note; hand focus back to the thumb that opened it. */
|
|
174
|
+
restoreThumbFocus(rating) {
|
|
175
|
+
if (!rating)
|
|
176
|
+
return;
|
|
177
|
+
requestAnimationFrame(() => {
|
|
178
|
+
var _a, _b;
|
|
179
|
+
if (!this.isConnected || this.open || !this.ownsFocus())
|
|
180
|
+
return;
|
|
181
|
+
(_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`[part~="message-feedback-${rating}"]`)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Focus is ours to move only while it is nowhere in particular or still inside this element —
|
|
186
|
+
* not once the user has gone on to type in the composer during a slow send. Walks the shadow
|
|
187
|
+
* roots, since `document.activeElement` is the assistant host for both.
|
|
188
|
+
*/
|
|
189
|
+
ownsFocus() {
|
|
190
|
+
var _a, _b;
|
|
191
|
+
let el = document.activeElement;
|
|
192
|
+
if (!el || el === document.body)
|
|
193
|
+
return true;
|
|
194
|
+
while (el) {
|
|
195
|
+
if (el === this)
|
|
196
|
+
return true;
|
|
197
|
+
el = (_b = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) !== null && _b !== void 0 ? _b : null;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
__decorate([
|
|
203
|
+
observable
|
|
204
|
+
], MessageFeedback.prototype, "designSystemPrefix", void 0);
|
|
205
|
+
__decorate([
|
|
206
|
+
observable
|
|
207
|
+
], MessageFeedback.prototype, "message", void 0);
|
|
208
|
+
__decorate([
|
|
209
|
+
observable
|
|
210
|
+
], MessageFeedback.prototype, "description", void 0);
|
|
211
|
+
__decorate([
|
|
212
|
+
attr({ mode: 'boolean' })
|
|
213
|
+
], MessageFeedback.prototype, "open", void 0);
|
|
214
|
+
__decorate([
|
|
215
|
+
observable
|
|
216
|
+
], MessageFeedback.prototype, "draftRating", void 0);
|
|
217
|
+
__decorate([
|
|
218
|
+
attr({ attribute: 'rated' })
|
|
219
|
+
], MessageFeedback.prototype, "sentRating", void 0);
|
|
220
|
+
__decorate([
|
|
221
|
+
observable
|
|
222
|
+
], MessageFeedback.prototype, "note", void 0);
|
|
223
|
+
__decorate([
|
|
224
|
+
observable
|
|
225
|
+
], MessageFeedback.prototype, "status", void 0);
|
|
226
|
+
__decorate([
|
|
227
|
+
observable
|
|
228
|
+
], MessageFeedback.prototype, "announcement", void 0);
|
|
229
|
+
__decorate([
|
|
230
|
+
volatile
|
|
231
|
+
], MessageFeedback.prototype, "activeRating", null);
|
|
232
|
+
MessageFeedback = __decorate([
|
|
233
|
+
customElement({
|
|
234
|
+
name: 'foundation-ai-message-feedback',
|
|
235
|
+
template: html `
|
|
236
|
+
${(x) => MessageFeedbackTemplate(x.designSystemPrefix)}
|
|
237
|
+
`,
|
|
238
|
+
styles,
|
|
239
|
+
})
|
|
240
|
+
], MessageFeedback);
|
|
241
|
+
export { MessageFeedback };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { css } from '@genesislcap/web-core';
|
|
2
|
+
export const styles = css `
|
|
3
|
+
:host {
|
|
4
|
+
display: block;
|
|
5
|
+
margin-top: calc(var(--design-unit) * 1.5px);
|
|
6
|
+
font-size: 13px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.thumbs {
|
|
10
|
+
display: flex;
|
|
11
|
+
gap: 2px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.thumb {
|
|
15
|
+
appearance: none;
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
width: 24px;
|
|
20
|
+
height: 24px;
|
|
21
|
+
padding: 0;
|
|
22
|
+
border: none;
|
|
23
|
+
border-radius: calc(var(--control-corner-radius) * 1px);
|
|
24
|
+
background: transparent;
|
|
25
|
+
color: inherit;
|
|
26
|
+
font-size: 12px;
|
|
27
|
+
opacity: 65%;
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.thumb:hover,
|
|
32
|
+
.thumb:focus-visible,
|
|
33
|
+
.thumb[aria-pressed='true'] {
|
|
34
|
+
opacity: 100%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.thumb:hover {
|
|
38
|
+
background: var(--neutral-fill-stealth-hover);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.thumb:focus-visible {
|
|
42
|
+
outline: calc(var(--focus-stroke-width) * 1px) solid var(--focus-stroke-outer);
|
|
43
|
+
outline-offset: 1px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.thumb:disabled {
|
|
47
|
+
cursor: default;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.panel {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: calc(var(--design-unit) * 2px);
|
|
54
|
+
margin-top: calc(var(--design-unit) * 1px);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.panel-title {
|
|
58
|
+
margin: 0;
|
|
59
|
+
font-weight: 600;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.note {
|
|
63
|
+
width: 100%;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.hint {
|
|
67
|
+
margin: 0;
|
|
68
|
+
font-size: 11px;
|
|
69
|
+
line-height: 1.4;
|
|
70
|
+
color: var(--neutral-foreground-hint);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.hint.is-error {
|
|
74
|
+
color: var(--error-color);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.actions {
|
|
78
|
+
display: flex;
|
|
79
|
+
justify-content: flex-end;
|
|
80
|
+
gap: calc(var(--design-unit) * 2px);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.visually-hidden {
|
|
84
|
+
position: absolute;
|
|
85
|
+
width: 1px;
|
|
86
|
+
height: 1px;
|
|
87
|
+
margin: -1px;
|
|
88
|
+
padding: 0;
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
clip-path: inset(50%);
|
|
91
|
+
white-space: nowrap;
|
|
92
|
+
border: 0;
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { html, ref, when } from '@genesislcap/web-core';
|
|
2
|
+
import { DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH } from '../../utils/resolve-debug-log-note';
|
|
3
|
+
const thumbLabel = {
|
|
4
|
+
positive: 'Good response',
|
|
5
|
+
negative: 'Bad response',
|
|
6
|
+
};
|
|
7
|
+
const panelTitle = {
|
|
8
|
+
positive: 'What went well?',
|
|
9
|
+
negative: 'What went wrong?',
|
|
10
|
+
};
|
|
11
|
+
export const MessageFeedbackTemplate = (designSystemPrefix) => {
|
|
12
|
+
const buttonTag = `${designSystemPrefix}-button`;
|
|
13
|
+
const textareaTag = `${designSystemPrefix}-text-area`;
|
|
14
|
+
const iconTag = `${designSystemPrefix}-icon`;
|
|
15
|
+
const thumb = (rating) => html `
|
|
16
|
+
<button
|
|
17
|
+
type="button"
|
|
18
|
+
class="thumb"
|
|
19
|
+
part="message-feedback-button message-feedback-${rating}"
|
|
20
|
+
aria-label="${thumbLabel[rating]}"
|
|
21
|
+
title="${(x) => x.sentRating === rating && !x.open ? 'Thanks for your feedback' : thumbLabel[rating]}"
|
|
22
|
+
aria-pressed="${(x) => String(x.activeRating === rating)}"
|
|
23
|
+
?disabled="${(x) => x.status === 'sending'}"
|
|
24
|
+
@click="${(x) => x.toggle(rating)}"
|
|
25
|
+
>
|
|
26
|
+
<${iconTag}
|
|
27
|
+
name="${rating === 'positive' ? 'thumbs-up' : 'thumbs-down'}"
|
|
28
|
+
variant="${(x) => (x.activeRating === rating ? 'solid' : 'regular')}"
|
|
29
|
+
></${iconTag}>
|
|
30
|
+
</button>
|
|
31
|
+
`;
|
|
32
|
+
return html `
|
|
33
|
+
<div class="root" @keydown="${(x, c) => x.handleKeydown(c.event)}">
|
|
34
|
+
<div class="thumbs" part="message-feedback-thumbs">
|
|
35
|
+
${thumb('positive')}${thumb('negative')}
|
|
36
|
+
</div>
|
|
37
|
+
<span class="visually-hidden" role="status" aria-live="polite">${(x) => x.announcement}</span>
|
|
38
|
+
${when((x) => x.open && x.draftRating !== null, html `
|
|
39
|
+
<div class="panel" part="message-feedback-panel" role="group" aria-label="Send feedback">
|
|
40
|
+
<p class="panel-title" part="message-feedback-title">
|
|
41
|
+
${(x) => panelTitle[x.draftRating]}
|
|
42
|
+
</p>
|
|
43
|
+
<${textareaTag}
|
|
44
|
+
${ref('noteEl')}
|
|
45
|
+
class="note"
|
|
46
|
+
part="message-feedback-note"
|
|
47
|
+
rows="3"
|
|
48
|
+
resize="vertical"
|
|
49
|
+
aria-label="Feedback note (optional)"
|
|
50
|
+
placeholder="Add a note (optional)"
|
|
51
|
+
maxlength="${() => DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH}"
|
|
52
|
+
:value="${(x) => x.note}"
|
|
53
|
+
?disabled="${(x) => x.status === 'sending'}"
|
|
54
|
+
@input="${(x, c) => x.noteInput(c.event.target.value)}"
|
|
55
|
+
></${textareaTag}>
|
|
56
|
+
<p class="hint ${(x) => (x.status === 'error' ? 'is-error' : '')}" part="message-feedback-hint">
|
|
57
|
+
${(x) => x.status === 'error' ? "Couldn't send your feedback. Try again." : x.description}
|
|
58
|
+
</p>
|
|
59
|
+
<div class="actions">
|
|
60
|
+
<${buttonTag}
|
|
61
|
+
part="message-feedback-cancel-button"
|
|
62
|
+
appearance="outline"
|
|
63
|
+
@click="${(x) => x.cancel()}"
|
|
64
|
+
>
|
|
65
|
+
Cancel
|
|
66
|
+
</${buttonTag}>
|
|
67
|
+
<${buttonTag}
|
|
68
|
+
part="message-feedback-send-button"
|
|
69
|
+
appearance="accent"
|
|
70
|
+
?disabled="${(x) => x.status === 'sending'}"
|
|
71
|
+
@click="${(x) => x.send()}"
|
|
72
|
+
>
|
|
73
|
+
${(x) => x.status === 'sending' ? 'Sending…' : x.status === 'error' ? 'Retry' : 'Send'}
|
|
74
|
+
</${buttonTag}>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
`)}
|
|
78
|
+
</div>
|
|
79
|
+
`;
|
|
80
|
+
};
|