my eye

Remove javascript build pipeline

Committed 729f55

index 0000000..45cecc0
--- /dev/null

+env:
+  browser: true
+  es2021: true
+extends: standard
+parserOptions:
+  ecmaVersion: latest
+  sourceType: module
+rules: {}

index 2a075fb..0000000
--- a/package.json

-{
-  "name": "web.js",
-  "version": "0.0.1",
-  "description": "social web scaffolding",
-  "scripts": {
-    "dev": "npx webpack --watch --mode=development",
-    "prod": "npx webpack --mode=production"
-  },
-  "homepage": "https://ragt.ag/code/projects/webint",
-  "repository": {
-    "type": "git",
-    "url": "https://ragt.ag/code/projects/webint.git"
-  },
-  "bugs": {
-    "url": "https://ragt.ag/code/projects/webint/issues"
-  },
-  "keywords": [
-    "web"
-  ],
-  "author": "Angelo Gladding <angelo@ragt.ag>",
-  "license": "AGPL-3.0-or-later",
-  "files": [
-    "dist/web.js"
-  ],
-  "dependencies": {
-    "@babel/register": "^7.17.7",
-    "babel-loader": "^8.2.4",
-    "body-parser": "^1.20.0",
-    "browserify-zlib": "^0.2.0",
-    "crypto-js": "^4.1.1",
-    "js-cookie": "^3.0.1",
-    "luxon": "^2.3.1",
-    "stream-browserify": "^3.0.0",
-    "ts-node": "^10.7.0",
-    "webextension-polyfill": "^0.8.0"
-  },
-  "devDependencies": {
-    "@typescript-eslint/eslint-plugin": "^4.27.0",
-    "@typescript-eslint/parser": "^4.27.0",
-    "asciinema-player": "^3.2.0",
-    "eslint": "^7.29.0",
-    "eslint-config-standard": "^16.0.3",
-    "eslint-plugin-import": "^2.23.4",
-    "eslint-plugin-node": "^11.1.0",
-    "eslint-plugin-promise": "^5.1.0",
-    "source-map-loader": "^3.0.1",
-    "ts-loader": "^9.2.3",
-    "webpack": "^5.39.1",
-    "webpack-bundle-analyzer": "^4.8.0",
-    "webpack-cli": "^4.10.0"
-  }
-}

index 333adc0..0000000
--- a/tsconfig.json

-{
-  "compilerOptions": {
-    "moduleResolution": "node",
-    "outDir": "./dist",
-    "allowJs": true,
-    "target": "esnext",
-    "module": "esnext",
-    "resolveJsonModule": true,
-    "allowSyntheticDefaultImports": true,
-    "esModuleInterop": true
-  },
-  "files": ["web.ts/index.ts"]
-}

index bc2e682..0000000
--- a/web.ts/index.ts

-// function bindWebActions() {
-//     $$("indie-action").each(function() {
-//         this.onclick = function(e) {
-//             var action_link = this.querySelector("a");
-//             // TODO action_link.attr("class", "fa fa-spinner fa-spin");
-//
-//             var action_do = this.getAttribute("do");
-//             var action_with = this.getAttribute("with");
-//
-//             // protocolCheck("web+action://" + action_do + "?url=" + action_with,
-//             // setTimeout(function() {
-//             //     var url = "//canopy.garden/?origin=" + window.location.href +
-//             //               "do=" + action_do + "&with=" + action_with;
-//             //     var html = `<!--p>Your device does not support web
-//             //                 actions<br><em><strong>or</strong></em><br>
-//             //                 You have not yet paired your website with
-//             //                 your browser</p>
-//             //                 <hr-->
-//             //                 <p>If you have a website that supports web
-//             //                 actions enter it here:</p>
-//             //                 <form id=action-handler action=/actions-finder>
-//             //                 <label>Your Website
-//             //                 <div class=bounding><input type=text
-//             //                 name=url></div></label>
-//             //                 <input type=hidden name=do value="${action_do}">
-//             //                 <input type=hidden name=with value="${action_with}">
-//             //                 <p><small>Target:
-//             //                 <code>${action_with}</code></small></p>
-//             //                 <button>${action_do}</button>
-//             //                 </form>
-//             //                 <p>If you do not you can create one <a
-//             //                 href="${url}">here</a>.</p>`;
-//             //     switch (action_do) {
-//             //         case "sign-in":
-//             //             html = html + `<p>If you are the owner of this site,
-//             //                            <a href=/security/identification>sign
-//             //                            in here</a>.</p>`;
-//             //     }
-//             //     html = html + `<p><small><a href=/help#web-actions>Learn
-//             //                    more about web actions</a></small></p>`;
-//             //     $("#webaction_help").innerHTML = html;
-//             //     $("#webaction_help").style.display = "block";
-//             //     $("#blackout").style.display = "block";
-//             //     $("#blackout").onclick = function() {
-//             //         $("#webaction_help").style.display = "none";
-//             //         $("#blackout").style.display = "none";
-//             //     };
-//             // }, 200);
-//
-//             window.location = action_link.getAttribute("href");
-//
-//             e.preventDefault ? e.preventDefault() : e.returnValue = false;
-//         }
-//     });
-// }
-
-// $.load(function () {
-//   bindWebActions()
-//
-//   /*
-//     $$(".pubkey").each(function() {
-//         var armored_pubkey = $(this).text();
-//         if (armored_pubkey) {
-//             var pubkey = get_pubkey(armored_pubkey);
-//             var fingerprint = pubkey.fingerprint.substring(0, 2);
-//             for (i = 2; i < 40; i = i + 2)
-//                 fingerprint = fingerprint + ":" +
-//                               pubkey.fingerprint.substring(i, i + 2);
-//             $(this).after("<code class=fingerprint><span>" +
-//                           fingerprint.substr(0, 30) + "</span><span>" +
-//                           fingerprint.substr(30, 60) + "</span></code>");
-//         }
-//     });
-//     */
-//
-//   // var mySVG = document.getElementById("action_like");
-//   // var svgDoc;
-//   // mySVG.addEventListener("load",function() {
-//   //     svgDoc = mySVG.contentDocument;
-//   //     path = svgDoc.querySelector("path");
-//   //     setTimeout(function() {
-//   //         path.setAttribute("fill", "red");
-//   //         mySVG.setAttribute("class", "icon animated pulse");
-//   //     }, 2000);
-//   // }, false);
-//
-//   // $$(".icon").each(function() {
-//   //     var svgDoc;
-//   //     var mySVG = this;
-//   //     mySVG.addEventListener("load", function() {
-//   //         svgDoc = mySVG.contentDocument;
-//   //         // function z() {
-//   //             svgDoc.querySelector("path").setAttribute("fill", "#2aa198");
-//   //             // mySVG.setAttribute("class", "icon animated pulse");
-//   //         // }
-//   //         // setTimeout(z, 2000);
-//   //     }, false);
-//   // });
-//
-//   // $("a.quote").click(function() {
-//   //     window.location = "web+action://quote=?url=" + window.location +
-//   //                       "&quote=" + window.getSelection().toString();
-//   //     return false
-//   // });
-//
-//   // $$("#search").submit(function() {
-//   //     $.ajax({method: "GET",
-//   //              url: "/search?query=" +
-//   //                   $(this).find("input[name=query]").val()})
-//   //         .done(function(msg) { $("#resource_preview").html(msg); });
-//   //     return false
-//   // });
-// })
-
-// function get_pubkey (armored_pubkey) {
-//   /*
-//     handle displaying of fingerprints
-//
-//     */
-//   let foundKeys = openpgp.key.readArmored(armored_pubkey).keys
-//   if (!foundKeys || foundKeys.length !== 1) {
-//     throw new Error('No key found or more than one key')
-//   }
-//   const pubKey = foundKeys[0]
-//   foundKeys = null
-//   return pubKey.primaryKey
-// }
-
-// activate fast AES-GCM mode (not yet OpenPGP standard)
-// openpgp.config.aead_protect = true;  // TODO move to after openpgp load
-
-// function sign (payload, handler) {
-//   // XXX var pubkey = localStorage["pubkey"];
-//   // XXX var privkey = localStorage["privkey"];
-//   // XXX var passphrase = "";  // window.prompt("please enter the pass phrase");
-//
-//   // XXX // console.log(openpgp.key.readArmored(privkey));
-//   // XXX // var privKeyObj = openpgp.key.readArmored(privkey).keys[0];
-//   // XXX // privKeyObj.decrypt(passphrase);
-//
-//   // XXX // options = {
-//   // XXX //     message: openpgp.cleartext.fromText('Hello, World!'),
-//   // XXX //     privateKeys: [privKeyObj]
-//   // XXX // };
-//
-//   // XXX // openpgp.sign(options).then(function(signed) {
-//   // XXX //     cleartext = signed.data;
-//   // XXX //     console.log(cleartext);
-//   // XXX // });
-//
-//   // XXX openpgp.key.readArmored(privkey).then(function(privKeyObj) {
-//   // XXX     // XXX var privKeyObj = z.keys[0];
-//   // XXX     // XXX privKeyObj.decrypt(passphrase);
-//   // XXX     // XXX var options = {data: payload, privateKeys: privKeyObj};
-//   // XXX     var options = {message: openpgp.cleartext.fromText("helloworld"),
-//   // XXX                    privateKeys: [privKeyObj]}
-//   // XXX     openpgp.sign(options).then(handler);
-//   // XXX });
-// }
-
-// function sign_form (form, data, submission_handler) {
-//   const button = form.find('button')
-//   button.prop('disabled', true)
-//   const timestamp = Date.now()
-//   form.append("<input type=hidden name=published value='" +
-//                 timestamp + "'>")
-//   data.published = timestamp
-//   const payload = JSON.stringify(data, Object.keys(data).sort(), '  ')
-//   sign(payload, function (signed) {
-//     form.append('<input id=signature type=hidden name=signature>')
-//     $('#signature').val(signed.data)
-//     // XXX form.submit();
-//     submission_handler()
-//     button.prop('disabled', false)
-//   })
-// }
-
-// function getTimeSlug (when) {
-//   const centiseconds = (((when.hours() * 3600) +
-//                          (when.minutes() * 60) +
-//                          when.seconds()) * 100) +
-//                        Math.round(when.milliseconds() / 10)
-//   return when.format('Y/MM/DD/') + num_to_sxgf(centiseconds, 4)
-// }
-
-// function getTextSlug (words) {
-//   let padding = ''
-//   if (words.slice(-1) == ' ') { padding = '_' }
-//   return words.toLowerCase().split(punct_re).join('_')
-//     .replace(/_$$/gm, '') + padding
-// }
-
-// function previewImage(file, preview_container) {
-//     return false
-//     var reader = new FileReader();
-//     reader.onload = function (e) {
-//         preview_container.attr("src", e.target.result);
-//     }
-//     reader.readAsDataURL(file);
-//
-//     // var data = new FormData();
-//     // data.append("file-0", file);
-//     // $.ajax({method: "POST",
-//     //         url: "/editor/media",
-//     //         contentType: "multipart/form-data",
-//     //         data: data
-//     //        }).done(function(msg) {
-//     //                    console.log("repsonse");
-//     //                    console.log(msg);
-//     //                    var body = msg["content"];
-//     //                    preview_container.html(body);
-//     //                });
-// }
-
-// function previewResource (url, handler) {
-//   if (url == '') {
-//     // preview_container.innerHTML = "";
-//     return
-//   }
-//
-//   const xhr = new XMLHttpRequest()
-//   xhr.open('GET', '/editor/preview/microformats?url=' +
-//                     encodeURIComponent(url))
-//   xhr.onload = function () {
-//     if (xhr.status === 200) {
-//       const response = JSON.parse(xhr.responseText)
-//       // var entry = response["entry"];
-//       // XXX console.log(response);
-//       handler(response)
-//       // var body = "";
-//       // if ("profile" in response) {
-//       //     // asd
-//       // } else if (entry) {
-//       //     if ("name" in entry)
-//       //         body = "unknown type";
-//       //     else if ("photo" in entry)
-//       //         body = "Photo:<br><img src=" + entry["photo"] + ">";
-//       //     else
-//       //         body = "Note:<br>" + entry["content"];
-//       // }
-//       // preview_container.innerHTML = body;
-//     } else { console.log('request failed: ' + xhr.status) }
-//   }
-//   xhr.send()
-// }
-
-// $(function() {
-//     var current_body = "";
-//     function setTimer() {
-//         setTimeout(function() {
-//             var new_body = $("#body").val();
-//             if (new_body != current_body) {
-//                 $.ajax({method: "POST",
-//                          url: "/content/editor/preview",
-//                          data: {content: new_body}
-//                         }).done(function(msg) {
-//                                     $("#body_readability").html(msg["readability"]);
-//                                     $("#body_preview").html(msg["content"]);
-//                                 });
-//                 current_body = new_body;
-//             }
-//             setTimer();
-//         }, 5000);
-//     };
-//     setTimer();
-// });
-
-// const socket_origin = (window.location.protocol == 'http:' ? 'ws' : 'wss') +
-//                       '://' + window.location.host + '/'
-
-//   followers (...channel) {
-//     let requestUrl = this.endpoint + 'action=follow'
-//     if (channel.length) {
-//       requestUrl += `&channel=${channel[0]}`
-//     }
-//     return fetch(requestUrl).then(response => {
-//       if (response.status === 200 || response.status === 201) {
-//         return response.json().then(data => {
-//           return data
-//         })
-//       }
-//     })
-//   }
-//
-//   follow (url, ...channel) {
-//     let body = `action=follow&url=${url}`
-//     if (channel.length) {
-//       body += `&channel=${channel[0]}`
-//     }
-//     fetch(this.endpoint, {
-//       method: 'POST',
-//       headers: { 'content-type': 'application/x-www-form-urlencoded' },
-//       body: body
-//     }).then(response => {
-//       if (response.status === 200 || response.status === 201) {
-//         return response.json().then(data => {
-//           return data
-//         })
-//       }
-//     })
-//   }
-//
-//   search (query, ...channel) {
-//     let body = `action=search&query=${query}`
-//     if (channel.length) {
-//       body += `&channel=${channel[0]}`
-//     }
-//     return fetch(this.endpoint, {
-//       method: 'POST',
-//       headers: { 'content-type': 'application/x-www-form-urlencoded' },
-//       body: body
-//     }).then(response => {
-//       if (response.status === 200 || response.status === 201) {
-//         return response.json().then(data => {
-//           return data
-//         })
-//       }
-//     })
-//   }
-
-import * as AsciinemaPlayer from 'asciinema-player'
-export const asciinemaplayer = AsciinemaPlayer
-
-// --- WORKING ---
-// import * as monaco from 'monaco-editor'
-// import { initVimMode } from 'monaco-vim'
-// import * as solarizedDark from './themes/solarized-dark'
-// // import { subscribeDT } from './dt/client'
-//
-// export const diamondMonaco = (url, editorEl, statusEl, connectionEl, versionEl, options, userID, vim) => {
-//   monaco.editor.defineTheme('solarized-dark', {
-//     base: solarizedDark.base,
-//     colors: solarizedDark.colors,
-//     inherit: solarizedDark.inherit,
-//     rules: solarizedDark.rules
-//   })
-//   options.theme = 'solarized-dark'
-//   options.automaticLayout = true
-//   const editor = monaco.editor.create(editorEl, options)
-//   if (vim === true) {
-//     initVimMode(editor, statusEl)
-//   }
-//   editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
-//     alert('saved')
-//   })
-//   editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
-//     alert('publish')
-//   })
-//   editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => {
-//     alert('publish and go live')
-//   })
-//   // subscribeDT(url + '.dt', editor, userID, {
-//   //   setStatus: m => {
-//   //     connectionEl.innerHTML = m
-//   //   },
-//   //   setInfo: m => {
-//   //     versionEl.innerHTML = m
-//   //   }
-//   // })
-//   return editor
-// }
-//
-// --- /WORKING ---
-
-/**
- * JavaScript Client Detection
- * (C) viazenetti GmbH (Christian Ludwig)
- */
-export const getBrowser = () => {
-  const unknown = '-'
-
-  // screen
-  let screenSize = ''
-  if (screen.width) {
-    const width = (screen.width) ? screen.width : ''
-    const height = (screen.height) ? screen.height : ''
-    screenSize += '' + width + ' x ' + height
-  }
-
-  // browser
-  const nVer = navigator.appVersion
-  const nAgt = navigator.userAgent
-  let browser = navigator.appName
-  let version = '' + parseFloat(navigator.appVersion)
-  let majorVersion = parseInt(navigator.appVersion, 10)
-  let nameOffset, verOffset, ix
-
-  // Opera
-  if ((verOffset = nAgt.indexOf('Opera')) !== -1) {
-    browser = 'Opera'
-    version = nAgt.substring(verOffset + 6)
-    if ((verOffset = nAgt.indexOf('Version')) !== -1) {
-      version = nAgt.substring(verOffset + 8)
-    }
-  }
-  // Opera Next
-  if ((verOffset = nAgt.indexOf('OPR')) !== -1) {
-    browser = 'Opera'
-    version = nAgt.substring(verOffset + 4)
-  } else if ((verOffset = nAgt.indexOf('Edge')) !== -1) { // Legacy Edge
-    browser = 'Microsoft Legacy Edge'
-    version = nAgt.substring(verOffset + 5)
-  } else if ((verOffset = nAgt.indexOf('Edg')) !== -1) { // Edge (Chromium)
-    browser = 'Microsoft Edge'
-    version = nAgt.substring(verOffset + 4)
-  } else if ((verOffset = nAgt.indexOf('MSIE')) !== -1) { // MSIE
-    browser = 'Microsoft Internet Explorer'
-    version = nAgt.substring(verOffset + 5)
-  } else if ((verOffset = nAgt.indexOf('Chrome')) !== -1) { // Chrome
-    browser = 'Chrome'
-    version = nAgt.substring(verOffset + 7)
-  } else if ((verOffset = nAgt.indexOf('Safari')) !== -1) { // Safari
-    browser = 'Safari'
-    version = nAgt.substring(verOffset + 7)
-    if ((verOffset = nAgt.indexOf('Version')) !== -1) {
-      version = nAgt.substring(verOffset + 8)
-    }
-  } else if ((verOffset = nAgt.indexOf('Firefox')) !== -1) { // Firefox
-    browser = 'Firefox'
-    version = nAgt.substring(verOffset + 8)
-  } else if (nAgt.indexOf('Trident/') !== -1) { // MSIE 11+
-    browser = 'Microsoft Internet Explorer'
-    version = nAgt.substring(nAgt.indexOf('rv:') + 3)
-  } else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) <
-             (verOffset = nAgt.lastIndexOf('/'))) { // Other browsers
-    browser = nAgt.substring(nameOffset, verOffset)
-    version = nAgt.substring(verOffset + 1)
-    if (browser.toLowerCase() === browser.toUpperCase()) {
-      browser = navigator.appName
-    }
-  }
-  // trim the version string
-  if ((ix = version.indexOf(';')) !== -1) version = version.substring(0, ix)
-  if ((ix = version.indexOf(' ')) !== -1) version = version.substring(0, ix)
-  if ((ix = version.indexOf(')')) !== -1) version = version.substring(0, ix)
-
-  majorVersion = parseInt('' + version, 10)
-  if (isNaN(majorVersion)) {
-    version = '' + parseFloat(navigator.appVersion)
-    majorVersion = parseInt(navigator.appVersion, 10)
-  }
-
-  // mobile version
-  const mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer)
-
-  // cookie
-  let cookieEnabled = !!(navigator.cookieEnabled)
-
-  if (typeof navigator.cookieEnabled === 'undefined' && !cookieEnabled) {
-    document.cookie = 'testcookie'
-    cookieEnabled = (document.cookie.indexOf('testcookie') !== -1)
-  }
-
-  // test
-
-  // system
-  let os = unknown
-  const clientStrings = [
-    { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ },
-    { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ },
-    { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ },
-    { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ },
-    { s: 'Windows Vista', r: /Windows NT 6.0/ },
-    { s: 'Windows Server 2003', r: /Windows NT 5.2/ },
-    { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ },
-    { s: 'Windows 2000', r: /(Windows NT 5.0|Windows 2000)/ },
-    { s: 'Windows ME', r: /(Win 9x 4.90|Windows ME)/ },
-    { s: 'Windows 98', r: /(Windows 98|Win98)/ },
-    { s: 'Windows 95', r: /(Windows 95|Win95|Windows_95)/ },
-    { s: 'Windows NT 4.0', r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ },
-    { s: 'Windows CE', r: /Windows CE/ },
-    { s: 'Windows 3.11', r: /Win16/ },
-    { s: 'Android', r: /Android/ },
-    { s: 'Open BSD', r: /OpenBSD/ },
-    { s: 'Sun OS', r: /SunOS/ },
-    { s: 'Chrome OS', r: /CrOS/ },
-    { s: 'Linux', r: /(Linux|X11(?!.*CrOS))/ },
-    { s: 'iOS', r: /(iPhone|iPad|iPod)/ },
-    { s: 'Mac OS X', r: /Mac OS X/ },
-    { s: 'Mac OS', r: /(Mac OS|MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
-    { s: 'QNX', r: /QNX/ },
-    { s: 'UNIX', r: /UNIX/ },
-    { s: 'BeOS', r: /BeOS/ },
-    { s: 'OS/2', r: /OS\/2/ },
-    { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ }
-  ]
-  for (const id in clientStrings) {
-    const cs = clientStrings[id]
-    if (cs.r.test(nAgt)) {
-      os = cs.s
-      break
-    }
-  }
-
-  let osVersion = unknown
-
-  if (/Windows/.test(os)) {
-    osVersion = /Windows (.*)/.exec(os)[1]
-    os = 'Windows'
-  }
-
-  switch (os) {
-    case 'Mac OS':
-    case 'Mac OS X':
-    case 'Android':
-      osVersion = /(?:Android|Mac OS|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh) ([._d]+)/.exec(nAgt)[1]
-      break
-
-      // TODO case 'iOS':
-      // TODO   osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer)
-      // TODO   osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0)
-      // TODO   break
-  }
-
-  // flash (you'll need to include swfobject)
-  /* script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js" */
-  // TODO var flashVersion = 'no check'
-  // TODO if (typeof swfobject !== 'undefined') {
-  // TODO   const fv = swfobject.getFlashPlayerVersion()
-  // TODO   if (fv.major > 0) {
-  // TODO     flashVersion = fv.major + '.' + fv.minor + ' r' + fv.release
-  // TODO   } else {
-  // TODO     flashVersion = unknown
-  // TODO   }
-  // TODO }
-
-  return {
-    screen: screenSize,
-    browser: browser,
-    browserVersion: version,
-    browserMajorVersion: majorVersion,
-    mobile: mobile,
-    os: os,
-    osVersion: osVersion,
-    cookies: cookieEnabled
-    // TODO flashVersion: flashVersion
-  }
-}
-
-// TODO alert(
-// TODO   'OS: ' + jscd.os + ' ' + jscd.osVersion + '\n' +
-// TODO     'Browser: ' + jscd.browser + ' ' + jscd.browserMajorVersion +
-// TODO       ' (' + jscd.browserVersion + ')\n' +
-// TODO     'Mobile: ' + jscd.mobile + '\n' +
-// TODO     'Flash: ' + jscd.flashVersion + '\n' +
-// TODO     'Cookies: ' + jscd.cookies + '\n' +
-// TODO     'Screen Size: ' + jscd.screen + '\n\n' +
-// TODO     'Full User Agent: ' + navigator.userAgent
-// TODO )
-
-const Cookies = require('js-cookie')
-// TODO const { DateTime } = require('luxon')
-// const { nb60encode, nb60decode } = require('NewMath')
-
-export const cookies = Cookies
-// TODO export const dt = DateTime
-// _.nb60encode = nb60encode
-// _.nb60decode = nb60decode
-
-export const _ = (selector) => {
-  const results = typeof selector === 'string'
-    ? document.querySelectorAll(selector)
-    : [selector]
-  // const results = Array.prototype.slice.call(nodes)
-  const items = {}
-  for (let i = 0; i < results.length; i++) {
-    items[i] = results[i]
-  }
-  items['_'] = _
-  items['el'] = items[0]
-  items['n'] = results.length
-  // items.splice = [].splice() // simulates an array FIXME
-  // items.each = callback => { nodes.forEach(callback, ) }
-  items['each'] = callback => {
-    for (let i = 0; i < results.length; i++) {
-      callback(results[i])
-    }
-  }
-  // for (let i = 0; i < results.length; i++) {
-  //   console.log(results[i])
-  //   results[i].addEventListener('click', () => { console.log('er') })
-  // }
-  // items.click = callback => {
-  //   for (let i = 0; i < results.length; i++) {
-  //     console.log(this.results[i], this)
-  //     this.results[i].addEventListener('click', () => { console.log('er') })
-  //     // callback.bind(nodes[i])
-  //     // callback(nodes[i])
-  //   }
-  // }
-  items['append'] = html => {
-    items['each'](item => item.appendChild(createEl(html)))
-  }
-  items['move'] = (left, top) => {
-    items['el'].style.left = left
-    items['el'].style.top = top
-  }
-  items['click'] = callback => {
-    items['each'](item => {
-      item.addEventListener('click', callback)
-    })
-  }
-  return items
-}
-
-export const createEl = html => {
-  const template = document.createElement('template')
-  template.innerHTML = html.trim()
-  return template.content.firstChild
-}
-
-export const createEls = html => {
-  const template = document.createElement('template')
-  template.innerHTML = html
-  return template.content.childNodes
-}
-
-const loadScripts = []
-const unloadScripts = []
-const executeLoadScripts = () => {
-  loadScripts.forEach(handler => handler())
-  loadScripts.length = 0
-}
-const executeUnloadScripts = () => {
-  unloadScripts.forEach(handler => handler())
-  unloadScripts.length = 0
-}
-document.addEventListener('DOMContentLoaded', () => executeLoadScripts())
-window.addEventListener('beforeunload', () => {
-  executeUnloadScripts()
-})
-export const load = handler => loadScripts.push(handler)
-export const unload = handler => unloadScripts.push(handler)
-
-export const mouseup = handler => document.addEventListener('mouseup', handler)
-export const popstate = handler => window.addEventListener('popstate', handler)
-export const online = handler => window.addEventListener('online', handler)
-export const offline = handler => window.addEventListener('offline', handler)
-export const error = handler => window.addEventListener('error', handler)
-
-export const upgradeTimestamps = () => {
-  // TODO const pageLoad = DateTime.now()
-  // TODO _('time').each(item => {
-  // TODO   item.setAttribute('title', item.innerHTML)
-  // TODO   item.innerHTML = DateTime.fromISO(item.attributes.datetime.value)
-  // TODO     .toRelative({ base: pageLoad })
-  // TODO })
-}
-
-// --------------------------------------------------------------------------
-
-interface Configuration {
-  q?: string
-  'media-endpoint'?: string
-  'syndicate-to'?: string
-  'visibility'?: string
-}
-
-export class MicropubClient {
-  endpoint: string
-  token: string
-  headers: any
-  config: Configuration
-
-  constructor (endpoint, token) {
-    this.endpoint = endpoint
-    this.token = token
-    this.headers = {
-      accept: 'application/json'
-    }
-    if (typeof token !== 'undefined') {
-      this.headers.authorization = `Bearer ${token}`
-    }
-
-    this.getConfig = this.getConfig.bind(this)
-
-    this.create = this.create.bind(this)
-    this.read = this.read.bind(this)
-    this.update = this.update.bind(this)
-    this.delete = this.delete.bind(this)
-
-    this.query = this.query.bind(this)
-    this.upload = this.upload.bind(this)
-  }
-
-  getConfig () {
-    return fetch(this.endpoint + '?q=config', {
-      headers: this.headers
-    }).then(response => {
-      if (response.status === 200 || response.status === 201) {
-        return response.json().then(data => {
-          return data
-        })
-      }
-    })
-  }
-
-  getCategories () {
-    return fetch(this.endpoint + '?q=category', {
-      headers: this.headers
-    }).then(response => {
-      if (response.status === 200 || response.status === 201) {
-        return response.json().then(data => {
-          return data
-        })
-      }
-    })
-  }
-
-  create (type: string, properties: object, visibility?: string) {
-    const headers = this.headers
-    headers['content-type'] = 'application/json'
-    if (typeof visibility === 'undefined') {
-      visibility = 'private'
-    }
-    // TODO properties.visibility = visibility
-    return fetch(this.endpoint, {
-      method: 'POST',
-      headers: headers,
-      body: JSON.stringify({
-        type: [`h-${type}`],
-        properties: properties
-      })
-    }).then(response => {
-      if (response.status === 200 || response.status === 201) {
-        return response.headers.get('location') // permalink
-      }
-    })
-  }
-
-  read (url: string) {
-    const headers = this.headers
-    headers['content-type'] = 'application/json'
-    return fetch(this.endpoint, {
-      method: 'GET',
-      headers: headers
-    }).then(response => {
-      if (response.status === 200 || response.status === 201) {
-        return response.json().then(data => {
-          return data
-        })
-      }
-    })
-  }
-
-  update (url: string, operation: string, properties: object) {
-    const payload = { action: 'update', url: url }
-    payload[operation] = properties
-    // payload[operation][property] = values
-    return fetch(this.endpoint, {
-      method: 'POST',
-      headers: {
-        accept: 'application/json',
-        authorization: `Bearer ${this.token}`,
-        'content-type': 'application/json'
-      },
-      body: JSON.stringify(payload)
-    }).then(response => {
-      if (response.status === 200 || response.status === 201) {
-        console.log('UPDATED!')
-      }
-    })
-  }
-
-  delete (url: string) {
-  }
-
-  query (q: string, args) {
-    return fetch(this.endpoint + `?q=${q}&search=${args}`, {
-      headers: this.headers
-    }).then(response => {
-      if (response.status === 200 || response.status === 201) {
-        return response.json().then(data => {
-          return data
-        })
-      }
-    })
-  }
-
-  upload () {
-  }
-}
-
-/* ************************************************************* */
-
-const appendAfter = (el, elAfter) => {
-  el.parentNode.insertBefore(elAfter, el.nextSibling)
-}
-
-/* ************************************************************* */
-
-window.onpopstate = function(e) {
-    if (e.state === null)
-        return
-    console.log("popping", e.state)
-    updateArticle(window.location, e.state.scroll)
-}
-
-export const updateArticle = (url, scroll) => {
-    // XXX _("#loading")['el'].style.display = "block"
-    const xhr = new XMLHttpRequest()
-    xhr.open("GET", url)
-    xhr.setRequestHeader("X-Chromeless", "1")
-    xhr.onload = function() {
-        executeUnloadScripts()
-
-        const dom = new DOMParser().parseFromString(xhr.responseText, "text/html")
-        console.log(xhr.responseText)
-
-        const newArticle = dom.querySelector("body > article")
-        const currentArticle = _("article#content")['el']
-        currentArticle.height = 0
-        appendAfter(currentArticle, newArticle)
-        currentArticle.remove()
-
-        const newAside = dom.querySelector("body > div")
-        const currentAside = _("aside div.page-related")['el']
-        currentAside.height = 0
-        appendAfter(currentAside, newAside)
-        currentAside.remove()
-
-        document.body.scrollTop = document.documentElement.scrollTop = scroll
-
-        _("article#content a:not(.breakout)")['each'](upgradeLink)
-        _("aside div.page-related a:not(.breakout)")['each'](upgradeLink)
-        _("article#content script")['each'](el => {
-            if (el.src != "")
-                document.getElementsByTagName("head")[0].appendChild(el)
-            else
-                eval(el.innerHTML)
-        });
-        executeLoadScripts()
-        upgradeTimestamps()
-        // TODO bindWebActions()
-
-        const h1 = newArticle.querySelector("h1")
-        let title = ""  // owner
-        if (h1)
-            title = h1.textContent + "\u2009\u2014\u2009" + title
-        window.document.title = title
-        // XXX _("#loading")['el'].style.display = "none"
-    }
-    /* xhr.onprogress = function() {
-        // progress on transfers from the server to the client (downloads)
-        function updateProgress (e) {
-            console.log(e);
-            if (e.lengthComputable) {
-                var percentComplete = e.loaded / e.total * 100;
-                debugSocket.send(percentComplete);
-                // ...
-            } else {
-                // Unable to compute progress information since the total size is unknown
-            }
-        }
-    } */
-    xhr.timeout = 10000
-    xhr.ontimeout = function () {
-        // TODO exponential backoff?
-        console.log(`Request for ${url} timed out. retrying..`)
-        updateArticle(url, scroll)
-    }
-    xhr.send()
-}
-
-// TODO back button when coming back from different origin or same page hash
-
-var WEBACTION = true;
-
-export function upgradeLink(el) {
-  var url = el.href
-  // TODO if (url.indexOf("web+action") == 0) {  // web actions
-  // TODO     el.addEventListener("click", (ev) => {
-  // TODO         if (ev.ctrlKey)
-  // TODO             return
-  // TODO         // ev.preventDefault()
-  // TODO         WEBACTION = true
-  // TODO         return
-  // TODO     });
-  // TODO     return  // use native
-  // TODO }
-  if (url.indexOf(origin) == -1) {  // different origin
-    return  // use native
-  }
-  if (url.indexOf("#") > -1) {  // same origin, contains fragment identifier
-    var url_parts = url.split("#")
-    var current_url_parts = window.location.href.split("#")
-    if (url_parts[0] == current_url_parts[0])  // same page
-      return  // use native
-    // different page
-    // XXX console.log(url_parts, current_url_parts);
-  }
-  el.addEventListener("click", (ev) => {
-    if (ev.ctrlKey)
-      return
-    ev.preventDefault();
-    go(url)
-  })
-}
-
-export function go(url) {
-  if ((url.startsWith("http://") || url.startsWith("https://")) &&
-      !url.startsWith(window.location.origin)) {
-    window.open(url, "_blank")
-  } else {
-    history.replaceState({scroll: window.pageYOffset}, "title", window.location.href)
-    updateArticle(url, 0)
-    history.pushState({scroll: 0}, "title", url)
-  }
-}
diff --git a/web.ts/themes/solarized-dark.ts b/web.ts/themes/solarized-dark.ts
deleted file mode 100644
index 609de1e..0000000
--- a/web.ts/themes/solarized-dark.ts
+++ /dev/null
-export const base = 'vs-dark'
-export const inherit = true
-export const colors = {
-  'editor.foreground': '#839496',
-  'editor.background': '#002B36',
-  'editor.selectionBackground': '#073642',
-  'editor.lineHighlightBackground': '#073642',
-  'editorCursor.foreground': '#819090',
-  'editorWhitespace.foreground': '#073642'
-}
-export const rules = [
-  {
-    background: '002B36',
-    token: ''
-  },
-  {
-    foreground: '586e75',
-    token: 'comment'
-  },
-  {
-    foreground: '2aa198',
-    token: 'string'
-  },
-  {
-    foreground: '586e75',
-    token: 'string'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'string.regexp'
-  },
-  {
-    foreground: 'd33682',
-    token: 'constant.numeric'
-  },
-  {
-    foreground: '268bd2',
-    token: 'variable.language'
-  },
-  {
-    foreground: '268bd2',
-    token: 'variable.other'
-  },
-  {
-    foreground: '859900',
-    token: 'keyword'
-  },
-  {
-    foreground: '859900',
-    token: 'storage'
-  },
-  {
-    foreground: '268bd2',
-    token: 'entity.name.class'
-  },
-  {
-    foreground: '268bd2',
-    token: 'entity.name.type.class'
-  },
-  {
-    foreground: '268bd2',
-    token: 'entity.name.function'
-  },
-  {
-    foreground: '859900',
-    token: 'punctuation.definition.variable'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.section.embedded.begin'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.section.embedded.end'
-  },
-  {
-    foreground: 'b58900',
-    token: 'constant.language'
-  },
-  {
-    foreground: 'b58900',
-    token: 'meta.preprocessor'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'support.function.construct'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'keyword.other.new'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'constant.character'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'constant.other'
-  },
-  {
-    foreground: '268bd2',
-    fontStyle: 'bold',
-    token: 'entity.name.tag'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.tag.html'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.tag.begin'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.tag.end'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'entity.other.attribute-name'
-  },
-  {
-    foreground: '268bd2',
-    token: 'support.function'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.separator.continuation'
-  },
-  {
-    foreground: '859900',
-    token: 'support.type'
-  },
-  {
-    foreground: '859900',
-    token: 'support.class'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'support.type.exception'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.other.special-method'
-  },
-  {
-    foreground: '2aa198',
-    token: 'string.quoted.double'
-  },
-  {
-    foreground: '2aa198',
-    token: 'string.quoted.single'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.string.begin'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.string.end'
-  },
-  {
-    foreground: 'b58900',
-    token: 'entity.name.tag.css'
-  },
-  {
-    foreground: 'b58900',
-    token: 'support.type.property-name.css'
-  },
-  {
-    foreground: 'b58900',
-    token: 'meta.property-name.css'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'source.css'
-  },
-  {
-    foreground: '586e75',
-    token: 'meta.selector.css'
-  },
-  {
-    foreground: '6c71c4',
-    token: 'punctuation.section.property-list.css'
-  },
-  {
-    foreground: '2aa198',
-    token: 'meta.property-value.css constant.numeric.css'
-  },
-  {
-    foreground: '2aa198',
-    token: 'keyword.other.unit.css'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.other.color.rgb-value.css'
-  },
-  {
-    foreground: '2aa198',
-    token: 'meta.property-value.css'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'keyword.other.important.css'
-  },
-  {
-    foreground: '2aa198',
-    token: 'support.constant.color'
-  },
-  {
-    foreground: '859900',
-    token: 'entity.name.tag.css'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.separator.key-value.css'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.terminator.rule.css'
-  },
-  {
-    foreground: '268bd2',
-    token: 'entity.other.attribute-name.class.css'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'entity.other.attribute-name.pseudo-element.css'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'entity.other.attribute-name.pseudo-class.css'
-  },
-  {
-    foreground: '268bd2',
-    token: 'entity.other.attribute-name.id.css'
-  },
-  {
-    foreground: 'b58900',
-    token: 'meta.function.js'
-  },
-  {
-    foreground: 'b58900',
-    token: 'entity.name.function.js'
-  },
-  {
-    foreground: 'b58900',
-    token: 'support.function.dom.js'
-  },
-  {
-    foreground: 'b58900',
-    token: 'text.html.basic source.js.embedded.html'
-  },
-  {
-    foreground: '268bd2',
-    token: 'storage.type.function.js'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.numeric.js'
-  },
-  {
-    foreground: '268bd2',
-    token: 'meta.brace.square.js'
-  },
-  {
-    foreground: '268bd2',
-    token: 'storage.type.js'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'meta.brace.round'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'punctuation.definition.parameters.begin.js'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'punctuation.definition.parameters.end.js'
-  },
-  {
-    foreground: '268bd2',
-    token: 'meta.brace.curly.js'
-  },
-  {
-    foreground: '93a1a1',
-    fontStyle: 'italic',
-    token: 'entity.name.tag.doctype.html'
-  },
-  {
-    foreground: '93a1a1',
-    fontStyle: 'italic',
-    token: 'meta.tag.sgml.html'
-  },
-  {
-    foreground: '93a1a1',
-    fontStyle: 'italic',
-    token: 'string.quoted.double.doctype.identifiers-and-DTDs.html'
-  },
-  {
-    foreground: '839496',
-    fontStyle: 'italic',
-    token: 'comment.block.html'
-  },
-  {
-    fontStyle: 'italic',
-    token: 'entity.name.tag.script.html'
-  },
-  {
-    foreground: '2aa198',
-    token: 'source.css.embedded.html string.quoted.double.html'
-  },
-  {
-    foreground: 'cb4b16',
-    fontStyle: 'bold',
-    token: 'text.html.ruby'
-  },
-  {
-    foreground: '657b83',
-    token: 'text.html.basic meta.tag.other.html'
-  },
-  {
-    foreground: '657b83',
-    token: 'text.html.basic meta.tag.any.html'
-  },
-  {
-    foreground: '657b83',
-    token: 'text.html.basic meta.tag.block.any'
-  },
-  {
-    foreground: '657b83',
-    token: 'text.html.basic meta.tag.inline.any'
-  },
-  {
-    foreground: '657b83',
-    token: 'text.html.basic meta.tag.structure.any.html'
-  },
-  {
-    foreground: '657b83',
-    token: 'text.html.basic source.js.embedded.html'
-  },
-  {
-    foreground: '657b83',
-    token: 'punctuation.separator.key-value.html'
-  },
-  {
-    foreground: '657b83',
-    token: 'text.html.basic entity.other.attribute-name.html'
-  },
-  {
-    foreground: '2aa198',
-    token: 'text.html.basic meta.tag.structure.any.html punctuation.definition.string.begin.html'
-  },
-  {
-    foreground: '2aa198',
-    token: 'punctuation.definition.string.begin.html'
-  },
-  {
-    foreground: '2aa198',
-    token: 'punctuation.definition.string.end.html'
-  },
-  {
-    foreground: '268bd2',
-    fontStyle: 'bold',
-    token: 'entity.name.tag.block.any.html'
-  },
-  {
-    fontStyle: 'italic',
-    token: 'source.css.embedded.html entity.name.tag.style.html'
-  },
-  {
-    foreground: '839496',
-    fontStyle: 'italic',
-    token: 'source.css.embedded.html'
-  },
-  {
-    foreground: '839496',
-    fontStyle: 'italic',
-    token: 'comment.block.html'
-  },
-  {
-    foreground: '268bd2',
-    token: 'punctuation.definition.variable.ruby'
-  },
-  {
-    foreground: '657b83',
-    token: 'meta.function.method.with-arguments.ruby'
-  },
-  {
-    foreground: '2aa198',
-    token: 'variable.language.ruby'
-  },
-  {
-    foreground: '268bd2',
-    token: 'entity.name.function.ruby'
-  },
-  {
-    foreground: '859900',
-    fontStyle: 'bold',
-    token: 'keyword.control.ruby'
-  },
-  {
-    foreground: '859900',
-    fontStyle: 'bold',
-    token: 'keyword.control.def.ruby'
-  },
-  {
-    foreground: '859900',
-    token: 'keyword.control.class.ruby'
-  },
-  {
-    foreground: '859900',
-    token: 'meta.class.ruby'
-  },
-  {
-    foreground: 'b58900',
-    token: 'entity.name.type.class.ruby'
-  },
-  {
-    foreground: '859900',
-    token: 'keyword.control.ruby'
-  },
-  {
-    foreground: 'b58900',
-    token: 'support.class.ruby'
-  },
-  {
-    foreground: '859900',
-    token: 'keyword.other.special-method.ruby'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.language.ruby'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.numeric.ruby'
-  },
-  {
-    foreground: 'b58900',
-    token: 'variable.other.constant.ruby'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.other.symbol.ruby'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.section.embedded.ruby'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.string.begin.ruby'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.string.end.ruby'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.other.special-method.ruby'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.control.import.include.php'
-  },
-  {
-    foreground: '839496',
-    token: 'text.html.ruby meta.tag.inline.any.html'
-  },
-  {
-    foreground: '2aa198',
-    token: 'text.html.ruby punctuation.definition.string.begin'
-  },
-  {
-    foreground: '2aa198',
-    token: 'text.html.ruby punctuation.definition.string.end'
-  },
-  {
-    foreground: '839496',
-    token: 'punctuation.definition.string.begin'
-  },
-  {
-    foreground: '839496',
-    token: 'punctuation.definition.string.end'
-  },
-  {
-    foreground: '839496',
-    token: 'support.class.php'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'keyword.operator.index-start.php'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'keyword.operator.index-end.php'
-  },
-  {
-    foreground: '586e75',
-    token: 'meta.array.php'
-  },
-  {
-    foreground: 'b58900',
-    token: 'meta.array.php support.function.construct.php'
-  },
-  {
-    foreground: 'b58900',
-    token: 'meta.array.empty.php support.function.construct.php'
-  },
-  {
-    foreground: 'b58900',
-    token: 'support.function.construct.php'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.array.begin'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.array.end'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.numeric.php'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.other.new.php'
-  },
-  {
-    foreground: '839496',
-    token: 'keyword.operator.class'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'variable.other.property.php'
-  },
-  {
-    foreground: 'b58900',
-    token: 'storage.modifier.extends.php'
-  },
-  {
-    foreground: 'b58900',
-    token: 'storage.type.class.php'
-  },
-  {
-    foreground: 'b58900',
-    token: 'keyword.operator.class.php'
-  },
-  {
-    foreground: '839496',
-    token: 'punctuation.terminator.expression.php'
-  },
-  {
-    foreground: '586e75',
-    token: 'meta.other.inherited-class.php'
-  },
-  {
-    foreground: '859900',
-    token: 'storage.type.php'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'entity.name.function.php'
-  },
-  {
-    foreground: '859900',
-    token: 'support.function.construct.php'
-  },
-  {
-    foreground: '839496',
-    token: 'entity.name.type.class.php'
-  },
-  {
-    foreground: '839496',
-    token: 'meta.function-call.php'
-  },
-  {
-    foreground: '839496',
-    token: 'meta.function-call.static.php'
-  },
-  {
-    foreground: '839496',
-    token: 'meta.function-call.object.php'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'keyword.other.phpdoc'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'source.php.embedded.block.html'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'storage.type.function.php'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.numeric.c'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'meta.preprocessor.c.include'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'meta.preprocessor.macro.c'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.control.import.define.c'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.control.import.include.c'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'entity.name.function.preprocessor.c'
-  },
-  {
-    foreground: '2aa198',
-    token: 'meta.preprocessor.c.include string.quoted.other.lt-gt.include.c'
-  },
-  {
-    foreground: '2aa198',
-    token: 'meta.preprocessor.c.include punctuation.definition.string.begin.c'
-  },
-  {
-    foreground: '2aa198',
-    token: 'meta.preprocessor.c.include punctuation.definition.string.end.c'
-  },
-  {
-    foreground: '586e75',
-    token: 'support.function.C99.c'
-  },
-  {
-    foreground: '586e75',
-    token: 'support.function.any-method.c'
-  },
-  {
-    foreground: '586e75',
-    token: 'entity.name.function.c'
-  },
-  {
-    foreground: '2aa198',
-    token: 'punctuation.definition.string.begin.c'
-  },
-  {
-    foreground: '2aa198',
-    token: 'punctuation.definition.string.end.c'
-  },
-  {
-    foreground: 'b58900',
-    token: 'storage.type.c'
-  },
-  {
-    foreground: 'e0eddd',
-    background: 'b58900',
-    fontStyle: 'italic',
-    token: 'meta.diff'
-  },
-  {
-    foreground: 'e0eddd',
-    background: 'b58900',
-    fontStyle: 'italic',
-    token: 'meta.diff.header'
-  },
-  {
-    foreground: 'dc322f',
-    background: 'eee8d5',
-    token: 'markup.deleted'
-  },
-  {
-    foreground: 'cb4b16',
-    background: 'eee8d5',
-    token: 'markup.changed'
-  },
-  {
-    foreground: '219186',
-    background: 'eee8d5',
-    token: 'markup.inserted'
-  },
-  {
-    foreground: 'e0eddd',
-    background: 'b58900',
-    token: 'text.html.markdown meta.dummy.line-break'
-  },
-  {
-    foreground: '2aa198',
-    token: 'text.html.markdown markup.raw.inline'
-  },
-  {
-    foreground: '2aa198',
-    token: 'text.restructuredtext markup.raw'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'other.package.exclude'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'other.remove'
-  },
-  {
-    foreground: '2aa198',
-    token: 'other.add'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.section.group.tex'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.arguments.begin.latex'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.arguments.end.latex'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.arguments.latex'
-  },
-  {
-    foreground: 'b58900',
-    token: 'meta.group.braces.tex'
-  },
-  {
-    foreground: 'b58900',
-    token: 'string.other.math.tex'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'variable.parameter.function.latex'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.constant.math.tex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'text.tex.latex constant.other.math.tex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.other.general.math.tex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.other.general.math.tex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'constant.character.math.tex'
-  },
-  {
-    foreground: 'b58900',
-    token: 'string.other.math.tex'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.string.begin.tex'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.string.end.tex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'keyword.control.label.latex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'text.tex.latex constant.other.general.math.tex'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'variable.parameter.definition.label.latex'
-  },
-  {
-    foreground: '859900',
-    token: 'support.function.be.latex'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'support.function.section.latex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'support.function.general.tex'
-  },
-  {
-    fontStyle: 'italic',
-    token: 'punctuation.definition.comment.tex'
-  },
-  {
-    fontStyle: 'italic',
-    token: 'comment.line.percentage.tex'
-  },
-  {
-    foreground: '2aa198',
-    token: 'keyword.control.ref.latex'
-  },
-  {
-    foreground: '586e75',
-    token: 'string.quoted.double.block.python'
-  },
-  {
-    foreground: '859900',
-    token: 'storage.type.class.python'
-  },
-  {
-    foreground: '859900',
-    token: 'storage.type.function.python'
-  },
-  {
-    foreground: '859900',
-    token: 'storage.modifier.global.python'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.control.import.python'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.control.import.from.python'
-  },
-  {
-    foreground: 'b58900',
-    token: 'support.type.exception.python'
-  },
-  {
-    foreground: '859900',
-    token: 'support.function.builtin.shell'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'variable.other.normal.shell'
-  },
-  {
-    foreground: '268bd2',
-    token: 'source.shell'
-  },
-  {
-    foreground: '586e75',
-    token: 'meta.scope.for-in-loop.shell'
-  },
-  {
-    foreground: '586e75',
-    token: 'variable.other.loop.shell'
-  },
-  {
-    foreground: '859900',
-    token: 'punctuation.definition.string.end.shell'
-  },
-  {
-    foreground: '859900',
-    token: 'punctuation.definition.string.begin.shell'
-  },
-  {
-    foreground: '586e75',
-    token: 'meta.scope.case-block.shell'
-  },
-  {
-    foreground: '586e75',
-    token: 'meta.scope.case-body.shell'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.logical-expression.shell'
-  },
-  {
-    fontStyle: 'italic',
-    token: 'comment.line.number-sign.shell'
-  },
-  {
-    foreground: 'cb4b16',
-    token: 'keyword.other.import.java'
-  },
-  {
-    foreground: '586e75',
-    token: 'storage.modifier.import.java'
-  },
-  {
-    foreground: 'b58900',
-    token: 'meta.class.java storage.modifier.java'
-  },
-  {
-    foreground: '586e75',
-    token: 'source.java comment.block'
-  },
-  {
-    foreground: '586e75',
-    token: 'comment.block meta.documentation.tag.param.javadoc keyword.other.documentation.param.javadoc'
-  },
-  {
-    foreground: 'b58900',
-    token: 'punctuation.definition.variable.perl'
-  },
-  {
-    foreground: 'b58900',
-    token: 'variable.other.readwrite.global.perl'
-  },
-  {
-    foreground: 'b58900',
-    token: 'variable.other.predefined.perl'
-  },
-  {
-    foreground: 'b58900',
-    token: 'keyword.operator.comparison.perl'
-  },
-  {
-    foreground: '859900',
-    token: 'support.function.perl'
-  },
-  {
-    foreground: '586e75',
-    fontStyle: 'italic',
-    token: 'comment.line.number-sign.perl'
-  },
-  {
-    foreground: '2aa198',
-    token: 'punctuation.definition.string.begin.perl'
-  },
-  {
-    foreground: '2aa198',
-    token: 'punctuation.definition.string.end.perl'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'constant.character.escape.perl'
-  },
-  {
-    foreground: '268bd2',
-    token: 'markup.heading.markdown'
-  },
-  {
-    foreground: '268bd2',
-    token: 'markup.heading.1.markdown'
-  },
-  {
-    foreground: '268bd2',
-    token: 'markup.heading.2.markdown'
-  },
-  {
-    foreground: '268bd2',
-    token: 'markup.heading.3.markdown'
-  },
-  {
-    foreground: '268bd2',
-    token: 'markup.heading.4.markdown'
-  },
-  {
-    foreground: '268bd2',
-    token: 'markup.heading.5.markdown'
-  },
-  {
-    foreground: '268bd2',
-    token: 'markup.heading.6.markdown'
-  },
-  {
-    foreground: '839496',
-    fontStyle: 'bold',
-    token: 'markup.bold.markdown'
-  },
-  {
-    foreground: '839496',
-    fontStyle: 'italic',
-    token: 'markup.italic.markdown'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.bold.markdown'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.italic.markdown'
-  },
-  {
-    foreground: 'dc322f',
-    token: 'punctuation.definition.raw.markdown'
-  },
-  {
-    foreground: 'b58900',
-    token: 'markup.list.unnumbered.markdown'
-  },
-  {
-    foreground: '859900',
-    token: 'markup.list.numbered.markdown'
-  },
-  {
-    foreground: '2aa198',
-    token: 'markup.raw.block.markdown'
-  },
-  {
-    foreground: '2aa198',
-    token: 'markup.raw.inline.markdown'
-  },
-  {
-    foreground: '6c71c4',
-    token: 'markup.quote.markdown'
-  },
-  {
-    foreground: '6c71c4',
-    token: 'punctuation.definition.blockquote.markdown'
-  },
-  {
-    foreground: 'd33682',
-    token: 'meta.separator.markdown'
-  },
-  {
-    foreground: '586e75',
-    fontStyle: 'italic',
-    token: 'meta.image.inline.markdown'
-  },
-  {
-    foreground: '586e75',
-    fontStyle: 'italic',
-    token: 'markup.underline.link.markdown'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'string.other.link.title.markdown'
-  },
-  {
-    foreground: '93a1a1',
-    token: 'string.other.link.description.markdown'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.link.markdown'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.metadata.markdown'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.string.begin.markdown'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.string.end.markdown'
-  },
-  {
-    foreground: '586e75',
-    token: 'punctuation.definition.constant.markdown'
-  },
-  {
-    foreground: 'eee8d5',
-    background: 'eee8d5',
-    token: 'sublimelinter.notes'
-  },
-  {
-    foreground: '93a1a1',
-    background: '93a1a1',
-    token: 'sublimelinter.outline.illegal'
-  },
-  {
-    background: 'dc322f',
-    token: 'sublimelinter.underline.illegal'
-  },
-  {
-    foreground: '839496',
-    background: '839496',
-    token: 'sublimelinter.outline.warning'
-  },
-  {
-    background: 'b58900',
-    token: 'sublimelinter.underline.warning'
-  },
-  {
-    foreground: '657b83',
-    background: '657b83',
-    token: 'sublimelinter.outline.violation'
-  },
-  {
-    background: 'cb4b16',
-    token: 'sublimelinter.underline.violation'
-  }
-]

--- a/web/framework/static/web.js
+++ b/web/framework/static/web.js

index 03d2ee9..0000000
--- a/webpack.config.js

-const path = require('path')
-// const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
-
-// XXX //Webpack Analyzer
-// XXX const WebpackBundleAnalyzer = require("webpack-bundle-analyzer")
-// XXX   .BundleAnalyzerPlugin;
-
-const webpageConfig = {
-  entry: './web.ts/index.ts',
-  resolve: {
-    extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js']
-  },
-
-  // mode: 'production',
-  // mode: 'development',
-  // devtool: 'inline-source-map',
-
-  node: false,
-  module: {
-    rules: [
-      { test: /\.tsx?$/, loader: 'ts-loader' },
-      // {
-      //   test: /\.css$/,
-      //   use: ['style-loader', 'css-loader']
-      // },
-      // {
-      //   test: /\.ttf$/,
-      //   type: 'asset/resource'
-      // },
-      // {
-      //   test: /\.js$/,
-      //   enforce: 'pre',
-      //   use: ['source-map-loader']
-      // }
-    ]
-  },
-  experiments: {
-    outputModule: true
-  },
-  // stats: {
-  //   errorDetails: true
-  // },
-  // plugins: [new MonacoWebpackPlugin()],
-  // XXX plugins: [new WebpackBundleAnalyzer()],
-  output: {
-    path: path.resolve(__dirname) + '/web/framework/static/',
-    filename: 'web.js',
-    publicPath: '/static/',
-    library: {
-      type: 'module'
-    }
-  },
-  ignoreWarnings: [/Failed to parse source map/]
-}
-
-// const extensionConfig = Object.assign({}, config, {
-//   output: {
-//     path: path.resolve(__dirname),
-//     filename: './dist/web.js',
-//     library: {
-//       name: 'web',
-//       type: 'commonjs-module'
-//     }
-//   }
-// })
-
-module.exports = [webpageConfig]