mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-09-11 12:25:42 +03:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
93629f0519
24
package.json
24
package.json
@ -13,14 +13,37 @@
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.4.2",
|
||||
"@codemirror/lang-cpp": "^6.0.2",
|
||||
"@codemirror/lang-css": "^6.1.1",
|
||||
"@codemirror/lang-html": "^6.4.2",
|
||||
"@codemirror/lang-java": "^6.0.1",
|
||||
"@codemirror/lang-javascript": "^6.1.4",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-markdown": "^6.1.0",
|
||||
"@codemirror/lang-php": "^6.0.1",
|
||||
"@codemirror/lang-python": "^6.1.2",
|
||||
"@codemirror/lang-vue": "^0.1.1",
|
||||
"@codemirror/lang-wast": "^6.0.1",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/view": "^6.9.2",
|
||||
"@lezer/common": "^1.0.2",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/javascript": "^1.4.1",
|
||||
"@lezer/lr": "^1.3.3",
|
||||
"@replit/codemirror-lang-svelte": "^6.0.0",
|
||||
"@square/svelte-store": "^1.0.14",
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"diff": "^5.1.0",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"diff2html": "^3.4.33",
|
||||
"highlight.js": "^11.7.0",
|
||||
"highlightjs-svelte": "^1.0.6",
|
||||
"inter-ui": "^3.19.3",
|
||||
"lang-svelte": "link:@replit/codemirror/lang-svelte",
|
||||
"nanoid": "^4.0.1",
|
||||
"posthog-js": "^1.46.1",
|
||||
"svelte-french-toast": "^1.0.3",
|
||||
@ -31,6 +54,7 @@
|
||||
"@sveltejs/kit": "next",
|
||||
"@tauri-apps/cli": "^1.2.2",
|
||||
"@types/diff": "^5.0.2",
|
||||
"@types/diff-match-patch": "^1.0.32",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"autoprefixer": "^10.4.7",
|
||||
|
351
pnpm-lock.yaml
351
pnpm-lock.yaml
@ -1,17 +1,40 @@
|
||||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@codemirror/autocomplete': ^6.4.2
|
||||
'@codemirror/lang-cpp': ^6.0.2
|
||||
'@codemirror/lang-css': ^6.1.1
|
||||
'@codemirror/lang-html': ^6.4.2
|
||||
'@codemirror/lang-java': ^6.0.1
|
||||
'@codemirror/lang-javascript': ^6.1.4
|
||||
'@codemirror/lang-json': ^6.0.1
|
||||
'@codemirror/lang-markdown': ^6.1.0
|
||||
'@codemirror/lang-php': ^6.0.1
|
||||
'@codemirror/lang-python': ^6.1.2
|
||||
'@codemirror/lang-vue': ^0.1.1
|
||||
'@codemirror/lang-wast': ^6.0.1
|
||||
'@codemirror/lang-xml': ^6.0.2
|
||||
'@codemirror/language': ^6.6.0
|
||||
'@codemirror/state': ^6.2.0
|
||||
'@codemirror/view': ^6.9.2
|
||||
'@lezer/common': ^1.0.2
|
||||
'@lezer/highlight': ^1.1.3
|
||||
'@lezer/javascript': ^1.4.1
|
||||
'@lezer/lr': ^1.3.3
|
||||
'@replit/codemirror-lang-svelte': ^6.0.0
|
||||
'@square/svelte-store': ^1.0.14
|
||||
'@sveltejs/adapter-static': next
|
||||
'@sveltejs/kit': next
|
||||
'@tauri-apps/api': ^1.2.0
|
||||
'@tauri-apps/cli': ^1.2.2
|
||||
'@types/diff': ^5.0.2
|
||||
'@types/diff-match-patch': ^1.0.32
|
||||
'@typescript-eslint/eslint-plugin': ^5.45.0
|
||||
'@typescript-eslint/parser': ^5.45.0
|
||||
autoprefixer: ^10.4.7
|
||||
date-fns: ^2.29.3
|
||||
diff: ^5.1.0
|
||||
diff-match-patch: ^1.0.5
|
||||
diff2html: ^3.4.33
|
||||
eslint: ^8.28.0
|
||||
eslint-config-prettier: ^8.5.0
|
||||
@ -19,6 +42,7 @@ specifiers:
|
||||
highlight.js: ^11.7.0
|
||||
highlightjs-svelte: ^1.0.6
|
||||
inter-ui: ^3.19.3
|
||||
lang-svelte: link:@replit/codemirror/lang-svelte
|
||||
nanoid: ^4.0.1
|
||||
postcss: ^8.4.14
|
||||
postcss-load-config: ^4.0.1
|
||||
@ -36,14 +60,37 @@ specifiers:
|
||||
vite: ^4.0.0
|
||||
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e
|
||||
'@codemirror/lang-cpp': 6.0.2
|
||||
'@codemirror/lang-css': 6.1.1_i3aqn63zftbgivbr4riltn5mqe
|
||||
'@codemirror/lang-html': 6.4.2
|
||||
'@codemirror/lang-java': 6.0.1
|
||||
'@codemirror/lang-javascript': 6.1.4
|
||||
'@codemirror/lang-json': 6.0.1
|
||||
'@codemirror/lang-markdown': 6.1.0
|
||||
'@codemirror/lang-php': 6.0.1
|
||||
'@codemirror/lang-python': 6.1.2_252h5dwvsiu2pnu2vr7ql3rya4
|
||||
'@codemirror/lang-vue': 0.1.1
|
||||
'@codemirror/lang-wast': 6.0.1
|
||||
'@codemirror/lang-xml': 6.0.2_@codemirror+view@6.9.2
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/javascript': 1.4.1
|
||||
'@lezer/lr': 1.3.3
|
||||
'@replit/codemirror-lang-svelte': 6.0.0_ybny7xhlf2ysg4majw54z4nkly
|
||||
'@square/svelte-store': 1.0.14
|
||||
'@tauri-apps/api': 1.2.0
|
||||
date-fns: 2.29.3
|
||||
diff: 5.1.0
|
||||
diff-match-patch: 1.0.5
|
||||
diff2html: 3.4.33
|
||||
highlight.js: 11.7.0
|
||||
highlightjs-svelte: 1.0.6
|
||||
inter-ui: 3.19.3
|
||||
lang-svelte: link:@replit/codemirror/lang-svelte
|
||||
nanoid: 4.0.1
|
||||
posthog-js: 1.46.1
|
||||
svelte-french-toast: 1.0.3_svelte@3.55.1
|
||||
@ -54,6 +101,7 @@ devDependencies:
|
||||
'@sveltejs/kit': 1.0.0-next.589_svelte@3.55.1+vite@4.0.4
|
||||
'@tauri-apps/cli': 1.2.3
|
||||
'@types/diff': 5.0.2
|
||||
'@types/diff-match-patch': 1.0.32
|
||||
'@typescript-eslint/eslint-plugin': 5.53.0_ny4s7qc6yg74faf3d6xty2ofzy
|
||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
autoprefixer: 10.4.13_postcss@8.4.21
|
||||
@ -74,6 +122,174 @@ devDependencies:
|
||||
|
||||
packages:
|
||||
|
||||
/@codemirror/autocomplete/6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e:
|
||||
resolution: {integrity: sha512-8WE2xp+D0MpWEv5lZ6zPW1/tf4AGb358T5GWYiKEuCP8MvFfT3tH2mIF9Y2yr2e3KbHuSvsVhosiEyqCpiJhZQ==}
|
||||
peerDependencies:
|
||||
'@codemirror/language': ^6.0.0
|
||||
'@codemirror/state': ^6.0.0
|
||||
'@codemirror/view': ^6.0.0
|
||||
'@lezer/common': ^1.0.0
|
||||
dependencies:
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
'@lezer/common': 1.0.2
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-cpp/6.0.2:
|
||||
resolution: {integrity: sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==}
|
||||
dependencies:
|
||||
'@codemirror/language': 6.6.0
|
||||
'@lezer/cpp': 1.1.0
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-css/6.1.1_i3aqn63zftbgivbr4riltn5mqe:
|
||||
resolution: {integrity: sha512-P6jdNEHyRcqqDgbvHYyC9Wxkek0rnG3a9aVSRi4a7WrjPbQtBTaOmvYpXmm13zZMAatO4Oqpac+0QZs7sy+LnQ==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@lezer/css': 1.1.1
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/view'
|
||||
- '@lezer/common'
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-html/6.4.2:
|
||||
resolution: {integrity: sha512-bqCBASkteKySwtIbiV/WCtGnn/khLRbbiV5TE+d9S9eQJD7BA4c5dTRm2b3bVmSpilff5EYxvB4PQaZzM/7cNw==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e
|
||||
'@codemirror/lang-css': 6.1.1_i3aqn63zftbgivbr4riltn5mqe
|
||||
'@codemirror/lang-javascript': 6.1.4
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/css': 1.1.1
|
||||
'@lezer/html': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-java/6.0.1:
|
||||
resolution: {integrity: sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==}
|
||||
dependencies:
|
||||
'@codemirror/language': 6.6.0
|
||||
'@lezer/java': 1.0.3
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-javascript/6.1.4:
|
||||
resolution: {integrity: sha512-OxLf7OfOZBTMRMi6BO/F72MNGmgOd9B0vetOLvHsDACFXayBzW8fm8aWnDM0yuy68wTK03MBf4HbjSBNRG5q7A==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/lint': 6.2.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/javascript': 1.4.1
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-json/6.0.1:
|
||||
resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==}
|
||||
dependencies:
|
||||
'@codemirror/language': 6.6.0
|
||||
'@lezer/json': 1.0.0
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-markdown/6.1.0:
|
||||
resolution: {integrity: sha512-HQDJg1Js19fPKKsI3Rp1X0J6mxyrRy2NX6+Evh0+/jGm6IZHL5ygMGKBYNWKXodoDQFvgdofNRG33gWOwV59Ag==}
|
||||
dependencies:
|
||||
'@codemirror/lang-html': 6.4.2
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/markdown': 1.0.2
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-php/6.0.1:
|
||||
resolution: {integrity: sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==}
|
||||
dependencies:
|
||||
'@codemirror/lang-html': 6.4.2
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/php': 1.0.1
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-python/6.1.2_252h5dwvsiu2pnu2vr7ql3rya4:
|
||||
resolution: {integrity: sha512-nbQfifLBZstpt6Oo4XxA2LOzlSp4b/7Bc5cmodG1R+Cs5PLLCTUvsMNWDnziiCfTOG/SW1rVzXq/GbIr6WXlcw==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e
|
||||
'@codemirror/language': 6.6.0
|
||||
'@lezer/python': 1.1.3
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/state'
|
||||
- '@codemirror/view'
|
||||
- '@lezer/common'
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-vue/0.1.1:
|
||||
resolution: {integrity: sha512-GIfc/MemCFKUdNSYGTFZDN8XsD2z0DUY7DgrK34on0dzdZ/CawZbi+SADYfVzWoPPdxngHzLhqlR5pSOqyPCvA==}
|
||||
dependencies:
|
||||
'@codemirror/lang-html': 6.4.2
|
||||
'@codemirror/lang-javascript': 6.1.4
|
||||
'@codemirror/language': 6.6.0
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-wast/6.0.1:
|
||||
resolution: {integrity: sha512-sQLsqhRjl2MWG3rxZysX+2XAyed48KhLBHLgq9xcKxIJu3npH/G+BIXW5NM5mHeDUjG0jcGh9BcjP0NfMStuzA==}
|
||||
dependencies:
|
||||
'@codemirror/language': 6.6.0
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-xml/6.0.2_@codemirror+view@6.9.2:
|
||||
resolution: {integrity: sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/xml': 1.0.1
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/view'
|
||||
dev: false
|
||||
|
||||
/@codemirror/language/6.6.0:
|
||||
resolution: {integrity: sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
style-mod: 4.0.2
|
||||
dev: false
|
||||
|
||||
/@codemirror/lint/6.2.0:
|
||||
resolution: {integrity: sha512-KVCECmR2fFeYBr1ZXDVue7x3q5PMI0PzcIbA+zKufnkniMBo1325t0h1jM85AKp8l3tj67LRxVpZfgDxEXlQkg==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
crelt: 1.0.5
|
||||
dev: false
|
||||
|
||||
/@codemirror/state/6.2.0:
|
||||
resolution: {integrity: sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==}
|
||||
dev: false
|
||||
|
||||
/@codemirror/view/6.9.2:
|
||||
resolution: {integrity: sha512-ci0r/v6aKOSlzOs7/STMTYP3jX/+YMq2dAfAJcLIB6uom4ThtrUlzeuS7GTRGNqJJ+qAJR1vGWfXgu4CO/0myQ==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.2.0
|
||||
style-mod: 4.0.2
|
||||
w3c-keyname: 2.2.6
|
||||
dev: false
|
||||
|
||||
/@esbuild/android-arm/0.16.17:
|
||||
resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==}
|
||||
engines: {node: '>=12'}
|
||||
@ -325,6 +541,93 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: true
|
||||
|
||||
/@lezer/common/1.0.2:
|
||||
resolution: {integrity: sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==}
|
||||
dev: false
|
||||
|
||||
/@lezer/cpp/1.1.0:
|
||||
resolution: {integrity: sha512-zUHrjNFuY/DOZCkOBJ6qItQIkcopHM/Zv/QOE0a4XNG3HDNahxTNu5fQYl8dIuKCpxCqRdMl5cEwl5zekFc7BA==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/css/1.1.1:
|
||||
resolution: {integrity: sha512-mSjx+unLLapEqdOYDejnGBokB5+AiJKZVclmud0MKQOKx3DLJ5b5VTCstgDDknR6iIV4gVrN6euzsCnj0A2gQA==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/highlight/1.1.3:
|
||||
resolution: {integrity: sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==}
|
||||
dependencies:
|
||||
'@lezer/common': 1.0.2
|
||||
dev: false
|
||||
|
||||
/@lezer/html/1.3.3:
|
||||
resolution: {integrity: sha512-04Fyvu66DjV2EjhDIG1kfDdktn5Pfw56SXPrzKNQH5B2m7BDfc6bDsz+ZJG8dLS3kIPEKbyyq1Sm2/kjeG0+AA==}
|
||||
dependencies:
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/java/1.0.3:
|
||||
resolution: {integrity: sha512-kKN17wmgP1cgHb8juR4pwVSPMKkDMzY/lAPbBsZ1fpXwbk2sg3N1kIrf0q+LefxgrANaQb/eNO7+m2QPruTFng==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/javascript/1.4.1:
|
||||
resolution: {integrity: sha512-Hqx36DJeYhKtdpc7wBYPR0XF56ZzIp0IkMO/zNNj80xcaFOV4Oj/P7TQc/8k2TxNhzl7tV5tXS8ZOCPbT4L3nA==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/json/1.0.0:
|
||||
resolution: {integrity: sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/lr/1.3.3:
|
||||
resolution: {integrity: sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==}
|
||||
dependencies:
|
||||
'@lezer/common': 1.0.2
|
||||
dev: false
|
||||
|
||||
/@lezer/markdown/1.0.2:
|
||||
resolution: {integrity: sha512-8CY0OoZ6V5EzPjSPeJ4KLVbtXdLBd8V6sRCooN5kHnO28ytreEGTyrtU/zUwo/XLRzGr/e1g44KlzKi3yWGB5A==}
|
||||
dependencies:
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/highlight': 1.1.3
|
||||
dev: false
|
||||
|
||||
/@lezer/php/1.0.1:
|
||||
resolution: {integrity: sha512-aqdCQJOXJ66De22vzdwnuC502hIaG9EnPK2rSi+ebXyUd+j7GAX1mRjWZOVOmf3GST1YUfUCu6WXDiEgDGOVwA==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/python/1.1.3:
|
||||
resolution: {integrity: sha512-rUdt5/H8JjVY3YIROZF2ZNUMx7eYB7h0cmC8c4TfkgJt4xcn6vLpjCOCk1usP4vV3YfMC+VDB786dKjJ6tL5Hw==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@lezer/xml/1.0.1:
|
||||
resolution: {integrity: sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==}
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@nodelib/fs.scandir/2.1.5:
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@ -350,6 +653,34 @@ packages:
|
||||
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
||||
dev: true
|
||||
|
||||
/@replit/codemirror-lang-svelte/6.0.0_ybny7xhlf2ysg4majw54z4nkly:
|
||||
resolution: {integrity: sha512-U2OqqgMM6jKelL0GNWbAmqlu1S078zZNoBqlJBW+retTc5M4Mha6/Y2cf4SVg6ddgloJvmcSpt4hHrVoM4ePRA==}
|
||||
peerDependencies:
|
||||
'@codemirror/autocomplete': ^6.0.0
|
||||
'@codemirror/lang-css': ^6.0.1
|
||||
'@codemirror/lang-html': ^6.2.0
|
||||
'@codemirror/lang-javascript': ^6.1.1
|
||||
'@codemirror/language': ^6.0.0
|
||||
'@codemirror/state': ^6.0.0
|
||||
'@codemirror/view': ^6.0.0
|
||||
'@lezer/common': ^1.0.0
|
||||
'@lezer/highlight': ^1.0.0
|
||||
'@lezer/javascript': ^1.2.0
|
||||
'@lezer/lr': ^1.0.0
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.2_lc2v3dpzp2l5pdzwtgfaudkm3e
|
||||
'@codemirror/lang-css': 6.1.1_i3aqn63zftbgivbr4riltn5mqe
|
||||
'@codemirror/lang-html': 6.4.2
|
||||
'@codemirror/lang-javascript': 6.1.4
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.9.2
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/javascript': 1.4.1
|
||||
'@lezer/lr': 1.3.3
|
||||
dev: false
|
||||
|
||||
/@square/svelte-store/1.0.14:
|
||||
resolution: {integrity: sha512-kNaSpEec2JevfOB9XaWkq00oAOvm2fq8/0yotg++UVEHuHXQqM6pW602ByjBwFHn1gTRSdb568BAtkEDBt5uKQ==}
|
||||
dependencies:
|
||||
@ -518,6 +849,10 @@ packages:
|
||||
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
||||
dev: true
|
||||
|
||||
/@types/diff-match-patch/1.0.32:
|
||||
resolution: {integrity: sha512-bPYT5ECFiblzsVzyURaNhljBH2Gh1t9LowgUwciMrNAhFewLkHT2H0Mto07Y4/3KCOGZHRQll3CTtQZ0X11D/A==}
|
||||
dev: true
|
||||
|
||||
/@types/diff/5.0.2:
|
||||
resolution: {integrity: sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg==}
|
||||
dev: true
|
||||
@ -875,6 +1210,10 @@ packages:
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: true
|
||||
|
||||
/crelt/1.0.5:
|
||||
resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==}
|
||||
dev: false
|
||||
|
||||
/cross-spawn/7.0.3:
|
||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||
engines: {node: '>= 8'}
|
||||
@ -943,6 +1282,10 @@ packages:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
dev: true
|
||||
|
||||
/diff-match-patch/1.0.5:
|
||||
resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
|
||||
dev: false
|
||||
|
||||
/diff/5.1.0:
|
||||
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
@ -2016,6 +2359,10 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/style-mod/4.0.2:
|
||||
resolution: {integrity: sha512-C4myMmRTO8iaC5Gg+N1ftK2WT4eXUTMAa+HEFPPrfVeO/NtqLTtAmV1HbqnuGtLwCek44Ra76fdGUkSqjiMPcQ==}
|
||||
dev: false
|
||||
|
||||
/supports-color/7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
@ -2299,6 +2646,10 @@ packages:
|
||||
vite: 4.0.4
|
||||
dev: true
|
||||
|
||||
/w3c-keyname/2.2.6:
|
||||
resolution: {integrity: sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==}
|
||||
dev: false
|
||||
|
||||
/which/2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -1,99 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { type Delta, Operation } from '$lib/deltas';
|
||||
import { createTwoFilesPatch } from 'diff';
|
||||
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/base16/gruvbox-dark-medium.css';
|
||||
|
||||
import { parse } from 'diff2html';
|
||||
import type { DiffBlock, LineType } from 'diff2html/lib/types';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(hljs.initHighlighting);
|
||||
|
||||
export let doc: string;
|
||||
export let deltas: Delta[];
|
||||
export let filepath: string;
|
||||
|
||||
const applyDeltas = (text: string, deltas: Delta[]) => {
|
||||
const operations = deltas.flatMap((delta) => delta.operations);
|
||||
|
||||
operations.forEach((operation) => {
|
||||
if (Operation.isInsert(operation)) {
|
||||
text =
|
||||
text.slice(0, operation.insert[0]) +
|
||||
operation.insert[1] +
|
||||
text.slice(operation.insert[0]);
|
||||
} else if (Operation.isDelete(operation)) {
|
||||
text =
|
||||
text.slice(0, operation.delete[0]) +
|
||||
text.slice(operation.delete[0] + operation.delete[1]);
|
||||
}
|
||||
});
|
||||
return text;
|
||||
};
|
||||
|
||||
const highlightBlocks = (blocks: DiffBlock[]) =>
|
||||
blocks.map(({ header, lines }) => ({
|
||||
header,
|
||||
lines: lines.map(({ oldNumber, newNumber, type, content }) => ({
|
||||
oldNumber,
|
||||
newNumber,
|
||||
type,
|
||||
prefix: content.substring(0, 1),
|
||||
originalContent: content.substring(1),
|
||||
content: hljs.highlight(content.substring(1), { language }).value
|
||||
}))
|
||||
}));
|
||||
|
||||
const getLanguage = (filepath: string) => {
|
||||
const ext = filepath.split('.').pop();
|
||||
return ext && hljs.getLanguage(ext) ? ext : 'plaintext';
|
||||
};
|
||||
|
||||
let editor: HTMLElement;
|
||||
|
||||
$: left = deltas.length > 0 ? applyDeltas(doc, deltas.slice(0, deltas.length - 1)) : doc;
|
||||
$: right = deltas.length > 0 ? applyDeltas(left, deltas.slice(deltas.length - 1)) : left;
|
||||
$: patch = createTwoFilesPatch(filepath, filepath, left, right, '', '', { context: 100000 });
|
||||
$: language = getLanguage(filepath);
|
||||
$: parsed = parse(patch);
|
||||
$: parsed &&
|
||||
editor &&
|
||||
editor.querySelector('.changed')?.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
||||
|
||||
const bgColor = (type: LineType) =>
|
||||
type === 'insert' ? 'bg-[#14FF00]/20' : type === 'delete' ? 'bg-[#FF0000]/20' : '';
|
||||
</script>
|
||||
|
||||
<table class="flex h-full w-full flex-col whitespace-pre font-mono" bind:this={editor}>
|
||||
{#each parsed as hunk}
|
||||
<tbody>
|
||||
{#each highlightBlocks(hunk.blocks) as block}
|
||||
{#each block.lines as line}
|
||||
<tr class="w-full">
|
||||
<td>
|
||||
<div class="flex select-none justify-between gap-2">
|
||||
<div>{line.oldNumber ?? ''}</div>
|
||||
<div>{line.newNumber ?? ''}</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="flex-auto {bgColor(line.type)}"
|
||||
class:changed={line.type === 'insert' || line.type === 'delete'}
|
||||
>
|
||||
<div class="px-4">
|
||||
{#if line.content}
|
||||
<span>{@html line.content}</span>
|
||||
{:else}
|
||||
<span>{line.originalContent}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{/each}
|
||||
</tbody>
|
||||
{/each}
|
||||
</table>
|
203
src/lib/components/CodeViewer/CodeHighlighter.ts
Normal file
203
src/lib/components/CodeViewer/CodeHighlighter.ts
Normal file
@ -0,0 +1,203 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import { HighlightStyle, LanguageSupport } from '@codemirror/language';
|
||||
import { tags, highlightTree } from '@lezer/highlight';
|
||||
import { NodeType, Tree } from '@lezer/common';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { css } from '@codemirror/lang-css';
|
||||
import { html } from '@codemirror/lang-html';
|
||||
import { xml } from '@codemirror/lang-xml';
|
||||
import { cpp } from '@codemirror/lang-cpp';
|
||||
import { java } from '@codemirror/lang-java';
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import { php } from '@codemirror/lang-php';
|
||||
import { python } from '@codemirror/lang-python';
|
||||
import { markdown } from '@codemirror/lang-markdown';
|
||||
import { wast } from '@codemirror/lang-wast';
|
||||
import { svelte } from '@replit/codemirror-lang-svelte';
|
||||
import { vue } from '@codemirror/lang-vue';
|
||||
|
||||
const t = tags;
|
||||
|
||||
export const highlightStyle: HighlightStyle = HighlightStyle.define([
|
||||
{ tag: t.variableName, class: 'token-variable' },
|
||||
{ tag: t.definition(t.variableName), class: 'token-definition' },
|
||||
{ tag: t.propertyName, class: 'token-property' },
|
||||
{ tag: [t.typeName, t.className, t.namespace, t.macroName], class: 'token-type' },
|
||||
{ tag: [t.special(t.name), t.constant(t.className)], class: 'token-variable-special' },
|
||||
{ tag: t.standard(t.variableName), class: 'token-builtin' },
|
||||
|
||||
{ tag: [t.number, t.literal, t.unit], class: 'token-number' },
|
||||
{ tag: t.string, class: 'token-string' },
|
||||
{ tag: [t.special(t.string), t.regexp, t.escape], class: 'token-string-special' },
|
||||
{ tag: [], class: 'token-atom' },
|
||||
|
||||
{ tag: t.keyword, class: 'token-keyword' },
|
||||
{ tag: [t.comment, t.quote], class: 'token-comment' },
|
||||
{ tag: t.meta, class: 'token-meta' },
|
||||
{ tag: t.invalid, class: 'token-invalid' },
|
||||
|
||||
{ tag: t.tagName, class: 'token-tag' },
|
||||
{ tag: t.attributeName, class: 'token-attribute' },
|
||||
{ tag: t.attributeValue, class: 'token-attribute-value' },
|
||||
|
||||
{ tag: t.inserted, class: 'token-inserted' },
|
||||
{ tag: t.deleted, class: 'token-deleted' },
|
||||
{ tag: t.heading, class: 'token-heading' },
|
||||
{ tag: t.link, class: 'token-link' },
|
||||
{ tag: t.strikethrough, class: 'token-strikethrough' },
|
||||
{ tag: t.strong, class: 'token-strong' },
|
||||
{ tag: t.emphasis, class: 'token-emphasis' }
|
||||
]);
|
||||
|
||||
export function create(code: string, mimeType: string): CodeHighlighter {
|
||||
const language = languageFromFilename(mimeType);
|
||||
let tree: Tree;
|
||||
if (language) {
|
||||
tree = language.language.parser.parse(code);
|
||||
} else {
|
||||
tree = new Tree(NodeType.none, [], [], code.length);
|
||||
}
|
||||
return new CodeHighlighter(code, tree);
|
||||
}
|
||||
|
||||
export function highlightNode(node: Element, mimeType: string): void {
|
||||
const code = node.textContent || '';
|
||||
const highlighter = create(code, mimeType);
|
||||
if (node.firstChild) {
|
||||
node.textContent = '';
|
||||
}
|
||||
highlighter.highlight((text, style) => {
|
||||
let token: Node = document.createTextNode(text);
|
||||
if (style) {
|
||||
const span = document.createElement('span');
|
||||
span.className = style;
|
||||
span.appendChild(token);
|
||||
token = span;
|
||||
}
|
||||
node.appendChild(token);
|
||||
});
|
||||
}
|
||||
|
||||
export function languageFromFilename(filename: string): LanguageSupport | null {
|
||||
const ext = filename.split('.').pop();
|
||||
switch (ext) {
|
||||
case 'jsx':
|
||||
case 'js':
|
||||
// We intentionally allow JSX in normal .js as well as .jsx files,
|
||||
// because there are simply too many existing applications and
|
||||
// examples out there that use JSX within .js files, and we don't
|
||||
// want to break them.
|
||||
return javascript({ jsx: true });
|
||||
case 'ts':
|
||||
return javascript({ typescript: true });
|
||||
case 'tsx':
|
||||
return javascript({ typescript: true, jsx: true });
|
||||
|
||||
case 'css':
|
||||
return css();
|
||||
|
||||
case 'html':
|
||||
return html({ selfClosingTags: true });
|
||||
|
||||
case 'xml':
|
||||
return xml();
|
||||
|
||||
case 'wasm':
|
||||
return wast();
|
||||
|
||||
case 'cpp':
|
||||
case 'c++':
|
||||
case 'hpp':
|
||||
case 'h++':
|
||||
return cpp();
|
||||
|
||||
// case 'text/x-go':
|
||||
// return new LanguageSupport(await CodeMirror.go());
|
||||
|
||||
case 'java':
|
||||
return java();
|
||||
|
||||
// case 'text/x-kotlin':
|
||||
// return new LanguageSupport(await CodeMirror.kotlin());
|
||||
|
||||
case 'json':
|
||||
return json();
|
||||
|
||||
case 'php':
|
||||
return php();
|
||||
|
||||
case 'python':
|
||||
return python();
|
||||
|
||||
case 'md':
|
||||
return markdown();
|
||||
|
||||
// case 'text/x-sh':
|
||||
// return new LanguageSupport(await CodeMirror.shell());
|
||||
|
||||
// case 'text/x-coffeescript':
|
||||
// return new LanguageSupport(await CodeMirror.coffeescript());
|
||||
|
||||
// case 'text/x-clojure':
|
||||
// return new LanguageSupport(await CodeMirror.clojure());
|
||||
|
||||
// case 'application/vnd.dart':
|
||||
// return new LanguageSupport(await CodeMirror.dart());
|
||||
|
||||
// case 'text/x-gss':
|
||||
// return new LanguageSupport(await CodeMirror.gss());
|
||||
|
||||
// case 'text/x-less':
|
||||
// return new CodeMirror.LanguageSupport(await CodeMirror.less());
|
||||
|
||||
// case 'text/x-sass':
|
||||
// return new LanguageSupport(await CodeMirror.sass());
|
||||
|
||||
// case 'text/x-scala':
|
||||
// return new LanguageSupport(await CodeMirror.scala());
|
||||
|
||||
// case 'text/x-scss':
|
||||
// return new LanguageSupport(await CodeMirror.scss());
|
||||
|
||||
case 'svelte':
|
||||
return svelte();
|
||||
|
||||
case 'vue':
|
||||
return vue();
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class CodeHighlighter {
|
||||
constructor(readonly code: string, readonly tree: Tree) { }
|
||||
|
||||
highlight(token: (text: string, style: string) => void): void {
|
||||
this.highlightRange(0, this.code.length, token);
|
||||
}
|
||||
|
||||
highlightRange(from: number, to: number, token: (text: string, style: string) => void): void {
|
||||
let pos = from;
|
||||
const flush = (to: number, style: string): void => {
|
||||
if (to > pos) {
|
||||
token(this.code.slice(pos, to), style);
|
||||
pos = to;
|
||||
}
|
||||
};
|
||||
highlightTree(
|
||||
this.tree,
|
||||
highlightStyle,
|
||||
(from, to, style) => {
|
||||
flush(from, '');
|
||||
flush(to, style);
|
||||
},
|
||||
from,
|
||||
to
|
||||
);
|
||||
flush(to, '');
|
||||
}
|
||||
}
|
33
src/lib/components/CodeViewer/CodeViewer.svelte
Normal file
33
src/lib/components/CodeViewer/CodeViewer.svelte
Normal file
@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import { type Delta, Operation } from '$lib/deltas';
|
||||
import { lineDiff } from './diff';
|
||||
import DiffView from './DiffView.svelte';
|
||||
|
||||
export let doc: string;
|
||||
export let deltas: Delta[];
|
||||
export let filepath: string;
|
||||
|
||||
const applyDeltas = (text: string, deltas: Delta[]) => {
|
||||
const operations = deltas.flatMap((delta) => delta.operations);
|
||||
|
||||
operations.forEach((operation) => {
|
||||
if (Operation.isInsert(operation)) {
|
||||
text =
|
||||
text.slice(0, operation.insert[0]) +
|
||||
operation.insert[1] +
|
||||
text.slice(operation.insert[0]);
|
||||
} else if (Operation.isDelete(operation)) {
|
||||
text =
|
||||
text.slice(0, operation.delete[0]) +
|
||||
text.slice(operation.delete[0] + operation.delete[1]);
|
||||
}
|
||||
});
|
||||
return text;
|
||||
};
|
||||
|
||||
$: left = deltas.length > 0 ? applyDeltas(doc, deltas.slice(0, deltas.length - 1)) : doc;
|
||||
$: right = deltas.length > 0 ? applyDeltas(left, deltas.slice(deltas.length - 1)) : left;
|
||||
$: diff = lineDiff(left.split('\n'), right.split('\n'));
|
||||
</script>
|
||||
|
||||
<DiffView {diff} {filepath} />
|
80
src/lib/components/CodeViewer/DiffView.svelte
Normal file
80
src/lib/components/CodeViewer/DiffView.svelte
Normal file
@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
import type { DiffArray } from './diff';
|
||||
import { create } from './CodeHighlighter';
|
||||
import { buildDiffRows, documentMap, RowType, type Row } from './renderer';
|
||||
import './diff.css';
|
||||
import './colors/gruvbox.css';
|
||||
|
||||
export let diff: DiffArray;
|
||||
export let filepath: string;
|
||||
|
||||
$: diffRows = buildDiffRows(diff);
|
||||
$: originalHighlighter = create(diffRows.originalLines.join('\n'), filepath);
|
||||
$: originalMap = documentMap(diffRows.originalLines);
|
||||
$: currentHighlighter = create(diffRows.currentLines.join('\n'), filepath);
|
||||
$: currentMap = documentMap(diffRows.currentLines);
|
||||
|
||||
const renderRowContent = (row: Row) => {
|
||||
if (row.type === RowType.Spacer) {
|
||||
return row.tokens.map((tok) => `${tok.text}`);
|
||||
}
|
||||
const [doc, startPos] =
|
||||
row.type === RowType.Deletion
|
||||
? [originalHighlighter, originalMap.get(row.originalLineNumber) as number]
|
||||
: [currentHighlighter, currentMap.get(row.currentLineNumber) as number];
|
||||
const content: string[] = [];
|
||||
let pos = startPos;
|
||||
|
||||
const sanitize = (text: string) => {
|
||||
var element = document.createElement('div');
|
||||
element.innerText = text;
|
||||
return element.innerHTML;
|
||||
};
|
||||
|
||||
for (const token of row.tokens) {
|
||||
let tokenContent = '';
|
||||
doc.highlightRange(pos, pos + token.text.length, (text, style) => {
|
||||
tokenContent += style ? `<span class=${style}>${sanitize(text)}</span>` : sanitize(text);
|
||||
});
|
||||
|
||||
content.push(
|
||||
token.className
|
||||
? `<span class=${token.className}>${tokenContent}</span>`
|
||||
: `${tokenContent}`
|
||||
);
|
||||
pos += token.text.length;
|
||||
}
|
||||
return content;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="diff-listing w-full select-text whitespace-pre font-mono">
|
||||
{#each diffRows.rows as row}
|
||||
{@const baseNumber =
|
||||
row.type === RowType.Equal || row.type === RowType.Deletion
|
||||
? String(row.originalLineNumber)
|
||||
: ''}
|
||||
{@const curNumber =
|
||||
row.type === RowType.Equal || row.type === RowType.Addition
|
||||
? String(row.currentLineNumber)
|
||||
: ''}
|
||||
<div class="select-none pr-1 pl-2.5 text-right text-[#665c54]">{baseNumber}</div>
|
||||
<div class="select-none pr-1 pl-2.5 text-right text-[#665c54]">{curNumber}</div>
|
||||
<div
|
||||
class="diff-line-marker"
|
||||
class:diff-line-addition={row.type === RowType.Addition}
|
||||
class:diff-line-deletion={row.type === RowType.Deletion}
|
||||
>
|
||||
{row.type === RowType.Addition ? '+' : row.type === RowType.Deletion ? '-' : ''}
|
||||
</div>
|
||||
<div
|
||||
class:line-changed={row.type === RowType.Addition || row.type === RowType.Deletion}
|
||||
class="px-1 diff-line-{row.type}"
|
||||
data-line-number={curNumber}
|
||||
>
|
||||
{#each renderRowContent(row) as content}
|
||||
{@html content}
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
36
src/lib/components/CodeViewer/characterIdMap.ts
Normal file
36
src/lib/components/CodeViewer/characterIdMap.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
export class CharacterIdMap<T> {
|
||||
readonly #elementToCharacter: Map<T, string>;
|
||||
readonly #characterToElement: Map<string, T>;
|
||||
#charCode: number;
|
||||
|
||||
constructor() {
|
||||
this.#elementToCharacter = new Map();
|
||||
this.#characterToElement = new Map();
|
||||
this.#charCode = 33;
|
||||
}
|
||||
|
||||
toChar(object: T): string {
|
||||
let character = this.#elementToCharacter.get(object);
|
||||
if (!character) {
|
||||
if (this.#charCode >= 0xffff) {
|
||||
throw new Error('CharacterIdMap ran out of capacity!');
|
||||
}
|
||||
character = String.fromCharCode(this.#charCode++);
|
||||
this.#elementToCharacter.set(object, character);
|
||||
this.#characterToElement.set(character, object);
|
||||
}
|
||||
return character;
|
||||
}
|
||||
|
||||
fromChar(character: string): T | null {
|
||||
const object = this.#characterToElement.get(character);
|
||||
if (object === undefined) {
|
||||
return null;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
88
src/lib/components/CodeViewer/colors/gruvbox.css
Normal file
88
src/lib/components/CodeViewer/colors/gruvbox.css
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
.token-variable {
|
||||
color: #83a598;
|
||||
}
|
||||
.token-property {
|
||||
color: #8ec07c;
|
||||
}
|
||||
.token-type {
|
||||
color: #fabd2f;
|
||||
}
|
||||
.token-variable-special {
|
||||
color: #d3869b;
|
||||
}
|
||||
.token-definition {
|
||||
color: #ebdbb2;
|
||||
}
|
||||
.token-builtin {
|
||||
color: #d3869b;
|
||||
}
|
||||
.token-number {
|
||||
color: #d3869b;
|
||||
}
|
||||
.token-string {
|
||||
color: #ebdbb2;
|
||||
}
|
||||
.token-string-special {
|
||||
color: #d3869b;
|
||||
}
|
||||
.token-atom {
|
||||
color: #d3869b;
|
||||
}
|
||||
|
||||
.token-keyword {
|
||||
color: #fb4934;
|
||||
}
|
||||
.token-comment {
|
||||
color: #928374;
|
||||
}
|
||||
.token-meta {
|
||||
color: #fabd2f;
|
||||
}
|
||||
.token-invalid {
|
||||
color: #fe8019;
|
||||
}
|
||||
|
||||
.token-tag {
|
||||
color: #8ec07c;
|
||||
}
|
||||
.token-attribute {
|
||||
color: #8ec07c;
|
||||
}
|
||||
.token-attribute-value {
|
||||
color: var(--color-token-attribute-value);
|
||||
}
|
||||
|
||||
.token-inserted {
|
||||
color: #076678;
|
||||
}
|
||||
.token-deleted {
|
||||
color: #8ec07c;
|
||||
}
|
||||
|
||||
.token-heading {
|
||||
color: var(--color-token-variable-special);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token-link {
|
||||
color: var(--color-token-variable-special);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.token-strikethrough {
|
||||
text-decoration: strike-through;
|
||||
}
|
||||
|
||||
.token-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token-emphasis {
|
||||
font-style: italic;
|
||||
}
|
43
src/lib/components/CodeViewer/diff.css
Normal file
43
src/lib/components/CodeViewer/diff.css
Normal file
@ -0,0 +1,43 @@
|
||||
.diff-listing {
|
||||
display: grid;
|
||||
grid-template-columns: max-content max-content max-content auto;
|
||||
}
|
||||
|
||||
.diff-line-marker {
|
||||
border-right: 1px solid var(--color-details-hairline);
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.diff-line-marker-addition,
|
||||
.diff-line-addition {
|
||||
--override-addition-background-color: hsl(144deg 55% 49% / 20%);
|
||||
|
||||
background-color: var(--override-addition-background-color);
|
||||
}
|
||||
|
||||
.diff-line-marker-deletion,
|
||||
.diff-line-deletion {
|
||||
--override-deletion-background-color: rgb(255 0 0 / 20%);
|
||||
|
||||
background-color: var(--override-deletion-background-color);
|
||||
}
|
||||
|
||||
.diff-line-addition .inner-diff {
|
||||
--override-addition-inner-diff-background-color: hsl(144deg 55% 49% / 30%);
|
||||
|
||||
background-color: var(--override-addition-inner-diff-background-color);
|
||||
}
|
||||
|
||||
.diff-line-deletion .inner-diff {
|
||||
--override-deletion-inner-diff-background-color: rgb(255 0 0 / 30%);
|
||||
|
||||
background-color: var(--override-deletion-inner-diff-background-color);
|
||||
}
|
||||
|
||||
.diff-line-spacer {
|
||||
--override-spacer-background-color: rgb(0 0 255 / 10%);
|
||||
|
||||
text-align: center;
|
||||
background-color: var(--override-spacer-background-color);
|
||||
}
|
44
src/lib/components/CodeViewer/diff.ts
Normal file
44
src/lib/components/CodeViewer/diff.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { CharacterIdMap } from './characterIdMap';
|
||||
import { diff_match_patch } from 'diff-match-patch';
|
||||
|
||||
export const charDiff = (
|
||||
text1: string,
|
||||
text2: string,
|
||||
cleanup?: boolean
|
||||
): { 0: number; 1: string }[] => {
|
||||
const differ = new diff_match_patch();
|
||||
const diff = differ.diff_main(text1, text2);
|
||||
if (cleanup) {
|
||||
differ.diff_cleanupSemantic(diff);
|
||||
}
|
||||
return diff;
|
||||
};
|
||||
|
||||
export const lineDiff = (lines1: string[], lines2: string[]): DiffArray => {
|
||||
const idMap = new CharacterIdMap<string>();
|
||||
const text1 = lines1.map((line) => idMap.toChar(line)).join('');
|
||||
const text2 = lines2.map((line) => idMap.toChar(line)).join('');
|
||||
|
||||
const diff = charDiff(text1, text2);
|
||||
const lineDiff = [];
|
||||
for (let i = 0; i < diff.length; i++) {
|
||||
const lines = [];
|
||||
for (let j = 0; j < diff[i][1].length; j++) {
|
||||
lines.push(idMap.fromChar(diff[i][1][j]) || '');
|
||||
}
|
||||
|
||||
lineDiff.push({ 0: diff[i][0], 1: lines });
|
||||
}
|
||||
return lineDiff;
|
||||
};
|
||||
|
||||
// TODO(crbug.com/1167717): Make this a const enum again
|
||||
// eslint-disable-next-line rulesdir/const_enum
|
||||
export enum Operation {
|
||||
Equal = 0,
|
||||
Insert = 1,
|
||||
Delete = -1,
|
||||
Edit = 2
|
||||
}
|
||||
|
||||
export type DiffArray = { 0: Operation; 1: string[] }[];
|
3
src/lib/components/CodeViewer/index.ts
Normal file
3
src/lib/components/CodeViewer/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { default as CodeViewer } from './CodeViewer.svelte';
|
||||
|
||||
export default CodeViewer;
|
155
src/lib/components/CodeViewer/renderer.ts
Normal file
155
src/lib/components/CodeViewer/renderer.ts
Normal file
@ -0,0 +1,155 @@
|
||||
import { Operation, type DiffArray, charDiff } from './diff';
|
||||
|
||||
export interface Token {
|
||||
text: string;
|
||||
className: string;
|
||||
}
|
||||
|
||||
export interface Row {
|
||||
originalLineNumber: number;
|
||||
currentLineNumber: number;
|
||||
tokens: Token[];
|
||||
type: RowType;
|
||||
}
|
||||
|
||||
export const enum RowType {
|
||||
Deletion = 'deletion',
|
||||
Addition = 'addition',
|
||||
Equal = 'equal',
|
||||
Spacer = 'spacer'
|
||||
}
|
||||
|
||||
export function buildDiffRows(diff: DiffArray): {
|
||||
originalLines: readonly string[];
|
||||
currentLines: readonly string[];
|
||||
rows: readonly Row[];
|
||||
} {
|
||||
let currentLineNumber = 0;
|
||||
let originalLineNumber = 0;
|
||||
const paddingLines = 100000;
|
||||
|
||||
const originalLines: string[] = [];
|
||||
const currentLines: string[] = [];
|
||||
const rows: Row[] = [];
|
||||
|
||||
for (let i = 0; i < diff.length; ++i) {
|
||||
const token = diff[i];
|
||||
switch (token[0]) {
|
||||
case Operation.Equal:
|
||||
rows.push(...createEqualRows(token[1], i === 0, i === diff.length - 1));
|
||||
originalLines.push(...token[1]);
|
||||
currentLines.push(...token[1]);
|
||||
break;
|
||||
case Operation.Insert:
|
||||
for (const line of token[1]) {
|
||||
rows.push(createRow(line, RowType.Addition));
|
||||
}
|
||||
currentLines.push(...token[1]);
|
||||
break;
|
||||
case Operation.Delete:
|
||||
originalLines.push(...token[1]);
|
||||
if (diff[i + 1] && diff[i + 1][0] === Operation.Insert) {
|
||||
i++;
|
||||
rows.push(...createModifyRows(token[1].join('\n'), diff[i][1].join('\n')));
|
||||
currentLines.push(...diff[i][1]);
|
||||
} else {
|
||||
for (const line of token[1]) {
|
||||
rows.push(createRow(line, RowType.Deletion));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { originalLines, currentLines, rows };
|
||||
|
||||
function createEqualRows(lines: string[], atStart: boolean, atEnd: boolean): Row[] {
|
||||
const equalRows = [];
|
||||
if (!atStart) {
|
||||
for (let i = 0; i < paddingLines && i < lines.length; i++) {
|
||||
equalRows.push(createRow(lines[i], RowType.Equal));
|
||||
}
|
||||
if (lines.length > paddingLines * 2 + 1 && !atEnd) {
|
||||
equalRows.push(
|
||||
createRow(`skipping ${lines.length - paddingLines * 2} matching lines`, RowType.Spacer)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!atEnd) {
|
||||
const start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : paddingLines);
|
||||
let skip = lines.length - paddingLines - 1;
|
||||
if (!atStart) {
|
||||
skip -= paddingLines;
|
||||
}
|
||||
if (skip > 0) {
|
||||
originalLineNumber += skip;
|
||||
currentLineNumber += skip;
|
||||
}
|
||||
|
||||
for (let i = start; i < lines.length; i++) {
|
||||
equalRows.push(createRow(lines[i], RowType.Equal));
|
||||
}
|
||||
}
|
||||
return equalRows;
|
||||
}
|
||||
|
||||
function createModifyRows(before: string, after: string): Row[] {
|
||||
const internalDiff = charDiff(before, after, true /* cleanup diff */);
|
||||
const deletionRows = [createRow('', RowType.Deletion)];
|
||||
const insertionRows = [createRow('', RowType.Addition)];
|
||||
|
||||
for (const token of internalDiff) {
|
||||
const text = token[1];
|
||||
const type = token[0];
|
||||
const className = type === Operation.Equal ? '' : 'inner-diff';
|
||||
const lines = text.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (i > 0 && type !== Operation.Insert) {
|
||||
deletionRows.push(createRow('', RowType.Deletion));
|
||||
}
|
||||
if (i > 0 && type !== Operation.Delete) {
|
||||
insertionRows.push(createRow('', RowType.Addition));
|
||||
}
|
||||
if (!lines[i]) {
|
||||
continue;
|
||||
}
|
||||
if (type !== Operation.Insert) {
|
||||
deletionRows[deletionRows.length - 1].tokens.push({ text: lines[i], className });
|
||||
}
|
||||
if (type !== Operation.Delete) {
|
||||
insertionRows[insertionRows.length - 1].tokens.push({ text: lines[i], className });
|
||||
}
|
||||
}
|
||||
}
|
||||
return deletionRows.concat(insertionRows);
|
||||
}
|
||||
|
||||
function createRow(text: string, type: RowType): Row {
|
||||
if (type === RowType.Addition) {
|
||||
currentLineNumber++;
|
||||
}
|
||||
if (type === RowType.Deletion) {
|
||||
originalLineNumber++;
|
||||
}
|
||||
if (type === RowType.Equal) {
|
||||
originalLineNumber++;
|
||||
currentLineNumber++;
|
||||
}
|
||||
|
||||
return {
|
||||
originalLineNumber,
|
||||
currentLineNumber,
|
||||
tokens: text ? [{ text, className: 'inner-diff' }] : [],
|
||||
type
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function documentMap(lines: readonly string[]): Map<number, number> {
|
||||
const map = new Map<number, number>();
|
||||
for (let pos = 0, lineNo = 0; lineNo < lines.length; lineNo++) {
|
||||
map.set(lineNo + 1, pos);
|
||||
pos += lines[lineNo].length + 1;
|
||||
}
|
||||
return map;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export { default as BackForwardButtons } from './BackForwardButtons.svelte';
|
||||
export { default as Login } from './Login.svelte';
|
||||
export { default as Breadcrumbs } from './Breadcrumbs.svelte';
|
||||
export { default as CodeViewer } from './CodeViewer.svelte';
|
||||
export { default as CodeViewer } from './CodeViewer';
|
||||
|
@ -12,7 +12,9 @@
|
||||
<span class="flex-grow" />
|
||||
<span>{formatDistanceToNow(processedResult.timestamp)} ago</span>
|
||||
</p>
|
||||
<div class="rounded-lg overflow-y-auto border border-zinc-700 bg-[#2F2F33] text-[#EBDBB2] drop-shadow-lg">
|
||||
<div
|
||||
class="overflow-y-auto rounded-lg border border-zinc-700 bg-[#2F2F33] text-[#EBDBB2] drop-shadow-lg"
|
||||
>
|
||||
{#each processedResult.hunks as hunk, i}
|
||||
{#if i > 0}
|
||||
<div class="border-b border-[#52525B]" />
|
||||
|
@ -5,6 +5,8 @@
|
||||
import { CodeViewer } from '$lib/components';
|
||||
import { IconPlayerPauseFilled, IconPlayerPlayFilled } from '$lib/components/icons';
|
||||
import { shortPath } from '$lib/paths';
|
||||
import { format } from 'date-fns';
|
||||
import type { Session } from '$lib/sessions';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@ -16,7 +18,7 @@
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let fileFilter = urlParams.get('file');
|
||||
|
||||
$: sessionDays = $sessions.reduce((group, session) => {
|
||||
$: sessionDays = $sessions.reduce((group: Record<string, Session[]>, session) => {
|
||||
const day = dateToYmd(new Date(session.meta.startTimestampMs));
|
||||
group[day] = group[day] ?? [];
|
||||
group[day].push(session);
|
||||
@ -100,11 +102,11 @@
|
||||
sessionFiles[sid][filepath] = '';
|
||||
sessionChapters[sid].editCount = sessionChapters[sid].edits.length;
|
||||
sessionChapters[sid].files[filepath] = deltas.length;
|
||||
sessionChapters[sid].firstDeltaTimestampMs = min(
|
||||
sessionChapters[sid].firstDeltaTimestampMs = Math.min(
|
||||
deltas.at(0)!.timestampMs,
|
||||
sessionChapters[sid].firstDeltaTimestampMs
|
||||
);
|
||||
sessionChapters[sid].lastDeltaTimestampMs = max(
|
||||
sessionChapters[sid].lastDeltaTimestampMs = Math.max(
|
||||
deltas.at(-1)!.timestampMs,
|
||||
sessionChapters[sid].lastDeltaTimestampMs
|
||||
);
|
||||
@ -142,11 +144,11 @@
|
||||
0
|
||||
),
|
||||
firstDeltaTimestampMs: Object.values(dayChapters).reduce(
|
||||
(acc, chapter) => min(acc, chapter.firstDeltaTimestampMs),
|
||||
(acc, chapter) => Math.min(acc, chapter.firstDeltaTimestampMs),
|
||||
9999999999999
|
||||
),
|
||||
lastDeltaTimestampMs: Object.values(dayChapters).reduce(
|
||||
(acc, chapter) => max(acc, chapter.lastDeltaTimestampMs),
|
||||
(acc, chapter) => Math.max(acc, chapter.lastDeltaTimestampMs),
|
||||
0
|
||||
)
|
||||
};
|
||||
@ -167,29 +169,12 @@
|
||||
});
|
||||
}
|
||||
|
||||
function max<T>(a: T, b: T): T {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
function min<T>(a: T, b: T): T {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function dateToYmd(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = ('0' + (date.getMonth() + 1)).slice(-2);
|
||||
const day = ('0' + date.getDate()).slice(-2);
|
||||
return `${year}-${month}-${day}`;
|
||||
return format(date, 'yyyy-MM-dd');
|
||||
}
|
||||
|
||||
function ymdToDate(dateString: string): Date {
|
||||
const [year, month, day] = dateString.split('-').map(Number);
|
||||
return new Date(year, month - 1, day);
|
||||
}
|
||||
|
||||
function ymdToDateLocale(dateString: string): string {
|
||||
let date = ymdToDate(dateString);
|
||||
return date.toLocaleString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });
|
||||
return new Date(dateString);
|
||||
}
|
||||
|
||||
function dateRange(chapter: VideoChapter) {
|
||||
@ -213,23 +198,8 @@
|
||||
return date.getDate();
|
||||
}
|
||||
|
||||
const month = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec'
|
||||
];
|
||||
function ymdToMonth(dateString: string): string {
|
||||
let date = ymdToDate(dateString);
|
||||
return month[date.getMonth()];
|
||||
return format(new Date(dateString), 'MMM');
|
||||
}
|
||||
|
||||
function selectDay(day: string) {
|
||||
@ -280,10 +250,15 @@
|
||||
}
|
||||
|
||||
function scrollToSession() {
|
||||
let sessionEl = document.getElementById('currentSession');
|
||||
const sessionEl = document.getElementById('currentSession');
|
||||
if (sessionEl) {
|
||||
sessionEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
const changedLines = document.getElementsByClassName('line-changed');
|
||||
if (changedLines.length > 0) {
|
||||
changedLines[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}
|
||||
|
||||
// player
|
||||
@ -311,6 +286,7 @@
|
||||
start({ direction, speed });
|
||||
};
|
||||
|
||||
// <!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
function handleKey() {}
|
||||
</script>
|
||||
|
||||
@ -328,28 +304,30 @@
|
||||
<div class="w-full p-2 font-mono text-lg">{fileFilter}</div>
|
||||
{/if}
|
||||
<div class="flex flex-row h-full w-full">
|
||||
<div id="left" class="w-20 flex-shrink-0 border-r border-zinc-700 p-2">
|
||||
{#each Object.entries(sessionDays) as [day, sessions]}
|
||||
{#if day == currentDay}
|
||||
<div
|
||||
class="mb-2 flex cursor-pointer flex-col rounded bg-zinc-600 border border-zinc-500 p-2 text-center text-white shadow"
|
||||
on:keydown={handleKey}
|
||||
on:click={selectDay(day)}
|
||||
>
|
||||
<div class="font-bold text-lg">{ymdToDay(day)}</div>
|
||||
<div class="">{ymdToMonth(day)}</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="mb-2 flex cursor-pointer flex-col rounded bg-zinc-700 border border-zinc-600 p-2 text-center shadow"
|
||||
on:keydown={handleKey}
|
||||
on:click={selectDay(day)}
|
||||
>
|
||||
<div class="font-bold text-lg">{ymdToDay(day)}</div>
|
||||
<div class="">{ymdToMonth(day)}</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
<div id="left" class="flex h-full w-20 flex-shrink-0 flex-col p-2">
|
||||
<div class="overflow-y-auto">
|
||||
{#each Object.entries(sessionDays) as [day, sessions]}
|
||||
{#if day == currentDay}
|
||||
<div
|
||||
class="mb-2 flex cursor-pointer flex-col rounded bg-zinc-600 border border-zinc-500 p-2 text-center text-white shadow"
|
||||
on:keydown={handleKey}
|
||||
on:click={selectDay(day)}
|
||||
>
|
||||
<div class="font-bold text-lg">{ymdToDay(day)}</div>
|
||||
<div class="">{ymdToMonth(day)}</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="mb-2 flex cursor-pointer flex-col rounded bg-zinc-700 border border-zinc-600 p-2 text-center shadow"
|
||||
on:keydown={handleKey}
|
||||
on:click={selectDay(day)}
|
||||
>
|
||||
<div class="font-bold text-lg">{ymdToDay(day)}</div>
|
||||
<div class="">{ymdToMonth(day)}</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="right" class="w-80 xl:w-96 flex-shrink-0 overflow-auto p-2">
|
||||
@ -389,7 +367,7 @@
|
||||
<div
|
||||
on:keydown={handleKey}
|
||||
on:click={() => {
|
||||
currentPlayerValue = max(
|
||||
currentPlayerValue = Math.max(
|
||||
dayPlaylist[currentDay].editOffsets[chapter.session],
|
||||
1
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user