relmio 0.3.0 → 0.4.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.
package/src/ui/app.js CHANGED
@@ -13,6 +13,10 @@ const state = {
13
13
  };
14
14
 
15
15
  const element = (id) => document.getElementById(id);
16
+ const localEndpointLink = element("local-endpoint-link");
17
+ if (token) {
18
+ localEndpointLink.href = `/local?session=${encodeURIComponent(token)}`;
19
+ }
16
20
  const messageToast = element("global-message");
17
21
  const message = element("global-message-text");
18
22
  const errorBox = element("global-error");
package/src/ui/index.html CHANGED
@@ -55,108 +55,108 @@
55
55
  </div>
56
56
  </header>
57
57
 
58
- <div class="toast-stack" aria-label="Wizard notifications">
59
- <aside
60
- id="global-safety"
61
- class="toast safety-note"
62
- aria-label="Safety guarantee"
63
- >
64
- <strong>Sidecar-only guarantee</strong>
65
- <span>
66
- No n8n restart. No n8n rebuild. No public port. You approve the exact
67
- plan before anything is written.
68
- </span>
69
- <button
70
- class="toast-close"
71
- type="button"
72
- data-dismiss-toast="global-safety"
73
- aria-label="Dismiss safety guarantee"
74
- >
75
- <span aria-hidden="true">×</span>
76
- </button>
77
- </aside>
78
-
79
- <aside
80
- id="global-backup"
81
- class="toast safety-note backup-note"
82
- aria-label="Backup reminder"
83
- >
84
- <strong>Back up first</strong>
85
- <span>
86
- Export your n8n workflows before connecting. This wizard uses
87
- sidecar-only commands, but it still writes to your VPS.
88
- </span>
89
- <button
90
- class="toast-close"
91
- type="button"
92
- data-dismiss-toast="global-backup"
93
- aria-label="Dismiss backup reminder"
94
- >
95
- <span aria-hidden="true">×</span>
96
- </button>
97
- </aside>
58
+ <main id="main-content" class="shell" tabindex="-1">
59
+ <aside class="rail" aria-label="Setup progress and safety">
60
+ <section class="intro" aria-labelledby="page-title">
61
+ <h1 id="page-title">Connect n8n without changing n8n</h1>
62
+ <p class="lede">
63
+ This wizard installs one separate Docker sidecar. Your existing n8n
64
+ image, Compose file, container, and workflows stay untouched.
65
+ </p>
66
+ </section>
67
+
68
+ <nav class="steps" aria-label="Setup progress">
69
+ <ol>
70
+ <li data-step-marker="1" aria-current="step">
71
+ <span>1</span><em>Sign in</em>
72
+ </li>
73
+ <li data-step-marker="2"><span>2</span><em>VPS</em></li>
74
+ <li data-step-marker="3"><span>3</span><em>n8n</em></li>
75
+ <li data-step-marker="4"><span>4</span><em>Review</em></li>
76
+ <li data-step-marker="5"><span>5</span><em>Done</em></li>
77
+ </ol>
78
+ </nav>
79
+
80
+ <div class="toast-stack" aria-label="Wizard notifications">
81
+ <aside
82
+ id="global-safety"
83
+ class="toast safety-note"
84
+ aria-label="Safety guarantee"
85
+ >
86
+ <strong>Sidecar-only guarantee</strong>
87
+ <span>
88
+ No n8n restart. No n8n rebuild. No public port. You approve the
89
+ exact plan before anything is written.
90
+ </span>
91
+ <button
92
+ class="toast-close"
93
+ type="button"
94
+ data-dismiss-toast="global-safety"
95
+ aria-label="Dismiss safety guarantee"
96
+ >
97
+ <span aria-hidden="true">×</span>
98
+ </button>
99
+ </aside>
98
100
 
99
- <div
100
- id="global-message"
101
- class="toast message"
102
- role="status"
103
- aria-live="polite"
104
- >
105
- <span id="global-message-text">Checking your local sign-in…</span>
106
- <button
107
- class="toast-close"
108
- type="button"
109
- data-dismiss-toast="global-message"
110
- aria-label="Dismiss status message"
111
- >
112
- <span aria-hidden="true">×</span>
113
- </button>
114
- </div>
101
+ <aside
102
+ id="global-backup"
103
+ class="toast safety-note backup-note"
104
+ aria-label="Backup reminder"
105
+ >
106
+ <strong>Back up first</strong>
107
+ <span>
108
+ Export your n8n workflows before connecting. This wizard uses
109
+ sidecar-only commands, but it still writes to your VPS.
110
+ </span>
111
+ <button
112
+ class="toast-close"
113
+ type="button"
114
+ data-dismiss-toast="global-backup"
115
+ aria-label="Dismiss backup reminder"
116
+ >
117
+ <span aria-hidden="true">×</span>
118
+ </button>
119
+ </aside>
115
120
 
116
- <div
117
- id="global-error"
118
- class="toast error"
119
- role="alert"
120
- tabindex="-1"
121
- hidden
122
- >
123
- <span id="global-error-text"></span>
124
- <button
125
- class="toast-close"
126
- type="button"
127
- data-dismiss-toast="global-error"
128
- aria-label="Dismiss error"
129
- >
130
- <span aria-hidden="true">×</span>
131
- </button>
132
- </div>
133
- </div>
134
-
135
- <main id="main-content" class="shell">
136
- <section class="intro" aria-labelledby="page-title">
137
- <p class="eyebrow">Guided VPS setup</p>
138
- <h1 id="page-title">Connect n8n without changing n8n</h1>
139
- <p class="lede">
140
- This wizard installs one separate Docker sidecar. Your existing n8n
141
- image, Compose file, container, and workflows stay untouched.
142
- </p>
143
- </section>
121
+ <div
122
+ id="global-message"
123
+ class="toast message"
124
+ role="status"
125
+ aria-live="polite"
126
+ >
127
+ <span id="global-message-text">Checking your local sign-in…</span>
128
+ <button
129
+ class="toast-close"
130
+ type="button"
131
+ data-dismiss-toast="global-message"
132
+ aria-label="Dismiss status message"
133
+ >
134
+ <span aria-hidden="true">×</span>
135
+ </button>
136
+ </div>
144
137
 
145
- <nav class="steps" aria-label="Setup progress">
146
- <ol>
147
- <li data-step-marker="1" aria-current="step">
148
- <span>1</span><em>Sign in</em>
149
- </li>
150
- <li data-step-marker="2"><span>2</span><em>VPS</em></li>
151
- <li data-step-marker="3"><span>3</span><em>n8n</em></li>
152
- <li data-step-marker="4"><span>4</span><em>Review</em></li>
153
- <li data-step-marker="5"><span>5</span><em>Done</em></li>
154
- </ol>
155
- </nav>
138
+ <div
139
+ id="global-error"
140
+ class="toast error"
141
+ role="alert"
142
+ tabindex="-1"
143
+ hidden
144
+ >
145
+ <span id="global-error-text"></span>
146
+ <button
147
+ class="toast-close"
148
+ type="button"
149
+ data-dismiss-toast="global-error"
150
+ aria-label="Dismiss error"
151
+ >
152
+ <span aria-hidden="true">×</span>
153
+ </button>
154
+ </div>
155
+ </div>
156
+ </aside>
156
157
 
157
158
  <section class="panel" data-step="1" aria-labelledby="signin-title">
158
159
  <div class="panel-heading">
159
- <span class="step-kicker">Step 1 of 5</span>
160
160
  <h2 id="signin-title" tabindex="-1">Use your local ChatGPT sign-in</h2>
161
161
  <p>
162
162
  The OAuth credential stays on this computer until you approve its
@@ -177,6 +177,19 @@
177
177
  </div>
178
178
  </div>
179
179
 
180
+ <aside class="final-note" aria-labelledby="local-endpoint-title">
181
+ <h3 id="local-endpoint-title">Need an endpoint on this computer?</h3>
182
+ <p>
183
+ Install a loopback-only OpenAI API endpoint with a Platform key, or
184
+ the experimental official Codex App Server with ChatGPT sign-in.
185
+ </p>
186
+ <div class="actions">
187
+ <a id="local-endpoint-link" class="button secondary">
188
+ Set up a local endpoint
189
+ </a>
190
+ </div>
191
+ </aside>
192
+
180
193
  <div class="actions">
181
194
  <button id="login-button" class="button secondary" type="button">
182
195
  Sign in with ChatGPT
@@ -198,7 +211,6 @@
198
211
 
199
212
  <section class="panel" data-step="2" aria-labelledby="vps-title" hidden>
200
213
  <div class="panel-heading">
201
- <span class="step-kicker">Step 2 of 5</span>
202
214
  <h2 id="vps-title" tabindex="-1">Connect to your VPS</h2>
203
215
  <p>
204
216
  Copy the address exactly from Hostinger. Your password is used only
@@ -294,7 +306,6 @@
294
306
 
295
307
  <section class="panel" data-step="3" aria-labelledby="n8n-title" hidden>
296
308
  <div class="panel-heading">
297
- <span class="step-kicker">Step 3 of 5</span>
298
309
  <h2 id="n8n-title" tabindex="-1">Choose the detected n8n</h2>
299
310
  <p>
300
311
  Discovery uses read-only Docker commands. It does not execute
@@ -332,7 +343,6 @@
332
343
 
333
344
  <section class="panel" data-step="4" aria-labelledby="review-title" hidden>
334
345
  <div class="panel-heading">
335
- <span class="step-kicker">Step 4 of 5</span>
336
346
  <h2 id="review-title" tabindex="-1">Review before installing</h2>
337
347
  <p>No existing n8n files or containers will be changed.</p>
338
348
  </div>
@@ -381,14 +391,15 @@
381
391
  </section>
382
392
 
383
393
  <section class="panel success-panel" data-step="5" aria-labelledby="done-title" hidden>
384
- <div class="success-mark" aria-hidden="true">✓</div>
385
- <div class="panel-heading">
386
- <span class="step-kicker">Step 5 of 5</span>
387
- <h2 id="done-title" tabindex="-1">The private bridge is ready</h2>
388
- <p>
389
- Copy the OpenAI credential values first, then use one of the node
390
- recipes below.
391
- </p>
394
+ <div class="success-heading">
395
+ <div class="success-mark" aria-hidden="true">✓</div>
396
+ <div class="panel-heading">
397
+ <h2 id="done-title" tabindex="-1">The private bridge is ready</h2>
398
+ <p>
399
+ Copy the OpenAI credential values first, then use one of the node
400
+ recipes below.
401
+ </p>
402
+ </div>
392
403
  </div>
393
404
 
394
405
  <h3>1. OpenAI credential</h3>
@@ -435,7 +446,7 @@
435
446
 
436
447
  <button
437
448
  id="copy-settings"
438
- class="button secondary"
449
+ class="button primary"
439
450
  type="button"
440
451
  data-copy-group="credential"
441
452
  data-copy-label="OpenAI credential settings"
@@ -0,0 +1,262 @@
1
+ .local-wizard .shell {
2
+ width: min(100% - 2.5rem, 78rem);
3
+ }
4
+
5
+ .local-wizard .panel {
6
+ scroll-behavior: smooth;
7
+ }
8
+
9
+ .local-wizard .status-row {
10
+ margin-bottom: 1rem;
11
+ }
12
+
13
+ .local-wizard .safety-note {
14
+ grid-template-columns: 1.75rem minmax(0, 1fr);
15
+ }
16
+
17
+ .local-wizard .safety-note > span {
18
+ grid-column: 2;
19
+ }
20
+
21
+ .target-picker {
22
+ display: grid;
23
+ grid-template-columns: repeat(2, minmax(0, 1fr));
24
+ gap: 0.75rem;
25
+ min-width: 0;
26
+ margin: 0 0 1rem;
27
+ padding: 0;
28
+ border: 0;
29
+ }
30
+
31
+ .target-picker legend {
32
+ width: 100%;
33
+ margin-bottom: 0.5rem;
34
+ font-weight: 800;
35
+ }
36
+
37
+ .target-card {
38
+ position: relative;
39
+ display: grid;
40
+ grid-template-columns: 1.25rem minmax(0, 1fr);
41
+ gap: 0.75rem;
42
+ min-width: 0;
43
+ padding: 1rem;
44
+ border: 1px solid var(--border-strong);
45
+ border-radius: var(--radius);
46
+ background: var(--surface-muted);
47
+ cursor: pointer;
48
+ transition:
49
+ border-color 180ms var(--ease),
50
+ background 180ms var(--ease),
51
+ box-shadow 180ms var(--ease),
52
+ transform 180ms var(--ease);
53
+ }
54
+
55
+ .target-card:hover {
56
+ border-color: var(--accent);
57
+ transform: translateY(-1px);
58
+ }
59
+
60
+ .target-card:has(input:checked) {
61
+ border-color: var(--accent);
62
+ background: var(--accent-soft);
63
+ box-shadow: 0 0 0 3px rgb(19 124 116 / 12%);
64
+ }
65
+
66
+ .target-card:has(input:focus-visible) {
67
+ outline: 3px solid #f2a94a;
68
+ outline-offset: 2px;
69
+ }
70
+
71
+ .target-card input {
72
+ width: 1.125rem;
73
+ height: 1.125rem;
74
+ margin: 0.2rem 0 0;
75
+ accent-color: var(--accent);
76
+ }
77
+
78
+ .target-card-copy {
79
+ display: grid;
80
+ gap: 0.35rem;
81
+ color: var(--text-muted);
82
+ font-size: 0.875rem;
83
+ }
84
+
85
+ .target-card-copy strong {
86
+ color: var(--text);
87
+ font-size: 1rem;
88
+ }
89
+
90
+ .target-card-copy .target-meta {
91
+ color: var(--accent-deep);
92
+ font-size: 0.75rem;
93
+ font-weight: 800;
94
+ letter-spacing: 0.02em;
95
+ text-transform: uppercase;
96
+ }
97
+
98
+ .warning-card:has(input:checked) {
99
+ border-color: var(--warning-border);
100
+ background: var(--warning-soft);
101
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--warning) 13%, transparent);
102
+ }
103
+
104
+ .warning-card .target-meta {
105
+ color: var(--warning);
106
+ }
107
+
108
+ .local-fields {
109
+ grid-template-columns: minmax(8rem, 0.55fr) minmax(0, 1.45fr);
110
+ }
111
+
112
+ .field textarea {
113
+ width: 100%;
114
+ min-height: 5.75rem;
115
+ padding: 0.625rem 0.875rem;
116
+ resize: vertical;
117
+ border: 1px solid var(--border-strong);
118
+ border-radius: var(--radius-sm);
119
+ color: var(--text);
120
+ background: var(--surface);
121
+ font: 0.875rem/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
122
+ transition:
123
+ border-color 200ms var(--ease),
124
+ box-shadow 200ms var(--ease);
125
+ }
126
+
127
+ .field textarea:hover {
128
+ border-color: var(--accent);
129
+ }
130
+
131
+ .field textarea:focus {
132
+ border-color: var(--accent);
133
+ outline: 0;
134
+ box-shadow: 0 0 0 4px rgb(19 124 116 / 13%);
135
+ }
136
+
137
+ .policy-note,
138
+ .high-trust-warning,
139
+ .one-time-note,
140
+ .codex-login {
141
+ margin-top: 1rem;
142
+ padding: 0.875rem 1rem;
143
+ border: 1px solid var(--accent-border);
144
+ border-radius: var(--radius);
145
+ background: var(--accent-soft);
146
+ }
147
+
148
+ .policy-note p,
149
+ .high-trust-warning p,
150
+ .one-time-note p,
151
+ .codex-login p {
152
+ margin: 0.35rem 0 0;
153
+ color: var(--text-muted);
154
+ font-size: 0.875rem;
155
+ }
156
+
157
+ .high-trust-warning {
158
+ border-color: var(--warning-border);
159
+ background: var(--warning-soft);
160
+ }
161
+
162
+ .high-trust-warning strong,
163
+ .high-trust-warning p {
164
+ color: var(--warning);
165
+ }
166
+
167
+ .one-time-note {
168
+ margin: 0 0 1rem;
169
+ border-color: var(--warning-border);
170
+ background: var(--warning-soft);
171
+ }
172
+
173
+ .one-time-note strong,
174
+ .one-time-note p {
175
+ color: var(--warning);
176
+ }
177
+
178
+ .local-review-grid {
179
+ margin-top: 1rem;
180
+ }
181
+
182
+ .review-details dd {
183
+ overflow-wrap: anywhere;
184
+ }
185
+
186
+ .review-details div {
187
+ grid-template-columns: 9.5rem minmax(0, 1fr);
188
+ }
189
+
190
+ .codex-login {
191
+ border-color: var(--warning-border);
192
+ background: var(--warning-soft);
193
+ }
194
+
195
+ .codex-login h3 {
196
+ margin-bottom: 0;
197
+ }
198
+
199
+ .codex-login > .button {
200
+ margin-top: 0.875rem;
201
+ }
202
+
203
+ .device-code-result {
204
+ margin-top: 1rem;
205
+ padding-top: 1rem;
206
+ border-top: 1px solid var(--warning-border);
207
+ }
208
+
209
+ .device-code-row {
210
+ display: flex;
211
+ flex-wrap: wrap;
212
+ align-items: center;
213
+ gap: 0.625rem;
214
+ margin: 0.75rem 0;
215
+ }
216
+
217
+ .device-code-row code {
218
+ padding: 0.65rem 0.875rem;
219
+ border: 1px solid var(--warning-border);
220
+ background: var(--surface);
221
+ font-size: 1.125rem;
222
+ font-weight: 800;
223
+ letter-spacing: 0.08em;
224
+ }
225
+
226
+ .device-code-status {
227
+ font-weight: 700;
228
+ }
229
+
230
+ .local-wizard a.button {
231
+ text-decoration: none;
232
+ }
233
+
234
+ @media (max-width: 60rem),
235
+ (min-width: 961px) and (max-height: 768px) {
236
+ .local-wizard .steps ol {
237
+ grid-template-columns: repeat(4, minmax(0, 1fr));
238
+ }
239
+ }
240
+
241
+ @media (max-width: 48rem) {
242
+ .target-picker,
243
+ .local-fields,
244
+ .local-review-grid {
245
+ grid-template-columns: minmax(0, 1fr);
246
+ }
247
+
248
+ .review-details div {
249
+ grid-template-columns: minmax(0, 1fr);
250
+ gap: 0.2rem;
251
+ }
252
+ }
253
+
254
+ @media (prefers-reduced-motion: reduce) {
255
+ .local-wizard *,
256
+ .local-wizard *::before,
257
+ .local-wizard *::after {
258
+ scroll-behavior: auto !important;
259
+ transition-duration: 0.01ms !important;
260
+ animation-duration: 0.01ms !important;
261
+ }
262
+ }