@dxos/plugin-markdown 0.6.8-main.046e6cf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +15 -0
  3. package/dist/lib/browser/DocumentCard-CDWDPILF.mjs +11 -0
  4. package/dist/lib/browser/DocumentCard-CDWDPILF.mjs.map +7 -0
  5. package/dist/lib/browser/DocumentEditor-VMHFHWOQ.mjs +11 -0
  6. package/dist/lib/browser/DocumentEditor-VMHFHWOQ.mjs.map +7 -0
  7. package/dist/lib/browser/MarkdownEditor-KYUQ45PC.mjs +10 -0
  8. package/dist/lib/browser/MarkdownEditor-KYUQ45PC.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-4GGD6YJO.mjs +19 -0
  10. package/dist/lib/browser/chunk-4GGD6YJO.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-6ZL2GJCQ.mjs +177 -0
  12. package/dist/lib/browser/chunk-6ZL2GJCQ.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-DX3K37SM.mjs +86 -0
  14. package/dist/lib/browser/chunk-DX3K37SM.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-MDX3MAMP.mjs +119 -0
  16. package/dist/lib/browser/chunk-MDX3MAMP.mjs.map +7 -0
  17. package/dist/lib/browser/chunk-PZDW7KVZ.mjs +172 -0
  18. package/dist/lib/browser/chunk-PZDW7KVZ.mjs.map +7 -0
  19. package/dist/lib/browser/chunk-RETREORA.mjs +39 -0
  20. package/dist/lib/browser/chunk-RETREORA.mjs.map +7 -0
  21. package/dist/lib/browser/index.mjs +506 -0
  22. package/dist/lib/browser/index.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -0
  24. package/dist/lib/browser/meta.mjs +9 -0
  25. package/dist/lib/browser/meta.mjs.map +7 -0
  26. package/dist/lib/browser/types/index.mjs +12 -0
  27. package/dist/lib/browser/types/index.mjs.map +7 -0
  28. package/dist/lib/node/DocumentCard-SOTFILJY.cjs +32 -0
  29. package/dist/lib/node/DocumentCard-SOTFILJY.cjs.map +7 -0
  30. package/dist/lib/node/DocumentEditor-CUKHGS5R.cjs +29 -0
  31. package/dist/lib/node/DocumentEditor-CUKHGS5R.cjs.map +7 -0
  32. package/dist/lib/node/MarkdownEditor-GGFCD26C.cjs +31 -0
  33. package/dist/lib/node/MarkdownEditor-GGFCD26C.cjs.map +7 -0
  34. package/dist/lib/node/chunk-FW5O2I25.cjs +114 -0
  35. package/dist/lib/node/chunk-FW5O2I25.cjs.map +7 -0
  36. package/dist/lib/node/chunk-IUQ2SKGY.cjs +202 -0
  37. package/dist/lib/node/chunk-IUQ2SKGY.cjs.map +7 -0
  38. package/dist/lib/node/chunk-TGMR2CKU.cjs +52 -0
  39. package/dist/lib/node/chunk-TGMR2CKU.cjs.map +7 -0
  40. package/dist/lib/node/chunk-TO3FCKT7.cjs +202 -0
  41. package/dist/lib/node/chunk-TO3FCKT7.cjs.map +7 -0
  42. package/dist/lib/node/chunk-TZDYK4MV.cjs +151 -0
  43. package/dist/lib/node/chunk-TZDYK4MV.cjs.map +7 -0
  44. package/dist/lib/node/chunk-VYLYUDDI.cjs +58 -0
  45. package/dist/lib/node/chunk-VYLYUDDI.cjs.map +7 -0
  46. package/dist/lib/node/index.cjs +517 -0
  47. package/dist/lib/node/index.cjs.map +7 -0
  48. package/dist/lib/node/meta.cjs +30 -0
  49. package/dist/lib/node/meta.cjs.map +7 -0
  50. package/dist/lib/node/meta.json +1 -0
  51. package/dist/lib/node/types/index.cjs +34 -0
  52. package/dist/lib/node/types/index.cjs.map +7 -0
  53. package/dist/types/src/MarkdownPlugin.d.ts +4 -0
  54. package/dist/types/src/MarkdownPlugin.d.ts.map +1 -0
  55. package/dist/types/src/components/DocumentCard.d.ts +16 -0
  56. package/dist/types/src/components/DocumentCard.d.ts.map +1 -0
  57. package/dist/types/src/components/DocumentEditor.d.ts +14 -0
  58. package/dist/types/src/components/DocumentEditor.d.ts.map +1 -0
  59. package/dist/types/src/components/HeadingMenu.d.ts +13 -0
  60. package/dist/types/src/components/HeadingMenu.d.ts.map +1 -0
  61. package/dist/types/src/components/Layout.d.ts +6 -0
  62. package/dist/types/src/components/Layout.d.ts.map +1 -0
  63. package/dist/types/src/components/MarkdownEditor.d.ts +19 -0
  64. package/dist/types/src/components/MarkdownEditor.d.ts.map +1 -0
  65. package/dist/types/src/components/MarkdownEditor.stories.d.ts +27 -0
  66. package/dist/types/src/components/MarkdownEditor.stories.d.ts.map +1 -0
  67. package/dist/types/src/components/MarkdownSettings.d.ts +6 -0
  68. package/dist/types/src/components/MarkdownSettings.d.ts.map +1 -0
  69. package/dist/types/src/components/Toolbar.stories.d.ts +9 -0
  70. package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -0
  71. package/dist/types/src/components/index.d.ts +13 -0
  72. package/dist/types/src/components/index.d.ts.map +1 -0
  73. package/dist/types/src/extensions.d.ts +20 -0
  74. package/dist/types/src/extensions.d.ts.map +1 -0
  75. package/dist/types/src/index.d.ts +6 -0
  76. package/dist/types/src/index.d.ts.map +1 -0
  77. package/dist/types/src/meta.d.ts +15 -0
  78. package/dist/types/src/meta.d.ts.map +1 -0
  79. package/dist/types/src/translations.d.ts +29 -0
  80. package/dist/types/src/translations.d.ts.map +1 -0
  81. package/dist/types/src/types/document.d.ts +97 -0
  82. package/dist/types/src/types/document.d.ts.map +1 -0
  83. package/dist/types/src/types/index.d.ts +3 -0
  84. package/dist/types/src/types/index.d.ts.map +1 -0
  85. package/dist/types/src/types/types.d.ts +42 -0
  86. package/dist/types/src/types/types.d.ts.map +1 -0
  87. package/dist/types/src/util.d.ts +11 -0
  88. package/dist/types/src/util.d.ts.map +1 -0
  89. package/package.json +94 -0
  90. package/src/MarkdownPlugin.tsx +341 -0
  91. package/src/components/DocumentCard.tsx +107 -0
  92. package/src/components/DocumentEditor.tsx +140 -0
  93. package/src/components/HeadingMenu.tsx +46 -0
  94. package/src/components/Layout.tsx +27 -0
  95. package/src/components/MarkdownEditor.stories.tsx +55 -0
  96. package/src/components/MarkdownEditor.tsx +241 -0
  97. package/src/components/MarkdownSettings.tsx +105 -0
  98. package/src/components/Toolbar.stories.tsx +118 -0
  99. package/src/components/index.ts +21 -0
  100. package/src/extensions.tsx +175 -0
  101. package/src/index.ts +11 -0
  102. package/src/meta.tsx +19 -0
  103. package/src/translations.ts +36 -0
  104. package/src/types/document.ts +17 -0
  105. package/src/types/index.ts +6 -0
  106. package/src/types/types.ts +75 -0
  107. package/src/util.tsx +48 -0
@@ -0,0 +1,175 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { ArrowSquareDown, ArrowSquareOut, type Icon } from '@phosphor-icons/react';
6
+ import React, { type AnchorHTMLAttributes, StrictMode } from 'react';
7
+ import { createRoot } from 'react-dom/client';
8
+
9
+ import { type IntentDispatcher, NavigationAction } from '@dxos/app-framework';
10
+ import { invariant } from '@dxos/invariant';
11
+ import { fullyQualifiedId, type Query } from '@dxos/react-client/echo';
12
+ import {
13
+ type AutocompleteResult,
14
+ type Extension,
15
+ type EditorViewMode,
16
+ autocomplete,
17
+ decorateMarkdown,
18
+ linkTooltip,
19
+ typewriter,
20
+ formattingKeymap,
21
+ InputModeExtensions,
22
+ folding,
23
+ } from '@dxos/react-ui-editor';
24
+ import { getSize, mx } from '@dxos/react-ui-theme';
25
+ import { isNotFalsy, nonNullable } from '@dxos/util';
26
+
27
+ import { type DocumentType, type MarkdownSettingsProps } from './types';
28
+
29
+ export type ExtensionsOptions = {
30
+ viewMode?: EditorViewMode;
31
+ settings?: MarkdownSettingsProps;
32
+ document?: DocumentType;
33
+ debug?: boolean;
34
+ experimental?: boolean;
35
+ numberedHeadings?: boolean;
36
+ folding?: boolean;
37
+ query?: Query<DocumentType>;
38
+ dispatch?: IntentDispatcher;
39
+ };
40
+
41
+ /**
42
+ * Create extension instances for editor.
43
+ */
44
+ export const createBaseExtensions = ({
45
+ viewMode,
46
+ settings,
47
+ document,
48
+ query,
49
+ dispatch,
50
+ }: ExtensionsOptions): Extension[] => {
51
+ const extensions: Extension[] = [
52
+ //
53
+ // Common.
54
+ //
55
+ ...(viewMode === 'source'
56
+ ? []
57
+ : [
58
+ decorateMarkdown({
59
+ selectionChangeDelay: 100,
60
+ numberedHeadings: settings?.numberedHeadings ? { from: 2 } : undefined,
61
+ // TODO(wittjosiah): For internal links, consider ignoring the link text and rendering the label of the object being linked to.
62
+ renderLinkButton:
63
+ dispatch && document
64
+ ? onRenderLink((id: string) => {
65
+ void dispatch({
66
+ action: NavigationAction.ADD_TO_ACTIVE,
67
+ data: {
68
+ id,
69
+ part: 'main',
70
+ pivotId: fullyQualifiedId(document),
71
+ scrollIntoView: true,
72
+ },
73
+ });
74
+ })
75
+ : undefined,
76
+ }),
77
+ settings?.folding && folding(),
78
+ ].filter(isNotFalsy)),
79
+ formattingKeymap(),
80
+ linkTooltip(renderLinkTooltip),
81
+ ];
82
+
83
+ //
84
+ // Editor mode.
85
+ //
86
+ if (settings?.editorInputMode) {
87
+ const extension = InputModeExtensions[settings.editorInputMode];
88
+ if (extension) {
89
+ extensions.push(extension);
90
+ }
91
+ }
92
+
93
+ //
94
+ // Autocomplete object links.
95
+ //
96
+ if (query) {
97
+ extensions.push(
98
+ autocomplete({
99
+ onSearch: (text: string) => {
100
+ // TODO query
101
+ // TODO(burdon): Specify filter (e.g., stack).
102
+ return query.objects
103
+ .map<AutocompleteResult | undefined>((object) =>
104
+ object.name?.length && object.id !== document?.id
105
+ ? {
106
+ label: object.name,
107
+ // TODO(burdon): Factor out URL builder.
108
+ apply: `[${object.name}](/${fullyQualifiedId(object)})`,
109
+ }
110
+ : undefined,
111
+ )
112
+ .filter(nonNullable);
113
+ },
114
+ }),
115
+ );
116
+ }
117
+
118
+ if (settings?.debug) {
119
+ const items = settings.typewriter ?? '';
120
+ extensions.push(...[items ? typewriter({ items: items.split(/[,\n]/) }) : undefined].filter(nonNullable));
121
+ }
122
+
123
+ if (settings?.experimental) {
124
+ extensions.push(...[].filter(nonNullable));
125
+ }
126
+
127
+ return extensions;
128
+ };
129
+
130
+ // TODO(burdon): Factor out style.
131
+ const hover = 'rounded-sm text-primary-600 hover:text-primary-500 dark:text-primary-300 hover:dark:text-primary-200';
132
+
133
+ const onRenderLink = (onSelectObject: (id: string) => void) => (el: Element, url: string) => {
134
+ // TODO(burdon): Formalize/document internal link format.
135
+ const isInternal =
136
+ url.startsWith('/') ||
137
+ // TODO(wittjosiah): This should probably be parsed out on paste?
138
+ url.startsWith(window.location.origin);
139
+
140
+ const options: AnchorHTMLAttributes<any> = isInternal
141
+ ? {
142
+ onClick: () => {
143
+ const qualifiedId = url.split('/').at(-1);
144
+ invariant(qualifiedId, 'Invalid link format.');
145
+ onSelectObject(qualifiedId);
146
+ },
147
+ }
148
+ : {
149
+ href: url,
150
+ rel: 'noreferrer',
151
+ target: '_blank',
152
+ };
153
+
154
+ const LinkIcon: Icon = isInternal ? ArrowSquareDown : ArrowSquareOut;
155
+
156
+ createRoot(el).render(
157
+ <StrictMode>
158
+ <a {...options} className={hover}>
159
+ <LinkIcon weight='bold' className={mx(getSize(4), 'inline-block leading-none mis-1 cursor-pointer')} />
160
+ </a>
161
+ </StrictMode>,
162
+ );
163
+ };
164
+
165
+ const renderLinkTooltip = (el: Element, url: string) => {
166
+ const web = new URL(url);
167
+ createRoot(el).render(
168
+ <StrictMode>
169
+ <a href={url} target='_blank' rel='noreferrer' className={hover}>
170
+ {web.origin}
171
+ <ArrowSquareOut weight='bold' className={mx(getSize(4), 'inline-block leading-none mis-1 cursor-pointer')} />
172
+ </a>
173
+ </StrictMode>,
174
+ );
175
+ };
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { MarkdownPlugin } from './MarkdownPlugin';
6
+
7
+ export default MarkdownPlugin;
8
+
9
+ export * from './MarkdownPlugin';
10
+ export * from './types';
11
+ export * from './util';
package/src/meta.tsx ADDED
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type IconProps, TextAa } from '@phosphor-icons/react';
6
+ import React from 'react';
7
+
8
+ import { pluginMeta } from '@dxos/app-framework';
9
+
10
+ export const MARKDOWN_PLUGIN = 'dxos.org/plugin/markdown';
11
+
12
+ export default pluginMeta({
13
+ id: MARKDOWN_PLUGIN,
14
+ name: 'Editor',
15
+ description: 'Markdown text editor.',
16
+ homePage: 'https://github.com/dxos/dxos/tree/main/packages/apps/plugins/plugin-markdown',
17
+ iconComponent: (props: IconProps) => <TextAa {...props} />,
18
+ iconSymbol: 'ph--text-aa--regular',
19
+ });
@@ -0,0 +1,36 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { MARKDOWN_PLUGIN } from './meta';
6
+
7
+ export default [
8
+ {
9
+ 'en-US': {
10
+ [MARKDOWN_PLUGIN]: {
11
+ 'plugin name': 'Editor',
12
+ 'create stack section label': 'Create document',
13
+ 'document title placeholder': 'New document',
14
+ 'choose markdown from space dialog title': 'Choose one or more documents to add',
15
+ // TODO(burdon): Style-guide for user-facing text (e.g., hints, questions, capitalization, etc.)
16
+ 'empty choose markdown from space message': 'None available; try creating a new one instead?',
17
+ 'chooser done label': 'Add selected',
18
+ 'create document label': 'Create document',
19
+ 'editor placeholder': 'New document',
20
+ 'editor input mode label': 'Editor input mode',
21
+ 'select editor input mode placeholder': 'Select editor input mode…',
22
+ 'settings editor input mode default label': 'Default',
23
+ 'settings editor input mode vim label': 'Vim',
24
+ 'settings editor input mode vscode label': 'VS Code',
25
+ 'settings toolbar label': 'Show toolbar',
26
+ 'settings numbered headings label': 'Numbered headings',
27
+ 'settings folding label': 'Folding',
28
+ 'settings experimental label': 'Enable experimental features',
29
+ 'settings debug label': 'Enable debugging features',
30
+ 'settings debug placeholder': 'Typewriter script...',
31
+ 'toggle view mode label': 'Toggle read-only',
32
+ 'default view mode label': 'Default view mode',
33
+ },
34
+ },
35
+ },
36
+ ];
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { ref, S, TypedObject } from '@dxos/echo-schema';
6
+ import { ThreadType } from '@dxos/plugin-space/types';
7
+
8
+ export class TextType extends TypedObject({ typename: 'dxos.org/type/Text', version: '0.1.0' })({
9
+ content: S.String,
10
+ }) {}
11
+
12
+ export class DocumentType extends TypedObject({ typename: 'dxos.org/type/Document', version: '0.1.0' })({
13
+ name: S.optional(S.String),
14
+ fallbackName: S.optional(S.String),
15
+ content: ref(TextType),
16
+ threads: S.mutable(S.Array(ref(ThreadType))),
17
+ }) {}
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './document';
6
+ export * from './types';
@@ -0,0 +1,75 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import type {
6
+ GraphBuilderProvides,
7
+ GraphSerializerProvides,
8
+ IntentResolverProvides,
9
+ MetadataRecordsProvides,
10
+ SettingsProvides,
11
+ SurfaceProvides,
12
+ TranslationsProvides,
13
+ } from '@dxos/app-framework';
14
+ import { type SchemaProvides } from '@dxos/plugin-client';
15
+ import { type Extension, type EditorInputMode, type EditorViewMode } from '@dxos/react-ui-editor';
16
+
17
+ import { type DocumentType } from './document';
18
+ import { MARKDOWN_PLUGIN } from '../meta';
19
+
20
+ const MARKDOWN_ACTION = `${MARKDOWN_PLUGIN}/action`;
21
+
22
+ export enum MarkdownAction {
23
+ CREATE = `${MARKDOWN_ACTION}/create`,
24
+ SET_VIEW_MODE = `${MARKDOWN_ACTION}/set-view-mode`,
25
+ }
26
+
27
+ export type MarkdownProperties = Record<string, any>;
28
+
29
+ export type ExtensionsProvider = (props: { document?: DocumentType }) => Extension[];
30
+
31
+ export type OnChange = (text: string) => void;
32
+
33
+ export type MarkdownExtensionProvides = {
34
+ markdown: {
35
+ extensions: ExtensionsProvider;
36
+ };
37
+ };
38
+
39
+ // TODO(wittjosiah): Factor out to graph plugin?
40
+ type StackProvides = {
41
+ stack: {
42
+ creators?: Record<string, any>[];
43
+ };
44
+ };
45
+
46
+ export type MarkdownPluginState = {
47
+ // Codemirror extensions provided by other plugins.
48
+ extensionProviders: NonNullable<ExtensionsProvider>[];
49
+
50
+ // TODO(burdon): Extend view mode per document to include scroll position, etc.
51
+ // View mode per document.
52
+ viewMode: { [key: string]: EditorViewMode };
53
+ };
54
+
55
+ export type MarkdownSettingsProps = {
56
+ defaultViewMode: EditorViewMode;
57
+ editorInputMode?: EditorInputMode;
58
+ experimental?: boolean;
59
+ debug?: boolean;
60
+ toolbar?: boolean;
61
+ typewriter?: string;
62
+ // TODO(burdon): Per document settings.
63
+ numberedHeadings?: boolean;
64
+ folding?: boolean;
65
+ };
66
+
67
+ export type MarkdownPluginProvides = SurfaceProvides &
68
+ IntentResolverProvides &
69
+ GraphBuilderProvides &
70
+ GraphSerializerProvides &
71
+ MetadataRecordsProvides &
72
+ SettingsProvides<MarkdownSettingsProps> &
73
+ TranslationsProvides &
74
+ SchemaProvides &
75
+ StackProvides;
package/src/util.tsx ADDED
@@ -0,0 +1,48 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Plugin } from '@dxos/app-framework';
6
+ import { debounce } from '@dxos/async';
7
+ import { type TypedObjectSerializer } from '@dxos/plugin-space/types';
8
+ import { create, createEchoObject, isEchoObject, loadObjectReferences } from '@dxos/react-client/echo';
9
+
10
+ import { DocumentType, type MarkdownProperties, type MarkdownExtensionProvides, TextType } from './types';
11
+
12
+ export const isMarkdownProperties = (data: unknown): data is MarkdownProperties =>
13
+ isEchoObject(data)
14
+ ? true
15
+ : data && typeof data === 'object'
16
+ ? 'title' in data && typeof data.title === 'string'
17
+ : false;
18
+
19
+ type MarkdownExtensionPlugin = Plugin<MarkdownExtensionProvides>;
20
+
21
+ export const markdownExtensionPlugins = (plugins: Plugin[]): MarkdownExtensionPlugin[] => {
22
+ return (plugins as MarkdownExtensionPlugin[]).filter((plugin) => Boolean(plugin.provides?.markdown));
23
+ };
24
+
25
+ const nonTitleChars = /[^\w ]/g;
26
+
27
+ export const getFallbackName = (content: string) => {
28
+ return content.substring(0, 31).split('\n')[0].replaceAll(nonTitleChars, '').trim();
29
+ };
30
+
31
+ export const setFallbackName = debounce((doc: DocumentType, content: string) => {
32
+ const name = getFallbackName(content);
33
+ if (doc.fallbackName !== name) {
34
+ doc.fallbackName = name;
35
+ }
36
+ }, 200);
37
+
38
+ export const serializer: TypedObjectSerializer<DocumentType> = {
39
+ serialize: async ({ object }): Promise<string> => {
40
+ const content = await loadObjectReferences(object, (doc) => doc.content);
41
+ return JSON.stringify({ name: object.name, content: content.content });
42
+ },
43
+
44
+ deserialize: async ({ content: serialized }) => {
45
+ const { name, content } = JSON.parse(serialized);
46
+ return createEchoObject(create(DocumentType, { name, content: create(TextType, { content }), threads: [] }));
47
+ },
48
+ };