@orbe-agro/client-core 5.6.201 → 5.6.202

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 (88) hide show
  1. package/dist/@ecme/assets/styles/app.css +82 -5
  2. package/dist/@ecme/assets/styles/tailwind/index.css +89 -6
  3. package/dist/@ecme/components/layouts/PostLoginLayout/PostLoginLayout.js +3 -0
  4. package/dist/@ecme/components/layouts/PostLoginLayout/PostLoginLayout.js.map +1 -1
  5. package/dist/@ecme/components/template/Footer.js +1 -1
  6. package/dist/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.js +100 -0
  7. package/dist/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.js.map +1 -0
  8. package/dist/@ecme/components/template/OrbeAiWidget/ResizeHandles.js +37 -0
  9. package/dist/@ecme/components/template/OrbeAiWidget/ResizeHandles.js.map +1 -0
  10. package/dist/@ecme/components/template/OrbeAiWidget/index.js +5 -0
  11. package/dist/@ecme/components/template/OrbeAiWidget/index.js.map +1 -0
  12. package/dist/@ecme/components/template/OrbeAiWidget/useResizablePanel.js +77 -0
  13. package/dist/@ecme/components/template/OrbeAiWidget/useResizablePanel.js.map +1 -0
  14. package/dist/@ecme/components/view/ChatBox/ChatBox.js +7 -1
  15. package/dist/@ecme/components/view/ChatBox/ChatBox.js.map +1 -1
  16. package/dist/@ecme/components/view/ChatBox/components/MessageList.js +7 -4
  17. package/dist/@ecme/components/view/ChatBox/components/MessageList.js.map +1 -1
  18. package/dist/@ecme/configs/navigation.config/concepts.navigation.config.js +0 -16
  19. package/dist/@ecme/configs/navigation.config/concepts.navigation.config.js.map +1 -1
  20. package/dist/@ecme/configs/routes.config/conceptsRoute.js +0 -9
  21. package/dist/@ecme/configs/routes.config/conceptsRoute.js.map +1 -1
  22. package/dist/@ecme/index.js +3 -1
  23. package/dist/@ecme/services/AiService.js +24 -4
  24. package/dist/@ecme/services/AiService.js.map +1 -1
  25. package/dist/@ecme/services/axios/AxiosBase.js +30 -24
  26. package/dist/@ecme/services/axios/AxiosBase.js.map +1 -1
  27. package/dist/@ecme/services/index.js +3 -1
  28. package/dist/@ecme/views/concepts/ai/Chat/components/AiThinkingIndicator.js +23 -0
  29. package/dist/@ecme/views/concepts/ai/Chat/components/AiThinkingIndicator.js.map +1 -0
  30. package/dist/@ecme/views/concepts/ai/Chat/components/ChatLandingView.js +16 -16
  31. package/dist/@ecme/views/concepts/ai/Chat/components/ChatLandingView.js.map +1 -1
  32. package/dist/@ecme/views/concepts/ai/Chat/components/ChatView.js +7 -4
  33. package/dist/@ecme/views/concepts/ai/Chat/components/ChatView.js.map +1 -1
  34. package/dist/@ecme/views/concepts/ai/Chat/components/ModelSelect.js +48 -0
  35. package/dist/@ecme/views/concepts/ai/Chat/components/ModelSelect.js.map +1 -0
  36. package/dist/@ecme/views/concepts/ai/Chat/hooks/useChatSend.js +6 -3
  37. package/dist/@ecme/views/concepts/ai/Chat/hooks/useChatSend.js.map +1 -1
  38. package/dist/@ecme/views/concepts/ai/Chat/store/aiModelStore.js +15 -0
  39. package/dist/@ecme/views/concepts/ai/Chat/store/aiModelStore.js.map +1 -0
  40. package/dist/@types/@ecme/components/layouts/PostLoginLayout/PostLoginLayout.d.ts.map +1 -1
  41. package/dist/@types/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.d.ts +3 -0
  42. package/dist/@types/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.d.ts.map +1 -0
  43. package/dist/@types/@ecme/components/template/OrbeAiWidget/ResizeHandles.d.ts +9 -0
  44. package/dist/@types/@ecme/components/template/OrbeAiWidget/ResizeHandles.d.ts.map +1 -0
  45. package/dist/@types/@ecme/components/template/OrbeAiWidget/index.d.ts +2 -0
  46. package/dist/@types/@ecme/components/template/OrbeAiWidget/index.d.ts.map +1 -0
  47. package/dist/@types/@ecme/components/template/OrbeAiWidget/useResizablePanel.d.ts +19 -0
  48. package/dist/@types/@ecme/components/template/OrbeAiWidget/useResizablePanel.d.ts.map +1 -0
  49. package/dist/@types/@ecme/components/view/ChatBox/ChatBox.d.ts.map +1 -1
  50. package/dist/@types/@ecme/components/view/ChatBox/components/MessageList.d.ts +3 -0
  51. package/dist/@types/@ecme/components/view/ChatBox/components/MessageList.d.ts.map +1 -1
  52. package/dist/@types/@ecme/configs/navigation.config/concepts.navigation.config.d.ts.map +1 -1
  53. package/dist/@types/@ecme/configs/routes.config/conceptsRoute.d.ts.map +1 -1
  54. package/dist/@types/@ecme/services/AiService.d.ts +12 -0
  55. package/dist/@types/@ecme/services/AiService.d.ts.map +1 -1
  56. package/dist/@types/@ecme/services/axios/AxiosBase.d.ts +12 -0
  57. package/dist/@types/@ecme/services/axios/AxiosBase.d.ts.map +1 -1
  58. package/dist/@types/@ecme/views/concepts/ai/Chat/components/AiThinkingIndicator.d.ts +7 -0
  59. package/dist/@types/@ecme/views/concepts/ai/Chat/components/AiThinkingIndicator.d.ts.map +1 -0
  60. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ChatView.d.ts.map +1 -1
  61. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ModelSelect.d.ts +8 -0
  62. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ModelSelect.d.ts.map +1 -0
  63. package/dist/@types/@ecme/views/concepts/ai/Chat/hooks/useChatSend.d.ts.map +1 -1
  64. package/dist/@types/@ecme/views/concepts/ai/Chat/store/aiModelStore.d.ts +25 -0
  65. package/dist/@types/@ecme/views/concepts/ai/Chat/store/aiModelStore.d.ts.map +1 -0
  66. package/dist/@types/base/components/forms/GenericForms/GenericArrayFormSection.d.ts +2 -2
  67. package/dist/@types/base/components/forms/GenericForms/GenericArrayFormSection.d.ts.map +1 -1
  68. package/dist/base/assets/styles/init.css +88 -5
  69. package/dist/base/components/forms/GenericForms/GenericArrayFormSection.js.map +1 -1
  70. package/lib/@ecme/components/layouts/PostLoginLayout/PostLoginLayout.tsx +2 -1
  71. package/lib/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.tsx +125 -0
  72. package/lib/@ecme/components/template/OrbeAiWidget/ResizeHandles.tsx +47 -0
  73. package/lib/@ecme/components/template/OrbeAiWidget/index.ts +1 -0
  74. package/lib/@ecme/components/template/OrbeAiWidget/useResizablePanel.ts +108 -0
  75. package/lib/@ecme/components/view/ChatBox/ChatBox.tsx +6 -0
  76. package/lib/@ecme/components/view/ChatBox/components/MessageList.tsx +21 -7
  77. package/lib/@ecme/configs/navigation.config/concepts.navigation.config.ts +0 -16
  78. package/lib/@ecme/configs/routes.config/conceptsRoute.ts +0 -9
  79. package/lib/@ecme/services/AiService.ts +40 -0
  80. package/lib/@ecme/services/axios/AxiosBase.ts +45 -24
  81. package/lib/@ecme/views/concepts/ai/Chat/components/AiThinkingIndicator.tsx +24 -0
  82. package/lib/@ecme/views/concepts/ai/Chat/components/ChatLandingView.tsx +17 -17
  83. package/lib/@ecme/views/concepts/ai/Chat/components/ChatView.tsx +11 -4
  84. package/lib/@ecme/views/concepts/ai/Chat/components/ModelSelect.tsx +66 -0
  85. package/lib/@ecme/views/concepts/ai/Chat/hooks/useChatSend.ts +5 -3
  86. package/lib/@ecme/views/concepts/ai/Chat/store/aiModelStore.ts +23 -0
  87. package/lib/base/components/forms/GenericForms/GenericArrayFormSection.tsx +1 -1
  88. package/package.json +1 -1
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Permite que uma requisição específica seja excluída do loading global
3
+ * (overlay de tela cheia). Útil para chamadas que exibem seu próprio
4
+ * indicador de carregamento — ex.: o chat de IA, que mostra o balãozinho
5
+ * de "digitando". Comportamento padrão inalterado: só afeta requisições
6
+ * que marcarem explicitamente `skipGlobalLoading: true`.
7
+ */
8
+ declare module "axios" {
9
+ interface AxiosRequestConfig {
10
+ skipGlobalLoading?: boolean;
11
+ }
12
+ }
1
13
  declare const AxiosBase: import("axios").AxiosInstance;
2
14
  export default AxiosBase;
3
15
  //# sourceMappingURL=AxiosBase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AxiosBase.d.ts","sourceRoot":"","sources":["../../../../../lib/@ecme/services/axios/AxiosBase.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,SAAS,+BAGb,CAAC;AAuDH,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"AxiosBase.d.ts","sourceRoot":"","sources":["../../../../../lib/@ecme/services/axios/AxiosBase.ts"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,OAAO,QAAQ,OAAO,CAAC;IACrB,UAAU,kBAAkB;QAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B;CACF;AAED,QAAA,MAAM,SAAS,+BAGb,CAAC;AA+DH,eAAe,SAAS,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Indicador de "pensando" do agente Orbe AI — loader profissional exibido
3
+ * enquanto a resposta está sendo gerada (estado isTyping do chat).
4
+ */
5
+ declare const AiThinkingIndicator: () => import("react/jsx-runtime").JSX.Element;
6
+ export default AiThinkingIndicator;
7
+ //# sourceMappingURL=AiThinkingIndicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiThinkingIndicator.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/components/AiThinkingIndicator.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AACH,QAAA,MAAM,mBAAmB,+CAexB,CAAA;AAED,eAAe,mBAAmB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChatView.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/components/ChatView.tsx"],"names":[],"mappings":"AAWA,QAAA,MAAM,QAAQ,+CA6Fb,CAAA;AAED,eAAe,QAAQ,CAAA"}
1
+ {"version":3,"file":"ChatView.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/components/ChatView.tsx"],"names":[],"mappings":"AAYA,QAAA,MAAM,QAAQ,+CAmGb,CAAA;AAED,eAAe,QAAQ,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Seletor do modelo de IA do chat (GET /ai/models). Se o backend estiver
3
+ * indisponivel ou nao expuser modelos, o seletor nao aparece e o backend usa o
4
+ * modelo default — por isso ele pode ser montado sem medo em qualquer MFE.
5
+ */
6
+ declare const ModelSelect: () => import("react/jsx-runtime").JSX.Element | null;
7
+ export default ModelSelect;
8
+ //# sourceMappingURL=ModelSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelSelect.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/components/ModelSelect.tsx"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,QAAA,MAAM,WAAW,sDAqDhB,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"useChatSend.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/hooks/useChatSend.ts"],"names":[],"mappings":"AAMA,QAAA,MAAM,WAAW;yBA0DqB,MAAM,gBAAgB,IAAI,EAAE;CAuBjE,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"useChatSend.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/hooks/useChatSend.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,WAAW;yBA2DqB,MAAM,gBAAgB,IAAI,EAAE;CAuBjE,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,25 @@
1
+ type AiModelState = {
2
+ selectedModel: string | null;
3
+ setSelectedModel: (model: string) => void;
4
+ };
5
+ /**
6
+ * Modelo de IA escolhido pelo usuario para o chat do Orbe AI.
7
+ *
8
+ * Persistido em localStorage: como todos os MFEs vivem na mesma origem, a
9
+ * escolha acompanha o usuario entre os modulos e entre sessoes.
10
+ */
11
+ export declare const useAiModelStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<AiModelState>, "setState" | "persist"> & {
12
+ setState(partial: AiModelState | Partial<AiModelState> | ((state: AiModelState) => AiModelState | Partial<AiModelState>), replace?: false | undefined): unknown;
13
+ setState(state: AiModelState | ((state: AiModelState) => AiModelState), replace: true): unknown;
14
+ persist: {
15
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AiModelState, AiModelState, unknown>>) => void;
16
+ clearStorage: () => void;
17
+ rehydrate: () => Promise<void> | void;
18
+ hasHydrated: () => boolean;
19
+ onHydrate: (fn: (state: AiModelState) => void) => () => void;
20
+ onFinishHydration: (fn: (state: AiModelState) => void) => () => void;
21
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<AiModelState, AiModelState, unknown>>;
22
+ };
23
+ }>;
24
+ export {};
25
+ //# sourceMappingURL=aiModelStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiModelStore.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/store/aiModelStore.ts"],"names":[],"mappings":"AAGA,KAAK,YAAY,GAAG;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;EAQ3B,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { IFormSectionBaseProps } from '@base/@types/forms';
2
- import { ArrayPath, FieldArray, FieldValues } from 'react-hook-form';
2
+ import { ArrayPath, FieldValues } from 'react-hook-form';
3
3
  type OptionType<L = string, V = unknown> = {
4
4
  label: L;
5
5
  value: V;
@@ -11,7 +11,7 @@ type SelectConfig<L = string, V = unknown> = {
11
11
  };
12
12
  type GeneralFormSectionProps<T extends FieldValues, K extends ArrayPath<T>> = IFormSectionBaseProps<T> & {
13
13
  nameArray: K;
14
- defaultAppendItem: FieldArray<T, K>;
14
+ defaultAppendItem: any;
15
15
  nameCampoStringArray: string[];
16
16
  nameCampoNumberArray: string[];
17
17
  nameCampoSelectArray?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"GenericArrayFormSection.d.ts","sourceRoot":"","sources":["../../../../../../lib/base/components/forms/GenericForms/GenericArrayFormSection.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EACH,SAAS,EACT,UAAU,EACV,WAAW,EAGd,MAAM,iBAAiB,CAAA;AAMxB,KAAK,UAAU,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,IAAI;IACvC,KAAK,EAAE,CAAC,CAAA;IACR,KAAK,EAAE,CAAC,CAAA;CACX,CAAA;AAED,KAAK,YAAY,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,IAAI;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,CAAC,CAAA;IACjC,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAA;CACvD,CAAA;AAED,KAAK,uBAAuB,CACxB,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,IACtB,qBAAqB,CAAC,CAAC,CAAC,GAAG;IAC3B,SAAS,EAAE,CAAC,CAAA;IACZ,iBAAiB,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACnC,oBAAoB,EAAE,MAAM,EAAE,CAAA;IAC9B,oBAAoB,EAAE,MAAM,EAAE,CAAA;IAC9B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC/B,aAAa,CAAC,EAAE,YAAY,EAAE,CAAA;CACjC,CAAA;AAED,iBAAS,uBAAuB,CAC5B,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EACxB,EACE,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAS,EACT,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,GACpB,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC,CAAC,2CA0E/B;AAED,eAAe,uBAAuB,CAAA"}
1
+ {"version":3,"file":"GenericArrayFormSection.d.ts","sourceRoot":"","sources":["../../../../../../lib/base/components/forms/GenericForms/GenericArrayFormSection.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EACH,SAAS,EAET,WAAW,EAGd,MAAM,iBAAiB,CAAA;AAMxB,KAAK,UAAU,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,IAAI;IACvC,KAAK,EAAE,CAAC,CAAA;IACR,KAAK,EAAE,CAAC,CAAA;CACX,CAAA;AAED,KAAK,YAAY,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,IAAI;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,CAAC,CAAA;IACjC,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAA;CACvD,CAAA;AAED,KAAK,uBAAuB,CACxB,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,IACtB,qBAAqB,CAAC,CAAC,CAAC,GAAG;IAC3B,SAAS,EAAE,CAAC,CAAA;IACZ,iBAAiB,EAAE,GAAG,CAAA;IACtB,oBAAoB,EAAE,MAAM,EAAE,CAAA;IAC9B,oBAAoB,EAAE,MAAM,EAAE,CAAA;IAC9B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC/B,aAAa,CAAC,EAAE,YAAY,EAAE,CAAA;CACjC,CAAA;AAED,iBAAS,uBAAuB,CAC5B,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EACxB,EACE,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAS,EACT,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,GACpB,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC,CAAC,2CA0E/B;AAED,eAAe,uBAAuB,CAAA"}
@@ -5106,6 +5106,9 @@ video {
5106
5106
  .bottom-4{
5107
5107
  bottom: 1rem;
5108
5108
  }
5109
+ .bottom-6{
5110
+ bottom: 1.5rem;
5111
+ }
5109
5112
  .bottom-full{
5110
5113
  bottom: 100%;
5111
5114
  }
@@ -5127,6 +5130,9 @@ video {
5127
5130
  .right-4{
5128
5131
  right: 1rem;
5129
5132
  }
5133
+ .right-6{
5134
+ right: 1.5rem;
5135
+ }
5130
5136
  .top-0{
5131
5137
  top: 0px;
5132
5138
  }
@@ -5148,9 +5154,15 @@ video {
5148
5154
  .z-10{
5149
5155
  z-index: 10;
5150
5156
  }
5157
+ .z-20{
5158
+ z-index: 20;
5159
+ }
5151
5160
  .z-30{
5152
5161
  z-index: 30;
5153
5162
  }
5163
+ .z-40{
5164
+ z-index: 40;
5165
+ }
5154
5166
  .z-50{
5155
5167
  z-index: 50;
5156
5168
  }
@@ -5631,6 +5643,9 @@ video {
5631
5643
  .max-h-full{
5632
5644
  max-height: 100%;
5633
5645
  }
5646
+ .min-h-0{
5647
+ min-height: 0px;
5648
+ }
5634
5649
  .min-h-10{
5635
5650
  min-height: 2.5rem;
5636
5651
  }
@@ -5640,8 +5655,8 @@ video {
5640
5655
  .min-h-14{
5641
5656
  min-height: 3.5rem;
5642
5657
  }
5643
- .min-h-40{
5644
- min-height: 10rem;
5658
+ .min-h-28{
5659
+ min-height: 7rem;
5645
5660
  }
5646
5661
  .min-h-6{
5647
5662
  min-height: 1.5rem;
@@ -5689,6 +5704,9 @@ video {
5689
5704
  .w-1{
5690
5705
  width: 0.25rem;
5691
5706
  }
5707
+ .w-1\.5{
5708
+ width: 0.375rem;
5709
+ }
5692
5710
  .w-1\/2{
5693
5711
  width: 50%;
5694
5712
  }
@@ -5936,6 +5954,9 @@ video {
5936
5954
  .max-w-\[900px\]{
5937
5955
  max-width: 900px;
5938
5956
  }
5957
+ .max-w-\[9rem\]{
5958
+ max-width: 9rem;
5959
+ }
5939
5960
  .max-w-full{
5940
5961
  max-width: 100%;
5941
5962
  }
@@ -6010,9 +6031,21 @@ video {
6010
6031
  .cursor-default{
6011
6032
  cursor: default;
6012
6033
  }
6034
+ .cursor-ew-resize{
6035
+ cursor: ew-resize;
6036
+ }
6037
+ .cursor-nesw-resize{
6038
+ cursor: nesw-resize;
6039
+ }
6013
6040
  .cursor-not-allowed{
6014
6041
  cursor: not-allowed;
6015
6042
  }
6043
+ .cursor-ns-resize{
6044
+ cursor: ns-resize;
6045
+ }
6046
+ .cursor-nwse-resize{
6047
+ cursor: nwse-resize;
6048
+ }
6016
6049
  .cursor-pointer{
6017
6050
  cursor: pointer;
6018
6051
  }
@@ -7455,6 +7488,12 @@ video {
7455
7488
  .ease-out{
7456
7489
  transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
7457
7490
  }
7491
+ .\[animation-delay\:-0\.15s\]{
7492
+ animation-delay: -0.15s;
7493
+ }
7494
+ .\[animation-delay\:-0\.3s\]{
7495
+ animation-delay: -0.3s;
7496
+ }
7458
7497
  .\[transform\:translateZ\(0\)\]{
7459
7498
  transform: translateZ(0);
7460
7499
  }
@@ -7638,6 +7677,10 @@ video {
7638
7677
  background-color: var(--primary);
7639
7678
  }
7640
7679
 
7680
+ .hover\:bg-primary-deep:hover{
7681
+ background-color: var(--primary-deep);
7682
+ }
7683
+
7641
7684
  .hover\:bg-primary-mild:hover{
7642
7685
  background-color: var(--primary-mild);
7643
7686
  }
@@ -7658,6 +7701,10 @@ video {
7658
7701
  color: var(--gray-300);
7659
7702
  }
7660
7703
 
7704
+ .hover\:text-gray-700:hover{
7705
+ color: var(--gray-700);
7706
+ }
7707
+
7661
7708
  .hover\:text-gray-800:hover{
7662
7709
  color: var(--gray-800);
7663
7710
  }
@@ -7785,6 +7832,20 @@ video {
7785
7832
  outline-offset: 2px;
7786
7833
  }
7787
7834
 
7835
+ .focus-visible\:ring-2:focus-visible{
7836
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
7837
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
7838
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
7839
+ }
7840
+
7841
+ .focus-visible\:ring-primary:focus-visible{
7842
+ --tw-ring-color: var(--primary);
7843
+ }
7844
+
7845
+ .focus-visible\:ring-offset-2:focus-visible{
7846
+ --tw-ring-offset-width: 2px;
7847
+ }
7848
+
7788
7849
  .active\:bg-gray-100:active{
7789
7850
  background-color: var(--gray-100);
7790
7851
  }
@@ -7895,6 +7956,27 @@ video {
7895
7956
  .motion-safe\:animate-\[bounce_1s_ease-in-out_infinite\]{
7896
7957
  animation: bounce 1s ease-in-out infinite;
7897
7958
  }
7959
+ @keyframes bounce{
7960
+ 0%, 100%{
7961
+ transform: translateY(-25%);
7962
+ animation-timing-function: cubic-bezier(0.8,0,1,1);
7963
+ }
7964
+ 50%{
7965
+ transform: none;
7966
+ animation-timing-function: cubic-bezier(0,0,0.2,1);
7967
+ }
7968
+ }
7969
+ .motion-safe\:animate-bounce{
7970
+ animation: bounce 1s infinite;
7971
+ }
7972
+ @keyframes pulse{
7973
+ 50%{
7974
+ opacity: .5;
7975
+ }
7976
+ }
7977
+ .motion-safe\:animate-pulse{
7978
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
7979
+ }
7898
7980
  }
7899
7981
 
7900
7982
  .dark\:border:is(.dark *){
@@ -8355,6 +8437,10 @@ video {
8355
8437
  color: var(--gray-100);
8356
8438
  }
8357
8439
 
8440
+ .dark\:hover\:text-gray-200:hover:is(.dark *){
8441
+ color: var(--gray-200);
8442
+ }
8443
+
8358
8444
  .dark\:hover\:text-white:hover:is(.dark *){
8359
8445
  --tw-text-opacity: 1;
8360
8446
  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
@@ -8750,9 +8836,6 @@ video {
8750
8836
  .\32xl\:grid{
8751
8837
  display: grid;
8752
8838
  }
8753
- .\32xl\:min-h-60{
8754
- min-height: 15rem;
8755
- }
8756
8839
  .\32xl\:w-\[500px\]{
8757
8840
  width: 500px;
8758
8841
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GenericArrayFormSection.js","sources":["../../../../../lib/base/components/forms/GenericForms/GenericArrayFormSection.tsx"],"sourcesContent":["import { IFormSectionBaseProps } from '@base/@types/forms'\nimport {\n ArrayPath,\n FieldArray,\n FieldValues,\n Path,\n useFieldArray,\n} from 'react-hook-form'\nimport { useTranslation } from 'react-i18next'\nimport { FaTrash } from 'react-icons/fa'\nimport GenericArrayNumberFormSection from './../ArraysForms/GenericArrayNumberFormSection'\nimport GenericArrayStringFormSection from './../ArraysForms/GenericArrayStringFormSection'\n\ntype OptionType<L = string, V = unknown> = {\n label: L\n value: V\n}\n\ntype SelectConfig<L = string, V = unknown> = {\n campo: string\n valueMapper: (item: unknown) => V\n getOptionLabel: (option: OptionType<L, V>) => string\n}\n\ntype GeneralFormSectionProps<\n T extends FieldValues,\n K extends ArrayPath<T>,\n> = IFormSectionBaseProps<T> & {\n nameArray: K\n defaultAppendItem: FieldArray<T, K>\n nameCampoStringArray: string[]\n nameCampoNumberArray: string[]\n nameCampoSelectArray?: string[]\n selectConfigs?: SelectConfig[]\n}\n\nfunction GenericArrayFormSection<\n T extends FieldValues,\n K extends ArrayPath<T>,\n>({\n control,\n errors,\n isView,\n nameArray,\n nameCampoStringArray,\n nameCampoNumberArray,\n defaultAppendItem,\n}: GeneralFormSectionProps<T, K>) {\n const { t } = useTranslation()\n const { fields, append, remove } = useFieldArray({\n control,\n name: nameArray,\n })\n\n return (\n <div className=\"flex flex-col gap-4\">\n {/* Lista de margens */}\n {fields.map((field, index) => (\n <div key={field.id} className=\"flex gap-2 items-center\">\n {nameCampoStringArray.map((campo) => (\n <GenericArrayStringFormSection\n key={campo}\n control={control}\n errors={errors}\n isView={isView}\n index={index}\n label={t(`page.form.${campo}.label`)}\n placeholder={t(`page.form.${campo}.placeholder`)}\n nameStringArray={\n `${nameArray}.${index}.${campo}` as Path<T>\n }\n />\n ))}\n\n {nameCampoNumberArray.map((campo) => (\n <GenericArrayNumberFormSection\n key={campo}\n control={control}\n errors={errors}\n isView={isView}\n index={index}\n label={t(`page.form.${campo}.label`)}\n placeholder={t(`page.form.${campo}.placeholder`)}\n nameNumberArray={\n `${nameArray}.${index}.${campo}` as Path<T>\n }\n />\n ))}\n\n <button\n type=\"button\"\n className=\"text-red-500 hover:text-red-700 ml-4\"\n onClick={() => remove(index)}\n >\n <FaTrash className=\"w-5 h-5\" />\n </button>\n </div>\n ))}\n\n {errors?.[nameArray as string]?.message &&\n typeof errors?.[nameArray as string]?.message === 'string' && (\n <p className=\"text-red-500 text-sm mt-2\">\n {String(errors?.[nameArray as string]?.message || '')}\n </p>\n )}\n\n {/* Botão separado */}\n <label className=\"form-label mb-2\">\n {t(`page.form.${nameArray}.label`)}\n </label>\n <div>\n <button\n type=\"button\"\n className=\"px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600\"\n onClick={() => append(defaultAppendItem)}\n >\n {t(`page.form.${nameArray}.nameButton`)}\n </button>\n </div>\n </div>\n )\n}\n\nexport default GenericArrayFormSection\n"],"names":[],"mappings":";;;;;;AAoCA,SAAS,wBAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,GAAkC;AAC9B,QAAM,EAAE,EAAA,IAAM,eAAA;AACd,QAAM,EAAE,QAAQ,QAAQ,OAAA,IAAW,cAAc;AAAA,IAC7C;AAAA,IACA,MAAM;AAAA,EAAA,CACT;AAED,SACI,qBAAC,OAAA,EAAI,WAAU,uBAEV,UAAA;AAAA,IAAA,OAAO,IAAI,CAAC,OAAO,UAChB,qBAAC,OAAA,EAAmB,WAAU,2BACzB,UAAA;AAAA,MAAA,qBAAqB,IAAI,CAAC,UACvB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEG;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,EAAE,aAAa,KAAK,QAAQ;AAAA,UACnC,aAAa,EAAE,aAAa,KAAK,cAAc;AAAA,UAC/C,iBACI,GAAG,SAAS,IAAI,KAAK,IAAI,KAAK;AAAA,QAAA;AAAA,QAR7B;AAAA,MAAA,CAWZ;AAAA,MAEA,qBAAqB,IAAI,CAAC,UACvB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEG;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,EAAE,aAAa,KAAK,QAAQ;AAAA,UACnC,aAAa,EAAE,aAAa,KAAK,cAAc;AAAA,UAC/C,iBACI,GAAG,SAAS,IAAI,KAAK,IAAI,KAAK;AAAA,QAAA;AAAA,QAR7B;AAAA,MAAA,CAWZ;AAAA,MAED;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,OAAO,KAAK;AAAA,UAE3B,UAAA,oBAAC,SAAA,EAAQ,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,IACjC,KArCM,MAAM,EAsChB,CACH;AAAA,IAEA,SAAS,SAAmB,GAAG,WAC5B,OAAO,SAAS,SAAmB,GAAG,YAAY,gCAC7C,KAAA,EAAE,WAAU,6BACR,UAAA,OAAO,SAAS,SAAmB,GAAG,WAAW,EAAE,GACxD;AAAA,IAIR,oBAAC,WAAM,WAAU,mBACZ,YAAE,aAAa,SAAS,QAAQ,EAAA,CACrC;AAAA,wBACC,OAAA,EACG,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,MAAM,OAAO,iBAAiB;AAAA,QAEtC,UAAA,EAAE,aAAa,SAAS,aAAa;AAAA,MAAA;AAAA,IAAA,EAC1C,CACJ;AAAA,EAAA,GACJ;AAER;"}
1
+ {"version":3,"file":"GenericArrayFormSection.js","sources":["../../../../../lib/base/components/forms/GenericForms/GenericArrayFormSection.tsx"],"sourcesContent":["import { IFormSectionBaseProps } from '@base/@types/forms'\nimport {\n ArrayPath,\n FieldArray,\n FieldValues,\n Path,\n useFieldArray,\n} from 'react-hook-form'\nimport { useTranslation } from 'react-i18next'\nimport { FaTrash } from 'react-icons/fa'\nimport GenericArrayNumberFormSection from './../ArraysForms/GenericArrayNumberFormSection'\nimport GenericArrayStringFormSection from './../ArraysForms/GenericArrayStringFormSection'\n\ntype OptionType<L = string, V = unknown> = {\n label: L\n value: V\n}\n\ntype SelectConfig<L = string, V = unknown> = {\n campo: string\n valueMapper: (item: unknown) => V\n getOptionLabel: (option: OptionType<L, V>) => string\n}\n\ntype GeneralFormSectionProps<\n T extends FieldValues,\n K extends ArrayPath<T>,\n> = IFormSectionBaseProps<T> & {\n nameArray: K\n defaultAppendItem: any\n nameCampoStringArray: string[]\n nameCampoNumberArray: string[]\n nameCampoSelectArray?: string[]\n selectConfigs?: SelectConfig[]\n}\n\nfunction GenericArrayFormSection<\n T extends FieldValues,\n K extends ArrayPath<T>,\n>({\n control,\n errors,\n isView,\n nameArray,\n nameCampoStringArray,\n nameCampoNumberArray,\n defaultAppendItem,\n}: GeneralFormSectionProps<T, K>) {\n const { t } = useTranslation()\n const { fields, append, remove } = useFieldArray({\n control,\n name: nameArray,\n })\n\n return (\n <div className=\"flex flex-col gap-4\">\n {/* Lista de margens */}\n {fields.map((field, index) => (\n <div key={field.id} className=\"flex gap-2 items-center\">\n {nameCampoStringArray.map((campo) => (\n <GenericArrayStringFormSection\n key={campo}\n control={control}\n errors={errors}\n isView={isView}\n index={index}\n label={t(`page.form.${campo}.label`)}\n placeholder={t(`page.form.${campo}.placeholder`)}\n nameStringArray={\n `${nameArray}.${index}.${campo}` as Path<T>\n }\n />\n ))}\n\n {nameCampoNumberArray.map((campo) => (\n <GenericArrayNumberFormSection\n key={campo}\n control={control}\n errors={errors}\n isView={isView}\n index={index}\n label={t(`page.form.${campo}.label`)}\n placeholder={t(`page.form.${campo}.placeholder`)}\n nameNumberArray={\n `${nameArray}.${index}.${campo}` as Path<T>\n }\n />\n ))}\n\n <button\n type=\"button\"\n className=\"text-red-500 hover:text-red-700 ml-4\"\n onClick={() => remove(index)}\n >\n <FaTrash className=\"w-5 h-5\" />\n </button>\n </div>\n ))}\n\n {errors?.[nameArray as string]?.message &&\n typeof errors?.[nameArray as string]?.message === 'string' && (\n <p className=\"text-red-500 text-sm mt-2\">\n {String(errors?.[nameArray as string]?.message || '')}\n </p>\n )}\n\n {/* Botão separado */}\n <label className=\"form-label mb-2\">\n {t(`page.form.${nameArray}.label`)}\n </label>\n <div>\n <button\n type=\"button\"\n className=\"px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600\"\n onClick={() => append(defaultAppendItem)}\n >\n {t(`page.form.${nameArray}.nameButton`)}\n </button>\n </div>\n </div>\n )\n}\n\nexport default GenericArrayFormSection\n"],"names":[],"mappings":";;;;;;AAoCA,SAAS,wBAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,GAAkC;AAC9B,QAAM,EAAE,EAAA,IAAM,eAAA;AACd,QAAM,EAAE,QAAQ,QAAQ,OAAA,IAAW,cAAc;AAAA,IAC7C;AAAA,IACA,MAAM;AAAA,EAAA,CACT;AAED,SACI,qBAAC,OAAA,EAAI,WAAU,uBAEV,UAAA;AAAA,IAAA,OAAO,IAAI,CAAC,OAAO,UAChB,qBAAC,OAAA,EAAmB,WAAU,2BACzB,UAAA;AAAA,MAAA,qBAAqB,IAAI,CAAC,UACvB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEG;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,EAAE,aAAa,KAAK,QAAQ;AAAA,UACnC,aAAa,EAAE,aAAa,KAAK,cAAc;AAAA,UAC/C,iBACI,GAAG,SAAS,IAAI,KAAK,IAAI,KAAK;AAAA,QAAA;AAAA,QAR7B;AAAA,MAAA,CAWZ;AAAA,MAEA,qBAAqB,IAAI,CAAC,UACvB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEG;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,EAAE,aAAa,KAAK,QAAQ;AAAA,UACnC,aAAa,EAAE,aAAa,KAAK,cAAc;AAAA,UAC/C,iBACI,GAAG,SAAS,IAAI,KAAK,IAAI,KAAK;AAAA,QAAA;AAAA,QAR7B;AAAA,MAAA,CAWZ;AAAA,MAED;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,OAAO,KAAK;AAAA,UAE3B,UAAA,oBAAC,SAAA,EAAQ,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,IACjC,KArCM,MAAM,EAsChB,CACH;AAAA,IAEA,SAAS,SAAmB,GAAG,WAC5B,OAAO,SAAS,SAAmB,GAAG,YAAY,gCAC7C,KAAA,EAAE,WAAU,6BACR,UAAA,OAAO,SAAS,SAAmB,GAAG,WAAW,EAAE,GACxD;AAAA,IAIR,oBAAC,WAAM,WAAU,mBACZ,YAAE,aAAa,SAAS,QAAQ,EAAA,CACrC;AAAA,wBACC,OAAA,EACG,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,MAAM,OAAO,iBAAiB;AAAA,QAEtC,UAAA,EAAE,aAAa,SAAS,aAAa;AAAA,MAAA;AAAA,IAAA,EAC1C,CACJ;AAAA,EAAA,GACJ;AAER;"}
@@ -15,8 +15,8 @@ import { LinkRenderer } from "../Layouts";
15
15
  import { useLocation } from "react-router-dom";
16
16
  import { Routes } from "@/@types";
17
17
  import { protectedRoutes, publicRoutes } from "@/configs";
18
- import { useThemeStore } from "@/store";
19
18
  import GlobalLoading from "@/components/shared/loaders/GlobalLoading";
19
+ import OrbeAiWidget from "@/components/template/OrbeAiWidget";
20
20
 
21
21
  type Layouts = Record<
22
22
  string,
@@ -66,6 +66,7 @@ const PostLoginLayout = ({
66
66
  return (
67
67
  <>
68
68
  <GlobalLoading />
69
+ <OrbeAiWidget />
69
70
  <Suspense
70
71
  fallback={
71
72
  <div className="flex flex-auto flex-col h-[100vh]">
@@ -0,0 +1,125 @@
1
+ import { useState } from 'react'
2
+ import { AnimatePresence, motion } from 'framer-motion'
3
+ import {
4
+ TbMessageChatbot,
5
+ TbArrowsMaximize,
6
+ TbArrowsMinimize,
7
+ } from 'react-icons/tb'
8
+ import { HiX } from 'react-icons/hi'
9
+ import ChatView from '@/views/concepts/ai/Chat/components/ChatView'
10
+ import ChatHistoryRenameDialog from '@/views/concepts/ai/Chat/components/ChatHistoryRenameDialog'
11
+ import ModelSelect from '@/views/concepts/ai/Chat/components/ModelSelect'
12
+ import useResizablePanel from './useResizablePanel'
13
+ import ResizeHandles from './ResizeHandles'
14
+
15
+ const iconButtonClass =
16
+ 'flex h-8 w-8 items-center justify-center rounded-full text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200'
17
+
18
+ const OrbeAiWidget = () => {
19
+ const [open, setOpen] = useState(false)
20
+ const [fullscreen, setFullscreen] = useState(false)
21
+ const { rect, startResize } = useResizablePanel()
22
+
23
+ const toggleOpen = () => setOpen((prev) => !prev)
24
+ const toggleFullscreen = () => setFullscreen((prev) => !prev)
25
+ const close = () => {
26
+ setOpen(false)
27
+ setFullscreen(false)
28
+ }
29
+
30
+ // Tela cheia: cobre o viewport inteiro (inset-0). Flutuante: usa o retângulo do resize.
31
+ const panelStyle = fullscreen
32
+ ? undefined
33
+ : {
34
+ top: rect.top,
35
+ left: rect.left,
36
+ width: rect.width,
37
+ height: rect.height,
38
+ }
39
+ const panelVariantClass = fullscreen
40
+ ? 'inset-0 rounded-none border-0'
41
+ : 'rounded-2xl border shadow-2xl'
42
+
43
+ return (
44
+ <div
45
+ className={`fixed bottom-6 right-6 flex flex-col items-end gap-3 print:hidden ${
46
+ fullscreen ? 'z-[9999]' : 'z-40'
47
+ }`}
48
+ >
49
+ <AnimatePresence>
50
+ {open && (
51
+ <motion.div
52
+ key="orbe-ai-panel"
53
+ initial={{ opacity: 0, y: 24, scale: 0.96 }}
54
+ animate={{ opacity: 1, y: 0, scale: 1 }}
55
+ exit={{ opacity: 0, y: 24, scale: 0.96 }}
56
+ transition={{ duration: 0.2, ease: 'easeOut' }}
57
+ style={panelStyle}
58
+ className={`fixed flex flex-col overflow-hidden border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800 ${panelVariantClass}`}
59
+ >
60
+ {!fullscreen && (
61
+ <ResizeHandles onResizeStart={startResize} />
62
+ )}
63
+
64
+ <div className="flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-700">
65
+ <div className="flex items-center gap-2">
66
+ <span className="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-white">
67
+ <TbMessageChatbot size={20} />
68
+ </span>
69
+ <span className="font-semibold text-gray-900 dark:text-gray-100">
70
+ Orbe AI
71
+ </span>
72
+ </div>
73
+ <div className="flex items-center gap-2">
74
+ <ModelSelect />
75
+ <button
76
+ type="button"
77
+ className={iconButtonClass}
78
+ onClick={toggleFullscreen}
79
+ aria-label={
80
+ fullscreen
81
+ ? 'Sair da tela cheia'
82
+ : 'Tela cheia'
83
+ }
84
+ >
85
+ {fullscreen ? (
86
+ <TbArrowsMinimize size={18} />
87
+ ) : (
88
+ <TbArrowsMaximize size={18} />
89
+ )}
90
+ </button>
91
+ <button
92
+ type="button"
93
+ className={iconButtonClass}
94
+ onClick={close}
95
+ aria-label="Fechar chat Orbe AI"
96
+ >
97
+ <HiX size={18} />
98
+ </button>
99
+ </div>
100
+ </div>
101
+
102
+ <div className="min-h-0 flex-1 p-2">
103
+ <ChatView />
104
+ <ChatHistoryRenameDialog />
105
+ </div>
106
+ </motion.div>
107
+ )}
108
+ </AnimatePresence>
109
+
110
+ {!fullscreen && (
111
+ <button
112
+ type="button"
113
+ className="flex h-14 w-14 items-center justify-center rounded-full bg-primary text-white shadow-lg transition-colors hover:bg-primary-deep focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
114
+ onClick={toggleOpen}
115
+ aria-label={open ? 'Fechar chat Orbe AI' : 'Abrir chat Orbe AI'}
116
+ aria-expanded={open}
117
+ >
118
+ {open ? <HiX size={24} /> : <TbMessageChatbot size={26} />}
119
+ </button>
120
+ )}
121
+ </div>
122
+ )
123
+ }
124
+
125
+ export default OrbeAiWidget
@@ -0,0 +1,47 @@
1
+ import type { PointerEvent as ReactPointerEvent } from 'react'
2
+ import type { ResizeDirection } from './useResizablePanel'
3
+
4
+ type ResizeHandlesProps = {
5
+ onResizeStart: (
6
+ event: ReactPointerEvent,
7
+ direction: ResizeDirection,
8
+ ) => void
9
+ }
10
+
11
+ const EDGES: { direction: ResizeDirection; className: string }[] = [
12
+ { direction: 'top', className: 'left-0 right-0 top-0 h-1.5 cursor-ns-resize' },
13
+ { direction: 'bottom', className: 'bottom-0 left-0 right-0 h-1.5 cursor-ns-resize' },
14
+ { direction: 'left', className: 'bottom-0 left-0 top-0 w-1.5 cursor-ew-resize' },
15
+ { direction: 'right', className: 'bottom-0 right-0 top-0 w-1.5 cursor-ew-resize' },
16
+ ]
17
+
18
+ const CORNERS: { direction: ResizeDirection; className: string }[] = [
19
+ { direction: 'top-left', className: 'left-0 top-0 cursor-nwse-resize' },
20
+ { direction: 'top-right', className: 'right-0 top-0 cursor-nesw-resize' },
21
+ { direction: 'bottom-left', className: 'bottom-0 left-0 cursor-nesw-resize' },
22
+ { direction: 'bottom-right', className: 'bottom-0 right-0 cursor-nwse-resize' },
23
+ ]
24
+
25
+ /** Alças transparentes de redimensionamento nas 4 bordas + 4 cantos do painel. */
26
+ const ResizeHandles = ({ onResizeStart }: ResizeHandlesProps) => (
27
+ <>
28
+ {EDGES.map(({ direction, className }) => (
29
+ <span
30
+ key={direction}
31
+ aria-hidden
32
+ onPointerDown={(event) => onResizeStart(event, direction)}
33
+ className={`absolute z-20 ${className}`}
34
+ />
35
+ ))}
36
+ {CORNERS.map(({ direction, className }) => (
37
+ <span
38
+ key={direction}
39
+ aria-hidden
40
+ onPointerDown={(event) => onResizeStart(event, direction)}
41
+ className={`absolute z-30 h-3 w-3 ${className}`}
42
+ />
43
+ ))}
44
+ </>
45
+ )
46
+
47
+ export default ResizeHandles
@@ -0,0 +1 @@
1
+ export { default } from './OrbeAiWidget'
@@ -0,0 +1,108 @@
1
+ import { useCallback, useState } from 'react'
2
+ import type { PointerEvent as ReactPointerEvent } from 'react'
3
+
4
+ export type Rect = { top: number; left: number; width: number; height: number }
5
+
6
+ export type ResizeDirection =
7
+ | 'top'
8
+ | 'bottom'
9
+ | 'left'
10
+ | 'right'
11
+ | 'top-left'
12
+ | 'top-right'
13
+ | 'bottom-left'
14
+ | 'bottom-right'
15
+
16
+ const MIN = { width: 360, height: 420 }
17
+ const DEFAULT = { width: 480, height: 720 }
18
+ const MARGIN = 12 // distância mínima das bordas do viewport
19
+ const ANCHOR = { right: 24, bottomOffset: 92 } // canto inferior-direito, acima do FAB
20
+
21
+ const clamp = (value: number, min: number, max: number) =>
22
+ Math.min(Math.max(value, min), max)
23
+
24
+ /** Retângulo inicial: ancorado no canto inferior-direito, logo acima do FAB. */
25
+ const getDefaultRect = (): Rect => {
26
+ const width = Math.min(DEFAULT.width, window.innerWidth - MARGIN * 2)
27
+ const height = Math.min(DEFAULT.height, window.innerHeight - MARGIN * 2)
28
+ return {
29
+ width,
30
+ height,
31
+ left: Math.max(MARGIN, window.innerWidth - width - ANCHOR.right),
32
+ top: Math.max(MARGIN, window.innerHeight - height - ANCHOR.bottomOffset),
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Painel redimensionável por qualquer borda/canto. O painel é posicionado
38
+ * livremente (`fixed` com top/left/width/height); cada direção move sua borda
39
+ * mantendo a borda oposta fixa, com limites de mínimo e do viewport.
40
+ */
41
+ const useResizablePanel = () => {
42
+ const [rect, setRect] = useState<Rect>(getDefaultRect)
43
+
44
+ const startResize = useCallback(
45
+ (event: ReactPointerEvent, direction: ResizeDirection) => {
46
+ event.preventDefault()
47
+ event.stopPropagation()
48
+
49
+ const startX = event.clientX
50
+ const startY = event.clientY
51
+ const start = rect
52
+ const right = start.left + start.width
53
+ const bottom = start.top + start.height
54
+
55
+ const previousUserSelect = document.body.style.userSelect
56
+ document.body.style.userSelect = 'none'
57
+
58
+ const handleMove = (moveEvent: PointerEvent) => {
59
+ const dx = moveEvent.clientX - startX
60
+ const dy = moveEvent.clientY - startY
61
+ const next: Rect = { ...start }
62
+
63
+ if (direction.includes('right')) {
64
+ next.width = clamp(
65
+ start.width + dx,
66
+ MIN.width,
67
+ window.innerWidth - start.left - MARGIN,
68
+ )
69
+ }
70
+ if (direction.includes('left')) {
71
+ next.width = clamp(start.width - dx, MIN.width, right - MARGIN)
72
+ next.left = right - next.width
73
+ }
74
+ if (direction.includes('bottom')) {
75
+ next.height = clamp(
76
+ start.height + dy,
77
+ MIN.height,
78
+ window.innerHeight - start.top - MARGIN,
79
+ )
80
+ }
81
+ if (direction.includes('top')) {
82
+ next.height = clamp(
83
+ start.height - dy,
84
+ MIN.height,
85
+ bottom - MARGIN,
86
+ )
87
+ next.top = bottom - next.height
88
+ }
89
+
90
+ setRect(next)
91
+ }
92
+
93
+ const stopResize = () => {
94
+ document.body.style.userSelect = previousUserSelect
95
+ window.removeEventListener('pointermove', handleMove)
96
+ window.removeEventListener('pointerup', stopResize)
97
+ }
98
+
99
+ window.addEventListener('pointermove', handleMove)
100
+ window.addEventListener('pointerup', stopResize)
101
+ },
102
+ [rect],
103
+ )
104
+
105
+ return { rect, startResize }
106
+ }
107
+
108
+ export default useResizablePanel
@@ -32,6 +32,9 @@ const ChatBox = forwardRef<ScrollBarRef, ChatBoxProps>((props, ref) => {
32
32
  customRenderer,
33
33
  customAction,
34
34
  bubbleClass,
35
+ myBubbleClass,
36
+ botBubbleClass,
37
+ renderTyping,
35
38
  typing,
36
39
  messageListClass,
37
40
  containerClass,
@@ -59,6 +62,9 @@ const ChatBox = forwardRef<ScrollBarRef, ChatBoxProps>((props, ref) => {
59
62
  typing={typing}
60
63
  messageListClass={messageListClass}
61
64
  bubbleClass={bubbleClass}
65
+ myBubbleClass={myBubbleClass}
66
+ botBubbleClass={botBubbleClass}
67
+ renderTyping={renderTyping}
62
68
  /> )}
63
69
  {children}
64
70
  </ChatContainer>