@ui-context-kit/bridge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +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"],"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\n if (e.altKey === needsAlt && e.ctrlKey === needsCtrl && e.shiftKey === needsShift && 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 type { UIContextKitOptions } from './types';\n\n/**\n * Wraps a Next.js config to add ui-context-kit babel plugin in development.\n *\n * Usage:\n * ```js\n * // next.config.js\n * const { withUIContext } = require('@ui-context-kit/bridge');\n * module.exports = withUIContext(nextConfig);\n * ```\n */\nexport function withUIContext(nextConfig: any = {}, _options?: UIContextKitOptions) {\n if (process.env.NODE_ENV !== 'development') return nextConfig;\n\n return {\n ...nextConfig,\n webpack(config: any, context: any) {\n // Add babel-loader with our plugin for JSX/TSX files\n // Note: This is a simplified approach. For production use,\n // users should configure babel.config.js directly.\n\n if (typeof nextConfig.webpack === 'function') {\n config = nextConfig.webpack(config, context);\n }\n\n return config;\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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmf5B;;;AI1kBO,SAAS,cAAc,aAAkB,CAAC,GAAG,UAAgC;AAClF,MAAI,QAAQ,IAAI,aAAa,cAAe,QAAO;AAEnD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,QAAa,SAAc;AAKjC,UAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,iBAAS,WAAW,QAAQ,QAAQ,OAAO;AAAA,MAC7C;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ALXO,SAAS,aAAa,SAA+B;AAC1D,SAAO,iBAAiB,OAAO;AACjC;","names":["import_path","import_fs","path","fs","timestamp","header","fs","path","fs"]}
@@ -0,0 +1,65 @@
1
+ import * as vite from 'vite';
2
+
3
+ interface SourceLocation {
4
+ file: string;
5
+ line: number;
6
+ col: number;
7
+ component?: string;
8
+ }
9
+ interface ElementContext {
10
+ source: SourceLocation | null;
11
+ tagName: string;
12
+ classes: string;
13
+ computedStyles: Record<string, string>;
14
+ dimensions: {
15
+ width: number;
16
+ height: number;
17
+ x: number;
18
+ y: number;
19
+ };
20
+ componentHierarchy: string[];
21
+ url: string;
22
+ screenshotDataUrl?: string;
23
+ }
24
+ interface UIContextKitOptions {
25
+ /** Toggle shortcut. Default: 'alt+x' */
26
+ shortcut?: string;
27
+ /** Output directory. Default: '.ui-context' */
28
+ outputDir?: string;
29
+ /** Copy to clipboard. Default: true */
30
+ clipboard?: boolean;
31
+ /** Include/exclude file patterns for babel transform */
32
+ include?: string[];
33
+ exclude?: string[];
34
+ }
35
+
36
+ declare function writeContext(context: ElementContext | ElementContext[], projectRoot: string, outputDir: string): Promise<string>;
37
+
38
+ /**
39
+ * Wraps a Next.js config to add ui-context-kit babel plugin in development.
40
+ *
41
+ * Usage:
42
+ * ```js
43
+ * // next.config.js
44
+ * const { withUIContext } = require('@ui-context-kit/bridge');
45
+ * module.exports = withUIContext(nextConfig);
46
+ * ```
47
+ */
48
+ declare function withUIContext(nextConfig?: any, _options?: UIContextKitOptions): any;
49
+
50
+ /**
51
+ * Vite plugin for UI Context Kit.
52
+ * Enables browser-based element selection and context capture for AI coding assistants.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * // vite.config.ts
57
+ * import { uiContextKit } from '@ui-context-kit/bridge'
58
+ * export default defineConfig({
59
+ * plugins: [react(), uiContextKit()]
60
+ * })
61
+ * ```
62
+ */
63
+ declare function uiContextKit(options?: UIContextKitOptions): vite.Plugin<any>[];
64
+
65
+ export { type ElementContext, type SourceLocation, type UIContextKitOptions, uiContextKit, withUIContext, writeContext };
@@ -0,0 +1,65 @@
1
+ import * as vite from 'vite';
2
+
3
+ interface SourceLocation {
4
+ file: string;
5
+ line: number;
6
+ col: number;
7
+ component?: string;
8
+ }
9
+ interface ElementContext {
10
+ source: SourceLocation | null;
11
+ tagName: string;
12
+ classes: string;
13
+ computedStyles: Record<string, string>;
14
+ dimensions: {
15
+ width: number;
16
+ height: number;
17
+ x: number;
18
+ y: number;
19
+ };
20
+ componentHierarchy: string[];
21
+ url: string;
22
+ screenshotDataUrl?: string;
23
+ }
24
+ interface UIContextKitOptions {
25
+ /** Toggle shortcut. Default: 'alt+x' */
26
+ shortcut?: string;
27
+ /** Output directory. Default: '.ui-context' */
28
+ outputDir?: string;
29
+ /** Copy to clipboard. Default: true */
30
+ clipboard?: boolean;
31
+ /** Include/exclude file patterns for babel transform */
32
+ include?: string[];
33
+ exclude?: string[];
34
+ }
35
+
36
+ declare function writeContext(context: ElementContext | ElementContext[], projectRoot: string, outputDir: string): Promise<string>;
37
+
38
+ /**
39
+ * Wraps a Next.js config to add ui-context-kit babel plugin in development.
40
+ *
41
+ * Usage:
42
+ * ```js
43
+ * // next.config.js
44
+ * const { withUIContext } = require('@ui-context-kit/bridge');
45
+ * module.exports = withUIContext(nextConfig);
46
+ * ```
47
+ */
48
+ declare function withUIContext(nextConfig?: any, _options?: UIContextKitOptions): any;
49
+
50
+ /**
51
+ * Vite plugin for UI Context Kit.
52
+ * Enables browser-based element selection and context capture for AI coding assistants.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * // vite.config.ts
57
+ * import { uiContextKit } from '@ui-context-kit/bridge'
58
+ * export default defineConfig({
59
+ * plugins: [react(), uiContextKit()]
60
+ * })
61
+ * ```
62
+ */
63
+ declare function uiContextKit(options?: UIContextKitOptions): vite.Plugin<any>[];
64
+
65
+ export { type ElementContext, type SourceLocation, type UIContextKitOptions, uiContextKit, withUIContext, writeContext };