@jant/core 0.0.1

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 (93) hide show
  1. package/bin/jant.js +188 -0
  2. package/drizzle.config.ts +10 -0
  3. package/lingui.config.ts +16 -0
  4. package/package.json +116 -0
  5. package/src/app.tsx +377 -0
  6. package/src/assets/datastar.min.js +8 -0
  7. package/src/auth.ts +38 -0
  8. package/src/client.ts +6 -0
  9. package/src/db/index.ts +14 -0
  10. package/src/db/migrations/0000_solid_moon_knight.sql +118 -0
  11. package/src/db/migrations/0001_add_search_fts.sql +40 -0
  12. package/src/db/migrations/0002_collection_path.sql +2 -0
  13. package/src/db/migrations/0003_collection_path_nullable.sql +21 -0
  14. package/src/db/migrations/0004_media_uuid.sql +35 -0
  15. package/src/db/migrations/meta/0000_snapshot.json +784 -0
  16. package/src/db/migrations/meta/_journal.json +41 -0
  17. package/src/db/schema.ts +159 -0
  18. package/src/i18n/EXAMPLES.md +235 -0
  19. package/src/i18n/README.md +296 -0
  20. package/src/i18n/Trans.tsx +31 -0
  21. package/src/i18n/context.tsx +101 -0
  22. package/src/i18n/detect.ts +100 -0
  23. package/src/i18n/i18n.ts +62 -0
  24. package/src/i18n/index.ts +65 -0
  25. package/src/i18n/locales/en.po +875 -0
  26. package/src/i18n/locales/en.ts +1 -0
  27. package/src/i18n/locales/zh-Hans.po +875 -0
  28. package/src/i18n/locales/zh-Hans.ts +1 -0
  29. package/src/i18n/locales/zh-Hant.po +875 -0
  30. package/src/i18n/locales/zh-Hant.ts +1 -0
  31. package/src/i18n/locales.ts +14 -0
  32. package/src/i18n/middleware.ts +59 -0
  33. package/src/index.ts +42 -0
  34. package/src/lib/assets.ts +47 -0
  35. package/src/lib/constants.ts +67 -0
  36. package/src/lib/image.ts +107 -0
  37. package/src/lib/index.ts +9 -0
  38. package/src/lib/markdown.ts +93 -0
  39. package/src/lib/schemas.ts +92 -0
  40. package/src/lib/sqid.ts +79 -0
  41. package/src/lib/sse.ts +152 -0
  42. package/src/lib/time.ts +117 -0
  43. package/src/lib/url.ts +107 -0
  44. package/src/middleware/auth.ts +59 -0
  45. package/src/routes/api/posts.ts +127 -0
  46. package/src/routes/api/search.ts +53 -0
  47. package/src/routes/api/upload.ts +240 -0
  48. package/src/routes/dash/collections.tsx +341 -0
  49. package/src/routes/dash/index.tsx +89 -0
  50. package/src/routes/dash/media.tsx +551 -0
  51. package/src/routes/dash/pages.tsx +245 -0
  52. package/src/routes/dash/posts.tsx +202 -0
  53. package/src/routes/dash/redirects.tsx +155 -0
  54. package/src/routes/dash/settings.tsx +93 -0
  55. package/src/routes/feed/rss.ts +119 -0
  56. package/src/routes/feed/sitemap.ts +75 -0
  57. package/src/routes/pages/archive.tsx +223 -0
  58. package/src/routes/pages/collection.tsx +79 -0
  59. package/src/routes/pages/home.tsx +93 -0
  60. package/src/routes/pages/page.tsx +64 -0
  61. package/src/routes/pages/post.tsx +81 -0
  62. package/src/routes/pages/search.tsx +162 -0
  63. package/src/services/collection.ts +180 -0
  64. package/src/services/index.ts +40 -0
  65. package/src/services/media.ts +97 -0
  66. package/src/services/post.ts +279 -0
  67. package/src/services/redirect.ts +74 -0
  68. package/src/services/search.ts +117 -0
  69. package/src/services/settings.ts +76 -0
  70. package/src/theme/components/ActionButtons.tsx +98 -0
  71. package/src/theme/components/CrudPageHeader.tsx +48 -0
  72. package/src/theme/components/DangerZone.tsx +77 -0
  73. package/src/theme/components/EmptyState.tsx +56 -0
  74. package/src/theme/components/ListItemRow.tsx +24 -0
  75. package/src/theme/components/PageForm.tsx +114 -0
  76. package/src/theme/components/Pagination.tsx +196 -0
  77. package/src/theme/components/PostForm.tsx +122 -0
  78. package/src/theme/components/PostList.tsx +68 -0
  79. package/src/theme/components/ThreadView.tsx +118 -0
  80. package/src/theme/components/TypeBadge.tsx +28 -0
  81. package/src/theme/components/VisibilityBadge.tsx +33 -0
  82. package/src/theme/components/index.ts +12 -0
  83. package/src/theme/index.ts +24 -0
  84. package/src/theme/layouts/BaseLayout.tsx +49 -0
  85. package/src/theme/layouts/DashLayout.tsx +108 -0
  86. package/src/theme/layouts/index.ts +2 -0
  87. package/src/theme/styles/main.css +52 -0
  88. package/src/types.ts +222 -0
  89. package/static/assets/datastar.min.js +7 -0
  90. package/static/assets/image-processor.js +234 -0
  91. package/tsconfig.json +16 -0
  92. package/vite.config.ts +82 -0
  93. package/wrangler.toml +21 -0
package/bin/jant.js ADDED
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Jant CLI
5
+ *
6
+ * Commands:
7
+ * swizzle <component> [--wrap|--eject] - Override a theme component
8
+ */
9
+
10
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
11
+ import { resolve, dirname } from "path";
12
+ import { fileURLToPath } from "url";
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ // Available components that can be swizzled
18
+ const SWIZZLABLE_COMPONENTS = {
19
+ PostCard: {
20
+ file: "PostCard.tsx",
21
+ props: "PostCardProps",
22
+ },
23
+ PostList: {
24
+ file: "PostList.tsx",
25
+ props: "PostListProps",
26
+ },
27
+ Pagination: {
28
+ file: "Pagination.tsx",
29
+ props: "PaginationProps",
30
+ },
31
+ EmptyState: {
32
+ file: "EmptyState.tsx",
33
+ props: "EmptyStateProps",
34
+ },
35
+ BaseLayout: {
36
+ file: "BaseLayout.tsx",
37
+ props: "BaseLayoutProps",
38
+ isLayout: true,
39
+ },
40
+ };
41
+
42
+ function showHelp() {
43
+ console.log(`
44
+ Jant CLI
45
+
46
+ Usage:
47
+ jant swizzle <component> [options]
48
+
49
+ Commands:
50
+ swizzle <component> Override a theme component
51
+
52
+ Options:
53
+ --wrap Create a wrapper around the original component (default)
54
+ --eject Copy the full component source for complete customization
55
+ --list List available components
56
+
57
+ Examples:
58
+ jant swizzle PostCard # Wrap PostCard component
59
+ jant swizzle PostCard --eject # Copy PostCard source
60
+ jant swizzle --list # List all swizzlable components
61
+ `);
62
+ }
63
+
64
+ function listComponents() {
65
+ console.log("\nAvailable components to swizzle:\n");
66
+ for (const [name, info] of Object.entries(SWIZZLABLE_COMPONENTS)) {
67
+ const type = info.isLayout ? "[Layout]" : "[Component]";
68
+ console.log(` ${name.padEnd(15)} ${type}`);
69
+ }
70
+ console.log("\nUsage: jant swizzle <component> [--wrap|--eject]\n");
71
+ }
72
+
73
+ function generateWrapperCode(componentName, info) {
74
+ const importPath = info.isLayout
75
+ ? "@jant/core/theme/layouts"
76
+ : "@jant/core/theme/components";
77
+
78
+ return `/**
79
+ * Custom ${componentName} component
80
+ *
81
+ * This is a wrapper around the original ${componentName}.
82
+ * You can customize the rendering while keeping the original functionality.
83
+ */
84
+
85
+ import type { ${info.props} } from "@jant/core";
86
+ import { ${componentName} as Original${componentName} } from "${importPath}";
87
+
88
+ export function ${componentName}(props: ${info.props}) {
89
+ // Add your customizations here
90
+ return (
91
+ <div class="custom-${componentName.toLowerCase()}-wrapper">
92
+ <Original${componentName} {...props} />
93
+ </div>
94
+ );
95
+ }
96
+ `;
97
+ }
98
+
99
+ function swizzle(componentName, mode) {
100
+ const info = SWIZZLABLE_COMPONENTS[componentName];
101
+ if (!info) {
102
+ console.error(`Error: Unknown component "${componentName}"`);
103
+ console.log("\nAvailable components:");
104
+ listComponents();
105
+ process.exit(1);
106
+ }
107
+
108
+ const targetDir = info.isLayout
109
+ ? resolve(process.cwd(), "src/theme/layouts")
110
+ : resolve(process.cwd(), "src/theme/components");
111
+
112
+ const targetFile = resolve(targetDir, info.file);
113
+
114
+ // Check if file already exists
115
+ if (existsSync(targetFile)) {
116
+ console.error(`Error: ${targetFile} already exists`);
117
+ console.log("Remove it first if you want to re-swizzle.");
118
+ process.exit(1);
119
+ }
120
+
121
+ // Create directory if needed
122
+ mkdirSync(targetDir, { recursive: true });
123
+
124
+ if (mode === "eject") {
125
+ // For eject mode, we'd need to copy the actual source
126
+ // For now, show a message about where to find it
127
+ console.log(`
128
+ To eject ${componentName}, copy the source from:
129
+ node_modules/@jant/core/src/theme/${info.isLayout ? "layouts" : "components"}/${info.file}
130
+
131
+ Then modify it as needed.
132
+ `);
133
+ return;
134
+ }
135
+
136
+ // Generate wrapper code
137
+ const code = generateWrapperCode(componentName, info);
138
+ writeFileSync(targetFile, code, "utf-8");
139
+
140
+ console.log(`
141
+ ✓ Created ${targetFile}
142
+
143
+ Next steps:
144
+ 1. Customize the component in the generated file
145
+ 2. Import it in your src/index.ts:
146
+
147
+ import { ${componentName} } from "./theme/${info.isLayout ? "layouts" : "components"}/${componentName}";
148
+
149
+ export default createApp({
150
+ theme: {
151
+ components: {
152
+ ${componentName},
153
+ },
154
+ },
155
+ });
156
+ `);
157
+ }
158
+
159
+ // Parse arguments
160
+ const args = process.argv.slice(2);
161
+
162
+ if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
163
+ showHelp();
164
+ process.exit(0);
165
+ }
166
+
167
+ const command = args[0];
168
+
169
+ if (command === "swizzle") {
170
+ if (args.includes("--list")) {
171
+ listComponents();
172
+ process.exit(0);
173
+ }
174
+
175
+ const componentName = args[1];
176
+ if (!componentName) {
177
+ console.error("Error: Component name required");
178
+ console.log("Usage: jant swizzle <component> [--wrap|--eject]");
179
+ process.exit(1);
180
+ }
181
+
182
+ const mode = args.includes("--eject") ? "eject" : "wrap";
183
+ swizzle(componentName, mode);
184
+ } else {
185
+ console.error(`Unknown command: ${command}`);
186
+ showHelp();
187
+ process.exit(1);
188
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "drizzle-kit";
2
+
3
+ export default defineConfig({
4
+ schema: "./src/db/schema.ts",
5
+ out: "./src/db/migrations",
6
+ dialect: "sqlite",
7
+ dbCredentials: {
8
+ url: ".wrangler/state/v3/d1/miniflare-D1DatabaseObject/local.sqlite",
9
+ },
10
+ });
@@ -0,0 +1,16 @@
1
+ import type { LinguiConfig } from "@lingui/conf";
2
+
3
+ const config: LinguiConfig = {
4
+ locales: ["en", "zh-Hans", "zh-Hant"],
5
+ sourceLocale: "en",
6
+ catalogs: [
7
+ {
8
+ path: "<rootDir>/src/i18n/locales/{locale}",
9
+ include: ["<rootDir>/src/**/*.{ts,tsx}"],
10
+ },
11
+ ],
12
+ format: "po",
13
+ compileNamespace: "ts",
14
+ };
15
+
16
+ export default config;
package/package.json ADDED
@@ -0,0 +1,116 @@
1
+ {
2
+ "name": "@jant/core",
3
+ "version": "0.0.1",
4
+ "description": "A modern, open-source microblogging platform built on Cloudflare Workers",
5
+ "type": "module",
6
+ "bin": {
7
+ "jant": "./bin/jant.js"
8
+ },
9
+ "exports": {
10
+ ".": "./src/index.ts",
11
+ "./theme": "./src/theme/index.ts",
12
+ "./theme/*": "./src/theme/*",
13
+ "./src/*": "./src/*",
14
+ "./static/*": "./static/*"
15
+ },
16
+ "files": [
17
+ "bin",
18
+ "src",
19
+ "static",
20
+ "migrations",
21
+ "drizzle.config.ts",
22
+ "lingui.config.ts",
23
+ "postcss.config.js",
24
+ "tailwind.config.ts",
25
+ "tsconfig.json",
26
+ "vite.config.ts",
27
+ "wrangler.toml"
28
+ ],
29
+ "scripts": {
30
+ "dev": "pnpm db:migrate:local && vite dev",
31
+ "dev:debug": "pnpm db:migrate:local && vite dev --port 19019",
32
+ "build": "vite build",
33
+ "deploy": "pnpm build && wrangler deploy",
34
+ "preview": "vite preview",
35
+ "typecheck": "tsc --noEmit",
36
+ "lint": "eslint src/",
37
+ "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
38
+ "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
39
+ "db:generate": "drizzle-kit generate",
40
+ "db:migrate:local": "wrangler d1 migrations apply DB --local",
41
+ "db:migrate:remote": "wrangler d1 migrations apply DB --remote",
42
+ "i18n:extract": "lingui extract",
43
+ "i18n:compile": "lingui compile --typescript",
44
+ "i18n:build": "pnpm i18n:extract && pnpm i18n:compile",
45
+ "prepare": "husky"
46
+ },
47
+ "dependencies": {
48
+ "@lingui/core": "^5.9.0",
49
+ "better-auth": "^1.4.18",
50
+ "drizzle-orm": "^0.45.1",
51
+ "hono": "^4.11.7",
52
+ "marked": "^17.0.1",
53
+ "sqids": "^0.3.0",
54
+ "uuidv7": "^1.1.0",
55
+ "zod": "^4.3.6"
56
+ },
57
+ "devDependencies": {
58
+ "@babel/cli": "^7.28.6",
59
+ "@babel/core": "^7.29.0",
60
+ "@babel/preset-typescript": "^7.28.5",
61
+ "@cloudflare/vite-plugin": "^1.22.1",
62
+ "@cloudflare/workers-types": "^4.20260131.0",
63
+ "@eslint/js": "^9.39.2",
64
+ "@lingui/babel-plugin-lingui-macro": "^5.9.0",
65
+ "@lingui/cli": "^5.9.0",
66
+ "@lingui/format-json": "^5.9.0",
67
+ "@lingui/format-po": "^5.9.0",
68
+ "@lingui/swc-plugin": "^5.10.1",
69
+ "@swc/core": "^1.15.11",
70
+ "@tailwindcss/cli": "^4.1.18",
71
+ "@tailwindcss/postcss": "^4.1.18",
72
+ "@types/node": "^25.1.0",
73
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
74
+ "@typescript-eslint/parser": "^8.54.0",
75
+ "autoprefixer": "^10.4.24",
76
+ "basecoat-css": "^0.3.10",
77
+ "drizzle-kit": "^0.31.8",
78
+ "esbuild-plugin-lingui-macro": "^1.0.1",
79
+ "eslint": "^9.39.2",
80
+ "eslint-plugin-react": "^7.37.5",
81
+ "glob": "^13.0.0",
82
+ "husky": "^9.1.7",
83
+ "lint-staged": "^16.2.7",
84
+ "postcss": "^8.5.6",
85
+ "prettier": "^3.8.1",
86
+ "tailwindcss": "^4.1.18",
87
+ "tsx": "^4.21.0",
88
+ "typescript": "^5.9.3",
89
+ "unplugin-swc": "^1.5.9",
90
+ "vite": "^7.3.1",
91
+ "wrangler": "^4.61.1"
92
+ },
93
+ "repository": {
94
+ "type": "git",
95
+ "url": "https://github.com/jant-me/jant.git",
96
+ "directory": "packages/core"
97
+ },
98
+ "keywords": [
99
+ "jant",
100
+ "microblog",
101
+ "cloudflare",
102
+ "workers",
103
+ "hono",
104
+ "d1",
105
+ "r2"
106
+ ],
107
+ "author": "Jant Contributors",
108
+ "license": "MIT",
109
+ "bugs": {
110
+ "url": "https://github.com/jant-me/jant/issues"
111
+ },
112
+ "homepage": "https://jant.me",
113
+ "engines": {
114
+ "node": ">=24.0.0"
115
+ }
116
+ }