@gyramais/gyra-design 0.2.56 → 0.2.58

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.
Files changed (24) hide show
  1. package/dist/components/ChatWidget/ChatBlocks.d.ts +29 -0
  2. package/dist/components/ChatWidget/ChatMarkdown.d.ts +10 -0
  3. package/dist/components/ChatWidget/ChatMessageBubble.d.ts +10 -1
  4. package/dist/components/ChatWidget/ChatWidget.d.ts +26 -3
  5. package/dist/components/ChatWidget/index.d.ts +2 -0
  6. package/dist/components/ChatWidget/index.js +25 -7
  7. package/dist/components/ChatWidget/index.js.map +1 -1
  8. package/dist/components/ChatWidget/types.d.ts +52 -0
  9. package/dist/components/Rail/index.js +1 -1
  10. package/dist/components/Rail/index.js.map +1 -1
  11. package/dist/emails/balance-sheet-finalized/components/ChatWidget/ChatBlocks.d.ts +29 -0
  12. package/dist/emails/balance-sheet-finalized/components/ChatWidget/ChatMarkdown.d.ts +10 -0
  13. package/dist/emails/balance-sheet-finalized/components/ChatWidget/ChatMessageBubble.d.ts +10 -1
  14. package/dist/emails/balance-sheet-finalized/components/ChatWidget/ChatWidget.d.ts +26 -3
  15. package/dist/emails/balance-sheet-finalized/components/ChatWidget/index.d.ts +2 -0
  16. package/dist/emails/balance-sheet-finalized/components/ChatWidget/types.d.ts +52 -0
  17. package/dist/emails/email/components/ChatWidget/ChatBlocks.d.ts +29 -0
  18. package/dist/emails/email/components/ChatWidget/ChatMarkdown.d.ts +10 -0
  19. package/dist/emails/email/components/ChatWidget/ChatMessageBubble.d.ts +10 -1
  20. package/dist/emails/email/components/ChatWidget/ChatWidget.d.ts +26 -3
  21. package/dist/emails/email/components/ChatWidget/index.d.ts +2 -0
  22. package/dist/emails/email/components/ChatWidget/types.d.ts +52 -0
  23. package/dist/index.css +286 -2
  24. package/package.json +1 -1
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { ChatActionBlock, ChatBlock, ChatLinkBlock, ChatVideoBlock } from "./types";
3
+ export interface ChatLinkCardProps {
4
+ block: ChatLinkBlock;
5
+ /** Recebe o `href` relativo. Sem ele, o link interno vira navegacao comum. */
6
+ onNavigate?: (href: string) => void;
7
+ }
8
+ declare const ChatLinkCard: ({ block, onNavigate }: ChatLinkCardProps) => React.JSX.Element | null;
9
+ export interface ChatVideoCardProps {
10
+ block: ChatVideoBlock;
11
+ }
12
+ declare const ChatVideoCard: ({ block }: ChatVideoCardProps) => React.JSX.Element | null;
13
+ export interface ChatActionCardProps {
14
+ block: ChatActionBlock;
15
+ /** Manda a resposta como a proxima mensagem do usuario. */
16
+ onRespond?: (text: string) => void | Promise<void>;
17
+ /** Trava os botoes (ja respondido, ou a conversa seguiu adiante). */
18
+ locked?: boolean;
19
+ onNavigate?: (href: string) => void;
20
+ }
21
+ declare const ChatActionCard: ({ block, onRespond, locked, onNavigate }: ChatActionCardProps) => React.JSX.Element;
22
+ export interface ChatBlocksProps {
23
+ blocks: ChatBlock[];
24
+ onNavigate?: (href: string) => void;
25
+ onRespond?: (text: string) => void | Promise<void>;
26
+ actionsLocked?: boolean;
27
+ }
28
+ declare const ChatBlocks: ({ blocks, onNavigate, onRespond, actionsLocked }: ChatBlocksProps) => React.JSX.Element;
29
+ export { ChatBlocks, ChatLinkCard, ChatVideoCard, ChatActionCard };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ export declare const isSafeHttpUrl: (href: string | undefined | null) => boolean;
3
+ /** Caminho interno do app ("/relatorios/123"). "//host" e protocolo relativo, nao conta. */
4
+ export declare const isInternalHref: (href: string | undefined | null) => boolean;
5
+ interface ChatMarkdownProps {
6
+ children: string;
7
+ onNavigate?: (href: string) => void;
8
+ }
9
+ declare const ChatMarkdown: ({ children, onNavigate }: ChatMarkdownProps) => React.JSX.Element;
10
+ export { ChatMarkdown };
@@ -2,6 +2,15 @@ import React from "react";
2
2
  import { ChatMessage } from "./types";
3
3
  interface ChatMessageBubbleProps {
4
4
  message: ChatMessage;
5
+ /** Esta e a resposta sendo gerada agora (mostra o passo atual). */
6
+ streaming?: boolean;
7
+ /** Rotulo do "pensando" quando a mensagem nao traz `steps` proprios. */
8
+ statusLabel?: string;
9
+ onNavigate?: (href: string) => void;
10
+ /** Resposta dos cards de confirmacao, pelo mesmo caminho do composer. */
11
+ onRespond?: (text: string) => void | Promise<void>;
12
+ /** Trava os cards de confirmacao desta mensagem. */
13
+ actionsLocked?: boolean;
5
14
  }
6
- declare const ChatMessageBubble: ({ message }: ChatMessageBubbleProps) => React.JSX.Element;
15
+ declare const ChatMessageBubble: ({ message, streaming, statusLabel, onNavigate, onRespond, actionsLocked, }: ChatMessageBubbleProps) => React.JSX.Element;
7
16
  export { ChatMessageBubble };
@@ -1,13 +1,36 @@
1
1
  import React from "react";
2
2
  import "./index.css";
3
- import { ChatMessage, ChatStatus, ChatTheme } from "./types";
3
+ import { ChatAttachment, ChatMessage, ChatSendOptions, ChatStatus, ChatTheme } from "./types";
4
4
  export interface ChatWidgetProps {
5
5
  open: boolean;
6
6
  onOpenChange: (open: boolean) => void;
7
7
  messages: ChatMessage[];
8
8
  status: ChatStatus;
9
- onSend: (text: string) => void | Promise<void>;
9
+ /**
10
+ * `options.attachmentIds` so vem quando ha anexos prontos no composer. Os
11
+ * cards de confirmacao tambem respondem por aqui (sem o segundo argumento).
12
+ */
13
+ onSend: (text: string, options?: ChatSendOptions) => void | Promise<void>;
10
14
  onReset?: () => void;
15
+ /**
16
+ * Links internos (href comecando com "/") dos cards e do markdown. Sem ele,
17
+ * viram navegacao comum do navegador.
18
+ */
19
+ onNavigate?: (href: string) => void;
20
+ /**
21
+ * Com ele, o composer ganha o clipe de anexo. Recebe o arquivo escolhido e
22
+ * devolve `{ id, name }` do upload; um erro lancado vira aviso no composer
23
+ * (use a `message` do erro para o texto em portugues).
24
+ */
25
+ onAttach?: (file: File) => Promise<ChatAttachment>;
26
+ /** `accept` do seletor de arquivos. */
27
+ attachAccept?: string;
28
+ /** Dica de formatos e tamanho, no tooltip do clipe e abaixo dos anexos. */
29
+ attachHint?: string;
30
+ /** Maximo de anexos por mensagem. */
31
+ maxAttachments?: number;
32
+ /** Texto ao lado dos pontinhos enquanto a resposta nao comeca a chegar. */
33
+ statusLabel?: string;
11
34
  theme?: ChatTheme;
12
35
  title?: string;
13
36
  triggerLabel?: string;
@@ -26,5 +49,5 @@ export interface ChatWidgetProps {
26
49
  headerExtra?: React.ReactNode;
27
50
  belowHeader?: React.ReactNode;
28
51
  }
29
- declare const ChatWidget: ({ open, onOpenChange, messages, status, onSend, onReset, theme, title, triggerLabel, placeholder, emptyTitle, emptySubtitle, suggestions, collapsedHeight, expandedHeight, collapsedWidth, expandedWidth, defaultExpanded, expandable, className, zIndex, headerExtra, belowHeader, }: ChatWidgetProps) => React.JSX.Element;
52
+ declare const ChatWidget: ({ open, onOpenChange, messages, status, onSend, onReset, onNavigate, onAttach, attachAccept, attachHint, maxAttachments, statusLabel, theme, title, triggerLabel, placeholder, emptyTitle, emptySubtitle, suggestions, collapsedHeight, expandedHeight, collapsedWidth, expandedWidth, defaultExpanded, expandable, className, zIndex, headerExtra, belowHeader, }: ChatWidgetProps) => React.JSX.Element;
30
53
  export { ChatWidget };
@@ -1,3 +1,5 @@
1
1
  export * from "./ChatWidget";
2
2
  export * from "./ChatMessageBubble";
3
+ export * from "./ChatBlocks";
4
+ export { ChatMarkdown } from "./ChatMarkdown";
3
5
  export * from "./types";
@@ -10,9 +10,61 @@ export interface ChatCitation {
10
10
  excerpt: string;
11
11
  url: string;
12
12
  }
13
+ /**
14
+ * Atalho com botao. `href` relativo (comeca com "/") navega dentro do app pelo
15
+ * `onNavigate` do widget; `https://...` abre em nova aba.
16
+ */
17
+ export interface ChatLinkBlock {
18
+ kind: 'link';
19
+ href: string;
20
+ label: string;
21
+ description?: string;
22
+ }
23
+ /** Video curto (mp4) tocando dentro do chat, com opcao de abrir grande. */
24
+ export interface ChatVideoBlock {
25
+ kind: 'video';
26
+ title: string;
27
+ src: string;
28
+ poster?: string;
29
+ description?: string;
30
+ docsUrl?: string;
31
+ }
32
+ /**
33
+ * Pedido de confirmacao. O clique manda `confirmMessage` (ou `cancelMessage`)
34
+ * como a proxima mensagem do usuario, pelo mesmo `onSend` do composer.
35
+ */
36
+ export interface ChatActionBlock {
37
+ kind: 'action';
38
+ actionId: string;
39
+ title: string;
40
+ /** Markdown com o resumo do que vai ser feito. */
41
+ preview: string;
42
+ confirmLabel: string;
43
+ cancelLabel: string;
44
+ confirmMessage: string;
45
+ cancelMessage: string;
46
+ }
47
+ export type ChatBlock = ChatLinkBlock | ChatVideoBlock | ChatActionBlock;
48
+ export interface ChatAttachment {
49
+ id: string;
50
+ name: string;
51
+ }
52
+ /** Segundo argumento opcional de `onSend`. */
53
+ export interface ChatSendOptions {
54
+ attachmentIds?: string[];
55
+ }
13
56
  export interface ChatMessage {
14
57
  role: ChatRole;
15
58
  content: string;
16
59
  toolCalls?: ChatToolCall[];
17
60
  citations?: ChatCitation[];
61
+ /** Cards ricos (link, video, confirmacao) abaixo do texto do assistente. */
62
+ blocks?: ChatBlock[];
63
+ /** Arquivos enviados junto com a mensagem do usuario (viram chips). */
64
+ attachments?: ChatAttachment[];
65
+ /**
66
+ * Passos do turno em andamento ("Lendo o relatorio..."). So o ultimo
67
+ * aparece, e so enquanto a resposta esta sendo gerada.
68
+ */
69
+ steps?: string[];
18
70
  }
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { ChatActionBlock, ChatBlock, ChatLinkBlock, ChatVideoBlock } from "./types";
3
+ export interface ChatLinkCardProps {
4
+ block: ChatLinkBlock;
5
+ /** Recebe o `href` relativo. Sem ele, o link interno vira navegacao comum. */
6
+ onNavigate?: (href: string) => void;
7
+ }
8
+ declare const ChatLinkCard: ({ block, onNavigate }: ChatLinkCardProps) => React.JSX.Element | null;
9
+ export interface ChatVideoCardProps {
10
+ block: ChatVideoBlock;
11
+ }
12
+ declare const ChatVideoCard: ({ block }: ChatVideoCardProps) => React.JSX.Element | null;
13
+ export interface ChatActionCardProps {
14
+ block: ChatActionBlock;
15
+ /** Manda a resposta como a proxima mensagem do usuario. */
16
+ onRespond?: (text: string) => void | Promise<void>;
17
+ /** Trava os botoes (ja respondido, ou a conversa seguiu adiante). */
18
+ locked?: boolean;
19
+ onNavigate?: (href: string) => void;
20
+ }
21
+ declare const ChatActionCard: ({ block, onRespond, locked, onNavigate }: ChatActionCardProps) => React.JSX.Element;
22
+ export interface ChatBlocksProps {
23
+ blocks: ChatBlock[];
24
+ onNavigate?: (href: string) => void;
25
+ onRespond?: (text: string) => void | Promise<void>;
26
+ actionsLocked?: boolean;
27
+ }
28
+ declare const ChatBlocks: ({ blocks, onNavigate, onRespond, actionsLocked }: ChatBlocksProps) => React.JSX.Element;
29
+ export { ChatBlocks, ChatLinkCard, ChatVideoCard, ChatActionCard };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ export declare const isSafeHttpUrl: (href: string | undefined | null) => boolean;
3
+ /** Caminho interno do app ("/relatorios/123"). "//host" e protocolo relativo, nao conta. */
4
+ export declare const isInternalHref: (href: string | undefined | null) => boolean;
5
+ interface ChatMarkdownProps {
6
+ children: string;
7
+ onNavigate?: (href: string) => void;
8
+ }
9
+ declare const ChatMarkdown: ({ children, onNavigate }: ChatMarkdownProps) => React.JSX.Element;
10
+ export { ChatMarkdown };
@@ -2,6 +2,15 @@ import React from "react";
2
2
  import { ChatMessage } from "./types";
3
3
  interface ChatMessageBubbleProps {
4
4
  message: ChatMessage;
5
+ /** Esta e a resposta sendo gerada agora (mostra o passo atual). */
6
+ streaming?: boolean;
7
+ /** Rotulo do "pensando" quando a mensagem nao traz `steps` proprios. */
8
+ statusLabel?: string;
9
+ onNavigate?: (href: string) => void;
10
+ /** Resposta dos cards de confirmacao, pelo mesmo caminho do composer. */
11
+ onRespond?: (text: string) => void | Promise<void>;
12
+ /** Trava os cards de confirmacao desta mensagem. */
13
+ actionsLocked?: boolean;
5
14
  }
6
- declare const ChatMessageBubble: ({ message }: ChatMessageBubbleProps) => React.JSX.Element;
15
+ declare const ChatMessageBubble: ({ message, streaming, statusLabel, onNavigate, onRespond, actionsLocked, }: ChatMessageBubbleProps) => React.JSX.Element;
7
16
  export { ChatMessageBubble };
@@ -1,13 +1,36 @@
1
1
  import React from "react";
2
2
  import "./index.css";
3
- import { ChatMessage, ChatStatus, ChatTheme } from "./types";
3
+ import { ChatAttachment, ChatMessage, ChatSendOptions, ChatStatus, ChatTheme } from "./types";
4
4
  export interface ChatWidgetProps {
5
5
  open: boolean;
6
6
  onOpenChange: (open: boolean) => void;
7
7
  messages: ChatMessage[];
8
8
  status: ChatStatus;
9
- onSend: (text: string) => void | Promise<void>;
9
+ /**
10
+ * `options.attachmentIds` so vem quando ha anexos prontos no composer. Os
11
+ * cards de confirmacao tambem respondem por aqui (sem o segundo argumento).
12
+ */
13
+ onSend: (text: string, options?: ChatSendOptions) => void | Promise<void>;
10
14
  onReset?: () => void;
15
+ /**
16
+ * Links internos (href comecando com "/") dos cards e do markdown. Sem ele,
17
+ * viram navegacao comum do navegador.
18
+ */
19
+ onNavigate?: (href: string) => void;
20
+ /**
21
+ * Com ele, o composer ganha o clipe de anexo. Recebe o arquivo escolhido e
22
+ * devolve `{ id, name }` do upload; um erro lancado vira aviso no composer
23
+ * (use a `message` do erro para o texto em portugues).
24
+ */
25
+ onAttach?: (file: File) => Promise<ChatAttachment>;
26
+ /** `accept` do seletor de arquivos. */
27
+ attachAccept?: string;
28
+ /** Dica de formatos e tamanho, no tooltip do clipe e abaixo dos anexos. */
29
+ attachHint?: string;
30
+ /** Maximo de anexos por mensagem. */
31
+ maxAttachments?: number;
32
+ /** Texto ao lado dos pontinhos enquanto a resposta nao comeca a chegar. */
33
+ statusLabel?: string;
11
34
  theme?: ChatTheme;
12
35
  title?: string;
13
36
  triggerLabel?: string;
@@ -26,5 +49,5 @@ export interface ChatWidgetProps {
26
49
  headerExtra?: React.ReactNode;
27
50
  belowHeader?: React.ReactNode;
28
51
  }
29
- declare const ChatWidget: ({ open, onOpenChange, messages, status, onSend, onReset, theme, title, triggerLabel, placeholder, emptyTitle, emptySubtitle, suggestions, collapsedHeight, expandedHeight, collapsedWidth, expandedWidth, defaultExpanded, expandable, className, zIndex, headerExtra, belowHeader, }: ChatWidgetProps) => React.JSX.Element;
52
+ declare const ChatWidget: ({ open, onOpenChange, messages, status, onSend, onReset, onNavigate, onAttach, attachAccept, attachHint, maxAttachments, statusLabel, theme, title, triggerLabel, placeholder, emptyTitle, emptySubtitle, suggestions, collapsedHeight, expandedHeight, collapsedWidth, expandedWidth, defaultExpanded, expandable, className, zIndex, headerExtra, belowHeader, }: ChatWidgetProps) => React.JSX.Element;
30
53
  export { ChatWidget };
@@ -1,3 +1,5 @@
1
1
  export * from "./ChatWidget";
2
2
  export * from "./ChatMessageBubble";
3
+ export * from "./ChatBlocks";
4
+ export { ChatMarkdown } from "./ChatMarkdown";
3
5
  export * from "./types";
@@ -10,9 +10,61 @@ export interface ChatCitation {
10
10
  excerpt: string;
11
11
  url: string;
12
12
  }
13
+ /**
14
+ * Atalho com botao. `href` relativo (comeca com "/") navega dentro do app pelo
15
+ * `onNavigate` do widget; `https://...` abre em nova aba.
16
+ */
17
+ export interface ChatLinkBlock {
18
+ kind: 'link';
19
+ href: string;
20
+ label: string;
21
+ description?: string;
22
+ }
23
+ /** Video curto (mp4) tocando dentro do chat, com opcao de abrir grande. */
24
+ export interface ChatVideoBlock {
25
+ kind: 'video';
26
+ title: string;
27
+ src: string;
28
+ poster?: string;
29
+ description?: string;
30
+ docsUrl?: string;
31
+ }
32
+ /**
33
+ * Pedido de confirmacao. O clique manda `confirmMessage` (ou `cancelMessage`)
34
+ * como a proxima mensagem do usuario, pelo mesmo `onSend` do composer.
35
+ */
36
+ export interface ChatActionBlock {
37
+ kind: 'action';
38
+ actionId: string;
39
+ title: string;
40
+ /** Markdown com o resumo do que vai ser feito. */
41
+ preview: string;
42
+ confirmLabel: string;
43
+ cancelLabel: string;
44
+ confirmMessage: string;
45
+ cancelMessage: string;
46
+ }
47
+ export type ChatBlock = ChatLinkBlock | ChatVideoBlock | ChatActionBlock;
48
+ export interface ChatAttachment {
49
+ id: string;
50
+ name: string;
51
+ }
52
+ /** Segundo argumento opcional de `onSend`. */
53
+ export interface ChatSendOptions {
54
+ attachmentIds?: string[];
55
+ }
13
56
  export interface ChatMessage {
14
57
  role: ChatRole;
15
58
  content: string;
16
59
  toolCalls?: ChatToolCall[];
17
60
  citations?: ChatCitation[];
61
+ /** Cards ricos (link, video, confirmacao) abaixo do texto do assistente. */
62
+ blocks?: ChatBlock[];
63
+ /** Arquivos enviados junto com a mensagem do usuario (viram chips). */
64
+ attachments?: ChatAttachment[];
65
+ /**
66
+ * Passos do turno em andamento ("Lendo o relatorio..."). So o ultimo
67
+ * aparece, e so enquanto a resposta esta sendo gerada.
68
+ */
69
+ steps?: string[];
18
70
  }
package/dist/index.css CHANGED
@@ -4512,6 +4512,8 @@ h6 {
4512
4512
  --chat-bg-code: var(--color-surface-secondary);
4513
4513
  --chat-bg-pre: var(--color-surface-secondary);
4514
4514
  --chat-bg-empty-icon: var(--gyra-blue-light);
4515
+ --chat-bg-card: var(--color-surface-primary);
4516
+ --chat-bg-media: var(--color-surface-secondary);
4515
4517
 
4516
4518
  --chat-fg-primary: var(--color-text-primary);
4517
4519
  --chat-fg-secondary: var(--color-text-secondary);
@@ -4525,6 +4527,7 @@ h6 {
4525
4527
  --chat-border: var(--color-border-secondary);
4526
4528
  --chat-border-strong: var(--color-border-primary);
4527
4529
  --chat-shadow: var(--shadow-modal);
4530
+ --chat-fg-danger: var(--color-danger);
4528
4531
  }
4529
4532
 
4530
4533
  .gyramais-chat-theme-dark {
@@ -4542,6 +4545,8 @@ h6 {
4542
4545
  --chat-bg-code: #262626;
4543
4546
  --chat-bg-pre: #171717;
4544
4547
  --chat-bg-empty-icon: rgba(139, 92, 246, 0.1);
4548
+ --chat-bg-card: #0a0a0a;
4549
+ --chat-bg-media: #000000;
4545
4550
 
4546
4551
  --chat-fg-primary: #e5e5e5;
4547
4552
  --chat-fg-secondary: #a3a3a3;
@@ -4555,6 +4560,7 @@ h6 {
4555
4560
  --chat-border: #262626;
4556
4561
  --chat-border-strong: #404040;
4557
4562
  --chat-shadow: 0 20px 50px -10px rgba(0, 0, 0, 0.6);
4563
+ --chat-fg-danger: #f87171;
4558
4564
  }
4559
4565
 
4560
4566
  /* ============================================================================
@@ -4864,9 +4870,18 @@ h6 {
4864
4870
  .gyramais-chat-typing {
4865
4871
  display: inline-flex;
4866
4872
  align-items: center;
4867
- gap: 6px;
4873
+ gap: 8px;
4874
+ font-size: 11.5px;
4868
4875
  color: var(--chat-fg-tertiary);
4869
4876
  }
4877
+ .gyramais-chat-typing-dots {
4878
+ display: inline-flex;
4879
+ align-items: center;
4880
+ gap: 6px;
4881
+ }
4882
+ .gyramais-chat-typing-label {
4883
+ color: var(--chat-fg-secondary);
4884
+ }
4870
4885
  .gyramais-chat-typing-dot {
4871
4886
  height: 4px;
4872
4887
  width: 4px;
@@ -5106,6 +5121,268 @@ h6 {
5106
5121
  font-size: 14px !important;
5107
5122
  }
5108
5123
 
5124
+ /* ============================================================================
5125
+ User bubble attachments
5126
+ ============================================================================ */
5127
+ .gyramais-chat-bubble-user-stack {
5128
+ display: flex;
5129
+ max-width: 82%;
5130
+ flex-direction: column;
5131
+ align-items: flex-end;
5132
+ gap: 4px;
5133
+ }
5134
+ .gyramais-chat-bubble-user-stack .gyramais-chat-bubble-user {
5135
+ max-width: 100%;
5136
+ }
5137
+
5138
+ .gyramais-chat-attachments {
5139
+ display: flex;
5140
+ flex-wrap: wrap;
5141
+ gap: 6px;
5142
+ }
5143
+ .gyramais-chat-attachments-sent {
5144
+ justify-content: flex-end;
5145
+ }
5146
+ .gyramais-chat-attachment {
5147
+ display: inline-flex;
5148
+ max-width: 100%;
5149
+ align-items: center;
5150
+ gap: 4px;
5151
+ border-radius: var(--radius-sm);
5152
+ border-width: 1px;
5153
+ border-color: var(--chat-border);
5154
+ background-color: var(--chat-bg-card);
5155
+ padding-left: 8px;
5156
+ padding-right: 8px;
5157
+ padding-top: 3px;
5158
+ padding-bottom: 3px;
5159
+ font-size: 11px;
5160
+ color: var(--chat-fg-secondary);
5161
+ }
5162
+ .gyramais-chat-attachment.is-uploading {
5163
+ opacity: 0.7;
5164
+ }
5165
+ .gyramais-chat-attachment-icon {
5166
+ height: 12px !important;
5167
+ width: 12px !important;
5168
+ flex-shrink: 0;
5169
+ font-size: 12px !important;
5170
+ }
5171
+ .gyramais-chat-attachment-name {
5172
+ max-width: 180px;
5173
+ overflow: hidden;
5174
+ text-overflow: ellipsis;
5175
+ white-space: nowrap;
5176
+ }
5177
+ .gyramais-chat-attachment-status {
5178
+ font-style: italic;
5179
+ color: var(--chat-fg-tertiary);
5180
+ }
5181
+ .gyramais-chat-attachment-remove {
5182
+ display: inline-flex;
5183
+ cursor: pointer;
5184
+ align-items: center;
5185
+ justify-content: center;
5186
+ border-radius: var(--radius-xs);
5187
+ border-style: none;
5188
+ background-color: transparent;
5189
+ padding: 1px;
5190
+ color: var(--chat-fg-tertiary);
5191
+ }
5192
+ .gyramais-chat-attachment-remove:hover {
5193
+ background-color: var(--chat-bg-icon-hover);
5194
+ color: var(--chat-fg-primary);
5195
+ }
5196
+ .gyramais-chat-attachment-remove-icon {
5197
+ height: 12px !important;
5198
+ width: 12px !important;
5199
+ font-size: 12px !important;
5200
+ }
5201
+
5202
+ /* Composer: anexos acima do campo de texto */
5203
+ .gyramais-chat-composer-attachments {
5204
+ margin-bottom: 8px;
5205
+ display: flex;
5206
+ flex-direction: column;
5207
+ gap: 4px;
5208
+ }
5209
+ .gyramais-chat-attach-hint {
5210
+ font-size: 10.5px;
5211
+ color: var(--chat-fg-tertiary);
5212
+ }
5213
+ .gyramais-chat-attach-error {
5214
+ font-size: 11px;
5215
+ color: var(--chat-fg-danger);
5216
+ }
5217
+ .gyramais-chat-file-input {
5218
+ display: none;
5219
+ }
5220
+ .gyramais-chat-attach {
5221
+ display: inline-flex;
5222
+ height: 24px;
5223
+ width: 24px;
5224
+ flex-shrink: 0;
5225
+ cursor: pointer;
5226
+ align-items: center;
5227
+ justify-content: center;
5228
+ border-radius: var(--radius-xs);
5229
+ border-style: none;
5230
+ background-color: transparent;
5231
+ color: var(--chat-fg-tertiary);
5232
+ transition-property: all;
5233
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
5234
+ transition-duration: 150ms;
5235
+ animation-duration: 150ms;
5236
+ }
5237
+ .gyramais-chat-attach:hover:not(:disabled) {
5238
+ background-color: var(--chat-bg-icon-hover);
5239
+ color: var(--chat-fg-primary);
5240
+ }
5241
+ .gyramais-chat-attach:disabled {
5242
+ cursor: not-allowed;
5243
+ opacity: 0.5;
5244
+ }
5245
+ .gyramais-chat-attach:focus-visible,
5246
+ .gyramais-chat-attachment-remove:focus-visible,
5247
+ .gyramais-chat-card-button:focus-visible {
5248
+ outline: 2px solid var(--chat-fg-accent);
5249
+ outline-offset: 1px;
5250
+ }
5251
+
5252
+ /* ============================================================================
5253
+ Rich blocks (link, video, action)
5254
+ ============================================================================ */
5255
+ .gyramais-chat-blocks {
5256
+ display: flex;
5257
+ flex-direction: column;
5258
+ gap: 8px;
5259
+ }
5260
+ .gyramais-chat-card {
5261
+ display: flex;
5262
+ min-width: 0px;
5263
+ flex-direction: column;
5264
+ gap: 8px;
5265
+ border-radius: var(--radius-md);
5266
+ border-width: 1px;
5267
+ border-color: var(--chat-border);
5268
+ background-color: var(--chat-bg-card);
5269
+ padding: 10px;
5270
+ }
5271
+ .gyramais-chat-link-card {
5272
+ flex-direction: row;
5273
+ align-items: center;
5274
+ justify-content: space-between;
5275
+ gap: 10px;
5276
+ }
5277
+ .gyramais-chat-card-body {
5278
+ display: flex;
5279
+ min-width: 0px;
5280
+ flex-direction: column;
5281
+ gap: 2px;
5282
+ }
5283
+ .gyramais-chat-card-label {
5284
+ font-size: 12.5px;
5285
+ font-weight: 500;
5286
+ color: var(--chat-fg-primary);
5287
+ overflow-wrap: anywhere;
5288
+ }
5289
+ .gyramais-chat-card-description {
5290
+ font-size: 11.5px;
5291
+ line-height: 1.45;
5292
+ color: var(--chat-fg-secondary);
5293
+ overflow-wrap: anywhere;
5294
+ }
5295
+ .gyramais-chat-card-actions {
5296
+ display: flex;
5297
+ flex-wrap: wrap;
5298
+ justify-content: flex-end;
5299
+ gap: 6px;
5300
+ }
5301
+ .gyramais-chat-card-button {
5302
+ display: inline-flex;
5303
+ flex-shrink: 0;
5304
+ cursor: pointer;
5305
+ align-items: center;
5306
+ justify-content: center;
5307
+ gap: 4px;
5308
+ border-radius: var(--radius-sm);
5309
+ border-width: 1px;
5310
+ border-color: var(--chat-border);
5311
+ background-color: var(--chat-bg-suggestion);
5312
+ padding-left: 10px;
5313
+ padding-right: 10px;
5314
+ padding-top: 5px;
5315
+ padding-bottom: 5px;
5316
+ font-size: 11.5px;
5317
+ font-weight: 500;
5318
+ color: var(--chat-fg-primary);
5319
+ text-decoration-line: none;
5320
+ transition-property: all;
5321
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
5322
+ transition-duration: 150ms;
5323
+ animation-duration: 150ms;
5324
+ font-family: inherit;
5325
+ }
5326
+ .gyramais-chat-card-button:hover:not(:disabled) {
5327
+ border-color: var(--chat-border-strong);
5328
+ background-color: var(--chat-bg-icon-hover);
5329
+ }
5330
+ .gyramais-chat-card-button-primary {
5331
+ border-color: transparent;
5332
+ background-color: var(--chat-bg-send);
5333
+ color: var(--chat-fg-on-send);
5334
+ }
5335
+ .gyramais-chat-card-button-primary:hover:not(:disabled) {
5336
+ border-color: transparent;
5337
+ background-color: var(--chat-bg-send-hover);
5338
+ --tw-text-opacity: 1;
5339
+ color: rgb(255 255 255 / var(--tw-text-opacity, 1));
5340
+ }
5341
+ .gyramais-chat-card-button:disabled {
5342
+ cursor: not-allowed;
5343
+ opacity: 0.5;
5344
+ }
5345
+ .gyramais-chat-card-button[aria-pressed="true"]:disabled {
5346
+ opacity: 1;
5347
+ box-shadow: inset 0 0 0 1px var(--chat-fg-accent);
5348
+ }
5349
+ .gyramais-chat-card-button-icon {
5350
+ height: 13px !important;
5351
+ width: 13px !important;
5352
+ font-size: 13px !important;
5353
+ }
5354
+
5355
+ .gyramais-chat-video {
5356
+ display: block;
5357
+ max-height: 240px;
5358
+ width: 100%;
5359
+ border-radius: var(--radius-sm);
5360
+ background-color: var(--chat-bg-media);
5361
+ aspect-ratio: 16 / 9;
5362
+ -o-object-fit: contain;
5363
+ object-fit: contain;
5364
+ }
5365
+ .gyramais-chat-video-expanded {
5366
+ display: block;
5367
+ max-height: 70vh;
5368
+ width: 100%;
5369
+ border-radius: var(--radius-sm);
5370
+ --tw-bg-opacity: 1;
5371
+ background-color: rgb(0 0 0 / var(--tw-bg-opacity, 1));
5372
+ aspect-ratio: 16 / 9;
5373
+ -o-object-fit: contain;
5374
+ object-fit: contain;
5375
+ }
5376
+
5377
+ .gyramais-chat-action-card {
5378
+ border-color: color-mix(in srgb, var(--chat-fg-accent) 35%, var(--chat-border));
5379
+ }
5380
+ .gyramais-chat-action-preview {
5381
+ padding-top: 4px;
5382
+ font-size: 12px;
5383
+ line-height: 1.5;
5384
+ }
5385
+
5109
5386
  .gyramais-checkbox-container {
5110
5387
 
5111
5388
  display: flex
@@ -11778,9 +12055,16 @@ h6 {
11778
12055
  }
11779
12056
 
11780
12057
  /* Estado anunciado uma vez para leitor de tela, invisivel na tela. As celulas
11781
- de skeleton sao decorativas (`aria-hidden`), entao nao repetem o anuncio. */
12058
+ de skeleton sao decorativas (`aria-hidden`), entao nao repetem o anuncio.
12059
+ `top`/`left` ancoram no topo de quem contem: sem eles o absolute fica na
12060
+ posicao estatica, e com o container sem posicao e a tabela no fim da tela
12061
+ ele cai abaixo da dobra, estica o documento e a pagina inteira rola junto
12062
+ com a sidebar. O container nao vira `relative` de proposito: com o
12063
+ overflow dele, cortaria menu e popover abertos dentro de uma celula. */
11782
12064
  .gyramais-table-container .gyramais-table-status {
11783
12065
  position: absolute;
12066
+ top: 0;
12067
+ left: 0;
11784
12068
  width: 1px;
11785
12069
  height: 1px;
11786
12070
  padding: 0;