- Wrap client in @vueuse/core to abstract mobile client dimensions
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dist/chat-client.js": "dist/chat-client.f7134231.js",
|
||||
"dist/chat-client.css": "dist/chat-client.99e10656.css",
|
||||
"dist/chat-client.js": "dist/chat-client.f8ee4d5a.js",
|
||||
"dist/chat-client.css": "dist/chat-client.2fffefae.css",
|
||||
"dist/main.js": "dist/main.6a617099.js",
|
||||
"dist/main.css": "dist/main.7182aac3.css"
|
||||
}
|
||||
@@ -9,6 +9,13 @@
|
||||
--message-bot-bg: #f8f9fa;
|
||||
--border-radius: 8px;
|
||||
--spacing: 16px;
|
||||
|
||||
/* Nieuwe, veilige viewport-variabelen voor mobiele lay-out
|
||||
- --vvh blijft als fallback uit viewport.js
|
||||
- --safe-vh wordt gezet door useChatViewport()
|
||||
- --safe-bottom-inset wordt gebruikt voor ondermarge bij de chat input */
|
||||
--safe-vh: var(--vvh, 1vh);
|
||||
--safe-bottom-inset: 0px;
|
||||
}
|
||||
|
||||
/* App container layout */
|
||||
@@ -16,7 +23,7 @@
|
||||
display: flex;
|
||||
/* Use visual viewport variable when available */
|
||||
min-height: 0;
|
||||
height: calc(var(--vvh, 1vh) * 100);
|
||||
height: calc(var(--safe-vh, var(--vvh, 1vh)) * 100);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -97,7 +104,8 @@
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: calc(var(--vvh, 1vh) * 100); min-height: 0;
|
||||
height: calc(var(--safe-vh, var(--vvh, 1vh)) * 100);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* Base reset & overflow control */
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
// useChatViewport.js
|
||||
// Centrale viewport-/keyboard-laag voor de chat client.
|
||||
// - Gebruikt visualViewport + VueUse (via window.VueUse) om veilige hoogte
|
||||
// en keyboard-status te bepalen
|
||||
// - Stelt CSS-variabelen --safe-vh en --safe-bottom-inset in op <html>
|
||||
// - Beheert body-klassen zoals chat-keyboard-open en ios-safari
|
||||
|
||||
import { ref, computed, watchEffect, onMounted, onBeforeUnmount } from 'vue';
|
||||
|
||||
// Haal VueUse-composables uit window.VueUse, die in de chat-bundel
|
||||
// worden geïnitialiseerd via nginx/frontend_src/js/vueuse-setup.js.
|
||||
// Als ze ontbreken (bv. in een testsituatie), vallen we terug op
|
||||
// eenvoudige lokale shims.
|
||||
|
||||
const vueUse = (typeof window !== 'undefined' && window.VueUse) || {};
|
||||
let { useEventListener, useWindowSize } = vueUse;
|
||||
|
||||
// Fallback-shim voor useEventListener
|
||||
if (!useEventListener) {
|
||||
useEventListener = (target, event, handler, options) => {
|
||||
if (!target || !target.addEventListener) return () => {};
|
||||
const opts = options || { passive: true };
|
||||
target.addEventListener(event, handler, opts);
|
||||
return () => target.removeEventListener(event, handler, opts);
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback-shim voor useWindowSize
|
||||
if (!useWindowSize) {
|
||||
useWindowSize = () => {
|
||||
const height = ref(typeof window !== 'undefined' ? window.innerHeight : 0);
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('resize', () => {
|
||||
height.value = window.innerHeight;
|
||||
}, { passive: true });
|
||||
}
|
||||
return { height };
|
||||
};
|
||||
}
|
||||
|
||||
function detectIosSafari() {
|
||||
if (typeof navigator === 'undefined') return false;
|
||||
const ua = navigator.userAgent || navigator.vendor || (window && window.opera) || '';
|
||||
const isIOS = /iP(ad|hone|od)/.test(ua);
|
||||
const isSafari = /Safari/.test(ua) && !/Chrome|CriOS|FxiOS/.test(ua);
|
||||
return isIOS && isSafari;
|
||||
}
|
||||
|
||||
// Drempel voor het detecteren van keyboard-open op basis van hoogteverschil
|
||||
const KEYBOARD_DELTA_THRESHOLD = 120; // px, afgestemd op bestaande viewport.js heuristiek
|
||||
|
||||
export function useChatViewport() {
|
||||
const isClient = typeof window !== 'undefined' && typeof document !== 'undefined';
|
||||
|
||||
const safeHeight = ref(isClient ? window.innerHeight : 0);
|
||||
const baselineHeight = ref(null); // baseline voor keyboard-detectie
|
||||
const keyboardOpen = ref(false);
|
||||
const safeBottomInset = ref(0);
|
||||
|
||||
const { height: windowHeight } = useWindowSize();
|
||||
const isIosSafari = ref(isClient ? detectIosSafari() : false);
|
||||
|
||||
const isMobile = computed(() => {
|
||||
if (!isClient) return false;
|
||||
// Houd de definitie in lijn met bestaande CSS (max-width: 768px)
|
||||
return window.innerWidth <= 768;
|
||||
});
|
||||
|
||||
function applyToCss() {
|
||||
if (!isClient) return;
|
||||
const root = document.documentElement;
|
||||
const vhUnit = (safeHeight.value || windowHeight.value || 0) / 100;
|
||||
|
||||
if (vhUnit > 0) {
|
||||
root.style.setProperty('--safe-vh', `${vhUnit}px`);
|
||||
}
|
||||
|
||||
root.style.setProperty('--safe-bottom-inset', `${safeBottomInset.value || 0}px`);
|
||||
|
||||
// Body-klassen
|
||||
document.body.classList.toggle('chat-keyboard-open', !!keyboardOpen.value);
|
||||
|
||||
if (isIosSafari.value) {
|
||||
root.classList.add('ios-safari');
|
||||
} else {
|
||||
root.classList.remove('ios-safari');
|
||||
}
|
||||
}
|
||||
|
||||
function updateFromVisualViewport() {
|
||||
if (!isClient) return;
|
||||
const vv = window.visualViewport;
|
||||
|
||||
const currentHeight = vv ? vv.height : window.innerHeight;
|
||||
safeHeight.value = currentHeight;
|
||||
|
||||
// Bepaal een eenvoudige bottom-inset: verschil tussen layout- en visual viewport
|
||||
const layoutHeight = window.innerHeight || currentHeight;
|
||||
const inset = Math.max(0, layoutHeight - currentHeight);
|
||||
safeBottomInset.value = inset;
|
||||
|
||||
// Keyboard-detectie: vergelijk met baseline
|
||||
if (baselineHeight.value == null) {
|
||||
baselineHeight.value = currentHeight;
|
||||
}
|
||||
const delta = (baselineHeight.value || currentHeight) - currentHeight;
|
||||
keyboardOpen.value = delta > KEYBOARD_DELTA_THRESHOLD;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!isClient) return;
|
||||
|
||||
// Initiale meting
|
||||
updateFromVisualViewport();
|
||||
applyToCss();
|
||||
|
||||
const vv = window.visualViewport;
|
||||
|
||||
// Luister naar relevante events via VueUse
|
||||
const stopResize = vv
|
||||
? useEventListener(vv, 'resize', () => {
|
||||
updateFromVisualViewport();
|
||||
applyToCss();
|
||||
})
|
||||
: useEventListener(window, 'resize', () => {
|
||||
updateFromVisualViewport();
|
||||
applyToCss();
|
||||
});
|
||||
|
||||
const stopScroll = vv
|
||||
? useEventListener(vv, 'scroll', () => {
|
||||
updateFromVisualViewport();
|
||||
applyToCss();
|
||||
})
|
||||
: () => {};
|
||||
|
||||
const stopOrientation = useEventListener(window, 'orientationchange', () => {
|
||||
// Bij oriëntatie-wijziging baseline resetten
|
||||
baselineHeight.value = null;
|
||||
updateFromVisualViewport();
|
||||
applyToCss();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopResize && stopResize();
|
||||
stopScroll && stopScroll();
|
||||
stopOrientation && stopOrientation();
|
||||
});
|
||||
});
|
||||
|
||||
// Reageer ook op generieke windowHeight veranderingen (fallback)
|
||||
watchEffect(() => {
|
||||
if (!isClient) return;
|
||||
if (!window.visualViewport) {
|
||||
safeHeight.value = windowHeight.value;
|
||||
safeBottomInset.value = 0;
|
||||
keyboardOpen.value = false;
|
||||
applyToCss();
|
||||
}
|
||||
});
|
||||
|
||||
// Initiale toepassing voor SSR / eerste render
|
||||
if (isClient) {
|
||||
applyToCss();
|
||||
}
|
||||
|
||||
return {
|
||||
safeHeight,
|
||||
safeBottomInset,
|
||||
keyboardOpen,
|
||||
isMobile,
|
||||
isIosSafari,
|
||||
};
|
||||
}
|
||||
|
||||
export default useChatViewport;
|
||||
13
eveai_chat_client/static/assets/vue-components/ChatRoot.vue
Normal file
13
eveai_chat_client/static/assets/vue-components/ChatRoot.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<SafeViewport>
|
||||
<ChatApp />
|
||||
</SafeViewport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// ChatRoot.vue
|
||||
// Kleine root-component die de ChatApp binnen de SafeViewport wrapper rendert.
|
||||
|
||||
import ChatApp from './ChatApp.vue';
|
||||
import SafeViewport from './SafeViewport.vue';
|
||||
</script>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="safe-viewport-wrapper">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// SafeViewport.vue
|
||||
// Wrapper-component die de chatapplicatie omhult en de viewport-/keyboard-logica
|
||||
// initialiseert via de useChatViewport composable.
|
||||
|
||||
// Belangrijk: de composable zelf leeft onder nginx/frontend_src/js zodat hij
|
||||
// binnen dezelfde npm-package valt als de bundel (en zo @vueuse/core kan resolven).
|
||||
// Daarom gebruiken we hier een relatieve pad vanuit de Vue-component naar die map.
|
||||
|
||||
import useChatViewport from '../js/composables/useChatViewport.js';
|
||||
|
||||
// Initialiseer de viewport-logica zodra deze wrapper instantiëert.
|
||||
useChatViewport();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.safe-viewport-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
@@ -10,7 +10,6 @@ import '../../../eveai_chat_client/static/assets/css/form-message.css';
|
||||
// Dependencies
|
||||
import { createApp, version } from 'vue';
|
||||
import { marked } from 'marked';
|
||||
import { FormField } from '../../../../../../../../../Users/josako/Library/Application Support/JetBrains/PyCharm2025.1/scratches/old js files/FormField.js';
|
||||
|
||||
// Import LanguageProvider for sidebar translation support
|
||||
import { createLanguageProvider, LANGUAGE_PROVIDER_KEY } from '../../../eveai_chat_client/static/assets/js/services/LanguageProvider.js';
|
||||
@@ -29,9 +28,13 @@ console.log('Components loaded:', Object.keys(Components));
|
||||
// Import specifieke componenten
|
||||
import LanguageSelector from '../../../eveai_chat_client/static/assets/vue-components/LanguageSelector.vue';
|
||||
import ChatApp from '../../../eveai_chat_client/static/assets/vue-components/ChatApp.vue';
|
||||
import ChatRoot from '../../../eveai_chat_client/static/assets/vue-components/ChatRoot.vue';
|
||||
import SideBar from '../../../eveai_chat_client/static/assets/vue-components/SideBar.vue';
|
||||
import MobileHeader from '../../../eveai_chat_client/static/assets/vue-components/MobileHeader.vue';
|
||||
|
||||
// VueUse-setup voor de chatclient (maakt composables beschikbaar via window.VueUse)
|
||||
import './vueuse-setup.js';
|
||||
|
||||
// Globale Vue error tracking
|
||||
window.addEventListener('error', function(event) {
|
||||
console.error('🚨 [Global Error]', event.error);
|
||||
@@ -230,8 +233,8 @@ function initializeChatApp() {
|
||||
allowedLanguages: window.chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de']
|
||||
};
|
||||
|
||||
// Mount de component met alle nodige componenten
|
||||
const app = createApp(ChatApp, props);
|
||||
// Mount de component via ChatRoot zodat SafeViewport de layout kan beheren
|
||||
const app = createApp(ChatRoot, props);
|
||||
|
||||
// SSE verbinding configuratie - injecteren in ChatApp component
|
||||
app.provide('sseConfig', {
|
||||
|
||||
15
nginx/frontend_src/js/vueuse-setup.js
Normal file
15
nginx/frontend_src/js/vueuse-setup.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// vueuse-setup.js
|
||||
// Setup-module voor VueUse in de chat client.
|
||||
// - Importeert benodigde composables uit '@vueuse/core'
|
||||
// - Maakt ze beschikbaar via window.VueUse zodat static assets
|
||||
// onder eveai_chat_client/static ze kunnen gebruiken zonder
|
||||
// directe npm-imports.
|
||||
|
||||
import { useEventListener, useWindowSize } from '@vueuse/core';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.VueUse = Object.assign({}, window.VueUse, {
|
||||
useEventListener,
|
||||
useWindowSize,
|
||||
});
|
||||
}
|
||||
68
nginx/package-lock.json
generated
68
nginx/package-lock.json
generated
@@ -6,12 +6,13 @@
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"animejs": "^4.0.2",
|
||||
"bootstrap": "^5.3.6",
|
||||
"datatables.net": "^2.3.1",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jquery": "^3.7.1",
|
||||
"marked": "^16.0.0",
|
||||
"marked": "16.3.0",
|
||||
"nouislider": "^15.8.1",
|
||||
"parallax": "^0.0.0",
|
||||
"prismjs": "^1.30.0",
|
||||
@@ -22,7 +23,6 @@
|
||||
"vue": "^3.5.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/reporter-bundle-analyzer": "^2.15.2",
|
||||
"@parcel/transformer-sass": "^2.15.2",
|
||||
"@parcel/transformer-vue": "^2.15.2",
|
||||
"parcel": "^2.15.2"
|
||||
@@ -1360,26 +1360,6 @@
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/reporter-bundle-analyzer": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/reporter-bundle-analyzer/-/reporter-bundle-analyzer-2.16.0.tgz",
|
||||
"integrity": "sha512-81EazkM7YjeFvzRRlKKV8kmLfLXfWlfLqlpC8jFRXthC43aZtdqMnzRgi9Q+6FIB7Ft3vnMXZPqRs4cn7+XgUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@parcel/plugin": "2.16.0",
|
||||
"@parcel/utils": "2.16.0",
|
||||
"nullthrows": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.0.0",
|
||||
"parcel": "^2.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/reporter-cli": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.16.0.tgz",
|
||||
@@ -2722,6 +2702,12 @@
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
||||
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
|
||||
@@ -2822,6 +2808,44 @@
|
||||
"integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.0.0.tgz",
|
||||
"integrity": "sha512-d6tKRWkZE8IQElX2aHBxXOMD478fHIYV+Dzm2y9Ag122ICBpNKtGICiXKOhWU3L1kKdttDD9dCMS4bGP3jhCTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.21",
|
||||
"@vueuse/metadata": "14.0.0",
|
||||
"@vueuse/shared": "14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.0.0.tgz",
|
||||
"integrity": "sha512-6yoGqbJcMldVCevkFiHDBTB1V5Hq+G/haPlGIuaFZHpXC0HADB0EN1ryQAAceiW+ryS3niUwvdFbGiqHqBrfVA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.0.0.tgz",
|
||||
"integrity": "sha512-mTCA0uczBgurRlwVaQHfG0Ja7UdGe4g9mwffiJmvLiTtp1G4AQyIjej6si/k8c8pUwTfVpNufck+23gXptPAkw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"tabulator-tables": "^6.3.1",
|
||||
"typed.js": "^2.1.0",
|
||||
"vanilla-jsoneditor": "^3.5.0",
|
||||
"vue": "^3.5.17"
|
||||
"vue": "^3.5.17",
|
||||
"@vueuse/core": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/transformer-sass": "^2.15.2",
|
||||
|
||||
Reference in New Issue
Block a user