@dxos/plugin-wnfs 0.8.4-main.fcfe5033a5 → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/PLUGIN.mdl +371 -0
- package/README.md +2 -2
- package/dist/lib/neutral/WnfsPlugin.mjs +51 -0
- package/dist/lib/neutral/WnfsPlugin.mjs.map +7 -0
- package/dist/lib/neutral/WnfsPlugin.node.mjs +12 -0
- package/dist/lib/neutral/WnfsPlugin.node.mjs.map +7 -0
- package/dist/lib/neutral/WnfsPlugin.workerd.mjs +12 -0
- package/dist/lib/neutral/WnfsPlugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/backend-4HPTHS33.mjs +47 -0
- package/dist/lib/neutral/backend-4HPTHS33.mjs.map +7 -0
- package/dist/lib/neutral/blockstore-U6SPNL2C.mjs +190 -0
- package/dist/lib/neutral/blockstore-U6SPNL2C.mjs.map +7 -0
- package/dist/lib/neutral/capabilities/index.mjs +13 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-KUQHRT52.mjs +39 -0
- package/dist/lib/neutral/chunk-KUQHRT52.mjs.map +7 -0
- package/dist/lib/{browser/chunk-N6Y4QPNF.mjs → neutral/chunk-L33MOD23.mjs} +97 -28
- package/dist/lib/neutral/chunk-L33MOD23.mjs.map +7 -0
- package/dist/lib/neutral/chunk-QODCKFTY.mjs +19 -0
- package/dist/lib/neutral/chunk-QODCKFTY.mjs.map +7 -0
- package/dist/lib/neutral/helpers/index.mjs +26 -0
- package/dist/lib/neutral/helpers/index.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +36 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +8 -0
- package/dist/lib/neutral/meta.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +12 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +17 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +8 -0
- package/dist/lib/neutral/types/index.mjs.map +7 -0
- package/dist/lib/neutral/url-resolver-BIRH7WHL.mjs +47 -0
- package/dist/lib/neutral/url-resolver-BIRH7WHL.mjs.map +7 -0
- package/dist/types/src/WnfsPlugin.d.ts +1 -0
- package/dist/types/src/WnfsPlugin.d.ts.map +1 -1
- package/dist/types/src/WnfsPlugin.node.d.ts +4 -0
- package/dist/types/src/WnfsPlugin.node.d.ts.map +1 -0
- package/dist/types/src/WnfsPlugin.test.d.ts +2 -0
- package/dist/types/src/WnfsPlugin.test.d.ts.map +1 -0
- package/dist/types/src/WnfsPlugin.workerd.d.ts +4 -0
- package/dist/types/src/WnfsPlugin.workerd.d.ts.map +1 -0
- package/dist/types/src/annotations.d.ts +7 -0
- package/dist/types/src/annotations.d.ts.map +1 -0
- package/dist/types/src/blockstore.d.ts.map +1 -1
- package/dist/types/src/capabilities/backend.d.ts +7 -0
- package/dist/types/src/capabilities/backend.d.ts.map +1 -0
- package/dist/types/src/capabilities/blockstore.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +3 -5
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/url-resolver.d.ts +7 -0
- package/dist/types/src/capabilities/url-resolver.d.ts.map +1 -0
- package/dist/types/src/helpers/common.d.ts.map +1 -1
- package/dist/types/src/helpers/load.d.ts.map +1 -1
- package/dist/types/src/helpers/upload.d.ts.map +1 -1
- package/dist/types/src/helpers/wnfs-url.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +1 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/plugin.d.ts +3 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +2 -16
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/WnfsCapabilities.d.ts +11 -0
- package/dist/types/src/types/WnfsCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +1 -3
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +74 -61
- package/src/WnfsPlugin.node.ts +11 -0
- package/src/WnfsPlugin.test.ts +20 -0
- package/src/WnfsPlugin.tsx +16 -40
- package/src/WnfsPlugin.workerd.ts +11 -0
- package/src/annotations.ts +16 -0
- package/src/capabilities/backend.ts +43 -0
- package/src/capabilities/blockstore.ts +1 -1
- package/src/capabilities/index.ts +11 -8
- package/src/capabilities/url-resolver.ts +44 -0
- package/src/helpers/load.ts +32 -18
- package/src/helpers/upload.ts +12 -3
- package/src/index.ts +3 -2
- package/src/meta.ts +22 -6
- package/src/plugin.ts +9 -0
- package/src/translations.ts +1 -14
- package/src/types/WnfsCapabilities.ts +18 -0
- package/src/types/index.ts +1 -3
- package/src/vite-env.d.ts +10 -0
- package/dist/lib/browser/chunk-BM3MEFES.mjs +0 -69
- package/dist/lib/browser/chunk-BM3MEFES.mjs.map +0 -7
- package/dist/lib/browser/chunk-N6Y4QPNF.mjs.map +0 -7
- package/dist/lib/browser/chunk-VXUCZHVZ.mjs +0 -69
- package/dist/lib/browser/chunk-VXUCZHVZ.mjs.map +0 -7
- package/dist/lib/browser/create-NOHKWE67.mjs +0 -24
- package/dist/lib/browser/create-NOHKWE67.mjs.map +0 -7
- package/dist/lib/browser/create-file-N7ADCFOF.mjs +0 -49
- package/dist/lib/browser/create-file-N7ADCFOF.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -117
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/operations/index.mjs +0 -13
- package/dist/lib/browser/operations/index.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -11
- package/dist/lib/browser/upload-DA7GBOXF.mjs +0 -42
- package/dist/lib/browser/upload-DA7GBOXF.mjs.map +0 -7
- package/dist/types/src/capabilities/file-uploader.d.ts +0 -6
- package/dist/types/src/capabilities/file-uploader.d.ts.map +0 -1
- package/dist/types/src/capabilities/markdown.d.ts +0 -5
- package/dist/types/src/capabilities/markdown.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +0 -6
- package/dist/types/src/capabilities/operation-handler.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface.d.ts +0 -5
- package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
- package/dist/types/src/components/FileInput/FileInput.d.ts +0 -8
- package/dist/types/src/components/FileInput/FileInput.d.ts.map +0 -1
- package/dist/types/src/components/FileInput/index.d.ts +0 -2
- package/dist/types/src/components/FileInput/index.d.ts.map +0 -1
- package/dist/types/src/components/FilePreview/FilePreview.d.ts +0 -12
- package/dist/types/src/components/FilePreview/FilePreview.d.ts.map +0 -1
- package/dist/types/src/components/FilePreview/index.d.ts +0 -2
- package/dist/types/src/components/FilePreview/index.d.ts.map +0 -1
- package/dist/types/src/components/index.d.ts +0 -3
- package/dist/types/src/components/index.d.ts.map +0 -1
- package/dist/types/src/containers/FileContainer/FileContainer.d.ts +0 -7
- package/dist/types/src/containers/FileContainer/FileContainer.d.ts.map +0 -1
- package/dist/types/src/containers/FileContainer/index.d.ts +0 -2
- package/dist/types/src/containers/FileContainer/index.d.ts.map +0 -1
- package/dist/types/src/containers/index.d.ts +0 -3
- package/dist/types/src/containers/index.d.ts.map +0 -1
- package/dist/types/src/extensions/image.d.ts +0 -14
- package/dist/types/src/extensions/image.d.ts.map +0 -1
- package/dist/types/src/extensions/image.stories.d.ts +0 -11
- package/dist/types/src/extensions/image.stories.d.ts.map +0 -1
- package/dist/types/src/extensions/index.d.ts +0 -2
- package/dist/types/src/extensions/index.d.ts.map +0 -1
- package/dist/types/src/operations/create-file.d.ts +0 -5
- package/dist/types/src/operations/create-file.d.ts.map +0 -1
- package/dist/types/src/operations/create.d.ts +0 -5
- package/dist/types/src/operations/create.d.ts.map +0 -1
- package/dist/types/src/operations/definitions.d.ts +0 -38
- package/dist/types/src/operations/definitions.d.ts.map +0 -1
- package/dist/types/src/operations/index.d.ts +0 -4
- package/dist/types/src/operations/index.d.ts.map +0 -1
- package/dist/types/src/operations/upload.d.ts +0 -5
- package/dist/types/src/operations/upload.d.ts.map +0 -1
- package/dist/types/src/types/File.d.ts +0 -16
- package/dist/types/src/types/File.d.ts.map +0 -1
- package/dist/types/src/types/capabilities.d.ts +0 -13
- package/dist/types/src/types/capabilities.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts +0 -9
- package/dist/types/src/types/types.d.ts.map +0 -1
- package/src/capabilities/file-uploader.ts +0 -34
- package/src/capabilities/markdown.ts +0 -34
- package/src/capabilities/operation-handler.ts +0 -16
- package/src/capabilities/react-surface.tsx +0 -54
- package/src/components/FileInput/FileInput.tsx +0 -47
- package/src/components/FileInput/index.ts +0 -5
- package/src/components/FilePreview/FilePreview.tsx +0 -43
- package/src/components/FilePreview/index.ts +0 -5
- package/src/components/index.ts +0 -6
- package/src/containers/FileContainer/FileContainer.tsx +0 -57
- package/src/containers/FileContainer/index.ts +0 -5
- package/src/containers/index.ts +0 -7
- package/src/extensions/image.stories.tsx +0 -103
- package/src/extensions/image.tsx +0 -204
- package/src/extensions/index.ts +0 -5
- package/src/operations/create-file.ts +0 -31
- package/src/operations/create.ts +0 -22
- package/src/operations/definitions.ts +0 -41
- package/src/operations/index.ts +0 -13
- package/src/operations/upload.ts +0 -29
- package/src/types/File.ts +0 -28
- package/src/types/capabilities.ts +0 -18
- package/src/types/types.ts +0 -24
- /package/dist/lib/{browser/types/index.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import { useCapability } from '@dxos/app-framework/ui';
|
|
8
|
-
import { type AppSurface } from '@dxos/app-toolkit/ui';
|
|
9
|
-
import { invariant } from '@dxos/invariant';
|
|
10
|
-
import { getSpace } from '@dxos/react-client/echo';
|
|
11
|
-
import { Panel, useAsyncEffect } from '@dxos/react-ui';
|
|
12
|
-
|
|
13
|
-
import { FilePreview } from '#components';
|
|
14
|
-
import { WnfsCapabilities, type WnfsFile } from '#types';
|
|
15
|
-
|
|
16
|
-
import { filePath, getBlobUrl, loadWnfs, wnfsUrl } from '../../helpers';
|
|
17
|
-
|
|
18
|
-
export type FileContainerProps = AppSurface.ObjectArticleProps<WnfsFile.File>;
|
|
19
|
-
|
|
20
|
-
export const FileContainer = ({ role, subject: file }: FileContainerProps) => {
|
|
21
|
-
const blockstore = useCapability(WnfsCapabilities.Blockstore);
|
|
22
|
-
const instances = useCapability(WnfsCapabilities.Instances);
|
|
23
|
-
const [blobUrl, setBlobUrl] = useState<string>();
|
|
24
|
-
|
|
25
|
-
useAsyncEffect(async () => {
|
|
26
|
-
const space = getSpace(file);
|
|
27
|
-
invariant(space);
|
|
28
|
-
const { directory, forest } = await loadWnfs({
|
|
29
|
-
blockstore,
|
|
30
|
-
instances,
|
|
31
|
-
space,
|
|
32
|
-
});
|
|
33
|
-
const path = filePath(file.cid.toString(), space);
|
|
34
|
-
const url = await getBlobUrl({
|
|
35
|
-
wnfsUrl: wnfsUrl(path),
|
|
36
|
-
blockstore,
|
|
37
|
-
directory,
|
|
38
|
-
forest,
|
|
39
|
-
type: file.type,
|
|
40
|
-
});
|
|
41
|
-
setBlobUrl(url);
|
|
42
|
-
}, [file]);
|
|
43
|
-
|
|
44
|
-
if (!blobUrl) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<Panel.Root role={role} className='dx-document'>
|
|
50
|
-
<Panel.Content asChild>
|
|
51
|
-
<FilePreview type={file.type} url={blobUrl} />
|
|
52
|
-
</Panel.Content>
|
|
53
|
-
</Panel.Root>
|
|
54
|
-
);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export default FileContainer;
|
package/src/containers/index.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import type { Blockstore } from 'interface-blockstore';
|
|
7
|
-
import React, { type ChangeEvent, useEffect, useState } from 'react';
|
|
8
|
-
|
|
9
|
-
import { random } from '@dxos/random';
|
|
10
|
-
import { useSpaces } from '@dxos/react-client/echo';
|
|
11
|
-
import { withClientProvider } from '@dxos/react-client/testing';
|
|
12
|
-
import { useThemeContext } from '@dxos/react-ui';
|
|
13
|
-
import { useTextEditor } from '@dxos/react-ui-editor';
|
|
14
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
15
|
-
import {
|
|
16
|
-
createBasicExtensions,
|
|
17
|
-
createMarkdownExtensions,
|
|
18
|
-
createThemeExtensions,
|
|
19
|
-
decorateMarkdown,
|
|
20
|
-
processEditorPayload,
|
|
21
|
-
} from '@dxos/ui-editor';
|
|
22
|
-
|
|
23
|
-
import { create as createBlockstore } from '../blockstore';
|
|
24
|
-
import { upload } from '../helpers';
|
|
25
|
-
import { image } from './image';
|
|
26
|
-
|
|
27
|
-
random.seed(1);
|
|
28
|
-
const initialValue = random.lorem.paragraphs(100);
|
|
29
|
-
const instances = {};
|
|
30
|
-
|
|
31
|
-
const DefaultStory = () => {
|
|
32
|
-
const { themeMode } = useThemeContext();
|
|
33
|
-
const [space] = useSpaces();
|
|
34
|
-
const [blockstore, setBlockstore] = useState<Blockstore>();
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (space) {
|
|
38
|
-
console.log('SPACE ID', space.id);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const create = async () => {
|
|
42
|
-
// NOTE: We don't pass in the URL of the Blob service, so we don't use it.
|
|
43
|
-
const bs = createBlockstore();
|
|
44
|
-
await bs.open();
|
|
45
|
-
setBlockstore(bs);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
create().catch(console.error);
|
|
49
|
-
}, [space]);
|
|
50
|
-
|
|
51
|
-
const { parentRef, view, focusAttributes } = useTextEditor(
|
|
52
|
-
() => ({
|
|
53
|
-
initialValue,
|
|
54
|
-
extensions: [
|
|
55
|
-
createBasicExtensions(),
|
|
56
|
-
createMarkdownExtensions(),
|
|
57
|
-
createThemeExtensions({ themeMode, syntaxHighlighting: true }),
|
|
58
|
-
blockstore && space ? [image({ blockstore, instances, space })] : [],
|
|
59
|
-
decorateMarkdown(),
|
|
60
|
-
],
|
|
61
|
-
}),
|
|
62
|
-
[themeMode, blockstore, space],
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const onFileChange = async (event: ChangeEvent<HTMLInputElement>) => {
|
|
66
|
-
const file = event.target.files?.[0];
|
|
67
|
-
if (file) {
|
|
68
|
-
if (blockstore) {
|
|
69
|
-
if (space) {
|
|
70
|
-
const info = await upload({ blockstore, file, space });
|
|
71
|
-
if (info && view) {
|
|
72
|
-
processEditorPayload(view, { type: 'image', data: info.url });
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
console.warn('Space is undefined.');
|
|
76
|
-
}
|
|
77
|
-
} else {
|
|
78
|
-
console.warn("Blockstore hasn't been initialised yet.");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
<>
|
|
85
|
-
<div className='mb-2'>
|
|
86
|
-
<input type='file' accept='image/*' onChange={onFileChange} />
|
|
87
|
-
</div>
|
|
88
|
-
<div className='w-[50rem]' ref={parentRef} {...focusAttributes} />
|
|
89
|
-
</>
|
|
90
|
-
);
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const meta = {
|
|
94
|
-
title: 'plugins/plugin-wnfs/extensions/image',
|
|
95
|
-
render: DefaultStory,
|
|
96
|
-
decorators: [withTheme(), withClientProvider({ createIdentity: true, createSpace: true })],
|
|
97
|
-
} satisfies Meta<typeof DefaultStory>;
|
|
98
|
-
|
|
99
|
-
export default meta;
|
|
100
|
-
|
|
101
|
-
type Story = StoryObj<typeof meta>;
|
|
102
|
-
|
|
103
|
-
export const Default: Story = {};
|
package/src/extensions/image.tsx
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { syntaxTree } from '@codemirror/language';
|
|
6
|
-
import { type EditorState, type Extension, type Range, StateField } from '@codemirror/state';
|
|
7
|
-
import { Decoration, EditorView, WidgetType } from '@codemirror/view';
|
|
8
|
-
import type { Blockstore } from 'interface-blockstore';
|
|
9
|
-
import React from 'react';
|
|
10
|
-
import { createRoot } from 'react-dom/client';
|
|
11
|
-
|
|
12
|
-
import { type Space } from '@dxos/react-client/echo';
|
|
13
|
-
import { Status, ThemeProvider } from '@dxos/react-ui';
|
|
14
|
-
import { focusField } from '@dxos/ui-editor';
|
|
15
|
-
import { defaultTx } from '@dxos/ui-theme';
|
|
16
|
-
import { type MaybePromise } from '@dxos/util';
|
|
17
|
-
|
|
18
|
-
import { type WnfsCapabilities } from '#types';
|
|
19
|
-
|
|
20
|
-
import { getBlobUrl, getPathFromUrl, loadWnfs } from '../helpers';
|
|
21
|
-
|
|
22
|
-
const WAIT_UNTIL_LOADER = 1500;
|
|
23
|
-
|
|
24
|
-
export type ImageOptions = {
|
|
25
|
-
blockstore: Blockstore;
|
|
26
|
-
instances: WnfsCapabilities.Instances;
|
|
27
|
-
space: Space;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Create WNFS image decorations.
|
|
32
|
-
*/
|
|
33
|
-
export const image = (options: ImageOptions): Extension[] => {
|
|
34
|
-
const blobUrlCache: Record<string, string> = {};
|
|
35
|
-
const preloaded = new Set<string>();
|
|
36
|
-
|
|
37
|
-
const preload = (url: string) => {
|
|
38
|
-
if (!preloaded.has(url)) {
|
|
39
|
-
const img = document.createElement('img');
|
|
40
|
-
img.src = url;
|
|
41
|
-
preloaded.add(url);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return [
|
|
46
|
-
StateField.define({
|
|
47
|
-
create: (state) => {
|
|
48
|
-
return Decoration.set(
|
|
49
|
-
buildDecorations({ from: 0, to: state.doc.length, state, blobUrlCache, preload, options }),
|
|
50
|
-
);
|
|
51
|
-
},
|
|
52
|
-
update: (value, tr) => {
|
|
53
|
-
if (!tr.docChanged && !tr.selection) {
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Find range of changes and cursor changes.
|
|
58
|
-
const cursor = tr.state.selection.main.head;
|
|
59
|
-
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
60
|
-
let from = Math.min(cursor, oldCursor);
|
|
61
|
-
let to = Math.max(cursor, oldCursor);
|
|
62
|
-
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
63
|
-
from = Math.min(from, fromB);
|
|
64
|
-
to = Math.max(to, toB);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Expand to cover lines.
|
|
68
|
-
from = tr.state.doc.lineAt(from).from;
|
|
69
|
-
to = tr.state.doc.lineAt(to).to;
|
|
70
|
-
|
|
71
|
-
return value.map(tr.changes).update({
|
|
72
|
-
filterFrom: from,
|
|
73
|
-
filterTo: to,
|
|
74
|
-
filter: () => false,
|
|
75
|
-
add: buildDecorations({ from, to, state: tr.state, blobUrlCache, preload, options }),
|
|
76
|
-
});
|
|
77
|
-
},
|
|
78
|
-
provide: (field) => EditorView.decorations.from(field),
|
|
79
|
-
}),
|
|
80
|
-
];
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const buildDecorations = ({
|
|
84
|
-
state,
|
|
85
|
-
from,
|
|
86
|
-
to,
|
|
87
|
-
blobUrlCache,
|
|
88
|
-
preload,
|
|
89
|
-
options: { blockstore, instances, space },
|
|
90
|
-
}: {
|
|
91
|
-
state: EditorState;
|
|
92
|
-
from: number;
|
|
93
|
-
to: number;
|
|
94
|
-
blobUrlCache: Record<string, string>;
|
|
95
|
-
preload: (url: string) => void;
|
|
96
|
-
options: ImageOptions;
|
|
97
|
-
}) => {
|
|
98
|
-
const decorations: Range<Decoration>[] = [];
|
|
99
|
-
const cursor = state.selection.main.head;
|
|
100
|
-
syntaxTree(state).iterate({
|
|
101
|
-
enter: (node) => {
|
|
102
|
-
if (node.name !== 'Image') {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const urlNode = node.node.getChild('URL');
|
|
107
|
-
if (!urlNode) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
112
|
-
const wnfsUrl = state.sliceDoc(urlNode.from, urlNode.to);
|
|
113
|
-
if (!wnfsUrl.startsWith('wnfs://')) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const path = getPathFromUrl(wnfsUrl);
|
|
118
|
-
|
|
119
|
-
// Cannot load images from other spaces
|
|
120
|
-
const spaceId = path[1];
|
|
121
|
-
if (spaceId !== space.id) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Load image contents into memory blob
|
|
126
|
-
const blobUrlPromise = (async () => {
|
|
127
|
-
const { directory, forest } = await loadWnfs({ blockstore, instances, space });
|
|
128
|
-
const url = await getBlobUrl({ wnfsUrl, blockstore, directory, forest });
|
|
129
|
-
blobUrlCache[path.join('/')] = url;
|
|
130
|
-
preload(url);
|
|
131
|
-
return url;
|
|
132
|
-
})();
|
|
133
|
-
|
|
134
|
-
decorations.push(
|
|
135
|
-
Decoration.replace({
|
|
136
|
-
block: true, // Prevent cursor from entering.
|
|
137
|
-
widget: new WnfsImageWidget(wnfsUrl, blobUrlCache[path.join('/')] ?? blobUrlPromise),
|
|
138
|
-
}).range(hide ? node.from : node.to, node.to),
|
|
139
|
-
);
|
|
140
|
-
},
|
|
141
|
-
from,
|
|
142
|
-
to,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
return decorations;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const createImg = (view: EditorView, url: string) => {
|
|
149
|
-
const img = document.createElement('img');
|
|
150
|
-
img.setAttribute('src', url);
|
|
151
|
-
img.setAttribute('class', 'cm-image-with-loader');
|
|
152
|
-
// If focused, hide image until successfully loaded to avoid flickering effects.
|
|
153
|
-
if (view.state.field(focusField)) {
|
|
154
|
-
img.onload = () => img.classList.add('cm-loaded-image');
|
|
155
|
-
} else {
|
|
156
|
-
img.classList.add('cm-loaded-image');
|
|
157
|
-
}
|
|
158
|
-
return img;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
class WnfsImageWidget extends WidgetType {
|
|
162
|
-
constructor(
|
|
163
|
-
readonly _wnfsUrl: string,
|
|
164
|
-
readonly _url: MaybePromise<string>,
|
|
165
|
-
) {
|
|
166
|
-
super();
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
override eq(other: this) {
|
|
170
|
-
return this._wnfsUrl === other._wnfsUrl;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
override toDOM(view: EditorView) {
|
|
174
|
-
if (typeof this._url === 'string') {
|
|
175
|
-
return createImg(view, this._url);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const widget = document.createElement('div');
|
|
179
|
-
widget.className = 'cm-image-wrapper';
|
|
180
|
-
const loader = document.createElement('div');
|
|
181
|
-
loader.className = 'mx-auto transition-opacity';
|
|
182
|
-
let loaderAdded = false;
|
|
183
|
-
|
|
184
|
-
const timeout = setTimeout(() => {
|
|
185
|
-
const root = createRoot(loader);
|
|
186
|
-
root.render(
|
|
187
|
-
<ThemeProvider tx={defaultTx}>
|
|
188
|
-
<Status indeterminate />
|
|
189
|
-
</ThemeProvider>,
|
|
190
|
-
);
|
|
191
|
-
widget.appendChild(loader);
|
|
192
|
-
loaderAdded = true;
|
|
193
|
-
}, WAIT_UNTIL_LOADER);
|
|
194
|
-
|
|
195
|
-
void this._url.then((url) => {
|
|
196
|
-
clearTimeout(timeout);
|
|
197
|
-
const img = createImg(view, url);
|
|
198
|
-
loaderAdded && widget.removeChild(loader);
|
|
199
|
-
widget.appendChild(img);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
return widget;
|
|
203
|
-
}
|
|
204
|
-
}
|
package/src/extensions/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Effect from 'effect/Effect';
|
|
6
|
-
|
|
7
|
-
import { Capability } from '@dxos/app-framework';
|
|
8
|
-
import { invariant } from '@dxos/invariant';
|
|
9
|
-
import { Operation } from '@dxos/operation';
|
|
10
|
-
import { ClientCapabilities } from '@dxos/plugin-client/types';
|
|
11
|
-
|
|
12
|
-
import { upload } from '../helpers';
|
|
13
|
-
import { WnfsCapabilities, WnfsFile } from '../types';
|
|
14
|
-
import { CreateFile } from './definitions';
|
|
15
|
-
|
|
16
|
-
const handler: Operation.WithHandler<typeof CreateFile> = CreateFile.pipe(
|
|
17
|
-
Operation.withHandler(
|
|
18
|
-
Effect.fnUntraced(function* ({ file, db }) {
|
|
19
|
-
const client = yield* Capability.get(ClientCapabilities.Client);
|
|
20
|
-
const blockstore = yield* Capability.get(WnfsCapabilities.Blockstore);
|
|
21
|
-
const space = client.spaces.get(db.spaceId);
|
|
22
|
-
invariant(space, 'Space not found');
|
|
23
|
-
const info = yield* Effect.promise(() => upload({ file, blockstore, space }));
|
|
24
|
-
return {
|
|
25
|
-
object: WnfsFile.make({ name: info.name, type: info.type, cid: info.cid }),
|
|
26
|
-
};
|
|
27
|
-
}),
|
|
28
|
-
),
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
export default handler;
|
package/src/operations/create.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Effect from 'effect/Effect';
|
|
6
|
-
|
|
7
|
-
import { Operation } from '@dxos/operation';
|
|
8
|
-
|
|
9
|
-
import { WnfsFile } from '../types';
|
|
10
|
-
import { Create } from './definitions';
|
|
11
|
-
|
|
12
|
-
const handler: Operation.WithHandler<typeof Create> = Create.pipe(
|
|
13
|
-
Operation.withHandler(
|
|
14
|
-
Effect.fnUntraced(function* ({ name, type, cid }) {
|
|
15
|
-
return {
|
|
16
|
-
object: WnfsFile.make({ name, type, cid }),
|
|
17
|
-
};
|
|
18
|
-
}),
|
|
19
|
-
),
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
export default handler;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Schema from 'effect/Schema';
|
|
6
|
-
|
|
7
|
-
import { Capability } from '@dxos/app-framework';
|
|
8
|
-
import { FileInfoSchema } from '@dxos/app-toolkit';
|
|
9
|
-
import { Database } from '@dxos/echo';
|
|
10
|
-
import { Operation } from '@dxos/operation';
|
|
11
|
-
|
|
12
|
-
import { meta } from '#meta';
|
|
13
|
-
|
|
14
|
-
import { WnfsFile, WnfsAction } from '../types';
|
|
15
|
-
|
|
16
|
-
const WNFS_OPERATION = `${meta.id}.operation`;
|
|
17
|
-
|
|
18
|
-
export const Create = Operation.make({
|
|
19
|
-
meta: { key: `${WNFS_OPERATION}.create`, name: 'Create WNFS File' },
|
|
20
|
-
services: [Capability.Service],
|
|
21
|
-
input: FileInfoSchema.pick('name', 'type', 'cid').pipe(Schema.required),
|
|
22
|
-
output: Schema.Struct({
|
|
23
|
-
object: WnfsFile.File,
|
|
24
|
-
}),
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export const Upload = Operation.make({
|
|
28
|
-
meta: { key: `${WNFS_OPERATION}.upload`, name: 'Upload File' },
|
|
29
|
-
services: [Capability.Service],
|
|
30
|
-
input: Schema.extend(WnfsAction.UploadFileSchema, Schema.Struct({ db: Database.Database })),
|
|
31
|
-
output: Schema.required(FileInfoSchema),
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
export const CreateFile = Operation.make({
|
|
35
|
-
meta: { key: `${WNFS_OPERATION}.create-file`, name: 'Create File' },
|
|
36
|
-
services: [Capability.Service],
|
|
37
|
-
input: Schema.extend(WnfsAction.UploadFileSchema, Schema.Struct({ db: Database.Database })),
|
|
38
|
-
output: Schema.Struct({
|
|
39
|
-
object: WnfsFile.File,
|
|
40
|
-
}),
|
|
41
|
-
});
|
package/src/operations/index.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { OperationHandlerSet } from '@dxos/operation';
|
|
6
|
-
|
|
7
|
-
export * as WnfsOperation from './definitions';
|
|
8
|
-
|
|
9
|
-
export const WnfsOperationHandlerSet = OperationHandlerSet.lazy(
|
|
10
|
-
() => import('./create'),
|
|
11
|
-
() => import('./create-file'),
|
|
12
|
-
() => import('./upload'),
|
|
13
|
-
);
|
package/src/operations/upload.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Effect from 'effect/Effect';
|
|
6
|
-
|
|
7
|
-
import { Capability } from '@dxos/app-framework';
|
|
8
|
-
import { invariant } from '@dxos/invariant';
|
|
9
|
-
import { Operation } from '@dxos/operation';
|
|
10
|
-
import { ClientCapabilities } from '@dxos/plugin-client/types';
|
|
11
|
-
|
|
12
|
-
import { upload } from '../helpers';
|
|
13
|
-
import { WnfsCapabilities } from '../types';
|
|
14
|
-
import { Upload } from './definitions';
|
|
15
|
-
|
|
16
|
-
const handler: Operation.WithHandler<typeof Upload> = Upload.pipe(
|
|
17
|
-
Operation.withHandler(
|
|
18
|
-
Effect.fnUntraced(function* ({ file, db }) {
|
|
19
|
-
const client = yield* Capability.get(ClientCapabilities.Client);
|
|
20
|
-
const blockstore = yield* Capability.get(WnfsCapabilities.Blockstore);
|
|
21
|
-
const space = client.spaces.get(db.spaceId);
|
|
22
|
-
invariant(space, 'Space not found');
|
|
23
|
-
const info = yield* Effect.promise(() => upload({ file, blockstore, space }));
|
|
24
|
-
return info;
|
|
25
|
-
}),
|
|
26
|
-
),
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
export default handler;
|
package/src/types/File.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Schema from 'effect/Schema';
|
|
6
|
-
|
|
7
|
-
import { Annotation, Obj, Type } from '@dxos/echo';
|
|
8
|
-
import { FormInputAnnotation } from '@dxos/echo/internal';
|
|
9
|
-
|
|
10
|
-
export const File = Schema.Struct({
|
|
11
|
-
name: Schema.String.pipe(Schema.optional),
|
|
12
|
-
type: Schema.String.pipe(FormInputAnnotation.set(false)),
|
|
13
|
-
cid: Schema.String.pipe(FormInputAnnotation.set(false)),
|
|
14
|
-
timestamp: Schema.String.pipe(FormInputAnnotation.set(false), Schema.optional),
|
|
15
|
-
}).pipe(
|
|
16
|
-
Type.object({
|
|
17
|
-
typename: 'org.dxos.type.file',
|
|
18
|
-
version: '0.1.0',
|
|
19
|
-
}),
|
|
20
|
-
Annotation.IconAnnotation.set({
|
|
21
|
-
icon: 'ph--file--regular',
|
|
22
|
-
hue: 'teal',
|
|
23
|
-
}),
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
export type File = Schema.Schema.Type<typeof File>;
|
|
27
|
-
|
|
28
|
-
export const make = (props: Obj.MakeProps<typeof File>) => Obj.make(File, props);
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type Blockstore } from 'interface-blockstore';
|
|
6
|
-
import { type PrivateDirectory, type PrivateForest } from 'wnfs';
|
|
7
|
-
|
|
8
|
-
import { Capability } from '@dxos/app-framework';
|
|
9
|
-
import { type SpaceId } from '@dxos/keys';
|
|
10
|
-
|
|
11
|
-
import { meta } from '#meta';
|
|
12
|
-
|
|
13
|
-
export namespace WnfsCapabilities {
|
|
14
|
-
export const Blockstore = Capability.make<Blockstore>(`${meta.id}.capability.blockstore`);
|
|
15
|
-
|
|
16
|
-
export type Instances = Record<SpaceId, { directory: PrivateDirectory; forest: PrivateForest }>;
|
|
17
|
-
export const Instances = Capability.make<Instances>(`${meta.id}.capability.instances`);
|
|
18
|
-
}
|
package/src/types/types.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Schema from 'effect/Schema';
|
|
6
|
-
|
|
7
|
-
import { meta } from '#meta';
|
|
8
|
-
|
|
9
|
-
export namespace WnfsAction {
|
|
10
|
-
export const UploadAnnotationId = Symbol.for(`${meta.id}.annotation.upload`);
|
|
11
|
-
|
|
12
|
-
export const UploadFileSchema = Schema.Struct({
|
|
13
|
-
file: Schema.instanceOf(File).annotations({
|
|
14
|
-
[UploadAnnotationId]: {
|
|
15
|
-
// Accept file types.
|
|
16
|
-
'image/*': ['.jpg', '.jpeg', '.png', '.gif'],
|
|
17
|
-
'application/pdf': ['.pdf'],
|
|
18
|
-
// TODO(wittjosiah): Add more file types.
|
|
19
|
-
},
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export type UploadFileForm = Schema.Schema.Type<typeof UploadFileSchema>;
|
|
24
|
-
}
|
|
File without changes
|