From fed05ec6035a0cb0c5cb8977020b5b66ac77a696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20GS=20Pereira?= Date: Mon, 25 May 2026 16:37:29 -0300 Subject: [PATCH] fix(shared): mirror pre-deploy fixes from openplc-web MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror of openplc-web#412 — keeps src/frontend/** and the shared globals.css byte-identical between the two IDEs so the Shared Surface Sync CI check stays green. Bundled (per file): - src/frontend/components/_molecules/menu-bar/menus/help.tsx → Community Support points at edge.autonomylogic.com/forum. - src/frontend/store/slices/ai/{types,slice,index}.ts + src/frontend/store/slices/index.ts → Drop the silent 50-message store cap so long conversations don't lose their oldest turns. addMessage / replaceMessages keep every message; backend's 500/conv cap remains the only ceiling. - src/frontend/store/__tests__/ai-slice.test.ts → Flip the two cap-related tests to assert no truncation. - src/backend/shared/styles/globals.css → Add `@keyframes spin` so Tailwind's `animate-spin` utility actually spins; the project-wide tailwind config overrode `theme.keyframes` instead of extending it and silently killed the default. Mirror of: https://github.com/Autonomy-Logic/openplc-web/pull/412 Co-Authored-By: Claude Opus 4.7 (1M context) --- src/backend/shared/styles/globals.css | 10 ++++++++ .../_molecules/menu-bar/menus/help.tsx | 2 +- src/frontend/store/__tests__/ai-slice.test.ts | 25 +++++++++---------- src/frontend/store/slices/ai/index.ts | 1 - src/frontend/store/slices/ai/slice.ts | 7 +----- src/frontend/store/slices/ai/types.ts | 6 ----- src/frontend/store/slices/index.ts | 1 - 7 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/backend/shared/styles/globals.css b/src/backend/shared/styles/globals.css index 6f923c0e3..d89679fb8 100644 --- a/src/backend/shared/styles/globals.css +++ b/src/backend/shared/styles/globals.css @@ -72,6 +72,16 @@ } } +/* Tailwind's animate-spin utility emits `animation: spin 1s linear infinite` + but our tailwind.config.ts overrides `theme.keyframes` (instead of + extending it), wiping out the default `spin` keyframe. Redefine it here + so animate-spin works app-wide. */ +@keyframes spin { + to { + transform: rotate(360deg); + } +} + .ai-chat-typing-dot { display: inline-block; width: 5px; diff --git a/src/frontend/components/_molecules/menu-bar/menus/help.tsx b/src/frontend/components/_molecules/menu-bar/menus/help.tsx index 30477ba96..712d3351e 100644 --- a/src/frontend/components/_molecules/menu-bar/menus/help.tsx +++ b/src/frontend/components/_molecules/menu-bar/menus/help.tsx @@ -14,7 +14,7 @@ export const HelpMenu = () => { const handleOpenCommunitySupport = () => { try { - window.open('https://openplc.discussion.community/', '_blank') + window.open('https://edge.autonomylogic.com/forum', '_blank') } catch (error) { console.error('Error opening link:', error) } diff --git a/src/frontend/store/__tests__/ai-slice.test.ts b/src/frontend/store/__tests__/ai-slice.test.ts index 3216caa56..09356cea1 100644 --- a/src/frontend/store/__tests__/ai-slice.test.ts +++ b/src/frontend/store/__tests__/ai-slice.test.ts @@ -2,7 +2,6 @@ import { createStore, StoreApi } from 'zustand/vanilla' import { createAISlice, createAISliceFactory } from '../slices/ai/slice' import type { AISlice, ChatMessage } from '../slices/ai/types' -import { MAX_CONVERSATION_MESSAGES } from '../slices/ai/types' // --------------------------------------------------------------------------- // Helpers @@ -282,17 +281,16 @@ describe('createAISlice', () => { expect(messages[1].id).toBe('msg-2') }) - it('enforces MAX_CONVERSATION_MESSAGES by keeping the most recent messages', () => { - expect(MAX_CONVERSATION_MESSAGES).toBe(50) - - for (let i = 0; i < MAX_CONVERSATION_MESSAGES + 5; i++) { + it('retains every appended message without truncation', () => { + const total = 75 + for (let i = 0; i < total; i++) { store.getState().aiActions.addMessage(makeMessage({ id: `msg-${i}`, content: `Message ${i}` })) } const messages = store.getState().ai.messages - expect(messages).toHaveLength(MAX_CONVERSATION_MESSAGES) - expect(messages[0].id).toBe('msg-5') - expect(messages[MAX_CONVERSATION_MESSAGES - 1].id).toBe(`msg-${MAX_CONVERSATION_MESSAGES + 4}`) + expect(messages).toHaveLength(total) + expect(messages[0].id).toBe('msg-0') + expect(messages[total - 1].id).toBe(`msg-${total - 1}`) }) }) @@ -613,17 +611,18 @@ describe('createAISlice', () => { expect(store.getState().ai.error).toBeNull() }) - it('caps at MAX_CONVERSATION_MESSAGES, keeping the most recent', () => { + it('replaces with the full payload without truncation', () => { + const total = 75 const many: ChatMessage[] = [] - for (let i = 0; i < MAX_CONVERSATION_MESSAGES + 7; i++) { + for (let i = 0; i < total; i++) { many.push(makeMessage({ id: `m-${i}`, content: `Message ${i}` })) } store.getState().aiActions.replaceMessages(many) const messages = store.getState().ai.messages - expect(messages).toHaveLength(MAX_CONVERSATION_MESSAGES) - expect(messages[0].id).toBe('m-7') - expect(messages[MAX_CONVERSATION_MESSAGES - 1].id).toBe(`m-${MAX_CONVERSATION_MESSAGES + 6}`) + expect(messages).toHaveLength(total) + expect(messages[0].id).toBe('m-0') + expect(messages[total - 1].id).toBe(`m-${total - 1}`) }) }) diff --git a/src/frontend/store/slices/ai/index.ts b/src/frontend/store/slices/ai/index.ts index e3095fd36..43567e4e5 100644 --- a/src/frontend/store/slices/ai/index.ts +++ b/src/frontend/store/slices/ai/index.ts @@ -1,3 +1,2 @@ export { createAISlice, createAISliceFactory } from './slice' export type { AIActions, AIPreferences, AISlice, AIState, ChatMessage, ChatMessageRole, DiffReviewEntry } from './types' -export { MAX_CONVERSATION_MESSAGES } from './types' diff --git a/src/frontend/store/slices/ai/slice.ts b/src/frontend/store/slices/ai/slice.ts index c041659de..237a87776 100644 --- a/src/frontend/store/slices/ai/slice.ts +++ b/src/frontend/store/slices/ai/slice.ts @@ -3,7 +3,6 @@ import { StateCreator } from 'zustand' import type { AIFeatureConfig } from '../../../../middleware/shared/ports/types' import type { AISlice } from './types' -import { MAX_CONVERSATION_MESSAGES } from './types' const DEFAULT_AI_STATE: AISlice['ai'] = { isEnabled: false, @@ -152,9 +151,6 @@ export function createAISliceFactory(config?: AIFeatureConfig): StateCreator { ai.messages.push(message) - if (ai.messages.length > MAX_CONVERSATION_MESSAGES) { - ai.messages = ai.messages.slice(-MAX_CONVERSATION_MESSAGES) - } }), ) }, @@ -292,8 +288,7 @@ export function createAISliceFactory(config?: AIFeatureConfig): StateCreator { setState( produce(({ ai }: AISlice) => { - ai.messages = - messages.length > MAX_CONVERSATION_MESSAGES ? messages.slice(-MAX_CONVERSATION_MESSAGES) : messages + ai.messages = messages ai.error = null }), ) diff --git a/src/frontend/store/slices/ai/types.ts b/src/frontend/store/slices/ai/types.ts index f3f4f7a81..831ad71a3 100644 --- a/src/frontend/store/slices/ai/types.ts +++ b/src/frontend/store/slices/ai/types.ts @@ -193,9 +193,3 @@ export type AIActions = { export type AISlice = AIState & { aiActions: AIActions } - -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -export const MAX_CONVERSATION_MESSAGES = 50 diff --git a/src/frontend/store/slices/index.ts b/src/frontend/store/slices/index.ts index b9b0206ef..81ff85dc0 100644 --- a/src/frontend/store/slices/index.ts +++ b/src/frontend/store/slices/index.ts @@ -1,6 +1,5 @@ export type { AISlice } from './ai' export { createAISlice, createAISliceFactory } from './ai' -export { MAX_CONVERSATION_MESSAGES } from './ai' export type { ConsoleSlice } from './console' export { createConsoleSlice } from './console' export type { DeviceSlice } from './device'