diff --git a/.arcconfig b/.arcconfig
new file mode 100644
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,6 @@
+{
+  "phabricator.uri": "https://we.phorge.it/",
+  "load": [
+    "src/"
+  ]
+}
diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,2 @@
-/data/drawio
-
-/src/__phutil_library_init__.php
-/src/__phutil_library_map__.php
+/drawio
 /src/.phutil_module_cache
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -3,12 +3,19 @@
 
 Installation
 ============
-1) Extract the content of this repository into <phorge>/src/extensions
-2) <arcanist>/bin/arc liberate
-3) <phorge>/bin/storage upgrade
-4) CD to <phorge>/src/extensions/drawio/data
-5) git clone https://github.com/jgraph/drawio.git
-6) Diagrams application is available under "More Applications" in Phorge.
+1) `git clone` this repository somewhere safe: `</somewhere/safe/diagrams>`
+2) CD to `</somewhere/safe/diagrams>` and `git clone https://github.com/jgraph/drawio.git && cd drawio && git switch v24.2.5`
+3) `<phorge>/bin/storage upgrade`
+4) In Phorge's `conf/local/local.json` add the path to the `src/` dir to the entry `load-libraries`,
+   something like this:
+```
+  ...
+  "load-libraries": [
+    "/somewhere/safe/diagrams/src/"
+  ],
+  ...
+```
+5) Diagrams application is available under "More Applications" in Phorge.
    You may add it to your navigator menu via "Edit Menu"
 
 Usage
diff --git a/data/iframe.css b/data/iframe.css
deleted file mode 100644
--- a/data/iframe.css
+++ /dev/null
@@ -1,97 +0,0 @@
-body { overflow:hidden; }
-div.picker { z-index: 10007; }
-.geSidebarContainer .geTitle input {
-    font-size:8pt;
-    color:#606060;
-}
-.geBlock {
-    z-index:-3;
-    margin:100px;
-    margin-top:40px;
-    margin-bottom:30px;
-    padding:20px;
-    text-align:center;
-    min-width:50%;
-}
-.geBlock h1, .geBlock h2 {
-    margin-top:0px;
-    padding-top:0px;
-}
-.geEditor *:not(.geScrollable)::-webkit-scrollbar {
-    width:10px;
-    height:10px;
-}
-.geEditor ::-webkit-scrollbar-track {
-    background-clip:padding-box;
-    border:solid transparent;
-    border-width:1px;
-}
-.geEditor ::-webkit-scrollbar-corner {
-    background-color:transparent;
-}
-.geEditor ::-webkit-scrollbar-thumb {
-    background-color:rgba(0,0,0,.1);
-    background-clip:padding-box;
-    border:solid transparent;
-    border-radius:10px;
-}
-.geEditor ::-webkit-scrollbar-thumb:hover {
-    background-color:rgba(0,0,0,.4);
-}
-.geTemplate {
-    border:1px solid transparent;
-    display:inline-block;
-    _display:inline;
-    vertical-align:top;
-    border-radius:3px;
-    overflow:hidden;
-    font-size:14pt;
-    cursor:pointer;
-    margin:5px;
-}
-
-@keyframes init-spinner-animation {
-  0%, 39%, 100% {
-     opacity: 0.33;
-  }
-  40% {
-     opacity: .83;
-  }
-}
-.init-spinner {
-  position: relative;
-  height: 80px;
-  width: 80px;
-  left: calc(50% - 50px);
-}
-.init-spinner .content {
-  position: absolute;
-  width: 0px;
-  left: 50%;
-  top: 50%;
-}
-
-.init-spinner .content .spike {
-  position: absolute;
-  top: -2.5px;
-  width: 15px;
-  height: 5px;
-  background: #000c;
-  border-radius: 2.5px;
-  transform-origin: left center 0px;
-}
-
-.init-spinner .content .spike .animator {
-  width: 100%;
-  height: 100%;
-  background: rgb(255, 255, 255);
-  border-radius: 2.5px;
-  box-shadow: transparent 0px 0px 1px;
-  animation-name: init-spinner-animation;
-  animation-duration: 1s;
-  animation-timing-function: linear;
-  animation-iteration-count: infinite;
-  animation-direction: normal;
-  animation-fill-mode: none;
-  animation-play-state: running;
-}
\ No newline at end of file
diff --git a/data/iframe1.js b/data/iframe1.js
deleted file mode 100644
--- a/data/iframe1.js
+++ /dev/null
@@ -1,144 +0,0 @@
-var urlParams = (function() {
-  var result = {};
-  var params = window.location.search.slice(1).split('&');
-
-  for (var i = 0; i < params.length; i++) {
-    var idx = params[i].indexOf('=');
-
-    if (idx > 0) {
-      result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
-    }
-  }
-
-  return result;
-})();
-
-if (window.location.hash != null && window.location.hash.substring(0, 2) == '#P') {
-  try {
-    urlParams = JSON.parse(decodeURIComponent(window.location.hash.substring(2)));
-
-    if (urlParams.hash != null) {
-      window.location.hash = urlParams.hash;
-    }
-  }
-  catch (e) {
-  }
-}
-
-(function() {
-  var proto = window.location.protocol;
-  var host = window.location.host;
-  var href = proto + '//' + host + window.location.href.substring(
-              window.location.protocol.length +
-              window.location.host.length + 2);
-
-  if (href != window.location.href) {
-    window.location.href = href;
-  }
-})();
-
-function mxmeta(name, content, httpEquiv) {
-  try {
-    var s = document.createElement('meta');
-
-    if (name != null) {
-      s.setAttribute('name', name);
-    }
-
-    s.setAttribute('content', content);
-
-    if (httpEquiv != null) {
-      s.setAttribute('http-equiv', httpEquiv);
-    }
-
-    var t = document.getElementsByTagName('meta')[0];
-    t.parentNode.insertBefore(s, t);
-  }
-  catch (e)
-  {
-  }
-}
-
-function mxscript(src, onLoad, id, dataAppKey, noWrite, onError) {
-  var s = document.createElement('script');
-  s.setAttribute('type', 'text/javascript');
-  s.setAttribute('defer', 'true');
-  s.setAttribute('src', src);
-
-  if (id != null) {
-    s.setAttribute('id', id);
-  }
-
-  if (dataAppKey != null) {
-    s.setAttribute('data-app-key', dataAppKey);
-  }
-
-  if (onLoad != null) {
-    var r = false;
-
-    s.onload = s.onreadystatechange = function() {
-      if (!r && (!this.readyState || this.readyState == 'complete')) {
-        r = true;
-        onLoad();
-      }
-    };
-  }
-
-  if (onError != null) {
-    s.onerror = function(e) {
-      onError('Failed to load ' + src, e);
-    };
-  }
-
-  var t = document.getElementsByTagName('script')[0];
-
-  if (t != null) {
-    t.parentNode.insertBefore(s, t);
-  }
-}
-
-function mxinclude(src) {
-  var g = document.createElement('script');
-  g.type = 'text/javascript';
-  g.async = true;
-  g.src = src;
-
-  var s = document.getElementsByTagName('script')[0];
-  s.parentNode.insertBefore(g, s);
-}
-
-(function() {
-  var name = 'diagrams.net';
-  mxmeta('apple-mobile-web-app-title', name);
-  mxmeta('application-name', name);
-})();
-
-var isLocalStorage = true;
-var mxScriptsLoaded = false, mxWinLoaded = false;
-
-function checkAllLoaded() {
-  if (mxScriptsLoaded && mxWinLoaded) {
-      App.main();
-  }
-}
-
-var t0 = new Date();
-  (function() {
-    function loadAppJS() {
-        mxscript('js/app.min.js', function() {
-            mxScriptsLoaded = true;
-            checkAllLoaded();
-            mxscript('js/PostConfig.js');
-        });
-    }
-
-    mxscript('js/PreConfig.js', loadAppJS);
-})();
-
-window.onerror = function() {
-  var status = document.getElementById('geStatus');
-
-  if (status != null) {
-      status.innerHTML = 'Page could not be loaded. Please try refreshing.';
-  }
-};
diff --git a/data/iframe2.js b/data/iframe2.js
deleted file mode 100644
--- a/data/iframe2.js
+++ /dev/null
@@ -1,7 +0,0 @@
-window.addEventListener('load', function()
-{
-  mxWinLoaded = true;
-  checkAllLoaded();
-});
-
-phorge_extension = parent.phorge_extension;
\ No newline at end of file
diff --git a/data/phorge_extension.js b/data/phorge_extension.js
deleted file mode 100644
--- a/data/phorge_extension.js
+++ /dev/null
@@ -1,246 +0,0 @@
-var phorge_extension = {};
-
-function edit(image) {
-  var iframe = document.createElement('iframe');
-  var iframeInitializedEvent = new CustomEvent('initReceived');
-  iframe.setAttribute('title', 'diagrams.net editor');
-  iframe.setAttribute('frameborder', '0');
-  iframe.style.width = '100%';
-  iframe.style.height = 'calc(100vh - 91px)';
-  iframe.style.marginTop = '30px';
-  iframe.style.marginBottom = '-16px';
-  image.style.display = 'none';
-
-  var receive = function (evt) {
-    if (evt.data.length > 0) {
-      var msg = JSON.parse(evt.data);
-      if (msg.event == 'init') {
-        if (phorge_extension.diagramBase64) {
-          image.src = 'data:image/png;base64,' + phorge_extension.diagramBase64;
-
-          var diagramName = document.querySelector('.diagramName');
-          diagramName.style.display = 'inline-block';
-          diagramName.querySelector('a').innerText = phorge_extension.diagramName;
-          if (phorge_extension.diagramVersion != '') {
-            diagramName.querySelector('.version').innerText = '(#'
-              + phorge_extension.diagramVersion 
-              + ')';
-          }
-        }
-
-        iframe.contentWindow.postMessage(JSON.stringify({
-          action: 'load',
-          autosave: 1,
-          xmlpng: image.getAttribute('src')
-        }), '*');
-      }
-      else if (msg.event == 'load') {
-        // enable Mathematical Typesettings by default
-        iframe.contentWindow.sb.editorUi.setMathEnabled(true);
-
-        setupButtonsInMenuToolbar();
-      }
-      else if (msg.event == 'export') {
-        saveFlowchart(name, msg.data, iframe);
-      }
-      else if (msg.event == 'save') {
-        iframe.contentWindow.postMessage(JSON.stringify({
-          action: 'export',
-          format: 'xmlpng',
-          xml: msg.xml,
-          spin: 'Updating page'
-        }), '*');
-      }
-    }
-  };
-
-  window.addEventListener('message', receive);
-  iframe.setAttribute('src', phorge_extension.editor);
-  document.querySelector('#mainScreen').appendChild(iframe);
-  iframe.dispatchEvent(iframeInitializedEvent);
-
-  iframe.contentWindow.RESOURCES_PATH = document.baseURI + 'iframe/resources';
-  iframe.contentWindow.STENCIL_PATH = document.baseURI + 'iframe/stencils';
-  iframe.contentWindow.IMAGE_PATH = document.baseURI + 'iframe/images';
-  iframe.contentWindow.STYLE_PATH = document.baseURI + 'iframe/styles';
-  iframe.contentWindow.CSS_PATH = document.baseURI + 'iframe/styles';
-}
-
-function loadJsExtension(diagramName, diagramPHID, diagramVersion, diagramBase64) {
-  var baseURI = document.baseURI;
-  if (diagramName != '' && diagramBase64 != '') {
-    baseURI = baseURI.substr(0, baseURI.length - diagramName.length - 1);
-    
-    if (diagramVersion != '') {
-      baseURI = baseURI.substr(0, baseURI.length - diagramVersion.length - 1);
-    }
-  }
-  phorge_extension.baseURI = baseURI;
-  phorge_extension.csrf = document.querySelector('input[name="__csrf__"]')?.value;
-  phorge_extension.editor = baseURI + '/iframe/?embed=1&spin=0&proto=json&noExitBtn=1';
-  phorge_extension.name = null;
-  phorge_extension.editor += '&lang=en';
-  phorge_extension.editor += '&ui=min';
-  phorge_extension.diagramPHID = diagramPHID;
-  phorge_extension.diagramName = diagramName;
-  phorge_extension.diagramVersion = diagramVersion;
-  phorge_extension.diagramBase64 = diagramBase64;
-
-  document.addEventListener('DOMContentLoaded', function () {
-    edit(document.querySelector('img.drawio'));
-  }, false);
-}
-
-function saveFlowchart(name, flowchartData, iframe) {
-  var diagramID = document.querySelector('.diagramName a').innerText.replace(/^DIAG/, '');
-  var csrf = document.querySelector('input[name="__csrf__"]')?.value;
-  var data = new URLSearchParams();
-  data.append('data', flowchartData);
-  data.append('diagramID', diagramID);
-  data.append('__csrf__', csrf);
-  data.append('__form__', '1');
-  data.append('__ajax__', 'true');
-
-  var xmlhttp = new XMLHttpRequest();
-  xmlhttp.overrideMimeType('application/json');
-  xmlhttp.open('POST', 'save/', true);
-  xmlhttp.onload = function () {
-    if (xmlhttp.readyState == 4) {
-      var errorMessage = null;
-      try {
-        var result = JSON.parse(xmlhttp.responseText);
-        if (result.Status != 'OK') {
-          errorMessage = result.Error;
-        } else {
-          // make sure we don't show messagebox about redirection in browser
-          iframe.parentNode.removeChild(iframe);
-
-          if (!phorge_extension.diagramVersion || !phorge_extension.diagramVersion.trim()) {
-            if (!phorge_extension.diagramName || !phorge_extension.diagramName.trim()) {
-              // load new diagram
-              window.location = window.location + '/DIAG' + result.DiagramID;
-            } else {
-              // reload actual page (so versioned diagrams info is also updated)
-              window.location.reload();
-            }
-          } else {
-            // cut off version id from url
-            var url = document.baseURI
-                              .substring(0, 
-                                         document.baseURI
-                                                 .length
-                                          - phorge_extension.diagramVersion
-                                                            .length
-                                          - 1
-                                        );
-            // redirect to latest version of diagram
-            window.location = url;
-          }
-        }
-      } catch (exc) {
-        errorMessage = exc.message;
-      }
-    }
-  };
-
-  xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;');
-  xmlhttp.send(data);
-}
-
-function setupButtonsInMenuToolbar() {
-  var iframe = document.querySelector('iframe');
-  var btnSave = Array.prototype.slice.call(
-    iframe.contentDocument
-      .querySelector('.geMenubarContainer')
-      .querySelectorAll('button'), 0
-  ).reverse()[0];
-
-  // identify Exit Button
-  btnSave.classList.add('btnSave');
-
-  // change layout settings of area where btnSave belongs to so
-  // that the dropdown menu is not hidden under the drawing area
-  btnSave.parentNode.style.position = 'fixed';
-
-  // create extra controls
-  var subscribeUnsubscribe = setupSubscriptionButtonInMenuToolbar(iframe, btnSave);
-  var dropdown = setupVersionDropDownInMenuToolbar(iframe, subscribeUnsubscribe);
-
-  // initialize toolbuttons
-  for (var tb in phorge_extension.toolbtn) {
-    if (phorge_extension.toolbtn.hasOwnProperty(tb)) {
-      phorge_extension.toolbtn[tb].instance.initialize();
-    }
-  }
-}
-
-function setupVersionDropDownInMenuToolbar(iframe, btnLeft) {
-  // generate 'Select Version' button
-  // create the dropdown element
-  const dropdown = document.createElement('div');
-  dropdown.classList.add('toolbtn-version-dropdown');
-
-  // create the dropdown toggle button
-  const toggle = document.createElement('button');
-  toggle.classList.add('toolbtn-version-dropdown-toggle');
-  toggle.textContent = 'Select Version';
-  toggle.title = 'View previous versions';
-  dropdown.appendChild(toggle);
-
-  const iframeContent = document.querySelector('iframe').contentDocument;
-  const menubarContainer = iframeContent.querySelector('.geMenubarContainer');
-
-  // create the dropdown menu
-  const menu = document.createElement('div');
-  menu.classList.add('toolbtn-version-dropdown-menu');
-  menubarContainer.parentNode.insertBefore(menu, menubarContainer.nextSibling);
-
-  // create the version list
-  const versionList = document.createElement('ul');
-  versionList.classList.add('toolbtn-version-list');
-
-  menu.appendChild(versionList);
-
-  // create the prev and next buttons
-  const prevBtn = document.createElement('button');
-  prevBtn.classList.add('toolbtn-version-prev-btn');
-  prevBtn.textContent = '<';
-
-  const nextBtn = document.createElement('button');
-  nextBtn.classList.add('toolbtn-version-next-btn');
-  nextBtn.textContent = '>';
-
-  menu.appendChild(prevBtn);
-  menu.appendChild(nextBtn);
-
-  // place dropdown next to button on the left
-  btnLeft.parentNode.insertBefore(dropdown, btnLeft.nextSibling);
-
-  return dropdown;
-}
-
-function setupSubscriptionButtonInMenuToolbar(iframe, btnLeft) {
-  // generate 'Subscribe/Unsubscribe' button
-  // create grouping div element
-  const div = document.createElement('div');
-  div.classList.add('toolbtn-diagram-subscription');
-
-  // create subscribe button
-  const btnSubscribe = document.createElement('button');
-  btnSubscribe.classList.add('subscribe');
-  btnSubscribe.classList.add('eye');
-  btnSubscribe.title = 'Subscribe';
-  div.appendChild(btnSubscribe);
-
-  // create unsubscribe button
-  const btnUnsubscribe = document.createElement('button');
-  btnUnsubscribe.classList.add('unsubscribe');
-  btnUnsubscribe.classList.add('eye');
-  btnUnsubscribe.title = 'Unsubscribe';
-  div.appendChild(btnUnsubscribe);
-
-  // place grouping next to button on the left
-  btnLeft.parentNode.insertBefore(div, btnLeft.nextSibling);
-
-  return div;
-}
\ No newline at end of file
diff --git a/data/iframe-toolbtn.css b/rsrc/iframe-toolbtn.css
rename from data/iframe-toolbtn.css
rename to rsrc/iframe-toolbtn.css
--- a/data/iframe-toolbtn.css
+++ b/rsrc/iframe-toolbtn.css
@@ -1,4 +1,8 @@
-/* begin stylesheets for version toolbutton */
+/**
+ * @provides diagram-css-iframe-toolbtn
+ */
+
+/* begin stylesheets for version toolbutton  */
 .toolbtn-version-dropdown {
   right: 0;
   white-space: nowrap;
diff --git a/data/iframe-toolbtn.js b/rsrc/iframe-toolbtn.js
rename from data/iframe-toolbtn.js
rename to rsrc/iframe-toolbtn.js
--- a/data/iframe-toolbtn.js
+++ b/rsrc/iframe-toolbtn.js
@@ -1,8 +1,14 @@
+/**
+ * @provides diagram-js-iframe-toolbtn
+ */
+
 /**
  * global dicttionary containing all configuration of all installed toolbuttons
  */
 const toolbtn = {};
 
+const phorge_extension = parent.__diagram;
+
 /**
  * Abstract parent class for creating ToolButtons
  */
@@ -15,7 +21,7 @@
     toolbtn[name] = {};
     toolbtn[name].instance = this;
 
-    parent.phorge_extension.toolbtn = toolbtn;
+    phorge_extension.toolbtn = toolbtn;
   }
 
   initialize() {
@@ -248,4 +254,4 @@
  * Initialization of all ToolButton classes
  */
 new ToolButtonVersion('version');
-new ToolButtonSubscription('subscription');
\ No newline at end of file
+new ToolButtonSubscription('subscription');
diff --git a/rsrc/phorge_extension.js b/rsrc/phorge_extension.js
new file mode 100644
--- /dev/null
+++ b/rsrc/phorge_extension.js
@@ -0,0 +1,280 @@
+/**
+ * @provides javelin-behavior-diagram-extension
+ */
+
+JX.behavior('diagram-extension', function(config) {
+
+  var phorge_extension = {};
+  console.log('diagram-extension behavior called with config:', config);
+
+  function edit(image) {
+    var iframe = document.createElement('iframe');
+    var iframeInitializedEvent = new CustomEvent('initReceived');
+    iframe.setAttribute('title', 'diagrams.net editor');
+    iframe.setAttribute('frameborder', '0');
+    iframe.style.width = '100%';
+    iframe.style.height = 'calc(100vh - 91px)';
+    iframe.style.marginTop = '30px';
+    iframe.style.marginBottom = '-16px';
+    image.style.display = 'none';
+
+    var receive = function (evt) {
+      if (evt.data.length > 0) {
+        var msg = JSON.parse(evt.data);
+        if (msg.event == 'init') {
+          if (phorge_extension.diagramBase64) {
+            image.src = 'data:image/png;base64,' + phorge_extension.diagramBase64;
+
+            var diagramName = document.querySelector('.diagramName');
+            diagramName.style.display = 'inline-block';
+            diagramName.querySelector('a').innerText = phorge_extension.diagramName;
+            if (phorge_extension.diagramVersion != '') {
+              diagramName.querySelector('.version').innerText = '(#'
+                + phorge_extension.diagramVersion
+                + ')';
+            }
+          }
+
+          iframe.contentWindow.postMessage(JSON.stringify({
+            action: 'load',
+            autosave: 1,
+            xmlpng: image.getAttribute('src')
+          }), '*');
+        }
+        else if (msg.event == 'load') {
+          // enable Mathematical Typesettings by default
+          iframe.contentWindow.sb.editorUi.setMathEnabled(true);
+
+          setupButtonsInMenuToolbar();
+        }
+        else if (msg.event == 'export') {
+          saveFlowchart(name, msg.data, iframe);
+        }
+        else if (msg.event == 'save') {
+          iframe.contentWindow.postMessage(JSON.stringify({
+            action: 'export',
+            format: 'xmlpng',
+            xml: msg.xml,
+            spin: 'Updating page'
+          }), '*');
+        }
+      }
+    };
+
+    window.addEventListener('message', receive);
+    iframe.setAttribute('src', phorge_extension.editor);
+    document.querySelector('#mainScreen').appendChild(iframe);
+    console.log('Iframe appended:', iframe);
+    iframe.contentWindow.addEventListener('DOMContentLoaded', addToolbarResourcesToIframe);
+    iframe.dispatchEvent(iframeInitializedEvent);
+
+    iframe.contentWindow.RESOURCES_PATH = document.baseURI + 'iframe/resources';
+    iframe.contentWindow.STENCIL_PATH = document.baseURI + 'iframe/stencils';
+    iframe.contentWindow.IMAGE_PATH = document.baseURI + 'iframe/images';
+    iframe.contentWindow.STYLE_PATH = document.baseURI + 'iframe/styles';
+    iframe.contentWindow.CSS_PATH = document.baseURI + 'iframe/styles';
+  }
+
+  function addToolbarResourcesToIframe() {
+    var head = this.document.getElementsByTagName('head')[0];
+
+    var script = this.document.createElement('script');
+    script.src = config.toolbarJs;
+    head.appendChild(script);
+
+    var styles = this.document.createElement('link');
+    styles.setAttribute('type', 'text/css');
+    styles.setAttribute('rel', 'stylesheet');
+    styles.setAttribute('href', config.toolbarCss);
+    head.appendChild(styles);
+
+    console.log('Iframe DOM loaded:', this.document.body.innerHTML);
+  }
+
+  function loadJsExtension(diagramName, diagramPHID, diagramVersion, diagramBase64) {
+    console.log('loadJsExtension called with args:', diagramName, diagramPHID, diagramVersion, diagramBase64);
+    var baseURI = document.baseURI;
+    if (diagramName != '' && diagramBase64 != '') {
+      baseURI = baseURI.substr(0, baseURI.length - diagramName.length - 1);
+
+      if (diagramVersion != '') {
+        baseURI = baseURI.substr(0, baseURI.length - diagramVersion.length - 1);
+      }
+    }
+    phorge_extension.baseURI = baseURI;
+    phorge_extension.csrf = document.querySelector('input[name="__csrf__"]')?.value;
+    phorge_extension.editor = baseURI + '/iframe/?embed=1&spin=0&proto=json&noExitBtn=1';
+    phorge_extension.name = null;
+    phorge_extension.editor += '&lang=en';
+    phorge_extension.editor += '&ui=min';
+    phorge_extension.diagramPHID = diagramPHID;
+    phorge_extension.diagramName = diagramName;
+    phorge_extension.diagramVersion = diagramVersion;
+    phorge_extension.diagramBase64 = diagramBase64;
+
+    document.addEventListener('DOMContentLoaded', function () {
+      edit(document.querySelector('img.drawio'));
+    }, false);
+  }
+
+  function saveFlowchart(name, flowchartData, iframe) {
+    var diagramID = document.querySelector('.diagramName a').innerText.replace(/^DIAG/, '');
+    var csrf = document.querySelector('input[name="__csrf__"]')?.value;
+    var data = new URLSearchParams();
+    data.append('data', flowchartData);
+    data.append('diagramID', diagramID);
+    data.append('__csrf__', csrf);
+    data.append('__form__', '1');
+    data.append('__ajax__', 'true');
+
+    var xmlhttp = new XMLHttpRequest();
+    xmlhttp.overrideMimeType('application/json');
+    xmlhttp.open('POST', 'save/', true);
+    xmlhttp.onload = function () {
+      if (xmlhttp.readyState == 4) {
+        var errorMessage = null;
+        try {
+          var result = JSON.parse(xmlhttp.responseText);
+          if (result.Status != 'OK') {
+            errorMessage = result.Error;
+          } else {
+            // make sure we don't show messagebox about redirection in browser
+            iframe.parentNode.removeChild(iframe);
+
+            if (!phorge_extension.diagramVersion || !phorge_extension.diagramVersion.trim()) {
+              if (!phorge_extension.diagramName || !phorge_extension.diagramName.trim()) {
+                // load new diagram
+                window.location = window.location + '/DIAG' + result.DiagramID;
+              } else {
+                // reload actual page (so versioned diagrams info is also updated)
+                window.location.reload();
+              }
+            } else {
+              // cut off version id from url
+              var url = document.baseURI
+                                .substring(0,
+                                          document.baseURI
+                                                  .length
+                                            - phorge_extension.diagramVersion
+                                                              .length
+                                            - 1
+                                          );
+              // redirect to latest version of diagram
+              window.location = url;
+            }
+          }
+        } catch (exc) {
+          errorMessage = exc.message;
+        }
+      }
+    };
+
+    xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;');
+    xmlhttp.send(data);
+  }
+
+  function setupButtonsInMenuToolbar() {
+    var iframe = document.querySelector('iframe');
+    var btnSave = Array.prototype.slice.call(
+      iframe.contentDocument
+        .querySelector('.geMenubarContainer')
+        .querySelectorAll('button'), 0
+    ).reverse()[0];
+
+    // identify Exit Button
+    btnSave.classList.add('btnSave');
+
+    // change layout settings of area where btnSave belongs to so
+    // that the dropdown menu is not hidden under the drawing area
+    btnSave.parentNode.style.position = 'fixed';
+
+    // create extra controls
+    var subscribeUnsubscribe = setupSubscriptionButtonInMenuToolbar(iframe, btnSave);
+    var dropdown = setupVersionDropDownInMenuToolbar(iframe, subscribeUnsubscribe);
+
+    // initialize toolbuttons
+    for (var tb in phorge_extension.toolbtn) {
+      if (phorge_extension.toolbtn.hasOwnProperty(tb)) {
+        phorge_extension.toolbtn[tb].instance.initialize();
+      }
+    }
+  }
+
+  function setupVersionDropDownInMenuToolbar(iframe, btnLeft) {
+    // generate 'Select Version' button
+    // create the dropdown element
+    const dropdown = document.createElement('div');
+    dropdown.classList.add('toolbtn-version-dropdown');
+
+    // create the dropdown toggle button
+    const toggle = document.createElement('button');
+    toggle.classList.add('toolbtn-version-dropdown-toggle');
+    toggle.textContent = 'Select Version';
+    toggle.title = 'View previous versions';
+    dropdown.appendChild(toggle);
+
+    const iframeContent = document.querySelector('iframe').contentDocument;
+    const menubarContainer = iframeContent.querySelector('.geMenubarContainer');
+
+    // create the dropdown menu
+    const menu = document.createElement('div');
+    menu.classList.add('toolbtn-version-dropdown-menu');
+    menubarContainer.parentNode.insertBefore(menu, menubarContainer.nextSibling);
+
+    // create the version list
+    const versionList = document.createElement('ul');
+    versionList.classList.add('toolbtn-version-list');
+
+    menu.appendChild(versionList);
+
+    // create the prev and next buttons
+    const prevBtn = document.createElement('button');
+    prevBtn.classList.add('toolbtn-version-prev-btn');
+    prevBtn.textContent = '<';
+
+    const nextBtn = document.createElement('button');
+    nextBtn.classList.add('toolbtn-version-next-btn');
+    nextBtn.textContent = '>';
+
+    menu.appendChild(prevBtn);
+    menu.appendChild(nextBtn);
+
+    // place dropdown next to button on the left
+    btnLeft.parentNode.insertBefore(dropdown, btnLeft.nextSibling);
+
+    return dropdown;
+  }
+
+  function setupSubscriptionButtonInMenuToolbar(iframe, btnLeft) {
+    // generate 'Subscribe/Unsubscribe' button
+    // create grouping div element
+    const div = document.createElement('div');
+    div.classList.add('toolbtn-diagram-subscription');
+
+    // create subscribe button
+    const btnSubscribe = document.createElement('button');
+    btnSubscribe.classList.add('subscribe');
+    btnSubscribe.classList.add('eye');
+    btnSubscribe.title = 'Subscribe';
+    div.appendChild(btnSubscribe);
+
+    // create unsubscribe button
+    const btnUnsubscribe = document.createElement('button');
+    btnUnsubscribe.classList.add('unsubscribe');
+    btnUnsubscribe.classList.add('eye');
+    btnUnsubscribe.title = 'Unsubscribe';
+    div.appendChild(btnUnsubscribe);
+
+    // place grouping next to button on the left
+    btnLeft.parentNode.insertBefore(div, btnLeft.nextSibling);
+
+    return div;
+  }
+
+  if (config.loadJsExtensionArgs) {
+    loadJsExtension.apply(undefined, config.loadJsExtensionArgs);
+  }
+
+  window.__diagram = phorge_extension;
+
+});
diff --git a/rsrc/remarkup-image.css b/rsrc/remarkup-image.css
new file mode 100644
--- /dev/null
+++ b/rsrc/remarkup-image.css
@@ -0,0 +1,8 @@
+/**
+ * @provides diagram-remarkup-image-css
+ */
+
+.diagram-container > .diagram-content {
+  max-width: 100%;
+  cursor: pointer;
+}
diff --git a/rsrc/remarkup-image.js b/rsrc/remarkup-image.js
new file mode 100644
--- /dev/null
+++ b/rsrc/remarkup-image.js
@@ -0,0 +1,27 @@
+/**
+ * @provides diagram-remarkup-image-js
+ */
+
+JX.onload(function() {
+
+  var singleClickTimeout = null;
+
+  JX.Stratcom.listen(
+    'click',
+    ['diagram-remarkup-image'],
+    function(evt) {
+      var detail = evt.getRawEvent().detail;
+
+      if (detail === 1) {
+        singleClickTimeout = window.setTimeout(function() {
+          window.open('/diagram/data/' + evt.getTarget().dataset.diagramVersion);
+          singleClickTimeout = null;
+        }, 300);
+      } else if (detail === 2) {
+        window.clearTimeout(singleClickTimeout);
+        window.open('/diagram/DIAG' + evt.getTarget().dataset.diagramId);
+      }
+    }
+  );
+
+});
diff --git a/src/__phutil_library_init__.php b/src/__phutil_library_init__.php
new file mode 100644
--- /dev/null
+++ b/src/__phutil_library_init__.php
@@ -0,0 +1,3 @@
+<?php
+
+phutil_register_library('diagram', __FILE__);
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
new file mode 100644
--- /dev/null
+++ b/src/__phutil_library_map__.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * This file is automatically generated. Use 'arc liberate' to rebuild it
+ *
+ * @generated
+ * @phutil-library-version 2
+ */
+phutil_register_library_map(array(
+  '__library_version__' => 2,
+  'class' => array(
+    'Diagram' => 'storage/Diagram.php',
+    'DiagramApplication' => 'application/DiagramApplication.php',
+    'DiagramCelerityResources' => 'celerity/DiagramCelerityResources.php',
+    'DiagramContentTransaction' => 'xaction/DiagramContentTransaction.php',
+    'DiagramController' => 'controller/DiagramController.php',
+    'DiagramDAO' => 'storage/DiagramDAO.php',
+    'DiagramPHIDType' => 'phid/DiagramPHIDType.php',
+    'DiagramPatchList' => 'storage/patch/DiagramPatchList.php',
+    'DiagramReplyHandler' => 'mail/DiagramReplyHandler.php',
+    'DiagramSchemaSpec' => 'storage/DiagramSchemaSpec.php',
+    'DiagramSearchConduitAPIMethod' => 'conduit/DiagramSearchConduitAPIMethod.php',
+    'DiagramTransaction' => 'storage/DiagramTransaction.php',
+    'DiagramTransactionEditor' => 'editor/DiagramTransactionEditor.php',
+    'DiagramTransactionType' => 'xaction/DiagramTransactionType.php',
+    'DiagramUploadConduitAPIMethod' => 'conduit/DiagramUploadConduitAPIMethod.php',
+    'DiagramVersion' => 'storage/DiagramVersion.php',
+    'PhabricatorDiagramQuery' => 'query/PhabricatorDiagramQuery.php',
+    'PhabricatorDiagramTransactionQuery' => 'query/PhabricatorDiagramTransactionQuery.php',
+    'PhabricatorDiagramVersionQuery' => 'query/PhabricatorDiagramVersionQuery.php',
+    'PhabricatorRemarkupDiagramRule' => 'remarkup/PhabricatorRemarkupDiagramRule.php',
+    'PlainHtmlWebpageResponse' => 'response/PlainHtmlWebpageResponse.php',
+  ),
+  'function' => array(),
+  'xmap' => array(
+    'Diagram' => array(
+      'DiagramDAO',
+      'PhabricatorApplicationTransactionInterface',
+      'PhabricatorDestructibleInterface',
+      'PhabricatorPolicyInterface',
+      'PhabricatorSubscribableInterface',
+    ),
+    'DiagramApplication' => 'PhabricatorApplication',
+    'DiagramCelerityResources' => 'CelerityResourcesOnDisk',
+    'DiagramContentTransaction' => 'DiagramTransactionType',
+    'DiagramController' => 'PhabricatorController',
+    'DiagramDAO' => 'PhabricatorLiskDAO',
+    'DiagramPHIDType' => 'PhabricatorPHIDType',
+    'DiagramPatchList' => 'PhabricatorSQLPatchList',
+    'DiagramReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
+    'DiagramSchemaSpec' => 'PhabricatorConfigSchemaSpec',
+    'DiagramSearchConduitAPIMethod' => 'ConduitAPIMethod',
+    'DiagramTransaction' => 'PhabricatorModularTransaction',
+    'DiagramTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
+    'DiagramTransactionType' => 'PhabricatorModularTransactionType',
+    'DiagramUploadConduitAPIMethod' => 'ConduitAPIMethod',
+    'DiagramVersion' => array(
+      'DiagramDAO',
+      'PhabricatorDestructibleInterface',
+      'PhabricatorPolicyInterface',
+    ),
+    'PhabricatorDiagramQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+    'PhabricatorDiagramTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+    'PhabricatorDiagramVersionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+    'PhabricatorRemarkupDiagramRule' => 'PhabricatorObjectRemarkupRule',
+    'PlainHtmlWebpageResponse' => 'AphrontHTMLResponse',
+  ),
+));
diff --git a/src/application/DiagramApplication.php b/src/application/DiagramApplication.php
--- a/src/application/DiagramApplication.php
+++ b/src/application/DiagramApplication.php
@@ -35,33 +35,33 @@
         // url to image data
         "data/(?P<diagramphid>PHID-DGVN-[a-z0-9]{20})"
         . "(?:/(?P<version>[^/]+))?"
-        . "(?:/|\?|$).*" 
+        . "(?:/|\?|$).*"
         => "DiagramController",
-  
+
         // version info requests
         "version/DIAG(?P<versioninfodiagram>(\d+))"
         . "/(?P<versioninfopage>(\d+))"
-        . "(?:/|\?|$).*" 
+        . "(?:/|\?|$).*"
         => "DiagramController",
-  
+
         // draw.io iframe urls
         "(?:(?P<versioneddiagramid>DIAG(\d+))/(?P<version>\d+)/?)"
         . "(?P<route>iframe)"
-        . "(?:/|\?|$).*" 
+        . "(?:/|\?|$).*"
         => "DiagramController",
-  
+
         // draw.io iframe urls
         "(?:(?P<diagramid>DIAG(\d+))/?)?"
         . "(?P<route>iframe)"
-        . "(?:/|\?|$).*" 
+        . "(?:/|\?|$).*"
         => "DiagramController",
-  
+
         // url with diagram id and version in it
         "(?P<versioneddiagramid>DIAG(\d+))"
         . "/(?P<version>\d+)"
-        . "(?:/|\?|$).*" 
+        . "(?:/|\?|$).*"
         => "DiagramController",
-  
+
         // url with diagram id in it
         "(?P<diagramid>DIAG(\d+))"
         . "(?:/|\?|$).*"
@@ -72,18 +72,10 @@
         . "(?P<subscriptionphid>[^/]+)/"
         => 'DiagramController',
 
-        // url for loading/initializing diagram content
-        "(?P<route>loadJsExtension)/"
-        . "(/?(?P<loadDiagramName>[^/]*))?"
-        . "(/?(?P<loadDiagramPHID>[^/]*))?"
-        . "(/?(?P<loadDiagramVersion>[^/]*))?"
-        . ".*"
-        => 'DiagramController',
-
         // other urls
         ".*"
         => "DiagramController",
       )
     );
   }
-}
\ No newline at end of file
+}
diff --git a/src/celerity/DiagramCelerityResources.php b/src/celerity/DiagramCelerityResources.php
new file mode 100644
--- /dev/null
+++ b/src/celerity/DiagramCelerityResources.php
@@ -0,0 +1,15 @@
+<?php
+
+final class DiagramCelerityResources extends CelerityResourcesOnDisk {
+  public function getName() {
+    return 'diagram-resources';
+  }
+
+  public function getPathToResources() {
+    return phutil_get_library_root('diagram').'/../rsrc';
+  }
+
+  public function getPathToMap() {
+    return phutil_get_library_root('diagram').'/celerity/map.php';
+  }
+}
diff --git a/src/celerity/map.php b/src/celerity/map.php
new file mode 100644
--- /dev/null
+++ b/src/celerity/map.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * This file is automatically generated. Use 'bin/celerity map' to rebuild it.
+ *
+ * @generated
+ */
+return array(
+  'names' => array(
+    'iframe-toolbtn.css' => '35ad6f49',
+    'iframe-toolbtn.js' => '26d75a35',
+    'phorge_extension.js' => 'd9f07830',
+    'remarkup-image.css' => '42b46bf1',
+    'remarkup-image.js' => '64e7e9e1',
+  ),
+  'symbols' => array(
+    'diagram-css-iframe-toolbtn' => '35ad6f49',
+    'diagram-js-iframe-toolbtn' => '26d75a35',
+    'diagram-remarkup-image-css' => '42b46bf1',
+    'diagram-remarkup-image-js' => '64e7e9e1',
+    'javelin-behavior-diagram-extension' => 'd9f07830',
+  ),
+  'requires' => array(),
+  'packages' => array(),
+);
diff --git a/src/controller/DiagramController.php b/src/controller/DiagramController.php
--- a/src/controller/DiagramController.php
+++ b/src/controller/DiagramController.php
@@ -21,9 +21,6 @@
     $route = $request->getURIData('route');
     $versioninfoDiagramID = $request->getURIData('versioninfodiagram');
     $versioninfoPage = $request->getURIData('versioninfopage');
-    $loadDiagramName = $request->getURIData('loadDiagramName');
-    $loadDiagramPHID = $request->getURIData('loadDiagramPHID');
-    $loadDiagramVersion = $request->getURIData('loadDiagramVersion');
 
     if (isset($diagramphid) && !empty(trim($diagramphid))) {
       // return PNG image data
@@ -103,39 +100,6 @@
       }
     }
 
-    if ($route == 'loadJsExtension') {
-      $response = new AphrontFileResponse();
-      $response->setMimeType('application/javascript');
-
-      $base64_data = "";
-      if (isset($loadDiagramName) && !empty(trim($loadDiagramName))) {
-        $diagram_id = (int) substr($loadDiagramName, strlen("DIAG"));
-        if (isset($loadDiagramVersion) && !empty(trim($loadDiagramVersion))) {
-          $diagramVersion = id(new DiagramVersion())->loadByVersionedDiagramID(
-            $diagram_id,
-            $loadDiagramVersion);
-        } else {
-          $diagramVersion = id(new DiagramVersion())->loadLatestByDiagramID(
-            $diagram_id);
-        }
-        if ($diagramVersion) {
-          $data = $diagramVersion->getData();
-          $base64_data = base64_encode($data);
-        }
-      }
-
-      $response->setContent('loadJsExtension("'
-          . $loadDiagramName
-          . '", "'
-          . $loadDiagramPHID
-          . '", "'
-          . $loadDiagramVersion
-          . '", "'
-          . $base64_data
-          . '");');
-      return $response;
-    }
-
     $root = '';
     $file = rtrim($request->getPath(), '/');
     $root = dirname(phutil_get_library_root('diagram'));
@@ -150,11 +114,11 @@
       if ($versioneddiagramid != null && $version != null) {
         $file = preg_replace(
           "/^\/diagram\/$versioneddiagramid\/$version" ."iframe\//",
-          "data/drawio/src/main/webapp/",
+          "drawio/src/main/webapp/",
           $file);
       } else {
         $file = preg_replace("/^\/diagram\/($diagramid\/?)?iframe\//",
-          "data/drawio/src/main/webapp/",
+          "drawio/src/main/webapp/",
           $file);
       }
     } else {
@@ -179,20 +143,6 @@
       }
     }
 
-    // check if we are trying to load "iframe loader" files,
-    // if so, correct the path accordingly
-    if ($file == "data/drawio/src/main/webapp/index.html") {
-      return $this->showIframe($request);
-    }
-    else
-    if ($file == "data/drawio/src/main/webapp/iframe.css"
-        || $file == "data/drawio/src/main/webapp/iframe-toolbtn.css"
-        || $file == "data/drawio/src/main/webapp/iframe1.js"
-        || $file == "data/drawio/src/main/webapp/iframe2.js"
-        || $file == "data/drawio/src/main/webapp/iframe-toolbtn.js") {
-        $file = str_replace("drawio/src/main/webapp/", "", $file);
-    }
-
     // determine full path
     $path = $root . '/' . $file;
 
@@ -230,6 +180,7 @@
       switch (pathinfo($file, PATHINFO_EXTENSION)) {
         case 'html':
           $response = id(new PlainHtmlWebpageResponse())
+            ->setDisableContentSecurityPolicy(true)
             ->setFrameable(true)
             ->setContent(file_get_contents($path));
           break;
@@ -265,6 +216,9 @@
       }
       try {
         $response->setContent(file_get_contents($path));
+        $response->setCacheDurationInSeconds(60 * 60 * 24 * 30);
+        $response->setLastModified(time());
+        $response->setCanCDN(true);
       } catch (Exception $e) {
         $response->setContent($route);
       }
@@ -464,13 +418,26 @@
    */
   private function showApplication(
     AphrontRequest $request,
-    string $diagramName = null,
-    string $diagramPHID = null,
-    string $diagramVersion = null,
-    string $diagramBase64 = null
+    string $diagramName = '',
+    string $diagramPHID = '',
+    string $diagramVersion = '',
+    string $base64_data = ''
   ) {
     $applicationUrl = "/" . explode("/", $request->getPath())[1];
 
+    $behaviorConfig = array();
+    $behaviorConfig['loadJsExtensionArgs'] = array(
+      $diagramName,
+      $diagramPHID,
+      $diagramVersion,
+      $base64_data
+    );
+    $behaviorConfig['toolbarCss'] = celerity_get_resource_uri('/iframe-toolbtn.css', 'diagram-resources');
+    $behaviorConfig['toolbarJs'] = celerity_get_resource_uri('/iframe-toolbtn.js', 'diagram-resources');
+
+    require_celerity_resource('javelin-behavior');
+    Javelin::initBehavior('diagram-extension', $behaviorConfig, 'diagram-resources');
+
     $content = phutil_tag(
       'div',
       array(),
@@ -489,28 +456,6 @@
                 'class' => 'drawio',
               )),
           )),
-        phutil_tag(
-          'script',
-          array(
-            'src' => 'phorge_extension.js',
-            'defer' => true
-          ),
-          ''
-        ),
-        phutil_tag(
-          'script',
-          array(
-            'src' => $applicationUrl . '/loadJsExtension/'
-                   . $diagramName
-                   . '/'
-                   . $diagramPHID
-                   . '/'
-                   . $diagramVersion
-                   . '/',
-            'defer' => true
-          ),
-          ''
-        ),
         phutil_tag(
           'div',
           array(
@@ -585,363 +530,4 @@
     return $response;
   }
 
-  /**
-   * Shows the internal draw.io application
-   */
-  private function showIframe(
-    AphrontRequest $request
-  ) {
-    $content = phutil_tag(
-        'html',
-        array(),
-        array(
-          phutil_tag(
-            'head',
-            array(),
-            array(
-              phutil_tag(
-                'title',
-                array(),
-                'Flowchart Maker & Online Diagram Software'
-              ),
-              phutil_tag(
-                'meta',
-                array(
-                  'charset' => 'utf-8'
-                )
-              ),
-              phutil_tag(
-                'meta',
-                array(
-                  'http-equiv' => 'content-type',
-                  'content' => 'text/html; charset=utf-8'
-                )
-              ),
-              phutil_tag(
-                'meta',
-                array(
-                  'name' => 'viewport',
-                  'content' => 'width=device-width, initial-scale=1.0, '
-                             . 'maximum-scale=1.0, user-scalable=no'
-                )
-              ),
-              phutil_tag(
-                'meta',
-                array(
-                  'name' => 'mobile-web-app-capable',
-                  'content' => 'yes'
-                )
-              ),
-              phutil_tag(
-                'link',
-                array(
-                  'rel' => 'stylesheet',
-                  'type' => 'text/css',
-                  'href' => 'iframe.css'
-                )
-              ),
-              phutil_tag(
-                'link',
-                array(
-                  'rel' => 'stylesheet',
-                  'type' => 'text/css',
-                  'href' => 'iframe-toolbtn.css'
-                )
-              ),
-              phutil_tag(
-                'link',
-                array(
-                  'rel' => 'stylesheet',
-                  'type' => 'text/css',
-                  'href' => 'styles/grapheditor.css'
-                )
-              ),
-              phutil_tag(
-                'script',
-                array(
-                  'type' => 'text/javascript',
-                  'src' => 'iframe1.js',
-                  'defer' => true
-                )
-              )
-            )
-          ),
-          phutil_tag(
-            'body',
-            array(
-              'class' => 'geEditor'
-            ),
-            array(
-              phutil_tag(
-                'div',
-                array(
-                  'id' => 'geinfo'
-                ),
-                phutil_tag(
-                  'div',
-                  array(
-                    'class' => 'geBlock'
-                  ),
-                  array(
-                    phutil_tag(
-                      'h1',
-                      array(),
-                      'Flowchart Maker and Online Diagram Software'
-                    ),
-                    phutil_tag(
-                      'p',
-                      array(),
-                      'draw.io is free online diagram software. You can use '
-                      . 'it as a flowchart maker, network diagram software, '
-                      . 'to create UML online, as an ER diagram tool, to '
-                      . 'design database schema, to build BPMN online, as a '
-                      . 'circuit diagram maker, and more. draw.io can import '
-                      . '.vsdx, Gliffy™ and Lucidchart™ files.'
-                    ),
-                    phutil_tag(
-                      'h2',
-                      array(
-                        'id' => 'gestatus'
-                      ),
-                      'Loading...'
-                    ),
-                    phutil_tag(
-                      'div',
-                      array(
-                        'class' => 'init-spinner'
-                      ),
-                      phutil_tag(
-                        'div',
-                        array(
-                          'class' => 'content'
-                        ),
-                        array(
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(0deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -1s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(27deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.923077ss;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(55deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.846154s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(83deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.769231s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(110deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.692308s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(138deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.615385s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(166deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.538462s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(193deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.461538s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(221deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.384615s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(249deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.307692s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(276deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.230769s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(304deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.153846s;'
-                              )
-                            )
-                          ),
-                          phutil_tag(
-                            'div',
-                            array(
-                              'class' => 'spike',
-                              'style' => 'transform: rotate(332deg)'
-                                                 . ' translatex(13px);'
-                            ),
-                            phutil_tag(
-                              'div',
-                              array(
-                                'class' => 'animator',
-                                'style' => 'animation-delay: -0.0769231s;'
-                              )
-                            )
-                          )
-                        )
-                      )
-                    )
-                  )
-                )
-              ),
-              phutil_tag(
-                'script',
-                array(
-                  'type' => 'text/javascript',
-                  'src' => 'iframe2.js',
-                  'defer' => true
-                )
-              ),
-              phutil_tag(
-                'script',
-                array(
-                  'type' => 'text/javascript',
-                  'src' => 'iframe-toolbtn.js',
-                  'defer' => true
-                )
-              )
-            )
-          )
-        )
-      );
-
-    $response = id(new PlainHtmlWebpageResponse())
-      ->setFrameable(true)
-      ->setContent($content);
-
-    return $response;
-  }
 }
diff --git a/src/remarkup/PhabricatorRemarkupDiagramRule.php b/src/remarkup/PhabricatorRemarkupDiagramRule.php
--- a/src/remarkup/PhabricatorRemarkupDiagramRule.php
+++ b/src/remarkup/PhabricatorRemarkupDiagramRule.php
@@ -1,5 +1,8 @@
 <?php
 
+require_celerity_resource('diagram-remarkup-image-js', 'diagram-resources');
+require_celerity_resource('diagram-remarkup-image-css', 'diagram-resources');
+
 final class PhabricatorRemarkupDiagramRule
   extends PhabricatorObjectRemarkupRule {
 
@@ -34,9 +37,6 @@
       $params = array();
     }
 
-    // Get the file PHID for the Diagram object.
-    $file_phid = $diagram->getPHID();
-
     // Generate the appropriate HTML using the data from the Diagram and
     // file objects.
     $style = '';
@@ -88,11 +88,12 @@
         array(
           'style' => $style,
           'class' => $class,
-          'src' => $diagram->getViewURI(),
+          'src' => 'data:image/png;base64,' . $diagram->getBase64Data(),
           'alt' => $alt,
-          'ondblclick' => 'window.open("/diagram/DIAG'
-                        . $diagram->getDiagramID()
-                        . '", "_blank")',
+          'data-sigil' => 'diagram-remarkup-image',
+          'data-diagram-version' => $diagram->getPHID(),
+          'data-diagram-id' => $diagram->getDiagramID(),
+          'title' => 'Double click to edit...'
         )
       )
     );