@immense/vue-pom-generator 1.0.70 → 1.0.71
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/RELEASE_NOTES.md +19 -11
- package/dist/index.cjs +1396 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1398 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ const tsMorph = require("ts-morph");
|
|
|
35
35
|
const types = require("@babel/types");
|
|
36
36
|
const node_perf_hooks = require("node:perf_hooks");
|
|
37
37
|
const vue = require("@vitejs/plugin-vue");
|
|
38
|
+
const dom = require("@floating-ui/dom");
|
|
38
39
|
var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
|
|
39
40
|
function _interopNamespaceDefault(e) {
|
|
40
41
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -9674,6 +9675,1400 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
9674
9675
|
}
|
|
9675
9676
|
return resultPlugins;
|
|
9676
9677
|
}
|
|
9678
|
+
function normalizeInlineText(value) {
|
|
9679
|
+
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
9680
|
+
return normalized || void 0;
|
|
9681
|
+
}
|
|
9682
|
+
function formatAnnotations(annotations, detail, pageUrl) {
|
|
9683
|
+
const shortUrl = pageUrl.replace(/^https?:\/\//, "");
|
|
9684
|
+
const lines = [];
|
|
9685
|
+
lines.push(`## Feedback — ${shortUrl}`);
|
|
9686
|
+
lines.push(`- **URL:** ${pageUrl}`);
|
|
9687
|
+
if (detail === "forensic" && typeof window !== "undefined") {
|
|
9688
|
+
lines.push(`- **Viewport:** ${window.innerWidth}x${window.innerHeight}`);
|
|
9689
|
+
}
|
|
9690
|
+
lines.push("");
|
|
9691
|
+
for (let i = 0; i < annotations.length; i += 1) {
|
|
9692
|
+
const annotation = annotations[i];
|
|
9693
|
+
const title = normalizeInlineText(annotation.comment) || annotation.targetLabel;
|
|
9694
|
+
lines.push(`### ${i + 1}. ${title}`);
|
|
9695
|
+
lines.push(`- **Source:** ${annotation.source || "Unable to find component file path."}`);
|
|
9696
|
+
if (annotation.component) {
|
|
9697
|
+
lines.push(`- **Component:** ${annotation.component}`);
|
|
9698
|
+
}
|
|
9699
|
+
lines.push(`- **Target:** \`${annotation.targetLabel}\``);
|
|
9700
|
+
const uiText = normalizeInlineText(annotation.uiText);
|
|
9701
|
+
if (uiText) {
|
|
9702
|
+
lines.push(`- **UI text:** \`${uiText}\``);
|
|
9703
|
+
}
|
|
9704
|
+
const locator = normalizeInlineText(annotation.locator);
|
|
9705
|
+
if (locator) {
|
|
9706
|
+
lines.push(`- **Locator:** ${locator}`);
|
|
9707
|
+
}
|
|
9708
|
+
if (detail === "forensic") {
|
|
9709
|
+
const domHint = normalizeInlineText(annotation.domHint);
|
|
9710
|
+
if (domHint) {
|
|
9711
|
+
lines.push(`- **DOM hint:** \`${domHint}\``);
|
|
9712
|
+
}
|
|
9713
|
+
}
|
|
9714
|
+
lines.push("");
|
|
9715
|
+
}
|
|
9716
|
+
return lines.join("\n");
|
|
9717
|
+
}
|
|
9718
|
+
function formatSingleAnnotationPreview(annotation, detail, pageUrl) {
|
|
9719
|
+
const formatted = formatAnnotations([annotation], detail, pageUrl);
|
|
9720
|
+
const headingIndex = formatted.indexOf("### 1.");
|
|
9721
|
+
return headingIndex >= 0 ? formatted.slice(headingIndex).trim() : formatted.trim();
|
|
9722
|
+
}
|
|
9723
|
+
const ANNOTATOR_ROOT_ATTR = "data-vpg-annotator-root";
|
|
9724
|
+
const ANNOTATOR_STYLES = `
|
|
9725
|
+
[${ANNOTATOR_ROOT_ATTR}] {
|
|
9726
|
+
--vpg-annotator-accent: #4f46e5;
|
|
9727
|
+
--vpg-annotator-accent-strong: #4338ca;
|
|
9728
|
+
--vpg-annotator-bg: rgba(15, 23, 42, 0.96);
|
|
9729
|
+
--vpg-annotator-bg-soft: rgba(30, 41, 59, 0.96);
|
|
9730
|
+
--vpg-annotator-border: rgba(148, 163, 184, 0.28);
|
|
9731
|
+
--vpg-annotator-text: #e2e8f0;
|
|
9732
|
+
--vpg-annotator-text-soft: #94a3b8;
|
|
9733
|
+
--vpg-annotator-shadow: 0 16px 40px rgba(15, 23, 42, 0.26);
|
|
9734
|
+
--vpg-annotator-radius: 0px;
|
|
9735
|
+
--vpg-annotator-edge-offset: 20px;
|
|
9736
|
+
color: var(--vpg-annotator-text);
|
|
9737
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
9738
|
+
}
|
|
9739
|
+
|
|
9740
|
+
[${ANNOTATOR_ROOT_ATTR}],
|
|
9741
|
+
[${ANNOTATOR_ROOT_ATTR}] *,
|
|
9742
|
+
[${ANNOTATOR_ROOT_ATTR}] *::before,
|
|
9743
|
+
[${ANNOTATOR_ROOT_ATTR}] *::after {
|
|
9744
|
+
box-sizing: border-box;
|
|
9745
|
+
}
|
|
9746
|
+
|
|
9747
|
+
.vpg-annotator-layer {
|
|
9748
|
+
position: fixed;
|
|
9749
|
+
inset: 0;
|
|
9750
|
+
pointer-events: none;
|
|
9751
|
+
}
|
|
9752
|
+
|
|
9753
|
+
.vpg-annotator-layer--markers {
|
|
9754
|
+
z-index: 2147483646;
|
|
9755
|
+
}
|
|
9756
|
+
|
|
9757
|
+
.vpg-annotator-layer--panels {
|
|
9758
|
+
z-index: 2147483647;
|
|
9759
|
+
}
|
|
9760
|
+
|
|
9761
|
+
.vpg-annotator-toolbar,
|
|
9762
|
+
.vpg-annotator-panel,
|
|
9763
|
+
.vpg-annotator-input,
|
|
9764
|
+
.vpg-annotator-settings,
|
|
9765
|
+
.vpg-annotator-toast {
|
|
9766
|
+
background: var(--vpg-annotator-bg);
|
|
9767
|
+
border: 1px solid var(--vpg-annotator-border);
|
|
9768
|
+
box-shadow: var(--vpg-annotator-shadow);
|
|
9769
|
+
backdrop-filter: blur(18px);
|
|
9770
|
+
}
|
|
9771
|
+
|
|
9772
|
+
.vpg-annotator-toolbar {
|
|
9773
|
+
position: fixed;
|
|
9774
|
+
right: var(--vpg-annotator-edge-offset);
|
|
9775
|
+
bottom: var(--vpg-annotator-edge-offset);
|
|
9776
|
+
z-index: 2147483647;
|
|
9777
|
+
display: flex;
|
|
9778
|
+
gap: 8px;
|
|
9779
|
+
align-items: center;
|
|
9780
|
+
padding: 8px;
|
|
9781
|
+
border-radius: var(--vpg-annotator-radius);
|
|
9782
|
+
user-select: none;
|
|
9783
|
+
}
|
|
9784
|
+
|
|
9785
|
+
.vpg-annotator-toolbar-handle,
|
|
9786
|
+
.vpg-annotator-btn {
|
|
9787
|
+
appearance: none;
|
|
9788
|
+
display: inline-flex;
|
|
9789
|
+
align-items: center;
|
|
9790
|
+
justify-content: center;
|
|
9791
|
+
min-width: 32px;
|
|
9792
|
+
min-height: 32px;
|
|
9793
|
+
padding: 7px 10px;
|
|
9794
|
+
border: 1px solid var(--vpg-annotator-border);
|
|
9795
|
+
border-radius: var(--vpg-annotator-radius);
|
|
9796
|
+
background: var(--vpg-annotator-bg-soft);
|
|
9797
|
+
color: var(--vpg-annotator-text);
|
|
9798
|
+
font-size: 12px;
|
|
9799
|
+
font-weight: 600;
|
|
9800
|
+
line-height: 1;
|
|
9801
|
+
}
|
|
9802
|
+
|
|
9803
|
+
.vpg-annotator-toolbar-handle {
|
|
9804
|
+
padding: 0;
|
|
9805
|
+
cursor: grab;
|
|
9806
|
+
touch-action: none;
|
|
9807
|
+
}
|
|
9808
|
+
|
|
9809
|
+
.vpg-annotator-toolbar--dragging,
|
|
9810
|
+
.vpg-annotator-toolbar--dragging * {
|
|
9811
|
+
cursor: grabbing !important;
|
|
9812
|
+
}
|
|
9813
|
+
|
|
9814
|
+
.vpg-annotator-btn {
|
|
9815
|
+
cursor: pointer;
|
|
9816
|
+
}
|
|
9817
|
+
|
|
9818
|
+
.vpg-annotator-btn--icon {
|
|
9819
|
+
width: 32px;
|
|
9820
|
+
padding: 0;
|
|
9821
|
+
}
|
|
9822
|
+
|
|
9823
|
+
.vpg-annotator-btn:hover,
|
|
9824
|
+
.vpg-annotator-toolbar-handle:hover {
|
|
9825
|
+
border-color: rgba(99, 102, 241, 0.45);
|
|
9826
|
+
}
|
|
9827
|
+
|
|
9828
|
+
.vpg-annotator-btn:disabled {
|
|
9829
|
+
opacity: 0.45;
|
|
9830
|
+
cursor: not-allowed;
|
|
9831
|
+
}
|
|
9832
|
+
|
|
9833
|
+
.vpg-annotator-btn--primary,
|
|
9834
|
+
.vpg-annotator-btn[aria-pressed="true"] {
|
|
9835
|
+
background: var(--vpg-annotator-accent);
|
|
9836
|
+
border-color: var(--vpg-annotator-accent);
|
|
9837
|
+
color: white;
|
|
9838
|
+
}
|
|
9839
|
+
|
|
9840
|
+
.vpg-annotator-icon {
|
|
9841
|
+
width: 16px;
|
|
9842
|
+
height: 16px;
|
|
9843
|
+
display: inline-flex;
|
|
9844
|
+
align-items: center;
|
|
9845
|
+
justify-content: center;
|
|
9846
|
+
}
|
|
9847
|
+
|
|
9848
|
+
.vpg-annotator-icon svg {
|
|
9849
|
+
width: 100%;
|
|
9850
|
+
height: 100%;
|
|
9851
|
+
display: block;
|
|
9852
|
+
fill: none;
|
|
9853
|
+
stroke: currentColor;
|
|
9854
|
+
stroke-width: 1.5;
|
|
9855
|
+
stroke-linecap: round;
|
|
9856
|
+
stroke-linejoin: round;
|
|
9857
|
+
}
|
|
9858
|
+
|
|
9859
|
+
.vpg-annotator-count {
|
|
9860
|
+
padding-left: 4px;
|
|
9861
|
+
white-space: nowrap;
|
|
9862
|
+
}
|
|
9863
|
+
|
|
9864
|
+
.vpg-annotator-panel,
|
|
9865
|
+
.vpg-annotator-input,
|
|
9866
|
+
.vpg-annotator-settings {
|
|
9867
|
+
position: fixed;
|
|
9868
|
+
z-index: 2147483647;
|
|
9869
|
+
min-width: 300px;
|
|
9870
|
+
max-width: min(560px, calc(100vw - 24px));
|
|
9871
|
+
border-radius: var(--vpg-annotator-radius);
|
|
9872
|
+
overflow: visible;
|
|
9873
|
+
pointer-events: auto;
|
|
9874
|
+
visibility: hidden;
|
|
9875
|
+
}
|
|
9876
|
+
|
|
9877
|
+
.vpg-annotator-settings {
|
|
9878
|
+
min-width: 260px;
|
|
9879
|
+
max-width: min(320px, calc(100vw - 24px));
|
|
9880
|
+
}
|
|
9881
|
+
|
|
9882
|
+
.vpg-annotator-arrow {
|
|
9883
|
+
position: absolute;
|
|
9884
|
+
z-index: 0;
|
|
9885
|
+
width: 14px;
|
|
9886
|
+
height: 14px;
|
|
9887
|
+
background: var(--vpg-annotator-bg);
|
|
9888
|
+
border: 1px solid var(--vpg-annotator-border);
|
|
9889
|
+
transform: rotate(45deg);
|
|
9890
|
+
}
|
|
9891
|
+
|
|
9892
|
+
.vpg-annotator-panel[data-placement^="top"] .vpg-annotator-arrow,
|
|
9893
|
+
.vpg-annotator-input[data-placement^="top"] .vpg-annotator-arrow,
|
|
9894
|
+
.vpg-annotator-settings[data-placement^="top"] .vpg-annotator-arrow {
|
|
9895
|
+
border-top: none;
|
|
9896
|
+
border-left: none;
|
|
9897
|
+
}
|
|
9898
|
+
|
|
9899
|
+
.vpg-annotator-panel[data-placement^="bottom"] .vpg-annotator-arrow,
|
|
9900
|
+
.vpg-annotator-input[data-placement^="bottom"] .vpg-annotator-arrow,
|
|
9901
|
+
.vpg-annotator-settings[data-placement^="bottom"] .vpg-annotator-arrow {
|
|
9902
|
+
border-right: none;
|
|
9903
|
+
border-bottom: none;
|
|
9904
|
+
}
|
|
9905
|
+
|
|
9906
|
+
.vpg-annotator-panel[data-placement^="left"] .vpg-annotator-arrow,
|
|
9907
|
+
.vpg-annotator-input[data-placement^="left"] .vpg-annotator-arrow,
|
|
9908
|
+
.vpg-annotator-settings[data-placement^="left"] .vpg-annotator-arrow {
|
|
9909
|
+
border-left: none;
|
|
9910
|
+
border-bottom: none;
|
|
9911
|
+
}
|
|
9912
|
+
|
|
9913
|
+
.vpg-annotator-panel[data-placement^="right"] .vpg-annotator-arrow,
|
|
9914
|
+
.vpg-annotator-input[data-placement^="right"] .vpg-annotator-arrow,
|
|
9915
|
+
.vpg-annotator-settings[data-placement^="right"] .vpg-annotator-arrow {
|
|
9916
|
+
border-top: none;
|
|
9917
|
+
border-right: none;
|
|
9918
|
+
}
|
|
9919
|
+
|
|
9920
|
+
.vpg-annotator-panel-body,
|
|
9921
|
+
.vpg-annotator-input-body,
|
|
9922
|
+
.vpg-annotator-settings-body {
|
|
9923
|
+
position: relative;
|
|
9924
|
+
z-index: 1;
|
|
9925
|
+
padding: 14px;
|
|
9926
|
+
}
|
|
9927
|
+
|
|
9928
|
+
.vpg-annotator-heading {
|
|
9929
|
+
margin: 0 0 6px;
|
|
9930
|
+
font-size: 13px;
|
|
9931
|
+
font-weight: 700;
|
|
9932
|
+
}
|
|
9933
|
+
|
|
9934
|
+
.vpg-annotator-subtle {
|
|
9935
|
+
margin: 0;
|
|
9936
|
+
font-size: 12px;
|
|
9937
|
+
color: var(--vpg-annotator-text-soft);
|
|
9938
|
+
}
|
|
9939
|
+
|
|
9940
|
+
.vpg-annotator-textarea,
|
|
9941
|
+
.vpg-annotator-comment,
|
|
9942
|
+
.vpg-annotator-select {
|
|
9943
|
+
width: 100%;
|
|
9944
|
+
border: 1px solid var(--vpg-annotator-border);
|
|
9945
|
+
border-radius: var(--vpg-annotator-radius);
|
|
9946
|
+
background: rgba(15, 23, 42, 0.72);
|
|
9947
|
+
color: var(--vpg-annotator-text);
|
|
9948
|
+
}
|
|
9949
|
+
|
|
9950
|
+
.vpg-annotator-textarea,
|
|
9951
|
+
.vpg-annotator-comment {
|
|
9952
|
+
padding: 10px 12px;
|
|
9953
|
+
font-size: 12px;
|
|
9954
|
+
line-height: 1.55;
|
|
9955
|
+
}
|
|
9956
|
+
|
|
9957
|
+
.vpg-annotator-textarea {
|
|
9958
|
+
min-height: 220px;
|
|
9959
|
+
margin-top: 12px;
|
|
9960
|
+
resize: vertical;
|
|
9961
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
9962
|
+
}
|
|
9963
|
+
|
|
9964
|
+
.vpg-annotator-comment {
|
|
9965
|
+
min-height: 92px;
|
|
9966
|
+
resize: vertical;
|
|
9967
|
+
font-family: inherit;
|
|
9968
|
+
}
|
|
9969
|
+
|
|
9970
|
+
.vpg-annotator-input-meta {
|
|
9971
|
+
min-height: 160px;
|
|
9972
|
+
margin-bottom: 10px;
|
|
9973
|
+
}
|
|
9974
|
+
|
|
9975
|
+
.vpg-annotator-row {
|
|
9976
|
+
display: flex;
|
|
9977
|
+
align-items: center;
|
|
9978
|
+
justify-content: space-between;
|
|
9979
|
+
gap: 12px;
|
|
9980
|
+
margin-top: 12px;
|
|
9981
|
+
}
|
|
9982
|
+
|
|
9983
|
+
.vpg-annotator-actions {
|
|
9984
|
+
display: flex;
|
|
9985
|
+
gap: 8px;
|
|
9986
|
+
justify-content: flex-end;
|
|
9987
|
+
margin-top: 12px;
|
|
9988
|
+
}
|
|
9989
|
+
|
|
9990
|
+
.vpg-annotator-field {
|
|
9991
|
+
display: grid;
|
|
9992
|
+
gap: 6px;
|
|
9993
|
+
margin-top: 12px;
|
|
9994
|
+
}
|
|
9995
|
+
|
|
9996
|
+
.vpg-annotator-shortcuts {
|
|
9997
|
+
display: grid;
|
|
9998
|
+
gap: 8px;
|
|
9999
|
+
}
|
|
10000
|
+
|
|
10001
|
+
.vpg-annotator-shortcut-row {
|
|
10002
|
+
display: flex;
|
|
10003
|
+
align-items: center;
|
|
10004
|
+
justify-content: space-between;
|
|
10005
|
+
gap: 12px;
|
|
10006
|
+
}
|
|
10007
|
+
|
|
10008
|
+
.vpg-annotator-label {
|
|
10009
|
+
font-size: 12px;
|
|
10010
|
+
font-weight: 600;
|
|
10011
|
+
color: var(--vpg-annotator-text-soft);
|
|
10012
|
+
}
|
|
10013
|
+
|
|
10014
|
+
.vpg-annotator-select,
|
|
10015
|
+
.vpg-annotator-checkbox {
|
|
10016
|
+
accent-color: var(--vpg-annotator-accent);
|
|
10017
|
+
}
|
|
10018
|
+
|
|
10019
|
+
.vpg-annotator-select {
|
|
10020
|
+
padding: 8px 10px;
|
|
10021
|
+
}
|
|
10022
|
+
|
|
10023
|
+
.vpg-annotator-kbd {
|
|
10024
|
+
min-width: 32px;
|
|
10025
|
+
padding: 4px 8px;
|
|
10026
|
+
border: 1px solid var(--vpg-annotator-border);
|
|
10027
|
+
border-radius: var(--vpg-annotator-radius);
|
|
10028
|
+
background: rgba(15, 23, 42, 0.72);
|
|
10029
|
+
color: var(--vpg-annotator-text);
|
|
10030
|
+
font: inherit;
|
|
10031
|
+
font-size: 11px;
|
|
10032
|
+
font-weight: 700;
|
|
10033
|
+
line-height: 1;
|
|
10034
|
+
text-align: center;
|
|
10035
|
+
}
|
|
10036
|
+
|
|
10037
|
+
.vpg-annotator-highlight {
|
|
10038
|
+
position: fixed;
|
|
10039
|
+
z-index: 2147483646;
|
|
10040
|
+
pointer-events: none;
|
|
10041
|
+
border: 2px solid var(--vpg-annotator-accent);
|
|
10042
|
+
border-radius: var(--vpg-annotator-radius);
|
|
10043
|
+
background: rgba(79, 70, 229, 0.08);
|
|
10044
|
+
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
|
|
10045
|
+
}
|
|
10046
|
+
|
|
10047
|
+
.vpg-annotator-highlight-label {
|
|
10048
|
+
position: absolute;
|
|
10049
|
+
left: 0;
|
|
10050
|
+
top: -30px;
|
|
10051
|
+
max-width: min(520px, calc(100vw - 24px));
|
|
10052
|
+
overflow: hidden;
|
|
10053
|
+
text-overflow: ellipsis;
|
|
10054
|
+
white-space: nowrap;
|
|
10055
|
+
border-radius: var(--vpg-annotator-radius);
|
|
10056
|
+
background: var(--vpg-annotator-accent);
|
|
10057
|
+
color: white;
|
|
10058
|
+
padding: 5px 10px;
|
|
10059
|
+
font-size: 12px;
|
|
10060
|
+
font-weight: 700;
|
|
10061
|
+
}
|
|
10062
|
+
|
|
10063
|
+
.vpg-annotator-shield {
|
|
10064
|
+
position: fixed;
|
|
10065
|
+
inset: 0;
|
|
10066
|
+
z-index: 2147483645;
|
|
10067
|
+
background: transparent;
|
|
10068
|
+
}
|
|
10069
|
+
|
|
10070
|
+
.vpg-annotator-marker {
|
|
10071
|
+
position: absolute;
|
|
10072
|
+
z-index: 2147483646;
|
|
10073
|
+
display: flex;
|
|
10074
|
+
align-items: center;
|
|
10075
|
+
justify-content: center;
|
|
10076
|
+
width: 24px;
|
|
10077
|
+
height: 24px;
|
|
10078
|
+
pointer-events: auto;
|
|
10079
|
+
border: none;
|
|
10080
|
+
border-radius: var(--vpg-annotator-radius);
|
|
10081
|
+
background: var(--vpg-annotator-accent);
|
|
10082
|
+
color: white;
|
|
10083
|
+
font-size: 12px;
|
|
10084
|
+
font-weight: 700;
|
|
10085
|
+
cursor: pointer;
|
|
10086
|
+
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.28);
|
|
10087
|
+
}
|
|
10088
|
+
|
|
10089
|
+
.vpg-annotator-toast {
|
|
10090
|
+
position: fixed;
|
|
10091
|
+
right: 24px;
|
|
10092
|
+
bottom: 86px;
|
|
10093
|
+
z-index: 2147483647;
|
|
10094
|
+
padding: 8px 12px;
|
|
10095
|
+
border-radius: var(--vpg-annotator-radius);
|
|
10096
|
+
color: var(--vpg-annotator-text);
|
|
10097
|
+
font-size: 12px;
|
|
10098
|
+
font-weight: 600;
|
|
10099
|
+
}
|
|
10100
|
+
`;
|
|
10101
|
+
const ignoredComponentNamePattern = /^(?:items\[\d+\]\.template|template|anonymous|slot|transition|transition-group)$/i;
|
|
10102
|
+
function getDirectVueInstances(element) {
|
|
10103
|
+
const instances = [];
|
|
10104
|
+
const vue2Instance = element.__vue__;
|
|
10105
|
+
const vue3Instance = element.__vueParentComponent;
|
|
10106
|
+
if (vue2Instance) {
|
|
10107
|
+
instances.push(vue2Instance);
|
|
10108
|
+
}
|
|
10109
|
+
if (vue3Instance && vue3Instance !== vue2Instance) {
|
|
10110
|
+
instances.push(vue3Instance);
|
|
10111
|
+
}
|
|
10112
|
+
return instances;
|
|
10113
|
+
}
|
|
10114
|
+
function stripSourcePosition(sourcePath) {
|
|
10115
|
+
return sourcePath.replace(/:\d+:\d+$/, "");
|
|
10116
|
+
}
|
|
10117
|
+
function inferNameFromFile(filePath) {
|
|
10118
|
+
const fileName = stripSourcePosition(filePath).split("/").pop();
|
|
10119
|
+
if (!fileName) {
|
|
10120
|
+
return null;
|
|
10121
|
+
}
|
|
10122
|
+
return fileName.replace(/\.vue$/, "");
|
|
10123
|
+
}
|
|
10124
|
+
function isMeaningfulComponentName(name) {
|
|
10125
|
+
return !!name && !ignoredComponentNamePattern.test(name.trim());
|
|
10126
|
+
}
|
|
10127
|
+
function isComponentLikeSourceTag(tag) {
|
|
10128
|
+
return !!tag && (/[A-Z]/.test(tag.trim()) || tag.includes("-"));
|
|
10129
|
+
}
|
|
10130
|
+
function formatSourceLabel(sourcePath, includePosition = true) {
|
|
10131
|
+
const match = sourcePath.match(/^(.*?)(?::(\d+):(\d+))?$/);
|
|
10132
|
+
if (!match) {
|
|
10133
|
+
return sourcePath;
|
|
10134
|
+
}
|
|
10135
|
+
const [, rawPath, line, column] = match;
|
|
10136
|
+
const frontendPathMatch = rawPath.match(/(?:^|\/)frontend\/(src\/.*)$/);
|
|
10137
|
+
const normalizedPath = frontendPathMatch?.[1] ?? rawPath;
|
|
10138
|
+
if (includePosition && line && column) {
|
|
10139
|
+
return `${normalizedPath}:${line}:${column}`;
|
|
10140
|
+
}
|
|
10141
|
+
return normalizedPath;
|
|
10142
|
+
}
|
|
10143
|
+
function getStringPropValue(props, key) {
|
|
10144
|
+
const value = props?.[key];
|
|
10145
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
10146
|
+
}
|
|
10147
|
+
function getDisplayComponentName(componentName, sourcePath, sourceTag) {
|
|
10148
|
+
if (isComponentLikeSourceTag(sourceTag)) {
|
|
10149
|
+
return sourceTag.trim();
|
|
10150
|
+
}
|
|
10151
|
+
const inferredName = sourcePath ? inferNameFromFile(sourcePath) : null;
|
|
10152
|
+
if (isMeaningfulComponentName(inferredName)) {
|
|
10153
|
+
return inferredName;
|
|
10154
|
+
}
|
|
10155
|
+
if (isMeaningfulComponentName(componentName)) {
|
|
10156
|
+
return componentName.trim();
|
|
10157
|
+
}
|
|
10158
|
+
return void 0;
|
|
10159
|
+
}
|
|
10160
|
+
function resolveAnnotatedInfo(componentName, sourcePath, sourceTag) {
|
|
10161
|
+
const component = getDisplayComponentName(componentName, sourcePath, sourceTag);
|
|
10162
|
+
const source = sourcePath ? formatSourceLabel(sourcePath, true) : void 0;
|
|
10163
|
+
if (!component && !source) {
|
|
10164
|
+
return void 0;
|
|
10165
|
+
}
|
|
10166
|
+
return {
|
|
10167
|
+
component,
|
|
10168
|
+
source,
|
|
10169
|
+
formatted: component ? source ? `${component} (${source})` : component : source
|
|
10170
|
+
};
|
|
10171
|
+
}
|
|
10172
|
+
function getAnnotatedInfoFromElement(element, options) {
|
|
10173
|
+
const pomComponentAttribute = `${options.metadataAttributePrefix}-component`;
|
|
10174
|
+
const pomTagAttribute = `${options.metadataAttributePrefix}-tag`;
|
|
10175
|
+
const annotatedElement = element.closest(`[${pomComponentAttribute}], [${options.sourceAttribute}]`);
|
|
10176
|
+
if (!annotatedElement) {
|
|
10177
|
+
return void 0;
|
|
10178
|
+
}
|
|
10179
|
+
return resolveAnnotatedInfo(
|
|
10180
|
+
annotatedElement.getAttribute(pomComponentAttribute),
|
|
10181
|
+
annotatedElement.getAttribute(options.sourceAttribute) ?? void 0,
|
|
10182
|
+
annotatedElement.getAttribute(pomTagAttribute)
|
|
10183
|
+
);
|
|
10184
|
+
}
|
|
10185
|
+
function getAnnotatedInfoFromInstance(instance, options) {
|
|
10186
|
+
const pomComponentAttribute = `${options.metadataAttributePrefix}-component`;
|
|
10187
|
+
const pomTagAttribute = `${options.metadataAttributePrefix}-tag`;
|
|
10188
|
+
return resolveAnnotatedInfo(
|
|
10189
|
+
getStringPropValue(instance.vnode?.props, pomComponentAttribute),
|
|
10190
|
+
getStringPropValue(instance.vnode?.props, options.sourceAttribute),
|
|
10191
|
+
getStringPropValue(instance.vnode?.props, pomTagAttribute)
|
|
10192
|
+
);
|
|
10193
|
+
}
|
|
10194
|
+
function getInstanceName(instance) {
|
|
10195
|
+
if (instance.$options) {
|
|
10196
|
+
const name = instance.$options.name ?? instance.$options._componentTag ?? inferNameFromFile(instance.$options.__file ?? "");
|
|
10197
|
+
if (isMeaningfulComponentName(name)) {
|
|
10198
|
+
return {
|
|
10199
|
+
component: name,
|
|
10200
|
+
source: instance.$options.__file ? formatSourceLabel(instance.$options.__file, true) : void 0,
|
|
10201
|
+
formatted: instance.$options.__file ? `${name} (${formatSourceLabel(instance.$options.__file, true)})` : name
|
|
10202
|
+
};
|
|
10203
|
+
}
|
|
10204
|
+
}
|
|
10205
|
+
if (instance.type) {
|
|
10206
|
+
const name = instance.type.name ?? instance.type.__name ?? inferNameFromFile(instance.type.__file ?? "");
|
|
10207
|
+
if (isMeaningfulComponentName(name)) {
|
|
10208
|
+
return {
|
|
10209
|
+
component: name,
|
|
10210
|
+
source: instance.type.__file ? formatSourceLabel(instance.type.__file, true) : void 0,
|
|
10211
|
+
formatted: instance.type.__file ? `${name} (${formatSourceLabel(instance.type.__file, true)})` : name
|
|
10212
|
+
};
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
return void 0;
|
|
10216
|
+
}
|
|
10217
|
+
function scoreInfo(info) {
|
|
10218
|
+
if (!info) {
|
|
10219
|
+
return -1;
|
|
10220
|
+
}
|
|
10221
|
+
return (info.component ? 10 : 0) + (info.source ? 100 : 0);
|
|
10222
|
+
}
|
|
10223
|
+
function resolveVueComponentInfo(element, options) {
|
|
10224
|
+
const annotatedElementInfo = getAnnotatedInfoFromElement(element, options);
|
|
10225
|
+
if (annotatedElementInfo) {
|
|
10226
|
+
return annotatedElementInfo;
|
|
10227
|
+
}
|
|
10228
|
+
const seenInstances = /* @__PURE__ */ new Set();
|
|
10229
|
+
let bestInfo;
|
|
10230
|
+
let bestScore = -1;
|
|
10231
|
+
let current = element;
|
|
10232
|
+
let depth = 0;
|
|
10233
|
+
while (current && current !== document.body && depth < 50) {
|
|
10234
|
+
for (const instance of getDirectVueInstances(current)) {
|
|
10235
|
+
if (seenInstances.has(instance)) {
|
|
10236
|
+
continue;
|
|
10237
|
+
}
|
|
10238
|
+
seenInstances.add(instance);
|
|
10239
|
+
const annotatedInstanceInfo = getAnnotatedInfoFromInstance(instance, options);
|
|
10240
|
+
const candidate = annotatedInstanceInfo ?? getInstanceName(instance);
|
|
10241
|
+
const score = scoreInfo(candidate);
|
|
10242
|
+
if (score > bestScore) {
|
|
10243
|
+
bestInfo = candidate;
|
|
10244
|
+
bestScore = score;
|
|
10245
|
+
}
|
|
10246
|
+
}
|
|
10247
|
+
current = current.parentElement;
|
|
10248
|
+
depth += 1;
|
|
10249
|
+
}
|
|
10250
|
+
return bestInfo;
|
|
10251
|
+
}
|
|
10252
|
+
const SETTINGS_STORAGE_KEY = "vpg-annotator-settings";
|
|
10253
|
+
const ANNOTATIONS_STORAGE_KEY = "vpg-annotator-annotations";
|
|
10254
|
+
const TOOLBAR_POSITION_STORAGE_KEY = "vpg-annotator-toolbar-position";
|
|
10255
|
+
const RUNTIME_GUARD = "__VUE_POM_GENERATOR_ANNOTATOR_RUNTIME__";
|
|
10256
|
+
const TOOLBAR_ICON_MARKUP = {
|
|
10257
|
+
drag: '<svg viewBox="0 0 16 16"><path d="M5 3h1v1H5zM10 3h1v1h-1zM5 7h1v1H5zM10 7h1v1h-1zM5 11h1v1H5zM10 11h1v1h-1z" fill="currentColor" stroke="none" /></svg>',
|
|
10258
|
+
inspect: '<svg viewBox="0 0 16 16"><path d="M8 2v3M8 11v3M2 8h3M11 8h3M4 4l2 2M10 10l2 2M12 4l-2 2M6 10l-2 2"/><circle cx="8" cy="8" r="2.5"/></svg>',
|
|
10259
|
+
preview: '<svg viewBox="0 0 16 16"><path d="M1.5 8s2.4-4 6.5-4 6.5 4 6.5 4-2.4 4-6.5 4-6.5-4-6.5-4Z"/><circle cx="8" cy="8" r="1.8"/></svg>',
|
|
10260
|
+
copy: '<svg viewBox="0 0 16 16"><path d="M6 2.5h6.5v9H6z"/><path d="M3.5 5.5H5v6.5h5.5v1.5h-7z"/></svg>',
|
|
10261
|
+
clear: '<svg viewBox="0 0 16 16"><path d="M2.5 4.5h11"/><path d="M6 2.5h4"/><path d="M5 4.5v8"/><path d="M8 4.5v8"/><path d="M11 4.5v8"/><path d="M4 4.5h8l-.6 9H4.6z"/></svg>',
|
|
10262
|
+
settings: '<svg viewBox="0 0 16 16"><path d="M8 2.2l1 .6 1.2-.2.8 1 .9.7-.3 1.2.5 1-.5 1 .3 1.2-.9.7-.8 1-1.2-.2-1 .6-1-.6-1.2.2-.8-1-.9-.7.3-1.2-.5-1 .5-1-.3-1.2.9-.7.8-1 1.2.2z"/><circle cx="8" cy="8" r="2.2"/></svg>'
|
|
10263
|
+
};
|
|
10264
|
+
const SHORTCUT_LABELS = {
|
|
10265
|
+
select: "S",
|
|
10266
|
+
preview: "P",
|
|
10267
|
+
copy: "C",
|
|
10268
|
+
clear: "X",
|
|
10269
|
+
settings: ",",
|
|
10270
|
+
cancel: "Esc"
|
|
10271
|
+
};
|
|
10272
|
+
function normalizeText(value) {
|
|
10273
|
+
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
10274
|
+
return normalized || void 0;
|
|
10275
|
+
}
|
|
10276
|
+
function formatShortcutTitle(label, shortcut) {
|
|
10277
|
+
return shortcut ? `${label} (${shortcut})` : label;
|
|
10278
|
+
}
|
|
10279
|
+
function isInsideAnnotatorTree(node) {
|
|
10280
|
+
return node instanceof Element && !!node.closest(`[${ANNOTATOR_ROOT_ATTR}]`);
|
|
10281
|
+
}
|
|
10282
|
+
function isEditableTarget(node) {
|
|
10283
|
+
if (!(node instanceof Element)) {
|
|
10284
|
+
return false;
|
|
10285
|
+
}
|
|
10286
|
+
if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement || node instanceof HTMLSelectElement) {
|
|
10287
|
+
return true;
|
|
10288
|
+
}
|
|
10289
|
+
return node.closest('[contenteditable=""], [contenteditable="true"]') !== null;
|
|
10290
|
+
}
|
|
10291
|
+
function getElementSummary(element) {
|
|
10292
|
+
const testId = element.getAttribute("data-testid");
|
|
10293
|
+
if (testId) {
|
|
10294
|
+
return `[data-testid="${testId}"]`;
|
|
10295
|
+
}
|
|
10296
|
+
const id = element.getAttribute("id");
|
|
10297
|
+
if (id) {
|
|
10298
|
+
return `${element.tagName.toLowerCase()}#${id}`;
|
|
10299
|
+
}
|
|
10300
|
+
const classes = Array.from(element.classList).slice(0, 2).join(".");
|
|
10301
|
+
return classes ? `${element.tagName.toLowerCase()}.${classes}` : element.tagName.toLowerCase();
|
|
10302
|
+
}
|
|
10303
|
+
function getElementPath(element) {
|
|
10304
|
+
const segments = [];
|
|
10305
|
+
let current = element;
|
|
10306
|
+
let depth = 0;
|
|
10307
|
+
while (current && current !== document.body && depth < 8) {
|
|
10308
|
+
const summary = getElementSummary(current);
|
|
10309
|
+
segments.unshift(summary);
|
|
10310
|
+
current = current.parentElement;
|
|
10311
|
+
depth += 1;
|
|
10312
|
+
}
|
|
10313
|
+
return segments.join(" > ");
|
|
10314
|
+
}
|
|
10315
|
+
function getNearbyText(element) {
|
|
10316
|
+
const ownText = normalizeText(element.textContent || void 0);
|
|
10317
|
+
if (ownText && ownText.length >= 2) {
|
|
10318
|
+
return ownText.length > 160 ? `${ownText.slice(0, 160)}...` : ownText;
|
|
10319
|
+
}
|
|
10320
|
+
const parentText = normalizeText(element.parentElement?.textContent || void 0);
|
|
10321
|
+
if (parentText) {
|
|
10322
|
+
return parentText.length > 160 ? `${parentText.slice(0, 160)}...` : parentText;
|
|
10323
|
+
}
|
|
10324
|
+
return void 0;
|
|
10325
|
+
}
|
|
10326
|
+
function getNearbyLocator(element) {
|
|
10327
|
+
const testId = element.getAttribute("data-testid") || element.getAttribute("data-v-pom-testid");
|
|
10328
|
+
if (testId) {
|
|
10329
|
+
return `near \`[data-testid="${testId}"]\``;
|
|
10330
|
+
}
|
|
10331
|
+
const siblings = Array.from(element.parentElement?.children || []).filter((sibling) => sibling !== element).slice(0, 3).map(getElementSummary);
|
|
10332
|
+
return siblings.length ? `near ${siblings.map((value) => `\`${value}\``).join(", ")}` : void 0;
|
|
10333
|
+
}
|
|
10334
|
+
function createFloatingArrow() {
|
|
10335
|
+
const arrowEl = document.createElement("div");
|
|
10336
|
+
arrowEl.className = "vpg-annotator-arrow";
|
|
10337
|
+
arrowEl.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10338
|
+
return arrowEl;
|
|
10339
|
+
}
|
|
10340
|
+
function toCssPixels(value) {
|
|
10341
|
+
return `${Math.round(value)}px`;
|
|
10342
|
+
}
|
|
10343
|
+
function clamp(value, min, max) {
|
|
10344
|
+
return Math.min(Math.max(value, min), max);
|
|
10345
|
+
}
|
|
10346
|
+
function createButtonBase(label, onClick, options = {}) {
|
|
10347
|
+
const button = document.createElement("button");
|
|
10348
|
+
button.type = "button";
|
|
10349
|
+
button.className = `vpg-annotator-btn${options.primary ? " vpg-annotator-btn--primary" : ""}`;
|
|
10350
|
+
button.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10351
|
+
button.setAttribute("aria-label", label);
|
|
10352
|
+
button.title = formatShortcutTitle(label, options.shortcut);
|
|
10353
|
+
if (options.shortcut) {
|
|
10354
|
+
button.setAttribute("aria-keyshortcuts", options.shortcut);
|
|
10355
|
+
}
|
|
10356
|
+
if (options.pressed) {
|
|
10357
|
+
button.setAttribute("aria-pressed", "true");
|
|
10358
|
+
}
|
|
10359
|
+
if (options.disabled) {
|
|
10360
|
+
button.disabled = true;
|
|
10361
|
+
}
|
|
10362
|
+
button.addEventListener("click", (event) => {
|
|
10363
|
+
event.preventDefault();
|
|
10364
|
+
event.stopPropagation();
|
|
10365
|
+
onClick();
|
|
10366
|
+
});
|
|
10367
|
+
return button;
|
|
10368
|
+
}
|
|
10369
|
+
function createIcon(iconName) {
|
|
10370
|
+
const icon = document.createElement("span");
|
|
10371
|
+
icon.className = "vpg-annotator-icon";
|
|
10372
|
+
icon.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10373
|
+
icon.setAttribute("aria-hidden", "true");
|
|
10374
|
+
icon.innerHTML = TOOLBAR_ICON_MARKUP[iconName];
|
|
10375
|
+
return icon;
|
|
10376
|
+
}
|
|
10377
|
+
function createButton(label, onClick, options = {}) {
|
|
10378
|
+
const button = createButtonBase(label, onClick, options);
|
|
10379
|
+
button.textContent = label;
|
|
10380
|
+
return button;
|
|
10381
|
+
}
|
|
10382
|
+
function createIconButton(label, iconName, onClick, options = {}) {
|
|
10383
|
+
const button = createButtonBase(label, onClick, options);
|
|
10384
|
+
button.classList.add("vpg-annotator-btn--icon");
|
|
10385
|
+
button.appendChild(createIcon(iconName));
|
|
10386
|
+
return button;
|
|
10387
|
+
}
|
|
10388
|
+
function createToolbarHandle() {
|
|
10389
|
+
const handle = document.createElement("div");
|
|
10390
|
+
handle.className = "vpg-annotator-toolbar-handle";
|
|
10391
|
+
handle.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10392
|
+
handle.setAttribute("title", "Drag annotator toolbar");
|
|
10393
|
+
handle.setAttribute("aria-hidden", "true");
|
|
10394
|
+
handle.appendChild(createIcon("drag"));
|
|
10395
|
+
return handle;
|
|
10396
|
+
}
|
|
10397
|
+
async function copyTextToClipboard(text) {
|
|
10398
|
+
if (navigator.clipboard?.writeText) {
|
|
10399
|
+
await navigator.clipboard.writeText(text);
|
|
10400
|
+
return;
|
|
10401
|
+
}
|
|
10402
|
+
const textarea = document.createElement("textarea");
|
|
10403
|
+
textarea.value = text;
|
|
10404
|
+
textarea.setAttribute("readonly", "true");
|
|
10405
|
+
textarea.style.position = "fixed";
|
|
10406
|
+
textarea.style.top = "0";
|
|
10407
|
+
textarea.style.left = "0";
|
|
10408
|
+
textarea.style.opacity = "0";
|
|
10409
|
+
document.body.appendChild(textarea);
|
|
10410
|
+
textarea.select();
|
|
10411
|
+
const copied = document.execCommand("copy");
|
|
10412
|
+
textarea.remove();
|
|
10413
|
+
if (!copied) {
|
|
10414
|
+
throw new Error("Clipboard copy failed.");
|
|
10415
|
+
}
|
|
10416
|
+
}
|
|
10417
|
+
class AnnotatorRuntime {
|
|
10418
|
+
options;
|
|
10419
|
+
settings;
|
|
10420
|
+
annotations = [];
|
|
10421
|
+
hoveredElement = null;
|
|
10422
|
+
inspectMode = false;
|
|
10423
|
+
pendingAnnotation = null;
|
|
10424
|
+
editingAnnotationId = null;
|
|
10425
|
+
toolbarEl;
|
|
10426
|
+
markerLayerEl;
|
|
10427
|
+
panelLayerEl;
|
|
10428
|
+
highlightEl;
|
|
10429
|
+
highlightLabelEl;
|
|
10430
|
+
shieldEl;
|
|
10431
|
+
toastEl = null;
|
|
10432
|
+
previewButton = null;
|
|
10433
|
+
settingsButton = null;
|
|
10434
|
+
currentPanelCleanup = null;
|
|
10435
|
+
currentPanelEl = null;
|
|
10436
|
+
toastTimer = null;
|
|
10437
|
+
toolbarPosition = null;
|
|
10438
|
+
constructor(options) {
|
|
10439
|
+
this.options = options;
|
|
10440
|
+
this.settings = this.loadSettings();
|
|
10441
|
+
this.annotations = this.loadAnnotations();
|
|
10442
|
+
this.toolbarPosition = this.loadToolbarPosition();
|
|
10443
|
+
}
|
|
10444
|
+
mount() {
|
|
10445
|
+
this.ensureStyles();
|
|
10446
|
+
this.createChrome();
|
|
10447
|
+
this.renderToolbar();
|
|
10448
|
+
this.renderMarkers();
|
|
10449
|
+
this.attachGlobalListeners();
|
|
10450
|
+
}
|
|
10451
|
+
ensureStyles() {
|
|
10452
|
+
if (document.getElementById("vpg-annotator-styles")) {
|
|
10453
|
+
return;
|
|
10454
|
+
}
|
|
10455
|
+
const style = document.createElement("style");
|
|
10456
|
+
style.id = "vpg-annotator-styles";
|
|
10457
|
+
style.textContent = ANNOTATOR_STYLES;
|
|
10458
|
+
document.head.appendChild(style);
|
|
10459
|
+
}
|
|
10460
|
+
createChrome() {
|
|
10461
|
+
this.toolbarEl = document.createElement("div");
|
|
10462
|
+
this.toolbarEl.className = "vpg-annotator-toolbar";
|
|
10463
|
+
this.toolbarEl.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10464
|
+
this.markerLayerEl = document.createElement("div");
|
|
10465
|
+
this.markerLayerEl.className = "vpg-annotator-layer vpg-annotator-layer--markers";
|
|
10466
|
+
this.markerLayerEl.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10467
|
+
this.panelLayerEl = document.createElement("div");
|
|
10468
|
+
this.panelLayerEl.className = "vpg-annotator-layer vpg-annotator-layer--panels";
|
|
10469
|
+
this.panelLayerEl.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10470
|
+
this.highlightEl = document.createElement("div");
|
|
10471
|
+
this.highlightEl.className = "vpg-annotator-highlight";
|
|
10472
|
+
this.highlightEl.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10473
|
+
this.highlightEl.hidden = true;
|
|
10474
|
+
this.highlightLabelEl = document.createElement("div");
|
|
10475
|
+
this.highlightLabelEl.className = "vpg-annotator-highlight-label";
|
|
10476
|
+
this.highlightLabelEl.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10477
|
+
this.highlightEl.appendChild(this.highlightLabelEl);
|
|
10478
|
+
this.panelLayerEl.appendChild(this.highlightEl);
|
|
10479
|
+
this.shieldEl = document.createElement("div");
|
|
10480
|
+
this.shieldEl.className = "vpg-annotator-shield";
|
|
10481
|
+
this.shieldEl.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10482
|
+
this.shieldEl.hidden = true;
|
|
10483
|
+
document.body.append(this.markerLayerEl, this.panelLayerEl, this.shieldEl, this.toolbarEl);
|
|
10484
|
+
}
|
|
10485
|
+
renderToolbar() {
|
|
10486
|
+
this.toolbarEl.replaceChildren();
|
|
10487
|
+
const handle = createToolbarHandle();
|
|
10488
|
+
this.attachToolbarDrag(handle);
|
|
10489
|
+
const count = document.createElement("span");
|
|
10490
|
+
count.className = "vpg-annotator-count vpg-annotator-subtle";
|
|
10491
|
+
count.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10492
|
+
count.textContent = `${this.annotations.length} annotation${this.annotations.length === 1 ? "" : "s"}`;
|
|
10493
|
+
const selectButton = createIconButton(this.inspectMode ? "Stop selecting" : "Select element", "inspect", () => {
|
|
10494
|
+
this.setInspectMode(!this.inspectMode);
|
|
10495
|
+
}, { primary: this.inspectMode, pressed: this.inspectMode, shortcut: SHORTCUT_LABELS.select });
|
|
10496
|
+
const previewButton = createIconButton("Preview annotations", "preview", () => this.openPreview(), {
|
|
10497
|
+
disabled: this.annotations.length === 0,
|
|
10498
|
+
shortcut: SHORTCUT_LABELS.preview
|
|
10499
|
+
});
|
|
10500
|
+
this.previewButton = previewButton;
|
|
10501
|
+
const copyButton = createIconButton("Copy annotations", "copy", () => this.copyAnnotations(), {
|
|
10502
|
+
disabled: this.annotations.length === 0,
|
|
10503
|
+
shortcut: SHORTCUT_LABELS.copy
|
|
10504
|
+
});
|
|
10505
|
+
const clearButton = createIconButton("Clear annotations", "clear", () => this.clearAnnotations(), {
|
|
10506
|
+
disabled: this.annotations.length === 0,
|
|
10507
|
+
shortcut: SHORTCUT_LABELS.clear
|
|
10508
|
+
});
|
|
10509
|
+
const settingsButton = createIconButton("Annotator settings", "settings", () => this.openSettings(), {
|
|
10510
|
+
shortcut: SHORTCUT_LABELS.settings
|
|
10511
|
+
});
|
|
10512
|
+
this.settingsButton = settingsButton;
|
|
10513
|
+
this.toolbarEl.append(handle, selectButton, previewButton, copyButton, clearButton, settingsButton, count);
|
|
10514
|
+
this.applyToolbarPosition();
|
|
10515
|
+
}
|
|
10516
|
+
renderMarkers() {
|
|
10517
|
+
this.markerLayerEl.replaceChildren();
|
|
10518
|
+
for (const [index, annotation] of this.annotations.entries()) {
|
|
10519
|
+
const marker = document.createElement("button");
|
|
10520
|
+
marker.type = "button";
|
|
10521
|
+
marker.className = "vpg-annotator-marker";
|
|
10522
|
+
marker.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10523
|
+
marker.textContent = String(index + 1);
|
|
10524
|
+
marker.style.left = toCssPixels(annotation.pageX);
|
|
10525
|
+
marker.style.top = toCssPixels(annotation.pageY - window.scrollY);
|
|
10526
|
+
marker.addEventListener("click", (event) => {
|
|
10527
|
+
event.preventDefault();
|
|
10528
|
+
event.stopPropagation();
|
|
10529
|
+
this.openInputForExistingAnnotation(annotation, marker);
|
|
10530
|
+
});
|
|
10531
|
+
this.markerLayerEl.appendChild(marker);
|
|
10532
|
+
}
|
|
10533
|
+
}
|
|
10534
|
+
attachGlobalListeners() {
|
|
10535
|
+
this.shieldEl.addEventListener("mousemove", (event) => this.onShieldMouseMove(event));
|
|
10536
|
+
this.shieldEl.addEventListener("click", (event) => this.onShieldClick(event));
|
|
10537
|
+
window.addEventListener("scroll", () => this.renderMarkers(), true);
|
|
10538
|
+
window.addEventListener("resize", () => {
|
|
10539
|
+
this.renderMarkers();
|
|
10540
|
+
this.applyToolbarPosition();
|
|
10541
|
+
});
|
|
10542
|
+
window.addEventListener("keydown", (event) => this.onWindowKeyDown(event), true);
|
|
10543
|
+
}
|
|
10544
|
+
loadSettings() {
|
|
10545
|
+
try {
|
|
10546
|
+
const raw = localStorage.getItem(SETTINGS_STORAGE_KEY);
|
|
10547
|
+
const parsed = raw ? JSON.parse(raw) : {};
|
|
10548
|
+
return {
|
|
10549
|
+
outputDetail: parsed.outputDetail ?? this.options.outputDetail,
|
|
10550
|
+
copyToClipboard: parsed.copyToClipboard ?? this.options.copyToClipboard,
|
|
10551
|
+
showComponentTree: parsed.showComponentTree ?? this.options.showComponentTree
|
|
10552
|
+
};
|
|
10553
|
+
} catch {
|
|
10554
|
+
return {
|
|
10555
|
+
outputDetail: this.options.outputDetail,
|
|
10556
|
+
copyToClipboard: this.options.copyToClipboard,
|
|
10557
|
+
showComponentTree: this.options.showComponentTree
|
|
10558
|
+
};
|
|
10559
|
+
}
|
|
10560
|
+
}
|
|
10561
|
+
saveSettings() {
|
|
10562
|
+
localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(this.settings));
|
|
10563
|
+
this.renderToolbar();
|
|
10564
|
+
}
|
|
10565
|
+
loadAnnotations() {
|
|
10566
|
+
try {
|
|
10567
|
+
const raw = sessionStorage.getItem(ANNOTATIONS_STORAGE_KEY);
|
|
10568
|
+
if (!raw) {
|
|
10569
|
+
return [];
|
|
10570
|
+
}
|
|
10571
|
+
const parsed = JSON.parse(raw);
|
|
10572
|
+
if (Array.isArray(parsed)) {
|
|
10573
|
+
return parsed;
|
|
10574
|
+
}
|
|
10575
|
+
return Array.isArray(parsed[window.location.href]) ? parsed[window.location.href] : [];
|
|
10576
|
+
} catch {
|
|
10577
|
+
return [];
|
|
10578
|
+
}
|
|
10579
|
+
}
|
|
10580
|
+
saveAnnotations() {
|
|
10581
|
+
const store = { [window.location.href]: this.annotations };
|
|
10582
|
+
sessionStorage.setItem(ANNOTATIONS_STORAGE_KEY, JSON.stringify(store));
|
|
10583
|
+
this.renderToolbar();
|
|
10584
|
+
this.renderMarkers();
|
|
10585
|
+
}
|
|
10586
|
+
loadToolbarPosition() {
|
|
10587
|
+
try {
|
|
10588
|
+
const raw = localStorage.getItem(TOOLBAR_POSITION_STORAGE_KEY);
|
|
10589
|
+
if (!raw) {
|
|
10590
|
+
return null;
|
|
10591
|
+
}
|
|
10592
|
+
const parsed = JSON.parse(raw);
|
|
10593
|
+
if (typeof parsed.left !== "number" || typeof parsed.top !== "number") {
|
|
10594
|
+
return null;
|
|
10595
|
+
}
|
|
10596
|
+
return { left: parsed.left, top: parsed.top };
|
|
10597
|
+
} catch {
|
|
10598
|
+
return null;
|
|
10599
|
+
}
|
|
10600
|
+
}
|
|
10601
|
+
saveToolbarPosition() {
|
|
10602
|
+
try {
|
|
10603
|
+
if (!this.toolbarPosition) {
|
|
10604
|
+
localStorage.removeItem(TOOLBAR_POSITION_STORAGE_KEY);
|
|
10605
|
+
return;
|
|
10606
|
+
}
|
|
10607
|
+
localStorage.setItem(TOOLBAR_POSITION_STORAGE_KEY, JSON.stringify(this.toolbarPosition));
|
|
10608
|
+
} catch {
|
|
10609
|
+
}
|
|
10610
|
+
}
|
|
10611
|
+
clampToolbarPosition(position) {
|
|
10612
|
+
const margin = 12;
|
|
10613
|
+
const width = this.toolbarEl.offsetWidth || 0;
|
|
10614
|
+
const height = this.toolbarEl.offsetHeight || 0;
|
|
10615
|
+
const maxLeft = Math.max(margin, window.innerWidth - width - margin);
|
|
10616
|
+
const maxTop = Math.max(margin, window.innerHeight - height - margin);
|
|
10617
|
+
return {
|
|
10618
|
+
left: clamp(position.left, margin, maxLeft),
|
|
10619
|
+
top: clamp(position.top, margin, maxTop)
|
|
10620
|
+
};
|
|
10621
|
+
}
|
|
10622
|
+
applyToolbarPosition() {
|
|
10623
|
+
if (!this.toolbarPosition) {
|
|
10624
|
+
this.toolbarEl.style.left = "";
|
|
10625
|
+
this.toolbarEl.style.top = "";
|
|
10626
|
+
this.toolbarEl.style.right = "";
|
|
10627
|
+
this.toolbarEl.style.bottom = "";
|
|
10628
|
+
return;
|
|
10629
|
+
}
|
|
10630
|
+
const position = this.clampToolbarPosition(this.toolbarPosition);
|
|
10631
|
+
this.toolbarPosition = position;
|
|
10632
|
+
this.toolbarEl.style.left = toCssPixels(position.left);
|
|
10633
|
+
this.toolbarEl.style.top = toCssPixels(position.top);
|
|
10634
|
+
this.toolbarEl.style.right = "auto";
|
|
10635
|
+
this.toolbarEl.style.bottom = "auto";
|
|
10636
|
+
}
|
|
10637
|
+
attachToolbarDrag(handle) {
|
|
10638
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
10639
|
+
if (event.button !== 0) {
|
|
10640
|
+
return;
|
|
10641
|
+
}
|
|
10642
|
+
event.preventDefault();
|
|
10643
|
+
event.stopPropagation();
|
|
10644
|
+
const rect = this.toolbarEl.getBoundingClientRect();
|
|
10645
|
+
const startX = event.clientX;
|
|
10646
|
+
const startY = event.clientY;
|
|
10647
|
+
const startLeft = rect.left;
|
|
10648
|
+
const startTop = rect.top;
|
|
10649
|
+
this.toolbarEl.classList.add("vpg-annotator-toolbar--dragging");
|
|
10650
|
+
const onPointerMove = (moveEvent) => {
|
|
10651
|
+
this.toolbarPosition = {
|
|
10652
|
+
left: startLeft + moveEvent.clientX - startX,
|
|
10653
|
+
top: startTop + moveEvent.clientY - startY
|
|
10654
|
+
};
|
|
10655
|
+
this.applyToolbarPosition();
|
|
10656
|
+
};
|
|
10657
|
+
const onPointerUp = () => {
|
|
10658
|
+
this.toolbarEl.classList.remove("vpg-annotator-toolbar--dragging");
|
|
10659
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
10660
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
10661
|
+
window.removeEventListener("pointercancel", onPointerUp);
|
|
10662
|
+
this.saveToolbarPosition();
|
|
10663
|
+
};
|
|
10664
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
10665
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
10666
|
+
window.addEventListener("pointercancel", onPointerUp);
|
|
10667
|
+
});
|
|
10668
|
+
}
|
|
10669
|
+
onWindowKeyDown(event) {
|
|
10670
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) {
|
|
10671
|
+
return;
|
|
10672
|
+
}
|
|
10673
|
+
if (event.key === "Escape") {
|
|
10674
|
+
if (!this.inspectMode && !this.currentPanelEl) {
|
|
10675
|
+
return;
|
|
10676
|
+
}
|
|
10677
|
+
event.preventDefault();
|
|
10678
|
+
if (this.inspectMode) {
|
|
10679
|
+
this.setInspectMode(false);
|
|
10680
|
+
} else {
|
|
10681
|
+
this.closePanel();
|
|
10682
|
+
}
|
|
10683
|
+
return;
|
|
10684
|
+
}
|
|
10685
|
+
if (isEditableTarget(event.target)) {
|
|
10686
|
+
return;
|
|
10687
|
+
}
|
|
10688
|
+
const key = event.key.toLowerCase();
|
|
10689
|
+
if (key === SHORTCUT_LABELS.select.toLowerCase()) {
|
|
10690
|
+
event.preventDefault();
|
|
10691
|
+
this.setInspectMode(!this.inspectMode);
|
|
10692
|
+
return;
|
|
10693
|
+
}
|
|
10694
|
+
if (key === SHORTCUT_LABELS.preview.toLowerCase()) {
|
|
10695
|
+
if (!this.annotations.length) {
|
|
10696
|
+
return;
|
|
10697
|
+
}
|
|
10698
|
+
event.preventDefault();
|
|
10699
|
+
this.openPreview();
|
|
10700
|
+
return;
|
|
10701
|
+
}
|
|
10702
|
+
if (key === SHORTCUT_LABELS.copy.toLowerCase()) {
|
|
10703
|
+
if (!this.annotations.length) {
|
|
10704
|
+
return;
|
|
10705
|
+
}
|
|
10706
|
+
event.preventDefault();
|
|
10707
|
+
void this.copyAnnotations();
|
|
10708
|
+
return;
|
|
10709
|
+
}
|
|
10710
|
+
if (key === SHORTCUT_LABELS.clear.toLowerCase()) {
|
|
10711
|
+
if (!this.annotations.length) {
|
|
10712
|
+
return;
|
|
10713
|
+
}
|
|
10714
|
+
event.preventDefault();
|
|
10715
|
+
this.clearAnnotations();
|
|
10716
|
+
return;
|
|
10717
|
+
}
|
|
10718
|
+
if (event.key === SHORTCUT_LABELS.settings) {
|
|
10719
|
+
event.preventDefault();
|
|
10720
|
+
this.openSettings();
|
|
10721
|
+
}
|
|
10722
|
+
}
|
|
10723
|
+
setInspectMode(next) {
|
|
10724
|
+
this.inspectMode = next;
|
|
10725
|
+
this.shieldEl.hidden = !next;
|
|
10726
|
+
this.highlightEl.hidden = !next;
|
|
10727
|
+
if (!next) {
|
|
10728
|
+
this.hoveredElement = null;
|
|
10729
|
+
this.highlightEl.hidden = true;
|
|
10730
|
+
}
|
|
10731
|
+
this.closePanel();
|
|
10732
|
+
this.renderToolbar();
|
|
10733
|
+
}
|
|
10734
|
+
stopInspectModePreservingPending() {
|
|
10735
|
+
this.inspectMode = false;
|
|
10736
|
+
this.shieldEl.hidden = true;
|
|
10737
|
+
this.highlightEl.hidden = true;
|
|
10738
|
+
this.hoveredElement = null;
|
|
10739
|
+
this.renderToolbar();
|
|
10740
|
+
}
|
|
10741
|
+
elementFromPoint(clientX, clientY) {
|
|
10742
|
+
const previousPointerEvents = this.shieldEl.style.pointerEvents;
|
|
10743
|
+
this.shieldEl.style.pointerEvents = "none";
|
|
10744
|
+
const elements = document.elementsFromPoint(clientX, clientY);
|
|
10745
|
+
this.shieldEl.style.pointerEvents = previousPointerEvents;
|
|
10746
|
+
return elements.find((element) => !isInsideAnnotatorTree(element)) ?? null;
|
|
10747
|
+
}
|
|
10748
|
+
onShieldMouseMove(event) {
|
|
10749
|
+
if (!this.inspectMode) {
|
|
10750
|
+
return;
|
|
10751
|
+
}
|
|
10752
|
+
const element = this.elementFromPoint(event.clientX, event.clientY);
|
|
10753
|
+
if (!element) {
|
|
10754
|
+
this.hoveredElement = null;
|
|
10755
|
+
this.highlightEl.hidden = true;
|
|
10756
|
+
return;
|
|
10757
|
+
}
|
|
10758
|
+
this.hoveredElement = element;
|
|
10759
|
+
const rect = element.getBoundingClientRect();
|
|
10760
|
+
const info = resolveVueComponentInfo(element, this.options);
|
|
10761
|
+
Object.assign(this.highlightEl.style, {
|
|
10762
|
+
left: toCssPixels(rect.left),
|
|
10763
|
+
top: toCssPixels(rect.top),
|
|
10764
|
+
width: toCssPixels(rect.width),
|
|
10765
|
+
height: toCssPixels(rect.height)
|
|
10766
|
+
});
|
|
10767
|
+
this.highlightLabelEl.textContent = info?.formatted || getElementSummary(element);
|
|
10768
|
+
this.highlightEl.hidden = false;
|
|
10769
|
+
}
|
|
10770
|
+
onShieldClick(event) {
|
|
10771
|
+
if (!this.inspectMode) {
|
|
10772
|
+
return;
|
|
10773
|
+
}
|
|
10774
|
+
event.preventDefault();
|
|
10775
|
+
event.stopPropagation();
|
|
10776
|
+
const element = this.elementFromPoint(event.clientX, event.clientY);
|
|
10777
|
+
if (!element) {
|
|
10778
|
+
return;
|
|
10779
|
+
}
|
|
10780
|
+
this.hoveredElement = element;
|
|
10781
|
+
this.pendingAnnotation = this.buildAnnotationDraft(element, "");
|
|
10782
|
+
this.editingAnnotationId = null;
|
|
10783
|
+
this.stopInspectModePreservingPending();
|
|
10784
|
+
this.openInputForDraft(element);
|
|
10785
|
+
}
|
|
10786
|
+
buildAnnotationDraft(element, comment) {
|
|
10787
|
+
const rect = element.getBoundingClientRect();
|
|
10788
|
+
const componentInfo = resolveVueComponentInfo(element, this.options);
|
|
10789
|
+
return {
|
|
10790
|
+
id: `draft-${Date.now()}`,
|
|
10791
|
+
comment,
|
|
10792
|
+
component: this.settings.showComponentTree ? componentInfo?.component : void 0,
|
|
10793
|
+
source: componentInfo?.source,
|
|
10794
|
+
targetLabel: componentInfo?.component || getElementSummary(element),
|
|
10795
|
+
uiText: getNearbyText(element),
|
|
10796
|
+
locator: getNearbyLocator(element),
|
|
10797
|
+
domHint: getElementPath(element),
|
|
10798
|
+
pageX: rect.left + rect.width / 2,
|
|
10799
|
+
pageY: rect.top + window.scrollY
|
|
10800
|
+
};
|
|
10801
|
+
}
|
|
10802
|
+
createPanelBase(className) {
|
|
10803
|
+
const panel = document.createElement("div");
|
|
10804
|
+
panel.className = className;
|
|
10805
|
+
panel.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10806
|
+
const arrowEl = createFloatingArrow();
|
|
10807
|
+
const body = document.createElement("div");
|
|
10808
|
+
body.className = className === "vpg-annotator-settings" ? "vpg-annotator-settings-body" : className === "vpg-annotator-input" ? "vpg-annotator-input-body" : "vpg-annotator-panel-body";
|
|
10809
|
+
body.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10810
|
+
panel.append(arrowEl, body);
|
|
10811
|
+
document.body.appendChild(panel);
|
|
10812
|
+
return { panel, arrowEl, body };
|
|
10813
|
+
}
|
|
10814
|
+
attachFloating(reference, panel, arrowEl, placement, allowedPlacements) {
|
|
10815
|
+
const cleanup = dom.autoUpdate(reference, panel, async () => {
|
|
10816
|
+
const result = await dom.computePosition(reference, panel, {
|
|
10817
|
+
strategy: "fixed",
|
|
10818
|
+
placement,
|
|
10819
|
+
middleware: [
|
|
10820
|
+
dom.offset(14),
|
|
10821
|
+
dom.autoPlacement({ allowedPlacements: [...allowedPlacements], padding: 12 }),
|
|
10822
|
+
dom.shift({ padding: 12 }),
|
|
10823
|
+
dom.arrow({ element: arrowEl, padding: 10 })
|
|
10824
|
+
]
|
|
10825
|
+
});
|
|
10826
|
+
panel.setAttribute("data-placement", result.placement);
|
|
10827
|
+
panel.style.left = toCssPixels(result.x);
|
|
10828
|
+
panel.style.top = toCssPixels(result.y);
|
|
10829
|
+
panel.style.visibility = "visible";
|
|
10830
|
+
const arrowData = result.middlewareData.arrow;
|
|
10831
|
+
const side = result.placement.split("-")[0];
|
|
10832
|
+
const staticSide = side === "top" ? "bottom" : side === "bottom" ? "top" : side === "left" ? "right" : "left";
|
|
10833
|
+
arrowEl.style.removeProperty("top");
|
|
10834
|
+
arrowEl.style.removeProperty("right");
|
|
10835
|
+
arrowEl.style.removeProperty("bottom");
|
|
10836
|
+
arrowEl.style.removeProperty("left");
|
|
10837
|
+
if (typeof arrowData?.x === "number") {
|
|
10838
|
+
arrowEl.style.left = toCssPixels(arrowData.x);
|
|
10839
|
+
}
|
|
10840
|
+
if (typeof arrowData?.y === "number") {
|
|
10841
|
+
arrowEl.style.top = toCssPixels(arrowData.y);
|
|
10842
|
+
}
|
|
10843
|
+
arrowEl.style.setProperty(staticSide, "-7px");
|
|
10844
|
+
});
|
|
10845
|
+
return cleanup;
|
|
10846
|
+
}
|
|
10847
|
+
openInputForDraft(reference) {
|
|
10848
|
+
if (!this.pendingAnnotation) {
|
|
10849
|
+
return;
|
|
10850
|
+
}
|
|
10851
|
+
const { panel, body, arrowEl } = this.createPanelBase("vpg-annotator-input");
|
|
10852
|
+
const title = document.createElement("div");
|
|
10853
|
+
title.className = "vpg-annotator-heading";
|
|
10854
|
+
title.textContent = this.pendingAnnotation.component || this.pendingAnnotation.targetLabel;
|
|
10855
|
+
const subtitle = document.createElement("p");
|
|
10856
|
+
subtitle.className = "vpg-annotator-subtle";
|
|
10857
|
+
subtitle.textContent = this.pendingAnnotation.source || "Unable to find component file path.";
|
|
10858
|
+
const metadata = document.createElement("textarea");
|
|
10859
|
+
metadata.className = "vpg-annotator-textarea vpg-annotator-input-meta";
|
|
10860
|
+
metadata.readOnly = true;
|
|
10861
|
+
metadata.value = formatSingleAnnotationPreview(this.pendingAnnotation, this.settings.outputDetail, window.location.href);
|
|
10862
|
+
const commentLabel = document.createElement("label");
|
|
10863
|
+
commentLabel.className = "vpg-annotator-label";
|
|
10864
|
+
commentLabel.textContent = "Comment";
|
|
10865
|
+
const comment = document.createElement("textarea");
|
|
10866
|
+
comment.className = "vpg-annotator-comment";
|
|
10867
|
+
comment.value = this.pendingAnnotation.comment;
|
|
10868
|
+
comment.placeholder = "Add a comment...";
|
|
10869
|
+
const actions = document.createElement("div");
|
|
10870
|
+
actions.className = "vpg-annotator-actions";
|
|
10871
|
+
const cancelButton = createButton("Cancel", () => this.closePanel());
|
|
10872
|
+
const saveButton = createButton(this.editingAnnotationId ? "Save" : "Add", () => {
|
|
10873
|
+
if (!this.pendingAnnotation) {
|
|
10874
|
+
return;
|
|
10875
|
+
}
|
|
10876
|
+
this.pendingAnnotation.comment = comment.value.trim();
|
|
10877
|
+
if (this.editingAnnotationId) {
|
|
10878
|
+
const index = this.annotations.findIndex((annotation) => annotation.id === this.editingAnnotationId);
|
|
10879
|
+
if (index >= 0) {
|
|
10880
|
+
this.annotations[index] = { ...this.pendingAnnotation, id: this.editingAnnotationId };
|
|
10881
|
+
}
|
|
10882
|
+
} else {
|
|
10883
|
+
this.annotations.push({ ...this.pendingAnnotation, id: `annotation-${Date.now()}` });
|
|
10884
|
+
}
|
|
10885
|
+
this.saveAnnotations();
|
|
10886
|
+
this.closePanel();
|
|
10887
|
+
}, { primary: true });
|
|
10888
|
+
actions.append(cancelButton);
|
|
10889
|
+
if (this.editingAnnotationId) {
|
|
10890
|
+
const deleteButton = createButton("Delete", () => {
|
|
10891
|
+
this.annotations = this.annotations.filter((annotation) => annotation.id !== this.editingAnnotationId);
|
|
10892
|
+
this.saveAnnotations();
|
|
10893
|
+
this.closePanel();
|
|
10894
|
+
});
|
|
10895
|
+
actions.append(deleteButton);
|
|
10896
|
+
}
|
|
10897
|
+
actions.append(saveButton);
|
|
10898
|
+
body.append(title, subtitle, metadata, commentLabel, comment, actions);
|
|
10899
|
+
this.showPanel(panel, () => this.attachFloating(reference, panel, arrowEl, "right-start", ["right-start", "left-start", "bottom-start", "top-start"]));
|
|
10900
|
+
comment.focus();
|
|
10901
|
+
}
|
|
10902
|
+
openInputForExistingAnnotation(annotation, reference) {
|
|
10903
|
+
this.pendingAnnotation = { ...annotation };
|
|
10904
|
+
this.editingAnnotationId = annotation.id;
|
|
10905
|
+
this.openInputForDraft(reference);
|
|
10906
|
+
}
|
|
10907
|
+
openPreview() {
|
|
10908
|
+
if (!this.previewButton || this.annotations.length === 0) {
|
|
10909
|
+
return;
|
|
10910
|
+
}
|
|
10911
|
+
const { panel, body, arrowEl } = this.createPanelBase("vpg-annotator-panel");
|
|
10912
|
+
const title = document.createElement("div");
|
|
10913
|
+
title.className = "vpg-annotator-heading";
|
|
10914
|
+
title.textContent = "Annotation preview";
|
|
10915
|
+
const subtitle = document.createElement("p");
|
|
10916
|
+
subtitle.className = "vpg-annotator-subtle";
|
|
10917
|
+
subtitle.textContent = "Exact text that Copy will copy.";
|
|
10918
|
+
const textarea = document.createElement("textarea");
|
|
10919
|
+
textarea.className = "vpg-annotator-textarea";
|
|
10920
|
+
textarea.readOnly = true;
|
|
10921
|
+
textarea.value = formatAnnotations(this.annotations, this.settings.outputDetail, window.location.href);
|
|
10922
|
+
body.append(title, subtitle, textarea);
|
|
10923
|
+
this.showPanel(panel, () => this.attachFloating(this.previewButton, panel, arrowEl, "top-start", ["top-start", "left-start", "top-end", "left-end"]));
|
|
10924
|
+
}
|
|
10925
|
+
openSettings() {
|
|
10926
|
+
if (!this.settingsButton) {
|
|
10927
|
+
return;
|
|
10928
|
+
}
|
|
10929
|
+
const { panel, body, arrowEl } = this.createPanelBase("vpg-annotator-settings");
|
|
10930
|
+
const title = document.createElement("div");
|
|
10931
|
+
title.className = "vpg-annotator-heading";
|
|
10932
|
+
title.textContent = "Annotator settings";
|
|
10933
|
+
const detailField = document.createElement("div");
|
|
10934
|
+
detailField.className = "vpg-annotator-field";
|
|
10935
|
+
const detailLabel = document.createElement("label");
|
|
10936
|
+
detailLabel.className = "vpg-annotator-label";
|
|
10937
|
+
detailLabel.textContent = "Output detail";
|
|
10938
|
+
const detailSelect = document.createElement("select");
|
|
10939
|
+
detailSelect.className = "vpg-annotator-select";
|
|
10940
|
+
detailSelect.innerHTML = '<option value="standard">Standard</option><option value="forensic">Forensic</option>';
|
|
10941
|
+
detailSelect.value = this.settings.outputDetail;
|
|
10942
|
+
detailSelect.addEventListener("change", () => {
|
|
10943
|
+
this.settings.outputDetail = detailSelect.value === "forensic" ? "forensic" : "standard";
|
|
10944
|
+
this.saveSettings();
|
|
10945
|
+
});
|
|
10946
|
+
detailField.append(detailLabel, detailSelect);
|
|
10947
|
+
const showComponentField = document.createElement("label");
|
|
10948
|
+
showComponentField.className = "vpg-annotator-row";
|
|
10949
|
+
const showComponentCopy = document.createElement("span");
|
|
10950
|
+
showComponentCopy.className = "vpg-annotator-label";
|
|
10951
|
+
showComponentCopy.textContent = "Show component labels";
|
|
10952
|
+
const showComponentCheckbox = document.createElement("input");
|
|
10953
|
+
showComponentCheckbox.type = "checkbox";
|
|
10954
|
+
showComponentCheckbox.className = "vpg-annotator-checkbox";
|
|
10955
|
+
showComponentCheckbox.checked = this.settings.showComponentTree;
|
|
10956
|
+
showComponentCheckbox.addEventListener("change", () => {
|
|
10957
|
+
this.settings.showComponentTree = showComponentCheckbox.checked;
|
|
10958
|
+
this.saveSettings();
|
|
10959
|
+
});
|
|
10960
|
+
showComponentField.append(showComponentCopy, showComponentCheckbox);
|
|
10961
|
+
const shortcutsField = document.createElement("div");
|
|
10962
|
+
shortcutsField.className = "vpg-annotator-field";
|
|
10963
|
+
const shortcutsLabel = document.createElement("div");
|
|
10964
|
+
shortcutsLabel.className = "vpg-annotator-label";
|
|
10965
|
+
shortcutsLabel.textContent = "Keyboard shortcuts";
|
|
10966
|
+
const shortcutsList = document.createElement("div");
|
|
10967
|
+
shortcutsList.className = "vpg-annotator-shortcuts";
|
|
10968
|
+
for (const [shortcut, description] of [
|
|
10969
|
+
[SHORTCUT_LABELS.select, "Toggle selection mode"],
|
|
10970
|
+
[SHORTCUT_LABELS.preview, "Open preview"],
|
|
10971
|
+
[SHORTCUT_LABELS.copy, "Copy annotations"],
|
|
10972
|
+
[SHORTCUT_LABELS.clear, "Clear annotations"],
|
|
10973
|
+
[SHORTCUT_LABELS.settings, "Open settings"],
|
|
10974
|
+
[SHORTCUT_LABELS.cancel, "Close panel / cancel selection"]
|
|
10975
|
+
]) {
|
|
10976
|
+
const row = document.createElement("div");
|
|
10977
|
+
row.className = "vpg-annotator-shortcut-row";
|
|
10978
|
+
const text = document.createElement("span");
|
|
10979
|
+
text.className = "vpg-annotator-subtle";
|
|
10980
|
+
text.textContent = description;
|
|
10981
|
+
const kbd = document.createElement("kbd");
|
|
10982
|
+
kbd.className = "vpg-annotator-kbd";
|
|
10983
|
+
kbd.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
10984
|
+
kbd.textContent = shortcut;
|
|
10985
|
+
row.append(text, kbd);
|
|
10986
|
+
shortcutsList.appendChild(row);
|
|
10987
|
+
}
|
|
10988
|
+
shortcutsField.append(shortcutsLabel, shortcutsList);
|
|
10989
|
+
body.append(title, detailField, showComponentField, shortcutsField);
|
|
10990
|
+
this.showPanel(panel, () => this.attachFloating(this.settingsButton, panel, arrowEl, "top-start", ["top-start", "left-start", "top-end", "left-end"]));
|
|
10991
|
+
}
|
|
10992
|
+
disposeCurrentPanel() {
|
|
10993
|
+
this.currentPanelCleanup?.();
|
|
10994
|
+
this.currentPanelCleanup = null;
|
|
10995
|
+
this.currentPanelEl?.remove();
|
|
10996
|
+
this.currentPanelEl = null;
|
|
10997
|
+
}
|
|
10998
|
+
showPanel(panel, attachFloatingFn) {
|
|
10999
|
+
this.disposeCurrentPanel();
|
|
11000
|
+
this.currentPanelEl = panel;
|
|
11001
|
+
this.currentPanelCleanup = attachFloatingFn();
|
|
11002
|
+
const onPointerDown = (event) => {
|
|
11003
|
+
const target = event.target;
|
|
11004
|
+
if (isInsideAnnotatorTree(target)) {
|
|
11005
|
+
return;
|
|
11006
|
+
}
|
|
11007
|
+
this.closePanel();
|
|
11008
|
+
};
|
|
11009
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
11010
|
+
const originalCleanup = this.currentPanelCleanup;
|
|
11011
|
+
this.currentPanelCleanup = () => {
|
|
11012
|
+
originalCleanup?.();
|
|
11013
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
11014
|
+
};
|
|
11015
|
+
}
|
|
11016
|
+
closePanel() {
|
|
11017
|
+
this.disposeCurrentPanel();
|
|
11018
|
+
this.pendingAnnotation = null;
|
|
11019
|
+
this.editingAnnotationId = null;
|
|
11020
|
+
}
|
|
11021
|
+
clearAnnotations() {
|
|
11022
|
+
if (!this.annotations.length) {
|
|
11023
|
+
return;
|
|
11024
|
+
}
|
|
11025
|
+
this.annotations = [];
|
|
11026
|
+
this.saveAnnotations();
|
|
11027
|
+
this.showToast("Annotations cleared");
|
|
11028
|
+
this.closePanel();
|
|
11029
|
+
}
|
|
11030
|
+
async copyAnnotations() {
|
|
11031
|
+
if (!this.annotations.length) {
|
|
11032
|
+
return;
|
|
11033
|
+
}
|
|
11034
|
+
const text = formatAnnotations(this.annotations, this.settings.outputDetail, window.location.href);
|
|
11035
|
+
try {
|
|
11036
|
+
await copyTextToClipboard(text);
|
|
11037
|
+
this.showToast("Copied");
|
|
11038
|
+
} catch {
|
|
11039
|
+
this.showToast("Copy failed");
|
|
11040
|
+
}
|
|
11041
|
+
}
|
|
11042
|
+
showToast(message) {
|
|
11043
|
+
this.toastEl?.remove();
|
|
11044
|
+
if (this.toastTimer !== null) {
|
|
11045
|
+
window.clearTimeout(this.toastTimer);
|
|
11046
|
+
}
|
|
11047
|
+
const toast = document.createElement("div");
|
|
11048
|
+
toast.className = "vpg-annotator-toast";
|
|
11049
|
+
toast.setAttribute(ANNOTATOR_ROOT_ATTR, "");
|
|
11050
|
+
toast.textContent = message;
|
|
11051
|
+
document.body.appendChild(toast);
|
|
11052
|
+
this.toastEl = toast;
|
|
11053
|
+
this.toastTimer = window.setTimeout(() => {
|
|
11054
|
+
toast.remove();
|
|
11055
|
+
if (this.toastEl === toast) {
|
|
11056
|
+
this.toastEl = null;
|
|
11057
|
+
}
|
|
11058
|
+
this.toastTimer = null;
|
|
11059
|
+
}, 1600);
|
|
11060
|
+
}
|
|
11061
|
+
}
|
|
11062
|
+
function mountAnnotatorClient(options) {
|
|
11063
|
+
const runtimeWindow = window;
|
|
11064
|
+
if (runtimeWindow[RUNTIME_GUARD]) {
|
|
11065
|
+
return runtimeWindow[RUNTIME_GUARD];
|
|
11066
|
+
}
|
|
11067
|
+
const runtime = new AnnotatorRuntime(options);
|
|
11068
|
+
runtime.mount();
|
|
11069
|
+
runtimeWindow[RUNTIME_GUARD] = runtime;
|
|
11070
|
+
return runtime;
|
|
11071
|
+
}
|
|
9677
11072
|
const nuxtConfigMarker = /* @__PURE__ */ Symbol.for("@immense/vue-pom-generator.nuxt");
|
|
9678
11073
|
function defineVuePomGeneratorConfig(options) {
|
|
9679
11074
|
return options;
|
|
@@ -9690,5 +11085,6 @@ exports.createVuePomGeneratorPlugins = createVuePomGeneratorPlugins;
|
|
|
9690
11085
|
exports.default = createVuePomGeneratorPlugins;
|
|
9691
11086
|
exports.defineNuxtPomGeneratorConfig = defineNuxtPomGeneratorConfig;
|
|
9692
11087
|
exports.defineVuePomGeneratorConfig = defineVuePomGeneratorConfig;
|
|
11088
|
+
exports.mountAnnotatorClient = mountAnnotatorClient;
|
|
9693
11089
|
exports.vuePomGenerator = createVuePomGeneratorPlugins;
|
|
9694
11090
|
//# sourceMappingURL=index.cjs.map
|