Updating Selected Text

I am able to drop in brand new text, but I can’t figure out how to update selected text on an artboard. Any help is greatly appreciated.

Are you referring to a selection within a text node or just a selected text node? If the former, plugins are not allowed to execute while the user has uncommitted text, so processing the selected portion of a text node isn’t possible at the moment.

If the latter, you should just be able to use the selection object that’s passed into your menu handler, like so:

function changeText(selection) {
    selection.items.forEach(node => {
        if (node instanceof Text) {
            node.text = "some other text";
        }
    });
}

Thanks Kerri, I enjoyed your session at Adobe Max. I am receiving the following error when trying to implement that: Plugin made a change outside the current edit context

It looks like the issue is that my text is in a repeat grid. I am able to change the text if it is not in a repeat grid. is there anything I need to do to enable this?

The Repeat Grid APIs might be of use: https://adobexdplatform.com/plugin-docs/reference/scenegraph.html#repeatgrid

1 Like

I’m having the same problem I believe.

I am trying to iterate through nodes (text, images, shapes) looking for text nodes. I then want to change the values. I can identify the text nodes but can’t change the value.

Here is my code:

const {Text, Rectangle, Color} = require("scenegraph");

function myCommand(selection) {
    let node = selection.items[0];

    console.log("The selected node is a: " + node.constructor.name);
      
    node.children.forEach(function (childNode, i) {
        
        if (childNode instanceof Text) {
            console.log("Child " + i + " is a " + childNode.constructor.name);
            console.log("Child " + i + " name is " + childNode.name);
            console.log("Child " + i + " value is " + childNode.text);
            childNode.text = "shawn";
        }
    });
}

module.exports = {
    commands: {
        myPluginCommand: myCommand
    }
};

What errors do you get?

@shawn
have you selected a Text element? if so, you already have that object into your selection.items[0]. You don’t need to iterate its children.
If you set node.text = “shawn” the text will change correctly.

If, otherwise, you have selected an Artboard (containing n elements including texts), then your code works perfectly!

This is the error: Plugin Error: Plugin made a change outside the current edit context

I’ve tested your initial code exactly as it was but I didn’t get any error.
Could you better describe the steps to reproduce the issue?

I select a Group with the text nodes and run my code. It doesn’t change anything and I get the error.

Here’s a more extensive error description:

Plugin Error: Plugin made a change outside the current edit context
at convertPluginErrorToString (plugins/PluginErrorUtil.js:1:198)
at internalFormatPluginError (plugins/PluginErrorUtil.js:1:503)
at internalReportPluginError (plugins/PluginErrorUtil.js:1:610)
at Object.reportPluginError (plugins/PluginErrorUtil.js:1:1015)
at r (plugins/ScenegraphGuard.js:1:310)
at Object.checkChange (plugins/ScenegraphGuard.js:1:729)
at Object.EventEmitter._emitEvent (third_party/events.js:1:1178)
at Object.EventEmitter.emit (third_party/events.js:1:1668)
at Text.Group.notifyChanged (lib/scenegraph/GroupExtension.js:1:879)
at setProperty (lib/scenegraph/TextExtension.js:1:2188)
at Text.set (lib/scenegraph/TextExtension.js:1:2520)
at Text. (lib/scenegraph/TextExtension.js:1:16542)
at Array.forEach ()
at Text.clearRangedStyles (lib/scenegraph/TextExtension.js:1:16515)
at Text. (plugins/ScenegraphWrappers.js:1:21791)
at Text. (plugins/ScenegraphWrappers.js:1:2423)
at /Users/sbaden/Library/Application Support/Adobe/Adobe XD CC/develop/MockData/main.js:57:28
at plugins/WrapperList.js:1:378
at LinkedList.forEach (lib/LinkedList.js:1:1534)
at ReadOnlyList.forEach (lib/ReadOnlyList.js:1:282)
at WrapperList.forEach (plugins/WrapperList.js:1:354)
at myCommand (/Users/sbaden/Library/Application Support/Adobe/Adobe XD CC/develop/MockData/main.js:51:19)
at Object.execScenegraphEdit (plugins/ScenegraphGuard.js:1:2318)
at BaseCommand._invokePluginCommand [as _commandFn] (plugins/PluginLoader.js:1:3139)
at BaseCommand.execute (lib/BaseCommand.js:1:929)
at Commands._execute (lib/Commands.js:1:1935)
at Commands.executeAsGesture (lib/Commands.js:1:2088)

Yes, correct - If I comment out childNode.text = ‘shawn’; It runs without error.

Am I not able to edit objects inside a group? That seems really strange… I can edit objects in a precomp in After Effects and objects in a group in Photoshop.

In this case, you can’t edit objects inside a group from the outside due to the edit context.
In fact, if you remove

childNode.text = "shawn";

all children are listed getting no errors.

You could
• enter the group manually (double click) before running the command
or
• ungroup > apply command > re-group using code

@shawn do you change selection to text before changing text value i mean before

selection.items = [i]; //add this
childNode.text = "shawn";

@PramUkesh I tried what you suggested. Here is my code:

function myCommand(selection) {
    let node = selection.items[0];
    console.log("The selected node is a: " + node.constructor.name);
      
    node.children.forEach(function (childNode, i) {
        
        if (childNode instanceof Text) {
            console.log("Child " + i + " is a: " + childNode.constructor.name);
            console.log("Child " + i + " name is " + childNode.name);
            console.log("Child " + i + " value is " + childNode.text);
            selection.items = [childNode]; //add this
            childNode.text = "shawn";
        }
    });
}

I’m getting a new error: Plugin Error: Cannot select node not in the current edit context: [object Object]

Hi @shawn,

As @PaoloBiagini mentioned above, it looks like you are running into the edit context limitation ( ref: https://adobexdplatform.com/plugin-docs/reference/core/edit-context.html).

You can only make edits to user selection’s current edit context, meaning the selected item and its immediate children. For example, if user selects an artboard which only has texts, your code will work. However, if user selects an artboard which has a group that has texts inside the group, your code will not work due to the edit context limitation. In this case, if user had selected the group directly, the code would have worked.

While this is a big limitation, there are often workarounds. If you have specific use cases in mind, please share them here. I’d happy to help you come up with potential workarounds.

1 Like

@stevekwak,

if user had selected the group directly, the code would have worked

Selecting the group directly while keeping the edit action

childNode.text = "shawn";

we’d get the same edit context error.

It works instead using just the logs part since it doesn’t edit the ‘protected’ nodes.

I’ve tested it with a selecting a group of 2 text elements.

With the edit action – loop stops at the first attempt to make changes:

The selected node is a: Group
Child 0 is a Text
Child 0 name is abc
Child 0 value is abcdefghijk
[Error: Cannot select node not in the current edit context: [object Object]]

Without the edit action – loop completes its iterations:

The selected node is a: Group
Child 0 is a Text
Child 0 name is abc
Child 0 value is abcdefghijk
Child 1 is a Text
Child 1 name is abc
Child 1 value is abcdefghijk

1 Like

@stevekwak @kerrishotts - Thanks for the insight. Right now I’m just trying to get my head wrapped around how to code for XD. I write scripts for AE that allow me to iterate thru 10’s of thousands or animations. I delivered 66K animations this last year.

There doesn’t seem to be much in common - scripting wise - between XD and the rest of the CC suite.

What got me started was trying to recreate the plugin “Data Populator”. It’s similar to what I do in AE on a regular basis and wanted to figure out how to do the same in XD.

What would be the best way to share my use cases? Post screen grabs and code? Here or elsewhere?

**As an aside: I am able to get the repeat grid version of this to work. I’m guessing that is the best course to follow. I just wanted to figure out how to do the same for an Ungrouped Grid. Is there a way to Regroup Grid?

If you’re comfortable sharing use cases here, by all means, please do so! :slight_smile:

As to the edit context – this is something that’s built deep in to XD’s architecture. Even XD itself has to deal with it. It’s definitely not ideal, but changes will require significant rearchitecting to XD itself, and that would be a ways off, if it can be justified. (Use cases help here!)

It’s important to note that you can iterate through all the nodes in the document and read the contents. You just can’t change the data throughout the entire document. How much data you can access depends highly upon the structure of your selection – a flatter design will permit more changes, for example.

One trick you might be able to use is to duplicate an existing selection, flatten it, iterate over the nodes, and then regroup (if you want). This doesn’t affect the existing nodes, but it might work for your purposes? Several plugins do this – WorldReady is a good example. It’s not perfect, because ungrouping can change the visual styling of some elements, but it’s one option.

@kerrishotts Would you suggest something for my case:

  1. User select artboard
  2. Click on plugin button
  3. Plugin makes a copy of artboard
  4. Update text nodes (node.text = ‘new string’) in copied artboard

My current implementation:

const children = root.children.map((el) => el);
const originalArtboard = children.find(({ guid }) => translation.id === guid);
selection.items = [originalArtboard];
duplicate();
const translationArtboard = selection.items[0];
const matchTree = { /* ... some data here ... */ };

for (const { id, characters } of textNodes) {
  const clonedTextNode = findOne(translationArtboard.children, (node) => node.guid === matchTree[id]);
  clonedTextNode.text = characters; // failed here with "Plugin made a change outside the current edit context"
}