diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json
index 6ae44422dc4..42ffdaed0fc 100644
--- a/common/config/rush/command-line.json
+++ b/common/config/rush/command-line.json
@@ -247,6 +247,14 @@
"safeForSimultaneousRushProcesses": true,
"shellCommand": "./common/scripts/docker.sh"
},
+ {
+ "commandKind": "global",
+ "name": "docker-server",
+ "summary": "Build docker with platform",
+ "description": "use to build all docker containers required for platform",
+ "safeForSimultaneousRushProcesses": true,
+ "shellCommand": "./common/scripts/docker-server.sh && rush docker:up"
+ },
{
"commandKind": "global",
"name": "docker:up",
diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml
index dbe07c961b5..f10d1f1fd02 100644
--- a/common/config/rush/pnpm-lock.yaml
+++ b/common/config/rush/pnpm-lock.yaml
@@ -6277,7 +6277,7 @@ packages:
version: 0.0.0
'@rush-temp/view-resources@file:projects/view-resources.tgz':
- resolution: {integrity: sha512-L3IzKi6Lo78o7lDzz+V2Ap+7lStsOm0mhIuaOtFk/UQQjSHp8CVcN0RwubG+KEHV/zv2li/pXTjNes3Ayg+Xsw==, tarball: file:projects/view-resources.tgz}
+ resolution: {integrity: sha512-iBRhD5GGptL67zFov48Awf3MSVsnV6uctkmKFRv7EzGXUXpbS5E5uv3mS0TUlwEV2GCGIxejsbnQ8tJrHYqMNw==, tarball: file:projects/view-resources.tgz}
version: 0.0.0
'@rush-temp/view@file:projects/view.tgz':
@@ -34768,6 +34768,7 @@ snapshots:
'@hcengineering/platform-rig': 0.7.19(@babel/core@7.23.9)(postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.9.3)))(postcss@8.5.3)(sass@1.93.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.9.3))
'@hcengineering/query': 0.7.6
'@hcengineering/text': 0.7.5(prosemirror-inputrules@1.4.0)(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.2)
+ '@hcengineering/text-markdown': 0.7.5
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.9.3))(eslint@8.56.0)(typescript@5.9.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.9.3)
diff --git a/common/scripts/docker-server.sh b/common/scripts/docker-server.sh
new file mode 100755
index 00000000000..ffc0f2dde62
--- /dev/null
+++ b/common/scripts/docker-server.sh
@@ -0,0 +1,3 @@
+rush docker:build -p 20 \
+--to @hcengineering/pod-server \
+--to @hcengineering/tool
diff --git a/models/card/src/actions.ts b/models/card/src/actions.ts
index 8023ffd44d1..4b2f99ed79e 100644
--- a/models/card/src/actions.ts
+++ b/models/card/src/actions.ts
@@ -101,6 +101,24 @@ export function createActions (builder: Builder): void {
card.action.UnsetParent
)
+ createAction(builder, {
+ action: view.actionImpl.CopyDocumentMarkdown,
+ actionProps: {
+ contentClass: card.class.Card,
+ contentField: 'content'
+ },
+ label: view.string.CopyDocumentMarkdown,
+ icon: view.icon.Print,
+ input: 'any',
+ category: card.category.Card,
+ target: card.class.Card,
+ query: {},
+ context: {
+ mode: ['context', 'browser'],
+ group: 'tools'
+ }
+ })
+
createAction(builder, {
action: view.actionImpl.ShowPopup,
actionProps: {
diff --git a/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte b/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte
index e181c3085f3..81b73719e29 100644
--- a/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte
+++ b/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte
@@ -458,6 +458,7 @@
onContentError: ({ error, disableCollaboration }) => {
disableCollaboration()
contentError = true
+ console.error(error)
Analytics.handleError(error)
}
})
diff --git a/plugins/view-assets/assets/icons.svg b/plugins/view-assets/assets/icons.svg
index 2f91c9eef9c..c3b816d1acd 100644
--- a/plugins/view-assets/assets/icons.svg
+++ b/plugins/view-assets/assets/icons.svg
@@ -275,4 +275,7 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/view-assets/lang/cs.json b/plugins/view-assets/lang/cs.json
index abf6949290d..62c219468ca 100644
--- a/plugins/view-assets/lang/cs.json
+++ b/plugins/view-assets/lang/cs.json
@@ -112,6 +112,7 @@
"Or": "Nebo",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "Kopírovat do schránky",
+ "CopyDocumentMarkdown": "Kopírovat jako Markdown",
"PublicView": "Veřejné",
"ChooseIcon": "Vybrat ikonu",
diff --git a/plugins/view-assets/lang/de.json b/plugins/view-assets/lang/de.json
index 970af37c99c..009b083074e 100644
--- a/plugins/view-assets/lang/de.json
+++ b/plugins/view-assets/lang/de.json
@@ -112,6 +112,7 @@
"Or": "Oder",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "In die Zwischenablage kopieren",
+ "CopyDocumentMarkdown": "Als Markdown kopieren",
"PublicView": "Öffentlich",
"ChooseIcon": "Symbol wählen",
diff --git a/plugins/view-assets/lang/en.json b/plugins/view-assets/lang/en.json
index da9dd3a8468..c622fb96ddc 100644
--- a/plugins/view-assets/lang/en.json
+++ b/plugins/view-assets/lang/en.json
@@ -112,6 +112,7 @@
"Or": "Or",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "Copy to clipboard",
+ "CopyDocumentMarkdown": "Copy as Markdown",
"PublicView": "Public",
"ChooseIcon": "Choose icon",
diff --git a/plugins/view-assets/lang/es.json b/plugins/view-assets/lang/es.json
index 18ff2e46a1a..e0ff34bac45 100644
--- a/plugins/view-assets/lang/es.json
+++ b/plugins/view-assets/lang/es.json
@@ -108,6 +108,7 @@
"Or": "O",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "Copiar al portapapeles",
+ "CopyDocumentMarkdown": "Copiar como Markdown",
"PublicView": "Público",
"ChooseIcon": "Elegir icono",
"IconColor": "Elegir color del icono",
diff --git a/plugins/view-assets/lang/fr.json b/plugins/view-assets/lang/fr.json
index 6bea876aba4..536206bf29b 100644
--- a/plugins/view-assets/lang/fr.json
+++ b/plugins/view-assets/lang/fr.json
@@ -108,6 +108,7 @@
"Or": "Ou",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "Copier dans le presse-papiers",
+ "CopyDocumentMarkdown": "Copier en Markdown",
"PublicView": "Vue publique",
"ChooseIcon": "Choisir une icône",
"IconColor": "Choisir la couleur de l'icône",
diff --git a/plugins/view-assets/lang/it.json b/plugins/view-assets/lang/it.json
index 5b408a036e2..41a6d5c8d7b 100644
--- a/plugins/view-assets/lang/it.json
+++ b/plugins/view-assets/lang/it.json
@@ -108,6 +108,7 @@
"Or": "O",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "Copia negli appunti",
+ "CopyDocumentMarkdown": "Copia come Markdown",
"PublicView": "Vista pubblica",
"ChooseIcon": "Scegli icona",
"IconColor": "Scegli colore icona",
diff --git a/plugins/view-assets/lang/ja.json b/plugins/view-assets/lang/ja.json
index 128b837fc7a..a12173afe3d 100644
--- a/plugins/view-assets/lang/ja.json
+++ b/plugins/view-assets/lang/ja.json
@@ -108,6 +108,7 @@
"Or": "または",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "クリップボードにコピー",
+ "CopyDocumentMarkdown": "Markdownとしてコピー",
"PublicView": "公開",
"ChooseIcon": "アイコンを選択",
"IconColor": "アイコンの色を選択",
diff --git a/plugins/view-assets/lang/pt.json b/plugins/view-assets/lang/pt.json
index 1d7ffbb0161..16d77361323 100644
--- a/plugins/view-assets/lang/pt.json
+++ b/plugins/view-assets/lang/pt.json
@@ -108,6 +108,7 @@
"Or": "Ou",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "Copiar para área de transferência",
+ "CopyDocumentMarkdown": "Copiar como Markdown",
"PublicView": "Público",
"ChooseIcon": "Escolher ícone",
"IconColor": "Escolher cor do ícone",
diff --git a/plugins/view-assets/lang/ru.json b/plugins/view-assets/lang/ru.json
index e57f8948117..59282cfff15 100644
--- a/plugins/view-assets/lang/ru.json
+++ b/plugins/view-assets/lang/ru.json
@@ -108,6 +108,7 @@
"Or": "Или",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "Скопировать в буфер обмена",
+ "CopyDocumentMarkdown": "Скопировать как Markdown",
"PublicView": "Публичный",
"ChooseIcon": "Выбрать иконку",
diff --git a/plugins/view-assets/lang/tr.json b/plugins/view-assets/lang/tr.json
index d2f3f3440a6..04628856489 100644
--- a/plugins/view-assets/lang/tr.json
+++ b/plugins/view-assets/lang/tr.json
@@ -108,6 +108,7 @@
"Or": "Veya",
"HyperlinkPlaceholder": "https://ornek.com",
"CopyToClipboard": "Panoya kopyala",
+ "CopyDocumentMarkdown": "Markdown olarak kopyala",
"PublicView": "Genel",
"ChooseIcon": "İkon seç",
"IconColor": "İkon rengi seç",
diff --git a/plugins/view-assets/lang/zh.json b/plugins/view-assets/lang/zh.json
index 3f41241c29e..35bca5815d7 100644
--- a/plugins/view-assets/lang/zh.json
+++ b/plugins/view-assets/lang/zh.json
@@ -112,6 +112,7 @@
"Or": "或",
"HyperlinkPlaceholder": "https://jappleseed.com",
"CopyToClipboard": "复制到剪贴板",
+ "CopyDocumentMarkdown": "复制为 Markdown",
"PublicView": "公开视图",
"ChooseIcon": "选择图标",
diff --git a/plugins/view-assets/src/index.ts b/plugins/view-assets/src/index.ts
index 63ed1612e4f..3e845fc7050 100644
--- a/plugins/view-assets/src/index.ts
+++ b/plugins/view-assets/src/index.ts
@@ -67,7 +67,8 @@ loadMetadata(view.icon, {
Feather: `${icons}#feather`,
MasterDetail: `${icons}#master-detail`,
Tree: `${icons}#tree`,
- Document: `${icons}#document`
+ Document: `${icons}#document`,
+ Print: `${icons}#print`
})
loadMetadata(core.icon, {
TypeString: `${icons}#string`,
diff --git a/plugins/view-resources/package.json b/plugins/view-resources/package.json
index 470dbc628c2..910c33b2058 100644
--- a/plugins/view-resources/package.json
+++ b/plugins/view-resources/package.json
@@ -49,6 +49,7 @@
"@hcengineering/ui": "^0.7.0",
"@hcengineering/task": "^0.7.0",
"@hcengineering/text": "^0.7.5",
+ "@hcengineering/text-markdown": "^0.7.5",
"@hcengineering/preference": "^0.7.0",
"@hcengineering/notification": "^0.7.0",
"@hcengineering/presentation": "^0.7.0",
diff --git a/plugins/view-resources/src/actionImpl.ts b/plugins/view-resources/src/actionImpl.ts
index e7160919486..8bc89c50f99 100644
--- a/plugins/view-resources/src/actionImpl.ts
+++ b/plugins/view-resources/src/actionImpl.ts
@@ -1,4 +1,6 @@
-import {
+import contact from '@hcengineering/contact'
+import core, {
+ type Blob,
type Class,
type Doc,
type DocumentQuery,
@@ -6,17 +8,21 @@ import {
type Ref,
type Space,
type TxResult,
- getCurrentAccount
+ getCurrentAccount,
+ makeDocCollabId
} from '@hcengineering/core'
import { type Asset, type IntlString, type Resource, getResource } from '@hcengineering/platform'
import {
- MessageBox,
- getClient,
- updateAttribute,
type ContextStore,
+ MessageBox,
contextStore,
- copyTextToClipboardOldBrowser
+ copyTextToClipboardOldBrowser,
+ getClient,
+ getMarkup,
+ updateAttribute
} from '@hcengineering/presentation'
+import { markupToJSON } from '@hcengineering/text'
+import { markupToMarkdown } from '@hcengineering/text-markdown'
import {
type AnyComponent,
type AnySvelteComponent,
@@ -24,10 +30,12 @@ import {
type PopupPosAlignment,
closeTooltip,
isPopupPosAlignment,
+ locationToUrl,
navigate,
showPanel,
showPopup
} from '@hcengineering/ui'
+import { get } from 'svelte/store'
import MoveView from './components/Move.svelte'
import view from './plugin'
import {
@@ -36,13 +44,10 @@ import {
type SelectionStore,
focusStore,
previewDocument,
- selectionStore,
- selectionLimit
+ selectionLimit,
+ selectionStore
} from './selection'
import { deleteObjects, getObjectId, getObjectLinkFragment, restrictionStore } from './utils'
-import contact from '@hcengineering/contact'
-import { locationToUrl } from '@hcengineering/ui'
-import { get } from 'svelte/store'
/**
* Action to be used for copying text to clipboard.
@@ -65,21 +70,20 @@ async function CopyTextToClipboard (
}
): Promise {
const getText = await getResource(props.textProvider)
+ const text = Array.isArray(doc)
+ ? (await Promise.all(doc.map(async (d) => await getText(d, props.props)))).join(',')
+ : await getText(doc, props.props)
+ await copyText(text, 'text/plain')
+}
+
+async function copyText (text: any, contentType: string = 'text/plain'): Promise {
try {
- // Safari specific behavior
- // see https://bugs.webkit.org/show_bug.cgi?id=222262
- const text = Array.isArray(doc)
- ? (await Promise.all(doc.map(async (d) => await getText(d, props.props)))).join(',')
- : await getText(doc, props.props)
const clipboardItem = new ClipboardItem({
'text/plain': text
})
await navigator.clipboard.write([clipboardItem])
} catch {
// Fallback to default clipboard API implementation
- const text = Array.isArray(doc)
- ? (await Promise.all(doc.map(async (d) => await getText(d, props.props)))).join(',')
- : await getText(doc, props.props)
if (navigator.clipboard != null && typeof navigator.clipboard.writeText === 'function') {
await navigator.clipboard.writeText(text)
} else copyTextToClipboardOldBrowser(text)
@@ -589,6 +593,41 @@ async function getPopupAlignment (
}
}
+async function CopyDocumentMarkdown (
+ doc: Doc | Doc[],
+ evt: Event,
+ props: {
+ contentClass: Ref>
+ contentField: string
+ }
+): Promise {
+ const docs = Array.isArray(doc) ? doc : doc !== undefined ? [doc] : []
+ if (docs.length !== 1) {
+ return
+ }
+ const client = getClient()
+ const hierarchy = client.getHierarchy()
+ const contentClass = hierarchy.getClass(props.contentClass)
+ if (contentClass == null) {
+ return
+ }
+ const contentField = hierarchy.findAttribute(props.contentClass, props.contentField)
+ if (contentField == null) {
+ return
+ }
+ if (contentField.type._class === core.class.TypeCollaborativeDoc) {
+ const content = await getMarkup(
+ makeDocCollabId(docs[0], props.contentField),
+ (docs[0] as any)[props.contentField] as Ref
+ )
+ if (content !== null) {
+ const jsonMarkup = markupToJSON(content)
+ const contentJson = markupToMarkdown(jsonMarkup)
+ await copyText(contentJson, 'text/markdown')
+ }
+ }
+}
+
/**
* @public
*/
@@ -616,5 +655,6 @@ export const actionImpl = {
ShowPopup,
ShowEditor,
ValueSelector,
- AttributeSelector
+ AttributeSelector,
+ CopyDocumentMarkdown
}
diff --git a/plugins/view/src/index.ts b/plugins/view/src/index.ts
index 4706644685d..b612783342d 100644
--- a/plugins/view/src/index.ts
+++ b/plugins/view/src/index.ts
@@ -156,7 +156,8 @@ const view = plugin(viewId, {
OpenInNewTab: '' as Ref,
RemoveRelation: '' as Ref,
- CopyLink: '' as Ref>
+ CopyLink: '' as Ref>,
+ CopyDocumentMarkdown: '' as Ref>
},
viewlet: {
Table: '' as Ref,
@@ -250,7 +251,8 @@ const view = plugin(viewId, {
Icon: '' as IntlString,
Select: '' as IntlString,
Color: '' as IntlString,
- AutomationOnly: '' as IntlString
+ AutomationOnly: '' as IntlString,
+ CopyDocumentMarkdown: '' as IntlString
},
icon: {
Table: '' as Asset,
@@ -301,7 +303,8 @@ const view = plugin(viewId, {
Feather: '' as Asset,
MasterDetail: '' as Asset,
Tree: '' as Asset,
- Document: '' as Asset
+ Document: '' as Asset,
+ Print: '' as Asset
},
category: {
General: '' as Ref,
@@ -344,6 +347,10 @@ const view = plugin(viewId, {
textProvider: Resource<(doc: Doc, props: Record) => Promise>
props?: Record
}>,
+ CopyDocumentMarkdown: '' as ViewAction<{
+ contentClass: Ref>
+ contentField: string
+ }>,
UpdateDocument: '' as ViewAction<{
key: string
value: any