@vite-plugin-opencode-assistant/components 1.0.25 → 1.0.27

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.
Files changed (33) hide show
  1. package/es/index.d.ts +1 -1
  2. package/es/index.js +1 -1
  3. package/es/open-code-widget/composables/use-inspector.js +118 -79
  4. package/es/open-code-widget/composables/use-persist-state.d.ts +24 -0
  5. package/es/open-code-widget/composables/use-persist-state.js +59 -0
  6. package/es/open-code-widget/src/components/FloatingBubble/FloatingBubble-sfc.css +1 -1
  7. package/es/open-code-widget/src/components/FloatingBubble/FloatingBubble.vue.d.ts +2 -3
  8. package/es/open-code-widget/src/components/FloatingBubble/FloatingBubble.vue.js +50 -60
  9. package/es/open-code-widget/src/components/Trigger.vue.d.ts +2 -5
  10. package/es/open-code-widget/src/components/Trigger.vue.js +10 -38
  11. package/es/open-code-widget/src/context.d.ts +3 -0
  12. package/es/open-code-widget/src/index-sfc.css +1 -1
  13. package/es/open-code-widget/src/index.vue.d.ts +10 -10
  14. package/es/open-code-widget/src/index.vue.js +143 -28
  15. package/lib/@vite-plugin-opencode-assistant/components.cjs.js +359 -200
  16. package/lib/@vite-plugin-opencode-assistant/components.es.js +360 -201
  17. package/lib/components.css +2 -2
  18. package/lib/index.d.ts +1 -1
  19. package/lib/index.js +1 -1
  20. package/lib/open-code-widget/composables/use-inspector.js +118 -79
  21. package/lib/open-code-widget/composables/use-persist-state.d.ts +24 -0
  22. package/lib/open-code-widget/composables/use-persist-state.js +78 -0
  23. package/lib/open-code-widget/src/components/FloatingBubble/FloatingBubble-sfc.css +1 -1
  24. package/lib/open-code-widget/src/components/FloatingBubble/FloatingBubble.vue.d.ts +2 -3
  25. package/lib/open-code-widget/src/components/FloatingBubble/FloatingBubble.vue.js +49 -59
  26. package/lib/open-code-widget/src/components/Trigger.vue.d.ts +2 -5
  27. package/lib/open-code-widget/src/components/Trigger.vue.js +9 -37
  28. package/lib/open-code-widget/src/context.d.ts +3 -0
  29. package/lib/open-code-widget/src/index-sfc.css +1 -1
  30. package/lib/open-code-widget/src/index.vue.d.ts +10 -10
  31. package/lib/open-code-widget/src/index.vue.js +141 -26
  32. package/lib/web-types.json +1 -1
  33. package/package.json +2 -2
@@ -592,27 +592,12 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
592
592
  const props = __props;
593
593
  const emit = __emit;
594
594
  const rootRef = (0, vue.ref)(null);
595
- const getInitialState = () => {
596
- if (props.offset) return {
597
- x: props.offset.x,
598
- y: props.offset.y,
599
- width: 0,
600
- height: 0
601
- };
602
- if (typeof window !== "undefined") return {
603
- x: window.innerWidth - 42 - 24,
604
- y: window.innerHeight - 42 - 24,
605
- width: 0,
606
- height: 0
607
- };
608
- return {
609
- x: 0,
610
- y: 0,
611
- width: 0,
612
- height: 0
613
- };
614
- };
615
- const state = (0, vue.ref)(getInitialState());
595
+ const state = (0, vue.ref)({
596
+ x: 0,
597
+ y: 0,
598
+ width: 0,
599
+ height: 0
600
+ });
616
601
  const isObject = (val) => val !== null && typeof val === "object";
617
602
  const gapX = (0, vue.computed)(() => isObject(props.gap) ? props.gap.x : props.gap);
618
603
  const gapY = (0, vue.computed)(() => isObject(props.gap) ? props.gap.y : props.gap);
@@ -624,20 +609,31 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
624
609
  bottom: windowHeight.value - state.value.height - gapY.value,
625
610
  left: gapX.value
626
611
  }));
612
+ const closest = (arr, target) => {
613
+ return arr.reduce((pre, cur) => Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur);
614
+ };
615
+ const applyMagnetic = () => {
616
+ if (props.magnetic === "x") {
617
+ const nextX = closest([boundary.value.left, boundary.value.right], state.value.x);
618
+ state.value.x = nextX;
619
+ }
620
+ if (props.magnetic === "y") {
621
+ const nextY = closest([boundary.value.top, boundary.value.bottom], state.value.y);
622
+ state.value.y = nextY;
623
+ }
624
+ };
627
625
  const dragging = (0, vue.ref)(false);
628
626
  const initialized = (0, vue.ref)(false);
629
627
  const rootStyle = (0, vue.computed)(() => {
630
628
  const style = {};
631
629
  style.transform = `translate3d(${`${state.value.x}px`}, ${`${state.value.y}px`}, 0)`;
632
- if (dragging.value) style.transition = "none";
630
+ if (dragging.value || !initialized.value) style.transition = "none";
633
631
  else style.transition = "transform 0.3s ease";
634
632
  return style;
635
633
  });
636
- const show = (0, vue.ref)(true);
637
634
  const updateState = () => {
638
- if (!show.value || !rootRef.value || typeof window === "undefined") return;
635
+ if (!rootRef.value || typeof window === "undefined") return;
639
636
  const rect = rootRef.value.getBoundingClientRect();
640
- if (rect.width === 0 || rect.height === 0) return;
641
637
  const { offset } = props;
642
638
  let x = offset ? offset.x : windowWidth.value - rect.width - gapX.value;
643
639
  let y = offset ? offset.y : windowHeight.value - rect.height - gapY.value;
@@ -647,12 +643,25 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
647
643
  if (x > maxX) x = maxX;
648
644
  if (y < gapY.value) y = gapY.value;
649
645
  if (y > maxY) y = maxY;
646
+ const oldX = state.value.x;
647
+ const oldY = state.value.y;
650
648
  state.value = {
651
649
  x,
652
650
  y,
653
651
  width: rect.width,
654
652
  height: rect.height
655
653
  };
654
+ if (!dragging.value) {
655
+ applyMagnetic();
656
+ if (state.value.x !== oldX || state.value.y !== oldY) {
657
+ const offset2 = {
658
+ x: state.value.x,
659
+ y: state.value.y
660
+ };
661
+ emit("update:offset", offset2);
662
+ emit("offset-change", offset2);
663
+ }
664
+ }
656
665
  };
657
666
  const touch = {
658
667
  startX: (0, vue.ref)(0),
@@ -689,6 +698,7 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
689
698
  dragging.value = true;
690
699
  prevX = state.value.x;
691
700
  prevY = state.value.y;
701
+ document.body.classList.add("floating-bubble-dragging");
692
702
  if (!("touches" in e)) {
693
703
  window.addEventListener("mousemove", onTouchMove, { passive: false });
694
704
  window.addEventListener("mouseup", onTouchEnd);
@@ -719,24 +729,15 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
719
729
  });
720
730
  }
721
731
  };
722
- const closest = (arr, target) => {
723
- return arr.reduce((pre, cur) => Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur);
724
- };
725
732
  const onTouchEnd = (e) => {
726
733
  dragging.value = false;
734
+ document.body.classList.remove("floating-bubble-dragging");
727
735
  if (e && !("touches" in e) && e.type === "mouseup") {
728
736
  window.removeEventListener("mousemove", onTouchMove);
729
737
  window.removeEventListener("mouseup", onTouchEnd);
730
738
  }
731
739
  requestAnimationFrame(() => {
732
- if (props.magnetic === "x") {
733
- const nextX = closest([boundary.value.left, boundary.value.right], state.value.x);
734
- state.value.x = nextX;
735
- }
736
- if (props.magnetic === "y") {
737
- const nextY = closest([boundary.value.top, boundary.value.bottom], state.value.y);
738
- state.value.y = nextY;
739
- }
740
+ applyMagnetic();
740
741
  if (!touch.isTap.value) {
741
742
  emit("drag-end");
742
743
  const offset = {
@@ -759,16 +760,15 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
759
760
  }
760
761
  };
761
762
  (0, vue.onMounted)(() => {
763
+ updateState();
762
764
  requestAnimationFrame(() => {
763
- updateState();
764
- requestAnimationFrame(() => {
765
- initialized.value = true;
766
- });
765
+ initialized.value = true;
767
766
  });
768
767
  if (typeof window !== "undefined") window.addEventListener("resize", handleResize);
769
768
  if (rootRef.value) rootRef.value.addEventListener("touchmove", onTouchMove, { passive: false });
770
769
  });
771
770
  (0, vue.onUnmounted)(() => {
771
+ document.body.classList.remove("floating-bubble-dragging");
772
772
  if (typeof window !== "undefined") {
773
773
  window.removeEventListener("resize", handleResize);
774
774
  window.removeEventListener("mousemove", onTouchMove);
@@ -783,21 +783,14 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
783
783
  gapY,
784
784
  () => props.offset
785
785
  ], updateState, { deep: true });
786
- const isOnRightSide = (0, vue.computed)(() => {
787
- return state.value.x > windowWidth.value / 2;
788
- });
789
- __expose({
790
- isOnRightSide,
791
- offset: (0, vue.computed)(() => ({
792
- x: state.value.x,
793
- y: state.value.y
794
- }))
795
- });
786
+ __expose({ offset: (0, vue.computed)(() => ({
787
+ x: state.value.x,
788
+ y: state.value.y
789
+ })) });
796
790
  const __returned__ = {
797
791
  props,
798
792
  emit,
799
793
  rootRef,
800
- getInitialState,
801
794
  state,
802
795
  isObject,
803
796
  gapX,
@@ -805,10 +798,11 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
805
798
  windowWidth,
806
799
  windowHeight,
807
800
  boundary,
801
+ closest,
802
+ applyMagnetic,
808
803
  dragging,
809
804
  initialized,
810
805
  rootStyle,
811
- show,
812
806
  updateState,
813
807
  touch,
814
808
  get prevX() {
@@ -825,11 +819,9 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
825
819
  },
826
820
  onTouchStart,
827
821
  onTouchMove,
828
- closest,
829
822
  onTouchEnd,
830
823
  onClick,
831
- handleResize,
832
- isOnRightSide
824
+ handleResize
833
825
  };
834
826
  Object.defineProperty(__returned__, "__isScriptSetup", {
835
827
  enumerable: false,
@@ -839,7 +831,7 @@ var __vue_sfc__$2 = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps$1(__s
839
831
  }
840
832
  }));
841
833
  function __vue_render__$2(_ctx, _cache, $props, $setup, $data, $options) {
842
- return (0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, { to: $props.teleport }, [(0, vue.withDirectives)((0, vue.createElementVNode)("div", {
834
+ return (0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, { to: $props.teleport }, [(0, vue.createElementVNode)("div", {
843
835
  ref: "rootRef",
844
836
  class: "floating-bubble",
845
837
  style: (0, vue.normalizeStyle)($setup.rootStyle),
@@ -848,60 +840,27 @@ function __vue_render__$2(_ctx, _cache, $props, $setup, $data, $options) {
848
840
  onTouchcancel: $setup.onTouchEnd,
849
841
  onMousedown: $setup.onTouchStart,
850
842
  onClickCapture: $setup.onClick
851
- }, [(0, vue.renderSlot)(_ctx.$slots, "default")], 36), [[vue.vShow, $setup.show && $setup.initialized]])], 8, ["to"]);
843
+ }, [(0, vue.renderSlot)(_ctx.$slots, "default")], 36)], 8, ["to"]);
852
844
  }
853
845
  __vue_sfc__$2.render = __vue_render__$2;
854
846
  var FloatingBubble_vue_default = __vue_sfc__$2;
855
847
  //#endregion
856
848
  //#region es/open-code-widget/src/components/Trigger.vue.js
857
- var STORAGE_KEY = "opencode-bubble-offset";
858
849
  var __vue_sfc__$1 = /* @__PURE__ */ (0, vue.defineComponent)({
859
850
  __name: "Trigger",
860
- emits: [
861
- "offset-change",
862
- "drag-start",
863
- "drag-end"
864
- ],
851
+ emits: ["drag-start", "drag-end"],
865
852
  setup(__props, { expose: __expose, emit: __emit }) {
866
- const { buttonActive: active, open, hotkeyLabel, thinking, resolvedTheme, handleToggle } = useOpenCodeWidgetContext();
867
- const loadOffset = () => {
868
- try {
869
- const saved = localStorage.getItem(STORAGE_KEY);
870
- if (saved) {
871
- const parsed = JSON.parse(saved);
872
- if (parsed && (parsed.x !== 0 || parsed.y !== 0)) return parsed;
873
- }
874
- } catch (e) {}
875
- return {
876
- x: 0,
877
- y: 0
878
- };
879
- };
880
- const offset = (0, vue.ref)(loadOffset());
853
+ const { buttonActive: active, open, hotkeyLabel, thinking, resolvedTheme, handleToggle, bubbleOffset, handleBubbleOffsetChange } = useOpenCodeWidgetContext();
854
+ const offset = (0, vue.ref)(bubbleOffset.value);
881
855
  const emit = __emit;
882
- const saveOffset = (value) => {
883
- try {
884
- localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
885
- } catch (e) {}
886
- };
887
856
  const handleOffsetChange = (value) => {
888
857
  offset.value = value;
889
- saveOffset(value);
890
- emit("offset-change", value);
858
+ handleBubbleOffsetChange(value);
891
859
  };
892
- const bubbleRef = (0, vue.ref)(null);
893
- const isOnRightSide = (0, vue.computed)(() => {
894
- if (typeof window === "undefined") return true;
895
- const centerX = window.innerWidth / 2;
896
- return offset.value.x > centerX;
897
- });
898
- (0, vue.onMounted)(() => {
899
- if (offset.value.x !== 0 || offset.value.y !== 0) emit("offset-change", offset.value);
900
- });
901
- __expose({
902
- isOnRightSide,
903
- offset
860
+ (0, vue.watch)(bubbleOffset, (newOffset) => {
861
+ offset.value = newOffset;
904
862
  });
863
+ __expose({ offset });
905
864
  const __returned__ = {
906
865
  active,
907
866
  open,
@@ -909,14 +868,11 @@ var __vue_sfc__$1 = /* @__PURE__ */ (0, vue.defineComponent)({
909
868
  thinking,
910
869
  resolvedTheme,
911
870
  handleToggle,
912
- STORAGE_KEY,
913
- loadOffset,
871
+ bubbleOffset,
872
+ handleBubbleOffsetChange,
914
873
  offset,
915
874
  emit,
916
- saveOffset,
917
875
  handleOffsetChange,
918
- bubbleRef,
919
- isOnRightSide,
920
876
  FloatingBubble: FloatingBubble_vue_default
921
877
  };
922
878
  Object.defineProperty(__returned__, "__isScriptSetup", {
@@ -1419,35 +1375,16 @@ function findFileInfo(element, inspector) {
1419
1375
  };
1420
1376
  }
1421
1377
  function getPreciseElementAtPoint(x, y, boundary) {
1422
- var _a, _b;
1423
- const highlight = document.querySelector(".opencode-element-highlight");
1424
- const tooltip = document.querySelector(".opencode-element-tooltip");
1425
- const highlightDisplay = ((_a = highlight == null ? void 0 : highlight.getAttribute("style")) == null ? void 0 : _a.includes("display: block")) ? "block" : "none";
1426
- const tooltipDisplay = ((_b = tooltip == null ? void 0 : tooltip.getAttribute("style")) == null ? void 0 : _b.includes("display: block")) ? "block" : "none";
1427
- if (highlight) highlight.style.display = "none";
1428
- if (tooltip) tooltip.style.display = "none";
1429
- let element = null;
1430
- try {
1431
- const elements = document.elementsFromPoint(x, y);
1432
- for (const el of elements) {
1433
- if (el.closest("#vue-inspector-container")) continue;
1434
- if (el.closest(".opencode-widget")) continue;
1435
- if (el.hasAttribute("data-v-inspector-ignore")) continue;
1436
- if (boundary) {
1437
- if (boundary.contains(el) || el === boundary) {
1438
- element = el;
1439
- break;
1440
- }
1441
- } else {
1442
- element = el;
1443
- break;
1444
- }
1445
- }
1446
- } finally {
1447
- if (highlight) highlight.style.display = highlightDisplay;
1448
- if (tooltip) tooltip.style.display = tooltipDisplay;
1378
+ const elements = document.elementsFromPoint(x, y);
1379
+ for (const el of elements) {
1380
+ if (el.closest("#vue-inspector-container")) continue;
1381
+ if (el.closest(".opencode-widget")) continue;
1382
+ if (el.hasAttribute("data-v-inspector-ignore")) continue;
1383
+ if (boundary) {
1384
+ if (boundary.contains(el) || el === boundary) return el;
1385
+ } else return el;
1449
1386
  }
1450
- return element;
1387
+ return null;
1451
1388
  }
1452
1389
  function useInspector(options) {
1453
1390
  const highlightVisible = (0, vue.ref)(false);
@@ -1468,74 +1405,117 @@ function useInspector(options) {
1468
1405
  });
1469
1406
  const INSPECTOR_CHECK_INTERVAL = 500;
1470
1407
  let inspectorCheckTimer = null;
1408
+ let currentHighlightElement = null;
1409
+ let currentFileInfo = {
1410
+ file: null,
1411
+ line: null,
1412
+ column: null
1413
+ };
1414
+ let currentPrimary = "#3b82f6";
1415
+ let currentPrimaryBg = "rgba(59, 130, 246, 0.1)";
1416
+ let currentDescription = "";
1417
+ let currentFileInfoText = "";
1471
1418
  function handleMouseMoveCore(e) {
1472
1419
  var _a, _b;
1473
1420
  if (!options.selectMode.value) return;
1474
1421
  const inspector = window.__VUE_INSPECTOR__;
1422
+ const highlight = document.querySelector(".opencode-element-highlight");
1423
+ const tooltip = document.querySelector(".opencode-element-tooltip");
1424
+ if (highlight) highlight.style.pointerEvents = "none";
1425
+ if (tooltip) tooltip.style.pointerEvents = "none";
1475
1426
  let elementToHighlight = null;
1427
+ let targetNode = null;
1476
1428
  let fileInfo = {
1477
1429
  file: null,
1478
1430
  line: null,
1479
1431
  column: null
1480
1432
  };
1481
- if (inspector) {
1482
- const { targetNode, params } = inspector.getTargetNode(e);
1483
- if (targetNode) {
1484
- elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode) || targetNode;
1485
- if (params && params.file) fileInfo = {
1486
- file: params.file,
1487
- line: (_a = params.line) != null ? _a : null,
1488
- column: (_b = params.column) != null ? _b : null
1489
- };
1490
- else if (elementToHighlight) fileInfo = findFileInfo(elementToHighlight, inspector);
1433
+ try {
1434
+ if (inspector) {
1435
+ const result = inspector.getTargetNode(e);
1436
+ targetNode = result.targetNode;
1437
+ const params = result.params;
1438
+ if (targetNode) {
1439
+ elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode) || targetNode;
1440
+ if (params && params.file) fileInfo = {
1441
+ file: params.file,
1442
+ line: (_a = params.line) != null ? _a : null,
1443
+ column: (_b = params.column) != null ? _b : null
1444
+ };
1445
+ else fileInfo = findFileInfo(targetNode, inspector);
1446
+ }
1491
1447
  }
1448
+ if (!elementToHighlight) elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, null);
1449
+ if (elementToHighlight && !fileInfo.file) fileInfo = getFileInfoFromVueInstance(elementToHighlight) || fileInfo;
1450
+ } finally {
1451
+ if (highlight) highlight.style.pointerEvents = "";
1452
+ if (tooltip) tooltip.style.pointerEvents = "";
1492
1453
  }
1493
- if (!elementToHighlight) elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, null);
1494
- if (elementToHighlight && !fileInfo.file) fileInfo = getFileInfoFromVueInstance(elementToHighlight) || fileInfo;
1495
1454
  if (elementToHighlight) {
1496
- const rect = elementToHighlight.getBoundingClientRect();
1497
- const widget = document.querySelector(".opencode-widget");
1498
- let primary = "#3b82f6";
1499
- let primaryBg = "rgba(59, 130, 246, 0.1)";
1500
- if (widget) {
1501
- const style = getComputedStyle(widget);
1502
- primary = style.getPropertyValue("--oc-primary").trim() || primary;
1503
- primaryBg = style.getPropertyValue("--oc-primary-bg").trim() || primaryBg;
1504
- }
1505
- highlightVisible.value = true;
1506
- highlightStyle.value = {
1507
- top: `${rect.top}px`,
1508
- left: `${rect.left}px`,
1509
- width: `${rect.width}px`,
1510
- height: `${rect.height}px`,
1511
- border: `2px solid ${primary}`,
1512
- background: primaryBg
1513
- };
1514
- const description = getElementDescription(elementToHighlight);
1515
- const fileName = fileInfo.file ? fileInfo.file.split("/").pop() : "";
1455
+ const elementChanged = currentHighlightElement !== elementToHighlight;
1456
+ if (elementChanged) {
1457
+ currentHighlightElement = elementToHighlight;
1458
+ currentFileInfo = fileInfo;
1459
+ const widget = document.querySelector(".opencode-widget");
1460
+ if (widget) {
1461
+ const style = getComputedStyle(widget);
1462
+ currentPrimary = style.getPropertyValue("--oc-primary").trim() || currentPrimary;
1463
+ currentPrimaryBg = style.getPropertyValue("--oc-primary-bg").trim() || currentPrimaryBg;
1464
+ }
1465
+ currentDescription = getElementDescription(elementToHighlight);
1466
+ } else if (!currentFileInfo.file && fileInfo.file) currentFileInfo = fileInfo;
1467
+ const fileName = currentFileInfo.file ? currentFileInfo.file.split("/").pop() : "";
1516
1468
  let lineInfo = "";
1517
- if (fileInfo.line) {
1518
- lineInfo = `:${fileInfo.line}`;
1519
- if (fileInfo.column) lineInfo += `:${fileInfo.column}`;
1469
+ if (currentFileInfo.line) {
1470
+ lineInfo = `:${currentFileInfo.line}`;
1471
+ if (currentFileInfo.column) lineInfo += `:${currentFileInfo.column}`;
1520
1472
  }
1521
- tooltipContent.value = {
1522
- description,
1523
- fileInfo: fileName ? `${fileName}${lineInfo}` : ""
1473
+ const newFileInfoText = fileName ? `${fileName}${lineInfo}` : "";
1474
+ if (elementChanged || currentFileInfoText !== newFileInfoText) {
1475
+ currentFileInfoText = newFileInfoText;
1476
+ tooltipContent.value = {
1477
+ description: currentDescription,
1478
+ fileInfo: currentFileInfoText
1479
+ };
1480
+ }
1481
+ const rect = elementToHighlight.getBoundingClientRect();
1482
+ const newTop = `${rect.top}px`;
1483
+ const newLeft = `${rect.left}px`;
1484
+ const newWidth = `${rect.width}px`;
1485
+ const newHeight = `${rect.height}px`;
1486
+ if (highlightStyle.value.top !== newTop || highlightStyle.value.left !== newLeft || highlightStyle.value.width !== newWidth || highlightStyle.value.height !== newHeight) highlightStyle.value = {
1487
+ top: newTop,
1488
+ left: newLeft,
1489
+ width: newWidth,
1490
+ height: newHeight,
1491
+ border: `2px solid ${currentPrimary}`,
1492
+ background: currentPrimaryBg
1524
1493
  };
1525
- tooltipVisible.value = true;
1526
1494
  const tooltipHeight = 50;
1527
1495
  const tooltipWidth = 200;
1528
1496
  let tooltipTop = rect.top - tooltipHeight - 8;
1529
1497
  let tooltipLeft = rect.left;
1530
1498
  if (tooltipTop < 10) tooltipTop = rect.bottom + 8;
1531
1499
  if (tooltipLeft + tooltipWidth > window.innerWidth - 10) tooltipLeft = window.innerWidth - tooltipWidth - 10;
1532
- tooltipStyle.value = {
1533
- top: `${tooltipTop}px`,
1534
- left: `${tooltipLeft}px`
1500
+ const newTooltipTop = `${tooltipTop}px`;
1501
+ const newTooltipLeft = `${tooltipLeft}px`;
1502
+ if (tooltipStyle.value.top !== newTooltipTop || tooltipStyle.value.left !== newTooltipLeft) tooltipStyle.value = {
1503
+ top: newTooltipTop,
1504
+ left: newTooltipLeft
1535
1505
  };
1506
+ if (!highlightVisible.value) highlightVisible.value = true;
1507
+ if (!tooltipVisible.value) tooltipVisible.value = true;
1536
1508
  } else {
1537
- highlightVisible.value = false;
1538
- tooltipVisible.value = false;
1509
+ currentHighlightElement = null;
1510
+ currentDescription = "";
1511
+ currentFileInfoText = "";
1512
+ currentFileInfo = {
1513
+ file: null,
1514
+ line: null,
1515
+ column: null
1516
+ };
1517
+ if (highlightVisible.value) highlightVisible.value = false;
1518
+ if (tooltipVisible.value) tooltipVisible.value = false;
1539
1519
  }
1540
1520
  }
1541
1521
  const handleMouseMove = throttle(handleMouseMoveCore, 16);
@@ -1599,6 +1579,14 @@ function useInspector(options) {
1599
1579
  if (inspector) inspector.disable();
1600
1580
  document.removeEventListener("mousemove", handleMouseMove);
1601
1581
  document.removeEventListener("keydown", handleKeydown, true);
1582
+ currentHighlightElement = null;
1583
+ currentDescription = "";
1584
+ currentFileInfoText = "";
1585
+ currentFileInfo = {
1586
+ file: null,
1587
+ line: null,
1588
+ column: null
1589
+ };
1602
1590
  highlightVisible.value = false;
1603
1591
  tooltipVisible.value = false;
1604
1592
  }
@@ -1629,6 +1617,62 @@ function useInspector(options) {
1629
1617
  };
1630
1618
  }
1631
1619
  //#endregion
1620
+ //#region es/open-code-widget/composables/use-persist-state.js
1621
+ var STORAGE_KEY = "opencode-widget-state";
1622
+ function loadState() {
1623
+ if (typeof window === "undefined") return null;
1624
+ try {
1625
+ const stored = localStorage.getItem(STORAGE_KEY);
1626
+ if (stored) return JSON.parse(stored);
1627
+ } catch (e) {
1628
+ console.warn("[OpenCodeWidget] Failed to load persisted state:", e);
1629
+ }
1630
+ return null;
1631
+ }
1632
+ function saveState(state) {
1633
+ if (typeof window === "undefined") return;
1634
+ try {
1635
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
1636
+ } catch (e) {
1637
+ console.warn("[OpenCodeWidget] Failed to save state:", e);
1638
+ }
1639
+ }
1640
+ function usePersistState(options) {
1641
+ const restoreState = () => {
1642
+ const saved = loadState();
1643
+ if (options.onRestore) options.onRestore(saved || {});
1644
+ return saved;
1645
+ };
1646
+ const getCurrentState = () => ({
1647
+ open: options.open.value,
1648
+ minimized: options.minimized.value,
1649
+ promptDockVisible: options.promptDockVisible.value,
1650
+ bubbleOffset: options.bubbleOffset.value,
1651
+ theme: options.theme.value,
1652
+ sessionListCollapsed: options.sessionListCollapsed.value
1653
+ });
1654
+ const persistState = () => {
1655
+ saveState(getCurrentState());
1656
+ };
1657
+ (0, vue.onMounted)(() => {
1658
+ restoreState();
1659
+ (0, vue.watch)([
1660
+ options.open,
1661
+ options.minimized,
1662
+ options.promptDockVisible,
1663
+ options.bubbleOffset,
1664
+ options.theme,
1665
+ options.sessionListCollapsed
1666
+ ], () => {
1667
+ persistState();
1668
+ }, { deep: true });
1669
+ });
1670
+ return {
1671
+ restoreState,
1672
+ persistState
1673
+ };
1674
+ }
1675
+ //#endregion
1632
1676
  //#region es/open-code-widget/src/index.vue.js
1633
1677
  var __defProp = Object.defineProperty;
1634
1678
  var __defProps = Object.defineProperties;
@@ -1787,6 +1831,10 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
1787
1831
  "thinking-change"
1788
1832
  ],
1789
1833
  setup(__props, { expose: __expose, emit: __emit }) {
1834
+ (0, vue.useCssVars)((_ctx) => ({
1835
+ "-chatAnimationOrigin.x": chatAnimationOrigin.value.x,
1836
+ "-chatAnimationOrigin.y": chatAnimationOrigin.value.y
1837
+ }));
1790
1838
  const props = __props;
1791
1839
  const emit = __emit;
1792
1840
  const slots = (0, vue.useSlots)();
@@ -1828,17 +1876,26 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
1828
1876
  var _a;
1829
1877
  (_a = frameRef.value) == null || _a.sendMessageToIframe(type, data);
1830
1878
  };
1879
+ const localSessionListCollapsed = (0, vue.ref)(props.sessionListCollapsed);
1880
+ const minimized = (0, vue.ref)(false);
1881
+ const promptDockVisible = (0, vue.ref)(true);
1882
+ const isRestoring = (0, vue.ref)(true);
1883
+ const iframeLoaded = (0, vue.ref)(false);
1884
+ const syncStateToIframe = () => {
1885
+ if (!iframeLoaded.value) return;
1886
+ sendMessageToIframe("prompt-dock-visibility-change", { visible: promptDockVisible.value });
1887
+ sendMessageToIframe("minimize-state-change", { minimized: minimized.value });
1888
+ };
1831
1889
  const handleFrameLoaded = () => {
1832
1890
  emit("frame-loaded");
1891
+ iframeLoaded.value = true;
1892
+ syncStateToIframe();
1833
1893
  };
1834
1894
  __expose({
1835
1895
  showNotification,
1836
1896
  showConfirmDialog,
1837
1897
  sendMessageToIframe
1838
1898
  });
1839
- const localSessionListCollapsed = (0, vue.ref)(props.sessionListCollapsed);
1840
- const minimized = (0, vue.ref)(false);
1841
- const promptDockVisible = (0, vue.ref)(true);
1842
1899
  (0, vue.watch)(() => props.sessionListCollapsed, (val) => {
1843
1900
  localSessionListCollapsed.value = val;
1844
1901
  });
@@ -1913,6 +1970,48 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
1913
1970
  emit("toggle-select-mode", false);
1914
1971
  }
1915
1972
  });
1973
+ const bubbleOffset = (0, vue.ref)(void 0);
1974
+ usePersistState({
1975
+ open: (0, vue.toRef)(props, "open"),
1976
+ minimized,
1977
+ promptDockVisible,
1978
+ bubbleOffset,
1979
+ theme: (0, vue.toRef)(props, "theme"),
1980
+ sessionListCollapsed: localSessionListCollapsed,
1981
+ onRestore: (state) => {
1982
+ if (state.open !== void 0 && state.open !== props.open) {
1983
+ emit("update:open", state.open);
1984
+ emit("toggle", state.open);
1985
+ }
1986
+ if (state.minimized !== void 0) minimized.value = state.minimized;
1987
+ if (state.bubbleOffset !== void 0) {
1988
+ const bubbleSize = 44;
1989
+ const margin = 10;
1990
+ const maxX = window.innerWidth - bubbleSize - margin;
1991
+ const maxY = window.innerHeight - bubbleSize - margin;
1992
+ bubbleOffset.value = {
1993
+ x: Math.max(margin, Math.min(state.bubbleOffset.x, maxX)),
1994
+ y: Math.max(margin, Math.min(state.bubbleOffset.y, maxY))
1995
+ };
1996
+ }
1997
+ if (state.theme !== void 0 && state.theme !== props.theme) {
1998
+ emit("update:theme", state.theme);
1999
+ emit("toggle-theme", state.theme);
2000
+ }
2001
+ if (state.sessionListCollapsed !== void 0 && state.sessionListCollapsed !== props.sessionListCollapsed) {
2002
+ localSessionListCollapsed.value = state.sessionListCollapsed;
2003
+ emit("update:sessionListCollapsed", state.sessionListCollapsed);
2004
+ }
2005
+ if (state.promptDockVisible !== void 0) promptDockVisible.value = state.promptDockVisible;
2006
+ else if (minimized.value) promptDockVisible.value = false;
2007
+ (0, vue.nextTick)(() => {
2008
+ syncStateToIframe();
2009
+ setTimeout(() => {
2010
+ isRestoring.value = false;
2011
+ }, 50);
2012
+ });
2013
+ }
2014
+ });
1916
2015
  const handleToggleMinimize = () => {
1917
2016
  minimized.value = !minimized.value;
1918
2017
  promptDockVisible.value = !minimized.value;
@@ -1923,40 +2022,70 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
1923
2022
  promptDockVisible.value = !promptDockVisible.value;
1924
2023
  sendMessageToIframe("prompt-dock-visibility-change", { visible: promptDockVisible.value });
1925
2024
  };
1926
- const bubbleOffset = (0, vue.ref)({
1927
- x: 0,
1928
- y: 0
2025
+ const windowWidth = (0, vue.ref)(typeof window !== "undefined" ? window.innerWidth : 0);
2026
+ const windowHeight = (0, vue.ref)(typeof window !== "undefined" ? window.innerHeight : 0);
2027
+ const handleWindowResize = () => {
2028
+ if (typeof window !== "undefined") {
2029
+ windowWidth.value = window.innerWidth;
2030
+ windowHeight.value = window.innerHeight;
2031
+ }
2032
+ };
2033
+ (0, vue.onMounted)(() => {
2034
+ if (typeof window !== "undefined") window.addEventListener("resize", handleWindowResize);
2035
+ });
2036
+ (0, vue.onUnmounted)(() => {
2037
+ if (typeof window !== "undefined") window.removeEventListener("resize", handleWindowResize);
2038
+ });
2039
+ const bubbleQuadrant = (0, vue.computed)(() => {
2040
+ var _a, _b, _c, _d;
2041
+ if (typeof window === "undefined") return "bottom-right";
2042
+ const centerX = windowWidth.value / 2;
2043
+ const centerY = windowHeight.value / 2;
2044
+ const bubbleSize = 44;
2045
+ const currentOffset = (_b = (_a = triggerRef.value) == null ? void 0 : _a.offset) != null ? _b : bubbleOffset.value;
2046
+ const effectiveX = ((_c = currentOffset == null ? void 0 : currentOffset.x) != null ? _c : windowWidth.value - bubbleSize - 24) + bubbleSize / 2;
2047
+ const effectiveY = ((_d = currentOffset == null ? void 0 : currentOffset.y) != null ? _d : windowHeight.value - bubbleSize - 24) + bubbleSize / 2;
2048
+ if (effectiveX >= centerX && effectiveY >= centerY) return "bottom-right";
2049
+ else if (effectiveX < centerX && effectiveY >= centerY) return "bottom-left";
2050
+ else if (effectiveX >= centerX && effectiveY < centerY) return "top-right";
2051
+ else return "top-left";
1929
2052
  });
1930
2053
  const isBubbleOnRightSide = (0, vue.computed)(() => {
1931
- if (typeof window === "undefined") return true;
1932
- const centerX = window.innerWidth / 2;
1933
- return bubbleOffset.value.x > centerX;
2054
+ const quadrant = bubbleQuadrant.value;
2055
+ return quadrant === "top-right" || quadrant === "bottom-right";
1934
2056
  });
1935
2057
  const chatPositionStyle = (0, vue.computed)(() => {
2058
+ var _a, _b, _c;
1936
2059
  if (typeof window === "undefined") return {};
1937
- const windowWidth = window.innerWidth;
1938
- const windowHeight = window.innerHeight;
1939
2060
  const chatWidth = minimized.value ? 300 : 700;
1940
- const chatHeight = minimized.value ? 300 : Math.min(windowHeight * .86, windowHeight - 40);
2061
+ const chatHeight = minimized.value ? 300 : Math.min(windowHeight.value * .86, windowHeight.value - 40);
1941
2062
  const gap = 24;
1942
2063
  const bubbleSize = 44;
1943
2064
  const screenMargin = 20;
2065
+ const effectiveOffset = (_c = (_b = (_a = triggerRef.value) == null ? void 0 : _a.offset) != null ? _b : bubbleOffset.value) != null ? _c : {
2066
+ x: windowWidth.value - bubbleSize - gap,
2067
+ y: windowHeight.value - bubbleSize - gap
2068
+ };
1944
2069
  const style = {};
1945
2070
  if (isBubbleOnRightSide.value) {
1946
- let rightPos = windowWidth - bubbleOffset.value.x + gap;
1947
- const maxRight = windowWidth - chatWidth - screenMargin;
2071
+ let rightPos = windowWidth.value - effectiveOffset.x + gap;
2072
+ const minRight = screenMargin;
2073
+ const maxRight = windowWidth.value - chatWidth - screenMargin;
1948
2074
  if (rightPos > maxRight) rightPos = maxRight;
2075
+ if (rightPos < minRight) rightPos = minRight;
1949
2076
  style.right = `${rightPos}px`;
1950
2077
  style.left = "auto";
1951
2078
  } else {
1952
- let leftPos = bubbleOffset.value.x + bubbleSize + gap;
1953
- const maxLeft = windowWidth - chatWidth - screenMargin;
2079
+ let leftPos = effectiveOffset.x + bubbleSize + gap;
2080
+ const minLeft = screenMargin;
2081
+ const maxLeft = windowWidth.value - chatWidth - screenMargin;
1954
2082
  if (leftPos > maxLeft) leftPos = maxLeft;
2083
+ if (leftPos < minLeft) leftPos = minLeft;
1955
2084
  style.left = `${leftPos}px`;
1956
2085
  style.right = "auto";
1957
2086
  }
1958
- let bottomPos = windowHeight - bubbleOffset.value.y - bubbleSize;
1959
- const maxBottom = windowHeight - chatHeight - screenMargin;
2087
+ let bottomPos = windowHeight.value - effectiveOffset.y - bubbleSize;
2088
+ const maxBottom = windowHeight.value - chatHeight - screenMargin;
1960
2089
  if (bottomPos > maxBottom) bottomPos = maxBottom;
1961
2090
  if (bottomPos < screenMargin) bottomPos = screenMargin;
1962
2091
  style.bottom = `${bottomPos}px`;
@@ -1965,6 +2094,26 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
1965
2094
  const handleBubbleOffsetChange = (offset) => {
1966
2095
  bubbleOffset.value = offset;
1967
2096
  };
2097
+ const chatAnimationOrigin = (0, vue.computed)(() => {
2098
+ switch (bubbleQuadrant.value) {
2099
+ case "top-left": return {
2100
+ x: "-20px",
2101
+ y: "-20px"
2102
+ };
2103
+ case "top-right": return {
2104
+ x: "20px",
2105
+ y: "-20px"
2106
+ };
2107
+ case "bottom-left": return {
2108
+ x: "-20px",
2109
+ y: "20px"
2110
+ };
2111
+ default: return {
2112
+ x: "20px",
2113
+ y: "20px"
2114
+ };
2115
+ }
2116
+ });
1968
2117
  const isDragging = (0, vue.ref)(false);
1969
2118
  let wasOpenBeforeDrag = false;
1970
2119
  const handleDragStart = () => {
@@ -1998,6 +2147,7 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
1998
2147
  thinking: (0, vue.toRef)(props, "thinking"),
1999
2148
  minimized,
2000
2149
  promptDockVisible,
2150
+ bubbleOffset,
2001
2151
  iframeSource,
2002
2152
  buttonActive,
2003
2153
  sessionListTitle,
@@ -2019,7 +2169,8 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
2019
2169
  handleClickSelectedNode,
2020
2170
  handleRemoveSelectedNode: (payload) => handleRemoveSelectedNode(payload.item, payload.index, payload.source),
2021
2171
  handleClearSelectedNodes,
2022
- handleFrameLoaded
2172
+ handleFrameLoaded,
2173
+ handleBubbleOffsetChange
2023
2174
  });
2024
2175
  const __returned__ = {
2025
2176
  props,
@@ -2049,10 +2200,13 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
2049
2200
  frameRef,
2050
2201
  triggerRef,
2051
2202
  sendMessageToIframe,
2052
- handleFrameLoaded,
2053
2203
  localSessionListCollapsed,
2054
2204
  minimized,
2055
2205
  promptDockVisible,
2206
+ isRestoring,
2207
+ iframeLoaded,
2208
+ syncStateToIframe,
2209
+ handleFrameLoaded,
2056
2210
  buttonActive,
2057
2211
  containerClasses,
2058
2212
  iframeSource,
@@ -2079,12 +2233,17 @@ var __vue_sfc__ = /* @__PURE__ */ (0, vue.defineComponent)(__spreadProps(__sprea
2079
2233
  tooltipVisible,
2080
2234
  tooltipStyle,
2081
2235
  tooltipContent,
2236
+ bubbleOffset,
2082
2237
  handleToggleMinimize,
2083
2238
  handleTogglePromptDock,
2084
- bubbleOffset,
2239
+ windowWidth,
2240
+ windowHeight,
2241
+ handleWindowResize,
2242
+ bubbleQuadrant,
2085
2243
  isBubbleOnRightSide,
2086
2244
  chatPositionStyle,
2087
2245
  handleBubbleOffsetChange,
2246
+ chatAnimationOrigin,
2088
2247
  isDragging,
2089
2248
  get wasOpenBeforeDrag() {
2090
2249
  return wasOpenBeforeDrag;
@@ -2136,7 +2295,6 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
2136
2295
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", { class: (0, vue.normalizeClass)($setup.containerClasses) }, [
2137
2296
  (0, vue.createVNode)($setup["Trigger"], {
2138
2297
  ref: "triggerRef",
2139
- onOffsetChange: $setup.handleBubbleOffsetChange,
2140
2298
  onDragStart: $setup.handleDragStart,
2141
2299
  onDragEnd: $setup.handleDragEnd
2142
2300
  }, (0, vue.createSlots)({ _: 2 }, [$setup.slots["button-icon"] ? {
@@ -2148,7 +2306,8 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
2148
2306
  class: (0, vue.normalizeClass)(["opencode-chat", {
2149
2307
  open: $props.open,
2150
2308
  minimized: $setup.minimized,
2151
- dragging: $setup.isDragging
2309
+ dragging: $setup.isDragging,
2310
+ "no-transition": $setup.isRestoring
2152
2311
  }]),
2153
2312
  style: (0, vue.normalizeStyle)($setup.chatPositionStyle)
2154
2313
  }, [
@@ -2203,11 +2362,11 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
2203
2362
  (0, vue.createVNode)($setup["SelectHint"]),
2204
2363
  (0, vue.withDirectives)((0, vue.createElementVNode)("div", {
2205
2364
  class: "opencode-element-highlight",
2206
- style: (0, vue.normalizeStyle)(__spreadValues({ display: $setup.highlightVisible ? "block" : "none" }, $setup.highlightStyle))
2365
+ style: (0, vue.normalizeStyle)($setup.highlightStyle)
2207
2366
  }, null, 4), [[vue.vShow, $setup.highlightVisible]]),
2208
2367
  (0, vue.withDirectives)((0, vue.createElementVNode)("div", {
2209
2368
  class: "opencode-element-tooltip",
2210
- style: (0, vue.normalizeStyle)(__spreadValues({ display: $setup.tooltipVisible ? "block" : "none" }, $setup.tooltipStyle))
2369
+ style: (0, vue.normalizeStyle)($setup.tooltipStyle)
2211
2370
  }, [(0, vue.createElementVNode)("div", _hoisted_3, (0, vue.toDisplayString)($setup.tooltipContent.description), 1), (0, vue.createElementVNode)("div", _hoisted_4, (0, vue.toDisplayString)($setup.tooltipContent.fileInfo), 1)], 4), [[vue.vShow, $setup.tooltipVisible]]),
2212
2371
  $setup.dialogVisible ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_5, [(0, vue.createElementVNode)("div", _hoisted_6, [(0, vue.createElementVNode)("div", _hoisted_7, [(0, vue.createElementVNode)("div", _hoisted_8, (0, vue.toDisplayString)($setup.dialogMessage), 1)]), (0, vue.createElementVNode)("div", { class: "opencode-dialog-actions" }, [(0, vue.createElementVNode)("button", {
2213
2372
  class: "opencode-dialog-btn cancel",
@@ -2225,7 +2384,7 @@ __vue_sfc__.render = __vue_render__;
2225
2384
  var open_code_widget_default = __vue_sfc__;
2226
2385
  //#endregion
2227
2386
  //#region es/index.js
2228
- var version = "1.0.25";
2387
+ var version = "1.0.27";
2229
2388
  function install(app, options) {
2230
2389
  [open_code_widget_default].forEach((item) => {
2231
2390
  if (item.install) app.use(item, options);