PaginaInicialView.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. <script setup>
  2. import { computed, onUnmounted, ref } from "vue";
  3. import ChatWindow from "../../components/ChatWindow.vue";
  4. import LayoutSistema from "../../layout/LayoutSistema.vue";
  5. import { useChat } from "../../composables/useChat.js";
  6. import BaseButton from "../../components/base/BaseButton.vue";
  7. const {
  8. messages: chatMessages,
  9. loading: chatLoading,
  10. error: chatError,
  11. send: sendMessage,
  12. clearError,
  13. cancelCurrentStream,
  14. activeConversationId,
  15. exportConversation
  16. } = useChat();
  17. onUnmounted(() => cancelCurrentStream());
  18. const exportando = ref(false);
  19. async function onExport() {
  20. if (!activeConversationId.value || exportando.value) return;
  21. exportando.value = true;
  22. try {
  23. await exportConversation(activeConversationId.value);
  24. } finally {
  25. exportando.value = false;
  26. }
  27. }
  28. const landingDraft = ref("");
  29. const isLanding = computed(() => {
  30. const ms = chatMessages.value ?? [];
  31. if (ms.length === 0) return true;
  32. if (ms.length === 1 && ms[0]?.role === "assistant") return true;
  33. return false;
  34. });
  35. async function onLandingSend() {
  36. const text = landingDraft.value;
  37. if (!text.trim()) return;
  38. landingDraft.value = "";
  39. await sendMessage(text);
  40. }
  41. </script>
  42. <template>
  43. <LayoutSistema>
  44. <div v-if="isLanding" class="min-h-[calc(100vh-var(--layout-header-height))] flex items-center justify-center pb-12 md:pb-16">
  45. <div class="w-full max-w-[720px]">
  46. <div class="text-3xl font-extrabold tracking-tight">Como posso ajudar?</div>
  47. <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
  48. Digite sua pergunta abaixo. Você também pode alimentar a base pela barra lateral.
  49. </div>
  50. <div class="mt-6 rounded-2xl border border-gray-200 bg-white/70 p-4 shadow-sm backdrop-blur-md dark:border-white/10 dark:bg-white/[0.04]">
  51. <textarea
  52. v-model="landingDraft"
  53. rows="3"
  54. placeholder="Digite sua pergunta..."
  55. class="min-h-20 resize-none border-0 bg-transparent shadow-none focus:ring-0 !outline-none"
  56. style="resize: none"
  57. @keydown.enter.exact.prevent="onLandingSend"
  58. />
  59. <div class="mt-3 flex items-center justify-between gap-3">
  60. <span class="text-xs text-gray-400 dark:text-gray-500">Enter para enviar · Shift+Enter para nova linha</span>
  61. <BaseButton :disabled="chatLoading || !landingDraft.trim()" @click="onLandingSend">
  62. <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  63. <path stroke-linecap="round" stroke-linejoin="round" d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5" />
  64. </svg>
  65. Enviar
  66. </BaseButton>
  67. </div>
  68. </div>
  69. <div v-if="chatError" class="mt-3 flex items-center justify-between gap-2 rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300">
  70. <span>Erro: {{ chatError }}</span>
  71. <button type="button" class="shrink-0 rounded p-0.5 opacity-60 hover:opacity-100 transition-opacity" aria-label="Fechar erro" @click="clearError">
  72. <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
  73. <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
  74. </svg>
  75. </button>
  76. </div>
  77. </div>
  78. </div>
  79. <div v-else class="relative h-full w-full">
  80. <div class="relative flex h-full w-full flex-col p-4 md:p-6">
  81. <div class="mb-2 flex items-center justify-end">
  82. <button
  83. v-if="activeConversationId"
  84. type="button"
  85. class="inline-flex items-center gap-1.5 rounded-lg border border-gray-200 bg-white/60 px-2.5 py-1.5 text-xs font-medium text-gray-500 transition-colors hover:bg-white hover:text-gray-700 disabled:opacity-50 dark:border-white/10 dark:bg-white/5 dark:text-white/50 dark:hover:bg-white/10 dark:hover:text-white/80"
  86. :disabled="exportando || chatLoading"
  87. @click="onExport"
  88. >
  89. <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  90. <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
  91. </svg>
  92. {{ exportando ? "Exportando..." : "Exportar .md" }}
  93. </button>
  94. </div>
  95. <ChatWindow class="flex-1 min-h-1" :messages="chatMessages" :loading="chatLoading" :error="chatError" @send="sendMessage" @clearError="clearError" />
  96. </div>
  97. </div>
  98. </LayoutSistema>
  99. </template>