@jant/core 0.3.25 → 0.3.26

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 (131) hide show
  1. package/dist/app.js +67 -562
  2. package/dist/client.js +1 -0
  3. package/dist/i18n/locales/en.js +1 -1
  4. package/dist/i18n/locales/zh-Hans.js +1 -1
  5. package/dist/i18n/locales/zh-Hant.js +1 -1
  6. package/dist/lib/avatar-upload.js +134 -0
  7. package/dist/lib/config.js +39 -0
  8. package/dist/lib/constants.js +10 -10
  9. package/dist/lib/favicon.js +102 -0
  10. package/dist/lib/image.js +13 -17
  11. package/dist/lib/media-helpers.js +2 -2
  12. package/dist/lib/navigation.js +23 -3
  13. package/dist/lib/render.js +10 -1
  14. package/dist/lib/schemas.js +31 -0
  15. package/dist/lib/timezones.js +388 -0
  16. package/dist/lib/view.js +1 -1
  17. package/dist/routes/api/posts.js +1 -1
  18. package/dist/routes/api/upload.js +3 -3
  19. package/dist/routes/auth/reset.js +221 -0
  20. package/dist/routes/auth/setup.js +194 -0
  21. package/dist/routes/auth/signin.js +176 -0
  22. package/dist/routes/dash/collections.js +23 -415
  23. package/dist/routes/dash/media.js +12 -392
  24. package/dist/routes/dash/pages.js +7 -330
  25. package/dist/routes/dash/redirects.js +18 -12
  26. package/dist/routes/dash/settings.js +198 -577
  27. package/dist/routes/feed/rss.js +2 -1
  28. package/dist/routes/feed/sitemap.js +4 -2
  29. package/dist/routes/pages/featured.js +5 -1
  30. package/dist/routes/pages/home.js +26 -1
  31. package/dist/routes/pages/latest.js +45 -0
  32. package/dist/services/post.js +30 -50
  33. package/dist/types/bindings.js +3 -0
  34. package/dist/types/config.js +147 -0
  35. package/dist/types/constants.js +27 -0
  36. package/dist/types/entities.js +3 -0
  37. package/dist/types/operations.js +3 -0
  38. package/dist/types/props.js +3 -0
  39. package/dist/types/views.js +5 -0
  40. package/dist/types.js +8 -111
  41. package/dist/ui/color-themes.js +33 -33
  42. package/dist/ui/compose/ComposeDialog.js +36 -21
  43. package/dist/ui/dash/PageForm.js +21 -15
  44. package/dist/ui/dash/PostForm.js +22 -16
  45. package/dist/ui/dash/collections/CollectionForm.js +152 -0
  46. package/dist/ui/dash/collections/CollectionsListContent.js +68 -0
  47. package/dist/ui/dash/collections/ViewCollectionContent.js +96 -0
  48. package/dist/ui/dash/media/MediaListContent.js +166 -0
  49. package/dist/ui/dash/media/ViewMediaContent.js +212 -0
  50. package/dist/ui/dash/pages/LinkFormContent.js +130 -0
  51. package/dist/ui/dash/pages/UnifiedPagesContent.js +193 -0
  52. package/dist/ui/dash/settings/AccountContent.js +209 -0
  53. package/dist/ui/dash/settings/AppearanceContent.js +259 -0
  54. package/dist/ui/dash/settings/GeneralContent.js +536 -0
  55. package/dist/ui/dash/settings/SettingsNav.js +41 -0
  56. package/dist/ui/font-themes.js +36 -0
  57. package/dist/ui/layouts/BaseLayout.js +24 -2
  58. package/dist/ui/layouts/SiteLayout.js +47 -19
  59. package/package.json +1 -1
  60. package/src/app.tsx +93 -553
  61. package/src/client.ts +1 -0
  62. package/src/i18n/locales/en.po +240 -175
  63. package/src/i18n/locales/en.ts +1 -1
  64. package/src/i18n/locales/zh-Hans.po +240 -175
  65. package/src/i18n/locales/zh-Hans.ts +1 -1
  66. package/src/i18n/locales/zh-Hant.po +240 -175
  67. package/src/i18n/locales/zh-Hant.ts +1 -1
  68. package/src/lib/__tests__/config.test.ts +192 -0
  69. package/src/lib/__tests__/favicon.test.ts +151 -0
  70. package/src/lib/__tests__/image.test.ts +2 -6
  71. package/src/lib/__tests__/timezones.test.ts +61 -0
  72. package/src/lib/__tests__/view.test.ts +2 -2
  73. package/src/lib/avatar-upload.ts +165 -0
  74. package/src/lib/config.ts +47 -0
  75. package/src/lib/constants.ts +19 -11
  76. package/src/lib/favicon.ts +115 -0
  77. package/src/lib/image.ts +13 -21
  78. package/src/lib/media-helpers.ts +2 -2
  79. package/src/lib/navigation.ts +33 -2
  80. package/src/lib/render.tsx +15 -1
  81. package/src/lib/schemas.ts +39 -0
  82. package/src/lib/timezones.ts +325 -0
  83. package/src/lib/view.ts +1 -1
  84. package/src/routes/api/posts.ts +1 -1
  85. package/src/routes/api/upload.ts +2 -3
  86. package/src/routes/auth/reset.tsx +239 -0
  87. package/src/routes/auth/setup.tsx +189 -0
  88. package/src/routes/auth/signin.tsx +163 -0
  89. package/src/routes/dash/__tests__/settings-avatar.test.ts +89 -0
  90. package/src/routes/dash/collections.tsx +17 -366
  91. package/src/routes/dash/media.tsx +12 -414
  92. package/src/routes/dash/pages.tsx +8 -348
  93. package/src/routes/dash/redirects.tsx +20 -14
  94. package/src/routes/dash/settings.tsx +243 -534
  95. package/src/routes/feed/__tests__/rss.test.ts +141 -0
  96. package/src/routes/feed/rss.ts +3 -1
  97. package/src/routes/feed/sitemap.ts +4 -2
  98. package/src/routes/pages/featured.tsx +7 -1
  99. package/src/routes/pages/home.tsx +25 -2
  100. package/src/routes/pages/latest.tsx +59 -0
  101. package/src/services/post.ts +34 -66
  102. package/src/styles/components.css +0 -65
  103. package/src/styles/tokens.css +1 -1
  104. package/src/styles/ui.css +24 -40
  105. package/src/types/bindings.ts +30 -0
  106. package/src/types/config.ts +183 -0
  107. package/src/types/constants.ts +26 -0
  108. package/src/types/entities.ts +109 -0
  109. package/src/types/operations.ts +88 -0
  110. package/src/types/props.ts +115 -0
  111. package/src/types/views.ts +172 -0
  112. package/src/types.ts +8 -644
  113. package/src/ui/__tests__/font-themes.test.ts +34 -0
  114. package/src/ui/color-themes.ts +34 -34
  115. package/src/ui/compose/ComposeDialog.tsx +40 -21
  116. package/src/ui/dash/PageForm.tsx +25 -19
  117. package/src/ui/dash/PostForm.tsx +26 -20
  118. package/src/ui/dash/collections/CollectionForm.tsx +153 -0
  119. package/src/ui/dash/collections/CollectionsListContent.tsx +85 -0
  120. package/src/ui/dash/collections/ViewCollectionContent.tsx +92 -0
  121. package/src/ui/dash/media/MediaListContent.tsx +201 -0
  122. package/src/ui/dash/media/ViewMediaContent.tsx +208 -0
  123. package/src/ui/dash/pages/LinkFormContent.tsx +119 -0
  124. package/src/ui/dash/pages/UnifiedPagesContent.tsx +203 -0
  125. package/src/ui/dash/settings/AccountContent.tsx +176 -0
  126. package/src/ui/dash/settings/AppearanceContent.tsx +254 -0
  127. package/src/ui/dash/settings/GeneralContent.tsx +533 -0
  128. package/src/ui/dash/settings/SettingsNav.tsx +56 -0
  129. package/src/ui/font-themes.ts +54 -0
  130. package/src/ui/layouts/BaseLayout.tsx +17 -0
  131. package/src/ui/layouts/SiteLayout.tsx +45 -31
@@ -14,10 +14,11 @@ export const rssRoutes = new Hono();
14
14
  const siteDescription = all["SITE_DESCRIPTION"] ?? "";
15
15
  const siteUrl = c.env.SITE_URL;
16
16
  const siteLanguage = await getSiteLanguage(c);
17
+ const feedLimit = parseInt(c.env.RSS_FEED_LIMIT ?? "50", 10) || 50;
17
18
  const posts = await c.var.services.posts.list({
18
19
  status: "published",
19
20
  excludeReplies: true,
20
- limit: 50
21
+ limit: feedLimit
21
22
  });
22
23
  // Batch load media for enclosures
23
24
  const postIds = posts.map((p)=>p.id);
@@ -32,10 +32,12 @@ sitemapRoutes.get("/sitemap.xml", async (c)=>{
32
32
  });
33
33
  });
34
34
  // robots.txt
35
- sitemapRoutes.get("/robots.txt", (c)=>{
35
+ sitemapRoutes.get("/robots.txt", async (c)=>{
36
36
  const siteUrl = c.env.SITE_URL;
37
+ const noindex = await c.var.services.settings.get("NOINDEX") === "true";
38
+ const directive = noindex ? "Disallow: /" : "Allow: /";
37
39
  const robots = `User-agent: *
38
- Allow: /
40
+ ${directive}
39
41
 
40
42
  Sitemap: ${siteUrl}/sitemap.xml
41
43
  `;
@@ -10,12 +10,16 @@ import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
10
10
  import { FeaturedPage } from "../../ui/pages/FeaturedPage.js";
11
11
  export const featuredRoutes = new Hono();
12
12
  featuredRoutes.get("/", async (c)=>{
13
+ const navData = await getNavigationData(c);
14
+ // When homepage already shows featured, redirect to avoid duplicate content
15
+ if (navData.homeDefaultView === "featured") {
16
+ return c.redirect("/", 302);
17
+ }
13
18
  const posts = await c.var.services.posts.list({
14
19
  featured: true,
15
20
  status: "published",
16
21
  excludeReplies: true
17
22
  });
18
- const navData = await getNavigationData(c);
19
23
  const mediaCtx = createMediaContext(c);
20
24
  const postViews = toPostViewsFromPosts(posts, mediaCtx);
21
25
  // Convert to timeline items (simple — no thread previews)
@@ -4,20 +4,45 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
4
4
  *
5
5
  * Timeline feed with per-type card components and thread previews.
6
6
  * Uses page-based pagination.
7
+ *
8
+ * When HOME_DEFAULT_VIEW is "featured", the homepage shows featured posts
9
+ * instead of latest. The /latest route always shows latest posts explicitly.
7
10
  */ import { Hono } from "hono";
8
11
  import { getNavigationData } from "../../lib/navigation.js";
9
12
  import { renderPublicPage } from "../../lib/render.js";
10
13
  import { assembleTimeline } from "../../lib/timeline.js";
11
14
  import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
12
15
  import { HomePage } from "../../ui/pages/HomePage.js";
16
+ import { FeaturedPage } from "../../ui/pages/FeaturedPage.js";
13
17
  export const homeRoutes = new Hono();
14
18
  homeRoutes.get("/", async (c)=>{
19
+ const navData = await getNavigationData(c);
20
+ if (navData.homeDefaultView === "featured") {
21
+ // Show featured posts on homepage
22
+ const posts = await c.var.services.posts.list({
23
+ featured: true,
24
+ status: "published",
25
+ excludeReplies: true
26
+ });
27
+ const mediaCtx = createMediaContext(c);
28
+ const postViews = toPostViewsFromPosts(posts, mediaCtx);
29
+ const items = postViews.map((post)=>({
30
+ post
31
+ }));
32
+ return renderPublicPage(c, {
33
+ title: navData.siteName,
34
+ navData,
35
+ content: /*#__PURE__*/ _jsx(FeaturedPage, {
36
+ items: items
37
+ })
38
+ });
39
+ }
40
+ // Default: show latest posts
15
41
  const pageParam = c.req.query("page");
16
42
  const page = pageParam ? Math.max(1, parseInt(pageParam, 10) || 1) : 1;
17
43
  const { items, currentPage, totalPages } = await assembleTimeline(c, {
18
44
  page
19
45
  });
20
- const navData = await getNavigationData(c);
21
46
  // Fetch pinned posts
22
47
  const pinnedPosts = await c.var.services.posts.list({
23
48
  pinned: true,
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
2
+ /**
3
+ * Latest Page Route
4
+ *
5
+ * Explicit /latest URL that always shows the latest posts timeline.
6
+ * When HOME_DEFAULT_VIEW is "latest" (default), this redirects to /
7
+ * to avoid duplicate content. When it's "featured", this serves as
8
+ * the explicit latest view.
9
+ */ import { Hono } from "hono";
10
+ import { getNavigationData } from "../../lib/navigation.js";
11
+ import { renderPublicPage } from "../../lib/render.js";
12
+ import { assembleTimeline } from "../../lib/timeline.js";
13
+ import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
14
+ import { HomePage } from "../../ui/pages/HomePage.js";
15
+ export const latestRoutes = new Hono();
16
+ latestRoutes.get("/", async (c)=>{
17
+ const navData = await getNavigationData(c);
18
+ // When homepage already shows latest, redirect to avoid duplicate content
19
+ if (navData.homeDefaultView !== "featured") {
20
+ return c.redirect("/", 302);
21
+ }
22
+ const pageParam = c.req.query("page");
23
+ const page = pageParam ? Math.max(1, parseInt(pageParam, 10) || 1) : 1;
24
+ const { items, currentPage, totalPages } = await assembleTimeline(c, {
25
+ page
26
+ });
27
+ // Fetch pinned posts
28
+ const pinnedPosts = await c.var.services.posts.list({
29
+ pinned: true,
30
+ status: "published",
31
+ excludeReplies: true
32
+ });
33
+ const mediaCtx = createMediaContext(c);
34
+ const pinnedItems = toPostViewsFromPosts(pinnedPosts, mediaCtx);
35
+ return renderPublicPage(c, {
36
+ title: `Latest - ${navData.siteName}`,
37
+ navData,
38
+ content: /*#__PURE__*/ _jsx(HomePage, {
39
+ items: items,
40
+ pinnedItems: pinnedItems,
41
+ currentPage: currentPage,
42
+ totalPages: totalPages
43
+ })
44
+ });
45
+ });
@@ -9,6 +9,34 @@ import { posts } from "../db/schema.js";
9
9
  import { now } from "../lib/time.js";
10
10
  import { render as renderMarkdown } from "../lib/markdown.js";
11
11
  export function createPostService(db) {
12
+ /** Build WHERE conditions from filters (shared by list and count) */ function buildFilterConditions(filters) {
13
+ const conditions = [];
14
+ if (filters.status) {
15
+ conditions.push(eq(posts.status, filters.status));
16
+ }
17
+ if (filters.featured !== undefined) {
18
+ conditions.push(eq(posts.featured, filters.featured ? 1 : 0));
19
+ }
20
+ if (filters.pinned !== undefined) {
21
+ conditions.push(eq(posts.pinned, filters.pinned ? 1 : 0));
22
+ }
23
+ if (filters.format) {
24
+ conditions.push(eq(posts.format, filters.format));
25
+ }
26
+ if (filters.collectionId !== undefined) {
27
+ conditions.push(eq(posts.collectionId, filters.collectionId));
28
+ }
29
+ if (filters.threadId) {
30
+ conditions.push(eq(posts.threadId, filters.threadId));
31
+ }
32
+ if (filters.excludeReplies) {
33
+ conditions.push(isNull(posts.threadId));
34
+ }
35
+ if (!filters.includeDeleted) {
36
+ conditions.push(isNull(posts.deletedAt));
37
+ }
38
+ return conditions;
39
+ }
12
40
  function toPost(row) {
13
41
  return {
14
42
  id: row.id,
@@ -42,31 +70,7 @@ export function createPostService(db) {
42
70
  return result[0] ? toPost(result[0]) : null;
43
71
  },
44
72
  async list (filters = {}) {
45
- const conditions = [];
46
- if (filters.status) {
47
- conditions.push(eq(posts.status, filters.status));
48
- }
49
- if (filters.featured !== undefined) {
50
- conditions.push(eq(posts.featured, filters.featured ? 1 : 0));
51
- }
52
- if (filters.pinned !== undefined) {
53
- conditions.push(eq(posts.pinned, filters.pinned ? 1 : 0));
54
- }
55
- if (filters.format) {
56
- conditions.push(eq(posts.format, filters.format));
57
- }
58
- if (filters.collectionId !== undefined) {
59
- conditions.push(eq(posts.collectionId, filters.collectionId));
60
- }
61
- if (filters.threadId) {
62
- conditions.push(eq(posts.threadId, filters.threadId));
63
- }
64
- if (filters.excludeReplies) {
65
- conditions.push(isNull(posts.threadId));
66
- }
67
- if (!filters.includeDeleted) {
68
- conditions.push(isNull(posts.deletedAt));
69
- }
73
+ const conditions = buildFilterConditions(filters);
70
74
  if (filters.cursor) {
71
75
  conditions.push(sql`${posts.id} < ${filters.cursor}`);
72
76
  }
@@ -78,31 +82,7 @@ export function createPostService(db) {
78
82
  return rows.map(toPost);
79
83
  },
80
84
  async count (filters = {}) {
81
- const conditions = [];
82
- if (filters.status) {
83
- conditions.push(eq(posts.status, filters.status));
84
- }
85
- if (filters.featured !== undefined) {
86
- conditions.push(eq(posts.featured, filters.featured ? 1 : 0));
87
- }
88
- if (filters.pinned !== undefined) {
89
- conditions.push(eq(posts.pinned, filters.pinned ? 1 : 0));
90
- }
91
- if (filters.format) {
92
- conditions.push(eq(posts.format, filters.format));
93
- }
94
- if (filters.collectionId !== undefined) {
95
- conditions.push(eq(posts.collectionId, filters.collectionId));
96
- }
97
- if (filters.threadId) {
98
- conditions.push(eq(posts.threadId, filters.threadId));
99
- }
100
- if (filters.excludeReplies) {
101
- conditions.push(isNull(posts.threadId));
102
- }
103
- if (!filters.includeDeleted) {
104
- conditions.push(isNull(posts.deletedAt));
105
- }
85
+ const conditions = buildFilterConditions(filters);
106
86
  const result = await db.select({
107
87
  count: sql`count(*)`.as("count")
108
88
  }).from(posts).where(conditions.length > 0 ? and(...conditions) : undefined);
@@ -0,0 +1,3 @@
1
+ /**
2
+ * Cloudflare Worker Bindings
3
+ */ export { };
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Configuration System
3
+ *
4
+ * Single Source of Truth for all configuration fields.
5
+ */ /**
6
+ * Configuration Registry - Single Source of Truth
7
+ *
8
+ * All available configuration fields with their metadata.
9
+ * Add new fields here, and they'll automatically work everywhere.
10
+ *
11
+ * Priority logic:
12
+ * - envOnly: false -> User-configurable (DB > ENV > Default)
13
+ * - envOnly: true -> Environment-only (ENV > Default)
14
+ */ export const CONFIG_FIELDS = {
15
+ // User-configurable (can be modified in dashboard)
16
+ SITE_NAME: {
17
+ defaultValue: "Jant",
18
+ envOnly: false
19
+ },
20
+ SITE_DESCRIPTION: {
21
+ defaultValue: "A microblog powered by Jant",
22
+ envOnly: false
23
+ },
24
+ SITE_LANGUAGE: {
25
+ defaultValue: "en",
26
+ envOnly: false
27
+ },
28
+ HOME_DEFAULT_VIEW: {
29
+ defaultValue: "latest",
30
+ envOnly: false
31
+ },
32
+ // Environment-only (deployment/infrastructure config)
33
+ SITE_URL: {
34
+ defaultValue: "",
35
+ envOnly: true
36
+ },
37
+ AUTH_SECRET: {
38
+ defaultValue: "",
39
+ envOnly: true
40
+ },
41
+ R2_PUBLIC_URL: {
42
+ defaultValue: "",
43
+ envOnly: true
44
+ },
45
+ IMAGE_TRANSFORM_URL: {
46
+ defaultValue: "",
47
+ envOnly: true
48
+ },
49
+ DEMO_EMAIL: {
50
+ defaultValue: "",
51
+ envOnly: true
52
+ },
53
+ DEMO_PASSWORD: {
54
+ defaultValue: "",
55
+ envOnly: true
56
+ },
57
+ PAGE_SIZE: {
58
+ defaultValue: "20",
59
+ envOnly: true
60
+ },
61
+ STORAGE_DRIVER: {
62
+ defaultValue: "r2",
63
+ envOnly: true
64
+ },
65
+ S3_ENDPOINT: {
66
+ defaultValue: "",
67
+ envOnly: true
68
+ },
69
+ S3_BUCKET: {
70
+ defaultValue: "",
71
+ envOnly: true
72
+ },
73
+ S3_ACCESS_KEY_ID: {
74
+ defaultValue: "",
75
+ envOnly: true
76
+ },
77
+ S3_SECRET_ACCESS_KEY: {
78
+ defaultValue: "",
79
+ envOnly: true
80
+ },
81
+ S3_REGION: {
82
+ defaultValue: "auto",
83
+ envOnly: true
84
+ },
85
+ S3_PUBLIC_URL: {
86
+ defaultValue: "",
87
+ envOnly: true
88
+ },
89
+ // Internal settings (DB-only, not configurable via env or dashboard)
90
+ THEME: {
91
+ defaultValue: "",
92
+ envOnly: false,
93
+ internal: true
94
+ },
95
+ CUSTOM_CSS: {
96
+ defaultValue: "",
97
+ envOnly: false,
98
+ internal: true
99
+ },
100
+ SITE_AVATAR: {
101
+ defaultValue: "",
102
+ envOnly: false,
103
+ internal: true
104
+ },
105
+ SHOW_HEADER_AVATAR: {
106
+ defaultValue: "",
107
+ envOnly: false,
108
+ internal: true
109
+ },
110
+ SITE_FAVICON_ICO: {
111
+ defaultValue: "",
112
+ envOnly: false,
113
+ internal: true
114
+ },
115
+ SITE_FAVICON_APPLE_TOUCH: {
116
+ defaultValue: "",
117
+ envOnly: false,
118
+ internal: true
119
+ },
120
+ FONT_THEME: {
121
+ defaultValue: "",
122
+ envOnly: false,
123
+ internal: true
124
+ },
125
+ TIME_ZONE: {
126
+ defaultValue: "UTC",
127
+ envOnly: false
128
+ },
129
+ SITE_FOOTER: {
130
+ defaultValue: "",
131
+ envOnly: false
132
+ },
133
+ NOINDEX: {
134
+ defaultValue: "",
135
+ envOnly: false
136
+ },
137
+ ONBOARDING_STATUS: {
138
+ defaultValue: "pending",
139
+ envOnly: false,
140
+ internal: true
141
+ },
142
+ PASSWORD_RESET_TOKEN: {
143
+ defaultValue: "",
144
+ envOnly: false,
145
+ internal: true
146
+ }
147
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Content Type Constants
3
+ */ export const FORMATS = [
4
+ "note",
5
+ "link",
6
+ "quote"
7
+ ];
8
+ export const STATUSES = [
9
+ "draft",
10
+ "published"
11
+ ];
12
+ export const SORT_ORDERS = [
13
+ "newest",
14
+ "oldest",
15
+ "rating_desc",
16
+ "rating_asc"
17
+ ];
18
+ export const NAV_ITEM_TYPES = [
19
+ "page",
20
+ "link"
21
+ ];
22
+ export const MAX_MEDIA_ATTACHMENTS = 20;
23
+ export const MAX_PINNED_POSTS = 3;
24
+ export const STORAGE_DRIVERS = [
25
+ "r2",
26
+ "s3"
27
+ ];
@@ -0,0 +1,3 @@
1
+ /**
2
+ * Entity Types (database-level models)
3
+ */ export { };
@@ -0,0 +1,3 @@
1
+ /**
2
+ * Operation Types (create/update DTOs)
3
+ */ export { };
@@ -0,0 +1,3 @@
1
+ /**
2
+ * Page-Level Props & Feed Data Types
3
+ */ /** Props for the timeline feed wrapper */ export { };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * View Model Types (render-ready, for theme components)
3
+ */ /**
4
+ * Site Layout Props
5
+ */ export { };
package/dist/types.js CHANGED
@@ -1,114 +1,11 @@
1
1
  /**
2
2
  * Jant Type Definitions (v2)
3
- */ // =============================================================================
4
- // Content Types
5
- // =============================================================================
6
- export const FORMATS = [
7
- "note",
8
- "link",
9
- "quote"
10
- ];
11
- export const STATUSES = [
12
- "draft",
13
- "published"
14
- ];
15
- export const SORT_ORDERS = [
16
- "newest",
17
- "oldest",
18
- "rating_desc",
19
- "rating_asc"
20
- ];
21
- export const NAV_ITEM_TYPES = [
22
- "page",
23
- "link"
24
- ];
25
- export const MAX_MEDIA_ATTACHMENTS = 20;
26
- export const MAX_PINNED_POSTS = 3;
27
- export const STORAGE_DRIVERS = [
28
- "r2",
29
- "s3"
30
- ];
31
- // =============================================================================
32
- // Configuration System
33
- // =============================================================================
34
- /**
35
- * Configuration Registry - Single Source of Truth
36
- *
37
- * All available configuration fields with their metadata.
38
- * Add new fields here, and they'll automatically work everywhere.
39
3
  *
40
- * Priority logic:
41
- * - envOnly: false -> User-configurable (DB > ENV > Default)
42
- * - envOnly: true -> Environment-only (ENV > Default)
43
- */ export const CONFIG_FIELDS = {
44
- // User-configurable (can be modified in dashboard)
45
- SITE_NAME: {
46
- defaultValue: "Jant",
47
- envOnly: false
48
- },
49
- SITE_DESCRIPTION: {
50
- defaultValue: "A microblog powered by Jant",
51
- envOnly: false
52
- },
53
- SITE_LANGUAGE: {
54
- defaultValue: "en",
55
- envOnly: false
56
- },
57
- // Environment-only (deployment/infrastructure config)
58
- SITE_URL: {
59
- defaultValue: "",
60
- envOnly: true
61
- },
62
- AUTH_SECRET: {
63
- defaultValue: "",
64
- envOnly: true
65
- },
66
- R2_PUBLIC_URL: {
67
- defaultValue: "",
68
- envOnly: true
69
- },
70
- IMAGE_TRANSFORM_URL: {
71
- defaultValue: "",
72
- envOnly: true
73
- },
74
- DEMO_EMAIL: {
75
- defaultValue: "",
76
- envOnly: true
77
- },
78
- DEMO_PASSWORD: {
79
- defaultValue: "",
80
- envOnly: true
81
- },
82
- PAGE_SIZE: {
83
- defaultValue: "20",
84
- envOnly: true
85
- },
86
- STORAGE_DRIVER: {
87
- defaultValue: "r2",
88
- envOnly: true
89
- },
90
- S3_ENDPOINT: {
91
- defaultValue: "",
92
- envOnly: true
93
- },
94
- S3_BUCKET: {
95
- defaultValue: "",
96
- envOnly: true
97
- },
98
- S3_ACCESS_KEY_ID: {
99
- defaultValue: "",
100
- envOnly: true
101
- },
102
- S3_SECRET_ACCESS_KEY: {
103
- defaultValue: "",
104
- envOnly: true
105
- },
106
- S3_REGION: {
107
- defaultValue: "auto",
108
- envOnly: true
109
- },
110
- S3_PUBLIC_URL: {
111
- defaultValue: "",
112
- envOnly: true
113
- }
114
- };
4
+ * Barrel re-export from focused submodules.
5
+ */ export * from "./types/constants.js";
6
+ export * from "./types/bindings.js";
7
+ export * from "./types/config.js";
8
+ export * from "./types/entities.js";
9
+ export * from "./types/operations.js";
10
+ export * from "./types/views.js";
11
+ export * from "./types/props.js";
@@ -71,9 +71,39 @@
71
71
  };
72
72
  }
73
73
  export const BUILTIN_COLOR_THEMES = [
74
+ defineTheme({
75
+ id: "halloween",
76
+ name: "Halloween",
77
+ preview: {
78
+ lightBg: "#f9f2e3",
79
+ lightText: "#352200",
80
+ lightLink: "#b84400",
81
+ darkBg: "#1e1000",
82
+ darkText: "#dfc390",
83
+ darkLink: "#ff8c00"
84
+ },
85
+ light: {
86
+ bg: "oklch(0.97 0.015 75)",
87
+ fg: "oklch(0.25 0.04 55)",
88
+ primary: "oklch(0.47 0.17 50)",
89
+ primaryFg: "oklch(0.98 0.01 75)",
90
+ muted: "oklch(0.93 0.02 75)",
91
+ mutedFg: "oklch(0.5 0.025 55)",
92
+ border: "oklch(0.88 0.025 75)"
93
+ },
94
+ dark: {
95
+ bg: "oklch(0.16 0.03 50)",
96
+ fg: "oklch(0.85 0.025 75)",
97
+ primary: "oklch(0.72 0.19 55)",
98
+ primaryFg: "oklch(0.14 0.03 50)",
99
+ muted: "oklch(0.22 0.025 50)",
100
+ mutedFg: "oklch(0.62 0.02 75)",
101
+ border: "oklch(0.28 0.025 50)"
102
+ }
103
+ }),
74
104
  {
75
105
  id: "default",
76
- name: "Default",
106
+ name: "Panda",
77
107
  light: {},
78
108
  dark: {},
79
109
  preview: {
@@ -175,36 +205,6 @@ export const BUILTIN_COLOR_THEMES = [
175
205
  border: "oklch(0.3 0 0)"
176
206
  }
177
207
  }),
178
- defineTheme({
179
- id: "halloween",
180
- name: "Halloween",
181
- preview: {
182
- lightBg: "#f9f2e3",
183
- lightText: "#352200",
184
- lightLink: "#cc5500",
185
- darkBg: "#1e1000",
186
- darkText: "#dfc390",
187
- darkLink: "#ff8c00"
188
- },
189
- light: {
190
- bg: "oklch(0.97 0.015 75)",
191
- fg: "oklch(0.25 0.04 55)",
192
- primary: "oklch(0.6 0.2 50)",
193
- primaryFg: "oklch(0.98 0.01 75)",
194
- muted: "oklch(0.93 0.02 75)",
195
- mutedFg: "oklch(0.5 0.025 55)",
196
- border: "oklch(0.88 0.025 75)"
197
- },
198
- dark: {
199
- bg: "oklch(0.16 0.03 50)",
200
- fg: "oklch(0.85 0.025 75)",
201
- primary: "oklch(0.72 0.19 55)",
202
- primaryFg: "oklch(0.14 0.03 50)",
203
- muted: "oklch(0.22 0.025 50)",
204
- mutedFg: "oklch(0.62 0.02 75)",
205
- border: "oklch(0.28 0.025 50)"
206
- }
207
- }),
208
208
  defineTheme({
209
209
  id: "notepad",
210
210
  name: "Notepad",
@@ -241,7 +241,7 @@ export const BUILTIN_COLOR_THEMES = [
241
241
  preview: {
242
242
  lightBg: "#f7eef5",
243
243
  lightText: "#2e1e2c",
244
- lightLink: "#9845c8",
244
+ lightLink: "#7a30a8",
245
245
  darkBg: "#1d1428",
246
246
  darkText: "#d4c2d0",
247
247
  darkLink: "#c080fc"
@@ -249,7 +249,7 @@ export const BUILTIN_COLOR_THEMES = [
249
249
  light: {
250
250
  bg: "oklch(0.97 0.012 325)",
251
251
  fg: "oklch(0.25 0.02 310)",
252
- primary: "oklch(0.55 0.2 300)",
252
+ primary: "oklch(0.45 0.2 300)",
253
253
  primaryFg: "oklch(0.98 0.008 325)",
254
254
  muted: "oklch(0.93 0.016 325)",
255
255
  mutedFg: "oklch(0.52 0.015 310)",