diff --git a/Makefile b/Makefile index 3ef5878..d448324 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,7 @@ prepare: bun install --frozen-lockfile @mkdir -p ./internal/handler/views/assets/js cp ./node_modules/htmx.org/dist/htmx.min.js ./internal/handler/views/assets/js + cp ./node_modules/clipboard/dist/clipboard.min.js ./internal/handler/views/assets/js # Compile template .PHONY: templ diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..997b758 Binary files /dev/null and b/bun.lockb differ diff --git a/internal/handler/views/home.templ b/internal/handler/views/home.templ index a2074bd..12ff172 100644 --- a/internal/handler/views/home.templ +++ b/internal/handler/views/home.templ @@ -89,7 +89,6 @@ templ Home() { <div class="group flex flex-col text-center gap-2"> <h2 class="font-semibold text-neutral-200 text-3xl">My Stagenet Public Node</h2> <p>Stagenet is what you need to learn Monero safely. Stagenet is technically equivalent to mainnet, both in terms of features and consensus rules.</p> - <!-- TODO: add copy to clipboard functionality --> <div> <label for="stagenet-p2p" class="sr-only">Stagenet P2P</label> <div class="flex rounded-lg shadow-sm"> @@ -97,7 +96,7 @@ templ Home() { <span class="text-sm text-neutral-400">P2P</span> </span> <input type="text" id="stagenet-p2p" name="stagenet-p2p" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500" value="stagenet.xmr.ditatompel.com:38080" readonly/> - <button type="button" class="w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700"> + <button class="clipboard copy-input" data-clipboard-target="#stagenet-p2p"> Copy </button> </div> @@ -109,7 +108,7 @@ templ Home() { <span class="text-sm text-neutral-400">RPC</span> </span> <input type="text" id="stagenet-rpc" name="stagenet-rpc" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500" value="stagenet.xmr.ditatompel.com:38089" readonly/> - <button type="button" class="w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700"> + <button class="clipboard copy-input" data-clipboard-target="#stagenet-rpc"> Copy </button> </div> @@ -121,7 +120,7 @@ templ Home() { <span class="text-sm text-neutral-400">RPC SSL</span> </span> <input type="text" id="stagenet-ssl" name="stagenet-ssl" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500" value="stagenet.xmr.ditatompel.com:443" readonly/> - <button type="button" class="w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700"> + <button class="clipboard copy-input" data-clipboard-target="#stagenet-ssl"> Copy </button> </div> @@ -131,7 +130,6 @@ templ Home() { <div class="group flex flex-col text-center gap-2"> <h2 class="font-semibold text-neutral-200 text-3xl">My Testnet Public Node</h2> <p>Testnet is the <em>"experimental"</em> network and blockchain where things get released long before mainnet. As a normal user, use mainnet instead.</p> - <!-- TODO: add copy to clipboard functionality --> <div> <label for="testnet-p2p" class="sr-only">Testnet P2P</label> <div class="flex rounded-lg shadow-sm"> @@ -139,7 +137,7 @@ templ Home() { <span class="text-sm text-neutral-400">P2P</span> </span> <input type="text" id="testnet-p2p" name="testnet-p2p" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500" value="testnet.xmr.ditatompel.com:28080" readonly/> - <button type="button" class="w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700"> + <button class="clipboard copy-input" data-clipboard-target="#testnet-p2p"> Copy </button> </div> @@ -151,7 +149,7 @@ templ Home() { <span class="text-sm text-neutral-400">RPC</span> </span> <input type="text" id="testnet-rpc" name="testnet-rpc" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500" value="testnet.xmr.ditatompel.com:28089" readonly/> - <button type="button" class="w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700"> + <button class="clipboard copy-input" data-clipboard-target="#testnet-rpc"> Copy </button> </div> @@ -163,7 +161,7 @@ templ Home() { <span class="text-sm text-neutral-400">RPC SSL</span> </span> <input type="text" id="testnet-ssl" name="testnet-ssl" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500" value="testnet.xmr.ditatompel.com:443" readonly/> - <button type="button" class="w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700"> + <button class="clipboard copy-input" data-clipboard-target="#testnet-ssl"> Copy </button> </div> @@ -199,9 +197,8 @@ templ Home() { <div class="md:basis-3/4"> <p>If you find this project useful, please consider making a donation to help cover the ongoing expenses. Your contribution will go towards ensuring the continued availability of <strong>this website</strong>, my <strong>stagenet</strong> and <strong>testnet</strong> public nodes.</p> <label for="donate" class="block text-sm text-white font-medium my-2">XMR Donation address:</label> - <!-- TODO: add copy to clipboard functionality --> - <textarea id="donate" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-neutral-500 focus:ring-neutral-600" rows="3" disabled>8BWYe6GzbNKbxe3D8mPkfFMQA2rViaZJFhWShhZTjJCNG6EZHkXRZCKHiuKmwwe4DXDYF8KKcbGkvNYaiRG3sNt7JhnVp7D</textarea> - <button type="button" class="mt-2 py-2 px-3 inline-flex items-center gap-x-2 text-sm font-bold rounded-full border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none"> + <textarea id="donate" class="py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-neutral-500 focus:ring-neutral-600" rows="3" readonly>8BWYe6GzbNKbxe3D8mPkfFMQA2rViaZJFhWShhZTjJCNG6EZHkXRZCKHiuKmwwe4DXDYF8KKcbGkvNYaiRG3sNt7JhnVp7D</textarea> + <button type="button" class="clipboard mt-2 py-2 px-3 inline-flex items-center gap-x-2 text-sm font-bold rounded-full border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none disabled:bg-green-600 disabled:opacity-60 disabled:pointer-events-none" data-clipboard-target="#donate" data-success-text="Donation Address Copied! 🤩"> Copy Donation Address </button> </div> diff --git a/internal/handler/views/home_templ.go b/internal/handler/views/home_templ.go index d8856aa..f3c5a64 100644 --- a/internal/handler/views/home_templ.go +++ b/internal/handler/views/home_templ.go @@ -74,7 +74,7 @@ func Home() templ.Component { return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("etc; can be an other good reference for you.</p></div></div></div></section><!-- End Hero --><!-- Alt Nav Section --><section class=\"max-w-5xl px-4 pb-10 sm:px-6 lg:px-8 mx-auto\"><div class=\"grid sm:grid-cols-2 gap-3 sm:gap-6\"><a href=\"/remote-nodes\" class=\"group flex flex-col text-center bg-neutral-900 border border-orange-400 shadow-sm rounded-xl transition hover:shadow-md hover:brightness-125\"><div class=\"p-4 md:p-5\"><div class=\"grow\"><p class=\"font-semibold text-orange-400\">Remote Nodes</p><p class=\"text-sm\">List of submitted Monero remote nodes you can use when you <strong>cannot</strong> run your own node.</p></div></div></a> <a href=\"/remote-nodes\" class=\"group flex flex-col text-center bg-neutral-900 border border-orange-400 shadow-sm rounded-xl transition hover:shadow-md hover:brightness-125\"><div class=\"p-4 md:p-5\"><div class=\"grow\"><p class=\"font-semibold text-orange-400\">Add Node</p><p class=\"text-sm\">Add your Monero public node to be monitored and see how it performs.</p></div></div></a></div></section><!-- End Alt Nav Section --><!-- My Public Nodes Section --><section id=\"my-nodes\" class=\"bg-neutral-800\"><div class=\"max-w-5xl px-4 py-10 sm:px-6 lg:px-8 mx-auto\"><div class=\"max-w-2xl text-center mx-auto mb-8 prose prose-invert\"><p>You can find few resources I provide related to Monero below:</p></div><!-- Grid --><div class=\"grid gap-2 sm:grid-cols-2 sm:gap-3\"><!-- Card --><div class=\"group flex flex-col text-center gap-2\"><h2 class=\"font-semibold text-neutral-200 text-3xl\">My Stagenet Public Node</h2><p>Stagenet is what you need to learn Monero safely. Stagenet is technically equivalent to mainnet, both in terms of features and consensus rules.</p><!-- TODO: add copy to clipboard functionality --><div><label for=\"stagenet-p2p\" class=\"sr-only\">Stagenet P2P</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">P2P</span></span> <input type=\"text\" id=\"stagenet-p2p\" name=\"stagenet-p2p\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"stagenet.xmr.ditatompel.com:38080\" readonly> <button type=\"button\" class=\"w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700\">Copy</button></div></div><div><label for=\"stagenet-rpc\" class=\"sr-only\">Stagenet RPC</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC</span></span> <input type=\"text\" id=\"stagenet-rpc\" name=\"stagenet-rpc\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"stagenet.xmr.ditatompel.com:38089\" readonly> <button type=\"button\" class=\"w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700\">Copy</button></div></div><div><label for=\"stagenet-ssl\" class=\"sr-only\">Stagenet RPC SSL</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC SSL</span></span> <input type=\"text\" id=\"stagenet-ssl\" name=\"stagenet-ssl\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"stagenet.xmr.ditatompel.com:443\" readonly> <button type=\"button\" class=\"w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700\">Copy</button></div></div></div><!-- Card --><div class=\"group flex flex-col text-center gap-2\"><h2 class=\"font-semibold text-neutral-200 text-3xl\">My Testnet Public Node</h2><p>Testnet is the <em>\"experimental\"</em> network and blockchain where things get released long before mainnet. As a normal user, use mainnet instead.</p><!-- TODO: add copy to clipboard functionality --><div><label for=\"testnet-p2p\" class=\"sr-only\">Testnet P2P</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">P2P</span></span> <input type=\"text\" id=\"testnet-p2p\" name=\"testnet-p2p\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"testnet.xmr.ditatompel.com:28080\" readonly> <button type=\"button\" class=\"w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700\">Copy</button></div></div><div><label for=\"testnet-rpc\" class=\"sr-only\">Testnet RPC</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC</span></span> <input type=\"text\" id=\"testnet-rpc\" name=\"testnet-rpc\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"testnet.xmr.ditatompel.com:28089\" readonly> <button type=\"button\" class=\"w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700\">Copy</button></div></div><div><label for=\"testnet-ssl\" class=\"sr-only\">Testnet RPC SSL</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC SSL</span></span> <input type=\"text\" id=\"testnet-ssl\" name=\"testnet-ssl\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"testnet.xmr.ditatompel.com:443\" readonly> <button type=\"button\" class=\"w-[2.875rem] h-[2.875rem] shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none focus:bg-orange-700\">Copy</button></div></div></div></div></div></section><!-- End My Public Nodes Section --><!-- Quote Section --><section class=\"max-w-5xl px-4 py-10 sm:px-6 lg:px-8 mx-auto\"><blockquote class=\"relative\"><svg class=\"absolute -top-6 -start-8 size-16 text-neutral-700\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M7.39762 10.3C7.39762 11.0733 7.14888 11.7 6.6514 12.18C6.15392 12.6333 5.52552 12.86 4.76621 12.86C3.84979 12.86 3.09047 12.5533 2.48825 11.94C1.91222 11.3266 1.62421 10.4467 1.62421 9.29999C1.62421 8.07332 1.96459 6.87332 2.64535 5.69999C3.35231 4.49999 4.33418 3.55332 5.59098 2.85999L6.4943 4.25999C5.81354 4.73999 5.26369 5.27332 4.84476 5.85999C4.45201 6.44666 4.19017 7.12666 4.05926 7.89999C4.29491 7.79332 4.56983 7.73999 4.88403 7.73999C5.61716 7.73999 6.21938 7.97999 6.69067 8.45999C7.16197 8.93999 7.39762 9.55333 7.39762 10.3ZM14.6242 10.3C14.6242 11.0733 14.3755 11.7 13.878 12.18C13.3805 12.6333 12.7521 12.86 11.9928 12.86C11.0764 12.86 10.3171 12.5533 9.71484 11.94C9.13881 11.3266 8.85079 10.4467 8.85079 9.29999C8.85079 8.07332 9.19117 6.87332 9.87194 5.69999C10.5789 4.49999 11.5608 3.55332 12.8176 2.85999L13.7209 4.25999C13.0401 4.73999 12.4903 5.27332 12.0713 5.85999C11.6786 6.44666 11.4168 7.12666 11.2858 7.89999C11.5215 7.79332 11.7964 7.73999 12.1106 7.73999C12.8437 7.73999 13.446 7.97999 13.9173 8.45999C14.3886 8.93999 14.6242 9.55333 14.6242 10.3Z\" fill=\"currentColor\"></path></svg><div class=\"relative z-10 text-center\"><p class=\"text-xl text-white md:text-2xl md:leading-normal\"><em>Since we desire privacy, we must ensure that each party to a transaction have knowledge only of that which is directly necessary for that transaction.</em></p><p class=\"my-2\"><strong>Eric Hughes</strong> in <a href=\"https://www.activism.net/cypherpunk/manifesto.html\" class=\"external\" target=\"_blank\" rel=\"noopener\"><cite title=\"Source Title\">A Cypherpunk's Manifesto</cite></a>.</p></div></blockquote></section><!-- End Quote Section --><hr class=\"border-orange-400 mx-auto max-w-3xl\"><!-- Monero Donation Section --><section id=\"monero-donation\" class=\"max-w-5xl px-4 py-10 sm:px-6 lg:px-8 mx-auto text-center\"><div class=\"mx-auto flex w-full max-w-4xl flex-col md:flex-row items-center gap-2 md:gap-10\"><div class=\"md:basis-3/4\"><p>If you find this project useful, please consider making a donation to help cover the ongoing expenses. Your contribution will go towards ensuring the continued availability of <strong>this website</strong>, my <strong>stagenet</strong> and <strong>testnet</strong> public nodes.</p><label for=\"donate\" class=\"block text-sm text-white font-medium my-2\">XMR Donation address:</label><!-- TODO: add copy to clipboard functionality --><textarea id=\"donate\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-neutral-500 focus:ring-neutral-600\" rows=\"3\" disabled>8BWYe6GzbNKbxe3D8mPkfFMQA2rViaZJFhWShhZTjJCNG6EZHkXRZCKHiuKmwwe4DXDYF8KKcbGkvNYaiRG3sNt7JhnVp7D</textarea> <button type=\"button\" class=\"mt-2 py-2 px-3 inline-flex items-center gap-x-2 text-sm font-bold rounded-full border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none\">Copy Donation Address</button></div><div class=\"md:basis-1/4\"><img src=\"/assets/img/monerotip.png\" class=\"w-full pb-2\" alt=\"ditatompel's monero address\"><p>Thank you so much! It means a lot to me. 🥰</p></div></div></section>") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("etc; can be an other good reference for you.</p></div></div></div></section><!-- End Hero --><!-- Alt Nav Section --><section class=\"max-w-5xl px-4 pb-10 sm:px-6 lg:px-8 mx-auto\"><div class=\"grid sm:grid-cols-2 gap-3 sm:gap-6\"><a href=\"/remote-nodes\" class=\"group flex flex-col text-center bg-neutral-900 border border-orange-400 shadow-sm rounded-xl transition hover:shadow-md hover:brightness-125\"><div class=\"p-4 md:p-5\"><div class=\"grow\"><p class=\"font-semibold text-orange-400\">Remote Nodes</p><p class=\"text-sm\">List of submitted Monero remote nodes you can use when you <strong>cannot</strong> run your own node.</p></div></div></a> <a href=\"/remote-nodes\" class=\"group flex flex-col text-center bg-neutral-900 border border-orange-400 shadow-sm rounded-xl transition hover:shadow-md hover:brightness-125\"><div class=\"p-4 md:p-5\"><div class=\"grow\"><p class=\"font-semibold text-orange-400\">Add Node</p><p class=\"text-sm\">Add your Monero public node to be monitored and see how it performs.</p></div></div></a></div></section><!-- End Alt Nav Section --><!-- My Public Nodes Section --><section id=\"my-nodes\" class=\"bg-neutral-800\"><div class=\"max-w-5xl px-4 py-10 sm:px-6 lg:px-8 mx-auto\"><div class=\"max-w-2xl text-center mx-auto mb-8 prose prose-invert\"><p>You can find few resources I provide related to Monero below:</p></div><!-- Grid --><div class=\"grid gap-2 sm:grid-cols-2 sm:gap-3\"><!-- Card --><div class=\"group flex flex-col text-center gap-2\"><h2 class=\"font-semibold text-neutral-200 text-3xl\">My Stagenet Public Node</h2><p>Stagenet is what you need to learn Monero safely. Stagenet is technically equivalent to mainnet, both in terms of features and consensus rules.</p><div><label for=\"stagenet-p2p\" class=\"sr-only\">Stagenet P2P</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">P2P</span></span> <input type=\"text\" id=\"stagenet-p2p\" name=\"stagenet-p2p\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"stagenet.xmr.ditatompel.com:38080\" readonly> <button class=\"clipboard copy-input\" data-clipboard-target=\"#stagenet-p2p\">Copy</button></div></div><div><label for=\"stagenet-rpc\" class=\"sr-only\">Stagenet RPC</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC</span></span> <input type=\"text\" id=\"stagenet-rpc\" name=\"stagenet-rpc\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"stagenet.xmr.ditatompel.com:38089\" readonly> <button class=\"clipboard copy-input\" data-clipboard-target=\"#stagenet-rpc\">Copy</button></div></div><div><label for=\"stagenet-ssl\" class=\"sr-only\">Stagenet RPC SSL</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC SSL</span></span> <input type=\"text\" id=\"stagenet-ssl\" name=\"stagenet-ssl\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"stagenet.xmr.ditatompel.com:443\" readonly> <button class=\"clipboard copy-input\" data-clipboard-target=\"#stagenet-ssl\">Copy</button></div></div></div><!-- Card --><div class=\"group flex flex-col text-center gap-2\"><h2 class=\"font-semibold text-neutral-200 text-3xl\">My Testnet Public Node</h2><p>Testnet is the <em>\"experimental\"</em> network and blockchain where things get released long before mainnet. As a normal user, use mainnet instead.</p><div><label for=\"testnet-p2p\" class=\"sr-only\">Testnet P2P</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">P2P</span></span> <input type=\"text\" id=\"testnet-p2p\" name=\"testnet-p2p\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"testnet.xmr.ditatompel.com:28080\" readonly> <button class=\"clipboard copy-input\" data-clipboard-target=\"#testnet-p2p\">Copy</button></div></div><div><label for=\"testnet-rpc\" class=\"sr-only\">Testnet RPC</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC</span></span> <input type=\"text\" id=\"testnet-rpc\" name=\"testnet-rpc\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"testnet.xmr.ditatompel.com:28089\" readonly> <button class=\"clipboard copy-input\" data-clipboard-target=\"#testnet-rpc\">Copy</button></div></div><div><label for=\"testnet-ssl\" class=\"sr-only\">Testnet RPC SSL</label><div class=\"flex rounded-lg shadow-sm\"><span class=\"px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-neutral-700 bg-neutral-700 text-sm\"><span class=\"text-sm text-neutral-400\">RPC SSL</span></span> <input type=\"text\" id=\"testnet-ssl\" name=\"testnet-ssl\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 shadow-sm rounded-0 text-sm focus:z-10 focus:border-orange-500 focus:ring-orange-500\" value=\"testnet.xmr.ditatompel.com:443\" readonly> <button class=\"clipboard copy-input\" data-clipboard-target=\"#testnet-ssl\">Copy</button></div></div></div></div></div></section><!-- End My Public Nodes Section --><!-- Quote Section --><section class=\"max-w-5xl px-4 py-10 sm:px-6 lg:px-8 mx-auto\"><blockquote class=\"relative\"><svg class=\"absolute -top-6 -start-8 size-16 text-neutral-700\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><path d=\"M7.39762 10.3C7.39762 11.0733 7.14888 11.7 6.6514 12.18C6.15392 12.6333 5.52552 12.86 4.76621 12.86C3.84979 12.86 3.09047 12.5533 2.48825 11.94C1.91222 11.3266 1.62421 10.4467 1.62421 9.29999C1.62421 8.07332 1.96459 6.87332 2.64535 5.69999C3.35231 4.49999 4.33418 3.55332 5.59098 2.85999L6.4943 4.25999C5.81354 4.73999 5.26369 5.27332 4.84476 5.85999C4.45201 6.44666 4.19017 7.12666 4.05926 7.89999C4.29491 7.79332 4.56983 7.73999 4.88403 7.73999C5.61716 7.73999 6.21938 7.97999 6.69067 8.45999C7.16197 8.93999 7.39762 9.55333 7.39762 10.3ZM14.6242 10.3C14.6242 11.0733 14.3755 11.7 13.878 12.18C13.3805 12.6333 12.7521 12.86 11.9928 12.86C11.0764 12.86 10.3171 12.5533 9.71484 11.94C9.13881 11.3266 8.85079 10.4467 8.85079 9.29999C8.85079 8.07332 9.19117 6.87332 9.87194 5.69999C10.5789 4.49999 11.5608 3.55332 12.8176 2.85999L13.7209 4.25999C13.0401 4.73999 12.4903 5.27332 12.0713 5.85999C11.6786 6.44666 11.4168 7.12666 11.2858 7.89999C11.5215 7.79332 11.7964 7.73999 12.1106 7.73999C12.8437 7.73999 13.446 7.97999 13.9173 8.45999C14.3886 8.93999 14.6242 9.55333 14.6242 10.3Z\" fill=\"currentColor\"></path></svg><div class=\"relative z-10 text-center\"><p class=\"text-xl text-white md:text-2xl md:leading-normal\"><em>Since we desire privacy, we must ensure that each party to a transaction have knowledge only of that which is directly necessary for that transaction.</em></p><p class=\"my-2\"><strong>Eric Hughes</strong> in <a href=\"https://www.activism.net/cypherpunk/manifesto.html\" class=\"external\" target=\"_blank\" rel=\"noopener\"><cite title=\"Source Title\">A Cypherpunk's Manifesto</cite></a>.</p></div></blockquote></section><!-- End Quote Section --><hr class=\"border-orange-400 mx-auto max-w-3xl\"><!-- Monero Donation Section --><section id=\"monero-donation\" class=\"max-w-5xl px-4 py-10 sm:px-6 lg:px-8 mx-auto text-center\"><div class=\"mx-auto flex w-full max-w-4xl flex-col md:flex-row items-center gap-2 md:gap-10\"><div class=\"md:basis-3/4\"><p>If you find this project useful, please consider making a donation to help cover the ongoing expenses. Your contribution will go towards ensuring the continued availability of <strong>this website</strong>, my <strong>stagenet</strong> and <strong>testnet</strong> public nodes.</p><label for=\"donate\" class=\"block text-sm text-white font-medium my-2\">XMR Donation address:</label> <textarea id=\"donate\" class=\"py-3 px-4 block w-full text-neutral-400 bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-neutral-500 focus:ring-neutral-600\" rows=\"3\" readonly>8BWYe6GzbNKbxe3D8mPkfFMQA2rViaZJFhWShhZTjJCNG6EZHkXRZCKHiuKmwwe4DXDYF8KKcbGkvNYaiRG3sNt7JhnVp7D</textarea> <button type=\"button\" class=\"clipboard mt-2 py-2 px-3 inline-flex items-center gap-x-2 text-sm font-bold rounded-full border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none disabled:bg-green-600 disabled:opacity-60 disabled:pointer-events-none\" data-clipboard-target=\"#donate\" data-success-text=\"Donation Address Copied! 🤩\">Copy Donation Address</button></div><div class=\"md:basis-1/4\"><img src=\"/assets/img/monerotip.png\" class=\"w-full pb-2\" alt=\"ditatompel's monero address\"><p>Thank you so much! It means a lot to me. 🥰</p></div></div></section>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/internal/handler/views/layout.templ b/internal/handler/views/layout.templ index 07ec568..a4f8f39 100644 --- a/internal/handler/views/layout.templ +++ b/internal/handler/views/layout.templ @@ -39,6 +39,7 @@ templ base(m Meta) { <meta property="og:description" content={ m.Description }/> <link href={ fmt.Sprintf("/assets/css/main.min.css?t=%d", buildTime) } rel="stylesheet"/> <script src={ fmt.Sprintf("/assets/js/htmx.min.js?t=%d", buildTime) }></script> + <script src={ fmt.Sprintf("/assets/js/clipboard.min.js?t=%d", buildTime) }></script> <script src={ fmt.Sprintf("/assets/js/main.min.js?t=%d", buildTime) }></script> </head> <body class="bg-neutral-900 text-neutral-400" hx-boost="true" hx-indicator="#hx-indicator-main"> diff --git a/internal/handler/views/layout_templ.go b/internal/handler/views/layout_templ.go index 4b55c46..7269842 100644 --- a/internal/handler/views/layout_templ.go +++ b/internal/handler/views/layout_templ.go @@ -181,14 +181,27 @@ func base(m Meta) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/assets/js/main.min.js?t=%d", buildTime)) + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/assets/js/clipboard.min.js?t=%d", buildTime)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 42, Col: 70} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 42, Col: 75} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></script><script src=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/assets/js/main.min.js?t=%d", buildTime)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 43, Col: 70} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></script></head><body class=\"bg-neutral-900 text-neutral-400\" hx-boost=\"true\" hx-indicator=\"#hx-indicator-main\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -209,12 +222,12 @@ func base(m Meta) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(config.Version) + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(config.Version) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 51, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 52, Col: 50} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -242,12 +255,12 @@ func BaseLayout(m Meta, cmp templ.Component) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var14 := templ.GetChildren(ctx) - if templ_7745c5c3_Var14 == nil { - templ_7745c5c3_Var14 = templ.NopComponent + templ_7745c5c3_Var15 := templ.GetChildren(ctx) + if templ_7745c5c3_Var15 == nil { + templ_7745c5c3_Var15 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var15 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var16 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -265,7 +278,7 @@ func BaseLayout(m Meta, cmp templ.Component) templ.Component { } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = base(m).Render(templ.WithChildren(ctx, templ_7745c5c3_Var15), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = base(m).Render(templ.WithChildren(ctx, templ_7745c5c3_Var16), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -289,9 +302,9 @@ func BlankLayout(cmp templ.Component) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var16 := templ.GetChildren(ctx) - if templ_7745c5c3_Var16 == nil { - templ_7745c5c3_Var16 = templ.NopComponent + templ_7745c5c3_Var17 := templ.GetChildren(ctx) + if templ_7745c5c3_Var17 == nil { + templ_7745c5c3_Var17 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = cmp.Render(ctx, templ_7745c5c3_Buffer) diff --git a/internal/handler/views/src/css/main.css b/internal/handler/views/src/css/main.css index 594c11b..6aed813 100644 --- a/internal/handler/views/src/css/main.css +++ b/internal/handler/views/src/css/main.css @@ -17,6 +17,11 @@ a.external { @apply py-0.5 md:py-3 px-4 md:px-1 border-s-2 md:border-s-0 md:border-b-2 border-orange-400 font-medium text-neutral-200 focus:outline-none; } +/** home page **/ a.btn-link { @apply py-1 px-3 mt-2 inline-flex items-center gap-x-1 text-sm font-medium rounded-lg border border-neutral-700 bg-neutral-800 text-white shadow-sm hover:bg-neutral-700; } +/* my nodes copy input button */ +button.copy-input { + @apply px-2 shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:brightness-125 focus:outline-none focus:bg-orange-700 disabled:opacity-50 disabled:pointer-events-none; +} diff --git a/internal/handler/views/src/js/main.js b/internal/handler/views/src/js/main.js index ae822d6..f3cb4c1 100644 --- a/internal/handler/views/src/js/main.js +++ b/internal/handler/views/src/js/main.js @@ -1,5 +1,25 @@ import "@preline/collapse"; +window.addEventListener("load", () => { + var clipboard = new ClipboardJS(".clipboard"); + clipboard.on("success", function (e) { + let btnText = e.trigger.textContent; + let successText = e.trigger.getAttribute("data-success-text"); + if (successText === null) { + successText = "Copied 👍"; + } + e.trigger.textContent = successText; + e.trigger.disabled = true; + setTimeout(function () { + e.trigger.textContent = btnText; + e.trigger.disabled = false; + }, 1000); + }); + clipboard.on("error", function (e) { + console.error("Clipboard error", e.trigger); + }); +}); + htmx.onLoad(function () { // Auto init preline JS, see https://preline.co/docs/preline-javascript.html // This need to be inside `htmx.onLoad` to be work together with hx-boost. diff --git a/package.json b/package.json index 50ebe4a..a2398e5 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "@preline/collapse": "^2.5.0", "@tailwindcss/forms": "^0.5.9", "@tailwindcss/typography": "^0.5.15", + "clipboard": "^2.0.11", "htmx.org": "^1.9.12", "tailwindcss": "^3.4.14" }