Skip to content

fix(shared): mirror pre-deploy fixes from openplc-web - #780

Merged
JoaoGSP merged 1 commit into
developmentfrom
fix/pre-deploy-editor-issues
May 26, 2026
Merged

fix(shared): mirror pre-deploy fixes from openplc-web#780
JoaoGSP merged 1 commit into
developmentfrom
fix/pre-deploy-editor-issues

Conversation

@JoaoGSP

@JoaoGSP JoaoGSP commented May 25, 2026

Copy link
Copy Markdown
Member

Summary

Mirror of openplc-web#412 — keeps src/frontend/** and the shared src/backend/shared/styles/globals.css byte-identical between the two IDEs so the Shared Surface Sync CI check stays green.

This PR contains only the shared-code changes. The chat UX work, PWA fix, and middleware-adapter changes from the web PR are web-only and not included here.

Files (byte-identical to the web PR)

  • src/frontend/components/_molecules/menu-bar/menus/help.tsx — Community Support link points at edge.autonomylogic.com/forum.
  • src/frontend/store/slices/ai/types.ts + slice.ts + index.ts + src/frontend/store/slices/index.ts — drop the silent 50-message store cap (MAX_CONVERSATION_MESSAGES). addMessage / replaceMessages keep every message; the backend's 500/conv cap stays 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's tailwind config overrode theme.keyframes instead of extending it, silently killing the default keyframe and breaking every animate-spin use across the app).

Mirror of

https://github.com/Autonomy-Logic/openplc-web/pull/412

Test plan

  • npm run lint / npm run test pass locally in editor
  • CI Shared Surface Sync now reports the two PRs as in-sync
  • Any spinner in the desktop IDE that uses animate-spin now actually spins
  • Help → Community Support opens the new forum URL
  • Long AI chat conversations don't lose oldest turns

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Resolved spinning animation compatibility issues to ensure animations render correctly across the entire application.
  • Improvements

    • Conversation message history is now unlimited—users can maintain larger conversation histories without automatic message removal.
  • Updates

    • Community forum link updated to direct users to the new forum location.

Review Change Stack

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: Autonomy-Logic/openplc-web#412

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

PR removes message truncation logic from the AI Redux slice to allow unlimited message retention, adds a CSS fallback for the spinner animation, and updates the help menu forum link. Supporting type definitions and re-exports are adjusted accordingly.

Changes

Core Updates

Layer / File(s) Summary
CSS Spinner Animation Fallback
src/backend/shared/styles/globals.css
Added @keyframes spin definition with documentation to ensure Tailwind's animate-spin utility works globally despite config overrides.
Help Menu Forum Link
src/frontend/components/_molecules/menu-bar/menus/help.tsx
Updated community support action to open new forum URL at https://edge.autonomylogic.com/forum.
AI Message Storage - Unlimited Retention
src/frontend/store/slices/ai/types.ts, src/frontend/store/slices/ai/slice.ts, src/frontend/store/__tests__/ai-slice.test.ts, src/frontend/store/slices/ai/index.ts, src/frontend/store/slices/index.ts
Removed MAX_CONVERSATION_MESSAGES import and truncation logic from addMessage and replaceMessages reducers so message arrays persist without size cap. Added AISlice type composing state with actions. Tests updated to assert full message retention without truncation. Updated exports to remove MAX_CONVERSATION_MESSAGES from public API surface and expose createAISlice, createAISliceFactory, and AISlice type.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Autonomy-Logic/openplc-editor#732: Both PRs touch AI store message retention around MAX_CONVERSATION_MESSAGES truncation behavior in frontend/store/slices/ai/slice.ts and related types.
  • Autonomy-Logic/openplc-editor#747: Both PRs modify AI Redux slice message and conversation handling, including frontend/store/slices/ai/slice.ts replaceMessages behavior and MAX_CONVERSATION_MESSAGES cap enforcement.
  • Autonomy-Logic/openplc-editor#739: Both PRs update the frontend AI slice/type surface in src/frontend/store/slices/ai/ files and adjust re-exports from the barrel module.

Suggested labels

bug

Suggested reviewers

  • thiagoralves
  • vmleroy

Poem

A rabbit spins in circles, round and round,
No more truncation limits will be found,
Messages flow freely through the store so bright,
The forum link now points to the right. 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(shared): mirror pre-deploy fixes from openplc-web' is fully related to the main change — it clearly identifies this as a mirror of upstream fixes with a focus on shared code.
Description check ✅ Passed The description is comprehensive and well-structured, covering the purpose, affected files, test plan, and references. However, it does not follow the provided template's required sections like 'References', 'DOD checklist', or explicit 'Description of the changes proposed' structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pre-deploy-editor-issues

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/frontend/store/slices/ai/slice.ts`:
- Around line 288-293: replaceMessages currently assigns the caller-owned
messages array by reference (ai.messages = messages), which can cause external
mutations to affect store state; inside the replaceMessages reducer (the
setState/produce callback for AISlice) replace that assignment with a cloned
array (e.g., a shallow copy like [...messages] or messages.slice()) so the store
owns its copy of messages and ai.error is still cleared.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b39ceaa1-602c-43e5-be33-b837cf4b031e

📥 Commits

Reviewing files that changed from the base of the PR and between aa3832d and fed05ec.

📒 Files selected for processing (7)
  • src/backend/shared/styles/globals.css
  • src/frontend/components/_molecules/menu-bar/menus/help.tsx
  • src/frontend/store/__tests__/ai-slice.test.ts
  • src/frontend/store/slices/ai/index.ts
  • src/frontend/store/slices/ai/slice.ts
  • src/frontend/store/slices/ai/types.ts
  • src/frontend/store/slices/index.ts
💤 Files with no reviewable changes (3)
  • src/frontend/store/slices/ai/index.ts
  • src/frontend/store/slices/index.ts
  • src/frontend/store/slices/ai/types.ts

Comment on lines 288 to 293
replaceMessages: (messages) => {
setState(
produce(({ ai }: AISlice) => {
ai.messages =
messages.length > MAX_CONVERSATION_MESSAGES ? messages.slice(-MAX_CONVERSATION_MESSAGES) : messages
ai.messages = messages
ai.error = null
}),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect replaceMessages call sites with context
rg -n --type=ts --type=tsx -C4 '\breplaceMessages\s*\(' src

# Inspect nearby in-place array mutations that may affect message arrays
rg -n --type=ts --type=tsx -C2 '\.(push|pop|shift|unshift|splice|sort|reverse)\(' src/frontend | rg -n 'message|messages' -i

Repository: Autonomy-Logic/openplc-editor

Length of output: 101


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the exact implementation around the reviewed lines
echo "==== src/frontend/store/slices/ai/slice.ts (around replaceMessages) ===="
nl -ba src/frontend/store/slices/ai/slice.ts | sed -n '250,340p'

# Find all replaceMessages call sites (ts/tsx only)
echo "==== replaceMessages call sites ===="
rg -n --glob '*.ts' --glob '*.tsx' -C4 '\breplaceMessages\s*\(' src/frontend

# Heuristic: find in-place mutations of things named message/messages nearby in frontend
echo "==== Heuristic: message/messages in-place mutations in src/frontend ===="
rg -n --glob '*.ts' --glob '*.tsx' -C2 '\.(push|pop|shift|unshift|splice|sort|reverse)\(' src/frontend \
  | rg -n 'message|messages' -i

Repository: Autonomy-Logic/openplc-editor

Length of output: 207


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== src/frontend/store/slices/ai/slice.ts (around replaceMessages) ===="
cat -n src/frontend/store/slices/ai/slice.ts | sed -n '250,340p'

echo "==== replaceMessages call sites ===="
rg -n --glob '*.ts' --glob '*.tsx' -C4 '\breplaceMessages\s*\(' src/frontend

echo "==== Heuristic: message/messages in-place mutations in src/frontend ===="
rg -n --glob '*.ts' --glob '*.tsx' -C2 '\.(push|pop|shift|unshift|splice|sort|reverse)\(' src/frontend \
  | rg -n 'message|messages' -i

Repository: Autonomy-Logic/openplc-editor

Length of output: 5433


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== replaceMessages usages in whole src (ts/tsx/js/jsx) ===="
rg -n --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' -C2 '\breplaceMessages\b' src

echo "==== aiActions.replaceMessages call sites in whole src ===="
rg -n --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' -C4 'aiActions\.replaceMessages\s*\(' src

echo "==== Heuristic: look for caller mutating a 'messages' variable that is passed to replaceMessages ===="
# After a replaceMessages call, see if the file later mutates arrays named messages/messages*
rg -n --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' -C6 'replaceMessages\s*\(' src \
  | rg -n 'push|pop|splice|sort|reverse|unshift|shift' -i

echo "==== Direct search for messages variable mutations in files that mention replaceMessages ===="
files=$(rg -l --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' '\breplaceMessages\b' src || true)
if [ -n "$files" ]; then
  echo "$files" | head -n 50
  rg -n --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' -C2 '\.(push|pop|shift|unshift|splice|sort|reverse)\(' $files | rg -n 'message|messages' -i
else
  echo "No files found containing replaceMessages"
fi

Repository: Autonomy-Logic/openplc-editor

Length of output: 5942


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== ChatMessage type (ai slice types.ts) ===="
rg -n --glob '*.ts' 'export type ChatMessage|export interface ChatMessage|type ChatMessage|interface ChatMessage' src/frontend/store/slices/ai/types.ts
echo "---- context ----"
sed -n '1,260p' src/frontend/store/slices/ai/types.ts

echo "==== ai/messages mutation patterns in slice.ts (around line ~120-200) ===="
sed -n '100,220p' src/frontend/store/slices/ai/slice.ts

Repository: Autonomy-Logic/openplc-editor

Length of output: 120


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== types.ts size ===="
wc -l src/frontend/store/slices/ai/types.ts

echo "==== Search for ChatMessage in types.ts ===="
rg -n 'ChatMessage' src/frontend/store/slices/ai/types.ts

echo "==== types.ts (relevant sections) ===="
cat -n src/frontend/store/slices/ai/types.ts | sed -n '1,260p'

echo "==== slice.ts size ===="
wc -l src/frontend/store/slices/ai/slice.ts

echo "==== slice.ts around messages mutation ===="
cat -n src/frontend/store/slices/ai/slice.ts | sed -n '110,210p'

Repository: Autonomy-Logic/openplc-editor

Length of output: 13469


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== replaceMessages occurrences in entire repo ===="
rg -n --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' '\breplaceMessages\b' .

echo "==== external direct mutations of ai.messages (set/assign) ===="
rg -n --glob '*.ts' --glob '*.tsx' '\bai\.messages\s*=' src/frontend

echo "==== external in-place array mutations on ai.messages (heuristic) ===="
rg -n --glob '*.ts' --glob '*.tsx' '\bai\.messages\.(push|pop|splice|sort|reverse|shift|unshift)\(' src/frontend

echo "==== mutations of message arrays named messages/message outside slice.ts (heuristic) ===="
rg -n --glob '*.ts' --glob '*.tsx' '\.(push|pop|splice|sort|reverse|shift|unshift)\(' src/frontend \
  | rg -n 'message|messages' -i

echo "==== inspect any bracket access 'replaceMessages' usages (heuristic) ===="
rg -n --glob '*.ts' --glob '*.tsx' '\[['"'"]replaceMessages['"'"]\]' src/frontend

Repository: Autonomy-Logic/openplc-editor

Length of output: 2031


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== replaceMessages( patterns in src/frontend ===="
rg -n --glob '*.ts' --glob '*.tsx' '\breplaceMessages\s*\(' src/frontend

echo "==== .replaceMessages( patterns in src/frontend ===="
rg -n --glob '*.ts' --glob '*.tsx' '\.replaceMessages\s*\(' src/frontend

echo "==== destructured replaceMessages usage patterns ===="
rg -n --glob '*.ts' --glob '*.tsx' '(replaceMessages\s*=\s*|{[^}]*replaceMessages[^}]*})' src/frontend

Repository: Autonomy-Logic/openplc-editor

Length of output: 1054


Avoid storing caller-owned messages array by reference in replaceMessages

src/frontend/store/slices/ai/slice.ts sets ai.messages = messages inside Immer, which aliases the caller’s array; if the caller mutates that array later, ai.messages can drift outside the store update flow. Only unit tests currently call this action, but cloning on write keeps store state ownership intact.

Proposed fix
       replaceMessages: (messages) => {
         setState(
           produce(({ ai }: AISlice) => {
-            ai.messages = messages
+            ai.messages = [...messages]
             ai.error = null
           }),
         )
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
replaceMessages: (messages) => {
setState(
produce(({ ai }: AISlice) => {
ai.messages =
messages.length > MAX_CONVERSATION_MESSAGES ? messages.slice(-MAX_CONVERSATION_MESSAGES) : messages
ai.messages = messages
ai.error = null
}),
replaceMessages: (messages) => {
setState(
produce(({ ai }: AISlice) => {
ai.messages = [...messages]
ai.error = null
}),
)
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/store/slices/ai/slice.ts` around lines 288 - 293,
replaceMessages currently assigns the caller-owned messages array by reference
(ai.messages = messages), which can cause external mutations to affect store
state; inside the replaceMessages reducer (the setState/produce callback for
AISlice) replace that assignment with a cloned array (e.g., a shallow copy like
[...messages] or messages.slice()) so the store owns its copy of messages and
ai.error is still cleared.

@JoaoGSP

JoaoGSP commented May 25, 2026

Copy link
Copy Markdown
Member Author

Note on the failing sync / Shared Surface Sync check

This check fails on pre-existing drift, not on anything this PR introduced. The 7 files this PR touches (help.tsx, the four AI slice files, the slice index, globals.css) are byte-identical between this PR and its mirror openplc-web#412.

Verified by running scripts/compare-surfaces.py locally between the two fix branches:

Surface Pre-existing drifts This PR's files
frontend/ 98 0
middleware/shared/ 10 0
backend/shared/ 22 1 (matches)
__architecture__/ 0 0
Total 130 0 introduced

The drift is between openplc-web/development and openplc-editor/development on files like frontend/assets/icons/project/Library.tsx, middleware/shared/ports/types.ts, backend/shared/debug/modbus-pdu.ts, etc. — accumulated mirror-policy debt that pre-dates this branch.

The web-side equivalent of this check (on PR #412) passed, probably because its checkout fell back to editor's main (where the repos may be in sync) instead of development. Same script, different checkout path.

Reconciling the 130 drifted files is a separate cleanup task; this PR is intentionally scoped to the pre-deploy fixes only. Suggest merging on the 7 byte-identical files being correct and tracking the broader sync separately.

🤖 Generated with Claude Code

@JoaoGSP
JoaoGSP merged commit ecb019f into development May 26, 2026
30 of 32 checks passed
@JoaoGSP
JoaoGSP deleted the fix/pre-deploy-editor-issues branch May 26, 2026 01:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant