@taterboom/shiteki 0.1.0 → 0.1.2
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/README.md +102 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +375 -148
- package/dist/index.mjs +357 -130
- package/dist/standalone.global.js +9 -9
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/ShitekiWidget.tsx
|
|
2
|
-
import { useCallback as useCallback9, useEffect as
|
|
2
|
+
import { useCallback as useCallback9, useEffect as useEffect8, useState as useState12 } from "react";
|
|
3
3
|
import ReactDOM from "react-dom";
|
|
4
4
|
import { AnimatePresence as AnimatePresence3 } from "motion/react";
|
|
5
5
|
|
|
@@ -44,6 +44,13 @@ function useAnnotations() {
|
|
|
44
44
|
});
|
|
45
45
|
return annotation;
|
|
46
46
|
}, []);
|
|
47
|
+
const update = useCallback((id, comment) => {
|
|
48
|
+
setAnnotations((prev) => {
|
|
49
|
+
const next = prev.map((a) => a.id === id ? { ...a, comment } : a);
|
|
50
|
+
writeStored(next, nextIdRef.current);
|
|
51
|
+
return next;
|
|
52
|
+
});
|
|
53
|
+
}, []);
|
|
47
54
|
const remove = useCallback((id) => {
|
|
48
55
|
setAnnotations((prev) => {
|
|
49
56
|
const next = prev.filter((a) => a.id !== id);
|
|
@@ -56,7 +63,7 @@ function useAnnotations() {
|
|
|
56
63
|
nextIdRef.current = 1;
|
|
57
64
|
removeStored();
|
|
58
65
|
}, []);
|
|
59
|
-
return { annotations, add, remove, clear };
|
|
66
|
+
return { annotations, add, update, remove, clear };
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
// src/hooks/useConfig.ts
|
|
@@ -78,7 +85,8 @@ function merge(defaults, stored) {
|
|
|
78
85
|
githubToken: stored.githubToken || defaults.githubToken,
|
|
79
86
|
owner: stored.owner || defaults.owner,
|
|
80
87
|
repo: stored.repo || defaults.repo,
|
|
81
|
-
labels: stored.labels ?? defaults.labels
|
|
88
|
+
labels: stored.labels ?? defaults.labels,
|
|
89
|
+
clearAfterCopy: stored.clearAfterCopy ?? defaults.clearAfterCopy
|
|
82
90
|
};
|
|
83
91
|
}
|
|
84
92
|
function useConfig(defaults) {
|
|
@@ -340,7 +348,7 @@ function useKeyboardShortcuts(opts) {
|
|
|
340
348
|
if (key === "c" && opts.annotationCount > 0) {
|
|
341
349
|
e.preventDefault();
|
|
342
350
|
opts.onCopy();
|
|
343
|
-
} else if (key === "s" && opts.annotationCount > 0) {
|
|
351
|
+
} else if (key === "s" && opts.annotationCount > 0 && opts.canSend) {
|
|
344
352
|
e.preventDefault();
|
|
345
353
|
opts.onSend();
|
|
346
354
|
} else if (key === "d" && opts.annotationCount > 0) {
|
|
@@ -360,6 +368,7 @@ function useKeyboardShortcuts(opts) {
|
|
|
360
368
|
opts.open,
|
|
361
369
|
opts.mode,
|
|
362
370
|
opts.annotationCount,
|
|
371
|
+
opts.canSend,
|
|
363
372
|
opts.settingsOpen,
|
|
364
373
|
opts.sendDialogOpen,
|
|
365
374
|
opts.onCopy,
|
|
@@ -406,13 +415,13 @@ function generatePrompt(annotations) {
|
|
|
406
415
|
`**Annotations:** ${annotations.length}`,
|
|
407
416
|
`**Captured:** ${now}`
|
|
408
417
|
];
|
|
409
|
-
|
|
418
|
+
annotations.forEach((ann, i) => {
|
|
410
419
|
const { elementInfo: el } = ann;
|
|
411
420
|
lines.push(
|
|
412
421
|
"",
|
|
413
422
|
"---",
|
|
414
423
|
"",
|
|
415
|
-
`## Annotation #${
|
|
424
|
+
`## Annotation #${i + 1}`,
|
|
416
425
|
"",
|
|
417
426
|
"**What should change:**",
|
|
418
427
|
`> ${ann.comment}`,
|
|
@@ -426,7 +435,7 @@ function generatePrompt(annotations) {
|
|
|
426
435
|
if (attrEntries.length > 0) {
|
|
427
436
|
lines.push(`- Attributes: ${attrEntries.map(([k, v]) => `\`${k}="${v}"\``).join(", ")}`);
|
|
428
437
|
}
|
|
429
|
-
}
|
|
438
|
+
});
|
|
430
439
|
lines.push("");
|
|
431
440
|
return lines.join("\n");
|
|
432
441
|
}
|
|
@@ -444,6 +453,7 @@ function Toolbar({
|
|
|
444
453
|
annotationCount,
|
|
445
454
|
copied,
|
|
446
455
|
sending,
|
|
456
|
+
canSend,
|
|
447
457
|
settingsOpen,
|
|
448
458
|
onOpen,
|
|
449
459
|
onCopy,
|
|
@@ -514,25 +524,25 @@ function Toolbar({
|
|
|
514
524
|
transition: spring,
|
|
515
525
|
style: { overflow: "hidden", display: "flex", alignItems: "center", gap: 4 },
|
|
516
526
|
children: [
|
|
517
|
-
|
|
518
|
-
|
|
527
|
+
/* @__PURE__ */ jsx("div", { className: "shiteki-toolbar-sep" }),
|
|
528
|
+
/* @__PURE__ */ jsx("button", { className: "shiteki-toolbar-btn", onClick: onCopy, disabled: annotationCount === 0, title: "Copy to clipboard", children: copied ? /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
519
529
|
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
520
530
|
/* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
521
531
|
] }) }),
|
|
522
|
-
|
|
532
|
+
/* @__PURE__ */ jsx(
|
|
523
533
|
"button",
|
|
524
534
|
{
|
|
525
535
|
className: "shiteki-toolbar-btn",
|
|
526
|
-
onClick: onSend,
|
|
527
|
-
disabled: sending,
|
|
528
|
-
title: "Send as GitHub Issue",
|
|
536
|
+
onClick: canSend ? onSend : onSettings,
|
|
537
|
+
disabled: sending || annotationCount === 0,
|
|
538
|
+
title: canSend ? "Send as GitHub Issue" : "Configure settings to enable send",
|
|
529
539
|
children: /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
530
540
|
/* @__PURE__ */ jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
531
541
|
/* @__PURE__ */ jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
532
542
|
] })
|
|
533
543
|
}
|
|
534
544
|
),
|
|
535
|
-
|
|
545
|
+
/* @__PURE__ */ jsx("button", { className: "shiteki-toolbar-btn", onClick: onClear, disabled: annotationCount === 0, title: "Clear all annotations", children: /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
536
546
|
/* @__PURE__ */ jsx("polyline", { points: "3 6 5 6 21 6" }),
|
|
537
547
|
/* @__PURE__ */ jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
538
548
|
] }) }),
|
|
@@ -566,20 +576,35 @@ function Toolbar({
|
|
|
566
576
|
exit: { opacity: 0, y: 4 },
|
|
567
577
|
transition: { duration: 0.15 },
|
|
568
578
|
children: [
|
|
569
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
570
|
-
/* @__PURE__ */ jsx("kbd", { children: "X" }),
|
|
571
|
-
" Toggle",
|
|
572
|
-
/* @__PURE__ */ jsx("kbd", { children: "C" }),
|
|
573
|
-
" Copy",
|
|
574
|
-
/* @__PURE__ */ jsx("kbd", { children: "S" }),
|
|
575
|
-
" Send",
|
|
576
|
-
/* @__PURE__ */ jsx("kbd", { children: "DD" }),
|
|
577
|
-
" Clear"
|
|
578
|
-
] }),
|
|
579
579
|
/* @__PURE__ */ jsx("button", { className: "shiteki-shortcut-hint-close", onClick: onDismissHint, "aria-label": "Dismiss", children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
580
580
|
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
581
581
|
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
582
|
-
] }) })
|
|
582
|
+
] }) }),
|
|
583
|
+
/* @__PURE__ */ jsxs("div", { className: "shiteki-shortcut-hint-list", children: [
|
|
584
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
585
|
+
/* @__PURE__ */ jsx("kbd", { children: "X" }),
|
|
586
|
+
" Toggle"
|
|
587
|
+
] }),
|
|
588
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
589
|
+
/* @__PURE__ */ jsx("kbd", { children: "C" }),
|
|
590
|
+
" Copy"
|
|
591
|
+
] }),
|
|
592
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
593
|
+
/* @__PURE__ */ jsx("kbd", { children: "S" }),
|
|
594
|
+
" Send"
|
|
595
|
+
] }),
|
|
596
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
597
|
+
/* @__PURE__ */ jsx("kbd", { children: "DD" }),
|
|
598
|
+
" Clear"
|
|
599
|
+
] }),
|
|
600
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
601
|
+
/* @__PURE__ */ jsxs("kbd", { children: [
|
|
602
|
+
navigator.userAgent.includes("Mac") ? "\u2318" : "Ctrl",
|
|
603
|
+
"+Click"
|
|
604
|
+
] }),
|
|
605
|
+
" Remove"
|
|
606
|
+
] })
|
|
607
|
+
] })
|
|
583
608
|
]
|
|
584
609
|
},
|
|
585
610
|
"hint"
|
|
@@ -699,23 +724,123 @@ function AnnotationPopover({ elementInfo, onAdd, onCancel }) {
|
|
|
699
724
|
);
|
|
700
725
|
}
|
|
701
726
|
|
|
727
|
+
// src/components/AnnotationDetailPopover.tsx
|
|
728
|
+
import { useEffect as useEffect4, useRef as useRef6, useState as useState8 } from "react";
|
|
729
|
+
import { motion as motion3 } from "motion/react";
|
|
730
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
731
|
+
function AnnotationDetailPopover({ annotation, index, onUpdate, onRemove, onClose }) {
|
|
732
|
+
const [comment, setComment] = useState8(annotation.comment);
|
|
733
|
+
const textareaRef = useRef6(null);
|
|
734
|
+
const [dragOffset, setDragOffset] = useState8({ x: 0, y: 0 });
|
|
735
|
+
const dragRef = useRef6(null);
|
|
736
|
+
useEffect4(() => {
|
|
737
|
+
textareaRef.current?.focus();
|
|
738
|
+
}, []);
|
|
739
|
+
const { rect } = annotation.elementInfo;
|
|
740
|
+
const spaceBelow = window.innerHeight - (rect.top + rect.height);
|
|
741
|
+
const above = spaceBelow < 250;
|
|
742
|
+
const top = above ? rect.top - 180 : rect.top + rect.height + 8;
|
|
743
|
+
let left = rect.left + rect.width / 2 - 160;
|
|
744
|
+
left = Math.max(12, Math.min(left, window.innerWidth - 332));
|
|
745
|
+
const yOffset = above ? -8 : 8;
|
|
746
|
+
const handlePointerDown = (e) => {
|
|
747
|
+
dragRef.current = { startX: e.clientX, startY: e.clientY, origX: dragOffset.x, origY: dragOffset.y };
|
|
748
|
+
e.target.setPointerCapture(e.pointerId);
|
|
749
|
+
};
|
|
750
|
+
const handlePointerMove = (e) => {
|
|
751
|
+
if (!dragRef.current) return;
|
|
752
|
+
setDragOffset({
|
|
753
|
+
x: dragRef.current.origX + e.clientX - dragRef.current.startX,
|
|
754
|
+
y: dragRef.current.origY + e.clientY - dragRef.current.startY
|
|
755
|
+
});
|
|
756
|
+
};
|
|
757
|
+
const handlePointerUp = () => {
|
|
758
|
+
dragRef.current = null;
|
|
759
|
+
};
|
|
760
|
+
const handleSubmit = (e) => {
|
|
761
|
+
e.preventDefault();
|
|
762
|
+
if (!comment.trim()) return;
|
|
763
|
+
onUpdate(comment.trim());
|
|
764
|
+
};
|
|
765
|
+
return /* @__PURE__ */ jsxs3(
|
|
766
|
+
motion3.div,
|
|
767
|
+
{
|
|
768
|
+
className: "shiteki-popover",
|
|
769
|
+
style: { top: top + dragOffset.y, left: left + dragOffset.x, transformOrigin: above ? "bottom center" : "top center" },
|
|
770
|
+
initial: { opacity: 0, scale: 0.95, y: yOffset },
|
|
771
|
+
animate: { opacity: 1, scale: 1, y: 0 },
|
|
772
|
+
exit: { opacity: 0, scale: 0.95, y: yOffset },
|
|
773
|
+
transition: spring,
|
|
774
|
+
children: [
|
|
775
|
+
/* @__PURE__ */ jsxs3(
|
|
776
|
+
"div",
|
|
777
|
+
{
|
|
778
|
+
className: "shiteki-popover-info",
|
|
779
|
+
style: { cursor: "grab" },
|
|
780
|
+
onPointerDown: handlePointerDown,
|
|
781
|
+
onPointerMove: handlePointerMove,
|
|
782
|
+
onPointerUp: handlePointerUp,
|
|
783
|
+
children: [
|
|
784
|
+
/* @__PURE__ */ jsxs3("span", { className: "shiteki-popover-tag", children: [
|
|
785
|
+
"#",
|
|
786
|
+
index,
|
|
787
|
+
" <",
|
|
788
|
+
annotation.elementInfo.tagName,
|
|
789
|
+
">"
|
|
790
|
+
] }),
|
|
791
|
+
annotation.elementInfo.textContent && /* @__PURE__ */ jsxs3("span", { className: "shiteki-popover-text", children: [
|
|
792
|
+
'"',
|
|
793
|
+
annotation.elementInfo.textContent,
|
|
794
|
+
'"'
|
|
795
|
+
] })
|
|
796
|
+
]
|
|
797
|
+
}
|
|
798
|
+
),
|
|
799
|
+
/* @__PURE__ */ jsxs3("form", { className: "shiteki-popover-form", onSubmit: handleSubmit, children: [
|
|
800
|
+
/* @__PURE__ */ jsx4(
|
|
801
|
+
"textarea",
|
|
802
|
+
{
|
|
803
|
+
ref: textareaRef,
|
|
804
|
+
className: "shiteki-popover-textarea",
|
|
805
|
+
placeholder: "What should change?",
|
|
806
|
+
value: comment,
|
|
807
|
+
onChange: (e) => setComment(e.target.value),
|
|
808
|
+
rows: 3
|
|
809
|
+
}
|
|
810
|
+
),
|
|
811
|
+
/* @__PURE__ */ jsxs3("div", { className: "shiteki-popover-actions", children: [
|
|
812
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "shiteki-btn shiteki-btn--danger shiteki-btn--icon", onClick: onRemove, title: "Remove annotation", children: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
813
|
+
/* @__PURE__ */ jsx4("polyline", { points: "3 6 5 6 21 6" }),
|
|
814
|
+
/* @__PURE__ */ jsx4("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
815
|
+
] }) }),
|
|
816
|
+
/* @__PURE__ */ jsx4("div", { style: { flex: 1 } }),
|
|
817
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "shiteki-btn shiteki-btn--ghost", onClick: onClose, children: "Cancel" }),
|
|
818
|
+
/* @__PURE__ */ jsx4("button", { type: "submit", className: "shiteki-btn shiteki-btn--primary", disabled: !comment.trim(), children: "Save" })
|
|
819
|
+
] })
|
|
820
|
+
] })
|
|
821
|
+
]
|
|
822
|
+
}
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
|
|
702
826
|
// src/components/AnnotationMarkers.tsx
|
|
703
827
|
import { AnimatePresence as AnimatePresence2 } from "motion/react";
|
|
704
828
|
|
|
705
829
|
// src/hooks/useMarkerPositions.ts
|
|
706
|
-
import { useEffect as
|
|
830
|
+
import { useEffect as useEffect5, useState as useState9 } from "react";
|
|
707
831
|
function useMarkerPositions(annotations) {
|
|
708
|
-
const [positions, setPositions] =
|
|
709
|
-
|
|
832
|
+
const [positions, setPositions] = useState9([]);
|
|
833
|
+
useEffect5(() => {
|
|
710
834
|
function update() {
|
|
711
|
-
const newPositions = annotations.map((ann) => {
|
|
835
|
+
const newPositions = annotations.map((ann, i) => {
|
|
836
|
+
const index = i + 1;
|
|
712
837
|
const el = document.querySelector(ann.elementInfo.selector);
|
|
713
838
|
if (el) {
|
|
714
839
|
const rect = el.getBoundingClientRect();
|
|
715
|
-
return { id: ann.id, top: rect.top, left: rect.left + rect.width };
|
|
840
|
+
return { id: ann.id, index, top: rect.top, left: rect.left + rect.width };
|
|
716
841
|
}
|
|
717
842
|
const r = ann.elementInfo.rect;
|
|
718
|
-
return { id: ann.id, top: r.top, left: r.left + r.width };
|
|
843
|
+
return { id: ann.id, index, top: r.top, left: r.left + r.width };
|
|
719
844
|
});
|
|
720
845
|
setPositions(newPositions);
|
|
721
846
|
}
|
|
@@ -731,56 +856,57 @@ function useMarkerPositions(annotations) {
|
|
|
731
856
|
}
|
|
732
857
|
|
|
733
858
|
// src/components/AnnotationMarker.tsx
|
|
734
|
-
import { motion as
|
|
735
|
-
import { jsx as
|
|
736
|
-
function AnnotationMarker({ id, top, left,
|
|
737
|
-
return /* @__PURE__ */
|
|
738
|
-
|
|
859
|
+
import { motion as motion4 } from "motion/react";
|
|
860
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
861
|
+
function AnnotationMarker({ id, index, top, left, onClick }) {
|
|
862
|
+
return /* @__PURE__ */ jsx5(
|
|
863
|
+
motion4.div,
|
|
739
864
|
{
|
|
740
865
|
className: "shiteki-marker",
|
|
741
866
|
style: { top: top - 10, left: left - 10 },
|
|
742
|
-
title:
|
|
743
|
-
onClick: () =>
|
|
867
|
+
title: `#${index} \u2014 click to view, ${navigator.userAgent.includes("Mac") ? "\u2318" : "Ctrl"}+click to remove`,
|
|
868
|
+
onClick: (e) => onClick(id, e),
|
|
744
869
|
initial: { opacity: 0, scale: 0 },
|
|
745
870
|
animate: { opacity: 1, scale: 1 },
|
|
746
871
|
exit: { opacity: 0, scale: 0 },
|
|
747
872
|
transition: spring,
|
|
748
873
|
whileHover: { scale: 1.25 },
|
|
749
|
-
children:
|
|
874
|
+
children: index
|
|
750
875
|
}
|
|
751
876
|
);
|
|
752
877
|
}
|
|
753
878
|
|
|
754
879
|
// src/components/AnnotationMarkers.tsx
|
|
755
|
-
import { jsx as
|
|
756
|
-
function AnnotationMarkers({ annotations,
|
|
880
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
881
|
+
function AnnotationMarkers({ annotations, onClick }) {
|
|
757
882
|
const positions = useMarkerPositions(annotations);
|
|
758
|
-
return /* @__PURE__ */
|
|
883
|
+
return /* @__PURE__ */ jsx6(AnimatePresence2, { children: positions.map((pos) => /* @__PURE__ */ jsx6(
|
|
759
884
|
AnnotationMarker,
|
|
760
885
|
{
|
|
761
886
|
id: pos.id,
|
|
887
|
+
index: pos.index,
|
|
762
888
|
top: pos.top,
|
|
763
889
|
left: pos.left,
|
|
764
|
-
|
|
890
|
+
onClick
|
|
765
891
|
},
|
|
766
892
|
pos.id
|
|
767
893
|
)) });
|
|
768
894
|
}
|
|
769
895
|
|
|
770
896
|
// src/components/StatusMessage.tsx
|
|
771
|
-
import { useEffect as
|
|
772
|
-
import { motion as
|
|
773
|
-
import { jsx as
|
|
897
|
+
import { useEffect as useEffect6 } from "react";
|
|
898
|
+
import { motion as motion5 } from "motion/react";
|
|
899
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
774
900
|
function StatusMessage({ state, onDismiss }) {
|
|
775
|
-
|
|
901
|
+
useEffect6(() => {
|
|
776
902
|
if (state.status === "success" || state.status === "error") {
|
|
777
903
|
const timer = setTimeout(onDismiss, 4e3);
|
|
778
904
|
return () => clearTimeout(timer);
|
|
779
905
|
}
|
|
780
906
|
}, [state.status, onDismiss]);
|
|
781
907
|
if (state.status === "success") {
|
|
782
|
-
return /* @__PURE__ */
|
|
783
|
-
|
|
908
|
+
return /* @__PURE__ */ jsxs4(
|
|
909
|
+
motion5.div,
|
|
784
910
|
{
|
|
785
911
|
className: "shiteki-status shiteki-status--success",
|
|
786
912
|
initial: { opacity: 0, y: 12, scale: 0.95 },
|
|
@@ -790,7 +916,7 @@ function StatusMessage({ state, onDismiss }) {
|
|
|
790
916
|
children: [
|
|
791
917
|
"Issue created!",
|
|
792
918
|
" ",
|
|
793
|
-
/* @__PURE__ */
|
|
919
|
+
/* @__PURE__ */ jsxs4("a", { href: state.result.issueUrl, target: "_blank", rel: "noopener noreferrer", children: [
|
|
794
920
|
"#",
|
|
795
921
|
state.result.issueNumber
|
|
796
922
|
] })
|
|
@@ -799,8 +925,8 @@ function StatusMessage({ state, onDismiss }) {
|
|
|
799
925
|
);
|
|
800
926
|
}
|
|
801
927
|
if (state.status === "error") {
|
|
802
|
-
return /* @__PURE__ */
|
|
803
|
-
|
|
928
|
+
return /* @__PURE__ */ jsx7(
|
|
929
|
+
motion5.div,
|
|
804
930
|
{
|
|
805
931
|
className: "shiteki-status shiteki-status--error",
|
|
806
932
|
initial: { opacity: 0, y: 12, scale: 0.95 },
|
|
@@ -812,8 +938,8 @@ function StatusMessage({ state, onDismiss }) {
|
|
|
812
938
|
);
|
|
813
939
|
}
|
|
814
940
|
if (state.status === "loading") {
|
|
815
|
-
return /* @__PURE__ */
|
|
816
|
-
|
|
941
|
+
return /* @__PURE__ */ jsx7(
|
|
942
|
+
motion5.div,
|
|
817
943
|
{
|
|
818
944
|
className: "shiteki-status",
|
|
819
945
|
initial: { opacity: 0, y: 12, scale: 0.95 },
|
|
@@ -828,16 +954,45 @@ function StatusMessage({ state, onDismiss }) {
|
|
|
828
954
|
}
|
|
829
955
|
|
|
830
956
|
// src/components/SettingsPanel.tsx
|
|
831
|
-
import { useCallback as useCallback7, useState as
|
|
832
|
-
import { motion as
|
|
833
|
-
import { jsx as
|
|
957
|
+
import { useCallback as useCallback7, useState as useState10 } from "react";
|
|
958
|
+
import { motion as motion6 } from "motion/react";
|
|
959
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
960
|
+
function HelpLink({ href }) {
|
|
961
|
+
return /* @__PURE__ */ jsx8(
|
|
962
|
+
"a",
|
|
963
|
+
{
|
|
964
|
+
className: "shiteki-settings-help",
|
|
965
|
+
href,
|
|
966
|
+
target: "_blank",
|
|
967
|
+
rel: "noopener noreferrer",
|
|
968
|
+
onClick: (e) => e.stopPropagation(),
|
|
969
|
+
children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: [
|
|
970
|
+
/* @__PURE__ */ jsx8("circle", { cx: "8", cy: "8", r: "7", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
971
|
+
/* @__PURE__ */ jsx8(
|
|
972
|
+
"text",
|
|
973
|
+
{
|
|
974
|
+
x: "8",
|
|
975
|
+
y: "11.5",
|
|
976
|
+
textAnchor: "middle",
|
|
977
|
+
fill: "currentColor",
|
|
978
|
+
fontSize: "10",
|
|
979
|
+
fontWeight: "600",
|
|
980
|
+
fontFamily: "inherit",
|
|
981
|
+
children: "?"
|
|
982
|
+
}
|
|
983
|
+
)
|
|
984
|
+
] })
|
|
985
|
+
}
|
|
986
|
+
);
|
|
987
|
+
}
|
|
834
988
|
function SettingsPanel({ config, onSave, onCancel }) {
|
|
835
|
-
const [mode, setMode] =
|
|
836
|
-
const [endpoint, setEndpoint] =
|
|
837
|
-
const [githubToken, setGithubToken] =
|
|
838
|
-
const [owner, setOwner] =
|
|
839
|
-
const [repo, setRepo] =
|
|
840
|
-
const [labels, setLabels] =
|
|
989
|
+
const [mode, setMode] = useState10(config.mode);
|
|
990
|
+
const [endpoint, setEndpoint] = useState10(config.endpoint);
|
|
991
|
+
const [githubToken, setGithubToken] = useState10(config.githubToken);
|
|
992
|
+
const [owner, setOwner] = useState10(config.owner);
|
|
993
|
+
const [repo, setRepo] = useState10(config.repo);
|
|
994
|
+
const [labels, setLabels] = useState10((config.labels ?? []).join(", "));
|
|
995
|
+
const [clearAfterCopy, setClearAfterCopy] = useState10(config.clearAfterCopy ?? false);
|
|
841
996
|
const handleSave = useCallback7(() => {
|
|
842
997
|
onSave({
|
|
843
998
|
mode,
|
|
@@ -845,11 +1000,12 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
845
1000
|
githubToken: githubToken.trim(),
|
|
846
1001
|
owner: owner.trim(),
|
|
847
1002
|
repo: repo.trim(),
|
|
848
|
-
labels: labels.split(",").map((l) => l.trim()).filter(Boolean)
|
|
1003
|
+
labels: labels.split(",").map((l) => l.trim()).filter(Boolean),
|
|
1004
|
+
clearAfterCopy
|
|
849
1005
|
});
|
|
850
|
-
}, [mode, endpoint, githubToken, owner, repo, labels, onSave]);
|
|
851
|
-
return /* @__PURE__ */
|
|
852
|
-
|
|
1006
|
+
}, [mode, endpoint, githubToken, owner, repo, labels, clearAfterCopy, onSave]);
|
|
1007
|
+
return /* @__PURE__ */ jsxs5(
|
|
1008
|
+
motion6.div,
|
|
853
1009
|
{
|
|
854
1010
|
className: "shiteki-settings",
|
|
855
1011
|
initial: { opacity: 0, y: 12 },
|
|
@@ -857,13 +1013,13 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
857
1013
|
exit: { opacity: 0, y: 12 },
|
|
858
1014
|
transition: spring,
|
|
859
1015
|
children: [
|
|
860
|
-
/* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__PURE__ */
|
|
863
|
-
/* @__PURE__ */
|
|
864
|
-
/* @__PURE__ */
|
|
865
|
-
/* @__PURE__ */
|
|
866
|
-
/* @__PURE__ */
|
|
1016
|
+
/* @__PURE__ */ jsx8("div", { className: "shiteki-settings-header", children: "Settings" }),
|
|
1017
|
+
/* @__PURE__ */ jsxs5("div", { className: "shiteki-settings-body", children: [
|
|
1018
|
+
/* @__PURE__ */ jsxs5("div", { className: "shiteki-settings-field", children: [
|
|
1019
|
+
/* @__PURE__ */ jsx8("span", { className: "shiteki-settings-label", children: "Submit mode" }),
|
|
1020
|
+
/* @__PURE__ */ jsxs5("div", { className: "shiteki-radio-group", children: [
|
|
1021
|
+
/* @__PURE__ */ jsxs5("label", { className: "shiteki-radio", children: [
|
|
1022
|
+
/* @__PURE__ */ jsx8(
|
|
867
1023
|
"input",
|
|
868
1024
|
{
|
|
869
1025
|
type: "radio",
|
|
@@ -872,10 +1028,10 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
872
1028
|
onChange: () => setMode("endpoint")
|
|
873
1029
|
}
|
|
874
1030
|
),
|
|
875
|
-
/* @__PURE__ */
|
|
1031
|
+
/* @__PURE__ */ jsx8("span", { children: "Endpoint" })
|
|
876
1032
|
] }),
|
|
877
|
-
/* @__PURE__ */
|
|
878
|
-
/* @__PURE__ */
|
|
1033
|
+
/* @__PURE__ */ jsxs5("label", { className: "shiteki-radio", children: [
|
|
1034
|
+
/* @__PURE__ */ jsx8(
|
|
879
1035
|
"input",
|
|
880
1036
|
{
|
|
881
1037
|
type: "radio",
|
|
@@ -884,13 +1040,16 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
884
1040
|
onChange: () => setMode("direct")
|
|
885
1041
|
}
|
|
886
1042
|
),
|
|
887
|
-
/* @__PURE__ */
|
|
1043
|
+
/* @__PURE__ */ jsx8("span", { children: "Direct" })
|
|
888
1044
|
] })
|
|
889
1045
|
] })
|
|
890
1046
|
] }),
|
|
891
|
-
mode === "endpoint" ? /* @__PURE__ */
|
|
892
|
-
/* @__PURE__ */
|
|
893
|
-
|
|
1047
|
+
mode === "endpoint" ? /* @__PURE__ */ jsxs5("label", { className: "shiteki-settings-field", children: [
|
|
1048
|
+
/* @__PURE__ */ jsxs5("span", { className: "shiteki-settings-label", children: [
|
|
1049
|
+
"Endpoint",
|
|
1050
|
+
/* @__PURE__ */ jsx8(HelpLink, { href: "https://github.com/taterboom/shiteki/tree/main/apps/api" })
|
|
1051
|
+
] }),
|
|
1052
|
+
/* @__PURE__ */ jsx8(
|
|
894
1053
|
"input",
|
|
895
1054
|
{
|
|
896
1055
|
className: "shiteki-settings-input",
|
|
@@ -900,9 +1059,12 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
900
1059
|
placeholder: "https://..."
|
|
901
1060
|
}
|
|
902
1061
|
)
|
|
903
|
-
] }) : /* @__PURE__ */
|
|
904
|
-
/* @__PURE__ */
|
|
905
|
-
|
|
1062
|
+
] }) : /* @__PURE__ */ jsxs5("label", { className: "shiteki-settings-field", children: [
|
|
1063
|
+
/* @__PURE__ */ jsxs5("span", { className: "shiteki-settings-label", children: [
|
|
1064
|
+
"GitHub Token",
|
|
1065
|
+
/* @__PURE__ */ jsx8(HelpLink, { href: "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens" })
|
|
1066
|
+
] }),
|
|
1067
|
+
/* @__PURE__ */ jsx8(
|
|
906
1068
|
"input",
|
|
907
1069
|
{
|
|
908
1070
|
className: "shiteki-settings-input",
|
|
@@ -913,9 +1075,9 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
913
1075
|
}
|
|
914
1076
|
)
|
|
915
1077
|
] }),
|
|
916
|
-
/* @__PURE__ */
|
|
917
|
-
/* @__PURE__ */
|
|
918
|
-
/* @__PURE__ */
|
|
1078
|
+
/* @__PURE__ */ jsxs5("label", { className: "shiteki-settings-field", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx8("span", { className: "shiteki-settings-label", children: "Owner" }),
|
|
1080
|
+
/* @__PURE__ */ jsx8(
|
|
919
1081
|
"input",
|
|
920
1082
|
{
|
|
921
1083
|
className: "shiteki-settings-input",
|
|
@@ -926,9 +1088,9 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
926
1088
|
}
|
|
927
1089
|
)
|
|
928
1090
|
] }),
|
|
929
|
-
/* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */
|
|
1091
|
+
/* @__PURE__ */ jsxs5("label", { className: "shiteki-settings-field", children: [
|
|
1092
|
+
/* @__PURE__ */ jsx8("span", { className: "shiteki-settings-label", children: "Repo" }),
|
|
1093
|
+
/* @__PURE__ */ jsx8(
|
|
932
1094
|
"input",
|
|
933
1095
|
{
|
|
934
1096
|
className: "shiteki-settings-input",
|
|
@@ -939,9 +1101,12 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
939
1101
|
}
|
|
940
1102
|
)
|
|
941
1103
|
] }),
|
|
942
|
-
/* @__PURE__ */
|
|
943
|
-
/* @__PURE__ */
|
|
944
|
-
|
|
1104
|
+
/* @__PURE__ */ jsxs5("label", { className: "shiteki-settings-field", children: [
|
|
1105
|
+
/* @__PURE__ */ jsxs5("span", { className: "shiteki-settings-label", children: [
|
|
1106
|
+
"Labels",
|
|
1107
|
+
/* @__PURE__ */ jsx8(HelpLink, { href: "https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels" })
|
|
1108
|
+
] }),
|
|
1109
|
+
/* @__PURE__ */ jsx8(
|
|
945
1110
|
"input",
|
|
946
1111
|
{
|
|
947
1112
|
className: "shiteki-settings-input",
|
|
@@ -951,11 +1116,23 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
951
1116
|
placeholder: "bug, feedback"
|
|
952
1117
|
}
|
|
953
1118
|
)
|
|
1119
|
+
] }),
|
|
1120
|
+
/* @__PURE__ */ jsxs5("label", { className: "shiteki-settings-field shiteki-settings-field--row", children: [
|
|
1121
|
+
/* @__PURE__ */ jsx8("span", { className: "shiteki-settings-label", children: "Clear after copy" }),
|
|
1122
|
+
/* @__PURE__ */ jsx8(
|
|
1123
|
+
"input",
|
|
1124
|
+
{
|
|
1125
|
+
type: "checkbox",
|
|
1126
|
+
className: "shiteki-settings-checkbox",
|
|
1127
|
+
checked: clearAfterCopy,
|
|
1128
|
+
onChange: (e) => setClearAfterCopy(e.target.checked)
|
|
1129
|
+
}
|
|
1130
|
+
)
|
|
954
1131
|
] })
|
|
955
1132
|
] }),
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
/* @__PURE__ */
|
|
958
|
-
/* @__PURE__ */
|
|
1133
|
+
/* @__PURE__ */ jsxs5("div", { className: "shiteki-settings-actions", children: [
|
|
1134
|
+
/* @__PURE__ */ jsx8("button", { className: "shiteki-btn shiteki-btn--ghost", onClick: onCancel, children: "Cancel" }),
|
|
1135
|
+
/* @__PURE__ */ jsx8("button", { className: "shiteki-btn shiteki-btn--primary", onClick: handleSave, children: "Save" })
|
|
959
1136
|
] })
|
|
960
1137
|
]
|
|
961
1138
|
}
|
|
@@ -963,13 +1140,13 @@ function SettingsPanel({ config, onSave, onCancel }) {
|
|
|
963
1140
|
}
|
|
964
1141
|
|
|
965
1142
|
// src/components/SendDialog.tsx
|
|
966
|
-
import { useCallback as useCallback8, useEffect as
|
|
967
|
-
import { motion as
|
|
968
|
-
import { jsx as
|
|
1143
|
+
import { useCallback as useCallback8, useEffect as useEffect7, useRef as useRef7, useState as useState11 } from "react";
|
|
1144
|
+
import { motion as motion7 } from "motion/react";
|
|
1145
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
969
1146
|
function SendDialog({ defaultTitle, sending, onConfirm, onCancel }) {
|
|
970
|
-
const [title, setTitle] =
|
|
971
|
-
const inputRef =
|
|
972
|
-
|
|
1147
|
+
const [title, setTitle] = useState11(defaultTitle);
|
|
1148
|
+
const inputRef = useRef7(null);
|
|
1149
|
+
useEffect7(() => {
|
|
973
1150
|
inputRef.current?.focus();
|
|
974
1151
|
inputRef.current?.select();
|
|
975
1152
|
}, []);
|
|
@@ -981,8 +1158,8 @@ function SendDialog({ defaultTitle, sending, onConfirm, onCancel }) {
|
|
|
981
1158
|
},
|
|
982
1159
|
[title, sending, onConfirm]
|
|
983
1160
|
);
|
|
984
|
-
return /* @__PURE__ */
|
|
985
|
-
|
|
1161
|
+
return /* @__PURE__ */ jsxs6(
|
|
1162
|
+
motion7.div,
|
|
986
1163
|
{
|
|
987
1164
|
className: "shiteki-popover",
|
|
988
1165
|
style: {
|
|
@@ -996,9 +1173,9 @@ function SendDialog({ defaultTitle, sending, onConfirm, onCancel }) {
|
|
|
996
1173
|
exit: { opacity: 0, y: 12 },
|
|
997
1174
|
transition: spring,
|
|
998
1175
|
children: [
|
|
999
|
-
/* @__PURE__ */
|
|
1000
|
-
/* @__PURE__ */
|
|
1001
|
-
/* @__PURE__ */
|
|
1176
|
+
/* @__PURE__ */ jsx9("div", { className: "shiteki-popover-info", children: /* @__PURE__ */ jsx9("span", { className: "shiteki-popover-tag", children: "Create Issue" }) }),
|
|
1177
|
+
/* @__PURE__ */ jsxs6("form", { className: "shiteki-popover-form", onSubmit: handleSubmit, children: [
|
|
1178
|
+
/* @__PURE__ */ jsx9(
|
|
1002
1179
|
"input",
|
|
1003
1180
|
{
|
|
1004
1181
|
ref: inputRef,
|
|
@@ -1009,8 +1186,8 @@ function SendDialog({ defaultTitle, sending, onConfirm, onCancel }) {
|
|
|
1009
1186
|
placeholder: "Issue title"
|
|
1010
1187
|
}
|
|
1011
1188
|
),
|
|
1012
|
-
/* @__PURE__ */
|
|
1013
|
-
/* @__PURE__ */
|
|
1189
|
+
/* @__PURE__ */ jsxs6("div", { className: "shiteki-popover-actions", children: [
|
|
1190
|
+
/* @__PURE__ */ jsx9(
|
|
1014
1191
|
"button",
|
|
1015
1192
|
{
|
|
1016
1193
|
type: "button",
|
|
@@ -1020,7 +1197,7 @@ function SendDialog({ defaultTitle, sending, onConfirm, onCancel }) {
|
|
|
1020
1197
|
children: "Cancel"
|
|
1021
1198
|
}
|
|
1022
1199
|
),
|
|
1023
|
-
/* @__PURE__ */
|
|
1200
|
+
/* @__PURE__ */ jsx9(
|
|
1024
1201
|
"button",
|
|
1025
1202
|
{
|
|
1026
1203
|
type: "submit",
|
|
@@ -1059,22 +1236,23 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
1059
1236
|
}
|
|
1060
1237
|
|
|
1061
1238
|
// src/styles/widget.css
|
|
1062
|
-
styleInject('.shiteki-root {\n --shiteki-primary: #2563eb;\n --shiteki-primary-hover: #1d4ed8;\n --shiteki-bg: #ffffff;\n --shiteki-border: #e5e7eb;\n --shiteki-text: #111827;\n --shiteki-text-secondary: #6b7280;\n --shiteki-radius: 12px;\n --shiteki-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n font-family:\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: var(--shiteki-text);\n box-sizing: border-box;\n}\n.shiteki-toolbar {\n position: fixed;\n bottom: 20px;\n right: 20px;\n display: flex;\n align-items: center;\n border-radius: 9999px;\n z-index: 99999;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n box-shadow: var(--shiteki-shadow);\n}\n.shiteki-toolbar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--shiteki-text);\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n.shiteki-toolbar-btn:hover {\n background: #f3f4f6;\n}\n.shiteki-toolbar-btn:active {\n transform: scale(0.9);\n}\n.shiteki-toolbar-btn--active {\n background: var(--shiteki-primary);\n color: #fff;\n}\n.shiteki-toolbar-btn--active:hover {\n background: var(--shiteki-primary-hover);\n}\n.shiteki-toolbar-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.shiteki-toolbar-picker {\n position: relative;\n}\n.shiteki-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -6px;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n border-radius: 8px;\n background: #ef4444;\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n line-height: 1;\n pointer-events: none;\n box-sizing: border-box;\n}\n.shiteki-toolbar-sep {\n width: 1px;\n height: 24px;\n background: var(--shiteki-border);\n margin: 0 4px;\n}\n.shiteki-highlight {\n position: fixed;\n pointer-events: none;\n border: 1.5px solid var(--shiteki-primary, #2563eb);\n background: rgba(37, 99, 235, 0.08);\n z-index: 99997;\n transition: all 0.05s ease-out;\n box-sizing: border-box;\n}\n.shiteki-marker {\n position: fixed;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: var(--shiteki-primary, #2563eb);\n color: #fff;\n font-size: 11px;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: 99998;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);\n user-select: none;\n}\n.shiteki-popover {\n position: fixed;\n width: 320px;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n border-radius: var(--shiteki-radius);\n box-shadow: var(--shiteki-shadow);\n z-index: 99999;\n overflow: hidden;\n}\n.shiteki-popover-info {\n padding: 10px 14px;\n background: #f9fafb;\n border-bottom: 1px solid var(--shiteki-border);\n display: flex;\n align-items: baseline;\n gap: 8px;\n overflow: hidden;\n}\n.shiteki-popover-tag {\n font-family:\n ui-monospace,\n SFMono-Regular,\n "SF Mono",\n Menlo,\n monospace;\n font-size: 12px;\n font-weight: 600;\n color: var(--shiteki-primary);\n white-space: nowrap;\n}\n.shiteki-popover-text {\n font-size: 12px;\n color: var(--shiteki-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.shiteki-popover-form {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.shiteki-popover-textarea {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid var(--shiteki-border);\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n color: var(--shiteki-text);\n background: transparent;\n outline: none;\n resize: vertical;\n min-height: 60px;\n box-sizing: border-box;\n transition: border-color 0.15s;\n}\n.shiteki-popover-textarea:focus {\n border-color: var(--shiteki-primary);\n}\n.shiteki-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.shiteki-btn {\n padding: 6px 14px;\n border: none;\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s;\n}\n.shiteki-btn:active {\n transform: scale(0.95);\n}\n.shiteki-btn--primary {\n background: var(--shiteki-primary);\n color: #fff;\n}\n.shiteki-btn--primary:hover:not(:disabled) {\n background: var(--shiteki-primary-hover);\n}\n.shiteki-btn--primary:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.shiteki-btn--ghost {\n background: transparent;\n color: var(--shiteki-text-secondary);\n}\n.shiteki-btn--ghost:hover {\n background: #f3f4f6;\n}\n.shiteki-status {\n position: fixed;\n bottom: 76px;\n right: 20px;\n padding: 8px 18px;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n border-radius: 8px;\n box-shadow: var(--shiteki-shadow);\n font-size: 13px;\n white-space: nowrap;\n z-index: 99998;\n}\n.shiteki-status--success {\n color: #059669;\n}\n.shiteki-status--error {\n color: #dc2626;\n}\n.shiteki-status a {\n color: inherit;\n text-decoration: underline;\n}\n.shiteki-settings {\n position: fixed;\n bottom: 76px;\n right: 20px;\n width: 280px;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n border-radius: var(--shiteki-radius);\n box-shadow: var(--shiteki-shadow);\n z-index: 99999;\n overflow: hidden;\n}\n.shiteki-settings-header {\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 600;\n border-bottom: 1px solid var(--shiteki-border);\n background: #f9fafb;\n}\n.shiteki-settings-body {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.shiteki-settings-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.shiteki-settings-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--shiteki-text-secondary);\n}\n.shiteki-settings-input {\n width: 100%;\n padding: 6px 8px;\n border: 1px solid var(--shiteki-border);\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n color: var(--shiteki-text);\n background: transparent;\n outline: none;\n box-sizing: border-box;\n transition: border-color 0.15s;\n}\n.shiteki-settings-input:focus {\n border-color: var(--shiteki-primary);\n}\n.shiteki-radio-group {\n display: flex;\n gap: 12px;\n}\n.shiteki-radio {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 13px;\n cursor: pointer;\n}\n.shiteki-radio input[type=radio] {\n margin: 0;\n accent-color: var(--shiteki-primary);\n cursor: pointer;\n}\n.shiteki-settings-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 0 14px 12px;\n}\n.shiteki-shortcut-hint {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n display: flex;\n
|
|
1239
|
+
styleInject('.shiteki-root {\n --shiteki-primary: #2563eb;\n --shiteki-primary-hover: #1d4ed8;\n --shiteki-bg: #ffffff;\n --shiteki-border: #e5e7eb;\n --shiteki-text: #111827;\n --shiteki-text-secondary: #6b7280;\n --shiteki-radius: 12px;\n --shiteki-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n font-family:\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: var(--shiteki-text);\n box-sizing: border-box;\n}\n.shiteki-toolbar {\n position: fixed;\n bottom: 20px;\n right: 20px;\n display: flex;\n align-items: center;\n gap: 2px;\n border-radius: 9999px;\n z-index: 99999;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n box-shadow: var(--shiteki-shadow);\n}\n.shiteki-toolbar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--shiteki-text);\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n.shiteki-toolbar-btn:hover {\n background: #f3f4f6;\n}\n.shiteki-toolbar-btn:active {\n transform: scale(0.9);\n}\n.shiteki-toolbar-btn--active {\n background: var(--shiteki-primary);\n color: #fff;\n}\n.shiteki-toolbar-btn--active:hover {\n background: var(--shiteki-primary-hover);\n}\n.shiteki-toolbar-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.shiteki-toolbar-picker {\n position: relative;\n}\n.shiteki-toolbar-badge {\n position: absolute;\n top: -4px;\n right: -6px;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n border-radius: 8px;\n background: #ef4444;\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n line-height: 1;\n pointer-events: none;\n box-sizing: border-box;\n}\n.shiteki-toolbar-sep {\n width: 1px;\n height: 24px;\n background: var(--shiteki-border);\n margin: 0 4px;\n}\n.shiteki-highlight {\n position: fixed;\n pointer-events: none;\n border: 1.5px solid var(--shiteki-primary, #2563eb);\n background: rgba(37, 99, 235, 0.08);\n z-index: 99997;\n transition: all 0.05s ease-out;\n box-sizing: border-box;\n}\n.shiteki-marker {\n position: fixed;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: var(--shiteki-primary, #2563eb);\n color: #fff;\n font-size: 11px;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n z-index: 99998;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);\n user-select: none;\n}\n.shiteki-popover {\n position: fixed;\n width: 320px;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n border-radius: var(--shiteki-radius);\n box-shadow: var(--shiteki-shadow);\n z-index: 99999;\n overflow: hidden;\n}\n.shiteki-popover-info {\n padding: 10px 14px;\n background: #f9fafb;\n border-bottom: 1px solid var(--shiteki-border);\n display: flex;\n align-items: baseline;\n gap: 8px;\n overflow: hidden;\n}\n.shiteki-popover-tag {\n font-family:\n ui-monospace,\n SFMono-Regular,\n "SF Mono",\n Menlo,\n monospace;\n font-size: 12px;\n font-weight: 600;\n color: var(--shiteki-primary);\n white-space: nowrap;\n}\n.shiteki-popover-text {\n font-size: 12px;\n color: var(--shiteki-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.shiteki-popover-form {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.shiteki-popover-textarea {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid var(--shiteki-border);\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n color: var(--shiteki-text);\n background: transparent;\n outline: none;\n resize: vertical;\n min-height: 60px;\n box-sizing: border-box;\n transition: border-color 0.15s;\n}\n.shiteki-popover-textarea:focus {\n border-color: var(--shiteki-primary);\n}\n.shiteki-popover-comment {\n font-size: 13px;\n color: var(--shiteki-text);\n white-space: pre-wrap;\n word-break: break-word;\n}\n.shiteki-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.shiteki-btn {\n padding: 6px 14px;\n border: none;\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s;\n}\n.shiteki-btn:active {\n transform: scale(0.95);\n}\n.shiteki-btn--primary {\n background: var(--shiteki-primary);\n color: #fff;\n}\n.shiteki-btn--primary:hover:not(:disabled) {\n background: var(--shiteki-primary-hover);\n}\n.shiteki-btn--primary:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.shiteki-btn--ghost {\n background: transparent;\n color: var(--shiteki-text-secondary);\n}\n.shiteki-btn--ghost:hover {\n background: #f3f4f6;\n}\n.shiteki-btn--danger {\n background: #fee2e2;\n color: #dc2626;\n}\n.shiteki-btn--danger:hover {\n background: #fecaca;\n}\n.shiteki-btn--icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n padding: 0;\n}\n.shiteki-status {\n position: fixed;\n bottom: 76px;\n right: 20px;\n padding: 8px 18px;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n border-radius: 8px;\n box-shadow: var(--shiteki-shadow);\n font-size: 13px;\n white-space: nowrap;\n z-index: 99998;\n}\n.shiteki-status--success {\n color: #059669;\n}\n.shiteki-status--error {\n color: #dc2626;\n}\n.shiteki-status a {\n color: inherit;\n text-decoration: underline;\n}\n.shiteki-settings {\n position: fixed;\n bottom: 76px;\n right: 20px;\n width: 280px;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n border-radius: var(--shiteki-radius);\n box-shadow: var(--shiteki-shadow);\n z-index: 99999;\n overflow: hidden;\n}\n.shiteki-settings-header {\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 600;\n border-bottom: 1px solid var(--shiteki-border);\n background: #f9fafb;\n}\n.shiteki-settings-body {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.shiteki-settings-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.shiteki-settings-field--row {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n}\n.shiteki-settings-checkbox {\n margin: 0;\n accent-color: var(--shiteki-primary);\n cursor: pointer;\n}\n.shiteki-settings-label {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 500;\n color: var(--shiteki-text-secondary);\n}\n.shiteki-settings-help {\n display: inline-flex;\n align-items: center;\n color: var(--shiteki-text-secondary);\n opacity: 0.5;\n transition: opacity 0.15s;\n}\n.shiteki-settings-help:hover {\n opacity: 1;\n}\n.shiteki-settings-input {\n width: 100%;\n padding: 6px 8px;\n border: 1px solid var(--shiteki-border);\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n color: var(--shiteki-text);\n background: transparent;\n outline: none;\n box-sizing: border-box;\n transition: border-color 0.15s;\n}\n.shiteki-settings-input:focus {\n border-color: var(--shiteki-primary);\n}\n.shiteki-radio-group {\n display: flex;\n gap: 12px;\n}\n.shiteki-radio {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 13px;\n cursor: pointer;\n}\n.shiteki-radio input[type=radio] {\n margin: 0;\n accent-color: var(--shiteki-primary);\n cursor: pointer;\n}\n.shiteki-settings-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 0 14px 12px;\n}\n.shiteki-shortcut-hint {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n display: flex;\n flex-direction: column;\n gap: 0;\n padding: 8px 12px;\n background: var(--shiteki-bg);\n border: 1px solid var(--shiteki-border);\n border-radius: 8px;\n box-shadow: var(--shiteki-shadow);\n font-size: 12px;\n color: var(--shiteki-text-secondary);\n white-space: nowrap;\n}\n.shiteki-shortcut-hint-close {\n position: absolute;\n top: 6px;\n right: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--shiteki-text-secondary);\n cursor: pointer;\n padding: 0;\n}\n.shiteki-shortcut-hint-close:hover {\n background: #f3f4f6;\n}\n.shiteki-shortcut-hint-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.shiteki-shortcut-hint-list span {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.shiteki-shortcut-hint kbd {\n display: inline-block;\n padding: 1px 5px;\n background: #f3f4f6;\n border: 1px solid var(--shiteki-border);\n border-radius: 4px;\n font-family: inherit;\n font-size: 11px;\n font-weight: 600;\n color: var(--shiteki-text);\n line-height: 1.4;\n min-width: 20px;\n text-align: center;\n}\n.shiteki-shortcut-hint-close:hover {\n background: #f3f4f6;\n}\n');
|
|
1063
1240
|
|
|
1064
1241
|
// src/components/ShitekiWidget.tsx
|
|
1065
|
-
import { Fragment, jsx as
|
|
1242
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1066
1243
|
function ShitekiWidget(props) {
|
|
1067
1244
|
const { config, updateConfig } = useConfig(props);
|
|
1068
|
-
const [open, setOpen] =
|
|
1069
|
-
const [mode, setMode] =
|
|
1070
|
-
const [settingsOpen, setSettingsOpen] =
|
|
1071
|
-
const [sendDialogOpen, setSendDialogOpen] =
|
|
1072
|
-
const [selectedElement, setSelectedElement] =
|
|
1073
|
-
const
|
|
1245
|
+
const [open, setOpen] = useState12(false);
|
|
1246
|
+
const [mode, setMode] = useState12("idle");
|
|
1247
|
+
const [settingsOpen, setSettingsOpen] = useState12(false);
|
|
1248
|
+
const [sendDialogOpen, setSendDialogOpen] = useState12(false);
|
|
1249
|
+
const [selectedElement, setSelectedElement] = useState12(null);
|
|
1250
|
+
const [viewingAnnotation, setViewingAnnotation] = useState12(null);
|
|
1251
|
+
const { annotations, add, update, remove, clear } = useAnnotations();
|
|
1074
1252
|
const { state: submitState, submit, reset: resetSubmit } = useSubmit(config);
|
|
1075
1253
|
const { copied, copy } = useClipboard();
|
|
1076
1254
|
const { showHint, dismissHint } = useShortcutHint();
|
|
1077
|
-
|
|
1255
|
+
useEffect8(() => {
|
|
1078
1256
|
if (submitState.status === "success") {
|
|
1079
1257
|
clear();
|
|
1080
1258
|
}
|
|
@@ -1117,10 +1295,43 @@ function ShitekiWidget(props) {
|
|
|
1117
1295
|
setSelectedElement(null);
|
|
1118
1296
|
setMode("picking");
|
|
1119
1297
|
}, []);
|
|
1298
|
+
const handleMarkerClick = useCallback9(
|
|
1299
|
+
(id, e) => {
|
|
1300
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1301
|
+
remove(id);
|
|
1302
|
+
} else {
|
|
1303
|
+
const ann = annotations.find((a) => a.id === id);
|
|
1304
|
+
if (ann) setViewingAnnotation(ann);
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
[annotations, remove]
|
|
1308
|
+
);
|
|
1309
|
+
const handleCloseViewing = useCallback9(() => {
|
|
1310
|
+
setViewingAnnotation(null);
|
|
1311
|
+
}, []);
|
|
1312
|
+
const handleUpdateViewing = useCallback9(
|
|
1313
|
+
(comment) => {
|
|
1314
|
+
if (viewingAnnotation) {
|
|
1315
|
+
update(viewingAnnotation.id, comment);
|
|
1316
|
+
setViewingAnnotation(null);
|
|
1317
|
+
}
|
|
1318
|
+
},
|
|
1319
|
+
[viewingAnnotation, update]
|
|
1320
|
+
);
|
|
1321
|
+
const handleRemoveViewing = useCallback9(() => {
|
|
1322
|
+
if (viewingAnnotation) {
|
|
1323
|
+
remove(viewingAnnotation.id);
|
|
1324
|
+
setViewingAnnotation(null);
|
|
1325
|
+
}
|
|
1326
|
+
}, [viewingAnnotation, remove]);
|
|
1120
1327
|
const handleCopy = useCallback9(() => {
|
|
1121
1328
|
const prompt = generatePrompt(annotations);
|
|
1122
1329
|
copy(prompt);
|
|
1123
|
-
|
|
1330
|
+
if (config.clearAfterCopy) {
|
|
1331
|
+
clear();
|
|
1332
|
+
setSelectedElement(null);
|
|
1333
|
+
}
|
|
1334
|
+
}, [annotations, copy, config.clearAfterCopy, clear]);
|
|
1124
1335
|
const handleSend = useCallback9(() => {
|
|
1125
1336
|
setSendDialogOpen(true);
|
|
1126
1337
|
}, []);
|
|
@@ -1138,11 +1349,13 @@ function ShitekiWidget(props) {
|
|
|
1138
1349
|
const handleClear = useCallback9(() => {
|
|
1139
1350
|
clear();
|
|
1140
1351
|
setSelectedElement(null);
|
|
1352
|
+
setViewingAnnotation(null);
|
|
1141
1353
|
}, [clear]);
|
|
1142
1354
|
const handleClose = useCallback9(() => {
|
|
1143
1355
|
setOpen(false);
|
|
1144
1356
|
setMode("idle");
|
|
1145
1357
|
setSelectedElement(null);
|
|
1358
|
+
setViewingAnnotation(null);
|
|
1146
1359
|
setSettingsOpen(false);
|
|
1147
1360
|
}, []);
|
|
1148
1361
|
const handleToggleSettings = useCallback9(() => {
|
|
@@ -1158,10 +1371,12 @@ function ShitekiWidget(props) {
|
|
|
1158
1371
|
const handleSettingsCancel = useCallback9(() => {
|
|
1159
1372
|
setSettingsOpen(false);
|
|
1160
1373
|
}, []);
|
|
1374
|
+
const canSend = config.owner.trim() !== "" && config.repo.trim() !== "" && (config.mode === "endpoint" ? config.endpoint.trim() !== "" : config.githubToken.trim() !== "");
|
|
1161
1375
|
useKeyboardShortcuts({
|
|
1162
1376
|
open,
|
|
1163
1377
|
mode,
|
|
1164
1378
|
annotationCount: annotations.length,
|
|
1379
|
+
canSend,
|
|
1165
1380
|
settingsOpen,
|
|
1166
1381
|
sendDialogOpen,
|
|
1167
1382
|
onCopy: handleCopy,
|
|
@@ -1174,14 +1389,15 @@ function ShitekiWidget(props) {
|
|
|
1174
1389
|
onCloseSendDialog: handleSendCancel
|
|
1175
1390
|
});
|
|
1176
1391
|
return ReactDOM.createPortal(
|
|
1177
|
-
/* @__PURE__ */
|
|
1178
|
-
/* @__PURE__ */
|
|
1392
|
+
/* @__PURE__ */ jsxs7("div", { className: "shiteki-root", children: [
|
|
1393
|
+
/* @__PURE__ */ jsx10(
|
|
1179
1394
|
Toolbar,
|
|
1180
1395
|
{
|
|
1181
1396
|
open,
|
|
1182
1397
|
annotationCount: annotations.length,
|
|
1183
1398
|
copied,
|
|
1184
1399
|
sending: submitState.status === "loading",
|
|
1400
|
+
canSend,
|
|
1185
1401
|
settingsOpen,
|
|
1186
1402
|
onOpen: handleOpen,
|
|
1187
1403
|
onCopy: handleCopy,
|
|
@@ -1193,9 +1409,9 @@ function ShitekiWidget(props) {
|
|
|
1193
1409
|
onDismissHint: dismissHint
|
|
1194
1410
|
}
|
|
1195
1411
|
),
|
|
1196
|
-
open && /* @__PURE__ */
|
|
1197
|
-
/* @__PURE__ */
|
|
1198
|
-
/* @__PURE__ */
|
|
1412
|
+
open && /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
1413
|
+
/* @__PURE__ */ jsx10(ElementHighlight, { rect: hoveredRect }),
|
|
1414
|
+
/* @__PURE__ */ jsx10(AnimatePresence3, { children: mode === "annotating" && selectedElement && /* @__PURE__ */ jsx10(
|
|
1199
1415
|
AnnotationPopover,
|
|
1200
1416
|
{
|
|
1201
1417
|
elementInfo: selectedElement,
|
|
@@ -1204,9 +1420,20 @@ function ShitekiWidget(props) {
|
|
|
1204
1420
|
},
|
|
1205
1421
|
"popover"
|
|
1206
1422
|
) }),
|
|
1207
|
-
/* @__PURE__ */
|
|
1208
|
-
/* @__PURE__ */
|
|
1209
|
-
|
|
1423
|
+
/* @__PURE__ */ jsx10(AnnotationMarkers, { annotations, onClick: handleMarkerClick }),
|
|
1424
|
+
/* @__PURE__ */ jsx10(AnimatePresence3, { children: viewingAnnotation && /* @__PURE__ */ jsx10(
|
|
1425
|
+
AnnotationDetailPopover,
|
|
1426
|
+
{
|
|
1427
|
+
annotation: viewingAnnotation,
|
|
1428
|
+
index: annotations.findIndex((a) => a.id === viewingAnnotation.id) + 1,
|
|
1429
|
+
onUpdate: handleUpdateViewing,
|
|
1430
|
+
onRemove: handleRemoveViewing,
|
|
1431
|
+
onClose: handleCloseViewing
|
|
1432
|
+
},
|
|
1433
|
+
"detail"
|
|
1434
|
+
) }),
|
|
1435
|
+
/* @__PURE__ */ jsxs7(AnimatePresence3, { children: [
|
|
1436
|
+
settingsOpen && /* @__PURE__ */ jsx10(
|
|
1210
1437
|
SettingsPanel,
|
|
1211
1438
|
{
|
|
1212
1439
|
config,
|
|
@@ -1215,7 +1442,7 @@ function ShitekiWidget(props) {
|
|
|
1215
1442
|
},
|
|
1216
1443
|
"settings"
|
|
1217
1444
|
),
|
|
1218
|
-
sendDialogOpen && /* @__PURE__ */
|
|
1445
|
+
sendDialogOpen && /* @__PURE__ */ jsx10(
|
|
1219
1446
|
SendDialog,
|
|
1220
1447
|
{
|
|
1221
1448
|
defaultTitle: `Visual Annotations (${annotations.length}) \u2014 ${document.title || location.href}`,
|
|
@@ -1226,7 +1453,7 @@ function ShitekiWidget(props) {
|
|
|
1226
1453
|
"send-dialog"
|
|
1227
1454
|
)
|
|
1228
1455
|
] }),
|
|
1229
|
-
/* @__PURE__ */
|
|
1456
|
+
/* @__PURE__ */ jsx10(AnimatePresence3, { children: submitState.status !== "idle" && /* @__PURE__ */ jsx10(StatusMessage, { state: submitState, onDismiss: resetSubmit }, "status") })
|
|
1230
1457
|
] })
|
|
1231
1458
|
] }),
|
|
1232
1459
|
document.body
|