mirror of
https://github.com/hcengineering/platform.git
synced 2026-10-01 22:15:04 +02:00
Merge remote-tracking branch 'origin/develop' into staging
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Generated
+469
-537
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
@@ -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": {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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} />
|
||||
|
||||
+8
-1
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
Reference in New Issue
Block a user