mesauth-angular 1.25.0 → 1.25.1
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.
|
@@ -10,7 +10,7 @@ import { DomSanitizer } from '@angular/platform-browser';
|
|
|
10
10
|
import { DatePipe, JsonPipe } from '@angular/common';
|
|
11
11
|
|
|
12
12
|
/** Current installed package version — keep in sync with package.json. */
|
|
13
|
-
const PACKAGE_VERSION = '1.25.
|
|
13
|
+
const PACKAGE_VERSION = '1.25.1';
|
|
14
14
|
/**
|
|
15
15
|
* Provides server-driven UI configuration loaded from the hosted manifest.
|
|
16
16
|
* Components read `labels()` and `features()` signals instead of hardcoded strings.
|
|
@@ -839,11 +839,11 @@ class MaAiToolsRegistry {
|
|
|
839
839
|
builtIn = [
|
|
840
840
|
{
|
|
841
841
|
name: 'navigate',
|
|
842
|
-
description: 'Navigate the
|
|
842
|
+
description: 'Navigate the browser to a frontend route path. The path must come from list_frontend_routes — do not guess or construct paths from backend operation names.',
|
|
843
843
|
parameters: {
|
|
844
844
|
type: 'object',
|
|
845
845
|
properties: {
|
|
846
|
-
path: { type: 'string', description: '
|
|
846
|
+
path: { type: 'string', description: 'Frontend route path returned by list_frontend_routes, e.g. "/auth/users"' }
|
|
847
847
|
},
|
|
848
848
|
required: ['path']
|
|
849
849
|
},
|
|
@@ -1039,6 +1039,13 @@ class MaAiService {
|
|
|
1039
1039
|
this.markToolStatus(q.callId, 'ok', answer);
|
|
1040
1040
|
await this.continueWithToolResult(q.callId, true, answer);
|
|
1041
1041
|
}
|
|
1042
|
+
/** Toggle the expanded/collapsed state of a tool chip with detail content. */
|
|
1043
|
+
toggleToolExpanded(callId) {
|
|
1044
|
+
this.messages.update(arr => arr.map(m => ({
|
|
1045
|
+
...m,
|
|
1046
|
+
toolEvents: m.toolEvents?.map(t => t.id === callId ? { ...t, expanded: !t.expanded } : t)
|
|
1047
|
+
})));
|
|
1048
|
+
}
|
|
1042
1049
|
/** User typed and submitted a message. */
|
|
1043
1050
|
async send(text, currentRoute) {
|
|
1044
1051
|
if (!text.trim())
|
|
@@ -1173,7 +1180,7 @@ class MaAiService {
|
|
|
1173
1180
|
});
|
|
1174
1181
|
break;
|
|
1175
1182
|
case 'tool_call_completed':
|
|
1176
|
-
this.markToolStatus(ev.id, ev.ok ? 'ok' : 'error', ev.summary);
|
|
1183
|
+
this.markToolStatus(ev.id, ev.ok ? 'ok' : 'error', ev.summary, ev.detail);
|
|
1177
1184
|
break;
|
|
1178
1185
|
case 'client_tool_call':
|
|
1179
1186
|
if (ev.name === 'ask_user') {
|
|
@@ -1222,7 +1229,7 @@ class MaAiService {
|
|
|
1222
1229
|
async executeClientTool(callId, name, args) {
|
|
1223
1230
|
this.markToolStatus(callId, 'running');
|
|
1224
1231
|
const { ok, result } = await this.tools.runTool(name, args);
|
|
1225
|
-
this.markToolStatus(callId, ok ? 'ok' : 'error', this.summarize(result));
|
|
1232
|
+
this.markToolStatus(callId, ok ? 'ok' : 'error', this.summarize(result), this.prettyDetail(result));
|
|
1226
1233
|
await this.continueWithToolResult(callId, ok, result);
|
|
1227
1234
|
}
|
|
1228
1235
|
async continueWithToolResult(callId, ok, result) {
|
|
@@ -1255,10 +1262,12 @@ class MaAiService {
|
|
|
1255
1262
|
? { ...m, toolEvents: [...(m.toolEvents ?? []), ev] }
|
|
1256
1263
|
: m));
|
|
1257
1264
|
}
|
|
1258
|
-
markToolStatus(callId, status, summary) {
|
|
1265
|
+
markToolStatus(callId, status, summary, detail) {
|
|
1259
1266
|
this.messages.update(arr => arr.map(m => ({
|
|
1260
1267
|
...m,
|
|
1261
|
-
toolEvents: m.toolEvents?.map(t => t.id === callId
|
|
1268
|
+
toolEvents: m.toolEvents?.map(t => t.id === callId
|
|
1269
|
+
? { ...t, status, summary: summary ?? t.summary, detail: detail ?? t.detail }
|
|
1270
|
+
: t)
|
|
1262
1271
|
})));
|
|
1263
1272
|
}
|
|
1264
1273
|
finalizeAssistant(extra) {
|
|
@@ -1273,10 +1282,64 @@ class MaAiService {
|
|
|
1273
1282
|
verbOf(name) {
|
|
1274
1283
|
return name.split('_')[0] ?? name;
|
|
1275
1284
|
}
|
|
1285
|
+
/** Short plain-English label for the chip — never raw JSON. */
|
|
1276
1286
|
summarize(s) {
|
|
1277
|
-
const
|
|
1287
|
+
const t = (s ?? '').trim();
|
|
1288
|
+
if (!t)
|
|
1289
|
+
return 'ok';
|
|
1290
|
+
const first = t[0];
|
|
1291
|
+
if (first === '{' || first === '[') {
|
|
1292
|
+
try {
|
|
1293
|
+
return this.summarizeJson(JSON.parse(t));
|
|
1294
|
+
}
|
|
1295
|
+
catch { /* fall through */ }
|
|
1296
|
+
}
|
|
1297
|
+
const flat = t.replace(/\s+/g, ' ');
|
|
1278
1298
|
return flat.length <= 80 ? flat : flat.slice(0, 80) + '…';
|
|
1279
1299
|
}
|
|
1300
|
+
summarizeJson(v) {
|
|
1301
|
+
if (Array.isArray(v))
|
|
1302
|
+
return `${v.length} items`;
|
|
1303
|
+
if (v && typeof v === 'object') {
|
|
1304
|
+
for (const k of ['total', 'count', 'returned', 'length']) {
|
|
1305
|
+
if (typeof v[k] === 'number')
|
|
1306
|
+
return `${v[k]} ${v[k] === 1 ? 'item' : 'items'}`;
|
|
1307
|
+
}
|
|
1308
|
+
for (const k of Object.keys(v)) {
|
|
1309
|
+
if (Array.isArray(v[k])) {
|
|
1310
|
+
const n = v[k].length;
|
|
1311
|
+
return `${n} ${n === 1 ? k.replace(/s$/, '') : k}`;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
const keys = Object.keys(v);
|
|
1315
|
+
if (keys.length === 1) {
|
|
1316
|
+
const k = keys[0];
|
|
1317
|
+
const val = v[k];
|
|
1318
|
+
if (val === null || typeof val !== 'object')
|
|
1319
|
+
return `${k}: ${String(val)}`;
|
|
1320
|
+
}
|
|
1321
|
+
return `${keys.length} fields`;
|
|
1322
|
+
}
|
|
1323
|
+
if (typeof v === 'string')
|
|
1324
|
+
return v.length <= 80 ? v : v.slice(0, 80) + '…';
|
|
1325
|
+
return String(v);
|
|
1326
|
+
}
|
|
1327
|
+
/** Pretty-printed JSON for the expandable panel; null when content isn't JSON. */
|
|
1328
|
+
prettyDetail(s) {
|
|
1329
|
+
const t = (s ?? '').trim();
|
|
1330
|
+
if (!t)
|
|
1331
|
+
return undefined;
|
|
1332
|
+
const first = t[0];
|
|
1333
|
+
if (first !== '{' && first !== '[')
|
|
1334
|
+
return undefined;
|
|
1335
|
+
try {
|
|
1336
|
+
const pretty = JSON.stringify(JSON.parse(t), null, 2);
|
|
1337
|
+
return pretty.length <= 4096 ? pretty : pretty.slice(0, 4096) + '\n...[truncated]';
|
|
1338
|
+
}
|
|
1339
|
+
catch {
|
|
1340
|
+
return undefined;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1280
1343
|
uid() {
|
|
1281
1344
|
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
1282
1345
|
}
|
|
@@ -1401,8 +1464,8 @@ class MaAiService {
|
|
|
1401
1464
|
const ev = lastAssistant.toolEvents?.find(e => e.id === (m.toolCallId ?? ''));
|
|
1402
1465
|
if (ev) {
|
|
1403
1466
|
ev.status = m.toolIsError ? 'error' : 'ok';
|
|
1404
|
-
|
|
1405
|
-
ev.
|
|
1467
|
+
ev.summary = this.summarize(m.content ?? '');
|
|
1468
|
+
ev.detail = this.prettyDetail(m.content ?? '');
|
|
1406
1469
|
}
|
|
1407
1470
|
}
|
|
1408
1471
|
}
|
|
@@ -2828,6 +2891,11 @@ class MaAiChatPanelComponent {
|
|
|
2828
2891
|
alwaysApprove() { this.ai.resolvePendingApproval('always'); }
|
|
2829
2892
|
decline() { this.ai.resolvePendingApproval('decline'); }
|
|
2830
2893
|
answerQuestion(answer) { this.ai.resolveQuestion(answer); }
|
|
2894
|
+
onToolChipClick(t) {
|
|
2895
|
+
if (!t.detail)
|
|
2896
|
+
return;
|
|
2897
|
+
this.ai.toggleToolExpanded(t.id);
|
|
2898
|
+
}
|
|
2831
2899
|
submitQuestionInput() {
|
|
2832
2900
|
const text = this.questionInput().trim();
|
|
2833
2901
|
if (!text)
|
|
@@ -2881,11 +2949,11 @@ class MaAiChatPanelComponent {
|
|
|
2881
2949
|
el.scrollTop = el.scrollHeight;
|
|
2882
2950
|
}
|
|
2883
2951
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: MaAiChatPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2884
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: MaAiChatPanelComponent, isStandalone: true, selector: "ma-ai-chat-panel", host: { properties: { "class": "this.themeClass" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }, { propertyName: "textArea", first: true, predicate: ["textArea"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ai-panel\" [class.open]=\"isOpen()\" [style.width.px]=\"width()\">\n\n <!-- Resize handle (left edge) -->\n <div class=\"ai-resize-handle\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResizeMove($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n aria-label=\"Resize panel\"\n title=\"Drag to resize\"></div>\n\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-header-left\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n <path d=\"M19 16 L19.8 18.2 L22 19 L19.8 19.8 L19 22 L18.2 19.8 L16 19 L18.2 18.2 Z\"/>\n </svg>\n <h3>AI Assistant</h3>\n </div>\n <div class=\"header-actions\">\n <button class=\"icon-btn\" (click)=\"newConversation()\" title=\"New conversation\" aria-label=\"New conversation\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14\"/><path d=\"M5 12h14\"/>\n </svg>\n </button>\n <button class=\"icon-btn\" (click)=\"toggleHistory()\" [class.active]=\"historyOpen()\" title=\"History\" aria-label=\"History\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/>\n </svg>\n </button>\n <button class=\"close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n </div>\n\n @if (historyOpen()) {\n <ma-ai-history-list (resumed)=\"onHistoryResumed($event)\"></ma-ai-history-list>\n }\n\n <!-- Conversation -->\n <div class=\"panel-content\" #scrollContainer>\n @if (!hasMessages()) {\n <div class=\"empty-state\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"36\" height=\"36\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\"0.5\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n </svg>\n <p>Ask anything about your account, approvals, notifications, or how to use the app.</p>\n <div class=\"quick-prompts\">\n @for (q of quickPrompts(); track q) {\n <button class=\"quick-prompt\" (click)=\"draft.set(q)\">{{ q }}</button>\n }\n </div>\n </div>\n }\n\n @for (m of messages(); track m.id) {\n <div class=\"bubble\" [class.user]=\"m.role === 'user'\" [class.assistant]=\"m.role === 'assistant'\">\n @if (m.role === 'assistant' && m.toolEvents && m.toolEvents.length > 0) {\n <div class=\"tool-strip\">\n @for (t of m.toolEvents; track t.id) {\n <div class=\"tool-chip\" [class.ok]=\"t.status === 'ok'\" [class.err]=\"t.status === 'error'\"\n [class.running]=\"t.status === 'running'\" [class.await]=\"t.status === 'awaiting-approval'\"\n [class.declined]=\"t.status === 'declined'\"\n [title]=\"t.summary ?? ''\">\n <span class=\"dot\"></span>\n <span class=\"tool-name\">{{ t.side === 'server' ? '\u2699' : '\u2318' }} {{ t.name }}</span>\n @if (t.status === 'running') { <span class=\"muted\">\u2026</span> }\n @if (t.status === 'ok' && t.summary) { <span class=\"muted\">\u00B7 {{ t.summary }}</span> }\n @if (t.status === 'error') { <span class=\"muted\">\u00B7 failed</span> }\n @if (t.status === 'awaiting-approval') { <span class=\"muted\">\u00B7 awaiting approval</span> }\n @if (t.status === 'declined') { <span class=\"muted\">\u00B7 declined</span> }\n </div>\n }\n </div>\n }\n @if (m.text) {\n @if (m.role === 'assistant') {\n <div class=\"bubble-text md-body\" [innerHTML]=\"m.text | maAiMarkdown\"></div>\n } @else {\n <div class=\"bubble-text\">{{ m.text }}</div>\n }\n }\n @if (m.pending && !m.text) {\n <div class=\"thinking\"><span class=\"spinner-small\"></span> Thinking\u2026</div>\n }\n </div>\n }\n </div>\n\n <!-- Pending approval card -->\n @if (pendingApproval(); as p) {\n <div class=\"approval-card\">\n <div class=\"approval-card-head\">\n <strong>Approve action?</strong>\n <code class=\"tool-name\">{{ p.name }}</code>\n </div>\n @if (p.args && (p.args | json) !== '{}') {\n <pre class=\"approval-args\">{{ p.args | json }}</pre>\n }\n <div class=\"approval-actions\">\n <button class=\"btn primary\" (click)=\"approve()\">Approve</button>\n <button class=\"btn\" (click)=\"alwaysApprove()\">Always</button>\n <button class=\"btn danger\" (click)=\"decline()\">Decline</button>\n </div>\n </div>\n }\n\n <!-- Question card (ask_user tool) -->\n @if (pendingQuestion(); as q) {\n <div class=\"question-card\">\n <p class=\"question-text\">{{ q.message }}</p>\n @if (q.options && q.options.length > 0) {\n <div class=\"question-options\">\n @for (opt of q.options; track opt) {\n <button class=\"btn question-option\" (click)=\"answerQuestion(opt)\">{{ opt }}</button>\n }\n </div>\n } @else {\n <div class=\"question-input-row\">\n <input type=\"text\"\n class=\"question-input\"\n placeholder=\"Type your answer\u2026\"\n [value]=\"questionInput()\"\n (input)=\"questionInput.set($any($event.target).value)\"\n (keydown.enter)=\"submitQuestionInput()\"\n autofocus />\n <button class=\"btn primary\" (click)=\"submitQuestionInput()\" [disabled]=\"!questionInput().trim()\">Send</button>\n </div>\n }\n </div>\n }\n\n <!-- Composer -->\n <div class=\"composer\">\n @if (lastError()) {\n <div class=\"composer-error\">{{ lastError() }}</div>\n }\n <div class=\"composer-row\">\n <textarea #textArea\n rows=\"1\"\n placeholder=\"Ask the AI\u2026\"\n [value]=\"draft()\"\n (input)=\"onTextareaInput($event)\"\n (keydown)=\"onTextareaKey($event)\"\n [disabled]=\"streaming()\"></textarea>\n @if (streaming()) {\n <button class=\"send-btn cancel\" (click)=\"cancel()\" title=\"Cancel\" aria-label=\"Cancel\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><rect x=\"6\" y=\"6\" width=\"12\" height=\"12\"/></svg>\n </button>\n } @else {\n <button class=\"send-btn\" (click)=\"send()\" [disabled]=\"!draft().trim()\" title=\"Send\" aria-label=\"Send\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>\n </button>\n }\n </div>\n </div>\n\n <!-- Footer with hints (lifts the composer off the absolute bottom of the viewport) -->\n <div class=\"ai-footer\">\n <span class=\"hint\">\n <kbd>Enter</kbd> send \u00B7 <kbd>Shift</kbd>+<kbd>Enter</kbd> new line\n </span>\n <span class=\"hint dim\">AI can make mistakes — verify important info</span>\n </div>\n</div>\n", styles: [".panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary)}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.close-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:11px 8px;background:none;border:none;border-bottom:2px solid transparent;color:var(--text-muted);cursor:pointer;font-size:13px;font-weight:500;transition:color .15s,border-color .15s}.tab-btn.active{color:var(--primary);border-bottom-color:var(--primary)}.tab-btn:hover:not(.active){color:var(--text-secondary)}.tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;background:var(--primary);color:#fff;font-size:11px;font-weight:700;border-radius:9px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:12px;color:var(--text-muted)}.empty-state p{margin:0;font-size:13px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;gap:12px;color:var(--text-muted);font-size:13px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n", ":host{--primary: #c4b5fd;--primary-strong: #a78bfa;--success: #66bb6a;--error: #ef5350;--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--bg-bubble-user: #312e81;--bg-bubble-assistant: #232336;--border-color: #383850;--shadow: rgba(0,0,0,.4)}:host(.theme-light){--primary: #7c3aed;--primary-strong: #6d28d9;--success: #2e7d32;--error: #c62828;--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f5f3ff;--bg-hover: #ede9fe;--bg-bubble-user: #ede9fe;--bg-bubble-assistant: #f5f5f5;--border-color: #e0e0e0;--shadow: rgba(0,0,0,.15)}.ai-panel{position:fixed;top:0;right:0;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1040;transform:translate(100%);transition:transform .3s cubic-bezier(.16,1,.3,1);will-change:transform,width;pointer-events:none}.ai-panel.open{transform:translate(0);pointer-events:auto}.ai-resize-handle{position:absolute;top:0;left:-4px;width:8px;height:100%;cursor:ew-resize;z-index:1;background:transparent}.ai-resize-handle:hover{background:linear-gradient(to right,transparent,rgba(124,58,237,.25),transparent)}.header-actions{display:flex;align-items:center;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.icon-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.panel-content{flex:1;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:10px}.quick-prompts{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-top:8px}.quick-prompt{background:var(--bg-secondary);border:1px solid var(--border-color);color:var(--text-secondary);padding:6px 10px;border-radius:14px;font-size:12px;cursor:pointer;transition:background .15s,color .15s}.quick-prompt:hover{background:var(--bg-hover);color:var(--text-primary)}.bubble{max-width:90%;padding:10px 12px;border-radius:10px;font-size:13.5px;line-height:1.5;word-wrap:break-word;white-space:pre-wrap}.bubble.user{align-self:flex-end;background:var(--bg-bubble-user);color:var(--text-primary)}.bubble.assistant{align-self:flex-start;background:var(--bg-bubble-assistant);color:var(--text-primary)}.bubble-text{display:block}.thinking{display:flex;align-items:center;gap:6px;color:var(--text-muted);font-size:12.5px}.spinner-small{width:12px;height:12px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.tool-strip{display:flex;flex-direction:column;gap:4px;margin-bottom:6px}.tool-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border-radius:12px;background:var(--bg-hover);font-size:11.5px;color:var(--text-secondary);border:1px solid var(--border-color);align-self:flex-start}.tool-chip .dot{width:6px;height:6px;border-radius:50%;background:var(--text-muted)}.tool-chip.running .dot{background:var(--primary);animation:pulse 1s ease-in-out infinite}.tool-chip.ok .dot{background:var(--success)}.tool-chip.err .dot{background:var(--error)}.tool-chip.await .dot{background:#f59e0b}.tool-chip.declined .dot{background:var(--text-muted)}.tool-chip .tool-name{font-weight:600;color:var(--text-primary)}.tool-chip .muted{color:var(--text-muted)}@keyframes pulse{50%{opacity:.4}}.approval-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--primary);border-radius:10px;background:var(--bg-secondary)}.approval-card-head{display:flex;align-items:center;gap:10px;margin-bottom:8px;color:var(--text-primary)}.approval-card-head code{background:var(--bg-hover);padding:2px 6px;border-radius:6px;font-size:12px;color:var(--primary)}.approval-args{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:6px;padding:8px;font-size:11.5px;max-height:100px;overflow:auto;margin:0 0 8px;color:var(--text-secondary)}.approval-actions{display:flex;gap:6px;flex-wrap:wrap}.btn{flex:1;padding:6px 10px;font-size:12.5px;font-weight:600;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);cursor:pointer;transition:background .15s,border-color .15s}.btn:hover{background:var(--bg-hover)}.btn.primary{background:var(--primary);color:#fff;border-color:var(--primary)}.btn.primary:hover{background:var(--primary-strong)}.btn.danger{color:var(--error)}.btn.danger:hover{background:#ef53501a}.question-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--border-color);border-radius:10px;background:var(--bg-secondary)}.question-text{margin:0 0 10px;font-size:13px;color:var(--text-primary);line-height:1.5}.question-options{display:flex;flex-wrap:wrap;gap:6px}.question-option{flex:0 1 auto}.question-input-row{display:flex;gap:6px;align-items:center}.question-input{flex:1;padding:6px 10px;font-size:13px;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);outline:none}.question-input:focus{border-color:var(--primary)}.composer{flex-shrink:0;border-top:1px solid var(--border-color);padding:10px 12px;background:var(--bg-secondary)}.composer-error{color:var(--error);font-size:12px;margin-bottom:6px}.composer-row{display:flex;align-items:flex-end;gap:8px}.composer textarea{flex:1;resize:none;padding:8px 10px;border-radius:8px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);font-size:13px;font-family:inherit;outline:none;min-height:36px;max-height:160px;line-height:1.4}.composer textarea:focus{border-color:var(--primary)}.composer textarea:disabled{opacity:.6}.send-btn{width:36px;height:36px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--primary);color:#fff;border:none;border-radius:8px;cursor:pointer;transition:background .15s}.send-btn:hover:not(:disabled){background:var(--primary-strong)}.send-btn:disabled{opacity:.5;cursor:not-allowed}.send-btn.cancel{background:var(--error)}.send-btn.cancel:hover{filter:brightness(1.1)}.ai-footer{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 12px 10px;background:var(--bg-secondary);border-top:1px solid var(--border-color);font-size:11px;color:var(--text-secondary);line-height:1.4;text-align:center}.ai-footer .hint{display:inline-block}.ai-footer .hint.dim{color:var(--text-muted);font-size:10.5px}.ai-footer kbd{display:inline-block;padding:0 5px;margin:0 1px;border:1px solid var(--border-color);border-bottom-width:2px;border-radius:4px;background:var(--bg-primary);color:var(--text-primary);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;line-height:1.4}.md-body{font-size:13.5px;line-height:1.55}.md-body .md-p{margin:0 0 8px}.md-body .md-p:last-child{margin-bottom:0}.md-body .md-h1,.md-body .md-h2,.md-body .md-h3,.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{margin:12px 0 6px;font-weight:700;color:var(--text-primary);line-height:1.3}.md-body .md-h1{font-size:17px}.md-body .md-h2{font-size:15.5px}.md-body .md-h3{font-size:14.5px}.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{font-size:13.5px}.md-body strong{font-weight:700}.md-body em{font-style:italic}.md-body .md-link{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.md-body .md-link:hover{color:var(--primary-strong)}.md-body .md-list{margin:0 0 8px;padding-left:22px}.md-body .md-list li{margin:2px 0}.md-body .md-list li::marker{color:var(--text-muted)}.md-body .md-quote{margin:0 0 8px;padding:6px 10px;border-left:3px solid var(--primary);background:var(--bg-hover);color:var(--text-secondary);border-radius:0 6px 6px 0}.md-body .md-code{background:var(--bg-hover);border:1px solid var(--border-color);border-radius:4px;padding:1px 5px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--primary)}.md-body .md-pre{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:10px 12px;margin:0 0 8px;overflow-x:auto;font-size:12px;line-height:1.5}.md-body .md-pre code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--text-primary);background:none;border:none;padding:0}.md-body .md-table-wrap{margin:0 0 8px;overflow-x:auto;border:1px solid var(--border-color);border-radius:8px}.md-body .md-table{border-collapse:collapse;width:100%;font-size:12.5px}.md-body .md-table th,.md-body .md-table td{padding:6px 10px;border-bottom:1px solid var(--border-color);vertical-align:top;text-align:left}.md-body .md-table th{background:var(--bg-secondary);color:var(--text-primary);font-weight:600}.md-body .md-table tr:last-child td{border-bottom:none}.md-body .md-table tbody tr:hover{background:var(--bg-hover)}\n"], dependencies: [{ kind: "component", type: MaAiHistoryListComponent, selector: "ma-ai-history-list", outputs: ["resumed"] }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: MaAiMarkdownPipe, name: "maAiMarkdown" }] });
|
|
2952
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: MaAiChatPanelComponent, isStandalone: true, selector: "ma-ai-chat-panel", host: { properties: { "class": "this.themeClass" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }, { propertyName: "textArea", first: true, predicate: ["textArea"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ai-panel\" [class.open]=\"isOpen()\" [style.width.px]=\"width()\">\n\n <!-- Resize handle (left edge) -->\n <div class=\"ai-resize-handle\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResizeMove($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n aria-label=\"Resize panel\"\n title=\"Drag to resize\"></div>\n\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-header-left\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n <path d=\"M19 16 L19.8 18.2 L22 19 L19.8 19.8 L19 22 L18.2 19.8 L16 19 L18.2 18.2 Z\"/>\n </svg>\n <h3>AI Assistant</h3>\n </div>\n <div class=\"header-actions\">\n <button class=\"icon-btn\" (click)=\"newConversation()\" title=\"New conversation\" aria-label=\"New conversation\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14\"/><path d=\"M5 12h14\"/>\n </svg>\n </button>\n <button class=\"icon-btn\" (click)=\"toggleHistory()\" [class.active]=\"historyOpen()\" title=\"History\" aria-label=\"History\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/>\n </svg>\n </button>\n <button class=\"close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n </div>\n\n @if (historyOpen()) {\n <ma-ai-history-list (resumed)=\"onHistoryResumed($event)\"></ma-ai-history-list>\n }\n\n <!-- Conversation -->\n <div class=\"panel-content\" #scrollContainer>\n @if (!hasMessages()) {\n <div class=\"empty-state\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"36\" height=\"36\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\"0.5\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n </svg>\n <p>Ask anything about your account, approvals, notifications, or how to use the app.</p>\n <div class=\"quick-prompts\">\n @for (q of quickPrompts(); track q) {\n <button class=\"quick-prompt\" (click)=\"draft.set(q)\">{{ q }}</button>\n }\n </div>\n </div>\n }\n\n @for (m of messages(); track m.id) {\n <div class=\"bubble\" [class.user]=\"m.role === 'user'\" [class.assistant]=\"m.role === 'assistant'\">\n @if (m.role === 'assistant' && m.toolEvents && m.toolEvents.length > 0) {\n <div class=\"tool-strip\">\n @for (t of m.toolEvents; track t.id) {\n <div class=\"tool-chip-wrap\">\n <div class=\"tool-chip\" [class.ok]=\"t.status === 'ok'\" [class.err]=\"t.status === 'error'\"\n [class.running]=\"t.status === 'running'\" [class.await]=\"t.status === 'awaiting-approval'\"\n [class.declined]=\"t.status === 'declined'\"\n [class.expandable]=\"!!t.detail\"\n [class.expanded]=\"t.expanded === true\"\n (click)=\"onToolChipClick(t)\"\n [attr.role]=\"t.detail ? 'button' : null\"\n [attr.tabindex]=\"t.detail ? 0 : null\"\n [attr.aria-expanded]=\"t.detail ? (t.expanded === true) : null\"\n [title]=\"t.detail ? (t.expanded ? 'Click to collapse' : 'Click to view full result') : ''\">\n <span class=\"dot\"></span>\n <span class=\"tool-name\">{{ t.side === 'server' ? '\u2699' : '\u2318' }} {{ t.name }}</span>\n @if (t.status === 'running') { <span class=\"muted\">\u2026</span> }\n @if (t.status === 'ok' && t.summary) { <span class=\"muted\">\u00B7 {{ t.summary }}</span> }\n @if (t.status === 'error') { <span class=\"muted\">\u00B7 failed</span> }\n @if (t.status === 'awaiting-approval') { <span class=\"muted\">\u00B7 awaiting approval</span> }\n @if (t.status === 'declined') { <span class=\"muted\">\u00B7 declined</span> }\n @if (t.detail) {\n <svg class=\"caret\" xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\"/>\n </svg>\n }\n </div>\n @if (t.detail && t.expanded) {\n <pre class=\"tool-detail\">{{ t.detail }}</pre>\n }\n </div>\n }\n </div>\n }\n @if (m.text) {\n @if (m.role === 'assistant') {\n <div class=\"bubble-text md-body\" [innerHTML]=\"m.text | maAiMarkdown\"></div>\n } @else {\n <div class=\"bubble-text\">{{ m.text }}</div>\n }\n }\n @if (m.pending && !m.text) {\n <div class=\"thinking\"><span class=\"spinner-small\"></span> Thinking\u2026</div>\n }\n </div>\n }\n </div>\n\n <!-- Pending approval card -->\n @if (pendingApproval(); as p) {\n <div class=\"approval-card\">\n <div class=\"approval-card-head\">\n <strong>Approve action?</strong>\n <code class=\"tool-name\">{{ p.name }}</code>\n </div>\n @if (p.args && (p.args | json) !== '{}') {\n <pre class=\"approval-args\">{{ p.args | json }}</pre>\n }\n <div class=\"approval-actions\">\n <button class=\"btn primary\" (click)=\"approve()\">Approve</button>\n <button class=\"btn\" (click)=\"alwaysApprove()\">Always</button>\n <button class=\"btn danger\" (click)=\"decline()\">Decline</button>\n </div>\n </div>\n }\n\n <!-- Question card (ask_user tool) -->\n @if (pendingQuestion(); as q) {\n <div class=\"question-card\">\n <p class=\"question-text\">{{ q.message }}</p>\n @if (q.options && q.options.length > 0) {\n <div class=\"question-options\">\n @for (opt of q.options; track opt) {\n <button class=\"btn question-option\" (click)=\"answerQuestion(opt)\">{{ opt }}</button>\n }\n </div>\n } @else {\n <div class=\"question-input-row\">\n <input type=\"text\"\n class=\"question-input\"\n placeholder=\"Type your answer\u2026\"\n [value]=\"questionInput()\"\n (input)=\"questionInput.set($any($event.target).value)\"\n (keydown.enter)=\"submitQuestionInput()\"\n autofocus />\n <button class=\"btn primary\" (click)=\"submitQuestionInput()\" [disabled]=\"!questionInput().trim()\">Send</button>\n </div>\n }\n </div>\n }\n\n <!-- Composer -->\n <div class=\"composer\">\n @if (lastError()) {\n <div class=\"composer-error\">{{ lastError() }}</div>\n }\n <div class=\"composer-row\">\n <textarea #textArea\n rows=\"1\"\n placeholder=\"Ask the AI\u2026\"\n [value]=\"draft()\"\n (input)=\"onTextareaInput($event)\"\n (keydown)=\"onTextareaKey($event)\"\n [disabled]=\"streaming()\"></textarea>\n @if (streaming()) {\n <button class=\"send-btn cancel\" (click)=\"cancel()\" title=\"Cancel\" aria-label=\"Cancel\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><rect x=\"6\" y=\"6\" width=\"12\" height=\"12\"/></svg>\n </button>\n } @else {\n <button class=\"send-btn\" (click)=\"send()\" [disabled]=\"!draft().trim()\" title=\"Send\" aria-label=\"Send\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>\n </button>\n }\n </div>\n </div>\n\n <!-- Footer with hints (lifts the composer off the absolute bottom of the viewport) -->\n <div class=\"ai-footer\">\n <span class=\"hint\">\n <kbd>Enter</kbd> send \u00B7 <kbd>Shift</kbd>+<kbd>Enter</kbd> new line\n </span>\n <span class=\"hint dim\">AI can make mistakes — verify important info</span>\n </div>\n</div>\n", styles: [".panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary)}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.close-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:11px 8px;background:none;border:none;border-bottom:2px solid transparent;color:var(--text-muted);cursor:pointer;font-size:13px;font-weight:500;transition:color .15s,border-color .15s}.tab-btn.active{color:var(--primary);border-bottom-color:var(--primary)}.tab-btn:hover:not(.active){color:var(--text-secondary)}.tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;background:var(--primary);color:#fff;font-size:11px;font-weight:700;border-radius:9px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:12px;color:var(--text-muted)}.empty-state p{margin:0;font-size:13px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;gap:12px;color:var(--text-muted);font-size:13px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n", ":host{--primary: #c4b5fd;--primary-strong: #a78bfa;--success: #66bb6a;--error: #ef5350;--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--bg-bubble-user: #312e81;--bg-bubble-assistant: #232336;--border-color: #383850;--shadow: rgba(0,0,0,.4)}:host(.theme-light){--primary: #7c3aed;--primary-strong: #6d28d9;--success: #2e7d32;--error: #c62828;--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f5f3ff;--bg-hover: #ede9fe;--bg-bubble-user: #ede9fe;--bg-bubble-assistant: #f5f5f5;--border-color: #e0e0e0;--shadow: rgba(0,0,0,.15)}.ai-panel{position:fixed;top:0;right:0;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1040;transform:translate(100%);transition:transform .3s cubic-bezier(.16,1,.3,1);will-change:transform,width;pointer-events:none}.ai-panel.open{transform:translate(0);pointer-events:auto}.ai-resize-handle{position:absolute;top:0;left:-4px;width:8px;height:100%;cursor:ew-resize;z-index:1;background:transparent}.ai-resize-handle:hover{background:linear-gradient(to right,transparent,rgba(124,58,237,.25),transparent)}.header-actions{display:flex;align-items:center;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.icon-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.panel-content{flex:1;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:10px}.quick-prompts{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-top:8px}.quick-prompt{background:var(--bg-secondary);border:1px solid var(--border-color);color:var(--text-secondary);padding:6px 10px;border-radius:14px;font-size:12px;cursor:pointer;transition:background .15s,color .15s}.quick-prompt:hover{background:var(--bg-hover);color:var(--text-primary)}.bubble{max-width:90%;padding:10px 12px;border-radius:10px;font-size:13.5px;line-height:1.5;word-wrap:break-word;white-space:pre-wrap}.bubble.user{align-self:flex-end;background:var(--bg-bubble-user);color:var(--text-primary)}.bubble.assistant{align-self:flex-start;background:var(--bg-bubble-assistant);color:var(--text-primary)}.bubble-text{display:block}.thinking{display:flex;align-items:center;gap:6px;color:var(--text-muted);font-size:12.5px}.spinner-small{width:12px;height:12px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.tool-strip{display:flex;flex-direction:column;gap:4px;margin-bottom:6px}.tool-chip-wrap{display:flex;flex-direction:column;align-items:flex-start;gap:4px}.tool-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border-radius:12px;background:var(--bg-hover);font-size:11.5px;color:var(--text-secondary);border:1px solid var(--border-color);align-self:flex-start;-webkit-user-select:none;user-select:none}.tool-chip.expandable{cursor:pointer;transition:background .15s,border-color .15s}.tool-chip.expandable:hover{background:var(--bg-secondary);border-color:var(--primary)}.tool-chip.expandable:focus-visible{outline:2px solid var(--primary);outline-offset:1px}.tool-chip .caret{margin-left:2px;opacity:.6;transition:transform .15s}.tool-chip.expanded .caret{transform:rotate(180deg)}.tool-chip .dot{width:6px;height:6px;border-radius:50%;background:var(--text-muted)}.tool-chip.running .dot{background:var(--primary);animation:pulse 1s ease-in-out infinite}.tool-chip.ok .dot{background:var(--success)}.tool-chip.err .dot{background:var(--error)}.tool-chip.await .dot{background:#f59e0b}.tool-chip.declined .dot{background:var(--text-muted)}.tool-chip .tool-name{font-weight:600;color:var(--text-primary)}.tool-chip .muted{color:var(--text-muted)}.tool-detail{margin:0;padding:8px 10px;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;line-height:1.45;color:var(--text-primary);max-height:280px;overflow:auto;white-space:pre;align-self:stretch}@keyframes pulse{50%{opacity:.4}}.approval-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--primary);border-radius:10px;background:var(--bg-secondary)}.approval-card-head{display:flex;align-items:center;gap:10px;margin-bottom:8px;color:var(--text-primary)}.approval-card-head code{background:var(--bg-hover);padding:2px 6px;border-radius:6px;font-size:12px;color:var(--primary)}.approval-args{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:6px;padding:8px;font-size:11.5px;max-height:100px;overflow:auto;margin:0 0 8px;color:var(--text-secondary)}.approval-actions{display:flex;gap:6px;flex-wrap:wrap}.btn{flex:1;padding:6px 10px;font-size:12.5px;font-weight:600;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);cursor:pointer;transition:background .15s,border-color .15s}.btn:hover{background:var(--bg-hover)}.btn.primary{background:var(--primary);color:#fff;border-color:var(--primary)}.btn.primary:hover{background:var(--primary-strong)}.btn.danger{color:var(--error)}.btn.danger:hover{background:#ef53501a}.question-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--border-color);border-radius:10px;background:var(--bg-secondary)}.question-text{margin:0 0 10px;font-size:13px;color:var(--text-primary);line-height:1.5}.question-options{display:flex;flex-wrap:wrap;gap:6px}.question-option{flex:0 1 auto}.question-input-row{display:flex;gap:6px;align-items:center}.question-input{flex:1;padding:6px 10px;font-size:13px;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);outline:none}.question-input:focus{border-color:var(--primary)}.composer{flex-shrink:0;border-top:1px solid var(--border-color);padding:10px 12px;background:var(--bg-secondary)}.composer-error{color:var(--error);font-size:12px;margin-bottom:6px}.composer-row{display:flex;align-items:flex-end;gap:8px}.composer textarea{flex:1;resize:none;padding:8px 10px;border-radius:8px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);font-size:13px;font-family:inherit;outline:none;min-height:36px;max-height:160px;line-height:1.4}.composer textarea:focus{border-color:var(--primary)}.composer textarea:disabled{opacity:.6}.send-btn{width:36px;height:36px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--primary);color:#fff;border:none;border-radius:8px;cursor:pointer;transition:background .15s}.send-btn:hover:not(:disabled){background:var(--primary-strong)}.send-btn:disabled{opacity:.5;cursor:not-allowed}.send-btn.cancel{background:var(--error)}.send-btn.cancel:hover{filter:brightness(1.1)}.ai-footer{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 12px 10px;background:var(--bg-secondary);border-top:1px solid var(--border-color);font-size:11px;color:var(--text-secondary);line-height:1.4;text-align:center}.ai-footer .hint{display:inline-block}.ai-footer .hint.dim{color:var(--text-muted);font-size:10.5px}.ai-footer kbd{display:inline-block;padding:0 5px;margin:0 1px;border:1px solid var(--border-color);border-bottom-width:2px;border-radius:4px;background:var(--bg-primary);color:var(--text-primary);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;line-height:1.4}.md-body{font-size:13.5px;line-height:1.55}.md-body .md-p{margin:0 0 8px}.md-body .md-p:last-child{margin-bottom:0}.md-body .md-h1,.md-body .md-h2,.md-body .md-h3,.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{margin:12px 0 6px;font-weight:700;color:var(--text-primary);line-height:1.3}.md-body .md-h1{font-size:17px}.md-body .md-h2{font-size:15.5px}.md-body .md-h3{font-size:14.5px}.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{font-size:13.5px}.md-body strong{font-weight:700}.md-body em{font-style:italic}.md-body .md-link{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.md-body .md-link:hover{color:var(--primary-strong)}.md-body .md-list{margin:0 0 8px;padding-left:22px}.md-body .md-list li{margin:2px 0}.md-body .md-list li::marker{color:var(--text-muted)}.md-body .md-quote{margin:0 0 8px;padding:6px 10px;border-left:3px solid var(--primary);background:var(--bg-hover);color:var(--text-secondary);border-radius:0 6px 6px 0}.md-body .md-code{background:var(--bg-hover);border:1px solid var(--border-color);border-radius:4px;padding:1px 5px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--primary)}.md-body .md-pre{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:10px 12px;margin:0 0 8px;overflow-x:auto;font-size:12px;line-height:1.5}.md-body .md-pre code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--text-primary);background:none;border:none;padding:0}.md-body .md-table-wrap{margin:0 0 8px;overflow-x:auto;border:1px solid var(--border-color);border-radius:8px}.md-body .md-table{border-collapse:collapse;width:100%;font-size:12.5px}.md-body .md-table th,.md-body .md-table td{padding:6px 10px;border-bottom:1px solid var(--border-color);vertical-align:top;text-align:left}.md-body .md-table th{background:var(--bg-secondary);color:var(--text-primary);font-weight:600}.md-body .md-table tr:last-child td{border-bottom:none}.md-body .md-table tbody tr:hover{background:var(--bg-hover)}\n"], dependencies: [{ kind: "component", type: MaAiHistoryListComponent, selector: "ma-ai-history-list", outputs: ["resumed"] }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: MaAiMarkdownPipe, name: "maAiMarkdown" }] });
|
|
2885
2953
|
}
|
|
2886
2954
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: MaAiChatPanelComponent, decorators: [{
|
|
2887
2955
|
type: Component,
|
|
2888
|
-
args: [{ selector: 'ma-ai-chat-panel', imports: [JsonPipe, MaAiMarkdownPipe, MaAiHistoryListComponent], template: "<div class=\"ai-panel\" [class.open]=\"isOpen()\" [style.width.px]=\"width()\">\n\n <!-- Resize handle (left edge) -->\n <div class=\"ai-resize-handle\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResizeMove($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n aria-label=\"Resize panel\"\n title=\"Drag to resize\"></div>\n\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-header-left\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n <path d=\"M19 16 L19.8 18.2 L22 19 L19.8 19.8 L19 22 L18.2 19.8 L16 19 L18.2 18.2 Z\"/>\n </svg>\n <h3>AI Assistant</h3>\n </div>\n <div class=\"header-actions\">\n <button class=\"icon-btn\" (click)=\"newConversation()\" title=\"New conversation\" aria-label=\"New conversation\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14\"/><path d=\"M5 12h14\"/>\n </svg>\n </button>\n <button class=\"icon-btn\" (click)=\"toggleHistory()\" [class.active]=\"historyOpen()\" title=\"History\" aria-label=\"History\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/>\n </svg>\n </button>\n <button class=\"close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n </div>\n\n @if (historyOpen()) {\n <ma-ai-history-list (resumed)=\"onHistoryResumed($event)\"></ma-ai-history-list>\n }\n\n <!-- Conversation -->\n <div class=\"panel-content\" #scrollContainer>\n @if (!hasMessages()) {\n <div class=\"empty-state\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"36\" height=\"36\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\"0.5\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n </svg>\n <p>Ask anything about your account, approvals, notifications, or how to use the app.</p>\n <div class=\"quick-prompts\">\n @for (q of quickPrompts(); track q) {\n <button class=\"quick-prompt\" (click)=\"draft.set(q)\">{{ q }}</button>\n }\n </div>\n </div>\n }\n\n @for (m of messages(); track m.id) {\n <div class=\"bubble\" [class.user]=\"m.role === 'user'\" [class.assistant]=\"m.role === 'assistant'\">\n @if (m.role === 'assistant' && m.toolEvents && m.toolEvents.length > 0) {\n <div class=\"tool-strip\">\n @for (t of m.toolEvents; track t.id) {\n <div class=\"tool-chip\" [class.ok]=\"t.status === 'ok'\" [class.err]=\"t.status === 'error'\"\n [class.running]=\"t.status === 'running'\" [class.await]=\"t.status === 'awaiting-approval'\"\n [class.declined]=\"t.status === 'declined'\"\n [title]=\"t.summary ?? ''\">\n <span class=\"dot\"></span>\n <span class=\"tool-name\">{{ t.side === 'server' ? '\u2699' : '\u2318' }} {{ t.name }}</span>\n @if (t.status === 'running') { <span class=\"muted\">\u2026</span> }\n @if (t.status === 'ok' && t.summary) { <span class=\"muted\">\u00B7 {{ t.summary }}</span> }\n @if (t.status === 'error') { <span class=\"muted\">\u00B7 failed</span> }\n @if (t.status === 'awaiting-approval') { <span class=\"muted\">\u00B7 awaiting approval</span> }\n @if (t.status === 'declined') { <span class=\"muted\">\u00B7 declined</span> }\n </div>\n }\n </div>\n }\n @if (m.text) {\n @if (m.role === 'assistant') {\n <div class=\"bubble-text md-body\" [innerHTML]=\"m.text | maAiMarkdown\"></div>\n } @else {\n <div class=\"bubble-text\">{{ m.text }}</div>\n }\n }\n @if (m.pending && !m.text) {\n <div class=\"thinking\"><span class=\"spinner-small\"></span> Thinking\u2026</div>\n }\n </div>\n }\n </div>\n\n <!-- Pending approval card -->\n @if (pendingApproval(); as p) {\n <div class=\"approval-card\">\n <div class=\"approval-card-head\">\n <strong>Approve action?</strong>\n <code class=\"tool-name\">{{ p.name }}</code>\n </div>\n @if (p.args && (p.args | json) !== '{}') {\n <pre class=\"approval-args\">{{ p.args | json }}</pre>\n }\n <div class=\"approval-actions\">\n <button class=\"btn primary\" (click)=\"approve()\">Approve</button>\n <button class=\"btn\" (click)=\"alwaysApprove()\">Always</button>\n <button class=\"btn danger\" (click)=\"decline()\">Decline</button>\n </div>\n </div>\n }\n\n <!-- Question card (ask_user tool) -->\n @if (pendingQuestion(); as q) {\n <div class=\"question-card\">\n <p class=\"question-text\">{{ q.message }}</p>\n @if (q.options && q.options.length > 0) {\n <div class=\"question-options\">\n @for (opt of q.options; track opt) {\n <button class=\"btn question-option\" (click)=\"answerQuestion(opt)\">{{ opt }}</button>\n }\n </div>\n } @else {\n <div class=\"question-input-row\">\n <input type=\"text\"\n class=\"question-input\"\n placeholder=\"Type your answer\u2026\"\n [value]=\"questionInput()\"\n (input)=\"questionInput.set($any($event.target).value)\"\n (keydown.enter)=\"submitQuestionInput()\"\n autofocus />\n <button class=\"btn primary\" (click)=\"submitQuestionInput()\" [disabled]=\"!questionInput().trim()\">Send</button>\n </div>\n }\n </div>\n }\n\n <!-- Composer -->\n <div class=\"composer\">\n @if (lastError()) {\n <div class=\"composer-error\">{{ lastError() }}</div>\n }\n <div class=\"composer-row\">\n <textarea #textArea\n rows=\"1\"\n placeholder=\"Ask the AI\u2026\"\n [value]=\"draft()\"\n (input)=\"onTextareaInput($event)\"\n (keydown)=\"onTextareaKey($event)\"\n [disabled]=\"streaming()\"></textarea>\n @if (streaming()) {\n <button class=\"send-btn cancel\" (click)=\"cancel()\" title=\"Cancel\" aria-label=\"Cancel\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><rect x=\"6\" y=\"6\" width=\"12\" height=\"12\"/></svg>\n </button>\n } @else {\n <button class=\"send-btn\" (click)=\"send()\" [disabled]=\"!draft().trim()\" title=\"Send\" aria-label=\"Send\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>\n </button>\n }\n </div>\n </div>\n\n <!-- Footer with hints (lifts the composer off the absolute bottom of the viewport) -->\n <div class=\"ai-footer\">\n <span class=\"hint\">\n <kbd>Enter</kbd> send \u00B7 <kbd>Shift</kbd>+<kbd>Enter</kbd> new line\n </span>\n <span class=\"hint dim\">AI can make mistakes — verify important info</span>\n </div>\n</div>\n", styles: [".panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary)}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.close-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:11px 8px;background:none;border:none;border-bottom:2px solid transparent;color:var(--text-muted);cursor:pointer;font-size:13px;font-weight:500;transition:color .15s,border-color .15s}.tab-btn.active{color:var(--primary);border-bottom-color:var(--primary)}.tab-btn:hover:not(.active){color:var(--text-secondary)}.tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;background:var(--primary);color:#fff;font-size:11px;font-weight:700;border-radius:9px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:12px;color:var(--text-muted)}.empty-state p{margin:0;font-size:13px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;gap:12px;color:var(--text-muted);font-size:13px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n", ":host{--primary: #c4b5fd;--primary-strong: #a78bfa;--success: #66bb6a;--error: #ef5350;--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--bg-bubble-user: #312e81;--bg-bubble-assistant: #232336;--border-color: #383850;--shadow: rgba(0,0,0,.4)}:host(.theme-light){--primary: #7c3aed;--primary-strong: #6d28d9;--success: #2e7d32;--error: #c62828;--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f5f3ff;--bg-hover: #ede9fe;--bg-bubble-user: #ede9fe;--bg-bubble-assistant: #f5f5f5;--border-color: #e0e0e0;--shadow: rgba(0,0,0,.15)}.ai-panel{position:fixed;top:0;right:0;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1040;transform:translate(100%);transition:transform .3s cubic-bezier(.16,1,.3,1);will-change:transform,width;pointer-events:none}.ai-panel.open{transform:translate(0);pointer-events:auto}.ai-resize-handle{position:absolute;top:0;left:-4px;width:8px;height:100%;cursor:ew-resize;z-index:1;background:transparent}.ai-resize-handle:hover{background:linear-gradient(to right,transparent,rgba(124,58,237,.25),transparent)}.header-actions{display:flex;align-items:center;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.icon-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.panel-content{flex:1;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:10px}.quick-prompts{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-top:8px}.quick-prompt{background:var(--bg-secondary);border:1px solid var(--border-color);color:var(--text-secondary);padding:6px 10px;border-radius:14px;font-size:12px;cursor:pointer;transition:background .15s,color .15s}.quick-prompt:hover{background:var(--bg-hover);color:var(--text-primary)}.bubble{max-width:90%;padding:10px 12px;border-radius:10px;font-size:13.5px;line-height:1.5;word-wrap:break-word;white-space:pre-wrap}.bubble.user{align-self:flex-end;background:var(--bg-bubble-user);color:var(--text-primary)}.bubble.assistant{align-self:flex-start;background:var(--bg-bubble-assistant);color:var(--text-primary)}.bubble-text{display:block}.thinking{display:flex;align-items:center;gap:6px;color:var(--text-muted);font-size:12.5px}.spinner-small{width:12px;height:12px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.tool-strip{display:flex;flex-direction:column;gap:4px;margin-bottom:6px}.tool-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border-radius:12px;background:var(--bg-hover);font-size:11.5px;color:var(--text-secondary);border:1px solid var(--border-color);align-self:flex-start}.tool-chip .dot{width:6px;height:6px;border-radius:50%;background:var(--text-muted)}.tool-chip.running .dot{background:var(--primary);animation:pulse 1s ease-in-out infinite}.tool-chip.ok .dot{background:var(--success)}.tool-chip.err .dot{background:var(--error)}.tool-chip.await .dot{background:#f59e0b}.tool-chip.declined .dot{background:var(--text-muted)}.tool-chip .tool-name{font-weight:600;color:var(--text-primary)}.tool-chip .muted{color:var(--text-muted)}@keyframes pulse{50%{opacity:.4}}.approval-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--primary);border-radius:10px;background:var(--bg-secondary)}.approval-card-head{display:flex;align-items:center;gap:10px;margin-bottom:8px;color:var(--text-primary)}.approval-card-head code{background:var(--bg-hover);padding:2px 6px;border-radius:6px;font-size:12px;color:var(--primary)}.approval-args{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:6px;padding:8px;font-size:11.5px;max-height:100px;overflow:auto;margin:0 0 8px;color:var(--text-secondary)}.approval-actions{display:flex;gap:6px;flex-wrap:wrap}.btn{flex:1;padding:6px 10px;font-size:12.5px;font-weight:600;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);cursor:pointer;transition:background .15s,border-color .15s}.btn:hover{background:var(--bg-hover)}.btn.primary{background:var(--primary);color:#fff;border-color:var(--primary)}.btn.primary:hover{background:var(--primary-strong)}.btn.danger{color:var(--error)}.btn.danger:hover{background:#ef53501a}.question-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--border-color);border-radius:10px;background:var(--bg-secondary)}.question-text{margin:0 0 10px;font-size:13px;color:var(--text-primary);line-height:1.5}.question-options{display:flex;flex-wrap:wrap;gap:6px}.question-option{flex:0 1 auto}.question-input-row{display:flex;gap:6px;align-items:center}.question-input{flex:1;padding:6px 10px;font-size:13px;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);outline:none}.question-input:focus{border-color:var(--primary)}.composer{flex-shrink:0;border-top:1px solid var(--border-color);padding:10px 12px;background:var(--bg-secondary)}.composer-error{color:var(--error);font-size:12px;margin-bottom:6px}.composer-row{display:flex;align-items:flex-end;gap:8px}.composer textarea{flex:1;resize:none;padding:8px 10px;border-radius:8px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);font-size:13px;font-family:inherit;outline:none;min-height:36px;max-height:160px;line-height:1.4}.composer textarea:focus{border-color:var(--primary)}.composer textarea:disabled{opacity:.6}.send-btn{width:36px;height:36px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--primary);color:#fff;border:none;border-radius:8px;cursor:pointer;transition:background .15s}.send-btn:hover:not(:disabled){background:var(--primary-strong)}.send-btn:disabled{opacity:.5;cursor:not-allowed}.send-btn.cancel{background:var(--error)}.send-btn.cancel:hover{filter:brightness(1.1)}.ai-footer{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 12px 10px;background:var(--bg-secondary);border-top:1px solid var(--border-color);font-size:11px;color:var(--text-secondary);line-height:1.4;text-align:center}.ai-footer .hint{display:inline-block}.ai-footer .hint.dim{color:var(--text-muted);font-size:10.5px}.ai-footer kbd{display:inline-block;padding:0 5px;margin:0 1px;border:1px solid var(--border-color);border-bottom-width:2px;border-radius:4px;background:var(--bg-primary);color:var(--text-primary);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;line-height:1.4}.md-body{font-size:13.5px;line-height:1.55}.md-body .md-p{margin:0 0 8px}.md-body .md-p:last-child{margin-bottom:0}.md-body .md-h1,.md-body .md-h2,.md-body .md-h3,.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{margin:12px 0 6px;font-weight:700;color:var(--text-primary);line-height:1.3}.md-body .md-h1{font-size:17px}.md-body .md-h2{font-size:15.5px}.md-body .md-h3{font-size:14.5px}.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{font-size:13.5px}.md-body strong{font-weight:700}.md-body em{font-style:italic}.md-body .md-link{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.md-body .md-link:hover{color:var(--primary-strong)}.md-body .md-list{margin:0 0 8px;padding-left:22px}.md-body .md-list li{margin:2px 0}.md-body .md-list li::marker{color:var(--text-muted)}.md-body .md-quote{margin:0 0 8px;padding:6px 10px;border-left:3px solid var(--primary);background:var(--bg-hover);color:var(--text-secondary);border-radius:0 6px 6px 0}.md-body .md-code{background:var(--bg-hover);border:1px solid var(--border-color);border-radius:4px;padding:1px 5px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--primary)}.md-body .md-pre{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:10px 12px;margin:0 0 8px;overflow-x:auto;font-size:12px;line-height:1.5}.md-body .md-pre code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--text-primary);background:none;border:none;padding:0}.md-body .md-table-wrap{margin:0 0 8px;overflow-x:auto;border:1px solid var(--border-color);border-radius:8px}.md-body .md-table{border-collapse:collapse;width:100%;font-size:12.5px}.md-body .md-table th,.md-body .md-table td{padding:6px 10px;border-bottom:1px solid var(--border-color);vertical-align:top;text-align:left}.md-body .md-table th{background:var(--bg-secondary);color:var(--text-primary);font-weight:600}.md-body .md-table tr:last-child td{border-bottom:none}.md-body .md-table tbody tr:hover{background:var(--bg-hover)}\n"] }]
|
|
2956
|
+
args: [{ selector: 'ma-ai-chat-panel', imports: [JsonPipe, MaAiMarkdownPipe, MaAiHistoryListComponent], template: "<div class=\"ai-panel\" [class.open]=\"isOpen()\" [style.width.px]=\"width()\">\n\n <!-- Resize handle (left edge) -->\n <div class=\"ai-resize-handle\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResizeMove($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n aria-label=\"Resize panel\"\n title=\"Drag to resize\"></div>\n\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-header-left\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n <path d=\"M19 16 L19.8 18.2 L22 19 L19.8 19.8 L19 22 L18.2 19.8 L16 19 L18.2 18.2 Z\"/>\n </svg>\n <h3>AI Assistant</h3>\n </div>\n <div class=\"header-actions\">\n <button class=\"icon-btn\" (click)=\"newConversation()\" title=\"New conversation\" aria-label=\"New conversation\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14\"/><path d=\"M5 12h14\"/>\n </svg>\n </button>\n <button class=\"icon-btn\" (click)=\"toggleHistory()\" [class.active]=\"historyOpen()\" title=\"History\" aria-label=\"History\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/>\n </svg>\n </button>\n <button class=\"close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n </div>\n\n @if (historyOpen()) {\n <ma-ai-history-list (resumed)=\"onHistoryResumed($event)\"></ma-ai-history-list>\n }\n\n <!-- Conversation -->\n <div class=\"panel-content\" #scrollContainer>\n @if (!hasMessages()) {\n <div class=\"empty-state\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"36\" height=\"36\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\"0.5\">\n <path d=\"M12 2 L13.5 7.5 L19 9 L13.5 10.5 L12 16 L10.5 10.5 L5 9 L10.5 7.5 Z\"/>\n </svg>\n <p>Ask anything about your account, approvals, notifications, or how to use the app.</p>\n <div class=\"quick-prompts\">\n @for (q of quickPrompts(); track q) {\n <button class=\"quick-prompt\" (click)=\"draft.set(q)\">{{ q }}</button>\n }\n </div>\n </div>\n }\n\n @for (m of messages(); track m.id) {\n <div class=\"bubble\" [class.user]=\"m.role === 'user'\" [class.assistant]=\"m.role === 'assistant'\">\n @if (m.role === 'assistant' && m.toolEvents && m.toolEvents.length > 0) {\n <div class=\"tool-strip\">\n @for (t of m.toolEvents; track t.id) {\n <div class=\"tool-chip-wrap\">\n <div class=\"tool-chip\" [class.ok]=\"t.status === 'ok'\" [class.err]=\"t.status === 'error'\"\n [class.running]=\"t.status === 'running'\" [class.await]=\"t.status === 'awaiting-approval'\"\n [class.declined]=\"t.status === 'declined'\"\n [class.expandable]=\"!!t.detail\"\n [class.expanded]=\"t.expanded === true\"\n (click)=\"onToolChipClick(t)\"\n [attr.role]=\"t.detail ? 'button' : null\"\n [attr.tabindex]=\"t.detail ? 0 : null\"\n [attr.aria-expanded]=\"t.detail ? (t.expanded === true) : null\"\n [title]=\"t.detail ? (t.expanded ? 'Click to collapse' : 'Click to view full result') : ''\">\n <span class=\"dot\"></span>\n <span class=\"tool-name\">{{ t.side === 'server' ? '\u2699' : '\u2318' }} {{ t.name }}</span>\n @if (t.status === 'running') { <span class=\"muted\">\u2026</span> }\n @if (t.status === 'ok' && t.summary) { <span class=\"muted\">\u00B7 {{ t.summary }}</span> }\n @if (t.status === 'error') { <span class=\"muted\">\u00B7 failed</span> }\n @if (t.status === 'awaiting-approval') { <span class=\"muted\">\u00B7 awaiting approval</span> }\n @if (t.status === 'declined') { <span class=\"muted\">\u00B7 declined</span> }\n @if (t.detail) {\n <svg class=\"caret\" xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\"/>\n </svg>\n }\n </div>\n @if (t.detail && t.expanded) {\n <pre class=\"tool-detail\">{{ t.detail }}</pre>\n }\n </div>\n }\n </div>\n }\n @if (m.text) {\n @if (m.role === 'assistant') {\n <div class=\"bubble-text md-body\" [innerHTML]=\"m.text | maAiMarkdown\"></div>\n } @else {\n <div class=\"bubble-text\">{{ m.text }}</div>\n }\n }\n @if (m.pending && !m.text) {\n <div class=\"thinking\"><span class=\"spinner-small\"></span> Thinking\u2026</div>\n }\n </div>\n }\n </div>\n\n <!-- Pending approval card -->\n @if (pendingApproval(); as p) {\n <div class=\"approval-card\">\n <div class=\"approval-card-head\">\n <strong>Approve action?</strong>\n <code class=\"tool-name\">{{ p.name }}</code>\n </div>\n @if (p.args && (p.args | json) !== '{}') {\n <pre class=\"approval-args\">{{ p.args | json }}</pre>\n }\n <div class=\"approval-actions\">\n <button class=\"btn primary\" (click)=\"approve()\">Approve</button>\n <button class=\"btn\" (click)=\"alwaysApprove()\">Always</button>\n <button class=\"btn danger\" (click)=\"decline()\">Decline</button>\n </div>\n </div>\n }\n\n <!-- Question card (ask_user tool) -->\n @if (pendingQuestion(); as q) {\n <div class=\"question-card\">\n <p class=\"question-text\">{{ q.message }}</p>\n @if (q.options && q.options.length > 0) {\n <div class=\"question-options\">\n @for (opt of q.options; track opt) {\n <button class=\"btn question-option\" (click)=\"answerQuestion(opt)\">{{ opt }}</button>\n }\n </div>\n } @else {\n <div class=\"question-input-row\">\n <input type=\"text\"\n class=\"question-input\"\n placeholder=\"Type your answer\u2026\"\n [value]=\"questionInput()\"\n (input)=\"questionInput.set($any($event.target).value)\"\n (keydown.enter)=\"submitQuestionInput()\"\n autofocus />\n <button class=\"btn primary\" (click)=\"submitQuestionInput()\" [disabled]=\"!questionInput().trim()\">Send</button>\n </div>\n }\n </div>\n }\n\n <!-- Composer -->\n <div class=\"composer\">\n @if (lastError()) {\n <div class=\"composer-error\">{{ lastError() }}</div>\n }\n <div class=\"composer-row\">\n <textarea #textArea\n rows=\"1\"\n placeholder=\"Ask the AI\u2026\"\n [value]=\"draft()\"\n (input)=\"onTextareaInput($event)\"\n (keydown)=\"onTextareaKey($event)\"\n [disabled]=\"streaming()\"></textarea>\n @if (streaming()) {\n <button class=\"send-btn cancel\" (click)=\"cancel()\" title=\"Cancel\" aria-label=\"Cancel\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><rect x=\"6\" y=\"6\" width=\"12\" height=\"12\"/></svg>\n </button>\n } @else {\n <button class=\"send-btn\" (click)=\"send()\" [disabled]=\"!draft().trim()\" title=\"Send\" aria-label=\"Send\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>\n </button>\n }\n </div>\n </div>\n\n <!-- Footer with hints (lifts the composer off the absolute bottom of the viewport) -->\n <div class=\"ai-footer\">\n <span class=\"hint\">\n <kbd>Enter</kbd> send \u00B7 <kbd>Shift</kbd>+<kbd>Enter</kbd> new line\n </span>\n <span class=\"hint dim\">AI can make mistakes — verify important info</span>\n </div>\n</div>\n", styles: [".panel-header{display:flex;justify-content:space-between;align-items:center;padding:16px 18px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.panel-header-left{display:flex;align-items:center;gap:9px;color:var(--primary)}.panel-header h3{margin:0;font-size:16px;font-weight:700;color:var(--text-primary)}.close-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.close-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.tabs{display:flex;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}.tab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:11px 8px;background:none;border:none;border-bottom:2px solid transparent;color:var(--text-muted);cursor:pointer;font-size:13px;font-weight:500;transition:color .15s,border-color .15s}.tab-btn.active{color:var(--primary);border-bottom-color:var(--primary)}.tab-btn:hover:not(.active){color:var(--text-secondary)}.tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;background:var(--primary);color:#fff;font-size:11px;font-weight:700;border-radius:9px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:12px;color:var(--text-muted)}.empty-state p{margin:0;font-size:13px}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;gap:12px;color:var(--text-muted);font-size:13px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n", ":host{--primary: #c4b5fd;--primary-strong: #a78bfa;--success: #66bb6a;--error: #ef5350;--text-primary: #e0e0e0;--text-secondary: #b0b0b0;--text-muted: #757575;--bg-primary: #1e1e2e;--bg-secondary: #27273a;--bg-hover: #2a2d4a;--bg-bubble-user: #312e81;--bg-bubble-assistant: #232336;--border-color: #383850;--shadow: rgba(0,0,0,.4)}:host(.theme-light){--primary: #7c3aed;--primary-strong: #6d28d9;--success: #2e7d32;--error: #c62828;--text-primary: #212121;--text-secondary: #616161;--text-muted: #9e9e9e;--bg-primary: #ffffff;--bg-secondary: #f5f3ff;--bg-hover: #ede9fe;--bg-bubble-user: #ede9fe;--bg-bubble-assistant: #f5f5f5;--border-color: #e0e0e0;--shadow: rgba(0,0,0,.15)}.ai-panel{position:fixed;top:0;right:0;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 24px var(--shadow);display:flex;flex-direction:column;z-index:1040;transform:translate(100%);transition:transform .3s cubic-bezier(.16,1,.3,1);will-change:transform,width;pointer-events:none}.ai-panel.open{transform:translate(0);pointer-events:auto}.ai-resize-handle{position:absolute;top:0;left:-4px;width:8px;height:100%;cursor:ew-resize;z-index:1;background:transparent}.ai-resize-handle:hover{background:linear-gradient(to right,transparent,rgba(124,58,237,.25),transparent)}.header-actions{display:flex;align-items:center;gap:4px}.icon-btn{background:none;border:none;cursor:pointer;color:var(--text-muted);width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}.icon-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.panel-content{flex:1;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:10px}.quick-prompts{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-top:8px}.quick-prompt{background:var(--bg-secondary);border:1px solid var(--border-color);color:var(--text-secondary);padding:6px 10px;border-radius:14px;font-size:12px;cursor:pointer;transition:background .15s,color .15s}.quick-prompt:hover{background:var(--bg-hover);color:var(--text-primary)}.bubble{max-width:90%;padding:10px 12px;border-radius:10px;font-size:13.5px;line-height:1.5;word-wrap:break-word;white-space:pre-wrap}.bubble.user{align-self:flex-end;background:var(--bg-bubble-user);color:var(--text-primary)}.bubble.assistant{align-self:flex-start;background:var(--bg-bubble-assistant);color:var(--text-primary)}.bubble-text{display:block}.thinking{display:flex;align-items:center;gap:6px;color:var(--text-muted);font-size:12.5px}.spinner-small{width:12px;height:12px;border:2px solid var(--border-color);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.tool-strip{display:flex;flex-direction:column;gap:4px;margin-bottom:6px}.tool-chip-wrap{display:flex;flex-direction:column;align-items:flex-start;gap:4px}.tool-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border-radius:12px;background:var(--bg-hover);font-size:11.5px;color:var(--text-secondary);border:1px solid var(--border-color);align-self:flex-start;-webkit-user-select:none;user-select:none}.tool-chip.expandable{cursor:pointer;transition:background .15s,border-color .15s}.tool-chip.expandable:hover{background:var(--bg-secondary);border-color:var(--primary)}.tool-chip.expandable:focus-visible{outline:2px solid var(--primary);outline-offset:1px}.tool-chip .caret{margin-left:2px;opacity:.6;transition:transform .15s}.tool-chip.expanded .caret{transform:rotate(180deg)}.tool-chip .dot{width:6px;height:6px;border-radius:50%;background:var(--text-muted)}.tool-chip.running .dot{background:var(--primary);animation:pulse 1s ease-in-out infinite}.tool-chip.ok .dot{background:var(--success)}.tool-chip.err .dot{background:var(--error)}.tool-chip.await .dot{background:#f59e0b}.tool-chip.declined .dot{background:var(--text-muted)}.tool-chip .tool-name{font-weight:600;color:var(--text-primary)}.tool-chip .muted{color:var(--text-muted)}.tool-detail{margin:0;padding:8px 10px;background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;line-height:1.45;color:var(--text-primary);max-height:280px;overflow:auto;white-space:pre;align-self:stretch}@keyframes pulse{50%{opacity:.4}}.approval-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--primary);border-radius:10px;background:var(--bg-secondary)}.approval-card-head{display:flex;align-items:center;gap:10px;margin-bottom:8px;color:var(--text-primary)}.approval-card-head code{background:var(--bg-hover);padding:2px 6px;border-radius:6px;font-size:12px;color:var(--primary)}.approval-args{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:6px;padding:8px;font-size:11.5px;max-height:100px;overflow:auto;margin:0 0 8px;color:var(--text-secondary)}.approval-actions{display:flex;gap:6px;flex-wrap:wrap}.btn{flex:1;padding:6px 10px;font-size:12.5px;font-weight:600;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);cursor:pointer;transition:background .15s,border-color .15s}.btn:hover{background:var(--bg-hover)}.btn.primary{background:var(--primary);color:#fff;border-color:var(--primary)}.btn.primary:hover{background:var(--primary-strong)}.btn.danger{color:var(--error)}.btn.danger:hover{background:#ef53501a}.question-card{flex-shrink:0;margin:8px 14px;padding:12px 14px;border:1px solid var(--border-color);border-radius:10px;background:var(--bg-secondary)}.question-text{margin:0 0 10px;font-size:13px;color:var(--text-primary);line-height:1.5}.question-options{display:flex;flex-wrap:wrap;gap:6px}.question-option{flex:0 1 auto}.question-input-row{display:flex;gap:6px;align-items:center}.question-input{flex:1;padding:6px 10px;font-size:13px;border-radius:6px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);outline:none}.question-input:focus{border-color:var(--primary)}.composer{flex-shrink:0;border-top:1px solid var(--border-color);padding:10px 12px;background:var(--bg-secondary)}.composer-error{color:var(--error);font-size:12px;margin-bottom:6px}.composer-row{display:flex;align-items:flex-end;gap:8px}.composer textarea{flex:1;resize:none;padding:8px 10px;border-radius:8px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-primary);font-size:13px;font-family:inherit;outline:none;min-height:36px;max-height:160px;line-height:1.4}.composer textarea:focus{border-color:var(--primary)}.composer textarea:disabled{opacity:.6}.send-btn{width:36px;height:36px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:var(--primary);color:#fff;border:none;border-radius:8px;cursor:pointer;transition:background .15s}.send-btn:hover:not(:disabled){background:var(--primary-strong)}.send-btn:disabled{opacity:.5;cursor:not-allowed}.send-btn.cancel{background:var(--error)}.send-btn.cancel:hover{filter:brightness(1.1)}.ai-footer{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 12px 10px;background:var(--bg-secondary);border-top:1px solid var(--border-color);font-size:11px;color:var(--text-secondary);line-height:1.4;text-align:center}.ai-footer .hint{display:inline-block}.ai-footer .hint.dim{color:var(--text-muted);font-size:10.5px}.ai-footer kbd{display:inline-block;padding:0 5px;margin:0 1px;border:1px solid var(--border-color);border-bottom-width:2px;border-radius:4px;background:var(--bg-primary);color:var(--text-primary);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;line-height:1.4}.md-body{font-size:13.5px;line-height:1.55}.md-body .md-p{margin:0 0 8px}.md-body .md-p:last-child{margin-bottom:0}.md-body .md-h1,.md-body .md-h2,.md-body .md-h3,.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{margin:12px 0 6px;font-weight:700;color:var(--text-primary);line-height:1.3}.md-body .md-h1{font-size:17px}.md-body .md-h2{font-size:15.5px}.md-body .md-h3{font-size:14.5px}.md-body .md-h4,.md-body .md-h5,.md-body .md-h6{font-size:13.5px}.md-body strong{font-weight:700}.md-body em{font-style:italic}.md-body .md-link{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.md-body .md-link:hover{color:var(--primary-strong)}.md-body .md-list{margin:0 0 8px;padding-left:22px}.md-body .md-list li{margin:2px 0}.md-body .md-list li::marker{color:var(--text-muted)}.md-body .md-quote{margin:0 0 8px;padding:6px 10px;border-left:3px solid var(--primary);background:var(--bg-hover);color:var(--text-secondary);border-radius:0 6px 6px 0}.md-body .md-code{background:var(--bg-hover);border:1px solid var(--border-color);border-radius:4px;padding:1px 5px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--primary)}.md-body .md-pre{background:var(--bg-primary);border:1px solid var(--border-color);border-radius:8px;padding:10px 12px;margin:0 0 8px;overflow-x:auto;font-size:12px;line-height:1.5}.md-body .md-pre code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--text-primary);background:none;border:none;padding:0}.md-body .md-table-wrap{margin:0 0 8px;overflow-x:auto;border:1px solid var(--border-color);border-radius:8px}.md-body .md-table{border-collapse:collapse;width:100%;font-size:12.5px}.md-body .md-table th,.md-body .md-table td{padding:6px 10px;border-bottom:1px solid var(--border-color);vertical-align:top;text-align:left}.md-body .md-table th{background:var(--bg-secondary);color:var(--text-primary);font-weight:600}.md-body .md-table tr:last-child td{border-bottom:none}.md-body .md-table tbody tr:hover{background:var(--bg-hover)}\n"] }]
|
|
2889
2957
|
}], ctorParameters: () => [], propDecorators: { scrollContainer: [{ type: i0.ViewChild, args: ['scrollContainer', { isSignal: true }] }], textArea: [{ type: i0.ViewChild, args: ['textArea', { isSignal: true }] }], themeClass: [{
|
|
2890
2958
|
type: HostBinding,
|
|
2891
2959
|
args: ['class']
|