@jant/core 0.2.16 → 0.2.18
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.d.ts +5 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +332 -119
- package/dist/i18n/context.d.ts +2 -2
- package/dist/i18n/context.js +1 -1
- package/dist/i18n/i18n.d.ts +1 -1
- package/dist/i18n/i18n.js +1 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/config.d.ts +83 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +104 -0
- package/dist/lib/constants.d.ts +2 -1
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +5 -2
- package/dist/lib/sse.d.ts +15 -0
- package/dist/lib/sse.d.ts.map +1 -1
- package/dist/lib/sse.js +13 -0
- package/dist/lib/theme.d.ts +44 -0
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/lib/theme.js +65 -0
- package/dist/routes/dash/appearance.d.ts +13 -0
- package/dist/routes/dash/appearance.d.ts.map +1 -0
- package/dist/routes/dash/appearance.js +164 -0
- package/dist/routes/dash/collections.d.ts.map +1 -1
- package/dist/routes/dash/collections.js +5 -4
- package/dist/routes/dash/index.d.ts.map +1 -1
- package/dist/routes/dash/index.js +2 -1
- package/dist/routes/dash/media.d.ts.map +1 -1
- package/dist/routes/dash/media.js +3 -2
- package/dist/routes/dash/pages.d.ts.map +1 -1
- package/dist/routes/dash/pages.js +5 -4
- package/dist/routes/dash/posts.d.ts.map +1 -1
- package/dist/routes/dash/posts.js +5 -4
- package/dist/routes/dash/redirects.d.ts.map +1 -1
- package/dist/routes/dash/redirects.js +3 -2
- package/dist/routes/dash/settings.d.ts.map +1 -1
- package/dist/routes/dash/settings.js +39 -38
- package/dist/routes/pages/archive.d.ts.map +1 -1
- package/dist/routes/pages/archive.js +2 -1
- package/dist/routes/pages/collection.d.ts.map +1 -1
- package/dist/routes/pages/collection.js +2 -1
- package/dist/routes/pages/home.d.ts.map +1 -1
- package/dist/routes/pages/home.js +2 -1
- package/dist/routes/pages/page.d.ts.map +1 -1
- package/dist/routes/pages/page.js +2 -1
- package/dist/routes/pages/post.d.ts.map +1 -1
- package/dist/routes/pages/post.js +2 -1
- package/dist/routes/pages/search.d.ts.map +1 -1
- package/dist/routes/pages/search.js +2 -1
- package/dist/services/settings.d.ts +1 -0
- package/dist/services/settings.d.ts.map +1 -1
- package/dist/services/settings.js +3 -0
- package/dist/theme/color-themes.d.ts +30 -0
- package/dist/theme/color-themes.d.ts.map +1 -0
- package/dist/theme/color-themes.js +268 -0
- package/dist/theme/layouts/BaseLayout.d.ts +5 -0
- package/dist/theme/layouts/BaseLayout.d.ts.map +1 -1
- package/dist/theme/layouts/BaseLayout.js +70 -3
- package/dist/theme/layouts/DashLayout.d.ts +2 -0
- package/dist/theme/layouts/DashLayout.d.ts.map +1 -1
- package/dist/theme/layouts/DashLayout.js +10 -1
- package/dist/theme/layouts/index.d.ts +1 -1
- package/dist/theme/layouts/index.d.ts.map +1 -1
- package/dist/types.d.ts +64 -32
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +52 -0
- package/package.json +1 -1
- package/src/app.tsx +286 -59
- package/src/db/migrations/{0000_solid_moon_knight.sql → 0000_square_wallflower.sql} +3 -3
- package/src/db/migrations/meta/0000_snapshot.json +9 -9
- package/src/db/migrations/meta/_journal.json +2 -30
- package/src/i18n/context.tsx +2 -2
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/index.ts +1 -1
- package/src/i18n/locales/en.po +328 -252
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +315 -278
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +315 -278
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +0 -2
- package/src/lib/config.ts +120 -0
- package/src/lib/constants.ts +3 -0
- package/src/lib/sse.ts +38 -0
- package/src/lib/theme.ts +86 -0
- package/src/preset.css +9 -0
- package/src/routes/dash/appearance.tsx +180 -0
- package/src/routes/dash/collections.tsx +5 -4
- package/src/routes/dash/index.tsx +2 -1
- package/src/routes/dash/media.tsx +3 -2
- package/src/routes/dash/pages.tsx +5 -4
- package/src/routes/dash/posts.tsx +5 -4
- package/src/routes/dash/redirects.tsx +3 -2
- package/src/routes/dash/settings.tsx +51 -49
- package/src/routes/pages/archive.tsx +2 -1
- package/src/routes/pages/collection.tsx +2 -1
- package/src/routes/pages/home.tsx +2 -1
- package/src/routes/pages/page.tsx +2 -1
- package/src/routes/pages/post.tsx +2 -1
- package/src/routes/pages/search.tsx +2 -1
- package/src/services/settings.ts +5 -0
- package/src/styles/components.css +93 -0
- package/src/theme/color-themes.ts +321 -0
- package/src/theme/layouts/BaseLayout.tsx +61 -1
- package/src/theme/layouts/DashLayout.tsx +13 -2
- package/src/theme/layouts/index.ts +5 -1
- package/src/types.ts +74 -34
- package/src/db/migrations/0001_add_search_fts.sql +0 -40
- package/src/db/migrations/0002_collection_path.sql +0 -2
- package/src/db/migrations/0003_collection_path_nullable.sql +0 -21
- package/src/db/migrations/0004_media_uuid.sql +0 -35
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
1
2
|
/**
|
|
2
3
|
* Dashboard Posts Routes
|
|
3
4
|
*/
|
|
@@ -54,7 +55,7 @@ postsRoutes.get("/", async (c) => {
|
|
|
54
55
|
const posts = await c.var.services.posts.list({
|
|
55
56
|
visibility: ["featured", "quiet", "unlisted", "draft"],
|
|
56
57
|
});
|
|
57
|
-
const siteName =
|
|
58
|
+
const siteName = await getSiteName(c);
|
|
58
59
|
|
|
59
60
|
return c.html(
|
|
60
61
|
<DashLayout
|
|
@@ -70,7 +71,7 @@ postsRoutes.get("/", async (c) => {
|
|
|
70
71
|
|
|
71
72
|
// New post form
|
|
72
73
|
postsRoutes.get("/new", async (c) => {
|
|
73
|
-
const siteName =
|
|
74
|
+
const siteName = await getSiteName(c);
|
|
74
75
|
|
|
75
76
|
return c.html(
|
|
76
77
|
<DashLayout
|
|
@@ -166,7 +167,7 @@ postsRoutes.get("/:id", async (c) => {
|
|
|
166
167
|
const post = await c.var.services.posts.getById(id);
|
|
167
168
|
if (!post) return c.notFound();
|
|
168
169
|
|
|
169
|
-
const siteName =
|
|
170
|
+
const siteName = await getSiteName(c);
|
|
170
171
|
const pageTitle = post.title || "Post";
|
|
171
172
|
|
|
172
173
|
return c.html(
|
|
@@ -189,7 +190,7 @@ postsRoutes.get("/:id/edit", async (c) => {
|
|
|
189
190
|
const post = await c.var.services.posts.getById(id);
|
|
190
191
|
if (!post) return c.notFound();
|
|
191
192
|
|
|
192
|
-
const siteName =
|
|
193
|
+
const siteName = await getSiteName(c);
|
|
193
194
|
|
|
194
195
|
return c.html(
|
|
195
196
|
<DashLayout
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
1
2
|
/**
|
|
2
3
|
* Dashboard Redirects Routes
|
|
3
4
|
*/
|
|
@@ -176,7 +177,7 @@ function NewRedirectContent() {
|
|
|
176
177
|
|
|
177
178
|
// List redirects
|
|
178
179
|
redirectsRoutes.get("/", async (c) => {
|
|
179
|
-
const siteName =
|
|
180
|
+
const siteName = await getSiteName(c);
|
|
180
181
|
const redirects = await c.var.services.redirects.list();
|
|
181
182
|
|
|
182
183
|
return c.html(
|
|
@@ -193,7 +194,7 @@ redirectsRoutes.get("/", async (c) => {
|
|
|
193
194
|
|
|
194
195
|
// New redirect form
|
|
195
196
|
redirectsRoutes.get("/new", async (c) => {
|
|
196
|
-
const siteName =
|
|
197
|
+
const siteName = await getSiteName(c);
|
|
197
198
|
|
|
198
199
|
return c.html(
|
|
199
200
|
<DashLayout
|
|
@@ -8,6 +8,7 @@ import type { Bindings } from "../../types.js";
|
|
|
8
8
|
import type { AppVariables } from "../../app.js";
|
|
9
9
|
import { DashLayout } from "../../theme/layouts/index.js";
|
|
10
10
|
import { sse } from "../../lib/sse.js";
|
|
11
|
+
import { getSiteLanguage, getConfigFallback } from "../../lib/config.js";
|
|
11
12
|
|
|
12
13
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
13
14
|
|
|
@@ -17,12 +18,14 @@ function SettingsContent({
|
|
|
17
18
|
siteName,
|
|
18
19
|
siteDescription,
|
|
19
20
|
siteLanguage,
|
|
20
|
-
|
|
21
|
+
siteNameFallback,
|
|
22
|
+
siteDescriptionFallback,
|
|
21
23
|
}: {
|
|
22
24
|
siteName: string;
|
|
23
25
|
siteDescription: string;
|
|
24
26
|
siteLanguage: string;
|
|
25
|
-
|
|
27
|
+
siteNameFallback: string;
|
|
28
|
+
siteDescriptionFallback: string;
|
|
26
29
|
}) {
|
|
27
30
|
const { t } = useLingui();
|
|
28
31
|
|
|
@@ -38,27 +41,11 @@ function SettingsContent({
|
|
|
38
41
|
{t({ message: "Settings", comment: "@context: Dashboard heading" })}
|
|
39
42
|
</h1>
|
|
40
43
|
|
|
41
|
-
{saved && (
|
|
42
|
-
<div
|
|
43
|
-
id="settings-saved-toast"
|
|
44
|
-
class="alert mb-4 max-w-lg transition-opacity duration-300"
|
|
45
|
-
data-init={`console.log('[toast] init fired at', Date.now()); history.replaceState({}, '', '/dash/settings'); setTimeout(() => { console.log('[toast] hiding at', Date.now()); const el = document.getElementById('settings-saved-toast'); if (el) { el.style.opacity = '0'; setTimeout(() => el.remove(), 300) } }, 3000)`}
|
|
46
|
-
>
|
|
47
|
-
<h2>
|
|
48
|
-
{t({
|
|
49
|
-
message: "Settings saved successfully.",
|
|
50
|
-
comment: "@context: Toast message after saving settings",
|
|
51
|
-
})}
|
|
52
|
-
</h2>
|
|
53
|
-
</div>
|
|
54
|
-
)}
|
|
55
|
-
|
|
56
44
|
<div class="flex flex-col gap-6 max-w-lg">
|
|
57
45
|
<form
|
|
58
46
|
data-signals={generalSignals}
|
|
59
47
|
data-on:submit__prevent="@post('/dash/settings')"
|
|
60
48
|
>
|
|
61
|
-
<div id="settings-message"></div>
|
|
62
49
|
<div class="card">
|
|
63
50
|
<header>
|
|
64
51
|
<h2>
|
|
@@ -80,7 +67,7 @@ function SettingsContent({
|
|
|
80
67
|
type="text"
|
|
81
68
|
data-bind="siteName"
|
|
82
69
|
class="input"
|
|
83
|
-
|
|
70
|
+
placeholder={siteNameFallback}
|
|
84
71
|
/>
|
|
85
72
|
</div>
|
|
86
73
|
|
|
@@ -91,7 +78,12 @@ function SettingsContent({
|
|
|
91
78
|
comment: "@context: Settings form field",
|
|
92
79
|
})}
|
|
93
80
|
</label>
|
|
94
|
-
<textarea
|
|
81
|
+
<textarea
|
|
82
|
+
data-bind="siteDescription"
|
|
83
|
+
class="textarea"
|
|
84
|
+
rows={3}
|
|
85
|
+
placeholder={siteDescriptionFallback}
|
|
86
|
+
>
|
|
95
87
|
{siteDescription}
|
|
96
88
|
</textarea>
|
|
97
89
|
</div>
|
|
@@ -130,7 +122,6 @@ function SettingsContent({
|
|
|
130
122
|
data-signals="{currentPassword: '', newPassword: '', confirmPassword: ''}"
|
|
131
123
|
data-on:submit__prevent="@post('/dash/settings/password')"
|
|
132
124
|
>
|
|
133
|
-
<div id="password-message"></div>
|
|
134
125
|
<div class="card">
|
|
135
126
|
<header>
|
|
136
127
|
<h2>
|
|
@@ -207,24 +198,33 @@ function SettingsContent({
|
|
|
207
198
|
|
|
208
199
|
// Settings page
|
|
209
200
|
settingsRoutes.get("/", async (c) => {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
201
|
+
const { settings } = c.var.services;
|
|
202
|
+
|
|
203
|
+
// Fetch raw DB values (null if not set)
|
|
204
|
+
const dbSiteName = await settings.get("SITE_NAME");
|
|
205
|
+
const dbSiteDescription = await settings.get("SITE_DESCRIPTION");
|
|
206
|
+
const siteLanguage = await getSiteLanguage(c);
|
|
207
|
+
|
|
208
|
+
// Fallback values (ENV > Default) for placeholders
|
|
209
|
+
const siteNameFallback = getConfigFallback(c, "SITE_NAME");
|
|
210
|
+
const siteDescriptionFallback = getConfigFallback(c, "SITE_DESCRIPTION");
|
|
211
|
+
|
|
214
212
|
const saved = c.req.query("saved") !== undefined;
|
|
215
213
|
|
|
216
214
|
return c.html(
|
|
217
215
|
<DashLayout
|
|
218
216
|
c={c}
|
|
219
217
|
title="Settings"
|
|
220
|
-
siteName={
|
|
218
|
+
siteName={dbSiteName || siteNameFallback}
|
|
221
219
|
currentPath="/dash/settings"
|
|
220
|
+
toast={saved ? { message: "Settings saved successfully." } : undefined}
|
|
222
221
|
>
|
|
223
222
|
<SettingsContent
|
|
224
|
-
siteName={
|
|
225
|
-
siteDescription={
|
|
223
|
+
siteName={dbSiteName || ""}
|
|
224
|
+
siteDescription={dbSiteDescription || ""}
|
|
226
225
|
siteLanguage={siteLanguage}
|
|
227
|
-
|
|
226
|
+
siteNameFallback={siteNameFallback}
|
|
227
|
+
siteDescriptionFallback={siteDescriptionFallback}
|
|
228
228
|
/>
|
|
229
229
|
</DashLayout>,
|
|
230
230
|
);
|
|
@@ -238,14 +238,25 @@ settingsRoutes.post("/", async (c) => {
|
|
|
238
238
|
siteLanguage: string;
|
|
239
239
|
}>();
|
|
240
240
|
|
|
241
|
-
const
|
|
242
|
-
(await c.var.services.settings.get("SITE_LANGUAGE")) ?? "en";
|
|
241
|
+
const { settings } = c.var.services;
|
|
243
242
|
|
|
244
|
-
await
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
const oldLanguage = (await settings.get("SITE_LANGUAGE")) ?? "en";
|
|
244
|
+
|
|
245
|
+
// For text fields: empty = remove from DB (fall back to ENV > Default)
|
|
246
|
+
if (body.siteName.trim()) {
|
|
247
|
+
await settings.set("SITE_NAME", body.siteName.trim());
|
|
248
|
+
} else {
|
|
249
|
+
await settings.remove("SITE_NAME");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (body.siteDescription.trim()) {
|
|
253
|
+
await settings.set("SITE_DESCRIPTION", body.siteDescription.trim());
|
|
254
|
+
} else {
|
|
255
|
+
await settings.remove("SITE_DESCRIPTION");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Language always has a value from the select
|
|
259
|
+
await settings.set("SITE_LANGUAGE", body.siteLanguage);
|
|
249
260
|
|
|
250
261
|
const languageChanged = oldLanguage !== body.siteLanguage;
|
|
251
262
|
|
|
@@ -254,10 +265,7 @@ settingsRoutes.post("/", async (c) => {
|
|
|
254
265
|
// Language changed - full reload needed to update all UI text
|
|
255
266
|
await stream.redirect("/dash/settings?saved");
|
|
256
267
|
} else {
|
|
257
|
-
|
|
258
|
-
await stream.patchElements(
|
|
259
|
-
'<div id="settings-message"><div class="alert mb-4 transition-opacity duration-300" data-init="setTimeout(() => { el.style.opacity = \'0\'; setTimeout(() => el.remove(), 300) }, 3000)"><h2>Settings saved successfully.</h2></div></div>',
|
|
260
|
-
);
|
|
268
|
+
await stream.toast("Settings saved successfully.");
|
|
261
269
|
}
|
|
262
270
|
});
|
|
263
271
|
});
|
|
@@ -272,9 +280,7 @@ settingsRoutes.post("/password", async (c) => {
|
|
|
272
280
|
|
|
273
281
|
if (body.newPassword !== body.confirmPassword) {
|
|
274
282
|
return sse(c, async (stream) => {
|
|
275
|
-
await stream.
|
|
276
|
-
'<div id="password-message"><div class="alert-destructive mb-4"><h2>Passwords do not match.</h2></div></div>',
|
|
277
|
-
);
|
|
283
|
+
await stream.toast("Passwords do not match.", "error");
|
|
278
284
|
});
|
|
279
285
|
}
|
|
280
286
|
|
|
@@ -289,16 +295,12 @@ settingsRoutes.post("/password", async (c) => {
|
|
|
289
295
|
});
|
|
290
296
|
} catch {
|
|
291
297
|
return sse(c, async (stream) => {
|
|
292
|
-
await stream.
|
|
293
|
-
'<div id="password-message"><div class="alert-destructive mb-4"><h2>Current password is incorrect.</h2></div></div>',
|
|
294
|
-
);
|
|
298
|
+
await stream.toast("Current password is incorrect.", "error");
|
|
295
299
|
});
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
return sse(c, async (stream) => {
|
|
299
|
-
await stream.
|
|
300
|
-
'<div id="password-message"><div class="alert mb-4"><h2>Password changed successfully.</h2></div></div>',
|
|
301
|
-
);
|
|
303
|
+
await stream.toast("Password changed successfully.");
|
|
302
304
|
await stream.patchSignals({
|
|
303
305
|
currentPassword: "",
|
|
304
306
|
newPassword: "",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
1
2
|
/**
|
|
2
3
|
* Archive Page Route
|
|
3
4
|
*
|
|
@@ -225,7 +226,7 @@ archiveRoutes.get("/", async (c) => {
|
|
|
225
226
|
const cursorParam = c.req.query("cursor");
|
|
226
227
|
const cursor = cursorParam ? parseInt(cursorParam, 10) : undefined;
|
|
227
228
|
|
|
228
|
-
const siteName =
|
|
229
|
+
const siteName = await getSiteName(c);
|
|
229
230
|
|
|
230
231
|
// Fetch one extra to check for more
|
|
231
232
|
const posts = await c.var.services.posts.list({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
1
2
|
/**
|
|
2
3
|
* Collection Page Route
|
|
3
4
|
*/
|
|
@@ -89,7 +90,7 @@ collectionRoutes.get("/:path", async (c) => {
|
|
|
89
90
|
if (!collection) return c.notFound();
|
|
90
91
|
|
|
91
92
|
const posts = await c.var.services.collections.getPosts(collection.id);
|
|
92
|
-
const siteName =
|
|
93
|
+
const siteName = await getSiteName(c);
|
|
93
94
|
|
|
94
95
|
return c.html(
|
|
95
96
|
<BaseLayout
|
|
@@ -9,6 +9,7 @@ import type { AppVariables } from "../../app.js";
|
|
|
9
9
|
import { BaseLayout } from "../../theme/layouts/index.js";
|
|
10
10
|
import * as sqid from "../../lib/sqid.js";
|
|
11
11
|
import * as time from "../../lib/time.js";
|
|
12
|
+
import { getSiteName } from "../../lib/config.js";
|
|
12
13
|
|
|
13
14
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
14
15
|
|
|
@@ -106,7 +107,7 @@ homeRoutes.get("/", async (c) => {
|
|
|
106
107
|
return c.redirect("/setup");
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
const siteName =
|
|
110
|
+
const siteName = await getSiteName(c);
|
|
110
111
|
|
|
111
112
|
const posts = await c.var.services.posts.list({
|
|
112
113
|
visibility: ["featured", "quiet"],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
1
2
|
/**
|
|
2
3
|
* Custom Page Route
|
|
3
4
|
*
|
|
@@ -60,7 +61,7 @@ pageRoutes.get("/:path", async (c) => {
|
|
|
60
61
|
return c.notFound();
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
const siteName =
|
|
64
|
+
const siteName = await getSiteName(c);
|
|
64
65
|
|
|
65
66
|
return c.html(
|
|
66
67
|
<BaseLayout
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
1
2
|
/**
|
|
2
3
|
* Single Post Page Route
|
|
3
4
|
*/
|
|
@@ -81,7 +82,7 @@ postRoutes.get("/:id", async (c) => {
|
|
|
81
82
|
return c.notFound();
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
const siteName =
|
|
85
|
+
const siteName = await getSiteName(c);
|
|
85
86
|
const title = post.title || siteName;
|
|
86
87
|
|
|
87
88
|
return c.html(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
1
2
|
/**
|
|
2
3
|
* Search Page Route
|
|
3
4
|
*/
|
|
@@ -155,7 +156,7 @@ searchRoutes.get("/", async (c) => {
|
|
|
155
156
|
const pageParam = c.req.query("page");
|
|
156
157
|
const page = pageParam ? Math.max(1, parseInt(pageParam, 10) || 1) : 1;
|
|
157
158
|
|
|
158
|
-
const siteName =
|
|
159
|
+
const siteName = await getSiteName(c);
|
|
159
160
|
|
|
160
161
|
// Only search if there's a query
|
|
161
162
|
let results: Awaited<ReturnType<typeof c.var.services.search.search>> = [];
|
package/src/services/settings.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface SettingsService {
|
|
|
19
19
|
getAll(): Promise<Record<string, string>>;
|
|
20
20
|
set(key: SettingsKey, value: string): Promise<void>;
|
|
21
21
|
setMany(entries: Partial<Record<SettingsKey, string>>): Promise<void>;
|
|
22
|
+
remove(key: SettingsKey): Promise<void>;
|
|
22
23
|
isOnboardingComplete(): Promise<boolean>;
|
|
23
24
|
completeOnboarding(): Promise<void>;
|
|
24
25
|
}
|
|
@@ -54,6 +55,10 @@ export function createSettingsService(db: Database): SettingsService {
|
|
|
54
55
|
});
|
|
55
56
|
},
|
|
56
57
|
|
|
58
|
+
async remove(key) {
|
|
59
|
+
await db.delete(settings).where(eq(settings.key, key));
|
|
60
|
+
},
|
|
61
|
+
|
|
57
62
|
async setMany(entries) {
|
|
58
63
|
const timestamp = now();
|
|
59
64
|
const keys = Object.keys(entries) as SettingsKey[];
|
|
@@ -12,6 +12,32 @@
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/* Alert variants */
|
|
16
|
+
@layer components {
|
|
17
|
+
.alert-success {
|
|
18
|
+
@apply relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current;
|
|
19
|
+
@apply text-success bg-card [&>svg]:text-current;
|
|
20
|
+
|
|
21
|
+
> h2,
|
|
22
|
+
> h3,
|
|
23
|
+
> h4,
|
|
24
|
+
> h5,
|
|
25
|
+
> h6,
|
|
26
|
+
> strong,
|
|
27
|
+
> [data-title] {
|
|
28
|
+
@apply col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
> section {
|
|
32
|
+
@apply text-success col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed;
|
|
33
|
+
|
|
34
|
+
ul {
|
|
35
|
+
@apply list-inside list-disc text-sm;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
15
41
|
/* Badge components */
|
|
16
42
|
@layer components {
|
|
17
43
|
.badge {
|
|
@@ -45,3 +71,70 @@
|
|
|
45
71
|
color: white;
|
|
46
72
|
}
|
|
47
73
|
}
|
|
74
|
+
|
|
75
|
+
/* Toast notifications */
|
|
76
|
+
@layer components {
|
|
77
|
+
.toast-container {
|
|
78
|
+
@apply fixed top-4 right-4 z-50 flex flex-col gap-2 pointer-events-none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.toast {
|
|
82
|
+
@apply pointer-events-auto rounded-lg border px-4 py-3 text-sm shadow-lg;
|
|
83
|
+
@apply flex items-start gap-2.5 min-w-64 max-w-sm;
|
|
84
|
+
background-color: var(--color-card);
|
|
85
|
+
animation: toast-in 0.3s ease-out;
|
|
86
|
+
|
|
87
|
+
> svg:first-child {
|
|
88
|
+
@apply size-4 shrink-0 translate-y-0.5;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
> span {
|
|
92
|
+
@apply flex-1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.toast-success {
|
|
97
|
+
color: var(--color-success);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.toast-error {
|
|
101
|
+
color: var(--color-destructive);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.toast-close {
|
|
105
|
+
@apply shrink-0 translate-y-0.5 cursor-pointer rounded-sm p-0 border-0 bg-transparent;
|
|
106
|
+
color: var(--color-muted-foreground);
|
|
107
|
+
|
|
108
|
+
&:hover {
|
|
109
|
+
color: var(--color-foreground);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
> svg {
|
|
113
|
+
@apply size-3.5;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.toast-out {
|
|
118
|
+
animation: toast-out 0.3s ease-in forwards;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@keyframes toast-in {
|
|
123
|
+
from {
|
|
124
|
+
opacity: 0;
|
|
125
|
+
transform: translateY(-0.5rem);
|
|
126
|
+
}
|
|
127
|
+
to {
|
|
128
|
+
opacity: 1;
|
|
129
|
+
transform: translateY(0);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@keyframes toast-out {
|
|
134
|
+
from {
|
|
135
|
+
opacity: 1;
|
|
136
|
+
}
|
|
137
|
+
to {
|
|
138
|
+
opacity: 0;
|
|
139
|
+
}
|
|
140
|
+
}
|