@jant/core 0.3.25 → 0.3.27
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/dist/app.js +70 -563
- package/dist/auth.js +3 -0
- package/dist/client.js +1 -0
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/lib/avatar-upload.js +134 -0
- package/dist/lib/config.js +39 -0
- package/dist/lib/constants.js +10 -10
- package/dist/lib/favicon.js +102 -0
- package/dist/lib/image.js +13 -17
- package/dist/lib/media-helpers.js +2 -2
- package/dist/lib/navigation.js +23 -3
- package/dist/lib/render.js +10 -1
- package/dist/lib/schemas.js +31 -0
- package/dist/lib/timezones.js +388 -0
- package/dist/lib/view.js +1 -1
- package/dist/routes/api/posts.js +1 -1
- package/dist/routes/api/upload.js +3 -3
- package/dist/routes/auth/reset.js +221 -0
- package/dist/routes/auth/setup.js +194 -0
- package/dist/routes/auth/signin.js +176 -0
- package/dist/routes/dash/collections.js +23 -415
- package/dist/routes/dash/media.js +12 -392
- package/dist/routes/dash/pages.js +7 -330
- package/dist/routes/dash/redirects.js +18 -12
- package/dist/routes/dash/settings.js +198 -577
- package/dist/routes/feed/rss.js +2 -1
- package/dist/routes/feed/sitemap.js +4 -2
- package/dist/routes/pages/featured.js +5 -1
- package/dist/routes/pages/home.js +26 -1
- package/dist/routes/pages/latest.js +45 -0
- package/dist/services/post.js +30 -50
- package/dist/types/bindings.js +3 -0
- package/dist/types/config.js +147 -0
- package/dist/types/constants.js +27 -0
- package/dist/types/entities.js +3 -0
- package/dist/types/operations.js +3 -0
- package/dist/types/props.js +3 -0
- package/dist/types/views.js +5 -0
- package/dist/types.js +8 -111
- package/dist/ui/color-themes.js +33 -33
- package/dist/ui/compose/ComposeDialog.js +36 -21
- package/dist/ui/dash/PageForm.js +21 -15
- package/dist/ui/dash/PostForm.js +22 -16
- package/dist/ui/dash/collections/CollectionForm.js +152 -0
- package/dist/ui/dash/collections/CollectionsListContent.js +68 -0
- package/dist/ui/dash/collections/ViewCollectionContent.js +96 -0
- package/dist/ui/dash/media/MediaListContent.js +166 -0
- package/dist/ui/dash/media/ViewMediaContent.js +212 -0
- package/dist/ui/dash/pages/LinkFormContent.js +130 -0
- package/dist/ui/dash/pages/UnifiedPagesContent.js +193 -0
- package/dist/ui/dash/settings/AccountContent.js +209 -0
- package/dist/ui/dash/settings/AppearanceContent.js +259 -0
- package/dist/ui/dash/settings/GeneralContent.js +536 -0
- package/dist/ui/dash/settings/SettingsNav.js +41 -0
- package/dist/ui/font-themes.js +36 -0
- package/dist/ui/layouts/BaseLayout.js +24 -2
- package/dist/ui/layouts/SiteLayout.js +47 -19
- package/package.json +1 -1
- package/src/app.tsx +95 -553
- package/src/auth.ts +4 -1
- package/src/client.ts +1 -0
- package/src/i18n/locales/en.po +240 -175
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +240 -175
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +240 -175
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/lib/__tests__/config.test.ts +192 -0
- package/src/lib/__tests__/favicon.test.ts +151 -0
- package/src/lib/__tests__/image.test.ts +2 -6
- package/src/lib/__tests__/timezones.test.ts +61 -0
- package/src/lib/__tests__/view.test.ts +2 -2
- package/src/lib/avatar-upload.ts +165 -0
- package/src/lib/config.ts +47 -0
- package/src/lib/constants.ts +19 -11
- package/src/lib/favicon.ts +115 -0
- package/src/lib/image.ts +13 -21
- package/src/lib/media-helpers.ts +2 -2
- package/src/lib/navigation.ts +33 -2
- package/src/lib/render.tsx +15 -1
- package/src/lib/schemas.ts +39 -0
- package/src/lib/timezones.ts +325 -0
- package/src/lib/view.ts +1 -1
- package/src/routes/api/posts.ts +1 -1
- package/src/routes/api/upload.ts +2 -3
- package/src/routes/auth/reset.tsx +239 -0
- package/src/routes/auth/setup.tsx +189 -0
- package/src/routes/auth/signin.tsx +163 -0
- package/src/routes/dash/__tests__/settings-avatar.test.ts +89 -0
- package/src/routes/dash/collections.tsx +17 -366
- package/src/routes/dash/media.tsx +12 -414
- package/src/routes/dash/pages.tsx +8 -348
- package/src/routes/dash/redirects.tsx +20 -14
- package/src/routes/dash/settings.tsx +243 -534
- package/src/routes/feed/__tests__/rss.test.ts +141 -0
- package/src/routes/feed/rss.ts +3 -1
- package/src/routes/feed/sitemap.ts +4 -2
- package/src/routes/pages/featured.tsx +7 -1
- package/src/routes/pages/home.tsx +25 -2
- package/src/routes/pages/latest.tsx +59 -0
- package/src/services/post.ts +34 -66
- package/src/styles/components.css +0 -65
- package/src/styles/tokens.css +1 -1
- package/src/styles/ui.css +24 -40
- package/src/types/bindings.ts +30 -0
- package/src/types/config.ts +183 -0
- package/src/types/constants.ts +26 -0
- package/src/types/entities.ts +109 -0
- package/src/types/operations.ts +88 -0
- package/src/types/props.ts +115 -0
- package/src/types/views.ts +172 -0
- package/src/types.ts +8 -644
- package/src/ui/__tests__/font-themes.test.ts +34 -0
- package/src/ui/color-themes.ts +34 -34
- package/src/ui/compose/ComposeDialog.tsx +40 -21
- package/src/ui/dash/PageForm.tsx +25 -19
- package/src/ui/dash/PostForm.tsx +26 -20
- package/src/ui/dash/collections/CollectionForm.tsx +153 -0
- package/src/ui/dash/collections/CollectionsListContent.tsx +85 -0
- package/src/ui/dash/collections/ViewCollectionContent.tsx +92 -0
- package/src/ui/dash/media/MediaListContent.tsx +201 -0
- package/src/ui/dash/media/ViewMediaContent.tsx +208 -0
- package/src/ui/dash/pages/LinkFormContent.tsx +119 -0
- package/src/ui/dash/pages/UnifiedPagesContent.tsx +203 -0
- package/src/ui/dash/settings/AccountContent.tsx +176 -0
- package/src/ui/dash/settings/AppearanceContent.tsx +254 -0
- package/src/ui/dash/settings/GeneralContent.tsx +533 -0
- package/src/ui/dash/settings/SettingsNav.tsx +56 -0
- package/src/ui/font-themes.ts +54 -0
- package/src/ui/layouts/BaseLayout.tsx +17 -0
- package/src/ui/layouts/SiteLayout.tsx +45 -31
|
@@ -1,593 +1,49 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Dashboard Settings Routes
|
|
4
4
|
*
|
|
5
5
|
* Sub-pages: General, Appearance, Account
|
|
6
6
|
*/ import { Hono } from "hono";
|
|
7
|
-
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
8
7
|
import { DashLayout } from "../../ui/layouts/DashLayout.js";
|
|
9
8
|
import { sse, dsRedirect, dsToast } from "../../lib/sse.js";
|
|
10
|
-
import {
|
|
9
|
+
import { arrayBufferToBase64 } from "../../lib/favicon.js";
|
|
10
|
+
import { getSiteLanguage, getSiteName, getHomeDefaultView, getTimeZone, getSiteFooter, isNoIndex, getConfigFallback } from "../../lib/config.js";
|
|
11
11
|
import { SETTINGS_KEYS } from "../../lib/constants.js";
|
|
12
12
|
import { getAvailableThemes } from "../../lib/theme.js";
|
|
13
|
+
import { getMediaUrl, getPublicUrlForProvider } from "../../lib/image.js";
|
|
14
|
+
import { TIMEZONES } from "../../lib/timezones.js";
|
|
15
|
+
import { BUILTIN_FONT_THEMES } from "../../ui/font-themes.js";
|
|
16
|
+
import { GeneralContent } from "../../ui/dash/settings/GeneralContent.js";
|
|
17
|
+
import { AppearanceContent } from "../../ui/dash/settings/AppearanceContent.js";
|
|
18
|
+
import { AccountContent } from "../../ui/dash/settings/AccountContent.js";
|
|
13
19
|
/** Escape HTML special characters for safe insertion into HTML strings */ function escapeHtml(str) {
|
|
14
20
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
15
21
|
}
|
|
16
22
|
export const settingsRoutes = new Hono();
|
|
17
|
-
function SettingsNav({ currentTab }) {
|
|
18
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
19
|
-
const tabs = [
|
|
20
|
-
{
|
|
21
|
-
id: "general",
|
|
22
|
-
label: $__i18n._({
|
|
23
|
-
id: "Weq9zb",
|
|
24
|
-
message: "General"
|
|
25
|
-
}),
|
|
26
|
-
href: "/dash/settings"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: "appearance",
|
|
30
|
-
label: $__i18n._({
|
|
31
|
-
id: "aAIQg2",
|
|
32
|
-
message: "Appearance"
|
|
33
|
-
}),
|
|
34
|
-
href: "/dash/settings/appearance"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
id: "account",
|
|
38
|
-
label: $__i18n._({
|
|
39
|
-
id: "AeXO77",
|
|
40
|
-
message: "Account"
|
|
41
|
-
}),
|
|
42
|
-
href: "/dash/settings/account"
|
|
43
|
-
}
|
|
44
|
-
];
|
|
45
|
-
return /*#__PURE__*/ _jsx("nav", {
|
|
46
|
-
class: "flex gap-1 mb-6",
|
|
47
|
-
children: tabs.map((tab)=>/*#__PURE__*/ _jsx("a", {
|
|
48
|
-
href: tab.href,
|
|
49
|
-
class: `px-3 py-2 text-sm rounded-md ${tab.id === currentTab ? "bg-accent text-accent-foreground font-medium" : "text-muted-foreground hover:bg-accent hover:text-accent-foreground"}`,
|
|
50
|
-
children: tab.label
|
|
51
|
-
}, tab.id))
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// General tab
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
function GeneralContent({ siteName, siteDescription, siteLanguage, siteNameFallback, siteDescriptionFallback }) {
|
|
58
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
59
|
-
const generalSignals = JSON.stringify({
|
|
60
|
-
siteName,
|
|
61
|
-
siteDescription,
|
|
62
|
-
siteLanguage
|
|
63
|
-
}).replace(/</g, "\\u003c");
|
|
64
|
-
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
65
|
-
children: [
|
|
66
|
-
/*#__PURE__*/ _jsx("h1", {
|
|
67
|
-
class: "text-2xl font-semibold mb-2",
|
|
68
|
-
children: $__i18n._({
|
|
69
|
-
id: "Tz0i8g",
|
|
70
|
-
message: "Settings"
|
|
71
|
-
})
|
|
72
|
-
}),
|
|
73
|
-
/*#__PURE__*/ _jsx(SettingsNav, {
|
|
74
|
-
currentTab: "general"
|
|
75
|
-
}),
|
|
76
|
-
/*#__PURE__*/ _jsx("div", {
|
|
77
|
-
class: "flex flex-col gap-6 max-w-lg",
|
|
78
|
-
children: /*#__PURE__*/ _jsxs("form", {
|
|
79
|
-
"data-signals": generalSignals,
|
|
80
|
-
"data-on:submit__prevent": "@post('/dash/settings')",
|
|
81
|
-
"data-indicator": "_loading",
|
|
82
|
-
children: [
|
|
83
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
84
|
-
class: "card",
|
|
85
|
-
children: [
|
|
86
|
-
/*#__PURE__*/ _jsx("header", {
|
|
87
|
-
children: /*#__PURE__*/ _jsx("h2", {
|
|
88
|
-
children: $__i18n._({
|
|
89
|
-
id: "Weq9zb",
|
|
90
|
-
message: "General"
|
|
91
|
-
})
|
|
92
|
-
})
|
|
93
|
-
}),
|
|
94
|
-
/*#__PURE__*/ _jsxs("section", {
|
|
95
|
-
class: "flex flex-col gap-4",
|
|
96
|
-
children: [
|
|
97
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
98
|
-
class: "field",
|
|
99
|
-
children: [
|
|
100
|
-
/*#__PURE__*/ _jsx("label", {
|
|
101
|
-
class: "label",
|
|
102
|
-
children: $__i18n._({
|
|
103
|
-
id: "SJmfuf",
|
|
104
|
-
message: "Site Name"
|
|
105
|
-
})
|
|
106
|
-
}),
|
|
107
|
-
/*#__PURE__*/ _jsx("input", {
|
|
108
|
-
type: "text",
|
|
109
|
-
"data-bind": "siteName",
|
|
110
|
-
class: "input",
|
|
111
|
-
placeholder: siteNameFallback
|
|
112
|
-
})
|
|
113
|
-
]
|
|
114
|
-
}),
|
|
115
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
116
|
-
class: "field",
|
|
117
|
-
children: [
|
|
118
|
-
/*#__PURE__*/ _jsx("label", {
|
|
119
|
-
class: "label",
|
|
120
|
-
children: $__i18n._({
|
|
121
|
-
id: "u2f7vd",
|
|
122
|
-
message: "Site Description"
|
|
123
|
-
})
|
|
124
|
-
}),
|
|
125
|
-
/*#__PURE__*/ _jsx("textarea", {
|
|
126
|
-
"data-bind": "siteDescription",
|
|
127
|
-
class: "textarea",
|
|
128
|
-
rows: 3,
|
|
129
|
-
placeholder: siteDescriptionFallback,
|
|
130
|
-
children: siteDescription
|
|
131
|
-
})
|
|
132
|
-
]
|
|
133
|
-
}),
|
|
134
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
135
|
-
class: "field",
|
|
136
|
-
children: [
|
|
137
|
-
/*#__PURE__*/ _jsx("label", {
|
|
138
|
-
class: "label",
|
|
139
|
-
children: $__i18n._({
|
|
140
|
-
id: "vXIe7J",
|
|
141
|
-
message: "Language"
|
|
142
|
-
})
|
|
143
|
-
}),
|
|
144
|
-
/*#__PURE__*/ _jsxs("select", {
|
|
145
|
-
"data-bind": "siteLanguage",
|
|
146
|
-
class: "select",
|
|
147
|
-
children: [
|
|
148
|
-
/*#__PURE__*/ _jsx("option", {
|
|
149
|
-
value: "en",
|
|
150
|
-
selected: siteLanguage === "en",
|
|
151
|
-
children: "English"
|
|
152
|
-
}),
|
|
153
|
-
/*#__PURE__*/ _jsx("option", {
|
|
154
|
-
value: "zh-Hans",
|
|
155
|
-
selected: siteLanguage === "zh-Hans",
|
|
156
|
-
children: "简体中文"
|
|
157
|
-
}),
|
|
158
|
-
/*#__PURE__*/ _jsx("option", {
|
|
159
|
-
value: "zh-Hant",
|
|
160
|
-
selected: siteLanguage === "zh-Hant",
|
|
161
|
-
children: "繁體中文"
|
|
162
|
-
})
|
|
163
|
-
]
|
|
164
|
-
})
|
|
165
|
-
]
|
|
166
|
-
})
|
|
167
|
-
]
|
|
168
|
-
})
|
|
169
|
-
]
|
|
170
|
-
}),
|
|
171
|
-
/*#__PURE__*/ _jsxs("button", {
|
|
172
|
-
type: "submit",
|
|
173
|
-
class: "btn mt-4",
|
|
174
|
-
"data-attr-disabled": "$_loading",
|
|
175
|
-
children: [
|
|
176
|
-
/*#__PURE__*/ _jsx("span", {
|
|
177
|
-
"data-show": "!$_loading",
|
|
178
|
-
children: $__i18n._({
|
|
179
|
-
id: "UGT5vp",
|
|
180
|
-
message: "Save Settings"
|
|
181
|
-
})
|
|
182
|
-
}),
|
|
183
|
-
/*#__PURE__*/ _jsx("span", {
|
|
184
|
-
"data-show": "$_loading",
|
|
185
|
-
children: $__i18n._({
|
|
186
|
-
id: "k1ifdL",
|
|
187
|
-
message: "Processing..."
|
|
188
|
-
})
|
|
189
|
-
})
|
|
190
|
-
]
|
|
191
|
-
})
|
|
192
|
-
]
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
]
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
// Appearance tab
|
|
200
|
-
// ---------------------------------------------------------------------------
|
|
201
|
-
function ThemeCard({ theme, selected }) {
|
|
202
|
-
const expr = `$theme === '${theme.id}'`;
|
|
203
|
-
const { preview } = theme;
|
|
204
|
-
return /*#__PURE__*/ _jsx("label", {
|
|
205
|
-
class: `block cursor-pointer rounded-lg border overflow-hidden transition-colors ${selected ? "border-primary" : "border-border"}`,
|
|
206
|
-
"data-class:border-primary": expr,
|
|
207
|
-
"data-class:border-border": `$theme !== '${theme.id}'`,
|
|
208
|
-
children: /*#__PURE__*/ _jsxs("div", {
|
|
209
|
-
class: "grid grid-cols-2",
|
|
210
|
-
children: [
|
|
211
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
212
|
-
class: "p-5",
|
|
213
|
-
style: `background-color:${preview.lightBg};color:${preview.lightText}`,
|
|
214
|
-
children: [
|
|
215
|
-
/*#__PURE__*/ _jsx("input", {
|
|
216
|
-
type: "radio",
|
|
217
|
-
name: "theme",
|
|
218
|
-
value: theme.id,
|
|
219
|
-
"data-bind": "theme",
|
|
220
|
-
checked: selected || undefined,
|
|
221
|
-
class: "mb-1"
|
|
222
|
-
}),
|
|
223
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
224
|
-
class: "font-bold text-lg",
|
|
225
|
-
children: theme.name
|
|
226
|
-
}),
|
|
227
|
-
/*#__PURE__*/ _jsxs("p", {
|
|
228
|
-
class: "text-sm mt-2 leading-relaxed",
|
|
229
|
-
children: [
|
|
230
|
-
"This is the ",
|
|
231
|
-
theme.name,
|
|
232
|
-
" theme in light mode. Links",
|
|
233
|
-
" ",
|
|
234
|
-
/*#__PURE__*/ _jsx("a", {
|
|
235
|
-
tabIndex: -1,
|
|
236
|
-
class: "underline",
|
|
237
|
-
style: `color:${preview.lightLink}`,
|
|
238
|
-
children: "look like this"
|
|
239
|
-
}),
|
|
240
|
-
". We'll show the correct light or dark mode based on your visitor's settings."
|
|
241
|
-
]
|
|
242
|
-
})
|
|
243
|
-
]
|
|
244
|
-
}),
|
|
245
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
246
|
-
class: "p-5",
|
|
247
|
-
style: `background-color:${preview.darkBg};color:${preview.darkText}`,
|
|
248
|
-
children: [
|
|
249
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
250
|
-
class: "font-bold text-lg",
|
|
251
|
-
children: theme.name
|
|
252
|
-
}),
|
|
253
|
-
/*#__PURE__*/ _jsxs("p", {
|
|
254
|
-
class: "text-sm mt-2 leading-relaxed",
|
|
255
|
-
children: [
|
|
256
|
-
"This is the ",
|
|
257
|
-
theme.name,
|
|
258
|
-
" theme in dark mode. Links",
|
|
259
|
-
" ",
|
|
260
|
-
/*#__PURE__*/ _jsx("a", {
|
|
261
|
-
tabIndex: -1,
|
|
262
|
-
class: "underline",
|
|
263
|
-
style: `color:${preview.darkLink}`,
|
|
264
|
-
children: "look like this"
|
|
265
|
-
}),
|
|
266
|
-
". We'll show the correct light or dark mode based on your visitor's settings."
|
|
267
|
-
]
|
|
268
|
-
})
|
|
269
|
-
]
|
|
270
|
-
})
|
|
271
|
-
]
|
|
272
|
-
})
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
function AppearanceContent({ themes, currentThemeId, customCSS }) {
|
|
276
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
277
|
-
const themeSignals = JSON.stringify({
|
|
278
|
-
theme: currentThemeId
|
|
279
|
-
}).replace(/</g, "\\u003c");
|
|
280
|
-
const cssSignals = JSON.stringify({
|
|
281
|
-
customCSS
|
|
282
|
-
}).replace(/</g, "\\u003c");
|
|
283
|
-
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
284
|
-
children: [
|
|
285
|
-
/*#__PURE__*/ _jsx("h1", {
|
|
286
|
-
class: "text-2xl font-semibold mb-2",
|
|
287
|
-
children: $__i18n._({
|
|
288
|
-
id: "Tz0i8g",
|
|
289
|
-
message: "Settings"
|
|
290
|
-
})
|
|
291
|
-
}),
|
|
292
|
-
/*#__PURE__*/ _jsx(SettingsNav, {
|
|
293
|
-
currentTab: "appearance"
|
|
294
|
-
}),
|
|
295
|
-
/*#__PURE__*/ _jsx("div", {
|
|
296
|
-
"data-signals": themeSignals,
|
|
297
|
-
"data-on:change": "@post('/dash/settings/appearance')",
|
|
298
|
-
class: "max-w-3xl",
|
|
299
|
-
children: /*#__PURE__*/ _jsxs("fieldset", {
|
|
300
|
-
children: [
|
|
301
|
-
/*#__PURE__*/ _jsx("legend", {
|
|
302
|
-
class: "text-lg font-semibold",
|
|
303
|
-
children: $__i18n._({
|
|
304
|
-
id: "rFmBG3",
|
|
305
|
-
message: "Color theme"
|
|
306
|
-
})
|
|
307
|
-
}),
|
|
308
|
-
/*#__PURE__*/ _jsx("p", {
|
|
309
|
-
class: "text-sm text-muted-foreground mb-4",
|
|
310
|
-
children: $__i18n._({
|
|
311
|
-
id: "07Epll",
|
|
312
|
-
message: "This will theme both your site and your dashboard. All color themes support dark mode."
|
|
313
|
-
})
|
|
314
|
-
}),
|
|
315
|
-
/*#__PURE__*/ _jsx("div", {
|
|
316
|
-
class: "flex flex-col gap-4",
|
|
317
|
-
children: themes.map((theme)=>/*#__PURE__*/ _jsx(ThemeCard, {
|
|
318
|
-
theme: theme,
|
|
319
|
-
selected: theme.id === currentThemeId
|
|
320
|
-
}, theme.id))
|
|
321
|
-
})
|
|
322
|
-
]
|
|
323
|
-
})
|
|
324
|
-
}),
|
|
325
|
-
/*#__PURE__*/ _jsxs("form", {
|
|
326
|
-
"data-signals": cssSignals,
|
|
327
|
-
"data-on:submit__prevent": "@post('/dash/settings/custom-css')",
|
|
328
|
-
"data-indicator": "_cssLoading",
|
|
329
|
-
class: "max-w-3xl mt-8",
|
|
330
|
-
children: [
|
|
331
|
-
/*#__PURE__*/ _jsxs("fieldset", {
|
|
332
|
-
children: [
|
|
333
|
-
/*#__PURE__*/ _jsx("legend", {
|
|
334
|
-
class: "text-lg font-semibold",
|
|
335
|
-
children: $__i18n._({
|
|
336
|
-
id: "9+vGLh",
|
|
337
|
-
message: "Custom CSS"
|
|
338
|
-
})
|
|
339
|
-
}),
|
|
340
|
-
/*#__PURE__*/ _jsx("p", {
|
|
341
|
-
class: "text-sm text-muted-foreground mb-4",
|
|
342
|
-
children: $__i18n._({
|
|
343
|
-
id: "vmQmHx",
|
|
344
|
-
message: "Add custom CSS to override any styles. Use data attributes like [data-page], [data-post], [data-format] to target specific elements."
|
|
345
|
-
})
|
|
346
|
-
}),
|
|
347
|
-
/*#__PURE__*/ _jsx("textarea", {
|
|
348
|
-
"data-bind": "customCSS",
|
|
349
|
-
class: "textarea font-mono text-sm min-h-32",
|
|
350
|
-
rows: 8,
|
|
351
|
-
placeholder: $__i18n._({
|
|
352
|
-
id: "wc+17X",
|
|
353
|
-
message: "/* Your custom CSS here */"
|
|
354
|
-
}),
|
|
355
|
-
children: customCSS
|
|
356
|
-
})
|
|
357
|
-
]
|
|
358
|
-
}),
|
|
359
|
-
/*#__PURE__*/ _jsxs("button", {
|
|
360
|
-
type: "submit",
|
|
361
|
-
class: "btn mt-4",
|
|
362
|
-
"data-attr-disabled": "$_cssLoading",
|
|
363
|
-
children: [
|
|
364
|
-
/*#__PURE__*/ _jsx("span", {
|
|
365
|
-
"data-show": "!$_cssLoading",
|
|
366
|
-
children: $__i18n._({
|
|
367
|
-
id: "NU2Fqi",
|
|
368
|
-
message: "Save CSS"
|
|
369
|
-
})
|
|
370
|
-
}),
|
|
371
|
-
/*#__PURE__*/ _jsx("span", {
|
|
372
|
-
"data-show": "$_cssLoading",
|
|
373
|
-
children: $__i18n._({
|
|
374
|
-
id: "k1ifdL",
|
|
375
|
-
message: "Processing..."
|
|
376
|
-
})
|
|
377
|
-
})
|
|
378
|
-
]
|
|
379
|
-
})
|
|
380
|
-
]
|
|
381
|
-
})
|
|
382
|
-
]
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
// ---------------------------------------------------------------------------
|
|
386
|
-
// Account tab
|
|
387
|
-
// ---------------------------------------------------------------------------
|
|
388
|
-
function AccountContent({ userName }) {
|
|
389
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
390
|
-
const profileSignals = JSON.stringify({
|
|
391
|
-
userName
|
|
392
|
-
}).replace(/</g, "\\u003c");
|
|
393
|
-
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
394
|
-
children: [
|
|
395
|
-
/*#__PURE__*/ _jsx("h1", {
|
|
396
|
-
class: "text-2xl font-semibold mb-2",
|
|
397
|
-
children: $__i18n._({
|
|
398
|
-
id: "Tz0i8g",
|
|
399
|
-
message: "Settings"
|
|
400
|
-
})
|
|
401
|
-
}),
|
|
402
|
-
/*#__PURE__*/ _jsx(SettingsNav, {
|
|
403
|
-
currentTab: "account"
|
|
404
|
-
}),
|
|
405
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
406
|
-
class: "flex flex-col gap-6 max-w-lg",
|
|
407
|
-
children: [
|
|
408
|
-
/*#__PURE__*/ _jsxs("form", {
|
|
409
|
-
"data-signals": profileSignals,
|
|
410
|
-
"data-on:submit__prevent": "@post('/dash/settings/account')",
|
|
411
|
-
"data-indicator": "_profileLoading",
|
|
412
|
-
children: [
|
|
413
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
414
|
-
class: "card",
|
|
415
|
-
children: [
|
|
416
|
-
/*#__PURE__*/ _jsx("header", {
|
|
417
|
-
children: /*#__PURE__*/ _jsx("h2", {
|
|
418
|
-
children: $__i18n._({
|
|
419
|
-
id: "vERlcd",
|
|
420
|
-
message: "Profile"
|
|
421
|
-
})
|
|
422
|
-
})
|
|
423
|
-
}),
|
|
424
|
-
/*#__PURE__*/ _jsx("section", {
|
|
425
|
-
class: "flex flex-col gap-4",
|
|
426
|
-
children: /*#__PURE__*/ _jsxs("div", {
|
|
427
|
-
class: "field",
|
|
428
|
-
children: [
|
|
429
|
-
/*#__PURE__*/ _jsx("label", {
|
|
430
|
-
class: "label",
|
|
431
|
-
children: $__i18n._({
|
|
432
|
-
id: "6YtxFj",
|
|
433
|
-
message: "Name"
|
|
434
|
-
})
|
|
435
|
-
}),
|
|
436
|
-
/*#__PURE__*/ _jsx("input", {
|
|
437
|
-
type: "text",
|
|
438
|
-
"data-bind": "userName",
|
|
439
|
-
class: "input",
|
|
440
|
-
required: true
|
|
441
|
-
})
|
|
442
|
-
]
|
|
443
|
-
})
|
|
444
|
-
})
|
|
445
|
-
]
|
|
446
|
-
}),
|
|
447
|
-
/*#__PURE__*/ _jsxs("button", {
|
|
448
|
-
type: "submit",
|
|
449
|
-
class: "btn mt-4",
|
|
450
|
-
"data-attr-disabled": "$_profileLoading",
|
|
451
|
-
children: [
|
|
452
|
-
/*#__PURE__*/ _jsx("span", {
|
|
453
|
-
"data-show": "!$_profileLoading",
|
|
454
|
-
children: $__i18n._({
|
|
455
|
-
id: "ssqvZi",
|
|
456
|
-
message: "Save Profile"
|
|
457
|
-
})
|
|
458
|
-
}),
|
|
459
|
-
/*#__PURE__*/ _jsx("span", {
|
|
460
|
-
"data-show": "$_profileLoading",
|
|
461
|
-
children: $__i18n._({
|
|
462
|
-
id: "k1ifdL",
|
|
463
|
-
message: "Processing..."
|
|
464
|
-
})
|
|
465
|
-
})
|
|
466
|
-
]
|
|
467
|
-
})
|
|
468
|
-
]
|
|
469
|
-
}),
|
|
470
|
-
/*#__PURE__*/ _jsxs("form", {
|
|
471
|
-
"data-signals": "{currentPassword: '', newPassword: '', confirmPassword: ''}",
|
|
472
|
-
"data-on:submit__prevent": "@post('/dash/settings/password')",
|
|
473
|
-
"data-indicator": "_passwordLoading",
|
|
474
|
-
children: [
|
|
475
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
476
|
-
class: "card",
|
|
477
|
-
children: [
|
|
478
|
-
/*#__PURE__*/ _jsx("header", {
|
|
479
|
-
children: /*#__PURE__*/ _jsx("h2", {
|
|
480
|
-
children: $__i18n._({
|
|
481
|
-
id: "VhMDMg",
|
|
482
|
-
message: "Change Password"
|
|
483
|
-
})
|
|
484
|
-
})
|
|
485
|
-
}),
|
|
486
|
-
/*#__PURE__*/ _jsxs("section", {
|
|
487
|
-
class: "flex flex-col gap-4",
|
|
488
|
-
children: [
|
|
489
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
490
|
-
class: "field",
|
|
491
|
-
children: [
|
|
492
|
-
/*#__PURE__*/ _jsx("label", {
|
|
493
|
-
class: "label",
|
|
494
|
-
children: $__i18n._({
|
|
495
|
-
id: "DCKkhU",
|
|
496
|
-
message: "Current Password"
|
|
497
|
-
})
|
|
498
|
-
}),
|
|
499
|
-
/*#__PURE__*/ _jsx("input", {
|
|
500
|
-
type: "password",
|
|
501
|
-
"data-bind": "currentPassword",
|
|
502
|
-
class: "input",
|
|
503
|
-
required: true,
|
|
504
|
-
autocomplete: "current-password"
|
|
505
|
-
})
|
|
506
|
-
]
|
|
507
|
-
}),
|
|
508
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
509
|
-
class: "field",
|
|
510
|
-
children: [
|
|
511
|
-
/*#__PURE__*/ _jsx("label", {
|
|
512
|
-
class: "label",
|
|
513
|
-
children: $__i18n._({
|
|
514
|
-
id: "7vhWI8",
|
|
515
|
-
message: "New Password"
|
|
516
|
-
})
|
|
517
|
-
}),
|
|
518
|
-
/*#__PURE__*/ _jsx("input", {
|
|
519
|
-
type: "password",
|
|
520
|
-
"data-bind": "newPassword",
|
|
521
|
-
class: "input",
|
|
522
|
-
required: true,
|
|
523
|
-
minlength: 8,
|
|
524
|
-
autocomplete: "new-password"
|
|
525
|
-
})
|
|
526
|
-
]
|
|
527
|
-
}),
|
|
528
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
529
|
-
class: "field",
|
|
530
|
-
children: [
|
|
531
|
-
/*#__PURE__*/ _jsx("label", {
|
|
532
|
-
class: "label",
|
|
533
|
-
children: $__i18n._({
|
|
534
|
-
id: "yjkELF",
|
|
535
|
-
message: "Confirm New Password"
|
|
536
|
-
})
|
|
537
|
-
}),
|
|
538
|
-
/*#__PURE__*/ _jsx("input", {
|
|
539
|
-
type: "password",
|
|
540
|
-
"data-bind": "confirmPassword",
|
|
541
|
-
class: "input",
|
|
542
|
-
required: true,
|
|
543
|
-
minlength: 8,
|
|
544
|
-
autocomplete: "new-password"
|
|
545
|
-
})
|
|
546
|
-
]
|
|
547
|
-
})
|
|
548
|
-
]
|
|
549
|
-
})
|
|
550
|
-
]
|
|
551
|
-
}),
|
|
552
|
-
/*#__PURE__*/ _jsxs("button", {
|
|
553
|
-
type: "submit",
|
|
554
|
-
class: "btn mt-4",
|
|
555
|
-
"data-attr-disabled": "$_passwordLoading",
|
|
556
|
-
children: [
|
|
557
|
-
/*#__PURE__*/ _jsx("span", {
|
|
558
|
-
"data-show": "!$_passwordLoading",
|
|
559
|
-
children: $__i18n._({
|
|
560
|
-
id: "VhMDMg",
|
|
561
|
-
message: "Change Password"
|
|
562
|
-
})
|
|
563
|
-
}),
|
|
564
|
-
/*#__PURE__*/ _jsx("span", {
|
|
565
|
-
"data-show": "$_passwordLoading",
|
|
566
|
-
children: $__i18n._({
|
|
567
|
-
id: "k1ifdL",
|
|
568
|
-
message: "Processing..."
|
|
569
|
-
})
|
|
570
|
-
})
|
|
571
|
-
]
|
|
572
|
-
})
|
|
573
|
-
]
|
|
574
|
-
})
|
|
575
|
-
]
|
|
576
|
-
})
|
|
577
|
-
]
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
23
|
// ===========================================================================
|
|
581
|
-
//
|
|
24
|
+
// General settings
|
|
582
25
|
// ===========================================================================
|
|
583
|
-
|
|
26
|
+
/** Resolve the avatar storage key to a URL */ async function resolveAvatarUrl(c) {
|
|
27
|
+
const avatarKey = await c.var.services.settings.get("SITE_AVATAR");
|
|
28
|
+
if (!avatarKey) return "";
|
|
29
|
+
const publicUrl = getPublicUrlForProvider(c.env.STORAGE_DRIVER || "r2", c.env.R2_PUBLIC_URL, c.env.S3_PUBLIC_URL);
|
|
30
|
+
return getMediaUrl(avatarKey, publicUrl);
|
|
31
|
+
}
|
|
584
32
|
settingsRoutes.get("/", async (c)=>{
|
|
585
33
|
const { settings } = c.var.services;
|
|
586
34
|
const dbSiteName = await settings.get("SITE_NAME");
|
|
587
35
|
const dbSiteDescription = await settings.get("SITE_DESCRIPTION");
|
|
588
|
-
const siteLanguage = await
|
|
36
|
+
const [siteLanguage, homeDefaultView, timeZone, siteFooter, noindex] = await Promise.all([
|
|
37
|
+
getSiteLanguage(c),
|
|
38
|
+
getHomeDefaultView(c),
|
|
39
|
+
getTimeZone(c),
|
|
40
|
+
getSiteFooter(c),
|
|
41
|
+
isNoIndex(c)
|
|
42
|
+
]);
|
|
589
43
|
const siteNameFallback = getConfigFallback(c, "SITE_NAME");
|
|
590
44
|
const siteDescriptionFallback = getConfigFallback(c, "SITE_DESCRIPTION");
|
|
45
|
+
const siteAvatarUrl = await resolveAvatarUrl(c);
|
|
46
|
+
const showHeaderAvatar = await settings.get("SHOW_HEADER_AVATAR") === "true";
|
|
591
47
|
const saved = c.req.query("saved") !== undefined;
|
|
592
48
|
return c.html(/*#__PURE__*/ _jsx(DashLayout, {
|
|
593
49
|
c: c,
|
|
@@ -601,12 +57,18 @@ settingsRoutes.get("/", async (c)=>{
|
|
|
601
57
|
siteName: dbSiteName || "",
|
|
602
58
|
siteDescription: dbSiteDescription || "",
|
|
603
59
|
siteLanguage: siteLanguage,
|
|
60
|
+
homeDefaultView: homeDefaultView,
|
|
604
61
|
siteNameFallback: siteNameFallback,
|
|
605
|
-
siteDescriptionFallback: siteDescriptionFallback
|
|
62
|
+
siteDescriptionFallback: siteDescriptionFallback,
|
|
63
|
+
siteAvatarUrl: siteAvatarUrl,
|
|
64
|
+
showHeaderAvatar: showHeaderAvatar,
|
|
65
|
+
timeZone: timeZone,
|
|
66
|
+
siteFooter: siteFooter,
|
|
67
|
+
noindex: noindex,
|
|
68
|
+
timezones: TIMEZONES
|
|
606
69
|
})
|
|
607
70
|
}));
|
|
608
71
|
});
|
|
609
|
-
// Save general settings
|
|
610
72
|
settingsRoutes.post("/", async (c)=>{
|
|
611
73
|
const body = await c.req.json();
|
|
612
74
|
const { settings } = c.var.services;
|
|
@@ -622,6 +84,18 @@ settingsRoutes.post("/", async (c)=>{
|
|
|
622
84
|
await settings.remove("SITE_DESCRIPTION");
|
|
623
85
|
}
|
|
624
86
|
await settings.set("SITE_LANGUAGE", body.siteLanguage);
|
|
87
|
+
// Save homepage default view (only store if non-default)
|
|
88
|
+
if (body.homeDefaultView === "featured") {
|
|
89
|
+
await settings.set("HOME_DEFAULT_VIEW", body.homeDefaultView);
|
|
90
|
+
} else {
|
|
91
|
+
await settings.remove("HOME_DEFAULT_VIEW");
|
|
92
|
+
}
|
|
93
|
+
// Timezone
|
|
94
|
+
if (body.timeZone && body.timeZone !== "UTC") {
|
|
95
|
+
await settings.set("TIME_ZONE", body.timeZone);
|
|
96
|
+
} else {
|
|
97
|
+
await settings.remove("TIME_ZONE");
|
|
98
|
+
}
|
|
625
99
|
const languageChanged = oldLanguage !== body.siteLanguage;
|
|
626
100
|
const displayName = body.siteName.trim() || getConfigFallback(c, "SITE_NAME");
|
|
627
101
|
return sse(c, async (stream)=>{
|
|
@@ -635,14 +109,147 @@ settingsRoutes.post("/", async (c)=>{
|
|
|
635
109
|
selector: "title"
|
|
636
110
|
});
|
|
637
111
|
await stream.toast("Settings saved successfully.");
|
|
112
|
+
await stream.patchSignals({
|
|
113
|
+
_orig_siteName: body.siteName,
|
|
114
|
+
_orig_siteDescription: body.siteDescription,
|
|
115
|
+
_orig_siteLanguage: body.siteLanguage,
|
|
116
|
+
_orig_homeDefaultView: body.homeDefaultView,
|
|
117
|
+
_orig_timeZone: body.timeZone,
|
|
118
|
+
_generalDirty: false
|
|
119
|
+
});
|
|
638
120
|
}
|
|
639
121
|
});
|
|
640
122
|
});
|
|
641
|
-
|
|
123
|
+
settingsRoutes.post("/footer", async (c)=>{
|
|
124
|
+
const body = await c.req.json();
|
|
125
|
+
const { settings } = c.var.services;
|
|
126
|
+
if (body.siteFooter?.trim()) {
|
|
127
|
+
await settings.set("SITE_FOOTER", body.siteFooter.trim());
|
|
128
|
+
} else {
|
|
129
|
+
await settings.remove("SITE_FOOTER");
|
|
130
|
+
}
|
|
131
|
+
return sse(c, async (stream)=>{
|
|
132
|
+
await stream.toast("Footer saved successfully.");
|
|
133
|
+
await stream.patchSignals({
|
|
134
|
+
_orig_siteFooter: body.siteFooter,
|
|
135
|
+
_footerDirty: false
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
settingsRoutes.post("/seo", async (c)=>{
|
|
140
|
+
const body = await c.req.json();
|
|
141
|
+
const { settings } = c.var.services;
|
|
142
|
+
// Checkbox "noindex" is the allow-indexing signal:
|
|
143
|
+
// checked (value "true") = indexing allowed -> remove NOINDEX
|
|
144
|
+
// unchecked (value "") = indexing blocked -> set NOINDEX=true
|
|
145
|
+
if (body.noindex === "true") {
|
|
146
|
+
await settings.remove("NOINDEX");
|
|
147
|
+
} else {
|
|
148
|
+
await settings.set("NOINDEX", "true");
|
|
149
|
+
}
|
|
150
|
+
return sse(c, async (stream)=>{
|
|
151
|
+
await stream.toast("SEO settings saved successfully.");
|
|
152
|
+
await stream.patchSignals({
|
|
153
|
+
_orig_noindex: body.noindex,
|
|
154
|
+
_seoDirty: false
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
// ===========================================================================
|
|
159
|
+
// Avatar upload & removal
|
|
160
|
+
// ===========================================================================
|
|
161
|
+
settingsRoutes.post("/avatar", async (c)=>{
|
|
162
|
+
const storage = c.var.storage;
|
|
163
|
+
if (!storage) {
|
|
164
|
+
return dsToast("Storage not configured.", "error");
|
|
165
|
+
}
|
|
166
|
+
const formData = await c.req.formData();
|
|
167
|
+
const file = formData.get("file");
|
|
168
|
+
if (!file) {
|
|
169
|
+
return dsToast("No file provided.", "error");
|
|
170
|
+
}
|
|
171
|
+
const allowedTypes = [
|
|
172
|
+
"image/jpeg",
|
|
173
|
+
"image/png",
|
|
174
|
+
"image/gif",
|
|
175
|
+
"image/webp",
|
|
176
|
+
"image/svg+xml"
|
|
177
|
+
];
|
|
178
|
+
if (!allowedTypes.includes(file.type)) {
|
|
179
|
+
return dsToast("File type not allowed.", "error");
|
|
180
|
+
}
|
|
181
|
+
const maxSize = 10 * 1024 * 1024;
|
|
182
|
+
if (file.size > maxSize) {
|
|
183
|
+
return dsToast("File too large (max 10MB).", "error");
|
|
184
|
+
}
|
|
185
|
+
const { uuidv7 } = await import("uuidv7");
|
|
186
|
+
const ext = file.name.split(".").pop() || "bin";
|
|
187
|
+
const id = uuidv7();
|
|
188
|
+
const date = new Date();
|
|
189
|
+
const year = date.getUTCFullYear();
|
|
190
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
191
|
+
const filename = `${id}.${ext}`;
|
|
192
|
+
const storageKey = `media/${year}/${month}/${filename}`;
|
|
193
|
+
try {
|
|
194
|
+
await storage.put(storageKey, file.stream(), {
|
|
195
|
+
contentType: file.type
|
|
196
|
+
});
|
|
197
|
+
await c.var.services.media.create({
|
|
198
|
+
id,
|
|
199
|
+
filename,
|
|
200
|
+
originalName: file.name,
|
|
201
|
+
mimeType: file.type,
|
|
202
|
+
size: file.size,
|
|
203
|
+
storageKey,
|
|
204
|
+
provider: c.env.STORAGE_DRIVER || "r2"
|
|
205
|
+
});
|
|
206
|
+
await c.var.services.settings.set("SITE_AVATAR", storageKey);
|
|
207
|
+
// Store favicon variants as base64 in settings (small files, accessed every page load)
|
|
208
|
+
const faviconFile = formData.get("favicon");
|
|
209
|
+
const appleTouchFile = formData.get("appleTouch");
|
|
210
|
+
if (faviconFile) {
|
|
211
|
+
const b64 = arrayBufferToBase64(await faviconFile.arrayBuffer());
|
|
212
|
+
await c.var.services.settings.set("SITE_FAVICON_ICO", b64);
|
|
213
|
+
}
|
|
214
|
+
if (appleTouchFile) {
|
|
215
|
+
const b64 = arrayBufferToBase64(await appleTouchFile.arrayBuffer());
|
|
216
|
+
await c.var.services.settings.set("SITE_FAVICON_APPLE_TOUCH", b64);
|
|
217
|
+
}
|
|
218
|
+
return dsRedirect("/dash/settings?saved");
|
|
219
|
+
} catch {
|
|
220
|
+
return dsToast("Upload failed. Please try again.", "error");
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
settingsRoutes.post("/avatar/remove", async (c)=>{
|
|
224
|
+
await c.var.services.settings.remove("SITE_AVATAR");
|
|
225
|
+
await c.var.services.settings.remove("SITE_FAVICON_ICO");
|
|
226
|
+
await c.var.services.settings.remove("SITE_FAVICON_APPLE_TOUCH");
|
|
227
|
+
return dsRedirect("/dash/settings?saved");
|
|
228
|
+
});
|
|
229
|
+
settingsRoutes.post("/avatar/display", async (c)=>{
|
|
230
|
+
const body = await c.req.json();
|
|
231
|
+
const { settings } = c.var.services;
|
|
232
|
+
if (body.showHeaderAvatar === "true") {
|
|
233
|
+
await settings.set("SHOW_HEADER_AVATAR", "true");
|
|
234
|
+
} else {
|
|
235
|
+
await settings.remove("SHOW_HEADER_AVATAR");
|
|
236
|
+
}
|
|
237
|
+
return sse(c, async (stream)=>{
|
|
238
|
+
await stream.toast("Avatar display setting saved successfully.");
|
|
239
|
+
await stream.patchSignals({
|
|
240
|
+
_orig_showHeaderAvatar: body.showHeaderAvatar,
|
|
241
|
+
_avatarDisplayDirty: false
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
// ===========================================================================
|
|
246
|
+
// Appearance
|
|
247
|
+
// ===========================================================================
|
|
642
248
|
settingsRoutes.get("/appearance", async (c)=>{
|
|
643
249
|
const { settings } = c.var.services;
|
|
644
250
|
const siteName = await getSiteName(c);
|
|
645
251
|
const currentThemeId = await settings.get(SETTINGS_KEYS.THEME) ?? "default";
|
|
252
|
+
const currentFontThemeId = await settings.get("FONT_THEME") ?? "default";
|
|
646
253
|
const customCSS = await settings.get(SETTINGS_KEYS.CUSTOM_CSS) ?? "";
|
|
647
254
|
const themes = getAvailableThemes(c.var.config);
|
|
648
255
|
const saved = c.req.query("saved") !== undefined;
|
|
@@ -657,11 +264,12 @@ settingsRoutes.get("/appearance", async (c)=>{
|
|
|
657
264
|
children: /*#__PURE__*/ _jsx(AppearanceContent, {
|
|
658
265
|
themes: themes,
|
|
659
266
|
currentThemeId: currentThemeId,
|
|
267
|
+
fontThemes: BUILTIN_FONT_THEMES,
|
|
268
|
+
currentFontThemeId: currentFontThemeId,
|
|
660
269
|
customCSS: customCSS
|
|
661
270
|
})
|
|
662
271
|
}));
|
|
663
272
|
});
|
|
664
|
-
// Save theme
|
|
665
273
|
settingsRoutes.post("/appearance", async (c)=>{
|
|
666
274
|
const body = await c.req.json();
|
|
667
275
|
const { settings } = c.var.services;
|
|
@@ -677,7 +285,20 @@ settingsRoutes.post("/appearance", async (c)=>{
|
|
|
677
285
|
}
|
|
678
286
|
return dsRedirect("/dash/settings/appearance?saved");
|
|
679
287
|
});
|
|
680
|
-
|
|
288
|
+
settingsRoutes.post("/font-theme", async (c)=>{
|
|
289
|
+
const body = await c.req.json();
|
|
290
|
+
const { settings } = c.var.services;
|
|
291
|
+
const validFont = BUILTIN_FONT_THEMES.find((f)=>f.id === body.fontTheme);
|
|
292
|
+
if (!validFont) {
|
|
293
|
+
return dsToast("Invalid font theme selected.", "error");
|
|
294
|
+
}
|
|
295
|
+
if (validFont.id === "default") {
|
|
296
|
+
await settings.remove("FONT_THEME");
|
|
297
|
+
} else {
|
|
298
|
+
await settings.set("FONT_THEME", validFont.id);
|
|
299
|
+
}
|
|
300
|
+
return dsRedirect("/dash/settings/appearance?saved");
|
|
301
|
+
});
|
|
681
302
|
settingsRoutes.post("/custom-css", async (c)=>{
|
|
682
303
|
const body = await c.req.json();
|
|
683
304
|
const { settings } = c.var.services;
|
|
@@ -689,7 +310,9 @@ settingsRoutes.post("/custom-css", async (c)=>{
|
|
|
689
310
|
}
|
|
690
311
|
return dsToast("Custom CSS saved successfully.");
|
|
691
312
|
});
|
|
692
|
-
//
|
|
313
|
+
// ===========================================================================
|
|
314
|
+
// Account
|
|
315
|
+
// ===========================================================================
|
|
693
316
|
settingsRoutes.get("/account", async (c)=>{
|
|
694
317
|
const siteName = await getSiteName(c);
|
|
695
318
|
const session = await c.var.auth.api.getSession({
|
|
@@ -710,7 +333,6 @@ settingsRoutes.get("/account", async (c)=>{
|
|
|
710
333
|
})
|
|
711
334
|
}));
|
|
712
335
|
});
|
|
713
|
-
// Save account profile
|
|
714
336
|
settingsRoutes.post("/account", async (c)=>{
|
|
715
337
|
const body = await c.req.json();
|
|
716
338
|
const name = body.userName?.trim();
|
|
@@ -729,7 +351,6 @@ settingsRoutes.post("/account", async (c)=>{
|
|
|
729
351
|
}
|
|
730
352
|
return dsToast("Profile saved successfully.");
|
|
731
353
|
});
|
|
732
|
-
// Change password
|
|
733
354
|
settingsRoutes.post("/password", async (c)=>{
|
|
734
355
|
const body = await c.req.json();
|
|
735
356
|
if (body.newPassword !== body.confirmPassword) {
|