juxscript 1.1.152 → 1.1.154

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 (2) hide show
  1. package/machinery/errors.js +134 -113
  2. package/package.json +1 -1
@@ -1,19 +1,16 @@
1
1
  /**
2
2
  * Generates an injectable client-side error collector script.
3
- * Catches runtime errors, unhandled promise rejections, and console.error calls.
4
- * Displays them in a floating overlay panel in the browser.
3
+ * Full-screen overlay similar to Vite's error screen.
5
4
  *
6
5
  * @param {Object} options
7
- * @param {boolean} [options.enabled=true] - Whether to enable the collector
8
- * @param {number} [options.maxErrors=50] - Max errors to keep in buffer
9
- * @param {string} [options.position='bottom-right'] - Panel position
6
+ * @param {boolean} [options.enabled=true]
7
+ * @param {number} [options.maxErrors=50]
10
8
  * @returns {string} Inline script to inject into HTML
11
9
  */
12
10
  export function generateErrorCollector(options = {}) {
13
11
  const {
14
12
  enabled = true,
15
- maxErrors = 50,
16
- position = 'bottom-right'
13
+ maxErrors = 50
17
14
  } = options;
18
15
 
19
16
  if (!enabled) return '';
@@ -21,93 +18,110 @@ export function generateErrorCollector(options = {}) {
21
18
  return `
22
19
  <script>
23
20
  (function() {
24
- var __juxErrors = [];
25
- var __juxMaxErrors = ${maxErrors};
26
- var __juxPanel = null;
27
- var __juxList = null;
28
- var __juxBadge = null;
29
- var __juxVisible = false;
30
-
31
- var posMap = {
32
- 'bottom-right': 'bottom:12px;right:12px;',
33
- 'bottom-left': 'bottom:12px;left:12px;',
34
- 'top-right': 'top:12px;right:12px;',
35
- 'top-left': 'top:12px;left:12px;'
36
- };
37
- var panelPos = posMap['${position}'] || posMap['bottom-right'];
38
-
39
- function createPanel() {
40
- // Toggle button / badge
41
- __juxBadge = document.createElement('div');
42
- __juxBadge.id = '__jux-error-badge';
43
- __juxBadge.setAttribute('style',
44
- 'position:fixed;' + panelPos +
45
- 'z-index:99999;width:36px;height:36px;border-radius:50%;' +
46
- 'background:#e74c3c;color:#fff;font-size:1rem;font-weight:bold;' +
47
- 'display:flex;align-items:center;justify-content:center;cursor:pointer;' +
48
- 'font-family:monospace;box-shadow:0 2px 8px rgba(0,0,0,0.3);'
49
- );
50
- __juxBadge.textContent = '0';
51
- __juxBadge.addEventListener('click', togglePanel);
52
- document.body.appendChild(__juxBadge);
53
-
54
- // Panel
55
- __juxPanel = document.createElement('div');
56
- __juxPanel.id = '__jux-error-panel';
57
- __juxPanel.setAttribute('style',
58
- 'position:fixed;bottom:56px;right:12px;z-index:99998;' +
59
- 'width:480px;max-height:360px;background:#1e1e1e;color:#f8f8f8;' +
60
- 'border:1px solid #e74c3c;border-radius:8px;font-family:monospace;' +
61
- 'font-size:1rem;display:flex;flex-direction:column;' +
62
- 'box-shadow:0 4px 16px rgba(0,0,0,0.4);'
63
- );
64
-
65
- // Header
21
+ var __errors = [];
22
+ var __maxErrors = ${maxErrors};
23
+ var __overlay = null;
24
+ var __list = null;
25
+
26
+ function createOverlay() {
27
+ __overlay = document.createElement('div');
28
+ __overlay.id = '__jux-error-overlay';
29
+ __overlay.setAttribute('style', [
30
+ 'position:fixed',
31
+ 'top:0',
32
+ 'left:0',
33
+ 'width:100vw',
34
+ 'height:100vh',
35
+ 'z-index:99999',
36
+ 'background:rgba(0,0,0,0.85)',
37
+ 'color:#f8f8f8',
38
+ 'font-family:monospace',
39
+ 'font-size:14px',
40
+ 'overflow-y:auto',
41
+ 'padding:0',
42
+ 'margin:0',
43
+ 'box-sizing:border-box'
44
+ ].join(';'));
45
+
46
+ var container = document.createElement('div');
47
+ container.setAttribute('style', [
48
+ 'max-width:960px',
49
+ 'margin:40px auto',
50
+ 'padding:24px 32px',
51
+ 'border:2px solid #ff5555',
52
+ 'border-radius:8px',
53
+ 'background:#1a1a1a'
54
+ ].join(';'));
55
+
56
+ // Title bar
66
57
  var header = document.createElement('div');
67
- header.setAttribute('style',
68
- 'padding:8px 12px;background:#e74c3c;color:#fff;font-weight:bold;' +
69
- 'display:flex;justify-content:space-between;align-items:center;' +
70
- 'border-radius:7px 7px 0 0;'
71
- );
72
- header.innerHTML = '<span>⚠ Runtime Errors</span>';
58
+ header.setAttribute('style', [
59
+ 'display:flex',
60
+ 'justify-content:space-between',
61
+ 'align-items:center',
62
+ 'margin-bottom:16px',
63
+ 'padding-bottom:12px',
64
+ 'border-bottom:1px solid #333'
65
+ ].join(';'));
66
+
67
+ var title = document.createElement('div');
68
+ title.setAttribute('style', 'color:#ff5555;font-size:16px;font-weight:bold;');
69
+ title.textContent = 'Runtime Error';
70
+ header.appendChild(title);
71
+
72
+ var actions = document.createElement('div');
73
+ actions.setAttribute('style', 'display:flex;gap:8px;');
73
74
 
74
75
  var clearBtn = document.createElement('button');
75
76
  clearBtn.textContent = 'Clear';
76
- clearBtn.setAttribute('style',
77
- 'background:transparent;border:1px solid #fff;color:#fff;' +
78
- 'padding:2px 8px;border-radius:4px;cursor:pointer;font-size:1rem;'
79
- );
77
+ clearBtn.setAttribute('style', [
78
+ 'background:transparent',
79
+ 'border:1px solid #555',
80
+ 'color:#aaa',
81
+ 'padding:4px 12px',
82
+ 'border-radius:4px',
83
+ 'cursor:pointer',
84
+ 'font-family:monospace',
85
+ 'font-size:12px'
86
+ ].join(';'));
80
87
  clearBtn.addEventListener('click', clearErrors);
81
- header.appendChild(clearBtn);
82
-
83
- __juxPanel.appendChild(header);
84
-
85
- // List
86
- __juxList = document.createElement('div');
87
- __juxList.setAttribute('style',
88
- 'overflow-y:auto;max-height:300px;padding:8px;'
89
- );
90
- __juxPanel.appendChild(__juxList);
91
88
 
92
- document.body.appendChild(__juxPanel);
93
- }
94
-
95
- function togglePanel() {
96
- __juxVisible = !__juxVisible;
97
- __juxPanel.style.display = __juxVisible ? 'flex' : 'none';
89
+ var closeBtn = document.createElement('button');
90
+ closeBtn.textContent = 'Close';
91
+ closeBtn.setAttribute('style', [
92
+ 'background:transparent',
93
+ 'border:1px solid #555',
94
+ 'color:#aaa',
95
+ 'padding:4px 12px',
96
+ 'border-radius:4px',
97
+ 'cursor:pointer',
98
+ 'font-family:monospace',
99
+ 'font-size:12px'
100
+ ].join(';'));
101
+ closeBtn.addEventListener('click', function() {
102
+ __overlay.style.display = 'none';
103
+ });
104
+
105
+ actions.appendChild(clearBtn);
106
+ actions.appendChild(closeBtn);
107
+ header.appendChild(actions);
108
+ container.appendChild(header);
109
+
110
+ __list = document.createElement('div');
111
+ container.appendChild(__list);
112
+
113
+ __overlay.appendChild(container);
114
+ document.body.appendChild(__overlay);
98
115
  }
99
116
 
100
117
  function clearErrors() {
101
- __juxErrors = [];
102
- __juxList.innerHTML = '';
103
- __juxBadge.textContent = '0';
104
- __juxBadge.style.display = 'none';
105
- __juxPanel.style.display = 'none';
106
- __juxVisible = false;
118
+ __errors = [];
119
+ if (__list) __list.innerHTML = '';
120
+ if (__overlay) __overlay.style.display = 'none';
107
121
  }
108
122
 
109
123
  function addError(type, message, source, line, col, stack) {
110
- if (__juxErrors.length >= __juxMaxErrors) __juxErrors.shift();
124
+ if (__errors.length >= __maxErrors) __errors.shift();
111
125
 
112
126
  var entry = {
113
127
  type: type,
@@ -118,39 +132,50 @@ export function generateErrorCollector(options = {}) {
118
132
  stack: stack || '',
119
133
  time: new Date().toLocaleTimeString()
120
134
  };
121
- __juxErrors.push(entry);
122
-
123
- if (!__juxPanel) createPanel();
135
+ __errors.push(entry);
124
136
 
125
- // Update badge
126
- __juxBadge.textContent = String(__juxErrors.length);
127
- __juxBadge.style.display = 'flex';
137
+ if (!__overlay) createOverlay();
138
+ __overlay.style.display = 'block';
128
139
 
129
- // ✅ Auto-open panel on first error
130
- if (!__juxVisible) {
131
- __juxVisible = true;
132
- __juxPanel.style.display = 'flex';
133
- }
134
-
135
- // Add to list
136
140
  var item = document.createElement('div');
137
- item.setAttribute('style',
138
- 'padding:6px 8px;margin-bottom:4px;background:#2d2d2d;border-radius:4px;' +
139
- 'border-left:3px solid ' + (type === 'error' ? '#e74c3c' : type === 'rejection' ? '#e67e22' : '#f39c12') + ';'
140
- );
141
+ item.setAttribute('style', [
142
+ 'padding:12px 16px',
143
+ 'margin-bottom:8px',
144
+ 'background:#222',
145
+ 'border-left:3px solid #ff5555',
146
+ 'border-radius:4px'
147
+ ].join(';'));
148
+
149
+ var typeLabel = type === 'error' ? 'Error' : type === 'rejection' ? 'Unhandled Rejection' : 'console.error';
141
150
 
142
- var label = type === 'error' ? 'Error' : type === 'rejection' ? 'Rejection' : 'Console';
151
+ var meta = entry.source ? entry.source + ':' + entry.line + ':' + entry.col : '';
143
152
 
144
153
  item.innerHTML =
145
- '<div style="color:#888;font-size:1rem;margin-bottom:2px;">' +
146
- entry.time + ' ' + label +
147
- (entry.source ? ' ' + entry.source + ':' + entry.line : '') +
154
+ '<div style="color:#888;font-size:12px;margin-bottom:4px;">' +
155
+ typeLabel + (meta ? ' ' + escapeHtml(meta) : '') +
156
+ '<span style="float:right;">' + entry.time + '</span>' +
148
157
  '</div>' +
149
- '<div style="word-break:break-word;">' + escapeHtml(entry.message) + '</div>' +
150
- (entry.stack ? '<div style="margin-top:4px;"><div style="cursor:pointer;color:#888;font-size:1rem;">Stack trace</div><pre style="margin:4px 0 0;font-size:1rem;color:#aaa;white-space:pre-wrap;">' + escapeHtml(entry.stack) + '</pre></div>' : '');
158
+ '<div style="color:#ff8888;white-space:pre-wrap;word-break:break-word;">' +
159
+ escapeHtml(entry.message) +
160
+ '</div>' +
161
+ (entry.stack ?
162
+ '<pre style="margin:8px 0 0;padding:8px;font-size:12px;color:#888;' +
163
+ 'white-space:pre-wrap;background:#1a1a1a;border-radius:4px;' +
164
+ 'border:1px solid #333;max-height:200px;overflow-y:auto;">' +
165
+ escapeHtml(cleanStack(entry.stack)) +
166
+ '</pre>'
167
+ : '');
168
+
169
+ __list.appendChild(item);
170
+ }
151
171
 
152
- __juxList.appendChild(item);
153
- __juxList.scrollTop = __juxList.scrollHeight;
172
+ function cleanStack(stack) {
173
+ // Remove the first line if it duplicates the message
174
+ var lines = stack.split('\\n');
175
+ if (lines.length > 1 && lines[0].indexOf('Error:') !== -1) {
176
+ lines.shift();
177
+ }
178
+ return lines.join('\\n').trim();
154
179
  }
155
180
 
156
181
  function escapeHtml(str) {
@@ -159,12 +184,10 @@ export function generateErrorCollector(options = {}) {
159
184
 
160
185
  // --- Hooks ---
161
186
 
162
- // 1. window.onerror
163
187
  window.onerror = function(msg, source, line, col, err) {
164
188
  addError('error', msg, source, line, col, err && err.stack ? err.stack : '');
165
189
  };
166
190
 
167
- // 2. Unhandled promise rejections
168
191
  window.addEventListener('unhandledrejection', function(e) {
169
192
  var reason = e.reason;
170
193
  var msg = reason instanceof Error ? reason.message : String(reason);
@@ -172,7 +195,6 @@ export function generateErrorCollector(options = {}) {
172
195
  addError('rejection', msg, '', 0, 0, stack);
173
196
  });
174
197
 
175
- // 3. Intercept console.error
176
198
  var _origConsoleError = console.error;
177
199
  console.error = function() {
178
200
  var args = Array.prototype.slice.call(arguments);
@@ -183,11 +205,10 @@ export function generateErrorCollector(options = {}) {
183
205
  _origConsoleError.apply(console, arguments);
184
206
  };
185
207
 
186
- // Expose API for programmatic access
187
208
  window.__juxErrors = {
188
- list: function() { return __juxErrors.slice(); },
209
+ list: function() { return __errors.slice(); },
189
210
  clear: clearErrors,
190
- count: function() { return __juxErrors.length; }
211
+ count: function() { return __errors.length; }
191
212
  };
192
213
  })();
193
214
  </script>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.152",
3
+ "version": "1.1.154",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",