@meith/theme-default 0.6.0 → 0.8.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 (41) hide show
  1. package/package.json +3 -3
  2. package/src/copy.ts +316 -0
  3. package/src/index.ts +6 -6
  4. package/src/messages/en.json +189 -0
  5. package/src/messages/index.ts +5 -0
  6. package/src/panel-icons.tsx +1 -2
  7. package/src/shared.tsx +4 -6
  8. package/src/slots/announcement.tsx +15 -9
  9. package/src/slots/auth-page.tsx +3 -1
  10. package/src/slots/board-index.tsx +11 -4
  11. package/src/slots/board-stats.tsx +14 -12
  12. package/src/slots/category-block.tsx +1 -1
  13. package/src/slots/discovery-view.tsx +16 -15
  14. package/src/slots/error-notice.tsx +17 -9
  15. package/src/slots/footer.tsx +15 -4
  16. package/src/slots/forum-display.tsx +25 -20
  17. package/src/slots/forum-jump.tsx +1 -1
  18. package/src/slots/forum-row.tsx +16 -15
  19. package/src/slots/header.tsx +13 -3
  20. package/src/slots/latest-posts.tsx +21 -12
  21. package/src/slots/latest-threads.tsx +27 -16
  22. package/src/slots/member-profile.tsx +19 -15
  23. package/src/slots/navigation.tsx +7 -4
  24. package/src/slots/notice.tsx +12 -9
  25. package/src/slots/pagination.tsx +16 -10
  26. package/src/slots/panel-nav.tsx +37 -16
  27. package/src/slots/panel-page.tsx +15 -3
  28. package/src/slots/panel-shell.tsx +1 -7
  29. package/src/slots/post-actions.tsx +18 -11
  30. package/src/slots/post-bit.tsx +42 -20
  31. package/src/slots/post-form.tsx +7 -3
  32. package/src/slots/redirect-notice.tsx +16 -7
  33. package/src/slots/search-form.tsx +13 -9
  34. package/src/slots/search-results.tsx +27 -26
  35. package/src/slots/shell.tsx +4 -3
  36. package/src/slots/subforum-list.tsx +12 -9
  37. package/src/slots/thread-row.tsx +19 -18
  38. package/src/slots/thread-view.tsx +23 -13
  39. package/src/slots/user-panel.tsx +19 -14
  40. package/src/slots/who-is-online.tsx +35 -19
  41. package/src/theme.ts +90 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meith/theme-default",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Meith's default theme — every slot and every token, and the reference for what a complete theme fills.",
5
5
  "license": "LGPL-3.0-or-later",
6
6
  "repository": {
@@ -20,8 +20,8 @@
20
20
  "access": "public"
21
21
  },
22
22
  "dependencies": {
23
- "@meith/theme-kit": "^0.6.0",
24
- "@meith/ui": "^0.6.0"
23
+ "@meith/theme-kit": "^0.8.0",
24
+ "@meith/ui": "^0.8.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": "^19.2.0"
package/src/copy.ts ADDED
@@ -0,0 +1,316 @@
1
+ import type { SlotCopy, Translator } from '@meith/theme-kit'
2
+
3
+ function copyFor(t: Translator, keys: readonly string[]): SlotCopy {
4
+ const copy: Record<string, string> = {}
5
+ for (const key of keys) copy[key] = t.t(key)
6
+ return copy
7
+ }
8
+
9
+ export function whoIsOnlineCopy(t: Translator): SlotCopy {
10
+ return copyFor(t, [
11
+ 'default.whoIsOnline.heading',
12
+ 'default.whoIsOnline.online',
13
+ 'default.whoIsOnline.member.one',
14
+ 'default.whoIsOnline.member.other',
15
+ 'default.whoIsOnline.guest.one',
16
+ 'default.whoIsOnline.guest.other',
17
+ 'default.whoIsOnline.nobody',
18
+ 'default.whoIsOnline.onlyGuests',
19
+ 'default.whoIsOnline.and',
20
+ 'default.whoIsOnline.more',
21
+ 'default.whoIsOnline.seeEveryone',
22
+ 'default.whoIsOnline.record',
23
+ 'default.whoIsOnline.invisible',
24
+ ])
25
+ }
26
+
27
+ export function announcementCopy(t: Translator): SlotCopy {
28
+ return copyFor(t, ['default.announcement.posted', 'default.announcement.postedBy'])
29
+ }
30
+
31
+ export function boardIndexCopy(t: Translator): SlotCopy {
32
+ return copyFor(t, ['default.boardIndex.boardActivity', 'default.boardIndex.markAllRead'])
33
+ }
34
+
35
+ export function boardStatsCopy(t: Translator): SlotCopy {
36
+ return copyFor(t, [
37
+ 'default.boardStats.counted',
38
+ 'default.boardStats.heading',
39
+ 'default.boardStats.members',
40
+ 'default.boardStats.newestMember',
41
+ 'default.boardStats.notComputed',
42
+ 'default.boardStats.posts',
43
+ 'default.boardStats.threads',
44
+ ])
45
+ }
46
+
47
+ export function discoveryViewCopy(t: Translator): SlotCopy {
48
+ return copyFor(t, [
49
+ 'default.discoveryView.by',
50
+ 'default.discoveryView.dot',
51
+ 'default.discoveryView.in',
52
+ 'default.discoveryView.lastPost',
53
+ 'default.discoveryView.nextArrow',
54
+ 'default.discoveryView.reply.one',
55
+ 'default.discoveryView.reply.other',
56
+ 'default.discoveryView.startedBy',
57
+ ])
58
+ }
59
+
60
+ export function errorNoticeCopy(t: Translator): SlotCopy {
61
+ return copyFor(t, [
62
+ 'default.errorNotice.error',
63
+ 'default.errorNotice.forumHome',
64
+ 'default.errorNotice.quoteThis',
65
+ ])
66
+ }
67
+
68
+ export function footerCopy(t: Translator): SlotCopy {
69
+ return copyFor(t, ['default.footer.nav', 'default.footer.timesShownIn'])
70
+ }
71
+
72
+ export function forumDisplayCopy(t: Translator): SlotCopy {
73
+ return copyFor(t, [
74
+ 'default.forumDisplay.emptyNoThread',
75
+ 'default.forumDisplay.emptyNoThreadFirst',
76
+ 'default.forumDisplay.markRead',
77
+ 'default.forumDisplay.newThread',
78
+ 'default.forumDisplay.noThreadsYet',
79
+ 'default.forumDisplay.post.one',
80
+ 'default.forumDisplay.post.other',
81
+ 'default.forumDisplay.postsLabel',
82
+ 'default.forumDisplay.startFirstThread',
83
+ 'default.forumDisplay.thread.one',
84
+ 'default.forumDisplay.thread.other',
85
+ 'default.forumDisplay.threadsLabel',
86
+ ])
87
+ }
88
+
89
+ export function forumRowCopy(t: Translator): SlotCopy {
90
+ return copyFor(t, [
91
+ 'default.forumRow.by',
92
+ 'default.forumRow.dot',
93
+ 'default.forumRow.newPosts',
94
+ 'default.forumRow.noPostsYet',
95
+ 'default.forumRow.post.one',
96
+ 'default.forumRow.post.other',
97
+ 'default.forumRow.postsLabel',
98
+ 'default.forumRow.subforums',
99
+ 'default.forumRow.thread.one',
100
+ 'default.forumRow.thread.other',
101
+ 'default.forumRow.threadsLabel',
102
+ ])
103
+ }
104
+
105
+ export function headerCopy(t: Translator): SlotCopy {
106
+ return copyFor(t, ['default.header.sections'])
107
+ }
108
+
109
+ export function latestPostsCopy(t: Translator): SlotCopy {
110
+ return copyFor(t, [
111
+ 'default.latestPosts.asOf',
112
+ 'default.latestPosts.dot',
113
+ 'default.latestPosts.emptyDescription',
114
+ 'default.latestPosts.heading',
115
+ 'default.latestPosts.in',
116
+ 'default.latestPosts.nothingYet',
117
+ ])
118
+ }
119
+
120
+ export function latestThreadsCopy(t: Translator): SlotCopy {
121
+ return copyFor(t, [
122
+ 'default.latestThreads.asOf',
123
+ 'default.latestThreads.dot',
124
+ 'default.latestThreads.emptyDescription',
125
+ 'default.latestThreads.heading',
126
+ 'default.latestThreads.in',
127
+ 'default.latestThreads.nothingYet',
128
+ 'default.latestThreads.reply.one',
129
+ 'default.latestThreads.reply.other',
130
+ ])
131
+ }
132
+
133
+ export function memberProfileCopy(t: Translator): SlotCopy {
134
+ return copyFor(t, [
135
+ 'default.memberProfile.about',
136
+ 'default.memberProfile.joinedLabel',
137
+ 'default.memberProfile.lastVisitLabel',
138
+ 'default.memberProfile.memberActions',
139
+ 'default.memberProfile.never',
140
+ 'default.memberProfile.postsLabel',
141
+ 'default.memberProfile.signature',
142
+ ])
143
+ }
144
+
145
+ export function navigationCopy(t: Translator): SlotCopy {
146
+ return copyFor(t, ['default.navigation.breadcrumb', 'default.navigation.separator'])
147
+ }
148
+
149
+ export function noticeCopy(t: Translator): SlotCopy {
150
+ return copyFor(t, [
151
+ 'default.notice.dismiss',
152
+ 'default.notice.error',
153
+ 'default.notice.info',
154
+ 'default.notice.success',
155
+ 'default.notice.warning',
156
+ ])
157
+ }
158
+
159
+ export function paginationCopy(t: Translator): SlotCopy {
160
+ return copyFor(t, [
161
+ 'default.pagination.ellipsis',
162
+ 'default.pagination.nav',
163
+ 'default.pagination.next',
164
+ 'default.pagination.of',
165
+ 'default.pagination.page',
166
+ 'default.pagination.previous',
167
+ ])
168
+ }
169
+
170
+ export function panelNavCopy(t: Translator): SlotCopy {
171
+ return copyFor(t, ['default.panelNav.countCap', 'default.panelNav.waiting'])
172
+ }
173
+
174
+ export function panelPageCopy(t: Translator): SlotCopy {
175
+ return copyFor(t, ['default.panelPage.back'])
176
+ }
177
+
178
+ export function postActionsCopy(t: Translator): SlotCopy {
179
+ return copyFor(t, [
180
+ 'default.postActions.edit',
181
+ 'default.postActions.moderate',
182
+ 'default.postActions.nav',
183
+ 'default.postActions.quote',
184
+ 'default.postActions.rate',
185
+ 'default.postActions.report',
186
+ 'default.postActions.restore',
187
+ 'default.postActions.warn',
188
+ ])
189
+ }
190
+
191
+ export function postBitCopy(t: Translator): SlotCopy {
192
+ return copyFor(t, [
193
+ 'default.postBit.attachment.one',
194
+ 'default.postBit.attachment.other',
195
+ 'default.postBit.deletedPost',
196
+ 'default.postBit.hiddenNotice',
197
+ 'default.postBit.ignoringPrefix',
198
+ 'default.postBit.joined',
199
+ 'default.postBit.online',
200
+ 'default.postBit.post.one',
201
+ 'default.postBit.post.other',
202
+ 'default.postBit.postsLabel',
203
+ 'default.postBit.reputation',
204
+ 'default.postBit.reputationLabel',
205
+ 'default.postBit.showAnyway',
206
+ 'default.postBit.staffOnly',
207
+ 'default.postBit.waitingApproval',
208
+ ])
209
+ }
210
+
211
+ export function postFormCopy(t: Translator): SlotCopy {
212
+ return copyFor(t, ['default.postForm.cannotPost'])
213
+ }
214
+
215
+ export function redirectNoticeCopy(t: Translator): SlotCopy {
216
+ return copyFor(t, [
217
+ 'default.redirectNotice.continueNow',
218
+ 'default.redirectNotice.continuingOnItsOwnIn',
219
+ 'default.redirectNotice.pleaseWait',
220
+ 'default.redirectNotice.redirecting',
221
+ 'default.redirectNotice.second.one',
222
+ 'default.redirectNotice.second.other',
223
+ ])
224
+ }
225
+
226
+ export function searchFormCopy(t: Translator): SlotCopy {
227
+ return copyFor(t, [
228
+ 'default.searchForm.in',
229
+ 'default.searchForm.noResults',
230
+ 'default.searchForm.placeholder',
231
+ 'default.searchForm.search',
232
+ 'default.searchForm.searchFor',
233
+ 'default.searchForm.sortBy',
234
+ ])
235
+ }
236
+
237
+ export function searchResultsCopy(t: Translator): SlotCopy {
238
+ return copyFor(t, [
239
+ 'default.searchResults.clearFilters',
240
+ 'default.searchResults.closeQuote',
241
+ 'default.searchResults.dot',
242
+ 'default.searchResults.nextArrow',
243
+ 'default.searchResults.nothingMatched',
244
+ 'default.searchResults.openQuote',
245
+ 'default.searchResults.removeFilter',
246
+ 'default.searchResults.removeFilterX',
247
+ 'default.searchResults.resultsFor',
248
+ 'default.searchResults.searched',
249
+ 'default.searchResults.searchedNote',
250
+ 'default.searchResults.startNewSearch',
251
+ 'default.searchResults.tryFewerWords',
252
+ ])
253
+ }
254
+
255
+ export function shellCopy(t: Translator): SlotCopy {
256
+ return copyFor(t, ['default.shell.skipToContent'])
257
+ }
258
+
259
+ export function subforumListCopy(t: Translator): SlotCopy {
260
+ return copyFor(t, [
261
+ 'default.subforumList.heading',
262
+ 'default.subforumList.post.one',
263
+ 'default.subforumList.post.other',
264
+ 'default.subforumList.postsLabel',
265
+ 'default.subforumList.thread.one',
266
+ 'default.subforumList.thread.other',
267
+ 'default.subforumList.threadsLabel',
268
+ ])
269
+ }
270
+
271
+ export function threadRowCopy(t: Translator): SlotCopy {
272
+ return copyFor(t, [
273
+ 'default.threadRow.by',
274
+ 'default.threadRow.dot',
275
+ 'default.threadRow.latestReply',
276
+ 'default.threadRow.locked',
277
+ 'default.threadRow.moved',
278
+ 'default.threadRow.newPosts',
279
+ 'default.threadRow.noRepliesYet',
280
+ 'default.threadRow.pinned',
281
+ 'default.threadRow.reply.one',
282
+ 'default.threadRow.reply.other',
283
+ 'default.threadRow.repliesLabel',
284
+ 'default.threadRow.startedBy',
285
+ 'default.threadRow.view.one',
286
+ 'default.threadRow.view.other',
287
+ 'default.threadRow.viewsLabel',
288
+ ])
289
+ }
290
+
291
+ export function threadViewCopy(t: Translator): SlotCopy {
292
+ return copyFor(t, [
293
+ 'default.threadView.locked',
294
+ 'default.threadView.markRead',
295
+ 'default.threadView.moved',
296
+ 'default.threadView.pinned',
297
+ 'default.threadView.reply.one',
298
+ 'default.threadView.reply.other',
299
+ 'default.threadView.replyAction',
300
+ 'default.threadView.repliesLabel',
301
+ 'default.threadView.view.one',
302
+ 'default.threadView.view.other',
303
+ 'default.threadView.viewsLabel',
304
+ ])
305
+ }
306
+
307
+ export function userPanelCopy(t: Translator): SlotCopy {
308
+ return copyFor(t, [
309
+ 'default.userPanel.new',
310
+ 'default.userPanel.signedIn',
311
+ 'default.userPanel.unread',
312
+ 'default.userPanel.unreadMessages',
313
+ 'default.userPanel.unreadNotifications',
314
+ 'default.userPanel.yourAccount',
315
+ ])
316
+ }
package/src/index.ts CHANGED
@@ -1,10 +1,10 @@
1
+ export { defaultMessages } from './messages'
2
+ export { defaultTheme } from './theme'
1
3
  export {
2
- TOKEN_NAMES,
3
- SCHEME_INDEPENDENT_TOKENS,
4
- LIGHT_TOKENS,
5
- DARK_TOKENS,
6
4
  BROWSER_THEME_COLOR,
5
+ DARK_TOKENS,
6
+ LIGHT_TOKENS,
7
+ SCHEME_INDEPENDENT_TOKENS,
8
+ TOKEN_NAMES,
7
9
  type TokenName,
8
10
  } from './tokens'
9
-
10
- export { defaultTheme } from './theme'
@@ -0,0 +1,189 @@
1
+ {
2
+ "default.announcement.posted": "Posted",
3
+ "default.announcement.postedBy": "Posted by",
4
+ "default.boardIndex.boardActivity": "Board activity",
5
+ "default.boardIndex.markAllRead": "Mark all forums read",
6
+ "default.boardStats.counted": "Counted",
7
+ "default.boardStats.heading": "Board statistics",
8
+ "default.boardStats.members": "members",
9
+ "default.boardStats.newestMember": "newest member",
10
+ "default.boardStats.notComputed": "The board’s totals are rolled up on a schedule, and the first run has not happened.",
11
+ "default.boardStats.posts": "posts",
12
+ "default.boardStats.threads": "threads",
13
+ "default.discoveryView.by": "by",
14
+ "default.discoveryView.dot": "·",
15
+ "default.discoveryView.in": "in",
16
+ "default.discoveryView.lastPost": "last post",
17
+ "default.discoveryView.nextArrow": "→",
18
+ "default.discoveryView.reply.one": "reply",
19
+ "default.discoveryView.reply.other": "replies",
20
+ "default.discoveryView.startedBy": "started by",
21
+ "default.errorNotice.error": "Error",
22
+ "default.errorNotice.forumHome": "Forum home",
23
+ "default.errorNotice.quoteThis": "Quote this if you report it:",
24
+ "default.footer.nav": "Footer",
25
+ "default.footer.timesShownIn": "Times are shown in",
26
+ "default.forumDisplay.emptyNoThread": "Nothing has been posted in this forum.",
27
+ "default.forumDisplay.emptyNoThreadFirst": "Nothing has been posted in this forum. Yours would be the first.",
28
+ "default.forumDisplay.markRead": "Mark read",
29
+ "default.forumDisplay.newThread": "New thread",
30
+ "default.forumDisplay.noThreadsYet": "No threads here yet",
31
+ "default.forumDisplay.post.one": "post",
32
+ "default.forumDisplay.post.other": "posts",
33
+ "default.forumDisplay.postsLabel": "Posts",
34
+ "default.forumDisplay.startFirstThread": "Start the first thread",
35
+ "default.forumDisplay.thread.one": "thread",
36
+ "default.forumDisplay.thread.other": "threads",
37
+ "default.forumDisplay.threadsLabel": "Threads",
38
+ "default.forumRow.by": "by",
39
+ "default.forumRow.dot": "·",
40
+ "default.forumRow.newPosts": "(new posts)",
41
+ "default.forumRow.noPostsYet": "No posts yet",
42
+ "default.forumRow.post.one": "post",
43
+ "default.forumRow.post.other": "posts",
44
+ "default.forumRow.postsLabel": "Posts",
45
+ "default.forumRow.subforums": "Subforums",
46
+ "default.forumRow.thread.one": "thread",
47
+ "default.forumRow.thread.other": "threads",
48
+ "default.forumRow.threadsLabel": "Threads",
49
+ "default.header.sections": "Board sections",
50
+ "default.latestPosts.asOf": "As of",
51
+ "default.latestPosts.dot": "·",
52
+ "default.latestPosts.emptyDescription": "The newest posts you can see will appear here.",
53
+ "default.latestPosts.heading": "Latest posts",
54
+ "default.latestPosts.in": "in",
55
+ "default.latestPosts.nothingYet": "Nothing said yet",
56
+ "default.latestThreads.asOf": "As of",
57
+ "default.latestThreads.dot": "·",
58
+ "default.latestThreads.emptyDescription": "The newest threads you can see will appear here.",
59
+ "default.latestThreads.heading": "Latest threads",
60
+ "default.latestThreads.in": "in",
61
+ "default.latestThreads.nothingYet": "Nothing started yet",
62
+ "default.latestThreads.reply.one": "reply",
63
+ "default.latestThreads.reply.other": "replies",
64
+ "default.memberProfile.about": "About",
65
+ "default.memberProfile.joinedLabel": "Joined",
66
+ "default.memberProfile.lastVisitLabel": "Last visit",
67
+ "default.memberProfile.memberActions": "Member actions",
68
+ "default.memberProfile.never": "Never",
69
+ "default.memberProfile.postsLabel": "Posts",
70
+ "default.memberProfile.signature": "Signature",
71
+ "default.navigation.breadcrumb": "Breadcrumb",
72
+ "default.navigation.separator": "/",
73
+ "default.notice.dismiss": "Dismiss",
74
+ "default.notice.error": "Error:",
75
+ "default.notice.info": "Notice:",
76
+ "default.notice.success": "Done:",
77
+ "default.notice.warning": "Warning:",
78
+ "default.pagination.ellipsis": "…",
79
+ "default.pagination.nav": "Pagination",
80
+ "default.pagination.next": "Next",
81
+ "default.pagination.of": "of",
82
+ "default.pagination.page": "Page",
83
+ "default.pagination.previous": "Previous",
84
+ "default.panelNav.countCap": "99+",
85
+ "default.panelNav.waiting": "waiting",
86
+ "default.panelPage.back": "←",
87
+ "default.postActions.edit": "Edit",
88
+ "default.postActions.moderate": "Moderate",
89
+ "default.postActions.nav": "Post actions",
90
+ "default.postActions.quote": "Quote",
91
+ "default.postActions.rate": "Rate",
92
+ "default.postActions.report": "Report",
93
+ "default.postActions.restore": "Restore",
94
+ "default.postActions.warn": "Warn",
95
+ "default.postBit.attachment.one": "attachment",
96
+ "default.postBit.attachment.other": "attachments",
97
+ "default.postBit.deletedPost": "Deleted post.",
98
+ "default.postBit.hiddenNotice": "This post is hidden.",
99
+ "default.postBit.ignoringPrefix": "You are ignoring",
100
+ "default.postBit.joined": "Joined",
101
+ "default.postBit.online": "Online",
102
+ "default.postBit.post.one": "post",
103
+ "default.postBit.post.other": "posts",
104
+ "default.postBit.postsLabel": "Posts",
105
+ "default.postBit.reputation": "reputation",
106
+ "default.postBit.reputationLabel": "Reputation",
107
+ "default.postBit.showAnyway": "Show it anyway",
108
+ "default.postBit.staffOnly": "Only moderators can see this.",
109
+ "default.postBit.waitingApproval": "Waiting for approval.",
110
+ "default.postForm.cannotPost": "Cannot post.",
111
+ "default.redirectNotice.continueNow": "Continue now",
112
+ "default.redirectNotice.continuingOnItsOwnIn": "Continuing on its own in",
113
+ "default.redirectNotice.pleaseWait": "Please wait",
114
+ "default.redirectNotice.redirecting": "Redirecting",
115
+ "default.redirectNotice.second.one": "second",
116
+ "default.redirectNotice.second.other": "seconds",
117
+ "default.searchForm.in": "In",
118
+ "default.searchForm.noResults": "No results.",
119
+ "default.searchForm.placeholder": "Words in a subject or a post",
120
+ "default.searchForm.search": "Search",
121
+ "default.searchForm.searchFor": "Search for",
122
+ "default.searchForm.sortBy": "Sort by",
123
+ "default.searchResults.clearFilters": "Clear filters",
124
+ "default.searchResults.closeQuote": "”",
125
+ "default.searchResults.dot": "·",
126
+ "default.searchResults.nextArrow": "→",
127
+ "default.searchResults.nothingMatched": "Nothing matched.",
128
+ "default.searchResults.openQuote": "“",
129
+ "default.searchResults.removeFilter": "— remove this filter",
130
+ "default.searchResults.removeFilterX": "×",
131
+ "default.searchResults.resultsFor": "Results for",
132
+ "default.searchResults.searched": "Searched",
133
+ "default.searchResults.searchedNote": "Results are checked against your access every time this page is opened, so they can change.",
134
+ "default.searchResults.startNewSearch": "Start a new search",
135
+ "default.searchResults.tryFewerWords": "Or nothing you can see does. Try fewer words, or a different spelling.",
136
+ "default.shell.skipToContent": "Skip to content",
137
+ "default.subforumList.heading": "Subforums",
138
+ "default.subforumList.post.one": "post",
139
+ "default.subforumList.post.other": "posts",
140
+ "default.subforumList.postsLabel": "Posts",
141
+ "default.subforumList.thread.one": "thread",
142
+ "default.subforumList.thread.other": "threads",
143
+ "default.subforumList.threadsLabel": "Threads",
144
+ "default.threadRow.by": "by",
145
+ "default.threadRow.dot": "·",
146
+ "default.threadRow.latestReply": "Latest reply",
147
+ "default.threadRow.locked": "Locked",
148
+ "default.threadRow.moved": "Moved",
149
+ "default.threadRow.newPosts": "(new posts)",
150
+ "default.threadRow.noRepliesYet": "No replies yet",
151
+ "default.threadRow.pinned": "Pinned",
152
+ "default.threadRow.repliesLabel": "Replies",
153
+ "default.threadRow.reply.one": "reply",
154
+ "default.threadRow.reply.other": "replies",
155
+ "default.threadRow.startedBy": "Started by",
156
+ "default.threadRow.view.one": "view",
157
+ "default.threadRow.view.other": "views",
158
+ "default.threadRow.viewsLabel": "Views",
159
+ "default.threadView.locked": "Locked — no new replies",
160
+ "default.threadView.markRead": "Mark read",
161
+ "default.threadView.moved": "Moved",
162
+ "default.threadView.pinned": "Pinned",
163
+ "default.threadView.repliesLabel": "Replies",
164
+ "default.threadView.reply.one": "reply",
165
+ "default.threadView.reply.other": "replies",
166
+ "default.threadView.replyAction": "Reply",
167
+ "default.threadView.view.one": "view",
168
+ "default.threadView.view.other": "views",
169
+ "default.threadView.viewsLabel": "Views",
170
+ "default.userPanel.new": "new",
171
+ "default.userPanel.signedIn": "Signed in",
172
+ "default.userPanel.unread": "unread",
173
+ "default.userPanel.unreadMessages": "unread messages",
174
+ "default.userPanel.unreadNotifications": "unread notifications",
175
+ "default.userPanel.yourAccount": "Your account",
176
+ "default.whoIsOnline.and": "and",
177
+ "default.whoIsOnline.guest.one": "guest",
178
+ "default.whoIsOnline.guest.other": "guests",
179
+ "default.whoIsOnline.heading": "Who’s online",
180
+ "default.whoIsOnline.invisible": "Invisible",
181
+ "default.whoIsOnline.member.one": "member",
182
+ "default.whoIsOnline.member.other": "members",
183
+ "default.whoIsOnline.more": "more",
184
+ "default.whoIsOnline.nobody": "Nobody is reading the board right now.",
185
+ "default.whoIsOnline.online": "online —",
186
+ "default.whoIsOnline.onlyGuests": "Only guests are reading the board right now.",
187
+ "default.whoIsOnline.record": "Record:",
188
+ "default.whoIsOnline.seeEveryone": "See everyone online"
189
+ }
@@ -0,0 +1,5 @@
1
+ import type { CatalogBundle } from '@meith/theme-kit'
2
+
3
+ import en from './en.json'
4
+
5
+ export const defaultMessages: CatalogBundle = { en }
@@ -1,7 +1,6 @@
1
1
  import type { PanelNavIcon } from '@meith/theme-kit'
2
2
 
3
3
  const SHARED = {
4
- 'aria-hidden': true,
5
4
  viewBox: '0 0 16 16',
6
5
  fill: 'none',
7
6
  stroke: 'currentColor',
@@ -57,7 +56,7 @@ export function PanelIcon({ icon }: { icon: PanelNavIcon }) {
57
56
  if (path === undefined) return null
58
57
 
59
58
  return (
60
- <svg {...SHARED}>
59
+ <svg {...SHARED} aria-hidden="true">
61
60
  <path d={path} />
62
61
  </svg>
63
62
  )
package/src/shared.tsx CHANGED
@@ -1,5 +1,5 @@
1
+ import type { CountModel, PrefixModel, TimeModel, UserRefModel } from '@meith/theme-kit'
1
2
  import { Badge, cn } from '@meith/ui'
2
- import type { PrefixModel, TimeModel, UserRefModel } from '@meith/theme-kit'
3
3
 
4
4
  const PAGE_WIDTH = 'max-w-6xl'
5
5
 
@@ -78,7 +78,7 @@ export function Prefix({ prefix }: { prefix: PrefixModel }) {
78
78
 
79
79
  export interface CountItem {
80
80
  readonly label: string
81
- readonly value: number
81
+ readonly value: CountModel
82
82
  readonly one: string
83
83
  readonly many: string
84
84
  }
@@ -90,10 +90,8 @@ export function Counts({ items, className }: { items: readonly CountItem[]; clas
90
90
  <div key={item.label}>
91
91
  <dt className="sr-only">{item.label}</dt>
92
92
  <dd>
93
- <span className={cn('font-medium text-foreground', NUMERIC)}>
94
- {item.value.toLocaleString('en')}
95
- </span>{' '}
96
- {item.value === 1 ? item.one : item.many}
93
+ <span className={cn('font-medium text-foreground', NUMERIC)}>{item.value.label}</span>{' '}
94
+ {item.value.value === 1 ? item.one : item.many}
97
95
  </dd>
98
96
  </div>
99
97
  ))}
@@ -1,28 +1,34 @@
1
+ import type { AnnouncementModel, SlotCopy } from '@meith/theme-kit'
2
+ import { fromSlotCopy } from '@meith/theme-kit'
1
3
  import { Card, CardContent, CardFooter } from '@meith/ui'
2
- import type { AnnouncementModel } from '@meith/theme-kit'
3
4
 
4
5
  import { MUTED_LINK, Stamp, UserRef } from '../shared'
5
6
 
6
- export function Announcement({ title, bodyHtml, postedBy, postedAt, forum }: AnnouncementModel) {
7
+ export function Announcement({
8
+ title,
9
+ bodyHtml,
10
+ postedBy,
11
+ postedAt,
12
+ forum,
13
+ copy,
14
+ }: AnnouncementModel & { copy: SlotCopy }) {
15
+ const c = (key: string) => fromSlotCopy(copy, `default.announcement.${key}`)
16
+
7
17
  return (
8
18
  <Card as="article" className="border-l-4 border-l-foreground">
9
19
  <CardContent className="flex flex-col gap-2 p-4">
10
20
  <h2 className="text-lg font-semibold tracking-tight text-balance">{title}</h2>
11
21
 
12
- <div
13
- className="prose-md text-sm"
14
- dangerouslySetInnerHTML={{ __html: bodyHtml }}
15
- />
22
+ <div className="prose-md text-sm" dangerouslySetInnerHTML={{ __html: bodyHtml }} />
16
23
  </CardContent>
17
24
 
18
25
  <CardFooter>
19
26
  <span>
20
27
  {postedBy === null ? (
21
- 'Posted'
28
+ c('posted')
22
29
  ) : (
23
30
  <>
24
- {'Posted by '}
25
- <UserRef user={postedBy} className="text-foreground" />
31
+ {c('postedBy')} <UserRef user={postedBy} className="text-foreground" />
26
32
  </>
27
33
  )}{' '}
28
34
  <Stamp at={postedAt} />
@@ -39,7 +39,9 @@ export function AuthPage({ title, alert, links, regions }: AuthPageModel) {
39
39
  {link.lead === null ? null : `${link.lead} `}
40
40
  <a
41
41
  href={link.href}
42
- className={link.lead === null ? MUTED_LINK : `font-medium text-foreground ${MUTED_LINK}`}
42
+ className={
43
+ link.lead === null ? MUTED_LINK : `font-medium text-foreground ${MUTED_LINK}`
44
+ }
43
45
  >
44
46
  {link.label}
45
47
  </a>