├── .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 |
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 | Try editing!
40 |
41 |
42 | Or replying .
43 |
44 |
45 | Or liking .
46 |
47 |
48 |
49 |
50 |
51 | Will be suggested as categories:
52 | demo
53 | test
54 | hello
55 |
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 |
192 | this._modify(draft => draft.type.splice(idx, 1))
193 | } title="Delete this type" class="icon-button">${iconCode(icons.minus)}
194 | `}
195 | ${!entryIsNew ? '' : html`
196 |
197 | this._modify(draft => draft.type.push(''))
198 | } title="Add new type" class="icon-button">${iconCode(icons.plus)}
199 | `}
200 |
201 | `)}
202 |
203 | ` : ''}
204 |
205 | ${entry && entry.properties && Object.keys(entry.properties).map(propname => html`
206 |
207 |
208 | {
209 | this.hiddenProps = produce(hiddenProps, x => { x[propname] = !(x[propname] || false) })
210 | }} title="Toggle display of this property" class="icon-button">${iconCode(hiddenProps[propname] ? icons.chevronDown : icons.chevronUp)}
211 | ${propname}
212 |
213 | this._modify(draft => {
214 | delete draft.properties[propname]
215 | if (!('x-micro-panel-deleted-properties' in draft)) {
216 | draft['x-micro-panel-deleted-properties'] = []
217 | }
218 | draft['x-micro-panel-deleted-properties'].push(propname)
219 | })
220 | } title="Delete this property" class="icon-button">${iconCode(icons.minus)}
221 | {
222 | this.openJsonEditors = produce(openJsonEditors, x => { x[propname] = !(x[propname] || false) })
223 | this.jsonParseError = produce(jsonParseError, pes => { pes[propname] = null })
224 | }} title="Edit this property as JSON" class="icon-button">${iconCode(icons.json)}
225 | ${'geolocation' in navigator ? html`
226 |
227 | geolocate()
228 | .then(loc => this._modify(draft => draft.properties[propname].push(loc)))
229 | .catch(reportError)
230 | } title="Add geolocation" class="icon-button">${iconCode(icons.mapMarkerPlus)}
231 | ` : ''}
232 | ${media && !openUploaders[propname] ? html`
233 | {
234 | this.uploadQueues = produce(uploadQueues, x => { x[propname] = [] })
235 | this.openUploaders = produce(openUploaders, x => { x[propname] = true })
236 | }} title="Upload media files" class="icon-button">${iconCode(icons.cloudUpload)}
237 | ` : ''}
238 |
239 | this._modify(draft => draft.properties[propname].push(''))
240 | } title="Add new value to this property" class="icon-button">${iconCode(icons.plus)}
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 |
248 | this._modify(draft => draft.properties[propname].splice(idx, 1))
249 | } title="Delete this value" class="icon-button">${iconCode(icons.minus)}
250 |
251 | `))}
252 | ${(!hiddenProps[propname] && propname === 'category') ? html`
253 |
254 | ${[...cats].map(cat => entry.properties.category.includes(cat) ? '' : html`
255 | this._modify(draft => draft.properties.category.push(cat))}>${cat}
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 | reverseGeocode(entry.properties).then(props =>
266 | this._modify(draft => {
267 | draft.type[0] = 'h-adr'
268 | draft.properties = props
269 | })).catch(reportError)
270 | }>Find street address via Nominatim
271 |
272 | ` : ''}
273 |
274 |
275 | this.addNewProp(e, entry)}/>
276 | this.addNewProp(e, entry)} class="icon-button">${iconCode(icons.plus)}
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 |
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 |
493 | this.uploadQueues = produce(uploadQueues, x => { x[propname].splice(idx, 1) })
494 | } title="Delete this file from the queue" class="icon-button">${iconCode(icons.minus)}
495 |
496 | `)}
497 |
498 |
{
499 | for (const [idx, wrapper] of uploadQueues[propname].entries()) {
500 | try {
501 | const result = await upload(media, mediatoken, wrapper.file, e =>
502 | this.uploadQueues = produce(this.uploadQueues, x => {
503 | const idxx = x[propname].findIndex(y => y.file === wrapper.file)
504 | if (e.lengthComputable) {
505 | x[propname][idxx].progress = e.loaded / e.total * 100
506 | } else {
507 | x[propname][idxx].progress = 'ind'
508 | }
509 | }))
510 | this._modify(this.entry, draft => {
511 | draft.properties[propname].push(result)
512 | })
513 | this.uploadQueues = produce(this.uploadQueues, x => {
514 | x[propname].splice(x[propname].findIndex(y => y.file === wrapper.file), 1)
515 | })
516 | } catch (e) {
517 | reportError(e)
518 | }
519 | }
520 | }}>Upload!
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 |
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 |
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 |
--------------------------------------------------------------------------------