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