+
+
+
setProp((p: FormContainerProps) => { p.recipientEmail = e.target.value; })}
+ placeholder="you@example.com"
+ style={{ width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 12 }}
+ />
+
+ Delivered via the site's contact-form relay. Requires the relay to be enabled on this server. Leave blank to use the Form Action URL below instead.
+
+
+
+
+
+ setProp((p: FormContainerProps) => { p.thankYouUrl = e.target.value; })}
+ placeholder="/thank-you (blank = hosted page)"
+ style={{ width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 12 }}
+ />
+
+
${childrenHtml}`,
+ html: `${marker}`,
};
};
diff --git a/craft/src/utils/form-relay-wiring.ts b/craft/src/utils/form-relay-wiring.ts
new file mode 100644
index 0000000..3164df7
--- /dev/null
+++ b/craft/src/utils/form-relay-wiring.ts
@@ -0,0 +1,46 @@
+/**
+ * Shared contact-form relay wiring for HTML export.
+ *
+ * Any form component that wants to deliver submissions by email (ContactForm,
+ * FormContainer, ...) emits the SAME marker/placeholder/honeypot shape so the
+ * WHP publish step (`fs_rewrite_contact_forms`) can provision a token, rewrite
+ * the action, and strip the marker. Keeping this in one place means the two
+ * components can't drift apart (a drift would leak the recipient into published
+ * HTML — see PR #47 review).
+ */
+
+const esc = (s: unknown): string =>
+ String(s ?? '').replace(//g, '>').replace(/"/g, '"');
+
+export interface RelayWiring {
+ /** true when a recipient is set (relay path); false = legacy formAction fallback */
+ useRelay: boolean;
+ /** the `` marker (stripped at publish); '' when not relay */
+ marker: string;
+ /** value for the form's `action` attribute (placeholder when relay, else the escaped fallback) */
+ actionAttr: string;
+ /** hidden honeypot `` to render as the form's first child; '' when not relay */
+ honeypot: string;
+}
+
+/**
+ * @param recipientEmail the "Send submissions to" address (empty/undefined = no relay)
+ * @param thankYouUrl optional post-submit redirect (blank = hosted thank-you page)
+ * @param fallbackAction the form's existing action to use when no recipient is set
+ */
+export function relayFormWiring(
+ recipientEmail: string | undefined,
+ thankYouUrl: string | undefined,
+ fallbackAction: string | undefined,
+): RelayWiring {
+ if (!recipientEmail) {
+ return { useRelay: false, marker: '', actionAttr: esc(fallbackAction || '#'), honeypot: '' };
+ }
+ const fid = 'F' + Math.random().toString(36).slice(2, 8);
+ return {
+ useRelay: true,
+ marker: ``,
+ actionAttr: `__WHP_FORM_ACTION__${fid}__`,
+ honeypot: ``,
+ };
+}