@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
@@ -1,4 +1,4 @@
1
- import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createStaticVNode, createVNode, defineComponent, inject, normalizeClass, normalizeStyle, onMounted, onUnmounted, openBlock, provide, ref, renderList, renderSlot, toDisplayString, toRef, useSlots, vShow, watch, withCtx, withDirectives, withModifiers } from "vue";
1
+ import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createStaticVNode, createVNode, defineComponent, inject, nextTick, normalizeClass, normalizeStyle, onMounted, onUnmounted, openBlock, provide, ref, renderList, renderSlot, toDisplayString, toRef, useCssVars, useSlots, vShow, watch, withCtx, withDirectives, withModifiers } from "vue";
2
2
  import { truncate } from "@vite-plugin-opencode-assistant/shared";
3
3
  import getCssSelector from "css-selector-generator";
4
4
  //#region es/open-code-widget/src/context.js
@@ -565,27 +565,12 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
565
565
  const props = __props;
566
566
  const emit = __emit;
567
567
  const rootRef = ref(null);
568
- const getInitialState = () => {
569
- if (props.offset) return {
570
- x: props.offset.x,
571
- y: props.offset.y,
572
- width: 0,
573
- height: 0
574
- };
575
- if (typeof window !== "undefined") return {
576
- x: window.innerWidth - 42 - 24,
577
- y: window.innerHeight - 42 - 24,
578
- width: 0,
579
- height: 0
580
- };
581
- return {
582
- x: 0,
583
- y: 0,
584
- width: 0,
585
- height: 0
586
- };
587
- };
588
- const state = ref(getInitialState());
568
+ const state = ref({
569
+ x: 0,
570
+ y: 0,
571
+ width: 0,
572
+ height: 0
573
+ });
589
574
  const isObject = (val) => val !== null && typeof val === "object";
590
575
  const gapX = computed(() => isObject(props.gap) ? props.gap.x : props.gap);
591
576
  const gapY = computed(() => isObject(props.gap) ? props.gap.y : props.gap);
@@ -597,20 +582,31 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
597
582
  bottom: windowHeight.value - state.value.height - gapY.value,
598
583
  left: gapX.value
599
584
  }));
585
+ const closest = (arr, target) => {
586
+ return arr.reduce((pre, cur) => Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur);
587
+ };
588
+ const applyMagnetic = () => {
589
+ if (props.magnetic === "x") {
590
+ const nextX = closest([boundary.value.left, boundary.value.right], state.value.x);
591
+ state.value.x = nextX;
592
+ }
593
+ if (props.magnetic === "y") {
594
+ const nextY = closest([boundary.value.top, boundary.value.bottom], state.value.y);
595
+ state.value.y = nextY;
596
+ }
597
+ };
600
598
  const dragging = ref(false);
601
599
  const initialized = ref(false);
602
600
  const rootStyle = computed(() => {
603
601
  const style = {};
604
602
  style.transform = `translate3d(${`${state.value.x}px`}, ${`${state.value.y}px`}, 0)`;
605
- if (dragging.value) style.transition = "none";
603
+ if (dragging.value || !initialized.value) style.transition = "none";
606
604
  else style.transition = "transform 0.3s ease";
607
605
  return style;
608
606
  });
609
- const show = ref(true);
610
607
  const updateState = () => {
611
- if (!show.value || !rootRef.value || typeof window === "undefined") return;
608
+ if (!rootRef.value || typeof window === "undefined") return;
612
609
  const rect = rootRef.value.getBoundingClientRect();
613
- if (rect.width === 0 || rect.height === 0) return;
614
610
  const { offset } = props;
615
611
  let x = offset ? offset.x : windowWidth.value - rect.width - gapX.value;
616
612
  let y = offset ? offset.y : windowHeight.value - rect.height - gapY.value;
@@ -620,12 +616,25 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
620
616
  if (x > maxX) x = maxX;
621
617
  if (y < gapY.value) y = gapY.value;
622
618
  if (y > maxY) y = maxY;
619
+ const oldX = state.value.x;
620
+ const oldY = state.value.y;
623
621
  state.value = {
624
622
  x,
625
623
  y,
626
624
  width: rect.width,
627
625
  height: rect.height
628
626
  };
627
+ if (!dragging.value) {
628
+ applyMagnetic();
629
+ if (state.value.x !== oldX || state.value.y !== oldY) {
630
+ const offset2 = {
631
+ x: state.value.x,
632
+ y: state.value.y
633
+ };
634
+ emit("update:offset", offset2);
635
+ emit("offset-change", offset2);
636
+ }
637
+ }
629
638
  };
630
639
  const touch = {
631
640
  startX: ref(0),
@@ -662,6 +671,7 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
662
671
  dragging.value = true;
663
672
  prevX = state.value.x;
664
673
  prevY = state.value.y;
674
+ document.body.classList.add("floating-bubble-dragging");
665
675
  if (!("touches" in e)) {
666
676
  window.addEventListener("mousemove", onTouchMove, { passive: false });
667
677
  window.addEventListener("mouseup", onTouchEnd);
@@ -692,24 +702,15 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
692
702
  });
693
703
  }
694
704
  };
695
- const closest = (arr, target) => {
696
- return arr.reduce((pre, cur) => Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur);
697
- };
698
705
  const onTouchEnd = (e) => {
699
706
  dragging.value = false;
707
+ document.body.classList.remove("floating-bubble-dragging");
700
708
  if (e && !("touches" in e) && e.type === "mouseup") {
701
709
  window.removeEventListener("mousemove", onTouchMove);
702
710
  window.removeEventListener("mouseup", onTouchEnd);
703
711
  }
704
712
  requestAnimationFrame(() => {
705
- if (props.magnetic === "x") {
706
- const nextX = closest([boundary.value.left, boundary.value.right], state.value.x);
707
- state.value.x = nextX;
708
- }
709
- if (props.magnetic === "y") {
710
- const nextY = closest([boundary.value.top, boundary.value.bottom], state.value.y);
711
- state.value.y = nextY;
712
- }
713
+ applyMagnetic();
713
714
  if (!touch.isTap.value) {
714
715
  emit("drag-end");
715
716
  const offset = {
@@ -732,16 +733,15 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
732
733
  }
733
734
  };
734
735
  onMounted(() => {
736
+ updateState();
735
737
  requestAnimationFrame(() => {
736
- updateState();
737
- requestAnimationFrame(() => {
738
- initialized.value = true;
739
- });
738
+ initialized.value = true;
740
739
  });
741
740
  if (typeof window !== "undefined") window.addEventListener("resize", handleResize);
742
741
  if (rootRef.value) rootRef.value.addEventListener("touchmove", onTouchMove, { passive: false });
743
742
  });
744
743
  onUnmounted(() => {
744
+ document.body.classList.remove("floating-bubble-dragging");
745
745
  if (typeof window !== "undefined") {
746
746
  window.removeEventListener("resize", handleResize);
747
747
  window.removeEventListener("mousemove", onTouchMove);
@@ -756,21 +756,14 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
756
756
  gapY,
757
757
  () => props.offset
758
758
  ], updateState, { deep: true });
759
- const isOnRightSide = computed(() => {
760
- return state.value.x > windowWidth.value / 2;
761
- });
762
- __expose({
763
- isOnRightSide,
764
- offset: computed(() => ({
765
- x: state.value.x,
766
- y: state.value.y
767
- }))
768
- });
759
+ __expose({ offset: computed(() => ({
760
+ x: state.value.x,
761
+ y: state.value.y
762
+ })) });
769
763
  const __returned__ = {
770
764
  props,
771
765
  emit,
772
766
  rootRef,
773
- getInitialState,
774
767
  state,
775
768
  isObject,
776
769
  gapX,
@@ -778,10 +771,11 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
778
771
  windowWidth,
779
772
  windowHeight,
780
773
  boundary,
774
+ closest,
775
+ applyMagnetic,
781
776
  dragging,
782
777
  initialized,
783
778
  rootStyle,
784
- show,
785
779
  updateState,
786
780
  touch,
787
781
  get prevX() {
@@ -798,11 +792,9 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
798
792
  },
799
793
  onTouchStart,
800
794
  onTouchMove,
801
- closest,
802
795
  onTouchEnd,
803
796
  onClick,
804
- handleResize,
805
- isOnRightSide
797
+ handleResize
806
798
  };
807
799
  Object.defineProperty(__returned__, "__isScriptSetup", {
808
800
  enumerable: false,
@@ -812,7 +804,7 @@ var __vue_sfc__$2 = /* @__PURE__ */ defineComponent(__spreadProps$1(__spreadValu
812
804
  }
813
805
  }));
814
806
  function __vue_render__$2(_ctx, _cache, $props, $setup, $data, $options) {
815
- return openBlock(), createBlock(Teleport, { to: $props.teleport }, [withDirectives(createElementVNode("div", {
807
+ return openBlock(), createBlock(Teleport, { to: $props.teleport }, [createElementVNode("div", {
816
808
  ref: "rootRef",
817
809
  class: "floating-bubble",
818
810
  style: normalizeStyle($setup.rootStyle),
@@ -821,60 +813,27 @@ function __vue_render__$2(_ctx, _cache, $props, $setup, $data, $options) {
821
813
  onTouchcancel: $setup.onTouchEnd,
822
814
  onMousedown: $setup.onTouchStart,
823
815
  onClickCapture: $setup.onClick
824
- }, [renderSlot(_ctx.$slots, "default")], 36), [[vShow, $setup.show && $setup.initialized]])], 8, ["to"]);
816
+ }, [renderSlot(_ctx.$slots, "default")], 36)], 8, ["to"]);
825
817
  }
826
818
  __vue_sfc__$2.render = __vue_render__$2;
827
819
  var FloatingBubble_vue_default = __vue_sfc__$2;
828
820
  //#endregion
829
821
  //#region es/open-code-widget/src/components/Trigger.vue.js
830
- var STORAGE_KEY = "opencode-bubble-offset";
831
822
  var __vue_sfc__$1 = /* @__PURE__ */ defineComponent({
832
823
  __name: "Trigger",
833
- emits: [
834
- "offset-change",
835
- "drag-start",
836
- "drag-end"
837
- ],
824
+ emits: ["drag-start", "drag-end"],
838
825
  setup(__props, { expose: __expose, emit: __emit }) {
839
- const { buttonActive: active, open, hotkeyLabel, thinking, resolvedTheme, handleToggle } = useOpenCodeWidgetContext();
840
- const loadOffset = () => {
841
- try {
842
- const saved = localStorage.getItem(STORAGE_KEY);
843
- if (saved) {
844
- const parsed = JSON.parse(saved);
845
- if (parsed && (parsed.x !== 0 || parsed.y !== 0)) return parsed;
846
- }
847
- } catch (e) {}
848
- return {
849
- x: 0,
850
- y: 0
851
- };
852
- };
853
- const offset = ref(loadOffset());
826
+ const { buttonActive: active, open, hotkeyLabel, thinking, resolvedTheme, handleToggle, bubbleOffset, handleBubbleOffsetChange } = useOpenCodeWidgetContext();
827
+ const offset = ref(bubbleOffset.value);
854
828
  const emit = __emit;
855
- const saveOffset = (value) => {
856
- try {
857
- localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
858
- } catch (e) {}
859
- };
860
829
  const handleOffsetChange = (value) => {
861
830
  offset.value = value;
862
- saveOffset(value);
863
- emit("offset-change", value);
831
+ handleBubbleOffsetChange(value);
864
832
  };
865
- const bubbleRef = ref(null);
866
- const isOnRightSide = computed(() => {
867
- if (typeof window === "undefined") return true;
868
- const centerX = window.innerWidth / 2;
869
- return offset.value.x > centerX;
870
- });
871
- onMounted(() => {
872
- if (offset.value.x !== 0 || offset.value.y !== 0) emit("offset-change", offset.value);
873
- });
874
- __expose({
875
- isOnRightSide,
876
- offset
833
+ watch(bubbleOffset, (newOffset) => {
834
+ offset.value = newOffset;
877
835
  });
836
+ __expose({ offset });
878
837
  const __returned__ = {
879
838
  active,
880
839
  open,
@@ -882,14 +841,11 @@ var __vue_sfc__$1 = /* @__PURE__ */ defineComponent({
882
841
  thinking,
883
842
  resolvedTheme,
884
843
  handleToggle,
885
- STORAGE_KEY,
886
- loadOffset,
844
+ bubbleOffset,
845
+ handleBubbleOffsetChange,
887
846
  offset,
888
847
  emit,
889
- saveOffset,
890
848
  handleOffsetChange,
891
- bubbleRef,
892
- isOnRightSide,
893
849
  FloatingBubble: FloatingBubble_vue_default
894
850
  };
895
851
  Object.defineProperty(__returned__, "__isScriptSetup", {
@@ -1392,35 +1348,16 @@ function findFileInfo(element, inspector) {
1392
1348
  };
1393
1349
  }
1394
1350
  function getPreciseElementAtPoint(x, y, boundary) {
1395
- var _a, _b;
1396
- const highlight = document.querySelector(".opencode-element-highlight");
1397
- const tooltip = document.querySelector(".opencode-element-tooltip");
1398
- const highlightDisplay = ((_a = highlight == null ? void 0 : highlight.getAttribute("style")) == null ? void 0 : _a.includes("display: block")) ? "block" : "none";
1399
- const tooltipDisplay = ((_b = tooltip == null ? void 0 : tooltip.getAttribute("style")) == null ? void 0 : _b.includes("display: block")) ? "block" : "none";
1400
- if (highlight) highlight.style.display = "none";
1401
- if (tooltip) tooltip.style.display = "none";
1402
- let element = null;
1403
- try {
1404
- const elements = document.elementsFromPoint(x, y);
1405
- for (const el of elements) {
1406
- if (el.closest("#vue-inspector-container")) continue;
1407
- if (el.closest(".opencode-widget")) continue;
1408
- if (el.hasAttribute("data-v-inspector-ignore")) continue;
1409
- if (boundary) {
1410
- if (boundary.contains(el) || el === boundary) {
1411
- element = el;
1412
- break;
1413
- }
1414
- } else {
1415
- element = el;
1416
- break;
1417
- }
1418
- }
1419
- } finally {
1420
- if (highlight) highlight.style.display = highlightDisplay;
1421
- if (tooltip) tooltip.style.display = tooltipDisplay;
1351
+ const elements = document.elementsFromPoint(x, y);
1352
+ for (const el of elements) {
1353
+ if (el.closest("#vue-inspector-container")) continue;
1354
+ if (el.closest(".opencode-widget")) continue;
1355
+ if (el.hasAttribute("data-v-inspector-ignore")) continue;
1356
+ if (boundary) {
1357
+ if (boundary.contains(el) || el === boundary) return el;
1358
+ } else return el;
1422
1359
  }
1423
- return element;
1360
+ return null;
1424
1361
  }
1425
1362
  function useInspector(options) {
1426
1363
  const highlightVisible = ref(false);
@@ -1441,74 +1378,117 @@ function useInspector(options) {
1441
1378
  });
1442
1379
  const INSPECTOR_CHECK_INTERVAL = 500;
1443
1380
  let inspectorCheckTimer = null;
1381
+ let currentHighlightElement = null;
1382
+ let currentFileInfo = {
1383
+ file: null,
1384
+ line: null,
1385
+ column: null
1386
+ };
1387
+ let currentPrimary = "#3b82f6";
1388
+ let currentPrimaryBg = "rgba(59, 130, 246, 0.1)";
1389
+ let currentDescription = "";
1390
+ let currentFileInfoText = "";
1444
1391
  function handleMouseMoveCore(e) {
1445
1392
  var _a, _b;
1446
1393
  if (!options.selectMode.value) return;
1447
1394
  const inspector = window.__VUE_INSPECTOR__;
1395
+ const highlight = document.querySelector(".opencode-element-highlight");
1396
+ const tooltip = document.querySelector(".opencode-element-tooltip");
1397
+ if (highlight) highlight.style.pointerEvents = "none";
1398
+ if (tooltip) tooltip.style.pointerEvents = "none";
1448
1399
  let elementToHighlight = null;
1400
+ let targetNode = null;
1449
1401
  let fileInfo = {
1450
1402
  file: null,
1451
1403
  line: null,
1452
1404
  column: null
1453
1405
  };
1454
- if (inspector) {
1455
- const { targetNode, params } = inspector.getTargetNode(e);
1456
- if (targetNode) {
1457
- elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode) || targetNode;
1458
- if (params && params.file) fileInfo = {
1459
- file: params.file,
1460
- line: (_a = params.line) != null ? _a : null,
1461
- column: (_b = params.column) != null ? _b : null
1462
- };
1463
- else if (elementToHighlight) fileInfo = findFileInfo(elementToHighlight, inspector);
1406
+ try {
1407
+ if (inspector) {
1408
+ const result = inspector.getTargetNode(e);
1409
+ targetNode = result.targetNode;
1410
+ const params = result.params;
1411
+ if (targetNode) {
1412
+ elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode) || targetNode;
1413
+ if (params && params.file) fileInfo = {
1414
+ file: params.file,
1415
+ line: (_a = params.line) != null ? _a : null,
1416
+ column: (_b = params.column) != null ? _b : null
1417
+ };
1418
+ else fileInfo = findFileInfo(targetNode, inspector);
1419
+ }
1464
1420
  }
1421
+ if (!elementToHighlight) elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, null);
1422
+ if (elementToHighlight && !fileInfo.file) fileInfo = getFileInfoFromVueInstance(elementToHighlight) || fileInfo;
1423
+ } finally {
1424
+ if (highlight) highlight.style.pointerEvents = "";
1425
+ if (tooltip) tooltip.style.pointerEvents = "";
1465
1426
  }
1466
- if (!elementToHighlight) elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, null);
1467
- if (elementToHighlight && !fileInfo.file) fileInfo = getFileInfoFromVueInstance(elementToHighlight) || fileInfo;
1468
1427
  if (elementToHighlight) {
1469
- const rect = elementToHighlight.getBoundingClientRect();
1470
- const widget = document.querySelector(".opencode-widget");
1471
- let primary = "#3b82f6";
1472
- let primaryBg = "rgba(59, 130, 246, 0.1)";
1473
- if (widget) {
1474
- const style = getComputedStyle(widget);
1475
- primary = style.getPropertyValue("--oc-primary").trim() || primary;
1476
- primaryBg = style.getPropertyValue("--oc-primary-bg").trim() || primaryBg;
1477
- }
1478
- highlightVisible.value = true;
1479
- highlightStyle.value = {
1480
- top: `${rect.top}px`,
1481
- left: `${rect.left}px`,
1482
- width: `${rect.width}px`,
1483
- height: `${rect.height}px`,
1484
- border: `2px solid ${primary}`,
1485
- background: primaryBg
1486
- };
1487
- const description = getElementDescription(elementToHighlight);
1488
- const fileName = fileInfo.file ? fileInfo.file.split("/").pop() : "";
1428
+ const elementChanged = currentHighlightElement !== elementToHighlight;
1429
+ if (elementChanged) {
1430
+ currentHighlightElement = elementToHighlight;
1431
+ currentFileInfo = fileInfo;
1432
+ const widget = document.querySelector(".opencode-widget");
1433
+ if (widget) {
1434
+ const style = getComputedStyle(widget);
1435
+ currentPrimary = style.getPropertyValue("--oc-primary").trim() || currentPrimary;
1436
+ currentPrimaryBg = style.getPropertyValue("--oc-primary-bg").trim() || currentPrimaryBg;
1437
+ }
1438
+ currentDescription = getElementDescription(elementToHighlight);
1439
+ } else if (!currentFileInfo.file && fileInfo.file) currentFileInfo = fileInfo;
1440
+ const fileName = currentFileInfo.file ? currentFileInfo.file.split("/").pop() : "";
1489
1441
  let lineInfo = "";
1490
- if (fileInfo.line) {
1491
- lineInfo = `:${fileInfo.line}`;
1492
- if (fileInfo.column) lineInfo += `:${fileInfo.column}`;
1442
+ if (currentFileInfo.line) {
1443
+ lineInfo = `:${currentFileInfo.line}`;
1444
+ if (currentFileInfo.column) lineInfo += `:${currentFileInfo.column}`;
1493
1445
  }
1494
- tooltipContent.value = {
1495
- description,
1496
- fileInfo: fileName ? `${fileName}${lineInfo}` : ""
1446
+ const newFileInfoText = fileName ? `${fileName}${lineInfo}` : "";
1447
+ if (elementChanged || currentFileInfoText !== newFileInfoText) {
1448
+ currentFileInfoText = newFileInfoText;
1449
+ tooltipContent.value = {
1450
+ description: currentDescription,
1451
+ fileInfo: currentFileInfoText
1452
+ };
1453
+ }
1454
+ const rect = elementToHighlight.getBoundingClientRect();
1455
+ const newTop = `${rect.top}px`;
1456
+ const newLeft = `${rect.left}px`;
1457
+ const newWidth = `${rect.width}px`;
1458
+ const newHeight = `${rect.height}px`;
1459
+ if (highlightStyle.value.top !== newTop || highlightStyle.value.left !== newLeft || highlightStyle.value.width !== newWidth || highlightStyle.value.height !== newHeight) highlightStyle.value = {
1460
+ top: newTop,
1461
+ left: newLeft,
1462
+ width: newWidth,
1463
+ height: newHeight,
1464
+ border: `2px solid ${currentPrimary}`,
1465
+ background: currentPrimaryBg
1497
1466
  };
1498
- tooltipVisible.value = true;
1499
1467
  const tooltipHeight = 50;
1500
1468
  const tooltipWidth = 200;
1501
1469
  let tooltipTop = rect.top - tooltipHeight - 8;
1502
1470
  let tooltipLeft = rect.left;
1503
1471
  if (tooltipTop < 10) tooltipTop = rect.bottom + 8;
1504
1472
  if (tooltipLeft + tooltipWidth > window.innerWidth - 10) tooltipLeft = window.innerWidth - tooltipWidth - 10;
1505
- tooltipStyle.value = {
1506
- top: `${tooltipTop}px`,
1507
- left: `${tooltipLeft}px`
1473
+ const newTooltipTop = `${tooltipTop}px`;
1474
+ const newTooltipLeft = `${tooltipLeft}px`;
1475
+ if (tooltipStyle.value.top !== newTooltipTop || tooltipStyle.value.left !== newTooltipLeft) tooltipStyle.value = {
1476
+ top: newTooltipTop,
1477
+ left: newTooltipLeft
1508
1478
  };
1479
+ if (!highlightVisible.value) highlightVisible.value = true;
1480
+ if (!tooltipVisible.value) tooltipVisible.value = true;
1509
1481
  } else {
1510
- highlightVisible.value = false;
1511
- tooltipVisible.value = false;
1482
+ currentHighlightElement = null;
1483
+ currentDescription = "";
1484
+ currentFileInfoText = "";
1485
+ currentFileInfo = {
1486
+ file: null,
1487
+ line: null,
1488
+ column: null
1489
+ };
1490
+ if (highlightVisible.value) highlightVisible.value = false;
1491
+ if (tooltipVisible.value) tooltipVisible.value = false;
1512
1492
  }
1513
1493
  }
1514
1494
  const handleMouseMove = throttle(handleMouseMoveCore, 16);
@@ -1572,6 +1552,14 @@ function useInspector(options) {
1572
1552
  if (inspector) inspector.disable();
1573
1553
  document.removeEventListener("mousemove", handleMouseMove);
1574
1554
  document.removeEventListener("keydown", handleKeydown, true);
1555
+ currentHighlightElement = null;
1556
+ currentDescription = "";
1557
+ currentFileInfoText = "";
1558
+ currentFileInfo = {
1559
+ file: null,
1560
+ line: null,
1561
+ column: null
1562
+ };
1575
1563
  highlightVisible.value = false;
1576
1564
  tooltipVisible.value = false;
1577
1565
  }
@@ -1602,6 +1590,62 @@ function useInspector(options) {
1602
1590
  };
1603
1591
  }
1604
1592
  //#endregion
1593
+ //#region es/open-code-widget/composables/use-persist-state.js
1594
+ var STORAGE_KEY = "opencode-widget-state";
1595
+ function loadState() {
1596
+ if (typeof window === "undefined") return null;
1597
+ try {
1598
+ const stored = localStorage.getItem(STORAGE_KEY);
1599
+ if (stored) return JSON.parse(stored);
1600
+ } catch (e) {
1601
+ console.warn("[OpenCodeWidget] Failed to load persisted state:", e);
1602
+ }
1603
+ return null;
1604
+ }
1605
+ function saveState(state) {
1606
+ if (typeof window === "undefined") return;
1607
+ try {
1608
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
1609
+ } catch (e) {
1610
+ console.warn("[OpenCodeWidget] Failed to save state:", e);
1611
+ }
1612
+ }
1613
+ function usePersistState(options) {
1614
+ const restoreState = () => {
1615
+ const saved = loadState();
1616
+ if (options.onRestore) options.onRestore(saved || {});
1617
+ return saved;
1618
+ };
1619
+ const getCurrentState = () => ({
1620
+ open: options.open.value,
1621
+ minimized: options.minimized.value,
1622
+ promptDockVisible: options.promptDockVisible.value,
1623
+ bubbleOffset: options.bubbleOffset.value,
1624
+ theme: options.theme.value,
1625
+ sessionListCollapsed: options.sessionListCollapsed.value
1626
+ });
1627
+ const persistState = () => {
1628
+ saveState(getCurrentState());
1629
+ };
1630
+ onMounted(() => {
1631
+ restoreState();
1632
+ watch([
1633
+ options.open,
1634
+ options.minimized,
1635
+ options.promptDockVisible,
1636
+ options.bubbleOffset,
1637
+ options.theme,
1638
+ options.sessionListCollapsed
1639
+ ], () => {
1640
+ persistState();
1641
+ }, { deep: true });
1642
+ });
1643
+ return {
1644
+ restoreState,
1645
+ persistState
1646
+ };
1647
+ }
1648
+ //#endregion
1605
1649
  //#region es/open-code-widget/src/index.vue.js
1606
1650
  var __defProp = Object.defineProperty;
1607
1651
  var __defProps = Object.defineProperties;
@@ -1760,6 +1804,10 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
1760
1804
  "thinking-change"
1761
1805
  ],
1762
1806
  setup(__props, { expose: __expose, emit: __emit }) {
1807
+ useCssVars((_ctx) => ({
1808
+ "-chatAnimationOrigin.x": chatAnimationOrigin.value.x,
1809
+ "-chatAnimationOrigin.y": chatAnimationOrigin.value.y
1810
+ }));
1763
1811
  const props = __props;
1764
1812
  const emit = __emit;
1765
1813
  const slots = useSlots();
@@ -1801,17 +1849,26 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
1801
1849
  var _a;
1802
1850
  (_a = frameRef.value) == null || _a.sendMessageToIframe(type, data);
1803
1851
  };
1852
+ const localSessionListCollapsed = ref(props.sessionListCollapsed);
1853
+ const minimized = ref(false);
1854
+ const promptDockVisible = ref(true);
1855
+ const isRestoring = ref(true);
1856
+ const iframeLoaded = ref(false);
1857
+ const syncStateToIframe = () => {
1858
+ if (!iframeLoaded.value) return;
1859
+ sendMessageToIframe("prompt-dock-visibility-change", { visible: promptDockVisible.value });
1860
+ sendMessageToIframe("minimize-state-change", { minimized: minimized.value });
1861
+ };
1804
1862
  const handleFrameLoaded = () => {
1805
1863
  emit("frame-loaded");
1864
+ iframeLoaded.value = true;
1865
+ syncStateToIframe();
1806
1866
  };
1807
1867
  __expose({
1808
1868
  showNotification,
1809
1869
  showConfirmDialog,
1810
1870
  sendMessageToIframe
1811
1871
  });
1812
- const localSessionListCollapsed = ref(props.sessionListCollapsed);
1813
- const minimized = ref(false);
1814
- const promptDockVisible = ref(true);
1815
1872
  watch(() => props.sessionListCollapsed, (val) => {
1816
1873
  localSessionListCollapsed.value = val;
1817
1874
  });
@@ -1886,6 +1943,48 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
1886
1943
  emit("toggle-select-mode", false);
1887
1944
  }
1888
1945
  });
1946
+ const bubbleOffset = ref(void 0);
1947
+ usePersistState({
1948
+ open: toRef(props, "open"),
1949
+ minimized,
1950
+ promptDockVisible,
1951
+ bubbleOffset,
1952
+ theme: toRef(props, "theme"),
1953
+ sessionListCollapsed: localSessionListCollapsed,
1954
+ onRestore: (state) => {
1955
+ if (state.open !== void 0 && state.open !== props.open) {
1956
+ emit("update:open", state.open);
1957
+ emit("toggle", state.open);
1958
+ }
1959
+ if (state.minimized !== void 0) minimized.value = state.minimized;
1960
+ if (state.bubbleOffset !== void 0) {
1961
+ const bubbleSize = 44;
1962
+ const margin = 10;
1963
+ const maxX = window.innerWidth - bubbleSize - margin;
1964
+ const maxY = window.innerHeight - bubbleSize - margin;
1965
+ bubbleOffset.value = {
1966
+ x: Math.max(margin, Math.min(state.bubbleOffset.x, maxX)),
1967
+ y: Math.max(margin, Math.min(state.bubbleOffset.y, maxY))
1968
+ };
1969
+ }
1970
+ if (state.theme !== void 0 && state.theme !== props.theme) {
1971
+ emit("update:theme", state.theme);
1972
+ emit("toggle-theme", state.theme);
1973
+ }
1974
+ if (state.sessionListCollapsed !== void 0 && state.sessionListCollapsed !== props.sessionListCollapsed) {
1975
+ localSessionListCollapsed.value = state.sessionListCollapsed;
1976
+ emit("update:sessionListCollapsed", state.sessionListCollapsed);
1977
+ }
1978
+ if (state.promptDockVisible !== void 0) promptDockVisible.value = state.promptDockVisible;
1979
+ else if (minimized.value) promptDockVisible.value = false;
1980
+ nextTick(() => {
1981
+ syncStateToIframe();
1982
+ setTimeout(() => {
1983
+ isRestoring.value = false;
1984
+ }, 50);
1985
+ });
1986
+ }
1987
+ });
1889
1988
  const handleToggleMinimize = () => {
1890
1989
  minimized.value = !minimized.value;
1891
1990
  promptDockVisible.value = !minimized.value;
@@ -1896,40 +1995,70 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
1896
1995
  promptDockVisible.value = !promptDockVisible.value;
1897
1996
  sendMessageToIframe("prompt-dock-visibility-change", { visible: promptDockVisible.value });
1898
1997
  };
1899
- const bubbleOffset = ref({
1900
- x: 0,
1901
- y: 0
1998
+ const windowWidth = ref(typeof window !== "undefined" ? window.innerWidth : 0);
1999
+ const windowHeight = ref(typeof window !== "undefined" ? window.innerHeight : 0);
2000
+ const handleWindowResize = () => {
2001
+ if (typeof window !== "undefined") {
2002
+ windowWidth.value = window.innerWidth;
2003
+ windowHeight.value = window.innerHeight;
2004
+ }
2005
+ };
2006
+ onMounted(() => {
2007
+ if (typeof window !== "undefined") window.addEventListener("resize", handleWindowResize);
2008
+ });
2009
+ onUnmounted(() => {
2010
+ if (typeof window !== "undefined") window.removeEventListener("resize", handleWindowResize);
2011
+ });
2012
+ const bubbleQuadrant = computed(() => {
2013
+ var _a, _b, _c, _d;
2014
+ if (typeof window === "undefined") return "bottom-right";
2015
+ const centerX = windowWidth.value / 2;
2016
+ const centerY = windowHeight.value / 2;
2017
+ const bubbleSize = 44;
2018
+ const currentOffset = (_b = (_a = triggerRef.value) == null ? void 0 : _a.offset) != null ? _b : bubbleOffset.value;
2019
+ const effectiveX = ((_c = currentOffset == null ? void 0 : currentOffset.x) != null ? _c : windowWidth.value - bubbleSize - 24) + bubbleSize / 2;
2020
+ const effectiveY = ((_d = currentOffset == null ? void 0 : currentOffset.y) != null ? _d : windowHeight.value - bubbleSize - 24) + bubbleSize / 2;
2021
+ if (effectiveX >= centerX && effectiveY >= centerY) return "bottom-right";
2022
+ else if (effectiveX < centerX && effectiveY >= centerY) return "bottom-left";
2023
+ else if (effectiveX >= centerX && effectiveY < centerY) return "top-right";
2024
+ else return "top-left";
1902
2025
  });
1903
2026
  const isBubbleOnRightSide = computed(() => {
1904
- if (typeof window === "undefined") return true;
1905
- const centerX = window.innerWidth / 2;
1906
- return bubbleOffset.value.x > centerX;
2027
+ const quadrant = bubbleQuadrant.value;
2028
+ return quadrant === "top-right" || quadrant === "bottom-right";
1907
2029
  });
1908
2030
  const chatPositionStyle = computed(() => {
2031
+ var _a, _b, _c;
1909
2032
  if (typeof window === "undefined") return {};
1910
- const windowWidth = window.innerWidth;
1911
- const windowHeight = window.innerHeight;
1912
2033
  const chatWidth = minimized.value ? 300 : 700;
1913
- const chatHeight = minimized.value ? 300 : Math.min(windowHeight * .86, windowHeight - 40);
2034
+ const chatHeight = minimized.value ? 300 : Math.min(windowHeight.value * .86, windowHeight.value - 40);
1914
2035
  const gap = 24;
1915
2036
  const bubbleSize = 44;
1916
2037
  const screenMargin = 20;
2038
+ const effectiveOffset = (_c = (_b = (_a = triggerRef.value) == null ? void 0 : _a.offset) != null ? _b : bubbleOffset.value) != null ? _c : {
2039
+ x: windowWidth.value - bubbleSize - gap,
2040
+ y: windowHeight.value - bubbleSize - gap
2041
+ };
1917
2042
  const style = {};
1918
2043
  if (isBubbleOnRightSide.value) {
1919
- let rightPos = windowWidth - bubbleOffset.value.x + gap;
1920
- const maxRight = windowWidth - chatWidth - screenMargin;
2044
+ let rightPos = windowWidth.value - effectiveOffset.x + gap;
2045
+ const minRight = screenMargin;
2046
+ const maxRight = windowWidth.value - chatWidth - screenMargin;
1921
2047
  if (rightPos > maxRight) rightPos = maxRight;
2048
+ if (rightPos < minRight) rightPos = minRight;
1922
2049
  style.right = `${rightPos}px`;
1923
2050
  style.left = "auto";
1924
2051
  } else {
1925
- let leftPos = bubbleOffset.value.x + bubbleSize + gap;
1926
- const maxLeft = windowWidth - chatWidth - screenMargin;
2052
+ let leftPos = effectiveOffset.x + bubbleSize + gap;
2053
+ const minLeft = screenMargin;
2054
+ const maxLeft = windowWidth.value - chatWidth - screenMargin;
1927
2055
  if (leftPos > maxLeft) leftPos = maxLeft;
2056
+ if (leftPos < minLeft) leftPos = minLeft;
1928
2057
  style.left = `${leftPos}px`;
1929
2058
  style.right = "auto";
1930
2059
  }
1931
- let bottomPos = windowHeight - bubbleOffset.value.y - bubbleSize;
1932
- const maxBottom = windowHeight - chatHeight - screenMargin;
2060
+ let bottomPos = windowHeight.value - effectiveOffset.y - bubbleSize;
2061
+ const maxBottom = windowHeight.value - chatHeight - screenMargin;
1933
2062
  if (bottomPos > maxBottom) bottomPos = maxBottom;
1934
2063
  if (bottomPos < screenMargin) bottomPos = screenMargin;
1935
2064
  style.bottom = `${bottomPos}px`;
@@ -1938,6 +2067,26 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
1938
2067
  const handleBubbleOffsetChange = (offset) => {
1939
2068
  bubbleOffset.value = offset;
1940
2069
  };
2070
+ const chatAnimationOrigin = computed(() => {
2071
+ switch (bubbleQuadrant.value) {
2072
+ case "top-left": return {
2073
+ x: "-20px",
2074
+ y: "-20px"
2075
+ };
2076
+ case "top-right": return {
2077
+ x: "20px",
2078
+ y: "-20px"
2079
+ };
2080
+ case "bottom-left": return {
2081
+ x: "-20px",
2082
+ y: "20px"
2083
+ };
2084
+ default: return {
2085
+ x: "20px",
2086
+ y: "20px"
2087
+ };
2088
+ }
2089
+ });
1941
2090
  const isDragging = ref(false);
1942
2091
  let wasOpenBeforeDrag = false;
1943
2092
  const handleDragStart = () => {
@@ -1971,6 +2120,7 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
1971
2120
  thinking: toRef(props, "thinking"),
1972
2121
  minimized,
1973
2122
  promptDockVisible,
2123
+ bubbleOffset,
1974
2124
  iframeSource,
1975
2125
  buttonActive,
1976
2126
  sessionListTitle,
@@ -1992,7 +2142,8 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
1992
2142
  handleClickSelectedNode,
1993
2143
  handleRemoveSelectedNode: (payload) => handleRemoveSelectedNode(payload.item, payload.index, payload.source),
1994
2144
  handleClearSelectedNodes,
1995
- handleFrameLoaded
2145
+ handleFrameLoaded,
2146
+ handleBubbleOffsetChange
1996
2147
  });
1997
2148
  const __returned__ = {
1998
2149
  props,
@@ -2022,10 +2173,13 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
2022
2173
  frameRef,
2023
2174
  triggerRef,
2024
2175
  sendMessageToIframe,
2025
- handleFrameLoaded,
2026
2176
  localSessionListCollapsed,
2027
2177
  minimized,
2028
2178
  promptDockVisible,
2179
+ isRestoring,
2180
+ iframeLoaded,
2181
+ syncStateToIframe,
2182
+ handleFrameLoaded,
2029
2183
  buttonActive,
2030
2184
  containerClasses,
2031
2185
  iframeSource,
@@ -2052,12 +2206,17 @@ var __vue_sfc__ = /* @__PURE__ */ defineComponent(__spreadProps(__spreadValues({
2052
2206
  tooltipVisible,
2053
2207
  tooltipStyle,
2054
2208
  tooltipContent,
2209
+ bubbleOffset,
2055
2210
  handleToggleMinimize,
2056
2211
  handleTogglePromptDock,
2057
- bubbleOffset,
2212
+ windowWidth,
2213
+ windowHeight,
2214
+ handleWindowResize,
2215
+ bubbleQuadrant,
2058
2216
  isBubbleOnRightSide,
2059
2217
  chatPositionStyle,
2060
2218
  handleBubbleOffsetChange,
2219
+ chatAnimationOrigin,
2061
2220
  isDragging,
2062
2221
  get wasOpenBeforeDrag() {
2063
2222
  return wasOpenBeforeDrag;
@@ -2109,7 +2268,6 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
2109
2268
  return openBlock(), createElementBlock("div", { class: normalizeClass($setup.containerClasses) }, [
2110
2269
  createVNode($setup["Trigger"], {
2111
2270
  ref: "triggerRef",
2112
- onOffsetChange: $setup.handleBubbleOffsetChange,
2113
2271
  onDragStart: $setup.handleDragStart,
2114
2272
  onDragEnd: $setup.handleDragEnd
2115
2273
  }, createSlots({ _: 2 }, [$setup.slots["button-icon"] ? {
@@ -2121,7 +2279,8 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
2121
2279
  class: normalizeClass(["opencode-chat", {
2122
2280
  open: $props.open,
2123
2281
  minimized: $setup.minimized,
2124
- dragging: $setup.isDragging
2282
+ dragging: $setup.isDragging,
2283
+ "no-transition": $setup.isRestoring
2125
2284
  }]),
2126
2285
  style: normalizeStyle($setup.chatPositionStyle)
2127
2286
  }, [
@@ -2176,11 +2335,11 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
2176
2335
  createVNode($setup["SelectHint"]),
2177
2336
  withDirectives(createElementVNode("div", {
2178
2337
  class: "opencode-element-highlight",
2179
- style: normalizeStyle(__spreadValues({ display: $setup.highlightVisible ? "block" : "none" }, $setup.highlightStyle))
2338
+ style: normalizeStyle($setup.highlightStyle)
2180
2339
  }, null, 4), [[vShow, $setup.highlightVisible]]),
2181
2340
  withDirectives(createElementVNode("div", {
2182
2341
  class: "opencode-element-tooltip",
2183
- style: normalizeStyle(__spreadValues({ display: $setup.tooltipVisible ? "block" : "none" }, $setup.tooltipStyle))
2342
+ style: normalizeStyle($setup.tooltipStyle)
2184
2343
  }, [createElementVNode("div", _hoisted_3, toDisplayString($setup.tooltipContent.description), 1), createElementVNode("div", _hoisted_4, toDisplayString($setup.tooltipContent.fileInfo), 1)], 4), [[vShow, $setup.tooltipVisible]]),
2185
2344
  $setup.dialogVisible ? (openBlock(), createElementBlock("div", _hoisted_5, [createElementVNode("div", _hoisted_6, [createElementVNode("div", _hoisted_7, [createElementVNode("div", _hoisted_8, toDisplayString($setup.dialogMessage), 1)]), createElementVNode("div", { class: "opencode-dialog-actions" }, [createElementVNode("button", {
2186
2345
  class: "opencode-dialog-btn cancel",
@@ -2198,7 +2357,7 @@ __vue_sfc__.render = __vue_render__;
2198
2357
  var open_code_widget_default = __vue_sfc__;
2199
2358
  //#endregion
2200
2359
  //#region es/index.js
2201
- var version = "1.0.25";
2360
+ var version = "1.0.27";
2202
2361
  function install(app, options) {
2203
2362
  [open_code_widget_default].forEach((item) => {
2204
2363
  if (item.install) app.use(item, options);