@useclickly/react 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -497,9 +497,15 @@ async function safeReadText(res) {
497
497
  return "";
498
498
  }
499
499
  }
500
+ var PANEL_W = 284;
501
+ var PANEL_H = 340;
502
+ var PANEL_GAP = 12;
503
+ var VIEWPORT_PAD = 8;
504
+ var TOOLBAR_H = 46;
500
505
  function SettingsPopover({ anchor, width, onClose }) {
501
506
  const ref = useRef(null);
502
- const s = useSettings();
507
+ const [view, setView] = useState("main");
508
+ const [detailMenuOpen, setDetailMenuOpen] = useState(false);
503
509
  useEffect(() => {
504
510
  const onDown = (e) => {
505
511
  if (ref.current && e.composedPath().includes(ref.current)) return;
@@ -508,95 +514,162 @@ function SettingsPopover({ anchor, width, onClose }) {
508
514
  window.addEventListener("pointerdown", onDown, true);
509
515
  return () => window.removeEventListener("pointerdown", onDown, true);
510
516
  }, [onClose]);
511
- const PANEL_H = 330;
512
- const top = Math.max(8, anchor.y - PANEL_H - 12);
513
- const left = Math.min(window.innerWidth - 300, Math.max(8, anchor.x + width - 284));
514
- return /* @__PURE__ */ jsxs("div", { ref, className: "clickly-settings", style: { left, top }, children: [
517
+ useEffect(() => {
518
+ const onKey = (e) => {
519
+ if (e.key !== "Escape") return;
520
+ if (view !== "main") {
521
+ e.preventDefault();
522
+ setView("main");
523
+ }
524
+ };
525
+ window.addEventListener("keydown", onKey);
526
+ return () => window.removeEventListener("keydown", onKey);
527
+ }, [view]);
528
+ const vw = typeof window !== "undefined" ? window.innerWidth : 1024;
529
+ const vh = typeof window !== "undefined" ? window.innerHeight : 768;
530
+ const spaceAbove = anchor.y - VIEWPORT_PAD;
531
+ const spaceBelow = vh - anchor.y - TOOLBAR_H - VIEWPORT_PAD;
532
+ const placeAbove = spaceAbove >= PANEL_H + PANEL_GAP || spaceAbove >= spaceBelow;
533
+ const top = placeAbove ? Math.max(VIEWPORT_PAD, anchor.y - PANEL_H - PANEL_GAP) : Math.min(vh - PANEL_H - VIEWPORT_PAD, anchor.y + TOOLBAR_H + PANEL_GAP);
534
+ const desiredLeft = anchor.x + width - PANEL_W;
535
+ const left = Math.min(
536
+ vw - PANEL_W - VIEWPORT_PAD,
537
+ Math.max(VIEWPORT_PAD, desiredLeft)
538
+ );
539
+ return /* @__PURE__ */ jsx(
540
+ "div",
541
+ {
542
+ ref,
543
+ className: "clickly-settings",
544
+ style: { left, top, width: PANEL_W, height: PANEL_H },
545
+ children: view === "main" ? /* @__PURE__ */ jsx(
546
+ MainView,
547
+ {
548
+ onClose,
549
+ onOpenMcp: () => setView("mcp"),
550
+ detailMenuOpen,
551
+ setDetailMenuOpen
552
+ }
553
+ ) : /* @__PURE__ */ jsx(McpSubView, { onBack: () => setView("main"), onClose })
554
+ }
555
+ );
556
+ }
557
+ function MainView({
558
+ onClose,
559
+ onOpenMcp,
560
+ detailMenuOpen,
561
+ setDetailMenuOpen
562
+ }) {
563
+ const s = useSettings();
564
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
515
565
  /* @__PURE__ */ jsxs("div", { className: "settings-header", children: [
516
566
  /* @__PURE__ */ jsx("span", { className: "settings-title", children: "Settings" }),
517
567
  /* @__PURE__ */ jsx("button", { className: "settings-close", onClick: onClose, "aria-label": "Close settings", children: /* @__PURE__ */ jsx(IconClose, {}) })
518
568
  ] }),
519
- /* @__PURE__ */ jsxs("div", { className: "settings-section", children: [
520
- /* @__PURE__ */ jsxs("label", { className: "settings-label", htmlFor: "clickly-detail", children: [
521
- "Output detail",
522
- /* @__PURE__ */ jsx("span", { className: "settings-hint", children: "How much metadata is captured" })
523
- ] }),
524
- /* @__PURE__ */ jsxs(
525
- "select",
526
- {
527
- id: "clickly-detail",
528
- className: "settings-select",
529
- value: s.outputDetail,
530
- onChange: (e) => s.set({ outputDetail: e.target.value }),
531
- children: [
532
- /* @__PURE__ */ jsx("option", { value: "compact", children: "Compact" }),
533
- /* @__PURE__ */ jsx("option", { value: "standard", children: "Standard" }),
534
- /* @__PURE__ */ jsx("option", { value: "detailed", children: "Detailed" }),
535
- /* @__PURE__ */ jsx("option", { value: "forensic", children: "Forensic" })
536
- ]
537
- }
538
- )
539
- ] }),
540
- /* @__PURE__ */ jsx("div", { className: "settings-divider" }),
541
- /* @__PURE__ */ jsxs("div", { className: "settings-section", children: [
542
- /* @__PURE__ */ jsxs("div", { className: "settings-row", children: [
543
- /* @__PURE__ */ jsxs("div", { className: "settings-row-label", children: [
544
- "Copy on add",
545
- /* @__PURE__ */ jsx("span", { className: "settings-hint", children: "Auto-copy markdown when annotating" })
546
- ] }),
547
- /* @__PURE__ */ jsxs("label", { className: "clickly-toggle", children: [
548
- /* @__PURE__ */ jsx(
549
- "input",
569
+ /* @__PURE__ */ jsxs("div", { className: "settings-scroll", children: [
570
+ /* @__PURE__ */ jsxs("div", { className: "settings-row-compact", children: [
571
+ /* @__PURE__ */ jsx(RowLabel, { hint: "How much metadata the markdown output captures.", children: "Output Detail" }),
572
+ /* @__PURE__ */ jsxs("div", { className: "settings-inline-select", children: [
573
+ /* @__PURE__ */ jsxs(
574
+ "button",
550
575
  {
551
- type: "checkbox",
552
- checked: s.copyOnAdd,
553
- onChange: (e) => s.set({ copyOnAdd: e.target.checked })
576
+ type: "button",
577
+ className: "settings-inline-select-trigger",
578
+ onClick: () => setDetailMenuOpen((v) => !v),
579
+ "aria-haspopup": "menu",
580
+ "aria-expanded": detailMenuOpen,
581
+ children: [
582
+ /* @__PURE__ */ jsx("span", { children: labelForDetail(s.outputDetail) }),
583
+ /* @__PURE__ */ jsx("span", { className: "settings-inline-chev", "aria-hidden": true, children: "\u22EE" })
584
+ ]
554
585
  }
555
586
  ),
556
- /* @__PURE__ */ jsx("span", { className: "toggle-track" })
557
- ] })
558
- ] }),
559
- /* @__PURE__ */ jsxs("div", { className: "settings-row", children: [
560
- /* @__PURE__ */ jsxs("div", { className: "settings-row-label", children: [
561
- "React components",
562
- /* @__PURE__ */ jsx("span", { className: "settings-hint", children: "Include component tree in output" })
563
- ] }),
564
- /* @__PURE__ */ jsxs("label", { className: "clickly-toggle", children: [
565
- /* @__PURE__ */ jsx(
566
- "input",
587
+ detailMenuOpen ? /* @__PURE__ */ jsx("div", { className: "settings-inline-menu", role: "menu", children: ["compact", "standard", "detailed", "forensic"].map((v) => /* @__PURE__ */ jsx(
588
+ "button",
567
589
  {
568
- type: "checkbox",
569
- checked: s.showReactComponents,
570
- onChange: (e) => s.set({ showReactComponents: e.target.checked })
571
- }
572
- ),
573
- /* @__PURE__ */ jsx("span", { className: "toggle-track" })
590
+ type: "button",
591
+ role: "menuitemradio",
592
+ "aria-checked": s.outputDetail === v,
593
+ className: `settings-inline-menu-item${s.outputDetail === v ? " is-active" : ""}`,
594
+ onClick: () => {
595
+ s.set({ outputDetail: v });
596
+ setDetailMenuOpen(false);
597
+ },
598
+ children: labelForDetail(v)
599
+ },
600
+ v
601
+ )) }) : null
574
602
  ] })
575
- ] })
576
- ] }),
577
- /* @__PURE__ */ jsx("div", { className: "settings-divider" }),
578
- /* @__PURE__ */ jsx("div", { className: "settings-section", children: /* @__PURE__ */ jsxs("div", { className: "settings-row", children: [
579
- /* @__PURE__ */ jsxs("div", { className: "settings-row-label", children: [
580
- "Marker color",
581
- /* @__PURE__ */ jsx("span", { className: "settings-hint", children: "Color used for annotation pins" })
582
603
  ] }),
583
- /* @__PURE__ */ jsxs("label", { className: "settings-color-wrap", "aria-label": "Marker color", children: [
584
- /* @__PURE__ */ jsx("span", { className: "color-swatch", style: { background: s.markerColor } }),
604
+ /* @__PURE__ */ jsxs("div", { className: "settings-row-compact", children: [
605
+ /* @__PURE__ */ jsx(RowLabel, { hint: "Auto-copy markdown to the clipboard every time you submit an annotation.", children: "Copy on Add" }),
585
606
  /* @__PURE__ */ jsx(
586
- "input",
607
+ Toggle,
587
608
  {
588
- type: "color",
589
- value: s.markerColor,
590
- onChange: (e) => s.set({ markerColor: e.target.value })
609
+ checked: s.copyOnAdd,
610
+ onChange: (v) => s.set({ copyOnAdd: v })
591
611
  }
592
612
  )
593
- ] })
594
- ] }) }),
595
- /* @__PURE__ */ jsx("div", { className: "settings-divider" }),
596
- /* @__PURE__ */ jsx(McpSection, {})
613
+ ] }),
614
+ /* @__PURE__ */ jsxs("div", { className: "settings-row-compact", children: [
615
+ /* @__PURE__ */ jsx(RowLabel, { hint: "Include the React component tree (App \u203A Page \u203A Button) in the output.", children: "React Components" }),
616
+ /* @__PURE__ */ jsx(
617
+ Toggle,
618
+ {
619
+ checked: s.showReactComponents,
620
+ onChange: (v) => s.set({ showReactComponents: v })
621
+ }
622
+ )
623
+ ] }),
624
+ /* @__PURE__ */ jsxs("div", { className: "settings-row-compact settings-row-stacked", children: [
625
+ /* @__PURE__ */ jsx(RowLabel, { hint: "Color used for the floating annotation pins on the page.", children: "Marker Color" }),
626
+ /* @__PURE__ */ jsxs("div", { className: "settings-color-presets", role: "radiogroup", "aria-label": "Marker color", children: [
627
+ MARKER_PRESETS.map((color) => /* @__PURE__ */ jsx(
628
+ "button",
629
+ {
630
+ type: "button",
631
+ role: "radio",
632
+ "aria-checked": s.markerColor.toLowerCase() === color.toLowerCase(),
633
+ className: `settings-color-preset${s.markerColor.toLowerCase() === color.toLowerCase() ? " is-active" : ""}`,
634
+ style: { "--preset": color },
635
+ onClick: () => s.set({ markerColor: color }),
636
+ title: color,
637
+ "aria-label": `Use color ${color}`
638
+ },
639
+ color
640
+ )),
641
+ /* @__PURE__ */ jsxs("label", { className: "settings-color-preset is-custom", title: "Custom color", children: [
642
+ /* @__PURE__ */ jsx(
643
+ "input",
644
+ {
645
+ type: "color",
646
+ value: s.markerColor,
647
+ onChange: (e) => s.set({ markerColor: e.target.value }),
648
+ "aria-label": "Pick a custom marker color"
649
+ }
650
+ ),
651
+ /* @__PURE__ */ jsx("span", { className: "settings-color-custom-glyph", children: "+" })
652
+ ] })
653
+ ] })
654
+ ] }),
655
+ /* @__PURE__ */ jsxs(
656
+ "button",
657
+ {
658
+ type: "button",
659
+ className: "settings-disclosure",
660
+ onClick: onOpenMcp,
661
+ "aria-haspopup": "menu",
662
+ children: [
663
+ /* @__PURE__ */ jsx("span", { className: "settings-disclosure-label", children: "Manage MCP & Webhooks" }),
664
+ /* @__PURE__ */ jsx(McpStatusDot, {}),
665
+ /* @__PURE__ */ jsx("span", { className: "settings-disclosure-chev", "aria-hidden": true, children: "\u203A" })
666
+ ]
667
+ }
668
+ )
669
+ ] })
597
670
  ] });
598
671
  }
599
- function McpSection() {
672
+ function McpSubView({ onBack, onClose }) {
600
673
  const s = useSettings();
601
674
  const status = useMcpStatus((m) => m.status);
602
675
  const lastError = useMcpStatus((m) => m.lastError);
@@ -609,64 +682,177 @@ function McpSection() {
609
682
  error: { label: "Error", modifier: "is-error" }
610
683
  };
611
684
  const meta = statusMeta[status];
612
- return /* @__PURE__ */ jsxs("div", { className: "settings-section", children: [
613
- /* @__PURE__ */ jsxs("div", { className: "settings-row", children: [
614
- /* @__PURE__ */ jsxs("div", { className: "settings-row-label", children: [
615
- "MCP sync",
616
- /* @__PURE__ */ jsx("span", { className: "settings-hint", children: "Push annotations to a running mcp-server so the agent reads them live" })
617
- ] }),
618
- /* @__PURE__ */ jsxs("label", { className: "clickly-toggle", children: [
619
- /* @__PURE__ */ jsx(
620
- "input",
621
- {
622
- type: "checkbox",
623
- checked: s.mcpEnabled,
624
- onChange: (e) => s.set({ mcpEnabled: e.target.checked })
625
- }
626
- ),
627
- /* @__PURE__ */ jsx("span", { className: "toggle-track" })
628
- ] })
629
- ] }),
630
- /* @__PURE__ */ jsxs("div", { className: "settings-row", style: { marginTop: 8 }, children: [
631
- /* @__PURE__ */ jsx("div", { className: "settings-row-label", style: { flex: "0 0 auto", minWidth: 72 }, children: "Endpoint" }),
632
- /* @__PURE__ */ jsx(
633
- "input",
634
- {
635
- type: "text",
636
- className: "settings-input",
637
- value: s.mcpEndpoint,
638
- onChange: (e) => s.set({ mcpEndpoint: e.target.value }),
639
- placeholder: "http://localhost:4747",
640
- disabled: !s.mcpEnabled,
641
- spellCheck: false
642
- }
643
- )
644
- ] }),
645
- /* @__PURE__ */ jsxs("div", { className: `settings-mcp-status ${meta.modifier}`, children: [
646
- /* @__PURE__ */ jsx("span", { className: "mcp-dot", "aria-hidden": true }),
647
- /* @__PURE__ */ jsx("span", { className: "mcp-status-label", children: meta.label }),
648
- status === "connected" && /* @__PURE__ */ jsxs("span", { className: "mcp-status-detail", children: [
649
- serverVersion ? `v${serverVersion}` : "",
650
- lastPingAt ? ` \xB7 pinged ${formatRelative(Date.now() - lastPingAt)} ago` : ""
651
- ] }),
652
- status === "error" && lastError ? /* @__PURE__ */ jsx("span", { className: "mcp-status-detail mcp-status-error", children: lastError }) : null
653
- ] }),
654
- status === "connected" && s.mcpSessionId ? /* @__PURE__ */ jsxs("div", { className: "settings-mcp-session", children: [
655
- /* @__PURE__ */ jsx("span", { className: "mcp-status-label", style: { opacity: 0.7 }, children: "Session" }),
656
- /* @__PURE__ */ jsx("code", { className: "mcp-session-id", children: s.mcpSessionId }),
685
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
686
+ /* @__PURE__ */ jsxs("div", { className: "settings-header settings-subview-header", children: [
657
687
  /* @__PURE__ */ jsx(
658
688
  "button",
659
689
  {
660
690
  type: "button",
661
- className: "mcp-session-reset",
662
- onClick: () => s.set({ mcpSessionId: null }),
663
- title: "Start a fresh session on the next ping",
664
- children: "Reset"
691
+ className: "settings-back",
692
+ onClick: onBack,
693
+ "aria-label": "Back to settings",
694
+ title: "Back (Esc)",
695
+ children: "\u2039"
665
696
  }
666
- )
667
- ] }) : null
697
+ ),
698
+ /* @__PURE__ */ jsx("span", { className: "settings-title", children: "Manage MCP & Webhooks" }),
699
+ /* @__PURE__ */ jsx("button", { className: "settings-close", onClick: onClose, "aria-label": "Close settings", children: /* @__PURE__ */ jsx(IconClose, {}) })
700
+ ] }),
701
+ /* @__PURE__ */ jsxs("div", { className: "settings-scroll", children: [
702
+ /* @__PURE__ */ jsxs("div", { className: "settings-subview-section", children: [
703
+ /* @__PURE__ */ jsxs("div", { className: "settings-subview-section-head", children: [
704
+ /* @__PURE__ */ jsx("span", { className: "settings-subview-section-title", children: "MCP Connection" }),
705
+ /* @__PURE__ */ jsx(InfoTip, { children: "Lets a running mcp-server bridge annotations to your AI agent. Requires the @useclickly/mcp-server CLI running on your machine." }),
706
+ /* @__PURE__ */ jsx(
707
+ Toggle,
708
+ {
709
+ checked: s.mcpEnabled,
710
+ onChange: (v) => s.set({ mcpEnabled: v })
711
+ }
712
+ )
713
+ ] }),
714
+ /* @__PURE__ */ jsxs("p", { className: "settings-subview-desc", children: [
715
+ "MCP connection allows agents to receive and act on annotations.",
716
+ " ",
717
+ /* @__PURE__ */ jsx(
718
+ "a",
719
+ {
720
+ href: "https://www.useclickly.com/docs/mcp",
721
+ target: "_blank",
722
+ rel: "noreferrer",
723
+ className: "settings-subview-link",
724
+ children: "Learn more"
725
+ }
726
+ )
727
+ ] }),
728
+ /* @__PURE__ */ jsxs("div", { className: "settings-subview-field", children: [
729
+ /* @__PURE__ */ jsx("label", { className: "settings-subview-field-label", htmlFor: "clickly-mcp-endpoint", children: "Endpoint" }),
730
+ /* @__PURE__ */ jsx(
731
+ "input",
732
+ {
733
+ id: "clickly-mcp-endpoint",
734
+ type: "text",
735
+ className: "settings-input",
736
+ value: s.mcpEndpoint,
737
+ onChange: (e) => s.set({ mcpEndpoint: e.target.value }),
738
+ placeholder: "http://localhost:4747",
739
+ disabled: !s.mcpEnabled,
740
+ spellCheck: false
741
+ }
742
+ )
743
+ ] }),
744
+ /* @__PURE__ */ jsxs("div", { className: `settings-mcp-status ${meta.modifier}`, children: [
745
+ /* @__PURE__ */ jsx("span", { className: "mcp-dot", "aria-hidden": true }),
746
+ /* @__PURE__ */ jsx("span", { className: "mcp-status-label", children: meta.label }),
747
+ status === "connected" && /* @__PURE__ */ jsxs("span", { className: "mcp-status-detail", children: [
748
+ serverVersion ? `v${serverVersion}` : "",
749
+ lastPingAt ? ` \xB7 pinged ${formatRelative(Date.now() - lastPingAt)} ago` : ""
750
+ ] }),
751
+ status === "error" && lastError ? /* @__PURE__ */ jsx("span", { className: "mcp-status-detail mcp-status-error", children: lastError }) : null
752
+ ] }),
753
+ status === "connected" && s.mcpSessionId ? /* @__PURE__ */ jsxs("div", { className: "settings-mcp-session", children: [
754
+ /* @__PURE__ */ jsx("span", { className: "mcp-status-label", style: { opacity: 0.7 }, children: "Session" }),
755
+ /* @__PURE__ */ jsx("code", { className: "mcp-session-id", children: s.mcpSessionId }),
756
+ /* @__PURE__ */ jsx(
757
+ "button",
758
+ {
759
+ type: "button",
760
+ className: "mcp-session-reset",
761
+ onClick: () => s.set({ mcpSessionId: null }),
762
+ title: "Start a fresh session on the next ping",
763
+ children: "Reset"
764
+ }
765
+ )
766
+ ] }) : null
767
+ ] }),
768
+ /* @__PURE__ */ jsxs("div", { className: "settings-subview-section is-dim", children: [
769
+ /* @__PURE__ */ jsxs("div", { className: "settings-subview-section-head", children: [
770
+ /* @__PURE__ */ jsx("span", { className: "settings-subview-section-title", children: "Webhooks" }),
771
+ /* @__PURE__ */ jsx(InfoTip, { children: "Forward annotation events to your own server. Not yet available \u2014 open an issue if you want this." }),
772
+ /* @__PURE__ */ jsx("span", { className: "settings-subview-badge", children: "Soon" })
773
+ ] }),
774
+ /* @__PURE__ */ jsx("p", { className: "settings-subview-desc", children: "The webhook URL will receive live annotation changes." }),
775
+ /* @__PURE__ */ jsx(
776
+ "textarea",
777
+ {
778
+ className: "settings-input settings-subview-textarea",
779
+ placeholder: "Webhook URL",
780
+ disabled: true,
781
+ rows: 2
782
+ }
783
+ )
784
+ ] })
785
+ ] })
786
+ ] });
787
+ }
788
+ function RowLabel({ children, hint }) {
789
+ return /* @__PURE__ */ jsxs("div", { className: "settings-row-label-compact", children: [
790
+ /* @__PURE__ */ jsx("span", { children }),
791
+ hint ? /* @__PURE__ */ jsx(InfoTip, { children: hint }) : null
668
792
  ] });
669
793
  }
794
+ function InfoTip({ children }) {
795
+ return /* @__PURE__ */ jsxs("span", { className: "settings-infotip", tabIndex: 0, "aria-label": "More info", children: [
796
+ "?",
797
+ /* @__PURE__ */ jsx("span", { className: "settings-infotip-bubble", role: "tooltip", children })
798
+ ] });
799
+ }
800
+ function Toggle({
801
+ checked,
802
+ onChange
803
+ }) {
804
+ return /* @__PURE__ */ jsxs("label", { className: "clickly-toggle", children: [
805
+ /* @__PURE__ */ jsx(
806
+ "input",
807
+ {
808
+ type: "checkbox",
809
+ checked,
810
+ onChange: (e) => onChange(e.target.checked)
811
+ }
812
+ ),
813
+ /* @__PURE__ */ jsx("span", { className: "toggle-track" })
814
+ ] });
815
+ }
816
+ function McpStatusDot() {
817
+ const s = useSettings();
818
+ const status = useMcpStatus((m) => m.status);
819
+ if (!s.mcpEnabled) return null;
820
+ return /* @__PURE__ */ jsx(
821
+ "span",
822
+ {
823
+ className: `settings-mcp-mini-dot is-${status}`,
824
+ "aria-label": `MCP sync: ${status}`
825
+ }
826
+ );
827
+ }
828
+ function labelForDetail(d) {
829
+ switch (d) {
830
+ case "compact":
831
+ return "Compact";
832
+ case "standard":
833
+ return "Standard";
834
+ case "detailed":
835
+ return "Detailed";
836
+ case "forensic":
837
+ return "Forensic";
838
+ }
839
+ }
840
+ var MARKER_PRESETS = [
841
+ "#6366f1",
842
+ // indigo
843
+ "#0ea5e9",
844
+ // sky
845
+ "#06b6d4",
846
+ // cyan (current default)
847
+ "#10b981",
848
+ // emerald
849
+ "#f59e0b",
850
+ // amber
851
+ "#f97316",
852
+ // orange
853
+ "#ef4444"
854
+ // red
855
+ ];
670
856
  function formatRelative(ms) {
671
857
  if (ms < 1500) return "just now";
672
858
  const s = Math.round(ms / 1e3);
@@ -4425,8 +4611,12 @@ var REACT_UI_CSS = `
4425
4611
  /* \u2500\u2500\u2500 Settings panel (redesigned) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
4426
4612
 
4427
4613
  .clickly-settings {
4614
+ /* Fixed-size frame. Width/height are also set inline by SettingsPopover
4615
+ so the values stay in one place (TS source of truth). The flex
4616
+ column splits: header (fixed) on top, scroll body underneath. */
4428
4617
  position: fixed;
4429
- width: 284px;
4618
+ display: flex;
4619
+ flex-direction: column;
4430
4620
  background: #fff;
4431
4621
  border-radius: 14px;
4432
4622
  box-shadow: 0 16px 48px rgba(2,6,23,0.20), 0 0 0 1px rgba(15,23,42,0.07);
@@ -4438,12 +4628,48 @@ var REACT_UI_CSS = `
4438
4628
  overflow: hidden;
4439
4629
  }
4440
4630
 
4631
+ /* Scrollable body \u2014 wraps everything below the header. Header stays
4632
+ pinned; this region grows to fill the remaining height of the
4633
+ fixed-size .clickly-settings frame and scrolls internally when
4634
+ content overflows.
4635
+
4636
+ The overscroll-behavior:contain rule prevents wheel/touch scroll from
4637
+ bubbling into the host page once you reach the top/bottom of the
4638
+ panel \u2014 without this, scrolling past the end would scroll the user's
4639
+ actual app underneath, which is jarring. */
4640
+ .settings-scroll {
4641
+ flex: 1 1 auto;
4642
+ min-height: 0; /* required for flex children to allow overflow */
4643
+ overflow-y: auto;
4644
+ overscroll-behavior: contain;
4645
+ /* Thin scrollbar in both light + dark themes */
4646
+ scrollbar-width: thin;
4647
+ scrollbar-color: rgba(15,23,42,0.20) transparent;
4648
+ }
4649
+ .settings-scroll::-webkit-scrollbar { width: 6px; }
4650
+ .settings-scroll::-webkit-scrollbar-thumb {
4651
+ background: rgba(15,23,42,0.18);
4652
+ border-radius: 3px;
4653
+ }
4654
+ .settings-scroll::-webkit-scrollbar-thumb:hover { background: rgba(15,23,42,0.32); }
4655
+ :host([data-clickly-theme="dark"]) .settings-scroll {
4656
+ scrollbar-color: rgba(255,255,255,0.18) transparent;
4657
+ }
4658
+ :host([data-clickly-theme="dark"]) .settings-scroll::-webkit-scrollbar-thumb {
4659
+ background: rgba(255,255,255,0.18);
4660
+ }
4661
+ :host([data-clickly-theme="dark"]) .settings-scroll::-webkit-scrollbar-thumb:hover {
4662
+ background: rgba(255,255,255,0.32);
4663
+ }
4664
+
4441
4665
  .settings-header {
4442
4666
  display: flex;
4443
4667
  align-items: center;
4444
4668
  justify-content: space-between;
4445
4669
  padding: 14px 14px 12px;
4446
4670
  border-bottom: 1px solid #f1f5f9;
4671
+ /* Header never shrinks \u2014 body scrolls instead */
4672
+ flex-shrink: 0;
4447
4673
  }
4448
4674
 
4449
4675
  .settings-title {
@@ -4538,6 +4764,415 @@ var REACT_UI_CSS = `
4538
4764
  min-width: 0;
4539
4765
  }
4540
4766
 
4767
+ /* \u2500\u2500\u2500 Dense Agentation-style rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4768
+ * One row per setting, single-line label on the left, control on the
4769
+ * right. Hints move into (?) hover tooltips so the row stays compact.
4770
+ * Used by the refactored SettingsPopover.
4771
+ */
4772
+
4773
+ .settings-row-compact {
4774
+ display: flex;
4775
+ align-items: center;
4776
+ justify-content: space-between;
4777
+ gap: 12px;
4778
+ padding: 10px 14px;
4779
+ border-top: 1px solid #f1f5f9;
4780
+ }
4781
+ .settings-row-compact:first-of-type { border-top: none; }
4782
+ .settings-row-compact.settings-row-stacked {
4783
+ flex-direction: column;
4784
+ align-items: stretch;
4785
+ gap: 8px;
4786
+ }
4787
+
4788
+ .settings-row-label-compact {
4789
+ display: inline-flex;
4790
+ align-items: center;
4791
+ gap: 6px;
4792
+ font-size: 13px;
4793
+ font-weight: 500;
4794
+ color: #1e293b;
4795
+ min-width: 0;
4796
+ }
4797
+
4798
+ /* Info tooltip \u2014 small (?) circle + hover bubble */
4799
+ .settings-infotip {
4800
+ display: inline-grid;
4801
+ place-items: center;
4802
+ width: 14px;
4803
+ height: 14px;
4804
+ border-radius: 50%;
4805
+ background: #e2e8f0;
4806
+ color: #64748b;
4807
+ font: 600 9px/1 -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
4808
+ cursor: help;
4809
+ position: relative;
4810
+ flex-shrink: 0;
4811
+ user-select: none;
4812
+ outline: none;
4813
+ }
4814
+ .settings-infotip:hover,
4815
+ .settings-infotip:focus-visible {
4816
+ background: #cbd5e1;
4817
+ color: #1e293b;
4818
+ }
4819
+ .settings-infotip-bubble {
4820
+ position: absolute;
4821
+ bottom: calc(100% + 8px);
4822
+ left: 50%;
4823
+ transform: translateX(-50%);
4824
+ background: rgba(15, 23, 42, 0.98);
4825
+ color: #f1f5f9;
4826
+ font: 400 12px/1.4 -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
4827
+ padding: 6px 10px;
4828
+ border-radius: 6px;
4829
+ width: max-content;
4830
+ max-width: 220px;
4831
+ white-space: normal;
4832
+ text-align: left;
4833
+ pointer-events: none;
4834
+ opacity: 0;
4835
+ transition: opacity 100ms ease 150ms;
4836
+ box-shadow: 0 8px 24px rgba(0,0,0,0.30);
4837
+ z-index: 10;
4838
+ }
4839
+ .settings-infotip-bubble::after {
4840
+ content: "";
4841
+ position: absolute;
4842
+ top: 100%;
4843
+ left: 50%;
4844
+ transform: translateX(-50%);
4845
+ border: 4px solid transparent;
4846
+ border-top-color: rgba(15,23,42,0.98);
4847
+ }
4848
+ .settings-infotip:hover .settings-infotip-bubble,
4849
+ .settings-infotip:focus-visible .settings-infotip-bubble { opacity: 1; }
4850
+
4851
+ /* Inline-value selector (Output Detail) */
4852
+ .settings-inline-select { position: relative; }
4853
+ .settings-inline-select-trigger {
4854
+ display: inline-flex;
4855
+ align-items: center;
4856
+ gap: 6px;
4857
+ background: transparent;
4858
+ border: none;
4859
+ font: inherit;
4860
+ font-size: 13px;
4861
+ color: #475569;
4862
+ cursor: pointer;
4863
+ padding: 4px 6px;
4864
+ border-radius: 6px;
4865
+ transition: background 100ms, color 100ms;
4866
+ }
4867
+ .settings-inline-select-trigger:hover { background: #f1f5f9; color: #0f172a; }
4868
+ .settings-inline-chev {
4869
+ font-size: 14px;
4870
+ line-height: 1;
4871
+ color: #94a3b8;
4872
+ transform: rotate(0deg);
4873
+ }
4874
+ .settings-inline-menu {
4875
+ position: absolute;
4876
+ right: 0;
4877
+ top: calc(100% + 6px);
4878
+ min-width: 140px;
4879
+ background: #fff;
4880
+ border-radius: 8px;
4881
+ box-shadow: 0 12px 32px rgba(2,6,23,0.22), 0 0 0 1px rgba(15,23,42,0.06);
4882
+ padding: 4px;
4883
+ z-index: 20;
4884
+ display: flex;
4885
+ flex-direction: column;
4886
+ gap: 1px;
4887
+ animation: clickly-fade-in 80ms ease;
4888
+ }
4889
+ .settings-inline-menu-item {
4890
+ display: flex;
4891
+ align-items: center;
4892
+ justify-content: flex-start;
4893
+ background: transparent;
4894
+ border: none;
4895
+ font: inherit;
4896
+ font-size: 13px;
4897
+ color: #1e293b;
4898
+ cursor: pointer;
4899
+ padding: 7px 10px;
4900
+ border-radius: 6px;
4901
+ text-align: left;
4902
+ transition: background 100ms, color 100ms;
4903
+ }
4904
+ .settings-inline-menu-item:hover { background: #f1f5f9; }
4905
+ .settings-inline-menu-item.is-active {
4906
+ background: rgba(14,165,233,0.10);
4907
+ color: #0284c7;
4908
+ font-weight: 600;
4909
+ }
4910
+
4911
+ /* Marker color preset circles */
4912
+ .settings-color-presets {
4913
+ display: flex;
4914
+ align-items: center;
4915
+ gap: 8px;
4916
+ flex-wrap: wrap;
4917
+ }
4918
+ .settings-color-preset {
4919
+ position: relative;
4920
+ width: 20px;
4921
+ height: 20px;
4922
+ border-radius: 50%;
4923
+ background: var(--preset, #94a3b8);
4924
+ border: none;
4925
+ cursor: pointer;
4926
+ padding: 0;
4927
+ transition: transform 100ms ease;
4928
+ box-shadow:
4929
+ 0 0 0 1.5px rgba(15,23,42,0.06),
4930
+ 0 1px 2px rgba(15,23,42,0.10);
4931
+ }
4932
+ .settings-color-preset:hover { transform: scale(1.08); }
4933
+ .settings-color-preset.is-active {
4934
+ /* Ring around the active preset \u2014 matches Agentation's selected style. */
4935
+ box-shadow:
4936
+ 0 0 0 2px #fff,
4937
+ 0 0 0 4px currentColor,
4938
+ 0 0 0 5px rgba(15,23,42,0.10);
4939
+ color: var(--preset, #0ea5e9);
4940
+ }
4941
+ .settings-color-preset.is-custom {
4942
+ display: inline-grid;
4943
+ place-items: center;
4944
+ background: #f1f5f9;
4945
+ border: 1.5px dashed #cbd5e1;
4946
+ }
4947
+ .settings-color-preset.is-custom input[type="color"] {
4948
+ position: absolute;
4949
+ inset: 0;
4950
+ opacity: 0;
4951
+ cursor: pointer;
4952
+ border: none;
4953
+ background: transparent;
4954
+ padding: 0;
4955
+ }
4956
+ .settings-color-custom-glyph {
4957
+ font: 600 13px/1 -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
4958
+ color: #64748b;
4959
+ }
4960
+
4961
+ /* MCP disclosure (collapsible footer row) */
4962
+ .settings-disclosure {
4963
+ display: flex;
4964
+ align-items: center;
4965
+ gap: 8px;
4966
+ width: 100%;
4967
+ background: transparent;
4968
+ border: none;
4969
+ border-top: 1px solid #f1f5f9;
4970
+ padding: 12px 14px;
4971
+ font: inherit;
4972
+ font-size: 13px;
4973
+ font-weight: 500;
4974
+ color: #1e293b;
4975
+ cursor: pointer;
4976
+ text-align: left;
4977
+ transition: background 100ms ease;
4978
+ }
4979
+ .settings-disclosure:hover { background: #f8fafc; }
4980
+ .settings-disclosure-label { flex: 1; }
4981
+ .settings-disclosure-chev {
4982
+ color: #94a3b8;
4983
+ font-size: 16px;
4984
+ line-height: 1;
4985
+ transition: transform 160ms ease;
4986
+ }
4987
+ .settings-disclosure.is-open .settings-disclosure-chev {
4988
+ transform: rotate(90deg);
4989
+ }
4990
+
4991
+ .settings-mcp-mini-dot {
4992
+ width: 7px;
4993
+ height: 7px;
4994
+ border-radius: 50%;
4995
+ background: #94a3b8;
4996
+ flex-shrink: 0;
4997
+ }
4998
+ .settings-mcp-mini-dot.is-connecting {
4999
+ background: #f59e0b;
5000
+ animation: mcp-dot-pulse 1.2s ease-in-out infinite;
5001
+ }
5002
+ .settings-mcp-mini-dot.is-connected { background: #10b981; }
5003
+ .settings-mcp-mini-dot.is-error { background: #ef4444; }
5004
+
5005
+ .settings-mcp-panel {
5006
+ background: rgba(15, 23, 42, 0.02);
5007
+ border-top: 1px solid #f1f5f9;
5008
+ padding: 4px 0 10px;
5009
+ }
5010
+
5011
+ /* \u2500\u2500\u2500 Sub-view (MCP / Webhooks page swap) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
5012
+
5013
+ /* Header gets a back arrow on the left + title centered.
5014
+ Re-uses .settings-header but with a 3-column layout. */
5015
+ .settings-header.settings-subview-header {
5016
+ display: grid;
5017
+ grid-template-columns: 24px 1fr 24px;
5018
+ align-items: center;
5019
+ gap: 8px;
5020
+ }
5021
+ .settings-header.settings-subview-header .settings-title {
5022
+ text-align: center;
5023
+ }
5024
+
5025
+ .settings-back {
5026
+ display: grid;
5027
+ place-items: center;
5028
+ width: 24px;
5029
+ height: 24px;
5030
+ background: transparent;
5031
+ border: none;
5032
+ color: #64748b;
5033
+ font: 600 18px/1 -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
5034
+ cursor: pointer;
5035
+ padding: 0;
5036
+ border-radius: 6px;
5037
+ transition: background 100ms, color 100ms;
5038
+ }
5039
+ .settings-back:hover { background: #f1f5f9; color: #0f172a; }
5040
+
5041
+ .settings-subview-section {
5042
+ padding: 14px 16px 16px;
5043
+ border-top: 1px solid #f1f5f9;
5044
+ }
5045
+ .settings-subview-section:first-of-type { border-top: none; }
5046
+ .settings-subview-section.is-dim { opacity: 0.65; }
5047
+
5048
+ .settings-subview-section-head {
5049
+ display: flex;
5050
+ align-items: center;
5051
+ gap: 6px;
5052
+ margin-bottom: 6px;
5053
+ }
5054
+ .settings-subview-section-title {
5055
+ font-size: 14px;
5056
+ font-weight: 600;
5057
+ color: #0f172a;
5058
+ flex: 1;
5059
+ }
5060
+ .settings-subview-section-head .clickly-toggle { margin-left: auto; }
5061
+
5062
+ .settings-subview-desc {
5063
+ margin: 0 0 12px;
5064
+ font-size: 12.5px;
5065
+ line-height: 1.5;
5066
+ color: #64748b;
5067
+ }
5068
+
5069
+ .settings-subview-link {
5070
+ color: #0ea5e9;
5071
+ text-decoration: none;
5072
+ font-weight: 500;
5073
+ }
5074
+ .settings-subview-link:hover {
5075
+ color: #0284c7;
5076
+ text-decoration: underline;
5077
+ }
5078
+
5079
+ .settings-subview-field {
5080
+ display: flex;
5081
+ flex-direction: column;
5082
+ gap: 6px;
5083
+ margin-bottom: 10px;
5084
+ }
5085
+ .settings-subview-field-label {
5086
+ font-size: 12px;
5087
+ font-weight: 600;
5088
+ color: #475569;
5089
+ text-transform: uppercase;
5090
+ letter-spacing: 0.04em;
5091
+ }
5092
+
5093
+ .settings-subview-textarea {
5094
+ resize: none;
5095
+ min-height: 56px;
5096
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
5097
+ font-size: 12px;
5098
+ }
5099
+
5100
+ .settings-subview-badge {
5101
+ margin-left: auto;
5102
+ font-size: 10px;
5103
+ font-weight: 600;
5104
+ text-transform: uppercase;
5105
+ letter-spacing: 0.05em;
5106
+ padding: 2px 6px;
5107
+ border-radius: 4px;
5108
+ background: #fef3c7;
5109
+ color: #b45309;
5110
+ }
5111
+
5112
+ :host([data-clickly-theme="dark"]) .settings-back { color: #94a3b8; }
5113
+ :host([data-clickly-theme="dark"]) .settings-back:hover {
5114
+ background: rgba(255,255,255,0.06);
5115
+ color: #f8fafc;
5116
+ }
5117
+ :host([data-clickly-theme="dark"]) .settings-subview-section {
5118
+ border-top-color: rgba(255,255,255,0.06);
5119
+ }
5120
+ :host([data-clickly-theme="dark"]) .settings-subview-section-title { color: #f1f5f9; }
5121
+ :host([data-clickly-theme="dark"]) .settings-subview-desc { color: #94a3b8; }
5122
+ :host([data-clickly-theme="dark"]) .settings-subview-field-label { color: #94a3b8; }
5123
+ :host([data-clickly-theme="dark"]) .settings-subview-link { color: #38bdf8; }
5124
+ :host([data-clickly-theme="dark"]) .settings-subview-link:hover { color: #7dd3fc; }
5125
+ :host([data-clickly-theme="dark"]) .settings-subview-badge {
5126
+ background: rgba(245, 158, 11, 0.18);
5127
+ color: #fbbf24;
5128
+ }
5129
+
5130
+ /* Dark-mode overrides for the new compact rows */
5131
+ :host([data-clickly-theme="dark"]) .settings-row-compact { border-top-color: rgba(255,255,255,0.06); }
5132
+ :host([data-clickly-theme="dark"]) .settings-row-label-compact { color: #e2e8f0; }
5133
+ :host([data-clickly-theme="dark"]) .settings-infotip { background: rgba(255,255,255,0.10); color: #94a3b8; }
5134
+ :host([data-clickly-theme="dark"]) .settings-infotip:hover { background: rgba(255,255,255,0.18); color: #f8fafc; }
5135
+ :host([data-clickly-theme="dark"]) .settings-inline-select-trigger { color: #cbd5e1; }
5136
+ :host([data-clickly-theme="dark"]) .settings-inline-select-trigger:hover {
5137
+ background: rgba(255,255,255,0.06);
5138
+ color: #f8fafc;
5139
+ }
5140
+ :host([data-clickly-theme="dark"]) .settings-inline-menu {
5141
+ background: #1e293b;
5142
+ box-shadow: 0 12px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.08);
5143
+ }
5144
+ :host([data-clickly-theme="dark"]) .settings-inline-menu-item { color: #e2e8f0; }
5145
+ :host([data-clickly-theme="dark"]) .settings-inline-menu-item:hover { background: rgba(255,255,255,0.06); }
5146
+ :host([data-clickly-theme="dark"]) .settings-inline-menu-item.is-active {
5147
+ background: rgba(14,165,233,0.20);
5148
+ color: #7dd3fc;
5149
+ }
5150
+ :host([data-clickly-theme="dark"]) .settings-color-preset {
5151
+ box-shadow:
5152
+ 0 0 0 1.5px rgba(255,255,255,0.10),
5153
+ 0 1px 2px rgba(0,0,0,0.40);
5154
+ }
5155
+ :host([data-clickly-theme="dark"]) .settings-color-preset.is-active {
5156
+ box-shadow:
5157
+ 0 0 0 2px #0f172a,
5158
+ 0 0 0 4px currentColor,
5159
+ 0 0 0 5px rgba(255,255,255,0.10);
5160
+ }
5161
+ :host([data-clickly-theme="dark"]) .settings-color-preset.is-custom {
5162
+ background: rgba(255,255,255,0.05);
5163
+ border-color: rgba(255,255,255,0.20);
5164
+ }
5165
+ :host([data-clickly-theme="dark"]) .settings-color-custom-glyph { color: #cbd5e1; }
5166
+ :host([data-clickly-theme="dark"]) .settings-disclosure {
5167
+ color: #e2e8f0;
5168
+ border-top-color: rgba(255,255,255,0.06);
5169
+ }
5170
+ :host([data-clickly-theme="dark"]) .settings-disclosure:hover { background: rgba(255,255,255,0.04); }
5171
+ :host([data-clickly-theme="dark"]) .settings-mcp-panel {
5172
+ background: rgba(255,255,255,0.02);
5173
+ border-top-color: rgba(255,255,255,0.06);
5174
+ }
5175
+
4541
5176
  /* Toggle switch */
4542
5177
  .clickly-toggle {
4543
5178
  position: relative;