@jant/core 0.3.27 → 0.3.29
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/bin/reset-password.js +22 -0
- package/dist/client/client.css +1 -0
- package/dist/client/client.js +31561 -0
- package/dist/index.js +15209 -15
- package/package.json +25 -15
- package/src/__tests__/helpers/app.ts +19 -3
- package/src/__tests__/helpers/db.ts +44 -0
- package/src/__tests__/helpers/lingui-core-macro-mock.ts +33 -0
- package/src/app.tsx +111 -174
- package/src/client.ts +13 -0
- package/src/db/migrations/0007_post_collections_m2m.sql +94 -0
- package/src/db/migrations/0008_add_collection_dividers.sql +8 -0
- package/src/db/migrations/0009_drop_collection_show_divider.sql +2 -0
- package/src/db/migrations/0010_add_performance_indexes.sql +16 -0
- package/src/db/schema.ts +24 -4
- package/src/i18n/locales/en.po +810 -385
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +733 -522
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +733 -522
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/i18n/middleware.ts +7 -11
- package/src/index.ts +1 -1
- package/src/lib/__tests__/icons.test.ts +178 -0
- package/src/lib/__tests__/resolve-config.test.ts +184 -0
- package/src/lib/__tests__/schemas.test.ts +12 -6
- package/src/lib/__tests__/theme.test.ts +62 -0
- package/src/lib/__tests__/timezones.test.ts +1 -1
- package/src/lib/__tests__/url.test.ts +12 -0
- package/src/lib/__tests__/view.test.ts +1 -5
- package/src/lib/avatar-upload.ts +18 -10
- package/src/lib/collection-form-bridge.ts +52 -0
- package/src/lib/collections-reorder.ts +28 -0
- package/src/lib/compose-bridge.ts +251 -0
- package/src/lib/errors.ts +116 -0
- package/src/lib/excerpt.ts +1 -1
- package/src/lib/favicon.ts +3 -5
- package/src/lib/html.ts +22 -0
- package/src/lib/icon-catalog.ts +181 -0
- package/src/lib/icons.ts +202 -0
- package/src/lib/navigation.ts +18 -33
- package/src/lib/pagination.ts +3 -2
- package/src/lib/post-form-bridge.ts +136 -0
- package/src/lib/render.tsx +11 -4
- package/src/lib/resolve-config.ts +157 -0
- package/src/lib/schemas.ts +76 -12
- package/src/lib/settings-bridge.ts +139 -0
- package/src/lib/storage.ts +37 -16
- package/src/lib/theme.ts +5 -7
- package/src/lib/timeline.ts +4 -8
- package/src/lib/toast.ts +134 -0
- package/src/lib/upload.ts +71 -0
- package/src/lib/url.ts +9 -1
- package/src/lib/version.ts +16 -0
- package/src/lib/view.ts +9 -10
- package/src/middleware/__tests__/auth.test.ts +6 -28
- package/src/middleware/__tests__/onboarding.test.ts +1 -1
- package/src/middleware/auth.ts +6 -12
- package/src/middleware/config.ts +51 -0
- package/src/middleware/error-handler.ts +56 -0
- package/src/middleware/onboarding.ts +1 -1
- package/src/preset.css +6 -0
- package/src/routes/__tests__/compose.test.ts +104 -17
- package/src/routes/api/__tests__/collections.test.ts +93 -2
- package/src/routes/api/__tests__/posts.test.ts +2 -1
- package/src/routes/api/__tests__/settings.test.ts +1 -1
- package/src/routes/api/collections.ts +64 -68
- package/src/routes/api/nav-items.ts +21 -59
- package/src/routes/api/pages.ts +18 -46
- package/src/routes/api/posts.ts +64 -86
- package/src/routes/api/search.ts +6 -4
- package/src/routes/api/settings.ts +8 -24
- package/src/routes/api/upload.ts +55 -53
- package/src/routes/auth/__tests__/setup.test.ts +118 -0
- package/src/routes/auth/reset.tsx +17 -66
- package/src/routes/auth/setup.tsx +67 -11
- package/src/routes/auth/signin.tsx +44 -8
- package/src/routes/compose.tsx +194 -0
- package/src/routes/dash/__tests__/font-theme.test.ts +110 -0
- package/src/routes/dash/__tests__/pages.test.ts +2 -2
- package/src/routes/dash/__tests__/settings-avatar.test.ts +23 -12
- package/src/routes/dash/appearance.tsx +173 -0
- package/src/routes/dash/collections.tsx +80 -14
- package/src/routes/dash/index.tsx +12 -14
- package/src/routes/dash/media.tsx +46 -49
- package/src/routes/dash/pages.tsx +85 -37
- package/src/routes/dash/posts.tsx +60 -23
- package/src/routes/dash/redirects.tsx +43 -33
- package/src/routes/dash/settings.tsx +234 -214
- package/src/routes/feed/__tests__/rss.test.ts +7 -3
- package/src/routes/feed/rss.ts +11 -16
- package/src/routes/feed/sitemap.ts +15 -9
- package/src/routes/pages/__tests__/collections.test.ts +9 -8
- package/src/routes/pages/archive.tsx +2 -2
- package/src/routes/pages/collection.tsx +76 -9
- package/src/routes/pages/collections.tsx +3 -1
- package/src/routes/pages/featured.tsx +2 -2
- package/src/routes/pages/home.tsx +3 -3
- package/src/routes/pages/latest.tsx +2 -2
- package/src/routes/pages/page.tsx +2 -2
- package/src/routes/pages/post.tsx +2 -2
- package/src/routes/pages/search.tsx +2 -2
- package/src/services/__tests__/collection.test.ts +324 -34
- package/src/services/__tests__/media.test.ts +1 -1
- package/src/services/__tests__/page.test.ts +116 -1
- package/src/services/auth.ts +88 -0
- package/src/services/collection.ts +169 -30
- package/src/services/index.ts +8 -3
- package/src/services/media.ts +39 -12
- package/src/services/navigation.ts +17 -5
- package/src/services/page.ts +24 -4
- package/src/services/post.ts +87 -19
- package/src/services/search.ts +0 -1
- package/src/services/settings.ts +21 -13
- package/src/style.css +3 -0
- package/src/styles/components.css +42 -1
- package/src/styles/tokens.css +4 -0
- package/src/styles/ui.css +902 -73
- package/src/types/app-context.ts +25 -0
- package/src/types/bindings.ts +1 -0
- package/src/types/config.ts +60 -23
- package/src/types/entities.ts +12 -2
- package/src/types/lingui-react-macro.d.ts +3 -3
- package/src/types/operations.ts +2 -4
- package/src/types/views.ts +1 -3
- package/src/ui/__tests__/font-themes.test.ts +27 -8
- package/src/ui/color-themes.ts +1 -1
- package/src/ui/components/__tests__/jant-collection-form.test.ts +153 -0
- package/src/ui/components/__tests__/jant-compose-dialog.test.ts +512 -0
- package/src/ui/components/__tests__/jant-compose-editor.test.ts +272 -0
- package/src/ui/components/__tests__/jant-post-form.test.ts +172 -0
- package/src/ui/components/__tests__/jant-settings-avatar.test.ts +235 -0
- package/src/ui/components/__tests__/jant-settings-general.test.ts +319 -0
- package/src/ui/components/collection-types.ts +45 -0
- package/src/ui/components/compose-types.ts +75 -0
- package/src/ui/components/jant-collection-form.ts +512 -0
- package/src/ui/components/jant-compose-dialog.ts +494 -0
- package/src/ui/components/jant-compose-editor.ts +799 -0
- package/src/ui/components/jant-post-form.ts +290 -0
- package/src/ui/components/jant-settings-avatar.ts +231 -0
- package/src/ui/components/jant-settings-general.ts +436 -0
- package/src/ui/components/post-form-template.ts +260 -0
- package/src/ui/components/post-form-types.ts +87 -0
- package/src/ui/components/settings-types.ts +62 -0
- package/src/ui/compose/ComposeDialog.tsx +141 -385
- package/src/ui/compose/ComposePrompt.tsx +3 -3
- package/src/ui/dash/PostList.tsx +55 -61
- package/src/ui/dash/appearance/AdvancedContent.tsx +80 -0
- package/src/ui/dash/appearance/AppearanceNav.tsx +56 -0
- package/src/ui/dash/appearance/ColorThemeContent.tsx +129 -0
- package/src/ui/dash/appearance/FontThemeContent.tsx +98 -0
- package/src/ui/dash/collections/CollectionForm.tsx +130 -117
- package/src/ui/dash/collections/CollectionsListContent.tsx +102 -41
- package/src/ui/dash/collections/IconPickerGrid.tsx +50 -0
- package/src/ui/dash/collections/ViewCollectionContent.tsx +14 -3
- package/src/ui/dash/index.ts +1 -1
- package/src/ui/dash/posts/PostForm.tsx +248 -0
- package/src/ui/dash/settings/AccountContent.tsx +69 -80
- package/src/ui/dash/settings/GeneralContent.tsx +159 -478
- package/src/ui/dash/settings/SettingsNav.tsx +4 -4
- package/src/ui/font-themes.ts +115 -32
- package/src/ui/layouts/BaseLayout.tsx +49 -19
- package/src/ui/layouts/DashLayout.tsx +14 -9
- package/src/ui/layouts/SiteLayout.tsx +38 -23
- package/src/ui/pages/CollectionPage.tsx +12 -2
- package/src/ui/pages/CollectionsPage.tsx +27 -27
- package/src/ui/pages/HomePage.tsx +15 -6
- package/src/ui/pages/SearchPage.tsx +1 -2
- package/src/ui/shared/CollectionsSidebar.tsx +59 -0
- package/src/ui/shared/Pagination.tsx +2 -2
- package/dist/app.js +0 -267
- package/dist/auth.js +0 -39
- package/dist/client.js +0 -13
- package/dist/db/index.js +0 -10
- package/dist/db/schema.js +0 -224
- package/dist/i18n/Trans.js +0 -24
- package/dist/i18n/context.js +0 -58
- package/dist/i18n/detect.js +0 -26
- package/dist/i18n/i18n.js +0 -49
- package/dist/i18n/index.js +0 -44
- package/dist/i18n/locales/en.js +0 -1
- package/dist/i18n/locales/zh-Hans.js +0 -1
- package/dist/i18n/locales/zh-Hant.js +0 -1
- package/dist/i18n/locales.js +0 -13
- package/dist/i18n/middleware.js +0 -30
- package/dist/lib/avatar-upload.js +0 -134
- package/dist/lib/config.js +0 -143
- package/dist/lib/constants.js +0 -50
- package/dist/lib/excerpt.js +0 -76
- package/dist/lib/favicon.js +0 -102
- package/dist/lib/feed.js +0 -123
- package/dist/lib/image-processor.js +0 -187
- package/dist/lib/image.js +0 -97
- package/dist/lib/index.js +0 -7
- package/dist/lib/markdown.js +0 -83
- package/dist/lib/media-helpers.js +0 -49
- package/dist/lib/media-upload.js +0 -104
- package/dist/lib/nav-reorder.js +0 -27
- package/dist/lib/navigation.js +0 -79
- package/dist/lib/pagination.js +0 -44
- package/dist/lib/render.js +0 -53
- package/dist/lib/schemas.js +0 -174
- package/dist/lib/sqid.js +0 -72
- package/dist/lib/sse.js +0 -218
- package/dist/lib/storage.js +0 -164
- package/dist/lib/theme.js +0 -65
- package/dist/lib/time.js +0 -159
- package/dist/lib/timeline.js +0 -95
- package/dist/lib/timezones.js +0 -388
- package/dist/lib/url.js +0 -89
- package/dist/lib/view.js +0 -217
- package/dist/middleware/auth.js +0 -52
- package/dist/middleware/onboarding.js +0 -41
- package/dist/routes/api/collections.js +0 -124
- package/dist/routes/api/nav-items.js +0 -104
- package/dist/routes/api/pages.js +0 -91
- package/dist/routes/api/posts.js +0 -218
- package/dist/routes/api/search.js +0 -48
- package/dist/routes/api/settings.js +0 -68
- package/dist/routes/api/upload.js +0 -246
- package/dist/routes/auth/reset.js +0 -221
- package/dist/routes/auth/setup.js +0 -194
- package/dist/routes/auth/signin.js +0 -176
- package/dist/routes/compose.js +0 -48
- package/dist/routes/dash/collections.js +0 -115
- package/dist/routes/dash/index.js +0 -118
- package/dist/routes/dash/media.js +0 -106
- package/dist/routes/dash/pages.js +0 -294
- package/dist/routes/dash/posts.js +0 -244
- package/dist/routes/dash/redirects.js +0 -257
- package/dist/routes/dash/settings.js +0 -379
- package/dist/routes/feed/rss.js +0 -62
- package/dist/routes/feed/sitemap.js +0 -49
- package/dist/routes/pages/archive.js +0 -62
- package/dist/routes/pages/collection.js +0 -34
- package/dist/routes/pages/collections.js +0 -28
- package/dist/routes/pages/featured.js +0 -36
- package/dist/routes/pages/home.js +0 -64
- package/dist/routes/pages/latest.js +0 -45
- package/dist/routes/pages/page.js +0 -68
- package/dist/routes/pages/post.js +0 -44
- package/dist/routes/pages/search.js +0 -54
- package/dist/services/collection.js +0 -109
- package/dist/services/index.js +0 -24
- package/dist/services/media.js +0 -117
- package/dist/services/navigation.js +0 -91
- package/dist/services/page.js +0 -84
- package/dist/services/post.js +0 -229
- package/dist/services/redirect.js +0 -48
- package/dist/services/search.js +0 -67
- package/dist/services/settings.js +0 -68
- package/dist/types/bindings.js +0 -3
- package/dist/types/config.js +0 -147
- package/dist/types/constants.js +0 -27
- package/dist/types/entities.js +0 -3
- package/dist/types/lingui-react-macro.d.js +0 -9
- package/dist/types/operations.js +0 -3
- package/dist/types/props.js +0 -3
- package/dist/types/sortablejs.d.js +0 -5
- package/dist/types/views.js +0 -5
- package/dist/types.js +0 -11
- package/dist/ui/color-themes.js +0 -268
- package/dist/ui/compose/ComposeDialog.js +0 -467
- package/dist/ui/compose/ComposePrompt.js +0 -55
- package/dist/ui/dash/ActionButtons.js +0 -46
- package/dist/ui/dash/CrudPageHeader.js +0 -22
- package/dist/ui/dash/DangerZone.js +0 -36
- package/dist/ui/dash/FormatBadge.js +0 -27
- package/dist/ui/dash/ListItemRow.js +0 -21
- package/dist/ui/dash/PageForm.js +0 -195
- package/dist/ui/dash/PostForm.js +0 -395
- package/dist/ui/dash/PostList.js +0 -83
- package/dist/ui/dash/StatusBadge.js +0 -46
- package/dist/ui/dash/collections/CollectionForm.js +0 -152
- package/dist/ui/dash/collections/CollectionsListContent.js +0 -68
- package/dist/ui/dash/collections/ViewCollectionContent.js +0 -96
- package/dist/ui/dash/index.js +0 -10
- package/dist/ui/dash/media/MediaListContent.js +0 -166
- package/dist/ui/dash/media/ViewMediaContent.js +0 -212
- package/dist/ui/dash/pages/LinkFormContent.js +0 -130
- package/dist/ui/dash/pages/UnifiedPagesContent.js +0 -193
- package/dist/ui/dash/settings/AccountContent.js +0 -209
- package/dist/ui/dash/settings/AppearanceContent.js +0 -259
- package/dist/ui/dash/settings/GeneralContent.js +0 -536
- package/dist/ui/dash/settings/SettingsNav.js +0 -41
- package/dist/ui/feed/LinkCard.js +0 -72
- package/dist/ui/feed/NoteCard.js +0 -58
- package/dist/ui/feed/QuoteCard.js +0 -63
- package/dist/ui/feed/ThreadPreview.js +0 -48
- package/dist/ui/feed/TimelineFeed.js +0 -41
- package/dist/ui/feed/TimelineItem.js +0 -27
- package/dist/ui/font-themes.js +0 -36
- package/dist/ui/layouts/BaseLayout.js +0 -153
- package/dist/ui/layouts/DashLayout.js +0 -141
- package/dist/ui/layouts/SiteLayout.js +0 -169
- package/dist/ui/pages/ArchivePage.js +0 -143
- package/dist/ui/pages/CollectionPage.js +0 -70
- package/dist/ui/pages/CollectionsPage.js +0 -76
- package/dist/ui/pages/FeaturedPage.js +0 -24
- package/dist/ui/pages/HomePage.js +0 -24
- package/dist/ui/pages/PostPage.js +0 -55
- package/dist/ui/pages/SearchPage.js +0 -122
- package/dist/ui/pages/SinglePage.js +0 -23
- package/dist/ui/shared/EmptyState.js +0 -27
- package/dist/ui/shared/MediaGallery.js +0 -35
- package/dist/ui/shared/Pagination.js +0 -195
- package/dist/ui/shared/ThreadView.js +0 -108
- package/dist/ui/shared/index.js +0 -5
- package/dist/vendor/datastar.js +0 -1606
- package/src/lib/__tests__/config.test.ts +0 -192
- package/src/lib/config.ts +0 -167
- package/src/routes/compose.ts +0 -63
- package/src/ui/dash/PostForm.tsx +0 -360
- package/src/ui/dash/settings/AppearanceContent.tsx +0 -254
package/dist/app.js
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Jant App Factory
|
|
3
|
-
*/ import { Hono } from "hono";
|
|
4
|
-
import { createDatabase } from "./db/index.js";
|
|
5
|
-
import { createServices } from "./services/index.js";
|
|
6
|
-
import { createAuth } from "./auth.js";
|
|
7
|
-
import { i18nMiddleware } from "./i18n/index.js";
|
|
8
|
-
import { SETTINGS_KEYS } from "./lib/constants.js";
|
|
9
|
-
// Routes - Auth
|
|
10
|
-
import { setupRoutes } from "./routes/auth/setup.js";
|
|
11
|
-
import { signinRoutes } from "./routes/auth/signin.js";
|
|
12
|
-
import { resetRoutes } from "./routes/auth/reset.js";
|
|
13
|
-
// Routes - Pages
|
|
14
|
-
import { homeRoutes } from "./routes/pages/home.js";
|
|
15
|
-
import { postRoutes } from "./routes/pages/post.js";
|
|
16
|
-
import { pageRoutes } from "./routes/pages/page.js";
|
|
17
|
-
import { collectionRoutes } from "./routes/pages/collection.js";
|
|
18
|
-
import { archiveRoutes } from "./routes/pages/archive.js";
|
|
19
|
-
import { searchRoutes } from "./routes/pages/search.js";
|
|
20
|
-
import { featuredRoutes } from "./routes/pages/featured.js";
|
|
21
|
-
import { latestRoutes } from "./routes/pages/latest.js";
|
|
22
|
-
import { collectionsPageRoutes } from "./routes/pages/collections.js";
|
|
23
|
-
// Routes - Dashboard
|
|
24
|
-
import { dashIndexRoutes } from "./routes/dash/index.js";
|
|
25
|
-
import { postsRoutes as dashPostsRoutes } from "./routes/dash/posts.js";
|
|
26
|
-
import { pagesRoutes as dashPagesRoutes } from "./routes/dash/pages.js";
|
|
27
|
-
import { mediaRoutes as dashMediaRoutes } from "./routes/dash/media.js";
|
|
28
|
-
import { settingsRoutes as dashSettingsRoutes } from "./routes/dash/settings.js";
|
|
29
|
-
import { redirectsRoutes as dashRedirectsRoutes } from "./routes/dash/redirects.js";
|
|
30
|
-
import { collectionsRoutes as dashCollectionsRoutes } from "./routes/dash/collections.js";
|
|
31
|
-
// Routes - API
|
|
32
|
-
import { postsApiRoutes } from "./routes/api/posts.js";
|
|
33
|
-
import { pagesApiRoutes } from "./routes/api/pages.js";
|
|
34
|
-
import { navItemsApiRoutes } from "./routes/api/nav-items.js";
|
|
35
|
-
import { collectionsApiRoutes } from "./routes/api/collections.js";
|
|
36
|
-
import { settingsApiRoutes } from "./routes/api/settings.js";
|
|
37
|
-
import { uploadApiRoutes } from "./routes/api/upload.js";
|
|
38
|
-
import { searchApiRoutes } from "./routes/api/search.js";
|
|
39
|
-
// Routes - Compose
|
|
40
|
-
import { composeRoutes } from "./routes/compose.js";
|
|
41
|
-
// Routes - Feed
|
|
42
|
-
import { rssRoutes } from "./routes/feed/rss.js";
|
|
43
|
-
import { sitemapRoutes } from "./routes/feed/sitemap.js";
|
|
44
|
-
// Middleware
|
|
45
|
-
import { requireAuth } from "./middleware/auth.js";
|
|
46
|
-
import { requireOnboarding } from "./middleware/onboarding.js";
|
|
47
|
-
import { getAvailableThemes, buildThemeStyle } from "./lib/theme.js";
|
|
48
|
-
import { createStorageDriver } from "./lib/storage.js";
|
|
49
|
-
import { BUILTIN_FONT_THEMES } from "./ui/font-themes.js";
|
|
50
|
-
import { getMediaUrl, getPublicUrlForProvider } from "./lib/image.js";
|
|
51
|
-
import { base64ToUint8Array } from "./lib/favicon.js";
|
|
52
|
-
/**
|
|
53
|
-
* Create a Jant application
|
|
54
|
-
*
|
|
55
|
-
* @param config - Optional configuration
|
|
56
|
-
* @returns Hono app instance
|
|
57
|
-
*
|
|
58
|
-
* Site settings (name, description, language) should be configured via
|
|
59
|
-
* environment variables (SITE_NAME, SITE_DESCRIPTION, SITE_LANGUAGE).
|
|
60
|
-
* They can also be set in the dashboard, which stores them in the database.
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```typescript
|
|
64
|
-
* import { createApp } from "@jant/core";
|
|
65
|
-
*
|
|
66
|
-
* export default createApp({
|
|
67
|
-
* cssVariables: { "--card-radius": "0" },
|
|
68
|
-
* });
|
|
69
|
-
* ```
|
|
70
|
-
*/ export function createApp(config = {}) {
|
|
71
|
-
const resolvedConfig = {
|
|
72
|
-
...config
|
|
73
|
-
};
|
|
74
|
-
const app = new Hono();
|
|
75
|
-
// Initialize services, auth, and config middleware
|
|
76
|
-
app.use("*", async (c, next)=>{
|
|
77
|
-
// Use withSession() to enable D1 Read Replication
|
|
78
|
-
const session = c.env.DB.withSession();
|
|
79
|
-
// Note: Drizzle ORM doesn't officially support D1DatabaseSession yet (issue #2226)
|
|
80
|
-
// but it works at runtime. We use type assertion as a temporary workaround.
|
|
81
|
-
const db = createDatabase(session);
|
|
82
|
-
const services = createServices(db, session);
|
|
83
|
-
c.set("services", services);
|
|
84
|
-
c.set("config", resolvedConfig);
|
|
85
|
-
c.set("storage", createStorageDriver(c.env));
|
|
86
|
-
if (!c.env.AUTH_SECRET) {
|
|
87
|
-
// eslint-disable-next-line no-console -- Startup warning is intentional
|
|
88
|
-
console.warn("[Jant] AUTH_SECRET is not set. Authentication is disabled. Set AUTH_SECRET in .dev.vars or wrangler.toml to enable auth.");
|
|
89
|
-
}
|
|
90
|
-
if (c.env.AUTH_SECRET) {
|
|
91
|
-
const baseURL = c.env.SITE_URL || new URL(c.req.url).origin;
|
|
92
|
-
const requestUrl = new URL(c.req.url);
|
|
93
|
-
const auth = createAuth(session, {
|
|
94
|
-
secret: c.env.AUTH_SECRET,
|
|
95
|
-
baseURL,
|
|
96
|
-
useSecureCookies: requestUrl.protocol === "https:"
|
|
97
|
-
});
|
|
98
|
-
c.set("auth", auth);
|
|
99
|
-
}
|
|
100
|
-
await next();
|
|
101
|
-
});
|
|
102
|
-
// Onboarding gate — redirect to /setup if not yet initialized
|
|
103
|
-
app.use("*", requireOnboarding());
|
|
104
|
-
// Theme middleware - resolve active color theme, font theme, custom CSS, and auth state
|
|
105
|
-
app.use("*", async (c, next)=>{
|
|
106
|
-
const [themeId, fontThemeId, customCSS, noindexValue, avatarKey] = await Promise.all([
|
|
107
|
-
c.var.services.settings.get(SETTINGS_KEYS.THEME),
|
|
108
|
-
c.var.services.settings.get("FONT_THEME"),
|
|
109
|
-
c.var.services.settings.get(SETTINGS_KEYS.CUSTOM_CSS),
|
|
110
|
-
c.var.services.settings.get("NOINDEX"),
|
|
111
|
-
c.var.services.settings.get("SITE_AVATAR")
|
|
112
|
-
]);
|
|
113
|
-
const themes = getAvailableThemes(resolvedConfig);
|
|
114
|
-
const activeTheme = themeId ? themes.find((t)=>t.id === themeId) : undefined;
|
|
115
|
-
// Build font override CSS variables
|
|
116
|
-
const fontTheme = fontThemeId ? BUILTIN_FONT_THEMES.find((f)=>f.id === fontThemeId) : undefined;
|
|
117
|
-
const fontOverrides = {};
|
|
118
|
-
if (fontTheme) {
|
|
119
|
-
fontOverrides["--font-body"] = fontTheme.fontFamily;
|
|
120
|
-
}
|
|
121
|
-
const themeStyle = buildThemeStyle(activeTheme, {
|
|
122
|
-
...resolvedConfig.cssVariables,
|
|
123
|
-
...fontOverrides
|
|
124
|
-
});
|
|
125
|
-
c.set("themeStyle", themeStyle);
|
|
126
|
-
c.set("customCSS", customCSS ?? "");
|
|
127
|
-
// Noindex
|
|
128
|
-
c.set("noindex", noindexValue === "true");
|
|
129
|
-
// Resolve favicon from avatar storage key
|
|
130
|
-
if (avatarKey) {
|
|
131
|
-
const publicUrl = getPublicUrlForProvider(c.env.STORAGE_DRIVER || "r2", c.env.R2_PUBLIC_URL, c.env.S3_PUBLIC_URL);
|
|
132
|
-
c.set("faviconUrl", getMediaUrl(avatarKey, publicUrl));
|
|
133
|
-
}
|
|
134
|
-
// Check auth state for data-authenticated attribute on <body>
|
|
135
|
-
let isAuthenticated = false;
|
|
136
|
-
if (c.var.auth) {
|
|
137
|
-
try {
|
|
138
|
-
const session = await c.var.auth.api.getSession({
|
|
139
|
-
headers: c.req.raw.headers
|
|
140
|
-
});
|
|
141
|
-
isAuthenticated = !!session;
|
|
142
|
-
} catch {
|
|
143
|
-
// Not authenticated
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
c.set("isAuthenticated", isAuthenticated);
|
|
147
|
-
await next();
|
|
148
|
-
});
|
|
149
|
-
// i18n middleware
|
|
150
|
-
app.use("*", i18nMiddleware());
|
|
151
|
-
// Trailing slash redirect (redirect /foo/ to /foo)
|
|
152
|
-
app.use("*", async (c, next)=>{
|
|
153
|
-
const url = new URL(c.req.url);
|
|
154
|
-
if (url.pathname !== "/" && url.pathname.endsWith("/")) {
|
|
155
|
-
const newUrl = url.pathname.slice(0, -1) + url.search;
|
|
156
|
-
return c.redirect(newUrl, 301);
|
|
157
|
-
}
|
|
158
|
-
await next();
|
|
159
|
-
});
|
|
160
|
-
// Redirect middleware
|
|
161
|
-
app.use("*", async (c, next)=>{
|
|
162
|
-
const path = new URL(c.req.url).pathname;
|
|
163
|
-
// Skip redirect check for API routes and static assets
|
|
164
|
-
if (path.startsWith("/api/") || path.startsWith("/assets/")) {
|
|
165
|
-
return next();
|
|
166
|
-
}
|
|
167
|
-
const redirect = await c.var.services.redirects.getByPath(path);
|
|
168
|
-
if (redirect) {
|
|
169
|
-
return c.redirect(redirect.toPath, redirect.type);
|
|
170
|
-
}
|
|
171
|
-
await next();
|
|
172
|
-
});
|
|
173
|
-
// Health check
|
|
174
|
-
app.get("/health", (c)=>c.json({
|
|
175
|
-
status: "ok",
|
|
176
|
-
auth: c.env.AUTH_SECRET ? "configured" : "missing",
|
|
177
|
-
authSecretLength: c.env.AUTH_SECRET?.length ?? 0
|
|
178
|
-
}));
|
|
179
|
-
// Favicon routes - serve from DB settings (small files, avoids R2 round-trip)
|
|
180
|
-
app.get("/favicon.ico", async (c)=>{
|
|
181
|
-
const data = await c.var.services.settings.get("SITE_FAVICON_ICO");
|
|
182
|
-
if (!data) return c.notFound();
|
|
183
|
-
return new Response(base64ToUint8Array(data), {
|
|
184
|
-
headers: {
|
|
185
|
-
"Content-Type": "image/x-icon",
|
|
186
|
-
"Cache-Control": "public, max-age=86400"
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
app.get("/apple-touch-icon.png", async (c)=>{
|
|
191
|
-
const data = await c.var.services.settings.get("SITE_FAVICON_APPLE_TOUCH");
|
|
192
|
-
if (!data) return c.notFound();
|
|
193
|
-
return new Response(base64ToUint8Array(data), {
|
|
194
|
-
headers: {
|
|
195
|
-
"Content-Type": "image/png",
|
|
196
|
-
"Cache-Control": "public, max-age=86400"
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
// better-auth handler
|
|
201
|
-
app.all("/api/auth/*", async (c)=>{
|
|
202
|
-
if (!c.var.auth) {
|
|
203
|
-
return c.json({
|
|
204
|
-
error: "Auth not configured. Set AUTH_SECRET."
|
|
205
|
-
}, 500);
|
|
206
|
-
}
|
|
207
|
-
return c.var.auth.handler(c.req.raw);
|
|
208
|
-
});
|
|
209
|
-
// API Routes
|
|
210
|
-
app.route("/api/posts", postsApiRoutes);
|
|
211
|
-
app.route("/api/pages", pagesApiRoutes);
|
|
212
|
-
app.route("/api/nav-items", navItemsApiRoutes);
|
|
213
|
-
app.route("/api/collections", collectionsApiRoutes);
|
|
214
|
-
app.route("/api/settings", settingsApiRoutes);
|
|
215
|
-
// Auth routes
|
|
216
|
-
app.route("/", setupRoutes);
|
|
217
|
-
app.route("/", signinRoutes);
|
|
218
|
-
app.route("/", resetRoutes);
|
|
219
|
-
// Dashboard routes (protected)
|
|
220
|
-
app.use("/dash/*", requireAuth());
|
|
221
|
-
app.route("/dash", dashIndexRoutes);
|
|
222
|
-
app.route("/dash/posts", dashPostsRoutes);
|
|
223
|
-
app.route("/dash/pages", dashPagesRoutes);
|
|
224
|
-
app.route("/dash/media", dashMediaRoutes);
|
|
225
|
-
app.route("/dash/settings", dashSettingsRoutes);
|
|
226
|
-
app.route("/dash/redirects", dashRedirectsRoutes);
|
|
227
|
-
app.route("/dash/collections", dashCollectionsRoutes);
|
|
228
|
-
// API routes
|
|
229
|
-
app.route("/api/upload", uploadApiRoutes);
|
|
230
|
-
app.route("/api/search", searchApiRoutes);
|
|
231
|
-
// Media files from storage (path matches storage key: media/YYYY/MM/uuid.ext)
|
|
232
|
-
app.get("/media/*", async (c)=>{
|
|
233
|
-
const storage = c.var.storage;
|
|
234
|
-
if (!storage) {
|
|
235
|
-
return c.notFound();
|
|
236
|
-
}
|
|
237
|
-
// The storage key is the full path without the leading "/"
|
|
238
|
-
const storageKey = c.req.path.slice(1);
|
|
239
|
-
const object = await storage.get(storageKey);
|
|
240
|
-
if (!object) {
|
|
241
|
-
return c.notFound();
|
|
242
|
-
}
|
|
243
|
-
const headers = new Headers();
|
|
244
|
-
headers.set("Content-Type", object.contentType || "application/octet-stream");
|
|
245
|
-
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
246
|
-
return new Response(object.body, {
|
|
247
|
-
headers
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
// Compose route (auth enforced in route middleware)
|
|
251
|
-
app.route("/compose", composeRoutes);
|
|
252
|
-
// Feed routes
|
|
253
|
-
app.route("/feed", rssRoutes);
|
|
254
|
-
app.route("/", sitemapRoutes);
|
|
255
|
-
// Frontend routes
|
|
256
|
-
app.route("/search", searchRoutes);
|
|
257
|
-
app.route("/archive", archiveRoutes);
|
|
258
|
-
app.route("/featured", featuredRoutes);
|
|
259
|
-
app.route("/latest", latestRoutes);
|
|
260
|
-
app.route("/collections", collectionsPageRoutes);
|
|
261
|
-
app.route("/c", collectionRoutes);
|
|
262
|
-
app.route("/p", postRoutes);
|
|
263
|
-
app.route("/", homeRoutes);
|
|
264
|
-
// Custom page catch-all (must be last)
|
|
265
|
-
app.route("/", pageRoutes);
|
|
266
|
-
return app;
|
|
267
|
-
}
|
package/dist/auth.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authentication with better-auth
|
|
3
|
-
*/ import { betterAuth } from "better-auth";
|
|
4
|
-
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
5
|
-
import { drizzle } from "drizzle-orm/d1";
|
|
6
|
-
import * as schema from "./db/schema.js";
|
|
7
|
-
export function createAuth(d1, options) {
|
|
8
|
-
const db = drizzle(d1, {
|
|
9
|
-
schema
|
|
10
|
-
});
|
|
11
|
-
return betterAuth({
|
|
12
|
-
database: drizzleAdapter(db, {
|
|
13
|
-
provider: "sqlite",
|
|
14
|
-
schema: {
|
|
15
|
-
user: schema.user,
|
|
16
|
-
session: schema.session,
|
|
17
|
-
account: schema.account,
|
|
18
|
-
verification: schema.verification
|
|
19
|
-
}
|
|
20
|
-
}),
|
|
21
|
-
secret: options.secret,
|
|
22
|
-
baseURL: options.baseURL,
|
|
23
|
-
advanced: {
|
|
24
|
-
useSecureCookies: options.useSecureCookies
|
|
25
|
-
},
|
|
26
|
-
emailAndPassword: {
|
|
27
|
-
enabled: true,
|
|
28
|
-
autoSignIn: true,
|
|
29
|
-
minPasswordLength: 8
|
|
30
|
-
},
|
|
31
|
-
session: {
|
|
32
|
-
expiresIn: 3600 * 24 * 366,
|
|
33
|
-
cookieCache: {
|
|
34
|
-
enabled: true,
|
|
35
|
-
maxAge: 60 * 5
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}
|
package/dist/client.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side JavaScript entry point
|
|
3
|
-
*
|
|
4
|
-
* Bundles all interactive components:
|
|
5
|
-
* - Datastar (reactivity)
|
|
6
|
-
* - BaseCoat (dialogs, dropdowns)
|
|
7
|
-
* - ImageProcessor (media uploads)
|
|
8
|
-
*/ import "./vendor/datastar.js";
|
|
9
|
-
import "basecoat-css/all";
|
|
10
|
-
import "./lib/image-processor.js";
|
|
11
|
-
import "./lib/media-upload.js";
|
|
12
|
-
import "./lib/avatar-upload.js";
|
|
13
|
-
import "./lib/nav-reorder.js";
|
package/dist/db/index.js
DELETED
package/dist/db/schema.js
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drizzle Schema
|
|
3
|
-
*
|
|
4
|
-
* Database schema for Jant v2
|
|
5
|
-
*/ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
6
|
-
// =============================================================================
|
|
7
|
-
// Posts
|
|
8
|
-
// =============================================================================
|
|
9
|
-
export const posts = sqliteTable("posts", {
|
|
10
|
-
id: integer("id").primaryKey({
|
|
11
|
-
autoIncrement: true
|
|
12
|
-
}),
|
|
13
|
-
format: text("format", {
|
|
14
|
-
enum: [
|
|
15
|
-
"note",
|
|
16
|
-
"link",
|
|
17
|
-
"quote"
|
|
18
|
-
]
|
|
19
|
-
}).notNull(),
|
|
20
|
-
status: text("status", {
|
|
21
|
-
enum: [
|
|
22
|
-
"draft",
|
|
23
|
-
"published"
|
|
24
|
-
]
|
|
25
|
-
}).notNull().default("published"),
|
|
26
|
-
featured: integer("featured").notNull().default(0),
|
|
27
|
-
pinned: integer("pinned").notNull().default(0),
|
|
28
|
-
path: text("path").unique(),
|
|
29
|
-
title: text("title"),
|
|
30
|
-
url: text("url"),
|
|
31
|
-
body: text("body"),
|
|
32
|
-
bodyHtml: text("body_html"),
|
|
33
|
-
quoteText: text("quote_text"),
|
|
34
|
-
rating: integer("rating"),
|
|
35
|
-
collectionId: integer("collection_id").references(()=>collections.id, {
|
|
36
|
-
onDelete: "set null"
|
|
37
|
-
}),
|
|
38
|
-
replyToId: integer("reply_to_id"),
|
|
39
|
-
threadId: integer("thread_id"),
|
|
40
|
-
deletedAt: integer("deleted_at"),
|
|
41
|
-
publishedAt: integer("published_at").notNull(),
|
|
42
|
-
createdAt: integer("created_at").notNull(),
|
|
43
|
-
updatedAt: integer("updated_at").notNull()
|
|
44
|
-
});
|
|
45
|
-
// =============================================================================
|
|
46
|
-
// Pages
|
|
47
|
-
// =============================================================================
|
|
48
|
-
export const pages = sqliteTable("pages", {
|
|
49
|
-
id: integer("id").primaryKey({
|
|
50
|
-
autoIncrement: true
|
|
51
|
-
}),
|
|
52
|
-
slug: text("slug").notNull().unique(),
|
|
53
|
-
title: text("title"),
|
|
54
|
-
body: text("body"),
|
|
55
|
-
bodyHtml: text("body_html"),
|
|
56
|
-
status: text("status", {
|
|
57
|
-
enum: [
|
|
58
|
-
"draft",
|
|
59
|
-
"published"
|
|
60
|
-
]
|
|
61
|
-
}).notNull().default("published"),
|
|
62
|
-
createdAt: integer("created_at").notNull(),
|
|
63
|
-
updatedAt: integer("updated_at").notNull()
|
|
64
|
-
});
|
|
65
|
-
// =============================================================================
|
|
66
|
-
// Media
|
|
67
|
-
// =============================================================================
|
|
68
|
-
export const media = sqliteTable("media", {
|
|
69
|
-
id: text("id").primaryKey(),
|
|
70
|
-
postId: integer("post_id").references(()=>posts.id),
|
|
71
|
-
filename: text("filename").notNull(),
|
|
72
|
-
originalName: text("original_name").notNull(),
|
|
73
|
-
mimeType: text("mime_type").notNull(),
|
|
74
|
-
size: integer("size").notNull(),
|
|
75
|
-
storageKey: text("storage_key").notNull(),
|
|
76
|
-
provider: text("provider").notNull().default("r2"),
|
|
77
|
-
width: integer("width"),
|
|
78
|
-
height: integer("height"),
|
|
79
|
-
alt: text("alt"),
|
|
80
|
-
position: integer("position").notNull().default(0),
|
|
81
|
-
blurhash: text("blurhash"),
|
|
82
|
-
createdAt: integer("created_at").notNull()
|
|
83
|
-
});
|
|
84
|
-
// =============================================================================
|
|
85
|
-
// Collections
|
|
86
|
-
// =============================================================================
|
|
87
|
-
export const collections = sqliteTable("collections", {
|
|
88
|
-
id: integer("id").primaryKey({
|
|
89
|
-
autoIncrement: true
|
|
90
|
-
}),
|
|
91
|
-
slug: text("slug").notNull().unique(),
|
|
92
|
-
title: text("title").notNull(),
|
|
93
|
-
description: text("description"),
|
|
94
|
-
icon: text("icon"),
|
|
95
|
-
sortOrder: text("sort_order", {
|
|
96
|
-
enum: [
|
|
97
|
-
"newest",
|
|
98
|
-
"oldest",
|
|
99
|
-
"rating_desc",
|
|
100
|
-
"rating_asc"
|
|
101
|
-
]
|
|
102
|
-
}).notNull().default("newest"),
|
|
103
|
-
position: integer("position").notNull().default(0),
|
|
104
|
-
showDivider: integer("show_divider").notNull().default(0),
|
|
105
|
-
createdAt: integer("created_at").notNull(),
|
|
106
|
-
updatedAt: integer("updated_at").notNull()
|
|
107
|
-
});
|
|
108
|
-
// =============================================================================
|
|
109
|
-
// Navigation Items
|
|
110
|
-
// =============================================================================
|
|
111
|
-
export const navItems = sqliteTable("nav_items", {
|
|
112
|
-
id: integer("id").primaryKey({
|
|
113
|
-
autoIncrement: true
|
|
114
|
-
}),
|
|
115
|
-
type: text("type", {
|
|
116
|
-
enum: [
|
|
117
|
-
"page",
|
|
118
|
-
"link"
|
|
119
|
-
]
|
|
120
|
-
}).notNull().default("link"),
|
|
121
|
-
label: text("label").notNull(),
|
|
122
|
-
url: text("url").notNull(),
|
|
123
|
-
pageId: integer("page_id").references(()=>pages.id, {
|
|
124
|
-
onDelete: "cascade"
|
|
125
|
-
}),
|
|
126
|
-
position: integer("position").notNull().default(0),
|
|
127
|
-
createdAt: integer("created_at").notNull(),
|
|
128
|
-
updatedAt: integer("updated_at").notNull()
|
|
129
|
-
});
|
|
130
|
-
// =============================================================================
|
|
131
|
-
// Redirects
|
|
132
|
-
// =============================================================================
|
|
133
|
-
export const redirects = sqliteTable("redirects", {
|
|
134
|
-
id: integer("id").primaryKey({
|
|
135
|
-
autoIncrement: true
|
|
136
|
-
}),
|
|
137
|
-
fromPath: text("from_path").notNull().unique(),
|
|
138
|
-
toPath: text("to_path").notNull(),
|
|
139
|
-
type: integer("type", {
|
|
140
|
-
mode: "number"
|
|
141
|
-
}).notNull().default(301),
|
|
142
|
-
createdAt: integer("created_at").notNull()
|
|
143
|
-
});
|
|
144
|
-
// =============================================================================
|
|
145
|
-
// Settings (Key-Value)
|
|
146
|
-
// =============================================================================
|
|
147
|
-
export const settings = sqliteTable("settings", {
|
|
148
|
-
key: text("key").primaryKey(),
|
|
149
|
-
value: text("value").notNull(),
|
|
150
|
-
updatedAt: integer("updated_at").notNull()
|
|
151
|
-
});
|
|
152
|
-
// =============================================================================
|
|
153
|
-
// better-auth tables
|
|
154
|
-
// Note: Using { mode: "timestamp" } so drizzle auto-converts Date <-> integer
|
|
155
|
-
// =============================================================================
|
|
156
|
-
export const user = sqliteTable("user", {
|
|
157
|
-
id: text("id").primaryKey(),
|
|
158
|
-
name: text("name").notNull(),
|
|
159
|
-
email: text("email").notNull().unique(),
|
|
160
|
-
emailVerified: integer("email_verified", {
|
|
161
|
-
mode: "boolean"
|
|
162
|
-
}).notNull().default(false),
|
|
163
|
-
image: text("image"),
|
|
164
|
-
role: text("role").default("admin"),
|
|
165
|
-
createdAt: integer("created_at", {
|
|
166
|
-
mode: "timestamp"
|
|
167
|
-
}).notNull(),
|
|
168
|
-
updatedAt: integer("updated_at", {
|
|
169
|
-
mode: "timestamp"
|
|
170
|
-
}).notNull()
|
|
171
|
-
});
|
|
172
|
-
export const session = sqliteTable("session", {
|
|
173
|
-
id: text("id").primaryKey(),
|
|
174
|
-
expiresAt: integer("expires_at", {
|
|
175
|
-
mode: "timestamp"
|
|
176
|
-
}).notNull(),
|
|
177
|
-
token: text("token").notNull().unique(),
|
|
178
|
-
createdAt: integer("created_at", {
|
|
179
|
-
mode: "timestamp"
|
|
180
|
-
}).notNull(),
|
|
181
|
-
updatedAt: integer("updated_at", {
|
|
182
|
-
mode: "timestamp"
|
|
183
|
-
}).notNull(),
|
|
184
|
-
ipAddress: text("ip_address"),
|
|
185
|
-
userAgent: text("user_agent"),
|
|
186
|
-
userId: text("user_id").notNull().references(()=>user.id)
|
|
187
|
-
});
|
|
188
|
-
export const account = sqliteTable("account", {
|
|
189
|
-
id: text("id").primaryKey(),
|
|
190
|
-
accountId: text("account_id").notNull(),
|
|
191
|
-
providerId: text("provider_id").notNull(),
|
|
192
|
-
userId: text("user_id").notNull().references(()=>user.id),
|
|
193
|
-
accessToken: text("access_token"),
|
|
194
|
-
refreshToken: text("refresh_token"),
|
|
195
|
-
idToken: text("id_token"),
|
|
196
|
-
accessTokenExpiresAt: integer("access_token_expires_at", {
|
|
197
|
-
mode: "timestamp"
|
|
198
|
-
}),
|
|
199
|
-
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
|
|
200
|
-
mode: "timestamp"
|
|
201
|
-
}),
|
|
202
|
-
scope: text("scope"),
|
|
203
|
-
password: text("password"),
|
|
204
|
-
createdAt: integer("created_at", {
|
|
205
|
-
mode: "timestamp"
|
|
206
|
-
}).notNull(),
|
|
207
|
-
updatedAt: integer("updated_at", {
|
|
208
|
-
mode: "timestamp"
|
|
209
|
-
}).notNull()
|
|
210
|
-
});
|
|
211
|
-
export const verification = sqliteTable("verification", {
|
|
212
|
-
id: text("id").primaryKey(),
|
|
213
|
-
identifier: text("identifier").notNull(),
|
|
214
|
-
value: text("value").notNull(),
|
|
215
|
-
expiresAt: integer("expires_at", {
|
|
216
|
-
mode: "timestamp"
|
|
217
|
-
}).notNull(),
|
|
218
|
-
createdAt: integer("created_at", {
|
|
219
|
-
mode: "timestamp"
|
|
220
|
-
}),
|
|
221
|
-
updatedAt: integer("updated_at", {
|
|
222
|
-
mode: "timestamp"
|
|
223
|
-
})
|
|
224
|
-
});
|
package/dist/i18n/Trans.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trans Component for Hono JSX
|
|
3
|
-
*
|
|
4
|
-
* Simple implementation that just renders children directly.
|
|
5
|
-
* For complex translations with embedded JSX, use the t() function with placeholders.
|
|
6
|
-
*/ import { jsx as _jsx, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
7
|
-
/**
|
|
8
|
-
* Trans component - renders children as-is
|
|
9
|
-
* Note: This is a simplified implementation. For translations with embedded JSX,
|
|
10
|
-
* it's recommended to use t() with placeholders instead.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```tsx
|
|
14
|
-
* <Trans comment="@context: Help text">
|
|
15
|
-
* Visit the <a href="/docs">documentation</a>
|
|
16
|
-
* </Trans>
|
|
17
|
-
* ```
|
|
18
|
-
*/ export const Trans = ({ children })=>{
|
|
19
|
-
// In a full implementation, this would extract and translate the content
|
|
20
|
-
// For now, we just render children as-is (works for English/default locale)
|
|
21
|
-
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
22
|
-
children: children
|
|
23
|
-
});
|
|
24
|
-
};
|
package/dist/i18n/context.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hono JSX i18n Context System
|
|
3
|
-
*
|
|
4
|
-
* Mimics React's Context API for Hono JSX to provide i18n without prop drilling
|
|
5
|
-
*/ import { jsx as _jsx, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import { getI18n as getI18nFromContext } from "./i18n.js";
|
|
7
|
-
// Store i18n instance during render
|
|
8
|
-
let currentI18n = null;
|
|
9
|
-
export const I18nProvider = ({ c, children })=>{
|
|
10
|
-
// Set current i18n for this render
|
|
11
|
-
// Note: In Hono JSX, rendering is synchronous and single-threaded per request
|
|
12
|
-
// so we can safely set global context without cleanup
|
|
13
|
-
currentI18n = getI18nFromContext(c);
|
|
14
|
-
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
15
|
-
children: children
|
|
16
|
-
});
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* useLingui hook - get i18n instance and translation function
|
|
20
|
-
* Mimics @lingui/react's useLingui() API
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```tsx
|
|
24
|
-
* import { t } from "@lingui/core/macro";
|
|
25
|
-
* import { useLingui } from "../i18n/index.js";
|
|
26
|
-
*
|
|
27
|
-
* function MyComponent() {
|
|
28
|
-
* const { t: _ } = useLingui(); // Use _ to avoid conflict with macro
|
|
29
|
-
*
|
|
30
|
-
* return (
|
|
31
|
-
* <div>
|
|
32
|
-
* <h1>{_(t({ message: "Dashboard", comment: "@context: Page title" }))}</h1>
|
|
33
|
-
* </div>
|
|
34
|
-
* );
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*
|
|
38
|
-
* Or use the i18n instance directly:
|
|
39
|
-
* ```tsx
|
|
40
|
-
* const { i18n } = useLingui();
|
|
41
|
-
* i18n._(t({ message: "Dashboard", comment: "@context: Page title" }))
|
|
42
|
-
* ```
|
|
43
|
-
*/ export function useLingui() {
|
|
44
|
-
if (!currentI18n) {
|
|
45
|
-
throw new Error("useLingui() called outside of I18nProvider. " + "Make sure your component is wrapped in <I18nProvider c={c}>...</I18nProvider>");
|
|
46
|
-
}
|
|
47
|
-
const translate = (descriptor)=>{
|
|
48
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- currentI18n is checked above
|
|
49
|
-
return currentI18n._(descriptor);
|
|
50
|
-
};
|
|
51
|
-
return {
|
|
52
|
-
i18n: currentI18n,
|
|
53
|
-
// t function - can be used with t macro from @lingui/core/macro
|
|
54
|
-
t: translate,
|
|
55
|
-
// _ is an alias for t (shorter)
|
|
56
|
-
_: translate
|
|
57
|
-
};
|
|
58
|
-
}
|
package/dist/i18n/detect.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Language Detection Utilities
|
|
3
|
-
*/ import { locales, isLocale } from "./locales.js";
|
|
4
|
-
/**
|
|
5
|
-
* Get display name for a language code
|
|
6
|
-
*/ export function getLanguageDisplayName(locale) {
|
|
7
|
-
const names = {
|
|
8
|
-
en: "English",
|
|
9
|
-
"zh-Hans": "简体中文",
|
|
10
|
-
"zh-Hant": "繁體中文"
|
|
11
|
-
};
|
|
12
|
-
return names[locale];
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Get all supported languages with display names
|
|
16
|
-
*/ export function getSupportedLanguages() {
|
|
17
|
-
return locales.map((code)=>({
|
|
18
|
-
code,
|
|
19
|
-
name: getLanguageDisplayName(code)
|
|
20
|
-
}));
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Check if a language code is valid
|
|
24
|
-
*/ export function isValidLanguage(lang) {
|
|
25
|
-
return isLocale(lang);
|
|
26
|
-
}
|