fix(editor): tighten template copy that wrapped into orphan lines

A CJK line-break gate over all fifty-eight templates caught eleven
sets whose body copy wrapped into three-character orphan tails or
punctuation-led lines. Each line is shortened to fit its measured
column (paired sides stay symmetric where the layout demands it),
affected root heights re-measured, previews re-baked. The one
remaining gate report is a simulator false positive disproven by the
rendered frame.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
This commit is contained in:
Fini 2026-08-09 02:18:04 +08:00
parent 3a38aa9638
commit 6fd0b343a1
37 changed files with 45 additions and 48 deletions

View file

@ -335,7 +335,7 @@ summary_fallback = "竖版信息长图:一条贯穿全图的时间轴,年份
tags = ["信息图", "长图", "时间线", "历程", "复盘", "大事记", "infographic", "timeline", "milestone", "history"]
frames = 1
frame_width = 1080
frame_height = 3110
frame_height = 3018
[[template]]
id = "concept-contrast-infographic"
@ -347,7 +347,7 @@ summary_fallback = "竖版信息长图:先给结论,再给两个概念各自
tags = ["信息图", "长图", "对比", "概念", "科普", "辨析", "infographic", "contrast", "concept", "explainer"]
frames = 1
frame_width = 1080
frame_height = 3106
frame_height = 3060
[[template]]
id = "ranking-board-infographic"
@ -371,7 +371,7 @@ summary_fallback = "竖版信息长图:六组一问一答,Q 实心 A 描边
tags = ["信息图", "长图", "问答", "FAQ", "答疑", "常见问题", "infographic", "faq", "qa", "questions"]
frames = 1
frame_width = 1080
frame_height = 2881
frame_height = 2789
[[template]]
id = "data-story-infographic"

View file

@ -549,7 +549,7 @@
"type": "text",
"id": "t27",
"name": "屏说明",
"content": "只留一个按钮,别让人在引导页做选择。",
"content": "只留一个按钮,别让人挑。",
"fontFamily": "Noto Sans SC",
"fontSize": 21,
"fontWeight": 400,

View file

@ -403,7 +403,7 @@
"type": "text",
"id": "t13",
"name": "规则说明",
"content": "在格子上划一笔。不补昨天的,也不提前划明天的。",
"content": "不补昨天不提前",
"fontFamily": "Noto Sans SC",
"fontSize": 25,
"fontWeight": 400,
@ -487,7 +487,7 @@
"type": "text",
"id": "t17",
"name": "规则说明",
"content": "漏一天就接着往下走。回到第一天是最常见的放弃方式。",
"content": "漏一天就往下走,别从第一天重来。",
"fontFamily": "Noto Sans SC",
"fontSize": 25,
"fontWeight": 400,

View file

@ -53,7 +53,7 @@
"id": "f1",
"name": "概念对比科普长图",
"width": 1080,
"height": 3106,
"height": 3060,
"layout": "vertical",
"gap": 0,
"fill": [
@ -788,7 +788,7 @@
"type": "text",
"id": "t34",
"name": "A 侧",
"content": "低频、连续:引擎、空调、地铁。",
"content": "低频连续:引擎、空调、地铁",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,
@ -820,7 +820,7 @@
"type": "text",
"id": "t36",
"name": "B 侧",
"content": "高频、突发:键盘、餐具、人声。",
"content": "高频突发:键盘、餐具、人声",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,
@ -1006,7 +1006,7 @@
"type": "text",
"id": "t48",
"name": "A 侧",
"content": "开关按一次,轰鸣感明显变小才算真降噪。",
"content": "按一次开关,轰鸣明显变小。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,

View file

@ -1765,7 +1765,7 @@
"type": "text",
"id": "t89",
"name": "步骤说明",
"content": "3 mm 钻头打穿,直接上螺丝木条会裂。",
"content": "先打 3 mm 孔,直接上螺丝会裂。",
"fontFamily": "Noto Sans SC",
"fontSize": 23,
"fontWeight": 400,

View file

@ -746,7 +746,7 @@
"type": "text",
"id": "t39",
"name": "示例标题",
"content": "AI 工具,我只留了三个",
"content": "AI 工具只留三个",
"fontFamily": "Noto Sans SC",
"fontSize": 34,
"fontWeight": 700,

View file

@ -53,7 +53,7 @@
"id": "f1",
"name": "问答 FAQ 长图",
"width": 1080,
"height": 2881,
"height": 2789,
"layout": "vertical",
"gap": 0,
"fill": [
@ -422,7 +422,7 @@
"type": "text",
"id": "t13",
"name": "答句",
"content": "先定一个与播放量无关的目标:连发十期。第十期之后再决定,不要在第三期决定。",
"content": "先定一个与播放量无关的目标:连发十期,第十期之后再决定。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,
@ -989,7 +989,7 @@
"type": "text",
"id": "t43",
"name": "答句",
"content": "封面决定有没有人点进来,内容决定下次还点不点。前十期先把内容做稳,封面之后再补。",
"content": "封面决定点不点进来,内容决定下次还点不点。前十期先做内容。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,

View file

@ -935,7 +935,7 @@
"type": "text",
"id": "t48",
"name": "论点阐述",
"content": "想让一句话被看见,办法不是把它放大,而是把它周围清空。四周留白足够时,30px 的字也会比挤在一堆里的 60px 更醒目。别用字号硬撑,先把周围清干净。",
"content": "想让一句话被看见,办法不是把它放大,而是把它周围清空。留白足够时,30px 的字也比挤成一堆的 60px 更醒目。",
"fontFamily": "Noto Sans SC",
"fontSize": 34,
"fontWeight": 400,

View file

@ -45,7 +45,7 @@
"id": "f1",
"name": "时间线大事记长图",
"width": 1080,
"height": 3110,
"height": 3018,
"layout": "vertical",
"gap": 0,
"fill": [
@ -1028,7 +1028,7 @@
"type": "text",
"id": "t47",
"name": "事件说明",
"content": "同一个版式做到第八次才存模板。回头看,第二次就该存。",
"content": "同一个版式做到第八次才存模板。第二次就该存。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,
@ -1781,7 +1781,7 @@
"type": "text",
"id": "t94",
"name": "待续说明",
"content": "把年份和事件换掉即可,行数不够就照着复制一行 —— 轴会自己接上。",
"content": "换掉年份和事件即可,复制一行轴会自己接上。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,

View file

@ -965,7 +965,7 @@
"type": "text",
"id": "t43",
"name": "卖点说明",
"content": "上传图纸,系统直接算出工时、材料与损耗。",
"content": "上传图纸,直接算出工时与材料。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,
@ -1050,7 +1050,7 @@
"type": "text",
"id": "t47",
"name": "卖点说明",
"content": "每一档价格旁边就是毛利率,接不接一眼决定。",
"content": "价格旁就是毛利率,接不接一眼定。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,
@ -1135,7 +1135,7 @@
"type": "text",
"id": "t51",
"name": "卖点说明",
"content": "每完成一单回流真实成本,模型自己往回修。",
"content": "每完成一单回流成本,模型自己修。",
"fontFamily": "Noto Sans SC",
"fontSize": 27,
"fontWeight": 400,

View file

@ -1096,7 +1096,7 @@
"type": "text",
"id": "t59",
"name": "要点说明",
"content": "关键路径的失败率下降到千分之一以内。",
"content": "失败率降到千分之一以内。",
"fontFamily": "Inter",
"fontSize": 28,
"fontWeight": 400,
@ -1189,7 +1189,7 @@
"type": "text",
"id": "t64",
"name": "要点说明",
"content": "同样的任务量,只用原来一半的算力预算。",
"content": "同样的任务量,只用一半算力。",
"fontFamily": "Inter",
"fontSize": 28,
"fontWeight": 400,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View file

@ -30,8 +30,7 @@ POINTS = [
"讲不完,就拆下一页。"),
("留白比字号更重要",
"想让一句话被看见,办法不是把它放大,而是把它周围清空。"
"四周留白足够时,30px 的字也会比挤在一堆里的 60px 更醒目。"
"别用字号硬撑,先把周围清干净。",
"留白足够时,30px 的字也比挤成一堆的 60px 更醒目。",
"想突出,先清空周围。"),
]

View file

@ -99,7 +99,7 @@ LH_DISPLAY, LH_HEAD, LH_BODY = 1.2, 1.3, 1.7
# 左栏(错)与右栏(对)的示例封面标题。两句必须是**同一个选题**的两种写
# 法,否则读者比的是内容不是写法。
BAD_TITLE = "关于如何高效使用 AI 工具的一些方法分享"
GOOD_TITLE = "AI 工具,我只留了三个"
GOOD_TITLE = "AI 工具只留三个"
# 每栏三条判据,逐条一一对应(第 n 条对第 n 条),≤14 字。
BAD_POINTS = [

View file

@ -110,9 +110,9 @@ ROOT_H = 2692
RULES = [
("固定同一个时间", "哪怕只做五分钟。时间固定了,要不要做就不用每天重想。",
"alarm-clock"),
("做完立刻划掉", "在格子上划一笔。不补昨天的,也不提前划明天的。",
("做完立刻划掉", "不补昨天不提前",
"check"),
("断了不重来", "漏一天就接着往下走。回到第一天是最常见的放弃方式。",
("断了不重来", "漏一天就往下走,别从第一天重来。",
"refresh-cw"),
]

View file

@ -99,7 +99,7 @@ CELL_GAP = 26
CELL_W = (INNER - CARD_PAD * 2 - RULE_W - CELL_GAP * 2) // 2
# 量出来的根高(做法同同档另外四张:根设 fit_content 渲一次读 PNG 高度)。
ROOT_H = 3106
ROOT_H = 3060
SIDE_A = ("主动降噪", "ANC", "waves",
"麦克风先录下环境噪声,喇叭再放一段反相声波把它抵消掉。")
@ -109,13 +109,13 @@ SIDE_B = ("被动隔音", "Passive", "shield",
# (维度, A 侧一句话, B 侧一句话)
DIMENSIONS = [
("擅长哪种噪声",
"低频、连续:引擎、空调、地铁。",
"高频、突发:键盘、餐具、人声。"),
"低频连续:引擎、空调、地铁",
"高频突发:键盘、餐具、人声"),
("代价是什么",
"要电、有轻微底噪,一部分人戴着会耳压不适。",
"要压耳,戴久了会闷热夹头,夏天尤其明显。"),
("怎么当场验证",
"开关按一次,轰鸣感明显变小才算真降噪。",
"按一次开关,轰鸣明显变小。",
"先别开电源,戴上安静下来的那部分就是隔音。"),
]

View file

@ -94,21 +94,19 @@ LH_DISPLAY, LH_HEAD, LH_BODY = 1.2, 1.3, 1.7
MARK = 46
# 量出来的根高(做法同同档另外六张:根设 fit_content 渲一次读 PNG 高度)。
ROOT_H = 2881
ROOT_H = 2789
# (问, 答)。问句用读者的口吻,答句必须给出可执行的判断标准。
QA = [
("没人看还要不要继续发?",
"先定一个与播放量无关的目标:连发十期。第十期之后再决定,"
"不要在第三期决定。"),
"先定一个与播放量无关的目标:连发十期,第十期之后再决定。"),
("多久发一次合适?",
"选一个你在最忙的那一周也能做到的频率。做不到的频率会让你在第五周"
"直接停掉。"),
("要不要先攒够素材再开始?",
"不要。攒素材是最舒服的一种拖延,发出去之后才知道该攒什么。"),
("封面和内容哪个更重要?",
"封面决定有没有人点进来,内容决定下次还点不点。前十期先把内容做稳,"
"封面之后再补。"),
"封面决定点不点进来,内容决定下次还点不点。前十期先做内容。"),
("被说得不好听怎么办?",
"只回带具体建议的那一条,其余不回。回一条要花掉二十分钟,"
"值不值这个时间自己算。"),

View file

@ -100,7 +100,7 @@ DOT_BOX_H = 46
DOT_D = 22
# 量出来的根高(做法同另外三张:根设 fit_content 渲一次读 PNG 高度)。
ROOT_H = 3110
ROOT_H = 3018
# (年月, 标签, 那时做了什么, 之后改了什么做法)
EVENTS = [
@ -111,7 +111,7 @@ EVENTS = [
("2021.06", "第一次被看见", "一篇只回答一个问题",
"放弃「什么都写一点」,一篇讲清一件事,转发的人立刻多起来。"),
("2022.02", "开始省时间", "把重复的版式存成模板",
"同一个版式做到第八次才存模板。回头看,第二次就该存。"),
"同一个版式做到第八次才存模板。第二次就该存。"),
("2023.05", "第一笔收入", "先做出来,再定价",
"免费发满 40 期之后才收费,报价按自己做一期的实际时长算。"),
("2024.09", "停更三个月", "停之前先发一条说明",
@ -241,7 +241,7 @@ def next_row():
text(ids, "待续标题", "这条线可以换成你自己的", 34, 600, "$c-ink",
family=CJK, line_height=LH_HEAD),
text(ids, "待续说明",
"把年份和事件换掉即可,行数不够就照着复制一行 —— 轴会自己接上。",
"换掉年份和事件即可,复制一行轴会自己接上。",
27, 400, "$c-muted", family=CJK, line_height=LH_BODY),
], gap=12, padding=[26, 30], cornerRadius=20)
card["fill"] = []

View file

@ -243,9 +243,9 @@ def problem():
# ------------------------------------------------------------------ 03 方案
SELLING_POINTS = [
("三分钟出价", "上传图纸,系统直接算出工时、材料与损耗。"),
("毛利先看见", "每一档价格旁边就是毛利率,接不接一眼决定。"),
("越报越准", "每完成一单回流真实成本,模型自己往回修。"),
("三分钟出价", "上传图纸,直接算出工时与材料。"),
("毛利先看见", "价格旁就是毛利率,接不接一眼定。"),
("越报越准", "每完成一单回流成本,模型自己修。"),
]

View file

@ -215,8 +215,8 @@ def agenda():
# ------------------------------------------------------------------ 03 要点
POINTS = [
("更快", "把等待时间从 8 秒压到 1.2 秒,用户不再中途离开。"),
("更稳", "关键路径的失败率下降到千分之一以内。"),
("更省", "同样的任务量,只用原来一半的算力预算。"),
("更稳", "失败率降到千分之一以内。"),
("更省", "同样的任务量,只用一半算力。"),
]

View file

@ -101,7 +101,7 @@ STEPS = [
("01", "量好再画线", "四根木条都量成 60 cm,用角尺画满一圈再锯。",
"画线特写"),
("02", "锯口留在线外", "锯片压着线的外侧走,锯完刚好到线。", "锯切中"),
("03", "先打引导孔", "3 mm 钻头打穿,直接上螺丝木条会裂。", "钻孔位置"),
("03", "先打引导孔", "先打 3 mm 孔,直接上螺丝会裂。", "钻孔位置"),
("04", "拧螺丝固定框", "对角线交替拧,一次只拧到八成紧。", "拧螺丝"),
("05", "上面板", "胶合板压在框上,四边各两颗螺丝。", "面板压合"),
("06", "打磨上油", "180 目磨平,320 目收光,再薄涂两遍油。", "打磨中"),

View file

@ -84,7 +84,7 @@ SCREEN_H = 396
# (序号, 一句标题 ≤10 字, 一句说明 ≤22 字, 图位提示)
SCREENS = [
("1", "先说清你是谁", "一句话讲明白这个 App 解决什么问题。", "第 1 屏截图"),
("2", "再给一个动作", "只留一个按钮,别让人在引导页做选择。", "第 2 屏截图"),
("2", "再给一个动作", "只留一个按钮,别让人挑。", "第 2 屏截图"),
("3", "最后请求权限", "在真正要用到的那一刻才要,通过率高得多。",
"第 3 屏截图"),
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 KiB

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 KiB

After

Width:  |  Height:  |  Size: 773 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 KiB

After

Width:  |  Height:  |  Size: 711 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 740 KiB

After

Width:  |  Height:  |  Size: 708 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 KiB

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 791 KiB

After

Width:  |  Height:  |  Size: 769 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 KiB

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 196 KiB