mitway-tagger 1.3.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/README.md +256 -0
- package/dist/index.cjs +909 -0
- package/dist/index.d.cts +204 -0
- package/dist/index.d.ts +204 -0
- package/dist/index.js +865 -0
- package/package.json +47 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
// src/features/jsxSource.ts
|
|
2
|
+
var VIRTUAL_JSX_RUNTIME = "virtual:mitway-jsx-runtime";
|
|
3
|
+
var RESOLVED_VIRTUAL_JSX_RUNTIME = "\0" + VIRTUAL_JSX_RUNTIME;
|
|
4
|
+
var ORIGINAL_JSX_RUNTIME = "\0__original_react_jsx_dev_runtime";
|
|
5
|
+
var JSX_RUNTIME_CODE = `
|
|
6
|
+
import * as OriginalRuntime from '\\0__original_react_jsx_dev_runtime';
|
|
7
|
+
|
|
8
|
+
// Symbol for storing source info on DOM nodes
|
|
9
|
+
const SOURCE_SYMBOL = Symbol.for('__mitwaySource__');
|
|
10
|
+
|
|
11
|
+
// Global map of source locations to DOM elements (using WeakRef)
|
|
12
|
+
if (typeof window !== 'undefined' && !window.mitwaySourceMap) {
|
|
13
|
+
window.mitwaySourceMap = new Map();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a ref callback that attaches source info to the DOM node
|
|
18
|
+
*/
|
|
19
|
+
function createSourceRef(source, existingRef) {
|
|
20
|
+
return (node) => {
|
|
21
|
+
if (node && source) {
|
|
22
|
+
// Attach source info directly to the DOM node
|
|
23
|
+
node[SOURCE_SYMBOL] = {
|
|
24
|
+
fileName: source.fileName,
|
|
25
|
+
lineNumber: source.lineNumber,
|
|
26
|
+
columnNumber: source.columnNumber,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Add to global source map for lookup
|
|
30
|
+
if (typeof window !== 'undefined' && window.mitwaySourceMap) {
|
|
31
|
+
const key = source.fileName + ':' + source.lineNumber + ':' + source.columnNumber;
|
|
32
|
+
|
|
33
|
+
if (!window.mitwaySourceMap.has(key)) {
|
|
34
|
+
window.mitwaySourceMap.set(key, new Set());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Use WeakRef to avoid memory leaks
|
|
38
|
+
const weakRef = new WeakRef(node);
|
|
39
|
+
window.mitwaySourceMap.get(key).add(weakRef);
|
|
40
|
+
|
|
41
|
+
// Cleanup dead refs periodically
|
|
42
|
+
const refs = window.mitwaySourceMap.get(key);
|
|
43
|
+
if (refs.size > 100) {
|
|
44
|
+
for (const ref of refs) {
|
|
45
|
+
if (!ref.deref()) {
|
|
46
|
+
refs.delete(ref);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Call existing ref if present
|
|
54
|
+
if (existingRef) {
|
|
55
|
+
if (typeof existingRef === 'function') {
|
|
56
|
+
existingRef(node);
|
|
57
|
+
} else if (typeof existingRef === 'object') {
|
|
58
|
+
existingRef.current = node;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Custom jsxDEV that wraps the original with source tracking
|
|
66
|
+
*/
|
|
67
|
+
export function jsxDEV(type, props, key, isStaticChildren, source, self) {
|
|
68
|
+
// Only track HTML elements and custom components, not fragments
|
|
69
|
+
if (source && type && typeof type !== 'symbol') {
|
|
70
|
+
const existingRef = props?.ref;
|
|
71
|
+
const newProps = {
|
|
72
|
+
...props,
|
|
73
|
+
ref: createSourceRef(source, existingRef),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return OriginalRuntime.jsxDEV(type, newProps, key, isStaticChildren, source, self);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return OriginalRuntime.jsxDEV(type, props, key, isStaticChildren, source, self);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Re-export everything else from the original runtime
|
|
83
|
+
export const Fragment = OriginalRuntime.Fragment;
|
|
84
|
+
export const jsx = OriginalRuntime.jsx;
|
|
85
|
+
export const jsxs = OriginalRuntime.jsxs;
|
|
86
|
+
`;
|
|
87
|
+
function createJsxSourcePlugin(options) {
|
|
88
|
+
return {
|
|
89
|
+
name: "mitway-tagger:jsx-source",
|
|
90
|
+
enforce: "pre",
|
|
91
|
+
resolveId(id, importer) {
|
|
92
|
+
if (id === ORIGINAL_JSX_RUNTIME || id === "\0__original_react_jsx_dev_runtime") {
|
|
93
|
+
return this.resolve("react/jsx-dev-runtime", importer, { skipSelf: true });
|
|
94
|
+
}
|
|
95
|
+
if (id === "react/jsx-dev-runtime") {
|
|
96
|
+
return RESOLVED_VIRTUAL_JSX_RUNTIME;
|
|
97
|
+
}
|
|
98
|
+
if (id === VIRTUAL_JSX_RUNTIME) {
|
|
99
|
+
return RESOLVED_VIRTUAL_JSX_RUNTIME;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
},
|
|
103
|
+
load(id) {
|
|
104
|
+
if (id === RESOLVED_VIRTUAL_JSX_RUNTIME) {
|
|
105
|
+
return JSX_RUNTIME_CODE;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
},
|
|
109
|
+
config() {
|
|
110
|
+
return {
|
|
111
|
+
// Ensure we're using jsxDEV in development
|
|
112
|
+
esbuild: {
|
|
113
|
+
jsx: "automatic",
|
|
114
|
+
jsxDev: true
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/features/virtualOverrides.ts
|
|
122
|
+
import path from "path";
|
|
123
|
+
var virtualOverrides = /* @__PURE__ */ new Map();
|
|
124
|
+
function normalizePath(filePath) {
|
|
125
|
+
return filePath.replace(/\\/g, "/");
|
|
126
|
+
}
|
|
127
|
+
function getAbsolutePath(relativePath, root) {
|
|
128
|
+
if (path.isAbsolute(relativePath)) {
|
|
129
|
+
return normalizePath(relativePath);
|
|
130
|
+
}
|
|
131
|
+
return normalizePath(path.join(root, relativePath));
|
|
132
|
+
}
|
|
133
|
+
function findModuleByPath(server, filePath) {
|
|
134
|
+
const normalizedPath = normalizePath(filePath);
|
|
135
|
+
for (const [, mod] of server.moduleGraph.idToModuleMap) {
|
|
136
|
+
if (mod.file && normalizePath(mod.file) === normalizedPath) {
|
|
137
|
+
return mod;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const variations = [
|
|
141
|
+
normalizedPath,
|
|
142
|
+
"/" + normalizedPath,
|
|
143
|
+
normalizedPath.replace(/^\//, "")
|
|
144
|
+
];
|
|
145
|
+
for (const variation of variations) {
|
|
146
|
+
const mod = server.moduleGraph.getModuleById(variation);
|
|
147
|
+
if (mod) return mod;
|
|
148
|
+
}
|
|
149
|
+
return void 0;
|
|
150
|
+
}
|
|
151
|
+
async function invalidateModule(server, filePath) {
|
|
152
|
+
const mod = findModuleByPath(server, filePath);
|
|
153
|
+
if (mod) {
|
|
154
|
+
server.moduleGraph.invalidateModule(mod);
|
|
155
|
+
if (mod.file) {
|
|
156
|
+
server.ws.send({
|
|
157
|
+
type: "update",
|
|
158
|
+
updates: [{
|
|
159
|
+
type: "js-update",
|
|
160
|
+
path: mod.url,
|
|
161
|
+
acceptedPath: mod.url,
|
|
162
|
+
timestamp: Date.now()
|
|
163
|
+
}]
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
server.ws.send({ type: "full-reload" });
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
function createVirtualOverridesPlugin() {
|
|
172
|
+
let server;
|
|
173
|
+
let root;
|
|
174
|
+
return {
|
|
175
|
+
name: "mitway-tagger:virtual-overrides",
|
|
176
|
+
enforce: "pre",
|
|
177
|
+
configResolved(config) {
|
|
178
|
+
root = config.root;
|
|
179
|
+
},
|
|
180
|
+
configureServer(_server) {
|
|
181
|
+
server = _server;
|
|
182
|
+
server.ws.on("mitway:override", async (data) => {
|
|
183
|
+
const absolutePath = getAbsolutePath(data.path, root);
|
|
184
|
+
console.log("[mitway-tagger] Setting override for:", absolutePath);
|
|
185
|
+
virtualOverrides.set(absolutePath, data.content);
|
|
186
|
+
await invalidateModule(server, absolutePath);
|
|
187
|
+
server.ws.send("mitway:override-applied", { path: data.path });
|
|
188
|
+
});
|
|
189
|
+
server.ws.on("mitway:clear-override", async (data) => {
|
|
190
|
+
const absolutePath = getAbsolutePath(data.path, root);
|
|
191
|
+
console.log("[mitway-tagger] Clearing override for:", absolutePath);
|
|
192
|
+
virtualOverrides.delete(absolutePath);
|
|
193
|
+
await invalidateModule(server, absolutePath);
|
|
194
|
+
server.ws.send("mitway:override-cleared", { path: data.path });
|
|
195
|
+
});
|
|
196
|
+
server.ws.on("mitway:clear-all-overrides", async () => {
|
|
197
|
+
console.log("[mitway-tagger] Clearing all overrides");
|
|
198
|
+
const paths = Array.from(virtualOverrides.keys());
|
|
199
|
+
virtualOverrides.clear();
|
|
200
|
+
for (const filePath of paths) {
|
|
201
|
+
await invalidateModule(server, filePath);
|
|
202
|
+
}
|
|
203
|
+
server.ws.send("mitway:all-overrides-cleared", { count: paths.length });
|
|
204
|
+
});
|
|
205
|
+
server.ws.on("mitway:get-overrides", () => {
|
|
206
|
+
const overrides = Array.from(virtualOverrides.entries()).map(([path2, content]) => ({
|
|
207
|
+
path: path2,
|
|
208
|
+
contentLength: content.length
|
|
209
|
+
}));
|
|
210
|
+
server.ws.send("mitway:overrides-list", { overrides });
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
/**
|
|
214
|
+
* Load hook - serves overridden content if available
|
|
215
|
+
*/
|
|
216
|
+
load(id) {
|
|
217
|
+
const normalizedId = normalizePath(id);
|
|
218
|
+
if (virtualOverrides.has(normalizedId)) {
|
|
219
|
+
console.log("[mitway-tagger] Serving override for:", normalizedId);
|
|
220
|
+
return virtualOverrides.get(normalizedId);
|
|
221
|
+
}
|
|
222
|
+
const withoutSlash = normalizedId.replace(/^\//, "");
|
|
223
|
+
if (virtualOverrides.has(withoutSlash)) {
|
|
224
|
+
console.log("[mitway-tagger] Serving override for:", withoutSlash);
|
|
225
|
+
return virtualOverrides.get(withoutSlash);
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
},
|
|
229
|
+
/**
|
|
230
|
+
* Transform hook - can be used for additional processing
|
|
231
|
+
*/
|
|
232
|
+
transform(code, id) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/features/visualEditor.ts
|
|
239
|
+
var VISUAL_EDITOR_SCRIPT = `
|
|
240
|
+
<script>
|
|
241
|
+
(function() {
|
|
242
|
+
'use strict';
|
|
243
|
+
|
|
244
|
+
// State
|
|
245
|
+
let isSelecting = false;
|
|
246
|
+
let selectedElement = null;
|
|
247
|
+
let originalStyles = new Map();
|
|
248
|
+
let highlightOverlay = null;
|
|
249
|
+
let selectionOverlay = null;
|
|
250
|
+
|
|
251
|
+
// Create highlight overlay element
|
|
252
|
+
function createOverlay(id, borderColor) {
|
|
253
|
+
const overlay = document.createElement('div');
|
|
254
|
+
overlay.id = id;
|
|
255
|
+
overlay.style.cssText = \`
|
|
256
|
+
position: fixed;
|
|
257
|
+
pointer-events: none;
|
|
258
|
+
border: 2px solid \${borderColor};
|
|
259
|
+
background: \${borderColor}10;
|
|
260
|
+
z-index: 999999;
|
|
261
|
+
transition: all 0.1s ease;
|
|
262
|
+
display: none;
|
|
263
|
+
\`;
|
|
264
|
+
document.body.appendChild(overlay);
|
|
265
|
+
return overlay;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Initialize overlays
|
|
269
|
+
function initOverlays() {
|
|
270
|
+
if (!highlightOverlay) {
|
|
271
|
+
highlightOverlay = createOverlay('mitway-highlight-overlay', '#00BDFF');
|
|
272
|
+
}
|
|
273
|
+
if (!selectionOverlay) {
|
|
274
|
+
selectionOverlay = createOverlay('mitway-selection-overlay', '#00FF88');
|
|
275
|
+
selectionOverlay.style.border = '2px dashed #00FF88';
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Position overlay on element
|
|
280
|
+
function positionOverlay(overlay, element) {
|
|
281
|
+
if (!overlay || !element) return;
|
|
282
|
+
const rect = element.getBoundingClientRect();
|
|
283
|
+
overlay.style.top = rect.top + 'px';
|
|
284
|
+
overlay.style.left = rect.left + 'px';
|
|
285
|
+
overlay.style.width = rect.width + 'px';
|
|
286
|
+
overlay.style.height = rect.height + 'px';
|
|
287
|
+
overlay.style.display = 'block';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Hide overlay
|
|
291
|
+
function hideOverlay(overlay) {
|
|
292
|
+
if (overlay) {
|
|
293
|
+
overlay.style.display = 'none';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Get XPath for element
|
|
298
|
+
function getXPath(element) {
|
|
299
|
+
if (!element) return '';
|
|
300
|
+
if (element.id) return '//*[@id="' + element.id + '"]';
|
|
301
|
+
if (element === document.body) return '/html/body';
|
|
302
|
+
|
|
303
|
+
let ix = 0;
|
|
304
|
+
const siblings = element.parentNode ? element.parentNode.childNodes : [];
|
|
305
|
+
|
|
306
|
+
for (let i = 0; i < siblings.length; i++) {
|
|
307
|
+
const sibling = siblings[i];
|
|
308
|
+
if (sibling === element) {
|
|
309
|
+
const parentPath = getXPath(element.parentNode);
|
|
310
|
+
const tagName = element.tagName.toLowerCase();
|
|
311
|
+
return parentPath + '/' + tagName + '[' + (ix + 1) + ']';
|
|
312
|
+
}
|
|
313
|
+
if (sibling.nodeType === 1 && sibling.tagName === element.tagName) {
|
|
314
|
+
ix++;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return '';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Get element by XPath
|
|
321
|
+
function getElementByXPath(xpath) {
|
|
322
|
+
try {
|
|
323
|
+
const result = document.evaluate(
|
|
324
|
+
xpath,
|
|
325
|
+
document,
|
|
326
|
+
null,
|
|
327
|
+
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
|
328
|
+
null
|
|
329
|
+
);
|
|
330
|
+
return result.singleNodeValue;
|
|
331
|
+
} catch (e) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Check if element has dynamic styles (className with expressions)
|
|
337
|
+
function hasDynamicStyles(element) {
|
|
338
|
+
const classAttr = element.getAttribute('class') || '';
|
|
339
|
+
return classAttr.includes('\${') ||
|
|
340
|
+
classAttr.includes('?') ||
|
|
341
|
+
classAttr.includes('(') ||
|
|
342
|
+
element.className.includes('[object');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Get computed styles for element
|
|
346
|
+
function getElementStyles(element) {
|
|
347
|
+
const computed = getComputedStyle(element);
|
|
348
|
+
return {
|
|
349
|
+
color: computed.color,
|
|
350
|
+
backgroundColor: computed.backgroundColor,
|
|
351
|
+
fontSize: computed.fontSize,
|
|
352
|
+
fontWeight: computed.fontWeight,
|
|
353
|
+
fontStyle: computed.fontStyle,
|
|
354
|
+
textAlign: computed.textAlign,
|
|
355
|
+
marginTop: computed.marginTop,
|
|
356
|
+
marginRight: computed.marginRight,
|
|
357
|
+
marginBottom: computed.marginBottom,
|
|
358
|
+
marginLeft: computed.marginLeft,
|
|
359
|
+
paddingTop: computed.paddingTop,
|
|
360
|
+
paddingRight: computed.paddingRight,
|
|
361
|
+
paddingBottom: computed.paddingBottom,
|
|
362
|
+
paddingLeft: computed.paddingLeft,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Get source info from mitway-tagger jsxSource
|
|
367
|
+
function getSourceInfo(element) {
|
|
368
|
+
const SOURCE_SYMBOL = Symbol.for('__mitwaySource__');
|
|
369
|
+
let current = element;
|
|
370
|
+
|
|
371
|
+
// Walk up the tree to find the first element with source info
|
|
372
|
+
while (current && current !== document.body) {
|
|
373
|
+
if (current[SOURCE_SYMBOL]) {
|
|
374
|
+
return current[SOURCE_SYMBOL];
|
|
375
|
+
}
|
|
376
|
+
current = current.parentElement;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Get element data for selection
|
|
383
|
+
function getElementData(element) {
|
|
384
|
+
const rect = element.getBoundingClientRect();
|
|
385
|
+
const sourceInfo = getSourceInfo(element);
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
tagName: element.tagName,
|
|
389
|
+
textContent: element.textContent?.trim().substring(0, 500) || '',
|
|
390
|
+
className: element.className || '',
|
|
391
|
+
computedStyles: getElementStyles(element),
|
|
392
|
+
xpath: getXPath(element),
|
|
393
|
+
rect: {
|
|
394
|
+
top: rect.top,
|
|
395
|
+
left: rect.left,
|
|
396
|
+
width: rect.width,
|
|
397
|
+
height: rect.height,
|
|
398
|
+
},
|
|
399
|
+
hasDynamicStyles: hasDynamicStyles(element),
|
|
400
|
+
source: sourceInfo ? {
|
|
401
|
+
fileName: sourceInfo.fileName,
|
|
402
|
+
lineNumber: sourceInfo.lineNumber,
|
|
403
|
+
columnNumber: sourceInfo.columnNumber,
|
|
404
|
+
} : null,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Store original inline styles
|
|
409
|
+
function storeOriginalStyles(element) {
|
|
410
|
+
if (!originalStyles.has(element)) {
|
|
411
|
+
originalStyles.set(element, element.getAttribute('style') || '');
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Apply preview styles to element
|
|
416
|
+
function applyPreviewStyles(element, styles) {
|
|
417
|
+
if (!element) return;
|
|
418
|
+
storeOriginalStyles(element);
|
|
419
|
+
|
|
420
|
+
Object.entries(styles).forEach(([key, value]) => {
|
|
421
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
422
|
+
element.style[key] = value;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
if (styles.textContent !== undefined) {
|
|
427
|
+
element.textContent = styles.textContent;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (styles.classes !== undefined) {
|
|
431
|
+
element.className = styles.classes;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Restore original styles
|
|
436
|
+
function restoreOriginalStyles(element) {
|
|
437
|
+
if (!element) return;
|
|
438
|
+
const original = originalStyles.get(element);
|
|
439
|
+
if (original !== undefined) {
|
|
440
|
+
element.setAttribute('style', original);
|
|
441
|
+
originalStyles.delete(element);
|
|
442
|
+
} else {
|
|
443
|
+
element.removeAttribute('style');
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Handle mouse move during selection
|
|
448
|
+
function handleMouseMove(event) {
|
|
449
|
+
if (!isSelecting) return;
|
|
450
|
+
|
|
451
|
+
const target = event.target;
|
|
452
|
+
|
|
453
|
+
if (target.id === 'mitway-highlight-overlay' ||
|
|
454
|
+
target.id === 'mitway-selection-overlay' ||
|
|
455
|
+
target.tagName === 'SCRIPT' ||
|
|
456
|
+
target.tagName === 'STYLE' ||
|
|
457
|
+
target.tagName === 'HTML') {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
positionOverlay(highlightOverlay, target);
|
|
462
|
+
|
|
463
|
+
window.parent?.postMessage({
|
|
464
|
+
type: 'VISUAL_EDITOR_ELEMENT_HOVER',
|
|
465
|
+
payload: {
|
|
466
|
+
tagName: target.tagName,
|
|
467
|
+
className: target.className,
|
|
468
|
+
}
|
|
469
|
+
}, '*');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Handle click during selection
|
|
473
|
+
function handleClick(event) {
|
|
474
|
+
if (!isSelecting) return;
|
|
475
|
+
|
|
476
|
+
event.preventDefault();
|
|
477
|
+
event.stopPropagation();
|
|
478
|
+
|
|
479
|
+
const target = event.target;
|
|
480
|
+
|
|
481
|
+
if (target.id === 'mitway-highlight-overlay' ||
|
|
482
|
+
target.id === 'mitway-selection-overlay') {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
selectedElement = target;
|
|
487
|
+
isSelecting = false;
|
|
488
|
+
|
|
489
|
+
hideOverlay(highlightOverlay);
|
|
490
|
+
positionOverlay(selectionOverlay, target);
|
|
491
|
+
|
|
492
|
+
window.parent?.postMessage({
|
|
493
|
+
type: 'VISUAL_EDITOR_ELEMENT_SELECTED',
|
|
494
|
+
payload: getElementData(target)
|
|
495
|
+
}, '*');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Enable selector mode
|
|
499
|
+
function enableSelector() {
|
|
500
|
+
initOverlays();
|
|
501
|
+
isSelecting = true;
|
|
502
|
+
selectedElement = null;
|
|
503
|
+
hideOverlay(selectionOverlay);
|
|
504
|
+
document.body.style.cursor = 'crosshair';
|
|
505
|
+
document.addEventListener('mousemove', handleMouseMove, true);
|
|
506
|
+
document.addEventListener('click', handleClick, true);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Disable selector mode
|
|
510
|
+
function disableSelector() {
|
|
511
|
+
isSelecting = false;
|
|
512
|
+
selectedElement = null;
|
|
513
|
+
document.body.style.cursor = '';
|
|
514
|
+
document.removeEventListener('mousemove', handleMouseMove, true);
|
|
515
|
+
document.removeEventListener('click', handleClick, true);
|
|
516
|
+
hideOverlay(highlightOverlay);
|
|
517
|
+
hideOverlay(selectionOverlay);
|
|
518
|
+
|
|
519
|
+
originalStyles.forEach((_, element) => {
|
|
520
|
+
restoreOriginalStyles(element);
|
|
521
|
+
});
|
|
522
|
+
originalStyles.clear();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Listen for messages from parent (mITway-App)
|
|
526
|
+
window.addEventListener('message', (event) => {
|
|
527
|
+
if (!event.data || typeof event.data.type !== 'string') return;
|
|
528
|
+
|
|
529
|
+
const { type, payload } = event.data;
|
|
530
|
+
|
|
531
|
+
switch (type) {
|
|
532
|
+
case 'VISUAL_EDITOR_ENABLE_SELECTOR':
|
|
533
|
+
enableSelector();
|
|
534
|
+
break;
|
|
535
|
+
|
|
536
|
+
case 'VISUAL_EDITOR_DISABLE_SELECTOR':
|
|
537
|
+
disableSelector();
|
|
538
|
+
break;
|
|
539
|
+
|
|
540
|
+
case 'VISUAL_EDITOR_PREVIEW_STYLES':
|
|
541
|
+
if (payload?.xpath) {
|
|
542
|
+
const element = getElementByXPath(payload.xpath);
|
|
543
|
+
if (element) {
|
|
544
|
+
applyPreviewStyles(element, payload.styles || {});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
break;
|
|
548
|
+
|
|
549
|
+
case 'VISUAL_EDITOR_RESTORE_ORIGINAL':
|
|
550
|
+
if (payload?.xpath) {
|
|
551
|
+
const element = getElementByXPath(payload.xpath);
|
|
552
|
+
if (element) {
|
|
553
|
+
restoreOriginalStyles(element);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// Notify parent that visual editor bridge is ready
|
|
561
|
+
window.parent?.postMessage({ type: 'VISUAL_EDITOR_BRIDGE_READY' }, '*');
|
|
562
|
+
})();
|
|
563
|
+
</script>`;
|
|
564
|
+
function createVisualEditorPlugin() {
|
|
565
|
+
return {
|
|
566
|
+
name: "mitway-tagger:visual-editor",
|
|
567
|
+
transformIndexHtml(html) {
|
|
568
|
+
return html.replace("</head>", VISUAL_EDITOR_SCRIPT + "\n</head>");
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/features/iframeNavigation.ts
|
|
574
|
+
var IFRAME_NAVIGATION_SCRIPT = `
|
|
575
|
+
<script>
|
|
576
|
+
(function() {
|
|
577
|
+
'use strict';
|
|
578
|
+
|
|
579
|
+
let lastPath = window.location.pathname + window.location.search + window.location.hash;
|
|
580
|
+
|
|
581
|
+
function notifyParent() {
|
|
582
|
+
const path = window.location.pathname + window.location.search + window.location.hash;
|
|
583
|
+
|
|
584
|
+
if (path !== lastPath) {
|
|
585
|
+
lastPath = path;
|
|
586
|
+
|
|
587
|
+
try {
|
|
588
|
+
window.parent.postMessage({
|
|
589
|
+
type: 'IFRAME_NAVIGATION',
|
|
590
|
+
url: path
|
|
591
|
+
}, '*');
|
|
592
|
+
} catch (e) {
|
|
593
|
+
// Silently fail if postMessage is blocked
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Intercept history methods
|
|
599
|
+
const originalPushState = history.pushState;
|
|
600
|
+
const originalReplaceState = history.replaceState;
|
|
601
|
+
|
|
602
|
+
history.pushState = function() {
|
|
603
|
+
originalPushState.apply(this, arguments);
|
|
604
|
+
setTimeout(notifyParent, 0);
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
history.replaceState = function() {
|
|
608
|
+
originalReplaceState.apply(this, arguments);
|
|
609
|
+
setTimeout(notifyParent, 0);
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// Listen to navigation events
|
|
613
|
+
window.addEventListener('popstate', function() {
|
|
614
|
+
setTimeout(notifyParent, 0);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
document.addEventListener('click', function() {
|
|
618
|
+
setTimeout(notifyParent, 100);
|
|
619
|
+
}, true);
|
|
620
|
+
|
|
621
|
+
// Initial notification
|
|
622
|
+
setTimeout(notifyParent, 500);
|
|
623
|
+
|
|
624
|
+
// Fallback check
|
|
625
|
+
setInterval(notifyParent, 500);
|
|
626
|
+
|
|
627
|
+
// Listen for parent navigation commands
|
|
628
|
+
window.addEventListener('message', function(event) {
|
|
629
|
+
if (event.data && event.data.type === 'NAVIGATE_TO') {
|
|
630
|
+
window.history.pushState(null, '', event.data.url);
|
|
631
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
632
|
+
setTimeout(notifyParent, 0);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
})();
|
|
636
|
+
</script>`;
|
|
637
|
+
function createIframeNavigationPlugin() {
|
|
638
|
+
return {
|
|
639
|
+
name: "mitway-tagger:iframe-navigation",
|
|
640
|
+
transformIndexHtml(html) {
|
|
641
|
+
return html.replace("</body>", `${IFRAME_NAVIGATION_SCRIPT}</body>`);
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// src/features/themeBridge.ts
|
|
647
|
+
var THEME_BRIDGE_SCRIPT = `
|
|
648
|
+
<script>
|
|
649
|
+
(function() {
|
|
650
|
+
// Store original CSS variable values for restoration
|
|
651
|
+
let originalStyles = {};
|
|
652
|
+
|
|
653
|
+
// CSS variables used in the theme
|
|
654
|
+
// Base variables (without --color- prefix, used in :root)
|
|
655
|
+
const baseVars = [
|
|
656
|
+
'--background', '--foreground', '--card', '--card-foreground',
|
|
657
|
+
'--popover', '--popover-foreground', '--primary', '--primary-foreground',
|
|
658
|
+
'--secondary', '--secondary-foreground', '--muted', '--muted-foreground',
|
|
659
|
+
'--accent', '--accent-foreground', '--destructive', '--destructive-foreground',
|
|
660
|
+
'--border', '--input', '--ring',
|
|
661
|
+
'--chart-1', '--chart-2', '--chart-3', '--chart-4', '--chart-5',
|
|
662
|
+
'--sidebar', '--sidebar-foreground', '--sidebar-primary',
|
|
663
|
+
'--sidebar-primary-foreground', '--sidebar-accent', '--sidebar-accent-foreground',
|
|
664
|
+
'--sidebar-border', '--sidebar-ring'
|
|
665
|
+
];
|
|
666
|
+
|
|
667
|
+
// Radius variables (no hsl wrapper needed)
|
|
668
|
+
const radiusVars = ['--radius', '--radius-sm', '--radius-md', '--radius-lg', '--radius-xl'];
|
|
669
|
+
|
|
670
|
+
// Capture original styles on first preview
|
|
671
|
+
function captureOriginalStyles() {
|
|
672
|
+
if (Object.keys(originalStyles).length > 0) return;
|
|
673
|
+
const root = document.documentElement;
|
|
674
|
+
const computed = getComputedStyle(root);
|
|
675
|
+
[...baseVars, ...radiusVars].forEach(prop => {
|
|
676
|
+
const value = computed.getPropertyValue(prop).trim();
|
|
677
|
+
if (value) originalStyles[prop] = value;
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Check if a value is a radius (not a color)
|
|
682
|
+
function isRadiusVar(key) {
|
|
683
|
+
return key.includes('radius');
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Normalize variable name to base format (--background, --primary, etc.)
|
|
687
|
+
function normalizeVarName(key) {
|
|
688
|
+
// Remove leading dashes if present
|
|
689
|
+
let normalized = key.replace(/^-+/, '');
|
|
690
|
+
|
|
691
|
+
// Remove 'color-' prefix if present (convert from Tailwind v4 format)
|
|
692
|
+
if (normalized.startsWith('color-')) {
|
|
693
|
+
normalized = normalized.replace('color-', '');
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return '--' + normalized;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Wrap color value with hsl() if needed
|
|
700
|
+
function wrapWithHsl(value, isRadius) {
|
|
701
|
+
if (isRadius) return value;
|
|
702
|
+
|
|
703
|
+
// Already has hsl/hsla/rgb/rgba/oklch wrapper
|
|
704
|
+
if (/^(hsl|hsla|rgb|rgba|oklch)\\(/.test(value)) {
|
|
705
|
+
return value;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Wrap with hsl()
|
|
709
|
+
return 'hsl(' + value + ')';
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Apply theme colors to CSS variables
|
|
713
|
+
// Values are received WITHOUT hsl() wrapper from mITway-App
|
|
714
|
+
function applyTheme(theme) {
|
|
715
|
+
if (!theme) return;
|
|
716
|
+
const root = document.documentElement;
|
|
717
|
+
|
|
718
|
+
Object.entries(theme).forEach(([key, value]) => {
|
|
719
|
+
const cssVar = normalizeVarName(key);
|
|
720
|
+
const isRadius = isRadiusVar(key);
|
|
721
|
+
const finalValue = wrapWithHsl(value, isRadius);
|
|
722
|
+
|
|
723
|
+
// Set the base variable (--background, --primary, etc.)
|
|
724
|
+
root.style.setProperty(cssVar, finalValue);
|
|
725
|
+
|
|
726
|
+
// Also set with --color- prefix for @theme inline compatibility
|
|
727
|
+
if (!isRadius) {
|
|
728
|
+
const colorVar = '--color-' + cssVar.replace('--', '');
|
|
729
|
+
root.style.setProperty(colorVar, finalValue);
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Restore original CSS variables
|
|
735
|
+
function restoreOriginal() {
|
|
736
|
+
const root = document.documentElement;
|
|
737
|
+
Object.entries(originalStyles).forEach(([key, value]) => {
|
|
738
|
+
root.style.setProperty(key, value);
|
|
739
|
+
// Also restore --color- prefixed version
|
|
740
|
+
if (!isRadiusVar(key)) {
|
|
741
|
+
const colorVar = '--color-' + key.replace('--', '');
|
|
742
|
+
root.style.setProperty(colorVar, value);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Listen for theme messages from parent (mITway-App)
|
|
748
|
+
window.addEventListener('message', (event) => {
|
|
749
|
+
// Accept messages from any origin (mITway-App domains)
|
|
750
|
+
if (!event.data || typeof event.data.type !== 'string') return;
|
|
751
|
+
|
|
752
|
+
switch (event.data.type) {
|
|
753
|
+
case 'PREVIEW_THEME':
|
|
754
|
+
captureOriginalStyles();
|
|
755
|
+
applyTheme(event.data.theme);
|
|
756
|
+
break;
|
|
757
|
+
|
|
758
|
+
case 'RESTORE_THEME':
|
|
759
|
+
restoreOriginal();
|
|
760
|
+
break;
|
|
761
|
+
|
|
762
|
+
case 'APPLY_THEME':
|
|
763
|
+
applyTheme(event.data.theme);
|
|
764
|
+
// Notify parent that theme was applied
|
|
765
|
+
window.parent?.postMessage({
|
|
766
|
+
type: 'THEME_APPLIED',
|
|
767
|
+
themeId: event.data.themeId
|
|
768
|
+
}, '*');
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
// Notify parent that theme bridge is ready
|
|
774
|
+
window.parent?.postMessage({ type: 'THEME_BRIDGE_READY' }, '*');
|
|
775
|
+
})();
|
|
776
|
+
</script>`;
|
|
777
|
+
function createThemeBridgePlugin() {
|
|
778
|
+
return {
|
|
779
|
+
name: "mitway-tagger:theme-bridge",
|
|
780
|
+
transformIndexHtml(html) {
|
|
781
|
+
return html.replace("</head>", `${THEME_BRIDGE_SCRIPT}
|
|
782
|
+
</head>`);
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/index.ts
|
|
788
|
+
var defaultOptions = {
|
|
789
|
+
jsxSource: true,
|
|
790
|
+
virtualOverrides: true,
|
|
791
|
+
visualEditor: true,
|
|
792
|
+
iframeNavigation: true,
|
|
793
|
+
themeBridge: true,
|
|
794
|
+
extensions: [".jsx", ".tsx"],
|
|
795
|
+
exclude: ["node_modules", ".git"]
|
|
796
|
+
};
|
|
797
|
+
function mitwayTagger(options = {}) {
|
|
798
|
+
const opts = { ...defaultOptions, ...options };
|
|
799
|
+
const plugins = [];
|
|
800
|
+
if (opts.jsxSource) {
|
|
801
|
+
plugins.push(createJsxSourcePlugin(opts));
|
|
802
|
+
}
|
|
803
|
+
if (opts.virtualOverrides) {
|
|
804
|
+
plugins.push(createVirtualOverridesPlugin());
|
|
805
|
+
}
|
|
806
|
+
if (opts.visualEditor) {
|
|
807
|
+
plugins.push(createVisualEditorPlugin());
|
|
808
|
+
}
|
|
809
|
+
if (opts.iframeNavigation) {
|
|
810
|
+
plugins.push(createIframeNavigationPlugin());
|
|
811
|
+
}
|
|
812
|
+
if (opts.themeBridge) {
|
|
813
|
+
plugins.push(createThemeBridgePlugin());
|
|
814
|
+
}
|
|
815
|
+
return plugins;
|
|
816
|
+
}
|
|
817
|
+
var index_default = mitwayTagger;
|
|
818
|
+
var mitwayServerConfig = {
|
|
819
|
+
host: "0.0.0.0",
|
|
820
|
+
port: 80,
|
|
821
|
+
cors: {
|
|
822
|
+
origin: [
|
|
823
|
+
"https://app.nttmitway.com",
|
|
824
|
+
"https://app.dev.nttmitway.com",
|
|
825
|
+
"http://app.mitway.local",
|
|
826
|
+
/^http:\/\/localhost(:\d+)?$/
|
|
827
|
+
],
|
|
828
|
+
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
829
|
+
allowedHeaders: ["Content-Type", "Authorization"],
|
|
830
|
+
credentials: true
|
|
831
|
+
},
|
|
832
|
+
headers: {
|
|
833
|
+
"Content-Security-Policy": "frame-ancestors https://app.nttmitway.com https://app.dev.nttmitway.com http://app.mitway.local http://localhost:*"
|
|
834
|
+
},
|
|
835
|
+
allowedHosts: [".azurecontainerapps.io", ".nttmitway.com"]
|
|
836
|
+
};
|
|
837
|
+
function createmITwayServerConfig(options = {}) {
|
|
838
|
+
const { additionalOrigins = [], additionalHosts = [], port = 80 } = options;
|
|
839
|
+
const allOrigins = [...mitwayServerConfig.cors.origin, ...additionalOrigins];
|
|
840
|
+
const allHosts = [...mitwayServerConfig.allowedHosts, ...additionalHosts];
|
|
841
|
+
const frameAncestors = allOrigins.filter((o) => typeof o === "string").join(" ");
|
|
842
|
+
return {
|
|
843
|
+
host: "0.0.0.0",
|
|
844
|
+
port,
|
|
845
|
+
cors: {
|
|
846
|
+
...mitwayServerConfig.cors,
|
|
847
|
+
origin: allOrigins
|
|
848
|
+
},
|
|
849
|
+
headers: {
|
|
850
|
+
"Content-Security-Policy": `frame-ancestors ${frameAncestors} http://localhost:*`
|
|
851
|
+
},
|
|
852
|
+
allowedHosts: allHosts
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
export {
|
|
856
|
+
createIframeNavigationPlugin,
|
|
857
|
+
createJsxSourcePlugin,
|
|
858
|
+
createThemeBridgePlugin,
|
|
859
|
+
createVirtualOverridesPlugin,
|
|
860
|
+
createVisualEditorPlugin,
|
|
861
|
+
createmITwayServerConfig,
|
|
862
|
+
index_default as default,
|
|
863
|
+
mitwayServerConfig,
|
|
864
|
+
mitwayTagger
|
|
865
|
+
};
|