 Thanks very much. I'm very happy to be here. So I'm an engineering lead on two of our product teams in Intercom. About 14 months ago, I introduced Ember to Intercom. It was a bit of a bet. We previously had a pretty large and growing Rails application. The UI was becoming quite complex. A lot of jQuery and a lot of problems with regression books. Performance was an issue. Development productivity was an issue. And I saw Ember as something that could solve a lot of these problems. A year and a bit later, we've replaced the whole of Intercom's UI with Ember. Out of our 50 engineers that we have, maybe 30 or so of them have contributed to the Embercom project. Probably around 18 actively work on the Ember app. Might even be more, might be 22. I wrote this blog post 30 years ago, right at the beginning, and I'm blinding what the promise of Ember was for Intercom. Basically, we've been rewarded handsomely for making that bet on Ember. Intercom, I'll give a quick demo so you can have an idea of what it is. This is Intercom here. So, this is a blog into a test app in Intercom here. Our mission is to make web business personal. And to allow any web business and all businesses, or increasingly, all businesses will be web businesses. To allow them to have personal and lightweight interactions with their customers. If you install Intercom in your own app, or your own mobile app, either through the JavaScript widget or through our Android or iOS SDK, you get this little widget. This in-app messenger is installed. We use it within our own product as well. And this allows you to track things that your users are doing. And that allows you to push data of each user to capture key events. What we're seeing here is a list of the users who are active on my fictitious application. You can segment users who see up to the top right. There's a list of segments. You can see who's active, who's created a project more than five times, who's slipping away, who's thinking about upgrading, but hasn't yet. From here, and the thing I'm actually going to talk about today is content creation within Intercom. What we're looking here is, you know, we want to send a simple message. This person, it will pop up. It could either be an in-app or an email. If it's an in-app, it will pop up in your own app. This content editor here, it's simple. It has a number of capabilities. We can insert attributes. It's got popovers. And it does things like, it knows a bit the scroll position, so we can prop it anywhere in the application, and it reacts like that. We can, you know, bold and italic, and it's got keyboard shortcuts. We can add links. We can navigate around. So a lot of this stuff you get for free with Content Edible, we really want to control and have very precise control over how this editor reacts. So we, as I've explained, we kind of had two goals at it. The second goal, we decided to basically ditch most of what Content Edible gives you and reimplement it ourselves. And this is one area that the editor has used. Another is creating auto-messages. An auto-message is something based on some criteria that you can queue up for messages for your users to receive. So it might be something like, when someone has been a user for three days, he might send them an auto-message and say, hey, how's it going? So I'll just write this here. So users who signed up, let's say more than three days ago, and we will make this an email. And here again is the Content Editor, here again. This is slightly different than the C on the left-hand side. As they scroll up and down, it's tracking the areas where we can drop things in. So I can put in buttons, for example, about alignment. The various things you can put in, HTML blocks, images, and data attributes again. You can also preview that in mobile. We can A-B test it, so we can have two versions of it. I'll actually go back and switch to an in-app, which are actually... This one's slightly different than it has. Headings, for example. Okay, so I'll come back to that when I go into some of the details ahead of SingWords. A third place that we use it in Intercom is in the inbox. So the inbox is a shared inbox across your team. And as you have conversations with your customers, this thing comes to life. And this is the Composer down here. And some interesting things about here is we have things like Save Applies, for example. We also have emoji. Oh, that was actually a... We have stickers. So if you do a single emoji, it becomes a sticker. If we put in a single emoji, a number comes in like that. Also, we have things like Mentions. And you can try and drop images in. There's many things we're doing with this over time. Basically, every page or every section of Intercom very soon will allow you to enter in content. So the Composer is really important for us. So it's an add-on. I'll show you it here. So we began it about six months ago. It's actually quite a big project. It's mainly three people working on it, Paddy, Pat, and myself. A couple of things to know about it. I mean, it's almost 3,000 commits. It was a really big project. It's building a content editor in the browser. It's surprisingly difficult. We've something like, I think, over 2,000 tests as well to make sure it works correctly. So I think why is an important question? Because this is actually, it's Composer 2 or B2. It's actually our third version. Composer B0 was basically a text box that accepted HTML or whatever. Composer B1 was based on content editable. And we released it and struggled with bugs for months and months and actually got to a point where we had maybe 80 or 90 bugs that were almost impossible to fix. And it resulted in sitting down and redesigning this thing and accepting that we needed to put in a big effort to build something really simple that we'd spoken for all over. My colleague, Pat, gave a talk to DublinJS a couple of months ago about content editable. And I'll just share a couple of these slides because it's kind of interesting and it kind of outlines the kind of problems we have. The API specified here were originally introduced in IE, but subsequently it can be copied by other browsers in a kind of inter-size fashion. Although the behavior specified here does not match any browser, it can serve as a target to converge to in the future. This was in 2014. So, I mean, right now content editable is still a bit of a mess. It's very difficult to do anything ambitious with. So here's a kind of example of that. So we have a content editable div. It's got a single character in it. I think it's what happens when we press Enter. So in Chrome, it appends an empty div and puts a card in that new div. Same as Safari. In IE 11, it's a paragraph in Firefox, a break, and another break with a type. This is one small example of maybe 50 or 100 cases or probably a lot more. We didn't discover them all, but it's really hard to work with this. It makes doing anything ambitious really difficult. There's other examples like putting in weird styles. There's also some of the APIs. Selection APIs are very difficult. Exec command is an API that you can manipulate content and selections within content editable. And again, it's full of bugs. So here's the kind of API it has. There's just huge differences in how these things work and what works and what doesn't across the browser as we found it. Some of them work with undo, some of them don't. So undo is something... When you start typing into a text area in the browser, you get all this stuff for free. You don't even think about it. You type a line of text and it flows over into another line and you go up the character. It doesn't just go directly up. It picks left or right. It kind of remembers where you were and you go back down again. As you navigate around, it's all this state internally. And it's only when we start a building in that we realize that we can't handle the upper character, for example, because we won't be able to put it in the places people expected. So, yeah, I'll link to this, but there's a lot of these slides that outline all of the complexity there. And ultimately, why we decided to build our V2 composer, which actually is a block editor and not a HML editor. So we created this thing called the block object model, which represents content as we want content to be in intercom. It's reasonably simple, but it's not HML. So the composer is a component. It's got two main parts to it. One that manages the document object model, the DOM, and the other, the block object model. And the DOM consists of a view that captures all of the events that happen. Mouse moves, clicks, character, keyboard, entries, anything like that, paste events. It deals with the DOM selection, so the range when you drag your cursor over some content, the actual selection. The UI coordinates are where your mouse is, and you can do insert points and things like that. And also deciding when to do rendering. The block object model has this thing called Composite State, which houses things like our custom undo stack. So as we found out with Content Edible, to have a coherent undo, redo stack, we just have to take control of ourselves. We have to take control of bold rules, metallic rules, return, backspace. Control, backspace, control, left character, right character, all of these things that you normally get for free. And then we have blocks for the actual data structure that represents the content. As I was building these slides today, I posted on Slack for some of my colleagues to let me know. Gustav is a designer, I think he might guess. He's formulated the blocks that run around, which they still aren't. I'm happy to say that. So the block object model, I guess, is probably the most interesting part of it. It's got an undo stack. It deals with block selection. It has a block list which represents content. It'll actually just jump into... This is the demo app for our CLI component, which is Embercom Composer. And you can see here, if I type below, press space, there's a couple of things to notice. One, this here is actually the block object model selection. So right now it's saying that the cart is in position six. You can see as I move left and right, that it represents the actual selection state. If I change it to a range, you can see that it's gone from, I see, one to four. And it's three-dimensional, so the rightmost numbers are the lowest level. So if I put in a couple of returns, and say another paragraph, you can see that I'm in block index one. So if I press up, that'll jump to zero, and then back to one again. So this, as you can see, is a move around. It represents where I am on the block object model. You can see that I can select across blocks. So this becomes very important when we want to mutate the block object model. If I want to apply a bold to this. As I said, the browser isn't going to do the bold for us. We need to do the bold ourselves. On the right-hand side here, we have some representations of what the composer, the block object model is. So here's the h and l section being rendered. This is a JSON representation of the blocks. And this is actually the block object model here itself. If I do something like do a bold, for example, you'll see that the block object model has a text of the first block, and it has a bold entity which goes from two to six, and there's a second bold entity in the second block, which goes from two to two, which is exactly what we're seeing up there. I'll come back to this in a little while, because there's some interesting things there. So to mutate the composer, we run commands on, so backspace, delete, return, insert attribute, paste, things like that. So here's a simple example. So let's say we have the text below there, and I'll actually mirror this here. So I think this is italic, and this is bold. Okay, so the blocks that we have, we have a single block, a single paragraph. The current is in the after first character. The paragraph has two entities, italic and bold, which go from various ranges. If I choose a selection, you can see the selection on the right-hand side changes. If I was to bold that, you'll see that I'm actually just running this command on the bold entity. If we run bold again, you'll see that it actually applies bold to it. You'll notice that if I press bold twice, the result is different. So we have to reverse engineer all of these sort of things that the browser does that you might have noticed, but if you were to use our composer and it did something different, you would definitely notice there's something not right for our composer. So some of the kind of interesting things that we do, if I type, let's say, this is a list, for example, but we want it so that if someone types one, if they want to do, you know, if they don't want a list, they just want to type the character of one, zero, and that they can actually backspace into it and it actually becomes just a text, one point and it's not a list anymore, as opposed to it actually being a list. That's the same for things like that. What else is interesting? Let's see. So I think rules like joining text, so what should happen when you go from press backspace here? Well, I mean, that makes sense, but again, we have to fill all of these rules ourselves. Distance necessitates a lot of tests, so we have a huge amount of tests that look like this. We have a nice DSL for writing these tests, so this is saying, this DSL is testing the case where backspace is pressed and before we have an order list that has two items, we have a particular selection, start and end, and then after the delete, we expect this to happen and the selection to be like that. So we have hundreds and hundreds of tests like this, which are just ensuring that same rules are happening within the composer. Sinking is interesting as well, so a lot of the stuff you get for free with Ember doesn't fly here because it's... Ember isn't designed to work in world within content-tendable. When Tom Dale was overworking with us at the big... early last year, he did a spike out of this, which used low-level end stuff and it was really interesting, but we found we had to... we hit a wall with Ember. Sorry, I should say, with the view layer in Ember, it's not optimized for working with content-tendable itself. I mean, it's a different world. So we take on syncing ourselves, so some UI event happens, a keystroke, a click, haste, something like that, and it's either going to be a command that we run or a sync, which I'll talk about in a second. Either one of those can you take the bomb, the block object model from that, then we can get a HTML representation of the block that's changed, and we do a sort of a dumb dip, not as clever as Climber does, but we compare them, and if there is a difference, we involve that change in the dump. We then reset the character to where it was, and that happens every time you... not every character, we don't do a sync for performance reasons. So, for example, if I type, hello, you'll see that nothing is updated yet. But if I click away, it will. Or if I press a space, it does. If I do a backspace into the word, it doesn't. But if I have a bold entity in there, it doesn't... it does. You can see it's updating there. So, we've all these rules about it, and we're very specific about when something key has happened, when we need to mutate the dump and re-render part of the composite, and we do it, but as much as possible, we queue it up so the browser is picked. So, really what we want is this thing to be very fast, and that people simply won't notice it and it actually feels like a text area. When there's actually all this whole cycle going on pretty much all the time. Performance is really important to us. So, we have all of these metrics that we capture for various things, like syncing. See, we actually haven't... we've only done one sync there. Calculation stuff, key up, key down, these have to be really quick. We have things like insert character, calculating the inner HTML of a particular block, black space. Interesting, when I just click on the browser there, it's actually running some of these things. This is what allows us to keep performance quicker. We're actually making it faster and faster all the time because as it's rolled out, more and more parts have come, and the more important it is to be really quick. One of the things I want to do is open source as much of this as possible. Go like this, or undo stack. So, this is the first baby steps towards doing that. So this is the undo stack that we extracted out of it. It's a very simple emerald seal icon add-on. It's like 40 or 50 lines of code, but it allows you to undo stack for easy in your own application. I think there's probably 10, 15 add-ons that we can extract out of the composer. We'll never make the whole composer open source because it's just so much specific to intercom within it. I was chatting to Tyler Love from Bustle, who's also building something like this, a slightly different model, but they're open sourcing the whole thing. So we're looking at the areas that we intersect and we can both work on the open sourcing components to make up the commonality. So expect to be open sourcing more of the composer soon. How am I doing for time? Five minutes? Okay, cool. So there's some other things we do. This is something I'd like to open source as well. Sorry, not that. System styling. Sorry, where is this? Sorry, it's been a little while since I've actually worked on this project and working on other things and the menu has changed slightly. I'll show it to you in the app. So let's say if I'm in an email, one of the things that's hard is, well, a challenge is that we allow our customers to create their own email templates and the problem with email templates is they can put any HTML in, any CSS. And usually what you do in that scenario is you just render the whole thing in an iFrame. It's isolated from the page. It's got its own natural CSS reset. You can't leak styles out or in. But because we've got the composer and it's part of the Ember application, we can't just stick an iFrame in there. So we have to solve it a different way. We came up with this... Let me find a template that actually works. Some of these are... Let's try this one. So we came up with this technique that we're nearly about an end for. We call it CSS slurping. So we take the template, we inject in the content that we now will allow. All of the possible blocks you can have. Put it off to an iFrame, run through the DOM, extracting CSS styles for all the things we care about and then building our own name... and putting that in a namespace and then injecting it into the page around the composer. So it actually allows us to allow our customers to inject their own styles here and for us to have it completely isolated from the rest of the application. Editors, I think I'll skip over. You select the range of text section. I'll do it very briefly. So it's simply these here. We've got ten different types of editor. What's interesting about them... I guess they're... On the right hand side, we keep track of things like... we've computed properties like... I have two composers on our page. So we keep track of things like the current, editable entity in block and it does not seem to be updating. So I'll just skip over that. Performance to talk about. Last thing is testing and we have lots and lots of tests. I'll just run them in the background as they go out. There is literally thousands of them. I'll leave that there. So that's it. Things I could have talked about. There's so many directions to go deep in the composer. There's so much detail in there. I'd be happy afterwards if anyone's got any questions to chat about any of those things. So, any questions? I was going to ask you what your strategy is in terms of versions of Denver. Which one are you on and how often do you update? Right, so we're on 110 at the moment. A custom build on 110 with some future flags. As much as possible, try to stay up to date. Our application is pretty large now and there's like four different product teams that are the eight or nine teams that we have that build a lot of Ember or build their application in Embercom. So it takes a little bit of coordination to upgrade. In some cases there are some areas of application where we've done something very advanced where we cross the boundary into using some private APIs and as we upgrade they become things that take a little bit of time. So, we're really interested and we're really motivated. It's all next week blocked booked to work on testing with Glimmer because for an Ember, an application like Intercom which is pretty large it's data heavy, data binding heavy we've like you know, the inbox for example it's pretty static when you look at it there but when you turn on real time and you have hundreds of conversations coming in every era and the whole thing's moving around speed is critical. So the promise of Glimmer is it's going to make our application just way faster than it is. If Glimmer let's say it never happened we would have had to invest a lot of time into solving that performance ourselves and what it meant ducking down in some components to manage things ourselves I call it ducking down to metal sometimes it's really jQuery, assembly or whatever we're really motivated to keep up to date where I guess two point releases behind that we'll be on one for the end or I'd imagine couple of weeks after it's released Just looking at all the config values it looks like you've got lots of different situations where this has slightly different permutations of functionality. How are you finding is it literally just that you just have a flat config and you just configure it differently in different places or do you have like different like an inheritance structure or a composition structure how are you managing that? We have lots of features you can turn on and off then we have these scenarios so this is like in-app commencement turn these things on plain email turn these things on email turn these on we even email canary that we can feature flag on our own app and try it out so all of the different places in-app chat this enforces what's allowed and what isn't and we have tests to verify that those things are you basically like pass a config object into the component and just pass it like a pre-defined and reach in and trick the bits ourselves we need to and the content it tells you where the cursor is alright so there's a DOM selection API which the pack talks about in his topic somewhat let's see if I can find it quickly selection okay so there's this selection API and it gives you all these anchor nodes and pivot nodes and it's quite it's quite a complex API it's not if you were to do it again it would be better you'd do a better API I think it's obviously crucial to the composer and we have a thing which maps the DOM selection to our block object model selection and that is isolated and once it does its job then we're in a much better world where we have block object model selection and we can operate on that much more readily because that's a hierarchy of amber objects rather than the DOM which is quite tricky to work with I think they were used in our paragraph like the fact that we slaked all the tabs yeah so do you like the way it works? can you just sort of tell drugs I want to use this new API and use yours instead so we use the browser's API to tell us what the selection is we then map that to a block object model selection let's say you click backspace for example we then run a backspace command on the block object model that will then change we'll look at the 2HML representation of that we compare it to the DOM we'll see what's changed we'll make those changes ourselves and then we'll reinstate the carrot where it should be based on what the block object model says the carrot is we have to do all this there's a lot involved there the nice thing is that no one notices it right so if I select there's a very simple example right and I click return right so it's done all of that right there but you don't notice it because it's quick enough and it behaves like a normal editor does so it seems normal so you don't want to use a vegetarian editor but I'll keep all the systems out of them and I'll search for a cool one it actually is pretty yeah I mean I'd say this is one of the funnest things I've ever built really challenging really deceptively challenging I'm lucky enough to have a worker really amazing people and this was a llama project an intercom we like really short projects it rave very quickly I mean for obvious reasons but sometimes you need to take on a six month project and this was a six month project and yeah it was successful in the end I've got another question you started out by talking about a pain point you know related to editable and all the different browsers how they behave how much time are you spending now are you distracted from that I imagine that you're targeting versus all the platforms no no so I think we've dropped even the IE 10 we may be thinking of dropping the IE 10 obviously a product like intercom and we have two parts to it one is our customers so the widget the in-app messenger that the javascript has deployed in their application like Google analytics or something like that that's actually a background application but it has to support IE 6 for example so they live in a completely different world we're working on the application and obviously our customers work that business our customers are in modern companies that have modern SaaS products themselves or mobile apps or whatever so they tend to use modern equipment modern browsers and like it's very easy for us to look at the metrics of our browser usage and it's like okay can we drop IE 10 and it's like oh yeah it's like not even 0.02% of course we can, it's great just to be able to do that so we're lucky enough that we work in modern browsers