relmio 0.2.12 → 0.2.13
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/CHANGELOG.md +16 -0
- package/package.json +1 -1
- package/src/ui/app.js +144 -51
- package/src/ui/index.html +177 -79
- package/src/ui/styles.css +177 -26
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ checks the registry separately after publication.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## [0.2.13] - 2026-08-04
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Compact the local setup wizard so its active step stays near the top, move
|
|
15
|
+
safety and status notices into dismissible toasts, and collapse the optional
|
|
16
|
+
AI Agent and HTTP Request recipes until users choose to open them.
|
|
17
|
+
- Complete the HTTP Request recipe with authorization, content type, and a
|
|
18
|
+
copyable sample Responses API JSON payload.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Copy credential values reliably in Opera GX on Windows by preserving the
|
|
23
|
+
synchronous user gesture for the selection-based clipboard path before
|
|
24
|
+
falling back to the modern Clipboard API.
|
|
25
|
+
|
|
10
26
|
## [0.2.12] - 2026-08-04
|
|
11
27
|
|
|
12
28
|
### Fixed
|
package/package.json
CHANGED
package/src/ui/app.js
CHANGED
|
@@ -13,8 +13,16 @@ const state = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const element = (id) => document.getElementById(id);
|
|
16
|
-
const
|
|
16
|
+
const messageToast = element("global-message");
|
|
17
|
+
const message = element("global-message-text");
|
|
17
18
|
const errorBox = element("global-error");
|
|
19
|
+
const errorMessage = element("global-error-text");
|
|
20
|
+
const toastTimers = new WeakMap();
|
|
21
|
+
|
|
22
|
+
function dismissToast(toast) {
|
|
23
|
+
window.clearTimeout(toastTimers.get(toast));
|
|
24
|
+
toast.hidden = true;
|
|
25
|
+
}
|
|
18
26
|
|
|
19
27
|
function resetFingerprint() {
|
|
20
28
|
state.fingerprint = null;
|
|
@@ -26,18 +34,24 @@ function resetFingerprint() {
|
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
function setMessage(text) {
|
|
37
|
+
messageToast.hidden = false;
|
|
29
38
|
message.textContent = text;
|
|
39
|
+
window.clearTimeout(toastTimers.get(messageToast));
|
|
40
|
+
toastTimers.set(
|
|
41
|
+
messageToast,
|
|
42
|
+
window.setTimeout(() => dismissToast(messageToast), 6_000),
|
|
43
|
+
);
|
|
30
44
|
}
|
|
31
45
|
|
|
32
46
|
function showError(error) {
|
|
33
|
-
|
|
47
|
+
errorMessage.textContent = error.message ?? "Something went wrong.";
|
|
34
48
|
errorBox.hidden = false;
|
|
35
49
|
errorBox.focus();
|
|
36
50
|
}
|
|
37
51
|
|
|
38
52
|
function clearError() {
|
|
39
53
|
errorBox.hidden = true;
|
|
40
|
-
|
|
54
|
+
errorMessage.textContent = "";
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
function setBusy(button, busy, busyText) {
|
|
@@ -68,32 +82,124 @@ function flashCopied(button) {
|
|
|
68
82
|
}
|
|
69
83
|
|
|
70
84
|
async function copyText(value) {
|
|
85
|
+
const previouslyFocused = document.activeElement;
|
|
86
|
+
const textarea = document.createElement("textarea");
|
|
87
|
+
textarea.value = value;
|
|
88
|
+
textarea.readOnly = true;
|
|
89
|
+
textarea.setAttribute("aria-hidden", "true");
|
|
90
|
+
textarea.style.position = "fixed";
|
|
91
|
+
textarea.style.opacity = "0";
|
|
92
|
+
|
|
93
|
+
let copied = false;
|
|
71
94
|
try {
|
|
72
|
-
await navigator.clipboard.writeText(value);
|
|
73
|
-
return;
|
|
74
|
-
} catch {
|
|
75
|
-
const previouslyFocused = document.activeElement;
|
|
76
|
-
const textarea = document.createElement("textarea");
|
|
77
|
-
textarea.value = value;
|
|
78
|
-
textarea.readOnly = true;
|
|
79
|
-
textarea.setAttribute("aria-hidden", "true");
|
|
80
|
-
textarea.style.position = "fixed";
|
|
81
|
-
textarea.style.opacity = "0";
|
|
82
95
|
document.body.append(textarea);
|
|
96
|
+
textarea.focus();
|
|
83
97
|
textarea.select();
|
|
84
|
-
|
|
98
|
+
textarea.setSelectionRange?.(0, textarea.value.length);
|
|
99
|
+
copied = document.execCommand("copy");
|
|
100
|
+
} catch {
|
|
101
|
+
copied = false;
|
|
102
|
+
} finally {
|
|
103
|
+
textarea.remove();
|
|
104
|
+
previouslyFocused?.focus?.();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (copied) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (navigator.clipboard?.writeText) {
|
|
85
112
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
if (!copied) {
|
|
92
|
-
throw new Error("The browser refused clipboard access.");
|
|
113
|
+
await navigator.clipboard.writeText(value);
|
|
114
|
+
return;
|
|
115
|
+
} catch {
|
|
116
|
+
// The generic error below avoids exposing copied configuration values.
|
|
93
117
|
}
|
|
94
118
|
}
|
|
119
|
+
|
|
120
|
+
throw new Error("The browser refused clipboard access.");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function renderHttpRequestBody(model) {
|
|
124
|
+
element("result-http-body").textContent = JSON.stringify(
|
|
125
|
+
{
|
|
126
|
+
model,
|
|
127
|
+
input: "Reply with exactly: bridge works",
|
|
128
|
+
},
|
|
129
|
+
null,
|
|
130
|
+
2,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function copyCredentialSettings() {
|
|
135
|
+
return [
|
|
136
|
+
`Base URL: ${element("result-url").textContent}`,
|
|
137
|
+
`API Key: ${element("result-key").textContent}`,
|
|
138
|
+
"Organization ID: leave empty",
|
|
139
|
+
"Add Custom Header: off",
|
|
140
|
+
].join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function copyHttpRecipe() {
|
|
144
|
+
return [
|
|
145
|
+
"Method: POST",
|
|
146
|
+
`URL: ${element("result-http-url").textContent}`,
|
|
147
|
+
"Authorization: Bearer local-only",
|
|
148
|
+
"Content-Type: application/json",
|
|
149
|
+
"Authentication: None",
|
|
150
|
+
"JSON body:",
|
|
151
|
+
element("result-http-body").textContent,
|
|
152
|
+
].join("\n");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function copyValueFor(button) {
|
|
156
|
+
if (button.dataset.copyTarget) {
|
|
157
|
+
return element(button.dataset.copyTarget).textContent;
|
|
158
|
+
}
|
|
159
|
+
if (button.dataset.copyGroup === "credential") {
|
|
160
|
+
return copyCredentialSettings();
|
|
161
|
+
}
|
|
162
|
+
if (button.dataset.copyGroup === "http") {
|
|
163
|
+
return copyHttpRecipe();
|
|
164
|
+
}
|
|
165
|
+
return "";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function createCopyClickHandler({
|
|
169
|
+
copyValueFor,
|
|
170
|
+
clearError,
|
|
171
|
+
copyText,
|
|
172
|
+
flashCopied,
|
|
173
|
+
setMessage,
|
|
174
|
+
showError,
|
|
175
|
+
}) {
|
|
176
|
+
return async function handleCopyClick(event) {
|
|
177
|
+
const button = event.currentTarget;
|
|
178
|
+
const label = button.dataset.copyLabel;
|
|
179
|
+
const value = copyValueFor(button);
|
|
180
|
+
clearError();
|
|
181
|
+
try {
|
|
182
|
+
if (!value) {
|
|
183
|
+
throw new Error("No displayed value is available to copy.");
|
|
184
|
+
}
|
|
185
|
+
await copyText(value);
|
|
186
|
+
flashCopied(button);
|
|
187
|
+
setMessage(`${label} copied.`);
|
|
188
|
+
} catch {
|
|
189
|
+
showError(new Error(`Copy failed. Select the ${label} manually.`));
|
|
190
|
+
}
|
|
191
|
+
};
|
|
95
192
|
}
|
|
96
193
|
|
|
194
|
+
const handleCopyClick = createCopyClickHandler({
|
|
195
|
+
copyValueFor,
|
|
196
|
+
clearError,
|
|
197
|
+
copyText,
|
|
198
|
+
flashCopied,
|
|
199
|
+
setMessage,
|
|
200
|
+
showError,
|
|
201
|
+
});
|
|
202
|
+
|
|
97
203
|
const delay = (milliseconds) =>
|
|
98
204
|
new Promise((resolve) => window.setTimeout(resolve, milliseconds));
|
|
99
205
|
|
|
@@ -126,6 +232,11 @@ async function waitForOAuthCompletion() {
|
|
|
126
232
|
|
|
127
233
|
function showStep(step) {
|
|
128
234
|
state.step = step;
|
|
235
|
+
document.body.dataset.currentStep = String(step);
|
|
236
|
+
if (step === 5) {
|
|
237
|
+
dismissToast(element("global-safety"));
|
|
238
|
+
dismissToast(element("global-backup"));
|
|
239
|
+
}
|
|
129
240
|
for (const panel of document.querySelectorAll("[data-step]")) {
|
|
130
241
|
const active = Number(panel.dataset.step) === step;
|
|
131
242
|
panel.hidden = !active;
|
|
@@ -483,10 +594,12 @@ element("install-button").addEventListener("click", async (event) => {
|
|
|
483
594
|
});
|
|
484
595
|
element("result-url").textContent = result.baseUrl;
|
|
485
596
|
element("result-key").textContent = result.apiKeyPlaceholder;
|
|
486
|
-
|
|
597
|
+
const firstModel = result.models[0] ?? "Not detected";
|
|
598
|
+
element("result-model").textContent = firstModel;
|
|
487
599
|
element("result-models").textContent = result.models.join(", ");
|
|
488
600
|
element("result-http-url").textContent =
|
|
489
601
|
`${result.baseUrl.replace(/\/$/u, "")}/responses`;
|
|
602
|
+
renderHttpRequestBody(firstModel);
|
|
490
603
|
showStep(5);
|
|
491
604
|
setMessage(
|
|
492
605
|
result.deploymentMode === "updated"
|
|
@@ -500,36 +613,15 @@ element("install-button").addEventListener("click", async (event) => {
|
|
|
500
613
|
}
|
|
501
614
|
});
|
|
502
615
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
"Add Custom Header: off",
|
|
509
|
-
].join("\n");
|
|
510
|
-
clearError();
|
|
511
|
-
try {
|
|
512
|
-
await copyText(settings);
|
|
513
|
-
flashCopied(event.currentTarget);
|
|
514
|
-
setMessage("OpenAI credential settings copied.");
|
|
515
|
-
} catch {
|
|
516
|
-
showError(new Error("Copy failed. Select the values manually."));
|
|
517
|
-
}
|
|
518
|
-
});
|
|
616
|
+
for (const button of document.querySelectorAll(
|
|
617
|
+
"[data-copy-target], [data-copy-group]",
|
|
618
|
+
)) {
|
|
619
|
+
button.addEventListener("click", handleCopyClick);
|
|
620
|
+
}
|
|
519
621
|
|
|
520
|
-
for (const button of document.querySelectorAll("[data-
|
|
521
|
-
button.addEventListener("click",
|
|
522
|
-
|
|
523
|
-
const label = event.currentTarget.dataset.copyLabel;
|
|
524
|
-
const value = target.textContent;
|
|
525
|
-
clearError();
|
|
526
|
-
try {
|
|
527
|
-
await copyText(value);
|
|
528
|
-
flashCopied(event.currentTarget);
|
|
529
|
-
setMessage(`${label} copied.`);
|
|
530
|
-
} catch {
|
|
531
|
-
showError(new Error(`Copy failed. Select the ${label} manually.`));
|
|
532
|
-
}
|
|
622
|
+
for (const button of document.querySelectorAll("[data-dismiss-toast]")) {
|
|
623
|
+
button.addEventListener("click", (event) => {
|
|
624
|
+
dismissToast(element(event.currentTarget.dataset.dismissToast));
|
|
533
625
|
});
|
|
534
626
|
}
|
|
535
627
|
|
|
@@ -545,4 +637,5 @@ for (const button of document.querySelectorAll(".back-button")) {
|
|
|
545
637
|
});
|
|
546
638
|
}
|
|
547
639
|
|
|
640
|
+
renderHttpRequestBody(element("result-model").textContent);
|
|
548
641
|
refreshAuthStatus().catch(showError);
|
package/src/ui/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<script src="/theme.js" type="module"></script>
|
|
17
17
|
<script src="/app.js" type="module"></script>
|
|
18
18
|
</head>
|
|
19
|
-
<body>
|
|
19
|
+
<body data-current-step="1">
|
|
20
20
|
<a class="skip-link" href="#main-content">Skip to setup</a>
|
|
21
21
|
|
|
22
22
|
<header class="site-header">
|
|
@@ -55,32 +55,93 @@
|
|
|
55
55
|
</div>
|
|
56
56
|
</header>
|
|
57
57
|
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
image, Compose file, container, and workflows stay untouched.
|
|
65
|
-
</p>
|
|
66
|
-
</section>
|
|
67
|
-
|
|
68
|
-
<aside class="safety-note" aria-label="Safety guarantee">
|
|
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
|
+
>
|
|
69
64
|
<strong>Sidecar-only guarantee</strong>
|
|
70
65
|
<span>
|
|
71
66
|
No n8n restart. No n8n rebuild. No public port. You approve the exact
|
|
72
67
|
plan before anything is written.
|
|
73
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>
|
|
74
77
|
</aside>
|
|
75
78
|
|
|
76
|
-
<aside
|
|
79
|
+
<aside
|
|
80
|
+
id="global-backup"
|
|
81
|
+
class="toast safety-note backup-note"
|
|
82
|
+
aria-label="Backup reminder"
|
|
83
|
+
>
|
|
77
84
|
<strong>Back up first</strong>
|
|
78
85
|
<span>
|
|
79
86
|
Export your n8n workflows before connecting. This wizard uses
|
|
80
87
|
sidecar-only commands, but it still writes to your VPS.
|
|
81
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>
|
|
82
97
|
</aside>
|
|
83
98
|
|
|
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>
|
|
115
|
+
|
|
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>
|
|
144
|
+
|
|
84
145
|
<nav class="steps" aria-label="Setup progress">
|
|
85
146
|
<ol>
|
|
86
147
|
<li data-step-marker="1" aria-current="step">
|
|
@@ -93,11 +154,6 @@
|
|
|
93
154
|
</ol>
|
|
94
155
|
</nav>
|
|
95
156
|
|
|
96
|
-
<div id="global-message" class="message" role="status" aria-live="polite">
|
|
97
|
-
Checking your local sign-in…
|
|
98
|
-
</div>
|
|
99
|
-
<div id="global-error" class="error" role="alert" tabindex="-1" hidden></div>
|
|
100
|
-
|
|
101
157
|
<section class="panel" data-step="1" aria-labelledby="signin-title">
|
|
102
158
|
<div class="panel-heading">
|
|
103
159
|
<span class="step-kicker">Step 1 of 5</span>
|
|
@@ -377,70 +433,112 @@
|
|
|
377
433
|
</div>
|
|
378
434
|
</dl>
|
|
379
435
|
|
|
380
|
-
<button
|
|
436
|
+
<button
|
|
437
|
+
id="copy-settings"
|
|
438
|
+
class="button secondary"
|
|
439
|
+
type="button"
|
|
440
|
+
data-copy-group="credential"
|
|
441
|
+
data-copy-label="OpenAI credential settings"
|
|
442
|
+
>
|
|
381
443
|
Copy credential settings
|
|
382
444
|
</button>
|
|
383
445
|
|
|
384
|
-
<
|
|
385
|
-
<
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
<
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
<
|
|
422
|
-
<
|
|
423
|
-
|
|
424
|
-
<code
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
<
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
446
|
+
<details class="recipe-disclosure">
|
|
447
|
+
<summary>
|
|
448
|
+
<span>2. OpenAI Chat Model for AI Agent or Basic LLM Chain</span>
|
|
449
|
+
</summary>
|
|
450
|
+
<div class="model-box recipe-content">
|
|
451
|
+
<dl class="result-list">
|
|
452
|
+
<div>
|
|
453
|
+
<dt>Model ID</dt>
|
|
454
|
+
<dd class="result-value">
|
|
455
|
+
<code id="result-model">Not detected</code>
|
|
456
|
+
<button
|
|
457
|
+
class="button secondary copy-value"
|
|
458
|
+
type="button"
|
|
459
|
+
data-copy-target="result-model"
|
|
460
|
+
data-copy-label="model ID"
|
|
461
|
+
aria-label="Copy model ID"
|
|
462
|
+
>
|
|
463
|
+
Copy
|
|
464
|
+
</button>
|
|
465
|
+
</dd>
|
|
466
|
+
</div>
|
|
467
|
+
<div>
|
|
468
|
+
<dt>Responses API</dt>
|
|
469
|
+
<dd><strong>On</strong> for Chat Model node version 1.3</dd>
|
|
470
|
+
</div>
|
|
471
|
+
</dl>
|
|
472
|
+
<p>All models detected: <span id="result-models">Not detected</span></p>
|
|
473
|
+
<p>
|
|
474
|
+
If the Responses API switch is absent, keep the node's default
|
|
475
|
+
Chat Completions behavior; the bridge supports that route too.
|
|
476
|
+
</p>
|
|
477
|
+
</div>
|
|
478
|
+
</details>
|
|
479
|
+
|
|
480
|
+
<details class="recipe-disclosure">
|
|
481
|
+
<summary><span>3. HTTP Request node</span></summary>
|
|
482
|
+
<div class="model-box recipe-content">
|
|
483
|
+
<dl class="result-list">
|
|
484
|
+
<div>
|
|
485
|
+
<dt>Method</dt>
|
|
486
|
+
<dd><code>POST</code></dd>
|
|
487
|
+
</div>
|
|
488
|
+
<div>
|
|
489
|
+
<dt>URL</dt>
|
|
490
|
+
<dd class="result-value">
|
|
491
|
+
<code id="result-http-url">http://n8n-openai-oauth:10531/v1/responses</code>
|
|
492
|
+
<button
|
|
493
|
+
class="button secondary copy-value"
|
|
494
|
+
type="button"
|
|
495
|
+
data-copy-target="result-http-url"
|
|
496
|
+
data-copy-label="HTTP endpoint"
|
|
497
|
+
aria-label="Copy HTTP endpoint"
|
|
498
|
+
>
|
|
499
|
+
Copy
|
|
500
|
+
</button>
|
|
501
|
+
</dd>
|
|
502
|
+
</div>
|
|
503
|
+
<div>
|
|
504
|
+
<dt>Authorization</dt>
|
|
505
|
+
<dd><code>Bearer local-only</code></dd>
|
|
506
|
+
</div>
|
|
507
|
+
<div>
|
|
508
|
+
<dt>Content-Type</dt>
|
|
509
|
+
<dd><code>application/json</code></dd>
|
|
510
|
+
</div>
|
|
511
|
+
<div>
|
|
512
|
+
<dt>Authentication</dt>
|
|
513
|
+
<dd>None</dd>
|
|
514
|
+
</div>
|
|
515
|
+
<div class="sample-body-row">
|
|
516
|
+
<dt>JSON body</dt>
|
|
517
|
+
<dd class="result-value">
|
|
518
|
+
<pre class="sample-json"><code id="result-http-body"></code></pre>
|
|
519
|
+
<button
|
|
520
|
+
class="button secondary copy-value"
|
|
521
|
+
type="button"
|
|
522
|
+
data-copy-target="result-http-body"
|
|
523
|
+
data-copy-label="HTTP JSON body"
|
|
524
|
+
aria-label="Copy HTTP JSON body"
|
|
525
|
+
>
|
|
526
|
+
Copy
|
|
527
|
+
</button>
|
|
528
|
+
</dd>
|
|
529
|
+
</div>
|
|
530
|
+
</dl>
|
|
531
|
+
<button
|
|
532
|
+
id="copy-http-recipe"
|
|
533
|
+
class="button secondary"
|
|
534
|
+
type="button"
|
|
535
|
+
data-copy-group="http"
|
|
536
|
+
data-copy-label="HTTP request recipe"
|
|
537
|
+
>
|
|
538
|
+
Copy HTTP request recipe
|
|
539
|
+
</button>
|
|
540
|
+
</div>
|
|
541
|
+
</details>
|
|
444
542
|
|
|
445
543
|
<p class="final-note">
|
|
446
544
|
The README includes complete AI Agent, Basic LLM Chain, JSON, and
|
package/src/ui/styles.css
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
--radius-sm: 0.5rem;
|
|
30
30
|
--radius: 0.875rem;
|
|
31
31
|
--radius-lg: 1.25rem;
|
|
32
|
+
--header-height: 3.75rem;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
:root[data-theme="dark"] {
|
|
@@ -160,8 +161,8 @@ a:focus-visible {
|
|
|
160
161
|
display: flex;
|
|
161
162
|
align-items: center;
|
|
162
163
|
justify-content: space-between;
|
|
163
|
-
min-height:
|
|
164
|
-
padding: 0.
|
|
164
|
+
min-height: var(--header-height);
|
|
165
|
+
padding: 0.5rem clamp(1rem, 4vw, 3rem);
|
|
165
166
|
border-bottom: 1px solid rgb(221 227 223 / 75%);
|
|
166
167
|
background: color-mix(in srgb, var(--background) 82%, transparent);
|
|
167
168
|
backdrop-filter: blur(14px) saturate(1.4);
|
|
@@ -179,8 +180,8 @@ a:focus-visible {
|
|
|
179
180
|
|
|
180
181
|
.brand-mark {
|
|
181
182
|
display: block;
|
|
182
|
-
width:
|
|
183
|
-
height:
|
|
183
|
+
width: 1.75rem;
|
|
184
|
+
height: 1.75rem;
|
|
184
185
|
flex: none;
|
|
185
186
|
border-radius: 0.4375rem;
|
|
186
187
|
transition:
|
|
@@ -302,15 +303,23 @@ a:focus-visible {
|
|
|
302
303
|
/* ---------- Shell & intro ---------- */
|
|
303
304
|
|
|
304
305
|
.shell {
|
|
305
|
-
width: min(100% - 2rem,
|
|
306
|
+
width: min(100% - 2rem, 62rem);
|
|
306
307
|
margin: 0 auto;
|
|
307
|
-
padding:
|
|
308
|
+
padding: 1.75rem 0 3.5rem;
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
.intro {
|
|
311
312
|
max-width: 44rem;
|
|
312
313
|
}
|
|
313
314
|
|
|
315
|
+
body:not([data-current-step="1"]) .intro {
|
|
316
|
+
display: none;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
body:not([data-current-step="1"]) .shell {
|
|
320
|
+
padding-top: 1rem;
|
|
321
|
+
}
|
|
322
|
+
|
|
314
323
|
.eyebrow,
|
|
315
324
|
.step-kicker {
|
|
316
325
|
margin: 0 0 0.5rem;
|
|
@@ -330,8 +339,8 @@ p {
|
|
|
330
339
|
|
|
331
340
|
h1 {
|
|
332
341
|
max-width: 42rem;
|
|
333
|
-
margin-bottom:
|
|
334
|
-
font-size: clamp(
|
|
342
|
+
margin-bottom: 0.75rem;
|
|
343
|
+
font-size: clamp(1.9rem, 5vw, 3rem);
|
|
335
344
|
line-height: 1.04;
|
|
336
345
|
letter-spacing: -0.045em;
|
|
337
346
|
}
|
|
@@ -355,19 +364,37 @@ h3 {
|
|
|
355
364
|
|
|
356
365
|
.lede {
|
|
357
366
|
max-width: 42rem;
|
|
358
|
-
|
|
367
|
+
margin-bottom: 0;
|
|
368
|
+
font-size: 1rem;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* ---------- Toasts ---------- */
|
|
372
|
+
|
|
373
|
+
.toast-stack {
|
|
374
|
+
position: fixed;
|
|
375
|
+
top: calc(var(--header-height) + 0.5rem);
|
|
376
|
+
right: clamp(0.75rem, 3vw, 2rem);
|
|
377
|
+
z-index: 15;
|
|
378
|
+
display: grid;
|
|
379
|
+
gap: 0.5rem;
|
|
380
|
+
width: min(25rem, calc(100vw - 1.5rem));
|
|
381
|
+
max-height: calc(100svh - var(--header-height) - 1rem);
|
|
382
|
+
overflow-y: auto;
|
|
383
|
+
pointer-events: none;
|
|
359
384
|
}
|
|
360
385
|
|
|
361
|
-
|
|
386
|
+
.toast {
|
|
387
|
+
pointer-events: auto;
|
|
388
|
+
}
|
|
362
389
|
|
|
363
390
|
.safety-note {
|
|
364
391
|
position: relative;
|
|
365
392
|
display: grid;
|
|
366
|
-
grid-template-columns:
|
|
393
|
+
grid-template-columns: 2rem minmax(0, 1fr) auto;
|
|
367
394
|
gap: 0.25rem 0.875rem;
|
|
368
395
|
align-items: start;
|
|
369
|
-
margin:
|
|
370
|
-
padding:
|
|
396
|
+
margin: 0;
|
|
397
|
+
padding: 0.875rem 0.875rem 0.875rem 1rem;
|
|
371
398
|
border: 1px solid var(--accent-border);
|
|
372
399
|
border-radius: var(--radius);
|
|
373
400
|
background: var(--accent-soft);
|
|
@@ -376,8 +403,8 @@ h3 {
|
|
|
376
403
|
|
|
377
404
|
.safety-note::before {
|
|
378
405
|
display: grid;
|
|
379
|
-
width:
|
|
380
|
-
height:
|
|
406
|
+
width: 2rem;
|
|
407
|
+
height: 2rem;
|
|
381
408
|
grid-row: span 2;
|
|
382
409
|
place-items: center;
|
|
383
410
|
border-radius: 0.625rem;
|
|
@@ -394,6 +421,7 @@ h3 {
|
|
|
394
421
|
|
|
395
422
|
.safety-note span {
|
|
396
423
|
color: var(--accent-copy);
|
|
424
|
+
font-size: 0.875rem;
|
|
397
425
|
}
|
|
398
426
|
|
|
399
427
|
.backup-note {
|
|
@@ -411,10 +439,41 @@ h3 {
|
|
|
411
439
|
color: var(--warning);
|
|
412
440
|
}
|
|
413
441
|
|
|
442
|
+
.toast-close {
|
|
443
|
+
display: inline-grid;
|
|
444
|
+
width: 1.75rem;
|
|
445
|
+
min-height: 1.75rem;
|
|
446
|
+
padding: 0;
|
|
447
|
+
place-items: center;
|
|
448
|
+
border: 0;
|
|
449
|
+
border-radius: 50%;
|
|
450
|
+
color: var(--text-muted);
|
|
451
|
+
background: transparent;
|
|
452
|
+
box-shadow: none;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
font-size: 1.25rem;
|
|
455
|
+
line-height: 1;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.toast-close:hover {
|
|
459
|
+
color: var(--text);
|
|
460
|
+
background: color-mix(in srgb, var(--text) 9%, transparent);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.toast-close:focus-visible {
|
|
464
|
+
outline: 3px solid #f2a94a;
|
|
465
|
+
outline-offset: 1px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.safety-note .toast-close {
|
|
469
|
+
grid-column: 3;
|
|
470
|
+
grid-row: 1;
|
|
471
|
+
}
|
|
472
|
+
|
|
414
473
|
/* ---------- Stepper ---------- */
|
|
415
474
|
|
|
416
475
|
.steps {
|
|
417
|
-
margin:
|
|
476
|
+
margin: 1.5rem 0 0.75rem;
|
|
418
477
|
}
|
|
419
478
|
|
|
420
479
|
.steps ol {
|
|
@@ -509,12 +568,13 @@ h3 {
|
|
|
509
568
|
|
|
510
569
|
.message,
|
|
511
570
|
.error {
|
|
512
|
-
display:
|
|
513
|
-
|
|
571
|
+
display: grid;
|
|
572
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
573
|
+
align-items: start;
|
|
514
574
|
gap: 0.625rem;
|
|
515
|
-
min-height:
|
|
516
|
-
margin:
|
|
517
|
-
padding: 0.75rem
|
|
575
|
+
min-height: 3rem;
|
|
576
|
+
margin: 0;
|
|
577
|
+
padding: 0.75rem;
|
|
518
578
|
border-radius: var(--radius-sm);
|
|
519
579
|
font-size: 0.9375rem;
|
|
520
580
|
}
|
|
@@ -527,6 +587,7 @@ h3 {
|
|
|
527
587
|
}
|
|
528
588
|
|
|
529
589
|
.message::before {
|
|
590
|
+
align-self: center;
|
|
530
591
|
flex: 0 0 auto;
|
|
531
592
|
width: 0.4375rem;
|
|
532
593
|
height: 0.4375rem;
|
|
@@ -536,6 +597,7 @@ h3 {
|
|
|
536
597
|
}
|
|
537
598
|
|
|
538
599
|
.error {
|
|
600
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
539
601
|
border: 1px solid var(--danger-border);
|
|
540
602
|
color: var(--danger);
|
|
541
603
|
background: var(--danger-soft);
|
|
@@ -546,7 +608,7 @@ h3 {
|
|
|
546
608
|
/* ---------- Panels ---------- */
|
|
547
609
|
|
|
548
610
|
.panel {
|
|
549
|
-
padding: clamp(1.
|
|
611
|
+
padding: clamp(1.25rem, 3vw, 1.75rem);
|
|
550
612
|
border: 1px solid var(--border);
|
|
551
613
|
border-radius: var(--radius-lg);
|
|
552
614
|
background: var(--surface);
|
|
@@ -555,7 +617,7 @@ h3 {
|
|
|
555
617
|
}
|
|
556
618
|
|
|
557
619
|
.panel-heading {
|
|
558
|
-
margin-bottom: 1.
|
|
620
|
+
margin-bottom: 1.125rem;
|
|
559
621
|
}
|
|
560
622
|
|
|
561
623
|
.panel-heading p {
|
|
@@ -955,7 +1017,7 @@ code {
|
|
|
955
1017
|
|
|
956
1018
|
.model-box,
|
|
957
1019
|
.final-note {
|
|
958
|
-
margin-top:
|
|
1020
|
+
margin-top: 1rem;
|
|
959
1021
|
padding: 1.125rem;
|
|
960
1022
|
border: 1px solid var(--border);
|
|
961
1023
|
border-radius: var(--radius);
|
|
@@ -971,6 +1033,71 @@ code {
|
|
|
971
1033
|
color: var(--text-muted);
|
|
972
1034
|
}
|
|
973
1035
|
|
|
1036
|
+
.recipe-disclosure {
|
|
1037
|
+
margin-top: 1rem;
|
|
1038
|
+
border: 1px solid var(--border);
|
|
1039
|
+
border-radius: var(--radius);
|
|
1040
|
+
background: var(--surface-muted);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.recipe-disclosure summary {
|
|
1044
|
+
display: flex;
|
|
1045
|
+
align-items: center;
|
|
1046
|
+
justify-content: space-between;
|
|
1047
|
+
gap: 0.75rem;
|
|
1048
|
+
padding: 0.875rem 1rem;
|
|
1049
|
+
cursor: pointer;
|
|
1050
|
+
font-weight: 750;
|
|
1051
|
+
list-style: none;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
.recipe-disclosure summary::-webkit-details-marker {
|
|
1055
|
+
display: none;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
.recipe-disclosure summary::after {
|
|
1059
|
+
flex: none;
|
|
1060
|
+
color: var(--accent);
|
|
1061
|
+
content: "+";
|
|
1062
|
+
font-size: 1.25rem;
|
|
1063
|
+
line-height: 1;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
.recipe-disclosure[open] summary::after {
|
|
1067
|
+
content: "−";
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
.recipe-disclosure summary:focus-visible {
|
|
1071
|
+
outline: 3px solid #f2a94a;
|
|
1072
|
+
outline-offset: -3px;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
.recipe-content {
|
|
1076
|
+
margin: 0;
|
|
1077
|
+
border: 0;
|
|
1078
|
+
border-top: 1px solid var(--border);
|
|
1079
|
+
border-radius: 0;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
.sample-body-row {
|
|
1083
|
+
align-items: start !important;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.sample-json {
|
|
1087
|
+
max-width: 100%;
|
|
1088
|
+
margin: 0;
|
|
1089
|
+
overflow-x: auto;
|
|
1090
|
+
white-space: pre-wrap;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
.sample-json code {
|
|
1094
|
+
display: block;
|
|
1095
|
+
padding: 0.75rem;
|
|
1096
|
+
border: 1px solid var(--border);
|
|
1097
|
+
background: var(--surface);
|
|
1098
|
+
line-height: 1.45;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
974
1101
|
footer {
|
|
975
1102
|
padding: 1.5rem 1rem;
|
|
976
1103
|
border-top: 1px solid var(--border);
|
|
@@ -1072,8 +1199,15 @@ footer p {
|
|
|
1072
1199
|
}
|
|
1073
1200
|
|
|
1074
1201
|
.shell {
|
|
1075
|
-
width: min(100% - 1rem,
|
|
1076
|
-
padding
|
|
1202
|
+
width: min(100% - 1rem, 62rem);
|
|
1203
|
+
padding: 1.25rem 0 2.5rem;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
.toast-stack {
|
|
1207
|
+
top: calc(var(--header-height) + 0.375rem);
|
|
1208
|
+
right: 0.5rem;
|
|
1209
|
+
width: calc(100vw - 1rem);
|
|
1210
|
+
gap: 0.375rem;
|
|
1077
1211
|
}
|
|
1078
1212
|
|
|
1079
1213
|
.safety-note,
|
|
@@ -1086,6 +1220,19 @@ footer p {
|
|
|
1086
1220
|
grid-row: auto;
|
|
1087
1221
|
}
|
|
1088
1222
|
|
|
1223
|
+
.toast-stack .safety-note {
|
|
1224
|
+
grid-template-columns: 2rem minmax(0, 1fr) auto;
|
|
1225
|
+
align-items: center;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
.toast-stack .safety-note::before {
|
|
1229
|
+
grid-row: auto;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
.toast-stack .safety-note > span {
|
|
1233
|
+
display: none;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1089
1236
|
.steps li em {
|
|
1090
1237
|
font-size: 0.6875rem;
|
|
1091
1238
|
}
|
|
@@ -1108,6 +1255,10 @@ footer p {
|
|
|
1108
1255
|
grid-template-columns: 1fr;
|
|
1109
1256
|
gap: 0.25rem;
|
|
1110
1257
|
}
|
|
1258
|
+
|
|
1259
|
+
.sample-body-row dd.result-value {
|
|
1260
|
+
align-items: stretch;
|
|
1261
|
+
}
|
|
1111
1262
|
}
|
|
1112
1263
|
|
|
1113
1264
|
@media (prefers-reduced-motion: reduce) {
|