Home

tailpolicy @main - refs - log -
-
https://git.jolheiser.com/tailpolicy.git
Tailscale policy editor on your tailnet
tree log patch
add prism and submit Signed-off-by: jolheiser <git@jolheiser.com>
Signature
-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQCRayGJ9cunQwqmX4f/A636/149XWG6VBYSZ6OZdiUwfL5FrHK53uOPCbmeHluJQvH 11/8lfrvITz7RWsyeGxwQ= -----END SSH SIGNATURE-----
jolheiser <git@jolheiser.com>
5 months ago
9 changed files, 204 additions(+), 21 deletions(-)
.gitignoreflags.goindex.gomain.gonix/module.nixstatic/ctp.cssstatic/index.tmplstatic/prism.cssstatic/prism.js
M .gitignore.gitignore
1
2
3
4
5
6
7
diff --git a/.gitignore b/.gitignore
index dc5678396ee36caf8e2508a04ea7172f711d8b84..20867276934e392b5449491d64335bd2c03458a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 .tsnet/
+.policy/
M flags.goflags.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
diff --git a/flags.go b/flags.go
index ad39886431f753fcd48f4feaccb11fde317dc034..96eac9b866d551c424debb7c1ac0514e114c67c5 100644
--- a/flags.go
+++ b/flags.go
@@ -5,6 +5,7 @@
 // Args are commandline arguments
 type Args struct {
 	RepoUrl      string
+	RepoOrigin   string
 	RepoLocation string
 	Hostname     string
 	DataDir      string
@@ -16,12 +17,14 @@
 // Flags is the [flag.FlagSet] for tailpolicy
 func Flags() (*Args, *flag.FlagSet) {
 	args := &Args{
-		DataDir:  ".tsnet",
-		Hostname: "policy",
+		DataDir:      ".tsnet",
+		Hostname:     "policy",
+		RepoLocation: ".policy",
 	}
 	fs := flag.NewFlagSet("tailpolicy", flag.ExitOnError)
 	fs.StringVar(&args.RepoUrl, "repo-url", args.RepoUrl, "Repo URL for the Tailscale policy")
-	fs.StringVar(&args.RepoLocation, "repo-location", args.RepoLocation, "Repo location to perform git operations against")
+	fs.StringVar(&args.RepoOrigin, "repo-origin", args.RepoOrigin, "Repo URL for cloning/pushing")
+	fs.StringVar(&args.RepoLocation, "repo-location", args.RepoLocation, "Repo storage location")
 	fs.StringVar(&args.Hostname, "hostname", args.Hostname, "Tailnet hostname")
 	fs.StringVar(&args.DataDir, "data-dir", args.DataDir, "tsnet data directory")
 	fs.StringVar(&args.AuthKey, "auth-key", args.AuthKey, "tsnet auth key")
M index.goindex.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
diff --git a/index.go b/index.go
index f04d3049ba13d44c1d77457f66b2ea8392deef41..acf87e8b25914215a9879f29787616ca7264de65 100644
--- a/index.go
+++ b/index.go
@@ -78,7 +78,7 @@ 				}
 				if err := repo.Push(&git.PushOptions{}); err != nil {
 					return err
 				}
-				http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+				http.Redirect(w, r, "/", http.StatusSeeOther)
 				return nil
 			default:
 				return errors.New("method not allowed")
M main.gomain.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
diff --git a/main.go b/main.go
index 9592ed3ca44ddb057811b669703a6d32aa0b1bde..11666fb37ae0aa5c3089576496e7cab0ad508001 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	_ "embed"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -16,6 +17,15 @@ 	"go.jolheiser.com/tailpolicy/static"
 	"go.jolheiser.com/tailroute"
 )
 
+var (
+	//go:embed static/prism.js
+	prismJS []byte
+	//go:embed static/prism.css
+	prismCSS []byte
+	//go:embed static/ctp.css
+	ctpCSS []byte
+)
+
 func maine() error {
 	args, fs := Flags()
 	if err := fs.Parse(os.Args[1:]); err != nil {
@@ -29,6 +39,18 @@ 	mux := http.NewServeMux()
 	mux.HandleFunc("/", Index(args.RepoUrl, args.RepoLocation))
 	mux.HandleFunc("/preview", preview(args.RepoUrl))
 	mux.HandleFunc("/tailwind.css", static.TailwindHandler)
+	mux.HandleFunc("/prism.js", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "text/javascript")
+		w.Write(prismJS)
+	})
+	mux.HandleFunc("/prism.css", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "text/css")
+		w.Write(prismCSS)
+	})
+	mux.HandleFunc("/ctp.css", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "text/css")
+		w.Write(ctpCSS)
+	})
 	r := tailroute.Router{
 		Tailnet: mux,
 	}
@@ -38,7 +60,7 @@ 		if err := os.MkdirAll(filepath.Dir(args.RepoLocation), os.ModePerm); err != nil {
 			return err
 		}
 		if _, err := git.PlainClone(args.RepoLocation, false, &git.CloneOptions{
-			URL: args.RepoUrl,
+			URL: args.RepoOrigin,
 		}); err != nil {
 			return err
 		}
M nix/module.nixnix/module.nix
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
diff --git a/nix/module.nix b/nix/module.nix
index 6fc0342232a168228551040f8e45bbbc831fe7cf..c8413873315281056d427ff7151b78e5de2b81b8 100644
--- a/nix/module.nix
+++ b/nix/module.nix
@@ -27,9 +27,14 @@         repo-url = mkOption {
           type = types.str;
           description = "Repo URL for the Tailscale policy";
         };
+        repo-origin = mkOption {
+          type = types.str;
+          description = "Repo URL for cloning/pushing";
+        };
         repo-location = mkOption {
           type = types.str;
-          description = "Repo location to perform git operations against";
+          description = "Repo storage location";
+          default = "/var/lib/tailpolicy/policy";
         };
         hostname = mkOption {
           type = types.str;
@@ -39,7 +44,7 @@         };
         data-dir = mkOption {
           type = types.str;
           description = "tsnet data directory";
-          default = ".tsnet";
+          default = "/var/lib/tailpolicy/tsnet";
 
         };
         auth-key = mkOption {
I static/ctp.css
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
diff --git a/static/ctp.css b/static/ctp.css
new file mode 100644
index 0000000000000000000000000000000000000000..ba2456d283f355e6b9e73d3e1db1ca3e77fc7bfc
--- /dev/null
+++ b/static/ctp.css
@@ -0,0 +1,135 @@
+code[class*="language-"],
+pre[class*="language-"] {
+  color: #cdd6f4;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+  background: #181825;
+}
+
+/* https://prismjs.com/tokens.html */
+
+.token.keyword {
+  color: #cba6f7;
+}
+
+.token.builtin {
+  color: #f38ba8;
+}
+
+.token.class-name {
+  color: #f9e2af;
+}
+
+.token.function {
+  color: #89b4fa;
+}
+
+.token.boolean,
+.token.number {
+  color: #fab387;
+}
+
+.token.string,
+.token.char {
+  color: #a6e3a1;
+}
+
+.token.symbol {
+  color: #f9e2af;
+}
+
+.token.regex {
+  color: #f5c2e7;
+}
+
+.token.url {
+  color: #a6e3a1;
+}
+
+.token.operator {
+  color: #89dceb;
+}
+
+.token.variable {
+  color: #cdd6f4;
+}
+
+.token.constant {
+  color: #fab387;
+}
+
+.token.property {
+  color: #89b4fa;
+}
+
+.token.punctuation {
+  color: #9399b2;
+}
+
+.token.important {
+  color: #cba6f7;
+}
+
+.token.comment {
+  color: #9399b2;
+}
+
+.token.tag {
+  color: #89b4fa;
+}
+
+.token.attr-name {
+  color: #f9e2af;
+}
+
+.token.attr-value {
+  color: #a6e3a1;
+}
+
+.token.namespace {
+  color: #f9e2af;
+}
+
+.token.prolog,
+.token.doctype {
+  color: #cba6f7;
+}
+
+.token.cdata {
+  color: #94e2d5;
+}
+
+.token.entity {
+  color: #f38ba8;
+}
+
+.token.atrule {
+  color: #cba6f7;
+}
+
+.token.selector {
+  color: #89b4fa;
+}
+
+/* Diff */
+
+.token.deleted {
+  color: #f38ba8;
+}
+
+.token.inserted {
+  color: #a6e3a1
+}
+
+/* Other */
+
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+
+.token.italic {
+  font-style: italic;
+}
\ No newline at end of file
M static/index.tmplstatic/index.tmpl
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
diff --git a/static/index.tmpl b/static/index.tmpl
index 63f20bbf87c08234887e8681b8430477412f1720..5252efeada0b5ae9de13ce8ff8f588f68bae7bbd 100644
--- a/static/index.tmpl
+++ b/static/index.tmpl
@@ -4,24 +4,30 @@
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>cfg</title>
+  <title>tailpolicy</title>
   <link rel="stylesheet" href="/tailwind.css" />
+  <link rel="stylesheet" href="/prism.css" />
+  <link rel="stylesheet" href="/ctp.css" />
 </head>
 
 <body class="latte dark:mocha bg-base/50 dark:bg-base/95 text-text">
-  <div class="flex h-screen">
-    <div class="flex-1 flex flex-col p-4">
-      <h2 class="text-2xl font-bold mb-4">Jsonnet</h2>
-      <textarea id="input" name="content"
-        class="bg-base dark:bg-base/50 flex-1 p-2 border rounded">{{ .content }}</textarea>
-    </div>
-    <div class="flex-1 flex flex-col p-4">
-      <h2 class="text-2xl font-bold mb-4">Policy</h2>
-      <textarea id="output" class="bg-base dark:bg-base/50 flex-1 p-2 border rounded"
-        placeholder="Converted policy will appear here..." readonly></textarea>
+  <form method="POST">
+    <div class="flex h-screen">
+      <div class="flex-1 flex flex-col p-4">
+        <h2 class="text-2xl font-bold mb-4">Jsonnet</h2>
+        <textarea id="input" name="content" class="bg-base dark:bg-base/50 flex-1 p-2 border rounded">{{ .content
+          }}</textarea>
+      </div>
+      <div class="flex-1 flex flex-col p-4">
+        <h2 class="text-2xl font-bold mb-4">Policy</h2>
+        <pre class="bg-base dark:bg-base/50 flex-1 p-2 border rounded"><code id="output" class="language-json5"></code>
+        </pre>
+      </div>
     </div>
-  </div>
+    <button type="submit">Save ACL</button>
+  </form>
 
+  <script src="/prism.js" data-manual></script>
   <script>
     const $inputText = document.getElementById('input');
     const $outputText = document.getElementById('output');
@@ -37,6 +43,7 @@         this.selectionStart = this.selectionEnd = start + 2;
       }
     });
 
+    if ($inputText.value !== "") convert();
 
     function convert() {
       fetch('/preview', {
@@ -50,11 +57,12 @@         }),
       })
         .then(response => response.text())
         .then(data => {
-          $outputText.value = data;
+          $outputText.innerHTML = data;
+          Prism.highlightElement($outputText);
         })
         .catch(error => {
           console.error('Error:', error);
-          $outputText.value = 'An error occurred during conversion.';
+          $outputText.innerHTML = 'An error occurred during conversion.';
         });
     }
 
I static/prism.css
1
2
3
4
5
6
7
8
9
diff --git a/static/prism.css b/static/prism.css
new file mode 100644
index 0000000000000000000000000000000000000000..e72328992ae4b0d3233a7a5418f6d1c1685eceee
--- /dev/null
+++ b/static/prism.css
@@ -0,0 +1,3 @@
+/* PrismJS 1.29.0
+https://prismjs.com/download.html#themes=prism&languages=json+json5 */
+code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
I static/prism.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/static/prism.js b/static/prism.js
new file mode 100644
index 0000000000000000000000000000000000000000..bf1ab8d82cbf2fa756bf45aadfa9fb619a618c0a
--- /dev/null
+++ b/static/prism.js
@@ -0,0 +1,6 @@
+/* PrismJS 1.29.0
+https://prismjs.com/download.html#themes=prism&languages=json+json5 */
+var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case"Object":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case"Array":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return"none"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,"gi"),""),e.classList.add("language-"+t)},currentScript:function(){if("undefined"==typeof document)return null;if("currentScript"in document)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName("script");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);"Object"!==u||i[l(s)]?"Array"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run("before-all-elements-highlight",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&"pre"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.element.innerHTML=s.highlightedCode,a.hooks.run("after-highlight",s),a.hooks.run("complete",s),r&&r.call(s.element)}if(a.hooks.run("before-sanity-check",s),(o=s.element.parentElement)&&"pre"===o.nodeName.toLowerCase()&&!o.hasAttribute("tabindex")&&o.setAttribute("tabindex","0"),!s.code)return a.hooks.run("complete",s),void(r&&r.call(s.element));if(a.hooks.run("before-highlight",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run("before-tokenize",r),!r.grammar)throw new Error('The language "'+r.language+'" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run("after-tokenize",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+","+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+"g")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(j<O||"string"==typeof C.value);C=C.next)L++,j+=C.value.length;L--,E=e.slice(A,j),P.index-=A}else if(!(P=l(b,0,E,m)))continue;S=P.index;var N=P[0],_=E.slice(0,S),M=E.slice(S+N.length),W=A+E.length;g&&W>g.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if("string"==typeof n)return n;if(Array.isArray(n)){var r="";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:"span",classes:["token",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,"&quot;")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
+Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json;
+!function(n){var e=/("|')(?:\\(?:\r\n?|\n|.)|(?!\1)[^\\\r\n])*\1/;n.languages.json5=n.languages.extend("json",{property:[{pattern:RegExp(e.source+"(?=\\s*:)"),greedy:!0},{pattern:/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/,alias:"unquoted"}],string:{pattern:e,greedy:!0},number:/[+-]?\b(?:NaN|Infinity|0x[a-fA-F\d]+)\b|[+-]?(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[eE][+-]?\d+\b)?/})}(Prism);
+