├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── CODE_OF_CONDUCT.md ├── README.md ├── UNLICENSE ├── demo.html ├── devserver.js ├── icons ├── apple-touch-icon.png ├── big-icon.png ├── favicon-32x32.png ├── favicon.ico ├── mask-icon.svg └── micro-panel.svg ├── manifest.json ├── package-lock.json ├── package.json ├── rollup.config.js └── src ├── indie-action.js ├── micro-panel-action.js ├── micro-panel-all.js ├── micro-panel-editor-entry.js ├── micro-panel-editor.js ├── micro-panel-toolbar.js ├── mp-code-mirror.js └── util.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = tab 8 | indent_size = 2 9 | 10 | [*.json] 11 | indent_style = space 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, 4 | "env": { "browser": true, "es6": true }, 5 | "rules": { 6 | "no-tabs": "off", 7 | "indent": ["error", "tab"], 8 | "comma-dangle": ["error", "only-multiline"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | tmp/ 4 | dist/ 5 | build/ 6 | shrinkwrap.yaml 7 | yarn.lock 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project owner at val@packett.cool. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project owner is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # micro-panel 4 | 5 | micro-panel is a set of [Web Components] made with [Lit] that provide an interface for editing [posts] on your website using the [Micropub] protocol from the [IndieWeb]. 6 | 7 | [Web Components]: https://developers.google.com/web/fundamentals/web-components/ 8 | [Lit]: https://lit.dev/ 9 | [posts]: https://indieweb.org/posts 10 | [Micropub]: https://micropub.net/ 11 | [IndieWeb]: https://indieweb.org/ 12 | 13 | ## Requirements 14 | 15 | To use micro-panel, your website needs to 16 | 17 | - have a Micropub endpoint (duh) that 18 | - supports browser session (cookie) authentication, i.e. you can "just log in" on your website and send micropub requests, without going through OAuth/IndieAuth and stuff (which technically violates [the spec](https://www.w3.org/TR/micropub/#authentication-1) :D) 19 | - supports [source content queries with `?q=source`](https://www.w3.org/TR/micropub/#source-content) 20 | - accepts [JSON requests](https://www.w3.org/TR/micropub/#json-syntax) 21 | - supports update and delete (if you want to be able to do these things, which you probably do) 22 | 23 | ## Installation 24 | 25 | (TODO publish to npm // for now, build with `npm run build`) 26 | 27 | First of all, put the bundled script somewhere on your server. 28 | 29 | Then edit the templates, like in the (hopefully obvious) pseudocode below: 30 | 31 | ### Script loading 32 | 33 | Just do it! 34 | 35 | ```html 36 | ... 37 | {{if logged-in-as-admin}} 38 | 39 | {{else}} 40 | 41 | {{endif}} 42 | 43 | ``` 44 | 45 | Note that micro-panel includes an implementation of the `indie-action` tag from [webactions] to allow you to easily e.g. like your own posts -- or actually not just your own posts if your site acts as a [reader]! 46 | So if you use a "guest" implementation to allow others to easily react to your posts -- such as the implementation from [indieweb-components] that uses [indie-config] -- load that one for non-admin users. 47 | 48 | [webactions]: https://indieweb.org/webactions 49 | [reader]: https://indieweb.org/reader 50 | [indie-config]: https://indieweb.org/indie-config 51 | [indieweb-components]: https://github.com/unrelentingtech/indieweb-components 52 | 53 | ### Editor and toolbar 54 | 55 | Place the `micro-panel-editor` directly into the body. 56 | Beginning, end, doesn't matter -- it's a full screen overlay. 57 | Don't forget to provide your Micropub endpoint's URL! 58 | There's no smart detection, it doesn't look up in various links for simplicity reasons. 59 | 60 | The `micro-panel-toolbar` is what provides the Edit/New post buttons. 61 | It's a normal block element. 62 | You can write custom styles to make it `position:sticky` or whatever. 63 | 64 | ```html 65 | 66 | 67 | 68 | ... 69 | ``` 70 | 71 | #### Media Endpoint 72 | 73 | You can provide a path to your [media endpoint](https://indieweb.org/micropub_media_endpoint) to enable file uploads: 74 | 75 | ```html 76 | 78 | ``` 79 | 80 | If it's a cross-domain endpoint, you have two options: 81 | 82 | - have a `Bearer` cookie accessible to JavaScript, it will be sent as `Authorization: Bearer COOKIE_CONTENT` 83 | - provide a `mediatoken="TOKEN"` attribute as well, it will be sent as `Authorization: MediaToken TOKEN` 84 | 85 | And of course, [CORS](https://enable-cors.org) should be configured in both cases: 86 | 87 | ``` 88 | Access-Control-Allow-Origin: * 89 | Access-Control-Allow-Headers: Authorization 90 | ``` 91 | 92 | If the endpoint is on the same domain, you don't have to care about any of this :) 93 | 94 | #### Media Objects 95 | 96 | micro-panel supports JSON objects for photos like this one: 97 | 98 | ```json 99 | { 100 | "aperture": 10, 101 | "focal_length": 27, 102 | "geo": null, 103 | "height": 2916, 104 | "iso": 100, 105 | "palette": [ 106 | { "b": 106, "g": 89, "r": 58 }, 107 | { "b": 198, "g": 201, "r": 201 }, 108 | { "b": 140, "g": 143, "r": 146 }, 109 | { "b": 25, "g": 23, "r": 2 }, 110 | { "b": 41, "g": 47, "r": 52 }, 111 | { "b": 181, "g": 149, "r": 101 }, 112 | { "b": 191, "g": 183, "r": 159 }, 113 | { "b": 153, "g": 141, "r": 113 }, 114 | { "b": 128, "g": 140, "r": 172 } 115 | ], 116 | "shutter_speed": [ 1, 320 ], 117 | "source": [ 118 | { 119 | "original": false, 120 | "srcset": [ 121 | { 122 | "src": "https://dl.unrelenting.technology/4a0c45a0ed40_img-7081.3000.jpg", 123 | "width": 3000 124 | }, 125 | { 126 | "src": "https://dl.unrelenting.technology/4a0c45a0ed40_img-7081.2000.jpg", 127 | "width": 2000 128 | }, 129 | { 130 | "src": "https://dl.unrelenting.technology/4a0c45a0ed40_img-7081.1000.jpg", 131 | "width": 1000 132 | } 133 | ], 134 | "type": "image/jpeg" 135 | }, 136 | { 137 | "original": false, 138 | "srcset": [ 139 | { 140 | "src": "https://dl.unrelenting.technology/4a0c45a0ed40_img-7081.3000.webp", 141 | "width": 3000 142 | }, 143 | { 144 | "src": "https://dl.unrelenting.technology/4a0c45a0ed40_img-7081.2000.webp", 145 | "width": 2000 146 | }, 147 | { 148 | "src": "https://dl.unrelenting.technology/4a0c45a0ed40_img-7081.1000.webp", 149 | "width": 1000 150 | } 151 | ], 152 | "type": "image/webp" 153 | }, 154 | { 155 | "original": true, 156 | "srcset": [ 157 | { 158 | "src": "https://dl.unrelenting.technology/IMG-7081.jpg", 159 | "width": 5184 160 | } 161 | ], 162 | "type": "image/jpeg" 163 | } 164 | ], 165 | "tiny_preview": "data:image/webp;base64,UklGRnAAAABXRUJQVlA4IGQAAAAwBACdASowABoAP93k6Gy/urEptVv8A/A7iWpn5FtTI0FdNumdDYJBregA/QjOCu+Vax2w/NNsn1WlEoWM/p71MMMgguqBQEtfbHi8eOBhwhKVvNAzA0Rvwyv7z3kaGgxQoYAA", 166 | "width": 5184 167 | } 168 | ``` 169 | 170 | ##### Server-Side Media Processing 171 | 172 | The above JSON can be generated on the server side, e.g. by [imgroll](https://github.com/unrelentingtech/imgroll). 173 | 174 | So, if the media endpoint returns a JSON body, it will be inserted verbatim. 175 | But that implies synchronous processing, which is slow. 176 | 177 | micro-panel also supports Server-Sent Events for asynchronously replacing plain URLs 178 | with objects that contain multiple sources, metadata, etc.: 179 | 180 | ```html 181 | 184 | ``` 185 | 186 | The SSE stream must stream JSON-encoded payloads that contain `url` and `object` keys 187 | (every property value equal to `url` will be replaced with the `object`. 188 | 189 | #### Default Content Type 190 | 191 | By default, newly created entries will be populated with `content: [{html: ''}]` to let you write HTML. 192 | If your endpoint supports a more convenient markup language such as Markdown, you can specify it: 193 | 194 | ```html 195 | 196 | ``` 197 | 198 | ### Posts 199 | 200 | The toolbar has an Edit button for the current page, but you can also add a button for quickly editing a particular post in a list (feed) of posts. 201 | 202 | In the post template, add a button or link wrapped in a `micro-panel-action`. 203 | The `with` attribute of the element will be used, without modification, for the `?q=source` Micropub request. 204 | So make sure that e.g. if it's relative here, your Micropub endpoint also understands relative URLs. 205 | 206 | And as already mentioned, `indie-action` is also handled. 207 | 208 | ```html 209 | ... 210 | 221 | ``` 222 | 223 | ### Category Suggestions 224 | 225 | To have the ability to conveniently add existing tags to the `category` property, mark up your tag list / tag cloud / whatever like so: 226 | 227 | ```html 228 | #demo 229 | #test 230 | ``` 231 | 232 | Everything with the `data-mf-category` will be used for the category suggestions. 233 | 234 | ### Quick Reaction URL 235 | 236 | micro-panel will automatically pop up a new reply/like/repost/etc. from a URL constructed like: 237 | 238 | ``` 239 | https://your.site/?mp-reaction=in-reply-to&with=https://example.com/entry&content=Hello+world 240 | ``` 241 | 242 | Where `in-reply-to` is the property name (can be anything). 243 | `content` is optional of course. 244 | 245 | And even `with` is optional, so you can have a plain "share" action: 246 | 247 | ``` 248 | https://your.site/?mp-reaction=post&content=Hello+world 249 | ``` 250 | 251 | This can be used to [set up indie-config](https://indieweb.org/indie-config#How_to_publish), for example: 252 | 253 | ```html 254 | 268 | ``` 269 | 270 | ## Usage 271 | 272 | With the setup above, you should be ready to go! 273 | 274 | Just log in to your website, click the New post or Edit buttons and enjoy. 275 | 276 | ## Contributing 277 | 278 | Please feel free to submit pull requests! 279 | 280 | By participating in this project you agree to follow the [Contributor Code of Conduct](https://contributor-covenant.org/version/1/4/) and to release your contributions under the Unlicense. 281 | 282 | [The list of contributors is available on GitHub](https://github.com/unrelentingtech/micro-panel/graphs/contributors). 283 | 284 | ## License 285 | 286 | This is free and unencumbered software released into the public domain. 287 | For more information, please refer to the `UNLICENSE` file or [unlicense.org](https://unlicense.org). 288 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | micro-panel demo 7 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |

Welcome to micro-panel

33 |
34 |

This is a set of Web Components for editing IndieWeb sites. You can try it out on this page!

35 |
36 |
37 |
38 | 39 | 40 | 41 | 42 | Or . 43 | 44 | 45 | Or liking. 46 | 47 |
48 |
49 | 50 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /devserver.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const polka = require('polka') 3 | const busboy = require('busboy') 4 | const esModuleDevserver = require('es-module-devserver') 5 | 6 | polka() 7 | .use(esModuleDevserver.middleware(__dirname)) 8 | .get('/', (req, res) => { 9 | const content = fs.readFileSync('demo.html') 10 | return res.end(content) 11 | }) 12 | .get('/fake-micropub', (req, res) => { 13 | res.setHeader('Content-Type', 'application/json') 14 | return res.end(JSON.stringify({ 15 | type: ['h-entry'], 16 | properties: { 17 | name: ['Welcome to micro-panel'], 18 | content: [ 19 | { 20 | markdown: `This is a set of Web Components for editing IndieWeb sites. 21 | Look, the post source is **different**! This is a *demo*, so the micropub endpoint is fake :)` 22 | } 23 | ], 24 | url: [`https://${req.headers.host}/fake/post`], 25 | category: ['test'], 26 | ['like-of']: [ 27 | { 28 | type: ['h-entry'], 29 | properties: { 30 | name: ['Nested entry test!'], 31 | url: [`https://${req.headers.host}/fake/other-post`], 32 | }, 33 | content: [ 34 | { 35 | html: `This works!` 36 | } 37 | ], 38 | }, 39 | ], 40 | video: [ 41 | { 42 | "height": 640, 43 | "width": 800, 44 | "source": [ 45 | { 46 | "src": "https://dl.unrelenting.technology/786ac297_x.mp4", 47 | "type": "video/mp4" 48 | }, 49 | { 50 | "src": "https://dl.unrelenting.technology/786ac297_x.webm", 51 | "type": "video/webm" 52 | }, 53 | { 54 | "src": "https://dl.unrelenting.technology/786ac297_x.poster.jpg", 55 | "type": "image/jpeg" 56 | } 57 | ], 58 | "meta": { 59 | "major_brand": "isom", 60 | "encoder": "Lavf57.46.100", 61 | "minor_version": "512", 62 | "compatible_brands": "isomiso2avc1mp41" 63 | } 64 | } 65 | ], 66 | photo: [ 67 | { 68 | "height": 380, 69 | "aperture": 10, 70 | "focal_length": 27, 71 | "geo": null, 72 | "height": 2916, 73 | "iso": 100, 74 | "palette": [ 75 | { "b": 106, "g": 89, "r": 58 }, 76 | { "b": 198, "g": 201, "r": 201 }, 77 | { "b": 140, "g": 143, "r": 146 }, 78 | { "b": 25, "g": 23, "r": 2 }, 79 | { "b": 41, "g": 47, "r": 52 }, 80 | { "b": 181, "g": 149, "r": 101 }, 81 | { "b": 191, "g": 183, "r": 159 }, 82 | { "b": 153, "g": 141, "r": 113 }, 83 | { "b": 128, "g": 140, "r": 172 } 84 | ], 85 | "shutter_speed": [ 1, 320 ], 86 | "width": 1276, 87 | "source": [ 88 | { 89 | "src": "https://dl.unrelenting.technology/b18de0b9_wayland-screenshot-2018-07-05_16-33-27-fs8.png", 90 | "type": "image/png" 91 | }, 92 | { 93 | "src": "https://dl.unrelenting.technology/b18de0b9_wayland-screenshot-2018-07-05_16-33-27-fs8.webp", 94 | "type": "image/webp" 95 | } 96 | ], 97 | "id": "snap" 98 | } 99 | ], 100 | }, 101 | })) 102 | }) 103 | .post('/fake-micropub', (req, res) => { 104 | req.pipe(process.stdout); 105 | res.setHeader('Location', req.headers.referer || `http://${req.headers.host}`) 106 | res.statusCode = 201 107 | res.end() 108 | }) 109 | .post('/fake-media', (req, res) => { 110 | let filename = null 111 | const bb = busboy({ headers: req.headers }) 112 | bb.on('file', (_fld, file, fname, _enc, _mt) => { 113 | file.on('data', _ => {}) // needs these 114 | file.on('end', () => {}) 115 | filename = fname 116 | }) 117 | bb.on('finish', () => { 118 | console.log(filename) 119 | res.setHeader('Location', `https://example.com/fake/media/${filename}`) 120 | res.statusCode = 201 121 | res.end() 122 | }) 123 | setTimeout(() => req.pipe(bb), 500) // simulate processing time 124 | }) 125 | .listen(3003) 126 | -------------------------------------------------------------------------------- /icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valpackett/micro-panel/44c918bab8d443507b07ef481547c6fc6a62e3d2/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /icons/big-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valpackett/micro-panel/44c918bab8d443507b07ef481547c6fc6a62e3d2/icons/big-icon.png -------------------------------------------------------------------------------- /icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valpackett/micro-panel/44c918bab8d443507b07ef481547c6fc6a62e3d2/icons/favicon-32x32.png -------------------------------------------------------------------------------- /icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valpackett/micro-panel/44c918bab8d443507b07ef481547c6fc6a62e3d2/icons/favicon.ico -------------------------------------------------------------------------------- /icons/mask-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/micro-panel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro-panel", 3 | "short_name": "μ-panel", 4 | "display": "browser", 5 | "orientation": "portrait-primary", 6 | "theme_color": "#00897b", 7 | "icons": [ 8 | { 9 | "src": "icons/big-icon.png", 10 | "sizes": "512x512", 11 | "type": "image/png" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro-panel", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@arr/every": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/@arr/every/-/every-1.0.0.tgz", 10 | "integrity": "sha1-MU+BaPUK5IoDLP2tX9tDb0ZKl6w=", 11 | "dev": true 12 | }, 13 | "@babel/code-frame": { 14 | "version": "7.14.5", 15 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", 16 | "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", 17 | "dev": true, 18 | "requires": { 19 | "@babel/highlight": "^7.14.5" 20 | } 21 | }, 22 | "@babel/helper-validator-identifier": { 23 | "version": "7.14.9", 24 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", 25 | "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", 26 | "dev": true 27 | }, 28 | "@babel/highlight": { 29 | "version": "7.14.5", 30 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", 31 | "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", 32 | "dev": true, 33 | "requires": { 34 | "@babel/helper-validator-identifier": "^7.14.5", 35 | "chalk": "^2.0.0", 36 | "js-tokens": "^4.0.0" 37 | } 38 | }, 39 | "@codemirror/autocomplete": { 40 | "version": "0.19.9", 41 | "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-0.19.9.tgz", 42 | "integrity": "sha512-Ph1LWHtFFqNUIqEVrws6I263ihe5TH+TRBPwxQ78j7st7Q67FDAmgKX6mNbUPh02dxfqQrc9qxlo5JIqKeiVdg==", 43 | "requires": { 44 | "@codemirror/language": "^0.19.0", 45 | "@codemirror/state": "^0.19.4", 46 | "@codemirror/text": "^0.19.2", 47 | "@codemirror/tooltip": "^0.19.0", 48 | "@codemirror/view": "^0.19.0", 49 | "@lezer/common": "^0.15.0" 50 | } 51 | }, 52 | "@codemirror/basic-setup": { 53 | "version": "0.19.1", 54 | "resolved": "https://registry.npmjs.org/@codemirror/basic-setup/-/basic-setup-0.19.1.tgz", 55 | "integrity": "sha512-gLjD7YgZU/we6BzS/ecCmD3viw83dsgv5ZUaSydYbYx9X4w4w9RqYnckcJ+0GDyHfNr5Jtfv2Z5ZtFQnBj0UDA==", 56 | "requires": { 57 | "@codemirror/autocomplete": "^0.19.0", 58 | "@codemirror/closebrackets": "^0.19.0", 59 | "@codemirror/commands": "^0.19.0", 60 | "@codemirror/comment": "^0.19.0", 61 | "@codemirror/fold": "^0.19.0", 62 | "@codemirror/gutter": "^0.19.0", 63 | "@codemirror/highlight": "^0.19.0", 64 | "@codemirror/history": "^0.19.0", 65 | "@codemirror/language": "^0.19.0", 66 | "@codemirror/lint": "^0.19.0", 67 | "@codemirror/matchbrackets": "^0.19.0", 68 | "@codemirror/rectangular-selection": "^0.19.0", 69 | "@codemirror/search": "^0.19.0", 70 | "@codemirror/state": "^0.19.0", 71 | "@codemirror/view": "^0.19.31" 72 | } 73 | }, 74 | "@codemirror/closebrackets": { 75 | "version": "0.19.0", 76 | "resolved": "https://registry.npmjs.org/@codemirror/closebrackets/-/closebrackets-0.19.0.tgz", 77 | "integrity": "sha512-dFWX5OEVYWRNtGaifSbwIAlymnRRjxWMiMbffbAjF7p0zfGHDbdGkiT56q3Xud63h5/tQdSo5dK1iyNTzHz5vg==", 78 | "requires": { 79 | "@codemirror/language": "^0.19.0", 80 | "@codemirror/rangeset": "^0.19.0", 81 | "@codemirror/state": "^0.19.0", 82 | "@codemirror/text": "^0.19.0", 83 | "@codemirror/view": "^0.19.0" 84 | } 85 | }, 86 | "@codemirror/commands": { 87 | "version": "0.19.6", 88 | "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-0.19.6.tgz", 89 | "integrity": "sha512-Mjc3ZfTifOn0h5499xI3MfCVIZvO2I0ochgzxfRtPOFwfXX/k7HTgnK0/KzuGDINyxUVeDaFCkf53TyyWjdxMQ==", 90 | "requires": { 91 | "@codemirror/language": "^0.19.0", 92 | "@codemirror/matchbrackets": "^0.19.0", 93 | "@codemirror/state": "^0.19.2", 94 | "@codemirror/text": "^0.19.0", 95 | "@codemirror/view": "^0.19.22", 96 | "@lezer/common": "^0.15.0" 97 | } 98 | }, 99 | "@codemirror/comment": { 100 | "version": "0.19.0", 101 | "resolved": "https://registry.npmjs.org/@codemirror/comment/-/comment-0.19.0.tgz", 102 | "integrity": "sha512-3hqAd0548fxqOBm4khFMcXVIivX8p0bSlbAuZJ6PNoUn/0wXhxkxowPp0FmFzU2+y37Z+ZQF5cRB5EREWPRIiQ==", 103 | "requires": { 104 | "@codemirror/state": "^0.19.0", 105 | "@codemirror/text": "^0.19.0", 106 | "@codemirror/view": "^0.19.0" 107 | } 108 | }, 109 | "@codemirror/fold": { 110 | "version": "0.19.2", 111 | "resolved": "https://registry.npmjs.org/@codemirror/fold/-/fold-0.19.2.tgz", 112 | "integrity": "sha512-FLi6RBhHPBnSbKZEu1S98z+VYSP5678cMdYVqhR58OWWTkEiLRVPeCTj8FhRKNL9B8Gx+lBQhGq3uwr3KtSs8w==", 113 | "requires": { 114 | "@codemirror/gutter": "^0.19.0", 115 | "@codemirror/language": "^0.19.0", 116 | "@codemirror/rangeset": "^0.19.0", 117 | "@codemirror/state": "^0.19.0", 118 | "@codemirror/view": "^0.19.22" 119 | } 120 | }, 121 | "@codemirror/gutter": { 122 | "version": "0.19.9", 123 | "resolved": "https://registry.npmjs.org/@codemirror/gutter/-/gutter-0.19.9.tgz", 124 | "integrity": "sha512-PFrtmilahin1g6uL27aG5tM/rqR9DZzZYZsIrCXA5Uc2OFTFqx4owuhoU9hqfYxHp5ovfvBwQ+txFzqS4vog6Q==", 125 | "requires": { 126 | "@codemirror/rangeset": "^0.19.0", 127 | "@codemirror/state": "^0.19.0", 128 | "@codemirror/view": "^0.19.23" 129 | } 130 | }, 131 | "@codemirror/highlight": { 132 | "version": "0.19.6", 133 | "resolved": "https://registry.npmjs.org/@codemirror/highlight/-/highlight-0.19.6.tgz", 134 | "integrity": "sha512-+eibu6on9quY8uN3xJ/n3rH+YIDLlpX7YulVmFvqAIz/ukRQ5tWaBmB7fMixHmnmRIRBRZgB8rNtonuMwZSAHQ==", 135 | "requires": { 136 | "@codemirror/language": "^0.19.0", 137 | "@codemirror/rangeset": "^0.19.0", 138 | "@codemirror/state": "^0.19.0", 139 | "@codemirror/view": "^0.19.0", 140 | "@lezer/common": "^0.15.0", 141 | "style-mod": "^4.0.0" 142 | } 143 | }, 144 | "@codemirror/history": { 145 | "version": "0.19.1", 146 | "resolved": "https://registry.npmjs.org/@codemirror/history/-/history-0.19.1.tgz", 147 | "integrity": "sha512-mcm856QzHj2K6it7XPtKN1lcQ15tB+A5XRtRix9GHRQak4bbY0ljqYJFxD4wNEAAb4uC2axUyl4yEW57DHJmdQ==", 148 | "requires": { 149 | "@codemirror/state": "^0.19.2", 150 | "@codemirror/view": "^0.19.0" 151 | } 152 | }, 153 | "@codemirror/lang-css": { 154 | "version": "0.19.3", 155 | "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-0.19.3.tgz", 156 | "integrity": "sha512-tyCUJR42/UlfOPLb94/p7dN+IPsYSIzHbAHP2KQHANj0I+Orqp+IyIOS++M8TuCX4zkWh9dvi8s92yy/Tn8Ifg==", 157 | "requires": { 158 | "@codemirror/autocomplete": "^0.19.0", 159 | "@codemirror/highlight": "^0.19.6", 160 | "@codemirror/language": "^0.19.0", 161 | "@codemirror/state": "^0.19.0", 162 | "@lezer/css": "^0.15.2" 163 | } 164 | }, 165 | "@codemirror/lang-html": { 166 | "version": "0.19.4", 167 | "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-0.19.4.tgz", 168 | "integrity": "sha512-GpiEikNuCBeFnS+/TJSeanwqaOfNm8Kkp9WpVNEPZCLyW1mAMCuFJu/3xlWYeWc778Hc3vJqGn3bn+cLNubgCA==", 169 | "requires": { 170 | "@codemirror/autocomplete": "^0.19.0", 171 | "@codemirror/highlight": "^0.19.6", 172 | "@codemirror/lang-css": "^0.19.0", 173 | "@codemirror/lang-javascript": "^0.19.0", 174 | "@codemirror/language": "^0.19.0", 175 | "@codemirror/state": "^0.19.0", 176 | "@lezer/common": "^0.15.0", 177 | "@lezer/html": "^0.15.0" 178 | } 179 | }, 180 | "@codemirror/lang-javascript": { 181 | "version": "0.19.3", 182 | "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-0.19.3.tgz", 183 | "integrity": "sha512-2NE5z98Nz9Rv4OS5UtgehCSnyQjac+P85+evzy1D/4wllp/EPaHIEEtSP1daBvrLy49SdI/9vES3ZJu6rSv4/w==", 184 | "requires": { 185 | "@codemirror/autocomplete": "^0.19.0", 186 | "@codemirror/highlight": "^0.19.6", 187 | "@codemirror/language": "^0.19.0", 188 | "@codemirror/lint": "^0.19.0", 189 | "@codemirror/state": "^0.19.0", 190 | "@codemirror/view": "^0.19.0", 191 | "@lezer/javascript": "^0.15.1" 192 | } 193 | }, 194 | "@codemirror/lang-json": { 195 | "version": "0.19.1", 196 | "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-0.19.1.tgz", 197 | "integrity": "sha512-66K5TT9HO0ODtpjY+3Ub6t3r0OB1d27P+Kl5oygk4tDavHUBpsyHTJRFw/CdeRM2VwjbpBfctGm/cTrSthFDZg==", 198 | "requires": { 199 | "@codemirror/highlight": "^0.19.0", 200 | "@codemirror/language": "^0.19.0", 201 | "@lezer/json": "^0.15.0" 202 | } 203 | }, 204 | "@codemirror/lang-markdown": { 205 | "version": "0.19.3", 206 | "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-0.19.3.tgz", 207 | "integrity": "sha512-kCLn5yKNOscCnCrJ03HiH3fHQE67E5cSG9+WhTOjIUMwxWTSo47AzorMAG/N5Ty37jP9Wd+fOSTCNU/A9Dq8yQ==", 208 | "requires": { 209 | "@codemirror/highlight": "^0.19.0", 210 | "@codemirror/lang-html": "^0.19.0", 211 | "@codemirror/language": "^0.19.0", 212 | "@codemirror/state": "^0.19.3", 213 | "@codemirror/view": "^0.19.0", 214 | "@lezer/common": "^0.15.0", 215 | "@lezer/markdown": "^0.15.0" 216 | } 217 | }, 218 | "@codemirror/language": { 219 | "version": "0.19.7", 220 | "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-0.19.7.tgz", 221 | "integrity": "sha512-pNNUtYWMIMG0lUSKyUXJr8U0rFiCKsKFXbA2Oj17PC+S1FY99hV0z1vcntW67ekAIZw9DMEUQnLsKBuIbAUX7Q==", 222 | "requires": { 223 | "@codemirror/state": "^0.19.0", 224 | "@codemirror/text": "^0.19.0", 225 | "@codemirror/view": "^0.19.0", 226 | "@lezer/common": "^0.15.5", 227 | "@lezer/lr": "^0.15.0" 228 | } 229 | }, 230 | "@codemirror/lint": { 231 | "version": "0.19.3", 232 | "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-0.19.3.tgz", 233 | "integrity": "sha512-+c39s05ybD2NjghxkPFsUbH/qBL0cdzKmtHbzUm0RVspeL2OiP7uHYJ6J5+Qr9RjMIPWzcqSauRqxfmCrctUfg==", 234 | "requires": { 235 | "@codemirror/gutter": "^0.19.4", 236 | "@codemirror/panel": "^0.19.0", 237 | "@codemirror/rangeset": "^0.19.1", 238 | "@codemirror/state": "^0.19.4", 239 | "@codemirror/tooltip": "^0.19.5", 240 | "@codemirror/view": "^0.19.0", 241 | "crelt": "^1.0.5" 242 | } 243 | }, 244 | "@codemirror/matchbrackets": { 245 | "version": "0.19.3", 246 | "resolved": "https://registry.npmjs.org/@codemirror/matchbrackets/-/matchbrackets-0.19.3.tgz", 247 | "integrity": "sha512-ljkrBxaLgh8jesroUiBa57pdEwqJamxkukXrJpL9LdyFZVJaF+9TldhztRaMsMZO1XnCSSHQ9sg32iuHo7Sc2g==", 248 | "requires": { 249 | "@codemirror/language": "^0.19.0", 250 | "@codemirror/state": "^0.19.0", 251 | "@codemirror/view": "^0.19.0", 252 | "@lezer/common": "^0.15.0" 253 | } 254 | }, 255 | "@codemirror/panel": { 256 | "version": "0.19.1", 257 | "resolved": "https://registry.npmjs.org/@codemirror/panel/-/panel-0.19.1.tgz", 258 | "integrity": "sha512-sYeOCMA3KRYxZYJYn5PNlt9yNsjy3zTNTrbYSfVgjgL9QomIVgOJWPO5hZ2sTN8lufO6lw0vTBsIPL9MSidmBg==", 259 | "requires": { 260 | "@codemirror/state": "^0.19.0", 261 | "@codemirror/view": "^0.19.0" 262 | } 263 | }, 264 | "@codemirror/rangeset": { 265 | "version": "0.19.5", 266 | "resolved": "https://registry.npmjs.org/@codemirror/rangeset/-/rangeset-0.19.5.tgz", 267 | "integrity": "sha512-L3b+RIwIRKOJ3pJLOtpkxCUjGnxZKFyPb0CjYWKnVLuzEIaEExWWK7sp6rsejxOy8RjYzfCHlFhYB4UdQN7brw==", 268 | "requires": { 269 | "@codemirror/state": "^0.19.0" 270 | } 271 | }, 272 | "@codemirror/rectangular-selection": { 273 | "version": "0.19.1", 274 | "resolved": "https://registry.npmjs.org/@codemirror/rectangular-selection/-/rectangular-selection-0.19.1.tgz", 275 | "integrity": "sha512-9ElnqOg3mpZIWe0prPRd1SZ48Q9QB3bR8Aocq8UtjboJSUG8ABhRrbuTZMW/rMqpBPSjVpCe9xkCCkEQMYQVmw==", 276 | "requires": { 277 | "@codemirror/state": "^0.19.0", 278 | "@codemirror/text": "^0.19.4", 279 | "@codemirror/view": "^0.19.0" 280 | } 281 | }, 282 | "@codemirror/search": { 283 | "version": "0.19.5", 284 | "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-0.19.5.tgz", 285 | "integrity": "sha512-9kbtCBpMDlzcj7AptMRBx9BZpC5wz+/tG8nIe4vdpOszP08ZY2AcxN5nlhCoUSZu+pd0b6fYiwjLXOf000rRpw==", 286 | "requires": { 287 | "@codemirror/panel": "^0.19.0", 288 | "@codemirror/rangeset": "^0.19.0", 289 | "@codemirror/state": "^0.19.3", 290 | "@codemirror/text": "^0.19.0", 291 | "@codemirror/view": "^0.19.0", 292 | "crelt": "^1.0.5" 293 | } 294 | }, 295 | "@codemirror/state": { 296 | "version": "0.19.6", 297 | "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.19.6.tgz", 298 | "integrity": "sha512-sqIQZE9VqwQj7D4c2oz9mfLhlT1ElAzGB5lO1lE33BPyrdNy1cJyCIOecT4cn4VeJOFrnjOeu+IftZ3zqdFETw==", 299 | "requires": { 300 | "@codemirror/text": "^0.19.0" 301 | } 302 | }, 303 | "@codemirror/text": { 304 | "version": "0.19.5", 305 | "resolved": "https://registry.npmjs.org/@codemirror/text/-/text-0.19.5.tgz", 306 | "integrity": "sha512-Syu5Xc7tZzeUAM/y4fETkT0zgGr48rDG+w4U38bPwSIUr+L9S/7w2wDE1WGNzjaZPz12F6gb1gxWiSTg9ocLow==" 307 | }, 308 | "@codemirror/tooltip": { 309 | "version": "0.19.10", 310 | "resolved": "https://registry.npmjs.org/@codemirror/tooltip/-/tooltip-0.19.10.tgz", 311 | "integrity": "sha512-xqIhCHr+IYoamdNLvBnU/oDh92zPnsbT1zLaFtKTFi9GI9SxOfBhWY3jfMENlK0j1C9rk8+AvwpXblPGvY/O6w==", 312 | "requires": { 313 | "@codemirror/state": "^0.19.0", 314 | "@codemirror/view": "^0.19.0" 315 | } 316 | }, 317 | "@codemirror/view": { 318 | "version": "0.19.37", 319 | "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.19.37.tgz", 320 | "integrity": "sha512-SLuLx9p0O1ZHXLehvl5MwSvUrQRcsNGemzTgJ0zRajmc3BBsNigI1PXxdo7tvBhO5DcAzRRBXoke9DZFUR6Qqg==", 321 | "requires": { 322 | "@codemirror/rangeset": "^0.19.5", 323 | "@codemirror/state": "^0.19.3", 324 | "@codemirror/text": "^0.19.0", 325 | "style-mod": "^4.0.0", 326 | "w3c-keyname": "^2.2.4" 327 | } 328 | }, 329 | "@jridgewell/gen-mapping": { 330 | "version": "0.3.2", 331 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", 332 | "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", 333 | "dev": true, 334 | "requires": { 335 | "@jridgewell/set-array": "^1.0.1", 336 | "@jridgewell/sourcemap-codec": "^1.4.10", 337 | "@jridgewell/trace-mapping": "^0.3.9" 338 | } 339 | }, 340 | "@jridgewell/resolve-uri": { 341 | "version": "3.1.0", 342 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 343 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 344 | "dev": true 345 | }, 346 | "@jridgewell/set-array": { 347 | "version": "1.1.2", 348 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 349 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 350 | "dev": true 351 | }, 352 | "@jridgewell/source-map": { 353 | "version": "0.3.2", 354 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", 355 | "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", 356 | "dev": true, 357 | "requires": { 358 | "@jridgewell/gen-mapping": "^0.3.0", 359 | "@jridgewell/trace-mapping": "^0.3.9" 360 | } 361 | }, 362 | "@jridgewell/sourcemap-codec": { 363 | "version": "1.4.14", 364 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 365 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 366 | "dev": true 367 | }, 368 | "@jridgewell/trace-mapping": { 369 | "version": "0.3.14", 370 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", 371 | "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", 372 | "dev": true, 373 | "requires": { 374 | "@jridgewell/resolve-uri": "^3.0.3", 375 | "@jridgewell/sourcemap-codec": "^1.4.10" 376 | } 377 | }, 378 | "@lezer/common": { 379 | "version": "0.15.11", 380 | "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.11.tgz", 381 | "integrity": "sha512-vv0nSdIaVCRcJ8rPuDdsrNVfBOYe/4Szr/LhF929XyDmBndLDuWiCCHooGlGlJfzELyO608AyDhVsuX/ZG36NA==" 382 | }, 383 | "@lezer/css": { 384 | "version": "0.15.2", 385 | "resolved": "https://registry.npmjs.org/@lezer/css/-/css-0.15.2.tgz", 386 | "integrity": "sha512-tnMOMZY0Zs6JQeVjqfmREYMV0GnmZR1NitndLWioZMD6mA7VQF/PPKPmJX1f+ZgVZQc5Am0df9mX3aiJnNJlKQ==", 387 | "requires": { 388 | "@lezer/lr": "^0.15.0" 389 | } 390 | }, 391 | "@lezer/html": { 392 | "version": "0.15.0", 393 | "resolved": "https://registry.npmjs.org/@lezer/html/-/html-0.15.0.tgz", 394 | "integrity": "sha512-ErmgP/Vv0AhYJvs/Ekb9oue4IzBHemKLi7K8tJ0jgS+20Y8FGC9foK6knCXtEHqdPaxVGQH9PVp7gecLnzLd9Q==", 395 | "requires": { 396 | "@lezer/lr": "^0.15.0" 397 | } 398 | }, 399 | "@lezer/javascript": { 400 | "version": "0.15.2", 401 | "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-0.15.2.tgz", 402 | "integrity": "sha512-ytWvdJ1NAc0pfrNipGQs8otJVfjVibpIiFKH0fl99rKSA6cVlyQN/XTj/dEAQCfBfCBPAFdc30cuUe5CGZ0odA==", 403 | "requires": { 404 | "@lezer/lr": "^0.15.0" 405 | } 406 | }, 407 | "@lezer/json": { 408 | "version": "0.15.0", 409 | "resolved": "https://registry.npmjs.org/@lezer/json/-/json-0.15.0.tgz", 410 | "integrity": "sha512-OsMjjBkTkeQ15iMCu5U1OiBubRC4V9Wm03zdIlUgNZ20aUPx5DWDRqUc5wG41JXVSj7Lxmo+idlFCfBBdxB8sw==", 411 | "requires": { 412 | "@lezer/lr": "^0.15.0" 413 | } 414 | }, 415 | "@lezer/lr": { 416 | "version": "0.15.5", 417 | "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.5.tgz", 418 | "integrity": "sha512-DEcLyhdmBxD1foQe7RegLrSlfS/XaTMGLkO5evkzHWAQKh/JnFWp7j7iNB7s2EpxzRrBCh0U+W7JDCeFhv2mng==", 419 | "requires": { 420 | "@lezer/common": "^0.15.0" 421 | } 422 | }, 423 | "@lezer/markdown": { 424 | "version": "0.15.3", 425 | "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-0.15.3.tgz", 426 | "integrity": "sha512-Wq9WGO5l4cnRKtJVDuJZtP3YZgCGpeexBhIvzvm4bmSmqqVMMWk+PUu8vX5KejT16wV1iV73EKn4/P/+lUFcFA==", 427 | "requires": { 428 | "@lezer/common": "^0.15.0" 429 | } 430 | }, 431 | "@lit/reactive-element": { 432 | "version": "1.0.2", 433 | "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.0.2.tgz", 434 | "integrity": "sha512-oz3d3MKjQ2tXynQgyaQaMpGTDNyNDeBdo6dXf1AbjTwhA1IRINHmA7kSaVYv9ttKweNkEoNqp9DqteDdgWzPEg==" 435 | }, 436 | "@polka/url": { 437 | "version": "0.5.0", 438 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz", 439 | "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==", 440 | "dev": true 441 | }, 442 | "@rollup/plugin-node-resolve": { 443 | "version": "13.1.2", 444 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.2.tgz", 445 | "integrity": "sha512-xyqbuf1vyOPC60jEKhx3DBHunymnCJswzjNTKfX4Jz7zCPar1UqbRZCNY1u5QaXh97beaFTWdoUUWiV4qX8o/g==", 446 | "dev": true, 447 | "requires": { 448 | "@rollup/pluginutils": "^3.1.0", 449 | "@types/resolve": "1.17.1", 450 | "builtin-modules": "^3.1.0", 451 | "deepmerge": "^4.2.2", 452 | "is-module": "^1.0.0", 453 | "resolve": "^1.19.0" 454 | } 455 | }, 456 | "@rollup/pluginutils": { 457 | "version": "3.1.0", 458 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", 459 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", 460 | "dev": true, 461 | "requires": { 462 | "@types/estree": "0.0.39", 463 | "estree-walker": "^1.0.1", 464 | "picomatch": "^2.2.2" 465 | }, 466 | "dependencies": { 467 | "estree-walker": { 468 | "version": "1.0.1", 469 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 470 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 471 | "dev": true 472 | } 473 | } 474 | }, 475 | "@types/clean-css": { 476 | "version": "4.2.5", 477 | "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.5.tgz", 478 | "integrity": "sha512-NEzjkGGpbs9S9fgC4abuBvTpVwE3i+Acu9BBod3PUyjDVZcNsGx61b8r2PphR61QGPnn0JHVs5ey6/I4eTrkxw==", 479 | "dev": true, 480 | "requires": { 481 | "@types/node": "*", 482 | "source-map": "^0.6.0" 483 | } 484 | }, 485 | "@types/estree": { 486 | "version": "0.0.39", 487 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 488 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 489 | "dev": true 490 | }, 491 | "@types/html-minifier": { 492 | "version": "3.5.3", 493 | "resolved": "https://registry.npmjs.org/@types/html-minifier/-/html-minifier-3.5.3.tgz", 494 | "integrity": "sha512-j1P/4PcWVVCPEy5lofcHnQ6BtXz9tHGiFPWzqm7TtGuWZEfCHEP446HlkSNc9fQgNJaJZ6ewPtp2aaFla/Uerg==", 495 | "dev": true, 496 | "requires": { 497 | "@types/clean-css": "*", 498 | "@types/relateurl": "*", 499 | "@types/uglify-js": "*" 500 | } 501 | }, 502 | "@types/node": { 503 | "version": "12.6.2", 504 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.2.tgz", 505 | "integrity": "sha512-gojym4tX0FWeV2gsW4Xmzo5wxGjXGm550oVUII7f7G5o4BV6c7DBdiG1RRQd+y1bvqRyYtPfMK85UM95vsapqQ==", 506 | "dev": true 507 | }, 508 | "@types/relateurl": { 509 | "version": "0.2.29", 510 | "resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.29.tgz", 511 | "integrity": "sha512-QSvevZ+IRww2ldtfv1QskYsqVVVwCKQf1XbwtcyyoRvLIQzfyPhj/C+3+PKzSDRdiyejaiLgnq//XTkleorpLg==", 512 | "dev": true 513 | }, 514 | "@types/resolve": { 515 | "version": "1.17.1", 516 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", 517 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", 518 | "dev": true, 519 | "requires": { 520 | "@types/node": "*" 521 | } 522 | }, 523 | "@types/trusted-types": { 524 | "version": "2.0.2", 525 | "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", 526 | "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" 527 | }, 528 | "@types/uglify-js": { 529 | "version": "3.13.1", 530 | "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz", 531 | "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==", 532 | "dev": true, 533 | "requires": { 534 | "source-map": "^0.6.1" 535 | } 536 | }, 537 | "@wessberg/color": { 538 | "version": "1.0.5", 539 | "resolved": "https://registry.npmjs.org/@wessberg/color/-/color-1.0.5.tgz", 540 | "integrity": "sha512-Df6kHsgbPn+9waCMlFM9yn1kk5CJCw6rQxFsBPzH5UWHL/MLMGG3sj3dvBDojpP2nX1OhJto5oXWsvTesSonmg==" 541 | }, 542 | "acorn": { 543 | "version": "8.7.1", 544 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", 545 | "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", 546 | "dev": true 547 | }, 548 | "ansi-styles": { 549 | "version": "3.2.1", 550 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 551 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 552 | "dev": true, 553 | "requires": { 554 | "color-convert": "^1.9.0" 555 | } 556 | }, 557 | "buffer-from": { 558 | "version": "1.1.2", 559 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 560 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 561 | "dev": true 562 | }, 563 | "builtin-modules": { 564 | "version": "3.2.0", 565 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", 566 | "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", 567 | "dev": true 568 | }, 569 | "busboy": { 570 | "version": "1.3.0", 571 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.3.0.tgz", 572 | "integrity": "sha512-ytF8pdwEKCNwl0K9PSwmv+yPcicy+ef+YNAw+L0FTfyBLzCWhp5V3jEfau2kb5A0JD0TkOPrdtdCKLoAHlMu1A==", 573 | "dev": true, 574 | "requires": { 575 | "streamsearch": "^1.1.0" 576 | } 577 | }, 578 | "camel-case": { 579 | "version": "3.0.0", 580 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", 581 | "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", 582 | "dev": true, 583 | "requires": { 584 | "no-case": "^2.2.0", 585 | "upper-case": "^1.1.1" 586 | } 587 | }, 588 | "chalk": { 589 | "version": "2.4.2", 590 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 591 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 592 | "dev": true, 593 | "requires": { 594 | "ansi-styles": "^3.2.1", 595 | "escape-string-regexp": "^1.0.5", 596 | "supports-color": "^5.3.0" 597 | } 598 | }, 599 | "clean-css": { 600 | "version": "4.2.3", 601 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", 602 | "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", 603 | "dev": true, 604 | "requires": { 605 | "source-map": "~0.6.0" 606 | } 607 | }, 608 | "color-convert": { 609 | "version": "1.9.3", 610 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 611 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 612 | "dev": true, 613 | "requires": { 614 | "color-name": "1.1.3" 615 | } 616 | }, 617 | "color-name": { 618 | "version": "1.1.3", 619 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 620 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 621 | "dev": true 622 | }, 623 | "commander": { 624 | "version": "2.20.3", 625 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 626 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 627 | "dev": true 628 | }, 629 | "crelt": { 630 | "version": "1.0.5", 631 | "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", 632 | "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" 633 | }, 634 | "deepmerge": { 635 | "version": "4.2.2", 636 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 637 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 638 | "dev": true 639 | }, 640 | "es-module-devserver": { 641 | "version": "0.1.3", 642 | "resolved": "https://registry.npmjs.org/es-module-devserver/-/es-module-devserver-0.1.3.tgz", 643 | "integrity": "sha512-9gJmuT9dxgPZzWUDg1KZv76eqdPCw1EzYB86NidQkEE5gjlNc5Nh2iliJHb7gnLgMdML5FzlFNmpbF+RQrdczw==", 644 | "dev": true, 645 | "requires": { 646 | "mime": "^2.5.2", 647 | "resolve": "^1.20.0" 648 | } 649 | }, 650 | "escape-string-regexp": { 651 | "version": "1.0.5", 652 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 653 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 654 | "dev": true 655 | }, 656 | "estree-walker": { 657 | "version": "0.6.1", 658 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 659 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 660 | "dev": true 661 | }, 662 | "fsevents": { 663 | "version": "2.3.2", 664 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 665 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 666 | "dev": true, 667 | "optional": true 668 | }, 669 | "function-bind": { 670 | "version": "1.1.1", 671 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 672 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 673 | "dev": true 674 | }, 675 | "has": { 676 | "version": "1.0.3", 677 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 678 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 679 | "dev": true, 680 | "requires": { 681 | "function-bind": "^1.1.1" 682 | } 683 | }, 684 | "has-flag": { 685 | "version": "3.0.0", 686 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 687 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 688 | "dev": true 689 | }, 690 | "he": { 691 | "version": "1.2.0", 692 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 693 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 694 | "dev": true 695 | }, 696 | "html-minifier": { 697 | "version": "4.0.0", 698 | "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", 699 | "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", 700 | "dev": true, 701 | "requires": { 702 | "camel-case": "^3.0.0", 703 | "clean-css": "^4.2.1", 704 | "commander": "^2.19.0", 705 | "he": "^1.2.0", 706 | "param-case": "^2.1.1", 707 | "relateurl": "^0.2.7", 708 | "uglify-js": "^3.5.1" 709 | }, 710 | "dependencies": { 711 | "commander": { 712 | "version": "2.20.3", 713 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 714 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 715 | "dev": true 716 | }, 717 | "uglify-js": { 718 | "version": "3.14.1", 719 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", 720 | "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", 721 | "dev": true 722 | } 723 | } 724 | }, 725 | "immer": { 726 | "version": "9.0.7", 727 | "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.7.tgz", 728 | "integrity": "sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA==" 729 | }, 730 | "is-core-module": { 731 | "version": "2.5.0", 732 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", 733 | "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", 734 | "dev": true, 735 | "requires": { 736 | "has": "^1.0.3" 737 | } 738 | }, 739 | "is-module": { 740 | "version": "1.0.0", 741 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 742 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 743 | "dev": true 744 | }, 745 | "jest-worker": { 746 | "version": "26.6.2", 747 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", 748 | "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", 749 | "dev": true, 750 | "requires": { 751 | "@types/node": "*", 752 | "merge-stream": "^2.0.0", 753 | "supports-color": "^7.0.0" 754 | }, 755 | "dependencies": { 756 | "has-flag": { 757 | "version": "4.0.0", 758 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 759 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 760 | "dev": true 761 | }, 762 | "supports-color": { 763 | "version": "7.2.0", 764 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 765 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 766 | "dev": true, 767 | "requires": { 768 | "has-flag": "^4.0.0" 769 | } 770 | } 771 | } 772 | }, 773 | "js-tokens": { 774 | "version": "4.0.0", 775 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 776 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 777 | "dev": true 778 | }, 779 | "lit": { 780 | "version": "2.0.2", 781 | "resolved": "https://registry.npmjs.org/lit/-/lit-2.0.2.tgz", 782 | "integrity": "sha512-hKA/1YaSB+P+DvKWuR2q1Xzy/iayhNrJ3aveD0OQ9CKn6wUjsdnF/7LavDOJsKP/K5jzW/kXsuduPgRvTFrFJw==", 783 | "requires": { 784 | "@lit/reactive-element": "^1.0.0", 785 | "lit-element": "^3.0.0", 786 | "lit-html": "^2.0.0" 787 | } 788 | }, 789 | "lit-element": { 790 | "version": "3.0.2", 791 | "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.0.2.tgz", 792 | "integrity": "sha512-9vTJ47D2DSE4Jwhle7aMzEwO2ZcOPRikqfT3CVG7Qol2c9/I4KZwinZNW5Xv8hNm+G/enSSfIwqQhIXi6ioAUg==", 793 | "requires": { 794 | "@lit/reactive-element": "^1.0.0", 795 | "lit-html": "^2.0.0" 796 | } 797 | }, 798 | "lit-html": { 799 | "version": "2.0.2", 800 | "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.0.2.tgz", 801 | "integrity": "sha512-dON7Zg8btb14/fWohQLQBdSgkoiQA4mIUy87evmyJHtxRq7zS6LlC32bT5EPWiof5PUQaDpF45v2OlrxHA5Clg==", 802 | "requires": { 803 | "@types/trusted-types": "^2.0.2" 804 | } 805 | }, 806 | "lower-case": { 807 | "version": "1.1.4", 808 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", 809 | "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", 810 | "dev": true 811 | }, 812 | "magic-string": { 813 | "version": "0.25.4", 814 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz", 815 | "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==", 816 | "dev": true, 817 | "requires": { 818 | "sourcemap-codec": "^1.4.4" 819 | } 820 | }, 821 | "matchit": { 822 | "version": "1.0.7", 823 | "resolved": "https://registry.npmjs.org/matchit/-/matchit-1.0.7.tgz", 824 | "integrity": "sha512-6GQP+4ukhBEL4pQPQlipd51XnpOlycit/3o6p4XhhZt2+9hc7JlHr7NuWbTLQ2MdSzcxR603L7LF4T8x1e1mXA==", 825 | "dev": true, 826 | "requires": { 827 | "@arr/every": "^1.0.0" 828 | } 829 | }, 830 | "merge-stream": { 831 | "version": "2.0.0", 832 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 833 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 834 | "dev": true 835 | }, 836 | "mime": { 837 | "version": "2.5.2", 838 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", 839 | "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", 840 | "dev": true 841 | }, 842 | "minify-html-literals": { 843 | "version": "1.3.5", 844 | "resolved": "https://registry.npmjs.org/minify-html-literals/-/minify-html-literals-1.3.5.tgz", 845 | "integrity": "sha512-p8T8ryePRR8FVfJZLVFmM53WY25FL0moCCTycUDuAu6rf9GMLwy0gNjXBGNin3Yun7Y+tIWd28axOf0t2EpAlQ==", 846 | "dev": true, 847 | "requires": { 848 | "@types/html-minifier": "^3.5.3", 849 | "clean-css": "^4.2.1", 850 | "html-minifier": "^4.0.0", 851 | "magic-string": "^0.25.0", 852 | "parse-literals": "^1.2.1" 853 | } 854 | }, 855 | "no-case": { 856 | "version": "2.3.2", 857 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", 858 | "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", 859 | "dev": true, 860 | "requires": { 861 | "lower-case": "^1.1.1" 862 | } 863 | }, 864 | "param-case": { 865 | "version": "2.1.1", 866 | "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", 867 | "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", 868 | "dev": true, 869 | "requires": { 870 | "no-case": "^2.2.0" 871 | } 872 | }, 873 | "parse-literals": { 874 | "version": "1.2.1", 875 | "resolved": "https://registry.npmjs.org/parse-literals/-/parse-literals-1.2.1.tgz", 876 | "integrity": "sha512-Ml0w104Ph2wwzuRdxrg9booVWsngXbB4bZ5T2z6WyF8b5oaNkUmBiDtahi34yUIpXD8Y13JjAK6UyIyApJ73RQ==", 877 | "dev": true, 878 | "requires": { 879 | "typescript": "^2.9.2 || ^3.0.0 || ^4.0.0" 880 | } 881 | }, 882 | "path-parse": { 883 | "version": "1.0.7", 884 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 885 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 886 | "dev": true 887 | }, 888 | "picomatch": { 889 | "version": "2.3.0", 890 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", 891 | "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", 892 | "dev": true 893 | }, 894 | "polka": { 895 | "version": "0.5.2", 896 | "resolved": "https://registry.npmjs.org/polka/-/polka-0.5.2.tgz", 897 | "integrity": "sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==", 898 | "dev": true, 899 | "requires": { 900 | "@polka/url": "^0.5.0", 901 | "trouter": "^2.0.1" 902 | } 903 | }, 904 | "randombytes": { 905 | "version": "2.1.0", 906 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 907 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 908 | "dev": true, 909 | "requires": { 910 | "safe-buffer": "^5.1.0" 911 | } 912 | }, 913 | "relateurl": { 914 | "version": "0.2.7", 915 | "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", 916 | "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", 917 | "dev": true 918 | }, 919 | "resolve": { 920 | "version": "1.20.0", 921 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 922 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 923 | "dev": true, 924 | "requires": { 925 | "is-core-module": "^2.2.0", 926 | "path-parse": "^1.0.6" 927 | } 928 | }, 929 | "rollup": { 930 | "version": "2.62.0", 931 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.62.0.tgz", 932 | "integrity": "sha512-cJEQq2gwB0GWMD3rYImefQTSjrPYaC6s4J9pYqnstVLJ1CHa/aZNVkD4Epuvg4iLeMA4KRiq7UM7awKK6j7jcw==", 933 | "dev": true, 934 | "requires": { 935 | "fsevents": "~2.3.2" 936 | } 937 | }, 938 | "rollup-plugin-minify-html-literals": { 939 | "version": "1.2.6", 940 | "resolved": "https://registry.npmjs.org/rollup-plugin-minify-html-literals/-/rollup-plugin-minify-html-literals-1.2.6.tgz", 941 | "integrity": "sha512-JRq2fjlCTiw0zu+1Sy3ClHGCxA79dWGr4HLHWSQgd060StVW9fBVksuj8Xw/suPkNSGClJf/4xNQ1MF6JeXPaw==", 942 | "dev": true, 943 | "requires": { 944 | "minify-html-literals": "^1.3.5", 945 | "rollup-pluginutils": "^2.8.2" 946 | }, 947 | "dependencies": { 948 | "rollup-pluginutils": { 949 | "version": "2.8.2", 950 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 951 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 952 | "dev": true, 953 | "requires": { 954 | "estree-walker": "^0.6.1" 955 | } 956 | } 957 | } 958 | }, 959 | "rollup-plugin-terser": { 960 | "version": "7.0.2", 961 | "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", 962 | "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", 963 | "dev": true, 964 | "requires": { 965 | "@babel/code-frame": "^7.10.4", 966 | "jest-worker": "^26.2.1", 967 | "serialize-javascript": "^4.0.0", 968 | "terser": "^5.0.0" 969 | } 970 | }, 971 | "safe-buffer": { 972 | "version": "5.2.1", 973 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 974 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 975 | "dev": true 976 | }, 977 | "serialize-javascript": { 978 | "version": "4.0.0", 979 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", 980 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", 981 | "dev": true, 982 | "requires": { 983 | "randombytes": "^2.1.0" 984 | } 985 | }, 986 | "source-map": { 987 | "version": "0.6.1", 988 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 989 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 990 | "dev": true 991 | }, 992 | "sourcemap-codec": { 993 | "version": "1.4.6", 994 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", 995 | "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", 996 | "dev": true 997 | }, 998 | "streamsearch": { 999 | "version": "1.1.0", 1000 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1001 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1002 | "dev": true 1003 | }, 1004 | "style-mod": { 1005 | "version": "4.0.0", 1006 | "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz", 1007 | "integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==" 1008 | }, 1009 | "supports-color": { 1010 | "version": "5.5.0", 1011 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1012 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1013 | "dev": true, 1014 | "requires": { 1015 | "has-flag": "^3.0.0" 1016 | } 1017 | }, 1018 | "terser": { 1019 | "version": "5.14.2", 1020 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", 1021 | "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", 1022 | "dev": true, 1023 | "requires": { 1024 | "@jridgewell/source-map": "^0.3.2", 1025 | "acorn": "^8.5.0", 1026 | "commander": "^2.20.0", 1027 | "source-map-support": "~0.5.20" 1028 | }, 1029 | "dependencies": { 1030 | "source-map-support": { 1031 | "version": "0.5.21", 1032 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1033 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1034 | "dev": true, 1035 | "requires": { 1036 | "buffer-from": "^1.0.0", 1037 | "source-map": "^0.6.0" 1038 | } 1039 | } 1040 | } 1041 | }, 1042 | "trouter": { 1043 | "version": "2.0.1", 1044 | "resolved": "https://registry.npmjs.org/trouter/-/trouter-2.0.1.tgz", 1045 | "integrity": "sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==", 1046 | "dev": true, 1047 | "requires": { 1048 | "matchit": "^1.0.0" 1049 | } 1050 | }, 1051 | "typescript": { 1052 | "version": "4.3.5", 1053 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", 1054 | "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", 1055 | "dev": true 1056 | }, 1057 | "upper-case": { 1058 | "version": "1.1.3", 1059 | "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", 1060 | "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", 1061 | "dev": true 1062 | }, 1063 | "w3c-keyname": { 1064 | "version": "2.2.4", 1065 | "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz", 1066 | "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==" 1067 | } 1068 | } 1069 | } 1070 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro-panel", 3 | "version": "2.0.0", 4 | "description": "Website editing components for microformats2 and micropub", 5 | "scripts": { 6 | "build": "rollup -c", 7 | "devserve": "node devserver.js", 8 | "prepublishOnly": "npm run build" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/unrelentingtech/micro-panel.git" 13 | }, 14 | "keywords": [ 15 | "micropub", 16 | "microformats", 17 | "indieweb", 18 | "webcomponent", 19 | "web-components", 20 | "custom-element" 21 | ], 22 | "author": "Val Packett ", 23 | "license": "Unlicense", 24 | "bugs": { 25 | "url": "https://github.com/unrelentingtech/micro-panel/issues" 26 | }, 27 | "homepage": "https://github.com/unrelentingtech/micro-panel#readme", 28 | "devDependencies": { 29 | "@rollup/plugin-node-resolve": "^13.1.2", 30 | "busboy": "^1.3.0", 31 | "es-module-devserver": "^0.1.3", 32 | "polka": "^0.5.2", 33 | "rollup": "^2.62.0", 34 | "rollup-plugin-minify-html-literals": "^1.2.6", 35 | "rollup-plugin-terser": "^7.0.2" 36 | }, 37 | "dependencies": { 38 | "@codemirror/basic-setup": "^0.19.1", 39 | "@codemirror/lang-html": "^0.19.4", 40 | "@codemirror/lang-json": "^0.19.1", 41 | "@codemirror/lang-markdown": "^0.19.3", 42 | "@wessberg/color": "^1.0.5", 43 | "immer": "^9.0.7", 44 | "lit": "^2.0.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import {terser} from 'rollup-plugin-terser' 3 | // import minifyHTML from 'rollup-plugin-minify-html-literals' // https://github.com/asyncLiz/minify-html-literals/issues/42 4 | 5 | export default { 6 | input: 'src/micro-panel-all.js', 7 | manualChunks(id) { 8 | if (id.includes('@codemirror')) { 9 | return 'micro-panel-codemirror' 10 | } 11 | }, 12 | output: [ 13 | { 14 | format: 'es', 15 | dir: 'dist', 16 | sourcemap: true, 17 | } 18 | ], 19 | plugins: [ 20 | resolve(), 21 | // minifyHTML(), 22 | terser({ 23 | ecma: 2020, 24 | module: true, 25 | warnings: true, 26 | }), 27 | ], 28 | preserveEntrySignatures: 'strict', 29 | } 30 | -------------------------------------------------------------------------------- /src/indie-action.js: -------------------------------------------------------------------------------- 1 | import { mpe } from './util.js' 2 | 3 | function matchingKey (action) { 4 | switch (action) { 5 | case 'like': return 'like-of' 6 | case 'repost': return 'repost-of' 7 | case 'bookmark': return 'bookmark-of' 8 | case 'quotation': return 'quotation-of' 9 | case 'tag': return 'tag-of' 10 | default: return 'in-reply-to' 11 | } 12 | } 13 | 14 | export default class IndieAction extends HTMLElement { 15 | connectedCallback () { 16 | for (const el of this.querySelectorAll('a, button')) { 17 | el.addEventListener('click', e => { 18 | e.preventDefault() 19 | e.stopPropagation() 20 | mpe().newReaction(matchingKey(this.getAttribute('do')), this.getAttribute('with')) 21 | }) 22 | } 23 | } 24 | } 25 | 26 | customElements.define('indie-action', IndieAction) 27 | -------------------------------------------------------------------------------- /src/micro-panel-action.js: -------------------------------------------------------------------------------- 1 | import { mpe } from './util.js' 2 | 3 | export default class MicroPanelAction extends HTMLElement { 4 | connectedCallback () { 5 | for (const el of this.querySelectorAll('a, button')) { 6 | el.addEventListener('click', e => mpe().editEntry(this.getAttribute('with'))) 7 | } 8 | } 9 | } 10 | 11 | customElements.define('micro-panel-action', MicroPanelAction) 12 | -------------------------------------------------------------------------------- /src/micro-panel-all.js: -------------------------------------------------------------------------------- 1 | // https://github.com/immerjs/immer/issues/557 2 | window.process = {env: {NODE_ENV: 'production'}} 3 | 4 | import { enablePatches } from 'immer' 5 | enablePatches() 6 | 7 | import './micro-panel-editor.js' 8 | import './micro-panel-toolbar.js' 9 | import './micro-panel-action.js' 10 | import './indie-action.js' 11 | -------------------------------------------------------------------------------- /src/micro-panel-editor-entry.js: -------------------------------------------------------------------------------- 1 | import './mp-code-mirror.js' 2 | import { rgbToHex, rgbTupleToRgb, hexToRgbTuple } from '@wessberg/color' 3 | import { LitElement, html, css } from 'lit' 4 | import { reportError, upload, geolocate, reverseGeocode, sharedStyles, icons, iconCode } from './util.js' 5 | import produce, { produceWithPatches, applyPatches } from 'immer' 6 | 7 | export default class MicroPanelEditorEntry extends LitElement { 8 | static get properties () { 9 | return { 10 | defaultctype: { type: String }, 11 | entry: { type: Object }, entryIsNew: { type: Boolean }, setEntry: { type: Function }, 12 | hiddenProps: { type: Object }, 13 | openUploaders: { type: Object }, uploadQueues: { type: Object }, 14 | openJsonEditors: { type: Object }, jsonParseError: { type: Object }, 15 | media: /* endpoint */ { type: String }, mediatoken: { type: String }, mediafirehose: { type: String }, 16 | cats: /* suggestions */ { type: Array }, 17 | } 18 | } 19 | 20 | constructor () { 21 | super() 22 | this.hiddenProps = {} 23 | this.openUploaders = { photo: true, video: true, audio: true } 24 | this.uploadQueues = { photo: [], video: [], audio: [] } 25 | this.openJsonEditors = { filter: true, unfilter: true, 'feed-settings': true, 'site-settings': true, 'site-web-push-subscriptions': true, } 26 | this.jsonParseError = {} 27 | this.undos = [] 28 | this.redos = [] 29 | } 30 | 31 | connectedCallback () { 32 | super.connectedCallback() 33 | if (this.mediafirehose) { 34 | this.mediaEventSrc = new EventSource(this.mediafirehose) 35 | this.mediaEventSrc.addEventListener('message', e => { 36 | if (e.data.length < 2) { return } 37 | const { object, url } = JSON.parse(e.data) 38 | this._modify(this.entry, draft => { 39 | for (const propname of Object.keys(draft.properties)) { 40 | draft.properties[propname] = draft.properties[propname].map(o => 41 | o == url ? object : o) 42 | } 43 | }) 44 | }) 45 | } 46 | } 47 | 48 | static get styles() { 49 | return [ 50 | sharedStyles, 51 | css` 52 | :host { display: block; } 53 | fieldset { 54 | border: 0; 55 | margin: 1rem auto; 56 | padding: 0 0 0.25rem 0; 57 | box-shadow: rgba(20, 20, 20, 0.24) 0 0 6px; 58 | border-radius: var(--roundness); 59 | overflow: hidden; 60 | } 61 | @media (prefers-color-scheme: dark) { 62 | fieldset { 63 | box-shadow: var(--very-light-accent) 0 0 6px; 64 | } 65 | } 66 | header { 67 | background: var(--light-accent); 68 | color: var(--neutral); 69 | padding: 0.15rem 0.5rem; 70 | } 71 | .bar { 72 | margin-bottom: 0.25rem; 73 | } 74 | .bar button + label { 75 | margin-left: 0; 76 | } 77 | .input-row { 78 | padding: 0.3rem 0.5rem; 79 | display: flex; 80 | align-items: start; 81 | } 82 | fieldset > .input-row:nth-of-type(even) { 83 | background: rgba(25, 25, 25, 0.06); 84 | } 85 | @media (prefers-color-scheme: dark) { 86 | fieldset > .input-row:nth-of-type(even) { 87 | background: rgba(255, 255, 255, 0.08); 88 | } 89 | } 90 | .input-row input, .input-row textarea, .input-row mp-code-mirror, 91 | .input-row micro-panel-editor-entry, .input-row .media-editor, .input-row .error-value { 92 | flex: 1; 93 | } 94 | .input-row button { 95 | margin: 0 0.5rem; 96 | } 97 | .input-row button:last-child { 98 | margin-right: 0; 99 | } 100 | .input-row-labeled { 101 | align-items: baseline; 102 | } 103 | .input-row-photo-stuff input { 104 | width: 5em; 105 | } 106 | textarea { 107 | resize: vertical; 108 | min-height: 200px; 109 | } 110 | .error-value { 111 | color: #bb1111; 112 | } 113 | .json-error { 114 | background: #bb1111; 115 | color: #fff; 116 | padding: 0.5rem; 117 | } 118 | 119 | .cat-suggest { 120 | padding: 0.3rem; 121 | font-size: 1.1em; 122 | line-height: 2; 123 | } 124 | 125 | .upload-zone { 126 | position: relative; 127 | padding: 0.5rem; 128 | } 129 | .drag-overlay { 130 | display: none; 131 | } 132 | .dragging .drag-overlay { 133 | display: flex; 134 | align-items: center; 135 | justify-content: center; 136 | text-align: center; 137 | position: absolute; 138 | top: 0; 139 | right: 0; 140 | bottom: 0; 141 | left: 0; 142 | background: rgba(60, 60, 60, 0.8); 143 | color: #fff; 144 | } 145 | progress { 146 | display: block; 147 | width: 100%; 148 | margin: 0.1rem 0 0.4rem; 149 | } 150 | 151 | img, video { 152 | max-width: 100%; 153 | } 154 | .palette-row { 155 | flex-wrap: wrap; 156 | line-height: 1.9; 157 | } 158 | .palette-color, .input-row-photo-stuff label { 159 | border-right: 1px solid #999; 160 | padding-right: 0.5rem; 161 | margin-right: 0.5rem; 162 | } 163 | .palette-color:first-of-type { 164 | margin-left: 0.5rem; 165 | } 166 | .palette-color:last-of-type, .input-row-photo-stuff label:last-of-type { 167 | border-right: 0; 168 | } 169 | 170 | @media screen and (min-width: 700px) { 171 | :host(.root-editor) fieldset { width: 70%; } 172 | } 173 | ` 174 | ]; 175 | } 176 | 177 | render () { 178 | const { 179 | defaultctype, entry, entryIsNew, hiddenProps, openUploaders, uploadQueues, 180 | openJsonEditors, jsonParseError, media, mediatoken, cats 181 | } = this 182 | return html` 183 | ${(entry && entry.type) ? html` 184 |
185 | ${entry.type.map((tval, idx) => html` 186 |
187 | 188 | this._modify(draft => draft.type[idx] = e.target.value) 189 | } ?disabled=${!entryIsNew}> 190 | ${(idx === 0 || !entryIsNew) ? '' : html` 191 | 194 | `} 195 | ${!entryIsNew ? '' : html` 196 | 199 | `} 200 |
201 | `)} 202 |
203 | ` : ''} 204 | 205 | ${entry && entry.properties && Object.keys(entry.properties).map(propname => html` 206 |
207 |
208 | 211 | 212 | 221 | 225 | ${'geolocation' in navigator ? html` 226 | 231 | ` : ''} 232 | ${media && !openUploaders[propname] ? html` 233 | 237 | ` : ''} 238 | 241 |
242 | ${hiddenProps[propname] ? '' 243 | : openJsonEditors[propname] ? this._jsonEditor(entry, propname, jsonParseError) 244 | : (entry.properties[propname] && entry.properties[propname].map((propval, idx) => html` 245 |
246 | ${this._rowEditor(entry, propname, propval, idx, media, mediatoken, cats, defaultctype)} 247 | 250 |
251 | `))} 252 | ${(!hiddenProps[propname] && propname === 'category') ? html` 253 |
254 | ${[...cats].map(cat => entry.properties.category.includes(cat) ? '' : html` 255 | 256 | `)} 257 |
258 | ` : ''} 259 | ${(!hiddenProps[propname] && openUploaders[propname]) ? this._mediaUploader(entry, propname, media, mediatoken, uploadQueues) : ''} 260 |
261 | `)} 262 | 263 | ${(entry && entry.type && entry.type.length > 0 && entry.type[0] === 'h-geo') ? html` 264 |
265 | 271 |
272 | ` : ''} 273 | 274 |
275 | this.addNewProp(e, entry)}/> 276 | 277 |
278 | ` 279 | } 280 | 281 | _rowEditor (entry, propname, propval, idx, media, mediatoken, cats, defaultctype) { 282 | if (propname === 'site-css' && typeof propval === 'string') { 283 | return html` 284 | 285 | this._modify(draft => draft.properties[propname][idx] = v) 286 | }> 287 | ` 288 | } 289 | if (typeof propval === 'string') { 290 | return html` 291 | 292 | this._modify(draft => draft.properties[propname][idx] = e.target.value) 293 | }/> 294 | ` 295 | } 296 | if (propval === null) { 297 | return html`
null
` 298 | } 299 | if (typeof propval !== 'object') { 300 | return html`
Item of unsupported type ${typeof propval}
` 301 | } 302 | if (propname === 'subscriptions') { 303 | return html` 304 | Feed  305 | this._modify(draft => draft.properties[propname][idx].feed = e.target.value) 306 | }> entries: ${(propval.entries || []).length} 307 | ` 308 | } 309 | if ('type' in propval) { 310 | return html` 311 | this._modify(draft => draft.properties[propname][idx] = nentry)}> 315 | 316 | ` 317 | } 318 | if ('html' in propval) { 319 | return html` 320 | 321 | this._modify(draft => draft.properties[propname][idx].html = v) 322 | }> 323 | ` 324 | } 325 | if ('markdown' in propval) { 326 | return html` 327 | 328 | this._modify(draft => draft.properties[propname][idx].markdown = v) 329 | }> 330 | ` 331 | } 332 | if ('text' in propval) { 333 | return html` 334 | 335 | this._modify(draft => draft.properties[propname][idx].text = v) 336 | }> 337 | ` 338 | } 339 | if ('source' in propval) { 340 | return html` 341 |
342 | ${(propval.width || propval.height) ? html` 343 |
344 | 345 | [${propval.width}x${propval.height}] 346 |
` : ''} 347 | ${propval.source.map(src => html` 348 |
349 | Source ${src.type} ${src.original ? 'original' : ''} 350 | ${src.src ? html` <src>` : ''} 351 | ${src.srcset ? src.srcset.map(setitem => html` <width ${setitem.width}>`) : ''} 352 |
353 | `)} 354 | ${propval.palette ? html` 355 |
356 | Palette 357 | ${Array.isArray(propval.palette) && propval.palette.map((clr, i) => html` 358 | 366 | `)} 367 |
` : ''} 368 |
369 | 374 | 381 | 386 | 391 |
392 | 398 | 404 |
405 | ` 406 | } 407 | return html`
Unsupported object with keys: ${Object.keys(propval).join(', ')}
` 408 | } 409 | 410 | _jsonEditor (entry, propname, jsonParseError) { 411 | return html` 412 | 413 | this._modify(draft => { 414 | try { 415 | draft.properties[propname] = JSON.parse(v) 416 | this.jsonParseError = produce(jsonParseError, pes => { pes[propname] = null }) 417 | } catch (e) { 418 | this.jsonParseError = produce(jsonParseError, pes => { pes[propname] = e.toString() }) 419 | } 420 | }) 421 | }> 422 | ${jsonParseError[propname] ? html`
423 |

JSON parsing error! The changes are not saved when this error is present. Please fix the syntax in the editor above. The error is:

424 |

${jsonParseError[propname]}

425 |
` : ''} 426 | ` 427 | } 428 | 429 | _mediaUploader (entry, propname, media, mediatoken, uploadQueues) { 430 | return html` 431 |
{ 433 | e.stopPropagation() 434 | e.preventDefault() 435 | console.log(this) 436 | if (this.dragFirst) { 437 | this.dragSecond = true 438 | } else { 439 | this.dragFirst = true 440 | e.dataTransfer.dropEffect = 'copy' 441 | e.target.classList.add('dragging') 442 | } 443 | }} 444 | @dragover=${e => { 445 | e.stopPropagation() 446 | e.preventDefault() 447 | }} 448 | @dragleave=${e => { 449 | e.stopPropagation() 450 | e.preventDefault() 451 | if (this.dragSecond) { 452 | this.dragSecond = false 453 | } else { 454 | this.dragFirst = false 455 | } 456 | if (!this.dragFirst && !this.dragSecond) { 457 | for (const zone of this.renderRoot.querySelectorAll('.upload-zone')) 458 | zone.classList.remove('dragging') 459 | } 460 | }} 461 | @drop=${e => { 462 | e.stopPropagation() 463 | e.preventDefault() 464 | this.dragFirst = false 465 | this.dragSecond = false 466 | for (const zone of this.renderRoot.querySelectorAll('.upload-zone')) 467 | zone.classList.remove('dragging') 468 | this.uploadQueues = produce(uploadQueues, x => { 469 | for (const file of e.dataTransfer.files) { 470 | x[propname].push({ file }) 471 | console.log(file) 472 | } 473 | }) 474 | }}> 475 | Drag'n'drop or select { 476 | this.uploadQueues = produce(uploadQueues, x => { 477 | for (const file of e.target.files) { 478 | x[propname].push({ file }) 479 | console.log(file) 480 | } 481 | }) 482 | }}> 483 | to upload. 484 | ${uploadQueues[propname].length > 0 ? html` 485 |
486 | ${uploadQueues[propname].map(({ file, progress }, idx) => html` 487 |
488 |
489 |
${file.name}
490 | ${progress ? html`${progress}%` : ''} 491 |
492 | 495 |
496 | `)} 497 |
498 | 521 | ` : ''} 522 |
523 | Drop files here! 524 |
525 |
526 | ` 527 | } 528 | 529 | addNewProp (e, entry) { 530 | if ('key' in e && e.key !== 'Enter') { 531 | return 532 | } 533 | const inp = this.renderRoot.getElementById('new-prop-inp') 534 | const propName = inp.value 535 | this._modify(draft => { 536 | if (propName.length > 0 && !(propName in draft.properties)) { 537 | if (propName === 'photo' || propName === 'video' || propName === 'audio' || propName === 'location') { 538 | draft.properties[propName] = [] 539 | } else if (propName === 'content') { 540 | draft.properties[propName] = [{ [this.defaultctype]: '' }] 541 | } else { 542 | draft.properties[propName] = [''] 543 | } 544 | if ('x-micro-panel-deleted-properties' in draft) { 545 | draft['x-micro-panel-deleted-properties'] = draft['x-micro-panel-deleted-properties'].filter(x => x !== propName) 546 | } 547 | } 548 | }) 549 | inp.value = '' 550 | } 551 | 552 | _modify (fn) { 553 | // NOTE: propagating the entry property assignment up to the top component 554 | // NOTE: eat return value here to avoid returning assignment results 555 | const [newEntry, patches, inversePatches] = produceWithPatches(this.entry, draft => { fn(draft) }) 556 | this.setEntry(newEntry) 557 | this.undos.push([patches, inversePatches]) 558 | this.redos = [] 559 | } 560 | 561 | undo () { 562 | if (this.undos.length < 1) { 563 | return 564 | } 565 | const [patches, inversePatches] = this.undos.pop() 566 | this.setEntry(produce(this.entry, draft => { applyPatches(draft, inversePatches) })) 567 | this.redos.unshift(patches) 568 | } 569 | 570 | redo () { 571 | if (this.redos.length < 1) { 572 | return 573 | } 574 | const [newEntry, patches, inversePatches] = produceWithPatches(this.entry, draft => { applyPatches(draft, this.redos.shift()) }) 575 | this.setEntry(newEntry) 576 | this.undos.push([patches, inversePatches]) 577 | } 578 | 579 | get canUndo() { 580 | return this.undos.length > 0 581 | } 582 | 583 | get canRedo() { 584 | return this.redos.length > 0 585 | } 586 | } 587 | 588 | customElements.define('micro-panel-editor-entry', MicroPanelEditorEntry) 589 | -------------------------------------------------------------------------------- /src/micro-panel-editor.js: -------------------------------------------------------------------------------- 1 | import './micro-panel-editor-entry.js' 2 | import { LitElement, html, css } from 'lit' 3 | import { sharedStyles, icons, iconCode } from './util.js' 4 | 5 | function micropubGet(endpoint, qs) { 6 | return fetch(endpoint.indexOf('?') === -1 ? endpoint + '?' + qs : endpoint + '&' + qs, { 7 | credentials: 'include', 8 | headers: { 9 | 'Accept': 'application/json', 10 | }, 11 | }) 12 | } 13 | 14 | function micropubPost(endpoint, obj, csrf_key, csrf_val) { 15 | const headers = { 16 | 'Accept': 'application/json', 17 | 'Content-Type': 'application/json', 18 | } 19 | if (csrf_key && csrf_val) 20 | headers[csrf_key] = csrf_val 21 | return fetch(endpoint, { 22 | method: 'post', 23 | credentials: 'include', 24 | headers, 25 | body: JSON.stringify(obj), 26 | }) 27 | } 28 | 29 | export default class MicroPanelEditor extends LitElement { 30 | static get properties () { 31 | return { 32 | micropub: { type: String }, media: { type: String }, mediatoken: { type: String }, mediafirehose: { type: String }, 33 | defaultctype: { type: String }, 34 | originalUrl: { type: String }, 35 | entry: { type: Object }, 36 | entryIsNew: { type: Boolean }, entryIsModified: { type: Boolean }, 37 | requestInFlight: { type: Boolean }, 38 | cats: { type: Array }, 39 | } 40 | } 41 | 42 | constructor () { 43 | super() 44 | // Use Set to prevent duplicates. Set does keep insertion order! 45 | this.cats = new Set() 46 | for (const el of document.querySelectorAll('[data-mf-category]')) { 47 | const catname = el.dataset.mfCategory.trim() 48 | if (catname.length > 0) { 49 | this.cats.add(catname) 50 | } 51 | } 52 | } 53 | 54 | connectedCallback () { 55 | super.connectedCallback() 56 | // Quick reply/like/etc URL for e.g. indie-action 57 | const query = new URLSearchParams(document.location.search.substring(1)) 58 | if (query.has('mp-reaction')) { 59 | if (query.has('with')) { 60 | this.newReaction(query.get('mp-reaction'), query.get('with'), query.get('content')) 61 | } else { 62 | this.newEntry({ name: [], content: [{[this.defaultctype || 'html']: query.get('content')}], category: [], photo: [] }) 63 | } 64 | } 65 | } 66 | 67 | static get styles() { 68 | return [ 69 | sharedStyles, 70 | css` 71 | :host { 72 | display: flex; 73 | flex-direction: column; 74 | position: fixed; 75 | top: 0; 76 | bottom: 0; 77 | left: 0; 78 | right: 0; 79 | background: var(--neutral); 80 | z-index: 69420; 81 | } 82 | #root-editor { 83 | flex: 1; 84 | overflow-y: auto; 85 | -webkit-overflow-scrolling: touch; 86 | padding: var(--major-padding); 87 | } 88 | ` 89 | ] 90 | } 91 | 92 | get rootEditor() { 93 | return this.renderRoot.getElementById('root-editor') 94 | } 95 | 96 | render () { 97 | const { micropub, media, mediatoken, mediafirehose, defaultctype, originalUrl, entry, entryIsNew, entryIsModified, cats } = this 98 | return html` 99 |
100 | 101 |

micro-panel editor

102 | 104 | 106 | ${originalUrl ? html` 107 | 108 | ` : ''} 109 | ${entryIsNew ? html` 110 | 112 | ` : html` 113 | 115 | `} 116 |
117 | 118 | { 122 | this.entry = entry 123 | this.entryIsModified = true 124 | this.update() 125 | }}> 126 | 127 | ` 128 | } 129 | 130 | async close () { 131 | if (this.entryIsModified && !confirm('Abandon current modified entry?')) { 132 | return 133 | } 134 | if ('animate' in this && 'Animation' in window && 'finished' in Animation.prototype) { 135 | await this.animate({transform: ['none', 'translateY(100vh)']}, {duration: 300, easing: 'ease-out'}).finished 136 | } 137 | this.hidden = true 138 | document.body.style.overflow = this.oldBodyOverflow 139 | } 140 | 141 | show () { 142 | this.oldBodyOverflow = document.body.style.overflow 143 | document.body.style.overflow = 'hidden' 144 | this.hidden = false 145 | if ('animate' in this) { 146 | this.animate({transform: ['translateY(100vh)', 'none']}, {duration: 300, easing: 'ease-out'}) 147 | } 148 | } 149 | 150 | async editEntry (url) { 151 | this.entry = await (await micropubGet(this.micropub, `q=source&url=${encodeURIComponent(url)}`)).json() 152 | if (!this.entry.properties) { 153 | this.entry.properties = {} 154 | } 155 | for (const k of Object.keys(this.entry.properties)) { 156 | if (!this.entry.properties[k]) { 157 | this.entry.properties[k] = [] 158 | } 159 | } 160 | if (!this.entry.properties.url) { 161 | this.entry.properties.url = [] 162 | } 163 | if (this.entry.properties.url.length < 1) { 164 | this.entry.properties.url.push(url) 165 | } 166 | this.originalUrl = this.entry.properties.url[0] // Store to allow changing the URL 167 | this.entryIsNew = false 168 | this.entryIsModified = false 169 | this.show() 170 | } 171 | 172 | newEntry (properties = { name: [], 173 | content: [{ [this.defaultctype || 'html']: '' }], 174 | category: [], 175 | photo: [], 176 | location: [], 177 | }) { 178 | this.entry = { 179 | type: ['h-entry'], 180 | properties, 181 | } 182 | this.originalUrl = undefined 183 | this.entryIsNew = true 184 | this.entryIsModified = false 185 | this.show() 186 | } 187 | 188 | newReaction (kind, url, content) { 189 | this.newEntry({ 190 | [kind]: [url], 191 | content: [{ [this.defaultctype]: content || '' }], 192 | photo: [], 193 | }) 194 | this.entryIsModified = true 195 | } 196 | 197 | createEntry () { 198 | if (!confirm('Create this entry?')) { 199 | return 200 | } 201 | this._post({ 202 | type: this.entry.type, 203 | properties: this.entry.properties, 204 | }) 205 | } 206 | 207 | updateEntry () { 208 | const url = this.originalUrl || ((this.entry.properties || {}).url || [null])[0] 209 | if (!url) { 210 | return alert('Somehow, an entry with no URL! I have no idea how to save that.') 211 | } 212 | if (!confirm('Update this entry?')) { 213 | return 214 | } 215 | this._post({ 216 | action: 'update', 217 | url, 218 | replace: this.entry.properties, 219 | 'delete': this.entry['x-micro-panel-deleted-properties'] || [], 220 | }) 221 | } 222 | 223 | deleteEntry () { 224 | if (!this.originalUrl) { 225 | return alert('Somehow, an entry with no URL! I have no idea how to delete that.') 226 | } 227 | if (!confirm('Delete this entry?')) { 228 | return 229 | } 230 | this._post({ 231 | action: 'delete', 232 | url: this.originalUrl 233 | }) 234 | } 235 | 236 | async _post (data) { 237 | this.requestInFlight = true 238 | let resp 239 | try { 240 | resp = await micropubPost(this.micropub, data, this.getAttribute('csrfheader'), this.getAttribute('csrftoken')) 241 | } catch (e) { 242 | alert(`Couldn't save the entry! Got error: ${e}`) 243 | return 244 | } finally { 245 | this.requestInFlight = false 246 | } 247 | if (resp.status >= 300) { 248 | alert(`Couldn't save the entry! Got status: ${resp.status}`) 249 | return 250 | } 251 | this.entryIsModified = false 252 | this.close() 253 | location.href = resp.headers.has('Location') ? resp.headers.get('Location') : this.entry.properties.url[0] 254 | } 255 | 256 | } 257 | 258 | customElements.define('micro-panel-editor', MicroPanelEditor) 259 | -------------------------------------------------------------------------------- /src/micro-panel-toolbar.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html, css } from 'lit' 2 | import { mpe, sharedStyles, icons, iconCode } from './util.js' 3 | 4 | export default class MicroPanelToolbar extends LitElement { 5 | static get properties () { return { } } 6 | 7 | static get styles() { 8 | return [ 9 | sharedStyles, 10 | css` 11 | :host { 12 | display: block; 13 | position: fixed; 14 | top: 0; 15 | right: 0; 16 | border-bottom-left-radius: var(--roundness); 17 | overflow: hidden; 18 | opacity: 0.9; 19 | } 20 | :host(:hover) { 21 | opacity: 1; 22 | } 23 | ` 24 | ] 25 | } 26 | 27 | render () { 28 | return html` 29 |
30 |

micro-panel

31 | 32 | 33 |
34 | ` 35 | } 36 | 37 | } 38 | 39 | customElements.define('micro-panel-toolbar', MicroPanelToolbar) 40 | -------------------------------------------------------------------------------- /src/mp-code-mirror.js: -------------------------------------------------------------------------------- 1 | import { ReactiveElement } from '@lit/reactive-element' 2 | 3 | export default class CodeMirrorElement extends ReactiveElement { 4 | static get properties () { 5 | return { 6 | value: { type: String, attribute: false }, 7 | setValue: { type: Function }, 8 | } 9 | } 10 | 11 | constructor () { 12 | super() 13 | this.value = '' 14 | } 15 | 16 | async connectedCallback() { 17 | super.connectedCallback() 18 | const { EditorState, EditorView, basicSetup } = await import('@codemirror/basic-setup') 19 | const { HighlightStyle, tags } = await import ('@codemirror/highlight') 20 | const { markdown } = await import('@codemirror/lang-markdown') 21 | const { html } = await import('@codemirror/lang-html') 22 | const { css } = await import('@codemirror/lang-css') 23 | const { json } = await import('@codemirror/lang-json') 24 | const dark = window.matchMedia('(prefers-color-scheme: dark)').matches 25 | const exts = [ 26 | basicSetup, 27 | EditorView.lineWrapping, 28 | EditorView.updateListener.of(e => { 29 | if (e.focusChanged) this.setValue(e.state.doc.toString()) 30 | }), 31 | EditorView.theme({ 32 | "&.cm-focused .cm-selectionBackground, ::selection": { backgroundColor: 'var(--light-accent)', color: 'var(--neutral)' }, 33 | ".cm-gutters": { backgroundColor: 'var(--neutral-hover)' }, 34 | ".cm-activeLine": { backgroundColor: 'var(--very-light-accent)' }, 35 | ".cm-activeLineGutter": { backgroundColor: 'var(--light-accent)', color: 'var(--neutral)' }, 36 | "&.cm-focused .cm-cursor": { borderLeftColor: "var(--accent)" }, 37 | }, { dark }), 38 | HighlightStyle.define([ 39 | { tag: tags.link, color: (dark ? '#3af0e8' : '#3a67f0') }, 40 | { tag: tags.invalid, color: (dark ? '#f03a3a' : '#bb0d0d') }, 41 | { tag: [tags.heading, tags.strong], fontWeight: "bold" }, 42 | { tag: tags.emphasis, fontStyle: "italic" }, 43 | { tag: tags.strikethrough, textDecoration: "line-through" }, 44 | ]), 45 | ] 46 | if (this.getAttribute('lang') === 'markdown') 47 | exts.push(markdown()) 48 | if (this.getAttribute('lang') === 'html') 49 | exts.push(html()) 50 | if (this.getAttribute('lang') === 'css') 51 | exts.push(css()) 52 | if (this.getAttribute('lang') === 'json') 53 | exts.push(json()) 54 | this.cmview = new EditorView({ 55 | state: EditorState.create({ 56 | doc: this.value, 57 | extensions: exts, 58 | }), 59 | root: this.renderRoot, 60 | parent: this.renderRoot, 61 | }) 62 | } 63 | 64 | update(changed) { 65 | if (changed.has('value') && this.cmview) 66 | this.cmview.dispatch({ 67 | changes: { 68 | from: 0, 69 | to: this.cmview.state.doc.toString().length, 70 | insert: this.value, 71 | } 72 | }) 73 | super.update(changed) 74 | } 75 | } 76 | 77 | customElements.define('mp-code-mirror', CodeMirrorElement) 78 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | import { html, css, svg } from 'lit' 2 | 3 | export function mpe () { 4 | return document.querySelector('micro-panel-editor') 5 | } 6 | 7 | export function reportError (e) { 8 | console.error(e) 9 | alert(e.toString()) 10 | } 11 | 12 | export function upload (endpoint, token, file, onProgress) { 13 | return new Promise((resolve, reject) => { 14 | const xhr = new XMLHttpRequest() 15 | xhr.upload.addEventListener('progress', onProgress, false) 16 | xhr.addEventListener('load', e => { 17 | if (xhr.status >= 200 && xhr.status < 300) { 18 | const ctype = xhr.getResponseHeader('Content-Type') 19 | if (ctype && ctype.includes('application/json')) { 20 | resolve(JSON.parse(xhr.responseText)) 21 | } else { 22 | resolve(xhr.getResponseHeader('Location')) 23 | } 24 | } else { 25 | reject(xhr.status) 26 | } 27 | }) 28 | xhr.addEventListener('error', reject) 29 | xhr.addEventListener('abort', reject) 30 | xhr.addEventListener('timeout', reject) 31 | xhr.upload.addEventListener('error', reject) 32 | xhr.upload.addEventListener('abort', reject) 33 | xhr.upload.addEventListener('timeout', reject) 34 | xhr.open('post', endpoint) 35 | if (token) { // simple token for using a different domain 36 | xhr.setRequestHeader('Authorization', 'MediaToken ' + token) 37 | } else { // requires non-HttpOnly cookies 38 | const bearerCookie = document.cookie.split('; ').find(c => c.split('=')[0] === 'Bearer') 39 | if (bearerCookie) { 40 | xhr.setRequestHeader('Authorization', 'Bearer ' + bearerCookie) 41 | } else { // same origin 42 | xhr.withCredentials = true 43 | } 44 | } 45 | const form = new FormData() 46 | form.append('file', file) 47 | xhr.send(form) 48 | }) 49 | } 50 | 51 | export async function geolocate () { 52 | const pos = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject)) 53 | return { 54 | 'type': ['h-geo'], 55 | properties: { 56 | latitude: [pos.coords.latitude.toString()], 57 | longitude: [pos.coords.longitude.toString()], 58 | } 59 | } 60 | } 61 | 62 | export async function reverseGeocode ({ latitude, longitude }) { 63 | const resp = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${encodeURIComponent(latitude[0])}&lon=${encodeURIComponent(longitude[0])}`) 64 | const { address } = await resp.json() 65 | const result = { 66 | latitude, longitude, 67 | 'street-address': [address.road], 68 | 'extended-address': [address.suburb], 69 | locality: [address.hamlet || address.village || address.town || address.city], 70 | region: [(address.county && address.state) ? (address.county + ', ' + address.state) : (address.state || address.county)], 71 | 'postal-code': [address.postcode], 72 | 'country-name': [address.country], 73 | 'country-code': [address.country_code && address.country_code.toUpperCase()], 74 | } 75 | for (const k of Object.keys(result)) { 76 | if (!result[k][0]) { 77 | delete result[k] 78 | } 79 | } 80 | return result 81 | } 82 | 83 | export const sharedStyles = css` 84 | :host { 85 | line-height: 1.15; 86 | -webkit-text-size-adjust: 100%; 87 | --major-padding: var(--micro-panel-major-padding, 0.5rem); 88 | --roundness: var(--micro-panel-roundness, 4px); 89 | --neutral: var(--micro-panel-neutral, #fefefe); 90 | --neutral-hover: var(--micro-panel-neutral-hover, #ebebeb); 91 | --accent: var(--micro-panel-accent, rgb(0, 137, 123)); 92 | --accent-hover: var(--micro-panel-accent-hover, rgb(0, 107, 103)); 93 | --light-accent: var(--micro-panel-light-accent, rgba(0, 137, 123, 0.55)); 94 | --very-light-accent: var(--micro-panel-very-light-accent, rgba(0, 137, 123, 0.1)); 95 | --text: var(--micro-panel-text, #333); 96 | color: var(--text); 97 | } 98 | :host([hidden]) { display: none !important; } 99 | ::selection { background: var(--light-accent); color: var(--neutral); } 100 | * { box-sizing: border-box; } 101 | 102 | @media (prefers-color-scheme: dark) { 103 | :host { 104 | --micro-panel-neutral: #292929; 105 | --micro-panel-neutral-hover: #323232; 106 | --micro-panel-text: #efefef; 107 | --micro-panel-accent: rgb(195, 230, 119); 108 | --micro-panel-accent-hover: rgb(215, 250, 139); 109 | --micro-panel-light-accent: rgba(195, 230, 119, 0.69); 110 | --micro-panel-very-light-accent: rgba(195, 230, 119, 0.15); 111 | } 112 | } 113 | 114 | input, textarea, button, .input-row mp-code-mirror { 115 | text-transform: none; 116 | border-radius: var(--roundness); 117 | padding: 0.4rem; 118 | outline: none; 119 | background: var(--neutral); 120 | color: var(--text); 121 | border: 1px solid var(--accent); 122 | vertical-align: baseline; 123 | } 124 | input[type=color] { 125 | padding: 0; 126 | vertical-align: middle; 127 | height: 1.6rem; 128 | width: 1.6rem; 129 | border-radius: 10rem; 130 | overflow: hidden; 131 | } 132 | ::-webkit-color-swatch-wrapper { 133 | padding: 0; 134 | } 135 | mp-code-mirror { display: block; padding: 0 !important; overflow: hidden; } 136 | ::-moz-focus-inner, ::-moz-color-swatch { 137 | border-style: none; 138 | padding: 0; 139 | } 140 | input:-moz-focusring, textarea:-moz-focusring, button:-moz-focusring { 141 | outline: 1px dotted ButtonText; 142 | } 143 | button { 144 | padding: 0.4rem 0.8rem; 145 | overflow: visible; 146 | -webkit-appearance: button; 147 | background: var(--accent); 148 | color: var(--neutral); 149 | transition: 0.15s ease-in transform; 150 | } 151 | button:focus { 152 | transform: scale(1.1); 153 | } 154 | button:not([disabled]):active { 155 | transform: scale(0.9); 156 | } 157 | button:hover { 158 | background: var(--accent-hover); 159 | } 160 | button[disabled] { 161 | opacity: 0.5; 162 | } 163 | 164 | a { 165 | color: var(--accent); 166 | } 167 | a:hover { 168 | color: var(--accent-hover); 169 | } 170 | 171 | .icon, .icon-button { 172 | vertical-align: middle; 173 | } 174 | button:not(.icon-button) .icon { 175 | width: 1.4rem; 176 | height: 1.4rem; 177 | margin-top: -0.2rem; 178 | } 179 | .icon-button { 180 | padding: 0.2rem; 181 | border-radius: 100rem; 182 | background: transparent; 183 | color: inherit; 184 | border: none; 185 | } 186 | .icon-button:hover { 187 | background: rgba(10, 10, 10, 0.2); 188 | } 189 | @media (prefers-color-scheme: dark) { 190 | .icon-button:hover { 191 | background: rgba(230, 230, 230, 0.2); 192 | } 193 | } 194 | 195 | .inverted { 196 | background: var(--accent); 197 | color: var(--neutral); 198 | } 199 | .inverted button { 200 | background: var(--neutral); 201 | color: var(--accent); 202 | border-color: var(--neutral); 203 | } 204 | .inverted button:hover { 205 | background: var(--neutral-hover); 206 | } 207 | 208 | .header-bar { 209 | padding: var(--major-padding); 210 | line-height: 25px; 211 | } 212 | .bar { 213 | margin: 0; 214 | display: flex; 215 | align-items: baseline; 216 | } 217 | .bar label, .bar h1, .bar .stretchy { 218 | flex: 1; 219 | } 220 | .bar > * { 221 | margin: 0 0.4rem; 222 | } 223 | .bar > *:first-child { 224 | margin-left: 0; 225 | } 226 | .bar > * + * { 227 | margin-right: 0; 228 | } 229 | .bar h1 { 230 | margin: 0; 231 | font-size: 1.1rem; 232 | } 233 | .bar button { 234 | font-size: 1rem; 235 | } 236 | ` 237 | 238 | /* https://materialdesignicons.com */ 239 | export const icons = { 240 | /* by Google | Apache 2 licensed: */ 241 | plus: svg` 242 | 243 | `, 244 | minus: svg` 245 | 246 | `, 247 | chevronUp: svg` 248 | 249 | `, 250 | chevronDown: svg` 251 | 252 | `, 253 | leadPencil: svg` 254 | 255 | `, 256 | close: svg` 257 | 258 | `, 259 | cloudUpload: svg` 260 | 261 | `, 262 | undo: svg` 263 | 264 | `, 265 | redo: svg` 266 | 267 | `, 268 | trash: svg` 269 | 270 | `, 271 | save: svg` 272 | 273 | `, 274 | /* by Austin Andrews @Templarian | OFL licensed: */ 275 | json: svg` 276 | 277 | `, 278 | /* by Cody @XT3000 | OFL licensed: */ 279 | mapMarkerPlus: svg` 280 | 281 | `, 282 | } 283 | 284 | export function iconCode (icon, title = null) { 285 | return html` 286 | ${icon} 287 | ${title ? html`${title}` : ''} 288 | ` 289 | } 290 | --------------------------------------------------------------------------------