UNPKG

element-prompt-builder

Version:

React component library for inspecting DOM elements and generating AI prompts

1 lines 125 kB
{"version":3,"sources":["#style-inject:#style-inject","../src/styles/global.css","../src/ElementSelector.tsx","../src/utils.ts","../src/styles/selector-styles.ts","../src/ElementHighlighter.tsx","../src/styles/highlighter-styles.ts","../src/ElementInspector.tsx","../src/constants.ts","../src/styles/bubble-menu-styles.ts","../src/components/bubble-menu/MenuArrow.tsx","../src/Icons.tsx","../src/styles/base-styles.ts","../src/styles/form-styles.ts","../src/components/MentionChat.tsx","../src/stores/workflows.ts","../src/components/Mapper.tsx","../src/components/ui/mention.tsx","../src/components/PromptForm.tsx","../src/styles/expanded-menu-styles.ts","../src/components/bubble-menu/ExpandedMenu.tsx","../src/styles/bubble-button-styles.ts","../src/components/BubbleMenuButton.tsx","../src/components/bubble-menu/FloatingButton.tsx","../src/components/bubble-menu/BubbleMenu.tsx","../src/components/ElementTagLabel.tsx","../src/styles/highlights-styles.ts","../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-blue-50: oklch(97% 0.014 254.604);\\n --color-blue-200: oklch(88.2% 0.059 254.128);\\n --color-blue-800: oklch(42.4% 0.199 265.638);\\n --color-blue-950: oklch(28.2% 0.091 267.935);\\n --color-gray-500: oklch(55.1% 0.027 264.364);\\n --color-gray-600: oklch(44.6% 0.03 256.802);\\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-semibold: 600;\\n --radius-sm: 0.25rem;\\n --radius-md: 0.375rem;\\n --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\\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 .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 .static {\\n position: static;\\n }\\n .z-50 {\\n z-index: 50;\\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 .mr-2 {\\n margin-right: calc(var(--spacing) * 2);\\n }\\n .ml-0 {\\n margin-left: calc(var(--spacing) * 0);\\n }\\n .block {\\n display: block;\\n }\\n .flex {\\n display: flex;\\n }\\n .field-sizing-content {\\n field-sizing: content;\\n }\\n .h-8 {\\n height: calc(var(--spacing) * 8);\\n }\\n .h-\\\\[200px\\\\] {\\n height: 200px;\\n }\\n .max-h-\\\\[200px\\\\] {\\n max-height: 200px;\\n }\\n .min-h-16 {\\n min-height: calc(var(--spacing) * 16);\\n }\\n .w-8 {\\n width: calc(var(--spacing) * 8);\\n }\\n .w-full {\\n width: 100%;\\n }\\n .max-w-\\\\[435px\\\\] {\\n max-width: 435px;\\n }\\n .min-w-\\\\[8rem\\\\] {\\n min-width: 8rem;\\n }\\n .min-w-\\\\[250px\\\\] {\\n min-width: 250px;\\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-default {\\n cursor: default;\\n }\\n .resize {\\n resize: both;\\n }\\n .resize-none {\\n resize: none;\\n }\\n .flex-col {\\n flex-direction: column;\\n }\\n .items-center {\\n align-items: center;\\n }\\n .items-start {\\n align-items: flex-start;\\n }\\n .gap-0\\\\.5 {\\n gap: calc(var(--spacing) * 0.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-md {\\n border-radius: var(--radius-md);\\n }\\n .rounded-sm {\\n border-radius: var(--radius-sm);\\n }\\n .border {\\n border-style: var(--tw-border-style);\\n border-width: 1px;\\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 .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 .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-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-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 .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-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-gray-500 {\\n color: var(--color-gray-500);\\n }\\n .text-white {\\n color: var(--color-white);\\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-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-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 .ease-in-out {\\n --tw-ease: var(--ease-in-out);\\n transition-timing-function: var(--ease-in-out);\\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 .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@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-ease { 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-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-ease: initial;\\n }\\n }\\n}\\n\")","\"use client\";\nimport React, { useCallback, useRef } from 'react';\nimport { getMostSpecificElementAtPoint } from './utils';\nimport { elementSelectorStyles } from './styles/selector-styles';\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={`element-selector ${className}`}\n data-element-selector=\"true\"\n style={{\n ...elementSelectorStyles.overlay,\n ...style,\n }}\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 elements\n if (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 // 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","\"use client\";\nimport React from 'react';\n\n/**\n * Styles for the ElementSelector component\n */\nexport const elementSelectorStyles = {\n overlay: {\n position: 'fixed' as const,\n inset: 0,\n height: '100vh',\n width: '100vw',\n cursor: 'cell',\n zIndex: 9999,\n pointerEvents: 'auto',\n } as React.CSSProperties\n};\n","\"use client\";\nimport React, { useCallback, useEffect, useRef } from 'react';\nimport { getHighlighterStyles } from './styles/highlighter-styles';\n\nexport interface ElementHighlighterProps {\n /**\n * The element to highlight\n */\n element: HTMLElement;\n \n /**\n * CSS class for the highlighter\n */\n className?: string;\n \n /**\n * Custom styles for the highlighter\n */\n style?: React.CSSProperties;\n \n /**\n * Update rate in frames per second (0 to disable updates)\n */\n updateRate?: number;\n \n /**\n * Content to render inside the highlighter\n */\n children?: React.ReactNode;\n \n /**\n * Border color for the highlighter\n */\n borderColor?: string;\n \n /**\n * Background color for the highlighter\n */\n backgroundColor?: string;\n}\n\n/**\n * A component that highlights a DOM element with a border\n */\nexport function ElementHighlighter({\n element,\n className = '',\n style = {},\n updateRate = 30,\n children,\n borderColor = 'rgba(59, 130, 246, 0.8)',\n backgroundColor = 'rgba(59, 130, 246, 0.2)',\n}: ElementHighlighterProps) {\n const boxRef = useRef<HTMLDivElement>(null);\n const updateIntervalRef = useRef<number | null>(null);\n\n const updateBoxPosition = useCallback(() => {\n if (boxRef.current && element) {\n const referenceRect = element.getBoundingClientRect();\n\n boxRef.current.style.top = `${referenceRect.top - 2}px`;\n boxRef.current.style.left = `${referenceRect.left - 2}px`;\n boxRef.current.style.width = `${referenceRect.width + 4}px`;\n boxRef.current.style.height = `${referenceRect.height + 4}px`;\n }\n }, [element]);\n\n // Set up the interval for updating the box position\n useEffect(() => {\n updateBoxPosition();\n \n if (updateRate > 0) {\n const intervalId = window.setInterval(() => {\n updateBoxPosition();\n }, 1000 / updateRate);\n \n updateIntervalRef.current = intervalId;\n \n return () => {\n if (updateIntervalRef.current !== null) {\n window.clearInterval(updateIntervalRef.current);\n }\n };\n }\n }, [updateBoxPosition, updateRate]);\n\n // Also update on window resize\n useEffect(() => {\n const handleResize = () => {\n updateBoxPosition();\n };\n \n window.addEventListener('resize', handleResize);\n return () => {\n window.removeEventListener('resize', handleResize);\n };\n }, [updateBoxPosition]);\n\n return (\n <div\n className={`element-highlighter ${className}`}\n style={getHighlighterStyles(borderColor, backgroundColor, style).container}\n ref={boxRef}\n >\n {children}\n </div>\n );\n}\n","\"use client\";\nimport React from 'react';\n\n/**\n * Styles for the ElementHighlighter component\n */\nexport const getHighlighterStyles = (\n borderColor: string = 'rgba(59, 130, 246, 0.8)',\n backgroundColor: string = 'rgba(59, 130, 246, 0.2)',\n customStyles: React.CSSProperties = {}\n) => {\n return {\n container: {\n position: 'fixed',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '4px',\n border: `2px solid ${borderColor}`,\n backgroundColor,\n transition: 'all 100ms',\n zIndex: 9998,\n pointerEvents: 'none',\n ...customStyles,\n } as React.CSSProperties\n };\n};\n","\"use client\";\nimport React, { useCallback, useEffect } from 'react';\nimport { BubbleMenu, InspectionOverlay } from './components';\nimport {\n useDarkMode,\n useElementBubblePosition,\n useElementSelection,\n useIframeMessaging,\n useInspector\n} from './hooks';\nimport { UI_CONSTANTS } from './constants';\n\nexport interface ElementInspectorProps {\n /**\n * Initial state of the inspector (active or not)\n */\n initialIsActive?: boolean;\n \n /**\n * CSS selector for elements to exclude from selection\n */\n excludeSelector?: string;\n \n /**\n * Custom label for selected elements\n */\n elementLabel?: (element: HTMLElement) => React.ReactNode;\n \n /**\n * Custom styles for the selector\n */\n selectorStyle?: React.CSSProperties;\n \n /**\n * Custom styles for the highlighter\n */\n highlighterStyle?: React.CSSProperties;\n \n /**\n * Whether to show the floating bubble menu button\n * @default false\n */\n showBubbleMenuButton?: boolean;\n}\n\n/**\n * A component that allows inspecting and selecting DOM elements with a UI for interaction\n */\nexport function ElementInspector({\n initialIsActive = true,\n excludeSelector = '.element-inspector-bubble, .element-inspector-controls, .element-inspector-mention',\n elementLabel,\n selectorStyle,\n highlighterStyle,\n showBubbleMenuButton = false,\n}: ElementInspectorProps) {\n // Custom hooks\n const isDarkMode = useDarkMode();\n const { \n isInIframe, \n sendSelectedElements, \n sendPrompt,\n shouldEnableInspect,\n } = useIframeMessaging();\n \n // Element selection hook\n const { \n hoveredElement, \n selectedElements, \n handleElementHovered, \n handleElementUnhovered, \n handleElementSelected,\n clearSelections \n } = useElementSelection({\n onElementsSelected: (elements) => {\n // Send selected elements to parent frame if in iframe\n if (isInIframe) {\n sendSelectedElements(elements);\n }\n }\n });\n \n // Bubble position hook\n const { bubblePosition, isMenuAboveElement } = useElementBubblePosition({\n selectedElements,\n menuHeight: UI_CONSTANTS.MENU_HEIGHT,\n menuWidth: UI_CONSTANTS.MENU_WIDTH,\n spacing: UI_CONSTANTS.SPACING\n });\n \n // Inspector state hook\n const {\n isInspecting,\n userPrompt,\n setUserPrompt,\n toggleInspection,\n handlePromptSubmit,\n } = useInspector({\n // Use shouldEnableInspect from iframe messaging if in iframe, otherwise use initialIsActive\n initialIsActive: isInIframe ? shouldEnableInspect : initialIsActive,\n onPromptGenerated: (prompt, elements) => {\n // Send prompt to parent frame if in iframe\n if (isInIframe) {\n sendPrompt(prompt, elements);\n }\n }\n });\n \n // Handle menu toggle - clear selections when toggling off\n const handleMenuToggle = useCallback(() => {\n toggleInspection();\n if (isInspecting) {\n clearSelections();\n }\n }, [isInspecting, toggleInspection, clearSelections]);\n \n // Handle form submission with selected elements\n const onSubmitPrompt = useCallback((e: React.FormEvent) => {\n handlePromptSubmit(e, selectedElements);\n }, [handlePromptSubmit, selectedElements]);\n \n // Update inspection state when shouldEnableInspect changes (from iframe messages)\n useEffect(() => {\n if (isInIframe && isInspecting !== shouldEnableInspect) {\n toggleInspection();\n }\n }, [isInIframe, isInspecting, shouldEnableInspect, toggleInspection]);\n\n // Send ready message to parent window when component mounts\n useEffect(() => {\n if (isInIframe) {\n window.parent.postMessage({ type: 'UI_BUILDER_READY' }, '*');\n }\n }, [isInIframe]);\n\n return (\n <div>\n {/* Element Inspector Components */}\n <InspectionOverlay\n isInspecting={isInspecting}\n hoveredElement={hoveredElement}\n selectedElements={selectedElements}\n onElementHovered={handleElementHovered}\n onElementSelected={handleElementSelected}\n onElementUnhovered={handleElementUnhovered}\n excludeSelector={excludeSelector}\n selectorStyle={selectorStyle}\n highlighterStyle={highlighterStyle}\n elementLabel={elementLabel}\n />\n\n {/* Bubble Menu */}\n <BubbleMenu\n isInspecting={isInspecting}\n selectedElements={selectedElements}\n bubblePosition={bubblePosition}\n isMenuAboveElement={isMenuAboveElement}\n userPrompt={userPrompt}\n setUserPrompt={setUserPrompt}\n onSubmitPrompt={onSubmitPrompt}\n onToggleInspection={handleMenuToggle}\n isDarkMode={isDarkMode}\n showBubbleMenuButton={showBubbleMenuButton}\n />\n </div>\n );\n}\n","/**\n * Shared constants for the element prompt builder\n */\n\n// UI Dimensions\nexport const UI_CONSTANTS = {\n // Menu dimensions\n MENU_WIDTH: 350, // Width of the control menu in pixels\n MENU_HEIGHT: 56, // Height of the control menu in pixels\n \n // Spacing and offsets\n SPACING: 10, // Standard spacing for margins and padding\n ARROW_LEFT_OFFSET: 20, // Left offset for the arrow from menu edge\n \n // Z-index values\n Z_INDEX: 10000, // Standard z-index for UI elements\n};\n","\"use client\";\nimport { UI_CONSTANTS } from \"../constants\";\n\n/**\n * Generates styles for the menu arrow component\n */\nexport const getMenuArrowStyles = (\n isMenuAboveElement: boolean,\n bubblePosition: { top: number; left: number },\n isDarkMode: boolean,\n isVisible: boolean,\n) => {\n const topPosition = isMenuAboveElement\n ? `${bubblePosition.top + UI_CONSTANTS.MENU_HEIGHT}px` // Arrow at bottom of menu\n : `${bubblePosition.top - 8}px`; // Arrow at top of menu\n\n const leftPosition = `${bubblePosition.left + UI_CONSTANTS.ARROW_LEFT_OFFSET}px`;\n\n const borderTop = isMenuAboveElement\n ? `8px solid ${isDarkMode ? \"#1f2937\" : \"white\"}`\n : \"none\";\n\n const borderBottom = isMenuAboveElement\n ? \"none\"\n : `8px solid ${isDarkMode ? \"#1f2937\" : \"white\"}`;\n\n return {\n menuArrow: {\n position: \"fixed\",\n width: 0,\n height: 0,\n borderLeft: \"8px solid transparent\",\n borderRight: \"8px solid transparent\",\n top: topPosition,\n left: leftPosition,\n borderTop,\n borderBottom,\n pointerEvents: \"none\",\n opacity: isVisible ? 1 : 0,\n zIndex: UI_CONSTANTS.Z_INDEX + 1,\n } as React.CSSProperties,\n };\n};\n\n/**\n * Styles for the floating button container\n */\nexport const floatingButtonStyles = {\n container: {\n position: \"fixed\",\n bottom: \"24px\",\n right: \"24px\",\n zIndex: UI_CONSTANTS.Z_INDEX,\n } as React.CSSProperties,\n};\n\n/**\n * Styles for the bubble container\n */\nexport const bubbleStyles = {\n container: {\n position: \"static\",\n } as React.CSSProperties,\n};\n","\"use client\";\nimport React from \"react\";\nimport { getMenuArrowStyles } from \"../../styles/bubble-menu-styles\";\n\ninterface MenuArrowProps {\n isMenuAboveElement: boolean;\n bubblePosition: { top: number; left: number };\n isDarkMode: boolean;\n isVisible: boolean;\n}\n\nexport const MenuArrow: React.FC<MenuArrowProps> = ({\n isMenuAboveElement,\n bubblePosition,\n isDarkMode,\n isVisible,\n}) => {\n const styles = getMenuArrowStyles(\n isMenuAboveElement,\n bubblePosition,\n isDarkMode,\n isVisible,\n );\n return (\n <div\n className=\"element-inspector-menu-arrow\"\n style={styles.menuArrow}\n aria-hidden=\"true\"\n />\n );\n};\n","\"use client\";\n\n// Custom icon components\nexport const IconPointer = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className=\"lucide lucide-mouse-pointer-click-icon lucide-mouse-pointer-click\"><path d=\"M14 4.1 12 6\"/><path d=\"m5.1 8-2.9-.8\"/><path d=\"m6 12-1.9 2\"/><path d=\"M7.2 2.2 8 5.1\"/><path d=\"M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z\"/></svg>\n);\n\nexport const IconSquareDashedPointer = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className=\"lucide lucide-square-dashed-mouse-pointer-icon lucide-square-dashed-mouse-pointer\"><path d=\"M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z\"/><path d=\"M5 3a2 2 0 0 0-2 2\"/><path d=\"M19 3a2 2 0 0 1 2 2\"/><path d=\"M5 21a2 2 0 0 1-2-2\"/><path d=\"M9 3h1\"/><path d=\"M9 21h2\"/><path d=\"M14 3h1\"/><path d=\"M3 9v1\"/><path d=\"M21 9v2\"/><path d=\"M3 14v1\"/></svg>\n);\n\nexport const IconX = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" wid