From 176b6a2a1fe72ab0f799ab3045767eb092f73534 Mon Sep 17 00:00:00 2001 From: cstns Date: Fri, 24 Jul 2026 15:38:42 +0300 Subject: [PATCH 1/3] Update webpack config with enhanced devServer setup and caching configuration --- config/webpack.config.js | 43 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index ce824aa6fa..fc0675f2c4 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -158,9 +158,12 @@ module.exports = function (env, argv) { new DotenvPlugin(), new DefinePlugin({ __VUE_OPTIONS_API__: true, - __VUE_PROD_DEVTOOLS__: devMode + __VUE_PROD_DEVTOOLS__: devMode, + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: devMode, + 'process.env.hotReloading': devMode && process.env.NODE_RUN_HOT === 'hot' }) ], + cache: { type: 'filesystem' }, optimization: { moduleIds: 'deterministic', runtimeChunk: 'single', @@ -182,8 +185,42 @@ module.exports = function (env, argv) { } }, devServer: { - port: 3000, - historyApiFallback: true + client: { + overlay: { errors: true, warnings: false }, + logging: 'error' + }, + port: 8080, + historyApiFallback: true, + static: { + directory: getPath('frontend/dist') + }, + compress: true, + hot: true, + liveReload: false, + devMiddleware: { + writeToDisk: true + }, + proxy: [ + { + context: ['/api/v1', '/account/', '/storage', '/ee/billing'], + target: 'http://localhost:3000', + // target: 'https://forge.flowfuse.local', + changeOrigin: true + }, + { + context: ['/api'], + target: 'https://registry.npmjs.com', + changeOrigin: true, + pathRewrite: { '^/api': '' } + }, + { + context: ['/api/**/projects/**/resources/stream', '/api/**/devices/**/editor/proxy/comms'], + target: 'ws://localhost:3000', + // target: 'https://forge.flowfuse.local', + ws: true, + changeOrigin: true + } + ] }, watchOptions: { poll: 1000, From 42f5d0ca1b7888e36acc270bef1a819e6852479c Mon Sep 17 00:00:00 2001 From: cstns Date: Fri, 24 Jul 2026 16:16:23 +0300 Subject: [PATCH 2/3] Add focus management to Expert components for better input handling --- .../drawers/expert/ExpertDrawer.vue | 6 +++--- frontend/src/components/expert/Expert.vue | 5 ++++- .../expert/components/ExpertChatInput.vue | 21 +++++++++++-------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/drawers/expert/ExpertDrawer.vue b/frontend/src/components/drawers/expert/ExpertDrawer.vue index 4c2878c6e1..b7ab60d7c4 100644 --- a/frontend/src/components/drawers/expert/ExpertDrawer.vue +++ b/frontend/src/components/drawers/expert/ExpertDrawer.vue @@ -35,7 +35,7 @@ - + @@ -90,9 +90,9 @@ export default { } }, mounted () { - // Wait for drawer slide-in animation to complete (300ms) before focusing + // Wait for drawer slide-in animation to complete (300ms) before focusing the chat input setTimeout(() => { - this.$refs.drawer?.focus() + this.$refs.panel?.focusInput() }, 350) }, methods: { diff --git a/frontend/src/components/expert/Expert.vue b/frontend/src/components/expert/Expert.vue index e0298b2017..ae82a1b764 100644 --- a/frontend/src/components/expert/Expert.vue +++ b/frontend/src/components/expert/Expert.vue @@ -18,7 +18,7 @@ - + @@ -153,6 +153,9 @@ export default { ]), ...mapActions(useProductExpertInsightsAgentStore, ['getCapabilities']), ...mapActions(useProductAssistantStore, ['reset']), + focusInput () { + this.$refs.chatInput?.focusInput() + }, handleStopGeneration () { if (this.abortController) { this.abortController.abort() diff --git a/frontend/src/components/expert/components/ExpertChatInput.vue b/frontend/src/components/expert/components/ExpertChatInput.vue index 0b4bfc8807..a485e64d76 100644 --- a/frontend/src/components/expert/components/ExpertChatInput.vue +++ b/frontend/src/components/expert/components/ExpertChatInput.vue @@ -242,6 +242,11 @@ export default { } }, watch: { + isWaitingForResponse (waiting, wasWaiting) { + if (wasWaiting && !waiting) { + this.$nextTick(() => this.$refs.textarea?.focus()) + } + }, pendingInput (text) { if (text) { this.inputText = text @@ -282,6 +287,9 @@ export default { methods: { ...mapActions(useProductAssistantStore, ['resetContextSelection']), ...mapActions(useProductExpertStore, ['startOver', 'handleQuery', 'setPendingInput', 'setComposerCommand', 'setQuestionCadence', 'setPlanMode', 'fetchToolCatalog']), + focusInput () { + this.$refs.textarea?.focus() + }, openSettings () { this.$refs.settingsDialog.show() }, @@ -295,15 +303,10 @@ export default { this.togglePinWithWidth() } - // handleQuery renders the reply itself (see the store); the composer only needs - // to refocus the input once the turn is on its way. - this.handleQuery({ query: message }) - .then(() => { - this.$nextTick(() => { - this.$refs.textarea.focus() - }) - }) - .catch(e => e) + // handleQuery renders the reply itself (see the store); the isWaitingForResponse + // watcher refocuses the input once the response completes (works for both HTTP + // and MQTT, where the promise resolves before the actual response arrives). + this.handleQuery({ query: message }).catch(e => e) this.inputText = '' this.requestingPlanChange = false From c6f9cf20c421c9c50ecf2d2ab679f3483b4023ec Mon Sep 17 00:00:00 2001 From: cstns Date: Fri, 24 Jul 2026 16:17:51 +0300 Subject: [PATCH 3/3] revert webpack config --- config/webpack.config.js | 43 +++------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index fc0675f2c4..ce824aa6fa 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -158,12 +158,9 @@ module.exports = function (env, argv) { new DotenvPlugin(), new DefinePlugin({ __VUE_OPTIONS_API__: true, - __VUE_PROD_DEVTOOLS__: devMode, - __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: devMode, - 'process.env.hotReloading': devMode && process.env.NODE_RUN_HOT === 'hot' + __VUE_PROD_DEVTOOLS__: devMode }) ], - cache: { type: 'filesystem' }, optimization: { moduleIds: 'deterministic', runtimeChunk: 'single', @@ -185,42 +182,8 @@ module.exports = function (env, argv) { } }, devServer: { - client: { - overlay: { errors: true, warnings: false }, - logging: 'error' - }, - port: 8080, - historyApiFallback: true, - static: { - directory: getPath('frontend/dist') - }, - compress: true, - hot: true, - liveReload: false, - devMiddleware: { - writeToDisk: true - }, - proxy: [ - { - context: ['/api/v1', '/account/', '/storage', '/ee/billing'], - target: 'http://localhost:3000', - // target: 'https://forge.flowfuse.local', - changeOrigin: true - }, - { - context: ['/api'], - target: 'https://registry.npmjs.com', - changeOrigin: true, - pathRewrite: { '^/api': '' } - }, - { - context: ['/api/**/projects/**/resources/stream', '/api/**/devices/**/editor/proxy/comms'], - target: 'ws://localhost:3000', - // target: 'https://forge.flowfuse.local', - ws: true, - changeOrigin: true - } - ] + port: 3000, + historyApiFallback: true }, watchOptions: { poll: 1000,