@praxisui/expansion 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 +7 -0
- package/fesm2022/praxisui-expansion.mjs +819 -11
- package/index.d.ts +78 -6
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -240,6 +240,13 @@ Ele modela edicoes em `ExpansionMetadata` por operacoes atomicas sobre paineis,
|
|
|
240
240
|
cabecalhos, conteudo lazy, estado expandido/desabilitado e comportamento do
|
|
241
241
|
accordion.
|
|
242
242
|
|
|
243
|
+
Quando `enableCustomization` esta ativo, o runtime abre o `PraxisAiAssistantShellComponent`
|
|
244
|
+
com contexto semantico dos paineis e registra a sessao no dock global de assistentes.
|
|
245
|
+
O assistente envia estado atual, perfil dos paineis, campos de schema e referencia ao
|
|
246
|
+
manifesto de authoring para a IA. Patches JSON livres nao sao aplicados no runtime:
|
|
247
|
+
mudancas locais devem nascer de um `componentEditPlan` validado pelo manifesto, e
|
|
248
|
+
regras compartilhadas seguem por `domain-rules/intake`.
|
|
249
|
+
|
|
243
250
|
Cobertura principal:
|
|
244
251
|
|
|
245
252
|
- `panel.add`, `panel.remove`, `panel.order.set`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i1 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { EventEmitter, inject, ChangeDetectorRef, DestroyRef, ViewChildren, ViewChild, Output, Input, ChangeDetectionStrategy, Component, Inject, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
4
|
+
import { EventEmitter, inject, ChangeDetectorRef, DestroyRef, effect, ViewChildren, ViewChild, Output, Input, ChangeDetectionStrategy, Component, Inject, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
5
5
|
import { ActivatedRoute } from '@angular/router';
|
|
6
6
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
7
|
import * as i2 from '@angular/material/expansion';
|
|
@@ -21,9 +21,9 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
|
|
21
21
|
import { DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
|
|
22
22
|
import { deepMerge, ASYNC_CONFIG_STORAGE, DynamicFormService, ComponentKeyService, DynamicWidgetLoaderDirective, PraxisIconDirective, ComponentMetadataRegistry } from '@praxisui/core';
|
|
23
23
|
import { SettingsPanelService, SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
|
|
24
|
-
import { BehaviorSubject } from 'rxjs';
|
|
24
|
+
import { firstValueFrom, BehaviorSubject, Subscription } from 'rxjs';
|
|
25
25
|
import { take } from 'rxjs/operators';
|
|
26
|
-
import { BaseAiAdapter,
|
|
26
|
+
import { BaseAiAdapter, AiBackendApiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, createPraxisAssistantViewportLayout, PraxisAiAssistantShellComponent } from '@praxisui/ai';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Capabilities catalog for Praxis Expansion (ExpansionMetadata).
|
|
@@ -105,6 +105,8 @@ const EXPANSION_AI_CAPABILITIES = {
|
|
|
105
105
|
|
|
106
106
|
class ExpansionAiAdapter extends BaseAiAdapter {
|
|
107
107
|
expansion;
|
|
108
|
+
componentId = 'praxis-expansion';
|
|
109
|
+
componentType = 'expansion';
|
|
108
110
|
componentName = 'Praxis Expansion';
|
|
109
111
|
constructor(expansion) {
|
|
110
112
|
super();
|
|
@@ -125,6 +127,43 @@ class ExpansionAiAdapter extends BaseAiAdapter {
|
|
|
125
127
|
multi: this.expansion.config?.accordion?.multi ?? false,
|
|
126
128
|
};
|
|
127
129
|
}
|
|
130
|
+
getDataProfile() {
|
|
131
|
+
const panels = this.getCurrentConfig().panels || [];
|
|
132
|
+
return {
|
|
133
|
+
expansionId: this.expansion.expansionId,
|
|
134
|
+
panelCount: panels.length,
|
|
135
|
+
contentPanelCount: panels.filter((panel) => Array.isArray(panel.content) && panel.content.length > 0).length,
|
|
136
|
+
widgetPanelCount: panels.filter((panel) => Array.isArray(panel.widgets) && panel.widgets.length > 0).length,
|
|
137
|
+
actionPanelCount: panels.filter((panel) => Array.isArray(panel.actionButtons) && panel.actionButtons.length > 0).length,
|
|
138
|
+
multi: this.expansion.config?.accordion?.multi ?? false,
|
|
139
|
+
displayMode: this.expansion.config?.accordion?.displayMode || 'default',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
getSchemaFields() {
|
|
143
|
+
return (this.getCurrentConfig().panels || []).map((panel, index) => ({
|
|
144
|
+
name: panel.id || panel.title || `panel-${index + 1}`,
|
|
145
|
+
label: panel.title || panel.id || `Painel ${index + 1}`,
|
|
146
|
+
disabled: !!panel.disabled,
|
|
147
|
+
expanded: !!panel.expanded,
|
|
148
|
+
hasContent: Array.isArray(panel.content) && panel.content.length > 0,
|
|
149
|
+
hasWidgets: Array.isArray(panel.widgets) && panel.widgets.length > 0,
|
|
150
|
+
hasActions: Array.isArray(panel.actionButtons) && panel.actionButtons.length > 0,
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
getAuthoringContext() {
|
|
154
|
+
return {
|
|
155
|
+
authoringManifestRef: 'PRAXIS_EXPANSION_AUTHORING_MANIFEST',
|
|
156
|
+
runtimeAuthoringPolicy: {
|
|
157
|
+
mode: 'agentic-authoring',
|
|
158
|
+
enableCustomization: !!this.expansion.enableCustomization,
|
|
159
|
+
canApplyLocalPatch: false,
|
|
160
|
+
reason: 'praxis-expansion exige componentEditPlan validado pelo manifesto antes de aplicar mudancas runtime.',
|
|
161
|
+
},
|
|
162
|
+
domainCatalog: {
|
|
163
|
+
recommendedAuthoringFlow: 'component_authoring',
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
128
167
|
createSnapshot() {
|
|
129
168
|
return this.getCurrentConfig();
|
|
130
169
|
}
|
|
@@ -185,6 +224,280 @@ class ExpansionAiAdapter extends BaseAiAdapter {
|
|
|
185
224
|
}
|
|
186
225
|
}
|
|
187
226
|
|
|
227
|
+
class ExpansionAgenticAuthoringTurnFlow {
|
|
228
|
+
adapter;
|
|
229
|
+
aiApi;
|
|
230
|
+
mode = 'agentic-authoring';
|
|
231
|
+
constructor(adapter, aiApi) {
|
|
232
|
+
this.adapter = adapter;
|
|
233
|
+
this.aiApi = aiApi;
|
|
234
|
+
}
|
|
235
|
+
async submit(request) {
|
|
236
|
+
const prompt = (request.prompt ?? '').trim();
|
|
237
|
+
if (!prompt)
|
|
238
|
+
return { state: 'listening', phase: 'capture', statusText: '' };
|
|
239
|
+
const componentId = this.adapter.componentId || request.componentId || 'praxis-expansion';
|
|
240
|
+
const componentType = this.adapter.componentType || request.componentType || 'expansion';
|
|
241
|
+
const currentState = this.toAiJsonObject(this.adapter.getCurrentConfig());
|
|
242
|
+
const dataProfile = this.optionalJsonObject(this.adapter.getDataProfile?.());
|
|
243
|
+
const runtimeState = this.optionalJsonObject(this.adapter.getRuntimeState?.());
|
|
244
|
+
const schemaFields = this.adapter.getSchemaFields?.()
|
|
245
|
+
?.map((field) => this.toAiJsonObject(field))
|
|
246
|
+
.filter((field) => Object.keys(field).length > 0);
|
|
247
|
+
const contextHints = this.optionalJsonObject(this.adapter.getAuthoringContext?.());
|
|
248
|
+
if (this.shouldRouteToGovernedDecision(prompt, contextHints)) {
|
|
249
|
+
return this.toGovernedDecisionHandoff(prompt, request);
|
|
250
|
+
}
|
|
251
|
+
const response = await firstValueFrom(this.aiApi.getPatch({
|
|
252
|
+
componentId,
|
|
253
|
+
componentType,
|
|
254
|
+
userPrompt: prompt,
|
|
255
|
+
sessionId: request.sessionId,
|
|
256
|
+
clientTurnId: request.clientTurnId,
|
|
257
|
+
messages: this.toChatMessages(request.messages, prompt),
|
|
258
|
+
currentState,
|
|
259
|
+
currentStateDigest: this.buildCurrentStateDigest(dataProfile),
|
|
260
|
+
uiContextRef: { componentId, componentType },
|
|
261
|
+
...(dataProfile ? { dataProfile } : {}),
|
|
262
|
+
...(runtimeState ? { runtimeState } : {}),
|
|
263
|
+
...(schemaFields?.length ? { schemaFields } : {}),
|
|
264
|
+
...(contextHints ? { contextHints } : {}),
|
|
265
|
+
}));
|
|
266
|
+
return this.toTurnResult(this.compileAdapterResponse(response), request);
|
|
267
|
+
}
|
|
268
|
+
async apply(_request) {
|
|
269
|
+
return {
|
|
270
|
+
state: 'error',
|
|
271
|
+
phase: 'apply',
|
|
272
|
+
assistantMessage: 'O expansion ainda exige componentEditPlan validado pelo manifesto antes de aplicar mudancas locais.',
|
|
273
|
+
errorText: 'Aplicacao local bloqueada ate existir compilacao manifest-backed para praxis-expansion.',
|
|
274
|
+
canApply: false,
|
|
275
|
+
pendingPatch: null,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
cancel() {
|
|
279
|
+
return Promise.resolve({
|
|
280
|
+
state: 'listening',
|
|
281
|
+
phase: 'capture',
|
|
282
|
+
assistantMessage: 'Solicitacao cancelada.',
|
|
283
|
+
statusText: '',
|
|
284
|
+
canApply: false,
|
|
285
|
+
pendingPatch: null,
|
|
286
|
+
pendingClarification: null,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
retry(request) {
|
|
290
|
+
const lastPrompt = [...(request.messages ?? [])].reverse()
|
|
291
|
+
.find((message) => message.role === 'user')?.text;
|
|
292
|
+
return this.submit({ ...request, prompt: lastPrompt ?? request.prompt, action: { kind: 'retry' } });
|
|
293
|
+
}
|
|
294
|
+
toTurnResult(response, request) {
|
|
295
|
+
if (!response) {
|
|
296
|
+
return { state: 'error', phase: 'capture', assistantMessage: 'Resposta vazia da IA.', errorText: 'Resposta vazia da IA.' };
|
|
297
|
+
}
|
|
298
|
+
if (response.type === 'clarification') {
|
|
299
|
+
return {
|
|
300
|
+
state: 'clarification',
|
|
301
|
+
phase: 'clarify',
|
|
302
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
303
|
+
assistantMessage: response.message || 'Preciso de mais detalhes para continuar.',
|
|
304
|
+
clarificationQuestions: this.toClarificationQuestions(response),
|
|
305
|
+
quickReplies: this.toQuickReplies(response),
|
|
306
|
+
canApply: false,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
if (response.type === 'info') {
|
|
310
|
+
const message = response.message || response.explanation || 'Informacao gerada.';
|
|
311
|
+
return { state: 'success', phase: 'summarize', sessionId: response.sessionId ?? request.sessionId, assistantMessage: message, statusText: message, canApply: false };
|
|
312
|
+
}
|
|
313
|
+
if (response.type === 'error') {
|
|
314
|
+
const message = response.message || 'Falha ao gerar alteracao de expansion.';
|
|
315
|
+
return {
|
|
316
|
+
state: 'error',
|
|
317
|
+
phase: 'capture',
|
|
318
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
319
|
+
assistantMessage: message,
|
|
320
|
+
errorText: message,
|
|
321
|
+
diagnostics: response.warnings?.length ? { warnings: response.warnings } : undefined,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
if (response.patch && Object.keys(response.patch).length > 0) {
|
|
325
|
+
return {
|
|
326
|
+
state: 'error',
|
|
327
|
+
phase: 'review',
|
|
328
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
329
|
+
assistantMessage: 'O expansion rejeitou patch livre. Gere um componentEditPlan validado pelo PRAXIS_EXPANSION_AUTHORING_MANIFEST antes de propor alteracao local.',
|
|
330
|
+
errorText: 'Patch livre de expansion rejeitado.',
|
|
331
|
+
canApply: false,
|
|
332
|
+
pendingPatch: null,
|
|
333
|
+
diagnostics: {
|
|
334
|
+
warnings: [
|
|
335
|
+
'free-expansion-patch-rejected',
|
|
336
|
+
'Use componentEditPlan validado contra PRAXIS_EXPANSION_AUTHORING_MANIFEST.',
|
|
337
|
+
],
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
state: 'success',
|
|
343
|
+
phase: 'summarize',
|
|
344
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
345
|
+
assistantMessage: response.message || response.explanation || 'Nenhuma alteracao necessaria.',
|
|
346
|
+
statusText: response.message || response.explanation || 'Nenhuma alteracao necessaria.',
|
|
347
|
+
canApply: false,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
compileAdapterResponse(response) {
|
|
351
|
+
const compiled = this.adapter.compileAiResponse?.(response);
|
|
352
|
+
if (!compiled)
|
|
353
|
+
return response;
|
|
354
|
+
if (compiled.type === 'error') {
|
|
355
|
+
return {
|
|
356
|
+
type: 'error',
|
|
357
|
+
message: compiled.message || 'O componentEditPlan do expansion nao passou na validacao de capacidades.',
|
|
358
|
+
warnings: compiled.warnings,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const warnings = [...(response.warnings ?? []), ...(compiled.warnings ?? [])];
|
|
362
|
+
return { ...response, ...compiled, patch: compiled.patch, warnings: warnings.length ? warnings : undefined };
|
|
363
|
+
}
|
|
364
|
+
toChatMessages(messages, prompt) {
|
|
365
|
+
const supported = (messages ?? [])
|
|
366
|
+
.filter((message) => message.role === 'user' || message.role === 'assistant' || message.role === 'system')
|
|
367
|
+
.map((message) => ({ role: message.role, content: message.text }))
|
|
368
|
+
.filter((message) => message.content.trim().length > 0);
|
|
369
|
+
return supported.length ? supported : [{ role: 'user', content: prompt }];
|
|
370
|
+
}
|
|
371
|
+
toClarificationQuestions(response) {
|
|
372
|
+
const labels = response.questions?.length
|
|
373
|
+
? response.questions
|
|
374
|
+
: response.message ? [response.message] : ['Qual ajuste voce quer aplicar nos paineis?'];
|
|
375
|
+
const options = this.toQuickReplies(response).map((reply) => ({ id: reply.id, label: reply.label, value: reply.prompt }));
|
|
376
|
+
return labels.map((label, index) => ({
|
|
377
|
+
id: `expansion-clarification-${index + 1}`,
|
|
378
|
+
type: options.length ? 'single-choice' : 'text',
|
|
379
|
+
label,
|
|
380
|
+
allowCustom: true,
|
|
381
|
+
options,
|
|
382
|
+
}));
|
|
383
|
+
}
|
|
384
|
+
toQuickReplies(response) {
|
|
385
|
+
const payloads = response.optionPayloads ?? [];
|
|
386
|
+
if (payloads.length) {
|
|
387
|
+
return payloads.map((option, index) => {
|
|
388
|
+
const label = option.label?.trim() || option.value?.trim() || `Opcao ${index + 1}`;
|
|
389
|
+
const prompt = option.example?.trim() || option.value?.trim() || label;
|
|
390
|
+
return { id: `option-${index + 1}`, label, prompt, kind: 'clarification-option' };
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return (response.options ?? [])
|
|
394
|
+
.filter((option) => !!option?.trim())
|
|
395
|
+
.map((option, index) => ({ id: `option-${index + 1}`, label: option.trim(), prompt: option.trim(), kind: 'clarification-option' }));
|
|
396
|
+
}
|
|
397
|
+
buildCurrentStateDigest(dataProfile) {
|
|
398
|
+
const panelCount = typeof dataProfile?.['panelCount'] === 'number' ? dataProfile['panelCount'] : undefined;
|
|
399
|
+
return panelCount !== undefined ? { rowCount: panelCount } : {};
|
|
400
|
+
}
|
|
401
|
+
shouldRouteToGovernedDecision(prompt, contextHints) {
|
|
402
|
+
const normalized = prompt.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
|
403
|
+
const recommendedFlow = this.toRecord(contextHints?.['domainCatalog'])?.['recommendedAuthoringFlow'];
|
|
404
|
+
if (recommendedFlow === 'shared_rule_authoring')
|
|
405
|
+
return true;
|
|
406
|
+
return [
|
|
407
|
+
'regra',
|
|
408
|
+
'politica',
|
|
409
|
+
'policy',
|
|
410
|
+
'compliance',
|
|
411
|
+
'lgpd',
|
|
412
|
+
'privacidade',
|
|
413
|
+
'aprovacao',
|
|
414
|
+
'aprovar',
|
|
415
|
+
'publicar',
|
|
416
|
+
'materializar',
|
|
417
|
+
'enforcement',
|
|
418
|
+
'validacao de negocio',
|
|
419
|
+
'validar negocio',
|
|
420
|
+
'elegibilidade',
|
|
421
|
+
'permissao',
|
|
422
|
+
'acesso',
|
|
423
|
+
].some((term) => normalized.includes(term));
|
|
424
|
+
}
|
|
425
|
+
toGovernedDecisionHandoff(prompt, request) {
|
|
426
|
+
const message = 'Esse pedido parece alterar uma decisao de negocio compartilhada. O expansion pode localizar o painel e a experiencia afetada, mas a regra deve seguir pelo fluxo governado de domain-rules antes de qualquer materializacao runtime.';
|
|
427
|
+
return {
|
|
428
|
+
state: 'clarification',
|
|
429
|
+
phase: 'clarify',
|
|
430
|
+
sessionId: request.sessionId,
|
|
431
|
+
assistantMessage: message,
|
|
432
|
+
statusText: 'Handoff governado necessario.',
|
|
433
|
+
canApply: false,
|
|
434
|
+
quickReplies: [
|
|
435
|
+
{
|
|
436
|
+
id: 'shared-rule-handoff',
|
|
437
|
+
label: 'Continuar como regra governada',
|
|
438
|
+
prompt,
|
|
439
|
+
kind: 'shared-rule-handoff',
|
|
440
|
+
description: 'Criar intake de domain-rules em vez de aplicar patch local no expansion.',
|
|
441
|
+
icon: 'rule',
|
|
442
|
+
tone: 'warning',
|
|
443
|
+
contextHints: {
|
|
444
|
+
flowId: 'shared_rule_authoring',
|
|
445
|
+
source: 'praxis-expansion',
|
|
446
|
+
recommendedAction: 'domain-rules/intake',
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
clarificationQuestions: [
|
|
451
|
+
{
|
|
452
|
+
id: 'expansion-governed-rule-confirmation',
|
|
453
|
+
type: 'confirm',
|
|
454
|
+
label: 'Deseja continuar pelo fluxo governado de regras compartilhadas?',
|
|
455
|
+
description: 'Esse caminho permite intake, simulacao, aprovacao/publicacao, materializacao e validacao de enforcement.',
|
|
456
|
+
required: true,
|
|
457
|
+
options: [
|
|
458
|
+
{
|
|
459
|
+
id: 'shared-rule-handoff',
|
|
460
|
+
label: 'Sim, continuar governado',
|
|
461
|
+
value: prompt,
|
|
462
|
+
description: 'Nao aplicar como patch local do expansion.',
|
|
463
|
+
contextHints: { flowId: 'shared_rule_authoring', source: 'praxis-expansion' },
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
},
|
|
467
|
+
],
|
|
468
|
+
diagnostics: {
|
|
469
|
+
governedDecisionHandoff: {
|
|
470
|
+
flowId: 'shared_rule_authoring',
|
|
471
|
+
sourcePrompt: prompt,
|
|
472
|
+
sourceComponent: 'praxis-expansion',
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
optionalJsonObject(value) {
|
|
478
|
+
if (value === undefined || value === null)
|
|
479
|
+
return undefined;
|
|
480
|
+
const object = this.toAiJsonObject(value);
|
|
481
|
+
return Object.keys(object).length ? object : undefined;
|
|
482
|
+
}
|
|
483
|
+
toAiJsonObject(value) {
|
|
484
|
+
const record = this.toRecord(value);
|
|
485
|
+
if (!record)
|
|
486
|
+
return {};
|
|
487
|
+
try {
|
|
488
|
+
return JSON.parse(JSON.stringify(record));
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
return {};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
toRecord(value) {
|
|
495
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
496
|
+
? value
|
|
497
|
+
: null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
188
501
|
class PraxisExpansion {
|
|
189
502
|
config;
|
|
190
503
|
expansionId;
|
|
@@ -212,9 +525,35 @@ class PraxisExpansion {
|
|
|
212
525
|
catch {
|
|
213
526
|
return undefined;
|
|
214
527
|
} })();
|
|
528
|
+
aiApi = inject(AiBackendApiService);
|
|
529
|
+
assistantSessions = inject(PraxisAssistantSessionRegistryService);
|
|
530
|
+
aiTurnOrchestrator = inject(PraxisAssistantTurnOrchestratorService);
|
|
531
|
+
aiAssistantSessionEffect = effect(() => {
|
|
532
|
+
const session = this.assistantSessions.activeSession();
|
|
533
|
+
if (!session || session.id !== this.resolveAiAssistantSessionId())
|
|
534
|
+
return;
|
|
535
|
+
if (!this.aiAssistantOpen) {
|
|
536
|
+
this.openAiAssistantFromSession(session);
|
|
537
|
+
}
|
|
538
|
+
}, ...(ngDevMode ? [{ debugName: "aiAssistantSessionEffect" }] : []));
|
|
215
539
|
warnedMissingId = false;
|
|
216
540
|
panelForms = new Map();
|
|
217
541
|
aiAdapter = new ExpansionAiAdapter(this);
|
|
542
|
+
aiAssistantOpen = false;
|
|
543
|
+
aiAssistantPrompt = '';
|
|
544
|
+
aiAssistantViewState = null;
|
|
545
|
+
aiAssistantLayout = createPraxisAssistantViewportLayout();
|
|
546
|
+
aiAssistantLabels = {
|
|
547
|
+
title: 'Copiloto semantico Praxis',
|
|
548
|
+
subtitle: 'Converse, revise e governe ajustes dos paineis.',
|
|
549
|
+
prompt: 'Mensagem',
|
|
550
|
+
promptPlaceholder: 'Descreva o ajuste que voce precisa nos paineis.',
|
|
551
|
+
emptyConversation: 'Diga o que voce quer alterar no expansion.',
|
|
552
|
+
submit: 'Interpretar pedido',
|
|
553
|
+
apply: 'Aplicar ajuste',
|
|
554
|
+
};
|
|
555
|
+
aiAssistantController = null;
|
|
556
|
+
aiAssistantStateSubscription = null;
|
|
218
557
|
accordionRef;
|
|
219
558
|
panels;
|
|
220
559
|
injectedDefaults = inject(MAT_EXPANSION_PANEL_DEFAULT_OPTIONS, { optional: true });
|
|
@@ -238,6 +577,10 @@ class PraxisExpansion {
|
|
|
238
577
|
this.persistConfig(this.config);
|
|
239
578
|
}
|
|
240
579
|
}
|
|
580
|
+
ngOnDestroy() {
|
|
581
|
+
this.assistantSessions.removeContextSession(this.buildAiAssistantContextSnapshot().identity);
|
|
582
|
+
this.aiAssistantStateSubscription?.unsubscribe();
|
|
583
|
+
}
|
|
241
584
|
styleCss() {
|
|
242
585
|
const t = this.config?.appearance?.tokens;
|
|
243
586
|
const appearance = this.config?.appearance;
|
|
@@ -477,6 +820,306 @@ class PraxisExpansion {
|
|
|
477
820
|
}
|
|
478
821
|
this.cdr.markForCheck();
|
|
479
822
|
}
|
|
823
|
+
openAiAssistant() {
|
|
824
|
+
this.initializeAiAssistantController();
|
|
825
|
+
this.aiAssistantOpen = true;
|
|
826
|
+
this.aiAssistantController?.setContextItems(this.buildAiAssistantContextItems());
|
|
827
|
+
this.syncAiAssistantSession('active');
|
|
828
|
+
this.cdr.markForCheck();
|
|
829
|
+
}
|
|
830
|
+
openAiAssistantFromSession(session) {
|
|
831
|
+
if (session.id !== this.resolveAiAssistantSessionId())
|
|
832
|
+
return;
|
|
833
|
+
this.initializeAiAssistantController();
|
|
834
|
+
this.aiAssistantOpen = true;
|
|
835
|
+
this.aiAssistantController?.setContextItems(this.buildAiAssistantContextItems());
|
|
836
|
+
this.syncAiAssistantSession('active');
|
|
837
|
+
this.cdr.markForCheck();
|
|
838
|
+
}
|
|
839
|
+
closeAiAssistant() {
|
|
840
|
+
this.aiAssistantOpen = false;
|
|
841
|
+
this.syncAiAssistantSession('minimized');
|
|
842
|
+
this.cdr.markForCheck();
|
|
843
|
+
}
|
|
844
|
+
onAiAssistantPromptChange(prompt) {
|
|
845
|
+
this.aiAssistantPrompt = prompt;
|
|
846
|
+
this.syncAiAssistantSession();
|
|
847
|
+
}
|
|
848
|
+
onAiAssistantSubmit(prompt) {
|
|
849
|
+
this.aiAssistantController?.submitPrompt(prompt).subscribe((state) => {
|
|
850
|
+
this.aiAssistantPrompt = '';
|
|
851
|
+
this.aiAssistantViewState = state;
|
|
852
|
+
this.syncAiAssistantSession();
|
|
853
|
+
this.cdr.markForCheck();
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
onAiAssistantApply() {
|
|
857
|
+
this.aiAssistantController?.apply().subscribe((state) => {
|
|
858
|
+
this.aiAssistantViewState = state;
|
|
859
|
+
this.syncAiAssistantSession();
|
|
860
|
+
this.cdr.markForCheck();
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
onAiAssistantRetry() {
|
|
864
|
+
this.aiAssistantController?.retry().subscribe((state) => {
|
|
865
|
+
this.aiAssistantViewState = state;
|
|
866
|
+
this.syncAiAssistantSession();
|
|
867
|
+
this.cdr.markForCheck();
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
onAiAssistantCancel() {
|
|
871
|
+
this.aiAssistantController?.cancel().subscribe((state) => {
|
|
872
|
+
this.aiAssistantPrompt = '';
|
|
873
|
+
this.aiAssistantViewState = state;
|
|
874
|
+
this.syncAiAssistantSession();
|
|
875
|
+
this.cdr.markForCheck();
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
onAiAssistantQuickReply(reply) {
|
|
879
|
+
const controller = this.aiAssistantController;
|
|
880
|
+
if (!controller)
|
|
881
|
+
return;
|
|
882
|
+
const state = controller.snapshot();
|
|
883
|
+
const next$ = state.state === 'clarification'
|
|
884
|
+
? controller.answerClarification(reply.prompt)
|
|
885
|
+
: controller.submitPrompt(reply.prompt, {
|
|
886
|
+
kind: reply.kind || 'quick-reply',
|
|
887
|
+
id: reply.id,
|
|
888
|
+
value: reply.prompt,
|
|
889
|
+
});
|
|
890
|
+
next$.subscribe((nextState) => {
|
|
891
|
+
this.aiAssistantPrompt = '';
|
|
892
|
+
this.aiAssistantViewState = nextState;
|
|
893
|
+
this.syncAiAssistantSession();
|
|
894
|
+
this.cdr.markForCheck();
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
onAiAssistantEditMessage(message) {
|
|
898
|
+
this.aiAssistantPrompt = message.text;
|
|
899
|
+
this.cdr.markForCheck();
|
|
900
|
+
}
|
|
901
|
+
onAiAssistantResendMessage(message) {
|
|
902
|
+
this.aiAssistantController?.resendMessage(message.id).subscribe((state) => {
|
|
903
|
+
this.aiAssistantPrompt = '';
|
|
904
|
+
this.aiAssistantViewState = state;
|
|
905
|
+
this.syncAiAssistantSession();
|
|
906
|
+
this.cdr.markForCheck();
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
onAiAssistantLayoutChange(layout) {
|
|
910
|
+
this.aiAssistantLayout = layout;
|
|
911
|
+
}
|
|
912
|
+
buildAiAssistantContextItems() {
|
|
913
|
+
const panels = this.config?.panels ?? [];
|
|
914
|
+
const items = [
|
|
915
|
+
{
|
|
916
|
+
id: 'component',
|
|
917
|
+
label: 'Componente',
|
|
918
|
+
value: 'Expansion',
|
|
919
|
+
kind: 'component',
|
|
920
|
+
icon: 'unfold_more',
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
id: 'expansion-id',
|
|
924
|
+
label: 'Expansion',
|
|
925
|
+
value: this.safeAiAssistantExpansionId(),
|
|
926
|
+
kind: 'custom',
|
|
927
|
+
icon: 'tag',
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
id: 'panels',
|
|
931
|
+
label: 'Paineis',
|
|
932
|
+
value: String(panels.length),
|
|
933
|
+
kind: 'custom',
|
|
934
|
+
icon: 'view_agenda',
|
|
935
|
+
},
|
|
936
|
+
];
|
|
937
|
+
const expanded = panels.filter((panel) => panel.expanded).length;
|
|
938
|
+
if (expanded > 0) {
|
|
939
|
+
items.push({
|
|
940
|
+
id: 'expanded-panels',
|
|
941
|
+
label: 'Expandidos',
|
|
942
|
+
value: String(expanded),
|
|
943
|
+
kind: 'custom',
|
|
944
|
+
icon: 'expand_less',
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
return items;
|
|
948
|
+
}
|
|
949
|
+
initializeAiAssistantController() {
|
|
950
|
+
if (this.aiAssistantController)
|
|
951
|
+
return;
|
|
952
|
+
const flow = new ExpansionAgenticAuthoringTurnFlow(this.aiAdapter, this.aiApi);
|
|
953
|
+
const controller = this.aiTurnOrchestrator.createController(flow, {
|
|
954
|
+
componentId: this.aiAdapter.componentId || 'praxis-expansion',
|
|
955
|
+
componentType: this.aiAdapter.componentType || 'expansion',
|
|
956
|
+
contextItems: this.buildAiAssistantContextItems(),
|
|
957
|
+
});
|
|
958
|
+
this.aiAssistantController = controller;
|
|
959
|
+
this.aiAssistantViewState = controller.snapshot();
|
|
960
|
+
this.aiAssistantStateSubscription?.unsubscribe();
|
|
961
|
+
this.aiAssistantStateSubscription = controller.state$.subscribe((state) => {
|
|
962
|
+
this.aiAssistantViewState = state;
|
|
963
|
+
this.syncAiAssistantSession();
|
|
964
|
+
this.cdr.markForCheck();
|
|
965
|
+
});
|
|
966
|
+
this.cdr.markForCheck();
|
|
967
|
+
}
|
|
968
|
+
buildAiAssistantContextSnapshot() {
|
|
969
|
+
const counts = this.collectAiAssistantCounts();
|
|
970
|
+
const panelNames = this.collectAiAssistantPanelNames();
|
|
971
|
+
return {
|
|
972
|
+
identity: {
|
|
973
|
+
sessionId: this.resolveAiAssistantSessionId(),
|
|
974
|
+
ownerId: this.resolveAiAssistantOwnerId(),
|
|
975
|
+
ownerType: 'expansion',
|
|
976
|
+
componentId: 'praxis-expansion',
|
|
977
|
+
componentType: 'expansion',
|
|
978
|
+
routeKey: this.resolveAiAssistantRouteKey(),
|
|
979
|
+
},
|
|
980
|
+
target: {
|
|
981
|
+
kind: 'component',
|
|
982
|
+
id: this.resolveAiAssistantOwnerId(),
|
|
983
|
+
label: this.safeAiAssistantExpansionId(),
|
|
984
|
+
metadata: {
|
|
985
|
+
expansionId: this.safeAiAssistantExpansionId(),
|
|
986
|
+
hasCustomization: !!this.enableCustomization,
|
|
987
|
+
},
|
|
988
|
+
},
|
|
989
|
+
contextItems: this.buildAiAssistantContextItems().map((item) => ({
|
|
990
|
+
id: item.id,
|
|
991
|
+
label: item.label,
|
|
992
|
+
value: item.value || '',
|
|
993
|
+
kind: item.kind,
|
|
994
|
+
})),
|
|
995
|
+
mode: 'agentic-authoring',
|
|
996
|
+
authoringManifestRef: {
|
|
997
|
+
componentId: 'praxis-expansion',
|
|
998
|
+
source: 'PRAXIS_EXPANSION_AUTHORING_MANIFEST',
|
|
999
|
+
},
|
|
1000
|
+
schemaFields: panelNames.length ? panelNames : undefined,
|
|
1001
|
+
dataProfileDigest: {
|
|
1002
|
+
summary: `${counts.panelCount} painel(is), ${counts.contentPanelCount} com campo(s), ${counts.widgetPanelCount} com widget(s)`,
|
|
1003
|
+
counts,
|
|
1004
|
+
},
|
|
1005
|
+
runtimeStateDigest: {
|
|
1006
|
+
summary: `Expansion ${this.config?.accordion?.multi ? 'multi' : 'single'}, ${counts.expandedCount} painel(is) expandido(s)`,
|
|
1007
|
+
fields: [
|
|
1008
|
+
'panels',
|
|
1009
|
+
'accordion',
|
|
1010
|
+
'content',
|
|
1011
|
+
'widgets',
|
|
1012
|
+
],
|
|
1013
|
+
},
|
|
1014
|
+
capabilityRefs: [
|
|
1015
|
+
{
|
|
1016
|
+
id: 'expansion.component-edit-plan',
|
|
1017
|
+
label: 'Plano de edicao de paineis',
|
|
1018
|
+
source: 'PRAXIS_EXPANSION_AUTHORING_MANIFEST',
|
|
1019
|
+
risk: 'medium',
|
|
1020
|
+
},
|
|
1021
|
+
],
|
|
1022
|
+
governanceHints: [
|
|
1023
|
+
{
|
|
1024
|
+
kind: 'business-rule-boundary',
|
|
1025
|
+
label: 'Regras compartilhadas exigem governanca',
|
|
1026
|
+
reason: 'Politicas de acesso, validacoes reutilizaveis e compliance nao devem ser aplicadas como patch local do expansion.',
|
|
1027
|
+
risk: 'high',
|
|
1028
|
+
},
|
|
1029
|
+
],
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
syncAiAssistantSession(visibility = null) {
|
|
1033
|
+
if (!this.enableCustomization)
|
|
1034
|
+
return;
|
|
1035
|
+
if (!this.aiAssistantOpen && !this.hasAiAssistantSessionState() && visibility !== 'minimized')
|
|
1036
|
+
return;
|
|
1037
|
+
const state = this.aiAssistantViewState;
|
|
1038
|
+
this.assistantSessions.upsertContextSession(this.buildAiAssistantContextSnapshot(), {
|
|
1039
|
+
title: 'Copiloto semantico Praxis',
|
|
1040
|
+
summary: this.resolveAiAssistantSummary(),
|
|
1041
|
+
mode: state?.mode || 'agentic-authoring',
|
|
1042
|
+
state: state?.state || 'idle',
|
|
1043
|
+
visibility: visibility ?? (this.aiAssistantOpen ? 'active' : 'minimized'),
|
|
1044
|
+
badge: this.resolveAiAssistantBadge(),
|
|
1045
|
+
icon: this.resolveAiAssistantIcon(),
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
hasAiAssistantSessionState() {
|
|
1049
|
+
return !!this.aiAssistantPrompt.trim()
|
|
1050
|
+
|| !!this.aiAssistantViewState?.messages?.length
|
|
1051
|
+
|| !!this.aiAssistantViewState?.quickReplies?.length
|
|
1052
|
+
|| !!this.aiAssistantViewState?.pendingPatch
|
|
1053
|
+
|| !!this.aiAssistantViewState?.statusText?.trim()
|
|
1054
|
+
|| !!this.aiAssistantViewState?.errorText?.trim();
|
|
1055
|
+
}
|
|
1056
|
+
resolveAiAssistantSessionId() {
|
|
1057
|
+
return `expansion:${this.resolveAiAssistantRouteKey()}:${this.resolveAiAssistantOwnerId()}`;
|
|
1058
|
+
}
|
|
1059
|
+
resolveAiAssistantOwnerId() {
|
|
1060
|
+
return (this.componentInstanceId || this.safeAiAssistantExpansionId() || 'expansion').trim() || 'expansion';
|
|
1061
|
+
}
|
|
1062
|
+
safeAiAssistantExpansionId() {
|
|
1063
|
+
return String(this.expansionId || '').trim();
|
|
1064
|
+
}
|
|
1065
|
+
resolveAiAssistantRouteKey() {
|
|
1066
|
+
const routePath = this.route?.snapshot?.routeConfig?.path?.trim();
|
|
1067
|
+
return routePath || 'local';
|
|
1068
|
+
}
|
|
1069
|
+
resolveAiAssistantSummary() {
|
|
1070
|
+
const status = this.aiAssistantViewState?.statusText?.trim();
|
|
1071
|
+
if (status)
|
|
1072
|
+
return status;
|
|
1073
|
+
const error = this.aiAssistantViewState?.errorText?.trim();
|
|
1074
|
+
if (error)
|
|
1075
|
+
return error;
|
|
1076
|
+
const prompt = this.aiAssistantPrompt.trim();
|
|
1077
|
+
if (prompt)
|
|
1078
|
+
return prompt.length > 96 ? `${prompt.slice(0, 93)}...` : prompt;
|
|
1079
|
+
const lastMessage = [...(this.aiAssistantViewState?.messages ?? [])].reverse()
|
|
1080
|
+
.find((message) => message.role === 'assistant' || message.role === 'user');
|
|
1081
|
+
if (lastMessage?.text) {
|
|
1082
|
+
return lastMessage.text.length > 96 ? `${lastMessage.text.slice(0, 93)}...` : lastMessage.text;
|
|
1083
|
+
}
|
|
1084
|
+
return 'Assistente contextual dos paineis.';
|
|
1085
|
+
}
|
|
1086
|
+
resolveAiAssistantBadge() {
|
|
1087
|
+
const state = this.aiAssistantViewState?.state;
|
|
1088
|
+
if (state === 'error')
|
|
1089
|
+
return 'erro';
|
|
1090
|
+
if (state === 'clarification')
|
|
1091
|
+
return 'revisar';
|
|
1092
|
+
if (state === 'review')
|
|
1093
|
+
return 'preview';
|
|
1094
|
+
if (state === 'success')
|
|
1095
|
+
return 'ok';
|
|
1096
|
+
return undefined;
|
|
1097
|
+
}
|
|
1098
|
+
resolveAiAssistantIcon() {
|
|
1099
|
+
const state = this.aiAssistantViewState?.state;
|
|
1100
|
+
if (state === 'error')
|
|
1101
|
+
return 'error';
|
|
1102
|
+
if (state === 'clarification')
|
|
1103
|
+
return 'rule';
|
|
1104
|
+
if (state === 'review')
|
|
1105
|
+
return 'rate_review';
|
|
1106
|
+
return 'auto_awesome';
|
|
1107
|
+
}
|
|
1108
|
+
collectAiAssistantPanelNames() {
|
|
1109
|
+
return Array.from(new Set((this.config?.panels ?? [])
|
|
1110
|
+
.map((panel, index) => panel.id || panel.title || `panel-${index + 1}`)
|
|
1111
|
+
.filter((name) => typeof name === 'string' && !!name.trim())));
|
|
1112
|
+
}
|
|
1113
|
+
collectAiAssistantCounts() {
|
|
1114
|
+
const panels = this.config?.panels ?? [];
|
|
1115
|
+
return {
|
|
1116
|
+
panelCount: panels.length,
|
|
1117
|
+
contentPanelCount: panels.filter((panel) => Array.isArray(panel.content) && panel.content.length > 0).length,
|
|
1118
|
+
widgetPanelCount: panels.filter((panel) => Array.isArray(panel.widgets) && panel.widgets.length > 0).length,
|
|
1119
|
+
actionPanelCount: panels.filter((panel) => Array.isArray(panel.actionButtons) && panel.actionButtons.length > 0).length,
|
|
1120
|
+
expandedCount: panels.filter((panel) => !!panel.expanded).length,
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
480
1123
|
openEditor() {
|
|
481
1124
|
const key = this.storageKey() || this.expansionId || 'default';
|
|
482
1125
|
const ref = this.settings.open({
|
|
@@ -574,10 +1217,48 @@ class PraxisExpansion {
|
|
|
574
1217
|
|
|
575
1218
|
@if (enableCustomization) {
|
|
576
1219
|
<div class="expansion-ai-assistant">
|
|
577
|
-
<
|
|
1220
|
+
<button
|
|
1221
|
+
mat-mini-fab
|
|
1222
|
+
color="primary"
|
|
1223
|
+
type="button"
|
|
1224
|
+
class="expansion-ai-assistant-trigger"
|
|
1225
|
+
aria-label="Abrir copiloto semantico Praxis dos paineis"
|
|
1226
|
+
data-testid="praxis-expansion-ai-assistant-trigger"
|
|
1227
|
+
(click)="openAiAssistant()"
|
|
1228
|
+
>
|
|
1229
|
+
<mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
|
|
1230
|
+
</button>
|
|
578
1231
|
</div>
|
|
579
1232
|
}
|
|
580
1233
|
|
|
1234
|
+
<praxis-ai-assistant-shell
|
|
1235
|
+
*ngIf="aiAssistantOpen && aiAssistantViewState"
|
|
1236
|
+
[labels]="aiAssistantLabels"
|
|
1237
|
+
[mode]="aiAssistantViewState.mode"
|
|
1238
|
+
[state]="aiAssistantViewState.state"
|
|
1239
|
+
[contextItems]="aiAssistantViewState.contextItems"
|
|
1240
|
+
[attachments]="aiAssistantViewState.attachments"
|
|
1241
|
+
[messages]="aiAssistantViewState.messages"
|
|
1242
|
+
[quickReplies]="aiAssistantViewState.quickReplies"
|
|
1243
|
+
[prompt]="aiAssistantPrompt"
|
|
1244
|
+
[statusText]="aiAssistantViewState.statusText"
|
|
1245
|
+
[errorText]="aiAssistantViewState.errorText"
|
|
1246
|
+
[busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
|
|
1247
|
+
[canApply]="aiAssistantViewState.canApply"
|
|
1248
|
+
[layout]="aiAssistantLayout"
|
|
1249
|
+
testIdPrefix="praxis-expansion-ai-assistant"
|
|
1250
|
+
(promptChange)="onAiAssistantPromptChange($event)"
|
|
1251
|
+
(submitPrompt)="onAiAssistantSubmit($event)"
|
|
1252
|
+
(apply)="onAiAssistantApply()"
|
|
1253
|
+
(retryTurn)="onAiAssistantRetry()"
|
|
1254
|
+
(cancelTurn)="onAiAssistantCancel()"
|
|
1255
|
+
(quickReply)="onAiAssistantQuickReply($event)"
|
|
1256
|
+
(editMessage)="onAiAssistantEditMessage($event)"
|
|
1257
|
+
(resendMessage)="onAiAssistantResendMessage($event)"
|
|
1258
|
+
(layoutChange)="onAiAssistantLayoutChange($event)"
|
|
1259
|
+
(close)="closeAiAssistant()"
|
|
1260
|
+
></praxis-ai-assistant-shell>
|
|
1261
|
+
|
|
581
1262
|
@if (hasMultiple()) {
|
|
582
1263
|
<mat-accordion
|
|
583
1264
|
#accordion
|
|
@@ -721,7 +1402,7 @@ class PraxisExpansion {
|
|
|
721
1402
|
>
|
|
722
1403
|
<mat-icon fontIcon="edit"></mat-icon>
|
|
723
1404
|
</button>
|
|
724
|
-
`, isInline: true, styles: [":host{display:block;position:relative;color:var(--md-sys-color-on-surface)}.praxis-expansion-root{display:block;--p-exp-surface: var(--md-sys-color-surface);--p-exp-surface-container: var(--md-sys-color-surface-container);--p-exp-border: var(--md-sys-color-outline-variant);--p-exp-text: var(--md-sys-color-on-surface);--p-exp-text-muted: var(--md-sys-color-on-surface-variant);--p-exp-focus: var(--md-sys-color-primary);--p-exp-radius: 12px}.mat-expansion-panel{background:var(--p-exp-surface);border:1px solid var(--p-exp-border);border-radius:var(--p-exp-radius);overflow:hidden}.mat-expansion-panel:not(:last-child){margin-bottom:var(--p-exp-panel-gap, 12px)}.mat-expansion-panel-header{background:var(--p-exp-surface-container);color:var(--p-exp-text)}.mat-expansion-panel-header:focus-visible{outline:2px solid var(--p-exp-focus);outline-offset:-2px}.mat-expansion-panel-header-title{font-weight:600}.mat-expansion-panel-header-description{color:var(--p-exp-text-muted)}.mat-expansion-panel-body{padding:12px 16px 16px}.mat-action-row{border-top:1px solid var(--p-exp-border)}.density-compact .mat-expansion-panel-body{padding:8px 12px 12px}.density-compact .mat-expansion-panel-header{min-height:40px;padding:0 12px}.density-comfortable .mat-expansion-panel-body{padding:12px 16px 16px}.density-comfortable .mat-expansion-panel-header{min-height:48px;padding:0 16px}.density-spacious .mat-expansion-panel-body{padding:16px 20px 20px}.density-spacious .mat-expansion-panel-header{min-height:56px;padding:0 20px}.praxis-expansion-empty{display:flex;gap:8px;align-items:center;padding:8px 12px;color:var(--p-exp-text-muted)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.expansion-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i2.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i2.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i2.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i2.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "directive", type: i2.MatExpansionPanelContent, selector: "ng-template[matExpansionPanelContent]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.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: i4.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type:
|
|
1405
|
+
`, isInline: true, styles: [":host{display:block;position:relative;color:var(--md-sys-color-on-surface)}.praxis-expansion-root{display:block;--p-exp-surface: var(--md-sys-color-surface);--p-exp-surface-container: var(--md-sys-color-surface-container);--p-exp-border: var(--md-sys-color-outline-variant);--p-exp-text: var(--md-sys-color-on-surface);--p-exp-text-muted: var(--md-sys-color-on-surface-variant);--p-exp-focus: var(--md-sys-color-primary);--p-exp-radius: 12px}.mat-expansion-panel{background:var(--p-exp-surface);border:1px solid var(--p-exp-border);border-radius:var(--p-exp-radius);overflow:hidden}.mat-expansion-panel:not(:last-child){margin-bottom:var(--p-exp-panel-gap, 12px)}.mat-expansion-panel-header{background:var(--p-exp-surface-container);color:var(--p-exp-text)}.mat-expansion-panel-header:focus-visible{outline:2px solid var(--p-exp-focus);outline-offset:-2px}.mat-expansion-panel-header-title{font-weight:600}.mat-expansion-panel-header-description{color:var(--p-exp-text-muted)}.mat-expansion-panel-body{padding:12px 16px 16px}.mat-action-row{border-top:1px solid var(--p-exp-border)}.density-compact .mat-expansion-panel-body{padding:8px 12px 12px}.density-compact .mat-expansion-panel-header{min-height:40px;padding:0 12px}.density-comfortable .mat-expansion-panel-body{padding:12px 16px 16px}.density-comfortable .mat-expansion-panel-header{min-height:48px;padding:0 16px}.density-spacious .mat-expansion-panel-body{padding:16px 20px 20px}.density-spacious .mat-expansion-panel-header{min-height:56px;padding:0 20px}.praxis-expansion-empty{display:flex;gap:8px;align-items:center;padding:8px 12px;color:var(--p-exp-text-muted)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.expansion-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.expansion-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2, 0 4px 12px rgba(0, 0, 0, .18))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i2.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "directive", type: i2.MatExpansionPanelActionRow, selector: "mat-action-row" }, { kind: "component", type: i2.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i2.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i2.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "directive", type: i2.MatExpansionPanelContent, selector: "ng-template[matExpansionPanelContent]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.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: i4.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
725
1406
|
}
|
|
726
1407
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisExpansion, decorators: [{
|
|
727
1408
|
type: Component,
|
|
@@ -733,7 +1414,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
733
1414
|
DynamicFieldLoaderDirective,
|
|
734
1415
|
DynamicWidgetLoaderDirective,
|
|
735
1416
|
PraxisIconDirective,
|
|
736
|
-
|
|
1417
|
+
PraxisAiAssistantShellComponent,
|
|
737
1418
|
], template: `
|
|
738
1419
|
<div
|
|
739
1420
|
class="praxis-expansion-root"
|
|
@@ -748,10 +1429,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
748
1429
|
|
|
749
1430
|
@if (enableCustomization) {
|
|
750
1431
|
<div class="expansion-ai-assistant">
|
|
751
|
-
<
|
|
1432
|
+
<button
|
|
1433
|
+
mat-mini-fab
|
|
1434
|
+
color="primary"
|
|
1435
|
+
type="button"
|
|
1436
|
+
class="expansion-ai-assistant-trigger"
|
|
1437
|
+
aria-label="Abrir copiloto semantico Praxis dos paineis"
|
|
1438
|
+
data-testid="praxis-expansion-ai-assistant-trigger"
|
|
1439
|
+
(click)="openAiAssistant()"
|
|
1440
|
+
>
|
|
1441
|
+
<mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
|
|
1442
|
+
</button>
|
|
752
1443
|
</div>
|
|
753
1444
|
}
|
|
754
1445
|
|
|
1446
|
+
<praxis-ai-assistant-shell
|
|
1447
|
+
*ngIf="aiAssistantOpen && aiAssistantViewState"
|
|
1448
|
+
[labels]="aiAssistantLabels"
|
|
1449
|
+
[mode]="aiAssistantViewState.mode"
|
|
1450
|
+
[state]="aiAssistantViewState.state"
|
|
1451
|
+
[contextItems]="aiAssistantViewState.contextItems"
|
|
1452
|
+
[attachments]="aiAssistantViewState.attachments"
|
|
1453
|
+
[messages]="aiAssistantViewState.messages"
|
|
1454
|
+
[quickReplies]="aiAssistantViewState.quickReplies"
|
|
1455
|
+
[prompt]="aiAssistantPrompt"
|
|
1456
|
+
[statusText]="aiAssistantViewState.statusText"
|
|
1457
|
+
[errorText]="aiAssistantViewState.errorText"
|
|
1458
|
+
[busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
|
|
1459
|
+
[canApply]="aiAssistantViewState.canApply"
|
|
1460
|
+
[layout]="aiAssistantLayout"
|
|
1461
|
+
testIdPrefix="praxis-expansion-ai-assistant"
|
|
1462
|
+
(promptChange)="onAiAssistantPromptChange($event)"
|
|
1463
|
+
(submitPrompt)="onAiAssistantSubmit($event)"
|
|
1464
|
+
(apply)="onAiAssistantApply()"
|
|
1465
|
+
(retryTurn)="onAiAssistantRetry()"
|
|
1466
|
+
(cancelTurn)="onAiAssistantCancel()"
|
|
1467
|
+
(quickReply)="onAiAssistantQuickReply($event)"
|
|
1468
|
+
(editMessage)="onAiAssistantEditMessage($event)"
|
|
1469
|
+
(resendMessage)="onAiAssistantResendMessage($event)"
|
|
1470
|
+
(layoutChange)="onAiAssistantLayoutChange($event)"
|
|
1471
|
+
(close)="closeAiAssistant()"
|
|
1472
|
+
></praxis-ai-assistant-shell>
|
|
1473
|
+
|
|
755
1474
|
@if (hasMultiple()) {
|
|
756
1475
|
<mat-accordion
|
|
757
1476
|
#accordion
|
|
@@ -895,7 +1614,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
895
1614
|
>
|
|
896
1615
|
<mat-icon fontIcon="edit"></mat-icon>
|
|
897
1616
|
</button>
|
|
898
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;position:relative;color:var(--md-sys-color-on-surface)}.praxis-expansion-root{display:block;--p-exp-surface: var(--md-sys-color-surface);--p-exp-surface-container: var(--md-sys-color-surface-container);--p-exp-border: var(--md-sys-color-outline-variant);--p-exp-text: var(--md-sys-color-on-surface);--p-exp-text-muted: var(--md-sys-color-on-surface-variant);--p-exp-focus: var(--md-sys-color-primary);--p-exp-radius: 12px}.mat-expansion-panel{background:var(--p-exp-surface);border:1px solid var(--p-exp-border);border-radius:var(--p-exp-radius);overflow:hidden}.mat-expansion-panel:not(:last-child){margin-bottom:var(--p-exp-panel-gap, 12px)}.mat-expansion-panel-header{background:var(--p-exp-surface-container);color:var(--p-exp-text)}.mat-expansion-panel-header:focus-visible{outline:2px solid var(--p-exp-focus);outline-offset:-2px}.mat-expansion-panel-header-title{font-weight:600}.mat-expansion-panel-header-description{color:var(--p-exp-text-muted)}.mat-expansion-panel-body{padding:12px 16px 16px}.mat-action-row{border-top:1px solid var(--p-exp-border)}.density-compact .mat-expansion-panel-body{padding:8px 12px 12px}.density-compact .mat-expansion-panel-header{min-height:40px;padding:0 12px}.density-comfortable .mat-expansion-panel-body{padding:12px 16px 16px}.density-comfortable .mat-expansion-panel-header{min-height:48px;padding:0 16px}.density-spacious .mat-expansion-panel-body{padding:16px 20px 20px}.density-spacious .mat-expansion-panel-header{min-height:56px;padding:0 20px}.praxis-expansion-empty{display:flex;gap:8px;align-items:center;padding:8px 12px;color:var(--p-exp-text-muted)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.expansion-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}\n"] }]
|
|
1617
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;position:relative;color:var(--md-sys-color-on-surface)}.praxis-expansion-root{display:block;--p-exp-surface: var(--md-sys-color-surface);--p-exp-surface-container: var(--md-sys-color-surface-container);--p-exp-border: var(--md-sys-color-outline-variant);--p-exp-text: var(--md-sys-color-on-surface);--p-exp-text-muted: var(--md-sys-color-on-surface-variant);--p-exp-focus: var(--md-sys-color-primary);--p-exp-radius: 12px}.mat-expansion-panel{background:var(--p-exp-surface);border:1px solid var(--p-exp-border);border-radius:var(--p-exp-radius);overflow:hidden}.mat-expansion-panel:not(:last-child){margin-bottom:var(--p-exp-panel-gap, 12px)}.mat-expansion-panel-header{background:var(--p-exp-surface-container);color:var(--p-exp-text)}.mat-expansion-panel-header:focus-visible{outline:2px solid var(--p-exp-focus);outline-offset:-2px}.mat-expansion-panel-header-title{font-weight:600}.mat-expansion-panel-header-description{color:var(--p-exp-text-muted)}.mat-expansion-panel-body{padding:12px 16px 16px}.mat-action-row{border-top:1px solid var(--p-exp-border)}.density-compact .mat-expansion-panel-body{padding:8px 12px 12px}.density-compact .mat-expansion-panel-header{min-height:40px;padding:0 12px}.density-comfortable .mat-expansion-panel-body{padding:12px 16px 16px}.density-comfortable .mat-expansion-panel-header{min-height:48px;padding:0 16px}.density-spacious .mat-expansion-panel-body{padding:16px 20px 20px}.density-spacious .mat-expansion-panel-header{min-height:56px;padding:0 20px}.praxis-expansion-empty{display:flex;gap:8px;align-items:center;padding:8px 12px;color:var(--p-exp-text-muted)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.expansion-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.expansion-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2, 0 4px 12px rgba(0, 0, 0, .18))}\n"] }]
|
|
899
1618
|
}], propDecorators: { config: [{
|
|
900
1619
|
type: Input
|
|
901
1620
|
}], expansionId: [{
|
|
@@ -1104,7 +1823,7 @@ class PraxisExpansionConfigEditor {
|
|
|
1104
1823
|
}
|
|
1105
1824
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisExpansionConfigEditor, deps: [{ token: SETTINGS_PANEL_DATA }], target: i0.ɵɵFactoryTarget.Component });
|
|
1106
1825
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisExpansionConfigEditor, isStandalone: true, selector: "praxis-expansion-config-editor", inputs: { config: "config", expansionId: "expansionId" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1107
|
-
<div class="pdx-expansion-editor">
|
|
1826
|
+
<div class="pdx-expansion-editor" data-testid="expansion-config-editor">
|
|
1108
1827
|
<section class="sec">
|
|
1109
1828
|
<h4>Aparência</h4>
|
|
1110
1829
|
<div class="grid two">
|
|
@@ -1357,7 +2076,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1357
2076
|
MatTooltipModule,
|
|
1358
2077
|
PraxisIconDirective,
|
|
1359
2078
|
], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1360
|
-
<div class="pdx-expansion-editor">
|
|
2079
|
+
<div class="pdx-expansion-editor" data-testid="expansion-config-editor">
|
|
1361
2080
|
<section class="sec">
|
|
1362
2081
|
<h4>Aparência</h4>
|
|
1363
2082
|
<div class="grid two">
|
|
@@ -1606,6 +2325,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1606
2325
|
type: Input
|
|
1607
2326
|
}] } });
|
|
1608
2327
|
|
|
2328
|
+
class PraxisExpansionWidgetConfigEditor {
|
|
2329
|
+
inputs = null;
|
|
2330
|
+
widgetKey;
|
|
2331
|
+
expansionEditor;
|
|
2332
|
+
isDirty$ = new BehaviorSubject(false);
|
|
2333
|
+
isValid$ = new BehaviorSubject(true);
|
|
2334
|
+
isBusy$ = new BehaviorSubject(false);
|
|
2335
|
+
subscription = new Subscription();
|
|
2336
|
+
emptyConfig = {};
|
|
2337
|
+
get config() {
|
|
2338
|
+
return this.inputs?.config ?? this.emptyConfig;
|
|
2339
|
+
}
|
|
2340
|
+
get expansionId() {
|
|
2341
|
+
return this.inputs?.expansionId ?? this.widgetKey;
|
|
2342
|
+
}
|
|
2343
|
+
ngAfterViewInit() {
|
|
2344
|
+
if (!this.expansionEditor) {
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
this.subscription.add(this.expansionEditor.isDirty$.subscribe((value) => this.isDirty$.next(value)));
|
|
2348
|
+
this.subscription.add(this.expansionEditor.isValid$.subscribe((value) => this.isValid$.next(value)));
|
|
2349
|
+
this.subscription.add(this.expansionEditor.isBusy$.subscribe((value) => this.isBusy$.next(value)));
|
|
2350
|
+
}
|
|
2351
|
+
ngOnDestroy() {
|
|
2352
|
+
this.subscription.unsubscribe();
|
|
2353
|
+
}
|
|
2354
|
+
getSettingsValue() {
|
|
2355
|
+
return this.buildValue(this.expansionEditor?.getSettingsValue());
|
|
2356
|
+
}
|
|
2357
|
+
onSave() {
|
|
2358
|
+
return this.buildValue(this.expansionEditor?.getSettingsValue());
|
|
2359
|
+
}
|
|
2360
|
+
reset() {
|
|
2361
|
+
this.expansionEditor?.reset();
|
|
2362
|
+
}
|
|
2363
|
+
buildValue(config) {
|
|
2364
|
+
return {
|
|
2365
|
+
inputs: {
|
|
2366
|
+
...(this.inputs ?? {}),
|
|
2367
|
+
config: config ?? this.config,
|
|
2368
|
+
...(this.expansionId ? { expansionId: this.expansionId } : {}),
|
|
2369
|
+
},
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisExpansionWidgetConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2373
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisExpansionWidgetConfigEditor, isStandalone: true, selector: "praxis-expansion-widget-config-editor", inputs: { inputs: "inputs", widgetKey: "widgetKey" }, viewQueries: [{ propertyName: "expansionEditor", first: true, predicate: ["expansionEditor"], descendants: true }], ngImport: i0, template: `
|
|
2374
|
+
<section data-testid="expansion-widget-config-editor">
|
|
2375
|
+
<praxis-expansion-config-editor
|
|
2376
|
+
#expansionEditor
|
|
2377
|
+
[config]="config"
|
|
2378
|
+
[expansionId]="expansionId"
|
|
2379
|
+
/>
|
|
2380
|
+
</section>
|
|
2381
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PraxisExpansionConfigEditor, selector: "praxis-expansion-config-editor", inputs: ["config", "expansionId"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2382
|
+
}
|
|
2383
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisExpansionWidgetConfigEditor, decorators: [{
|
|
2384
|
+
type: Component,
|
|
2385
|
+
args: [{
|
|
2386
|
+
selector: 'praxis-expansion-widget-config-editor',
|
|
2387
|
+
standalone: true,
|
|
2388
|
+
imports: [CommonModule, PraxisExpansionConfigEditor],
|
|
2389
|
+
template: `
|
|
2390
|
+
<section data-testid="expansion-widget-config-editor">
|
|
2391
|
+
<praxis-expansion-config-editor
|
|
2392
|
+
#expansionEditor
|
|
2393
|
+
[config]="config"
|
|
2394
|
+
[expansionId]="expansionId"
|
|
2395
|
+
/>
|
|
2396
|
+
</section>
|
|
2397
|
+
`,
|
|
2398
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2399
|
+
}]
|
|
2400
|
+
}], propDecorators: { inputs: [{
|
|
2401
|
+
type: Input
|
|
2402
|
+
}], widgetKey: [{
|
|
2403
|
+
type: Input
|
|
2404
|
+
}], expansionEditor: [{
|
|
2405
|
+
type: ViewChild,
|
|
2406
|
+
args: ['expansionEditor']
|
|
2407
|
+
}] } });
|
|
2408
|
+
|
|
1609
2409
|
const PRAXIS_EXPANSION_PORTS = [
|
|
1610
2410
|
{
|
|
1611
2411
|
id: 'config',
|
|
@@ -1642,6 +2442,14 @@ const PRAXIS_EXPANSION_COMPONENT_METADATA = {
|
|
|
1642
2442
|
friendlyName: 'Praxis Expansion Panel',
|
|
1643
2443
|
description: 'Acordeão/Painéis de expansão configuráveis por metadata, com aparência e tokens M3.',
|
|
1644
2444
|
icon: 'unfold_more',
|
|
2445
|
+
authoringManifestRef: {
|
|
2446
|
+
componentId: 'praxis-expansion',
|
|
2447
|
+
source: 'PRAXIS_EXPANSION_AUTHORING_MANIFEST',
|
|
2448
|
+
},
|
|
2449
|
+
configEditor: {
|
|
2450
|
+
component: PraxisExpansionWidgetConfigEditor,
|
|
2451
|
+
title: 'Configure expansion',
|
|
2452
|
+
},
|
|
1645
2453
|
inputs: [
|
|
1646
2454
|
{ name: 'config', type: 'ExpansionMetadata', label: 'Configuração', description: 'Configuração JSON (acordeão, aparência e painéis)' },
|
|
1647
2455
|
{ name: 'expansionId', type: 'string', label: 'ID', description: 'Identificador para persistência (obrigatório)' },
|
|
@@ -2049,4 +2857,4 @@ const PRAXIS_EXPANSION_AUTHORING_MANIFEST = {
|
|
|
2049
2857
|
* Generated bundle index. Do not edit.
|
|
2050
2858
|
*/
|
|
2051
2859
|
|
|
2052
|
-
export { EXPANSION_AI_CAPABILITIES, PRAXIS_EXPANSION_AUTHORING_MANIFEST, PRAXIS_EXPANSION_COMPONENT_METADATA, PraxisExpansion, PraxisExpansionConfigEditor, providePraxisExpansionDefaults, providePraxisExpansionMetadata };
|
|
2860
|
+
export { EXPANSION_AI_CAPABILITIES, PRAXIS_EXPANSION_AUTHORING_MANIFEST, PRAXIS_EXPANSION_COMPONENT_METADATA, PraxisExpansion, PraxisExpansionConfigEditor, PraxisExpansionWidgetConfigEditor, providePraxisExpansionDefaults, providePraxisExpansionMetadata };
|
package/index.d.ts
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { OnChanges, OnInit, EventEmitter, QueryList, SimpleChanges, Provider } from '@angular/core';
|
|
2
|
+
import { OnChanges, OnDestroy, OnInit, EventEmitter, QueryList, SimpleChanges, AfterViewInit, Provider } from '@angular/core';
|
|
3
3
|
import { FormGroup } from '@angular/forms';
|
|
4
4
|
import { MatAccordionDisplayMode, MatAccordionTogglePosition, MatExpansionPanelDefaultOptions, MatAccordion, MatExpansionPanel } from '@angular/material/expansion';
|
|
5
|
-
import { AiCapability, FieldMetadata, WidgetDefinition, WidgetEventEnvelope, ComponentDocMeta, AiCapabilityCategory, AiValueKind, AiCapabilityCatalog, ComponentAuthoringManifest } from '@praxisui/core';
|
|
5
|
+
import { AiCapability, FieldMetadata, WidgetDefinition, WidgetEventEnvelope, SettingsValueProvider as SettingsValueProvider$1, ComponentDocMeta, AiCapabilityCategory, AiValueKind, AiCapabilityCatalog, ComponentAuthoringManifest } from '@praxisui/core';
|
|
6
6
|
import { BehaviorSubject } from 'rxjs';
|
|
7
|
-
import { BaseAiAdapter, PatchResult } from '@praxisui/ai';
|
|
7
|
+
import { BaseAiAdapter, PatchResult, PraxisAssistantTurnViewState, PraxisAssistantShellLayout, PraxisAssistantShellLabels, PraxisAssistantSessionSnapshot, PraxisAssistantShellQuickReply, PraxisAssistantShellMessage, PraxisAssistantShellContextItem } from '@praxisui/ai';
|
|
8
8
|
import { SettingsValueProvider } from '@praxisui/settings-panel';
|
|
9
9
|
|
|
10
10
|
declare class ExpansionAiAdapter extends BaseAiAdapter<ExpansionMetadata> {
|
|
11
11
|
private expansion;
|
|
12
|
+
componentId: string;
|
|
13
|
+
componentType: string;
|
|
12
14
|
componentName: string;
|
|
13
15
|
constructor(expansion: PraxisExpansion);
|
|
14
16
|
getCurrentConfig(): ExpansionMetadata;
|
|
15
17
|
getCapabilities(): AiCapability[];
|
|
16
18
|
getRuntimeState(): Record<string, any>;
|
|
19
|
+
getDataProfile(): Record<string, any>;
|
|
20
|
+
getSchemaFields(): Array<Record<string, any>>;
|
|
21
|
+
getAuthoringContext(): Record<string, any>;
|
|
17
22
|
createSnapshot(): ExpansionMetadata;
|
|
18
23
|
restoreSnapshot(snapshot: ExpansionMetadata): Promise<void>;
|
|
19
24
|
applyPatch(patch: Partial<ExpansionMetadata>): Promise<PatchResult>;
|
|
@@ -103,7 +108,7 @@ interface PanelMetadata {
|
|
|
103
108
|
action?: string;
|
|
104
109
|
}>;
|
|
105
110
|
}
|
|
106
|
-
declare class PraxisExpansion implements OnChanges, OnInit {
|
|
111
|
+
declare class PraxisExpansion implements OnChanges, OnDestroy, OnInit {
|
|
107
112
|
config?: ExpansionMetadata | null;
|
|
108
113
|
expansionId: string;
|
|
109
114
|
componentInstanceId?: string;
|
|
@@ -144,9 +149,20 @@ declare class PraxisExpansion implements OnChanges, OnInit {
|
|
|
144
149
|
private readonly dynamicForm;
|
|
145
150
|
private readonly componentKeys;
|
|
146
151
|
private readonly route;
|
|
152
|
+
private readonly aiApi;
|
|
153
|
+
private readonly assistantSessions;
|
|
154
|
+
private readonly aiTurnOrchestrator;
|
|
155
|
+
private readonly aiAssistantSessionEffect;
|
|
147
156
|
private warnedMissingId;
|
|
148
157
|
private readonly panelForms;
|
|
149
158
|
aiAdapter: ExpansionAiAdapter;
|
|
159
|
+
aiAssistantOpen: boolean;
|
|
160
|
+
aiAssistantPrompt: string;
|
|
161
|
+
aiAssistantViewState: PraxisAssistantTurnViewState | null;
|
|
162
|
+
aiAssistantLayout: PraxisAssistantShellLayout;
|
|
163
|
+
readonly aiAssistantLabels: Partial<PraxisAssistantShellLabels>;
|
|
164
|
+
private aiAssistantController;
|
|
165
|
+
private aiAssistantStateSubscription;
|
|
150
166
|
accordionRef?: MatAccordion;
|
|
151
167
|
panels?: QueryList<MatExpansionPanel>;
|
|
152
168
|
injectedDefaults: MatExpansionPanelDefaultOptions | null;
|
|
@@ -154,6 +170,7 @@ declare class PraxisExpansion implements OnChanges, OnInit {
|
|
|
154
170
|
single: () => PanelMetadata | null;
|
|
155
171
|
ngOnInit(): void;
|
|
156
172
|
ngOnChanges(changes: SimpleChanges): void;
|
|
173
|
+
ngOnDestroy(): void;
|
|
157
174
|
styleCss(): string | null;
|
|
158
175
|
emitOpened(p: PanelMetadata, index: number): void;
|
|
159
176
|
emitClosed(p: PanelMetadata, index: number): void;
|
|
@@ -174,6 +191,32 @@ declare class PraxisExpansion implements OnChanges, OnInit {
|
|
|
174
191
|
panelDomId(p: PanelMetadata | null, index: number): string;
|
|
175
192
|
panelFormFor(panel: PanelMetadata, index: number): FormGroup<Record<string, any>>;
|
|
176
193
|
applyConfigFromAdapter(next: ExpansionMetadata): void;
|
|
194
|
+
openAiAssistant(): void;
|
|
195
|
+
openAiAssistantFromSession(session: PraxisAssistantSessionSnapshot): void;
|
|
196
|
+
closeAiAssistant(): void;
|
|
197
|
+
onAiAssistantPromptChange(prompt: string): void;
|
|
198
|
+
onAiAssistantSubmit(prompt: string): void;
|
|
199
|
+
onAiAssistantApply(): void;
|
|
200
|
+
onAiAssistantRetry(): void;
|
|
201
|
+
onAiAssistantCancel(): void;
|
|
202
|
+
onAiAssistantQuickReply(reply: PraxisAssistantShellQuickReply): void;
|
|
203
|
+
onAiAssistantEditMessage(message: PraxisAssistantShellMessage): void;
|
|
204
|
+
onAiAssistantResendMessage(message: PraxisAssistantShellMessage): void;
|
|
205
|
+
onAiAssistantLayoutChange(layout: PraxisAssistantShellLayout): void;
|
|
206
|
+
buildAiAssistantContextItems(): PraxisAssistantShellContextItem[];
|
|
207
|
+
private initializeAiAssistantController;
|
|
208
|
+
private buildAiAssistantContextSnapshot;
|
|
209
|
+
private syncAiAssistantSession;
|
|
210
|
+
private hasAiAssistantSessionState;
|
|
211
|
+
private resolveAiAssistantSessionId;
|
|
212
|
+
private resolveAiAssistantOwnerId;
|
|
213
|
+
private safeAiAssistantExpansionId;
|
|
214
|
+
private resolveAiAssistantRouteKey;
|
|
215
|
+
private resolveAiAssistantSummary;
|
|
216
|
+
private resolveAiAssistantBadge;
|
|
217
|
+
private resolveAiAssistantIcon;
|
|
218
|
+
private collectAiAssistantPanelNames;
|
|
219
|
+
private collectAiAssistantCounts;
|
|
177
220
|
openEditor(): void;
|
|
178
221
|
private storageKey;
|
|
179
222
|
private persistConfig;
|
|
@@ -216,6 +259,35 @@ declare class PraxisExpansionConfigEditor implements SettingsValueProvider, OnCh
|
|
|
216
259
|
static ɵcmp: i0.ɵɵComponentDeclaration<PraxisExpansionConfigEditor, "praxis-expansion-config-editor", never, { "config": { "alias": "config"; "required": false; }; "expansionId": { "alias": "expansionId"; "required": false; }; }, {}, never, never, true, never>;
|
|
217
260
|
}
|
|
218
261
|
|
|
262
|
+
interface PraxisExpansionWidgetEditorInputs {
|
|
263
|
+
config?: ExpansionMetadata | null;
|
|
264
|
+
expansionId?: string;
|
|
265
|
+
[key: string]: unknown;
|
|
266
|
+
}
|
|
267
|
+
interface PraxisExpansionWidgetEditorValue {
|
|
268
|
+
inputs: PraxisExpansionWidgetEditorInputs;
|
|
269
|
+
}
|
|
270
|
+
declare class PraxisExpansionWidgetConfigEditor implements SettingsValueProvider$1, AfterViewInit, OnDestroy {
|
|
271
|
+
inputs: PraxisExpansionWidgetEditorInputs | null;
|
|
272
|
+
widgetKey?: string;
|
|
273
|
+
expansionEditor?: PraxisExpansionConfigEditor;
|
|
274
|
+
readonly isDirty$: BehaviorSubject<boolean>;
|
|
275
|
+
readonly isValid$: BehaviorSubject<boolean>;
|
|
276
|
+
readonly isBusy$: BehaviorSubject<boolean>;
|
|
277
|
+
private readonly subscription;
|
|
278
|
+
private readonly emptyConfig;
|
|
279
|
+
get config(): ExpansionMetadata;
|
|
280
|
+
get expansionId(): string | undefined;
|
|
281
|
+
ngAfterViewInit(): void;
|
|
282
|
+
ngOnDestroy(): void;
|
|
283
|
+
getSettingsValue(): PraxisExpansionWidgetEditorValue;
|
|
284
|
+
onSave(): PraxisExpansionWidgetEditorValue;
|
|
285
|
+
reset(): void;
|
|
286
|
+
private buildValue;
|
|
287
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PraxisExpansionWidgetConfigEditor, never>;
|
|
288
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PraxisExpansionWidgetConfigEditor, "praxis-expansion-widget-config-editor", never, { "inputs": { "alias": "inputs"; "required": false; }; "widgetKey": { "alias": "widgetKey"; "required": false; }; }, {}, never, never, true, never>;
|
|
289
|
+
}
|
|
290
|
+
|
|
219
291
|
declare const PRAXIS_EXPANSION_COMPONENT_METADATA: ComponentDocMeta;
|
|
220
292
|
declare function providePraxisExpansionMetadata(): Provider;
|
|
221
293
|
declare function providePraxisExpansionDefaults(opts: MatExpansionPanelDefaultOptions): Provider;
|
|
@@ -245,5 +317,5 @@ declare const EXPANSION_AI_CAPABILITIES: CapabilityCatalog;
|
|
|
245
317
|
|
|
246
318
|
declare const PRAXIS_EXPANSION_AUTHORING_MANIFEST: ComponentAuthoringManifest;
|
|
247
319
|
|
|
248
|
-
export { EXPANSION_AI_CAPABILITIES, PRAXIS_EXPANSION_AUTHORING_MANIFEST, PRAXIS_EXPANSION_COMPONENT_METADATA, PraxisExpansion, PraxisExpansionConfigEditor, providePraxisExpansionDefaults, providePraxisExpansionMetadata };
|
|
249
|
-
export type { Capability, CapabilityCatalog, CapabilityCategory, ExpansionAppearanceContainer, ExpansionAppearanceHeader, ExpansionAppearanceStates, ExpansionMetadata, PanelMetadata, ValueKind };
|
|
320
|
+
export { EXPANSION_AI_CAPABILITIES, PRAXIS_EXPANSION_AUTHORING_MANIFEST, PRAXIS_EXPANSION_COMPONENT_METADATA, PraxisExpansion, PraxisExpansionConfigEditor, PraxisExpansionWidgetConfigEditor, providePraxisExpansionDefaults, providePraxisExpansionMetadata };
|
|
321
|
+
export type { Capability, CapabilityCatalog, CapabilityCategory, ExpansionAppearanceContainer, ExpansionAppearanceHeader, ExpansionAppearanceStates, ExpansionMetadata, PanelMetadata, PraxisExpansionWidgetEditorInputs, PraxisExpansionWidgetEditorValue, ValueKind };
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/expansion",
|
|
3
|
-
"version": "8.0.0-beta.
|
|
3
|
+
"version": "8.0.0-beta.21",
|
|
4
4
|
"description": "Expansion panel (accordion) components for Praxis UI with metadata configuration and editor integration.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^20.0.0",
|
|
7
7
|
"@angular/core": "^20.0.0",
|
|
8
8
|
"@angular/material": "^20.0.0",
|
|
9
9
|
"@angular/cdk": "^20.0.0",
|
|
10
|
-
"@praxisui/core": "^8.0.0-beta.
|
|
11
|
-
"@praxisui/dynamic-fields": "^8.0.0-beta.
|
|
12
|
-
"@praxisui/settings-panel": "^8.0.0-beta.
|
|
10
|
+
"@praxisui/core": "^8.0.0-beta.21",
|
|
11
|
+
"@praxisui/dynamic-fields": "^8.0.0-beta.21",
|
|
12
|
+
"@praxisui/settings-panel": "^8.0.0-beta.21",
|
|
13
13
|
"@angular/forms": "^20.0.0",
|
|
14
14
|
"@angular/router": "^20.0.0",
|
|
15
|
-
"@praxisui/ai": "^8.0.0-beta.
|
|
15
|
+
"@praxisui/ai": "^8.0.0-beta.21",
|
|
16
16
|
"rxjs": "~7.8.0"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|