dsh-structured 0.0.0-stage → 0.7.4
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/ACCEPTANCE.md +3 -0
- package/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +239 -2
- package/assets/screenshots/01-title-card.png +0 -0
- package/assets/screenshots/06-status-table.png +0 -0
- package/assets/screenshots/07-code-block.png +0 -0
- package/assets/screenshots/08-narrow.png +0 -0
- package/cordis.patch.yml +20 -0
- package/docs/CARD-SYSTEM.md +62 -0
- package/docs/DESIGN-REFERENCES.md +51 -0
- package/docs/METRICS.md +89 -0
- package/docs/RISK-ROLLBACK.md +129 -0
- package/docs/ROADMAP.md +85 -0
- package/docs/TROUBLESHOOT-VISUAL.md +78 -0
- package/docs/VISUAL-ACCEPTANCE.md +224 -0
- package/docs/VISUAL-CRITIQUE.md +57 -0
- package/docs/VISUAL-PLAN.md +133 -0
- package/icon.svg +10 -0
- package/lib/client.js +467 -0
- package/lib/index.js +483 -0
- package/locale/en.json +1 -0
- package/locale/zh.json +1 -0
- package/package.json +83 -4
- package/screenshots.json +6 -0
- package/scripts/build.mjs +22 -0
- package/scripts/desktop-check.mjs +129 -0
- package/scripts/lib/session-log.mjs +176 -0
- package/scripts/measure-prose.mjs +227 -0
- package/scripts/session-inspect.mjs +123 -0
- package/scripts/uninstall.mjs +53 -0
- package/src/client.js +465 -0
- package/src/index.js +481 -0
- package/test/structured.test.mjs +537 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 单命令卸载:先移除 profile 里的插件(package + bundle 行),再删除状态目录。
|
|
4
|
+
* 不碰其他插件;只删 $DSH_HOME/dsh-structured。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* node scripts/uninstall.mjs --profile structured-test
|
|
8
|
+
* node scripts/uninstall.mjs --profile structured-test --dry-run
|
|
9
|
+
* node scripts/uninstall.mjs # 只清理状态目录
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, rmSync } from 'node:fs'
|
|
13
|
+
import { homedir } from 'node:os'
|
|
14
|
+
import { basename, join, resolve } from 'node:path'
|
|
15
|
+
import { spawnSync } from 'node:child_process'
|
|
16
|
+
|
|
17
|
+
const argv = process.argv.slice(2)
|
|
18
|
+
const profileIndex = argv.indexOf('--profile')
|
|
19
|
+
const profile = profileIndex >= 0 ? argv[profileIndex + 1] : null
|
|
20
|
+
const dryRun = argv.includes('--dry-run')
|
|
21
|
+
const dshHome = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
22
|
+
const target = resolve(dshHome, 'dsh-structured')
|
|
23
|
+
|
|
24
|
+
if (profile) {
|
|
25
|
+
if (dryRun) {
|
|
26
|
+
process.stdout.write('uninstall: 将执行 dsh plugin --profile ' + profile + ' remove dsh-structured(dry-run)\n')
|
|
27
|
+
} else {
|
|
28
|
+
const safeProfile = String(profile).replace(/[^a-zA-Z0-9._-]/g, '')
|
|
29
|
+
const command = 'dsh plugin --profile "' + safeProfile + '" remove dsh-structured'
|
|
30
|
+
const result = spawnSync(command, {
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
shell: true,
|
|
33
|
+
env: process.env,
|
|
34
|
+
})
|
|
35
|
+
if (result.error || result.status !== 0) {
|
|
36
|
+
process.stderr.write('uninstall: dsh plugin remove 失败,未删除状态目录\n')
|
|
37
|
+
process.exitCode = result.status || 1
|
|
38
|
+
process.exit()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (basename(target) !== 'dsh-structured') {
|
|
44
|
+
process.stderr.write('uninstall: 拒绝删除非目标目录 ' + target + '\n')
|
|
45
|
+
process.exitCode = 2
|
|
46
|
+
} else if (!existsSync(target)) {
|
|
47
|
+
process.stdout.write('uninstall: 状态目录不存在,无需清理:' + target + '\n')
|
|
48
|
+
} else if (dryRun) {
|
|
49
|
+
process.stdout.write('uninstall: 将删除 ' + target + '(dry-run,未执行)\n')
|
|
50
|
+
} else {
|
|
51
|
+
rmSync(target, { recursive: true, force: true })
|
|
52
|
+
process.stdout.write('uninstall: 已删除 ' + target + '\n')
|
|
53
|
+
}
|
package/src/client.js
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-structured client
|
|
3
|
+
*
|
|
4
|
+
* 两层,都是纯 DOM 增强,不改消息内容、不替换 app root:
|
|
5
|
+
* 1) 对话 markdown 视觉层:标题字号/层级、表格、代码块、引用块、列表、链接的排版与配色。
|
|
6
|
+
* 2) 语义装饰:结论卡片、风险/验收等 callout 色调、标题关键词图标、代码块语言色。
|
|
7
|
+
*
|
|
8
|
+
* 只给宿主已有节点加 class/attribute,不搬 React 节点;markdown 重渲染后由 MutationObserver
|
|
9
|
+
* 重新扫描。主题色全部走 --dsw-alias-*,自动跟随明暗主题。
|
|
10
|
+
*/
|
|
11
|
+
window.__ModuleLoader__.load({
|
|
12
|
+
id: 'dsh-structured',
|
|
13
|
+
factory() {
|
|
14
|
+
const CARD_ATTR = 'data-dsh-structured-card'
|
|
15
|
+
const CARD_TYPE_ATTR = 'data-dsh-structured-card-type'
|
|
16
|
+
const ICON_ATTR = 'data-dsh-structured-icon'
|
|
17
|
+
const TONE_ATTR = 'data-dsh-structured-tone'
|
|
18
|
+
const LANG_ATTR = 'data-dsh-structured-lang'
|
|
19
|
+
const STATUS_ATTR = 'data-dsh-structured-status'
|
|
20
|
+
const STATUS_TABLE_ATTR = 'data-dsh-structured-status-table'
|
|
21
|
+
const LEAD_ATTR = 'data-dsh-structured-lead'
|
|
22
|
+
const ROW_ATTR = 'data-dsh-structured-row'
|
|
23
|
+
const ROOT_ATTR = 'data-dsh-structured'
|
|
24
|
+
const STYLE_ID = 'dsh-structured-skin-style'
|
|
25
|
+
const LEGACY_STYLE_ID = 'dsh-structured-card-style'
|
|
26
|
+
|
|
27
|
+
const CARD_TYPES = [
|
|
28
|
+
[/^结论|^总结|^答案/, 'conclusion'],
|
|
29
|
+
[/^判断|^决策/, 'decision'],
|
|
30
|
+
[/^指标|^数据/, 'metric'],
|
|
31
|
+
[/^风险|^注意|^警告/, 'risk'],
|
|
32
|
+
[/^下一步|^行动|^待办/, 'next'],
|
|
33
|
+
[/^验收|^通过|^完成/, 'accept'],
|
|
34
|
+
[/^重点|^关键/, 'key'],
|
|
35
|
+
]
|
|
36
|
+
const ICONS = [
|
|
37
|
+
[/结论|总结|答案|判断/, '◆'],
|
|
38
|
+
[/方案|计划|步骤|流程/, '▸'],
|
|
39
|
+
[/风险|注意|警告|坑/, '⚠'],
|
|
40
|
+
[/验收|检查|核对/, '✓'],
|
|
41
|
+
[/对比|选型|取舍/, '⇄'],
|
|
42
|
+
[/下一步|行动|待办/, '→'],
|
|
43
|
+
[/背景|原因|根因/, '◎'],
|
|
44
|
+
[/指标|数据|结果/, '▦'],
|
|
45
|
+
]
|
|
46
|
+
const TONES = [
|
|
47
|
+
[/风险|注意|警告|坑/, 'warn'],
|
|
48
|
+
[/验收|通过|成功/, 'success'],
|
|
49
|
+
[/重点|关键|核心/, 'info'],
|
|
50
|
+
[/下一步|行动|待办/, 'info'],
|
|
51
|
+
[/结论|判断|答案/, 'info'],
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const CSS = `
|
|
55
|
+
[class*="_markdown_"]{
|
|
56
|
+
--ds-accent:#2563eb;
|
|
57
|
+
--ds-text:var(--dsw-alias-label-primary,#111827);
|
|
58
|
+
--ds-muted:var(--dsw-alias-label-secondary,#6b7280);
|
|
59
|
+
--ds-border:var(--dsw-alias-border-l2,rgba(127,127,127,.22));
|
|
60
|
+
--ds-surface:var(--dsw-alias-bg-layer-1,#fff);
|
|
61
|
+
--ds-surface-2:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.06));
|
|
62
|
+
font-size:15px;line-height:1.72;letter-spacing:.01em;
|
|
63
|
+
}
|
|
64
|
+
[data-theme="dark"] [class*="_markdown_"]{--ds-accent:#60a5fa}
|
|
65
|
+
[class*="_markdown_"] p{margin:.72em 0}
|
|
66
|
+
[class*="_markdown_"] h1,[class*="_markdown_"] h2,[class*="_markdown_"] h3,
|
|
67
|
+
[class*="_markdown_"] h4,[class*="_markdown_"] h5,[class*="_markdown_"] h6{
|
|
68
|
+
color:var(--ds-text);line-height:1.32;letter-spacing:-.01em;
|
|
69
|
+
}
|
|
70
|
+
[class*="_markdown_"] h1{font-size:26px;font-weight:700;margin:32px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--ds-border)}
|
|
71
|
+
[class*="_markdown_"] h2{font-size:21px;font-weight:700;margin:28px 0 10px;padding-left:12px;border-left:3px solid var(--ds-accent)}
|
|
72
|
+
[class*="_markdown_"] h3{font-size:17px;font-weight:650;margin:22px 0 8px}
|
|
73
|
+
[class*="_markdown_"] h4{font-size:15px;font-weight:650;margin:16px 0 6px}
|
|
74
|
+
[class*="_markdown_"] h5,[class*="_markdown_"] h6{font-size:13px;font-weight:700;margin:14px 0 6px;color:var(--ds-muted);text-transform:uppercase;letter-spacing:.06em}
|
|
75
|
+
[class*="_markdown_"] h2[${ICON_ATTR}]::before,
|
|
76
|
+
[class*="_markdown_"] h3[${ICON_ATTR}]::before{
|
|
77
|
+
content:attr(${ICON_ATTR});
|
|
78
|
+
display:inline-flex;align-items:center;justify-content:center;
|
|
79
|
+
width:22px;height:22px;margin-right:8px;border-radius:6px;
|
|
80
|
+
background:color-mix(in srgb,var(--ds-accent) 12%,transparent);
|
|
81
|
+
color:var(--ds-accent);font-size:12px;font-weight:700;vertical-align:2px;
|
|
82
|
+
}
|
|
83
|
+
[class*="_markdown_"] strong{font-weight:700;color:var(--ds-text)}
|
|
84
|
+
[class*="_markdown_"] a{color:var(--ds-accent);text-decoration:none;border-bottom:1px solid color-mix(in srgb,var(--ds-accent) 38%,transparent)}
|
|
85
|
+
[class*="_markdown_"] a:hover{border-bottom-color:var(--ds-accent)}
|
|
86
|
+
[class*="_markdown_"] ul,[class*="_markdown_"] ol{padding-left:1.5em;margin:.6em 0}
|
|
87
|
+
[class*="_markdown_"] li{margin:.28em 0}
|
|
88
|
+
[class*="_markdown_"] li::marker{color:var(--ds-accent)}
|
|
89
|
+
[class*="_markdown_"] hr{height:1px;border:0;margin:28px 0;background:linear-gradient(90deg,transparent,var(--ds-border),transparent)}
|
|
90
|
+
[class*="_markdown_"] table{
|
|
91
|
+
width:100%;margin:18px 0;border-collapse:separate;border-spacing:0;
|
|
92
|
+
border:1px solid var(--ds-border);border-radius:12px;overflow:hidden;font-size:13.5px;
|
|
93
|
+
box-shadow:0 1px 2px rgba(0,0,0,.03);
|
|
94
|
+
}
|
|
95
|
+
[class*="_markdown_"] thead th{
|
|
96
|
+
background:var(--ds-surface-2);
|
|
97
|
+
font-size:11px!important;letter-spacing:.07em!important;color:var(--ds-muted);
|
|
98
|
+
font-weight:700!important;text-align:left;padding:12px 18px!important;
|
|
99
|
+
border-bottom:1px solid var(--ds-border);
|
|
100
|
+
}
|
|
101
|
+
[class*="_markdown_"] tbody td{
|
|
102
|
+
padding:13px 18px!important;line-height:1.55;vertical-align:middle;border-left:0;border-right:0;
|
|
103
|
+
border-bottom:1px solid color-mix(in srgb,var(--ds-border) 55%,transparent);
|
|
104
|
+
word-break:break-word;
|
|
105
|
+
}
|
|
106
|
+
[class*="_markdown_"] tbody tr:nth-child(even){background:color-mix(in srgb,var(--ds-surface-2) 45%,transparent)}
|
|
107
|
+
[class*="_markdown_"] tbody tr:hover{background:color-mix(in srgb,var(--ds-accent) 5%,transparent)}
|
|
108
|
+
[class*="_markdown_"] tbody tr:last-child td{border-bottom:0}
|
|
109
|
+
[class*="_markdown_"] table:has(thead th:nth-child(2):last-child) th:first-child,
|
|
110
|
+
[class*="_markdown_"] table:has(thead th:nth-child(2):last-child) td:first-child{width:30%}
|
|
111
|
+
[class*="_markdown_"] table:not([${STATUS_TABLE_ATTR}]):has(thead th:nth-child(3):last-child) th:first-child,
|
|
112
|
+
[class*="_markdown_"] table:not([${STATUS_TABLE_ATTR}]):has(thead th:nth-child(3):last-child) td:first-child{width:24%}
|
|
113
|
+
[class*="_markdown_"] table:not([${STATUS_TABLE_ATTR}]):has(thead th:nth-child(3):last-child) th:nth-child(2),
|
|
114
|
+
[class*="_markdown_"] table:not([${STATUS_TABLE_ATTR}]):has(thead th:nth-child(3):last-child) td:nth-child(2){width:22%}
|
|
115
|
+
[class*="_markdown_"] table[${STATUS_TABLE_ATTR}] th:first-child,
|
|
116
|
+
[class*="_markdown_"] table[${STATUS_TABLE_ATTR}] td:first-child{
|
|
117
|
+
width:68px;text-align:center;padding:7px 8px!important;font-size:11px;font-weight:700;
|
|
118
|
+
white-space:nowrap;vertical-align:middle;border-radius:7px;
|
|
119
|
+
}
|
|
120
|
+
[class*="_markdown_"] table[${STATUS_TABLE_ATTR}] tbody tr[${ROW_ATTR}="success"] td:first-child{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#16a34a) 12%,transparent);color:var(--dsw-alias-state-success-primary,#16a34a)}
|
|
121
|
+
[class*="_markdown_"] table[${STATUS_TABLE_ATTR}] tbody tr[${ROW_ATTR}="warn"] td:first-child{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#d97706) 12%,transparent);color:var(--dsw-alias-state-warn-primary,#d97706)}
|
|
122
|
+
[class*="_markdown_"] table[${STATUS_TABLE_ATTR}] tbody tr[${ROW_ATTR}="error"] td:first-child{background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#dc2626) 12%,transparent);color:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
123
|
+
[class*="_markdown_"] p[${LEAD_ATTR}="table"]{
|
|
124
|
+
border-left:2px solid var(--ds-accent);padding-left:10px;margin:10px 0 8px;
|
|
125
|
+
font-size:14px;color:var(--ds-text);
|
|
126
|
+
}
|
|
127
|
+
[class*="_markdown_"] p[${LEAD_ATTR}="table"] strong{color:var(--ds-accent)}
|
|
128
|
+
[class*="_markdown_"] h2+table,[class*="_markdown_"] h2+p,[class*="_markdown_"] h3+table,[class*="_markdown_"] h3+p{margin-top:8px}
|
|
129
|
+
[class*="_markdown_"] :not(pre)>code{
|
|
130
|
+
background:var(--ds-surface-2);border:1px solid var(--ds-border);border-radius:6px;
|
|
131
|
+
padding:.12em .38em;font-size:.9em;color:var(--ds-accent);overflow-wrap:anywhere;
|
|
132
|
+
}
|
|
133
|
+
[class*="_markdown_"] .md-code-block{
|
|
134
|
+
margin:16px 0;border:1px solid var(--ds-border);border-radius:12px;overflow:hidden;
|
|
135
|
+
background:var(--ds-surface-2);box-shadow:0 1px 2px rgba(0,0,0,.04);
|
|
136
|
+
}
|
|
137
|
+
[class*="_markdown_"] .md-code-block > [class*="_bannerWrap"]{
|
|
138
|
+
margin:0;padding:0;border:0;background:none;
|
|
139
|
+
}
|
|
140
|
+
[class*="_markdown_"] .md-code-block [data-code-block-banner]{
|
|
141
|
+
display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0;border-radius:0;
|
|
142
|
+
padding:6px 12px;font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
|
|
143
|
+
color:var(--ds-muted);border-bottom:1px solid var(--ds-border);
|
|
144
|
+
background:linear-gradient(90deg,color-mix(in srgb,var(--ds-code-accent,var(--ds-accent)) 14%,transparent),transparent 72%);
|
|
145
|
+
}
|
|
146
|
+
[class*="_markdown_"] .md-code-block [data-code-block-banner] > [class*="_heading"]{
|
|
147
|
+
flex:1;min-width:0;justify-content:flex-start;text-align:left;
|
|
148
|
+
}
|
|
149
|
+
[class*="_markdown_"] .md-code-block [data-code-block-banner] > [class*="_actions"]{
|
|
150
|
+
margin-left:auto;flex:none;
|
|
151
|
+
}
|
|
152
|
+
[class*="_markdown_"] .md-code-block > [data-code-block-content]{padding:0}
|
|
153
|
+
[class*="_markdown_"] .md-code-block [data-code-block-banner]::before{
|
|
154
|
+
content:"";width:8px;height:8px;border-radius:50%;flex:none;
|
|
155
|
+
background:var(--ds-code-accent,var(--ds-accent));
|
|
156
|
+
box-shadow:0 0 0 3px color-mix(in srgb,var(--ds-code-accent,var(--ds-accent)) 18%,transparent);
|
|
157
|
+
}
|
|
158
|
+
[class*="_markdown_"] .md-code-block pre{margin:0;padding:14px 16px;font-size:13px;line-height:1.65;overflow:auto}
|
|
159
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="js"]{--ds-code-accent:#eab308}
|
|
160
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="ts"],
|
|
161
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="tsx"]{--ds-code-accent:#3b82f6}
|
|
162
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="py"],
|
|
163
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="python"]{--ds-code-accent:#22c55e}
|
|
164
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="bash"],
|
|
165
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="sh"],
|
|
166
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="powershell"]{--ds-code-accent:#14b8a6}
|
|
167
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="json"]{--ds-code-accent:#a855f7}
|
|
168
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="html"],
|
|
169
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="css"]{--ds-code-accent:#f97316}
|
|
170
|
+
[class*="_markdown_"] .md-code-block[data-dsh-structured-lang="sql"]{--ds-code-accent:#06b6d4}
|
|
171
|
+
[class*="_markdown_"] blockquote{
|
|
172
|
+
margin:14px 0;padding:10px 14px;border-left:3px solid var(--ds-accent);
|
|
173
|
+
background:color-mix(in srgb,var(--ds-accent) 6%,transparent);border-radius:0 10px 10px 0;
|
|
174
|
+
}
|
|
175
|
+
[class*="_markdown_"] blockquote[${LEAD_ATTR}="lead"]{
|
|
176
|
+
position:relative;border-left:4px solid var(--ds-accent);border-radius:12px;
|
|
177
|
+
padding:14px 16px 14px 38px;
|
|
178
|
+
background:linear-gradient(135deg,color-mix(in srgb,var(--ds-accent) 7%,var(--ds-surface)),var(--ds-surface) 62%);
|
|
179
|
+
box-shadow:0 1px 2px rgba(0,0,0,.04),0 12px 32px -24px color-mix(in srgb,var(--ds-accent) 32%,transparent);
|
|
180
|
+
}
|
|
181
|
+
[class*="_markdown_"] blockquote[${LEAD_ATTR}="lead"]::before{
|
|
182
|
+
content:"◆";position:absolute;left:14px;top:15px;
|
|
183
|
+
color:var(--ds-accent);font-size:13px;line-height:1;
|
|
184
|
+
}
|
|
185
|
+
[class*="_markdown_"] blockquote[${LEAD_ATTR}="lead"]>p:first-child{
|
|
186
|
+
margin-top:0;font-size:16.5px;line-height:1.6;font-weight:600;color:var(--ds-text);
|
|
187
|
+
}
|
|
188
|
+
[class*="_markdown_"] blockquote[${TONE_ATTR}="warn"]{--ds-accent:var(--dsw-alias-state-warn-primary,#d97706)}
|
|
189
|
+
[class*="_markdown_"] blockquote[${TONE_ATTR}="success"]{--ds-accent:var(--dsw-alias-state-success-primary,#16a34a)}
|
|
190
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]{
|
|
191
|
+
border:1px solid color-mix(in srgb,var(--ds-accent) 16%,var(--ds-border));
|
|
192
|
+
border-left:3px solid var(--ds-accent);
|
|
193
|
+
border-radius:12px;padding:14px 16px 15px;
|
|
194
|
+
background:linear-gradient(135deg,color-mix(in srgb,var(--ds-accent) 6%,var(--ds-surface)),var(--ds-surface) 60%);
|
|
195
|
+
box-shadow:0 1px 2px rgba(0,0,0,.04),0 12px 32px -24px color-mix(in srgb,var(--ds-accent) 32%,transparent);
|
|
196
|
+
}
|
|
197
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p:first-child>strong:first-child{
|
|
198
|
+
display:flex;align-items:center;gap:9px;position:relative;
|
|
199
|
+
font-size:13px;line-height:1.1;letter-spacing:.06em;text-transform:uppercase;
|
|
200
|
+
color:var(--ds-accent);margin-bottom:8px;
|
|
201
|
+
}
|
|
202
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p:first-child>strong:first-child::before{
|
|
203
|
+
content:"";display:inline-flex;flex:none;
|
|
204
|
+
width:26px;height:26px;border-radius:7px;
|
|
205
|
+
background:color-mix(in srgb,var(--ds-accent) 12%,transparent);
|
|
206
|
+
}
|
|
207
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p:first-child>strong:first-child::after{
|
|
208
|
+
content:"";position:absolute;left:5.5px;top:5.5px;width:15px;height:15px;
|
|
209
|
+
background-color:var(--ds-accent);
|
|
210
|
+
-webkit-mask:var(--ds-card-icon) center/contain no-repeat;
|
|
211
|
+
mask:var(--ds-card-icon) center/contain no-repeat;
|
|
212
|
+
}
|
|
213
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p:nth-child(2)>strong:first-child{
|
|
214
|
+
display:block;font-size:16px;font-weight:700;line-height:1.45;
|
|
215
|
+
letter-spacing:-.01em;color:var(--ds-text);margin-bottom:4px;
|
|
216
|
+
}
|
|
217
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p{font-size:15.5px;line-height:1.7;font-weight:450;color:var(--ds-text)}
|
|
218
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p strong{font-weight:700;color:var(--ds-text)}
|
|
219
|
+
[class*="_markdown_"] blockquote[${TONE_ATTR}]>p:first-child>strong:first-child{color:var(--ds-accent)}
|
|
220
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="conclusion"]{
|
|
221
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3l9 9-9 9-9-9 9-9z' fill='none' stroke='black' stroke-width='1.8' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
222
|
+
}
|
|
223
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="decision"]{
|
|
224
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4 7h6l4 10h6M4 17h6l4-10h6' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
225
|
+
}
|
|
226
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="metric"]{
|
|
227
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 20V10M12 20V4M19 20v-7' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
|
|
228
|
+
}
|
|
229
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="risk"]{
|
|
230
|
+
--ds-accent:var(--dsw-alias-state-warn-primary,#d97706);
|
|
231
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 4l9 16H3L12 4z' fill='none' stroke='black' stroke-width='1.8' stroke-linejoin='round'/%3E%3Cpath d='M12 10v4M12 17v.5' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
|
|
232
|
+
}
|
|
233
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="next"]{
|
|
234
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
235
|
+
}
|
|
236
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="accept"]{
|
|
237
|
+
--ds-accent:var(--dsw-alias-state-success-primary,#16a34a);
|
|
238
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4 12.5l5 5L20 6.5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
239
|
+
}
|
|
240
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="key"]{
|
|
241
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3l2.6 6.2 6.7.5-5.1 4.4 1.6 6.5L12 17.3 6.2 20.6l1.6-6.5L2.7 9.7l6.7-.5L12 3z' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
242
|
+
}
|
|
243
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="statement"]{
|
|
244
|
+
--ds-card-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3l9 9-9 9-9-9 9-9z' fill='none' stroke='black' stroke-width='1.8' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
245
|
+
}
|
|
246
|
+
[class*="_markdown_"] blockquote[${CARD_TYPE_ATTR}="statement"]>p:first-child>strong:first-child{
|
|
247
|
+
font-size:16px;line-height:1.45;letter-spacing:-.01em;text-transform:none;
|
|
248
|
+
color:var(--ds-text);margin-bottom:4px;
|
|
249
|
+
}
|
|
250
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p:first-child{margin-top:0}
|
|
251
|
+
[class*="_markdown_"] blockquote[${CARD_ATTR}="1"]>p:last-child{margin-bottom:0}
|
|
252
|
+
[class*="_markdown_"] p[${STATUS_ATTR}],[class*="_markdown_"] li[${STATUS_ATTR}]{
|
|
253
|
+
border-left:3px solid var(--ds-accent);background:color-mix(in srgb,var(--ds-accent) 7%,transparent);
|
|
254
|
+
border-radius:0 6px 6px 0;padding:4px 10px;margin:.4em 0;
|
|
255
|
+
}
|
|
256
|
+
[class*="_markdown_"] p[${STATUS_ATTR}="success"],[class*="_markdown_"] li[${STATUS_ATTR}="success"]{--ds-accent:var(--dsw-alias-state-success-primary,#16a34a)}
|
|
257
|
+
[class*="_markdown_"] p[${STATUS_ATTR}="warn"],[class*="_markdown_"] li[${STATUS_ATTR}="warn"]{--ds-accent:var(--dsw-alias-state-warn-primary,#d97706)}
|
|
258
|
+
[class*="_markdown_"] p[${STATUS_ATTR}="error"],[class*="_markdown_"] li[${STATUS_ATTR}="error"]{--ds-accent:var(--dsw-alias-state-error-primary,#dc2626)}
|
|
259
|
+
@media (max-width:720px){
|
|
260
|
+
[class*="_markdown_"]{font-size:14.5px}
|
|
261
|
+
[class*="_markdown_"] h1{font-size:22px}
|
|
262
|
+
[class*="_markdown_"] h2{font-size:18px}
|
|
263
|
+
[class*="_markdown_"] table{font-size:13px}
|
|
264
|
+
[class*="_markdown_"] thead th,[class*="_markdown_"] tbody td{padding:7px 9px}
|
|
265
|
+
}
|
|
266
|
+
@media (prefers-reduced-motion:reduce){
|
|
267
|
+
[class*="_markdown_"] *{transition:none!important;animation:none!important}
|
|
268
|
+
}
|
|
269
|
+
`
|
|
270
|
+
|
|
271
|
+
function iconFor(text) {
|
|
272
|
+
for (const [pattern, icon] of ICONS) if (pattern.test(text)) return icon
|
|
273
|
+
return null
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function cardTypeFor(text) {
|
|
277
|
+
for (const [pattern, type] of CARD_TYPES) if (pattern.test(text)) return type
|
|
278
|
+
return null
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function inGenui(element) {
|
|
282
|
+
try {
|
|
283
|
+
return Boolean(element.closest?.('[data-genui], [class*="genui-root"]'))
|
|
284
|
+
} catch {
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function toneFor(text) {
|
|
290
|
+
for (const [pattern, tone] of TONES) if (pattern.test(text)) return tone
|
|
291
|
+
return null
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function statusFor(text) {
|
|
295
|
+
if (/^✅/.test(text)) return 'success'
|
|
296
|
+
if (/^⚠/.test(text)) return 'warn'
|
|
297
|
+
if (/^❌/.test(text)) return 'error'
|
|
298
|
+
return null
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function isFirstBlockquote(block) {
|
|
302
|
+
const markdown = block.closest?.('[class*="_markdown_"]')
|
|
303
|
+
return Boolean(markdown && markdown.querySelector('blockquote') === block)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function isStatementCard(block) {
|
|
307
|
+
const markdown = block.closest?.('[class*="_markdown_"]')
|
|
308
|
+
if (!markdown || markdown.querySelector('blockquote') !== block) return false
|
|
309
|
+
const paragraphs = [...block.children].filter((element) => element.tagName === 'P')
|
|
310
|
+
if (paragraphs.length < 2) {
|
|
311
|
+
const first = paragraphs[0]
|
|
312
|
+
const strong = first?.firstElementChild
|
|
313
|
+
if (!strong || strong.tagName !== 'STRONG') return false
|
|
314
|
+
const rest = first.textContent.slice(strong.textContent.length).trim()
|
|
315
|
+
return rest.length > 0 || Boolean(first.querySelector('br'))
|
|
316
|
+
}
|
|
317
|
+
const first = paragraphs[0]
|
|
318
|
+
return Boolean(first?.firstElementChild && first.firstElementChild.tagName === 'STRONG')
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function decorate(root) {
|
|
322
|
+
for (const heading of root.querySelectorAll('[class*="_markdown_"] h2, [class*="_markdown_"] h3')) {
|
|
323
|
+
if (inGenui(heading)) continue
|
|
324
|
+
const icon = iconFor((heading.textContent ?? '').trim())
|
|
325
|
+
if (icon === null) heading.removeAttribute(ICON_ATTR)
|
|
326
|
+
else heading.setAttribute(ICON_ATTR, icon)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
for (const block of root.querySelectorAll('blockquote')) {
|
|
330
|
+
if (inGenui(block)) continue
|
|
331
|
+
const text = (block.textContent ?? '').trimStart()
|
|
332
|
+
let type = cardTypeFor(text)
|
|
333
|
+
if (type === null && isStatementCard(block)) type = 'statement'
|
|
334
|
+
if (type === null) {
|
|
335
|
+
block.removeAttribute(CARD_ATTR)
|
|
336
|
+
block.removeAttribute(CARD_TYPE_ATTR)
|
|
337
|
+
if (isFirstBlockquote(block)) block.setAttribute(LEAD_ATTR, 'lead')
|
|
338
|
+
else block.removeAttribute(LEAD_ATTR)
|
|
339
|
+
const tone = toneFor(text)
|
|
340
|
+
if (tone === null) block.removeAttribute(TONE_ATTR)
|
|
341
|
+
else block.setAttribute(TONE_ATTR, tone)
|
|
342
|
+
continue
|
|
343
|
+
}
|
|
344
|
+
block.setAttribute(CARD_ATTR, '1')
|
|
345
|
+
block.setAttribute(CARD_TYPE_ATTR, type)
|
|
346
|
+
block.removeAttribute(LEAD_ATTR)
|
|
347
|
+
block.setAttribute(TONE_ATTR, type === 'risk' ? 'warn' : type === 'accept' ? 'success' : 'info')
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
for (const element of root.querySelectorAll('[class*="_markdown_"] p, [class*="_markdown_"] li')) {
|
|
351
|
+
if (inGenui(element) || element.closest('blockquote, table')) {
|
|
352
|
+
element.removeAttribute(STATUS_ATTR)
|
|
353
|
+
continue
|
|
354
|
+
}
|
|
355
|
+
const status = statusFor((element.textContent ?? '').trimStart())
|
|
356
|
+
if (status === null) element.removeAttribute(STATUS_ATTR)
|
|
357
|
+
else element.setAttribute(STATUS_ATTR, status)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
for (const row of root.querySelectorAll('[class*="_markdown_"] table tr')) {
|
|
361
|
+
if (inGenui(row) || row.closest('thead')) {
|
|
362
|
+
row.removeAttribute(ROW_ATTR)
|
|
363
|
+
continue
|
|
364
|
+
}
|
|
365
|
+
const status = statusFor((row.textContent ?? '').trimStart())
|
|
366
|
+
if (status === null) row.removeAttribute(ROW_ATTR)
|
|
367
|
+
else row.setAttribute(ROW_ATTR, status)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
for (const table of root.querySelectorAll('[class*="_markdown_"] table')) {
|
|
371
|
+
if (inGenui(table)) continue
|
|
372
|
+
const rows = [...table.querySelectorAll('tbody tr')]
|
|
373
|
+
const statusTable =
|
|
374
|
+
rows.length > 0 &&
|
|
375
|
+
rows.every((row) => statusFor((row.cells?.[0]?.textContent ?? '').trimStart()) !== null)
|
|
376
|
+
if (statusTable) table.setAttribute(STATUS_TABLE_ATTR, '1')
|
|
377
|
+
else table.removeAttribute(STATUS_TABLE_ATTR)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
for (const paragraph of root.querySelectorAll('[class*="_markdown_"] p')) {
|
|
381
|
+
if (inGenui(paragraph) || paragraph.closest('blockquote, table')) {
|
|
382
|
+
paragraph.removeAttribute(LEAD_ATTR)
|
|
383
|
+
continue
|
|
384
|
+
}
|
|
385
|
+
if (/^表格结论/.test((paragraph.textContent ?? '').trimStart())) paragraph.setAttribute(LEAD_ATTR, 'table')
|
|
386
|
+
else paragraph.removeAttribute(LEAD_ATTR)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
for (const code of root.querySelectorAll('[class*="_markdown_"] .md-code-block')) {
|
|
390
|
+
if (inGenui(code)) continue
|
|
391
|
+
const banner = code.querySelector('[data-code-block-banner]')
|
|
392
|
+
const raw = (banner?.textContent ?? '').trim().split(/\s+/)[0] ?? ''
|
|
393
|
+
const lang = raw.toLowerCase().replace(/[^a-z0-9+#]/g, '')
|
|
394
|
+
if (lang.length === 0) code.removeAttribute(LANG_ATTR)
|
|
395
|
+
else code.setAttribute(LANG_ATTR, lang)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
inject: [],
|
|
401
|
+
apply(ctx) {
|
|
402
|
+
if (typeof document === 'undefined' || typeof MutationObserver === 'undefined') return
|
|
403
|
+
if (document.body === null) return
|
|
404
|
+
|
|
405
|
+
document.getElementById(LEGACY_STYLE_ID)?.remove()
|
|
406
|
+
document.getElementById(STYLE_ID)?.remove()
|
|
407
|
+
const style = document.createElement('style')
|
|
408
|
+
style.id = STYLE_ID
|
|
409
|
+
style.textContent = CSS
|
|
410
|
+
document.head.appendChild(style)
|
|
411
|
+
document.documentElement.setAttribute(ROOT_ATTR, 'active')
|
|
412
|
+
try {
|
|
413
|
+
console.info('[dsh-structured] visual layer active')
|
|
414
|
+
} catch {
|
|
415
|
+
// console 不可用时忽略。
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let disposed = false
|
|
419
|
+
let scheduled = false
|
|
420
|
+
let pending = null
|
|
421
|
+
const run = () => {
|
|
422
|
+
if (disposed) return
|
|
423
|
+
scheduled = false
|
|
424
|
+
pending = null
|
|
425
|
+
decorate(document)
|
|
426
|
+
}
|
|
427
|
+
const schedule = () => {
|
|
428
|
+
if (disposed || scheduled) return
|
|
429
|
+
scheduled = true
|
|
430
|
+
if (typeof requestAnimationFrame === 'function') pending = requestAnimationFrame(run)
|
|
431
|
+
else pending = setTimeout(run, 16)
|
|
432
|
+
}
|
|
433
|
+
const observer = new MutationObserver(schedule)
|
|
434
|
+
observer.observe(document.body, { childList: true, subtree: true, characterData: true })
|
|
435
|
+
run()
|
|
436
|
+
|
|
437
|
+
const dispose = () => {
|
|
438
|
+
if (disposed) return
|
|
439
|
+
disposed = true
|
|
440
|
+
observer.disconnect()
|
|
441
|
+
if (pending !== null) {
|
|
442
|
+
if (typeof cancelAnimationFrame === 'function') cancelAnimationFrame(pending)
|
|
443
|
+
else clearTimeout(pending)
|
|
444
|
+
}
|
|
445
|
+
style.remove()
|
|
446
|
+
document.documentElement.removeAttribute(ROOT_ATTR)
|
|
447
|
+
for (const attr of [CARD_ATTR, CARD_TYPE_ATTR, ICON_ATTR, TONE_ATTR, LANG_ATTR, STATUS_ATTR, STATUS_TABLE_ATTR, LEAD_ATTR, ROW_ATTR]) {
|
|
448
|
+
for (const element of document.querySelectorAll('[' + attr + ']')) element.removeAttribute(attr)
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
console.info('[dsh-structured] visual layer disposed')
|
|
452
|
+
} catch {
|
|
453
|
+
// console 不可用时忽略。
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
ctx?.effect?.(() => dispose, 'dsh-structured: visual layer')
|
|
458
|
+
} catch {
|
|
459
|
+
dispose()
|
|
460
|
+
}
|
|
461
|
+
return dispose
|
|
462
|
+
},
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
})
|