web-remarq 0.6.0 → 0.7.0
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 +27 -2
- package/dist/core/index.cjs +101 -5
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +39 -3
- package/dist/core/index.d.ts +39 -3
- package/dist/core/index.js +96 -4
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +456 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -3
- package/dist/index.d.ts +34 -3
- package/dist/index.js +456 -76
- package/dist/index.js.map +1 -1
- package/dist/web-remarq.global.global.js +456 -76
- package/dist/web-remarq.global.global.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -89,6 +89,21 @@ function destroyViewportListener() {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// src/core/storage.ts
|
|
92
|
+
function migrateAnnotation(legacy) {
|
|
93
|
+
const rawStatus = legacy.status;
|
|
94
|
+
const status = rawStatus === "resolved" ? "verified" : rawStatus;
|
|
95
|
+
if (Array.isArray(legacy.lifecycle) && legacy.lifecycle.length > 0) {
|
|
96
|
+
return __spreadProps(__spreadValues({}, legacy), { status, lifecycle: legacy.lifecycle });
|
|
97
|
+
}
|
|
98
|
+
const createdTs = typeof legacy.timestamp === "number" ? legacy.timestamp : Date.now();
|
|
99
|
+
const lifecycle = [
|
|
100
|
+
{ type: "created", actor: "designer", timestamp: createdTs }
|
|
101
|
+
];
|
|
102
|
+
if (rawStatus === "resolved") {
|
|
103
|
+
lifecycle.push({ type: "migrated", actor: null, timestamp: Date.now() });
|
|
104
|
+
}
|
|
105
|
+
return __spreadProps(__spreadValues({}, legacy), { status, lifecycle });
|
|
106
|
+
}
|
|
92
107
|
var AnnotationStorage = class {
|
|
93
108
|
constructor(adapter) {
|
|
94
109
|
this.adapter = adapter;
|
|
@@ -105,6 +120,9 @@ var AnnotationStorage = class {
|
|
|
105
120
|
getByRoute(route) {
|
|
106
121
|
return this.cache.filter((a) => a.route === route);
|
|
107
122
|
}
|
|
123
|
+
getById(id) {
|
|
124
|
+
return this.cache.find((a) => a.id === id);
|
|
125
|
+
}
|
|
108
126
|
async add(annotation) {
|
|
109
127
|
this.cache.push(annotation);
|
|
110
128
|
await this.adapter.save(annotation);
|
|
@@ -131,7 +149,7 @@ var AnnotationStorage = class {
|
|
|
131
149
|
};
|
|
132
150
|
}
|
|
133
151
|
async importJSON(data) {
|
|
134
|
-
this.cache =
|
|
152
|
+
this.cache = data.annotations.map(migrateAnnotation);
|
|
135
153
|
this.migrateViewportBuckets();
|
|
136
154
|
await this.adapter.clear();
|
|
137
155
|
for (const ann of this.cache) {
|
|
@@ -141,7 +159,7 @@ var AnnotationStorage = class {
|
|
|
141
159
|
async init() {
|
|
142
160
|
const data = await this.adapter.load();
|
|
143
161
|
if (data) {
|
|
144
|
-
this.cache = data.annotations;
|
|
162
|
+
this.cache = data.annotations.map(migrateAnnotation);
|
|
145
163
|
this.migrateViewportBuckets();
|
|
146
164
|
}
|
|
147
165
|
}
|
|
@@ -611,7 +629,16 @@ function generateAgentExport(annotations, viewportBucket) {
|
|
|
611
629
|
status: ann.status,
|
|
612
630
|
timestamp: ann.timestamp,
|
|
613
631
|
source: resolveSource(ann.fingerprint),
|
|
614
|
-
searchHints: buildSearchHints(ann.fingerprint)
|
|
632
|
+
searchHints: buildSearchHints(ann.fingerprint),
|
|
633
|
+
lifecycle: ann.lifecycle.map((ev) => {
|
|
634
|
+
const out = {
|
|
635
|
+
type: ev.type,
|
|
636
|
+
actor: ev.actor,
|
|
637
|
+
timestamp: ev.timestamp
|
|
638
|
+
};
|
|
639
|
+
if (ev.reason !== void 0) out.reason = ev.reason;
|
|
640
|
+
return out;
|
|
641
|
+
})
|
|
615
642
|
}));
|
|
616
643
|
return {
|
|
617
644
|
version: 1,
|
|
@@ -621,6 +648,67 @@ function generateAgentExport(annotations, viewportBucket) {
|
|
|
621
648
|
};
|
|
622
649
|
}
|
|
623
650
|
|
|
651
|
+
// src/core/lifecycle.ts
|
|
652
|
+
var InvalidTransitionError = class extends Error {
|
|
653
|
+
constructor(from, action) {
|
|
654
|
+
super(`Cannot ${action} from status "${from}"`);
|
|
655
|
+
this.name = "InvalidTransitionError";
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
var ACTION_TO_EVENT = {
|
|
659
|
+
acknowledge: "acknowledged",
|
|
660
|
+
claimFix: "fix_claimed",
|
|
661
|
+
verify: "verified",
|
|
662
|
+
reject: "rejected",
|
|
663
|
+
dismiss: "dismissed",
|
|
664
|
+
reopen: "reopened"
|
|
665
|
+
};
|
|
666
|
+
var DEFAULT_ACTOR_BY_EVENT = {
|
|
667
|
+
created: "designer",
|
|
668
|
+
acknowledged: "developer",
|
|
669
|
+
fix_claimed: "agent",
|
|
670
|
+
verified: "developer",
|
|
671
|
+
rejected: "developer",
|
|
672
|
+
dismissed: "developer",
|
|
673
|
+
reopened: "developer",
|
|
674
|
+
migrated: null
|
|
675
|
+
};
|
|
676
|
+
function createEvent(type, opts = {}) {
|
|
677
|
+
var _a3, _b;
|
|
678
|
+
const event = {
|
|
679
|
+
type,
|
|
680
|
+
actor: (_a3 = opts.actor) != null ? _a3 : DEFAULT_ACTOR_BY_EVENT[type],
|
|
681
|
+
timestamp: (_b = opts.timestamp) != null ? _b : Date.now()
|
|
682
|
+
};
|
|
683
|
+
if (opts.actorName !== void 0) event.actorName = opts.actorName;
|
|
684
|
+
if (opts.reason !== void 0) event.reason = opts.reason;
|
|
685
|
+
return event;
|
|
686
|
+
}
|
|
687
|
+
function nextStatus(from, action) {
|
|
688
|
+
switch (action) {
|
|
689
|
+
case "acknowledge":
|
|
690
|
+
return from === "pending" ? "in_progress" : null;
|
|
691
|
+
case "claimFix":
|
|
692
|
+
return from === "pending" || from === "in_progress" ? "fixed_unverified" : null;
|
|
693
|
+
case "verify":
|
|
694
|
+
return from === "fixed_unverified" || from === "in_progress" ? "verified" : null;
|
|
695
|
+
case "reject":
|
|
696
|
+
return from === "fixed_unverified" ? "pending" : null;
|
|
697
|
+
case "dismiss":
|
|
698
|
+
return from === "pending" || from === "in_progress" || from === "fixed_unverified" ? "dismissed" : null;
|
|
699
|
+
case "reopen":
|
|
700
|
+
return from === "dismissed" || from === "verified" ? "pending" : null;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function transition(annotation, action, opts = {}) {
|
|
704
|
+
const next = nextStatus(annotation.status, action);
|
|
705
|
+
if (next === null) {
|
|
706
|
+
throw new InvalidTransitionError(annotation.status, action);
|
|
707
|
+
}
|
|
708
|
+
const event = createEvent(ACTION_TO_EVENT[action], opts);
|
|
709
|
+
return { status: next, event };
|
|
710
|
+
}
|
|
711
|
+
|
|
624
712
|
// src/ui/styles.ts
|
|
625
713
|
var STYLES_ID = "data-remarq-styles";
|
|
626
714
|
var CSS = `
|
|
@@ -635,6 +723,11 @@ var CSS = `
|
|
|
635
723
|
--remarq-resolved: #22c55e;
|
|
636
724
|
--remarq-overlay: rgba(59, 130, 246, 0.15);
|
|
637
725
|
--remarq-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
726
|
+
--remarq-status-pending: #f97316;
|
|
727
|
+
--remarq-status-in-progress: #eab308;
|
|
728
|
+
--remarq-status-fixed-unverified: #3b82f6;
|
|
729
|
+
--remarq-status-verified: #22c55e;
|
|
730
|
+
--remarq-status-dismissed: #6b7280;
|
|
638
731
|
}
|
|
639
732
|
|
|
640
733
|
[data-remarq-theme="dark"] {
|
|
@@ -648,6 +741,11 @@ var CSS = `
|
|
|
648
741
|
--remarq-resolved: #4ade80;
|
|
649
742
|
--remarq-overlay: rgba(96, 165, 250, 0.15);
|
|
650
743
|
--remarq-shadow: 0 4px 12px rgba(0,0,0,0.4);
|
|
744
|
+
--remarq-status-pending: #fb923c;
|
|
745
|
+
--remarq-status-in-progress: #facc15;
|
|
746
|
+
--remarq-status-fixed-unverified: #60a5fa;
|
|
747
|
+
--remarq-status-verified: #4ade80;
|
|
748
|
+
--remarq-status-dismissed: #9ca3af;
|
|
651
749
|
}
|
|
652
750
|
|
|
653
751
|
.remarq-toolbar {
|
|
@@ -691,19 +789,29 @@ var CSS = `
|
|
|
691
789
|
|
|
692
790
|
.remarq-badge {
|
|
693
791
|
position: absolute;
|
|
694
|
-
top: -
|
|
695
|
-
right: -
|
|
696
|
-
|
|
697
|
-
height:
|
|
698
|
-
padding: 0
|
|
699
|
-
border-radius:
|
|
792
|
+
top: -6px;
|
|
793
|
+
right: -6px;
|
|
794
|
+
width: 18px;
|
|
795
|
+
height: 18px;
|
|
796
|
+
padding: 0;
|
|
797
|
+
border-radius: 50%;
|
|
700
798
|
background: var(--remarq-pending);
|
|
701
799
|
color: #ffffff;
|
|
702
800
|
font-size: 10px;
|
|
703
801
|
font-weight: 600;
|
|
802
|
+
line-height: 1;
|
|
704
803
|
display: flex;
|
|
705
804
|
align-items: center;
|
|
706
805
|
justify-content: center;
|
|
806
|
+
box-sizing: border-box;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.remarq-toolbar-badge--verification {
|
|
810
|
+
top: auto;
|
|
811
|
+
bottom: -6px;
|
|
812
|
+
right: -6px;
|
|
813
|
+
background: var(--remarq-status-fixed-unverified);
|
|
814
|
+
cursor: pointer;
|
|
707
815
|
}
|
|
708
816
|
|
|
709
817
|
.remarq-overlay {
|
|
@@ -753,8 +861,11 @@ var CSS = `
|
|
|
753
861
|
}
|
|
754
862
|
|
|
755
863
|
.remarq-marker:hover { transform: scale(1.2); }
|
|
756
|
-
.remarq-marker
|
|
757
|
-
.remarq-marker
|
|
864
|
+
.remarq-marker--pending { background: var(--remarq-status-pending); }
|
|
865
|
+
.remarq-marker--in-progress { background: var(--remarq-status-in-progress); }
|
|
866
|
+
.remarq-marker--fixed-unverified { background: var(--remarq-status-fixed-unverified); }
|
|
867
|
+
.remarq-marker--verified { background: var(--remarq-status-verified); opacity: 0.7; }
|
|
868
|
+
.remarq-marker--dismissed { background: var(--remarq-status-dismissed); opacity: 0.5; }
|
|
758
869
|
|
|
759
870
|
.remarq-popup {
|
|
760
871
|
position: absolute;
|
|
@@ -799,14 +910,30 @@ var CSS = `
|
|
|
799
910
|
|
|
800
911
|
.remarq-popup-actions {
|
|
801
912
|
display: flex;
|
|
802
|
-
|
|
803
|
-
gap:
|
|
913
|
+
flex-direction: column;
|
|
914
|
+
gap: 6px;
|
|
804
915
|
padding: 8px 12px;
|
|
805
916
|
border-top: 1px solid var(--remarq-border);
|
|
806
917
|
}
|
|
807
918
|
|
|
919
|
+
.remarq-popup-actions-row {
|
|
920
|
+
display: flex;
|
|
921
|
+
flex-wrap: wrap;
|
|
922
|
+
gap: 6px;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.remarq-popup-actions-row--transitions {
|
|
926
|
+
justify-content: flex-start;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.remarq-popup-actions-row--utility {
|
|
930
|
+
justify-content: flex-end;
|
|
931
|
+
padding-top: 6px;
|
|
932
|
+
border-top: 1px dashed var(--remarq-border);
|
|
933
|
+
}
|
|
934
|
+
|
|
808
935
|
.remarq-popup-actions button {
|
|
809
|
-
padding:
|
|
936
|
+
padding: 5px 12px;
|
|
810
937
|
border: 1px solid var(--remarq-border);
|
|
811
938
|
border-radius: 4px;
|
|
812
939
|
background: var(--remarq-bg);
|
|
@@ -821,6 +948,19 @@ var CSS = `
|
|
|
821
948
|
color: #ffffff;
|
|
822
949
|
}
|
|
823
950
|
|
|
951
|
+
.remarq-popup-utility-btn {
|
|
952
|
+
font-size: 11px !important;
|
|
953
|
+
padding: 3px 10px !important;
|
|
954
|
+
color: var(--remarq-text-secondary) !important;
|
|
955
|
+
background: transparent !important;
|
|
956
|
+
border-color: transparent !important;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.remarq-popup-utility-btn:hover {
|
|
960
|
+
background: var(--remarq-bg-secondary) !important;
|
|
961
|
+
color: var(--remarq-text) !important;
|
|
962
|
+
}
|
|
963
|
+
|
|
824
964
|
.remarq-detached-panel {
|
|
825
965
|
position: fixed;
|
|
826
966
|
z-index: 2147483646;
|
|
@@ -1079,6 +1219,51 @@ var CSS = `
|
|
|
1079
1219
|
color: var(--remarq-text-secondary);
|
|
1080
1220
|
margin-top: 4px;
|
|
1081
1221
|
}
|
|
1222
|
+
|
|
1223
|
+
.remarq-popup-history {
|
|
1224
|
+
margin-top: 8px;
|
|
1225
|
+
font-size: 12px;
|
|
1226
|
+
color: var(--remarq-text-secondary);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
.remarq-popup-history summary {
|
|
1230
|
+
cursor: pointer;
|
|
1231
|
+
padding: 4px 0;
|
|
1232
|
+
user-select: none;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
.remarq-popup-history-list {
|
|
1236
|
+
list-style: none;
|
|
1237
|
+
margin: 4px 0 0 0;
|
|
1238
|
+
padding: 0;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
.remarq-popup-history-list li {
|
|
1242
|
+
padding: 2px 0;
|
|
1243
|
+
font-size: 11px;
|
|
1244
|
+
line-height: 1.4;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
.remarq-popup-reason {
|
|
1248
|
+
width: 100%;
|
|
1249
|
+
min-height: 50px;
|
|
1250
|
+
margin-bottom: 8px;
|
|
1251
|
+
padding: 6px;
|
|
1252
|
+
border: 1px solid var(--remarq-border);
|
|
1253
|
+
border-radius: 4px;
|
|
1254
|
+
background: var(--remarq-bg-secondary);
|
|
1255
|
+
color: var(--remarq-text);
|
|
1256
|
+
font-family: inherit;
|
|
1257
|
+
font-size: 12px;
|
|
1258
|
+
resize: vertical;
|
|
1259
|
+
box-sizing: border-box;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
.remarq-popup-reason-row {
|
|
1263
|
+
display: flex;
|
|
1264
|
+
justify-content: flex-end;
|
|
1265
|
+
gap: 8px;
|
|
1266
|
+
}
|
|
1082
1267
|
`;
|
|
1083
1268
|
function injectStyles() {
|
|
1084
1269
|
if (document.querySelector(`style[${STYLES_ID}]`)) return;
|
|
@@ -1186,8 +1371,19 @@ var Toolbar = class {
|
|
|
1186
1371
|
this.inspectBtn = this.createButton("inspect", ICONS.inspect, () => callbacks.onInspect());
|
|
1187
1372
|
this.badgeEl = document.createElement("span");
|
|
1188
1373
|
this.badgeEl.className = "remarq-badge";
|
|
1374
|
+
this.badgeEl.title = "Needs attention";
|
|
1189
1375
|
this.badgeEl.style.display = "none";
|
|
1190
1376
|
this.inspectBtn.appendChild(this.badgeEl);
|
|
1377
|
+
this.verificationBadgeEl = document.createElement("span");
|
|
1378
|
+
this.verificationBadgeEl.className = "remarq-badge remarq-toolbar-badge--verification";
|
|
1379
|
+
this.verificationBadgeEl.title = "Pending your verification";
|
|
1380
|
+
this.verificationBadgeEl.style.display = "none";
|
|
1381
|
+
this.verificationBadgeEl.addEventListener("click", (e) => {
|
|
1382
|
+
var _a3;
|
|
1383
|
+
e.stopPropagation();
|
|
1384
|
+
(_a3 = callbacks.onVerificationBadgeClick) == null ? void 0 : _a3.call(callbacks);
|
|
1385
|
+
});
|
|
1386
|
+
this.inspectBtn.appendChild(this.verificationBadgeEl);
|
|
1191
1387
|
this.spacingBtn = this.createButton("spacing", ICONS.spacing, () => callbacks.onSpacingToggle());
|
|
1192
1388
|
this.spacingBtn.disabled = true;
|
|
1193
1389
|
const copyBtn = this.createButton("copy", ICONS.copy, () => callbacks.onCopy());
|
|
@@ -1234,6 +1430,10 @@ var Toolbar = class {
|
|
|
1234
1430
|
this.badgeEl.textContent = String(count);
|
|
1235
1431
|
this.badgeEl.style.display = count > 0 ? "flex" : "none";
|
|
1236
1432
|
}
|
|
1433
|
+
setVerificationBadgeCount(count) {
|
|
1434
|
+
this.verificationBadgeEl.textContent = String(count);
|
|
1435
|
+
this.verificationBadgeEl.style.display = count > 0 ? "flex" : "none";
|
|
1436
|
+
}
|
|
1237
1437
|
getFileInput() {
|
|
1238
1438
|
return this.fileInput;
|
|
1239
1439
|
}
|
|
@@ -1717,6 +1917,47 @@ var SpacingOverlay = class {
|
|
|
1717
1917
|
};
|
|
1718
1918
|
|
|
1719
1919
|
// src/ui/popup.ts
|
|
1920
|
+
var STATUS_LABEL = {
|
|
1921
|
+
pending: "Pending",
|
|
1922
|
+
in_progress: "In progress",
|
|
1923
|
+
fixed_unverified: "Fix claimed",
|
|
1924
|
+
verified: "Verified",
|
|
1925
|
+
dismissed: "Dismissed"
|
|
1926
|
+
};
|
|
1927
|
+
var EVENT_LABEL = {
|
|
1928
|
+
created: "Created",
|
|
1929
|
+
acknowledged: "In progress",
|
|
1930
|
+
fix_claimed: "Fix claimed",
|
|
1931
|
+
verified: "Verified",
|
|
1932
|
+
rejected: "Rejected",
|
|
1933
|
+
dismissed: "Dismissed",
|
|
1934
|
+
reopened: "Reopened",
|
|
1935
|
+
migrated: "Migrated"
|
|
1936
|
+
};
|
|
1937
|
+
function actionsForStatus(status) {
|
|
1938
|
+
switch (status) {
|
|
1939
|
+
case "pending":
|
|
1940
|
+
return [
|
|
1941
|
+
{ label: "Acknowledge", action: "acknowledge", primary: true },
|
|
1942
|
+
{ label: "Dismiss", action: "dismiss", needsReason: true }
|
|
1943
|
+
];
|
|
1944
|
+
case "in_progress":
|
|
1945
|
+
return [
|
|
1946
|
+
{ label: "Mark verified", action: "verify", primary: true },
|
|
1947
|
+
{ label: "Dismiss", action: "dismiss", needsReason: true }
|
|
1948
|
+
];
|
|
1949
|
+
case "fixed_unverified":
|
|
1950
|
+
return [
|
|
1951
|
+
{ label: "Verify", action: "verify", primary: true },
|
|
1952
|
+
{ label: "Reject", action: "reject", needsReason: true },
|
|
1953
|
+
{ label: "Dismiss", action: "dismiss", needsReason: true }
|
|
1954
|
+
];
|
|
1955
|
+
case "verified":
|
|
1956
|
+
return [{ label: "Reopen", action: "reopen" }];
|
|
1957
|
+
case "dismissed":
|
|
1958
|
+
return [{ label: "Reopen", action: "reopen" }];
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1720
1961
|
var POPUP_WIDTH = 300;
|
|
1721
1962
|
var POPUP_MARGIN = 8;
|
|
1722
1963
|
var Popup = class {
|
|
@@ -1725,6 +1966,7 @@ var Popup = class {
|
|
|
1725
1966
|
this.popupEl = null;
|
|
1726
1967
|
this.keyHandler = null;
|
|
1727
1968
|
this.outsideClickHandler = null;
|
|
1969
|
+
this.pendingEditFlush = null;
|
|
1728
1970
|
}
|
|
1729
1971
|
show(info, position, onSubmit, onCancel) {
|
|
1730
1972
|
this.hide();
|
|
@@ -1809,7 +2051,7 @@ var Popup = class {
|
|
|
1809
2051
|
popup2.className = "remarq-popup";
|
|
1810
2052
|
const header = document.createElement("div");
|
|
1811
2053
|
header.className = "remarq-popup-header";
|
|
1812
|
-
header.textContent = `<${info.tag}>${info.text ? ` "${info.text}"` : ""} [${info.status}]`;
|
|
2054
|
+
header.textContent = `<${info.tag}>${info.text ? ` "${info.text}"` : ""} [${STATUS_LABEL[info.status]}]`;
|
|
1813
2055
|
const body = document.createElement("div");
|
|
1814
2056
|
body.className = "remarq-popup-body";
|
|
1815
2057
|
const makeCommentEl = () => {
|
|
@@ -1821,38 +2063,10 @@ var Popup = class {
|
|
|
1821
2063
|
return el;
|
|
1822
2064
|
};
|
|
1823
2065
|
body.appendChild(makeCommentEl());
|
|
2066
|
+
body.appendChild(this.buildLifecycleViewer(info.lifecycle));
|
|
1824
2067
|
const actions = document.createElement("div");
|
|
1825
2068
|
actions.className = "remarq-popup-actions";
|
|
1826
|
-
|
|
1827
|
-
const resolveBtn = document.createElement("button");
|
|
1828
|
-
resolveBtn.className = "remarq-primary";
|
|
1829
|
-
resolveBtn.textContent = "Resolve";
|
|
1830
|
-
resolveBtn.addEventListener("click", () => {
|
|
1831
|
-
this.hide();
|
|
1832
|
-
callbacks.onResolve();
|
|
1833
|
-
});
|
|
1834
|
-
actions.appendChild(resolveBtn);
|
|
1835
|
-
}
|
|
1836
|
-
const copyBtn = document.createElement("button");
|
|
1837
|
-
copyBtn.textContent = "Copy";
|
|
1838
|
-
copyBtn.addEventListener("click", () => {
|
|
1839
|
-
callbacks.onCopy();
|
|
1840
|
-
});
|
|
1841
|
-
actions.appendChild(copyBtn);
|
|
1842
|
-
const deleteBtn = document.createElement("button");
|
|
1843
|
-
deleteBtn.textContent = "Delete";
|
|
1844
|
-
deleteBtn.addEventListener("click", () => {
|
|
1845
|
-
this.hide();
|
|
1846
|
-
callbacks.onDelete();
|
|
1847
|
-
});
|
|
1848
|
-
actions.appendChild(deleteBtn);
|
|
1849
|
-
const closeBtn = document.createElement("button");
|
|
1850
|
-
closeBtn.textContent = "Close";
|
|
1851
|
-
closeBtn.addEventListener("click", () => {
|
|
1852
|
-
this.hide();
|
|
1853
|
-
callbacks.onClose();
|
|
1854
|
-
});
|
|
1855
|
-
actions.appendChild(closeBtn);
|
|
2069
|
+
this.renderActionButtons(actions, info, callbacks);
|
|
1856
2070
|
popup2.appendChild(header);
|
|
1857
2071
|
popup2.appendChild(body);
|
|
1858
2072
|
popup2.appendChild(actions);
|
|
@@ -1879,7 +2093,103 @@ var Popup = class {
|
|
|
1879
2093
|
document.addEventListener("mousedown", this.outsideClickHandler);
|
|
1880
2094
|
}, 0);
|
|
1881
2095
|
}
|
|
2096
|
+
buildLifecycleViewer(lifecycle) {
|
|
2097
|
+
var _a3, _b;
|
|
2098
|
+
const details = document.createElement("details");
|
|
2099
|
+
details.className = "remarq-popup-history";
|
|
2100
|
+
const summary = document.createElement("summary");
|
|
2101
|
+
summary.textContent = `History (${lifecycle.length})`;
|
|
2102
|
+
details.appendChild(summary);
|
|
2103
|
+
const list = document.createElement("ul");
|
|
2104
|
+
list.className = "remarq-popup-history-list";
|
|
2105
|
+
for (const ev of lifecycle) {
|
|
2106
|
+
const li = document.createElement("li");
|
|
2107
|
+
const when = new Date(ev.timestamp).toLocaleString();
|
|
2108
|
+
const who = (_a3 = ev.actor) != null ? _a3 : "system";
|
|
2109
|
+
const what = (_b = EVENT_LABEL[ev.type]) != null ? _b : ev.type;
|
|
2110
|
+
let text = `${when} \xB7 ${who} \xB7 ${what}`;
|
|
2111
|
+
if (ev.reason) text += ` \u2014 ${ev.reason}`;
|
|
2112
|
+
li.textContent = text;
|
|
2113
|
+
list.appendChild(li);
|
|
2114
|
+
}
|
|
2115
|
+
details.appendChild(list);
|
|
2116
|
+
return details;
|
|
2117
|
+
}
|
|
2118
|
+
renderActionButtons(container, info, callbacks) {
|
|
2119
|
+
container.replaceChildren();
|
|
2120
|
+
const transitions = document.createElement("div");
|
|
2121
|
+
transitions.className = "remarq-popup-actions-row remarq-popup-actions-row--transitions";
|
|
2122
|
+
for (const def of actionsForStatus(info.status)) {
|
|
2123
|
+
const btn = document.createElement("button");
|
|
2124
|
+
btn.textContent = def.label;
|
|
2125
|
+
if (def.primary) btn.className = "remarq-primary";
|
|
2126
|
+
btn.addEventListener("click", () => {
|
|
2127
|
+
if (def.needsReason) {
|
|
2128
|
+
this.showReasonInput(container, info, callbacks, def);
|
|
2129
|
+
} else {
|
|
2130
|
+
this.hide();
|
|
2131
|
+
callbacks.onTransition(def.action);
|
|
2132
|
+
}
|
|
2133
|
+
});
|
|
2134
|
+
transitions.appendChild(btn);
|
|
2135
|
+
}
|
|
2136
|
+
container.appendChild(transitions);
|
|
2137
|
+
const utility = document.createElement("div");
|
|
2138
|
+
utility.className = "remarq-popup-actions-row remarq-popup-actions-row--utility";
|
|
2139
|
+
const copyBtn = document.createElement("button");
|
|
2140
|
+
copyBtn.className = "remarq-popup-utility-btn";
|
|
2141
|
+
copyBtn.textContent = "Copy";
|
|
2142
|
+
copyBtn.addEventListener("click", () => callbacks.onCopy());
|
|
2143
|
+
utility.appendChild(copyBtn);
|
|
2144
|
+
const deleteBtn = document.createElement("button");
|
|
2145
|
+
deleteBtn.className = "remarq-popup-utility-btn";
|
|
2146
|
+
deleteBtn.textContent = "Delete";
|
|
2147
|
+
deleteBtn.addEventListener("click", () => {
|
|
2148
|
+
this.hide();
|
|
2149
|
+
callbacks.onDelete();
|
|
2150
|
+
});
|
|
2151
|
+
utility.appendChild(deleteBtn);
|
|
2152
|
+
const closeBtn = document.createElement("button");
|
|
2153
|
+
closeBtn.className = "remarq-popup-utility-btn";
|
|
2154
|
+
closeBtn.textContent = "Close";
|
|
2155
|
+
closeBtn.addEventListener("click", () => {
|
|
2156
|
+
this.hide();
|
|
2157
|
+
callbacks.onClose();
|
|
2158
|
+
});
|
|
2159
|
+
utility.appendChild(closeBtn);
|
|
2160
|
+
container.appendChild(utility);
|
|
2161
|
+
}
|
|
2162
|
+
showReasonInput(container, info, callbacks, def) {
|
|
2163
|
+
container.replaceChildren();
|
|
2164
|
+
const textarea = document.createElement("textarea");
|
|
2165
|
+
textarea.placeholder = `Reason for ${def.label.toLowerCase()} (optional)\u2026`;
|
|
2166
|
+
textarea.className = "remarq-popup-reason";
|
|
2167
|
+
container.appendChild(textarea);
|
|
2168
|
+
const row = document.createElement("div");
|
|
2169
|
+
row.className = "remarq-popup-reason-row";
|
|
2170
|
+
const cancel = document.createElement("button");
|
|
2171
|
+
cancel.textContent = "Cancel";
|
|
2172
|
+
cancel.addEventListener("click", () => {
|
|
2173
|
+
this.renderActionButtons(container, info, callbacks);
|
|
2174
|
+
});
|
|
2175
|
+
const submit = document.createElement("button");
|
|
2176
|
+
submit.className = "remarq-primary";
|
|
2177
|
+
submit.textContent = "Submit";
|
|
2178
|
+
submit.addEventListener("click", () => {
|
|
2179
|
+
const reason = textarea.value.trim() || void 0;
|
|
2180
|
+
this.hide();
|
|
2181
|
+
callbacks.onTransition(def.action, reason);
|
|
2182
|
+
});
|
|
2183
|
+
row.appendChild(cancel);
|
|
2184
|
+
row.appendChild(submit);
|
|
2185
|
+
container.appendChild(row);
|
|
2186
|
+
textarea.focus();
|
|
2187
|
+
}
|
|
1882
2188
|
hide() {
|
|
2189
|
+
if (this.pendingEditFlush) {
|
|
2190
|
+
this.pendingEditFlush();
|
|
2191
|
+
this.pendingEditFlush = null;
|
|
2192
|
+
}
|
|
1883
2193
|
if (this.popupEl) {
|
|
1884
2194
|
this.popupEl.remove();
|
|
1885
2195
|
this.popupEl = null;
|
|
@@ -1914,12 +2224,8 @@ var Popup = class {
|
|
|
1914
2224
|
commentEl.replaceWith(textarea);
|
|
1915
2225
|
textarea.focus();
|
|
1916
2226
|
textarea.selectionStart = textarea.value.length;
|
|
1917
|
-
const
|
|
1918
|
-
|
|
1919
|
-
if (newComment && newComment !== info.comment) {
|
|
1920
|
-
info.comment = newComment;
|
|
1921
|
-
callbacks.onEdit(newComment);
|
|
1922
|
-
}
|
|
2227
|
+
const restoreView = () => {
|
|
2228
|
+
if (!textarea.isConnected) return;
|
|
1923
2229
|
const restored = document.createElement("div");
|
|
1924
2230
|
restored.textContent = info.comment;
|
|
1925
2231
|
restored.style.cursor = "pointer";
|
|
@@ -1927,26 +2233,33 @@ var Popup = class {
|
|
|
1927
2233
|
restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
|
|
1928
2234
|
textarea.replaceWith(restored);
|
|
1929
2235
|
};
|
|
2236
|
+
const commitEdit = () => {
|
|
2237
|
+
this.pendingEditFlush = null;
|
|
2238
|
+
const newComment = textarea.value.trim();
|
|
2239
|
+
if (newComment && newComment !== info.comment) {
|
|
2240
|
+
info.comment = newComment;
|
|
2241
|
+
callbacks.onEdit(newComment);
|
|
2242
|
+
}
|
|
2243
|
+
restoreView();
|
|
2244
|
+
};
|
|
2245
|
+
const cancelEdit = () => {
|
|
2246
|
+
this.pendingEditFlush = null;
|
|
2247
|
+
restoreView();
|
|
2248
|
+
};
|
|
2249
|
+
this.pendingEditFlush = commitEdit;
|
|
1930
2250
|
textarea.addEventListener("keydown", (e) => {
|
|
1931
2251
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
1932
2252
|
e.preventDefault();
|
|
1933
|
-
|
|
2253
|
+
commitEdit();
|
|
1934
2254
|
}
|
|
1935
2255
|
if (e.key === "Escape") {
|
|
1936
2256
|
e.stopPropagation();
|
|
1937
|
-
|
|
1938
|
-
restored.textContent = info.comment;
|
|
1939
|
-
restored.style.cursor = "pointer";
|
|
1940
|
-
restored.title = "Click to edit";
|
|
1941
|
-
restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
|
|
1942
|
-
textarea.replaceWith(restored);
|
|
2257
|
+
cancelEdit();
|
|
1943
2258
|
}
|
|
1944
2259
|
});
|
|
1945
2260
|
textarea.addEventListener("blur", () => {
|
|
1946
2261
|
setTimeout(() => {
|
|
1947
|
-
if (textarea.isConnected)
|
|
1948
|
-
saveEdit();
|
|
1949
|
-
}
|
|
2262
|
+
if (textarea.isConnected) commitEdit();
|
|
1950
2263
|
}, 50);
|
|
1951
2264
|
});
|
|
1952
2265
|
}
|
|
@@ -1974,6 +2287,16 @@ var Popup = class {
|
|
|
1974
2287
|
};
|
|
1975
2288
|
|
|
1976
2289
|
// src/ui/markers.ts
|
|
2290
|
+
var STATUS_COLOR = {
|
|
2291
|
+
pending: "var(--remarq-status-pending)",
|
|
2292
|
+
in_progress: "var(--remarq-status-in-progress)",
|
|
2293
|
+
fixed_unverified: "var(--remarq-status-fixed-unverified)",
|
|
2294
|
+
verified: "var(--remarq-status-verified)",
|
|
2295
|
+
dismissed: "var(--remarq-status-dismissed)"
|
|
2296
|
+
};
|
|
2297
|
+
function statusClass(status) {
|
|
2298
|
+
return `remarq-marker--${status.replace("_", "-")}`;
|
|
2299
|
+
}
|
|
1977
2300
|
var MarkerManager = class {
|
|
1978
2301
|
constructor(container, onClick) {
|
|
1979
2302
|
this.container = container;
|
|
@@ -1986,7 +2309,7 @@ var MarkerManager = class {
|
|
|
1986
2309
|
addMarker(annotation, target) {
|
|
1987
2310
|
this.counter++;
|
|
1988
2311
|
const markerEl = document.createElement("div");
|
|
1989
|
-
markerEl.className =
|
|
2312
|
+
markerEl.className = `remarq-marker ${statusClass(annotation.status)}`;
|
|
1990
2313
|
markerEl.setAttribute("data-status", annotation.status);
|
|
1991
2314
|
markerEl.setAttribute("data-annotation-id", annotation.id);
|
|
1992
2315
|
markerEl.textContent = String(this.counter);
|
|
@@ -2012,7 +2335,17 @@ var MarkerManager = class {
|
|
|
2012
2335
|
const entry = this.markers.get(id);
|
|
2013
2336
|
if (entry) {
|
|
2014
2337
|
entry.annotation.status = status;
|
|
2338
|
+
entry.markerEl.className = `remarq-marker ${statusClass(status)}`;
|
|
2015
2339
|
entry.markerEl.setAttribute("data-status", status);
|
|
2340
|
+
this.applyOutline(entry.target, status);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
scrollToMarker(id) {
|
|
2344
|
+
const entry = this.markers.get(id);
|
|
2345
|
+
if (!entry) return;
|
|
2346
|
+
try {
|
|
2347
|
+
entry.target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
2348
|
+
} catch (e) {
|
|
2016
2349
|
}
|
|
2017
2350
|
}
|
|
2018
2351
|
clear() {
|
|
@@ -2031,7 +2364,7 @@ var MarkerManager = class {
|
|
|
2031
2364
|
this.clear();
|
|
2032
2365
|
}
|
|
2033
2366
|
applyOutline(target, status) {
|
|
2034
|
-
const color = status
|
|
2367
|
+
const color = STATUS_COLOR[status];
|
|
2035
2368
|
target.style.outline = `2px solid ${color}`;
|
|
2036
2369
|
target.style.outlineOffset = "2px";
|
|
2037
2370
|
}
|
|
@@ -2378,8 +2711,18 @@ function refreshMarkers() {
|
|
|
2378
2711
|
markers.addMarker(ann, el);
|
|
2379
2712
|
}
|
|
2380
2713
|
detachedPanel.update(otherBreakpoint, detached);
|
|
2381
|
-
const
|
|
2382
|
-
|
|
2714
|
+
const needsAttention = anns.filter(
|
|
2715
|
+
(a) => a.status === "pending" || a.status === "in_progress"
|
|
2716
|
+
).length;
|
|
2717
|
+
const needsVerification = anns.filter((a) => a.status === "fixed_unverified").length;
|
|
2718
|
+
toolbar.setBadgeCount(needsAttention);
|
|
2719
|
+
toolbar.setVerificationBadgeCount(needsVerification);
|
|
2720
|
+
}
|
|
2721
|
+
function jumpToFirstUnverified() {
|
|
2722
|
+
if (!storage || !markers) return;
|
|
2723
|
+
const ann = storage.getByRoute(currentRoute()).find((a) => a.status === "fixed_unverified");
|
|
2724
|
+
if (!ann) return;
|
|
2725
|
+
markers.scrollToMarker(ann.id);
|
|
2383
2726
|
}
|
|
2384
2727
|
function scheduleRefresh() {
|
|
2385
2728
|
if (refreshScheduled) return;
|
|
@@ -2412,6 +2755,7 @@ function handleInspectClick(e) {
|
|
|
2412
2755
|
classFilter: options.classFilter,
|
|
2413
2756
|
dataAttribute: options.dataAttribute
|
|
2414
2757
|
});
|
|
2758
|
+
const now = Date.now();
|
|
2415
2759
|
const ann = {
|
|
2416
2760
|
id: generateId(),
|
|
2417
2761
|
comment,
|
|
@@ -2419,8 +2763,9 @@ function handleInspectClick(e) {
|
|
|
2419
2763
|
route: currentRoute(),
|
|
2420
2764
|
viewport: `${window.innerWidth}x${window.innerHeight}`,
|
|
2421
2765
|
viewportBucket: toBucket(window.innerWidth),
|
|
2422
|
-
timestamp:
|
|
2423
|
-
status: "pending"
|
|
2766
|
+
timestamp: now,
|
|
2767
|
+
status: "pending",
|
|
2768
|
+
lifecycle: [{ type: "created", actor: "designer", timestamp: now }]
|
|
2424
2769
|
};
|
|
2425
2770
|
cacheElement(ann.id, target);
|
|
2426
2771
|
storage.add(ann);
|
|
@@ -2512,7 +2857,8 @@ function handleMarkerClick(annotationId) {
|
|
|
2512
2857
|
tag: ann.fingerprint.tagName,
|
|
2513
2858
|
text: (_a3 = ann.fingerprint.textContent) != null ? _a3 : "",
|
|
2514
2859
|
comment: ann.comment,
|
|
2515
|
-
status: ann.status
|
|
2860
|
+
status: ann.status,
|
|
2861
|
+
lifecycle: ann.lifecycle
|
|
2516
2862
|
},
|
|
2517
2863
|
{
|
|
2518
2864
|
top: window.scrollY + rect.bottom + 8,
|
|
@@ -2520,9 +2866,8 @@ function handleMarkerClick(annotationId) {
|
|
|
2520
2866
|
anchorBottom: window.scrollY + rect.top - 8
|
|
2521
2867
|
},
|
|
2522
2868
|
{
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
refreshMarkers();
|
|
2869
|
+
onTransition: (action, reason) => {
|
|
2870
|
+
applyTransition(ann.id, action, reason ? { reason } : void 0);
|
|
2526
2871
|
},
|
|
2527
2872
|
onDelete: () => {
|
|
2528
2873
|
elementCache.delete(ann.id);
|
|
@@ -2536,12 +2881,14 @@ function handleMarkerClick(annotationId) {
|
|
|
2536
2881
|
refreshMarkers();
|
|
2537
2882
|
},
|
|
2538
2883
|
onCopy: () => {
|
|
2539
|
-
|
|
2884
|
+
var _a4;
|
|
2885
|
+
const fresh = (_a4 = storage.getById(ann.id)) != null ? _a4 : ann;
|
|
2886
|
+
const fp = fresh.fingerprint;
|
|
2540
2887
|
const lines = [
|
|
2541
|
-
`[${
|
|
2888
|
+
`[${fresh.status}] "${fresh.comment}"`,
|
|
2542
2889
|
`Element: <${fp.tagName}>${fp.textContent ? ` "${fp.textContent}"` : ""}`,
|
|
2543
|
-
`Route: ${
|
|
2544
|
-
`Viewport: ${
|
|
2890
|
+
`Route: ${fresh.route}`,
|
|
2891
|
+
`Viewport: ${fresh.viewportBucket}px`
|
|
2545
2892
|
];
|
|
2546
2893
|
if (fp.sourceLocation) lines.push(`Source: ${fp.sourceLocation}`);
|
|
2547
2894
|
navigator.clipboard.writeText(lines.join("\n")).then(() => {
|
|
@@ -2676,6 +3023,16 @@ function copyAgentToClipboard() {
|
|
|
2676
3023
|
console.warn("[web-remarq] Clipboard write failed");
|
|
2677
3024
|
});
|
|
2678
3025
|
}
|
|
3026
|
+
function applyTransition(id, action, opts = {}) {
|
|
3027
|
+
if (!storage) return;
|
|
3028
|
+
const ann = storage.getById(id);
|
|
3029
|
+
if (!ann) return;
|
|
3030
|
+
const { status, event } = transition(ann, action, opts);
|
|
3031
|
+
const lifecycle = [...ann.lifecycle, event];
|
|
3032
|
+
storage.update(id, { status, lifecycle });
|
|
3033
|
+
markers == null ? void 0 : markers.updateStatus(id, status);
|
|
3034
|
+
refreshMarkers();
|
|
3035
|
+
}
|
|
2679
3036
|
function setupMutationObserver() {
|
|
2680
3037
|
mutationObserver = new MutationObserver((mutations) => {
|
|
2681
3038
|
let hasExternalMutation = false;
|
|
@@ -2737,7 +3094,8 @@ var WebRemarq = {
|
|
|
2737
3094
|
showToast(themeManager.container, "All annotations cleared");
|
|
2738
3095
|
},
|
|
2739
3096
|
onThemeToggle: () => themeManager.toggle(),
|
|
2740
|
-
onHelp: () => showShortcutsModal(themeManager.container)
|
|
3097
|
+
onHelp: () => showShortcutsModal(themeManager.container),
|
|
3098
|
+
onVerificationBadgeClick: jumpToFirstUnverified
|
|
2741
3099
|
}, position);
|
|
2742
3100
|
routeObserver = new RouteObserver();
|
|
2743
3101
|
unsubRoute = routeObserver.onChange(() => refreshMarkers());
|
|
@@ -2831,6 +3189,28 @@ var WebRemarq = {
|
|
|
2831
3189
|
elementCache.clear();
|
|
2832
3190
|
storage == null ? void 0 : storage.clearAll();
|
|
2833
3191
|
if (initialized) refreshMarkers();
|
|
3192
|
+
},
|
|
3193
|
+
acknowledge(id, opts) {
|
|
3194
|
+
applyTransition(id, "acknowledge", opts);
|
|
3195
|
+
},
|
|
3196
|
+
claimFix(id, opts) {
|
|
3197
|
+
applyTransition(id, "claimFix", opts);
|
|
3198
|
+
},
|
|
3199
|
+
verify(id, opts) {
|
|
3200
|
+
applyTransition(id, "verify", opts);
|
|
3201
|
+
},
|
|
3202
|
+
reject(id, opts) {
|
|
3203
|
+
applyTransition(id, "reject", opts);
|
|
3204
|
+
},
|
|
3205
|
+
dismiss(id, opts) {
|
|
3206
|
+
applyTransition(id, "dismiss", opts);
|
|
3207
|
+
},
|
|
3208
|
+
reopen(id, opts) {
|
|
3209
|
+
applyTransition(id, "reopen", opts);
|
|
3210
|
+
},
|
|
3211
|
+
/** @deprecated Use verify() instead. */
|
|
3212
|
+
markResolved(id) {
|
|
3213
|
+
applyTransition(id, "verify");
|
|
2834
3214
|
}
|
|
2835
3215
|
};
|
|
2836
3216
|
// Annotate the CommonJS export names for ESM import in node:
|