Merge remote-tracking branch 'origin/develop' into staging

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2024-09-18 19:44:53 +07:00
27 changed files with 913 additions and 718 deletions
+469 -537
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -102,7 +102,7 @@ import print, { printId } from '@hcengineering/print'
import sign from '@hcengineering/sign'
import analyticsCollector, { analyticsCollectorId } from '@hcengineering/analytics-collector'
import { setDefaultLanguage } from '@hcengineering/theme'
import { setDefaultLanguage, initThemeStore } from '@hcengineering/theme'
import { configureNotifications } from './notifications'
import { Config, IPCMainExposed, Branding } from './types'
@@ -316,6 +316,8 @@ export async function configurePlatform (): Promise<void> {
setMetadata(workbench.metadata.DefaultSpace, myBranding.defaultSpace ?? tracker.project.DefaultProject)
setMetadata(workbench.metadata.DefaultSpecial, myBranding.defaultSpecial ?? 'issues')
initThemeStore()
addEventListener(workbench.event.NotifyConnection, async (evt) => {
await ipcMain.setFrontCookie(
config.FRONT_URL,
+3 -1
View File
@@ -110,7 +110,7 @@ import { coreId } from '@hcengineering/core'
import presentation, { loadServerConfig, parsePreviewConfig, presentationId } from '@hcengineering/presentation'
import { setMetadata } from '@hcengineering/platform'
import { setDefaultLanguage } from '@hcengineering/theme'
import { setDefaultLanguage, initThemeStore } from '@hcengineering/theme'
import { preferenceId } from '@hcengineering/preference'
import { uiId } from '@hcengineering/ui/src/plugin'
@@ -400,4 +400,6 @@ export async function configurePlatform() {
setMetadata(workbench.metadata.DefaultApplication, myBranding.defaultApplication ?? 'tracker')
setMetadata(workbench.metadata.DefaultSpace, myBranding.defaultSpace ?? tracker.project.DefaultProject)
setMetadata(workbench.metadata.DefaultSpecial, myBranding.defaultSpecial ?? 'issues')
initThemeStore()
}
+105
View File
@@ -0,0 +1,105 @@
## How to Import Documents from Notion
To import Notion documents:
1. Export documents from Notion as *Markdown & CSV*
2. Extract the exported archive
3. 1. If you want your docs to be imported along with Notion teamspaces, use this command:
```
rushx run-local import-notion-with-teamspaces ${dir} \
--user ${user} \
--password ${password} \
--workspace ${workspace}
```
3. 2. If you want the docs to be imported to a new teamspace, use this command:
```
rushx run-local import-notion-to-teamspace {dir} \
--user ${user} \
--password ${password} \
--workspace ${workspace} \
--teamspace ${teamspace}
```
* *dir* - path to the root of the extracted archive
* *user* - your username or email
* *password* - password
* *workspace* - workspace name where the documents should be imported to
* *teamspace* - teamspace to be created for newly imported docs
### Examples
#### For local run:
When importing Notion workspace with teamspaces
```
rushx run-local import-notion-with-teamspaces /home/john/extracted-notion-docs \
--user john.doe@gmail.com \
--password qwe1234 \
--workspace ws1
```
When importing Notion workspace without teamspaces or a page with subpages
```
rushx run-local import-notion-to-teamspace /home/john/extracted-notion-docs \
--user john.doe@gmail.com \
--password qwe1234 \
--workspace ws1 \
--teamspace imported
```
#### For clouds:
##### Here is an example for those who's using huly.app cloud:
To import Notion workspace with teamspaces
```
docker run \
-e SERVER_SECRET="" \
-e ACCOUNTS_URL="https://account.huly.app" \
-e FRONT_URL="https://huly.app" \
hardcoreeng/tool:latest \
-- bundle.js import-notion-with-teamspaces /tmp/notion-docs \
--user jane.doe@gmail.com \
--password 4321qwe \
--workspace ws1
```
To import Notion workspace without teamspaces or a page with subpages.
```
docker run \
-e SERVER_SECRET="" \
-e ACCOUNTS_URL="https://account.huly.app" \
-e FRONT_URL="https://huly.app" \
hardcoreeng/tool:latest \
-- bundle.js import-notion-to-teamspace /tmp/notion-docs \
--user jane.doe@gmail.com \
--password 4321qwe \
--workspace ws1 \
--teamspace notion
```
##### Here is an example for those who's using bold.ru cloud:
To import Notion workspace with teamspaces
```
docker run \
-e SERVER_SECRET="" \
-e ACCOUNTS_URL="https://account.bold.ru" \
-e FRONT_URL="https://app.bold.ru" \
hardcoreeng/tool:latest \
-- bundle.js import-notion-with-teamspaces /tmp/notion-docs \
--user jane.doe@gmail.com \
--password 4321qwe \
--workspace ws1
```
To import Notion workspace without teamspaces or a page with subpages.
```
docker run \
-e SERVER_SECRET="" \
-e ACCOUNTS_URL="https://account.bold.ru" \
-e FRONT_URL="https://app.bold.ru" \
hardcoreeng/tool:latest \
-- bundle.js import-notion-to-teamspace /tmp/notion-docs \
--user jane.doe@gmail.com \
--password 4321qwe \
--workspace ws1 \
--teamspace notion
```
+23 -23
View File
@@ -40,32 +40,32 @@
},
"dependencies": {
"@hcengineering/core": "^0.6.32",
"@tiptap/core": "^2.2.4",
"@tiptap/html": "^2.2.4",
"@tiptap/pm": "^2.2.4",
"@tiptap/starter-kit": "^2.2.4",
"@tiptap/extension-gapcursor": "^2.2.4",
"@tiptap/extension-heading": "^2.2.4",
"@tiptap/extension-highlight": "^2.2.4",
"@tiptap/extension-history": "^2.2.4",
"@tiptap/extension-link": "^2.2.4",
"@tiptap/extension-mention": "^2.2.4",
"@tiptap/extension-table": "^2.2.4",
"@tiptap/extension-table-cell": "^2.2.4",
"@tiptap/extension-table-header": "^2.2.4",
"@tiptap/extension-table-row": "^2.2.4",
"@tiptap/extension-task-item": "^2.2.4",
"@tiptap/extension-task-list": "^2.2.4",
"@tiptap/extension-typography": "^2.2.4",
"@tiptap/extension-code-block": "^2.2.4",
"@tiptap/extension-code": "^2.2.4",
"@tiptap/extension-underline": "^2.2.4",
"@tiptap/suggestion": "^2.2.4",
"@tiptap/core": "^2.6.6",
"@tiptap/html": "^2.6.6",
"@tiptap/pm": "^2.6.6",
"@tiptap/starter-kit": "^2.6.6",
"@tiptap/extension-gapcursor": "^2.6.6",
"@tiptap/extension-heading": "^2.6.6",
"@tiptap/extension-highlight": "^2.6.6",
"@tiptap/extension-history": "^2.6.6",
"@tiptap/extension-link": "^2.6.6",
"@tiptap/extension-mention": "^2.6.6",
"@tiptap/extension-table": "^2.6.6",
"@tiptap/extension-table-cell": "^2.6.6",
"@tiptap/extension-table-header": "^2.6.6",
"@tiptap/extension-table-row": "^2.6.6",
"@tiptap/extension-task-item": "^2.6.6",
"@tiptap/extension-task-list": "^2.6.6",
"@tiptap/extension-typography": "^2.6.6",
"@tiptap/extension-code-block": "^2.6.6",
"@tiptap/extension-code": "^2.6.6",
"@tiptap/extension-underline": "^2.6.6",
"@tiptap/suggestion": "^2.6.6",
"prosemirror-codemark": "^0.4.2",
"markdown-it": "^14.0.0",
"fast-equals": "^5.0.1",
"yjs": "^13.5.52",
"y-prosemirror": "^1.2.1"
"yjs": "^13.6.19",
"y-prosemirror": "^1.2.12"
},
"repository": "https://github.com/hcenginneing/anticrm",
"publishConfig": {
+2 -52
View File
@@ -13,10 +13,11 @@
// limitations under the License.
//
import { isActive, textblockTypeInputRule } from '@tiptap/core'
import { textblockTypeInputRule } from '@tiptap/core'
import CodeBlock, { CodeBlockOptions } from '@tiptap/extension-code-block'
export const codeBlockOptions: CodeBlockOptions = {
defaultLanguage: null,
languageClassPrefix: 'language-',
exitOnArrowDown: true,
exitOnTripleEnter: true,
@@ -36,57 +37,6 @@ export const backtickInputRegex = /^```$/
export const tildeInputRegex = /^~~~$/
export const CodeBlockExtension = CodeBlock.extend({
addCommands () {
return {
setCodeBlock:
(attributes) =>
({ commands }) => {
return commands.setNode(this.name, attributes)
},
toggleCodeBlock:
(attributes) =>
({ chain, commands, state }) => {
const { from, to } = state.selection
// merge multiple paragraphs into codeblock
if (!isActive(state, this.name) && !state.selection.empty) {
let hasParagraphsOnlySelected = true
const textArr: string[] = []
state.doc.nodesBetween(from, to, (node, pos) => {
if (node.isInline) {
return false
}
if (node.type.name !== 'paragraph') {
if (pos + 1 <= from && pos + node.nodeSize - 1 >= to) {
// skip nodes outside of the selected range
return false
} else {
// cannot merge non-paragraph nodes inside selection
hasParagraphsOnlySelected = false
return false
}
} else {
const selectedText = (node.textContent ?? '').slice(
pos + 1 > from ? 0 : from - pos - 1,
pos + node.nodeSize - 1 < to ? node.nodeSize - 1 : to - pos - 1
)
textArr.push(selectedText ?? '')
}
})
if (hasParagraphsOnlySelected && textArr.length > 1) {
return chain()
.command(({ state, tr }) => {
tr.replaceRangeWith(from, to, this.type.create(attributes, state.schema.text(textArr.join('\n'))))
return true
})
.setTextSelection({ from: from + 2, to: from + 2 })
.run()
}
}
return commands.toggleNode(this.name, 'paragraph', attributes)
}
}
},
addAttributes () {
return {
language: {
+10 -6
View File
@@ -67,10 +67,14 @@ export class ThemeOptions {
readonly language: string
) {}
}
export const themeStore = writable<ThemeOptions>(
new ThemeOptions(
getCurrentFontSize() === 'normal-font' ? 16 : 14,
isThemeDark(getCurrentTheme()),
getCurrentLanguage()
export const themeStore = writable<ThemeOptions>()
export function initThemeStore (): void {
themeStore.set(
new ThemeOptions(
getCurrentFontSize() === 'normal-font' ? 16 : 14,
isThemeDark(getCurrentTheme()),
getCurrentLanguage()
)
)
)
}
+7
View File
@@ -200,6 +200,13 @@
}
}
.text-markup-view {
margin: 0;
p:first-child { margin-block-start: 0; }
p:last-child { margin-block-end: 0; }
}
@supports (selector(:has(.text-editor-image-container))) {
p:has(> .text-editor-image-container) {
user-select: none;
@@ -29,6 +29,7 @@
export let messages: ActivityMessage[]
export let object: Doc
export let isNewestFirst = false
export let showFilter = true
const allId = activity.ids.AllFilter
const client = getClient()
@@ -117,30 +118,32 @@
)
</script>
<div class="w-4 min-w-4 max-w-4" />
{#if selectedFiltersRefs === 'All'}
<div class="antiSection-header__tag highlight">
<Label label={activity.string.All} />
</div>
{:else}
{#each selectedFilters as filter}
<div class="antiSection-header__tag overflow-label">
<Label label={filter.label} />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="tag-icon"
on:click={() => {
if (selectedFiltersRefs !== allId && Array.isArray(selectedFiltersRefs)) {
const ids = selectedFiltersRefs.filter((it) => it !== filter._id)
selectedFiltersRefs = ids.length > 0 ? ids : allId
}
}}
>
<Icon icon={IconClose} size={'small'} />
</div>
{#if showFilter}
<div class="w-4 min-w-4 max-w-4" />
{#if selectedFiltersRefs === allId}
<div class="antiSection-header__tag highlight">
<Label label={activity.string.All} />
</div>
{/each}
{:else}
{#each selectedFilters as filter}
<div class="antiSection-header__tag overflow-label">
<Label label={filter.label} />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="tag-icon"
on:click={() => {
if (selectedFiltersRefs !== allId && Array.isArray(selectedFiltersRefs)) {
const ids = selectedFiltersRefs.filter((it) => it !== filter._id)
selectedFiltersRefs = ids.length > 0 ? ids : allId
}
}}
>
<Icon icon={IconClose} size={'small'} />
</div>
</div>
{/each}
{/if}
{/if}
<div class="w-4 min-w-4 max-w-4" />
<ActionIcon icon={IconFilter} size={'medium'} action={handleOptions} />
@@ -201,7 +201,9 @@
{#if !isShort}
<div class="header clear-mins">
{#if person}
<EmployeePresenter value={person} shouldShowAvatar={false} compact />
<div class="username">
<EmployeePresenter value={person} shouldShowAvatar={false} compact />
</div>
{:else}
<div class="strong">
<Label label={core.string.System} />
@@ -420,4 +422,9 @@
right: -0.375rem;
color: var(--content-color);
}
.username {
font-weight: 500;
margin-right: 0.25rem;
}
</style>
+1
View File
@@ -52,6 +52,7 @@ export { default as ActivityMessagePresenter } from './components/activity-messa
export { default as ActivityExtension } from './components/ActivityExtension.svelte'
export { default as ActivityDocLink } from './components/ActivityDocLink.svelte'
export { default as ReactionPresenter } from './components/reactions/ReactionPresenter.svelte'
export { default as ActivityFilter } from './components/ActivityFilter.svelte'
export { default as ActivityMessageNotificationLabel } from './components/activity-message/ActivityMessageNotificationLabel.svelte'
export { default as ActivityMessageHeader } from './components/activity-message/ActivityMessageHeader.svelte'
export { default as ActivityMessageAction } from './components/ActivityMessageAction.svelte'
@@ -195,6 +195,9 @@
<ShowMore limit={compact ? 80 : undefined}>
<div class="clear-mins">
<MessageViewer message={value.message} />
{#if (value.attachments ?? 0) > 0 || (value.inlineButtons ?? 0) > 0}
<div class="mt-2" />
{/if}
<AttachmentDocList {value} {attachments} imageSize={attachmentImageSize} {videoPreload} />
<InlineButtons {value} {inlineButtons} />
</div>
@@ -202,6 +205,9 @@
{:else}
<div class="clear-mins">
<MessageViewer message={value.message} />
{#if (value.attachments ?? 0) > 0 || (value.inlineButtons ?? 0) > 0}
<div class="mt-2" />
{/if}
<AttachmentDocList {value} {attachments} imageSize={attachmentImageSize} {videoPreload} />
<InlineButtons {value} {inlineButtons} />
</div>
@@ -66,7 +66,7 @@
"@hcengineering/controlled-documents": "^0.1.0",
"@hcengineering/training": "^0.1.0",
"@hcengineering/training-resources": "^0.1.0",
"@tiptap/core": "^2.2.4",
"@tiptap/core": "^2.6.6",
"effector": "~22.8.7",
"svelte": "^4.2.12",
"slugify": "^1.6.6",
+1 -1
View File
@@ -63,7 +63,7 @@
"@hcengineering/document": "^0.6.0",
"@hcengineering/time": "^0.6.0",
"@hcengineering/rank": "^0.6.4",
"@tiptap/core": "^2.2.4",
"@tiptap/core": "^2.6.6",
"slugify": "^1.6.6",
"fast-equals": "^5.0.1"
}
@@ -15,6 +15,7 @@
//
-->
<script lang="ts">
import activity from '@hcengineering/activity'
import attachment, { Attachment } from '@hcengineering/attachment'
import core, { Doc, Ref, WithLookup, generateId, type Blob } from '@hcengineering/core'
import { Document, DocumentEvents } from '@hcengineering/document'
@@ -58,8 +59,9 @@
import DocumentEditor from './DocumentEditor.svelte'
import DocumentPresenter from './DocumentPresenter.svelte'
import DocumentTitle from './DocumentTitle.svelte'
import References from './sidebar/References.svelte'
import Activity from './sidebar/Activity.svelte'
import History from './sidebar/History.svelte'
import References from './sidebar/References.svelte'
export let _id: Ref<Document>
export let readonly: boolean = false
@@ -185,11 +187,11 @@
{
id: 'references',
icon: document.icon.References
},
{
id: 'activity',
icon: activity.icon.Activity
}
// {
// id: 'history',
// icon: document.icon.History
// }
]
let selectedAside: string | boolean = false
@@ -372,6 +374,8 @@
<svelte:fragment slot="aside">
{#if selectedAside === 'references'}
<References doc={doc._id} />
{:else if selectedAside === 'activity'}
<Activity value={doc} />
{:else if selectedAside === 'history'}
<History value={doc} {readonly} />
{/if}
@@ -0,0 +1,97 @@
<!--
//
// 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 activity, { ActivityMessage } from '@hcengineering/activity'
import { ActivityFilter, ActivityMessagePresenter, canGroupMessages } from '@hcengineering/activity-resources'
import { SortingOrder } from '@hcengineering/core'
import { Document } from '@hcengineering/document'
import { createQuery } from '@hcengineering/presentation'
import { Label, Lazy, Scroller } from '@hcengineering/ui'
export let value: Document
const query = createQuery()
let messages: ActivityMessage[] = []
let filteredMessages: ActivityMessage[] = []
$: query.query(
activity.class.ActivityMessage,
{ attachedTo: value._id, space: value.space },
(res) => {
messages = res
},
{
sort: {
createdOn: SortingOrder.Ascending
}
}
)
</script>
{#key value._id}
<div class="h-full flex-col clear-mins">
<div class="header">
<div class="title"><Label label={activity.string.Activity} /></div>
<ActivityFilter
{messages}
object={value}
showFilter={false}
on:update={(e) => {
filteredMessages = e.detail
}}
/>
</div>
<div class="divider" />
{#if filteredMessages.length > 0}
<Scroller padding="0.75rem 0.25rem">
{#each filteredMessages as message, index}
{@const canGroup = canGroupMessages(message, filteredMessages[index - 1])}
<Lazy>
<ActivityMessagePresenter
value={message}
doc={value}
hideLink={true}
type={canGroup ? 'short' : 'default'}
/>
</Lazy>
{/each}
</Scroller>
{/if}
</div>
{/key}
<style lang="scss">
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1.25rem;
height: 3rem;
min-height: 3rem;
border-bottom: 1px solid var(--theme-divider-color);
.title {
flex-grow: 1;
font-weight: 500;
color: var(--caption-color);
user-select: none;
}
}
</style>
+23 -23
View File
@@ -48,33 +48,33 @@
"@hcengineering/text": "^0.6.5",
"@hcengineering/text-editor": "^0.6.0",
"@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",
"@tiptap/extension-hard-break": "^2.2.4",
"@tiptap/extension-bubble-menu": "^2.2.4",
"@tiptap/extension-table": "^2.2.4",
"@tiptap/extension-table-cell": "^2.2.4",
"@tiptap/extension-table-header": "^2.2.4",
"@tiptap/extension-table-row": "^2.2.4",
"@tiptap/extension-heading": "^2.2.4",
"@tiptap/extension-list-keymap": "^2.2.4",
"@tiptap/extension-code": "^2.2.4",
"@tiptap/extension-code-block": "^2.2.4",
"@tiptap/extension-highlight": "^2.2.4",
"@tiptap/extension-typography": "^2.2.4",
"@tiptap/extension-link": "^2.2.4",
"@tiptap/starter-kit": "^2.2.4",
"@tiptap/extension-underline": "^2.2.4",
"@tiptap/core": "^2.6.6",
"@tiptap/pm": "^2.6.6",
"@tiptap/extension-code-block-lowlight": "^2.6.6",
"@tiptap/extension-collaboration": "^2.6.6",
"@tiptap/extension-collaboration-cursor": "^2.6.6",
"@tiptap/extension-placeholder": "^2.6.6",
"@tiptap/extension-hard-break": "^2.6.6",
"@tiptap/extension-bubble-menu": "^2.6.6",
"@tiptap/extension-table": "^2.6.6",
"@tiptap/extension-table-cell": "^2.6.6",
"@tiptap/extension-table-header": "^2.6.6",
"@tiptap/extension-table-row": "^2.6.6",
"@tiptap/extension-heading": "^2.6.6",
"@tiptap/extension-list-keymap": "^2.6.6",
"@tiptap/extension-code": "^2.6.6",
"@tiptap/extension-code-block": "^2.6.6",
"@tiptap/extension-highlight": "^2.6.6",
"@tiptap/extension-typography": "^2.6.6",
"@tiptap/extension-link": "^2.6.6",
"@tiptap/starter-kit": "^2.6.6",
"@tiptap/extension-underline": "^2.6.6",
"@hocuspocus/provider": "^2.11.0",
"prosemirror-codemark": "^0.4.2",
"y-protocols": "^1.0.6",
"y-prosemirror": "^1.2.1",
"y-prosemirror": "^1.2.12",
"y-websocket": "^2.0.4",
"yjs": "^13.5.52",
"yjs": "^13.6.19",
"fast-equals": "^5.0.1",
"rfc6902": "^5.0.1",
"diff": "^5.1.0",
@@ -27,13 +27,15 @@
function save (): void {
dispatch('update', link)
}
$: canSave = link === '' || URL.canParse(link)
</script>
<Card
label={textEditor.string.Link}
okLabel={textEditor.string.Save}
okAction={save}
canSave
{canSave}
on:close={() => {
dispatch('close')
}}
@@ -1 +1,5 @@
export const defaultEditorAttributes: Record<string, string> = { class: 'text-editor-view' }
export const defaultEditorAttributes: Record<string, string> = {
class: 'text-editor-view',
// https://github.com/ueberdosis/tiptap/issues/3357
tabindex: '0'
}
@@ -13,8 +13,9 @@
// limitations under the License.
//
import { codeBlockOptions } from '@hcengineering/text'
import { backtickInputRegex, codeBlockOptions, tildeInputRegex } from '@hcengineering/text'
import { DropdownLabelsPopup, getEventPositionElement, showPopup } from '@hcengineering/ui'
import { isActive, textblockTypeInputRule } from '@tiptap/core'
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'
@@ -33,6 +34,71 @@ export const codeBlockHighlightOptions: CodeBlockLowlightOptions = {
}
export const CodeBlockHighlighExtension = CodeBlockLowlight.extend<CodeBlockLowlightOptions>({
addCommands () {
return {
setCodeBlock:
(attributes) =>
({ commands }) => {
return commands.setNode(this.name, attributes)
},
toggleCodeBlock:
(attributes) =>
({ chain, commands, state }) => {
const { from, to } = state.selection
// merge multiple paragraphs into codeblock
if (!isActive(state, this.name) && !state.selection.empty) {
let hasParagraphsOnlySelected = true
const textArr: string[] = []
state.doc.nodesBetween(from, to, (node, pos) => {
if (node.isInline) {
return false
}
if (node.type.name !== 'paragraph') {
if (pos + 1 <= from && pos + node.nodeSize - 1 >= to) {
// skip nodes outside of the selected range
return false
} else {
// cannot merge non-paragraph nodes inside selection
hasParagraphsOnlySelected = false
return false
}
} else {
const selectedText = (node.textContent ?? '').slice(
pos + 1 > from ? 0 : from - pos - 1,
pos + node.nodeSize - 1 < to ? node.nodeSize - 1 : to - pos - 1
)
textArr.push(selectedText ?? '')
}
})
if (hasParagraphsOnlySelected && textArr.length > 1) {
return chain()
.command(({ state, tr }) => {
tr.replaceRangeWith(from, to, this.type.create(attributes, state.schema.text(textArr.join('\n'))))
return true
})
.setTextSelection({ from: from + 2, to: from + 2 })
.run()
}
}
return commands.toggleNode(this.name, 'paragraph', attributes)
}
}
},
addInputRules () {
return [
textblockTypeInputRule({
find: backtickInputRegex,
type: this.type
}),
textblockTypeInputRule({
find: tildeInputRegex,
type: this.type
})
]
},
addProseMirrorPlugins () {
return [...(this.parent?.() ?? []), LanguageSelector(this.options)]
}
@@ -73,11 +73,14 @@ export const DefaultKit = Extension.create<DefaultKitOptions>({
export async function formatLink (editor: Editor): Promise<void> {
const link = editor.getAttributes('link').href
showPopup(LinkPopup, { link }, undefined, undefined, (newLink) => {
if (newLink === '') {
editor.chain().focus().extendMarkRange('link').unsetLink().run()
} else {
editor.chain().focus().extendMarkRange('link').setLink({ href: newLink }).run()
}
// give editor some time to handle blur event
setTimeout(() => {
showPopup(LinkPopup, { link }, undefined, undefined, (newLink) => {
if (newLink === '') {
editor.chain().focus().extendMarkRange('link').unsetLink().run()
} else {
editor.chain().focus().extendMarkRange('link').setLink({ href: newLink }).run()
}
})
})
}
+3 -3
View File
@@ -38,14 +38,14 @@
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"yjs": "^13.5.52"
"yjs": "^13.6.19"
},
"dependencies": {
"@hcengineering/presentation": "^0.6.3",
"@hcengineering/platform": "^0.6.11",
"@hcengineering/core": "^0.6.32",
"@hcengineering/ui": "^0.6.15",
"@tiptap/core": "^2.2.4",
"@tiptap/pm": "^2.2.4"
"@tiptap/core": "^2.6.6",
"@tiptap/pm": "^2.6.6"
}
}
+2 -2
View File
@@ -64,8 +64,8 @@
"@hcengineering/text-editor-resources": "^0.6.0",
"@hcengineering/time": "^0.6.0",
"@hcengineering/rank": "^0.6.4",
"@tiptap/extension-task-item": "^2.2.4",
"@tiptap/extension-task-list": "^2.2.4",
"@tiptap/extension-task-item": "^2.6.6",
"@tiptap/extension-task-list": "^2.6.6",
"fast-equals": "^5.0.1",
"@hcengineering/activity": "^0.6.0",
"@hcengineering/activity-resources": "^0.6.1",
+1 -1
View File
@@ -41,6 +41,6 @@
"@hcengineering/core": "^0.6.32",
"@hcengineering/server-core": "^0.6.1",
"base64-js": "^1.5.1",
"yjs": "^13.5.52"
"yjs": "^13.6.19"
}
}
+4 -4
View File
@@ -64,11 +64,11 @@
"@hcengineering/mongo": "^0.6.1",
"@hocuspocus/server": "^2.13.5",
"@hocuspocus/transformer": "^2.13.5",
"@tiptap/core": "^2.2.4",
"@tiptap/html": "^2.2.4",
"@tiptap/core": "^2.6.6",
"@tiptap/html": "^2.6.6",
"mongodb": "^6.9.0",
"yjs": "^13.5.52",
"y-prosemirror": "^1.2.1",
"yjs": "^13.6.19",
"y-prosemirror": "^1.2.12",
"express": "^4.19.2",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
+23 -23
View File
@@ -86,29 +86,29 @@
"fast-equals": "^5.0.1",
"@hcengineering/minio": "^0.6.0",
"markdown-it": "^14.0.0",
"@tiptap/html": "^2.2.4",
"@tiptap/core": "^2.2.4",
"@tiptap/pm": "^2.2.4",
"@tiptap/starter-kit": "^2.2.4",
"@tiptap/extension-highlight": "^2.2.4",
"@tiptap/extension-placeholder": "^2.2.4",
"@tiptap/extension-mention": "^2.2.4",
"@tiptap/extension-typography": "^2.2.4",
"@tiptap/extension-link": "^2.2.4",
"@tiptap/suggestion": "^2.2.4",
"@tiptap/extension-task-list": "^2.2.4",
"@tiptap/extension-task-item": "^2.2.4",
"@tiptap/extension-collaboration": "^2.2.4",
"@tiptap/extension-collaboration-cursor": "^2.2.4",
"@tiptap/extension-code-block": "^2.2.4",
"@tiptap/extension-gapcursor": "^2.2.4",
"@tiptap/extension-heading": "^2.2.4",
"@tiptap/extension-table": "^2.2.4",
"@tiptap/extension-table-cell": "^2.2.4",
"@tiptap/extension-table-header": "^2.2.4",
"@tiptap/extension-table-row": "^2.2.4",
"@tiptap/extension-code": "^2.2.4",
"@tiptap/extension-bubble-menu": "^2.2.4",
"@tiptap/html": "^2.6.6",
"@tiptap/core": "^2.6.6",
"@tiptap/pm": "^2.6.6",
"@tiptap/starter-kit": "^2.6.6",
"@tiptap/extension-highlight": "^2.6.6",
"@tiptap/extension-placeholder": "^2.6.6",
"@tiptap/extension-mention": "^2.6.6",
"@tiptap/extension-typography": "^2.6.6",
"@tiptap/extension-link": "^2.6.6",
"@tiptap/suggestion": "^2.6.6",
"@tiptap/extension-task-list": "^2.6.6",
"@tiptap/extension-task-item": "^2.6.6",
"@tiptap/extension-collaboration": "^2.6.6",
"@tiptap/extension-collaboration-cursor": "^2.6.6",
"@tiptap/extension-code-block": "^2.6.6",
"@tiptap/extension-gapcursor": "^2.6.6",
"@tiptap/extension-heading": "^2.6.6",
"@tiptap/extension-table": "^2.6.6",
"@tiptap/extension-table-cell": "^2.6.6",
"@tiptap/extension-table-header": "^2.6.6",
"@tiptap/extension-table-row": "^2.6.6",
"@tiptap/extension-code": "^2.6.6",
"@tiptap/extension-bubble-menu": "^2.6.6",
"@hcengineering/task": "^0.6.20",
"express": "^4.19.2",
"cors": "^2.8.5",
@@ -147,8 +147,8 @@ test.describe('Documents tests', () => {
await documentContentPage.addRandomLines(5)
await documentContentPage.addContentToTheNewLine(contentLink)
await documentContentPage.addRandomLines(5)
await documentContentPage.addLinkToText(contentLink, 'test/link/123456')
await documentContentPage.checkLinkInTheText(contentLink, 'test/link/123456')
await documentContentPage.addLinkToText(contentLink, 'http://test/link/123456')
await documentContentPage.checkLinkInTheText(contentLink, 'http://test/link/123456')
})
test('Locked document and checking URL', async ({ page, context }) => {