feat: codeblock syntax highlight (#6505)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov 2024-09-10 00:32:22 +07:00 committed by GitHub
parent 3bc46c71f7
commit 92b20ad47f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 416 additions and 32 deletions

View File

@ -154,7 +154,7 @@ dependencies:
version: file:projects/collaboration.tgz(esbuild@0.20.1)(ts-node@10.9.2)
'@rush-temp/collaborator':
specifier: file:./projects/collaborator.tgz
version: file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(prosemirror-model@1.19.4)(utf-8-validate@6.0.4)(y-protocols@1.0.6)
version: file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6)
'@rush-temp/collaborator-client':
specifier: file:./projects/collaborator-client.tgz
version: file:projects/collaborator-client.tgz(ts-node@10.9.2)
@ -997,7 +997,7 @@ dependencies:
version: file:projects/text-editor-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2)
'@rush-temp/text-editor-resources':
specifier: file:./projects/text-editor-resources.tgz
version: file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(prosemirror-model@1.19.4)(ts-node@10.9.2)(utf-8-validate@6.0.4)
version: file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(highlight.js@11.8.0)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)(utf-8-validate@6.0.4)
'@rush-temp/theme':
specifier: file:./projects/theme.tgz
version: file:projects/theme.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)
@ -1127,6 +1127,9 @@ dependencies:
'@tiptap/extension-code-block':
specifier: ^2.2.4
version: 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)
'@tiptap/extension-code-block-lowlight':
specifier: ^2.2.4
version: 2.6.6(@tiptap/core@2.2.4)(@tiptap/extension-code-block@2.2.4)(@tiptap/pm@2.2.4)(highlight.js@11.8.0)(lowlight@3.1.0)
'@tiptap/extension-collaboration':
specifier: ^2.2.4
version: 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)(y-prosemirror@1.2.2)
@ -1634,6 +1637,9 @@ dependencies:
livekit-server-sdk:
specifier: ^2.0.10
version: 2.6.0
lowlight:
specifier: ^3.1.0
version: 3.1.0
mammoth:
specifier: ^1.6.0
version: 1.8.0
@ -1720,10 +1726,7 @@ dependencies:
version: 3.2.2(prettier@3.2.5)(svelte@4.2.12)
prosemirror-codemark:
specifier: ^0.4.2
version: 0.4.2(prosemirror-model@1.19.4)
prosemirror-model:
specifier: ^1.19.4
version: 1.19.4
version: 0.4.2
puppeteer:
specifier: ^22.6.1
version: 22.14.0(bufferutil@4.0.8)(typescript@5.3.3)(utf-8-validate@6.0.4)
@ -1867,7 +1870,7 @@ dependencies:
version: 9.0.12(yjs@13.6.12)
y-prosemirror:
specifier: ^1.2.1
version: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12)
version: 1.2.2(y-protocols@1.0.6)(yjs@13.6.12)
y-protocols:
specifier: ^1.0.6
version: 1.0.6(yjs@13.6.12)
@ -4833,7 +4836,7 @@ packages:
'@tiptap/core': 2.2.4(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
'@tiptap/starter-kit': 2.2.4(@tiptap/pm@2.2.4)
y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12)
y-prosemirror: 1.2.2(y-protocols@1.0.6)(yjs@13.6.12)
yjs: 13.6.12
dev: false
@ -8390,6 +8393,22 @@ packages:
'@tiptap/core': 2.2.4(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-code-block-lowlight@2.6.6(@tiptap/core@2.2.4)(@tiptap/extension-code-block@2.2.4)(@tiptap/pm@2.2.4)(highlight.js@11.8.0)(lowlight@3.1.0):
resolution: {integrity: sha512-GXzuQGKxxOmozzvwBEKdEnX1fv9R8qt9Q4Q+j3Itc+um7nYNKHDT1xNIk1BQUeu8Mr6fQVFgCu3FDybsRp9Ncw==}
peerDependencies:
'@tiptap/core': ^2.6.6
'@tiptap/extension-code-block': ^2.6.6
'@tiptap/pm': ^2.6.6
highlight.js: ^11
lowlight: ^2 || ^3
dependencies:
'@tiptap/core': 2.2.4(@tiptap/pm@2.2.4)
'@tiptap/extension-code-block': 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
highlight.js: 11.8.0
lowlight: 3.1.0
dev: false
/@tiptap/extension-code-block@2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-h6WV9TmaBEZmvqe1ezMR83DhCPUap6P2mSR5pwVk0WVq6rvZjfgU0iF3EetBJOeDgPlz7cNe2NMDfVb1nGTM/g==}
peerDependencies:
@ -8415,7 +8434,7 @@ packages:
y-prosemirror: ^1.2.1
dependencies:
'@tiptap/core': 2.2.4(@tiptap/pm@2.2.4)
y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12)
y-prosemirror: 1.2.2(y-protocols@1.0.6)(yjs@13.6.12)
dev: false
/@tiptap/extension-collaboration@2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)(y-prosemirror@1.2.2):
@ -8427,7 +8446,7 @@ packages:
dependencies:
'@tiptap/core': 2.2.4(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12)
y-prosemirror: 1.2.2(y-protocols@1.0.6)(yjs@13.6.12)
dev: false
/@tiptap/extension-document@2.2.4(@tiptap/core@2.2.4):
@ -9010,6 +9029,12 @@ packages:
'@types/node': 20.11.19
dev: false
/@types/hast@3.0.4:
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
dependencies:
'@types/unist': 2.0.10
dev: false
/@types/html-minifier-terser@6.1.0:
resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==}
dev: false
@ -12691,6 +12716,12 @@ packages:
- supports-color
dev: false
/devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
dependencies:
dequal: 2.0.3
dev: false
/devtools-protocol@0.0.1312386:
resolution: {integrity: sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==}
dev: false
@ -15549,7 +15580,6 @@ packages:
engines: {node: '>=12.0.0'}
requiresBuild: true
dev: false
optional: true
/hogan.js@3.0.2:
resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==}
@ -17917,6 +17947,14 @@ packages:
engines: {node: '>=8'}
dev: false
/lowlight@3.1.0:
resolution: {integrity: sha512-CEbNVoSikAxwDMDPjXlqlFYiZLkDJHwyGu/MfOsJnF3d7f3tds5J3z8s/l9TMXhzfsJCCJEAsD78842mwmg0PQ==}
dependencies:
'@types/hast': 3.0.4
devlop: 1.1.0
highlight.js: 11.9.0
dev: false
/lru-cache@10.2.0:
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
engines: {node: 14 || >=16.14}
@ -19901,15 +19939,13 @@ packages:
prosemirror-transform: 1.8.0
dev: false
/prosemirror-codemark@0.4.2(prosemirror-model@1.19.4):
/prosemirror-codemark@0.4.2:
resolution: {integrity: sha512-4n+PnGQToa/vTjn0OiivUvE8/moLtguUAfry8UA4Q8p47MhqT2Qpf2zBLustX5Upi4mSp3z1ZYBqLLovZC6abA==}
peerDependencies:
prosemirror-inputrules: ^1.2.0
prosemirror-model: ^1.18.1
prosemirror-state: ^1.4.1
prosemirror-view: ^1.26.2
dependencies:
prosemirror-model: 1.19.4
dev: false
/prosemirror-collab@1.3.1:
@ -23971,7 +24007,7 @@ packages:
dev: false
optional: true
/y-prosemirror@1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12):
/y-prosemirror@1.2.2(y-protocols@1.0.6)(yjs@13.6.12):
resolution: {integrity: sha512-hHdnIAhfa8mIoLWtTkMDb6RBzN3lye1QVkaZwVm58sledAA1zTl+yyEtgkrY/sdH6SaQL0rsLj61zHjgr5D0HQ==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
peerDependencies:
@ -23982,7 +24018,6 @@ packages:
yjs: ^13.5.38
dependencies:
lib0: 0.2.89
prosemirror-model: 1.19.4
y-protocols: 1.0.6(yjs@13.6.12)
yjs: 13.6.12
dev: false
@ -25363,7 +25398,7 @@ packages:
- ts-node
dev: false
file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(prosemirror-model@1.19.4)(utf-8-validate@6.0.4)(y-protocols@1.0.6):
file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6):
resolution: {integrity: sha512-svaotn6ykOZH+tModYWz/Y3hct1yHDm/enwC9NCpH17VVNALvRIqUx8F2DWvQV5mUwJYxLcKgdsBqNPB4MjMag==, tarball: file:projects/collaborator.tgz}
id: file:projects/collaborator.tgz
name: '@rush-temp/collaborator'
@ -25383,7 +25418,6 @@ packages:
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
body-parser: 1.20.2
compression: 1.7.4
cors: 2.8.5
cross-env: 7.0.3
esbuild: 0.20.1
@ -25400,7 +25434,7 @@ packages:
ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.3.3)
typescript: 5.3.3
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12)
y-prosemirror: 1.2.2(y-protocols@1.0.6)(yjs@13.6.12)
yjs: 13.6.12
transitivePeerDependencies:
- '@aws-sdk/credential-providers'
@ -30023,7 +30057,7 @@ packages:
dev: false
file:projects/pod-telegram-bot.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4):
resolution: {integrity: sha512-nHK8VvEEKMela0QxFYFIFHgIjnQj8A01bct7tu+T54i9PP0bgk1zuS+6neAPX45/su+hRG+oiXjdXnuAPMYv3w==, tarball: file:projects/pod-telegram-bot.tgz}
resolution: {integrity: sha512-iYHC3LQQX/oFuRh+llDGC7Sj4smIcYENpabAX4HN+Gr0bmN8A2dKK69P7ngJ8ozhjAIUpLKwPKmCSwzyz+ZRpw==, tarball: file:projects/pod-telegram-bot.tgz}
id: file:projects/pod-telegram-bot.tgz
name: '@rush-temp/pod-telegram-bot'
version: 0.0.0
@ -34036,7 +34070,7 @@ packages:
dev: false
file:projects/telegram.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2):
resolution: {integrity: sha512-e+PHTbBJ8rRCfiVXlZhoP/ZEQMabsxRySsXUYs+AEMTve0J/p/GiVp85veIxitGNOInRyfGkUakfAPsN7YxrxA==, tarball: file:projects/telegram.tgz}
resolution: {integrity: sha512-pONnGW65uWwdCHtwvxsANuytteLIKC07izzOVXL58qFKs5BQMZu6rOaT3RzbLvJIu+0FMgEIeyZtxABS8NEEMw==, tarball: file:projects/telegram.tgz}
id: file:projects/telegram.tgz
name: '@rush-temp/telegram'
version: 0.0.0
@ -34229,8 +34263,8 @@ packages:
- ts-node
dev: false
file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(prosemirror-model@1.19.4)(ts-node@10.9.2)(utf-8-validate@6.0.4):
resolution: {integrity: sha512-Qyx6oJpj8FfTxV0rqKs4gmH48kx9bYYDHPpdA36B5x71Mtss/cPWvq/fEgzVyNQKhH1qaqzWv9BnpKHCcpBNzQ==, tarball: file:projects/text-editor-resources.tgz}
file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(highlight.js@11.8.0)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)(utf-8-validate@6.0.4):
resolution: {integrity: sha512-huPldI0/rDujfU2CwuJf+PLI/RUu73Ry7zZvomQt2HJHL+yxwIBXr/t4T5PIEblfnf6KpmiqMQ1dEbjRQEuGGw==, tarball: file:projects/text-editor-resources.tgz}
id: file:projects/text-editor-resources.tgz
name: '@rush-temp/text-editor-resources'
version: 0.0.0
@ -34240,6 +34274,7 @@ packages:
'@tiptap/extension-bubble-menu': 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)
'@tiptap/extension-code': 2.2.4(@tiptap/core@2.2.4)
'@tiptap/extension-code-block': 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)
'@tiptap/extension-code-block-lowlight': 2.6.6(@tiptap/core@2.2.4)(@tiptap/extension-code-block@2.2.4)(@tiptap/pm@2.2.4)(highlight.js@11.8.0)(lowlight@3.1.0)
'@tiptap/extension-collaboration': 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)(y-prosemirror@1.2.2)
'@tiptap/extension-collaboration-cursor': 2.2.4(@tiptap/core@2.2.4)(y-prosemirror@1.2.2)
'@tiptap/extension-hard-break': 2.2.4(@tiptap/core@2.2.4)
@ -34270,9 +34305,10 @@ packages:
fast-equals: 5.0.1
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
lib0: 0.2.89
lowlight: 3.1.0
prettier: 3.2.5
prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12)
prosemirror-codemark: 0.4.2(prosemirror-model@1.19.4)
prosemirror-codemark: 0.4.2
rfc6902: 5.1.1
sass: 1.71.1
slugify: 1.6.6
@ -34284,7 +34320,7 @@ packages:
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
y-indexeddb: 9.0.12(yjs@13.6.12)
y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12)
y-prosemirror: 1.2.2(y-protocols@1.0.6)(yjs@13.6.12)
y-protocols: 1.0.6(yjs@13.6.12)
y-websocket: 2.0.4(yjs@13.6.12)
yjs: 13.6.12
@ -34297,6 +34333,7 @@ packages:
- bufferutil
- coffeescript
- esbuild
- highlight.js
- less
- node-notifier
- postcss
@ -34351,7 +34388,7 @@ packages:
dev: false
file:projects/text.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4)(y-protocols@1.0.6):
resolution: {integrity: sha512-ZQInjd9DHWVEXBeaR3wy+jQjDfHYnvJGKBiRWjr6i1AKIl/GNdDG/3G8QTQpu6O637ME04TzDGRbe3/9KNY+0w==, tarball: file:projects/text.tgz}
resolution: {integrity: sha512-ScXfcb1Mfk2cvle6rh4X5/3b6/cpenpUGR+U5c/kkbaZ6H3TUg1jsOh/2C6Hy7z9JWu3oIxlLpxEnd9lRXTgbA==, tarball: file:projects/text.tgz}
id: file:projects/text.tgz
name: '@rush-temp/text'
version: 0.0.0
@ -34391,11 +34428,11 @@ packages:
jest-environment-jsdom: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
markdown-it: 14.0.0
prettier: 3.2.5
prosemirror-codemark: 0.4.2(prosemirror-model@1.19.4)
prosemirror-codemark: 0.4.2
prosemirror-model: 1.19.4
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12)
y-prosemirror: 1.2.2(y-protocols@1.0.6)(yjs@13.6.12)
yjs: 13.6.12
transitivePeerDependencies:
- '@babel/core'
@ -34408,6 +34445,7 @@ packages:
- esbuild
- node-notifier
- prosemirror-inputrules
- prosemirror-model
- prosemirror-state
- prosemirror-view
- supports-color

View File

@ -61,7 +61,6 @@
"@tiptap/extension-code": "^2.2.4",
"@tiptap/extension-underline": "^2.2.4",
"@tiptap/suggestion": "^2.2.4",
"prosemirror-model": "^1.19.4",
"prosemirror-codemark": "^0.4.2",
"markdown-it": "^14.0.0",
"fast-equals": "^5.0.1",

View File

@ -15,7 +15,7 @@
import { Markup } from '@hcengineering/core'
import { Extensions, getSchema } from '@tiptap/core'
import { Node, Schema } from 'prosemirror-model'
import { Node, Schema } from '@tiptap/pm/model'
import { prosemirrorJSONToYDoc, prosemirrorToYDoc, yDocToProsemirrorJSON } from 'y-prosemirror'
import { Doc as YDoc, applyUpdate, encodeStateAsUpdate, XmlElement as YXmlElement, XmlText as YXmlText } from 'yjs'
import { defaultExtensions } from './extensions'

View File

@ -348,6 +348,14 @@
}
}
.theme-dark {
@import './github-dark.scss';
}
.theme-light {
@import './github-light.scss';
}
.theme-dark .text-editor-note-marker {
background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20256%20256%22%20id%3D%22note%22%3E%3Crect%20width%3D%22256%22%20height%3D%22256%22%20fill%3D%22none%22%3E%3C%2Frect%3E%3Cline%20x1%3D%2296%22%20x2%3D%22160%22%20y1%3D%2296%22%20y2%3D%2296%22%20fill%3D%22none%22%20stroke%3D%22%23FDFDF7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%228%22%3E%3C%2Fline%3E%3Cline%20x1%3D%2296%22%20x2%3D%22160%22%20y1%3D%22128%22%20y2%3D%22128%22%20fill%3D%22none%22%20stroke%3D%22%23FDFDF7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%228%22%3E%3C%2Fline%3E%3Cline%20x1%3D%2296%22%20x2%3D%22128%22%20y1%3D%22160%22%20y2%3D%22160%22%20fill%3D%22none%22%20stroke%3D%22%23FDFDF7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%228%22%3E%3C%2Fline%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23FDFDF7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%228%22%20d%3D%22M156.68629%2C216H48a8%2C8%2C0%2C0%2C1-8-8V48a8%2C8%2C0%2C0%2C1%2C8-8H208a8%2C8%2C0%2C0%2C1%2C8%2C8V156.68629a8%2C8%2C0%2C0%2C1-2.34315%2C5.65686l-51.3137%2C51.3137A8%2C8%2C0%2C0%2C1%2C156.68629%2C216Z%22%3E%3C%2Fpath%3E%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22%23FDFDF7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%228%22%20points%3D%22215.277%20159.992%20160%20159.992%20160%20215.272%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E');
}

View File

@ -0,0 +1,94 @@
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
} /*!
Theme: GitHub Dark
Description: Dark theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-dark
Current colors taken from GitHub's CSS
*/
.hljs {
color: #c9d1d9;
// background: #0d1117;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: #ff7b72;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: #d2a8ff;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id,
.hljs-variable {
color: #79c0ff;
}
.hljs-meta .hljs-string,
.hljs-regexp,
.hljs-string {
color: #a5d6ff;
}
.hljs-built_in,
.hljs-symbol {
color: #ffa657;
}
.hljs-code,
.hljs-comment,
.hljs-formula {
color: #8b949e;
}
.hljs-name,
.hljs-quote,
.hljs-selector-pseudo,
.hljs-selector-tag {
color: #7ee787;
}
.hljs-subst {
color: #c9d1d9;
}
.hljs-section {
color: #1f6feb;
font-weight: 700;
}
.hljs-bullet {
color: #f2cc60;
}
.hljs-emphasis {
color: #c9d1d9;
font-style: italic;
}
.hljs-strong {
color: #c9d1d9;
font-weight: 700;
}
.hljs-addition {
color: #aff5b4;
background-color: #033a16;
}
.hljs-deletion {
color: #ffdcd7;
background-color: #67060c;
}

View File

@ -0,0 +1,94 @@
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
} /*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/
.hljs {
color: #24292e;
// background: #fff;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: #d73a49;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: #6f42c1;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id,
.hljs-variable {
color: #005cc5;
}
.hljs-meta .hljs-string,
.hljs-regexp,
.hljs-string {
color: #032f62;
}
.hljs-built_in,
.hljs-symbol {
color: #e36209;
}
.hljs-code,
.hljs-comment,
.hljs-formula {
color: #6a737d;
}
.hljs-name,
.hljs-quote,
.hljs-selector-pseudo,
.hljs-selector-tag {
color: #22863a;
}
.hljs-subst {
color: #24292e;
}
.hljs-section {
color: #005cc5;
font-weight: 700;
}
.hljs-bullet {
color: #735c0f;
}
.hljs-emphasis {
color: #24292e;
font-style: italic;
}
.hljs-strong {
color: #24292e;
font-weight: 700;
}
.hljs-addition {
color: #22863a;
background-color: #f0fff4;
}
.hljs-deletion {
color: #b31d28;
background-color: #ffeef0;
}

View File

@ -363,6 +363,7 @@ table.proseTable {
}
pre.proseCodeBlock {
position: relative;
white-space: pre-wrap;
word-wrap: break-word;
word-break: normal;
@ -383,3 +384,11 @@ pre.proseCodeBlock > pre.proseCode {
h1, h2, h3, p, pre, code { cursor: text; }
p div { cursor: auto; }
}
.theme-dark {
@import './github-dark.scss';
}
.theme-light {
@import './github-light.scss';
}

View File

@ -50,6 +50,7 @@
"@hcengineering/collaborator-client": "^0.6.4",
"@tiptap/core": "^2.2.4",
"@tiptap/pm": "^2.2.4",
"@tiptap/extension-code-block-lowlight": "^2.2.4",
"@tiptap/extension-collaboration": "^2.2.4",
"@tiptap/extension-collaboration-cursor": "^2.2.4",
"@tiptap/extension-placeholder": "^2.2.4",
@ -79,6 +80,7 @@
"diff": "^5.1.0",
"slugify": "^1.6.6",
"lib0": "^0.2.88",
"y-indexeddb": "^9.0.12"
"y-indexeddb": "^9.0.12",
"lowlight": "^3.1.0"
}
}

View File

@ -0,0 +1,134 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { DropdownLabelsPopup, getEventPositionElement, showPopup } from '@hcengineering/ui'
import { type CodeBlockLowlightOptions, CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
import { type Node as ProseMirrorNode } from '@tiptap/pm/model'
import { Plugin, PluginKey } from '@tiptap/pm/state'
import { Decoration, DecorationSet, type EditorView } from '@tiptap/pm/view'
import { type createLowlight } from 'lowlight'
type Lowlight = ReturnType<typeof createLowlight>
const chevronSvg = `<svg width="16" height="16" viewBox="0 0 32 32" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M16 22L6 12L7.4 10.6L16 19.2L24.6 10.6L26 12L16 22Z" />
</svg>`
export const CodeBlockExtension = CodeBlockLowlight.extend<CodeBlockLowlightOptions>({
addProseMirrorPlugins () {
return [...(this.parent?.() ?? []), LanguageSelector(this.options)]
}
})
export function LanguageSelector (options: CodeBlockLowlightOptions): Plugin {
return new Plugin({
key: new PluginKey('codeblock-language-selector'),
props: {
decorations (state) {
return this.getState(state)
}
},
state: {
init () {
return DecorationSet.empty
},
apply (tr, prev) {
if (tr.docChanged) {
return createDecorations(tr.doc, options)
}
return prev
}
}
})
}
function createDecorations (doc: ProseMirrorNode, options: CodeBlockLowlightOptions): DecorationSet {
const decorations: Decoration[] = []
doc.descendants((node, pos) => {
if (node.type.name === CodeBlockLowlight.name) {
decorations.push(
Decoration.widget(pos + node.nodeSize - 1, (view) => {
const button = createLangButton(node.attrs.language)
if (view.editable) {
button.addEventListener('click', (e) => {
e.preventDefault()
e.stopPropagation()
handleLangButtonClick(e, node, pos, view, options)
})
} else {
button.disabled = true
}
return button
})
)
}
})
return DecorationSet.create(doc, decorations)
}
function createLangButton (language: string | null): HTMLButtonElement {
const button = document.createElement('button')
button.className = 'antiButton ghost small sh-no-shape bs-none gap-medium iconR'
button.style.position = 'absolute'
button.style.top = '0.375rem'
button.style.right = '0.375rem'
button.style.zIndex = '1'
const label = document.createElement('span')
label.className = 'overflow-label label disabled mr-2'
label.textContent = language ?? 'auto'
const icon = document.createElement('div')
icon.innerHTML = chevronSvg
button.append(label, icon)
return button
}
function handleLangButtonClick (
evt: MouseEvent,
node: ProseMirrorNode,
pos: number,
view: EditorView,
options: CodeBlockLowlightOptions
): void {
const language = node.attrs.language
const lowlight: Lowlight = options.lowlight
const items = lowlight.listLanguages().map((language) => ({
id: language,
label: language
}))
showPopup(
DropdownLabelsPopup,
{
items,
selected: language
},
getEventPositionElement(evt),
(result) => {
if (result != null) {
const tr = view.state.tr.setNodeAttribute(pos, 'language', result)
view.dispatch(tr)
}
}
)
}

View File

@ -25,8 +25,10 @@ import Link from '@tiptap/extension-link'
import Typography from '@tiptap/extension-typography'
import Underline from '@tiptap/extension-underline'
import StarterKit from '@tiptap/starter-kit'
import { common, createLowlight } from 'lowlight'
import LinkPopup from '../components/LinkPopup.svelte'
import { CodeBlockExtension } from '../components/extension/codeblock'
export interface DefaultKitOptions {
codeBlock?: Partial<CodeBlockOptions> | false
@ -50,7 +52,7 @@ export const DefaultKit = Extension.create<DefaultKitOptions>({
}
},
code: this.options.code ?? codeOptions,
codeBlock: this.options.codeBlock ?? codeBlockOptions,
codeBlock: false,
hardBreak: this.options.hardBreak,
heading: this.options.heading,
history: this.options.history
@ -63,6 +65,10 @@ export const DefaultKit = Extension.create<DefaultKitOptions>({
Link.configure({
openOnClick: true,
HTMLAttributes: { class: 'cursor-pointer', rel: 'noopener noreferrer', target: '_blank' }
}),
CodeBlockExtension.configure({
...codeBlockOptions,
lowlight: createLowlight(common)
})
]
}