|
1 | | -# Javascript embedded in the URL: run and edit |
2 | 1 |
|
3 | | -Run code and edit javascript code in the browser. No server required. |
| 2 | +# Markdown viewer [metaframe](https://docs.metapage.io/) |
4 | 3 |
|
5 | | -### Longer description |
6 | | -Run arbitrary user javascript modules embedded in the URL. Designed for [metapages](https://metapage.io) so you can connect inputs + outputs to other metaframe URLs. |
7 | 4 |
|
8 | | -## Javascript high level |
| 5 | +Metaframe (configurable website) for showing markdown. |
9 | 6 |
|
10 | | - - code is an es6 module |
11 | | - - top-level `await` |
12 | | - - export a function `onInputs` to listen to inputs |
13 | | - - send outputs with `setOutput`/`setOutputs` (predefined functions available in your module) |
14 | | - - export a function `onResize` to listen to window/div resizes |
15 | | - - use es6 module imports, or add any css / npm modules to the page, they are embedded in the URL |
16 | 7 |
|
17 | | -## Useful code snippets |
| 8 | +The URL contains the entire markdown text: |
18 | 9 |
|
19 | | -### Handling Inputs and outputs in code |
20 | 10 |
|
21 | | -Simply export a function (arrow function also good 👍) called `onInputs`: |
| 11 | +🔗 [`https://markdownv2.mtfm.io#?md=`](https://markdownv2.mtfm.io/#?md=)`<encoded markdown>` |
22 | 12 |
|
23 | | -```javascript |
24 | | -// regular js function |
25 | | -export function onInputs(inputs) { |
26 | | - // do something here |
27 | | - // inputs is a plain object (key and values) |
28 | | -} |
29 | | -// OR arrow function |
30 | | -export const onInputs = (inputs) => { |
31 | | - // do something here |
32 | | - // inputs is a plain object (key and values) |
33 | | -} |
34 | | -``` |
35 | 13 |
|
36 | | -To send outputs, there are two functions in the scope `setOutput` and `setOutputs`: |
| 14 | +This makes the URL |
37 | 15 |
|
38 | | -```javascript |
39 | | -// send a single JSON output |
40 | | -setOutput("outputname", 42); |
| 16 | +- **editable** |
| 17 | +- **sharable** |
| 18 | +- **embeddable** |
| 19 | +- **standalone** |
41 | 20 |
|
42 | | -// send an output object of keys+values |
43 | | -setOutputs({ |
44 | | - outputname:true, |
45 | | - someOtherOutputName: "bar", |
46 | | -}); |
47 | | -``` |
48 | 21 |
|
49 | | -Output values can be strings, JSON, objects, arrays, numbers, ArrayBuffers, typed arrays such as `Uint8Array`; |
| 22 | +## **How to create / edit** |
50 | 23 |
|
51 | | -### Define Inputs and Outputs |
52 | 24 |
|
53 | | -In `Settings` you can define inputs and outputs. This doesn't change how the code runs, but it allows much easier connecting upstream and downstream metaframes when editing [metapages](https://metapage.io). |
| 25 | +To modify the options, edit them in the webpage, then copy the URL with the copy button. The URL contains all content and options. |
54 | 26 |
|
55 | | -In this example, we defined an input: `input.json` and an output `data.csv`: |
56 | 27 |
|
57 | | - |
| 28 | +**Create/Edit Option 1: Pasting or write markdown in the embedded editor** |
58 | 29 |
|
59 | | -You will see these inputs and outputs automatically in the metapage editor. |
60 | 30 |
|
61 | | -### The root display div element is exposed in the scope |
| 31 | +Manually write or paste in markdown into the embedded editor |
62 | 32 |
|
63 | | -The root display div is exposed in the script scope: the name is `root` and the id is also `root`: |
64 | 33 |
|
65 | | -```javascript |
66 | | -console.log(root.id) |
67 | | -// logs "root" |
68 | | -// Add any custom dome elements into "root". |
69 | | -``` |
| 34 | +When finished, copy the URL, or click on the copy button at the top. |
70 | 35 |
|
71 | | -You can also just get it with: |
72 | | -```javascript |
73 | | -document.getElementById("root") |
74 | | -``` |
75 | 36 |
|
76 | | -### Window resize |
| 37 | +**Create/Edit Option 3: Automatically generate URLs** |
77 | 38 |
|
78 | | -Simply export a function (arrow function also good 👍) called `onResize`. This will be called when either the window resizes event and/or the local `div` element resizes: |
79 | 39 |
|
80 | | -```javascript |
81 | | -// regular js function |
82 | | -export function onResize(width, height) { |
83 | | - // Your own code here, handling the resize of the root div |
84 | | -} |
85 | | -// OR arrow function |
86 | | -export const onResize = (width, height) => { |
87 | | - // Your own code here, handling the resize of the root div |
88 | | -} |
89 | | -``` |
| 40 | +See below for a code example |
| 41 | + |
| 42 | + |
| 43 | +## Dynamically add content |
| 44 | + |
90 | 45 |
|
91 | | -### Unload/cleanup |
| 46 | +### Dynamic content via metapages |
92 | 47 |
|
93 | | -When iterating with the code editor, the script is re-run. In some cases, this can cause problems as multiple listeners maybe responding to the same event. |
94 | 48 |
|
95 | | -This is not an issue when simply running the page once with code, only when develping iteratively. |
| 49 | +Data flow when the metaframe is embedded. This allows dynamic markdown rendering |
96 | 50 |
|
97 | | -To have your script cleaned up because of new script (when editing), declare a function `cleanup`, this will be called prior to the updated script re-running: |
98 | 51 |
|
99 | | -```javascript |
100 | | -// regular js function |
101 | | -export function cleanup() { |
102 | | - console.log("internal scriptUnload call") |
103 | | - // do your cleanup here |
104 | | -} |
105 | | -// OR arrow function |
106 | | -export const cleanup = () => { |
107 | | - // do your cleanup here |
108 | | -} |
| 52 | +```mermaid |
| 53 | +graph LR |
| 54 | + classDef mtfm fill:#fff,stroke:#32AEE4,stroke-width:2px; |
| 55 | + linkStyle default stroke:#32AEE4,stroke-width:2px,color:black |
| 56 | +
|
| 57 | + mfup["upstream metaframe"]:::mtfm |
| 58 | + subgraph metaframe ["markdownv2.mtfm.io"] |
| 59 | + M[Render markdown]:::mtfm |
| 60 | + inputs[metaframe inputs]:::mtfm --> M |
| 61 | + end |
| 62 | + url["encoded in URL hash parameters"]:::mtfm --> |"#?md=[encoded markdown]"| M |
| 63 | + mfup --> |"md"| inputs |
| 64 | +
|
| 65 | + style metaframe fill:#fff,stroke:#32AEE4,stroke-width:2px; |
109 | 66 | ``` |
110 | 67 |
|
111 | | -### Wait until page `load` |
| 68 | +- input pipes: |
| 69 | + - `md` |
| 70 | + - Raw markdown text |
112 | 71 |
|
113 | | -You don't need to wait for the `load` event: your script will not run until `load` event fires. |
| 72 | +Any text coming in those input pipes will be immediately rendered. If there is markdown embedded in the URL, that will be rendered first, but replaced by any markdown from input pipes. |
114 | 73 |
|
115 | | -### Logging to the display (instead of console) |
| 74 | + - output pipes |
| 75 | + - `click` |
| 76 | + - From clickable mermaid flowcharts |
116 | 77 |
|
117 | | -Some globally available functions for logging: |
118 | 78 |
|
119 | | -```javascript |
| 79 | +## Embed markdown renderer in another application |
120 | 80 |
|
121 | | - log("something here"); |
122 | | - logStdout("something here"); |
123 | | - logStderr("an error"); |
124 | 81 |
|
125 | | -``` |
| 82 | +It requires no installation, and is secure due to cross-origin isolation. |
| 83 | + |
126 | 84 |
|
127 | | -These will be added to the root div (see below) so if your own code manipulates the root div, it could be overwritten. This is mostly useful for headless code. |
| 85 | +### E.g. React |
128 | 86 |
|
129 | | -### Misc |
130 | 87 |
|
131 | | - - `"use strict"` is automatically added to the top of the module code. |
| 88 | +The help menu on this website uses this page in a external iframed element. While this uses `@metapages/metapage-react` to make some things more convenient, you can also just embed a `<iframe src="<url>" />` element directly: |
132 | 89 |
|
133 | | -## Connect upstream/downstream metaframes |
134 | 90 |
|
135 | | -[](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqFkU9LxDAQxb9KiJcVuqC9KBG8uN7sRT1JL2MzaYP5RzJhle1-d9NWd1HBnVMm770fw8yOd14iF7yPEAb28Ng6VqozkNIGFbPhzhsfmdLGiLPrK3mhVJUo-jf81a63WtIg6vB-szBSfl2gFgkC9Lh8T2VQ0eUqh5JEsLNBRbB4ztbr21G7kCmNrFk9Dzqx5qAKIb7m-YmqT6OOgWYS2OgzLVLU_VCGkX7r_jBOpOr_Uuhk63jFLUYLWpYV7yah5TSgxZaL8pSoIBtqeev2xZqDBMJ7qclHLhSYhBWHTP7pw3VcUMz4bdpoKMu1B1cA9-L9sccZ0iy3nU-8_wSxOqSj) |
| 91 | +```typescript |
| 92 | +import { useCallback } from 'react'; |
136 | 93 |
|
| 94 | +import { MetaframeStandaloneComponent } from '@metapages/metapage-react'; |
| 95 | + |
| 96 | +export const PanelMarkdownEditor: React.FC<{markdown:string}> = ({markdown) => { |
| 97 | + |
| 98 | + return ( |
| 99 | + <div> |
| 100 | + <MetaframeStandaloneComponent |
| 101 | + url="https://markdownv2.mtfm.io/#?button=invisible&md=JTIzJTIwVGhpcyUyMGlzJTIweW91ciUyMG1hcmtkb3duJTBBJTBBVGhlJTIwVVJMJTIwY29udGFpbnMlMjBhbGwlMjB0aGUlMjBjb250ZW50&menuhidden=true&options=JTdCJTIyZGlzcGxheW1vZGUlMjIlM0ElMjJkZWZhdWx0JTIyJTdE" |
| 102 | + inputs={{ value: markdown}} |
| 103 | + /> |
| 104 | + </div> |
| 105 | + ); |
| 106 | +}; |
| 107 | +``` |
137 | 108 |
|
138 | | -## Examples |
139 | 109 |
|
140 | | - - [Rotate stick on canvas from incoming angles](https://app.metapages.org/#?definition=JTdCJTIybWV0YSUyMiUzQSU3QiUyMmxheW91dHMlMjIlM0ElN0IlMjJyZWFjdC1ncmlkLWxheW91dCUyMiUzQSU3QiUyMmRvY3MlMjIlM0ElMjJodHRwcyUzQSUyRiUyRnd3dy5ucG1qcy5jb20lMkZwYWNrYWdlJTJGcmVhY3QtZ3JpZC1sYXlvdXQlMjIlMkMlMjJsYXlvdXQlMjIlM0ElNUIlN0IlMjJoJTIyJTNBMyUyQyUyMmklMjIlM0ElMjJyYW5kb20tZGF0YSUyMiUyQyUyMm1vdmVkJTIyJTNBZmFsc2UlMkMlMjJzdGF0aWMlMjIlM0FmYWxzZSUyQyUyMnclMjIlM0E1JTJDJTIyeCUyMiUzQTAlMkMlMjJ5JTIyJTNBMCU3RCUyQyU3QiUyMmglMjIlM0EzJTJDJTIyaSUyMiUzQSUyMnN0aWNrJTIyJTJDJTIybW92ZWQlMjIlM0FmYWxzZSUyQyUyMnN0YXRpYyUyMiUzQWZhbHNlJTJDJTIydyUyMiUzQTclMkMlMjJ4JTIyJTNBNSUyQyUyMnklMjIlM0EwJTdEJTVEJTJDJTIycHJvcHMlMjIlM0ElN0IlMjJjb2xzJTIyJTNBMTIlMkMlMjJjb250YWluZXJQYWRkaW5nJTIyJTNBJTVCNSUyQzUlNUQlMkMlMjJtYXJnaW4lMjIlM0ElNUIxMCUyQzIwJTVEJTJDJTIycm93SGVpZ2h0JTIyJTNBMTAwJTdEJTdEJTdEJTdEJTJDJTIybWV0YWZyYW1lcyUyMiUzQSU3QiUyMnJhbmRvbS1kYXRhJTIyJTNBJTdCJTIydXJsJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZyYW5kb20ubXRmbS5pbyUyRiUyMyUzRmRpc3RyaWJ1dGlvbiUzRGV5SmthWE4wY21saWRYUnBiMjRpT2lKemFXNGlMQ0ptY21WeGRXVnVZM2tpT2pFd0xDSnZjSFJwYjI1eklqcDdJbWx1WTNKbGJXVnVkQ0k2TUM0eGZTd2ljMmh2ZDA5MWRIQjFkQ0k2Wm1Gc2MyVjklMjIlN0QlMkMlMjJzdGljayUyMiUzQSU3QiUyMmlucHV0cyUyMiUzQSU1QiU3QiUyMm1ldGFmcmFtZSUyMiUzQSUyMnJhbmRvbS1kYXRhJTIyJTJDJTIyc291cmNlJTIyJTNBJTIydiUyMiUyQyUyMnRhcmdldCUyMiUzQSUyMnklMjIlN0QlNUQlMkMlMjJ1cmwlMjIlM0ElMjJodHRwcyUzQSUyRiUyRmpzLm10Zm0uaW8lMkYlMjMlM0ZlZGl0JTNEMSUyNmpzJTNEWTI5dWMzUWxNakJrYVhOd2IzTmxjbk1sTWpBbE0wUWxNakFsTlVJbE5VUWxNMElsTUVGamIyNXpkQ1V5TUhKdmIzUWxNakFsTTBRbE1qQmtiMk4xYldWdWRDNW5aWFJGYkdWdFpXNTBRbmxKWkNnbE1qSnliMjkwSlRJeUtTVXpRaVV3UVNVd1FTVXlSaVV5UmlVeU1ISmxibVJsY2lVeU1IUm9aU1V5TUdGdVoyeGxKVEJCZG1GeUpUSXdZMkZ1ZG1GekpUSXdKVE5FSlRJd1pHOWpkVzFsYm5RdVkzSmxZWFJsUld4bGJXVnVkQ2dsTWpKallXNTJZWE1sTWpJcEpUTkNKVEJCWTJGdWRtRnpMbmRwWkhSb0pUSXdKVE5FSlRJd01qQXdKVE5DSlRCQlkyRnVkbUZ6TG1obGFXZG9kQ1V5TUNVelJDVXlNREl3TUNVelFpVXdRWEp2YjNRdVlYQndaVzVrUTJocGJHUW9ZMkZ1ZG1GektTVXpRaVV3UVNVd1FXTnZibk4wSlRJd1kzUjRKVEl3SlRORUpUSXdZMkZ1ZG1GekxtZGxkRU52Ym5SbGVIUW9KVEl5TW1RbE1qSXBKVE5DSlRCQlkyOXVjM1FsTWpBbE5VSjNKVEpESlRJd2FDVTFSQ1V5TUNVelJDVXlNQ1UxUWpJd0pUSkRKVEl3WTJGdWRtRnpMbWhsYVdkb2RDVTFSQ1V6UWlVd1FXTnZibk4wSlRJd0pUVkNlQ1V5UXlVeU1Ia2xOVVFsTWpBbE0wUWxNakFsTlVKallXNTJZWE11ZDJsa2RHZ2xNakFsTWtZbE1qQXlKVEpESlRJd1kyRnVkbUZ6TG1obGFXZG9kQ1V5TUNVeVJpVXlNRElsTlVRbE0wSWxNRUVsTUVGamIyNXpkQ1V5TUdSeVlYY2xNakFsTTBRbE1qQW9jbUZrS1NVeU1DVXpSQ1V6UlNVeU1DVTNRaVV3UVNVeU1DVXlNR04wZUM1amJHVmhjbEpsWTNRb01DVXlReVV5TURBbE1rTWxNakJqWVc1MllYTXVkMmxrZEdnbE1rTWxNakJqWVc1MllYTXVhR1ZwWjJoMEtTVXpRaVV3UVNVd1FTVXlNQ1V5TUNVeVJpVXlSaVV5TUdacGNuTjBKVEl3YzJGMlpTVXlNSFJvWlNVeU1IVnVkSEpoYm5Oc1lYUmxaQ1V5Um5WdWNtOTBZWFJsWkNVeU1HTnZiblJsZUhRbE1FRWxNakFsTWpCamRIZ3VjMkYyWlNncEpUTkNKVEJCSlRCQkpUSXdKVEl3WTNSNExtSmxaMmx1VUdGMGFDZ3BKVE5DSlRCQkpUSXdKVEl3SlRKR0pUSkdKVEl3Ylc5MlpTVXlNSFJvWlNVeU1ISnZkR0YwYVc5dUpUSXdjRzlwYm5RbE1qQjBieVV5TUhSb1pTVXlNR05sYm5SbGNpVXlNRzltSlRJd2RHaGxKVEl3Y21WamRDVXdRU1V5TUNVeU1HTjBlQzUwY21GdWMyeGhkR1VvZUNVeVF5VXlNR2dsTWpBbE1rWWxNakF5S1NVelFpVXdRU1V5TUNVeU1DVXlSaVV5UmlVeU1ISnZkR0YwWlNVeU1IUm9aU1V5TUhKbFkzUWxNRUVsTWpBbE1qQmpkSGd1Y205MFlYUmxLSEpoWkNrbE0wSWxNRUVsTUVFbE1qQWxNakFsTWtZbE1rWWxNakJrY21GM0pUSXdkR2hsSlRJd2NtVmpkQ1V5TUc5dUpUSXdkR2hsSlRJd2RISmhibk5tYjNKdFpXUWxNakJqYjI1MFpYaDBKVEJCSlRJd0pUSXdZM1I0TG5KbFkzUW9MWGNsTWpBbE1rWWxNakF5SlRKREpUSXdMV2dsTWpBbE1rWWxNakF5SlRKREpUSXdkeVV5UXlVeU1HZ3BKVE5DSlRCQkpUQkJKVEl3SlRJd1kzUjRMbVpwYkd4VGRIbHNaU1V5TUNVelJDVXlNQ1V5TW1keVpXVnVKVEl5SlROQ0pUQkJKVEl3SlRJd1kzUjRMbVpwYkd3b0tTVXpRaVV3UVNVd1FTVXlNQ1V5TUdOMGVDNXlaWE4wYjNKbEtDa2xNMElsTUVFbE4wUWxNMElsTUVFbE1FRWxNa1lsTWtZbE1qQk1hWE4wWlc0bE1qQjBieVV5TUdGdUpUSXdhVzV3ZFhRbE1FRmthWE53YjNObGNuTXVjSFZ6YUNnbE1FRWxNakFsTWpCdFpYUmhabkpoYldVdWIyNUpibkIxZEhNb0tHbHVjSFYwY3lrbE1qQWxNMFFsTTBVbE1qQWxOMElsTUVFbE1qQWxNakFsTWpBbE1qQnRaWFJoWm5KaGJXVXVjMlYwVDNWMGNIVjBjeWhwYm5CMWRITXBKVE5DSlRCQkpUSXdKVEl3SlRJd0pUSXdZMjl1YzNRbE1qQjVKVEl3SlRORUpUSXdhVzV3ZFhSekpUVkNKVEl5ZVNVeU1pVTFSQ1V6UWlVd1FTVXlNQ1V5TUNVeU1DVXlNR2xtSlRJd0tDRjVLU1V5TUNVM1FpVXdRU1V5TUNVeU1DVXlNQ1V5TUNVeU1DVXlNSEpsZEhWeWJpVXpRaVV3UVNVeU1DVXlNQ1V5TUNVeU1DVTNSQ1V3UVNVeU1DVXlNQ1V5TUNVeU1HUnlZWGNvZVNrbE0wSWxNRUVsTWpBbE1qQWxOMFFwSlRCQktTVXpRaVV3UVdSeVlYY29NQ2tsTTBJbE1FRWxNRUVsTWtZbE1rWWxNakJTWlhSMWNtNGxNakJoSlRJd1kyeGxZVzUxY0NVeU1HWjFibU4wYVc5dUpUQkJkMmx1Wkc5M0xuTmpjbWx3ZEZWdWJHOWhaQ1V5TUNVelJDVXlNQ2dwSlRJd0pUTkVKVE5GSlRJd0pUZENKVEJCSlRJd0pUSXdkMmhwYkdVbE1qQW9aR2x6Y0c5elpYSnpMbXhsYm1kMGFDVXlNQ1V6UlNVeU1EQXBKVEl3SlRkQ0pUQkJKVEl3SlRJd0pUSXdKVEl3WkdsemNHOXpaWEp6TG5CdmNDZ3BLQ2tsTTBJbE1FRWxNakFsTWpBbE4wUWxNRUVsTjBRbE0wSWxNRUUlM0QlMjZtb2R1bGVzJTNESlRWQ0pUVkUlMjZvcHRpb25zJTNEZXlKb2FXUmxiV1Z1ZFdsbWFXWnlZVzFsSWpwMGNuVmxMQ0p0YjJSbElqb2lhbUYyWVhOamNtbHdkQ0lzSW5OaGRtVnNiMkZrYVc1b1lYTm9JanAwY25WbExDSjBhR1Z0WlNJNklteHBaMmgwSW4wJTNEJTIyJTdEJTdEJTJDJTIycGx1Z2lucyUyMiUzQSU1QiUyMmh0dHBzJTNBJTJGJTJGbWVybWFpZC5tdGZtLmlvJTJGJTIzJTNGaG0lM0RkaXNhYmxlZCUyMiUyQyUyMmh0dHBzJTNBJTJGJTJGZWRpdG9yLm10Zm0uaW8lMkYlMjMlM0ZobSUzRGRpc2FibGVkJTI2b3B0aW9ucyUzREpUZENKVEl5Ylc5a1pTVXlNaVV6UVNVeU1tcHpiMjRsTWpJbE1rTWxNakp6WVhabGJHOWhaR2x1YUdGemFDVXlNaVV6UVhSeWRXVWxNa01sTWpKMGFHVnRaU1V5TWlVelFTVXlNbXhwWjJoMEpUSXlKVGRFJTIyJTVEJTJDJTIydmVyc2lvbiUyMiUzQSUyMjAuMyUyMiU3RA==&minimal=false) |
| 110 | +## Developers: creating your own markdown links |
141 | 111 |
|
142 | 112 |
|
143 | | -## Description |
| 113 | +You can generate your own markdown pages to display in your own webpages easily as embedded iframes. |
144 | 114 |
|
145 | | - - user created javascript |
146 | | - - user defined css modules |
147 | 115 |
|
148 | | -Think of this like [codepen](https://codepen.io) or [others](https://www.sitepoint.com/code-playgrounds/) but stripped down focusing on core essentials, performance, and durability. |
| 116 | +The markdown text is simply encoded as follows: |
149 | 117 |
|
150 | | -This website is also a [metaframe](https://docs.metapage.io/docs/what-is-a-metaframe): connect metaframes together into apps/workflows/dashboards: [metapages](https://docs.metapage.io/docs) |
151 | 118 |
|
152 | | -[Github Repo](https://github.com/metapages/metaframe-js) |
| 119 | +```typescript |
| 120 | +export const encodeMarkdown = (md: string) => { |
| 121 | + var b64 = window.btoa(encodeURIComponent(md)); |
| 122 | + return b64; |
| 123 | +}; |
| 124 | + |
| 125 | +const yourRawMarkdown = "### Title\n\nSome content"; |
| 126 | +const encodedMarkdown = encodeMarkdown(yourRawMarkdown); |
| 127 | +const url = `https://markdownv2.mtfm.io/#?button=invisible&md=${encodedMarkdown}&menuhidden=true&options=JTdCJTIyZGlzcGxheW1vZGUlMjIlM0ElMjJkZWZhdWx0JTIyJTdE` |
| 128 | +``` |
153 | 129 |
|
154 | | -**Architecture:** |
155 | 130 |
|
| 131 | +Just take that `url` and embed as described above, or via embedded as an iframe: |
156 | 132 |
|
157 | | - - no state is stored on the server (all embedded in the URL) |
158 | | - - this imposes some limits but current URL lengths are large or not specifically limited |
159 | | - - The server simply serves a little `index.html` |
160 | | - - The client then runs the embedded javascript (the javascript code is **not** sent to the server) |
161 | 133 |
|
162 | | -The server runs on https://deno.com/deploy which is |
| 134 | +`<iframe src={url} />` |
163 | 135 |
|
164 | | - - simple |
165 | | - - fast |
166 | | - - very performant |
167 | | - - deploys immediately with a simply push to the repository |
168 | | - - 🌟🌟🌟🌟🌟 |
0 commit comments