diff --git a/public/index.html b/public/index.html index 5be8f03..6e55927 100644 --- a/public/index.html +++ b/public/index.html @@ -77,8 +77,12 @@ - + diff --git a/public/nostr-chat-widget.js b/public/nostr-chat-widget.js index 92c77b3..286bcaf 100644 --- a/public/nostr-chat-widget.js +++ b/public/nostr-chat-widget.js @@ -48,506 +48,7 @@ EMBED IT WITH: * * */ - -(function() { - 'use strict'; - - // Get configuration from script tag - const scriptTag = document.currentScript; - const csPubkey = scriptTag.getAttribute('data-nostr-pubkey') || 'PUBKEY_TO_RECEICE_MESSAGES'; - const brandName = scriptTag.getAttribute('data-brand-name') || 'Support Team Messaging'; - const primaryColor = scriptTag.getAttribute('data-color') || '#fdad01'; - const secondaryColor = scriptTag.getAttribute('data-color-secondary') || '#000000'; - - // Default relay configuration - const DEFAULT_RELAYS = [ - 'wss://relay.damus.io', - 'wss://relay.primal.net', - 'wss://nos.lol', - 'wss://relay.btcforplebs.com' - ]; - - // Inject Tailwind CSS - const tailwindLink = document.createElement('link'); - tailwindLink.href = 'https://cdn.tailwindcss.com'; - tailwindLink.rel = 'stylesheet'; - document.head.appendChild(tailwindLink); - - // Inject custom styles - const style = document.createElement('style'); - style.textContent = ` - .safe-area-bottom { - padding-bottom: env(safe-area-inset-bottom); - } - #nostr-chat-widget-root > div { - pointer-events: auto !important; - z-index: 99999 !important; - } - `; - document.head.appendChild(style); - - // Create widget container - const widgetRoot = document.createElement('div'); - widgetRoot.id = 'nostr-chat-widget-root'; - document.body.appendChild(widgetRoot); - - // Import map for nostr-tools - const importMap = document.createElement('script'); - importMap.type = 'importmap'; - importMap.textContent = JSON.stringify({ - imports: { - 'nostr-tools': 'https://esm.sh/nostr-tools@1.17.0' - } - }); - document.head.appendChild(importMap); - - // Main widget script - const widgetScript = document.createElement('script'); - widgetScript.type = 'module'; - widgetScript.textContent = ` - import { - relayInit, - generatePrivateKey, - getPublicKey, - getEventHash, - signEvent, - nip19, - nip04 - } from 'nostr-tools'; - - const CONFIG = { - relays: ${JSON.stringify(DEFAULT_RELAYS)}, - csPubkey: '${csPubkey}', - brandName: '${brandName}', - primaryColor: '${primaryColor}', - secondaryColor: '${secondaryColor}' - }; - - let state = { - isOpen: false, - messages: [], - inputMessage: '', - myPrivKey: null, - myPubKey: null, - relays: [], - connected: false, - sessionId: null - }; - - function getSessionKey() { - const stored = localStorage.getItem('nostr_chat_session'); - if (stored) { - try { - const session = JSON.parse(stored); - if (Date.now() - session.created < 24 * 60 * 60 * 1000) { - return session.privKey; - } - } catch (e) {} - } - - const privKey = generatePrivateKey(); - localStorage.setItem('nostr_chat_session', JSON.stringify({ - privKey, - created: Date.now() - })); - return privKey; - } - - async function init() { - state.myPrivKey = getSessionKey(); - state.myPubKey = getPublicKey(state.myPrivKey); - state.sessionId = state.myPubKey.substring(0, 8); - - console.log('🔑 Session Identity:', nip19.npubEncode(state.myPubKey)); - - const relayPromises = CONFIG.relays.map(async (url) => { - try { - const relay = relayInit(url); - - relay.on('connect', () => { - console.log(\`✓ Connected to \${url}\`); - checkConnection(); - }); - - relay.on('disconnect', () => { - console.log(\`✗ Disconnected from \${url}\`); - }); - - await relay.connect(); - return relay; - } catch (error) { - console.error(\`Failed: \${url}:\`, error); - return null; - } - }); - - state.relays = (await Promise.all(relayPromises)).filter(r => r !== null); - - if (state.relays.length === 0) { - addMessage('system', '⚠️ Failed to connect to any relays'); - return; - } - - console.log(\`✓ Connected to \${state.relays.length}/\${CONFIG.relays.length} relays\`); - - subscribeToReplies(); - loadPreviousMessages(); - - state.connected = true; - render(); - } - - function checkConnection() { - const connected = state.relays.some(r => r.status === 1); - state.connected = connected; - render(); - } - - function subscribeToReplies() { - const filters = [{ - kinds: [4], - '#p': [state.myPubKey], - authors: [CONFIG.csPubkey], - since: Math.floor(Date.now() / 1000) - 86400 - }]; - - console.log('🔔 Subscribing to replies...'); - - state.relays.forEach(relay => { - const sub = relay.sub(filters); - - sub.on('event', (event) => { - handleIncomingMessage(event); - }); - - sub.on('eose', () => { - console.log(\`✓ Subscribed: \${relay.url}\`); - }); - }); - } - - function loadPreviousMessages() { - const stored = localStorage.getItem(\`nostr_chat_messages_\${state.sessionId}\`); - if (stored) { - try { - const messages = JSON.parse(stored); - messages.forEach(msg => state.messages.push(msg)); - render(); - } catch (e) {} - } - } - - function saveMessages() { - localStorage.setItem(\`nostr_chat_messages_\${state.sessionId}\`, JSON.stringify(state.messages)); - } - - async function handleIncomingMessage(event) { - try { - if (state.messages.find(m => m.id === event.id)) { - return; - } - - console.log('📨 Received message'); - - const decryptedText = await nip04.decrypt( - state.myPrivKey, - event.pubkey, - event.content - ); - - const message = { - id: event.id, - text: decryptedText, - sender: 'cs', - timestamp: new Date(event.created_at * 1000).toISOString() - }; - - addMessage('cs', decryptedText, message); - - if (!document.hasFocus()) { - const originalTitle = document.title; - document.title = '💬 New message!'; - setTimeout(() => { - document.title = originalTitle; - }, 3000); - } - } catch (error) { - console.error('Error decrypting message:', error); - } - } - - async function sendMessage() { - if (!state.inputMessage.trim()) return; - - const messageText = state.inputMessage; - - try { - console.log('🔐 Encrypting and sending...'); - - const encrypted = await nip04.encrypt( - state.myPrivKey, - CONFIG.csPubkey, - messageText - ); - - let event = { - kind: 4, - created_at: Math.floor(Date.now() / 1000), - tags: [['p', CONFIG.csPubkey]], - content: encrypted, - pubkey: state.myPubKey - }; - - event.id = getEventHash(event); - event.sig = signEvent(event, state.myPrivKey); - - let published = 0; - for (const relay of state.relays) { - try { - await relay.publish(event); - published++; - console.log(\`✓ Published to \${relay.url}\`); - } catch (err) { - console.error(\`✗ Failed: \${relay.url}:\`, err); - } - } - - if (published === 0) { - addMessage('system', '⚠️ Failed to send - no relay connections'); - return; - } - - console.log(\`✓ Published to \${published}/\${state.relays.length} relays\`); - - const message = { - id: event.id, - text: messageText, - sender: 'user', - timestamp: new Date().toISOString() - }; - - addMessage('user', messageText, message); - state.inputMessage = ''; - render(); - - } catch (error) { - console.error('Error sending:', error); - addMessage('system', '⚠️ Failed to send message'); - } - } - - function addMessage(sender, text, fullMessage = null) { - const msg = fullMessage || { - id: Date.now().toString(), - text, - sender, - timestamp: new Date().toISOString() - }; - - state.messages.push(msg); - saveMessages(); - render(); - scrollToBottom(); - } - - function scrollToBottom() { - setTimeout(() => { - const container = document.getElementById('nostr-messages'); - if (container) { - container.scrollTop = container.scrollHeight; - } - }, 100); - } - - function escapeHtml(text) { - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; - } - - function formatTime(timestamp) { - const date = new Date(timestamp); - return date.toLocaleTimeString('en-US', { - hour: 'numeric', - minute: '2-digit', - hour12: true - }); - } - - function render() { - const container = document.getElementById('nostr-chat-widget-root'); - - if (!container) return; - - if (!state.isOpen) { - container.innerHTML = \` -
- -
- \`; - return; - } - - container.innerHTML = \` -
-
-
-
-
-

\${CONFIG.brandName}

-
-
- - \${state.connected ? \`P2P E2EE • \${state.relays.length} relays\` : 'Connecting...'} - -
-
- -
-
- -
- \${state.messages.length === 0 ? \` -
- - - -

Start a conversation

-
- \` : state.messages.map(msg => { - if (msg.sender === 'system') { - return \` -
-
- \${escapeHtml(msg.text)} -
-
- \`; - } else if (msg.sender === 'user') { - return \` -
-
-
- \${escapeHtml(msg.text)} -
-
\${formatTime(msg.timestamp)}
-
-
- \`; - } else if (msg.sender === 'cs') { - return \` -
-
-
-
\${CONFIG.brandName}
- \${escapeHtml(msg.text)} -
-
\${formatTime(msg.timestamp)}
-
-
- \`; - } - return ''; - }).join('')} -
- -
-
- - -
-
-
-
- \`; - - const messageInput = document.getElementById('nostr-message-input'); - if (messageInput) { - messageInput.addEventListener('input', (e) => { - state.inputMessage = e.target.value; - const sendButton = document.querySelector('button[onclick="window.NostrChat.send()"]'); - if (sendButton) { - sendButton.disabled = !state.connected || !e.target.value.trim(); - } - }); - messageInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - sendMessage(); - } - }); - - const messagesContainer = document.getElementById('nostr-messages'); - if (messagesContainer && state.messages.length > 0) { - setTimeout(() => { - messagesContainer.scrollTop = messagesContainer.scrollHeight; - }, 100); - } - - messageInput.focus(); - } - } - - // Expose global API - window.NostrChat = { - open: async () => { - state.isOpen = true; - render(); - if (state.relays.length === 0) { - await init(); - } - }, - close: () => { - state.isOpen = false; - render(); - }, - send: sendMessage - }; - - // Initial render - render(); - `; - - document.body.appendChild(widgetScript); -})(); +!function(){"use strict";const n=document.currentScript,e=n.getAttribute("data-nostr-pubkey")||"PUBKEY_TO_RECEICE_MESSAGES",t=n.getAttribute("data-brand-name")||"Support Team Messaging",s=n.getAttribute("data-color")||"#fdad01",o=n.getAttribute("data-color-secondary")||"#000000",r=document.createElement("link");r.href="https://cdn.tailwindcss.com",r.rel="stylesheet",document.head.appendChild(r);const a=document.createElement("style");a.textContent="\n .safe-area-bottom {\n padding-bottom: env(safe-area-inset-bottom);\n }\n #nostr-chat-widget-root > div {\n pointer-events: auto !important;\n z-index: 99999 !important;\n }\n ",document.head.appendChild(a);const i=document.createElement("div");i.id="nostr-chat-widget-root",document.body.appendChild(i);const l=document.createElement("script");l.type="importmap",l.textContent=JSON.stringify({imports:{"nostr-tools":"https://esm.sh/nostr-tools@1.17.0"}}),document.head.appendChild(l);const d=document.createElement("script");d.type="module",d.textContent=`\n import { \n relayInit,\n generatePrivateKey,\n getPublicKey,\n getEventHash,\n signEvent,\n nip19,\n nip04\n } from 'nostr-tools';\n\n const CONFIG = {\n relays: ${JSON.stringify(["wss://relay.damus.io","wss://relay.primal.net","wss://nos.lol","wss://relay.btcforplebs.com"])},\n csPubkey: '${e}',\n brandName: '${t}',\n primaryColor: '${s}',\n secondaryColor: '${o}'\n };\n\n let state = {\n isOpen: false,\n messages: [],\n inputMessage: '',\n myPrivKey: null,\n myPubKey: null,\n relays: [],\n connected: false,\n sessionId: null\n };\n\n function getSessionKey() {\n const stored = localStorage.getItem('nostr_chat_session');\n if (stored) {\n try {\n const session = JSON.parse(stored);\n if (Date.now() - session.created < 24 * 60 * 60 * 1000) {\n return session.privKey;\n }\n } catch (e) {}\n }\n \n const privKey = generatePrivateKey();\n localStorage.setItem('nostr_chat_session', JSON.stringify({\n privKey,\n created: Date.now()\n }));\n return privKey;\n }\n\n async function init() {\n state.myPrivKey = getSessionKey();\n state.myPubKey = getPublicKey(state.myPrivKey);\n state.sessionId = state.myPubKey.substring(0, 8);\n \n console.log('🔑 Session Identity:', nip19.npubEncode(state.myPubKey));\n \n const relayPromises = CONFIG.relays.map(async (url) => {\n try {\n const relay = relayInit(url);\n \n relay.on('connect', () => {\n console.log(\`✓ Connected to \${url}\`);\n checkConnection();\n });\n \n relay.on('disconnect', () => {\n console.log(\`✗ Disconnected from \${url}\`);\n });\n \n await relay.connect();\n return relay;\n } catch (error) {\n console.error(\`Failed: \${url}:\`, error);\n return null;\n }\n });\n \n state.relays = (await Promise.all(relayPromises)).filter(r => r !== null);\n \n if (state.relays.length === 0) {\n addMessage('system', '⚠️ Failed to connect to any relays');\n return;\n }\n \n console.log(\`✓ Connected to \${state.relays.length}/\${CONFIG.relays.length} relays\`);\n \n subscribeToReplies();\n loadPreviousMessages();\n \n state.connected = true;\n render();\n }\n\n function checkConnection() {\n const connected = state.relays.some(r => r.status === 1);\n state.connected = connected;\n render();\n }\n\n function subscribeToReplies() {\n const filters = [{\n kinds: [4],\n '#p': [state.myPubKey],\n authors: [CONFIG.csPubkey],\n since: Math.floor(Date.now() / 1000) - 86400\n }];\n \n console.log('🔔 Subscribing to replies...');\n\n state.relays.forEach(relay => {\n const sub = relay.sub(filters);\n \n sub.on('event', (event) => {\n handleIncomingMessage(event);\n });\n \n sub.on('eose', () => {\n console.log(\`✓ Subscribed: \${relay.url}\`);\n });\n });\n }\n\n function loadPreviousMessages() {\n const stored = localStorage.getItem(\`nostr_chat_messages_\${state.sessionId}\`);\n if (stored) {\n try {\n const messages = JSON.parse(stored);\n messages.forEach(msg => state.messages.push(msg));\n render();\n } catch (e) {}\n }\n }\n\n function saveMessages() {\n localStorage.setItem(\`nostr_chat_messages_\${state.sessionId}\`, JSON.stringify(state.messages));\n }\n\n async function handleIncomingMessage(event) {\n try {\n if (state.messages.find(m => m.id === event.id)) {\n return;\n }\n \n console.log('📨 Received message');\n \n const decryptedText = await nip04.decrypt(\n state.myPrivKey,\n event.pubkey,\n event.content\n );\n \n const message = {\n id: event.id,\n text: decryptedText,\n sender: 'cs',\n timestamp: new Date(event.created_at * 1000).toISOString()\n };\n \n addMessage('cs', decryptedText, message);\n \n if (!document.hasFocus()) {\n const originalTitle = document.title;\n document.title = '💬 New message!';\n setTimeout(() => {\n document.title = originalTitle;\n }, 3000);\n }\n } catch (error) {\n console.error('Error decrypting message:', error);\n }\n }\n\n async function sendMessage() {\n if (!state.inputMessage.trim()) return;\n\n const messageText = state.inputMessage;\n \n try {\n console.log('🔐 Encrypting and sending...');\n \n const encrypted = await nip04.encrypt(\n state.myPrivKey,\n CONFIG.csPubkey,\n messageText\n );\n \n let event = {\n kind: 4,\n created_at: Math.floor(Date.now() / 1000),\n tags: [['p', CONFIG.csPubkey]],\n content: encrypted,\n pubkey: state.myPubKey\n };\n \n event.id = getEventHash(event);\n event.sig = signEvent(event, state.myPrivKey);\n \n let published = 0;\n for (const relay of state.relays) {\n try {\n await relay.publish(event);\n published++;\n console.log(\`✓ Published to \${relay.url}\`);\n } catch (err) {\n console.error(\`✗ Failed: \${relay.url}:\`, err);\n }\n }\n \n if (published === 0) {\n addMessage('system', '⚠️ Failed to send - no relay connections');\n return;\n }\n \n console.log(\`✓ Published to \${published}/\${state.relays.length} relays\`);\n \n const message = {\n id: event.id,\n text: messageText,\n sender: 'user',\n timestamp: new Date().toISOString()\n };\n \n addMessage('user', messageText, message);\n state.inputMessage = '';\n render();\n \n } catch (error) {\n console.error('Error sending:', error);\n addMessage('system', '⚠️ Failed to send message');\n }\n }\n\n function addMessage(sender, text, fullMessage = null) {\n const msg = fullMessage || {\n id: Date.now().toString(),\n text,\n sender,\n timestamp: new Date().toISOString()\n };\n \n state.messages.push(msg);\n saveMessages();\n render();\n scrollToBottom();\n }\n\n function scrollToBottom() {\n setTimeout(() => {\n const container = document.getElementById('nostr-messages');\n if (container) {\n container.scrollTop = container.scrollHeight;\n }\n }, 100);\n }\n\n function escapeHtml(text) {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { \n hour: 'numeric', \n minute: '2-digit',\n hour12: true \n });\n }\n\n function render() {\n const container = document.getElementById('nostr-chat-widget-root');\n \n if (!container) return;\n\n if (!state.isOpen) {\n container.innerHTML = \`\n
\n \n
\n \`;\n return;\n }\n\n container.innerHTML = \`\n
\n
\n
\n
\n
\n

\${CONFIG.brandName}

\n
\n
\n \n \${state.connected ? \`P2P E2EE • \${state.relays.length} relays\` : 'Connecting...'}\n \n
\n
\n \n
\n
\n\n
\n \${state.messages.length === 0 ? \`\n
\n \n \n \n

Start a conversation

\n
\n \` : state.messages.map(msg => {\n if (msg.sender === 'system') {\n return \`\n
\n
\n \${escapeHtml(msg.text)}\n
\n
\n \`;\n } else if (msg.sender === 'user') {\n return \`\n
\n
\n
\n \${escapeHtml(msg.text)}\n
\n
\${formatTime(msg.timestamp)}
\n
\n
\n \`;\n } else if (msg.sender === 'cs') {\n return \`\n
\n
\n
\n
\${CONFIG.brandName}
\n \${escapeHtml(msg.text)}\n
\n
\${formatTime(msg.timestamp)}
\n
\n
\n \`;\n }\n return '';\n }).join('')}\n
\n\n
\n
\n \n \n
\n
\n
\n
\n \`;\n\n const messageInput = document.getElementById('nostr-message-input');\n if (messageInput) {\n messageInput.addEventListener('input', (e) => {\n state.inputMessage = e.target.value;\n const sendButton = document.querySelector('button[onclick="window.NostrChat.send()"]');\n if (sendButton) {\n sendButton.disabled = !state.connected || !e.target.value.trim();\n }\n });\n messageInput.addEventListener('keypress', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n });\n \n const messagesContainer = document.getElementById('nostr-messages');\n if (messagesContainer && state.messages.length > 0) {\n setTimeout(() => {\n messagesContainer.scrollTop = messagesContainer.scrollHeight;\n }, 100);\n }\n \n messageInput.focus();\n }\n }\n\n // Expose global API\n window.NostrChat = {\n open: async () => {\n state.isOpen = true;\n render();\n if (state.relays.length === 0) {\n await init();\n }\n },\n close: () => {\n state.isOpen = false;\n render();\n },\n send: sendMessage\n };\n\n // Initial render\n render();\n `,document.body.appendChild(d)}(); /*! @@ -635,4 +136,4 @@ Check your Browserslist config to be sure that your targets are set up correctly https://github.com/postcss/autoprefixer#readme https://github.com/browserslist/browserslist#readme -`))}gv.exports=Dr;function Dr(...r){let e;if(r.length===1&&V5(r[0])?(e=r[0],r=void 0):r.length===0||r.length===1&&!r[0]?r=void 0:r.length<=2&&(Array.isArray(r[0])||!r[0])?(e=r[1],r=r[0]):typeof r[r.length-1]=="object"&&(e=r.pop()),e||(e={}),e.browser)throw new Error("Change `browser` option to `overrideBrowserslist` in Autoprefixer");if(e.browserslist)throw new Error("Change `browserslist` option to `overrideBrowserslist` in Autoprefixer");e.overrideBrowserslist?r=e.overrideBrowserslist:e.browsers&&(typeof console!="undefined"&&console.warn&&(of.red?console.warn(of.red(mv.replace(/`[^`]+`/g,n=>of.yellow(n.slice(1,-1))))):console.warn(mv)),r=e.browsers);let t={ignoreUnknownVersions:e.ignoreUnknownVersions,stats:e.stats,env:e.env};function i(n){let s=hv,a=new F5(s.browsers,r,n,t),o=a.selected.join(", ")+JSON.stringify(e);return lf.has(o)||lf.set(o,new j5(s.prefixes,a,e)),lf.get(o)}return{postcssPlugin:"autoprefixer",prepare(n){let s=i({from:n.opts.from,env:e.env});return{OnceExit(a){H5(n,s),e.remove!==!1&&s.processor.remove(a,n),e.add!==!1&&s.processor.add(a,n)}}},info(n){return n=n||{},n.from=n.from||m.cwd(),U5(i(n))},options:e,browsers:r}}Dr.postcss=!0;Dr.data=hv;Dr.defaults=N5.defaults;Dr.info=()=>Dr().info()});var bv={};Ge(bv,{default:()=>W5});var W5,wv=P(()=>{u();W5=[]});var xv={};Ge(xv,{default:()=>G5});var vv,G5,kv=P(()=>{u();Xi();vv=pe(rn()),G5=St(vv.default.theme)});var Av={};Ge(Av,{default:()=>Q5});var Sv,Q5,Cv=P(()=>{u();Xi();Sv=pe(rn()),Q5=St(Sv.default)});u();"use strict";var Y5=vt(_y()),K5=vt($e()),X5=vt(yv()),Z5=vt((wv(),bv)),J5=vt((kv(),xv)),eP=vt((Cv(),Av)),tP=vt((Vs(),_f)),rP=vt((al(),sl)),iP=vt((sa(),sc));function vt(r){return r&&r.__esModule?r:{default:r}}console.warn("cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation");var Ns="tailwind",uf="text/tailwindcss",_v="/template.html",Yt,Ev=!0,Ov=0,ff=new Set,cf,Tv="",Rv=(r=!1)=>({get(e,t){return(!r||t==="config")&&typeof e[t]=="object"&&e[t]!==null?new Proxy(e[t],Rv()):e[t]},set(e,t,i){return e[t]=i,(!r||t==="config")&&pf(!0),!0}});window[Ns]=new Proxy({config:{},defaultTheme:J5.default,defaultConfig:eP.default,colors:tP.default,plugin:rP.default,resolveConfig:iP.default},Rv(!0));function Pv(r){cf.observe(r,{attributes:!0,attributeFilter:["type"],characterData:!0,subtree:!0,childList:!0})}new MutationObserver(async r=>{let e=!1;if(!cf){cf=new MutationObserver(async()=>await pf(!0));for(let t of document.querySelectorAll(`style[type="${uf}"]`))Pv(t)}for(let t of r)for(let i of t.addedNodes)i.nodeType===1&&i.tagName==="STYLE"&&i.getAttribute("type")===uf&&(Pv(i),e=!0);await pf(e)}).observe(document.documentElement,{attributes:!0,attributeFilter:["class"],childList:!0,subtree:!0});async function pf(r=!1){r&&(Ov++,ff.clear());let e="";for(let i of document.querySelectorAll(`style[type="${uf}"]`))e+=i.textContent;let t=new Set;for(let i of document.querySelectorAll("[class]"))for(let n of i.classList)ff.has(n)||t.add(n);if(document.body&&(Ev||t.size>0||e!==Tv||!Yt||!Yt.isConnected)){for(let n of t)ff.add(n);Ev=!1,Tv=e,self[_v]=Array.from(t).join(" ");let{css:i}=await(0,K5.default)([(0,Y5.default)({...window[Ns].config,_hash:Ov,content:{files:[_v],extract:{html:n=>n.split(" ")}},plugins:[...Z5.default,...Array.isArray(window[Ns].config.plugins)?window[Ns].config.plugins:[]]}),(0,X5.default)({remove:!1})]).process(`@tailwind base;@tailwind components;@tailwind utilities;${e}`);(!Yt||!Yt.isConnected)&&(Yt=document.createElement("style"),document.head.append(Yt)),Yt.textContent=i}}})(); +`))}gv.exports=Dr;function Dr(...r){let e;if(r.length===1&&V5(r[0])?(e=r[0],r=void 0):r.length===0||r.length===1&&!r[0]?r=void 0:r.length<=2&&(Array.isArray(r[0])||!r[0])?(e=r[1],r=r[0]):typeof r[r.length-1]=="object"&&(e=r.pop()),e||(e={}),e.browser)throw new Error("Change `browser` option to `overrideBrowserslist` in Autoprefixer");if(e.browserslist)throw new Error("Change `browserslist` option to `overrideBrowserslist` in Autoprefixer");e.overrideBrowserslist?r=e.overrideBrowserslist:e.browsers&&(typeof console!="undefined"&&console.warn&&(of.red?console.warn(of.red(mv.replace(/`[^`]+`/g,n=>of.yellow(n.slice(1,-1))))):console.warn(mv)),r=e.browsers);let t={ignoreUnknownVersions:e.ignoreUnknownVersions,stats:e.stats,env:e.env};function i(n){let s=hv,a=new F5(s.browsers,r,n,t),o=a.selected.join(", ")+JSON.stringify(e);return lf.has(o)||lf.set(o,new j5(s.prefixes,a,e)),lf.get(o)}return{postcssPlugin:"autoprefixer",prepare(n){let s=i({from:n.opts.from,env:e.env});return{OnceExit(a){H5(n,s),e.remove!==!1&&s.processor.remove(a,n),e.add!==!1&&s.processor.add(a,n)}}},info(n){return n=n||{},n.from=n.from||m.cwd(),U5(i(n))},options:e,browsers:r}}Dr.postcss=!0;Dr.data=hv;Dr.defaults=N5.defaults;Dr.info=()=>Dr().info()});var bv={};Ge(bv,{default:()=>W5});var W5,wv=P(()=>{u();W5=[]});var xv={};Ge(xv,{default:()=>G5});var vv,G5,kv=P(()=>{u();Xi();vv=pe(rn()),G5=St(vv.default.theme)});var Av={};Ge(Av,{default:()=>Q5});var Sv,Q5,Cv=P(()=>{u();Xi();Sv=pe(rn()),Q5=St(Sv.default)});u();"use strict";var Y5=vt(_y()),K5=vt($e()),X5=vt(yv()),Z5=vt((wv(),bv)),J5=vt((kv(),xv)),eP=vt((Cv(),Av)),tP=vt((Vs(),_f)),rP=vt((al(),sl)),iP=vt((sa(),sc));function vt(r){return r&&r.__esModule?r:{default:r}}console.warn("cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation");var Ns="tailwind",uf="text/tailwindcss",_v="/template.html",Yt,Ev=!0,Ov=0,ff=new Set,cf,Tv="",Rv=(r=!1)=>({get(e,t){return(!r||t==="config")&&typeof e[t]=="object"&&e[t]!==null?new Proxy(e[t],Rv()):e[t]},set(e,t,i){return e[t]=i,(!r||t==="config")&&pf(!0),!0}});window[Ns]=new Proxy({config:{},defaultTheme:J5.default,defaultConfig:eP.default,colors:tP.default,plugin:rP.default,resolveConfig:iP.default},Rv(!0));function Pv(r){cf.observe(r,{attributes:!0,attributeFilter:["type"],characterData:!0,subtree:!0,childList:!0})}new MutationObserver(async r=>{let e=!1;if(!cf){cf=new MutationObserver(async()=>await pf(!0));for(let t of document.querySelectorAll(`style[type="${uf}"]`))Pv(t)}for(let t of r)for(let i of t.addedNodes)i.nodeType===1&&i.tagName==="STYLE"&&i.getAttribute("type")===uf&&(Pv(i),e=!0);await pf(e)}).observe(document.documentElement,{attributes:!0,attributeFilter:["class"],childList:!0,subtree:!0});async function pf(r=!1){r&&(Ov++,ff.clear());let e="";for(let i of document.querySelectorAll(`style[type="${uf}"]`))e+=i.textContent;let t=new Set;for(let i of document.querySelectorAll("[class]"))for(let n of i.classList)ff.has(n)||t.add(n);if(document.body&&(Ev||t.size>0||e!==Tv||!Yt||!Yt.isConnected)){for(let n of t)ff.add(n);Ev=!1,Tv=e,self[_v]=Array.from(t).join(" ");let{css:i}=await(0,K5.default)([(0,Y5.default)({...window[Ns].config,_hash:Ov,content:{files:[_v],extract:{html:n=>n.split(" ")}},plugins:[...Z5.default,...Array.isArray(window[Ns].config.plugins)?window[Ns].config.plugins:[]]}),(0,X5.default)({remove:!1})]).process(`@tailwind base;@tailwind components;@tailwind utilities;${e}`);(!Yt||!Yt.isConnected)&&(Yt=document.createElement("style"),document.head.append(Yt)),Yt.textContent=i}}})(); \ No newline at end of file