@meith/web 0.25.0 → 0.26.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.
- package/app/(board)/member/[id]/reputation/page.tsx +2 -2
- package/app/(board)/messages/[id]/page.tsx +28 -20
- package/app/(board)/messages/page.tsx +2 -2
- package/app/(board)/notifications/page.tsx +3 -1
- package/app/(board)/subscriptions/page.tsx +6 -2
- package/app/(board)/usercp/contacts/page.tsx +3 -7
- package/app/admin/api-tokens/page.tsx +3 -2
- package/app/admin/forums/[id]/page.tsx +25 -22
- package/app/admin/groups/memberships/page.tsx +6 -6
- package/app/admin/groups/promotions/page.tsx +14 -16
- package/app/admin/log/page.tsx +5 -3
- package/app/admin/plugins/page.tsx +1 -1
- package/app/admin/security/page.tsx +4 -3
- package/app/admin/system/page.tsx +13 -10
- package/app/admin/themes/page.tsx +1 -1
- package/bin/forum-web.mjs +3 -3
- package/bin/forum-web.test.ts +2 -2
- package/package.json +48 -48
- package/src/components/account/active-sessions.tsx +9 -6
- package/src/components/account/credential-proof-form.tsx +6 -7
- package/src/components/account/logout-form.tsx +5 -3
- package/src/components/account/notification-forms.tsx +9 -9
- package/src/components/account/relation-forms.tsx +5 -5
- package/src/components/account/reputation-forms.tsx +10 -10
- package/src/components/account/sign-in-methods.tsx +12 -6
- package/src/components/account/subscription-forms.tsx +7 -7
- package/src/components/account/two-factor-forms.tsx +12 -12
- package/src/components/account/usercp-forms.tsx +13 -13
- package/src/components/admin/admin-forms.tsx +7 -7
- package/src/components/admin/badge-forms.tsx +3 -3
- package/src/components/admin/ban-filter-forms.tsx +4 -4
- package/src/components/admin/branding-forms.tsx +3 -3
- package/src/components/admin/content-forms.tsx +15 -15
- package/src/components/admin/forum-forms.tsx +7 -7
- package/src/components/admin/group-forms.tsx +5 -5
- package/src/components/admin/mail-test-card.tsx +3 -3
- package/src/components/admin/marketplace-forms.tsx +4 -4
- package/src/components/admin/navigation-forms.tsx +3 -3
- package/src/components/admin/plugin-forms.tsx +7 -7
- package/src/components/admin/settings-form.tsx +5 -2
- package/src/components/admin/theme-forms.tsx +7 -10
- package/src/components/auth/form-controls.tsx +23 -1
- package/src/components/auth/second-factor-form.tsx +4 -4
- package/src/components/content/composer-intents.tsx +7 -6
- package/src/components/content/edit-post-form.tsx +7 -7
- package/src/components/content/poll-edit-form.tsx +3 -1
- package/src/components/content/poll.tsx +4 -2
- package/src/components/content/thanks-button.tsx +3 -3
- package/src/components/content/thread-rating.tsx +3 -3
- package/src/components/messages/message-forms.tsx +15 -15
- package/src/components/moderation/inline-moderation-form.tsx +19 -20
- package/src/components/moderation/queue-form.tsx +5 -7
- package/src/components/moderation/report-forms.tsx +8 -10
- package/src/components/moderation/signature-lock-form.tsx +5 -5
- package/src/components/moderation/thread-surgery-form.tsx +8 -5
- package/src/components/moderation/thread-tools-form.tsx +11 -22
- package/src/components/moderation/warning-forms.tsx +6 -6
- package/src/components/shell/onboarding-banner.tsx +6 -2
- package/src/components/shell/theme-switcher.tsx +6 -6
- package/src/components/shell/view-tabs.tsx +12 -7
- package/src/server/theme.ts +1 -1
- package/src/server/upgrade-notice.ts +1 -1
|
@@ -5,7 +5,7 @@ import { useActionState } from 'react'
|
|
|
5
5
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
6
6
|
import { messageBulkAction, sendMessageAction } from '@/server/message-actions'
|
|
7
7
|
|
|
8
|
-
import { FormError } from '../auth/form-controls'
|
|
8
|
+
import { FormError, PendingButton } from '../auth/form-controls'
|
|
9
9
|
import { MarkdownEditor } from '../content/markdown-editor'
|
|
10
10
|
import { type Copy, fromCopy } from '../shell/copy'
|
|
11
11
|
|
|
@@ -89,9 +89,9 @@ export function ComposeForm({
|
|
|
89
89
|
</label>
|
|
90
90
|
|
|
91
91
|
<div>
|
|
92
|
-
<
|
|
92
|
+
<PendingButton className={BUTTON} showWorking>
|
|
93
93
|
{fromCopy(copy, 'messageForm.send')}
|
|
94
|
-
</
|
|
94
|
+
</PendingButton>
|
|
95
95
|
</div>
|
|
96
96
|
</form>
|
|
97
97
|
)
|
|
@@ -120,31 +120,31 @@ export function MessageActionBar({
|
|
|
120
120
|
|
|
121
121
|
{folder === 'inbox' && (
|
|
122
122
|
<>
|
|
123
|
-
<
|
|
123
|
+
<PendingButton name="command" value="read" className={SECONDARY}>
|
|
124
124
|
{fromCopy(copy, 'messageForm.markRead')}
|
|
125
|
-
</
|
|
126
|
-
<
|
|
125
|
+
</PendingButton>
|
|
126
|
+
<PendingButton name="command" value="unread" className={SECONDARY}>
|
|
127
127
|
{fromCopy(copy, 'messageForm.markUnread')}
|
|
128
|
-
</
|
|
128
|
+
</PendingButton>
|
|
129
129
|
</>
|
|
130
130
|
)}
|
|
131
131
|
|
|
132
132
|
{folder === 'trash' ? (
|
|
133
133
|
<>
|
|
134
|
-
<
|
|
134
|
+
<PendingButton name="command" value="restore" className={SECONDARY}>
|
|
135
135
|
{fromCopy(copy, 'messageForm.restore')}
|
|
136
|
-
</
|
|
137
|
-
<
|
|
136
|
+
</PendingButton>
|
|
137
|
+
<PendingButton name="command" value="delete" className={SECONDARY}>
|
|
138
138
|
{fromCopy(copy, 'messageForm.deleteForever')}
|
|
139
|
-
</
|
|
140
|
-
<
|
|
139
|
+
</PendingButton>
|
|
140
|
+
<PendingButton name="command" value="empty" className={SECONDARY}>
|
|
141
141
|
{fromCopy(copy, 'messageForm.emptyTrash')}
|
|
142
|
-
</
|
|
142
|
+
</PendingButton>
|
|
143
143
|
</>
|
|
144
144
|
) : (
|
|
145
|
-
<
|
|
145
|
+
<PendingButton name="command" value="trash" className={SECONDARY}>
|
|
146
146
|
{fromCopy(copy, 'messageForm.moveToTrash')}
|
|
147
|
-
</
|
|
147
|
+
</PendingButton>
|
|
148
148
|
)}
|
|
149
149
|
</div>
|
|
150
150
|
</form>
|
|
@@ -7,7 +7,7 @@ import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
|
7
7
|
import { inlineModerateAction } from '@/server/inline-moderation-actions'
|
|
8
8
|
import { splitSelectedAction } from '@/server/surgery-actions'
|
|
9
9
|
|
|
10
|
-
import { FormError } from '../auth/form-controls'
|
|
10
|
+
import { FormError, PendingButton } from '../auth/form-controls'
|
|
11
11
|
import { type Copy, fromCopy } from '../shell/copy'
|
|
12
12
|
|
|
13
13
|
const BUTTON =
|
|
@@ -70,34 +70,34 @@ export function InlineModerationForm({
|
|
|
70
70
|
</span>
|
|
71
71
|
|
|
72
72
|
{rights.approve && (
|
|
73
|
-
<
|
|
73
|
+
<PendingButton name="tool" value="approve" className={BUTTON}>
|
|
74
74
|
{fromCopy(copy, 'moderationForm.inline.approve')}
|
|
75
|
-
</
|
|
75
|
+
</PendingButton>
|
|
76
76
|
)}
|
|
77
77
|
{rights.lock && scope === 'threads' && (
|
|
78
78
|
<>
|
|
79
|
-
<
|
|
79
|
+
<PendingButton name="tool" value="lock" className={BUTTON}>
|
|
80
80
|
{fromCopy(copy, 'moderationForm.tool.lock')}
|
|
81
|
-
</
|
|
82
|
-
<
|
|
81
|
+
</PendingButton>
|
|
82
|
+
<PendingButton name="tool" value="unlock" className={BUTTON}>
|
|
83
83
|
{fromCopy(copy, 'moderationForm.tool.unlock')}
|
|
84
|
-
</
|
|
84
|
+
</PendingButton>
|
|
85
85
|
</>
|
|
86
86
|
)}
|
|
87
87
|
{rights.stick && scope === 'threads' && (
|
|
88
88
|
<>
|
|
89
|
-
<
|
|
89
|
+
<PendingButton name="tool" value="stick" className={BUTTON}>
|
|
90
90
|
{fromCopy(copy, 'moderationForm.tool.pin')}
|
|
91
|
-
</
|
|
92
|
-
<
|
|
91
|
+
</PendingButton>
|
|
92
|
+
<PendingButton name="tool" value="unstick" className={BUTTON}>
|
|
93
93
|
{fromCopy(copy, 'moderationForm.tool.unpin')}
|
|
94
|
-
</
|
|
94
|
+
</PendingButton>
|
|
95
95
|
</>
|
|
96
96
|
)}
|
|
97
97
|
{rights.restore && (
|
|
98
|
-
<
|
|
98
|
+
<PendingButton name="tool" value="restore" className={BUTTON}>
|
|
99
99
|
{fromCopy(copy, 'moderationForm.inline.restore')}
|
|
100
|
-
</
|
|
100
|
+
</PendingButton>
|
|
101
101
|
)}
|
|
102
102
|
|
|
103
103
|
{rights.move && scope === 'threads' && moveTargets.length > 0 && (
|
|
@@ -115,9 +115,9 @@ export function InlineModerationForm({
|
|
|
115
115
|
))}
|
|
116
116
|
</select>
|
|
117
117
|
</label>
|
|
118
|
-
<
|
|
118
|
+
<PendingButton name="tool" value="move" className={BUTTON}>
|
|
119
119
|
{fromCopy(copy, 'moderationForm.tool.move')}
|
|
120
|
-
</
|
|
120
|
+
</PendingButton>
|
|
121
121
|
</span>
|
|
122
122
|
)}
|
|
123
123
|
|
|
@@ -136,21 +136,20 @@ export function InlineModerationForm({
|
|
|
136
136
|
className="h-8 w-48 rounded-md border border-border bg-background px-2 text-xs focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
137
137
|
/>
|
|
138
138
|
</label>
|
|
139
|
-
<
|
|
139
|
+
<PendingButton formAction={splitAction} className={BUTTON}>
|
|
140
140
|
{fromCopy(copy, 'moderationForm.inline.splitOut')}
|
|
141
|
-
</
|
|
141
|
+
</PendingButton>
|
|
142
142
|
</span>
|
|
143
143
|
)}
|
|
144
144
|
|
|
145
145
|
{rights.delete && (
|
|
146
|
-
<
|
|
147
|
-
type="submit"
|
|
146
|
+
<PendingButton
|
|
148
147
|
name="tool"
|
|
149
148
|
value="delete"
|
|
150
149
|
className={`${BUTTON} border-destructive/40 text-destructive`}
|
|
151
150
|
>
|
|
152
151
|
{fromCopy(copy, 'moderationForm.inline.delete')}
|
|
153
|
-
</
|
|
152
|
+
</PendingButton>
|
|
154
153
|
)}
|
|
155
154
|
</form>
|
|
156
155
|
</div>
|
|
@@ -5,7 +5,7 @@ import { useActionState } from 'react'
|
|
|
5
5
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
6
6
|
import { moderateQueueAction } from '@/server/moderation-actions'
|
|
7
7
|
|
|
8
|
-
import { FormError } from '../auth/form-controls'
|
|
8
|
+
import { FormError, PendingButton } from '../auth/form-controls'
|
|
9
9
|
import { type Copy, formatFromCopy, fromCopy } from '../shell/copy'
|
|
10
10
|
|
|
11
11
|
export interface QueueFormRow {
|
|
@@ -74,22 +74,20 @@ export function QueueForm({ rows, copy }: { rows: readonly QueueFormRow[]; copy:
|
|
|
74
74
|
</ul>
|
|
75
75
|
|
|
76
76
|
<div className="flex flex-wrap gap-3">
|
|
77
|
-
<
|
|
78
|
-
type="submit"
|
|
77
|
+
<PendingButton
|
|
79
78
|
name="decision"
|
|
80
79
|
value="approve"
|
|
81
80
|
className="inline-flex h-10 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
82
81
|
>
|
|
83
82
|
{fromCopy(copy, 'moderationForm.queue.approve')}
|
|
84
|
-
</
|
|
85
|
-
<
|
|
86
|
-
type="submit"
|
|
83
|
+
</PendingButton>
|
|
84
|
+
<PendingButton
|
|
87
85
|
name="decision"
|
|
88
86
|
value="reject"
|
|
89
87
|
className="inline-flex h-10 items-center justify-center rounded-md border border-destructive/40 px-4 text-sm font-medium text-destructive transition-opacity hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
90
88
|
>
|
|
91
89
|
{fromCopy(copy, 'moderationForm.queue.reject')}
|
|
92
|
-
</
|
|
90
|
+
</PendingButton>
|
|
93
91
|
</div>
|
|
94
92
|
</form>
|
|
95
93
|
)
|
|
@@ -7,7 +7,7 @@ import { Card, CardContent, Field, Textarea } from '@meith/ui'
|
|
|
7
7
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
8
8
|
import { assignReportAction, closeReportAction, fileReportAction } from '@/server/report-actions'
|
|
9
9
|
|
|
10
|
-
import { FormError, SubmitButton } from '../auth/form-controls'
|
|
10
|
+
import { FormError, PendingButton, SubmitButton } from '../auth/form-controls'
|
|
11
11
|
import { type Copy, fromCopy } from '../shell/copy'
|
|
12
12
|
|
|
13
13
|
export function ReportForm({
|
|
@@ -66,14 +66,14 @@ export function AssignReportForm({
|
|
|
66
66
|
<FormError message={state.error} />
|
|
67
67
|
<input type="hidden" name="reportId" value={reportId} />
|
|
68
68
|
<input type="hidden" name="take" value={mine ? '0' : '1'} />
|
|
69
|
-
<
|
|
70
|
-
|
|
69
|
+
<PendingButton
|
|
70
|
+
showWorking
|
|
71
71
|
className="text-xs font-medium text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
72
72
|
>
|
|
73
73
|
{mine
|
|
74
74
|
? fromCopy(copy, 'moderationForm.report.putBack')
|
|
75
75
|
: fromCopy(copy, 'moderationForm.report.take')}
|
|
76
|
-
</
|
|
76
|
+
</PendingButton>
|
|
77
77
|
</form>
|
|
78
78
|
)
|
|
79
79
|
}
|
|
@@ -98,22 +98,20 @@ export function CloseReportForm({ reportId, copy }: { reportId: number; copy: Co
|
|
|
98
98
|
/>
|
|
99
99
|
</label>
|
|
100
100
|
<div className="flex flex-wrap gap-2">
|
|
101
|
-
<
|
|
102
|
-
type="submit"
|
|
101
|
+
<PendingButton
|
|
103
102
|
name="status"
|
|
104
103
|
value="resolved"
|
|
105
104
|
className="inline-flex h-8 items-center rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
106
105
|
>
|
|
107
106
|
{fromCopy(copy, 'moderationForm.report.resolve')}
|
|
108
|
-
</
|
|
109
|
-
<
|
|
110
|
-
type="submit"
|
|
107
|
+
</PendingButton>
|
|
108
|
+
<PendingButton
|
|
111
109
|
name="status"
|
|
112
110
|
value="rejected"
|
|
113
111
|
className="inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
114
112
|
>
|
|
115
113
|
{fromCopy(copy, 'moderationForm.report.dismiss')}
|
|
116
|
-
</
|
|
114
|
+
</PendingButton>
|
|
117
115
|
</div>
|
|
118
116
|
</form>
|
|
119
117
|
)
|
|
@@ -5,7 +5,7 @@ import { useActionState } from 'react'
|
|
|
5
5
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
6
6
|
import { setAvatarLockAction, setSignatureLockAction } from '@/server/moderation-actions'
|
|
7
7
|
|
|
8
|
-
import { FormError } from '../auth/form-controls'
|
|
8
|
+
import { FormError, PendingButton } from '../auth/form-controls'
|
|
9
9
|
import { type Copy, fromCopy } from '../shell/copy'
|
|
10
10
|
|
|
11
11
|
const REASON_INPUT = 'h-8 rounded-md border border-border bg-background px-2 text-xs'
|
|
@@ -38,11 +38,11 @@ export function SignatureLockForm({
|
|
|
38
38
|
/>
|
|
39
39
|
)}
|
|
40
40
|
|
|
41
|
-
<
|
|
41
|
+
<PendingButton showWorking className={LOCK_BUTTON}>
|
|
42
42
|
{locked
|
|
43
43
|
? fromCopy(copy, 'moderationForm.lock.unlockSignature')
|
|
44
44
|
: fromCopy(copy, 'moderationForm.lock.signature')}
|
|
45
|
-
</
|
|
45
|
+
</PendingButton>
|
|
46
46
|
</form>
|
|
47
47
|
)
|
|
48
48
|
}
|
|
@@ -73,11 +73,11 @@ export function AvatarLockForm({
|
|
|
73
73
|
/>
|
|
74
74
|
)}
|
|
75
75
|
|
|
76
|
-
<
|
|
76
|
+
<PendingButton showWorking className={LOCK_BUTTON}>
|
|
77
77
|
{locked
|
|
78
78
|
? fromCopy(copy, 'moderationForm.lock.unlockAvatar')
|
|
79
79
|
: fromCopy(copy, 'moderationForm.lock.avatar')}
|
|
80
|
-
</
|
|
80
|
+
</PendingButton>
|
|
81
81
|
</form>
|
|
82
82
|
)
|
|
83
83
|
}
|
|
@@ -5,7 +5,7 @@ import { useActionState } from 'react'
|
|
|
5
5
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
6
6
|
import { mergeThreadAction, splitThreadAction } from '@/server/surgery-actions'
|
|
7
7
|
|
|
8
|
-
import { FormError } from '../auth/form-controls'
|
|
8
|
+
import { FormError, PendingButton } from '../auth/form-controls'
|
|
9
9
|
import { type Copy, formatFromCopy, fromCopy } from '../shell/copy'
|
|
10
10
|
|
|
11
11
|
const BUTTON =
|
|
@@ -64,9 +64,9 @@ export function ThreadSurgeryForm({
|
|
|
64
64
|
className={`${FIELD} w-48`}
|
|
65
65
|
/>
|
|
66
66
|
</label>
|
|
67
|
-
<
|
|
67
|
+
<PendingButton showWorking className={BUTTON}>
|
|
68
68
|
{fromCopy(copy, 'moderationForm.surgery.split')}
|
|
69
|
-
</
|
|
69
|
+
</PendingButton>
|
|
70
70
|
</div>
|
|
71
71
|
<FormError message={splitState.error} />
|
|
72
72
|
</form>
|
|
@@ -88,9 +88,12 @@ export function ThreadSurgeryForm({
|
|
|
88
88
|
className={`${FIELD} w-40`}
|
|
89
89
|
/>
|
|
90
90
|
</label>
|
|
91
|
-
<
|
|
91
|
+
<PendingButton
|
|
92
|
+
showWorking
|
|
93
|
+
className={`${BUTTON} border-destructive/40 text-destructive`}
|
|
94
|
+
>
|
|
92
95
|
{fromCopy(copy, 'moderationForm.surgery.merge')}
|
|
93
|
-
</
|
|
96
|
+
</PendingButton>
|
|
94
97
|
</div>
|
|
95
98
|
<FormError message={mergeState.error} />
|
|
96
99
|
</form>
|
|
@@ -5,7 +5,7 @@ import { useActionState } from 'react'
|
|
|
5
5
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
6
6
|
import { threadToolAction } from '@/server/thread-tool-actions'
|
|
7
7
|
|
|
8
|
-
import { FormError } from '../auth/form-controls'
|
|
8
|
+
import { FormError, PendingButton } from '../auth/form-controls'
|
|
9
9
|
import { type Copy, fromCopy } from '../shell/copy'
|
|
10
10
|
|
|
11
11
|
const BUTTON =
|
|
@@ -49,38 +49,27 @@ export function ThreadToolsForm({
|
|
|
49
49
|
<input type="hidden" name="threadId" value={threadId} />
|
|
50
50
|
|
|
51
51
|
{rights.lock && (
|
|
52
|
-
<
|
|
53
|
-
type="submit"
|
|
54
|
-
name="tool"
|
|
55
|
-
value={isLocked ? 'unlock' : 'lock'}
|
|
56
|
-
className={BUTTON}
|
|
57
|
-
>
|
|
52
|
+
<PendingButton name="tool" value={isLocked ? 'unlock' : 'lock'} className={BUTTON}>
|
|
58
53
|
{isLocked
|
|
59
54
|
? fromCopy(copy, 'moderationForm.tool.unlock')
|
|
60
55
|
: fromCopy(copy, 'moderationForm.tool.lock')}
|
|
61
|
-
</
|
|
56
|
+
</PendingButton>
|
|
62
57
|
)}
|
|
63
58
|
{rights.stick && (
|
|
64
|
-
<
|
|
65
|
-
type="submit"
|
|
66
|
-
name="tool"
|
|
67
|
-
value={isSticky ? 'unstick' : 'stick'}
|
|
68
|
-
className={BUTTON}
|
|
69
|
-
>
|
|
59
|
+
<PendingButton name="tool" value={isSticky ? 'unstick' : 'stick'} className={BUTTON}>
|
|
70
60
|
{isSticky
|
|
71
61
|
? fromCopy(copy, 'moderationForm.tool.unpin')
|
|
72
62
|
: fromCopy(copy, 'moderationForm.tool.pin')}
|
|
73
|
-
</
|
|
63
|
+
</PendingButton>
|
|
74
64
|
)}
|
|
75
65
|
{rights.delete && (
|
|
76
|
-
<
|
|
77
|
-
type="submit"
|
|
66
|
+
<PendingButton
|
|
78
67
|
name="tool"
|
|
79
68
|
value="delete"
|
|
80
69
|
className={`${BUTTON} border-destructive/40 text-destructive`}
|
|
81
70
|
>
|
|
82
71
|
{fromCopy(copy, 'moderationForm.tool.deleteThread')}
|
|
83
|
-
</
|
|
72
|
+
</PendingButton>
|
|
84
73
|
)}
|
|
85
74
|
|
|
86
75
|
{rights.move && moveTargets.length > 0 && (
|
|
@@ -98,12 +87,12 @@ export function ThreadToolsForm({
|
|
|
98
87
|
))}
|
|
99
88
|
</select>
|
|
100
89
|
</label>
|
|
101
|
-
<
|
|
90
|
+
<PendingButton name="tool" value="move" className={BUTTON}>
|
|
102
91
|
{fromCopy(copy, 'moderationForm.tool.move')}
|
|
103
|
-
</
|
|
104
|
-
<
|
|
92
|
+
</PendingButton>
|
|
93
|
+
<PendingButton name="tool" value="copy" className={BUTTON}>
|
|
105
94
|
{fromCopy(copy, 'moderationForm.tool.copy')}
|
|
106
|
-
</
|
|
95
|
+
</PendingButton>
|
|
107
96
|
</span>
|
|
108
97
|
)}
|
|
109
98
|
</form>
|
|
@@ -5,7 +5,7 @@ import { useActionState } from 'react'
|
|
|
5
5
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
6
6
|
import { issueWarningAction, revokeWarningAction } from '@/server/warning-actions'
|
|
7
7
|
|
|
8
|
-
import { FormError } from '../auth/form-controls'
|
|
8
|
+
import { FormError, PendingButton } from '../auth/form-controls'
|
|
9
9
|
import { type Copy, fromCopy } from '../shell/copy'
|
|
10
10
|
|
|
11
11
|
const FIELD =
|
|
@@ -80,9 +80,9 @@ export function IssueWarningForm({
|
|
|
80
80
|
</label>
|
|
81
81
|
|
|
82
82
|
<div>
|
|
83
|
-
<
|
|
83
|
+
<PendingButton showWorking className={BUTTON}>
|
|
84
84
|
{fromCopy(copy, 'moderationForm.warn.submit')}
|
|
85
|
-
</
|
|
85
|
+
</PendingButton>
|
|
86
86
|
</div>
|
|
87
87
|
</form>
|
|
88
88
|
)
|
|
@@ -114,12 +114,12 @@ export function RevokeWarningForm({
|
|
|
114
114
|
className="h-8 w-full rounded-md border border-border bg-background px-2 text-xs focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
115
115
|
/>
|
|
116
116
|
</label>
|
|
117
|
-
<
|
|
118
|
-
|
|
117
|
+
<PendingButton
|
|
118
|
+
showWorking
|
|
119
119
|
className="inline-flex h-8 shrink-0 items-center rounded-md border border-border px-3 text-xs font-medium hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
120
120
|
>
|
|
121
121
|
{fromCopy(copy, 'moderationForm.warn.revoke')}
|
|
122
|
-
</
|
|
122
|
+
</PendingButton>
|
|
123
123
|
</form>
|
|
124
124
|
)
|
|
125
125
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PendingButton } from '@/components/auth/form-controls'
|
|
1
2
|
import { BOARD_MEASURE } from '@/components/shell/measure'
|
|
2
3
|
import type { OnboardingNoticeModel } from '@/server/onboarding'
|
|
3
4
|
import { dismissOnboardingAction } from '@/server/onboarding-actions'
|
|
@@ -29,9 +30,12 @@ export function OnboardingBanner({
|
|
|
29
30
|
|
|
30
31
|
<form action={dismissOnboardingAction}>
|
|
31
32
|
<input type="hidden" name="panel" value={panel} />
|
|
32
|
-
<
|
|
33
|
+
<PendingButton
|
|
34
|
+
showWorking
|
|
35
|
+
className="text-xs text-muted-foreground hover:text-foreground"
|
|
36
|
+
>
|
|
33
37
|
{dismissLabel}
|
|
34
|
-
</
|
|
38
|
+
</PendingButton>
|
|
35
39
|
</form>
|
|
36
40
|
</div>
|
|
37
41
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PendingButton } from '@/components/auth/form-controls'
|
|
1
2
|
import { MonitorIcon, MoonIcon, SunIcon } from '@/components/shell/appearance-icons'
|
|
2
3
|
import { BOARD_MEASURE } from '@/components/shell/measure'
|
|
3
4
|
import { setAppearanceAction } from '@/server/appearance-actions'
|
|
@@ -50,12 +51,12 @@ export async function ThemeSwitcher() {
|
|
|
50
51
|
</option>
|
|
51
52
|
))}
|
|
52
53
|
</select>
|
|
53
|
-
<
|
|
54
|
-
|
|
54
|
+
<PendingButton
|
|
55
|
+
showWorking
|
|
55
56
|
className="h-8 rounded-md border border-border px-2.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
56
57
|
>
|
|
57
58
|
{t.t('appearance.apply')}
|
|
58
|
-
</
|
|
59
|
+
</PendingButton>
|
|
59
60
|
</form>
|
|
60
61
|
)}
|
|
61
62
|
|
|
@@ -70,9 +71,8 @@ export async function ThemeSwitcher() {
|
|
|
70
71
|
const label = t.t(SCHEME_LABEL_KEY[option])
|
|
71
72
|
const selected = scheme === option
|
|
72
73
|
return (
|
|
73
|
-
<
|
|
74
|
+
<PendingButton
|
|
74
75
|
key={option}
|
|
75
|
-
type="submit"
|
|
76
76
|
name="scheme"
|
|
77
77
|
value={option}
|
|
78
78
|
title={label}
|
|
@@ -85,7 +85,7 @@ export async function ThemeSwitcher() {
|
|
|
85
85
|
>
|
|
86
86
|
<Icon />
|
|
87
87
|
<span className="sr-only">{label}</span>
|
|
88
|
-
</
|
|
88
|
+
</PendingButton>
|
|
89
89
|
)
|
|
90
90
|
})}
|
|
91
91
|
</span>
|
|
@@ -21,27 +21,32 @@ export function ViewTabs({
|
|
|
21
21
|
if (tabs.length === 0) return null
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
|
-
<nav
|
|
25
|
-
|
|
24
|
+
<nav
|
|
25
|
+
aria-label={label}
|
|
26
|
+
className={cn('flex flex-wrap items-center gap-x-3 gap-y-2', className)}
|
|
27
|
+
>
|
|
28
|
+
<ul className="inline-flex max-w-full items-center gap-1 overflow-x-auto rounded-lg border border-border bg-surface p-1">
|
|
26
29
|
{tabs.map((tab) => (
|
|
27
30
|
<li key={tab.href} className="shrink-0">
|
|
28
31
|
<a
|
|
29
32
|
href={tab.href}
|
|
30
33
|
{...(tab.isCurrent ? { 'aria-current': 'page' as const } : {})}
|
|
31
34
|
className={cn(
|
|
32
|
-
'inline-flex h-
|
|
35
|
+
'inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-sm whitespace-nowrap transition-colors',
|
|
33
36
|
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',
|
|
34
37
|
tab.isCurrent
|
|
35
|
-
? 'bg-
|
|
36
|
-
: 'text-muted-foreground hover:
|
|
38
|
+
? 'bg-card font-semibold text-foreground shadow-sm'
|
|
39
|
+
: 'font-medium text-muted-foreground hover:text-foreground',
|
|
37
40
|
)}
|
|
38
41
|
>
|
|
39
42
|
{tab.label}
|
|
40
43
|
{tab.count !== undefined && tab.count > 0 && (
|
|
41
44
|
<span
|
|
42
45
|
className={cn(
|
|
43
|
-
'rounded
|
|
44
|
-
tab.isCurrent
|
|
46
|
+
'rounded px-1.5 text-xs font-semibold tabular-nums',
|
|
47
|
+
tab.isCurrent
|
|
48
|
+
? 'bg-secondary text-secondary-foreground'
|
|
49
|
+
: 'bg-muted text-muted-foreground',
|
|
45
50
|
)}
|
|
46
51
|
>
|
|
47
52
|
{tab.count}
|
package/src/server/theme.ts
CHANGED
|
@@ -25,7 +25,7 @@ export const buildTheme: ResolvedTheme = RESOLVED.get(forumConfig.defaultTheme)!
|
|
|
25
25
|
|
|
26
26
|
if (buildTheme === undefined) {
|
|
27
27
|
throw new Error(
|
|
28
|
-
`
|
|
28
|
+
`meith.config.ts: defaultTheme "${forumConfig.defaultTheme}" fills no slots, so this ` +
|
|
29
29
|
'board cannot render a page. A token-only theme is legitimate as an alternate, ' +
|
|
30
30
|
'never as the theme a build is made of.',
|
|
31
31
|
)
|
|
@@ -6,7 +6,7 @@ import { planUpgrade, type UpgradeState, upgradeNotice } from '@meith/upgrade'
|
|
|
6
6
|
|
|
7
7
|
import { activeDefinitions } from './plugin-host'
|
|
8
8
|
|
|
9
|
-
export const CODE_VERSION = '0.
|
|
9
|
+
export const CODE_VERSION = '0.26.0'
|
|
10
10
|
|
|
11
11
|
export async function pendingUpgradeNotice(): Promise<string | null> {
|
|
12
12
|
if (env.DATA_SOURCE !== 'postgres') return null
|