dryymatt commited on
Commit
b6a8514
Β·
verified Β·
1 Parent(s): b273462

Upload static/wizard.js

Browse files
Files changed (1) hide show
  1. static/wizard.js +112 -37
static/wizard.js CHANGED
@@ -1,29 +1,77 @@
1
  /**
2
  * ╔══════════════════════════════════════════════════════╗
3
- * β•‘ WIZARD-VIBE STUDIO β€” Client Runtime β•‘
4
- * β•‘ SSE Streaming Β· Sandbox iframe Β· Publish Gate β•‘
5
  * β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
6
  */
7
 
8
- const S = { sessionId: null, sandboxStatus: 'idle', fullCode: '', errorsFound: 0, errorsFixed: 0, publishData: null };
9
  const D = {
10
- prompt: document.getElementById('prompt-input'), vibeBtn: document.getElementById('vibe-btn'),
11
- codeContent: document.getElementById('code-content'), previewFrame: document.getElementById('preview-frame'),
12
- previewOverlay: document.getElementById('preview-overlay'), statusDot: document.getElementById('status-dot'),
13
- statusText: document.getElementById('status-text'), statusPhase: document.getElementById('status-phase'),
14
- sandboxBadge: document.getElementById('sandbox-badge'), publishBtn: document.getElementById('publish-btn'),
15
- publishGate: document.getElementById('publish-gate'), gateIcon: document.querySelector('.gate-icon'),
16
- gateText: document.querySelector('.gate-text'), publishResult: document.getElementById('publish-result'),
17
- publishUrl: document.getElementById('publish-url'), githubLink: document.getElementById('github-link'),
18
- spacesLink: document.getElementById('spaces-link'), agentLink: document.getElementById('agent-link'),
19
- modelBadges: document.getElementById('model-badges'), wizardHat: document.getElementById('wizard-hat'),
20
- statusRing: document.getElementById('status-ring'), copyBtn: document.getElementById('copy-btn'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  };
22
 
23
- // ═══ SSE STREAMING β€” ASYNC ITERATOR CLIENT ═══
 
 
 
 
 
 
 
 
 
 
 
 
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  async function* streamFromServer(prompt) {
26
- const resp = await fetch('/api/stream', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({prompt}) });
 
 
 
 
27
  if (!resp.ok) throw new Error(`Stream error: ${resp.status}`);
28
  const reader = resp.body.getReader(), dec = new TextDecoder();
29
  let buf = '';
@@ -42,8 +90,7 @@ async function* streamFromServer(prompt) {
42
  }
43
  }
44
 
45
- // ═══ STATE MACHINE β€” WIZARD HAT MORPHING ═══
46
-
47
  function setSandboxStatus(st) {
48
  S.sandboxStatus = st;
49
  D.wizardHat.classList.remove('stable','error','published','building');
@@ -73,8 +120,7 @@ function setSandboxStatus(st) {
73
  D.statusText.textContent = st.charAt(0).toUpperCase()+st.slice(1);
74
  }
75
 
76
- // ═══ IFRAME HOT-RELOAD ═══
77
-
78
  function updateIframe(code) {
79
  const blob = new Blob([code], {type:'text/html'});
80
  const url = URL.createObjectURL(blob);
@@ -86,7 +132,8 @@ function updateIframe(code) {
86
 
87
  function appendCode(chunk) {
88
  if (D.codeContent.querySelector('.placeholder')) D.codeContent.innerHTML = '';
89
- const span = document.createElement('span'); span.className='code-chunk-new'; span.textContent=chunk;
 
90
  D.codeContent.appendChild(span);
91
  D.codeContent.parentElement.scrollTop = D.codeContent.parentElement.scrollHeight;
92
  }
@@ -103,17 +150,32 @@ function updateModelBadges(plan) {
103
  });
104
  }
105
 
106
- // ═══ MAIN GENERATION ═══
107
-
108
  async function generate() {
109
  const prompt = D.prompt.value.trim(); if (!prompt) return;
110
- S.fullCode = ''; S.errorsFound = 0; S.errorsFixed = 0; S.publishData = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  D.codeContent.innerHTML = '<span class="placeholder">Streaming...</span>';
112
  D.previewOverlay.classList.remove('hidden');
 
113
  D.publishResult.style.display = 'none';
114
  D.publishBtn.classList.remove('gold'); D.publishBtn.disabled = true;
115
  D.modelBadges.innerHTML = '';
116
- D.vibeBtn.disabled = true; D.vibeBtn.querySelector('.btn-text').textContent = 'Generating...';
 
117
  setSandboxStatus('building');
118
  D.statusPhase.textContent = 'Connecting...';
119
 
@@ -124,27 +186,38 @@ async function generate() {
124
  if (ev.chunk) { appendCode(ev.chunk); S.fullCode += ev.chunk; }
125
  if (ev.partial) updateIframe(ev.partial);
126
  if (ev.heal) { S.errorsFound = ev.errors_found; S.errorsFixed = ev.errors_fixed; }
127
- if (ev.sandbox) setSandboxStatus(ev.status);
 
 
 
 
 
 
128
  }
129
  } catch (err) {
130
  console.error('Stream error:', err);
131
  setSandboxStatus('error');
132
  D.statusText.textContent = `Error: ${err.message}`;
133
  } finally {
134
- D.vibeBtn.disabled = false; D.vibeBtn.querySelector('.btn-text').textContent = 'Generate';
 
135
  D.statusText.textContent = 'Done';
 
 
 
136
  }
137
  }
138
 
139
- // ═══ PUBLISH ═══
140
-
141
  async function publish() {
142
- const repoName = prompt('Repo name:', `wizard-vibe-${Date.now().toString(36)}`);
143
  if (!repoName) return;
144
- D.publishBtn.disabled = true; D.publishBtn.querySelector('.btn-text').textContent = 'Publishing...';
 
145
  try {
146
  const resp = await fetch('/api/publish', {
147
- method:'POST', headers:{'Content-Type':'application/json'},
 
148
  body:JSON.stringify({repo_name:repoName, description:D.prompt.value.trim().slice(0,200)}),
149
  });
150
  const r = await resp.json();
@@ -160,18 +233,20 @@ async function publish() {
160
  } catch (err) {
161
  D.statusText.textContent = `Publish error: ${err.message}`;
162
  } finally {
163
- D.publishBtn.disabled = false; D.publishBtn.querySelector('.btn-text').textContent = 'Publish to A2A Network';
 
164
  }
165
  }
166
 
167
- // ═══ EVENT BINDINGS ═══
168
-
169
  D.vibeBtn.addEventListener('click', generate);
170
  D.publishBtn.addEventListener('click', publish);
171
- D.prompt.addEventListener('keydown', e => { if ((e.ctrlKey||e.metaKey) && e.key==='Enter') { e.preventDefault(); generate(); } });
 
 
172
  D.copyBtn.addEventListener('click', async () => {
173
  if (!S.fullCode) return;
174
  try { await navigator.clipboard.writeText(S.fullCode); } catch {}
175
  });
176
 
177
- console.log('πŸ§™β€β™‚οΈ Wizard-Vibe Studio β€” Liquid Glass Client ready');
 
1
  /**
2
  * ╔══════════════════════════════════════════════════════╗
3
+ * β•‘ OMNI-VIBE STUDIO β€” Client Runtime β•‘
4
+ * β•‘ Tabbed Forge/Preview Β· SSE Stream Β· Ghost Deploy β•‘
5
  * β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
6
  */
7
 
8
+ const S = { sessionId: null, sandboxStatus: 'idle', fullCode: '', errorsFound: 0, errorsFixed: 0, publishData: null, previewUnlocked: false };
9
  const D = {
10
+ prompt: document.getElementById('prompt-input'),
11
+ vibeBtn: document.getElementById('vibe-btn'),
12
+ codeContent: document.getElementById('code-content'),
13
+ previewFrame: document.getElementById('preview-frame'),
14
+ previewOverlay: document.getElementById('preview-overlay'),
15
+ statusDot: document.getElementById('status-dot'),
16
+ statusText: document.getElementById('status-text'),
17
+ statusPhase: document.getElementById('status-phase'),
18
+ sandboxBadge: document.getElementById('sandbox-badge'),
19
+ publishBtn: document.getElementById('publish-btn'),
20
+ publishGate: document.getElementById('publish-gate'),
21
+ gateIcon: document.querySelector('.gate-icon'),
22
+ gateText: document.querySelector('.gate-text'),
23
+ publishResult: document.getElementById('publish-result'),
24
+ publishUrl: document.getElementById('publish-url'),
25
+ githubLink: document.getElementById('github-link'),
26
+ spacesLink: document.getElementById('spaces-link'),
27
+ agentLink: document.getElementById('agent-link'),
28
+ modelBadges: document.getElementById('model-badges'),
29
+ wizardHat: document.getElementById('wizard-hat'),
30
+ statusRing: document.getElementById('status-ring'),
31
+ copyBtn: document.getElementById('copy-btn'),
32
+ previewTabBtn: document.getElementById('preview-tab-btn'),
33
+ panelForge: document.getElementById('panel-forge'),
34
+ panelPreview: document.getElementById('panel-preview'),
35
  };
36
 
37
+ /* ═══ TAB SWITCHING ═══ */
38
+ document.querySelectorAll('.tab-btn').forEach(btn => {
39
+ btn.addEventListener('click', () => {
40
+ if (btn.disabled) return;
41
+ // Deactivate all
42
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
43
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
44
+ // Activate clicked
45
+ btn.classList.add('active');
46
+ const panelId = 'panel-' + btn.dataset.tab;
47
+ document.getElementById(panelId).classList.add('active');
48
+ });
49
+ });
50
 
51
+ function unlockPreview() {
52
+ if (S.previewUnlocked) return;
53
+ S.previewUnlocked = true;
54
+ D.previewTabBtn.disabled = false;
55
+ D.previewTabBtn.classList.remove('locked');
56
+ D.previewTabBtn.classList.add('unlocked');
57
+ D.previewTabBtn.querySelector('.tab-icon').textContent = 'πŸ‘οΈ';
58
+ }
59
+
60
+ function switchToPreview() {
61
+ if (!S.previewUnlocked) return;
62
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
63
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
64
+ D.previewTabBtn.classList.add('active');
65
+ D.panelPreview.classList.add('active');
66
+ }
67
+
68
+ /* ═══ SSE STREAMING ═══ */
69
  async function* streamFromServer(prompt) {
70
+ const resp = await fetch('/api/stream', {
71
+ method:'POST',
72
+ headers:{'Content-Type':'application/json'},
73
+ body:JSON.stringify({prompt})
74
+ });
75
  if (!resp.ok) throw new Error(`Stream error: ${resp.status}`);
76
  const reader = resp.body.getReader(), dec = new TextDecoder();
77
  let buf = '';
 
90
  }
91
  }
92
 
93
+ /* ═══ STATE MACHINE ═══ */
 
94
  function setSandboxStatus(st) {
95
  S.sandboxStatus = st;
96
  D.wizardHat.classList.remove('stable','error','published','building');
 
120
  D.statusText.textContent = st.charAt(0).toUpperCase()+st.slice(1);
121
  }
122
 
123
+ /* ═══ IFRAME HOT-RELOAD ═══ */
 
124
  function updateIframe(code) {
125
  const blob = new Blob([code], {type:'text/html'});
126
  const url = URL.createObjectURL(blob);
 
132
 
133
  function appendCode(chunk) {
134
  if (D.codeContent.querySelector('.placeholder')) D.codeContent.innerHTML = '';
135
+ const span = document.createElement('span');
136
+ span.className='code-chunk-new'; span.textContent=chunk;
137
  D.codeContent.appendChild(span);
138
  D.codeContent.parentElement.scrollTop = D.codeContent.parentElement.scrollHeight;
139
  }
 
150
  });
151
  }
152
 
153
+ /* ═══ MAIN GENERATION ═══ */
 
154
  async function generate() {
155
  const prompt = D.prompt.value.trim(); if (!prompt) return;
156
+ S.fullCode = ''; S.errorsFound = 0; S.errorsFixed = 0;
157
+ S.publishData = null; S.previewUnlocked = false;
158
+
159
+ // Reset to Forge tab
160
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
161
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
162
+ document.querySelector('.tab-btn[data-tab="forge"]').classList.add('active');
163
+ D.panelForge.classList.add('active');
164
+
165
+ // Lock Preview tab
166
+ D.previewTabBtn.disabled = true;
167
+ D.previewTabBtn.classList.add('locked');
168
+ D.previewTabBtn.classList.remove('unlocked');
169
+ D.previewTabBtn.querySelector('.tab-icon').textContent = 'πŸ”’';
170
+
171
  D.codeContent.innerHTML = '<span class="placeholder">Streaming...</span>';
172
  D.previewOverlay.classList.remove('hidden');
173
+ D.previewFrame.src = 'about:blank';
174
  D.publishResult.style.display = 'none';
175
  D.publishBtn.classList.remove('gold'); D.publishBtn.disabled = true;
176
  D.modelBadges.innerHTML = '';
177
+ D.vibeBtn.disabled = true;
178
+ D.vibeBtn.querySelector('.btn-text').textContent = 'Generating...';
179
  setSandboxStatus('building');
180
  D.statusPhase.textContent = 'Connecting...';
181
 
 
186
  if (ev.chunk) { appendCode(ev.chunk); S.fullCode += ev.chunk; }
187
  if (ev.partial) updateIframe(ev.partial);
188
  if (ev.heal) { S.errorsFound = ev.errors_found; S.errorsFixed = ev.errors_fixed; }
189
+ if (ev.sandbox) {
190
+ setSandboxStatus(ev.status);
191
+ // Unlock Preview tab when stable or published
192
+ if (ev.status === 'stable' || ev.status === 'published') {
193
+ unlockPreview();
194
+ }
195
+ }
196
  }
197
  } catch (err) {
198
  console.error('Stream error:', err);
199
  setSandboxStatus('error');
200
  D.statusText.textContent = `Error: ${err.message}`;
201
  } finally {
202
+ D.vibeBtn.disabled = false;
203
+ D.vibeBtn.querySelector('.btn-text').textContent = 'Generate';
204
  D.statusText.textContent = 'Done';
205
+ // Always unlock preview after generation, even on errors
206
+ // (so user can see partial/error output)
207
+ if (S.fullCode) unlockPreview();
208
  }
209
  }
210
 
211
+ /* ═══ PUBLISH ═══ */
 
212
  async function publish() {
213
+ const repoName = prompt('Repo name:', `omni-vibe-${Date.now().toString(36)}`);
214
  if (!repoName) return;
215
+ D.publishBtn.disabled = true;
216
+ D.publishBtn.querySelector('.btn-text').textContent = 'Publishing...';
217
  try {
218
  const resp = await fetch('/api/publish', {
219
+ method:'POST',
220
+ headers:{'Content-Type':'application/json'},
221
  body:JSON.stringify({repo_name:repoName, description:D.prompt.value.trim().slice(0,200)}),
222
  });
223
  const r = await resp.json();
 
233
  } catch (err) {
234
  D.statusText.textContent = `Publish error: ${err.message}`;
235
  } finally {
236
+ D.publishBtn.disabled = false;
237
+ D.publishBtn.querySelector('.btn-text').textContent = 'Publish to A2A Network';
238
  }
239
  }
240
 
241
+ /* ═══ EVENT BINDINGS ═══ */
 
242
  D.vibeBtn.addEventListener('click', generate);
243
  D.publishBtn.addEventListener('click', publish);
244
+ D.prompt.addEventListener('keydown', e => {
245
+ if ((e.ctrlKey||e.metaKey) && e.key==='Enter') { e.preventDefault(); generate(); }
246
+ });
247
  D.copyBtn.addEventListener('click', async () => {
248
  if (!S.fullCode) return;
249
  try { await navigator.clipboard.writeText(S.fullCode); } catch {}
250
  });
251
 
252
+ console.log('πŸ§™β€β™‚οΈ Omni-Vibe Studio β€” Liquid Glass Client ready');