tryassay 0.21.1 → 0.22.0

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 (37) hide show
  1. package/README.md +4 -4
  2. package/demo/.claude/.truth_last_prompt +1 -0
  3. package/demo/.claude/truth_status +1 -0
  4. package/demo/css/style.css +1181 -0
  5. package/demo/data/demo-events.json +103 -0
  6. package/demo/index.html +222 -0
  7. package/demo/js/chat.js +292 -0
  8. package/demo/js/code-panel.js +206 -0
  9. package/demo/js/demo-mode.js +107 -0
  10. package/demo/js/orb.js +634 -0
  11. package/demo/js/question-cards.js +207 -0
  12. package/demo/js/sse-client.js +473 -0
  13. package/demo/js/state.js +162 -0
  14. package/demo/js/timeline.js +394 -0
  15. package/demo/js/voice.js +154 -0
  16. package/dist/api/server.d.ts +1 -0
  17. package/dist/api/server.js +65 -2
  18. package/dist/api/server.js.map +1 -1
  19. package/dist/cli.js +13 -0
  20. package/dist/cli.js.map +1 -1
  21. package/dist/commands/demo.d.ts +5 -0
  22. package/dist/commands/demo.js +107 -0
  23. package/dist/commands/demo.js.map +1 -0
  24. package/dist/commands/runtime.d.ts +4 -0
  25. package/dist/commands/runtime.js +50 -3
  26. package/dist/commands/runtime.js.map +1 -1
  27. package/dist/runtime/agents/planner-agent.d.ts +5 -2
  28. package/dist/runtime/agents/planner-agent.js +232 -1
  29. package/dist/runtime/agents/planner-agent.js.map +1 -1
  30. package/dist/runtime/app-create-orchestrator.d.ts +4 -0
  31. package/dist/runtime/app-create-orchestrator.js +151 -48
  32. package/dist/runtime/app-create-orchestrator.js.map +1 -1
  33. package/dist/runtime/dashboard-sync.d.ts +25 -0
  34. package/dist/runtime/dashboard-sync.js +169 -0
  35. package/dist/runtime/dashboard-sync.js.map +1 -0
  36. package/dist/runtime/types.d.ts +28 -0
  37. package/package.json +3 -2
@@ -0,0 +1,206 @@
1
+ // code-panel.js — Bottom streaming code display with typewriter effect
2
+ // Subscribes to AppState code_generated events, renders tabs + syntax-highlighted code
3
+
4
+ const files = []; // { path, code }
5
+ let activeIndex = -1;
6
+ let typewriterRaf = 0; // requestAnimationFrame ID
7
+
8
+ const KEYWORDS = new Set([
9
+ 'const', 'let', 'var', 'function', 'return', 'import', 'export', 'from',
10
+ 'if', 'else', 'async', 'await', 'class', 'extends', 'new', 'this',
11
+ 'throw', 'try', 'catch', 'typeof', 'interface', 'type'
12
+ ]);
13
+
14
+ const BUILDING_PHASES = new Set(['building_feature', 'scaffolding']);
15
+
16
+ function highlightSyntax(code) {
17
+ let result = '';
18
+ let i = 0;
19
+ const len = code.length;
20
+
21
+ while (i < len) {
22
+ // Comments: // to end of line
23
+ if (code[i] === '/' && code[i + 1] === '/') {
24
+ const end = code.indexOf('\n', i);
25
+ const slice = end === -1 ? code.slice(i) : code.slice(i, end);
26
+ result += `<span class="comment">${escapeHtml(slice)}</span>`;
27
+ i += slice.length;
28
+ continue;
29
+ }
30
+
31
+ // Strings: single or double quoted
32
+ if (code[i] === '"' || code[i] === "'") {
33
+ const quote = code[i];
34
+ let j = i + 1;
35
+ while (j < len && code[j] !== quote) {
36
+ if (code[j] === '\\') j++; // skip escaped chars
37
+ j++;
38
+ }
39
+ j++; // include closing quote
40
+ result += `<span class="string">${escapeHtml(code.slice(i, j))}</span>`;
41
+ i = j;
42
+ continue;
43
+ }
44
+
45
+ // Template literals
46
+ if (code[i] === '`') {
47
+ let j = i + 1;
48
+ while (j < len && code[j] !== '`') {
49
+ if (code[j] === '\\') j++;
50
+ j++;
51
+ }
52
+ j++;
53
+ result += `<span class="string">${escapeHtml(code.slice(i, j))}</span>`;
54
+ i = j;
55
+ continue;
56
+ }
57
+
58
+ // Numbers
59
+ if (/\d/.test(code[i]) && (i === 0 || /[\s,;:=+\-*/([\]{}<>!&|^~?]/.test(code[i - 1]))) {
60
+ let j = i;
61
+ while (j < len && /[\d.xXa-fA-F_]/.test(code[j])) j++;
62
+ result += `<span class="number">${escapeHtml(code.slice(i, j))}</span>`;
63
+ i = j;
64
+ continue;
65
+ }
66
+
67
+ // Words (potential keywords)
68
+ if (/[a-zA-Z_$]/.test(code[i])) {
69
+ let j = i;
70
+ while (j < len && /[a-zA-Z0-9_$]/.test(code[j])) j++;
71
+ const word = code.slice(i, j);
72
+ if (KEYWORDS.has(word)) {
73
+ result += `<span class="keyword">${word}</span>`;
74
+ } else {
75
+ result += escapeHtml(word);
76
+ }
77
+ i = j;
78
+ continue;
79
+ }
80
+
81
+ // Everything else
82
+ result += escapeHtml(code[i]);
83
+ i++;
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ function escapeHtml(str) {
90
+ return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
91
+ }
92
+
93
+ function renderTabs() {
94
+ const tabsEl = document.getElementById('code-tabs');
95
+ if (!tabsEl) return;
96
+
97
+ tabsEl.innerHTML = '';
98
+
99
+ for (let i = 0; i < files.length; i++) {
100
+ const btn = document.createElement('button');
101
+ btn.className = 'code-tab';
102
+ if (i === activeIndex) btn.classList.add('active');
103
+ // Show just the filename, not the full path
104
+ const name = files[i].path.split('/').pop();
105
+ btn.textContent = name;
106
+ btn.addEventListener('click', () => showFile(i));
107
+ tabsEl.appendChild(btn);
108
+ }
109
+ }
110
+
111
+ function showFile(index) {
112
+ if (index < 0 || index >= files.length) return;
113
+ activeIndex = index;
114
+ renderTabs();
115
+ startTypewriter(files[index].code);
116
+ }
117
+
118
+ function startTypewriter(code) {
119
+ const contentEl = document.getElementById('code-content');
120
+ if (!contentEl) return;
121
+
122
+ // Cancel any ongoing typewriter
123
+ if (typewriterRaf) cancelAnimationFrame(typewriterRaf);
124
+
125
+ const highlighted = highlightSyntax(code);
126
+ let charIndex = 0;
127
+ const charsPerFrame = 30;
128
+
129
+ contentEl.innerHTML = '';
130
+ contentEl.classList.add('cursor-blink');
131
+
132
+ function tick() {
133
+ charIndex += charsPerFrame;
134
+ if (charIndex >= highlighted.length) {
135
+ contentEl.innerHTML = highlighted;
136
+ contentEl.classList.remove('cursor-blink');
137
+ typewriterRaf = 0;
138
+ return;
139
+ }
140
+ // Render up to charIndex, but don't break inside HTML tags
141
+ let safeEnd = charIndex;
142
+ const openBracket = highlighted.lastIndexOf('<', safeEnd);
143
+ const closeBracket = highlighted.lastIndexOf('>', safeEnd);
144
+ if (openBracket > closeBracket) {
145
+ // We're inside a tag — extend to close it
146
+ const nextClose = highlighted.indexOf('>', safeEnd);
147
+ if (nextClose !== -1) safeEnd = nextClose + 1;
148
+ }
149
+ contentEl.innerHTML = highlighted.slice(0, safeEnd);
150
+ typewriterRaf = requestAnimationFrame(tick);
151
+ }
152
+
153
+ typewriterRaf = requestAnimationFrame(tick);
154
+ }
155
+
156
+ function updatePanelVisibility(phase) {
157
+ const panel = document.getElementById('code-panel');
158
+ if (!panel) return;
159
+ if (BUILDING_PHASES.has(phase)) {
160
+ panel.classList.add('active');
161
+ } else {
162
+ panel.classList.remove('active');
163
+ }
164
+ }
165
+
166
+ export function initCodePanel() {
167
+ const state = window.AppState;
168
+ if (!state) return;
169
+
170
+ state.on('code_generated', (file) => {
171
+ if (!file || !file.path) return;
172
+ const existing = files.findIndex(f => f.path === file.path);
173
+ const normalized = { path: file.path, code: file.content || file.code || '' };
174
+ if (existing >= 0) {
175
+ files[existing] = normalized;
176
+ if (existing === activeIndex) {
177
+ startTypewriter(normalized.code);
178
+ }
179
+ } else {
180
+ files.push(normalized);
181
+ // Auto-select newly added file
182
+ showFile(files.length - 1);
183
+ }
184
+ renderTabs();
185
+ });
186
+
187
+ state.on('phase_change', ({ to }) => {
188
+ updatePanelVisibility(to);
189
+ });
190
+
191
+ state.on('reset', () => {
192
+ files.length = 0;
193
+ activeIndex = -1;
194
+ if (typewriterRaf) cancelAnimationFrame(typewriterRaf);
195
+ typewriterRaf = 0;
196
+ const tabsEl = document.getElementById('code-tabs');
197
+ const contentEl = document.getElementById('code-content');
198
+ if (tabsEl) tabsEl.innerHTML = '';
199
+ if (contentEl) {
200
+ contentEl.innerHTML = '';
201
+ contentEl.classList.remove('cursor-blink');
202
+ }
203
+ const panel = document.getElementById('code-panel');
204
+ if (panel) panel.classList.remove('active');
205
+ });
206
+ }
@@ -0,0 +1,107 @@
1
+ // demo-mode.js — Canned event replay for offline demos
2
+ // Loads pre-recorded events and replays them with timing
3
+
4
+ const AppState = window.AppState;
5
+
6
+ let events = null;
7
+ let timers = [];
8
+
9
+ function dispatchEvent(event) {
10
+ const { type, data } = event;
11
+
12
+ switch (type) {
13
+ case 'phase_change':
14
+ AppState.update({ phase: data.phase });
15
+ break;
16
+
17
+ case 'agent_activate': {
18
+ const existing = AppState.get().agents.find(a => a.name === data.name);
19
+ if (existing) {
20
+ AppState.updateAgent(data.name, data);
21
+ } else {
22
+ AppState.addAgent(data);
23
+ }
24
+ break;
25
+ }
26
+
27
+ case 'task_add':
28
+ AppState.addTask(data);
29
+ AppState.update({ currentTask: { name: data.title || data.name, agent: data.agent, status: data.status } });
30
+ break;
31
+
32
+ case 'task_update':
33
+ AppState.updateTask(data.id, data);
34
+ break;
35
+
36
+ case 'verification':
37
+ AppState.addVerification(data);
38
+ break;
39
+
40
+ case 'code_generated':
41
+ AppState.addCodeFile(data);
42
+ break;
43
+
44
+ case 'metric_update':
45
+ AppState.update({ metrics: data });
46
+ break;
47
+ }
48
+ }
49
+
50
+ function clearTimers() {
51
+ for (const id of timers) {
52
+ clearTimeout(id);
53
+ }
54
+ timers = [];
55
+ }
56
+
57
+ export function initDemoMode() {
58
+ return fetch('data/demo-events.json')
59
+ .then(res => {
60
+ if (!res.ok) throw new Error(`Failed to load demo events: ${res.status}`);
61
+ return res.json();
62
+ })
63
+ .then(data => {
64
+ events = data;
65
+ })
66
+ .catch(err => {
67
+ console.error('[Demo] Failed to load events:', err);
68
+ });
69
+ }
70
+
71
+ export function startDemo(prompt) {
72
+ if (!events || events.length === 0) {
73
+ console.error('[Demo] No events loaded. Call initDemoMode() first.');
74
+ return;
75
+ }
76
+
77
+ // Reset state for fresh demo
78
+ AppState.reset();
79
+ clearTimers();
80
+
81
+ // Disable input during demo
82
+ const input = document.getElementById('prompt-input');
83
+ const btn = document.getElementById('submit-btn');
84
+ if (input) input.disabled = true;
85
+ if (btn) btn.disabled = true;
86
+
87
+ // Log the prompt
88
+ AppState.addLog({ level: 'info', message: `Demo started: "${prompt}"` });
89
+
90
+ // Schedule all events by their absolute delay
91
+ const lastDelay = events[events.length - 1].delay;
92
+
93
+ for (const event of events) {
94
+ const id = setTimeout(() => {
95
+ dispatchEvent(event);
96
+ }, event.delay);
97
+ timers.push(id);
98
+ }
99
+
100
+ // Re-enable input after all events complete
101
+ const finishId = setTimeout(() => {
102
+ if (input) input.disabled = false;
103
+ if (btn) btn.disabled = false;
104
+ timers = [];
105
+ }, lastDelay + 500);
106
+ timers.push(finishId);
107
+ }