create-jant 0.3.22 → 0.3.24

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.22";
12
+ var CORE_VERSION = "0.3.24";
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.22",
3
+ "version": "0.3.24",
4
4
  "description": "Create a new Jant project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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"]] : []),
59
58
  [
60
59
  "posts",
61
- noMedia
62
- ? "SELECT * FROM posts WHERE deleted_at IS NULL AND type != 'image'"
63
- : "SELECT * FROM posts WHERE deleted_at IS NULL",
60
+ "SELECT * FROM posts WHERE deleted_at IS NULL",
64
61
  ],
62
+ ["pages"],
65
63
  ["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,27 +1,25 @@
1
- -- =============================================================================
2
- -- Reset script for local development
3
- -- Clears ALL data (including users) to prepare for re-seeding
1
+ -- Reset script for Jant local development (v2 schema)
2
+ -- Clears all data so seed-local.sql can re-insert everything
4
3
  -- Usage: mise run db-seed (runs this then seed-local.sql)
5
- -- =============================================================================
6
4
 
7
- -- Clear FTS index first
5
+ -- Clear FTS index first (to avoid trigger issues)
8
6
  DELETE FROM posts_fts;
9
7
 
10
- -- Clear join tables
11
- DELETE FROM post_collections;
12
-
13
- -- Clear main tables
8
+ -- Clear main tables (order matters for FK constraints)
9
+ DELETE FROM nav_items;
14
10
  DELETE FROM media;
15
11
  DELETE FROM posts;
12
+ DELETE FROM pages;
16
13
  DELETE FROM collections;
17
14
  DELETE FROM redirects;
18
- DELETE FROM settings;
19
15
 
20
- -- Clear auth tables
16
+ -- Clear auth tables (order matters: session → account → user)
21
17
  DELETE FROM session;
22
- DELETE FROM verification;
23
18
  DELETE FROM account;
24
19
  DELETE FROM user;
25
20
 
21
+ -- Clear settings
22
+ DELETE FROM settings;
23
+
26
24
  -- Reset auto-increment counters
27
- DELETE FROM sqlite_sequence;
25
+ DELETE FROM sqlite_sequence WHERE name IN ('posts', 'pages', 'collections', 'nav_items', 'redirects');
@@ -1,195 +1,182 @@
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
7
+ -- Reset (clear existing content)
8
+ DELETE FROM posts_fts;
75
9
 
76
- ```bash
77
- # Create a new Jant site
78
- pnpm create jant my-blog
10
+ DELETE FROM post_collections;
79
11
 
80
- # Start development
81
- cd my-blog
82
- pnpm dev
12
+ DELETE FROM media;
13
+ DELETE FROM posts;
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', 'media', 'collections', '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(4,'note','quiet',NULL,NULL,'没有人真的想交付自己都看不上的东西。我们天然就想把事情做得更对一点、更好一点。','<p>没有人真的想交付自己都看不上的东西。我们天然就想把事情做得更对一点、更好一点。</p>
22
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771114861,1771114861,1771114861);
23
+ INSERT INTO posts VALUES(5,'note','quiet',NULL,NULL,'新荣记的老板会给出预算,让店长和主厨每两周可以自己订一家餐厅去体验,而且预算没有上限。从到东京筹备开店至今,他们已经吃了日本十几家不同的餐厅。很多餐厅的服务员从没吃过自家餐厅的招牌菜,而新荣记不但安排自己的厨师和服务人员吃自己家的菜,还会提供费用,让这些年轻人去他们向往的餐厅,体验别人家的优点。"。我想,对我们网上冲浪的启发就是我们也要多体验别人做的好东西,从中提高审美和获得灵感。','<p>新荣记的老板会给出预算,让店长和主厨每两周可以自己订一家餐厅去体验,而且预算没有上限。从到东京筹备开店至今,他们已经吃了日本十几家不同的餐厅。很多餐厅的服务员从没吃过自家餐厅的招牌菜,而新荣记不但安排自己的厨师和服务人员吃自己家的菜,还会提供费用,让这些年轻人去他们向往的餐厅,体验别人家的优点。&quot;。我想,对我们网上冲浪的启发就是我们也要多体验别人做的好东西,从中提高审美和获得灵感。</p>
24
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771114906,1771114906,1771114906);
25
+ INSERT INTO posts VALUES(6,'link','quiet','Forty News',NULL,'40年前今日的新闻...','<p>40年前今日的新闻...</p>
26
+ ','https://forty.news/',NULL,'forty.news',NULL,NULL,NULL,1771114955,1771114955,1771114955);
27
+ INSERT INTO posts VALUES(7,'article','quiet','我从 Hacker News 中学到了什么?',NULL,'昨天看到 Hacker News 的创始人 Paul Graham 在 2009
28
+ 年写的一篇文章[《我从 Hacker News 中学到了什么》](http://www.paulgraham.com/hackernews.html),
29
+ 作者提到:
30
+
31
+ > 随着 Hacker News 变得越来越大,占用的时间比我预期的要多,但我并不后悔,因为我从工作中学到了很多东西。
32
+
33
+ 就挺感慨的,一方面是我亲自做了一遍 HN 的中文翻译,在整个过程中学到了很多,另一方面也让我回想起大学期间做的一个社区,对我的之后的种种影响。
34
+
35
+ 如果有机会的话,我觉得每个人都应该尽可能的去亲自参与创造某个能有其他人一起创造信息的社区,比如成立一个小组,一个论坛,一个社群等等,线下的可能更好。和人交流,能碰撞出我更多的想法,以及让我知道更大的世界,更多元的价值观。
36
+
37
+ [hacker news 的中文翻译](https://hn.buzzing.cc)是一个非常普通的想法,每年可能都有人声称自己做了一个 HN 的中文版。因为
38
+ HN 上不仅文章质量普遍优质,而且最重要的是: HN 的 API 是业界最友好的,所有人都可以用来练手,做一个 HN 的 XX
39
+ 版,比如各种平台的客户端,各种语言的实现,rss 服务,email 订阅,评论通知,大数据分析,newsletter
40
+ 等等,不胜枚举。由于我的英文水平并不能像母语一样让我能快速找到感兴趣的东西(后来发现其实不少看起来英文很好的人,也有类似的体验),所以如果我想阅读 HN
41
+ 的话,我可能有 2 个选择:
42
+
43
+ 1. 抱着锻炼自己英语的想法逼迫自己不太流利的浏览 HN
44
+ 2. 使用浏览器自带的翻译功能,浏览 HN
45
+
46
+ 这两个阶段我都经历过。我希望你不要经历,因为每个阶段都可能使你错过 HN,错过一个很优质的信息源。
47
+
48
+ 第一个阶段,我只能坚持几天,然后就会本能的优先去看其他中文信息,慢慢的其实就没在看 HN 了。这在我的推特账号上尤其明显,我注册了 2
49
+ 个推特号,一个是英文号[@TheOwenYoung](https://twitter.com/TheOwenYoung),一个是中文号[@OwenYoungZh](https://twitter.com/OwenYoungZh),本来是先开英文号的,专门关注的都是些英文博主,但是后面实在受不了了,说那就再开个中文号,尽量少关注人,只发一些东西。后来的故事就是,英文号上的很少,主力变成了中文号。唉,人就是离不开舒适圈,所以我们最好不要给自己创造舒适圈,比如在浏览器上设置很多社交网络的快捷书签;新
50
+ tab 页放很多快捷入口;手机上下载很多好用的应用;如果想克制自己,就最好只下必要应用,优先用网页版,或者像我现在做的,把 iPhone13
51
+ 当成测试机,用一台好几年前的 Pixel 3a,又卡,掉电还快,但是用起来其实完全没问题,然后我又重新开始用
52
+ Kindle,只能一个操作,就是下一页,这样就能节省很多时间来看书了。
53
+
54
+ 第二个阶段,我发现浏览器自带的翻译功能并不是太好用(去年我开始做 hn
55
+ 中文版的时候更不好用,现在其实好了很多),比如有一些网页它会失败,要么就是要重试,然后排版也不太好看,我更希望的是有些区域不要翻译,只翻译标题之类的,但是浏览器会全部翻译,就不太好浏览。而作为一个要长期阅读的地方,体验还是挺重要的。
56
+
57
+ 正好去年那会儿,刚做完[Actionsflow](https://github.com/actionsflow/actionsflow), 想利用
58
+ Actionsflow 做点什么,所以说那就做一个 HN
59
+ 的中文翻译吧,这很适合工作流去处理,处理完之后生成一个静态网页,就算没人用,我自己也可以天天用,做好之后也不用维护,就一直在那。于是就开干,没想到做到后面发现还可以顺便做点别的我感兴趣的英文内容翻译,比如[Reddit 的美股讨论](https://reddit.buzzing.cc),[国外的权威媒体报道](https://news.buzzing.cc),[ProductHunt](https://ph.buzzing.cc)等等,最后就汇总成了一个[Buzzing](https://www.buzzing.cc)
60
+
61
+ Buzzing
62
+ 运行了一年之后,发现之前的设计有不少欠考虑的地方,[Actionsflow](https://github.com/actionsflow/actionsflow)也有点太重了,尤其是看了一年
63
+ HN 的帖子后,觉得 Actionsflow
64
+ 的设计简直就是一坨屎,又臭又长。这个时候[才发现 Deno 是最适合做工作流的运行时](https://twitter.com/OwenYoungZh/status/1478928692781137925),Deno
65
+ 的依赖只需要
66
+ URL,天生适合脚本。这样就可以和`package.json`,`package-lock.json`,`node_modules`地狱说拜拜了,而且我的工作流也不用再依赖
67
+ Docker 和[act](https://github.com/nektos/act)了,于是过了 10 多天之后,我开始着手做了一个史上最快兑现的
68
+ 「如果我有时间,我一定要做 XXX
69
+ 」的项目:[Denoflow](https://twitter.com/OwenYoungZh/status/1485381401327267840),用来做我的低代码
70
+ IFTTT 或者说 Zapier,用流行一点的话说,这是配置即代码服务(Configure as Code)。
71
+
72
+ 这一次,产生了一些变化,在泡了一年自己做的 HN 中文版之后:
73
+
74
+ 1. 首先我没有像 Actionsflow 一样设计一个浮夸的[Landing 页](https://actionsflow.github.io/).
75
+ 2. 其次我没有用自己蹩脚的英语凑成一个看起来功能很丰富,维护人员很多,很正规的[一个文档网站](https://actionsflow.github.io/docs/reference/)。我只在项目的[Readme 文件](https://github.com/denoflow/denoflow)里写了一个我自认为很诚恳的说明,在一页的文本里说清楚这个项目的用途,和看一个示例就能明白的使用方法,没有多余的营销话语,把时间真正花在工具本身,利用省下的时间做了一个[在线 PlayGround](https://playground.owenyoung.com/),运行在我的廉价
76
+ VPS 上。
77
+ 3. 没考虑买一个域名,尽管`denoflow.com`域名还在。直接放在我的个人子域`playground.owenyoung.com`.
78
+ 4. 版本号的克制,对于刚推出的项目,我使用`0.0.x`,而不是像 Actionsflow
79
+ 一样,一上来就是`1.0.0`。而后来的证明,`0.0.x`是最适合这个项目的,因为在后来的使用中,发现了不少的致命 bug
80
+ 5. 在文档的显眼处声明:`项目仍处于非常早期的阶段,谨慎使用!`
81
+ 6. 仔细思考了项目的 License,选择了 Apache2.0
82
+
83
+ 做完之后,又花了 2 个小时,用 Denoflow
84
+ 做了[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)对比下,没有追踪,没有
85
+ JS
86
+ 代码,只是使用一个舒服的背景色,使用[class less 原则](https://github.com/dbohdan/classless-css),同时生成一个
87
+ RSS。没有使用任何框架,就几句简单的 deno
88
+ 代码即可生成。和之前的[HN 热门](https://hn.buzzing.cc)相比,[HN 热门](https://hn.buzzing.cc)在一个屏幕里只能显示出
89
+ 2 篇文章,而现在[HN 首页](https://hackernews.buzzing.cc/)可以放 20 多篇,效率直接提升 10 倍啊有木有。
90
+
91
+ 很快,我发现按照这样布局的话,阅读效率高了太多了,已有的这些时间流,已经不够我看了,所以索性单独为我自己生成一个[HN 时间流](https://myfeed.owenyoung.com/),里面包含了
92
+ hn 上前 100 位用户提交的任何帖子。
93
+
94
+ 几天之后我就发现,之前只看 HN 的热门帖子损失有多大,HN
95
+ 上有太多优秀的内容没有被顶上去了。热门真的很随机,所以我们也要万分警惕大公司的算法机制。基于此,我又做了一个变化,直接抓取 HN 上最新的提交(我去掉了 Ask
96
+ HN 的内容,因为 Ask HN
97
+ 的质量相对较差,并且我还有专门的[Ask HN](https://ask.buzzing.cc)可以回头再看)。那么现在专属于我的[HN 时间流](https://myfeed.owenyoung.com/)就包括了
98
+ HN 上所有最新的文章了。即使是全部文章,由于是母语阅读,我还是能快速读完这些标题,找到感兴趣的内容,还可能留下我的 2 cents.
99
+ 把之前关注了很久的不少优质英文 RSS
100
+ 源,也利于[Denoflow](https://github.com/denoflow/denoflow)生成了一个专属于我的[RSS 翻译流](https://myrss.owenyoung.com/).
101
+ 这样下来我的空余时间就可以住要花在阅读长文章,而不是刷推特这样更短的文章。我们都知道推特其实无法代替长文章,就像长文章也无法代替读书一样。
102
+
103
+ 如果有人看到这里的话,那你一定会觉得 HN 有毒,而我已经上瘾了。其实没错,目前阶段是这样。
104
+
105
+ 好在 HN 上的人也经常推荐图书,比如以下两个链接就是 HN 的用户制作的 HN 评论大数据最推荐的书(HN 的评论比图书网站,或者第三方网站更可信):
106
+
107
+ - [汇总了 hacker news 上评论里提到的书籍的排名](https://hacker-recommended-books.vercel.app/)
108
+ - [又一个 hacker news 提到的图书周报](https://hackernewsbooks.com/top-books-on-hacker-news)
109
+
110
+ 比如我读了上面推荐的[《深度工作》](https://d.buzzing.cc/post/1),作者提到:
111
+
112
+ > “如果在你全部的清醒时间,都能给自己的大脑找到有意义的事情去做,而不是放任自己在迷糊的状态下漫无目的地浏览几个小时网页,那么在一天结束时,你会觉得更加充实,第二天开始时更加轻松。
113
+ > – 卡尔·纽波特《深度工作》
114
+
115
+ 从我沉迷 HN
116
+ 的例子里就能看出,网络已经剥夺了我保持专注和沉思的能力,但是真正值得探索和学习的知识都需要我拥有这种能力。如果我没有在某一个特定时段给自己安排任务,那么这些网站总是会更有诱惑力。所以如果我想抵御社交网络对我时间和精力的诱惑,那么必须要给大脑找一些高质量的替代活动,所以我基于开源
117
+ [Lemmy](https://lemmy.ml/) 搭建了一个类似 Reddit
118
+ 的社区,叫[**如何度过每一天**](https://d.buzzing.cc/),希望能在社区里分享有意义的事,不虚度光阴的事,可以是阅读某本书,学习某项技能,看某部纪录片等等等等。社区有投票的功能,某项活动被投票的次数越多,说明认为这项活动有意义的更多。
119
+
120
+ 目前其实就只有少数几个用户在用,网站依然是部署在我的一个 廉价 VPS 上,没有盈利的打算,目前加载也有点慢,但是够用。
121
+
122
+ 以上是我在 Hacker News 上学到的一些东西。
123
+ ','<p>昨天看到 Hacker News 的创始人 Paul Graham 在 2009<br>年写的一篇文章<a href="http://www.paulgraham.com/hackernews.html">《我从 Hacker News 中学到了什么》</a>,<br>作者提到:</p>
128
124
  <blockquote>
129
- <p><strong>Jant</strong> = Jantelagen (Law of Jante)<br>Low-key, de-socialized personal expression.</p>
125
+ <p>随着 Hacker News 变得越来越大,占用的时间比我预期的要多,但我并不后悔,因为我从工作中学到了很多东西。</p>
130
126
  </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>
127
+ <p>就挺感慨的,一方面是我亲自做了一遍 HN 的中文翻译,在整个过程中学到了很多,另一方面也让我回想起大学期间做的一个社区,对我的之后的种种影响。</p>
128
+ <p>如果有机会的话,我觉得每个人都应该尽可能的去亲自参与创造某个能有其他人一起创造信息的社区,比如成立一个小组,一个论坛,一个社群等等,线下的可能更好。和人交流,能碰撞出我更多的想法,以及让我知道更大的世界,更多元的价值观。</p>
129
+ <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>
130
+ <ol>
131
+ <li>抱着锻炼自己英语的想法逼迫自己不太流利的浏览 HN</li>
132
+ <li>使用浏览器自带的翻译功能,浏览 HN</li>
133
+ </ol>
134
+ <p>这两个阶段我都经历过。我希望你不要经历,因为每个阶段都可能使你错过 HN,错过一个很优质的信息源。</p>
135
+ <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>
136
+ <p>第二个阶段,我发现浏览器自带的翻译功能并不是太好用(去年我开始做 hn<br>中文版的时候更不好用,现在其实好了很多),比如有一些网页它会失败,要么就是要重试,然后排版也不太好看,我更希望的是有些区域不要翻译,只翻译标题之类的,但是浏览器会全部翻译,就不太好浏览。而作为一个要长期阅读的地方,体验还是挺重要的。</p>
137
+ <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>
138
+ <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>
139
+ <p>这一次,产生了一些变化,在泡了一年自己做的 HN 中文版之后:</p>
140
+ <ol>
141
+ <li>首先我没有像 Actionsflow 一样设计一个浮夸的<a href="https://actionsflow.github.io/">Landing 页</a>.</li>
142
+ <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>
143
+ <li>没考虑买一个域名,尽管<code>denoflow.com</code>域名还在。直接放在我的个人子域<code>playground.owenyoung.com</code>.</li>
144
+ <li>版本号的克制,对于刚推出的项目,我使用<code>0.0.x</code>,而不是像 Actionsflow<br>一样,一上来就是<code>1.0.0</code>。而后来的证明,<code>0.0.x</code>是最适合这个项目的,因为在后来的使用中,发现了不少的致命 bug</li>
145
+ <li>在文档的显眼处声明:<code>项目仍处于非常早期的阶段,谨慎使用!</code></li>
146
+ <li>仔细思考了项目的 License,选择了 Apache2.0</li>
147
+ </ol>
148
+ <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>
149
+ <p>很快,我发现按照这样布局的话,阅读效率高了太多了,已有的这些时间流,已经不够我看了,所以索性单独为我自己生成一个<a href="https://myfeed.owenyoung.com/">HN 时间流</a>,里面包含了<br>hn 上前 100 位用户提交的任何帖子。</p>
150
+ <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>
151
+ <p>如果有人看到这里的话,那你一定会觉得 HN 有毒,而我已经上瘾了。其实没错,目前阶段是这样。</p>
152
+ <p>好在 HN 上的人也经常推荐图书,比如以下两个链接就是 HN 的用户制作的 HN 评论大数据最推荐的书(HN 的评论比图书网站,或者第三方网站更可信):</p>
134
153
  <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>
154
+ <li><a href="https://hacker-recommended-books.vercel.app/">汇总了 hacker news 上评论里提到的书籍的排名</a></li>
155
+ <li><a href="https://hackernewsbooks.com/top-books-on-hacker-news">又一个 hacker news 提到的图书周报</a></li>
140
156
  </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);
157
+ <p>比如我读了上面推荐的<a href="https://d.buzzing.cc/post/1">《深度工作》</a>,作者提到:</p>
158
+ <blockquote>
159
+ <p>“如果在你全部的清醒时间,都能给自己的大脑找到有意义的事情去做,而不是放任自己在迷糊的状态下漫无目的地浏览几个小时网页,那么在一天结束时,你会觉得更加充实,第二天开始时更加轻松。<br>– 卡尔·纽波特《深度工作》</p>
160
+ </blockquote>
161
+ <p>从我沉迷 HN<br>的例子里就能看出,网络已经剥夺了我保持专注和沉思的能力,但是真正值得探索和学习的知识都需要我拥有这种能力。如果我没有在某一个特定时段给自己安排任务,那么这些网站总是会更有诱惑力。所以如果我想抵御社交网络对我时间和精力的诱惑,那么必须要给大脑找一些高质量的替代活动,所以我基于开源<br><a href="https://lemmy.ml/">Lemmy</a> 搭建了一个类似 Reddit<br>的社区,叫<a href="https://d.buzzing.cc/"><strong>如何度过每一天</strong></a>,希望能在社区里分享有意义的事,不虚度光阴的事,可以是阅读某本书,学习某项技能,看某部纪录片等等等等。社区有投票的功能,某项活动被投票的次数越多,说明认为这项活动有意义的更多。</p>
162
+ <p>目前其实就只有少数几个用户在用,网站依然是部署在我的一个 廉价 VPS 上,没有盈利的打算,目前加载也有点慢,但是够用。</p>
163
+ <p>以上是我在 Hacker News 上学到的一些东西。</p>
164
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771115051,1771115051,1771115051);
165
+ INSERT INTO posts VALUES(8,'quote','quiet',NULL,NULL,'拥有五万个从不打开邮件的订阅者名单,其价值远不及五百个逐字阅读并偶尔给予深度回复的读者。后者会购买你的著作,向朋友推荐你,并长久陪伴你。前者不过是你在那些无聊的数字炫耀派对上能拿出来吹嘘的数字而已。','<p>拥有五万个从不打开邮件的订阅者名单,其价值远不及五百个逐字阅读并偶尔给予深度回复的读者。后者会购买你的著作,向朋友推荐你,并长久陪伴你。前者不过是你在那些无聊的数字炫耀派对上能拿出来吹嘘的数字而已。</p>
166
+ ','https://www.joanwestenberg.com/why-my-newsletter-costs-2-50/',NULL,'www.joanwestenberg.com',NULL,NULL,NULL,1771115103,1771115103,1771115103);
167
+ INSERT INTO posts VALUES(9,'quote','featured',NULL,NULL,'弗吉尼亚·伍尔夫曾论述拥有独立空间的重要性:那是供人进行创作的物理场所,不受干扰与控制。博客便是网络世界中的独立空间。在这里,你自主决定书写内容与表达方式,无需屈从于平台算法的任性摆布——那些平台只顾榨取你的参与度,却不关心这种参与究竟是否让你或他人变得更聪明。
168
+
169
+ 我们不会通过等待平台减少剥削来获得更好的互联网。我们需要亲手构建它——维护自己的空间,相互链接,打造独立网站的互联网络,让博客圈重现昔日的辉煌。','<p>弗吉尼亚·伍尔夫曾论述拥有独立空间的重要性:那是供人进行创作的物理场所,不受干扰与控制。博客便是网络世界中的独立空间。在这里,你自主决定书写内容与表达方式,无需屈从于平台算法的任性摆布——那些平台只顾榨取你的参与度,却不关心这种参与究竟是否让你或他人变得更聪明。</p>
170
+ <p>我们不会通过等待平台减少剥削来获得更好的互联网。我们需要亲手构建它——维护自己的空间,相互链接,打造独立网站的互联网络,让博客圈重现昔日的辉煌。</p>
171
+ ','https://www.joanwestenberg.com/the-case-for-blogging-in-the-ruins/','JA Westenberg','www.joanwestenberg.com',NULL,NULL,NULL,1771115159,1771115159,1771115177);
172
+ INSERT INTO posts VALUES(10,'link','quiet','Some pics',NULL,'一个干净的图片流网站
173
+ ','<p>一个干净的图片流网站</p>
174
+ ','https://some.pics/',NULL,'some.pics',NULL,NULL,NULL,1771115271,1771115271,1771115271);
175
+ INSERT INTO posts VALUES(11,'image','quiet',NULL,NULL,'From upsplash','<p>From upsplash</p>
176
+ ',NULL,NULL,NULL,NULL,NULL,NULL,1771123457,1771123457,1771123457);
188
177
 
189
178
  -- 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');
179
+ INSERT INTO media VALUES('019c5f2e-6d5f-74f3-a521-d4fbac0b4ac3',NULL,'019c5f2e-6d5f-74f3-a521-d4fbac0b4ac3.webp','brianna-parks-frXRtKYPqXk-unsplash.webp','image/webp',264554,'media/2026/02/019c5f2e-6d5f-74f3-a521-d4fbac0b4ac3.webp',NULL,NULL,NULL,1771123404,0,NULL,'r2');
180
+ INSERT INTO media VALUES('019c5f2e-7dea-729c-a260-6c006faf3384',11,'019c5f2e-7dea-729c-a260-6c006faf3384.webp','land-o-lakes-inc-peEOQ4_dqOo-unsplash.webp','image/webp',470774,'media/2026/02/019c5f2e-7dea-729c-a260-6c006faf3384.webp',NULL,NULL,NULL,1771123408,1,NULL,'r2');
181
+ INSERT INTO media VALUES('019c5f2e-8a17-77dc-a91c-2d51f14a622c',NULL,'019c5f2e-8a17-77dc-a91c-2d51f14a622c.webp','museum-of-new-zealand-te-papa-tongarewa-1C7oRRwUkgU-unsplash.webp','image/webp',322656,'media/2026/02/019c5f2e-8a17-77dc-a91c-2d51f14a622c.webp',NULL,NULL,NULL,1771123411,0,NULL,'r2');
182
+ INSERT INTO media VALUES('019c5f2e-96d2-7e56-801a-6073bd6183f6',11,'019c5f2e-96d2-7e56-801a-6073bd6183f6.webp','takashi-sakamoto-J9nW9Tc1HVc-unsplash.webp','image/webp',505172,'media/2026/02/019c5f2e-96d2-7e56-801a-6073bd6183f6.webp',NULL,NULL,NULL,1771123415,0,NULL,'r2');