vision-navigator 1.0.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.
package/public/app.js ADDED
@@ -0,0 +1,155 @@
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const runBtn = document.getElementById('runBtn');
3
+ const yamlInput = document.getElementById('yamlInput');
4
+ const btnText = document.getElementById('btnText');
5
+ const loader = document.getElementById('loader');
6
+ const statusMessage = document.getElementById('statusMessage');
7
+ const resultsContainer = document.getElementById('resultsContainer');
8
+ const basePath = window.location.pathname.endsWith('/') ? window.location.pathname : window.location.pathname + '/';
9
+ const urlFor = (p) => basePath + String(p || '').replace(/^\//, '');
10
+
11
+ // Default placeholder
12
+ yamlInput.value = `steps:
13
+ - instruction: "Navigate to https://quotes.toscrape.com"
14
+ - instruction: "Click the 'Login' link"
15
+ - instruction: "Type 'admin' into the username field"
16
+ - instruction: "Type 'password' into the password field"
17
+ - instruction: "Click the 'Login' button"`;
18
+
19
+ runBtn.addEventListener('click', async () => {
20
+ const yamlContent = yamlInput.value.trim();
21
+
22
+ if (!yamlContent) {
23
+ alert('Please enter a YAML workflow.');
24
+ return;
25
+ }
26
+
27
+ // Set Loading State
28
+ runBtn.disabled = true;
29
+ runBtn.classList.add('opacity-75', 'cursor-not-allowed');
30
+ btnText.innerText = 'Executing...';
31
+ loader.classList.remove('hidden');
32
+
33
+ statusMessage.innerText = 'Workflow is currently running. The AI is processing your instructions in a headless browser...';
34
+ statusMessage.classList.remove('hidden');
35
+ resultsContainer.innerHTML = '';
36
+ resultsContainer.classList.add('hidden');
37
+
38
+ try {
39
+ const response = await fetch(urlFor('workflow'), {
40
+ method: 'POST',
41
+ headers: {
42
+ 'Content-Type': 'text/yaml'
43
+ },
44
+ body: yamlContent
45
+ });
46
+
47
+ const data = await response.json();
48
+
49
+ if (!response.ok) {
50
+ throw new Error(data.error || 'Unknown error occurred');
51
+ }
52
+
53
+ renderResults(data.run?.results || data.results);
54
+
55
+ } catch (error) {
56
+ statusMessage.innerHTML = `<span class="text-red-600 font-semibold">Error:</span> ${error.message}`;
57
+ } finally {
58
+ // Reset Loading State
59
+ runBtn.disabled = false;
60
+ runBtn.classList.remove('opacity-75', 'cursor-not-allowed');
61
+ btnText.innerText = 'Run Workflow';
62
+ loader.classList.add('hidden');
63
+ }
64
+ });
65
+
66
+ function renderResults(results) {
67
+ if (!results || results.length === 0) {
68
+ statusMessage.innerText = 'Workflow executed but returned no results.';
69
+ return;
70
+ }
71
+
72
+ statusMessage.classList.add('hidden');
73
+ resultsContainer.classList.remove('hidden');
74
+
75
+ results.forEach((step, index) => {
76
+ const stepDiv = document.createElement('div');
77
+ stepDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50';
78
+
79
+ // Header
80
+ const header = document.createElement('h3');
81
+ header.className = 'text-lg font-bold text-blue-700 mb-2';
82
+ header.innerText = `Step ${index + 1}: ${step.step}`;
83
+ stepDiv.appendChild(header);
84
+
85
+ // Action taken
86
+ if (step.action) {
87
+ const actionDiv = document.createElement('div');
88
+ actionDiv.className = 'bg-gray-800 text-green-400 p-3 rounded text-sm font-mono mb-4 whitespace-pre-wrap';
89
+
90
+ // Try to parse action if it's JSON string
91
+ let displayAction = step.action;
92
+ try {
93
+ const parsed = JSON.parse(step.action);
94
+ displayAction = JSON.stringify(parsed, null, 2);
95
+ } catch(e) {}
96
+
97
+ actionDiv.innerText = displayAction;
98
+ stepDiv.appendChild(actionDiv);
99
+ }
100
+
101
+ // Console Logs
102
+ if (step.logs && step.logs.length > 0) {
103
+ const logsDiv = document.createElement('div');
104
+ logsDiv.className = 'bg-black text-gray-300 p-3 rounded text-xs font-mono mb-4 h-32 overflow-y-auto';
105
+
106
+ const logsTitle = document.createElement('div');
107
+ logsTitle.className = 'text-gray-500 mb-1 border-b border-gray-700 pb-1';
108
+ logsTitle.innerText = 'Browser Console Logs:';
109
+ logsDiv.appendChild(logsTitle);
110
+
111
+ step.logs.forEach(log => {
112
+ const logLine = document.createElement('div');
113
+ logLine.innerText = log;
114
+ if (log.includes('ERROR')) logLine.className = 'text-red-400';
115
+ else if (log.includes('WARN')) logLine.className = 'text-yellow-400';
116
+ logsDiv.appendChild(logLine);
117
+ });
118
+
119
+ stepDiv.appendChild(logsDiv);
120
+ }
121
+
122
+ // Screenshots
123
+ if (step.screenshots) {
124
+ const imgGrid = document.createElement('div');
125
+ imgGrid.className = 'grid grid-cols-1 md:grid-cols-2 gap-4 mt-4';
126
+
127
+ if (step.screenshots.before) {
128
+ const beforeDiv = document.createElement('div');
129
+ beforeDiv.innerHTML = `
130
+ <p class="text-sm font-semibold text-gray-600 mb-1">Before Action</p>
131
+ <a href="${urlFor(step.screenshots.before)}" target="_blank">
132
+ <img src="${urlFor(step.screenshots.before)}" class="w-full border border-gray-300 rounded shadow-sm hover:opacity-90 transition cursor-pointer" alt="Before">
133
+ </a>
134
+ `;
135
+ imgGrid.appendChild(beforeDiv);
136
+ }
137
+
138
+ if (step.screenshots.after) {
139
+ const afterDiv = document.createElement('div');
140
+ afterDiv.innerHTML = `
141
+ <p class="text-sm font-semibold text-gray-600 mb-1">After Action</p>
142
+ <a href="${urlFor(step.screenshots.after)}" target="_blank">
143
+ <img src="${urlFor(step.screenshots.after)}" class="w-full border border-gray-300 rounded shadow-sm hover:opacity-90 transition cursor-pointer" alt="After">
144
+ </a>
145
+ `;
146
+ imgGrid.appendChild(afterDiv);
147
+ }
148
+
149
+ stepDiv.appendChild(imgGrid);
150
+ }
151
+
152
+ resultsContainer.appendChild(stepDiv);
153
+ });
154
+ }
155
+ });