@openrails/contentkit-ui 0.104.0-rc.2 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/client/contents.d.ts +2 -0
  2. package/dist/client/contents.js +68 -58
  3. package/dist/client/contents.js.map +1 -1
  4. package/dist/client/generated/error-codes.d.ts +1 -1
  5. package/dist/client/generated/error-codes.js +4 -0
  6. package/dist/client/generated/error-codes.js.map +1 -1
  7. package/dist/client/generated/routes.d.ts +2 -2
  8. package/dist/client/generated/routes.js +7 -2
  9. package/dist/client/generated/routes.js.map +1 -1
  10. package/dist/components/admin/reader-content-editor.js +1 -1
  11. package/dist/components/admin/reader-content-editor.js.map +1 -1
  12. package/dist/components/social/poll.js +57 -48
  13. package/dist/components/social/poll.js.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/locales/de.js +1 -0
  16. package/dist/locales/de.js.map +1 -1
  17. package/dist/locales/en.d.ts +1 -0
  18. package/dist/locales/en.js +1 -0
  19. package/dist/locales/en.js.map +1 -1
  20. package/dist/locales/es.js +1 -0
  21. package/dist/locales/es.js.map +1 -1
  22. package/dist/locales/ja.js +1 -0
  23. package/dist/locales/ja.js.map +1 -1
  24. package/dist/locales/ko.js +1 -0
  25. package/dist/locales/ko.js.map +1 -1
  26. package/dist/locales/zh.js +1 -0
  27. package/dist/locales/zh.js.map +1 -1
  28. package/dist/react/contents.js +1 -1
  29. package/dist/react/contents.js.map +1 -1
  30. package/dist/react/use-engagement.js +32 -31
  31. package/dist/react/use-engagement.js.map +1 -1
  32. package/dist/react/versions.d.ts +3 -1
  33. package/dist/react/versions.js +62 -60
  34. package/dist/react/versions.js.map +1 -1
  35. package/dist/styles.css +1 -1
  36. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"zh.js","names":[],"sources":["../../src/locales/zh.ts"],"sourcesContent":["import type { ContentKitUiMessageBundle } from \"../i18n/messages.js\";\nimport { admin } from \"./admin/zh.js\";\nimport { social } from \"./social/zh.js\";\nimport { groups } from \"./groups/zh.js\";\nimport { discovery } from \"./discovery/zh.js\";\nimport { content } from \"./content/zh.js\";\n\nexport const zh: ContentKitUiMessageBundle = {\n ...social,\n ...admin,\n ...groups,\n ...discovery,\n ...content,\n common: {\n cancel: \"取消\",\n close: \"关闭\",\n save: \"保存\",\n saving: \"正在保存…\",\n retry: \"重试\",\n change: \"更换\",\n upload: \"上传\",\n editCrop: \"编辑裁剪\",\n remove: \"移除\",\n rename: \"重命名\",\n replace: \"替换\",\n },\n crop: {\n title: \"裁剪图片\",\n avatarTitle: \"裁剪头像\",\n coverTitle: \"裁剪封面\",\n description: \"拖动调整位置。滚动、双指缩放或使用滑块来缩放。\",\n area: \"裁剪区域。使用方向键移动图片,按 + 或 − 缩放,按 R 旋转。\",\n zoom: \"缩放\",\n zoomIn: \"放大\",\n zoomOut: \"缩小\",\n rotateLeft: \"向左旋转\",\n rotateRight: \"向右旋转\",\n reset: \"重置\",\n undersized: \"此裁剪宽 {width} 像素;在高分辨率屏幕上需要 {target} 像素或以上才清晰。\",\n tooSmall: \"此图片最宽 {width} 像素;至少需要 {min} 像素。请使用更大的图片。\",\n shape: \"比例\",\n original: \"原始比例\",\n preview: \"效果预览\",\n size: \"{width} × {height} 像素\",\n },\n sortable: {\n handle: \"调整 {name} 的顺序\",\n instructions: \"要调整顺序,请按空格键或回车键,用方向键移动,再按空格键或回车键放下。按 Esc 取消。\",\n instructionsTile: \"要重新排序,请按空格键,用方向键移动,再按空格键放下。回车键选择,Esc 键取消。\",\n pickedUp: \"已拿起 {name},位于第 {position} 位,共 {total} 项。\",\n over: \"{name} 将移到第 {position} 位,共 {total} 项。\",\n dropped: \"{name} 已放在第 {position} 位,共 {total} 项。\",\n droppedAway: \"{name} 已放下。\",\n canceled: \"已取消移动 {name}。\",\n },\n folder: {\n label: \"媒体\",\n drop: \"将文件拖放到此处,或点击选择\",\n add: \"添加文件\",\n commit: \"添加 {count} 个\",\n commitAgain: \"重新添加\",\n adding: \"正在添加…\",\n queued: \"等待中\",\n uploaded: \"已上传\",\n notAdded: \"已上传,尚未添加\",\n processing: \"处理中…\",\n processingPercent: \"处理中 {percent}%\",\n edited: \"已编辑\",\n failed: \"未处理\",\n failedVideo: \"无法播放\",\n crop: \"裁剪和旋转\",\n select: \"选择 {name}\",\n selected: \"已选择 {count} 个\",\n removeSelected: \"移除所选\",\n clearSelection: \"取消选择\",\n confirmRemove: \"要移除 {count} 个文件吗?此操作无法撤销。\",\n renameLabel: \"{name} 的新名称\",\n paused: \"上传已暂停\",\n resume: \"继续\",\n dismiss: \"关闭\",\n limitSize: \"{what}最大 {size}\",\n limitMax: \"最多 {max} 个\",\n limitAspect: \"视频比例为 {min} 至 {max}\",\n kinds: {\n image: \"图片\",\n video: \"视频\",\n audio: \"音频\",\n subtitle: \"字幕\",\n file: \"文件\",\n },\n },\n readiness: {\n processing: \"正在处理媒体\",\n processingHint: \"所有文件处理完成后将自动发布。\",\n failedOne: \"无法处理 {name}\",\n failedMany: \"无法处理 {count} 个文件:{names}\",\n failedHint: \"移除这些文件后将自动发布。\",\n },\n avatar: {\n label: \"头像\",\n empty: \"暂无头像\",\n hint: \"正方形图片,至少 {size} 像素。\",\n },\n cover: {\n label: \"封面图片\",\n empty: \"暂无封面\",\n hint: \"横向图片,至少 {width} × {height} 像素。\",\n drop: \"将图片拖放到此处\",\n },\n poster: {\n title: \"设置封面\",\n description: \"从视频中选取一帧,或上传一张图片。封面与视频比例相同。\",\n frame: \"视频画面\",\n upload: \"上传图片\",\n time: \"时间\",\n previousFrame: \"上一帧\",\n nextFrame: \"下一帧\",\n jumpTo: \"跳到 {time}\",\n useFrame: \"使用此帧\",\n crop: \"裁剪…\",\n cropTitle: \"裁剪封面\",\n auto: \"自动\",\n autoHint: \"在你选择之前,将使用自动选取的画面。\",\n drop: \"将图片拖到这里,或点击选择\",\n processing: \"视频仍在处理中。编码完成后即可选择封面。\",\n rendering: \"正在生成封面…\",\n },\n progress: {\n decoding: \"正在打开图片…\",\n hashing: \"正在准备…\",\n uploading: \"正在上传… {percent}%\",\n completing: \"正在完成上传…\",\n rendering: \"正在生成各尺寸…\",\n },\n encode: {\n processing: \"正在处理视频\",\n queued: \"等待处理\",\n queuedPosition: \"排队中 · 第 {position} 位\",\n stalled: \"处理已暂停,正在等待服务器…\",\n segments: \"片段 {done} / {total}\",\n higherQualities: \"更高画质\",\n remainingSeconds: \"剩余约 {seconds} 秒\",\n remainingMinutes: \"剩余约 {minutes} 分钟\",\n remainingHours: \"剩余约 {hours} 小时 {minutes} 分钟\",\n almostDone: \"即将完成\",\n estimating: \"正在估算…\",\n phase: {\n queued: \"排队中\",\n downloading: \"准备中\",\n probing: \"分析中\",\n encoding: \"编码中\",\n muxing: \"封装中\",\n uploading: \"保存中\",\n publishing: \"发布中\",\n images: \"正在生成封面\",\n },\n },\n gallery: {\n label: \"媒体\",\n view: \"布局\",\n carousel: \"轮播\",\n grid: \"网格\",\n counter: \"{current} / {total}\",\n slide: \"第 {current} 项,共 {total} 项\",\n previous: \"上一个\",\n next: \"下一个\",\n goTo: \"转到第 {index} 项\",\n open: \"打开第 {index} 项,共 {total} 项\",\n lightbox: \"媒体查看器\",\n locked: \"其余内容已锁定\",\n processingImage: \"正在处理图片…\",\n image: \"图片 {index}\",\n video: \"视频 {index}\",\n audio: \"音频 {index}\",\n processingAudio: \"正在处理音频…\",\n download: \"下载\",\n failedAudio: \"无法处理此音频。\",\n failedImage: \"无法处理此图片。\",\n },\n player: {\n play: \"播放视频\",\n loading: \"正在加载视频…\",\n retry: \"重试\",\n code: \"代码 {code}\",\n quality: \"画质\",\n auto: \"自动\",\n autoCurrent: \"自动({quality})\",\n label: \"视频播放器\",\n resume: \"播放\",\n pause: \"暂停\",\n replay: \"重新播放\",\n seek: \"跳转\",\n time: \"{current} / {duration}\",\n seekBack: \"后退 {seconds} 秒\",\n seekForward: \"前进 {seconds} 秒\",\n seconds: \"{seconds} 秒\",\n mute: \"静音\",\n unmute: \"取消静音\",\n volume: \"音量\",\n settings: \"设置\",\n speed: \"播放速度\",\n normal: \"正常\",\n audio: \"音轨\",\n subtitles: \"字幕\",\n subtitlesOff: \"关闭\",\n forced: \"{label}(强制)\",\n track: \"轨道 {number}\",\n fullscreen: \"全屏\",\n exitFullscreen: \"退出全屏\",\n pip: \"画中画\",\n exitPip: \"退出画中画\",\n theater: \"影院模式\",\n miniPlayer: \"迷你播放器\",\n expand: \"展开\",\n close: \"关闭\",\n failed: \"无法处理此视频。\",\n errors: {\n network: \"无法连接到视频服务器。\",\n access: \"你已无权访问此视频。\",\n not_found: \"此视频不可用。\",\n rate_limited: \"请求过多,请稍后重试。\",\n unsupported: \"此浏览器无法播放此视频。\",\n stalled: \"播放已停滞。请检查网络连接后重试。\",\n unknown: \"目前无法播放此视频。\",\n },\n },\n versionDownload: {\n unavailable: \"目前无法下载。\",\n availableIn_one: \"{count} 天后可下载\",\n availableIn_other: \"{count} 天后可下载\",\n label: \"下载\",\n preparing: \"正在准备下载…\",\n locked: \"解锁后即可下载。\",\n limited: \"已达到下载上限,请在 {minutes} 分钟后重试。\",\n failed: \"无法开始下载,请重试。\",\n },\n errors: {\n ambiguous_entity: \"有多个条目与此名称匹配。请选择所需的条目。\",\n unknown_entity: \"未找到此条目。请选择现有条目或允许创建新条目。\",\n operation_conflict: \"此提交已包含其他内容。请创建新的提交。\",\n operation_busy: \"此提交仍在更新。请稍后重试。\",\n source_conflict: \"此来源已提交。\",\n content_changed: \"内容在提交后发生了更改。请在发布前检查。\",\n invalid_archive: \"无法处理此ZIP。请检查其中是否包含支持的文件。\",\n download_unavailable: \"目前无法下载。\",\n no_download: \"暂时没有可下载的内容。\",\n generic: \"我们这边出了问题,请重试。\",\n network: \"无法连接到服务器。请检查网络连接后重试。\",\n server_unavailable: \"服务器暂时没有响应,请稍后重试。\",\n storage: \"上传中断,请重试。\",\n decode: \"无法将此文件作为图片打开。\",\n invalid_request: \"无法保存图片。\",\n unauthorized: \"会话已过期,请重新登录后上传。\",\n forbidden: \"你没有权限更改此图片。\",\n not_found: \"此项目已不存在。\",\n conflict: \"已在其他地方被更改,请刷新后重试。\",\n incomplete: \"上传未完成,请重试。\",\n not_uploaded: \"上传已过期,请重新选择图片。\",\n too_large: \"文件过大。\",\n quota_exceeded: \"已达到存储空间上限。\",\n type_not_allowed: \"此处不支持该文件类型。\",\n checksum_mismatch: \"上传过程中文件发生了变化,请重试。\",\n rate_limited: \"上传次数过多,请在 {seconds} 秒后重试。\",\n unavailable: \"媒体存储暂时不可用,请稍后重试。\",\n upgrade_required: \"此项目正在升级,请稍后重试。\",\n internal_error: \"我们这边出了问题,请重试。\",\n allowedTypes: \"请使用 {allowed}。\",\n tooLargeBy: \"此文件为 {size};上限为 {max}。\",\n image_too_small: \"裁剪后图片宽 {width} 像素;至少需要 {min} 像素。请选择更大的区域或使用更大的图片。\",\n image_too_large: \"此图片为 {width}×{height} 像素;图片最多 {megapixels} 百万像素。\",\n image_unreadable: \"此文件不是有效的 {format} 图片。\",\n render_timeout: \"服务器仍在处理,完成后会显示;请稍后再查看。\",\n animation_not_allowed: \"此处不允许动图。请上传静态图片。\",\n animation_unsupported: \"不支持动态 {format} 图片。请使用动态 WebP 或 GIF。\",\n animationFrames: \"动图最多 {maxFrames} 帧;此图有 {frames} 帧。\",\n animationSeconds: \"动图最长 {maxSeconds} 秒。\",\n animationTooLarge: \"此动图过大:{frames} 帧 {width}×{height} 像素。请减少帧数或缩小尺寸。\",\n too_many_files: \"此项目的文件数已达上限。请先删除一些。\",\n animation_too_long: \"此动图过长。\",\n video_too_long: \"此文件时长超出此处允许的范围。\",\n video_too_large: \"此视频的画面尺寸超出此处允许的范围。\",\n videoTooLongBy: \"此文件时长 {duration} 秒;此处上限为 {maxSeconds} 秒。\",\n videoTooLargeBy: \"此视频为 {width}×{height} 像素;视频最多 {megapixels} 百万像素。\",\n video_over_budget: \"处理此视频耗时过长。请使用更短或更小的视频。\",\n video_aspect_unsupported: \"此视频过宽或过高,无法在此使用。\",\n videoAspectBy: \"此视频的比例为 {shape};此处的视频比例须在 {minAspect} 至 {maxAspect} 之间。\",\n tooManyFilesBy: \"此项目最多可包含 {maxFiles} 个此类文件。请先移除一些。\",\n aborted: \"已取消。\",\n resume_mismatch: \"保存的上传属于另一个文件。请重新选择文件。\",\n moderation_rejected: \"已被审核拒绝。\",\n comment_banned: \"你无法在此评论。\",\n comments_closed: \"此处评论已关闭。\",\n comments_locked: \"解锁后即可查看和发表评论。\",\n not_joinable: \"目前无法加入此群组。\",\n limit_reached: \"已达到允许的上限。\",\n comment_too_long: \"评论太长了。\",\n commentTooLongBy: \"评论最多 {maxChars} 个字符。\",\n not_configured: \"此网站未提供该功能。\",\n tenant_mismatch: \"我们这边出了点问题,请重试。\",\n gone: \"此内容已不可用。\",\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"zh.js","names":[],"sources":["../../src/locales/zh.ts"],"sourcesContent":["import type { ContentKitUiMessageBundle } from \"../i18n/messages.js\";\nimport { admin } from \"./admin/zh.js\";\nimport { social } from \"./social/zh.js\";\nimport { groups } from \"./groups/zh.js\";\nimport { discovery } from \"./discovery/zh.js\";\nimport { content } from \"./content/zh.js\";\n\nexport const zh: ContentKitUiMessageBundle = {\n ...social,\n ...admin,\n ...groups,\n ...discovery,\n ...content,\n common: {\n cancel: \"取消\",\n close: \"关闭\",\n save: \"保存\",\n saving: \"正在保存…\",\n retry: \"重试\",\n change: \"更换\",\n upload: \"上传\",\n editCrop: \"编辑裁剪\",\n remove: \"移除\",\n rename: \"重命名\",\n replace: \"替换\",\n },\n crop: {\n title: \"裁剪图片\",\n avatarTitle: \"裁剪头像\",\n coverTitle: \"裁剪封面\",\n description: \"拖动调整位置。滚动、双指缩放或使用滑块来缩放。\",\n area: \"裁剪区域。使用方向键移动图片,按 + 或 − 缩放,按 R 旋转。\",\n zoom: \"缩放\",\n zoomIn: \"放大\",\n zoomOut: \"缩小\",\n rotateLeft: \"向左旋转\",\n rotateRight: \"向右旋转\",\n reset: \"重置\",\n undersized: \"此裁剪宽 {width} 像素;在高分辨率屏幕上需要 {target} 像素或以上才清晰。\",\n tooSmall: \"此图片最宽 {width} 像素;至少需要 {min} 像素。请使用更大的图片。\",\n shape: \"比例\",\n original: \"原始比例\",\n preview: \"效果预览\",\n size: \"{width} × {height} 像素\",\n },\n sortable: {\n handle: \"调整 {name} 的顺序\",\n instructions: \"要调整顺序,请按空格键或回车键,用方向键移动,再按空格键或回车键放下。按 Esc 取消。\",\n instructionsTile: \"要重新排序,请按空格键,用方向键移动,再按空格键放下。回车键选择,Esc 键取消。\",\n pickedUp: \"已拿起 {name},位于第 {position} 位,共 {total} 项。\",\n over: \"{name} 将移到第 {position} 位,共 {total} 项。\",\n dropped: \"{name} 已放在第 {position} 位,共 {total} 项。\",\n droppedAway: \"{name} 已放下。\",\n canceled: \"已取消移动 {name}。\",\n },\n folder: {\n label: \"媒体\",\n drop: \"将文件拖放到此处,或点击选择\",\n add: \"添加文件\",\n commit: \"添加 {count} 个\",\n commitAgain: \"重新添加\",\n adding: \"正在添加…\",\n queued: \"等待中\",\n uploaded: \"已上传\",\n notAdded: \"已上传,尚未添加\",\n processing: \"处理中…\",\n processingPercent: \"处理中 {percent}%\",\n edited: \"已编辑\",\n failed: \"未处理\",\n failedVideo: \"无法播放\",\n crop: \"裁剪和旋转\",\n select: \"选择 {name}\",\n selected: \"已选择 {count} 个\",\n removeSelected: \"移除所选\",\n clearSelection: \"取消选择\",\n confirmRemove: \"要移除 {count} 个文件吗?此操作无法撤销。\",\n renameLabel: \"{name} 的新名称\",\n paused: \"上传已暂停\",\n resume: \"继续\",\n dismiss: \"关闭\",\n limitSize: \"{what}最大 {size}\",\n limitMax: \"最多 {max} 个\",\n limitAspect: \"视频比例为 {min} 至 {max}\",\n kinds: {\n image: \"图片\",\n video: \"视频\",\n audio: \"音频\",\n subtitle: \"字幕\",\n file: \"文件\",\n },\n },\n readiness: {\n processing: \"正在处理媒体\",\n processingHint: \"所有文件处理完成后将自动发布。\",\n failedOne: \"无法处理 {name}\",\n failedMany: \"无法处理 {count} 个文件:{names}\",\n failedHint: \"移除这些文件后将自动发布。\",\n },\n avatar: {\n label: \"头像\",\n empty: \"暂无头像\",\n hint: \"正方形图片,至少 {size} 像素。\",\n },\n cover: {\n label: \"封面图片\",\n empty: \"暂无封面\",\n hint: \"横向图片,至少 {width} × {height} 像素。\",\n drop: \"将图片拖放到此处\",\n },\n poster: {\n title: \"设置封面\",\n description: \"从视频中选取一帧,或上传一张图片。封面与视频比例相同。\",\n frame: \"视频画面\",\n upload: \"上传图片\",\n time: \"时间\",\n previousFrame: \"上一帧\",\n nextFrame: \"下一帧\",\n jumpTo: \"跳到 {time}\",\n useFrame: \"使用此帧\",\n crop: \"裁剪…\",\n cropTitle: \"裁剪封面\",\n auto: \"自动\",\n autoHint: \"在你选择之前,将使用自动选取的画面。\",\n drop: \"将图片拖到这里,或点击选择\",\n processing: \"视频仍在处理中。编码完成后即可选择封面。\",\n rendering: \"正在生成封面…\",\n },\n progress: {\n decoding: \"正在打开图片…\",\n hashing: \"正在准备…\",\n uploading: \"正在上传… {percent}%\",\n completing: \"正在完成上传…\",\n rendering: \"正在生成各尺寸…\",\n },\n encode: {\n processing: \"正在处理视频\",\n queued: \"等待处理\",\n queuedPosition: \"排队中 · 第 {position} 位\",\n stalled: \"处理已暂停,正在等待服务器…\",\n segments: \"片段 {done} / {total}\",\n higherQualities: \"更高画质\",\n remainingSeconds: \"剩余约 {seconds} 秒\",\n remainingMinutes: \"剩余约 {minutes} 分钟\",\n remainingHours: \"剩余约 {hours} 小时 {minutes} 分钟\",\n almostDone: \"即将完成\",\n estimating: \"正在估算…\",\n phase: {\n queued: \"排队中\",\n downloading: \"准备中\",\n probing: \"分析中\",\n encoding: \"编码中\",\n muxing: \"封装中\",\n uploading: \"保存中\",\n publishing: \"发布中\",\n images: \"正在生成封面\",\n },\n },\n gallery: {\n label: \"媒体\",\n view: \"布局\",\n carousel: \"轮播\",\n grid: \"网格\",\n counter: \"{current} / {total}\",\n slide: \"第 {current} 项,共 {total} 项\",\n previous: \"上一个\",\n next: \"下一个\",\n goTo: \"转到第 {index} 项\",\n open: \"打开第 {index} 项,共 {total} 项\",\n lightbox: \"媒体查看器\",\n locked: \"其余内容已锁定\",\n processingImage: \"正在处理图片…\",\n image: \"图片 {index}\",\n video: \"视频 {index}\",\n audio: \"音频 {index}\",\n processingAudio: \"正在处理音频…\",\n download: \"下载\",\n failedAudio: \"无法处理此音频。\",\n failedImage: \"无法处理此图片。\",\n },\n player: {\n play: \"播放视频\",\n loading: \"正在加载视频…\",\n retry: \"重试\",\n code: \"代码 {code}\",\n quality: \"画质\",\n auto: \"自动\",\n autoCurrent: \"自动({quality})\",\n label: \"视频播放器\",\n resume: \"播放\",\n pause: \"暂停\",\n replay: \"重新播放\",\n seek: \"跳转\",\n time: \"{current} / {duration}\",\n seekBack: \"后退 {seconds} 秒\",\n seekForward: \"前进 {seconds} 秒\",\n seconds: \"{seconds} 秒\",\n mute: \"静音\",\n unmute: \"取消静音\",\n volume: \"音量\",\n settings: \"设置\",\n speed: \"播放速度\",\n normal: \"正常\",\n audio: \"音轨\",\n subtitles: \"字幕\",\n subtitlesOff: \"关闭\",\n forced: \"{label}(强制)\",\n track: \"轨道 {number}\",\n fullscreen: \"全屏\",\n exitFullscreen: \"退出全屏\",\n pip: \"画中画\",\n exitPip: \"退出画中画\",\n theater: \"影院模式\",\n miniPlayer: \"迷你播放器\",\n expand: \"展开\",\n close: \"关闭\",\n failed: \"无法处理此视频。\",\n errors: {\n network: \"无法连接到视频服务器。\",\n access: \"你已无权访问此视频。\",\n not_found: \"此视频不可用。\",\n rate_limited: \"请求过多,请稍后重试。\",\n unsupported: \"此浏览器无法播放此视频。\",\n stalled: \"播放已停滞。请检查网络连接后重试。\",\n unknown: \"目前无法播放此视频。\",\n },\n },\n versionDownload: {\n unavailable: \"目前无法下载。\",\n availableIn_one: \"{count} 天后可下载\",\n availableIn_other: \"{count} 天后可下载\",\n label: \"下载\",\n preparing: \"正在准备下载…\",\n locked: \"解锁后即可下载。\",\n limited: \"已达到下载上限,请在 {minutes} 分钟后重试。\",\n failed: \"无法开始下载,请重试。\",\n },\n errors: {\n ambiguous_entity: \"有多个条目与此名称匹配。请选择所需的条目。\",\n unknown_entity: \"未找到此条目。请选择现有条目或允许创建新条目。\",\n operation_conflict: \"此提交已包含其他内容。请创建新的提交。\",\n operation_busy: \"此提交仍在更新。请稍后重试。\",\n source_conflict: \"此来源已提交。\",\n content_changed: \"内容在提交后发生了更改。请在发布前检查。\",\n not_ready: \"文件仍在处理中,完成后再发布。\",\n invalid_archive: \"无法处理此ZIP。请检查其中是否包含支持的文件。\",\n download_unavailable: \"目前无法下载。\",\n no_download: \"暂时没有可下载的内容。\",\n generic: \"我们这边出了问题,请重试。\",\n network: \"无法连接到服务器。请检查网络连接后重试。\",\n server_unavailable: \"服务器暂时没有响应,请稍后重试。\",\n storage: \"上传中断,请重试。\",\n decode: \"无法将此文件作为图片打开。\",\n invalid_request: \"无法保存图片。\",\n unauthorized: \"会话已过期,请重新登录后上传。\",\n forbidden: \"你没有权限更改此图片。\",\n not_found: \"此项目已不存在。\",\n conflict: \"已在其他地方被更改,请刷新后重试。\",\n incomplete: \"上传未完成,请重试。\",\n not_uploaded: \"上传已过期,请重新选择图片。\",\n too_large: \"文件过大。\",\n quota_exceeded: \"已达到存储空间上限。\",\n type_not_allowed: \"此处不支持该文件类型。\",\n checksum_mismatch: \"上传过程中文件发生了变化,请重试。\",\n rate_limited: \"上传次数过多,请在 {seconds} 秒后重试。\",\n unavailable: \"媒体存储暂时不可用,请稍后重试。\",\n upgrade_required: \"此项目正在升级,请稍后重试。\",\n internal_error: \"我们这边出了问题,请重试。\",\n allowedTypes: \"请使用 {allowed}。\",\n tooLargeBy: \"此文件为 {size};上限为 {max}。\",\n image_too_small: \"裁剪后图片宽 {width} 像素;至少需要 {min} 像素。请选择更大的区域或使用更大的图片。\",\n image_too_large: \"此图片为 {width}×{height} 像素;图片最多 {megapixels} 百万像素。\",\n image_unreadable: \"此文件不是有效的 {format} 图片。\",\n render_timeout: \"服务器仍在处理,完成后会显示;请稍后再查看。\",\n animation_not_allowed: \"此处不允许动图。请上传静态图片。\",\n animation_unsupported: \"不支持动态 {format} 图片。请使用动态 WebP 或 GIF。\",\n animationFrames: \"动图最多 {maxFrames} 帧;此图有 {frames} 帧。\",\n animationSeconds: \"动图最长 {maxSeconds} 秒。\",\n animationTooLarge: \"此动图过大:{frames} 帧 {width}×{height} 像素。请减少帧数或缩小尺寸。\",\n too_many_files: \"此项目的文件数已达上限。请先删除一些。\",\n animation_too_long: \"此动图过长。\",\n video_too_long: \"此文件时长超出此处允许的范围。\",\n video_too_large: \"此视频的画面尺寸超出此处允许的范围。\",\n videoTooLongBy: \"此文件时长 {duration} 秒;此处上限为 {maxSeconds} 秒。\",\n videoTooLargeBy: \"此视频为 {width}×{height} 像素;视频最多 {megapixels} 百万像素。\",\n video_over_budget: \"处理此视频耗时过长。请使用更短或更小的视频。\",\n video_aspect_unsupported: \"此视频过宽或过高,无法在此使用。\",\n videoAspectBy: \"此视频的比例为 {shape};此处的视频比例须在 {minAspect} 至 {maxAspect} 之间。\",\n tooManyFilesBy: \"此项目最多可包含 {maxFiles} 个此类文件。请先移除一些。\",\n aborted: \"已取消。\",\n resume_mismatch: \"保存的上传属于另一个文件。请重新选择文件。\",\n moderation_rejected: \"已被审核拒绝。\",\n comment_banned: \"你无法在此评论。\",\n comments_closed: \"此处评论已关闭。\",\n comments_locked: \"解锁后即可查看和发表评论。\",\n not_joinable: \"目前无法加入此群组。\",\n limit_reached: \"已达到允许的上限。\",\n comment_too_long: \"评论太长了。\",\n commentTooLongBy: \"评论最多 {maxChars} 个字符。\",\n not_configured: \"此网站未提供该功能。\",\n tenant_mismatch: \"我们这边出了点问题,请重试。\",\n gone: \"此内容已不可用。\",\n },\n};\n"],"mappings":"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"}
@@ -154,7 +154,7 @@ function _(n, i, h, _ = {}) {
154
154
  k,
155
155
  h,
156
156
  Q
157
- ]), ce = u((e) => O(() => v.contents.publish(k(), e)), [
157
+ ]), ce = u((e) => O(() => v.contents.publishWhenReady(k(), e)), [
158
158
  v,
159
159
  O,
160
160
  k
@@ -1 +1 @@
1
- {"version":3,"file":"contents.js","names":[],"sources":["../../src/react/contents.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { CatalogText, Content, ContentInput, ContentPatch, Version } from \"../client/generated/wire.js\";\nimport { IMAGE_SCHEME, imageRef, imageRefs, type ManageQuery } from \"../client/contents.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { useGuarded } from \"./context.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface ManageFilter extends Omit<ManageQuery, \"limit\" | \"offset\"> {\n pageSize?: number;\n}\n\n/** The content items the caller manages, newest first, a page at a time. */\nexport function useManagedContents(f: ManageFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Content> {\n const { client, store, scope } = useContentScope(o.client);\n const { pageSize, ...q } = f;\n const size = pageSize ?? 20;\n return useList(\n store,\n keyOf(\"contents\", scope, q.kind, q.state, q.owner_group, q.q, q.language, size),\n { type: \"contents\", scope: q.state === \"deleted\" ? \"deleted\" : undefined },\n offsetPages(size, (page, signal) => client.contents.manage({ ...q, ...page }, signal)),\n );\n}\n\nexport interface UseContent {\n /** null before create (or while loading). */\n content: Content | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Reads the item again. */\n reload: () => void;\n /** Creates the item as a draft; the hook then edits it. */\n create: (input: ContentInput) => Promise<Content>;\n update: (patch: ContentPatch) => Promise<Content>;\n /** Sets the text in the hook's language; a body from the editor is stored with image references (storedBody). */\n putText: (text: CatalogText) => Promise<Content>;\n /** Publishes now, or schedules for at. */\n publish: (at?: string) => Promise<Content>;\n unpublish: () => Promise<Content>;\n remove: () => Promise<void>;\n restore: () => Promise<Content>;\n /**\n * The version whose cover and body images the hook edits: the one asked\n * for, else the first. Every version has its own cover and its own copy of\n * the body's images.\n */\n version: Version | null;\n /** Uploads the version's cover (null returns to its first upload); resolves with a URL that shows it now. */\n setCover: (file: Blob | null) => Promise<string | null>;\n /** Uploads an image for the body into the version, copied into the others; resolves with a URL that shows it now, to place in the editor. */\n uploadImage: (file: Blob) => Promise<string>;\n /**\n * A URL that shows one of the item's images now, by name (\"i-{uuid}\") or\n * reference: a fresh upload from its file, else its public file\n * (`content.images`), else, while it has none (an unpublished item's), its\n * signed editor view from an editor read. Anything else is returned as is.\n */\n imageSrc: (image: string | null | undefined) => string | undefined;\n /** The version's cover through imageSrc. */\n coverSrc: string | undefined;\n /** The language's stored text, its body's images through imageSrc: what an editor shows. */\n text: CatalogText | undefined;\n /** HTML from the editor with every image URL this hook showed turned back into the image's reference. */\n storedBody: (html: string) => string;\n}\n\n/** The names of the images a body references, once each. */\nconst refNames = (body: string | undefined) => [...new Set([...(body ?? \"\").matchAll(imageRefs())].map((m) => m[1]!))];\nconst uploadName = (path: string) => {\n const base = path.slice(path.lastIndexOf(\"/\") + 1);\n const dot = base.lastIndexOf(\".\");\n return dot > 0 ? base.slice(0, dot) : base;\n};\n\n/**\n * One content item of kind for its editors: read, create, text in language,\n * publish, delete, restore, a version's cover and the body's images. null\n * creates a new item.\n */\nexport function useContent(\n id: string | null | undefined,\n kind: string,\n language: string,\n o: { initial?: Content; client?: ContentKitClient; version?: string } = {},\n): UseContent {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const cid = id ?? created;\n const key = cid ? keyOf(\"content\", scope, cid) : null;\n const r = useResource<Content | null>(store, key, { type: \"content\", id: cid ?? undefined }, (s) => client.contents.get(cid!, language, s), { initial: o.initial });\n const content = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const guarded = useGuarded();\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await guarded(fn);\n } finally {\n setInflight((n) => n - 1);\n }\n }, [guarded]);\n const need = useCallback(() => {\n if (!cid) throw new Error(\"contentkit-ui: create the item first\");\n return cid;\n }, [cid]);\n const stored = content?.text?.[language];\n const body = stored?.body;\n const version = useMemo(() => {\n const vs = [...(content?.versions ?? [])].sort((a, b) => a.position - b.position);\n return vs.find((v) => v.id === o.version) ?? vs[0] ?? null;\n }, [content?.versions, o.version]);\n const vid = version?.id;\n const cover = version?.cover;\n const needVersion = useCallback(() => {\n if (!vid) throw new Error(\"contentkit-ui: the item has no version yet\");\n return vid;\n }, [vid]);\n\n // Images: fresh uploads show from their file, published ones from\n // content.images, the rest (an unpublished item's) from an editor read.\n const [previews, setPreviews] = useState<ReadonlyMap<string, string>>(new Map());\n const blobs = useRef<string[]>([]);\n useEffect(() => {\n const urls = blobs.current;\n return () => {\n for (const u of urls) URL.revokeObjectURL(u);\n };\n }, []);\n const published = useMemo(() => {\n const out = new Map(Object.entries(content?.images ?? {}));\n if (cover?.image && cover.url) out.set(cover.image, cover.url);\n return out;\n }, [content?.images, cover?.image, cover?.url]);\n // The cover and the body's images are the version's.\n const coverPending = !!cover?.image && !cover.url && !previews.has(cover.image) && !published.has(cover.image);\n const bodyPending = useMemo(() => refNames(body).filter((n) => !previews.has(n) && !published.has(n)), [body, previews, published]);\n const pending = useMemo(() => (coverPending ? [...bodyPending, cover!.image!] : bodyPending), [coverPending, bodyPending, cover]);\n const folder = useMemo(() => ({ kind, id: cid ?? \"none\", version: vid }), [kind, cid, vid]);\n const folderRead = useMediaRead(folder, { editor: true, read: pending.length && vid ? undefined : null, client });\n const files = useMemo(() => folderRead.read?.files ?? [], [folderRead.read]);\n const views = useMemo(() => {\n const out = new Map<string, string>();\n for (const f of files) if (f.upload && f.editor_url) out.set(uploadName(f.path), f.editor_url);\n return out;\n }, [files]);\n // Editor views render on the first editor read that misses them: read again, backing off.\n const failed = (n: string) => files.some((f) => f.failed && uploadName(f.path) === n);\n const missing = !!folderRead.read && pending.some((n) => !views.has(n) && !failed(n));\n const tries = useRef(0);\n const reload = folderRead.reload;\n useEffect(() => {\n if (!missing) {\n tries.current = 0;\n return;\n }\n if (tries.current >= 6) return;\n const t = setTimeout(() => {\n tries.current++;\n reload();\n }, 1000 * 2 ** tries.current);\n return () => clearTimeout(t);\n }, [missing, files, reload]);\n\n // Every URL shown for an image, of this and earlier reads, back to its name.\n const shown = useRef(new Map<string, string>());\n useEffect(() => {\n for (const m of [previews, published, views]) for (const [n, u] of m) shown.current.set(u, n);\n }, [previews, published, views]);\n\n const imageSrc = useCallback(\n (image: string | null | undefined) => {\n if (!image) return undefined;\n const n = image.startsWith(IMAGE_SCHEME) ? image.slice(IMAGE_SCHEME.length) : image;\n return previews.get(n) ?? published.get(n) ?? views.get(n) ?? image;\n },\n [previews, published, views],\n );\n const text = useMemo(\n () => (stored ? { ...stored, body: (stored.body ?? \"\").replace(imageRefs(), (ref, n: string) => previews.get(n) ?? published.get(n) ?? views.get(n) ?? ref) } : undefined),\n [stored, previews, published, views],\n );\n const storedBody = useCallback(\n (html: string) => {\n const back = new Map(shown.current);\n for (const m of [previews, published, views]) for (const [n, u] of m) back.set(u, n);\n let out = html;\n for (const [u, n] of back) out = out.split(u).join(imageRef(n)).split(u.replaceAll(\"&\", \"&amp;\")).join(imageRef(n));\n return out;\n },\n [previews, published, views],\n );\n const preview = useCallback((name: string, file: Blob): string | undefined => {\n if (typeof URL.createObjectURL !== \"function\") return undefined;\n const url = URL.createObjectURL(file);\n blobs.current.push(url);\n setPreviews((m) => new Map(m).set(name, url));\n return url;\n }, []);\n\n const create = useCallback(\n (input: ContentInput) =>\n run(async () => {\n const c = await client.contents.create(input);\n setCreated(c.id);\n return c;\n }),\n [client, run],\n );\n const update = useCallback((patch: ContentPatch) => run(() => client.contents.update(need(), patch)), [client, run, need]);\n const putText = useCallback(\n (t: CatalogText) => run(() => client.contents.putText(need(), language, t.body === undefined ? t : { ...t, body: storedBody(t.body) })),\n [client, run, need, language, storedBody],\n );\n const publish = useCallback((at?: string) => run(() => client.contents.publish(need(), at)), [client, run, need]);\n const unpublish = useCallback(() => run(() => client.contents.unpublish(need())), [client, run, need]);\n const remove = useCallback(() => run(() => client.contents.remove(need())), [client, run, need]);\n const restore = useCallback(() => run(() => client.contents.restore(need())), [client, run, need]);\n const setCover = useCallback(\n (file: Blob | null) =>\n run(async () => {\n if (!file) {\n await client.contents.setCover(need(), needVersion(), null);\n return null;\n }\n const ref = { kind, id: need(), version: needVersion() };\n const up = await client.media.uploadNamed(ref, file, { path: await client.media.presetPath(kind, \"cover\", file) });\n const saved = await client.contents.setCover(ref.id, ref.version, up.name);\n const url = preview(up.name, file) ?? saved.cover?.url ?? null;\n if (url) shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, needVersion, kind, preview],\n );\n const uploadImage = useCallback(\n (file: Blob) =>\n run(async () => {\n const up = await client.media.uploadInline({ kind, id: need(), version: needVersion() }, file);\n const url = preview(up.name, file) ?? up.url ?? up.ref;\n shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, needVersion, kind, preview],\n );\n return {\n content,\n reload: r.reload,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n putText,\n publish,\n unpublish,\n remove,\n restore,\n version,\n setCover,\n uploadImage,\n imageSrc,\n coverSrc: cover?.image ? imageSrc(cover.image) : (cover?.url ?? undefined),\n text,\n storedBody,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"contents.js","names":[],"sources":["../../src/react/contents.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { CatalogText, Content, ContentInput, ContentPatch, Version } from \"../client/generated/wire.js\";\nimport { IMAGE_SCHEME, imageRef, imageRefs, type ManageQuery } from \"../client/contents.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { useGuarded } from \"./context.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface ManageFilter extends Omit<ManageQuery, \"limit\" | \"offset\"> {\n pageSize?: number;\n}\n\n/** The content items the caller manages, newest first, a page at a time. */\nexport function useManagedContents(f: ManageFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Content> {\n const { client, store, scope } = useContentScope(o.client);\n const { pageSize, ...q } = f;\n const size = pageSize ?? 20;\n return useList(\n store,\n keyOf(\"contents\", scope, q.kind, q.state, q.owner_group, q.q, q.language, size),\n { type: \"contents\", scope: q.state === \"deleted\" ? \"deleted\" : undefined },\n offsetPages(size, (page, signal) => client.contents.manage({ ...q, ...page }, signal)),\n );\n}\n\nexport interface UseContent {\n /** null before create (or while loading). */\n content: Content | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Reads the item again. */\n reload: () => void;\n /** Creates the item as a draft; the hook then edits it. */\n create: (input: ContentInput) => Promise<Content>;\n update: (patch: ContentPatch) => Promise<Content>;\n /** Sets the text in the hook's language; a body from the editor is stored with image references (storedBody). */\n putText: (text: CatalogText) => Promise<Content>;\n /** Publishes now, or schedules for at. */\n publish: (at?: string) => Promise<Content>;\n unpublish: () => Promise<Content>;\n remove: () => Promise<void>;\n restore: () => Promise<Content>;\n /**\n * The version whose cover and body images the hook edits: the one asked\n * for, else the first. Every version has its own cover and its own copy of\n * the body's images.\n */\n version: Version | null;\n /** Uploads the version's cover (null returns to its first upload); resolves with a URL that shows it now. */\n setCover: (file: Blob | null) => Promise<string | null>;\n /** Uploads an image for the body into the version, copied into the others; resolves with a URL that shows it now, to place in the editor. */\n uploadImage: (file: Blob) => Promise<string>;\n /**\n * A URL that shows one of the item's images now, by name (\"i-{uuid}\") or\n * reference: a fresh upload from its file, else its public file\n * (`content.images`), else, while it has none (an unpublished item's), its\n * signed editor view from an editor read. Anything else is returned as is.\n */\n imageSrc: (image: string | null | undefined) => string | undefined;\n /** The version's cover through imageSrc. */\n coverSrc: string | undefined;\n /** The language's stored text, its body's images through imageSrc: what an editor shows. */\n text: CatalogText | undefined;\n /** HTML from the editor with every image URL this hook showed turned back into the image's reference. */\n storedBody: (html: string) => string;\n}\n\n/** The names of the images a body references, once each. */\nconst refNames = (body: string | undefined) => [...new Set([...(body ?? \"\").matchAll(imageRefs())].map((m) => m[1]!))];\nconst uploadName = (path: string) => {\n const base = path.slice(path.lastIndexOf(\"/\") + 1);\n const dot = base.lastIndexOf(\".\");\n return dot > 0 ? base.slice(0, dot) : base;\n};\n\n/**\n * One content item of kind for its editors: read, create, text in language,\n * publish, delete, restore, a version's cover and the body's images. null\n * creates a new item.\n */\nexport function useContent(\n id: string | null | undefined,\n kind: string,\n language: string,\n o: { initial?: Content; client?: ContentKitClient; version?: string } = {},\n): UseContent {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const cid = id ?? created;\n const key = cid ? keyOf(\"content\", scope, cid) : null;\n const r = useResource<Content | null>(store, key, { type: \"content\", id: cid ?? undefined }, (s) => client.contents.get(cid!, language, s), { initial: o.initial });\n const content = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const guarded = useGuarded();\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await guarded(fn);\n } finally {\n setInflight((n) => n - 1);\n }\n }, [guarded]);\n const need = useCallback(() => {\n if (!cid) throw new Error(\"contentkit-ui: create the item first\");\n return cid;\n }, [cid]);\n const stored = content?.text?.[language];\n const body = stored?.body;\n const version = useMemo(() => {\n const vs = [...(content?.versions ?? [])].sort((a, b) => a.position - b.position);\n return vs.find((v) => v.id === o.version) ?? vs[0] ?? null;\n }, [content?.versions, o.version]);\n const vid = version?.id;\n const cover = version?.cover;\n const needVersion = useCallback(() => {\n if (!vid) throw new Error(\"contentkit-ui: the item has no version yet\");\n return vid;\n }, [vid]);\n\n // Images: fresh uploads show from their file, published ones from\n // content.images, the rest (an unpublished item's) from an editor read.\n const [previews, setPreviews] = useState<ReadonlyMap<string, string>>(new Map());\n const blobs = useRef<string[]>([]);\n useEffect(() => {\n const urls = blobs.current;\n return () => {\n for (const u of urls) URL.revokeObjectURL(u);\n };\n }, []);\n const published = useMemo(() => {\n const out = new Map(Object.entries(content?.images ?? {}));\n if (cover?.image && cover.url) out.set(cover.image, cover.url);\n return out;\n }, [content?.images, cover?.image, cover?.url]);\n // The cover and the body's images are the version's.\n const coverPending = !!cover?.image && !cover.url && !previews.has(cover.image) && !published.has(cover.image);\n const bodyPending = useMemo(() => refNames(body).filter((n) => !previews.has(n) && !published.has(n)), [body, previews, published]);\n const pending = useMemo(() => (coverPending ? [...bodyPending, cover!.image!] : bodyPending), [coverPending, bodyPending, cover]);\n const folder = useMemo(() => ({ kind, id: cid ?? \"none\", version: vid }), [kind, cid, vid]);\n const folderRead = useMediaRead(folder, { editor: true, read: pending.length && vid ? undefined : null, client });\n const files = useMemo(() => folderRead.read?.files ?? [], [folderRead.read]);\n const views = useMemo(() => {\n const out = new Map<string, string>();\n for (const f of files) if (f.upload && f.editor_url) out.set(uploadName(f.path), f.editor_url);\n return out;\n }, [files]);\n // Editor views render on the first editor read that misses them: read again, backing off.\n const failed = (n: string) => files.some((f) => f.failed && uploadName(f.path) === n);\n const missing = !!folderRead.read && pending.some((n) => !views.has(n) && !failed(n));\n const tries = useRef(0);\n const reload = folderRead.reload;\n useEffect(() => {\n if (!missing) {\n tries.current = 0;\n return;\n }\n if (tries.current >= 6) return;\n const t = setTimeout(() => {\n tries.current++;\n reload();\n }, 1000 * 2 ** tries.current);\n return () => clearTimeout(t);\n }, [missing, files, reload]);\n\n // Every URL shown for an image, of this and earlier reads, back to its name.\n const shown = useRef(new Map<string, string>());\n useEffect(() => {\n for (const m of [previews, published, views]) for (const [n, u] of m) shown.current.set(u, n);\n }, [previews, published, views]);\n\n const imageSrc = useCallback(\n (image: string | null | undefined) => {\n if (!image) return undefined;\n const n = image.startsWith(IMAGE_SCHEME) ? image.slice(IMAGE_SCHEME.length) : image;\n return previews.get(n) ?? published.get(n) ?? views.get(n) ?? image;\n },\n [previews, published, views],\n );\n const text = useMemo(\n () => (stored ? { ...stored, body: (stored.body ?? \"\").replace(imageRefs(), (ref, n: string) => previews.get(n) ?? published.get(n) ?? views.get(n) ?? ref) } : undefined),\n [stored, previews, published, views],\n );\n const storedBody = useCallback(\n (html: string) => {\n const back = new Map(shown.current);\n for (const m of [previews, published, views]) for (const [n, u] of m) back.set(u, n);\n let out = html;\n for (const [u, n] of back) out = out.split(u).join(imageRef(n)).split(u.replaceAll(\"&\", \"&amp;\")).join(imageRef(n));\n return out;\n },\n [previews, published, views],\n );\n const preview = useCallback((name: string, file: Blob): string | undefined => {\n if (typeof URL.createObjectURL !== \"function\") return undefined;\n const url = URL.createObjectURL(file);\n blobs.current.push(url);\n setPreviews((m) => new Map(m).set(name, url));\n return url;\n }, []);\n\n const create = useCallback(\n (input: ContentInput) =>\n run(async () => {\n const c = await client.contents.create(input);\n setCreated(c.id);\n return c;\n }),\n [client, run],\n );\n const update = useCallback((patch: ContentPatch) => run(() => client.contents.update(need(), patch)), [client, run, need]);\n const putText = useCallback(\n (t: CatalogText) => run(() => client.contents.putText(need(), language, t.body === undefined ? t : { ...t, body: storedBody(t.body) })),\n [client, run, need, language, storedBody],\n );\n const publish = useCallback((at?: string) => run(() => client.contents.publishWhenReady(need(), at)), [client, run, need]);\n const unpublish = useCallback(() => run(() => client.contents.unpublish(need())), [client, run, need]);\n const remove = useCallback(() => run(() => client.contents.remove(need())), [client, run, need]);\n const restore = useCallback(() => run(() => client.contents.restore(need())), [client, run, need]);\n const setCover = useCallback(\n (file: Blob | null) =>\n run(async () => {\n if (!file) {\n await client.contents.setCover(need(), needVersion(), null);\n return null;\n }\n const ref = { kind, id: need(), version: needVersion() };\n const up = await client.media.uploadNamed(ref, file, { path: await client.media.presetPath(kind, \"cover\", file) });\n const saved = await client.contents.setCover(ref.id, ref.version, up.name);\n const url = preview(up.name, file) ?? saved.cover?.url ?? null;\n if (url) shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, needVersion, kind, preview],\n );\n const uploadImage = useCallback(\n (file: Blob) =>\n run(async () => {\n const up = await client.media.uploadInline({ kind, id: need(), version: needVersion() }, file);\n const url = preview(up.name, file) ?? up.url ?? up.ref;\n shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, needVersion, kind, preview],\n );\n return {\n content,\n reload: r.reload,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n putText,\n publish,\n unpublish,\n remove,\n restore,\n version,\n setCover,\n uploadImage,\n imageSrc,\n coverSrc: cover?.image ? imageSrc(cover.image) : (cover?.url ?? undefined),\n text,\n storedBody,\n };\n}\n"],"mappings":"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"}
@@ -35,22 +35,22 @@ function f(f, p, m = {}) {
35
35
  let C = l(g?.language);
36
36
  C.current = g?.language;
37
37
  let w = c(() => {
38
- let e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new WeakMap(), i, a = !1, o = !1, s = !1, c = null, l, u = () => b && x.current.permitted && x.current.owner === v && h.preferences.identity() === v, g, _ = () => v === null ? g ??= n() : void 0, w = new r({
38
+ let e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new WeakMap(), i, a = !1, o = !1, s = !1, c = null, l, u = () => x.current.owner === v && h.preferences.identity() === v, g = () => b && x.current.permitted && u(), _, w = () => v === null ? _ ??= n() : void 0, T = new r({
39
39
  contentId: f ?? "",
40
40
  versionId: p,
41
41
  idleAfter: m.idleAfter,
42
42
  checkpointInterval: m.checkpointInterval,
43
43
  begin: async () => {
44
- if (!u()) return null;
44
+ if (!g()) return null;
45
45
  let e = await h.discovery.activityContexts([{
46
46
  content_id: f,
47
47
  version_id: p
48
- }], _(), C.current);
49
- return !u() || !e.enabled ? null : e.contexts.find((e) => e.content_id === f && (!p || e.version_id === p)) ?? null;
48
+ }], w(), C.current);
49
+ return !g() || !e.enabled ? null : e.contexts.find((e) => e.content_id === f && (!p || e.version_id === p)) ?? null;
50
50
  },
51
51
  submit: async (e, t) => {
52
52
  u() && await h.discovery.engagement([e], {
53
- session: _(),
53
+ session: w(),
54
54
  ...t
55
55
  });
56
56
  },
@@ -60,8 +60,8 @@ function f(f, p, m = {}) {
60
60
  ...S.current ? { kind: S.current } : {}
61
61
  });
62
62
  }
63
- }), T = () => {
64
- if (!a || !u()) return;
63
+ }), E = () => {
64
+ if (!a || !g()) return;
65
65
  let n = [], r;
66
66
  for (let [i, a] of e) {
67
67
  let e = a.element;
@@ -86,17 +86,17 @@ function f(f, p, m = {}) {
86
86
  positionMs: Math.max(0, Math.round(c.currentTime * 1e3)),
87
87
  playing: !s && !c.paused && !c.ended && !c.seeking,
88
88
  seeking: c.seeking
89
- }), i.video && (i.resume = String(c.currentTime)), w.observe(i), w.attention(!o && document.visibilityState !== "hidden" && document.hasFocus());
90
- }, E = (t, n) => {
89
+ }), i.video && (i.resume = String(c.currentTime)), T.observe(i), T.attention(!o && document.visibilityState !== "hidden" && document.hasFocus());
90
+ }, D = (t, n) => {
91
91
  let r = e.get(t);
92
92
  return r || (r = {
93
93
  element: null,
94
94
  resume: n,
95
95
  ref: (e) => {
96
- r.element && (r.element.removeEventListener("load", T), i?.unobserve(r.element)), r.element = e, e && i?.observe(e), e?.addEventListener("load", T), T();
96
+ r.element && (r.element.removeEventListener("load", E), i?.unobserve(r.element)), r.element = e, e && i?.observe(e), e?.addEventListener("load", E), E();
97
97
  }
98
98
  }, e.set(t, r)), r.resume = n, r.ref;
99
- }, D = [
99
+ }, O = [
100
100
  "play",
101
101
  "playing",
102
102
  "canplay",
@@ -108,57 +108,58 @@ function f(f, p, m = {}) {
108
108
  "ratechange",
109
109
  "waiting",
110
110
  "stalled"
111
- ], O = (e) => {
111
+ ], k = (e) => {
112
112
  e.type === "waiting" || e.type === "stalled" ? s = !0 : [
113
113
  "playing",
114
114
  "canplay",
115
115
  "seeked",
116
116
  "loadeddata"
117
- ].includes(e.type) && (s = !1), T();
117
+ ].includes(e.type) && (s = !1), E();
118
118
  };
119
119
  return {
120
- tracker: w,
121
- sample: T,
122
- owns: u,
123
- page: E,
120
+ tracker: T,
121
+ sample: E,
122
+ owns: g,
123
+ sameOwner: u,
124
+ page: D,
124
125
  bindVideo: (e) => {
125
- for (let e of D) c?.removeEventListener(e, O);
126
+ for (let e of O) c?.removeEventListener(e, k);
126
127
  c && i?.unobserve(c), c = e, s = !1, c && i?.observe(c);
127
- for (let e of D) c?.addEventListener(e, O);
128
- T();
128
+ for (let e of O) c?.addEventListener(e, k);
129
+ E();
129
130
  },
130
- flush: () => (T(), u() ? w.flush() : Promise.resolve()),
131
+ flush: () => (E(), u() ? T.flush() : Promise.resolve()),
131
132
  setResume: (e) => {
132
- l = e, T();
133
+ l = e, E();
133
134
  },
134
135
  start: () => {
135
136
  if (a = !0, !i && typeof IntersectionObserver < "u") {
136
137
  i = new IntersectionObserver((e) => {
137
138
  for (let n of e) t.set(n.target, n.isIntersecting);
138
- T();
139
+ E();
139
140
  });
140
141
  for (let t of e.values()) t.element && i.observe(t.element);
141
142
  c && i.observe(c);
142
143
  }
143
- T();
144
+ E();
144
145
  },
145
146
  stop: () => {
146
147
  a = !1;
147
148
  },
148
149
  isActive: () => a,
149
150
  hide: () => {
150
- T(), o = !0, w.attention(!1), w.flush(!0);
151
+ E(), o = !0, T.attention(!1), T.flush(!0);
151
152
  },
152
153
  show: () => {
153
- o = !1, w.activity(), T();
154
+ o = !1, T.activity(), E();
154
155
  },
155
156
  activity: () => {
156
- T(), w.activity();
157
+ E(), T.activity();
157
158
  },
158
159
  cleanup: () => {
159
160
  i?.disconnect();
160
- for (let t of e.values()) t.element?.removeEventListener("load", T);
161
- for (let e of D) c?.removeEventListener(e, O);
161
+ for (let t of e.values()) t.element?.removeEventListener("load", E);
162
+ for (let e of O) c?.removeEventListener(e, k);
162
163
  }
163
164
  };
164
165
  }, [
@@ -172,7 +173,7 @@ function f(f, p, m = {}) {
172
173
  m.checkpointInterval
173
174
  ]);
174
175
  s(() => h.preferences.subscribeIdentity(() => {
175
- w.owns() || w.tracker.discard();
176
+ w.sameOwner() || w.tracker.discard();
176
177
  }), [h, w]), o(() => {
177
178
  if (!b || !w.owns()) return;
178
179
  w.start();
@@ -198,7 +199,7 @@ function f(f, p, m = {}) {
198
199
  for (let e of t) window.removeEventListener(e, w.activity, !0);
199
200
  window.removeEventListener("scroll", w.sample, !0), window.removeEventListener("resize", w.sample), window.removeEventListener("focus", w.show), window.removeEventListener("blur", r), window.removeEventListener("pagehide", w.hide), window.removeEventListener("pageshow", w.show), window.removeEventListener("online", w.flush), document.removeEventListener("visibilitychange", n), queueMicrotask(() => {
200
201
  if (!w.isActive()) {
201
- if (w.cleanup(), !w.owns()) {
202
+ if (w.cleanup(), !w.sameOwner()) {
202
203
  w.tracker.discard();
203
204
  return;
204
205
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-engagement.js","names":[],"sources":["../../src/react/use-engagement.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport { readerSession } from \"../client/beacons.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { EngagementTracker, type ReadingObservation, type VisiblePage } from \"../client/engagement.js\";\nimport { ContentKitContext, useContentKitClient } from \"./context.js\";\n\nexport interface ReadingEngagementOptions {\n client?: ContentKitClient;\n enabled?: boolean;\n /** Local guest history grouping only; the server infers kind from content. */\n kind?: string;\n /** Genuine input renews attention for this long; default 60 seconds. */\n idleAfter?: number;\n /** Snapshot interval, at most 120 seconds. Page changes never send requests. */\n checkpointInterval?: number;\n}\n\nexport interface ReadingEngagement {\n /** Attach to the actual loaded image, not its placeholder or thumbnail. */\n page: (index: number, resume?: string) => (element: HTMLImageElement | null) => void;\n /** Attach to the playing video. Seeks do not count as watched intervals. */\n video: (element: HTMLVideoElement | null) => void;\n /** Attach to a text post's body: one unit covered by its visible share. */\n text: (element: HTMLElement | null) => void;\n /** Existing filename/anchor selector to restore, independent of page index. */\n setResume: (resume: string | undefined) => void;\n flush: () => Promise<void>;\n}\n\ninterface PageBinding {\n element: HTMLElement | null;\n resume?: string;\n ref: (element: HTMLElement | null) => void;\n}\n\nfunction visibleRange(element: HTMLElement): { from: number; to: number; area: number } | undefined {\n if (!element.isConnected) return;\n const style = getComputedStyle(element);\n if (style.display === \"none\" || style.visibility === \"hidden\" || (style.opacity !== \"\" && Number(style.opacity) === 0)) return;\n const box = element.getBoundingClientRect();\n if (box.width <= 0 || box.height <= 0) return;\n let top = box.top, left = box.left, width = box.width, height = box.height;\n if (element instanceof HTMLImageElement && style.objectFit === \"contain\") {\n const scale = Math.min(width / element.naturalWidth, height / element.naturalHeight);\n const imageWidth = element.naturalWidth * scale, imageHeight = element.naturalHeight * scale;\n const [x = \"50%\", y = \"50%\"] = style.objectPosition.split(\" \");\n const offset = (position: string, space: number) => position.endsWith(\"%\") ? space * Number.parseFloat(position) / 100 : space / 2;\n left += offset(x, width - imageWidth); top += offset(y, height - imageHeight);\n width = imageWidth; height = imageHeight;\n }\n const viewport = element.closest(\"[data-reader-viewport]\")?.getBoundingClientRect();\n const visibleTop = Math.max(top, 0, viewport?.top ?? 0);\n const visibleBottom = Math.min(top + height, window.innerHeight, viewport?.bottom ?? window.innerHeight);\n const visibleLeft = Math.max(left, 0, viewport?.left ?? 0);\n const visibleRight = Math.min(left + width, window.innerWidth, viewport?.right ?? window.innerWidth);\n if (visibleBottom <= visibleTop || visibleRight <= visibleLeft) return;\n return { from: Math.max(0, Math.floor((visibleTop - top) / height * 10_000)),\n to: Math.min(10_000, Math.ceil((visibleBottom - top) / height * 10_000)),\n area: (visibleBottom - visibleTop) * (visibleRight - visibleLeft) };\n}\n\n/**\n * Keep this hook in the content page, above horizontal/vertical renderer switches.\n * It records loaded, visible attention, with one visit and bounded access receipts;\n * preloads and route opens alone produce no engagement or network writes.\n */\nexport function useReadingEngagement(contentId: string | null | undefined, versionId?: string, options: ReadingEngagementOptions = {}): ReadingEngagement {\n const client = useContentKitClient(options.client);\n const context = useContext(ContentKitContext);\n const identity = useSyncExternalStore(client.preferences.subscribeIdentity, client.preferences.identity, client.preferences.identity);\n const owner = context ? context.viewer : identity;\n const explicitGuest = context?.viewer === null;\n const permitted = !!contentId && options.enabled !== false && owner !== undefined;\n const current = useRef({ owner, permitted });\n current.current = { owner, permitted };\n const kind = useRef(options.kind);\n kind.current = options.kind;\n const language = useRef(context?.language);\n language.current = context?.language;\n\n const state = useMemo(() => {\n const pages = new Map<number, PageBinding>();\n const intersections = new WeakMap<Element, boolean>();\n let observer: IntersectionObserver | undefined;\n let active = false, hidden = false, buffering = false, video: HTMLVideoElement | null = null, resume: string | undefined;\n const owns = () => permitted && current.current.permitted && current.current.owner === owner && client.preferences.identity() === owner;\n let session: string | undefined;\n const getSession = () => owner === null ? (session ??= readerSession()) : undefined;\n const tracker = new EngagementTracker({\n contentId: contentId ?? \"\", versionId, idleAfter: options.idleAfter, checkpointInterval: options.checkpointInterval,\n begin: async () => {\n if (!owns()) return null;\n const result = await client.discovery.activityContexts([{ content_id: contentId!, version_id: versionId }], getSession(), language.current);\n if (!owns() || !result.enabled) return null;\n return result.contexts.find((receipt) => receipt.content_id === contentId && (!versionId || receipt.version_id === versionId)) ?? null;\n },\n submit: async (snapshot, submitOptions) => {\n if (!owns()) return;\n await client.discovery.engagement([snapshot], { session: getSession(), ...submitOptions });\n },\n onSnapshot: (snapshot) => {\n if (owns() && owner === null && explicitGuest) client.guestHistory.record({ ...snapshot, ...(kind.current ? { kind: kind.current } : {}) });\n },\n });\n const sample = () => {\n if (!active || !owns()) return;\n const visible: VisiblePage[] = [];\n let selected: { area: number; resume?: string } | undefined;\n for (const [index, binding] of pages) {\n const image = binding.element;\n // An image counts once loaded; a text body as soon as it shows.\n if (!image || image instanceof HTMLImageElement && (!image.complete || image.naturalWidth <= 0) || intersections.get(image) === false) continue;\n const range = visibleRange(image);\n if (!range) continue;\n visible.push({ index, ranges: [{ from: range.from, to: range.to }] });\n if (!selected || range.area > selected.area) selected = { area: range.area, resume: binding.resume };\n }\n const observation: ReadingObservation = { pages: visible, resume: resume ?? selected?.resume };\n if (video && video.readyState >= 2 && visibleRange(video)) observation.video = {\n positionMs: Math.max(0, Math.round(video.currentTime * 1000)), playing: !buffering && !video.paused && !video.ended && !video.seeking,\n seeking: video.seeking,\n };\n if (observation.video) observation.resume = String(video!.currentTime);\n tracker.observe(observation);\n tracker.attention(!hidden && document.visibilityState !== \"hidden\" && document.hasFocus());\n };\n const page = (index: number, selector?: string) => {\n let binding = pages.get(index);\n if (!binding) {\n binding = { element: null, resume: selector, ref: (element) => {\n if (binding!.element) {\n binding!.element.removeEventListener(\"load\", sample);\n observer?.unobserve(binding!.element);\n }\n binding!.element = element;\n if (element) observer?.observe(element);\n element?.addEventListener(\"load\", sample);\n sample();\n } };\n pages.set(index, binding);\n }\n binding.resume = selector;\n return binding.ref;\n };\n const mediaEvents = [\"play\", \"playing\", \"canplay\", \"pause\", \"seeking\", \"seeked\", \"ended\", \"loadeddata\", \"ratechange\", \"waiting\", \"stalled\"];\n const mediaEvent = (event: Event) => {\n if (event.type === \"waiting\" || event.type === \"stalled\") buffering = true;\n else if ([\"playing\", \"canplay\", \"seeked\", \"loadeddata\"].includes(event.type)) buffering = false;\n sample();\n };\n const bindVideo = (element: HTMLVideoElement | null) => {\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n if (video) observer?.unobserve(video);\n video = element; buffering = false;\n if (video) observer?.observe(video);\n for (const event of mediaEvents) video?.addEventListener(event, mediaEvent);\n sample();\n };\n const flush = () => { sample(); return owns() ? tracker.flush() : Promise.resolve(); };\n return {\n tracker, sample, owns, page, bindVideo, flush,\n setResume: (value: string | undefined) => { resume = value; sample(); },\n start: () => {\n active = true;\n if (!observer && typeof IntersectionObserver !== \"undefined\") {\n observer = new IntersectionObserver((entries) => {\n for (const entry of entries) intersections.set(entry.target, entry.isIntersecting);\n sample();\n });\n for (const binding of pages.values()) if (binding.element) observer.observe(binding.element);\n if (video) observer.observe(video);\n }\n sample();\n },\n stop: () => { active = false; },\n isActive: () => active,\n hide: () => { sample(); hidden = true; tracker.attention(false); void tracker.flush(true); },\n show: () => { hidden = false; tracker.activity(); sample(); },\n activity: () => { sample(); tracker.activity(); },\n cleanup: () => {\n observer?.disconnect();\n for (const binding of pages.values()) binding.element?.removeEventListener(\"load\", sample);\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n },\n };\n // Language and renderer mode do not start new visits. Owner changes do.\n }, [client, contentId, versionId, owner, explicitGuest, permitted, options.idleAfter, options.checkpointInterval]);\n\n useLayoutEffect(() => client.preferences.subscribeIdentity(() => {\n if (!state.owns()) state.tracker.discard();\n }), [client, state]);\n\n useEffect(() => {\n if (!permitted || !state.owns()) return;\n state.start();\n const timer = setInterval(state.sample, 250);\n const inputEvents = [\"pointerdown\", \"pointermove\", \"keydown\", \"wheel\", \"touchstart\", \"touchmove\"];\n for (const event of inputEvents) window.addEventListener(event, state.activity, { passive: true, capture: true });\n const visibility = () => { if (document.visibilityState === \"hidden\") state.hide(); else state.show(); };\n const blur = () => { state.sample(); state.tracker.attention(false); };\n window.addEventListener(\"scroll\", state.sample, true);\n window.addEventListener(\"resize\", state.sample);\n window.addEventListener(\"focus\", state.show);\n window.addEventListener(\"blur\", blur);\n window.addEventListener(\"pagehide\", state.hide);\n window.addEventListener(\"pageshow\", state.show);\n window.addEventListener(\"online\", state.flush);\n document.addEventListener(\"visibilitychange\", visibility);\n return () => {\n state.stop(); clearInterval(timer);\n for (const event of inputEvents) window.removeEventListener(event, state.activity, true);\n window.removeEventListener(\"scroll\", state.sample, true);\n window.removeEventListener(\"resize\", state.sample);\n window.removeEventListener(\"focus\", state.show);\n window.removeEventListener(\"blur\", blur);\n window.removeEventListener(\"pagehide\", state.hide);\n window.removeEventListener(\"pageshow\", state.show);\n window.removeEventListener(\"online\", state.flush);\n document.removeEventListener(\"visibilitychange\", visibility);\n // StrictMode's immediate setup replay must keep the same visit alive.\n queueMicrotask(() => {\n if (state.isActive()) return;\n state.cleanup();\n if (!state.owns()) { state.tracker.discard(); return; }\n state.tracker.attention(false);\n void state.tracker.flush(true).finally(() => state.tracker.discard());\n });\n };\n }, [permitted, state]);\n\n const video = useCallback((element: HTMLVideoElement | null) => state.bindVideo(element), [state]);\n const text = useCallback((element: HTMLElement | null) => state.page(0)(element), [state]);\n return useMemo(() => ({ page: state.page, video, text, setResume: state.setResume, flush: state.flush }), [state, video, text]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-engagement.js","names":[],"sources":["../../src/react/use-engagement.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport { readerSession } from \"../client/beacons.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { EngagementTracker, type ReadingObservation, type VisiblePage } from \"../client/engagement.js\";\nimport { ContentKitContext, useContentKitClient } from \"./context.js\";\n\nexport interface ReadingEngagementOptions {\n client?: ContentKitClient;\n enabled?: boolean;\n /** Local guest history grouping only; the server infers kind from content. */\n kind?: string;\n /** Genuine input renews attention for this long; default 60 seconds. */\n idleAfter?: number;\n /** Snapshot interval, at most 120 seconds. Page changes never send requests. */\n checkpointInterval?: number;\n}\n\nexport interface ReadingEngagement {\n /** Attach to the actual loaded image, not its placeholder or thumbnail. */\n page: (index: number, resume?: string) => (element: HTMLImageElement | null) => void;\n /** Attach to the playing video. Seeks do not count as watched intervals. */\n video: (element: HTMLVideoElement | null) => void;\n /** Attach to a text post's body: one unit covered by its visible share. */\n text: (element: HTMLElement | null) => void;\n /** Existing filename/anchor selector to restore, independent of page index. */\n setResume: (resume: string | undefined) => void;\n flush: () => Promise<void>;\n}\n\ninterface PageBinding {\n element: HTMLElement | null;\n resume?: string;\n ref: (element: HTMLElement | null) => void;\n}\n\nfunction visibleRange(element: HTMLElement): { from: number; to: number; area: number } | undefined {\n if (!element.isConnected) return;\n const style = getComputedStyle(element);\n if (style.display === \"none\" || style.visibility === \"hidden\" || (style.opacity !== \"\" && Number(style.opacity) === 0)) return;\n const box = element.getBoundingClientRect();\n if (box.width <= 0 || box.height <= 0) return;\n let top = box.top, left = box.left, width = box.width, height = box.height;\n if (element instanceof HTMLImageElement && style.objectFit === \"contain\") {\n const scale = Math.min(width / element.naturalWidth, height / element.naturalHeight);\n const imageWidth = element.naturalWidth * scale, imageHeight = element.naturalHeight * scale;\n const [x = \"50%\", y = \"50%\"] = style.objectPosition.split(\" \");\n const offset = (position: string, space: number) => position.endsWith(\"%\") ? space * Number.parseFloat(position) / 100 : space / 2;\n left += offset(x, width - imageWidth); top += offset(y, height - imageHeight);\n width = imageWidth; height = imageHeight;\n }\n const viewport = element.closest(\"[data-reader-viewport]\")?.getBoundingClientRect();\n const visibleTop = Math.max(top, 0, viewport?.top ?? 0);\n const visibleBottom = Math.min(top + height, window.innerHeight, viewport?.bottom ?? window.innerHeight);\n const visibleLeft = Math.max(left, 0, viewport?.left ?? 0);\n const visibleRight = Math.min(left + width, window.innerWidth, viewport?.right ?? window.innerWidth);\n if (visibleBottom <= visibleTop || visibleRight <= visibleLeft) return;\n return { from: Math.max(0, Math.floor((visibleTop - top) / height * 10_000)),\n to: Math.min(10_000, Math.ceil((visibleBottom - top) / height * 10_000)),\n area: (visibleBottom - visibleTop) * (visibleRight - visibleLeft) };\n}\n\n/**\n * Keep this hook in the content page, above horizontal/vertical renderer switches.\n * It records loaded, visible attention, with one visit and bounded access receipts;\n * preloads and route opens alone produce no engagement or network writes.\n */\nexport function useReadingEngagement(contentId: string | null | undefined, versionId?: string, options: ReadingEngagementOptions = {}): ReadingEngagement {\n const client = useContentKitClient(options.client);\n const context = useContext(ContentKitContext);\n const identity = useSyncExternalStore(client.preferences.subscribeIdentity, client.preferences.identity, client.preferences.identity);\n const owner = context ? context.viewer : identity;\n const explicitGuest = context?.viewer === null;\n const permitted = !!contentId && options.enabled !== false && owner !== undefined;\n const current = useRef({ owner, permitted });\n current.current = { owner, permitted };\n const kind = useRef(options.kind);\n kind.current = options.kind;\n const language = useRef(context?.language);\n language.current = context?.language;\n\n const state = useMemo(() => {\n const pages = new Map<number, PageBinding>();\n const intersections = new WeakMap<Element, boolean>();\n let observer: IntersectionObserver | undefined;\n let active = false, hidden = false, buffering = false, video: HTMLVideoElement | null = null, resume: string | undefined;\n // A visit belongs to its owner; disabling tracking (e.g. closing the reader) ends it, an owner change discards it.\n const sameOwner = () => current.current.owner === owner && client.preferences.identity() === owner;\n const owns = () => permitted && current.current.permitted && sameOwner();\n let session: string | undefined;\n const getSession = () => owner === null ? (session ??= readerSession()) : undefined;\n const tracker = new EngagementTracker({\n contentId: contentId ?? \"\", versionId, idleAfter: options.idleAfter, checkpointInterval: options.checkpointInterval,\n begin: async () => {\n if (!owns()) return null;\n const result = await client.discovery.activityContexts([{ content_id: contentId!, version_id: versionId }], getSession(), language.current);\n if (!owns() || !result.enabled) return null;\n return result.contexts.find((receipt) => receipt.content_id === contentId && (!versionId || receipt.version_id === versionId)) ?? null;\n },\n submit: async (snapshot, submitOptions) => {\n if (!sameOwner()) return;\n await client.discovery.engagement([snapshot], { session: getSession(), ...submitOptions });\n },\n onSnapshot: (snapshot) => {\n if (sameOwner() && owner === null && explicitGuest) client.guestHistory.record({ ...snapshot, ...(kind.current ? { kind: kind.current } : {}) });\n },\n });\n const sample = () => {\n if (!active || !owns()) return;\n const visible: VisiblePage[] = [];\n let selected: { area: number; resume?: string } | undefined;\n for (const [index, binding] of pages) {\n const image = binding.element;\n // An image counts once loaded; a text body as soon as it shows.\n if (!image || image instanceof HTMLImageElement && (!image.complete || image.naturalWidth <= 0) || intersections.get(image) === false) continue;\n const range = visibleRange(image);\n if (!range) continue;\n visible.push({ index, ranges: [{ from: range.from, to: range.to }] });\n if (!selected || range.area > selected.area) selected = { area: range.area, resume: binding.resume };\n }\n const observation: ReadingObservation = { pages: visible, resume: resume ?? selected?.resume };\n if (video && video.readyState >= 2 && visibleRange(video)) observation.video = {\n positionMs: Math.max(0, Math.round(video.currentTime * 1000)), playing: !buffering && !video.paused && !video.ended && !video.seeking,\n seeking: video.seeking,\n };\n if (observation.video) observation.resume = String(video!.currentTime);\n tracker.observe(observation);\n tracker.attention(!hidden && document.visibilityState !== \"hidden\" && document.hasFocus());\n };\n const page = (index: number, selector?: string) => {\n let binding = pages.get(index);\n if (!binding) {\n binding = { element: null, resume: selector, ref: (element) => {\n if (binding!.element) {\n binding!.element.removeEventListener(\"load\", sample);\n observer?.unobserve(binding!.element);\n }\n binding!.element = element;\n if (element) observer?.observe(element);\n element?.addEventListener(\"load\", sample);\n sample();\n } };\n pages.set(index, binding);\n }\n binding.resume = selector;\n return binding.ref;\n };\n const mediaEvents = [\"play\", \"playing\", \"canplay\", \"pause\", \"seeking\", \"seeked\", \"ended\", \"loadeddata\", \"ratechange\", \"waiting\", \"stalled\"];\n const mediaEvent = (event: Event) => {\n if (event.type === \"waiting\" || event.type === \"stalled\") buffering = true;\n else if ([\"playing\", \"canplay\", \"seeked\", \"loadeddata\"].includes(event.type)) buffering = false;\n sample();\n };\n const bindVideo = (element: HTMLVideoElement | null) => {\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n if (video) observer?.unobserve(video);\n video = element; buffering = false;\n if (video) observer?.observe(video);\n for (const event of mediaEvents) video?.addEventListener(event, mediaEvent);\n sample();\n };\n const flush = () => { sample(); return sameOwner() ? tracker.flush() : Promise.resolve(); };\n return {\n tracker, sample, owns, sameOwner, page, bindVideo, flush,\n setResume: (value: string | undefined) => { resume = value; sample(); },\n start: () => {\n active = true;\n if (!observer && typeof IntersectionObserver !== \"undefined\") {\n observer = new IntersectionObserver((entries) => {\n for (const entry of entries) intersections.set(entry.target, entry.isIntersecting);\n sample();\n });\n for (const binding of pages.values()) if (binding.element) observer.observe(binding.element);\n if (video) observer.observe(video);\n }\n sample();\n },\n stop: () => { active = false; },\n isActive: () => active,\n hide: () => { sample(); hidden = true; tracker.attention(false); void tracker.flush(true); },\n show: () => { hidden = false; tracker.activity(); sample(); },\n activity: () => { sample(); tracker.activity(); },\n cleanup: () => {\n observer?.disconnect();\n for (const binding of pages.values()) binding.element?.removeEventListener(\"load\", sample);\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n },\n };\n // Language and renderer mode do not start new visits. Owner changes do.\n }, [client, contentId, versionId, owner, explicitGuest, permitted, options.idleAfter, options.checkpointInterval]);\n\n useLayoutEffect(() => client.preferences.subscribeIdentity(() => {\n if (!state.sameOwner()) state.tracker.discard();\n }), [client, state]);\n\n useEffect(() => {\n if (!permitted || !state.owns()) return;\n state.start();\n const timer = setInterval(state.sample, 250);\n const inputEvents = [\"pointerdown\", \"pointermove\", \"keydown\", \"wheel\", \"touchstart\", \"touchmove\"];\n for (const event of inputEvents) window.addEventListener(event, state.activity, { passive: true, capture: true });\n const visibility = () => { if (document.visibilityState === \"hidden\") state.hide(); else state.show(); };\n const blur = () => { state.sample(); state.tracker.attention(false); };\n window.addEventListener(\"scroll\", state.sample, true);\n window.addEventListener(\"resize\", state.sample);\n window.addEventListener(\"focus\", state.show);\n window.addEventListener(\"blur\", blur);\n window.addEventListener(\"pagehide\", state.hide);\n window.addEventListener(\"pageshow\", state.show);\n window.addEventListener(\"online\", state.flush);\n document.addEventListener(\"visibilitychange\", visibility);\n return () => {\n state.stop(); clearInterval(timer);\n for (const event of inputEvents) window.removeEventListener(event, state.activity, true);\n window.removeEventListener(\"scroll\", state.sample, true);\n window.removeEventListener(\"resize\", state.sample);\n window.removeEventListener(\"focus\", state.show);\n window.removeEventListener(\"blur\", blur);\n window.removeEventListener(\"pagehide\", state.hide);\n window.removeEventListener(\"pageshow\", state.show);\n window.removeEventListener(\"online\", state.flush);\n document.removeEventListener(\"visibilitychange\", visibility);\n // StrictMode's immediate setup replay must keep the same visit alive.\n queueMicrotask(() => {\n if (state.isActive()) return;\n state.cleanup();\n if (!state.sameOwner()) { state.tracker.discard(); return; }\n state.tracker.attention(false);\n void state.tracker.flush(true).finally(() => state.tracker.discard());\n });\n };\n }, [permitted, state]);\n\n const video = useCallback((element: HTMLVideoElement | null) => state.bindVideo(element), [state]);\n const text = useCallback((element: HTMLElement | null) => state.page(0)(element), [state]);\n return useMemo(() => ({ page: state.page, video, text, setResume: state.setResume, flush: state.flush }), [state, video, text]);\n}\n"],"mappings":"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"}
@@ -68,7 +68,9 @@ export declare function startDownload(d: FileDownload): void;
68
68
  export declare function downloadMessage(m: Translator, err: ContentKitError | undefined, now?: number): string | undefined;
69
69
  /**
70
70
  * A version's download, charged to the viewer's download quota: the
71
- * original or rendition its kind serves, or its pages as a zip.
71
+ * original or rendition its kind serves, or its pages as a zip. Polls while
72
+ * it is prepared (a zip is built after its pages publish, behind the worker
73
+ * queue); render_timeout after ten minutes.
72
74
  */
73
75
  export declare function useVersionDownload(content: string, version: string, o?: {
74
76
  poll?: number;