Skip to content

Performance fixes#98

Open
eweren wants to merge 6 commits intotolgee:mainfrom
eweren:performance-fixes
Open

Performance fixes#98
eweren wants to merge 6 commits intotolgee:mainfrom
eweren:performance-fixes

Conversation

@eweren
Copy link
Copy Markdown
Collaborator

@eweren eweren commented May 6, 2026

Summary by CodeRabbit

  • New Features

    • Clearing of prefilled keys from settings
    • Screenshot deduplication for frames where multiple nodes share a key
  • Bug Fixes

    • Fixed duplicate screenshot entries in push payloads
    • Preserved connected node keys while clearing unconnected prefilled keys
  • Improvements

    • Faster text-node traversal
    • More reliable key-connection metadata loading and push state handling
  • Tests

    • Added E2E tests covering push deduplication and prefill toggle behavior

eweren added 5 commits May 6, 2026 10:14
- Updated useEffect to refetch selection and connected-nodes data when returning to the Index page, ensuring changes made elsewhere are reflected.
- Modified queryClient.invalidateQueries in useSetNodesDataMutation to mark connected-nodes data as stale without immediate refetch, preventing UI freezes during typing.
- Changed connectNodes and handleConnectOnly functions to use async/await for better handling of asynchronous operations.
- Ensured local Figma node data is updated before navigating away from the Push view to prevent stale data issues.
…eval

- Simplified visibility checks in shouldIncludeNode function to enhance performance.
- Updated findTextNodes to skip hidden subtrees early, reducing unnecessary parent checks.
- Adjusted handleConnect to utilize new key translation fetching logic, improving data handling during connection.
…ndling

- Registered clearPrefilledKeysEndpoint in main.ts to manage prefilled key states.
- Updated usePrefilledKey hook to allow dynamic enabling of queries based on the new parameter.
- Enhanced StringsSection to clear prefilled keys when the prefill toggle is disabled, ensuring data consistency.
- Implemented a test to ensure screenshot entries are deduplicated when multiple nodes share the same key in the Push component.
- Added a regression test to verify no changes are shown when re-opening Push immediately after a successful push.
- Created tests to confirm that prefilled keys are cleared for unconnected nodes when the prefill toggle is disabled, while preserving keys for connected nodes.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Walkthrough

This PR adds an endpoint to clear non-connected prefilled keys, refactors text-node discovery, deduplicates screenshots per (key,namespace) in push payloads, fetches per-key metadata during connect, converts push/connect flows to async mutations, updates settings UI to clear persisted prefilled keys, and adds E2E tests plus updated web mocks.

Changes

Prefilled Key Lifecycle & Connect Flow Redesign

Layer / File(s) Summary
Core Endpoint
src/main/endpoints/clearPrefilledKeys.ts, src/main/main.ts
New endpoint clears prefilled keys for non-connected nodes by traversing pages, reading plugin data, and removing keys where connectivity is absent.
Hook & Visibility Refactor
src/main/utils/nodeTools.ts, src/ui/hooks/usePrefilledKey.ts
findTextNodes refactored to thread visibility and ancestor-hidden context recursively; usePrefilledKey gains enabled parameter to conditionally fetch prefilled keys.
Connect Metadata Flow
src/ui/views/Connect/Connect.tsx
Replaces global translations cache with per-key metadata fetches; handleConnect resolves translation and plural fields before updating node data; search rows pass richer key data.
Push & Query Integration
src/ui/views/Push/Push.tsx, src/ui/hooks/useSetNodesDataMutation.ts, src/ui/views/Index/Index.tsx
connectNodes uses mutateAsync; pushes capture keysPushed. Node-data mutation success now invalidates connected-nodes query with explicit refetch options. Index refetches selection and connected nodes on route change back to index after initial mount.
Settings & Prefill Toggle
src/ui/views/Settings/StringsSection.tsx, src/ui/views/Index/ListItem.tsx
Settings now calls clearPrefilledKeysEndpoint and invalidates queries when prefill is disabled; ListItem forwards prefillKeyFormat into usePrefilledKey.
Screenshot Deduplication
src/tools/getPushChanges.ts
Introduces per-screenshot seen-key tracking and a getKeyScreenshots helper so each (key,namespace) pair appears at most once per screenshot in aggregated payloads.
Test Coverage & Mocks
cypress/e2e/push.cy.ts, cypress/e2e/settings.cy.ts, src/web/main.ts
Adds E2E tests for screenshot deduplication and prefill toggle behavior; web mocks updated for dynamic screenshots and clear-prefilled-keys behavior.
Project Settings Cleanup
src/ui/views/Settings/ProjectSettings.tsx
Simplifies branchNames memoization and initial branch selection expression.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as Connect UI
    participant API as API Layer
    participant DB as Node Data
    participant Mutation as setNodesDataMutation

    User->>UI: Select key to connect
    UI->>API: Fetch per-key translation metadata
    API-->>UI: Return isPlural, pluralParamValue, translation
    UI->>UI: Resolve translation (metadata > provided > default)
    User->>UI: Confirm connect
    UI->>Mutation: Mutate with translation, isPlural, pluralParamValue
    Mutation->>DB: Update node plugin data
    DB-->>Mutation: Success
    Mutation->>API: Invalidate getConnectedNodes query
    API-->>UI: Query refreshed
    UI->>UI: Navigate to Index
Loading
sequenceDiagram
    participant User
    participant Settings as Settings UI
    participant QueryClient as React Query
    participant Endpoint as clearPrefilledKeysEndpoint
    participant PluginData as Plugin Data Store

    User->>Settings: Toggle prefill OFF
    Settings->>Endpoint: Call clearPrefilledKeysEndpoint
    Endpoint->>PluginData: Walk all nodes in document
    PluginData->>Endpoint: Read TOLGEE_NODE_INFO for each node
    Endpoint->>Endpoint: Filter non-connected nodes
    Endpoint->>PluginData: Clear key field for unconnected nodes
    Endpoint-->>Settings: Complete
    Settings->>QueryClient: Invalidate getConnectedNodes & selection
    QueryClient-->>Settings: Queries refetched
    Settings->>UI: UI reflects cleared prefilled keys
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tolgee/figma-plugin#94: Directly related through shared modifications to screenshot aggregation logic in getPushChanges.
  • tolgee/figma-plugin#93: Related through shared screenshot grouping/lookup changes and push behavior.
  • tolgee/figma-plugin#72: Overlaps on prefilled-key lifecycle, hook changes, and E2E tests for push/settings.

Suggested reviewers

  • JanCizmar
  • stepan662

🐰 A toggle flips, keys clear like dew,
Metadata flows, one translation true,
Screenshots merge where nodes align,
Async awaits the stars to shine,
Connected stays, unbound takes flight!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Performance fixes' is too vague and generic—it doesn't clearly identify what specific performance improvements are being made or which components are affected. Replace the title with a more specific description of the main performance improvement, such as 'Optimize text node traversal with visibility caching' or 'Add screenshot deduplication to reduce push payload size'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ui/hooks/usePrefilledKey.ts (1)

15-27: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent stale prefill values from persisting after prefill is disabled.

In react-query v3, enabled: false stops fetching but still exposes cached data. When users disable prefill, this hook continues returning the previously computed key, allowing ListItem to re-seed its keyName state (line 52–54) and immediately persist that stale value back to plugin data via mutation (lines 71–74), defeating the "disable and clear" flow.

Fix by returning undefined for key when the query is disabled:

Suggested fix
 export function usePrefilledKey(
   nodeId: string,
   keyFormat: string,
   variableCasing: TolgeeConfig["variableCasing"],
   enabled: boolean = true,
   nodeKey?: string,
 ) {
+  const queryEnabled = enabled && !!nodeId && !!keyFormat && !nodeKey;
   const result = useQuery<string, undefined, string>(
     [preformatKeyEndpoint.name, nodeId, keyFormat],
     async () => {
       if (!nodeId || !keyFormat) return "";
       return preformatKeyEndpoint.call({ keyFormat, nodeId, variableCasing });
     },
     {
-      enabled: enabled && !!nodeId && !!keyFormat && !nodeKey,
+      enabled: queryEnabled,
       structuralSharing: false,
     },
   );
 
-  return { key: result.data, isLoading: result.isLoading };
+  return {
+    key: queryEnabled ? result.data : undefined,
+    isLoading: queryEnabled && result.isLoading,
+  };
 }
🤖 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/ui/hooks/usePrefilledKey.ts` around lines 15 - 27, The hook
usePrefilledKey returns cached result.data even when the react-query is
disabled; change the return so that key is undefined whenever prefill is
disabled (i.e., when enabled is false or the query conditions are unmet) instead
of returning result.data. Locate usePrefilledKey and the query using
preformatKeyEndpoint.call(...) and update the final return to only expose
result.data when the query is actually enabled/successful (e.g., check enabled
&& result.isSuccess or similar) otherwise return key: undefined so stale prefill
values cannot persist.
🧹 Nitpick comments (1)
src/ui/views/Settings/StringsSection.tsx (1)

169-169: 💤 Low value

Type the event parameter consistently with the other handlers.

The other change handlers in this file use TargetedEvent<HTMLInputElement, Event>; aligning here keeps the surface uniform and avoids the any escape hatch.

♻️ Proposed change
-  const handlePrefillChange = async (e: any) => {
-    const checked = e.currentTarget.checked;
+  const handlePrefillChange = async (
+    e: TargetedEvent<HTMLInputElement, Event>,
+  ) => {
+    const checked = e.currentTarget.checked;
🤖 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/ui/views/Settings/StringsSection.tsx` at line 169, The event parameter of
the handler handlePrefillChange is typed as any; update its signature to use the
same type as other handlers (TargetedEvent<HTMLInputElement, Event>) to make the
typing consistent and avoid the any escape hatch, and adjust any internal refs
to e.target.value accordingly so the function compiles with the stricter type.
🤖 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/ui/views/Connect/Connect.tsx`:
- Around line 162-167: The multi-line call to handleConnect (the invocation with
key.id, key.name, key.namespace, key.translation in Connect.tsx) is failing
Prettier/ESLint; reformat this call to match the project's Prettier rules
(collapse the arguments onto a single line or otherwise run the project's
formatter) so the prettier/prettier lint error is resolved—run the project's
formatter or IDE formatting on the handleConnect(...) call to produce the
expected single-line argument list.

In `@src/ui/views/Index/Index.tsx`:
- Around line 93-103: The useEffect currently triggers
selectionLoadable.refetch() and allNodes.refetch() on the initial mount when
route[0] === "index", causing a double-expensive fetch; fix by tracking initial
mount with a ref (import useRef from "preact/hooks") and only call
selectionLoadable.refetch() and allNodes.refetch() inside the useEffect when the
ref indicates this is not the first render (i.e., set a mounted ref after the
first render and skip the refetch when mountedRef.current is false).

---

Outside diff comments:
In `@src/ui/hooks/usePrefilledKey.ts`:
- Around line 15-27: The hook usePrefilledKey returns cached result.data even
when the react-query is disabled; change the return so that key is undefined
whenever prefill is disabled (i.e., when enabled is false or the query
conditions are unmet) instead of returning result.data. Locate usePrefilledKey
and the query using preformatKeyEndpoint.call(...) and update the final return
to only expose result.data when the query is actually enabled/successful (e.g.,
check enabled && result.isSuccess or similar) otherwise return key: undefined so
stale prefill values cannot persist.

---

Nitpick comments:
In `@src/ui/views/Settings/StringsSection.tsx`:
- Line 169: The event parameter of the handler handlePrefillChange is typed as
any; update its signature to use the same type as other handlers
(TargetedEvent<HTMLInputElement, Event>) to make the typing consistent and avoid
the any escape hatch, and adjust any internal refs to e.target.value accordingly
so the function compiles with the stricter type.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c179c84a-117e-4c26-a23f-12e4d1828bae

📥 Commits

Reviewing files that changed from the base of the PR and between 796a241 and d502558.

📒 Files selected for processing (14)
  • cypress/e2e/push.cy.ts
  • cypress/e2e/settings.cy.ts
  • src/main/endpoints/clearPrefilledKeys.ts
  • src/main/main.ts
  • src/main/utils/nodeTools.ts
  • src/tools/getPushChanges.ts
  • src/ui/hooks/usePrefilledKey.ts
  • src/ui/hooks/useSetNodesDataMutation.ts
  • src/ui/views/Connect/Connect.tsx
  • src/ui/views/Index/Index.tsx
  • src/ui/views/Index/ListItem.tsx
  • src/ui/views/Push/Push.tsx
  • src/ui/views/Settings/StringsSection.tsx
  • src/web/main.ts

Comment thread src/ui/views/Connect/Connect.tsx Outdated
Comment thread src/ui/views/Index/Index.tsx
- Removed unnecessary line breaks in Cypress tests for better readability.
- Simplified the handleConnect function call in the Connect component.
- Optimized the useMemo hook in ProjectSettings for branch names.
- Added a mountedRef to prevent unnecessary effect execution in the Index component.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/ui/views/Connect/Connect.tsx`:
- Line 97: The assignment to the translation property currently uses a logical
OR which will replace intentionally empty strings; change the fallback to a
nullish coalescing so translation: resolvedTranslation ?? node.characters
(replace the OR usage where translation: resolvedTranslation || node.characters
is set) to preserve empty-string translations while still falling back when
resolvedTranslation is null or undefined.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 17e23db3-04e3-4b38-8548-77682e0f8269

📥 Commits

Reviewing files that changed from the base of the PR and between d502558 and f518dd1.

📒 Files selected for processing (4)
  • cypress/e2e/settings.cy.ts
  • src/ui/views/Connect/Connect.tsx
  • src/ui/views/Index/Index.tsx
  • src/ui/views/Settings/ProjectSettings.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • cypress/e2e/settings.cy.ts
  • src/ui/views/Index/Index.tsx

{
...node,
translation: translation || node.characters,
translation: resolvedTranslation || node.characters,
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

Use nullish fallback for translation to avoid overwriting valid empty strings.

At Line 97, resolvedTranslation || node.characters will replace "" with node.characters. If an empty translation is intentional, this silently changes user data. Use ?? instead.

Suggested fix
-          translation: resolvedTranslation || node.characters,
+          translation: resolvedTranslation ?? node.characters,
📝 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
translation: resolvedTranslation || node.characters,
translation: resolvedTranslation ?? node.characters,
🤖 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/ui/views/Connect/Connect.tsx` at line 97, The assignment to the
translation property currently uses a logical OR which will replace
intentionally empty strings; change the fallback to a nullish coalescing so
translation: resolvedTranslation ?? node.characters (replace the OR usage where
translation: resolvedTranslation || node.characters is set) to preserve
empty-string translations while still falling back when resolvedTranslation is
null or undefined.

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