@impakers/debug 1.3.1 → 1.3.4

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
@@ -1576,39 +1576,6 @@ ${a.stack?.split("\n").slice(0, 3).join("\n")}`;
1576
1576
  });
1577
1577
  }
1578
1578
  installConsoleCapture();
1579
- var MAX_NETWORK_ERRORS = 5;
1580
- var recentNetworkErrors = [];
1581
- var fetchPatched = false;
1582
- function installFetchCapture() {
1583
- if (fetchPatched || typeof window === "undefined") return;
1584
- fetchPatched = true;
1585
- const originalFetch = window.fetch;
1586
- window.fetch = async (input, init) => {
1587
- const method = init?.method || "GET";
1588
- const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1589
- try {
1590
- const response = await originalFetch(input, init);
1591
- if (!response.ok && response.status >= 400) {
1592
- recentNetworkErrors.push({
1593
- url: url.slice(0, 200),
1594
- status: response.status,
1595
- method
1596
- });
1597
- if (recentNetworkErrors.length > MAX_NETWORK_ERRORS) recentNetworkErrors.shift();
1598
- }
1599
- return response;
1600
- } catch (err) {
1601
- recentNetworkErrors.push({
1602
- url: url.slice(0, 200),
1603
- status: 0,
1604
- method
1605
- });
1606
- if (recentNetworkErrors.length > MAX_NETWORK_ERRORS) recentNetworkErrors.shift();
1607
- throw err;
1608
- }
1609
- };
1610
- }
1611
- installFetchCapture();
1612
1579
  function parseBrowser(ua) {
1613
1580
  const edge = ua.match(/Edg\/(\d+[\d.]*)/);
1614
1581
  if (edge) return `Edge ${edge[1]}`;
@@ -1743,7 +1710,6 @@ function collectMetadata(getUser) {
1743
1710
  cookies: parseCookies(),
1744
1711
  jwtClaims: findAndDecodeJwt() ?? void 0,
1745
1712
  consoleErrors: recentConsoleErrors.length > 0 ? [...recentConsoleErrors] : void 0,
1746
- networkErrors: recentNetworkErrors.length > 0 ? [...recentNetworkErrors] : void 0,
1747
1713
  performance: getPagePerformance()
1748
1714
  };
1749
1715
  if (getUser) {
@@ -1786,94 +1752,313 @@ async function apiFetch(url, options = {}) {
1786
1752
  return res;
1787
1753
  }
1788
1754
  var cache = /* @__PURE__ */ new Map();
1755
+ var inflightRequests = /* @__PURE__ */ new Map();
1756
+ var listeners = /* @__PURE__ */ new Map();
1789
1757
  var CACHE_TTL = 3e4;
1790
- function getCached(key) {
1791
- const entry = cache.get(key);
1792
- if (!entry) return null;
1793
- if (Date.now() - entry.timestamp > CACHE_TTL) {
1794
- cache.delete(key);
1795
- return null;
1796
- }
1797
- return entry.data;
1758
+ function getFeedbacksCacheKey(url) {
1759
+ return `feedbacks:${url}`;
1760
+ }
1761
+ function getAllFeedbacksCacheKey() {
1762
+ return "feedbacks:__all__";
1763
+ }
1764
+ function getCommentsCacheKey(taskId) {
1765
+ return `comments:${taskId}`;
1798
1766
  }
1799
1767
  function setCache(key, data) {
1800
1768
  cache.set(key, { data, timestamp: Date.now() });
1769
+ const subs = listeners.get(key);
1770
+ if (!subs) return;
1771
+ subs.forEach((listener) => listener(data));
1772
+ }
1773
+ function peekCache(key) {
1774
+ const entry = cache.get(key);
1775
+ return entry?.data ?? null;
1776
+ }
1777
+ function isCacheFresh(key) {
1778
+ const entry = cache.get(key);
1779
+ if (!entry) return false;
1780
+ return Date.now() - entry.timestamp <= CACHE_TTL;
1781
+ }
1782
+ function getMatchingKeys(pattern) {
1783
+ return Array.from(cache.keys()).filter((key) => key.includes(pattern));
1801
1784
  }
1802
- function invalidateCache(pattern) {
1803
- if (!pattern) {
1804
- cache.clear();
1805
- return;
1785
+ function snapshotCaches(pattern) {
1786
+ const snapshots = /* @__PURE__ */ new Map();
1787
+ for (const key of getMatchingKeys(pattern)) {
1788
+ const value = peekCache(key);
1789
+ if (value !== null) snapshots.set(key, value);
1806
1790
  }
1807
- for (const key of cache.keys()) {
1808
- if (key.includes(pattern)) cache.delete(key);
1791
+ return snapshots;
1792
+ }
1793
+ function restoreSnapshots(snapshots) {
1794
+ snapshots.forEach((value, key) => setCache(key, value));
1795
+ }
1796
+ function mutateCache(key, updater) {
1797
+ const next = updater(peekCache(key));
1798
+ setCache(key, next);
1799
+ return next;
1800
+ }
1801
+ function mutateMatchingCaches(pattern, updater) {
1802
+ for (const key of getMatchingKeys(pattern)) {
1803
+ const current = peekCache(key);
1804
+ if (current === null) continue;
1805
+ setCache(key, updater(current));
1809
1806
  }
1810
1807
  }
1808
+ async function revalidateCache(key, fetcher) {
1809
+ const existing = inflightRequests.get(key);
1810
+ if (existing) return existing;
1811
+ const request = (async () => {
1812
+ try {
1813
+ const data = await fetcher();
1814
+ setCache(key, data);
1815
+ return data;
1816
+ } finally {
1817
+ inflightRequests.delete(key);
1818
+ }
1819
+ })();
1820
+ inflightRequests.set(key, request);
1821
+ return request;
1822
+ }
1823
+ async function readWithCache(key, fetcher, options = {}) {
1824
+ const cached = peekCache(key);
1825
+ if (cached !== null && !options.force) {
1826
+ if (options.staleWhileRevalidate || !isCacheFresh(key)) {
1827
+ void revalidateCache(key, fetcher);
1828
+ }
1829
+ return cached;
1830
+ }
1831
+ return revalidateCache(key, fetcher);
1832
+ }
1833
+ function subscribeCache(key, listener) {
1834
+ const subs = listeners.get(key) || /* @__PURE__ */ new Set();
1835
+ subs.add(listener);
1836
+ listeners.set(key, subs);
1837
+ return () => {
1838
+ const current = listeners.get(key);
1839
+ if (!current) return;
1840
+ current.delete(listener);
1841
+ if (current.size === 0) listeners.delete(key);
1842
+ };
1843
+ }
1844
+ function getCachedSnapshot(key) {
1845
+ return peekCache(key);
1846
+ }
1811
1847
  async function submitFeedback(endpoint, payload) {
1812
- const res = await apiFetch(endpoint, {
1813
- method: "POST",
1814
- body: JSON.stringify(payload)
1815
- });
1816
- if (payload.feedbackUrl) {
1817
- invalidateCache(`feedbacks:${payload.feedbackUrl}`);
1848
+ const tempTaskId = `temp-task-${Date.now()}`;
1849
+ const feedbackUrl = payload.feedbackUrl;
1850
+ const marker = payload.feedbackMarker;
1851
+ const previousFeedbacks = feedbackUrl ? peekCache(getFeedbacksCacheKey(feedbackUrl)) || [] : null;
1852
+ const previousAll = peekCache(getAllFeedbacksCacheKey());
1853
+ const optimisticTask = feedbackUrl ? {
1854
+ id: tempTaskId,
1855
+ taskNumber: 0,
1856
+ title: payload.title,
1857
+ status: "todo",
1858
+ priority: payload.priority,
1859
+ feedbackUrl,
1860
+ feedbackMarker: marker || null,
1861
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1862
+ commentCount: 0,
1863
+ _optimistic: true
1864
+ } : null;
1865
+ if (feedbackUrl && optimisticTask) {
1866
+ setCache(getFeedbacksCacheKey(feedbackUrl), [optimisticTask, ...previousFeedbacks]);
1867
+ if (previousAll) {
1868
+ setCache(getAllFeedbacksCacheKey(), [optimisticTask, ...previousAll]);
1869
+ }
1818
1870
  }
1819
- invalidateCache("feedbacks:");
1820
1871
  try {
1821
- return await res.json();
1822
- } catch {
1823
- return {};
1824
- }
1825
- }
1826
- async function fetchFeedbacks(endpoint, url) {
1827
- const cacheKey = `feedbacks:${url}`;
1828
- const cached = getCached(cacheKey);
1829
- if (cached) return cached;
1830
- const res = await apiFetch(`${endpoint}?url=${encodeURIComponent(url)}`);
1831
- const data = await res.json();
1832
- const tasks = data.tasks || [];
1833
- setCache(cacheKey, tasks);
1834
- return tasks;
1835
- }
1836
- async function fetchComments(endpoint, taskId) {
1837
- const cacheKey = `comments:${taskId}`;
1838
- const cached = getCached(cacheKey);
1839
- if (cached) return cached;
1840
- const res = await apiFetch(`${endpoint}/${taskId}/comments`);
1841
- const data = await res.json();
1842
- const comments = data.comments || [];
1843
- setCache(cacheKey, comments);
1844
- return comments;
1845
- }
1846
- async function postComment(endpoint, taskId, content, authorName, authorId, screenshot) {
1872
+ const res = await apiFetch(endpoint, {
1873
+ method: "POST",
1874
+ body: JSON.stringify(payload)
1875
+ });
1876
+ const result = await res.json().catch(() => ({}));
1877
+ if (feedbackUrl && optimisticTask) {
1878
+ const finalTask = {
1879
+ ...optimisticTask,
1880
+ id: result.taskId || tempTaskId,
1881
+ taskNumber: result.taskNumber || 0,
1882
+ _optimistic: false
1883
+ };
1884
+ mutateCache(
1885
+ getFeedbacksCacheKey(feedbackUrl),
1886
+ (current) => (current || []).map((item) => item.id === tempTaskId ? finalTask : item)
1887
+ );
1888
+ if (previousAll) {
1889
+ mutateCache(
1890
+ getAllFeedbacksCacheKey(),
1891
+ (current) => (current || []).map((item) => item.id === tempTaskId ? finalTask : item)
1892
+ );
1893
+ }
1894
+ void revalidateFeedbacks(endpoint, feedbackUrl).catch(() => {
1895
+ });
1896
+ if (previousAll) void revalidateAllFeedbacks(endpoint).catch(() => {
1897
+ });
1898
+ }
1899
+ return result;
1900
+ } catch (error) {
1901
+ if (feedbackUrl && previousFeedbacks) {
1902
+ setCache(getFeedbacksCacheKey(feedbackUrl), previousFeedbacks);
1903
+ }
1904
+ if (previousAll) {
1905
+ setCache(getAllFeedbacksCacheKey(), previousAll);
1906
+ }
1907
+ throw error;
1908
+ }
1909
+ }
1910
+ async function fetchFeedbacks(endpoint, url, options = {}) {
1911
+ const cacheKey = getFeedbacksCacheKey(url);
1912
+ return readWithCache(cacheKey, async () => {
1913
+ const res = await apiFetch(`${endpoint}?url=${encodeURIComponent(url)}`);
1914
+ const data = await res.json();
1915
+ return data.tasks || [];
1916
+ }, options);
1917
+ }
1918
+ async function revalidateFeedbacks(endpoint, url) {
1919
+ const cacheKey = getFeedbacksCacheKey(url);
1920
+ return revalidateCache(cacheKey, async () => {
1921
+ const res = await apiFetch(`${endpoint}?url=${encodeURIComponent(url)}`);
1922
+ const data = await res.json();
1923
+ return data.tasks || [];
1924
+ });
1925
+ }
1926
+ async function revalidateAllFeedbacks(endpoint) {
1927
+ const cacheKey = getAllFeedbacksCacheKey();
1928
+ return revalidateCache(cacheKey, async () => {
1929
+ const res = await apiFetch(`${endpoint}?url=__all__`);
1930
+ const data = await res.json();
1931
+ return data.tasks || [];
1932
+ });
1933
+ }
1934
+ async function fetchComments(endpoint, taskId, options = {}) {
1935
+ const cacheKey = getCommentsCacheKey(taskId);
1936
+ return readWithCache(cacheKey, async () => {
1937
+ const res = await apiFetch(`${endpoint}/${taskId}/comments`);
1938
+ const data = await res.json();
1939
+ return data.comments || [];
1940
+ }, options);
1941
+ }
1942
+ async function revalidateComments(endpoint, taskId) {
1943
+ const cacheKey = getCommentsCacheKey(taskId);
1944
+ return revalidateCache(cacheKey, async () => {
1945
+ const res = await apiFetch(`${endpoint}/${taskId}/comments`);
1946
+ const data = await res.json();
1947
+ return data.comments || [];
1948
+ });
1949
+ }
1950
+ function incrementCommentCount(taskId, delta) {
1951
+ mutateMatchingCaches(
1952
+ "feedbacks:",
1953
+ (items) => items.map((item) => item.id === taskId ? { ...item, commentCount: Math.max(0, (item.commentCount || 0) + delta) } : item)
1954
+ );
1955
+ }
1956
+ function revalidateCachedFeedbackLists(endpoint) {
1957
+ const feedbackKeys = getMatchingKeys("feedbacks:");
1958
+ feedbackKeys.forEach((key) => {
1959
+ const url = key.replace("feedbacks:", "");
1960
+ if (url === "__all__") {
1961
+ void revalidateAllFeedbacks(endpoint).catch(() => {
1962
+ });
1963
+ } else {
1964
+ void revalidateFeedbacks(endpoint, url).catch(() => {
1965
+ });
1966
+ }
1967
+ });
1968
+ }
1969
+ async function postComment(endpoint, taskId, content, authorName, authorId, screenshot, file) {
1847
1970
  const tempComment = {
1848
1971
  id: `temp-${Date.now()}`,
1849
1972
  content,
1850
1973
  authorName,
1851
1974
  authorId,
1852
- imageUrl: screenshot ? "loading..." : void 0,
1975
+ imageUrl: screenshot || void 0,
1976
+ fileUrl: file?.url,
1977
+ fileName: file?.fileName,
1978
+ fileType: file?.fileType,
1979
+ fileSize: file?.fileSize,
1980
+ fileSource: file?.fileSource,
1853
1981
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1854
1982
  };
1855
- const cacheKey = `comments:${taskId}`;
1856
- const existing = getCached(cacheKey) || [];
1857
- setCache(cacheKey, [...existing, tempComment]);
1858
- const res = await apiFetch(`${endpoint}/${taskId}/comments`, {
1983
+ const cacheKey = getCommentsCacheKey(taskId);
1984
+ const previousComments = peekCache(cacheKey) || [];
1985
+ const feedbackSnapshots = snapshotCaches("feedbacks:");
1986
+ setCache(cacheKey, [...previousComments, tempComment]);
1987
+ incrementCommentCount(taskId, 1);
1988
+ try {
1989
+ const res = await apiFetch(`${endpoint}/${taskId}/comments`, {
1990
+ method: "POST",
1991
+ body: JSON.stringify({
1992
+ content,
1993
+ authorName,
1994
+ authorId,
1995
+ screenshot,
1996
+ ...file && {
1997
+ fileUrl: file.url,
1998
+ fileName: file.fileName,
1999
+ fileType: file.fileType,
2000
+ fileSize: file.fileSize,
2001
+ fileSource: file.fileSource
2002
+ }
2003
+ })
2004
+ });
2005
+ const data = await res.json();
2006
+ const serverComment = data.comment;
2007
+ mutateCache(
2008
+ cacheKey,
2009
+ (current) => (current || []).map((comment) => comment.id === tempComment.id ? serverComment : comment)
2010
+ );
2011
+ void revalidateComments(endpoint, taskId);
2012
+ revalidateCachedFeedbackLists(endpoint);
2013
+ return serverComment;
2014
+ } catch (error) {
2015
+ setCache(cacheKey, previousComments);
2016
+ restoreSnapshots(feedbackSnapshots);
2017
+ throw error;
2018
+ }
2019
+ }
2020
+ var MAX_FILE_SIZE = 4.5 * 1024 * 1024;
2021
+ async function uploadFile(endpoint, file, context, taskId) {
2022
+ if (file.size > MAX_FILE_SIZE) {
2023
+ throw new Error(`\uD30C\uC77C \uD06C\uAE30\uAC00 4.5MB\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4. (${(file.size / 1024 / 1024).toFixed(1)}MB)`);
2024
+ }
2025
+ const token = getToken();
2026
+ const formData = new FormData();
2027
+ formData.append("file", file);
2028
+ formData.append("context", context);
2029
+ if (taskId) formData.append("taskId", taskId);
2030
+ const res = await fetch(`${endpoint}/upload`, {
1859
2031
  method: "POST",
1860
- body: JSON.stringify({ content, authorName, authorId, screenshot })
2032
+ headers: {
2033
+ Authorization: `Bearer ${token}`
2034
+ // Content-Type은 FormData가 자동 설정 (boundary 포함)
2035
+ },
2036
+ body: formData
1861
2037
  });
1862
- const data = await res.json();
1863
- const serverComment = data.comment;
1864
- const updated = (getCached(cacheKey) || []).map(
1865
- (c) => c.id === tempComment.id ? serverComment : c
1866
- );
1867
- setCache(cacheKey, updated);
1868
- invalidateCache("feedbacks:");
1869
- return serverComment;
2038
+ if (!res.ok) {
2039
+ if (res.status === 401) throw new Error("\uC778\uC99D\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
2040
+ if (res.status === 413) throw new Error("\uD30C\uC77C \uD06C\uAE30\uAC00 4.5MB\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4.");
2041
+ const err = await res.json().catch(() => ({ error: "\uD30C\uC77C \uC5C5\uB85C\uB4DC \uC2E4\uD328" }));
2042
+ throw new Error(err.error || "\uD30C\uC77C \uC5C5\uB85C\uB4DC \uC2E4\uD328");
2043
+ }
2044
+ return res.json();
1870
2045
  }
1871
2046
  async function updateTaskStatus(endpoint, taskId, status) {
1872
- await apiFetch(`${endpoint}/${taskId}/status`, {
1873
- method: "PATCH",
1874
- body: JSON.stringify({ status })
1875
- });
1876
- invalidateCache("feedbacks:");
2047
+ const snapshots = snapshotCaches("feedbacks:");
2048
+ mutateMatchingCaches(
2049
+ "feedbacks:",
2050
+ (items) => items.map((item) => item.id === taskId ? { ...item, status } : item)
2051
+ );
2052
+ try {
2053
+ await apiFetch(`${endpoint}/${taskId}/status`, {
2054
+ method: "PATCH",
2055
+ body: JSON.stringify({ status })
2056
+ });
2057
+ revalidateCachedFeedbackLists(endpoint);
2058
+ } catch (error) {
2059
+ restoreSnapshots(snapshots);
2060
+ throw error;
2061
+ }
1877
2062
  }
1878
2063
 
1879
2064
  // src/core/sourcemap-resolver.ts
@@ -2025,8 +2210,8 @@ function PendingMarker({ x, y, accentColor }) {
2025
2210
  var import_react3 = require("react");
2026
2211
 
2027
2212
  // src/components/comment-thread/styles.module.scss
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}\n\n.styles-module__lightbox___KoWEF {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n.styles-module__lightbox___KoWEF img {\n max-width: 90vw;\n max-height: 90vh;\n border-radius: 8px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n cursor: default;\n}\n\n.styles-module__lightboxClose___tGZlm {\n position: absolute;\n top: 16px;\n right: 16px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: none;\n cursor: pointer;\n font-size: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.styles-module__lightboxClose___tGZlm:hover {\n background: rgba(255, 255, 255, 0.25);\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", "lightbox": "styles-module__lightbox___KoWEF", "lightboxClose": "styles-module__lightboxClose___tGZlm" };
2213
+ 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__fileCard___-iOro {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n background: #f9fafb;\n text-decoration: none;\n color: inherit;\n cursor: pointer;\n transition: background 0.12s;\n max-width: 200px;\n}\n.styles-module__fileCard___-iOro:hover {\n background: #f3f4f6;\n}\n\n.styles-module__fileIcon___dNJMT {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n background: #e5e7eb;\n color: #374151;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n flex-shrink: 0;\n letter-spacing: 0.5px;\n}\n\n.styles-module__fileInfo___dVrLV {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.styles-module__fileCardName___SHOj- {\n font-size: 12px;\n font-weight: 500;\n color: #1a1a1a;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.styles-module__fileCardSize___W53JL {\n font-size: 11px;\n color: #9ca3af;\n}\n\n.styles-module__pendingFile___-OSRs {\n position: relative;\n margin: 0 16px 8px;\n padding: 8px 10px;\n padding-right: 30px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n background: #f9fafb;\n display: flex;\n align-items: center;\n gap: 8px;\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}\n\n.styles-module__lightbox___KoWEF {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n.styles-module__lightbox___KoWEF img {\n max-width: 90vw;\n max-height: 90vh;\n border-radius: 8px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n cursor: default;\n}\n\n.styles-module__lightboxClose___tGZlm {\n position: absolute;\n top: 16px;\n right: 16px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: none;\n cursor: pointer;\n font-size: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.styles-module__lightboxClose___tGZlm:hover {\n background: rgba(255, 255, 255, 0.25);\n}';
2214
+ 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", "fileCard": "styles-module__fileCard___-iOro", "fileIcon": "styles-module__fileIcon___dNJMT", "fileInfo": "styles-module__fileInfo___dVrLV", "fileCardName": "styles-module__fileCardName___SHOj-", "fileCardSize": "styles-module__fileCardSize___W53JL", "pendingFile": "styles-module__pendingFile___-OSRs", "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", "lightbox": "styles-module__lightbox___KoWEF", "lightboxClose": "styles-module__lightboxClose___tGZlm" };
2030
2215
  if (typeof document !== "undefined") {
2031
2216
  let style = document.getElementById("impakers-debug-styles-comment-thread-styles");
2032
2217
  if (!style) {
@@ -2054,6 +2239,21 @@ function timeAgo(dateStr) {
2054
2239
  function getInitials(name) {
2055
2240
  return name.slice(0, 1).toUpperCase();
2056
2241
  }
2242
+ function formatFileSize(bytes) {
2243
+ if (bytes < 1024) return `${bytes}B`;
2244
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}KB`;
2245
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
2246
+ }
2247
+ function getFileIcon(mimeType) {
2248
+ if (mimeType.includes("pdf")) return "PDF";
2249
+ if (mimeType.includes("spreadsheet") || mimeType.includes("excel") || mimeType.includes("csv")) return "XLS";
2250
+ if (mimeType.includes("presentation") || mimeType.includes("powerpoint")) return "PPT";
2251
+ if (mimeType.includes("document") || mimeType.includes("word")) return "DOC";
2252
+ if (mimeType.includes("zip") || mimeType.includes("compressed") || mimeType.includes("archive")) return "ZIP";
2253
+ if (mimeType.includes("video")) return "VID";
2254
+ if (mimeType.includes("audio")) return "AUD";
2255
+ return "FILE";
2256
+ }
2057
2257
  function CommentThread({
2058
2258
  task,
2059
2259
  currentUserName,
@@ -2068,6 +2268,7 @@ function CommentThread({
2068
2268
  const [replyText, setReplyText] = (0, import_react3.useState)("");
2069
2269
  const [exiting, setExiting] = (0, import_react3.useState)(false);
2070
2270
  const [pendingImage, setPendingImage] = (0, import_react3.useState)(null);
2271
+ const [pendingFile, setPendingFile] = (0, import_react3.useState)(null);
2071
2272
  const [capturingDom, setCapturingDom] = (0, import_react3.useState)(false);
2072
2273
  const textareaRef = (0, import_react3.useRef)(null);
2073
2274
  const listRef = (0, import_react3.useRef)(null);
@@ -2094,11 +2295,12 @@ function CommentThread({
2094
2295
  setTimeout(() => onClose(), 120);
2095
2296
  }, [onClose]);
2096
2297
  const handleSend = (0, import_react3.useCallback)(() => {
2097
- if (!replyText.trim() && !pendingImage) return;
2098
- onReply(task.id, replyText.trim(), pendingImage || void 0);
2298
+ if (!replyText.trim() && !pendingImage && !pendingFile) return;
2299
+ onReply(task.id, replyText.trim(), pendingImage || void 0, pendingFile || void 0);
2099
2300
  setReplyText("");
2100
2301
  setPendingImage(null);
2101
- }, [replyText, pendingImage, task.id, onReply]);
2302
+ setPendingFile(null);
2303
+ }, [replyText, pendingImage, pendingFile, task.id, onReply]);
2102
2304
  const handleKeyDown = (0, import_react3.useCallback)(
2103
2305
  (e) => {
2104
2306
  e.stopPropagation();
@@ -2174,12 +2376,23 @@ function CommentThread({
2174
2376
  }, []);
2175
2377
  const handleFileChange = (0, import_react3.useCallback)((e) => {
2176
2378
  const file = e.target.files?.[0];
2177
- if (!file || !file.type.startsWith("image/")) return;
2178
- const reader = new FileReader();
2179
- reader.onload = () => {
2180
- setPendingImage(reader.result);
2181
- };
2182
- reader.readAsDataURL(file);
2379
+ if (!file) return;
2380
+ if (file.size > 4.5 * 1024 * 1024) {
2381
+ console.warn("[@impakers/debug] \uD30C\uC77C \uD06C\uAE30\uAC00 4.5MB\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4.");
2382
+ e.target.value = "";
2383
+ return;
2384
+ }
2385
+ if (file.type.startsWith("image/")) {
2386
+ setPendingFile(null);
2387
+ const reader = new FileReader();
2388
+ reader.onload = () => {
2389
+ setPendingImage(reader.result);
2390
+ };
2391
+ reader.readAsDataURL(file);
2392
+ } else {
2393
+ setPendingImage(null);
2394
+ setPendingFile(file);
2395
+ }
2183
2396
  e.target.value = "";
2184
2397
  }, []);
2185
2398
  const feedbackTitle = task.title.replace(/^\[피드백\]\s*/, "");
@@ -2228,6 +2441,23 @@ function CommentThread({
2228
2441
  style: { cursor: "pointer" },
2229
2442
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: comment.imageUrl, alt: "screenshot" })
2230
2443
  }
2444
+ ),
2445
+ comment.fileUrl && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2446
+ "a",
2447
+ {
2448
+ className: styles_module_default3.fileCard,
2449
+ href: comment.fileUrl,
2450
+ target: "_blank",
2451
+ rel: "noopener noreferrer",
2452
+ onClick: (e) => e.stopPropagation(),
2453
+ children: [
2454
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.fileIcon, children: getFileIcon(comment.fileType || "") }),
2455
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: styles_module_default3.fileInfo, children: [
2456
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.fileCardName, children: comment.fileName || "\uD30C\uC77C" }),
2457
+ comment.fileSize != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.fileCardSize, children: formatFileSize(comment.fileSize) })
2458
+ ] })
2459
+ ]
2460
+ }
2231
2461
  )
2232
2462
  ] })
2233
2463
  ] }, comment.id)) })
@@ -2236,6 +2466,14 @@ function CommentThread({
2236
2466
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: pendingImage, alt: "pending" }),
2237
2467
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.pendingRemove, onClick: () => setPendingImage(null), type: "button", children: "\xD7" })
2238
2468
  ] }),
2469
+ pendingFile && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles_module_default3.pendingFile, children: [
2470
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.fileIcon, children: getFileIcon(pendingFile.type) }),
2471
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: styles_module_default3.fileInfo, children: [
2472
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.fileCardName, children: pendingFile.name }),
2473
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles_module_default3.fileCardSize, children: formatFileSize(pendingFile.size) })
2474
+ ] }),
2475
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.pendingRemove, onClick: () => setPendingFile(null), type: "button", children: "\xD7" })
2476
+ ] }),
2239
2477
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles_module_default3.replyArea, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2240
2478
  "textarea",
2241
2479
  {
@@ -2249,7 +2487,7 @@ function CommentThread({
2249
2487
  }
2250
2488
  ) }),
2251
2489
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles_module_default3.replyToolbar, children: [
2252
- /* @__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: [
2490
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: styles_module_default3.replyTool, onClick: handleAttachClick, type: "button", title: "\uD30C\uC77C \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: [
2253
2491
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
2254
2492
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
2255
2493
  ] }) }),
@@ -2260,9 +2498,9 @@ function CommentThread({
2260
2498
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2261
2499
  "button",
2262
2500
  {
2263
- className: `${styles_module_default3.replySend} ${replyText.trim() || pendingImage ? styles_module_default3.active : ""}`,
2501
+ className: `${styles_module_default3.replySend} ${replyText.trim() || pendingImage || pendingFile ? styles_module_default3.active : ""}`,
2264
2502
  onClick: handleSend,
2265
- disabled: !replyText.trim() && !pendingImage,
2503
+ disabled: !replyText.trim() && !pendingImage && !pendingFile,
2266
2504
  type: "button",
2267
2505
  title: "\uC804\uC1A1",
2268
2506
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
@@ -2277,7 +2515,7 @@ function CommentThread({
2277
2515
  {
2278
2516
  ref: fileInputRef,
2279
2517
  type: "file",
2280
- accept: "image/*",
2518
+ accept: "*/*",
2281
2519
  style: { display: "none" },
2282
2520
  onChange: handleFileChange
2283
2521
  }
@@ -2475,8 +2713,8 @@ var import_react5 = require("react");
2475
2713
  var import_react_dom = require("react-dom");
2476
2714
 
2477
2715
  // src/components/inbox-panel/styles.module.scss
2478
- var css5 = '@keyframes styles-module__slideIn___0o0s- {\n from {\n transform: translateX(100%);\n }\n to {\n transform: translateX(0);\n }\n}\n@keyframes styles-module__slideOut___nKrBX {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(100%);\n }\n}\n@keyframes styles-module__fadeIn___j09Ts {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.styles-module__backdrop___zYhcU {\n position: fixed;\n inset: 0;\n z-index: 100003;\n animation: styles-module__fadeIn___j09Ts 0.15s ease-out;\n}\n\n.styles-module__panel___6YS8k {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 420px;\n max-width: 100vw;\n background: #fafafa;\n z-index: 100004;\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.08);\n animation: styles-module__slideIn___0o0s- 0.2s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n.styles-module__panel___6YS8k.styles-module__exiting___6A-Ag {\n animation: styles-module__slideOut___nKrBX 0.15s ease-in forwards;\n}\n\n.styles-module__header___fBbGz {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid #e5e7eb;\n background: #fff;\n flex-shrink: 0;\n}\n\n.styles-module__headerLeft___e0YLf {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.styles-module__headerTitle___oEPJa {\n font-size: 14px;\n font-weight: 600;\n color: #18181b;\n}\n\n.styles-module__closeBtn___-H8ra {\n width: 32px;\n height: 32px;\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: #6b7280;\n transition: background 0.12s, color 0.12s;\n}\n.styles-module__closeBtn___-H8ra:hover {\n background: #f3f4f6;\n color: #18181b;\n}\n.styles-module__closeBtn___-H8ra svg {\n width: 18px;\n height: 18px;\n}\n\n.styles-module__tabs___nfuX7 {\n display: flex;\n padding: 0 20px;\n gap: 0;\n border-bottom: 1px solid #e5e7eb;\n background: #fff;\n flex-shrink: 0;\n}\n\n.styles-module__tab___Hc-jn {\n padding: 10px 16px;\n font-size: 13px;\n font-weight: 500;\n color: #6b7280;\n border: none;\n background: none;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n transition: color 0.12s, border-color 0.12s;\n font-family: inherit;\n}\n.styles-module__tab___Hc-jn:hover {\n color: #374151;\n}\n.styles-module__tab___Hc-jn.styles-module__active___ZQzA5 {\n color: #18181b;\n border-bottom-color: #18181b;\n}\n\n.styles-module__tabBadge___IdFDQ {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n background: #e5e7eb;\n color: #374151;\n font-size: 11px;\n font-weight: 600;\n padding: 0 5px;\n margin-left: 6px;\n}\n\n.styles-module__content___L2lRw {\n flex: 1;\n overflow-y: auto;\n padding: 12px;\n}\n.styles-module__content___L2lRw::-webkit-scrollbar {\n width: 4px;\n}\n.styles-module__content___L2lRw::-webkit-scrollbar-thumb {\n background: #d1d5db;\n border-radius: 2px;\n}\n\n.styles-module__card___F8qwd {\n background: #fff;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n margin-bottom: 12px;\n overflow: hidden;\n transition: box-shadow 0.12s;\n cursor: pointer;\n}\n.styles-module__card___F8qwd:hover {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n}\n\n.styles-module__cardHeader___QOVGR {\n padding: 14px 16px 8px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.styles-module__cardAvatar___FfKmW {\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__cardInfo___BmrdF {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__cardMeta___7p9KD {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 4px;\n}\n\n.styles-module__cardAuthor___YvnuR {\n font-size: 13px;\n font-weight: 600;\n color: #18181b;\n}\n\n.styles-module__cardTime___N1Png {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__cardActions___7QF72 {\n display: flex;\n gap: 2px;\n flex-shrink: 0;\n}\n\n.styles-module__cardAction___t4b3V {\n width: 26px;\n height: 26px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 5px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__cardAction___t4b3V:hover {\n background: #f3f4f6;\n color: #374151;\n}\n.styles-module__cardAction___t4b3V svg {\n width: 14px;\n height: 14px;\n}\n\n.styles-module__cardTitle___xJxDN {\n font-size: 13px;\n color: #1a1a1a;\n line-height: 1.5;\n padding: 0 16px 8px;\n}\n\n.styles-module__cardScreenshot___I5-QL {\n margin: 0 16px 8px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n position: relative;\n max-height: 160px;\n}\n.styles-module__cardScreenshot___I5-QL img {\n width: 100%;\n height: auto;\n display: block;\n object-fit: cover;\n}\n\n.styles-module__cardRoute___U4gB6 {\n margin: 0 16px 10px;\n padding: 8px 12px;\n background: #f9fafb;\n border: 1px solid #f3f4f6;\n border-radius: 8px;\n font-size: 12px;\n color: #6b7280;\n line-height: 1.4;\n}\n\n.styles-module__cardRouteName___BDH75 {\n font-weight: 500;\n color: #374151;\n}\n\n.styles-module__cardRoutePath___zP-fh {\n color: #9ca3af;\n}\n\n.styles-module__cardFooter___wfPa4 {\n padding: 8px 16px 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.styles-module__cardReplyDot___YnqzI {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #16a34a;\n flex-shrink: 0;\n}\n\n.styles-module__cardReplyCount___O1khL {\n font-size: 12px;\n font-weight: 500;\n color: #374151;\n}\n\n.styles-module__statusDot___l4IHG {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n.styles-module__statusDot___l4IHG.styles-module__todo___894P6 {\n background: #f59e0b;\n}\n.styles-module__statusDot___l4IHG.styles-module__inProgress___IZ1mJ {\n background: #3b82f6;\n}\n.styles-module__statusDot___l4IHG.styles-module__done___n4cWP {\n background: #16a34a;\n}\n\n.styles-module__empty___IADrw {\n text-align: center;\n padding: 48px 20px;\n color: #9ca3af;\n font-size: 13px;\n line-height: 1.6;\n}\n\n.styles-module__emptyIcon___1x5wT {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: #f3f4f6;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 12px;\n color: #9ca3af;\n}\n.styles-module__emptyIcon___1x5wT svg {\n width: 24px;\n height: 24px;\n}\n\n.styles-module__checkBtn___KjHxm {\n width: 28px;\n height: 28px;\n border: 2px solid #d1d5db;\n border-radius: 50%;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: transparent;\n transition: all 0.15s;\n padding: 0;\n flex-shrink: 0;\n}\n.styles-module__checkBtn___KjHxm svg {\n width: 14px;\n height: 14px;\n}\n.styles-module__checkBtn___KjHxm:hover {\n border-color: #16a34a;\n color: #16a34a;\n}\n.styles-module__checkBtn___KjHxm.styles-module__checked___8pype {\n border-color: #16a34a;\n background: #16a34a;\n color: #fff;\n}\n\n.styles-module__cardDone___-m4w- {\n opacity: 0.6;\n}\n.styles-module__cardDone___-m4w- .styles-module__cardTitle___xJxDN {\n text-decoration: line-through;\n color: #9ca3af;\n}\n\n.styles-module__backBtn___PTIAl {\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: #6b7280;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n flex-shrink: 0;\n}\n.styles-module__backBtn___PTIAl:hover {\n background: #f3f4f6;\n color: #18181b;\n}\n\n.styles-module__detailView___36cvH {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.styles-module__detailHeader___Hepgq {\n display: flex;\n gap: 10px;\n padding: 16px 20px;\n border-bottom: 1px solid #f3f4f6;\n}\n\n.styles-module__detailTitle___7TvrX {\n font-size: 13px;\n color: #1a1a1a;\n line-height: 1.5;\n margin-top: 4px;\n}\n\n.styles-module__detailScreenshot___UPF0f {\n margin: 0 20px 0;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n max-height: 180px;\n}\n.styles-module__detailScreenshot___UPF0f img {\n width: 100%;\n height: auto;\n display: block;\n object-fit: cover;\n}\n\n.styles-module__commentsList___5Uigg {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n}\n.styles-module__commentsList___5Uigg::-webkit-scrollbar {\n width: 4px;\n}\n.styles-module__commentsList___5Uigg::-webkit-scrollbar-thumb {\n background: #d1d5db;\n border-radius: 2px;\n}\n\n.styles-module__commentItem___5u78u {\n display: flex;\n gap: 10px;\n padding: 10px 20px;\n}\n.styles-module__commentItem___5u78u:hover {\n background: #f9fafb;\n}\n\n.styles-module__commentAvatar___OatKg {\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__commentBody___gmv1J {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__commentMeta___JNJa3 {\n display: flex;\n gap: 6px;\n align-items: center;\n margin-bottom: 2px;\n}\n\n.styles-module__commentAuthorName___XNVRk {\n font-size: 13px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.styles-module__commentTime___CokGf {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__commentContent___yMiLR {\n font-size: 13px;\n color: #374151;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n.styles-module__loadingComments___zpATD, .styles-module__noComments___kvl90 {\n text-align: center;\n padding: 24px 20px;\n color: #9ca3af;\n font-size: 13px;\n}\n\n.styles-module__replyBox___FO-XS {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n padding: 12px 20px;\n border-top: 1px solid #f3f4f6;\n background: #fff;\n}\n\n.styles-module__replyInput___k8C9e {\n flex: 1;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 8px 12px;\n font-size: 13px;\n font-family: inherit;\n color: #1a1a1a;\n resize: none;\n outline: none;\n min-height: 20px;\n max-height: 80px;\n line-height: 1.5;\n transition: border-color 0.12s;\n}\n.styles-module__replyInput___k8C9e:focus {\n border-color: #18181b;\n}\n.styles-module__replyInput___k8C9e::placeholder {\n color: #9ca3af;\n}\n\n.styles-module__sendBtn___pBzWa {\n width: 32px;\n height: 32px;\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: #d1d5db;\n transition: color 0.12s;\n padding: 0;\n flex-shrink: 0;\n}\n.styles-module__sendBtn___pBzWa.styles-module__active___ZQzA5 {\n color: #3b82f6;\n}\n.styles-module__sendBtn___pBzWa:hover {\n color: #3b82f6;\n}\n\n.styles-module__commentImage___LuH8X {\n margin-top: 6px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n max-width: 240px;\n cursor: pointer;\n transition: opacity 0.12s;\n}\n.styles-module__commentImage___LuH8X:hover {\n opacity: 0.9;\n}\n.styles-module__commentImage___LuH8X img {\n width: 100%;\n height: auto;\n display: block;\n}\n\n.styles-module__lightbox___8CDdt {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n.styles-module__lightbox___8CDdt img {\n max-width: 90vw;\n max-height: 90vh;\n border-radius: 8px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n cursor: default;\n}\n\n.styles-module__lightboxClose___VGfjh {\n position: absolute;\n top: 16px;\n right: 16px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: none;\n cursor: pointer;\n font-size: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.styles-module__lightboxClose___VGfjh:hover {\n background: rgba(255, 255, 255, 0.25);\n}';
2479
- var classNames5 = { "backdrop": "styles-module__backdrop___zYhcU", "fadeIn": "styles-module__fadeIn___j09Ts", "panel": "styles-module__panel___6YS8k", "slideIn": "styles-module__slideIn___0o0s-", "exiting": "styles-module__exiting___6A-Ag", "slideOut": "styles-module__slideOut___nKrBX", "header": "styles-module__header___fBbGz", "headerLeft": "styles-module__headerLeft___e0YLf", "headerTitle": "styles-module__headerTitle___oEPJa", "closeBtn": "styles-module__closeBtn___-H8ra", "tabs": "styles-module__tabs___nfuX7", "tab": "styles-module__tab___Hc-jn", "active": "styles-module__active___ZQzA5", "tabBadge": "styles-module__tabBadge___IdFDQ", "content": "styles-module__content___L2lRw", "card": "styles-module__card___F8qwd", "cardHeader": "styles-module__cardHeader___QOVGR", "cardAvatar": "styles-module__cardAvatar___FfKmW", "cardInfo": "styles-module__cardInfo___BmrdF", "cardMeta": "styles-module__cardMeta___7p9KD", "cardAuthor": "styles-module__cardAuthor___YvnuR", "cardTime": "styles-module__cardTime___N1Png", "cardActions": "styles-module__cardActions___7QF72", "cardAction": "styles-module__cardAction___t4b3V", "cardTitle": "styles-module__cardTitle___xJxDN", "cardScreenshot": "styles-module__cardScreenshot___I5-QL", "cardRoute": "styles-module__cardRoute___U4gB6", "cardRouteName": "styles-module__cardRouteName___BDH75", "cardRoutePath": "styles-module__cardRoutePath___zP-fh", "cardFooter": "styles-module__cardFooter___wfPa4", "cardReplyDot": "styles-module__cardReplyDot___YnqzI", "cardReplyCount": "styles-module__cardReplyCount___O1khL", "statusDot": "styles-module__statusDot___l4IHG", "todo": "styles-module__todo___894P6", "inProgress": "styles-module__inProgress___IZ1mJ", "done": "styles-module__done___n4cWP", "empty": "styles-module__empty___IADrw", "emptyIcon": "styles-module__emptyIcon___1x5wT", "checkBtn": "styles-module__checkBtn___KjHxm", "checked": "styles-module__checked___8pype", "cardDone": "styles-module__cardDone___-m4w-", "backBtn": "styles-module__backBtn___PTIAl", "detailView": "styles-module__detailView___36cvH", "detailHeader": "styles-module__detailHeader___Hepgq", "detailTitle": "styles-module__detailTitle___7TvrX", "detailScreenshot": "styles-module__detailScreenshot___UPF0f", "commentsList": "styles-module__commentsList___5Uigg", "commentItem": "styles-module__commentItem___5u78u", "commentAvatar": "styles-module__commentAvatar___OatKg", "commentBody": "styles-module__commentBody___gmv1J", "commentMeta": "styles-module__commentMeta___JNJa3", "commentAuthorName": "styles-module__commentAuthorName___XNVRk", "commentTime": "styles-module__commentTime___CokGf", "commentContent": "styles-module__commentContent___yMiLR", "loadingComments": "styles-module__loadingComments___zpATD", "noComments": "styles-module__noComments___kvl90", "replyBox": "styles-module__replyBox___FO-XS", "replyInput": "styles-module__replyInput___k8C9e", "sendBtn": "styles-module__sendBtn___pBzWa", "commentImage": "styles-module__commentImage___LuH8X", "lightbox": "styles-module__lightbox___8CDdt", "lightboxClose": "styles-module__lightboxClose___VGfjh" };
2716
+ var css5 = '@keyframes styles-module__slideIn___0o0s- {\n from {\n transform: translateX(100%);\n }\n to {\n transform: translateX(0);\n }\n}\n@keyframes styles-module__slideOut___nKrBX {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(100%);\n }\n}\n@keyframes styles-module__fadeIn___j09Ts {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.styles-module__backdrop___zYhcU {\n position: fixed;\n inset: 0;\n z-index: 100003;\n animation: styles-module__fadeIn___j09Ts 0.15s ease-out;\n}\n\n.styles-module__panel___6YS8k {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 420px;\n max-width: 100vw;\n background: #fafafa;\n z-index: 100004;\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.08);\n animation: styles-module__slideIn___0o0s- 0.2s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n.styles-module__panel___6YS8k.styles-module__exiting___6A-Ag {\n animation: styles-module__slideOut___nKrBX 0.15s ease-in forwards;\n}\n\n.styles-module__header___fBbGz {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid #e5e7eb;\n background: #fff;\n flex-shrink: 0;\n}\n\n.styles-module__headerLeft___e0YLf {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.styles-module__headerTitle___oEPJa {\n font-size: 14px;\n font-weight: 600;\n color: #18181b;\n}\n\n.styles-module__closeBtn___-H8ra {\n width: 32px;\n height: 32px;\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: #6b7280;\n transition: background 0.12s, color 0.12s;\n}\n.styles-module__closeBtn___-H8ra:hover {\n background: #f3f4f6;\n color: #18181b;\n}\n.styles-module__closeBtn___-H8ra svg {\n width: 18px;\n height: 18px;\n}\n\n.styles-module__tabs___nfuX7 {\n display: flex;\n padding: 0 20px;\n gap: 0;\n border-bottom: 1px solid #e5e7eb;\n background: #fff;\n flex-shrink: 0;\n}\n\n.styles-module__tab___Hc-jn {\n padding: 10px 16px;\n font-size: 13px;\n font-weight: 500;\n color: #6b7280;\n border: none;\n background: none;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n transition: color 0.12s, border-color 0.12s;\n font-family: inherit;\n}\n.styles-module__tab___Hc-jn:hover {\n color: #374151;\n}\n.styles-module__tab___Hc-jn.styles-module__active___ZQzA5 {\n color: #18181b;\n border-bottom-color: #18181b;\n}\n\n.styles-module__tabBadge___IdFDQ {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n border-radius: 9px;\n background: #e5e7eb;\n color: #374151;\n font-size: 11px;\n font-weight: 600;\n padding: 0 5px;\n margin-left: 6px;\n}\n\n.styles-module__content___L2lRw {\n flex: 1;\n overflow-y: auto;\n padding: 12px;\n}\n.styles-module__content___L2lRw::-webkit-scrollbar {\n width: 4px;\n}\n.styles-module__content___L2lRw::-webkit-scrollbar-thumb {\n background: #d1d5db;\n border-radius: 2px;\n}\n\n.styles-module__card___F8qwd {\n background: #fff;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n margin-bottom: 12px;\n overflow: hidden;\n transition: box-shadow 0.12s;\n cursor: pointer;\n}\n.styles-module__card___F8qwd:hover {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n}\n\n.styles-module__cardHeader___QOVGR {\n padding: 14px 16px 8px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.styles-module__cardAvatar___FfKmW {\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__cardInfo___BmrdF {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__cardMeta___7p9KD {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 4px;\n}\n\n.styles-module__cardAuthor___YvnuR {\n font-size: 13px;\n font-weight: 600;\n color: #18181b;\n}\n\n.styles-module__cardTime___N1Png {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__cardActions___7QF72 {\n display: flex;\n gap: 2px;\n flex-shrink: 0;\n}\n\n.styles-module__cardAction___t4b3V {\n width: 26px;\n height: 26px;\n border: none;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 5px;\n color: #9ca3af;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n}\n.styles-module__cardAction___t4b3V:hover {\n background: #f3f4f6;\n color: #374151;\n}\n.styles-module__cardAction___t4b3V svg {\n width: 14px;\n height: 14px;\n}\n\n.styles-module__cardTitle___xJxDN {\n font-size: 13px;\n color: #1a1a1a;\n line-height: 1.5;\n padding: 0 16px 8px;\n}\n\n.styles-module__cardScreenshot___I5-QL {\n margin: 0 16px 8px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n position: relative;\n max-height: 160px;\n}\n.styles-module__cardScreenshot___I5-QL img {\n width: 100%;\n height: auto;\n display: block;\n object-fit: cover;\n}\n\n.styles-module__cardRoute___U4gB6 {\n margin: 0 16px 10px;\n padding: 8px 12px;\n background: #f9fafb;\n border: 1px solid #f3f4f6;\n border-radius: 8px;\n font-size: 12px;\n color: #6b7280;\n line-height: 1.4;\n}\n\n.styles-module__cardRouteName___BDH75 {\n font-weight: 500;\n color: #374151;\n}\n\n.styles-module__cardRoutePath___zP-fh {\n color: #9ca3af;\n}\n\n.styles-module__cardFooter___wfPa4 {\n padding: 8px 16px 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.styles-module__cardReplyDot___YnqzI {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #16a34a;\n flex-shrink: 0;\n}\n\n.styles-module__cardReplyCount___O1khL {\n font-size: 12px;\n font-weight: 500;\n color: #374151;\n}\n\n.styles-module__statusDot___l4IHG {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n.styles-module__statusDot___l4IHG.styles-module__todo___894P6 {\n background: #f59e0b;\n}\n.styles-module__statusDot___l4IHG.styles-module__inProgress___IZ1mJ {\n background: #3b82f6;\n}\n.styles-module__statusDot___l4IHG.styles-module__done___n4cWP {\n background: #16a34a;\n}\n\n.styles-module__empty___IADrw {\n text-align: center;\n padding: 48px 20px;\n color: #9ca3af;\n font-size: 13px;\n line-height: 1.6;\n}\n\n.styles-module__emptyIcon___1x5wT {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: #f3f4f6;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 12px;\n color: #9ca3af;\n}\n.styles-module__emptyIcon___1x5wT svg {\n width: 24px;\n height: 24px;\n}\n\n.styles-module__checkBtn___KjHxm {\n width: 28px;\n height: 28px;\n border: 2px solid #d1d5db;\n border-radius: 50%;\n background: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: transparent;\n transition: all 0.15s;\n padding: 0;\n flex-shrink: 0;\n}\n.styles-module__checkBtn___KjHxm svg {\n width: 14px;\n height: 14px;\n}\n.styles-module__checkBtn___KjHxm:hover {\n border-color: #16a34a;\n color: #16a34a;\n}\n.styles-module__checkBtn___KjHxm.styles-module__checked___8pype {\n border-color: #16a34a;\n background: #16a34a;\n color: #fff;\n}\n\n.styles-module__cardDone___-m4w- {\n opacity: 0.6;\n}\n.styles-module__cardDone___-m4w- .styles-module__cardTitle___xJxDN {\n text-decoration: line-through;\n color: #9ca3af;\n}\n\n.styles-module__backBtn___PTIAl {\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: #6b7280;\n transition: background 0.12s, color 0.12s;\n padding: 0;\n flex-shrink: 0;\n}\n.styles-module__backBtn___PTIAl:hover {\n background: #f3f4f6;\n color: #18181b;\n}\n\n.styles-module__detailView___36cvH {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.styles-module__detailHeader___Hepgq {\n display: flex;\n gap: 10px;\n padding: 16px 20px;\n border-bottom: 1px solid #f3f4f6;\n}\n\n.styles-module__detailTitle___7TvrX {\n font-size: 13px;\n color: #1a1a1a;\n line-height: 1.5;\n margin-top: 4px;\n}\n\n.styles-module__detailScreenshot___UPF0f {\n margin: 0 20px 0;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n max-height: 180px;\n}\n.styles-module__detailScreenshot___UPF0f img {\n width: 100%;\n height: auto;\n display: block;\n object-fit: cover;\n}\n\n.styles-module__commentsList___5Uigg {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n}\n.styles-module__commentsList___5Uigg::-webkit-scrollbar {\n width: 4px;\n}\n.styles-module__commentsList___5Uigg::-webkit-scrollbar-thumb {\n background: #d1d5db;\n border-radius: 2px;\n}\n\n.styles-module__commentItem___5u78u {\n display: flex;\n gap: 10px;\n padding: 10px 20px;\n}\n.styles-module__commentItem___5u78u:hover {\n background: #f9fafb;\n}\n\n.styles-module__commentAvatar___OatKg {\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__commentBody___gmv1J {\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__commentMeta___JNJa3 {\n display: flex;\n gap: 6px;\n align-items: center;\n margin-bottom: 2px;\n}\n\n.styles-module__commentAuthorName___XNVRk {\n font-size: 13px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.styles-module__commentTime___CokGf {\n font-size: 12px;\n color: #9ca3af;\n}\n\n.styles-module__commentContent___yMiLR {\n font-size: 13px;\n color: #374151;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n.styles-module__loadingComments___zpATD, .styles-module__noComments___kvl90 {\n text-align: center;\n padding: 24px 20px;\n color: #9ca3af;\n font-size: 13px;\n}\n\n.styles-module__replyBox___FO-XS {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n padding: 12px 20px;\n border-top: 1px solid #f3f4f6;\n background: #fff;\n}\n\n.styles-module__replyInput___k8C9e {\n flex: 1;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 8px 12px;\n font-size: 13px;\n font-family: inherit;\n color: #1a1a1a;\n resize: none;\n outline: none;\n min-height: 20px;\n max-height: 80px;\n line-height: 1.5;\n transition: border-color 0.12s;\n}\n.styles-module__replyInput___k8C9e:focus {\n border-color: #18181b;\n}\n.styles-module__replyInput___k8C9e::placeholder {\n color: #9ca3af;\n}\n\n.styles-module__sendBtn___pBzWa {\n width: 32px;\n height: 32px;\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: #d1d5db;\n transition: color 0.12s;\n padding: 0;\n flex-shrink: 0;\n}\n.styles-module__sendBtn___pBzWa.styles-module__active___ZQzA5 {\n color: #3b82f6;\n}\n.styles-module__sendBtn___pBzWa:hover {\n color: #3b82f6;\n}\n\n.styles-module__commentImage___LuH8X {\n margin-top: 6px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n max-width: 240px;\n cursor: pointer;\n transition: opacity 0.12s;\n}\n.styles-module__commentImage___LuH8X:hover {\n opacity: 0.9;\n}\n.styles-module__commentImage___LuH8X img {\n width: 100%;\n height: auto;\n display: block;\n}\n\n.styles-module__commentFileCard___XE44y {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n background: #f9fafb;\n cursor: pointer;\n transition: background 0.12s;\n max-width: 240px;\n}\n.styles-module__commentFileCard___XE44y:hover {\n background: #f3f4f6;\n}\n\n.styles-module__commentFileIcon___G58-y {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n background: #e5e7eb;\n color: #374151;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n flex-shrink: 0;\n letter-spacing: 0.5px;\n}\n\n.styles-module__commentFileInfo___OFIhR {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.styles-module__commentFileName___NUoII {\n font-size: 12px;\n font-weight: 500;\n color: #1a1a1a;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.styles-module__commentFileSize___sokU8 {\n font-size: 11px;\n color: #9ca3af;\n}\n\n.styles-module__filePreviewOverlay___5RxYd {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.styles-module__filePreviewModal___MGJLW {\n width: 90vw;\n max-width: 960px;\n height: 80vh;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\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}\n\n.styles-module__filePreviewHeader___E9SWJ {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid #e5e7eb;\n flex-shrink: 0;\n}\n\n.styles-module__filePreviewName___z8t-L {\n font-size: 14px;\n font-weight: 600;\n color: #18181b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n flex: 1;\n min-width: 0;\n}\n\n.styles-module__filePreviewActions___weizo {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.styles-module__filePreviewOpen___JpaEs {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 500;\n color: #3b82f6;\n text-decoration: none;\n padding: 4px 8px;\n border-radius: 6px;\n transition: background 0.12s;\n}\n.styles-module__filePreviewOpen___JpaEs:hover {\n background: #eff6ff;\n}\n\n.styles-module__filePreviewClose___TFcbr {\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: #6b7280;\n font-size: 18px;\n transition: background 0.12s, color 0.12s;\n}\n.styles-module__filePreviewClose___TFcbr:hover {\n background: #f3f4f6;\n color: #18181b;\n}\n\n.styles-module__filePreviewBody___gwg1S {\n flex: 1;\n overflow: hidden;\n}\n\n.styles-module__filePreviewIframe___OQ3Oo {\n width: 100%;\n height: 100%;\n border: none;\n}\n\n.styles-module__filePreviewFallback___U1rB2 {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 12px;\n color: #6b7280;\n font-size: 14px;\n}\n.styles-module__filePreviewFallback___U1rB2 a {\n color: #3b82f6;\n text-decoration: none;\n font-weight: 500;\n}\n.styles-module__filePreviewFallback___U1rB2 a:hover {\n text-decoration: underline;\n}\n\n.styles-module__filePreviewFallbackIcon___0--41 {\n width: 56px;\n height: 56px;\n border-radius: 12px;\n background: #f3f4f6;\n color: #6b7280;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 700;\n}\n\n.styles-module__lightbox___8CDdt {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n.styles-module__lightbox___8CDdt img {\n max-width: 90vw;\n max-height: 90vh;\n border-radius: 8px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n cursor: default;\n}\n\n.styles-module__lightboxClose___VGfjh {\n position: absolute;\n top: 16px;\n right: 16px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: none;\n cursor: pointer;\n font-size: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.styles-module__lightboxClose___VGfjh:hover {\n background: rgba(255, 255, 255, 0.25);\n}';
2717
+ var classNames5 = { "backdrop": "styles-module__backdrop___zYhcU", "fadeIn": "styles-module__fadeIn___j09Ts", "panel": "styles-module__panel___6YS8k", "slideIn": "styles-module__slideIn___0o0s-", "exiting": "styles-module__exiting___6A-Ag", "slideOut": "styles-module__slideOut___nKrBX", "header": "styles-module__header___fBbGz", "headerLeft": "styles-module__headerLeft___e0YLf", "headerTitle": "styles-module__headerTitle___oEPJa", "closeBtn": "styles-module__closeBtn___-H8ra", "tabs": "styles-module__tabs___nfuX7", "tab": "styles-module__tab___Hc-jn", "active": "styles-module__active___ZQzA5", "tabBadge": "styles-module__tabBadge___IdFDQ", "content": "styles-module__content___L2lRw", "card": "styles-module__card___F8qwd", "cardHeader": "styles-module__cardHeader___QOVGR", "cardAvatar": "styles-module__cardAvatar___FfKmW", "cardInfo": "styles-module__cardInfo___BmrdF", "cardMeta": "styles-module__cardMeta___7p9KD", "cardAuthor": "styles-module__cardAuthor___YvnuR", "cardTime": "styles-module__cardTime___N1Png", "cardActions": "styles-module__cardActions___7QF72", "cardAction": "styles-module__cardAction___t4b3V", "cardTitle": "styles-module__cardTitle___xJxDN", "cardScreenshot": "styles-module__cardScreenshot___I5-QL", "cardRoute": "styles-module__cardRoute___U4gB6", "cardRouteName": "styles-module__cardRouteName___BDH75", "cardRoutePath": "styles-module__cardRoutePath___zP-fh", "cardFooter": "styles-module__cardFooter___wfPa4", "cardReplyDot": "styles-module__cardReplyDot___YnqzI", "cardReplyCount": "styles-module__cardReplyCount___O1khL", "statusDot": "styles-module__statusDot___l4IHG", "todo": "styles-module__todo___894P6", "inProgress": "styles-module__inProgress___IZ1mJ", "done": "styles-module__done___n4cWP", "empty": "styles-module__empty___IADrw", "emptyIcon": "styles-module__emptyIcon___1x5wT", "checkBtn": "styles-module__checkBtn___KjHxm", "checked": "styles-module__checked___8pype", "cardDone": "styles-module__cardDone___-m4w-", "backBtn": "styles-module__backBtn___PTIAl", "detailView": "styles-module__detailView___36cvH", "detailHeader": "styles-module__detailHeader___Hepgq", "detailTitle": "styles-module__detailTitle___7TvrX", "detailScreenshot": "styles-module__detailScreenshot___UPF0f", "commentsList": "styles-module__commentsList___5Uigg", "commentItem": "styles-module__commentItem___5u78u", "commentAvatar": "styles-module__commentAvatar___OatKg", "commentBody": "styles-module__commentBody___gmv1J", "commentMeta": "styles-module__commentMeta___JNJa3", "commentAuthorName": "styles-module__commentAuthorName___XNVRk", "commentTime": "styles-module__commentTime___CokGf", "commentContent": "styles-module__commentContent___yMiLR", "loadingComments": "styles-module__loadingComments___zpATD", "noComments": "styles-module__noComments___kvl90", "replyBox": "styles-module__replyBox___FO-XS", "replyInput": "styles-module__replyInput___k8C9e", "sendBtn": "styles-module__sendBtn___pBzWa", "commentImage": "styles-module__commentImage___LuH8X", "commentFileCard": "styles-module__commentFileCard___XE44y", "commentFileIcon": "styles-module__commentFileIcon___G58-y", "commentFileInfo": "styles-module__commentFileInfo___OFIhR", "commentFileName": "styles-module__commentFileName___NUoII", "commentFileSize": "styles-module__commentFileSize___sokU8", "filePreviewOverlay": "styles-module__filePreviewOverlay___5RxYd", "filePreviewModal": "styles-module__filePreviewModal___MGJLW", "filePreviewHeader": "styles-module__filePreviewHeader___E9SWJ", "filePreviewName": "styles-module__filePreviewName___z8t-L", "filePreviewActions": "styles-module__filePreviewActions___weizo", "filePreviewOpen": "styles-module__filePreviewOpen___JpaEs", "filePreviewClose": "styles-module__filePreviewClose___TFcbr", "filePreviewBody": "styles-module__filePreviewBody___gwg1S", "filePreviewIframe": "styles-module__filePreviewIframe___OQ3Oo", "filePreviewFallback": "styles-module__filePreviewFallback___U1rB2", "filePreviewFallbackIcon": "styles-module__filePreviewFallbackIcon___0--41", "lightbox": "styles-module__lightbox___8CDdt", "lightboxClose": "styles-module__lightboxClose___VGfjh" };
2480
2718
  if (typeof document !== "undefined") {
2481
2719
  let style = document.getElementById("impakers-debug-styles-inbox-panel-styles");
2482
2720
  if (!style) {
@@ -2503,6 +2741,74 @@ function ImageLightbox({ src, onClose }) {
2503
2741
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: styles_module_default5.lightboxClose, onClick: onClose, type: "button", children: "\xD7" })
2504
2742
  ] });
2505
2743
  }
2744
+ function formatFileSize2(bytes) {
2745
+ if (bytes < 1024) return `${bytes}B`;
2746
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}KB`;
2747
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
2748
+ }
2749
+ function getFileIcon2(mimeType) {
2750
+ if (mimeType.includes("pdf")) return "PDF";
2751
+ if (mimeType.includes("spreadsheet") || mimeType.includes("excel") || mimeType.includes("csv")) return "XLS";
2752
+ if (mimeType.includes("presentation") || mimeType.includes("powerpoint")) return "PPT";
2753
+ if (mimeType.includes("document") || mimeType.includes("word")) return "DOC";
2754
+ if (mimeType.includes("zip") || mimeType.includes("compressed") || mimeType.includes("archive")) return "ZIP";
2755
+ if (mimeType.includes("video")) return "VID";
2756
+ if (mimeType.includes("audio")) return "AUD";
2757
+ return "FILE";
2758
+ }
2759
+ function extractDriveFileId(url) {
2760
+ const match = url.match(/\/d\/([^/]+)/);
2761
+ return match ? match[1] : null;
2762
+ }
2763
+ function getPreviewSrc(fileUrl, fileType, fileSource) {
2764
+ const isOffice = /\.(pptx?|xlsx?|docx?)$/i.test(fileUrl) || fileType.includes("presentation") || fileType.includes("powerpoint") || fileType.includes("spreadsheet") || fileType.includes("excel") || fileType.includes("document") || fileType.includes("word") || fileType.includes("msword");
2765
+ if (fileSource === "google_drive") {
2766
+ const fileId = extractDriveFileId(fileUrl);
2767
+ if (fileId) return `https://drive.google.com/file/d/${fileId}/preview`;
2768
+ }
2769
+ if (isOffice && fileSource !== "google_drive") {
2770
+ return `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(fileUrl)}`;
2771
+ }
2772
+ if (fileType.includes("pdf")) return fileUrl;
2773
+ return null;
2774
+ }
2775
+ function FilePreviewModal({
2776
+ fileUrl,
2777
+ fileName,
2778
+ fileType,
2779
+ fileSource,
2780
+ onClose
2781
+ }) {
2782
+ (0, import_react5.useEffect)(() => {
2783
+ const handler = (e) => {
2784
+ if (e.key === "Escape") onClose();
2785
+ };
2786
+ document.addEventListener("keydown", handler);
2787
+ return () => document.removeEventListener("keydown", handler);
2788
+ }, [onClose]);
2789
+ const previewSrc = getPreviewSrc(fileUrl, fileType, fileSource);
2790
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: styles_module_default5.filePreviewOverlay, onClick: onClose, "data-impakers-debug": "", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: styles_module_default5.filePreviewModal, onClick: (e) => e.stopPropagation(), children: [
2791
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: styles_module_default5.filePreviewHeader, children: [
2792
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: styles_module_default5.filePreviewName, children: fileName }),
2793
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: styles_module_default5.filePreviewActions, children: [
2794
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("a", { href: fileUrl, target: "_blank", rel: "noopener noreferrer", className: styles_module_default5.filePreviewOpen, children: [
2795
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "14", height: "14", children: [
2796
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
2797
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "15 3 21 3 21 9" }),
2798
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
2799
+ ] }),
2800
+ "\uC5F4\uAE30"
2801
+ ] }),
2802
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: styles_module_default5.filePreviewClose, onClick: onClose, type: "button", children: "\xD7" })
2803
+ ] })
2804
+ ] }),
2805
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: styles_module_default5.filePreviewBody, children: previewSrc ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("iframe", { src: previewSrc, className: styles_module_default5.filePreviewIframe, title: fileName }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: styles_module_default5.filePreviewFallback, children: [
2806
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: styles_module_default5.filePreviewFallbackIcon, children: getFileIcon2(fileType) }),
2807
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\uBBF8\uB9AC\uBCF4\uAE30\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4" }),
2808
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: fileUrl, target: "_blank", rel: "noopener noreferrer", children: "\uD30C\uC77C \uC5F4\uAE30" })
2809
+ ] }) })
2810
+ ] }) });
2811
+ }
2506
2812
  function timeAgo2(dateStr) {
2507
2813
  const diff = Date.now() - new Date(dateStr).getTime();
2508
2814
  const m = Math.floor(diff / 6e4);
@@ -2535,6 +2841,7 @@ function InboxPanel({
2535
2841
  const [loadingComments, setLoadingComments] = (0, import_react5.useState)(false);
2536
2842
  const [replyText, setReplyText] = (0, import_react5.useState)("");
2537
2843
  const [lightboxSrc, setLightboxSrc] = (0, import_react5.useState)(null);
2844
+ const [filePreview, setFilePreview] = (0, import_react5.useState)(null);
2538
2845
  const replyRef = (0, import_react5.useRef)(null);
2539
2846
  const commentsEndRef = (0, import_react5.useRef)(null);
2540
2847
  const handleClose = (0, import_react5.useCallback)(() => {
@@ -2553,16 +2860,30 @@ function InboxPanel({
2553
2860
  return;
2554
2861
  }
2555
2862
  setSelectedItem(item);
2556
- setLoadingComments(true);
2557
- try {
2558
- const data = await fetchComments(endpoint, item.id);
2559
- setComments(data);
2560
- } catch {
2561
- setComments([]);
2562
- } finally {
2863
+ const cached = getCachedSnapshot(getCommentsCacheKey(item.id));
2864
+ setComments(cached || []);
2865
+ setLoadingComments(!cached);
2866
+ }, [endpoint]);
2867
+ (0, import_react5.useEffect)(() => {
2868
+ if (!selectedItem) return;
2869
+ const cacheKey = getCommentsCacheKey(selectedItem.id);
2870
+ const cached = getCachedSnapshot(cacheKey);
2871
+ if (cached) {
2872
+ setComments(cached);
2563
2873
  setLoadingComments(false);
2564
2874
  }
2565
- }, [endpoint]);
2875
+ const unsubscribe = subscribeCache(cacheKey, (nextComments) => {
2876
+ setComments(nextComments);
2877
+ setLoadingComments(false);
2878
+ });
2879
+ fetchComments(endpoint, selectedItem.id, { staleWhileRevalidate: true }).then((data) => {
2880
+ setComments(data);
2881
+ setLoadingComments(false);
2882
+ }).catch(() => {
2883
+ setLoadingComments(false);
2884
+ });
2885
+ return unsubscribe;
2886
+ }, [selectedItem, endpoint]);
2566
2887
  (0, import_react5.useEffect)(() => {
2567
2888
  commentsEndRef.current?.scrollIntoView({ behavior: "smooth" });
2568
2889
  }, [comments.length]);
@@ -2573,17 +2894,8 @@ function InboxPanel({
2573
2894
  if (!replyText.trim() || !selectedItem) return;
2574
2895
  const content = replyText.trim();
2575
2896
  setReplyText("");
2576
- const temp = {
2577
- id: `temp-${Date.now()}`,
2578
- content,
2579
- authorName: currentUserName,
2580
- authorId: currentUserId,
2581
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
2582
- };
2583
- setComments((prev) => [...prev, temp]);
2584
2897
  try {
2585
- const serverComment = await postComment(endpoint, selectedItem.id, content, currentUserName, currentUserId);
2586
- setComments((prev) => prev.map((c) => c.id === temp.id ? serverComment : c));
2898
+ await postComment(endpoint, selectedItem.id, content, currentUserName, currentUserId);
2587
2899
  } catch (err) {
2588
2900
  console.error("[@impakers/debug] \uCF54\uBA58\uD2B8 \uC804\uC1A1 \uC2E4\uD328:", err);
2589
2901
  }
@@ -2597,15 +2909,12 @@ function InboxPanel({
2597
2909
  }
2598
2910
  if (e.key === "Escape") handleClose();
2599
2911
  }, [handleSendReply, handleClose]);
2600
- const [statusOverrides, setStatusOverrides] = (0, import_react5.useState)({});
2601
2912
  const handleToggleStatus = (0, import_react5.useCallback)((item, e) => {
2602
2913
  e.stopPropagation();
2603
2914
  const newStatus = item.status === "done" ? "todo" : "done";
2604
- setStatusOverrides((prev) => ({ ...prev, [item.id]: newStatus }));
2605
2915
  onStatusChange?.(item.id, newStatus);
2606
2916
  }, [onStatusChange]);
2607
- const applyOverrides = (items2) => items2.map((item) => statusOverrides[item.id] ? { ...item, status: statusOverrides[item.id] } : item);
2608
- const items = applyOverrides(tab === "page" ? pageItems : allItems);
2917
+ const items = tab === "page" ? pageItems : allItems;
2609
2918
  if (typeof document === "undefined") return null;
2610
2919
  return (0, import_react_dom.createPortal)(
2611
2920
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { "data-impakers-debug": "", children: [
@@ -2654,6 +2963,28 @@ function InboxPanel({
2654
2963
  },
2655
2964
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: c.imageUrl, alt: "attachment" })
2656
2965
  }
2966
+ ),
2967
+ c.fileUrl && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2968
+ "div",
2969
+ {
2970
+ className: styles_module_default5.commentFileCard,
2971
+ onClick: (e) => {
2972
+ e.stopPropagation();
2973
+ setFilePreview({
2974
+ url: c.fileUrl,
2975
+ name: c.fileName || "\uD30C\uC77C",
2976
+ type: c.fileType || "",
2977
+ source: c.fileSource
2978
+ });
2979
+ },
2980
+ children: [
2981
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: styles_module_default5.commentFileIcon, children: getFileIcon2(c.fileType || "") }),
2982
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: styles_module_default5.commentFileInfo, children: [
2983
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: styles_module_default5.commentFileName, children: c.fileName || "\uD30C\uC77C" }),
2984
+ c.fileSize != null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: styles_module_default5.commentFileSize, children: formatFileSize2(c.fileSize) })
2985
+ ] })
2986
+ ]
2987
+ }
2657
2988
  )
2658
2989
  ] })
2659
2990
  ] }, c.id)),
@@ -2732,7 +3063,17 @@ function InboxPanel({
2732
3063
  ] }, item.id)) })
2733
3064
  ] })
2734
3065
  ] }),
2735
- lightboxSrc && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ImageLightbox, { src: lightboxSrc, onClose: () => setLightboxSrc(null) })
3066
+ lightboxSrc && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ImageLightbox, { src: lightboxSrc, onClose: () => setLightboxSrc(null) }),
3067
+ filePreview && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3068
+ FilePreviewModal,
3069
+ {
3070
+ fileUrl: filePreview.url,
3071
+ fileName: filePreview.name,
3072
+ fileType: filePreview.type,
3073
+ fileSource: filePreview.source,
3074
+ onClose: () => setFilePreview(null)
3075
+ }
3076
+ )
2736
3077
  ] }),
2737
3078
  document.body
2738
3079
  );
@@ -2915,6 +3256,20 @@ function saveRouteAnnotations(annotations) {
2915
3256
  } catch {
2916
3257
  }
2917
3258
  }
3259
+ function taskToAnnotation(task) {
3260
+ if (!task.feedbackMarker) return null;
3261
+ return {
3262
+ id: task.id,
3263
+ x: task.feedbackMarker.x,
3264
+ y: task.feedbackMarker.y,
3265
+ comment: task.title.replace(/^\[피드백\]\s*/, ""),
3266
+ element: task.feedbackMarker.element || "",
3267
+ elementPath: "",
3268
+ timestamp: new Date(task.createdAt).getTime(),
3269
+ isFixed: task.feedbackMarker.isFixed,
3270
+ boundingBox: task.feedbackMarker.boundingBox
3271
+ };
3272
+ }
2918
3273
  function DebugWidget({ endpoint, getUser, onHide }) {
2919
3274
  const [isActive, setIsActive] = (0, import_react7.useState)(false);
2920
3275
  const [annotations, setAnnotations] = (0, import_react7.useState)(() => loadRouteAnnotations());
@@ -2933,6 +3288,8 @@ function DebugWidget({ endpoint, getUser, onHide }) {
2933
3288
  const [showInbox, setShowInbox] = (0, import_react7.useState)(false);
2934
3289
  const [showSettings, setShowSettings] = (0, import_react7.useState)(false);
2935
3290
  const [settings, setSettings] = (0, import_react7.useState)(() => loadSettings());
3291
+ const [routePath, setRoutePath] = (0, import_react7.useState)(() => window.location.pathname);
3292
+ const [hasHydratedServerTasks, setHasHydratedServerTasks] = (0, import_react7.useState)(false);
2936
3293
  const popupRef = (0, import_react7.useRef)(null);
2937
3294
  const recentlyAddedIdRef = (0, import_react7.useRef)(null);
2938
3295
  const justSubmittedRef = (0, import_react7.useRef)(false);
@@ -2976,6 +3333,7 @@ function DebugWidget({ endpoint, getUser, onHide }) {
2976
3333
  (0, import_react7.useEffect)(() => {
2977
3334
  setAnnotations(loadRouteAnnotations());
2978
3335
  const handleRouteChange = () => {
3336
+ setRoutePath(window.location.pathname);
2979
3337
  setAnnotations(loadRouteAnnotations());
2980
3338
  setPendingAnnotation(null);
2981
3339
  setHoverInfo(null);
@@ -2992,25 +3350,29 @@ function DebugWidget({ endpoint, getUser, onHide }) {
2992
3350
  };
2993
3351
  }, []);
2994
3352
  (0, import_react7.useEffect)(() => {
2995
- const currentPath2 = window.location.pathname;
2996
- fetchFeedbacks(endpoint, currentPath2).then((tasks) => {
3353
+ const cacheKey = getFeedbacksCacheKey(routePath);
3354
+ const cached = getCachedSnapshot(cacheKey);
3355
+ if (cached) {
3356
+ setServerTasks(cached);
3357
+ setHasHydratedServerTasks(true);
3358
+ }
3359
+ const unsubscribe = subscribeCache(cacheKey, (tasks) => {
3360
+ setServerTasks(tasks);
3361
+ setHasHydratedServerTasks(true);
3362
+ });
3363
+ fetchFeedbacks(endpoint, routePath, { staleWhileRevalidate: true }).then((tasks) => {
2997
3364
  setServerTasks(tasks);
2998
- const serverAnnotations = tasks.filter((t) => t.feedbackMarker).map((t) => ({
2999
- id: t.id,
3000
- x: t.feedbackMarker.x,
3001
- y: t.feedbackMarker.y,
3002
- comment: t.title.replace(/^\[피드백\]\s*/, ""),
3003
- element: t.feedbackMarker.element || "",
3004
- elementPath: "",
3005
- timestamp: new Date(t.createdAt).getTime(),
3006
- isFixed: t.feedbackMarker.isFixed,
3007
- boundingBox: t.feedbackMarker.boundingBox
3008
- }));
3009
- setAnnotations(serverAnnotations);
3010
- saveRouteAnnotations(serverAnnotations);
3365
+ setHasHydratedServerTasks(true);
3011
3366
  }).catch(() => {
3012
3367
  });
3013
- }, [endpoint]);
3368
+ return unsubscribe;
3369
+ }, [endpoint, routePath]);
3370
+ (0, import_react7.useEffect)(() => {
3371
+ if (!hasHydratedServerTasks) return;
3372
+ const serverAnnotations = serverTasks.map(taskToAnnotation).filter((annotation) => annotation !== null);
3373
+ setAnnotations(serverAnnotations);
3374
+ saveRouteAnnotations(serverAnnotations);
3375
+ }, [serverTasks, hasHydratedServerTasks]);
3014
3376
  const isInitialMount = (0, import_react7.useRef)(true);
3015
3377
  (0, import_react7.useEffect)(() => {
3016
3378
  if (isInitialMount.current) {
@@ -3216,11 +3578,6 @@ ${elementInfo.join("\n")}`);
3216
3578
  **\uCD5C\uADFC \uCF58\uC194 \uC5D0\uB7EC**:`);
3217
3579
  metadata.consoleErrors.forEach((e) => descriptionParts.push(`- \`${e.slice(0, 200)}\``));
3218
3580
  }
3219
- if (metadata.networkErrors?.length) {
3220
- descriptionParts.push(`
3221
- **\uCD5C\uADFC \uB124\uD2B8\uC6CC\uD06C \uC2E4\uD328**:`);
3222
- metadata.networkErrors.forEach((e) => descriptionParts.push(`- ${e.method} ${e.url} \u2192 ${e.status || "FAILED"}`));
3223
- }
3224
3581
  const result = await submitFeedback(endpoint, {
3225
3582
  title: comment.slice(0, 100),
3226
3583
  description: descriptionParts.join("\n"),
@@ -3238,26 +3595,14 @@ ${elementInfo.join("\n")}`);
3238
3595
  });
3239
3596
  setShowToast(true);
3240
3597
  originalSetTimeout(() => setShowToast(false), 3e3);
3241
- const newAnnotation = {
3242
- id: result.taskId || Date.now().toString(),
3243
- // 서버 UUID 사용
3244
- x: pendingAnnotation.x,
3245
- y: pendingAnnotation.y,
3246
- comment,
3247
- element: pendingAnnotation.element,
3248
- elementPath: pendingAnnotation.elementPath,
3249
- timestamp: Date.now(),
3250
- selectedText: pendingAnnotation.selectedText,
3251
- boundingBox: pendingAnnotation.boundingBox,
3252
- isFixed: pendingAnnotation.isFixed
3253
- };
3254
- setAnnotations((prev) => [...prev, newAnnotation]);
3255
- recentlyAddedIdRef.current = newAnnotation.id;
3598
+ const newTaskId = result.taskId || null;
3599
+ recentlyAddedIdRef.current = newTaskId;
3256
3600
  originalSetTimeout(() => {
3257
3601
  recentlyAddedIdRef.current = null;
3258
3602
  }, 300);
3259
3603
  originalSetTimeout(() => {
3260
- setAnimatedMarkers((prev) => new Set(prev).add(newAnnotation.id));
3604
+ if (!newTaskId) return;
3605
+ setAnimatedMarkers((prev) => new Set(prev).add(newTaskId));
3261
3606
  }, 250);
3262
3607
  setPendingExiting(true);
3263
3608
  originalSetTimeout(() => {
@@ -3296,43 +3641,34 @@ ${elementInfo.join("\n")}`);
3296
3641
  const newId = annotation.id;
3297
3642
  setActiveThread((prev) => {
3298
3643
  if (prev === newId) return null;
3299
- fetchComments(endpoint, newId).then((comments) => {
3300
- setThreadComments((prev2) => ({ ...prev2, [newId]: comments }));
3301
- }).catch(() => {
3302
- });
3303
3644
  return newId;
3304
3645
  });
3305
- }, [endpoint]);
3306
- const handleThreadReply = (0, import_react7.useCallback)(async (taskId, content, screenshot) => {
3646
+ }, []);
3647
+ (0, import_react7.useEffect)(() => {
3648
+ if (!activeThread) return;
3649
+ const cacheKey = getCommentsCacheKey(activeThread);
3650
+ const cached = getCachedSnapshot(cacheKey);
3651
+ if (cached) {
3652
+ setThreadComments((prev) => ({ ...prev, [activeThread]: cached }));
3653
+ }
3654
+ const unsubscribe = subscribeCache(cacheKey, (comments) => {
3655
+ setThreadComments((prev) => ({ ...prev, [activeThread]: comments }));
3656
+ });
3657
+ fetchComments(endpoint, activeThread, { staleWhileRevalidate: true }).catch(() => {
3658
+ });
3659
+ return unsubscribe;
3660
+ }, [activeThread, endpoint]);
3661
+ const handleThreadReply = (0, import_react7.useCallback)(async (taskId, content, screenshot, file) => {
3307
3662
  const authorName = getUser?.()?.name ? String(getUser().name) : "\uC775\uBA85";
3308
3663
  const authorId = getUser?.()?.id ? String(getUser().id) : void 0;
3309
- const tempComment = {
3310
- id: `temp-${Date.now()}`,
3311
- content,
3312
- authorName,
3313
- authorId,
3314
- imageUrl: screenshot ? screenshot : void 0,
3315
- // 로컬 미리보기
3316
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
3317
- };
3318
- setThreadComments((prev) => ({
3319
- ...prev,
3320
- [taskId]: [...prev[taskId] || [], tempComment]
3321
- }));
3322
3664
  try {
3323
- const serverComment = await postComment(endpoint, taskId, content, authorName, authorId, screenshot);
3324
- setThreadComments((prev) => ({
3325
- ...prev,
3326
- [taskId]: (prev[taskId] || []).map(
3327
- (c) => c.id === tempComment.id ? { ...serverComment } : c
3328
- )
3329
- }));
3665
+ let fileResult;
3666
+ if (file) {
3667
+ fileResult = await uploadFile(endpoint, file, "comment", taskId);
3668
+ }
3669
+ await postComment(endpoint, taskId, content, authorName, authorId, screenshot, fileResult);
3330
3670
  } catch (err) {
3331
3671
  console.error("[@impakers/debug] \uCF54\uBA58\uD2B8 \uC804\uC1A1 \uC2E4\uD328:", err);
3332
- setThreadComments((prev) => ({
3333
- ...prev,
3334
- [taskId]: (prev[taskId] || []).filter((c) => c.id !== tempComment.id)
3335
- }));
3336
3672
  }
3337
3673
  }, [getUser, endpoint]);
3338
3674
  const handleThreadClose = (0, import_react7.useCallback)(() => {
@@ -3400,16 +3736,16 @@ ${elementInfo.join("\n")}`);
3400
3736
  ] })
3401
3737
  }
3402
3738
  ];
3403
- const currentPath = typeof window !== "undefined" ? window.location.pathname : "/";
3404
- const allInboxItems = annotations.map((a) => ({
3405
- id: a.id,
3406
- title: a.comment || "\uD53C\uB4DC\uBC31",
3407
- status: "todo",
3408
- priority: "medium",
3739
+ const currentPath = routePath;
3740
+ const allInboxItems = serverTasks.map((task) => ({
3741
+ id: task.id,
3742
+ title: task.title || "\uD53C\uB4DC\uBC31",
3743
+ status: task.status,
3744
+ priority: task.priority,
3409
3745
  authorName: getUser?.()?.name ? String(getUser().name) : "\uC775\uBA85",
3410
- feedbackUrl: currentPath,
3411
- createdAt: new Date(a.timestamp).toISOString(),
3412
- commentCount: (threadComments[a.id] || []).length
3746
+ feedbackUrl: task.feedbackUrl || currentPath,
3747
+ createdAt: task.createdAt,
3748
+ commentCount: task.commentCount || 0
3413
3749
  }));
3414
3750
  const allRouteItems = (() => {
3415
3751
  const items = [...allInboxItems];
@@ -3461,9 +3797,6 @@ ${elementInfo.join("\n")}`);
3461
3797
  onStatusChange: async (taskId, status) => {
3462
3798
  try {
3463
3799
  await updateTaskStatus(endpoint, taskId, status);
3464
- const currentPath2 = window.location.pathname;
3465
- const tasks = await fetchFeedbacks(endpoint, currentPath2);
3466
- setServerTasks(tasks);
3467
3800
  } catch (err) {
3468
3801
  console.error("[@impakers/debug] \uC0C1\uD0DC \uBCC0\uACBD \uC2E4\uD328:", err);
3469
3802
  }
@@ -3596,6 +3929,7 @@ ${elementInfo.join("\n")}`);
3596
3929
  ),
3597
3930
  activeThread && (() => {
3598
3931
  const annotation = annotations.find((a) => a.id === activeThread);
3932
+ const taskData = serverTasks.find((task2) => task2.id === activeThread);
3599
3933
  if (!annotation) return null;
3600
3934
  const markerX = annotation.x / 100 * window.innerWidth;
3601
3935
  const markerY = annotation.isFixed ? annotation.y : annotation.y - window.scrollY;
@@ -3603,9 +3937,9 @@ ${elementInfo.join("\n")}`);
3603
3937
  const threadTop = Math.max(10, Math.min(markerY, window.innerHeight - 400));
3604
3938
  const task = {
3605
3939
  id: annotation.id,
3606
- title: annotation.comment || "\uD53C\uB4DC\uBC31",
3607
- status: "todo",
3608
- createdAt: new Date(annotation.timestamp).toISOString(),
3940
+ title: taskData?.title || annotation.comment || "\uD53C\uB4DC\uBC31",
3941
+ status: taskData?.status || "todo",
3942
+ createdAt: taskData?.createdAt || new Date(annotation.timestamp).toISOString(),
3609
3943
  comments: threadComments[annotation.id] || []
3610
3944
  };
3611
3945
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(