🐛 fix(vscode): 代码阅读的 Markdown 预览固定同一份 HTML,悬停链接不再闪
This commit is contained in:
1 parent
079657da37
commit
3abc87dbef
5 files changed
+256
-10
No files matched your search
@@ -539,6 +539,7 @@
|
|||||||
"concurrently": "^9.1.0",
|
"concurrently": "^9.1.0",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
|
"happy-dom": "^20.14.5",
|
||||||
"ovsx": "^0.10.7",
|
"ovsx": "^0.10.7",
|
||||||
"tree-sitter-wasm": "^2.0.2",
|
"tree-sitter-wasm": "^2.0.2",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
|
|||||||
Generated
+74
-7
@@ -37,7 +37,7 @@ importers:
|
|||||||
devDependencies:
|
devDependencies:
|
||||||
'@antfu/eslint-config':
|
'@antfu/eslint-config':
|
||||||
specifier: ^6.7.3
|
specifier: ^6.7.3
|
||||||
version: 6.7.3(@vue/compiler-sfc@3.4.27)(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))
|
version: 6.7.3(@vue/compiler-sfc@3.4.27)(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(happy-dom@20.14.5)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))
|
||||||
'@types/ali-oss':
|
'@types/ali-oss':
|
||||||
specifier: ^6.23.3
|
specifier: ^6.23.3
|
||||||
version: 6.23.3
|
version: 6.23.3
|
||||||
@@ -65,6 +65,9 @@ importers:
|
|||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.39.2
|
specifier: ^9.39.2
|
||||||
version: 9.39.2(jiti@2.6.1)
|
version: 9.39.2(jiti@2.6.1)
|
||||||
|
happy-dom:
|
||||||
|
specifier: ^20.14.5
|
||||||
|
version: 20.14.5
|
||||||
ovsx:
|
ovsx:
|
||||||
specifier: ^0.10.7
|
specifier: ^0.10.7
|
||||||
version: 0.10.7
|
version: 0.10.7
|
||||||
@@ -76,7 +79,7 @@ importers:
|
|||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.0.16
|
specifier: ^4.0.16
|
||||||
version: 4.0.16(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2)
|
version: 4.0.16(@types/node@25.0.3)(happy-dom@20.14.5)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2)
|
||||||
|
|
||||||
webview-ui:
|
webview-ui:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1301,6 +1304,12 @@ packages:
|
|||||||
'@types/vscode@1.97.0':
|
'@types/vscode@1.97.0':
|
||||||
resolution: {integrity: sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==}
|
resolution: {integrity: sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==}
|
||||||
|
|
||||||
|
'@types/whatwg-mimetype@3.0.2':
|
||||||
|
resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
|
||||||
|
|
||||||
|
'@types/ws@8.18.2':
|
||||||
|
resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==}
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.52.0':
|
'@typescript-eslint/eslint-plugin@8.52.0':
|
||||||
resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==}
|
resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
@@ -1626,6 +1635,10 @@ packages:
|
|||||||
buffer-equal-constant-time@1.0.1:
|
buffer-equal-constant-time@1.0.1:
|
||||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||||
|
|
||||||
|
buffer-image-size@0.6.4:
|
||||||
|
resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
buffer@5.7.1:
|
buffer@5.7.1:
|
||||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||||
|
|
||||||
@@ -1948,6 +1961,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||||
engines: {node: '>=0.12'}
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
|
entities@7.0.1:
|
||||||
|
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
|
||||||
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
entities@8.1.0:
|
entities@8.1.0:
|
||||||
resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==}
|
resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==}
|
||||||
engines: {node: '>=20.19.0'}
|
engines: {node: '>=20.19.0'}
|
||||||
@@ -2382,6 +2399,10 @@ packages:
|
|||||||
engines: {node: '>=0.4.7'}
|
engines: {node: '>=0.4.7'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
happy-dom@20.14.5:
|
||||||
|
resolution: {integrity: sha512-x/RzkpWO40bTjIoT30iQtt64FLLmH/iRcUCN2X//bLx7H3ifkdfPXyqsro/OYtqzIAhiLMMA7mmiOR9C3NOKjQ==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
|
||||||
has-flag@4.0.0:
|
has-flag@4.0.0:
|
||||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3807,6 +3828,10 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
|
|
||||||
|
whatwg-mimetype@3.0.0:
|
||||||
|
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -3835,6 +3860,18 @@ packages:
|
|||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
|
ws@8.22.0:
|
||||||
|
resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
bufferutil: ^4.0.1
|
||||||
|
utf-8-validate: '>=5.0.2'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bufferutil:
|
||||||
|
optional: true
|
||||||
|
utf-8-validate:
|
||||||
|
optional: true
|
||||||
|
|
||||||
xml-name-validator@4.0.0:
|
xml-name-validator@4.0.0:
|
||||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -3904,7 +3941,7 @@ snapshots:
|
|||||||
|
|
||||||
'@aashutoshrathi/word-wrap@1.2.6': {}
|
'@aashutoshrathi/word-wrap@1.2.6': {}
|
||||||
|
|
||||||
'@antfu/eslint-config@6.7.3(@vue/compiler-sfc@3.4.27)(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))':
|
'@antfu/eslint-config@6.7.3(@vue/compiler-sfc@3.4.27)(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(happy-dom@20.14.5)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@antfu/install-pkg': 1.1.0
|
'@antfu/install-pkg': 1.1.0
|
||||||
'@clack/prompts': 0.11.0
|
'@clack/prompts': 0.11.0
|
||||||
@@ -3913,7 +3950,7 @@ snapshots:
|
|||||||
'@stylistic/eslint-plugin': 5.6.1(eslint@9.39.2(jiti@2.6.1))
|
'@stylistic/eslint-plugin': 5.6.1(eslint@9.39.2(jiti@2.6.1))
|
||||||
'@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@vitest/eslint-plugin': 1.6.5(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))
|
'@vitest/eslint-plugin': 1.6.5(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(happy-dom@20.14.5)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))
|
||||||
ansis: 4.2.0
|
ansis: 4.2.0
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
eslint: 9.39.2(jiti@2.6.1)
|
eslint: 9.39.2(jiti@2.6.1)
|
||||||
@@ -4953,6 +4990,12 @@ snapshots:
|
|||||||
|
|
||||||
'@types/vscode@1.97.0': {}
|
'@types/vscode@1.97.0': {}
|
||||||
|
|
||||||
|
'@types/whatwg-mimetype@3.0.2': {}
|
||||||
|
|
||||||
|
'@types/ws@8.18.2':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 25.0.3
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
'@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.12.2
|
'@eslint-community/regexpp': 4.12.2
|
||||||
@@ -5056,14 +5099,14 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@vitest/eslint-plugin@1.6.5(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))':
|
'@vitest/eslint-plugin@1.6.5(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.16(@types/node@25.0.3)(happy-dom@20.14.5)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 8.52.0
|
'@typescript-eslint/scope-manager': 8.52.0
|
||||||
'@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||||
eslint: 9.39.2(jiti@2.6.1)
|
eslint: 9.39.2(jiti@2.6.1)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
vitest: 4.0.16(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2)
|
vitest: 4.0.16(@types/node@25.0.3)(happy-dom@20.14.5)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -5376,6 +5419,10 @@ snapshots:
|
|||||||
|
|
||||||
buffer-equal-constant-time@1.0.1(patch_hash=beceab8aa83d254a64c495a1832ac06b1829a43e89426e612563a08fed744b51): {}
|
buffer-equal-constant-time@1.0.1(patch_hash=beceab8aa83d254a64c495a1832ac06b1829a43e89426e612563a08fed744b51): {}
|
||||||
|
|
||||||
|
buffer-image-size@0.6.4:
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 25.0.3
|
||||||
|
|
||||||
buffer@5.7.1:
|
buffer@5.7.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js: 1.5.1
|
base64-js: 1.5.1
|
||||||
@@ -5681,6 +5728,8 @@ snapshots:
|
|||||||
|
|
||||||
entities@4.5.0: {}
|
entities@4.5.0: {}
|
||||||
|
|
||||||
|
entities@7.0.1: {}
|
||||||
|
|
||||||
entities@8.1.0: {}
|
entities@8.1.0: {}
|
||||||
|
|
||||||
environment@1.1.0: {}
|
environment@1.1.0: {}
|
||||||
@@ -6226,6 +6275,19 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
uglify-js: 3.19.3
|
uglify-js: 3.19.3
|
||||||
|
|
||||||
|
happy-dom@20.14.5:
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 25.0.3
|
||||||
|
'@types/whatwg-mimetype': 3.0.2
|
||||||
|
'@types/ws': 8.18.2
|
||||||
|
buffer-image-size: 0.6.4
|
||||||
|
entities: 7.0.1
|
||||||
|
whatwg-mimetype: 3.0.0
|
||||||
|
ws: 8.22.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- utf-8-validate
|
||||||
|
|
||||||
has-flag@4.0.0: {}
|
has-flag@4.0.0: {}
|
||||||
|
|
||||||
has-property-descriptors@1.0.1:
|
has-property-descriptors@1.0.1:
|
||||||
@@ -7767,7 +7829,7 @@ snapshots:
|
|||||||
tsx: 4.19.2
|
tsx: 4.19.2
|
||||||
yaml: 2.8.2
|
yaml: 2.8.2
|
||||||
|
|
||||||
vitest@4.0.16(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2):
|
vitest@4.0.16(@types/node@25.0.3)(happy-dom@20.14.5)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.0.16
|
'@vitest/expect': 4.0.16
|
||||||
'@vitest/mocker': 4.0.16(vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))
|
'@vitest/mocker': 4.0.16(vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.19.2)(yaml@2.8.2))
|
||||||
@@ -7791,6 +7853,7 @@ snapshots:
|
|||||||
why-is-node-running: 2.3.0
|
why-is-node-running: 2.3.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 25.0.3
|
'@types/node': 25.0.3
|
||||||
|
happy-dom: 20.14.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- jiti
|
- jiti
|
||||||
- less
|
- less
|
||||||
@@ -7816,6 +7879,8 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
whatwg-mimetype@3.0.0: {}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
@@ -7845,6 +7910,8 @@ snapshots:
|
|||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
|
ws@8.22.0: {}
|
||||||
|
|
||||||
xml-name-validator@4.0.0: {}
|
xml-name-validator@4.0.0: {}
|
||||||
|
|
||||||
xml2js@0.5.0:
|
xml2js@0.5.0:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { InlinePart, TextDiff } from './types'
|
|||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { renderMarkdownDiff } from './markdown'
|
import { renderMarkdownDiff } from './markdown'
|
||||||
import { SegmentText } from './SegmentText'
|
import { SegmentText } from './SegmentText'
|
||||||
|
import { MarkdownPreview } from './TextPreview'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 着色的词里的增删(同一行的值改了):没变的照常,删掉的一截红底划掉(旧值,不能跳定义),新加的一截绿底,
|
* 着色的词里的增删(同一行的值改了):没变的照常,删掉的一截红底划掉(旧值,不能跳定义),新加的一截绿底,
|
||||||
@@ -25,7 +26,7 @@ export function DiffInline({ parts }: { parts: InlinePart[] }): ReactElement {
|
|||||||
/** 预览里一段带增删的文字:着色的词逐截标(DiffInline),Markdown 说明照常排版、增删的字按词 / 字标(同 TextPreview 的排版)。 */
|
/** 预览里一段带增删的文字:着色的词逐截标(DiffInline),Markdown 说明照常排版、增删的字按词 / 字标(同 TextPreview 的排版)。 */
|
||||||
export function DiffText({ diff, className }: { diff: TextDiff, className?: string }): ReactElement {
|
export function DiffText({ diff, className }: { diff: TextDiff, className?: string }): ReactElement {
|
||||||
if ('markdown' in diff)
|
if ('markdown' in diff)
|
||||||
return <div className={cn('cr-markdown', className)} dangerouslySetInnerHTML={{ __html: renderMarkdownDiff(diff.markdown) }} />
|
return <MarkdownPreview html={renderMarkdownDiff(diff.markdown)} className={className} />
|
||||||
return (
|
return (
|
||||||
<p className={cn('cr-code', className)}>
|
<p className={cn('cr-code', className)}>
|
||||||
<DiffInline parts={diff.inline} />
|
<DiffInline parts={diff.inline} />
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
// @vitest-environment happy-dom
|
||||||
|
|
||||||
|
import type { ReactElement } from 'react'
|
||||||
|
import type { Root } from 'react-dom/client'
|
||||||
|
import type { ShownPeek } from './definitionPeek'
|
||||||
|
import type { DefinitionPeek } from './types'
|
||||||
|
import { act, useState } from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
import { DefinitionPeeks, linkPeekSource } from './definitionPeek'
|
||||||
|
import { DiffText } from './DiffText'
|
||||||
|
import { MarkdownKindContext, TextPreview } from './TextPreview'
|
||||||
|
|
||||||
|
let container: HTMLDivElement
|
||||||
|
let root: Root
|
||||||
|
let peeks: DefinitionPeeks<HTMLAnchorElement> | undefined
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
|
||||||
|
container = document.createElement('div')
|
||||||
|
document.body.append(container)
|
||||||
|
root = createRoot(container)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
peeks?.dispose()
|
||||||
|
peeks = undefined
|
||||||
|
await act(async () => root.unmount())
|
||||||
|
container.remove()
|
||||||
|
vi.useRealTimers()
|
||||||
|
vi.unstubAllGlobals()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function render(element: ReactElement): Promise<void> {
|
||||||
|
await act(async () => root.render(element))
|
||||||
|
}
|
||||||
|
|
||||||
|
function anchor(): HTMLAnchorElement {
|
||||||
|
const link = container.querySelector('a')
|
||||||
|
if (!link)
|
||||||
|
throw new Error('没有渲染符号链接')
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
describe.each(['TextPreview', 'DiffText'] as const)('%s 的 Markdown 页面元素', (component) => {
|
||||||
|
function preview(markdown: string): ReactElement {
|
||||||
|
return component === 'TextPreview'
|
||||||
|
? <TextPreview text={{ markdown }} className="custom-preview" />
|
||||||
|
: <DiffText diff={{ markdown: [{ text: markdown }] }} className="custom-preview" />
|
||||||
|
}
|
||||||
|
|
||||||
|
it('父级无关状态更新、传入等值新对象时,保留原链接且仍在页面中', async () => {
|
||||||
|
function Parent(): ReactElement {
|
||||||
|
const [count, setCount] = useState(0)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={() => setCount(value => value + 1)}>{count}</button>
|
||||||
|
{preview('[AsyncOpenAI]')}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
await render(<Parent />)
|
||||||
|
const original = anchor()
|
||||||
|
for (let count = 1; count <= 3; count++) {
|
||||||
|
await act(async () => container.querySelector('button')!.click())
|
||||||
|
expect(container.querySelector('button')!.textContent).toBe(String(count))
|
||||||
|
expect(anchor() === original).toBe(true)
|
||||||
|
expect(original.isConnected).toBe(true)
|
||||||
|
}
|
||||||
|
expect(container.querySelector('.cr-markdown.custom-preview')).not.toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('正文变化时更新链接和内容', async () => {
|
||||||
|
await render(preview('[AsyncOpenAI]'))
|
||||||
|
const original = anchor()
|
||||||
|
await render(preview('[Client] 新说明'))
|
||||||
|
expect(anchor().textContent).toBe('Client')
|
||||||
|
expect(anchor().getAttribute('href')).toBe('symbol:Client')
|
||||||
|
expect(container.textContent).toContain('新说明')
|
||||||
|
expect(original.isConnected).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('空 Markdown 与着色代码片段可以来回切换', async () => {
|
||||||
|
const empty = (): ReactElement => component === 'TextPreview'
|
||||||
|
? <TextPreview text={{ markdown: '' }} />
|
||||||
|
: <DiffText diff={{ markdown: [] }} />
|
||||||
|
await render(preview('[Client]'))
|
||||||
|
await render(empty())
|
||||||
|
expect(container.querySelector('.cr-markdown')?.innerHTML).toBe('')
|
||||||
|
expect(container.querySelector('a')).toBeNull()
|
||||||
|
await render(component === 'TextPreview'
|
||||||
|
? <TextPreview text={[{ text: 'client', role: 'variable' }]} />
|
||||||
|
: <DiffText diff={{ inline: [{ segments: [{ text: 'client', role: 'variable' }], change: 'add' }] }} />)
|
||||||
|
expect(container.querySelector('.cr-code .cr-role-variable')?.textContent).toBe('client')
|
||||||
|
expect(container.querySelector('.cr-markdown')).toBeNull()
|
||||||
|
if (component === 'DiffText')
|
||||||
|
expect(container.querySelector('[data-change="add"]')?.textContent).toBe('client')
|
||||||
|
await render(empty())
|
||||||
|
expect(container.querySelector('.cr-markdown')?.innerHTML).toBe('')
|
||||||
|
expect(container.querySelector('.cr-code')).toBeNull()
|
||||||
|
await render(preview('[Client]'))
|
||||||
|
expect(anchor().textContent).toBe('Client')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('显示定义预览引起父级更新后,继续指向同一链接不重复请求也不关闭', async () => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
const post = vi.fn()
|
||||||
|
function Parent(): ReactElement {
|
||||||
|
const [shown, setShown] = useState<ShownPeek<HTMLAnchorElement>>()
|
||||||
|
const [controller] = useState(() => new DefinitionPeeks<HTMLAnchorElement>(post, setShown))
|
||||||
|
peeks = controller
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{preview('[AsyncOpenAI]')}
|
||||||
|
<output>{shown ? '预览已显示' : '预览未显示'}</output>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
await render(<Parent />)
|
||||||
|
const controller = peeks!
|
||||||
|
controller.setDelays(300, 500)
|
||||||
|
const point = (): void => {
|
||||||
|
const link = anchor()
|
||||||
|
const source = linkPeekSource(link.getAttribute('href')!)
|
||||||
|
if (!source)
|
||||||
|
throw new Error('符号链接不能预览')
|
||||||
|
controller.point({ anchor: link, source })
|
||||||
|
}
|
||||||
|
point()
|
||||||
|
await act(async () => vi.advanceTimersByTime(300))
|
||||||
|
expect(post).toHaveBeenCalledTimes(1)
|
||||||
|
expect(post).toHaveBeenLastCalledWith({ type: 'peekLink', id: 1, symbol: 'AsyncOpenAI' })
|
||||||
|
const peek: DefinitionPeek = {
|
||||||
|
path: 'client.py',
|
||||||
|
line: 1,
|
||||||
|
block: { id: '1', anchor: '1', kind: 'class', startLine: 1, endLine: 2, code: '', codeTokens: [], attributes: { kind: 'class' }, concepts: [], children: [], translation: [] },
|
||||||
|
names: [],
|
||||||
|
}
|
||||||
|
await act(async () => controller.answer(1, peek))
|
||||||
|
expect(container.querySelector('output')?.textContent).toBe('预览已显示')
|
||||||
|
point()
|
||||||
|
await act(async () => vi.advanceTimersByTime(1000))
|
||||||
|
expect(post).toHaveBeenCalledTimes(1)
|
||||||
|
expect(container.querySelector('output')?.textContent).toBe('预览已显示')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('组件 TextPreview 随注释与定义预览模式切换更新相同正文的链接', async () => {
|
||||||
|
const preview = (kind: 'comment' | 'peek'): ReactElement => (
|
||||||
|
<MarkdownKindContext value={kind}>
|
||||||
|
<TextPreview text={{ markdown: '[Client]' }} />
|
||||||
|
</MarkdownKindContext>
|
||||||
|
)
|
||||||
|
await render(preview('comment'))
|
||||||
|
const original = anchor()
|
||||||
|
await render(preview('peek'))
|
||||||
|
expect(container.querySelector('a')).toBeNull()
|
||||||
|
expect(container.textContent).toBe('[Client]\n')
|
||||||
|
expect(original.isConnected).toBe(false)
|
||||||
|
await render(preview('comment'))
|
||||||
|
expect(anchor().getAttribute('href')).toBe('symbol:Client')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('组件 DiffText 更新正文时保留 Markdown 排版和增删标记', async () => {
|
||||||
|
await render(<DiffText diff={{ markdown: [{ text: '[Client] ' }, { text: '旧说明', change: 'del' }, { text: '新说明', change: 'add' }] }} />)
|
||||||
|
expect(anchor().textContent).toBe('Client')
|
||||||
|
expect(container.querySelector('del')?.textContent).toBe('旧说明')
|
||||||
|
expect(container.querySelector('ins')?.textContent).toBe('新说明')
|
||||||
|
await render(<DiffText diff={{ markdown: [{ text: '[Client] ' }, { text: '**更新**', change: 'add' }] }} />)
|
||||||
|
expect(container.querySelector('del')).toBeNull()
|
||||||
|
expect(container.querySelector('ins strong')?.textContent).toBe('更新')
|
||||||
|
})
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { ReactElement } from 'react'
|
import type { ReactElement } from 'react'
|
||||||
import type { MarkdownKind } from './markdown'
|
import type { MarkdownKind } from './markdown'
|
||||||
import type { PreviewText } from './types'
|
import type { PreviewText } from './types'
|
||||||
import { createContext, useContext } from 'react'
|
import { createContext, useContext, useMemo } from 'react'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { renderMarkdown } from './markdown'
|
import { renderMarkdown } from './markdown'
|
||||||
import { SegmentText } from './SegmentText'
|
import { SegmentText } from './SegmentText'
|
||||||
@@ -13,10 +13,15 @@ export const MarkdownKindContext = createContext<Exclude<MarkdownKind, 'answer'>
|
|||||||
export function TextPreview({ text, className }: { text: PreviewText, className?: string }): ReactElement {
|
export function TextPreview({ text, className }: { text: PreviewText, className?: string }): ReactElement {
|
||||||
const kind = useContext(MarkdownKindContext)
|
const kind = useContext(MarkdownKindContext)
|
||||||
if ('markdown' in text)
|
if ('markdown' in text)
|
||||||
return <div className={cn('cr-markdown', className)} dangerouslySetInnerHTML={{ __html: renderMarkdown(text.markdown, kind) }} />
|
return <MarkdownPreview html={renderMarkdown(text.markdown, kind)} className={className} />
|
||||||
return (
|
return (
|
||||||
<p className={cn('cr-code', className)}>
|
<p className={cn('cr-code', className)}>
|
||||||
<SegmentText segments={text} />
|
<SegmentText segments={text} />
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MarkdownPreview({ html, className }: { html: string, className?: string }): ReactElement {
|
||||||
|
const content = useMemo(() => ({ __html: html }), [html])
|
||||||
|
return <div className={cn('cr-markdown', className)} dangerouslySetInnerHTML={content} />
|
||||||
|
}
|
||||||
Reference in new issue
Block a user