mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-21 16:09:12 +03:00
fix: codeblock various fixes (#6550)
This commit is contained in:
parent
81fbac2a73
commit
24bd0e9f59
@ -1223,6 +1223,9 @@ dependencies:
|
||||
'@types/domhandler':
|
||||
specifier: ^2.4.5
|
||||
version: 2.4.5
|
||||
'@types/dompurify':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
'@types/email-addresses':
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
@ -1451,6 +1454,9 @@ dependencies:
|
||||
domhandler:
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3
|
||||
dompurify:
|
||||
specifier: ^3.1.6
|
||||
version: 3.1.6
|
||||
domutils:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0
|
||||
@ -8954,6 +8960,12 @@ packages:
|
||||
resolution: {integrity: sha512-lANhC2grmFG1gBac/8sDAKdIXx+TzAdkJIAjEOSMA+qW3297ybACEbacJnG15aNYfrzDO6fdcoouokqAKsy6aQ==}
|
||||
dev: false
|
||||
|
||||
/@types/dompurify@3.0.5:
|
||||
resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==}
|
||||
dependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
dev: false
|
||||
|
||||
/@types/domutils@1.7.8:
|
||||
resolution: {integrity: sha512-iZGboDV79ibrO3D625p9yD+VgmMDnyJocdIRJvu9Xz66R8SHfOY/XNgdjY5SFoFiLgILceVfSLt7IUhlk1Vhhg==}
|
||||
dependencies:
|
||||
@ -9534,6 +9546,10 @@ packages:
|
||||
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
|
||||
dev: false
|
||||
|
||||
/@types/trusted-types@2.0.7:
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
dev: false
|
||||
|
||||
/@types/unist@2.0.10:
|
||||
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
|
||||
dev: false
|
||||
@ -12958,6 +12974,10 @@ packages:
|
||||
domelementtype: 2.3.0
|
||||
dev: false
|
||||
|
||||
/dompurify@3.1.6:
|
||||
resolution: {integrity: sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==}
|
||||
dev: false
|
||||
|
||||
/domutils@1.5.1:
|
||||
resolution: {integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==}
|
||||
dependencies:
|
||||
@ -30472,7 +30492,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/presentation.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):
|
||||
resolution: {integrity: sha512-r+NP0EMgEeKbfaa4v8P1Iho0cfYqe9PhOBfV6SPd/9xnNPt42nK9Gu4r5so1LTolhEUzbFiKh7zSX1ADL5e/3g==, tarball: file:projects/presentation.tgz}
|
||||
resolution: {integrity: sha512-ryBht4b1zE/Ik6KZqDL/joAzt3968bkRbGZOt3x+pE929i7yCtHmlMC7W65Nlr1eglhC2JTSy2NiKTNv9yjcuw==, tarball: file:projects/presentation.tgz}
|
||||
id: file:projects/presentation.tgz
|
||||
name: '@rush-temp/presentation'
|
||||
version: 0.0.0
|
||||
@ -35107,17 +35127,19 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/ui.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):
|
||||
resolution: {integrity: sha512-WtSFJW84fNe+3lwzv2a8CRmyYIsY8B6HHJwg3YKLd7jWHF4T8hYIf892hAEv7kvh/vrZ7elq8E8b1znmCNd7Sw==, tarball: file:projects/ui.tgz}
|
||||
resolution: {integrity: sha512-umESBjjPj7ES3uF9YcS31H5dwqZtMATByltYeDc+XG+7ovD1SOM11UAjBpHCqj026RvvqcSjE8lAQP1zRXxCoA==, tarball: file:projects/ui.tgz}
|
||||
id: file:projects/ui.tgz
|
||||
name: '@rush-temp/ui'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@types/dompurify': 3.0.5
|
||||
'@types/jest': 29.5.12
|
||||
'@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)
|
||||
autolinker: 4.0.0
|
||||
date-fns: 2.30.0
|
||||
date-fns-tz: 2.0.0(date-fns@2.30.0)
|
||||
dompurify: 3.1.6
|
||||
emoji-regex: 10.3.0
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
|
||||
|
@ -48,6 +48,7 @@
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/view": "^0.6.13",
|
||||
"@hcengineering/text": "^0.6.5",
|
||||
"@hcengineering/diffview": "^0.6.0",
|
||||
"@hcengineering/uploader": "^0.6.0",
|
||||
"svelte": "^4.2.12",
|
||||
"@hcengineering/client": "^0.6.18",
|
||||
|
@ -0,0 +1,32 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import diffview from '@hcengineering/diffview'
|
||||
import { MarkupNode } from '@hcengineering/text'
|
||||
import { Component } from '@hcengineering/ui'
|
||||
|
||||
export let node: MarkupNode
|
||||
export let preview = false
|
||||
|
||||
$: language = node.attrs?.language
|
||||
$: content = node.content ?? []
|
||||
$: value = content.map((node) => node.text).join('/n')
|
||||
</script>
|
||||
|
||||
{#if node}
|
||||
<pre class="proseCodeBlock" style:margin={preview ? '0' : null}><code
|
||||
><Component is={diffview.component.Highlight} props={{ value, language }} /></code
|
||||
></pre>
|
||||
{/if}
|
@ -17,6 +17,7 @@
|
||||
import { AttrValue, MarkupNode, MarkupNodeType } from '@hcengineering/text'
|
||||
|
||||
import MarkupNodes from './Nodes.svelte'
|
||||
import CodeBlockNode from './CodeBlockNode.svelte'
|
||||
import ObjectNode from './ObjectNode.svelte'
|
||||
|
||||
export let node: MarkupNode
|
||||
@ -71,7 +72,7 @@
|
||||
<MarkupNodes {nodes} {preview} />
|
||||
</svelte:element>
|
||||
{:else if node.type === MarkupNodeType.code_block}
|
||||
<pre class="proseCodeBlock" style:margin={preview ? '0' : null}><code><MarkupNodes {nodes} {preview} /></code></pre>
|
||||
<CodeBlockNode {node} {preview} />
|
||||
{:else if node.type === MarkupNodeType.image}
|
||||
{@const src = toString(attrs.src)}
|
||||
{@const alt = toString(attrs.alt)}
|
||||
|
@ -77,7 +77,6 @@
|
||||
--text-editor-highlighted-node-delete-background-color: #F6DCDA;
|
||||
--text-editor-highlighted-node-delete-font-color: #54201C;
|
||||
|
||||
--text-editor-inline-code-color: #B02B46;
|
||||
--text-editor-table-marker-color: #bebebf;
|
||||
|
||||
--theme-clockface-sec-arrow: conic-gradient(at 50% -10px, rgba(255, 0, 0, 0), rgba(255, 0, 0, 0) 49%, #F47758 50%, rgba(255, 0, 0, 0) 51%, rgba(255, 0, 0, 0) 100%);
|
||||
|
@ -345,7 +345,6 @@ table.proseTable {
|
||||
margin: 0 1px;
|
||||
padding: 0 .25rem;
|
||||
font-family: var(--mono-font);
|
||||
color: var(--text-editor-inline-code-color);
|
||||
background-color: var(--theme-button-default);
|
||||
border: 1px solid var(--theme-button-border);
|
||||
border-radius: .25rem;
|
||||
|
@ -33,6 +33,7 @@
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"svelte-eslint-parser": "^0.33.1"
|
||||
@ -47,6 +48,7 @@
|
||||
"emoji-regex": "^10.1.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"date-fns-tz": "^2.0.0",
|
||||
"dompurify": "^3.1.6",
|
||||
"@hcengineering/analytics": "^0.6.0"
|
||||
},
|
||||
"repository": "https://github.com/hcenginneing/anticrm",
|
||||
|
23
packages/ui/src/components/Html.svelte
Normal file
23
packages/ui/src/components/Html.svelte
Normal file
@ -0,0 +1,23 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import dompurify from 'dompurify'
|
||||
|
||||
export let value: string
|
||||
|
||||
$: sanitized = dompurify.sanitize(value)
|
||||
</script>
|
||||
|
||||
{@html sanitized}
|
@ -96,6 +96,7 @@ export { default as DatePresenter } from './components/calendar/DatePresenter.sv
|
||||
export { default as DueDatePresenter } from './components/calendar/DueDatePresenter.svelte'
|
||||
export { default as DateTimePresenter } from './components/calendar/DateTimePresenter.svelte'
|
||||
export { default as TimeInputBox } from './components/calendar/TimeInputBox.svelte'
|
||||
export { default as Html } from './components/Html.svelte'
|
||||
export { default as StylishEdit } from './components/StylishEdit.svelte'
|
||||
export { default as Grid } from './components/Grid.svelte'
|
||||
export { default as Row } from './components/Row.svelte'
|
||||
|
26
plugins/diffview-resources/src/components/Highlight.svelte
Normal file
26
plugins/diffview-resources/src/components/Highlight.svelte
Normal file
@ -0,0 +1,26 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Html } from '@hcengineering/ui'
|
||||
|
||||
import { highlightText } from '../highlight'
|
||||
|
||||
export let value: string
|
||||
export let language: string | undefined = undefined
|
||||
|
||||
$: highlighted = highlightText(value, { language })
|
||||
</script>
|
||||
|
||||
<Html value={highlighted} />
|
@ -19,18 +19,18 @@ import { hljsDefineSvelte } from './languages/svelte-hljs'
|
||||
hljs.registerLanguage('svelte', hljsDefineSvelte)
|
||||
|
||||
export interface HighlightOptions {
|
||||
language: string
|
||||
language: string | undefined
|
||||
}
|
||||
|
||||
export function highlightText (text: string, options: HighlightOptions): string {
|
||||
// We should always use highlighter because it sanitizes the input
|
||||
// We have to always use highlighter to ensure that the input is sanitized
|
||||
const validLanguage = options.language !== '' && hljs.getLanguage(options.language) !== undefined
|
||||
const language = validLanguage ? options.language : 'text'
|
||||
const { language } = options
|
||||
const validLanguage = language !== undefined && hljs.getLanguage(language) !== undefined
|
||||
|
||||
const { value: highlighted } = hljs.highlight(text, { language })
|
||||
const normalized = normalizeHighlightTags(highlighted)
|
||||
return normalized
|
||||
const { value: highlighted } = validLanguage ? hljs.highlight(text, { language }) : hljs.highlightAuto(text)
|
||||
|
||||
return normalizeHighlightTags(highlighted)
|
||||
}
|
||||
|
||||
export function highlightLines (lines: string[], options: HighlightOptions): string[] {
|
||||
|
@ -15,10 +15,12 @@
|
||||
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
import DiffView from './components/DiffView.svelte'
|
||||
import Highlight from './components/Highlight.svelte'
|
||||
import InlineDiffView from './components/InlineDiffView.svelte'
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
DiffView,
|
||||
InlineDiffView
|
||||
InlineDiffView,
|
||||
Highlight
|
||||
}
|
||||
})
|
||||
|
@ -48,7 +48,8 @@ export interface DiffFileId {
|
||||
export default plugin(diffviewId, {
|
||||
component: {
|
||||
DiffView: '' as AnyComponent,
|
||||
InlineDiffView: '' as AnyComponent
|
||||
InlineDiffView: '' as AnyComponent,
|
||||
Highlight: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
ViewMode: '' as IntlString,
|
||||
|
@ -13,12 +13,13 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { codeBlockOptions } from '@hcengineering/text'
|
||||
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'
|
||||
import { common, createLowlight } from 'lowlight'
|
||||
|
||||
type Lowlight = ReturnType<typeof createLowlight>
|
||||
|
||||
@ -26,14 +27,19 @@ const chevronSvg = `<svg width="16" height="16" viewBox="0 0 32 32" fill="curren
|
||||
<path d="M16 22L6 12L7.4 10.6L16 19.2L24.6 10.6L26 12L16 22Z" />
|
||||
</svg>`
|
||||
|
||||
export const CodeBlockExtension = CodeBlockLowlight.extend<CodeBlockLowlightOptions>({
|
||||
export const codeBlockHighlightOptions: CodeBlockLowlightOptions = {
|
||||
...codeBlockOptions,
|
||||
lowlight: createLowlight(common)
|
||||
}
|
||||
|
||||
export const CodeBlockHighlighExtension = CodeBlockLowlight.extend<CodeBlockLowlightOptions>({
|
||||
addProseMirrorPlugins () {
|
||||
return [...(this.parent?.() ?? []), LanguageSelector(this.options)]
|
||||
}
|
||||
})
|
||||
|
||||
export function LanguageSelector (options: CodeBlockLowlightOptions): Plugin {
|
||||
return new Plugin({
|
||||
return new Plugin<DecorationSet>({
|
||||
key: new PluginKey('codeblock-language-selector'),
|
||||
props: {
|
||||
decorations (state) {
|
||||
@ -41,13 +47,14 @@ export function LanguageSelector (options: CodeBlockLowlightOptions): Plugin {
|
||||
}
|
||||
},
|
||||
state: {
|
||||
init () {
|
||||
return DecorationSet.empty
|
||||
init (config, state) {
|
||||
return createDecorations(state.doc, options)
|
||||
},
|
||||
apply (tr, prev) {
|
||||
if (tr.docChanged) {
|
||||
return createDecorations(tr.doc, options)
|
||||
}
|
||||
|
||||
return prev
|
||||
}
|
||||
}
|
||||
@ -84,7 +91,7 @@ function createDecorations (doc: ProseMirrorNode, options: CodeBlockLowlightOpti
|
||||
|
||||
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.className = 'antiButton link-bordered small sh-no-shape bs-none gap-medium iconR'
|
||||
button.style.position = 'absolute'
|
||||
button.style.top = '0.375rem'
|
||||
button.style.right = '0.375rem'
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { codeBlockOptions, codeOptions } from '@hcengineering/text'
|
||||
import { codeOptions } from '@hcengineering/text'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import { type Editor, Extension } from '@tiptap/core'
|
||||
import type { CodeOptions } from '@tiptap/extension-code'
|
||||
@ -25,10 +25,9 @@ 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'
|
||||
import { CodeBlockHighlighExtension, codeBlockHighlightOptions } from '../components/extension/codeblock'
|
||||
|
||||
export interface DefaultKitOptions {
|
||||
codeBlock?: Partial<CodeBlockOptions> | false
|
||||
@ -66,10 +65,7 @@ export const DefaultKit = Extension.create<DefaultKitOptions>({
|
||||
openOnClick: true,
|
||||
HTMLAttributes: { class: 'cursor-pointer', rel: 'noopener noreferrer', target: '_blank' }
|
||||
}),
|
||||
CodeBlockExtension.configure({
|
||||
...codeBlockOptions,
|
||||
lowlight: createLowlight(common)
|
||||
})
|
||||
CodeBlockHighlighExtension.configure(codeBlockHighlightOptions)
|
||||
]
|
||||
}
|
||||
})
|
||||
|
@ -15,7 +15,7 @@
|
||||
import { type Class, type Doc, type Ref, type Space } from '@hcengineering/core'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { getBlobRef, getClient } from '@hcengineering/presentation'
|
||||
import { CodeBlockExtension, codeBlockOptions, CodeExtension, codeOptions } from '@hcengineering/text'
|
||||
import { CodeExtension, codeOptions } from '@hcengineering/text'
|
||||
import textEditor, { type ActionContext, type ExtensionCreator, type TextEditorMode } from '@hcengineering/text-editor'
|
||||
import { type AnyExtension, type Editor, Extension } from '@tiptap/core'
|
||||
import { type Level } from '@tiptap/extension-heading'
|
||||
@ -23,6 +23,7 @@ import ListKeymap from '@tiptap/extension-list-keymap'
|
||||
import TableHeader from '@tiptap/extension-table-header'
|
||||
import 'prosemirror-codemark/dist/codemark.css'
|
||||
|
||||
import { CodeBlockHighlighExtension, codeBlockHighlightOptions } from '../components/extension/codeblock'
|
||||
import { NoteExtension, type NoteOptions } from '../components/extension/note'
|
||||
import { FileExtension, type FileOptions } from '../components/extension/fileExt'
|
||||
import { HardBreakExtension } from '../components/extension/hardBreak'
|
||||
@ -171,7 +172,7 @@ async function buildEditorKit (): Promise<Extension<EditorKitOptions, any>> {
|
||||
}
|
||||
})
|
||||
],
|
||||
[200, CodeBlockExtension.configure(codeBlockOptions)],
|
||||
[200, CodeBlockHighlighExtension.configure(codeBlockHighlightOptions)],
|
||||
[210, CodeExtension.configure(codeOptions)],
|
||||
[220, HardBreakExtension.configure({ shortcuts: mode })]
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user