@impakers/debug 1.2.1 → 1.3.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/dist/react.js CHANGED
@@ -1843,12 +1843,13 @@ async function fetchComments(endpoint, taskId) {
1843
1843
  setCache(cacheKey, comments);
1844
1844
  return comments;
1845
1845
  }
1846
- async function postComment(endpoint, taskId, content, authorName, authorId) {
1846
+ async function postComment(endpoint, taskId, content, authorName, authorId, screenshot) {
1847
1847
  const tempComment = {
1848
1848
  id: `temp-${Date.now()}`,
1849
1849
  content,
1850
1850
  authorName,
1851
1851
  authorId,
1852
+ imageUrl: screenshot ? "loading..." : void 0,
1852
1853
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1853
1854
  };
1854
1855
  const cacheKey = `comments:${taskId}`;
@@ -1856,7 +1857,7 @@ async function postComment(endpoint, taskId, content, authorName, authorId) {
1856
1857
  setCache(cacheKey, [...existing, tempComment]);
1857
1858
  const res = await apiFetch(`${endpoint}/${taskId}/comments`, {
1858
1859
  method: "POST",
1859
- body: JSON.stringify({ content, authorName, authorId })
1860
+ body: JSON.stringify({ content, authorName, authorId, screenshot })
1860
1861
  });
1861
1862
  const data = await res.json();
1862
1863
  const serverComment = data.comment;
@@ -1867,6 +1868,13 @@ async function postComment(endpoint, taskId, content, authorName, authorId) {
1867
1868
  invalidateCache("feedbacks:");
1868
1869
  return serverComment;
1869
1870
  }
1871
+ async function updateTaskStatus(endpoint, taskId, status) {
1872
+ await apiFetch(`${endpoint}/${taskId}/status`, {
1873
+ method: "PATCH",
1874
+ body: JSON.stringify({ status })
1875
+ });
1876
+ invalidateCache("feedbacks:");
1877
+ }
1870
1878
 
1871
1879
  // src/core/sourcemap-resolver.ts
1872
1880
  var import_trace_mapping = require("@jridgewell/trace-mapping");
@@ -2017,8 +2025,8 @@ function PendingMarker({ x, y, accentColor }) {
2017
2025
  var import_react3 = require("react");
2018
2026
 
2019
2027
  // src/components/comment-thread/styles.module.scss
2020
- var css3 = '@keyframes styles-module__threadIn___pBTFZ {\n from {\n opacity: 0;\n transform: scale(0.96) translateY(4px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n@keyframes styles-module__threadOut___-ccKh {\n from {\n opacity: 1;\n transform: scale(1);\n }\n to {\n opacity: 0;\n transform: scale(0.96);\n }\n}\n.styles-module__thread___ua2EO {\n position: fixed;\n width: 340px;\n max-height: 480px;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n z-index: 100002;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n font-size: 13px;\n color: #1a1a1a;\n animation: styles-module__threadIn___pBTFZ 0.15s ease-out;\n -webkit-font-smoothing: antialiased;\n}\n.styles-module__thread___ua2EO.styles-module__exiting___RIPeX {\n animation: styles-module__threadOut___-ccKh 0.12s ease-in forwards;\n pointer-events: none;\n}\n\n.styles-module__header___GiEBq {\n padding: 14px 16px 10px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.styles-module__avatar___JElAd {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #16a34a;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 11px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.styles-module__headerInfo___E8809 {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__headerTop___eDiCd {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 4px;\n}\n\n.styles-module__authorName___T1BfB {\n font-size: 13px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.styles-module__timestamp___WusBf {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__headerActions___8FsMY {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n}\n\n.styles-module__headerAction___Tinmq {\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__headerAction___Tinmq:hover {\n background: #f3f4f6;\n color: #374151;\n}\n.styles-module__headerAction___Tinmq svg {\n width: 16px;\n height: 16px;\n}\n\n.styles-module__title___qkfqY {\n font-size: 13px;\n color: #1a1a1a;\n line-height: 1.5;\n word-break: break-word;\n}\n\n.styles-module__commentsList___kYqAR {\n flex: 1;\n overflow-y: auto;\n padding: 0;\n}\n.styles-module__commentsList___kYqAR::-webkit-scrollbar {\n width: 4px;\n}\n.styles-module__commentsList___kYqAR::-webkit-scrollbar-thumb {\n background: #d1d5db;\n border-radius: 2px;\n}\n\n.styles-module__comment___pW3IO {\n padding: 10px 16px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n.styles-module__comment___pW3IO:hover {\n background: #f9fafb;\n}\n.styles-module__comment___pW3IO:hover .styles-module__commentActions___wO0fs {\n opacity: 1;\n}\n\n.styles-module__commentHighlight___EiTmx {\n background: #eff6ff;\n border-left: 2px solid #3b82f6;\n padding-left: 14px;\n}\n\n.styles-module__commentContent___RObGr {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__commentTop___BbTF3 {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 2px;\n}\n\n.styles-module__commentAuthor___tBjpl {\n font-size: 13px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.styles-module__commentTime___0OLrz {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__commentActions___wO0fs {\n display: flex;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.12s;\n}\n\n.styles-module__commentText___ldy2V {\n font-size: 13px;\n color: #374151;\n line-height: 1.5;\n word-break: break-word;\n white-space: pre-wrap;\n}\n\n.styles-module__screenshot___jUqau {\n margin-top: 8px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n position: relative;\n max-width: 200px;\n}\n.styles-module__screenshot___jUqau img {\n width: 100%;\n height: auto;\n display: block;\n}\n.styles-module__screenshot___jUqau .styles-module__screenshotBadge___roEqY {\n position: absolute;\n top: 6px;\n right: 6px;\n background: rgba(0, 0, 0, 0.6);\n color: #fff;\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 500;\n}\n\n.styles-module__divider___kDjxN {\n height: 1px;\n background: #f3f4f6;\n margin: 0;\n}\n\n.styles-module__replyArea___VQn9b {\n padding: 10px 16px 8px;\n border-top: 1px solid #f3f4f6;\n}\n\n.styles-module__replyInput___uZNBW {\n width: 100%;\n border: none;\n outline: none;\n font-size: 13px;\n font-family: inherit;\n color: #1a1a1a;\n resize: none;\n padding: 0;\n min-height: 20px;\n max-height: 80px;\n line-height: 1.5;\n}\n.styles-module__replyInput___uZNBW::placeholder {\n color: #9ca3af;\n}\n\n.styles-module__replyToolbar___fTFJU {\n display: flex;\n align-items: center;\n padding: 4px 16px 10px;\n gap: 4px;\n}\n\n.styles-module__replyTool___Ho-Rx {\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__replyTool___Ho-Rx:hover {\n background: #f3f4f6;\n color: #374151;\n}\n.styles-module__replyTool___Ho-Rx svg {\n width: 16px;\n height: 16px;\n}\n\n.styles-module__replySend___e0VSb {\n margin-left: auto;\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__replySend___e0VSb:hover {\n color: #3b82f6;\n}\n.styles-module__replySend___e0VSb.styles-module__active___R--Jj {\n color: #3b82f6;\n}\n.styles-module__replySend___e0VSb svg {\n width: 16px;\n height: 16px;\n}\n\n.styles-module__empty___XXGiw {\n padding: 24px 16px;\n text-align: center;\n color: #9ca3af;\n font-size: 13px;\n}\n\n.styles-module__statusBadge___el8ml {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n margin-left: auto;\n}\n.styles-module__statusBadge___el8ml.styles-module__todo___rUWBr {\n background: #fef3c7;\n color: #92400e;\n}\n.styles-module__statusBadge___el8ml.styles-module__inProgress___HEWgU {\n background: #dbeafe;\n color: #1e40af;\n}\n.styles-module__statusBadge___el8ml.styles-module__done___zC12n {\n background: #dcfce7;\n color: #166534;\n}';
2021
- var classNames3 = { "thread": "styles-module__thread___ua2EO", "threadIn": "styles-module__threadIn___pBTFZ", "exiting": "styles-module__exiting___RIPeX", "threadOut": "styles-module__threadOut___-ccKh", "header": "styles-module__header___GiEBq", "avatar": "styles-module__avatar___JElAd", "headerInfo": "styles-module__headerInfo___E8809", "headerTop": "styles-module__headerTop___eDiCd", "authorName": "styles-module__authorName___T1BfB", "timestamp": "styles-module__timestamp___WusBf", "headerActions": "styles-module__headerActions___8FsMY", "headerAction": "styles-module__headerAction___Tinmq", "title": "styles-module__title___qkfqY", "commentsList": "styles-module__commentsList___kYqAR", "comment": "styles-module__comment___pW3IO", "commentActions": "styles-module__commentActions___wO0fs", "commentHighlight": "styles-module__commentHighlight___EiTmx", "commentContent": "styles-module__commentContent___RObGr", "commentTop": "styles-module__commentTop___BbTF3", "commentAuthor": "styles-module__commentAuthor___tBjpl", "commentTime": "styles-module__commentTime___0OLrz", "commentText": "styles-module__commentText___ldy2V", "screenshot": "styles-module__screenshot___jUqau", "screenshotBadge": "styles-module__screenshotBadge___roEqY", "divider": "styles-module__divider___kDjxN", "replyArea": "styles-module__replyArea___VQn9b", "replyInput": "styles-module__replyInput___uZNBW", "replyToolbar": "styles-module__replyToolbar___fTFJU", "replyTool": "styles-module__replyTool___Ho-Rx", "replySend": "styles-module__replySend___e0VSb", "active": "styles-module__active___R--Jj", "empty": "styles-module__empty___XXGiw", "statusBadge": "styles-module__statusBadge___el8ml", "todo": "styles-module__todo___rUWBr", "inProgress": "styles-module__inProgress___HEWgU", "done": "styles-module__done___zC12n" };
2028
+ var css3 = '@keyframes styles-module__threadIn___pBTFZ {\n from {\n opacity: 0;\n transform: scale(0.96) translateY(4px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n@keyframes styles-module__threadOut___-ccKh {\n from {\n opacity: 1;\n transform: scale(1);\n }\n to {\n opacity: 0;\n transform: scale(0.96);\n }\n}\n.styles-module__thread___ua2EO {\n position: fixed;\n width: 340px;\n max-height: 480px;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n z-index: 100002;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n font-size: 13px;\n color: #1a1a1a;\n animation: styles-module__threadIn___pBTFZ 0.15s ease-out;\n -webkit-font-smoothing: antialiased;\n}\n.styles-module__thread___ua2EO.styles-module__exiting___RIPeX {\n animation: styles-module__threadOut___-ccKh 0.12s ease-in forwards;\n pointer-events: none;\n}\n\n.styles-module__header___GiEBq {\n padding: 14px 16px 10px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.styles-module__avatar___JElAd {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #16a34a;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 11px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.styles-module__headerInfo___E8809 {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__headerTop___eDiCd {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 4px;\n}\n\n.styles-module__authorName___T1BfB {\n font-size: 13px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.styles-module__timestamp___WusBf {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__headerActions___8FsMY {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n}\n\n.styles-module__headerAction___Tinmq {\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__headerAction___Tinmq:hover {\n background: #f3f4f6;\n color: #374151;\n}\n.styles-module__headerAction___Tinmq svg {\n width: 16px;\n height: 16px;\n}\n\n.styles-module__title___qkfqY {\n font-size: 13px;\n color: #1a1a1a;\n line-height: 1.5;\n word-break: break-word;\n}\n\n.styles-module__commentsList___kYqAR {\n flex: 1;\n overflow-y: auto;\n padding: 0;\n}\n.styles-module__commentsList___kYqAR::-webkit-scrollbar {\n width: 4px;\n}\n.styles-module__commentsList___kYqAR::-webkit-scrollbar-thumb {\n background: #d1d5db;\n border-radius: 2px;\n}\n\n.styles-module__comment___pW3IO {\n padding: 10px 16px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n.styles-module__comment___pW3IO:hover {\n background: #f9fafb;\n}\n.styles-module__comment___pW3IO:hover .styles-module__commentActions___wO0fs {\n opacity: 1;\n}\n\n.styles-module__commentHighlight___EiTmx {\n background: #eff6ff;\n border-left: 2px solid #3b82f6;\n padding-left: 14px;\n}\n\n.styles-module__commentContent___RObGr {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__commentTop___BbTF3 {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 2px;\n}\n\n.styles-module__commentAuthor___tBjpl {\n font-size: 13px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.styles-module__commentTime___0OLrz {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__commentActions___wO0fs {\n display: flex;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.12s;\n}\n\n.styles-module__commentText___ldy2V {\n font-size: 13px;\n color: #374151;\n line-height: 1.5;\n word-break: break-word;\n white-space: pre-wrap;\n}\n\n.styles-module__screenshot___jUqau {\n margin-top: 8px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n position: relative;\n max-width: 200px;\n}\n.styles-module__screenshot___jUqau img {\n width: 100%;\n height: auto;\n display: block;\n}\n.styles-module__screenshot___jUqau .styles-module__screenshotBadge___roEqY {\n position: absolute;\n top: 6px;\n right: 6px;\n background: rgba(0, 0, 0, 0.6);\n color: #fff;\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 500;\n}\n\n.styles-module__divider___kDjxN {\n height: 1px;\n background: #f3f4f6;\n margin: 0;\n}\n\n.styles-module__pendingImage___gHaF3 {\n position: relative;\n margin: 0 16px 8px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n max-height: 120px;\n}\n.styles-module__pendingImage___gHaF3 img {\n width: 100%;\n height: auto;\n display: block;\n object-fit: cover;\n max-height: 120px;\n}\n\n.styles-module__pendingRemove___FRL4h {\n position: absolute;\n top: 4px;\n right: 4px;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: rgba(0, 0, 0, 0.6);\n color: #fff;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n line-height: 1;\n padding: 0;\n}\n.styles-module__pendingRemove___FRL4h:hover {\n background: rgba(0, 0, 0, 0.8);\n}\n\n.styles-module__replyArea___VQn9b {\n padding: 10px 16px 8px;\n border-top: 1px solid #f3f4f6;\n}\n\n.styles-module__replyInput___uZNBW {\n width: 100%;\n border: none;\n outline: none;\n font-size: 13px;\n font-family: inherit;\n color: #1a1a1a;\n resize: none;\n padding: 0;\n min-height: 20px;\n max-height: 80px;\n line-height: 1.5;\n}\n.styles-module__replyInput___uZNBW::placeholder {\n color: #9ca3af;\n}\n\n.styles-module__replyToolbar___fTFJU {\n display: flex;\n align-items: center;\n padding: 4px 16px 10px;\n gap: 4px;\n}\n\n.styles-module__replyTool___Ho-Rx {\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__replyTool___Ho-Rx:hover {\n background: #f3f4f6;\n color: #374151;\n}\n.styles-module__replyTool___Ho-Rx svg {\n width: 16px;\n height: 16px;\n}\n\n.styles-module__replySend___e0VSb {\n margin-left: auto;\n width: 28px;\n height: 28px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__replySend___e0VSb:hover {\n color: #3b82f6;\n}\n.styles-module__replySend___e0VSb.styles-module__active___R--Jj {\n color: #3b82f6;\n}\n.styles-module__replySend___e0VSb svg {\n width: 16px;\n height: 16px;\n}\n\n.styles-module__empty___XXGiw {\n padding: 24px 16px;\n text-align: center;\n color: #9ca3af;\n font-size: 13px;\n}\n\n.styles-module__statusBadge___el8ml {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n margin-left: auto;\n}\n.styles-module__statusBadge___el8ml.styles-module__todo___rUWBr {\n background: #fef3c7;\n color: #92400e;\n}\n.styles-module__statusBadge___el8ml.styles-module__inProgress___HEWgU {\n background: #dbeafe;\n color: #1e40af;\n}\n.styles-module__statusBadge___el8ml.styles-module__done___zC12n {\n background: #dcfce7;\n color: #166534;\n}';
2029
+ var classNames3 = { "thread": "styles-module__thread___ua2EO", "threadIn": "styles-module__threadIn___pBTFZ", "exiting": "styles-module__exiting___RIPeX", "threadOut": "styles-module__threadOut___-ccKh", "header": "styles-module__header___GiEBq", "avatar": "styles-module__avatar___JElAd", "headerInfo": "styles-module__headerInfo___E8809", "headerTop": "styles-module__headerTop___eDiCd", "authorName": "styles-module__authorName___T1BfB", "timestamp": "styles-module__timestamp___WusBf", "headerActions": "styles-module__headerActions___8FsMY", "headerAction": "styles-module__headerAction___Tinmq", "title": "styles-module__title___qkfqY", "commentsList": "styles-module__commentsList___kYqAR", "comment": "styles-module__comment___pW3IO", "commentActions": "styles-module__commentActions___wO0fs", "commentHighlight": "styles-module__commentHighlight___EiTmx", "commentContent": "styles-module__commentContent___RObGr", "commentTop": "styles-module__commentTop___BbTF3", "commentAuthor": "styles-module__commentAuthor___tBjpl", "commentTime": "styles-module__commentTime___0OLrz", "commentText": "styles-module__commentText___ldy2V", "screenshot": "styles-module__screenshot___jUqau", "screenshotBadge": "styles-module__screenshotBadge___roEqY", "divider": "styles-module__divider___kDjxN", "pendingImage": "styles-module__pendingImage___gHaF3", "pendingRemove": "styles-module__pendingRemove___FRL4h", "replyArea": "styles-module__replyArea___VQn9b", "replyInput": "styles-module__replyInput___uZNBW", "replyToolbar": "styles-module__replyToolbar___fTFJU", "replyTool": "styles-module__replyTool___Ho-Rx", "replySend": "styles-module__replySend___e0VSb", "active": "styles-module__active___R--Jj", "empty": "styles-module__empty___XXGiw", "statusBadge": "styles-module__statusBadge___el8ml", "todo": "styles-module__todo___rUWBr", "inProgress": "styles-module__inProgress___HEWgU", "done": "styles-module__done___zC12n" };
2022
2030
  if (typeof document !== "undefined") {
2023
2031
  let style = document.getElementById("impakers-debug-styles-comment-thread-styles");
2024
2032
  if (!style) {
@@ -2033,16 +2041,14 @@ var styles_module_default3 = classNames3;
2033
2041
  // src/components/comment-thread/index.tsx
2034
2042
  var import_jsx_runtime4 = require("react/jsx-runtime");
2035
2043
  function timeAgo(dateStr) {
2036
- const now = Date.now();
2037
- const then = new Date(dateStr).getTime();
2038
- const diff = now - then;
2039
- const minutes = Math.floor(diff / 6e4);
2040
- if (minutes < 1) return "just now";
2041
- if (minutes < 60) return `${minutes}m`;
2042
- const hours = Math.floor(minutes / 60);
2043
- if (hours < 24) return `${hours}h`;
2044
- const days = Math.floor(hours / 24);
2045
- if (days < 30) return `${days}d`;
2044
+ const diff = Date.now() - new Date(dateStr).getTime();
2045
+ const m = Math.floor(diff / 6e4);
2046
+ if (m < 1) return "just now";
2047
+ if (m < 60) return `${m}m`;
2048
+ const h = Math.floor(m / 60);
2049
+ if (h < 24) return `${h}h`;
2050
+ const d = Math.floor(h / 24);
2051
+ if (d < 30) return `${d}d`;
2046
2052
  return new Date(dateStr).toLocaleDateString("ko-KR", { month: "short", day: "numeric" });
2047
2053
  }
2048
2054
  function getInitials(name) {
@@ -2061,15 +2067,17 @@ function CommentThread({
2061
2067
  }) {
2062
2068
  const [replyText, setReplyText] = (0, import_react3.useState)("");
2063
2069
  const [exiting, setExiting] = (0, import_react3.useState)(false);
2070
+ const [pendingImage, setPendingImage] = (0, import_react3.useState)(null);
2071
+ const [capturingDom, setCapturingDom] = (0, import_react3.useState)(false);
2064
2072
  const textareaRef = (0, import_react3.useRef)(null);
2065
2073
  const listRef = (0, import_react3.useRef)(null);
2074
+ const fileInputRef = (0, import_react3.useRef)(null);
2066
2075
  (0, import_react3.useEffect)(() => {
2067
- if (listRef.current) {
2068
- listRef.current.scrollTop = listRef.current.scrollHeight;
2069
- }
2076
+ if (listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight;
2070
2077
  textareaRef.current?.focus();
2071
2078
  }, [task.comments.length]);
2072
2079
  (0, import_react3.useEffect)(() => {
2080
+ if (capturingDom) return;
2073
2081
  const handler = (e) => {
2074
2082
  const target = e.target;
2075
2083
  if (target.closest(`.${styles_module_default3.thread}`)) return;
@@ -2079,16 +2087,17 @@ function CommentThread({
2079
2087
  };
2080
2088
  document.addEventListener("mousedown", handler);
2081
2089
  return () => document.removeEventListener("mousedown", handler);
2082
- }, []);
2090
+ }, [capturingDom]);
2083
2091
  const handleClose = (0, import_react3.useCallback)(() => {
2084
2092
  setExiting(true);
2085
2093
  setTimeout(() => onClose(), 120);
2086
2094
  }, [onClose]);
2087
2095
  const handleSend = (0, import_react3.useCallback)(() => {
2088
- if (!replyText.trim()) return;
2089
- onReply(task.id, replyText.trim());
2096
+ if (!replyText.trim() && !pendingImage) return;
2097
+ onReply(task.id, replyText.trim(), pendingImage || void 0);
2090
2098
  setReplyText("");
2091
- }, [replyText, task.id, onReply]);
2099
+ setPendingImage(null);
2100
+ }, [replyText, pendingImage, task.id, onReply]);
2092
2101
  const handleKeyDown = (0, import_react3.useCallback)(
2093
2102
  (e) => {
2094
2103
  e.stopPropagation();
@@ -2097,12 +2106,81 @@ function CommentThread({
2097
2106
  e.preventDefault();
2098
2107
  handleSend();
2099
2108
  }
2100
- if (e.key === "Escape") {
2101
- handleClose();
2102
- }
2109
+ if (e.key === "Escape") handleClose();
2103
2110
  },
2104
2111
  [handleClose, handleSend]
2105
2112
  );
2113
+ const handleCameraClick = (0, import_react3.useCallback)(() => {
2114
+ setCapturingDom(true);
2115
+ const threadEl = document.querySelector(`.${styles_module_default3.thread}`);
2116
+ if (threadEl) threadEl.style.visibility = "hidden";
2117
+ document.documentElement.style.cursor = "crosshair";
2118
+ const allEls = document.querySelectorAll("*");
2119
+ allEls.forEach((el) => el.style.cursor = "crosshair");
2120
+ let hoverBox = null;
2121
+ const showHover = (rect) => {
2122
+ if (!hoverBox) {
2123
+ hoverBox = document.createElement("div");
2124
+ hoverBox.style.cssText = `position:fixed;border:2px solid #3b82f6;background:rgba(59,130,246,0.05);border-radius:4px;pointer-events:none;z-index:999999;transition:all 0.08s ease`;
2125
+ document.body.appendChild(hoverBox);
2126
+ }
2127
+ hoverBox.style.left = `${rect.left}px`;
2128
+ hoverBox.style.top = `${rect.top}px`;
2129
+ hoverBox.style.width = `${rect.width}px`;
2130
+ hoverBox.style.height = `${rect.height}px`;
2131
+ };
2132
+ const handleMove = (e) => {
2133
+ const el = document.elementFromPoint(e.clientX, e.clientY);
2134
+ if (!el || el === hoverBox) return;
2135
+ showHover(el.getBoundingClientRect());
2136
+ };
2137
+ const handleClick = async (e) => {
2138
+ e.preventDefault();
2139
+ e.stopPropagation();
2140
+ document.removeEventListener("mousemove", handleMove);
2141
+ document.removeEventListener("click", handleClick, true);
2142
+ document.documentElement.style.cursor = "";
2143
+ allEls.forEach((el) => el.style.cursor = "");
2144
+ hoverBox?.remove();
2145
+ const targetEl = document.elementFromPoint(e.clientX, e.clientY);
2146
+ if (!targetEl) {
2147
+ if (threadEl) threadEl.style.visibility = "";
2148
+ setCapturingDom(false);
2149
+ return;
2150
+ }
2151
+ try {
2152
+ const html2canvas = (await import("html2canvas")).default;
2153
+ const canvas = await html2canvas(targetEl, {
2154
+ useCORS: true,
2155
+ allowTaint: true,
2156
+ scale: Math.min(window.devicePixelRatio, 2),
2157
+ logging: false
2158
+ });
2159
+ const base64 = canvas.toDataURL("image/jpeg", 0.7);
2160
+ setPendingImage(base64);
2161
+ } catch (err) {
2162
+ console.error("[@impakers/debug] DOM \uC2A4\uD06C\uB9B0\uC0F7 \uC2E4\uD328:", err);
2163
+ }
2164
+ if (threadEl) threadEl.style.visibility = "";
2165
+ setCapturingDom(false);
2166
+ textareaRef.current?.focus();
2167
+ };
2168
+ document.addEventListener("mousemove", handleMove);
2169
+ document.addEventListener("click", handleClick, true);
2170
+ }, []);
2171
+ const handleAttachClick = (0, import_react3.useCallback)(() => {
2172
+ fileInputRef.current?.click();
2173
+ }, []);
2174
+ const handleFileChange = (0, import_react3.useCallback)((e) => {
2175
+ const file = e.target.files?.[0];
2176
+ if (!file || !file.type.startsWith("image/")) return;
2177
+ const reader = new FileReader();
2178
+ reader.onload = () => {
2179
+ setPendingImage(reader.result);
2180
+ };
2181
+ reader.readAsDataURL(file);
2182
+ e.target.value = "";
2183
+ }, []);
2106
2184
  const feedbackTitle = task.title.replace(/^\[피드백\]\s*/, "");
2107
2185
  const positionStyle = {
2108
2186
  left,
@@ -2127,30 +2205,8 @@ function CommentThread({
2127
2205
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles_module_default3.title, children: feedbackTitle })
2128
2206
  ] }),
2129
2207
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles_module_default3.headerActions, children: [
2130
- onResolve && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2131
- "button",
2132
- {
2133
- className: styles_module_default3.headerAction,
2134
- onClick: () => onResolve(task.id),
2135
- title: "\uC644\uB8CC \uCC98\uB9AC",
2136
- type: "button",
2137
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: "20 6 9 17 4 12" }) })
2138
- }
2139
- ),
2140
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2141
- "button",
2142
- {
2143
- className: styles_module_default3.headerAction,
2144
- onClick: handleClose,
2145
- title: "\uB2EB\uAE30",
2146
- type: "button",
2147
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2148
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "1" }),
2149
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "19", cy: "12", r: "1" }),
2150
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5", cy: "12", r: "1" })
2151
- ] })
2152
- }
2153
- )
2208
+ onResolve && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.headerAction, onClick: () => onResolve(task.id), title: "\uC644\uB8CC \uCC98\uB9AC", type: "button", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: "20 6 9 17 4 12" }) }) }),
2209
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.headerAction, onClick: handleClose, title: "\uB2EB\uAE30", type: "button", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M18 6L6 18M6 6l12 12" }) }) })
2154
2210
  ] })
2155
2211
  ] }),
2156
2212
  task.comments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
@@ -2162,11 +2218,15 @@ function CommentThread({
2162
2218
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.commentAuthor, children: comment.authorName }),
2163
2219
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.commentTime, children: timeAgo(comment.createdAt) })
2164
2220
  ] }),
2165
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles_module_default3.commentText, children: comment.content }),
2166
- comment.screenshot && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles_module_default3.screenshot, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: comment.screenshot, alt: "screenshot" }) })
2221
+ comment.content && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles_module_default3.commentText, children: comment.content }),
2222
+ comment.imageUrl && comment.imageUrl !== "loading..." && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles_module_default3.screenshot, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: comment.imageUrl, alt: "screenshot" }) })
2167
2223
  ] })
2168
2224
  ] }, comment.id)) })
2169
2225
  ] }),
2226
+ pendingImage && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles_module_default3.pendingImage, children: [
2227
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: pendingImage, alt: "pending" }),
2228
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.pendingRemove, onClick: () => setPendingImage(null), type: "button", children: "\xD7" })
2229
+ ] }),
2170
2230
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles_module_default3.replyArea, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2171
2231
  "textarea",
2172
2232
  {
@@ -2180,21 +2240,20 @@ function CommentThread({
2180
2240
  }
2181
2241
  ) }),
2182
2242
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles_module_default3.replyToolbar, children: [
2183
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.replyTool, type: "button", title: "\uCCA8\uBD80", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2243
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.replyTool, onClick: handleAttachClick, type: "button", title: "\uC774\uBBF8\uC9C0 \uCCA8\uBD80", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2184
2244
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
2185
2245
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
2186
2246
  ] }) }),
2187
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.replyTool, type: "button", title: "\uC2A4\uD06C\uB9B0\uC0F7", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2247
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.replyTool, onClick: handleCameraClick, type: "button", title: "\uC694\uC18C \uC2A4\uD06C\uB9B0\uC0F7", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2188
2248
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M14.5 4h-5L7 7H4a2 2 0 00-2 2v9a2 2 0 002 2h16a2 2 0 002-2V9a2 2 0 00-2-2h-3l-2.5-3z" }),
2189
2249
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "13", r: "3" })
2190
2250
  ] }) }),
2191
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.replyTool, type: "button", title: "\uC11C\uC2DD", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { x: "4", y: "17", fontSize: "14", fontWeight: "600", fontFamily: "serif", fill: "currentColor", children: "Aa" }) }) }),
2192
2251
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2193
2252
  "button",
2194
2253
  {
2195
- className: `${styles_module_default3.replySend} ${replyText.trim() ? styles_module_default3.active : ""}`,
2254
+ className: `${styles_module_default3.replySend} ${replyText.trim() || pendingImage ? styles_module_default3.active : ""}`,
2196
2255
  onClick: handleSend,
2197
- disabled: !replyText.trim(),
2256
+ disabled: !replyText.trim() && !pendingImage,
2198
2257
  type: "button",
2199
2258
  title: "\uC804\uC1A1",
2200
2259
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
@@ -2203,7 +2262,17 @@ function CommentThread({
2203
2262
  ] })
2204
2263
  }
2205
2264
  )
2206
- ] })
2265
+ ] }),
2266
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2267
+ "input",
2268
+ {
2269
+ ref: fileInputRef,
2270
+ type: "file",
2271
+ accept: "image/*",
2272
+ style: { display: "none" },
2273
+ onChange: handleFileChange
2274
+ }
2275
+ )
2207
2276
  ]
2208
2277
  }
2209
2278
  );
@@ -2501,12 +2570,15 @@ function InboxPanel({
2501
2570
  }
2502
2571
  if (e.key === "Escape") handleClose();
2503
2572
  }, [handleSendReply, handleClose]);
2573
+ const [statusOverrides, setStatusOverrides] = (0, import_react5.useState)({});
2504
2574
  const handleToggleStatus = (0, import_react5.useCallback)((item, e) => {
2505
2575
  e.stopPropagation();
2506
2576
  const newStatus = item.status === "done" ? "todo" : "done";
2577
+ setStatusOverrides((prev) => ({ ...prev, [item.id]: newStatus }));
2507
2578
  onStatusChange?.(item.id, newStatus);
2508
2579
  }, [onStatusChange]);
2509
- const items = tab === "page" ? pageItems : allItems;
2580
+ const applyOverrides = (items2) => items2.map((item) => statusOverrides[item.id] ? { ...item, status: statusOverrides[item.id] } : item);
2581
+ const items = applyOverrides(tab === "page" ? pageItems : allItems);
2510
2582
  if (typeof document === "undefined") return null;
2511
2583
  return (0, import_react_dom.createPortal)(
2512
2584
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { "data-impakers-debug": "", children: [
@@ -3192,7 +3264,7 @@ ${elementInfo.join("\n")}`);
3192
3264
  return newId;
3193
3265
  });
3194
3266
  }, [endpoint]);
3195
- const handleThreadReply = (0, import_react7.useCallback)(async (taskId, content) => {
3267
+ const handleThreadReply = (0, import_react7.useCallback)(async (taskId, content, screenshot) => {
3196
3268
  const authorName = getUser?.()?.name ? String(getUser().name) : "\uC775\uBA85";
3197
3269
  const authorId = getUser?.()?.id ? String(getUser().id) : void 0;
3198
3270
  const tempComment = {
@@ -3200,6 +3272,8 @@ ${elementInfo.join("\n")}`);
3200
3272
  content,
3201
3273
  authorName,
3202
3274
  authorId,
3275
+ imageUrl: screenshot ? screenshot : void 0,
3276
+ // 로컬 미리보기
3203
3277
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
3204
3278
  };
3205
3279
  setThreadComments((prev) => ({
@@ -3207,7 +3281,7 @@ ${elementInfo.join("\n")}`);
3207
3281
  [taskId]: [...prev[taskId] || [], tempComment]
3208
3282
  }));
3209
3283
  try {
3210
- const serverComment = await postComment(endpoint, taskId, content, authorName, authorId);
3284
+ const serverComment = await postComment(endpoint, taskId, content, authorName, authorId, screenshot);
3211
3285
  setThreadComments((prev) => ({
3212
3286
  ...prev,
3213
3287
  [taskId]: (prev[taskId] || []).map(
@@ -3345,10 +3419,15 @@ ${elementInfo.join("\n")}`);
3345
3419
  currentUserName: getUser?.()?.name ? String(getUser().name) : "\uC775\uBA85",
3346
3420
  currentUserId: getUser?.()?.id ? String(getUser().id) : void 0,
3347
3421
  onClose: () => setShowInbox(false),
3348
- onStatusChange: (taskId, status) => {
3349
- setAnnotations(
3350
- (prev) => prev.map((a) => a.id === taskId ? { ...a, status } : a)
3351
- );
3422
+ onStatusChange: async (taskId, status) => {
3423
+ try {
3424
+ await updateTaskStatus(endpoint, taskId, status);
3425
+ const currentPath2 = window.location.pathname;
3426
+ const tasks = await fetchFeedbacks(endpoint, currentPath2);
3427
+ setServerTasks(tasks);
3428
+ } catch (err) {
3429
+ console.error("[@impakers/debug] \uC0C1\uD0DC \uBCC0\uACBD \uC2E4\uD328:", err);
3430
+ }
3352
3431
  }
3353
3432
  }
3354
3433
  ),
@@ -3679,7 +3758,13 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
3679
3758
  function ImpakersDebugProvider({ endpoint, getUser }) {
3680
3759
  const [authenticated, setAuthenticated] = (0, import_react9.useState)(false);
3681
3760
  const [showAuth, setShowAuth] = (0, import_react9.useState)(false);
3682
- const [hidden, setHidden] = (0, import_react9.useState)(false);
3761
+ const [hidden, setHidden] = (0, import_react9.useState)(() => {
3762
+ try {
3763
+ return localStorage.getItem("impakers-debug-hidden") === "1";
3764
+ } catch {
3765
+ return false;
3766
+ }
3767
+ });
3683
3768
  (0, import_react9.useEffect)(() => {
3684
3769
  setAuthenticated(isAuthenticated());
3685
3770
  }, []);
@@ -3688,9 +3773,17 @@ function ImpakersDebugProvider({ endpoint, getUser }) {
3688
3773
  ImpakersDebug._on("impakers-debug:show-auth", () => {
3689
3774
  setShowAuth(true);
3690
3775
  setHidden(false);
3776
+ try {
3777
+ localStorage.removeItem("impakers-debug-hidden");
3778
+ } catch {
3779
+ }
3691
3780
  }),
3692
3781
  ImpakersDebug._on("impakers-debug:open", () => {
3693
3782
  setHidden(false);
3783
+ try {
3784
+ localStorage.removeItem("impakers-debug-hidden");
3785
+ } catch {
3786
+ }
3694
3787
  }),
3695
3788
  ImpakersDebug._on("impakers-debug:deactivate", () => {
3696
3789
  setAuthenticated(false);
@@ -3705,6 +3798,10 @@ function ImpakersDebugProvider({ endpoint, getUser }) {
3705
3798
  e.preventDefault();
3706
3799
  if (hidden) {
3707
3800
  setHidden(false);
3801
+ try {
3802
+ localStorage.removeItem("impakers-debug-hidden");
3803
+ } catch {
3804
+ }
3708
3805
  return;
3709
3806
  }
3710
3807
  if (isAuthenticated()) {
@@ -3726,6 +3823,10 @@ function ImpakersDebugProvider({ endpoint, getUser }) {
3726
3823
  }, []);
3727
3824
  const handleHide = (0, import_react9.useCallback)(() => {
3728
3825
  setHidden(true);
3826
+ try {
3827
+ localStorage.setItem("impakers-debug-hidden", "1");
3828
+ } catch {
3829
+ }
3729
3830
  }, []);
3730
3831
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3731
3832
  authenticated && !hidden && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DebugWidget, { endpoint, getUser, onHide: handleHide }),