@deernova/style 0.0.0-stage → 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.
- package/README.md +18 -2
- package/bin/cli.mjs +35 -0
- package/package.json +17 -4
- package/skill/SKILL.md +38 -0
- package/skill/assets/design/README.md +170 -0
- package/skill/assets/design/components/Banner/README.md +33 -0
- package/skill/assets/design/components/Banner/preview.html +26 -0
- package/skill/assets/design/components/Button/README.md +59 -0
- package/skill/assets/design/components/Button/preview.html +27 -0
- package/skill/assets/design/components/Card/README.md +31 -0
- package/skill/assets/design/components/Card/preview.html +24 -0
- package/skill/assets/design/components/CodeBlock/README.md +34 -0
- package/skill/assets/design/components/CodeBlock/preview.html +16 -0
- package/skill/assets/design/components/Composer/README.md +42 -0
- package/skill/assets/design/components/Composer/preview.html +28 -0
- package/skill/assets/design/components/Cover/preview.html +53 -0
- package/skill/assets/design/components/EmptyState/README.md +28 -0
- package/skill/assets/design/components/EmptyState/preview.html +16 -0
- package/skill/assets/design/components/GlassControls/README.md +43 -0
- package/skill/assets/design/components/GlassControls/preview.html +40 -0
- package/skill/assets/design/components/ListGroup/README.md +39 -0
- package/skill/assets/design/components/ListGroup/preview.html +22 -0
- package/skill/assets/design/components/MacWindow/README.md +27 -0
- package/skill/assets/design/components/MacWindow/preview.html +70 -0
- package/skill/assets/design/components/Message/README.md +31 -0
- package/skill/assets/design/components/Message/preview.html +21 -0
- package/skill/assets/design/components/PhoneScreen/README.md +25 -0
- package/skill/assets/design/components/PhoneScreen/preview.html +74 -0
- package/skill/assets/design/components/Segmented/README.md +21 -0
- package/skill/assets/design/components/Segmented/preview.html +21 -0
- package/skill/assets/design/components/Sheet/README.md +29 -0
- package/skill/assets/design/components/Sheet/preview.html +31 -0
- package/skill/assets/design/components/Sidebar/README.md +36 -0
- package/skill/assets/design/components/Sidebar/preview.html +20 -0
- package/skill/assets/design/components/StepRow/README.md +35 -0
- package/skill/assets/design/components/StepRow/preview.html +17 -0
- package/skill/assets/design/components/Tag/README.md +36 -0
- package/skill/assets/design/components/Tag/preview.html +22 -0
- package/skill/assets/design/components/TextField/README.md +29 -0
- package/skill/assets/design/components/TextField/preview.html +28 -0
- package/skill/assets/design/components/bundle.css +189 -0
- package/skill/assets/design/design-system.json +25 -0
- 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 +187 -0
- package/skill/assets/design/tokens.json +142 -0
- package/skill/manifest.json +24 -0
- package/skill/references/components.md +648 -0
- package/skill/references/foundations.md +170 -0
- package/skill/references/native.md +7 -0
- package/skill/references/review.md +13 -0
- package/skill/references/tokens.md +74 -0
- package/skill/references/web.md +11 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Sheet
|
|
2
|
+
|
|
3
|
+
从底部弹出的面板,用于一个短小、独立的任务:确认购买、编辑一个字段、选择分享方式。
|
|
4
|
+
|
|
5
|
+
**需要提供**:标题、一两句说明、内容(短表单或选项)、主操作和取消操作,以及要支持的高度档位。
|
|
6
|
+
|
|
7
|
+
**规格**
|
|
8
|
+
|
|
9
|
+
- iOS 26 上,系统会把中等高度的弹窗渲染成悬浮的玻璃面板,四周留出间距;拉到全高后变成不透明。直接使用系统样式,不要自己画背景。
|
|
10
|
+
- 顶部拖拽条 36×5pt,`line-control` 色。
|
|
11
|
+
- 标题用 `ios-title`(衬线),说明用 `ios-callout` `ink-secondary`,下方留 `space-4`。
|
|
12
|
+
- 主操作是通栏 46pt 胶囊主按钮(`control-ios-lg`),下面是同尺寸的文字按钮「暂不需要」,两者间距 `space-1`。
|
|
13
|
+
- 内容超过一屏,或需要长时间阅读时,改成全高弹窗或推入新页面,背景用 `canvas`。
|
|
14
|
+
- Mac 上对应的是附着在窗口上的 sheet 和系统 Alert,同样直接使用系统样式。
|
|
15
|
+
|
|
16
|
+
```swift
|
|
17
|
+
.sheet(isPresented: $showPaywall) {
|
|
18
|
+
PaywallView()
|
|
19
|
+
.presentationDetents([.medium, .large])
|
|
20
|
+
.presentationDragIndicator(.visible)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 确认删除这类二选一的决定,用系统对话框
|
|
24
|
+
.confirmationDialog("删除这条笔记?", isPresented: $confirmDelete) {
|
|
25
|
+
Button("删除", role: .destructive) { delete() }
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**不要**:在弹窗里再弹一个弹窗;把长篇文章放进玻璃弹窗;没有取消方式;把价格之类的关键信息藏在小字里。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!-- @dsCard group="Feedback" height=420 subtitle="iOS 底部弹窗,系统玻璃" -->
|
|
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>Sheet</title>
|
|
5
|
+
<style>
|
|
6
|
+
.scene { position: relative; height: 420px; overflow: hidden; background: var(--canvas); display: flex; justify-content: center; }
|
|
7
|
+
.under { position: absolute; inset: 0; padding: var(--space-5) var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); align-items: center; }
|
|
8
|
+
.under .dn-card { width: 359px; }
|
|
9
|
+
.shade { position: absolute; inset: 0; background: var(--scrim); }
|
|
10
|
+
.dn-sheet { position: absolute; bottom: 8px; }
|
|
11
|
+
</style>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div class="scene">
|
|
15
|
+
<div class="under" aria-hidden="true">
|
|
16
|
+
<div class="dn-card"><p class="dn-card-title">本周回顾</p><p class="dn-card-body">完成 12 条笔记,整理了两个项目的待办。</p></div>
|
|
17
|
+
<div class="dn-card"><p class="dn-card-title">读书摘录</p><p class="dn-card-body">好的工具会退到背景里。</p></div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="shade"></div>
|
|
20
|
+
<div class="dn-sheet dn-glass" role="dialog" aria-label="开通全家桶">
|
|
21
|
+
<div class="dn-grabber"></div>
|
|
22
|
+
<h3>开通全家桶</h3>
|
|
23
|
+
<p>一次订阅,旗下所有应用的高级功能都可以使用,AI 次数在各应用之间共享。</p>
|
|
24
|
+
<div class="dn-stack">
|
|
25
|
+
<button class="dn-btn dn-btn-primary dn-btn-ios-lg">¥168 / 年,免费试用 7 天</button>
|
|
26
|
+
<button class="dn-btn dn-btn-plain dn-btn-ios-lg" style="font-weight:500">暂不需要</button>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Sidebar
|
|
2
|
+
|
|
3
|
+
macOS 应用的主导航:顶部放全局操作,下面按分组列出各个位置。
|
|
4
|
+
|
|
5
|
+
**需要提供**:分组(标题可选)、每一项的 SF Symbol 和名称、可选的计数或快捷键、绑定的选中项。
|
|
6
|
+
|
|
7
|
+
**规格**
|
|
8
|
+
|
|
9
|
+
- 宽度 `sidebar-mac`(220),用户可以拖动调整。macOS 26 上侧边栏是悬浮的玻璃面板,由 `NavigationSplitView` 自动提供;预览里用 `surface-sunken` 模拟。
|
|
10
|
+
- 每项高 `row-mac`(26),`radius-sm`,左右内边距 `space-2`,图标 14pt、颜色 `ink-secondary`,文字 13pt `ink`。
|
|
11
|
+
- 悬停时底色 `fill-hover`。选中时底色 `fill-selected`、文字中粗、图标变 `ink`。选中块不用黑色填满,保持安静。
|
|
12
|
+
- 分组标题 11pt 半粗 `ink-tertiary`,上方留 `space-3`、下方留 `space-1`。
|
|
13
|
+
- 计数和快捷键放在右侧,11pt `ink-tertiary`,用等宽数字。
|
|
14
|
+
- 名称太长时在末尾截断,悬停时显示完整内容。
|
|
15
|
+
|
|
16
|
+
```swift
|
|
17
|
+
NavigationSplitView {
|
|
18
|
+
List(selection: $selection) {
|
|
19
|
+
Section("资料库") {
|
|
20
|
+
Label("全部笔记", systemImage: "doc.text").badge(128).tag(Item.all)
|
|
21
|
+
Label("收藏", systemImage: "star").badge(9).tag(Item.favorites)
|
|
22
|
+
}
|
|
23
|
+
Section("最近") {
|
|
24
|
+
ForEach(recent) { chat in
|
|
25
|
+
Label(chat.title, systemImage: "bubble.left").tag(Item.chat(chat.id))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
.navigationSplitViewColumnWidth(min: 180, ideal: 220, max: 300)
|
|
30
|
+
} detail: {
|
|
31
|
+
DetailView(selection: selection)
|
|
32
|
+
}
|
|
33
|
+
.tint(.brandAccent)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**不要**:给侧边栏加实色背景(会盖掉系统玻璃);一级导航超过两层;用彩色图标区分每一项(图标一律灰色)。
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- @dsCard group="Navigation" height=320 subtitle="macOS 侧边栏" -->
|
|
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>Sidebar</title></head>
|
|
5
|
+
<body>
|
|
6
|
+
<div class="dn-stage" style="align-items:flex-start">
|
|
7
|
+
<nav class="dn-sidebar" aria-label="侧边栏">
|
|
8
|
+
<div class="dn-side-item"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg><span class="dn-side-text">新建笔记</span><span class="dn-count">⌘N</span></div>
|
|
9
|
+
<div class="dn-side-item"><svg class="dn-icon" viewBox="0 0 24 24"><circle cx="11" cy="11" r="6"/><path d="M20 20l-4.5-4.5"/></svg><span class="dn-side-text">搜索</span></div>
|
|
10
|
+
<div class="dn-side-head">资料库</div>
|
|
11
|
+
<div class="dn-side-item is-selected"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M7 3h7l5 5v13H7z"/><path d="M14 3v5h5"/></svg><span class="dn-side-text">全部笔记</span><span class="dn-count">128</span></div>
|
|
12
|
+
<div class="dn-side-item"><svg class="dn-icon" viewBox="0 0 24 24"><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><span class="dn-side-text">收藏</span><span class="dn-count">9</span></div>
|
|
13
|
+
<div class="dn-side-item"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg><span class="dn-side-text">工作</span><span class="dn-count">41</span></div>
|
|
14
|
+
<div class="dn-side-head">最近</div>
|
|
15
|
+
<div class="dn-side-item"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M5 5h14v10H9l-4 4z"/></svg><span class="dn-side-text">把周报整理成三条要点</span></div>
|
|
16
|
+
<div class="dn-side-item"><svg class="dn-icon" viewBox="0 0 24 24"><path d="M5 5h14v10H9l-4 4z"/></svg><span class="dn-side-text">下周去杭州的行程安排和预算</span></div>
|
|
17
|
+
</nav>
|
|
18
|
+
</div>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# StepRow
|
|
2
|
+
|
|
3
|
+
一行显示一个执行步骤:做了什么、结果如何、用了多久。点开可以看到细节。用于 AI 的工具调用、导入导出、批量处理这类任务。
|
|
4
|
+
|
|
5
|
+
**需要提供**:状态(进行中 / 成功 / 失败)、动作名(2–4 个字的动词)、对象摘要(文件名、参数、结果)、耗时或说明;可选的详细内容,点击后展开。
|
|
6
|
+
|
|
7
|
+
**规格**
|
|
8
|
+
|
|
9
|
+
- 高 `control-mac`,`radius-sm`,悬停时底色 `fill-hover`。整行默认 `ink-secondary`,让位于正文内容。
|
|
10
|
+
- 状态图标 14pt:进行中是 `ink` 色的转圈,成功是 `success` 色,失败是 `danger` 色。失败时摘要要写清楚原因。
|
|
11
|
+
- 动作名 12pt 半粗 `ink`;摘要用 11pt 等宽,单行,在末尾截断;右侧耗时 11pt `ink-tertiary`。
|
|
12
|
+
- 展开后的详情缩进到和文字对齐,`surface-sunken` 底,`radius-sm`,12pt 等宽。
|
|
13
|
+
- 连续 3 步以上全部成功时,合并成一行「完成 5 个步骤」,点击后展开。
|
|
14
|
+
|
|
15
|
+
```swift
|
|
16
|
+
DisclosureGroup(isExpanded: $expanded) {
|
|
17
|
+
Text(step.detail)
|
|
18
|
+
.font(.system(.callout, design: .monospaced))
|
|
19
|
+
.foregroundStyle(.inkSecondary)
|
|
20
|
+
.padding(.vertical, 6).padding(.horizontal, 8)
|
|
21
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
22
|
+
.background(.surfaceSunken, in: .rect(cornerRadius: 6))
|
|
23
|
+
} label: {
|
|
24
|
+
HStack(spacing: 7) {
|
|
25
|
+
StatusGlyph(step.status) // ProgressView / checkmark.circle / xmark.circle
|
|
26
|
+
Text(step.action).fontWeight(.semibold).foregroundStyle(.ink)
|
|
27
|
+
Text(step.summary).font(.callout.monospaced()).lineLimit(1)
|
|
28
|
+
Spacer()
|
|
29
|
+
Text(step.duration).font(.caption).monospacedDigit().foregroundStyle(.inkTertiary)
|
|
30
|
+
}
|
|
31
|
+
.foregroundStyle(.inkSecondary)
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**不要**:把每一步都做成卡片;成功的步骤也用醒目的颜色;把原始日志直接铺在对话里(放进折叠详情)。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- @dsCard group="Content" height=190 subtitle="AI 或后台任务的执行步骤" -->
|
|
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>StepRow</title></head>
|
|
5
|
+
<body>
|
|
6
|
+
<div class="dn-stage" style="justify-content:center">
|
|
7
|
+
<div class="dn-steps">
|
|
8
|
+
<div class="dn-step dn-step-ok"><svg class="dn-icon dn-glyph" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M8 12.5l2.5 2.5L16 9.5"/></svg><span class="dn-step-name">读取</span><span class="dn-step-text">周报草稿.md、10 月数据.csv</span><span class="dn-step-meta">0.4 秒</span><svg class="dn-icon dn-chevron" viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></svg></div>
|
|
9
|
+
<div class="dn-step dn-step-ok"><svg class="dn-icon dn-glyph" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M8 12.5l2.5 2.5L16 9.5"/></svg><span class="dn-step-name">计算</span><span class="dn-step-text">订阅转化率 3.1% → 4.4%</span><span class="dn-step-meta">1.2 秒</span><svg class="dn-icon dn-chevron" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></div>
|
|
10
|
+
<div class="dn-step-detail">conversions / trials = 88 / 2000 = 4.4%
|
|
11
|
+
上月 = 62 / 2000 = 3.1%</div>
|
|
12
|
+
<div class="dn-step dn-step-fail"><svg class="dn-icon dn-glyph" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9 9l6 6M15 9l-6 6"/></svg><span class="dn-step-name">读取</span><span class="dn-step-text">销售日报.xlsx:文件已被移动或删除</span><span class="dn-step-meta">已跳过</span></div>
|
|
13
|
+
<div class="dn-step"><span class="dn-spinner" role="img" aria-label="进行中"></span><span class="dn-step-name">整理</span><span class="dn-step-text">生成三条要点</span><span class="dn-step-meta">3.0 秒</span></div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Tag
|
|
2
|
+
|
|
3
|
+
一到四个字的标签,标出状态或分类,放在标题旁边或元信息里。
|
|
4
|
+
|
|
5
|
+
**需要提供**:文字、语气(中性 / 强调 / success / warning / danger / info);表示状态时加一个圆点。
|
|
6
|
+
|
|
7
|
+
**规格**
|
|
8
|
+
|
|
9
|
+
- Mac:高 18pt,`radius-xs`,11pt 中粗,左右内边距 6pt。iOS:高 22pt,胶囊形,12pt。
|
|
10
|
+
- 中性标签:`fill-selected` 底、`ink-secondary` 字。强调标签:`accent` 黑底、`on-accent` 白字,只用于「新」「Pro」这类需要跳出来的少数标记。有语气的标签:用对应的 `-soft` 作底色,用语气色作文字和圆点,例如 `success-soft` 底配 `success` 字。
|
|
11
|
+
- 表示状态时一定加 5pt 圆点(`radius-full`),文字本身就要说清楚状态是什么,不能只靠颜色。
|
|
12
|
+
|
|
13
|
+
```swift
|
|
14
|
+
struct Tag: View {
|
|
15
|
+
var text: LocalizedStringKey
|
|
16
|
+
var tone: Color = .inkSecondary
|
|
17
|
+
var background: Color = .fillSelected
|
|
18
|
+
var showsDot = false
|
|
19
|
+
|
|
20
|
+
var body: some View {
|
|
21
|
+
HStack(spacing: 4) {
|
|
22
|
+
if showsDot { Circle().frame(width: 5, height: 5) }
|
|
23
|
+
Text(text)
|
|
24
|
+
}
|
|
25
|
+
.font(.caption.weight(.medium))
|
|
26
|
+
.foregroundStyle(tone)
|
|
27
|
+
.padding(.horizontal, 6)
|
|
28
|
+
.frame(height: 18)
|
|
29
|
+
.background(background, in: .rect(cornerRadius: 4))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Tag(text: "已同步", tone: .success, background: .successSoft, showsDot: true)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**不要**:用标签当按钮;一行里放超过三个标签;大量使用黑底强调标签(它和主按钮长得像,多了就分不清)。
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- @dsCard group="Feedback" height=120 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>Tag</title></head>
|
|
5
|
+
<body>
|
|
6
|
+
<div class="dn-stage dn-stack" style="align-items:flex-start">
|
|
7
|
+
<div class="dn-row">
|
|
8
|
+
<span class="dn-tag">草稿</span>
|
|
9
|
+
<span class="dn-tag dn-tag-accent">新功能</span>
|
|
10
|
+
<span class="dn-tag dn-tag-success"><span class="dn-dot"></span>已同步</span>
|
|
11
|
+
<span class="dn-tag dn-tag-warning"><span class="dn-dot"></span>额度剩余 10%</span>
|
|
12
|
+
<span class="dn-tag dn-tag-danger"><span class="dn-dot"></span>同步失败</span>
|
|
13
|
+
<span class="dn-tag dn-tag-info">Beta</span>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="dn-row">
|
|
16
|
+
<span class="dn-tag dn-tag-ios">工作</span>
|
|
17
|
+
<span class="dn-tag dn-tag-ios dn-tag-success"><span class="dn-dot"></span>已完成</span>
|
|
18
|
+
<span class="dn-tag dn-tag-ios dn-tag-accent">全家桶</span>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# TextField
|
|
2
|
+
|
|
3
|
+
单行文字输入,配一个标签和一句说明。
|
|
4
|
+
|
|
5
|
+
**需要提供**:标签(名词)、绑定的文本、可选的占位符(给示例,不要重复标签)、说明文字,校验失败时提供错误信息。
|
|
6
|
+
|
|
7
|
+
**规格**
|
|
8
|
+
|
|
9
|
+
- 底色 `surface-raised`,边框 `stroke-control` 宽、`line-control` 色。Mac 用 `radius-sm`,高 `control-mac`;iOS 用 `radius-md`,高 `control-ios`。
|
|
10
|
+
- 聚焦时边框变 `focus-ring`,外圈加 3pt 的 `accent-soft` 灰色光晕。
|
|
11
|
+
- 出错时边框变 `danger`,说明文字换成错误信息,同样用 `danger` 色。错误信息要说清楚怎么改。
|
|
12
|
+
- 标签 12pt 中粗 `ink-secondary`,放在输入框上方;说明 11pt `ink-tertiary`,放在下方。
|
|
13
|
+
|
|
14
|
+
```swift
|
|
15
|
+
VStack(alignment: .leading, spacing: 6) {
|
|
16
|
+
Text("邮箱").font(.caption.weight(.medium)).foregroundStyle(.inkSecondary)
|
|
17
|
+
TextField("name@example.com", text: $email)
|
|
18
|
+
.textFieldStyle(.roundedBorder) // Mac;iOS 用自定义样式实现上面的规格
|
|
19
|
+
.textContentType(.emailAddress)
|
|
20
|
+
.keyboardType(.emailAddress) // iOS
|
|
21
|
+
Text(error ?? "用于接收登录验证码。")
|
|
22
|
+
.font(.caption)
|
|
23
|
+
.foregroundStyle(error == nil ? .inkTertiary : .danger)
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
iOS 设置页这类表单,直接在 `Form` 里用 `TextField`,交给系统的分组样式(见 ListGroup)。
|
|
28
|
+
|
|
29
|
+
**不要**:只用占位符代替标签;出错时只把边框变红、不给说明;把多行输入塞进单行框(多行用 `TextField(axis: .vertical)` 或 Composer)。
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!-- @dsCard group="Input" height=180 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>TextField</title></head>
|
|
5
|
+
<body>
|
|
6
|
+
<div class="dn-stage" style="align-items:flex-start;gap:var(--space-5)">
|
|
7
|
+
<div class="dn-field">
|
|
8
|
+
<label for="f1">名称</label>
|
|
9
|
+
<input id="f1" class="dn-input" placeholder="例如:读书摘录">
|
|
10
|
+
<span class="dn-help">会显示在侧边栏里。</span>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="dn-field">
|
|
13
|
+
<label for="f2">邮箱</label>
|
|
14
|
+
<input id="f2" class="dn-input is-focus" value="hello@paperlight.app">
|
|
15
|
+
<span class="dn-help">用于接收登录验证码。</span>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="dn-field">
|
|
18
|
+
<label for="f3">验证码</label>
|
|
19
|
+
<input id="f3" class="dn-input is-error" value="12 34">
|
|
20
|
+
<span class="dn-help is-error">验证码是 6 位数字,请检查后重新输入。</span>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="dn-field" style="width:300px">
|
|
23
|
+
<label for="f4">iOS · 44pt</label>
|
|
24
|
+
<input id="f4" class="dn-input dn-input-ios" placeholder="搜索笔记">
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/* Deernova: web approximations of the SwiftUI components, used by the previews */
|
|
2
|
+
* { box-sizing: border-box; }
|
|
3
|
+
html, body { margin: 0; }
|
|
4
|
+
body { font-family: var(--font-sans); font-size: 13px; line-height: 18px; background: var(--canvas); color: var(--ink); -webkit-font-smoothing: antialiased; }
|
|
5
|
+
|
|
6
|
+
.dn-stage { padding: var(--space-4); display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
|
|
7
|
+
.dn-stack { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
8
|
+
.dn-row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
|
|
9
|
+
.dn-note { font-size: 11px; line-height: 13px; color: var(--ink-tertiary); }
|
|
10
|
+
.dn-icon { width: 14px; height: 14px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
|
|
11
|
+
|
|
12
|
+
/* Button */
|
|
13
|
+
.dn-btn { appearance: none; display: inline-flex; align-items: center; justify-content: center; gap: 5px; height: var(--control-mac); padding: 0 var(--space-3); font: inherit; font-size: 13px; font-weight: 500; line-height: 1; white-space: nowrap; border-radius: var(--radius-sm); border: var(--stroke-hairline) solid var(--line); background: var(--surface-raised); color: var(--ink); box-shadow: var(--shadow-control); cursor: pointer; }
|
|
14
|
+
.dn-btn:hover { background: var(--surface); }
|
|
15
|
+
.dn-btn:focus-visible, .dn-btn.is-focus { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: 2px; }
|
|
16
|
+
.dn-btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
|
|
17
|
+
.dn-btn-primary:hover { background: var(--accent); opacity: 0.88; }
|
|
18
|
+
.dn-btn-plain { background: transparent; border-color: transparent; box-shadow: none; color: var(--ink-secondary); }
|
|
19
|
+
.dn-btn-plain:hover { background: var(--fill-hover); color: var(--ink); }
|
|
20
|
+
.dn-btn-danger { background: var(--danger-soft); border-color: transparent; box-shadow: none; color: var(--danger); }
|
|
21
|
+
.dn-btn-danger:hover { background: var(--danger-soft); filter: brightness(0.97); }
|
|
22
|
+
.dn-btn-sm { height: var(--control-mac-sm); padding: 0 var(--space-2); font-size: 11px; border-radius: var(--radius-xs); }
|
|
23
|
+
.dn-btn-lg { height: var(--control-mac-lg); padding: 0 var(--space-4); }
|
|
24
|
+
.dn-btn-ios { height: var(--button-ios); padding: 0 var(--space-4); font-size: 15px; font-weight: 600; border-radius: var(--radius-full); }
|
|
25
|
+
.dn-btn-ios-sm { height: var(--button-ios-sm); padding: 0 var(--space-3); font-size: 13px; font-weight: 600; border-radius: var(--radius-full); }
|
|
26
|
+
.dn-btn-ios-lg { height: var(--control-ios-lg); width: 100%; padding: 0 var(--space-5); font-size: 16px; font-weight: 600; border-radius: var(--radius-full); }
|
|
27
|
+
.dn-btn[disabled] { opacity: 0.35; cursor: default; filter: none; }
|
|
28
|
+
.dn-btn .dn-icon { width: 13px; height: 13px; }
|
|
29
|
+
|
|
30
|
+
.dn-iconbtn { appearance: none; width: var(--control-mac); height: var(--control-mac); padding: 0; border-radius: var(--radius-sm); border: 0; background: transparent; color: var(--ink-secondary); display: inline-grid; place-items: center; cursor: pointer; flex: none; }
|
|
31
|
+
.dn-iconbtn:hover { background: var(--fill-hover); color: var(--ink); }
|
|
32
|
+
.dn-iconbtn:focus-visible { outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: 1px; }
|
|
33
|
+
|
|
34
|
+
/* Glass: web approximation of .glassEffect() */
|
|
35
|
+
.dn-backdrop { position: relative; overflow: hidden; background: var(--surface); }
|
|
36
|
+
.dn-glass { background: var(--glass-fill); -webkit-backdrop-filter: blur(14px) saturate(1.4); backdrop-filter: blur(14px) saturate(1.4); border: var(--stroke-hairline) solid var(--glass-stroke); box-shadow: var(--shadow-glass); border-radius: var(--radius-full); }
|
|
37
|
+
.dn-glass-btn { appearance: none; width: var(--control-ios); height: var(--control-ios); padding: 0; display: inline-grid; place-items: center; color: var(--ink); cursor: pointer; font: inherit; flex: none; }
|
|
38
|
+
.dn-glass-btn .dn-icon { width: 19px; height: 19px; }
|
|
39
|
+
.dn-glass-prominent { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
40
|
+
.dn-glass-group { display: inline-flex; align-items: center; gap: 2px; padding: 3px; }
|
|
41
|
+
.dn-glass-group .dn-glass-btn { background: transparent; border: 0; box-shadow: none; width: 36px; height: 36px; border-radius: var(--radius-full); color: var(--ink-secondary); }
|
|
42
|
+
.dn-glass-group .dn-glass-btn.is-selected { background: var(--fill-selected); color: var(--ink); }
|
|
43
|
+
.dn-glass-pill { height: 40px; padding: 0 var(--space-4); display: inline-flex; align-items: center; gap: var(--space-2); font-size: 14px; font-weight: 500; color: var(--ink); }
|
|
44
|
+
|
|
45
|
+
/* Segmented */
|
|
46
|
+
.dn-seg { display: inline-flex; padding: 2px; gap: 2px; background: var(--fill-selected); border-radius: var(--radius-sm); }
|
|
47
|
+
.dn-seg button { appearance: none; border: 0; font: inherit; font-size: 12px; font-weight: 500; height: var(--control-mac-sm); padding: 0 var(--space-3); border-radius: var(--radius-xs); background: transparent; color: var(--ink-secondary); cursor: pointer; }
|
|
48
|
+
.dn-seg button[aria-pressed="true"] { background: var(--surface-raised); color: var(--ink); box-shadow: var(--shadow-control); }
|
|
49
|
+
.dn-seg-ios { padding: 2px; border-radius: var(--radius-full); }
|
|
50
|
+
.dn-seg-ios button { height: 30px; padding: 0 var(--space-4); font-size: 14px; border-radius: var(--radius-full); }
|
|
51
|
+
|
|
52
|
+
/* Text field */
|
|
53
|
+
.dn-field { display: flex; flex-direction: column; gap: 4px; width: 240px; max-width: 100%; }
|
|
54
|
+
.dn-field label { font-size: 12px; line-height: 16px; font-weight: 500; color: var(--ink-secondary); }
|
|
55
|
+
.dn-input { height: var(--control-mac); padding: 0 var(--space-2); font: inherit; font-size: 13px; color: var(--ink); background: var(--surface-raised); border: var(--stroke-control) solid var(--line-control); border-radius: var(--radius-sm); outline: none; width: 100%; }
|
|
56
|
+
.dn-input::placeholder { color: var(--ink-tertiary); }
|
|
57
|
+
.dn-input:focus, .dn-input.is-focus { border-color: var(--focus-ring); box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
58
|
+
.dn-input.is-error { border-color: var(--danger); }
|
|
59
|
+
.dn-input-ios { height: var(--control-ios); padding: 0 var(--space-3); font-size: 17px; border-radius: var(--radius-md); }
|
|
60
|
+
.dn-help { font-size: 11px; line-height: 13px; color: var(--ink-tertiary); }
|
|
61
|
+
.dn-help.is-error { color: var(--danger); }
|
|
62
|
+
|
|
63
|
+
/* Composer */
|
|
64
|
+
.dn-composer { width: 100%; max-width: 560px; background: var(--surface-raised); border: var(--stroke-hairline) solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-float); padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3); display: flex; flex-direction: column; gap: var(--space-1); }
|
|
65
|
+
.dn-composer textarea { border: 0; outline: none; resize: none; background: transparent; font: inherit; font-size: 13px; line-height: 18px; color: var(--ink); min-height: 36px; padding: 2px 0; width: 100%; }
|
|
66
|
+
.dn-composer textarea::placeholder { color: var(--ink-tertiary); }
|
|
67
|
+
.dn-composer:focus-within { border-color: var(--line-control); }
|
|
68
|
+
.dn-composer-bar { display: flex; align-items: center; gap: var(--space-1); margin-left: -4px; }
|
|
69
|
+
.dn-spacer { flex: 1; }
|
|
70
|
+
.dn-chip { display: inline-flex; align-items: center; gap: 3px; height: var(--control-mac-sm); padding: 0 var(--space-2); border-radius: var(--radius-full); font: inherit; font-size: 11px; color: var(--ink-secondary); background: transparent; border: var(--stroke-hairline) solid var(--line); cursor: pointer; white-space: nowrap; }
|
|
71
|
+
.dn-chip .dn-icon { width: 11px; height: 11px; }
|
|
72
|
+
.dn-send { appearance: none; width: var(--control-mac); height: var(--control-mac); padding: 0; border-radius: var(--radius-full); border: 0; display: grid; place-items: center; background: var(--accent); color: var(--on-accent); cursor: pointer; flex: none; }
|
|
73
|
+
.dn-send .dn-icon { width: 13px; height: 13px; stroke-width: 2.2; }
|
|
74
|
+
.dn-send[disabled] { background: var(--fill-selected); color: var(--ink-tertiary); cursor: default; }
|
|
75
|
+
.dn-attach { display: inline-flex; align-items: center; gap: 5px; height: var(--control-mac); padding: 0 var(--space-2); border-radius: var(--radius-sm); background: var(--surface-sunken); font-size: 12px; color: var(--ink-secondary); align-self: flex-start; }
|
|
76
|
+
|
|
77
|
+
/* Sidebar (macOS) */
|
|
78
|
+
.dn-sidebar { width: var(--sidebar-mac); background: var(--surface-sunken); border: var(--stroke-hairline) solid var(--line); border-radius: var(--radius-lg); padding: 6px; display: flex; flex-direction: column; gap: 1px; }
|
|
79
|
+
.dn-side-head { font-size: 11px; line-height: 13px; font-weight: 600; color: var(--ink-tertiary); padding: var(--space-3) var(--space-2) var(--space-1); }
|
|
80
|
+
.dn-side-item { display: flex; align-items: center; gap: 7px; height: var(--row-mac); padding: 0 var(--space-2); border-radius: var(--radius-sm); font-size: 13px; color: var(--ink); cursor: default; min-width: 0; }
|
|
81
|
+
.dn-side-item .dn-icon { width: 14px; height: 14px; color: var(--ink-secondary); }
|
|
82
|
+
.dn-side-item:hover { background: var(--fill-hover); }
|
|
83
|
+
.dn-side-item.is-selected { background: var(--fill-selected); font-weight: 500; }
|
|
84
|
+
.dn-side-item.is-selected .dn-icon { color: var(--ink); }
|
|
85
|
+
.dn-side-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
86
|
+
.dn-count { margin-left: auto; font-size: 11px; color: var(--ink-tertiary); font-variant-numeric: tabular-nums; }
|
|
87
|
+
|
|
88
|
+
/* List group (iOS inset grouped) */
|
|
89
|
+
.dn-listwrap { background: var(--canvas); padding: var(--space-4); width: 375px; max-width: 100%; display: flex; flex-direction: column; }
|
|
90
|
+
.dn-list-head { font-size: 13px; line-height: 17px; color: var(--ink-secondary); padding: 0 var(--space-4) 6px; }
|
|
91
|
+
.dn-list { background: var(--surface-raised); border-radius: var(--radius-lg); overflow: hidden; }
|
|
92
|
+
.dn-list-row { --sep-inset: var(--space-4); display: flex; align-items: center; gap: var(--space-3); min-height: var(--control-ios); padding: 0 var(--space-4); font-size: 17px; line-height: 22px; color: var(--ink); position: relative; }
|
|
93
|
+
.dn-list-row.has-icon { --sep-inset: 56px; }
|
|
94
|
+
.dn-list-row + .dn-list-row::before { content: ""; position: absolute; top: 0; left: var(--sep-inset); right: 0; border-top: var(--stroke-hairline) solid var(--line); }
|
|
95
|
+
.dn-list-icon { width: 28px; height: 28px; border-radius: var(--radius-sm); display: grid; place-items: center; background: var(--accent-soft); color: var(--ink); flex: none; }
|
|
96
|
+
.dn-list-icon .dn-icon { width: 16px; height: 16px; }
|
|
97
|
+
.dn-list-value { margin-left: auto; color: var(--ink-secondary); white-space: nowrap; }
|
|
98
|
+
.dn-chevron { color: var(--ink-tertiary); width: 13px; height: 13px; }
|
|
99
|
+
.dn-list-foot { font-size: 13px; line-height: 17px; color: var(--ink-tertiary); padding: 6px var(--space-4) 0; }
|
|
100
|
+
.dn-switch { margin-left: auto; width: 51px; height: 31px; border-radius: var(--radius-full); background: var(--success); position: relative; flex: none; }
|
|
101
|
+
.dn-switch::after { content: ""; position: absolute; top: 2px; right: 2px; width: 27px; height: 27px; border-radius: var(--radius-full); background: var(--knob); box-shadow: var(--shadow-control); }
|
|
102
|
+
.dn-switch.is-off { background: var(--fill-selected); }
|
|
103
|
+
.dn-switch.is-off::after { right: auto; left: 2px; }
|
|
104
|
+
|
|
105
|
+
/* Card */
|
|
106
|
+
.dn-card { background: var(--surface); border: var(--stroke-hairline) solid var(--line); border-radius: var(--radius-md); padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-1); width: 240px; max-width: 100%; }
|
|
107
|
+
.dn-card-title { font-family: var(--font-serif); font-size: 15px; line-height: 20px; font-weight: 600; color: var(--ink); margin: 0; }
|
|
108
|
+
.dn-card-body { font-size: 12px; line-height: 17px; color: var(--ink-secondary); margin: 0; }
|
|
109
|
+
.dn-card-meta { font-size: 11px; line-height: 13px; color: var(--ink-tertiary); display: flex; gap: 6px; align-items: center; margin-top: 2px; font-variant-numeric: tabular-nums; }
|
|
110
|
+
.dn-card.is-interactive { cursor: pointer; }
|
|
111
|
+
.dn-card.is-interactive:hover { border-color: var(--line-control); }
|
|
112
|
+
.dn-card.is-selected { border-color: var(--accent); box-shadow: inset 0 0 0 0.5px var(--accent); }
|
|
113
|
+
|
|
114
|
+
/* Tag */
|
|
115
|
+
.dn-tag { display: inline-flex; align-items: center; gap: 4px; height: 18px; padding: 0 6px; border-radius: var(--radius-xs); font-size: 11px; font-weight: 500; line-height: 1; white-space: nowrap; background: var(--fill-selected); color: var(--ink-secondary); }
|
|
116
|
+
.dn-tag-accent { background: var(--accent); color: var(--on-accent); }
|
|
117
|
+
.dn-tag-success { background: var(--success-soft); color: var(--success); }
|
|
118
|
+
.dn-tag-warning { background: var(--warning-soft); color: var(--warning); }
|
|
119
|
+
.dn-tag-danger { background: var(--danger-soft); color: var(--danger); }
|
|
120
|
+
.dn-tag-info { background: var(--info-soft); color: var(--info); }
|
|
121
|
+
.dn-dot { width: 5px; height: 5px; border-radius: var(--radius-full); background: currentColor; flex: none; }
|
|
122
|
+
.dn-tag-ios { height: 22px; padding: 0 var(--space-2); font-size: 12px; border-radius: var(--radius-full); }
|
|
123
|
+
|
|
124
|
+
/* Banner and toast */
|
|
125
|
+
.dn-banner { display: flex; gap: 10px; align-items: flex-start; padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); background: var(--surface-raised); border: var(--stroke-hairline) solid var(--line); width: 100%; max-width: 520px; }
|
|
126
|
+
.dn-banner > .dn-icon { width: 16px; height: 16px; margin-top: 1px; color: var(--ink-secondary); }
|
|
127
|
+
.dn-banner-text { flex: 1; min-width: 0; }
|
|
128
|
+
.dn-banner-title { font-size: 13px; line-height: 18px; font-weight: 600; color: var(--ink); margin: 0; }
|
|
129
|
+
.dn-banner-body { font-size: 12px; line-height: 17px; color: var(--ink-secondary); margin: 0; }
|
|
130
|
+
.dn-banner-success { background: var(--success-soft); border-color: transparent; }
|
|
131
|
+
.dn-banner-success > .dn-icon { color: var(--success); }
|
|
132
|
+
.dn-banner-warning { background: var(--warning-soft); border-color: transparent; }
|
|
133
|
+
.dn-banner-warning > .dn-icon { color: var(--warning); }
|
|
134
|
+
.dn-banner-danger { background: var(--danger-soft); border-color: transparent; }
|
|
135
|
+
.dn-banner-danger > .dn-icon { color: var(--danger); }
|
|
136
|
+
.dn-toast { display: inline-flex; align-items: center; gap: var(--space-2); height: 40px; padding: 0 var(--space-4) 0 var(--space-3); font-size: 14px; font-weight: 500; color: var(--ink); }
|
|
137
|
+
.dn-toast .dn-icon { width: 16px; height: 16px; color: var(--success); }
|
|
138
|
+
|
|
139
|
+
/* Message */
|
|
140
|
+
.dn-thread { display: flex; flex-direction: column; gap: var(--space-4); width: 100%; max-width: 600px; }
|
|
141
|
+
.dn-msg-user { align-self: flex-end; max-width: 80%; background: var(--surface-sunken); border-radius: var(--radius-lg); padding: 6px var(--space-3); font-size: 13px; line-height: 18px; color: var(--ink); }
|
|
142
|
+
.dn-msg-assistant { font-family: var(--font-serif); font-size: 14px; line-height: 22px; color: var(--ink); }
|
|
143
|
+
.dn-msg-assistant p { margin: 0 0 var(--space-2); }
|
|
144
|
+
.dn-msg-assistant p:last-of-type { margin-bottom: 0; }
|
|
145
|
+
.dn-msg-assistant strong { font-weight: 600; }
|
|
146
|
+
.dn-msg-assistant a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; text-decoration-color: var(--line-control); }
|
|
147
|
+
.dn-msg-actions { display: flex; gap: 2px; margin-top: var(--space-1); margin-left: -5px; }
|
|
148
|
+
.dn-msg-actions .dn-iconbtn { width: 22px; height: 22px; color: var(--ink-tertiary); }
|
|
149
|
+
.dn-msg-actions .dn-icon { width: 13px; height: 13px; }
|
|
150
|
+
|
|
151
|
+
/* Code block */
|
|
152
|
+
.dn-code { background: var(--surface-sunken); border: var(--stroke-hairline) solid var(--line); border-radius: var(--radius-md); overflow: hidden; width: 100%; max-width: 560px; }
|
|
153
|
+
.dn-code-head { display: flex; align-items: center; height: 28px; padding: 0 var(--space-1) 0 var(--space-3); border-bottom: var(--stroke-hairline) solid var(--line); font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
|
154
|
+
.dn-code-head .dn-btn { margin-left: auto; }
|
|
155
|
+
.dn-code pre { margin: 0; padding: var(--space-2) var(--space-3); overflow-x: auto; font-family: var(--font-mono); font-size: 12px; line-height: 17px; color: var(--ink); }
|
|
156
|
+
.tk-kw { color: var(--ink); font-weight: 600; }
|
|
157
|
+
.tk-str { color: var(--ink-secondary); }
|
|
158
|
+
.tk-num { color: var(--ink); }
|
|
159
|
+
.tk-com { color: var(--ink-tertiary); font-style: italic; }
|
|
160
|
+
.tk-type { color: var(--ink); }
|
|
161
|
+
|
|
162
|
+
/* Step row */
|
|
163
|
+
.dn-steps { display: flex; flex-direction: column; width: 100%; max-width: 560px; }
|
|
164
|
+
.dn-step { display: flex; align-items: center; gap: 7px; height: var(--control-mac); padding: 0 6px; border-radius: var(--radius-sm); font-size: 12px; color: var(--ink-secondary); cursor: default; min-width: 0; }
|
|
165
|
+
.dn-step:hover { background: var(--fill-hover); }
|
|
166
|
+
.dn-step .dn-icon { width: 14px; height: 14px; }
|
|
167
|
+
.dn-step-name { font-weight: 600; color: var(--ink); white-space: nowrap; }
|
|
168
|
+
.dn-step-text { font-family: var(--font-mono); font-size: 11px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
169
|
+
.dn-step-meta { margin-left: auto; padding-left: var(--space-2); font-size: 11px; color: var(--ink-tertiary); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
170
|
+
.dn-step-ok .dn-glyph { color: var(--success); }
|
|
171
|
+
.dn-step-fail .dn-glyph { color: var(--danger); }
|
|
172
|
+
.dn-step .dn-chevron { color: var(--ink-tertiary); width: 11px; height: 11px; }
|
|
173
|
+
.dn-step-detail { margin: 2px 0 6px 27px; padding: 6px var(--space-2); background: var(--surface-sunken); border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--ink-secondary); white-space: pre-wrap; }
|
|
174
|
+
.dn-spinner { width: 13px; height: 13px; border-radius: var(--radius-full); border: 2px solid var(--line); border-top-color: var(--ink); animation: dn-spin 0.9s linear infinite; flex: none; }
|
|
175
|
+
@keyframes dn-spin { to { transform: rotate(360deg); } }
|
|
176
|
+
@media (prefers-reduced-motion: reduce) { .dn-spinner { animation-duration: 2.4s; } }
|
|
177
|
+
|
|
178
|
+
/* Empty state */
|
|
179
|
+
.dn-empty { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--space-1); padding: var(--space-6) var(--space-5); }
|
|
180
|
+
.dn-empty > .dn-icon { width: 28px; height: 28px; color: var(--ink-tertiary); stroke-width: 1.4; }
|
|
181
|
+
.dn-empty h2 { font-family: var(--font-serif); font-size: 22px; line-height: 28px; font-weight: 600; letter-spacing: -0.01em; margin: var(--space-2) 0 0; color: var(--ink); }
|
|
182
|
+
.dn-empty p { margin: 0 0 var(--space-3); max-width: 320px; color: var(--ink-secondary); font-size: 13px; line-height: 18px; }
|
|
183
|
+
|
|
184
|
+
/* Sheet (iOS) */
|
|
185
|
+
.dn-sheet { width: 359px; max-width: 100%; border-radius: var(--radius-xl); padding: var(--space-2) var(--space-5) var(--space-5); }
|
|
186
|
+
.dn-grabber { width: 36px; height: 5px; border-radius: var(--radius-full); background: var(--line-control); margin: 0 auto var(--space-3); }
|
|
187
|
+
.dn-sheet h3 { font-family: var(--font-serif); font-size: 20px; line-height: 25px; font-weight: 600; margin: 0 0 var(--space-1); color: var(--ink); }
|
|
188
|
+
.dn-sheet p { font-size: 15px; line-height: 20px; color: var(--ink-secondary); margin: 0 0 var(--space-4); }
|
|
189
|
+
.dn-sheet .dn-stack { gap: var(--space-1); }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 3,
|
|
3
|
+
"assetGroups": {},
|
|
4
|
+
"blobs": {},
|
|
5
|
+
"createdOnFiles": {
|
|
6
|
+
"at": "2026-10-31T17:19:19Z",
|
|
7
|
+
"v": 1
|
|
8
|
+
},
|
|
9
|
+
"docs": {
|
|
10
|
+
"readme": "project/README.md",
|
|
11
|
+
"sections": []
|
|
12
|
+
},
|
|
13
|
+
"groups": [],
|
|
14
|
+
"lastChange": {
|
|
15
|
+
"by": "Claude",
|
|
16
|
+
"via": "Claude Code",
|
|
17
|
+
"at": "2026-11-01T13:33:31Z",
|
|
18
|
+
"note": "修正 Composer、TextField、MacWindow、Button 预览被裁掉底部的问题。"
|
|
19
|
+
},
|
|
20
|
+
"layout": "files",
|
|
21
|
+
"libraries": [],
|
|
22
|
+
"namespace": "Deernova",
|
|
23
|
+
"sections": {},
|
|
24
|
+
"title": "Deernova"
|
|
25
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|