@jant/core 0.3.0 → 0.3.2
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.map +1 -1
- package/dist/app.js +0 -2
- package/dist/auth.js +1 -1
- package/dist/lib/constants.d.ts +1 -1
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +1 -2
- package/dist/routes/dash/settings.d.ts +2 -0
- package/dist/routes/dash/settings.d.ts.map +1 -1
- package/dist/routes/dash/settings.js +413 -93
- package/dist/theme/layouts/DashLayout.d.ts.map +1 -1
- package/dist/theme/layouts/DashLayout.js +0 -8
- package/package.json +1 -2
- package/src/app.tsx +0 -3
- package/src/auth.ts +1 -1
- package/src/db/migrations/0001_add_search_fts.sql +34 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/src/lib/constants.ts +0 -1
- package/src/routes/dash/settings.tsx +350 -16
- package/src/theme/layouts/DashLayout.tsx +0 -9
- package/dist/routes/dash/appearance.d.ts +0 -13
- package/dist/routes/dash/appearance.d.ts.map +0 -1
- package/dist/routes/dash/appearance.js +0 -160
- package/src/routes/dash/appearance.tsx +0 -176
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Dashboard Appearance Routes
|
|
4
|
-
*/ import { Hono } from "hono";
|
|
5
|
-
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
6
|
-
import { DashLayout } from "../../theme/layouts/index.js";
|
|
7
|
-
import { dsRedirect, dsToast } from "../../lib/sse.js";
|
|
8
|
-
import { getSiteName } from "../../lib/config.js";
|
|
9
|
-
import { SETTINGS_KEYS } from "../../lib/constants.js";
|
|
10
|
-
import { getAvailableThemes } from "../../lib/theme.js";
|
|
11
|
-
export const appearanceRoutes = new Hono();
|
|
12
|
-
function ThemeCard({ theme, selected }) {
|
|
13
|
-
const expr = `$theme === '${theme.id}'`;
|
|
14
|
-
const { preview } = theme;
|
|
15
|
-
return /*#__PURE__*/ _jsx("label", {
|
|
16
|
-
class: `block cursor-pointer rounded-lg border overflow-hidden transition-colors ${selected ? "border-primary" : "border-border"}`,
|
|
17
|
-
"data-class:border-primary": expr,
|
|
18
|
-
"data-class:border-border": `$theme !== '${theme.id}'`,
|
|
19
|
-
children: /*#__PURE__*/ _jsxs("div", {
|
|
20
|
-
class: "grid grid-cols-2",
|
|
21
|
-
children: [
|
|
22
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
23
|
-
class: "p-5",
|
|
24
|
-
style: `background-color:${preview.lightBg};color:${preview.lightText}`,
|
|
25
|
-
children: [
|
|
26
|
-
/*#__PURE__*/ _jsx("input", {
|
|
27
|
-
type: "radio",
|
|
28
|
-
name: "theme",
|
|
29
|
-
value: theme.id,
|
|
30
|
-
"data-bind": "theme",
|
|
31
|
-
checked: selected || undefined,
|
|
32
|
-
class: "mb-1"
|
|
33
|
-
}),
|
|
34
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
35
|
-
class: "font-bold text-lg",
|
|
36
|
-
children: theme.name
|
|
37
|
-
}),
|
|
38
|
-
/*#__PURE__*/ _jsxs("p", {
|
|
39
|
-
class: "text-sm mt-2 leading-relaxed",
|
|
40
|
-
children: [
|
|
41
|
-
"This is the ",
|
|
42
|
-
theme.name,
|
|
43
|
-
" theme in light mode. Links",
|
|
44
|
-
" ",
|
|
45
|
-
/*#__PURE__*/ _jsx("a", {
|
|
46
|
-
tabIndex: -1,
|
|
47
|
-
class: "underline",
|
|
48
|
-
style: `color:${preview.lightLink}`,
|
|
49
|
-
children: "look like this"
|
|
50
|
-
}),
|
|
51
|
-
". We'll show the correct light or dark mode based on your visitor's settings."
|
|
52
|
-
]
|
|
53
|
-
})
|
|
54
|
-
]
|
|
55
|
-
}),
|
|
56
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
57
|
-
class: "p-5",
|
|
58
|
-
style: `background-color:${preview.darkBg};color:${preview.darkText}`,
|
|
59
|
-
children: [
|
|
60
|
-
/*#__PURE__*/ _jsx("h3", {
|
|
61
|
-
class: "font-bold text-lg",
|
|
62
|
-
children: theme.name
|
|
63
|
-
}),
|
|
64
|
-
/*#__PURE__*/ _jsxs("p", {
|
|
65
|
-
class: "text-sm mt-2 leading-relaxed",
|
|
66
|
-
children: [
|
|
67
|
-
"This is the ",
|
|
68
|
-
theme.name,
|
|
69
|
-
" theme in dark mode. Links",
|
|
70
|
-
" ",
|
|
71
|
-
/*#__PURE__*/ _jsx("a", {
|
|
72
|
-
tabIndex: -1,
|
|
73
|
-
class: "underline",
|
|
74
|
-
style: `color:${preview.darkLink}`,
|
|
75
|
-
children: "look like this"
|
|
76
|
-
}),
|
|
77
|
-
". We'll show the correct light or dark mode based on your visitor's settings."
|
|
78
|
-
]
|
|
79
|
-
})
|
|
80
|
-
]
|
|
81
|
-
})
|
|
82
|
-
]
|
|
83
|
-
})
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
function AppearanceContent({ themes, currentThemeId }) {
|
|
87
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
88
|
-
const signals = JSON.stringify({
|
|
89
|
-
theme: currentThemeId
|
|
90
|
-
}).replace(/</g, "\\u003c");
|
|
91
|
-
return /*#__PURE__*/ _jsx("div", {
|
|
92
|
-
"data-signals": signals,
|
|
93
|
-
"data-on:change": "@post('/dash/appearance')",
|
|
94
|
-
class: "max-w-3xl",
|
|
95
|
-
children: /*#__PURE__*/ _jsxs("fieldset", {
|
|
96
|
-
children: [
|
|
97
|
-
/*#__PURE__*/ _jsx("legend", {
|
|
98
|
-
class: "text-lg font-semibold",
|
|
99
|
-
children: $__i18n._({
|
|
100
|
-
id: "rFmBG3",
|
|
101
|
-
message: "Color theme"
|
|
102
|
-
})
|
|
103
|
-
}),
|
|
104
|
-
/*#__PURE__*/ _jsx("p", {
|
|
105
|
-
class: "text-sm text-muted-foreground mb-4",
|
|
106
|
-
children: $__i18n._({
|
|
107
|
-
id: "07Epll",
|
|
108
|
-
message: "This will theme both your site and your dashboard. All color themes support dark mode."
|
|
109
|
-
})
|
|
110
|
-
}),
|
|
111
|
-
/*#__PURE__*/ _jsx("div", {
|
|
112
|
-
class: "flex flex-col gap-4",
|
|
113
|
-
children: themes.map((theme)=>/*#__PURE__*/ _jsx(ThemeCard, {
|
|
114
|
-
theme: theme,
|
|
115
|
-
selected: theme.id === currentThemeId
|
|
116
|
-
}, theme.id))
|
|
117
|
-
})
|
|
118
|
-
]
|
|
119
|
-
})
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
// Appearance page
|
|
123
|
-
appearanceRoutes.get("/", async (c)=>{
|
|
124
|
-
const { settings } = c.var.services;
|
|
125
|
-
const siteName = await getSiteName(c);
|
|
126
|
-
const currentThemeId = await settings.get(SETTINGS_KEYS.THEME) ?? "default";
|
|
127
|
-
const themes = getAvailableThemes(c.var.config);
|
|
128
|
-
const saved = c.req.query("saved") !== undefined;
|
|
129
|
-
return c.html(/*#__PURE__*/ _jsx(DashLayout, {
|
|
130
|
-
c: c,
|
|
131
|
-
title: "Appearance",
|
|
132
|
-
siteName: siteName,
|
|
133
|
-
currentPath: "/dash/appearance",
|
|
134
|
-
toast: saved ? {
|
|
135
|
-
message: "Theme saved successfully."
|
|
136
|
-
} : undefined,
|
|
137
|
-
children: /*#__PURE__*/ _jsx(AppearanceContent, {
|
|
138
|
-
themes: themes,
|
|
139
|
-
currentThemeId: currentThemeId
|
|
140
|
-
})
|
|
141
|
-
}));
|
|
142
|
-
});
|
|
143
|
-
// Save theme
|
|
144
|
-
appearanceRoutes.post("/", async (c)=>{
|
|
145
|
-
const body = await c.req.json();
|
|
146
|
-
const { settings } = c.var.services;
|
|
147
|
-
const themes = getAvailableThemes(c.var.config);
|
|
148
|
-
// Validate theme ID
|
|
149
|
-
const validTheme = themes.find((t)=>t.id === body.theme);
|
|
150
|
-
if (!validTheme) {
|
|
151
|
-
return dsToast("Invalid theme selected.", "error");
|
|
152
|
-
}
|
|
153
|
-
if (validTheme.id === "default") {
|
|
154
|
-
await settings.remove(SETTINGS_KEYS.THEME);
|
|
155
|
-
} else {
|
|
156
|
-
await settings.set(SETTINGS_KEYS.THEME, validTheme.id);
|
|
157
|
-
}
|
|
158
|
-
// Full page reload to apply the new theme CSS
|
|
159
|
-
return dsRedirect("/dash/appearance?saved");
|
|
160
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dashboard Appearance Routes
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Hono } from "hono";
|
|
6
|
-
import { useLingui } from "@lingui/react/macro";
|
|
7
|
-
import type { Bindings } from "../../types.js";
|
|
8
|
-
import type { AppVariables } from "../../app.js";
|
|
9
|
-
import { DashLayout } from "../../theme/layouts/index.js";
|
|
10
|
-
import { dsRedirect, dsToast } from "../../lib/sse.js";
|
|
11
|
-
import { getSiteName } from "../../lib/config.js";
|
|
12
|
-
import { SETTINGS_KEYS } from "../../lib/constants.js";
|
|
13
|
-
import { getAvailableThemes } from "../../lib/theme.js";
|
|
14
|
-
import type { ColorTheme } from "../../theme/color-themes.js";
|
|
15
|
-
|
|
16
|
-
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
17
|
-
|
|
18
|
-
export const appearanceRoutes = new Hono<Env>();
|
|
19
|
-
|
|
20
|
-
function ThemeCard({
|
|
21
|
-
theme,
|
|
22
|
-
selected,
|
|
23
|
-
}: {
|
|
24
|
-
theme: ColorTheme;
|
|
25
|
-
selected: boolean;
|
|
26
|
-
}) {
|
|
27
|
-
const expr = `$theme === '${theme.id}'`;
|
|
28
|
-
const { preview } = theme;
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<label
|
|
32
|
-
class={`block cursor-pointer rounded-lg border overflow-hidden transition-colors ${selected ? "border-primary" : "border-border"}`}
|
|
33
|
-
data-class:border-primary={expr}
|
|
34
|
-
data-class:border-border={`$theme !== '${theme.id}'`}
|
|
35
|
-
>
|
|
36
|
-
<div class="grid grid-cols-2">
|
|
37
|
-
<div
|
|
38
|
-
class="p-5"
|
|
39
|
-
style={`background-color:${preview.lightBg};color:${preview.lightText}`}
|
|
40
|
-
>
|
|
41
|
-
<input
|
|
42
|
-
type="radio"
|
|
43
|
-
name="theme"
|
|
44
|
-
value={theme.id}
|
|
45
|
-
data-bind="theme"
|
|
46
|
-
checked={selected || undefined}
|
|
47
|
-
class="mb-1"
|
|
48
|
-
/>
|
|
49
|
-
<h3 class="font-bold text-lg">{theme.name}</h3>
|
|
50
|
-
<p class="text-sm mt-2 leading-relaxed">
|
|
51
|
-
This is the {theme.name} theme in light mode. Links{" "}
|
|
52
|
-
<a
|
|
53
|
-
tabIndex={-1}
|
|
54
|
-
class="underline"
|
|
55
|
-
style={`color:${preview.lightLink}`}
|
|
56
|
-
>
|
|
57
|
-
look like this
|
|
58
|
-
</a>
|
|
59
|
-
. We'll show the correct light or dark mode based on your visitor's
|
|
60
|
-
settings.
|
|
61
|
-
</p>
|
|
62
|
-
</div>
|
|
63
|
-
<div
|
|
64
|
-
class="p-5"
|
|
65
|
-
style={`background-color:${preview.darkBg};color:${preview.darkText}`}
|
|
66
|
-
>
|
|
67
|
-
<h3 class="font-bold text-lg">{theme.name}</h3>
|
|
68
|
-
<p class="text-sm mt-2 leading-relaxed">
|
|
69
|
-
This is the {theme.name} theme in dark mode. Links{" "}
|
|
70
|
-
<a
|
|
71
|
-
tabIndex={-1}
|
|
72
|
-
class="underline"
|
|
73
|
-
style={`color:${preview.darkLink}`}
|
|
74
|
-
>
|
|
75
|
-
look like this
|
|
76
|
-
</a>
|
|
77
|
-
. We'll show the correct light or dark mode based on your visitor's
|
|
78
|
-
settings.
|
|
79
|
-
</p>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
</label>
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function AppearanceContent({
|
|
87
|
-
themes,
|
|
88
|
-
currentThemeId,
|
|
89
|
-
}: {
|
|
90
|
-
themes: ColorTheme[];
|
|
91
|
-
currentThemeId: string;
|
|
92
|
-
}) {
|
|
93
|
-
const { t } = useLingui();
|
|
94
|
-
|
|
95
|
-
const signals = JSON.stringify({ theme: currentThemeId }).replace(
|
|
96
|
-
/</g,
|
|
97
|
-
"\\u003c",
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<div
|
|
102
|
-
data-signals={signals}
|
|
103
|
-
data-on:change="@post('/dash/appearance')"
|
|
104
|
-
class="max-w-3xl"
|
|
105
|
-
>
|
|
106
|
-
<fieldset>
|
|
107
|
-
<legend class="text-lg font-semibold">
|
|
108
|
-
{t({
|
|
109
|
-
message: "Color theme",
|
|
110
|
-
comment: "@context: Appearance settings heading",
|
|
111
|
-
})}
|
|
112
|
-
</legend>
|
|
113
|
-
<p class="text-sm text-muted-foreground mb-4">
|
|
114
|
-
{t({
|
|
115
|
-
message:
|
|
116
|
-
"This will theme both your site and your dashboard. All color themes support dark mode.",
|
|
117
|
-
comment: "@context: Appearance settings description",
|
|
118
|
-
})}
|
|
119
|
-
</p>
|
|
120
|
-
|
|
121
|
-
<div class="flex flex-col gap-4">
|
|
122
|
-
{themes.map((theme) => (
|
|
123
|
-
<ThemeCard
|
|
124
|
-
key={theme.id}
|
|
125
|
-
theme={theme}
|
|
126
|
-
selected={theme.id === currentThemeId}
|
|
127
|
-
/>
|
|
128
|
-
))}
|
|
129
|
-
</div>
|
|
130
|
-
</fieldset>
|
|
131
|
-
</div>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Appearance page
|
|
136
|
-
appearanceRoutes.get("/", async (c) => {
|
|
137
|
-
const { settings } = c.var.services;
|
|
138
|
-
const siteName = await getSiteName(c);
|
|
139
|
-
const currentThemeId = (await settings.get(SETTINGS_KEYS.THEME)) ?? "default";
|
|
140
|
-
const themes = getAvailableThemes(c.var.config);
|
|
141
|
-
const saved = c.req.query("saved") !== undefined;
|
|
142
|
-
|
|
143
|
-
return c.html(
|
|
144
|
-
<DashLayout
|
|
145
|
-
c={c}
|
|
146
|
-
title="Appearance"
|
|
147
|
-
siteName={siteName}
|
|
148
|
-
currentPath="/dash/appearance"
|
|
149
|
-
toast={saved ? { message: "Theme saved successfully." } : undefined}
|
|
150
|
-
>
|
|
151
|
-
<AppearanceContent themes={themes} currentThemeId={currentThemeId} />
|
|
152
|
-
</DashLayout>,
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Save theme
|
|
157
|
-
appearanceRoutes.post("/", async (c) => {
|
|
158
|
-
const body = await c.req.json<{ theme: string }>();
|
|
159
|
-
const { settings } = c.var.services;
|
|
160
|
-
const themes = getAvailableThemes(c.var.config);
|
|
161
|
-
|
|
162
|
-
// Validate theme ID
|
|
163
|
-
const validTheme = themes.find((t) => t.id === body.theme);
|
|
164
|
-
if (!validTheme) {
|
|
165
|
-
return dsToast("Invalid theme selected.", "error");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (validTheme.id === "default") {
|
|
169
|
-
await settings.remove(SETTINGS_KEYS.THEME);
|
|
170
|
-
} else {
|
|
171
|
-
await settings.set(SETTINGS_KEYS.THEME, validTheme.id);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Full page reload to apply the new theme CSS
|
|
175
|
-
return dsRedirect("/dash/appearance?saved");
|
|
176
|
-
});
|