@praxisui/ai 8.0.0-beta.20 → 8.0.0-beta.21
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/README.md +20 -4
- package/fesm2022/praxisui-ai.mjs +705 -21
- package/index.d.ts +189 -5
- package/package.json +2 -2
package/fesm2022/praxisui-ai.mjs
CHANGED
|
@@ -60,6 +60,317 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
60
60
|
* Models for Praxis AI (Centralized)
|
|
61
61
|
*/
|
|
62
62
|
|
|
63
|
+
const PRAXIS_ASSISTANT_CONTEXT_TEXT_LIMIT = 160;
|
|
64
|
+
const PRAXIS_ASSISTANT_CONTEXT_ITEM_LIMIT = 12;
|
|
65
|
+
const PRAXIS_ASSISTANT_CONTEXT_SCHEMA_FIELD_LIMIT = 40;
|
|
66
|
+
const PRAXIS_ASSISTANT_CONTEXT_ATTACHMENT_LIMIT = 8;
|
|
67
|
+
const REDACTED = '[redacted]';
|
|
68
|
+
const PROHIBITED_CONTEXT_KEYS = new Set([
|
|
69
|
+
'file',
|
|
70
|
+
'blob',
|
|
71
|
+
'bytes',
|
|
72
|
+
'base64',
|
|
73
|
+
'previewUrl',
|
|
74
|
+
'currentState',
|
|
75
|
+
'runtimeState',
|
|
76
|
+
'formValues',
|
|
77
|
+
'values',
|
|
78
|
+
'rows',
|
|
79
|
+
'rowData',
|
|
80
|
+
'pendingPatch',
|
|
81
|
+
'diagnostics',
|
|
82
|
+
'payload',
|
|
83
|
+
'config',
|
|
84
|
+
]);
|
|
85
|
+
const SENSITIVE_TEXT_PATTERNS = [
|
|
86
|
+
/\bBearer\s+[A-Za-z0-9\-._~+/]+=*/gi,
|
|
87
|
+
/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
88
|
+
/\bsk-[A-Za-z0-9]{20,}\b/g,
|
|
89
|
+
/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
|
|
90
|
+
/\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b/g,
|
|
91
|
+
/\b(?:\+?55\s?)?(?:\(?\d{2}\)?\s?)?(?:9\s?)?\d{4}[-\s]?\d{4}\b/g,
|
|
92
|
+
/\b(?:api[_-]?key|token|secret|password|senha)\s*[:=]\s*['"]?[^'"\s,;]+/gi,
|
|
93
|
+
];
|
|
94
|
+
function sanitizePraxisAssistantText(value, limit = PRAXIS_ASSISTANT_CONTEXT_TEXT_LIMIT) {
|
|
95
|
+
if (value === null || value === undefined)
|
|
96
|
+
return '';
|
|
97
|
+
let sanitized = String(value);
|
|
98
|
+
for (const pattern of SENSITIVE_TEXT_PATTERNS) {
|
|
99
|
+
sanitized = sanitized.replace(pattern, REDACTED);
|
|
100
|
+
}
|
|
101
|
+
sanitized = sanitized.replace(/\s+/g, ' ').trim();
|
|
102
|
+
const safeLimit = Math.max(0, limit);
|
|
103
|
+
return sanitized.length > safeLimit ? `${sanitized.slice(0, Math.max(0, safeLimit - 3))}...` : sanitized;
|
|
104
|
+
}
|
|
105
|
+
function normalizePraxisAssistantAttachmentSummary(attachment) {
|
|
106
|
+
if (!isRecord(attachment))
|
|
107
|
+
return null;
|
|
108
|
+
const id = sanitizePraxisAssistantText(attachment['id'] ?? attachment['name'], 96);
|
|
109
|
+
const name = sanitizePraxisAssistantText(attachment['name'] ?? id, 120);
|
|
110
|
+
if (!id || !name)
|
|
111
|
+
return null;
|
|
112
|
+
return {
|
|
113
|
+
id,
|
|
114
|
+
name,
|
|
115
|
+
kind: sanitizePraxisAssistantText(attachment['kind'] ?? 'file', 32) || 'file',
|
|
116
|
+
mimeType: sanitizePraxisAssistantText(attachment['mimeType'], 96) || undefined,
|
|
117
|
+
sizeBytes: normalizeNonNegativeNumber(attachment['sizeBytes']),
|
|
118
|
+
source: sanitizePraxisAssistantText(attachment['source'], 64) || undefined,
|
|
119
|
+
hasPreview: Boolean(attachment['hasPreview']),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function normalizePraxisAssistantContextSnapshot(value) {
|
|
123
|
+
if (!isRecord(value)) {
|
|
124
|
+
throw new Error('Praxis assistant context snapshot requires an object.');
|
|
125
|
+
}
|
|
126
|
+
const identity = normalizeIdentity(value['identity']);
|
|
127
|
+
const contextItems = normalizeContextItems(value['contextItems']);
|
|
128
|
+
return {
|
|
129
|
+
identity,
|
|
130
|
+
target: normalizeTargetRef(value['target']) ?? undefined,
|
|
131
|
+
contextItems,
|
|
132
|
+
mode: normalizeMode(value['mode']),
|
|
133
|
+
authoringManifestRef: normalizeManifestRef(value['authoringManifestRef']) ?? undefined,
|
|
134
|
+
resourcePath: sanitizePraxisAssistantText(value['resourcePath'], 160) || undefined,
|
|
135
|
+
schemaFields: normalizeStringList(value['schemaFields'], PRAXIS_ASSISTANT_CONTEXT_SCHEMA_FIELD_LIMIT, 96),
|
|
136
|
+
dataProfileDigest: normalizeDigest(value['dataProfileDigest']) ?? undefined,
|
|
137
|
+
runtimeStateDigest: normalizeDigest(value['runtimeStateDigest']) ?? undefined,
|
|
138
|
+
capabilityRefs: normalizeCapabilityRefs(value['capabilityRefs']),
|
|
139
|
+
governanceHints: normalizeGovernanceHints(value['governanceHints']),
|
|
140
|
+
riskHints: normalizeGovernanceHints(value['riskHints']),
|
|
141
|
+
attachmentSummaries: normalizeAttachmentSummaries(value['attachmentSummaries']),
|
|
142
|
+
actions: normalizeActions(value['actions']),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function normalizeIdentity(value) {
|
|
146
|
+
if (!isRecord(value)) {
|
|
147
|
+
throw new Error('Praxis assistant context snapshot requires identity.');
|
|
148
|
+
}
|
|
149
|
+
const sessionId = sanitizePraxisAssistantText(value['sessionId'], 120);
|
|
150
|
+
const ownerId = sanitizePraxisAssistantText(value['ownerId'], 120);
|
|
151
|
+
const ownerType = sanitizePraxisAssistantText(value['ownerType'], 64);
|
|
152
|
+
if (!sessionId || !ownerId || !ownerType) {
|
|
153
|
+
throw new Error('Praxis assistant identity requires sessionId, ownerId and ownerType.');
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
sessionId,
|
|
157
|
+
ownerId,
|
|
158
|
+
ownerType,
|
|
159
|
+
componentId: sanitizePraxisAssistantText(value['componentId'], 120) || undefined,
|
|
160
|
+
componentType: sanitizePraxisAssistantText(value['componentType'], 64) || undefined,
|
|
161
|
+
routeKey: sanitizePraxisAssistantText(value['routeKey'], 160) || undefined,
|
|
162
|
+
tenantId: sanitizePraxisAssistantText(value['tenantId'], 96) || undefined,
|
|
163
|
+
env: sanitizePraxisAssistantText(value['env'], 48) || undefined,
|
|
164
|
+
userId: sanitizePraxisAssistantText(value['userId'], 96) || undefined,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function normalizeTargetRef(value) {
|
|
168
|
+
if (!isRecord(value))
|
|
169
|
+
return null;
|
|
170
|
+
const kind = sanitizePraxisAssistantText(value['kind'], 48);
|
|
171
|
+
const id = sanitizePraxisAssistantText(value['id'], 120);
|
|
172
|
+
if (!kind || !id)
|
|
173
|
+
return null;
|
|
174
|
+
return {
|
|
175
|
+
kind,
|
|
176
|
+
id,
|
|
177
|
+
label: sanitizePraxisAssistantText(value['label'], 120) || undefined,
|
|
178
|
+
path: sanitizePraxisAssistantText(value['path'], 160) || undefined,
|
|
179
|
+
schemaPath: sanitizePraxisAssistantText(value['schemaPath'], 160) || undefined,
|
|
180
|
+
metadata: normalizeMetadata(value['metadata']),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function normalizeContextItems(value) {
|
|
184
|
+
if (!Array.isArray(value))
|
|
185
|
+
return [];
|
|
186
|
+
return value
|
|
187
|
+
.slice(0, PRAXIS_ASSISTANT_CONTEXT_ITEM_LIMIT)
|
|
188
|
+
.map((item) => {
|
|
189
|
+
if (!isRecord(item))
|
|
190
|
+
return null;
|
|
191
|
+
const id = sanitizePraxisAssistantText(item['id'], 80);
|
|
192
|
+
const label = sanitizePraxisAssistantText(item['label'], 80);
|
|
193
|
+
const itemValue = sanitizePraxisAssistantText(item['value'], 160);
|
|
194
|
+
if (!id || !label || !itemValue)
|
|
195
|
+
return null;
|
|
196
|
+
return {
|
|
197
|
+
id,
|
|
198
|
+
label,
|
|
199
|
+
value: itemValue,
|
|
200
|
+
kind: sanitizePraxisAssistantText(item['kind'], 48) || undefined,
|
|
201
|
+
tone: normalizeTone(item['tone']),
|
|
202
|
+
};
|
|
203
|
+
})
|
|
204
|
+
.filter(isDefined);
|
|
205
|
+
}
|
|
206
|
+
function normalizeManifestRef(value) {
|
|
207
|
+
if (!isRecord(value))
|
|
208
|
+
return null;
|
|
209
|
+
const componentId = sanitizePraxisAssistantText(value['componentId'], 120);
|
|
210
|
+
if (!componentId)
|
|
211
|
+
return null;
|
|
212
|
+
return {
|
|
213
|
+
componentId,
|
|
214
|
+
version: sanitizePraxisAssistantText(value['version'], 64) || undefined,
|
|
215
|
+
source: sanitizePraxisAssistantText(value['source'], 160) || undefined,
|
|
216
|
+
hash: sanitizePraxisAssistantText(value['hash'], 96) || undefined,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function normalizeDigest(value) {
|
|
220
|
+
if (!isRecord(value))
|
|
221
|
+
return null;
|
|
222
|
+
return {
|
|
223
|
+
label: sanitizePraxisAssistantText(value['label'], 80) || undefined,
|
|
224
|
+
summary: sanitizePraxisAssistantText(value['summary'], 240) || undefined,
|
|
225
|
+
hash: sanitizePraxisAssistantText(value['hash'], 96) || undefined,
|
|
226
|
+
source: sanitizePraxisAssistantText(value['source'], 160) || undefined,
|
|
227
|
+
fields: normalizeStringList(value['fields'], 20, 96),
|
|
228
|
+
counts: normalizeNumberMap(value['counts']),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function normalizeCapabilityRefs(value) {
|
|
232
|
+
if (!Array.isArray(value))
|
|
233
|
+
return undefined;
|
|
234
|
+
const refs = value.slice(0, 20).map((item) => {
|
|
235
|
+
if (!isRecord(item))
|
|
236
|
+
return null;
|
|
237
|
+
const id = sanitizePraxisAssistantText(item['id'], 120);
|
|
238
|
+
if (!id)
|
|
239
|
+
return null;
|
|
240
|
+
return {
|
|
241
|
+
id,
|
|
242
|
+
label: sanitizePraxisAssistantText(item['label'], 120) || undefined,
|
|
243
|
+
source: sanitizePraxisAssistantText(item['source'], 160) || undefined,
|
|
244
|
+
risk: normalizeRisk(item['risk']),
|
|
245
|
+
};
|
|
246
|
+
}).filter(isDefined);
|
|
247
|
+
return refs.length ? refs : undefined;
|
|
248
|
+
}
|
|
249
|
+
function normalizeGovernanceHints(value) {
|
|
250
|
+
if (!Array.isArray(value))
|
|
251
|
+
return undefined;
|
|
252
|
+
const hints = value.slice(0, 12).map((item) => {
|
|
253
|
+
if (!isRecord(item))
|
|
254
|
+
return null;
|
|
255
|
+
const kind = sanitizePraxisAssistantText(item['kind'], 80);
|
|
256
|
+
if (!kind)
|
|
257
|
+
return null;
|
|
258
|
+
return {
|
|
259
|
+
kind,
|
|
260
|
+
label: sanitizePraxisAssistantText(item['label'], 120) || undefined,
|
|
261
|
+
reason: sanitizePraxisAssistantText(item['reason'], 200) || undefined,
|
|
262
|
+
target: sanitizePraxisAssistantText(item['target'], 160) || undefined,
|
|
263
|
+
risk: normalizeRisk(item['risk']),
|
|
264
|
+
};
|
|
265
|
+
}).filter(isDefined);
|
|
266
|
+
return hints.length ? hints : undefined;
|
|
267
|
+
}
|
|
268
|
+
function normalizeAttachmentSummaries(value) {
|
|
269
|
+
if (!Array.isArray(value))
|
|
270
|
+
return undefined;
|
|
271
|
+
const summaries = value
|
|
272
|
+
.slice(0, PRAXIS_ASSISTANT_CONTEXT_ATTACHMENT_LIMIT)
|
|
273
|
+
.map(normalizePraxisAssistantAttachmentSummary)
|
|
274
|
+
.filter((item) => !!item);
|
|
275
|
+
return summaries.length ? summaries : undefined;
|
|
276
|
+
}
|
|
277
|
+
function normalizeActions(value) {
|
|
278
|
+
if (!Array.isArray(value))
|
|
279
|
+
return undefined;
|
|
280
|
+
const actions = value.slice(0, 20).map((item) => {
|
|
281
|
+
if (!isRecord(item))
|
|
282
|
+
return null;
|
|
283
|
+
const id = sanitizePraxisAssistantText(item['id'], 120);
|
|
284
|
+
const kind = sanitizePraxisAssistantText(item['kind'], 80);
|
|
285
|
+
if (!id || !kind)
|
|
286
|
+
return null;
|
|
287
|
+
return {
|
|
288
|
+
id,
|
|
289
|
+
kind,
|
|
290
|
+
label: sanitizePraxisAssistantText(item['label'], 120) || undefined,
|
|
291
|
+
target: normalizeTargetRef(item['target']) ?? undefined,
|
|
292
|
+
capabilityRef: sanitizePraxisAssistantText(item['capabilityRef'], 120) || undefined,
|
|
293
|
+
risk: normalizeRisk(item['risk']),
|
|
294
|
+
handoffEndpoint: sanitizePraxisAssistantText(item['handoffEndpoint'], 180) || undefined,
|
|
295
|
+
description: sanitizePraxisAssistantText(item['description'], 200) || undefined,
|
|
296
|
+
};
|
|
297
|
+
}).filter(isDefined);
|
|
298
|
+
return actions.length ? actions : undefined;
|
|
299
|
+
}
|
|
300
|
+
function normalizeMetadata(value) {
|
|
301
|
+
if (!isRecord(value))
|
|
302
|
+
return undefined;
|
|
303
|
+
const metadata = {};
|
|
304
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
305
|
+
if (PROHIBITED_CONTEXT_KEYS.has(key))
|
|
306
|
+
continue;
|
|
307
|
+
const safeKey = sanitizePraxisAssistantText(key, 64);
|
|
308
|
+
if (!safeKey)
|
|
309
|
+
continue;
|
|
310
|
+
if (typeof rawValue === 'string')
|
|
311
|
+
metadata[safeKey] = sanitizePraxisAssistantText(rawValue, 120);
|
|
312
|
+
if (typeof rawValue === 'number' && Number.isFinite(rawValue))
|
|
313
|
+
metadata[safeKey] = rawValue;
|
|
314
|
+
if (typeof rawValue === 'boolean')
|
|
315
|
+
metadata[safeKey] = rawValue;
|
|
316
|
+
}
|
|
317
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
318
|
+
}
|
|
319
|
+
function normalizeStringList(value, limit, textLimit) {
|
|
320
|
+
if (!Array.isArray(value))
|
|
321
|
+
return undefined;
|
|
322
|
+
const list = value
|
|
323
|
+
.slice(0, limit)
|
|
324
|
+
.map((item) => sanitizePraxisAssistantText(item, textLimit))
|
|
325
|
+
.filter(Boolean);
|
|
326
|
+
return list.length ? list : undefined;
|
|
327
|
+
}
|
|
328
|
+
function normalizeNumberMap(value) {
|
|
329
|
+
if (!isRecord(value))
|
|
330
|
+
return undefined;
|
|
331
|
+
const counts = {};
|
|
332
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
333
|
+
const safeKey = sanitizePraxisAssistantText(key, 64);
|
|
334
|
+
if (!safeKey || typeof rawValue !== 'number' || !Number.isFinite(rawValue))
|
|
335
|
+
continue;
|
|
336
|
+
counts[safeKey] = rawValue;
|
|
337
|
+
}
|
|
338
|
+
return Object.keys(counts).length ? counts : undefined;
|
|
339
|
+
}
|
|
340
|
+
function normalizeMode(value) {
|
|
341
|
+
const mode = sanitizePraxisAssistantText(value, 48);
|
|
342
|
+
if (mode === 'config'
|
|
343
|
+
|| mode === 'agentic-authoring'
|
|
344
|
+
|| mode === 'chat'
|
|
345
|
+
|| mode === 'diagnostic'
|
|
346
|
+
|| mode === 'review'
|
|
347
|
+
|| mode === 'inline-help') {
|
|
348
|
+
return mode;
|
|
349
|
+
}
|
|
350
|
+
return 'chat';
|
|
351
|
+
}
|
|
352
|
+
function normalizeRisk(value) {
|
|
353
|
+
const risk = sanitizePraxisAssistantText(value, 24);
|
|
354
|
+
return risk === 'low' || risk === 'medium' || risk === 'high' || risk === 'blocked'
|
|
355
|
+
? risk
|
|
356
|
+
: undefined;
|
|
357
|
+
}
|
|
358
|
+
function normalizeTone(value) {
|
|
359
|
+
const tone = sanitizePraxisAssistantText(value, 24);
|
|
360
|
+
return tone === 'neutral' || tone === 'info' || tone === 'success' || tone === 'warning' || tone === 'danger'
|
|
361
|
+
? tone
|
|
362
|
+
: undefined;
|
|
363
|
+
}
|
|
364
|
+
function normalizeNonNegativeNumber(value) {
|
|
365
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
366
|
+
}
|
|
367
|
+
function isRecord(value) {
|
|
368
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
369
|
+
}
|
|
370
|
+
function isDefined(value) {
|
|
371
|
+
return value !== null && value !== undefined;
|
|
372
|
+
}
|
|
373
|
+
|
|
63
374
|
function toPraxisAssistantConversationMessages(messages, limit = 12) {
|
|
64
375
|
return messages
|
|
65
376
|
.filter((message) => !!message.text?.trim() && isPraxisAssistantConversationMessageRole(message.role))
|
|
@@ -1590,7 +1901,10 @@ class PraxisAssistantTurnController {
|
|
|
1590
1901
|
const current = this.stateSubject.value;
|
|
1591
1902
|
const effectiveAction = this.resolveSubmitAction(action, current);
|
|
1592
1903
|
const clientTurnId = this.createId('turn');
|
|
1593
|
-
const
|
|
1904
|
+
const displayPrompt = typeof effectiveAction.displayPrompt === 'string'
|
|
1905
|
+
? effectiveAction.displayPrompt.trim()
|
|
1906
|
+
: '';
|
|
1907
|
+
const userMessage = this.buildMessage('user', displayPrompt || normalized, true);
|
|
1594
1908
|
this.patchState({
|
|
1595
1909
|
state: 'processing',
|
|
1596
1910
|
phase: 'contextualize',
|
|
@@ -1889,6 +2203,7 @@ class PraxisAssistantSessionRegistryService {
|
|
|
1889
2203
|
state: normalized.state,
|
|
1890
2204
|
visibility: normalized.visibility,
|
|
1891
2205
|
contextItems: normalized.contextItems,
|
|
2206
|
+
contextSnapshot: normalized.contextSnapshot,
|
|
1892
2207
|
badge: normalized.badge,
|
|
1893
2208
|
icon: normalized.icon,
|
|
1894
2209
|
createdAt: existing?.createdAt ?? now,
|
|
@@ -1900,18 +2215,47 @@ class PraxisAssistantSessionRegistryService {
|
|
|
1900
2215
|
], next.visibility === 'active' ? next.id : null));
|
|
1901
2216
|
return next;
|
|
1902
2217
|
}
|
|
2218
|
+
upsertContextSession(contextSnapshot, descriptor = {}) {
|
|
2219
|
+
const normalizedContext = normalizePraxisAssistantContextSnapshot(contextSnapshot);
|
|
2220
|
+
const identity = normalizedContext.identity;
|
|
2221
|
+
return this.upsertSession({
|
|
2222
|
+
...descriptor,
|
|
2223
|
+
id: identity.sessionId,
|
|
2224
|
+
ownerId: identity.ownerId,
|
|
2225
|
+
ownerType: identity.ownerType,
|
|
2226
|
+
title: descriptor.title?.trim()
|
|
2227
|
+
|| normalizedContext.target?.label
|
|
2228
|
+
|| identity.componentId
|
|
2229
|
+
|| identity.ownerType
|
|
2230
|
+
|| 'Praxis assistant',
|
|
2231
|
+
mode: descriptor.mode || normalizedContext.mode,
|
|
2232
|
+
contextSnapshot: normalizedContext,
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
1903
2235
|
openSession(sessionId) {
|
|
1904
2236
|
return this.setVisibility(sessionId, 'active');
|
|
1905
2237
|
}
|
|
2238
|
+
openContextSession(identity) {
|
|
2239
|
+
return this.openSession(this.resolveSessionId(identity));
|
|
2240
|
+
}
|
|
1906
2241
|
minimizeSession(sessionId) {
|
|
1907
2242
|
return this.setVisibility(sessionId, 'minimized');
|
|
1908
2243
|
}
|
|
2244
|
+
minimizeContextSession(identity) {
|
|
2245
|
+
return this.minimizeSession(this.resolveSessionId(identity));
|
|
2246
|
+
}
|
|
1909
2247
|
removeSession(sessionId) {
|
|
1910
2248
|
this.sessionsState.update((sessions) => sessions.filter((session) => session.id !== sessionId));
|
|
1911
2249
|
}
|
|
2250
|
+
removeContextSession(identity) {
|
|
2251
|
+
this.removeSession(this.resolveSessionId(identity));
|
|
2252
|
+
}
|
|
1912
2253
|
getSession(sessionId) {
|
|
1913
2254
|
return this.sessionsState().find((session) => session.id === sessionId) ?? null;
|
|
1914
2255
|
}
|
|
2256
|
+
getContextSession(identity) {
|
|
2257
|
+
return this.getSession(this.resolveSessionId(identity));
|
|
2258
|
+
}
|
|
1915
2259
|
clear() {
|
|
1916
2260
|
this.sessionsState.set([]);
|
|
1917
2261
|
}
|
|
@@ -1921,6 +2265,13 @@ class PraxisAssistantSessionRegistryService {
|
|
|
1921
2265
|
return null;
|
|
1922
2266
|
return this.upsertSession({ ...session, visibility });
|
|
1923
2267
|
}
|
|
2268
|
+
resolveSessionId(identity) {
|
|
2269
|
+
if (typeof identity === 'string')
|
|
2270
|
+
return identity;
|
|
2271
|
+
if ('identity' in identity)
|
|
2272
|
+
return identity.identity.sessionId;
|
|
2273
|
+
return identity.sessionId;
|
|
2274
|
+
}
|
|
1924
2275
|
normalizeDescriptor(descriptor) {
|
|
1925
2276
|
const id = descriptor.id?.trim();
|
|
1926
2277
|
const ownerId = descriptor.ownerId?.trim();
|
|
@@ -1928,6 +2279,15 @@ class PraxisAssistantSessionRegistryService {
|
|
|
1928
2279
|
if (!id || !ownerId || !ownerType) {
|
|
1929
2280
|
throw new Error('Praxis assistant sessions require id, ownerId and ownerType.');
|
|
1930
2281
|
}
|
|
2282
|
+
const contextSnapshot = descriptor.contextSnapshot
|
|
2283
|
+
? normalizePraxisAssistantContextSnapshot(descriptor.contextSnapshot)
|
|
2284
|
+
: null;
|
|
2285
|
+
if (contextSnapshot) {
|
|
2286
|
+
this.assertContextIdentity(id, ownerId, ownerType, contextSnapshot);
|
|
2287
|
+
}
|
|
2288
|
+
const contextItems = descriptor.contextItems
|
|
2289
|
+
? [...descriptor.contextItems]
|
|
2290
|
+
: this.toShellContextItems(contextSnapshot);
|
|
1931
2291
|
return {
|
|
1932
2292
|
id,
|
|
1933
2293
|
ownerId,
|
|
@@ -1937,12 +2297,29 @@ class PraxisAssistantSessionRegistryService {
|
|
|
1937
2297
|
mode: descriptor.mode || 'chat',
|
|
1938
2298
|
state: descriptor.state || 'idle',
|
|
1939
2299
|
visibility: descriptor.visibility || 'minimized',
|
|
1940
|
-
contextItems
|
|
2300
|
+
contextItems,
|
|
2301
|
+
contextSnapshot,
|
|
1941
2302
|
badge: descriptor.badge?.trim() || '',
|
|
1942
2303
|
icon: descriptor.icon?.trim() || '',
|
|
1943
2304
|
updatedAt: descriptor.updatedAt?.trim() || null,
|
|
1944
2305
|
};
|
|
1945
2306
|
}
|
|
2307
|
+
assertContextIdentity(id, ownerId, ownerType, contextSnapshot) {
|
|
2308
|
+
const identity = contextSnapshot.identity;
|
|
2309
|
+
if (identity.sessionId !== id || identity.ownerId !== ownerId || identity.ownerType !== ownerType) {
|
|
2310
|
+
throw new Error('Praxis assistant session context identity must match id, ownerId and ownerType.');
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
toShellContextItems(contextSnapshot) {
|
|
2314
|
+
if (!contextSnapshot)
|
|
2315
|
+
return [];
|
|
2316
|
+
return contextSnapshot.contextItems.map((item) => ({
|
|
2317
|
+
id: item.id,
|
|
2318
|
+
label: item.label,
|
|
2319
|
+
value: item.value,
|
|
2320
|
+
kind: item.kind,
|
|
2321
|
+
}));
|
|
2322
|
+
}
|
|
1946
2323
|
sortSessions(sessions, activeSessionId) {
|
|
1947
2324
|
return sessions
|
|
1948
2325
|
.map((session) => activeSessionId && session.id !== activeSessionId
|
|
@@ -6103,16 +6480,18 @@ const DEFAULT_LAYOUT = {
|
|
|
6103
6480
|
const DEFAULT_LABELS = {
|
|
6104
6481
|
title: 'Assistente de IA',
|
|
6105
6482
|
subtitle: 'Revise o resultado gerado antes de aplicar.',
|
|
6106
|
-
close: '
|
|
6483
|
+
close: 'Minimizar assistente',
|
|
6107
6484
|
prompt: 'Prompt',
|
|
6108
6485
|
promptPlaceholder: 'Descreva o que você quer criar ou alterar.',
|
|
6109
6486
|
emptyConversation: 'Diga o que você quer criar ou alterar.',
|
|
6110
|
-
submit: '
|
|
6111
|
-
apply: 'Aplicar',
|
|
6487
|
+
submit: 'Interpretar pedido',
|
|
6488
|
+
apply: 'Aplicar ajuste',
|
|
6112
6489
|
conversationAria: 'Conversa com IA',
|
|
6113
6490
|
quickRepliesAria: 'Respostas rápidas',
|
|
6491
|
+
quickReplyDetails: 'Detalhes técnicos',
|
|
6114
6492
|
dragHandleAria: 'Mover assistente de IA',
|
|
6115
6493
|
resizeHandleAria: 'Redimensionar assistente de IA',
|
|
6494
|
+
resetLayout: 'Restaurar posição do assistente',
|
|
6116
6495
|
contextAria: 'Contexto ativo',
|
|
6117
6496
|
attachmentsAria: 'Contexto anexado',
|
|
6118
6497
|
attach: 'Anexar',
|
|
@@ -6149,10 +6528,15 @@ class PraxisAiAssistantShellComponent {
|
|
|
6149
6528
|
panelTestId = '';
|
|
6150
6529
|
submitTestId = '';
|
|
6151
6530
|
applyTestId = '';
|
|
6531
|
+
primaryAction = null;
|
|
6532
|
+
secondaryActions = [];
|
|
6533
|
+
governanceActions = [];
|
|
6152
6534
|
busy = false;
|
|
6153
6535
|
canSubmit = true;
|
|
6154
6536
|
canApply = false;
|
|
6155
6537
|
submitOnEnter = true;
|
|
6538
|
+
showAttachAction = true;
|
|
6539
|
+
enablePastedAttachments = true;
|
|
6156
6540
|
enableFileAttachments = false;
|
|
6157
6541
|
attachmentAccept = '';
|
|
6158
6542
|
attachmentMultiple = true;
|
|
@@ -6165,6 +6549,9 @@ class PraxisAiAssistantShellComponent {
|
|
|
6165
6549
|
promptChange = new EventEmitter();
|
|
6166
6550
|
submitPrompt = new EventEmitter();
|
|
6167
6551
|
apply = new EventEmitter();
|
|
6552
|
+
retryTurn = new EventEmitter();
|
|
6553
|
+
cancelTurn = new EventEmitter();
|
|
6554
|
+
shellAction = new EventEmitter();
|
|
6168
6555
|
close = new EventEmitter();
|
|
6169
6556
|
attach = new EventEmitter();
|
|
6170
6557
|
attachmentsPasted = new EventEmitter();
|
|
@@ -6181,7 +6568,9 @@ class PraxisAiAssistantShellComponent {
|
|
|
6181
6568
|
currentPrompt = '';
|
|
6182
6569
|
resolvedLabels = DEFAULT_LABELS;
|
|
6183
6570
|
currentLayout = { ...DEFAULT_LAYOUT };
|
|
6571
|
+
resizeHandles = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'];
|
|
6184
6572
|
pointerSession = null;
|
|
6573
|
+
baselineLayout = null;
|
|
6185
6574
|
ownedPreviewUrls = new Set();
|
|
6186
6575
|
onWindowPointerMove = (event) => this.handlePointerMove(event);
|
|
6187
6576
|
onWindowPointerUp = (event) => this.finishPointerSession(event);
|
|
@@ -6197,6 +6586,7 @@ class PraxisAiAssistantShellComponent {
|
|
|
6197
6586
|
}
|
|
6198
6587
|
if (changes['layout']) {
|
|
6199
6588
|
this.currentLayout = this.normalizeLayout(this.layout);
|
|
6589
|
+
this.baselineLayout ??= { ...this.currentLayout };
|
|
6200
6590
|
}
|
|
6201
6591
|
if (changes['messages']) {
|
|
6202
6592
|
this.scheduleConversationScroll();
|
|
@@ -6227,6 +6617,9 @@ class PraxisAiAssistantShellComponent {
|
|
|
6227
6617
|
this.onSubmit();
|
|
6228
6618
|
}
|
|
6229
6619
|
onPromptPaste(event) {
|
|
6620
|
+
if (!this.enablePastedAttachments) {
|
|
6621
|
+
return;
|
|
6622
|
+
}
|
|
6230
6623
|
const files = Array.from(event.clipboardData?.files ?? [])
|
|
6231
6624
|
.filter((file) => file.type.startsWith('image/'));
|
|
6232
6625
|
if (this.busy || !files.length) {
|
|
@@ -6261,6 +6654,163 @@ class PraxisAiAssistantShellComponent {
|
|
|
6261
6654
|
return;
|
|
6262
6655
|
this.apply.emit();
|
|
6263
6656
|
}
|
|
6657
|
+
onShellAction(action) {
|
|
6658
|
+
if (this.isShellActionDisabled(action))
|
|
6659
|
+
return;
|
|
6660
|
+
this.shellAction.emit(action);
|
|
6661
|
+
switch (action.kind) {
|
|
6662
|
+
case 'submit-prompt':
|
|
6663
|
+
this.onSubmit();
|
|
6664
|
+
return;
|
|
6665
|
+
case 'apply':
|
|
6666
|
+
this.onApply();
|
|
6667
|
+
return;
|
|
6668
|
+
case 'retry':
|
|
6669
|
+
this.retryTurn.emit();
|
|
6670
|
+
return;
|
|
6671
|
+
case 'cancel':
|
|
6672
|
+
this.cancelTurn.emit();
|
|
6673
|
+
return;
|
|
6674
|
+
default:
|
|
6675
|
+
return;
|
|
6676
|
+
}
|
|
6677
|
+
}
|
|
6678
|
+
getPrimaryAction() {
|
|
6679
|
+
return this.normalizeShellAction(this.primaryAction, this.getDefaultPrimaryAction());
|
|
6680
|
+
}
|
|
6681
|
+
getSecondaryActions() {
|
|
6682
|
+
const actions = [
|
|
6683
|
+
...this.secondaryActions,
|
|
6684
|
+
...this.governanceActions,
|
|
6685
|
+
];
|
|
6686
|
+
if (this.canApply) {
|
|
6687
|
+
actions.push({
|
|
6688
|
+
id: 'apply',
|
|
6689
|
+
kind: 'apply',
|
|
6690
|
+
label: this.resolvedLabels.apply,
|
|
6691
|
+
icon: 'check_circle',
|
|
6692
|
+
tone: 'governance',
|
|
6693
|
+
disabled: !this.canApply,
|
|
6694
|
+
testId: this.applyTestId || `${this.testIdPrefix}-apply`,
|
|
6695
|
+
});
|
|
6696
|
+
}
|
|
6697
|
+
if (this.state === 'error') {
|
|
6698
|
+
actions.push({
|
|
6699
|
+
id: 'retry',
|
|
6700
|
+
kind: 'retry',
|
|
6701
|
+
label: 'Tentar novamente',
|
|
6702
|
+
icon: 'replay',
|
|
6703
|
+
tone: 'warning',
|
|
6704
|
+
testId: `${this.testIdPrefix}-retry`,
|
|
6705
|
+
});
|
|
6706
|
+
}
|
|
6707
|
+
if (this.hasRecoverableTurn()) {
|
|
6708
|
+
actions.push({
|
|
6709
|
+
id: 'cancel',
|
|
6710
|
+
kind: 'cancel',
|
|
6711
|
+
label: 'Cancelar pedido',
|
|
6712
|
+
icon: 'close',
|
|
6713
|
+
tone: 'neutral',
|
|
6714
|
+
testId: `${this.testIdPrefix}-cancel-turn`,
|
|
6715
|
+
});
|
|
6716
|
+
}
|
|
6717
|
+
return actions.map((action) => this.normalizeShellAction(action));
|
|
6718
|
+
}
|
|
6719
|
+
getPrimaryActionTooltip(action) {
|
|
6720
|
+
return action.iconOnly ? action.ariaLabel || action.label : '';
|
|
6721
|
+
}
|
|
6722
|
+
isShellActionDisabled(action) {
|
|
6723
|
+
return Boolean(this.busy
|
|
6724
|
+
|| action.disabled
|
|
6725
|
+
|| (action.requiresPrompt && !this.currentPrompt.trim())
|
|
6726
|
+
|| (action.kind === 'submit-prompt' && !this.canSubmit)
|
|
6727
|
+
|| (action.kind === 'apply' && !this.canApply));
|
|
6728
|
+
}
|
|
6729
|
+
getShellActionTone(action) {
|
|
6730
|
+
const tone = (action.tone || (action.kind === 'apply' ? 'governance' : 'secondary')).toLowerCase();
|
|
6731
|
+
switch (tone) {
|
|
6732
|
+
case 'primary':
|
|
6733
|
+
case 'secondary':
|
|
6734
|
+
case 'governance':
|
|
6735
|
+
case 'success':
|
|
6736
|
+
case 'warning':
|
|
6737
|
+
case 'danger':
|
|
6738
|
+
case 'neutral':
|
|
6739
|
+
return tone;
|
|
6740
|
+
default:
|
|
6741
|
+
return 'secondary';
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
trackShellAction(_index, action) {
|
|
6745
|
+
return action.id;
|
|
6746
|
+
}
|
|
6747
|
+
hasRecoverableTurn() {
|
|
6748
|
+
if (this.busy || this.state === 'idle' || this.state === 'listening') {
|
|
6749
|
+
return false;
|
|
6750
|
+
}
|
|
6751
|
+
return this.messages.length > 0
|
|
6752
|
+
|| this.quickReplies.length > 0
|
|
6753
|
+
|| this.canApply
|
|
6754
|
+
|| this.state === 'clarification'
|
|
6755
|
+
|| this.state === 'review'
|
|
6756
|
+
|| this.state === 'error';
|
|
6757
|
+
}
|
|
6758
|
+
getDefaultPrimaryAction() {
|
|
6759
|
+
const base = {
|
|
6760
|
+
id: 'submit',
|
|
6761
|
+
kind: 'submit-prompt',
|
|
6762
|
+
label: this.resolvedLabels.submit,
|
|
6763
|
+
icon: 'auto_awesome',
|
|
6764
|
+
tone: 'primary',
|
|
6765
|
+
requiresPrompt: true,
|
|
6766
|
+
testId: this.submitTestId || `${this.testIdPrefix}-submit`,
|
|
6767
|
+
};
|
|
6768
|
+
switch (this.state) {
|
|
6769
|
+
case 'processing':
|
|
6770
|
+
return {
|
|
6771
|
+
...base,
|
|
6772
|
+
label: 'Interpretando...',
|
|
6773
|
+
icon: 'hourglass_top',
|
|
6774
|
+
disabled: true,
|
|
6775
|
+
};
|
|
6776
|
+
case 'clarification':
|
|
6777
|
+
return {
|
|
6778
|
+
...base,
|
|
6779
|
+
label: 'Responder',
|
|
6780
|
+
icon: 'question_answer',
|
|
6781
|
+
};
|
|
6782
|
+
case 'review':
|
|
6783
|
+
return {
|
|
6784
|
+
...base,
|
|
6785
|
+
label: 'Refinar pedido',
|
|
6786
|
+
icon: 'tune',
|
|
6787
|
+
};
|
|
6788
|
+
case 'applying':
|
|
6789
|
+
return {
|
|
6790
|
+
...base,
|
|
6791
|
+
label: 'Aplicando...',
|
|
6792
|
+
icon: 'sync',
|
|
6793
|
+
disabled: true,
|
|
6794
|
+
};
|
|
6795
|
+
case 'error':
|
|
6796
|
+
return {
|
|
6797
|
+
...base,
|
|
6798
|
+
label: 'Corrigir pedido',
|
|
6799
|
+
icon: 'edit_note',
|
|
6800
|
+
tone: 'warning',
|
|
6801
|
+
};
|
|
6802
|
+
case 'success':
|
|
6803
|
+
return {
|
|
6804
|
+
...base,
|
|
6805
|
+
label: 'Novo pedido',
|
|
6806
|
+
icon: 'add_comment',
|
|
6807
|
+
};
|
|
6808
|
+
case 'idle':
|
|
6809
|
+
case 'listening':
|
|
6810
|
+
default:
|
|
6811
|
+
return base;
|
|
6812
|
+
}
|
|
6813
|
+
}
|
|
6264
6814
|
onQuickReply(reply) {
|
|
6265
6815
|
if (this.busy)
|
|
6266
6816
|
return;
|
|
@@ -6271,6 +6821,21 @@ class PraxisAiAssistantShellComponent {
|
|
|
6271
6821
|
const description = reply.description?.trim();
|
|
6272
6822
|
return description ? `${label}. ${description}` : label;
|
|
6273
6823
|
}
|
|
6824
|
+
getQuickReplyTechnicalDetails(reply) {
|
|
6825
|
+
const hints = reply.contextHints;
|
|
6826
|
+
const details = this.asRecord(hints?.['technicalDetails']) ?? hints;
|
|
6827
|
+
if (!details)
|
|
6828
|
+
return '';
|
|
6829
|
+
const submitMethod = this.stringHint(details, 'submitMethod') || this.stringHint(details, 'operation');
|
|
6830
|
+
const submitUrl = this.stringHint(details, 'submitUrl');
|
|
6831
|
+
const resourcePath = this.stringHint(details, 'resourcePath');
|
|
6832
|
+
const schemaUrl = this.stringHint(details, 'schemaUrl');
|
|
6833
|
+
return [
|
|
6834
|
+
submitMethod && submitUrl ? `${submitMethod.toUpperCase()} ${submitUrl}` : '',
|
|
6835
|
+
resourcePath && resourcePath !== submitUrl ? `Recurso: ${resourcePath}` : '',
|
|
6836
|
+
schemaUrl ? `Schema: ${schemaUrl}` : '',
|
|
6837
|
+
].filter(Boolean).join('\n');
|
|
6838
|
+
}
|
|
6274
6839
|
getQuickReplyTone(reply) {
|
|
6275
6840
|
const tone = (reply.tone || reply.kind || 'neutral').toLowerCase();
|
|
6276
6841
|
switch (tone) {
|
|
@@ -6293,6 +6858,44 @@ class PraxisAiAssistantShellComponent {
|
|
|
6293
6858
|
return 'neutral';
|
|
6294
6859
|
}
|
|
6295
6860
|
}
|
|
6861
|
+
asRecord(value) {
|
|
6862
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
6863
|
+
? value
|
|
6864
|
+
: null;
|
|
6865
|
+
}
|
|
6866
|
+
stringHint(source, key) {
|
|
6867
|
+
const value = source[key];
|
|
6868
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
6869
|
+
}
|
|
6870
|
+
getCloseIcon() {
|
|
6871
|
+
const label = this.resolvedLabels.close.toLocaleLowerCase('pt-BR');
|
|
6872
|
+
return label.includes('minimiz') ? 'remove' : 'close';
|
|
6873
|
+
}
|
|
6874
|
+
getResetLayoutLabel() {
|
|
6875
|
+
return this.resolvedLabels.resetLayout || DEFAULT_LABELS.resetLayout || 'Restaurar posição do assistente';
|
|
6876
|
+
}
|
|
6877
|
+
resetLayout() {
|
|
6878
|
+
const bounds = this.resolveViewportBounds();
|
|
6879
|
+
const layout = this.clampLayout(this.baselineLayout ?? DEFAULT_LAYOUT, bounds.width, bounds.height);
|
|
6880
|
+
this.currentLayout = layout;
|
|
6881
|
+
this.layoutChange.emit(layout);
|
|
6882
|
+
this.cdr.markForCheck();
|
|
6883
|
+
}
|
|
6884
|
+
normalizeShellAction(action, fallback) {
|
|
6885
|
+
const source = action ?? fallback;
|
|
6886
|
+
return {
|
|
6887
|
+
id: source?.id || fallback?.id || 'action',
|
|
6888
|
+
label: source?.label || fallback?.label || '',
|
|
6889
|
+
kind: source?.kind ?? fallback?.kind ?? 'custom',
|
|
6890
|
+
icon: source?.icon ?? fallback?.icon ?? null,
|
|
6891
|
+
tone: source?.tone ?? fallback?.tone ?? null,
|
|
6892
|
+
disabled: source?.disabled ?? fallback?.disabled ?? false,
|
|
6893
|
+
requiresPrompt: source?.requiresPrompt ?? fallback?.requiresPrompt ?? false,
|
|
6894
|
+
testId: source?.testId ?? fallback?.testId ?? null,
|
|
6895
|
+
ariaLabel: source?.ariaLabel ?? fallback?.ariaLabel ?? null,
|
|
6896
|
+
iconOnly: source?.iconOnly ?? fallback?.iconOnly ?? false,
|
|
6897
|
+
};
|
|
6898
|
+
}
|
|
6296
6899
|
onRemoveAttachment(attachment) {
|
|
6297
6900
|
if (this.busy)
|
|
6298
6901
|
return;
|
|
@@ -6310,6 +6913,26 @@ class PraxisAiAssistantShellComponent {
|
|
|
6310
6913
|
this.resendMessage.emit(message);
|
|
6311
6914
|
}
|
|
6312
6915
|
}
|
|
6916
|
+
getMessageActionIcon(action) {
|
|
6917
|
+
if (action.icon)
|
|
6918
|
+
return action.icon;
|
|
6919
|
+
switch (action.kind) {
|
|
6920
|
+
case 'edit':
|
|
6921
|
+
return 'edit';
|
|
6922
|
+
case 'resend':
|
|
6923
|
+
return 'replay';
|
|
6924
|
+
case 'copy':
|
|
6925
|
+
return 'content_copy';
|
|
6926
|
+
default:
|
|
6927
|
+
return '';
|
|
6928
|
+
}
|
|
6929
|
+
}
|
|
6930
|
+
getMessageActionLabel(action) {
|
|
6931
|
+
return action.ariaLabel || action.label;
|
|
6932
|
+
}
|
|
6933
|
+
isMessageActionIconOnly(action) {
|
|
6934
|
+
return action.iconOnly ?? !!this.getMessageActionIcon(action);
|
|
6935
|
+
}
|
|
6313
6936
|
getModeLabel() {
|
|
6314
6937
|
switch (this.mode) {
|
|
6315
6938
|
case 'config':
|
|
@@ -6353,10 +6976,13 @@ class PraxisAiAssistantShellComponent {
|
|
|
6353
6976
|
return;
|
|
6354
6977
|
this.startPointerSession('drag', event);
|
|
6355
6978
|
}
|
|
6356
|
-
startResize(event) {
|
|
6979
|
+
startResize(direction, event) {
|
|
6357
6980
|
if (!this.resizable || event.button !== 0)
|
|
6358
6981
|
return;
|
|
6359
|
-
this.startPointerSession('resize', event);
|
|
6982
|
+
this.startPointerSession('resize', event, direction);
|
|
6983
|
+
}
|
|
6984
|
+
trackResizeHandle(_index, direction) {
|
|
6985
|
+
return direction;
|
|
6360
6986
|
}
|
|
6361
6987
|
trackMessage(_index, message) {
|
|
6362
6988
|
return message.id;
|
|
@@ -6373,15 +6999,16 @@ class PraxisAiAssistantShellComponent {
|
|
|
6373
6999
|
trackAttachment(_index, attachment) {
|
|
6374
7000
|
return attachment.id;
|
|
6375
7001
|
}
|
|
6376
|
-
startPointerSession(mode, event) {
|
|
7002
|
+
startPointerSession(mode, event, resizeDirection) {
|
|
6377
7003
|
event.preventDefault();
|
|
6378
7004
|
event.stopPropagation();
|
|
6379
7005
|
const panel = this.panel?.nativeElement;
|
|
6380
7006
|
if (!panel)
|
|
6381
7007
|
return;
|
|
6382
|
-
const bounds = this.
|
|
7008
|
+
const bounds = this.resolveViewportBounds();
|
|
6383
7009
|
this.pointerSession = {
|
|
6384
7010
|
mode,
|
|
7011
|
+
resizeDirection,
|
|
6385
7012
|
pointerId: event.pointerId,
|
|
6386
7013
|
startX: event.clientX,
|
|
6387
7014
|
startY: event.clientY,
|
|
@@ -6411,15 +7038,40 @@ class PraxisAiAssistantShellComponent {
|
|
|
6411
7038
|
left: session.startLayout.left + deltaX,
|
|
6412
7039
|
top: session.startLayout.top + deltaY,
|
|
6413
7040
|
}
|
|
6414
|
-
:
|
|
6415
|
-
...session.startLayout,
|
|
6416
|
-
width: session.startLayout.width + deltaX,
|
|
6417
|
-
height: session.startLayout.height + deltaY,
|
|
6418
|
-
};
|
|
7041
|
+
: this.resizeLayout(session, deltaX, deltaY);
|
|
6419
7042
|
this.currentLayout = this.clampLayout(next, session.boundsWidth, session.boundsHeight);
|
|
6420
7043
|
this.layoutChange.emit(this.currentLayout);
|
|
6421
7044
|
this.cdr.markForCheck();
|
|
6422
7045
|
}
|
|
7046
|
+
resizeLayout(session, deltaX, deltaY) {
|
|
7047
|
+
const direction = session.resizeDirection ?? 'se';
|
|
7048
|
+
const start = session.startLayout;
|
|
7049
|
+
const right = start.left + start.width;
|
|
7050
|
+
const bottom = start.top + start.height;
|
|
7051
|
+
let left = start.left;
|
|
7052
|
+
let top = start.top;
|
|
7053
|
+
let width = start.width;
|
|
7054
|
+
let height = start.height;
|
|
7055
|
+
if (direction.includes('e')) {
|
|
7056
|
+
const maxWidth = Math.max(this.minWidth, session.boundsWidth - start.left - this.margin);
|
|
7057
|
+
width = this.clamp(start.width + deltaX, this.minWidth, maxWidth);
|
|
7058
|
+
}
|
|
7059
|
+
if (direction.includes('s')) {
|
|
7060
|
+
const maxHeight = Math.max(this.minHeight, session.boundsHeight - start.top - this.margin);
|
|
7061
|
+
height = this.clamp(start.height + deltaY, this.minHeight, maxHeight);
|
|
7062
|
+
}
|
|
7063
|
+
if (direction.includes('w')) {
|
|
7064
|
+
const nextLeft = this.clamp(start.left + deltaX, this.margin, right - this.minWidth);
|
|
7065
|
+
left = nextLeft;
|
|
7066
|
+
width = right - nextLeft;
|
|
7067
|
+
}
|
|
7068
|
+
if (direction.includes('n')) {
|
|
7069
|
+
const nextTop = this.clamp(start.top + deltaY, this.margin, bottom - this.minHeight);
|
|
7070
|
+
top = nextTop;
|
|
7071
|
+
height = bottom - nextTop;
|
|
7072
|
+
}
|
|
7073
|
+
return { left, top, width, height };
|
|
7074
|
+
}
|
|
6423
7075
|
finishPointerSession(event) {
|
|
6424
7076
|
if (this.pointerSession && event.pointerId === this.pointerSession.pointerId) {
|
|
6425
7077
|
try {
|
|
@@ -6437,10 +7089,9 @@ class PraxisAiAssistantShellComponent {
|
|
|
6437
7089
|
window.removeEventListener('pointerup', this.onWindowPointerUp);
|
|
6438
7090
|
window.removeEventListener('pointercancel', this.onWindowPointerUp);
|
|
6439
7091
|
}
|
|
6440
|
-
|
|
6441
|
-
const
|
|
6442
|
-
const
|
|
6443
|
-
const height = hostBounds?.height || (typeof window !== 'undefined' ? window.innerHeight : 768);
|
|
7092
|
+
resolveViewportBounds() {
|
|
7093
|
+
const width = typeof window !== 'undefined' ? window.innerWidth : 1024;
|
|
7094
|
+
const height = typeof window !== 'undefined' ? window.innerHeight : 768;
|
|
6444
7095
|
return {
|
|
6445
7096
|
width: Math.max(width, this.minWidth + this.margin * 2),
|
|
6446
7097
|
height: Math.max(height, this.minHeight + this.margin * 2),
|
|
@@ -6533,7 +7184,7 @@ class PraxisAiAssistantShellComponent {
|
|
|
6533
7184
|
this.ownedPreviewUrls.delete(previewUrl);
|
|
6534
7185
|
}
|
|
6535
7186
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisAiAssistantShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6536
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisAiAssistantShellComponent, isStandalone: true, selector: "praxis-ai-assistant-shell", inputs: { labels: "labels", mode: "mode", state: "state", contextItems: "contextItems", attachments: "attachments", messages: "messages", quickReplies: "quickReplies", prompt: "prompt", statusText: "statusText", errorText: "errorText", testIdPrefix: "testIdPrefix", panelTestId: "panelTestId", submitTestId: "submitTestId", applyTestId: "applyTestId", busy: "busy", canSubmit: "canSubmit", canApply: "canApply", submitOnEnter: "submitOnEnter", enableFileAttachments: "enableFileAttachments", attachmentAccept: "attachmentAccept", attachmentMultiple: "attachmentMultiple", draggable: "draggable", resizable: "resizable", minWidth: "minWidth", minHeight: "minHeight", margin: "margin", layout: "layout" }, outputs: { promptChange: "promptChange", submitPrompt: "submitPrompt", apply: "apply", close: "close", attach: "attach", attachmentsPasted: "attachmentsPasted", attachmentsSelected: "attachmentsSelected", removeAttachment: "removeAttachment", messageAction: "messageAction", editMessage: "editMessage", resendMessage: "resendMessage", quickReply: "quickReply", layoutChange: "layoutChange" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, static: true }, { propertyName: "conversation", first: true, predicate: ["conversation"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<section\n #panel\n class=\"praxis-ai-assistant-shell\"\n role=\"dialog\"\n [attr.aria-label]=\"resolvedLabels.title\"\n [attr.aria-busy]=\"busy ? 'true' : null\"\n [style.left.px]=\"currentLayout.left\"\n [style.top.px]=\"currentLayout.top\"\n [style.width.px]=\"currentLayout.width\"\n [style.height.px]=\"currentLayout.height\"\n [attr.data-testid]=\"panelTestId || testIdPrefix\"\n>\n <header\n class=\"praxis-ai-assistant-shell__header\"\n [attr.data-testid]=\"testIdPrefix + '-drag-handle'\"\n [attr.aria-label]=\"resolvedLabels.dragHandleAria\"\n (pointerdown)=\"startDrag($event)\"\n >\n <div class=\"praxis-ai-assistant-shell__identity\" aria-hidden=\"true\">\n <mat-icon>auto_awesome</mat-icon>\n </div>\n <div class=\"praxis-ai-assistant-shell__title-group\">\n <strong>{{ resolvedLabels.title }}</strong>\n <p *ngIf=\"resolvedLabels.subtitle\">{{ resolvedLabels.subtitle }}</p>\n </div>\n <div class=\"praxis-ai-assistant-shell__badges\" aria-hidden=\"true\">\n <span class=\"praxis-ai-assistant-shell__badge\">{{ getModeLabel() }}</span>\n <span class=\"praxis-ai-assistant-shell__badge\" [class.praxis-ai-assistant-shell__badge--error]=\"state === 'error'\">\n {{ getStateLabel() }}\n </span>\n </div>\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"resolvedLabels.close\"\n [attr.aria-label]=\"resolvedLabels.close\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"close.emit()\"\n [attr.data-testid]=\"testIdPrefix + '-close'\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"praxis-ai-assistant-shell__body\">\n <div\n *ngIf=\"contextItems.length\"\n class=\"praxis-ai-assistant-shell__context\"\n [attr.aria-label]=\"resolvedLabels.contextAria\"\n [attr.data-testid]=\"testIdPrefix + '-context'\"\n >\n <span\n *ngFor=\"let item of contextItems; trackBy: trackContextItem\"\n class=\"praxis-ai-assistant-shell__context-item\"\n [attr.data-testid]=\"testIdPrefix + '-context-' + item.id\"\n >\n <mat-icon *ngIf=\"item.icon\" aria-hidden=\"true\">{{ item.icon }}</mat-icon>\n <span class=\"praxis-ai-assistant-shell__context-label\">{{ item.label }}</span>\n <span *ngIf=\"item.value\" class=\"praxis-ai-assistant-shell__context-value\">{{ item.value }}</span>\n </span>\n </div>\n\n <div\n #conversation\n class=\"praxis-ai-assistant-shell__conversation\"\n [attr.data-testid]=\"testIdPrefix + '-conversation'\"\n [attr.aria-label]=\"resolvedLabels.conversationAria\"\n >\n <article\n *ngIf=\"!messages.length\"\n class=\"praxis-ai-assistant-shell__message praxis-ai-assistant-shell__message--assistant\"\n [attr.data-testid]=\"testIdPrefix + '-message-assistant-empty'\"\n >\n {{ resolvedLabels.emptyConversation }}\n </article>\n <article\n *ngFor=\"let message of messages; trackBy: trackMessage\"\n class=\"praxis-ai-assistant-shell__message\"\n [class.praxis-ai-assistant-shell__message--user]=\"message.role === 'user'\"\n [class.praxis-ai-assistant-shell__message--assistant]=\"message.role === 'assistant'\"\n [class.praxis-ai-assistant-shell__message--status]=\"message.role === 'status'\"\n [class.praxis-ai-assistant-shell__message--error]=\"message.role === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-message-' + message.role\"\n >\n {{ message.text }}\n <div\n *ngIf=\"message.actions?.length || message.editable || message.resendable\"\n class=\"praxis-ai-assistant-shell__message-actions\"\n >\n <button\n *ngIf=\"message.editable\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy\"\n [attr.data-testid]=\"testIdPrefix + '-message-edit-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'edit', label: resolvedLabels.editMessage, kind: 'edit' })\"\n >\n {{ resolvedLabels.editMessage }}\n </button>\n <button\n *ngIf=\"message.resendable\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy\"\n [attr.data-testid]=\"testIdPrefix + '-message-resend-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'resend', label: resolvedLabels.resendMessage, kind: 'resend' })\"\n >\n {{ resolvedLabels.resendMessage }}\n </button>\n <button\n *ngFor=\"let action of message.actions || []; trackBy: trackMessageAction\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy || action.disabled\"\n [attr.data-testid]=\"testIdPrefix + '-message-action-' + message.id + '-' + action.id\"\n (click)=\"onMessageAction(message, action)\"\n >\n {{ action.label }}\n </button>\n </div>\n </article>\n </div>\n\n <div\n *ngIf=\"attachments.length\"\n class=\"praxis-ai-assistant-shell__attachments\"\n [attr.aria-label]=\"resolvedLabels.attachmentsAria\"\n [attr.data-testid]=\"testIdPrefix + '-attachments'\"\n >\n <div\n *ngFor=\"let attachment of attachments; trackBy: trackAttachment\"\n class=\"praxis-ai-assistant-shell__attachment\"\n [class.praxis-ai-assistant-shell__attachment--error]=\"attachment.status === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-' + attachment.id\"\n >\n <img\n *ngIf=\"attachment.previewUrl && attachment.kind === 'image'\"\n class=\"praxis-ai-assistant-shell__attachment-preview\"\n [src]=\"attachment.previewUrl\"\n [alt]=\"attachment.name\"\n >\n <span class=\"praxis-ai-assistant-shell__attachment-name\">{{ attachment.name }}</span>\n <span class=\"praxis-ai-assistant-shell__attachment-kind\">{{ attachment.kind }}</span>\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.removeAttachment\"\n [attr.aria-label]=\"resolvedLabels.removeAttachment + ': ' + attachment.name\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-remove-' + attachment.id\"\n (click)=\"onRemoveAttachment(attachment)\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <div\n *ngIf=\"quickReplies.length\"\n class=\"praxis-ai-assistant-shell__quick-replies\"\n [attr.data-testid]=\"testIdPrefix + '-quick-replies'\"\n [attr.aria-label]=\"resolvedLabels.quickRepliesAria\"\n >\n <button\n *ngFor=\"let reply of quickReplies; trackBy: trackQuickReply\"\n mat-stroked-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__quick-reply\"\n [ngClass]=\"'praxis-ai-assistant-shell__quick-reply--tone-' + getQuickReplyTone(reply)\"\n [attr.data-testid]=\"testIdPrefix + '-quick-reply-' + (reply.id || reply.kind)\"\n [attr.aria-label]=\"getQuickReplyAriaLabel(reply)\"\n [disabled]=\"busy\"\n (click)=\"onQuickReply(reply)\"\n >\n <mat-icon\n *ngIf=\"reply.icon\"\n class=\"praxis-ai-assistant-shell__quick-reply-icon\"\n aria-hidden=\"true\"\n >\n {{ reply.icon }}\n </mat-icon>\n <span class=\"praxis-ai-assistant-shell__quick-reply-copy\">\n <span class=\"praxis-ai-assistant-shell__quick-reply-label\">{{ reply.label }}</span>\n <span\n *ngIf=\"reply.description\"\n class=\"praxis-ai-assistant-shell__quick-reply-description\"\n >\n {{ reply.description }}\n </span>\n </span>\n </button>\n </div>\n <p\n *ngIf=\"statusText\"\n class=\"praxis-ai-assistant-shell__status\"\n [attr.data-testid]=\"testIdPrefix + '-status'\"\n >\n {{ statusText }}\n </p>\n <p\n *ngIf=\"errorText\"\n class=\"praxis-ai-assistant-shell__error\"\n [attr.data-testid]=\"testIdPrefix + '-error'\"\n >\n {{ errorText }}\n </p>\n </div>\n\n <footer class=\"praxis-ai-assistant-shell__footer\">\n <label class=\"praxis-ai-assistant-shell__label\" for=\"praxis-ai-assistant-shell-prompt\">\n {{ resolvedLabels.prompt }}\n </label>\n <div class=\"praxis-ai-assistant-shell__composer\">\n <textarea\n id=\"praxis-ai-assistant-shell-prompt\"\n class=\"praxis-ai-assistant-shell__prompt\"\n [attr.data-testid]=\"testIdPrefix + '-prompt'\"\n [placeholder]=\"resolvedLabels.promptPlaceholder\"\n [ngModel]=\"currentPrompt\"\n [disabled]=\"busy\"\n (ngModelChange)=\"onPromptInput($event)\"\n (keydown)=\"onPromptKeydown($event)\"\n (paste)=\"onPromptPaste($event)\"\n ></textarea>\n <div class=\"praxis-ai-assistant-shell__composer-actions\">\n <input\n #attachmentInput\n type=\"file\"\n hidden\n [attr.accept]=\"attachmentAccept || null\"\n [attr.multiple]=\"attachmentMultiple ? '' : null\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-input'\"\n (change)=\"onAttachmentFilesSelected($event)\"\n >\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"busy\"\n (click)=\"onAttachClick(attachmentInput)\"\n [attr.data-testid]=\"testIdPrefix + '-attach'\"\n >\n <mat-icon>attach_file</mat-icon>\n {{ resolvedLabels.attach }}\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n [disabled]=\"busy || !canSubmit || !currentPrompt.trim()\"\n (click)=\"onSubmit()\"\n [attr.data-testid]=\"submitTestId || (testIdPrefix + '-submit')\"\n >\n {{ resolvedLabels.submit }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"busy || !canApply\"\n (click)=\"onApply()\"\n [attr.data-testid]=\"applyTestId || (testIdPrefix + '-apply')\"\n >\n {{ resolvedLabels.apply }}\n </button>\n <mat-spinner *ngIf=\"busy\" diameter=\"20\" [attr.data-testid]=\"testIdPrefix + '-spinner'\"></mat-spinner>\n </div>\n </div>\n </footer>\n\n <button\n *ngIf=\"resizable\"\n type=\"button\"\n class=\"praxis-ai-assistant-shell__resize-handle\"\n [attr.data-testid]=\"testIdPrefix + '-resize-handle'\"\n [attr.aria-label]=\"resolvedLabels.resizeHandleAria\"\n [matTooltip]=\"resolvedLabels.resizeHandleAria\"\n (pointerdown)=\"startResize($event)\"\n ></button>\n</section>\n", styles: [":host{display:block}.praxis-ai-assistant-shell{position:absolute;box-sizing:border-box;min-width:360px;min-height:360px;display:flex;flex-direction:column;overflow:hidden;color:var(--md-sys-color-on-surface, #f8fafc);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface, #0f172a);box-shadow:0 24px 60px #0006;z-index:10}.praxis-ai-assistant-shell__header{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:12px;border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:radial-gradient(circle at top left,rgba(96,165,250,.22),transparent 34%),var(--md-sys-color-surface-container, #172033)}.praxis-ai-assistant-shell__header{justify-content:flex-start;border-bottom:1px solid;cursor:move;touch-action:none}.praxis-ai-assistant-shell__identity{flex:0 0 auto;display:grid;place-items:center;width:34px;height:34px;border-radius:8px;background:var(--md-sys-color-primary, #60a5fa);color:var(--md-sys-color-on-primary, #020617);box-shadow:0 8px 20px #60a5fa4d}.praxis-ai-assistant-shell__identity mat-icon{width:20px;height:20px;font-size:20px}.praxis-ai-assistant-shell__title-group{min-width:0;flex:1 1 auto;display:grid;gap:3px}.praxis-ai-assistant-shell__badges{flex:0 1 auto;display:flex;align-items:center;justify-content:flex-end;gap:6px;min-width:0;flex-wrap:wrap}.praxis-ai-assistant-shell__badge{display:inline-flex;align-items:center;min-height:22px;max-width:140px;padding:2px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 80%,transparent);border-radius:8px;color:var(--md-sys-color-on-surface-variant, #cbd5e1);background:var(--md-sys-color-surface-container-high, #263244);font-size:11px;line-height:1.2;overflow-wrap:anywhere}.praxis-ai-assistant-shell__badge--error{color:var(--md-sys-color-error, #ff6b6b);border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__title-group strong,.praxis-ai-assistant-shell__title-group p{min-width:0;margin:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__title-group p{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:12px;line-height:1.35}.praxis-ai-assistant-shell__body{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:12px;padding:14px 14px 8px;overflow:auto;background:linear-gradient(180deg,var(--md-sys-color-surface-container-low, #111827),var(--md-sys-color-surface, #0f172a))}.praxis-ai-assistant-shell__context,.praxis-ai-assistant-shell__attachments{flex:0 0 auto;display:flex;align-items:center;gap:8px;overflow-x:auto}.praxis-ai-assistant-shell__context-item{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;max-width:240px;padding:5px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);font-size:12px;line-height:1.25}.praxis-ai-assistant-shell__context-item mat-icon{width:16px;height:16px;font-size:16px}.praxis-ai-assistant-shell__context-label,.praxis-ai-assistant-shell__context-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.praxis-ai-assistant-shell__context-label{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__context-value{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-ai-assistant-shell__prompt{box-sizing:border-box;width:100%;min-height:56px;max-height:128px;resize:none;border:0;padding:12px;color:var(--md-sys-color-on-surface, #f8fafc);background:transparent;font:inherit;line-height:1.45;outline:none}.praxis-ai-assistant-shell__conversation{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:8px;overflow:auto;padding:2px}.praxis-ai-assistant-shell__message{max-width:86%;align-self:flex-start;padding:9px 11px;border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);color:var(--md-sys-color-on-surface, #f8fafc);font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__message-actions{display:flex;align-items:center;gap:4px;flex-wrap:wrap;margin-top:8px}.praxis-ai-assistant-shell__message-action{min-height:28px;padding:0 8px;border-radius:8px;font-size:12px}.praxis-ai-assistant-shell__message--assistant{border-bottom-left-radius:2px}.praxis-ai-assistant-shell__message--user{align-self:flex-end;border-bottom-right-radius:2px;background:var(--md-sys-color-primary-container, #17375f);color:var(--md-sys-color-on-primary-container, #f8fafc)}.praxis-ai-assistant-shell__message--status{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__message--error,.praxis-ai-assistant-shell__error{color:var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-replies{display:flex;flex-wrap:wrap;gap:8px}.praxis-ai-assistant-shell__attachment{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;max-width:260px;min-height:34px;padding:4px 4px 4px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244)}.praxis-ai-assistant-shell__attachment--error{border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__attachment-preview{flex:0 0 auto;width:28px;height:28px;border-radius:6px;object-fit:cover}.praxis-ai-assistant-shell__attachment-name,.praxis-ai-assistant-shell__attachment-kind{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.praxis-ai-assistant-shell__attachment-name{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__attachment-kind{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__quick-reply{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-primary, #60a5fa);--praxis-ai-assistant-shell-quick-reply-background: color-mix( in srgb, var(--praxis-ai-assistant-shell-quick-reply-accent) 12%, var(--md-sys-color-surface-container-high, #263244) );--praxis-ai-assistant-shell-quick-reply-foreground: var(--md-sys-color-on-surface, #f8fafc);max-width:100%;min-height:38px;border-color:color-mix(in srgb,var(--praxis-ai-assistant-shell-quick-reply-accent) 58%,transparent);border-radius:8px;color:var(--praxis-ai-assistant-shell-quick-reply-foreground);background:var(--praxis-ai-assistant-shell-quick-reply-background);white-space:normal;text-align:left}.praxis-ai-assistant-shell__quick-reply ::ng-deep .mdc-button__label{min-width:0;display:inline-flex;align-items:center;gap:8px;width:100%}.praxis-ai-assistant-shell__quick-reply-icon{flex:0 0 auto;width:18px;height:18px;color:var(--praxis-ai-assistant-shell-quick-reply-accent);font-size:18px}.praxis-ai-assistant-shell__quick-reply-copy{min-width:0;display:grid;gap:2px}.praxis-ai-assistant-shell__quick-reply-label,.praxis-ai-assistant-shell__quick-reply-description{min-width:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__quick-reply-label{font-weight:600;line-height:1.2}.praxis-ai-assistant-shell__quick-reply-description{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:11px;line-height:1.25}.praxis-ai-assistant-shell__quick-reply--tone-analytics{--praxis-ai-assistant-shell-quick-reply-accent: #38bdf8}.praxis-ai-assistant-shell__quick-reply--tone-resource{--praxis-ai-assistant-shell-quick-reply-accent: #34d399}.praxis-ai-assistant-shell__quick-reply--tone-warning{--praxis-ai-assistant-shell-quick-reply-accent: #facc15}.praxis-ai-assistant-shell__quick-reply--tone-success{--praxis-ai-assistant-shell-quick-reply-accent: #22c55e}.praxis-ai-assistant-shell__quick-reply--tone-danger{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-reply--tone-neutral{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-outline, #94a3b8)}.praxis-ai-assistant-shell__status,.praxis-ai-assistant-shell__error{margin:0;font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__footer{flex:0 0 auto;padding:10px 12px 12px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:var(--md-sys-color-surface-container-low, #111827)}.praxis-ai-assistant-shell__composer{display:grid;gap:8px;border:1px solid var(--md-sys-color-outline-variant, #334155);border-radius:8px;background:var(--md-sys-color-surface-container-lowest, #020617);box-shadow:inset 0 1px #ffffff0a}.praxis-ai-assistant-shell__composer:focus-within{border-color:var(--md-sys-color-primary, #60a5fa);box-shadow:0 0 0 2px #60a5fa29}.praxis-ai-assistant-shell__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:0 8px 8px;flex-wrap:wrap}.praxis-ai-assistant-shell__resize-handle{position:absolute;right:0;bottom:0;width:22px;height:22px;border:0;background:transparent;cursor:nwse-resize;touch-action:none}.praxis-ai-assistant-shell__resize-handle:after{content:\"\";position:absolute;right:6px;bottom:6px;width:10px;height:10px;border-right:2px solid var(--md-sys-color-outline, #94a3b8);border-bottom:2px solid var(--md-sys-color-outline, #94a3b8)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i8.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7187
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisAiAssistantShellComponent, isStandalone: true, selector: "praxis-ai-assistant-shell", inputs: { labels: "labels", mode: "mode", state: "state", contextItems: "contextItems", attachments: "attachments", messages: "messages", quickReplies: "quickReplies", prompt: "prompt", statusText: "statusText", errorText: "errorText", testIdPrefix: "testIdPrefix", panelTestId: "panelTestId", submitTestId: "submitTestId", applyTestId: "applyTestId", primaryAction: "primaryAction", secondaryActions: "secondaryActions", governanceActions: "governanceActions", busy: "busy", canSubmit: "canSubmit", canApply: "canApply", submitOnEnter: "submitOnEnter", showAttachAction: "showAttachAction", enablePastedAttachments: "enablePastedAttachments", enableFileAttachments: "enableFileAttachments", attachmentAccept: "attachmentAccept", attachmentMultiple: "attachmentMultiple", draggable: "draggable", resizable: "resizable", minWidth: "minWidth", minHeight: "minHeight", margin: "margin", layout: "layout" }, outputs: { promptChange: "promptChange", submitPrompt: "submitPrompt", apply: "apply", retryTurn: "retryTurn", cancelTurn: "cancelTurn", shellAction: "shellAction", close: "close", attach: "attach", attachmentsPasted: "attachmentsPasted", attachmentsSelected: "attachmentsSelected", removeAttachment: "removeAttachment", messageAction: "messageAction", editMessage: "editMessage", resendMessage: "resendMessage", quickReply: "quickReply", layoutChange: "layoutChange" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, static: true }, { propertyName: "conversation", first: true, predicate: ["conversation"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<section\n #panel\n class=\"praxis-ai-assistant-shell\"\n role=\"dialog\"\n [attr.aria-label]=\"resolvedLabels.title\"\n [attr.aria-busy]=\"busy ? 'true' : null\"\n [style.left.px]=\"currentLayout.left\"\n [style.top.px]=\"currentLayout.top\"\n [style.width.px]=\"currentLayout.width\"\n [style.height.px]=\"currentLayout.height\"\n [attr.data-testid]=\"panelTestId || testIdPrefix\"\n>\n <header\n class=\"praxis-ai-assistant-shell__header\"\n [attr.data-testid]=\"testIdPrefix + '-drag-handle'\"\n [attr.aria-label]=\"resolvedLabels.dragHandleAria\"\n (pointerdown)=\"startDrag($event)\"\n >\n <div class=\"praxis-ai-assistant-shell__identity\" aria-hidden=\"true\">\n <mat-icon>auto_awesome</mat-icon>\n </div>\n <div class=\"praxis-ai-assistant-shell__title-group\">\n <div class=\"praxis-ai-assistant-shell__title-row\">\n <strong>{{ resolvedLabels.title }}</strong>\n <div class=\"praxis-ai-assistant-shell__badges\" aria-hidden=\"true\">\n <span class=\"praxis-ai-assistant-shell__badge praxis-ai-assistant-shell__badge--context\">\n {{ getModeLabel() }}\n </span>\n <span\n class=\"praxis-ai-assistant-shell__badge praxis-ai-assistant-shell__badge--state\"\n [class.praxis-ai-assistant-shell__badge--error]=\"state === 'error'\"\n >\n <span class=\"praxis-ai-assistant-shell__state-dot\" aria-hidden=\"true\"></span>\n {{ getStateLabel() }}\n </span>\n </div>\n </div>\n <p *ngIf=\"resolvedLabels.subtitle\">{{ resolvedLabels.subtitle }}</p>\n </div>\n <div class=\"praxis-ai-assistant-shell__header-actions\">\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"getResetLayoutLabel()\"\n [attr.aria-label]=\"getResetLayoutLabel()\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"resetLayout()\"\n [attr.data-testid]=\"testIdPrefix + '-reset-layout'\"\n >\n <mat-icon>center_focus_strong</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"resolvedLabels.close\"\n [attr.aria-label]=\"resolvedLabels.close\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"close.emit()\"\n [attr.data-testid]=\"testIdPrefix + '-close'\"\n >\n <mat-icon>{{ getCloseIcon() }}</mat-icon>\n </button>\n </div>\n </header>\n\n <div class=\"praxis-ai-assistant-shell__body\">\n <div\n *ngIf=\"contextItems.length\"\n class=\"praxis-ai-assistant-shell__context\"\n [attr.aria-label]=\"resolvedLabels.contextAria\"\n [attr.data-testid]=\"testIdPrefix + '-context'\"\n >\n <span\n *ngFor=\"let item of contextItems; trackBy: trackContextItem\"\n class=\"praxis-ai-assistant-shell__context-item\"\n [attr.data-testid]=\"testIdPrefix + '-context-' + item.id\"\n >\n <mat-icon *ngIf=\"item.icon\" aria-hidden=\"true\">{{ item.icon }}</mat-icon>\n <span class=\"praxis-ai-assistant-shell__context-label\">{{ item.label }}</span>\n <span *ngIf=\"item.value\" class=\"praxis-ai-assistant-shell__context-value\">{{ item.value }}</span>\n </span>\n </div>\n\n <div\n #conversation\n class=\"praxis-ai-assistant-shell__conversation\"\n [attr.data-testid]=\"testIdPrefix + '-conversation'\"\n [attr.aria-label]=\"resolvedLabels.conversationAria\"\n >\n <article\n *ngIf=\"!messages.length\"\n class=\"praxis-ai-assistant-shell__message praxis-ai-assistant-shell__message--assistant\"\n [attr.data-testid]=\"testIdPrefix + '-message-assistant-empty'\"\n >\n {{ resolvedLabels.emptyConversation }}\n </article>\n <article\n *ngFor=\"let message of messages; trackBy: trackMessage\"\n class=\"praxis-ai-assistant-shell__message\"\n [class.praxis-ai-assistant-shell__message--user]=\"message.role === 'user'\"\n [class.praxis-ai-assistant-shell__message--assistant]=\"message.role === 'assistant'\"\n [class.praxis-ai-assistant-shell__message--status]=\"message.role === 'status'\"\n [class.praxis-ai-assistant-shell__message--error]=\"message.role === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-message-' + message.role\"\n >\n {{ message.text }}\n <div\n *ngIf=\"message.actions?.length || message.editable || message.resendable\"\n class=\"praxis-ai-assistant-shell__message-actions\"\n >\n <button\n *ngIf=\"message.editable\"\n mat-icon-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action praxis-ai-assistant-shell__message-action--icon\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.editMessage\"\n [attr.aria-label]=\"resolvedLabels.editMessage\"\n [attr.data-testid]=\"testIdPrefix + '-message-edit-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'edit', label: resolvedLabels.editMessage, kind: 'edit' })\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n *ngIf=\"message.resendable\"\n mat-icon-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action praxis-ai-assistant-shell__message-action--icon\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.resendMessage\"\n [attr.aria-label]=\"resolvedLabels.resendMessage\"\n [attr.data-testid]=\"testIdPrefix + '-message-resend-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'resend', label: resolvedLabels.resendMessage, kind: 'resend' })\"\n >\n <mat-icon aria-hidden=\"true\">replay</mat-icon>\n </button>\n <ng-container *ngFor=\"let action of message.actions || []; trackBy: trackMessageAction\">\n <button\n *ngIf=\"isMessageActionIconOnly(action); else textualMessageAction\"\n mat-icon-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action praxis-ai-assistant-shell__message-action--icon\"\n [disabled]=\"busy || action.disabled\"\n [matTooltip]=\"getMessageActionLabel(action)\"\n [attr.aria-label]=\"getMessageActionLabel(action)\"\n [attr.data-testid]=\"testIdPrefix + '-message-action-' + message.id + '-' + action.id\"\n (click)=\"onMessageAction(message, action)\"\n >\n <mat-icon aria-hidden=\"true\">{{ getMessageActionIcon(action) }}</mat-icon>\n </button>\n <ng-template #textualMessageAction>\n <button\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy || action.disabled\"\n [attr.aria-label]=\"getMessageActionLabel(action)\"\n [attr.data-testid]=\"testIdPrefix + '-message-action-' + message.id + '-' + action.id\"\n (click)=\"onMessageAction(message, action)\"\n >\n {{ action.label }}\n </button>\n </ng-template>\n </ng-container>\n </div>\n </article>\n </div>\n\n <div\n *ngIf=\"attachments.length\"\n class=\"praxis-ai-assistant-shell__attachments\"\n [attr.aria-label]=\"resolvedLabels.attachmentsAria\"\n [attr.data-testid]=\"testIdPrefix + '-attachments'\"\n >\n <div\n *ngFor=\"let attachment of attachments; trackBy: trackAttachment\"\n class=\"praxis-ai-assistant-shell__attachment\"\n [class.praxis-ai-assistant-shell__attachment--error]=\"attachment.status === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-' + attachment.id\"\n >\n <img\n *ngIf=\"attachment.previewUrl && attachment.kind === 'image'\"\n class=\"praxis-ai-assistant-shell__attachment-preview\"\n [src]=\"attachment.previewUrl\"\n [alt]=\"attachment.name\"\n >\n <span class=\"praxis-ai-assistant-shell__attachment-name\">{{ attachment.name }}</span>\n <span class=\"praxis-ai-assistant-shell__attachment-kind\">{{ attachment.kind }}</span>\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.removeAttachment\"\n [attr.aria-label]=\"resolvedLabels.removeAttachment + ': ' + attachment.name\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-remove-' + attachment.id\"\n (click)=\"onRemoveAttachment(attachment)\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <div\n *ngIf=\"quickReplies.length\"\n class=\"praxis-ai-assistant-shell__quick-replies\"\n [attr.data-testid]=\"testIdPrefix + '-quick-replies'\"\n [attr.aria-label]=\"resolvedLabels.quickRepliesAria\"\n >\n <button\n *ngFor=\"let reply of quickReplies; trackBy: trackQuickReply\"\n mat-stroked-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__quick-reply\"\n [ngClass]=\"'praxis-ai-assistant-shell__quick-reply--tone-' + getQuickReplyTone(reply)\"\n [attr.data-testid]=\"testIdPrefix + '-quick-reply-' + (reply.id || reply.kind)\"\n [attr.aria-label]=\"getQuickReplyAriaLabel(reply)\"\n [disabled]=\"busy\"\n (click)=\"onQuickReply(reply)\"\n >\n <mat-icon\n *ngIf=\"reply.icon\"\n class=\"praxis-ai-assistant-shell__quick-reply-icon\"\n aria-hidden=\"true\"\n >\n {{ reply.icon }}\n </mat-icon>\n <span class=\"praxis-ai-assistant-shell__quick-reply-copy\">\n <span class=\"praxis-ai-assistant-shell__quick-reply-label\">{{ reply.label }}</span>\n <span\n *ngIf=\"reply.description\"\n class=\"praxis-ai-assistant-shell__quick-reply-description\"\n >\n {{ reply.description }}\n </span>\n </span>\n <mat-icon\n *ngIf=\"getQuickReplyTechnicalDetails(reply)\"\n class=\"praxis-ai-assistant-shell__quick-reply-details\"\n [matTooltip]=\"getQuickReplyTechnicalDetails(reply)\"\n [attr.aria-label]=\"resolvedLabels.quickReplyDetails\"\n >\n info\n </mat-icon>\n </button>\n </div>\n <p\n *ngIf=\"statusText\"\n class=\"praxis-ai-assistant-shell__status\"\n [attr.data-testid]=\"testIdPrefix + '-status'\"\n >\n {{ statusText }}\n </p>\n <p\n *ngIf=\"errorText\"\n class=\"praxis-ai-assistant-shell__error\"\n [attr.data-testid]=\"testIdPrefix + '-error'\"\n >\n {{ errorText }}\n </p>\n </div>\n\n <footer class=\"praxis-ai-assistant-shell__footer\">\n <label class=\"praxis-ai-assistant-shell__label\" for=\"praxis-ai-assistant-shell-prompt\">\n {{ resolvedLabels.prompt }}\n </label>\n <div class=\"praxis-ai-assistant-shell__composer\">\n <textarea\n id=\"praxis-ai-assistant-shell-prompt\"\n class=\"praxis-ai-assistant-shell__prompt\"\n [attr.data-testid]=\"testIdPrefix + '-prompt'\"\n [placeholder]=\"resolvedLabels.promptPlaceholder\"\n [ngModel]=\"currentPrompt\"\n [disabled]=\"busy\"\n (ngModelChange)=\"onPromptInput($event)\"\n (keydown)=\"onPromptKeydown($event)\"\n (paste)=\"onPromptPaste($event)\"\n ></textarea>\n <div class=\"praxis-ai-assistant-shell__composer-actions\">\n <ng-container *ngIf=\"showAttachAction\">\n <input\n #attachmentInput\n type=\"file\"\n hidden\n [attr.accept]=\"attachmentAccept || null\"\n [attr.multiple]=\"attachmentMultiple ? '' : null\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-input'\"\n (change)=\"onAttachmentFilesSelected($event)\"\n >\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"busy\"\n (click)=\"onAttachClick(attachmentInput)\"\n [attr.data-testid]=\"testIdPrefix + '-attach'\"\n >\n <mat-icon>attach_file</mat-icon>\n {{ resolvedLabels.attach }}\n </button>\n </ng-container>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n class=\"praxis-ai-assistant-shell__action praxis-ai-assistant-shell__action--primary\"\n [class.praxis-ai-assistant-shell__action--icon-only]=\"getPrimaryAction().iconOnly\"\n [matTooltip]=\"getPrimaryActionTooltip(getPrimaryAction())\"\n [ngClass]=\"'praxis-ai-assistant-shell__action--tone-' + getShellActionTone(getPrimaryAction())\"\n [disabled]=\"isShellActionDisabled(getPrimaryAction())\"\n (click)=\"onShellAction(getPrimaryAction())\"\n [attr.data-testid]=\"getPrimaryAction().testId || (submitTestId || (testIdPrefix + '-submit'))\"\n [attr.aria-label]=\"getPrimaryAction().ariaLabel || getPrimaryAction().label\"\n >\n <mat-icon *ngIf=\"getPrimaryAction().icon\" aria-hidden=\"true\">{{ getPrimaryAction().icon }}</mat-icon>\n <span *ngIf=\"!getPrimaryAction().iconOnly\">{{ getPrimaryAction().label }}</span>\n </button>\n <button\n *ngFor=\"let action of getSecondaryActions(); trackBy: trackShellAction\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__action praxis-ai-assistant-shell__action--secondary\"\n [ngClass]=\"'praxis-ai-assistant-shell__action--tone-' + getShellActionTone(action)\"\n [disabled]=\"isShellActionDisabled(action)\"\n (click)=\"onShellAction(action)\"\n [attr.data-testid]=\"action.testId || (testIdPrefix + '-action-' + action.id)\"\n [attr.aria-label]=\"action.ariaLabel || action.label\"\n >\n <mat-icon *ngIf=\"action.icon\" aria-hidden=\"true\">{{ action.icon }}</mat-icon>\n {{ action.label }}\n </button>\n <mat-spinner *ngIf=\"busy\" diameter=\"20\" [attr.data-testid]=\"testIdPrefix + '-spinner'\"></mat-spinner>\n </div>\n </div>\n </footer>\n\n <ng-container *ngIf=\"resizable\">\n <span\n *ngFor=\"let direction of resizeHandles; trackBy: trackResizeHandle\"\n class=\"praxis-ai-assistant-shell__resize-handle praxis-ai-assistant-shell__resize-handle--{{ direction }}\"\n [attr.data-testid]=\"direction === 'se' ? testIdPrefix + '-resize-handle' : testIdPrefix + '-resize-handle-' + direction\"\n aria-hidden=\"true\"\n role=\"presentation\"\n (pointerdown)=\"startResize(direction, $event)\"\n ></span>\n </ng-container>\n</section>\n", styles: [":host{display:block}.praxis-ai-assistant-shell{position:fixed;box-sizing:border-box;min-width:360px;min-height:360px;display:flex;flex-direction:column;overflow:hidden;color:var(--md-sys-color-on-surface, #f8fafc);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface, #0f172a);box-shadow:0 24px 60px #0006;z-index:var(--praxis-ai-assistant-shell-z-index, 1200)}.praxis-ai-assistant-shell__header{flex:0 0 auto;display:flex;align-items:flex-start;gap:9px;padding:10px 10px 9px;border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:var(--md-sys-color-surface-container, #172033)}.praxis-ai-assistant-shell__header{justify-content:flex-start;border-bottom:1px solid;cursor:move;touch-action:none}.praxis-ai-assistant-shell__identity{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;border-radius:8px;background:var(--md-sys-color-primary, #60a5fa);color:var(--md-sys-color-on-primary, #020617);box-shadow:0 6px 16px #60a5fa38}.praxis-ai-assistant-shell__identity mat-icon{width:18px;height:18px;font-size:18px}.praxis-ai-assistant-shell__title-group{min-width:0;flex:1 1 auto;display:grid;gap:4px}.praxis-ai-assistant-shell__title-row{min-width:0;display:flex;align-items:center;gap:8px}.praxis-ai-assistant-shell__badges{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;gap:4px;min-width:0;flex-wrap:wrap}.praxis-ai-assistant-shell__badge{display:inline-flex;align-items:center;gap:5px;min-height:20px;max-width:120px;padding:2px 7px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 80%,transparent);border-radius:8px;color:var(--md-sys-color-on-surface-variant, #cbd5e1);background:var(--md-sys-color-surface-container-high, #263244);font-size:11px;line-height:1.2;overflow-wrap:anywhere}.praxis-ai-assistant-shell__badge--context{color:var(--md-sys-color-on-surface, #f8fafc);background:color-mix(in srgb,var(--md-sys-color-primary, #60a5fa) 9%,var(--md-sys-color-surface-container-high, #263244))}.praxis-ai-assistant-shell__badge--state{border-color:transparent;background:transparent;color:var(--md-sys-color-on-surface-variant, #cbd5e1);padding-inline:3px}.praxis-ai-assistant-shell__badge--error{color:var(--md-sys-color-error, #ff6b6b);border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__state-dot{flex:0 0 auto;width:6px;height:6px;border-radius:999px;background:var(--md-sys-color-primary, #60a5fa);box-shadow:0 0 0 2px color-mix(in srgb,var(--md-sys-color-primary, #60a5fa) 16%,transparent)}.praxis-ai-assistant-shell__badge--error .praxis-ai-assistant-shell__state-dot{background:var(--md-sys-color-error, #ff6b6b);box-shadow:0 0 0 2px color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 16%,transparent)}.praxis-ai-assistant-shell__title-group strong,.praxis-ai-assistant-shell__title-group p{min-width:0;margin:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__title-group strong{flex:1 1 auto;font-size:13px;line-height:1.2}.praxis-ai-assistant-shell__title-group p{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:11.5px;line-height:1.3}.praxis-ai-assistant-shell__header-actions{flex:0 0 auto;display:flex;align-items:center;gap:2px;margin-top:-3px}.praxis-ai-assistant-shell__header-actions button{width:34px;height:34px;color:var(--md-sys-color-on-surface-variant, #cbd5e1);opacity:.78}.praxis-ai-assistant-shell__header-actions button:hover,.praxis-ai-assistant-shell__header-actions button:focus-visible{opacity:1}.praxis-ai-assistant-shell__header-actions mat-icon{width:18px;height:18px;font-size:18px}.praxis-ai-assistant-shell__body{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:12px;padding:14px 14px 8px;overflow:auto;background:linear-gradient(180deg,var(--md-sys-color-surface-container-low, #111827),var(--md-sys-color-surface, #0f172a))}.praxis-ai-assistant-shell__context,.praxis-ai-assistant-shell__attachments{flex:0 0 auto;display:flex;align-items:center;gap:8px;overflow-x:auto}.praxis-ai-assistant-shell__context-item{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;max-width:240px;padding:5px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);font-size:12px;line-height:1.25}.praxis-ai-assistant-shell__context-item mat-icon{width:16px;height:16px;font-size:16px}.praxis-ai-assistant-shell__context-label,.praxis-ai-assistant-shell__context-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.praxis-ai-assistant-shell__context-label{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__context-value{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-ai-assistant-shell__prompt{box-sizing:border-box;width:100%;min-height:56px;max-height:128px;resize:none;border:0;padding:12px;color:var(--md-sys-color-on-surface, #f8fafc);background:transparent;font:inherit;line-height:1.45;outline:none}.praxis-ai-assistant-shell__conversation{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:8px;overflow:auto;padding:2px}.praxis-ai-assistant-shell__message{max-width:86%;align-self:flex-start;padding:9px 11px;border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);color:var(--md-sys-color-on-surface, #f8fafc);font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__message-actions{display:flex;align-items:center;gap:4px;flex-wrap:wrap;margin-top:8px}.praxis-ai-assistant-shell__message-action{min-height:28px;padding:0 8px;border-radius:8px;font-size:12px}.praxis-ai-assistant-shell__message-action--icon{width:30px;min-width:30px;height:30px;padding:0;color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__message-action--icon mat-icon{width:17px;height:17px;font-size:17px}.praxis-ai-assistant-shell__message--assistant{border-bottom-left-radius:2px}.praxis-ai-assistant-shell__message--user{align-self:flex-end;border-bottom-right-radius:2px;background:var(--md-sys-color-primary-container, #17375f);color:var(--md-sys-color-on-primary-container, #f8fafc)}.praxis-ai-assistant-shell__message--status{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__message--error,.praxis-ai-assistant-shell__error{color:var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-replies{display:flex;flex-wrap:wrap;gap:8px}.praxis-ai-assistant-shell__attachment{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;max-width:260px;min-height:34px;padding:4px 4px 4px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244)}.praxis-ai-assistant-shell__attachment--error{border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__attachment-preview{flex:0 0 auto;width:28px;height:28px;border-radius:6px;object-fit:cover}.praxis-ai-assistant-shell__attachment-name,.praxis-ai-assistant-shell__attachment-kind{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.praxis-ai-assistant-shell__attachment-name{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__attachment-kind{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__quick-reply{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-primary, #60a5fa);--praxis-ai-assistant-shell-quick-reply-background: color-mix( in srgb, var(--praxis-ai-assistant-shell-quick-reply-accent) 12%, var(--md-sys-color-surface-container-high, #263244) );--praxis-ai-assistant-shell-quick-reply-foreground: var(--md-sys-color-on-surface, #f8fafc);max-width:100%;min-height:38px;border-color:color-mix(in srgb,var(--praxis-ai-assistant-shell-quick-reply-accent) 58%,transparent);border-radius:8px;color:var(--praxis-ai-assistant-shell-quick-reply-foreground);background:var(--praxis-ai-assistant-shell-quick-reply-background);white-space:normal;text-align:left}.praxis-ai-assistant-shell__quick-reply ::ng-deep .mdc-button__label{min-width:0;display:inline-flex;align-items:center;gap:8px;width:100%}.praxis-ai-assistant-shell__quick-reply-icon{flex:0 0 auto;width:18px;height:18px;color:var(--praxis-ai-assistant-shell-quick-reply-accent);font-size:18px}.praxis-ai-assistant-shell__quick-reply-details{flex:0 0 auto;width:16px;height:16px;color:var(--md-sys-color-on-surface-variant, #94a3b8);font-size:16px;opacity:.82}.praxis-ai-assistant-shell__quick-reply-copy{min-width:0;display:grid;gap:2px}.praxis-ai-assistant-shell__quick-reply-label,.praxis-ai-assistant-shell__quick-reply-description{min-width:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__quick-reply-label{font-weight:600;line-height:1.2}.praxis-ai-assistant-shell__quick-reply-description{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:11px;line-height:1.25}.praxis-ai-assistant-shell__quick-reply--tone-analytics{--praxis-ai-assistant-shell-quick-reply-accent: #38bdf8}.praxis-ai-assistant-shell__quick-reply--tone-resource{--praxis-ai-assistant-shell-quick-reply-accent: #34d399}.praxis-ai-assistant-shell__quick-reply--tone-warning{--praxis-ai-assistant-shell-quick-reply-accent: #facc15}.praxis-ai-assistant-shell__quick-reply--tone-success{--praxis-ai-assistant-shell-quick-reply-accent: #22c55e}.praxis-ai-assistant-shell__quick-reply--tone-danger{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-reply--tone-neutral{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-outline, #94a3b8)}.praxis-ai-assistant-shell__status,.praxis-ai-assistant-shell__error{margin:0;font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__footer{flex:0 0 auto;padding:10px 12px 12px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:var(--md-sys-color-surface-container-low, #111827)}.praxis-ai-assistant-shell__composer{display:grid;gap:8px;border:1px solid var(--md-sys-color-outline-variant, #334155);border-radius:8px;background:var(--md-sys-color-surface-container-lowest, #020617);box-shadow:inset 0 1px #ffffff0a}.praxis-ai-assistant-shell__composer:focus-within{border-color:var(--md-sys-color-primary, #60a5fa);box-shadow:0 0 0 2px #60a5fa29}.praxis-ai-assistant-shell__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:0 8px 8px;flex-wrap:wrap}.praxis-ai-assistant-shell__action{min-height:36px;border-radius:10px;font-weight:650}.praxis-ai-assistant-shell__action mat-icon{width:18px;height:18px;margin-right:6px;font-size:18px}.praxis-ai-assistant-shell__action--icon-only{width:40px;min-width:40px;height:40px;padding-inline:0;border-radius:50%}.praxis-ai-assistant-shell__action--icon-only mat-icon{margin-right:0}.praxis-ai-assistant-shell__action--secondary{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__action--tone-governance{color:var(--md-sys-color-primary, #60a5fa);background:color-mix(in srgb,var(--md-sys-color-primary, #60a5fa) 10%,transparent)}.praxis-ai-assistant-shell__action--tone-success{color:#34d399;background:color-mix(in srgb,#34d399 10%,transparent)}.praxis-ai-assistant-shell__action--tone-warning{color:#facc15;background:color-mix(in srgb,#facc15 10%,transparent)}.praxis-ai-assistant-shell__action--tone-danger{color:var(--md-sys-color-error, #ff6b6b);background:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 10%,transparent)}.praxis-ai-assistant-shell__resize-handle{position:absolute;z-index:2;border:0;background:transparent;touch-action:none}.praxis-ai-assistant-shell__resize-handle--n,.praxis-ai-assistant-shell__resize-handle--s{left:16px;right:16px;height:10px;cursor:ns-resize}.praxis-ai-assistant-shell__resize-handle--n{top:-5px}.praxis-ai-assistant-shell__resize-handle--s{bottom:-5px}.praxis-ai-assistant-shell__resize-handle--e,.praxis-ai-assistant-shell__resize-handle--w{top:16px;bottom:16px;width:10px;cursor:ew-resize}.praxis-ai-assistant-shell__resize-handle--e{right:-5px}.praxis-ai-assistant-shell__resize-handle--w{left:-5px}.praxis-ai-assistant-shell__resize-handle--ne,.praxis-ai-assistant-shell__resize-handle--nw,.praxis-ai-assistant-shell__resize-handle--se,.praxis-ai-assistant-shell__resize-handle--sw{width:22px;height:22px}.praxis-ai-assistant-shell__resize-handle--ne{top:-5px;right:-5px;cursor:nesw-resize}.praxis-ai-assistant-shell__resize-handle--nw{top:-5px;left:-5px;cursor:nwse-resize}.praxis-ai-assistant-shell__resize-handle--se{right:-5px;bottom:-5px;cursor:nwse-resize}.praxis-ai-assistant-shell__resize-handle--sw{bottom:-5px;left:-5px;cursor:nesw-resize}.praxis-ai-assistant-shell__resize-handle--se:after{content:\"\";position:absolute;right:8px;bottom:8px;width:10px;height:10px;border-right:2px solid var(--md-sys-color-outline, #94a3b8);border-bottom:2px solid var(--md-sys-color-outline, #94a3b8)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i8.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6537
7188
|
}
|
|
6538
7189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisAiAssistantShellComponent, decorators: [{
|
|
6539
7190
|
type: Component,
|
|
@@ -6544,7 +7195,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6544
7195
|
MatIconModule,
|
|
6545
7196
|
MatProgressSpinnerModule,
|
|
6546
7197
|
MatTooltipModule,
|
|
6547
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n #panel\n class=\"praxis-ai-assistant-shell\"\n role=\"dialog\"\n [attr.aria-label]=\"resolvedLabels.title\"\n [attr.aria-busy]=\"busy ? 'true' : null\"\n [style.left.px]=\"currentLayout.left\"\n [style.top.px]=\"currentLayout.top\"\n [style.width.px]=\"currentLayout.width\"\n [style.height.px]=\"currentLayout.height\"\n [attr.data-testid]=\"panelTestId || testIdPrefix\"\n>\n <header\n class=\"praxis-ai-assistant-shell__header\"\n [attr.data-testid]=\"testIdPrefix + '-drag-handle'\"\n [attr.aria-label]=\"resolvedLabels.dragHandleAria\"\n (pointerdown)=\"startDrag($event)\"\n >\n <div class=\"praxis-ai-assistant-shell__identity\" aria-hidden=\"true\">\n <mat-icon>auto_awesome</mat-icon>\n </div>\n <div class=\"praxis-ai-assistant-shell__title-group\">\n <strong>{{ resolvedLabels.title }}</strong>\n <p *ngIf=\"resolvedLabels.subtitle\">{{ resolvedLabels.subtitle }}</p>\n </div>\n <div class=\"praxis-ai-assistant-shell__badges\" aria-hidden=\"true\">\n <span class=\"praxis-ai-assistant-shell__badge\">{{ getModeLabel() }}</span>\n <span class=\"praxis-ai-assistant-shell__badge\" [class.praxis-ai-assistant-shell__badge--error]=\"state === 'error'\">\n {{ getStateLabel() }}\n </span>\n </div>\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"resolvedLabels.close\"\n [attr.aria-label]=\"resolvedLabels.close\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"close.emit()\"\n [attr.data-testid]=\"testIdPrefix + '-close'\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"praxis-ai-assistant-shell__body\">\n <div\n *ngIf=\"contextItems.length\"\n class=\"praxis-ai-assistant-shell__context\"\n [attr.aria-label]=\"resolvedLabels.contextAria\"\n [attr.data-testid]=\"testIdPrefix + '-context'\"\n >\n <span\n *ngFor=\"let item of contextItems; trackBy: trackContextItem\"\n class=\"praxis-ai-assistant-shell__context-item\"\n [attr.data-testid]=\"testIdPrefix + '-context-' + item.id\"\n >\n <mat-icon *ngIf=\"item.icon\" aria-hidden=\"true\">{{ item.icon }}</mat-icon>\n <span class=\"praxis-ai-assistant-shell__context-label\">{{ item.label }}</span>\n <span *ngIf=\"item.value\" class=\"praxis-ai-assistant-shell__context-value\">{{ item.value }}</span>\n </span>\n </div>\n\n <div\n #conversation\n class=\"praxis-ai-assistant-shell__conversation\"\n [attr.data-testid]=\"testIdPrefix + '-conversation'\"\n [attr.aria-label]=\"resolvedLabels.conversationAria\"\n >\n <article\n *ngIf=\"!messages.length\"\n class=\"praxis-ai-assistant-shell__message praxis-ai-assistant-shell__message--assistant\"\n [attr.data-testid]=\"testIdPrefix + '-message-assistant-empty'\"\n >\n {{ resolvedLabels.emptyConversation }}\n </article>\n <article\n *ngFor=\"let message of messages; trackBy: trackMessage\"\n class=\"praxis-ai-assistant-shell__message\"\n [class.praxis-ai-assistant-shell__message--user]=\"message.role === 'user'\"\n [class.praxis-ai-assistant-shell__message--assistant]=\"message.role === 'assistant'\"\n [class.praxis-ai-assistant-shell__message--status]=\"message.role === 'status'\"\n [class.praxis-ai-assistant-shell__message--error]=\"message.role === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-message-' + message.role\"\n >\n {{ message.text }}\n <div\n *ngIf=\"message.actions?.length || message.editable || message.resendable\"\n class=\"praxis-ai-assistant-shell__message-actions\"\n >\n <button\n *ngIf=\"message.editable\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy\"\n [attr.data-testid]=\"testIdPrefix + '-message-edit-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'edit', label: resolvedLabels.editMessage, kind: 'edit' })\"\n >\n {{ resolvedLabels.editMessage }}\n </button>\n <button\n *ngIf=\"message.resendable\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy\"\n [attr.data-testid]=\"testIdPrefix + '-message-resend-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'resend', label: resolvedLabels.resendMessage, kind: 'resend' })\"\n >\n {{ resolvedLabels.resendMessage }}\n </button>\n <button\n *ngFor=\"let action of message.actions || []; trackBy: trackMessageAction\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy || action.disabled\"\n [attr.data-testid]=\"testIdPrefix + '-message-action-' + message.id + '-' + action.id\"\n (click)=\"onMessageAction(message, action)\"\n >\n {{ action.label }}\n </button>\n </div>\n </article>\n </div>\n\n <div\n *ngIf=\"attachments.length\"\n class=\"praxis-ai-assistant-shell__attachments\"\n [attr.aria-label]=\"resolvedLabels.attachmentsAria\"\n [attr.data-testid]=\"testIdPrefix + '-attachments'\"\n >\n <div\n *ngFor=\"let attachment of attachments; trackBy: trackAttachment\"\n class=\"praxis-ai-assistant-shell__attachment\"\n [class.praxis-ai-assistant-shell__attachment--error]=\"attachment.status === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-' + attachment.id\"\n >\n <img\n *ngIf=\"attachment.previewUrl && attachment.kind === 'image'\"\n class=\"praxis-ai-assistant-shell__attachment-preview\"\n [src]=\"attachment.previewUrl\"\n [alt]=\"attachment.name\"\n >\n <span class=\"praxis-ai-assistant-shell__attachment-name\">{{ attachment.name }}</span>\n <span class=\"praxis-ai-assistant-shell__attachment-kind\">{{ attachment.kind }}</span>\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.removeAttachment\"\n [attr.aria-label]=\"resolvedLabels.removeAttachment + ': ' + attachment.name\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-remove-' + attachment.id\"\n (click)=\"onRemoveAttachment(attachment)\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <div\n *ngIf=\"quickReplies.length\"\n class=\"praxis-ai-assistant-shell__quick-replies\"\n [attr.data-testid]=\"testIdPrefix + '-quick-replies'\"\n [attr.aria-label]=\"resolvedLabels.quickRepliesAria\"\n >\n <button\n *ngFor=\"let reply of quickReplies; trackBy: trackQuickReply\"\n mat-stroked-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__quick-reply\"\n [ngClass]=\"'praxis-ai-assistant-shell__quick-reply--tone-' + getQuickReplyTone(reply)\"\n [attr.data-testid]=\"testIdPrefix + '-quick-reply-' + (reply.id || reply.kind)\"\n [attr.aria-label]=\"getQuickReplyAriaLabel(reply)\"\n [disabled]=\"busy\"\n (click)=\"onQuickReply(reply)\"\n >\n <mat-icon\n *ngIf=\"reply.icon\"\n class=\"praxis-ai-assistant-shell__quick-reply-icon\"\n aria-hidden=\"true\"\n >\n {{ reply.icon }}\n </mat-icon>\n <span class=\"praxis-ai-assistant-shell__quick-reply-copy\">\n <span class=\"praxis-ai-assistant-shell__quick-reply-label\">{{ reply.label }}</span>\n <span\n *ngIf=\"reply.description\"\n class=\"praxis-ai-assistant-shell__quick-reply-description\"\n >\n {{ reply.description }}\n </span>\n </span>\n </button>\n </div>\n <p\n *ngIf=\"statusText\"\n class=\"praxis-ai-assistant-shell__status\"\n [attr.data-testid]=\"testIdPrefix + '-status'\"\n >\n {{ statusText }}\n </p>\n <p\n *ngIf=\"errorText\"\n class=\"praxis-ai-assistant-shell__error\"\n [attr.data-testid]=\"testIdPrefix + '-error'\"\n >\n {{ errorText }}\n </p>\n </div>\n\n <footer class=\"praxis-ai-assistant-shell__footer\">\n <label class=\"praxis-ai-assistant-shell__label\" for=\"praxis-ai-assistant-shell-prompt\">\n {{ resolvedLabels.prompt }}\n </label>\n <div class=\"praxis-ai-assistant-shell__composer\">\n <textarea\n id=\"praxis-ai-assistant-shell-prompt\"\n class=\"praxis-ai-assistant-shell__prompt\"\n [attr.data-testid]=\"testIdPrefix + '-prompt'\"\n [placeholder]=\"resolvedLabels.promptPlaceholder\"\n [ngModel]=\"currentPrompt\"\n [disabled]=\"busy\"\n (ngModelChange)=\"onPromptInput($event)\"\n (keydown)=\"onPromptKeydown($event)\"\n (paste)=\"onPromptPaste($event)\"\n ></textarea>\n <div class=\"praxis-ai-assistant-shell__composer-actions\">\n <input\n #attachmentInput\n type=\"file\"\n hidden\n [attr.accept]=\"attachmentAccept || null\"\n [attr.multiple]=\"attachmentMultiple ? '' : null\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-input'\"\n (change)=\"onAttachmentFilesSelected($event)\"\n >\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"busy\"\n (click)=\"onAttachClick(attachmentInput)\"\n [attr.data-testid]=\"testIdPrefix + '-attach'\"\n >\n <mat-icon>attach_file</mat-icon>\n {{ resolvedLabels.attach }}\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n [disabled]=\"busy || !canSubmit || !currentPrompt.trim()\"\n (click)=\"onSubmit()\"\n [attr.data-testid]=\"submitTestId || (testIdPrefix + '-submit')\"\n >\n {{ resolvedLabels.submit }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"busy || !canApply\"\n (click)=\"onApply()\"\n [attr.data-testid]=\"applyTestId || (testIdPrefix + '-apply')\"\n >\n {{ resolvedLabels.apply }}\n </button>\n <mat-spinner *ngIf=\"busy\" diameter=\"20\" [attr.data-testid]=\"testIdPrefix + '-spinner'\"></mat-spinner>\n </div>\n </div>\n </footer>\n\n <button\n *ngIf=\"resizable\"\n type=\"button\"\n class=\"praxis-ai-assistant-shell__resize-handle\"\n [attr.data-testid]=\"testIdPrefix + '-resize-handle'\"\n [attr.aria-label]=\"resolvedLabels.resizeHandleAria\"\n [matTooltip]=\"resolvedLabels.resizeHandleAria\"\n (pointerdown)=\"startResize($event)\"\n ></button>\n</section>\n", styles: [":host{display:block}.praxis-ai-assistant-shell{position:absolute;box-sizing:border-box;min-width:360px;min-height:360px;display:flex;flex-direction:column;overflow:hidden;color:var(--md-sys-color-on-surface, #f8fafc);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface, #0f172a);box-shadow:0 24px 60px #0006;z-index:10}.praxis-ai-assistant-shell__header{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:12px;border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:radial-gradient(circle at top left,rgba(96,165,250,.22),transparent 34%),var(--md-sys-color-surface-container, #172033)}.praxis-ai-assistant-shell__header{justify-content:flex-start;border-bottom:1px solid;cursor:move;touch-action:none}.praxis-ai-assistant-shell__identity{flex:0 0 auto;display:grid;place-items:center;width:34px;height:34px;border-radius:8px;background:var(--md-sys-color-primary, #60a5fa);color:var(--md-sys-color-on-primary, #020617);box-shadow:0 8px 20px #60a5fa4d}.praxis-ai-assistant-shell__identity mat-icon{width:20px;height:20px;font-size:20px}.praxis-ai-assistant-shell__title-group{min-width:0;flex:1 1 auto;display:grid;gap:3px}.praxis-ai-assistant-shell__badges{flex:0 1 auto;display:flex;align-items:center;justify-content:flex-end;gap:6px;min-width:0;flex-wrap:wrap}.praxis-ai-assistant-shell__badge{display:inline-flex;align-items:center;min-height:22px;max-width:140px;padding:2px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 80%,transparent);border-radius:8px;color:var(--md-sys-color-on-surface-variant, #cbd5e1);background:var(--md-sys-color-surface-container-high, #263244);font-size:11px;line-height:1.2;overflow-wrap:anywhere}.praxis-ai-assistant-shell__badge--error{color:var(--md-sys-color-error, #ff6b6b);border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__title-group strong,.praxis-ai-assistant-shell__title-group p{min-width:0;margin:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__title-group p{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:12px;line-height:1.35}.praxis-ai-assistant-shell__body{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:12px;padding:14px 14px 8px;overflow:auto;background:linear-gradient(180deg,var(--md-sys-color-surface-container-low, #111827),var(--md-sys-color-surface, #0f172a))}.praxis-ai-assistant-shell__context,.praxis-ai-assistant-shell__attachments{flex:0 0 auto;display:flex;align-items:center;gap:8px;overflow-x:auto}.praxis-ai-assistant-shell__context-item{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;max-width:240px;padding:5px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);font-size:12px;line-height:1.25}.praxis-ai-assistant-shell__context-item mat-icon{width:16px;height:16px;font-size:16px}.praxis-ai-assistant-shell__context-label,.praxis-ai-assistant-shell__context-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.praxis-ai-assistant-shell__context-label{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__context-value{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-ai-assistant-shell__prompt{box-sizing:border-box;width:100%;min-height:56px;max-height:128px;resize:none;border:0;padding:12px;color:var(--md-sys-color-on-surface, #f8fafc);background:transparent;font:inherit;line-height:1.45;outline:none}.praxis-ai-assistant-shell__conversation{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:8px;overflow:auto;padding:2px}.praxis-ai-assistant-shell__message{max-width:86%;align-self:flex-start;padding:9px 11px;border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);color:var(--md-sys-color-on-surface, #f8fafc);font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__message-actions{display:flex;align-items:center;gap:4px;flex-wrap:wrap;margin-top:8px}.praxis-ai-assistant-shell__message-action{min-height:28px;padding:0 8px;border-radius:8px;font-size:12px}.praxis-ai-assistant-shell__message--assistant{border-bottom-left-radius:2px}.praxis-ai-assistant-shell__message--user{align-self:flex-end;border-bottom-right-radius:2px;background:var(--md-sys-color-primary-container, #17375f);color:var(--md-sys-color-on-primary-container, #f8fafc)}.praxis-ai-assistant-shell__message--status{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__message--error,.praxis-ai-assistant-shell__error{color:var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-replies{display:flex;flex-wrap:wrap;gap:8px}.praxis-ai-assistant-shell__attachment{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;max-width:260px;min-height:34px;padding:4px 4px 4px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244)}.praxis-ai-assistant-shell__attachment--error{border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__attachment-preview{flex:0 0 auto;width:28px;height:28px;border-radius:6px;object-fit:cover}.praxis-ai-assistant-shell__attachment-name,.praxis-ai-assistant-shell__attachment-kind{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.praxis-ai-assistant-shell__attachment-name{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__attachment-kind{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__quick-reply{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-primary, #60a5fa);--praxis-ai-assistant-shell-quick-reply-background: color-mix( in srgb, var(--praxis-ai-assistant-shell-quick-reply-accent) 12%, var(--md-sys-color-surface-container-high, #263244) );--praxis-ai-assistant-shell-quick-reply-foreground: var(--md-sys-color-on-surface, #f8fafc);max-width:100%;min-height:38px;border-color:color-mix(in srgb,var(--praxis-ai-assistant-shell-quick-reply-accent) 58%,transparent);border-radius:8px;color:var(--praxis-ai-assistant-shell-quick-reply-foreground);background:var(--praxis-ai-assistant-shell-quick-reply-background);white-space:normal;text-align:left}.praxis-ai-assistant-shell__quick-reply ::ng-deep .mdc-button__label{min-width:0;display:inline-flex;align-items:center;gap:8px;width:100%}.praxis-ai-assistant-shell__quick-reply-icon{flex:0 0 auto;width:18px;height:18px;color:var(--praxis-ai-assistant-shell-quick-reply-accent);font-size:18px}.praxis-ai-assistant-shell__quick-reply-copy{min-width:0;display:grid;gap:2px}.praxis-ai-assistant-shell__quick-reply-label,.praxis-ai-assistant-shell__quick-reply-description{min-width:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__quick-reply-label{font-weight:600;line-height:1.2}.praxis-ai-assistant-shell__quick-reply-description{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:11px;line-height:1.25}.praxis-ai-assistant-shell__quick-reply--tone-analytics{--praxis-ai-assistant-shell-quick-reply-accent: #38bdf8}.praxis-ai-assistant-shell__quick-reply--tone-resource{--praxis-ai-assistant-shell-quick-reply-accent: #34d399}.praxis-ai-assistant-shell__quick-reply--tone-warning{--praxis-ai-assistant-shell-quick-reply-accent: #facc15}.praxis-ai-assistant-shell__quick-reply--tone-success{--praxis-ai-assistant-shell-quick-reply-accent: #22c55e}.praxis-ai-assistant-shell__quick-reply--tone-danger{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-reply--tone-neutral{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-outline, #94a3b8)}.praxis-ai-assistant-shell__status,.praxis-ai-assistant-shell__error{margin:0;font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__footer{flex:0 0 auto;padding:10px 12px 12px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:var(--md-sys-color-surface-container-low, #111827)}.praxis-ai-assistant-shell__composer{display:grid;gap:8px;border:1px solid var(--md-sys-color-outline-variant, #334155);border-radius:8px;background:var(--md-sys-color-surface-container-lowest, #020617);box-shadow:inset 0 1px #ffffff0a}.praxis-ai-assistant-shell__composer:focus-within{border-color:var(--md-sys-color-primary, #60a5fa);box-shadow:0 0 0 2px #60a5fa29}.praxis-ai-assistant-shell__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:0 8px 8px;flex-wrap:wrap}.praxis-ai-assistant-shell__resize-handle{position:absolute;right:0;bottom:0;width:22px;height:22px;border:0;background:transparent;cursor:nwse-resize;touch-action:none}.praxis-ai-assistant-shell__resize-handle:after{content:\"\";position:absolute;right:6px;bottom:6px;width:10px;height:10px;border-right:2px solid var(--md-sys-color-outline, #94a3b8);border-bottom:2px solid var(--md-sys-color-outline, #94a3b8)}\n"] }]
|
|
7198
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n #panel\n class=\"praxis-ai-assistant-shell\"\n role=\"dialog\"\n [attr.aria-label]=\"resolvedLabels.title\"\n [attr.aria-busy]=\"busy ? 'true' : null\"\n [style.left.px]=\"currentLayout.left\"\n [style.top.px]=\"currentLayout.top\"\n [style.width.px]=\"currentLayout.width\"\n [style.height.px]=\"currentLayout.height\"\n [attr.data-testid]=\"panelTestId || testIdPrefix\"\n>\n <header\n class=\"praxis-ai-assistant-shell__header\"\n [attr.data-testid]=\"testIdPrefix + '-drag-handle'\"\n [attr.aria-label]=\"resolvedLabels.dragHandleAria\"\n (pointerdown)=\"startDrag($event)\"\n >\n <div class=\"praxis-ai-assistant-shell__identity\" aria-hidden=\"true\">\n <mat-icon>auto_awesome</mat-icon>\n </div>\n <div class=\"praxis-ai-assistant-shell__title-group\">\n <div class=\"praxis-ai-assistant-shell__title-row\">\n <strong>{{ resolvedLabels.title }}</strong>\n <div class=\"praxis-ai-assistant-shell__badges\" aria-hidden=\"true\">\n <span class=\"praxis-ai-assistant-shell__badge praxis-ai-assistant-shell__badge--context\">\n {{ getModeLabel() }}\n </span>\n <span\n class=\"praxis-ai-assistant-shell__badge praxis-ai-assistant-shell__badge--state\"\n [class.praxis-ai-assistant-shell__badge--error]=\"state === 'error'\"\n >\n <span class=\"praxis-ai-assistant-shell__state-dot\" aria-hidden=\"true\"></span>\n {{ getStateLabel() }}\n </span>\n </div>\n </div>\n <p *ngIf=\"resolvedLabels.subtitle\">{{ resolvedLabels.subtitle }}</p>\n </div>\n <div class=\"praxis-ai-assistant-shell__header-actions\">\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"getResetLayoutLabel()\"\n [attr.aria-label]=\"getResetLayoutLabel()\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"resetLayout()\"\n [attr.data-testid]=\"testIdPrefix + '-reset-layout'\"\n >\n <mat-icon>center_focus_strong</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"resolvedLabels.close\"\n [attr.aria-label]=\"resolvedLabels.close\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"close.emit()\"\n [attr.data-testid]=\"testIdPrefix + '-close'\"\n >\n <mat-icon>{{ getCloseIcon() }}</mat-icon>\n </button>\n </div>\n </header>\n\n <div class=\"praxis-ai-assistant-shell__body\">\n <div\n *ngIf=\"contextItems.length\"\n class=\"praxis-ai-assistant-shell__context\"\n [attr.aria-label]=\"resolvedLabels.contextAria\"\n [attr.data-testid]=\"testIdPrefix + '-context'\"\n >\n <span\n *ngFor=\"let item of contextItems; trackBy: trackContextItem\"\n class=\"praxis-ai-assistant-shell__context-item\"\n [attr.data-testid]=\"testIdPrefix + '-context-' + item.id\"\n >\n <mat-icon *ngIf=\"item.icon\" aria-hidden=\"true\">{{ item.icon }}</mat-icon>\n <span class=\"praxis-ai-assistant-shell__context-label\">{{ item.label }}</span>\n <span *ngIf=\"item.value\" class=\"praxis-ai-assistant-shell__context-value\">{{ item.value }}</span>\n </span>\n </div>\n\n <div\n #conversation\n class=\"praxis-ai-assistant-shell__conversation\"\n [attr.data-testid]=\"testIdPrefix + '-conversation'\"\n [attr.aria-label]=\"resolvedLabels.conversationAria\"\n >\n <article\n *ngIf=\"!messages.length\"\n class=\"praxis-ai-assistant-shell__message praxis-ai-assistant-shell__message--assistant\"\n [attr.data-testid]=\"testIdPrefix + '-message-assistant-empty'\"\n >\n {{ resolvedLabels.emptyConversation }}\n </article>\n <article\n *ngFor=\"let message of messages; trackBy: trackMessage\"\n class=\"praxis-ai-assistant-shell__message\"\n [class.praxis-ai-assistant-shell__message--user]=\"message.role === 'user'\"\n [class.praxis-ai-assistant-shell__message--assistant]=\"message.role === 'assistant'\"\n [class.praxis-ai-assistant-shell__message--status]=\"message.role === 'status'\"\n [class.praxis-ai-assistant-shell__message--error]=\"message.role === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-message-' + message.role\"\n >\n {{ message.text }}\n <div\n *ngIf=\"message.actions?.length || message.editable || message.resendable\"\n class=\"praxis-ai-assistant-shell__message-actions\"\n >\n <button\n *ngIf=\"message.editable\"\n mat-icon-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action praxis-ai-assistant-shell__message-action--icon\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.editMessage\"\n [attr.aria-label]=\"resolvedLabels.editMessage\"\n [attr.data-testid]=\"testIdPrefix + '-message-edit-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'edit', label: resolvedLabels.editMessage, kind: 'edit' })\"\n >\n <mat-icon aria-hidden=\"true\">edit</mat-icon>\n </button>\n <button\n *ngIf=\"message.resendable\"\n mat-icon-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action praxis-ai-assistant-shell__message-action--icon\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.resendMessage\"\n [attr.aria-label]=\"resolvedLabels.resendMessage\"\n [attr.data-testid]=\"testIdPrefix + '-message-resend-' + message.id\"\n (click)=\"onMessageAction(message, { id: 'resend', label: resolvedLabels.resendMessage, kind: 'resend' })\"\n >\n <mat-icon aria-hidden=\"true\">replay</mat-icon>\n </button>\n <ng-container *ngFor=\"let action of message.actions || []; trackBy: trackMessageAction\">\n <button\n *ngIf=\"isMessageActionIconOnly(action); else textualMessageAction\"\n mat-icon-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action praxis-ai-assistant-shell__message-action--icon\"\n [disabled]=\"busy || action.disabled\"\n [matTooltip]=\"getMessageActionLabel(action)\"\n [attr.aria-label]=\"getMessageActionLabel(action)\"\n [attr.data-testid]=\"testIdPrefix + '-message-action-' + message.id + '-' + action.id\"\n (click)=\"onMessageAction(message, action)\"\n >\n <mat-icon aria-hidden=\"true\">{{ getMessageActionIcon(action) }}</mat-icon>\n </button>\n <ng-template #textualMessageAction>\n <button\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__message-action\"\n [disabled]=\"busy || action.disabled\"\n [attr.aria-label]=\"getMessageActionLabel(action)\"\n [attr.data-testid]=\"testIdPrefix + '-message-action-' + message.id + '-' + action.id\"\n (click)=\"onMessageAction(message, action)\"\n >\n {{ action.label }}\n </button>\n </ng-template>\n </ng-container>\n </div>\n </article>\n </div>\n\n <div\n *ngIf=\"attachments.length\"\n class=\"praxis-ai-assistant-shell__attachments\"\n [attr.aria-label]=\"resolvedLabels.attachmentsAria\"\n [attr.data-testid]=\"testIdPrefix + '-attachments'\"\n >\n <div\n *ngFor=\"let attachment of attachments; trackBy: trackAttachment\"\n class=\"praxis-ai-assistant-shell__attachment\"\n [class.praxis-ai-assistant-shell__attachment--error]=\"attachment.status === 'error'\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-' + attachment.id\"\n >\n <img\n *ngIf=\"attachment.previewUrl && attachment.kind === 'image'\"\n class=\"praxis-ai-assistant-shell__attachment-preview\"\n [src]=\"attachment.previewUrl\"\n [alt]=\"attachment.name\"\n >\n <span class=\"praxis-ai-assistant-shell__attachment-name\">{{ attachment.name }}</span>\n <span class=\"praxis-ai-assistant-shell__attachment-kind\">{{ attachment.kind }}</span>\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"busy\"\n [matTooltip]=\"resolvedLabels.removeAttachment\"\n [attr.aria-label]=\"resolvedLabels.removeAttachment + ': ' + attachment.name\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-remove-' + attachment.id\"\n (click)=\"onRemoveAttachment(attachment)\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <div\n *ngIf=\"quickReplies.length\"\n class=\"praxis-ai-assistant-shell__quick-replies\"\n [attr.data-testid]=\"testIdPrefix + '-quick-replies'\"\n [attr.aria-label]=\"resolvedLabels.quickRepliesAria\"\n >\n <button\n *ngFor=\"let reply of quickReplies; trackBy: trackQuickReply\"\n mat-stroked-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__quick-reply\"\n [ngClass]=\"'praxis-ai-assistant-shell__quick-reply--tone-' + getQuickReplyTone(reply)\"\n [attr.data-testid]=\"testIdPrefix + '-quick-reply-' + (reply.id || reply.kind)\"\n [attr.aria-label]=\"getQuickReplyAriaLabel(reply)\"\n [disabled]=\"busy\"\n (click)=\"onQuickReply(reply)\"\n >\n <mat-icon\n *ngIf=\"reply.icon\"\n class=\"praxis-ai-assistant-shell__quick-reply-icon\"\n aria-hidden=\"true\"\n >\n {{ reply.icon }}\n </mat-icon>\n <span class=\"praxis-ai-assistant-shell__quick-reply-copy\">\n <span class=\"praxis-ai-assistant-shell__quick-reply-label\">{{ reply.label }}</span>\n <span\n *ngIf=\"reply.description\"\n class=\"praxis-ai-assistant-shell__quick-reply-description\"\n >\n {{ reply.description }}\n </span>\n </span>\n <mat-icon\n *ngIf=\"getQuickReplyTechnicalDetails(reply)\"\n class=\"praxis-ai-assistant-shell__quick-reply-details\"\n [matTooltip]=\"getQuickReplyTechnicalDetails(reply)\"\n [attr.aria-label]=\"resolvedLabels.quickReplyDetails\"\n >\n info\n </mat-icon>\n </button>\n </div>\n <p\n *ngIf=\"statusText\"\n class=\"praxis-ai-assistant-shell__status\"\n [attr.data-testid]=\"testIdPrefix + '-status'\"\n >\n {{ statusText }}\n </p>\n <p\n *ngIf=\"errorText\"\n class=\"praxis-ai-assistant-shell__error\"\n [attr.data-testid]=\"testIdPrefix + '-error'\"\n >\n {{ errorText }}\n </p>\n </div>\n\n <footer class=\"praxis-ai-assistant-shell__footer\">\n <label class=\"praxis-ai-assistant-shell__label\" for=\"praxis-ai-assistant-shell-prompt\">\n {{ resolvedLabels.prompt }}\n </label>\n <div class=\"praxis-ai-assistant-shell__composer\">\n <textarea\n id=\"praxis-ai-assistant-shell-prompt\"\n class=\"praxis-ai-assistant-shell__prompt\"\n [attr.data-testid]=\"testIdPrefix + '-prompt'\"\n [placeholder]=\"resolvedLabels.promptPlaceholder\"\n [ngModel]=\"currentPrompt\"\n [disabled]=\"busy\"\n (ngModelChange)=\"onPromptInput($event)\"\n (keydown)=\"onPromptKeydown($event)\"\n (paste)=\"onPromptPaste($event)\"\n ></textarea>\n <div class=\"praxis-ai-assistant-shell__composer-actions\">\n <ng-container *ngIf=\"showAttachAction\">\n <input\n #attachmentInput\n type=\"file\"\n hidden\n [attr.accept]=\"attachmentAccept || null\"\n [attr.multiple]=\"attachmentMultiple ? '' : null\"\n [attr.data-testid]=\"testIdPrefix + '-attachment-input'\"\n (change)=\"onAttachmentFilesSelected($event)\"\n >\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"busy\"\n (click)=\"onAttachClick(attachmentInput)\"\n [attr.data-testid]=\"testIdPrefix + '-attach'\"\n >\n <mat-icon>attach_file</mat-icon>\n {{ resolvedLabels.attach }}\n </button>\n </ng-container>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n class=\"praxis-ai-assistant-shell__action praxis-ai-assistant-shell__action--primary\"\n [class.praxis-ai-assistant-shell__action--icon-only]=\"getPrimaryAction().iconOnly\"\n [matTooltip]=\"getPrimaryActionTooltip(getPrimaryAction())\"\n [ngClass]=\"'praxis-ai-assistant-shell__action--tone-' + getShellActionTone(getPrimaryAction())\"\n [disabled]=\"isShellActionDisabled(getPrimaryAction())\"\n (click)=\"onShellAction(getPrimaryAction())\"\n [attr.data-testid]=\"getPrimaryAction().testId || (submitTestId || (testIdPrefix + '-submit'))\"\n [attr.aria-label]=\"getPrimaryAction().ariaLabel || getPrimaryAction().label\"\n >\n <mat-icon *ngIf=\"getPrimaryAction().icon\" aria-hidden=\"true\">{{ getPrimaryAction().icon }}</mat-icon>\n <span *ngIf=\"!getPrimaryAction().iconOnly\">{{ getPrimaryAction().label }}</span>\n </button>\n <button\n *ngFor=\"let action of getSecondaryActions(); trackBy: trackShellAction\"\n mat-button\n type=\"button\"\n class=\"praxis-ai-assistant-shell__action praxis-ai-assistant-shell__action--secondary\"\n [ngClass]=\"'praxis-ai-assistant-shell__action--tone-' + getShellActionTone(action)\"\n [disabled]=\"isShellActionDisabled(action)\"\n (click)=\"onShellAction(action)\"\n [attr.data-testid]=\"action.testId || (testIdPrefix + '-action-' + action.id)\"\n [attr.aria-label]=\"action.ariaLabel || action.label\"\n >\n <mat-icon *ngIf=\"action.icon\" aria-hidden=\"true\">{{ action.icon }}</mat-icon>\n {{ action.label }}\n </button>\n <mat-spinner *ngIf=\"busy\" diameter=\"20\" [attr.data-testid]=\"testIdPrefix + '-spinner'\"></mat-spinner>\n </div>\n </div>\n </footer>\n\n <ng-container *ngIf=\"resizable\">\n <span\n *ngFor=\"let direction of resizeHandles; trackBy: trackResizeHandle\"\n class=\"praxis-ai-assistant-shell__resize-handle praxis-ai-assistant-shell__resize-handle--{{ direction }}\"\n [attr.data-testid]=\"direction === 'se' ? testIdPrefix + '-resize-handle' : testIdPrefix + '-resize-handle-' + direction\"\n aria-hidden=\"true\"\n role=\"presentation\"\n (pointerdown)=\"startResize(direction, $event)\"\n ></span>\n </ng-container>\n</section>\n", styles: [":host{display:block}.praxis-ai-assistant-shell{position:fixed;box-sizing:border-box;min-width:360px;min-height:360px;display:flex;flex-direction:column;overflow:hidden;color:var(--md-sys-color-on-surface, #f8fafc);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface, #0f172a);box-shadow:0 24px 60px #0006;z-index:var(--praxis-ai-assistant-shell-z-index, 1200)}.praxis-ai-assistant-shell__header{flex:0 0 auto;display:flex;align-items:flex-start;gap:9px;padding:10px 10px 9px;border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:var(--md-sys-color-surface-container, #172033)}.praxis-ai-assistant-shell__header{justify-content:flex-start;border-bottom:1px solid;cursor:move;touch-action:none}.praxis-ai-assistant-shell__identity{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;border-radius:8px;background:var(--md-sys-color-primary, #60a5fa);color:var(--md-sys-color-on-primary, #020617);box-shadow:0 6px 16px #60a5fa38}.praxis-ai-assistant-shell__identity mat-icon{width:18px;height:18px;font-size:18px}.praxis-ai-assistant-shell__title-group{min-width:0;flex:1 1 auto;display:grid;gap:4px}.praxis-ai-assistant-shell__title-row{min-width:0;display:flex;align-items:center;gap:8px}.praxis-ai-assistant-shell__badges{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;gap:4px;min-width:0;flex-wrap:wrap}.praxis-ai-assistant-shell__badge{display:inline-flex;align-items:center;gap:5px;min-height:20px;max-width:120px;padding:2px 7px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 80%,transparent);border-radius:8px;color:var(--md-sys-color-on-surface-variant, #cbd5e1);background:var(--md-sys-color-surface-container-high, #263244);font-size:11px;line-height:1.2;overflow-wrap:anywhere}.praxis-ai-assistant-shell__badge--context{color:var(--md-sys-color-on-surface, #f8fafc);background:color-mix(in srgb,var(--md-sys-color-primary, #60a5fa) 9%,var(--md-sys-color-surface-container-high, #263244))}.praxis-ai-assistant-shell__badge--state{border-color:transparent;background:transparent;color:var(--md-sys-color-on-surface-variant, #cbd5e1);padding-inline:3px}.praxis-ai-assistant-shell__badge--error{color:var(--md-sys-color-error, #ff6b6b);border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__state-dot{flex:0 0 auto;width:6px;height:6px;border-radius:999px;background:var(--md-sys-color-primary, #60a5fa);box-shadow:0 0 0 2px color-mix(in srgb,var(--md-sys-color-primary, #60a5fa) 16%,transparent)}.praxis-ai-assistant-shell__badge--error .praxis-ai-assistant-shell__state-dot{background:var(--md-sys-color-error, #ff6b6b);box-shadow:0 0 0 2px color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 16%,transparent)}.praxis-ai-assistant-shell__title-group strong,.praxis-ai-assistant-shell__title-group p{min-width:0;margin:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__title-group strong{flex:1 1 auto;font-size:13px;line-height:1.2}.praxis-ai-assistant-shell__title-group p{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:11.5px;line-height:1.3}.praxis-ai-assistant-shell__header-actions{flex:0 0 auto;display:flex;align-items:center;gap:2px;margin-top:-3px}.praxis-ai-assistant-shell__header-actions button{width:34px;height:34px;color:var(--md-sys-color-on-surface-variant, #cbd5e1);opacity:.78}.praxis-ai-assistant-shell__header-actions button:hover,.praxis-ai-assistant-shell__header-actions button:focus-visible{opacity:1}.praxis-ai-assistant-shell__header-actions mat-icon{width:18px;height:18px;font-size:18px}.praxis-ai-assistant-shell__body{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:12px;padding:14px 14px 8px;overflow:auto;background:linear-gradient(180deg,var(--md-sys-color-surface-container-low, #111827),var(--md-sys-color-surface, #0f172a))}.praxis-ai-assistant-shell__context,.praxis-ai-assistant-shell__attachments{flex:0 0 auto;display:flex;align-items:center;gap:8px;overflow-x:auto}.praxis-ai-assistant-shell__context-item{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;max-width:240px;padding:5px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);font-size:12px;line-height:1.25}.praxis-ai-assistant-shell__context-item mat-icon{width:16px;height:16px;font-size:16px}.praxis-ai-assistant-shell__context-label,.praxis-ai-assistant-shell__context-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.praxis-ai-assistant-shell__context-label{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__context-value{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-ai-assistant-shell__prompt{box-sizing:border-box;width:100%;min-height:56px;max-height:128px;resize:none;border:0;padding:12px;color:var(--md-sys-color-on-surface, #f8fafc);background:transparent;font:inherit;line-height:1.45;outline:none}.praxis-ai-assistant-shell__conversation{min-height:0;flex:1 1 auto;display:flex;flex-direction:column;gap:8px;overflow:auto;padding:2px}.praxis-ai-assistant-shell__message{max-width:86%;align-self:flex-start;padding:9px 11px;border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244);color:var(--md-sys-color-on-surface, #f8fafc);font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__message-actions{display:flex;align-items:center;gap:4px;flex-wrap:wrap;margin-top:8px}.praxis-ai-assistant-shell__message-action{min-height:28px;padding:0 8px;border-radius:8px;font-size:12px}.praxis-ai-assistant-shell__message-action--icon{width:30px;min-width:30px;height:30px;padding:0;color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__message-action--icon mat-icon{width:17px;height:17px;font-size:17px}.praxis-ai-assistant-shell__message--assistant{border-bottom-left-radius:2px}.praxis-ai-assistant-shell__message--user{align-self:flex-end;border-bottom-right-radius:2px;background:var(--md-sys-color-primary-container, #17375f);color:var(--md-sys-color-on-primary-container, #f8fafc)}.praxis-ai-assistant-shell__message--status{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__message--error,.praxis-ai-assistant-shell__error{color:var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-replies{display:flex;flex-wrap:wrap;gap:8px}.praxis-ai-assistant-shell__attachment{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;max-width:260px;min-height:34px;padding:4px 4px 4px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 70%,transparent);border-radius:8px;background:var(--md-sys-color-surface-container-high, #263244)}.praxis-ai-assistant-shell__attachment--error{border-color:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 60%,transparent)}.praxis-ai-assistant-shell__attachment-preview{flex:0 0 auto;width:28px;height:28px;border-radius:6px;object-fit:cover}.praxis-ai-assistant-shell__attachment-name,.praxis-ai-assistant-shell__attachment-kind{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.praxis-ai-assistant-shell__attachment-name{color:var(--md-sys-color-on-surface, #f8fafc)}.praxis-ai-assistant-shell__attachment-kind{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__quick-reply{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-primary, #60a5fa);--praxis-ai-assistant-shell-quick-reply-background: color-mix( in srgb, var(--praxis-ai-assistant-shell-quick-reply-accent) 12%, var(--md-sys-color-surface-container-high, #263244) );--praxis-ai-assistant-shell-quick-reply-foreground: var(--md-sys-color-on-surface, #f8fafc);max-width:100%;min-height:38px;border-color:color-mix(in srgb,var(--praxis-ai-assistant-shell-quick-reply-accent) 58%,transparent);border-radius:8px;color:var(--praxis-ai-assistant-shell-quick-reply-foreground);background:var(--praxis-ai-assistant-shell-quick-reply-background);white-space:normal;text-align:left}.praxis-ai-assistant-shell__quick-reply ::ng-deep .mdc-button__label{min-width:0;display:inline-flex;align-items:center;gap:8px;width:100%}.praxis-ai-assistant-shell__quick-reply-icon{flex:0 0 auto;width:18px;height:18px;color:var(--praxis-ai-assistant-shell-quick-reply-accent);font-size:18px}.praxis-ai-assistant-shell__quick-reply-details{flex:0 0 auto;width:16px;height:16px;color:var(--md-sys-color-on-surface-variant, #94a3b8);font-size:16px;opacity:.82}.praxis-ai-assistant-shell__quick-reply-copy{min-width:0;display:grid;gap:2px}.praxis-ai-assistant-shell__quick-reply-label,.praxis-ai-assistant-shell__quick-reply-description{min-width:0;overflow-wrap:anywhere}.praxis-ai-assistant-shell__quick-reply-label{font-weight:600;line-height:1.2}.praxis-ai-assistant-shell__quick-reply-description{color:var(--md-sys-color-on-surface-variant, #cbd5e1);font-size:11px;line-height:1.25}.praxis-ai-assistant-shell__quick-reply--tone-analytics{--praxis-ai-assistant-shell-quick-reply-accent: #38bdf8}.praxis-ai-assistant-shell__quick-reply--tone-resource{--praxis-ai-assistant-shell-quick-reply-accent: #34d399}.praxis-ai-assistant-shell__quick-reply--tone-warning{--praxis-ai-assistant-shell-quick-reply-accent: #facc15}.praxis-ai-assistant-shell__quick-reply--tone-success{--praxis-ai-assistant-shell-quick-reply-accent: #22c55e}.praxis-ai-assistant-shell__quick-reply--tone-danger{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-error, #ff6b6b)}.praxis-ai-assistant-shell__quick-reply--tone-neutral{--praxis-ai-assistant-shell-quick-reply-accent: var(--md-sys-color-outline, #94a3b8)}.praxis-ai-assistant-shell__status,.praxis-ai-assistant-shell__error{margin:0;font-size:13px;line-height:1.35;overflow-wrap:anywhere}.praxis-ai-assistant-shell__footer{flex:0 0 auto;padding:10px 12px 12px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #334155) 72%,transparent);background:var(--md-sys-color-surface-container-low, #111827)}.praxis-ai-assistant-shell__composer{display:grid;gap:8px;border:1px solid var(--md-sys-color-outline-variant, #334155);border-radius:8px;background:var(--md-sys-color-surface-container-lowest, #020617);box-shadow:inset 0 1px #ffffff0a}.praxis-ai-assistant-shell__composer:focus-within{border-color:var(--md-sys-color-primary, #60a5fa);box-shadow:0 0 0 2px #60a5fa29}.praxis-ai-assistant-shell__composer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:0 8px 8px;flex-wrap:wrap}.praxis-ai-assistant-shell__action{min-height:36px;border-radius:10px;font-weight:650}.praxis-ai-assistant-shell__action mat-icon{width:18px;height:18px;margin-right:6px;font-size:18px}.praxis-ai-assistant-shell__action--icon-only{width:40px;min-width:40px;height:40px;padding-inline:0;border-radius:50%}.praxis-ai-assistant-shell__action--icon-only mat-icon{margin-right:0}.praxis-ai-assistant-shell__action--secondary{color:var(--md-sys-color-on-surface-variant, #cbd5e1)}.praxis-ai-assistant-shell__action--tone-governance{color:var(--md-sys-color-primary, #60a5fa);background:color-mix(in srgb,var(--md-sys-color-primary, #60a5fa) 10%,transparent)}.praxis-ai-assistant-shell__action--tone-success{color:#34d399;background:color-mix(in srgb,#34d399 10%,transparent)}.praxis-ai-assistant-shell__action--tone-warning{color:#facc15;background:color-mix(in srgb,#facc15 10%,transparent)}.praxis-ai-assistant-shell__action--tone-danger{color:var(--md-sys-color-error, #ff6b6b);background:color-mix(in srgb,var(--md-sys-color-error, #ff6b6b) 10%,transparent)}.praxis-ai-assistant-shell__resize-handle{position:absolute;z-index:2;border:0;background:transparent;touch-action:none}.praxis-ai-assistant-shell__resize-handle--n,.praxis-ai-assistant-shell__resize-handle--s{left:16px;right:16px;height:10px;cursor:ns-resize}.praxis-ai-assistant-shell__resize-handle--n{top:-5px}.praxis-ai-assistant-shell__resize-handle--s{bottom:-5px}.praxis-ai-assistant-shell__resize-handle--e,.praxis-ai-assistant-shell__resize-handle--w{top:16px;bottom:16px;width:10px;cursor:ew-resize}.praxis-ai-assistant-shell__resize-handle--e{right:-5px}.praxis-ai-assistant-shell__resize-handle--w{left:-5px}.praxis-ai-assistant-shell__resize-handle--ne,.praxis-ai-assistant-shell__resize-handle--nw,.praxis-ai-assistant-shell__resize-handle--se,.praxis-ai-assistant-shell__resize-handle--sw{width:22px;height:22px}.praxis-ai-assistant-shell__resize-handle--ne{top:-5px;right:-5px;cursor:nesw-resize}.praxis-ai-assistant-shell__resize-handle--nw{top:-5px;left:-5px;cursor:nwse-resize}.praxis-ai-assistant-shell__resize-handle--se{right:-5px;bottom:-5px;cursor:nwse-resize}.praxis-ai-assistant-shell__resize-handle--sw{bottom:-5px;left:-5px;cursor:nesw-resize}.praxis-ai-assistant-shell__resize-handle--se:after{content:\"\";position:absolute;right:8px;bottom:8px;width:10px;height:10px;border-right:2px solid var(--md-sys-color-outline, #94a3b8);border-bottom:2px solid var(--md-sys-color-outline, #94a3b8)}\n"] }]
|
|
6548
7199
|
}], propDecorators: { labels: [{
|
|
6549
7200
|
type: Input
|
|
6550
7201
|
}], mode: [{
|
|
@@ -6573,6 +7224,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6573
7224
|
type: Input
|
|
6574
7225
|
}], applyTestId: [{
|
|
6575
7226
|
type: Input
|
|
7227
|
+
}], primaryAction: [{
|
|
7228
|
+
type: Input
|
|
7229
|
+
}], secondaryActions: [{
|
|
7230
|
+
type: Input
|
|
7231
|
+
}], governanceActions: [{
|
|
7232
|
+
type: Input
|
|
6576
7233
|
}], busy: [{
|
|
6577
7234
|
type: Input
|
|
6578
7235
|
}], canSubmit: [{
|
|
@@ -6581,6 +7238,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6581
7238
|
type: Input
|
|
6582
7239
|
}], submitOnEnter: [{
|
|
6583
7240
|
type: Input
|
|
7241
|
+
}], showAttachAction: [{
|
|
7242
|
+
type: Input
|
|
7243
|
+
}], enablePastedAttachments: [{
|
|
7244
|
+
type: Input
|
|
6584
7245
|
}], enableFileAttachments: [{
|
|
6585
7246
|
type: Input
|
|
6586
7247
|
}], attachmentAccept: [{
|
|
@@ -6605,6 +7266,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6605
7266
|
type: Output
|
|
6606
7267
|
}], apply: [{
|
|
6607
7268
|
type: Output
|
|
7269
|
+
}], retryTurn: [{
|
|
7270
|
+
type: Output
|
|
7271
|
+
}], cancelTurn: [{
|
|
7272
|
+
type: Output
|
|
7273
|
+
}], shellAction: [{
|
|
7274
|
+
type: Output
|
|
6608
7275
|
}], close: [{
|
|
6609
7276
|
type: Output
|
|
6610
7277
|
}], attach: [{
|
|
@@ -6633,6 +7300,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6633
7300
|
args: ['conversation']
|
|
6634
7301
|
}] } });
|
|
6635
7302
|
|
|
7303
|
+
function createPraxisAssistantViewportLayout(options = {}) {
|
|
7304
|
+
const width = normalizePositiveNumber(options.width, 560);
|
|
7305
|
+
const height = normalizePositiveNumber(options.height, 620);
|
|
7306
|
+
const top = normalizePositiveNumber(options.top, 88);
|
|
7307
|
+
const margin = normalizePositiveNumber(options.margin, 32);
|
|
7308
|
+
const viewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1280;
|
|
7309
|
+
return {
|
|
7310
|
+
left: Math.max(margin, viewportWidth - width - margin),
|
|
7311
|
+
top,
|
|
7312
|
+
width,
|
|
7313
|
+
height,
|
|
7314
|
+
};
|
|
7315
|
+
}
|
|
7316
|
+
function normalizePositiveNumber(value, fallback) {
|
|
7317
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
7318
|
+
}
|
|
7319
|
+
|
|
6636
7320
|
class PraxisAiAssistantDockComponent {
|
|
6637
7321
|
title = 'Praxis copilot active';
|
|
6638
7322
|
summary = 'Conversation preserved. Continue where you stopped.';
|
|
@@ -7688,4 +8372,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7688
8372
|
* Generated bundle index. Do not edit.
|
|
7689
8373
|
*/
|
|
7690
8374
|
|
|
7691
|
-
export { AI_BACKEND_CONFIG_STORE, AI_BACKEND_STORAGE_OPTIONS, AI_CONTRACT_SCHEMA_HASH, AI_CONTRACT_VERSION, AI_INTENT_CONTRACT_SCHEMA_HASH, AI_INTENT_CONTRACT_VERSION, AI_STREAM_EVENT_SCHEMA_VERSION, AI_STREAM_EVENT_TYPES, AiBackendApiService, AiPatchStreamConnectionError, AiResponseValidatorService, AiRuleWizardDialogComponent, BaseAiAdapter, PraxisAi, PraxisAiAssistantComponent, PraxisAiAssistantDockComponent, PraxisAiAssistantSessionHostComponent, PraxisAiAssistantShellComponent, PraxisAiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnController, PraxisAssistantTurnOrchestratorService, SchemaMinifierService, toPraxisAssistantConversationMessages };
|
|
8375
|
+
export { AI_BACKEND_CONFIG_STORE, AI_BACKEND_STORAGE_OPTIONS, AI_CONTRACT_SCHEMA_HASH, AI_CONTRACT_VERSION, AI_INTENT_CONTRACT_SCHEMA_HASH, AI_INTENT_CONTRACT_VERSION, AI_STREAM_EVENT_SCHEMA_VERSION, AI_STREAM_EVENT_TYPES, AiBackendApiService, AiPatchStreamConnectionError, AiResponseValidatorService, AiRuleWizardDialogComponent, BaseAiAdapter, PRAXIS_ASSISTANT_CONTEXT_ATTACHMENT_LIMIT, PRAXIS_ASSISTANT_CONTEXT_ITEM_LIMIT, PRAXIS_ASSISTANT_CONTEXT_SCHEMA_FIELD_LIMIT, PRAXIS_ASSISTANT_CONTEXT_TEXT_LIMIT, PraxisAi, PraxisAiAssistantComponent, PraxisAiAssistantDockComponent, PraxisAiAssistantSessionHostComponent, PraxisAiAssistantShellComponent, PraxisAiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnController, PraxisAssistantTurnOrchestratorService, SchemaMinifierService, createPraxisAssistantViewportLayout, normalizePraxisAssistantAttachmentSummary, normalizePraxisAssistantContextSnapshot, sanitizePraxisAssistantText, toPraxisAssistantConversationMessages };
|