- Layout improvements for the Chat client

This commit is contained in:
Josako
2025-07-23 18:06:47 +02:00
parent ccc1a2afb8
commit 32df3d0589
14 changed files with 339 additions and 93 deletions

View File

@@ -21,11 +21,15 @@ active_text_color<template>
:allow-file-upload="true"
:allow-voice-message="false"
:form-data="currentInputFormData"
:active-ai-message="activeAiMessage"
:api-prefix="apiPrefix"
@send-message="sendMessage"
@update-message="updateCurrentMessage"
@upload-file="handleFileUpload"
@record-voice="handleVoiceRecord"
@submit-form="submitFormFromInput"
@specialist-error="handleSpecialistError"
@specialist-complete="handleSpecialistComplete"
ref="chatInput"
class="chat-input-area"
></chat-input>
@@ -150,6 +154,11 @@ export default {
return this.isSearching ? this.filteredMessages : this.allMessages;
},
// Active AI message that should be shown in ChatInput
activeAiMessage() {
return this.allMessages.find(msg => msg.isTemporarilyAtBottom);
},
hasMessages() {
return this.allMessages.length > 0;
},

View File

@@ -1,6 +1,21 @@
<template>
<div class="chat-input-container">
<!-- Material Icons worden nu globaal geladen in scripts.html -->
<!-- Active AI Message Area -->
<div v-if="activeAiMessage" class="active-ai-message-area">
<chat-message
:message="activeAiMessage"
:is-submitting-form="false"
:api-prefix="apiPrefix"
:is-latest-ai-message="true"
:is-in-input-area="true"
@image-loaded="handleImageLoaded"
@specialist-complete="$emit('specialist-complete', $event)"
@specialist-error="$emit('specialist-error', $event)"
></chat-message>
</div>
<!-- Dynamisch formulier container -->
<div v-if="formData" class="dynamic-form-container">
<!-- De titel wordt in DynamicForm weergegeven en niet hier om dubbele titels te voorkomen -->
@@ -62,13 +77,15 @@
<script>
// Importeer de benodigde componenten
import DynamicForm from './DynamicForm.vue';
import ChatMessage from './ChatMessage.vue';
import { useIconManager } from '../js/composables/useIconManager.js';
import { useTranslationClient } from '../js/composables/useTranslation.js';
export default {
name: 'ChatInput',
components: {
'dynamic-form': DynamicForm
'dynamic-form': DynamicForm,
'chat-message': ChatMessage
},
setup(props) {
const { watchIcon } = useIconManager();
@@ -103,8 +120,16 @@ export default {
type: Object,
default: null
},
activeAiMessage: {
type: Object,
default: null
},
apiPrefix: {
type: String,
default: ''
}
},
emits: ['send-message', 'update-message', 'submit-form'],
emits: ['send-message', 'update-message', 'submit-form', 'specialist-complete', 'specialist-error'],
data() {
return {
localMessage: this.currentMessage,
@@ -377,6 +402,12 @@ export default {
if (JSON.stringify(newValues) !== JSON.stringify(this.formValues)) {
this.formValues = JSON.parse(JSON.stringify(newValues));
}
},
handleImageLoaded() {
// Handle image loaded events from ChatMessage component
// This method can be used for layout adjustments if needed
console.log('Image loaded in active AI message');
}
}
};
@@ -500,4 +531,28 @@ export default {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Active AI Message Area - positioned at top of ChatInput */
.active-ai-message-area {
margin-bottom: 15px;
padding: 12px;
background-color: var(--active-background-color);
color: var(--active-text-color);
border-radius: 8px;
font-family: Arial, sans-serif;
font-size: 14px;
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
}
/* Ensure the active AI message integrates well with ChatInput styling */
.active-ai-message-area .message {
margin-bottom: 0;
max-width: 100%;
}
.active-ai-message-area .message-content {
background-color: transparent;
border: none;
padding: 0;
}
</style>

View File

@@ -9,6 +9,7 @@
:task-id="message.taskId"
:api-prefix="apiPrefix"
:is-latest-ai-message="isLatestAiMessage"
:is-in-input-area="isInInputArea"
class="message-progress"
@specialist-complete="handleSpecialistComplete"
@specialist-error="handleSpecialistError"
@@ -169,11 +170,12 @@ export default {
isLatestAiMessage: {
type: Boolean,
default: false
}
},
isInStickyArea: {
isInInputArea: {
type: Boolean,
default: false },
default: false
}
},
emits: ['image-loaded', 'retry-message', 'specialist-complete', 'specialist-error'],
data() {
return {
@@ -309,7 +311,13 @@ export default {
// Add class for messages in sticky area
if (this.isInStickyArea) {
classes += " sticky-area";
}
}
// Add class for messages in input area
if (this.isInInputArea) {
classes += " input-area";
}
return classes;
}
}
@@ -338,12 +346,10 @@ export default {
.message.ai.temporarily-at-bottom {
background-color: var(--active-background-color);
color: var(--active-text-color);
border-left: 3px solid var(--active-text-color);
opacity: 0.9;
border-radius: 8px;
padding: 8px;
margin: 8px 0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Styling for messages in sticky area - override history colors with active colors */
@@ -361,6 +367,14 @@ export default {
border-radius: 8px;
padding: 12px;
}
/* Active styling for messages in input area */
.message.input-area .message-content {
background-color: var(--active-background-color);
color: var(--active-text-color);
border-radius: 8px;
padding: 12px;
}
.message-content {
width: 100%;
font-family: Arial, sans-serif;

View File

@@ -297,6 +297,7 @@ export default {
.dynamic-form {
padding: 15px;
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
}
.form-header {
@@ -347,11 +348,10 @@ export default {
}
.form-field label {
display: block;
margin-bottom: 6px;
font-weight: 500;
font-size: 0.9rem;
color: #555;
display: block;
margin-bottom: 6px;
font-weight: 500;
font-size: 0.9rem;
}
.form-field input,
@@ -396,7 +396,6 @@ export default {
.checkbox-text {
font-size: 0.9rem;
color: #555;
}
.field-description {

View File

@@ -373,7 +373,6 @@ export default {
.checkbox-text {
font-size: 0.9rem;
color: #555;
}
/* Context field styling */

View File

@@ -174,10 +174,6 @@ export default {
.language-selector {
padding: 8px 12px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.2);
color: var(--sidebar-color);
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s ease;

View File

@@ -8,7 +8,7 @@
</div>
<!-- Empty state -->
<div v-if="normalMessages.length === 0 && !stickyAiMessage" class="empty-state">
<div v-if="normalMessages.length === 0" class="empty-state">
<div class="empty-icon">💬</div>
<div class="empty-text">Nog geen berichten</div>
<div class="empty-subtext">Start een gesprek door een bericht te typen!</div>
@@ -35,18 +35,6 @@
<typing-indicator v-if="isTyping"></typing-indicator>
</div>
<!-- Sticky bottom area for temporarily positioned AI messages -->
<div v-if="stickyAiMessage" class="sticky-ai-area">
<chat-message
:message="stickyAiMessage"
:is-submitting-form="isSubmittingForm"
:api-prefix="apiPrefix"
:is-latest-ai-message="isLatestAiMessage(stickyAiMessage)"
:is-in-sticky-area="true" @image-loaded="handleImageLoaded"
@specialist-complete="$emit('specialist-complete', $event)"
@specialist-error="$emit('specialist-error', $event)"
></chat-message>
</div>
</div>
</template>
@@ -104,10 +92,6 @@ export default {
normalMessages() {
return this.messages.filter(msg => !msg.isTemporarilyAtBottom);
},
// AI message that should be shown in sticky bottom area
stickyAiMessage() {
return this.messages.find(msg => msg.isTemporarilyAtBottom);
}
},
watch: {
messages: {
@@ -291,17 +275,6 @@ export default {
scroll-behavior: smooth;
}
/* Sticky bottom area for temporarily positioned AI messages */
.sticky-ai-area {
flex-shrink: 0;
max-height: 30%; /* Takes max 30% of available space */
border-top: 1px solid #e0e0e0;
background-color: var(--active-background-color);
color: var(--active-text-color);
padding: 10px;
overflow-y: auto;
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
}
.load-more-indicator {
text-align: center;

View File

@@ -3,7 +3,8 @@
<div v-if="shouldShowFullTracker" class="progress-tracker" :class="{
'expanded': isExpanded,
'completed': isCompleted && !hasError,
'error': error || hasError
'error': error || hasError,
'input-area': isInInputArea
}">
<div
class="progress-header"
@@ -106,6 +107,10 @@ export default {
isLatestAiMessage: {
type: Boolean,
default: false
},
isInInputArea: {
type: Boolean,
default: false
}
},
emits: ['specialist-complete', 'specialist-error'],
@@ -590,8 +595,8 @@ export default {
}
.working-animation-only {
width: 24px;
height: 24px;
width: 50px;
height: 50px;
/* Evie working animatie styling */
}
@@ -604,4 +609,11 @@ export default {
font-size: 13px;
border: 1px solid #f44336;
}
/* Input area specific styling */
.progress-tracker.input-area .progress-title {
justify-content: flex-start;
}
</style>

View File

@@ -49,12 +49,11 @@ const handleImageError = () => {
justify-content: center;
align-items: center;
padding: 20px 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.logo-image {
max-width: 100%;
max-height: 60px;
max-height: 100%;
object-fit: contain;
}

View File

@@ -23,7 +23,6 @@ const props = defineProps({
.sidebar-make-name {
padding: 15px;
text-align: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.make-name-text {