@xhub-short/ui 0.1.0-beta.8 → 1.0.0-beta.18

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.
Files changed (75) hide show
  1. package/dist/CommentSheet.css-DuBy01rU.d.ts +219 -0
  2. package/dist/VideoSlotPlayIndicator-DPs8Xt5C.d.ts +51 -0
  3. package/dist/chunk-3OB3OVYR.js +349 -0
  4. package/dist/chunk-4RIMQOBR.js +58 -0
  5. package/dist/chunk-5MKYDI4X.js +1 -0
  6. package/dist/chunk-5Y43HNRG.js +296 -0
  7. package/dist/chunk-7WXAQHJI.js +350 -0
  8. package/dist/chunk-BADA7OLG.js +564 -0
  9. package/dist/chunk-BEJAJFV6.js +191 -0
  10. package/dist/chunk-BNI7CYRI.js +334 -0
  11. package/dist/{chunk-UPCVSGWH.js → chunk-CAWE42LH.js} +5 -2
  12. package/dist/chunk-DGKMO3AE.js +717 -0
  13. package/dist/chunk-DR7KR7OT.js +103 -0
  14. package/dist/chunk-GSNIZ6DF.js +605 -0
  15. package/dist/chunk-HXQPEZRG.js +105 -0
  16. package/dist/chunk-IC2KUU4V.js +1264 -0
  17. package/dist/chunk-IWSBYOSS.js +91 -0
  18. package/dist/chunk-MFJS65C5.js +368 -0
  19. package/dist/{chunk-CXPNPSF7.js → chunk-NJXIYSDZ.js} +12 -1
  20. package/dist/{chunk-NRQXKZO3.js → chunk-OM4L7RE5.js} +12 -2
  21. package/dist/chunk-QCRRF76W.js +75 -0
  22. package/dist/chunk-QUEJHA24.js +508 -0
  23. package/dist/chunk-UYBQTE4M.js +337 -0
  24. package/dist/chunk-VJ744W5N.js +603 -0
  25. package/dist/chunk-VXW7AOGM.js +285 -0
  26. package/dist/{chunk-OQ7P5XC7.js → chunk-YB7AXTX7.js} +1 -1
  27. package/dist/components/ActionBar/index.d.ts +7 -2
  28. package/dist/components/ActionBar/index.js +1 -1
  29. package/dist/components/AdvanceMenu/index.d.ts +80 -0
  30. package/dist/components/AdvanceMenu/index.js +1 -0
  31. package/dist/components/ArticleSlot/index.d.ts +213 -0
  32. package/dist/components/ArticleSlot/index.js +1 -0
  33. package/dist/components/AuthorInfo/index.js +1 -1
  34. package/dist/components/BottomSheet/index.d.ts +87 -0
  35. package/dist/components/BottomSheet/index.js +1 -0
  36. package/dist/components/CleanModeOverlay/index.d.ts +60 -0
  37. package/dist/components/CleanModeOverlay/index.js +1 -0
  38. package/dist/components/CommentSheet/index.d.ts +155 -3
  39. package/dist/components/CommentSheet/index.js +1 -1
  40. package/dist/components/DetailView/index.d.ts +314 -0
  41. package/dist/components/DetailView/index.js +1 -0
  42. package/dist/components/ErrorBoundary/index.js +1 -1
  43. package/dist/components/{VideoFeed → Feed}/index.d.ts +64 -45
  44. package/dist/components/Feed/index.js +1 -0
  45. package/dist/components/ImageCarousel/index.d.ts +50 -0
  46. package/dist/components/ImageCarousel/index.js +1 -0
  47. package/dist/components/Playlist/index.d.ts +117 -0
  48. package/dist/components/Playlist/index.js +1 -0
  49. package/dist/components/ProgressBar/index.js +1 -1
  50. package/dist/components/QualityPicker/index.d.ts +35 -0
  51. package/dist/components/QualityPicker/index.js +1 -0
  52. package/dist/components/ReportSheet/index.d.ts +74 -0
  53. package/dist/components/ReportSheet/index.js +1 -0
  54. package/dist/components/Skeleton/index.js +1 -1
  55. package/dist/components/SpeedPicker/index.d.ts +32 -0
  56. package/dist/components/SpeedPicker/index.js +1 -0
  57. package/dist/components/VideoInfo/index.d.ts +7 -3
  58. package/dist/components/VideoInfo/index.js +1 -1
  59. package/dist/components/VideoPlayer/index.js +1 -1
  60. package/dist/components/VideoSlot/index.d.ts +31 -68
  61. package/dist/components/VideoSlot/index.js +2 -1
  62. package/dist/components/VirtualSlider/index.js +1 -1
  63. package/dist/index.d.ts +74 -11
  64. package/dist/index.js +39 -15
  65. package/package.json +8 -4
  66. package/dist/CommentSheet.css-BD6XbpU2.d.ts +0 -207
  67. package/dist/chunk-EBAMBI3O.js +0 -571
  68. package/dist/chunk-K3CETRCY.js +0 -737
  69. package/dist/chunk-PGHLVKXS.js +0 -148
  70. package/dist/chunk-QF7O26KZ.js +0 -357
  71. package/dist/chunk-RKS7YA7Z.js +0 -562
  72. package/dist/chunk-T4RQWGRY.js +0 -1519
  73. package/dist/chunk-UXNIXHII.js +0 -2040
  74. package/dist/chunk-YWAPI7JO.js +0 -204
  75. package/dist/components/VideoFeed/index.js +0 -1
@@ -1,2040 +0,0 @@
1
- import { clsx } from 'clsx';
2
- import { createContext, memo, useState, useRef, useEffect, useCallback, useMemo, useContext } from 'react';
3
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
-
5
- // src/components/CommentSheet/CommentSheetHeadless.tsx
6
- var CommentSheetContext = createContext(void 0);
7
- function useCommentSheetContext() {
8
- const context = useContext(CommentSheetContext);
9
- if (!context) {
10
- throw new Error("useCommentSheetContext must be used within <CommentSheetHeadless>");
11
- }
12
- return context;
13
- }
14
- function useOptionalCommentSheetContext() {
15
- return useContext(CommentSheetContext);
16
- }
17
- function ImageIcon({ className }) {
18
- return /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", className, viewBox: "0 0 20 20", fill: "none", children: [
19
- /* @__PURE__ */ jsx(
20
- "rect",
21
- {
22
- x: "2",
23
- y: "4",
24
- width: "16",
25
- height: "12",
26
- rx: "2",
27
- stroke: "#164d8e",
28
- strokeWidth: "1.5",
29
- fill: "none"
30
- }
31
- ),
32
- /* @__PURE__ */ jsx(
33
- "path",
34
- {
35
- d: "M2 13l4-4 3 3 5-5 4 4",
36
- stroke: "#164d8e",
37
- strokeWidth: "1.5",
38
- strokeLinecap: "round",
39
- strokeLinejoin: "round",
40
- fill: "none"
41
- }
42
- ),
43
- /* @__PURE__ */ jsx("circle", { cx: "14", cy: "8", r: "1.5", fill: "#9fc6e9" })
44
- ] });
45
- }
46
- function EmojiIcon({ className }) {
47
- return /* @__PURE__ */ jsxs(
48
- "svg",
49
- {
50
- "aria-hidden": "true",
51
- className,
52
- viewBox: "0 0 24 24",
53
- fill: "none",
54
- stroke: "currentColor",
55
- strokeWidth: "1.5",
56
- children: [
57
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
58
- /* @__PURE__ */ jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2", strokeLinecap: "round" }),
59
- /* @__PURE__ */ jsx("line", { x1: "9", y1: "9", x2: "9.01", y2: "9", strokeWidth: "2", strokeLinecap: "round" }),
60
- /* @__PURE__ */ jsx("line", { x1: "15", y1: "9", x2: "15.01", y2: "9", strokeWidth: "2", strokeLinecap: "round" })
61
- ]
62
- }
63
- );
64
- }
65
- function CloseIcon({ className }) {
66
- return /* @__PURE__ */ jsxs(
67
- "svg",
68
- {
69
- className,
70
- viewBox: "0 0 24 24",
71
- fill: "none",
72
- stroke: "currentColor",
73
- strokeWidth: "2",
74
- "aria-hidden": "true",
75
- focusable: "false",
76
- children: [
77
- /* @__PURE__ */ jsx("title", { children: "Close" }),
78
- /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
79
- /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
80
- ]
81
- }
82
- );
83
- }
84
- var DEFAULT_EMOJIS = ["\u{1F600}", "\u{1F602}", "\u{1F60D}", "\u{1F525}", "\u{1F44D}", "\u{1F4AF}", "\u2764\uFE0F", "\u{1F622}"];
85
- var CommentInput = memo(function CommentInput2({
86
- placeholder = "Add a comment...",
87
- emojis = DEFAULT_EMOJIS,
88
- maxLength = 500,
89
- className,
90
- onSubmitSuccess,
91
- onMediaClick,
92
- replyingToText = "Replying to",
93
- replyPlaceholderTemplate = "Reply to @{name}...",
94
- cancelReplyAriaLabel = "Cancel reply",
95
- addMediaAriaLabel = "Add media",
96
- submitAriaLabel = "Submit comment",
97
- emojiAriaLabel = "Open emoji",
98
- addEmojiAriaLabel = "Add {emoji}"
99
- }) {
100
- const { actions, state, replyTarget, setReplyTarget } = useCommentSheetContext();
101
- const [text, setText] = useState("");
102
- const [showEmojis, setShowEmojis] = useState(false);
103
- const inputRef = useRef(null);
104
- useEffect(() => {
105
- if (replyTarget && inputRef.current) {
106
- inputRef.current.focus();
107
- }
108
- }, [replyTarget]);
109
- const handleTextChange = useCallback(
110
- (e) => {
111
- const value = e.target.value;
112
- if (value.length <= maxLength) {
113
- setText(value);
114
- }
115
- },
116
- [maxLength]
117
- );
118
- const handleSubmit = useCallback(async () => {
119
- const trimmed = text.trim();
120
- if (!trimmed || state.isPosting) return;
121
- try {
122
- if (replyTarget) {
123
- await actions.postReply(replyTarget.commentId, trimmed, {
124
- id: replyTarget.authorId,
125
- name: replyTarget.authorName
126
- });
127
- setReplyTarget(null);
128
- } else {
129
- await actions.postComment(trimmed);
130
- }
131
- setText("");
132
- setShowEmojis(false);
133
- onSubmitSuccess?.();
134
- } catch {
135
- }
136
- }, [text, state.isPosting, replyTarget, actions, setReplyTarget, onSubmitSuccess]);
137
- const handleKeyDown = useCallback(
138
- (e) => {
139
- if (e.key === "Enter") {
140
- e.preventDefault();
141
- handleSubmit();
142
- }
143
- },
144
- [handleSubmit]
145
- );
146
- const handleEmojiClick = useCallback((emoji) => {
147
- setText((prev) => prev + emoji);
148
- inputRef.current?.focus();
149
- }, []);
150
- const handleClearReply = useCallback(() => {
151
- setReplyTarget(null);
152
- inputRef.current?.focus();
153
- }, [setReplyTarget]);
154
- const toggleEmojis = useCallback(() => {
155
- setShowEmojis((prev) => !prev);
156
- }, []);
157
- const canSubmit = text.trim().length > 0 && !state.isPosting;
158
- return /* @__PURE__ */ jsxs("div", { className: clsx("sv-comment-sheet__input-area", className), children: [
159
- replyTarget && /* @__PURE__ */ jsxs("div", { className: "sv-comment-sheet__reply-to", children: [
160
- /* @__PURE__ */ jsxs("span", { children: [
161
- replyingToText,
162
- " \u2022 ",
163
- /* @__PURE__ */ jsx("span", { className: "sv-comment-sheet__reply-to-author-name", children: replyTarget.authorName })
164
- ] }),
165
- /* @__PURE__ */ jsx(
166
- "button",
167
- {
168
- type: "button",
169
- className: "sv-comment-sheet__reply-to-clear",
170
- onClick: handleClearReply,
171
- "aria-label": cancelReplyAriaLabel,
172
- children: /* @__PURE__ */ jsx(CloseIcon, { className: "sv-comment-item__action-icon" })
173
- }
174
- )
175
- ] }),
176
- /* @__PURE__ */ jsx(
177
- "div",
178
- {
179
- className: clsx(
180
- "sv-comment-sheet__emoji-bar",
181
- showEmojis && "sv-comment-sheet__emoji-bar--visible"
182
- ),
183
- children: emojis.map((emoji) => /* @__PURE__ */ jsx(
184
- "button",
185
- {
186
- type: "button",
187
- className: "sv-comment-sheet__emoji-bar-btn",
188
- onClick: () => handleEmojiClick(emoji),
189
- "aria-label": addEmojiAriaLabel.replace("{emoji}", emoji),
190
- children: emoji
191
- },
192
- emoji
193
- ))
194
- }
195
- ),
196
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-sheet__input-wrapper", children: [
197
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-sheet__input-left", children: [
198
- /* @__PURE__ */ jsx(
199
- "button",
200
- {
201
- type: "button",
202
- className: "sv-comment-sheet__media-btn",
203
- onClick: onMediaClick,
204
- "aria-label": addMediaAriaLabel,
205
- children: /* @__PURE__ */ jsx(ImageIcon, {})
206
- }
207
- ),
208
- /* @__PURE__ */ jsx(
209
- "input",
210
- {
211
- ref: inputRef,
212
- type: "text",
213
- className: "sv-comment-sheet__input",
214
- value: text,
215
- onChange: handleTextChange,
216
- onKeyDown: handleKeyDown,
217
- placeholder: replyTarget ? replyPlaceholderTemplate.replace("{name}", replyTarget.authorName) : placeholder,
218
- disabled: state.isPosting,
219
- maxLength
220
- }
221
- )
222
- ] }),
223
- canSubmit ? /* @__PURE__ */ jsx(
224
- "button",
225
- {
226
- type: "button",
227
- className: "sv-comment-sheet__emoji-btn",
228
- onClick: handleSubmit,
229
- "aria-label": submitAriaLabel,
230
- children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
231
- }
232
- ) : /* @__PURE__ */ jsx(
233
- "button",
234
- {
235
- type: "button",
236
- className: "sv-comment-sheet__emoji-btn",
237
- onClick: toggleEmojis,
238
- "aria-label": emojiAriaLabel,
239
- children: /* @__PURE__ */ jsx(EmojiIcon, {})
240
- }
241
- )
242
- ] })
243
- ] });
244
- });
245
- CommentInput.displayName = "CommentInput";
246
- function PinIcon({ className }) {
247
- return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className, viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M9.828.722a.5.5 0 0 1 .354.146l4.95 4.95a.5.5 0 0 1 0 .707c-.48.48-1.072.588-1.503.588-.177 0-.335-.018-.46-.039l-3.134 3.134a5.927 5.927 0 0 1 .16 1.013c.046.702-.032 1.687-.72 2.375a.5.5 0 0 1-.707 0l-2.829-2.828-3.182 3.182c-.195.195-1.219.902-1.414.707-.195-.195.512-1.22.707-1.414l3.182-3.182-2.828-2.829a.5.5 0 0 1 0-.707c.688-.688 1.673-.767 2.375-.72a5.922 5.922 0 0 1 1.013.16l3.134-3.133a2.772 2.772 0 0 1-.04-.461c0-.43.108-1.022.589-1.503a.5.5 0 0 1 .353-.146z" }) });
248
- }
249
- function HeartIcon({ filled, className }) {
250
- if (filled) {
251
- return /* @__PURE__ */ jsx(
252
- "svg",
253
- {
254
- xmlns: "http://www.w3.org/2000/svg",
255
- "aria-label": "Heart icon",
256
- "aria-hidden": "true",
257
- className,
258
- viewBox: "0 0 24 24",
259
- fill: "currentColor",
260
- children: /* @__PURE__ */ jsx("path", { d: "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" })
261
- }
262
- );
263
- }
264
- return /* @__PURE__ */ jsx(
265
- "svg",
266
- {
267
- xmlns: "http://www.w3.org/2000/svg",
268
- "aria-label": "Heart icon",
269
- "aria-hidden": "true",
270
- className,
271
- viewBox: "0 0 24 24",
272
- fill: "none",
273
- stroke: "currentColor",
274
- strokeWidth: "1.5",
275
- children: /* @__PURE__ */ jsx("path", { d: "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" })
276
- }
277
- );
278
- }
279
- function ReplyIcon({ className }) {
280
- return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className, viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx(
281
- "path",
282
- {
283
- fillRule: "evenodd",
284
- d: "M7.793 2.232a.75.75 0 01-.025 1.06L3.622 7.25h10.003a5.375 5.375 0 010 10.75H10.75a.75.75 0 010-1.5h2.875a3.875 3.875 0 000-7.75H3.622l4.146 3.957a.75.75 0 01-1.036 1.085l-5.5-5.25a.75.75 0 010-1.085l5.5-5.25a.75.75 0 011.06.025z",
285
- clipRule: "evenodd"
286
- }
287
- ) });
288
- }
289
- function VerifiedIcon({ className }) {
290
- return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) });
291
- }
292
- function MoreIcon({ className }) {
293
- return /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", className, viewBox: "0 0 24 24", fill: "currentColor", children: [
294
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.5" }),
295
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "6", r: "1.5" }),
296
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "18", r: "1.5" })
297
- ] });
298
- }
299
- function ChevronIcon({ direction, className }) {
300
- return /* @__PURE__ */ jsx(
301
- "svg",
302
- {
303
- "aria-hidden": "true",
304
- className,
305
- viewBox: "0 0 20 20",
306
- fill: "currentColor",
307
- style: { transform: direction === "up" ? "rotate(180deg)" : void 0 },
308
- children: /* @__PURE__ */ jsx(
309
- "path",
310
- {
311
- fillRule: "evenodd",
312
- d: "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z",
313
- clipRule: "evenodd"
314
- }
315
- )
316
- }
317
- );
318
- }
319
- function CommentAvatar({
320
- src,
321
- name,
322
- size = "normal",
323
- className
324
- }) {
325
- const [error, setError] = useState(false);
326
- const sizeClass = size === "small" ? "sv-comment-reply__avatar" : "sv-comment-item__avatar";
327
- const initial = name.charAt(0).toUpperCase();
328
- if (!src || error) {
329
- return /* @__PURE__ */ jsx(
330
- "div",
331
- {
332
- className: clsx(sizeClass, className),
333
- style: {
334
- display: "flex",
335
- alignItems: "center",
336
- justifyContent: "center",
337
- fontSize: size === "small" ? 10 : 14,
338
- fontWeight: 600,
339
- color: "#6b7271"
340
- },
341
- children: initial
342
- }
343
- );
344
- }
345
- return /* @__PURE__ */ jsx(
346
- "img",
347
- {
348
- src,
349
- alt: name,
350
- className: clsx(sizeClass, className),
351
- onError: () => setError(true)
352
- }
353
- );
354
- }
355
- function CommentText({
356
- text,
357
- maxLines,
358
- className,
359
- expandText = "See more",
360
- collapseText = "See less"
361
- }) {
362
- const [expanded, setExpanded] = useState(false);
363
- const needsCollapse = text.length > maxLines * 60 || text.split("\n").length > maxLines;
364
- return /* @__PURE__ */ jsxs("div", { className, children: [
365
- /* @__PURE__ */ jsx(
366
- "p",
367
- {
368
- className: clsx(
369
- "sv-comment-item__text",
370
- !expanded && needsCollapse && "sv-comment-item__text--collapsed"
371
- ),
372
- style: { "--sv-comment-max-lines": maxLines },
373
- children: text
374
- }
375
- ),
376
- needsCollapse && /* @__PURE__ */ jsx(
377
- "button",
378
- {
379
- type: "button",
380
- className: "sv-comment-item__expand-btn",
381
- onClick: () => setExpanded(!expanded),
382
- children: expanded ? collapseText : expandText
383
- }
384
- )
385
- ] });
386
- }
387
- function defaultFormatTimeAgo(dateStr) {
388
- const date = new Date(dateStr);
389
- const now = /* @__PURE__ */ new Date();
390
- const diff = Math.floor((now.getTime() - date.getTime()) / 1e3);
391
- if (diff < 60) return "just now";
392
- if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
393
- if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
394
- if (diff < 604800) return `${Math.floor(diff / 86400)}d ago`;
395
- if (diff < 2592e3) return `${Math.floor(diff / 604800)}w ago`;
396
- return `${Math.floor(diff / 2592e3)}mo ago`;
397
- }
398
- function formatCount(count) {
399
- if (count >= 1e6) {
400
- return `${(count / 1e6).toFixed(1)}M`;
401
- }
402
- if (count >= 1e3) {
403
- return `${(count / 1e3).toFixed(1)}K`;
404
- }
405
- return String(count);
406
- }
407
- var ReplyItemComponent = memo(function ReplyItemComponent2({
408
- reply,
409
- parentComment
410
- }) {
411
- const { actions, setReplyTarget } = useCommentSheetContext();
412
- const handleLike = useCallback(() => {
413
- if (reply.isLiked) {
414
- actions.unlikeComment(reply.id);
415
- } else {
416
- actions.likeComment(reply.id);
417
- }
418
- }, [actions, reply.id, reply.isLiked]);
419
- const handleReply = useCallback(() => {
420
- setReplyTarget({
421
- commentId: parentComment.id,
422
- authorName: reply.author.name,
423
- authorId: reply.author.id
424
- });
425
- }, [setReplyTarget, parentComment.id, reply.author]);
426
- return /* @__PURE__ */ jsxs("div", { className: clsx("sv-comment-reply", reply.isPending && "sv-comment-item--pending"), children: [
427
- /* @__PURE__ */ jsx(CommentAvatar, { src: reply.author.avatar, name: reply.author.name, size: "small" }),
428
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__content", children: [
429
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__header", children: [
430
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__header-left", children: [
431
- /* @__PURE__ */ jsx("span", { className: "sv-comment-item__author", children: reply.author.name }),
432
- reply.author.isVerified && /* @__PURE__ */ jsx(VerifiedIcon, { className: "sv-comment-item__verified" })
433
- ] }),
434
- /* @__PURE__ */ jsx("button", { type: "button", className: "sv-comment-item__menu-btn", "aria-label": "More options", children: /* @__PURE__ */ jsx(MoreIcon, {}) })
435
- ] }),
436
- /* @__PURE__ */ jsxs("p", { className: "sv-comment-item__text", children: [
437
- reply.replyTo && reply.replyTo.id !== parentComment.author.id && /* @__PURE__ */ jsxs("span", { className: "sv-comment-reply__mention", children: [
438
- "@",
439
- reply.replyTo.name,
440
- " "
441
- ] }),
442
- reply.content
443
- ] }),
444
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__actions", children: [
445
- /* @__PURE__ */ jsxs(
446
- "button",
447
- {
448
- type: "button",
449
- className: clsx(
450
- "sv-comment-item__action-btn",
451
- reply.isLiked && "sv-comment-item__action-btn--active"
452
- ),
453
- onClick: handleLike,
454
- children: [
455
- /* @__PURE__ */ jsx(HeartIcon, { filled: reply.isLiked, className: "sv-comment-item__action-icon" }),
456
- reply.likeCount > 0 && /* @__PURE__ */ jsx("span", { children: formatCount(reply.likeCount) })
457
- ]
458
- }
459
- ),
460
- /* @__PURE__ */ jsx("button", { type: "button", className: "sv-comment-item__action-btn", onClick: handleReply, children: /* @__PURE__ */ jsx(ReplyIcon, { className: "sv-comment-item__action-icon" }) })
461
- ] })
462
- ] })
463
- ] });
464
- });
465
- function RepliesList({ comment }) {
466
- const { actions, config, i18n } = useCommentSheetContext();
467
- const [expanded, setExpanded] = useState(false);
468
- const { replyCount, replies = [], repliesLoaded } = comment;
469
- const shouldAutoExpand = replyCount <= config.repliesAutoExpandThreshold;
470
- const showReplies = shouldAutoExpand || expanded;
471
- const visibleReplies = showReplies ? replies : [];
472
- const hasMoreReplies = !repliesLoaded && replyCount > replies.length;
473
- const remainingReplies = replyCount - visibleReplies.length;
474
- const hideRepliesText = i18n.hideRepliesText ?? "Hide replies";
475
- const viewRepliesText = (i18n.viewRepliesText ?? "View {count} more replies").replace(
476
- "{count}",
477
- String(remainingReplies)
478
- );
479
- const viewMoreRepliesText = i18n.viewMoreRepliesText ?? "View more replies";
480
- const loadRepliesText = i18n.loadRepliesText ?? "Load replies";
481
- const handleToggle = useCallback(() => {
482
- if (!expanded && replies.length === 0) {
483
- actions.loadReplies(comment.id);
484
- }
485
- setExpanded(!expanded);
486
- }, [expanded, replies.length, actions, comment.id]);
487
- const handleLoadMore = useCallback(() => {
488
- actions.loadReplies(comment.id);
489
- }, [actions, comment.id]);
490
- if (replyCount === 0) return null;
491
- return /* @__PURE__ */ jsxs("div", { className: "sv-comment-replies", children: [
492
- showReplies && visibleReplies.length > 0 && /* @__PURE__ */ jsx("div", { className: "sv-comment-replies__list", children: visibleReplies.map((reply) => /* @__PURE__ */ jsx(ReplyItemComponent, { reply, parentComment: comment }, reply.id)) }),
493
- !shouldAutoExpand && /* @__PURE__ */ jsxs("button", { type: "button", className: "sv-comment-replies__toggle", onClick: handleToggle, children: [
494
- /* @__PURE__ */ jsx("span", { className: "sv-comment-replies__toggle-line" }),
495
- /* @__PURE__ */ jsx("span", { className: "sv-comment-replies__toggle-text", children: expanded ? hideRepliesText : viewRepliesText }),
496
- /* @__PURE__ */ jsx(
497
- ChevronIcon,
498
- {
499
- direction: expanded ? "up" : "down",
500
- className: "sv-comment-replies__toggle-icon"
501
- }
502
- )
503
- ] }),
504
- expanded && hasMoreReplies && /* @__PURE__ */ jsxs("button", { type: "button", className: "sv-comment-replies__toggle", onClick: handleLoadMore, children: [
505
- /* @__PURE__ */ jsx("span", { className: "sv-comment-replies__toggle-line" }),
506
- /* @__PURE__ */ jsx("span", { className: "sv-comment-replies__toggle-text", children: viewMoreRepliesText }),
507
- /* @__PURE__ */ jsx(ChevronIcon, { direction: "down", className: "sv-comment-replies__toggle-icon" })
508
- ] }),
509
- shouldAutoExpand && replies.length === 0 && replyCount > 0 && /* @__PURE__ */ jsxs(
510
- "button",
511
- {
512
- type: "button",
513
- className: "sv-comment-replies__toggle",
514
- onClick: () => actions.loadReplies(comment.id),
515
- children: [
516
- /* @__PURE__ */ jsx("span", { className: "sv-comment-replies__toggle-line" }),
517
- /* @__PURE__ */ jsx("span", { className: "sv-comment-replies__toggle-text", children: loadRepliesText }),
518
- /* @__PURE__ */ jsx(ChevronIcon, { direction: "down", className: "sv-comment-replies__toggle-icon" })
519
- ]
520
- }
521
- )
522
- ] });
523
- }
524
- var CommentItemComponent = memo(function CommentItemComponent2({
525
- comment,
526
- className
527
- }) {
528
- const { actions, config, setReplyTarget, i18n } = useCommentSheetContext();
529
- const expandText = i18n.expandText ?? "See more";
530
- const collapseText = i18n.collapseText ?? "See less";
531
- const formatTimeAgo = i18n.formatRelativeTime ?? defaultFormatTimeAgo;
532
- const handleLike = useCallback(() => {
533
- if (comment.isLiked) {
534
- actions.unlikeComment(comment.id);
535
- } else {
536
- actions.likeComment(comment.id);
537
- }
538
- }, [actions, comment.id, comment.isLiked]);
539
- const handleReply = useCallback(() => {
540
- setReplyTarget({
541
- commentId: comment.id,
542
- authorName: comment.author.name,
543
- authorId: comment.author.id
544
- });
545
- }, [setReplyTarget, comment.id, comment.author]);
546
- const hasReplies = comment.replyCount > 0;
547
- return /* @__PURE__ */ jsxs(
548
- "div",
549
- {
550
- className: clsx(
551
- "sv-comment-item",
552
- comment.isPending && "sv-comment-item--pending",
553
- comment.isPinned && "sv-comment-item--pinned",
554
- hasReplies && "sv-comment-item--has-replies",
555
- className
556
- ),
557
- children: [
558
- /* @__PURE__ */ jsx(CommentAvatar, { src: comment.author.avatar, name: comment.author.name }),
559
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__content", children: [
560
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__header", children: [
561
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__header-left", children: [
562
- comment.isPinned && /* @__PURE__ */ jsx(PinIcon, { className: "sv-comment-item__pin-icon" }),
563
- /* @__PURE__ */ jsx("span", { className: "sv-comment-item__author", children: comment.author.name }),
564
- comment.author.isVerified && /* @__PURE__ */ jsx(VerifiedIcon, { className: "sv-comment-item__verified" }),
565
- comment.author.badge && /* @__PURE__ */ jsx("span", { className: "sv-comment-item__badge", children: comment.author.badge })
566
- ] }),
567
- /* @__PURE__ */ jsx("button", { type: "button", className: "sv-comment-item__menu-btn", "aria-label": "More options", children: /* @__PURE__ */ jsx(MoreIcon, {}) })
568
- ] }),
569
- /* @__PURE__ */ jsx(
570
- CommentText,
571
- {
572
- text: comment.content,
573
- maxLines: config.textMaxLines,
574
- expandText,
575
- collapseText
576
- }
577
- ),
578
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-item__actions", children: [
579
- /* @__PURE__ */ jsx("span", { className: "sv-comment-item__time", children: formatTimeAgo(comment.createdAt) }),
580
- /* @__PURE__ */ jsxs(
581
- "button",
582
- {
583
- type: "button",
584
- className: clsx(
585
- "sv-comment-item__action-btn",
586
- comment.isLiked && "sv-comment-item__action-btn--active"
587
- ),
588
- onClick: handleLike,
589
- children: [
590
- /* @__PURE__ */ jsx(HeartIcon, { filled: comment.isLiked, className: "sv-comment-item__action-icon" }),
591
- comment.likeCount > 0 && /* @__PURE__ */ jsx("span", { children: formatCount(comment.likeCount) })
592
- ]
593
- }
594
- ),
595
- /* @__PURE__ */ jsxs("button", { type: "button", className: "sv-comment-item__action-btn", onClick: handleReply, children: [
596
- /* @__PURE__ */ jsx(ReplyIcon, { className: "sv-comment-item__action-icon" }),
597
- comment.replyCount > 0 && /* @__PURE__ */ jsx("span", { children: formatCount(comment.replyCount) })
598
- ] })
599
- ] }),
600
- /* @__PURE__ */ jsx(RepliesList, { comment })
601
- ] })
602
- ]
603
- }
604
- );
605
- });
606
- CommentItemComponent.displayName = "CommentItem";
607
- var CommentItemSkeleton = memo(function CommentItemSkeleton2() {
608
- return /* @__PURE__ */ jsxs("div", { className: "sv-comment-item sv-comment-skeleton", children: [
609
- /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton__avatar" }),
610
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-skeleton__content", children: [
611
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-skeleton__header", children: [
612
- /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton__author" }),
613
- /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton__time" })
614
- ] }),
615
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-skeleton__text", children: [
616
- /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton__line sv-comment-skeleton__line--full" }),
617
- /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton__line sv-comment-skeleton__line--medium" })
618
- ] }),
619
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-skeleton__actions", children: [
620
- /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton__action" }),
621
- /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton__action" })
622
- ] })
623
- ] })
624
- ] });
625
- });
626
- CommentItemSkeleton.displayName = "CommentItemSkeleton";
627
- var CommentListSkeleton = memo(function CommentListSkeleton2({
628
- count = 5
629
- }) {
630
- return /* @__PURE__ */ jsx("div", { className: "sv-comment-skeleton-list", children: Array.from({ length: count }).map((_, index) => /* @__PURE__ */ jsx(
631
- CommentItemSkeleton,
632
- {},
633
- `skeleton-${// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
634
- index}`
635
- )) });
636
- });
637
- CommentListSkeleton.displayName = "CommentListSkeleton";
638
- var CommentList = memo(function CommentList2({
639
- emptyState,
640
- loadingState,
641
- errorState,
642
- className,
643
- errorText = "Failed to load comments",
644
- retryText = "Try again",
645
- emptyText = "No comments yet. Be the first!",
646
- loadMoreText = "Load more comments"
647
- }) {
648
- const { state, actions, videoId } = useCommentSheetContext();
649
- const listRef = useRef(null);
650
- const loadMoreRef = useRef(null);
651
- const { comments, isLoading, error, hasMore, isLoadingMore } = state;
652
- useEffect(() => {
653
- if (!hasMore || isLoadingMore) return;
654
- const observer = new IntersectionObserver(
655
- (entries) => {
656
- if (entries[0]?.isIntersecting) {
657
- actions.loadMore();
658
- }
659
- },
660
- { threshold: 0.1 }
661
- );
662
- const target = loadMoreRef.current;
663
- if (target) {
664
- observer.observe(target);
665
- }
666
- return () => {
667
- if (target) {
668
- observer.unobserve(target);
669
- }
670
- };
671
- }, [hasMore, isLoadingMore, actions]);
672
- const handleRetry = useCallback(() => {
673
- if (videoId) {
674
- actions.loadComments(videoId);
675
- }
676
- }, [actions, videoId]);
677
- if (isLoading && comments.length === 0) {
678
- return /* @__PURE__ */ jsx("div", { className: clsx("sv-comment-sheet__list", className), children: loadingState ?? /* @__PURE__ */ jsx(CommentListSkeleton, { count: 5 }) });
679
- }
680
- if (error && comments.length === 0) {
681
- return /* @__PURE__ */ jsx("div", { className: clsx("sv-comment-sheet__list", className), children: errorState?.(error, handleRetry) ?? /* @__PURE__ */ jsxs("div", { className: "sv-comment-sheet__error", children: [
682
- /* @__PURE__ */ jsx("span", { children: errorText }),
683
- /* @__PURE__ */ jsx("button", { type: "button", className: "sv-comment-sheet__error-btn", onClick: handleRetry, children: retryText })
684
- ] }) });
685
- }
686
- if (comments.length === 0) {
687
- return /* @__PURE__ */ jsx("div", { className: clsx("sv-comment-sheet__list", className), children: emptyState ?? /* @__PURE__ */ jsxs("div", { className: "sv-comment-sheet__empty", children: [
688
- /* @__PURE__ */ jsx("span", { className: "sv-comment-sheet__empty-icon", children: "\u{1F4AC}" }),
689
- /* @__PURE__ */ jsx("span", { className: "sv-comment-sheet__empty-text", children: emptyText })
690
- ] }) });
691
- }
692
- return /* @__PURE__ */ jsxs("div", { ref: listRef, className: clsx("sv-comment-sheet__list", className), children: [
693
- comments.map((comment) => /* @__PURE__ */ jsx(CommentItemComponent, { comment }, comment.id)),
694
- hasMore && /* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "sv-comment-sheet__load-more", children: isLoadingMore ? /* @__PURE__ */ jsx(CommentItemSkeleton, {}) : /* @__PURE__ */ jsx(
695
- "button",
696
- {
697
- type: "button",
698
- className: "sv-comment-sheet__load-more-btn",
699
- onClick: () => actions.loadMore(),
700
- children: loadMoreText
701
- }
702
- ) })
703
- ] });
704
- });
705
- CommentList.displayName = "CommentList";
706
-
707
- // src/components/CommentSheet/CommentSheet.css.ts
708
- var COMMENT_SHEET_CSS = `
709
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
710
- CSS VARIABLES (Light Theme)
711
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
712
-
713
- :root {
714
- /* Primary colors */
715
- --sv-comment-primary: #164d8e;
716
- --sv-comment-primary-light: #1354ae;
717
- --sv-comment-primary-dark: #003477;
718
-
719
- /* Contrast colors */
720
- --sv-comment-bg: #ffffff;
721
- --sv-comment-text: #121212;
722
- --sv-comment-text-secondary: #6b7271;
723
- --sv-comment-border: rgba(18, 18, 18, 0.08);
724
-
725
- /* Accent colors */
726
- --sv-comment-like-color: #ff434e;
727
- --sv-comment-verified-color: #1ea031;
728
- --sv-comment-mention-color: #164d8e;
729
-
730
- /* Component specific */
731
- --sv-comment-avatar-bg: #e5e6e6;
732
- --sv-comment-input-border: #a3a3a3;
733
- --sv-comment-reply-line: #e5e6e6;
734
- }
735
-
736
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
737
- BACKDROP & CONTAINER
738
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
739
-
740
- .sv-comment-sheet-backdrop {
741
- position: fixed;
742
- inset: 0;
743
- background: var(--sv-comment-backdrop-bg, rgba(0, 0, 0, 0.5));
744
- z-index: var(--sv-comment-sheet-z-index, 1000);
745
- opacity: 0;
746
- visibility: hidden;
747
- pointer-events: none;
748
- transition: opacity var(--sv-comment-animation-duration, 400ms) ease,
749
- visibility 0s linear var(--sv-comment-animation-duration, 400ms);
750
- }
751
-
752
- .sv-comment-sheet-backdrop--open {
753
- opacity: 1;
754
- visibility: visible;
755
- pointer-events: auto;
756
- transition: opacity var(--sv-comment-animation-duration, 400ms) ease,
757
- visibility 0s linear 0s;
758
- }
759
-
760
- /* Keyframes for slide animations */
761
- @keyframes sv-sheet-slide-up {
762
- from { transform: translateY(100%); }
763
- to { transform: translateY(0); }
764
- }
765
-
766
- @keyframes sv-sheet-slide-down {
767
- from { transform: translateY(0); }
768
- to { transform: translateY(100%); }
769
- }
770
-
771
- .sv-comment-sheet {
772
- position: fixed;
773
- margin: 16px;
774
- left: 0;
775
- right: 0;
776
- bottom: 0;
777
- background: var(--sv-comment-bg, #ffffff);
778
- border-radius: var(--sv-comment-sheet-radius, 24px);
779
- z-index: var(--sv-comment-sheet-z-index, 1001);
780
- display: flex;
781
- flex-direction: column;
782
- max-height: var(--sv-comment-sheet-height, 55vh);
783
- transform: translateY(100%);
784
- overflow: hidden;
785
- touch-action: pan-y;
786
- isolation: isolate;
787
- pointer-events: none;
788
- will-change: transform;
789
- }
790
-
791
- .sv-comment-sheet--open {
792
- transform: translateY(0);
793
- pointer-events: auto;
794
- animation: sv-sheet-slide-up var(--sv-comment-animation-duration, 400ms)
795
- var(--sv-comment-animation-easing, cubic-bezier(0.32, 0.72, 0, 1)) forwards;
796
- }
797
-
798
- .sv-comment-sheet--open.sv-comment-sheet--closing {
799
- pointer-events: none;
800
- animation: sv-sheet-slide-down var(--sv-comment-animation-duration, 400ms)
801
- var(--sv-comment-animation-easing, cubic-bezier(0.32, 0.72, 0, 1)) forwards !important;
802
- }
803
-
804
- /* CRITICAL: Disable VirtualSlider/VideoFeed swipe when CommentSheet is open */
805
- body.sv-comment-sheet-open .sv-slider,
806
- body.sv-comment-sheet-open .sv-video-feed {
807
- pointer-events: none !important;
808
- touch-action: none !important;
809
- }
810
-
811
- body.sv-comment-sheet-open .sv-comment-sheet,
812
- body.sv-comment-sheet-open .sv-comment-sheet-backdrop {
813
- touch-action: auto !important;
814
- }
815
-
816
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
817
- HEADER
818
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
819
-
820
- .sv-comment-sheet__header {
821
- display: flex;
822
- align-items: center;
823
- justify-content: space-between;
824
- padding: 16px;
825
- border-bottom: 1px solid var(--sv-comment-border);
826
- flex-shrink: 0;
827
- position: relative;
828
- cursor: grab;
829
- touch-action: none;
830
- }
831
-
832
- .sv-comment-sheet__header:active {
833
- cursor: grabbing;
834
- }
835
-
836
- .sv-comment-sheet__header-handle {
837
- position: absolute;
838
- top: 8px;
839
- left: 50%;
840
- transform: translateX(-50%);
841
- width: 36px;
842
- height: 4px;
843
- background: var(--sv-comment-text-secondary);
844
- border-radius: 2px;
845
- opacity: 0.3;
846
- }
847
-
848
- .sv-comment-sheet__header-left {
849
- display: flex;
850
- align-items: center;
851
- gap: 2px;
852
- }
853
-
854
- .sv-comment-sheet__title {
855
- font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
856
- font-size: 18px;
857
- font-weight: 600;
858
- line-height: 1.4;
859
- color: var(--sv-comment-text);
860
- opacity: 0.9;
861
- margin: 0;
862
- }
863
-
864
- .sv-comment-sheet__sort-btn {
865
- display: flex;
866
- align-items: center;
867
- justify-content: center;
868
- background: none;
869
- border: none;
870
- padding: 0;
871
- cursor: pointer;
872
- color: var(--sv-comment-text);
873
- }
874
-
875
- .sv-comment-sheet__sort-btn svg {
876
- width: 20px;
877
- height: 20px;
878
- }
879
-
880
- .sv-comment-sheet__close-btn {
881
- display: flex;
882
- align-items: center;
883
- justify-content: center;
884
- width: 36px;
885
- height: 36px;
886
- border: none;
887
- border-radius: 100px;
888
- background: linear-gradient(180deg, var(--sv-comment-primary-light) 0%, var(--sv-comment-primary-dark) 100%);
889
- cursor: pointer;
890
- transition: opacity 0.15s ease;
891
- }
892
-
893
- .sv-comment-sheet__close-btn:hover {
894
- opacity: 0.9;
895
- }
896
-
897
- .sv-comment-sheet__close-btn svg {
898
- width: 20px;
899
- height: 20px;
900
- color: #ffffff;
901
- }
902
-
903
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
904
- COMMENT LIST
905
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
906
-
907
- .sv-comment-sheet__list {
908
- flex: 1;
909
- overflow-y: auto;
910
- overflow-x: hidden;
911
- padding: 8px 16px 28px 16px;
912
- -webkit-overflow-scrolling: touch;
913
- overscroll-behavior: contain;
914
- touch-action: pan-y;
915
- }
916
-
917
- .sv-comment-sheet__list::-webkit-scrollbar {
918
- width: 0px;
919
- }
920
-
921
- .sv-comment-sheet__list::-webkit-scrollbar-thumb {
922
- background: rgba(0, 0, 0, 0.0);
923
- border-radius: 0px;
924
- }
925
-
926
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
927
- COMMENT ITEM
928
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
929
-
930
- .sv-comment-item {
931
- display: flex;
932
- gap: 6px;
933
- padding: 16px 0;
934
- position: relative;
935
- }
936
-
937
- .sv-comment-item--pending {
938
- opacity: 0.6;
939
- }
940
-
941
- .sv-comment-item--pinned {
942
- background: var(--sv-comment-bg);
943
- }
944
-
945
- .sv-comment-item__avatar {
946
- width: 40px;
947
- height: 40px;
948
- border-radius: 120px;
949
- flex-shrink: 0;
950
- object-fit: cover;
951
- background: var(--sv-comment-avatar-bg);
952
- }
953
-
954
- .sv-comment-item__content {
955
- flex: 1;
956
- min-width: 0;
957
- display: flex;
958
- flex-direction: column;
959
- gap: 6px;
960
- }
961
-
962
- .sv-comment-item__header {
963
- display: flex;
964
- align-items: center;
965
- justify-content: space-between;
966
- gap: 4px;
967
- }
968
-
969
- .sv-comment-item__header-left {
970
- display: flex;
971
- align-items: center;
972
- gap: 4px;
973
- min-width: 0;
974
- }
975
-
976
- .sv-comment-item__pin-icon {
977
- width: 16px;
978
- height: 16px;
979
- flex-shrink: 0;
980
- color: var(--sv-comment-primary);
981
- }
982
-
983
- .sv-comment-item__author {
984
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
985
- font-size: 14px;
986
- font-weight: 600;
987
- line-height: 1.4;
988
- color: var(--sv-comment-text);
989
- overflow: hidden;
990
- text-overflow: ellipsis;
991
- white-space: nowrap;
992
- }
993
-
994
- .sv-comment-item__verified {
995
- width: 12px;
996
- height: 12px;
997
- flex-shrink: 0;
998
- color: var(--sv-comment-verified-color);
999
- }
1000
-
1001
- .sv-comment-item__badge {
1002
- font-size: 10px;
1003
- padding: 2px 6px;
1004
- background: var(--sv-comment-primary);
1005
- color: #ffffff;
1006
- border-radius: 4px;
1007
- flex-shrink: 0;
1008
- }
1009
-
1010
- .sv-comment-item__menu-btn {
1011
- display: flex;
1012
- align-items: center;
1013
- justify-content: center;
1014
- background: none;
1015
- border: none;
1016
- padding: 0;
1017
- cursor: pointer;
1018
- color: var(--sv-comment-text-secondary);
1019
- }
1020
-
1021
- .sv-comment-item__menu-btn svg {
1022
- width: 20px;
1023
- height: 20px;
1024
- }
1025
-
1026
- .sv-comment-item__text {
1027
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
1028
- font-size: 12px;
1029
- font-weight: 400;
1030
- line-height: 1.4;
1031
- color: var(--sv-comment-text);
1032
- word-break: break-word;
1033
- margin: 0;
1034
- padding-right: 16px;
1035
- }
1036
-
1037
- /* Text collapse */
1038
- .sv-comment-item__text--collapsed {
1039
- display: -webkit-box;
1040
- -webkit-line-clamp: var(--sv-comment-max-lines, 3);
1041
- -webkit-box-orient: vertical;
1042
- overflow: hidden;
1043
- }
1044
-
1045
- .sv-comment-item__expand-btn {
1046
- background: none;
1047
- border: none;
1048
- padding: 0;
1049
- font-size: 12px;
1050
- color: var(--sv-comment-text-secondary);
1051
- cursor: pointer;
1052
- margin-top: 4px;
1053
- }
1054
-
1055
- .sv-comment-item__expand-btn:hover {
1056
- color: var(--sv-comment-text);
1057
- }
1058
-
1059
- /* Actions row */
1060
- .sv-comment-item__actions {
1061
- display: flex;
1062
- align-items: center;
1063
- gap: 16px;
1064
- }
1065
-
1066
- .sv-comment-item__time {
1067
- font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
1068
- font-size: 12px;
1069
- font-weight: 500;
1070
- line-height: 1.2;
1071
- color: var(--sv-comment-text-secondary);
1072
- overflow: hidden;
1073
- text-overflow: ellipsis;
1074
- white-space: nowrap;
1075
- }
1076
-
1077
- .sv-comment-item__action-btn {
1078
- display: flex;
1079
- align-items: center;
1080
- gap: 4px;
1081
- background: none;
1082
- border: none;
1083
- padding: 0;
1084
- font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
1085
- font-size: 12px;
1086
- font-weight: 500;
1087
- line-height: 1.2;
1088
- color: var(--sv-comment-text-secondary);
1089
- cursor: pointer;
1090
- transition: color 0.15s ease;
1091
- }
1092
-
1093
- .sv-comment-item__action-btn:hover {
1094
- color: var(--sv-comment-text);
1095
- }
1096
-
1097
- .sv-comment-item__action-btn--active {
1098
- color: var(--sv-comment-like-color);
1099
- }
1100
-
1101
- .sv-comment-item__action-btn--active:hover {
1102
- color: var(--sv-comment-like-color);
1103
- }
1104
-
1105
- .sv-comment-item__action-icon {
1106
- width: 20px;
1107
- height: 20px;
1108
- }
1109
-
1110
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1111
- REPLIES
1112
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
1113
-
1114
- .sv-comment-replies {
1115
- margin-top: 12px;
1116
- position: relative;
1117
- }
1118
-
1119
- /* Vertical connecting line */
1120
- .sv-comment-item--has-replies::before {
1121
- content: '';
1122
- position: absolute;
1123
- left: 20px;
1124
- top: 56px;
1125
- bottom: 0;
1126
- width: 1px;
1127
- background: var(--sv-comment-reply-line);
1128
- }
1129
-
1130
- .sv-comment-replies__toggle {
1131
- display: flex;
1132
- align-items: center;
1133
- gap: 0;
1134
- background: none;
1135
- border: none;
1136
- padding: 4px 0;
1137
- cursor: pointer;
1138
- }
1139
-
1140
- .sv-comment-replies__toggle-line {
1141
- width: 24px;
1142
- height: 1px;
1143
- background: #a3a3a3;
1144
- margin-right: 0;
1145
- }
1146
-
1147
- .sv-comment-replies__toggle-text {
1148
- font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
1149
- font-size: 13px;
1150
- font-weight: 600;
1151
- line-height: 1.4;
1152
- color: var(--sv-comment-text-secondary);
1153
- white-space: nowrap;
1154
- }
1155
-
1156
- .sv-comment-replies__toggle-icon {
1157
- width: 20px;
1158
- height: 20px;
1159
- color: var(--sv-comment-text-secondary);
1160
- }
1161
-
1162
- .sv-comment-replies__list {
1163
- margin-top: 12px;
1164
- }
1165
-
1166
- .sv-comment-reply {
1167
- display: flex;
1168
- gap: 6px;
1169
- padding: 0 0 12px 0;
1170
- position: relative;
1171
- }
1172
-
1173
- .sv-comment-reply__avatar {
1174
- width: 24px;
1175
- height: 24px;
1176
- border-radius: 100px;
1177
- flex-shrink: 0;
1178
- object-fit: cover;
1179
- background: var(--sv-comment-avatar-bg);
1180
- }
1181
-
1182
- .sv-comment-reply__mention {
1183
- color: var(--sv-comment-mention-color);
1184
- font-weight: 500;
1185
- }
1186
-
1187
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1188
- INPUT AREA
1189
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
1190
-
1191
- .sv-comment-sheet__input-area {
1192
- padding: 0 16px 36px 16px;
1193
- flex-shrink: 0;
1194
- }
1195
-
1196
- /* Guest mode - login prompt instead of input */
1197
- .sv-comment-input-guest {
1198
- padding: 16px;
1199
- border-top: 1px solid var(--sv-comment-border);
1200
- display: flex;
1201
- align-items: center;
1202
- justify-content: center;
1203
- cursor: pointer;
1204
- background: var(--sv-comment-bg);
1205
- color: var(--sv-comment-primary);
1206
- font-weight: 600;
1207
- font-size: 14px;
1208
- width: 100%;
1209
- transition: background 0.15s ease;
1210
- }
1211
-
1212
- .sv-comment-input-guest:hover {
1213
- background: rgba(22, 77, 142, 0.05);
1214
- }
1215
-
1216
- .sv-comment-input-guest:active {
1217
- background: rgba(22, 77, 142, 0.1);
1218
- }
1219
-
1220
- .sv-comment-sheet__reply-to {
1221
- display: flex;
1222
- align-items: center;
1223
- justify-content: space-between;
1224
- padding: 4px 0;
1225
- margin-bottom: 8px;
1226
- font-size: 12px;
1227
- color: var(--sv-comment-text-secondary);
1228
- }
1229
-
1230
- .sv-comment-sheet__reply-to-author-name {
1231
- font-size: 12px;
1232
- color: var(--sv-comment-text);
1233
- font-weight: 500;
1234
- }
1235
-
1236
- .sv-comment-sheet__reply-to-clear {
1237
- background: none;
1238
- border: none;
1239
- padding: 4px;
1240
- color: var(--sv-comment-text-secondary);
1241
- cursor: pointer;
1242
- font-size: 12px;
1243
- }
1244
-
1245
- .sv-comment-sheet__input-wrapper {
1246
- display: flex;
1247
- align-items: center;
1248
- gap: 8px;
1249
- height: 44px;
1250
- padding: 16px;
1251
- background: var(--sv-comment-bg);
1252
- border: 1px solid var(--sv-comment-input-border);
1253
- border-radius: 12px;
1254
- backdrop-filter: blur(50px);
1255
- }
1256
-
1257
- .sv-comment-sheet__input-left {
1258
- display: flex;
1259
- align-items: center;
1260
- gap: 8px;
1261
- flex: 1;
1262
- min-width: 0;
1263
- }
1264
-
1265
- .sv-comment-sheet__media-btn {
1266
- display: flex;
1267
- align-items: center;
1268
- justify-content: center;
1269
- background: none;
1270
- border: none;
1271
- padding: 0;
1272
- cursor: pointer;
1273
- flex-shrink: 0;
1274
- }
1275
-
1276
- .sv-comment-sheet__media-btn svg {
1277
- width: 20px;
1278
- height: 20px;
1279
- }
1280
-
1281
- .sv-comment-sheet__input {
1282
- flex: 1;
1283
- background: none;
1284
- border: none;
1285
- outline: none;
1286
- resize: none;
1287
- font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
1288
- font-size: 12px;
1289
- font-style: italic;
1290
- font-weight: 400;
1291
- line-height: 1.4;
1292
- color: var(--sv-comment-text);
1293
- padding: 0;
1294
- min-width: 0;
1295
- }
1296
-
1297
- .sv-comment-sheet__input::placeholder {
1298
- color: var(--sv-comment-text-secondary);
1299
- }
1300
-
1301
- .sv-comment-sheet__input:not(:placeholder-shown) {
1302
- font-style: normal;
1303
- }
1304
-
1305
- .sv-comment-sheet__emoji-btn {
1306
- display: flex;
1307
- align-items: center;
1308
- justify-content: center;
1309
- background: none;
1310
- border: none;
1311
- padding: 0;
1312
- cursor: pointer;
1313
- flex-shrink: 0;
1314
- }
1315
-
1316
- .sv-comment-sheet__emoji-btn svg {
1317
- width: 20px;
1318
- height: 20px;
1319
- color: var(--sv-comment-primary);
1320
- }
1321
-
1322
- /* Hidden emoji bar (can be toggled) */
1323
- .sv-comment-sheet__emoji-bar {
1324
- display: none;
1325
- gap: 8px;
1326
- padding: 8px 0;
1327
- overflow-x: auto;
1328
- -webkit-overflow-scrolling: touch;
1329
- }
1330
-
1331
- .sv-comment-sheet__emoji-bar--visible {
1332
- display: flex;
1333
- }
1334
-
1335
- .sv-comment-sheet__emoji-bar::-webkit-scrollbar {
1336
- display: none;
1337
- }
1338
-
1339
- .sv-comment-sheet__emoji-bar-btn {
1340
- background: none;
1341
- border: none;
1342
- padding: 4px;
1343
- font-size: 20px;
1344
- cursor: pointer;
1345
- border-radius: 4px;
1346
- transition: background 0.15s ease;
1347
- }
1348
-
1349
- .sv-comment-sheet__emoji-bar-btn:hover {
1350
- background: rgba(0, 0, 0, 0.05);
1351
- }
1352
-
1353
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1354
- STATES
1355
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
1356
-
1357
- .sv-comment-sheet__loading {
1358
- display: flex;
1359
- flex-direction: column;
1360
- align-items: center;
1361
- justify-content: center;
1362
- padding: 32px;
1363
- color: var(--sv-comment-text-secondary);
1364
- }
1365
-
1366
- .sv-comment-sheet__loading-spinner {
1367
- width: 24px;
1368
- height: 24px;
1369
- border: 2px solid rgba(0, 0, 0, 0.1);
1370
- border-top-color: var(--sv-comment-primary);
1371
- border-radius: 50%;
1372
- animation: sv-comment-spin 0.8s linear infinite;
1373
- }
1374
-
1375
- @keyframes sv-comment-spin {
1376
- to { transform: rotate(360deg); }
1377
- }
1378
-
1379
- .sv-comment-sheet__empty {
1380
- display: flex;
1381
- flex-direction: column;
1382
- align-items: center;
1383
- justify-content: center;
1384
- padding: 32px;
1385
- text-align: center;
1386
- }
1387
-
1388
- .sv-comment-sheet__empty-icon {
1389
- font-size: 48px;
1390
- margin-bottom: 12px;
1391
- }
1392
-
1393
- .sv-comment-sheet__empty-text {
1394
- color: var(--sv-comment-text-secondary);
1395
- font-size: 14px;
1396
- }
1397
-
1398
- .sv-comment-sheet__error {
1399
- display: flex;
1400
- flex-direction: column;
1401
- align-items: center;
1402
- padding: 32px;
1403
- text-align: center;
1404
- color: var(--sv-comment-like-color);
1405
- }
1406
-
1407
- .sv-comment-sheet__error-btn {
1408
- margin-top: 12px;
1409
- padding: 8px 16px;
1410
- background: rgba(0, 0, 0, 0.05);
1411
- border: none;
1412
- border-radius: 8px;
1413
- color: var(--sv-comment-text);
1414
- cursor: pointer;
1415
- }
1416
-
1417
- /* Load more */
1418
- .sv-comment-sheet__load-more {
1419
- display: flex;
1420
- justify-content: center;
1421
- padding: 12px;
1422
- }
1423
-
1424
- .sv-comment-sheet__load-more-btn {
1425
- background: none;
1426
- border: none;
1427
- padding: 8px 16px;
1428
- color: var(--sv-comment-text-secondary);
1429
- cursor: pointer;
1430
- font-size: 13px;
1431
- }
1432
-
1433
- .sv-comment-sheet__load-more-btn:hover {
1434
- color: var(--sv-comment-text);
1435
- }
1436
-
1437
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1438
- DELETE CONFIRMATION
1439
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
1440
-
1441
- .sv-comment-delete-confirm {
1442
- position: fixed;
1443
- inset: 0;
1444
- display: flex;
1445
- align-items: center;
1446
- justify-content: center;
1447
- background: rgba(0, 0, 0, 0.5);
1448
- z-index: var(--sv-comment-modal-z-index, 1100);
1449
- }
1450
-
1451
- .sv-comment-delete-confirm__dialog {
1452
- background: var(--sv-comment-bg);
1453
- border-radius: 12px;
1454
- padding: 16px;
1455
- max-width: 280px;
1456
- text-align: center;
1457
- }
1458
-
1459
- .sv-comment-delete-confirm__title {
1460
- font-size: 16px;
1461
- font-weight: 600;
1462
- color: var(--sv-comment-text);
1463
- margin-bottom: 8px;
1464
- }
1465
-
1466
- .sv-comment-delete-confirm__text {
1467
- font-size: 14px;
1468
- color: var(--sv-comment-text-secondary);
1469
- margin-bottom: 16px;
1470
- }
1471
-
1472
- .sv-comment-delete-confirm__actions {
1473
- display: flex;
1474
- gap: 8px;
1475
- }
1476
-
1477
- .sv-comment-delete-confirm__btn {
1478
- flex: 1;
1479
- padding: 8px 12px;
1480
- border: none;
1481
- border-radius: 8px;
1482
- font-size: 14px;
1483
- font-weight: 600;
1484
- cursor: pointer;
1485
- }
1486
-
1487
- .sv-comment-delete-confirm__btn--cancel {
1488
- background: rgba(0, 0, 0, 0.05);
1489
- color: var(--sv-comment-text);
1490
- }
1491
-
1492
- .sv-comment-delete-confirm__btn--delete {
1493
- background: var(--sv-comment-like-color);
1494
- color: #ffffff;
1495
- }
1496
-
1497
- /* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1498
- SKELETON LOADING
1499
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
1500
-
1501
- @keyframes sv-skeleton-shimmer {
1502
- 0% { background-position: -200% 0; }
1503
- 100% { background-position: 200% 0; }
1504
- }
1505
-
1506
- .sv-comment-skeleton-list {
1507
- display: flex;
1508
- flex-direction: column;
1509
- }
1510
-
1511
- .sv-comment-skeleton {
1512
- animation: sv-skeleton-fade-in 0.3s ease;
1513
- }
1514
-
1515
- @keyframes sv-skeleton-fade-in {
1516
- from { opacity: 0; }
1517
- to { opacity: 1; }
1518
- }
1519
-
1520
- .sv-comment-skeleton__avatar {
1521
- width: 40px;
1522
- height: 40px;
1523
- border-radius: 50%;
1524
- background: linear-gradient(
1525
- 90deg,
1526
- rgba(0, 0, 0, 0.04) 25%,
1527
- rgba(0, 0, 0, 0.08) 50%,
1528
- rgba(0, 0, 0, 0.04) 75%
1529
- );
1530
- background-size: 200% 100%;
1531
- animation: sv-skeleton-shimmer 1.5s ease-in-out infinite;
1532
- }
1533
-
1534
- .sv-comment-skeleton__content {
1535
- flex: 1;
1536
- min-width: 0;
1537
- display: flex;
1538
- flex-direction: column;
1539
- gap: 8px;
1540
- }
1541
-
1542
- .sv-comment-skeleton__header {
1543
- display: flex;
1544
- align-items: center;
1545
- gap: 12px;
1546
- }
1547
-
1548
- .sv-comment-skeleton__author {
1549
- width: 80px;
1550
- height: 14px;
1551
- border-radius: 4px;
1552
- background: linear-gradient(
1553
- 90deg,
1554
- rgba(0, 0, 0, 0.04) 25%,
1555
- rgba(0, 0, 0, 0.08) 50%,
1556
- rgba(0, 0, 0, 0.04) 75%
1557
- );
1558
- background-size: 200% 100%;
1559
- animation: sv-skeleton-shimmer 1.5s ease-in-out infinite;
1560
- animation-delay: 0.1s;
1561
- }
1562
-
1563
- .sv-comment-skeleton__time {
1564
- width: 40px;
1565
- height: 12px;
1566
- border-radius: 4px;
1567
- background: linear-gradient(
1568
- 90deg,
1569
- rgba(0, 0, 0, 0.04) 25%,
1570
- rgba(0, 0, 0, 0.08) 50%,
1571
- rgba(0, 0, 0, 0.04) 75%
1572
- );
1573
- background-size: 200% 100%;
1574
- animation: sv-skeleton-shimmer 1.5s ease-in-out infinite;
1575
- animation-delay: 0.15s;
1576
- }
1577
-
1578
- .sv-comment-skeleton__text {
1579
- display: flex;
1580
- flex-direction: column;
1581
- gap: 4px;
1582
- }
1583
-
1584
- .sv-comment-skeleton__line {
1585
- height: 14px;
1586
- border-radius: 4px;
1587
- background: linear-gradient(
1588
- 90deg,
1589
- rgba(0, 0, 0, 0.04) 25%,
1590
- rgba(0, 0, 0, 0.08) 50%,
1591
- rgba(0, 0, 0, 0.04) 75%
1592
- );
1593
- background-size: 200% 100%;
1594
- animation: sv-skeleton-shimmer 1.5s ease-in-out infinite;
1595
- }
1596
-
1597
- .sv-comment-skeleton__line--full {
1598
- width: 100%;
1599
- animation-delay: 0.2s;
1600
- }
1601
-
1602
- .sv-comment-skeleton__line--medium {
1603
- width: 65%;
1604
- animation-delay: 0.25s;
1605
- }
1606
-
1607
- .sv-comment-skeleton__actions {
1608
- display: flex;
1609
- align-items: center;
1610
- gap: 16px;
1611
- margin-top: 4px;
1612
- }
1613
-
1614
- .sv-comment-skeleton__action {
1615
- width: 50px;
1616
- height: 12px;
1617
- border-radius: 4px;
1618
- background: linear-gradient(
1619
- 90deg,
1620
- rgba(0, 0, 0, 0.04) 25%,
1621
- rgba(0, 0, 0, 0.08) 50%,
1622
- rgba(0, 0, 0, 0.04) 75%
1623
- );
1624
- background-size: 200% 100%;
1625
- animation: sv-skeleton-shimmer 1.5s ease-in-out infinite;
1626
- animation-delay: 0.3s;
1627
- }
1628
- `;
1629
- function AngleDownIcon({ className }) {
1630
- return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className, viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx(
1631
- "path",
1632
- {
1633
- fillRule: "evenodd",
1634
- d: "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z",
1635
- clipRule: "evenodd"
1636
- }
1637
- ) });
1638
- }
1639
- function CloseIcon2({ className, style }) {
1640
- return /* @__PURE__ */ jsxs(
1641
- "svg",
1642
- {
1643
- "aria-hidden": "true",
1644
- className,
1645
- style,
1646
- viewBox: "0 0 24 24",
1647
- fill: "none",
1648
- stroke: "currentColor",
1649
- strokeWidth: "2",
1650
- strokeLinecap: "round",
1651
- strokeLinejoin: "round",
1652
- children: [
1653
- /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1654
- /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1655
- ]
1656
- }
1657
- );
1658
- }
1659
- function formatCount2(count) {
1660
- if (count >= 1e6) {
1661
- return `${(count / 1e6).toFixed(1)}M`;
1662
- }
1663
- if (count >= 1e3) {
1664
- return `${(count / 1e3).toFixed(1)}K`;
1665
- }
1666
- return String(count);
1667
- }
1668
- var SheetHeader = memo(function SheetHeader2({
1669
- titleLabel = "Comments",
1670
- showHandle = false,
1671
- showSort = true,
1672
- onSortClick,
1673
- closeIcon,
1674
- className,
1675
- sortAriaLabel = "Sort comments",
1676
- closeAriaLabel = "Close comments"
1677
- }) {
1678
- const { state, onClose, initialCount } = useCommentSheetContext();
1679
- const totalCount = state.totalCount > 0 ? state.totalCount : initialCount ?? 0;
1680
- return /* @__PURE__ */ jsxs("div", { className: clsx("sv-comment-sheet__header", className), children: [
1681
- showHandle && /* @__PURE__ */ jsx("div", { className: "sv-comment-sheet__header-handle" }),
1682
- /* @__PURE__ */ jsxs("div", { className: "sv-comment-sheet__header-left", children: [
1683
- /* @__PURE__ */ jsxs("h2", { className: "sv-comment-sheet__title", children: [
1684
- formatCount2(totalCount),
1685
- " ",
1686
- titleLabel
1687
- ] }),
1688
- showSort && /* @__PURE__ */ jsx(
1689
- "button",
1690
- {
1691
- type: "button",
1692
- className: "sv-comment-sheet__sort-btn",
1693
- onClick: onSortClick,
1694
- "aria-label": sortAriaLabel,
1695
- children: /* @__PURE__ */ jsx(AngleDownIcon, {})
1696
- }
1697
- )
1698
- ] }),
1699
- /* @__PURE__ */ jsx(
1700
- "button",
1701
- {
1702
- type: "button",
1703
- className: "sv-comment-sheet__close-btn",
1704
- onClick: onClose,
1705
- "aria-label": closeAriaLabel,
1706
- children: closeIcon ?? /* @__PURE__ */ jsx(CloseIcon2, {})
1707
- }
1708
- )
1709
- ] });
1710
- });
1711
- SheetHeader.displayName = "SheetHeader";
1712
- var cssInjected = false;
1713
- function injectCSS() {
1714
- if (cssInjected || typeof document === "undefined") return;
1715
- const styleId = "sv-comment-sheet-styles";
1716
- if (!document.getElementById(styleId)) {
1717
- const style = document.createElement("style");
1718
- style.id = styleId;
1719
- style.textContent = COMMENT_SHEET_CSS;
1720
- document.head.appendChild(style);
1721
- }
1722
- cssInjected = true;
1723
- }
1724
- var DEFAULT_CONFIG = {
1725
- pageSize: 20,
1726
- replyPageSize: 10,
1727
- cacheTTL: 5 * 60 * 1e3,
1728
- // 5 minutes
1729
- repliesAutoExpandThreshold: 1,
1730
- repliesCollapseLimit: 3,
1731
- textMaxLines: 3,
1732
- inputMaxLength: 500,
1733
- enableOptimisticUI: true,
1734
- requireDeleteConfirm: true
1735
- };
1736
- var ANIMATION_DURATION = 400;
1737
- var DEFAULT_I18N = {
1738
- expandText: "See more",
1739
- collapseText: "See less",
1740
- viewRepliesText: "View {count} more replies",
1741
- hideRepliesText: "Hide replies",
1742
- loadRepliesText: "Load replies",
1743
- viewMoreRepliesText: "View more replies"
1744
- };
1745
- var CommentSheetHeadless = memo(function CommentSheetHeadless2({
1746
- isOpen,
1747
- onClose,
1748
- videoId,
1749
- state,
1750
- actions,
1751
- config: configOverride,
1752
- initialCount,
1753
- children,
1754
- className,
1755
- backdropClassName,
1756
- closeOnBackdropClick = true,
1757
- closeOnEscape = true,
1758
- enableDragToDismiss = true,
1759
- dragCloseThreshold = 150,
1760
- i18n: i18nOverride
1761
- }) {
1762
- useEffect(() => {
1763
- injectCSS();
1764
- }, []);
1765
- const sheetRef = useRef(null);
1766
- const dragStartY = useRef(0);
1767
- const currentTranslateY = useRef(0);
1768
- const isDragging = useRef(false);
1769
- const [isClosing, setIsClosing] = useState(false);
1770
- const isDragClosingRef = useRef(false);
1771
- const closeTimeoutRef = useRef(null);
1772
- const openedAtRef = useRef(0);
1773
- const config = useMemo(() => ({ ...DEFAULT_CONFIG, ...configOverride }), [configOverride]);
1774
- const i18n = useMemo(() => ({ ...DEFAULT_I18N, ...i18nOverride }), [i18nOverride]);
1775
- const handleClose = useCallback(() => {
1776
- if (isClosing) return;
1777
- const timeSinceOpen = Date.now() - openedAtRef.current;
1778
- if (timeSinceOpen < 100) return;
1779
- setIsClosing(true);
1780
- closeTimeoutRef.current = setTimeout(() => {
1781
- setIsClosing(false);
1782
- onClose();
1783
- }, ANIMATION_DURATION);
1784
- }, [isClosing, onClose]);
1785
- useEffect(() => {
1786
- return () => {
1787
- if (closeTimeoutRef.current) {
1788
- clearTimeout(closeTimeoutRef.current);
1789
- }
1790
- };
1791
- }, []);
1792
- useEffect(() => {
1793
- if (isOpen) {
1794
- openedAtRef.current = Date.now();
1795
- setIsClosing(false);
1796
- if (closeTimeoutRef.current) {
1797
- clearTimeout(closeTimeoutRef.current);
1798
- closeTimeoutRef.current = null;
1799
- }
1800
- }
1801
- }, [isOpen]);
1802
- const [replyTarget, setReplyTarget] = useState(null);
1803
- useEffect(() => {
1804
- if (!isOpen) {
1805
- setReplyTarget(null);
1806
- }
1807
- }, [isOpen]);
1808
- useEffect(() => {
1809
- if (!closeOnEscape || !isOpen) return;
1810
- const handleEscapeKey = (e) => {
1811
- if (e.key === "Escape") {
1812
- handleClose();
1813
- }
1814
- };
1815
- document.addEventListener("keydown", handleEscapeKey);
1816
- return () => document.removeEventListener("keydown", handleEscapeKey);
1817
- }, [closeOnEscape, isOpen, handleClose]);
1818
- const handleBackdropClick = useCallback(
1819
- (e) => {
1820
- e.stopPropagation();
1821
- if (closeOnBackdropClick && e.target === e.currentTarget) {
1822
- handleClose();
1823
- }
1824
- },
1825
- [closeOnBackdropClick, handleClose]
1826
- );
1827
- useEffect(() => {
1828
- if (!isOpen) return;
1829
- const originalOverflow = document.body.style.overflow;
1830
- document.body.style.overflow = "hidden";
1831
- document.body.classList.add("sv-comment-sheet-open");
1832
- const blockTouchMove = (e) => {
1833
- const target = e.target;
1834
- const isInsideSheet = target.closest(".sv-comment-sheet");
1835
- if (!isInsideSheet) {
1836
- e.preventDefault();
1837
- e.stopPropagation();
1838
- }
1839
- };
1840
- document.addEventListener("touchmove", blockTouchMove, { passive: false, capture: true });
1841
- return () => {
1842
- document.body.style.overflow = originalOverflow;
1843
- document.body.classList.remove("sv-comment-sheet-open");
1844
- document.removeEventListener("touchmove", blockTouchMove, { capture: true });
1845
- };
1846
- }, [isOpen]);
1847
- const dragSourceRef = useRef(null);
1848
- const isListAtTop = useCallback(() => {
1849
- const list = sheetRef.current?.querySelector(".sv-comment-sheet__list");
1850
- if (!list) return true;
1851
- return list.scrollTop <= 0;
1852
- }, []);
1853
- useEffect(() => {
1854
- if (!isOpen || !sheetRef.current) return;
1855
- sheetRef.current.style.transform = "";
1856
- sheetRef.current.style.transition = "";
1857
- sheetRef.current.style.animation = "";
1858
- currentTranslateY.current = 0;
1859
- isDragging.current = false;
1860
- dragSourceRef.current = null;
1861
- isDragClosingRef.current = false;
1862
- }, [isOpen]);
1863
- const getDragSource = useCallback(
1864
- (target) => {
1865
- const isInput = target.closest(".sv-comment-sheet__input-container");
1866
- if (isInput) return null;
1867
- const isHeader = target.closest(".sv-comment-sheet__header");
1868
- if (isHeader) return "header";
1869
- const isList = target.closest(".sv-comment-sheet__list");
1870
- if (isList && isListAtTop()) return "overscroll";
1871
- return null;
1872
- },
1873
- [isListAtTop]
1874
- );
1875
- const animateClose = useCallback(
1876
- (sheet) => {
1877
- isDragClosingRef.current = true;
1878
- setIsClosing(true);
1879
- sheet.style.transition = `transform ${ANIMATION_DURATION}ms cubic-bezier(0.32, 0.72, 0, 1)`;
1880
- sheet.style.transform = "translateY(100%)";
1881
- closeTimeoutRef.current = setTimeout(() => {
1882
- sheet.style.transition = "";
1883
- sheet.style.transform = "";
1884
- sheet.style.animation = "";
1885
- isDragClosingRef.current = false;
1886
- setIsClosing(false);
1887
- onClose();
1888
- }, ANIMATION_DURATION);
1889
- },
1890
- [onClose]
1891
- );
1892
- const animateSnapBack = useCallback((sheet) => {
1893
- sheet.style.transition = `transform ${ANIMATION_DURATION}ms cubic-bezier(0.32, 0.72, 0, 1)`;
1894
- sheet.style.transform = "translateY(0)";
1895
- setTimeout(() => {
1896
- sheet.style.transition = "";
1897
- sheet.style.transform = "";
1898
- }, ANIMATION_DURATION);
1899
- }, []);
1900
- useEffect(() => {
1901
- const sheet = sheetRef.current;
1902
- if (!sheet || !isOpen || !enableDragToDismiss) return;
1903
- const onTouchStart = (e) => {
1904
- e.stopPropagation();
1905
- const source = getDragSource(e.target);
1906
- if (!source) return;
1907
- dragSourceRef.current = source;
1908
- isDragging.current = true;
1909
- dragStartY.current = e.touches[0]?.clientY ?? 0;
1910
- currentTranslateY.current = 0;
1911
- sheet.style.animation = "none";
1912
- sheet.style.transition = "none";
1913
- void sheet.offsetHeight;
1914
- };
1915
- const onTouchMove = (e) => {
1916
- e.stopPropagation();
1917
- const clientY = e.touches[0]?.clientY ?? 0;
1918
- const deltaY = clientY - dragStartY.current;
1919
- if (dragSourceRef.current === "overscroll" && (deltaY <= 0 || !isListAtTop())) {
1920
- isDragging.current = false;
1921
- return;
1922
- }
1923
- if (!isDragging.current) return;
1924
- currentTranslateY.current = Math.max(0, deltaY);
1925
- if (currentTranslateY.current > 0) {
1926
- e.preventDefault();
1927
- }
1928
- sheet.style.transform = `translateY(${currentTranslateY.current}px)`;
1929
- };
1930
- const onTouchEnd = (e) => {
1931
- e.stopPropagation();
1932
- dragSourceRef.current = null;
1933
- if (!isDragging.current) return;
1934
- isDragging.current = false;
1935
- const dragDistance = currentTranslateY.current;
1936
- currentTranslateY.current = 0;
1937
- if (dragDistance > dragCloseThreshold) {
1938
- animateClose(sheet);
1939
- } else if (dragDistance > 0) {
1940
- animateSnapBack(sheet);
1941
- }
1942
- };
1943
- sheet.addEventListener("touchstart", onTouchStart, { passive: false });
1944
- sheet.addEventListener("touchmove", onTouchMove, { passive: false });
1945
- sheet.addEventListener("touchend", onTouchEnd, { passive: false });
1946
- return () => {
1947
- sheet.removeEventListener("touchstart", onTouchStart);
1948
- sheet.removeEventListener("touchmove", onTouchMove);
1949
- sheet.removeEventListener("touchend", onTouchEnd);
1950
- };
1951
- }, [
1952
- isOpen,
1953
- enableDragToDismiss,
1954
- dragCloseThreshold,
1955
- isListAtTop,
1956
- getDragSource,
1957
- animateClose,
1958
- animateSnapBack
1959
- ]);
1960
- const contextValue = useMemo(
1961
- () => ({
1962
- state,
1963
- actions,
1964
- videoId,
1965
- config,
1966
- isOpen,
1967
- onClose: handleClose,
1968
- // Use animated close handler
1969
- replyTarget,
1970
- setReplyTarget,
1971
- initialCount,
1972
- i18n
1973
- }),
1974
- [state, actions, videoId, config, isOpen, handleClose, replyTarget, initialCount, i18n]
1975
- );
1976
- const defaultChildren = /* @__PURE__ */ jsxs(Fragment, { children: [
1977
- /* @__PURE__ */ jsx(SheetHeader, {}),
1978
- /* @__PURE__ */ jsx(CommentList, {}),
1979
- /* @__PURE__ */ jsx(CommentInput, {})
1980
- ] });
1981
- const handleKeyDown = useCallback(
1982
- (e) => {
1983
- if (e.key === "Escape") {
1984
- handleClose();
1985
- }
1986
- },
1987
- [handleClose]
1988
- );
1989
- return /* @__PURE__ */ jsxs(CommentSheetContext.Provider, { value: contextValue, children: [
1990
- /* @__PURE__ */ jsx(
1991
- "div",
1992
- {
1993
- className: clsx(
1994
- "sv-comment-sheet-backdrop",
1995
- isOpen && !isClosing && "sv-comment-sheet-backdrop--open",
1996
- backdropClassName
1997
- ),
1998
- onClick: handleBackdropClick,
1999
- onKeyDown: handleKeyDown,
2000
- onPointerDown: (e) => e.stopPropagation(),
2001
- onPointerUp: (e) => e.stopPropagation(),
2002
- onTouchStart: (e) => e.stopPropagation(),
2003
- onTouchMove: (e) => e.stopPropagation(),
2004
- onTouchEnd: (e) => e.stopPropagation(),
2005
- role: "presentation"
2006
- }
2007
- ),
2008
- /* @__PURE__ */ jsx(
2009
- "div",
2010
- {
2011
- ref: sheetRef,
2012
- className: clsx(
2013
- "sv-comment-sheet",
2014
- isOpen && "sv-comment-sheet--open",
2015
- // Keep --open while closing for proper animation
2016
- isClosing && !isDragClosingRef.current && "sv-comment-sheet--closing",
2017
- // Skip CSS animation for drag close
2018
- className
2019
- ),
2020
- "aria-label": "Comments",
2021
- "aria-hidden": !isOpen,
2022
- onKeyDown: handleKeyDown,
2023
- tabIndex: isOpen ? 0 : -1,
2024
- onClick: (e) => e.stopPropagation(),
2025
- onPointerDown: (e) => e.stopPropagation(),
2026
- onPointerUp: (e) => e.stopPropagation(),
2027
- children: children ?? defaultChildren
2028
- }
2029
- )
2030
- ] });
2031
- });
2032
- CommentSheetHeadless.displayName = "CommentSheetHeadless";
2033
- var CommentSheet = Object.assign(CommentSheetHeadless, {
2034
- Header: SheetHeader,
2035
- List: CommentList,
2036
- Input: CommentInput,
2037
- Item: CommentItemComponent
2038
- });
2039
-
2040
- export { COMMENT_SHEET_CSS, CommentInput, CommentItemComponent, CommentItemSkeleton, CommentList, CommentListSkeleton, CommentSheet, CommentSheetContext, CommentSheetHeadless, SheetHeader, useCommentSheetContext, useOptionalCommentSheetContext };