syntaxmatrix 2.5.6__py3-none-any.whl → 2.6.2__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.
Files changed (41) hide show
  1. syntaxmatrix/agentic/agents.py +1220 -169
  2. syntaxmatrix/agentic/agents_orchestrer.py +326 -0
  3. syntaxmatrix/agentic/code_tools_registry.py +27 -32
  4. syntaxmatrix/commentary.py +16 -16
  5. syntaxmatrix/core.py +185 -81
  6. syntaxmatrix/db.py +460 -4
  7. syntaxmatrix/{display.py → display_html.py} +2 -6
  8. syntaxmatrix/gpt_models_latest.py +1 -1
  9. syntaxmatrix/media/__init__.py +0 -0
  10. syntaxmatrix/media/media_pixabay.py +277 -0
  11. syntaxmatrix/models.py +1 -1
  12. syntaxmatrix/page_builder_defaults.py +183 -0
  13. syntaxmatrix/page_builder_generation.py +1122 -0
  14. syntaxmatrix/page_layout_contract.py +644 -0
  15. syntaxmatrix/page_patch_publish.py +1471 -0
  16. syntaxmatrix/preface.py +142 -21
  17. syntaxmatrix/profiles.py +28 -10
  18. syntaxmatrix/routes.py +1740 -453
  19. syntaxmatrix/selftest_page_templates.py +360 -0
  20. syntaxmatrix/settings/client_items.py +28 -0
  21. syntaxmatrix/settings/model_map.py +1022 -207
  22. syntaxmatrix/settings/prompts.py +328 -130
  23. syntaxmatrix/static/assets/hero-default.svg +22 -0
  24. syntaxmatrix/static/icons/bot-icon.png +0 -0
  25. syntaxmatrix/static/icons/favicon.png +0 -0
  26. syntaxmatrix/static/icons/logo.png +0 -0
  27. syntaxmatrix/static/icons/logo3.png +0 -0
  28. syntaxmatrix/templates/admin_branding.html +104 -0
  29. syntaxmatrix/templates/admin_features.html +63 -0
  30. syntaxmatrix/templates/admin_secretes.html +108 -0
  31. syntaxmatrix/templates/dashboard.html +296 -133
  32. syntaxmatrix/templates/dataset_resize.html +535 -0
  33. syntaxmatrix/templates/edit_page.html +2535 -0
  34. syntaxmatrix/utils.py +2431 -2383
  35. {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/METADATA +6 -2
  36. {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/RECORD +39 -24
  37. syntaxmatrix/generate_page.py +0 -644
  38. syntaxmatrix/static/icons/hero_bg.jpg +0 -0
  39. {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/WHEEL +0 -0
  40. {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/licenses/LICENSE.txt +0 -0
  41. {syntaxmatrix-2.5.6.dist-info → syntaxmatrix-2.6.2.dist-info}/top_level.txt +0 -0
@@ -1,644 +0,0 @@
1
- import re as _re
2
- import json as _json
3
- from urllib.parse import urlparse
4
- from . import profiles as _prof
5
-
6
- __all__ = ["generate_page_html"]
7
-
8
-
9
- _profile = {}
10
-
11
- _SMX_RUNTIME_GUARD = '''
12
- <script>
13
- (function(){
14
- if (window.ReactDOM && ReactDOM.createRoot) {
15
- const _cr = ReactDOM.createRoot;
16
- ReactDOM.createRoot = function(container){
17
- if (container === document.body || container === document.documentElement){
18
- console.warn("SMX: Redirected React root from <body>/<html> to #smx-react-root");
19
- let host = document.getElementById('smx-react-root');
20
- if (!host){ host = document.createElement('div'); host.id='smx-react-root'; document.body.appendChild(host); }
21
- return _cr.call(this, host);
22
- }
23
- return _cr.call(this, container);
24
- };
25
- }
26
- })();
27
- </script>
28
- '''.strip()
29
-
30
- _SMX_FADEIN_CSS = '''
31
- <style>
32
- .smx-fade{opacity:0;transform:translateY(14px);transition:opacity .6s ease,transform .6s ease;}
33
- .smx-in{opacity:1;transform:none;}
34
- @media (prefers-reduced-motion: reduce){.smx-fade{transition:none;transform:none;opacity:1;}}
35
- </style>
36
- '''.strip()
37
-
38
- _SMX_LAYOUT_CSS = """
39
- <style>
40
- #smx-root,
41
- [id^="smx-"]{
42
- margin-top: 0;
43
- margin-bottom: 40px;
44
- /* No side padding on desktop */
45
- padding-inline: 0;
46
- }
47
-
48
- /* Keep a bit of breathing room on small screens */
49
- @media (max-width: 768px) {
50
- #smx-root,
51
- [id^="smx-"]{
52
- padding-inline: 12px;
53
- }
54
- }
55
- </style>
56
- """.strip()
57
-
58
- def smx_strip_fences(html: str) -> str:
59
- s = (html or '').strip()
60
- m = _re.match(r"^```[a-zA-Z0-9_-]*\s*(.*?)\s*```$", s, _re.S)
61
- if m:
62
- return m.group(1)
63
- return s.replace("```html", "").replace("```HTML", "").replace("```", "")
64
-
65
- def smx_validate_html(html: str):
66
- errs = []
67
- if _re.search(r"ReactDOM\.createRoot\(\s*document\.(body|documentElement)\s*\)", html):
68
- errs.append("Do not mount React to <body> or <html>. Use #smx-react-root.")
69
- if _re.search(r"document\.write\s*\(", html, _re.I):
70
- errs.append("document.write is not allowed after initial load.")
71
- if _re.search(r"document\.body\.innerHTML\s*=", html):
72
- errs.append("Do not overwrite body.innerHTML.")
73
- if (("react-dom" in html) or ("ReactDOM" in html)) and ('id=\"smx-react-root\"' not in html):
74
- errs.append('Missing <div id=\"smx-react-root\"></div> for React mounting.')
75
- return errs
76
-
77
- def smx_autofix_html(html: str) -> str:
78
- inject = _SMX_FADEIN_CSS + "\n" + _SMX_LAYOUT_CSS + "\n" + _SMX_IMG_FALLBACK
79
- if ("react-dom" in html or "ReactDOM" in html) and ('id=\"smx-react-root\"' not in html):
80
- html = html.replace("</body>", '<div id=\"smx-react-root\"></div>\n</body>')
81
- html = _re.sub(
82
- r"ReactDOM\.createRoot\(\s*document\.(body|documentElement)\s*\)",
83
- 'ReactDOM.createRoot(document.getElementById(\"smx-react-root\"))',
84
- html
85
- )
86
- if "</body>" in html:
87
- return html.replace("</body>", f"{inject}\n{_SMX_RUNTIME_GUARD}\n</body>")
88
- return f"{inject}\n{_SMX_RUNTIME_GUARD}\n{html}"
89
-
90
- def _normalise_img_src(url: str) -> str:
91
- try:
92
- from urllib.parse import urlparse as _up
93
- u = _up(url)
94
- return (u.netloc.lower() + u.path).rstrip('/')
95
- except Exception:
96
- return url or ""
97
-
98
- def smx_dedupe_images(html: str) -> str:
99
- img_pat = _re.compile(r'<img\b[^>]*\bsrc\s*=\s*([\'"])(.*?)\1[^>]*>', _re.I)
100
- fb_pat = _re.compile(r'\bdata-fallbacks\s*=\s*([\'"])(.*?)\1', _re.I | _re.S)
101
-
102
- parts, last_end, seen_norm, unique_count = [], 0, set(), 0
103
-
104
- def _replace_src(tag: str, old: str, new: str) -> str:
105
- return _re.sub(r'(\bsrc\s*=\s*[\'"])' + _re.escape(old) + r'([\'"])', r"\1" + new + r"\2", tag, count=1)
106
-
107
- for m in img_pat.finditer(html):
108
- parts.append(html[last_end:m.start()])
109
- tag = m.group(0)
110
- src_val = m.group(2).strip()
111
- norm = _normalise_img_src(src_val)
112
-
113
- if norm in seen_norm:
114
- fb_m = fb_pat.search(tag)
115
- chosen = None
116
- if fb_m:
117
- try:
118
- cands = _json.loads(fb_m.group(2).replace("&quot;", '"').replace("&apos;", "'"))
119
- for cand in cands:
120
- if isinstance(cand, str) and _normalise_img_src(cand) not in seen_norm:
121
- chosen = cand; break
122
- except Exception:
123
- chosen = None
124
- if not chosen:
125
- unique_count += 1
126
- chosen = f"https://picsum.photos/seed/smx-{unique_count}/1200/700"
127
- tag = _replace_src(tag, src_val, chosen)
128
- seen_norm.add(_normalise_img_src(chosen))
129
- else:
130
- seen_norm.add(norm)
131
-
132
- parts.append(tag)
133
- last_end = m.end()
134
-
135
- parts.append(html[last_end:])
136
- return "".join(parts)
137
-
138
- def smx_ensure_min_images(html: str, min_images: int = 5) -> str:
139
- if not _re.search(r'<section[^>]+id=["\']smx-[^"\']+["\']', html, _re.I):
140
- return html
141
- if len(list(_re.finditer(r'<img\b', html, _re.I))) >= min_images:
142
- return html
143
-
144
- needed = min_images - len(list(_re.finditer(r'<img\b', html, _re.I)))
145
- figs = []
146
- for i in range(1, needed + 1):
147
- seed = f"smx-gallery-{i}"
148
- figs.append(f'''
149
- <figure class="smx-figure smx-fade">
150
- <img class="smx-img"
151
- src="https://picsum.photos/seed/{seed}/1200/700"
152
- data-fallbacks='["https://picsum.photos/seed/{seed}-alt/1200/700",
153
- "data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%271200%27 height=%27700%27%3E%3Crect fill=%27%23eef2f7%27 width=%27100%25%27 height=%27100%25%27/%3E%3Ctext x=%2750%25%27 y=%2750%25%27 dominant-baseline=%27middle%27 text-anchor=%27middle%27 fill=%27%23677%27 font-size=%2732%27%3EImage%20Placeholder%3C/text%3E%3C/svg%3E"]'
154
- alt="Gallery image {i}">
155
- </figure>
156
- '''.strip())
157
-
158
- gallery = f'''
159
- <section class="smx-gallery smx-container" aria-label="Image Gallery">
160
- <div class="smx-grid">
161
- {' '.join(figs)}
162
- </div>
163
- </section>
164
- '''.strip()
165
-
166
- return _re.sub(r'(</section>\s*)', gallery + r'\1', html, count=1, flags=_re.I)
167
-
168
- def smx_inject_card_icons(html: str) -> str:
169
- def _add_icon(match):
170
- card = match.group(0)
171
- if _re.search(r'<svg\b', card):
172
- return card
173
- svg = ('<svg aria-hidden="true" focusable="false" width="18" height="18" viewBox="0 0 24 24" '
174
- 'fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" '
175
- 'style="margin-right:8px;vertical-align:-2px;"><path d="M12 2l7 4v6c0 5-3.5 9-7 10-3.5-1-7-5-7-10V6l7-4z"/></svg>')
176
- return _re.sub(r'(<h3\b[^>]*>)', r'\1' + svg, card, count=1)
177
-
178
- pattern = _re.compile(r'<(?:div|article)\b[^>]*\bclass=["\'][^"\']*smx-card[^"\']*["\'][^>]*>.*?</(?:div|article)>', _re.I | _re.S)
179
- return pattern.sub(_add_icon, html)
180
-
181
- def smx_lightboxify(html: str) -> str:
182
- def _wrap(fig):
183
- tag = fig.group(0)
184
- if _re.search(r'</a>\s*</figure>\s*$', tag, _re.S):
185
- return tag
186
- src_m = _re.search(r'src=([\'"])(.*?)\1', tag, _re.I)
187
- alt_m = _re.search(r'alt=([\'"])(.*?)\1', tag, _re.I)
188
- if not src_m:
189
- return tag
190
- src = src_m.group(2)
191
- alt = alt_m.group(2) if alt_m else ""
192
- return _re.sub(r'(<img\b[^>]*>)', f'<a href="{src}" data-glightbox="title: {alt}">\\1</a>', tag, count=1)
193
- pattern = _re.compile(r'<figure\b[^>]*class=["\'][^"\']*smx-figure[^"\']*["\'][^>]*>.*?</figure>', _re.I | _re_S)
194
- return pattern.sub(_wrap, html)
195
-
196
- def smx_inject_extras(html: str, slug: str, extras: list) -> str:
197
- extras = extras or []
198
- tags = []
199
- if "aos" in extras:
200
- tags += [
201
- '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.css">',
202
- '<script src="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.js"></script>',
203
- ]
204
- if "embla" in extras:
205
- tags += ['<script src="https://cdn.jsdelivr.net/npm/embla-carousel@8.1.6/embla.umd.js"></script>']
206
- if "glightbox" in extras:
207
- tags += [
208
- '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css">',
209
- '<script src="https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js"></script>',
210
- ]
211
-
212
- init_template = '''
213
- <script>(function(){
214
- var root = document.getElementById("__SLUG__"); if(!root) return;
215
- if (window.AOS) {
216
- root.querySelectorAll('.smx-fade').forEach(function(el){ el.setAttribute('data-aos','fade-up'); });
217
- AOS.init({ disableMutationObserver: true, once: true, startEvent: 'smx:ready' });
218
- }
219
- if (window.GLightbox) { GLightbox({ selector: '#__SLUG__ [data-glightbox]' }); }
220
- if (window.EmblaCarousel) {
221
- root.querySelectorAll('.smx-carousel .smx-carousel__viewport').forEach(function(view){
222
- try { EmblaCarousel(view, { loop:true, align:'start' }); } catch(e){ console.warn('SMX Embla', e); }
223
- });
224
- }
225
- document.dispatchEvent(new Event('smx:ready'));
226
- })();
227
- </script>
228
- '''.strip().replace("__SLUG__", slug)
229
-
230
- payload = "\n".join(tags + [init_template])
231
- if "</body>" in html:
232
- return html.replace("</body>", payload + "\n</body>")
233
- return html + payload
234
-
235
- def smx_upgrade_insecure_urls(html: str) -> str:
236
- # Upgrade insecure resources in img/src, href, CSS url(), and data-fallbacks
237
- html = _re.sub(r'(\bsrc\s*=\s*[\'"])http://', r'\1https://', html, flags=_re.I)
238
- html = _re.sub(r'(\bhref\s*=\s*[\'"])http://', r'\1https://', html, flags=_re.I)
239
- html = _re.sub(r'url\((["\']?)http://', r'url(\1https://', html, flags=_re.I)
240
- html = _re.sub(r'(data-fallbacks\s*=\s*[\'"][^\'"]*)http://', r'\1https://', html, flags=_re.I)
241
- # Avoid brittle redirects
242
- html = html.replace('://source.unsplash.com', '://images.unsplash.com')
243
- return html
244
-
245
- _SMX_IMG_FALLBACK = '''
246
- <script>
247
- document.addEventListener('DOMContentLoaded', function(){
248
- document.querySelectorAll('img[data-fallbacks]').forEach(function(img){
249
- var list=[];
250
- try { list = JSON.parse(img.getAttribute('data-fallbacks')||'[]'); } catch(e){}
251
- var i=0;
252
- img.addEventListener('error', function onErr(){
253
- if (i < list.length) { img.src = list[i++]; }
254
- else { img.removeEventListener('error', onErr); }
255
- });
256
- });
257
- });
258
- </script>'''.strip()
259
-
260
- def smx_mirror_hero_background_to_img(html: str) -> str:
261
- # If a .smx-hero uses only CSS background, inject an <img class="smx-img"> so fallbacks can work
262
- def repl(m):
263
- tag = m.group(0)
264
- if _re.search(r'<img[^>]+class=["\'][^"\']*smx-img', tag, _re.I): # already has an image
265
- return tag
266
- urlm = _re.search(r'background-image\s*:\s*url\((["\']?)(.*?)\1\)', tag, _re.I)
267
- if not urlm: return tag
268
- src = urlm.group(2)
269
- return _re.sub(r'(>)', f'>\n <img class="smx-img" src="{src}" alt="" />', tag, count=1)
270
- pattern = _re.compile(r'<(section|div)\b[^>]*class=["\'][^"\']*smx-hero[^"\']*["\'][^>]*>.*?</\1>', _re.I | _re.S)
271
- return pattern.sub(repl, html)
272
-
273
- PROMPT_RULES = '''
274
- Non-negotiable Output Contract
275
- - Return only HTML that can be inserted inside an existing <main> element.
276
- - Do not include <!doctype>, <html>, <head>, or <body>.
277
- - Do not include triple backticks, code fences, YAML, or explanations of any kind.
278
- - No global resets; never style html, body, * selectors.
279
- - Scope all styles and scripts to a single top-level container: <section id="smx-{{slug}}" class="smx">...</section>
280
- where {{slug}} is a kebab-case version of the page title.
281
- - Include JavaScript, it must be vanilla JS and must only affect elements inside #smx-{{slug}}.
282
-
283
- Design & Structure
284
- - Your entire design MUST be fully responsive: desktop and mobile.
285
- - Build a hero with a contained banner: image must have rounded corners, aspect-ratio ≈16:9, and max-height ≤ 56vh.
286
- - Place hero text beside or above the image for good balance; ensure readable contrast.
287
- - Add 4-6 rich content blocks (feature grid, highlights, updates). Maintain heading order.
288
- - Use semantic HTML (sections, subsections articles, figures, h2-h4) and accessible labelling (aria-*, meaningful alt).
289
- - Avoid heavy frameworks. If utility classes appear, also provide a small scoped CSS fallback.
290
-
291
- Styling Rules (scoped)
292
- - Add a <style> block inside the returned HTML. Prefix every selector with #smx-{{slug}}.
293
- - Provide CSS variables on #smx-{{slug}}: --smx-primary, --smx-secondary, --smx-bg, --smx-fg.
294
- - Include a tiny fade-in reveal: elements with .smx-fade start hidden and animate into view.
295
- - Decide and animate which ever other area/feature you want.
296
-
297
- Images & Asset Reliability
298
- - For each important image, provide multiple candidates via data-fallbacks AND a safe default src.
299
- - Never leave src empty. Avoid dead links.
300
- - If an image fails to load, the inline script must try the next URL from data-fallbacks.
301
- - Provide descriptive alt text; decorative images may use alt="" and role="presentation".
302
- - Do not reuse the same image URL (ignoring querystrings) twice on the same page.
303
- - Aim for at least five total images across hero, feature cards, highlight, or an extra gallery.
304
- '''.strip()
305
-
306
- PROMPT_REQUIRED_SCRIPT = '''
307
- Required Fallback Script (place once, scoped to #smx-{{slug}}; no fences)
308
- <script>(function(){
309
- const root = document.querySelector('#smx-{{slug}}'); if(!root) return;
310
- root.querySelectorAll('img[data-fallbacks]').forEach(img=>{
311
- const list = (()=>{try{return JSON.parse(img.getAttribute('data-fallbacks'));}catch{return [];}})();
312
- let i=0; img.addEventListener('error', function onErr(){ if(i<list.length){img.src=list[i++];} else {img.removeEventListener('error', onErr);} });
313
- });
314
- const IO = 'IntersectionObserver' in window ? new IntersectionObserver(es=>{
315
- es.forEach(e=>{ if(e.isIntersecting){ e.target.classList.add('smx-in'); IO.unobserve(e.target); } });
316
- },{rootMargin:'-5% 0px'}) : null;
317
- if(IO){ root.querySelectorAll('.smx-fade').forEach(el=>IO.observe(el)); }
318
- else { root.querySelectorAll('.smx-fade').forEach(el=>el.classList.add('smx-in')); }
319
- })();</script>
320
- '''.strip()
321
-
322
- PROMPT_SCOPED_CSS = '''
323
- Scoped CSS (place in same HTML; no fences)
324
- <style>
325
- #smx-{{slug}} {
326
- --smx-primary: __PRIMARY__;
327
- --smx-secondary: __SECONDARY__;
328
- --smx-bg:#ffffff; --smx-fg:#0f172a; color:var(--smx-fg); background:var(--smx-bg);
329
- }
330
- #smx-{{slug}} .smx-hero { border-radius:1.25rem; padding:clamp(2rem,4vw,4rem); background:linear-gradient(135deg,var(--smx-primary),var(--smx-secondary)); color:#fff; }
331
- #smx-{{slug}} .smx-grid { display:grid; gap:clamp(1rem,2vw,2rem); grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); }
332
- #smx-{{slug}} .smx-card { background:#fff; border:1px solid #e5e7eb; border-radius:1rem; padding:1.25rem; box-shadow:0 6px 20px rgba(2,6,23,0.06); }
333
- #smx-{{slug}} .smx-figure { margin:0; }
334
- #smx-{{slug}} .smx-img { width:100%; height:auto; display:block; border-radius:.75rem; object-fit:cover; }
335
- #smx-{{slug}} .smx-cta { display:inline-block; padding:.8rem 1.2rem; border-radius:.8rem; background:#fff; color:var(--smx-primary); font-weight:600; text-decoration:none; }
336
- #smx-{{slug}} .smx-fade { opacity:0; transform: translateY(14px); transition: opacity .6s ease, transform .6s ease; }
337
- #smx-{{slug}} .smx-in { opacity:1; transform:none; }
338
- @media (prefers-reduced-motion: reduce){ #smx-{{slug}} .smx-fade { transition:none; transform:none; opacity:1; } }
339
- </style>
340
- '''.strip()
341
-
342
- def _build_prompt(page_title: str, website_description: str, brand_primary: str = "", brand_secondary: str = "") -> str:
343
- primary = (brand_primary or "#2563eb").strip()
344
- secondary = (brand_secondary or "#14b8a6").strip()
345
-
346
- header = (
347
- "You generate a modern, responsive, accessible content section to be embedded into an existing website.\n"
348
- "The host site already provides navbar, footer, and global CSS. Your output must be RAW HTML only\n"
349
- "(to be placed inside <main>), with its own SCOPED styles and tiny vanilla JS. Use UK English.\n\n"
350
- f"Inputs\n- Page Title: {page_title}\n- Website Mission/Vision/Goals: {website_description}\n"
351
- f"- Optional brand colours: primary '{primary}', secondary '{secondary}'\n\n"
352
- )
353
-
354
- content_requirements = '''
355
- Content to Produce inside #smx-{{slug}}
356
- 1) Hero with balanced layout (text + contained image), one .smx-cta, and a hero image using the fallback pattern.
357
- 2) Feature grid (3-4 cards) describing capabilities or topics.
358
- 3) Highlight/Showcase section with one image and supporting text.
359
- 4) Updates/testimonials.
360
- Headings must be in logical order (no skipping levels).
361
-
362
- Final step
363
- - Validate your HTML mentally; ensure no triple backticks or extra prose.
364
- - Output only the HTML for <section id="smx-{{slug}}" class="smx">…</section>.
365
- '''.strip()
366
-
367
- prompt = header + PROMPT_RULES + "\n\n" + PROMPT_REQUIRED_SCRIPT + "\n\n" + PROMPT_SCOPED_CSS + "\n\n" + content_requirements
368
- prompt = prompt.replace("__PRIMARY__", primary).replace("__SECONDARY__", secondary)
369
- return prompt
370
-
371
- def google_generate_code(client, model, instructions):
372
- response = client.models.generate_content(
373
- model=model,
374
- contents=instructions
375
- )
376
- return response.text.strip()
377
-
378
- def gpt_models_latest_generate_code(client, model, instructions):
379
- response = client.responses.create(
380
- model=model,
381
- input=instructions,
382
- reasoning={ "effort": "low" },
383
- text={ "verbosity": "medium", },
384
- )
385
- return response.output_text.strip()
386
-
387
- def openai_sdk_generate_code(client, model, instructions):
388
- try:
389
- response = client.chat.completions.create(
390
- model=model,
391
- contents=[
392
- {"role": "system", "content": "You are a senior web developer tasked with creating a single-file responsive webpage."},
393
- {"role": "user", "content": instructions}
394
- ],
395
- max_tokens=4096,
396
- temperature=0.7
397
- )
398
- return response.choices[0].message.content.strip()
399
- except Exception as e:
400
- return f"Error generating webpage: {str(e)}"
401
-
402
-
403
-
404
- def generate_page_html1(page_title:str,website_description:str,brand_primary:str="",brand_secondary:str="",extras=None)->str:
405
- prof = _profile
406
- if not prof:
407
- return "No profile to generate page"
408
-
409
- _client = _prof.get_client(_profile)
410
- _model = _profile["model"]
411
-
412
- try:
413
- prompt = _build_prompt(page_title, website_description, brand_primary, brand_secondary)
414
- resp = _client.chat.completions.create(
415
- model=_model,
416
- messages=[
417
- {"role": "system", "content": "You are a senior front-end engineer creating an embedded content section. Follow the instructions exactly."},
418
- {"role": "user", "content": prompt},
419
- ],
420
- temperature=0.8,
421
- max_tokens=4096,
422
- )
423
- html = smx_strip_fences(resp.choices[0].message.content or "")
424
-
425
- _ = smx_validate_html(html)
426
- html = smx_autofix_html(html)
427
- html = smx_upgrade_insecure_urls(html) # ← new
428
- html = smx_mirror_hero_background_to_img(html) # ← new
429
- html = smx_dedupe_images(html)
430
- html = smx_ensure_min_images(html, min_images=5)
431
-
432
- try:
433
- html = smx_inject_card_icons(html)
434
- except Exception:
435
- pass
436
-
437
- if extras:
438
- m = _re.search(r'<section[^>]+id=["\'](smx-[^"\']+)["\']', html, _re.I)
439
- slug = m.group(1) if m else "smx-root"
440
- if "glightbox" in extras:
441
- html = smx_lightboxify(html)
442
- html = smx_inject_extras(html, slug, extras)
443
-
444
- return html
445
- except Exception as e:
446
- return f"Error generating webpage: {e}"
447
-
448
-
449
- def generate_page_html2(page_title: str, website_description: str) -> str:
450
-
451
- prof = _profile
452
- if not prof:
453
- return "No profile to generate page"
454
-
455
- client = _prof.get_client(_profile)
456
- model = _profile["model"]
457
-
458
- prompt = f"""
459
- Generate the webpage following the guidelines given, ensuring images are sourced also.
460
- The output must be a single HTML file containing all HTML, CSS (using Tailwind CSS via CDN), and JavaScript (using React via CDN) with no external dependencies beyond CDNs. The page must reflect the essence of the page title in relation to the website's objectives, include a hero section and subsections, and use free, high-quality images from Unsplash or Pexels (licensed for commercial use). Ensure animations (e.g., fade-ins, hover effects) are included, and the design is responsive for all devices. Do not include headers or footers, as they are provisioned by the website. Avoid variable names that might clash with existing ones (e.g., use unique names like 'heroSection', 'subSectionContent'). Follow these guidelines:\n
461
-
462
- **Input**
463
- - Page Title: {page_title}\n
464
- - Website Objectives: {website_description}\n\n
465
-
466
- **Instructions**:
467
- 1. **Output Format**: The entire output must be a single HTML file. Include all CSS and JavaScript within the HTML file using `<style>` and `<script>` tags.
468
- 2. **Modern Design**: Use a modern, vibrant, and professional design. The layout should be clean and visually appealing.
469
- 3. **Responsive**: The page must be fully responsive and work on desktop, tablet, and mobile devices. Use media queries to ensure proper display on all screen sizes. [15]
470
- 4. **Dynamic and Animated**: Incorporate subtle animations and transitions (e.g., fade-ins on scroll, hover effects) to make the page dynamic and engaging.
471
- 5. **Content Structure**:
472
- * **Hero Section**: A prominent introductory section with a compelling headline, a brief description, and a call-to-action button.
473
- * **Main Content**: Subsections that elaborate on the page's topic. For an "About" page, this could include a mission, vision, and team section. For a "Services" page, it should list the services with descriptions. Improvise and create placeholder content if not enough information is provided in the objectives.
474
- * **No Header/Footer**: Do not include `<header>` or `<footer>` tags as these are assumed to be provided by the website where this page will be embedded.
475
- 6. **Images**:
476
- * Source high-quality, royalty-free images from Unsplash or Pexels.
477
- * Use appropriate placeholder image URLs directly in the `src` attributes of the `<img>` tags. For example: `https://images.unsplash.com/photo-12345?q=80&w=1920...`
478
- * Ensure images are relevant to the content and theme of the website.
479
- 7. **Code Style**:
480
- * Use HTML5 semantic tags (`<section>`, `<article>`, etc.). [14]
481
- * For styling, you can use inline CSS or a `<style>` block. You can also use a CSS framework like Tailwind CSS via a CDN link.
482
- * For JavaScript, ensure variable and function names are unique to avoid conflicts with existing scripts on the website (e.g., use a unique prefix like `myUniquePage_`).
483
- * Include CDN links for any external libraries if necessary (e.g., React, though vanilla JS is preferred for simplicity). [1]
484
- 8. **Review and Refine**:
485
- Before generating the final code, mentally review the requirements to ensure the output will be high-quality, functional, and visually appealing.
486
- 9 Your code must begin with the <html> and closes with </tml> tag.
487
- Do not include and preamble, no comments or profix. No trailing backticks. All content must be inside the html tags.
488
- You must return just the html code.
489
-
490
- **Output Format**
491
- Return only the complete HTML content as a string, wrapped in triple backticks (```), with no additional explanation or comments outside the HTML code. Ensure the HTML includes all necessary scripts, styles, and JSX components.
492
-
493
- ```
494
- <!-- Complete HTML content here -->
495
- ```
496
- """
497
- try:
498
- # API call to OpenAI's chat completions endpoint [4, 9]
499
- chat_completion = client.chat.completions.create(
500
- model=model,
501
- messages=[
502
- {"role": "system", "content": "You are a senior web developer tasked with creating a single-file responsive webpage."},
503
- {"role": "user", "content": prompt}
504
- ]
505
- )
506
-
507
- if chat_completion.choices:
508
- html_content = chat_completion.choices[0].message.content.strip()
509
-
510
- return html_content
511
- else:
512
- return "Error: Unable to generate a response from the API."
513
-
514
- except Exception as e:
515
- return f"An error occurred: {e}"
516
-
517
-
518
- def generate_page_html(page_title, website_description):
519
- global _profile
520
- if not _profile:
521
- profile = _prof.get_profile("coding") or _prof.get_profile("admin")
522
- if not profile:
523
- return
524
- _profile = profile
525
-
526
- _ai_code_id = f" You are a senior web developer tasked with creating a single-file responsive webpage. "
527
- _instructions = f"""
528
- Generate the webpage following the guidelines given, ensuring images are sourced also.
529
- The output must be a single HTML file containing all HTML, CSS (using Tailwind CSS via CDN), and JavaScript (using React via CDN) with no external dependencies beyond CDNs. The page must reflect the essence of the page title in relation to the website's objectives, include a hero section and subsections, and use free, high-quality images from Unsplash or Pexels (licensed for commercial use). Ensure animations (e.g., fade-ins, hover effects) are included, and the design is responsive for all devices. Do not include headers or footers, as they are provisioned by the website. Avoid variable names that might clash with existing ones (e.g., use unique names like 'heroSection', 'subSectionContent'). Follow these guidelines:\n
530
-
531
- <input>
532
- - Page title: {page_title}
533
- - Website description: {website_description}
534
- </input>
535
-
536
- <requirements>
537
- - Never mount React to document.body or document.documentElement; mount only to <div id="smx-react-root"></div>.
538
- - Include <div id="smx-react-root"></div> if React/ReactDOM are used.
539
- - Prefer vanilla JS for simple effects (scroll/hover). If you use React, mount via createRoot(document.getElementById('smx-react-root')).
540
- - Do not call document.write() or overwrite document.body.innerHTML.
541
- - Start elements with class .fade-in hidden (opacity:0, translateY(20px)) and reveal with transitions.
542
- - Create a single HTML file with embedded React
543
- - Use JSX via:
544
- CDN: (https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js and https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js)
545
- Babel: (https://cdn.jsdelivr.net/npm/@babel/standalone@7.20.6/babel.min.js)
546
- Tailwind CSS: (https://cdn.tailwindcss.com).
547
- - Structure the page with a hero section (prominent headline, description, call-to-action) and subsections relevant to the page title and objectives.
548
- - Use modern JavaScript syntax and JSX for reusable components (e.g., HeroComponent, SectionComponent, SubSectionComponent).
549
- - Ensure the design is vibrant, professional, and consistent with the website's theme (infer theme from objectives, e.g., calming colors for health-related sites).
550
- - Include animations (e.g., fade-ins using Tailwind or JavaScript) without overwhelming the page.
551
- - Source images from Unsplash and/or Pexels, and place them appropriately (e.g., hero section background, subsection images).
552
- - Ensure responsiveness using Tailwind's responsive classes (e.g., sm:, md:, lg:).
553
- - Avoid <form> onSubmit due to sandbox restrictions; use buttons with click handlers instead.
554
- - Use className instead of class in JSX.
555
- - Verify the code is complete, functional, and ready to be saved as an HTML file and viewed in a browser.
556
- </requirements>
557
-
558
- <task>
559
- Now, for this page title: {page_title}\n,
560
- generate html content that aligns with the given website description:\n {website_description}.\n\n
561
- </task>
562
-
563
- <output>
564
- Return only the complete HTML content as a string, with no additional explanation or comments outside the HTML code. Ensure the HTML includes all necessary scripts, styles, and JSX components.
565
- </task>
566
- """
567
-
568
- from syntaxmatrix.settings import model_map as mm, prompts
569
-
570
- _client = _prof.get_client(_profile)
571
- _provider = _profile["provider"]
572
- _model = _profile["model"]
573
-
574
- def google_generate_code():
575
- response = _client.models.generate_content(
576
- model=_model,
577
- contents=f"{_ai_code_id}\n\n{_instructions}\n\n"
578
- )
579
- return response.text
580
-
581
- def gpt_models_latest_generate_code(reasoning_effort = "medium", verbosity = "medium"):
582
- from syntaxmatrix.gpt_models_latest import extract_output_text as _out, set_args
583
- try:
584
- args = set_args(
585
- model=_model,
586
- instructions=_ai_code_id,
587
- input=_instructions,
588
- reasoning_effort=reasoning_effort,
589
- verbosity=verbosity,
590
- )
591
-
592
- resp = _client.responses.create(**args)
593
- code = _out(resp)
594
- return code
595
- except Exception as e:
596
- return f"Error!"
597
-
598
- def anthropic_generate_code():
599
- try:
600
- response = _client.messages.create(
601
- model=_model,
602
- max_tokens=1024,
603
- system=_ai_code_id,
604
- messages=[{"role": "user", "content":_instructions}],
605
- stream=False,
606
- )
607
- return response.content[0].text.strip()
608
-
609
- except Exception as e:
610
- return f"Error: {str(e)}"
611
-
612
- def openai_sdk_generate_code():
613
- try:
614
- response = _client.chat.completions.create(
615
- model=_model,
616
- messages=[
617
- {"role": "system", "content": _ai_code_id},
618
- {"role": "user", "content": _instructions},
619
- ],
620
- temperature=0.3,
621
- max_tokens=2048,
622
- )
623
- return response.choices[0].message.content
624
- except Exception as e:
625
- return f"Error!"
626
-
627
- html = ""
628
-
629
- if _provider == "google":
630
- html = google_generate_code()
631
- elif _model in mm.GPT_MODELS_LATEST:
632
- html = gpt_models_latest_generate_code()
633
- elif _provider == "anthropic":
634
- html = anthropic_generate_code()
635
- else:
636
- html = openai_sdk_generate_code()
637
-
638
- html = smx_autofix_html(html)
639
- html = smx_upgrade_insecure_urls(html)
640
- html = smx_mirror_hero_background_to_img(html)
641
- # html = smx_dedupe_images(html)
642
- html = smx_ensure_min_images(html, min_images=8)
643
-
644
- return html
Binary file