@david-xpn/llm-ui-feedback 0.1.7 → 0.1.8
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 +129 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +143 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -66,31 +66,59 @@ function FloatingButton({ onPickClick, onPanelToggle, draftCount, panelOpen, pos
|
|
|
66
66
|
gap: 8
|
|
67
67
|
};
|
|
68
68
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: anchor, children: [
|
|
69
|
-
draftCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
69
|
+
draftCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
70
70
|
"button",
|
|
71
71
|
{
|
|
72
72
|
onClick: onPanelToggle,
|
|
73
73
|
"aria-label": panelOpen ? "Close drafts panel" : `Open drafts panel (${draftCount})`,
|
|
74
74
|
style: {
|
|
75
|
-
width:
|
|
76
|
-
height:
|
|
75
|
+
width: 36,
|
|
76
|
+
height: 36,
|
|
77
77
|
borderRadius: "50%",
|
|
78
78
|
border: "none",
|
|
79
79
|
background: panelOpen ? "#ef4444" : buttonColor,
|
|
80
80
|
color: panelOpen ? "#fff" : getContrastColor(buttonColor),
|
|
81
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
82
|
-
fontSize: 13,
|
|
83
|
-
fontWeight: 700,
|
|
84
|
-
lineHeight: 1,
|
|
85
81
|
cursor: "pointer",
|
|
86
82
|
display: "flex",
|
|
87
83
|
alignItems: "center",
|
|
88
84
|
justifyContent: "center",
|
|
89
85
|
boxShadow: "0 2px 8px rgba(0,0,0,0.25)",
|
|
90
86
|
transition: "background 0.15s",
|
|
91
|
-
padding: 0
|
|
87
|
+
padding: 0,
|
|
88
|
+
position: "relative"
|
|
92
89
|
},
|
|
93
|
-
children:
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
92
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "3", width: "2", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
93
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "6", y: "3", width: "10", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "8", width: "2", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
95
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "6", y: "8", width: "10", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
96
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "13", width: "2", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
97
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "6", y: "13", width: "10", height: "2", rx: "0.5", fill: "currentColor" })
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
100
|
+
"span",
|
|
101
|
+
{
|
|
102
|
+
style: {
|
|
103
|
+
position: "absolute",
|
|
104
|
+
top: -4,
|
|
105
|
+
right: -4,
|
|
106
|
+
minWidth: 16,
|
|
107
|
+
height: 16,
|
|
108
|
+
borderRadius: 8,
|
|
109
|
+
background: "#ef4444",
|
|
110
|
+
color: "#fff",
|
|
111
|
+
fontSize: 10,
|
|
112
|
+
fontWeight: 700,
|
|
113
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
114
|
+
lineHeight: "16px",
|
|
115
|
+
textAlign: "center",
|
|
116
|
+
padding: "0 3px"
|
|
117
|
+
},
|
|
118
|
+
children: draftCount
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
]
|
|
94
122
|
}
|
|
95
123
|
),
|
|
96
124
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -668,6 +696,20 @@ function SidePanel({
|
|
|
668
696
|
|
|
669
697
|
// src/components/DraftModal.tsx
|
|
670
698
|
var import_react4 = require("react");
|
|
699
|
+
|
|
700
|
+
// src/utils/blob.ts
|
|
701
|
+
function dataUrlToBlob(dataUrl) {
|
|
702
|
+
const [header, base64] = dataUrl.split(",");
|
|
703
|
+
const mime = header.match(/:(.*?);/)?.[1] || "image/png";
|
|
704
|
+
const binary = atob(base64);
|
|
705
|
+
const bytes = new Uint8Array(binary.length);
|
|
706
|
+
for (let i = 0; i < binary.length; i++) {
|
|
707
|
+
bytes[i] = binary.charCodeAt(i);
|
|
708
|
+
}
|
|
709
|
+
return new Blob([bytes], { type: mime });
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/components/DraftModal.tsx
|
|
671
713
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
672
714
|
var WIDGET_CONTAINER_ID2 = "llm-ui-feedback-root";
|
|
673
715
|
function DraftModal({ pendingContext, addingDraft, onAdd, onCancel }) {
|
|
@@ -694,6 +736,46 @@ function DraftModal({ pendingContext, addingDraft, onAdd, onCancel }) {
|
|
|
694
736
|
}
|
|
695
737
|
};
|
|
696
738
|
const { context, screenshot } = pendingContext;
|
|
739
|
+
const [copiedText, setCopiedText] = (0, import_react4.useState)(false);
|
|
740
|
+
const [copiedImage, setCopiedImage] = (0, import_react4.useState)(false);
|
|
741
|
+
const handleCopyText = (0, import_react4.useCallback)(async () => {
|
|
742
|
+
const lines = [
|
|
743
|
+
`Component: ${context.componentPath}`,
|
|
744
|
+
`Path: ${context.urlPath}`,
|
|
745
|
+
...context.elementText ? [`Element: "${context.elementText}"`] : [],
|
|
746
|
+
...comment.trim() ? [`
|
|
747
|
+
Comment: ${comment.trim()}`] : []
|
|
748
|
+
];
|
|
749
|
+
try {
|
|
750
|
+
await navigator.clipboard.writeText(lines.join("\n"));
|
|
751
|
+
setCopiedText(true);
|
|
752
|
+
setTimeout(() => setCopiedText(false), 2e3);
|
|
753
|
+
} catch {
|
|
754
|
+
}
|
|
755
|
+
}, [context, comment]);
|
|
756
|
+
const handleCopyImage = (0, import_react4.useCallback)(async () => {
|
|
757
|
+
if (!screenshot) return;
|
|
758
|
+
try {
|
|
759
|
+
const blob = dataUrlToBlob(screenshot);
|
|
760
|
+
const pngBlob = blob.type === "image/png" ? blob : await new Promise((resolve) => {
|
|
761
|
+
const img = new Image();
|
|
762
|
+
img.onload = () => {
|
|
763
|
+
const canvas = document.createElement("canvas");
|
|
764
|
+
canvas.width = img.naturalWidth;
|
|
765
|
+
canvas.height = img.naturalHeight;
|
|
766
|
+
canvas.getContext("2d").drawImage(img, 0, 0);
|
|
767
|
+
canvas.toBlob((b) => resolve(b), "image/png");
|
|
768
|
+
};
|
|
769
|
+
img.src = screenshot;
|
|
770
|
+
});
|
|
771
|
+
await navigator.clipboard.write([
|
|
772
|
+
new ClipboardItem({ "image/png": pngBlob })
|
|
773
|
+
]);
|
|
774
|
+
setCopiedImage(true);
|
|
775
|
+
setTimeout(() => setCopiedImage(false), 2e3);
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
778
|
+
}, [screenshot]);
|
|
697
779
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
698
780
|
"div",
|
|
699
781
|
{
|
|
@@ -758,6 +840,44 @@ function DraftModal({ pendingContext, addingDraft, onAdd, onCancel }) {
|
|
|
758
840
|
}
|
|
759
841
|
}
|
|
760
842
|
),
|
|
843
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 8, marginBottom: 12 }, children: [
|
|
844
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
845
|
+
"button",
|
|
846
|
+
{
|
|
847
|
+
onClick: handleCopyText,
|
|
848
|
+
style: {
|
|
849
|
+
padding: "5px 12px",
|
|
850
|
+
borderRadius: 6,
|
|
851
|
+
border: "1px solid #d1d5db",
|
|
852
|
+
background: copiedText ? "#22c55e" : "#f3f4f6",
|
|
853
|
+
color: copiedText ? "#fff" : "#374151",
|
|
854
|
+
fontSize: 12,
|
|
855
|
+
fontWeight: 500,
|
|
856
|
+
cursor: "pointer",
|
|
857
|
+
transition: "background 0.15s, color 0.15s"
|
|
858
|
+
},
|
|
859
|
+
children: copiedText ? "Copied!" : "Copy Text"
|
|
860
|
+
}
|
|
861
|
+
),
|
|
862
|
+
screenshot && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
863
|
+
"button",
|
|
864
|
+
{
|
|
865
|
+
onClick: handleCopyImage,
|
|
866
|
+
style: {
|
|
867
|
+
padding: "5px 12px",
|
|
868
|
+
borderRadius: 6,
|
|
869
|
+
border: "1px solid #d1d5db",
|
|
870
|
+
background: copiedImage ? "#22c55e" : "#f3f4f6",
|
|
871
|
+
color: copiedImage ? "#fff" : "#374151",
|
|
872
|
+
fontSize: 12,
|
|
873
|
+
fontWeight: 500,
|
|
874
|
+
cursor: "pointer",
|
|
875
|
+
transition: "background 0.15s, color 0.15s"
|
|
876
|
+
},
|
|
877
|
+
children: copiedImage ? "Copied!" : "Copy Image"
|
|
878
|
+
}
|
|
879
|
+
)
|
|
880
|
+
] }),
|
|
761
881
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
762
882
|
"textarea",
|
|
763
883
|
{
|
|
@@ -1093,18 +1213,6 @@ ${propsStr}`);
|
|
|
1093
1213
|
return lines.join("\n");
|
|
1094
1214
|
}
|
|
1095
1215
|
|
|
1096
|
-
// src/utils/blob.ts
|
|
1097
|
-
function dataUrlToBlob(dataUrl) {
|
|
1098
|
-
const [header, base64] = dataUrl.split(",");
|
|
1099
|
-
const mime = header.match(/:(.*?);/)?.[1] || "image/png";
|
|
1100
|
-
const binary = atob(base64);
|
|
1101
|
-
const bytes = new Uint8Array(binary.length);
|
|
1102
|
-
for (let i = 0; i < binary.length; i++) {
|
|
1103
|
-
bytes[i] = binary.charCodeAt(i);
|
|
1104
|
-
}
|
|
1105
|
-
return new Blob([bytes], { type: mime });
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
1216
|
// src/hooks/useSession.ts
|
|
1109
1217
|
var import_react6 = require("react");
|
|
1110
1218
|
var SESSION_TOKEN_KEY = "llm_feedback_session_token";
|
|
@@ -1402,7 +1510,6 @@ function FeedbackWidget({
|
|
|
1402
1510
|
(0, import_react7.useEffect)(() => {
|
|
1403
1511
|
if (session.status === "authenticated" && pendingOpenRef.current) {
|
|
1404
1512
|
pendingOpenRef.current = false;
|
|
1405
|
-
dispatch({ type: "OPEN_PANEL" });
|
|
1406
1513
|
}
|
|
1407
1514
|
}, [session.status]);
|
|
1408
1515
|
(0, import_react7.useEffect)(() => {
|