commit 4488e8c02ca4fda6d967e903705f902b9e7ae8ef Author: DobriyKola Date: Wed Dec 31 14:51:29 2025 +0300 add main.py состояние проекта такая чисто демочка diff --git a/main.py b/main.py new file mode 100644 index 0000000..4dd0eda --- /dev/null +++ b/main.py @@ -0,0 +1,1005 @@ +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +import os +import json +import shutil + +class Web3Builder: + def __init__(self, root): + self.root = root + self.root.title("Web3 Site Builder") + self.root.geometry("1200x700") + + self.components = [] + self.selected_component = None + self.drag_data = {"x": 0, "y": 0, "item": None} + + self.bg_color = "#1a1a2e" + self.sidebar_color = "#16213e" + self.text_color = "#ffffff" + + self.setup_ui() + + def setup_ui(self): + style = ttk.Style() + style.theme_use('clam') + + main_container = tk.Frame(self.root, bg=self.bg_color) + main_container.pack(fill=tk.BOTH, expand=True) + + sidebar = tk.Frame(main_container, width=200, bg=self.sidebar_color) + sidebar.pack(side=tk.LEFT, fill=tk.Y) + sidebar.pack_propagate(False) + + tk.Label(sidebar, text="Web3 Components", bg=self.sidebar_color, + fg=self.text_color, font=('Arial', 12, 'bold')).pack(pady=20) + + components = [ + ("Wallet Connect", "🦊", "wallet_connect"), + ("NFT Gallery", "🖼️", "nft_gallery"), + ("Token Balance", "💰", "token_balance"), + ("Swap Interface", "🔄", "swap_interface"), + ("DAO Voting", "🗳️", "dao_voting"), + ("Header", "📝", "header"), + ("Paragraph", "📄", "paragraph"), + ("Button", "🔘", "button"), + ("Image", "🖼️", "image"), + ("Footer", "👇", "footer") + ] + + for name, icon, tag in components: + btn = tk.Button(sidebar, text=f"{icon} {name}", + bg="#0f3460", fg=self.text_color, + relief=tk.FLAT, font=('Arial', 10), + command=lambda t=tag: self.add_component(t)) + btn.pack(pady=5, padx=10, fill=tk.X) + + self.properties_panel = tk.Frame(main_container, width=300, bg=self.sidebar_color) + self.properties_panel.pack(side=tk.RIGHT, fill=tk.Y) + self.properties_panel.pack_propagate(False) + + tk.Label(self.properties_panel, text="Properties", bg=self.sidebar_color, + fg=self.text_color, font=('Arial', 12, 'bold')).pack(pady=20) + + self.canvas = tk.Canvas(main_container, bg=self.bg_color, highlightthickness=0) + self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10) + + self.canvas.bind("", self.start_drag) + self.canvas.bind("", self.drag) + self.canvas.bind("", self.stop_drag) + self.canvas.bind("", self.select_component) + + export_btn = tk.Button(sidebar, text="🚀 Export Website", + bg="#4CAF50", fg="white", + font=('Arial', 11, 'bold'), + command=self.export_site) + export_btn.pack(side=tk.BOTTOM, pady=20, padx=10, fill=tk.X) + + self.init_properties_panel() + self.add_default_components() + + def add_default_components(self): + header = { + 'id': 'header_default', + 'type': 'header', + 'x': 50, 'y': 30, + 'width': 800, 'height': 80, + 'text': 'My Web3 Website', + 'color': '#ffffff', + 'bg_color': '#0f3460', + 'font_size': 36 + } + self.components.append(header) + self.draw_component(header) + + def add_component(self, component_type): + component_id = f"{component_type}_{len(self.components) + 1}" + + defaults = { + 'header': { + 'type': 'header', + 'text': 'New Header', + 'color': '#ffffff', + 'bg_color': '#0f3460', + 'font_size': 24, + 'width': 400, 'height': 60 + }, + 'paragraph': { + 'type': 'paragraph', + 'text': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'color': '#cccccc', + 'font_size': 14, + 'width': 400, 'height': 100 + }, + 'button': { + 'type': 'button', + 'text': 'Click Me', + 'color': '#ffffff', + 'bg_color': '#4CAF50', + 'width': 120, 'height': 40 + }, + 'wallet_connect': { + 'type': 'wallet_connect', + 'text': 'Connect Wallet', + 'bg_color': '#f6851b', + 'width': 200, 'height': 50 + }, + 'nft_gallery': { + 'type': 'nft_gallery', + 'title': 'NFT Gallery', + 'width': 600, 'height': 400 + }, + 'token_balance': { + 'type': 'token_balance', + 'token': 'ETH', + 'width': 300, 'height': 150 + }, + 'swap_interface': { + 'type': 'swap_interface', + 'width': 400, 'height': 300 + }, + 'dao_voting': { + 'type': 'dao_voting', + 'title': 'DAO Proposal', + 'width': 500, 'height': 200 + }, + 'image': { + 'type': 'image', + 'src': 'https://placehold.co/400x300', + 'width': 400, 'height': 300 + }, + 'footer': { + 'type': 'footer', + 'text': '© 2024 Web3 Website', + 'bg_color': '#0f3460', + 'width': 800, 'height': 60 + } + } + + component = defaults.get(component_type, defaults['header']).copy() + component['id'] = component_id + component['x'] = 100 + component['y'] = 150 + len(self.components) * 20 + + self.components.append(component) + self.draw_component(component) + + def draw_component(self, component): + x, y = component['x'], component['y'] + width, height = component['width'], component['height'] + comp_id = component['id'] + + rect = self.canvas.create_rectangle( + x, y, x + width, y + height, + fill=component.get('bg_color', '#2d4059'), + outline='#4a69bd', + width=2, + tags=('component', comp_id) + ) + + label_y = y + height + 15 + self.canvas.create_text( + x + width/2, label_y, + text=component['type'].replace('_', ' ').title(), + fill='#cccccc', + font=('Arial', 9), + tags=('label', comp_id) + ) + + if component['type'] in ['header', 'button', 'wallet_connect']: + self.canvas.create_text( + x + width/2, y + height/2, + text=component.get('text', ''), + fill=component.get('color', '#ffffff'), + font=('Arial', component.get('font_size', 14)), + tags=('text', comp_id) + ) + elif component['type'] == 'paragraph': + self.canvas.create_text( + x + width/2, y + height/2, + text=component.get('text', ''), + fill=component.get('color', '#cccccc'), + font=('Arial', component.get('font_size', 14)), + width=width - 20, + tags=('text', comp_id) + ) + + icons = { + 'wallet_connect': '🦊', + 'nft_gallery': '🖼️', + 'token_balance': '💰', + 'swap_interface': '🔄', + 'dao_voting': '🗳️' + } + + if component['type'] in icons: + self.canvas.create_text( + x + 30, y + 25, + text=icons[component['type']], + font=('Arial', 24), + tags=('icon', comp_id) + ) + + self.canvas.create_text( + x + width/2, y + height/2, + text=component.get('title', component['type'].replace('_', ' ').title()), + fill='#ffffff', + font=('Arial', 16, 'bold'), + tags=('title', comp_id) + ) + + def start_drag(self, event): + items = self.canvas.find_overlapping(event.x-1, event.y-1, event.x+1, event.y+1) + for item in items: + tags = self.canvas.gettags(item) + for tag in tags: + if tag in [comp['id'] for comp in self.components]: + self.drag_data["item"] = tag + self.drag_data["x"] = event.x + self.drag_data["y"] = event.y + self.selected_component = tag + self.show_properties(tag) + self.highlight_component(tag) + return + + def drag(self, event): + if self.drag_data["item"]: + dx = event.x - self.drag_data["x"] + dy = event.y - self.drag_data["y"] + + items = self.canvas.find_withtag(self.drag_data["item"]) + for item in items: + self.canvas.move(item, dx, dy) + + self.drag_data["x"] = event.x + self.drag_data["y"] = event.y + + def stop_drag(self, event): + if self.drag_data["item"]: + for comp in self.components: + if comp['id'] == self.drag_data["item"]: + items = self.canvas.find_withtag(self.drag_data["item"]) + for item in items: + if self.canvas.type(item) == 'rectangle': + coords = self.canvas.coords(item) + comp['x'] = coords[0] + comp['y'] = coords[1] + comp['width'] = coords[2] - coords[0] + comp['height'] = coords[3] - coords[1] + break + self.drag_data["item"] = None + + def select_component(self, event): + items = self.canvas.find_overlapping(event.x-1, event.y-1, event.x+1, event.y+1) + for item in items: + tags = self.canvas.gettags(item) + for tag in tags: + if tag in [comp['id'] for comp in self.components]: + self.selected_component = tag + self.show_properties(tag) + self.highlight_component(tag) + return + + self.selected_component = None + self.clear_highlights() + + def highlight_component(self, component_id): + self.clear_highlights() + items = self.canvas.find_withtag(component_id) + for item in items: + if self.canvas.type(item) == 'rectangle': + self.canvas.itemconfig(item, outline='#00ff00', width=3) + + def clear_highlights(self): + for item in self.canvas.find_withtag('component'): + self.canvas.itemconfig(item, outline='#4a69bd', width=2) + + def init_properties_panel(self): + self.property_widgets = {} + + properties_frame = tk.Frame(self.properties_panel, bg=self.sidebar_color) + properties_frame.pack(fill=tk.X, padx=10, pady=5) + + tk.Label(properties_frame, text="Text:", bg=self.sidebar_color, + fg=self.text_color).grid(row=0, column=0, sticky='w', pady=5) + self.property_widgets['text'] = tk.Entry(properties_frame, width=25) + self.property_widgets['text'].grid(row=0, column=1, pady=5) + + tk.Label(properties_frame, text="Width:", bg=self.sidebar_color, + fg=self.text_color).grid(row=1, column=0, sticky='w', pady=5) + self.property_widgets['width'] = tk.Scale(properties_frame, from_=50, to=800, + orient=tk.HORIZONTAL, length=180) + self.property_widgets['width'].grid(row=1, column=1, pady=5) + + tk.Label(properties_frame, text="Height:", bg=self.sidebar_color, + fg=self.text_color).grid(row=2, column=0, sticky='w', pady=5) + self.property_widgets['height'] = tk.Scale(properties_frame, from_=30, to=600, + orient=tk.HORIZONTAL, length=180) + self.property_widgets['height'].grid(row=2, column=1, pady=5) + + tk.Label(properties_frame, text="Text Color:", bg=self.sidebar_color, + fg=self.text_color).grid(row=3, column=0, sticky='w', pady=5) + self.property_widgets['color'] = tk.Entry(properties_frame, width=25) + self.property_widgets['color'].grid(row=3, column=1, pady=5) + self.property_widgets['color'].insert(0, "#ffffff") + + tk.Label(properties_frame, text="BG Color:", bg=self.sidebar_color, + fg=self.text_color).grid(row=4, column=0, sticky='w', pady=5) + self.property_widgets['bg_color'] = tk.Entry(properties_frame, width=25) + self.property_widgets['bg_color'].grid(row=4, column=1, pady=5) + self.property_widgets['bg_color'].insert(0, "#0f3460") + + update_btn = tk.Button(properties_frame, text="Update Properties", + command=self.update_properties, + bg="#4CAF50", fg="white") + update_btn.grid(row=5, column=0, columnspan=2, pady=20) + + delete_btn = tk.Button(properties_frame, text="Delete Component", + command=self.delete_component, + bg="#f44336", fg="white") + delete_btn.grid(row=6, column=0, columnspan=2, pady=10) + + def show_properties(self, component_id): + component = next((c for c in self.components if c['id'] == component_id), None) + if not component: + return + + self.property_widgets['text'].delete(0, tk.END) + self.property_widgets['text'].insert(0, component.get('text', '')) + + self.property_widgets['width'].set(component.get('width', 200)) + self.property_widgets['height'].set(component.get('height', 50)) + + self.property_widgets['color'].delete(0, tk.END) + self.property_widgets['color'].insert(0, component.get('color', '#ffffff')) + + self.property_widgets['bg_color'].delete(0, tk.END) + self.property_widgets['bg_color'].insert(0, component.get('bg_color', '#0f3460')) + + def update_properties(self): + if not self.selected_component: + return + + for comp in self.components: + if comp['id'] == self.selected_component: + comp['text'] = self.property_widgets['text'].get() + comp['width'] = self.property_widgets['width'].get() + comp['height'] = self.property_widgets['height'].get() + comp['color'] = self.property_widgets['color'].get() + comp['bg_color'] = self.property_widgets['bg_color'].get() + + self.redraw_component(comp) + break + + def delete_component(self): + if not self.selected_component: + return + + items = self.canvas.find_withtag(self.selected_component) + for item in items: + self.canvas.delete(item) + + self.components = [c for c in self.components if c['id'] != self.selected_component] + self.selected_component = None + + def redraw_component(self, component): + items = self.canvas.find_withtag(component['id']) + for item in items: + self.canvas.delete(item) + + self.draw_component(component) + + def export_site(self): + export_dir = filedialog.askdirectory(title="Select Export Directory") + if not export_dir: + return + + site_dir = os.path.join(export_dir, "web3_site") + os.makedirs(site_dir, exist_ok=True) + + html_content = self.generate_html() + css_content = self.generate_css() + js_content = self.generate_js() + + with open(os.path.join(site_dir, "index.html"), "w", encoding="utf-8") as f: + f.write(html_content) + + with open(os.path.join(site_dir, "style.css"), "w", encoding="utf-8") as f: + f.write(css_content) + + with open(os.path.join(site_dir, "script.js"), "w", encoding="utf-8") as f: + f.write(js_content) + + self.copy_assets(site_dir) + + messagebox.showinfo("Export Complete", + f"Website exported to:\n{site_dir}\n\nOpen index.html in your browser!") + + def generate_html(self): + html = ''' + + + + + Web3 Website + + + + + + +
+''' + + for comp in sorted(self.components, key=lambda x: (x['y'], x['x'])): + if comp['type'] == 'header': + html += f''' +
+

{comp.get('text', 'Header')}

+
''' + + elif comp['type'] == 'paragraph': + html += f''' +
+

{comp.get('text', 'Paragraph text')}

+
''' + + elif comp['type'] == 'button': + html += f''' +
+ +
''' + + elif comp['type'] == 'wallet_connect': + html += f''' +
+ +
+
''' + + elif comp['type'] == 'nft_gallery': + html += f''' +
+

{comp.get('title', 'NFT Gallery')}

+
+
+
''' + + elif comp['type'] == 'token_balance': + html += f''' +
+
+

Token Balance

+
0.00 ETH
+
$0.00
+
+
''' + + elif comp['type'] == 'swap_interface': + html += f''' +
+
+

Swap Tokens

+
+ + +
+
+
+ + +
+ +
+
''' + + elif comp['type'] == 'dao_voting': + html += f''' +
+
+

{comp.get('title', 'DAO Proposal')}

+

Proposal #123: Upgrade protocol to v2.0

+
+ + +
+
Connected wallets can vote
+
+
''' + + elif comp['type'] == 'image': + html += f''' +
+ Image +
''' + + elif comp['type'] == 'footer': + html += f''' +
+

{comp.get('text', '© 2024 Web3 Website')}

+
''' + + html += ''' +
+ + + +''' + + return html + + def generate_css(self): + return '''* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); + color: #ffffff; + min-height: 100vh; + padding: 20px; +} + +.container { + position: relative; + min-height: 800px; + margin: 0 auto; + max-width: 1200px; +} + +.component { + position: absolute; + border-radius: 10px; + padding: 20px; + transition: all 0.3s ease; +} + +.component:hover { + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); +} + +header { + background: linear-gradient(90deg, #0f3460 0%, #1a5f9c 100%); + display: flex; + align-items: center; + justify-content: center; + border-radius: 15px; +} + +header h1 { + font-size: 2.5rem; + background: linear-gradient(45deg, #00dbde, #fc00ff); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +.custom-btn { + background: linear-gradient(45deg, #4CAF50, #2E7D32); + color: white; + border: none; + padding: 12px 24px; + border-radius: 25px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + width: 100%; + height: 100%; +} + +.custom-btn:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(76, 175, 80, 0.4); +} + +.wallet-btn { + background: linear-gradient(45deg, #f6851b, #e2761b); + color: white; + border: none; + padding: 15px 30px; + border-radius: 25px; + font-size: 18px; + font-weight: bold; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + width: 100%; + height: 100%; + transition: all 0.3s ease; +} + +.wallet-btn:hover { + transform: translateY(-2px); + box-shadow: 0 5px 20px rgba(246, 133, 27, 0.4); +} + +.wallet-info { + margin-top: 15px; + padding: 15px; + background: rgba(255, 255, 255, 0.1); + border-radius: 10px; + display: none; +} + +.nft-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 20px; + margin-top: 20px; +} + +.nft-item { + background: rgba(255, 255, 255, 0.1); + border-radius: 15px; + padding: 15px; + text-align: center; + transition: transform 0.3s ease; +} + +.nft-item:hover { + transform: translateY(-5px); + background: rgba(255, 255, 255, 0.15); +} + +.nft-item img { + width: 100%; + border-radius: 10px; + margin-bottom: 10px; +} + +.token-card { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 20px; + padding: 25px; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.balance { + font-size: 2.5rem; + font-weight: bold; + margin: 10px 0; +} + +.balance-usd { + font-size: 1.2rem; + opacity: 0.9; +} + +.swap-container { + background: rgba(255, 255, 255, 0.1); + border-radius: 20px; + padding: 25px; + height: 100%; +} + +.swap-input { + background: rgba(255, 255, 255, 0.05); + border-radius: 15px; + padding: 15px; + margin: 15px 0; + display: flex; + align-items: center; +} + +.swap-input input { + background: transparent; + border: none; + color: white; + font-size: 1.5rem; + width: 70%; + outline: none; +} + +.swap-input select { + background: rgba(255, 255, 255, 0.1); + color: white; + border: none; + padding: 10px; + border-radius: 10px; + width: 30%; + cursor: pointer; +} + +.swap-arrow { + text-align: center; + margin: 10px 0; + color: #4CAF50; + font-size: 1.5rem; +} + +.swap-btn { + background: linear-gradient(45deg, #2196F3, #1976D2); + color: white; + border: none; + padding: 15px; + border-radius: 15px; + font-size: 1.1rem; + font-weight: bold; + cursor: pointer; + width: 100%; + margin-top: 20px; + transition: all 0.3s ease; +} + +.swap-btn:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(33, 150, 243, 0.4); +} + +.dao-proposal { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + border-radius: 20px; + padding: 25px; + height: 100%; +} + +.vote-buttons { + display: flex; + gap: 15px; + margin: 20px 0; +} + +.vote-btn { + flex: 1; + padding: 15px; + border: none; + border-radius: 15px; + font-size: 1.1rem; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; +} + +.vote-btn.yes { + background: linear-gradient(45deg, #4CAF50, #2E7D32); + color: white; +} + +.vote-btn.no { + background: linear-gradient(45deg, #f44336, #c62828); + color: white; +} + +.vote-btn:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); +} + +.vote-info { + font-size: 0.9rem; + opacity: 0.8; + margin-top: 15px; +} + +footer { + background: linear-gradient(90deg, #0f3460 0%, #1a5f9c 100%); + display: flex; + align-items: center; + justify-content: center; + border-radius: 15px; +} + +footer p { + font-size: 1.1rem; + opacity: 0.9; +} + +@media (max-width: 768px) { + .component { + position: relative !important; + left: 0 !important; + top: 0 !important; + width: 100% !important; + margin-bottom: 20px; + } + + .container { + min-height: auto; + } +} +''' + + def generate_js(self): + return '''let web3; +let connectedAccount = null; + +async function connectWallet() { + try { + if (window.ethereum) { + web3 = new Web3(window.ethereum); + await window.ethereum.request({ method: 'eth_requestAccounts' }); + const accounts = await web3.eth.getAccounts(); + connectedAccount = accounts[0]; + + const walletInfo = document.getElementById('walletInfo'); + const shortAddress = connectedAccount.substring(0, 6) + '...' + connectedAccount.substring(38); + walletInfo.innerHTML = ` +

Connected: ${shortAddress}

+ + `; + walletInfo.style.display = 'block'; + + updateBalance(); + alert('Wallet connected successfully!'); + } else { + alert('Please install MetaMask!'); + } + } catch (error) { + console.error('Error connecting wallet:', error); + alert('Error connecting wallet'); + } +} + +function disconnectWallet() { + connectedAccount = null; + document.getElementById('walletInfo').style.display = 'none'; + document.getElementById('ethBalance').textContent = '0.00 ETH'; + document.getElementById('ethUsd').textContent = '$0.00'; +} + +async function updateBalance() { + if (!connectedAccount || !web3) return; + + try { + const balanceWei = await web3.eth.getBalance(connectedAccount); + const balanceEth = web3.utils.fromWei(balanceWei, 'ether'); + const formattedBalance = parseFloat(balanceEth).toFixed(4); + + const ethPrice = 3500; + const usdValue = (parseFloat(formattedBalance) * ethPrice).toFixed(2); + + document.getElementById('ethBalance').textContent = `${formattedBalance} ETH`; + document.getElementById('ethUsd').textContent = `$${usdValue} USD`; + + loadMockNFTs(); + } catch (error) { + console.error('Error updating balance:', error); + } +} + +function loadMockNFTs() { + const nftGrid = document.getElementById('nftGrid'); + if (!nftGrid) return; + + const mockNFTs = [ + { name: 'CryptoPunk #1234', image: 'https://placehold.co/150x150/00ffaa/000000?text=CryptoPunk' }, + { name: 'Bored Ape #5678', image: 'https://placehold.co/150x150/ffaa00/000000?text=Bored+Ape' }, + { name: 'Art Blocks #9012', image: 'https://placehold.co/150x150/aa00ff/000000?text=Art+Blocks' }, + { name: 'Doodle #3456', image: 'https://placehold.co/150x150/00aaff/000000?text=Doodle' } + ]; + + nftGrid.innerHTML = mockNFTs.map(nft => ` +
+ ${nft.name} +

${nft.name}

+
+ `).join(''); +} + +function simulateSwap() { + const fromAmount = document.getElementById('fromAmount').value; + const fromToken = document.getElementById('fromToken').value; + const toToken = document.getElementById('toToken').value; + + if (!fromAmount || parseFloat(fromAmount) <= 0) { + alert('Please enter a valid amount'); + return; + } + + const rates = { + 'ETH_DAI': 3500, + 'DAI_ETH': 0.0002857 + }; + + const rateKey = `${fromToken}_${toToken}`; + const rate = rates[rateKey] || 1; + const toAmount = (parseFloat(fromAmount) * rate).toFixed(4); + + document.getElementById('toAmount').value = toAmount; + alert(`Swap successful! ${fromAmount} ${fromToken} = ${toAmount} ${toToken}`); +} + +function vote(choice) { + if (!connectedAccount) { + alert('Please connect your wallet to vote'); + return; + } + + const votes = { + 'yes': Math.floor(Math.random() * 1000) + 700, + 'no': Math.floor(Math.random() * 500) + 300 + }; + + const totalVotes = votes.yes + votes.no; + const yesPercent = Math.round((votes.yes / totalVotes) * 100); + const noPercent = 100 - yesPercent; + + document.querySelector('.vote-btn.yes').textContent = `Yes (${yesPercent}%)`; + document.querySelector('.vote-btn.no').textContent = `No (${noPercent}%)`; + + alert(`Vote recorded: ${choice.toUpperCase()}`); +} + +window.addEventListener('DOMContentLoaded', () => { + if (window.ethereum) { + web3 = new Web3(window.ethereum); + + window.ethereum.on('accountsChanged', (accounts) => { + if (accounts.length > 0) { + connectedAccount = accounts[0]; + updateBalance(); + } else { + disconnectWallet(); + } + }); + + window.ethereum.on('chainChanged', () => { + window.location.reload(); + }); + } + + document.querySelectorAll('.custom-btn').forEach(btn => { + btn.addEventListener('click', function() { + alert('Button clicked!'); + }); + }); + + loadMockNFTs(); +}); +''' + + def copy_assets(self, site_dir): + assets_dir = os.path.join(site_dir, "assets") + os.makedirs(assets_dir, exist_ok=True) + + favicon_path = os.path.join(assets_dir, "favicon.ico") + + readme = """# Web3 Website + +This website was generated using Web3 Site Builder. + +## Features: +- Wallet connection (MetaMask) +- NFT gallery display +- Token balance tracking +- Token swap interface +- DAO voting simulation + +## How to Use: +1. Open index.html in your browser +2. Connect your wallet (MetaMask recommended) +3. Interact with Web3 components + +## Note: +This is a demo site. For production use, implement proper security and connect to real blockchain networks. +""" + + with open(os.path.join(site_dir, "README.md"), "w", encoding="utf-8") as f: + f.write(readme) + +def main(): + root = tk.Tk() + app = Web3Builder(root) + root.mainloop() + +if __name__ == "__main__": + main()