@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
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Composer 输入区
|
|
2
|
+
|
|
3
|
+
发消息、写笔记、向 AI 提问的输入区,放在对话栏底部。它承载的是用户正在写的内容,所以是纸面,不是玻璃。
|
|
4
|
+
|
|
5
|
+
由上到下三部分:可选的**提示条**(贴在输入框正上方)、**输入框**(加号、输入、语音、发送排在同一行)、**信息行**(模型、思考强度、权限模式、上下文用量,靠右)。
|
|
6
|
+
|
|
7
|
+
**需要提供**:绑定的文本、占位符、发送动作、是否可以发送(内容为空或正在生成时禁用)。可选:附件列表、提示条、模型和权限的选择、上下文用量。
|
|
8
|
+
|
|
9
|
+
## 尺寸
|
|
10
|
+
|
|
11
|
+
<!-- tokens:composer -->
|
|
12
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
13
|
+
| --- | --- | --- | --- | --- |
|
|
14
|
+
| `composer-radius` | 14 | 24 | 16 | Composer 外框圆角。 |
|
|
15
|
+
| `composer-min-height` | 44 | 44 | 48 | 单行时的高度:加号、输入、发送排在同一行,输入多行时向上长高。 |
|
|
16
|
+
<!-- /tokens -->
|
|
17
|
+
|
|
18
|
+
- 输入框:`surface-raised` 底、1px `line` 描边、`composer-radius`、`shadow-float`。单行时高 `composer-min-height`(桌面 44),加号、输入、发送垂直居中在同一行;输入多行时向上长高,最多 8 行,超出后内部滚动。获得焦点时描边加深为 `line-control`。
|
|
19
|
+
- 文字用 `type-body`;占位符写清快捷键,例如「按 ⏎ 发送,按 ⌘⏎ 加入队列稍后发送」。
|
|
20
|
+
- 加号和语音是[图标按钮](button.md)。发送按钮是 `button-height-md` 的圆形,`accent` 底、`on-accent` 箭头;不能发送时 `fill-selected` 底、`ink-tertiary` 箭头,不隐藏;正在生成时变成停止方块。
|
|
21
|
+
- 附件:在输入框内、文字上方单独一行,`surface-sunken` 底、`radius-sm` 的文件条。
|
|
22
|
+
- 提示条:宽度和输入框一致,`surface-raised` 底、`banner-radius`,单行高 `composer-min-height`,左边图标和一句话,右边关闭按钮;次要信息用 `ink-secondary`。和输入框之间 `space-2`。
|
|
23
|
+
- 信息行:输入框下方 `space-2`,靠右排列,`callout` 字号;当前值用 `ink`,说明用 `ink-secondary`;每一项都是可以点开选择的文字按钮。
|
|
24
|
+
- 指针端 Return 发送、Shift-Return 换行;触控端换行键就是换行,只能点按钮发送。
|
|
25
|
+
|
|
26
|
+
## Web
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
.dn-composer-stack { display: grid; gap: var(--space-2); width: 100%; max-width: var(--content-max); }
|
|
30
|
+
.dn-composer-notice {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
gap: var(--space-2);
|
|
34
|
+
min-height: var(--composer-min-height);
|
|
35
|
+
padding: 0 var(--space-1) 0 var(--space-3);
|
|
36
|
+
border-radius: var(--banner-radius);
|
|
37
|
+
background: var(--surface-raised);
|
|
38
|
+
color: var(--ink);
|
|
39
|
+
font: var(--type-callout);
|
|
40
|
+
}
|
|
41
|
+
.dn-composer-notice > .dn-icon { width: var(--icon-size-sm); height: var(--icon-size-sm); color: var(--ink-secondary); }
|
|
42
|
+
.dn-composer-notice .dn-muted { color: var(--ink-secondary); }
|
|
43
|
+
.dn-composer-notice .dn-icon-button { margin-left: auto; }
|
|
44
|
+
.dn-composer {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-wrap: wrap;
|
|
47
|
+
align-items: flex-end;
|
|
48
|
+
gap: var(--space-1);
|
|
49
|
+
min-height: var(--composer-min-height);
|
|
50
|
+
padding: calc((var(--composer-min-height) - var(--button-height-md)) / 2 - var(--stroke-line)) var(--space-2);
|
|
51
|
+
border: var(--stroke-line) solid var(--line);
|
|
52
|
+
border-radius: var(--composer-radius);
|
|
53
|
+
background: var(--surface-raised);
|
|
54
|
+
box-shadow: var(--shadow-float);
|
|
55
|
+
transition: border-color var(--duration-instant) var(--ease-standard);
|
|
56
|
+
}
|
|
57
|
+
.dn-composer:focus-within { border-color: var(--line-control); }
|
|
58
|
+
.dn-composer textarea {
|
|
59
|
+
flex: 1;
|
|
60
|
+
min-width: 0;
|
|
61
|
+
min-height: var(--button-height-md);
|
|
62
|
+
max-height: calc(var(--type-body-line) * 8 + 4px);
|
|
63
|
+
padding: calc((var(--button-height-md) - var(--type-body-line)) / 2) var(--space-1);
|
|
64
|
+
border: 0;
|
|
65
|
+
outline: none;
|
|
66
|
+
resize: none;
|
|
67
|
+
field-sizing: content;
|
|
68
|
+
background: transparent;
|
|
69
|
+
color: var(--ink);
|
|
70
|
+
font: var(--type-body);
|
|
71
|
+
}
|
|
72
|
+
.dn-composer textarea::placeholder { color: var(--ink-tertiary); }
|
|
73
|
+
.dn-composer-attachments { display: flex; flex-wrap: wrap; flex-basis: 100%; gap: var(--space-1); padding: 0 0 var(--space-1); }
|
|
74
|
+
.dn-attachment {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: var(--gap-inline);
|
|
78
|
+
height: var(--button-height-sm);
|
|
79
|
+
padding: 0 var(--space-2);
|
|
80
|
+
border-radius: var(--radius-sm);
|
|
81
|
+
background: var(--surface-sunken);
|
|
82
|
+
color: var(--ink-secondary);
|
|
83
|
+
font: var(--weight-regular) var(--type-callout-size)/1 var(--font-sans);
|
|
84
|
+
}
|
|
85
|
+
.dn-attachment .dn-icon { width: var(--icon-size-sm); height: var(--icon-size-sm); }
|
|
86
|
+
.dn-send {
|
|
87
|
+
position: relative;
|
|
88
|
+
display: grid;
|
|
89
|
+
place-items: center;
|
|
90
|
+
flex: none;
|
|
91
|
+
width: var(--button-height-md);
|
|
92
|
+
height: var(--button-height-md);
|
|
93
|
+
padding: 0;
|
|
94
|
+
border: 0;
|
|
95
|
+
border-radius: var(--radius-full);
|
|
96
|
+
background: var(--accent);
|
|
97
|
+
color: var(--on-accent);
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
}
|
|
100
|
+
.dn-send .dn-icon { width: var(--icon-size-sm); height: var(--icon-size-sm); stroke-width: 2.2; }
|
|
101
|
+
.dn-send:disabled { background: var(--fill-selected); color: var(--ink-tertiary); cursor: default; }
|
|
102
|
+
.dn-send:focus-visible { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
|
103
|
+
.dn-send::before {
|
|
104
|
+
content: "";
|
|
105
|
+
position: absolute;
|
|
106
|
+
top: 50%;
|
|
107
|
+
left: 50%;
|
|
108
|
+
width: max(100%, var(--hit-min));
|
|
109
|
+
height: max(100%, var(--hit-min));
|
|
110
|
+
transform: translate(-50%, -50%);
|
|
111
|
+
}
|
|
112
|
+
.dn-composer-meta { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 2px; }
|
|
113
|
+
.dn-meta-button {
|
|
114
|
+
position: relative;
|
|
115
|
+
display: inline-flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: var(--gap-inline);
|
|
118
|
+
height: var(--button-height-sm);
|
|
119
|
+
padding: 0 var(--space-2);
|
|
120
|
+
border: 0;
|
|
121
|
+
border-radius: var(--button-radius-sm);
|
|
122
|
+
background: transparent;
|
|
123
|
+
color: var(--ink-secondary);
|
|
124
|
+
font: var(--weight-regular) var(--type-callout-size)/1 var(--font-sans);
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
}
|
|
127
|
+
.dn-meta-button:hover { background: var(--fill-hover); }
|
|
128
|
+
.dn-meta-button b { color: var(--ink); font-weight: var(--weight-regular); }
|
|
129
|
+
.dn-context-ring {
|
|
130
|
+
width: var(--icon-size-sm);
|
|
131
|
+
height: var(--icon-size-sm);
|
|
132
|
+
border-radius: var(--radius-full);
|
|
133
|
+
background: conic-gradient(var(--ink-secondary) calc(var(--used, 0.3) * 1turn), var(--fill-selected) 0);
|
|
134
|
+
-webkit-mask: radial-gradient(circle, transparent 55%, #000 57%);
|
|
135
|
+
mask: radial-gradient(circle, transparent 55%, #000 57%);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<div class="dn-composer-stack">
|
|
141
|
+
<div class="dn-composer-notice" role="status">
|
|
142
|
+
<svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>
|
|
143
|
+
<span>本周额度将提前用完 <span class="dn-muted">已使用 52% · 11 月 9 日重置</span></span>
|
|
144
|
+
<button class="dn-icon-button" type="button" aria-label="关闭提示"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="dn-composer">
|
|
147
|
+
<div class="dn-composer-attachments"><span class="dn-attachment"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3h7l5 5v13H7z"/><path d="M14 3v5h5"/></svg>周报草稿.md</span></div>
|
|
148
|
+
<button class="dn-icon-button" type="button" aria-label="添加附件"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button>
|
|
149
|
+
<textarea rows="1" aria-label="消息" placeholder="按 ⏎ 发送,按 ⌘⏎ 加入队列稍后发送"></textarea>
|
|
150
|
+
<button class="dn-icon-button" type="button" aria-label="语音输入"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3"/></svg></button>
|
|
151
|
+
<button class="dn-send" type="button" aria-label="发送" disabled><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 19V5M6 11l6-6 6 6"/></svg></button>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="dn-composer-meta">
|
|
154
|
+
<button class="dn-meta-button" type="button"><b>Opus 5.5</b>高</button>
|
|
155
|
+
<button class="dn-meta-button" type="button"><b>询问权限</b></button>
|
|
156
|
+
<span class="dn-context-ring" role="img" aria-label="上下文已用 30%"></span>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## SwiftUI
|
|
162
|
+
|
|
163
|
+
```swift
|
|
164
|
+
VStack(alignment: .trailing, spacing: Deernova.space2) {
|
|
165
|
+
HStack(alignment: .bottom, spacing: Deernova.space1) {
|
|
166
|
+
Button("添加附件", systemImage: "plus") { attach() }
|
|
167
|
+
.labelStyle(.iconOnly).buttonStyle(.borderless)
|
|
168
|
+
TextField("按 ⏎ 发送,按 ⌘⏎ 加入队列稍后发送", text: $draft, axis: .vertical)
|
|
169
|
+
.lineLimit(1...8)
|
|
170
|
+
.textFieldStyle(.plain)
|
|
171
|
+
.font(.deernovaBody)
|
|
172
|
+
.onSubmit(send) // Mac:Return 发送
|
|
173
|
+
Button("语音输入", systemImage: "mic") { dictate() }
|
|
174
|
+
.labelStyle(.iconOnly).buttonStyle(.borderless)
|
|
175
|
+
Button(action: send) {
|
|
176
|
+
Image(systemName: "arrow.up").fontWeight(.semibold)
|
|
177
|
+
.frame(width: Deernova.buttonHeightMd, height: Deernova.buttonHeightMd)
|
|
178
|
+
}
|
|
179
|
+
.buttonStyle(.borderedProminent)
|
|
180
|
+
.buttonBorderShape(.circle)
|
|
181
|
+
.disabled(draft.isEmpty)
|
|
182
|
+
}
|
|
183
|
+
.padding(.horizontal, Deernova.space2)
|
|
184
|
+
.frame(minHeight: Deernova.composerMinHeight)
|
|
185
|
+
.background(.surfaceRaised, in: .rect(cornerRadius: Deernova.composerRadius))
|
|
186
|
+
.overlay(RoundedRectangle(cornerRadius: Deernova.composerRadius).strokeBorder(.line, lineWidth: Deernova.strokeLine))
|
|
187
|
+
.shadow(color: .black.opacity(0.08), radius: 10, y: 6)
|
|
188
|
+
|
|
189
|
+
HStack(spacing: Deernova.space2) { // 信息行:模型、权限
|
|
190
|
+
Menu("Opus 5.5 · 高") { … }
|
|
191
|
+
Menu("询问权限") { … }
|
|
192
|
+
}
|
|
193
|
+
.font(.deernovaCallout)
|
|
194
|
+
.menuStyle(.borderlessButton)
|
|
195
|
+
.foregroundStyle(.inkSecondary)
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
放在页面底部时用 `.safeAreaInset(edge: .bottom)`,让内容可以滚到它的上方。
|
|
200
|
+
|
|
201
|
+
## 不要
|
|
202
|
+
|
|
203
|
+
- 把 Composer 做成玻璃(它承载的是内容)。
|
|
204
|
+
- 把模型、权限这类设置塞进输入框里;把发送按钮放到框外。
|
|
205
|
+
- 禁用时把按钮直接隐藏,让布局跳动。
|
|
206
|
+
- 提示条和输入框宽度不一致,或者离得很远。
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 空状态
|
|
2
|
+
|
|
3
|
+
列表、搜索结果或整个页面没有内容时显示:说明现在是什么状态,再给出下一步。
|
|
4
|
+
|
|
5
|
+
**需要提供**:图标、标题(描述状态)、一句正文(告诉用户怎么开始),以及最多一个主操作和一个次操作。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:empty-state -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `empty-title-size` | 24 | 20 | 32 | 空状态标题(衬线)字号。 |
|
|
13
|
+
| `empty-icon-size` | 32 | 36 | 40 | 空状态图标的边长。 |
|
|
14
|
+
<!-- /tokens -->
|
|
15
|
+
|
|
16
|
+
- 在可用区域里居中。图标 `empty-icon-size`、细线、`ink-tertiary`;标题用衬线 `empty-title-size`、半粗;正文 `type-body`、`ink-secondary`,最大宽度 320。
|
|
17
|
+
- 按钮用[大号](button.md)。
|
|
18
|
+
- 搜索没有结果时不放按钮,标题写成「没有找到“关键词”」,正文给出修改建议。
|
|
19
|
+
|
|
20
|
+
## Web
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
.dn-empty { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); padding: var(--space-6) var(--space-5); text-align: center; }
|
|
24
|
+
.dn-empty > .dn-icon { width: var(--empty-icon-size); height: var(--empty-icon-size); color: var(--ink-tertiary); stroke-width: 1.4; }
|
|
25
|
+
.dn-empty-title { margin: var(--space-2) 0 0; color: var(--ink); font: var(--weight-semibold) var(--empty-title-size)/1.25 var(--font-serif); letter-spacing: var(--type-display-tracking); }
|
|
26
|
+
.dn-empty-body { max-width: 320px; margin: 0 0 var(--space-3); color: var(--ink-secondary); font: var(--type-body); }
|
|
27
|
+
.dn-empty-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--gap-stack); }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<div class="dn-empty">
|
|
32
|
+
<svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3h7l5 5v13H7z"/><path d="M14 3v5h5"/></svg>
|
|
33
|
+
<h3 class="dn-empty-title">还没有笔记</h3>
|
|
34
|
+
<p class="dn-empty-body">写下第一条想法,它会同步到你所有的设备。</p>
|
|
35
|
+
<div class="dn-empty-actions">
|
|
36
|
+
<button class="dn-button is-primary is-lg" type="button">新建笔记</button>
|
|
37
|
+
<button class="dn-button is-lg" type="button">从文件导入</button>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## SwiftUI
|
|
43
|
+
|
|
44
|
+
```swift
|
|
45
|
+
ContentUnavailableView {
|
|
46
|
+
Label("还没有笔记", systemImage: "doc.text")
|
|
47
|
+
.font(.system(size: Deernova.emptyTitleSize, weight: .semibold, design: .serif))
|
|
48
|
+
} description: {
|
|
49
|
+
Text("写下第一条想法,它会同步到你所有的设备。")
|
|
50
|
+
} actions: {
|
|
51
|
+
Button("新建笔记") { create() }.buttonStyle(.borderedProminent).controlSize(.large)
|
|
52
|
+
Button("从文件导入") { importFile() }.controlSize(.large)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 搜索没有结果
|
|
56
|
+
ContentUnavailableView.search(text: query)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 不要
|
|
60
|
+
|
|
61
|
+
- 用插画或吉祥物填满空白。
|
|
62
|
+
- 标题写「空空如也」这种玩笑话。
|
|
63
|
+
- 出现三个以上的按钮。
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# 玻璃操作层
|
|
2
|
+
|
|
3
|
+
浮在内容之上的玻璃操作层:SwiftUI 端的工具栏按钮组、标签栏、浮动操作按钮,以及官网与文档的页眉。规则见 [玻璃](../foundations/glass.md)。
|
|
4
|
+
|
|
5
|
+
**Web 应用不用玻璃**:顶栏和工具按钮见 [顶栏与侧面板](toolbar.md)。
|
|
6
|
+
|
|
7
|
+
**什么时候用**:用户在浏览内容时随时要用到的操作,比如新建、搜索、切换页面、分享。内容本身永远不放在玻璃上。
|
|
8
|
+
|
|
9
|
+
**需要提供**:每个按钮的图标和无障碍标签、点击动作;如果是标签栏,还要提供当前选中项。
|
|
10
|
+
|
|
11
|
+
## 尺寸
|
|
12
|
+
|
|
13
|
+
<!-- tokens:glass-controls -->
|
|
14
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
15
|
+
| --- | --- | --- | --- | --- |
|
|
16
|
+
| `glass-button-size` | 34 | 44 | 36 | 玻璃按钮的边长:macOS 26 工具栏按钮约 34(系统绘制,这里只作参考和自绘时对齐),官网页眉的图标按钮也用它;按钮组里的按钮比它小 8。Web 应用的工具栏不用玻璃,用 button-height-md。 |
|
|
17
|
+
<!-- /tokens -->
|
|
18
|
+
|
|
19
|
+
- 单个玻璃按钮:圆形,边长 `glass-button-size`,图标 `icon-size-lg`。macOS 26 的工具栏按钮由系统绘制,约 34,和红绿灯在同一条线上,见 [macOS](../scenes/macos.md)。
|
|
20
|
+
- 按钮组:胶囊形,内边距 3,组内按钮比单个按钮小 8,间距 2。相邻的玻璃元素放进同一组,组与组之间 `space-3`。
|
|
21
|
+
- 选中项:图标从 `ink-secondary` 变成 `ink`,底色 `fill-selected`。
|
|
22
|
+
- 实色玻璃(`accent` 底、`on-accent` 图标)一屏只用一次,留给主操作,比如「新建」。
|
|
23
|
+
|
|
24
|
+
## Web
|
|
25
|
+
|
|
26
|
+
只用于官网与文档:网页没有系统玻璃,页眉用半透明填充加背景模糊近似。Web 应用里不要用。
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
.dn-glass {
|
|
30
|
+
border: var(--stroke-line) solid var(--glass-stroke);
|
|
31
|
+
border-radius: var(--radius-full);
|
|
32
|
+
background: var(--glass-fill);
|
|
33
|
+
box-shadow: var(--shadow-glass);
|
|
34
|
+
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
35
|
+
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
|
36
|
+
}
|
|
37
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
38
|
+
.dn-glass { background: var(--surface-raised); -webkit-backdrop-filter: none; backdrop-filter: none; }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.dn-glass-button {
|
|
42
|
+
position: relative;
|
|
43
|
+
display: inline-grid;
|
|
44
|
+
place-items: center;
|
|
45
|
+
flex: none;
|
|
46
|
+
width: var(--glass-button-size);
|
|
47
|
+
height: var(--glass-button-size);
|
|
48
|
+
padding: 0;
|
|
49
|
+
border-radius: var(--radius-full);
|
|
50
|
+
color: var(--ink);
|
|
51
|
+
font: inherit;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
.dn-glass-button .dn-icon { width: var(--icon-size-lg); height: var(--icon-size-lg); }
|
|
55
|
+
.dn-glass-button.is-prominent { border-color: transparent; background: var(--accent); color: var(--on-accent); }
|
|
56
|
+
.dn-glass-button:focus-visible { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
|
57
|
+
.dn-glass-button::before {
|
|
58
|
+
content: "";
|
|
59
|
+
position: absolute;
|
|
60
|
+
top: 50%;
|
|
61
|
+
left: 50%;
|
|
62
|
+
width: max(100%, var(--hit-min));
|
|
63
|
+
height: max(100%, var(--hit-min));
|
|
64
|
+
transform: translate(-50%, -50%);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.dn-glass-group { display: inline-flex; align-items: center; gap: 2px; padding: 3px; }
|
|
68
|
+
.dn-glass-group .dn-glass-button {
|
|
69
|
+
width: calc(var(--glass-button-size) - 8px);
|
|
70
|
+
height: calc(var(--glass-button-size) - 8px);
|
|
71
|
+
border: 0;
|
|
72
|
+
background: transparent;
|
|
73
|
+
box-shadow: none;
|
|
74
|
+
color: var(--ink-secondary);
|
|
75
|
+
}
|
|
76
|
+
.dn-glass-group .dn-glass-button[aria-current="page"], .dn-glass-group .dn-glass-button[aria-pressed="true"] { background: var(--fill-selected); color: var(--ink); }
|
|
77
|
+
.dn-glass-group .dn-glass-button .dn-icon { width: var(--icon-size-md); height: var(--icon-size-md); }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<div class="dn-glass-demo">
|
|
82
|
+
<div class="dn-row">
|
|
83
|
+
<nav class="dn-glass dn-glass-group" aria-label="页面">
|
|
84
|
+
<button class="dn-glass-button" type="button" aria-label="笔记" aria-current="page"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3h7l5 5v13H7z"/><path d="M14 3v5h5"/></svg></button>
|
|
85
|
+
<button class="dn-glass-button" type="button" aria-label="收藏"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4l2.4 5 5.6.6-4.2 3.8 1.2 5.5L12 16l-5 2.9 1.2-5.5L4 9.6l5.6-.6z"/></svg></button>
|
|
86
|
+
<button class="dn-glass-button" type="button" aria-label="设置"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/></svg></button>
|
|
87
|
+
</nav>
|
|
88
|
+
<button class="dn-glass dn-glass-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>
|
|
89
|
+
<button class="dn-glass dn-glass-button is-prominent" type="button" aria-label="新建"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## SwiftUI
|
|
95
|
+
|
|
96
|
+
```swift
|
|
97
|
+
// 标准标签栏与工具栏,系统自动渲染为玻璃
|
|
98
|
+
TabView {
|
|
99
|
+
Tab("笔记", systemImage: "doc.text") { NotesView() }
|
|
100
|
+
Tab("收藏", systemImage: "star") { FavoritesView() }
|
|
101
|
+
Tab(role: .search) { SearchView() } // 搜索单独成为右侧的圆形玻璃按钮
|
|
102
|
+
}
|
|
103
|
+
.tabBarMinimizeBehavior(.onScrollDown)
|
|
104
|
+
.tint(.brandAccent)
|
|
105
|
+
|
|
106
|
+
// 自定义浮动按钮:放进同一个容器,互相融合
|
|
107
|
+
GlassEffectContainer(spacing: 12) {
|
|
108
|
+
HStack(spacing: 12) {
|
|
109
|
+
Button("搜索", systemImage: "magnifyingglass") { search() }
|
|
110
|
+
.buttonStyle(.glass)
|
|
111
|
+
Button("新建", systemImage: "plus") { create() }
|
|
112
|
+
.buttonStyle(.glassProminent)
|
|
113
|
+
}
|
|
114
|
+
.labelStyle(.iconOnly)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 完全自定义形状时
|
|
118
|
+
Image(systemName: "mic")
|
|
119
|
+
.frame(width: Deernova.glassButtonSize, height: Deernova.glassButtonSize)
|
|
120
|
+
.glassEffect(.regular.interactive(), in: .circle)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 不要
|
|
124
|
+
|
|
125
|
+
- 在玻璃上放段落文字或列表;玻璃叠玻璃。
|
|
126
|
+
- 给工具栏加实色背景挡住滚动边缘效果。
|
|
127
|
+
- 同一屏出现两个实色玻璃。
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# 分组列表与设置页
|
|
2
|
+
|
|
3
|
+
分组列表,用于设置页、详情页和各种表单:一组组白色的列表浮在 `canvas` 上。
|
|
4
|
+
|
|
5
|
+
**需要提供**:分组,每个分组可以有页头和页脚说明;每一行提供标题,以及可选的图标、右侧的值、开关或箭头,点击后跳转或执行动作。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:list -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `row-icon-size` | 20 | 28 | 24 | 列表行左侧图标底的边长。 |
|
|
13
|
+
| `switch-width` | 36 | 51 | 44 | 开关宽度。 |
|
|
14
|
+
| `switch-height` | 20 | 31 | 24 | 开关高度;圆钮直径 = 高度 − 4。 |
|
|
15
|
+
<!-- /tokens -->
|
|
16
|
+
|
|
17
|
+
行高、行内边距沿用[侧边栏](sidebar.md)的 `row-height`、`row-padding-x`;分组圆角沿用[卡片](card.md)的 `card-radius`。
|
|
18
|
+
|
|
19
|
+
- 页面底色 `canvas`,分组底色 `surface-raised`,分组之间 `gap-group`。
|
|
20
|
+
- 每行最小高度 `row-height`,文字 `type-body`、`ink`;右侧的值用 `ink-secondary`,箭头用 `ink-tertiary`。
|
|
21
|
+
- 图标放在边长 `row-icon-size` 的方块里,`accent-soft` 底、`ink` 图标、`radius-sm`。整个列表统一用灰色,不要每行一个颜色。
|
|
22
|
+
- 分隔线 `stroke-line`、`line` 色,从文字起点开始。
|
|
23
|
+
- 页头 `footnote` 字号、`ink-secondary`;页脚 `footnote` 字号、`ink-tertiary`,写清楚这个设置会带来什么后果。
|
|
24
|
+
- 开关打开时用 `success`。不要用黑白色调的开关:深色主题下白色轨道会把白色圆钮吞掉。
|
|
25
|
+
|
|
26
|
+
## Web
|
|
27
|
+
|
|
28
|
+
开关用原生的 `<input type="checkbox" role="switch">`,保留键盘和读屏支持。
|
|
29
|
+
|
|
30
|
+
```css
|
|
31
|
+
.dn-list-section { display: grid; gap: 6px; }
|
|
32
|
+
.dn-list-header, .dn-list-footer { margin: 0; padding: 0 var(--row-padding-x); font: var(--weight-regular) var(--type-footnote-size)/var(--type-footnote-line) var(--font-sans); }
|
|
33
|
+
.dn-list-header { color: var(--ink-secondary); }
|
|
34
|
+
.dn-list-footer { color: var(--ink-tertiary); }
|
|
35
|
+
.dn-list { margin: 0; padding: 0; overflow: hidden; list-style: none; border-radius: var(--card-radius); background: var(--surface-raised); }
|
|
36
|
+
.dn-list-row {
|
|
37
|
+
--separator-inset: var(--row-padding-x);
|
|
38
|
+
position: relative;
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: var(--space-3);
|
|
42
|
+
min-height: var(--row-height);
|
|
43
|
+
padding: 0 var(--row-padding-x);
|
|
44
|
+
color: var(--ink);
|
|
45
|
+
font: var(--type-body);
|
|
46
|
+
}
|
|
47
|
+
.dn-list-row.has-icon { --separator-inset: calc(var(--row-padding-x) + var(--row-icon-size) + var(--space-3)); }
|
|
48
|
+
.dn-list-row + .dn-list-row::before {
|
|
49
|
+
content: "";
|
|
50
|
+
position: absolute;
|
|
51
|
+
top: 0;
|
|
52
|
+
right: 0;
|
|
53
|
+
left: var(--separator-inset);
|
|
54
|
+
border-top: var(--stroke-line) var(--line-style-divider) var(--line);
|
|
55
|
+
}
|
|
56
|
+
.dn-list-icon { display: grid; place-items: center; flex: none; width: var(--row-icon-size); height: var(--row-icon-size); border-radius: var(--radius-sm); background: var(--accent-soft); color: var(--ink); }
|
|
57
|
+
.dn-list-icon .dn-icon { width: calc(var(--row-icon-size) * 0.6); height: calc(var(--row-icon-size) * 0.6); }
|
|
58
|
+
.dn-list-value { margin-left: auto; color: var(--ink-secondary); white-space: nowrap; }
|
|
59
|
+
.dn-list-row .dn-chevron { width: var(--icon-size-sm); height: var(--icon-size-sm); color: var(--ink-tertiary); }
|
|
60
|
+
|
|
61
|
+
.dn-switch {
|
|
62
|
+
position: relative;
|
|
63
|
+
flex: none;
|
|
64
|
+
width: var(--switch-width);
|
|
65
|
+
height: var(--switch-height);
|
|
66
|
+
margin: 0 0 0 auto;
|
|
67
|
+
border-radius: var(--radius-full);
|
|
68
|
+
background: var(--fill-selected);
|
|
69
|
+
appearance: none;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
transition: background-color var(--duration-base) var(--ease-standard);
|
|
72
|
+
}
|
|
73
|
+
.dn-switch::after {
|
|
74
|
+
content: "";
|
|
75
|
+
position: absolute;
|
|
76
|
+
top: 2px;
|
|
77
|
+
left: 2px;
|
|
78
|
+
width: calc(var(--switch-height) - 4px);
|
|
79
|
+
height: calc(var(--switch-height) - 4px);
|
|
80
|
+
border-radius: var(--radius-full);
|
|
81
|
+
background: var(--knob);
|
|
82
|
+
box-shadow: var(--shadow-control);
|
|
83
|
+
transition: transform var(--duration-base) var(--ease-standard);
|
|
84
|
+
}
|
|
85
|
+
.dn-switch:checked { background: var(--success); }
|
|
86
|
+
.dn-switch:checked::after { transform: translateX(calc(var(--switch-width) - var(--switch-height))); }
|
|
87
|
+
.dn-switch:focus-visible { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<section class="dn-list-section">
|
|
92
|
+
<h3 class="dn-list-header">同步</h3>
|
|
93
|
+
<ul class="dn-list">
|
|
94
|
+
<li class="dn-list-row has-icon"><span class="dn-list-icon"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 20a8 8 0 0 1 16 0"/></svg></span>个人资料<span class="dn-list-value">林一</span><svg class="dn-icon dn-chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 6l6 6-6 6"/></svg></li>
|
|
95
|
+
<li class="dn-list-row has-icon"><span class="dn-list-icon"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 18h10a4 4 0 0 0 .5-8 6 6 0 0 0-11.5 1.5A3.5 3.5 0 0 0 7 18z"/></svg></span><label for="sync">iCloud 同步</label><input class="dn-switch" id="sync" type="checkbox" role="switch" checked></li>
|
|
96
|
+
</ul>
|
|
97
|
+
<p class="dn-list-footer">关闭 iCloud 同步后,新笔记只保存在这台设备上。</p>
|
|
98
|
+
</section>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## SwiftUI
|
|
102
|
+
|
|
103
|
+
```swift
|
|
104
|
+
Form {
|
|
105
|
+
Section("账号") {
|
|
106
|
+
NavigationLink { ProfileView() } label: {
|
|
107
|
+
LabeledContent { Text("林一") } label: { Label("个人资料", systemImage: "person") }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
Section {
|
|
111
|
+
Toggle(isOn: $iCloudSync) { Label("iCloud 同步", systemImage: "icloud") }
|
|
112
|
+
.tint(.success)
|
|
113
|
+
} header: {
|
|
114
|
+
Text("同步")
|
|
115
|
+
} footer: {
|
|
116
|
+
Text("关闭 iCloud 同步后,新笔记只保存在这台设备上。")
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
.scrollContentBackground(.hidden)
|
|
120
|
+
.background(.canvas)
|
|
121
|
+
.tint(.brandAccent)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Mac 上的设置窗口同样用 `Form` 加 `.formStyle(.grouped)`,系统会给出对应的 Mac 样式。
|
|
125
|
+
|
|
126
|
+
## 不要
|
|
127
|
+
|
|
128
|
+
- 每行用不同颜色的图标。
|
|
129
|
+
- 页脚写成一大段。
|
|
130
|
+
- 在列表行里放主按钮(主按钮放在页面底部)。
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# 消息
|
|
2
|
+
|
|
3
|
+
对话里的一条消息。用户说的话放在比内容区亮一层的气泡里,靠右;AI 的回答直接排在内容区上,用和界面一样的无衬线正文,靠左。执行过程见 [工具调用与执行步骤](step-row.md),任务进度见 [任务清单](task-list.md)。
|
|
4
|
+
|
|
5
|
+
**需要提供**:发送者(用户或 AI)、内容(Markdown,可以包含代码块、列表、链接)、AI 消息的操作(复制、重新生成),以及生成中的状态。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:message -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `message-font-size` | 15 | 15 | 15 | 用户消息气泡的字号。AI 回答用 type-body,和界面正文一致。 |
|
|
13
|
+
| `message-action-size` | 24 | 32 | 28 | AI 消息下方操作按钮的可见尺寸。 |
|
|
14
|
+
<!-- /tokens -->
|
|
15
|
+
|
|
16
|
+
- 用户消息:靠右,最大宽度 80%,`surface-raised` 底,`radius-lg`,内边距上下 `space-3`、左右 `space-4`,字号 `message-font-size`、行高同 `type-body`。
|
|
17
|
+
- AI 消息:靠左,不加气泡,正文用 `type-body`(桌面 15/24),栏宽不超过 `content-max`。段落之间 `space-3`;加粗用 600;列表缩进一级、项与项之间 4。代码见[代码块](code-block.md),行内代码用等宽字加 `fill-selected` 底。
|
|
18
|
+
- 消息、工具调用行、卡片之间统一 `space-4`。
|
|
19
|
+
- 操作按钮放在 AI 消息下方,`message-action-size` 的图标按钮,`ink-tertiary` 色;指针端只在悬停时显示,触控端一直显示。
|
|
20
|
+
- 链接用 `ink` 色,加 1px 的 `line-control` 色下划线,下偏移 3px。
|
|
21
|
+
- 生成中:回答末尾显示「正在运行」状态(见 [工具调用与执行步骤](step-row.md)),Composer 的发送按钮变成「停止」。
|
|
22
|
+
|
|
23
|
+
## Web
|
|
24
|
+
|
|
25
|
+
```css
|
|
26
|
+
.dn-thread { display: flex; flex-direction: column; gap: var(--space-4); max-width: var(--content-max); }
|
|
27
|
+
.dn-message-user {
|
|
28
|
+
align-self: flex-end;
|
|
29
|
+
max-width: 80%;
|
|
30
|
+
padding: var(--space-3) var(--space-4);
|
|
31
|
+
border-radius: var(--radius-lg);
|
|
32
|
+
background: var(--surface-raised);
|
|
33
|
+
color: var(--ink);
|
|
34
|
+
font: var(--weight-regular) var(--message-font-size)/var(--type-body-line) var(--font-sans);
|
|
35
|
+
}
|
|
36
|
+
.dn-message-assistant { color: var(--ink); font: var(--type-body); }
|
|
37
|
+
.dn-message-assistant p { margin: 0 0 var(--space-3); }
|
|
38
|
+
.dn-message-assistant p:last-of-type { margin-bottom: 0; }
|
|
39
|
+
.dn-message-assistant ul, .dn-message-assistant ol { margin: 0 0 var(--space-3); padding-left: 1.4em; }
|
|
40
|
+
.dn-message-assistant li + li { margin-top: var(--space-1); }
|
|
41
|
+
.dn-message-assistant strong { font-weight: var(--weight-semibold); }
|
|
42
|
+
.dn-message-assistant code { padding: 1px 5px; border-radius: var(--radius-xs); background: var(--fill-selected); font: 0.9em/1 var(--font-mono); }
|
|
43
|
+
.dn-message-assistant a { color: var(--ink); text-decoration: underline 1px var(--line-control); text-underline-offset: 3px; }
|
|
44
|
+
.dn-message-actions { display: flex; gap: 2px; margin: var(--space-2) 0 0 calc(var(--space-1) * -1); }
|
|
45
|
+
.dn-message-actions .dn-icon-button { width: var(--message-action-size); height: var(--message-action-size); color: var(--ink-tertiary); }
|
|
46
|
+
.dn-message-actions .dn-icon { width: var(--icon-size-sm); height: var(--icon-size-sm); }
|
|
47
|
+
@media (hover: hover) and (pointer: fine) {
|
|
48
|
+
.dn-message-actions { opacity: 0; transition: opacity var(--duration-instant) var(--ease-standard); }
|
|
49
|
+
.dn-message-assistant:hover .dn-message-actions, .dn-message-actions:focus-within { opacity: 1; }
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<div class="dn-thread">
|
|
55
|
+
<div class="dn-message-user">帮我把这份周报整理成三条要点</div>
|
|
56
|
+
<div class="dn-message-assistant">
|
|
57
|
+
<p>这周的进展可以归成三件事:</p>
|
|
58
|
+
<ul>
|
|
59
|
+
<li><strong>首页改版定稿。</strong>信息层级按「最近、收藏、全部」排列,下周三前交给开发。</li>
|
|
60
|
+
<li><strong>同步问题修复。</strong>离线编辑后重复保存的问题已经解决。</li>
|
|
61
|
+
<li><strong>订阅转化提升。</strong>年付转化率从 3.1% 升到 4.4%。</li>
|
|
62
|
+
</ul>
|
|
63
|
+
<p>需要的话,我可以按 <a href="#">周报模板</a> 再排一次,或者导出成 <code>weekly.md</code>。</p>
|
|
64
|
+
<div class="dn-message-actions">
|
|
65
|
+
<button class="dn-icon-button" type="button" aria-label="复制"><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>
|
|
66
|
+
<button class="dn-icon-button" type="button" aria-label="重新生成"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12a8 8 0 1 0 2.3-5.6"/><path d="M4 4v4h4"/></svg></button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## SwiftUI
|
|
73
|
+
|
|
74
|
+
```swift
|
|
75
|
+
// 用户消息
|
|
76
|
+
Text(message.text)
|
|
77
|
+
.font(.system(size: Deernova.messageFontSize))
|
|
78
|
+
.padding(.vertical, Deernova.space3).padding(.horizontal, Deernova.space4)
|
|
79
|
+
.background(.surfaceRaised, in: .rect(cornerRadius: Deernova.radiusLg))
|
|
80
|
+
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
81
|
+
|
|
82
|
+
// AI 消息:用 AttributedString(markdown:) 渲染,或接入 Markdown 渲染库
|
|
83
|
+
Text(try AttributedString(markdown: message.text))
|
|
84
|
+
.font(.deernovaBody)
|
|
85
|
+
.lineSpacing(6)
|
|
86
|
+
.textSelection(.enabled)
|
|
87
|
+
.frame(maxWidth: Deernova.contentMax, alignment: .leading)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 不要
|
|
91
|
+
|
|
92
|
+
- 给 AI 回答加气泡,或者用衬线排 AI 回答。
|
|
93
|
+
- 用头像区分谁在说话(靠位置就够了)。
|
|
94
|
+
- 在正文里用彩色强调文字(强调用加粗)。
|