mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-22 16:27:22 +00:00
UBERF-4795 Make editor extensions kit configurable (#4329)
Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
parent
e6ae8703b3
commit
b84aa01534
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@ -90,6 +90,7 @@
|
||||
"SECRET": "secret",
|
||||
"METRICS_CONSOLE": "true",
|
||||
"TRANSACTOR_URL": "ws://localhost:3333",
|
||||
"UPLOAD_URL": "/files",
|
||||
"MONGO_URL": "mongodb://localhost:27017",
|
||||
"MINIO_ACCESS_KEY": "minioadmin",
|
||||
"MINIO_SECRET_KEY": "minioadmin",
|
||||
|
@ -707,6 +707,9 @@ dependencies:
|
||||
'@tiptap/extension-highlight':
|
||||
specifier: ^2.1.12
|
||||
version: 2.1.12(@tiptap/core@2.1.12)
|
||||
'@tiptap/extension-history':
|
||||
specifier: ^2.1.12
|
||||
version: 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
'@tiptap/extension-link':
|
||||
specifier: ^2.1.12
|
||||
version: 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
@ -23650,7 +23653,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/text-editor.tgz(@types/node@16.11.68)(bufferutil@4.0.7)(esbuild@0.16.17)(postcss-load-config@4.0.1)(postcss@8.4.31)(prosemirror-model@1.19.3)(ts-node@10.9.1):
|
||||
resolution: {integrity: sha512-OTmONKS1vIIvQHrDVFHNsaVZopEiXGbimLMlkHpMj1sznAdusJITPsPbj9Q3YLvIc8VXNSlQeRzN2QlkwW2IYg==, tarball: file:projects/text-editor.tgz}
|
||||
resolution: {integrity: sha512-QgUDUW3v5zIVHETcfC/UsoZ8EPeauNcSyGO+I/J6rXLG2ndtNKIWRdl7Herj480/Q6JjPjWmm5RTIRAnz8lZXQ==, tarball: file:projects/text-editor.tgz}
|
||||
id: file:projects/text-editor.tgz
|
||||
name: '@rush-temp/text-editor'
|
||||
version: 0.0.0
|
||||
@ -23665,6 +23668,7 @@ packages:
|
||||
'@tiptap/extension-gapcursor': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
'@tiptap/extension-heading': 2.1.12(@tiptap/core@2.1.12)
|
||||
'@tiptap/extension-highlight': 2.1.12(@tiptap/core@2.1.12)
|
||||
'@tiptap/extension-history': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
'@tiptap/extension-link': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
'@tiptap/extension-list-keymap': 2.1.12(@tiptap/core@2.1.12)
|
||||
'@tiptap/extension-mention': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)(@tiptap/suggestion@2.1.12)
|
||||
@ -23735,7 +23739,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/text.tgz(@types/node@16.11.68)(esbuild@0.16.17)(svelte@4.2.5)(ts-node@10.9.1):
|
||||
resolution: {integrity: sha512-mAnt4uQfrjjGcqAXJI8y+sOq5joLxJAU4l8shfLkgdv0FJY+JcR7/lnT0cg79pAEySJ3WWb3RnXlyH+TtGejJQ==, tarball: file:projects/text.tgz}
|
||||
resolution: {integrity: sha512-yBBmdVZ6t7OiE7WauaYvqDsgVsOUzS8W4sKslQ6EXa/gvDvnV3F/cjAMIvbH6B4w1bFOWoqgC25sqxO8HUaEOQ==, tarball: file:projects/text.tgz}
|
||||
id: file:projects/text.tgz
|
||||
name: '@rush-temp/text'
|
||||
version: 0.0.0
|
||||
@ -23744,6 +23748,7 @@ packages:
|
||||
'@tiptap/extension-gapcursor': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
'@tiptap/extension-heading': 2.1.12(@tiptap/core@2.1.12)
|
||||
'@tiptap/extension-highlight': 2.1.12(@tiptap/core@2.1.12)
|
||||
'@tiptap/extension-history': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
'@tiptap/extension-link': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
'@tiptap/extension-mention': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)(@tiptap/suggestion@2.1.12)
|
||||
'@tiptap/extension-table': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
|
||||
|
@ -103,6 +103,7 @@ services:
|
||||
- COLLABORATOR_PORT=3078
|
||||
- SECRET=secret
|
||||
- TRANSACTOR_URL=ws://localhost:3333
|
||||
- UPLOAD_URL=/files
|
||||
- MONGO_URL=mongodb://mongodb:27017
|
||||
- MINIO_ENDPOINT=minio
|
||||
- MINIO_ACCESS_KEY=minioadmin
|
||||
|
@ -60,6 +60,7 @@
|
||||
"@tiptap/extension-code-block": "^2.1.12",
|
||||
"@tiptap/extension-gapcursor": "^2.1.12",
|
||||
"@tiptap/extension-heading": "^2.1.12",
|
||||
"@tiptap/extension-history": "^2.1.12",
|
||||
"@tiptap/extension-table": "^2.1.12",
|
||||
"@tiptap/extension-table-cell": "^2.1.12",
|
||||
"@tiptap/extension-table-header": "^2.1.12",
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
import { calculateDecorations, createYdocDocument } from './diff/decorations'
|
||||
import { defaultEditorAttributes } from './editor/editorProps'
|
||||
import { defaultExtensions } from './extensions'
|
||||
import { EditorKit } from '../kits/editor-kit'
|
||||
|
||||
export let ydoc: Ydoc
|
||||
export let field: string | undefined = undefined
|
||||
@ -79,7 +79,7 @@
|
||||
editorProps: { attributes: mergeAttributes(defaultEditorAttributes, { class: 'flex-grow' }) },
|
||||
element,
|
||||
editable: false,
|
||||
extensions: [...defaultExtensions, DecorationExtension, Collaboration.configure({ document: ydoc, field })]
|
||||
extensions: [EditorKit, DecorationExtension, Collaboration.configure({ document: ydoc, field })]
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -100,4 +100,5 @@
|
||||
on:focus
|
||||
on:blur
|
||||
on:update
|
||||
on:open-document
|
||||
/>
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
import { Completion } from '../Completion'
|
||||
import { textEditorCommandHandler } from '../commands'
|
||||
import { EditorKit } from '../kits/editor-kit'
|
||||
import textEditorPlugin from '../plugin'
|
||||
import { DocumentId, TiptapCollabProvider } from '../provider'
|
||||
import {
|
||||
@ -47,7 +48,7 @@
|
||||
import { FileAttachFunction, ImageExtension } from './extension/imageExt'
|
||||
import { InlinePopupExtension } from './extension/inlinePopup'
|
||||
import { InlineStyleToolbarExtension } from './extension/inlineStyleToolbar'
|
||||
import { completionConfig, defaultExtensions } from './extensions'
|
||||
import { completionConfig } from './extensions'
|
||||
|
||||
export let documentId: DocumentId
|
||||
export let field: string | undefined = undefined
|
||||
@ -207,7 +208,8 @@
|
||||
optionalExtensions.push(
|
||||
ImageExtension.configure({
|
||||
inline: true,
|
||||
attachFile
|
||||
attachFile,
|
||||
uploadUrl: getMetadata(presentation.metadata.UploadURL)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -220,7 +222,7 @@
|
||||
element,
|
||||
editorProps: { attributes: mergeAttributes(defaultEditorAttributes, editorAttributes, { class: 'flex-grow' }) },
|
||||
extensions: [
|
||||
...defaultExtensions,
|
||||
EditorKit.configure({ history: false }),
|
||||
...optionalExtensions,
|
||||
Placeholder.configure({ placeholder: placeHolderStr }),
|
||||
InlineStyleToolbarExtension.configure({
|
||||
@ -259,7 +261,7 @@
|
||||
dispatch('open-document', { event, _id, _class })
|
||||
}
|
||||
}),
|
||||
EmojiExtension.configure(),
|
||||
EmojiExtension,
|
||||
...extensions
|
||||
],
|
||||
parseOptions: {
|
||||
|
@ -20,10 +20,13 @@
|
||||
import { DecorationSet } from '@tiptap/pm/view'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { Markup } from '@hcengineering/core'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
|
||||
import { calculateDecorations, createMarkupDocument } from './diff/decorations'
|
||||
import { defaultEditorAttributes } from './editor/editorProps'
|
||||
import { defaultExtensions } from './extensions'
|
||||
import { ImageExtension } from './extension/imageExt'
|
||||
import { EditorKit } from '../kits/editor-kit'
|
||||
|
||||
export let content: Markup
|
||||
export let comparedVersion: Markup | undefined = undefined
|
||||
@ -78,7 +81,13 @@
|
||||
element,
|
||||
content,
|
||||
editable: false,
|
||||
extensions: [...defaultExtensions, DecorationExtension],
|
||||
extensions: [
|
||||
EditorKit,
|
||||
ImageExtension.configure({
|
||||
uploadUrl: getMetadata(presentation.metadata.UploadURL)
|
||||
}),
|
||||
DecorationExtension
|
||||
],
|
||||
onTransaction: () => {
|
||||
// force re-render so `editor.isActive` works as expected
|
||||
editor = editor
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { IntlString, getMetadata } from '@hcengineering/platform'
|
||||
import presentation, { MessageViewer } from '@hcengineering/presentation'
|
||||
import {
|
||||
ActionIcon,
|
||||
@ -175,7 +175,8 @@
|
||||
attachFile,
|
||||
reportNode: (id, node) => {
|
||||
attachments.set(id, node)
|
||||
}
|
||||
},
|
||||
uploadUrl: getMetadata(presentation.metadata.UploadURL)
|
||||
})
|
||||
|
||||
const completionPlugin = Completion.configure({
|
||||
|
@ -31,7 +31,7 @@
|
||||
import { InlinePopupExtension } from './extension/inlinePopup'
|
||||
import { InlineStyleToolbarExtension } from './extension/inlineStyleToolbar'
|
||||
import { SubmitExtension } from './extension/submit'
|
||||
import { defaultExtensions } from './extensions'
|
||||
import { EditorKit } from '../kits/editor-kit'
|
||||
|
||||
export let content: string = ''
|
||||
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
|
||||
@ -124,7 +124,7 @@
|
||||
editorProps: { attributes: mergeAttributes(defaultEditorAttributes, editorAttributes) },
|
||||
content,
|
||||
extensions: [
|
||||
...defaultExtensions,
|
||||
EditorKit,
|
||||
...(supportSubmit ? [Handle] : []), // order important
|
||||
Placeholder.configure({ placeholder: placeHolderStr }),
|
||||
...extensions,
|
||||
|
@ -12,8 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import presentation, { PDFViewer, getFileUrl } from '@hcengineering/presentation'
|
||||
import { PDFViewer } from '@hcengineering/presentation'
|
||||
import { ImageNode, type ImageOptions as ImageNodeOptions } from '@hcengineering/text'
|
||||
import { type IconSize, getIconSize2x, showPopup } from '@hcengineering/ui'
|
||||
import { mergeAttributes, nodeInputRule } from '@tiptap/core'
|
||||
@ -37,6 +36,7 @@ export type ImageAlignment = 'center' | 'left' | 'right'
|
||||
export interface ImageOptions extends ImageNodeOptions {
|
||||
attachFile?: FileAttachFunction
|
||||
reportNode?: (id: string, node: ProseMirrorNode) => void
|
||||
uploadUrl: string
|
||||
}
|
||||
|
||||
export interface ImageAlignmentOptions {
|
||||
@ -80,6 +80,11 @@ function getType (type: string): 'image' | 'other' {
|
||||
return 'other'
|
||||
}
|
||||
|
||||
// This is a simplified version of getFileUrl from presentation plugin, which we cannot use
|
||||
function getFileUrl (fileId: string, size: IconSize = 'full', uploadUrl: string): string {
|
||||
return `${uploadUrl}?file=${fileId}&size=${size as string}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -87,7 +92,8 @@ export const ImageExtension = ImageNode.extend<ImageOptions>({
|
||||
addOptions () {
|
||||
return {
|
||||
inline: true,
|
||||
HTMLAttributes: {}
|
||||
HTMLAttributes: {},
|
||||
uploadUrl: ''
|
||||
}
|
||||
},
|
||||
|
||||
@ -117,9 +123,11 @@ export const ImageExtension = ImageNode.extend<ImageOptions>({
|
||||
HTMLAttributes
|
||||
)
|
||||
|
||||
const uploadUrl = this.options.uploadUrl ?? ''
|
||||
|
||||
const id = imgAttributes['file-id']
|
||||
if (id != null) {
|
||||
imgAttributes.src = getFileUrl(id, 'full')
|
||||
imgAttributes.src = getFileUrl(id, 'full', uploadUrl)
|
||||
let width: IconSize | undefined
|
||||
switch (imgAttributes.width) {
|
||||
case '32px':
|
||||
@ -137,8 +145,9 @@ export const ImageExtension = ImageNode.extend<ImageOptions>({
|
||||
break
|
||||
}
|
||||
if (width !== undefined) {
|
||||
imgAttributes.src = getFileUrl(id, width)
|
||||
imgAttributes.srcset = getFileUrl(id, width) + ' 1x,' + getFileUrl(id, getIconSize2x(width)) + ' 2x'
|
||||
imgAttributes.src = getFileUrl(id, width, uploadUrl)
|
||||
imgAttributes.srcset =
|
||||
getFileUrl(id, width, uploadUrl) + ' 1x,' + getFileUrl(id, getIconSize2x(width), uploadUrl) + ' 2x'
|
||||
}
|
||||
imgAttributes.class = 'text-editor-image'
|
||||
imgAttributes.contentEditable = false
|
||||
@ -207,8 +216,7 @@ export const ImageExtension = ImageNode.extend<ImageOptions>({
|
||||
for (const uri of uris) {
|
||||
if (uri !== '') {
|
||||
const url = new URL(uri)
|
||||
const uploadUrl = getMetadata(presentation.metadata.UploadURL)
|
||||
if (uploadUrl === undefined || !url.href.includes(uploadUrl)) {
|
||||
if (opt.uploadUrl === undefined || !url.href.includes(opt.uploadUrl)) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -1,91 +1,23 @@
|
||||
import TableHeader from '@tiptap/extension-table-header'
|
||||
//
|
||||
// Copyright © 2023 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 TaskItem from '@tiptap/extension-task-item'
|
||||
import TaskList from '@tiptap/extension-task-list'
|
||||
|
||||
import { type Level } from '@tiptap/extension-heading'
|
||||
import Highlight from '@tiptap/extension-highlight'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import Underline from '@tiptap/extension-underline'
|
||||
|
||||
import Gapcursor from '@tiptap/extension-gapcursor'
|
||||
import ListKeymap from '@tiptap/extension-list-keymap'
|
||||
|
||||
import { type AnyExtension } from '@tiptap/core'
|
||||
import Link from '@tiptap/extension-link'
|
||||
import Typography from '@tiptap/extension-typography'
|
||||
import { type CompletionOptions } from '../Completion'
|
||||
import MentionList from './MentionList.svelte'
|
||||
import { NodeUuidExtension } from './extension/nodeUuid'
|
||||
import { SvelteRenderer } from './node-view'
|
||||
import { CodemarkExtension } from './extension/codemark'
|
||||
import type { SuggestionKeyDownProps, SuggestionProps } from './extension/suggestion'
|
||||
|
||||
import { Table, TableCell, TableRow } from './extension/table'
|
||||
|
||||
export const tableExtensions = [
|
||||
Table.configure({
|
||||
resizable: false,
|
||||
HTMLAttributes: {
|
||||
class: 'proseTable'
|
||||
}
|
||||
}),
|
||||
TableRow.configure({}),
|
||||
TableHeader.configure({}),
|
||||
TableCell.configure({})
|
||||
]
|
||||
|
||||
export const taskListExtensions = [
|
||||
TaskList,
|
||||
TaskItem.configure({
|
||||
nested: true,
|
||||
HTMLAttributes: {
|
||||
class: 'flex flex-grow gap-1 checkbox_style'
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
export const supportedHeadingLevels: Level[] = [1, 2, 3]
|
||||
|
||||
export const defaultExtensions: AnyExtension[] = [
|
||||
StarterKit.configure({
|
||||
code: {
|
||||
HTMLAttributes: {
|
||||
class: 'proseCode'
|
||||
}
|
||||
},
|
||||
codeBlock: {
|
||||
languageClassPrefix: 'language-',
|
||||
exitOnArrowDown: true,
|
||||
exitOnTripleEnter: true,
|
||||
HTMLAttributes: {
|
||||
class: 'proseCodeBlock'
|
||||
}
|
||||
},
|
||||
heading: {
|
||||
levels: supportedHeadingLevels,
|
||||
HTMLAttributes: {
|
||||
class: 'proseHeading'
|
||||
}
|
||||
}
|
||||
}),
|
||||
CodemarkExtension,
|
||||
Highlight.configure({
|
||||
multicolor: false
|
||||
}),
|
||||
Underline.configure({}),
|
||||
Typography.configure({}),
|
||||
Gapcursor,
|
||||
Link.configure({
|
||||
openOnClick: true,
|
||||
HTMLAttributes: { class: 'cursor-pointer', rel: 'noopener noreferrer', target: '_blank' }
|
||||
}),
|
||||
ListKeymap.configure({}),
|
||||
NodeUuidExtension,
|
||||
...tableExtensions
|
||||
// ...taskListExtensions // Disable since tasks are not working properly now.
|
||||
]
|
||||
|
||||
export const mInsertTable = [
|
||||
{
|
||||
label: '2x2',
|
||||
|
63
packages/text-editor/src/kits/default-kit.ts
Normal file
63
packages/text-editor/src/kits/default-kit.ts
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// Copyright © 2023 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 { Extension } from '@tiptap/core'
|
||||
|
||||
import type { Level } from '@tiptap/extension-heading'
|
||||
import Highlight from '@tiptap/extension-highlight'
|
||||
import Link from '@tiptap/extension-link'
|
||||
import Typography from '@tiptap/extension-typography'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
|
||||
export interface DefaultKitOptions {
|
||||
heading?: {
|
||||
levels?: Level[]
|
||||
}
|
||||
history?: false
|
||||
}
|
||||
|
||||
export const DefaultKit = Extension.create<DefaultKitOptions>({
|
||||
name: 'defaultKit',
|
||||
|
||||
addExtensions () {
|
||||
return [
|
||||
StarterKit.configure({
|
||||
code: {
|
||||
HTMLAttributes: {
|
||||
class: 'proseCode'
|
||||
}
|
||||
},
|
||||
codeBlock: {
|
||||
languageClassPrefix: 'language-',
|
||||
exitOnArrowDown: true,
|
||||
exitOnTripleEnter: true,
|
||||
HTMLAttributes: {
|
||||
class: 'proseCodeBlock'
|
||||
}
|
||||
},
|
||||
heading: this.options.heading,
|
||||
history: this.options.history
|
||||
}),
|
||||
Highlight.configure({
|
||||
multicolor: false
|
||||
}),
|
||||
Typography.configure({}),
|
||||
Link.configure({
|
||||
openOnClick: true,
|
||||
HTMLAttributes: { class: 'cursor-pointer', rel: 'noopener noreferrer', target: '_blank' }
|
||||
})
|
||||
]
|
||||
}
|
||||
})
|
77
packages/text-editor/src/kits/editor-kit.ts
Normal file
77
packages/text-editor/src/kits/editor-kit.ts
Normal file
@ -0,0 +1,77 @@
|
||||
//
|
||||
// Copyright © 2023 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 { Extension } from '@tiptap/core'
|
||||
import { type Level } from '@tiptap/extension-heading'
|
||||
import ListKeymap from '@tiptap/extension-list-keymap'
|
||||
import TableHeader from '@tiptap/extension-table-header'
|
||||
import TaskItem from '@tiptap/extension-task-item'
|
||||
import TaskList from '@tiptap/extension-task-list'
|
||||
import Underline from '@tiptap/extension-underline'
|
||||
|
||||
import { DefaultKit, type DefaultKitOptions } from './default-kit'
|
||||
|
||||
import { CodemarkExtension } from '../components/extension/codemark'
|
||||
import { NodeUuidExtension } from '../components/extension/nodeUuid'
|
||||
import { Table, TableCell, TableRow } from '../components/extension/table'
|
||||
|
||||
const headingLevels: Level[] = [1, 2, 3]
|
||||
|
||||
export const tableExtensions = [
|
||||
Table.configure({
|
||||
resizable: false,
|
||||
HTMLAttributes: {
|
||||
class: 'proseTable'
|
||||
}
|
||||
}),
|
||||
TableRow.configure({}),
|
||||
TableHeader.configure({}),
|
||||
TableCell.configure({})
|
||||
]
|
||||
|
||||
export const taskListExtensions = [
|
||||
TaskList,
|
||||
TaskItem.configure({
|
||||
nested: true,
|
||||
HTMLAttributes: {
|
||||
class: 'flex flex-grow gap-1 checkbox_style'
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
export interface EditorKitOptions extends DefaultKitOptions {
|
||||
history?: false
|
||||
}
|
||||
|
||||
export const EditorKit = Extension.create<EditorKitOptions>({
|
||||
name: 'defaultKit',
|
||||
|
||||
addExtensions () {
|
||||
return [
|
||||
DefaultKit.configure({
|
||||
...this.options,
|
||||
heading: {
|
||||
levels: headingLevels
|
||||
}
|
||||
}),
|
||||
CodemarkExtension,
|
||||
Underline,
|
||||
ListKeymap,
|
||||
NodeUuidExtension,
|
||||
...tableExtensions
|
||||
// ...taskListExtensions // Disable since tasks are not working properly now.
|
||||
]
|
||||
}
|
||||
})
|
@ -37,6 +37,7 @@
|
||||
"@tiptap/extension-gapcursor": "^2.1.12",
|
||||
"@tiptap/extension-heading": "^2.1.12",
|
||||
"@tiptap/extension-highlight": "^2.1.12",
|
||||
"@tiptap/extension-history": "^2.1.12",
|
||||
"@tiptap/extension-link": "^2.1.12",
|
||||
"@tiptap/extension-mention": "^2.1.12",
|
||||
"@tiptap/extension-table": "^2.1.12",
|
||||
|
@ -1,106 +0,0 @@
|
||||
//
|
||||
// Copyright © 2023 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 { AnyExtension } from '@tiptap/core'
|
||||
import { Level } from '@tiptap/extension-heading'
|
||||
import Highlight from '@tiptap/extension-highlight'
|
||||
import Link from '@tiptap/extension-link'
|
||||
import Table from '@tiptap/extension-table'
|
||||
import TableCell from '@tiptap/extension-table-cell'
|
||||
import TableHeader from '@tiptap/extension-table-header'
|
||||
import TableRow from '@tiptap/extension-table-row'
|
||||
import TaskItem from '@tiptap/extension-task-item'
|
||||
import TaskList from '@tiptap/extension-task-list'
|
||||
import Typography from '@tiptap/extension-typography'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { ImageNode, ReferenceNode, TodoItemNode, TodoListNode } from './nodes'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const headingLevels: Level[] = [1, 2, 3, 4, 5, 6]
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const tableExtensions = [
|
||||
Table.configure({
|
||||
resizable: false,
|
||||
HTMLAttributes: {
|
||||
class: 'proseTable'
|
||||
}
|
||||
}),
|
||||
TableRow.configure({}),
|
||||
TableHeader.configure({}),
|
||||
TableCell.configure({})
|
||||
]
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const taskListExtensions = [
|
||||
TaskList,
|
||||
TaskItem.configure({
|
||||
nested: true,
|
||||
HTMLAttributes: {
|
||||
class: 'flex flex-grow gap-1 checkbox_style'
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const defaultExtensions: AnyExtension[] = [
|
||||
StarterKit.configure({
|
||||
code: {
|
||||
HTMLAttributes: {
|
||||
class: 'proseCode'
|
||||
}
|
||||
},
|
||||
codeBlock: {
|
||||
languageClassPrefix: 'language-',
|
||||
exitOnArrowDown: true,
|
||||
exitOnTripleEnter: true,
|
||||
HTMLAttributes: {
|
||||
class: 'proseCodeBlock'
|
||||
}
|
||||
},
|
||||
heading: {
|
||||
levels: headingLevels
|
||||
}
|
||||
}),
|
||||
Highlight.configure({
|
||||
multicolor: false
|
||||
}),
|
||||
Typography.configure({}),
|
||||
Link.configure({
|
||||
openOnClick: true,
|
||||
HTMLAttributes: { class: 'cursor-pointer', rel: 'noopener noreferrer', target: '_blank' }
|
||||
}),
|
||||
...tableExtensions,
|
||||
...taskListExtensions
|
||||
]
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const serverExtensions: AnyExtension[] = [
|
||||
...defaultExtensions,
|
||||
ImageNode,
|
||||
ReferenceNode,
|
||||
TodoItemNode,
|
||||
TodoListNode
|
||||
]
|
@ -17,7 +17,10 @@ import { Extensions, getSchema } from '@tiptap/core'
|
||||
import { generateJSON, generateHTML } from '@tiptap/html'
|
||||
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
||||
|
||||
import { defaultExtensions } from './extensions'
|
||||
import { ServerKit } from './kits/server-kit'
|
||||
|
||||
const defaultExtensions = [ServerKit]
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -13,8 +13,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
export * from './extensions'
|
||||
export * from './html'
|
||||
export * from './node'
|
||||
export * from './nodes'
|
||||
export * from './text'
|
||||
|
||||
export * from './kits/default-kit'
|
||||
export * from './kits/server-kit'
|
||||
|
63
packages/text/src/kits/default-kit.ts
Normal file
63
packages/text/src/kits/default-kit.ts
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// Copyright © 2023 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 { Extension } from '@tiptap/core'
|
||||
|
||||
import { Level } from '@tiptap/extension-heading'
|
||||
import Highlight from '@tiptap/extension-highlight'
|
||||
import Link from '@tiptap/extension-link'
|
||||
import Typography from '@tiptap/extension-typography'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
|
||||
export interface DefaultKitOptions {
|
||||
heading?: {
|
||||
levels?: Level[]
|
||||
}
|
||||
history?: false
|
||||
}
|
||||
|
||||
export const DefaultKit = Extension.create<DefaultKitOptions>({
|
||||
name: 'defaultKit',
|
||||
|
||||
addExtensions () {
|
||||
return [
|
||||
StarterKit.configure({
|
||||
code: {
|
||||
HTMLAttributes: {
|
||||
class: 'proseCode'
|
||||
}
|
||||
},
|
||||
codeBlock: {
|
||||
languageClassPrefix: 'language-',
|
||||
exitOnArrowDown: true,
|
||||
exitOnTripleEnter: true,
|
||||
HTMLAttributes: {
|
||||
class: 'proseCodeBlock'
|
||||
}
|
||||
},
|
||||
heading: this.options.heading,
|
||||
history: this.options.history
|
||||
}),
|
||||
Highlight.configure({
|
||||
multicolor: false
|
||||
}),
|
||||
Typography.configure({}),
|
||||
Link.configure({
|
||||
openOnClick: true,
|
||||
HTMLAttributes: { class: 'cursor-pointer', rel: 'noopener noreferrer', target: '_blank' }
|
||||
})
|
||||
]
|
||||
}
|
||||
})
|
78
packages/text/src/kits/server-kit.ts
Normal file
78
packages/text/src/kits/server-kit.ts
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// Copyright © 2023 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 { Extension } from '@tiptap/core'
|
||||
import Table from '@tiptap/extension-table'
|
||||
import TableCell from '@tiptap/extension-table-cell'
|
||||
import TableHeader from '@tiptap/extension-table-header'
|
||||
import TableRow from '@tiptap/extension-table-row'
|
||||
import TaskItem from '@tiptap/extension-task-item'
|
||||
import TaskList from '@tiptap/extension-task-list'
|
||||
|
||||
import { ImageNode, ImageOptions } from '../nodes/image'
|
||||
import { ReferenceNode } from '../nodes/reference'
|
||||
import { TodoItemNode, TodoListNode } from '../nodes/todo'
|
||||
|
||||
import { DefaultKit, DefaultKitOptions } from './default-kit'
|
||||
import { Level } from '@tiptap/extension-heading'
|
||||
|
||||
const headingLevels: Level[] = [1, 2, 3, 4, 5, 6]
|
||||
|
||||
const tableExtensions = [
|
||||
Table.configure({
|
||||
resizable: false,
|
||||
HTMLAttributes: {
|
||||
class: 'proseTable'
|
||||
}
|
||||
}),
|
||||
TableRow.configure({}),
|
||||
TableHeader.configure({}),
|
||||
TableCell.configure({})
|
||||
]
|
||||
|
||||
const taskListExtensions = [
|
||||
TaskList,
|
||||
TaskItem.configure({
|
||||
nested: true,
|
||||
HTMLAttributes: {
|
||||
class: 'flex flex-grow gap-1 checkbox_style'
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
export interface ServerKitOptions extends DefaultKitOptions {
|
||||
image: Partial<ImageOptions>
|
||||
}
|
||||
|
||||
export const ServerKit = Extension.create<ServerKitOptions>({
|
||||
name: 'serverKit',
|
||||
|
||||
addExtensions () {
|
||||
return [
|
||||
DefaultKit.configure({
|
||||
...this.options,
|
||||
heading: {
|
||||
levels: headingLevels
|
||||
}
|
||||
}),
|
||||
...tableExtensions,
|
||||
...taskListExtensions,
|
||||
ImageNode.configure(this.options.image),
|
||||
TodoItemNode,
|
||||
TodoListNode,
|
||||
ReferenceNode
|
||||
]
|
||||
}
|
||||
})
|
@ -21,6 +21,12 @@ import { getDataAttribute } from './utils'
|
||||
export interface ImageOptions {
|
||||
inline: boolean
|
||||
HTMLAttributes: Record<string, any>
|
||||
uploadUrl?: string
|
||||
}
|
||||
|
||||
// This is a simplified version of getFileUrl from presentation plugin, which we cannot use
|
||||
function getFileUrl (uploadUrl: string, fileId: string, size: string = 'full'): string {
|
||||
return `${uploadUrl}?file=${fileId}&size=${size}`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,7 +38,8 @@ export const ImageNode = Node.create<ImageOptions>({
|
||||
addOptions () {
|
||||
return {
|
||||
inline: true,
|
||||
HTMLAttributes: {}
|
||||
HTMLAttributes: {},
|
||||
uploadUrl: ''
|
||||
}
|
||||
},
|
||||
|
||||
@ -98,6 +105,12 @@ export const ImageNode = Node.create<ImageOptions>({
|
||||
HTMLAttributes
|
||||
)
|
||||
|
||||
const fileId = imgAttributes['file-id']
|
||||
if (fileId != null) {
|
||||
const uploadUrl = this.options.uploadUrl ?? ''
|
||||
imgAttributes.src = getFileUrl(uploadUrl, fileId)
|
||||
}
|
||||
|
||||
return ['div', divAttributes, ['img', imgAttributes]]
|
||||
}
|
||||
})
|
||||
|
@ -15,9 +15,9 @@
|
||||
|
||||
import chunter, { Backlink } from '@hcengineering/chunter'
|
||||
import { Class, Data, Doc, Ref, Tx, TxFactory } from '@hcengineering/core'
|
||||
import { extractReferences, getHTML, parseHTML, serverExtensions } from '@hcengineering/text'
|
||||
import { ServerKit, extractReferences, getHTML, parseHTML } from '@hcengineering/text'
|
||||
|
||||
const extensions = serverExtensions
|
||||
const extensions = [ServerKit]
|
||||
|
||||
export function getBacklinks (
|
||||
backlinkId: Ref<Doc>,
|
||||
|
@ -26,6 +26,7 @@ export interface Config {
|
||||
|
||||
TransactorUrl: string
|
||||
MongoUrl: string
|
||||
UploadUrl: string
|
||||
|
||||
MinioEndpoint: string
|
||||
MinioAccessKey: string
|
||||
@ -39,6 +40,7 @@ const envMap: { [key in keyof Config]: string } = {
|
||||
Port: 'COLLABORATOR_PORT',
|
||||
TransactorUrl: 'TRANSACTOR_URL',
|
||||
MongoUrl: 'MONGO_URL',
|
||||
UploadUrl: 'UPLOAD_URL',
|
||||
MinioEndpoint: 'MINIO_ENDPOINT',
|
||||
MinioAccessKey: 'MINIO_ACCESS_KEY',
|
||||
MinioSecretKey: 'MINIO_SECRET_KEY'
|
||||
@ -63,6 +65,7 @@ const config: Config = (() => {
|
||||
Port: parseInt(process.env[envMap.Port] ?? '3078'),
|
||||
TransactorUrl: process.env[envMap.TransactorUrl],
|
||||
MongoUrl: process.env[envMap.MongoUrl],
|
||||
UploadUrl: process.env[envMap.UploadUrl] ?? '/files',
|
||||
MinioEndpoint: process.env[envMap.MinioEndpoint],
|
||||
MinioAccessKey: process.env[envMap.MinioAccessKey],
|
||||
MinioSecretKey: process.env[envMap.MinioSecretKey]
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import { MeasureContext } from '@hcengineering/core'
|
||||
import { MinioService } from '@hcengineering/minio'
|
||||
import { serverExtensions } from '@hcengineering/text'
|
||||
import { ServerKit } from '@hcengineering/text'
|
||||
import { Hocuspocus, onAuthenticatePayload } from '@hocuspocus/server'
|
||||
import bp from 'body-parser'
|
||||
import compression from 'compression'
|
||||
@ -72,6 +72,14 @@ export async function start (
|
||||
})
|
||||
)
|
||||
|
||||
const extensions = [
|
||||
ServerKit.configure({
|
||||
image: {
|
||||
uploadUrl: config.UploadUrl
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
const extensionsCtx = ctx.newChild('extensions', {})
|
||||
const storageCtx = ctx.newChild('storage', {})
|
||||
|
||||
@ -111,7 +119,7 @@ export async function start (
|
||||
extensions: [
|
||||
new ActionsExtension({
|
||||
ctx: extensionsCtx.newChild('actions', {}),
|
||||
transformer: new HtmlTransformer(serverExtensions)
|
||||
transformer: new HtmlTransformer(extensions)
|
||||
}),
|
||||
new StorageExtension({
|
||||
ctx: extensionsCtx.newChild('storage', {}),
|
||||
@ -121,12 +129,12 @@ export async function start (
|
||||
mongodb: new MongodbStorageAdapter(
|
||||
storageCtx.newChild('mongodb', {}),
|
||||
mongo,
|
||||
new HtmlTransformer(serverExtensions)
|
||||
new HtmlTransformer(extensions)
|
||||
),
|
||||
platform: new PlatformStorageAdapter(
|
||||
storageCtx.newChild('platform', {}),
|
||||
config.TransactorUrl,
|
||||
new HtmlTransformer(serverExtensions)
|
||||
new HtmlTransformer(extensions)
|
||||
)
|
||||
},
|
||||
'minio'
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { MeasureContext, WorkspaceId } from '@hcengineering/core'
|
||||
import { ContentTextAdapter } from '@hcengineering/server-core'
|
||||
import { getText, serverExtensions, yDocContentToNodes } from '@hcengineering/text'
|
||||
import { ServerKit, getText, yDocContentToNodes } from '@hcengineering/text'
|
||||
import { Readable } from 'stream'
|
||||
|
||||
const extensions = serverExtensions
|
||||
const extensions = [ServerKit]
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
@ -111,6 +111,7 @@ services:
|
||||
- COLLABORATOR_PORT=3078
|
||||
- SECRET=secret
|
||||
- TRANSACTOR_URL=ws://localhost:3334
|
||||
- UPLOAD_URL=/files
|
||||
- MONGO_URL=mongodb://mongodb:27018
|
||||
- MINIO_ENDPOINT=minio
|
||||
- MINIO_ACCESS_KEY=minioadmin
|
||||
|
Loading…
Reference in New Issue
Block a user