@praxisui/table 8.0.0-beta.94 → 8.0.0-beta.96

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.
@@ -126,8 +126,8 @@ const PRAXIS_TABLE_RUNTIME_I18N_CONFIG = {
126
126
  'table.assistant.recommendation.globalNavigation.description': 'Conecta a tabela a rotas ou páginas publicadas pelo host.',
127
127
  'table.assistant.recommendation.globalNavigation.prompt': 'Mostre quais ações globais de navegação podem ser adicionadas nesta tabela.',
128
128
  'table.assistant.recommendation.selection.label': 'Analisar selecionados',
129
- 'table.assistant.recommendation.selection.description': 'Compara os registros marcados e sugere filtros, ranges ou ações compatíveis.',
130
- 'table.assistant.recommendation.selection.prompt': 'Analise os registros selecionados e sugira as próximas ações úteis para esta tabela.',
129
+ 'table.assistant.recommendation.selection.description': 'Resume semelhanças, diferenças e próximos caminhos sem alterar a tabela automaticamente.',
130
+ 'table.assistant.recommendation.selection.prompt': 'Analise os registros selecionados e responda em linguagem natural: o que eles têm em comum, quais diferenças importam e quais próximos caminhos seriam úteis. Não aplique alterações sem um pedido explícito.',
131
131
  'table.assistant.recommendation.capabilityDiscovery.label': 'Ver recursos recomendados',
132
132
  'table.assistant.recommendation.capabilityDiscovery.description': 'Analisa configuração, colunas e dados para sugerir os próximos ajustes úteis.',
133
133
  'table.assistant.recommendation.capabilityDiscovery.prompt': 'Analise esta tabela, suas configurações atuais, colunas e dados disponíveis, e recomende os recursos mais úteis para aplicar agora.',
@@ -39947,7 +39947,7 @@ class PraxisTable {
39947
39947
  if (this.aiAdapter || this.aiAdapterLoadStarted)
39948
39948
  return;
39949
39949
  this.aiAdapterLoadStarted = true;
39950
- import('./praxisui-table-table-ai.adapter-BWnL5QfP.mjs')
39950
+ import('./praxisui-table-table-ai.adapter-DR0VP12I.mjs')
39951
39951
  .then(({ TableAiAdapter }) => {
39952
39952
  this.aiAdapter = new TableAiAdapter(this);
39953
39953
  this.initializeAiAssistantController();
@@ -40256,7 +40256,7 @@ class PraxisTable {
40256
40256
  intents.push({
40257
40257
  id: 'table-analyze-selection',
40258
40258
  label: t('table.assistant.recommendation.selection.label', 'Analisar selecionados'),
40259
- description: t('table.assistant.recommendation.selection.description', 'Compara os registros marcados e sugere filtros, ranges ou ações compatíveis.'),
40259
+ description: t('table.assistant.recommendation.selection.description', 'Resume semelhanças, diferenças e próximos caminhos sem alterar a tabela automaticamente.'),
40260
40260
  group: {
40261
40261
  id: 'table-selection',
40262
40262
  label: t('table.assistant.recommendation.group.selection', 'Seleção'),
@@ -40265,10 +40265,15 @@ class PraxisTable {
40265
40265
  tone: 'success',
40266
40266
  action: {
40267
40267
  kind: 'submit-prompt',
40268
- prompt: t('table.assistant.recommendation.selection.prompt', 'Analise os registros selecionados e sugira as próximas ações úteis para esta tabela.'),
40268
+ prompt: t('table.assistant.recommendation.selection.prompt', 'Analise os registros selecionados e responda em linguagem natural: o que eles têm em comum, quais diferenças importam e quais próximos caminhos seriam úteis. Não aplique alterações sem um pedido explícito.'),
40269
40269
  contextHints: {
40270
40270
  source: 'table-opportunity-catalog',
40271
40271
  opportunityId: 'table.selection.analyze',
40272
+ authoringMode: 'table.selection.readonly-analysis',
40273
+ responseContract: {
40274
+ preferredResponse: 'info',
40275
+ mutatesTable: false,
40276
+ },
40272
40277
  target: { kind: 'component', id: tableContext.tableId },
40273
40278
  componentState: tableContext,
40274
40279
  selectedRecordsContext: this.getAiAssistantSelectedRowsContext(),
@@ -40561,7 +40566,7 @@ class PraxisTable {
40561
40566
  initializeAiAssistantController() {
40562
40567
  if (!this.aiAdapter || this.aiAssistantController)
40563
40568
  return;
40564
- import('./praxisui-table-table-agentic-authoring-turn-flow-DUu4AIDF.mjs')
40569
+ import('./praxisui-table-table-agentic-authoring-turn-flow-DSn29-0N.mjs')
40565
40570
  .then(({ TableAgenticAuthoringTurnFlow }) => {
40566
40571
  if (this.aiAssistantController || !this.aiAdapter)
40567
40572
  return;
@@ -393,6 +393,17 @@ class TableAgenticAuthoringTurnFlow {
393
393
  message: 'Selecione um ou mais registros para eu sugerir filtros, exportações ou ações compatíveis com essa seleção.',
394
394
  };
395
395
  }
396
+ if (this.selectedRecordReadOnlyAnalysisRequested(request)) {
397
+ return {
398
+ type: 'info',
399
+ sessionId: request.sessionId,
400
+ message: this.selectedRecordsAnalyticalSummary(contextHints),
401
+ warnings: [
402
+ 'recommended-intent-local-selected-records-analysis',
403
+ 'Resposta informativa gerada a partir do recommendedIntent canonico table.selection.analyze e selectedRecordsContext.sampleRows; nao houve roteamento textual primario.',
404
+ ],
405
+ };
406
+ }
396
407
  const candidates = this.selectedRecordFilterCandidates(contextHints);
397
408
  const fields = candidates
398
409
  .map((candidate) => this.stringValue(candidate['field']))
@@ -480,6 +491,8 @@ class TableAgenticAuthoringTurnFlow {
480
491
  return null;
481
492
  if (!this.runtimeOperationAllowed(contextHints, 'table.filter.apply'))
482
493
  return null;
494
+ if (this.selectedRecordReadOnlyAnalysisRequested(request))
495
+ return null;
483
496
  const normalizedPrompt = this.normalizeLabel(request.prompt ?? '');
484
497
  if (!normalizedPrompt || !this.promptRequestsRuntimeFilter(normalizedPrompt))
485
498
  return null;
@@ -1525,9 +1538,18 @@ class TableAgenticAuthoringTurnFlow {
1525
1538
  'render',
1526
1539
  'oculta',
1527
1540
  'ocultar',
1541
+ 'oculte',
1528
1542
  'esconde',
1529
1543
  'exibe',
1530
1544
  'mostra coluna',
1545
+ 'nova coluna',
1546
+ 'coluna calculada',
1547
+ 'junta',
1548
+ 'junte',
1549
+ 'juntar',
1550
+ 'unir',
1551
+ 'combinar',
1552
+ 'mesclar',
1531
1553
  'adiciona botao',
1532
1554
  'botao',
1533
1555
  ].some((token) => normalizedPrompt.includes(this.normalizeLabel(token)));
@@ -2227,7 +2249,9 @@ class TableAgenticAuthoringTurnFlow {
2227
2249
  observationId: response.observationId ?? request.observationId ?? null,
2228
2250
  assistantMessage: message,
2229
2251
  statusText: message,
2230
- quickReplies: this.toQuickReplies(response, request),
2252
+ quickReplies: this.shouldSuppressInfoQuickReplies(response, request)
2253
+ ? []
2254
+ : this.toQuickReplies(response, request),
2231
2255
  canApply: false,
2232
2256
  };
2233
2257
  }
@@ -2282,6 +2306,11 @@ class TableAgenticAuthoringTurnFlow {
2282
2306
  const record = this.toRecord(patch);
2283
2307
  return !!record && Object.keys(record).length > 0;
2284
2308
  }
2309
+ shouldSuppressInfoQuickReplies(response, request) {
2310
+ return this.selectedRecordReadOnlyAnalysisRequested(request)
2311
+ || (response.warnings ?? []).some((warning) => this.normalizeLabel(warning).includes('selected records analysis')
2312
+ || this.normalizeLabel(warning).includes('selected record readonly'));
2313
+ }
2285
2314
  isTurnInProgressResponse(response) {
2286
2315
  return this.normalizeLabel(response.code ?? '') === 'turn in progress';
2287
2316
  }
@@ -2334,6 +2363,10 @@ class TableAgenticAuthoringTurnFlow {
2334
2363
  if (openOnlySurfaceRuntimeOperation) {
2335
2364
  return openOnlySurfaceRuntimeOperation;
2336
2365
  }
2366
+ const selectedRecordReadonlyBoundary = this.selectedRecordReadonlyBoundaryResponse(executableResponse, request);
2367
+ if (selectedRecordReadonlyBoundary) {
2368
+ return selectedRecordReadonlyBoundary;
2369
+ }
2337
2370
  return executableResponse;
2338
2371
  }
2339
2372
  if (compiledExecutable?.type === 'error') {
@@ -2527,6 +2560,10 @@ class TableAgenticAuthoringTurnFlow {
2527
2560
  patch: normalizedCompiled.patch,
2528
2561
  warnings: warnings.length ? warnings : undefined,
2529
2562
  };
2563
+ const selectedRecordReadonlyBoundary = this.selectedRecordReadonlyBoundaryResponse(compiledResponse, request);
2564
+ if (selectedRecordReadonlyBoundary) {
2565
+ return selectedRecordReadonlyBoundary;
2566
+ }
2530
2567
  const columnVisibilitySurfaceMismatch = this.columnVisibilityPlanForMisroutedSurfaceRuntime(compiledResponse, request);
2531
2568
  if (columnVisibilitySurfaceMismatch) {
2532
2569
  return this.compileAdapterResponse(columnVisibilitySurfaceMismatch, request);
@@ -3810,10 +3847,7 @@ class TableAgenticAuthoringTurnFlow {
3810
3847
  .find((candidate) => this.stringValue(candidate['field']) === normalizedField) ?? null;
3811
3848
  }
3812
3849
  selectedRecordFilterCandidates(contextHints) {
3813
- const authoringContract = this.toRecord(contextHints?.['authoringContract']);
3814
- const consultativeContext = this.toRecord(authoringContract?.['consultativeContext']);
3815
- const selectedRecordsContext = this.toRecord(consultativeContext?.['selectedRecordsContext'])
3816
- ?? this.toRecord(contextHints?.['selectedRecordsContext']);
3850
+ const selectedRecordsContext = this.selectedRecordsContextRecord(contextHints);
3817
3851
  const candidates = Array.isArray(selectedRecordsContext?.['filterCandidates'])
3818
3852
  ? selectedRecordsContext['filterCandidates']
3819
3853
  : [];
@@ -3822,10 +3856,7 @@ class TableAgenticAuthoringTurnFlow {
3822
3856
  .filter((candidate) => !!candidate);
3823
3857
  }
3824
3858
  selectedRecordSampleRows(contextHints) {
3825
- const authoringContract = this.toRecord(contextHints?.['authoringContract']);
3826
- const consultativeContext = this.toRecord(authoringContract?.['consultativeContext']);
3827
- const selectedRecordsContext = this.toRecord(consultativeContext?.['selectedRecordsContext'])
3828
- ?? this.toRecord(contextHints?.['selectedRecordsContext']);
3859
+ const selectedRecordsContext = this.selectedRecordsContextRecord(contextHints);
3829
3860
  const sampleRows = Array.isArray(selectedRecordsContext?.['sampleRows'])
3830
3861
  ? selectedRecordsContext['sampleRows']
3831
3862
  : [];
@@ -3833,6 +3864,12 @@ class TableAgenticAuthoringTurnFlow {
3833
3864
  .map((row) => this.toRecord(row))
3834
3865
  .filter((row) => !!row);
3835
3866
  }
3867
+ selectedRecordsContextRecord(contextHints) {
3868
+ const authoringContract = this.toRecord(contextHints?.['authoringContract']);
3869
+ const consultativeContext = this.toRecord(authoringContract?.['consultativeContext']);
3870
+ return this.toRecord(consultativeContext?.['selectedRecordsContext'])
3871
+ ?? this.toRecord(contextHints?.['selectedRecordsContext']);
3872
+ }
3836
3873
  selectedRecordFilterClarificationOptionPayload(entry, contextHints) {
3837
3874
  const description = this.selectedRecordFilterCandidateDescription(entry.name, contextHints);
3838
3875
  return {
@@ -6212,6 +6249,300 @@ class TableAgenticAuthoringTurnFlow {
6212
6249
  .map((operation) => this.toRecord(operation))
6213
6250
  .filter((operation) => !!operation && this.stringValue(operation['operationId']) === 'table.filter.apply');
6214
6251
  }
6252
+ selectedRecordReadonlyBoundaryResponse(response, request) {
6253
+ if (!this.selectedRecordReadOnlyAnalysisRequested(request))
6254
+ return null;
6255
+ if (!response.patch || this.tableFilterApplyOperations(response.patch).length === 0)
6256
+ return null;
6257
+ if (this.responseDeclaresTableMutation(response))
6258
+ return null;
6259
+ const contextHints = this.contextHintsFor(request);
6260
+ return {
6261
+ type: 'info',
6262
+ sessionId: response.sessionId ?? request?.sessionId,
6263
+ message: this.selectedRecordsAnalyticalSummary(contextHints),
6264
+ warnings: [
6265
+ ...(response.warnings ?? []),
6266
+ 'selected-record-readonly-analysis-filter-materialization-blocked',
6267
+ 'A plataforma bloqueou table.filter.apply porque o turno tinha selectedRecordsContext consultivo e a resposta nao declarou uma decisao semantica mutante explicita.',
6268
+ ],
6269
+ };
6270
+ }
6271
+ selectedRecordReadOnlyAnalysisRequested(request) {
6272
+ const contextHints = this.contextHintsFor(request);
6273
+ const selectedCount = this.extractSelectedRecordsCount(contextHints);
6274
+ const hasSelectedRows = selectedCount > 0 || this.selectedRecordSampleRows(contextHints).length > 0;
6275
+ if (!hasSelectedRows)
6276
+ return false;
6277
+ if (this.toRecord(contextHints?.['selectedRecordsFilter'])
6278
+ || this.toRecord(request?.action?.contextHints?.['selectedRecordsFilter'])
6279
+ || this.toRecord(contextHints?.['tableRuntimeOperation'])
6280
+ || this.toRecord(request?.action?.contextHints?.['tableRuntimeOperation'])) {
6281
+ return false;
6282
+ }
6283
+ const normalizedPrompt = this.normalizeLabel(request?.prompt ?? '');
6284
+ if (this.promptExplicitlyForbidsTableMutation(normalizedPrompt))
6285
+ return true;
6286
+ if (this.promptRequestsSelectedRecordDerivedOperation(normalizedPrompt))
6287
+ return false;
6288
+ const responseContract = this.toRecord(contextHints?.['responseContract'])
6289
+ ?? this.toRecord(request?.action?.contextHints?.['responseContract']);
6290
+ if (responseContract) {
6291
+ const preferredResponse = this.normalizeLabel(this.stringValue(responseContract['preferredResponse']));
6292
+ const mutatesTable = responseContract['mutatesTable'];
6293
+ if (preferredResponse === 'info' || mutatesTable === false)
6294
+ return true;
6295
+ if (mutatesTable === true)
6296
+ return false;
6297
+ }
6298
+ const authoringMode = this.normalizeLabel(this.stringValue(contextHints?.['authoringMode']).replace(/[._-]+/gu, ' '));
6299
+ if (authoringMode === 'table selection readonly analysis')
6300
+ return true;
6301
+ return this.selectedRecordPromptLooksConsultative(normalizedPrompt);
6302
+ }
6303
+ selectedRecordPromptLooksConsultative(normalizedPrompt) {
6304
+ if (!normalizedPrompt)
6305
+ return false;
6306
+ if (this.promptRequestsRuntimeFilter(normalizedPrompt)
6307
+ || this.promptRequestsVisualOrStructuralEdit(normalizedPrompt)
6308
+ || this.promptRequestsExportOperation(normalizedPrompt)) {
6309
+ return this.promptExplicitlyForbidsTableMutation(normalizedPrompt);
6310
+ }
6311
+ if (this.promptExplicitlyForbidsTableMutation(normalizedPrompt))
6312
+ return true;
6313
+ // This is a defensive read-only boundary after selectedRecordsContext is already in scope.
6314
+ // It does not choose a table operation; it only avoids materializing one when the turn is a question.
6315
+ const questionSignals = [
6316
+ 'qual',
6317
+ 'quais',
6318
+ 'o que',
6319
+ 'quem',
6320
+ 'quanto',
6321
+ 'quantos',
6322
+ 'compare',
6323
+ 'comparar',
6324
+ 'resuma',
6325
+ 'resumir',
6326
+ 'analise',
6327
+ 'analisar',
6328
+ 'explique',
6329
+ 'explicar',
6330
+ 'tem algum',
6331
+ 'existe algum',
6332
+ 'faixa salarial',
6333
+ 'ganha mais',
6334
+ 'ganha menos',
6335
+ 'em comum',
6336
+ 'padrao',
6337
+ 'padrão',
6338
+ ].map((value) => this.normalizeLabel(value));
6339
+ return normalizedPrompt.includes('?')
6340
+ || questionSignals.some((signal) => this.normalizedTextHasStandaloneToken(normalizedPrompt, signal)
6341
+ || this.normalizedTextContainsApproxPhrase(normalizedPrompt, signal));
6342
+ }
6343
+ promptRequestsSelectedRecordDerivedOperation(normalizedPrompt) {
6344
+ if (!normalizedPrompt)
6345
+ return false;
6346
+ if (this.promptExplicitlyForbidsTableMutation(normalizedPrompt))
6347
+ return false;
6348
+ if (this.promptRequestsExportOperation(normalizedPrompt) || this.promptRequestsVisualOrStructuralEdit(normalizedPrompt)) {
6349
+ return true;
6350
+ }
6351
+ // This ranks explicit selected-record operation wording after selectedRecordsContext is already available.
6352
+ // The primary intent still comes from the semantic turn/contract; these phrases only prevent a read-only
6353
+ // safety boundary from cancelling an operation the user explicitly asked to materialize.
6354
+ return [
6355
+ 'filtrar',
6356
+ 'filtra',
6357
+ 'filtro',
6358
+ 'filtre',
6359
+ 'buscar registros',
6360
+ 'procurar registros',
6361
+ 'outros registros',
6362
+ 'registro parecido',
6363
+ 'registros parecidos',
6364
+ 'parecidos',
6365
+ 'semelhantes',
6366
+ 'mesmo cargo',
6367
+ 'mesmos cargos',
6368
+ 'mesmo departamento',
6369
+ 'mesmos departamentos',
6370
+ 'mesmos valores',
6371
+ 'gente com os mesmos',
6372
+ 'pessoas com os mesmos',
6373
+ 'funcionarios com os mesmos',
6374
+ 'funcionários com os mesmos',
6375
+ 'quero outros',
6376
+ 'quero os mesmo',
6377
+ 'quero os mesmos',
6378
+ 'quero ver gente',
6379
+ ].some((signal) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, signal));
6380
+ }
6381
+ promptExplicitlyForbidsTableMutation(normalizedPrompt) {
6382
+ if (!normalizedPrompt)
6383
+ return false;
6384
+ return [
6385
+ 'sem alterar',
6386
+ 'nao altere',
6387
+ 'não altere',
6388
+ 'nao aplique',
6389
+ 'não aplique',
6390
+ 'so explique',
6391
+ 'só explique',
6392
+ 'apenas explique',
6393
+ 'sem aplicar filtro',
6394
+ 'nao filtrar',
6395
+ 'não filtrar',
6396
+ ].some((signal) => this.normalizedTextContainsApproxPhrase(normalizedPrompt, signal));
6397
+ }
6398
+ responseDeclaresTableMutation(response) {
6399
+ const responseRecord = response;
6400
+ const semanticDecision = this.toRecord(responseRecord['semanticDecision'])
6401
+ ?? this.toRecord(this.toRecord(responseRecord['intentResolution'])?.['semanticDecision'])
6402
+ ?? this.toRecord(this.toRecord(responseRecord['diagnostics'])?.['semanticDecision']);
6403
+ const responseContract = this.toRecord(responseRecord['responseContract'])
6404
+ ?? this.toRecord(semanticDecision?.['responseContract']);
6405
+ if (responseContract?.['mutatesTable'] === true)
6406
+ return true;
6407
+ if (semanticDecision?.['mutatesTable'] === true)
6408
+ return true;
6409
+ const operationKind = this.normalizeLabel(this.stringValue(semanticDecision?.['operationKind']));
6410
+ const changeKind = this.normalizeLabel(this.stringValue(semanticDecision?.['changeKind']));
6411
+ const artifactIntent = this.normalizeLabel(this.stringValue(semanticDecision?.['artifactIntent']));
6412
+ return [
6413
+ operationKind,
6414
+ changeKind,
6415
+ artifactIntent,
6416
+ ].some((value) => value.includes('table filter apply')
6417
+ || value.includes('filter apply')
6418
+ || value.includes('runtime operation')
6419
+ || value.includes('mutation')
6420
+ || value.includes('mutate'));
6421
+ }
6422
+ selectedRecordsAnalyticalSummary(contextHints) {
6423
+ const rows = this.selectedRecordSampleRows(contextHints);
6424
+ const selectedCount = this.extractSelectedRecordsCount(contextHints) || rows.length;
6425
+ if (!rows.length) {
6426
+ return `Encontrei ${selectedCount} registro${selectedCount === 1 ? '' : 's'} selecionado${selectedCount === 1 ? '' : 's'}, mas o contexto enviado não trouxe amostras de valores suficientes para comparar sem alterar a tabela.`;
6427
+ }
6428
+ const columns = this.toArray(this.adapter.getCurrentConfig?.()?.['columns'])
6429
+ .map((column) => this.toRecord(column))
6430
+ .filter((column) => !!column);
6431
+ const labelByField = new Map();
6432
+ for (const column of columns) {
6433
+ const field = this.stringValue(column['field']);
6434
+ if (!field)
6435
+ continue;
6436
+ labelByField.set(field, this.stringValue(column['header']) || this.humanizeField(field));
6437
+ }
6438
+ const fields = Array.from(new Set(rows.flatMap((row) => Object.keys(row))))
6439
+ .filter((field) => this.selectedRecordAnalyticalFieldAllowed(field, rows));
6440
+ const nameField = fields.find((field) => ['nome', 'nome completo', 'funcionario', 'funcionário', 'name'].includes(this.normalizeLabel(field)))
6441
+ ?? fields.find((field) => this.normalizeLabel(field).includes('nome'))
6442
+ ?? fields[0];
6443
+ const names = nameField
6444
+ ? rows.map((row) => this.readableCellValue(row[nameField])).filter((value) => !!value)
6445
+ : [];
6446
+ const common = fields
6447
+ .map((field) => {
6448
+ const values = rows.map((row) => this.readableCellValue(row[field])).filter((value) => !!value);
6449
+ const unique = Array.from(new Set(values));
6450
+ return unique.length === 1 ? `${labelByField.get(field) || this.humanizeField(field)}: ${unique[0]}` : null;
6451
+ })
6452
+ .filter((entry) => !!entry)
6453
+ .slice(0, 5);
6454
+ const numericRanges = fields
6455
+ .map((field) => {
6456
+ const values = rows.map((row) => this.numberValue(row[field])).filter((value) => value !== null);
6457
+ if (values.length < 2)
6458
+ return null;
6459
+ const min = Math.min(...values);
6460
+ const max = Math.max(...values);
6461
+ if (min === max)
6462
+ return null;
6463
+ return `${labelByField.get(field) || this.humanizeField(field)}: ${this.formatAnalysisNumber(min)} até ${this.formatAnalysisNumber(max)}`;
6464
+ })
6465
+ .filter((entry) => !!entry)
6466
+ .slice(0, 4);
6467
+ const dateRanges = fields
6468
+ .map((field) => {
6469
+ const timestamps = rows
6470
+ .map((row) => this.dateTimestamp(row[field]))
6471
+ .filter((value) => value !== null);
6472
+ if (timestamps.length < 2)
6473
+ return null;
6474
+ const min = Math.min(...timestamps);
6475
+ const max = Math.max(...timestamps);
6476
+ if (min === max)
6477
+ return null;
6478
+ return `${labelByField.get(field) || this.humanizeField(field)}: ${this.formatAnalysisDate(min)} até ${this.formatAnalysisDate(max)}`;
6479
+ })
6480
+ .filter((entry) => !!entry)
6481
+ .slice(0, 3);
6482
+ const differences = fields
6483
+ .map((field) => {
6484
+ const values = rows.map((row) => this.readableCellValue(row[field])).filter((value) => !!value);
6485
+ const unique = Array.from(new Set(values));
6486
+ if (unique.length <= 1 || unique.length > 5)
6487
+ return null;
6488
+ return `${labelByField.get(field) || this.humanizeField(field)}: ${unique.join(', ')}`;
6489
+ })
6490
+ .filter((entry) => !!entry)
6491
+ .slice(0, 5);
6492
+ const lines = [
6493
+ `Analisei ${selectedCount} registro${selectedCount === 1 ? '' : 's'} selecionado${selectedCount === 1 ? '' : 's'} sem alterar a tabela.`,
6494
+ names.length ? `Selecionados: ${names.slice(0, 6).join(', ')}.` : null,
6495
+ common.length ? `Em comum: ${common.join('; ')}.` : 'Não encontrei um valor idêntico em todos os campos visíveis enviados.',
6496
+ numericRanges.length ? `Faixas numéricas observadas: ${numericRanges.join('; ')}.` : null,
6497
+ dateRanges.length ? `Períodos observados: ${dateRanges.join('; ')}.` : null,
6498
+ differences.length ? `Diferenças relevantes: ${differences.join('; ')}.` : null,
6499
+ ].filter((line) => !!line);
6500
+ return lines.join('\n');
6501
+ }
6502
+ selectedRecordAnalyticalFieldAllowed(field, rows) {
6503
+ const normalized = this.normalizeLabel(field);
6504
+ if (!normalized)
6505
+ return false;
6506
+ if (normalized === 'id' || normalized.endsWith(' id') || normalized.endsWith(' ids'))
6507
+ return false;
6508
+ return rows.some((row) => this.readableCellValue(row[field]));
6509
+ }
6510
+ readableCellValue(value) {
6511
+ if (value === null || value === undefined)
6512
+ return '';
6513
+ if (typeof value === 'boolean')
6514
+ return value ? 'Sim' : 'Não';
6515
+ if (typeof value === 'number')
6516
+ return Number.isFinite(value) ? this.formatAnalysisNumber(value) : '';
6517
+ if (value instanceof Date)
6518
+ return this.formatAnalysisDate(value.getTime());
6519
+ if (typeof value === 'string')
6520
+ return value.trim();
6521
+ if (Array.isArray(value)) {
6522
+ return value.map((entry) => this.readableCellValue(entry)).filter(Boolean).join(', ');
6523
+ }
6524
+ return '';
6525
+ }
6526
+ dateTimestamp(value) {
6527
+ const raw = value instanceof Date
6528
+ ? value.getTime()
6529
+ : typeof value === 'string'
6530
+ ? Date.parse(value)
6531
+ : Number.NaN;
6532
+ return Number.isFinite(raw) ? raw : null;
6533
+ }
6534
+ formatAnalysisDate(timestamp) {
6535
+ return new Intl.DateTimeFormat('pt-BR', {
6536
+ day: '2-digit',
6537
+ month: '2-digit',
6538
+ year: 'numeric',
6539
+ }).format(new Date(timestamp));
6540
+ }
6541
+ formatAnalysisNumber(value) {
6542
+ return new Intl.NumberFormat('pt-BR', {
6543
+ maximumFractionDigits: 2,
6544
+ }).format(value);
6545
+ }
6215
6546
  visualPresentationSurfaceRuntimeMismatch(response, request) {
6216
6547
  if (!request)
6217
6548
  return null;
@@ -7960,6 +8291,7 @@ class TableAgenticAuthoringTurnFlow {
7960
8291
  withCapabilitySystemMessages(messages, contextHints, prompt = '') {
7961
8292
  const policies = [
7962
8293
  this.buildRecommendedCapabilitiesSystemPolicy(contextHints),
8294
+ this.buildSelectedRecordsAnalysisSystemPolicy(contextHints),
7963
8295
  this.buildComponentPresentationAuthoringSystemPolicy(contextHints, prompt),
7964
8296
  this.buildComponentLayoutAuthoringSystemPolicy(contextHints, prompt),
7965
8297
  this.buildFilterExpressionSystemPolicy(contextHints),
@@ -7967,6 +8299,40 @@ class TableAgenticAuthoringTurnFlow {
7967
8299
  ].filter((message) => !!message);
7968
8300
  return policies.length ? [...policies, ...messages] : messages;
7969
8301
  }
8302
+ buildSelectedRecordsAnalysisSystemPolicy(contextHints) {
8303
+ const selectedRecordsContext = this.selectedRecordsContextRecord(contextHints);
8304
+ if (!selectedRecordsContext)
8305
+ return null;
8306
+ const selectedCount = this.numberValue(selectedRecordsContext['selectedCount']);
8307
+ const sampleRows = Array.isArray(selectedRecordsContext['sampleRows'])
8308
+ ? selectedRecordsContext['sampleRows']
8309
+ : [];
8310
+ if ((!selectedCount || selectedCount <= 0) && !sampleRows.length)
8311
+ return null;
8312
+ const fields = Array.isArray(selectedRecordsContext['fields'])
8313
+ ? selectedRecordsContext['fields'].map((field) => this.stringValue(field)).filter((field) => !!field)
8314
+ : [];
8315
+ const candidateLabels = Array.isArray(selectedRecordsContext['filterCandidates'])
8316
+ ? selectedRecordsContext['filterCandidates']
8317
+ .map((candidate) => this.stringValue(this.toRecord(candidate)?.['label']))
8318
+ .filter((label) => !!label)
8319
+ .slice(0, 8)
8320
+ : [];
8321
+ return {
8322
+ role: 'system',
8323
+ content: [
8324
+ 'Praxis table selected-record analysis policy:',
8325
+ '- selectedRecordsContext is a governed read-only grounding source for questions about the currently selected records.',
8326
+ '- When the user asks to understand, summarize, compare, inspect, explain, rank, or identify commonalities/differences in selected records, return type "info" with a human analytical answer grounded in selectedRecordsContext.sampleRows. Do not return componentEditPlan, table.filter.apply, export, or row-action operations for that consultative question.',
8327
+ '- Only materialize table changes, filters, exports, navigation, or row actions when the user explicitly asks to apply/configure/create/open/export something. Otherwise, answer first and optionally mention next actions as suggestions.',
8328
+ '- If the selected sample rows are insufficient to answer a business question, explain the missing data succinctly instead of converting the question into a selected-record filter clarification.',
8329
+ '- Keep the answer human-facing: use visible labels and values; do not expose raw ids, endpoint paths, operationIds, schema keys, or payload examples unless the user asks for technical details.',
8330
+ `Selected records declared count: ${selectedCount ?? sampleRows.length}.`,
8331
+ fields.length ? `Selected record fields: ${fields.join(', ')}.` : 'Selected record fields: inferred from sampleRows.',
8332
+ candidateLabels.length ? `Selection-derived filter candidates available for explicit filter requests: ${candidateLabels.join(', ')}.` : 'Selection-derived filter candidates available for explicit filter requests: none declared.',
8333
+ ].join('\n'),
8334
+ };
8335
+ }
7970
8336
  buildRecommendedCapabilitiesSystemPolicy(contextHints) {
7971
8337
  const recommendedIntent = this.toRecord(contextHints?.['recommendedIntent']);
7972
8338
  const intentId = this.stringValue(recommendedIntent?.['id'])
@@ -8105,6 +8471,7 @@ class TableAgenticAuthoringTurnFlow {
8105
8471
  '- Use the declared filterFieldCatalog as the canonical grounding for row filtering decisions.',
8106
8472
  '- When the user asks to restrict, search, list, show only, compare values, define ranges, or filter rows, prefer a table.filter.apply runtime operation over visual renderer or column formatting operations.',
8107
8473
  ...(this.promptRequestsVisualOrStructuralEdit(normalizedPrompt)
8474
+ || this.promptRequestsCompoundColumnPresentationEdit(normalizedPrompt)
8108
8475
  ? ['- This turn contains an explicit visual or structural table-edit signal; do not reinterpret that signal as a row filter unless the user also explicitly asks to restrict table rows.']
8109
8476
  : []),
8110
8477
  '- For numeric and date comparisons, use the declared range filter field from filterFieldCatalog instead of the raw listing column field.',
@@ -8255,6 +8622,11 @@ class TableAgenticAuthoringTurnFlow {
8255
8622
  normalizeSelectedRecordFilterClarification(response, request) {
8256
8623
  if (response.type !== 'clarification' || this.selectedRecordsCountForTurn <= 0)
8257
8624
  return null;
8625
+ const normalizedPrompt = this.normalizeLabel(request?.prompt ?? '');
8626
+ if (this.promptRequestsVisualOrStructuralEdit(normalizedPrompt)
8627
+ || this.promptRequestsCompoundColumnPresentationEdit(normalizedPrompt)) {
8628
+ return null;
8629
+ }
8258
8630
  const contextHints = this.contextHintsFor(request);
8259
8631
  const candidates = this.selectedRecordFilterCandidates(contextHints);
8260
8632
  if (!candidates.length)
@@ -1,7 +1,7 @@
1
1
  import { firstValueFrom } from 'rxjs';
2
2
  import { BaseAiAdapter, createComponentAuthoringContext } from '@praxisui/ai';
3
3
  import { PRAXIS_GLOBAL_ACTION_CATALOG, deepMerge } from '@praxisui/core';
4
- import { G as TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS, k as PRAXIS_TABLE_AUTHORING_MANIFEST, T as TABLE_AI_CAPABILITIES, W as coerceTableComponentEditPlans, Y as compileTableComponentEditPlans, I as TASK_PRESETS, a1 as getTableComponentEditPlanCapabilities, y as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, w as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, z as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, H as TABLE_COMPONENT_EDIT_PLAN_VERSION, x as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, E as TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table-praxisui-table-DXpnCND9.mjs';
4
+ import { G as TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS, k as PRAXIS_TABLE_AUTHORING_MANIFEST, T as TABLE_AI_CAPABILITIES, W as coerceTableComponentEditPlans, Y as compileTableComponentEditPlans, I as TASK_PRESETS, a1 as getTableComponentEditPlanCapabilities, y as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, w as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, z as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, H as TABLE_COMPONENT_EDIT_PLAN_VERSION, x as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, E as TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table-praxisui-table-BKb6n5uc.mjs';
5
5
 
6
6
  const TABLE_COMPONENT_CONTEXT_PACK = {
7
7
  version: 'v1',
@@ -1 +1 @@
1
- export { A as AnalyticsTableConfigAdapterService, a as AnalyticsTableContractService, b as AnalyticsTableStatsApiService, B as BOOLEAN_PRESETS, c as BehaviorConfigEditorComponent, C as CURRENCY_PRESETS, d as ColumnsConfigEditorComponent, D as DATE_PRESETS, e as DataFormatterComponent, f as DataFormattingService, F as FORMULA_TEMPLATES, g as FilterConfigService, h as FilterSettingsComponent, i as FormulaGeneratorService, J as JsonConfigEditorComponent, M as MessagesLocalizationEditorComponent, N as NUMBER_PRESETS, P as PERCENTAGE_PRESETS, j as PRAXIS_FILTER_COMPONENT_METADATA, k as PRAXIS_TABLE_AUTHORING_MANIFEST, l as PRAXIS_TABLE_COMPONENT_METADATA, m as PRAXIS_TABLE_TOOLBAR_DEFAULT_APPEARANCE, n as PRAXIS_TABLE_TOOLBAR_TOKEN_PRESETS, o as PraxisFilter, p as PraxisFilterWidgetConfigEditor, q as PraxisTable, r as PraxisTableConfigEditor, s as PraxisTableInlineAuthoringEditorComponent, t as PraxisTableToolbar, u as PraxisTableWidgetConfigEditor, S as STRING_PRESETS, T as TABLE_AI_CAPABILITIES, v as TABLE_COMPONENT_AI_CAPABILITIES, w as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, x as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, y as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, z as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, E as TABLE_COMPONENT_EDIT_PLAN_KIND, H as TABLE_COMPONENT_EDIT_PLAN_VERSION, I as TASK_PRESETS, K as TableDefaultsProvider, L as TableRulesEditorComponent, O as ToolbarActionsEditorComponent, V as ValueMappingEditorComponent, Q as VisualFormulaBuilderComponent, R as buildTableApplyPlan, U as coerceTableComponentEditPlan, W as coerceTableComponentEditPlans, X as compileTableComponentEditPlan, Y as compileTableComponentEditPlans, Z as createTableAuthoringDocument, _ as getActionId, $ as getEnum, a0 as getTableCapabilities, a1 as getTableComponentEditPlanCapabilities, a2 as isTableRendererSupportedByRichContentP0, a3 as mapTableRendererToRichContentP0, a4 as normalizeTableAuthoringDocument, a5 as parseLegacyOrTableDocument, a6 as providePraxisFilterMetadata, a7 as providePraxisTableMetadata, a8 as providePraxisTableToolbarAppearance, a9 as serializeTableAuthoringDocument, aa as toCanonicalTableConfig, ab as validateTableAuthoringDocument } from './praxisui-table-praxisui-table-DXpnCND9.mjs';
1
+ export { A as AnalyticsTableConfigAdapterService, a as AnalyticsTableContractService, b as AnalyticsTableStatsApiService, B as BOOLEAN_PRESETS, c as BehaviorConfigEditorComponent, C as CURRENCY_PRESETS, d as ColumnsConfigEditorComponent, D as DATE_PRESETS, e as DataFormatterComponent, f as DataFormattingService, F as FORMULA_TEMPLATES, g as FilterConfigService, h as FilterSettingsComponent, i as FormulaGeneratorService, J as JsonConfigEditorComponent, M as MessagesLocalizationEditorComponent, N as NUMBER_PRESETS, P as PERCENTAGE_PRESETS, j as PRAXIS_FILTER_COMPONENT_METADATA, k as PRAXIS_TABLE_AUTHORING_MANIFEST, l as PRAXIS_TABLE_COMPONENT_METADATA, m as PRAXIS_TABLE_TOOLBAR_DEFAULT_APPEARANCE, n as PRAXIS_TABLE_TOOLBAR_TOKEN_PRESETS, o as PraxisFilter, p as PraxisFilterWidgetConfigEditor, q as PraxisTable, r as PraxisTableConfigEditor, s as PraxisTableInlineAuthoringEditorComponent, t as PraxisTableToolbar, u as PraxisTableWidgetConfigEditor, S as STRING_PRESETS, T as TABLE_AI_CAPABILITIES, v as TABLE_COMPONENT_AI_CAPABILITIES, w as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, x as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, y as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, z as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, E as TABLE_COMPONENT_EDIT_PLAN_KIND, H as TABLE_COMPONENT_EDIT_PLAN_VERSION, I as TASK_PRESETS, K as TableDefaultsProvider, L as TableRulesEditorComponent, O as ToolbarActionsEditorComponent, V as ValueMappingEditorComponent, Q as VisualFormulaBuilderComponent, R as buildTableApplyPlan, U as coerceTableComponentEditPlan, W as coerceTableComponentEditPlans, X as compileTableComponentEditPlan, Y as compileTableComponentEditPlans, Z as createTableAuthoringDocument, _ as getActionId, $ as getEnum, a0 as getTableCapabilities, a1 as getTableComponentEditPlanCapabilities, a2 as isTableRendererSupportedByRichContentP0, a3 as mapTableRendererToRichContentP0, a4 as normalizeTableAuthoringDocument, a5 as parseLegacyOrTableDocument, a6 as providePraxisFilterMetadata, a7 as providePraxisTableMetadata, a8 as providePraxisTableToolbarAppearance, a9 as serializeTableAuthoringDocument, aa as toCanonicalTableConfig, ab as validateTableAuthoringDocument } from './praxisui-table-praxisui-table-BKb6n5uc.mjs';
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@praxisui/table",
3
- "version": "8.0.0-beta.94",
3
+ "version": "8.0.0-beta.96",
4
4
  "description": "Advanced data table for Angular (Praxis UI) with editing, filtering, sorting, virtualization, and settings panel integration.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
- "@praxisui/ai": "^8.0.0-beta.94",
9
- "@praxisui/core": "^8.0.0-beta.94",
10
- "@praxisui/dynamic-fields": "^8.0.0-beta.94",
11
- "@praxisui/dynamic-form": "^8.0.0-beta.94",
12
- "@praxisui/metadata-editor": "^8.0.0-beta.94",
13
- "@praxisui/rich-content": "^8.0.0-beta.94",
14
- "@praxisui/settings-panel": "^8.0.0-beta.94",
15
- "@praxisui/table-rule-builder": "^8.0.0-beta.94",
8
+ "@praxisui/ai": "^8.0.0-beta.96",
9
+ "@praxisui/core": "^8.0.0-beta.96",
10
+ "@praxisui/dynamic-fields": "^8.0.0-beta.96",
11
+ "@praxisui/dynamic-form": "^8.0.0-beta.96",
12
+ "@praxisui/metadata-editor": "^8.0.0-beta.96",
13
+ "@praxisui/rich-content": "^8.0.0-beta.96",
14
+ "@praxisui/settings-panel": "^8.0.0-beta.96",
15
+ "@praxisui/table-rule-builder": "^8.0.0-beta.96",
16
16
  "@angular/cdk": "^21.0.0",
17
17
  "@angular/forms": "^21.0.0",
18
18
  "@angular/material": "^21.0.0",
19
19
  "@angular/router": "^21.0.0",
20
- "@praxisui/dialog": "^8.0.0-beta.94",
20
+ "@praxisui/dialog": "^8.0.0-beta.96",
21
21
  "rxjs": "~7.8.0"
22
22
  },
23
23
  "dependencies": {