@deernova/style 0.1.0 → 0.3.0
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/README.md +8 -6
- package/bin/cli.mjs +212 -29
- package/package.json +3 -4
- package/skill/SKILL.md +91 -21
- package/skill/assets/DeernovaTokens.swift +376 -0
- package/skill/assets/screens/iphone-button-sizes-dark.png +0 -0
- package/skill/assets/screens/iphone-button-sizes-light.png +0 -0
- package/skill/assets/screens/iphone-screens-dark.png +0 -0
- package/skill/assets/screens/iphone-screens-light.png +0 -0
- package/skill/assets/screens/mac-window-dark.png +0 -0
- package/skill/assets/screens/mac-window-light.png +0 -0
- package/skill/assets/tailwind.css +106 -0
- package/skill/assets/tokens.css +533 -0
- package/skill/assets/tokens.json +1686 -0
- package/skill/manifest.json +2 -22
- package/skill/references/components/banner.md +95 -0
- package/skill/references/components/button.md +181 -0
- package/skill/references/components/card.md +89 -0
- package/skill/references/components/code-block.md +77 -0
- package/skill/references/components/composer.md +206 -0
- package/skill/references/components/empty-state.md +63 -0
- package/skill/references/components/glass-controls.md +127 -0
- package/skill/references/components/list.md +130 -0
- package/skill/references/components/message.md +94 -0
- package/skill/references/components/segmented.md +79 -0
- package/skill/references/components/sheet.md +84 -0
- package/skill/references/components/sidebar.md +139 -0
- package/skill/references/components/step-row.md +135 -0
- package/skill/references/components/tag.md +86 -0
- package/skill/references/components/task-list.md +83 -0
- package/skill/references/components/text-field.md +86 -0
- package/skill/references/components/toolbar.md +139 -0
- package/skill/references/foundations/accessibility.md +27 -0
- package/skill/references/foundations/color.md +58 -0
- package/skill/references/foundations/elevation.md +17 -0
- package/skill/references/foundations/glass.md +36 -0
- package/skill/references/foundations/iconography.md +44 -0
- package/skill/references/foundations/motion.md +22 -0
- package/skill/references/foundations/radius.md +31 -0
- package/skill/references/foundations/spacing.md +38 -0
- package/skill/references/foundations/stroke.md +35 -0
- package/skill/references/foundations/typography.md +54 -0
- package/skill/references/foundations/writing.md +10 -0
- package/skill/references/review.md +26 -13
- package/skill/references/scenes/ios.md +61 -0
- package/skill/references/scenes/ipados.md +42 -0
- package/skill/references/scenes/macos.md +65 -0
- package/skill/references/scenes/swiftui.md +94 -0
- package/skill/references/scenes/web-marketing.md +32 -0
- package/skill/references/scenes/web.md +83 -0
- package/skill/references/tokens.md +441 -74
- package/skill/assets/design/README.md +0 -170
- package/skill/assets/design/components/Banner/README.md +0 -33
- package/skill/assets/design/components/Banner/preview.html +0 -26
- package/skill/assets/design/components/Button/README.md +0 -59
- package/skill/assets/design/components/Button/preview.html +0 -27
- package/skill/assets/design/components/Card/README.md +0 -31
- package/skill/assets/design/components/Card/preview.html +0 -24
- package/skill/assets/design/components/CodeBlock/README.md +0 -34
- package/skill/assets/design/components/CodeBlock/preview.html +0 -16
- package/skill/assets/design/components/Composer/README.md +0 -42
- package/skill/assets/design/components/Composer/preview.html +0 -28
- package/skill/assets/design/components/Cover/preview.html +0 -53
- package/skill/assets/design/components/EmptyState/README.md +0 -28
- package/skill/assets/design/components/EmptyState/preview.html +0 -16
- package/skill/assets/design/components/GlassControls/README.md +0 -43
- package/skill/assets/design/components/GlassControls/preview.html +0 -40
- package/skill/assets/design/components/ListGroup/README.md +0 -39
- package/skill/assets/design/components/ListGroup/preview.html +0 -22
- package/skill/assets/design/components/MacWindow/README.md +0 -27
- package/skill/assets/design/components/MacWindow/preview.html +0 -70
- package/skill/assets/design/components/Message/README.md +0 -31
- package/skill/assets/design/components/Message/preview.html +0 -21
- package/skill/assets/design/components/PhoneScreen/README.md +0 -25
- package/skill/assets/design/components/PhoneScreen/preview.html +0 -74
- package/skill/assets/design/components/Segmented/README.md +0 -21
- package/skill/assets/design/components/Segmented/preview.html +0 -21
- package/skill/assets/design/components/Sheet/README.md +0 -29
- package/skill/assets/design/components/Sheet/preview.html +0 -31
- package/skill/assets/design/components/Sidebar/README.md +0 -36
- package/skill/assets/design/components/Sidebar/preview.html +0 -20
- package/skill/assets/design/components/StepRow/README.md +0 -35
- package/skill/assets/design/components/StepRow/preview.html +0 -17
- package/skill/assets/design/components/Tag/README.md +0 -36
- package/skill/assets/design/components/Tag/preview.html +0 -22
- package/skill/assets/design/components/TextField/README.md +0 -29
- package/skill/assets/design/components/TextField/preview.html +0 -28
- package/skill/assets/design/components/bundle.css +0 -189
- package/skill/assets/design/design-system.json +0 -25
- package/skill/assets/design/images/Banner-dark.png +0 -0
- package/skill/assets/design/images/Banner-light.png +0 -0
- package/skill/assets/design/images/Button-dark.png +0 -0
- package/skill/assets/design/images/Button-light.png +0 -0
- package/skill/assets/design/images/Card-dark.png +0 -0
- package/skill/assets/design/images/Card-light.png +0 -0
- package/skill/assets/design/images/CodeBlock-dark.png +0 -0
- package/skill/assets/design/images/CodeBlock-light.png +0 -0
- package/skill/assets/design/images/Composer-dark.png +0 -0
- package/skill/assets/design/images/Composer-light.png +0 -0
- package/skill/assets/design/images/Cover-dark.png +0 -0
- package/skill/assets/design/images/Cover-light.png +0 -0
- package/skill/assets/design/images/EmptyState-dark.png +0 -0
- package/skill/assets/design/images/EmptyState-light.png +0 -0
- package/skill/assets/design/images/Example-iOSButtonsInPhone-dark.png +0 -0
- package/skill/assets/design/images/Example-iOSButtonsInPhone-light.png +0 -0
- package/skill/assets/design/images/GlassControls-dark.png +0 -0
- package/skill/assets/design/images/GlassControls-light.png +0 -0
- package/skill/assets/design/images/ListGroup-dark.png +0 -0
- package/skill/assets/design/images/ListGroup-light.png +0 -0
- package/skill/assets/design/images/MacWindow-dark.png +0 -0
- package/skill/assets/design/images/MacWindow-light.png +0 -0
- package/skill/assets/design/images/Message-dark.png +0 -0
- package/skill/assets/design/images/Message-light.png +0 -0
- package/skill/assets/design/images/PhoneScreen-dark.png +0 -0
- package/skill/assets/design/images/PhoneScreen-light.png +0 -0
- package/skill/assets/design/images/Segmented-dark.png +0 -0
- package/skill/assets/design/images/Segmented-light.png +0 -0
- package/skill/assets/design/images/Sheet-dark.png +0 -0
- package/skill/assets/design/images/Sheet-light.png +0 -0
- package/skill/assets/design/images/Sidebar-dark.png +0 -0
- package/skill/assets/design/images/Sidebar-light.png +0 -0
- package/skill/assets/design/images/StepRow-dark.png +0 -0
- package/skill/assets/design/images/StepRow-light.png +0 -0
- package/skill/assets/design/images/Tag-dark.png +0 -0
- package/skill/assets/design/images/Tag-light.png +0 -0
- package/skill/assets/design/images/TextField-dark.png +0 -0
- package/skill/assets/design/images/TextField-light.png +0 -0
- package/skill/assets/design/tokens.css +0 -187
- package/skill/assets/design/tokens.json +0 -142
- package/skill/references/components.md +0 -648
- package/skill/references/foundations.md +0 -170
- package/skill/references/native.md +0 -7
- package/skill/references/web.md +0 -11
- package/template/app.js +0 -7
- package/template/index.html +0 -1290
- package/template/manifest.json +0 -24
- package/template/project/README.md +0 -170
- package/template/project/components/Banner/README.md +0 -33
- package/template/project/components/Banner/preview.html +0 -26
- package/template/project/components/Button/README.md +0 -59
- package/template/project/components/Button/preview.html +0 -27
- package/template/project/components/Card/README.md +0 -31
- package/template/project/components/Card/preview.html +0 -24
- package/template/project/components/CodeBlock/README.md +0 -34
- package/template/project/components/CodeBlock/preview.html +0 -16
- package/template/project/components/Composer/README.md +0 -42
- package/template/project/components/Composer/preview.html +0 -28
- package/template/project/components/Cover/preview.html +0 -53
- package/template/project/components/EmptyState/README.md +0 -28
- package/template/project/components/EmptyState/preview.html +0 -16
- package/template/project/components/GlassControls/README.md +0 -43
- package/template/project/components/GlassControls/preview.html +0 -40
- package/template/project/components/ListGroup/README.md +0 -39
- package/template/project/components/ListGroup/preview.html +0 -22
- package/template/project/components/MacWindow/README.md +0 -27
- package/template/project/components/MacWindow/preview.html +0 -70
- package/template/project/components/Message/README.md +0 -31
- package/template/project/components/Message/preview.html +0 -21
- package/template/project/components/PhoneScreen/README.md +0 -25
- package/template/project/components/PhoneScreen/preview.html +0 -74
- package/template/project/components/Segmented/README.md +0 -21
- package/template/project/components/Segmented/preview.html +0 -21
- package/template/project/components/Sheet/README.md +0 -29
- package/template/project/components/Sheet/preview.html +0 -31
- package/template/project/components/Sidebar/README.md +0 -36
- package/template/project/components/Sidebar/preview.html +0 -20
- package/template/project/components/StepRow/README.md +0 -35
- package/template/project/components/StepRow/preview.html +0 -17
- package/template/project/components/Tag/README.md +0 -36
- package/template/project/components/Tag/preview.html +0 -22
- package/template/project/components/TextField/README.md +0 -29
- package/template/project/components/TextField/preview.html +0 -28
- package/template/project/components/bundle.css +0 -189
- package/template/project/design-system.json +0 -25
- package/template/project/images/Banner-dark.png +0 -0
- package/template/project/images/Banner-light.png +0 -0
- package/template/project/images/Button-dark.png +0 -0
- package/template/project/images/Button-light.png +0 -0
- package/template/project/images/Card-dark.png +0 -0
- package/template/project/images/Card-light.png +0 -0
- package/template/project/images/CodeBlock-dark.png +0 -0
- package/template/project/images/CodeBlock-light.png +0 -0
- package/template/project/images/Composer-dark.png +0 -0
- package/template/project/images/Composer-light.png +0 -0
- package/template/project/images/Cover-dark.png +0 -0
- package/template/project/images/Cover-light.png +0 -0
- package/template/project/images/EmptyState-dark.png +0 -0
- package/template/project/images/EmptyState-light.png +0 -0
- package/template/project/images/Example-iOSButtonsInPhone-dark.png +0 -0
- package/template/project/images/Example-iOSButtonsInPhone-light.png +0 -0
- package/template/project/images/GlassControls-dark.png +0 -0
- package/template/project/images/GlassControls-light.png +0 -0
- package/template/project/images/ListGroup-dark.png +0 -0
- package/template/project/images/ListGroup-light.png +0 -0
- package/template/project/images/MacWindow-dark.png +0 -0
- package/template/project/images/MacWindow-light.png +0 -0
- package/template/project/images/Message-dark.png +0 -0
- package/template/project/images/Message-light.png +0 -0
- package/template/project/images/PhoneScreen-dark.png +0 -0
- package/template/project/images/PhoneScreen-light.png +0 -0
- package/template/project/images/Segmented-dark.png +0 -0
- package/template/project/images/Segmented-light.png +0 -0
- package/template/project/images/Sheet-dark.png +0 -0
- package/template/project/images/Sheet-light.png +0 -0
- package/template/project/images/Sidebar-dark.png +0 -0
- package/template/project/images/Sidebar-light.png +0 -0
- package/template/project/images/StepRow-dark.png +0 -0
- package/template/project/images/StepRow-light.png +0 -0
- package/template/project/images/Tag-dark.png +0 -0
- package/template/project/images/Tag-light.png +0 -0
- package/template/project/images/TextField-dark.png +0 -0
- package/template/project/images/TextField-light.png +0 -0
- package/template/project/tokens.css +0 -187
- package/template/project/tokens.json +0 -142
- package/template/style.css +0 -1
package/skill/manifest.json
CHANGED
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
3
|
-
"sourceHash": "
|
|
4
|
-
"components": [
|
|
5
|
-
"Banner",
|
|
6
|
-
"Button",
|
|
7
|
-
"Card",
|
|
8
|
-
"CodeBlock",
|
|
9
|
-
"Composer",
|
|
10
|
-
"Cover",
|
|
11
|
-
"EmptyState",
|
|
12
|
-
"GlassControls",
|
|
13
|
-
"ListGroup",
|
|
14
|
-
"MacWindow",
|
|
15
|
-
"Message",
|
|
16
|
-
"PhoneScreen",
|
|
17
|
-
"Segmented",
|
|
18
|
-
"Sheet",
|
|
19
|
-
"Sidebar",
|
|
20
|
-
"StepRow",
|
|
21
|
-
"Tag",
|
|
22
|
-
"TextField"
|
|
23
|
-
]
|
|
2
|
+
"version": "0.3.0",
|
|
3
|
+
"sourceHash": "fe10f84def992286ab1d1fcb7230de8cde61c4973cc3e366603517b0add8cb9d"
|
|
24
4
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# 横幅与轻提示
|
|
2
|
+
|
|
3
|
+
横幅是页面内的提示条,告诉用户一件需要知道的事,必要时附带一个解决动作。轻提示(Toast)是它的短暂版本。
|
|
4
|
+
|
|
5
|
+
**需要提供**:语气(中性 / success / warning / danger)、标题(一句话说明状态)、正文(后果或解决办法)、可选的一个动作按钮;能关闭的横幅还要提供关闭动作。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:banner -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `toast-height` | 36 | 40 | 40 | 轻提示胶囊的高度。 |
|
|
13
|
+
| `banner-radius` | 10 | 12 | 12 | 横幅和贴在输入区上方的提示条的圆角。 |
|
|
14
|
+
<!-- /tokens -->
|
|
15
|
+
|
|
16
|
+
- 中性横幅:`surface-raised` 底、`line` 描边、`ink-secondary` 图标。有语气的横幅:对应的 `-soft` 底、无描边、图标用语气色。文字始终是 `ink` 和 `ink-secondary`。
|
|
17
|
+
- 圆角 `banner-radius`,内边距上下 `space-2`、左右 `space-3`,图标 `icon-size-md`;标题 `type-body` 半粗,正文 `callout`。状态图标见 [图标](../foundations/iconography.md)。
|
|
18
|
+
- 动作按钮用小号次按钮,放在右侧,一个横幅最多一个。
|
|
19
|
+
- 和输入区有关的提示(额度将用完、网络断开)贴在 Composer 正上方,宽度和 Composer 一致,见 [Composer](composer.md)。
|
|
20
|
+
- 轻提示:底部居中的玻璃胶囊,高 `toast-height`,2 秒后自动消失,只用来确认刚刚完成的操作(「已复制」「已保存」),文字用完成态,不放按钮。读屏用 `role="status"`。
|
|
21
|
+
|
|
22
|
+
## Web
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
.dn-banner {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: flex-start;
|
|
28
|
+
gap: var(--space-2);
|
|
29
|
+
padding: var(--space-2) var(--space-3);
|
|
30
|
+
border: var(--stroke-line) solid var(--line);
|
|
31
|
+
border-radius: var(--banner-radius);
|
|
32
|
+
background: var(--surface-raised);
|
|
33
|
+
}
|
|
34
|
+
.dn-banner > .dn-icon { width: var(--icon-size-md); height: var(--icon-size-md); margin-top: calc((var(--type-body-line) - var(--icon-size-md)) / 2); color: var(--ink-secondary); }
|
|
35
|
+
.dn-banner-text { flex: 1; min-width: 0; }
|
|
36
|
+
.dn-banner-title { margin: 0; color: var(--ink); font: var(--weight-semibold) var(--type-body-size)/var(--type-body-line) var(--font-sans); }
|
|
37
|
+
.dn-banner-body { margin: 0; color: var(--ink-secondary); font: var(--type-callout); }
|
|
38
|
+
.dn-banner .dn-button { align-self: center; }
|
|
39
|
+
.dn-banner.is-success { border-color: transparent; background: var(--success-soft); }
|
|
40
|
+
.dn-banner.is-success > .dn-icon { color: var(--success); }
|
|
41
|
+
.dn-banner.is-warning { border-color: transparent; background: var(--warning-soft); }
|
|
42
|
+
.dn-banner.is-warning > .dn-icon { color: var(--warning); }
|
|
43
|
+
.dn-banner.is-danger { border-color: transparent; background: var(--danger-soft); }
|
|
44
|
+
.dn-banner.is-danger > .dn-icon { color: var(--danger); }
|
|
45
|
+
|
|
46
|
+
.dn-toast { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--toast-height); padding: 0 var(--space-4) 0 var(--space-3); color: var(--ink); font: var(--weight-medium) var(--type-body-size)/1 var(--font-sans); }
|
|
47
|
+
.dn-toast .dn-icon { width: var(--icon-size-md); height: var(--icon-size-md); color: var(--success); }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<div class="dn-stack">
|
|
52
|
+
<div class="dn-banner">
|
|
53
|
+
<svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></svg>
|
|
54
|
+
<div class="dn-banner-text"><p class="dn-banner-title">新版本已就绪</p><p class="dn-banner-body">重新打开应用后生效,未保存的内容会自动保留。</p></div>
|
|
55
|
+
<button class="dn-button is-sm" type="button">重新打开</button>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="dn-banner is-warning">
|
|
58
|
+
<svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4l9 16H3z"/><path d="M12 10v4M12 17h.01"/></svg>
|
|
59
|
+
<div class="dn-banner-text"><p class="dn-banner-title">本月 AI 次数还剩 10%</p><p class="dn-banner-body">用完后可以继续写笔记,AI 功能会在 11 月 1 日恢复。</p></div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="dn-banner is-danger">
|
|
62
|
+
<svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M9 9l6 6M15 9l-6 6"/></svg>
|
|
63
|
+
<div class="dn-banner-text"><p class="dn-banner-title">同步失败</p><p class="dn-banner-body">网络连接已断开,恢复后会自动重试。</p></div>
|
|
64
|
+
<button class="dn-button is-sm" type="button">立即重试</button>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="dn-glass dn-toast" role="status"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M8 12.5l2.5 2.5L16 9.5"/></svg>已复制到剪贴板</div>
|
|
67
|
+
</div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## SwiftUI
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
HStack(alignment: .top, spacing: Deernova.space2) {
|
|
74
|
+
Image(systemName: "exclamationmark.triangle.fill").foregroundStyle(.warning)
|
|
75
|
+
VStack(alignment: .leading, spacing: 2) {
|
|
76
|
+
Text("本月 AI 次数还剩 10%").font(.headline)
|
|
77
|
+
Text("用完后可以继续写笔记,AI 功能会在 11 月 1 日恢复。")
|
|
78
|
+
.font(.deernovaCallout).foregroundStyle(.inkSecondary)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
.padding(.vertical, Deernova.space2).padding(.horizontal, Deernova.space3)
|
|
82
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
83
|
+
.background(.warningSoft, in: .rect(cornerRadius: Deernova.bannerRadius))
|
|
84
|
+
|
|
85
|
+
// 轻提示
|
|
86
|
+
Label("已复制到剪贴板", systemImage: "checkmark.circle.fill")
|
|
87
|
+
.padding(.horizontal, Deernova.space4).frame(height: Deernova.toastHeight)
|
|
88
|
+
.glassEffect(in: .capsule)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 不要
|
|
92
|
+
|
|
93
|
+
- 一个页面同时出现多个横幅。
|
|
94
|
+
- 用横幅打广告;错误信息只写「出错了」。
|
|
95
|
+
- 轻提示里放需要用户处理的事情(那应该用横幅或弹窗)。
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# 按钮
|
|
2
|
+
|
|
3
|
+
按钮执行一个动作。每个视图最多一个主按钮(`accent` 填充:浅色主题黑底白字,深色主题白底黑字),用来完成这一屏存在的理由。
|
|
4
|
+
|
|
5
|
+
**需要提供**:标签(动词开头)、可选的图标、点击动作;加载中和禁用状态。
|
|
6
|
+
|
|
7
|
+
## 变体
|
|
8
|
+
|
|
9
|
+
- **主按钮** `is-primary`:`accent` 底、`on-accent` 字。保存、发送、购买这类关键动作,一屏最多一个。
|
|
10
|
+
- **次按钮**(默认):`fill-selected` 灰底、无描边、无阴影、`ink` 字。半透明的灰底放在任何纸面上都成立。
|
|
11
|
+
- **文字按钮** `is-plain`:无底,`ink-secondary` 字,悬停时出现 `fill-hover`。工具栏和卡片里的次要操作。
|
|
12
|
+
- **危险按钮** `is-danger`:`danger-soft` 底、`danger` 字。只有在最终确认弹窗里,才用实色的危险样式(SwiftUI 的 `role: .destructive`)。
|
|
13
|
+
- **图标按钮** `dn-icon-button`:只有图标,无底,`ink-secondary`,必须有无障碍标签。
|
|
14
|
+
|
|
15
|
+
## 尺寸
|
|
16
|
+
|
|
17
|
+
<!-- tokens:button -->
|
|
18
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
19
|
+
| --- | --- | --- | --- | --- |
|
|
20
|
+
| `button-height-sm` | 24 | 30 | 32 | 小按钮的可见高度:列表行、卡片角落里的行内操作。 |
|
|
21
|
+
| `button-height-md` | 28 | 36 | 36 | 默认按钮、图标按钮、发送按钮、步骤行的高度。 |
|
|
22
|
+
| `button-height-lg` | 32 | 46 | 44 | 大按钮:空状态、引导页;触控端是整页唯一的通栏主按钮。 |
|
|
23
|
+
| `button-padding-x-sm` | 8 | 12 | 14 | 小按钮左右内边距。上下内边距永远是 0。 |
|
|
24
|
+
| `button-padding-x-md` | 12 | 16 | 18 | 默认按钮左右内边距。 |
|
|
25
|
+
| `button-padding-x-lg` | 16 | 20 | 24 | 大按钮左右内边距。 |
|
|
26
|
+
| `button-font-size-sm` | 12 | 13 | 13 | 小按钮字号。 |
|
|
27
|
+
| `button-font-size-md` | 14 | 15 | 14 | 默认按钮字号。 |
|
|
28
|
+
| `button-font-size-lg` | 14 | 16 | 15 | 大按钮字号。 |
|
|
29
|
+
| `button-font-weight` | 500 | 600 | 500 | 按钮字重。 |
|
|
30
|
+
| `button-radius` | 6 | 胶囊 | 胶囊 | 默认和大按钮的圆角:指针端是 radius-sm,触控端和官网是胶囊。 |
|
|
31
|
+
| `button-radius-sm` | 4 | 胶囊 | 胶囊 | 小按钮的圆角。 |
|
|
32
|
+
<!-- /tokens -->
|
|
33
|
+
|
|
34
|
+
高度只由 `button-height-*` 决定:**上下内边距为 0,文字行高为 1**。按钮里的文字不能继承页面的行高,否则按钮会跟着页面行高变高。
|
|
35
|
+
|
|
36
|
+
可见高度小于 `hit-min` 时(触控端的 30 和 36),点击区域用透明的命中区补足到 44,可见的按钮不变大。
|
|
37
|
+
|
|
38
|
+
**怎么选尺寸**:按按钮所在的位置定,不随意挑。
|
|
39
|
+
|
|
40
|
+
- 列表行、卡片角落里的行内操作(获取、关注、打开)→ 小号 `is-sm`。
|
|
41
|
+
- 页面和卡片里的普通按钮,包括并排的两个按钮(以后再说 / 开启)→ 默认。同一行里的按钮用同一个尺寸。
|
|
42
|
+
- 空状态、引导页的操作 → 大号 `is-lg`。触控端整页唯一的底部主操作(登录、订阅、开始使用)用大号加 `is-block` 通栏,一屏最多一个。
|
|
43
|
+
- 导航栏、标签栏、工具栏里的按钮 → 用系统控件或[玻璃操作层](glass-controls.md),不自己设尺寸。
|
|
44
|
+
|
|
45
|
+
触控端比指针端大是有意的:手指比鼠标指针粗。三种尺寸放在手机里的样子见 `assets/screens/iphone-button-sizes-light.png`。
|
|
46
|
+
|
|
47
|
+
## 状态
|
|
48
|
+
|
|
49
|
+
- 悬停:次按钮在灰底上再叠一层 `fill-hover`,文字按钮出现 `fill-hover`,主按钮不透明度降到 `opacity-solid-hover`。
|
|
50
|
+
- 焦点:`stroke-focus` 宽的 `focus-ring`,外偏移 `focus-offset`,只在键盘操作(`:focus-visible`)时显示。
|
|
51
|
+
- 禁用:`opacity-disabled`,保持原位,不隐藏。
|
|
52
|
+
- 加载:按钮宽度不变,把标签换成转圈,`aria-busy="true"`。
|
|
53
|
+
|
|
54
|
+
## Web
|
|
55
|
+
|
|
56
|
+
```css
|
|
57
|
+
.dn-button {
|
|
58
|
+
position: relative;
|
|
59
|
+
display: inline-flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
gap: var(--gap-inline);
|
|
63
|
+
height: var(--button-height-md);
|
|
64
|
+
padding: 0 var(--button-padding-x-md); /* 上下永远是 0:高度只由 height 决定 */
|
|
65
|
+
border: 0;
|
|
66
|
+
border-radius: var(--button-radius);
|
|
67
|
+
background: var(--fill-selected);
|
|
68
|
+
color: var(--ink);
|
|
69
|
+
font: var(--button-font-weight) var(--button-font-size-md)/1 var(--font-sans); /* 行高 1,不继承页面行高 */
|
|
70
|
+
white-space: nowrap;
|
|
71
|
+
text-decoration: none;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
transition: background-color var(--duration-instant) var(--ease-standard), opacity var(--duration-instant) var(--ease-standard);
|
|
74
|
+
}
|
|
75
|
+
.dn-button:hover:not(:disabled) { background-image: linear-gradient(var(--fill-hover), var(--fill-hover)); }
|
|
76
|
+
.dn-button:active:not(:disabled) { background-image: linear-gradient(var(--fill-selected), var(--fill-selected)); }
|
|
77
|
+
.dn-button:focus-visible { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
|
78
|
+
.dn-button:disabled { opacity: var(--opacity-disabled); cursor: default; }
|
|
79
|
+
|
|
80
|
+
.dn-button.is-primary { background: var(--accent); color: var(--on-accent); }
|
|
81
|
+
.dn-button.is-primary:hover:not(:disabled), .dn-button.is-primary:active { background-image: none; opacity: var(--opacity-solid-hover); }
|
|
82
|
+
.dn-button.is-plain { background: transparent; color: var(--ink-secondary); }
|
|
83
|
+
.dn-button.is-plain:hover:not(:disabled) { background: var(--fill-hover); color: var(--ink); }
|
|
84
|
+
.dn-button.is-danger { background: var(--danger-soft); color: var(--danger); }
|
|
85
|
+
|
|
86
|
+
.dn-button.is-sm { height: var(--button-height-sm); padding: 0 var(--button-padding-x-sm); border-radius: var(--button-radius-sm); font-size: var(--button-font-size-sm); }
|
|
87
|
+
.dn-button.is-lg { height: var(--button-height-lg); padding: 0 var(--button-padding-x-lg); font-size: var(--button-font-size-lg); }
|
|
88
|
+
.dn-button.is-block { width: 100%; }
|
|
89
|
+
.dn-button .dn-icon { width: var(--icon-size-sm); height: var(--icon-size-sm); }
|
|
90
|
+
|
|
91
|
+
.dn-icon-button {
|
|
92
|
+
position: relative;
|
|
93
|
+
display: inline-grid;
|
|
94
|
+
place-items: center;
|
|
95
|
+
flex: none;
|
|
96
|
+
width: var(--button-height-md);
|
|
97
|
+
height: var(--button-height-md);
|
|
98
|
+
padding: 0;
|
|
99
|
+
border: 0;
|
|
100
|
+
border-radius: var(--button-radius);
|
|
101
|
+
background: transparent;
|
|
102
|
+
color: var(--ink-secondary);
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
transition: background-color var(--duration-instant) var(--ease-standard);
|
|
105
|
+
}
|
|
106
|
+
.dn-icon-button:hover { background: var(--fill-hover); color: var(--ink); }
|
|
107
|
+
.dn-icon-button:focus-visible { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
|
108
|
+
|
|
109
|
+
/* 点击区域:可见尺寸不变,用透明的伪元素把可点范围撑到 hit-min。
|
|
110
|
+
不要用 padding 补:padding 在背景里面,会把可见的按钮一起撑大。 */
|
|
111
|
+
.dn-button::before, .dn-icon-button::before {
|
|
112
|
+
content: "";
|
|
113
|
+
position: absolute;
|
|
114
|
+
top: 50%;
|
|
115
|
+
left: 50%;
|
|
116
|
+
width: max(100%, var(--hit-min));
|
|
117
|
+
height: max(100%, var(--hit-min));
|
|
118
|
+
transform: translate(-50%, -50%);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<div class="dn-row">
|
|
124
|
+
<button class="dn-button is-primary" type="button">保存</button>
|
|
125
|
+
<button class="dn-button" type="button">取消</button>
|
|
126
|
+
<button class="dn-button is-plain" type="button">跳过</button>
|
|
127
|
+
<button class="dn-button is-danger" type="button">删除</button>
|
|
128
|
+
<button class="dn-button is-primary" type="button" disabled>保存</button>
|
|
129
|
+
<button class="dn-icon-button" type="button" aria-label="搜索"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="M20 20l-4.5-4.5"/></svg></button>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="dn-row">
|
|
132
|
+
<button class="dn-button is-sm" type="button">获取</button>
|
|
133
|
+
<button class="dn-button" type="button"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>新建笔记</button>
|
|
134
|
+
<button class="dn-button is-primary is-lg" type="button">开始使用</button>
|
|
135
|
+
</div>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## SwiftUI
|
|
139
|
+
|
|
140
|
+
macOS 直接用系统按钮,`.controlSize(.regular)` 就是 24pt。iOS 的可见高度和点击区域分开处理。
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
Button("保存") { save() }
|
|
144
|
+
.buttonStyle(.borderedProminent) // 主按钮,颜色跟随 .tint(.brandAccent)
|
|
145
|
+
Button("取消", role: .cancel) { dismiss() }
|
|
146
|
+
.buttonStyle(.bordered)
|
|
147
|
+
Button("删除", role: .destructive) { delete() }
|
|
148
|
+
|
|
149
|
+
// iOS 默认按钮:看起来 36pt,点击区域补足到 44pt
|
|
150
|
+
Button("继续") { next() }
|
|
151
|
+
.font(.subheadline.weight(.semibold))
|
|
152
|
+
.buttonStyle(.borderedProminent)
|
|
153
|
+
.buttonBorderShape(.capsule)
|
|
154
|
+
.controlSize(.regular)
|
|
155
|
+
.padding(.vertical, (Deernova.hitMin - Deernova.buttonHeightMd) / 2) // 写在 buttonStyle 之后,是透明的
|
|
156
|
+
.contentShape(.rect)
|
|
157
|
+
|
|
158
|
+
// iOS 底部通栏主按钮
|
|
159
|
+
Button { start() } label: {
|
|
160
|
+
Text("开始使用").font(.callout.weight(.semibold))
|
|
161
|
+
.frame(maxWidth: .infinity, minHeight: Deernova.buttonHeightLg)
|
|
162
|
+
}
|
|
163
|
+
.buttonStyle(.borderedProminent)
|
|
164
|
+
.buttonBorderShape(.capsule)
|
|
165
|
+
|
|
166
|
+
// 加载中:宽度不变,标签换成转圈
|
|
167
|
+
Button { save() } label: {
|
|
168
|
+
ZStack { Text("保存").opacity(isSaving ? 0 : 1); if isSaving { ProgressView() } }
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
放在玻璃层上时改用 `.glassProminent` / `.glass`,见 [玻璃操作层](glass-controls.md)。
|
|
173
|
+
|
|
174
|
+
## 不要
|
|
175
|
+
|
|
176
|
+
- 用上下内边距撑出按钮高度,或让按钮继承页面的行高。
|
|
177
|
+
- 为了凑 44 点击区域把可见的按钮做成 44 高。
|
|
178
|
+
- 给次按钮加描边和阴影(它是一块灰底,不是浮起来的卡片)。
|
|
179
|
+
- 一个视图里放两个主按钮;为了强调把次要按钮也做成黑底。
|
|
180
|
+
- 用「确定」「OK」做标签;按钮里写一整句话。
|
|
181
|
+
- 把官网的胶囊大按钮搬进应用界面。
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# 卡片
|
|
2
|
+
|
|
3
|
+
卡片把一条内容的摘要装进一个可以点开的块里,用于网格和看板。
|
|
4
|
+
|
|
5
|
+
**需要提供**:标题、一到两行摘要、元信息(时间、分类、状态[标签](tag.md)),点击后打开详情。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:card -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `card-padding-x` | 16 | 16 | 24 | 卡片左右内边距。 |
|
|
13
|
+
| `card-padding-y` | 12 | 14 | 24 | 卡片上下内边距。 |
|
|
14
|
+
| `card-radius` | 12 | 12 | 12 | 卡片、分组列表、任务清单的圆角。 |
|
|
15
|
+
| `card-gap` | 12 | 12 | 16 | 卡片网格的间距。 |
|
|
16
|
+
<!-- /tokens -->
|
|
17
|
+
|
|
18
|
+
- `surface` 底、1px(`stroke-line`)`line` 描边,不加阴影,圆角 `card-radius`。
|
|
19
|
+
- 标题用 `type-headline`(无衬线、半粗);摘要用 `footnote`、`ink-secondary`,最多 3 行;元信息用 `caption`、`ink-tertiary`。内部间距 `space-1`。
|
|
20
|
+
- 悬停时描边加深为 `line-control`。选中时描边变成 `stroke-control` 宽的 `ink`。
|
|
21
|
+
- 网格间距 `card-gap`;每张卡片高度由内容决定,同一行顶部对齐。
|
|
22
|
+
|
|
23
|
+
## Web
|
|
24
|
+
|
|
25
|
+
整张卡片可点时,用 `<a>` 或 `<button>` 包住整张卡片,不要在卡片里再放一个主按钮。
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
.dn-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--card-gap); align-items: start; }
|
|
29
|
+
.dn-card {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
gap: var(--space-1);
|
|
33
|
+
min-width: 0;
|
|
34
|
+
padding: var(--card-padding-y) var(--card-padding-x);
|
|
35
|
+
border: var(--stroke-line) solid var(--line);
|
|
36
|
+
border-radius: var(--card-radius);
|
|
37
|
+
background: var(--surface);
|
|
38
|
+
color: var(--ink);
|
|
39
|
+
text-decoration: none;
|
|
40
|
+
transition: border-color var(--duration-instant) var(--ease-standard);
|
|
41
|
+
}
|
|
42
|
+
a.dn-card:hover, button.dn-card:hover { border-color: var(--line-control); }
|
|
43
|
+
.dn-card:focus-visible { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
|
44
|
+
.dn-card[aria-current="true"], .dn-card.is-selected { border-color: var(--ink); }
|
|
45
|
+
.dn-card-title { margin: 0; font: var(--type-headline); }
|
|
46
|
+
.dn-card-summary {
|
|
47
|
+
display: -webkit-box;
|
|
48
|
+
margin: 0;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
color: var(--ink-secondary);
|
|
51
|
+
font: var(--type-footnote);
|
|
52
|
+
-webkit-line-clamp: 3;
|
|
53
|
+
-webkit-box-orient: vertical;
|
|
54
|
+
}
|
|
55
|
+
.dn-card-meta { display: flex; align-items: center; gap: 6px; margin-top: 2px; color: var(--ink-tertiary); font: var(--weight-regular) var(--type-caption-size)/var(--type-caption-line) var(--font-sans); font-variant-numeric: tabular-nums; }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<div class="dn-card-grid">
|
|
60
|
+
<a class="dn-card" href="#"><h3 class="dn-card-title">本周回顾</h3><p class="dn-card-summary">完成 12 条笔记,整理了两个项目的待办。</p><div class="dn-card-meta">今天 09:41</div></a>
|
|
61
|
+
<a class="dn-card is-selected" href="#" aria-current="true"><h3 class="dn-card-title">读书摘录</h3><p class="dn-card-summary">好的工具会退到背景里,让人只看见自己的工作。</p><div class="dn-card-meta">昨天</div></a>
|
|
62
|
+
<a class="dn-card" href="#"><h3 class="dn-card-title">旅行清单</h3><p class="dn-card-summary">护照、转换插头、两本书、一件薄外套。</p><div class="dn-card-meta">10 月 28 日</div></a>
|
|
63
|
+
</div>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## SwiftUI
|
|
67
|
+
|
|
68
|
+
```swift
|
|
69
|
+
VStack(alignment: .leading, spacing: Deernova.space1) {
|
|
70
|
+
Text(note.title).font(.deernovaHeadline)
|
|
71
|
+
Text(note.summary).font(.deernovaFootnote).foregroundStyle(.inkSecondary).lineLimit(3)
|
|
72
|
+
Text(note.date, format: .relative(presentation: .named))
|
|
73
|
+
.font(.caption).foregroundStyle(.inkTertiary)
|
|
74
|
+
}
|
|
75
|
+
.padding(.horizontal, Deernova.cardPaddingX)
|
|
76
|
+
.padding(.vertical, Deernova.cardPaddingY)
|
|
77
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
78
|
+
.background(.surface, in: .rect(cornerRadius: Deernova.cardRadius))
|
|
79
|
+
.overlay(
|
|
80
|
+
RoundedRectangle(cornerRadius: Deernova.cardRadius)
|
|
81
|
+
.strokeBorder(isSelected ? .ink : .line, lineWidth: Deernova.strokeLine)
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 不要
|
|
86
|
+
|
|
87
|
+
- 给卡片加阴影或玻璃效果。
|
|
88
|
+
- 在卡片左侧加一条彩色竖条。
|
|
89
|
+
- 卡片里套卡片;一张卡片里放多个主按钮。
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# 代码块
|
|
2
|
+
|
|
3
|
+
展示代码、命令或结构化数据,带语言标签和复制按钮。
|
|
4
|
+
|
|
5
|
+
**需要提供**:代码文本、语言名称;可选的文件名(替代语言名显示在表头)。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:code-block -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `code-header-height` | 32 | 36 | 36 | 代码块表头的高度。 |
|
|
13
|
+
<!-- /tokens -->
|
|
14
|
+
|
|
15
|
+
- `surface-sunken` 底、`line` 细线描边、`radius-md`。表头高 `code-header-height`,左边是语言名或文件名(`caption` 字号、等宽、`ink-tertiary`),右边是一个小号文字按钮「复制」;点击后显示「已复制」1.5 秒,按钮宽度不变。
|
|
16
|
+
- 代码用 `type-code`,内边距上下 `space-2`、左右 `space-3`,过长时横向滚动,不自动换行。
|
|
17
|
+
- 语法高亮只用灰阶:关键字 `ink` 加粗,字符串 `ink-secondary`,注释 `ink-tertiary` 加斜体,其余 `ink`。这些颜色在 `surface-sunken` 上都不低于 4.5:1。
|
|
18
|
+
|
|
19
|
+
## Web
|
|
20
|
+
|
|
21
|
+
```css
|
|
22
|
+
.dn-code { overflow: hidden; min-width: 0; border: var(--stroke-line) solid var(--line); border-radius: var(--radius-md); background: var(--surface-sunken); }
|
|
23
|
+
.dn-code-header {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: var(--space-2);
|
|
27
|
+
height: var(--code-header-height);
|
|
28
|
+
padding: 0 var(--space-1) 0 var(--space-3);
|
|
29
|
+
border-bottom: var(--stroke-line) solid var(--line);
|
|
30
|
+
color: var(--ink-tertiary);
|
|
31
|
+
font: var(--weight-regular) var(--type-caption-size)/1 var(--font-mono);
|
|
32
|
+
}
|
|
33
|
+
.dn-code-header .dn-button { margin-left: auto; }
|
|
34
|
+
.dn-code pre { margin: 0; padding: var(--space-2) var(--space-3); overflow-x: auto; color: var(--ink); font: var(--type-code); }
|
|
35
|
+
.dn-code code { font: inherit; }
|
|
36
|
+
.dn-code .tk-keyword { color: var(--ink); font-weight: var(--weight-semibold); }
|
|
37
|
+
.dn-code .tk-string { color: var(--ink-secondary); }
|
|
38
|
+
.dn-code .tk-comment { color: var(--ink-tertiary); font-style: italic; }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<div class="dn-code">
|
|
43
|
+
<div class="dn-code-header">swift<button class="dn-button is-plain is-sm" type="button"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/></svg>复制</button></div>
|
|
44
|
+
<pre><code><span class="tk-comment">// 统计本周完成的笔记数</span>
|
|
45
|
+
<span class="tk-keyword">let</span> done = notes.filter { $0.completedAt >= start }
|
|
46
|
+
<span class="tk-keyword">print</span>(<span class="tk-string">"本周完成 \(done.count) 条"</span>)</code></pre>
|
|
47
|
+
</div>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## SwiftUI
|
|
51
|
+
|
|
52
|
+
```swift
|
|
53
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
54
|
+
HStack {
|
|
55
|
+
Text(language).font(.caption.monospaced()).foregroundStyle(.inkTertiary)
|
|
56
|
+
Spacer()
|
|
57
|
+
Button(copied ? "已复制" : "复制", systemImage: copied ? "checkmark" : "doc.on.doc") { copy() }
|
|
58
|
+
.buttonStyle(.borderless).controlSize(.small)
|
|
59
|
+
}
|
|
60
|
+
.padding(.horizontal, Deernova.space3).frame(height: Deernova.codeHeaderHeight)
|
|
61
|
+
Divider()
|
|
62
|
+
ScrollView(.horizontal) {
|
|
63
|
+
Text(highlighted) // 用 AttributedString 按上面的颜色着色
|
|
64
|
+
.font(.deernovaCode)
|
|
65
|
+
.textSelection(.enabled)
|
|
66
|
+
.padding(.vertical, Deernova.space2).padding(.horizontal, Deernova.space3)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
.background(.surfaceSunken, in: .rect(cornerRadius: Deernova.radiusMd))
|
|
70
|
+
.overlay(RoundedRectangle(cornerRadius: Deernova.radiusMd).strokeBorder(.line, lineWidth: 1 / displayScale))
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 不要
|
|
74
|
+
|
|
75
|
+
- 用彩色的编辑器配色主题(和黑白灰的整体风格冲突)。
|
|
76
|
+
- 给代码块加阴影。
|
|
77
|
+
- 自动换行打乱缩进。
|