UNPKG

element-prompt-builder

Version:

React component library for inspecting DOM elements and generating AI prompts

1 lines 138 kB
{"version":3,"sources":["#style-inject:#style-inject","../src/styles/global.css","../src/ElementSelector.tsx","../src/utils.ts","../src/ElementHighlighter.tsx","../src/ElementInspector.tsx","../src/constants.ts","../src/components/bubble-menu/MenuArrow.tsx","../src/components/MentionChat.tsx","../src/stores/workflows.ts","../src/stores/elementEvent.ts","../src/components/ui/mention.tsx","../src/Icons.tsx","../src/components/PromptForm.tsx","../src/components/bubble-menu/ExpandedMenu.tsx","../src/components/BubbleMenuButton.tsx","../src/components/bubble-menu/FloatingButton.tsx","../src/components/bubble-menu/BubbleMenu.tsx","../src/components/ElementTagLabel.tsx","../src/components/highlights/HoveredElement.tsx","../src/components/highlights/SelectedElements.tsx","../src/components/InspectionOverlay.tsx","../src/hooks/useDarkMode.ts","../src/hooks/useElementBubblePosition.ts","../src/hooks/useElementSelection.ts","../src/hooks/useIframeMessaging.ts","../src/hooks/useInspector.ts"],"sourcesContent":["\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\"/*! tailwindcss v4.1.10 | MIT License | https://tailwindcss.com */\\n@layer properties;\\n@layer theme, base, components, utilities;\\n@layer theme {\\n :root,\\n :host {\\n --font-sans:\\n ui-sans-serif,\\n system-ui,\\n sans-serif,\\n \\\"Apple Color Emoji\\\",\\n \\\"Segoe UI Emoji\\\",\\n \\\"Segoe UI Symbol\\\",\\n \\\"Noto Color Emoji\\\";\\n --font-mono:\\n ui-monospace,\\n SFMono-Regular,\\n Menlo,\\n Monaco,\\n Consolas,\\n \\\"Liberation Mono\\\",\\n \\\"Courier New\\\",\\n monospace;\\n --color-red-400: oklch(70.4% 0.191 22.216);\\n --color-red-700: oklch(50.5% 0.213 27.518);\\n --color-blue-50: oklch(97% 0.014 254.604);\\n --color-blue-200: oklch(88.2% 0.059 254.128);\\n --color-blue-500: oklch(62.3% 0.214 259.815);\\n --color-blue-700: oklch(48.8% 0.243 264.376);\\n --color-blue-800: oklch(42.4% 0.199 265.638);\\n --color-blue-950: oklch(28.2% 0.091 267.935);\\n --color-indigo-500: oklch(58.5% 0.233 277.117);\\n --color-gray-300: oklch(87.2% 0.01 258.338);\\n --color-gray-500: oklch(55.1% 0.027 264.364);\\n --color-gray-600: oklch(44.6% 0.03 256.802);\\n --color-gray-700: oklch(37.3% 0.034 259.733);\\n --color-gray-800: oklch(27.8% 0.033 256.848);\\n --color-black: #000;\\n --color-white: #fff;\\n --spacing: 0.25rem;\\n --text-xs: 0.75rem;\\n --text-xs--line-height: calc(1 / 0.75);\\n --text-sm: 0.875rem;\\n --text-sm--line-height: calc(1.25 / 0.875);\\n --text-base: 1rem;\\n --text-base--line-height: calc(1.5 / 1);\\n --font-weight-medium: 500;\\n --font-weight-semibold: 600;\\n --radius-sm: 0.25rem;\\n --radius-md: 0.375rem;\\n --radius-xl: 0.75rem;\\n --animate-spin: spin 1s linear infinite;\\n --default-transition-duration: 150ms;\\n --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\\n --default-font-family: var(--font-sans);\\n --default-mono-font-family: var(--font-mono);\\n }\\n}\\n@layer base {\\n *,\\n ::after,\\n ::before,\\n ::backdrop,\\n ::file-selector-button {\\n box-sizing: border-box;\\n margin: 0;\\n padding: 0;\\n border: 0 solid;\\n }\\n html,\\n :host {\\n line-height: 1.5;\\n -webkit-text-size-adjust: 100%;\\n -moz-tab-size: 4;\\n -o-tab-size: 4;\\n tab-size: 4;\\n font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, \\\"Apple Color Emoji\\\", \\\"Segoe UI Emoji\\\", \\\"Segoe UI Symbol\\\", \\\"Noto Color Emoji\\\");\\n font-feature-settings: var(--default-font-feature-settings, normal);\\n font-variation-settings: var(--default-font-variation-settings, normal);\\n -webkit-tap-highlight-color: transparent;\\n }\\n hr {\\n height: 0;\\n color: inherit;\\n border-top-width: 1px;\\n }\\n abbr:where([title]) {\\n -webkit-text-decoration: underline dotted;\\n text-decoration: underline dotted;\\n }\\n h1,\\n h2,\\n h3,\\n h4,\\n h5,\\n h6 {\\n font-size: inherit;\\n font-weight: inherit;\\n }\\n a {\\n color: inherit;\\n -webkit-text-decoration: inherit;\\n text-decoration: inherit;\\n }\\n b,\\n strong {\\n font-weight: bolder;\\n }\\n code,\\n kbd,\\n samp,\\n pre {\\n font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \\\"Liberation Mono\\\", \\\"Courier New\\\", monospace);\\n font-feature-settings: var(--default-mono-font-feature-settings, normal);\\n font-variation-settings: var(--default-mono-font-variation-settings, normal);\\n font-size: 1em;\\n }\\n small {\\n font-size: 80%;\\n }\\n sub,\\n sup {\\n font-size: 75%;\\n line-height: 0;\\n position: relative;\\n vertical-align: baseline;\\n }\\n sub {\\n bottom: -0.25em;\\n }\\n sup {\\n top: -0.5em;\\n }\\n table {\\n text-indent: 0;\\n border-color: inherit;\\n border-collapse: collapse;\\n }\\n :-moz-focusring {\\n outline: auto;\\n }\\n progress {\\n vertical-align: baseline;\\n }\\n summary {\\n display: list-item;\\n }\\n ol,\\n ul,\\n menu {\\n list-style: none;\\n }\\n img,\\n svg,\\n video,\\n canvas,\\n audio,\\n iframe,\\n embed,\\n object {\\n display: block;\\n vertical-align: middle;\\n }\\n img,\\n video {\\n max-width: 100%;\\n height: auto;\\n }\\n button,\\n input,\\n select,\\n optgroup,\\n textarea,\\n ::file-selector-button {\\n font: inherit;\\n font-feature-settings: inherit;\\n font-variation-settings: inherit;\\n letter-spacing: inherit;\\n color: inherit;\\n border-radius: 0;\\n background-color: transparent;\\n opacity: 1;\\n }\\n :where(select:is([multiple], [size])) optgroup {\\n font-weight: bolder;\\n }\\n :where(select:is([multiple], [size])) optgroup option {\\n padding-inline-start: 20px;\\n }\\n ::file-selector-button {\\n margin-inline-end: 4px;\\n }\\n ::-moz-placeholder {\\n opacity: 1;\\n }\\n ::placeholder {\\n opacity: 1;\\n }\\n @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\\n ::-moz-placeholder {\\n color: currentcolor;\\n @supports (color: color-mix(in lab, red, red)) {\\n color: color-mix(in oklab, currentcolor 50%, transparent);\\n }\\n }\\n ::placeholder {\\n color: currentcolor;\\n @supports (color: color-mix(in lab, red, red)) {\\n color: color-mix(in oklab, currentcolor 50%, transparent);\\n }\\n }\\n }\\n textarea {\\n resize: vertical;\\n }\\n ::-webkit-search-decoration {\\n -webkit-appearance: none;\\n }\\n ::-webkit-date-and-time-value {\\n min-height: 1lh;\\n text-align: inherit;\\n }\\n ::-webkit-datetime-edit {\\n display: inline-flex;\\n }\\n ::-webkit-datetime-edit-fields-wrapper {\\n padding: 0;\\n }\\n ::-webkit-datetime-edit,\\n ::-webkit-datetime-edit-year-field,\\n ::-webkit-datetime-edit-month-field,\\n ::-webkit-datetime-edit-day-field,\\n ::-webkit-datetime-edit-hour-field,\\n ::-webkit-datetime-edit-minute-field,\\n ::-webkit-datetime-edit-second-field,\\n ::-webkit-datetime-edit-millisecond-field,\\n ::-webkit-datetime-edit-meridiem-field {\\n padding-block: 0;\\n }\\n :-moz-ui-invalid {\\n box-shadow: none;\\n }\\n button,\\n input:where([type=button], [type=reset], [type=submit]),\\n ::file-selector-button {\\n -webkit-appearance: button;\\n -moz-appearance: button;\\n appearance: button;\\n }\\n ::-webkit-inner-spin-button,\\n ::-webkit-outer-spin-button {\\n height: auto;\\n }\\n [hidden]:where(:not([hidden=until-found])) {\\n display: none !important;\\n }\\n}\\n@layer utilities {\\n .pointer-events-auto {\\n pointer-events: auto;\\n }\\n .pointer-events-none {\\n pointer-events: none;\\n }\\n .visible {\\n visibility: visible;\\n }\\n .sr-only {\\n position: absolute;\\n width: 1px;\\n height: 1px;\\n padding: 0;\\n margin: -1px;\\n overflow: hidden;\\n clip: rect(0, 0, 0, 0);\\n white-space: nowrap;\\n border-width: 0;\\n }\\n .absolute {\\n position: absolute;\\n }\\n .fixed {\\n position: fixed;\\n }\\n .relative {\\n position: relative;\\n }\\n .inset-0 {\\n inset: calc(var(--spacing) * 0);\\n }\\n .inset-y-0 {\\n inset-block: calc(var(--spacing) * 0);\\n }\\n .top-0 {\\n top: calc(var(--spacing) * 0);\\n }\\n .top-\\\\[0\\\\.5px\\\\] {\\n top: 0.5px;\\n }\\n .right-0 {\\n right: calc(var(--spacing) * 0);\\n }\\n .right-6 {\\n right: calc(var(--spacing) * 6);\\n }\\n .bottom-6 {\\n bottom: calc(var(--spacing) * 6);\\n }\\n .left-\\\\[0\\\\.5px\\\\] {\\n left: 0.5px;\\n }\\n .z-50 {\\n z-index: 50;\\n }\\n .z-\\\\[9998\\\\] {\\n z-index: 9998;\\n }\\n .z-\\\\[9999\\\\] {\\n z-index: 9999;\\n }\\n .z-\\\\[10000\\\\] {\\n z-index: 10000;\\n }\\n .z-\\\\[10001\\\\] {\\n z-index: 10001;\\n }\\n .container {\\n width: 100%;\\n @media (width >= 40rem) {\\n max-width: 40rem;\\n }\\n @media (width >= 48rem) {\\n max-width: 48rem;\\n }\\n @media (width >= 64rem) {\\n max-width: 64rem;\\n }\\n @media (width >= 80rem) {\\n max-width: 80rem;\\n }\\n @media (width >= 96rem) {\\n max-width: 96rem;\\n }\\n }\\n .mt-3 {\\n margin-top: calc(var(--spacing) * 3);\\n }\\n .mr-2 {\\n margin-right: calc(var(--spacing) * 2);\\n }\\n .mb-2 {\\n margin-bottom: calc(var(--spacing) * 2);\\n }\\n .mb-4 {\\n margin-bottom: calc(var(--spacing) * 4);\\n }\\n .mb-6 {\\n margin-bottom: calc(var(--spacing) * 6);\\n }\\n .-ml-1 {\\n margin-left: calc(var(--spacing) * -1);\\n }\\n .block {\\n display: block;\\n }\\n .flex {\\n display: flex;\\n }\\n .inline {\\n display: inline;\\n }\\n .inline-flex {\\n display: inline-flex;\\n }\\n .table {\\n display: table;\\n }\\n .field-sizing-content {\\n field-sizing: content;\\n }\\n .h-0 {\\n height: calc(var(--spacing) * 0);\\n }\\n .h-4 {\\n height: calc(var(--spacing) * 4);\\n }\\n .h-6 {\\n height: calc(var(--spacing) * 6);\\n }\\n .h-8 {\\n height: calc(var(--spacing) * 8);\\n }\\n .h-screen {\\n height: 100vh;\\n }\\n .min-h-16 {\\n min-height: calc(var(--spacing) * 16);\\n }\\n .w-0 {\\n width: calc(var(--spacing) * 0);\\n }\\n .w-4 {\\n width: calc(var(--spacing) * 4);\\n }\\n .w-6 {\\n width: calc(var(--spacing) * 6);\\n }\\n .w-8 {\\n width: calc(var(--spacing) * 8);\\n }\\n .w-\\\\[350px\\\\] {\\n width: 350px;\\n }\\n .w-full {\\n width: 100%;\\n }\\n .w-screen {\\n width: 100vw;\\n }\\n .min-w-\\\\[8rem\\\\] {\\n min-width: 8rem;\\n }\\n .min-w-\\\\[300px\\\\] {\\n min-width: 300px;\\n }\\n .border-collapse {\\n border-collapse: collapse;\\n }\\n .-translate-y-1 {\\n --tw-translate-y: calc(var(--spacing) * -1);\\n translate: var(--tw-translate-x) var(--tw-translate-y);\\n }\\n .transform {\\n transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);\\n }\\n .animate-spin {\\n animation: var(--animate-spin);\\n }\\n .cursor-cell {\\n cursor: cell;\\n }\\n .cursor-default {\\n cursor: default;\\n }\\n .cursor-not-allowed {\\n cursor: not-allowed;\\n }\\n .cursor-pointer {\\n cursor: pointer;\\n }\\n .resize {\\n resize: both;\\n }\\n .resize-none {\\n resize: none;\\n }\\n .appearance-none {\\n -webkit-appearance: none;\\n -moz-appearance: none;\\n appearance: none;\\n }\\n .items-center {\\n align-items: center;\\n }\\n .justify-between {\\n justify-content: space-between;\\n }\\n .justify-center {\\n justify-content: center;\\n }\\n .gap-1 {\\n gap: calc(var(--spacing) * 1);\\n }\\n .gap-1\\\\.5 {\\n gap: calc(var(--spacing) * 1.5);\\n }\\n .gap-2 {\\n gap: calc(var(--spacing) * 2);\\n }\\n .overflow-hidden {\\n overflow: hidden;\\n }\\n .overflow-y-auto {\\n overflow-y: auto;\\n }\\n .rounded {\\n border-radius: 0.25rem;\\n }\\n .rounded-full {\\n border-radius: calc(infinity * 1px);\\n }\\n .rounded-md {\\n border-radius: var(--radius-md);\\n }\\n .rounded-sm {\\n border-radius: var(--radius-sm);\\n }\\n .rounded-xl {\\n border-radius: var(--radius-xl);\\n }\\n .border {\\n border-style: var(--tw-border-style);\\n border-width: 1px;\\n }\\n .border-2 {\\n border-style: var(--tw-border-style);\\n border-width: 2px;\\n }\\n .border-t-8 {\\n border-top-style: var(--tw-border-style);\\n border-top-width: 8px;\\n }\\n .border-r-8 {\\n border-right-style: var(--tw-border-style);\\n border-right-width: 8px;\\n }\\n .border-b-8 {\\n border-bottom-style: var(--tw-border-style);\\n border-bottom-width: 8px;\\n }\\n .border-l-8 {\\n border-left-style: var(--tw-border-style);\\n border-left-width: 8px;\\n }\\n .\\\\!border-none {\\n --tw-border-style: none !important;\\n border-style: none !important;\\n }\\n .border-none {\\n --tw-border-style: none;\\n border-style: none;\\n }\\n .border-gray-300 {\\n border-color: var(--color-gray-300);\\n }\\n .border-red-400 {\\n border-color: var(--color-red-400);\\n }\\n .border-transparent {\\n border-color: transparent;\\n }\\n .border-t-gray-800 {\\n border-top-color: var(--color-gray-800);\\n }\\n .border-t-white {\\n border-top-color: var(--color-white);\\n }\\n .border-b-gray-800 {\\n border-bottom-color: var(--color-gray-800);\\n }\\n .border-b-white {\\n border-bottom-color: var(--color-white);\\n }\\n .bg-\\\\[\\\\#3b82f6\\\\] {\\n background-color: #3b82f6;\\n }\\n .bg-\\\\[\\\\#93c5fd\\\\] {\\n background-color: #93c5fd;\\n }\\n .bg-\\\\[\\\\#2563eb\\\\] {\\n background-color: #2563eb;\\n }\\n .bg-\\\\[rgba\\\\(52\\\\,53\\\\,65\\\\,0\\\\.8\\\\)\\\\] {\\n background-color: rgba(52, 53, 65, 0.8);\\n }\\n .bg-transparent {\\n background-color: transparent;\\n }\\n .bg-white {\\n background-color: var(--color-white);\\n }\\n .fill-black {\\n fill: var(--color-black);\\n }\\n .p-1 {\\n padding: calc(var(--spacing) * 1);\\n }\\n .p-2 {\\n padding: calc(var(--spacing) * 2);\\n }\\n .p-4 {\\n padding: calc(var(--spacing) * 4);\\n }\\n .px-0 {\\n padding-inline: calc(var(--spacing) * 0);\\n }\\n .px-0\\\\.5 {\\n padding-inline: calc(var(--spacing) * 0.5);\\n }\\n .px-1 {\\n padding-inline: calc(var(--spacing) * 1);\\n }\\n .px-1\\\\.5 {\\n padding-inline: calc(var(--spacing) * 1.5);\\n }\\n .px-2 {\\n padding-inline: calc(var(--spacing) * 2);\\n }\\n .px-3 {\\n padding-inline: calc(var(--spacing) * 3);\\n }\\n .px-4 {\\n padding-inline: calc(var(--spacing) * 4);\\n }\\n .py-0 {\\n padding-block: calc(var(--spacing) * 0);\\n }\\n .py-0\\\\.5 {\\n padding-block: calc(var(--spacing) * 0.5);\\n }\\n .py-1 {\\n padding-block: calc(var(--spacing) * 1);\\n }\\n .py-1\\\\.5 {\\n padding-block: calc(var(--spacing) * 1.5);\\n }\\n .py-2 {\\n padding-block: calc(var(--spacing) * 2);\\n }\\n .py-3 {\\n padding-block: calc(var(--spacing) * 3);\\n }\\n .pr-\\\\[40px\\\\] {\\n padding-right: 40px;\\n }\\n .text-base {\\n font-size: var(--text-base);\\n line-height: var(--tw-leading, var(--text-base--line-height));\\n }\\n .text-sm {\\n font-size: var(--text-sm);\\n line-height: var(--tw-leading, var(--text-sm--line-height));\\n }\\n .text-xs {\\n font-size: var(--text-xs);\\n line-height: var(--tw-leading, var(--text-xs--line-height));\\n }\\n .text-\\\\[15px\\\\] {\\n font-size: 15px;\\n }\\n .font-medium {\\n --tw-font-weight: var(--font-weight-medium);\\n font-weight: var(--font-weight-medium);\\n }\\n .font-semibold {\\n --tw-font-weight: var(--font-weight-semibold);\\n font-weight: var(--font-weight-semibold);\\n }\\n .\\\\!whitespace-nowrap {\\n white-space: nowrap !important;\\n }\\n .text-blue-500 {\\n color: var(--color-blue-500);\\n }\\n .text-gray-500 {\\n color: var(--color-gray-500);\\n }\\n .text-gray-700 {\\n color: var(--color-gray-700);\\n }\\n .text-red-400 {\\n color: var(--color-red-400);\\n }\\n .text-white {\\n color: var(--color-white);\\n }\\n .underline {\\n text-decoration-line: underline;\\n }\\n .opacity-0 {\\n opacity: 0%;\\n }\\n .opacity-25 {\\n opacity: 25%;\\n }\\n .opacity-50 {\\n opacity: 50%;\\n }\\n .opacity-75 {\\n opacity: 75%;\\n }\\n .opacity-100 {\\n opacity: 100%;\\n }\\n .\\\\!shadow-none {\\n --tw-shadow: 0 0 #0000 !important;\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow) !important;\\n }\\n .shadow-lg {\\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n .shadow-md {\\n --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n .shadow-sm {\\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n .shadow-xs {\\n --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n .\\\\!ring-0 {\\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor) !important;\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow) !important;\\n }\\n .ring {\\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n .outline-hidden {\\n --tw-outline-style: none;\\n outline-style: none;\\n @media (forced-colors: active) {\\n outline: 2px solid transparent;\\n outline-offset: 2px;\\n }\\n }\\n .outline {\\n outline-style: var(--tw-outline-style);\\n outline-width: 1px;\\n }\\n .transition {\\n transition-property:\\n color,\\n background-color,\\n border-color,\\n outline-color,\\n text-decoration-color,\\n fill,\\n stroke,\\n --tw-gradient-from,\\n --tw-gradient-via,\\n --tw-gradient-to,\\n opacity,\\n box-shadow,\\n transform,\\n translate,\\n scale,\\n rotate,\\n filter,\\n -webkit-backdrop-filter,\\n backdrop-filter,\\n display,\\n visibility,\\n content-visibility,\\n overlay,\\n pointer-events;\\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\\n transition-duration: var(--tw-duration, var(--default-transition-duration));\\n }\\n .transition-\\\\[color\\\\,box-shadow\\\\] {\\n transition-property: color, box-shadow;\\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\\n transition-duration: var(--tw-duration, var(--default-transition-duration));\\n }\\n .transition-all {\\n transition-property: all;\\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\\n transition-duration: var(--tw-duration, var(--default-transition-duration));\\n }\\n .transition-colors {\\n transition-property:\\n color,\\n background-color,\\n border-color,\\n outline-color,\\n text-decoration-color,\\n fill,\\n stroke,\\n --tw-gradient-from,\\n --tw-gradient-via,\\n --tw-gradient-to;\\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\\n transition-duration: var(--tw-duration, var(--default-transition-duration));\\n }\\n .duration-100 {\\n --tw-duration: 100ms;\\n transition-duration: 100ms;\\n }\\n .duration-150 {\\n --tw-duration: 150ms;\\n transition-duration: 150ms;\\n }\\n .duration-200 {\\n --tw-duration: 200ms;\\n transition-duration: 200ms;\\n }\\n .outline-none {\\n --tw-outline-style: none;\\n outline-style: none;\\n }\\n .select-none {\\n -webkit-user-select: none;\\n -moz-user-select: none;\\n user-select: none;\\n }\\n .hover\\\\:border-red-700 {\\n &:hover {\\n @media (hover: hover) {\\n border-color: var(--color-red-700);\\n }\\n }\\n }\\n .hover\\\\:bg-\\\\[\\\\#1d4ed8\\\\] {\\n &:hover {\\n @media (hover: hover) {\\n background-color: #1d4ed8;\\n }\\n }\\n }\\n .hover\\\\:bg-\\\\[\\\\#60a5fa\\\\] {\\n &:hover {\\n @media (hover: hover) {\\n background-color: #60a5fa;\\n }\\n }\\n }\\n .hover\\\\:bg-\\\\[\\\\#2563eb\\\\] {\\n &:hover {\\n @media (hover: hover) {\\n background-color: #2563eb;\\n }\\n }\\n }\\n .hover\\\\:text-blue-700 {\\n &:hover {\\n @media (hover: hover) {\\n color: var(--color-blue-700);\\n }\\n }\\n }\\n .hover\\\\:text-red-700 {\\n &:hover {\\n @media (hover: hover) {\\n color: var(--color-red-700);\\n }\\n }\\n }\\n .focus\\\\:border-indigo-500 {\\n &:focus {\\n border-color: var(--color-indigo-500);\\n }\\n }\\n .focus\\\\:ring-0 {\\n &:focus {\\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n }\\n .focus\\\\:ring-indigo-500 {\\n &:focus {\\n --tw-ring-color: var(--color-indigo-500);\\n }\\n }\\n .focus\\\\:outline-none {\\n &:focus {\\n --tw-outline-style: none;\\n outline-style: none;\\n }\\n }\\n .focus-visible\\\\:ring-1 {\\n &:focus-visible {\\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n }\\n .focus-visible\\\\:ring-\\\\[3px\\\\] {\\n &:focus-visible {\\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\\n box-shadow:\\n var(--tw-inset-shadow),\\n var(--tw-inset-ring-shadow),\\n var(--tw-ring-offset-shadow),\\n var(--tw-ring-shadow),\\n var(--tw-shadow);\\n }\\n }\\n .focus-visible\\\\:outline-hidden {\\n &:focus-visible {\\n --tw-outline-style: none;\\n outline-style: none;\\n @media (forced-colors: active) {\\n outline: 2px solid transparent;\\n outline-offset: 2px;\\n }\\n }\\n }\\n .disabled\\\\:cursor-not-allowed {\\n &:disabled {\\n cursor: not-allowed;\\n }\\n }\\n .disabled\\\\:opacity-50 {\\n &:disabled {\\n opacity: 50%;\\n }\\n }\\n .data-disabled\\\\:pointer-events-none {\\n &[data-disabled] {\\n pointer-events: none;\\n }\\n }\\n .data-disabled\\\\:opacity-50 {\\n &[data-disabled] {\\n opacity: 50%;\\n }\\n }\\n .\\\\*\\\\*\\\\:data-tag\\\\:rounded {\\n :is(& *) {\\n &[data-tag] {\\n border-radius: 0.25rem;\\n }\\n }\\n }\\n .\\\\*\\\\*\\\\:data-tag\\\\:bg-blue-200 {\\n :is(& *) {\\n &[data-tag] {\\n background-color: var(--color-blue-200);\\n }\\n }\\n }\\n .\\\\*\\\\*\\\\:data-tag\\\\:py-px {\\n :is(& *) {\\n &[data-tag] {\\n padding-block: 1px;\\n }\\n }\\n }\\n .\\\\*\\\\*\\\\:data-tag\\\\:text-blue-950 {\\n :is(& *) {\\n &[data-tag] {\\n color: var(--color-blue-950);\\n }\\n }\\n }\\n .md\\\\:text-sm {\\n @media (width >= 48rem) {\\n font-size: var(--text-sm);\\n line-height: var(--tw-leading, var(--text-sm--line-height));\\n }\\n }\\n .dark\\\\:text-gray-600 {\\n @media (prefers-color-scheme: dark) {\\n color: var(--color-gray-600);\\n }\\n }\\n .dark\\\\:\\\\*\\\\*\\\\:data-tag\\\\:bg-blue-800 {\\n @media (prefers-color-scheme: dark) {\\n :is(& *) {\\n &[data-tag] {\\n background-color: var(--color-blue-800);\\n }\\n }\\n }\\n }\\n .dark\\\\:\\\\*\\\\*\\\\:data-tag\\\\:text-blue-50 {\\n @media (prefers-color-scheme: dark) {\\n :is(& *) {\\n &[data-tag] {\\n color: var(--color-blue-50);\\n }\\n }\\n }\\n }\\n}\\nbody span[data-tag] {\\n background-color: #ebf5fb !important;\\n color: #1566a4 !important;\\n border-radius: 4px !important;\\n}\\n.bg-\\\\[\\\\#3b82f6\\\\] {\\n background-color: #3b82f6;\\n}\\n.hover\\\\:bg-\\\\[\\\\#2563eb\\\\]:hover {\\n background-color: #2563eb;\\n}\\n.-translate-y-1\\\\/2 {\\n transform: translateY(-50%);\\n}\\n.border-red-400 {\\n border-color: #ff6467;\\n}\\n.border-t-8 {\\n border-top-width: 8px !important;\\n}\\n.border-b-8 {\\n border-bottom-width: 8px !important;\\n}\\n.border-r-8 {\\n border-right-width: 8px !important;\\n}\\n.border-l-8 {\\n border-left-width: 8px !important;\\n}\\n.border-transparent {\\n border-color: transparent !important;\\n}\\n.border-t-white {\\n border-top-color: white !important;\\n}\\n.border-b-white {\\n border-bottom-color: white !important;\\n}\\n.shadow-lg {\\n box-shadow:\\n 0 0 #0000,\\n 0 0 #0000,\\n 0 0 #0000,\\n 0 0 #0000,\\n 0 10px 15px -3px #0000001a,\\n 0 4px 6px -4px #0000001a;\\n}\\n.text-red-400 {\\n color: #ff6467;\\n}\\n.hover\\\\:border-red-700 {\\n border-color: #c10007;\\n}\\n.hover\\\\:text-red-700 {\\n color: #c10007;\\n}\\n.bg-\\\\[\\\\#4f39f6\\\\] {\\n background-color: #4f39f6;\\n}\\n.hover\\\\:bg-\\\\[\\\\#432dd7\\\\]:hover {\\n background-color: #432dd7;\\n}\\n.text-gray-700 {\\n color: #364153;\\n}\\n.text-xs {\\n font-size: 12px;\\n}\\n.text-white {\\n color: white;\\n}\\n.pr-\\\\[40px\\\\] {\\n padding-right: 40px !important;\\n}\\n.text-blue-500 {\\n color: #2b7fff;\\n}\\n.element-inspector-menu {\\n color: black;\\n}\\n@property --tw-translate-x { syntax: \\\"*\\\"; inherits: false; initial-value: 0; }\\n@property --tw-translate-y { syntax: \\\"*\\\"; inherits: false; initial-value: 0; }\\n@property --tw-translate-z { syntax: \\\"*\\\"; inherits: false; initial-value: 0; }\\n@property --tw-rotate-x { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-rotate-y { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-rotate-z { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-skew-x { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-skew-y { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-border-style { syntax: \\\"*\\\"; inherits: false; initial-value: solid; }\\n@property --tw-font-weight { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-shadow-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-shadow-alpha { syntax: \\\"<percentage>\\\"; inherits: false; initial-value: 100%; }\\n@property --tw-inset-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-inset-shadow-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-inset-shadow-alpha { syntax: \\\"<percentage>\\\"; inherits: false; initial-value: 100%; }\\n@property --tw-ring-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-ring-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-inset-ring-color { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-inset-ring-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-ring-inset { syntax: \\\"*\\\"; inherits: false; }\\n@property --tw-ring-offset-width { syntax: \\\"<length>\\\"; inherits: false; initial-value: 0px; }\\n@property --tw-ring-offset-color { syntax: \\\"*\\\"; inherits: false; initial-value: #fff; }\\n@property --tw-ring-offset-shadow { syntax: \\\"*\\\"; inherits: false; initial-value: 0 0 #0000; }\\n@property --tw-outline-style { syntax: \\\"*\\\"; inherits: false; initial-value: solid; }\\n@property --tw-duration { syntax: \\\"*\\\"; inherits: false; }\\n@keyframes spin {\\n to {\\n transform: rotate(360deg);\\n }\\n}\\n@layer properties {\\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\\n *,\\n ::before,\\n ::after,\\n ::backdrop {\\n --tw-translate-x: 0;\\n --tw-translate-y: 0;\\n --tw-translate-z: 0;\\n --tw-rotate-x: initial;\\n --tw-rotate-y: initial;\\n --tw-rotate-z: initial;\\n --tw-skew-x: initial;\\n --tw-skew-y: initial;\\n --tw-border-style: solid;\\n --tw-font-weight: initial;\\n --tw-shadow: 0 0 #0000;\\n --tw-shadow-color: initial;\\n --tw-shadow-alpha: 100%;\\n --tw-inset-shadow: 0 0 #0000;\\n --tw-inset-shadow-color: initial;\\n --tw-inset-shadow-alpha: 100%;\\n --tw-ring-color: initial;\\n --tw-ring-shadow: 0 0 #0000;\\n --tw-inset-ring-color: initial;\\n --tw-inset-ring-shadow: 0 0 #0000;\\n --tw-ring-inset: initial;\\n --tw-ring-offset-width: 0px;\\n --tw-ring-offset-color: #fff;\\n --tw-ring-offset-shadow: 0 0 #0000;\\n --tw-outline-style: solid;\\n --tw-duration: initial;\\n }\\n }\\n}\\n\")","\"use client\";\nimport React, { useCallback, useRef } from 'react';\nimport { getMostSpecificElementAtPoint } from './utils';\nimport { cn } from \"./utils\";\n\nexport interface ElementSelectorProps {\n /**\n * Callback when an element is hovered\n */\n onElementHovered: (element: HTMLElement) => void;\n \n /**\n * Callback when an element is no longer hovered\n */\n onElementUnhovered: () => void;\n \n /**\n * Callback when an element is selected\n */\n onElementSelected: (element: HTMLElement) => void;\n \n /**\n * List of elements to ignore during selection\n */\n ignoreList?: HTMLElement[];\n \n /**\n * CSS selector for elements to exclude\n */\n excludeSelector?: string;\n \n /**\n * CSS class name for the selector overlay\n */\n className?: string;\n \n /**\n * Custom styles for the selector overlay\n */\n style?: React.CSSProperties;\n}\n\n/**\n * ElementSelector component that creates an overlay to select DOM elements\n */\nexport function ElementSelector({\n onElementHovered,\n onElementUnhovered,\n onElementSelected,\n ignoreList = [],\n excludeSelector = '',\n className = '',\n style = {},\n}: ElementSelectorProps) {\n const lastHoveredElement = useRef<HTMLElement | null>(null);\n\n const handleMouseMove = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n const { clientX, clientY } = event;\n \n // Hide the overlay temporarily to get accurate element detection\n const overlayElement = event.currentTarget;\n if (overlayElement) {\n overlayElement.style.pointerEvents = 'none';\n }\n \n // Get element at this point (with overlay hidden)\n const refElement = getMostSpecificElementAtPoint(clientX, clientY, excludeSelector);\n \n // Restore the overlay\n if (overlayElement) {\n overlayElement.style.pointerEvents = 'auto';\n }\n \n // Ignore if element is in the ignore list\n if (ignoreList.includes(refElement)) return;\n \n // Ignore if element is the overlay itself or a child of it\n if (refElement === overlayElement || \n (overlayElement && overlayElement.contains(refElement))) {\n return;\n }\n \n // Only trigger callback if the hovered element has changed\n if (lastHoveredElement.current !== refElement) {\n lastHoveredElement.current = refElement;\n onElementHovered(refElement);\n }\n },\n [onElementHovered, ignoreList, excludeSelector]\n );\n\n const handleMouseLeave = useCallback(() => {\n lastHoveredElement.current = null;\n onElementUnhovered();\n }, [onElementUnhovered]);\n\n const handleMouseClick = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n // Prevent the default action and stop propagation\n event.preventDefault();\n event.stopPropagation();\n \n const { clientX, clientY } = event;\n \n // Hide the overlay temporarily to get accurate element detection\n const overlayElement = event.currentTarget;\n if (overlayElement) {\n overlayElement.style.pointerEvents = 'none';\n }\n \n // Get the most accurate element at click position\n const clickedElement = getMostSpecificElementAtPoint(clientX, clientY, excludeSelector);\n \n // Restore the overlay\n if (overlayElement) {\n overlayElement.style.pointerEvents = 'auto';\n }\n \n // Don't select if it's the overlay\n if (\n clickedElement === overlayElement || \n (overlayElement && overlayElement.contains(clickedElement))\n ) {\n return;\n }\n \n // Update last hovered element and trigger selection\n lastHoveredElement.current = clickedElement;\n onElementSelected(clickedElement);\n },\n [onElementSelected, ignoreList, excludeSelector]\n );\n\n return (\n <div\n className={cn(\n \"element-selector fixed inset-0 h-screen w-screen cursor-cell z-[9999] pointer-events-auto\",\n className\n )}\n data-element-selector=\"true\"\n style={style}\n onMouseMove={handleMouseMove}\n onMouseLeave={handleMouseLeave}\n onClick={handleMouseClick}\n role=\"button\"\n tabIndex={0}\n />\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility functions for DOM element inspection and manipulation\n */\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Checks if a point is within an element's bounds\n * @param element - The element to check\n * @param clientX - The x coordinate\n * @param clientY - The y coordinate\n * @returns Whether the point is within the element's bounds\n */\nexport const isElementAtPoint = (\n element: HTMLElement,\n clientX: number,\n clientY: number,\n): boolean => {\n const boundingRect = element.getBoundingClientRect();\n\n const isInHorizontalBounds =\n clientX >= boundingRect.left &&\n clientX <= boundingRect.left + boundingRect.width;\n const isInVerticalBounds =\n clientY >= boundingRect.top &&\n clientY <= boundingRect.top + boundingRect.height;\n\n return isInHorizontalBounds && isInVerticalBounds;\n};\n\n/**\n * Calculates the percentage offsets from a point to an element\n * @param refElement - The reference element\n * @param x - The x coordinate\n * @param y - The y coordinate\n * @returns The percentage offsets\n */\nexport function getOffsetsFromPointToElement(\n refElement: HTMLElement,\n x: number,\n y: number,\n): { offsetTop: number; offsetLeft: number } {\n const referenceClientBounds = refElement.getBoundingClientRect();\n\n const offsetTop =\n ((y - referenceClientBounds.top) * 100) / referenceClientBounds.height;\n const offsetLeft =\n ((x - referenceClientBounds.left) * 100) / referenceClientBounds.width;\n\n return {\n offsetTop,\n offsetLeft,\n };\n}\n\n/**\n * Gets the XPath for an element\n * @param element - The element to get the XPath for\n * @param useId - Whether to use the element's ID if available\n * @returns The XPath for the element\n */\nexport const getXPathForElement = (element: HTMLElement, useId = true): string => {\n if (element.id && useId) {\n return `//*[@id=\"${element.id}\"]`;\n }\n\n let nodeElem: HTMLElement | null = element;\n const parts: string[] = [];\n while (nodeElem && Node.ELEMENT_NODE === nodeElem.nodeType) {\n let nbOfPreviousSiblings = 0;\n let hasNextSiblings = false;\n let sibling = nodeElem.previousSibling;\n while (sibling) {\n if (\n sibling.nodeType !== Node.DOCUMENT_TYPE_NODE &&\n sibling.nodeName === nodeElem.nodeName\n ) {\n nbOfPreviousSiblings++;\n }\n sibling = sibling.previousSibling;\n }\n sibling = nodeElem.nextSibling;\n while (sibling) {\n if (sibling.nodeName === nodeElem.nodeName) {\n hasNextSiblings = true;\n break;\n }\n sibling = sibling.nextSibling;\n }\n const prefix = nodeElem.prefix ? `${nodeElem.prefix}:` : '';\n const nth =\n nbOfPreviousSiblings || hasNextSiblings\n ? `[${nbOfPreviousSiblings + 1}]`\n : '';\n parts.push(prefix + nodeElem.localName + nth);\n nodeElem = nodeElem.parentElement;\n }\n return parts.length ? `/${parts.reverse().join('/')}` : '';\n};\n\n/**\n * Extracts relevant attributes from an HTMLElement\n * @param element - The element to get attributes from\n * @returns An object containing the element's attributes\n */\nexport function getElementAttributes(element: HTMLElement): { [key: string]: string } {\n const attrs: { [key: string]: string } = {};\n const priorityAttrs = [\n 'id',\n 'class',\n 'name',\n 'type',\n 'href',\n 'src',\n 'alt',\n 'for',\n 'placeholder',\n ];\n const dataAttrs: { name: string; value: string }[] = [];\n\n for (let i = 0; i < element.attributes.length; i++) {\n const attr = element.attributes[i];\n if (attr.name.startsWith('data-')) {\n dataAttrs.push({ name: attr.name, value: attr.value });\n } else if (\n priorityAttrs.includes(attr.name.toLowerCase()) ||\n attr.name.toLowerCase() !== 'style'\n ) {\n attrs[attr.name] = attr.value;\n }\n }\n \n dataAttrs.forEach((da) => {\n attrs[da.name] = da.value;\n });\n \n return attrs;\n}\n\n/**\n * Generates a detailed context string for a single HTMLElement\n * @param element - The element to generate context for\n * @param index - The index of the element\n * @param totalElements - The total number of elements\n * @returns A string containing the element's context\n */\nexport function generateElementContext(\n element: HTMLElement,\n index: number,\n totalElements: number,\n): string {\n let context = \"\";\n \n // Only add element numbering if there are multiple elements\n if (totalElements > 1) {\n context += `#### Element ${index + 1}\\n`;\n }\n \n context += `- **Tag**: ${element.tagName.toLowerCase()}\\n`;\n\n const id = element.id;\n if (id) {\n context += `- **ID**: ${id}\\n`;\n }\n\n const classes = Array.from(element.classList).join(', ');\n if (classes) {\n context += `- **Classes**: ${classes}\\n`;\n }\n\n const attributes = getElementAttributes(element);\n if (Object.keys(attributes).length > 0) {\n context += `- **Attributes**:\\n`;\n for (const [key, value] of Object.entries(attributes)) {\n if (key.toLowerCase() !== 'class' || !classes) {\n context += ` - ${key}: ${value}\\n`;\n }\n }\n }\n\n const text = element.innerText?.trim();\n if (text) {\n const maxLength = 100;\n context += `- **Text**: ${text.length > maxLength ? `${text.substring(0, maxLength)}...` : text}\\n`;\n }\n\n context += `- **Structural Context**:\\n`;\n if (element.parentElement) {\n const parent = element.parentElement;\n context += ` - **Parent**:\\n`;\n context += ` - Tag: ${parent.tagName.toLowerCase()}\\n`;\n if (parent.id) {\n context += ` - ID: ${parent.id}\\n`;\n }\n const parentClasses = Array.from(parent.classList).join(', ');\n if (parentClasses) {\n context += ` - Classes: ${parentClasses}\\n`;\n }\n } else {\n context += ` - **Parent**: No parent element found (likely root or disconnected)\\n`;\n }\n\n try {\n const styles = window.getComputedStyle(element);\n const relevantStyles = {\n color: styles.color,\n backgroundColor: styles.backgroundColor,\n fontSize: styles.fontSize,\n fontWeight: styles.fontWeight,\n display: styles.display,\n };\n context += `- **Styles**:\\n`;\n for (const [key, value] of Object.entries(relevantStyles)) {\n context += ` - ${key}: ${value}\\n`;\n }\n } catch (e) {\n context += `- **Styles**: Could not retrieve computed styles\\n`;\n }\n\n context += `\\n`;\n return context;\n}\n\n/**\n * Creates a prompt containing information about the selected elements\n * @param selectedElements - The selected elements\n * @param userPrompt - The user's prompt\n * @param url - The page URL\n * @returns A string containing the prompt\n */\nexport function createElementsPrompt(\n selectedElements: HTMLElement[],\n userPrompt: string,\n): string {\n if (!selectedElements || selectedElements.length === 0) {\n return `\n${userPrompt}\n\n### Context\nNo specific element was selected on the page. Please analyze the page code in general or ask for clarification.`.trim();\n }\n\n let detailedContext = '';\n const totalElements = selectedElements.length;\n \n selectedElements.forEach((element, index) => {\n detailedContext += generateElementContext(element, index, totalElements);\n });\n\n // Use singular or plural based on number of selected elements\n const sectionHeader = totalElements > 1 ? '### Selected Elements' : '### Selected Element';\n\n return `\n${userPrompt}\n\n${sectionHeader}\n${detailedContext.trim()}`.trim();\n}\n\n/**\n * Finds the most specific/innermost element at a given point\n * @param x - The x coordinate\n * @param y - The y coordinate\n * @param excludeSelector - CSS selector to exclude elements\n * @returns The most specific element at the point\n */\nexport function getMostSpecificElementAtPoint(\n x: number,\n y: number,\n excludeSelector?: string\n): HTMLElement {\n // Add a default exclude for the element selector itself\n const fullExcludeSelector = excludeSelector \n ? `${excludeSelector}, .element-selector, [data-element-selector=\"true\"]` \n : `.element-selector, [data-element-selector=\"true\"]`;\n \n const elements = document.elementsFromPoint(x, y);\n \n // Filter out excluded elements and SVG elements\n const eligibleElements = elements.filter(element => {\n // Skip the element selector and any specified excludes\n if (fullExcludeSelector && \n (element.matches(fullExcludeSelector) || \n element.closest(fullExcludeSelector))) {\n return false;\n }\n \n // Skip SVG child elements (but allow the SVG element itself)\n if (element.tagName !== 'svg' && element.closest('svg')) {\n return false;\n }\n \n // Skip the element selector class or attribute\n if (element.classList.contains('element-selector') || \n element.hasAttribute('data-element-selector')) {\n return false;\n }\n \n return isElementAtPoint(element as HTMLElement, x, y);\n }) as HTMLElement[];\n \n if (eligibleElements.length === 0) {\n return document.body;\n }\n\n return eligibleElements[0];\n \n // Sort elements by various criteria to find the most specific one\n const sortedElements = [...eligibleElements].sort((a, b) => {\n // 1. First check depth - deeper DOM elements are more specific\n const aDepth = getElementDepth(a);\n const bDepth = getElementDepth(b);\n \n if (aDepth !== bDepth) {\n return bDepth - aDepth; // Higher depth is more specific\n }\n \n // 2. If at same depth, prefer elements with less children\n const aChildren = a.children.length;\n const bChildren = b.children.length;\n \n if (aChildren !== bChildren) {\n return aChildren - bChildren; // Fewer children is more specific\n }\n \n // 3. If still tied, use element area\n const aRect = a.getBoundingClientRect();\n const bRect = b.getBoundingClientRect();\n const aArea = aRect.width * aRect.height;\n const bArea = bRect.width * bRect.height;\n return aArea - bArea; // Smaller area is more specific\n });\n \n // Return the most specific eligible element\n return sortedElements[0];\n}\n\n/**\n * Helper function to calculate the depth of an element in the DOM tree\n * @param element - The element to check\n * @returns The depth of the element\n */\nfunction getElementDepth(element: HTMLElement): number {\n let depth = 0;\n let current = element;\n \n while (current.parentElement) {\n depth++;\n current = current.parentElement;\n }\n \n return depth;\n}\n\n/**\n * Extracts common properties from an HTML element\n */\nexport const extractElementProperties = (el: HTMLElement) => {\n // Extract only direct text nodes, excluding child element text\n const getDirectTextContent = (element: HTMLElement): string => {\n // Check if element has any child elements with text content\n const hasChildElementsWithText = Array.from(element.children).some(child => {\n return child.textContent && child.textContent.trim().length > 0;\n });\n \n // If there are child elements with text, return empty string\n if (hasChildElementsWithText) {\n return '';\n }\n \n // Otherwise, extract direct text nodes only\n let text = '';\n for (const node of element.childNodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent || '';\n }\n }\n return text.trim();\n };\n\n // Check if the element's data-simplify-id is duplicated\n const checkIsDuplicated = (element: HTMLElement): boolean => {\n const simplifyId = element.getAttribute('data-simplify-id');\n if (!simplifyId) {\n return false;\n }\n const elementsWithSameId = document.querySelectorAll(`[data-simplify-id=\"${simplifyId}\"]`);\n return elementsWithSameId.length > 1;\n };\n\n // Extract inline styles in camelCase\n const getElementStyles = (element: HTMLElement): Record<string, string> => {\n const styles: Record<string, string> = {};\n for (let i = 0; i < element.style.length; i++) {\n const prop = element.style[i];\n const value = element.style.getPropertyValue(prop);\n if (value) {\n // Convert kebab-case to camelCase\n const camelProp = prop.replace(/-([a-z])/g, (g) => g[1].toUpperCase());\n styles[camelProp] = value;\n }\n }\n return styles;\n };\n\n return {\n tagName: el.tagName,\n id: el.id,\n className: typeof el.className === 'string' ? el.className : (el.className as any).baseVal || '',\n style: getElementStyles(el),\n isDuplicated: checkIsDuplicated(el),\n textContent: getDirectTextContent(el),\n attributes: Array.from(el.attributes).map(attr => ({\n name: attr.name,\n value: attr.value\n })),\n };\n};\n\n/**\n * Maps an HTML element to a serializable object representation\n */\nexport const getAllFormElements = (): string[] => {\n const formElements = Array.from(\n document.querySelectorAll('[data-name][data-description]')\n ).filter((formEl): formEl is HTMLElement => formEl instanceof HTMLElement);\n const dataNames = formElements.map((el) => el.getAttribute(\"data-name\") || \"\");\n return dataNames.filter(Boolean); // Remove any empty strings if attribute is missing\n};\n\n/**\n * Interface for event handler payload\n */\ninterface EventHandlerPayload {\n event: string;\n action: string;\n elements: any[];\n}\n\n/**\n * Generates a prompt for adding an event handler to a selected element\n * @param payload The event handler configuration\n * @returns A string prompt describing how to add the event handler\n */\nexport function generateEventHandlerPrompt(\n payload: EventHandlerPayload,\n): string {\n const { eve