crystalwindow 3.8.9__py3-none-any.whl → 4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
crystalwindow/__init__.py CHANGED
@@ -29,7 +29,7 @@ from .animation import Animation
29
29
  from .collision import check_collision, resolve_collision
30
30
 
31
31
  # === GUI & Extensions ===
32
- from .gui import Button, Label, GUIManager, random_color, hex_to_rgb, Fade
32
+ from .gui import Button, Label, GUIManager, hex_to_rgb, Fade
33
33
  from .gui_ext import Toggle, Slider
34
34
 
35
35
  # === Time ===
@@ -42,13 +42,15 @@ from .draw_text_helper import DrawTextManager
42
42
  from .draw_tool import CrystalDraw
43
43
 
44
44
  # === Misc Helpers ===
45
- from .fun_helpers import random_name, DebugOverlay
45
+ from .fun_helpers import random_name, DebugOverlay, random_color, random_palette, lerp
46
46
  from .camera import Camera
47
47
  from .color_handler import Colors, Color
48
48
 
49
49
  # === 3D Engine ===
50
50
  from .crystal3d import CW3D
51
51
 
52
+ # === AI Engine ===
53
+ from .ai import AI
52
54
 
53
55
  __all__ = [
54
56
  # --- Core ---
@@ -79,11 +81,14 @@ __all__ = [
79
81
  "Clock",
80
82
 
81
83
  # --- Drawing ---
82
- "gradient_rect", "CameraShake", "DrawHelper", "DrawTextManager", "CrystalDraw",
84
+ "gradient_rect", "CameraShake", "DrawHelper", "DrawTextManager", "CrystalDraw",
83
85
 
84
86
  # --- Misc ---
85
- "random_name", "DebugOverlay", "Camera", "Colors", "Color",
87
+ "random_name", "DebugOverlay", "Camera", "Colors", "Color", "random_palette", "lerp"
86
88
 
87
89
  # --- 3D ---
88
90
  "CW3D",
91
+
92
+ # --- AI ---
93
+ "AI",
89
94
  ]
crystalwindow/ai.py ADDED
@@ -0,0 +1,275 @@
1
+ # ==========================================================
2
+ # CrystalAI v0.6 — Unified Engine
3
+ # ----------------------------------------------------------
4
+ # Combines:
5
+ # - v0.4: auto-fix, AST parser, docs index, diff tools
6
+ # - v0.5: personality, library ingestion, safe key check
7
+ # - Groq fallback, local fallback, file analysis
8
+ # ==========================================================
9
+
10
+ import os
11
+ import ast
12
+ import difflib
13
+ from typing import Optional, Dict, Any
14
+
15
+
16
+ # ==========================================================
17
+ # Response Wrapper
18
+ # ==========================================================
19
+ class CrystalAIResponse:
20
+ def __init__(self, text: str, meta: Optional[Dict[str, Any]] = None):
21
+ self.text = text
22
+ self.meta = meta or {}
23
+
24
+ def __str__(self):
25
+ return self.text
26
+
27
+
28
+ # ==========================================================
29
+ # MAIN ENGINE
30
+ # ==========================================================
31
+ class AI:
32
+ DEFAULT_MODEL = "llama-3.1-8b"
33
+ DEFAULT_PERSONALITY = (
34
+ "You are CrystalWindow AI. You help users with Python code, "
35
+ "debugging, errors, docs, and file analysis. "
36
+ "Be friendly, technical, clear, and precise."
37
+ )
38
+ PLACEHOLDER_KEY = "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"
39
+
40
+ # ------------------------------------------------------
41
+ def __init__(self, key=None, model=None):
42
+ # --- KEY VALIDATION ---
43
+ if not key or len(key) < 20 or " " in key:
44
+ print("[CrystalAI] Warning: Invalid or missing key → using placeholder. To get a Fixed Key go to 'console.groq.com/keys'")
45
+ self.key = self.PLACEHOLDER_KEY
46
+ else:
47
+ self.key = key
48
+
49
+ # --- MODEL VALIDATION ---
50
+ if not model or not isinstance(model, str) or len(model) < 3:
51
+ print("[CrystalAI] Unknown model → using default.")
52
+ self.model = self.DEFAULT_MODEL
53
+ else:
54
+ self.model = model
55
+
56
+ # Persona
57
+ self.personality = self.DEFAULT_PERSONALITY
58
+
59
+ # Library knowledge (loaded .py files)
60
+ self.library_context = ""
61
+
62
+ # v0.4 memory system (optional)
63
+ self.memory = []
64
+ self.use_memory = True
65
+
66
+ # v0.4 toggle for forcing local engine
67
+ self.force_local = False
68
+
69
+ # ==========================================================
70
+ # PERSONALITY
71
+ # ==========================================================
72
+ def set_personality(self, txt):
73
+ if not isinstance(txt, str) or len(txt.strip()) < 10:
74
+ print("Oops! thats not how to use it—reverting to default.")
75
+ self.personality = self.DEFAULT_PERSONALITY
76
+ return
77
+
78
+ if len(txt) > 3000:
79
+ print("Oops, personality too long → using default.")
80
+ self.personality = self.DEFAULT_PERSONALITY
81
+ return
82
+
83
+ self.personality = txt.strip()
84
+
85
+ # ==========================================================
86
+ # LIBRARY INGESTION
87
+ # ==========================================================
88
+ def index_library(self, folder):
89
+ """
90
+ Load all Python files as context for smarter answers.
91
+ """
92
+ out = []
93
+ if not os.path.exists(folder):
94
+ print("[CrystalAI] Library folder not found.")
95
+ return
96
+
97
+ for root, _, files in os.walk(folder):
98
+ for f in files:
99
+ if f.endswith(".py"):
100
+ try:
101
+ path = os.path.join(root, f)
102
+ with open(path, "r", encoding="utf8") as fp:
103
+ out.append(f"# FILE: {path}\n" + fp.read() + "\n\n")
104
+ except:
105
+ pass
106
+
107
+ self.library_context = "\n".join(out)
108
+
109
+ # ==========================================================
110
+ # FILE READER
111
+ # ==========================================================
112
+ def _read_file(self, path):
113
+ if not path:
114
+ return None
115
+ if not os.path.exists(path):
116
+ return f"[CrystalAI] file not found: {path}"
117
+ try:
118
+ with open(path, "r", encoding="utf8") as f:
119
+ return f.read()
120
+ except:
121
+ return "[CrystalAI] couldnt read file."
122
+
123
+ # ==========================================================
124
+ # PROMPT BUILDER (Unified)
125
+ # ==========================================================
126
+ def _build_prompt(self, user_text, file_data=None):
127
+ final = (
128
+ f"[SYSTEM]\n{self.personality}\n\n"
129
+ f"[USER]\n{user_text}\n\n"
130
+ )
131
+
132
+ if self.use_memory and self.memory:
133
+ final += "[MEMORY]\n"
134
+ for m in self.memory[-6:]:
135
+ final += f"User: {m['user']}\nAI: {m['ai']}\n"
136
+ final += "\n"
137
+
138
+ if self.library_context:
139
+ final += f"[LIBRARY]\n{self.library_context}\n\n"
140
+
141
+ if file_data:
142
+ final += f"[FILE]\n{file_data}\n\n"
143
+
144
+ return final
145
+
146
+ def _save_memory(self, user, ai):
147
+ self.memory.append({"user": user, "ai": ai})
148
+ if len(self.memory) > 60:
149
+ self.memory.pop(0)
150
+
151
+ # ==========================================================
152
+ # LOCAL FALLBACK AI (v0.4 + v0.5 merged)
153
+ # ==========================================================
154
+ def _local_ai(self, prompt, file_data):
155
+ """
156
+ Uses AST parsing to inspect Python,
157
+ gives real syntax help + fallback personality text.
158
+ """
159
+ if file_data and not file_data.startswith("[CrystalAI]"):
160
+ try:
161
+ ast.parse(file_data)
162
+ if "fix" in prompt.lower() or "error" in prompt.lower():
163
+ return "[LocalAI] File has no syntax errors. What exactly breaks?"
164
+ return "[LocalAI] File parsed OK. Ask me to summarize or fix something."
165
+ except SyntaxError as se:
166
+ # Syntax help
167
+ lineno = se.lineno or 0
168
+ offset = se.offset or 0
169
+ msg = f"[LocalAI] SyntaxError: {se.msg} at line {lineno}, col {offset}"
170
+ snippet = self._snippet(file_data, lineno)
171
+ return msg + "\n" + snippet
172
+
173
+ # generic offline
174
+ if "error" in prompt.lower():
175
+ return "[LocalAI] maybe u forgot a colon or indent lol"
176
+
177
+ return "[LocalAI] offline fallback active."
178
+
179
+ # ==========================================================
180
+ # ASK
181
+ # ==========================================================
182
+ def ask(self, text, file=None):
183
+ file_data = self._read_file(file)
184
+ prompt = self._build_prompt(text, file_data)
185
+
186
+ # Real Groq request would go here
187
+ try:
188
+ raise Exception("simulate offline")
189
+ except:
190
+ resp = self._local_ai(prompt, file_data)
191
+
192
+ self._save_memory(text, resp)
193
+ return CrystalAIResponse(resp)
194
+
195
+ # ==========================================================
196
+ # ASK (terminal)
197
+ # ==========================================================
198
+ def ask_t(self, text, file=None):
199
+ return self.ask(f"[TERMINAL] {text}", file)
200
+
201
+ # ==========================================================
202
+ # AUTO FIX CODE (v0.4)
203
+ # ==========================================================
204
+ def fix_code(self, file_path):
205
+ orig = self._read_file(file_path)
206
+
207
+ if not orig or orig.startswith("[CrystalAI]"):
208
+ return CrystalAIResponse(orig or "[CrystalAI] file missing")
209
+
210
+ try:
211
+ ast.parse(orig)
212
+ return CrystalAIResponse("[AI] No syntax errors found.")
213
+ except SyntaxError as se:
214
+ fixed, notes = self._simple_fix(orig, se)
215
+ diff = self._make_diff(orig, fixed)
216
+ pretty = "[AI] Auto-fix result:\n" + "\n".join(notes) + "\n\n" + diff
217
+ return CrystalAIResponse(pretty, {"diff": diff, "notes": notes})
218
+
219
+ # ==========================================================
220
+ # SIMPLE AUTO-FIX ENGINE
221
+ # ==========================================================
222
+ def _simple_fix(self, src, syntax_error):
223
+ notes = []
224
+ lines = src.splitlines()
225
+ msg = getattr(syntax_error, "msg", "")
226
+ lineno = syntax_error.lineno or 0
227
+
228
+ # missing colon
229
+ if "expected" in msg and ":" in msg:
230
+ if 1 <= lineno <= len(lines):
231
+ line = lines[lineno - 1].rstrip()
232
+ if not line.endswith(":"):
233
+ lines[lineno - 1] = line + ":"
234
+ notes.append("[fix] added missing ':'")
235
+ candidate = "\n".join(lines)
236
+ try:
237
+ ast.parse(candidate)
238
+ return candidate, notes
239
+ except:
240
+ pass
241
+
242
+ # fallback
243
+ notes.append("[info] auto-fix could not fix everything")
244
+ return src, notes
245
+
246
+ # ==========================================================
247
+ # DIFF UTIL
248
+ # ==========================================================
249
+ def _make_diff(self, old, new):
250
+ return "\n".join(
251
+ difflib.unified_diff(
252
+ old.splitlines(),
253
+ new.splitlines(),
254
+ fromfile="old",
255
+ tofile="new",
256
+ lineterm=""
257
+ )
258
+ )
259
+
260
+ # ==========================================================
261
+ # SNIPPET HELPER
262
+ # ==========================================================
263
+ def _snippet(self, src, lineno, ctx=2):
264
+ lines = src.splitlines()
265
+ start = max(0, lineno - ctx - 1)
266
+ end = min(len(lines), lineno + ctx)
267
+ out = []
268
+ for i in range(start, end):
269
+ mark = "->" if (i + 1) == lineno else " "
270
+ out.append(f"{mark} {i+1:4}: {lines[i]}")
271
+ return "\n".join(out)
272
+
273
+ # ==========================================================
274
+ # END OF ENGINE
275
+ # ==========================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crystalwindow
3
- Version: 3.8.9
3
+ Version: 4.1
4
4
  Summary: A Tkinter powered window + GUI toolkit made by Crystal (MEEEEEE)! Easier apps, smoother UI and all-in-one helpers!
5
5
  Home-page: https://pypi.org/project/crystalwindow/
6
6
  Author: CrystalBallyHereXD
@@ -1,5 +1,6 @@
1
1
  crystalwindow/FileHelper.py,sha256=aUnnRG7UwvzJt-idjWjmpwy3RM6nqLlC3-7Bae6Yb94,5471
2
- crystalwindow/__init__.py,sha256=Ugp3GLsfYK_OAA8J9WnGc95P4Uev5fAePmzNNXmpSKQ,2144
2
+ crystalwindow/__init__.py,sha256=a2kdMZ29QZ4kSQ3M8jLvCR6g3OUQxNhdaT3ycxoames,2264
3
+ crystalwindow/ai.py,sha256=eImP3B7Zr_bzbpbEmcXvcu_PYxmV2Z6ku78obXtJwew,10265
3
4
  crystalwindow/animation.py,sha256=zHjrdBXQeyNaLAuaGPldJueX05OZ5j31YR8NizmR0uQ,427
4
5
  crystalwindow/assets.py,sha256=2Cj0zdhMWo3mWjdr9KU5n-9_8iKj_fJ9uShMFA-27HU,5193
5
6
  crystalwindow/camera.py,sha256=tbn4X-jxMIszAUg3Iu-89gJN5nij0mjPMEzGotcLbJI,712
@@ -32,8 +33,8 @@ crystalwindow/gametests/guitesting.py,sha256=SrOssY5peCQEV6TQ1AiOWtjb9phVGdRzW-Q
32
33
  crystalwindow/gametests/sandbox.py,sha256=Oo2tU2N0y3BPVa6T5vs_h9N6islhQrjSrr_78XLut5I,1007
33
34
  crystalwindow/gametests/squaremove.py,sha256=poP2Zjl2oc2HVvIAgIK34H2jVj6otL4jEdvAOR6L9sI,572
34
35
  crystalwindow/gametests/windowtesting.py,sha256=_9X6wnV1-_X_PtNS-0zu-k209NtFIwAc4vpxLPp7V2o,97
35
- crystalwindow-3.8.9.dist-info/licenses/LICENSE,sha256=Gt5cJRchdNt0guxyQMHKsATN5PM5mjuDhdO6Gzs9qQc,1096
36
- crystalwindow-3.8.9.dist-info/METADATA,sha256=FJligl342pLMxbxJzIhvnpyHaserCLpWOsRr7KDzSdI,7340
37
- crystalwindow-3.8.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
- crystalwindow-3.8.9.dist-info/top_level.txt,sha256=PeQSld4b19XWT-zvbYkvE2Xg8sakIMbDzSzSdOSRN8o,14
39
- crystalwindow-3.8.9.dist-info/RECORD,,
36
+ crystalwindow-4.1.dist-info/licenses/LICENSE,sha256=Gt5cJRchdNt0guxyQMHKsATN5PM5mjuDhdO6Gzs9qQc,1096
37
+ crystalwindow-4.1.dist-info/METADATA,sha256=IUVMvg9Aekt1CYxWpzsp3412Yzb21HHFFnJTKG9-7os,7338
38
+ crystalwindow-4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
+ crystalwindow-4.1.dist-info/top_level.txt,sha256=PeQSld4b19XWT-zvbYkvE2Xg8sakIMbDzSzSdOSRN8o,14
40
+ crystalwindow-4.1.dist-info/RECORD,,