import json
import uuid
import urllib.request
import urllib.parse
import ssl
import asyncio
import websockets
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.image import Image
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.modalview import ModalView
from kivy.core.window import Window
from kivy.graphics import Color, Rectangle, RoundedRectangle, BorderImage, Line, Mesh, Ellipse, StencilPush, StencilUse, StencilUnUse, StencilPop, Triangle
from kivy.properties import StringProperty, ObjectProperty, BooleanProperty, NumericProperty, ListProperty
from kivy.clock import Clock
from kivy.metrics import dp
from kivy.lang import Builder
from kivy.utils import get_color_from_hex
from kivy.animation import Animation
from kivy.core.image import Image as CoreImage
from kivy.effects.scroll import ScrollEffect
from kivy.effects.dampedscroll import DampedScrollEffect
from kivy.uix.relativelayout import RelativeLayout
import os
import random
import string
import requests
from io import BytesIO
from PIL import Image as PILImage
import time
from math import sin, cos, pi, radians

Enhanced color palette with better visibility

MAIN_ORANGE = (0.75, 0.30, 0.08, 1.0) # Deeper, more muted base orange
LIGHT_ORANGE = (0.85, 0.40, 0.15, 1.0) # Darker but still distinguishable
DARK_ORANGE = (0.55, 0.20, 0.03, 1.0) # Much darker, almost brown-orange
GLASS_HIGHLIGHT = (1.0, 1.0, 1.0, 0.25) # Stronger glass highlight
GLASS_SHADOW = (0.2, 0.1, 0.05, 0.25) # Deeper shadow

Enhanced glass effect colors

GLASS_TOP = (1.0, 1.0, 1.0, 0.55) # More prominent top highlight
GLASS_MIDDLE = (1.0, 1.0, 1.0, 0.3) # More visible middle layer
GLASS_BOTTOM = (1.0, 1.0, 1.0, 0.15) # Subtle but visible bottom

Enhanced gradient colors

GRADIENT_TOP = (1.0, 0.50, 0.25, 1.0) # Brighter top
GRADIENT_BOTTOM = (0.95, 0.40, 0.15, 1.0) # Richer bottom

Enhanced energy field colors

FIELD_OUTER = (1.0, 0.50, 0.25, 0.2) # More visible outer field
FIELD_INNER = (0.95, 0.40, 0.15, 0.3) # Stronger inner field

System constants

PARTICLE_COUNT = 20
TWO_PI = 2 * pi
CORNER_ROTATION_SPEED = 60 # Degrees per second
HEXAGON_POINTS = 6
ENERGY_PARTICLES = 15

def ensure_assets():
if not os.path.exists(assets_path):
os.makedirs(assets_path)

default_assets = {
    'wait.png': (36, 36, (255, 255, 255, 0)),
    'out.png': (512, 512, (40, 40, 40)),
    'floppy.png': (36, 36, (255, 255, 255, 255)),
    'bg.png': (1920, 1080, (0, 0, 0, 0))
}

for filename, (width, height, color) in default_assets.items():
    filepath = os.path.join(assets_path, filename)
    if not os.path.exists(filepath):
        img = PILImage.new('RGBA', (int(width), int(height)), color)
        img.save(filepath)

Path and window setup

is_mobile = os.path.exists("/storage/emulated/0")

if is_mobile:
assets_path = "/storage/emulated/0/python/assets"
save_path = "/storage/emulated/0/Pictures"
settings_path = "/storage/emulated/0/python/settings"
else:
base_path = os.path.dirname(os.path.abspath(file))
assets_path = os.path.join(base_path, "assets")
save_path = os.path.join(os.path.expanduser("~"), "Pictures")
settings_path = os.path.join(base_path, "settings")

from kivy.config import Config
Config.set('graphics', 'resizable', True)
Config.set('kivy', 'keyboard_mode', 'systemanddock')

if is_mobile:
Window.softinput_mode = 'below_target'
Window.keyboard_anim_args = {'d': .2, 't': 'in_out_expo'}

1
2
3
4
5
def update_window_size(*args):
    Window.size = Window.system_size

Window.bind(on_resize=update_window_size)
Clock.schedule_once(lambda dt: update_window_size(), 0)

else:
Window.size = (1440, 720)
Window.left = 100
Window.top = 100

Window.minimum_width = 320
Window.minimum_height = 480

Builder.load_string('''

: import get_color_from_hex kivy.utils.get_color_from_hex

: set primary_dark '#1A1A1A'

: set secondary_dark '#2D2D2D'

: set accent_orange '#FF5722'

: set accent_light '#FF8A65'

: set text_primary '#FFFFFF'

: set text_secondary '#B3B3B3'

<MainButton>:
background_normal: ''
background_color: 0, 0, 0, 0
font_name: 'Roboto'
font_size: dp(18)
bold: True
text: 'Generate'
glow_radius: 0
border_width: dp(2)
canvas.after:
Color:
rgba: 0, 0, 0, 0.2
Rectangle:
pos: self.pos[0] + dp(2), self.pos[1] - dp(2)
size: self.size

<GlassButton>:
background_color: 0, 0, 0, 0
color: get_color_from_hex(text_primary)
font_size: '13sp'
bold: True
padding: dp(8), dp(4)

canvas.before:
    Color:
        rgba: (*get_color_from_hex(accent_orange)[:3], 0.9) if self.state == 'normal' else (*get_color_from_hex(accent_light)[:3], 0.9)
    RoundedRectangle:
        pos: self.pos
        size: self.size
        radius: [dp(5)]

    Color:
        rgba: 1, 1, 1, 0.1
    RoundedRectangle:
        pos: self.x, self.y + self.height * 0.5
        size: self.width, self.height * 0.5
        radius: [dp(5), dp(5), 0, 0]

    Color:
        rgba: (*get_color_from_hex(accent_light)[:3], 0.3)
    Line:
        rounded_rectangle: (self.x, self.y, self.width, self.height, dp(5))
        width: 1

<ScrollText>:
background_color: get_color_from_hex(primary_dark)
foreground_color: get_color_from_hex(text_primary)
hint_text_color: get_color_from_hex(text_secondary)
cursor_color: get_color_from_hex(accent_orange)
selection_color: get_color_from_hex(accent_orange + '40')
font_size: '13sp'
padding: dp(5)

<GlassPanel>:
canvas.before:
Color:
rgba: get_color_from_hex(secondary_dark)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [dp(5)]

Color:
    rgba: 1, 1, 1, 0.03
RoundedRectangle:
    pos: self.x, self.y + self.height * 0.5
    size: self.width, self.height * 0.5
    radius: [dp(5), dp(5), 0, 0]

Color:
    rgba: get_color_from_hex(accent_orange + '20')
Line:
    rounded_rectangle: (self.x, self.y, self.width, self.height, dp(5))
    width: 1

CustomButton@Button:
background_color: 0, 0, 0, 0
color: get_color_from_hex(text_primary)
canvas.before:
Color:
rgba: [get_color_from_hex(accent_orange)[:3], 0.9] if self.state == 'normal' else [get_color_from_hex(accent_light)[:3], 0.9]
RoundedRectangle:
pos: self.pos
size: self.size
radius: [dp(3)]
font_size: '13sp'
padding: dp(6), dp(3)

CustomTextInput@TextInput:
background_color: get_color_from_hex(primary_dark)
foreground_color: get_color_from_hex(text_primary)
hint_text_color: get_color_from_hex(text_secondary)
cursor_color: get_color_from_hex(accent_orange)
selection_color: get_color_from_hex(accent_orange + '40')
font_size: '13sp'
padding: dp(5)
multiline: False

CustomLabel@Label:
color: get_color_from_hex(text_primary)
font_size: '13sp'

<ScrollView>:
bar_width: dp(5)
bar_color: [get_color_from_hex(accent_orange)[:3], 0.8]
bar_inactive_color: [
get_color_from_hex(secondary_dark)[:3], 0.4]
effect_cls: "ScrollEffect"

<ModalView>:
background_color: get_color_from_hex(primary_dark)
canvas.before:
Color:
rgba: get_color_from_hex(secondary_dark)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [dp(5)]

<BoxLayout>:
padding: dp(1)
spacing: dp(1)

<FloatLayout>:
canvas.before:
Color:
rgba: get_color_from_hex(primary_dark)
Rectangle:
pos: self.pos
size: self.size

<ImageViewer>:
canvas.before:
Color:
rgba: get_color_from_hex(primary_dark)
Rectangle:
pos: self.pos
size: self.size

<FullscreenImage>:
canvas.before:
Color:
rgba: get_color_from_hex(primary_dark + 'F0')
Rectangle:
pos: self.pos
size: self.size

1
2
3
4
5
6
7
8
9
BoxLayout:
    padding: dp(3)

    canvas.before:
        Color:
            rgba: 0, 0, 0, 0.7
        Rectangle:
            pos: self.pos
            size: self.size

NotificationPanel@BoxLayout:
canvas.before:
Color:
rgba: get_color_from_hex(accent_orange + 'E6')
RoundedRectangle:
pos: self.pos
size: self.size
radius: [dp(3)]
''')

class EnergyParticle:
def init(self, x, y, angle, speed, size, lifetime):
self.x = x
self.y = y
self.angle = angle
self.speed = speed
self.size = size
self.lifetime = lifetime
self.max_lifetime = lifetime
self.active = True

1
2
3
4
5
6
7
8
def update(self, dt):
    self.lifetime -= dt
    if self.lifetime <= 0:
        self.active = False
        return

    self.x += cos(self.angle) * self.speed * dt
    self.y += sin(self.angle) * self.speed * dt

class MainButton(Button):
glow_radius = NumericProperty(0)
border_width = NumericProperty(dp(2))
border_color = ListProperty(MAIN_ORANGE)

def __init__(self, **kwargs):
    super(MainButton, self).__init__(**kwargs)
    self._time = time.time()
    self._pulse_alpha = 0
    self._energy_alpha = 0
    self._shine_pos = 0
    self._glass_shine_pos = 0
    self._particles = []
    self._corner_angles = [0] * 4
    self._particle_life = [random.random() for _ in range(PARTICLE_COUNT)]
    self._particle_speed = [random.random() * 0.5 + 0.5 for _ in range(PARTICLE_COUNT)]

    # Properties for enhanced effects
    self._hexagon_rotation = 0
    self._energy_flow = 0
    self._energy_particles = []
    self._energy_level = 0
    self._holo_offset = 0

    self._generate_particles()
    self._init_energy_particles()

    self.fbind('pos', self._update)
    self.fbind('size', self._update)
    self.fbind('state', self._on_state)

    Clock.schedule_once(self._update, 0)
    Clock.schedule_interval(self._ambient_animation, 1/60)

def _draw_glass_effect(self):
    with self.canvas.before:
        # Base glass layer
        StencilPush()
        Color(rgba=(1, 1, 1, 1))
        RoundedRectangle(
            pos=self.pos,
            size=self.size,
            radius=[dp(12)]
        )
        StencilUse()

        # Enhanced glass gradient
        steps = 25  # Increased steps for smoother gradient
        height_segment = self.height / steps
        for i in range(steps):
            progress = i / steps
            alpha = 0.4 - (progress * 0.25)  # Increased alpha values
            Color(rgba=(1, 1, 1, alpha))
            Rectangle(
                pos=(self.pos[0], self.pos[1] + i * height_segment),
                size=(self.width, height_segment)
            )

        # Enhanced top highlight
        Color(rgba=GLASS_TOP)
        Rectangle(
            pos=(self.pos[0], self.pos[1] + self.height * 0.75),
            size=(self.width, self.height * 0.25)
        )

        # Enhanced moving glass reflection
        shine_width = self.width * 0.5  # Wider shine
        shine_height = self.height * 1.5
        shine_x = self.pos[0] - shine_width + (self.width + shine_width * 2) * self._glass_shine_pos
        Color(rgba=(1, 1, 1, 0.15))  # Slightly stronger shine
        Mesh(
            vertices=[
                shine_x, self.pos[1], 0, 0,
                shine_x + shine_width, self.pos[1], 1, 0,
                shine_x + shine_width * 0.7, self.pos[1] + shine_height, 1, 1,
                shine_x - shine_width * 0.3, self.pos[1] + shine_height, 0, 1],
            indices=[0, 1, 2, 2, 3, 0],
            mode='triangles'
        )

        StencilUnUse()
        StencilPop()

def _init_energy_particles(self):
    self._energy_particles = []
    for _ in range(ENERGY_PARTICLES):
        self._create_energy_particle()

def _create_energy_particle(self):
    angle = random.random() * TWO_PI
    speed = random.random() * 100 + 50
    size = random.random() * dp(3) + dp(1)
    lifetime = random.random() * 2 + 1
    particle = EnergyParticle(
        random.random(), random.random(),
        angle, speed, size, lifetime
    )
    self._energy_particles.append(particle)

def _generate_particles(self):
    self._particles = [
        (random.random(), random.random(),
         random.random() * TWO_PI,
         random.random() * 0.4 + 0.3)
        for _ in range(PARTICLE_COUNT)
    ]

def _draw_hexagonal_border(self):
    with self.canvas.before:
        center_x = self.pos[0] + self.width / 2
        center_y = self.pos[1] + self.height / 2
        radius = min(self.width, self.height) * 0.3
        points = []

        for i in range(HEXAGON_POINTS):
            angle = radians(i * 60 + self._hexagon_rotation)
            points.extend([
                center_x + cos(angle) * radius,
                center_y + sin(angle) * radius
            ])

        # Enhanced hexagon layers with better visibility
        for i in range(3):
            Color(rgba=(
                LIGHT_ORANGE[0],
                LIGHT_ORANGE[1],
                LIGHT_ORANGE[2],
                (0.3 - i * 0.05) * self._energy_level  # Increased base alpha
            ))
            Line(points=points, width=self.border_width - dp(i * 0.5))

def _draw_energy_particles(self):
    with self.canvas.before:
        for particle in self._energy_particles:
            life_ratio = particle.lifetime / particle.max_lifetime
            Color(rgba=(
                LIGHT_ORANGE[0],
                LIGHT_ORANGE[1],
                LIGHT_ORANGE[2],
                life_ratio * 0.4  # Increased visibility
            ))

            Ellipse(
                pos=(
                    self.pos[0] + particle.x * self.width - particle.size/2,
                    self.pos[1] + particle.y * self.height - particle.size/2
                ),
                size=(particle.size, particle.size)
            )

def _draw_corner_decoration(self, pos, angle):
    with self.canvas.before:
        # Enhanced outer triangle
        Color(rgba=(1.0, 0.8, 0.6, 0.9))  # Increased visibility
        size = dp(10)  # Slightly larger size
        Triangle(points=[
            pos[0], pos[1],
            pos[0] + size * cos(radians(angle)),
            pos[1] + size * sin(radians(angle)),
            pos[0] + size * cos(radians(angle + 90)),
            pos[1] + size * sin(radians(angle + 90))
        ])

        # Enhanced inner triangle
        Color(rgba=(1.0, 0.9, 0.7, 0.8))  # Increased visibility
        size_small = dp(5)
        Triangle(points=[
            pos[0], pos[1],
            pos[0] + size_small * cos(radians(angle)),
            pos[1] + size_small * sin(radians(angle)),
            pos[0] + size_small * cos(radians(angle + 90)),
            pos[1] + size_small * sin(radians(angle + 90))
        ])

def _on_state(self, instance, value):
    Animation.cancel_all(self)
    if value == 'down':
        anim = Animation(
            glow_radius=dp(15),  # Increased glow radius
            border_width=dp(3),
            duration=0.15,
            t='out_quad'
        )
    else:
        anim = Animation(
            glow_radius=dp(0),
            border_width=dp(2),
            duration=0.25,
            t='out_quad'
        )
    anim.start(self)

def _ambient_animation(self, dt):
    self._time += dt
    self._pulse_alpha = (sin(self._time * 2) + 1) * 0.2  # Increased pulse visibility
    self._energy_alpha = (sin(self._time * 1.5) + 1) * 0.6  # Enhanced energy effect
    self._shine_pos = (self._shine_pos + dt * 4.5) % 3
    self._glass_shine_pos = (self._glass_shine_pos + dt * 1.8) % 5
    self._hexagon_rotation += dt * 30  # 30 degrees per second
    self._energy_flow = (self._energy_flow + dt * 1.2) % 1
    self._energy_level = (sin(self._time * 1.5) + 1) * 0.6  # Increased energy level

    # Update corner rotations
    rotation = dt * CORNER_ROTATION_SPEED
    self._corner_angles = [(angle + rotation) % 360 for angle in self._corner_angles]

    # Update regular particles
    for i, (x, y, angle, speed) in enumerate(self._particles):
        new_x = (x + cos(angle) * dt * speed) % 1
        new_y = (y + sin(angle) * dt * speed) % 1
        self._particles[i] = (new_x, new_y, angle, speed)

        self._particle_life[i] -= 0.01 * self._particle_speed[i]
        if self._particle_life[i] <= 0:
            self._particle_life[i] = 1.0
            self._particles[i] = (random.random(), random.random(),
                                random.random() * TWO_PI,
                                random.random() * 0.4 + 0.3)

    # Update energy particles
    for particle in self._energy_particles:
        particle.update(dt)
        if not particle.active:
            self._energy_particles.remove(particle)
            self._create_energy_particle()

    self._update()

def _update(self, *args):
    self.canvas.before.clear()
    with self.canvas.before:
        # Enhanced outer glow with improved flickering effect
        for i in range(6):  # Added an extra layer
            glow_alpha = (0.15 - i * 0.02) * (0.7 + 0.3 * sin(self._time * 8 + i))
            Color(rgba=(*LIGHT_ORANGE[:3], glow_alpha))
            RoundedRectangle(
                pos=(self.pos[0] - dp(3+i*2), self.pos[1] - dp(3+i*2)),
                size=(self.width + dp(6+i*4), self.height + dp(6+i*4)),
                radius=[dp(15+i*2)]
            )

        # Enhanced base button color
        Color(rgba=DARK_ORANGE)
        RoundedRectangle(
            pos=self.pos,
            size=self.size,
            radius=[dp(12)]
        )

        # Enhanced energy field effect
        Color(rgba=(*FIELD_OUTER[:3], FIELD_OUTER[3] * self._energy_level * 1.2))  # Increased visibility
        RoundedRectangle(
            pos=(self.pos[0] - dp(4), self.pos[1] - dp(4)),
            size=(self.width + dp(8), self.height + dp(8)),
            radius=[dp(14)]
        )

        # Base gradient and enhanced effects
        self._draw_glass_effect()

        # Enhanced energy particles
        self._draw_energy_particles()

        # Enhanced hexagonal rotating symbol
        self._draw_hexagonal_border()

        # Enhanced particle effects
        for i, (x, y, angle, speed) in enumerate(self._particles):
            particle_alpha = self._particle_life[i] * 0.6 * (0.6 + self._pulse_alpha)  # Increased visibility
            Color(rgba=(*LIGHT_ORANGE[:3], particle_alpha))
            size = dp(2 + self._particle_life[i] * 2)  # Slightly larger particles
            Rectangle(
                pos=(self.pos[0] + x * self.width - size/2,
                     self.pos[1] + y * self.height - size/2),
                size=(size, size)
            )

        # Enhanced corner decorations
        for i, angle in enumerate(self._corner_angles):
            if i == 0:
                self._draw_corner_decoration(
                    (self.pos[0] + dp(4), self.pos[1] + self.height - dp(4)),
                    angle)
            elif i == 1:
                self._draw_corner_decoration(
                    (self.pos[0] + self.width - dp(4), self.pos[1] + self.height - dp(4)),
                    angle + 90)
            elif i == 2:
                self._draw_corner_decoration(
                    (self.pos[0] + self.width - dp(4), self.pos[1] + dp(4)),
                    angle + 180)
            else:
                self._draw_corner_decoration(
                    (self.pos[0] + dp(4), self.pos[1] + dp(4)),
                    angle + 270)

        # Enhanced border effect
        for i in range(3):  # Added an extra layer for more depth
            Color(rgba=(*LIGHT_ORANGE[:3],
                    0.95 - i * 0.25 + self._pulse_alpha * 0.2))
            Line(
                rounded_rectangle=(
                    self.pos[0] + dp(i), self.pos[1] + dp(i),
                    self.width - dp(i*2), self.height - dp(i*2),
                    dp(12-i)
                ),
                width=self.border_width - dp(i*0.3)
            )

    # Enhanced text shadow with better visibility
    self.canvas.after.clear()
    with self.canvas.after:
        Color(rgba=(0, 0, 0, 0.3))  # Slightly darker shadow
        Rectangle(
            pos=(self.pos[0] + dp(1), self.pos[1] - dp(1)),  # Adjusted offset
            size=(self.width, self.height)
        )

    # Enhanced text color with better contrast
    self.color = [1, 1, 1, 0.98]  # Slightly increased opacity for better readability

class ScrollText(TextInput):
def init(self, kwargs):
super().init(
kwargs)
self.background_color = get_color_from_hex('#383838')
self.foreground_color = get_color_from_hex('#FFFFFF')
self.cursor_color = get_color_from_hex('#8C5EF2')
self.selection_color = get_color_from_hex('#FF8C0080')
self.hint_text_color = get_color_from_hex('#CCCCCC')

    if hasattr(self, '_text'):
        self._text.color = self.foreground_color

def on_touch_down(self, touch):
    if self.collide_point(*touch.pos):
        anim = Animation(background_color=get_color_from_hex('#404040'), duration=0.2)
        anim.start(self)
    return super().on_touch_down(touch)

def on_touch_up(self, touch):
    if self.collide_point(*touch.pos):
        anim = Animation(background_color=get_color_from_hex('#383838'), duration=0.2)
        anim.start(self)
    return super().on_touch_up(touch)

class GlassPanel(BoxLayout):
def init(self, kwargs):
super().init(
kwargs)
self.orientation = kwargs.get('orientation', 'vertical')
self.padding = dp(10)
self.spacing = dp(6)
self.opacity = 0

anim = Animation(opacity=1, duration=0.3, transition='out_quad')
Clock.schedule_once(lambda dt: anim.start(self), 0.1)

class ImageViewer(Image):
def init(self, kwargs):
super().init(
kwargs)
self.allow_stretch = True
self.keep_ratio = True
self.opacity = 0
self.size_hint = (1, 1)

    # Add smooth fade-in animation
    anim = Animation(opacity=1, duration=0.3, transition='out_quad')
    Clock.schedule_once(lambda dt: anim.start(self), 0.2)

def on_touch_down(self, touch):
    # Get the actual image position and size within the widget
    image_size = self.norm_image_size
    image_pos = self.get_image_pos()

    # Check if touch is within the actual image bounds
    if (image_pos[0] <= touch.x <= image_pos[0] + image_size[0] and
        image_pos[1] <= touch.y <= image_pos[1] + image_size[1]):
        anim = Animation(opacity=0.8, duration=0.1) + Animation(opacity=1, duration=0.1)
        anim.start(self)
        self.show_fullscreen()
        return True
    return super().on_touch_down(touch)

def get_image_pos(self):
    # Calculate the actual position of the image within the widget
    image_size = self.norm_image_size
    x = self.center_x - image_size[0] / 2
    y = self.center_y - image_size[1] / 2
    return (x, y)

def show_fullscreen(self):
    popup = FullscreenImage(self.source)
    popup.open()

class FullscreenImage(ModalView):
def init(self, image_source, kwargs):
super().init(
kwargs)
self.background_color = (0, 0, 0, 0)
self.size_hint = (1, 1)

    self.layout = FloatLayout()

    with self.layout.canvas.before:
        Color(rgba=get_color_from_hex('#000000F0'))
        self.bg_rect = Rectangle(pos=self.pos, size=self.size)

    self.image = Image(
        source=image_source,
        allow_stretch=True,
        keep_ratio=True,
        pos_hint={'center_x': 0.5, 'center_y': 0.5},
        size_hint=(0.95, 0.95),
        opacity=0
    )
    self.layout.add_widget(self.image)

    # Save button with icon
    floppy_path = os.path.join(assets_path, 'floppy.png')
    self.save_btn = Button(
        background_normal=floppy_path,
        background_down=floppy_path,
        size_hint=(None, None),
        size=(dp(48), dp(48)),
        pos_hint={'x': 0.02, 'top': 0.98},
        opacity=0
    )
    self.save_btn.bind(on_release=self._save_image)
    self.layout.add_widget(self.save_btn)

    # Notification panel
    self.notification_bg = BoxLayout(
        size_hint=(None, None),
        size=(dp(280), dp(44)),
        pos_hint={'center_x': 0.5, 'top': 0.95},
        opacity=0
    )
    with self.notification_bg.canvas.before:
        Color(rgba=get_color_from_hex('#8C5EF2'))
        self.rect = RoundedRectangle(
            pos=self.notification_bg.pos,
            size=self.notification_bg.size,
            radius=[dp(10)]
        )

    self.notification = Label(
        text='',
        size_hint=(1, 1),
        color=(1, 1, 1, 1),
        font_size='16sp'
    )
    self.notification_bg.add_widget(self.notification)
    self.layout.add_widget(self.notification_bg)

    self.notification_bg.bind(pos=self._update_rect, size=self._update_rect)
    self.bind(pos=self._update_bg, size=self._update_bg)

    self.add_widget(self.layout)

    # Smooth opening animation
    def start_animations(dt):
        Animation(background_color=(0, 0, 0, 0.97), duration=0.3).start(self)
        Animation(opacity=1, duration=0.4, transition='out_quad').start(self.image)
        Animation(opacity=1, duration=0.5, transition='out_quad').start(self.save_btn)
    Clock.schedule_once(start_animations, 0)

def _update_rect(self, instance, value):
    self.rect.pos = instance.pos
    self.rect.size = instance.size

def _update_bg(self, instance, value):
    self.bg_rect.pos = instance.pos
    self.bg_rect.size = instance.size

def on_touch_down(self, touch):
    if not self.save_btn.collide_point(*touch.pos):
        anim = Animation(opacity=0, duration=0.2)
        anim.bind(on_complete=lambda *x: self.dismiss())
        anim.start(self.image)
        Animation(background_color=(0, 0, 0, 0), duration=0.3).start(self)
        return True
    return super().on_touch_down(touch)

def show_notification(self, text):
    self.notification.text = text
    anim = (Animation(opacity=1, duration=0.2) +
            Animation(opacity=1, duration=1.5) +
            Animation(opacity=0, duration=0.3))
    anim.start(self.notification_bg)

def _save_image(self, instance):
    try:
        random_name = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
        save_file = os.path.join(save_path, f"{random_name}.png")

        os.makedirs(save_path, exist_ok=True)

        import shutil
        shutil.copy2(self.image.source, save_file)

        # Simple button press animation
        anim = Animation(size=(dp(42), dp(42)), duration=0.1) + Animation(size=(dp(48), dp(48)), duration=0.1)
        anim.start(self.save_btn)

        self.show_notification(f'Saved as: {os.path.basename(save_file)}')

    except Exception as e:
        self.show_notification(f'Save failed: {str(e)}')

class MainLayout(BoxLayout):
def init(self, kwargs):
super().init(
kwargs)
Window.softinput_mode = 'below_target'
self.orientation = 'vertical'
self.spacing = dp(3)
self.opacity = 0

    self._init_top_section()  # Call _init_top_section before accessing prompt_input
    self._init_bottom_section()

    anim = Animation(opacity=1, duration=0.3)
    Clock.schedule_once(lambda dt: anim.start(self), 0.1)

    app = App.get_running_app()
    if app.last_image_source:
        self.preview_image.source = app.last_image_source
        self.preview_image.reload()

    try:
        with open(os.path.join(settings_path, "prompt.txt"), "r", encoding='utf-8') as f:
            prompt_text = f.read()
        with open(os.path.join(settings_path, "negative.txt"), "r", encoding='utf-8') as f:
            negative_text = f.read()

        self.prompt_input.text = prompt_text
        self.negative_input.text = negative_text
    except FileNotFoundError:
        print(f"Error: prompt.txt or negative.txt not found in {settings_path}")
        pass

def _init_top_section(self):
    top_section = BoxLayout(
        orientation='horizontal',
        size_hint_y=0.3,  # Use relative size
        spacing=dp(3)
    )

    input_panel = GlassPanel(
        orientation='vertical',
        size_hint_x=0.85
    )
    self.prompt_input = ScrollText(
        size_hint_y=1,
        text=self.load_text("prompt.txt")
    )
    input_panel.add_widget(self.prompt_input)

    control_panel = GlassPanel(
        orientation='vertical',
        size_hint_x=0.15,
        padding=dp(3)
    )
    wait_icon_path = os.path.join(assets_path, 'wait.png')
    self.wait_icon = Image(
        source=wait_icon_path,
        size_hint=(None, None),
        size=(dp(24), dp(24)),
        opacity=0,
        pos_hint={'center_x': 0.5}
    )
    self.generate_btn = MainButton(
        size_hint_y=None,
        height=dp(44),
        on_release=self.generate_image
    )
    control_panel.add_widget(self.wait_icon)
    control_panel.add_widget(self.generate_btn)

    top_section.add_widget(input_panel)
    top_section.add_widget(control_panel)
    self.add_widget(top_section)

def _init_bottom_section(self):
    bottom_section = BoxLayout(
        orientation='horizontal',
        size_hint_y=0.7,  # Use relative size
        spacing=dp(3)
    )

    preview_panel = GlassPanel(
        orientation='vertical',
        size_hint_x=0.85
    )
    preview_panel.padding = 0

    default_img_path = os.path.join(assets_path, 'out.png')
    self.preview_image = ImageViewer(
        source=default_img_path,
        size_hint=(1, 1),
        pos_hint={'top': 1}
    )
    preview_panel.add_widget(self.preview_image)

    negative_panel = GlassPanel(
        orientation='vertical',
        size_hint_x=0.15
    )
    self.negative_input = ScrollText(
        size_hint_y=1,
        text=self.load_text("negative.txt")
    )
    negative_panel.add_widget(self.negative_input)

    bottom_section.add_widget(preview_panel)
    bottom_section.add_widget(negative_panel)

    self.add_widget(bottom_section)

def load_text(self, filename):
    try:
        with open(os.path.join(settings_path, filename), "r", encoding='utf-8') as f:
            return f.read()
    except FileNotFoundError:
        return ""

def _init_bottom_section(self):
    bottom_section = BoxLayout(
        orientation='horizontal',
        size_hint_y=0.7,  # Use relative size
        spacing=dp(3)
    )

    preview_panel = GlassPanel(
        orientation='vertical',
        size_hint_x=0.85
    )
    preview_panel.padding = 0

    default_img_path = os.path.join(assets_path, 'out.png')
    self.preview_image = ImageViewer(
        source=default_img_path,
        size_hint=(1, 1),
        pos_hint={'top': 1}
    )
    preview_panel.add_widget(self.preview_image)

    negative_panel = GlassPanel(
        orientation='vertical',
        size_hint_x=0.15
    )
    self.negative_input = ScrollText(
        size_hint_y=1,
        text=self.load_text("negative.txt")  # Correct filename here
    )
    negative_panel.add_widget(self.negative_input)

    bottom_section.add_widget(preview_panel)
    bottom_section.add_widget(negative_panel)

    self.add_widget(bottom_section)


def _update_image_height(self, instance, width):  # Define the method
    if instance.texture:
        ratio = instance.texture.height / instance.texture.width
        instance.height = width * ratio


def on_size(self, *args):
    Clock.schedule_once(self._update_layout, 0)

def _update_layout(self, dt):
    is_portrait = Window.height > Window.width
    self.padding = dp(5) if is_portrait else dp(7)
    self.spacing = dp(5) if is_portrait else dp(7)

    base_font_size = 15
    width_factor = Window.width / 1440
    adjusted_font_size = max(13, min(17, base_font_size * width_factor))

    self.prompt_input.font_size = f'{adjusted_font_size}sp'
    self.negative_input.font_size = f'{adjusted_font_size}sp'
    self.generate_btn.height = dp(40) if is_portrait else dp(50)

    # Update the layout of child widgets
    self.clear_widgets()
    self._init_top_section()
    self._init_bottom_section()

def generate_image(self, instance):
    prompt = self.prompt_input.text
    negative_prompt = self.negative_input.text

    try:
        os.makedirs(settings_path, exist_ok=True)
        with open(os.path.join(settings_path, "prompt.txt"), "w", encoding='utf-8') as f:
            f.write(prompt)
        with open(os.path.join(settings_path, "negative.txt"), "w", encoding='utf-8') as f:
            f.write(negative_prompt)
    except Exception as e:
        print(f"Error saving settings: {e}")

    self.generate_btn.disabled = True
    self.wait_icon.opacity = 1
    anim = Animation(opacity=0.7, duration=0.4) + Animation(opacity=1, duration=0.4)
    anim.repeat = True
    anim.start(self.wait_icon)

    asyncio.run(self._generate_image_api(prompt, negative_prompt))

async def _generate_image_api(self, prompt, negative):
    try:
        with open(os.path.join(settings_path, "workflow.json"), 'r') as f:
            prompt_json = json.load(f)

        prompt_json["6"]["inputs"]["text"] = prompt
        prompt_json["7"]["inputs"]["text"] = negative
        prompt_json["3"]["inputs"]["seed"] = random.randint(0, 999999999)
        prompt_json["4"]["inputs"]["ckpt_name"] = "YOUR MODEL NAME"
        ws_url = "YOUR URL"

        client_id = str(uuid.uuid4())

        async def queue_prompt(prompt):
            p = {"prompt": prompt, "client_id": client_id}
            data = json.dumps(p).encode('utf-8')
            req = urllib.request.Request(f"https://{ws_url}/prompt", data=data, headers={'Content-Type': 'application/json'})
            response = urllib.request.urlopen(req, context=ssl.create_default_context())
            return json.loads(response.read())

        async def get_image(filename, subfolder, folder_type):
            data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
            url_values = urllib.parse.urlencode(data)
            with urllib.request.urlopen(f"https://{ws_url}/view?{url_values}", context=ssl.create_default_context()) as response:
                return response.read()

        async def get_history(prompt_id):
            with urllib.request.urlopen(f"https://{ws_url}/history/{prompt_id}", context=ssl.create_default_context()) as response:
                return json.loads(response.read())

        async def get_images(ws, prompt):
            prompt_id = (await queue_prompt(prompt))['prompt_id']
            while True:
                out = await ws.recv()
                if isinstance(out, str):
                    message = json.loads(out)
                    if message['type'] == 'executing' and message['data']['node'] is None and message['data']['prompt_id'] == prompt_id:
                        break
            history = await get_history(prompt_id)
            history = history[prompt_id]
            for node_id, node_output in history['outputs'].items():
                if 'images' in node_output:
                    for image in node_output['images']:
                        return await get_image(image['filename'], image['subfolder'], image['type'])
            return None

        async with websockets.connect(f"wss://{ws_url}/ws?clientId={client_id}") as ws:
            image_data = await get_images(ws, prompt_json)

        if image_data:
            temp_path = os.path.join(assets_path, "cache", f"temp_image.png")
            os.makedirs(os.path.dirname(temp_path), exist_ok=True)
            with open(temp_path, "wb") as f:
                f.write(image_data)

            def update_preview(dt):
                self.preview_image.source = temp_path
                self.preview_image.reload()
                anim = Animation(opacity=1, duration=0.3, transition='out_quad')
                anim.start(self.preview_image)
                app = App.get_running_app()
                app.last_image_source = temp_path

            Clock.schedule_once(update_preview, 0)
        else:
            print("Error: No image data returned")

    except Exception as e:
        print(f"Image generation error: {e}")

    finally:
        self.wait_icon.opacity = 0
        Animation.cancel_all(self.wait_icon)
        self.generate_btn.disabled = False

class ImageGeneratorApp(App):
last_image_source = StringProperty('')

def build(self):
    self.title = 'AI Image Generator'
    Window.clearcolor = get_color_from_hex('#2C2C2C')

    bg_path = os.path.join(assets_path, 'bg.png')
    background = Image(source=bg_path, allow_stretch=True, keep_ratio=False)

    self.main_layout = MainLayout()
    Window.bind(on_resize=self._on_window_resize)

    root = FloatLayout()
    root.add_widget(background)
    root.add_widget(self.main_layout)

    return root

def _on_window_resize(self, instance, width, height):
    Clock.schedule_once(lambda dt: self.main_layout.on_size(), 0.1)

def on_pause(self):
    return True

def on_resume(self):
    Clock.schedule_once(lambda dt: self.main_layout.on_size(), 0.1)

def on_stop(self):
    cache_dir = os.path.join(assets_path, "cache")
    if os.path.exists(cache_dir):
        import shutil
        try:
            shutil.rmtree(cache_dir)
        except:
            pass

if name == 'main':
try:
ensure_assets()

    if not is_mobile:
        Window.size = (1440, 720)
        Window.left = 100
        Window.top = 100

    app = ImageGeneratorApp()
    app.run()

except Exception as e:
    print(f"Application error: {str(e)}")

finally:
    cache_dir = os.path.join(assets_path, "cache")
    if os.path.exists(cache_dir):
        import shutil
        try:
            shutil.rmtree(cache_dir)
        except:
            pass
Edit Report
Pub: 08 Nov 2024 02:20 UTC
Views: 36