@praxisui/tabs 8.0.0-beta.20 → 8.0.0-beta.22
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 +9 -0
- package/fesm2022/praxisui-tabs.mjs +929 -14
- package/index.d.ts +87 -6
- package/package.json +5 -5
- package/src/lib/praxis-tabs.json-api.md +5 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Inject, Component, EventEmitter, signal, Output,
|
|
2
|
+
import { inject, Input, Inject, Component, ChangeDetectorRef, effect, EventEmitter, signal, Output, ChangeDetectionStrategy, ViewChild, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import { ActivatedRoute } from '@angular/router';
|
|
4
4
|
import * as i1$1 from '@angular/common';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
@@ -25,11 +25,11 @@ import * as i9 from '@angular/material/slide-toggle';
|
|
|
25
25
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
26
26
|
import * as i10 from '@angular/cdk/drag-drop';
|
|
27
27
|
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
|
|
28
|
-
import { BehaviorSubject, Subject } from 'rxjs';
|
|
28
|
+
import { BehaviorSubject, firstValueFrom, Subject, Subscription } from 'rxjs';
|
|
29
29
|
import { produce } from 'immer';
|
|
30
30
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
31
31
|
import { take, takeUntil } from 'rxjs/operators';
|
|
32
|
-
import { BaseAiAdapter,
|
|
32
|
+
import { BaseAiAdapter, AiBackendApiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, createPraxisAssistantViewportLayout, PraxisAiAssistantShellComponent } from '@praxisui/ai';
|
|
33
33
|
|
|
34
34
|
const DOCUMENT_KIND = 'praxis.tabs.editor';
|
|
35
35
|
const DOCUMENT_VERSION = 1;
|
|
@@ -533,6 +533,12 @@ function providePraxisTabsI18n() {
|
|
|
533
533
|
class PraxisTabsConfigEditor {
|
|
534
534
|
registry;
|
|
535
535
|
i18n = inject(PraxisI18nService);
|
|
536
|
+
set document(value) {
|
|
537
|
+
if (!value) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
this.initializeDocument(value);
|
|
541
|
+
}
|
|
536
542
|
primaryMode = 'group';
|
|
537
543
|
editedDocument;
|
|
538
544
|
editedConfig;
|
|
@@ -612,12 +618,18 @@ class PraxisTabsConfigEditor {
|
|
|
612
618
|
this.initialDocument = structuredClone(incomingDocument);
|
|
613
619
|
this.editedDocument = structuredClone(incomingDocument);
|
|
614
620
|
this.editedConfig = this.editedDocument.config;
|
|
615
|
-
this.
|
|
616
|
-
this.
|
|
621
|
+
this.initializeDocument(incomingDocument);
|
|
622
|
+
this.componentOptions = this.registry.getAll().map((m) => ({ id: m.id, friendlyName: m.friendlyName }));
|
|
623
|
+
}
|
|
624
|
+
initializeDocument(document) {
|
|
625
|
+
const normalized = normalizeTabsAuthoringDocument(document);
|
|
626
|
+
this.initialDocument = structuredClone(normalized);
|
|
627
|
+
this.syncEditorStateFromDocument(normalized);
|
|
617
628
|
this.jsonText = this.stringify(this.editedDocument);
|
|
629
|
+
this.isValid = true;
|
|
630
|
+
this.errorMsg = '';
|
|
618
631
|
this.updateDirty();
|
|
619
632
|
this.refreshDiagnostics();
|
|
620
|
-
this.componentOptions = this.registry.getAll().map((m) => ({ id: m.id, friendlyName: m.friendlyName }));
|
|
621
633
|
}
|
|
622
634
|
inferPrimaryMode(config) {
|
|
623
635
|
return config?.nav?.links?.length ? 'nav' : 'group';
|
|
@@ -955,7 +967,7 @@ class PraxisTabsConfigEditor {
|
|
|
955
967
|
this.onAppearanceChange();
|
|
956
968
|
}
|
|
957
969
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabsConfigEditor, deps: [{ token: SETTINGS_PANEL_DATA }, { token: i1.ComponentMetadataRegistry }], target: i0.ɵɵFactoryTarget.Component });
|
|
958
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabsConfigEditor, isStandalone: true, selector: "praxis-tabs-config-editor", providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], ngImport: i0, template: `
|
|
970
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabsConfigEditor, isStandalone: true, selector: "praxis-tabs-config-editor", inputs: { document: "document" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], ngImport: i0, template: `
|
|
959
971
|
<div class="editor-shell">
|
|
960
972
|
<div class="editor-topbar">
|
|
961
973
|
<mat-form-field appearance="outline" class="editor-mode-field">
|
|
@@ -1971,7 +1983,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1971
1983
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1972
1984
|
type: Inject,
|
|
1973
1985
|
args: [SETTINGS_PANEL_DATA]
|
|
1974
|
-
}] }, { type: i1.ComponentMetadataRegistry }]
|
|
1986
|
+
}] }, { type: i1.ComponentMetadataRegistry }], propDecorators: { document: [{
|
|
1987
|
+
type: Input
|
|
1988
|
+
}] } });
|
|
1975
1989
|
|
|
1976
1990
|
class TabsQuickSetupComponent {
|
|
1977
1991
|
i18n = inject(PraxisI18nService);
|
|
@@ -2282,6 +2296,8 @@ const TABS_AI_CAPABILITIES = {
|
|
|
2282
2296
|
class TabsAiAdapter extends BaseAiAdapter {
|
|
2283
2297
|
tabs;
|
|
2284
2298
|
componentName = 'Praxis Tabs';
|
|
2299
|
+
componentId = 'praxis-tabs';
|
|
2300
|
+
componentType = 'tabs';
|
|
2285
2301
|
constructor(tabs) {
|
|
2286
2302
|
super();
|
|
2287
2303
|
this.tabs = tabs;
|
|
@@ -2292,6 +2308,49 @@ class TabsAiAdapter extends BaseAiAdapter {
|
|
|
2292
2308
|
getCapabilities() {
|
|
2293
2309
|
return TABS_AI_CAPABILITIES.capabilities;
|
|
2294
2310
|
}
|
|
2311
|
+
getDataProfile() {
|
|
2312
|
+
const cfg = this.tabs.config;
|
|
2313
|
+
return {
|
|
2314
|
+
mode: cfg?.nav?.links?.length ? 'nav' : 'group',
|
|
2315
|
+
tabCount: cfg?.tabs?.length ?? 0,
|
|
2316
|
+
linkCount: cfg?.nav?.links?.length ?? 0,
|
|
2317
|
+
widgetCount: [
|
|
2318
|
+
...(cfg?.tabs ?? []).flatMap((tab) => tab.widgets ?? []),
|
|
2319
|
+
...(cfg?.nav?.links ?? []).flatMap((link) => link.widgets ?? []),
|
|
2320
|
+
].length,
|
|
2321
|
+
fieldCount: [
|
|
2322
|
+
...(cfg?.tabs ?? []).flatMap((tab) => tab.content ?? []),
|
|
2323
|
+
...(cfg?.nav?.links ?? []).flatMap((link) => link.content ?? []),
|
|
2324
|
+
].length,
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
getSchemaFields() {
|
|
2328
|
+
const cfg = this.tabs.config;
|
|
2329
|
+
return [
|
|
2330
|
+
...(cfg?.tabs ?? []).flatMap((tab) => tab.content ?? []),
|
|
2331
|
+
...(cfg?.nav?.links ?? []).flatMap((link) => link.content ?? []),
|
|
2332
|
+
]
|
|
2333
|
+
.map((field) => field?.name || field?.key || field?.id)
|
|
2334
|
+
.filter((name) => typeof name === 'string' && !!name.trim())
|
|
2335
|
+
.map((name) => ({ name }));
|
|
2336
|
+
}
|
|
2337
|
+
getAuthoringContext() {
|
|
2338
|
+
return {
|
|
2339
|
+
authoringManifestRef: {
|
|
2340
|
+
componentId: 'praxis-tabs',
|
|
2341
|
+
source: 'PRAXIS_TABS_AUTHORING_MANIFEST',
|
|
2342
|
+
},
|
|
2343
|
+
runtimeAuthoringPolicy: {
|
|
2344
|
+
mode: 'agentic-authoring',
|
|
2345
|
+
enableCustomization: !!this.tabs.enableCustomization,
|
|
2346
|
+
canApplyLocalPatch: false,
|
|
2347
|
+
reason: 'praxis-tabs ainda exige componentEditPlan manifest-backed antes de aplicar patch local pelo copiloto.',
|
|
2348
|
+
},
|
|
2349
|
+
domainCatalog: {
|
|
2350
|
+
recommendedAuthoringFlow: 'component_authoring',
|
|
2351
|
+
},
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2295
2354
|
getRuntimeState() {
|
|
2296
2355
|
const cfg = this.tabs.config;
|
|
2297
2356
|
return {
|
|
@@ -2377,6 +2436,340 @@ class TabsAiAdapter extends BaseAiAdapter {
|
|
|
2377
2436
|
}
|
|
2378
2437
|
}
|
|
2379
2438
|
|
|
2439
|
+
class TabsAgenticAuthoringTurnFlow {
|
|
2440
|
+
adapter;
|
|
2441
|
+
aiApi;
|
|
2442
|
+
mode = 'agentic-authoring';
|
|
2443
|
+
constructor(adapter, aiApi) {
|
|
2444
|
+
this.adapter = adapter;
|
|
2445
|
+
this.aiApi = aiApi;
|
|
2446
|
+
}
|
|
2447
|
+
async submit(request) {
|
|
2448
|
+
const prompt = (request.prompt ?? '').trim();
|
|
2449
|
+
if (!prompt) {
|
|
2450
|
+
return {
|
|
2451
|
+
state: 'listening',
|
|
2452
|
+
phase: 'capture',
|
|
2453
|
+
statusText: '',
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
const componentId = this.adapter.componentId || request.componentId || 'praxis-tabs';
|
|
2457
|
+
const componentType = this.adapter.componentType || request.componentType || 'tabs';
|
|
2458
|
+
const currentState = this.toAiJsonObject(this.adapter.getCurrentConfig());
|
|
2459
|
+
const dataProfile = this.optionalJsonObject(this.adapter.getDataProfile?.());
|
|
2460
|
+
const runtimeState = this.optionalJsonObject(this.adapter.getRuntimeState?.());
|
|
2461
|
+
const schemaFields = this.adapter.getSchemaFields?.()
|
|
2462
|
+
?.map((field) => this.toAiJsonObject(field))
|
|
2463
|
+
.filter((field) => Object.keys(field).length > 0);
|
|
2464
|
+
const contextHints = this.optionalJsonObject(this.adapter.getAuthoringContext?.());
|
|
2465
|
+
if (this.shouldRouteToGovernedDecision(prompt, contextHints)) {
|
|
2466
|
+
return this.toGovernedDecisionHandoff(prompt, request);
|
|
2467
|
+
}
|
|
2468
|
+
const response = await firstValueFrom(this.aiApi.getPatch({
|
|
2469
|
+
componentId,
|
|
2470
|
+
componentType,
|
|
2471
|
+
userPrompt: prompt,
|
|
2472
|
+
sessionId: request.sessionId,
|
|
2473
|
+
clientTurnId: request.clientTurnId,
|
|
2474
|
+
messages: this.toChatMessages(request.messages, prompt),
|
|
2475
|
+
currentState,
|
|
2476
|
+
currentStateDigest: this.buildCurrentStateDigest(dataProfile),
|
|
2477
|
+
uiContextRef: {
|
|
2478
|
+
componentId,
|
|
2479
|
+
componentType,
|
|
2480
|
+
},
|
|
2481
|
+
...(dataProfile ? { dataProfile } : {}),
|
|
2482
|
+
...(runtimeState ? { runtimeState } : {}),
|
|
2483
|
+
...(schemaFields?.length ? { schemaFields } : {}),
|
|
2484
|
+
...(contextHints ? { contextHints } : {}),
|
|
2485
|
+
}));
|
|
2486
|
+
return this.toTurnResult(this.compileAdapterResponse(response), request);
|
|
2487
|
+
}
|
|
2488
|
+
async apply(_request) {
|
|
2489
|
+
return {
|
|
2490
|
+
state: 'error',
|
|
2491
|
+
phase: 'apply',
|
|
2492
|
+
assistantMessage: 'As abas ainda exigem componentEditPlan validado pelo manifesto antes de aplicar mudancas locais.',
|
|
2493
|
+
errorText: 'Aplicacao local bloqueada ate existir compilacao manifest-backed para praxis-tabs.',
|
|
2494
|
+
canApply: false,
|
|
2495
|
+
pendingPatch: null,
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
cancel() {
|
|
2499
|
+
return Promise.resolve({
|
|
2500
|
+
state: 'listening',
|
|
2501
|
+
phase: 'capture',
|
|
2502
|
+
assistantMessage: 'Solicitacao cancelada.',
|
|
2503
|
+
statusText: '',
|
|
2504
|
+
canApply: false,
|
|
2505
|
+
pendingPatch: null,
|
|
2506
|
+
pendingClarification: null,
|
|
2507
|
+
});
|
|
2508
|
+
}
|
|
2509
|
+
retry(request) {
|
|
2510
|
+
const lastPrompt = [...(request.messages ?? [])].reverse()
|
|
2511
|
+
.find((message) => message.role === 'user')?.text;
|
|
2512
|
+
return this.submit({
|
|
2513
|
+
...request,
|
|
2514
|
+
prompt: lastPrompt ?? request.prompt,
|
|
2515
|
+
action: { kind: 'retry' },
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
toTurnResult(response, request) {
|
|
2519
|
+
if (!response) {
|
|
2520
|
+
return {
|
|
2521
|
+
state: 'error',
|
|
2522
|
+
phase: 'capture',
|
|
2523
|
+
assistantMessage: 'Resposta vazia da IA.',
|
|
2524
|
+
errorText: 'Resposta vazia da IA.',
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
if (response.type === 'clarification') {
|
|
2528
|
+
return {
|
|
2529
|
+
state: 'clarification',
|
|
2530
|
+
phase: 'clarify',
|
|
2531
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
2532
|
+
assistantMessage: response.message || 'Preciso de mais detalhes para continuar.',
|
|
2533
|
+
clarificationQuestions: this.toClarificationQuestions(response),
|
|
2534
|
+
quickReplies: this.toQuickReplies(response),
|
|
2535
|
+
canApply: false,
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
if (response.type === 'info') {
|
|
2539
|
+
const message = response.message || response.explanation || 'Informacao gerada.';
|
|
2540
|
+
return {
|
|
2541
|
+
state: 'success',
|
|
2542
|
+
phase: 'summarize',
|
|
2543
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
2544
|
+
assistantMessage: message,
|
|
2545
|
+
statusText: message,
|
|
2546
|
+
canApply: false,
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
if (response.type === 'error') {
|
|
2550
|
+
const message = response.message || 'Falha ao gerar alteracao de abas.';
|
|
2551
|
+
return {
|
|
2552
|
+
state: 'error',
|
|
2553
|
+
phase: 'capture',
|
|
2554
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
2555
|
+
assistantMessage: message,
|
|
2556
|
+
errorText: message,
|
|
2557
|
+
diagnostics: response.warnings?.length ? { warnings: response.warnings } : undefined,
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
if (response.patch && Object.keys(response.patch).length > 0) {
|
|
2561
|
+
return {
|
|
2562
|
+
state: 'error',
|
|
2563
|
+
phase: 'review',
|
|
2564
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
2565
|
+
assistantMessage: 'As abas rejeitaram patch livre. Gere um componentEditPlan validado pelo PRAXIS_TABS_AUTHORING_MANIFEST antes de propor alteracao local.',
|
|
2566
|
+
errorText: 'Patch livre de abas rejeitado.',
|
|
2567
|
+
canApply: false,
|
|
2568
|
+
pendingPatch: null,
|
|
2569
|
+
diagnostics: {
|
|
2570
|
+
warnings: [
|
|
2571
|
+
'free-tabs-patch-rejected',
|
|
2572
|
+
'Use componentEditPlan validado contra PRAXIS_TABS_AUTHORING_MANIFEST.',
|
|
2573
|
+
],
|
|
2574
|
+
},
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
return {
|
|
2578
|
+
state: 'success',
|
|
2579
|
+
phase: 'summarize',
|
|
2580
|
+
sessionId: response.sessionId ?? request.sessionId,
|
|
2581
|
+
assistantMessage: response.message || response.explanation || 'Nenhuma alteracao necessaria.',
|
|
2582
|
+
statusText: response.message || response.explanation || 'Nenhuma alteracao necessaria.',
|
|
2583
|
+
canApply: false,
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
compileAdapterResponse(response) {
|
|
2587
|
+
const compiled = this.adapter.compileAiResponse?.(response);
|
|
2588
|
+
if (!compiled) {
|
|
2589
|
+
return response;
|
|
2590
|
+
}
|
|
2591
|
+
if (compiled.type === 'error') {
|
|
2592
|
+
return {
|
|
2593
|
+
type: 'error',
|
|
2594
|
+
message: compiled.message || 'O componentEditPlan das abas nao passou na validacao de capacidades.',
|
|
2595
|
+
warnings: compiled.warnings,
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
const warnings = [
|
|
2599
|
+
...(response.warnings ?? []),
|
|
2600
|
+
...(compiled.warnings ?? []),
|
|
2601
|
+
];
|
|
2602
|
+
return {
|
|
2603
|
+
...response,
|
|
2604
|
+
...compiled,
|
|
2605
|
+
patch: compiled.patch,
|
|
2606
|
+
warnings: warnings.length ? warnings : undefined,
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
toChatMessages(messages, prompt) {
|
|
2610
|
+
const supported = (messages ?? [])
|
|
2611
|
+
.filter((message) => message.role === 'user' || message.role === 'assistant' || message.role === 'system')
|
|
2612
|
+
.map((message) => ({
|
|
2613
|
+
role: message.role,
|
|
2614
|
+
content: message.text,
|
|
2615
|
+
}))
|
|
2616
|
+
.filter((message) => message.content.trim().length > 0);
|
|
2617
|
+
return supported.length ? supported : [{ role: 'user', content: prompt }];
|
|
2618
|
+
}
|
|
2619
|
+
toClarificationQuestions(response) {
|
|
2620
|
+
const labels = response.questions?.length
|
|
2621
|
+
? response.questions
|
|
2622
|
+
: response.message
|
|
2623
|
+
? [response.message]
|
|
2624
|
+
: ['Qual ajuste voce quer aplicar nas abas?'];
|
|
2625
|
+
const options = this.toQuickReplies(response).map((reply) => ({
|
|
2626
|
+
id: reply.id,
|
|
2627
|
+
label: reply.label,
|
|
2628
|
+
value: reply.prompt,
|
|
2629
|
+
}));
|
|
2630
|
+
return labels.map((label, index) => ({
|
|
2631
|
+
id: `tabs-clarification-${index + 1}`,
|
|
2632
|
+
type: options.length ? 'single-choice' : 'text',
|
|
2633
|
+
label,
|
|
2634
|
+
allowCustom: true,
|
|
2635
|
+
options,
|
|
2636
|
+
}));
|
|
2637
|
+
}
|
|
2638
|
+
toQuickReplies(response) {
|
|
2639
|
+
const payloads = response.optionPayloads ?? [];
|
|
2640
|
+
if (payloads.length) {
|
|
2641
|
+
return payloads
|
|
2642
|
+
.map((option, index) => {
|
|
2643
|
+
const label = option.label?.trim() || option.value?.trim() || `Opcao ${index + 1}`;
|
|
2644
|
+
const prompt = option.example?.trim() || option.value?.trim() || label;
|
|
2645
|
+
return {
|
|
2646
|
+
id: `option-${index + 1}`,
|
|
2647
|
+
label,
|
|
2648
|
+
prompt,
|
|
2649
|
+
kind: 'clarification-option',
|
|
2650
|
+
};
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
return (response.options ?? [])
|
|
2654
|
+
.filter((option) => !!option?.trim())
|
|
2655
|
+
.map((option, index) => ({
|
|
2656
|
+
id: `option-${index + 1}`,
|
|
2657
|
+
label: option.trim(),
|
|
2658
|
+
prompt: option.trim(),
|
|
2659
|
+
kind: 'clarification-option',
|
|
2660
|
+
}));
|
|
2661
|
+
}
|
|
2662
|
+
buildCurrentStateDigest(dataProfile) {
|
|
2663
|
+
const tabCount = typeof dataProfile?.['tabCount'] === 'number' ? dataProfile['tabCount'] : undefined;
|
|
2664
|
+
const linkCount = typeof dataProfile?.['linkCount'] === 'number' ? dataProfile['linkCount'] : undefined;
|
|
2665
|
+
const rowCount = (tabCount ?? 0) + (linkCount ?? 0);
|
|
2666
|
+
return rowCount > 0 ? { rowCount } : {};
|
|
2667
|
+
}
|
|
2668
|
+
shouldRouteToGovernedDecision(prompt, contextHints) {
|
|
2669
|
+
const normalized = prompt.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
|
2670
|
+
const recommendedFlow = this.toRecord(contextHints?.['domainCatalog'])?.['recommendedAuthoringFlow'];
|
|
2671
|
+
if (recommendedFlow === 'shared_rule_authoring')
|
|
2672
|
+
return true;
|
|
2673
|
+
return [
|
|
2674
|
+
'regra',
|
|
2675
|
+
'politica',
|
|
2676
|
+
'policy',
|
|
2677
|
+
'compliance',
|
|
2678
|
+
'lgpd',
|
|
2679
|
+
'privacidade',
|
|
2680
|
+
'aprovacao',
|
|
2681
|
+
'aprovar',
|
|
2682
|
+
'publicar',
|
|
2683
|
+
'materializar',
|
|
2684
|
+
'enforcement',
|
|
2685
|
+
'validacao de negocio',
|
|
2686
|
+
'validar negocio',
|
|
2687
|
+
'elegibilidade',
|
|
2688
|
+
'permissao',
|
|
2689
|
+
'acesso',
|
|
2690
|
+
].some((term) => normalized.includes(term));
|
|
2691
|
+
}
|
|
2692
|
+
toGovernedDecisionHandoff(prompt, request) {
|
|
2693
|
+
const message = 'Esse pedido parece alterar uma decisao de negocio compartilhada. As abas podem ajudar a localizar a experiencia afetada, mas a regra deve seguir pelo fluxo governado de domain-rules antes de qualquer materializacao runtime.';
|
|
2694
|
+
return {
|
|
2695
|
+
state: 'clarification',
|
|
2696
|
+
phase: 'clarify',
|
|
2697
|
+
sessionId: request.sessionId,
|
|
2698
|
+
assistantMessage: message,
|
|
2699
|
+
statusText: 'Handoff governado necessario.',
|
|
2700
|
+
canApply: false,
|
|
2701
|
+
quickReplies: [
|
|
2702
|
+
{
|
|
2703
|
+
id: 'shared-rule-handoff',
|
|
2704
|
+
label: 'Continuar como regra governada',
|
|
2705
|
+
prompt,
|
|
2706
|
+
kind: 'shared-rule-handoff',
|
|
2707
|
+
description: 'Criar intake de domain-rules em vez de aplicar patch local nas abas.',
|
|
2708
|
+
icon: 'rule',
|
|
2709
|
+
tone: 'warning',
|
|
2710
|
+
contextHints: {
|
|
2711
|
+
flowId: 'shared_rule_authoring',
|
|
2712
|
+
source: 'praxis-tabs',
|
|
2713
|
+
recommendedAction: 'domain-rules/intake',
|
|
2714
|
+
},
|
|
2715
|
+
},
|
|
2716
|
+
],
|
|
2717
|
+
clarificationQuestions: [
|
|
2718
|
+
{
|
|
2719
|
+
id: 'tabs-governed-rule-confirmation',
|
|
2720
|
+
type: 'confirm',
|
|
2721
|
+
label: 'Deseja continuar pelo fluxo governado de regras compartilhadas?',
|
|
2722
|
+
description: 'Esse caminho permite intake, simulacao, aprovacao/publicacao, materializacao e validacao de enforcement.',
|
|
2723
|
+
required: true,
|
|
2724
|
+
options: [
|
|
2725
|
+
{
|
|
2726
|
+
id: 'shared-rule-handoff',
|
|
2727
|
+
label: 'Sim, continuar governado',
|
|
2728
|
+
value: prompt,
|
|
2729
|
+
description: 'Nao aplicar como patch local das abas.',
|
|
2730
|
+
contextHints: {
|
|
2731
|
+
flowId: 'shared_rule_authoring',
|
|
2732
|
+
source: 'praxis-tabs',
|
|
2733
|
+
},
|
|
2734
|
+
},
|
|
2735
|
+
],
|
|
2736
|
+
},
|
|
2737
|
+
],
|
|
2738
|
+
diagnostics: {
|
|
2739
|
+
governedDecisionHandoff: {
|
|
2740
|
+
flowId: 'shared_rule_authoring',
|
|
2741
|
+
sourcePrompt: prompt,
|
|
2742
|
+
sourceComponent: 'praxis-tabs',
|
|
2743
|
+
},
|
|
2744
|
+
},
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
optionalJsonObject(value) {
|
|
2748
|
+
if (value === undefined || value === null) {
|
|
2749
|
+
return undefined;
|
|
2750
|
+
}
|
|
2751
|
+
const object = this.toAiJsonObject(value);
|
|
2752
|
+
return Object.keys(object).length ? object : undefined;
|
|
2753
|
+
}
|
|
2754
|
+
toAiJsonObject(value) {
|
|
2755
|
+
const record = this.toRecord(value);
|
|
2756
|
+
if (!record) {
|
|
2757
|
+
return {};
|
|
2758
|
+
}
|
|
2759
|
+
try {
|
|
2760
|
+
return JSON.parse(JSON.stringify(record));
|
|
2761
|
+
}
|
|
2762
|
+
catch {
|
|
2763
|
+
return {};
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
toRecord(value) {
|
|
2767
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
2768
|
+
? value
|
|
2769
|
+
: null;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2380
2773
|
class PraxisTabs {
|
|
2381
2774
|
i18n = inject(PraxisI18nService);
|
|
2382
2775
|
settings = inject(SettingsPanelService);
|
|
@@ -2384,12 +2777,24 @@ class PraxisTabs {
|
|
|
2384
2777
|
snack = inject(MatSnackBar);
|
|
2385
2778
|
componentKeys = inject(ComponentKeyService);
|
|
2386
2779
|
logger = inject(LoggerService);
|
|
2780
|
+
cdr = inject(ChangeDetectorRef);
|
|
2781
|
+
aiApi = inject(AiBackendApiService);
|
|
2782
|
+
assistantSessions = inject(PraxisAssistantSessionRegistryService);
|
|
2783
|
+
aiTurnOrchestrator = inject(PraxisAssistantTurnOrchestratorService);
|
|
2387
2784
|
route = (() => { try {
|
|
2388
2785
|
return inject(ActivatedRoute);
|
|
2389
2786
|
}
|
|
2390
2787
|
catch {
|
|
2391
2788
|
return undefined;
|
|
2392
2789
|
} })();
|
|
2790
|
+
aiAssistantSessionEffect = effect(() => {
|
|
2791
|
+
const session = this.assistantSessions.activeSession();
|
|
2792
|
+
if (!session || session.id !== this.resolveAiAssistantSessionId())
|
|
2793
|
+
return;
|
|
2794
|
+
if (!this.aiAssistantOpen) {
|
|
2795
|
+
this.openAiAssistantFromSession(session);
|
|
2796
|
+
}
|
|
2797
|
+
}, ...(ngDevMode ? [{ debugName: "aiAssistantSessionEffect" }] : []));
|
|
2393
2798
|
warnedMissingId = false;
|
|
2394
2799
|
config = null;
|
|
2395
2800
|
tabsId;
|
|
@@ -2411,6 +2816,21 @@ class PraxisTabs {
|
|
|
2411
2816
|
selectFocusedIndex = new EventEmitter();
|
|
2412
2817
|
widgetEvent = new EventEmitter();
|
|
2413
2818
|
aiAdapter = new TabsAiAdapter(this);
|
|
2819
|
+
aiAssistantOpen = false;
|
|
2820
|
+
aiAssistantPrompt = '';
|
|
2821
|
+
aiAssistantViewState = null;
|
|
2822
|
+
aiAssistantLayout = createPraxisAssistantViewportLayout();
|
|
2823
|
+
aiAssistantLabels = {
|
|
2824
|
+
title: 'Copiloto semantico Praxis',
|
|
2825
|
+
subtitle: 'Converse, revise e governe ajustes das abas.',
|
|
2826
|
+
prompt: 'Mensagem',
|
|
2827
|
+
promptPlaceholder: 'Descreva o ajuste que voce precisa nas abas.',
|
|
2828
|
+
emptyConversation: 'Diga o que voce quer alterar nas abas.',
|
|
2829
|
+
submit: 'Interpretar pedido',
|
|
2830
|
+
apply: 'Aplicar ajuste',
|
|
2831
|
+
};
|
|
2832
|
+
aiAssistantController = null;
|
|
2833
|
+
aiAssistantStateSubscription = null;
|
|
2414
2834
|
// Signals to manage local state for selection in Nav mode and Group mode
|
|
2415
2835
|
currentNavIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentNavIndex" }] : []));
|
|
2416
2836
|
selectedIndexSignal = signal(0, ...(ngDevMode ? [{ debugName: "selectedIndexSignal" }] : []));
|
|
@@ -2443,6 +2863,8 @@ class PraxisTabs {
|
|
|
2443
2863
|
}
|
|
2444
2864
|
}
|
|
2445
2865
|
ngOnDestroy() {
|
|
2866
|
+
this.assistantSessions.removeContextSession(this.buildAiAssistantContextSnapshot().identity);
|
|
2867
|
+
this.aiAssistantStateSubscription?.unsubscribe();
|
|
2446
2868
|
this.destroy$.next();
|
|
2447
2869
|
this.destroy$.complete();
|
|
2448
2870
|
}
|
|
@@ -2681,6 +3103,305 @@ class PraxisTabs {
|
|
|
2681
3103
|
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe(applyDocument);
|
|
2682
3104
|
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe(applyDocument);
|
|
2683
3105
|
}
|
|
3106
|
+
openAiAssistant() {
|
|
3107
|
+
this.initializeAiAssistantController();
|
|
3108
|
+
this.aiAssistantOpen = true;
|
|
3109
|
+
this.aiAssistantController?.setContextItems(this.buildAiAssistantContextItems());
|
|
3110
|
+
this.syncAiAssistantSession('active');
|
|
3111
|
+
this.cdr.markForCheck();
|
|
3112
|
+
}
|
|
3113
|
+
openAiAssistantFromSession(session) {
|
|
3114
|
+
if (session.id !== this.resolveAiAssistantSessionId())
|
|
3115
|
+
return;
|
|
3116
|
+
this.initializeAiAssistantController();
|
|
3117
|
+
this.aiAssistantOpen = true;
|
|
3118
|
+
this.aiAssistantController?.setContextItems(this.buildAiAssistantContextItems());
|
|
3119
|
+
this.syncAiAssistantSession('active');
|
|
3120
|
+
this.cdr.markForCheck();
|
|
3121
|
+
}
|
|
3122
|
+
closeAiAssistant() {
|
|
3123
|
+
this.aiAssistantOpen = false;
|
|
3124
|
+
this.syncAiAssistantSession('minimized');
|
|
3125
|
+
this.cdr.markForCheck();
|
|
3126
|
+
}
|
|
3127
|
+
onAiAssistantPromptChange(prompt) {
|
|
3128
|
+
this.aiAssistantPrompt = prompt;
|
|
3129
|
+
this.syncAiAssistantSession();
|
|
3130
|
+
}
|
|
3131
|
+
onAiAssistantSubmit(prompt) {
|
|
3132
|
+
this.aiAssistantController?.submitPrompt(prompt).subscribe((state) => {
|
|
3133
|
+
this.aiAssistantPrompt = '';
|
|
3134
|
+
this.aiAssistantViewState = state;
|
|
3135
|
+
this.syncAiAssistantSession();
|
|
3136
|
+
this.cdr.markForCheck();
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
onAiAssistantApply() {
|
|
3140
|
+
this.aiAssistantController?.apply().subscribe((state) => {
|
|
3141
|
+
this.aiAssistantViewState = state;
|
|
3142
|
+
this.syncAiAssistantSession();
|
|
3143
|
+
this.cdr.markForCheck();
|
|
3144
|
+
});
|
|
3145
|
+
}
|
|
3146
|
+
onAiAssistantRetry() {
|
|
3147
|
+
this.aiAssistantController?.retry().subscribe((state) => {
|
|
3148
|
+
this.aiAssistantViewState = state;
|
|
3149
|
+
this.syncAiAssistantSession();
|
|
3150
|
+
this.cdr.markForCheck();
|
|
3151
|
+
});
|
|
3152
|
+
}
|
|
3153
|
+
onAiAssistantCancel() {
|
|
3154
|
+
this.aiAssistantController?.cancel().subscribe((state) => {
|
|
3155
|
+
this.aiAssistantPrompt = '';
|
|
3156
|
+
this.aiAssistantViewState = state;
|
|
3157
|
+
this.syncAiAssistantSession();
|
|
3158
|
+
this.cdr.markForCheck();
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
onAiAssistantQuickReply(reply) {
|
|
3162
|
+
const controller = this.aiAssistantController;
|
|
3163
|
+
if (!controller)
|
|
3164
|
+
return;
|
|
3165
|
+
const state = controller.snapshot();
|
|
3166
|
+
const next$ = state.state === 'clarification'
|
|
3167
|
+
? controller.answerClarification(reply.prompt)
|
|
3168
|
+
: controller.submitPrompt(reply.prompt, {
|
|
3169
|
+
kind: reply.kind || 'quick-reply',
|
|
3170
|
+
id: reply.id,
|
|
3171
|
+
value: reply.prompt,
|
|
3172
|
+
});
|
|
3173
|
+
next$.subscribe((nextState) => {
|
|
3174
|
+
this.aiAssistantPrompt = '';
|
|
3175
|
+
this.aiAssistantViewState = nextState;
|
|
3176
|
+
this.syncAiAssistantSession();
|
|
3177
|
+
this.cdr.markForCheck();
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3180
|
+
onAiAssistantEditMessage(message) {
|
|
3181
|
+
this.aiAssistantPrompt = message.text;
|
|
3182
|
+
this.cdr.markForCheck();
|
|
3183
|
+
}
|
|
3184
|
+
onAiAssistantResendMessage(message) {
|
|
3185
|
+
this.aiAssistantController?.resendMessage(message.id).subscribe((state) => {
|
|
3186
|
+
this.aiAssistantPrompt = '';
|
|
3187
|
+
this.aiAssistantViewState = state;
|
|
3188
|
+
this.syncAiAssistantSession();
|
|
3189
|
+
this.cdr.markForCheck();
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
onAiAssistantLayoutChange(layout) {
|
|
3193
|
+
this.aiAssistantLayout = layout;
|
|
3194
|
+
}
|
|
3195
|
+
initializeAiAssistantController() {
|
|
3196
|
+
if (this.aiAssistantController)
|
|
3197
|
+
return;
|
|
3198
|
+
const flow = new TabsAgenticAuthoringTurnFlow(this.aiAdapter, this.aiApi);
|
|
3199
|
+
const controller = this.aiTurnOrchestrator.createController(flow, {
|
|
3200
|
+
componentId: this.aiAdapter.componentId || 'praxis-tabs',
|
|
3201
|
+
componentType: this.aiAdapter.componentType || 'tabs',
|
|
3202
|
+
contextItems: this.buildAiAssistantContextItems(),
|
|
3203
|
+
});
|
|
3204
|
+
this.aiAssistantController = controller;
|
|
3205
|
+
this.aiAssistantViewState = controller.snapshot();
|
|
3206
|
+
this.aiAssistantStateSubscription?.unsubscribe();
|
|
3207
|
+
this.aiAssistantStateSubscription = controller.state$.subscribe((state) => {
|
|
3208
|
+
this.aiAssistantViewState = state;
|
|
3209
|
+
this.syncAiAssistantSession();
|
|
3210
|
+
this.cdr.markForCheck();
|
|
3211
|
+
});
|
|
3212
|
+
this.cdr.markForCheck();
|
|
3213
|
+
}
|
|
3214
|
+
buildAiAssistantContextItems() {
|
|
3215
|
+
const items = [
|
|
3216
|
+
{
|
|
3217
|
+
id: 'component',
|
|
3218
|
+
label: 'Componente',
|
|
3219
|
+
value: 'Abas',
|
|
3220
|
+
kind: 'component',
|
|
3221
|
+
icon: 'tab',
|
|
3222
|
+
},
|
|
3223
|
+
{
|
|
3224
|
+
id: 'mode',
|
|
3225
|
+
label: 'Modo',
|
|
3226
|
+
value: this.isNavMode() ? 'nav' : 'group',
|
|
3227
|
+
kind: 'custom',
|
|
3228
|
+
icon: 'account_tree',
|
|
3229
|
+
},
|
|
3230
|
+
];
|
|
3231
|
+
if (this.tabsId) {
|
|
3232
|
+
items.push({
|
|
3233
|
+
id: 'tabs-id',
|
|
3234
|
+
label: 'Tabs',
|
|
3235
|
+
value: this.tabsId,
|
|
3236
|
+
kind: 'custom',
|
|
3237
|
+
icon: 'tag',
|
|
3238
|
+
});
|
|
3239
|
+
}
|
|
3240
|
+
return items;
|
|
3241
|
+
}
|
|
3242
|
+
buildAiAssistantContextSnapshot() {
|
|
3243
|
+
const fieldNames = this.collectContentFieldNames();
|
|
3244
|
+
const counts = this.collectTabsCounts();
|
|
3245
|
+
return {
|
|
3246
|
+
identity: {
|
|
3247
|
+
sessionId: this.resolveAiAssistantSessionId(),
|
|
3248
|
+
ownerId: this.resolveAiAssistantOwnerId(),
|
|
3249
|
+
ownerType: 'tabs',
|
|
3250
|
+
componentId: 'praxis-tabs',
|
|
3251
|
+
componentType: 'tabs',
|
|
3252
|
+
routeKey: this.resolveAiAssistantRouteKey(),
|
|
3253
|
+
},
|
|
3254
|
+
target: {
|
|
3255
|
+
kind: 'component',
|
|
3256
|
+
id: this.resolveAiAssistantOwnerId(),
|
|
3257
|
+
label: this.tabsId || 'Abas',
|
|
3258
|
+
metadata: {
|
|
3259
|
+
mode: this.isNavMode() ? 'nav' : 'group',
|
|
3260
|
+
hasCustomization: !!this.enableCustomization,
|
|
3261
|
+
},
|
|
3262
|
+
},
|
|
3263
|
+
contextItems: this.buildAiAssistantContextItems().map((item) => ({
|
|
3264
|
+
id: item.id,
|
|
3265
|
+
label: item.label,
|
|
3266
|
+
value: item.value || '',
|
|
3267
|
+
kind: item.kind,
|
|
3268
|
+
})),
|
|
3269
|
+
mode: 'agentic-authoring',
|
|
3270
|
+
authoringManifestRef: {
|
|
3271
|
+
componentId: 'praxis-tabs',
|
|
3272
|
+
source: 'PRAXIS_TABS_AUTHORING_MANIFEST',
|
|
3273
|
+
},
|
|
3274
|
+
schemaFields: fieldNames.length ? fieldNames : undefined,
|
|
3275
|
+
dataProfileDigest: {
|
|
3276
|
+
summary: `${counts.tabCount} aba(s), ${counts.linkCount} link(s), ${counts.widgetCount} widget(s), ${counts.fieldCount} campo(s)`,
|
|
3277
|
+
counts,
|
|
3278
|
+
},
|
|
3279
|
+
runtimeStateDigest: {
|
|
3280
|
+
summary: this.isNavMode()
|
|
3281
|
+
? `Nav ativo no indice ${this.currentNavIndex()}`
|
|
3282
|
+
: `Grupo ativo no indice ${this.selectedIndexSignal()}`,
|
|
3283
|
+
fields: [
|
|
3284
|
+
this.isNavMode() ? 'nav.links' : 'tabs',
|
|
3285
|
+
'selectedIndex',
|
|
3286
|
+
'widgetEvent',
|
|
3287
|
+
],
|
|
3288
|
+
},
|
|
3289
|
+
capabilityRefs: [
|
|
3290
|
+
{
|
|
3291
|
+
id: 'tabs.component-edit-plan',
|
|
3292
|
+
label: 'Plano de edicao de abas',
|
|
3293
|
+
source: 'PRAXIS_TABS_AUTHORING_MANIFEST',
|
|
3294
|
+
risk: 'medium',
|
|
3295
|
+
},
|
|
3296
|
+
],
|
|
3297
|
+
governanceHints: [
|
|
3298
|
+
{
|
|
3299
|
+
kind: 'business-rule-boundary',
|
|
3300
|
+
label: 'Regras compartilhadas exigem governanca',
|
|
3301
|
+
reason: 'Politicas, validacoes reutilizaveis e compliance nao devem ser aplicados como patch local das abas.',
|
|
3302
|
+
risk: 'high',
|
|
3303
|
+
},
|
|
3304
|
+
],
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
syncAiAssistantSession(visibility = null) {
|
|
3308
|
+
if (!this.enableCustomization)
|
|
3309
|
+
return;
|
|
3310
|
+
if (!this.aiAssistantOpen && !this.hasAiAssistantSessionState())
|
|
3311
|
+
return;
|
|
3312
|
+
const state = this.aiAssistantViewState;
|
|
3313
|
+
this.assistantSessions.upsertContextSession(this.buildAiAssistantContextSnapshot(), {
|
|
3314
|
+
title: 'Copiloto semantico Praxis',
|
|
3315
|
+
summary: this.resolveAiAssistantSummary(),
|
|
3316
|
+
mode: state?.mode || 'agentic-authoring',
|
|
3317
|
+
state: state?.state || 'idle',
|
|
3318
|
+
visibility: visibility ?? (this.aiAssistantOpen ? 'active' : 'minimized'),
|
|
3319
|
+
badge: this.resolveAiAssistantBadge(),
|
|
3320
|
+
icon: this.resolveAiAssistantIcon(),
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
hasAiAssistantSessionState() {
|
|
3324
|
+
return !!this.aiAssistantPrompt.trim()
|
|
3325
|
+
|| !!this.aiAssistantViewState?.messages?.length
|
|
3326
|
+
|| !!this.aiAssistantViewState?.quickReplies?.length
|
|
3327
|
+
|| !!this.aiAssistantViewState?.pendingPatch
|
|
3328
|
+
|| !!this.aiAssistantViewState?.statusText?.trim()
|
|
3329
|
+
|| !!this.aiAssistantViewState?.errorText?.trim();
|
|
3330
|
+
}
|
|
3331
|
+
resolveAiAssistantSessionId() {
|
|
3332
|
+
return `tabs:${this.resolveAiAssistantRouteKey()}:${this.resolveAiAssistantOwnerId()}`;
|
|
3333
|
+
}
|
|
3334
|
+
resolveAiAssistantOwnerId() {
|
|
3335
|
+
return (this.componentInstanceId || this.tabsId || 'tabs').trim() || 'tabs';
|
|
3336
|
+
}
|
|
3337
|
+
resolveAiAssistantRouteKey() {
|
|
3338
|
+
const routePath = this.route?.snapshot?.routeConfig?.path?.trim();
|
|
3339
|
+
return routePath || 'local';
|
|
3340
|
+
}
|
|
3341
|
+
resolveAiAssistantSummary() {
|
|
3342
|
+
const status = this.aiAssistantViewState?.statusText?.trim();
|
|
3343
|
+
if (status)
|
|
3344
|
+
return status;
|
|
3345
|
+
const error = this.aiAssistantViewState?.errorText?.trim();
|
|
3346
|
+
if (error)
|
|
3347
|
+
return error;
|
|
3348
|
+
const prompt = this.aiAssistantPrompt.trim();
|
|
3349
|
+
if (prompt)
|
|
3350
|
+
return prompt.length > 96 ? `${prompt.slice(0, 93)}...` : prompt;
|
|
3351
|
+
const lastMessage = [...(this.aiAssistantViewState?.messages ?? [])].reverse()
|
|
3352
|
+
.find((message) => message.role === 'assistant' || message.role === 'user');
|
|
3353
|
+
if (lastMessage?.text) {
|
|
3354
|
+
return lastMessage.text.length > 96 ? `${lastMessage.text.slice(0, 93)}...` : lastMessage.text;
|
|
3355
|
+
}
|
|
3356
|
+
return this.isNavMode() ? 'Assistente contextual das abas de navegacao.' : 'Assistente contextual do grupo de abas.';
|
|
3357
|
+
}
|
|
3358
|
+
resolveAiAssistantBadge() {
|
|
3359
|
+
const state = this.aiAssistantViewState?.state;
|
|
3360
|
+
if (state === 'error')
|
|
3361
|
+
return 'erro';
|
|
3362
|
+
if (state === 'clarification')
|
|
3363
|
+
return 'revisar';
|
|
3364
|
+
if (state === 'review')
|
|
3365
|
+
return 'preview';
|
|
3366
|
+
if (state === 'success')
|
|
3367
|
+
return 'ok';
|
|
3368
|
+
return undefined;
|
|
3369
|
+
}
|
|
3370
|
+
resolveAiAssistantIcon() {
|
|
3371
|
+
const state = this.aiAssistantViewState?.state;
|
|
3372
|
+
if (state === 'error')
|
|
3373
|
+
return 'error';
|
|
3374
|
+
if (state === 'clarification')
|
|
3375
|
+
return 'rule';
|
|
3376
|
+
if (state === 'review')
|
|
3377
|
+
return 'rate_review';
|
|
3378
|
+
return 'auto_awesome';
|
|
3379
|
+
}
|
|
3380
|
+
collectContentFieldNames() {
|
|
3381
|
+
const fields = [
|
|
3382
|
+
...(this.config?.tabs ?? []).flatMap((tab) => tab.content ?? []),
|
|
3383
|
+
...(this.config?.nav?.links ?? []).flatMap((link) => link.content ?? []),
|
|
3384
|
+
];
|
|
3385
|
+
return Array.from(new Set(fields
|
|
3386
|
+
.map((field) => field?.name || field?.key || field?.id)
|
|
3387
|
+
.filter((name) => typeof name === 'string' && !!name.trim())));
|
|
3388
|
+
}
|
|
3389
|
+
collectTabsCounts() {
|
|
3390
|
+
const tabs = this.config?.tabs ?? [];
|
|
3391
|
+
const links = this.config?.nav?.links ?? [];
|
|
3392
|
+
return {
|
|
3393
|
+
tabCount: tabs.length,
|
|
3394
|
+
linkCount: links.length,
|
|
3395
|
+
widgetCount: [
|
|
3396
|
+
...tabs.flatMap((tab) => tab.widgets ?? []),
|
|
3397
|
+
...links.flatMap((link) => link.widgets ?? []),
|
|
3398
|
+
].length,
|
|
3399
|
+
fieldCount: [
|
|
3400
|
+
...tabs.flatMap((tab) => tab.content ?? []),
|
|
3401
|
+
...links.flatMap((link) => link.content ?? []),
|
|
3402
|
+
].length,
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
2684
3405
|
addEmptyTab() {
|
|
2685
3406
|
const next = produce(this.config || {}, (draft) => {
|
|
2686
3407
|
if (!draft.group)
|
|
@@ -3067,9 +3788,51 @@ class PraxisTabs {
|
|
|
3067
3788
|
<style *ngIf="styleCss() as s" [innerHTML]="s"></style>
|
|
3068
3789
|
|
|
3069
3790
|
<div class="tabs-ai-assistant" *ngIf="enableCustomization">
|
|
3070
|
-
<
|
|
3791
|
+
<button
|
|
3792
|
+
mat-mini-fab
|
|
3793
|
+
type="button"
|
|
3794
|
+
color="primary"
|
|
3795
|
+
class="tabs-ai-assistant-trigger"
|
|
3796
|
+
(click)="openAiAssistant()"
|
|
3797
|
+
matTooltip="Copiloto semantico Praxis"
|
|
3798
|
+
aria-label="Abrir copiloto semantico Praxis das abas"
|
|
3799
|
+
data-testid="praxis-tabs-ai-assistant-trigger"
|
|
3800
|
+
>
|
|
3801
|
+
<mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
|
|
3802
|
+
</button>
|
|
3071
3803
|
</div>
|
|
3072
3804
|
|
|
3805
|
+
<praxis-ai-assistant-shell
|
|
3806
|
+
*ngIf="aiAssistantOpen && aiAssistantViewState"
|
|
3807
|
+
[labels]="aiAssistantLabels"
|
|
3808
|
+
[mode]="aiAssistantViewState.mode"
|
|
3809
|
+
[state]="aiAssistantViewState.state"
|
|
3810
|
+
[contextItems]="aiAssistantViewState.contextItems"
|
|
3811
|
+
[attachments]="aiAssistantViewState.attachments"
|
|
3812
|
+
[messages]="aiAssistantViewState.messages"
|
|
3813
|
+
[quickReplies]="aiAssistantViewState.quickReplies"
|
|
3814
|
+
[prompt]="aiAssistantPrompt"
|
|
3815
|
+
[statusText]="aiAssistantViewState.statusText"
|
|
3816
|
+
[errorText]="aiAssistantViewState.errorText"
|
|
3817
|
+
[busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
|
|
3818
|
+
[canApply]="aiAssistantViewState.canApply"
|
|
3819
|
+
[layout]="aiAssistantLayout"
|
|
3820
|
+
testIdPrefix="praxis-tabs-ai-assistant"
|
|
3821
|
+
panelTestId="praxis-tabs-ai-assistant-panel"
|
|
3822
|
+
submitTestId="praxis-tabs-ai-assistant-submit"
|
|
3823
|
+
applyTestId="praxis-tabs-ai-assistant-apply"
|
|
3824
|
+
(promptChange)="onAiAssistantPromptChange($event)"
|
|
3825
|
+
(submitPrompt)="onAiAssistantSubmit($event)"
|
|
3826
|
+
(apply)="onAiAssistantApply()"
|
|
3827
|
+
(retryTurn)="onAiAssistantRetry()"
|
|
3828
|
+
(cancelTurn)="onAiAssistantCancel()"
|
|
3829
|
+
(quickReply)="onAiAssistantQuickReply($event)"
|
|
3830
|
+
(editMessage)="onAiAssistantEditMessage($event)"
|
|
3831
|
+
(resendMessage)="onAiAssistantResendMessage($event)"
|
|
3832
|
+
(layoutChange)="onAiAssistantLayoutChange($event)"
|
|
3833
|
+
(close)="closeAiAssistant()"
|
|
3834
|
+
></praxis-ai-assistant-shell>
|
|
3835
|
+
|
|
3073
3836
|
<!-- Empty state (global) -->
|
|
3074
3837
|
<ng-container *ngIf="isEmptyGlobal(); else notEmpty">
|
|
3075
3838
|
<praxis-empty-state-card
|
|
@@ -3291,7 +4054,7 @@ class PraxisTabs {
|
|
|
3291
4054
|
<mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
|
|
3292
4055
|
</button>
|
|
3293
4056
|
</div>
|
|
3294
|
-
`, isInline: true, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3$1.MatTabContent, selector: "[matTabContent]" }, { kind: "directive", type: i3$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3$1.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i3$1.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i3$1.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i10.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i10.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i10.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { 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: "component", type:
|
|
4057
|
+
`, isInline: true, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.tabs-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3$1.MatTabContent, selector: "[matTabContent]" }, { kind: "directive", type: i3$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3$1.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i3$1.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i3$1.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i10.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i10.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i10.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { 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: "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 });
|
|
3295
4058
|
}
|
|
3296
4059
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabs, decorators: [{
|
|
3297
4060
|
type: Component,
|
|
@@ -3307,7 +4070,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3307
4070
|
EmptyStateCardComponent,
|
|
3308
4071
|
DynamicFieldLoaderDirective,
|
|
3309
4072
|
DynamicWidgetLoaderDirective,
|
|
3310
|
-
|
|
4073
|
+
PraxisAiAssistantShellComponent,
|
|
3311
4074
|
], template: `
|
|
3312
4075
|
<div
|
|
3313
4076
|
class="praxis-tabs-root"
|
|
@@ -3325,9 +4088,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3325
4088
|
<style *ngIf="styleCss() as s" [innerHTML]="s"></style>
|
|
3326
4089
|
|
|
3327
4090
|
<div class="tabs-ai-assistant" *ngIf="enableCustomization">
|
|
3328
|
-
<
|
|
4091
|
+
<button
|
|
4092
|
+
mat-mini-fab
|
|
4093
|
+
type="button"
|
|
4094
|
+
color="primary"
|
|
4095
|
+
class="tabs-ai-assistant-trigger"
|
|
4096
|
+
(click)="openAiAssistant()"
|
|
4097
|
+
matTooltip="Copiloto semantico Praxis"
|
|
4098
|
+
aria-label="Abrir copiloto semantico Praxis das abas"
|
|
4099
|
+
data-testid="praxis-tabs-ai-assistant-trigger"
|
|
4100
|
+
>
|
|
4101
|
+
<mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
|
|
4102
|
+
</button>
|
|
3329
4103
|
</div>
|
|
3330
4104
|
|
|
4105
|
+
<praxis-ai-assistant-shell
|
|
4106
|
+
*ngIf="aiAssistantOpen && aiAssistantViewState"
|
|
4107
|
+
[labels]="aiAssistantLabels"
|
|
4108
|
+
[mode]="aiAssistantViewState.mode"
|
|
4109
|
+
[state]="aiAssistantViewState.state"
|
|
4110
|
+
[contextItems]="aiAssistantViewState.contextItems"
|
|
4111
|
+
[attachments]="aiAssistantViewState.attachments"
|
|
4112
|
+
[messages]="aiAssistantViewState.messages"
|
|
4113
|
+
[quickReplies]="aiAssistantViewState.quickReplies"
|
|
4114
|
+
[prompt]="aiAssistantPrompt"
|
|
4115
|
+
[statusText]="aiAssistantViewState.statusText"
|
|
4116
|
+
[errorText]="aiAssistantViewState.errorText"
|
|
4117
|
+
[busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
|
|
4118
|
+
[canApply]="aiAssistantViewState.canApply"
|
|
4119
|
+
[layout]="aiAssistantLayout"
|
|
4120
|
+
testIdPrefix="praxis-tabs-ai-assistant"
|
|
4121
|
+
panelTestId="praxis-tabs-ai-assistant-panel"
|
|
4122
|
+
submitTestId="praxis-tabs-ai-assistant-submit"
|
|
4123
|
+
applyTestId="praxis-tabs-ai-assistant-apply"
|
|
4124
|
+
(promptChange)="onAiAssistantPromptChange($event)"
|
|
4125
|
+
(submitPrompt)="onAiAssistantSubmit($event)"
|
|
4126
|
+
(apply)="onAiAssistantApply()"
|
|
4127
|
+
(retryTurn)="onAiAssistantRetry()"
|
|
4128
|
+
(cancelTurn)="onAiAssistantCancel()"
|
|
4129
|
+
(quickReply)="onAiAssistantQuickReply($event)"
|
|
4130
|
+
(editMessage)="onAiAssistantEditMessage($event)"
|
|
4131
|
+
(resendMessage)="onAiAssistantResendMessage($event)"
|
|
4132
|
+
(layoutChange)="onAiAssistantLayoutChange($event)"
|
|
4133
|
+
(close)="closeAiAssistant()"
|
|
4134
|
+
></praxis-ai-assistant-shell>
|
|
4135
|
+
|
|
3331
4136
|
<!-- Empty state (global) -->
|
|
3332
4137
|
<ng-container *ngIf="isEmptyGlobal(); else notEmpty">
|
|
3333
4138
|
<praxis-empty-state-card
|
|
@@ -3549,7 +4354,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3549
4354
|
<mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
|
|
3550
4355
|
</button>
|
|
3551
4356
|
</div>
|
|
3552
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"] }]
|
|
4357
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.tabs-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"] }]
|
|
3553
4358
|
}], propDecorators: { config: [{
|
|
3554
4359
|
type: Input
|
|
3555
4360
|
}], tabsId: [{
|
|
@@ -3581,6 +4386,108 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3581
4386
|
type: Output
|
|
3582
4387
|
}] } });
|
|
3583
4388
|
|
|
4389
|
+
class PraxisTabsWidgetConfigEditor {
|
|
4390
|
+
set inputs(value) {
|
|
4391
|
+
this._inputs = value;
|
|
4392
|
+
this.editorDocument = this.createDocument();
|
|
4393
|
+
}
|
|
4394
|
+
get inputs() {
|
|
4395
|
+
return this._inputs;
|
|
4396
|
+
}
|
|
4397
|
+
set widgetKey(value) {
|
|
4398
|
+
this._widgetKey = value;
|
|
4399
|
+
this.editorDocument = this.createDocument();
|
|
4400
|
+
}
|
|
4401
|
+
get widgetKey() {
|
|
4402
|
+
return this._widgetKey;
|
|
4403
|
+
}
|
|
4404
|
+
tabsEditor;
|
|
4405
|
+
isDirty$ = new BehaviorSubject(false);
|
|
4406
|
+
isValid$ = new BehaviorSubject(true);
|
|
4407
|
+
isBusy$ = new BehaviorSubject(false);
|
|
4408
|
+
subscription = new Subscription();
|
|
4409
|
+
emptyConfig = {};
|
|
4410
|
+
_inputs = null;
|
|
4411
|
+
_widgetKey;
|
|
4412
|
+
editorDocument = this.createDocument();
|
|
4413
|
+
get config() {
|
|
4414
|
+
return this.inputs?.config ?? this.emptyConfig;
|
|
4415
|
+
}
|
|
4416
|
+
get tabsId() {
|
|
4417
|
+
return this.inputs?.tabsId ?? this.widgetKey;
|
|
4418
|
+
}
|
|
4419
|
+
get componentInstanceId() {
|
|
4420
|
+
return this.inputs?.componentInstanceId ?? undefined;
|
|
4421
|
+
}
|
|
4422
|
+
ngAfterViewInit() {
|
|
4423
|
+
if (!this.tabsEditor) {
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
4426
|
+
this.subscription.add(this.tabsEditor.isDirty$.subscribe((value) => this.isDirty$.next(value)));
|
|
4427
|
+
this.subscription.add(this.tabsEditor.isValid$.subscribe((value) => this.isValid$.next(value)));
|
|
4428
|
+
this.subscription.add(this.tabsEditor.isBusy$.subscribe((value) => this.isBusy$.next(value)));
|
|
4429
|
+
}
|
|
4430
|
+
ngOnDestroy() {
|
|
4431
|
+
this.subscription.unsubscribe();
|
|
4432
|
+
}
|
|
4433
|
+
getSettingsValue() {
|
|
4434
|
+
return this.buildValue(this.tabsEditor?.getSettingsValue());
|
|
4435
|
+
}
|
|
4436
|
+
onSave() {
|
|
4437
|
+
return this.buildValue(this.tabsEditor?.onSave?.() ?? this.tabsEditor?.getSettingsValue());
|
|
4438
|
+
}
|
|
4439
|
+
reset() {
|
|
4440
|
+
this.tabsEditor?.reset();
|
|
4441
|
+
}
|
|
4442
|
+
buildValue(document) {
|
|
4443
|
+
const bindings = document?.bindings ?? {};
|
|
4444
|
+
return {
|
|
4445
|
+
inputs: {
|
|
4446
|
+
...(this.inputs ?? {}),
|
|
4447
|
+
config: document?.config ?? this.config,
|
|
4448
|
+
...(bindings.tabsId ? { tabsId: bindings.tabsId } : this.tabsId ? { tabsId: this.tabsId } : {}),
|
|
4449
|
+
...(bindings.componentInstanceId ? { componentInstanceId: bindings.componentInstanceId } : {}),
|
|
4450
|
+
},
|
|
4451
|
+
};
|
|
4452
|
+
}
|
|
4453
|
+
createDocument() {
|
|
4454
|
+
return createTabsAuthoringDocument({
|
|
4455
|
+
config: this.config,
|
|
4456
|
+
bindings: {
|
|
4457
|
+
tabsId: this.tabsId,
|
|
4458
|
+
componentInstanceId: this.componentInstanceId,
|
|
4459
|
+
},
|
|
4460
|
+
});
|
|
4461
|
+
}
|
|
4462
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabsWidgetConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4463
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabsWidgetConfigEditor, isStandalone: true, selector: "praxis-tabs-widget-config-editor", inputs: { inputs: "inputs", widgetKey: "widgetKey" }, viewQueries: [{ propertyName: "tabsEditor", first: true, predicate: ["tabsEditor"], descendants: true }], ngImport: i0, template: `
|
|
4464
|
+
<section data-testid="tabs-widget-config-editor">
|
|
4465
|
+
<praxis-tabs-config-editor #tabsEditor [document]="editorDocument" />
|
|
4466
|
+
</section>
|
|
4467
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PraxisTabsConfigEditor, selector: "praxis-tabs-config-editor", inputs: ["document"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4468
|
+
}
|
|
4469
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabsWidgetConfigEditor, decorators: [{
|
|
4470
|
+
type: Component,
|
|
4471
|
+
args: [{
|
|
4472
|
+
selector: 'praxis-tabs-widget-config-editor',
|
|
4473
|
+
standalone: true,
|
|
4474
|
+
imports: [CommonModule, PraxisTabsConfigEditor],
|
|
4475
|
+
template: `
|
|
4476
|
+
<section data-testid="tabs-widget-config-editor">
|
|
4477
|
+
<praxis-tabs-config-editor #tabsEditor [document]="editorDocument" />
|
|
4478
|
+
</section>
|
|
4479
|
+
`,
|
|
4480
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4481
|
+
}]
|
|
4482
|
+
}], propDecorators: { inputs: [{
|
|
4483
|
+
type: Input
|
|
4484
|
+
}], widgetKey: [{
|
|
4485
|
+
type: Input
|
|
4486
|
+
}], tabsEditor: [{
|
|
4487
|
+
type: ViewChild,
|
|
4488
|
+
args: ['tabsEditor']
|
|
4489
|
+
}] } });
|
|
4490
|
+
|
|
3584
4491
|
const PRAXIS_TABS_PORTS = [
|
|
3585
4492
|
{
|
|
3586
4493
|
id: 'context',
|
|
@@ -3657,6 +4564,14 @@ const PRAXIS_TABS_COMPONENT_METADATA = {
|
|
|
3657
4564
|
friendlyName: 'Praxis Tabs',
|
|
3658
4565
|
description: 'Abas dinâmicas baseadas em metadata, com MatTabGroup/TabNav e suporte a tokens M3 via appearance.',
|
|
3659
4566
|
icon: 'tab',
|
|
4567
|
+
authoringManifestRef: {
|
|
4568
|
+
componentId: 'praxis-tabs',
|
|
4569
|
+
source: 'PRAXIS_TABS_AUTHORING_MANIFEST',
|
|
4570
|
+
},
|
|
4571
|
+
configEditor: {
|
|
4572
|
+
component: PraxisTabsWidgetConfigEditor,
|
|
4573
|
+
title: 'Configure tabs',
|
|
4574
|
+
},
|
|
3660
4575
|
inputs: [
|
|
3661
4576
|
{ name: 'config', type: 'TabsMetadata', label: 'Configuração', description: 'Configuração JSON (tabs/nav, aparência e widgets internos)' },
|
|
3662
4577
|
{
|
|
@@ -4141,4 +5056,4 @@ const PRAXIS_TABS_AUTHORING_MANIFEST = {
|
|
|
4141
5056
|
* Generated bundle index. Do not edit.
|
|
4142
5057
|
*/
|
|
4143
5058
|
|
|
4144
|
-
export { PRAXIS_TABS_AUTHORING_MANIFEST, PRAXIS_TABS_COMPONENT_METADATA, PRAXIS_TABS_I18N_CONFIG, PRAXIS_TABS_I18N_NAMESPACE, PraxisTabs, PraxisTabsConfigEditor, TABS_AI_CAPABILITIES, buildTabsApplyPlan, createPraxisTabsI18nConfig, createTabsAuthoringDocument, normalizeTabsAuthoringDocument, providePraxisTabsI18n, providePraxisTabsMetadata, serializeTabsAuthoringDocument, toCanonicalTabsConfig, validateTabsAuthoringDocument };
|
|
5059
|
+
export { PRAXIS_TABS_AUTHORING_MANIFEST, PRAXIS_TABS_COMPONENT_METADATA, PRAXIS_TABS_I18N_CONFIG, PRAXIS_TABS_I18N_NAMESPACE, PraxisTabs, PraxisTabsConfigEditor, PraxisTabsWidgetConfigEditor, TABS_AI_CAPABILITIES, buildTabsApplyPlan, createPraxisTabsI18nConfig, createTabsAuthoringDocument, normalizeTabsAuthoringDocument, providePraxisTabsI18n, providePraxisTabsMetadata, serializeTabsAuthoringDocument, toCanonicalTabsConfig, validateTabsAuthoringDocument };
|