@columenlabs/lumen-ui 0.1.66 → 0.1.68

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.
@@ -48,7 +48,17 @@ function EmailForm({ adapter, view, inline, }) {
48
48
  const [replacing, setReplacing] = useState(!view.current);
49
49
  const [result, setResult] = useState(null);
50
50
  const [busy, setBusy] = useState(null);
51
- useEffect(() => setDraft(blank(view)), [view]);
51
+ const [testTo, setTestTo] = useState(view.me);
52
+ // Each route keeps what was typed for it, so switching back and forth loses nothing.
53
+ const [perRoute, setPerRoute] = useState({});
54
+ // Start over only when the saved settings change, never on a refetch of the same ones.
55
+ const savedKey = `${view.current?.updatedAt ?? ''}|${view.current?.route ?? ''}|${view.current?.url ?? ''}`;
56
+ // biome-ignore lint/correctness/useExhaustiveDependencies: reset only when the saved settings change
57
+ useEffect(() => {
58
+ setDraft(blank(view));
59
+ setReplacing(!view.current);
60
+ setPerRoute({});
61
+ }, [savedKey]);
52
62
  if (!api)
53
63
  return null;
54
64
  const route = view.routes.find((r) => r.id === draft.route);
@@ -58,7 +68,9 @@ function EmailForm({ adapter, view, inline, }) {
58
68
  const run = async (kind) => {
59
69
  setBusy(kind);
60
70
  try {
61
- const check = await (kind === 'check' ? api.check(draft) : api.test(draft));
71
+ const check = await (kind === 'check'
72
+ ? api.check(draft)
73
+ : api.test({ ...draft, testTo: testTo.trim() }));
62
74
  setResult({ for: draft, check });
63
75
  }
64
76
  catch (e) {
@@ -107,6 +119,7 @@ function EmailForm({ adapter, view, inline, }) {
107
119
  : 'Test to save' }), changed && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
108
120
  setDraft(blank(view));
109
121
  setReplacing(!view.current);
122
+ setPerRoute({});
110
123
  setResult(null);
111
124
  }, children: "Revert" })), _jsx(SaveButton, { issues: issues, busy: busy === 'save', disabled: !changed, onSave: save })] }));
112
125
  return (_jsxs("div", { className: "flex flex-col", children: [inline ? (_jsx("div", { className: "flex justify-end pb-2", children: badge })) : (_jsx(SettingsActions, { children: badge })), _jsx(Step, { n: 1, title: "Reach the server through", children: _jsx(LumenChoice, { label: "Reach the server through", value: draft.route, options: view.routes.map((r) => ({
@@ -114,12 +127,23 @@ function EmailForm({ adapter, view, inline, }) {
114
127
  icon: r.id === 'public' ? GlobeIcon : CableIcon,
115
128
  title: (_jsxs("span", { className: "flex w-full items-center gap-2", children: [r.label, r.state && (_jsx("span", { className: "ml-auto font-normal text-gain text-xs", children: r.state }))] })),
116
129
  })), onValueChange: (id) => {
130
+ if (id === draft.route)
131
+ return;
132
+ setPerRoute((m) => ({
133
+ ...m,
134
+ [draft.route]: { url: draft.url, apiKey: draft.apiKey ?? '', replacing },
135
+ }));
136
+ const kept = perRoute[id];
117
137
  const next = view.routes.find((r) => r.id === id);
118
- set({ route: id, ...(next?.url && id !== draft.route ? { url: next.url } : {}) });
138
+ const onSaved = !!view.current && view.current.route === id;
139
+ const url = kept?.url ?? (onSaved ? view.current?.url : undefined) ?? next?.url ?? '';
140
+ // The saved key carries to every route (the same server is often reached either way).
141
+ setReplacing(kept?.replacing ?? !view.current);
142
+ set({ route: id, url, apiKey: kept?.apiKey ?? '' });
119
143
  } }) }), _jsxs(Step, { n: 2, title: "Email server", children: [_jsx(Labelled, { id: `${ids}-url`, label: _jsx(InfoLabel, { info: route?.urlInfo, children: "Server address" }), children: _jsx(Input, { id: `${ids}-url`, className: "font-code text-xs", value: draft.url, placeholder: route?.url ?? 'https://api.resend.com', onChange: (e) => set({ url: e.target.value }) }) }), _jsx(Labelled, { id: `${ids}-key`, label: "API key", children: replacing ? (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { id: `${ids}-key`, type: "password", autoComplete: "off", className: "font-code text-xs", value: draft.apiKey ?? '', onChange: (e) => set({ apiKey: e.target.value }) }), view.current && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
120
144
  setReplacing(false);
121
145
  set({ apiKey: '' });
122
- }, children: "Keep" }))] })) : (_jsxs("div", { className: "flex h-9 items-center gap-3 rounded-md border px-3 text-sm", children: [_jsx("span", { className: "font-code text-muted-foreground text-xs", children: view.current?.keyHint }), _jsx("span", { className: "ml-auto text-gain text-xs", children: "sealed" }), _jsx(Button, { size: "xs", variant: "link", onClick: () => setReplacing(true), children: "Replace" })] })) })] }), _jsx(Step, { n: 3, title: "Sent from", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row", children: [_jsx(Labelled, { id: `${ids}-name`, label: "Name", className: "sm:flex-1", children: _jsx(Input, { id: `${ids}-name`, value: draft.fromName, onChange: (e) => set({ fromName: e.target.value }) }) }), _jsx(Labelled, { id: `${ids}-from`, label: "Address", className: "sm:flex-[1.4]", children: _jsx(Input, { id: `${ids}-from`, type: "email", value: draft.from, placeholder: "hello@example.com", onChange: (e) => set({ from: e.target.value }) }) })] }) }), _jsxs(Step, { n: 4, title: "Test it", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Button, { size: "sm", variant: "outline", disabled: !ready || !!busy, onClick: () => run('check'), children: [busy === 'check' ? _jsx(Spinner, {}) : _jsx(PlugZapIcon, {}), " Check connection"] }), _jsxs(Button, { size: "sm", variant: "outline", disabled: !ready || !!busy, onClick: () => run('test'), children: [busy === 'test' ? _jsx(Spinner, {}) : _jsx(MailIcon, {}), " Send test to ", view.me] })] }), result && same(result.for, draft) && _jsx(Steps, { check: result.check }), !result && view.lastTest && (_jsxs("p", { className: "text-muted-foreground text-xs", children: ["Last ", view.lastTest.kind === 'test' ? 'test' : 'check', " ", when(view.lastTest.at), ":", ' ', view.lastTest.ok ? 'passed' : 'failed', "."] }))] }), inline ? (_jsx("div", { className: "border-t pt-3", children: footer })) : (_jsx(SettingsFooter, { children: footer }))] }));
146
+ }, children: "Keep" }))] })) : (_jsxs("div", { className: "flex h-9 items-center gap-3 rounded-md border px-3 text-sm", children: [_jsx("span", { className: "font-code text-muted-foreground text-xs", children: view.current?.keyHint }), _jsx("span", { className: "ml-auto text-gain text-xs", children: "sealed" }), _jsx(Button, { size: "xs", variant: "link", onClick: () => setReplacing(true), children: "Replace" })] })) })] }), _jsx(Step, { n: 3, title: "Sent from", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row", children: [_jsx(Labelled, { id: `${ids}-name`, label: "Name", className: "sm:flex-1", children: _jsx(Input, { id: `${ids}-name`, value: draft.fromName, onChange: (e) => set({ fromName: e.target.value }) }) }), _jsx(Labelled, { id: `${ids}-from`, label: "Address", className: "sm:flex-[1.4]", children: _jsx(Input, { id: `${ids}-from`, type: "email", value: draft.from, placeholder: "hello@example.com", onChange: (e) => set({ from: e.target.value }) }) })] }) }), _jsxs(Step, { n: 4, title: "Test it", children: [_jsx("div", { className: "flex flex-wrap gap-2", children: _jsxs(Button, { size: "sm", variant: "outline", disabled: !ready || !!busy, onClick: () => run('check'), children: [busy === 'check' ? _jsx(Spinner, {}) : _jsx(PlugZapIcon, {}), " Check connection"] }) }), _jsx(Labelled, { id: `${ids}-test-to`, label: "Send a test email to", children: _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { id: `${ids}-test-to`, type: "email", className: "text-sm", value: testTo, placeholder: view.me, onChange: (e) => setTestTo(e.target.value) }), _jsxs(Button, { size: "sm", variant: "outline", disabled: !ready || !!busy || !testTo.trim(), onClick: () => run('test'), children: [busy === 'test' ? _jsx(Spinner, {}) : _jsx(MailIcon, {}), " Send test"] })] }) }), result && same(result.for, draft) && _jsx(Steps, { check: result.check }), !result && view.lastTest && (_jsxs("p", { className: "text-muted-foreground text-xs", children: ["Last ", view.lastTest.kind === 'test' ? 'test' : 'check', " ", when(view.lastTest.at), ":", ' ', view.lastTest.ok ? 'passed' : 'failed', "."] }))] }), inline ? (_jsx("div", { className: "border-t pt-3", children: footer })) : (_jsx(SettingsFooter, { children: footer }))] }));
123
147
  }
124
148
  function Step({ n, title, children }) {
125
149
  return (_jsxs("section", { className: "flex gap-4 border-t py-5 first-of-type:border-t-0", "aria-label": title, children: [_jsx("span", { className: "num flex size-6 shrink-0 items-center justify-center rounded-full bg-muted font-semibold text-xs", children: n }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-3", children: [_jsx("h3", { className: "font-medium text-sm", children: title }), children] })] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  "@codemirror/lang-javascript": "6.2.5",
46
46
  "@codemirror/lang-json": "6.0.2",
47
47
  "@codemirror/lang-sql": "6.10.0",
48
- "@columenlabs/lumen-ui-types": "0.1.66",
48
+ "@columenlabs/lumen-ui-types": "0.1.68",
49
49
  "@dnd-kit/core": "6.3.1",
50
50
  "@dnd-kit/sortable": "10.0.0",
51
51
  "@dnd-kit/utilities": "3.2.2",