dsh-codinput 0.3.6 → 0.3.8
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/CHANGELOG.md +60 -13
- package/NOTICE +18 -3
- package/README.en.md +10 -7
- package/README.md +10 -7
- package/TESTING.md +19 -5
- package/lib/client.js +183 -54
- package/package.json +3 -2
- package/src/client/components/AttachmentRail.tsx +3 -3
- package/src/client/components/StatsRow.tsx +212 -91
- package/src/client/components/UserMessageMarkdown.tsx +676 -0
- package/src/client/i18n.ts +29 -9
- package/src/client/index.tsx +47 -1
- package/src/client/prefs.ts +3 -0
- package/src/client/settings.tsx +7 -0
- package/src/client/styles.ts +153 -19
package/src/client/i18n.ts
CHANGED
|
@@ -92,6 +92,15 @@ const ZH: Dict = {
|
|
|
92
92
|
'perm.preset.danger-full-access': '完全权限',
|
|
93
93
|
'set.enabled': '启用接管',
|
|
94
94
|
'set.enabledDesc': '关闭即恢复官方输入框,草稿不丢;/codinput 同样可切换',
|
|
95
|
+
'set.userMarkdown': '用户消息 Markdown 渲染',
|
|
96
|
+
'set.userMarkdownDesc': '右侧自己发出的消息按 Markdown 排版(标题/列表/代码块等);关闭恢复官方纯文本',
|
|
97
|
+
'umsg.copy': '复制',
|
|
98
|
+
'umsg.copied': '已复制',
|
|
99
|
+
'umsg.referenceSummary': '引用:{labels}',
|
|
100
|
+
'umsg.clock.md': '{m}月{d}日',
|
|
101
|
+
'umsg.clock.ymd': '{y}年{m}月{d}日',
|
|
102
|
+
'umsg.extraBlock': '额外内容块',
|
|
103
|
+
'umsg.image': '图片',
|
|
95
104
|
'set.lineNumbers': '显示行号',
|
|
96
105
|
'set.lineNumbersDesc': '当前行与行号高亮常开',
|
|
97
106
|
'set.font': '编辑器字体',
|
|
@@ -196,6 +205,15 @@ const EN: Dict = {
|
|
|
196
205
|
'perm.preset.danger-full-access': 'Full access',
|
|
197
206
|
'set.enabled': 'Take over composer',
|
|
198
207
|
'set.enabledDesc': 'Turning it off restores the official composer; the draft is kept. /codinput toggles it too.',
|
|
208
|
+
'set.userMarkdown': 'Markdown for user messages',
|
|
209
|
+
'set.userMarkdownDesc': 'Render your own (right-side) messages as Markdown — headings, lists, code blocks; off restores the official plain text',
|
|
210
|
+
'umsg.copy': 'Copy',
|
|
211
|
+
'umsg.copied': 'Copied',
|
|
212
|
+
'umsg.referenceSummary': 'Referenced: {labels}',
|
|
213
|
+
'umsg.clock.md': '{m}/{d}',
|
|
214
|
+
'umsg.clock.ymd': '{y}/{m}/{d}',
|
|
215
|
+
'umsg.extraBlock': 'Extra content block',
|
|
216
|
+
'umsg.image': 'Image',
|
|
199
217
|
'set.lineNumbers': 'Show line numbers',
|
|
200
218
|
'set.lineNumbersDesc': 'The current line and its gutter number stay highlighted',
|
|
201
219
|
'set.font': 'Editor font',
|
|
@@ -291,16 +309,18 @@ export function installI18n(locale: unknown): void {
|
|
|
291
309
|
* 的翻译函数;宿主 face 未就绪或 bind 失败返回 null。结果按 ns 缓存。
|
|
292
310
|
*/
|
|
293
311
|
export function bindForeign(ns: string): ((key: string, params?: TranslateParams) => string) | null {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
312
|
+
const cached = foreignBinds.get(ns);
|
|
313
|
+
if (cached) return cached;
|
|
314
|
+
// 只缓存成功的绑定:宿主 locale 服务可能晚于首次调用就绪,null 缓存会让
|
|
315
|
+
// 之后所有文案永远拿不到官方词典。
|
|
316
|
+
let bound: ((key: string, params?: TranslateParams) => string) | null = null;
|
|
317
|
+
try {
|
|
318
|
+
bound = hostFace?.bind?.(ns) ?? null;
|
|
319
|
+
} catch {
|
|
320
|
+
bound = null;
|
|
302
321
|
}
|
|
303
|
-
|
|
322
|
+
if (bound) foreignBinds.set(ns, bound);
|
|
323
|
+
return bound;
|
|
304
324
|
}
|
|
305
325
|
|
|
306
326
|
export function subscribeI18n(fn: () => void): () => void {
|
package/src/client/index.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import { diagError, diagWindow, runGuarded } from './diag';
|
|
|
17
17
|
import { loadPrefs, subscribePrefs } from './prefs';
|
|
18
18
|
import { isAnySidebarInputCarried, subscribeSidebarInput } from './sideinput';
|
|
19
19
|
import { barRegistration, CodinputBar } from './components/CodinputBar';
|
|
20
|
+
import { userMarkdownRegistrations } from './components/UserMessageMarkdown';
|
|
20
21
|
import { sidebarBodyRegistration, sidebarTabDefinition, sidebarTitleRegistration } from './sidebar';
|
|
21
22
|
import { settingsRegistration } from './settings';
|
|
22
23
|
import { registerCodinputCommand } from './command';
|
|
@@ -55,7 +56,12 @@ export function apply(ctx: ClientContext): void {
|
|
|
55
56
|
const orig = w.console.error.bind(w.console);
|
|
56
57
|
w.console.error = (...args: unknown[]): void => {
|
|
57
58
|
const first = args.find((a) => a instanceof Error);
|
|
58
|
-
if (first)
|
|
59
|
+
if (first) {
|
|
60
|
+
// 与 diagError 同口径限长:长会话里 console.error 不应无界堆积。
|
|
61
|
+
const errs = w.__dshCodinputErrs as unknown as unknown[];
|
|
62
|
+
errs.push(first);
|
|
63
|
+
if (errs.length > 200) errs.splice(0, errs.length - 200);
|
|
64
|
+
}
|
|
59
65
|
orig(...args);
|
|
60
66
|
};
|
|
61
67
|
}
|
|
@@ -121,6 +127,46 @@ export function apply(ctx: ClientContext): void {
|
|
|
121
127
|
subscribeSidebarInput(reconcileBar);
|
|
122
128
|
});
|
|
123
129
|
|
|
130
|
+
// ---- 用户消息 Markdown:conversation.chat.node 键控座 user/steering 遮蔽 ----
|
|
131
|
+
// 独立于接管开关,自带 prefs.userMarkdown;关闭即卸载条目把消息渲染还座
|
|
132
|
+
// 官方(同 key 低 priority 者胜,动态包守卫强制分配负秩)。
|
|
133
|
+
runGuarded('md.register', () => {
|
|
134
|
+
const mdSeats = userMarkdownRegistrations();
|
|
135
|
+
const mdMounted = new Map<string, () => void>();
|
|
136
|
+
const mdWanted = (): boolean => loadPrefs().userMarkdown;
|
|
137
|
+
const mountMdSeat = (seat: (typeof mdSeats)[number]): void => {
|
|
138
|
+
const key = seat.options.key ?? '';
|
|
139
|
+
if (mdMounted.has(key) || !mdWanted()) return;
|
|
140
|
+
mdMounted.set(key, ctx.slots.register(seat.options, seat.component));
|
|
141
|
+
diagWindow().__dshCodinputShell[`md.${key}`] = 'mounted';
|
|
142
|
+
};
|
|
143
|
+
const unmountMdSeat = (key: string): void => {
|
|
144
|
+
const disposer = mdMounted.get(key);
|
|
145
|
+
if (!disposer) return;
|
|
146
|
+
mdMounted.delete(key);
|
|
147
|
+
try {
|
|
148
|
+
disposer();
|
|
149
|
+
diagWindow().__dshCodinputShell[`md.${key}`] = 'unmounted';
|
|
150
|
+
} catch (error) {
|
|
151
|
+
diagError('md.dispose', error);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const reconcileMd = (): void => {
|
|
155
|
+
for (const seat of mdSeats) {
|
|
156
|
+
const key = seat.options.key ?? '';
|
|
157
|
+
if (mdWanted()) {
|
|
158
|
+
tryRegister(() => mountMdSeat(seat), `md.${key}`);
|
|
159
|
+
} else {
|
|
160
|
+
unmountMdSeat(key);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
reconcileMd();
|
|
165
|
+
runGuarded('md.prefs', () => {
|
|
166
|
+
subscribePrefs(reconcileMd);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
124
170
|
// ---- 侧边模式:sidebarRightTabs 两段式注册(dockkit 承担停靠/浮动) ----
|
|
125
171
|
runGuarded('sidebar.register', () => {
|
|
126
172
|
ctx.inject(['slots', 'sidebarRightTabs', 'sidebarRight'], (scope: Record<string, unknown>) => {
|
package/src/client/prefs.ts
CHANGED
|
@@ -17,6 +17,8 @@ export type CodinputMode = 'normal' | 'float' | 'side';
|
|
|
17
17
|
export interface CodinputPrefs {
|
|
18
18
|
/** 启用接管:关闭即恢复官方输入框(卸载遮蔽条目)。 */
|
|
19
19
|
enabled: boolean;
|
|
20
|
+
/** 用户消息 Markdown 渲染(键控座 conversation.chat.node 的 user/steering 遮蔽)。 */
|
|
21
|
+
userMarkdown: boolean;
|
|
20
22
|
/** 显示行号(当前行与行号高亮常开)。 */
|
|
21
23
|
lineNumbers: boolean;
|
|
22
24
|
/** 编辑器字体(默认 JetBrains Mono)。 */
|
|
@@ -43,6 +45,7 @@ export interface CodinputPrefs {
|
|
|
43
45
|
|
|
44
46
|
export const DEFAULT_PREFS: CodinputPrefs = {
|
|
45
47
|
enabled: true,
|
|
48
|
+
userMarkdown: true,
|
|
46
49
|
lineNumbers: true,
|
|
47
50
|
fontFamily: "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
48
51
|
sendHotkey: 'mod+enter',
|
package/src/client/settings.tsx
CHANGED
|
@@ -176,6 +176,13 @@ export function CodinputSettingsSection(): JSX.Element {
|
|
|
176
176
|
<Row title={t("set.enabled")} desc={t("set.enabledDesc")}>
|
|
177
177
|
<Switch on={prefs.enabled} label={t("set.enabled")} onToggle={() => update({ enabled: !prefs.enabled })} />
|
|
178
178
|
</Row>
|
|
179
|
+
<Row title={t("set.userMarkdown")} desc={t("set.userMarkdownDesc")}>
|
|
180
|
+
<Switch
|
|
181
|
+
on={prefs.userMarkdown}
|
|
182
|
+
label={t("set.userMarkdown")}
|
|
183
|
+
onToggle={() => update({ userMarkdown: !prefs.userMarkdown })}
|
|
184
|
+
/>
|
|
185
|
+
</Row>
|
|
179
186
|
<Row title={t("set.lineNumbers")} desc={t("set.lineNumbersDesc")}>
|
|
180
187
|
<Switch on={prefs.lineNumbers} label={t("set.lineNumbers")} onToggle={() => update({ lineNumbers: !prefs.lineNumbers })} />
|
|
181
188
|
</Row>
|
package/src/client/styles.ts
CHANGED
|
@@ -412,13 +412,19 @@ button.dci-attach-body { color: inherit; font: inherit; text-align: left; backgr
|
|
|
412
412
|
height: 36px; color: var(--dsw-alias-label-primary, inherit); border: none; border-radius: 18px; outline: none;
|
|
413
413
|
padding: 0 14px; font-size: 13px; min-width: 260px; max-width: 320px; }
|
|
414
414
|
.dci-set-input:focus { outline: 2px solid var(--dsw-alias-border-l3, transparent); }
|
|
415
|
+
/* 开关:官方 primitives Switch 逐值(Switch.module.css)。ON 轨道走
|
|
416
|
+
--dsw-alias-brand-primary——该 token 自 0.2.0-rc.2 起浅色主题即近黑(#0f1115)、
|
|
417
|
+
深色主题近白(#f9fafb),桌面 rc.3 同款;同 token 跟随宿主主题,就是官方样式的来源。 */
|
|
415
418
|
.dci-switch { box-sizing: border-box; position: relative; flex: none; width: 36px; height: 20px; padding: 2px;
|
|
416
|
-
border: 0; border-radius:
|
|
419
|
+
border: 0; border-radius: 999px; corner-shape: round;
|
|
420
|
+
background: var(--dsw-alias-border-l3, color-mix(in srgb, currentColor 30%, transparent));
|
|
417
421
|
cursor: pointer; }
|
|
418
422
|
.dci-switch[aria-checked="true"] { background: var(--dsw-alias-brand-primary, color-mix(in srgb, currentColor 70%, transparent)); }
|
|
419
|
-
.dci-switch:
|
|
420
|
-
.dci-switch
|
|
423
|
+
.dci-switch:disabled { cursor: default; opacity: 0.5; }
|
|
424
|
+
.dci-switch:focus-visible { outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary, currentColor)); outline-offset: 2px; }
|
|
425
|
+
.dci-switch::after { content: ''; display: block; width: 16px; height: 16px; border-radius: 50%; corner-shape: round;
|
|
421
426
|
background: var(--dsw-alias-label-primary-foreground, #fff); transition: transform 120ms ease; }
|
|
427
|
+
.dci-switch[aria-checked="false"]::after { background: var(--dsw-alias-switch-thumb, #fff); }
|
|
422
428
|
.dci-switch[aria-checked="true"]::after { transform: translateX(16px); }
|
|
423
429
|
|
|
424
430
|
|
|
@@ -440,15 +446,17 @@ button.dci-attach-body { color: inherit; font: inherit; text-align: left; backgr
|
|
|
440
446
|
padding: 4px 0 0; font-size: calc(var(--dsh-content-font-size-secondary, 13px) - 1px);
|
|
441
447
|
line-height: calc(20px + var(--dsh-content-font-delta-secondary, 0px));
|
|
442
448
|
justify-content: center; gap: 12px; display: flex; }
|
|
443
|
-
.dci-stat-anchor { min-width: 0; display: inline-flex;
|
|
449
|
+
.dci-stat-anchor { min-width: 0; display: inline-flex; }
|
|
444
450
|
|
|
445
|
-
/* 数据行详情面板(官方 bRhRbq
|
|
446
|
-
|
|
451
|
+
/* 数据行详情面板(官方 bRhRbq 复刻:portal 到 body、fixed、锚点上方弹出)。
|
|
452
|
+
背景与圆角逐值同官方:--dsw-specific-menu 优先(macOS 桌面上官方把它覆写成不透明),
|
|
453
|
+
旧宿主/皮肤缺该 token 时回退 --dsw-menu-surface-fill 链,不落到硬编码深色。 */
|
|
454
|
+
.dci-stat-panel { z-index: 1100; box-sizing: border-box; background: var(--dsw-specific-menu, var(--dsw-menu-surface-fill, rgba(30, 30, 34, 0.98)));
|
|
447
455
|
backdrop-filter: var(--dsw-menu-backdrop-filter, none);
|
|
448
456
|
color: var(--dsw-alias-label-secondary, inherit); --dsw-elevation-stroke-color: var(--dsw-alias-border-l1, transparent);
|
|
449
457
|
width: max-content; min-width: min(300px, 100vw - 24px); max-width: min(440px, 100vw - 24px); text-align: left;
|
|
450
458
|
box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35)); cursor: default; border: 0;
|
|
451
|
-
border-radius: 12px; padding: 16px; font-size: 12px; line-height: 18px; position:
|
|
459
|
+
border-radius: var(--dsw-radius-lg, 12px); padding: 16px; font-size: 12px; line-height: 18px; position: fixed; }
|
|
452
460
|
.dci-stat-panelTitle { color: var(--dsw-alias-label-primary, inherit); justify-content: space-between; gap: 16px;
|
|
453
461
|
margin-bottom: 8px; font-weight: 500; display: flex; }
|
|
454
462
|
.dci-stat-panelLabel { align-items: center; gap: 6px; min-width: 0; display: inline-flex; }
|
|
@@ -460,36 +468,40 @@ button.dci-attach-body { color: inherit; font: inherit; text-align: left; backgr
|
|
|
460
468
|
.dci-stat-details dt, .dci-stat-details dd { min-width: 0; margin: 0; }
|
|
461
469
|
.dci-stat-details dd { color: var(--dsw-alias-label-secondary, inherit); font-variant-numeric: tabular-nums; text-align: right; }
|
|
462
470
|
.dci-stat-pill { font: inherit; box-sizing: border-box; max-width: 100%; color: var(--dsw-alias-label-tertiary, currentColor);
|
|
463
|
-
white-space: nowrap; background: transparent; border: none; border-radius:
|
|
471
|
+
white-space: nowrap; background: transparent; border: none; border-radius: 999px; corner-shape: round;
|
|
472
|
+
line-height: inherit; align-items: center; gap: 6px;
|
|
464
473
|
padding: 1px 8px; display: inline-flex; cursor: pointer; font-variant-numeric: tabular-nums; }
|
|
465
474
|
.dci-stat-pill svg { flex: none; width: 14px; height: 14px; }
|
|
466
|
-
.dci-stat-pill:hover { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
|
|
475
|
+
.dci-stat-pill:hover, .dci-stat-pill[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
|
|
467
476
|
color: var(--dsw-alias-label-secondary, inherit); }
|
|
468
477
|
.dci-stat-label { text-overflow: ellipsis; min-width: 0; overflow: hidden; }
|
|
469
478
|
.dci-stat-sep { color: var(--dsw-alias-separator-primary, currentColor); margin: 0 6px; }
|
|
470
479
|
|
|
471
480
|
/* ===== 上下文小圈(官方 JObwrW 复刻:双环仪表 + 分段面板)
|
|
472
481
|
0.1.7-rc.2 起官方把它挪进卡片下方 dock 行(stats 胶囊之后),trigger 变为「圈+百分比」药丸 ===== */
|
|
473
|
-
.dci-gauge-root { flex: none; display: inline-flex;
|
|
482
|
+
.dci-gauge-root { flex: none; display: inline-flex; }
|
|
474
483
|
.dci-gauge-trigger { border-radius: var(--dsw-radius-sm, 8px); color: var(--dsw-alias-label-tertiary, currentColor);
|
|
475
|
-
font-family: inherit; font-size: 13px; font-variant-numeric: tabular-nums;
|
|
484
|
+
font-family: inherit; font-size: var(--dsh-content-font-size-secondary, 13px); font-variant-numeric: tabular-nums;
|
|
485
|
+
line-height: calc(20px + var(--dsh-content-font-delta-secondary, 0px)); white-space: nowrap;
|
|
476
486
|
cursor: pointer; background: transparent; border: none; flex: none; align-items: center; gap: 6px; padding: 1px 8px; display: inline-flex; }
|
|
477
487
|
.dci-gauge-trigger:hover, .dci-gauge-trigger[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
|
|
478
488
|
color: var(--dsw-alias-label-secondary, inherit); }
|
|
479
489
|
.dci-gauge-track { fill: none; stroke: var(--dsw-alias-border-l3, currentColor); stroke-width: 2px; }
|
|
480
490
|
.dci-gauge-fill { fill: none; stroke: var(--dsw-alias-label-tertiary, currentColor); stroke-width: 2px; stroke-linecap: round; }
|
|
481
|
-
|
|
491
|
+
/* 面板 portal 到 body + fixed,left/top 由 useAnchoredPanel(官方 useAnchoredPosition
|
|
492
|
+
移植:面板左缘对齐锚点左缘、面板底距锚点顶 8px、视口 margin 12 夹取)下发。 */
|
|
493
|
+
.dci-gauge-panel { z-index: 1100; box-sizing: border-box; background: var(--dsw-specific-menu, var(--dsw-menu-surface-fill, rgba(30, 30, 34, 0.98)));
|
|
482
494
|
backdrop-filter: var(--dsw-menu-backdrop-filter, none);
|
|
483
495
|
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1, transparent); width: min(264px, 100vw - 24px);
|
|
484
496
|
box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35));
|
|
485
497
|
color: var(--dsw-alias-label-secondary, inherit); cursor: default; border: 0; border-radius: var(--dsw-radius-lg, 12px); padding: 12px;
|
|
486
|
-
font-size: 12px; line-height: 20px; position:
|
|
498
|
+
font-size: 12px; line-height: 20px; position: fixed; }
|
|
487
499
|
.dci-gauge-header { align-items: center; gap: 6px; display: flex; }
|
|
488
500
|
.dci-gauge-figures { font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-primary, inherit); margin-left: auto; font-weight: 500; }
|
|
489
501
|
.dci-gauge-percent { color: var(--dsw-alias-label-primary, inherit); font-weight: 500; }
|
|
490
502
|
.dci-gauge-headline { color: var(--dsw-alias-label-tertiary, currentColor); }
|
|
491
503
|
.dci-gauge-bar { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
|
|
492
|
-
border-radius: 999px; gap: 1px; height: 4px; margin: 10px 0 12px; display: flex; overflow: hidden; }
|
|
504
|
+
border-radius: 999px; corner-shape: round; gap: 1px; height: 4px; margin: 10px 0 12px; display: flex; overflow: hidden; }
|
|
493
505
|
.dci-gauge-segment { background: var(--meter-tint, var(--dsw-alias-label-tertiary, currentColor)); border-radius: 1px;
|
|
494
506
|
flex: 0 0 auto; min-width: 2px; height: 100%; }
|
|
495
507
|
.dci-gauge-swatch { background: var(--meter-tint, currentColor); vertical-align: baseline; border-radius: 2px;
|
|
@@ -537,12 +549,134 @@ button.dci-attach-body { color: inherit; font: inherit; text-align: left; backgr
|
|
|
537
549
|
.dci-btn-primary:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover, currentColor); }
|
|
538
550
|
.dci-btn-cancel { min-width: 72px; }
|
|
539
551
|
.dci-btn-confirm { min-width: 136px; }
|
|
552
|
+
|
|
553
|
+
/* ===== 用户消息 Markdown(chat.node 键控座遮蔽条目)=====
|
|
554
|
+
气泡/操作行/附件行取值逐条移植官方 MessageItem.module.css 与
|
|
555
|
+
MessageIconActions.module.css;Markdown 排版逐条移植官方
|
|
556
|
+
MarkdownText.module.css;引用 chip 逐条移植官方 user-text.module.css。
|
|
557
|
+
官方走 --dsw-* token 的取值这里同样走 token(换肤全盘跟随)。 */
|
|
558
|
+
.dci-umsg-row { flex-direction: column; align-items: flex-end; gap: 6px; display: flex; }
|
|
559
|
+
.dci-umsg-stack { min-width: 0; max-width: min(calc(var(--dsh-chat-content-width, 748px) * 0.702), 82%);
|
|
560
|
+
flex-direction: column; align-items: flex-end; gap: 8px; display: flex; }
|
|
561
|
+
.dci-umsg-bubble { background: var(--dsw-specific-bubble); border-radius: var(--dsw-radius-xl); max-width: 100%;
|
|
562
|
+
font-size: var(--dsh-content-font-size, 14px); line-height: calc(22px + var(--dsh-content-font-delta, 0px));
|
|
563
|
+
color: var(--dsw-alias-label-primary); white-space: pre-wrap; word-break: break-word; padding: 10px 16px; }
|
|
564
|
+
.dci-umsg-refsum { color: var(--dsw-alias-label-tertiary);
|
|
565
|
+
font-size: var(--dsh-content-font-size-secondary, 13px); line-height: calc(18px + var(--dsh-content-font-delta-secondary, 0px)); }
|
|
566
|
+
.dci-umsg-attrow { flex-wrap: wrap; justify-content: flex-end; gap: 8px; max-width: 100%; display: flex; }
|
|
567
|
+
.dci-umsg-file { border: 0.5px solid var(--dsw-alias-border-l2, #0000001f); border-radius: var(--dsw-radius-xl);
|
|
568
|
+
background: var(--dsw-specific-input-major, transparent); box-sizing: border-box; flex: 0 0 240px;
|
|
569
|
+
align-items: center; gap: 10px; width: 240px; min-height: 64px; padding: 8px 12px; display: inline-flex; }
|
|
570
|
+
.dci-umsg-fileglyph { flex: none; width: 28px; height: 28px; }
|
|
571
|
+
.dci-umsg-filecontent { flex-direction: column; flex: 1; min-width: 0; display: flex; }
|
|
572
|
+
.dci-umsg-filename { white-space: nowrap; text-overflow: ellipsis; color: var(--dsw-alias-label-primary);
|
|
573
|
+
font-size: 14px; font-weight: 500; line-height: 22px; overflow: hidden; }
|
|
574
|
+
.dci-umsg-filemeta { white-space: nowrap; text-overflow: ellipsis; color: var(--dsw-alias-label-tertiary, #00000073);
|
|
575
|
+
font-size: 12px; line-height: 15px; overflow: hidden; }
|
|
576
|
+
.dci-umsg-actions { height: calc(28px + var(--dsh-content-font-delta, 0px)); align-items: center; gap: 8px; display: flex; }
|
|
577
|
+
.dci-umsg-time { font-size: var(--dsh-content-font-size-secondary, 13px);
|
|
578
|
+
line-height: calc(24px + var(--dsh-content-font-delta, 0px)); color: var(--dsw-alias-label-tertiary); white-space: nowrap; padding-right: 12px; }
|
|
579
|
+
.dci-umsg-action { width: calc(28px + var(--dsh-content-font-delta, 0px)); height: calc(28px + var(--dsh-content-font-delta, 0px));
|
|
580
|
+
border-radius: var(--dsw-radius-sm); color: var(--dsw-alias-label-tertiary); cursor: pointer; background: 0 0;
|
|
581
|
+
border: none; justify-content: center; align-items: center; padding: 6px; display: inline-flex; }
|
|
582
|
+
.dci-umsg-action svg { width: calc(15px + var(--dsh-content-font-delta, 0px)); height: calc(15px + var(--dsh-content-font-delta, 0px)); }
|
|
583
|
+
.dci-umsg-action:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-secondary); }
|
|
584
|
+
/* 悬停显形(官方 MessageItem.module.css 同款):data-actions-reveal 挂在 assistant 的
|
|
585
|
+
turn-tail 根上、不在用户消息的祖先链里,用户消息的实际驱动只有 flowItem 的
|
|
586
|
+
data-chat-flow-kind 兄弟关系(后一条 user/steering 存在即隐藏,悬停/聚焦显形)。 */
|
|
587
|
+
@media (hover: hover) {
|
|
588
|
+
:is([data-chat-flow-kind="user"], [data-chat-flow-kind="steering"]):has(~ :is([data-chat-flow-kind="user"], [data-chat-flow-kind="steering"])) .dci-umsg-actions { opacity: 0; transition: opacity 80ms; }
|
|
589
|
+
:is([data-chat-flow-kind="user"], [data-chat-flow-kind="steering"]):hover .dci-umsg-actions,
|
|
590
|
+
:is([data-chat-flow-kind="user"], [data-chat-flow-kind="steering"]):focus-within .dci-umsg-actions { opacity: 1; }
|
|
591
|
+
}
|
|
592
|
+
.dci-umsg-extra { margin: 8px 0 0; padding: 8px; border: 0.5px solid var(--dsw-alias-border-l2);
|
|
593
|
+
border-radius: var(--dsw-radius-sm); font-family: var(--ds-font-family-code); font-size: 12px; line-height: 18px;
|
|
594
|
+
overflow: auto; max-height: 200px; white-space: pre; }
|
|
595
|
+
|
|
596
|
+
/* Markdown 排版(官方 MarkdownText.module.css 逐条移植,.markdown → 本根)。 */
|
|
597
|
+
.dci-umsg-md { min-width: 0; overflow-wrap: anywhere; font: var(--dsw-font-markdown-base);
|
|
598
|
+
color: var(--dsw-alias-label-primary); white-space: normal; }
|
|
599
|
+
.dci-umsg-md strong { font-weight: 600; }
|
|
600
|
+
.dci-umsg-md h1 { font: var(--dsw-font-markdown-h1); margin: 32px 0 16px; }
|
|
601
|
+
.dci-umsg-md h2 { font: var(--dsw-font-markdown-h2); margin: 32px 0 16px; }
|
|
602
|
+
.dci-umsg-md h3 { font: var(--dsw-font-markdown-h3); margin: 32px 0 16px; }
|
|
603
|
+
.dci-umsg-md h4 { font: var(--dsw-font-markdown-h4); margin: 16px 0; }
|
|
604
|
+
.dci-umsg-md :where(h5, h6) { font: var(--dsw-font-markdown-base-strong); margin: 16px 0; }
|
|
605
|
+
.dci-umsg-md :where(h1, h2, h3, h4, h5, h6) strong { font-weight: inherit; }
|
|
606
|
+
.dci-umsg-md p { margin: 16px 0; }
|
|
607
|
+
.dci-umsg-md :where(h4, h5, h6) + :where(ul, ol) { margin-top: 8px; }
|
|
608
|
+
.dci-umsg-md :where(h4, h5, h6):has(+ :where(ul, ol)) { margin-bottom: 8px; }
|
|
609
|
+
.dci-umsg-md a { color: var(--dsw-alias-link); font-weight: 500;
|
|
610
|
+
transition: box-shadow var(--ds-transition-duration) var(--ds-ease-in-out); position: relative; text-decoration: none;
|
|
611
|
+
border-left: 3px solid rgb(255 255 255 / 0); border-right: 3px solid rgb(255 255 255 / 0);
|
|
612
|
+
border-top: 2px solid rgb(255 255 255 / 0); border-bottom: 2px solid rgb(255 255 255 / 0);
|
|
613
|
+
margin-left: -3px; margin-right: -3px; }
|
|
614
|
+
.dci-umsg-md a:hover, .dci-umsg-md a:focus { outline: none; text-decoration: underline dotted var(--dsw-alias-link); text-underline-offset: 3px; }
|
|
615
|
+
.dci-umsg-md a:focus-visible { box-shadow: 0 0 0 2px var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); }
|
|
616
|
+
.dci-umsg-md :where(ul, ol) { margin: 16px 0; padding-left: 18px; }
|
|
617
|
+
.dci-umsg-md li:not(:first-child) { margin-top: 6px; }
|
|
618
|
+
.dci-umsg-md li > :where(ul, ol) { margin-top: 4px; }
|
|
619
|
+
.dci-umsg-md li::marker { line-height: 24px; color: var(--dsw-alias-label-secondary); }
|
|
620
|
+
.dci-umsg-md :where(ul, ol) ol { list-style-position: inside; padding-left: 0; }
|
|
621
|
+
.dci-umsg-md :where(ul, ol) ol li p { display: inline; }
|
|
622
|
+
.dci-umsg-md li > p { margin: 8px 0; }
|
|
623
|
+
.dci-umsg-md li > *:first-child { margin-top: 0; }
|
|
624
|
+
.dci-umsg-md hr { display: block; border: none; height: 0.5px; margin: 32px 0; background: var(--dsw-alias-border-l2); }
|
|
625
|
+
.dci-umsg-md blockquote { border-left: 2px solid var(--dsw-alias-label-caption); margin: 16px 0 0; padding-left: 14px; }
|
|
626
|
+
/* 围栏代码块:官方 CodeBlock.module.css 的 .block :where(pre) 逐值——底色/圆角/
|
|
627
|
+
字体走官方 token(换肤跟随),气泡内靠底色与气泡面区分。语言横幅仍不做
|
|
628
|
+
(组件头注释的已知边界)。 */
|
|
629
|
+
.dci-umsg-md pre { margin: 16px 0; padding: 16px; font: var(--dsw-font-markdown-code-block, var(--ds-font-family-code));
|
|
630
|
+
background: var(--dsw-alias-markdown-code-block, color-mix(in srgb, currentColor 6%, transparent));
|
|
631
|
+
color: var(--dsw-alias-label-primary); white-space: pre-wrap; word-break: break-all; overflow-x: auto;
|
|
632
|
+
border-radius: var(--dsw-radius-lg); }
|
|
633
|
+
.dci-umsg-md pre code { font: inherit; background: none; padding: 0; }
|
|
634
|
+
.dci-umsg-md :not(pre) > code { display: inline-flex; align-items: center; box-sizing: border-box;
|
|
635
|
+
font: var(--dsw-font-markdown-code); font-family: var(--ds-font-family-code); font-size: 0.875em !important;
|
|
636
|
+
/* 底色与围栏代码块同一 token(--dsw-alias-markdown-code-block,产品决策 2026-10-29:
|
|
637
|
+
两种代码面同色)。官方 --dsw-alias-markdown-inline-code 对页面底设计(深色
|
|
638
|
+
rgb(41,41,41)),进用户气泡(深色 rgb(44,44,46))只差 3/255 不可辨,故不取它。 */
|
|
639
|
+
background-color: var(--dsw-alias-markdown-code-block, color-mix(in srgb, currentColor 6%, transparent));
|
|
640
|
+
border: 0.5px solid var(--dsw-alias-border-l1);
|
|
641
|
+
border-radius: var(--dsw-radius-sm); padding: 0 5px; }
|
|
642
|
+
.dci-umsg-md :where(h1, h2, h3, h4, h5, h6) code { font: inherit; font-family: var(--ds-font-family-code); }
|
|
643
|
+
.dci-umsg-md input[type='checkbox'] { margin: 0 8px 0 0; accent-color: var(--dsw-alias-label-secondary); }
|
|
644
|
+
.dci-umsg-md img { display: block; width: auto; max-width: 100%; height: auto; margin: 0;
|
|
645
|
+
border-radius: var(--dsw-radius-md); background: var(--dsw-alias-bg-base); object-fit: contain; }
|
|
646
|
+
.dci-umsg-tscroll { max-width: 100%; overflow-x: auto; overscroll-behavior-x: contain; }
|
|
647
|
+
.dci-umsg-tscroll table { border-collapse: collapse; width: max-content; max-width: max-content; }
|
|
648
|
+
.dci-umsg-tscroll th { text-align: start; padding: 10px 16px; border-bottom: 0.5px solid var(--dsw-alias-border-l3);
|
|
649
|
+
border-top: none; font: var(--dsw-font-markdown-table-head); max-width: min(30vw, 320px); min-width: 100px; }
|
|
650
|
+
.dci-umsg-tscroll td { padding: 10px 16px; border-bottom: 0.5px solid var(--dsw-alias-border-l2);
|
|
651
|
+
font: var(--dsw-font-markdown-table); max-width: min(30vw, 320px); min-width: 100px; }
|
|
652
|
+
.dci-umsg-tscroll th:first-child, .dci-umsg-tscroll td:first-child { padding-left: 0; }
|
|
653
|
+
.dci-umsg-tscroll td:last-child { padding-right: 0; }
|
|
654
|
+
.dci-umsg-tscroll table code { font-size: 11px; }
|
|
655
|
+
.dci-umsg-md > :first-child, .dci-umsg-md p:first-child { margin-top: 0 !important; }
|
|
656
|
+
.dci-umsg-md > :last-child, .dci-umsg-md p:last-child { margin-bottom: 0 !important; }
|
|
657
|
+
|
|
658
|
+
/* 引用 chip(官方 user-text.module.css 逐条移植):inline 走消费方基线,
|
|
659
|
+
可点 chip 用官方 fileMention 的链接语言。 */
|
|
660
|
+
.dci-umsg-chip { display: inline; max-width: 100%; margin: 0 2px; color: var(--dsw-alias-state-business-primary);
|
|
661
|
+
font-weight: 500; white-space: inherit; overflow-wrap: anywhere; text-align: inherit; }
|
|
662
|
+
button.dci-umsg-chip { margin: 0; padding: 0; border: none; background: none; font: inherit;
|
|
663
|
+
color: var(--dsw-alias-link); text-decoration: none; cursor: pointer; }
|
|
664
|
+
button.dci-umsg-chip:hover, button.dci-umsg-chip:focus { outline: none; text-decoration: underline dotted var(--dsw-alias-link); text-underline-offset: 3px; }
|
|
665
|
+
button.dci-umsg-chip:focus-visible { box-shadow: 0 0 0 2px var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); }
|
|
666
|
+
.dci-umsg-chip[data-chip="slash"] { font-family: var(--dsw-font-markdown-code-font-family); }
|
|
667
|
+
.dci-umsg-chipicon { width: 1em; height: 1em; margin-right: 4px; vertical-align: -0.125em; }
|
|
540
668
|
`;
|
|
541
669
|
|
|
542
670
|
export function injectStyles(): void {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
el
|
|
547
|
-
|
|
671
|
+
const el = document.getElementById(STYLE_ID);
|
|
672
|
+
// 已有本插件样式(宿主热替换/原地更新插件后重 apply)时按内容原位刷新:
|
|
673
|
+
// 旧 <style> 还挂着会让新版新增的类(如 dci-umsg-*)永远注入不进去。
|
|
674
|
+
if (el) {
|
|
675
|
+
if (el.textContent !== CSS) el.textContent = CSS;
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
const tag = document.createElement('style');
|
|
679
|
+
tag.id = STYLE_ID;
|
|
680
|
+
tag.textContent = CSS;
|
|
681
|
+
document.head.appendChild(tag);
|
|
548
682
|
}
|