create-jant 0.3.23 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import path from "path";
9
9
  import { fileURLToPath } from "url";
10
10
  var __filename = fileURLToPath(import.meta.url);
11
11
  var __dirname = path.dirname(__filename);
12
- var CORE_VERSION = "0.3.23";
12
+ var CORE_VERSION = "0.3.25";
13
13
  var TEMPLATE_DIR = fs.existsSync(path.resolve(__dirname, "../template")) ? path.resolve(__dirname, "../template") : path.resolve(__dirname, "../../../templates/jant-site");
14
14
  function isValidProjectName(name) {
15
15
  return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(name);
@@ -40,6 +40,9 @@ function detectPackageManager() {
40
40
  function formatRunCmd(pm, script) {
41
41
  return pm === "npm" ? `npm run ${script}` : `${pm} ${script}`;
42
42
  }
43
+ function formatAddCmd(pm, pkg) {
44
+ return pm === "npm" ? `npm install ${pkg}` : `${pm} add ${pkg}`;
45
+ }
43
46
  function runCommand(cmd, cwd) {
44
47
  try {
45
48
  execSync(cmd, { stdio: "ignore", cwd });
@@ -174,6 +177,21 @@ S3_SECRET_ACCESS_KEY=
174
177
  content = processMarkers(content, {});
175
178
  await fs.writeFile(viteConfigPath, content, "utf-8");
176
179
  }
180
+ const readmePath = path.join(targetDir, "README.md");
181
+ if (packageManager !== "npm" && await fs.pathExists(readmePath)) {
182
+ let readme = await fs.readFile(readmePath, "utf-8");
183
+ readme = readme.replace(
184
+ /npm install (\S+)/g,
185
+ (_, pkg) => formatAddCmd(packageManager, pkg)
186
+ );
187
+ readme = readme.replace(/npm install/g, `${packageManager} install`);
188
+ readme = readme.replace(
189
+ /npm run (\S+)/g,
190
+ (_, script) => formatRunCmd(packageManager, script)
191
+ );
192
+ readme = readme.replace(/npm create/g, `${packageManager} create`);
193
+ await fs.writeFile(readmePath, readme, "utf-8");
194
+ }
177
195
  if (packageManager === "pnpm") {
178
196
  const wsSource = path.join(TEMPLATE_DIR, "pnpm-workspace.yaml");
179
197
  if (await fs.pathExists(wsSource)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-jant",
3
- "version": "0.3.23",
3
+ "version": "0.3.25",
4
4
  "description": "Create a new Jant project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -207,65 +207,60 @@ For all available variables (site name, language, R2 storage, image optimization
207
207
 
208
208
  ## Customization
209
209
 
210
- ### Theme Components
210
+ ### Color Themes
211
211
 
212
- Override theme components by creating files in `src/theme/components/`:
212
+ Pick a color theme from the dashboard: **Settings > Appearance**.
213
213
 
214
- ```typescript
215
- // src/theme/components/PostCard.tsx
216
- import type { PostCardProps } from "@jant/core";
217
- import { PostCard as OriginalPostCard } from "@jant/core/theme";
218
-
219
- export function PostCard(props: PostCardProps) {
220
- return (
221
- <div class="my-wrapper">
222
- <OriginalPostCard {...props} />
223
- </div>
224
- );
225
- }
226
- ```
214
+ ### Custom CSS
227
215
 
228
- Then register it in `src/index.ts`:
216
+ Inject custom CSS from the dashboard: **Settings > Appearance > Custom CSS**. This CSS is applied with the highest priority, so you can override any built-in styles.
229
217
 
230
- ```typescript
231
- import { createApp } from "@jant/core";
232
- import { PostCard } from "./theme/components/PostCard";
218
+ ### CSS Design Tokens
233
219
 
234
- export default createApp({
235
- theme: {
236
- components: {
237
- PostCard,
238
- },
239
- },
240
- });
220
+ Override design tokens in your custom CSS to change the look and feel:
221
+
222
+ ```css
223
+ :root {
224
+ --site-width: 720px;
225
+ --card-radius: 0.5rem;
226
+ --font-body: "Inter", system-ui, sans-serif;
227
+ --avatar-size: 42px;
228
+ }
241
229
  ```
242
230
 
243
- ### Custom Styles
231
+ ### Data Attributes
244
232
 
245
- Add custom CSS in `src/theme/styles/`:
233
+ Target specific elements with stable data attributes:
246
234
 
247
235
  ```css
248
- /* src/theme/styles/custom.css */
249
- @import "@jant/core/theme/styles/main.css";
236
+ /* Style only note-format posts */
237
+ [data-format="note"] {
238
+ border-left: 3px solid var(--primary);
239
+ }
240
+
241
+ /* Style the home page differently */
242
+ [data-page="home"] {
243
+ background: var(--muted);
244
+ }
250
245
 
251
- /* Your custom styles */
252
- .my-custom-class {
253
- /* ... */
246
+ /* Hide compose prompt for unauthenticated visitors */
247
+ body:not([data-authenticated]) .compose-prompt {
248
+ display: none;
254
249
  }
255
250
  ```
256
251
 
257
- ### Using Third-Party Themes
252
+ ### Code-Level Customization
258
253
 
259
- ```bash
260
- npm install @jant-themes/minimal
261
- ```
254
+ Pass CSS variable overrides or custom color themes via `createApp()`:
262
255
 
263
256
  ```typescript
264
257
  import { createApp } from "@jant/core";
265
- import { theme as MinimalTheme } from "@jant-themes/minimal";
266
258
 
267
259
  export default createApp({
268
- theme: MinimalTheme,
260
+ cssVariables: {
261
+ "--site-width": "720px",
262
+ "--card-radius": "0.5rem",
263
+ },
269
264
  });
270
265
  ```
271
266
 
@@ -1,5 +1,5 @@
1
1
  import { execSync } from "child_process";
2
- import { writeFileSync } from "fs";
2
+ import { readFileSync, writeFileSync } from "fs";
3
3
  import { resolve, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
 
@@ -53,20 +53,27 @@ function dumpTable(name, query) {
53
53
 
54
54
  const header = `-- =============================================================================
55
55
  -- Demo seed data for Jant (demo.jant.me)
56
- -- Exported from remote demo D1 database via: mise run demo-backup
56
+ -- Exported from remote demo D1 database via: mise run demo-export
57
57
  -- Usage: mise run demo-reset
58
58
  -- =============================================================================
59
59
  `;
60
60
 
61
+ // Read reset-demo.sql and include it at the top
62
+ const resetSql = readFileSync(resolve(__dirname, "reset-demo.sql"), "utf-8");
63
+
61
64
  const tables = [
62
65
  // settings, user, account are preserved by reset-demo.sql — don't export
63
66
  ["posts", "SELECT * FROM posts WHERE deleted_at IS NULL"],
67
+ ["pages"],
64
68
  ["collections"],
65
- ["post_collections"],
69
+ ["nav_items"],
66
70
  ["media"],
67
71
  ];
68
72
 
69
73
  let sql = header;
74
+ sql += "\n-- Reset (clear existing content)\n";
75
+ sql += resetSql.replace(/^--.*\n/gm, "").trim() + "\n";
76
+
70
77
  for (const [name, query] of tables) {
71
78
  const data = dumpTable(name, query);
72
79
  if (data) sql += `\n-- ${name}\n${data}\n`;
@@ -1,11 +1,12 @@
1
1
  import { createRequire } from "module";
2
- import { readdirSync, writeFileSync } from "fs";
2
+ import { readFileSync, readdirSync, writeFileSync } from "fs";
3
3
  import { resolve, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
 
6
6
  // Parse flags
7
7
  const args = process.argv.slice(2);
8
8
  const noMedia = args.includes("--no-media");
9
+ const noAuth = args.includes("--no-auth");
9
10
  const outputIndex = args.indexOf("--output");
10
11
  const outputFile =
11
12
  outputIndex !== -1 ? args[outputIndex + 1] : "seed-local.sql";
@@ -53,17 +54,14 @@ const header = `-- =============================================================
53
54
  `;
54
55
 
55
56
  const tables = [
56
- ["settings"],
57
- ["user"],
58
- ["account"],
57
+ ...(!noAuth ? [["settings"], ["user"], ["account"]] : []),
58
+ ["pages"],
59
+ ["collections"],
59
60
  [
60
61
  "posts",
61
- noMedia
62
- ? "SELECT * FROM posts WHERE deleted_at IS NULL AND type != 'image'"
63
- : "SELECT * FROM posts WHERE deleted_at IS NULL",
62
+ "SELECT * FROM posts WHERE deleted_at IS NULL",
64
63
  ],
65
- ["collections"],
66
- ["post_collections"],
64
+ ["nav_items"],
67
65
  ];
68
66
 
69
67
  // Include media table only when --no-media is not set
@@ -72,6 +70,14 @@ if (!noMedia) {
72
70
  }
73
71
 
74
72
  let sql = header;
73
+
74
+ // When --no-auth, embed reset statements so everything runs in a single D1 import
75
+ if (noAuth) {
76
+ const resetSql = readFileSync(resolve(__dirname, "reset-demo.sql"), "utf-8");
77
+ sql += "\n-- Reset (clear existing content)\n";
78
+ sql += resetSql.replace(/^--.*\n/gm, "").trim() + "\n";
79
+ }
80
+
75
81
  for (const [name, query] of tables) {
76
82
  const data = dumpTable(name, query);
77
83
  if (data) sql += `\n-- ${name}\n${data}\n`;
@@ -1,24 +1,20 @@
1
- -- Reset script for Jant demo site (demo.jant.me)
2
- -- Clears all user-created data while preserving users/schema
1
+ -- Reset script for Jant demo site (demo.jant.me) (v2 schema)
2
+ -- Clears content data while preserving users/settings/schema
3
3
  -- Usage: mise run demo-reset (runs this then seed-demo.sql)
4
4
 
5
- -- Clear FTS index first (to avoid foreign key issues)
5
+ -- Clear FTS index first (to avoid trigger issues)
6
6
  DELETE FROM posts_fts;
7
7
 
8
- -- Clear join tables
9
- DELETE FROM post_collections;
10
-
11
- -- Clear main tables
8
+ -- Clear main tables (order matters for FK constraints)
9
+ DELETE FROM nav_items;
12
10
  DELETE FROM media;
13
11
  DELETE FROM posts;
12
+ DELETE FROM pages;
14
13
  DELETE FROM collections;
15
14
  DELETE FROM redirects;
16
15
 
17
16
  -- Sessions, users, accounts, and settings are preserved
18
- -- (only content data is reset)
17
+ -- (seed-demo.sql only contains content data)
19
18
 
20
19
  -- Reset auto-increment counters
21
- DELETE FROM sqlite_sequence WHERE name IN ('posts', 'media', 'collections', 'redirects');
22
-
23
- -- Note: Settings and users are preserved
24
- -- Seed data will be re-inserted by seed-demo.sql
20
+ DELETE FROM sqlite_sequence WHERE name IN ('posts', 'pages', 'collections', 'nav_items', 'redirects');
@@ -1,24 +1,25 @@
1
- -- Reset script for Jant demo site (demo.jant.me)
2
- -- Clears all user-created data while preserving users/schema
3
- -- Usage: mise run demo-reset (runs this then seed-demo.sql)
1
+ -- Reset script for Jant local development (v2 schema)
2
+ -- Clears all data so seed-local.sql can re-insert everything
3
+ -- Usage: mise run db-seed (runs this then seed-local.sql)
4
4
 
5
- -- Clear FTS index first (to avoid foreign key issues)
5
+ -- Clear FTS index first (to avoid trigger issues)
6
6
  DELETE FROM posts_fts;
7
7
 
8
- -- Clear join tables
9
- DELETE FROM post_collections;
10
-
11
- -- Clear main tables
8
+ -- Clear main tables (order matters for FK constraints)
9
+ DELETE FROM nav_items;
12
10
  DELETE FROM media;
13
11
  DELETE FROM posts;
12
+ DELETE FROM pages;
14
13
  DELETE FROM collections;
15
14
  DELETE FROM redirects;
16
15
 
17
- -- Sessions, users, accounts, and settings are preserved
18
- -- (only content data is reset)
16
+ -- Clear auth tables (order matters: session → account → user)
17
+ DELETE FROM session;
18
+ DELETE FROM account;
19
+ DELETE FROM user;
19
20
 
20
- -- Reset auto-increment counters
21
- DELETE FROM sqlite_sequence WHERE name IN ('posts', 'media', 'collections', 'redirects');
21
+ -- Clear settings
22
+ DELETE FROM settings;
22
23
 
23
- -- Note: Settings and users are preserved
24
- -- Seed data will be re-inserted by seed-demo.sql
24
+ -- Reset auto-increment counters
25
+ DELETE FROM sqlite_sequence WHERE name IN ('posts', 'pages', 'collections', 'nav_items', 'redirects');
@@ -1,195 +1,187 @@
1
1
  -- =============================================================================
2
2
  -- Demo seed data for Jant (demo.jant.me)
3
- -- Exported from remote demo D1 database via: mise run demo-backup
3
+ -- Exported from remote demo D1 database via: mise run demo-export
4
4
  -- Usage: mise run demo-reset
5
5
  -- =============================================================================
6
6
 
7
- -- posts
8
- INSERT INTO posts VALUES(1,'article','featured','Welcome to Jant',NULL,'# Welcome to Jant Demo
9
-
10
- Jant is a modern microblog platform built for Cloudflare Workers. This demo site resets daily at 00:00 UTC.
11
-
12
- ## Features
13
-
14
- - **Multiple post types**: Notes, articles, links, quotes, images, and pages
15
- - **Collections**: Organize posts into collections
16
- - **Full-text search**: Search across all your content
17
- - **Internationalization**: Built-in i18n support
18
- - **Fast**: Edge-deployed on Cloudflare Workers
19
-
20
- ## Getting Started
21
-
22
- ```bash
23
- pnpm create jant my-blog
24
- cd my-blog
25
- pnpm install
26
- pnpm dev
27
- ```
28
-
29
- Visit the [dashboard](/dash) to create your own posts!','<h1>Welcome to Jant Demo</h1>
30
- <p>Jant is a modern microblog platform built for Cloudflare Workers. This demo site resets daily at 00:00 UTC.</p>
31
- <h2>Features</h2>
32
- <ul>
33
- <li><strong>Multiple post types</strong>: Notes, articles, links, quotes, images, and pages</li>
34
- <li><strong>Collections</strong>: Organize posts into collections</li>
35
- <li><strong>Full-text search</strong>: Search across all your content</li>
36
- <li><strong>Internationalization</strong>: Built-in i18n support</li>
37
- <li><strong>Fast</strong>: Edge-deployed on Cloudflare Workers</li>
38
- </ul>
39
- <h2>Getting Started</h2>
40
- <pre><code class="language-bash">pnpm create jant my-blog
41
- cd my-blog
42
- pnpm install
43
- pnpm dev
44
- </code></pre>
45
- <p>Visit the <a href="/dash">dashboard</a> to create your own posts!</p>',NULL,NULL,NULL,NULL,NULL,NULL,1770689095,1770689095,1770689095);
46
- INSERT INTO posts VALUES(2,'note','quiet',NULL,NULL,'This is a demo note. Notes are short posts without titles, perfect for quick thoughts and updates.','<p>This is a demo note. Notes are short posts without titles, perfect for quick thoughts and updates.</p>',NULL,NULL,NULL,NULL,NULL,NULL,1770685495,1770685495,1770685495);
47
- INSERT INTO posts VALUES(3,'link','quiet','Jant on GitHub',NULL,'Check out the source code and documentation for Jant.','<p>Check out the source code and documentation for Jant.</p>','https://github.com/jant-me/jant','GitHub','github.com',NULL,NULL,NULL,1770681895,1770681895,1770681895);
48
- INSERT INTO posts VALUES(4,'quote','quiet',NULL,NULL,'The best way to predict the future is to invent it.','<p>The best way to predict the future is to invent it.</p>',NULL,'Alan Kay',NULL,NULL,NULL,NULL,1770678295,1770678295,1770678295);
49
- INSERT INTO posts VALUES(5,'image','quiet',NULL,NULL,'Image 1','<p>Image 1</p>
50
- ',NULL,NULL,NULL,NULL,NULL,NULL,1770758516,1770758516,1770758516);
51
- INSERT INTO posts VALUES(6,'image','quiet',NULL,NULL,'Image 2','<p>Image 2</p>
52
- ',NULL,NULL,NULL,NULL,NULL,NULL,1770758537,1770758537,1770759299);
53
- INSERT INTO posts VALUES(7,'page','unlisted','About','about','> **Work in Progress**: This project is still under active development and not yet ready for use. See the latest build at [demo.jant.me](https://demo.jant.me).
54
- >
55
- > Demo login: `demo@jant.me` / `demodemo` — Dashboard: [demo.jant.me/dash](https://demo.jant.me/dash)
56
-
57
- A personal microblogging system as smooth as <https://threads.com>.
58
-
59
- > **Jant** = Jantelagen (Law of Jante)
60
- > Low-key, de-socialized personal expression.
61
-
62
- ## What is Jant?
63
-
64
- Jant is a single-author microblog for people who want to share thoughts without the noise of social media. No followers, no likes, no retweets—just your words.
65
-
66
- **Features**:
67
-
68
- - Multiple content types: notes, articles, links, quotes, images
69
- - Thread support for longer thoughts
70
- - Collections for curated topics
71
- - Beautiful, themeable design
72
- - Deploys to Cloudflare Workers in minutes
73
-
74
- ## Quick Start
75
-
76
- ```bash
77
- # Create a new Jant site
78
- pnpm create jant my-blog
7
+ -- Reset (clear existing content)
8
+ DELETE FROM posts_fts;
79
9
 
80
- # Start development
81
- cd my-blog
82
- pnpm dev
10
+ DELETE FROM nav_items;
11
+ DELETE FROM media;
12
+ DELETE FROM posts;
13
+ DELETE FROM pages;
14
+ DELETE FROM collections;
15
+ DELETE FROM redirects;
83
16
 
84
- # Deploy to Cloudflare
85
- pnpm run deploy
86
- ```
87
17
 
88
- ## Documentation
18
+ DELETE FROM sqlite_sequence WHERE name IN ('posts', 'pages', 'collections', 'nav_items', 'redirects');
89
19
 
90
- - [Getting Started](docs/getting-started.md)
91
- - [Deployment](docs/deployment.md)
92
- - [Configuration](docs/configuration.md)
93
- - [Theming](docs/theming.md)
94
- - [API Reference](docs/API.md)
95
-
96
- ## Development
97
-
98
- Requires [mise](https://mise.jdx.dev/) it manages Node.js and pnpm automatically.
99
-
100
- ```bash
101
- # Install mise (macOS/Linux)
102
- curl https://mise.run | sh
103
-
104
- # Clone and setup
105
- git clone https://github.com/jant-me/jant.git
106
- cd jant
107
- mise install # installs Node.js and pnpm
108
- pnpm install # installs dependencies
109
-
110
- # Start development server (http://localhost:9019)
111
- mise run dev
112
- ```
113
-
114
- See [CONTRIBUTING.md](CONTRIBUTING.md) for code style, PR process, and release workflow.
115
-
116
- ## Philosophy
117
-
118
- Jant is built on the idea that not everything needs to be optimized for engagement. Write for yourself. Share if you want. No metrics, no pressure.
119
-
120
- ## License
121
-
122
- AGPL-3.0
123
- ','<blockquote>
124
- <p><strong>Work in Progress</strong>: This project is still under active development and not yet ready for use. See the latest build at <a href="https://demo.jant.me">demo.jant.me</a>.</p>
125
- <p>Demo login: <code>demo@jant.me</code> / <code>demodemo</code> — Dashboard: <a href="https://demo.jant.me/dash">demo.jant.me/dash</a></p>
126
- </blockquote>
127
- <p>A personal microblogging system as smooth as <a href="https://threads.com">https://threads.com</a>.</p>
20
+ -- posts
21
+ INSERT INTO posts VALUES(1,'note','published',0,0,NULL,'really?',NULL,'is this works?','<p>is this works?</p>
22
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771089786,1771089786,1771089786);
23
+ INSERT INTO posts VALUES(2,'quote','published',0,0,NULL,NULL,NULL,NULL,NULL,'worked?',NULL,NULL,NULL,NULL,NULL,1771089808,1771089808,1771089808);
24
+ INSERT INTO posts VALUES(3,'note','published',0,0,NULL,'greate image',NULL,'great','<p>great</p>
25
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771092931,1771092931,1771268464);
26
+ INSERT INTO posts VALUES(4,'note','published',0,0,NULL,NULL,NULL,'没有人真的想交付自己都看不上的东西。我们天然就想把事情做得更对一点、更好一点。','<p>没有人真的想交付自己都看不上的东西。我们天然就想把事情做得更对一点、更好一点。</p>
27
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771114861,1771114861,1771114861);
28
+ INSERT INTO posts VALUES(5,'note','published',0,0,NULL,NULL,NULL,'新荣记的老板会给出预算,让店长和主厨每两周可以自己订一家餐厅去体验,而且预算没有上限。从到东京筹备开店至今,他们已经吃了日本十几家不同的餐厅。很多餐厅的服务员从没吃过自家餐厅的招牌菜,而新荣记不但安排自己的厨师和服务人员吃自己家的菜,还会提供费用,让这些年轻人去他们向往的餐厅,体验别人家的优点。"。我想,对我们网上冲浪的启发就是我们也要多体验别人做的好东西,从中提高审美和获得灵感。','<p>新荣记的老板会给出预算,让店长和主厨每两周可以自己订一家餐厅去体验,而且预算没有上限。从到东京筹备开店至今,他们已经吃了日本十几家不同的餐厅。很多餐厅的服务员从没吃过自家餐厅的招牌菜,而新荣记不但安排自己的厨师和服务人员吃自己家的菜,还会提供费用,让这些年轻人去他们向往的餐厅,体验别人家的优点。&quot;。我想,对我们网上冲浪的启发就是我们也要多体验别人做的好东西,从中提高审美和获得灵感。</p>
29
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771114906,1771114906,1771114906);
30
+ INSERT INTO posts VALUES(6,'link','published',0,0,NULL,'Forty News','https://forty.news/','40年前今日的新闻...','<p>40年前今日的新闻...</p>
31
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771114955,1771114955,1771114955);
32
+ INSERT INTO posts VALUES(7,'note','published',0,0,NULL,'我从 Hacker News 中学到了什么?',NULL,'昨天看到 Hacker News 的创始人 Paul Graham 在 2009
33
+ 年写的一篇文章[《我从 Hacker News 中学到了什么》](http://www.paulgraham.com/hackernews.html),
34
+ 作者提到:
35
+
36
+ > 随着 Hacker News 变得越来越大,占用的时间比我预期的要多,但我并不后悔,因为我从工作中学到了很多东西。
37
+
38
+ 就挺感慨的,一方面是我亲自做了一遍 HN 的中文翻译,在整个过程中学到了很多,另一方面也让我回想起大学期间做的一个社区,对我的之后的种种影响。
39
+
40
+ 如果有机会的话,我觉得每个人都应该尽可能的去亲自参与创造某个能有其他人一起创造信息的社区,比如成立一个小组,一个论坛,一个社群等等,线下的可能更好。和人交流,能碰撞出我更多的想法,以及让我知道更大的世界,更多元的价值观。
41
+
42
+ [hacker news 的中文翻译](https://hn.buzzing.cc)是一个非常普通的想法,每年可能都有人声称自己做了一个 HN 的中文版。因为
43
+ HN 上不仅文章质量普遍优质,而且最重要的是: HN 的 API 是业界最友好的,所有人都可以用来练手,做一个 HN 的 XX
44
+ 版,比如各种平台的客户端,各种语言的实现,rss 服务,email 订阅,评论通知,大数据分析,newsletter
45
+ 等等,不胜枚举。由于我的英文水平并不能像母语一样让我能快速找到感兴趣的东西(后来发现其实不少看起来英文很好的人,也有类似的体验),所以如果我想阅读 HN
46
+ 的话,我可能有 2 个选择:
47
+
48
+ 1. 抱着锻炼自己英语的想法逼迫自己不太流利的浏览 HN
49
+ 2. 使用浏览器自带的翻译功能,浏览 HN
50
+
51
+ 这两个阶段我都经历过。我希望你不要经历,因为每个阶段都可能使你错过 HN,错过一个很优质的信息源。
52
+
53
+ 第一个阶段,我只能坚持几天,然后就会本能的优先去看其他中文信息,慢慢的其实就没在看 HN 了。这在我的推特账号上尤其明显,我注册了 2
54
+ 个推特号,一个是英文号[@TheOwenYoung](https://twitter.com/TheOwenYoung),一个是中文号[@OwenYoungZh](https://twitter.com/OwenYoungZh),本来是先开英文号的,专门关注的都是些英文博主,但是后面实在受不了了,说那就再开个中文号,尽量少关注人,只发一些东西。后来的故事就是,英文号上的很少,主力变成了中文号。唉,人就是离不开舒适圈,所以我们最好不要给自己创造舒适圈,比如在浏览器上设置很多社交网络的快捷书签;新
55
+ tab 页放很多快捷入口;手机上下载很多好用的应用;如果想克制自己,就最好只下必要应用,优先用网页版,或者像我现在做的,把 iPhone13
56
+ 当成测试机,用一台好几年前的 Pixel 3a,又卡,掉电还快,但是用起来其实完全没问题,然后我又重新开始用
57
+ Kindle,只能一个操作,就是下一页,这样就能节省很多时间来看书了。
58
+
59
+ 第二个阶段,我发现浏览器自带的翻译功能并不是太好用(去年我开始做 hn
60
+ 中文版的时候更不好用,现在其实好了很多),比如有一些网页它会失败,要么就是要重试,然后排版也不太好看,我更希望的是有些区域不要翻译,只翻译标题之类的,但是浏览器会全部翻译,就不太好浏览。而作为一个要长期阅读的地方,体验还是挺重要的。
61
+
62
+ 正好去年那会儿,刚做完[Actionsflow](https://github.com/actionsflow/actionsflow), 想利用
63
+ Actionsflow 做点什么,所以说那就做一个 HN
64
+ 的中文翻译吧,这很适合工作流去处理,处理完之后生成一个静态网页,就算没人用,我自己也可以天天用,做好之后也不用维护,就一直在那。于是就开干,没想到做到后面发现还可以顺便做点别的我感兴趣的英文内容翻译,比如[Reddit 的美股讨论](https://reddit.buzzing.cc),[国外的权威媒体报道](https://news.buzzing.cc),[ProductHunt](https://ph.buzzing.cc)等等,最后就汇总成了一个[Buzzing](https://www.buzzing.cc)
65
+
66
+ Buzzing
67
+ 运行了一年之后,发现之前的设计有不少欠考虑的地方,[Actionsflow](https://github.com/actionsflow/actionsflow)也有点太重了,尤其是看了一年
68
+ HN 的帖子后,觉得 Actionsflow
69
+ 的设计简直就是一坨屎,又臭又长。这个时候[才发现 Deno 是最适合做工作流的运行时](https://twitter.com/OwenYoungZh/status/1478928692781137925),Deno
70
+ 的依赖只需要
71
+ URL,天生适合脚本。这样就可以和`package.json`,`package-lock.json`,`node_modules`地狱说拜拜了,而且我的工作流也不用再依赖
72
+ Docker 和[act](https://github.com/nektos/act)了,于是过了 10 多天之后,我开始着手做了一个史上最快兑现的
73
+ 「如果我有时间,我一定要做 XXX
74
+ 」的项目:[Denoflow](https://twitter.com/OwenYoungZh/status/1485381401327267840),用来做我的低代码
75
+ IFTTT 或者说 Zapier,用流行一点的话说,这是配置即代码服务(Configure as Code)。
76
+
77
+ 这一次,产生了一些变化,在泡了一年自己做的 HN 中文版之后:
78
+
79
+ 1. 首先我没有像 Actionsflow 一样设计一个浮夸的[Landing 页](https://actionsflow.github.io/).
80
+ 2. 其次我没有用自己蹩脚的英语凑成一个看起来功能很丰富,维护人员很多,很正规的[一个文档网站](https://actionsflow.github.io/docs/reference/)。我只在项目的[Readme 文件](https://github.com/denoflow/denoflow)里写了一个我自认为很诚恳的说明,在一页的文本里说清楚这个项目的用途,和看一个示例就能明白的使用方法,没有多余的营销话语,把时间真正花在工具本身,利用省下的时间做了一个[在线 PlayGround](https://playground.owenyoung.com/),运行在我的廉价
81
+ VPS 上。
82
+ 3. 没考虑买一个域名,尽管`denoflow.com`域名还在。直接放在我的个人子域`playground.owenyoung.com`.
83
+ 4. 版本号的克制,对于刚推出的项目,我使用`0.0.x`,而不是像 Actionsflow
84
+ 一样,一上来就是`1.0.0`。而后来的证明,`0.0.x`是最适合这个项目的,因为在后来的使用中,发现了不少的致命 bug
85
+ 5. 在文档的显眼处声明:`项目仍处于非常早期的阶段,谨慎使用!`
86
+ 6. 仔细思考了项目的 License,选择了 Apache2.0
87
+
88
+ 做完之后,又花了 2 个小时,用 Denoflow
89
+ 做了[Show HN](https://showhn.buzzing.cc/),[Ask HN](https://askhn.buzzing.cc/),[HN 首页](https://hackernews.buzzing.cc/),[Best HN](https://besthn.buzzing.cc),这里可以和之前的[HN 热门](https://hn.buzzing.cc)对比下,没有追踪,没有
90
+ JS
91
+ 代码,只是使用一个舒服的背景色,使用[class less 原则](https://github.com/dbohdan/classless-css),同时生成一个
92
+ RSS。没有使用任何框架,就几句简单的 deno
93
+ 代码即可生成。和之前的[HN 热门](https://hn.buzzing.cc)相比,[HN 热门](https://hn.buzzing.cc)在一个屏幕里只能显示出
94
+ 2 篇文章,而现在[HN 首页](https://hackernews.buzzing.cc/)可以放 20 多篇,效率直接提升 10 倍啊有木有。
95
+
96
+ 很快,我发现按照这样布局的话,阅读效率高了太多了,已有的这些时间流,已经不够我看了,所以索性单独为我自己生成一个[HN 时间流](https://myfeed.owenyoung.com/),里面包含了
97
+ hn 上前 100 位用户提交的任何帖子。
98
+
99
+ 几天之后我就发现,之前只看 HN 的热门帖子损失有多大,HN
100
+ 上有太多优秀的内容没有被顶上去了。热门真的很随机,所以我们也要万分警惕大公司的算法机制。基于此,我又做了一个变化,直接抓取 HN 上最新的提交(我去掉了 Ask
101
+ HN 的内容,因为 Ask HN
102
+ 的质量相对较差,并且我还有专门的[Ask HN](https://ask.buzzing.cc)可以回头再看)。那么现在专属于我的[HN 时间流](https://myfeed.owenyoung.com/)就包括了
103
+ HN 上所有最新的文章了。即使是全部文章,由于是母语阅读,我还是能快速读完这些标题,找到感兴趣的内容,还可能留下我的 2 cents.
104
+ 把之前关注了很久的不少优质英文 RSS
105
+ 源,也利于[Denoflow](https://github.com/denoflow/denoflow)生成了一个专属于我的[RSS 翻译流](https://myrss.owenyoung.com/).
106
+ 这样下来我的空余时间就可以住要花在阅读长文章,而不是刷推特这样更短的文章。我们都知道推特其实无法代替长文章,就像长文章也无法代替读书一样。
107
+
108
+ 如果有人看到这里的话,那你一定会觉得 HN 有毒,而我已经上瘾了。其实没错,目前阶段是这样。
109
+
110
+ 好在 HN 上的人也经常推荐图书,比如以下两个链接就是 HN 的用户制作的 HN 评论大数据最推荐的书(HN 的评论比图书网站,或者第三方网站更可信):
111
+
112
+ - [汇总了 hacker news 上评论里提到的书籍的排名](https://hacker-recommended-books.vercel.app/)
113
+ - [又一个 hacker news 提到的图书周报](https://hackernewsbooks.com/top-books-on-hacker-news)
114
+
115
+ 比如我读了上面推荐的[《深度工作》](https://d.buzzing.cc/post/1),作者提到:
116
+
117
+ > "如果在你全部的清醒时间,都能给自己的大脑找到有意义的事情去做,而不是放任自己在迷糊的状态下漫无目的地浏览几个小时网页,那么在一天结束时,你会觉得更加充实,第二天开始时更加轻松。
118
+ > – 卡尔·纽波特《深度工作》
119
+
120
+ 从我沉迷 HN
121
+ 的例子里就能看出,网络已经剥夺了我保持专注和沉思的能力,但是真正值得探索和学习的知识都需要我拥有这种能力。如果我没有在某一个特定时段给自己安排任务,那么这些网站总是会更有诱惑力。所以如果我想抵御社交网络对我时间和精力的诱惑,那么必须要给大脑找一些高质量的替代活动,所以我基于开源
122
+ [Lemmy](https://lemmy.ml/) 搭建了一个类似 Reddit
123
+ 的社区,叫[**如何度过每一天**](https://d.buzzing.cc/),希望能在社区里分享有意义的事,不虚度光阴的事,可以是阅读某本书,学习某项技能,看某部纪录片等等等等。社区有投票的功能,某项活动被投票的次数越多,说明认为这项活动有意义的更多。
124
+
125
+ 目前其实就只有少数几个用户在用,网站依然是部署在我的一个 廉价 VPS 上,没有盈利的打算,目前加载也有点慢,但是够用。
126
+
127
+ 以上是我在 Hacker News 上学到的一些东西。
128
+ ','<p>昨天看到 Hacker News 的创始人 Paul Graham 在 2009<br>年写的一篇文章<a href="http://www.paulgraham.com/hackernews.html">《我从 Hacker News 中学到了什么》</a>,<br>作者提到:</p>
128
129
  <blockquote>
129
- <p><strong>Jant</strong> = Jantelagen (Law of Jante)<br>Low-key, de-socialized personal expression.</p>
130
+ <p>随着 Hacker News 变得越来越大,占用的时间比我预期的要多,但我并不后悔,因为我从工作中学到了很多东西。</p>
130
131
  </blockquote>
131
- <h2>What is Jant?</h2>
132
- <p>Jant is a single-author microblog for people who want to share thoughts without the noise of social media. No followers, no likes, no retweets—just your words.</p>
133
- <p><strong>Features</strong>:</p>
132
+ <p>就挺感慨的,一方面是我亲自做了一遍 HN 的中文翻译,在整个过程中学到了很多,另一方面也让我回想起大学期间做的一个社区,对我的之后的种种影响。</p>
133
+ <p>如果有机会的话,我觉得每个人都应该尽可能的去亲自参与创造某个能有其他人一起创造信息的社区,比如成立一个小组,一个论坛,一个社群等等,线下的可能更好。和人交流,能碰撞出我更多的想法,以及让我知道更大的世界,更多元的价值观。</p>
134
+ <p><a href="https://hn.buzzing.cc">hacker news 的中文翻译</a>是一个非常普通的想法,每年可能都有人声称自己做了一个 HN 的中文版。因为<br>HN 上不仅文章质量普遍优质,而且最重要的是: HN 的 API 是业界最友好的,所有人都可以用来练手,做一个 HN 的 XX<br>版,比如各种平台的客户端,各种语言的实现,rss 服务,email 订阅,评论通知,大数据分析,newsletter<br>等等,不胜枚举。由于我的英文水平并不能像母语一样让我能快速找到感兴趣的东西(后来发现其实不少看起来英文很好的人,也有类似的体验),所以如果我想阅读 HN<br>的话,我可能有 2 个选择:</p>
135
+ <ol>
136
+ <li>抱着锻炼自己英语的想法逼迫自己不太流利的浏览 HN</li>
137
+ <li>使用浏览器自带的翻译功能,浏览 HN</li>
138
+ </ol>
139
+ <p>这两个阶段我都经历过。我希望你不要经历,因为每个阶段都可能使你错过 HN,错过一个很优质的信息源。</p>
140
+ <p>第一个阶段,我只能坚持几天,然后就会本能的优先去看其他中文信息,慢慢的其实就没在看 HN 了。这在我的推特账号上尤其明显,我注册了 2<br>个推特号,一个是英文号<a href="https://twitter.com/TheOwenYoung">@TheOwenYoung</a>,一个是中文号<a href="https://twitter.com/OwenYoungZh">@OwenYoungZh</a>,本来是先开英文号的,专门关注的都是些英文博主,但是后面实在受不了了,说那就再开个中文号,尽量少关注人,只发一些东西。后来的故事就是,英文号上的很少,主力变成了中文号。唉,人就是离不开舒适圈,所以我们最好不要给自己创造舒适圈,比如在浏览器上设置很多社交网络的快捷书签;新<br>tab 页放很多快捷入口;手机上下载很多好用的应用;如果想克制自己,就最好只下必要应用,优先用网页版,或者像我现在做的,把 iPhone13<br>当成测试机,用一台好几年前的 Pixel 3a,又卡,掉电还快,但是用起来其实完全没问题,然后我又重新开始用<br>Kindle,只能一个操作,就是下一页,这样就能节省很多时间来看书了。</p>
141
+ <p>第二个阶段,我发现浏览器自带的翻译功能并不是太好用(去年我开始做 hn<br>中文版的时候更不好用,现在其实好了很多),比如有一些网页它会失败,要么就是要重试,然后排版也不太好看,我更希望的是有些区域不要翻译,只翻译标题之类的,但是浏览器会全部翻译,就不太好浏览。而作为一个要长期阅读的地方,体验还是挺重要的。</p>
142
+ <p>正好去年那会儿,刚做完<a href="https://github.com/actionsflow/actionsflow">Actionsflow</a>, 想利用<br>Actionsflow 做点什么,所以说那就做一个 HN<br>的中文翻译吧,这很适合工作流去处理,处理完之后生成一个静态网页,就算没人用,我自己也可以天天用,做好之后也不用维护,就一直在那。于是就开干,没想到做到后面发现还可以顺便做点别的我感兴趣的英文内容翻译,比如<a href="https://reddit.buzzing.cc">Reddit 的美股讨论</a>,<a href="https://news.buzzing.cc">国外的权威媒体报道</a>,<a href="https://ph.buzzing.cc">ProductHunt</a>等等,最后就汇总成了一个<a href="https://www.buzzing.cc">Buzzing</a></p>
143
+ <p>Buzzing<br>运行了一年之后,发现之前的设计有不少欠考虑的地方,<a href="https://github.com/actionsflow/actionsflow">Actionsflow</a>也有点太重了,尤其是看了一年<br>HN 的帖子后,觉得 Actionsflow<br>的设计简直就是一坨屎,又臭又长。这个时候<a href="https://twitter.com/OwenYoungZh/status/1478928692781137925">才发现 Deno 是最适合做工作流的运行时</a>,Deno<br>的依赖只需要<br>URL,天生适合脚本。这样就可以和<code>package.json</code>,<code>package-lock.json</code>,<code>node_modules</code>地狱说拜拜了,而且我的工作流也不用再依赖<br>Docker 和<a href="https://github.com/nektos/act">act</a>了,于是过了 10 多天之后,我开始着手做了一个史上最快兑现的<br>「如果我有时间,我一定要做 XXX<br>」的项目:<a href="https://twitter.com/OwenYoungZh/status/1485381401327267840">Denoflow</a>,用来做我的低代码<br>IFTTT 或者说 Zapier,用流行一点的话说,这是配置即代码服务(Configure as Code)。</p>
144
+ <p>这一次,产生了一些变化,在泡了一年自己做的 HN 中文版之后:</p>
145
+ <ol>
146
+ <li>首先我没有像 Actionsflow 一样设计一个浮夸的<a href="https://actionsflow.github.io/">Landing 页</a>.</li>
147
+ <li>其次我没有用自己蹩脚的英语凑成一个看起来功能很丰富,维护人员很多,很正规的<a href="https://actionsflow.github.io/docs/reference/">一个文档网站</a>。我只在项目的<a href="https://github.com/denoflow/denoflow">Readme 文件</a>里写了一个我自认为很诚恳的说明,在一页的文本里说清楚这个项目的用途,和看一个示例就能明白的使用方法,没有多余的营销话语,把时间真正花在工具本身,利用省下的时间做了一个<a href="https://playground.owenyoung.com/">在线 PlayGround</a>,运行在我的廉价<br>VPS 上。</li>
148
+ <li>没考虑买一个域名,尽管<code>denoflow.com</code>域名还在。直接放在我的个人子域<code>playground.owenyoung.com</code>.</li>
149
+ <li>版本号的克制,对于刚推出的项目,我使用<code>0.0.x</code>,而不是像 Actionsflow<br>一样,一上来就是<code>1.0.0</code>。而后来的证明,<code>0.0.x</code>是最适合这个项目的,因为在后来的使用中,发现了不少的致命 bug</li>
150
+ <li>在文档的显眼处声明:<code>项目仍处于非常早期的阶段,谨慎使用!</code></li>
151
+ <li>仔细思考了项目的 License,选择了 Apache2.0</li>
152
+ </ol>
153
+ <p>做完之后,又花了 2 个小时,用 Denoflow<br>做了<a href="https://showhn.buzzing.cc/">Show HN</a>,<a href="https://askhn.buzzing.cc/">Ask HN</a>,<a href="https://hackernews.buzzing.cc/">HN 首页</a>,<a href="https://besthn.buzzing.cc">Best HN</a>,这里可以和之前的<a href="https://hn.buzzing.cc">HN 热门</a>对比下,没有追踪,没有<br>JS<br>代码,只是使用一个舒服的背景色,使用<a href="https://github.com/dbohdan/classless-css">class less 原则</a>,同时生成一个<br>RSS。没有使用任何框架,就几句简单的 deno<br>代码即可生成。和之前的<a href="https://hn.buzzing.cc">HN 热门</a>相比,<a href="https://hn.buzzing.cc">HN 热门</a>在一个屏幕里只能显示出<br>2 篇文章,而现在<a href="https://hackernews.buzzing.cc/">HN 首页</a>可以放 20 多篇,效率直接提升 10 倍啊有木有。</p>
154
+ <p>很快,我发现按照这样布局的话,阅读效率高了太多了,已有的这些时间流,已经不够我看了,所以索性单独为我自己生成一个<a href="https://myfeed.owenyoung.com/">HN 时间流</a>,里面包含了<br>hn 上前 100 位用户提交的任何帖子。</p>
155
+ <p>几天之后我就发现,之前只看 HN 的热门帖子损失有多大,HN<br>上有太多优秀的内容没有被顶上去了。热门真的很随机,所以我们也要万分警惕大公司的算法机制。基于此,我又做了一个变化,直接抓取 HN 上最新的提交(我去掉了 Ask<br>HN 的内容,因为 Ask HN<br>的质量相对较差,并且我还有专门的<a href="https://ask.buzzing.cc">Ask HN</a>可以回头再看)。那么现在专属于我的<a href="https://myfeed.owenyoung.com/">HN 时间流</a>就包括了<br>HN 上所有最新的文章了。即使是全部文章,由于是母语阅读,我还是能快速读完这些标题,找到感兴趣的内容,还可能留下我的 2 cents.<br>把之前关注了很久的不少优质英文 RSS<br>源,也利于<a href="https://github.com/denoflow/denoflow">Denoflow</a>生成了一个专属于我的<a href="https://myrss.owenyoung.com/">RSS 翻译流</a>.<br>这样下来我的空余时间就可以住要花在阅读长文章,而不是刷推特这样更短的文章。我们都知道推特其实无法代替长文章,就像长文章也无法代替读书一样。</p>
156
+ <p>如果有人看到这里的话,那你一定会觉得 HN 有毒,而我已经上瘾了。其实没错,目前阶段是这样。</p>
157
+ <p>好在 HN 上的人也经常推荐图书,比如以下两个链接就是 HN 的用户制作的 HN 评论大数据最推荐的书(HN 的评论比图书网站,或者第三方网站更可信):</p>
134
158
  <ul>
135
- <li>Multiple content types: notes, articles, links, quotes, images</li>
136
- <li>Thread support for longer thoughts</li>
137
- <li>Collections for curated topics</li>
138
- <li>Beautiful, themeable design</li>
139
- <li>Deploys to Cloudflare Workers in minutes</li>
159
+ <li><a href="https://hacker-recommended-books.vercel.app/">汇总了 hacker news 上评论里提到的书籍的排名</a></li>
160
+ <li><a href="https://hackernewsbooks.com/top-books-on-hacker-news">又一个 hacker news 提到的图书周报</a></li>
140
161
  </ul>
141
- <h2>Quick Start</h2>
142
- <pre><code class="language-bash"># Create a new Jant site
143
- pnpm create jant my-blog
144
-
145
- # Start development
146
- cd my-blog
147
- pnpm dev
148
-
149
- # Deploy to Cloudflare
150
- pnpm run deploy
151
- </code></pre>
152
- <h2>Documentation</h2>
153
- <ul>
154
- <li><a href="docs/getting-started.md">Getting Started</a></li>
155
- <li><a href="docs/deployment.md">Deployment</a></li>
156
- <li><a href="docs/configuration.md">Configuration</a></li>
157
- <li><a href="docs/theming.md">Theming</a></li>
158
- <li><a href="docs/API.md">API Reference</a></li>
159
- </ul>
160
- <h2>Development</h2>
161
- <p>Requires <a href="https://mise.jdx.dev/">mise</a> — it manages Node.js and pnpm automatically.</p>
162
- <pre><code class="language-bash"># Install mise (macOS/Linux)
163
- curl https://mise.run | sh
164
-
165
- # Clone and setup
166
- git clone https://github.com/jant-me/jant.git
167
- cd jant
168
- mise install # installs Node.js and pnpm
169
- pnpm install # installs dependencies
170
-
171
- # Start development server (http://localhost:9019)
172
- mise run dev
173
- </code></pre>
174
- <p>See <a href="CONTRIBUTING.md">CONTRIBUTING.md</a> for code style, PR process, and release workflow.</p>
175
- <h2>Philosophy</h2>
176
- <p>Jant is built on the idea that not everything needs to be optimized for engagement. Write for yourself. Share if you want. No metrics, no pressure.</p>
177
- <h2>License</h2>
178
- <p>AGPL-3.0</p>
179
- ',NULL,NULL,NULL,NULL,NULL,NULL,1770759271,1770759271,1770759271);
180
-
181
- -- collections
182
- INSERT INTO collections VALUES(1,'getting-started','Getting Started','Resources for getting started with Jant',1770689095,1770689095);
183
- INSERT INTO collections VALUES(2,'inspires','Inspires',NULL,1770758555,1770758555);
184
-
185
- -- post_collections
186
- INSERT INTO post_collections VALUES(1,1,1770689095);
187
- INSERT INTO post_collections VALUES(6,2,1770758568);
162
+ <p>比如我读了上面推荐的<a href="https://d.buzzing.cc/post/1">《深度工作》</a>,作者提到:</p>
163
+ <blockquote>
164
+ <p>"如果在你全部的清醒时间,都能给自己的大脑找到有意义的事情去做,而不是放任自己在迷糊的状态下漫无目的地浏览几个小时网页,那么在一天结束时,你会觉得更加充实,第二天开始时更加轻松。<br>– 卡尔·纽波特《深度工作》</p>
165
+ </blockquote>
166
+ <p>从我沉迷 HN<br>的例子里就能看出,网络已经剥夺了我保持专注和沉思的能力,但是真正值得探索和学习的知识都需要我拥有这种能力。如果我没有在某一个特定时段给自己安排任务,那么这些网站总是会更有诱惑力。所以如果我想抵御社交网络对我时间和精力的诱惑,那么必须要给大脑找一些高质量的替代活动,所以我基于开源<br><a href="https://lemmy.ml/">Lemmy</a> 搭建了一个类似 Reddit<br>的社区,叫<a href="https://d.buzzing.cc/"><strong>如何度过每一天</strong></a>,希望能在社区里分享有意义的事,不虚度光阴的事,可以是阅读某本书,学习某项技能,看某部纪录片等等等等。社区有投票的功能,某项活动被投票的次数越多,说明认为这项活动有意义的更多。</p>
167
+ <p>目前其实就只有少数几个用户在用,网站依然是部署在我的一个 廉价 VPS 上,没有盈利的打算,目前加载也有点慢,但是够用。</p>
168
+ <p>以上是我在 Hacker News 上学到的一些东西。</p>
169
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771115051,1771115051,1771115051);
170
+ INSERT INTO posts VALUES(8,'quote','published',0,0,NULL,NULL,'https://www.joanwestenberg.com/why-my-newsletter-costs-2-50/',NULL,NULL,'拥有五万个从不打开邮件的订阅者名单,其价值远不及五百个逐字阅读并偶尔给予深度回复的读者。后者会购买你的著作,向朋友推荐你,并长久陪伴你。前者不过是你在那些无聊的数字炫耀派对上能拿出来吹嘘的数字而已。',NULL,NULL,NULL,NULL,NULL,1771115103,1771115103,1771115103);
171
+ INSERT INTO posts VALUES(9,'quote','published',1,0,NULL,'JA Westenberg','https://www.joanwestenberg.com/the-case-for-blogging-in-the-ruins/',NULL,NULL,'弗吉尼亚·伍尔夫曾论述拥有独立空间的重要性:那是供人进行创作的物理场所,不受干扰与控制。博客便是网络世界中的独立空间。在这里,你自主决定书写内容与表达方式,无需屈从于平台算法的任性摆布——那些平台只顾榨取你的参与度,却不关心这种参与究竟是否让你或他人变得更聪明。
172
+
173
+ 我们不会通过等待平台减少剥削来获得更好的互联网。我们需要亲手构建它——维护自己的空间,相互链接,打造独立网站的互联网络,让博客圈重现昔日的辉煌。',NULL,NULL,NULL,NULL,NULL,1771115159,1771115159,1771115177);
174
+ INSERT INTO posts VALUES(10,'link','published',0,0,NULL,'Some pics','https://some.pics/','一个干净的图片流网站
175
+ ','<p>一个干净的图片流网站</p>
176
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771115271,1771115271,1771115271);
177
+ INSERT INTO posts VALUES(11,'note','published',0,0,NULL,NULL,NULL,'From splash','<p>From splash</p>
178
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771268320,1771268320,1771268958);
188
179
 
189
180
  -- media
190
- INSERT INTO media VALUES('019c496c-46bd-7954-bd6a-77b1b8f1d451',NULL,'019c496c-46bd-7954-bd6a-77b1b8f1d451.webp','tegan-conway-KaFfNTw8OYQ-unsplash.webp','image/webp',715364,'media/2026/02/019c496c-46bd-7954-bd6a-77b1b8f1d451.webp',NULL,NULL,NULL,1770758358,0,NULL,'r2');
191
- INSERT INTO media VALUES('019c496c-5e44-70d2-ac8a-c0c0bdaab65c',6,'019c496c-5e44-70d2-ac8a-c0c0bdaab65c.webp','land-o-lakes-inc-9w6Qb-dqBwE-unsplash.webp','image/webp',306042,'media/2026/02/019c496c-5e44-70d2-ac8a-c0c0bdaab65c.webp',NULL,NULL,NULL,1770758364,3,NULL,'r2');
192
- INSERT INTO media VALUES('019c496d-5011-7981-89a0-b4373a695d78',6,'019c496d-5011-7981-89a0-b4373a695d78.webp','land-o-lakes-inc-k71TQkbVIgI-unsplash.webp','image/webp',597680,'media/2026/02/019c496d-5011-7981-89a0-b4373a695d78.webp',NULL,NULL,NULL,1770758426,2,NULL,'r2');
193
- INSERT INTO media VALUES('019c496d-630c-70b4-9004-51a194746566',6,'019c496d-630c-70b4-9004-51a194746566.webp','thingsneverchange-CgHNmQ0c2w4-unsplash.webp','image/webp',358320,'media/2026/02/019c496d-630c-70b4-9004-51a194746566.webp',NULL,NULL,NULL,1770758431,1,NULL,'r2');
194
- INSERT INTO media VALUES('019c496d-720f-70d2-98b8-3779457de73c',6,'019c496d-720f-70d2-98b8-3779457de73c.webp','willian-justen-de-vasconcellos-7jg7Y_Mlf2Q-unsplash.webp','image/webp',478956,'media/2026/02/019c496d-720f-70d2-98b8-3779457de73c.webp',NULL,NULL,NULL,1770758435,0,NULL,'r2');
195
- INSERT INTO media VALUES('019c496e-6904-7f61-b14f-080035ffe23f',5,'019c496e-6904-7f61-b14f-080035ffe23f.webp','richard-stachmann-Es--yoQocSM-unsplash.webp','image/webp',381534,'media/2026/02/019c496e-6904-7f61-b14f-080035ffe23f.webp',NULL,NULL,NULL,1770758498,0,NULL,'r2');
181
+ INSERT INTO media VALUES('019c676d-1fcd-7c11-9737-5aebd10c1087',3,'019c676d-1fcd-7c11-9737-5aebd10c1087.webp','ansis-kancs-XGxp1rvTVag-unsplash.webp','image/webp',202748,'media/2026/02/019c676d-1fcd-7c11-9737-5aebd10c1087.webp',NULL,NULL,NULL,1771261731,0,NULL,'r2');
182
+ INSERT INTO media VALUES('019c679e-98f9-73ba-bd57-ab6ebf94fe05',3,'019c679e-98f9-73ba-bd57-ab6ebf94fe05.webp','dan-begel-pbfkmEL3QaU-unsplash.webp','image/webp',229392,'media/2026/02/019c679e-98f9-73ba-bd57-ab6ebf94fe05.webp',NULL,NULL,NULL,1771264973,1,NULL,'r2');
183
+ INSERT INTO media VALUES('019c679e-a8ce-7d49-9c21-8d75a1551198',3,'019c679e-a8ce-7d49-9c21-8d75a1551198.webp','willian-justen-de-vasconcellos--pKhrem8YpE-unsplash.webp','image/webp',324826,'media/2026/02/019c679e-a8ce-7d49-9c21-8d75a1551198.webp',NULL,NULL,NULL,1771264977,2,NULL,'r2');
184
+ INSERT INTO media VALUES('019c679e-c351-76dc-9569-2a716b4384b5',NULL,'019c679e-c351-76dc-9569-2a716b4384b5.webp','brianna-parks-frXRtKYPqXk-unsplash.webp','image/webp',264554,'media/2026/02/019c679e-c351-76dc-9569-2a716b4384b5.webp',NULL,NULL,NULL,1771264984,0,NULL,'r2');
185
+ INSERT INTO media VALUES('019c67d5-1022-7efd-9ae8-0454325cba78',NULL,'019c67d5-1022-7efd-9ae8-0454325cba78.webp','takashi-sakamoto-J9nW9Tc1HVc-unsplash.webp','image/webp',505172,'media/2026/02/019c67d5-1022-7efd-9ae8-0454325cba78.webp',NULL,NULL,NULL,1771268543,0,NULL,'r2');
186
+ INSERT INTO media VALUES('019c67d5-28b8-7ec3-ae2e-83d9eb1b096b',NULL,'019c67d5-28b8-7ec3-ae2e-83d9eb1b096b.webp','museum-of-new-zealand-te-papa-tongarewa-1C7oRRwUkgU-unsplash.webp','image/webp',322656,'media/2026/02/019c67d5-28b8-7ec3-ae2e-83d9eb1b096b.webp',NULL,NULL,NULL,1771268549,0,NULL,'r2');
187
+ INSERT INTO media VALUES('019c67d5-39c9-7723-8b0c-4c8a50076dc3',11,'019c67d5-39c9-7723-8b0c-4c8a50076dc3.webp','land-o-lakes-inc-peEOQ4_dqOo-unsplash.webp','image/webp',470774,'media/2026/02/019c67d5-39c9-7723-8b0c-4c8a50076dc3.webp',NULL,NULL,NULL,1771268554,0,NULL,'r2');
@@ -1,195 +1,197 @@
1
1
  -- =============================================================================
2
- -- Demo seed data for Jant (demo.jant.me)
3
- -- Exported from remote demo D1 database via: mise run demo-backup
4
- -- Usage: mise run demo-reset
2
+ -- Local development seed data for Jant
3
+ -- Exported from local D1 database
4
+ -- Usage: mise run db-seed
5
5
  -- =============================================================================
6
6
 
7
- -- posts
8
- INSERT INTO posts VALUES(1,'article','featured','Welcome to Jant',NULL,'# Welcome to Jant Demo
9
-
10
- Jant is a modern microblog platform built for Cloudflare Workers. This demo site resets daily at 00:00 UTC.
11
-
12
- ## Features
13
-
14
- - **Multiple post types**: Notes, articles, links, quotes, images, and pages
15
- - **Collections**: Organize posts into collections
16
- - **Full-text search**: Search across all your content
17
- - **Internationalization**: Built-in i18n support
18
- - **Fast**: Edge-deployed on Cloudflare Workers
19
-
20
- ## Getting Started
21
-
22
- ```bash
23
- pnpm create jant my-blog
24
- cd my-blog
25
- pnpm install
26
- pnpm dev
27
- ```
28
-
29
- Visit the [dashboard](/dash) to create your own posts!','<h1>Welcome to Jant Demo</h1>
30
- <p>Jant is a modern microblog platform built for Cloudflare Workers. This demo site resets daily at 00:00 UTC.</p>
31
- <h2>Features</h2>
32
- <ul>
33
- <li><strong>Multiple post types</strong>: Notes, articles, links, quotes, images, and pages</li>
34
- <li><strong>Collections</strong>: Organize posts into collections</li>
35
- <li><strong>Full-text search</strong>: Search across all your content</li>
36
- <li><strong>Internationalization</strong>: Built-in i18n support</li>
37
- <li><strong>Fast</strong>: Edge-deployed on Cloudflare Workers</li>
38
- </ul>
39
- <h2>Getting Started</h2>
40
- <pre><code class="language-bash">pnpm create jant my-blog
41
- cd my-blog
42
- pnpm install
43
- pnpm dev
44
- </code></pre>
45
- <p>Visit the <a href="/dash">dashboard</a> to create your own posts!</p>',NULL,NULL,NULL,NULL,NULL,NULL,1770689095,1770689095,1770689095);
46
- INSERT INTO posts VALUES(2,'note','quiet',NULL,NULL,'This is a demo note. Notes are short posts without titles, perfect for quick thoughts and updates.','<p>This is a demo note. Notes are short posts without titles, perfect for quick thoughts and updates.</p>',NULL,NULL,NULL,NULL,NULL,NULL,1770685495,1770685495,1770685495);
47
- INSERT INTO posts VALUES(3,'link','quiet','Jant on GitHub',NULL,'Check out the source code and documentation for Jant.','<p>Check out the source code and documentation for Jant.</p>','https://github.com/jant-me/jant','GitHub','github.com',NULL,NULL,NULL,1770681895,1770681895,1770681895);
48
- INSERT INTO posts VALUES(4,'quote','quiet',NULL,NULL,'The best way to predict the future is to invent it.','<p>The best way to predict the future is to invent it.</p>',NULL,'Alan Kay',NULL,NULL,NULL,NULL,1770678295,1770678295,1770678295);
49
- INSERT INTO posts VALUES(5,'image','quiet',NULL,NULL,'Image 1','<p>Image 1</p>
50
- ',NULL,NULL,NULL,NULL,NULL,NULL,1770758516,1770758516,1770758516);
51
- INSERT INTO posts VALUES(6,'image','quiet',NULL,NULL,'Image 2','<p>Image 2</p>
52
- ',NULL,NULL,NULL,NULL,NULL,NULL,1770758537,1770758537,1770759299);
53
- INSERT INTO posts VALUES(7,'page','unlisted','About','about','> **Work in Progress**: This project is still under active development and not yet ready for use. See the latest build at [demo.jant.me](https://demo.jant.me).
54
- >
55
- > Demo login: `demo@jant.me` / `demodemo` — Dashboard: [demo.jant.me/dash](https://demo.jant.me/dash)
56
-
57
- A personal microblogging system as smooth as <https://threads.com>.
58
-
59
- > **Jant** = Jantelagen (Law of Jante)
60
- > Low-key, de-socialized personal expression.
61
-
62
- ## What is Jant?
63
-
64
- Jant is a single-author microblog for people who want to share thoughts without the noise of social media. No followers, no likes, no retweets—just your words.
65
-
66
- **Features**:
67
-
68
- - Multiple content types: notes, articles, links, quotes, images
69
- - Thread support for longer thoughts
70
- - Collections for curated topics
71
- - Beautiful, themeable design
72
- - Deploys to Cloudflare Workers in minutes
73
-
74
- ## Quick Start
75
-
76
- ```bash
77
- # Create a new Jant site
78
- pnpm create jant my-blog
79
-
80
- # Start development
81
- cd my-blog
82
- pnpm dev
7
+ -- settings
8
+ INSERT INTO settings VALUES('ONBOARDING_STATUS','completed',1770761877);
83
9
 
84
- # Deploy to Cloudflare
85
- pnpm run deploy
86
- ```
10
+ -- user
11
+ INSERT INTO user VALUES('xCkM3BgGOkgQnec6l8EF3f4q2VnG6AoU','test','theowenyoung@gmail.com',0,NULL,'admin',1770761877,1770761877);
87
12
 
88
- ## Documentation
13
+ -- account
14
+ INSERT INTO account VALUES('jC56rT53Pe9aXkrv4YtlCBuPQACWoOuY','xCkM3BgGOkgQnec6l8EF3f4q2VnG6AoU','credential','xCkM3BgGOkgQnec6l8EF3f4q2VnG6AoU',NULL,NULL,NULL,NULL,NULL,NULL,'3d915508e712a5b481109e907ae99cc2:5303d92b81403b2a77a1a65d58586c781d896e8ba71c02ea19207f94e2e9d9ebf82a2e9fd1f1a61556aee6cd7dcdf07c70cb64f6fe42d30e05870b2f7b90c27c',1770761877,1770761877);
89
15
 
90
- - [Getting Started](docs/getting-started.md)
91
- - [Deployment](docs/deployment.md)
92
- - [Configuration](docs/configuration.md)
93
- - [Theming](docs/theming.md)
94
- - [API Reference](docs/API.md)
16
+ -- pages
17
+ INSERT INTO pages VALUES(1,'about','About','
18
+ Yes about','<p>Yes about</p>
19
+ ','published',1771272645,1771273719);
95
20
 
96
- ## Development
97
-
98
- Requires [mise](https://mise.jdx.dev/) — it manages Node.js and pnpm automatically.
99
-
100
- ```bash
101
- # Install mise (macOS/Linux)
102
- curl https://mise.run | sh
103
-
104
- # Clone and setup
105
- git clone https://github.com/jant-me/jant.git
106
- cd jant
107
- mise install # installs Node.js and pnpm
108
- pnpm install # installs dependencies
109
-
110
- # Start development server (http://localhost:9019)
111
- mise run dev
112
- ```
113
-
114
- See [CONTRIBUTING.md](CONTRIBUTING.md) for code style, PR process, and release workflow.
115
-
116
- ## Philosophy
117
-
118
- Jant is built on the idea that not everything needs to be optimized for engagement. Write for yourself. Share if you want. No metrics, no pressure.
119
-
120
- ## License
21
+ -- collections
22
+ INSERT INTO collections VALUES(1,'best-quotes','发人深省',NULL,NULL,'newest',0,0,1771273707,1771273707);
121
23
 
122
- AGPL-3.0
123
- ','<blockquote>
124
- <p><strong>Work in Progress</strong>: This project is still under active development and not yet ready for use. See the latest build at <a href="https://demo.jant.me">demo.jant.me</a>.</p>
125
- <p>Demo login: <code>demo@jant.me</code> / <code>demodemo</code> — Dashboard: <a href="https://demo.jant.me/dash">demo.jant.me/dash</a></p>
126
- </blockquote>
127
- <p>A personal microblogging system as smooth as <a href="https://threads.com">https://threads.com</a>.</p>
24
+ -- posts
25
+ INSERT INTO posts VALUES(1,'note','published',0,0,NULL,'really?',NULL,'is this works?','<p>is this works?</p>
26
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771089786,1771089786,1771089786);
27
+ INSERT INTO posts VALUES(2,'quote','published',0,0,NULL,NULL,NULL,NULL,NULL,'worked?',NULL,NULL,NULL,NULL,NULL,1771089808,1771089808,1771089808);
28
+ INSERT INTO posts VALUES(3,'note','published',0,0,NULL,'greate image',NULL,'great','<p>great</p>
29
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771092931,1771092931,1771268464);
30
+ INSERT INTO posts VALUES(4,'note','published',0,0,NULL,NULL,NULL,'没有人真的想交付自己都看不上的东西。我们天然就想把事情做得更对一点、更好一点。','<p>没有人真的想交付自己都看不上的东西。我们天然就想把事情做得更对一点、更好一点。</p>
31
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771114861,1771114861,1771114861);
32
+ INSERT INTO posts VALUES(5,'note','published',0,0,NULL,NULL,NULL,'新荣记的老板会给出预算,让店长和主厨每两周可以自己订一家餐厅去体验,而且预算没有上限。从到东京筹备开店至今,他们已经吃了日本十几家不同的餐厅。很多餐厅的服务员从没吃过自家餐厅的招牌菜,而新荣记不但安排自己的厨师和服务人员吃自己家的菜,还会提供费用,让这些年轻人去他们向往的餐厅,体验别人家的优点。"。我想,对我们网上冲浪的启发就是我们也要多体验别人做的好东西,从中提高审美和获得灵感。','<p>新荣记的老板会给出预算,让店长和主厨每两周可以自己订一家餐厅去体验,而且预算没有上限。从到东京筹备开店至今,他们已经吃了日本十几家不同的餐厅。很多餐厅的服务员从没吃过自家餐厅的招牌菜,而新荣记不但安排自己的厨师和服务人员吃自己家的菜,还会提供费用,让这些年轻人去他们向往的餐厅,体验别人家的优点。&quot;。我想,对我们网上冲浪的启发就是我们也要多体验别人做的好东西,从中提高审美和获得灵感。</p>
33
+ ',NULL,NULL,1,NULL,NULL,NULL,1771114906,1771114906,1771273733);
34
+ INSERT INTO posts VALUES(6,'link','published',0,0,NULL,'Forty News','https://forty.news/','40年前今日的新闻...','<p>40年前今日的新闻...</p>
35
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771114955,1771114955,1771114955);
36
+ INSERT INTO posts VALUES(7,'note','published',0,0,NULL,'我从 Hacker News 中学到了什么?',NULL,'昨天看到 Hacker News 的创始人 Paul Graham 在 2009
37
+ 年写的一篇文章[《我从 Hacker News 中学到了什么》](http://www.paulgraham.com/hackernews.html),
38
+ 作者提到:
39
+
40
+ > 随着 Hacker News 变得越来越大,占用的时间比我预期的要多,但我并不后悔,因为我从工作中学到了很多东西。
41
+
42
+ 就挺感慨的,一方面是我亲自做了一遍 HN 的中文翻译,在整个过程中学到了很多,另一方面也让我回想起大学期间做的一个社区,对我的之后的种种影响。
43
+
44
+ 如果有机会的话,我觉得每个人都应该尽可能的去亲自参与创造某个能有其他人一起创造信息的社区,比如成立一个小组,一个论坛,一个社群等等,线下的可能更好。和人交流,能碰撞出我更多的想法,以及让我知道更大的世界,更多元的价值观。
45
+
46
+ [hacker news 的中文翻译](https://hn.buzzing.cc)是一个非常普通的想法,每年可能都有人声称自己做了一个 HN 的中文版。因为
47
+ HN 上不仅文章质量普遍优质,而且最重要的是: HN 的 API 是业界最友好的,所有人都可以用来练手,做一个 HN 的 XX
48
+ 版,比如各种平台的客户端,各种语言的实现,rss 服务,email 订阅,评论通知,大数据分析,newsletter
49
+ 等等,不胜枚举。由于我的英文水平并不能像母语一样让我能快速找到感兴趣的东西(后来发现其实不少看起来英文很好的人,也有类似的体验),所以如果我想阅读 HN
50
+ 的话,我可能有 2 个选择:
51
+
52
+ 1. 抱着锻炼自己英语的想法逼迫自己不太流利的浏览 HN
53
+ 2. 使用浏览器自带的翻译功能,浏览 HN
54
+
55
+ 这两个阶段我都经历过。我希望你不要经历,因为每个阶段都可能使你错过 HN,错过一个很优质的信息源。
56
+
57
+ 第一个阶段,我只能坚持几天,然后就会本能的优先去看其他中文信息,慢慢的其实就没在看 HN 了。这在我的推特账号上尤其明显,我注册了 2
58
+ 个推特号,一个是英文号[@TheOwenYoung](https://twitter.com/TheOwenYoung),一个是中文号[@OwenYoungZh](https://twitter.com/OwenYoungZh),本来是先开英文号的,专门关注的都是些英文博主,但是后面实在受不了了,说那就再开个中文号,尽量少关注人,只发一些东西。后来的故事就是,英文号上的很少,主力变成了中文号。唉,人就是离不开舒适圈,所以我们最好不要给自己创造舒适圈,比如在浏览器上设置很多社交网络的快捷书签;新
59
+ tab 页放很多快捷入口;手机上下载很多好用的应用;如果想克制自己,就最好只下必要应用,优先用网页版,或者像我现在做的,把 iPhone13
60
+ 当成测试机,用一台好几年前的 Pixel 3a,又卡,掉电还快,但是用起来其实完全没问题,然后我又重新开始用
61
+ Kindle,只能一个操作,就是下一页,这样就能节省很多时间来看书了。
62
+
63
+ 第二个阶段,我发现浏览器自带的翻译功能并不是太好用(去年我开始做 hn
64
+ 中文版的时候更不好用,现在其实好了很多),比如有一些网页它会失败,要么就是要重试,然后排版也不太好看,我更希望的是有些区域不要翻译,只翻译标题之类的,但是浏览器会全部翻译,就不太好浏览。而作为一个要长期阅读的地方,体验还是挺重要的。
65
+
66
+ 正好去年那会儿,刚做完[Actionsflow](https://github.com/actionsflow/actionsflow), 想利用
67
+ Actionsflow 做点什么,所以说那就做一个 HN
68
+ 的中文翻译吧,这很适合工作流去处理,处理完之后生成一个静态网页,就算没人用,我自己也可以天天用,做好之后也不用维护,就一直在那。于是就开干,没想到做到后面发现还可以顺便做点别的我感兴趣的英文内容翻译,比如[Reddit 的美股讨论](https://reddit.buzzing.cc),[国外的权威媒体报道](https://news.buzzing.cc),[ProductHunt](https://ph.buzzing.cc)等等,最后就汇总成了一个[Buzzing](https://www.buzzing.cc)
69
+
70
+ Buzzing
71
+ 运行了一年之后,发现之前的设计有不少欠考虑的地方,[Actionsflow](https://github.com/actionsflow/actionsflow)也有点太重了,尤其是看了一年
72
+ HN 的帖子后,觉得 Actionsflow
73
+ 的设计简直就是一坨屎,又臭又长。这个时候[才发现 Deno 是最适合做工作流的运行时](https://twitter.com/OwenYoungZh/status/1478928692781137925),Deno
74
+ 的依赖只需要
75
+ URL,天生适合脚本。这样就可以和`package.json`,`package-lock.json`,`node_modules`地狱说拜拜了,而且我的工作流也不用再依赖
76
+ Docker 和[act](https://github.com/nektos/act)了,于是过了 10 多天之后,我开始着手做了一个史上最快兑现的
77
+ 「如果我有时间,我一定要做 XXX
78
+ 」的项目:[Denoflow](https://twitter.com/OwenYoungZh/status/1485381401327267840),用来做我的低代码
79
+ IFTTT 或者说 Zapier,用流行一点的话说,这是配置即代码服务(Configure as Code)。
80
+
81
+ 这一次,产生了一些变化,在泡了一年自己做的 HN 中文版之后:
82
+
83
+ 1. 首先我没有像 Actionsflow 一样设计一个浮夸的[Landing 页](https://actionsflow.github.io/).
84
+ 2. 其次我没有用自己蹩脚的英语凑成一个看起来功能很丰富,维护人员很多,很正规的[一个文档网站](https://actionsflow.github.io/docs/reference/)。我只在项目的[Readme 文件](https://github.com/denoflow/denoflow)里写了一个我自认为很诚恳的说明,在一页的文本里说清楚这个项目的用途,和看一个示例就能明白的使用方法,没有多余的营销话语,把时间真正花在工具本身,利用省下的时间做了一个[在线 PlayGround](https://playground.owenyoung.com/),运行在我的廉价
85
+ VPS 上。
86
+ 3. 没考虑买一个域名,尽管`denoflow.com`域名还在。直接放在我的个人子域`playground.owenyoung.com`.
87
+ 4. 版本号的克制,对于刚推出的项目,我使用`0.0.x`,而不是像 Actionsflow
88
+ 一样,一上来就是`1.0.0`。而后来的证明,`0.0.x`是最适合这个项目的,因为在后来的使用中,发现了不少的致命 bug
89
+ 5. 在文档的显眼处声明:`项目仍处于非常早期的阶段,谨慎使用!`
90
+ 6. 仔细思考了项目的 License,选择了 Apache2.0
91
+
92
+ 做完之后,又花了 2 个小时,用 Denoflow
93
+ 做了[Show HN](https://showhn.buzzing.cc/),[Ask HN](https://askhn.buzzing.cc/),[HN 首页](https://hackernews.buzzing.cc/),[Best HN](https://besthn.buzzing.cc),这里可以和之前的[HN 热门](https://hn.buzzing.cc)对比下,没有追踪,没有
94
+ JS
95
+ 代码,只是使用一个舒服的背景色,使用[class less 原则](https://github.com/dbohdan/classless-css),同时生成一个
96
+ RSS。没有使用任何框架,就几句简单的 deno
97
+ 代码即可生成。和之前的[HN 热门](https://hn.buzzing.cc)相比,[HN 热门](https://hn.buzzing.cc)在一个屏幕里只能显示出
98
+ 2 篇文章,而现在[HN 首页](https://hackernews.buzzing.cc/)可以放 20 多篇,效率直接提升 10 倍啊有木有。
99
+
100
+ 很快,我发现按照这样布局的话,阅读效率高了太多了,已有的这些时间流,已经不够我看了,所以索性单独为我自己生成一个[HN 时间流](https://myfeed.owenyoung.com/),里面包含了
101
+ hn 上前 100 位用户提交的任何帖子。
102
+
103
+ 几天之后我就发现,之前只看 HN 的热门帖子损失有多大,HN
104
+ 上有太多优秀的内容没有被顶上去了。热门真的很随机,所以我们也要万分警惕大公司的算法机制。基于此,我又做了一个变化,直接抓取 HN 上最新的提交(我去掉了 Ask
105
+ HN 的内容,因为 Ask HN
106
+ 的质量相对较差,并且我还有专门的[Ask HN](https://ask.buzzing.cc)可以回头再看)。那么现在专属于我的[HN 时间流](https://myfeed.owenyoung.com/)就包括了
107
+ HN 上所有最新的文章了。即使是全部文章,由于是母语阅读,我还是能快速读完这些标题,找到感兴趣的内容,还可能留下我的 2 cents.
108
+ 把之前关注了很久的不少优质英文 RSS
109
+ 源,也利于[Denoflow](https://github.com/denoflow/denoflow)生成了一个专属于我的[RSS 翻译流](https://myrss.owenyoung.com/).
110
+ 这样下来我的空余时间就可以住要花在阅读长文章,而不是刷推特这样更短的文章。我们都知道推特其实无法代替长文章,就像长文章也无法代替读书一样。
111
+
112
+ 如果有人看到这里的话,那你一定会觉得 HN 有毒,而我已经上瘾了。其实没错,目前阶段是这样。
113
+
114
+ 好在 HN 上的人也经常推荐图书,比如以下两个链接就是 HN 的用户制作的 HN 评论大数据最推荐的书(HN 的评论比图书网站,或者第三方网站更可信):
115
+
116
+ - [汇总了 hacker news 上评论里提到的书籍的排名](https://hacker-recommended-books.vercel.app/)
117
+ - [又一个 hacker news 提到的图书周报](https://hackernewsbooks.com/top-books-on-hacker-news)
118
+
119
+ 比如我读了上面推荐的[《深度工作》](https://d.buzzing.cc/post/1),作者提到:
120
+
121
+ > "如果在你全部的清醒时间,都能给自己的大脑找到有意义的事情去做,而不是放任自己在迷糊的状态下漫无目的地浏览几个小时网页,那么在一天结束时,你会觉得更加充实,第二天开始时更加轻松。
122
+ > – 卡尔·纽波特《深度工作》
123
+
124
+ 从我沉迷 HN
125
+ 的例子里就能看出,网络已经剥夺了我保持专注和沉思的能力,但是真正值得探索和学习的知识都需要我拥有这种能力。如果我没有在某一个特定时段给自己安排任务,那么这些网站总是会更有诱惑力。所以如果我想抵御社交网络对我时间和精力的诱惑,那么必须要给大脑找一些高质量的替代活动,所以我基于开源
126
+ [Lemmy](https://lemmy.ml/) 搭建了一个类似 Reddit
127
+ 的社区,叫[**如何度过每一天**](https://d.buzzing.cc/),希望能在社区里分享有意义的事,不虚度光阴的事,可以是阅读某本书,学习某项技能,看某部纪录片等等等等。社区有投票的功能,某项活动被投票的次数越多,说明认为这项活动有意义的更多。
128
+
129
+ 目前其实就只有少数几个用户在用,网站依然是部署在我的一个 廉价 VPS 上,没有盈利的打算,目前加载也有点慢,但是够用。
130
+
131
+ 以上是我在 Hacker News 上学到的一些东西。
132
+ ','<p>昨天看到 Hacker News 的创始人 Paul Graham 在 2009<br>年写的一篇文章<a href="http://www.paulgraham.com/hackernews.html">《我从 Hacker News 中学到了什么》</a>,<br>作者提到:</p>
128
133
  <blockquote>
129
- <p><strong>Jant</strong> = Jantelagen (Law of Jante)<br>Low-key, de-socialized personal expression.</p>
134
+ <p>随着 Hacker News 变得越来越大,占用的时间比我预期的要多,但我并不后悔,因为我从工作中学到了很多东西。</p>
130
135
  </blockquote>
131
- <h2>What is Jant?</h2>
132
- <p>Jant is a single-author microblog for people who want to share thoughts without the noise of social media. No followers, no likes, no retweets—just your words.</p>
133
- <p><strong>Features</strong>:</p>
136
+ <p>就挺感慨的,一方面是我亲自做了一遍 HN 的中文翻译,在整个过程中学到了很多,另一方面也让我回想起大学期间做的一个社区,对我的之后的种种影响。</p>
137
+ <p>如果有机会的话,我觉得每个人都应该尽可能的去亲自参与创造某个能有其他人一起创造信息的社区,比如成立一个小组,一个论坛,一个社群等等,线下的可能更好。和人交流,能碰撞出我更多的想法,以及让我知道更大的世界,更多元的价值观。</p>
138
+ <p><a href="https://hn.buzzing.cc">hacker news 的中文翻译</a>是一个非常普通的想法,每年可能都有人声称自己做了一个 HN 的中文版。因为<br>HN 上不仅文章质量普遍优质,而且最重要的是: HN 的 API 是业界最友好的,所有人都可以用来练手,做一个 HN 的 XX<br>版,比如各种平台的客户端,各种语言的实现,rss 服务,email 订阅,评论通知,大数据分析,newsletter<br>等等,不胜枚举。由于我的英文水平并不能像母语一样让我能快速找到感兴趣的东西(后来发现其实不少看起来英文很好的人,也有类似的体验),所以如果我想阅读 HN<br>的话,我可能有 2 个选择:</p>
139
+ <ol>
140
+ <li>抱着锻炼自己英语的想法逼迫自己不太流利的浏览 HN</li>
141
+ <li>使用浏览器自带的翻译功能,浏览 HN</li>
142
+ </ol>
143
+ <p>这两个阶段我都经历过。我希望你不要经历,因为每个阶段都可能使你错过 HN,错过一个很优质的信息源。</p>
144
+ <p>第一个阶段,我只能坚持几天,然后就会本能的优先去看其他中文信息,慢慢的其实就没在看 HN 了。这在我的推特账号上尤其明显,我注册了 2<br>个推特号,一个是英文号<a href="https://twitter.com/TheOwenYoung">@TheOwenYoung</a>,一个是中文号<a href="https://twitter.com/OwenYoungZh">@OwenYoungZh</a>,本来是先开英文号的,专门关注的都是些英文博主,但是后面实在受不了了,说那就再开个中文号,尽量少关注人,只发一些东西。后来的故事就是,英文号上的很少,主力变成了中文号。唉,人就是离不开舒适圈,所以我们最好不要给自己创造舒适圈,比如在浏览器上设置很多社交网络的快捷书签;新<br>tab 页放很多快捷入口;手机上下载很多好用的应用;如果想克制自己,就最好只下必要应用,优先用网页版,或者像我现在做的,把 iPhone13<br>当成测试机,用一台好几年前的 Pixel 3a,又卡,掉电还快,但是用起来其实完全没问题,然后我又重新开始用<br>Kindle,只能一个操作,就是下一页,这样就能节省很多时间来看书了。</p>
145
+ <p>第二个阶段,我发现浏览器自带的翻译功能并不是太好用(去年我开始做 hn<br>中文版的时候更不好用,现在其实好了很多),比如有一些网页它会失败,要么就是要重试,然后排版也不太好看,我更希望的是有些区域不要翻译,只翻译标题之类的,但是浏览器会全部翻译,就不太好浏览。而作为一个要长期阅读的地方,体验还是挺重要的。</p>
146
+ <p>正好去年那会儿,刚做完<a href="https://github.com/actionsflow/actionsflow">Actionsflow</a>, 想利用<br>Actionsflow 做点什么,所以说那就做一个 HN<br>的中文翻译吧,这很适合工作流去处理,处理完之后生成一个静态网页,就算没人用,我自己也可以天天用,做好之后也不用维护,就一直在那。于是就开干,没想到做到后面发现还可以顺便做点别的我感兴趣的英文内容翻译,比如<a href="https://reddit.buzzing.cc">Reddit 的美股讨论</a>,<a href="https://news.buzzing.cc">国外的权威媒体报道</a>,<a href="https://ph.buzzing.cc">ProductHunt</a>等等,最后就汇总成了一个<a href="https://www.buzzing.cc">Buzzing</a></p>
147
+ <p>Buzzing<br>运行了一年之后,发现之前的设计有不少欠考虑的地方,<a href="https://github.com/actionsflow/actionsflow">Actionsflow</a>也有点太重了,尤其是看了一年<br>HN 的帖子后,觉得 Actionsflow<br>的设计简直就是一坨屎,又臭又长。这个时候<a href="https://twitter.com/OwenYoungZh/status/1478928692781137925">才发现 Deno 是最适合做工作流的运行时</a>,Deno<br>的依赖只需要<br>URL,天生适合脚本。这样就可以和<code>package.json</code>,<code>package-lock.json</code>,<code>node_modules</code>地狱说拜拜了,而且我的工作流也不用再依赖<br>Docker 和<a href="https://github.com/nektos/act">act</a>了,于是过了 10 多天之后,我开始着手做了一个史上最快兑现的<br>「如果我有时间,我一定要做 XXX<br>」的项目:<a href="https://twitter.com/OwenYoungZh/status/1485381401327267840">Denoflow</a>,用来做我的低代码<br>IFTTT 或者说 Zapier,用流行一点的话说,这是配置即代码服务(Configure as Code)。</p>
148
+ <p>这一次,产生了一些变化,在泡了一年自己做的 HN 中文版之后:</p>
149
+ <ol>
150
+ <li>首先我没有像 Actionsflow 一样设计一个浮夸的<a href="https://actionsflow.github.io/">Landing 页</a>.</li>
151
+ <li>其次我没有用自己蹩脚的英语凑成一个看起来功能很丰富,维护人员很多,很正规的<a href="https://actionsflow.github.io/docs/reference/">一个文档网站</a>。我只在项目的<a href="https://github.com/denoflow/denoflow">Readme 文件</a>里写了一个我自认为很诚恳的说明,在一页的文本里说清楚这个项目的用途,和看一个示例就能明白的使用方法,没有多余的营销话语,把时间真正花在工具本身,利用省下的时间做了一个<a href="https://playground.owenyoung.com/">在线 PlayGround</a>,运行在我的廉价<br>VPS 上。</li>
152
+ <li>没考虑买一个域名,尽管<code>denoflow.com</code>域名还在。直接放在我的个人子域<code>playground.owenyoung.com</code>.</li>
153
+ <li>版本号的克制,对于刚推出的项目,我使用<code>0.0.x</code>,而不是像 Actionsflow<br>一样,一上来就是<code>1.0.0</code>。而后来的证明,<code>0.0.x</code>是最适合这个项目的,因为在后来的使用中,发现了不少的致命 bug</li>
154
+ <li>在文档的显眼处声明:<code>项目仍处于非常早期的阶段,谨慎使用!</code></li>
155
+ <li>仔细思考了项目的 License,选择了 Apache2.0</li>
156
+ </ol>
157
+ <p>做完之后,又花了 2 个小时,用 Denoflow<br>做了<a href="https://showhn.buzzing.cc/">Show HN</a>,<a href="https://askhn.buzzing.cc/">Ask HN</a>,<a href="https://hackernews.buzzing.cc/">HN 首页</a>,<a href="https://besthn.buzzing.cc">Best HN</a>,这里可以和之前的<a href="https://hn.buzzing.cc">HN 热门</a>对比下,没有追踪,没有<br>JS<br>代码,只是使用一个舒服的背景色,使用<a href="https://github.com/dbohdan/classless-css">class less 原则</a>,同时生成一个<br>RSS。没有使用任何框架,就几句简单的 deno<br>代码即可生成。和之前的<a href="https://hn.buzzing.cc">HN 热门</a>相比,<a href="https://hn.buzzing.cc">HN 热门</a>在一个屏幕里只能显示出<br>2 篇文章,而现在<a href="https://hackernews.buzzing.cc/">HN 首页</a>可以放 20 多篇,效率直接提升 10 倍啊有木有。</p>
158
+ <p>很快,我发现按照这样布局的话,阅读效率高了太多了,已有的这些时间流,已经不够我看了,所以索性单独为我自己生成一个<a href="https://myfeed.owenyoung.com/">HN 时间流</a>,里面包含了<br>hn 上前 100 位用户提交的任何帖子。</p>
159
+ <p>几天之后我就发现,之前只看 HN 的热门帖子损失有多大,HN<br>上有太多优秀的内容没有被顶上去了。热门真的很随机,所以我们也要万分警惕大公司的算法机制。基于此,我又做了一个变化,直接抓取 HN 上最新的提交(我去掉了 Ask<br>HN 的内容,因为 Ask HN<br>的质量相对较差,并且我还有专门的<a href="https://ask.buzzing.cc">Ask HN</a>可以回头再看)。那么现在专属于我的<a href="https://myfeed.owenyoung.com/">HN 时间流</a>就包括了<br>HN 上所有最新的文章了。即使是全部文章,由于是母语阅读,我还是能快速读完这些标题,找到感兴趣的内容,还可能留下我的 2 cents.<br>把之前关注了很久的不少优质英文 RSS<br>源,也利于<a href="https://github.com/denoflow/denoflow">Denoflow</a>生成了一个专属于我的<a href="https://myrss.owenyoung.com/">RSS 翻译流</a>.<br>这样下来我的空余时间就可以住要花在阅读长文章,而不是刷推特这样更短的文章。我们都知道推特其实无法代替长文章,就像长文章也无法代替读书一样。</p>
160
+ <p>如果有人看到这里的话,那你一定会觉得 HN 有毒,而我已经上瘾了。其实没错,目前阶段是这样。</p>
161
+ <p>好在 HN 上的人也经常推荐图书,比如以下两个链接就是 HN 的用户制作的 HN 评论大数据最推荐的书(HN 的评论比图书网站,或者第三方网站更可信):</p>
134
162
  <ul>
135
- <li>Multiple content types: notes, articles, links, quotes, images</li>
136
- <li>Thread support for longer thoughts</li>
137
- <li>Collections for curated topics</li>
138
- <li>Beautiful, themeable design</li>
139
- <li>Deploys to Cloudflare Workers in minutes</li>
163
+ <li><a href="https://hacker-recommended-books.vercel.app/">汇总了 hacker news 上评论里提到的书籍的排名</a></li>
164
+ <li><a href="https://hackernewsbooks.com/top-books-on-hacker-news">又一个 hacker news 提到的图书周报</a></li>
140
165
  </ul>
141
- <h2>Quick Start</h2>
142
- <pre><code class="language-bash"># Create a new Jant site
143
- pnpm create jant my-blog
144
-
145
- # Start development
146
- cd my-blog
147
- pnpm dev
148
-
149
- # Deploy to Cloudflare
150
- pnpm run deploy
151
- </code></pre>
152
- <h2>Documentation</h2>
153
- <ul>
154
- <li><a href="docs/getting-started.md">Getting Started</a></li>
155
- <li><a href="docs/deployment.md">Deployment</a></li>
156
- <li><a href="docs/configuration.md">Configuration</a></li>
157
- <li><a href="docs/theming.md">Theming</a></li>
158
- <li><a href="docs/API.md">API Reference</a></li>
159
- </ul>
160
- <h2>Development</h2>
161
- <p>Requires <a href="https://mise.jdx.dev/">mise</a> — it manages Node.js and pnpm automatically.</p>
162
- <pre><code class="language-bash"># Install mise (macOS/Linux)
163
- curl https://mise.run | sh
164
-
165
- # Clone and setup
166
- git clone https://github.com/jant-me/jant.git
167
- cd jant
168
- mise install # installs Node.js and pnpm
169
- pnpm install # installs dependencies
170
-
171
- # Start development server (http://localhost:9019)
172
- mise run dev
173
- </code></pre>
174
- <p>See <a href="CONTRIBUTING.md">CONTRIBUTING.md</a> for code style, PR process, and release workflow.</p>
175
- <h2>Philosophy</h2>
176
- <p>Jant is built on the idea that not everything needs to be optimized for engagement. Write for yourself. Share if you want. No metrics, no pressure.</p>
177
- <h2>License</h2>
178
- <p>AGPL-3.0</p>
179
- ',NULL,NULL,NULL,NULL,NULL,NULL,1770759271,1770759271,1770759271);
180
-
181
- -- collections
182
- INSERT INTO collections VALUES(1,'getting-started','Getting Started','Resources for getting started with Jant',1770689095,1770689095);
183
- INSERT INTO collections VALUES(2,'inspires','Inspires',NULL,1770758555,1770758555);
184
-
185
- -- post_collections
186
- INSERT INTO post_collections VALUES(1,1,1770689095);
187
- INSERT INTO post_collections VALUES(6,2,1770758568);
166
+ <p>比如我读了上面推荐的<a href="https://d.buzzing.cc/post/1">《深度工作》</a>,作者提到:</p>
167
+ <blockquote>
168
+ <p>"如果在你全部的清醒时间,都能给自己的大脑找到有意义的事情去做,而不是放任自己在迷糊的状态下漫无目的地浏览几个小时网页,那么在一天结束时,你会觉得更加充实,第二天开始时更加轻松。<br>– 卡尔·纽波特《深度工作》</p>
169
+ </blockquote>
170
+ <p>从我沉迷 HN<br>的例子里就能看出,网络已经剥夺了我保持专注和沉思的能力,但是真正值得探索和学习的知识都需要我拥有这种能力。如果我没有在某一个特定时段给自己安排任务,那么这些网站总是会更有诱惑力。所以如果我想抵御社交网络对我时间和精力的诱惑,那么必须要给大脑找一些高质量的替代活动,所以我基于开源<br><a href="https://lemmy.ml/">Lemmy</a> 搭建了一个类似 Reddit<br>的社区,叫<a href="https://d.buzzing.cc/"><strong>如何度过每一天</strong></a>,希望能在社区里分享有意义的事,不虚度光阴的事,可以是阅读某本书,学习某项技能,看某部纪录片等等等等。社区有投票的功能,某项活动被投票的次数越多,说明认为这项活动有意义的更多。</p>
171
+ <p>目前其实就只有少数几个用户在用,网站依然是部署在我的一个 廉价 VPS 上,没有盈利的打算,目前加载也有点慢,但是够用。</p>
172
+ <p>以上是我在 Hacker News 上学到的一些东西。</p>
173
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771115051,1771115051,1771115051);
174
+ INSERT INTO posts VALUES(8,'quote','published',0,0,NULL,NULL,'https://www.joanwestenberg.com/why-my-newsletter-costs-2-50/',NULL,NULL,'拥有五万个从不打开邮件的订阅者名单,其价值远不及五百个逐字阅读并偶尔给予深度回复的读者。后者会购买你的著作,向朋友推荐你,并长久陪伴你。前者不过是你在那些无聊的数字炫耀派对上能拿出来吹嘘的数字而已。',NULL,NULL,NULL,NULL,NULL,1771115103,1771115103,1771115103);
175
+ INSERT INTO posts VALUES(9,'quote','published',1,0,NULL,'JA Westenberg','https://www.joanwestenberg.com/the-case-for-blogging-in-the-ruins/',NULL,NULL,'弗吉尼亚·伍尔夫曾论述拥有独立空间的重要性:那是供人进行创作的物理场所,不受干扰与控制。博客便是网络世界中的独立空间。在这里,你自主决定书写内容与表达方式,无需屈从于平台算法的任性摆布——那些平台只顾榨取你的参与度,却不关心这种参与究竟是否让你或他人变得更聪明。
176
+
177
+ 我们不会通过等待平台减少剥削来获得更好的互联网。我们需要亲手构建它——维护自己的空间,相互链接,打造独立网站的互联网络,让博客圈重现昔日的辉煌。',NULL,NULL,NULL,NULL,NULL,1771115159,1771115159,1771115177);
178
+ INSERT INTO posts VALUES(10,'link','published',0,0,NULL,'Some pics','https://some.pics/','一个干净的图片流网站
179
+ ','<p>一个干净的图片流网站</p>
180
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771115271,1771115271,1771115271);
181
+ INSERT INTO posts VALUES(11,'note','published',0,0,NULL,NULL,NULL,'From splash','<p>From splash</p>
182
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771268320,1771268320,1771268958);
183
+
184
+ -- nav_items
185
+ INSERT INTO nav_items VALUES(1,'link','Featured','/featured',NULL,1,1771272664,1771273781);
186
+ INSERT INTO nav_items VALUES(2,'link','Collections','/collections',NULL,2,1771272682,1771273781);
187
+ INSERT INTO nav_items VALUES(4,'page','About','/about',1,3,1771272722,1771273781);
188
+ INSERT INTO nav_items VALUES(5,'link','Home','/',NULL,0,1771273779,1771273781);
188
189
 
189
190
  -- media
190
- INSERT INTO media VALUES('019c496c-46bd-7954-bd6a-77b1b8f1d451',NULL,'019c496c-46bd-7954-bd6a-77b1b8f1d451.webp','tegan-conway-KaFfNTw8OYQ-unsplash.webp','image/webp',715364,'media/2026/02/019c496c-46bd-7954-bd6a-77b1b8f1d451.webp',NULL,NULL,NULL,1770758358,0,NULL,'r2');
191
- INSERT INTO media VALUES('019c496c-5e44-70d2-ac8a-c0c0bdaab65c',6,'019c496c-5e44-70d2-ac8a-c0c0bdaab65c.webp','land-o-lakes-inc-9w6Qb-dqBwE-unsplash.webp','image/webp',306042,'media/2026/02/019c496c-5e44-70d2-ac8a-c0c0bdaab65c.webp',NULL,NULL,NULL,1770758364,3,NULL,'r2');
192
- INSERT INTO media VALUES('019c496d-5011-7981-89a0-b4373a695d78',6,'019c496d-5011-7981-89a0-b4373a695d78.webp','land-o-lakes-inc-k71TQkbVIgI-unsplash.webp','image/webp',597680,'media/2026/02/019c496d-5011-7981-89a0-b4373a695d78.webp',NULL,NULL,NULL,1770758426,2,NULL,'r2');
193
- INSERT INTO media VALUES('019c496d-630c-70b4-9004-51a194746566',6,'019c496d-630c-70b4-9004-51a194746566.webp','thingsneverchange-CgHNmQ0c2w4-unsplash.webp','image/webp',358320,'media/2026/02/019c496d-630c-70b4-9004-51a194746566.webp',NULL,NULL,NULL,1770758431,1,NULL,'r2');
194
- INSERT INTO media VALUES('019c496d-720f-70d2-98b8-3779457de73c',6,'019c496d-720f-70d2-98b8-3779457de73c.webp','willian-justen-de-vasconcellos-7jg7Y_Mlf2Q-unsplash.webp','image/webp',478956,'media/2026/02/019c496d-720f-70d2-98b8-3779457de73c.webp',NULL,NULL,NULL,1770758435,0,NULL,'r2');
195
- INSERT INTO media VALUES('019c496e-6904-7f61-b14f-080035ffe23f',5,'019c496e-6904-7f61-b14f-080035ffe23f.webp','richard-stachmann-Es--yoQocSM-unsplash.webp','image/webp',381534,'media/2026/02/019c496e-6904-7f61-b14f-080035ffe23f.webp',NULL,NULL,NULL,1770758498,0,NULL,'r2');
191
+ INSERT INTO media VALUES('019c676d-1fcd-7c11-9737-5aebd10c1087',3,'019c676d-1fcd-7c11-9737-5aebd10c1087.webp','ansis-kancs-XGxp1rvTVag-unsplash.webp','image/webp',202748,'media/2026/02/019c676d-1fcd-7c11-9737-5aebd10c1087.webp',NULL,NULL,NULL,1771261731,0,NULL,'r2');
192
+ INSERT INTO media VALUES('019c679e-98f9-73ba-bd57-ab6ebf94fe05',3,'019c679e-98f9-73ba-bd57-ab6ebf94fe05.webp','dan-begel-pbfkmEL3QaU-unsplash.webp','image/webp',229392,'media/2026/02/019c679e-98f9-73ba-bd57-ab6ebf94fe05.webp',NULL,NULL,NULL,1771264973,1,NULL,'r2');
193
+ INSERT INTO media VALUES('019c679e-a8ce-7d49-9c21-8d75a1551198',3,'019c679e-a8ce-7d49-9c21-8d75a1551198.webp','willian-justen-de-vasconcellos--pKhrem8YpE-unsplash.webp','image/webp',324826,'media/2026/02/019c679e-a8ce-7d49-9c21-8d75a1551198.webp',NULL,NULL,NULL,1771264977,2,NULL,'r2');
194
+ INSERT INTO media VALUES('019c679e-c351-76dc-9569-2a716b4384b5',NULL,'019c679e-c351-76dc-9569-2a716b4384b5.webp','brianna-parks-frXRtKYPqXk-unsplash.webp','image/webp',264554,'media/2026/02/019c679e-c351-76dc-9569-2a716b4384b5.webp',NULL,NULL,NULL,1771264984,0,NULL,'r2');
195
+ INSERT INTO media VALUES('019c67d5-1022-7efd-9ae8-0454325cba78',NULL,'019c67d5-1022-7efd-9ae8-0454325cba78.webp','takashi-sakamoto-J9nW9Tc1HVc-unsplash.webp','image/webp',505172,'media/2026/02/019c67d5-1022-7efd-9ae8-0454325cba78.webp',NULL,NULL,NULL,1771268543,0,NULL,'r2');
196
+ INSERT INTO media VALUES('019c67d5-28b8-7ec3-ae2e-83d9eb1b096b',NULL,'019c67d5-28b8-7ec3-ae2e-83d9eb1b096b.webp','museum-of-new-zealand-te-papa-tongarewa-1C7oRRwUkgU-unsplash.webp','image/webp',322656,'media/2026/02/019c67d5-28b8-7ec3-ae2e-83d9eb1b096b.webp',NULL,NULL,NULL,1771268549,0,NULL,'r2');
197
+ INSERT INTO media VALUES('019c67d5-39c9-7723-8b0c-4c8a50076dc3',11,'019c67d5-39c9-7723-8b0c-4c8a50076dc3.webp','land-o-lakes-inc-peEOQ4_dqOo-unsplash.webp','image/webp',470774,'media/2026/02/019c67d5-39c9-7723-8b0c-4c8a50076dc3.webp',NULL,NULL,NULL,1771268554,0,NULL,'r2');
@@ -2,7 +2,7 @@
2
2
  * Jant Site Entry Point
3
3
  *
4
4
  * This is the main entry point for your Jant site.
5
- * The minimal theme is used by default. To customize, pass options to createApp().
5
+ * Customize with CSS variables, color themes, or custom CSS in the dashboard.
6
6
  *
7
7
  * Configuration:
8
8
  * - Site settings (name, description, language) should be configured via
@@ -27,6 +27,9 @@ SITE_URL = ""
27
27
  # SITE_DESCRIPTION = "A personal blog powered by Jant"
28
28
  # SITE_LANGUAGE = "en"
29
29
 
30
+ # Optional: Timeline page size (default: 20)
31
+ # PAGE_SIZE = "5"
32
+
30
33
  # Optional: R2 Storage (for media uploads)
31
34
  # R2_PUBLIC_URL = "https://cdn.example.com"
32
35
  R2_PUBLIC_URL = "https://demo-media.jant.me" # @create-jant: @remove
@@ -47,13 +50,6 @@ R2_PUBLIC_URL = "https://demo-media.jant.me" # @create-jant: @remove
47
50
  # S3_BUCKET = "my-bucket"
48
51
  # S3_REGION = "us-east-1"
49
52
  # S3_PUBLIC_URL = "https://cdn.example.com"
50
- # @create-jant: @remove-start
51
- STORAGE_DRIVER = "s3"
52
- S3_ENDPOINT = "https://s3.us-east-005.backblazeb2.com"
53
- S3_BUCKET = "jant-media"
54
- S3_REGION = "us-east-005"
55
- S3_PUBLIC_URL = "https://files.owenyoung.com/file/jant-media"
56
- # @create-jant: @remove-end
57
53
 
58
54
  [[d1_databases]]
59
55
  binding = "DB"
@@ -1,21 +0,0 @@
1
- #!/bin/sh
2
- basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
-
4
- case `uname` in
5
- *CYGWIN*|*MINGW*|*MSYS*)
6
- if command -v cygpath > /dev/null 2>&1; then
7
- basedir=`cygpath -w "$basedir"`
8
- fi
9
- ;;
10
- esac
11
-
12
- if [ -z "$NODE_PATH" ]; then
13
- export NODE_PATH="/home/runner/work/jant/jant/packages/core/bin/node_modules:/home/runner/work/jant/jant/packages/core/node_modules:/home/runner/work/jant/jant/packages/node_modules:/home/runner/work/jant/jant/node_modules:/home/runner/work/jant/node_modules:/home/runner/work/node_modules:/home/runner/node_modules:/home/node_modules:/node_modules:/home/runner/work/jant/jant/node_modules/.pnpm/node_modules"
14
- else
15
- export NODE_PATH="/home/runner/work/jant/jant/packages/core/bin/node_modules:/home/runner/work/jant/jant/packages/core/node_modules:/home/runner/work/jant/jant/packages/node_modules:/home/runner/work/jant/jant/node_modules:/home/runner/work/jant/node_modules:/home/runner/work/node_modules:/home/runner/node_modules:/home/node_modules:/node_modules:/home/runner/work/jant/jant/node_modules/.pnpm/node_modules:$NODE_PATH"
16
- fi
17
- if [ -x "$basedir/node" ]; then
18
- exec "$basedir/node" "$basedir/../@jant/core/bin/jant.js" "$@"
19
- else
20
- exec node "$basedir/../@jant/core/bin/jant.js" "$@"
21
- fi