@dxos/plugin-markdown 0.8.4-main.5ea62a8 → 0.8.4-main.72ec0f3
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.
- package/dist/lib/browser/MarkdownCard-JYMDPKV5.mjs +12 -0
- package/dist/lib/browser/MarkdownContainer-Y75XSVBX.mjs +15 -0
- package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs → anchor-sort-3HGPGCOO.mjs} +6 -7
- package/dist/lib/browser/anchor-sort-3HGPGCOO.mjs.map +7 -0
- package/dist/lib/browser/{app-graph-serializer-OX62DNPT.mjs → app-graph-serializer-POZN234F.mjs} +10 -10
- package/dist/lib/browser/app-graph-serializer-POZN234F.mjs.map +7 -0
- package/dist/lib/browser/blueprint-definition-GIPKFDY5.mjs +13 -0
- package/dist/lib/browser/blueprint-definition-GIPKFDY5.mjs.map +7 -0
- package/dist/lib/browser/{chunk-Z7P6JGGW.mjs → chunk-22XSSNBS.mjs} +7 -4
- package/dist/lib/browser/{chunk-Z7P6JGGW.mjs.map → chunk-22XSSNBS.mjs.map} +2 -2
- package/dist/lib/browser/chunk-2MLGSYRN.mjs +20 -0
- package/dist/lib/browser/chunk-2MLGSYRN.mjs.map +7 -0
- package/dist/lib/browser/{chunk-ODB2PTBP.mjs → chunk-BQTYJOFB.mjs} +4 -4
- package/dist/lib/browser/chunk-BQTYJOFB.mjs.map +7 -0
- package/dist/lib/browser/{chunk-LAVZ2W6X.mjs → chunk-GH6GQSBL.mjs} +9 -8
- package/dist/lib/browser/chunk-GH6GQSBL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-OY6CGPOO.mjs → chunk-IBCHVMZW.mjs} +2 -2
- package/dist/lib/browser/{chunk-OY6CGPOO.mjs.map → chunk-IBCHVMZW.mjs.map} +2 -2
- package/dist/lib/browser/chunk-K3LXOU3E.mjs +827 -0
- package/dist/lib/browser/chunk-K3LXOU3E.mjs.map +7 -0
- package/dist/lib/browser/chunk-PBJLFIOX.mjs +96 -0
- package/dist/lib/browser/chunk-PBJLFIOX.mjs.map +7 -0
- package/dist/lib/browser/{chunk-BEE7VQPU.mjs → chunk-QYSEJ5GP.mjs} +13 -12
- package/dist/lib/browser/chunk-QYSEJ5GP.mjs.map +7 -0
- package/dist/lib/browser/chunk-Y53FQREH.mjs +150 -0
- package/dist/lib/browser/chunk-Y53FQREH.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +24 -16
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-WDDH56JC.mjs → intent-resolver-Z5L7TXUK.mjs} +8 -8
- package/dist/lib/browser/intent-resolver-Z5L7TXUK.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-LN2XK2UN.mjs → react-surface-GO5ZOKNN.mjs} +59 -63
- package/dist/lib/browser/react-surface-GO5ZOKNN.mjs.map +7 -0
- package/dist/lib/browser/{settings-AABBTB4Q.mjs → settings-TZUDB5EW.mjs} +5 -5
- package/dist/lib/browser/{settings-AABBTB4Q.mjs.map → settings-TZUDB5EW.mjs.map} +1 -1
- package/dist/lib/browser/{state-FTHQQX7V.mjs → state-BTUKVZHY.mjs} +5 -5
- package/dist/lib/browser/{state-FTHQQX7V.mjs.map → state-BTUKVZHY.mjs.map} +1 -1
- package/dist/lib/browser/toolkit.mjs +13 -0
- package/dist/lib/browser/toolkit.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -2
- package/dist/lib/node-esm/MarkdownCard-ZXPJLUYO.mjs +13 -0
- package/dist/lib/node-esm/MarkdownCard-ZXPJLUYO.mjs.map +7 -0
- package/dist/lib/node-esm/MarkdownContainer-YRDSRDCS.mjs +16 -0
- package/dist/lib/node-esm/MarkdownContainer-YRDSRDCS.mjs.map +7 -0
- package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs → anchor-sort-PCDXEBJ2.mjs} +6 -7
- package/dist/lib/node-esm/anchor-sort-PCDXEBJ2.mjs.map +7 -0
- package/dist/lib/node-esm/{app-graph-serializer-56TD3BMX.mjs → app-graph-serializer-NF65JYAS.mjs} +10 -10
- package/dist/lib/node-esm/app-graph-serializer-NF65JYAS.mjs.map +7 -0
- package/dist/lib/node-esm/blueprint-definition-ENKJZYQS.mjs +14 -0
- package/dist/lib/node-esm/blueprint-definition-ENKJZYQS.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs → chunk-AMHACOXW.mjs} +7 -4
- package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs.map → chunk-AMHACOXW.mjs.map} +2 -2
- package/dist/lib/node-esm/chunk-CT7CFX5G.mjs +828 -0
- package/dist/lib/node-esm/chunk-CT7CFX5G.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs → chunk-GMMVSXQ6.mjs} +2 -2
- package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs.map → chunk-GMMVSXQ6.mjs.map} +2 -2
- package/dist/lib/node-esm/chunk-HAIEWPU7.mjs +151 -0
- package/dist/lib/node-esm/chunk-HAIEWPU7.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-JPXFCBC4.mjs → chunk-KCHUTL3Q.mjs} +8 -8
- package/dist/lib/node-esm/chunk-KCHUTL3Q.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-FXILAQ5F.mjs → chunk-NGYJNQ6A.mjs} +13 -12
- package/dist/lib/node-esm/chunk-NGYJNQ6A.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-PIOOG7A5.mjs +97 -0
- package/dist/lib/node-esm/chunk-PIOOG7A5.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-O6EXWGGS.mjs → chunk-PLZ7EVCT.mjs} +9 -8
- package/dist/lib/node-esm/chunk-PLZ7EVCT.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-VCG2U522.mjs → chunk-SHAMSMKQ.mjs} +4 -4
- package/dist/lib/node-esm/chunk-SHAMSMKQ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +24 -16
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/{intent-resolver-2I5HKCUU.mjs → intent-resolver-6B3PFQ5F.mjs} +8 -8
- package/dist/lib/node-esm/intent-resolver-6B3PFQ5F.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{react-surface-DJGGKYBD.mjs → react-surface-I46BPCWT.mjs} +59 -63
- package/dist/lib/node-esm/react-surface-I46BPCWT.mjs.map +7 -0
- package/dist/lib/node-esm/{settings-CXGR6DH4.mjs → settings-CJ3T5EX4.mjs} +5 -5
- package/dist/lib/node-esm/{settings-CXGR6DH4.mjs.map → settings-CJ3T5EX4.mjs.map} +1 -1
- package/dist/lib/node-esm/{state-NWMQ3XAI.mjs → state-K6EH7SRZ.mjs} +5 -5
- package/dist/lib/node-esm/{state-NWMQ3XAI.mjs.map → state-K6EH7SRZ.mjs.map} +1 -1
- package/dist/lib/node-esm/toolkit.mjs +14 -0
- package/dist/lib/node-esm/toolkit.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +2 -2
- package/dist/types/src/MarkdownPlugin.d.ts +1 -1
- package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/anchor-sort.d.ts +2 -4
- package/dist/types/src/capabilities/anchor-sort.d.ts.map +1 -1
- package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
- package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -3
- package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +1 -4
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts +3 -3
- package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts +0 -1
- package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownContainer.d.ts +8 -12
- package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownContainer.stories.d.ts +7 -4
- package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts +11 -0
- package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts.map +1 -0
- package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +42 -23
- package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +5 -110
- package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts +26 -0
- package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts.map +1 -0
- package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts +12 -0
- package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts.map +1 -0
- package/dist/types/src/components/Suggestions.stories.d.ts +1 -2
- package/dist/types/src/components/Suggestions.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +3 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/functions/create.d.ts +5 -9
- package/dist/types/src/functions/create.d.ts.map +1 -1
- package/dist/types/src/functions/create.test.d.ts +2 -0
- package/dist/types/src/functions/create.test.d.ts.map +1 -0
- package/dist/types/src/functions/index.d.ts +17 -3
- package/dist/types/src/functions/index.d.ts.map +1 -1
- package/dist/types/src/functions/open.d.ts +1 -1
- package/dist/types/src/functions/open.d.ts.map +1 -1
- package/dist/types/src/functions/{diff.d.ts → update.d.ts} +2 -2
- package/dist/types/src/functions/update.d.ts.map +1 -0
- package/dist/types/src/functions/update.test.d.ts +2 -0
- package/dist/types/src/functions/update.test.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useEditorMenuOptions.d.ts +9 -0
- package/dist/types/src/hooks/useEditorMenuOptions.d.ts.map +1 -0
- package/dist/types/src/hooks/useExtensions.d.ts +21 -0
- package/dist/types/src/hooks/useExtensions.d.ts.map +1 -0
- package/dist/types/src/hooks/useLinkQuery.d.ts +4 -0
- package/dist/types/src/hooks/useLinkQuery.d.ts.map +1 -0
- package/dist/types/src/hooks/useSelectCurrentThread.d.ts +1 -1
- package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
- package/dist/types/src/testing.d.ts +6 -0
- package/dist/types/src/testing.d.ts.map +1 -0
- package/dist/types/src/toolkit.d.ts +3 -0
- package/dist/types/src/toolkit.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +3 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Markdown.d.ts +7 -5
- package/dist/types/src/types/Markdown.d.ts.map +1 -1
- package/dist/types/src/types/MarkdownAction.d.ts +3 -2
- package/dist/types/src/types/MarkdownAction.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +3 -3
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +72 -56
- package/src/MarkdownPlugin.tsx +99 -94
- package/src/capabilities/anchor-sort.ts +3 -3
- package/src/capabilities/app-graph-serializer.ts +5 -5
- package/src/capabilities/artifact-definition.ts +7 -6
- package/src/capabilities/blueprint-definition.ts +30 -26
- package/src/capabilities/capabilities.ts +1 -0
- package/src/capabilities/index.ts +1 -1
- package/src/capabilities/intent-resolver.ts +3 -2
- package/src/capabilities/react-surface.tsx +44 -66
- package/src/components/MarkdownCard/MarkdownCard.stories.tsx +6 -9
- package/src/components/MarkdownCard/MarkdownCard.tsx +52 -38
- package/src/components/MarkdownContainer.stories.tsx +74 -38
- package/src/components/MarkdownContainer.tsx +78 -220
- package/src/components/MarkdownEditor/FileUpload.tsx +63 -0
- package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +55 -35
- package/src/components/MarkdownEditor/MarkdownEditor.tsx +221 -273
- package/src/components/MarkdownEditor/MarkdownEditorContent.tsx +136 -0
- package/src/components/MarkdownEditor/MarkdownEditorToolbar.tsx +63 -0
- package/src/components/Suggestions.stories.tsx +41 -34
- package/src/components/index.ts +3 -1
- package/src/functions/create.conversations.json +1 -0
- package/src/functions/create.test.ts +128 -0
- package/src/functions/create.ts +20 -9
- package/src/functions/index.ts +9 -3
- package/src/functions/open.ts +4 -2
- package/src/functions/update.conversations.json +1 -0
- package/src/functions/update.test.ts +151 -0
- package/src/functions/{diff.ts → update.ts} +4 -2
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useEditorMenuOptions.ts +71 -0
- package/src/{extensions.tsx → hooks/useExtensions.tsx} +60 -109
- package/src/hooks/useLinkQuery.ts +83 -0
- package/src/hooks/useSelectCurrentThread.tsx +15 -5
- package/src/meta.ts +3 -3
- package/src/testing.ts +27 -0
- package/src/toolkit.ts +6 -0
- package/src/translations.ts +3 -0
- package/src/types/Markdown.ts +9 -7
- package/src/types/MarkdownAction.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/util.tsx +10 -5
- package/dist/lib/browser/MarkdownCard-JLUQITYK.mjs +0 -80
- package/dist/lib/browser/MarkdownCard-JLUQITYK.mjs.map +0 -7
- package/dist/lib/browser/MarkdownContainer-7M37DXAD.mjs +0 -781
- package/dist/lib/browser/MarkdownContainer-7M37DXAD.mjs.map +0 -7
- package/dist/lib/browser/anchor-sort-E33BSTYF.mjs.map +0 -7
- package/dist/lib/browser/app-graph-serializer-OX62DNPT.mjs.map +0 -7
- package/dist/lib/browser/blueprint-definition-Z3RQGWUD.mjs +0 -11
- package/dist/lib/browser/chunk-BEE7VQPU.mjs.map +0 -7
- package/dist/lib/browser/chunk-D7UYVHL6.mjs +0 -20
- package/dist/lib/browser/chunk-D7UYVHL6.mjs.map +0 -7
- package/dist/lib/browser/chunk-LAVZ2W6X.mjs.map +0 -7
- package/dist/lib/browser/chunk-ODB2PTBP.mjs.map +0 -7
- package/dist/lib/browser/chunk-ZVVKLB5L.mjs +0 -121
- package/dist/lib/browser/chunk-ZVVKLB5L.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-WDDH56JC.mjs.map +0 -7
- package/dist/lib/browser/react-surface-LN2XK2UN.mjs.map +0 -7
- package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs +0 -81
- package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs.map +0 -7
- package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs +0 -782
- package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs.map +0 -7
- package/dist/lib/node-esm/anchor-sort-ALP2NH24.mjs.map +0 -7
- package/dist/lib/node-esm/app-graph-serializer-56TD3BMX.mjs.map +0 -7
- package/dist/lib/node-esm/blueprint-definition-735OAX33.mjs +0 -12
- package/dist/lib/node-esm/chunk-FXILAQ5F.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JPXFCBC4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-O6EXWGGS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-VCG2U522.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Y422WR6A.mjs +0 -122
- package/dist/lib/node-esm/chunk-Y422WR6A.mjs.map +0 -7
- package/dist/lib/node-esm/intent-resolver-2I5HKCUU.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-DJGGKYBD.mjs.map +0 -7
- package/dist/types/src/components/Toolbar.stories.d.ts +0 -48
- package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
- package/dist/types/src/extensions.d.ts +0 -24
- package/dist/types/src/extensions.d.ts.map +0 -1
- package/dist/types/src/functions/diff.d.ts.map +0 -1
- package/src/components/Toolbar.stories.tsx +0 -118
- /package/dist/lib/browser/{blueprint-definition-Z3RQGWUD.mjs.map → MarkdownCard-JYMDPKV5.mjs.map} +0 -0
- /package/dist/lib/{node-esm/blueprint-definition-735OAX33.mjs.map → browser/MarkdownContainer-Y75XSVBX.mjs.map} +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from '@effect/vitest';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Layer from 'effect/Layer';
|
|
8
|
+
|
|
9
|
+
import { AiService, ConsolePrinter, MemoizedAiService } from '@dxos/ai';
|
|
10
|
+
import { TestAiService } from '@dxos/ai/testing';
|
|
11
|
+
import {
|
|
12
|
+
AiConversation,
|
|
13
|
+
type ContextBinding,
|
|
14
|
+
GenerationObserver,
|
|
15
|
+
makeToolExecutionServiceFromFunctions,
|
|
16
|
+
makeToolResolverFromFunctions,
|
|
17
|
+
} from '@dxos/assistant';
|
|
18
|
+
import { Blueprint } from '@dxos/blueprints';
|
|
19
|
+
import { PropertiesType } from '@dxos/client-protocol';
|
|
20
|
+
import { Obj, Query, Ref } from '@dxos/echo';
|
|
21
|
+
import { TestHelpers, acquireReleaseResource } from '@dxos/effect';
|
|
22
|
+
import {
|
|
23
|
+
CredentialsService,
|
|
24
|
+
DatabaseService,
|
|
25
|
+
FunctionInvocationService,
|
|
26
|
+
QueueService,
|
|
27
|
+
TracingService,
|
|
28
|
+
} from '@dxos/functions';
|
|
29
|
+
import { FunctionInvocationServiceLayerTest, TestDatabaseLayer } from '@dxos/functions-runtime/testing';
|
|
30
|
+
import { invariant } from '@dxos/invariant';
|
|
31
|
+
import { ObjectId } from '@dxos/keys';
|
|
32
|
+
import { Markdown } from '@dxos/plugin-markdown/types';
|
|
33
|
+
import { Collection } from '@dxos/schema';
|
|
34
|
+
import { HasSubject, type Message } from '@dxos/types';
|
|
35
|
+
|
|
36
|
+
import { WithProperties, testToolkit } from '../testing';
|
|
37
|
+
import { MarkdownBlueprint, MarkdownFunction } from '../toolkit';
|
|
38
|
+
|
|
39
|
+
ObjectId.dangerouslyDisableRandomness();
|
|
40
|
+
|
|
41
|
+
const TestLayer = Layer.mergeAll(
|
|
42
|
+
AiService.model('@anthropic/claude-opus-4-0'),
|
|
43
|
+
makeToolResolverFromFunctions([MarkdownFunction.create, MarkdownFunction.open, MarkdownFunction.update], testToolkit),
|
|
44
|
+
makeToolExecutionServiceFromFunctions(testToolkit, testToolkit.toLayer({}) as any),
|
|
45
|
+
).pipe(
|
|
46
|
+
Layer.provideMerge(
|
|
47
|
+
FunctionInvocationServiceLayerTest({
|
|
48
|
+
functions: [MarkdownFunction.create, MarkdownFunction.open, MarkdownFunction.update],
|
|
49
|
+
}),
|
|
50
|
+
),
|
|
51
|
+
Layer.provideMerge(
|
|
52
|
+
Layer.mergeAll(
|
|
53
|
+
TestAiService(),
|
|
54
|
+
TestDatabaseLayer({
|
|
55
|
+
spaceKey: 'fixed',
|
|
56
|
+
indexing: { vector: true },
|
|
57
|
+
types: [PropertiesType, Collection.Collection, Blueprint.Blueprint, Markdown.Document, HasSubject.HasSubject],
|
|
58
|
+
}),
|
|
59
|
+
CredentialsService.configuredLayer([]),
|
|
60
|
+
TracingService.layerNoop,
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
describe('update', () => {
|
|
66
|
+
it.effect(
|
|
67
|
+
'call a function to update a markdown document',
|
|
68
|
+
Effect.fnUntraced(
|
|
69
|
+
function* (_) {
|
|
70
|
+
const doc = Markdown.make({
|
|
71
|
+
name: 'BlueYard',
|
|
72
|
+
content: 'Founders and portfolio of BlueYard.',
|
|
73
|
+
});
|
|
74
|
+
yield* DatabaseService.add(doc);
|
|
75
|
+
|
|
76
|
+
yield* FunctionInvocationService.invokeFunction(MarkdownFunction.update, {
|
|
77
|
+
id: doc.id,
|
|
78
|
+
diffs: ['- Founders', '+ # Founders'],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const updatedDoc = yield* DatabaseService.resolve(Obj.getDXN(doc), Markdown.Document);
|
|
82
|
+
expect(updatedDoc.name).toBe(doc.name);
|
|
83
|
+
const text = yield* DatabaseService.load(updatedDoc.content);
|
|
84
|
+
expect(text.content).toBe('# Founders and portfolio of BlueYard.');
|
|
85
|
+
},
|
|
86
|
+
WithProperties,
|
|
87
|
+
Effect.provide(TestLayer),
|
|
88
|
+
TestHelpers.provideTestContext,
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
it.scoped(
|
|
93
|
+
'create and update a markdown document',
|
|
94
|
+
Effect.fnUntraced(
|
|
95
|
+
function* (_) {
|
|
96
|
+
const queue = yield* QueueService.createQueue<Message.Message | ContextBinding>();
|
|
97
|
+
const conversation = yield* acquireReleaseResource(() => new AiConversation(queue));
|
|
98
|
+
|
|
99
|
+
yield* DatabaseService.flush({ indexes: true });
|
|
100
|
+
const markdownBlueprint = yield* DatabaseService.add(Obj.clone(MarkdownBlueprint));
|
|
101
|
+
yield* Effect.promise(() =>
|
|
102
|
+
conversation.context.bind({
|
|
103
|
+
blueprints: [Ref.make(markdownBlueprint)],
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const observer = GenerationObserver.fromPrinter(new ConsolePrinter());
|
|
108
|
+
|
|
109
|
+
yield* conversation.createRequest({
|
|
110
|
+
observer,
|
|
111
|
+
prompt: `Create a document with a cookie recipe.`,
|
|
112
|
+
});
|
|
113
|
+
{
|
|
114
|
+
const { objects: docs } = yield* DatabaseService.runQuery(Query.type(Markdown.Document));
|
|
115
|
+
if (docs.length !== 1) {
|
|
116
|
+
throw new Error(`Expected 1 document; got ${docs.length}: ${docs.map((_) => _.name)}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const doc = docs[0];
|
|
120
|
+
invariant(Obj.instanceOf(Markdown.Document, doc));
|
|
121
|
+
console.log({
|
|
122
|
+
name: doc.name,
|
|
123
|
+
content: yield* DatabaseService.load(doc.content).pipe(Effect.map((_) => _.content)),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
yield* conversation.createRequest({
|
|
128
|
+
observer,
|
|
129
|
+
prompt: 'Add a section with a holiday-themed variation.',
|
|
130
|
+
});
|
|
131
|
+
{
|
|
132
|
+
const { objects: docs } = yield* DatabaseService.runQuery(Query.type(Markdown.Document));
|
|
133
|
+
if (docs.length !== 1) {
|
|
134
|
+
throw new Error(`Expected 1 document; got ${docs.length}: ${docs.map((_) => _.name)}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const doc = docs[0];
|
|
138
|
+
invariant(Obj.instanceOf(Markdown.Document, doc));
|
|
139
|
+
console.log({
|
|
140
|
+
name: doc.name,
|
|
141
|
+
content: yield* DatabaseService.load(doc.content).pipe(Effect.map((_) => _.content)),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
WithProperties,
|
|
146
|
+
Effect.provide(TestLayer),
|
|
147
|
+
TestHelpers.provideTestContext,
|
|
148
|
+
),
|
|
149
|
+
MemoizedAiService.isGenerationEnabled() ? 240_000 : 30_000,
|
|
150
|
+
);
|
|
151
|
+
});
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
|
|
7
8
|
import { ArtifactId, applyDiffs } from '@dxos/assistant';
|
|
8
9
|
import { createDocAccessor } from '@dxos/echo-db';
|
|
@@ -13,7 +14,8 @@ import { Markdown } from '../types';
|
|
|
13
14
|
|
|
14
15
|
// TODO(wittjosiah): Reconcile with ThreadAction.AddProposal.
|
|
15
16
|
export default defineFunction({
|
|
16
|
-
|
|
17
|
+
key: 'dxos.org/function/markdown/update',
|
|
18
|
+
name: 'Update',
|
|
17
19
|
description: trim`
|
|
18
20
|
Applies a set of diffs to the markdown document.
|
|
19
21
|
`,
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type EditorView } from '@codemirror/view';
|
|
6
|
+
import { useCallback, useMemo, useRef } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Domino, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import {
|
|
10
|
+
type EditorMenuGroup,
|
|
11
|
+
type UseEditorMenuProps,
|
|
12
|
+
filterMenuGroups,
|
|
13
|
+
formattingCommands,
|
|
14
|
+
linkSlashCommands,
|
|
15
|
+
} from '@dxos/react-ui-editor';
|
|
16
|
+
|
|
17
|
+
import { meta } from '../meta';
|
|
18
|
+
|
|
19
|
+
export type UseEditorMenuOptionsProps = {
|
|
20
|
+
editorView?: EditorView;
|
|
21
|
+
slashCommandGroups?: EditorMenuGroup[];
|
|
22
|
+
onLinkQuery?: (query?: string) => Promise<EditorMenuGroup[]>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const useEditorMenuOptions = ({
|
|
26
|
+
editorView,
|
|
27
|
+
slashCommandGroups,
|
|
28
|
+
onLinkQuery,
|
|
29
|
+
}: UseEditorMenuOptionsProps): UseEditorMenuProps => {
|
|
30
|
+
const { t } = useTranslation(meta.id);
|
|
31
|
+
|
|
32
|
+
const getMenu = useCallback<NonNullable<UseEditorMenuProps['getMenu']>>(
|
|
33
|
+
({ text, trigger }) => {
|
|
34
|
+
switch (trigger) {
|
|
35
|
+
case '@': {
|
|
36
|
+
return onLinkQuery?.(text) ?? [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
case '/':
|
|
40
|
+
default: {
|
|
41
|
+
return filterMenuGroups([formattingCommands, linkSlashCommands, ...(slashCommandGroups ?? [])], (item) =>
|
|
42
|
+
text ? toLocalizedString(item.label, t).toLowerCase().includes(text.toLowerCase()) : true,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
[slashCommandGroups, onLinkQuery],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const viewRef = useRef(editorView);
|
|
51
|
+
return useMemo<UseEditorMenuProps>(() => {
|
|
52
|
+
const trigger = onLinkQuery ? ['/', '@'] : ['/'];
|
|
53
|
+
const placeholder = {
|
|
54
|
+
delay: 3_000,
|
|
55
|
+
content: () =>
|
|
56
|
+
Domino.of('div')
|
|
57
|
+
.children(
|
|
58
|
+
Domino.of('span').text('Press'),
|
|
59
|
+
...trigger.map((text) =>
|
|
60
|
+
Domino.of('span')
|
|
61
|
+
.classNames('mx-1 pli-1.5 pt-[1px] pb-[2px] border border-separator rounded-sm')
|
|
62
|
+
.text(text),
|
|
63
|
+
),
|
|
64
|
+
Domino.of('span').text('for commands.'),
|
|
65
|
+
)
|
|
66
|
+
.build(),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return { viewRef, getMenu, trigger, placeholder };
|
|
70
|
+
}, [getMenu, onLinkQuery]);
|
|
71
|
+
};
|
|
@@ -6,21 +6,17 @@ import { type ViewUpdate } from '@codemirror/view';
|
|
|
6
6
|
import React, { type AnchorHTMLAttributes, type ReactNode, useMemo } from 'react';
|
|
7
7
|
import { createRoot } from 'react-dom/client';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
type PromiseIntentDispatcher,
|
|
12
|
-
createIntent,
|
|
13
|
-
useCapabilities,
|
|
14
|
-
useIntentDispatcher,
|
|
15
|
-
} from '@dxos/app-framework';
|
|
9
|
+
import { LayoutAction, type PromiseIntentDispatcher, createIntent } from '@dxos/app-framework';
|
|
10
|
+
import { useIntentDispatcher } from '@dxos/app-framework/react';
|
|
16
11
|
import { debounceAndThrottle } from '@dxos/async';
|
|
12
|
+
import { Obj } from '@dxos/echo';
|
|
17
13
|
import { invariant } from '@dxos/invariant';
|
|
18
|
-
import {
|
|
14
|
+
import { createDocAccessor } from '@dxos/react-client/echo';
|
|
15
|
+
import { getSpace } from '@dxos/react-client/echo';
|
|
19
16
|
import { useIdentity } from '@dxos/react-client/halo';
|
|
20
17
|
import { Icon, ThemeProvider } from '@dxos/react-ui';
|
|
21
18
|
import { type SelectionManager } from '@dxos/react-ui-attention';
|
|
22
19
|
import {
|
|
23
|
-
type AutocompleteResult,
|
|
24
20
|
Cursor,
|
|
25
21
|
type EditorStateStore,
|
|
26
22
|
EditorView,
|
|
@@ -29,7 +25,6 @@ import {
|
|
|
29
25
|
InputModeExtensions,
|
|
30
26
|
type PreviewOptions,
|
|
31
27
|
type RenderCallback,
|
|
32
|
-
autocomplete,
|
|
33
28
|
createDataExtensions,
|
|
34
29
|
decorateMarkdown,
|
|
35
30
|
documentId,
|
|
@@ -38,24 +33,24 @@ import {
|
|
|
38
33
|
linkTooltip,
|
|
39
34
|
listener,
|
|
40
35
|
preview,
|
|
36
|
+
replacer,
|
|
41
37
|
selectionState,
|
|
42
38
|
typewriter,
|
|
43
39
|
} from '@dxos/react-ui-editor';
|
|
44
40
|
import { defaultTx } from '@dxos/react-ui-theme';
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
41
|
+
import { Text } from '@dxos/schema';
|
|
42
|
+
import { isTruthy } from '@dxos/util';
|
|
43
|
+
|
|
44
|
+
import { Markdown } from '../types';
|
|
45
|
+
import { setFallbackName } from '../util';
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
import { type Markdown } from './types';
|
|
50
|
-
import { setFallbackName } from './util';
|
|
47
|
+
export type DocumentType = Markdown.Document | Text.Text | { id: string; text: string };
|
|
51
48
|
|
|
52
|
-
type ExtensionsOptions = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
text?: DataType.Text;
|
|
49
|
+
export type ExtensionsOptions = {
|
|
50
|
+
id: string;
|
|
51
|
+
object?: DocumentType;
|
|
56
52
|
dispatch?: PromiseIntentDispatcher;
|
|
57
|
-
|
|
58
|
-
settings: Markdown.Settings;
|
|
53
|
+
settings?: Markdown.Settings;
|
|
59
54
|
selectionManager?: SelectionManager;
|
|
60
55
|
viewMode?: EditorViewMode;
|
|
61
56
|
editorStateStore?: EditorStateStore;
|
|
@@ -64,9 +59,8 @@ type ExtensionsOptions = {
|
|
|
64
59
|
|
|
65
60
|
// TODO(burdon): Merge with createBaseExtensions below.
|
|
66
61
|
export const useExtensions = ({
|
|
67
|
-
document,
|
|
68
62
|
id,
|
|
69
|
-
|
|
63
|
+
object,
|
|
70
64
|
settings,
|
|
71
65
|
selectionManager,
|
|
72
66
|
viewMode,
|
|
@@ -75,93 +69,72 @@ export const useExtensions = ({
|
|
|
75
69
|
}: ExtensionsOptions): Extension[] => {
|
|
76
70
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
77
71
|
const identity = useIdentity();
|
|
78
|
-
const space = getSpace(
|
|
72
|
+
const space = getSpace(object);
|
|
73
|
+
|
|
74
|
+
let target: Obj.Any | undefined;
|
|
75
|
+
if (Obj.instanceOf(Markdown.Document, object)) {
|
|
76
|
+
target = (object as Markdown.Document).content.target;
|
|
77
|
+
} else if (Obj.instanceOf(Text.Text, object)) {
|
|
78
|
+
target = object;
|
|
79
|
+
}
|
|
79
80
|
|
|
80
81
|
// TODO(wittjosiah): Autocomplete is not working and this query is causing performance issues.
|
|
81
82
|
// TODO(burdon): Unsubscribe.
|
|
82
83
|
// const query = space?.db.query(Filter.type(DocumentType));
|
|
83
84
|
// query?.subscribe();
|
|
85
|
+
|
|
84
86
|
const baseExtensions = useMemo(
|
|
85
87
|
() =>
|
|
86
88
|
createBaseExtensions({
|
|
87
|
-
document,
|
|
88
89
|
id,
|
|
89
|
-
|
|
90
|
+
object,
|
|
90
91
|
settings,
|
|
91
92
|
selectionManager,
|
|
92
93
|
viewMode,
|
|
93
94
|
previewOptions,
|
|
94
95
|
dispatch,
|
|
95
|
-
// query,
|
|
96
96
|
}),
|
|
97
97
|
[
|
|
98
|
-
document,
|
|
99
98
|
id,
|
|
100
|
-
|
|
99
|
+
object,
|
|
101
100
|
viewMode,
|
|
102
101
|
dispatch,
|
|
103
102
|
previewOptions,
|
|
104
103
|
settings,
|
|
105
|
-
settings
|
|
106
|
-
settings
|
|
107
|
-
settings
|
|
108
|
-
settings
|
|
109
|
-
settings
|
|
104
|
+
settings?.debug,
|
|
105
|
+
settings?.editorInputMode,
|
|
106
|
+
settings?.folding,
|
|
107
|
+
settings?.numberedHeadings,
|
|
108
|
+
settings?.typewriter,
|
|
110
109
|
selectionManager,
|
|
111
110
|
],
|
|
112
111
|
);
|
|
113
112
|
|
|
114
|
-
const extensionProviders = useCapabilities(MarkdownCapabilities.Extensions);
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
// External extensions from other plugins.
|
|
118
|
-
//
|
|
119
|
-
const pluginExtensions = useMemo<Extension[]>(() => {
|
|
120
|
-
if (!document) {
|
|
121
|
-
return [];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return extensionProviders.flat().reduce((acc: Extension[], provider) => {
|
|
125
|
-
const extension = typeof provider === 'function' ? provider({ document }) : provider;
|
|
126
|
-
if (extension) {
|
|
127
|
-
acc.push(extension);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return acc;
|
|
131
|
-
}, []);
|
|
132
|
-
}, [extensionProviders, document]);
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
// Basic plugins.
|
|
136
|
-
//
|
|
137
113
|
return useMemo<Extension[]>(
|
|
138
114
|
() =>
|
|
139
115
|
[
|
|
116
|
+
// TODO(burdon): Pass this in?
|
|
140
117
|
// NOTE: Data extensions must be first so that automerge is updated before other extensions compute their state.
|
|
141
|
-
|
|
142
|
-
createDataExtensions({
|
|
143
|
-
id: document.id,
|
|
144
|
-
text: document.content.target && createDocAccessor(document.content.target, ['content']),
|
|
145
|
-
space,
|
|
146
|
-
identity,
|
|
147
|
-
}),
|
|
148
|
-
text &&
|
|
149
|
-
id &&
|
|
118
|
+
target &&
|
|
150
119
|
createDataExtensions({
|
|
151
120
|
id,
|
|
152
|
-
text: createDocAccessor(
|
|
121
|
+
text: createDocAccessor(target, ['content']),
|
|
153
122
|
space,
|
|
154
123
|
identity,
|
|
155
124
|
}),
|
|
156
|
-
|
|
157
|
-
|
|
125
|
+
|
|
126
|
+
// TODO(burdon): Reconcile with effect in parent.
|
|
127
|
+
Obj.instanceOf(Markdown.Document, object) &&
|
|
158
128
|
listener({
|
|
159
|
-
onChange: (text) =>
|
|
129
|
+
onChange: ({ text }) => {
|
|
130
|
+
setFallbackName(object as Markdown.Document, text);
|
|
131
|
+
},
|
|
160
132
|
}),
|
|
133
|
+
|
|
161
134
|
baseExtensions,
|
|
162
|
-
|
|
163
|
-
].filter(
|
|
164
|
-
[
|
|
135
|
+
selectionState(editorStateStore),
|
|
136
|
+
].filter(isTruthy),
|
|
137
|
+
[identity, space, id, object, target, baseExtensions],
|
|
165
138
|
);
|
|
166
139
|
};
|
|
167
140
|
|
|
@@ -169,20 +142,19 @@ export const useExtensions = ({
|
|
|
169
142
|
* Create extension instances for editor.
|
|
170
143
|
*/
|
|
171
144
|
const createBaseExtensions = ({
|
|
172
|
-
document,
|
|
173
145
|
id,
|
|
146
|
+
object,
|
|
174
147
|
dispatch,
|
|
175
148
|
settings,
|
|
176
149
|
selectionManager,
|
|
177
|
-
query,
|
|
178
150
|
viewMode,
|
|
179
151
|
previewOptions,
|
|
180
152
|
}: ExtensionsOptions): Extension[] => {
|
|
181
153
|
const extensions: Extension[] = [
|
|
182
154
|
selectionManager && selectionChange(selectionManager),
|
|
183
|
-
settings
|
|
184
|
-
settings
|
|
185
|
-
].filter(
|
|
155
|
+
settings?.editorInputMode && InputModeExtensions[settings.editorInputMode],
|
|
156
|
+
settings?.folding && folding(),
|
|
157
|
+
].filter(isTruthy);
|
|
186
158
|
|
|
187
159
|
//
|
|
188
160
|
// Markdown
|
|
@@ -193,17 +165,18 @@ const createBaseExtensions = ({
|
|
|
193
165
|
formattingKeymap(),
|
|
194
166
|
decorateMarkdown({
|
|
195
167
|
selectionChangeDelay: 100,
|
|
196
|
-
numberedHeadings: settings
|
|
168
|
+
numberedHeadings: settings?.numberedHeadings ? { from: 2 } : undefined,
|
|
197
169
|
// TODO(wittjosiah): For internal links, consider ignoring the link text and rendering the label of the object being linked to.
|
|
170
|
+
// TODO(burdon): Create dx-tag.
|
|
198
171
|
renderLinkButton:
|
|
199
|
-
dispatch && (
|
|
172
|
+
dispatch && (object || id)
|
|
200
173
|
? createLinkRenderer((id: string) => {
|
|
201
174
|
void dispatch(
|
|
202
175
|
createIntent(LayoutAction.Open, {
|
|
203
176
|
part: 'main',
|
|
204
177
|
subject: [id],
|
|
205
178
|
options: {
|
|
206
|
-
pivotId:
|
|
179
|
+
pivotId: object && Obj.isObject(object) ? Obj.getDXN(object).toString() : id,
|
|
207
180
|
},
|
|
208
181
|
}),
|
|
209
182
|
);
|
|
@@ -212,35 +185,12 @@ const createBaseExtensions = ({
|
|
|
212
185
|
}),
|
|
213
186
|
linkTooltip(renderLinkTooltip),
|
|
214
187
|
preview(previewOptions),
|
|
188
|
+
replacer(),
|
|
215
189
|
],
|
|
216
190
|
);
|
|
217
191
|
}
|
|
218
192
|
|
|
219
|
-
|
|
220
|
-
// Autocomplete object links.
|
|
221
|
-
//
|
|
222
|
-
if (query) {
|
|
223
|
-
extensions.push(
|
|
224
|
-
autocomplete({
|
|
225
|
-
onSearch: (text: string) => {
|
|
226
|
-
// TODO(burdon): Specify filter (e.g., stack).
|
|
227
|
-
return query.objects
|
|
228
|
-
.map<AutocompleteResult | undefined>((object) =>
|
|
229
|
-
object.name?.length && object.id !== document?.id
|
|
230
|
-
? {
|
|
231
|
-
label: object.name,
|
|
232
|
-
// TODO(burdon): Factor out URL builder.
|
|
233
|
-
apply: `[${object.name}](/${fullyQualifiedId(object)})`,
|
|
234
|
-
}
|
|
235
|
-
: undefined,
|
|
236
|
-
)
|
|
237
|
-
.filter(isNotFalsy);
|
|
238
|
-
},
|
|
239
|
-
}),
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (settings.debug) {
|
|
193
|
+
if (settings?.debug) {
|
|
244
194
|
const items = settings.typewriter?.split(/[,\n]/) ?? '';
|
|
245
195
|
if (items) {
|
|
246
196
|
extensions.push(typewriter({ items }));
|
|
@@ -250,7 +200,7 @@ const createBaseExtensions = ({
|
|
|
250
200
|
return extensions;
|
|
251
201
|
};
|
|
252
202
|
|
|
253
|
-
|
|
203
|
+
const selectionChange = (selectionManager: SelectionManager) => {
|
|
254
204
|
return EditorView.updateListener.of(
|
|
255
205
|
debounceAndThrottle((update: ViewUpdate) => {
|
|
256
206
|
if (update.selectionSet) {
|
|
@@ -263,6 +213,7 @@ export const selectionChange = (selectionManager: SelectionManager) => {
|
|
|
263
213
|
to: cursorConverter.toCursor(range.to),
|
|
264
214
|
}))
|
|
265
215
|
.filter(({ from, to }) => to > from);
|
|
216
|
+
|
|
266
217
|
selectionManager.updateMultiRange(id, ranges);
|
|
267
218
|
}
|
|
268
219
|
}, 100),
|
|
@@ -321,8 +272,8 @@ const renderLinkTooltip: RenderCallback<{ url: string }> = (el, { url }) => {
|
|
|
321
272
|
);
|
|
322
273
|
};
|
|
323
274
|
|
|
324
|
-
// TODO(burdon):
|
|
325
|
-
|
|
275
|
+
// TODO(burdon): REMOVE.
|
|
276
|
+
const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
|
|
326
277
|
createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
|
|
327
278
|
return root;
|
|
328
279
|
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useCallback, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { Capabilities } from '@dxos/app-framework';
|
|
8
|
+
import { useCapabilities, usePluginManager } from '@dxos/app-framework/react';
|
|
9
|
+
import { Filter, Obj, Query, Type } from '@dxos/echo';
|
|
10
|
+
import { ClientCapabilities } from '@dxos/plugin-client';
|
|
11
|
+
import { SpaceCapabilities } from '@dxos/plugin-space';
|
|
12
|
+
import { type Space } from '@dxos/react-client/echo';
|
|
13
|
+
import { toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
14
|
+
import { type EditorMenuGroup, type EditorMenuItem, insertAtCursor, insertAtLineStart } from '@dxos/react-ui-editor';
|
|
15
|
+
|
|
16
|
+
export const useLinkQuery = (space: Space | undefined) => {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
|
|
19
|
+
const manager = usePluginManager();
|
|
20
|
+
const resolve = useCallback(
|
|
21
|
+
(typename: string) =>
|
|
22
|
+
manager.context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {},
|
|
23
|
+
[manager],
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const objectForms = useCapabilities(SpaceCapabilities.ObjectForm);
|
|
27
|
+
const schemaWhiteList = useCapabilities(ClientCapabilities.SchemaWhiteList);
|
|
28
|
+
const filter = useMemo(
|
|
29
|
+
() =>
|
|
30
|
+
Filter.or(
|
|
31
|
+
...objectForms.map((form) => Filter.type(form.objectSchema)),
|
|
32
|
+
...schemaWhiteList.flat().map((schema) => Filter.typename(Type.getTypename(schema))),
|
|
33
|
+
),
|
|
34
|
+
[objectForms, schemaWhiteList],
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const handleLinkQuery = useCallback(
|
|
38
|
+
async (query?: string): Promise<EditorMenuGroup[]> => {
|
|
39
|
+
const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : (query?.toLowerCase() ?? '');
|
|
40
|
+
const results = await space?.db.query(Query.select(filter)).run();
|
|
41
|
+
|
|
42
|
+
// TODO(wittjosiah): Use `Obj.Any` type.
|
|
43
|
+
const getLabel = (object: any) => {
|
|
44
|
+
const label = Obj.getLabel(object);
|
|
45
|
+
if (label) {
|
|
46
|
+
return label;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// TODO(wittjosiah): Remove metadata labels.
|
|
50
|
+
const type = Obj.getTypename(object)!;
|
|
51
|
+
const metadata = resolve(type);
|
|
52
|
+
return metadata.label?.(object) || ['object name placeholder', { ns: type, default: 'New object' }];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const items =
|
|
56
|
+
results?.objects
|
|
57
|
+
.filter((object) => toLocalizedString(getLabel(object), t).toLowerCase().includes(name))
|
|
58
|
+
// TODO(wittjosiah): Remove `any` type.
|
|
59
|
+
.map((object: any): EditorMenuItem => {
|
|
60
|
+
const metadata = resolve(Obj.getTypename(object)!);
|
|
61
|
+
const label = toLocalizedString(getLabel(object), t);
|
|
62
|
+
return {
|
|
63
|
+
id: object.id,
|
|
64
|
+
label,
|
|
65
|
+
icon: metadata.icon,
|
|
66
|
+
onSelect: ({ view, head }) => {
|
|
67
|
+
const link = `[${label}](${Obj.getDXN(object)})`;
|
|
68
|
+
if (query?.startsWith('@')) {
|
|
69
|
+
insertAtLineStart(view, head, `!${link}\n`);
|
|
70
|
+
} else {
|
|
71
|
+
insertAtCursor(view, head, `${link} `);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}) ?? [];
|
|
76
|
+
|
|
77
|
+
return [{ id: 'echo', items }];
|
|
78
|
+
},
|
|
79
|
+
[space, filter, resolve],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return handleLinkQuery;
|
|
83
|
+
};
|