makethisbetter 1.5.0 → 1.5.2
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/api/client.d.ts +6 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/i18n/de.d.ts +2 -0
- package/dist/i18n/de.d.ts.map +1 -1
- package/dist/i18n/en.d.ts +2 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/es.d.ts +2 -0
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/fr.d.ts +2 -0
- package/dist/i18n/fr.d.ts.map +1 -1
- package/dist/i18n/ja.d.ts +2 -0
- package/dist/i18n/ja.d.ts.map +1 -1
- package/dist/i18n/ko.d.ts +2 -0
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/zh-CN.d.ts +2 -0
- package/dist/i18n/zh-CN.d.ts.map +1 -1
- package/dist/makethisbetter.cjs +36 -15
- package/dist/makethisbetter.cjs.map +1 -1
- package/dist/makethisbetter.esm.js +480 -356
- package/dist/makethisbetter.esm.js.map +1 -1
- package/dist/makethisbetter.js +36 -15
- package/dist/makethisbetter.js.map +1 -1
- package/dist/popup/clarify.d.ts +10 -0
- package/dist/popup/clarify.d.ts.map +1 -1
- package/dist/screenshot/capture.d.ts.map +1 -1
- package/dist/widget/controller.d.ts +1 -0
- package/dist/widget/controller.d.ts.map +1 -1
- package/dist/widget/shadow.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api/client.test.ts +31 -0
- package/src/api/client.ts +75 -2
- package/src/i18n/de.ts +3 -1
- package/src/i18n/en.ts +3 -1
- package/src/i18n/es.ts +3 -1
- package/src/i18n/fr.ts +3 -1
- package/src/i18n/ja.ts +3 -1
- package/src/i18n/ko.ts +3 -1
- package/src/i18n/zh-CN.ts +3 -1
- package/src/popup/clarify.test.ts +136 -27
- package/src/popup/clarify.ts +128 -42
- package/src/screenshot/capture.test.ts +14 -12
- package/src/screenshot/capture.ts +8 -2
- package/src/styles/widget.css +137 -57
- package/src/widget/controller.test.ts +45 -0
- package/src/widget/controller.ts +11 -5
- package/src/widget/shadow.test.ts +19 -0
- package/src/widget/shadow.ts +4 -1
package/src/popup/clarify.ts
CHANGED
|
@@ -2,25 +2,34 @@ import type { ShadowContainer } from '../widget/shadow'
|
|
|
2
2
|
import type { I18nMessages } from '../i18n'
|
|
3
3
|
import type { ApiClient } from '../api/client'
|
|
4
4
|
import type { ClarifyMessage } from '../types'
|
|
5
|
-
import { BRAND_COLOR } from '../styles'
|
|
6
5
|
|
|
7
6
|
const POLL_INTERVAL_MS = 2000
|
|
8
7
|
const DONE_DELAY_MS = 2000
|
|
8
|
+
// Give up polling after this long with no assistant reply so the card never
|
|
9
|
+
// spins forever (e.g. a backend job that silently died).
|
|
10
|
+
const POLL_TIMEOUT_MS = 45000
|
|
9
11
|
|
|
10
12
|
interface ClarifyCardOptions {
|
|
11
13
|
feedbackId: string
|
|
12
14
|
apiClient: ApiClient
|
|
13
15
|
messages: I18nMessages
|
|
14
16
|
onDone: () => void
|
|
17
|
+
x?: number
|
|
18
|
+
y?: number
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
const CARD_WIDTH = 346
|
|
22
|
+
const CARD_HEIGHT = 320
|
|
23
|
+
|
|
17
24
|
export class ClarifyCard {
|
|
18
25
|
private el: HTMLDivElement
|
|
19
26
|
private messagesEl!: HTMLDivElement
|
|
20
|
-
private inputEl!:
|
|
27
|
+
private inputEl!: HTMLTextAreaElement
|
|
21
28
|
private sendBtn!: HTMLButtonElement
|
|
22
29
|
private pollTimer: ReturnType<typeof setInterval> | null = null
|
|
30
|
+
private pollDeadline = 0
|
|
23
31
|
private doneTimer: ReturnType<typeof setTimeout> | null = null
|
|
32
|
+
private streamingBubble: HTMLDivElement | null = null
|
|
24
33
|
private feedbackId: string
|
|
25
34
|
private apiClient: ApiClient
|
|
26
35
|
private onDone: () => void
|
|
@@ -33,9 +42,8 @@ export class ClarifyCard {
|
|
|
33
42
|
this.onDone = opts.onDone
|
|
34
43
|
|
|
35
44
|
this.el = shadow.el<HTMLDivElement>('div', 'mtb-clarify')
|
|
36
|
-
this.el.style.right = '20px'
|
|
37
|
-
this.el.style.bottom = '20px'
|
|
38
45
|
this.el.innerHTML = this.buildHTML(opts.messages)
|
|
46
|
+
this.positionCard(opts.x, opts.y)
|
|
39
47
|
shadow.append(this.el)
|
|
40
48
|
|
|
41
49
|
this.messagesEl = this.el.querySelector('.mtb-clarify-messages')!
|
|
@@ -46,39 +54,78 @@ export class ClarifyCard {
|
|
|
46
54
|
this.startConversation()
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
// Anchors the card near the annotation point, clamped to the viewport so it
|
|
58
|
+
// never overflows a screen edge (mirrors CommentPopup.positionPopup).
|
|
59
|
+
private positionCard(x?: number, y?: number): void {
|
|
60
|
+
const vw = window.innerWidth
|
|
61
|
+
const vh = window.innerHeight
|
|
62
|
+
const margin = 12
|
|
63
|
+
|
|
64
|
+
if (x === undefined || y === undefined) {
|
|
65
|
+
this.el.style.left = `${Math.max(margin, (vw - CARD_WIDTH) / 2)}px`
|
|
66
|
+
this.el.style.top = `${Math.max(margin, (vh - CARD_HEIGHT) / 2)}px`
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let left = x + 12
|
|
71
|
+
let top = y + 12
|
|
72
|
+
if (left + CARD_WIDTH > vw - margin) left = x - CARD_WIDTH - 12
|
|
73
|
+
if (left < margin) left = margin
|
|
74
|
+
if (top + CARD_HEIGHT > vh - margin) top = vh - CARD_HEIGHT - margin
|
|
75
|
+
if (top < margin) top = margin
|
|
76
|
+
|
|
77
|
+
this.el.style.left = `${left}px`
|
|
78
|
+
this.el.style.top = `${top}px`
|
|
79
|
+
}
|
|
80
|
+
|
|
49
81
|
private buildHTML(m: I18nMessages): string {
|
|
50
82
|
return `
|
|
51
83
|
<div class="mtb-clarify-header">
|
|
52
|
-
<
|
|
53
|
-
<svg width="
|
|
54
|
-
<path d="
|
|
84
|
+
<span class="mtb-clarify-icon">
|
|
85
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round">
|
|
86
|
+
<path d="M12 3l1.6 5.4L19 10l-5.4 1.6L12 17l-1.6-5.4L5 10l5.4-1.6L12 3z"/>
|
|
55
87
|
</svg>
|
|
88
|
+
</span>
|
|
89
|
+
<div class="mtb-clarify-titles">
|
|
90
|
+
<div class="mtb-clarify-title">${escapeHtml(m.clarify.title)}</div>
|
|
91
|
+
<div class="mtb-clarify-subtitle">${escapeHtml(m.clarify.subtitle)}</div>
|
|
56
92
|
</div>
|
|
57
|
-
<button class="mtb-clarify-skip"
|
|
93
|
+
<button class="mtb-clarify-close" type="button" title="${escapeHtml(m.clarify.skip)}" aria-label="${escapeHtml(m.clarify.skip)}">
|
|
94
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
|
95
|
+
<path d="M18 6L6 18M6 6l12 12"/>
|
|
96
|
+
</svg>
|
|
97
|
+
</button>
|
|
58
98
|
</div>
|
|
59
99
|
<div class="mtb-clarify-messages"></div>
|
|
60
100
|
<div class="mtb-clarify-footer">
|
|
61
|
-
<
|
|
62
|
-
<
|
|
101
|
+
<textarea class="mtb-clarify-input" rows="2" placeholder="${escapeHtml(m.clarify.placeholder)}"></textarea>
|
|
102
|
+
<div class="mtb-clarify-actions">
|
|
103
|
+
<button class="mtb-clarify-skip" type="button">${escapeHtml(m.clarify.skip)}</button>
|
|
104
|
+
<button class="mtb-clarify-send" type="button">
|
|
105
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
106
|
+
<line x1="22" y1="2" x2="11" y2="13"/>
|
|
107
|
+
<polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
|
108
|
+
</svg>
|
|
109
|
+
<span>${escapeHtml(m.clarify.send)}</span>
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
63
112
|
</div>
|
|
64
113
|
`
|
|
65
114
|
}
|
|
66
115
|
|
|
67
116
|
private bindEvents(m: I18nMessages): void {
|
|
68
|
-
|
|
117
|
+
const skip = () => {
|
|
69
118
|
this.stopPolling()
|
|
70
119
|
this.onDone()
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
const hasText = this.inputEl.value.trim().length > 0
|
|
75
|
-
this.sendBtn.disabled = !hasText || this.sending
|
|
76
|
-
})
|
|
120
|
+
}
|
|
121
|
+
this.el.querySelector('.mtb-clarify-skip')!.addEventListener('click', skip)
|
|
122
|
+
this.el.querySelector('.mtb-clarify-close')!.addEventListener('click', skip)
|
|
77
123
|
|
|
78
124
|
this.inputEl.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
79
125
|
// keyCode 229: IME composition fallback for browsers without isComposing
|
|
80
126
|
if (e.isComposing || e.keyCode === 229) return
|
|
81
|
-
|
|
127
|
+
// Enter sends; Shift+Enter inserts a newline in the textarea.
|
|
128
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
82
129
|
e.preventDefault()
|
|
83
130
|
this.sendMessage(m)
|
|
84
131
|
}
|
|
@@ -89,22 +136,11 @@ export class ClarifyCard {
|
|
|
89
136
|
|
|
90
137
|
private async startConversation(): Promise<void> {
|
|
91
138
|
this.showThinking()
|
|
92
|
-
|
|
93
|
-
const res = await this.apiClient.startClarification(this.feedbackId)
|
|
94
|
-
if (this.destroyed) return
|
|
95
|
-
this.renderConversation(res.messages, res.done)
|
|
96
|
-
if (res.done) {
|
|
97
|
-
this.handleDone()
|
|
98
|
-
} else {
|
|
99
|
-
this.startPolling()
|
|
100
|
-
}
|
|
101
|
-
} catch {
|
|
102
|
-
if (this.destroyed) return
|
|
103
|
-
this.onDone()
|
|
104
|
-
}
|
|
139
|
+
await this.runTurn(undefined)
|
|
105
140
|
}
|
|
106
141
|
|
|
107
142
|
private async sendMessage(m: I18nMessages): Promise<void> {
|
|
143
|
+
void m
|
|
108
144
|
const text = this.inputEl.value.trim()
|
|
109
145
|
if (!text || this.sending) return
|
|
110
146
|
|
|
@@ -118,27 +154,67 @@ export class ClarifyCard {
|
|
|
118
154
|
this.showThinking()
|
|
119
155
|
|
|
120
156
|
try {
|
|
121
|
-
|
|
157
|
+
await this.runTurn(text)
|
|
158
|
+
} finally {
|
|
159
|
+
this.sending = false
|
|
160
|
+
this.inputEl.disabled = false
|
|
161
|
+
this.sendBtn.disabled = false
|
|
162
|
+
this.inputEl.focus()
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Streams the assistant reply; falls back to POST + polling if the backend
|
|
167
|
+
// doesn't support SSE (older server) or the stream errors mid-flight.
|
|
168
|
+
private async runTurn(message: string | undefined): Promise<void> {
|
|
169
|
+
try {
|
|
170
|
+
const result = await this.apiClient.streamClarification(
|
|
171
|
+
this.feedbackId,
|
|
172
|
+
message,
|
|
173
|
+
(text) => this.appendStreamDelta(text),
|
|
174
|
+
)
|
|
175
|
+
if (this.destroyed) return
|
|
176
|
+
this.endStreamingBubble()
|
|
177
|
+
this.renderConversation(result.messages, result.done)
|
|
178
|
+
if (result.done) this.handleDone()
|
|
179
|
+
else this.startPolling()
|
|
180
|
+
} catch {
|
|
181
|
+
if (this.destroyed) return
|
|
182
|
+
this.endStreamingBubble()
|
|
183
|
+
await this.runTurnViaPolling(message)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private async runTurnViaPolling(message: string | undefined): Promise<void> {
|
|
188
|
+
try {
|
|
189
|
+
const res = await this.apiClient.startClarification(this.feedbackId, message)
|
|
122
190
|
if (this.destroyed) return
|
|
123
191
|
this.renderConversation(res.messages, res.done)
|
|
124
|
-
if (res.done)
|
|
125
|
-
|
|
126
|
-
} else {
|
|
127
|
-
this.startPolling()
|
|
128
|
-
}
|
|
192
|
+
if (res.done) this.handleDone()
|
|
193
|
+
else this.startPolling()
|
|
129
194
|
} catch {
|
|
130
195
|
if (this.destroyed) return
|
|
131
196
|
this.onDone()
|
|
132
|
-
} finally {
|
|
133
|
-
this.sending = false
|
|
134
|
-
this.inputEl.disabled = false
|
|
135
|
-
this.sendBtn.disabled = this.inputEl.value.trim().length === 0
|
|
136
|
-
this.inputEl.focus()
|
|
137
197
|
}
|
|
138
198
|
}
|
|
139
199
|
|
|
200
|
+
private appendStreamDelta(text: string): void {
|
|
201
|
+
if (!this.streamingBubble) {
|
|
202
|
+
this.removeThinking()
|
|
203
|
+
this.streamingBubble = document.createElement('div')
|
|
204
|
+
this.streamingBubble.className = 'mtb-clarify-bubble mtb-clarify-ai'
|
|
205
|
+
this.messagesEl.appendChild(this.streamingBubble)
|
|
206
|
+
}
|
|
207
|
+
this.streamingBubble.textContent = (this.streamingBubble.textContent ?? '') + text
|
|
208
|
+
this.messagesEl.scrollTop = this.messagesEl.scrollHeight
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private endStreamingBubble(): void {
|
|
212
|
+
this.streamingBubble = null
|
|
213
|
+
}
|
|
214
|
+
|
|
140
215
|
private startPolling(): void {
|
|
141
216
|
this.stopPolling()
|
|
217
|
+
this.pollDeadline = Date.now() + POLL_TIMEOUT_MS
|
|
142
218
|
this.pollTimer = setInterval(() => this.poll(), POLL_INTERVAL_MS)
|
|
143
219
|
}
|
|
144
220
|
|
|
@@ -150,6 +226,11 @@ export class ClarifyCard {
|
|
|
150
226
|
}
|
|
151
227
|
|
|
152
228
|
private async poll(): Promise<void> {
|
|
229
|
+
if (Date.now() > this.pollDeadline) {
|
|
230
|
+
this.stopPolling()
|
|
231
|
+
this.handleDone()
|
|
232
|
+
return
|
|
233
|
+
}
|
|
153
234
|
try {
|
|
154
235
|
const res = await this.apiClient.getClarification(this.feedbackId)
|
|
155
236
|
if (this.destroyed) return
|
|
@@ -195,6 +276,7 @@ export class ClarifyCard {
|
|
|
195
276
|
}
|
|
196
277
|
|
|
197
278
|
private showThinking(): void {
|
|
279
|
+
if (this.messagesEl.querySelector('.mtb-clarify-thinking')) return
|
|
198
280
|
const dots = document.createElement('div')
|
|
199
281
|
dots.className = 'mtb-clarify-bubble mtb-clarify-ai mtb-clarify-thinking'
|
|
200
282
|
dots.innerHTML = '<span class="mtb-dot"></span><span class="mtb-dot"></span><span class="mtb-dot"></span>'
|
|
@@ -202,6 +284,10 @@ export class ClarifyCard {
|
|
|
202
284
|
this.messagesEl.scrollTop = this.messagesEl.scrollHeight
|
|
203
285
|
}
|
|
204
286
|
|
|
287
|
+
private removeThinking(): void {
|
|
288
|
+
this.messagesEl.querySelector('.mtb-clarify-thinking')?.remove()
|
|
289
|
+
}
|
|
290
|
+
|
|
205
291
|
destroy(): void {
|
|
206
292
|
this.destroyed = true
|
|
207
293
|
this.stopPolling()
|
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import { toJpeg } from 'html-to-image'
|
|
3
3
|
import { captureScreenshot } from './capture'
|
|
4
4
|
import type { Annotation } from '../types'
|
|
5
5
|
|
|
6
6
|
vi.mock('html-to-image', () => ({
|
|
7
|
-
|
|
7
|
+
toJpeg: vi.fn(async () => 'data:image/jpeg;base64,/9j/AA=='),
|
|
8
8
|
}))
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const toJpegMock = vi.mocked(toJpeg)
|
|
11
11
|
|
|
12
12
|
describe('captureScreenshot', () => {
|
|
13
13
|
afterEach(() => {
|
|
14
14
|
document.body.innerHTML = ''
|
|
15
|
-
|
|
15
|
+
toJpegMock.mockClear()
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
-
it('captures a
|
|
18
|
+
it('captures a compressed jpeg blob', async () => {
|
|
19
19
|
const blob = await captureScreenshot()
|
|
20
20
|
|
|
21
21
|
expect(blob).toBeInstanceOf(Blob)
|
|
22
|
-
expect(blob?.type).toBe('image/
|
|
23
|
-
expect(
|
|
22
|
+
expect(blob?.type).toBe('image/jpeg')
|
|
23
|
+
expect(toJpegMock).toHaveBeenCalledWith(
|
|
24
24
|
document.body,
|
|
25
25
|
expect.objectContaining({
|
|
26
|
+
quality: 0.85,
|
|
27
|
+
pixelRatio: 1,
|
|
26
28
|
cacheBust: true,
|
|
27
29
|
skipAutoScale: false,
|
|
28
30
|
}),
|
|
@@ -31,9 +33,9 @@ describe('captureScreenshot', () => {
|
|
|
31
33
|
|
|
32
34
|
it('mounts pin annotations during capture and removes them after capture', async () => {
|
|
33
35
|
let captureText = ''
|
|
34
|
-
|
|
36
|
+
toJpegMock.mockImplementationOnce(async () => {
|
|
35
37
|
captureText = document.querySelector<HTMLElement>('[data-mtb-capture-annotations]')?.textContent ?? ''
|
|
36
|
-
return 'data:image/
|
|
38
|
+
return 'data:image/jpeg;base64,/9j/AA=='
|
|
37
39
|
})
|
|
38
40
|
|
|
39
41
|
const annotation: Annotation = { type: 'pin', x: 40, y: 60 }
|
|
@@ -45,9 +47,9 @@ describe('captureScreenshot', () => {
|
|
|
45
47
|
|
|
46
48
|
it('mounts draw annotations during capture', async () => {
|
|
47
49
|
let drawPath: string | null = null
|
|
48
|
-
|
|
50
|
+
toJpegMock.mockImplementationOnce(async () => {
|
|
49
51
|
drawPath = document.querySelector('[data-mtb-capture-annotations] path')?.getAttribute('d') ?? null
|
|
50
|
-
return 'data:image/
|
|
52
|
+
return 'data:image/jpeg;base64,/9j/AA=='
|
|
51
53
|
})
|
|
52
54
|
|
|
53
55
|
const annotation: Annotation = { type: 'draw', x: 50, y: 50, drawPath: 'M 10 10 L 90 90' }
|
|
@@ -57,7 +59,7 @@ describe('captureScreenshot', () => {
|
|
|
57
59
|
})
|
|
58
60
|
|
|
59
61
|
it('returns null and cleans up if capture fails', async () => {
|
|
60
|
-
|
|
62
|
+
toJpegMock.mockRejectedValueOnce(new Error('capture failed'))
|
|
61
63
|
|
|
62
64
|
const annotation: Annotation = { type: 'pin', x: 40, y: 60 }
|
|
63
65
|
const blob = await captureScreenshot([annotation])
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toJpeg } from 'html-to-image'
|
|
2
2
|
import type { Annotation } from '../types'
|
|
3
3
|
|
|
4
|
+
// Capture at CSS pixels (pixelRatio 1) as JPEG to keep the upload small: on
|
|
5
|
+
// retina screens the default devicePixelRatio would quadruple the pixel count,
|
|
6
|
+
// and the model downscales past ~1568px anyway.
|
|
4
7
|
export async function captureScreenshot(annotations: Annotation[] = []): Promise<Blob | null> {
|
|
5
8
|
const annotationLayer = mountCaptureAnnotations(annotations)
|
|
6
9
|
|
|
7
10
|
try {
|
|
8
|
-
const dataUrl = await
|
|
11
|
+
const dataUrl = await toJpeg(document.body, {
|
|
12
|
+
quality: 0.85,
|
|
13
|
+
pixelRatio: 1,
|
|
14
|
+
backgroundColor: '#ffffff',
|
|
9
15
|
cacheBust: true,
|
|
10
16
|
skipAutoScale: false,
|
|
11
17
|
filter: (node) => {
|
package/src/styles/widget.css
CHANGED
|
@@ -934,142 +934,191 @@
|
|
|
934
934
|
.mtb-clarify {
|
|
935
935
|
position: fixed;
|
|
936
936
|
z-index: 2147483645;
|
|
937
|
-
width:
|
|
937
|
+
width: 346px;
|
|
938
938
|
background: #fff;
|
|
939
939
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
940
|
-
border-radius:
|
|
941
|
-
box-shadow: 0
|
|
940
|
+
border-radius: 14px;
|
|
941
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.16);
|
|
942
942
|
display: flex;
|
|
943
943
|
flex-direction: column;
|
|
944
|
-
animation: mtbSlideUp 0.
|
|
944
|
+
animation: mtbSlideUp 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
945
945
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
946
946
|
overflow: hidden;
|
|
947
947
|
}
|
|
948
948
|
|
|
949
949
|
.mtb-clarify-header {
|
|
950
|
-
padding:
|
|
950
|
+
padding: 11px 13px 11px 14px;
|
|
951
951
|
display: flex;
|
|
952
952
|
align-items: center;
|
|
953
|
-
|
|
953
|
+
gap: 9px;
|
|
954
954
|
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
955
955
|
}
|
|
956
956
|
|
|
957
957
|
.mtb-clarify-icon {
|
|
958
|
-
width:
|
|
959
|
-
height:
|
|
960
|
-
border-radius:
|
|
961
|
-
background:
|
|
958
|
+
width: 24px;
|
|
959
|
+
height: 24px;
|
|
960
|
+
border-radius: 7px;
|
|
961
|
+
background: linear-gradient(140deg, #10b981, #059669);
|
|
962
|
+
box-shadow: 0 1px 3px rgba(5, 150, 105, 0.4);
|
|
962
963
|
display: flex;
|
|
963
964
|
align-items: center;
|
|
964
965
|
justify-content: center;
|
|
965
966
|
flex-shrink: 0;
|
|
966
967
|
}
|
|
967
968
|
|
|
968
|
-
.mtb-clarify-
|
|
969
|
+
.mtb-clarify-titles {
|
|
970
|
+
flex: 1;
|
|
971
|
+
min-width: 0;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.mtb-clarify-title {
|
|
975
|
+
font-size: 12.5px;
|
|
976
|
+
font-weight: 650;
|
|
977
|
+
line-height: 1.2;
|
|
978
|
+
color: #191a1c;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.mtb-clarify-subtitle {
|
|
982
|
+
font-size: 10px;
|
|
983
|
+
color: rgba(0, 0, 0, 0.42);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
.mtb-clarify-close {
|
|
969
987
|
background: none;
|
|
970
988
|
border: none;
|
|
971
989
|
cursor: pointer;
|
|
972
|
-
|
|
973
|
-
color: rgba(0, 0, 0, 0.
|
|
974
|
-
|
|
990
|
+
padding: 3px;
|
|
991
|
+
color: rgba(0, 0, 0, 0.38);
|
|
992
|
+
display: flex;
|
|
975
993
|
transition: color 0.12s;
|
|
976
994
|
}
|
|
977
995
|
|
|
978
|
-
.mtb-clarify-
|
|
996
|
+
.mtb-clarify-close:hover {
|
|
979
997
|
color: rgba(0, 0, 0, 0.7);
|
|
980
998
|
}
|
|
981
999
|
|
|
982
1000
|
.mtb-clarify-messages {
|
|
983
1001
|
flex: 1;
|
|
984
|
-
max-height:
|
|
1002
|
+
max-height: 250px;
|
|
985
1003
|
overflow-y: auto;
|
|
986
|
-
padding:
|
|
1004
|
+
padding: 13px 13px 5px;
|
|
987
1005
|
display: flex;
|
|
988
1006
|
flex-direction: column;
|
|
989
|
-
gap:
|
|
1007
|
+
gap: 9px;
|
|
990
1008
|
}
|
|
991
1009
|
|
|
992
1010
|
.mtb-clarify-bubble {
|
|
993
|
-
max-width:
|
|
994
|
-
padding:
|
|
995
|
-
|
|
996
|
-
font-size: 13px;
|
|
1011
|
+
max-width: 82%;
|
|
1012
|
+
padding: 9px 12px;
|
|
1013
|
+
font-size: 12.5px;
|
|
997
1014
|
line-height: 1.5;
|
|
998
|
-
|
|
1015
|
+
white-space: pre-wrap;
|
|
1016
|
+
word-break: break-word;
|
|
999
1017
|
}
|
|
1000
1018
|
|
|
1001
1019
|
.mtb-clarify-ai {
|
|
1002
1020
|
align-self: flex-start;
|
|
1003
|
-
background: #
|
|
1021
|
+
background: #ffffff;
|
|
1004
1022
|
color: #191a1c;
|
|
1005
|
-
border
|
|
1023
|
+
border: 1px solid rgba(0, 0, 0, 0.09);
|
|
1024
|
+
border-radius: 13px 13px 13px 4px;
|
|
1006
1025
|
}
|
|
1007
1026
|
|
|
1008
1027
|
.mtb-clarify-user {
|
|
1009
1028
|
align-self: flex-end;
|
|
1010
1029
|
background: #059669;
|
|
1011
1030
|
color: #fff;
|
|
1012
|
-
border
|
|
1031
|
+
border: 1px solid #059669;
|
|
1032
|
+
border-radius: 13px 13px 4px 13px;
|
|
1013
1033
|
}
|
|
1014
1034
|
|
|
1015
1035
|
.mtb-clarify-thinking {
|
|
1016
1036
|
display: flex;
|
|
1017
1037
|
align-items: center;
|
|
1018
|
-
gap:
|
|
1019
|
-
padding:
|
|
1038
|
+
gap: 4px;
|
|
1039
|
+
padding: 11px 14px;
|
|
1020
1040
|
}
|
|
1021
1041
|
|
|
1022
1042
|
.mtb-clarify-thinking .mtb-dot {
|
|
1023
|
-
|
|
1043
|
+
width: 6px;
|
|
1044
|
+
height: 6px;
|
|
1045
|
+
background: rgba(5, 150, 105, 0.7);
|
|
1024
1046
|
}
|
|
1025
1047
|
|
|
1026
1048
|
.mtb-clarify-footer {
|
|
1027
|
-
padding:
|
|
1028
|
-
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
|
1049
|
+
padding: 3px 13px 13px;
|
|
1029
1050
|
display: flex;
|
|
1030
|
-
|
|
1051
|
+
flex-direction: column;
|
|
1031
1052
|
}
|
|
1032
1053
|
|
|
1033
1054
|
.mtb-clarify-input {
|
|
1034
|
-
|
|
1035
|
-
height:
|
|
1036
|
-
padding:
|
|
1055
|
+
width: 100%;
|
|
1056
|
+
height: 52px;
|
|
1057
|
+
padding: 9px 11px;
|
|
1037
1058
|
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
1038
|
-
border-radius:
|
|
1059
|
+
border-radius: 9px;
|
|
1039
1060
|
font-family: inherit;
|
|
1040
|
-
font-size:
|
|
1061
|
+
font-size: 13px;
|
|
1062
|
+
line-height: 1.5;
|
|
1041
1063
|
color: #191a1c;
|
|
1042
1064
|
outline: none;
|
|
1043
|
-
background: #
|
|
1044
|
-
|
|
1065
|
+
background: #fafafa;
|
|
1066
|
+
resize: none;
|
|
1067
|
+
box-sizing: border-box;
|
|
1068
|
+
transition: border-color 0.12s, box-shadow 0.12s, background 0.12s;
|
|
1045
1069
|
}
|
|
1046
1070
|
|
|
1047
1071
|
.mtb-clarify-input:focus {
|
|
1048
1072
|
border-color: #10b981;
|
|
1049
1073
|
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
|
|
1074
|
+
background: #fff;
|
|
1050
1075
|
}
|
|
1051
1076
|
|
|
1052
1077
|
.mtb-clarify-input::placeholder {
|
|
1053
1078
|
color: rgba(0, 0, 0, 0.34);
|
|
1054
1079
|
}
|
|
1055
1080
|
|
|
1056
|
-
.mtb-clarify-
|
|
1057
|
-
|
|
1081
|
+
.mtb-clarify-actions {
|
|
1082
|
+
display: flex;
|
|
1083
|
+
gap: 8px;
|
|
1084
|
+
margin-top: 9px;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
.mtb-clarify-skip {
|
|
1088
|
+
flex: 0 0 auto;
|
|
1089
|
+
height: 34px;
|
|
1058
1090
|
padding: 0 14px;
|
|
1059
|
-
border:
|
|
1060
|
-
border-radius:
|
|
1061
|
-
background: #
|
|
1062
|
-
color: #fff;
|
|
1091
|
+
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
1092
|
+
border-radius: 9px;
|
|
1093
|
+
background: #fff;
|
|
1063
1094
|
cursor: pointer;
|
|
1064
1095
|
font-family: inherit;
|
|
1065
|
-
font-size:
|
|
1066
|
-
font-weight:
|
|
1096
|
+
font-size: 12.5px;
|
|
1097
|
+
font-weight: 550;
|
|
1098
|
+
color: rgba(0, 0, 0, 0.55);
|
|
1067
1099
|
transition: background 0.12s;
|
|
1068
|
-
flex-shrink: 0;
|
|
1069
1100
|
}
|
|
1070
1101
|
|
|
1071
|
-
.mtb-clarify-
|
|
1102
|
+
.mtb-clarify-skip:hover {
|
|
1103
|
+
background: rgba(0, 0, 0, 0.04);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.mtb-clarify-send {
|
|
1107
|
+
flex: 1;
|
|
1108
|
+
height: 34px;
|
|
1109
|
+
display: inline-flex;
|
|
1110
|
+
align-items: center;
|
|
1111
|
+
justify-content: center;
|
|
1112
|
+
gap: 6px;
|
|
1113
|
+
border: none;
|
|
1114
|
+
border-radius: 9px;
|
|
1072
1115
|
background: #059669;
|
|
1116
|
+
color: #fff;
|
|
1117
|
+
cursor: pointer;
|
|
1118
|
+
font-family: inherit;
|
|
1119
|
+
font-size: 12.5px;
|
|
1120
|
+
font-weight: 600;
|
|
1121
|
+
transition: background 0.12s;
|
|
1073
1122
|
}
|
|
1074
1123
|
|
|
1075
1124
|
.mtb-clarify-send:not(:disabled):hover {
|
|
@@ -1077,6 +1126,7 @@
|
|
|
1077
1126
|
}
|
|
1078
1127
|
|
|
1079
1128
|
.mtb-clarify-send:disabled {
|
|
1129
|
+
background: rgba(0, 0, 0, 0.15);
|
|
1080
1130
|
cursor: not-allowed;
|
|
1081
1131
|
}
|
|
1082
1132
|
|
|
@@ -1090,20 +1140,35 @@
|
|
|
1090
1140
|
border-bottom-color: rgba(255, 255, 255, 0.1);
|
|
1091
1141
|
}
|
|
1092
1142
|
|
|
1093
|
-
:host([data-mtb-theme="dark"]) .mtb-clarify-
|
|
1094
|
-
|
|
1143
|
+
:host([data-mtb-theme="dark"]) .mtb-clarify-title {
|
|
1144
|
+
color: #f9fafb;
|
|
1095
1145
|
}
|
|
1096
1146
|
|
|
1097
|
-
:host([data-mtb-theme="dark"]) .mtb-clarify-
|
|
1147
|
+
:host([data-mtb-theme="dark"]) .mtb-clarify-subtitle {
|
|
1098
1148
|
color: rgba(255, 255, 255, 0.45);
|
|
1099
1149
|
}
|
|
1100
1150
|
|
|
1101
|
-
:host([data-mtb-theme="dark"]) .mtb-clarify-
|
|
1151
|
+
:host([data-mtb-theme="dark"]) .mtb-clarify-close {
|
|
1152
|
+
color: rgba(255, 255, 255, 0.45);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
:host([data-mtb-theme="dark"]) .mtb-clarify-close:hover {
|
|
1156
|
+
color: rgba(255, 255, 255, 0.75);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
:host([data-mtb-theme="dark"]) .mtb-clarify-skip {
|
|
1160
|
+
background: #1f2327;
|
|
1161
|
+
border-color: rgba(255, 255, 255, 0.14);
|
|
1102
1162
|
color: rgba(255, 255, 255, 0.7);
|
|
1103
1163
|
}
|
|
1104
1164
|
|
|
1165
|
+
:host([data-mtb-theme="dark"]) .mtb-clarify-skip:hover {
|
|
1166
|
+
background: rgba(255, 255, 255, 0.06);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1105
1169
|
:host([data-mtb-theme="dark"]) .mtb-clarify-ai {
|
|
1106
1170
|
background: #1f2327;
|
|
1171
|
+
border-color: rgba(255, 255, 255, 0.12);
|
|
1107
1172
|
color: #f9fafb;
|
|
1108
1173
|
}
|
|
1109
1174
|
|
|
@@ -1124,20 +1189,35 @@
|
|
|
1124
1189
|
border-bottom-color: rgba(255, 255, 255, 0.1);
|
|
1125
1190
|
}
|
|
1126
1191
|
|
|
1127
|
-
:host([data-mtb-theme="auto"]) .mtb-clarify-
|
|
1128
|
-
|
|
1192
|
+
:host([data-mtb-theme="auto"]) .mtb-clarify-title {
|
|
1193
|
+
color: #f9fafb;
|
|
1129
1194
|
}
|
|
1130
1195
|
|
|
1131
|
-
:host([data-mtb-theme="auto"]) .mtb-clarify-
|
|
1196
|
+
:host([data-mtb-theme="auto"]) .mtb-clarify-subtitle {
|
|
1132
1197
|
color: rgba(255, 255, 255, 0.45);
|
|
1133
1198
|
}
|
|
1134
1199
|
|
|
1135
|
-
:host([data-mtb-theme="auto"]) .mtb-clarify-
|
|
1200
|
+
:host([data-mtb-theme="auto"]) .mtb-clarify-close {
|
|
1201
|
+
color: rgba(255, 255, 255, 0.45);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
:host([data-mtb-theme="auto"]) .mtb-clarify-close:hover {
|
|
1205
|
+
color: rgba(255, 255, 255, 0.75);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
:host([data-mtb-theme="auto"]) .mtb-clarify-skip {
|
|
1209
|
+
background: #1f2327;
|
|
1210
|
+
border-color: rgba(255, 255, 255, 0.14);
|
|
1136
1211
|
color: rgba(255, 255, 255, 0.7);
|
|
1137
1212
|
}
|
|
1138
1213
|
|
|
1214
|
+
:host([data-mtb-theme="auto"]) .mtb-clarify-skip:hover {
|
|
1215
|
+
background: rgba(255, 255, 255, 0.06);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1139
1218
|
:host([data-mtb-theme="auto"]) .mtb-clarify-ai {
|
|
1140
1219
|
background: #1f2327;
|
|
1220
|
+
border-color: rgba(255, 255, 255, 0.12);
|
|
1141
1221
|
color: #f9fafb;
|
|
1142
1222
|
}
|
|
1143
1223
|
|