BoxIDs, Selections and NotebookProgramming, in the FrontEnd

A technical walkthrough of programmatically editing Wolfram Mathematica notebooks from the front end using box IDs, cell tags, and UUIDs. The speaker shows how to read, find, replace, and inject content without disrupting the user's selection, and identifies which box types are stable targets for graphics, input fields, and widgets.
Chapters
Welcome to this presentation on box ID selections and notebook programming in the front end. I'm just going to briefly introduce this and then launch straight into it in a hands on way that you can actually see me evaluate and play around with the various expressions and control structures to demonstrate the functionality of notebook programming and box IDs. So in doing that, we'll go through some introduction to the normal way we used to do notebook programming of selection, moving and stuff around that will talk about selections, moving on to the underlying cell objects and their associated UUIDs, cell IDs, cell tags, and then finally moving on to this really neat thing that box IDs and being able to find them and change them and move around and show then some advanced examples where the functionality is actually used in the front end. And so this is actual a topic that's really needed to do advanced programming and interface design in the front end. Right, let's just launch into it. Okay, let's actually do a concrete example of this, of this notebook programming. So let's create a new notebook here and we're going to modify its contents programmatically. So we put in here my fun project, and then put in here this is my fun, fun notebook project, put in an equation, E equals MC squared, for instance, and we're good. Okay, so now we'd actually like to get the contents of that notebook and then modify them. So how do we get the contents? Well, we can actually take a look at the notebooks we've got in our system currently at the moment. This of course is the talk notebook that I'm demonstrating this from. These are the other, this is the list of the complete notebooks, and this is a reference to this particular notebook here that we've just created. So let's actually get that reference. And now let's get notebook, get the contents of that notebook. Okay, and here if I take a look at this input form, sorry, input form, we can see that here we can see the underlying representation of this equals MC squared. And you can sort of see there's a row box with a SuperScript box of C2. And so we can see that the superscript box C squared is actually represented by this. And of course if I turn around and take a look at X squared, we can actually see the superscripts are strings in this representation. These boxes of course get sent to the kernel, which makes an expression from it, kernel, does replacements on it, and then sends it back to the front end. But the way the front end represents this internally is these strings. So let's actually go and replace our superscripts here by our squared by cubes and we get a new notebook expression. And we can put that back to the front end notebook put of that to the front end and we see we get a new notebook. And of course here we've got MC Cubed instead of MC2 and we've got a different notebook that that represents what happens if we'd like to actually put this back not to a new notebook, but the actual same notebook and replace the contents completely of the current notebook. And so even if we have a selection and we actually do this replacement like so we see we've changed the superscripts to cubes instead of squared and we see that we've actually completely splattered over the contents of this Untitled 2 notebook. Our selection got lost because we've got completely new contents. And so that's sort of typical of what we could do. A little bit more sophisticated example if we wanted to would be maybe replace, let's say the fun with cool and so we could get the contents. This is much more in line with what we do here. We get the contents of the notebook and we do some replacement. We're going to look for some strings subject to the condition that that string is string contains. Contains fun. And we should ignore the case here. And what will we change that to? Well, we're going to go and replace an A. All fun goes to cool. And we also want to ignore the case here. Ignore the case. Okay, so that gives us another set of contents. And we can see here the fun here is being replaced here. It's being replaced there. Looking good. We could actually turn around and just do notebook put here that into the actual notebook as follows. We could make that contents. We've done, we've assigned that and it means as well, whatever changes that are actually in this notebook, we put another title in. Once we do this replacement with the old contents which doesn't have this change in will replace all of the contents with the notebook with this new changed change transmogrified notebook expression. Okay, so we do this and as before, all our selections will get blown away and all the contents of that notebook Untitled 2 will get completely replaced. And so that is an example of doing a complete replacement of the contents. And so this is a valid form of doing notebook programming is taking all of the contents, making some changes to it and then spitting them all out again. However, this is. It's a bit old school and in fact there's parallels in web technology. Ajax came in around about 2000, 2005, in terms of web technologies and HTML and those kinds of things. And so they made great advances in actually having more refined targeting. Instead of replacing the whole web page, or in our case replacing the whole notebook, we can replace just parts of it in sections of it. So that came in. And then we've got virtual doms and stuff, REACT frameworks and Sveltekit and some of the other new modern web frameworks out there. We have sort of similar things quite in parallel, developed completely independently. But that's what we're about to see now, refined selection replacements. So let's take a look at that. Okay, let's see if we can target this more in a more refined way. So what we want to do is we want to change this SuperScript here to A3. How do we do this? Well, we can use selection move. And so selection move takes an object, a target, and that's the notebook we're targeting. It takes a unit by which we're moving. So we're going to move to four and it takes a direction and then a unit. And so we're going to move before the notebook. And so that will move the selection, as you'll see, up to the top. And from there we could turn around and we could move after that. So we're going to move to the next cell. And so you'll see it now we'll select my fun project. We can give it a count. So to move down to this third cell, we could change there. And so it's moving into the. Selecting the third cell. From there we can actually move into the cell move. Next we're going to go cell contents. And so that gives us no, after, before cell contents of this notebook. Right? And if we're here, we could turn around and we could go notebook, right. For instance, into the envy somewhere like, hello. And you see, we've been able to insert where the cursor is some change programmatically change the contents of the notebook. Okay, so what we need to do is we now need to select the superscript so we can move inside there. We can move to the next cell, we can move the next character territor. And you see we've selected the E. We can move by the 1. Sorry, 2 as you see, 3 as we were selecting successive characters. So let's go and select. Let's say six gives us the space. No, when you need one more, seven is the C, eight is the whole expression. Nine gives us the superscript. And now we could turn around and just change that SuperScript by writing 2 to it, writing 3 to it, changing it cubed. Okay, so this is a fairly long winded way of actually going through the notebook and changing that superscript to a three as we see here. Well, of course this has got some deficiencies. If we have another cell. Hello. And we try and run this again, we're going to get the wrong response, as we'd expect. So we need now a better way to probably target this selection into a particular cell and we need to be able to name that. Okay. And that's what we're going to do here. Okay, so let's go back and let's start by cleaning this up to get to where we are. Okay. So the next thing we want to be able to do is somewhere, wherever the selection is in the notebook, we want to be able to select this cell, this equation cell. So we can move in and change the superscript. So it turns out that Mathematica has, or the front end has this functionality to get all of the cells in a notebook. And these are cell references. Just before we had like back here when we're doing notebook programming, we have a notebook object here, we have a cell object. And so the cell object, if we grab that one there and we make this, the equation reference equals this particular cell object. And we can now selection, move to the equation ref. And we're going to move before the cell contents. And now this will move the cell contents to just before as we see. And now we could just go and grab this. Now that we know how to handle that we're in the cell, we could go here, move that, and now wherever we are in the notebook, even if we're here, say, and we have another cell here, hello, we can just run this bit of code and it'll actually move to that particular cell referenced by this uuid. So how do we know what this UUID is and where are these UUIDs? Well, if we take a look at this reference, this equation reference, and we take a look at its full form, we'll see that it's got this uuid, that's the sort of unique reference. And this is sort of an accession instance reference. And so we can take a look at that, save that notebook as on the desktop, my fun project, for instance, and then go here, take a look at my fun project on the desktop of netbbedit for instance. And you'll see it has where's here? This is the first third cell right down here. And we see it's got this uuid, this expression uuid dda, blah blah blah, the same. And these things will match right here. And so when we write out the notebook onto a file system, we can actually see these UUIDs. But if we go here and do the command shift D, we don't actually see these UUIDs present. So they're present, they're there, but they're not actually showing. And they are persistent when we write the notebook out, but they're not completely persistent in the sense that if I copy that and place it there and then try and actually run my code, it will have regenerated the UUIDs in a different way. So if I try and run this particular bit of code, it's, it's not doing will just move to the next character and type the three depending on where the selection is. So it never actually did the selection move to where we want it. Even if we put this back into the same position it was and we run this code, it's not going to work. So the UUIDs have been regenerated. When we copy and paste to some degree, some degree, they're a little bit stable, but they're quite unstable. So that's cell UUIDs. And then we can move on to the next topic, which is a little bit more stable. And that's cell IDs. Okay, let's talk about cell IDs. So we can go here. We've got our notebook and what are cell IDs. Well, Mathematica can turn around and add an auto generated cell ID to each new cell. So we're going to turn these on for the selected notebook. And now if we enter a cell, a new cell, create something, we see it's got a cell id, both input and the output will have. Our existing cells don't have a cell id, but we can regenerate them. We can generate them by shift return, which gives us a new output cell. And the input cell we could just simply cut and then paste again and we get a cell id. This one, this one right here. And so we could take that cell and move it to a different place and we'll see it actually still has the same cell id. Great. So now we need to actually find by cell ID. And the cell IDs are a bit more persistent. They're written out and they exist when we're, when they maintain, they persist when we copy them from one place to another, unlike the UUIDs. Okay, but how do we find by them? Well, we can actually use the cells. We can find all cells in this notebook with Cell id, the given cell ID that we've got. And one of those is the one that was actually just referenced. And that gives us the cell object. And we could get the first part of that. And that gives us the cell object here. We could turn around and say that's ref equation or equation ref. Equation ref equals that thing. And now that we've got an equation ref, we can just use the exact same code that we've got from before, go here, paste this in, and we can now move and it will change this particular superscript, which is great. And we can put that in a different place just to show that it's working and we can evaluate all of that. And it works, it changes it. However, when we copy it, we saw that the UUID underneath changes. And this equation ref is basically, it's a cell object and its cell object references the uuid. So this is now referencing a stale uuid. So this thing, it no longer finds that equation ref. So we could turn around and we could actually make it find the equation ref each time and execute that. And this will now work if we go here, change, get rid of that. This will now work wherever it is in the notebook, which is great. And of course equation ref is changing each time we see that 3, 4, 9, 8, we can almost. Yep, we can see it change each time. And so that's working as we want. The other solution we could do is turn around and use notebook find. And so we could notebook find and we're going to find in the nb, what are we going to find? We're going to find this particular cell id and we're going to select the whole cell when we find it. And it's where the element is, the cell id. Right. And so this will now in this notebook, it'll now select the cell bracket of the one we want, which is good. And now we just turn around and use the rest of the stuff that we've got from before here, here, and we're selecting in the notebook. Right. And that will now just directly go and change the cell wherever it is. And we don't have to go through cells. Okay. And why is it important? Well, we're just about to use it for cell tags. Okay. Okay. To work with cell tags, the first thing we're going to do is turn off our cell IDs. So if we go to the notebook and we're no longer going to have cell IDs, and we'll regenerate this particular cell en mc squared, get rid of the old one, do that return and we should no longer have any cell IDs. No, no, no cell IDs. So let's turn around now and add a cell tag to this. So cell tags goes to. What are we going to put in here? Maybe equation ref. Okay. And we could turn around and show cell tags for the selected notebook. Right? And so now we can see it's got this equationref tag. We could use the cell tags, add, remove cell tags. And if we wanted to, we could add Billy as a particular tag here and you can sort of see Billy, but we don't want Billy as a tag, so we're going to remove him. Okay, now that we've got a cell tag, we can reference it in much the same way as we did before. So we'll grab all of that, go here and insert that. And we don't want to look for cell tags. Which cell tag do we want to look for? We want to look for equation ref. We'll give a it to us. And this should now with that equation ref, turn around and we can replace the superscript just as much as we did before. And likewise, we could turn around and use the notebook find here. And what are we going to find? We're going to find the equation ref and it's going to be the cell tags that we're looking for. Right, like that. And so Change that to 2. We can now and this will now work without going through the cells function. And of course, since cell tags are more stable, we can have more than one cell with the same cell tag. Which one will actually be targeted? Well, it targets it. If we're going for, you know, next, for instance, we go next here, it'll get the next one from the top, it'll get the first one. If we wanted to actually replace the second one, we'd have to find next to get the first cell with that cell tag of equation ref and then find the next one with it. And so that'll change the second one. Okay, now with that we can move on to box IDs. Okay, box references. We're here. So the first thing we're going to do is remove the current cell tags in this particular notebook. And so we've now got a clean notebook. Okay. So box IDs, unlike UUIDs, which are just generated everywhere, and cell IDs, which if we ask for them, are generated on everything we have to add. And like cell tags, we have to add them where we actually want them to be. And so we are going to add box ID on this particular on the superscript box. And we're going to just evaluate this ease of use. I'll explain that in a second. I'll go into that in details. And so given this, how do we create a reference to that box id? Well, that's a box reference. It's going to be in a given notebook and it has the name soup. Okay, that looks good. So now, given that box reference, we could go box reference, find the reference, and that will find the particular superscript box in the notebook. Just with that simple reference, that was easy compared to what we've done before. If we have a second box id, box ID of the same name, because these things might be copied and pasted, then box reference find will find the next one in the notebook. And of course it loops around from the end, it will go to the beginning. We can change the search direction if we wanted to go backward. And so if we start at the end, this will find the last one and we move the selection up, it'll find that first one. We could change the search start if we wanted to, start from beginning if we wanted to. And so then of course this will just start from the beginning, finds it, and no matter if the selection is further on, it'll still find the starting reference. Okay, so that's box reference find. We can, with given box references, we can ask other questions like, does that box reference actually exist? Does that box reference exist to that box id? And it does. If we change it to something that isn't in the notebook, it doesn't exist. So that's acting as we would expect. We can do box reference, read the current reference, and that will give us the expression there. It's a superscript box with base C and script 3. Superscript 3. And so finally, now the previous slides, we've been trying to actually get into the superscript there. We'll be trying to select the superscript. So how do we do that? Given we targeted the box ID on the superscript box as a whole, we wanted to get to the script. So we can ask for the box offset to the box child here, which child will try the first child there. And that gives us the base, the second child gives us the script. There we go, the script. And so finally, now that we've got the script, we could turn around and we could do box reference, replace of the current reference with something different, let's say a two. And there we go, we've changed the script. And most importantly, this doesn't actually change the selection. And so that's quite powerful. As we're changing the script here, it won't interfere with the current selection and the current state of the user interface. So this is a very powerful technique, unlike selection moving around and it'll blow away the selection or indeed replacing the whole content. This is very targeted, very refined and it doesn't mess with the current selection. Okay, so some of the other things that's to do with box references, once we've got them, once we've got box IDs. But how do we get these box IDs. Well, we can actually ask in the current notebook what are the current box IDs and we see it's soup. We could turn around and add a box ID to the current selection. We're to going, going to add, you know, FUD for instance. And so we could see that indeed this box has soup and FUD as its current box IDs. We could set the box IDs, set the box IDs on the notebook to let's say Moo. And so we now see that the box ID is now Moo. And finally we could go and we could remove box IDs on the current selection and this will now no longer have any box IDs. Okay. And so then we just go back, set this to soup and we're back Box reference find it's the second one as before. Okay, so that's box reference that's getting, setting, adding and removing box references. And one of the other things we might want to do sometimes is, is I'd like to talk about anonymous box IDs given something else like this. Let's say we want to add a box ID to there, but we don't actually want to name it and we don't want it to be persistent. Well you can do add box IDs of NB right there and it'll add this selection based. And this is a temporary selection. Box ID goes to the selection of this, it's computed to hash, it's a unique number. And so now with that we could assign that to ref 2 equals that particular box. And so we could box reference find that particular second ref and it will find that anonymous box ID. And so these box IDs are not the anonymous ones are not saved out when we go and save the notebook. Okay. Okay. To examine this in a bit more depth, let's look at this ease of use. So what I did is I actually created wrappers. Box reference example exists because the real functionality of Voxids is actually contained in front end packets. So we need to Call through using something like MathLink call frontend with a particular packet. So I just created these various wrappers so you could sort of see without the context and other accoutrements of MathLink calling and packets that you could sort of see what was going on. In the same way, these various symbols, box reference, start, stop, the offset, child, parent, siblings, and so on, actually exist in the feback context, not in the system context in general. Okay, let's take a look at the documentation. And so we can see here there's documentation on ad set, get remove box IDs and there's documentation here on box reference Find. If we take a look, we can see the various options here. We can go down and look at the whole option documentation and see it's all documented search, start, stop directions, start from first doc, cell, or beginning or current selection, looping options, loop around, start at, stop at end directions and so on. Okay, so going back to box reference find, let's actually take a look at some of these examples. So we get up a notebook here, look at that and 300% and get this notebook here and look at it here. Okay, so this, this is a pre curated document with various box IDs. One of them is a box ID on the actual cell and in this new version, not the one that's currently shipping, you can, you can now search on or find box IDs on cells. The next thing to point out is that you can have multiple box IDs within a cell. And so if we take a look at this particular structure, we see there's an outer wrapper which is a pain box, which has a box ID of pain and it has an inner SuperScript box with two box IDs on it. And so we can search and make sure that require that we have both the outer box IDs and the inner box IDs. In other words, we have to follow this chain of box IDs down to actually form a match. And any of these, if they don't match, will make this thing not actually match here. This will be false. It can't actually find something there, but it can if it's pain. And of course we could just choose one of the box IDs for the inner box ID and that will still actually match. So from wherever we are here, it will find a match. And so there's various options here. We can change the order of the bar in the foo and it still finds it. Change the search directions. We've seen that we can take a look at some of the box offset examples, this is an offset to the second child. This will go to the whole parent of that particular box, this will go to the parent. And then the second child, this will go to the absolute sibling from that, from the found box, which is two here box, it'll go to the right sibling and so on. And it will find it if it's too far, and so on. Okay, so there's some sibling examples. And if we want to take a look at the segments, we can go find this, this particular box here. This is the first child. So this finds this box reference foo. And then it finds the first child, which is the space box. And then it'll find within that, with segments two to three, it finds by character count, two to three, it'll find the Z. Okay, so that's the basics for the documentation. And there's more documentation throughout that particular notebook. Right. Then I want to point out here, dynamic location, just some other uses of box IDs so in graphics we can use this. We have these dynamic locations which were typeset and they're basically here, a box ID on this dynamic name with the tag box with dynamic name Bob and Fred. And so this draws a line from the dynamic location of Bob to Fred. And so we can take a look at that. We can move that around. Let's just regenerate that here and move that around, Move that around and we see it's updating. And this I don't think actually goes through kernel, or it shouldn't. It doesn't necessarily need to. Okay, so there's. And I won't go into that too much, but that's used extensively in some parts of graphics. So then we'll talk about injection. So one of the things that we've got here with when we want to be able to inject, we've got these big complicated structures, sometimes very complicated box structures with very deeply nested sorts of things. And we want to be able to actually move the selection to a particular part of this box structure and maybe change some particular values. And so here we can do that. This is an example of injecting the selection into some deeply nested part of the actual box structure. And in fact, if we go get the help up and go command shift F on grid, we find there's an input field right there and there's actually a box ID behind that. And we've injected the value there. That's how the Help Brothers is actually working. It's working through box IDs right there. Okay, that's injection. And so then I wanted to talk a little bit about. I won't talk too much about namespace boxes, but here we have this box where from the outside in, we've got some button here, which is a box reference to the evaluation box. And so this box reference will look from where we are at the evaluation box up through to try and find an outer wrapper, outer parent. And from there it will find the box ID fug. And from there it will go back up to the parent of fug, wherever it is. And this whole button will replace that found box reference if it finds it with hello. And so we sort of see the structure. And so deep inside the structure, this button here do. It will actually go and it'll replace itself with hello. And of course, we don't need to track this, you know, with selection move, we've got to track exactly the parents put some more levels in here, put some other sort of structure of that particular structure R of H there, some sort of structure like that. And it will function exactly as. And in fact, we use this. This exact technique is used by our very own Ludy Andrea. In this particular example, we did the linguistic assistant. We can click on this and we change this to the. We want this to be milliseconds. And if we take a look at that box structure, it's kind of complicated. And the precursor was even more complicated. And if we hit the accept button, it will change that to be. It'll rewrite itself. It'll change the actual screen structure. This isn't a dynamic. This is not dynamic functionality. This is notebook programming. And it goes up from the template box through some namespace from the template display function to find the box and change it. And then, of course, this now has the correct full form, as we'd expect. Okay. And we can contrast that now with selection moving and this change of the button here, I should point out this button doesn't actually alter the selection. See that? The selection is its structure. I click on the button, it hasn't actually moved and hasn't changed the selection in the same way with the linguistic assistant. However, if we have this panel here and we're asking it to selection, move to the parent box, the evaluation box, which is something we can do right there, that will actually change the selection. So if selection is there, it will of course change the selection. Okay, so let's wrap up. Okay, so in conclusion, you can attach box IDs to specific boxes. And so one thing I should actually finally point out is that some of the box structures are somewhat we'd call unstable, like row boxes. As you type and different precedence levels and different sort of expressions, the row boxes will sort of shift around and be recreated and destroyed. And so you should attach the box IDs to stable boxes. Those are things like subs or superscript boxes, frame boxes, grid boxes, template boxes, tag boxes, any of the control slider box or input fields or some of those other sort of stable box structures. And once you've got them there, then you can actually target those boxes or indeed siblings or related. You can go from there to reference some particular sort of structures that you want to either inject stuff into or read stuff from, or overwrite or work with. And so these box IDs are a nice way to actually get very targeted and refined access to various parts of the notebook to do notebook programming and control the various interfaces and indeed an interface building. We in the front end here at Wolfram Research and everybody else in the world will be using these techniques and all of the other techniques that we've seen in terms of using cell IDs and UUIDs and cell objects and some of the other related stuff. Okay, that's it. I wanted to just point out there is this office hour coming up. If you do have any questions on these and related topics, please address them here at either the end of here. I'll be around or in this notebook front end office hour at 11 o' clock today. Okay, thank you. Okay, questions? Yeah, hi. And so I don't see any questions here. I'm sorry, the actual chat window there. There was a problem with bigmaker and I couldn't actually respond to any questions during the actual session. If there's any questions now, I'm happy to answer them. If not, I will see everybody in the front end session. Okay, thank you.
More from Guy

Cheryl Strauss Einhorn | The Human Edge: Smarter Decisions in the Age of AI | Talks at Google

Rachel Glennerster | Innovation and the Center for Global Development | Talks at Google

Rebecca Winthrop | Rethinking the Purpose of Education in the Age of AI | Talks at Google

Dr. Robert Wachter | A Giant Leap: How AI Is Transforming Healthcare... | Talks at Google






