@jant/core 0.3.35 → 0.3.37

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 (307) hide show
  1. package/bin/commands/export.js +1 -1
  2. package/bin/commands/import-site.js +529 -0
  3. package/bin/commands/reset-password.js +3 -2
  4. package/dist/client/assets/heic-to-DIRPI3VF.js +1 -0
  5. package/dist/client/assets/module-RjUF93sV.js +716 -0
  6. package/dist/client/assets/native-48B9X9Wg.js +1 -0
  7. package/dist/client/assets/url-FWFqPJPb.js +1 -0
  8. package/dist/client/client.css +1 -1
  9. package/dist/client/client.js +4564 -3013
  10. package/dist/index.js +12885 -8161
  11. package/package.json +23 -6
  12. package/src/__tests__/helpers/app.ts +10 -10
  13. package/src/__tests__/helpers/db.ts +91 -87
  14. package/src/app.tsx +157 -31
  15. package/src/auth.ts +20 -2
  16. package/src/client/archive-nav.js +187 -0
  17. package/src/client/audio-player.ts +478 -0
  18. package/src/client/audio-processor.ts +84 -0
  19. package/src/{lib → client}/avatar-upload.ts +4 -3
  20. package/src/{lib → client}/collection-form-bridge.ts +2 -2
  21. package/src/{ui → client}/components/__tests__/jant-collection-form.test.ts +26 -9
  22. package/src/client/components/__tests__/jant-compose-dialog.test.ts +1140 -0
  23. package/src/client/components/__tests__/jant-compose-editor.test.ts +504 -0
  24. package/src/{ui → client}/components/__tests__/jant-post-form.test.ts +37 -17
  25. package/src/{ui → client}/components/__tests__/jant-settings-avatar.test.ts +2 -2
  26. package/src/{ui → client}/components/__tests__/jant-settings-general.test.ts +3 -3
  27. package/src/client/components/collection-sidebar-types.ts +43 -0
  28. package/src/{ui → client}/components/collection-types.ts +3 -4
  29. package/src/client/components/compose-types.ts +174 -0
  30. package/src/client/components/jant-collection-form.ts +667 -0
  31. package/src/client/components/jant-collection-sidebar.ts +805 -0
  32. package/src/client/components/jant-compose-dialog.ts +2161 -0
  33. package/src/client/components/jant-compose-editor.ts +1813 -0
  34. package/src/client/components/jant-compose-fullscreen.ts +283 -0
  35. package/src/client/components/jant-media-lightbox.ts +259 -0
  36. package/src/{ui → client}/components/jant-nav-manager.ts +97 -298
  37. package/src/{ui → client}/components/jant-post-form.ts +141 -12
  38. package/src/client/components/jant-post-menu.ts +1019 -0
  39. package/src/{ui → client}/components/jant-settings-avatar.ts +3 -3
  40. package/src/{ui → client}/components/jant-settings-general.ts +38 -4
  41. package/src/client/components/jant-text-preview.ts +232 -0
  42. package/src/{ui → client}/components/nav-manager-types.ts +6 -18
  43. package/src/{ui → client}/components/post-form-template.ts +137 -38
  44. package/src/{ui → client}/components/post-form-types.ts +15 -4
  45. package/src/client/compose-bridge.ts +583 -0
  46. package/src/{lib → client}/image-processor.ts +26 -8
  47. package/src/client/lazy-slugify.ts +51 -0
  48. package/src/client/media-metadata.ts +247 -0
  49. package/src/client/multipart-upload.ts +160 -0
  50. package/src/{lib → client}/nav-manager-bridge.ts +1 -1
  51. package/src/{lib → client}/post-form-bridge.ts +53 -2
  52. package/src/{lib → client}/settings-bridge.ts +3 -15
  53. package/src/client/thread-context.ts +140 -0
  54. package/src/client/tiptap/bubble-menu.ts +205 -0
  55. package/src/client/tiptap/create-editor.ts +86 -0
  56. package/src/client/tiptap/exitable-marks.ts +73 -0
  57. package/src/client/tiptap/extensions.ts +65 -0
  58. package/src/client/tiptap/image-node.ts +482 -0
  59. package/src/client/tiptap/link-toolbar.ts +371 -0
  60. package/src/client/tiptap/more-break.ts +50 -0
  61. package/src/client/tiptap/paste-image.ts +129 -0
  62. package/src/client/tiptap/slash-commands.ts +438 -0
  63. package/src/{lib → client}/toast.ts +101 -3
  64. package/src/client/types/sortablejs.d.ts +44 -0
  65. package/src/client/upload-with-metadata.ts +54 -0
  66. package/src/client/video-processor.ts +207 -0
  67. package/src/client.ts +27 -17
  68. package/src/db/__tests__/migrations.test.ts +118 -0
  69. package/src/db/index.ts +52 -0
  70. package/src/db/migrations/0000_baseline.sql +269 -0
  71. package/src/db/migrations/0001_fts_setup.sql +31 -0
  72. package/src/db/migrations/meta/0000_snapshot.json +703 -119
  73. package/src/db/migrations/meta/0001_snapshot.json +1337 -0
  74. package/src/db/migrations/meta/_journal.json +4 -39
  75. package/src/db/schema.ts +409 -140
  76. package/src/i18n/__tests__/detect.test.ts +115 -0
  77. package/src/i18n/context.tsx +2 -2
  78. package/src/i18n/detect.ts +85 -1
  79. package/src/i18n/i18n.ts +1 -1
  80. package/src/i18n/index.ts +2 -1
  81. package/src/i18n/locales/en.po +783 -1087
  82. package/src/i18n/locales/en.ts +1 -1
  83. package/src/i18n/locales/zh-Hans.po +867 -812
  84. package/src/i18n/locales/zh-Hans.ts +1 -1
  85. package/src/i18n/locales/zh-Hant.po +878 -823
  86. package/src/i18n/locales/zh-Hant.ts +1 -1
  87. package/src/i18n/middleware.ts +6 -0
  88. package/src/index.ts +5 -7
  89. package/src/lib/__tests__/blurhash-placeholder.test.ts +75 -0
  90. package/src/lib/__tests__/constants.test.ts +0 -1
  91. package/src/lib/__tests__/markdown-to-tiptap.test.ts +358 -0
  92. package/src/lib/__tests__/nanoid.test.ts +26 -0
  93. package/src/lib/__tests__/resolve-config.test.ts +2 -2
  94. package/src/lib/__tests__/schemas.test.ts +186 -65
  95. package/src/lib/__tests__/slug.test.ts +126 -0
  96. package/src/lib/__tests__/sse.test.ts +6 -6
  97. package/src/lib/__tests__/summary.test.ts +264 -0
  98. package/src/lib/__tests__/theme.test.ts +1 -1
  99. package/src/lib/__tests__/timeline.test.ts +33 -30
  100. package/src/lib/__tests__/tiptap-to-markdown.test.ts +346 -0
  101. package/src/lib/__tests__/url.test.ts +2 -2
  102. package/src/lib/__tests__/view.test.ts +140 -65
  103. package/src/lib/blurhash-placeholder.ts +102 -0
  104. package/src/lib/constants.ts +3 -1
  105. package/src/lib/emoji-catalog.ts +963 -0
  106. package/src/lib/errors.ts +11 -8
  107. package/src/lib/feed.ts +77 -31
  108. package/src/lib/html.ts +2 -1
  109. package/src/lib/icon-catalog.ts +5033 -1
  110. package/src/lib/icons.ts +3 -2
  111. package/src/lib/index.ts +0 -1
  112. package/src/lib/markdown-to-tiptap.ts +286 -0
  113. package/src/lib/media-helpers.ts +22 -12
  114. package/src/lib/nanoid.ts +29 -0
  115. package/src/lib/navigation.ts +1 -1
  116. package/src/lib/render.tsx +24 -5
  117. package/src/lib/resolve-config.ts +13 -2
  118. package/src/lib/schemas.ts +226 -58
  119. package/src/lib/search-snippet.ts +34 -0
  120. package/src/lib/slug.ts +96 -0
  121. package/src/lib/sse.ts +6 -6
  122. package/src/lib/storage.ts +115 -7
  123. package/src/lib/summary.ts +158 -0
  124. package/src/lib/theme.ts +11 -8
  125. package/src/lib/timeline.ts +76 -34
  126. package/src/lib/tiptap-render.ts +191 -0
  127. package/src/lib/tiptap-to-markdown.ts +305 -0
  128. package/src/lib/upload.ts +263 -14
  129. package/src/lib/url.ts +37 -22
  130. package/src/lib/view.ts +236 -55
  131. package/src/middleware/__tests__/auth.test.ts +191 -11
  132. package/src/middleware/__tests__/onboarding.test.ts +12 -10
  133. package/src/middleware/auth.ts +63 -9
  134. package/src/middleware/error-handler.ts +3 -3
  135. package/src/middleware/onboarding.ts +1 -1
  136. package/src/middleware/secure-headers.ts +40 -0
  137. package/src/preset.css +83 -2
  138. package/src/routes/__tests__/compose.test.ts +17 -24
  139. package/src/routes/api/__tests__/collections.test.ts +109 -61
  140. package/src/routes/api/__tests__/nav-items.test.ts +46 -29
  141. package/src/routes/api/__tests__/posts.test.ts +132 -68
  142. package/src/routes/api/__tests__/search.test.ts +15 -2
  143. package/src/routes/api/__tests__/upload-multipart.test.ts +534 -0
  144. package/src/routes/api/collections.ts +57 -31
  145. package/src/routes/api/custom-urls.ts +80 -0
  146. package/src/routes/api/export.ts +31 -0
  147. package/src/routes/api/nav-items.ts +23 -19
  148. package/src/routes/api/posts.ts +81 -62
  149. package/src/routes/api/search.ts +3 -4
  150. package/src/routes/api/upload-multipart.ts +245 -0
  151. package/src/routes/api/upload.ts +92 -24
  152. package/src/routes/auth/__tests__/setup.test.ts +20 -60
  153. package/src/routes/auth/reset.tsx +5 -4
  154. package/src/routes/auth/setup.tsx +39 -31
  155. package/src/routes/auth/signin.tsx +13 -14
  156. package/src/routes/compose.tsx +27 -63
  157. package/src/routes/dash/__tests__/settings-avatar.test.ts +44 -9
  158. package/src/routes/dash/custom-urls.tsx +414 -0
  159. package/src/routes/dash/settings.tsx +475 -99
  160. package/src/routes/feed/__tests__/rss.test.ts +22 -23
  161. package/src/routes/feed/rss.ts +6 -2
  162. package/src/routes/feed/sitemap.ts +2 -12
  163. package/src/routes/pages/__tests__/collections.test.ts +5 -6
  164. package/src/routes/pages/__tests__/featured.test.ts +36 -18
  165. package/src/routes/pages/archive.tsx +177 -37
  166. package/src/routes/pages/collection.tsx +43 -14
  167. package/src/routes/pages/collections.tsx +11 -2
  168. package/src/routes/pages/featured.tsx +27 -3
  169. package/src/routes/pages/home.tsx +15 -14
  170. package/src/routes/pages/latest.tsx +1 -11
  171. package/src/routes/pages/new.tsx +39 -0
  172. package/src/routes/pages/page.tsx +95 -49
  173. package/src/routes/pages/search.tsx +1 -1
  174. package/src/services/__tests__/api-token.test.ts +135 -0
  175. package/src/services/__tests__/collection.test.ts +275 -227
  176. package/src/services/__tests__/custom-url.test.ts +213 -0
  177. package/src/services/__tests__/media.test.ts +162 -22
  178. package/src/services/__tests__/navigation.test.ts +109 -68
  179. package/src/services/__tests__/post-timeline.test.ts +205 -32
  180. package/src/services/__tests__/post.test.ts +800 -230
  181. package/src/services/__tests__/search.test.ts +67 -10
  182. package/src/services/__tests__/settings.test.ts +3 -3
  183. package/src/services/api-token.ts +166 -0
  184. package/src/services/auth.ts +17 -2
  185. package/src/services/collection.ts +397 -131
  186. package/src/services/custom-url.ts +188 -0
  187. package/src/services/export.ts +802 -0
  188. package/src/services/index.ts +26 -19
  189. package/src/services/media.ts +100 -22
  190. package/src/services/navigation.ts +158 -47
  191. package/src/services/path.ts +339 -0
  192. package/src/services/post.ts +764 -172
  193. package/src/services/search.ts +161 -74
  194. package/src/services/settings.ts +6 -2
  195. package/src/styles/components.css +293 -62
  196. package/src/styles/tokens.css +93 -5
  197. package/src/styles/ui.css +4349 -766
  198. package/src/types/bindings.ts +8 -0
  199. package/src/types/config.ts +34 -4
  200. package/src/types/constants.ts +17 -2
  201. package/src/types/entities.ts +83 -37
  202. package/src/types/operations.ts +20 -27
  203. package/src/types/props.ts +52 -17
  204. package/src/types/views.ts +48 -24
  205. package/src/ui/color-themes.ts +133 -23
  206. package/src/ui/compose/ComposeDialog.tsx +255 -16
  207. package/src/ui/compose/ComposePrompt.tsx +1 -1
  208. package/src/ui/dash/CrudPageHeader.tsx +1 -1
  209. package/src/ui/dash/ListItemRow.tsx +1 -1
  210. package/src/ui/dash/StatusBadge.tsx +12 -2
  211. package/src/ui/dash/appearance/AdvancedContent.tsx +71 -59
  212. package/src/ui/dash/appearance/ColorThemeContent.tsx +48 -33
  213. package/src/ui/dash/appearance/FontThemeContent.tsx +65 -73
  214. package/src/ui/dash/appearance/NavigationContent.tsx +106 -135
  215. package/src/ui/dash/index.ts +0 -3
  216. package/src/ui/dash/settings/AccountContent.tsx +87 -146
  217. package/src/ui/dash/settings/AccountMenuContent.tsx +147 -0
  218. package/src/ui/dash/settings/ApiTokensContent.tsx +232 -0
  219. package/src/ui/dash/settings/AvatarContent.tsx +78 -0
  220. package/src/ui/dash/settings/GeneralContent.tsx +3 -62
  221. package/src/ui/dash/settings/SessionsContent.tsx +159 -0
  222. package/src/ui/dash/settings/SettingsRootContent.tsx +266 -0
  223. package/src/ui/feed/LinkCard.tsx +89 -40
  224. package/src/ui/feed/NoteCard.tsx +39 -25
  225. package/src/ui/feed/PostStatusBadges.tsx +67 -0
  226. package/src/ui/feed/QuoteCard.tsx +38 -23
  227. package/src/ui/feed/ThreadPreview.tsx +90 -26
  228. package/src/ui/feed/TimelineFeed.tsx +3 -2
  229. package/src/ui/feed/TimelineItem.tsx +15 -6
  230. package/src/ui/feed/__tests__/thread-preview.test.ts +107 -0
  231. package/src/ui/feed/thread-preview-state.ts +61 -0
  232. package/src/ui/font-themes.ts +2 -2
  233. package/src/ui/layouts/BaseLayout.tsx +2 -2
  234. package/src/ui/layouts/SiteLayout.tsx +116 -103
  235. package/src/ui/pages/ArchivePage.tsx +923 -95
  236. package/src/ui/pages/CollectionPage.tsx +6 -35
  237. package/src/ui/pages/CollectionsPage.tsx +2 -1
  238. package/src/ui/pages/ComposePage.tsx +54 -0
  239. package/src/ui/pages/FeaturedPage.tsx +2 -1
  240. package/src/ui/pages/HomePage.tsx +1 -1
  241. package/src/ui/pages/PostPage.tsx +30 -45
  242. package/src/ui/pages/SearchPage.tsx +182 -38
  243. package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
  244. package/src/ui/shared/CollectionsSidebar.tsx +239 -4
  245. package/src/ui/shared/MediaGallery.tsx +475 -41
  246. package/src/ui/shared/PostFooter.tsx +204 -0
  247. package/src/ui/shared/StarRating.tsx +27 -0
  248. package/src/ui/shared/__tests__/format-chars.test.ts +35 -0
  249. package/src/ui/shared/index.ts +0 -1
  250. package/src/db/migrations/0000_square_wallflower.sql +0 -118
  251. package/src/db/migrations/0001_add_search_fts.sql +0 -34
  252. package/src/db/migrations/0002_add_media_attachments.sql +0 -3
  253. package/src/db/migrations/0003_add_navigation_links.sql +0 -8
  254. package/src/db/migrations/0004_add_storage_provider.sql +0 -3
  255. package/src/db/migrations/0005_v2_schema_migration.sql +0 -268
  256. package/src/db/migrations/0006_rename_slug_to_path.sql +0 -5
  257. package/src/db/migrations/0007_post_collections_m2m.sql +0 -94
  258. package/src/db/migrations/0008_add_collection_dividers.sql +0 -8
  259. package/src/db/migrations/0009_drop_collection_show_divider.sql +0 -2
  260. package/src/db/migrations/0010_add_performance_indexes.sql +0 -16
  261. package/src/db/migrations/0011_add_path_registry.sql +0 -23
  262. package/src/db/migrations/meta/0003_snapshot.json +0 -821
  263. package/src/lib/__tests__/sqid.test.ts +0 -65
  264. package/src/lib/collections-reorder.ts +0 -28
  265. package/src/lib/compose-bridge.ts +0 -280
  266. package/src/lib/media-upload.ts +0 -148
  267. package/src/lib/sqid.ts +0 -79
  268. package/src/routes/api/__tests__/pages.test.ts +0 -218
  269. package/src/routes/api/pages.ts +0 -73
  270. package/src/routes/dash/__tests__/pages.test.ts +0 -226
  271. package/src/routes/dash/appearance.tsx +0 -240
  272. package/src/routes/dash/collections.tsx +0 -211
  273. package/src/routes/dash/index.tsx +0 -103
  274. package/src/routes/dash/media.tsx +0 -132
  275. package/src/routes/dash/pages.tsx +0 -239
  276. package/src/routes/dash/posts.tsx +0 -334
  277. package/src/routes/dash/redirects.tsx +0 -257
  278. package/src/routes/pages/post.tsx +0 -59
  279. package/src/services/__tests__/page.test.ts +0 -298
  280. package/src/services/__tests__/path-registry.test.ts +0 -165
  281. package/src/services/__tests__/redirect.test.ts +0 -159
  282. package/src/services/page.ts +0 -203
  283. package/src/services/path-registry.ts +0 -160
  284. package/src/services/redirect.ts +0 -97
  285. package/src/types/sortablejs.d.ts +0 -29
  286. package/src/ui/components/__tests__/jant-compose-dialog.test.ts +0 -512
  287. package/src/ui/components/__tests__/jant-compose-editor.test.ts +0 -272
  288. package/src/ui/components/compose-types.ts +0 -75
  289. package/src/ui/components/jant-collection-form.ts +0 -512
  290. package/src/ui/components/jant-compose-dialog.ts +0 -495
  291. package/src/ui/components/jant-compose-editor.ts +0 -814
  292. package/src/ui/dash/PageForm.tsx +0 -185
  293. package/src/ui/dash/PostList.tsx +0 -95
  294. package/src/ui/dash/appearance/AppearanceNav.tsx +0 -60
  295. package/src/ui/dash/collections/CollectionForm.tsx +0 -166
  296. package/src/ui/dash/collections/CollectionsListContent.tsx +0 -146
  297. package/src/ui/dash/collections/IconPickerGrid.tsx +0 -50
  298. package/src/ui/dash/collections/ViewCollectionContent.tsx +0 -103
  299. package/src/ui/dash/media/MediaListContent.tsx +0 -201
  300. package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
  301. package/src/ui/dash/pages/PagesContent.tsx +0 -74
  302. package/src/ui/dash/posts/PostForm.tsx +0 -248
  303. package/src/ui/dash/settings/SettingsNav.tsx +0 -52
  304. package/src/ui/layouts/DashLayout.tsx +0 -165
  305. package/src/ui/pages/SinglePage.tsx +0 -23
  306. package/src/ui/shared/ThreadView.tsx +0 -136
  307. /package/src/{ui → client}/components/settings-types.ts +0 -0
@@ -2,25 +2,67 @@
2
2
  * Collections Sidebar
3
3
  *
4
4
  * Shared sidebar navigation for public collection pages.
5
- * Shows all collections with icons and active state.
5
+ * - Anonymous users: static nav with collections and dividers from sidebar items
6
+ * - Authenticated users: interactive Lit component with CRUD, reorder, divider management
6
7
  */
7
8
 
8
9
  import type { FC } from "hono/jsx";
9
10
  import { useLingui } from "@lingui/react/macro";
10
- import type { Collection } from "../../types.js";
11
+ import type { Collection, SidebarItem } from "../../types.js";
11
12
  import { renderCollectionIcon } from "../../lib/icons.js";
12
13
 
14
+ const escapeJson = (data: unknown) =>
15
+ JSON.stringify(data).replace(/</g, "\\u003c");
16
+
13
17
  export interface CollectionsSidebarProps {
14
18
  collections: Collection[];
19
+ sidebarItems: SidebarItem[];
15
20
  activeSlug?: string;
21
+ isAuthenticated?: boolean;
22
+ postCounts?: Map<string, number>;
16
23
  }
17
24
 
18
25
  export const CollectionsSidebar: FC<CollectionsSidebarProps> = ({
19
26
  collections,
27
+ sidebarItems,
20
28
  activeSlug,
29
+ isAuthenticated,
30
+ postCounts,
21
31
  }) => {
32
+ if (isAuthenticated) {
33
+ return (
34
+ <AuthenticatedSidebar
35
+ collections={collections}
36
+ sidebarItems={sidebarItems}
37
+ activeSlug={activeSlug}
38
+ postCounts={postCounts}
39
+ />
40
+ );
41
+ }
42
+
43
+ return (
44
+ <AnonymousSidebar
45
+ collections={collections}
46
+ sidebarItems={sidebarItems}
47
+ activeSlug={activeSlug}
48
+ />
49
+ );
50
+ };
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Anonymous: static HTML nav
54
+ // ---------------------------------------------------------------------------
55
+
56
+ const AnonymousSidebar: FC<{
57
+ collections: Collection[];
58
+ sidebarItems: SidebarItem[];
59
+ activeSlug?: string;
60
+ }> = ({ collections, sidebarItems, activeSlug }) => {
22
61
  const { t } = useLingui();
23
62
 
63
+ // Build collection lookup
64
+ const collectionMap = new Map(collections.map((c) => [c.id, c]));
65
+
24
66
  return (
25
67
  <nav class="flex flex-col gap-1 pt-6">
26
68
  <h2 class="px-3 pb-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
@@ -29,11 +71,22 @@ export const CollectionsSidebar: FC<CollectionsSidebarProps> = ({
29
71
  comment: "@context: Sidebar heading for collections nav",
30
72
  })}
31
73
  </h2>
32
- {collections.map((col) => {
74
+ {sidebarItems.map((item) => {
75
+ if (item.type === "divider") {
76
+ return (
77
+ <div key={item.id} class="px-3 py-1">
78
+ <hr class="border-border" />
79
+ </div>
80
+ );
81
+ }
82
+ const col = item.collectionId
83
+ ? collectionMap.get(item.collectionId)
84
+ : undefined;
85
+ if (!col) return null;
33
86
  const isActive = col.slug === activeSlug;
34
87
  return (
35
88
  <a
36
- key={col.id}
89
+ key={item.id}
37
90
  href={`/c/${col.slug}`}
38
91
  class={`flex items-center gap-2.5 px-3 py-2 text-sm rounded-md truncate ${
39
92
  isActive
@@ -57,3 +110,185 @@ export const CollectionsSidebar: FC<CollectionsSidebarProps> = ({
57
110
  </nav>
58
111
  );
59
112
  };
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Authenticated: Lit component shell
116
+ // ---------------------------------------------------------------------------
117
+
118
+ const AuthenticatedSidebar: FC<{
119
+ collections: Collection[];
120
+ sidebarItems: SidebarItem[];
121
+ activeSlug?: string;
122
+ postCounts?: Map<string, number>;
123
+ }> = ({ collections, sidebarItems, activeSlug, postCounts }) => {
124
+ const { t } = useLingui();
125
+
126
+ // Build collection lookup for enriching sidebar items
127
+ const collectionMap = new Map(
128
+ collections.map((col) => [
129
+ col.id,
130
+ {
131
+ id: col.id,
132
+ slug: col.slug,
133
+ title: col.title,
134
+ description: col.description,
135
+ icon: col.icon,
136
+ sortOrder: col.sortOrder,
137
+ postCount: postCounts?.get(col.id) ?? 0,
138
+ },
139
+ ]),
140
+ );
141
+
142
+ const clientSidebarItems = sidebarItems.map((item) => ({
143
+ id: item.id,
144
+ type: item.type,
145
+ collectionId: item.collectionId,
146
+ position: item.position,
147
+ collection: item.collectionId
148
+ ? collectionMap.get(item.collectionId)
149
+ : undefined,
150
+ }));
151
+
152
+ const labels = {
153
+ collections: t({
154
+ message: "Collections",
155
+ comment: "@context: Sidebar heading for collections nav",
156
+ }),
157
+ reorder: t({
158
+ message: "Reorder",
159
+ comment: "@context: Menu action to reorder collections",
160
+ }),
161
+ done: t({
162
+ message: "Done",
163
+ comment: "@context: Button to exit reorder mode",
164
+ }),
165
+ addDivider: t({
166
+ message: "Add Divider",
167
+ comment: "@context: Menu action to add a divider",
168
+ }),
169
+ newCollection: t({
170
+ message: "New Collection",
171
+ comment: "@context: Tooltip/aria for add collection button",
172
+ }),
173
+ edit: t({
174
+ message: "Edit",
175
+ comment: "@context: Per-collection edit action",
176
+ }),
177
+ deleteDivider: t({
178
+ message: "Remove Divider",
179
+ comment: "@context: Tooltip for divider delete button",
180
+ }),
181
+ moreActions: t({
182
+ message: "More actions",
183
+ comment: "@context: Aria-label for more button",
184
+ }),
185
+ deleteCollection: t({
186
+ message: "Delete",
187
+ comment: "@context: Delete collection action",
188
+ }),
189
+ confirmDelete: t({
190
+ message:
191
+ "Delete this collection permanently? Posts inside won't be removed.",
192
+ comment: "@context: Confirm dialog for deleting a collection",
193
+ }),
194
+ orderSaved: t({
195
+ message: "Order saved",
196
+ comment: "@context: Toast after reordering collections",
197
+ }),
198
+ saved: t({
199
+ message: "Saved",
200
+ comment: "@context: Toast after saving a collection",
201
+ }),
202
+ saveFailed: t({
203
+ message: "Couldn't save. Try again in a moment.",
204
+ comment: "@context: Toast when save fails",
205
+ }),
206
+ deleted: t({
207
+ message: "Deleted",
208
+ comment: "@context: Toast after deleting a collection",
209
+ }),
210
+ formLabels: {
211
+ titleLabel: t({
212
+ message: "Title",
213
+ comment: "@context: Collection form field",
214
+ }),
215
+ titlePlaceholder: t({
216
+ message: "My Collection",
217
+ comment: "@context: Collection title placeholder",
218
+ }),
219
+ slugLabel: t({
220
+ message: "Slug",
221
+ comment: "@context: Collection form field",
222
+ }),
223
+ slugHelp: t({
224
+ message:
225
+ "URL-safe identifier (lowercase, numbers, hyphens). For CJK titles, slug will be auto-generated on the server.",
226
+ comment: "@context: Collection path help text",
227
+ }),
228
+ descriptionLabel: t({
229
+ message: "Description (optional)",
230
+ comment: "@context: Collection form field",
231
+ }),
232
+ descriptionPlaceholder: t({
233
+ message: "What's this collection about?",
234
+ comment: "@context: Collection description placeholder",
235
+ }),
236
+ removeIcon: t({
237
+ message: "Remove",
238
+ comment: "@context: Button to remove icon",
239
+ }),
240
+ iconsTab: t({
241
+ message: "Icons",
242
+ comment: "@context: Icon picker tab label",
243
+ }),
244
+ emojisTab: t({
245
+ message: "Emojis",
246
+ comment: "@context: Emoji picker tab label",
247
+ }),
248
+ searchIconsPlaceholder: t({
249
+ message: "Search icons...",
250
+ comment: "@context: Icon picker search placeholder",
251
+ }),
252
+ searchEmojisPlaceholder: t({
253
+ message: "Search emojis...",
254
+ comment: "@context: Emoji picker search placeholder",
255
+ }),
256
+ sortOrderLabel: t({
257
+ message: "Sort Order",
258
+ comment: "@context: Collection form field",
259
+ }),
260
+ sortNewest: t({
261
+ message: "Newest first",
262
+ comment: "@context: Collection sort order option",
263
+ }),
264
+ sortOldest: t({
265
+ message: "Oldest first",
266
+ comment: "@context: Collection sort order option",
267
+ }),
268
+ sortRatingDesc: t({
269
+ message: "Highest rated",
270
+ comment: "@context: Collection sort order option",
271
+ }),
272
+ sortRatingAsc: t({
273
+ message: "Lowest rated",
274
+ comment: "@context: Collection sort order option",
275
+ }),
276
+ submitLabel: t({
277
+ message: "Save",
278
+ comment: "@context: Button to save collection",
279
+ }),
280
+ cancelLabel: t({
281
+ message: "Cancel",
282
+ comment: "@context: Button to cancel form",
283
+ }),
284
+ },
285
+ };
286
+
287
+ return (
288
+ <jant-collection-sidebar
289
+ sidebar-items={escapeJson(clientSidebarItems)}
290
+ labels={escapeJson(labels)}
291
+ active-slug={activeSlug ?? ""}
292
+ />
293
+ );
294
+ };