@ui-context-kit/bridge 0.2.0 → 0.2.1

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/dist/index.cjs CHANGED
@@ -890,22 +890,58 @@ var import_url = require("url");
890
890
  // src/next-server.ts
891
891
  var import_http = __toESM(require("http"), 1);
892
892
  var DEFAULT_PORT = 19638;
893
- var serverInstance = null;
893
+ var HOST = "127.0.0.1";
894
+ var MAX_RETRIES = 10;
895
+ var RETRY_DELAY_MS = 1e3;
896
+ var GLOBAL_KEY = /* @__PURE__ */ Symbol.for("__ui_context_kit_server__");
897
+ var EXIT_HANDLER_KEY = /* @__PURE__ */ Symbol.for("__ui_context_kit_exit_handler__");
898
+ function getGlobalServer() {
899
+ return globalThis[GLOBAL_KEY];
900
+ }
901
+ function setGlobalServer(state) {
902
+ globalThis[GLOBAL_KEY] = state;
903
+ }
894
904
  function startContextServer(options = {}) {
895
- if (serverInstance) return;
905
+ if (getGlobalServer()) return;
906
+ const script = process.argv[1] || "";
907
+ if (script.includes("detached-flush") || script.includes("/telemetry/")) {
908
+ return;
909
+ }
896
910
  const port = DEFAULT_PORT;
897
911
  const outputDir = options.outputDir || ".ui-context";
898
912
  const shouldCopy = options.clipboard !== false;
899
913
  const projectRoot = process.cwd();
900
- serverInstance = import_http.default.createServer((req, res) => {
914
+ if (!globalThis[EXIT_HANDLER_KEY]) {
915
+ globalThis[EXIT_HANDLER_KEY] = true;
916
+ process.on("exit", () => {
917
+ const state = getGlobalServer();
918
+ if (state) {
919
+ try {
920
+ state.server.close();
921
+ } catch {
922
+ }
923
+ setGlobalServer(null);
924
+ }
925
+ });
926
+ }
927
+ tryStart(port, outputDir, shouldCopy, projectRoot, 0);
928
+ }
929
+ function tryStart(port, outputDir, shouldCopy, projectRoot, attempt) {
930
+ if (getGlobalServer()) return;
931
+ const server = import_http.default.createServer((req, res) => {
901
932
  res.setHeader("Access-Control-Allow-Origin", "*");
902
- res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
933
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
903
934
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");
904
935
  if (req.method === "OPTIONS") {
905
936
  res.writeHead(204);
906
937
  res.end();
907
938
  return;
908
939
  }
940
+ if (req.method === "GET" && req.url === "/__health") {
941
+ res.writeHead(200, { "Content-Type": "application/json" });
942
+ res.end(JSON.stringify({ status: "ok" }));
943
+ return;
944
+ }
909
945
  if (req.method === "POST" && req.url === "/capture") {
910
946
  let body = "";
911
947
  req.on("data", (chunk) => {
@@ -940,16 +976,24 @@ function startContextServer(options = {}) {
940
976
  res.writeHead(404);
941
977
  res.end();
942
978
  });
943
- serverInstance.listen(port, () => {
944
- console.log(`[ui-context-kit] Context server running on http://localhost:${port}`);
979
+ server.listen(port, HOST, () => {
980
+ console.log(`[ui-context-kit] Context server running on http://${HOST}:${port}`);
981
+ setGlobalServer({ server, port });
945
982
  });
946
- serverInstance.on("error", (err) => {
983
+ server.on("error", (err) => {
947
984
  if (err.code === "EADDRINUSE") {
948
- console.log(`[ui-context-kit] Context server already running on port ${port}`);
949
- serverInstance = null;
985
+ if (attempt < MAX_RETRIES) {
986
+ console.log(
987
+ `[ui-context-kit] Port ${port} in use, retrying in ${RETRY_DELAY_MS}ms (${attempt + 1}/${MAX_RETRIES})...`
988
+ );
989
+ setTimeout(() => tryStart(port, outputDir, shouldCopy, projectRoot, attempt + 1), RETRY_DELAY_MS);
990
+ } else {
991
+ console.error(
992
+ `[ui-context-kit] Failed to start context server after ${MAX_RETRIES} retries. Port ${port} is still occupied.`
993
+ );
994
+ }
950
995
  } else {
951
996
  console.error("[ui-context-kit] Context server error:", err);
952
- serverInstance = null;
953
997
  }
954
998
  });
955
999
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/vite-plugin.ts","../src/context-writer.ts","../src/clipboard.ts","../src/ws-server.ts","../src/next-wrapper.ts","../src/next-server.ts"],"sourcesContent":["import { createVitePlugin } from './vite-plugin';\nimport { writeContext } from './context-writer';\nimport { withUIContext } from './next-wrapper';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Vite plugin for UI Context Kit.\n * Enables browser-based element selection and context capture for AI coding assistants.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { uiContextKit } from '@ui-context-kit/bridge'\n * export default defineConfig({\n * plugins: [react(), uiContextKit()]\n * })\n * ```\n */\nexport function uiContextKit(options?: UIContextKitOptions) {\n return createVitePlugin(options);\n}\n\nexport { writeContext, withUIContext };\nexport type { UIContextKitOptions, ElementContext, SourceLocation } from './types';\n","import type { Plugin } from 'vite';\nimport path from 'path';\nimport fs from 'fs';\nimport { injectSourceAttributes } from '@ui-context-kit/babel-plugin';\nimport { setupWebSocket } from './ws-server';\nimport type { UIContextKitOptions } from './types';\n\nconst JSX_EXTENSIONS = /\\.(jsx|tsx)$/;\nconst VIRTUAL_MODULE_ID = 'virtual:ui-context-overlay';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0' + VIRTUAL_MODULE_ID;\n\nexport function createVitePlugin(options: UIContextKitOptions = {}): Plugin[] {\n const {\n shortcut = 'alt+x',\n include,\n exclude = ['node_modules'],\n } = options;\n\n let projectRoot = '';\n\n const transformPlugin: Plugin = {\n name: 'ui-context-kit:transform',\n enforce: 'pre',\n apply: 'serve',\n\n configResolved(config) {\n projectRoot = config.root;\n },\n\n transform(code, id) {\n if (!JSX_EXTENSIONS.test(id)) return null;\n\n const relativePath = path.relative(projectRoot, id);\n if (exclude?.some(p => relativePath.includes(p))) return null;\n if (include && !include.some(p => relativePath.includes(p))) return null;\n if (id.includes('\\0') || id.includes('node_modules')) return null;\n\n const filePath = path.relative(projectRoot, id);\n\n let originalCode: string | undefined;\n try {\n originalCode = fs.readFileSync(id, 'utf-8');\n } catch {\n // ignore read errors\n }\n\n const result = injectSourceAttributes(code, filePath, originalCode);\n if (!result) return null;\n\n return { code: result.code, map: result.map };\n },\n };\n\n const overlayPlugin: Plugin = {\n name: 'ui-context-kit:overlay',\n apply: 'serve',\n\n resolveId(id) {\n if (id === VIRTUAL_MODULE_ID) return RESOLVED_VIRTUAL_MODULE_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return getOverlayCode(shortcut);\n }\n },\n\n configureServer(server) {\n setupWebSocket(server, options);\n },\n\n transformIndexHtml() {\n return [\n {\n tag: 'script',\n attrs: { type: 'module', src: `/@id/${VIRTUAL_MODULE_ID}` },\n injectTo: 'body' as const,\n },\n ];\n },\n };\n\n return [transformPlugin, overlayPlugin];\n}\n\nfunction getOverlayCode(shortcut: string): string {\n return `\n// UI Context Kit - Overlay (auto-injected in dev mode)\n(function() {\n if (typeof window === 'undefined') return;\n\n var isActive = false;\n var highlightEl = null;\n var toolbarEl = null;\n var selectedEl = null;\n var isFrozen = false;\n var frozenObservers = [];\n var multiSelected = new Set();\n\n var SHORTCUT = '${shortcut}';\n var ONBOARDING_KEY = 'ui-context-kit-onboarding-dismissed';\n\n // ---- Shadow DOM Overlay ----\n function createOverlay() {\n var host = document.createElement('div');\n host.id = 'ui-context-host';\n host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;';\n document.body.appendChild(host);\n\n var shadow = host.attachShadow({ mode: 'open' });\n shadow.innerHTML = [\n '<style>',\n '.highlight { position:fixed; border:2px solid #3b82f6; background:rgba(59,130,246,0.08); pointer-events:none; transition:all 0.15s ease; border-radius:3px; display:none; }',\n '.highlight-label { position:absolute; top:-22px; left:-2px; background:#3b82f6; color:white; font-size:11px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; padding:2px 8px; border-radius:3px 3px 0 0; white-space:nowrap; }',\n '.toolbar { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:#0f172a; border:1px solid #334155; border-radius:12px; padding:8px 12px; display:flex; gap:6px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.5); pointer-events:auto; }',\n '.toolbar-label { color:#94a3b8; font-size:12px; padding:0 4px; }',\n '.toolbar-btn { color:white; border:none; padding:6px 14px; border-radius:6px; font-size:12px; font-weight:500; cursor:pointer; white-space:nowrap; }',\n '.toolbar-btn:hover { filter:brightness(1.15); }',\n '.toolbar-btn.primary { background:#3b82f6; }',\n '.toolbar-btn.secondary { background:#475569; }',\n '.toolbar-btn.freeze { background:#475569; }',\n '.toolbar-btn.freeze.active { background:#f59e0b; color:#1e293b; }',\n '.badge { display:inline-block; background:#f59e0b; color:#1e293b; font-size:10px; font-weight:700; padding:1px 5px; border-radius:10px; margin-left:4px; }',\n '.divider { width:1px; height:20px; background:#334155; }',\n '.toast { position:fixed; bottom:72px; left:50%; transform:translateX(-50%); background:#0f172a; color:#e2e8f0; padding:10px 20px; border-radius:8px; font-size:13px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 4px 16px rgba(0,0,0,0.4); pointer-events:none; animation:toastIn 0.2s ease; }',\n '@keyframes toastIn { from { opacity:0; transform:translateX(-50%) translateY(8px); } }',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.banner-text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.banner-text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.banner-close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.banner-close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"highlight\"><div class=\"highlight-label\"></div></div>',\n '<div class=\"toolbar\" style=\"display:none\">',\n ' <span class=\"toolbar-label\">UI Context</span>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-btn\">Capture</button>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-all-btn\" style=\"display:none\">Capture All <span class=\"badge\" id=\"multi-badge\" style=\"display:none\">0</span></button>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn freeze\" id=\"freeze-btn\" title=\"Freeze DOM (Ctrl+Shift+F)\">Freeze</button>',\n ' <button class=\"toolbar-btn secondary\" id=\"close-btn\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n highlightEl = shadow.querySelector('.highlight');\n toolbarEl = shadow.querySelector('.toolbar');\n\n shadow.getElementById('close-btn').addEventListener('click', deactivate);\n shadow.getElementById('capture-btn').addEventListener('click', function() {\n if (selectedEl) captureAndSend(selectedEl);\n });\n shadow.getElementById('capture-all-btn').addEventListener('click', captureAllSelected);\n shadow.getElementById('freeze-btn').addEventListener('click', toggleFreeze);\n\n return { host: host, shadow: shadow };\n }\n\n var overlayRef = null;\n\n function showHighlight(el) {\n if (!highlightEl || !el) { highlightEl && (highlightEl.style.display = 'none'); return; }\n var rect = el.getBoundingClientRect();\n highlightEl.style.display = 'block';\n highlightEl.style.top = rect.top + 'px';\n highlightEl.style.left = rect.left + 'px';\n highlightEl.style.width = rect.width + 'px';\n highlightEl.style.height = rect.height + 'px';\n\n var label = highlightEl.querySelector('.highlight-label');\n var src = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var comp = el.getAttribute('data-source-component');\n label.textContent = src\n ? (comp || el.tagName.toLowerCase()) + ' \\\\u00b7 ' + src + ':' + line\n : el.tagName.toLowerCase();\n }\n\n function showToast(msg, duration) {\n if (!overlayRef) return;\n duration = duration || 3000;\n var t = document.createElement('div');\n t.className = 'toast';\n t.textContent = msg;\n overlayRef.shadow.appendChild(t);\n setTimeout(function() { t.remove(); }, duration);\n }\n\n // ---- React Fiber Hierarchy ----\n function getComponentHierarchy(el) {\n var fiberKey = Object.keys(el).find(function(k) {\n return k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$');\n });\n if (!fiberKey) return [];\n var hierarchy = [];\n var fiber = el[fiberKey];\n while (fiber) {\n if (typeof fiber.type === 'function') {\n var name = fiber.type.displayName || fiber.type.name;\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n } else if (typeof fiber.type === 'object' && fiber.type) {\n var name = fiber.type.displayName || (fiber.type.render && (fiber.type.render.displayName || fiber.type.render.name));\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n }\n fiber = fiber.return;\n }\n return hierarchy;\n }\n\n // ---- Computed Styles ----\n var STYLE_PROPS = [\n 'padding','padding-top','padding-right','padding-bottom','padding-left',\n 'margin','margin-top','margin-right','margin-bottom','margin-left',\n 'background-color','color','font-size','font-weight','font-family',\n 'border-radius','display','flex-direction','justify-content','align-items',\n 'gap','width','height','overflow','position','z-index','opacity',\n 'border','box-shadow','line-height','letter-spacing','text-align'\n ];\n\n function extractContext(el) {\n var computed = getComputedStyle(el);\n var rect = el.getBoundingClientRect();\n var styles = {};\n for (var i = 0; i < STYLE_PROPS.length; i++) {\n var p = STYLE_PROPS[i];\n var v = computed.getPropertyValue(p);\n if (v && v !== 'none' && v !== 'normal' && v !== 'auto' && v !== '0px' && v !== 'rgba(0, 0, 0, 0)') {\n styles[p] = v;\n }\n }\n\n var file = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var col = el.getAttribute('data-source-col');\n var component = el.getAttribute('data-source-component');\n\n return {\n source: file ? { file: file, line: parseInt(line||'0'), col: parseInt(col||'0'), component: component || undefined } : null,\n tagName: el.tagName.toLowerCase(),\n classes: el.className || '',\n computedStyles: styles,\n dimensions: { width: rect.width, height: rect.height, x: rect.x, y: rect.y },\n componentHierarchy: getComponentHierarchy(el),\n url: location.href\n };\n }\n\n // ---- Screenshot ----\n function captureScreenshot(el) {\n return new Promise(function(resolve) {\n try {\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) { resolve(undefined); return; }\n\n var canvas = document.createElement('canvas');\n var dpr = window.devicePixelRatio || 1;\n var pad = 8;\n var w = Math.ceil(rect.width + pad * 2);\n var h = Math.ceil(rect.height + pad * 2);\n canvas.width = w * dpr;\n canvas.height = h * dpr;\n\n var ctx = canvas.getContext('2d');\n if (!ctx) { resolve(undefined); return; }\n ctx.scale(dpr, dpr);\n\n // Try SVG foreignObject approach\n var clone = el.cloneNode(true);\n copyStyles(el, clone);\n\n var svgNs = 'http://www.w3.org/2000/svg';\n var svg = document.createElementNS(svgNs, 'svg');\n svg.setAttribute('width', String(w));\n svg.setAttribute('height', String(h));\n svg.setAttribute('xmlns', svgNs);\n\n var fo = document.createElementNS(svgNs, 'foreignObject');\n fo.setAttribute('width', '100%');\n fo.setAttribute('height', '100%');\n\n var container = document.createElement('div');\n container.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n container.style.cssText = 'width:' + w + 'px;height:' + h + 'px;display:flex;align-items:center;justify-content:center;background:white;padding:' + pad + 'px;box-sizing:border-box;';\n container.appendChild(clone);\n fo.appendChild(container);\n svg.appendChild(fo);\n\n var svgStr = new XMLSerializer().serializeToString(svg);\n var blob = new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' });\n var url = URL.createObjectURL(blob);\n\n var img = new Image();\n img.onload = function() {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.onerror = function() {\n URL.revokeObjectURL(url);\n // Fallback: placeholder\n ctx.fillStyle = '#f8fafc';\n ctx.fillRect(0, 0, w, h);\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 2;\n ctx.strokeRect(pad, pad, rect.width, rect.height);\n ctx.fillStyle = '#64748b';\n ctx.font = '11px -apple-system, BlinkMacSystemFont, sans-serif';\n ctx.fillText(Math.round(rect.width) + ' x ' + Math.round(rect.height) + 'px', pad + 4, pad + rect.height / 2 + 4);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.src = url;\n } catch(e) {\n resolve(undefined);\n }\n });\n }\n\n function copyStyles(src, tgt) {\n var cs = getComputedStyle(src);\n var props = ['display','width','height','padding','margin','border','border-radius','background','background-color','color','font-size','font-weight','font-family','line-height','text-align','box-shadow','opacity','flex-direction','justify-content','align-items','gap','overflow'];\n for (var i = 0; i < props.length; i++) {\n tgt.style.setProperty(props[i], cs.getPropertyValue(props[i]));\n }\n var sc = src.children, tc = tgt.children;\n for (var j = 0; j < sc.length && j < tc.length; j++) {\n if (sc[j] instanceof HTMLElement && tc[j] instanceof HTMLElement) copyStyles(sc[j], tc[j]);\n }\n }\n\n // ---- Capture & Send ----\n function captureAndSend(el) {\n showToast('Capturing context...');\n var ctx = extractContext(el);\n\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', ctx);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast('Context captured! Check .ui-context/latest.md');\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n });\n }\n\n function captureAllSelected() {\n if (multiSelected.size === 0) {\n showToast('No elements selected. Use Shift+Click to select multiple.');\n return;\n }\n showToast('Capturing ' + multiSelected.size + ' elements...');\n\n var elements = Array.from(multiSelected);\n var contexts = [];\n var pending = elements.length;\n\n elements.forEach(function(el) {\n var ctx = extractContext(el);\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n contexts.push(ctx);\n pending--;\n if (pending === 0) {\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', contexts);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast(contexts.length + ' elements captured!');\n clearMultiSelection();\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n }\n });\n });\n }\n\n function updateMultiBadge() {\n if (!overlayRef) return;\n var btn = overlayRef.shadow.getElementById('capture-all-btn');\n var badge = overlayRef.shadow.getElementById('multi-badge');\n if (multiSelected.size > 0) {\n btn.style.display = '';\n badge.style.display = '';\n badge.textContent = String(multiSelected.size);\n } else {\n btn.style.display = 'none';\n badge.style.display = 'none';\n }\n }\n\n function clearMultiSelection() {\n multiSelected.forEach(function(el) { el.style.outline = ''; });\n multiSelected.clear();\n updateMultiBadge();\n }\n\n // ---- Freeze Mode ----\n function toggleFreeze() {\n if (isFrozen) {\n unfreezeDOM();\n showToast('DOM unfrozen');\n } else {\n freezeDOM();\n showToast('DOM frozen — hover states preserved');\n }\n var btn = overlayRef && overlayRef.shadow.getElementById('freeze-btn');\n if (btn) {\n if (isFrozen) {\n btn.classList.add('active');\n btn.textContent = 'Frozen';\n } else {\n btn.classList.remove('active');\n btn.textContent = 'Freeze';\n }\n }\n }\n\n function freezeDOM() {\n isFrozen = true;\n var observer = new MutationObserver(function(mutations) {\n for (var i = 0; i < mutations.length; i++) {\n var m = mutations[i];\n for (var j = 0; j < m.addedNodes.length; j++) {\n var node = m.addedNodes[j];\n if (node instanceof HTMLElement && !(node.id && node.id.indexOf('ui-context') !== -1)) {\n node.remove();\n }\n }\n if (m.type === 'attributes' && m.target instanceof HTMLElement) {\n if (!(m.target.id && m.target.id.indexOf('ui-context') !== -1)) {\n var old = m.oldValue;\n if (old !== null && m.attributeName) {\n m.target.setAttribute(m.attributeName, old);\n }\n }\n }\n }\n });\n observer.observe(document.body, {\n childList: true, subtree: true, attributes: true, attributeOldValue: true\n });\n frozenObservers.push(observer);\n }\n\n function unfreezeDOM() {\n isFrozen = false;\n frozenObservers.forEach(function(obs) { obs.disconnect(); });\n frozenObservers = [];\n }\n\n // ---- Event Handlers ----\n function isOverlayEl(t) {\n return t.id === 'ui-context-host' || (t.closest && t.closest('#ui-context-host'));\n }\n\n function onMouseMove(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n showHighlight(t);\n }\n\n function onClick(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n e.preventDefault();\n e.stopPropagation();\n\n // Shift+Click: multi-select\n if (e.shiftKey) {\n if (multiSelected.has(t)) {\n multiSelected.delete(t);\n t.style.outline = '';\n } else {\n multiSelected.add(t);\n t.style.outline = '2px dashed #f59e0b';\n }\n updateMultiBadge();\n return;\n }\n\n selectedEl = t;\n captureAndSend(t);\n }\n\n // ---- Activate / Deactivate ----\n function activate() {\n if (isActive) return;\n isActive = true;\n overlayRef = createOverlay();\n toolbarEl.style.display = 'flex';\n document.addEventListener('mousemove', onMouseMove, true);\n document.addEventListener('click', onClick, true);\n document.body.style.cursor = 'crosshair';\n showToast('Click to capture, Shift+click for multi-select', 2000);\n }\n\n function deactivate() {\n if (!isActive) return;\n isActive = false;\n selectedEl = null;\n if (isFrozen) unfreezeDOM();\n clearMultiSelection();\n document.removeEventListener('mousemove', onMouseMove, true);\n document.removeEventListener('click', onClick, true);\n document.body.style.cursor = '';\n if (overlayRef) {\n overlayRef.host.remove();\n overlayRef = null;\n highlightEl = null;\n toolbarEl = null;\n }\n }\n\n // ---- Keyboard Shortcuts ----\n document.addEventListener('keydown', function(e) {\n var keys = SHORTCUT.split('+');\n var needsAlt = keys.indexOf('alt') !== -1;\n var needsCtrl = keys.indexOf('ctrl') !== -1;\n var needsShift = keys.indexOf('shift') !== -1;\n var mainKey = keys[keys.length - 1].toLowerCase();\n var mainCode = /^[a-z]$/.test(mainKey) ? 'Key' + mainKey.toUpperCase() : /^[0-9]$/.test(mainKey) ? 'Digit' + mainKey : '';\n\n if (e.altKey === needsAlt && e.ctrlKey === needsCtrl && e.shiftKey === needsShift && (mainCode ? e.code === mainCode : e.key.toLowerCase() === mainKey)) {\n e.preventDefault();\n isActive ? deactivate() : activate();\n return;\n }\n\n // Freeze: Ctrl+Shift+F (only when active)\n if (isActive && e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'f') {\n e.preventDefault();\n toggleFreeze();\n }\n });\n\n // ---- Onboarding Banner ----\n function showOnboarding() {\n try { if (localStorage.getItem(ONBOARDING_KEY)) return; } catch(e) { return; }\n\n // Wait for overlay ref to be set up\n var bannerHost = document.createElement('div');\n bannerHost.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483646;pointer-events:none;';\n document.body.appendChild(bannerHost);\n var bannerShadow = bannerHost.attachShadow({ mode: 'open' });\n\n var shortcutDisplay = SHORTCUT.split('+').map(function(k) { return k.charAt(0).toUpperCase() + k.slice(1); }).join(' + ');\n\n bannerShadow.innerHTML = [\n '<style>',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"banner\">',\n ' <span style=\"font-size:20px\">\\\\u{1F3AF}</span>',\n ' <span class=\"text\">',\n ' <strong>UI Context Kit</strong> is ready! Press <kbd class=\"kbd\">' + shortcutDisplay + '</kbd> to capture any UI element for AI.',\n ' <strong>Shift+Click</strong> for multi-select, <strong>Ctrl+Shift+F</strong> to freeze state.',\n ' </span>',\n ' <button class=\"close\" title=\"Dismiss\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n bannerShadow.querySelector('.close').addEventListener('click', function() {\n bannerHost.remove();\n try { localStorage.setItem(ONBOARDING_KEY, '1'); } catch(e) {}\n });\n\n setTimeout(function() {\n if (bannerHost.parentNode) {\n var banner = bannerShadow.querySelector('.banner');\n if (banner) {\n banner.style.transition = 'opacity 0.3s ease';\n banner.style.opacity = '0';\n setTimeout(function() { bannerHost.remove(); }, 300);\n }\n }\n }, 8000);\n }\n\n showOnboarding();\n console.log('[ui-context-kit] Ready. Press ' + SHORTCUT + ' to activate.');\n})();\n `;\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { ElementContext } from './types';\n\nexport async function writeContext(\n context: ElementContext | ElementContext[],\n projectRoot: string,\n outputDir: string,\n): Promise<string> {\n const outPath = path.resolve(projectRoot, outputDir);\n const capturesDir = path.join(outPath, 'captures');\n\n // Ensure output directories exist\n fs.mkdirSync(outPath, { recursive: true });\n fs.mkdirSync(path.join(outPath, 'screenshots'), { recursive: true });\n fs.mkdirSync(capturesDir, { recursive: true });\n\n const contexts = Array.isArray(context) ? context : [context];\n\n // Save screenshots if available\n const screenshotPaths: (string | undefined)[] = [];\n for (const ctx of contexts) {\n if (ctx.screenshotDataUrl) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = `capture-${timestamp}.png`;\n const relPath = `./screenshots/${filename}`;\n const base64 = ctx.screenshotDataUrl.replace(/^data:image\\/\\w+;base64,/, '');\n fs.writeFileSync(path.join(outPath, 'screenshots', filename), Buffer.from(base64, 'base64'));\n screenshotPaths.push(relPath);\n } else {\n screenshotPaths.push(undefined);\n }\n }\n\n // Read source code snippets\n const sourceSnippets: string[] = [];\n for (const ctx of contexts) {\n if (ctx.source) {\n sourceSnippets.push(readSourceSnippet(\n path.resolve(projectRoot, ctx.source.file),\n ctx.source.line,\n 10,\n ));\n } else {\n sourceSnippets.push('');\n }\n }\n\n // Build markdown\n const md = contexts.length === 1\n ? buildMarkdown(contexts[0], sourceSnippets[0], screenshotPaths[0])\n : buildMultiMarkdown(contexts, sourceSnippets, screenshotPaths);\n\n // Write latest.md\n const latestPath = path.join(outPath, 'latest.md');\n fs.writeFileSync(latestPath, md, 'utf-8');\n\n // Write timestamped capture file for history\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const capturePath = path.join(capturesDir, `${timestamp}.md`);\n fs.writeFileSync(capturePath, md, 'utf-8');\n\n // Update history.md (append summary, keep last 20 entries)\n updateHistory(outPath, contexts, timestamp);\n\n // Clean up old captures (keep last 50)\n cleanupOldCaptures(capturesDir, 50);\n\n console.log(`[ui-context-kit] Context written to ${path.relative(projectRoot, latestPath)}`);\n console.log(`[ui-context-kit] History saved to ${path.relative(projectRoot, capturePath)}`);\n\n return latestPath;\n}\n\nfunction updateHistory(outPath: string, contexts: ElementContext[], timestamp: string): void {\n const historyPath = path.join(outPath, 'history.md');\n\n // Build summary for this capture\n const lines: string[] = [];\n const time = timestamp.replace(/T/, ' ').replace(/-(\\d{2})-(\\d{2})-(\\d+)Z?$/, ':$1:$2');\n\n for (const ctx of contexts) {\n const component = ctx.source?.component || ctx.tagName;\n const file = ctx.source ? `${ctx.source.file}:${ctx.source.line}` : '';\n lines.push(`- **${component}** ${file ? `(\\`${file}\\`)` : ''} — ${ctx.url}`);\n }\n\n const entry = `### ${time}\\n${lines.join('\\n')}\\n\\n`;\n\n // Read existing history or create new\n let existing = '';\n try {\n existing = fs.readFileSync(historyPath, 'utf-8');\n } catch {\n existing = '# UI Context Capture History\\n\\n';\n }\n\n // Insert after the header line\n const headerEnd = existing.indexOf('\\n\\n');\n if (headerEnd !== -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n existing = header + entry + body;\n } else {\n existing += entry;\n }\n\n // Keep only last 20 entries\n const entries = existing.split(/(?=### )/);\n const header = entries[0]; // everything before first ###\n const captureEntries = entries.slice(1);\n if (captureEntries.length > 20) {\n existing = header + captureEntries.slice(0, 20).join('');\n }\n\n fs.writeFileSync(historyPath, existing, 'utf-8');\n}\n\nfunction cleanupOldCaptures(capturesDir: string, maxFiles: number): void {\n try {\n const files = fs.readdirSync(capturesDir)\n .filter(f => f.endsWith('.md'))\n .sort()\n .reverse();\n\n for (let i = maxFiles; i < files.length; i++) {\n fs.unlinkSync(path.join(capturesDir, files[i]));\n }\n } catch {\n // ignore cleanup errors\n }\n}\n\nfunction readSourceSnippet(filePath: string, targetLine: number, contextLines: number): string {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n const start = Math.max(0, targetLine - contextLines - 1);\n const end = Math.min(lines.length, targetLine + contextLines);\n\n return lines\n .slice(start, end)\n .map((line, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === targetLine ? ' // <-- target' : '';\n return `${line}${marker}`;\n })\n .join('\\n');\n } catch {\n return '// Source file not found';\n }\n}\n\nfunction buildMarkdown(\n context: ElementContext,\n sourceSnippet: string,\n screenshotPath?: string,\n): string {\n const { source, tagName, classes, computedStyles, dimensions, componentHierarchy, url } = context;\n\n const lines: string[] = ['# UI Context Capture', ''];\n\n // Target Element section\n lines.push('## Target Element');\n if (source) {\n if (source.component) {\n lines.push(`- **Component**: \\`${source.component}\\` in \\`${source.file}:${source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${source.file}:${source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${url}`);\n lines.push(`- **Tag**: \\`<${tagName}>\\``);\n lines.push('');\n\n // Source Code section\n if (source && sourceSnippet) {\n const startLine = Math.max(1, source.line - 10);\n const endLine = source.line + 10;\n lines.push('## Source Code');\n lines.push('```tsx');\n lines.push(`// ${source.file}:${startLine}-${endLine}`);\n lines.push(sourceSnippet);\n lines.push('```');\n lines.push('');\n }\n\n // Styling section\n lines.push('## Styling');\n if (classes) {\n lines.push(`- **Classes**: \\`${classes}\\``);\n }\n\n const styleEntries = Object.entries(computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n\n lines.push(`- **Dimensions**: ${Math.round(dimensions.width)}x${Math.round(dimensions.height)}px at (${Math.round(dimensions.x)}, ${Math.round(dimensions.y)})`);\n lines.push('');\n\n // Component Hierarchy section\n if (componentHierarchy.length > 0) {\n lines.push('## Component Hierarchy');\n lines.push(componentHierarchy.join(' > '));\n lines.push('');\n }\n\n // Screenshot section\n if (screenshotPath) {\n lines.push('## Screenshot');\n lines.push(`![element](${screenshotPath})`);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nfunction buildMultiMarkdown(\n contexts: ElementContext[],\n sourceSnippets: string[],\n screenshotPaths: (string | undefined)[],\n): string {\n const lines: string[] = [\n `# UI Context Capture (${contexts.length} elements)`,\n '',\n ];\n\n for (let i = 0; i < contexts.length; i++) {\n const ctx = contexts[i];\n const snippet = sourceSnippets[i];\n const screenshot = screenshotPaths[i];\n\n lines.push(`---`);\n lines.push('');\n lines.push(`## Element ${i + 1}: \\`<${ctx.tagName}>\\`${ctx.source?.component ? ` (${ctx.source.component})` : ''}`);\n lines.push('');\n\n if (ctx.source) {\n if (ctx.source.component) {\n lines.push(`- **Component**: \\`${ctx.source.component}\\` in \\`${ctx.source.file}:${ctx.source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${ctx.source.file}:${ctx.source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${ctx.url}`);\n lines.push('');\n\n if (ctx.source && snippet) {\n const startLine = Math.max(1, ctx.source.line - 10);\n const endLine = ctx.source.line + 10;\n lines.push('### Source Code');\n lines.push('```tsx');\n lines.push(`// ${ctx.source.file}:${startLine}-${endLine}`);\n lines.push(snippet);\n lines.push('```');\n lines.push('');\n }\n\n lines.push('### Styling');\n if (ctx.classes) {\n lines.push(`- **Classes**: \\`${ctx.classes}\\``);\n }\n const styleEntries = Object.entries(ctx.computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n lines.push(`- **Dimensions**: ${Math.round(ctx.dimensions.width)}x${Math.round(ctx.dimensions.height)}px at (${Math.round(ctx.dimensions.x)}, ${Math.round(ctx.dimensions.y)})`);\n lines.push('');\n\n if (ctx.componentHierarchy.length > 0) {\n lines.push('### Component Hierarchy');\n lines.push(ctx.componentHierarchy.join(' > '));\n lines.push('');\n }\n\n if (screenshot) {\n lines.push('### Screenshot');\n lines.push(`![element ${i + 1}](${screenshot})`);\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());\n}\n","import { exec } from 'child_process';\nimport { platform } from 'os';\n\nexport function copyToClipboard(text: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const os = platform();\n let cmd: string;\n\n if (os === 'darwin') {\n cmd = 'pbcopy';\n } else if (os === 'linux') {\n cmd = 'xclip -selection clipboard';\n } else if (os === 'win32') {\n cmd = 'clip';\n } else {\n reject(new Error(`Unsupported platform: ${os}`));\n return;\n }\n\n const proc = exec(cmd, (err) => {\n if (err) reject(err);\n else resolve();\n });\n\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n","import type { ViteDevServer } from 'vite';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { ElementContext, UIContextKitOptions } from './types';\n\nexport function setupWebSocket(server: ViteDevServer, options: UIContextKitOptions): void {\n const root = server.config.root;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n\n server.ws.on('ui-context:capture', async (data: ElementContext | ElementContext[], client) => {\n try {\n const count = Array.isArray(data) ? data.length : 1;\n console.log(`[ui-context-kit] Received context capture (${count} element${count > 1 ? 's' : ''})`);\n\n const filePath = await writeContext(data, root, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch (e) {\n console.warn('[ui-context-kit] Could not copy to clipboard:', e);\n }\n }\n\n client.send('ui-context:capture-result', { success: true });\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n client.send('ui-context:capture-result', { success: false, error: err.message });\n }\n });\n}\n","import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { startContextServer } from './next-server';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Resolves the path to the next-loader.cjs file in the dist directory.\n */\nfunction resolveLoaderPath(): string {\n try {\n // ESM: import.meta.url is available\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, 'next-loader.cjs');\n } catch {\n // CJS fallback: __dirname is available\n return path.resolve(__dirname, 'next-loader.cjs');\n }\n}\n\n/**\n * Wraps a Next.js config to add ui-context-kit source attribute injection in development.\n * Supports both webpack and Turbopack.\n *\n * Also starts a standalone HTTP context server (port 19638) so the browser overlay\n * can send captured context without Vite's HMR WebSocket.\n *\n * Usage:\n * ```js\n * // next.config.mjs\n * import { withUIContext } from '@ui-context-kit/bridge';\n * export default withUIContext({ /* your config *\\/ });\n * ```\n */\nexport function withUIContext(nextConfig: any = {}, options?: UIContextKitOptions) {\n if (process.env.NODE_ENV !== 'development') return nextConfig;\n\n // Start the standalone context server for receiving captures\n startContextServer(options);\n\n const loaderPath = resolveLoaderPath();\n\n return {\n ...nextConfig,\n\n // Turbopack support\n experimental: {\n ...nextConfig.experimental,\n turbo: {\n ...nextConfig.experimental?.turbo,\n rules: {\n ...nextConfig.experimental?.turbo?.rules,\n '*.tsx': {\n loaders: [loaderPath],\n as: '*.tsx',\n },\n '*.jsx': {\n loaders: [loaderPath],\n as: '*.jsx',\n },\n },\n },\n },\n\n // Webpack support\n webpack(config: any, context: any) {\n config.module.rules.unshift({\n test: /\\.(jsx|tsx)$/,\n exclude: /node_modules/,\n enforce: 'pre' as const,\n use: [{ loader: loaderPath }],\n });\n\n if (typeof nextConfig.webpack === 'function') {\n config = nextConfig.webpack(config, context);\n }\n\n return config;\n },\n };\n}\n","import http from 'http';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { UIContextKitOptions } from './types';\n\nconst DEFAULT_PORT = 19638;\nlet serverInstance: http.Server | null = null;\n\n/**\n * Starts a standalone HTTP server for receiving UI context captures.\n * Used by Next.js projects where Vite's HMR WebSocket is not available.\n *\n * The overlay's transport sends HTTP POST to /capture with the context data.\n */\nexport function startContextServer(options: UIContextKitOptions = {}): void {\n if (serverInstance) return;\n\n const port = DEFAULT_PORT;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n const projectRoot = process.cwd();\n\n serverInstance = http.createServer((req, res) => {\n // CORS headers for cross-port requests\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method === 'POST' && req.url === '/capture') {\n let body = '';\n req.on('data', (chunk: string) => {\n body += chunk;\n });\n req.on('end', async () => {\n try {\n const data = JSON.parse(body);\n console.log(\n `[ui-context-kit] Received context capture (${Array.isArray(data) ? data.length : 1} element${Array.isArray(data) && data.length > 1 ? 's' : ''})`,\n );\n\n const filePath = await writeContext(data, projectRoot, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch {\n // clipboard copy is best-effort\n }\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: true }));\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: false, error: err.message }));\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n serverInstance.listen(port, () => {\n console.log(`[ui-context-kit] Context server running on http://localhost:${port}`);\n });\n\n serverInstance.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`[ui-context-kit] Context server already running on port ${port}`);\n serverInstance = null;\n } else {\n console.error('[ui-context-kit] Context server error:', err);\n serverInstance = null;\n }\n });\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["import_path","import_fs","path","fs","timestamp","header","fs","path","fs","import_path","http","fs","__dirname","path"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/vite-plugin.ts","../src/context-writer.ts","../src/clipboard.ts","../src/ws-server.ts","../src/next-wrapper.ts","../src/next-server.ts"],"sourcesContent":["import { createVitePlugin } from './vite-plugin';\nimport { writeContext } from './context-writer';\nimport { withUIContext } from './next-wrapper';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Vite plugin for UI Context Kit.\n * Enables browser-based element selection and context capture for AI coding assistants.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { uiContextKit } from '@ui-context-kit/bridge'\n * export default defineConfig({\n * plugins: [react(), uiContextKit()]\n * })\n * ```\n */\nexport function uiContextKit(options?: UIContextKitOptions) {\n return createVitePlugin(options);\n}\n\nexport { writeContext, withUIContext };\nexport type { UIContextKitOptions, ElementContext, SourceLocation } from './types';\n","import type { Plugin } from 'vite';\nimport path from 'path';\nimport fs from 'fs';\nimport { injectSourceAttributes } from '@ui-context-kit/babel-plugin';\nimport { setupWebSocket } from './ws-server';\nimport type { UIContextKitOptions } from './types';\n\nconst JSX_EXTENSIONS = /\\.(jsx|tsx)$/;\nconst VIRTUAL_MODULE_ID = 'virtual:ui-context-overlay';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0' + VIRTUAL_MODULE_ID;\n\nexport function createVitePlugin(options: UIContextKitOptions = {}): Plugin[] {\n const {\n shortcut = 'alt+x',\n include,\n exclude = ['node_modules'],\n } = options;\n\n let projectRoot = '';\n\n const transformPlugin: Plugin = {\n name: 'ui-context-kit:transform',\n enforce: 'pre',\n apply: 'serve',\n\n configResolved(config) {\n projectRoot = config.root;\n },\n\n transform(code, id) {\n if (!JSX_EXTENSIONS.test(id)) return null;\n\n const relativePath = path.relative(projectRoot, id);\n if (exclude?.some(p => relativePath.includes(p))) return null;\n if (include && !include.some(p => relativePath.includes(p))) return null;\n if (id.includes('\\0') || id.includes('node_modules')) return null;\n\n const filePath = path.relative(projectRoot, id);\n\n let originalCode: string | undefined;\n try {\n originalCode = fs.readFileSync(id, 'utf-8');\n } catch {\n // ignore read errors\n }\n\n const result = injectSourceAttributes(code, filePath, originalCode);\n if (!result) return null;\n\n return { code: result.code, map: result.map };\n },\n };\n\n const overlayPlugin: Plugin = {\n name: 'ui-context-kit:overlay',\n apply: 'serve',\n\n resolveId(id) {\n if (id === VIRTUAL_MODULE_ID) return RESOLVED_VIRTUAL_MODULE_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return getOverlayCode(shortcut);\n }\n },\n\n configureServer(server) {\n setupWebSocket(server, options);\n },\n\n transformIndexHtml() {\n return [\n {\n tag: 'script',\n attrs: { type: 'module', src: `/@id/${VIRTUAL_MODULE_ID}` },\n injectTo: 'body' as const,\n },\n ];\n },\n };\n\n return [transformPlugin, overlayPlugin];\n}\n\nfunction getOverlayCode(shortcut: string): string {\n return `\n// UI Context Kit - Overlay (auto-injected in dev mode)\n(function() {\n if (typeof window === 'undefined') return;\n\n var isActive = false;\n var highlightEl = null;\n var toolbarEl = null;\n var selectedEl = null;\n var isFrozen = false;\n var frozenObservers = [];\n var multiSelected = new Set();\n\n var SHORTCUT = '${shortcut}';\n var ONBOARDING_KEY = 'ui-context-kit-onboarding-dismissed';\n\n // ---- Shadow DOM Overlay ----\n function createOverlay() {\n var host = document.createElement('div');\n host.id = 'ui-context-host';\n host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;';\n document.body.appendChild(host);\n\n var shadow = host.attachShadow({ mode: 'open' });\n shadow.innerHTML = [\n '<style>',\n '.highlight { position:fixed; border:2px solid #3b82f6; background:rgba(59,130,246,0.08); pointer-events:none; transition:all 0.15s ease; border-radius:3px; display:none; }',\n '.highlight-label { position:absolute; top:-22px; left:-2px; background:#3b82f6; color:white; font-size:11px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; padding:2px 8px; border-radius:3px 3px 0 0; white-space:nowrap; }',\n '.toolbar { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:#0f172a; border:1px solid #334155; border-radius:12px; padding:8px 12px; display:flex; gap:6px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.5); pointer-events:auto; }',\n '.toolbar-label { color:#94a3b8; font-size:12px; padding:0 4px; }',\n '.toolbar-btn { color:white; border:none; padding:6px 14px; border-radius:6px; font-size:12px; font-weight:500; cursor:pointer; white-space:nowrap; }',\n '.toolbar-btn:hover { filter:brightness(1.15); }',\n '.toolbar-btn.primary { background:#3b82f6; }',\n '.toolbar-btn.secondary { background:#475569; }',\n '.toolbar-btn.freeze { background:#475569; }',\n '.toolbar-btn.freeze.active { background:#f59e0b; color:#1e293b; }',\n '.badge { display:inline-block; background:#f59e0b; color:#1e293b; font-size:10px; font-weight:700; padding:1px 5px; border-radius:10px; margin-left:4px; }',\n '.divider { width:1px; height:20px; background:#334155; }',\n '.toast { position:fixed; bottom:72px; left:50%; transform:translateX(-50%); background:#0f172a; color:#e2e8f0; padding:10px 20px; border-radius:8px; font-size:13px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 4px 16px rgba(0,0,0,0.4); pointer-events:none; animation:toastIn 0.2s ease; }',\n '@keyframes toastIn { from { opacity:0; transform:translateX(-50%) translateY(8px); } }',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.banner-text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.banner-text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.banner-close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.banner-close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"highlight\"><div class=\"highlight-label\"></div></div>',\n '<div class=\"toolbar\" style=\"display:none\">',\n ' <span class=\"toolbar-label\">UI Context</span>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-btn\">Capture</button>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-all-btn\" style=\"display:none\">Capture All <span class=\"badge\" id=\"multi-badge\" style=\"display:none\">0</span></button>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn freeze\" id=\"freeze-btn\" title=\"Freeze DOM (Ctrl+Shift+F)\">Freeze</button>',\n ' <button class=\"toolbar-btn secondary\" id=\"close-btn\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n highlightEl = shadow.querySelector('.highlight');\n toolbarEl = shadow.querySelector('.toolbar');\n\n shadow.getElementById('close-btn').addEventListener('click', deactivate);\n shadow.getElementById('capture-btn').addEventListener('click', function() {\n if (selectedEl) captureAndSend(selectedEl);\n });\n shadow.getElementById('capture-all-btn').addEventListener('click', captureAllSelected);\n shadow.getElementById('freeze-btn').addEventListener('click', toggleFreeze);\n\n return { host: host, shadow: shadow };\n }\n\n var overlayRef = null;\n\n function showHighlight(el) {\n if (!highlightEl || !el) { highlightEl && (highlightEl.style.display = 'none'); return; }\n var rect = el.getBoundingClientRect();\n highlightEl.style.display = 'block';\n highlightEl.style.top = rect.top + 'px';\n highlightEl.style.left = rect.left + 'px';\n highlightEl.style.width = rect.width + 'px';\n highlightEl.style.height = rect.height + 'px';\n\n var label = highlightEl.querySelector('.highlight-label');\n var src = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var comp = el.getAttribute('data-source-component');\n label.textContent = src\n ? (comp || el.tagName.toLowerCase()) + ' \\\\u00b7 ' + src + ':' + line\n : el.tagName.toLowerCase();\n }\n\n function showToast(msg, duration) {\n if (!overlayRef) return;\n duration = duration || 3000;\n var t = document.createElement('div');\n t.className = 'toast';\n t.textContent = msg;\n overlayRef.shadow.appendChild(t);\n setTimeout(function() { t.remove(); }, duration);\n }\n\n // ---- React Fiber Hierarchy ----\n function getComponentHierarchy(el) {\n var fiberKey = Object.keys(el).find(function(k) {\n return k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$');\n });\n if (!fiberKey) return [];\n var hierarchy = [];\n var fiber = el[fiberKey];\n while (fiber) {\n if (typeof fiber.type === 'function') {\n var name = fiber.type.displayName || fiber.type.name;\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n } else if (typeof fiber.type === 'object' && fiber.type) {\n var name = fiber.type.displayName || (fiber.type.render && (fiber.type.render.displayName || fiber.type.render.name));\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n }\n fiber = fiber.return;\n }\n return hierarchy;\n }\n\n // ---- Computed Styles ----\n var STYLE_PROPS = [\n 'padding','padding-top','padding-right','padding-bottom','padding-left',\n 'margin','margin-top','margin-right','margin-bottom','margin-left',\n 'background-color','color','font-size','font-weight','font-family',\n 'border-radius','display','flex-direction','justify-content','align-items',\n 'gap','width','height','overflow','position','z-index','opacity',\n 'border','box-shadow','line-height','letter-spacing','text-align'\n ];\n\n function extractContext(el) {\n var computed = getComputedStyle(el);\n var rect = el.getBoundingClientRect();\n var styles = {};\n for (var i = 0; i < STYLE_PROPS.length; i++) {\n var p = STYLE_PROPS[i];\n var v = computed.getPropertyValue(p);\n if (v && v !== 'none' && v !== 'normal' && v !== 'auto' && v !== '0px' && v !== 'rgba(0, 0, 0, 0)') {\n styles[p] = v;\n }\n }\n\n var file = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var col = el.getAttribute('data-source-col');\n var component = el.getAttribute('data-source-component');\n\n return {\n source: file ? { file: file, line: parseInt(line||'0'), col: parseInt(col||'0'), component: component || undefined } : null,\n tagName: el.tagName.toLowerCase(),\n classes: el.className || '',\n computedStyles: styles,\n dimensions: { width: rect.width, height: rect.height, x: rect.x, y: rect.y },\n componentHierarchy: getComponentHierarchy(el),\n url: location.href\n };\n }\n\n // ---- Screenshot ----\n function captureScreenshot(el) {\n return new Promise(function(resolve) {\n try {\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) { resolve(undefined); return; }\n\n var canvas = document.createElement('canvas');\n var dpr = window.devicePixelRatio || 1;\n var pad = 8;\n var w = Math.ceil(rect.width + pad * 2);\n var h = Math.ceil(rect.height + pad * 2);\n canvas.width = w * dpr;\n canvas.height = h * dpr;\n\n var ctx = canvas.getContext('2d');\n if (!ctx) { resolve(undefined); return; }\n ctx.scale(dpr, dpr);\n\n // Try SVG foreignObject approach\n var clone = el.cloneNode(true);\n copyStyles(el, clone);\n\n var svgNs = 'http://www.w3.org/2000/svg';\n var svg = document.createElementNS(svgNs, 'svg');\n svg.setAttribute('width', String(w));\n svg.setAttribute('height', String(h));\n svg.setAttribute('xmlns', svgNs);\n\n var fo = document.createElementNS(svgNs, 'foreignObject');\n fo.setAttribute('width', '100%');\n fo.setAttribute('height', '100%');\n\n var container = document.createElement('div');\n container.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n container.style.cssText = 'width:' + w + 'px;height:' + h + 'px;display:flex;align-items:center;justify-content:center;background:white;padding:' + pad + 'px;box-sizing:border-box;';\n container.appendChild(clone);\n fo.appendChild(container);\n svg.appendChild(fo);\n\n var svgStr = new XMLSerializer().serializeToString(svg);\n var blob = new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' });\n var url = URL.createObjectURL(blob);\n\n var img = new Image();\n img.onload = function() {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.onerror = function() {\n URL.revokeObjectURL(url);\n // Fallback: placeholder\n ctx.fillStyle = '#f8fafc';\n ctx.fillRect(0, 0, w, h);\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 2;\n ctx.strokeRect(pad, pad, rect.width, rect.height);\n ctx.fillStyle = '#64748b';\n ctx.font = '11px -apple-system, BlinkMacSystemFont, sans-serif';\n ctx.fillText(Math.round(rect.width) + ' x ' + Math.round(rect.height) + 'px', pad + 4, pad + rect.height / 2 + 4);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.src = url;\n } catch(e) {\n resolve(undefined);\n }\n });\n }\n\n function copyStyles(src, tgt) {\n var cs = getComputedStyle(src);\n var props = ['display','width','height','padding','margin','border','border-radius','background','background-color','color','font-size','font-weight','font-family','line-height','text-align','box-shadow','opacity','flex-direction','justify-content','align-items','gap','overflow'];\n for (var i = 0; i < props.length; i++) {\n tgt.style.setProperty(props[i], cs.getPropertyValue(props[i]));\n }\n var sc = src.children, tc = tgt.children;\n for (var j = 0; j < sc.length && j < tc.length; j++) {\n if (sc[j] instanceof HTMLElement && tc[j] instanceof HTMLElement) copyStyles(sc[j], tc[j]);\n }\n }\n\n // ---- Capture & Send ----\n function captureAndSend(el) {\n showToast('Capturing context...');\n var ctx = extractContext(el);\n\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', ctx);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast('Context captured! Check .ui-context/latest.md');\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n });\n }\n\n function captureAllSelected() {\n if (multiSelected.size === 0) {\n showToast('No elements selected. Use Shift+Click to select multiple.');\n return;\n }\n showToast('Capturing ' + multiSelected.size + ' elements...');\n\n var elements = Array.from(multiSelected);\n var contexts = [];\n var pending = elements.length;\n\n elements.forEach(function(el) {\n var ctx = extractContext(el);\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n contexts.push(ctx);\n pending--;\n if (pending === 0) {\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', contexts);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast(contexts.length + ' elements captured!');\n clearMultiSelection();\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n }\n });\n });\n }\n\n function updateMultiBadge() {\n if (!overlayRef) return;\n var btn = overlayRef.shadow.getElementById('capture-all-btn');\n var badge = overlayRef.shadow.getElementById('multi-badge');\n if (multiSelected.size > 0) {\n btn.style.display = '';\n badge.style.display = '';\n badge.textContent = String(multiSelected.size);\n } else {\n btn.style.display = 'none';\n badge.style.display = 'none';\n }\n }\n\n function clearMultiSelection() {\n multiSelected.forEach(function(el) { el.style.outline = ''; });\n multiSelected.clear();\n updateMultiBadge();\n }\n\n // ---- Freeze Mode ----\n function toggleFreeze() {\n if (isFrozen) {\n unfreezeDOM();\n showToast('DOM unfrozen');\n } else {\n freezeDOM();\n showToast('DOM frozen — hover states preserved');\n }\n var btn = overlayRef && overlayRef.shadow.getElementById('freeze-btn');\n if (btn) {\n if (isFrozen) {\n btn.classList.add('active');\n btn.textContent = 'Frozen';\n } else {\n btn.classList.remove('active');\n btn.textContent = 'Freeze';\n }\n }\n }\n\n function freezeDOM() {\n isFrozen = true;\n var observer = new MutationObserver(function(mutations) {\n for (var i = 0; i < mutations.length; i++) {\n var m = mutations[i];\n for (var j = 0; j < m.addedNodes.length; j++) {\n var node = m.addedNodes[j];\n if (node instanceof HTMLElement && !(node.id && node.id.indexOf('ui-context') !== -1)) {\n node.remove();\n }\n }\n if (m.type === 'attributes' && m.target instanceof HTMLElement) {\n if (!(m.target.id && m.target.id.indexOf('ui-context') !== -1)) {\n var old = m.oldValue;\n if (old !== null && m.attributeName) {\n m.target.setAttribute(m.attributeName, old);\n }\n }\n }\n }\n });\n observer.observe(document.body, {\n childList: true, subtree: true, attributes: true, attributeOldValue: true\n });\n frozenObservers.push(observer);\n }\n\n function unfreezeDOM() {\n isFrozen = false;\n frozenObservers.forEach(function(obs) { obs.disconnect(); });\n frozenObservers = [];\n }\n\n // ---- Event Handlers ----\n function isOverlayEl(t) {\n return t.id === 'ui-context-host' || (t.closest && t.closest('#ui-context-host'));\n }\n\n function onMouseMove(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n showHighlight(t);\n }\n\n function onClick(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n e.preventDefault();\n e.stopPropagation();\n\n // Shift+Click: multi-select\n if (e.shiftKey) {\n if (multiSelected.has(t)) {\n multiSelected.delete(t);\n t.style.outline = '';\n } else {\n multiSelected.add(t);\n t.style.outline = '2px dashed #f59e0b';\n }\n updateMultiBadge();\n return;\n }\n\n selectedEl = t;\n captureAndSend(t);\n }\n\n // ---- Activate / Deactivate ----\n function activate() {\n if (isActive) return;\n isActive = true;\n overlayRef = createOverlay();\n toolbarEl.style.display = 'flex';\n document.addEventListener('mousemove', onMouseMove, true);\n document.addEventListener('click', onClick, true);\n document.body.style.cursor = 'crosshair';\n showToast('Click to capture, Shift+click for multi-select', 2000);\n }\n\n function deactivate() {\n if (!isActive) return;\n isActive = false;\n selectedEl = null;\n if (isFrozen) unfreezeDOM();\n clearMultiSelection();\n document.removeEventListener('mousemove', onMouseMove, true);\n document.removeEventListener('click', onClick, true);\n document.body.style.cursor = '';\n if (overlayRef) {\n overlayRef.host.remove();\n overlayRef = null;\n highlightEl = null;\n toolbarEl = null;\n }\n }\n\n // ---- Keyboard Shortcuts ----\n document.addEventListener('keydown', function(e) {\n var keys = SHORTCUT.split('+');\n var needsAlt = keys.indexOf('alt') !== -1;\n var needsCtrl = keys.indexOf('ctrl') !== -1;\n var needsShift = keys.indexOf('shift') !== -1;\n var mainKey = keys[keys.length - 1].toLowerCase();\n var mainCode = /^[a-z]$/.test(mainKey) ? 'Key' + mainKey.toUpperCase() : /^[0-9]$/.test(mainKey) ? 'Digit' + mainKey : '';\n\n if (e.altKey === needsAlt && e.ctrlKey === needsCtrl && e.shiftKey === needsShift && (mainCode ? e.code === mainCode : e.key.toLowerCase() === mainKey)) {\n e.preventDefault();\n isActive ? deactivate() : activate();\n return;\n }\n\n // Freeze: Ctrl+Shift+F (only when active)\n if (isActive && e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'f') {\n e.preventDefault();\n toggleFreeze();\n }\n });\n\n // ---- Onboarding Banner ----\n function showOnboarding() {\n try { if (localStorage.getItem(ONBOARDING_KEY)) return; } catch(e) { return; }\n\n // Wait for overlay ref to be set up\n var bannerHost = document.createElement('div');\n bannerHost.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483646;pointer-events:none;';\n document.body.appendChild(bannerHost);\n var bannerShadow = bannerHost.attachShadow({ mode: 'open' });\n\n var shortcutDisplay = SHORTCUT.split('+').map(function(k) { return k.charAt(0).toUpperCase() + k.slice(1); }).join(' + ');\n\n bannerShadow.innerHTML = [\n '<style>',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"banner\">',\n ' <span style=\"font-size:20px\">\\\\u{1F3AF}</span>',\n ' <span class=\"text\">',\n ' <strong>UI Context Kit</strong> is ready! Press <kbd class=\"kbd\">' + shortcutDisplay + '</kbd> to capture any UI element for AI.',\n ' <strong>Shift+Click</strong> for multi-select, <strong>Ctrl+Shift+F</strong> to freeze state.',\n ' </span>',\n ' <button class=\"close\" title=\"Dismiss\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n bannerShadow.querySelector('.close').addEventListener('click', function() {\n bannerHost.remove();\n try { localStorage.setItem(ONBOARDING_KEY, '1'); } catch(e) {}\n });\n\n setTimeout(function() {\n if (bannerHost.parentNode) {\n var banner = bannerShadow.querySelector('.banner');\n if (banner) {\n banner.style.transition = 'opacity 0.3s ease';\n banner.style.opacity = '0';\n setTimeout(function() { bannerHost.remove(); }, 300);\n }\n }\n }, 8000);\n }\n\n showOnboarding();\n console.log('[ui-context-kit] Ready. Press ' + SHORTCUT + ' to activate.');\n})();\n `;\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { ElementContext } from './types';\n\nexport async function writeContext(\n context: ElementContext | ElementContext[],\n projectRoot: string,\n outputDir: string,\n): Promise<string> {\n const outPath = path.resolve(projectRoot, outputDir);\n const capturesDir = path.join(outPath, 'captures');\n\n // Ensure output directories exist\n fs.mkdirSync(outPath, { recursive: true });\n fs.mkdirSync(path.join(outPath, 'screenshots'), { recursive: true });\n fs.mkdirSync(capturesDir, { recursive: true });\n\n const contexts = Array.isArray(context) ? context : [context];\n\n // Save screenshots if available\n const screenshotPaths: (string | undefined)[] = [];\n for (const ctx of contexts) {\n if (ctx.screenshotDataUrl) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = `capture-${timestamp}.png`;\n const relPath = `./screenshots/${filename}`;\n const base64 = ctx.screenshotDataUrl.replace(/^data:image\\/\\w+;base64,/, '');\n fs.writeFileSync(path.join(outPath, 'screenshots', filename), Buffer.from(base64, 'base64'));\n screenshotPaths.push(relPath);\n } else {\n screenshotPaths.push(undefined);\n }\n }\n\n // Read source code snippets\n const sourceSnippets: string[] = [];\n for (const ctx of contexts) {\n if (ctx.source) {\n sourceSnippets.push(readSourceSnippet(\n path.resolve(projectRoot, ctx.source.file),\n ctx.source.line,\n 10,\n ));\n } else {\n sourceSnippets.push('');\n }\n }\n\n // Build markdown\n const md = contexts.length === 1\n ? buildMarkdown(contexts[0], sourceSnippets[0], screenshotPaths[0])\n : buildMultiMarkdown(contexts, sourceSnippets, screenshotPaths);\n\n // Write latest.md\n const latestPath = path.join(outPath, 'latest.md');\n fs.writeFileSync(latestPath, md, 'utf-8');\n\n // Write timestamped capture file for history\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const capturePath = path.join(capturesDir, `${timestamp}.md`);\n fs.writeFileSync(capturePath, md, 'utf-8');\n\n // Update history.md (append summary, keep last 20 entries)\n updateHistory(outPath, contexts, timestamp);\n\n // Clean up old captures (keep last 50)\n cleanupOldCaptures(capturesDir, 50);\n\n console.log(`[ui-context-kit] Context written to ${path.relative(projectRoot, latestPath)}`);\n console.log(`[ui-context-kit] History saved to ${path.relative(projectRoot, capturePath)}`);\n\n return latestPath;\n}\n\nfunction updateHistory(outPath: string, contexts: ElementContext[], timestamp: string): void {\n const historyPath = path.join(outPath, 'history.md');\n\n // Build summary for this capture\n const lines: string[] = [];\n const time = timestamp.replace(/T/, ' ').replace(/-(\\d{2})-(\\d{2})-(\\d+)Z?$/, ':$1:$2');\n\n for (const ctx of contexts) {\n const component = ctx.source?.component || ctx.tagName;\n const file = ctx.source ? `${ctx.source.file}:${ctx.source.line}` : '';\n lines.push(`- **${component}** ${file ? `(\\`${file}\\`)` : ''} — ${ctx.url}`);\n }\n\n const entry = `### ${time}\\n${lines.join('\\n')}\\n\\n`;\n\n // Read existing history or create new\n let existing = '';\n try {\n existing = fs.readFileSync(historyPath, 'utf-8');\n } catch {\n existing = '# UI Context Capture History\\n\\n';\n }\n\n // Insert after the header line\n const headerEnd = existing.indexOf('\\n\\n');\n if (headerEnd !== -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n existing = header + entry + body;\n } else {\n existing += entry;\n }\n\n // Keep only last 20 entries\n const entries = existing.split(/(?=### )/);\n const header = entries[0]; // everything before first ###\n const captureEntries = entries.slice(1);\n if (captureEntries.length > 20) {\n existing = header + captureEntries.slice(0, 20).join('');\n }\n\n fs.writeFileSync(historyPath, existing, 'utf-8');\n}\n\nfunction cleanupOldCaptures(capturesDir: string, maxFiles: number): void {\n try {\n const files = fs.readdirSync(capturesDir)\n .filter(f => f.endsWith('.md'))\n .sort()\n .reverse();\n\n for (let i = maxFiles; i < files.length; i++) {\n fs.unlinkSync(path.join(capturesDir, files[i]));\n }\n } catch {\n // ignore cleanup errors\n }\n}\n\nfunction readSourceSnippet(filePath: string, targetLine: number, contextLines: number): string {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n const start = Math.max(0, targetLine - contextLines - 1);\n const end = Math.min(lines.length, targetLine + contextLines);\n\n return lines\n .slice(start, end)\n .map((line, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === targetLine ? ' // <-- target' : '';\n return `${line}${marker}`;\n })\n .join('\\n');\n } catch {\n return '// Source file not found';\n }\n}\n\nfunction buildMarkdown(\n context: ElementContext,\n sourceSnippet: string,\n screenshotPath?: string,\n): string {\n const { source, tagName, classes, computedStyles, dimensions, componentHierarchy, url } = context;\n\n const lines: string[] = ['# UI Context Capture', ''];\n\n // Target Element section\n lines.push('## Target Element');\n if (source) {\n if (source.component) {\n lines.push(`- **Component**: \\`${source.component}\\` in \\`${source.file}:${source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${source.file}:${source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${url}`);\n lines.push(`- **Tag**: \\`<${tagName}>\\``);\n lines.push('');\n\n // Source Code section\n if (source && sourceSnippet) {\n const startLine = Math.max(1, source.line - 10);\n const endLine = source.line + 10;\n lines.push('## Source Code');\n lines.push('```tsx');\n lines.push(`// ${source.file}:${startLine}-${endLine}`);\n lines.push(sourceSnippet);\n lines.push('```');\n lines.push('');\n }\n\n // Styling section\n lines.push('## Styling');\n if (classes) {\n lines.push(`- **Classes**: \\`${classes}\\``);\n }\n\n const styleEntries = Object.entries(computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n\n lines.push(`- **Dimensions**: ${Math.round(dimensions.width)}x${Math.round(dimensions.height)}px at (${Math.round(dimensions.x)}, ${Math.round(dimensions.y)})`);\n lines.push('');\n\n // Component Hierarchy section\n if (componentHierarchy.length > 0) {\n lines.push('## Component Hierarchy');\n lines.push(componentHierarchy.join(' > '));\n lines.push('');\n }\n\n // Screenshot section\n if (screenshotPath) {\n lines.push('## Screenshot');\n lines.push(`![element](${screenshotPath})`);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nfunction buildMultiMarkdown(\n contexts: ElementContext[],\n sourceSnippets: string[],\n screenshotPaths: (string | undefined)[],\n): string {\n const lines: string[] = [\n `# UI Context Capture (${contexts.length} elements)`,\n '',\n ];\n\n for (let i = 0; i < contexts.length; i++) {\n const ctx = contexts[i];\n const snippet = sourceSnippets[i];\n const screenshot = screenshotPaths[i];\n\n lines.push(`---`);\n lines.push('');\n lines.push(`## Element ${i + 1}: \\`<${ctx.tagName}>\\`${ctx.source?.component ? ` (${ctx.source.component})` : ''}`);\n lines.push('');\n\n if (ctx.source) {\n if (ctx.source.component) {\n lines.push(`- **Component**: \\`${ctx.source.component}\\` in \\`${ctx.source.file}:${ctx.source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${ctx.source.file}:${ctx.source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${ctx.url}`);\n lines.push('');\n\n if (ctx.source && snippet) {\n const startLine = Math.max(1, ctx.source.line - 10);\n const endLine = ctx.source.line + 10;\n lines.push('### Source Code');\n lines.push('```tsx');\n lines.push(`// ${ctx.source.file}:${startLine}-${endLine}`);\n lines.push(snippet);\n lines.push('```');\n lines.push('');\n }\n\n lines.push('### Styling');\n if (ctx.classes) {\n lines.push(`- **Classes**: \\`${ctx.classes}\\``);\n }\n const styleEntries = Object.entries(ctx.computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n lines.push(`- **Dimensions**: ${Math.round(ctx.dimensions.width)}x${Math.round(ctx.dimensions.height)}px at (${Math.round(ctx.dimensions.x)}, ${Math.round(ctx.dimensions.y)})`);\n lines.push('');\n\n if (ctx.componentHierarchy.length > 0) {\n lines.push('### Component Hierarchy');\n lines.push(ctx.componentHierarchy.join(' > '));\n lines.push('');\n }\n\n if (screenshot) {\n lines.push('### Screenshot');\n lines.push(`![element ${i + 1}](${screenshot})`);\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());\n}\n","import { exec } from 'child_process';\nimport { platform } from 'os';\n\nexport function copyToClipboard(text: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const os = platform();\n let cmd: string;\n\n if (os === 'darwin') {\n cmd = 'pbcopy';\n } else if (os === 'linux') {\n cmd = 'xclip -selection clipboard';\n } else if (os === 'win32') {\n cmd = 'clip';\n } else {\n reject(new Error(`Unsupported platform: ${os}`));\n return;\n }\n\n const proc = exec(cmd, (err) => {\n if (err) reject(err);\n else resolve();\n });\n\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n","import type { ViteDevServer } from 'vite';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { ElementContext, UIContextKitOptions } from './types';\n\nexport function setupWebSocket(server: ViteDevServer, options: UIContextKitOptions): void {\n const root = server.config.root;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n\n server.ws.on('ui-context:capture', async (data: ElementContext | ElementContext[], client) => {\n try {\n const count = Array.isArray(data) ? data.length : 1;\n console.log(`[ui-context-kit] Received context capture (${count} element${count > 1 ? 's' : ''})`);\n\n const filePath = await writeContext(data, root, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch (e) {\n console.warn('[ui-context-kit] Could not copy to clipboard:', e);\n }\n }\n\n client.send('ui-context:capture-result', { success: true });\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n client.send('ui-context:capture-result', { success: false, error: err.message });\n }\n });\n}\n","import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { startContextServer } from './next-server';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Resolves the path to the next-loader.cjs file in the dist directory.\n */\nfunction resolveLoaderPath(): string {\n try {\n // ESM: import.meta.url is available\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, 'next-loader.cjs');\n } catch {\n // CJS fallback: __dirname is available\n return path.resolve(__dirname, 'next-loader.cjs');\n }\n}\n\n/**\n * Wraps a Next.js config to add ui-context-kit source attribute injection in development.\n * Supports both webpack and Turbopack.\n *\n * Also starts a standalone HTTP context server (port 19638) so the browser overlay\n * can send captured context without Vite's HMR WebSocket.\n *\n * Usage:\n * ```js\n * // next.config.mjs\n * import { withUIContext } from '@ui-context-kit/bridge';\n * export default withUIContext({ /* your config *\\/ });\n * ```\n */\nexport function withUIContext(nextConfig: any = {}, options?: UIContextKitOptions) {\n if (process.env.NODE_ENV !== 'development') return nextConfig;\n\n // Start the standalone context server for receiving captures\n startContextServer(options);\n\n const loaderPath = resolveLoaderPath();\n\n return {\n ...nextConfig,\n\n // Turbopack support\n experimental: {\n ...nextConfig.experimental,\n turbo: {\n ...nextConfig.experimental?.turbo,\n rules: {\n ...nextConfig.experimental?.turbo?.rules,\n '*.tsx': {\n loaders: [loaderPath],\n as: '*.tsx',\n },\n '*.jsx': {\n loaders: [loaderPath],\n as: '*.jsx',\n },\n },\n },\n },\n\n // Webpack support\n webpack(config: any, context: any) {\n config.module.rules.unshift({\n test: /\\.(jsx|tsx)$/,\n exclude: /node_modules/,\n enforce: 'pre' as const,\n use: [{ loader: loaderPath }],\n });\n\n if (typeof nextConfig.webpack === 'function') {\n config = nextConfig.webpack(config, context);\n }\n\n return config;\n },\n };\n}\n","import http from 'http';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { UIContextKitOptions } from './types';\n\nconst DEFAULT_PORT = 19638;\nconst HOST = '127.0.0.1';\nconst MAX_RETRIES = 10;\nconst RETRY_DELAY_MS = 1000;\n\nconst GLOBAL_KEY = Symbol.for('__ui_context_kit_server__');\nconst EXIT_HANDLER_KEY = Symbol.for('__ui_context_kit_exit_handler__');\n\ninterface ServerState {\n server: http.Server;\n port: number;\n}\n\nfunction getGlobalServer(): ServerState | undefined {\n return (globalThis as any)[GLOBAL_KEY];\n}\n\nfunction setGlobalServer(state: ServerState | null): void {\n (globalThis as any)[GLOBAL_KEY] = state;\n}\n\n/**\n * Starts a standalone HTTP server for receiving UI context captures.\n * Used by Next.js projects where Vite's HMR WebSocket is not available.\n *\n * Uses globalThis singleton to survive module re-evaluation by requireFromString().\n * On EADDRINUSE (stale socket from previous dev server), retries up to 10 times\n * with 1-second intervals.\n */\nexport function startContextServer(options: UIContextKitOptions = {}): void {\n // Check globalThis singleton first — survives requireFromString() re-evaluation\n if (getGlobalServer()) return;\n\n // Don't start in Next.js helper processes (telemetry flush, etc.)\n // These are short-lived detached processes that also load next.config.ts.\n // If they grab the port, the actual dev server can't bind and the server\n // dies when the helper exits.\n const script = process.argv[1] || '';\n if (script.includes('detached-flush') || script.includes('/telemetry/')) {\n return;\n }\n\n const port = DEFAULT_PORT;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n const projectRoot = process.cwd();\n\n // Register exit handler once per process\n if (!(globalThis as any)[EXIT_HANDLER_KEY]) {\n (globalThis as any)[EXIT_HANDLER_KEY] = true;\n process.on('exit', () => {\n const state = getGlobalServer();\n if (state) {\n try {\n state.server.close();\n } catch {\n // best-effort cleanup\n }\n setGlobalServer(null);\n }\n });\n }\n\n tryStart(port, outputDir, shouldCopy, projectRoot, 0);\n}\n\nfunction tryStart(\n port: number,\n outputDir: string,\n shouldCopy: boolean,\n projectRoot: string,\n attempt: number,\n): void {\n // Re-check globalThis in case another call succeeded while we were waiting\n if (getGlobalServer()) return;\n\n const server = http.createServer((req, res) => {\n // CORS headers for cross-port requests\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n // Health check endpoint\n if (req.method === 'GET' && req.url === '/__health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n\n if (req.method === 'POST' && req.url === '/capture') {\n let body = '';\n req.on('data', (chunk: string) => {\n body += chunk;\n });\n req.on('end', async () => {\n try {\n const data = JSON.parse(body);\n console.log(\n `[ui-context-kit] Received context capture (${Array.isArray(data) ? data.length : 1} element${Array.isArray(data) && data.length > 1 ? 's' : ''})`,\n );\n\n const filePath = await writeContext(data, projectRoot, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch {\n // clipboard copy is best-effort\n }\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: true }));\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: false, error: err.message }));\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n server.listen(port, HOST, () => {\n console.log(`[ui-context-kit] Context server running on http://${HOST}:${port}`);\n setGlobalServer({ server, port });\n });\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n if (attempt < MAX_RETRIES) {\n console.log(\n `[ui-context-kit] Port ${port} in use, retrying in ${RETRY_DELAY_MS}ms (${attempt + 1}/${MAX_RETRIES})...`,\n );\n setTimeout(() => tryStart(port, outputDir, shouldCopy, projectRoot, attempt + 1), RETRY_DELAY_MS);\n } else {\n console.error(\n `[ui-context-kit] Failed to start context server after ${MAX_RETRIES} retries. Port ${port} is still occupied.`,\n );\n }\n } else {\n console.error('[ui-context-kit] Context server error:', err);\n }\n });\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["import_path","import_fs","path","fs","timestamp","header","fs","path","fs","import_path","http","fs","__dirname","path"]}
package/dist/index.js CHANGED
@@ -852,22 +852,58 @@ import { fileURLToPath } from "url";
852
852
  // src/next-server.ts
853
853
  import http from "http";
854
854
  var DEFAULT_PORT = 19638;
855
- var serverInstance = null;
855
+ var HOST = "127.0.0.1";
856
+ var MAX_RETRIES = 10;
857
+ var RETRY_DELAY_MS = 1e3;
858
+ var GLOBAL_KEY = /* @__PURE__ */ Symbol.for("__ui_context_kit_server__");
859
+ var EXIT_HANDLER_KEY = /* @__PURE__ */ Symbol.for("__ui_context_kit_exit_handler__");
860
+ function getGlobalServer() {
861
+ return globalThis[GLOBAL_KEY];
862
+ }
863
+ function setGlobalServer(state) {
864
+ globalThis[GLOBAL_KEY] = state;
865
+ }
856
866
  function startContextServer(options = {}) {
857
- if (serverInstance) return;
867
+ if (getGlobalServer()) return;
868
+ const script = process.argv[1] || "";
869
+ if (script.includes("detached-flush") || script.includes("/telemetry/")) {
870
+ return;
871
+ }
858
872
  const port = DEFAULT_PORT;
859
873
  const outputDir = options.outputDir || ".ui-context";
860
874
  const shouldCopy = options.clipboard !== false;
861
875
  const projectRoot = process.cwd();
862
- serverInstance = http.createServer((req, res) => {
876
+ if (!globalThis[EXIT_HANDLER_KEY]) {
877
+ globalThis[EXIT_HANDLER_KEY] = true;
878
+ process.on("exit", () => {
879
+ const state = getGlobalServer();
880
+ if (state) {
881
+ try {
882
+ state.server.close();
883
+ } catch {
884
+ }
885
+ setGlobalServer(null);
886
+ }
887
+ });
888
+ }
889
+ tryStart(port, outputDir, shouldCopy, projectRoot, 0);
890
+ }
891
+ function tryStart(port, outputDir, shouldCopy, projectRoot, attempt) {
892
+ if (getGlobalServer()) return;
893
+ const server = http.createServer((req, res) => {
863
894
  res.setHeader("Access-Control-Allow-Origin", "*");
864
- res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
895
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
865
896
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");
866
897
  if (req.method === "OPTIONS") {
867
898
  res.writeHead(204);
868
899
  res.end();
869
900
  return;
870
901
  }
902
+ if (req.method === "GET" && req.url === "/__health") {
903
+ res.writeHead(200, { "Content-Type": "application/json" });
904
+ res.end(JSON.stringify({ status: "ok" }));
905
+ return;
906
+ }
871
907
  if (req.method === "POST" && req.url === "/capture") {
872
908
  let body = "";
873
909
  req.on("data", (chunk) => {
@@ -902,16 +938,24 @@ function startContextServer(options = {}) {
902
938
  res.writeHead(404);
903
939
  res.end();
904
940
  });
905
- serverInstance.listen(port, () => {
906
- console.log(`[ui-context-kit] Context server running on http://localhost:${port}`);
941
+ server.listen(port, HOST, () => {
942
+ console.log(`[ui-context-kit] Context server running on http://${HOST}:${port}`);
943
+ setGlobalServer({ server, port });
907
944
  });
908
- serverInstance.on("error", (err) => {
945
+ server.on("error", (err) => {
909
946
  if (err.code === "EADDRINUSE") {
910
- console.log(`[ui-context-kit] Context server already running on port ${port}`);
911
- serverInstance = null;
947
+ if (attempt < MAX_RETRIES) {
948
+ console.log(
949
+ `[ui-context-kit] Port ${port} in use, retrying in ${RETRY_DELAY_MS}ms (${attempt + 1}/${MAX_RETRIES})...`
950
+ );
951
+ setTimeout(() => tryStart(port, outputDir, shouldCopy, projectRoot, attempt + 1), RETRY_DELAY_MS);
952
+ } else {
953
+ console.error(
954
+ `[ui-context-kit] Failed to start context server after ${MAX_RETRIES} retries. Port ${port} is still occupied.`
955
+ );
956
+ }
912
957
  } else {
913
958
  console.error("[ui-context-kit] Context server error:", err);
914
- serverInstance = null;
915
959
  }
916
960
  });
917
961
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vite-plugin.ts","../src/context-writer.ts","../src/clipboard.ts","../src/ws-server.ts","../src/next-wrapper.ts","../src/next-server.ts","../src/index.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport path from 'path';\nimport fs from 'fs';\nimport { injectSourceAttributes } from '@ui-context-kit/babel-plugin';\nimport { setupWebSocket } from './ws-server';\nimport type { UIContextKitOptions } from './types';\n\nconst JSX_EXTENSIONS = /\\.(jsx|tsx)$/;\nconst VIRTUAL_MODULE_ID = 'virtual:ui-context-overlay';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0' + VIRTUAL_MODULE_ID;\n\nexport function createVitePlugin(options: UIContextKitOptions = {}): Plugin[] {\n const {\n shortcut = 'alt+x',\n include,\n exclude = ['node_modules'],\n } = options;\n\n let projectRoot = '';\n\n const transformPlugin: Plugin = {\n name: 'ui-context-kit:transform',\n enforce: 'pre',\n apply: 'serve',\n\n configResolved(config) {\n projectRoot = config.root;\n },\n\n transform(code, id) {\n if (!JSX_EXTENSIONS.test(id)) return null;\n\n const relativePath = path.relative(projectRoot, id);\n if (exclude?.some(p => relativePath.includes(p))) return null;\n if (include && !include.some(p => relativePath.includes(p))) return null;\n if (id.includes('\\0') || id.includes('node_modules')) return null;\n\n const filePath = path.relative(projectRoot, id);\n\n let originalCode: string | undefined;\n try {\n originalCode = fs.readFileSync(id, 'utf-8');\n } catch {\n // ignore read errors\n }\n\n const result = injectSourceAttributes(code, filePath, originalCode);\n if (!result) return null;\n\n return { code: result.code, map: result.map };\n },\n };\n\n const overlayPlugin: Plugin = {\n name: 'ui-context-kit:overlay',\n apply: 'serve',\n\n resolveId(id) {\n if (id === VIRTUAL_MODULE_ID) return RESOLVED_VIRTUAL_MODULE_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return getOverlayCode(shortcut);\n }\n },\n\n configureServer(server) {\n setupWebSocket(server, options);\n },\n\n transformIndexHtml() {\n return [\n {\n tag: 'script',\n attrs: { type: 'module', src: `/@id/${VIRTUAL_MODULE_ID}` },\n injectTo: 'body' as const,\n },\n ];\n },\n };\n\n return [transformPlugin, overlayPlugin];\n}\n\nfunction getOverlayCode(shortcut: string): string {\n return `\n// UI Context Kit - Overlay (auto-injected in dev mode)\n(function() {\n if (typeof window === 'undefined') return;\n\n var isActive = false;\n var highlightEl = null;\n var toolbarEl = null;\n var selectedEl = null;\n var isFrozen = false;\n var frozenObservers = [];\n var multiSelected = new Set();\n\n var SHORTCUT = '${shortcut}';\n var ONBOARDING_KEY = 'ui-context-kit-onboarding-dismissed';\n\n // ---- Shadow DOM Overlay ----\n function createOverlay() {\n var host = document.createElement('div');\n host.id = 'ui-context-host';\n host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;';\n document.body.appendChild(host);\n\n var shadow = host.attachShadow({ mode: 'open' });\n shadow.innerHTML = [\n '<style>',\n '.highlight { position:fixed; border:2px solid #3b82f6; background:rgba(59,130,246,0.08); pointer-events:none; transition:all 0.15s ease; border-radius:3px; display:none; }',\n '.highlight-label { position:absolute; top:-22px; left:-2px; background:#3b82f6; color:white; font-size:11px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; padding:2px 8px; border-radius:3px 3px 0 0; white-space:nowrap; }',\n '.toolbar { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:#0f172a; border:1px solid #334155; border-radius:12px; padding:8px 12px; display:flex; gap:6px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.5); pointer-events:auto; }',\n '.toolbar-label { color:#94a3b8; font-size:12px; padding:0 4px; }',\n '.toolbar-btn { color:white; border:none; padding:6px 14px; border-radius:6px; font-size:12px; font-weight:500; cursor:pointer; white-space:nowrap; }',\n '.toolbar-btn:hover { filter:brightness(1.15); }',\n '.toolbar-btn.primary { background:#3b82f6; }',\n '.toolbar-btn.secondary { background:#475569; }',\n '.toolbar-btn.freeze { background:#475569; }',\n '.toolbar-btn.freeze.active { background:#f59e0b; color:#1e293b; }',\n '.badge { display:inline-block; background:#f59e0b; color:#1e293b; font-size:10px; font-weight:700; padding:1px 5px; border-radius:10px; margin-left:4px; }',\n '.divider { width:1px; height:20px; background:#334155; }',\n '.toast { position:fixed; bottom:72px; left:50%; transform:translateX(-50%); background:#0f172a; color:#e2e8f0; padding:10px 20px; border-radius:8px; font-size:13px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 4px 16px rgba(0,0,0,0.4); pointer-events:none; animation:toastIn 0.2s ease; }',\n '@keyframes toastIn { from { opacity:0; transform:translateX(-50%) translateY(8px); } }',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.banner-text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.banner-text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.banner-close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.banner-close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"highlight\"><div class=\"highlight-label\"></div></div>',\n '<div class=\"toolbar\" style=\"display:none\">',\n ' <span class=\"toolbar-label\">UI Context</span>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-btn\">Capture</button>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-all-btn\" style=\"display:none\">Capture All <span class=\"badge\" id=\"multi-badge\" style=\"display:none\">0</span></button>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn freeze\" id=\"freeze-btn\" title=\"Freeze DOM (Ctrl+Shift+F)\">Freeze</button>',\n ' <button class=\"toolbar-btn secondary\" id=\"close-btn\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n highlightEl = shadow.querySelector('.highlight');\n toolbarEl = shadow.querySelector('.toolbar');\n\n shadow.getElementById('close-btn').addEventListener('click', deactivate);\n shadow.getElementById('capture-btn').addEventListener('click', function() {\n if (selectedEl) captureAndSend(selectedEl);\n });\n shadow.getElementById('capture-all-btn').addEventListener('click', captureAllSelected);\n shadow.getElementById('freeze-btn').addEventListener('click', toggleFreeze);\n\n return { host: host, shadow: shadow };\n }\n\n var overlayRef = null;\n\n function showHighlight(el) {\n if (!highlightEl || !el) { highlightEl && (highlightEl.style.display = 'none'); return; }\n var rect = el.getBoundingClientRect();\n highlightEl.style.display = 'block';\n highlightEl.style.top = rect.top + 'px';\n highlightEl.style.left = rect.left + 'px';\n highlightEl.style.width = rect.width + 'px';\n highlightEl.style.height = rect.height + 'px';\n\n var label = highlightEl.querySelector('.highlight-label');\n var src = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var comp = el.getAttribute('data-source-component');\n label.textContent = src\n ? (comp || el.tagName.toLowerCase()) + ' \\\\u00b7 ' + src + ':' + line\n : el.tagName.toLowerCase();\n }\n\n function showToast(msg, duration) {\n if (!overlayRef) return;\n duration = duration || 3000;\n var t = document.createElement('div');\n t.className = 'toast';\n t.textContent = msg;\n overlayRef.shadow.appendChild(t);\n setTimeout(function() { t.remove(); }, duration);\n }\n\n // ---- React Fiber Hierarchy ----\n function getComponentHierarchy(el) {\n var fiberKey = Object.keys(el).find(function(k) {\n return k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$');\n });\n if (!fiberKey) return [];\n var hierarchy = [];\n var fiber = el[fiberKey];\n while (fiber) {\n if (typeof fiber.type === 'function') {\n var name = fiber.type.displayName || fiber.type.name;\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n } else if (typeof fiber.type === 'object' && fiber.type) {\n var name = fiber.type.displayName || (fiber.type.render && (fiber.type.render.displayName || fiber.type.render.name));\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n }\n fiber = fiber.return;\n }\n return hierarchy;\n }\n\n // ---- Computed Styles ----\n var STYLE_PROPS = [\n 'padding','padding-top','padding-right','padding-bottom','padding-left',\n 'margin','margin-top','margin-right','margin-bottom','margin-left',\n 'background-color','color','font-size','font-weight','font-family',\n 'border-radius','display','flex-direction','justify-content','align-items',\n 'gap','width','height','overflow','position','z-index','opacity',\n 'border','box-shadow','line-height','letter-spacing','text-align'\n ];\n\n function extractContext(el) {\n var computed = getComputedStyle(el);\n var rect = el.getBoundingClientRect();\n var styles = {};\n for (var i = 0; i < STYLE_PROPS.length; i++) {\n var p = STYLE_PROPS[i];\n var v = computed.getPropertyValue(p);\n if (v && v !== 'none' && v !== 'normal' && v !== 'auto' && v !== '0px' && v !== 'rgba(0, 0, 0, 0)') {\n styles[p] = v;\n }\n }\n\n var file = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var col = el.getAttribute('data-source-col');\n var component = el.getAttribute('data-source-component');\n\n return {\n source: file ? { file: file, line: parseInt(line||'0'), col: parseInt(col||'0'), component: component || undefined } : null,\n tagName: el.tagName.toLowerCase(),\n classes: el.className || '',\n computedStyles: styles,\n dimensions: { width: rect.width, height: rect.height, x: rect.x, y: rect.y },\n componentHierarchy: getComponentHierarchy(el),\n url: location.href\n };\n }\n\n // ---- Screenshot ----\n function captureScreenshot(el) {\n return new Promise(function(resolve) {\n try {\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) { resolve(undefined); return; }\n\n var canvas = document.createElement('canvas');\n var dpr = window.devicePixelRatio || 1;\n var pad = 8;\n var w = Math.ceil(rect.width + pad * 2);\n var h = Math.ceil(rect.height + pad * 2);\n canvas.width = w * dpr;\n canvas.height = h * dpr;\n\n var ctx = canvas.getContext('2d');\n if (!ctx) { resolve(undefined); return; }\n ctx.scale(dpr, dpr);\n\n // Try SVG foreignObject approach\n var clone = el.cloneNode(true);\n copyStyles(el, clone);\n\n var svgNs = 'http://www.w3.org/2000/svg';\n var svg = document.createElementNS(svgNs, 'svg');\n svg.setAttribute('width', String(w));\n svg.setAttribute('height', String(h));\n svg.setAttribute('xmlns', svgNs);\n\n var fo = document.createElementNS(svgNs, 'foreignObject');\n fo.setAttribute('width', '100%');\n fo.setAttribute('height', '100%');\n\n var container = document.createElement('div');\n container.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n container.style.cssText = 'width:' + w + 'px;height:' + h + 'px;display:flex;align-items:center;justify-content:center;background:white;padding:' + pad + 'px;box-sizing:border-box;';\n container.appendChild(clone);\n fo.appendChild(container);\n svg.appendChild(fo);\n\n var svgStr = new XMLSerializer().serializeToString(svg);\n var blob = new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' });\n var url = URL.createObjectURL(blob);\n\n var img = new Image();\n img.onload = function() {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.onerror = function() {\n URL.revokeObjectURL(url);\n // Fallback: placeholder\n ctx.fillStyle = '#f8fafc';\n ctx.fillRect(0, 0, w, h);\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 2;\n ctx.strokeRect(pad, pad, rect.width, rect.height);\n ctx.fillStyle = '#64748b';\n ctx.font = '11px -apple-system, BlinkMacSystemFont, sans-serif';\n ctx.fillText(Math.round(rect.width) + ' x ' + Math.round(rect.height) + 'px', pad + 4, pad + rect.height / 2 + 4);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.src = url;\n } catch(e) {\n resolve(undefined);\n }\n });\n }\n\n function copyStyles(src, tgt) {\n var cs = getComputedStyle(src);\n var props = ['display','width','height','padding','margin','border','border-radius','background','background-color','color','font-size','font-weight','font-family','line-height','text-align','box-shadow','opacity','flex-direction','justify-content','align-items','gap','overflow'];\n for (var i = 0; i < props.length; i++) {\n tgt.style.setProperty(props[i], cs.getPropertyValue(props[i]));\n }\n var sc = src.children, tc = tgt.children;\n for (var j = 0; j < sc.length && j < tc.length; j++) {\n if (sc[j] instanceof HTMLElement && tc[j] instanceof HTMLElement) copyStyles(sc[j], tc[j]);\n }\n }\n\n // ---- Capture & Send ----\n function captureAndSend(el) {\n showToast('Capturing context...');\n var ctx = extractContext(el);\n\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', ctx);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast('Context captured! Check .ui-context/latest.md');\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n });\n }\n\n function captureAllSelected() {\n if (multiSelected.size === 0) {\n showToast('No elements selected. Use Shift+Click to select multiple.');\n return;\n }\n showToast('Capturing ' + multiSelected.size + ' elements...');\n\n var elements = Array.from(multiSelected);\n var contexts = [];\n var pending = elements.length;\n\n elements.forEach(function(el) {\n var ctx = extractContext(el);\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n contexts.push(ctx);\n pending--;\n if (pending === 0) {\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', contexts);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast(contexts.length + ' elements captured!');\n clearMultiSelection();\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n }\n });\n });\n }\n\n function updateMultiBadge() {\n if (!overlayRef) return;\n var btn = overlayRef.shadow.getElementById('capture-all-btn');\n var badge = overlayRef.shadow.getElementById('multi-badge');\n if (multiSelected.size > 0) {\n btn.style.display = '';\n badge.style.display = '';\n badge.textContent = String(multiSelected.size);\n } else {\n btn.style.display = 'none';\n badge.style.display = 'none';\n }\n }\n\n function clearMultiSelection() {\n multiSelected.forEach(function(el) { el.style.outline = ''; });\n multiSelected.clear();\n updateMultiBadge();\n }\n\n // ---- Freeze Mode ----\n function toggleFreeze() {\n if (isFrozen) {\n unfreezeDOM();\n showToast('DOM unfrozen');\n } else {\n freezeDOM();\n showToast('DOM frozen — hover states preserved');\n }\n var btn = overlayRef && overlayRef.shadow.getElementById('freeze-btn');\n if (btn) {\n if (isFrozen) {\n btn.classList.add('active');\n btn.textContent = 'Frozen';\n } else {\n btn.classList.remove('active');\n btn.textContent = 'Freeze';\n }\n }\n }\n\n function freezeDOM() {\n isFrozen = true;\n var observer = new MutationObserver(function(mutations) {\n for (var i = 0; i < mutations.length; i++) {\n var m = mutations[i];\n for (var j = 0; j < m.addedNodes.length; j++) {\n var node = m.addedNodes[j];\n if (node instanceof HTMLElement && !(node.id && node.id.indexOf('ui-context') !== -1)) {\n node.remove();\n }\n }\n if (m.type === 'attributes' && m.target instanceof HTMLElement) {\n if (!(m.target.id && m.target.id.indexOf('ui-context') !== -1)) {\n var old = m.oldValue;\n if (old !== null && m.attributeName) {\n m.target.setAttribute(m.attributeName, old);\n }\n }\n }\n }\n });\n observer.observe(document.body, {\n childList: true, subtree: true, attributes: true, attributeOldValue: true\n });\n frozenObservers.push(observer);\n }\n\n function unfreezeDOM() {\n isFrozen = false;\n frozenObservers.forEach(function(obs) { obs.disconnect(); });\n frozenObservers = [];\n }\n\n // ---- Event Handlers ----\n function isOverlayEl(t) {\n return t.id === 'ui-context-host' || (t.closest && t.closest('#ui-context-host'));\n }\n\n function onMouseMove(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n showHighlight(t);\n }\n\n function onClick(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n e.preventDefault();\n e.stopPropagation();\n\n // Shift+Click: multi-select\n if (e.shiftKey) {\n if (multiSelected.has(t)) {\n multiSelected.delete(t);\n t.style.outline = '';\n } else {\n multiSelected.add(t);\n t.style.outline = '2px dashed #f59e0b';\n }\n updateMultiBadge();\n return;\n }\n\n selectedEl = t;\n captureAndSend(t);\n }\n\n // ---- Activate / Deactivate ----\n function activate() {\n if (isActive) return;\n isActive = true;\n overlayRef = createOverlay();\n toolbarEl.style.display = 'flex';\n document.addEventListener('mousemove', onMouseMove, true);\n document.addEventListener('click', onClick, true);\n document.body.style.cursor = 'crosshair';\n showToast('Click to capture, Shift+click for multi-select', 2000);\n }\n\n function deactivate() {\n if (!isActive) return;\n isActive = false;\n selectedEl = null;\n if (isFrozen) unfreezeDOM();\n clearMultiSelection();\n document.removeEventListener('mousemove', onMouseMove, true);\n document.removeEventListener('click', onClick, true);\n document.body.style.cursor = '';\n if (overlayRef) {\n overlayRef.host.remove();\n overlayRef = null;\n highlightEl = null;\n toolbarEl = null;\n }\n }\n\n // ---- Keyboard Shortcuts ----\n document.addEventListener('keydown', function(e) {\n var keys = SHORTCUT.split('+');\n var needsAlt = keys.indexOf('alt') !== -1;\n var needsCtrl = keys.indexOf('ctrl') !== -1;\n var needsShift = keys.indexOf('shift') !== -1;\n var mainKey = keys[keys.length - 1].toLowerCase();\n var mainCode = /^[a-z]$/.test(mainKey) ? 'Key' + mainKey.toUpperCase() : /^[0-9]$/.test(mainKey) ? 'Digit' + mainKey : '';\n\n if (e.altKey === needsAlt && e.ctrlKey === needsCtrl && e.shiftKey === needsShift && (mainCode ? e.code === mainCode : e.key.toLowerCase() === mainKey)) {\n e.preventDefault();\n isActive ? deactivate() : activate();\n return;\n }\n\n // Freeze: Ctrl+Shift+F (only when active)\n if (isActive && e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'f') {\n e.preventDefault();\n toggleFreeze();\n }\n });\n\n // ---- Onboarding Banner ----\n function showOnboarding() {\n try { if (localStorage.getItem(ONBOARDING_KEY)) return; } catch(e) { return; }\n\n // Wait for overlay ref to be set up\n var bannerHost = document.createElement('div');\n bannerHost.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483646;pointer-events:none;';\n document.body.appendChild(bannerHost);\n var bannerShadow = bannerHost.attachShadow({ mode: 'open' });\n\n var shortcutDisplay = SHORTCUT.split('+').map(function(k) { return k.charAt(0).toUpperCase() + k.slice(1); }).join(' + ');\n\n bannerShadow.innerHTML = [\n '<style>',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"banner\">',\n ' <span style=\"font-size:20px\">\\\\u{1F3AF}</span>',\n ' <span class=\"text\">',\n ' <strong>UI Context Kit</strong> is ready! Press <kbd class=\"kbd\">' + shortcutDisplay + '</kbd> to capture any UI element for AI.',\n ' <strong>Shift+Click</strong> for multi-select, <strong>Ctrl+Shift+F</strong> to freeze state.',\n ' </span>',\n ' <button class=\"close\" title=\"Dismiss\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n bannerShadow.querySelector('.close').addEventListener('click', function() {\n bannerHost.remove();\n try { localStorage.setItem(ONBOARDING_KEY, '1'); } catch(e) {}\n });\n\n setTimeout(function() {\n if (bannerHost.parentNode) {\n var banner = bannerShadow.querySelector('.banner');\n if (banner) {\n banner.style.transition = 'opacity 0.3s ease';\n banner.style.opacity = '0';\n setTimeout(function() { bannerHost.remove(); }, 300);\n }\n }\n }, 8000);\n }\n\n showOnboarding();\n console.log('[ui-context-kit] Ready. Press ' + SHORTCUT + ' to activate.');\n})();\n `;\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { ElementContext } from './types';\n\nexport async function writeContext(\n context: ElementContext | ElementContext[],\n projectRoot: string,\n outputDir: string,\n): Promise<string> {\n const outPath = path.resolve(projectRoot, outputDir);\n const capturesDir = path.join(outPath, 'captures');\n\n // Ensure output directories exist\n fs.mkdirSync(outPath, { recursive: true });\n fs.mkdirSync(path.join(outPath, 'screenshots'), { recursive: true });\n fs.mkdirSync(capturesDir, { recursive: true });\n\n const contexts = Array.isArray(context) ? context : [context];\n\n // Save screenshots if available\n const screenshotPaths: (string | undefined)[] = [];\n for (const ctx of contexts) {\n if (ctx.screenshotDataUrl) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = `capture-${timestamp}.png`;\n const relPath = `./screenshots/${filename}`;\n const base64 = ctx.screenshotDataUrl.replace(/^data:image\\/\\w+;base64,/, '');\n fs.writeFileSync(path.join(outPath, 'screenshots', filename), Buffer.from(base64, 'base64'));\n screenshotPaths.push(relPath);\n } else {\n screenshotPaths.push(undefined);\n }\n }\n\n // Read source code snippets\n const sourceSnippets: string[] = [];\n for (const ctx of contexts) {\n if (ctx.source) {\n sourceSnippets.push(readSourceSnippet(\n path.resolve(projectRoot, ctx.source.file),\n ctx.source.line,\n 10,\n ));\n } else {\n sourceSnippets.push('');\n }\n }\n\n // Build markdown\n const md = contexts.length === 1\n ? buildMarkdown(contexts[0], sourceSnippets[0], screenshotPaths[0])\n : buildMultiMarkdown(contexts, sourceSnippets, screenshotPaths);\n\n // Write latest.md\n const latestPath = path.join(outPath, 'latest.md');\n fs.writeFileSync(latestPath, md, 'utf-8');\n\n // Write timestamped capture file for history\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const capturePath = path.join(capturesDir, `${timestamp}.md`);\n fs.writeFileSync(capturePath, md, 'utf-8');\n\n // Update history.md (append summary, keep last 20 entries)\n updateHistory(outPath, contexts, timestamp);\n\n // Clean up old captures (keep last 50)\n cleanupOldCaptures(capturesDir, 50);\n\n console.log(`[ui-context-kit] Context written to ${path.relative(projectRoot, latestPath)}`);\n console.log(`[ui-context-kit] History saved to ${path.relative(projectRoot, capturePath)}`);\n\n return latestPath;\n}\n\nfunction updateHistory(outPath: string, contexts: ElementContext[], timestamp: string): void {\n const historyPath = path.join(outPath, 'history.md');\n\n // Build summary for this capture\n const lines: string[] = [];\n const time = timestamp.replace(/T/, ' ').replace(/-(\\d{2})-(\\d{2})-(\\d+)Z?$/, ':$1:$2');\n\n for (const ctx of contexts) {\n const component = ctx.source?.component || ctx.tagName;\n const file = ctx.source ? `${ctx.source.file}:${ctx.source.line}` : '';\n lines.push(`- **${component}** ${file ? `(\\`${file}\\`)` : ''} — ${ctx.url}`);\n }\n\n const entry = `### ${time}\\n${lines.join('\\n')}\\n\\n`;\n\n // Read existing history or create new\n let existing = '';\n try {\n existing = fs.readFileSync(historyPath, 'utf-8');\n } catch {\n existing = '# UI Context Capture History\\n\\n';\n }\n\n // Insert after the header line\n const headerEnd = existing.indexOf('\\n\\n');\n if (headerEnd !== -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n existing = header + entry + body;\n } else {\n existing += entry;\n }\n\n // Keep only last 20 entries\n const entries = existing.split(/(?=### )/);\n const header = entries[0]; // everything before first ###\n const captureEntries = entries.slice(1);\n if (captureEntries.length > 20) {\n existing = header + captureEntries.slice(0, 20).join('');\n }\n\n fs.writeFileSync(historyPath, existing, 'utf-8');\n}\n\nfunction cleanupOldCaptures(capturesDir: string, maxFiles: number): void {\n try {\n const files = fs.readdirSync(capturesDir)\n .filter(f => f.endsWith('.md'))\n .sort()\n .reverse();\n\n for (let i = maxFiles; i < files.length; i++) {\n fs.unlinkSync(path.join(capturesDir, files[i]));\n }\n } catch {\n // ignore cleanup errors\n }\n}\n\nfunction readSourceSnippet(filePath: string, targetLine: number, contextLines: number): string {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n const start = Math.max(0, targetLine - contextLines - 1);\n const end = Math.min(lines.length, targetLine + contextLines);\n\n return lines\n .slice(start, end)\n .map((line, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === targetLine ? ' // <-- target' : '';\n return `${line}${marker}`;\n })\n .join('\\n');\n } catch {\n return '// Source file not found';\n }\n}\n\nfunction buildMarkdown(\n context: ElementContext,\n sourceSnippet: string,\n screenshotPath?: string,\n): string {\n const { source, tagName, classes, computedStyles, dimensions, componentHierarchy, url } = context;\n\n const lines: string[] = ['# UI Context Capture', ''];\n\n // Target Element section\n lines.push('## Target Element');\n if (source) {\n if (source.component) {\n lines.push(`- **Component**: \\`${source.component}\\` in \\`${source.file}:${source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${source.file}:${source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${url}`);\n lines.push(`- **Tag**: \\`<${tagName}>\\``);\n lines.push('');\n\n // Source Code section\n if (source && sourceSnippet) {\n const startLine = Math.max(1, source.line - 10);\n const endLine = source.line + 10;\n lines.push('## Source Code');\n lines.push('```tsx');\n lines.push(`// ${source.file}:${startLine}-${endLine}`);\n lines.push(sourceSnippet);\n lines.push('```');\n lines.push('');\n }\n\n // Styling section\n lines.push('## Styling');\n if (classes) {\n lines.push(`- **Classes**: \\`${classes}\\``);\n }\n\n const styleEntries = Object.entries(computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n\n lines.push(`- **Dimensions**: ${Math.round(dimensions.width)}x${Math.round(dimensions.height)}px at (${Math.round(dimensions.x)}, ${Math.round(dimensions.y)})`);\n lines.push('');\n\n // Component Hierarchy section\n if (componentHierarchy.length > 0) {\n lines.push('## Component Hierarchy');\n lines.push(componentHierarchy.join(' > '));\n lines.push('');\n }\n\n // Screenshot section\n if (screenshotPath) {\n lines.push('## Screenshot');\n lines.push(`![element](${screenshotPath})`);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nfunction buildMultiMarkdown(\n contexts: ElementContext[],\n sourceSnippets: string[],\n screenshotPaths: (string | undefined)[],\n): string {\n const lines: string[] = [\n `# UI Context Capture (${contexts.length} elements)`,\n '',\n ];\n\n for (let i = 0; i < contexts.length; i++) {\n const ctx = contexts[i];\n const snippet = sourceSnippets[i];\n const screenshot = screenshotPaths[i];\n\n lines.push(`---`);\n lines.push('');\n lines.push(`## Element ${i + 1}: \\`<${ctx.tagName}>\\`${ctx.source?.component ? ` (${ctx.source.component})` : ''}`);\n lines.push('');\n\n if (ctx.source) {\n if (ctx.source.component) {\n lines.push(`- **Component**: \\`${ctx.source.component}\\` in \\`${ctx.source.file}:${ctx.source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${ctx.source.file}:${ctx.source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${ctx.url}`);\n lines.push('');\n\n if (ctx.source && snippet) {\n const startLine = Math.max(1, ctx.source.line - 10);\n const endLine = ctx.source.line + 10;\n lines.push('### Source Code');\n lines.push('```tsx');\n lines.push(`// ${ctx.source.file}:${startLine}-${endLine}`);\n lines.push(snippet);\n lines.push('```');\n lines.push('');\n }\n\n lines.push('### Styling');\n if (ctx.classes) {\n lines.push(`- **Classes**: \\`${ctx.classes}\\``);\n }\n const styleEntries = Object.entries(ctx.computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n lines.push(`- **Dimensions**: ${Math.round(ctx.dimensions.width)}x${Math.round(ctx.dimensions.height)}px at (${Math.round(ctx.dimensions.x)}, ${Math.round(ctx.dimensions.y)})`);\n lines.push('');\n\n if (ctx.componentHierarchy.length > 0) {\n lines.push('### Component Hierarchy');\n lines.push(ctx.componentHierarchy.join(' > '));\n lines.push('');\n }\n\n if (screenshot) {\n lines.push('### Screenshot');\n lines.push(`![element ${i + 1}](${screenshot})`);\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());\n}\n","import { exec } from 'child_process';\nimport { platform } from 'os';\n\nexport function copyToClipboard(text: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const os = platform();\n let cmd: string;\n\n if (os === 'darwin') {\n cmd = 'pbcopy';\n } else if (os === 'linux') {\n cmd = 'xclip -selection clipboard';\n } else if (os === 'win32') {\n cmd = 'clip';\n } else {\n reject(new Error(`Unsupported platform: ${os}`));\n return;\n }\n\n const proc = exec(cmd, (err) => {\n if (err) reject(err);\n else resolve();\n });\n\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n","import type { ViteDevServer } from 'vite';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { ElementContext, UIContextKitOptions } from './types';\n\nexport function setupWebSocket(server: ViteDevServer, options: UIContextKitOptions): void {\n const root = server.config.root;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n\n server.ws.on('ui-context:capture', async (data: ElementContext | ElementContext[], client) => {\n try {\n const count = Array.isArray(data) ? data.length : 1;\n console.log(`[ui-context-kit] Received context capture (${count} element${count > 1 ? 's' : ''})`);\n\n const filePath = await writeContext(data, root, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch (e) {\n console.warn('[ui-context-kit] Could not copy to clipboard:', e);\n }\n }\n\n client.send('ui-context:capture-result', { success: true });\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n client.send('ui-context:capture-result', { success: false, error: err.message });\n }\n });\n}\n","import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { startContextServer } from './next-server';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Resolves the path to the next-loader.cjs file in the dist directory.\n */\nfunction resolveLoaderPath(): string {\n try {\n // ESM: import.meta.url is available\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, 'next-loader.cjs');\n } catch {\n // CJS fallback: __dirname is available\n return path.resolve(__dirname, 'next-loader.cjs');\n }\n}\n\n/**\n * Wraps a Next.js config to add ui-context-kit source attribute injection in development.\n * Supports both webpack and Turbopack.\n *\n * Also starts a standalone HTTP context server (port 19638) so the browser overlay\n * can send captured context without Vite's HMR WebSocket.\n *\n * Usage:\n * ```js\n * // next.config.mjs\n * import { withUIContext } from '@ui-context-kit/bridge';\n * export default withUIContext({ /* your config *\\/ });\n * ```\n */\nexport function withUIContext(nextConfig: any = {}, options?: UIContextKitOptions) {\n if (process.env.NODE_ENV !== 'development') return nextConfig;\n\n // Start the standalone context server for receiving captures\n startContextServer(options);\n\n const loaderPath = resolveLoaderPath();\n\n return {\n ...nextConfig,\n\n // Turbopack support\n experimental: {\n ...nextConfig.experimental,\n turbo: {\n ...nextConfig.experimental?.turbo,\n rules: {\n ...nextConfig.experimental?.turbo?.rules,\n '*.tsx': {\n loaders: [loaderPath],\n as: '*.tsx',\n },\n '*.jsx': {\n loaders: [loaderPath],\n as: '*.jsx',\n },\n },\n },\n },\n\n // Webpack support\n webpack(config: any, context: any) {\n config.module.rules.unshift({\n test: /\\.(jsx|tsx)$/,\n exclude: /node_modules/,\n enforce: 'pre' as const,\n use: [{ loader: loaderPath }],\n });\n\n if (typeof nextConfig.webpack === 'function') {\n config = nextConfig.webpack(config, context);\n }\n\n return config;\n },\n };\n}\n","import http from 'http';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { UIContextKitOptions } from './types';\n\nconst DEFAULT_PORT = 19638;\nlet serverInstance: http.Server | null = null;\n\n/**\n * Starts a standalone HTTP server for receiving UI context captures.\n * Used by Next.js projects where Vite's HMR WebSocket is not available.\n *\n * The overlay's transport sends HTTP POST to /capture with the context data.\n */\nexport function startContextServer(options: UIContextKitOptions = {}): void {\n if (serverInstance) return;\n\n const port = DEFAULT_PORT;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n const projectRoot = process.cwd();\n\n serverInstance = http.createServer((req, res) => {\n // CORS headers for cross-port requests\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method === 'POST' && req.url === '/capture') {\n let body = '';\n req.on('data', (chunk: string) => {\n body += chunk;\n });\n req.on('end', async () => {\n try {\n const data = JSON.parse(body);\n console.log(\n `[ui-context-kit] Received context capture (${Array.isArray(data) ? data.length : 1} element${Array.isArray(data) && data.length > 1 ? 's' : ''})`,\n );\n\n const filePath = await writeContext(data, projectRoot, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch {\n // clipboard copy is best-effort\n }\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: true }));\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: false, error: err.message }));\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n serverInstance.listen(port, () => {\n console.log(`[ui-context-kit] Context server running on http://localhost:${port}`);\n });\n\n serverInstance.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`[ui-context-kit] Context server already running on port ${port}`);\n serverInstance = null;\n } else {\n console.error('[ui-context-kit] Context server error:', err);\n serverInstance = null;\n }\n });\n}\n","import { createVitePlugin } from './vite-plugin';\nimport { writeContext } from './context-writer';\nimport { withUIContext } from './next-wrapper';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Vite plugin for UI Context Kit.\n * Enables browser-based element selection and context capture for AI coding assistants.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { uiContextKit } from '@ui-context-kit/bridge'\n * export default defineConfig({\n * plugins: [react(), uiContextKit()]\n * })\n * ```\n */\nexport function uiContextKit(options?: UIContextKitOptions) {\n return createVitePlugin(options);\n}\n\nexport { writeContext, withUIContext };\nexport type { UIContextKitOptions, ElementContext, SourceLocation } from './types';\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["path","fs","timestamp","header","fs","path","fs","path","fs","__dirname","path"]}
1
+ {"version":3,"sources":["../src/vite-plugin.ts","../src/context-writer.ts","../src/clipboard.ts","../src/ws-server.ts","../src/next-wrapper.ts","../src/next-server.ts","../src/index.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport path from 'path';\nimport fs from 'fs';\nimport { injectSourceAttributes } from '@ui-context-kit/babel-plugin';\nimport { setupWebSocket } from './ws-server';\nimport type { UIContextKitOptions } from './types';\n\nconst JSX_EXTENSIONS = /\\.(jsx|tsx)$/;\nconst VIRTUAL_MODULE_ID = 'virtual:ui-context-overlay';\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0' + VIRTUAL_MODULE_ID;\n\nexport function createVitePlugin(options: UIContextKitOptions = {}): Plugin[] {\n const {\n shortcut = 'alt+x',\n include,\n exclude = ['node_modules'],\n } = options;\n\n let projectRoot = '';\n\n const transformPlugin: Plugin = {\n name: 'ui-context-kit:transform',\n enforce: 'pre',\n apply: 'serve',\n\n configResolved(config) {\n projectRoot = config.root;\n },\n\n transform(code, id) {\n if (!JSX_EXTENSIONS.test(id)) return null;\n\n const relativePath = path.relative(projectRoot, id);\n if (exclude?.some(p => relativePath.includes(p))) return null;\n if (include && !include.some(p => relativePath.includes(p))) return null;\n if (id.includes('\\0') || id.includes('node_modules')) return null;\n\n const filePath = path.relative(projectRoot, id);\n\n let originalCode: string | undefined;\n try {\n originalCode = fs.readFileSync(id, 'utf-8');\n } catch {\n // ignore read errors\n }\n\n const result = injectSourceAttributes(code, filePath, originalCode);\n if (!result) return null;\n\n return { code: result.code, map: result.map };\n },\n };\n\n const overlayPlugin: Plugin = {\n name: 'ui-context-kit:overlay',\n apply: 'serve',\n\n resolveId(id) {\n if (id === VIRTUAL_MODULE_ID) return RESOLVED_VIRTUAL_MODULE_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n return getOverlayCode(shortcut);\n }\n },\n\n configureServer(server) {\n setupWebSocket(server, options);\n },\n\n transformIndexHtml() {\n return [\n {\n tag: 'script',\n attrs: { type: 'module', src: `/@id/${VIRTUAL_MODULE_ID}` },\n injectTo: 'body' as const,\n },\n ];\n },\n };\n\n return [transformPlugin, overlayPlugin];\n}\n\nfunction getOverlayCode(shortcut: string): string {\n return `\n// UI Context Kit - Overlay (auto-injected in dev mode)\n(function() {\n if (typeof window === 'undefined') return;\n\n var isActive = false;\n var highlightEl = null;\n var toolbarEl = null;\n var selectedEl = null;\n var isFrozen = false;\n var frozenObservers = [];\n var multiSelected = new Set();\n\n var SHORTCUT = '${shortcut}';\n var ONBOARDING_KEY = 'ui-context-kit-onboarding-dismissed';\n\n // ---- Shadow DOM Overlay ----\n function createOverlay() {\n var host = document.createElement('div');\n host.id = 'ui-context-host';\n host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;';\n document.body.appendChild(host);\n\n var shadow = host.attachShadow({ mode: 'open' });\n shadow.innerHTML = [\n '<style>',\n '.highlight { position:fixed; border:2px solid #3b82f6; background:rgba(59,130,246,0.08); pointer-events:none; transition:all 0.15s ease; border-radius:3px; display:none; }',\n '.highlight-label { position:absolute; top:-22px; left:-2px; background:#3b82f6; color:white; font-size:11px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; padding:2px 8px; border-radius:3px 3px 0 0; white-space:nowrap; }',\n '.toolbar { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:#0f172a; border:1px solid #334155; border-radius:12px; padding:8px 12px; display:flex; gap:6px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.5); pointer-events:auto; }',\n '.toolbar-label { color:#94a3b8; font-size:12px; padding:0 4px; }',\n '.toolbar-btn { color:white; border:none; padding:6px 14px; border-radius:6px; font-size:12px; font-weight:500; cursor:pointer; white-space:nowrap; }',\n '.toolbar-btn:hover { filter:brightness(1.15); }',\n '.toolbar-btn.primary { background:#3b82f6; }',\n '.toolbar-btn.secondary { background:#475569; }',\n '.toolbar-btn.freeze { background:#475569; }',\n '.toolbar-btn.freeze.active { background:#f59e0b; color:#1e293b; }',\n '.badge { display:inline-block; background:#f59e0b; color:#1e293b; font-size:10px; font-weight:700; padding:1px 5px; border-radius:10px; margin-left:4px; }',\n '.divider { width:1px; height:20px; background:#334155; }',\n '.toast { position:fixed; bottom:72px; left:50%; transform:translateX(-50%); background:#0f172a; color:#e2e8f0; padding:10px 20px; border-radius:8px; font-size:13px; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 4px 16px rgba(0,0,0,0.4); pointer-events:none; animation:toastIn 0.2s ease; }',\n '@keyframes toastIn { from { opacity:0; transform:translateX(-50%) translateY(8px); } }',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.banner-text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.banner-text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.banner-close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.banner-close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"highlight\"><div class=\"highlight-label\"></div></div>',\n '<div class=\"toolbar\" style=\"display:none\">',\n ' <span class=\"toolbar-label\">UI Context</span>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-btn\">Capture</button>',\n ' <button class=\"toolbar-btn primary\" id=\"capture-all-btn\" style=\"display:none\">Capture All <span class=\"badge\" id=\"multi-badge\" style=\"display:none\">0</span></button>',\n ' <div class=\"divider\"></div>',\n ' <button class=\"toolbar-btn freeze\" id=\"freeze-btn\" title=\"Freeze DOM (Ctrl+Shift+F)\">Freeze</button>',\n ' <button class=\"toolbar-btn secondary\" id=\"close-btn\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n highlightEl = shadow.querySelector('.highlight');\n toolbarEl = shadow.querySelector('.toolbar');\n\n shadow.getElementById('close-btn').addEventListener('click', deactivate);\n shadow.getElementById('capture-btn').addEventListener('click', function() {\n if (selectedEl) captureAndSend(selectedEl);\n });\n shadow.getElementById('capture-all-btn').addEventListener('click', captureAllSelected);\n shadow.getElementById('freeze-btn').addEventListener('click', toggleFreeze);\n\n return { host: host, shadow: shadow };\n }\n\n var overlayRef = null;\n\n function showHighlight(el) {\n if (!highlightEl || !el) { highlightEl && (highlightEl.style.display = 'none'); return; }\n var rect = el.getBoundingClientRect();\n highlightEl.style.display = 'block';\n highlightEl.style.top = rect.top + 'px';\n highlightEl.style.left = rect.left + 'px';\n highlightEl.style.width = rect.width + 'px';\n highlightEl.style.height = rect.height + 'px';\n\n var label = highlightEl.querySelector('.highlight-label');\n var src = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var comp = el.getAttribute('data-source-component');\n label.textContent = src\n ? (comp || el.tagName.toLowerCase()) + ' \\\\u00b7 ' + src + ':' + line\n : el.tagName.toLowerCase();\n }\n\n function showToast(msg, duration) {\n if (!overlayRef) return;\n duration = duration || 3000;\n var t = document.createElement('div');\n t.className = 'toast';\n t.textContent = msg;\n overlayRef.shadow.appendChild(t);\n setTimeout(function() { t.remove(); }, duration);\n }\n\n // ---- React Fiber Hierarchy ----\n function getComponentHierarchy(el) {\n var fiberKey = Object.keys(el).find(function(k) {\n return k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$');\n });\n if (!fiberKey) return [];\n var hierarchy = [];\n var fiber = el[fiberKey];\n while (fiber) {\n if (typeof fiber.type === 'function') {\n var name = fiber.type.displayName || fiber.type.name;\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n } else if (typeof fiber.type === 'object' && fiber.type) {\n var name = fiber.type.displayName || (fiber.type.render && (fiber.type.render.displayName || fiber.type.render.name));\n if (name && !name.startsWith('_') && name !== 'Fragment') hierarchy.unshift(name);\n }\n fiber = fiber.return;\n }\n return hierarchy;\n }\n\n // ---- Computed Styles ----\n var STYLE_PROPS = [\n 'padding','padding-top','padding-right','padding-bottom','padding-left',\n 'margin','margin-top','margin-right','margin-bottom','margin-left',\n 'background-color','color','font-size','font-weight','font-family',\n 'border-radius','display','flex-direction','justify-content','align-items',\n 'gap','width','height','overflow','position','z-index','opacity',\n 'border','box-shadow','line-height','letter-spacing','text-align'\n ];\n\n function extractContext(el) {\n var computed = getComputedStyle(el);\n var rect = el.getBoundingClientRect();\n var styles = {};\n for (var i = 0; i < STYLE_PROPS.length; i++) {\n var p = STYLE_PROPS[i];\n var v = computed.getPropertyValue(p);\n if (v && v !== 'none' && v !== 'normal' && v !== 'auto' && v !== '0px' && v !== 'rgba(0, 0, 0, 0)') {\n styles[p] = v;\n }\n }\n\n var file = el.getAttribute('data-source-file');\n var line = el.getAttribute('data-source-line');\n var col = el.getAttribute('data-source-col');\n var component = el.getAttribute('data-source-component');\n\n return {\n source: file ? { file: file, line: parseInt(line||'0'), col: parseInt(col||'0'), component: component || undefined } : null,\n tagName: el.tagName.toLowerCase(),\n classes: el.className || '',\n computedStyles: styles,\n dimensions: { width: rect.width, height: rect.height, x: rect.x, y: rect.y },\n componentHierarchy: getComponentHierarchy(el),\n url: location.href\n };\n }\n\n // ---- Screenshot ----\n function captureScreenshot(el) {\n return new Promise(function(resolve) {\n try {\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) { resolve(undefined); return; }\n\n var canvas = document.createElement('canvas');\n var dpr = window.devicePixelRatio || 1;\n var pad = 8;\n var w = Math.ceil(rect.width + pad * 2);\n var h = Math.ceil(rect.height + pad * 2);\n canvas.width = w * dpr;\n canvas.height = h * dpr;\n\n var ctx = canvas.getContext('2d');\n if (!ctx) { resolve(undefined); return; }\n ctx.scale(dpr, dpr);\n\n // Try SVG foreignObject approach\n var clone = el.cloneNode(true);\n copyStyles(el, clone);\n\n var svgNs = 'http://www.w3.org/2000/svg';\n var svg = document.createElementNS(svgNs, 'svg');\n svg.setAttribute('width', String(w));\n svg.setAttribute('height', String(h));\n svg.setAttribute('xmlns', svgNs);\n\n var fo = document.createElementNS(svgNs, 'foreignObject');\n fo.setAttribute('width', '100%');\n fo.setAttribute('height', '100%');\n\n var container = document.createElement('div');\n container.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n container.style.cssText = 'width:' + w + 'px;height:' + h + 'px;display:flex;align-items:center;justify-content:center;background:white;padding:' + pad + 'px;box-sizing:border-box;';\n container.appendChild(clone);\n fo.appendChild(container);\n svg.appendChild(fo);\n\n var svgStr = new XMLSerializer().serializeToString(svg);\n var blob = new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' });\n var url = URL.createObjectURL(blob);\n\n var img = new Image();\n img.onload = function() {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.onerror = function() {\n URL.revokeObjectURL(url);\n // Fallback: placeholder\n ctx.fillStyle = '#f8fafc';\n ctx.fillRect(0, 0, w, h);\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 2;\n ctx.strokeRect(pad, pad, rect.width, rect.height);\n ctx.fillStyle = '#64748b';\n ctx.font = '11px -apple-system, BlinkMacSystemFont, sans-serif';\n ctx.fillText(Math.round(rect.width) + ' x ' + Math.round(rect.height) + 'px', pad + 4, pad + rect.height / 2 + 4);\n try { resolve(canvas.toDataURL('image/png')); } catch(e) { resolve(undefined); }\n };\n img.src = url;\n } catch(e) {\n resolve(undefined);\n }\n });\n }\n\n function copyStyles(src, tgt) {\n var cs = getComputedStyle(src);\n var props = ['display','width','height','padding','margin','border','border-radius','background','background-color','color','font-size','font-weight','font-family','line-height','text-align','box-shadow','opacity','flex-direction','justify-content','align-items','gap','overflow'];\n for (var i = 0; i < props.length; i++) {\n tgt.style.setProperty(props[i], cs.getPropertyValue(props[i]));\n }\n var sc = src.children, tc = tgt.children;\n for (var j = 0; j < sc.length && j < tc.length; j++) {\n if (sc[j] instanceof HTMLElement && tc[j] instanceof HTMLElement) copyStyles(sc[j], tc[j]);\n }\n }\n\n // ---- Capture & Send ----\n function captureAndSend(el) {\n showToast('Capturing context...');\n var ctx = extractContext(el);\n\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', ctx);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast('Context captured! Check .ui-context/latest.md');\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n });\n }\n\n function captureAllSelected() {\n if (multiSelected.size === 0) {\n showToast('No elements selected. Use Shift+Click to select multiple.');\n return;\n }\n showToast('Capturing ' + multiSelected.size + ' elements...');\n\n var elements = Array.from(multiSelected);\n var contexts = [];\n var pending = elements.length;\n\n elements.forEach(function(el) {\n var ctx = extractContext(el);\n captureScreenshot(el).then(function(screenshot) {\n if (screenshot) ctx.screenshotDataUrl = screenshot;\n contexts.push(ctx);\n pending--;\n if (pending === 0) {\n if (import.meta.hot) {\n import.meta.hot.send('ui-context:capture', contexts);\n import.meta.hot.on('ui-context:capture-result', function(result) {\n if (result.success) {\n showToast(contexts.length + ' elements captured!');\n clearMultiSelection();\n } else {\n showToast('Capture failed: ' + (result.error || 'unknown error'));\n }\n });\n }\n }\n });\n });\n }\n\n function updateMultiBadge() {\n if (!overlayRef) return;\n var btn = overlayRef.shadow.getElementById('capture-all-btn');\n var badge = overlayRef.shadow.getElementById('multi-badge');\n if (multiSelected.size > 0) {\n btn.style.display = '';\n badge.style.display = '';\n badge.textContent = String(multiSelected.size);\n } else {\n btn.style.display = 'none';\n badge.style.display = 'none';\n }\n }\n\n function clearMultiSelection() {\n multiSelected.forEach(function(el) { el.style.outline = ''; });\n multiSelected.clear();\n updateMultiBadge();\n }\n\n // ---- Freeze Mode ----\n function toggleFreeze() {\n if (isFrozen) {\n unfreezeDOM();\n showToast('DOM unfrozen');\n } else {\n freezeDOM();\n showToast('DOM frozen — hover states preserved');\n }\n var btn = overlayRef && overlayRef.shadow.getElementById('freeze-btn');\n if (btn) {\n if (isFrozen) {\n btn.classList.add('active');\n btn.textContent = 'Frozen';\n } else {\n btn.classList.remove('active');\n btn.textContent = 'Freeze';\n }\n }\n }\n\n function freezeDOM() {\n isFrozen = true;\n var observer = new MutationObserver(function(mutations) {\n for (var i = 0; i < mutations.length; i++) {\n var m = mutations[i];\n for (var j = 0; j < m.addedNodes.length; j++) {\n var node = m.addedNodes[j];\n if (node instanceof HTMLElement && !(node.id && node.id.indexOf('ui-context') !== -1)) {\n node.remove();\n }\n }\n if (m.type === 'attributes' && m.target instanceof HTMLElement) {\n if (!(m.target.id && m.target.id.indexOf('ui-context') !== -1)) {\n var old = m.oldValue;\n if (old !== null && m.attributeName) {\n m.target.setAttribute(m.attributeName, old);\n }\n }\n }\n }\n });\n observer.observe(document.body, {\n childList: true, subtree: true, attributes: true, attributeOldValue: true\n });\n frozenObservers.push(observer);\n }\n\n function unfreezeDOM() {\n isFrozen = false;\n frozenObservers.forEach(function(obs) { obs.disconnect(); });\n frozenObservers = [];\n }\n\n // ---- Event Handlers ----\n function isOverlayEl(t) {\n return t.id === 'ui-context-host' || (t.closest && t.closest('#ui-context-host'));\n }\n\n function onMouseMove(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n showHighlight(t);\n }\n\n function onClick(e) {\n if (!isActive) return;\n var t = e.target;\n if (isOverlayEl(t)) return;\n e.preventDefault();\n e.stopPropagation();\n\n // Shift+Click: multi-select\n if (e.shiftKey) {\n if (multiSelected.has(t)) {\n multiSelected.delete(t);\n t.style.outline = '';\n } else {\n multiSelected.add(t);\n t.style.outline = '2px dashed #f59e0b';\n }\n updateMultiBadge();\n return;\n }\n\n selectedEl = t;\n captureAndSend(t);\n }\n\n // ---- Activate / Deactivate ----\n function activate() {\n if (isActive) return;\n isActive = true;\n overlayRef = createOverlay();\n toolbarEl.style.display = 'flex';\n document.addEventListener('mousemove', onMouseMove, true);\n document.addEventListener('click', onClick, true);\n document.body.style.cursor = 'crosshair';\n showToast('Click to capture, Shift+click for multi-select', 2000);\n }\n\n function deactivate() {\n if (!isActive) return;\n isActive = false;\n selectedEl = null;\n if (isFrozen) unfreezeDOM();\n clearMultiSelection();\n document.removeEventListener('mousemove', onMouseMove, true);\n document.removeEventListener('click', onClick, true);\n document.body.style.cursor = '';\n if (overlayRef) {\n overlayRef.host.remove();\n overlayRef = null;\n highlightEl = null;\n toolbarEl = null;\n }\n }\n\n // ---- Keyboard Shortcuts ----\n document.addEventListener('keydown', function(e) {\n var keys = SHORTCUT.split('+');\n var needsAlt = keys.indexOf('alt') !== -1;\n var needsCtrl = keys.indexOf('ctrl') !== -1;\n var needsShift = keys.indexOf('shift') !== -1;\n var mainKey = keys[keys.length - 1].toLowerCase();\n var mainCode = /^[a-z]$/.test(mainKey) ? 'Key' + mainKey.toUpperCase() : /^[0-9]$/.test(mainKey) ? 'Digit' + mainKey : '';\n\n if (e.altKey === needsAlt && e.ctrlKey === needsCtrl && e.shiftKey === needsShift && (mainCode ? e.code === mainCode : e.key.toLowerCase() === mainKey)) {\n e.preventDefault();\n isActive ? deactivate() : activate();\n return;\n }\n\n // Freeze: Ctrl+Shift+F (only when active)\n if (isActive && e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'f') {\n e.preventDefault();\n toggleFreeze();\n }\n });\n\n // ---- Onboarding Banner ----\n function showOnboarding() {\n try { if (localStorage.getItem(ONBOARDING_KEY)) return; } catch(e) { return; }\n\n // Wait for overlay ref to be set up\n var bannerHost = document.createElement('div');\n bannerHost.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483646;pointer-events:none;';\n document.body.appendChild(bannerHost);\n var bannerShadow = bannerHost.attachShadow({ mode: 'open' });\n\n var shortcutDisplay = SHORTCUT.split('+').map(function(k) { return k.charAt(0).toUpperCase() + k.slice(1); }).join(' + ');\n\n bannerShadow.innerHTML = [\n '<style>',\n '.banner { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:linear-gradient(135deg,#0f172a 0%,#1e293b 100%); border:1px solid #334155; border-radius:12px; padding:12px 20px; display:flex; gap:12px; align-items:center; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif; box-shadow:0 8px 32px rgba(0,0,0,0.4); animation:bannerIn 0.4s ease; max-width:480px; pointer-events:auto; }',\n '@keyframes bannerIn { from { opacity:0; transform:translateX(-50%) translateY(20px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }',\n '.text { color:#e2e8f0; font-size:13px; line-height:1.4; }',\n '.text strong { color:#60a5fa; }',\n '.kbd { display:inline-block; background:#334155; color:#e2e8f0; padding:1px 6px; border-radius:4px; font-size:12px; font-family:monospace; border:1px solid #475569; }',\n '.close { background:none; border:none; color:#64748b; cursor:pointer; padding:4px; font-size:16px; flex-shrink:0; }',\n '.close:hover { color:#94a3b8; }',\n '</style>',\n '<div class=\"banner\">',\n ' <span style=\"font-size:20px\">\\\\u{1F3AF}</span>',\n ' <span class=\"text\">',\n ' <strong>UI Context Kit</strong> is ready! Press <kbd class=\"kbd\">' + shortcutDisplay + '</kbd> to capture any UI element for AI.',\n ' <strong>Shift+Click</strong> for multi-select, <strong>Ctrl+Shift+F</strong> to freeze state.',\n ' </span>',\n ' <button class=\"close\" title=\"Dismiss\">\\\\u2715</button>',\n '</div>',\n ].join('\\\\n');\n\n bannerShadow.querySelector('.close').addEventListener('click', function() {\n bannerHost.remove();\n try { localStorage.setItem(ONBOARDING_KEY, '1'); } catch(e) {}\n });\n\n setTimeout(function() {\n if (bannerHost.parentNode) {\n var banner = bannerShadow.querySelector('.banner');\n if (banner) {\n banner.style.transition = 'opacity 0.3s ease';\n banner.style.opacity = '0';\n setTimeout(function() { bannerHost.remove(); }, 300);\n }\n }\n }, 8000);\n }\n\n showOnboarding();\n console.log('[ui-context-kit] Ready. Press ' + SHORTCUT + ' to activate.');\n})();\n `;\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { ElementContext } from './types';\n\nexport async function writeContext(\n context: ElementContext | ElementContext[],\n projectRoot: string,\n outputDir: string,\n): Promise<string> {\n const outPath = path.resolve(projectRoot, outputDir);\n const capturesDir = path.join(outPath, 'captures');\n\n // Ensure output directories exist\n fs.mkdirSync(outPath, { recursive: true });\n fs.mkdirSync(path.join(outPath, 'screenshots'), { recursive: true });\n fs.mkdirSync(capturesDir, { recursive: true });\n\n const contexts = Array.isArray(context) ? context : [context];\n\n // Save screenshots if available\n const screenshotPaths: (string | undefined)[] = [];\n for (const ctx of contexts) {\n if (ctx.screenshotDataUrl) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = `capture-${timestamp}.png`;\n const relPath = `./screenshots/${filename}`;\n const base64 = ctx.screenshotDataUrl.replace(/^data:image\\/\\w+;base64,/, '');\n fs.writeFileSync(path.join(outPath, 'screenshots', filename), Buffer.from(base64, 'base64'));\n screenshotPaths.push(relPath);\n } else {\n screenshotPaths.push(undefined);\n }\n }\n\n // Read source code snippets\n const sourceSnippets: string[] = [];\n for (const ctx of contexts) {\n if (ctx.source) {\n sourceSnippets.push(readSourceSnippet(\n path.resolve(projectRoot, ctx.source.file),\n ctx.source.line,\n 10,\n ));\n } else {\n sourceSnippets.push('');\n }\n }\n\n // Build markdown\n const md = contexts.length === 1\n ? buildMarkdown(contexts[0], sourceSnippets[0], screenshotPaths[0])\n : buildMultiMarkdown(contexts, sourceSnippets, screenshotPaths);\n\n // Write latest.md\n const latestPath = path.join(outPath, 'latest.md');\n fs.writeFileSync(latestPath, md, 'utf-8');\n\n // Write timestamped capture file for history\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const capturePath = path.join(capturesDir, `${timestamp}.md`);\n fs.writeFileSync(capturePath, md, 'utf-8');\n\n // Update history.md (append summary, keep last 20 entries)\n updateHistory(outPath, contexts, timestamp);\n\n // Clean up old captures (keep last 50)\n cleanupOldCaptures(capturesDir, 50);\n\n console.log(`[ui-context-kit] Context written to ${path.relative(projectRoot, latestPath)}`);\n console.log(`[ui-context-kit] History saved to ${path.relative(projectRoot, capturePath)}`);\n\n return latestPath;\n}\n\nfunction updateHistory(outPath: string, contexts: ElementContext[], timestamp: string): void {\n const historyPath = path.join(outPath, 'history.md');\n\n // Build summary for this capture\n const lines: string[] = [];\n const time = timestamp.replace(/T/, ' ').replace(/-(\\d{2})-(\\d{2})-(\\d+)Z?$/, ':$1:$2');\n\n for (const ctx of contexts) {\n const component = ctx.source?.component || ctx.tagName;\n const file = ctx.source ? `${ctx.source.file}:${ctx.source.line}` : '';\n lines.push(`- **${component}** ${file ? `(\\`${file}\\`)` : ''} — ${ctx.url}`);\n }\n\n const entry = `### ${time}\\n${lines.join('\\n')}\\n\\n`;\n\n // Read existing history or create new\n let existing = '';\n try {\n existing = fs.readFileSync(historyPath, 'utf-8');\n } catch {\n existing = '# UI Context Capture History\\n\\n';\n }\n\n // Insert after the header line\n const headerEnd = existing.indexOf('\\n\\n');\n if (headerEnd !== -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n existing = header + entry + body;\n } else {\n existing += entry;\n }\n\n // Keep only last 20 entries\n const entries = existing.split(/(?=### )/);\n const header = entries[0]; // everything before first ###\n const captureEntries = entries.slice(1);\n if (captureEntries.length > 20) {\n existing = header + captureEntries.slice(0, 20).join('');\n }\n\n fs.writeFileSync(historyPath, existing, 'utf-8');\n}\n\nfunction cleanupOldCaptures(capturesDir: string, maxFiles: number): void {\n try {\n const files = fs.readdirSync(capturesDir)\n .filter(f => f.endsWith('.md'))\n .sort()\n .reverse();\n\n for (let i = maxFiles; i < files.length; i++) {\n fs.unlinkSync(path.join(capturesDir, files[i]));\n }\n } catch {\n // ignore cleanup errors\n }\n}\n\nfunction readSourceSnippet(filePath: string, targetLine: number, contextLines: number): string {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const lines = content.split('\\n');\n const start = Math.max(0, targetLine - contextLines - 1);\n const end = Math.min(lines.length, targetLine + contextLines);\n\n return lines\n .slice(start, end)\n .map((line, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === targetLine ? ' // <-- target' : '';\n return `${line}${marker}`;\n })\n .join('\\n');\n } catch {\n return '// Source file not found';\n }\n}\n\nfunction buildMarkdown(\n context: ElementContext,\n sourceSnippet: string,\n screenshotPath?: string,\n): string {\n const { source, tagName, classes, computedStyles, dimensions, componentHierarchy, url } = context;\n\n const lines: string[] = ['# UI Context Capture', ''];\n\n // Target Element section\n lines.push('## Target Element');\n if (source) {\n if (source.component) {\n lines.push(`- **Component**: \\`${source.component}\\` in \\`${source.file}:${source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${source.file}:${source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${url}`);\n lines.push(`- **Tag**: \\`<${tagName}>\\``);\n lines.push('');\n\n // Source Code section\n if (source && sourceSnippet) {\n const startLine = Math.max(1, source.line - 10);\n const endLine = source.line + 10;\n lines.push('## Source Code');\n lines.push('```tsx');\n lines.push(`// ${source.file}:${startLine}-${endLine}`);\n lines.push(sourceSnippet);\n lines.push('```');\n lines.push('');\n }\n\n // Styling section\n lines.push('## Styling');\n if (classes) {\n lines.push(`- **Classes**: \\`${classes}\\``);\n }\n\n const styleEntries = Object.entries(computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n\n lines.push(`- **Dimensions**: ${Math.round(dimensions.width)}x${Math.round(dimensions.height)}px at (${Math.round(dimensions.x)}, ${Math.round(dimensions.y)})`);\n lines.push('');\n\n // Component Hierarchy section\n if (componentHierarchy.length > 0) {\n lines.push('## Component Hierarchy');\n lines.push(componentHierarchy.join(' > '));\n lines.push('');\n }\n\n // Screenshot section\n if (screenshotPath) {\n lines.push('## Screenshot');\n lines.push(`![element](${screenshotPath})`);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nfunction buildMultiMarkdown(\n contexts: ElementContext[],\n sourceSnippets: string[],\n screenshotPaths: (string | undefined)[],\n): string {\n const lines: string[] = [\n `# UI Context Capture (${contexts.length} elements)`,\n '',\n ];\n\n for (let i = 0; i < contexts.length; i++) {\n const ctx = contexts[i];\n const snippet = sourceSnippets[i];\n const screenshot = screenshotPaths[i];\n\n lines.push(`---`);\n lines.push('');\n lines.push(`## Element ${i + 1}: \\`<${ctx.tagName}>\\`${ctx.source?.component ? ` (${ctx.source.component})` : ''}`);\n lines.push('');\n\n if (ctx.source) {\n if (ctx.source.component) {\n lines.push(`- **Component**: \\`${ctx.source.component}\\` in \\`${ctx.source.file}:${ctx.source.line}\\``);\n } else {\n lines.push(`- **File**: \\`${ctx.source.file}:${ctx.source.line}\\``);\n }\n }\n lines.push(`- **URL**: ${ctx.url}`);\n lines.push('');\n\n if (ctx.source && snippet) {\n const startLine = Math.max(1, ctx.source.line - 10);\n const endLine = ctx.source.line + 10;\n lines.push('### Source Code');\n lines.push('```tsx');\n lines.push(`// ${ctx.source.file}:${startLine}-${endLine}`);\n lines.push(snippet);\n lines.push('```');\n lines.push('');\n }\n\n lines.push('### Styling');\n if (ctx.classes) {\n lines.push(`- **Classes**: \\`${ctx.classes}\\``);\n }\n const styleEntries = Object.entries(ctx.computedStyles);\n if (styleEntries.length > 0) {\n lines.push(`- **Computed**: ${styleEntries.map(([k, v]) => `${camelToKebab(k)}: ${v}`).join(', ')}`);\n }\n lines.push(`- **Dimensions**: ${Math.round(ctx.dimensions.width)}x${Math.round(ctx.dimensions.height)}px at (${Math.round(ctx.dimensions.x)}, ${Math.round(ctx.dimensions.y)})`);\n lines.push('');\n\n if (ctx.componentHierarchy.length > 0) {\n lines.push('### Component Hierarchy');\n lines.push(ctx.componentHierarchy.join(' > '));\n lines.push('');\n }\n\n if (screenshot) {\n lines.push('### Screenshot');\n lines.push(`![element ${i + 1}](${screenshot})`);\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());\n}\n","import { exec } from 'child_process';\nimport { platform } from 'os';\n\nexport function copyToClipboard(text: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const os = platform();\n let cmd: string;\n\n if (os === 'darwin') {\n cmd = 'pbcopy';\n } else if (os === 'linux') {\n cmd = 'xclip -selection clipboard';\n } else if (os === 'win32') {\n cmd = 'clip';\n } else {\n reject(new Error(`Unsupported platform: ${os}`));\n return;\n }\n\n const proc = exec(cmd, (err) => {\n if (err) reject(err);\n else resolve();\n });\n\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n","import type { ViteDevServer } from 'vite';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { ElementContext, UIContextKitOptions } from './types';\n\nexport function setupWebSocket(server: ViteDevServer, options: UIContextKitOptions): void {\n const root = server.config.root;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n\n server.ws.on('ui-context:capture', async (data: ElementContext | ElementContext[], client) => {\n try {\n const count = Array.isArray(data) ? data.length : 1;\n console.log(`[ui-context-kit] Received context capture (${count} element${count > 1 ? 's' : ''})`);\n\n const filePath = await writeContext(data, root, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch (e) {\n console.warn('[ui-context-kit] Could not copy to clipboard:', e);\n }\n }\n\n client.send('ui-context:capture-result', { success: true });\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n client.send('ui-context:capture-result', { success: false, error: err.message });\n }\n });\n}\n","import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { startContextServer } from './next-server';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Resolves the path to the next-loader.cjs file in the dist directory.\n */\nfunction resolveLoaderPath(): string {\n try {\n // ESM: import.meta.url is available\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, 'next-loader.cjs');\n } catch {\n // CJS fallback: __dirname is available\n return path.resolve(__dirname, 'next-loader.cjs');\n }\n}\n\n/**\n * Wraps a Next.js config to add ui-context-kit source attribute injection in development.\n * Supports both webpack and Turbopack.\n *\n * Also starts a standalone HTTP context server (port 19638) so the browser overlay\n * can send captured context without Vite's HMR WebSocket.\n *\n * Usage:\n * ```js\n * // next.config.mjs\n * import { withUIContext } from '@ui-context-kit/bridge';\n * export default withUIContext({ /* your config *\\/ });\n * ```\n */\nexport function withUIContext(nextConfig: any = {}, options?: UIContextKitOptions) {\n if (process.env.NODE_ENV !== 'development') return nextConfig;\n\n // Start the standalone context server for receiving captures\n startContextServer(options);\n\n const loaderPath = resolveLoaderPath();\n\n return {\n ...nextConfig,\n\n // Turbopack support\n experimental: {\n ...nextConfig.experimental,\n turbo: {\n ...nextConfig.experimental?.turbo,\n rules: {\n ...nextConfig.experimental?.turbo?.rules,\n '*.tsx': {\n loaders: [loaderPath],\n as: '*.tsx',\n },\n '*.jsx': {\n loaders: [loaderPath],\n as: '*.jsx',\n },\n },\n },\n },\n\n // Webpack support\n webpack(config: any, context: any) {\n config.module.rules.unshift({\n test: /\\.(jsx|tsx)$/,\n exclude: /node_modules/,\n enforce: 'pre' as const,\n use: [{ loader: loaderPath }],\n });\n\n if (typeof nextConfig.webpack === 'function') {\n config = nextConfig.webpack(config, context);\n }\n\n return config;\n },\n };\n}\n","import http from 'http';\nimport { writeContext } from './context-writer';\nimport { copyToClipboard } from './clipboard';\nimport type { UIContextKitOptions } from './types';\n\nconst DEFAULT_PORT = 19638;\nconst HOST = '127.0.0.1';\nconst MAX_RETRIES = 10;\nconst RETRY_DELAY_MS = 1000;\n\nconst GLOBAL_KEY = Symbol.for('__ui_context_kit_server__');\nconst EXIT_HANDLER_KEY = Symbol.for('__ui_context_kit_exit_handler__');\n\ninterface ServerState {\n server: http.Server;\n port: number;\n}\n\nfunction getGlobalServer(): ServerState | undefined {\n return (globalThis as any)[GLOBAL_KEY];\n}\n\nfunction setGlobalServer(state: ServerState | null): void {\n (globalThis as any)[GLOBAL_KEY] = state;\n}\n\n/**\n * Starts a standalone HTTP server for receiving UI context captures.\n * Used by Next.js projects where Vite's HMR WebSocket is not available.\n *\n * Uses globalThis singleton to survive module re-evaluation by requireFromString().\n * On EADDRINUSE (stale socket from previous dev server), retries up to 10 times\n * with 1-second intervals.\n */\nexport function startContextServer(options: UIContextKitOptions = {}): void {\n // Check globalThis singleton first — survives requireFromString() re-evaluation\n if (getGlobalServer()) return;\n\n // Don't start in Next.js helper processes (telemetry flush, etc.)\n // These are short-lived detached processes that also load next.config.ts.\n // If they grab the port, the actual dev server can't bind and the server\n // dies when the helper exits.\n const script = process.argv[1] || '';\n if (script.includes('detached-flush') || script.includes('/telemetry/')) {\n return;\n }\n\n const port = DEFAULT_PORT;\n const outputDir = options.outputDir || '.ui-context';\n const shouldCopy = options.clipboard !== false;\n const projectRoot = process.cwd();\n\n // Register exit handler once per process\n if (!(globalThis as any)[EXIT_HANDLER_KEY]) {\n (globalThis as any)[EXIT_HANDLER_KEY] = true;\n process.on('exit', () => {\n const state = getGlobalServer();\n if (state) {\n try {\n state.server.close();\n } catch {\n // best-effort cleanup\n }\n setGlobalServer(null);\n }\n });\n }\n\n tryStart(port, outputDir, shouldCopy, projectRoot, 0);\n}\n\nfunction tryStart(\n port: number,\n outputDir: string,\n shouldCopy: boolean,\n projectRoot: string,\n attempt: number,\n): void {\n // Re-check globalThis in case another call succeeded while we were waiting\n if (getGlobalServer()) return;\n\n const server = http.createServer((req, res) => {\n // CORS headers for cross-port requests\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n // Health check endpoint\n if (req.method === 'GET' && req.url === '/__health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n\n if (req.method === 'POST' && req.url === '/capture') {\n let body = '';\n req.on('data', (chunk: string) => {\n body += chunk;\n });\n req.on('end', async () => {\n try {\n const data = JSON.parse(body);\n console.log(\n `[ui-context-kit] Received context capture (${Array.isArray(data) ? data.length : 1} element${Array.isArray(data) && data.length > 1 ? 's' : ''})`,\n );\n\n const filePath = await writeContext(data, projectRoot, outputDir);\n\n if (shouldCopy) {\n try {\n const fs = await import('fs');\n const content = fs.readFileSync(filePath, 'utf-8');\n await copyToClipboard(content);\n console.log('[ui-context-kit] Context copied to clipboard');\n } catch {\n // clipboard copy is best-effort\n }\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: true }));\n } catch (err: any) {\n console.error('[ui-context-kit] Error writing context:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: false, error: err.message }));\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n server.listen(port, HOST, () => {\n console.log(`[ui-context-kit] Context server running on http://${HOST}:${port}`);\n setGlobalServer({ server, port });\n });\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n if (attempt < MAX_RETRIES) {\n console.log(\n `[ui-context-kit] Port ${port} in use, retrying in ${RETRY_DELAY_MS}ms (${attempt + 1}/${MAX_RETRIES})...`,\n );\n setTimeout(() => tryStart(port, outputDir, shouldCopy, projectRoot, attempt + 1), RETRY_DELAY_MS);\n } else {\n console.error(\n `[ui-context-kit] Failed to start context server after ${MAX_RETRIES} retries. Port ${port} is still occupied.`,\n );\n }\n } else {\n console.error('[ui-context-kit] Context server error:', err);\n }\n });\n}\n","import { createVitePlugin } from './vite-plugin';\nimport { writeContext } from './context-writer';\nimport { withUIContext } from './next-wrapper';\nimport type { UIContextKitOptions } from './types';\n\n/**\n * Vite plugin for UI Context Kit.\n * Enables browser-based element selection and context capture for AI coding assistants.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { uiContextKit } from '@ui-context-kit/bridge'\n * export default defineConfig({\n * plugins: [react(), uiContextKit()]\n * })\n * ```\n */\nexport function uiContextKit(options?: UIContextKitOptions) {\n return createVitePlugin(options);\n}\n\nexport { writeContext, withUIContext };\nexport type { UIContextKitOptions, ElementContext, SourceLocation } from './types';\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["path","fs","timestamp","header","fs","path","fs","path","fs","__dirname","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui-context-kit/bridge",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Server bridge for ui-context-kit - writes context files for AI coding assistants",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -19,17 +19,13 @@
19
19
  "files": [
20
20
  "dist"
21
21
  ],
22
- "scripts": {
23
- "build": "tsup",
24
- "dev": "tsup --watch"
25
- },
26
22
  "dependencies": {
27
23
  "@babel/parser": "^7.24.0",
28
24
  "@babel/traverse": "^7.24.0",
29
25
  "@babel/types": "^7.24.0",
30
- "@ui-context-kit/babel-plugin": "workspace:*",
31
- "@ui-context-kit/overlay": "workspace:*",
32
- "magic-string": "^0.30.8"
26
+ "magic-string": "^0.30.8",
27
+ "@ui-context-kit/overlay": "0.2.1",
28
+ "@ui-context-kit/babel-plugin": "0.1.0"
33
29
  },
34
30
  "peerDependencies": {
35
31
  "vite": ">=5.0.0"
@@ -44,5 +40,9 @@
44
40
  "tsup": "^8.0.0",
45
41
  "typescript": "^5.4.0",
46
42
  "vite": "^5.4.0"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "dev": "tsup --watch"
47
47
  }
48
- }
48
+ }