@deernova/style 0.1.0 → 0.2.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.
Files changed (88) hide show
  1. package/README.md +11 -8
  2. package/bin/cli.mjs +10 -7
  3. package/package.json +3 -4
  4. package/skill/SKILL.md +4 -2
  5. package/skill/manifest.json +2 -2
  6. package/skill/references/web.md +2 -2
  7. package/template/app.js +0 -7
  8. package/template/index.html +0 -1290
  9. package/template/manifest.json +0 -24
  10. package/template/project/README.md +0 -170
  11. package/template/project/components/Banner/README.md +0 -33
  12. package/template/project/components/Banner/preview.html +0 -26
  13. package/template/project/components/Button/README.md +0 -59
  14. package/template/project/components/Button/preview.html +0 -27
  15. package/template/project/components/Card/README.md +0 -31
  16. package/template/project/components/Card/preview.html +0 -24
  17. package/template/project/components/CodeBlock/README.md +0 -34
  18. package/template/project/components/CodeBlock/preview.html +0 -16
  19. package/template/project/components/Composer/README.md +0 -42
  20. package/template/project/components/Composer/preview.html +0 -28
  21. package/template/project/components/Cover/preview.html +0 -53
  22. package/template/project/components/EmptyState/README.md +0 -28
  23. package/template/project/components/EmptyState/preview.html +0 -16
  24. package/template/project/components/GlassControls/README.md +0 -43
  25. package/template/project/components/GlassControls/preview.html +0 -40
  26. package/template/project/components/ListGroup/README.md +0 -39
  27. package/template/project/components/ListGroup/preview.html +0 -22
  28. package/template/project/components/MacWindow/README.md +0 -27
  29. package/template/project/components/MacWindow/preview.html +0 -70
  30. package/template/project/components/Message/README.md +0 -31
  31. package/template/project/components/Message/preview.html +0 -21
  32. package/template/project/components/PhoneScreen/README.md +0 -25
  33. package/template/project/components/PhoneScreen/preview.html +0 -74
  34. package/template/project/components/Segmented/README.md +0 -21
  35. package/template/project/components/Segmented/preview.html +0 -21
  36. package/template/project/components/Sheet/README.md +0 -29
  37. package/template/project/components/Sheet/preview.html +0 -31
  38. package/template/project/components/Sidebar/README.md +0 -36
  39. package/template/project/components/Sidebar/preview.html +0 -20
  40. package/template/project/components/StepRow/README.md +0 -35
  41. package/template/project/components/StepRow/preview.html +0 -17
  42. package/template/project/components/Tag/README.md +0 -36
  43. package/template/project/components/Tag/preview.html +0 -22
  44. package/template/project/components/TextField/README.md +0 -29
  45. package/template/project/components/TextField/preview.html +0 -28
  46. package/template/project/components/bundle.css +0 -189
  47. package/template/project/design-system.json +0 -25
  48. package/template/project/images/Banner-dark.png +0 -0
  49. package/template/project/images/Banner-light.png +0 -0
  50. package/template/project/images/Button-dark.png +0 -0
  51. package/template/project/images/Button-light.png +0 -0
  52. package/template/project/images/Card-dark.png +0 -0
  53. package/template/project/images/Card-light.png +0 -0
  54. package/template/project/images/CodeBlock-dark.png +0 -0
  55. package/template/project/images/CodeBlock-light.png +0 -0
  56. package/template/project/images/Composer-dark.png +0 -0
  57. package/template/project/images/Composer-light.png +0 -0
  58. package/template/project/images/Cover-dark.png +0 -0
  59. package/template/project/images/Cover-light.png +0 -0
  60. package/template/project/images/EmptyState-dark.png +0 -0
  61. package/template/project/images/EmptyState-light.png +0 -0
  62. package/template/project/images/Example-iOSButtonsInPhone-dark.png +0 -0
  63. package/template/project/images/Example-iOSButtonsInPhone-light.png +0 -0
  64. package/template/project/images/GlassControls-dark.png +0 -0
  65. package/template/project/images/GlassControls-light.png +0 -0
  66. package/template/project/images/ListGroup-dark.png +0 -0
  67. package/template/project/images/ListGroup-light.png +0 -0
  68. package/template/project/images/MacWindow-dark.png +0 -0
  69. package/template/project/images/MacWindow-light.png +0 -0
  70. package/template/project/images/Message-dark.png +0 -0
  71. package/template/project/images/Message-light.png +0 -0
  72. package/template/project/images/PhoneScreen-dark.png +0 -0
  73. package/template/project/images/PhoneScreen-light.png +0 -0
  74. package/template/project/images/Segmented-dark.png +0 -0
  75. package/template/project/images/Segmented-light.png +0 -0
  76. package/template/project/images/Sheet-dark.png +0 -0
  77. package/template/project/images/Sheet-light.png +0 -0
  78. package/template/project/images/Sidebar-dark.png +0 -0
  79. package/template/project/images/Sidebar-light.png +0 -0
  80. package/template/project/images/StepRow-dark.png +0 -0
  81. package/template/project/images/StepRow-light.png +0 -0
  82. package/template/project/images/Tag-dark.png +0 -0
  83. package/template/project/images/Tag-light.png +0 -0
  84. package/template/project/images/TextField-dark.png +0 -0
  85. package/template/project/images/TextField-light.png +0 -0
  86. package/template/project/tokens.css +0 -187
  87. package/template/project/tokens.json +0 -142
  88. package/template/style.css +0 -1
@@ -1,24 +0,0 @@
1
- {
2
- "version": "0.1.0",
3
- "sourceHash": "277411693f87d8b2b805a12f90482ef52a45f52dbfe51a2e607b683f0a1119b9",
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
- ]
24
- }
@@ -1,170 +0,0 @@
1
- Deernova 是给 macOS 和 iOS 应用用的一套设计语言:只用黑、白、灰。纸面承载内容,液态玻璃(Liquid Glass)承载操作,强调靠明暗对比,不靠彩色。整体紧凑、安静、克制,像一件趁手的工具。
2
-
3
- ## 原则
4
-
5
- 1. **纸面与玻璃分层。** 文字、列表、卡片、代码这些内容,放在不透明的纸面上:`canvas`、`surface`、`surface-raised`、`surface-sunken`。工具栏、标签栏、浮动按钮、弹出层这些操作,浮在内容之上,用系统的 Liquid Glass。玻璃上不放内容,内容也不做成玻璃。
6
- 2. **黑白灰,强调靠对比。** `accent` 就是墨色本身:浅色主题下主按钮是黑底白字,深色主题下是白底黑字。界面里唯一的彩色是状态色(成功、警告、错误、提示),只在表达状态时出现。
7
- 3. **用线条代替阴影。** 区块之间用 `line` 细线分隔(`stroke-hairline`)。只有真正浮起来的东西(菜单、弹窗、浮动面板)才加阴影 `shadow-float`。
8
- 4. **衬线用来"说",无衬线用来"做"。** 标题、空状态、长文阅读(文章、AI 回答)用衬线 `--font-serif`;按钮、标签、列表、表单这些界面文字,用系统无衬线 `--font-sans`。
9
- 5. **紧凑。** Mac 正文 13pt、行高约 1.4 倍,控件 24pt、列表行 26pt;靠细线和少量留白分组,不靠大块空白。iOS 保留系统的 17pt 正文和 44pt 点击区域,但按钮看起来只有 36pt。
10
- 6. **原生优先。** 系统已有的控件(Toggle、Picker、Menu、DatePicker、Alert)直接用,只通过 `tint` 和字体让它们融入。自定义组件只做系统没有的东西:Composer、Message、StepRow。
11
-
12
- ## 文案
13
-
14
- - 用"你"称呼用户。语气平实直接,不用感叹号,不用 emoji。
15
- - 按钮以动词开头,说清点了会发生什么:「保存」「新建笔记」「删除 3 个文件」。不用「确定」「OK」。操作完成后的提示用完成态:「已保存」「已复制」。
16
- - 错误提示要说明出了什么问题、怎么解决:「网络连接已断开,恢复后会自动重试。」不道歉,不含糊。
17
- - 中文与英文、中文与数字之间加一个半角空格:「同步到 iCloud」「剩余 12 次」。数字和单位用半角。
18
- - 标题、按钮、标签不加句号;完整的句子加句号。
19
- - 空状态写法:标题说明现在的状态,正文告诉用户下一步做什么。例如「还没有笔记」/「写下第一条想法,它会同步到你所有的设备。」
20
-
21
- ## 颜色
22
-
23
- 所有颜色都是语义 token,分 Light 和 Dark 两套主题,两套都验算过对比度。灰阶全部是中性灰,不偏暖也不偏冷。
24
-
25
- - **纸面层级**,从下到上:窗口底色 `canvas` → 内容区和卡片 `surface` → 输入框、Composer、弹出层 `surface-raised`。代码块、用户消息气泡、展开的详情用 `surface-sunken`。
26
- - **文字**:正文用 `ink`,次要信息用 `ink-secondary`,占位符、时间戳、计数用 `ink-tertiary`。三者在四种纸面上都不低于 4.5:1。
27
- - **线条**:分隔线和卡片描边用 `line`,属于装饰。输入框边框用 `line-control`,不低于 3:1,让人看得出这里可以输入。
28
- - **强调**:`accent` 等于 `ink`,用于主按钮填充、选中指示、焦点环。主按钮上的文字用 `on-accent`,不要写死白色,深色主题下它是黑色。浅灰强调底用 `accent-soft`,上面的文字用 `ink`。链接用 `ink` 加下划线,不用彩色。
29
- - **焦点**:`focus-ring` 等于 `accent`,2pt 实线,外偏移 2pt。
30
- - **状态色**:`success`(青绿)、`warning`(赭黄)、`danger`(莓红)、`info`(蓝),各自配一个 `-soft` 浅底。这是界面里仅有的彩色,只用在状态上,不拿来装饰。状态必须同时带图标或文字,不能只靠颜色区分。`success` 特意偏青,和 `danger` 拉开距离,色弱用户也能分辨。开关打开时用 `success`,因为黑白开关在深色主题下看不清圆钮。
31
- - **交互底色**:悬停用 `fill-hover`,选中用 `fill-selected`。它们是半透明的,叠在任何纸面上都成立。
32
- - **仅限网页预览**:`glass-fill`、`glass-stroke`、`shadow-glass` 只是在网页里模拟玻璃效果。App 里一律用系统的 `.glassEffect()`,不要用这些值自己画玻璃。
33
-
34
- 在 SwiftUI 里落地:在 Asset Catalog 里按 token 名建 Color Set,分别填入 Any 和 Dark 两套值,再写一个扩展来引用。扩展写在 `ShapeStyle` 上,这样 `.foregroundStyle(.inkSecondary)`、`.background(.surface, in: …)` 都能直接用点语法。组件说明里的代码都按这套命名书写:
35
-
36
- ```swift
37
- extension ShapeStyle where Self == Color {
38
- static var canvas: Color { Color("canvas") }
39
- static var surface: Color { Color("surface") }
40
- static var surfaceRaised: Color { Color("surface-raised") }
41
- static var surfaceSunken: Color { Color("surface-sunken") }
42
- static var ink: Color { Color("ink") }
43
- static var inkSecondary: Color { Color("ink-secondary") }
44
- static var inkTertiary: Color { Color("ink-tertiary") }
45
- static var line: Color { Color("line") }
46
- static var brandAccent: Color { Color("accent") } // 等于 ink;避开系统的 .accentColor
47
- // success、successSoft、warning、danger、fillSelected 等其余 token 同理
48
- }
49
-
50
- // 在 App 入口统一着色,系统控件会自动跟随(按钮、选中态变成黑 / 白)
51
- WindowGroup {
52
- RootView().tint(.brandAccent)
53
- }
54
- // 开关单独着色
55
- Toggle("iCloud 同步", isOn: $sync).tint(.success)
56
- ```
57
-
58
- ## 字体
59
-
60
- 全部使用系统字体,不打包字体文件:App 体积不增加,也自动支持动态字体(Dynamic Type)。
61
-
62
- | 角色 | 西文 | 中文 | SwiftUI |
63
- | --- | --- | --- | --- |
64
- | 衬线:标题、长文 | New York | 苹方 | `.system(.title, design: .serif)` |
65
- | 无衬线:界面 | SF Pro | 苹方 | `.body`、`.headline` 等 |
66
- | 等宽:代码、数据 | SF Mono | 苹方 | `.system(.body, design: .monospaced)` |
67
-
68
- - macOS 和 iOS 各有一套字号,见 Typography。Mac 正文 13pt、行高 18(`mac-body`),长文 14pt、行高 22(`mac-prose`);iOS 正文 17pt、行高 23(`ios-body`)。
69
- - iOS 一律用文本样式(`.body`、`.headline`),不要写死字号,这样动态字体才会生效。需要衬线时用 `.system(.title2, design: .serif)` 这种写法,同样能随系统缩放。
70
- - 界面文字行高约为字号的 1.4 倍;长文阅读放宽到 1.5–1.6 倍,中文读起来不挤。
71
- - 如果想让中文标题也有衬线的气质,可以打包思源宋体(Source Han Serif,开源可商用)的子集,只用在大标题和营销页上。界面文字始终用苹方。
72
- - 需要上下对齐的数字(计数、价格、时间)用等宽数字:`.monospacedDigit()`。
73
-
74
- ## 间距与尺寸
75
-
76
- - 采用 4pt 网格:`space-1`(4)、`space-2`(8)、`space-3`(12)、`space-4`(16)、`space-5`(20)、`space-6`(24)、`space-8`(32)、`space-10`(40)。日常布局基本只用到 `space-1` 到 `space-4`。
77
- - **macOS 紧凑**:控件高 `control-mac`(24),侧边栏和列表行 `row-mac`(26),内容区边距 `space-4`,卡片内边距 `space-3`,消息之间 `space-4`,侧边栏宽 `sidebar-mac`(220)。
78
- - **iOS**:可点区域至少 `control-ios`(44)。按钮的可见高度:默认 `button-ios`(36),小号 `button-ios-sm`(30),通栏主按钮 `control-ios-lg`(46)。页面左右边距 `space-4`,卡片之间 `space-2`,分组之间 `space-5`。
79
- - 长文阅读的栏宽不超过 `content-max`(680),14pt 下大约每行 45 个汉字。
80
- - 组与组之间的间距至少是组内间距的 2 倍。层次靠间距建立,不靠加更多的线和框。
81
-
82
- ## 圆角
83
-
84
- - `radius-xs`(4):标签、复选框。
85
- - `radius-sm`(6):Mac 按钮、输入框、侧边栏选中块。
86
- - `radius-md`(8):Mac 卡片、横幅、代码块、菜单。
87
- - `radius-lg`(12):iOS 卡片、分组列表、Composer、消息气泡、Mac 侧边栏面板。
88
- - `radius-xl`(24):iOS 底部弹窗、单行 Composer。
89
- - `radius-full`:胶囊和圆形,用于玻璃按钮、iOS 主按钮、发送按钮。
90
- - 嵌套时保持同心:内圆角 = 外圆角 − 内边距。iOS 26 / macOS 26 起可以直接用 `ConcentricRectangle()`,内层会自动和外层容器同心。
91
-
92
- ## 线条
93
-
94
- - `stroke-hairline`:1 个物理像素,在 Retina 屏上是 0.5pt,用于分隔线和卡片描边。SwiftUI 里写成 `1 / displayScale`,`displayScale` 从 `@Environment(\.displayScale)` 读取。
95
- - `stroke-control`:1pt,用于输入框边框。
96
- - `stroke-focus`:2pt,用于焦点环。
97
- - 列表分隔线从文字起点开始,不贯穿到左边缘。
98
-
99
- ## 层级与阴影
100
-
101
- 从下到上分三层:
102
-
103
- 1. 纸面:`canvas` 和 `surface`。
104
- 2. 用细线分隔的内容块:没有阴影。
105
- 3. 浮层:玻璃控件、菜单、弹窗,带 `shadow-float` 或系统玻璃自带的阴影。
106
-
107
- 卡片不加阴影。
108
-
109
- ## Liquid Glass
110
-
111
- 目标系统设为 iOS 26+ / macOS 26+,直接用系统 API,不写兼容层。
112
-
113
- **用玻璃的地方**(大部分由系统自动提供):
114
-
115
- - 导航栏、工具栏、标签栏、Mac 侧边栏:使用标准的 `NavigationStack`、`NavigationSplitView`、`TabView`、`.toolbar`,系统会自动渲染成玻璃。
116
- - 浮动操作按钮、浮动工具条:用 `.glassEffect()`。相邻的几个玻璃元素放进同一个 `GlassEffectContainer`,它们会互相融合,切换时也会跟着变形。
117
- - 底部弹窗、弹出菜单、popover:系统自动处理。
118
- - 玻璃层上的主操作用 `.buttonStyle(.glassProminent)` 加 `.tint(.brandAccent)`,次要操作用 `.buttonStyle(.glass)`。
119
-
120
- **不用玻璃的地方**:卡片、列表行、消息、输入框、代码块,以及所有需要长时间阅读的内容。
121
-
122
- **规则**:
123
-
124
- - 玻璃上不叠玻璃。
125
- - 玻璃上只放图标和很短的标签,不放段落。
126
- - 只有主操作的玻璃才用实色(黑底白图标),一屏最多一个。
127
- - iOS 上可点击的自定义玻璃加 `.interactive()`,按下时会有形变反馈。
128
- - 让内容从工具栏下面滚过去,保留系统的滚动边缘效果。不要给工具栏加实色背景。
129
- - iOS 标签栏在向下滚动时收起:`.tabBarMinimizeBehavior(.onScrollDown)`。
130
- - Mac 上侧边栏旁边的大图,用 `.backgroundExtensionEffect()` 延伸到侧边栏下方。
131
- - 用户开启「降低透明度」后,系统会自动把玻璃换成实色。自己写的半透明效果也要读取 `accessibilityReduceTransparency`,并做同样的处理。
132
-
133
- ```swift
134
- // 浮动工具条:两个玻璃按钮融合成一组
135
- GlassEffectContainer(spacing: 12) {
136
- HStack(spacing: 12) {
137
- Button("新建", systemImage: "plus") { createNote() }
138
- .buttonStyle(.glassProminent)
139
- Button("搜索", systemImage: "magnifyingglass") { search() }
140
- .buttonStyle(.glass)
141
- }
142
- }
143
- .tint(.brandAccent)
144
- ```
145
-
146
- ## 动效
147
-
148
- - 默认用弹簧动画:`.spring(response: 0.35, dampingFraction: 0.85)`。悬停、按压这类即时反馈用 0.12s,展开、切换用 0.2s,弹窗的动画交给系统。
149
- - 动效用来说明因果:东西从哪里来、到哪里去。不做装饰性的循环动画。
150
- - 用户开启「减弱动态效果」(`accessibilityReduceMotion`)后,位移和缩放动画换成淡入淡出。
151
-
152
- ## 图标
153
-
154
- - 只用 SF Symbols,和系统保持一致。不用 emoji 当图标。
155
- - 图标粗细跟随旁边的文字。Mac 上图标 14pt,13pt 文字旁的图标用 `.medium`,看起来更清楚。
156
- - 默认颜色 `ink-secondary`,选中态和主操作用 `ink`。图标不用彩色,状态图标除外。多层符号用 `.symbolRenderingMode(.hierarchical)`。
157
- - 状态图标固定为:成功 `checkmark.circle.fill`,警告 `exclamationmark.triangle.fill`,错误 `xmark.octagon.fill`,提示 `info.circle.fill`。
158
- - 目前还没有品牌标志,界面里先用文字名称。做好标志后,再新增一个 Logos 资产组。
159
- - 组件预览里的图标是近似 SF Symbols 的线稿,App 里换成对应的 SF Symbols。
160
-
161
- ## 无障碍
162
-
163
- - 每个文字 token 在它说明里注明的纸面上不低于 4.5:1,两套主题都验算过。界面紧凑不等于字小:Mac 正文不低于 13pt,说明文字不低于 11pt。
164
- - iOS 全面支持动态字体,在最大字号下也不截断文字:允许换行,横排放不下就改成竖排。
165
- - iOS 上每个可点元素的点击区域至少 44×44pt,看起来可以更小(按钮 36pt、小按钮 30pt),差的部分用透明的内边距补足;Mac 上的图标按钮至少 24×24pt。
166
- - 只靠颜色传达的信息,一律补上图标或文字。
167
-
168
- ## 组件
169
-
170
- 组件页里的预览是网页上的近似效果,用来对齐尺寸、颜色和层级。App 里用 SwiftUI 实现,每个组件的说明里都写了对应的 SwiftUI 写法,以及需要传入哪些内容。Layouts 分组里有一个 Mac 窗口示例和一个 iPhone 界面示例,展示各个组件组合在一起的样子。
@@ -1,33 +0,0 @@
1
- # Banner
2
-
3
- 页面内的提示条,告诉用户一件需要知道的事,必要时附带一个解决动作。轻提示(Toast)是它的短暂版本。
4
-
5
- **需要提供**:语气(中性 / success / warning / danger)、标题(一句话说明状态)、正文(后果或解决办法)、可选的一个动作按钮;能关闭的横幅还要提供关闭动作。
6
-
7
- **规格**
8
-
9
- - 中性横幅:`surface-raised` 底、`line` 描边、`ink-secondary` 图标。有语气的横幅:对应的 `-soft` 底、无描边、图标用语气色。文字始终是 `ink` 和 `ink-secondary`。
10
- - `radius-md`,内边距上下 `space-2`、左右 `space-3`,图标 16pt;标题 13pt 半粗,正文 12pt。图标统一用 README「图标」一节里的状态符号。
11
- - 动作按钮用小号次按钮,放在右侧,一个横幅最多一个。
12
- - 轻提示:底部居中的玻璃胶囊,高 40pt,2 秒后自动消失,只用来确认刚刚完成的操作(「已复制」「已保存」),文字用完成态,不放按钮。
13
-
14
- ```swift
15
- HStack(alignment: .top, spacing: 10) {
16
- Image(systemName: "exclamationmark.triangle.fill").foregroundStyle(.warning)
17
- VStack(alignment: .leading, spacing: 2) {
18
- Text("本月 AI 次数还剩 10%").font(.headline)
19
- Text("用完后可以继续写笔记,AI 功能会在 11 月 1 日恢复。")
20
- .foregroundStyle(.inkSecondary)
21
- }
22
- }
23
- .padding(.vertical, 8).padding(.horizontal, 12)
24
- .frame(maxWidth: .infinity, alignment: .leading)
25
- .background(.warningSoft, in: .rect(cornerRadius: 8))
26
-
27
- // 轻提示
28
- Label("已复制到剪贴板", systemImage: "checkmark.circle.fill")
29
- .padding(.horizontal, 16).frame(height: 40)
30
- .glassEffect(in: .capsule)
31
- ```
32
-
33
- **不要**:一个页面同时出现多个横幅;用横幅打广告;错误信息只写「出错了」;轻提示里放需要用户处理的事情(那应该用横幅或弹窗)。
@@ -1,26 +0,0 @@
1
- <!-- @dsCard group="Feedback" height=280 subtitle="页面内提示与轻提示" -->
2
- <!doctype html>
3
- <html lang="zh-CN">
4
- <head><link rel="stylesheet" href="../../tokens.css"><link rel="stylesheet" href="../bundle.css"><script>(function(){function a(){var t=location.hash.slice(1),r=document.documentElement;if(t==="light"||t==="dark")r.setAttribute("data-theme",t);else r.removeAttribute("data-theme");}a();addEventListener("hashchange",a);})();</script><meta charset="utf-8"><title>Banner</title></head>
5
- <body>
6
- <div class="dn-stage dn-stack" style="align-items:flex-start">
7
- <div class="dn-banner">
8
- <svg class="dn-icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></svg>
9
- <div class="dn-banner-text"><p class="dn-banner-title">新版本已就绪</p><p class="dn-banner-body">重新打开应用后生效,未保存的内容会自动保留。</p></div>
10
- <button class="dn-btn dn-btn-sm">重新打开</button>
11
- </div>
12
- <div class="dn-banner dn-banner-warning">
13
- <svg class="dn-icon" viewBox="0 0 24 24"><path d="M12 4l9 16H3z"/><path d="M12 10v4M12 17h.01"/></svg>
14
- <div class="dn-banner-text"><p class="dn-banner-title">本月 AI 次数还剩 10%</p><p class="dn-banner-body">用完后可以继续写笔记,AI 功能会在 11 月 1 日恢复。</p></div>
15
- </div>
16
- <div class="dn-banner dn-banner-danger">
17
- <svg class="dn-icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9 9l6 6M15 9l-6 6"/></svg>
18
- <div class="dn-banner-text"><p class="dn-banner-title">同步失败</p><p class="dn-banner-body">网络连接已断开,恢复后会自动重试。</p></div>
19
- <button class="dn-btn dn-btn-sm">立即重试</button>
20
- </div>
21
- <div class="dn-glass dn-toast" role="status">
22
- <svg class="dn-icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M8 12.5l2.5 2.5L16 9.5"/></svg>已复制到剪贴板
23
- </div>
24
- </div>
25
- </body>
26
- </html>
@@ -1,59 +0,0 @@
1
- # Button
2
-
3
- 按钮执行一个动作;每个视图最多一个主按钮(`accent` 填充,浅色主题黑底白字、深色主题白底黑字),用来完成这一屏存在的理由。
4
-
5
- **变体**
6
-
7
- - 主按钮:`accent` 底、`on-accent` 字。用于保存、发送、购买等关键动作。
8
- - 次按钮:`surface-raised` 底、`line` 描边、`ink` 字,带 `shadow-control`。
9
- - 文字按钮:无底,`ink-secondary` 字,悬停时出现 `fill-hover`。用于工具栏和卡片内的次要操作。
10
- - 危险按钮:`danger-soft` 底、`danger` 字。只有在最终确认弹窗里,才用系统的 `.destructive` 样式。
11
-
12
- **尺寸**
13
-
14
- | | macOS | iOS |
15
- | --- | --- | --- |
16
- | 小 | `control-mac-sm`(20),`radius-xs`,11pt | `button-ios-sm`(30),胶囊,13pt 半粗,用于卡片和列表里的行内操作 |
17
- | 默认 | `control-mac`(24),`radius-sm`,13pt 中粗 | `button-ios`(36),胶囊,15pt 半粗 |
18
- | 大 | `control-mac-lg`(28) | `control-ios-lg`(46),通栏胶囊,16pt 半粗,只用于页面底部或弹窗里唯一的主操作 |
19
-
20
- iOS 按钮看起来可以比 44pt 小,但点击区域必须补足到 44pt(`control-ios`):在按钮外面加透明的内边距,或者用 `.contentShape` 扩大可点范围。
21
-
22
- **iOS 怎么选尺寸**:不是随意挑,按按钮所在的位置定。
23
-
24
- - 列表行、卡片角落里的行内操作(获取、关注、打开)→ 小号 30pt。
25
- - 页面和卡片里的普通按钮,包括并排的两个按钮(以后再说 / 开启)→ 默认 36pt。同一行里的按钮用同一个尺寸。
26
- - 整页唯一的主操作,放在页面底部(登录、订阅、开始使用)→ 通栏 46pt,一屏最多一个。
27
- - 导航栏、标签栏、工具栏里的按钮 → 用系统控件,不自己设尺寸。
28
-
29
- iOS 按钮比 Mac 大是有意的:手指比鼠标指针粗,Mac 默认 24pt,iOS 默认 36pt。在 Mac 屏幕上看 iOS 设计图会比真机上大一些,因为 Mac 上 1pt 的物理尺寸比 iPhone 上大,判断大小时以真机为准。
30
-
31
- **需要提供**:标签(动词开头)、可选的 SF Symbol、点击动作。加载中时保持按钮宽度不变,把标签换成 `ProgressView()`。
32
-
33
- ```swift
34
- Button("保存") { save() }
35
- .buttonStyle(.borderedProminent) // 主按钮,颜色跟随 .tint(.brandAccent)
36
- Button("取消", role: .cancel) { dismiss() }
37
- .buttonStyle(.bordered)
38
- Button("删除", role: .destructive) { delete() }
39
-
40
- // iOS 默认按钮:看起来 36pt 高,点击区域补足到 44pt
41
- Button("继续") { next() }
42
- .font(.subheadline.weight(.semibold))
43
- .buttonStyle(.borderedProminent)
44
- .buttonBorderShape(.capsule)
45
- .controlSize(.regular)
46
- .padding(.vertical, 4) // 透明的外边距,把点击区域撑到 44pt
47
- .contentShape(.rect)
48
-
49
- // iOS 底部通栏主按钮(46pt)
50
- Button { start() } label: {
51
- Text("开始使用").font(.callout.weight(.semibold)).frame(maxWidth: .infinity, minHeight: 46)
52
- }
53
- .buttonStyle(.borderedProminent)
54
- .buttonBorderShape(.capsule)
55
-
56
- // 放在玻璃层上时改用 .glassProminent / .glass,见 GlassControls
57
- ```
58
-
59
- **不要**:一个视图里放两个主按钮;用「确定」「OK」做标签;按钮里写一整句话;为了强调把次要按钮也做成黑底。
@@ -1,27 +0,0 @@
1
- <!-- @dsCard group="Actions" height=170 subtitle="主按钮每屏最多一个" -->
2
- <!doctype html>
3
- <html lang="zh-CN">
4
- <head><link rel="stylesheet" href="../../tokens.css"><link rel="stylesheet" href="../bundle.css"><script>(function(){function a(){var t=location.hash.slice(1),r=document.documentElement;if(t==="light"||t==="dark")r.setAttribute("data-theme",t);else r.removeAttribute("data-theme");}a();addEventListener("hashchange",a);})();</script><meta charset="utf-8"><title>Button</title></head>
5
- <body>
6
- <div class="dn-stage dn-stack" style="align-items:flex-start">
7
- <div class="dn-note">macOS · 24pt</div>
8
- <div class="dn-row">
9
- <button class="dn-btn dn-btn-primary">保存</button>
10
- <button class="dn-btn">取消</button>
11
- <button class="dn-btn dn-btn-plain">跳过</button>
12
- <button class="dn-btn dn-btn-danger">删除</button>
13
- <button class="dn-btn dn-btn-sm">导出</button>
14
- <button class="dn-btn is-focus">键盘焦点</button>
15
- <button class="dn-btn dn-btn-primary" disabled>保存</button>
16
- </div>
17
- <div class="dn-note" style="margin-top:var(--space-2)">iOS · 默认 36pt / 小号 30pt / 通栏 46pt,点击区域都补足到 44pt</div>
18
- <div class="dn-row">
19
- <button class="dn-btn dn-btn-primary dn-btn-ios">继续</button>
20
- <button class="dn-btn dn-btn-ios">稍后</button>
21
- <button class="dn-btn dn-btn-ios-sm">获取</button>
22
- <button class="dn-btn dn-btn-primary dn-btn-ios-sm">已订阅</button>
23
- <div style="width:280px"><button class="dn-btn dn-btn-primary dn-btn-ios-lg">开始使用</button></div>
24
- </div>
25
- </div>
26
- </body>
27
- </html>
@@ -1,31 +0,0 @@
1
- # Card
2
-
3
- 卡片把一条内容的摘要装进一个可以点开的块里,用于网格和看板。
4
-
5
- **需要提供**:标题、一到两行摘要、元信息(时间、分类、状态标签),点击后打开详情。
6
-
7
- **规格**
8
-
9
- - `surface` 底、`stroke-hairline` 宽的 `line` 描边,不加阴影。Mac 用 `radius-md`,iOS 用 `radius-lg`。Mac 内边距 `space-3`,iOS 内边距 14pt × `space-4`,内部间距 `space-1`。
10
- - 标题用衬线 15pt 半粗(Mac)或 17pt(iOS);摘要用 `mac-callout` 或 `ios-footnote`、`ink-secondary`,最多 3 行;元信息 11pt `ink-tertiary`。
11
- - 悬停时描边加深为 `line-control`。选中时描边变成 1pt 的 `ink`。
12
- - 网格间距 `space-2`(Mac)或 `space-3`(iOS);每张卡片高度由内容决定,同一行顶部对齐。
13
-
14
- ```swift
15
- VStack(alignment: .leading, spacing: 4) {
16
- Text(note.title).font(.system(.headline, design: .serif))
17
- Text(note.summary).font(.callout).foregroundStyle(.inkSecondary).lineLimit(3)
18
- Text(note.date, format: .relative(presentation: .named))
19
- .font(.caption).foregroundStyle(.inkTertiary)
20
- }
21
- .padding(12)
22
- .frame(maxWidth: .infinity, alignment: .leading)
23
- .background(.surface, in: .rect(cornerRadius: 8))
24
- .overlay(
25
- RoundedRectangle(cornerRadius: 8)
26
- .strokeBorder(isSelected ? .ink : .line,
27
- lineWidth: isSelected ? 1 : 1 / displayScale)
28
- )
29
- ```
30
-
31
- **不要**:给卡片加阴影或玻璃效果;在卡片左侧加一条彩色竖条;卡片里套卡片;一张卡片里放多个主按钮。
@@ -1,24 +0,0 @@
1
- <!-- @dsCard group="Content" height=170 subtitle="细线描边,不加阴影" -->
2
- <!doctype html>
3
- <html lang="zh-CN">
4
- <head><link rel="stylesheet" href="../../tokens.css"><link rel="stylesheet" href="../bundle.css"><script>(function(){function a(){var t=location.hash.slice(1),r=document.documentElement;if(t==="light"||t==="dark")r.setAttribute("data-theme",t);else r.removeAttribute("data-theme");}a();addEventListener("hashchange",a);})();</script><meta charset="utf-8"><title>Card</title></head>
5
- <body>
6
- <div class="dn-stage" style="align-items:stretch">
7
- <article class="dn-card is-interactive">
8
- <p class="dn-card-title">本周回顾</p>
9
- <p class="dn-card-body">完成 12 条笔记,整理了两个项目的待办,下周重点是首页改版。</p>
10
- <div class="dn-card-meta"><span>今天 09:41</span><span>·</span><span>工作</span></div>
11
- </article>
12
- <article class="dn-card is-selected">
13
- <p class="dn-card-title">读书摘录</p>
14
- <p class="dn-card-body">好的工具会退到背景里,让人只看见自己的工作。</p>
15
- <div class="dn-card-meta"><span class="dn-tag dn-tag-accent">已选中</span><span>昨天</span></div>
16
- </article>
17
- <article class="dn-card">
18
- <p class="dn-card-title">旅行清单</p>
19
- <p class="dn-card-body">护照、转换插头、两本书、一件薄外套。</p>
20
- <div class="dn-card-meta"><span class="dn-tag dn-tag-success"><span class="dn-dot"></span>已同步</span><span>10 月 28 日</span></div>
21
- </article>
22
- </div>
23
- </body>
24
- </html>
@@ -1,34 +0,0 @@
1
- # CodeBlock
2
-
3
- 展示代码、命令或结构化数据,带语言标签和复制按钮。
4
-
5
- **需要提供**:代码文本、语言名称;可选的文件名(替代语言名显示在表头)。
6
-
7
- **规格**
8
-
9
- - `surface-sunken` 底、`line` 细线描边、`radius-md`。表头高 28pt,左边是语言名或文件名(11pt 等宽、`ink-tertiary`),右边是一个小号文字按钮「复制」;点击后显示「已复制」1.5 秒。
10
- - 代码字体 `code-mac` 或 `code-ios`,内边距上下 `space-2`、左右 `space-3`,过长时横向滚动,不自动换行。
11
- - 语法高亮只用灰阶:关键字 `ink` 加粗,字符串 `ink-secondary`,注释 `ink-tertiary` 加斜体,其余 `ink`。这些颜色在 `surface-sunken` 上都不低于 4.5:1。
12
-
13
- ```swift
14
- VStack(alignment: .leading, spacing: 0) {
15
- HStack {
16
- Text(language).font(.caption.monospaced()).foregroundStyle(.inkTertiary)
17
- Spacer()
18
- Button(copied ? "已复制" : "复制", systemImage: copied ? "checkmark" : "doc.on.doc") { copy() }
19
- .buttonStyle(.borderless).controlSize(.small)
20
- }
21
- .padding(.horizontal, 12).frame(height: 28)
22
- Divider()
23
- ScrollView(.horizontal) {
24
- Text(highlighted) // 用 AttributedString 按上面的颜色着色
25
- .font(.system(.callout, design: .monospaced))
26
- .textSelection(.enabled)
27
- .padding(.vertical, 8).padding(.horizontal, 12)
28
- }
29
- }
30
- .background(.surfaceSunken, in: .rect(cornerRadius: 8))
31
- .overlay(RoundedRectangle(cornerRadius: 8).strokeBorder(.line, lineWidth: 1 / displayScale))
32
- ```
33
-
34
- **不要**:用彩色的编辑器配色主题(和黑白灰的整体风格冲突);给代码块加阴影;自动换行打乱缩进。
@@ -1,16 +0,0 @@
1
- <!-- @dsCard group="Content" height=170 subtitle="代码与命令" -->
2
- <!doctype html>
3
- <html lang="zh-CN">
4
- <head><link rel="stylesheet" href="../../tokens.css"><link rel="stylesheet" href="../bundle.css"><script>(function(){function a(){var t=location.hash.slice(1),r=document.documentElement;if(t==="light"||t==="dark")r.setAttribute("data-theme",t);else r.removeAttribute("data-theme");}a();addEventListener("hashchange",a);})();</script><meta charset="utf-8"><title>CodeBlock</title></head>
5
- <body>
6
- <div class="dn-stage" style="justify-content:center">
7
- <div class="dn-code">
8
- <div class="dn-code-head">swift<button class="dn-btn dn-btn-plain dn-btn-sm"><svg class="dn-icon" viewBox="0 0 24 24"><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>
9
- <pre><code><span class="tk-com">// 统计本周完成的笔记数</span>
10
- <span class="tk-kw">let</span> start = calendar.<span class="tk-type">dateInterval</span>(of: .weekOfYear, for: .now)!.start
11
- <span class="tk-kw">let</span> done = notes.filter { $0.completedAt &gt;= start }
12
- <span class="tk-kw">print</span>(<span class="tk-str">"本周完成 \(done.count) 条"</span>) <span class="tk-com">// 本周完成 <span class="tk-num">12</span> 条</span></code></pre>
13
- </div>
14
- </div>
15
- </body>
16
- </html>
@@ -1,42 +0,0 @@
1
- # Composer
2
-
3
- 多行输入框加一条工具栏,用于发消息、写笔记、向 AI 提问。
4
-
5
- **需要提供**:绑定的文本、占位符、发送动作、是否可以发送(内容为空或正在生成时禁用)。可选:附件列表、左侧的工具按钮(添加附件、模式选择)、右侧的状态文字(剩余次数、字数)。
6
-
7
- **规格**
8
-
9
- - 容器:`surface-raised` 底、`line` 细线描边、`radius-lg`、`shadow-float`;内边距上、右、下 8,左 12。获得焦点时描边加深为 `line-control`。
10
- - 文字:Mac 13pt(`mac-body`),iOS 17pt(`ios-body`)。高度随内容增长,最多 8 行,超出后内部滚动。
11
- - 附件:放在输入区上方,`surface-sunken` 底、`radius-sm` 的文件条。
12
- - 工具条:左边是 24pt 的图标按钮和 20pt 高的胶囊形选择器(`line` 描边),右边是状态文字和发送按钮。
13
- - 发送按钮:24pt 圆形,`accent` 底、`on-accent` 箭头(浅色主题黑底白箭头)。不能发送时换成 `fill-selected` 底、`ink-tertiary` 箭头。正在生成时变成「停止」方块。
14
- - Mac 上 Return 发送,Shift-Return 换行;iOS 上换行键就是换行,只能点按钮发送。
15
-
16
- ```swift
17
- VStack(alignment: .leading, spacing: 4) {
18
- TextField("写点什么,或拖入文件", text: $draft, axis: .vertical)
19
- .lineLimit(1...8)
20
- .textFieldStyle(.plain)
21
- .onSubmit(send) // Mac:Return 发送
22
- HStack(spacing: 4) {
23
- Button("添加附件", systemImage: "plus") { attach() }
24
- .labelStyle(.iconOnly).buttonStyle(.borderless)
25
- Spacer()
26
- Button(action: send) {
27
- Image(systemName: "arrow.up")
28
- .fontWeight(.semibold)
29
- .frame(width: 24, height: 24)
30
- }
31
- .buttonStyle(.borderedProminent)
32
- .buttonBorderShape(.circle)
33
- .disabled(draft.isEmpty)
34
- }
35
- }
36
- .padding(EdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 8))
37
- .background(.surfaceRaised, in: .rect(cornerRadius: 12))
38
- .overlay(RoundedRectangle(cornerRadius: 12).strokeBorder(.line, lineWidth: 1 / displayScale))
39
- .shadow(color: .black.opacity(0.08), radius: 10, y: 6)
40
- ```
41
-
42
- **不要**:把 Composer 做成玻璃(它承载的是内容);把发送按钮放到框外;禁用时把按钮直接隐藏,让布局跳动。
@@ -1,28 +0,0 @@
1
- <!-- @dsCard group="Input" height=240 subtitle="多行输入与发送" -->
2
- <!doctype html>
3
- <html lang="zh-CN">
4
- <head><link rel="stylesheet" href="../../tokens.css"><link rel="stylesheet" href="../bundle.css"><script>(function(){function a(){var t=location.hash.slice(1),r=document.documentElement;if(t==="light"||t==="dark")r.setAttribute("data-theme",t);else r.removeAttribute("data-theme");}a();addEventListener("hashchange",a);})();</script><meta charset="utf-8"><title>Composer</title></head>
5
- <body>
6
- <div class="dn-stage dn-stack" style="align-items:center">
7
- <div class="dn-composer">
8
- <div class="dn-attach"><svg class="dn-icon" viewBox="0 0 24 24" style="width:12px;height:12px"><path d="M7 3h7l5 5v13H7z"/><path d="M14 3v5h5"/></svg>周报草稿.md</div>
9
- <textarea id="c1" rows="2" aria-label="消息" placeholder="写点什么,或拖入文件">帮我把这份周报整理成三条要点</textarea>
10
- <div class="dn-composer-bar">
11
- <button class="dn-iconbtn" aria-label="添加附件"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg></button>
12
- <button class="dn-chip">标准模式<svg class="dn-icon" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></button>
13
- <span class="dn-spacer"></span>
14
- <span class="dn-note" style="margin-right:var(--space-2)">剩余 42 次</span>
15
- <button class="dn-send" aria-label="发送"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M12 19V5M6 11l6-6 6 6"/></svg></button>
16
- </div>
17
- </div>
18
- <div class="dn-composer" style="box-shadow:none">
19
- <textarea id="c2" rows="1" aria-label="消息" placeholder="写点什么,或拖入文件"></textarea>
20
- <div class="dn-composer-bar">
21
- <button class="dn-iconbtn" aria-label="添加附件"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg></button>
22
- <span class="dn-spacer"></span>
23
- <button class="dn-send" disabled aria-label="发送"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M12 19V5M6 11l6-6 6 6"/></svg></button>
24
- </div>
25
- </div>
26
- </div>
27
- </body>
28
- </html>
@@ -1,53 +0,0 @@
1
- <!-- @dsCard height=288 -->
2
- <!doctype html>
3
- <html lang="zh-CN">
4
- <head><link rel="stylesheet" href="../../tokens.css"><link rel="stylesheet" href="../bundle.css"><script>(function(){function a(){var t=location.hash.slice(1),r=document.documentElement;if(t==="light"||t==="dark")r.setAttribute("data-theme",t);else r.removeAttribute("data-theme");}a();addEventListener("hashchange",a);})();</script>
5
- <meta charset="utf-8">
6
- <title>Deernova</title>
7
- <style>
8
- html, body { margin: 0; height: 100%; }
9
- body { background: var(--canvas); color: var(--ink); font-family: var(--font-sans); overflow: hidden; }
10
- .cover { position: relative; height: 288px; overflow: hidden; }
11
- .art { position: absolute; top: 0; left: 480px; width: 480px; height: 288px; overflow: hidden; }
12
- .art svg { display: block; width: 480px; height: 288px; }
13
- .slab { fill: var(--ink); }
14
- .mid { fill: var(--line-control); }
15
- .warm { fill: var(--ink-secondary); }
16
- .state { fill: var(--success); }
17
- .blk { rx: var(--radius-md); }
18
- .glass { fill: var(--glass-fill); stroke: var(--glass-stroke); stroke-width: 1; }
19
- .cap44 { rx: 22px; }
20
- .cap28 { rx: 14px; }
21
- .cap16 { rx: 8px; }
22
- .words { position: absolute; left: var(--space-6); bottom: var(--space-6); max-width: 440px; }
23
- .name { margin: 0; font-family: var(--font-serif); font-size: 80px; line-height: 0.92; font-weight: 600; letter-spacing: -0.02em; color: var(--ink); }
24
- .tag { margin: var(--space-6) 0 0 3px; font-size: 14px; line-height: 20px; color: var(--ink-secondary); }
25
- </style>
26
- </head>
27
- <body>
28
- <div class="cover">
29
- <div class="art" aria-hidden="true">
30
- <svg viewBox="0 0 480 288" width="480" height="288">
31
- <!--
32
- blocks ink slab 168×280 bled off the top (black in light, white in dark: the brand is its contrast) · line-control 288×192 bleeding off the right edge · ink-secondary 120×76 bled off the top · success 48×16 once (the only hue, a state colour, kept small); together about 40% of 960×288
33
- arrangement one tall ink slab with a mid-grey plane beside it, bottoms on one line space-6 above the edge, gutters space-4
34
- pattern glass capsules from "纸面承载内容,玻璃承载操作": a 44pt tab-bar capsule and a 44pt circle floating across the slab/plane seam, a 28pt control group on the grey plane, all glass-fill with a glass-stroke edge
35
- scales gutters space-4, capsule gap space-3 (GlassEffectContainer spacing); block corners radius-md; capsules at control-ios (44) and control-mac-lg (28), rx half their height
36
- -->
37
- <rect class="slab blk" x="24" y="-16" width="168" height="280" rx="8"/>
38
- <rect class="warm blk" x="208" y="-20" width="120" height="76" rx="8"/>
39
- <rect class="state cap16" x="344" y="40" width="48" height="16" rx="8"/>
40
- <rect class="mid blk" x="208" y="72" width="288" height="192" rx="8"/>
41
- <rect class="glass cap44" x="56" y="196" width="176" height="44" rx="22"/>
42
- <rect class="glass cap44" x="244" y="196" width="44" height="44" rx="22"/>
43
- <rect class="glass cap28" x="344" y="92" width="28" height="28" rx="14"/>
44
- <rect class="glass cap28" x="380" y="92" width="76" height="28" rx="14"/>
45
- </svg>
46
- </div>
47
- <div class="words">
48
- <h1 class="name">Deernova</h1>
49
- <p class="tag">纸面承载内容,玻璃承载操作。</p>
50
- </div>
51
- </div>
52
- </body>
53
- </html>
@@ -1,28 +0,0 @@
1
- # EmptyState
2
-
3
- 列表、搜索结果或整个页面没有内容时显示:说明现在是什么状态,再给出下一步。
4
-
5
- **需要提供**:SF Symbol、标题(描述状态)、一句正文(告诉用户怎么开始),以及最多一个主操作和一个次操作。
6
-
7
- **规格**
8
-
9
- - 在可用区域里居中。图标 28pt、细线、`ink-tertiary` 色;标题用 `mac-display` 或 `ios-title`(衬线);正文 `ink-secondary`,最大宽度 320pt。
10
- - 按钮用大号:Mac 是 `control-mac-lg`,iOS 是 `control-ios`。
11
- - 搜索没有结果时不放按钮,标题写成「没有找到"关键词"」,正文给出修改建议。
12
-
13
- ```swift
14
- ContentUnavailableView {
15
- Label("还没有笔记", systemImage: "doc.text")
16
- .font(.system(.title2, design: .serif))
17
- } description: {
18
- Text("写下第一条想法,它会同步到你所有的设备。")
19
- } actions: {
20
- Button("新建笔记") { create() }.buttonStyle(.borderedProminent)
21
- Button("从文件导入") { importFile() }
22
- }
23
-
24
- // 搜索没有结果
25
- ContentUnavailableView.search(text: query)
26
- ```
27
-
28
- **不要**:用插画或吉祥物填满空白;标题写「空空如也」这种玩笑话;出现三个以上的按钮。