dsh-structured 0.9.4 → 0.9.9
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 +130 -108
- package/README.md +274 -273
- 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/assets/screenshots/09-theme-light.png +0 -0
- package/assets/screenshots/10-theme-dark.png +0 -0
- package/docs/0.9-FINAL-RELEASE.md +22 -0
- package/docs/1.0-BASELINE.md +38 -0
- package/docs/1.0-GATE.md +39 -0
- package/docs/1.0-PERSONA-SPEC.md +59 -0
- package/lib/client.js +95 -68
- package/lib/index.js +518 -517
- package/package.json +102 -99
- package/scripts/theme-contrast.mjs +2 -2
- package/scripts/theme-hue-distance.mjs +55 -0
- package/src/client.js +93 -66
- package/src/index.js +516 -515
- package/test/client-cards.test.mjs +145 -140
- package/test/client-incremental.test.mjs +105 -0
- package/test/client-visual.test.mjs +299 -290
- package/test/fixtures/first-glance.html +80 -0
- package/test/fixtures/persona-gate/no-persona.md +8 -0
- package/test/fixtures/persona-gate/persona-append.md +9 -0
- package/test/fixtures/persona-gate/style-override.md +8 -0
- package/test/fixtures/style-probe.mjs +74 -0
- package/test/structured.test.mjs +590 -582
- package/test/style-browser.test.mjs +71 -0
- package/test/theme.test.mjs +8 -9
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { execSync } from 'node:child_process'
|
|
3
|
+
import { readFileSync, rmSync } from 'node:fs'
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
5
|
+
import test from 'node:test'
|
|
6
|
+
|
|
7
|
+
// CI runner 通常没有 ego-browser;缺失时跳过,不让环境差异把测试判成失败。
|
|
8
|
+
function egoAvailable() {
|
|
9
|
+
try {
|
|
10
|
+
execSync(process.platform === 'win32' ? 'where ego-browser' : 'command -v ego-browser', { stdio: 'ignore' })
|
|
11
|
+
return true
|
|
12
|
+
} catch {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('0.9.6 style:真实浏览器 computedStyle 与 800px 布局', { timeout: 120_000 }, (t) => {
|
|
18
|
+
if (!egoAvailable()) {
|
|
19
|
+
t.skip('ego-browser 不可用,跳过真实浏览器样式检查')
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
const source = readFileSync(new URL('../src/client.js', import.meta.url), 'utf8')
|
|
23
|
+
assert.doesNotMatch(source, /--ds-card-icon/)
|
|
24
|
+
const probe = fileURLToPath(new URL('./fixtures/style-probe.mjs', import.meta.url))
|
|
25
|
+
const resultPath = fileURLToPath(new URL('../artifacts/audit-0.9.6/style-result.json', import.meta.url))
|
|
26
|
+
const url = pathToFileURL(probe).href
|
|
27
|
+
rmSync(resultPath, { force: true })
|
|
28
|
+
execSync(`ego-browser nodejs -e "await import('${url}')"`, {
|
|
29
|
+
encoding: 'utf8',
|
|
30
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
|
+
timeout: 90_000,
|
|
32
|
+
})
|
|
33
|
+
const { style, narrow } = JSON.parse(readFileSync(resultPath, 'utf8'))
|
|
34
|
+
rmSync(resultPath, { force: true })
|
|
35
|
+
|
|
36
|
+
assert.equal(style.titleSize, '24px')
|
|
37
|
+
assert.equal(style.titleWeight, '700')
|
|
38
|
+
assert.equal(style.bodySize, '13.5px')
|
|
39
|
+
assert.equal(style.bodyLine, '21.6px')
|
|
40
|
+
assert.equal(style.dividerHeight, '1px')
|
|
41
|
+
assert.equal(style.dividerBackground, 'rgb(226, 232, 240)')
|
|
42
|
+
assert.equal(style.headerSize, '13px')
|
|
43
|
+
assert.equal(style.headerWeight, '700')
|
|
44
|
+
assert.equal(style.headerHeight, '38px')
|
|
45
|
+
assert.equal(style.bodyCellSize, '12.5px')
|
|
46
|
+
assert.equal(style.statusPillSize, '10px')
|
|
47
|
+
assert.equal(style.leadSize, '13px')
|
|
48
|
+
assert.equal(style.leadStrongSize, '14px')
|
|
49
|
+
assert.equal(style.leadStrongWeight, '700')
|
|
50
|
+
assert.equal(style.leadBorderTop, '0px')
|
|
51
|
+
assert.equal(style.leadBorderRight, '0px')
|
|
52
|
+
assert.equal(style.leadBorderBottom, '0px')
|
|
53
|
+
assert.equal(style.leadBorderLeft, '0px')
|
|
54
|
+
assert.equal(style.nextTitleSize, '16px')
|
|
55
|
+
assert.equal(style.nextTitleWeight, '700')
|
|
56
|
+
assert.equal(style.nextStepSize, '13px')
|
|
57
|
+
assert.equal(style.nextBackground, 'rgba(0, 0, 0, 0)')
|
|
58
|
+
assert.equal(style.nextBorderTop, '1px')
|
|
59
|
+
assert.equal(style.nextBorderRight, '0px')
|
|
60
|
+
assert.equal(style.nextBorderBottom, '0px')
|
|
61
|
+
assert.equal(style.nextBorderLeft, '0px')
|
|
62
|
+
assert.equal(style.triangleColor, 'rgb(194, 65, 12)')
|
|
63
|
+
assert.equal(style.triangleLeftWidth, '9px')
|
|
64
|
+
assert.equal(style.triangleTopWidth, '5px')
|
|
65
|
+
assert.equal(style.triangleBottomWidth, '5px')
|
|
66
|
+
assert.equal(style.darkTriangleColor, 'rgb(251, 146, 60)')
|
|
67
|
+
assert.ok(Math.abs(style.liLeft - style.labelLeft) <= 1, `next label/li delta ${style.liLeft - style.labelLeft}`)
|
|
68
|
+
assert.equal(style.cardIconBefore, 'none')
|
|
69
|
+
assert.equal(narrow.overflow, false)
|
|
70
|
+
assert.ok(narrow.docScrollWidth <= narrow.innerWidth)
|
|
71
|
+
})
|
package/test/theme.test.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs'
|
|
|
3
3
|
import test from 'node:test'
|
|
4
4
|
import { Config, statusText, THEMES } from '../src/index.js'
|
|
5
5
|
import { checkThemeContrast, THEME_TOKENS } from '../scripts/theme-contrast.mjs'
|
|
6
|
+
import { checkHueSeparation } from '../scripts/theme-hue-distance.mjs'
|
|
6
7
|
|
|
7
8
|
const value = (field) => (field && typeof field.get === 'function' ? field.get() : field)
|
|
8
9
|
|
|
@@ -14,7 +15,13 @@ test('0.9.0 Config:theme 默认 deepseek,三值可选', () => {
|
|
|
14
15
|
assert.equal(value(Config({ defaultEnabled: false }).defaultEnabled), false)
|
|
15
16
|
})
|
|
16
17
|
|
|
17
|
-
test('0.9.
|
|
18
|
+
test('0.9.8 accent/status hue 分离:12 项通过', () => {
|
|
19
|
+
const { results, failures } = checkHueSeparation()
|
|
20
|
+
assert.equal(failures.length, 0, JSON.stringify(failures, null, 2))
|
|
21
|
+
assert.equal(results.length, 12)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('0.9.8 Config:六个字段的默认值与显式值', () => {
|
|
18
25
|
const defaults = Config({})
|
|
19
26
|
assert.equal(value(defaults.theme), 'deepseek')
|
|
20
27
|
assert.equal(value(defaults.visual), true)
|
|
@@ -82,14 +89,6 @@ test('0.9.0 client:主题 token/暗色 selector/code token 存在,旧 select
|
|
|
82
89
|
assert.doesNotMatch(client, /--dsw-alias-state-/)
|
|
83
90
|
assert.match(client, /--ds-accent:#2563EB/)
|
|
84
91
|
for (const theme of ['codex', 'anthropic']) assert.match(client, new RegExp(`data-dsh-structured-theme="${theme}"`))
|
|
85
|
-
assert.match(client, /a\{color:var\(--ds-accent-text\)/)
|
|
86
|
-
assert.match(client, /li::marker\{color:var\(--ds-accent-text\)/)
|
|
87
|
-
assert.match(client, /p\[\$\{LEAD_ATTR\}="table"\] strong\{color:var\(--ds-accent-text\)/)
|
|
88
|
-
assert.match(client, /color:var\(--ds-accent-text\);overflow-wrap:anywhere/)
|
|
89
|
-
assert.match(client, /blockquote\[\$\{TONE_ATTR\}\]>p:first-child>strong:first-child\{color:var\(--ds-accent-text\)/)
|
|
90
|
-
const narrow = client.slice(client.indexOf('@media (max-width:720px)'), client.indexOf('@media (prefers-reduced-motion'))
|
|
91
|
-
assert.match(narrow, /table:not\(\[\$\{SKIP_ATTR\}\]\)/)
|
|
92
|
-
assert.doesNotMatch(narrow, /\[class\*="_markdown_"\] table\{/)
|
|
93
92
|
})
|
|
94
93
|
|
|
95
94
|
test('0.9.1 主题 v2:三套 token 精确值与 accentText', () => {
|