tmux-weblink 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +70 -0
  2. package/dist/assets/addon-fit-D5N335P2.js +16 -0
  3. package/dist/assets/terminal-client.js +4 -0
  4. package/dist/assets/xterm-NI46XXQY.js +51 -0
  5. package/dist/assets/xterm.css +285 -0
  6. package/dist/browser/terminal-client.js +4 -0
  7. package/dist/frontend.js +1 -0
  8. package/dist/index.js +12 -0
  9. package/dist/lib/agent-detect.js +3 -0
  10. package/dist/lib/agents-watch.js +2 -0
  11. package/dist/lib/atomicWrite.js +1 -0
  12. package/dist/lib/auditLog.js +1 -0
  13. package/dist/lib/auth.js +2 -0
  14. package/dist/lib/cli.js +32 -0
  15. package/dist/lib/commandbar.js +555 -0
  16. package/dist/lib/db.js +1 -0
  17. package/dist/lib/drawer-resize.js +166 -0
  18. package/dist/lib/drawer-script.js +8 -0
  19. package/dist/lib/ext-loader.js +1 -0
  20. package/dist/lib/html.js +1 -0
  21. package/dist/lib/icons.js +1 -0
  22. package/dist/lib/image-upload.js +1 -0
  23. package/dist/lib/load-env.js +6 -0
  24. package/dist/lib/mobile-toolbar.js +313 -0
  25. package/dist/lib/notes-db.js +18 -0
  26. package/dist/lib/notes-drawer.js +210 -0
  27. package/dist/lib/notes-utils.js +15 -0
  28. package/dist/lib/pages/agents-index.js +105 -0
  29. package/dist/lib/pages/history-index.js +85 -0
  30. package/dist/lib/pages/landing.js +78 -0
  31. package/dist/lib/pages/login.js +191 -0
  32. package/dist/lib/pages/notes-index.js +65 -0
  33. package/dist/lib/pages/notes-page.js +158 -0
  34. package/dist/lib/pages/quick-commands.js +361 -0
  35. package/dist/lib/pages/schedule-index.js +392 -0
  36. package/dist/lib/pages/settings.js +206 -0
  37. package/dist/lib/pages/terminal.js +364 -0
  38. package/dist/lib/pinned-views.js +1 -0
  39. package/dist/lib/plugins.js +3 -0
  40. package/dist/lib/quick-commands.js +1 -0
  41. package/dist/lib/rateLimiter.js +2 -0
  42. package/dist/lib/schedule-delay.js +9 -0
  43. package/dist/lib/scheduler-drawer.js +379 -0
  44. package/dist/lib/scheduler.js +1 -0
  45. package/dist/lib/security-config.js +2 -0
  46. package/dist/lib/session-access.js +1 -0
  47. package/dist/lib/session-windows.js +1 -0
  48. package/dist/lib/sessions-drawer.js +634 -0
  49. package/dist/lib/sessions-sidebar.js +1 -0
  50. package/dist/lib/settings.js +2 -0
  51. package/dist/lib/setup-features.js +1 -0
  52. package/dist/lib/setup-prompts.js +3 -0
  53. package/dist/lib/shared-layout.js +412 -0
  54. package/dist/lib/state-paths.js +1 -0
  55. package/dist/lib/terminal-config.js +1 -0
  56. package/dist/lib/theme-store.js +2 -0
  57. package/dist/lib/theme.js +28 -0
  58. package/dist/lib/themes/dark-cove.js +1 -0
  59. package/dist/lib/themes/ghostty.js +1 -0
  60. package/dist/lib/themes/index.js +1 -0
  61. package/dist/lib/themes/types.js +0 -0
  62. package/dist/lib/themes/vscode.js +1 -0
  63. package/dist/lib/themes/warm-clay.js +1 -0
  64. package/dist/lib/tmux-capture.js +5 -0
  65. package/dist/lib/tmux-control.js +2 -0
  66. package/dist/lib/tmux-panes.js +2 -0
  67. package/dist/lib/tmux-windows.js +4 -0
  68. package/dist/lib/watched-panes.js +1 -0
  69. package/dist/lib/window-history.js +1 -0
  70. package/dist/lib/window-labels.js +1 -0
  71. package/dist/lib/windows-drawer.js +364 -0
  72. package/dist/lib/ws-message.js +1 -0
  73. package/dist/sessions.js +2 -0
  74. package/package.json +62 -0
  75. package/scripts/fix-pty-perms.mjs +16 -0
@@ -0,0 +1,191 @@
1
+ import{cssVarsStyle as d}from"../theme.js";function p(r){const{setupMode:e,error:o,theme:i}=r,a=e?"Set your password":"Welcome back",t=e?"Set Password":"Sign in",s=e?"Choose a strong password to secure this server.":"Enter your password to continue to your sessions.",l=o?`<div class="login-error">${n(o)}</div>`:"";return`<!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
+ <title>${e?"Set Password":"Sign In"} \xB7 tmux-weblink</title>
8
+ <style>
9
+ ${d(i.shell)}
10
+ *, *::before, *::after { box-sizing: border-box; }
11
+ html, body {
12
+ background: var(--page-bg);
13
+ color: var(--page-fg);
14
+ min-height: 100%;
15
+ font-family: var(--font-sans);
16
+ margin: 0;
17
+ padding: 0;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ }
22
+ .login-card {
23
+ background: var(--panel-bg);
24
+ border: 1px solid var(--panel-border);
25
+ border-radius: 24px;
26
+ padding: 48px;
27
+ width: min(420px, calc(100vw - 32px));
28
+ box-shadow: 0 4px 28px rgba(0,0,0,0.04);
29
+ }
30
+ .login-eyebrow {
31
+ font-size: var(--text-xs);
32
+ font-weight: 600;
33
+ letter-spacing: 0.08em;
34
+ text-transform: uppercase;
35
+ color: var(--panel-accent);
36
+ margin: 0 0 14px;
37
+ }
38
+ .login-card h1 {
39
+ font-size: var(--text-2xl);
40
+ font-weight: 600;
41
+ letter-spacing: -0.03em;
42
+ margin: 0 0 12px;
43
+ }
44
+ .login-card p {
45
+ font-size: var(--text-base);
46
+ color: var(--panel-muted);
47
+ margin: 0 0 32px;
48
+ line-height: 1.6;
49
+ }
50
+ .login-field { margin-bottom: 24px; }
51
+ .login-field label {
52
+ display: block;
53
+ font-size: var(--text-sm);
54
+ font-weight: 500;
55
+ color: var(--page-fg);
56
+ margin-bottom: 8px;
57
+ }
58
+ .login-field input {
59
+ width: 100%;
60
+ padding: 14px 16px;
61
+ background: var(--page-bg);
62
+ border: 1px solid var(--panel-border);
63
+ border-radius: 14px;
64
+ color: var(--page-fg);
65
+ font: inherit;
66
+ font-size: var(--text-base);
67
+ outline: none;
68
+ transition: border-color 0.15s, box-shadow 0.15s;
69
+ }
70
+ .login-field input:focus {
71
+ border-color: var(--panel-accent);
72
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--panel-accent) 12%, transparent);
73
+ }
74
+ .login-btn {
75
+ width: 100%;
76
+ padding: 15px;
77
+ border: none;
78
+ border-radius: 14px;
79
+ background: var(--panel-accent);
80
+ color: var(--panel-accent-on);
81
+ font: inherit;
82
+ font-size: var(--text-base);
83
+ font-weight: 600;
84
+ cursor: pointer;
85
+ transition: opacity 0.15s, transform 0.1s;
86
+ min-height: 44px;
87
+ }
88
+ .login-btn:hover { opacity: 0.9; }
89
+ .login-btn:active { transform: scale(0.995); }
90
+ .login-btn:focus-visible { box-shadow: 0 0 0 2px var(--panel-accent); outline: none; }
91
+ .login-btn:disabled { opacity: 0.5; cursor: not-allowed; }
92
+ .login-error {
93
+ background: color-mix(in srgb, var(--panel-accent) 8%, #f87171);
94
+ border: 1px solid color-mix(in srgb, var(--panel-accent) 20%, #f87171);
95
+ color: #b91c1c;
96
+ padding: 12px 14px;
97
+ border-radius: 12px;
98
+ font-size: var(--text-sm);
99
+ margin-bottom: 22px;
100
+ }
101
+ .login-footer {
102
+ margin-top: 28px;
103
+ text-align: center;
104
+ font-size: var(--text-sm);
105
+ color: var(--panel-muted);
106
+ }
107
+ .login-footer a { color: var(--panel-accent); font-weight: 500; }
108
+ .spinner {
109
+ display: inline-block;
110
+ width: 14px;
111
+ height: 14px;
112
+ border: 2px solid rgba(255,255,255,0.35);
113
+ border-top-color: #fff;
114
+ border-radius: 50%;
115
+ animation: spin 0.8s linear infinite;
116
+ margin-left: 8px;
117
+ vertical-align: middle;
118
+ }
119
+ @keyframes spin { to { transform: rotate(360deg); } }
120
+ @media (max-width: 480px) {
121
+ .login-card { padding: 36px 24px; border-radius: 20px; }
122
+ .login-card h1 { font-size: var(--text-xl); }
123
+ }
124
+ </style>
125
+ </head>
126
+ <body>
127
+ <div class="login-card">
128
+ <div class="login-eyebrow">tmux-weblink</div>
129
+ <h1>${a}</h1>
130
+ <p>${n(s)}</p>
131
+ ${l}
132
+ <form id="login-form">
133
+ <div class="login-field">
134
+ <label for="password">Password</label>
135
+ <input type="password" id="password" name="password" autocomplete="current-password" required autofocus minlength="8" />
136
+ </div>
137
+ <button type="submit" class="login-btn" id="submit-btn">${t}</button>
138
+ </form>
139
+ <div class="login-footer">
140
+ ${e?'<a href="/login">Back to sign in</a>':'First time? <a href="/login?setup=1">Set password</a>'}
141
+ </div>
142
+ </div>
143
+ <script>
144
+ (function() {
145
+ const form = document.getElementById('login-form');
146
+ const password = document.getElementById('password');
147
+ const submit = document.getElementById('submit-btn');
148
+ const error = document.querySelector('.login-error');
149
+ const setupMode = ${JSON.stringify(e)};
150
+
151
+ function setError(msg) {
152
+ if (!msg) return;
153
+ const div = document.createElement('div');
154
+ div.className = 'login-error';
155
+ div.textContent = msg;
156
+ if (error) error.replaceWith(div);
157
+ else form.parentNode.insertBefore(div, form);
158
+ }
159
+
160
+ form.addEventListener('submit', async (e) => {
161
+ e.preventDefault();
162
+ submit.disabled = true;
163
+ submit.innerHTML = ${JSON.stringify(t)} + '<span class="spinner"></span>';
164
+ try {
165
+ const res = await fetch('/api/auth/password', {
166
+ method: 'POST',
167
+ headers: { 'Content-Type': 'application/json' },
168
+ body: JSON.stringify({ password: password.value }),
169
+ });
170
+ const data = await res.json().catch(() => ({}));
171
+ if (!res.ok) {
172
+ setError(data.error || 'Authentication failed');
173
+ submit.disabled = false;
174
+ submit.textContent = ${JSON.stringify(t)};
175
+ return;
176
+ }
177
+ if (data.token) {
178
+ localStorage.setItem('tmux-web-token', data.token);
179
+ }
180
+ const returnTo = new URL(location.href).searchParams.get('returnTo') || '/';
181
+ location.href = returnTo;
182
+ } catch (err) {
183
+ setError('Network error. Please try again.');
184
+ submit.disabled = false;
185
+ submit.textContent = ${JSON.stringify(t)};
186
+ }
187
+ });
188
+ })();
189
+ </script>
190
+ </body>
191
+ </html>`}function n(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}export{p as renderLoginPage};
@@ -0,0 +1,65 @@
1
+ import{cssVarsStyle as f}from"../theme.js";import{commandbarCSS as x,commandbarHTML as h,commandbarScript as u}from"../commandbar.js";import{notesDrawerCSS as w,notesDrawerHTML as S,notesDrawerScript as $}from"../notes-drawer.js";import{sharedLayoutCSS as y,sharedHeader as _,sharedSidebar as k,newSessionModalHTML as D,newSessionModalScript as T}from"../shared-layout.js";function s(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function C(t){const a=new Date(t);return a.toLocaleDateString()+" "+a.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function z(t,a,r=!1,l=[],i=!1){const n=[...t].sort((e,o)=>(o.updatedAt??0)-(e.updatedAt??0)),c=n.map(e=>{const o=e.scope==="__global__",m=o?"Global":e.scope.replace(/^session:/,""),v=o?"/notes/__global__":"/notes/"+encodeURIComponent(e.scope.replace(/^session:/,"")),g=s((e.content||"").slice(0,200).trim()||"No content"),b=s(C(e.updatedAt));return`<a class="note-card" href="${v}">
2
+ <div class="label ${o?"global":""}">${s(m)}</div>
3
+ <div class="preview">${g}</div>
4
+ <div class="meta"><span>${b}</span></div>
5
+ </a>`}).join(`
6
+ `),p=n.length?c:'<p class="empty">No notes yet.</p>',d=`
7
+ .note-card {
8
+ display: block; padding: 16px 18px; border: 1px solid var(--panel-border); border-radius: 12px;
9
+ margin-bottom: 10px; text-decoration: none; color: var(--page-fg);
10
+ background: var(--panel-bg); transition: border-color 0.15s, transform 0.1s, box-shadow 0.15s; cursor: pointer;
11
+ }
12
+ .note-card:hover { border-color: var(--panel-accent); transform: translateY(-1px); box-shadow: 0 4px 20px color-mix(in srgb, var(--panel-accent) 8%, transparent); }
13
+ .note-card .label {
14
+ font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
15
+ color: var(--panel-accent); margin-bottom: 6px;
16
+ }
17
+ .note-card .label.global { color: var(--panel-success); }
18
+ .note-card .preview {
19
+ font-size: var(--text-sm); color: var(--panel-muted); line-height: 1.5;
20
+ white-space: pre-wrap; word-break: break-word;
21
+ display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
22
+ overflow: hidden;
23
+ }
24
+ .note-card .meta {
25
+ font-size: var(--text-xs); color: var(--panel-muted); margin-top: 10px;
26
+ display: flex; justify-content: space-between;
27
+ }
28
+ .empty { font-size: var(--text-sm); color: var(--panel-muted); line-height: 1.6; margin-top: 20px; }
29
+ ${r?x():""}
30
+ ${w()}`;return`<!DOCTYPE html>
31
+ <html lang="en">
32
+ <head>
33
+ <meta charset="UTF-8" />
34
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
35
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
36
+ <title>Notes - tmux-web</title>
37
+ <style>
38
+ ${f(a.shell)}
39
+ ${y(d)}
40
+ </style>
41
+ </head>
42
+ <body>
43
+
44
+ ${_({commandbarEnabled:r,title:"All Notes",themeTemplate:a.template})}
45
+
46
+ <div class="page-wrap">
47
+ <div class="page-layout">
48
+ ${k({activePage:"notes",agentsEnabled:i,refreshHref:"/notes"})}
49
+ <main class="main-panel">
50
+ <div id="notes-list">${p}</div>
51
+ </main>
52
+ </div>
53
+ </div>
54
+
55
+ ${D()}
56
+ ${r?h():""}
57
+ ${S("Notes - Global")}
58
+
59
+ <script type="module">
60
+ ${$("__global__")}
61
+ ${r?u(l,[]):""}
62
+ ${T()}
63
+ </script>
64
+ </body>
65
+ </html>`}export{z as renderNotesIndex};
@@ -0,0 +1,158 @@
1
+ import{cssVarsStyle as b}from"../theme.js";import{escapeHtml as m}from"../html.js";import{notesDbScript as g}from"../notes-db.js";import{notesUtilsScript as u}from"../notes-utils.js";import{commandbarCSS as x,commandbarHTML as f,commandbarScript as v,commandbarButtonHTML as y}from"../commandbar.js";function L(t,a,e=!1,r=[]){const o=t==="__global__",i=o?"Global":t,s=o?"__global__":"session:"+t,d="/notes",l=o?"notes-global":"notes-session-"+t.replace(/[:\/\\]/g,"-"),c=JSON.stringify(s),p=JSON.stringify(`${l}.md`),n=m(i);return`<!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
+ <title>Notes - ${n} - tmux-web</title>
8
+ <style>
9
+ ${b(a.shell)}
10
+ html, body { background: var(--page-bg); color: var(--page-fg); min-height: 100%; font-family: var(--font-mono); }
11
+ .container { max-width: 720px; margin: 40px auto; padding: 0 20px; }
12
+ .page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; }
13
+ .breadcrumb { font-size: var(--text-sm); color: var(--panel-muted); }
14
+ .breadcrumb a { color: var(--panel-muted); text-decoration: none; }
15
+ .breadcrumb a:hover { color: var(--panel-accent); }
16
+ .breadcrumb span { color: var(--panel-accent); font-weight: 500; }
17
+ .toolbar {
18
+ display: flex; align-items: center; gap: 10px; margin-bottom: 16px; padding-bottom: 12px;
19
+ border-bottom: 1px solid var(--panel-border); flex-wrap: wrap;
20
+ }
21
+ .toolbar button {
22
+ font-size: var(--text-xs); color: var(--panel-muted); background: none;
23
+ border: 1px solid var(--panel-border); padding: 8px 14px; border-radius: 8px;
24
+ cursor: pointer; font-family: var(--font-mono); transition: all 0.15s;
25
+ min-height: 44px;
26
+ }
27
+ .toolbar button:hover { border-color: var(--panel-accent); color: var(--panel-accent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
28
+ .toolbar button:focus-visible { box-shadow: 0 0 0 2px var(--panel-accent); outline: none; }
29
+ .toolbar button:disabled { opacity: 0.4; cursor: not-allowed; }
30
+ .toolbar button.flash { color: var(--panel-success); border-color: var(--panel-success); }
31
+ .toolbar .badge {
32
+ font-size: var(--text-xs); color: var(--panel-success); letter-spacing: 0.1em; text-transform: uppercase;
33
+ opacity: 0; transition: opacity 0.15s; margin-left: auto;
34
+ }
35
+ .toolbar .badge.show { opacity: 1; }
36
+ #notes-editor {
37
+ min-height: 400px; padding: 16px; outline: none;
38
+ font-size: var(--text-sm); line-height: 1.7; font-family: var(--font-mono);
39
+ white-space: pre-wrap; word-break: break-word; color: var(--page-fg);
40
+ border: 1px solid transparent; border-radius: 8px;
41
+ transition: border-color 0.15s, background 0.15s;
42
+ }
43
+ #notes-editor:empty::before {
44
+ content: "Double-click to edit notes..."; color: var(--panel-muted); pointer-events: none;
45
+ }
46
+ #notes-editor[contenteditable="true"] {
47
+ border: 1px solid color-mix(in srgb, var(--panel-accent) 30%, transparent);
48
+ background: color-mix(in srgb, var(--page-bg) 70%, transparent);
49
+ }
50
+ #notes-editor a { color: var(--panel-accent); text-decoration: none; }
51
+ #notes-editor a:hover { text-decoration: underline; }
52
+ @media (max-width: 560px) {
53
+ .container { margin: 20px auto; padding: 0 16px; }
54
+ #notes-editor { min-height: 300px; padding: 12px; }
55
+ }
56
+ ${e?x():""}
57
+ </style>
58
+ </head>
59
+ <body>
60
+ <div class="container">
61
+ <div class="page-header">
62
+ <div class="breadcrumb">
63
+ <a href="${d}">Notes</a> <span>/</span> <span>${n}</span>
64
+ </div>
65
+ ${e?y("Search"):""}
66
+ </div>
67
+ <div class="toolbar">
68
+ <button id="notes-copy">Copy</button>
69
+ <button id="notes-export">Export .md</button>
70
+ <span class="badge" id="edit-badge">Editing</span>
71
+ </div>
72
+ <div id="notes-editor"></div>
73
+ </div>
74
+
75
+ <script type="module">
76
+ const NOTES_SCOPE = ${c};
77
+ ${g()}
78
+ ${u()}
79
+
80
+ const notesEditor = document.getElementById('notes-editor');
81
+ const copyBtn = document.getElementById('notes-copy');
82
+ const exportBtn = document.getElementById('notes-export');
83
+ const editBadge = document.getElementById('edit-badge');
84
+
85
+ let notePlain = '';
86
+ let editing = false;
87
+
88
+ function setButtonsEnabled(enabled) {
89
+ copyBtn.disabled = !enabled;
90
+ exportBtn.disabled = !enabled;
91
+ }
92
+
93
+ async function renderNote() {
94
+ const rec = await loadNote(NOTES_SCOPE);
95
+ notePlain = rec?.content || '';
96
+ notesEditor.innerHTML = linkifyHTML(escapeHTML(notePlain));
97
+ setButtonsEnabled(!!notePlain);
98
+ }
99
+
100
+ function startEditing() {
101
+ editing = true;
102
+ notesEditor.contentEditable = 'true';
103
+ notesEditor.textContent = notePlain;
104
+ editBadge.classList.add('show');
105
+ notesEditor.focus();
106
+ }
107
+
108
+ async function stopEditing() {
109
+ if (!editing) return;
110
+ editing = false;
111
+ // innerText (not textContent): Enter in contenteditable inserts <br> or blocks;
112
+ // textContent drops those breaks and merges lines.
113
+ notePlain = (notesEditor.innerText ?? '').replace(/\\r\\n/g, '\\n');
114
+ notesEditor.contentEditable = 'false';
115
+ notesEditor.innerHTML = linkifyHTML(escapeHTML(notePlain));
116
+ editBadge.classList.remove('show');
117
+ await saveNote(NOTES_SCOPE, notePlain);
118
+ setButtonsEnabled(!!notePlain);
119
+ }
120
+
121
+ notesEditor.addEventListener('dblclick', (e) => {
122
+ if (!editing) { e.preventDefault(); startEditing(); }
123
+ });
124
+
125
+ notesEditor.addEventListener('blur', () => { if (editing) stopEditing(); });
126
+
127
+ notesEditor.addEventListener('keydown', (e) => {
128
+ if (e.key === 'Escape' && editing) { e.preventDefault(); stopEditing(); }
129
+ });
130
+
131
+ copyBtn.addEventListener('click', async () => {
132
+ if (!notePlain) return;
133
+ try {
134
+ await navigator.clipboard.writeText(notePlain);
135
+ copyBtn.textContent = 'Copied!';
136
+ copyBtn.classList.add('flash');
137
+ setTimeout(() => { copyBtn.textContent = 'Copy'; copyBtn.classList.remove('flash'); }, 1500);
138
+ } catch {}
139
+ });
140
+
141
+ exportBtn.addEventListener('click', () => {
142
+ if (!notePlain) return;
143
+ const blob = new Blob([notePlain], { type: 'text/markdown' });
144
+ const a = document.createElement('a');
145
+ a.href = URL.createObjectURL(blob);
146
+ a.download = ${p};
147
+ a.click();
148
+ URL.revokeObjectURL(a.href);
149
+ });
150
+
151
+ renderNote();
152
+ </script>
153
+ ${e?f():""}
154
+ <script type="module">
155
+ ${e?v(r,[]):""}
156
+ </script>
157
+ </body>
158
+ </html>`}export{L as renderNotesPage};