@lynn123411/dsh-maple-theme 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.
@@ -1,171 +0,0 @@
1
- /**
2
- * 配置到 DOM 的投影:把一份已解析的配置算成 `<html>` 上要写的属性与自定义变量。
3
- *
4
- * 这里是纯函数,不碰 document、不读配置源——浏览器半边负责把它的结果写到页面上,
5
- * 自检直接 import 这个文件断言映射,不用起 DOM。
6
- *
7
- * 为什么写在 `<html>` 而不是 `body`:宿主把令牌声明在 `body` 上,我们的样式表也要覆盖
8
- * 同名变量,写在同一层只能靠注入顺序取胜;写到 `<html>` 上,`body` 里那些 `var(--maple-*)`
9
- * 一律读得到,而两套配色各自的选择器 (`body` / `body[data-ds-dark-theme]`) 与宿主同形,
10
- * 只靠后注入这一条优势。**但开关必须写在祖先上**:属性选择器要多一个 key 才能稳定压过宿主。
11
- */
12
-
13
- /** 本插件在 `<html>` 上用的根属性:开着时才加,关掉整层样式一并失效。 */
14
- export const ROOT_ATTRIBUTE = 'data-maple-theme';
15
-
16
- /** 标题配色方式落到 `<html>` 上的属性名。 */
17
- export const HEADING_ATTRIBUTE = 'data-maple-heading';
18
-
19
- /** 标题等级标记的开关属性名。 */
20
- export const INDICATOR_ATTRIBUTE = 'data-maple-indicator';
21
-
22
- /** 代码配色的开关属性名。 */
23
- export const CODE_ATTRIBUTE = 'data-maple-code';
24
-
25
- /** 外壳装饰(侧栏、标签条、输入区)的开关属性名。 */
26
- export const SHELL_ATTRIBUTE = 'data-maple-shell';
27
-
28
- /** 一份配置投影出来的结果:要写的属性与变量。 */
29
- export interface MapleProjection {
30
- /** `<html>` 上要设的属性(值为 `null` 表示删掉)。 */
31
- attributes: Record<string, string | null>;
32
- /** `<html>` 行内样式上要写的自定义变量(值为 `null` 表示删掉)。 */
33
- variables: Record<string, string | null>;
34
- }
35
-
36
- /** 本插件写在 `<html>` 上的全部变量名:撤痕迹时按这张表逐个删,不靠猜。 */
37
- export const ROOT_VARIABLES = [
38
- '--maple-hue-light',
39
- '--maple-hue-dark',
40
- '--maple-line-height',
41
- '--maple-p-gap',
42
- ] as const;
43
-
44
- /** 配置里取色相关字段的兜底:宿主没给值时按 Maple 出厂值走。 */
45
- export interface MapleSettings {
46
- hueLight: number;
47
- hueDark: number;
48
- lineHeight: number;
49
- paragraphGap: number;
50
- headingColor: 'colorful' | 'accent' | 'text';
51
- headingIndicator: boolean;
52
- codePalette: boolean;
53
- shellSkin: boolean;
54
- }
55
-
56
- /** 出厂值,与宿主半边 Config 的默认值同一组数字。 */
57
- export const DEFAULT_SETTINGS: Readonly<MapleSettings> = Object.freeze({
58
- hueLight: 35,
59
- hueDark: 207,
60
- lineHeight: 1.75,
61
- paragraphGap: 10,
62
- headingColor: 'colorful',
63
- headingIndicator: true,
64
- codePalette: true,
65
- shellSkin: true,
66
- });
67
-
68
- /**
69
- * 把一个数夹进闭区间;不是有限数就用兜底值。
70
- * 手改过的设置文档可能存进越界值或字符串,投影这一步不信任它。
71
- * @param value - 候选值。
72
- * @param min - 下界。
73
- * @param max - 上界。
74
- * @param fallback - 不是有限数时用它。
75
- * @returns 夹好的数。
76
- */
77
- export function clampNumber(value: unknown, min: number, max: number, fallback: number): number {
78
- if (typeof value !== 'number' && typeof value !== 'string') return fallback;
79
- // 空串、全空白,以及 null 被 Number 读成 0 的那一类,都不算「给了一个数」。
80
- if (typeof value === 'string' && value.trim() === '') return fallback;
81
- const parsed = typeof value === 'number' ? value : Number(value);
82
- if (!Number.isFinite(parsed)) return fallback;
83
- return Math.min(max, Math.max(min, parsed));
84
- }
85
-
86
- /**
87
- * 把宿主可能给出的任意值收成一份可信的配置。
88
- * @param raw - 配置源里的原始值(可能缺字段、可能越界)。
89
- * @returns 每个字段都在合法范围内的一份配置。
90
- */
91
- export function normalizeSettings(raw: Partial<MapleSettings> | undefined | null): MapleSettings {
92
- const source = raw ?? {};
93
- const heading = source.headingColor;
94
- return {
95
- hueLight: clampNumber(source.hueLight, 0, 360, DEFAULT_SETTINGS.hueLight),
96
- hueDark: clampNumber(source.hueDark, 0, 360, DEFAULT_SETTINGS.hueDark),
97
- lineHeight: clampNumber(source.lineHeight, 1.4, 2, DEFAULT_SETTINGS.lineHeight),
98
- paragraphGap: clampNumber(source.paragraphGap, 4, 24, DEFAULT_SETTINGS.paragraphGap),
99
- headingColor: heading === 'colorful' || heading === 'accent' || heading === 'text'
100
- ? heading
101
- : DEFAULT_SETTINGS.headingColor,
102
- headingIndicator: source.headingIndicator ?? DEFAULT_SETTINGS.headingIndicator,
103
- codePalette: source.codePalette ?? DEFAULT_SETTINGS.codePalette,
104
- shellSkin: source.shellSkin ?? DEFAULT_SETTINGS.shellSkin,
105
- };
106
- }
107
-
108
- /**
109
- * 算出一份配置该在 `<html>` 上留下什么。
110
- *
111
- * 关掉的开关写成 `null`(调用方删属性),而不是写 `off`:属性不存在时那批规则整段不匹配,
112
- * 少一个匹配分支也少一处能跟宿主撞车的地方。
113
- * @param settings - 已收拢到合法范围的配置。
114
- * @param enabled - 整个主题是否启用;关掉时只留一个「关」标记,其余全部撤掉。
115
- * @returns 要写的属性与变量。
116
- */
117
- export function projectSettings(settings: MapleSettings, enabled = true): MapleProjection {
118
- if (!enabled) {
119
- const variables: Record<string, null> = {};
120
- for (const name of ROOT_VARIABLES) variables[name] = null;
121
- return {
122
- attributes: {
123
- [ROOT_ATTRIBUTE]: null,
124
- [HEADING_ATTRIBUTE]: null,
125
- [INDICATOR_ATTRIBUTE]: null,
126
- [CODE_ATTRIBUTE]: null,
127
- [SHELL_ATTRIBUTE]: null,
128
- },
129
- variables,
130
- };
131
- }
132
- return {
133
- attributes: {
134
- [ROOT_ATTRIBUTE]: 'on',
135
- [HEADING_ATTRIBUTE]: settings.headingColor,
136
- [INDICATOR_ATTRIBUTE]: settings.headingIndicator ? 'on' : null,
137
- [CODE_ATTRIBUTE]: settings.codePalette ? 'on' : null,
138
- [SHELL_ATTRIBUTE]: settings.shellSkin ? 'on' : null,
139
- },
140
- variables: {
141
- '--maple-hue-light': String(settings.hueLight),
142
- '--maple-hue-dark': String(settings.hueDark),
143
- '--maple-line-height': String(settings.lineHeight),
144
- '--maple-p-gap': `${settings.paragraphGap}px`,
145
- },
146
- };
147
- }
148
-
149
- /**
150
- * 把投影写到页面上;同一个元素重复调用是幂等的。
151
- * @param root - 文档根元素(真实浏览器里是 `document.documentElement`)。
152
- * @param projection - {@link projectSettings} 的结果。
153
- */
154
- export function applyProjection(root: HTMLElement, projection: MapleProjection): void {
155
- for (const [attribute, value] of Object.entries(projection.attributes)) {
156
- if (value === null) root.removeAttribute(attribute);
157
- else root.setAttribute(attribute, value);
158
- }
159
- for (const [property, value] of Object.entries(projection.variables)) {
160
- if (value === null) root.style.removeProperty(property);
161
- else root.style.setProperty(property, value);
162
- }
163
- }
164
-
165
- /**
166
- * 撤掉本插件在页面上留下的一切痕迹(卸载与关掉主题都走这一条)。
167
- * @param root - 文档根元素。
168
- */
169
- export function clearProjection(root: HTMLElement): void {
170
- applyProjection(root, projectSettings(normalizeSettings(undefined), false));
171
- }
@@ -1,266 +0,0 @@
1
- /* Maple 正文层:对话流里的 markdown 排版与装饰。
2
- *
3
- * 作用范围只有一处:助手回答的正文(`[data-chat-flow-kind='assistant-step']`)。
4
- * 推理块用的是同一个 kind,但它自己带 `data-chat-group-part='reasoning'`,被排除在外——
5
- * 推理行要保持宿主的小字号与紧凑行距,跟着正文一起放大就糊成一片。
6
- * 工具行也排除:它的 flow kind 是 `tool-call`,宿主本来就不在里面放 markdown 元素。
7
- *
8
- * 选择器一律「宿主的数据属性 + 元素名」两层:宿主正文的类名是 CSS Modules 的哈希串,
9
- * 换一次构建就变,不能当锚点。典型一条是
10
- * `html[data-maple-theme][data-maple-heading][data-chat-flow-kind]:not([data-chat-group-part]) body :where(h2)`,
11
- * 属性三个(`html` 与 `body` 两个元素不计在内)压过宿主 module 样式表里的
12
- * `.markdown h2` 那一个类,所以不需要 !important。
13
- */
14
-
15
- html[data-maple-theme='on'] {
16
- --maple-p-gap: 10px;
17
- --maple-underline-offset: 2px;
18
- --maple-inline-code: hsl(0 45% 48%);
19
- --maple-quote-bar: hsl(var(--maple-hue-light, 35) 26% 38% / 45%);
20
- --maple-quote-fill: hsl(var(--maple-hue-light, 35) 20% 95%);
21
- --maple-table-head: hsl(var(--maple-hue-light, 35) 30% 91%);
22
- }
23
-
24
- html[data-maple-theme='on'] body[data-ds-dark-theme] {
25
- --maple-inline-code: hsl(0 44% 68%);
26
- --maple-quote-bar: hsl(var(--maple-hue-dark, 207) 30% 72% / 40%);
27
- --maple-quote-fill: hsl(var(--maple-hue-dark, 207) 10% 14%);
28
- --maple-table-head: hsl(var(--maple-hue-dark, 207) 12% 17%);
29
- }
30
-
31
- /* 段距:宿主的正文容器是纵向 flex,块与块之间的空隙由容器的 `gap: 16px` 给,
32
- * 段落自己的 margin 被归零。所以要改的是容器那一个 gap,不是 p 的 margin。
33
- * 容器是包住 markdown 根节点的那个 div,用 :has() 从根节点往上认。 */
34
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) {
35
- --maple-gap: var(--maple-p-gap, 10px);
36
- }
37
-
38
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :has(> [class*='markdown']) {
39
- gap: var(--maple-p-gap, 10px);
40
- }
41
-
42
- /* 列表项里的段落仍按块级 margin 走:宿主的列表没有 gap,段落间距归零会挤成一坨。 */
43
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(li > p) {
44
- margin: 4px 0;
45
- }
46
-
47
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(strong) {
48
- font-weight: 600;
49
- }
50
-
51
- /* 行内代码:Maple 用暖红一色区分,不再是「加了底的正文」。 */
52
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :not(pre) > code {
53
- color: var(--maple-inline-code);
54
- background-color: color-mix(in srgb, var(--maple-inline-code) 8%, transparent);
55
- border-color: color-mix(in srgb, var(--maple-inline-code) 18%, transparent);
56
- }
57
-
58
- /* 链接下划线的抬升量:Maple 的 --setting-underline-offset 默认 2px。 */
59
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(a) {
60
- text-underline-offset: var(--maple-underline-offset);
61
- }
62
-
63
- /* 标题:字号与字重来自 tokens.css 的复合令牌,这里只补颜色、间距与 h5/h6。 */
64
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1, h2, h3, h4, h5, h6) {
65
- position: relative;
66
- margin: 28px 0 12px;
67
- }
68
-
69
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h5) {
70
- font-size: calc(var(--dsh-content-font-size, 14px) * 1.05);
71
- font-weight: 600;
72
- line-height: 1.5;
73
- }
74
-
75
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h6) {
76
- font-size: var(--dsh-content-font-size, 14px);
77
- font-weight: 600;
78
- line-height: 1.6;
79
- }
80
-
81
- html[data-maple-theme='on'][data-maple-heading='colorful'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1) { color: var(--maple-h1); }
82
- html[data-maple-theme='on'][data-maple-heading='colorful'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h2) { color: var(--maple-h2); }
83
- html[data-maple-theme='on'][data-maple-heading='colorful'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h3) { color: var(--maple-h3); }
84
- html[data-maple-theme='on'][data-maple-heading='colorful'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h4) { color: var(--maple-h4); }
85
- html[data-maple-theme='on'][data-maple-heading='colorful'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h5) { color: var(--maple-h5); }
86
- html[data-maple-theme='on'][data-maple-heading='colorful'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h6) { color: var(--maple-h6); }
87
-
88
- /* 另两种标题配色:六级统一取一个色,不再逐级换。
89
- * 令牌层给了 --maple-heading-color,这里按元素逐级挂上——宿主用的是元素选择器,
90
- * 一次 :where(h1, h2, …) 的优先级与它相同,分不开。 */
91
- html[data-maple-theme='on'][data-maple-heading='accent'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1),
92
- html[data-maple-theme='on'][data-maple-heading='accent'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h2),
93
- html[data-maple-theme='on'][data-maple-heading='accent'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h3),
94
- html[data-maple-theme='on'][data-maple-heading='accent'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h4),
95
- html[data-maple-theme='on'][data-maple-heading='accent'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h5),
96
- html[data-maple-theme='on'][data-maple-heading='accent'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h6) { color: var(--maple-heading-color); }
97
-
98
- html[data-maple-theme='on'][data-maple-heading='text'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1),
99
- html[data-maple-theme='on'][data-maple-heading='text'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h2),
100
- html[data-maple-theme='on'][data-maple-heading='text'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h3),
101
- html[data-maple-theme='on'][data-maple-heading='text'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h4),
102
- html[data-maple-theme='on'][data-maple-heading='text'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h5),
103
- html[data-maple-theme='on'][data-maple-heading='text'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h6) { color: var(--maple-heading-color); }
104
-
105
- /* 标题等级标记:Maple 的 heading-indicator-auto——平时看不见,悬停时在左侧淡入 H1…H6。 */
106
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1, h2, h3, h4, h5, h6)::before {
107
- position: absolute;
108
- inset-inline-start: -1.9rem;
109
- top: 0.15em;
110
- font-size: calc(var(--dsh-content-font-size, 14px) * 0.7);
111
- font-weight: 500;
112
- line-height: 1;
113
- color: var(--dsw-alias-label-caption);
114
- opacity: 0;
115
- transition: opacity var(--ds-transition-duration-fast) var(--ds-ease-in-out);
116
- pointer-events: none;
117
- }
118
-
119
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1, h2, h3, h4, h5, h6):hover::before {
120
- opacity: 1;
121
- }
122
-
123
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1)::before { content: 'H1'; }
124
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h2)::before { content: 'H2'; }
125
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h3)::before { content: 'H3'; }
126
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h4)::before { content: 'H4'; }
127
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h5)::before { content: 'H5'; }
128
- html[data-maple-theme='on'][data-maple-indicator='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h6)::before { content: 'H6'; }
129
-
130
- /* 引用:Maple 是浅底 + 左侧一条色带,不是宿主的单边竖线。 */
131
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(blockquote) {
132
- position: relative;
133
- margin: 14px 0;
134
- padding: 8px 14px 8px 18px;
135
- border: 0;
136
- border-radius: var(--dsw-radius-sm);
137
- background: var(--maple-quote-fill);
138
- color: var(--dsw-alias-label-secondary);
139
- }
140
-
141
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(blockquote)::before {
142
- content: '';
143
- position: absolute;
144
- inset-block: 8px;
145
- inset-inline-start: 7px;
146
- width: 3px;
147
- border-radius: var(--dsw-radius-xs);
148
- background: var(--maple-quote-bar);
149
- }
150
-
151
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(blockquote blockquote) {
152
- margin-inline-end: 0;
153
- background: color-mix(in srgb, var(--maple-quote-fill) 70%, var(--dsw-alias-bg-base));
154
- }
155
-
156
- /* 列表:Maple 的标记用等宽字体,并按层级换形状(实心圆 → 空心圆 → 方块)。 */
157
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(ul, ol) {
158
- margin: var(--maple-p-gap) 0;
159
- padding-inline-start: 1.35em;
160
- }
161
-
162
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(li)::marker {
163
- font-family: var(--ds-font-family-code);
164
- color: var(--dsw-alias-state-business-primary);
165
- }
166
-
167
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(ul) > :where(li)::marker {
168
- font-size: 0.85em;
169
- }
170
-
171
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(ul ul, ul ul ul ul, ul ul ul ul ul ul) {
172
- list-style-type: circle;
173
- }
174
-
175
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(ul ul ul, ul ul ul ul ul) {
176
- list-style-type: square;
177
- }
178
-
179
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(li) {
180
- margin: 3px 0;
181
- }
182
-
183
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(input[type='checkbox']) {
184
- accent-color: var(--dsw-alias-state-business-primary);
185
- margin-inline-end: 8px;
186
- }
187
-
188
- /* 表格:Maple 的表头填一层浅底、外边圆角、横线只在行之间。
189
- * 宿主用 border-collapse: collapse,圆角会被裁掉,所以改成 separate + 零间距,
190
- * 只给四角的单元格上圆角。 */
191
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(table) {
192
- border-collapse: separate;
193
- border-spacing: 0;
194
- font-size: calc(var(--dsh-content-font-size, 14px) - 1px);
195
- }
196
-
197
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(th) {
198
- background: var(--maple-table-head);
199
- font-weight: 600;
200
- }
201
-
202
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(thead > tr > th:first-child) {
203
- border-start-start-radius: var(--dsw-radius-sm);
204
- }
205
-
206
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(thead > tr > th:last-child) {
207
- border-start-end-radius: var(--dsw-radius-sm);
208
- }
209
-
210
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(tbody > tr:last-child > td:first-child) {
211
- border-end-start-radius: var(--dsw-radius-sm);
212
- }
213
-
214
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(tbody > tr:last-child > td:last-child) {
215
- border-end-end-radius: var(--dsw-radius-sm);
216
- }
217
-
218
- /* 分隔线:Maple 是一条两端淡出的渐变,不是一根等宽实线,上下留白也更大。 */
219
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(hr) {
220
- height: 1px;
221
- margin: 28px 0;
222
- border: 0;
223
- background: linear-gradient(
224
- to right,
225
- transparent 0%,
226
- var(--dsw-alias-border-l3) 22%,
227
- var(--dsw-alias-border-l4) 50%,
228
- var(--dsw-alias-border-l3) 78%,
229
- transparent 100%
230
- );
231
- }
232
-
233
- /* 代码块:Maple 给它一圈发丝边框、比宿主小一档的圆角,标题条用比正文深一档的底。
234
- * 宿主把圆角收在一个局部变量里(--dsl-code-block-border-radius,默认 radius-lg),
235
- * 所以换圆角只要换那个变量,不必去写各角的 border-radius。 */
236
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(.md-code-block) {
237
- border: 0.5px solid var(--dsw-alias-border-l2);
238
- --dsl-code-block-border-radius: var(--dsw-radius-md);
239
- }
240
-
241
- /* 标题条:Maple 的 code-language 用等宽字体、行内代码那个暖色,字号小一号。 */
242
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where([data-code-block-banner]) {
243
- border-bottom: 0.5px solid var(--dsw-alias-border-l1);
244
- }
245
-
246
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where([data-code-block-banner]) :where(div:first-child) {
247
- font-family: var(--ds-font-family-code);
248
- letter-spacing: 0.02em;
249
- color: var(--maple-inline-code);
250
- }
251
-
252
- /* 行号栏:Maple 的行号比宿主的再淡一档,让代码本身更突出。
253
- * 宿主把行号画在 `.line::before` 上并直接取 --dsw-alias-label-tertiary,所以在行号容器里
254
- * 把那个变量换成 caption 一档;`--dsl-*` 是组件自己的局部变量,不在这里改。 */
255
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where([data-line-numbers]) {
256
- --dsw-alias-label-tertiary: var(--dsw-alias-label-caption);
257
- }
258
-
259
- /* 选中文字:Maple 用强调色的低透明度,不是浏览器的默认蓝。 */
260
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step'] ::selection {
261
- background: hsl(35 60% 62% / 28%);
262
- }
263
-
264
- html[data-maple-theme='on'] [data-chat-flow-kind='assistant-step']:not([data-chat-group-part='reasoning']) :where(h1, h2, h3, h4, h5, h6):first-child {
265
- margin-top: 0;
266
- }
@@ -1,83 +0,0 @@
1
- /* Maple 外壳层:侧栏、会话头部、输入区与标签条的观感。
2
- *
3
- * 锚点只有一样是稳的:`data-slot`。宿主的外壳类名全是 CSS Modules 的哈希串
4
- * (`pI_x6G_sidebarCol`、`wSkVaW_tabActive`),换一次构建就变;而 `data-slot` 是座位注册时
5
- * 写死的名字,属于跨包契约——插件的座位就是靠它认的。需要选中「装座位的那个壳」时用
6
- * `:has(> [data-slot='…'])`:壳没有名字,但它的孩子有。行与标签另有具体属性:
7
- * `[data-row-key^='session:']`、`[data-conversation-tabs] [aria-selected]`。
8
- *
9
- * 这一层只改**观感**:底色、分隔线、圆角、留白。唯独中央列那一圈内边距算是尺寸——
10
- * 它是 Maple 卡片式布局的外沿,值固定在 6px,不参与宿主的任何计算。
11
- * 不动 grid 轨道、不动滚动归属,所以折叠、拖宽、响应式都照旧。
12
- *
13
- * 整层受 `data-maple-shell` 开关控制,配置页上可以整个关掉回到宿主原样。
14
- */
15
-
16
- /* 中央列:Maple 的 app-layout-card 把编辑区当成一张浮在侧栏底色上的卡片,
17
- * 四周留一条窄缝(它的 --app-layout-spacing 默认在 6~8px)。这里照同样思路,
18
- * 但只给内边距,不动 grid 轨道——轨道由宿主按拖宽实时写行内样式。 */
19
- html[data-maple-theme='on'][data-maple-shell='on'] body :has(> [data-slot='main']) {
20
- padding: 6px 6px 0;
21
- box-sizing: border-box;
22
- }
23
-
24
- /* 侧栏与中央卡片之间:宿主的 0.5px 冷灰分隔线,Maple 用留白分层,不要这条线。
25
- * 两侧都要撤——非 darwin 画在侧栏的右边框上,darwin 画在中央列的左边框上。
26
- * 侧栏底色已由 tokens.css 的 --dsw-specific-sidebar-fill 给出(Maple 的暖灰)。 */
27
- html[data-maple-theme='on'][data-maple-shell='on'] body :has(> [data-slot='sidebar']) {
28
- border-right: 0;
29
- }
30
-
31
- html[data-maple-theme='on'][data-maple-shell='on'] body :has(> [data-slot='main']) {
32
- border-left: 0;
33
- }
34
-
35
- /* 会话头部:Maple 的标题区没有那条贯穿的分隔线。
36
- * 座位锚点自己是 display: contents,边框画在它包着的真 <header> 上,
37
- * 所以这里往下一层找;改成 transparent 而不是 none,避免高度跳 0.5px。 */
38
- html[data-maple-theme='on'][data-maple-shell='on'] body [data-slot='conversation.header'] header {
39
- border-bottom-color: transparent;
40
- }
41
-
42
- /* 标签条:Maple 的 tab-float 把活动标签做成一枚浮起的圆角块,底下不带指示条。
43
- * 宿主的活动指示是 `::after` 的一条 2px 线,这里撤掉它、改用同宽的圆角底。
44
- * 圆角与 corner-shape 必须写在同一组声明里,否则宿主的 superellipse 会把
45
- * 这个直角圆角揉成方圆形。 */
46
- html[data-maple-theme='on'][data-maple-shell='on'] body [data-conversation-tabs] [aria-selected='true'] {
47
- border-radius: var(--dsw-radius-sm);
48
- corner-shape: round;
49
- background: var(--dsw-alias-interactive-bg-hover-solid);
50
- }
51
-
52
- html[data-maple-theme='on'][data-maple-shell='on'] body [data-conversation-tabs] [aria-selected='true']::after {
53
- opacity: 0;
54
- }
55
-
56
- /* 输入区:Maple 的输入卡是「比正文浅一档的纸」,圆角取面板级。
57
- * 宿主的 composer 已经是 border: 0 + elevation-soft(投影层只换填充与圆角,不碰阴影)。 */
58
- html[data-maple-theme='on'][data-maple-shell='on'] body [data-composer-card] {
59
- background: var(--dsw-specific-input-major);
60
- border-radius: var(--dsw-radius-panel);
61
- }
62
-
63
- /* 侧栏的导航行与工作区行:Maple 的列表项带圆角,悬停与选中都是圆角块。 */
64
- html[data-maple-theme='on'][data-maple-shell='on'] body [data-row-key^='session:'],
65
- html[data-maple-theme='on'][data-maple-shell='on'] body [data-row-key^='workspace:'] {
66
- border-radius: var(--dsw-radius-sm);
67
- }
68
-
69
- /* 侧栏底部工具栏:宿主的冷灰浮层,换成 Maple 的纸色。
70
- * 座位锚点是 display: contents,涂在它不会见效,得涂装它的那个壳。 */
71
- html[data-maple-theme='on'][data-maple-shell='on'] body :has(> [data-slot='shell.bottom']) {
72
- background: var(--dsw-specific-tip);
73
- }
74
-
75
- /* 会话头部的图标按钮:Maple 的图标按钮是紧凑控件级圆角。 */
76
- html[data-maple-theme='on'][data-maple-shell='on'] body [data-slot='conversation.session.header.actions'] button {
77
- border-radius: var(--dsw-radius-sm);
78
- }
79
-
80
- /* 滚动条:Maple 的滑块宽 12px、圆头,颜色取它的暖灰(tokens.css 已换)。 */
81
- html[data-maple-theme='on'][data-maple-shell='on'] body {
82
- --dsh-scrollbar-width: 12px;
83
- }
@@ -1,119 +0,0 @@
1
- /* Maple 装饰层:标题配色、代码语法色、正文排版。
2
- *
3
- * 配色令牌本身不在这里——它们走宿主的主题通道(src/client/tokens.ts +
4
- * ctx.theme.overrideTokens),由宿主写到 body 的行内样式上。这里只剩三类:
5
- * 按开关切换的标题配色、shiki 语法色、以及正文排版的复合令牌。
6
- *
7
- * 每一条都挂在 `html[data-maple-theme='on']` 与它自己的开关属性下面:
8
- * 关掉时整段不匹配,不必跟宿主争优先级。
9
- */
10
-
11
- /* 标题六色:Maple 默认的 colorful,逐级换色。浅色那一列按 4.5:1 调过明度,
12
- * 深色用它自己那套提亮色。 */
13
- html[data-maple-theme='on'][data-maple-heading='colorful'] {
14
- --maple-h1: hsl(0 45% 52.5%);
15
- --maple-h2: hsl(32 70% 37%);
16
- --maple-h3: hsl(95 75% 29%);
17
- --maple-h4: hsl(193 94% 31.5%);
18
- --maple-h5: hsl(260 20% 48%);
19
- --maple-h6: hsl(156 75% 28%);
20
- }
21
-
22
- html[data-maple-theme='on'][data-maple-heading='colorful'] body[data-ds-dark-theme] {
23
- --maple-h1: hsl(0 44% 68%);
24
- --maple-h2: hsl(20 40% 68%);
25
- --maple-h3: hsl(130 32% 68%);
26
- --maple-h4: hsl(210 80% 72%);
27
- --maple-h5: hsl(247 65% 82%);
28
- --maple-h6: hsl(178 40% 64%);
29
- }
30
-
31
- /* 强调色一档:六级标题统一用主题的强调色,不分级换色。
32
- * 这是 Maple 的「标题颜色 = 强调色」分支,与本主题既有的强调色取同一个值。 */
33
- html[data-maple-theme='on'][data-maple-heading='accent'] {
34
- --maple-heading-color: hsl(35 26% 38%);
35
- }
36
-
37
- /* 正文色一档:标题只靠字号与字重区分,颜色跟正文一致。 */
38
- html[data-maple-theme='on'][data-maple-heading='text'] {
39
- --maple-heading-color: hsl(var(--maple-hue-light, 35) 10% 18%);
40
- }
41
-
42
- html[data-maple-theme='on'][data-maple-heading='accent'] body[data-ds-dark-theme] {
43
- --maple-heading-color: hsl(207 26% 72%);
44
- }
45
-
46
- html[data-maple-theme='on'][data-maple-heading='text'] body[data-ds-dark-theme] {
47
- --maple-heading-color: hsl(var(--maple-hue-dark, 207) 24% 88%);
48
- }
49
-
50
- /* 代码语法色:Maple 的 prism 调色板落到宿主的 shiki 变量上。
51
- * 宿主 CodeBlock 只认 --shiki-token-*,换这一组就整体换配色。 */
52
- html[data-maple-theme='on'][data-maple-code='on'] {
53
- --shiki-token-keyword: hsl(260 20% 48%);
54
- --shiki-token-string: hsl(95 75% 29%);
55
- --shiki-token-string-expression: hsl(95 75% 29%);
56
- --shiki-token-function: hsl(193 94% 31.5%);
57
- --shiki-token-constant: hsl(156 75% 28%);
58
- --shiki-token-parameter: hsl(32 70% 37%);
59
- --shiki-token-comment: hsl(35 6% 58%);
60
- --shiki-token-punctuation: hsl(35 7% 46%);
61
- --shiki-token-link: hsl(162 76% 29%);
62
- }
63
-
64
- html[data-maple-theme='on'][data-maple-code='on'] body[data-ds-dark-theme] {
65
- --shiki-token-keyword: hsl(247 65% 82%);
66
- --shiki-token-string: hsl(130 32% 68%);
67
- --shiki-token-string-expression: hsl(130 32% 68%);
68
- --shiki-token-function: hsl(210 80% 72%);
69
- --shiki-token-constant: hsl(178 40% 64%);
70
- --shiki-token-parameter: hsl(20 40% 68%);
71
- --shiki-token-comment: hsl(207 10% 46%);
72
- --shiki-token-punctuation: hsl(207 12% 58%);
73
- --shiki-token-link: hsl(178 30% 62%);
74
- }
75
-
76
- /* 正文排版:MarkdownText 用 `font: var(--dsw-font-markdown-*)` 一次取字号、行高与字族,
77
- * 所以改这几条就够,不必碰 CSS Modules 的哈希类名。
78
- *
79
- * 行高从宿主的字号轴推,不写死像素:宿主把正文行高写成「24px + 字号增量」,
80
- * 这里改成「字号 × 配置的行高倍数」——两个轴同时生效,用户调大字号时行高跟着长。
81
- *
82
- * h5 / h6 宿主归并进 base-strong,没有独立令牌,由 markdown.css 用元素选择器补。 */
83
- html[data-maple-theme='on'] body {
84
- --dsw-font-markdown-base:
85
- var(--dsh-content-font-size, 14px) / calc(var(--dsh-content-font-size, 14px) * var(--maple-line-height, 1.75))
86
- var(--dsw-font-family);
87
- --dsw-font-markdown-base-font-size: var(--dsh-content-font-size, 14px);
88
- --dsw-font-markdown-base-line-height: calc(var(--dsh-content-font-size, 14px) * var(--maple-line-height, 1.75));
89
-
90
- --dsw-font-markdown-base-strong:
91
- 600 var(--dsh-content-font-size, 14px) / calc(var(--dsh-content-font-size, 14px) * var(--maple-line-height, 1.75))
92
- var(--dsw-font-family);
93
- --dsw-font-markdown-base-strong-font-size: var(--dsh-content-font-size, 14px);
94
- --dsw-font-markdown-base-strong-line-height: calc(var(--dsh-content-font-size, 14px) * var(--maple-line-height, 1.75));
95
-
96
- --dsw-font-markdown-h1:
97
- 700 calc(var(--dsh-content-font-size, 14px) * 1.6) / 1.3 var(--dsw-font-family);
98
- --dsw-font-markdown-h1-font-size: calc(var(--dsh-content-font-size, 14px) * 1.6);
99
- --dsw-font-markdown-h1-font-weight: 700;
100
- --dsw-font-markdown-h1-line-height: 1.3;
101
-
102
- --dsw-font-markdown-h2:
103
- 700 calc(var(--dsh-content-font-size, 14px) * 1.5) / 1.3 var(--dsw-font-family);
104
- --dsw-font-markdown-h2-font-size: calc(var(--dsh-content-font-size, 14px) * 1.5);
105
- --dsw-font-markdown-h2-font-weight: 700;
106
- --dsw-font-markdown-h2-line-height: 1.3;
107
-
108
- --dsw-font-markdown-h3:
109
- 600 calc(var(--dsh-content-font-size, 14px) * 1.35) / 1.35 var(--dsw-font-family);
110
- --dsw-font-markdown-h3-font-size: calc(var(--dsh-content-font-size, 14px) * 1.35);
111
- --dsw-font-markdown-h3-font-weight: 600;
112
- --dsw-font-markdown-h3-line-height: 1.35;
113
-
114
- --dsw-font-markdown-h4:
115
- 600 calc(var(--dsh-content-font-size, 14px) * 1.15) / 1.4 var(--dsw-font-family);
116
- --dsw-font-markdown-h4-font-size: calc(var(--dsh-content-font-size, 14px) * 1.15);
117
- --dsw-font-markdown-h4-font-weight: 600;
118
- --dsw-font-markdown-h4-line-height: 1.4;
119
- }