Skip to content

fix incomplete theme application issue#3756

Open
akshay-gupta7 wants to merge 3 commits intomainfrom
akshay/fix-incomplete-theme-application
Open

fix incomplete theme application issue#3756
akshay-gupta7 wants to merge 3 commits intomainfrom
akshay/fix-incomplete-theme-application

Conversation

@akshay-gupta7
Copy link
Contributor

@akshay-gupta7 akshay-gupta7 commented Jan 21, 2026

Why does this PR exist?

Closes #3707

What does this pull request do?

Testing this change

Additional Notes (if any)


Note

Improves node discovery and token application to address incomplete theme updates.

  • Adds SECTION to ValidNodeTypes
  • Introduces "smart discovery" in findAll to include nodes without local plugin data by collecting nodes with token data plus INSTANCE nodes and their children; deduplicates via a Set
  • NodeManager.findBaseNodesWithData now only returns nodes that actually have token data; update enables this mode (nodesWithoutPluginData: true)
  • SharedDataHandler.getAll now reads only known Properties keys and safely parses values
  • updateNodes awaits async setValuesOnNode to ensure complete application

Written by Cursor Bugbot for commit 5390708. Configure here.

@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

⚠️ No Changeset found

Latest commit: 5390708

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Jan 21, 2026

⤵️ 📦 ✨ The artifact was successfully created! Want to test it? Download it here 👀 🎁

@akshay-gupta7 akshay-gupta7 changed the title fix incomplete theme application isue fix incomplete theme application issue Jan 21, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 21, 2026

Commit SHA:44259dbe0a52b5458b7b4ce3e5706f07b17d7625

Test coverage results 🧪

Code coverage diff between base branch:main and head branch: akshay/fix-incomplete-theme-application 
Status File % Stmts % Branch % Funcs % Lines
🔴 total 64.54 (-0.03) 56.96 (0) 61.22 (-0.07) 64.87 (-0.04)
🔴 packages/tokens-studio-for-figma/src/plugin/NodeManager.ts 56.09 (-2.88) 25 (-5) 62.5 (0) 56.41 (-3.04)
🟢 packages/tokens-studio-for-figma/src/plugin/SharedDataHandler.ts 41.17 (6.17) 12.5 (2.5) 66.66 (0) 41.17 (6.17)

@github-actions
Copy link
Contributor

github-actions bot commented Jan 21, 2026

Commit SHA:44259dbe0a52b5458b7b4ce3e5706f07b17d7625
Current PR reduces the test coverage percentage by 1 for some tests

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes issue #3707 where applying themes to selected frames fails to update all component layers in a single pass, requiring users to click "Apply to Selection" multiple times.

Changes:

  • Enhanced node discovery algorithm to include component instance children that inherit token data from parent components
  • Made setValuesOnNode properly awaited to ensure synchronous completion of node updates
  • Refactored SharedDataHandler.getAll() to check all known properties instead of only existing keys
  • Added filtering in NodeManager to exclude nodes without any token data
  • Added 'SECTION' to valid node types for token application

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/tokens-studio-for-figma/src/utils/findAll.ts Implements "Smart Discovery" mode that finds both nodes with local token data and instance children that may inherit token data, using Set for deduplication
packages/tokens-studio-for-figma/src/plugin/updateNodes.ts Adds await keyword to properly wait for setValuesOnNode completion
packages/tokens-studio-for-figma/src/plugin/asyncMessageHandlers/update.ts Enables Smart Discovery mode by passing nodesWithoutPluginData: true parameter
packages/tokens-studio-for-figma/src/plugin/SharedDataHandler.ts Changes getAll() to iterate through all Properties enum values instead of only existing node keys
packages/tokens-studio-for-figma/src/plugin/NodeManager.ts Adds filtering to exclude nodes with empty token objects from processing
packages/tokens-studio-for-figma/src/constants/ValidNodeTypes.ts Adds 'SECTION' node type to support section nodes

Comment on lines +25 to 37
Object.values(Properties).forEach((key) => {
const value = this.get(node, key);
if (value) {
try {
// make sure we catch JSON parse errors in case invalid property keys are set and found
// we're storing `none` as a string without quotes
const parsedValue = value === 'none' ? 'none' : JSON.parse(value);
result[key] = parsedValue;
} catch (err) {
console.warn(err);
}
}
return null;
});
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

This refactoring changes the iteration logic from checking only the keys present on the node to checking all possible Properties enum values. While this ensures all known properties are checked (which may help with the incomplete theme application issue), it's less efficient as it makes up to 61 getSharedPluginData calls per node instead of only checking existing keys. Consider documenting why this change was necessary for fixing the theme application issue, as it's a significant performance trade-off.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +37
withLocalData.forEach(n => allNodesSet.add(n));

// 2. Find all instances (their children have inherited token data)
const instances = node.findAllWithCriteria({
types: ['INSTANCE'],
});
instances.forEach(instance => {
allNodesSet.add(instance);
// For each instance, we must also check all its children
instance.findAllWithCriteria({
types: ValidNodeTypes,
}).forEach(n => allNodesSet.add(n));
});


} else {
// Standard Discovery: Only nodes with local token data
node.findAllWithCriteria({
types: ValidNodeTypes,
...pluginDataOptions,
}),
);
sharedPluginData: { namespace: 'tokens' },
}).forEach(n => allNodesSet.add(n));
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

Inconsistent arrow function parameter style. The codebase convention uses parentheses around single parameters (e.g., '(n) =>' not 'n =>'). Apply this consistently on lines 17, 23, 28, and 37 for better code consistency.

Copilot uses AI. Check for mistakes.
@@ -1,23 +1,44 @@
import { ValidNodeTypes } from '@/constants/ValidNodeTypes';

export function findAll(nodes: readonly BaseNode[], includeSelf = false, nodesWithoutPluginData = false): BaseNode[] {
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

The parameter name 'nodesWithoutPluginData' is misleading and creates confusion. When set to true, the function actually finds MORE nodes (including nodes without plugin data), not fewer. The name suggests it would exclude nodes with plugin data, but it actually enables a broader "Smart Discovery" mode that includes both nodes with plugin data AND instance children that may not have local plugin data. Consider renaming to something like 'includeInstanceChildren' or 'enableSmartDiscovery' to better reflect its actual behavior.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 44
import { ValidNodeTypes } from '@/constants/ValidNodeTypes';

export function findAll(nodes: readonly BaseNode[], includeSelf = false, nodesWithoutPluginData = false): BaseNode[] {
let allNodes = includeSelf ? [...nodes] : [];
const pluginDataOptions = nodesWithoutPluginData
? {}
: {
sharedPluginData: {
namespace: 'tokens',
},
};


const allNodesSet = new Set<BaseNode>(includeSelf ? nodes : []);

nodes.forEach((node) => {
if ('children' in node) {
allNodes = allNodes.concat(
if (nodesWithoutPluginData) {
// Smart Discovery:
// 1. Find all nodes with local token data (Figma optimized search)
const withLocalData = node.findAllWithCriteria({
types: ValidNodeTypes,
sharedPluginData: { namespace: 'tokens' },
});
withLocalData.forEach(n => allNodesSet.add(n));

// 2. Find all instances (their children have inherited token data)
const instances = node.findAllWithCriteria({
types: ['INSTANCE'],
});
instances.forEach(instance => {
allNodesSet.add(instance);
// For each instance, we must also check all its children
instance.findAllWithCriteria({
types: ValidNodeTypes,
}).forEach(n => allNodesSet.add(n));
});


} else {
// Standard Discovery: Only nodes with local token data
node.findAllWithCriteria({
types: ValidNodeTypes,
...pluginDataOptions,
}),
);
sharedPluginData: { namespace: 'tokens' },
}).forEach(n => allNodesSet.add(n));
}
}
});

const allNodes = Array.from(allNodesSet);
return allNodes;
}
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

Missing mandatory changeset. According to the repository guidelines, every PR must include a changeset created by running 'yarn changeset'. The changeset should be a 'patch' version with a clear, user-facing description of the changes. This is required for proper versioning and release notes.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +19 to +25
// 2. Find all instances (their children have inherited token data)
const instances = node.findAllWithCriteria({
types: ['INSTANCE'],
});
instances.forEach(instance => {
allNodesSet.add(instance);
// For each instance, we must also check all its children
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

The instance children search (lines 26-28) finds ALL children of ValidNodeTypes without filtering for plugin data. While this is necessary to capture instance children that inherit token data from their parent component (and thus have no local plugin data), it creates performance overhead by collecting many nodes that have no token data at all. These are later filtered out in NodeManager.ts (lines 83-89), but it would be more efficient to avoid collecting them in the first place. Consider adding a comment explaining why this broad search is necessary for the fix, as it's not immediately obvious that instance children need to be included even without local plugin data.

Suggested change
// 2. Find all instances (their children have inherited token data)
const instances = node.findAllWithCriteria({
types: ['INSTANCE'],
});
instances.forEach(instance => {
allNodesSet.add(instance);
// For each instance, we must also check all its children
// 2. Find all instances (their children may have inherited token data)
const instances = node.findAllWithCriteria({
types: ['INSTANCE'],
});
instances.forEach(instance => {
allNodesSet.add(instance);
// IMPORTANT:
// We intentionally search *all* children of instances, without filtering
// by shared plugin data here. Children can inherit token data from their
// parent component and therefore may not have any local plugin data set.
// These nodes are later filtered in NodeManager based on actual token data.
// Narrowing this search (e.g. by sharedPluginData) would cause us to miss
// inherited tokens on instance children.

Copilot uses AI. Check for mistakes.
@six7
Copy link
Collaborator

six7 commented Jan 22, 2026

@cursor review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

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.

Incomplete Theme Application Requires Multiple "Apply to Selection" Clicks

3 participants