@jant/core 0.3.35 → 0.3.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/client/assets/module-RjUF93sV.js +716 -0
  2. package/dist/client/assets/native-48B9X9Wg.js +1 -0
  3. package/dist/client/assets/url-8Dj-5CLW.js +1 -0
  4. package/dist/client/client.css +1 -1
  5. package/dist/client/client.js +3109 -2294
  6. package/dist/index.js +3026 -2778
  7. package/package.json +13 -4
  8. package/src/__tests__/helpers/app.ts +1 -1
  9. package/src/__tests__/helpers/db.ts +6 -0
  10. package/src/app.tsx +1 -5
  11. package/src/{lib → client}/avatar-upload.ts +1 -1
  12. package/src/{lib → client}/collection-form-bridge.ts +2 -2
  13. package/src/{ui → client}/components/__tests__/jant-collection-form.test.ts +26 -9
  14. package/src/{ui → client}/components/__tests__/jant-compose-dialog.test.ts +46 -14
  15. package/src/{ui → client}/components/__tests__/jant-compose-editor.test.ts +64 -24
  16. package/src/{ui → client}/components/__tests__/jant-post-form.test.ts +24 -14
  17. package/src/{ui → client}/components/__tests__/jant-settings-general.test.ts +3 -3
  18. package/src/client/components/collection-sidebar-types.ts +45 -0
  19. package/src/{ui → client}/components/collection-types.ts +3 -4
  20. package/src/{ui → client}/components/compose-types.ts +3 -1
  21. package/src/{ui → client}/components/jant-collection-form.ts +301 -182
  22. package/src/client/components/jant-collection-sidebar.ts +801 -0
  23. package/src/{ui → client}/components/jant-compose-dialog.ts +231 -1
  24. package/src/client/components/jant-compose-editor.ts +1249 -0
  25. package/src/client/components/jant-compose-fullscreen.ts +338 -0
  26. package/src/client/components/jant-media-lightbox.ts +257 -0
  27. package/src/{ui → client}/components/jant-nav-manager.ts +143 -84
  28. package/src/{ui → client}/components/jant-post-form.ts +57 -8
  29. package/src/{ui → client}/components/jant-settings-general.ts +2 -2
  30. package/src/{ui → client}/components/nav-manager-types.ts +3 -0
  31. package/src/{ui → client}/components/post-form-template.ts +35 -31
  32. package/src/{ui → client}/components/post-form-types.ts +7 -3
  33. package/src/{lib → client}/compose-bridge.ts +9 -7
  34. package/src/client/lazy-slugify.ts +51 -0
  35. package/src/{lib → client}/media-upload.ts +16 -3
  36. package/src/{lib → client}/nav-manager-bridge.ts +1 -1
  37. package/src/client/page-slug-bridge.ts +42 -0
  38. package/src/{lib → client}/post-form-bridge.ts +2 -2
  39. package/src/{lib → client}/settings-bridge.ts +3 -3
  40. package/src/client/tiptap/bubble-menu.ts +205 -0
  41. package/src/client/tiptap/create-editor.ts +40 -0
  42. package/src/client/tiptap/exitable-marks.ts +73 -0
  43. package/src/client/tiptap/extensions.ts +60 -0
  44. package/src/client/tiptap/image-node.ts +488 -0
  45. package/src/client/tiptap/link-toolbar.ts +371 -0
  46. package/src/client/tiptap/more-break.ts +50 -0
  47. package/src/client/tiptap/paste-image.ts +140 -0
  48. package/src/client/tiptap/slash-commands.ts +328 -0
  49. package/src/{types → client/types}/sortablejs.d.ts +1 -1
  50. package/src/client.ts +24 -17
  51. package/src/db/migrations/0012_add_tiptap_columns.sql +2 -0
  52. package/src/db/migrations/0013_replace_featured_with_visibility.sql +8 -0
  53. package/src/db/schema.ts +6 -1
  54. package/src/i18n/locales/en.po +641 -215
  55. package/src/i18n/locales/en.ts +1 -1
  56. package/src/i18n/locales/zh-Hans.po +642 -204
  57. package/src/i18n/locales/zh-Hans.ts +1 -1
  58. package/src/i18n/locales/zh-Hant.po +642 -204
  59. package/src/i18n/locales/zh-Hant.ts +1 -1
  60. package/src/lib/__tests__/resolve-config.test.ts +2 -2
  61. package/src/lib/__tests__/schemas.test.ts +9 -6
  62. package/src/lib/__tests__/url.test.ts +2 -2
  63. package/src/lib/__tests__/view.test.ts +9 -9
  64. package/src/lib/emoji-catalog.ts +146 -0
  65. package/src/lib/feed.ts +1 -1
  66. package/src/lib/media-helpers.ts +10 -9
  67. package/src/lib/render.tsx +4 -3
  68. package/src/lib/resolve-config.ts +8 -1
  69. package/src/lib/schemas.ts +2 -3
  70. package/src/lib/summary.ts +92 -0
  71. package/src/lib/timeline.ts +2 -0
  72. package/src/lib/tiptap-render.ts +196 -0
  73. package/src/lib/upload.ts +97 -9
  74. package/src/lib/url.ts +7 -23
  75. package/src/lib/view.ts +33 -19
  76. package/src/middleware/error-handler.ts +3 -3
  77. package/src/preset.css +38 -0
  78. package/src/routes/api/collections.ts +20 -3
  79. package/src/routes/api/posts.ts +48 -33
  80. package/src/routes/api/upload.ts +7 -5
  81. package/src/routes/auth/reset.tsx +5 -4
  82. package/src/routes/auth/setup.tsx +26 -11
  83. package/src/routes/auth/signin.tsx +10 -7
  84. package/src/routes/compose.tsx +20 -11
  85. package/src/routes/dash/__tests__/settings-avatar.test.ts +43 -8
  86. package/src/routes/dash/index.tsx +7 -1
  87. package/src/routes/dash/media.tsx +3 -0
  88. package/src/routes/dash/pages.tsx +8 -2
  89. package/src/routes/dash/posts.tsx +6 -2
  90. package/src/routes/dash/redirects.tsx +15 -9
  91. package/src/routes/dash/settings.tsx +336 -32
  92. package/src/routes/feed/__tests__/rss.test.ts +7 -7
  93. package/src/routes/feed/rss.ts +8 -6
  94. package/src/routes/pages/__tests__/featured.test.ts +6 -7
  95. package/src/routes/pages/archive.tsx +11 -7
  96. package/src/routes/pages/collection.tsx +32 -15
  97. package/src/routes/pages/collections.tsx +11 -2
  98. package/src/routes/pages/featured.tsx +1 -1
  99. package/src/routes/pages/home.tsx +1 -1
  100. package/src/services/__tests__/post.test.ts +124 -33
  101. package/src/services/__tests__/settings.test.ts +3 -3
  102. package/src/services/page.ts +16 -3
  103. package/src/services/post.ts +96 -37
  104. package/src/services/search.ts +4 -2
  105. package/src/services/settings.ts +6 -2
  106. package/src/styles/components.css +240 -60
  107. package/src/styles/tokens.css +10 -0
  108. package/src/styles/ui.css +1157 -81
  109. package/src/types/bindings.ts +5 -0
  110. package/src/types/config.ts +23 -1
  111. package/src/types/constants.ts +3 -0
  112. package/src/types/entities.ts +9 -2
  113. package/src/types/operations.ts +9 -3
  114. package/src/types/props.ts +3 -3
  115. package/src/types/views.ts +3 -2
  116. package/src/ui/compose/ComposeDialog.tsx +24 -7
  117. package/src/ui/dash/PageForm.tsx +2 -0
  118. package/src/ui/dash/PostList.tsx +5 -5
  119. package/src/ui/dash/StatusBadge.tsx +13 -5
  120. package/src/ui/dash/appearance/AdvancedContent.tsx +52 -61
  121. package/src/ui/dash/appearance/ColorThemeContent.tsx +30 -35
  122. package/src/ui/dash/appearance/FontThemeContent.tsx +65 -73
  123. package/src/ui/dash/appearance/NavigationContent.tsx +107 -96
  124. package/src/ui/dash/media/MediaListContent.tsx +9 -4
  125. package/src/ui/dash/media/ViewMediaContent.tsx +2 -2
  126. package/src/ui/dash/pages/PagesContent.tsx +2 -1
  127. package/src/ui/dash/posts/PostForm.tsx +19 -7
  128. package/src/ui/dash/settings/AccountContent.tsx +133 -138
  129. package/src/ui/dash/settings/AvatarContent.tsx +70 -0
  130. package/src/ui/dash/settings/GeneralContent.tsx +3 -62
  131. package/src/ui/dash/settings/SettingsRootContent.tsx +236 -0
  132. package/src/ui/layouts/DashLayout.tsx +157 -75
  133. package/src/ui/layouts/SiteLayout.tsx +13 -13
  134. package/src/ui/pages/ArchivePage.tsx +10 -7
  135. package/src/ui/pages/CollectionPage.tsx +6 -35
  136. package/src/ui/pages/CollectionsPage.tsx +2 -1
  137. package/src/ui/pages/FeaturedPage.tsx +2 -1
  138. package/src/ui/pages/HomePage.tsx +1 -1
  139. package/src/ui/pages/SearchPage.tsx +1 -1
  140. package/src/ui/shared/CollectionsSidebar.tsx +228 -3
  141. package/src/ui/shared/MediaGallery.tsx +179 -41
  142. package/src/lib/collections-reorder.ts +0 -28
  143. package/src/routes/dash/appearance.tsx +0 -240
  144. package/src/routes/dash/collections.tsx +0 -211
  145. package/src/ui/components/jant-compose-editor.ts +0 -814
  146. package/src/ui/dash/appearance/AppearanceNav.tsx +0 -60
  147. package/src/ui/dash/collections/CollectionForm.tsx +0 -166
  148. package/src/ui/dash/collections/CollectionsListContent.tsx +0 -146
  149. package/src/ui/dash/collections/IconPickerGrid.tsx +0 -50
  150. package/src/ui/dash/collections/ViewCollectionContent.tsx +0 -103
  151. package/src/ui/dash/settings/SettingsNav.tsx +0 -52
  152. /package/src/{ui → client}/components/__tests__/jant-settings-avatar.test.ts +0 -0
  153. /package/src/{ui → client}/components/jant-settings-avatar.ts +0 -0
  154. /package/src/{ui → client}/components/settings-types.ts +0 -0
  155. /package/src/{lib → client}/image-processor.ts +0 -0
  156. /package/src/{lib → client}/toast.ts +0 -0
@@ -27,6 +27,11 @@ export interface Bindings {
27
27
  S3_SECRET_ACCESS_KEY?: string;
28
28
  S3_REGION?: string;
29
29
  S3_PUBLIC_URL?: string;
30
+ // Upload
31
+ UPLOAD_MAX_FILE_SIZE?: string;
32
+ // Summary extraction
33
+ SUMMARY_MAX_PARAGRAPHS?: string;
34
+ SUMMARY_MAX_CHARS?: string;
30
35
  // RSS feed
31
36
  RSS_FEED_LIMIT?: string;
32
37
  }
@@ -33,7 +33,7 @@ export const CONFIG_FIELDS = {
33
33
  envOnly: false,
34
34
  },
35
35
  HEADER_NAV_MAX_VISIBLE: {
36
- defaultValue: "3",
36
+ defaultValue: "2",
37
37
  envOnly: false,
38
38
  },
39
39
 
@@ -98,6 +98,18 @@ export const CONFIG_FIELDS = {
98
98
  defaultValue: "",
99
99
  envOnly: true,
100
100
  },
101
+ UPLOAD_MAX_FILE_SIZE: {
102
+ defaultValue: "500",
103
+ envOnly: true,
104
+ },
105
+ SUMMARY_MAX_PARAGRAPHS: {
106
+ defaultValue: "5",
107
+ envOnly: true,
108
+ },
109
+ SUMMARY_MAX_CHARS: {
110
+ defaultValue: "500",
111
+ envOnly: true,
112
+ },
101
113
 
102
114
  // Internal settings (DB-only, not configurable via env or dashboard)
103
115
  THEME: {
@@ -195,6 +207,16 @@ export interface AppConfig {
195
207
  s3PublicUrl: string;
196
208
  imageTransformUrl: string;
197
209
 
210
+ // Upload (ENV only, parsed to number)
211
+ /** Max upload file size in MB. Defaults to 500. */
212
+ uploadMaxFileSize: number;
213
+
214
+ // Summary extraction (ENV only)
215
+ /** Max paragraphs to include in auto-extracted summary. Defaults to 5. */
216
+ summaryMaxParagraphs: number;
217
+ /** Max characters to include in auto-extracted summary. Defaults to 500. */
218
+ summaryMaxChars: number;
219
+
198
220
  // Pagination/Feed (ENV only, parsed to number)
199
221
  pageSize: number;
200
222
  rssFeedLimit: number;
@@ -8,6 +8,9 @@ export type Format = (typeof FORMATS)[number];
8
8
  export const STATUSES = ["draft", "published"] as const;
9
9
  export type Status = (typeof STATUSES)[number];
10
10
 
11
+ export const VISIBILITIES = ["listed", "featured", "unlisted"] as const;
12
+ export type Visibility = (typeof VISIBILITIES)[number];
13
+
11
14
  export const SORT_ORDERS = [
12
15
  "newest",
13
16
  "oldest",
@@ -2,13 +2,19 @@
2
2
  * Entity Types (database-level models)
3
3
  */
4
4
 
5
- import type { Format, Status, SortOrder, NavItemType } from "./constants.js";
5
+ import type {
6
+ Format,
7
+ Status,
8
+ Visibility,
9
+ SortOrder,
10
+ NavItemType,
11
+ } from "./constants.js";
6
12
 
7
13
  export interface Post {
8
14
  id: number;
9
15
  format: Format;
10
16
  status: Status;
11
- featured: number; // 0 | 1
17
+ visibility: Visibility;
12
18
  pinned: number; // 0 | 1
13
19
  path: string | null;
14
20
  title: string | null;
@@ -16,6 +22,7 @@ export interface Post {
16
22
  body: string | null;
17
23
  bodyHtml: string | null;
18
24
  quoteText: string | null;
25
+ summary: string | null;
19
26
  rating: number | null;
20
27
  replyToId: number | null;
21
28
  threadId: number | null;
@@ -2,12 +2,18 @@
2
2
  * Operation Types (create/update DTOs)
3
3
  */
4
4
 
5
- import type { Format, Status, SortOrder, NavItemType } from "./constants.js";
5
+ import type {
6
+ Format,
7
+ Status,
8
+ Visibility,
9
+ SortOrder,
10
+ NavItemType,
11
+ } from "./constants.js";
6
12
 
7
13
  export interface CreatePost {
8
14
  format: Format;
9
15
  status?: Status;
10
- featured?: boolean;
16
+ visibility?: Visibility;
11
17
  pinned?: boolean;
12
18
  path?: string;
13
19
  title?: string;
@@ -24,7 +30,7 @@ export interface CreatePost {
24
30
  export interface UpdatePost {
25
31
  format?: Format;
26
32
  status?: Status;
27
- featured?: boolean;
33
+ visibility?: Visibility;
28
34
  pinned?: boolean;
29
35
  path?: string | null;
30
36
  title?: string | null;
@@ -2,7 +2,7 @@
2
2
  * Page-Level Props & Feed Data Types
3
3
  */
4
4
 
5
- import type { Format } from "./constants.js";
5
+ import type { Format, Visibility } from "./constants.js";
6
6
  import type { Collection } from "./entities.js";
7
7
  import type {
8
8
  PostView,
@@ -45,7 +45,7 @@ export interface ArchivePageProps {
45
45
  hasMore: boolean;
46
46
  nextCursor?: number;
47
47
  format?: Format;
48
- featured?: boolean;
48
+ visibility?: Visibility;
49
49
  }
50
50
 
51
51
  /** Props for the search page component */
@@ -60,7 +60,7 @@ export interface SearchPageProps {
60
60
  /** Props for the single collection page component */
61
61
  export interface CollectionPageProps {
62
62
  collection: Collection;
63
- posts: PostView[];
63
+ items: TimelineItemView[];
64
64
  hasMore: boolean;
65
65
  nextCursor?: number;
66
66
  }
@@ -2,7 +2,7 @@
2
2
  * View Model Types (render-ready, for theme components)
3
3
  */
4
4
 
5
- import type { Format, Status, NavItemType } from "./constants.js";
5
+ import type { Format, Status, Visibility, NavItemType } from "./constants.js";
6
6
  import type { Post, Collection } from "./entities.js";
7
7
 
8
8
  /**
@@ -35,7 +35,7 @@ export interface PostView {
35
35
  // Metadata
36
36
  format: Format;
37
37
  status: Status;
38
- featured: boolean;
38
+ visibility: Visibility;
39
39
  pinned: boolean;
40
40
  rating?: number;
41
41
 
@@ -167,4 +167,5 @@ export interface SiteLayoutProps {
167
167
  showHeaderAvatar?: boolean;
168
168
  siteFooterHtml?: string;
169
169
  sidebar?: import("hono/jsx").Child;
170
+ uploadMaxFileSize?: number;
170
171
  }
@@ -15,9 +15,13 @@ import { useLingui } from "@lingui/react/macro";
15
15
 
16
16
  export interface ComposeDialogProps {
17
17
  collections?: Collection[];
18
+ uploadMaxFileSize?: number;
18
19
  }
19
20
 
20
- export const ComposeDialog: FC<ComposeDialogProps> = ({ collections }) => {
21
+ export const ComposeDialog: FC<ComposeDialogProps> = ({
22
+ collections,
23
+ uploadMaxFileSize,
24
+ }) => {
21
25
  const { t } = useLingui();
22
26
 
23
27
  const labels = JSON.stringify({
@@ -90,9 +94,13 @@ export const ComposeDialog: FC<ComposeDialogProps> = ({ collections }) => {
90
94
  message: "Media",
91
95
  comment: "@context: Compose toolbar - media tooltip",
92
96
  }),
93
- score: t({
94
- message: "Score",
95
- comment: "@context: Compose toolbar - score tooltip",
97
+ rate: t({
98
+ message: "Rate",
99
+ comment: "@context: Compose toolbar - rate tooltip",
100
+ }),
101
+ emoji: t({
102
+ message: "Emoji",
103
+ comment: "@context: Compose toolbar - emoji picker tooltip",
96
104
  }),
97
105
  title: t({
98
106
  message: "Title",
@@ -107,7 +115,7 @@ export const ComposeDialog: FC<ComposeDialogProps> = ({ collections }) => {
107
115
  comment: "@context: Compose collection combobox search placeholder",
108
116
  }),
109
117
  noCollections: t({
110
- message: "No collections found.",
118
+ message: "No matching collections.",
111
119
  comment: "@context: Compose collection combobox empty state",
112
120
  }),
113
121
  post: t({
@@ -127,7 +135,7 @@ export const ComposeDialog: FC<ComposeDialogProps> = ({ collections }) => {
127
135
  comment: "@context: Alt text textarea placeholder",
128
136
  }),
129
137
  altHint: t({
130
- message: "Alt text improves accessibility",
138
+ message: "Helps screen readers describe the image",
131
139
  comment: "@context: Hint text in alt text panel",
132
140
  }),
133
141
  addMore: t({
@@ -142,6 +150,11 @@ export const ComposeDialog: FC<ComposeDialogProps> = ({ collections }) => {
142
150
  message: "Published!",
143
151
  comment: "@context: Toast shown after successful deferred publish",
144
152
  }),
153
+ retryAll: t({
154
+ message: "Click to retry all",
155
+ comment:
156
+ "@context: Tooltip hint on failed upload overlay, tells user clicking retries all failed uploads",
157
+ }),
145
158
  }).replace(/</g, "\\u003c");
146
159
 
147
160
  const collectionsJson = JSON.stringify(
@@ -158,7 +171,11 @@ export const ComposeDialog: FC<ComposeDialogProps> = ({ collections }) => {
158
171
  class="compose-dialog"
159
172
  onclick="event.target === this && this.close()"
160
173
  >
161
- <jant-compose-dialog collections={collectionsJson} labels={labels}>
174
+ <jant-compose-dialog
175
+ collections={collectionsJson}
176
+ labels={labels}
177
+ upload-max-file-size={uploadMaxFileSize ?? 500}
178
+ >
162
179
  {/* SSR fallback skeleton */}
163
180
  <div class="compose-dialog-inner">
164
181
  <div class="compose-dialog-header" />
@@ -31,6 +31,8 @@ export const PageForm: FC<PageFormProps> = ({
31
31
 
32
32
  return (
33
33
  <form
34
+ data-page-form
35
+ {...(isEdit ? { "data-page-edit": "" } : {})}
34
36
  data-signals={signals}
35
37
  data-on:submit__prevent={`@post('${action}')`}
36
38
  data-indicator="_loading"
@@ -22,11 +22,12 @@ export const PostList: FC<PostListProps> = ({ posts }) => {
22
22
  return (
23
23
  <EmptyState
24
24
  message={t({
25
- message: "No posts yet.",
25
+ message:
26
+ "Nothing published yet. Write your first post to get started.",
26
27
  comment: "@context: Empty state message when no posts exist",
27
28
  })}
28
29
  ctaText={t({
29
- message: "Create your first post",
30
+ message: "Write your first post",
30
31
  comment: "@context: Button in empty state to create first post",
31
32
  })}
32
33
  ctaHref="/dash/posts/new"
@@ -53,8 +54,7 @@ export const PostList: FC<PostListProps> = ({ posts }) => {
53
54
  })}
54
55
  deleteAction={`/dash/posts/${sqid.encode(post.id)}/delete`}
55
56
  deleteConfirm={t({
56
- message:
57
- "Are you sure you want to delete this post? This cannot be undone.",
57
+ message: "Delete this post permanently? This can't be undone.",
58
58
  comment:
59
59
  "@context: Confirmation dialog when deleting a post from the list",
60
60
  })}
@@ -65,7 +65,7 @@ export const PostList: FC<PostListProps> = ({ posts }) => {
65
65
  <FormatBadge type={post.format} />
66
66
  <StatusBadge
67
67
  status={post.status}
68
- featured={post.featured}
68
+ visibility={post.visibility}
69
69
  pinned={post.pinned}
70
70
  />
71
71
  <span class="text-xs text-muted-foreground">
@@ -1,22 +1,22 @@
1
1
  /**
2
2
  * Status Badge Component
3
3
  *
4
- * Displays badges for post status, featured, and pinned state.
4
+ * Displays badges for post status, visibility, and pinned state.
5
5
  */
6
6
 
7
7
  import type { FC } from "hono/jsx";
8
8
  import { useLingui } from "@lingui/react/macro";
9
- import type { Status } from "../../types.js";
9
+ import type { Status, Visibility } from "../../types.js";
10
10
 
11
11
  export interface StatusBadgeProps {
12
12
  status: Status;
13
- featured?: boolean;
13
+ visibility?: Visibility;
14
14
  pinned?: boolean;
15
15
  }
16
16
 
17
17
  export const StatusBadge: FC<StatusBadgeProps> = ({
18
18
  status,
19
- featured,
19
+ visibility,
20
20
  pinned,
21
21
  }) => {
22
22
  const { t } = useLingui();
@@ -40,7 +40,7 @@ export const StatusBadge: FC<StatusBadgeProps> = ({
40
40
  return (
41
41
  <span class="flex items-center gap-1">
42
42
  <span class={statusVariants[status]}>{statusLabels[status]}</span>
43
- {featured && (
43
+ {visibility === "featured" && (
44
44
  <span class="badge-primary">
45
45
  {t({
46
46
  message: "Featured",
@@ -48,6 +48,14 @@ export const StatusBadge: FC<StatusBadgeProps> = ({
48
48
  })}
49
49
  </span>
50
50
  )}
51
+ {visibility === "unlisted" && (
52
+ <span class="badge-outline">
53
+ {t({
54
+ message: "Unlisted",
55
+ comment: "@context: Post badge - unlisted",
56
+ })}
57
+ </span>
58
+ )}
51
59
  {pinned && (
52
60
  <span class="badge-outline">
53
61
  {t({
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import { useLingui } from "@lingui/react/macro";
6
- import { AppearanceNav } from "./AppearanceNav.js";
7
6
 
8
7
  export function AdvancedContent({ customCSS }: { customCSS: string }) {
9
8
  const { t } = useLingui();
@@ -11,67 +10,59 @@ export function AdvancedContent({ customCSS }: { customCSS: string }) {
11
10
  const cssSignals = JSON.stringify({ customCSS }).replace(/</g, "\\u003c");
12
11
 
13
12
  return (
14
- <>
15
- <AppearanceNav currentTab="advanced" />
16
-
17
- <form
18
- data-signals={cssSignals}
19
- data-on:submit__prevent="@post('/dash/appearance/custom-css')"
20
- data-indicator="_cssLoading"
21
- class="max-w-3xl"
22
- >
23
- <fieldset>
24
- <legend class="text-lg font-semibold">
25
- {t({
26
- message: "Custom CSS",
27
- comment: "@context: Appearance settings heading for custom CSS",
28
- })}
29
- </legend>
30
- <p class="text-sm text-muted-foreground mb-4">
31
- {t({
32
- message:
33
- "Add custom CSS to override any styles. Use data attributes like [data-page], [data-post], [data-format] to target specific elements.",
34
- comment: "@context: Custom CSS settings description",
35
- })}
36
- </p>
37
- <textarea
38
- data-bind="customCSS"
39
- class="textarea font-mono text-sm min-h-32"
40
- rows={8}
41
- placeholder={t({
42
- message: "/* Your custom CSS here */",
43
- comment: "@context: Custom CSS textarea placeholder",
44
- })}
45
- >
46
- {customCSS}
47
- </textarea>
48
- </fieldset>
49
- <button
50
- type="submit"
51
- class="btn mt-4"
52
- data-attr:disabled="$_cssLoading"
53
- >
54
- <svg
55
- data-show="$_cssLoading"
56
- style="display:none"
57
- class="animate-spin size-4"
58
- xmlns="http://www.w3.org/2000/svg"
59
- viewBox="0 0 24 24"
60
- fill="none"
61
- stroke="currentColor"
62
- stroke-width="2"
63
- stroke-linecap="round"
64
- stroke-linejoin="round"
65
- role="status"
66
- >
67
- <path d="M21 12a9 9 0 1 1-6.219-8.56" />
68
- </svg>
13
+ <form
14
+ data-signals={cssSignals}
15
+ data-on:submit__prevent="@post('/dash/settings/custom-css')"
16
+ data-indicator="_cssLoading"
17
+ class="max-w-3xl"
18
+ >
19
+ <fieldset>
20
+ <legend class="text-lg font-semibold">
21
+ {t({
22
+ message: "Custom CSS",
23
+ comment: "@context: Appearance settings heading for custom CSS",
24
+ })}
25
+ </legend>
26
+ <p class="text-sm text-muted-foreground mb-4">
69
27
  {t({
70
- message: "Save CSS",
71
- comment: "@context: Button to save custom CSS",
28
+ message:
29
+ "Add custom CSS to override any styles. Use data attributes like [data-page], [data-post], [data-format] to target specific elements.",
30
+ comment: "@context: Custom CSS settings description",
72
31
  })}
73
- </button>
74
- </form>
75
- </>
32
+ </p>
33
+ <textarea
34
+ data-bind="customCSS"
35
+ class="textarea font-mono text-sm min-h-32"
36
+ rows={8}
37
+ placeholder={t({
38
+ message: "/* Your custom CSS here */",
39
+ comment: "@context: Custom CSS textarea placeholder",
40
+ })}
41
+ >
42
+ {customCSS}
43
+ </textarea>
44
+ </fieldset>
45
+ <button type="submit" class="btn mt-4" data-attr:disabled="$_cssLoading">
46
+ <svg
47
+ data-show="$_cssLoading"
48
+ style="display:none"
49
+ class="animate-spin size-4"
50
+ xmlns="http://www.w3.org/2000/svg"
51
+ viewBox="0 0 24 24"
52
+ fill="none"
53
+ stroke="currentColor"
54
+ stroke-width="2"
55
+ stroke-linecap="round"
56
+ stroke-linejoin="round"
57
+ role="status"
58
+ >
59
+ <path d="M21 12a9 9 0 1 1-6.219-8.56" />
60
+ </svg>
61
+ {t({
62
+ message: "Save CSS",
63
+ comment: "@context: Button to save custom CSS",
64
+ })}
65
+ </button>
66
+ </form>
76
67
  );
77
68
  }
@@ -4,7 +4,6 @@
4
4
 
5
5
  import { useLingui } from "@lingui/react/macro";
6
6
  import type { ColorTheme } from "../../color-themes.js";
7
- import { AppearanceNav } from "./AppearanceNav.js";
8
7
 
9
8
  function ThemeCard({
10
9
  theme,
@@ -87,40 +86,36 @@ export function ColorThemeContent({
87
86
  );
88
87
 
89
88
  return (
90
- <>
91
- <AppearanceNav currentTab="color" />
92
-
93
- <div
94
- data-signals={themeSignals}
95
- data-on:change="@post('/dash/appearance/color')"
96
- class="max-w-3xl"
97
- >
98
- <fieldset>
99
- <legend class="text-lg font-semibold">
100
- {t({
101
- message: "Color theme",
102
- comment: "@context: Appearance settings heading",
103
- })}
104
- </legend>
105
- <p class="text-sm text-muted-foreground mb-4">
106
- {t({
107
- message:
108
- "This will theme both your site and your dashboard. All color themes support dark mode.",
109
- comment: "@context: Appearance settings description",
110
- })}
111
- </p>
89
+ <div
90
+ data-signals={themeSignals}
91
+ data-on:change="@post('/dash/settings/color-theme')"
92
+ class="max-w-3xl"
93
+ >
94
+ <fieldset>
95
+ <legend class="text-lg font-semibold">
96
+ {t({
97
+ message: "Color theme",
98
+ comment: "@context: Appearance settings heading",
99
+ })}
100
+ </legend>
101
+ <p class="text-sm text-muted-foreground mb-4">
102
+ {t({
103
+ message:
104
+ "This will theme both your site and your dashboard. All color themes support dark mode.",
105
+ comment: "@context: Appearance settings description",
106
+ })}
107
+ </p>
112
108
 
113
- <div class="flex flex-col gap-4">
114
- {themes.map((theme) => (
115
- <ThemeCard
116
- key={theme.id}
117
- theme={theme}
118
- selected={theme.id === currentThemeId}
119
- />
120
- ))}
121
- </div>
122
- </fieldset>
123
- </div>
124
- </>
109
+ <div class="flex flex-col gap-4">
110
+ {themes.map((theme) => (
111
+ <ThemeCard
112
+ key={theme.id}
113
+ theme={theme}
114
+ selected={theme.id === currentThemeId}
115
+ />
116
+ ))}
117
+ </div>
118
+ </fieldset>
119
+ </div>
125
120
  );
126
121
  }