@dxos/plugin-wnfs 0.8.4-main.c4373fc → 0.8.4-main.d9fc60f731
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-D6VTEQIE.mjs → neutral/chunk-L33MOD23.mjs} +85 -25
- 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 +3 -1
- 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 +3 -2
- package/dist/types/src/capabilities/blockstore.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +5 -6
- 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 +1 -1
- package/dist/types/src/helpers/load.d.ts.map +1 -1
- package/dist/types/src/helpers/upload.d.ts +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 +2 -2
- 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 -12
- 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 -2
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +86 -50
- package/src/WnfsPlugin.node.ts +11 -0
- package/src/WnfsPlugin.test.ts +20 -0
- package/src/WnfsPlugin.tsx +32 -76
- package/src/WnfsPlugin.workerd.ts +11 -0
- package/src/annotations.ts +16 -0
- package/src/blockstore.ts +1 -1
- package/src/capabilities/backend.ts +43 -0
- package/src/capabilities/blockstore.ts +14 -10
- package/src/capabilities/index.ts +12 -7
- package/src/capabilities/url-resolver.ts +44 -0
- package/src/helpers/load.ts +37 -18
- package/src/helpers/upload.ts +14 -2
- package/src/index.ts +3 -2
- package/src/meta.ts +26 -5
- package/src/plugin.ts +9 -0
- package/src/translations.ts +2 -11
- package/src/types/WnfsCapabilities.ts +18 -0
- package/src/types/index.ts +1 -2
- package/src/vite-env.d.ts +10 -0
- package/dist/lib/browser/FileContainer-A4OJVMVS.mjs +0 -103
- package/dist/lib/browser/FileContainer-A4OJVMVS.mjs.map +0 -7
- package/dist/lib/browser/blockstore-HK4IYHJJ.mjs +0 -273
- package/dist/lib/browser/blockstore-HK4IYHJJ.mjs.map +0 -7
- package/dist/lib/browser/chunk-D6VTEQIE.mjs.map +0 -7
- package/dist/lib/browser/chunk-FYRZOKHG.mjs +0 -13
- package/dist/lib/browser/chunk-FYRZOKHG.mjs.map +0 -7
- package/dist/lib/browser/chunk-PAIK4SJ3.mjs +0 -16
- package/dist/lib/browser/chunk-PAIK4SJ3.mjs.map +0 -7
- package/dist/lib/browser/chunk-QQB5IBX7.mjs +0 -64
- package/dist/lib/browser/chunk-QQB5IBX7.mjs.map +0 -7
- package/dist/lib/browser/chunk-Y5JMTQ74.mjs +0 -16
- package/dist/lib/browser/chunk-Y5JMTQ74.mjs.map +0 -7
- package/dist/lib/browser/file-uploader-5S37ZZCW.mjs +0 -30
- package/dist/lib/browser/file-uploader-5S37ZZCW.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -116
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-3KMIEDBO.mjs +0 -49
- package/dist/lib/browser/intent-resolver-3KMIEDBO.mjs.map +0 -7
- package/dist/lib/browser/markdown-5XD3I2NQ.mjs +0 -209
- package/dist/lib/browser/markdown-5XD3I2NQ.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/react-surface-5D7M3Y4W.mjs +0 -88
- package/dist/lib/browser/react-surface-5D7M3Y4W.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -10
- package/dist/types/src/capabilities/capabilities.d.ts +0 -12
- package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
- package/dist/types/src/capabilities/file-uploader.d.ts +0 -4
- package/dist/types/src/capabilities/file-uploader.d.ts.map +0 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/markdown.d.ts +0 -4
- package/dist/types/src/capabilities/markdown.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface.d.ts +0 -4
- package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
- package/dist/types/src/components/FileContainer.d.ts +0 -9
- package/dist/types/src/components/FileContainer.d.ts.map +0 -1
- package/dist/types/src/components/FileInput.d.ts +0 -8
- package/dist/types/src/components/FileInput.d.ts.map +0 -1
- package/dist/types/src/components/FilePreview.d.ts +0 -11
- package/dist/types/src/components/FilePreview.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/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/types/file.d.ts +0 -16
- package/dist/types/src/types/file.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts +0 -51
- package/dist/types/src/types/types.d.ts.map +0 -1
- package/src/capabilities/capabilities.ts +0 -18
- package/src/capabilities/file-uploader.ts +0 -27
- package/src/capabilities/intent-resolver.ts +0 -33
- package/src/capabilities/markdown.ts +0 -26
- package/src/capabilities/react-surface.tsx +0 -45
- package/src/components/FileContainer.tsx +0 -49
- package/src/components/FileInput.tsx +0 -47
- package/src/components/FilePreview.tsx +0 -23
- package/src/components/index.ts +0 -9
- package/src/extensions/image.stories.tsx +0 -104
- package/src/extensions/image.tsx +0 -203
- package/src/extensions/index.ts +0 -5
- package/src/types/file.ts +0 -17
- package/src/types/types.ts +0 -41
- /package/dist/lib/{browser/types/index.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import { useCapability } from '@dxos/app-framework';
|
|
8
|
-
import { invariant } from '@dxos/invariant';
|
|
9
|
-
import { getSpace } from '@dxos/react-client/echo';
|
|
10
|
-
import { useAsyncEffect } from '@dxos/react-ui';
|
|
11
|
-
import { StackItem } from '@dxos/react-ui-stack';
|
|
12
|
-
|
|
13
|
-
import { WnfsCapabilities } from '../capabilities';
|
|
14
|
-
import { filePath, getBlobUrl, loadWnfs, wnfsUrl } from '../helpers';
|
|
15
|
-
import { type FileType } from '../types';
|
|
16
|
-
|
|
17
|
-
import { FilePreview } from './FilePreview';
|
|
18
|
-
|
|
19
|
-
export type FileContainerProps = {
|
|
20
|
-
role: string;
|
|
21
|
-
file: FileType;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const FileContainer = ({ file }: FileContainerProps) => {
|
|
25
|
-
const blockstore = useCapability(WnfsCapabilities.Blockstore);
|
|
26
|
-
const instances = useCapability(WnfsCapabilities.Instances);
|
|
27
|
-
const [blobUrl, setBlobUrl] = useState<string>();
|
|
28
|
-
|
|
29
|
-
useAsyncEffect(async () => {
|
|
30
|
-
const space = getSpace(file);
|
|
31
|
-
invariant(space);
|
|
32
|
-
const { directory, forest } = await loadWnfs({ blockstore, instances, space });
|
|
33
|
-
const path = filePath(file.cid.toString(), space);
|
|
34
|
-
const url = await getBlobUrl({ wnfsUrl: wnfsUrl(path), blockstore, directory, forest, type: file.type });
|
|
35
|
-
setBlobUrl(url);
|
|
36
|
-
}, [file]);
|
|
37
|
-
|
|
38
|
-
if (!blobUrl) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<StackItem.Content>
|
|
44
|
-
<FilePreview type={file.type} url={blobUrl} />
|
|
45
|
-
</StackItem.Content>
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export default FileContainer;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import type * as Schema from 'effect/Schema';
|
|
6
|
-
import React, { useCallback } from 'react';
|
|
7
|
-
import { useDropzone } from 'react-dropzone';
|
|
8
|
-
|
|
9
|
-
import { findAnnotation } from '@dxos/effect';
|
|
10
|
-
import { useTranslation } from '@dxos/react-ui';
|
|
11
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
12
|
-
|
|
13
|
-
import { meta } from '../meta';
|
|
14
|
-
import { WnfsAction } from '../types';
|
|
15
|
-
|
|
16
|
-
export type FileInputProps = {
|
|
17
|
-
schema: Schema.Schema.Any;
|
|
18
|
-
onChange: (file: File) => void;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const FileInput = ({ schema, onChange }: FileInputProps) => {
|
|
22
|
-
const { t } = useTranslation(meta.id);
|
|
23
|
-
const accept = findAnnotation<Record<string, string[]>>(schema.ast, WnfsAction.UploadAnnotationId);
|
|
24
|
-
|
|
25
|
-
const onDropAccepted = useCallback((files: File[]) => onChange?.(files[0]), [onChange]);
|
|
26
|
-
|
|
27
|
-
const { acceptedFiles, getRootProps, getInputProps, isFocused, isDragAccept, isDragReject } = useDropzone({
|
|
28
|
-
multiple: false,
|
|
29
|
-
accept,
|
|
30
|
-
onDropAccepted,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<div
|
|
35
|
-
{...getRootProps()}
|
|
36
|
-
className={mx(
|
|
37
|
-
'flex flex-col items-center p-8 border border-separator',
|
|
38
|
-
isFocused && 'focus-ring',
|
|
39
|
-
isDragAccept && 'bg-attention',
|
|
40
|
-
isDragReject && 'border-error',
|
|
41
|
-
)}
|
|
42
|
-
>
|
|
43
|
-
<input {...getInputProps()} />
|
|
44
|
-
{acceptedFiles[0] ? <p>{acceptedFiles[0].name}</p> : <p>{t('file input placeholder')}</p>}
|
|
45
|
-
</div>
|
|
46
|
-
);
|
|
47
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React from 'react';
|
|
6
|
-
|
|
7
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
8
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
9
|
-
|
|
10
|
-
export type FilePreviewProps = ThemedClassName<{ type: string; url: string }>;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* File/content preview iframe.
|
|
14
|
-
*/
|
|
15
|
-
export const FilePreview = ({ type, url, classNames }: FilePreviewProps) => {
|
|
16
|
-
if (type.startsWith('image/')) {
|
|
17
|
-
return <img className={mx('w-full h-full object-contain', classNames)} src={url} />;
|
|
18
|
-
} else if (type.startsWith('video/')) {
|
|
19
|
-
return <video className={mx('w-full h-full object-contain', classNames)} src={url} controls />;
|
|
20
|
-
} else {
|
|
21
|
-
return <iframe className={mx('w-full h-full overflow-auto', classNames)} src={url} />;
|
|
22
|
-
}
|
|
23
|
-
};
|
package/src/components/index.ts
DELETED
|
@@ -1,104 +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 { faker } from '@dxos/random';
|
|
10
|
-
import { useSpace } from '@dxos/react-client/echo';
|
|
11
|
-
import { withClientProvider } from '@dxos/react-client/testing';
|
|
12
|
-
import { useThemeContext } from '@dxos/react-ui';
|
|
13
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
14
|
-
import {
|
|
15
|
-
createBasicExtensions,
|
|
16
|
-
createMarkdownExtensions,
|
|
17
|
-
createThemeExtensions,
|
|
18
|
-
decorateMarkdown,
|
|
19
|
-
processEditorPayload,
|
|
20
|
-
useTextEditor,
|
|
21
|
-
} from '@dxos/react-ui-editor';
|
|
22
|
-
|
|
23
|
-
import { create as createBlockstore } from '../blockstore';
|
|
24
|
-
import { upload } from '../helpers';
|
|
25
|
-
|
|
26
|
-
import { image } from './image';
|
|
27
|
-
|
|
28
|
-
faker.seed(1);
|
|
29
|
-
const initialValue = faker.lorem.paragraphs(100);
|
|
30
|
-
const instances = {};
|
|
31
|
-
|
|
32
|
-
const DefaultStory = () => {
|
|
33
|
-
const { themeMode } = useThemeContext();
|
|
34
|
-
const space = useSpace();
|
|
35
|
-
const [blockstore, setBlockstore] = useState<Blockstore>();
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (space) {
|
|
39
|
-
console.log('SPACE ID', space.id);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const create = async () => {
|
|
43
|
-
// NOTE: We don't pass in the URL of the Blob service, so we don't use it.
|
|
44
|
-
const bs = createBlockstore();
|
|
45
|
-
await bs.open();
|
|
46
|
-
setBlockstore(bs);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
create().catch(console.error);
|
|
50
|
-
}, [space]);
|
|
51
|
-
|
|
52
|
-
const { parentRef, view, focusAttributes } = useTextEditor(
|
|
53
|
-
() => ({
|
|
54
|
-
initialValue,
|
|
55
|
-
extensions: [
|
|
56
|
-
createBasicExtensions(),
|
|
57
|
-
createMarkdownExtensions(),
|
|
58
|
-
createThemeExtensions({ themeMode, syntaxHighlighting: true }),
|
|
59
|
-
blockstore && space ? [image({ blockstore, instances, space })] : [],
|
|
60
|
-
decorateMarkdown(),
|
|
61
|
-
],
|
|
62
|
-
}),
|
|
63
|
-
[themeMode, blockstore, space],
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
const onFileChange = async (event: ChangeEvent<HTMLInputElement>) => {
|
|
67
|
-
const file = event.target.files?.[0];
|
|
68
|
-
if (file) {
|
|
69
|
-
if (blockstore) {
|
|
70
|
-
if (space) {
|
|
71
|
-
const info = await upload({ blockstore, file, space });
|
|
72
|
-
if (info && view) {
|
|
73
|
-
processEditorPayload(view, { type: 'image', data: info.url });
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
console.warn('Space is undefined.');
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
console.warn("Blockstore hasn't been initialised yet.");
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<>
|
|
86
|
-
<div className='mb-2'>
|
|
87
|
-
<input type='file' accept='image/*' onChange={onFileChange} />
|
|
88
|
-
</div>
|
|
89
|
-
<div className='w-[50rem]' ref={parentRef} {...focusAttributes} />
|
|
90
|
-
</>
|
|
91
|
-
);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const meta = {
|
|
95
|
-
title: 'plugins/plugin-wnfs/image',
|
|
96
|
-
render: DefaultStory,
|
|
97
|
-
decorators: [withTheme, withClientProvider({ createIdentity: true, createSpace: true })],
|
|
98
|
-
} satisfies Meta<typeof DefaultStory>;
|
|
99
|
-
|
|
100
|
-
export default meta;
|
|
101
|
-
|
|
102
|
-
type Story = StoryObj<typeof meta>;
|
|
103
|
-
|
|
104
|
-
export const Default: Story = {};
|
package/src/extensions/image.tsx
DELETED
|
@@ -1,203 +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/react-ui-editor';
|
|
15
|
-
import { defaultTx } from '@dxos/react-ui-theme';
|
|
16
|
-
import { type MaybePromise } from '@dxos/util';
|
|
17
|
-
|
|
18
|
-
import { type WnfsCapabilities } from '../capabilities';
|
|
19
|
-
import { getBlobUrl, getPathFromUrl, loadWnfs } from '../helpers';
|
|
20
|
-
|
|
21
|
-
const WAIT_UNTIL_LOADER = 1500;
|
|
22
|
-
|
|
23
|
-
export type ImageOptions = {
|
|
24
|
-
blockstore: Blockstore;
|
|
25
|
-
instances: WnfsCapabilities.Instances;
|
|
26
|
-
space: Space;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Create WNFS image decorations.
|
|
31
|
-
*/
|
|
32
|
-
export const image = (options: ImageOptions): Extension[] => {
|
|
33
|
-
const blobUrlCache: Record<string, string> = {};
|
|
34
|
-
const preloaded = new Set<string>();
|
|
35
|
-
|
|
36
|
-
const preload = (url: string) => {
|
|
37
|
-
if (!preloaded.has(url)) {
|
|
38
|
-
const img = document.createElement('img');
|
|
39
|
-
img.src = url;
|
|
40
|
-
preloaded.add(url);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
return [
|
|
45
|
-
StateField.define({
|
|
46
|
-
create: (state) => {
|
|
47
|
-
return Decoration.set(
|
|
48
|
-
buildDecorations({ from: 0, to: state.doc.length, state, blobUrlCache, preload, options }),
|
|
49
|
-
);
|
|
50
|
-
},
|
|
51
|
-
update: (value, tr) => {
|
|
52
|
-
if (!tr.docChanged && !tr.selection) {
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Find range of changes and cursor changes.
|
|
57
|
-
const cursor = tr.state.selection.main.head;
|
|
58
|
-
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
59
|
-
let from = Math.min(cursor, oldCursor);
|
|
60
|
-
let to = Math.max(cursor, oldCursor);
|
|
61
|
-
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
62
|
-
from = Math.min(from, fromB);
|
|
63
|
-
to = Math.max(to, toB);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Expand to cover lines.
|
|
67
|
-
from = tr.state.doc.lineAt(from).from;
|
|
68
|
-
to = tr.state.doc.lineAt(to).to;
|
|
69
|
-
|
|
70
|
-
return value.map(tr.changes).update({
|
|
71
|
-
filterFrom: from,
|
|
72
|
-
filterTo: to,
|
|
73
|
-
filter: () => false,
|
|
74
|
-
add: buildDecorations({ from, to, state: tr.state, blobUrlCache, preload, options }),
|
|
75
|
-
});
|
|
76
|
-
},
|
|
77
|
-
provide: (field) => EditorView.decorations.from(field),
|
|
78
|
-
}),
|
|
79
|
-
];
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const buildDecorations = ({
|
|
83
|
-
state,
|
|
84
|
-
from,
|
|
85
|
-
to,
|
|
86
|
-
blobUrlCache,
|
|
87
|
-
preload,
|
|
88
|
-
options: { blockstore, instances, space },
|
|
89
|
-
}: {
|
|
90
|
-
state: EditorState;
|
|
91
|
-
from: number;
|
|
92
|
-
to: number;
|
|
93
|
-
blobUrlCache: Record<string, string>;
|
|
94
|
-
preload: (url: string) => void;
|
|
95
|
-
options: ImageOptions;
|
|
96
|
-
}) => {
|
|
97
|
-
const decorations: Range<Decoration>[] = [];
|
|
98
|
-
const cursor = state.selection.main.head;
|
|
99
|
-
syntaxTree(state).iterate({
|
|
100
|
-
enter: (node) => {
|
|
101
|
-
if (node.name !== 'Image') {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const urlNode = node.node.getChild('URL');
|
|
106
|
-
if (!urlNode) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
111
|
-
const wnfsUrl = state.sliceDoc(urlNode.from, urlNode.to);
|
|
112
|
-
if (!wnfsUrl.startsWith('wnfs://')) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const path = getPathFromUrl(wnfsUrl);
|
|
117
|
-
|
|
118
|
-
// Cannot load images from other spaces
|
|
119
|
-
const spaceId = path[1];
|
|
120
|
-
if (spaceId !== space.id) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Load image contents into memory blob
|
|
125
|
-
const blobUrlPromise = (async () => {
|
|
126
|
-
const { directory, forest } = await loadWnfs({ blockstore, instances, space });
|
|
127
|
-
const url = await getBlobUrl({ wnfsUrl, blockstore, directory, forest });
|
|
128
|
-
blobUrlCache[path.join('/')] = url;
|
|
129
|
-
preload(url);
|
|
130
|
-
return url;
|
|
131
|
-
})();
|
|
132
|
-
|
|
133
|
-
decorations.push(
|
|
134
|
-
Decoration.replace({
|
|
135
|
-
block: true, // Prevent cursor from entering.
|
|
136
|
-
widget: new WnfsImageWidget(wnfsUrl, blobUrlCache[path.join('/')] ?? blobUrlPromise),
|
|
137
|
-
}).range(hide ? node.from : node.to, node.to),
|
|
138
|
-
);
|
|
139
|
-
},
|
|
140
|
-
from,
|
|
141
|
-
to,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
return decorations;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const createImg = (view: EditorView, url: string) => {
|
|
148
|
-
const img = document.createElement('img');
|
|
149
|
-
img.setAttribute('src', url);
|
|
150
|
-
img.setAttribute('class', 'cm-image-with-loader');
|
|
151
|
-
// If focused, hide image until successfully loaded to avoid flickering effects.
|
|
152
|
-
if (view.state.field(focusField)) {
|
|
153
|
-
img.onload = () => img.classList.add('cm-loaded-image');
|
|
154
|
-
} else {
|
|
155
|
-
img.classList.add('cm-loaded-image');
|
|
156
|
-
}
|
|
157
|
-
return img;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
class WnfsImageWidget extends WidgetType {
|
|
161
|
-
constructor(
|
|
162
|
-
readonly _wnfsUrl: string,
|
|
163
|
-
readonly _url: MaybePromise<string>,
|
|
164
|
-
) {
|
|
165
|
-
super();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
override eq(other: this): boolean {
|
|
169
|
-
return this._wnfsUrl === (other as any as WnfsImageWidget)._wnfsUrl;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
override toDOM(view: EditorView): HTMLDivElement {
|
|
173
|
-
if (typeof this._url === 'string') {
|
|
174
|
-
return createImg(view, this._url);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const widget = document.createElement('div');
|
|
178
|
-
widget.className = 'cm-image-wrapper';
|
|
179
|
-
const loader = document.createElement('div');
|
|
180
|
-
loader.className = 'mx-auto transition-opacity';
|
|
181
|
-
let loaderAdded = false;
|
|
182
|
-
|
|
183
|
-
const timeout = setTimeout(() => {
|
|
184
|
-
const root = createRoot(loader);
|
|
185
|
-
root.render(
|
|
186
|
-
<ThemeProvider tx={defaultTx}>
|
|
187
|
-
<Status indeterminate />
|
|
188
|
-
</ThemeProvider>,
|
|
189
|
-
);
|
|
190
|
-
widget.appendChild(loader);
|
|
191
|
-
loaderAdded = true;
|
|
192
|
-
}, WAIT_UNTIL_LOADER);
|
|
193
|
-
|
|
194
|
-
void this._url.then((url) => {
|
|
195
|
-
clearTimeout(timeout);
|
|
196
|
-
const img = createImg(view, url);
|
|
197
|
-
loaderAdded && widget.removeChild(loader);
|
|
198
|
-
widget.appendChild(img);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
return widget;
|
|
202
|
-
}
|
|
203
|
-
}
|
package/src/extensions/index.ts
DELETED
package/src/types/file.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Schema from 'effect/Schema';
|
|
6
|
-
|
|
7
|
-
import { TypedObject } from '@dxos/echo/internal';
|
|
8
|
-
|
|
9
|
-
export class FileType extends TypedObject({
|
|
10
|
-
typename: 'dxos.org/type/File',
|
|
11
|
-
version: '0.1.0',
|
|
12
|
-
})({
|
|
13
|
-
name: Schema.optional(Schema.String),
|
|
14
|
-
type: Schema.String,
|
|
15
|
-
cid: Schema.String,
|
|
16
|
-
timestamp: Schema.optional(Schema.String),
|
|
17
|
-
}) {}
|
package/src/types/types.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Schema from 'effect/Schema';
|
|
6
|
-
|
|
7
|
-
import { FileInfoSchema } from '@dxos/app-framework';
|
|
8
|
-
import { SpaceSchema } from '@dxos/react-client/echo';
|
|
9
|
-
|
|
10
|
-
import { meta } from '../meta';
|
|
11
|
-
|
|
12
|
-
import { FileType } from './file';
|
|
13
|
-
|
|
14
|
-
export namespace WnfsAction {
|
|
15
|
-
export class Create extends Schema.TaggedClass<Create>()(`${meta.id}/action/create`, {
|
|
16
|
-
input: FileInfoSchema.pick('name', 'type', 'cid').pipe(Schema.required),
|
|
17
|
-
output: Schema.Struct({
|
|
18
|
-
object: FileType,
|
|
19
|
-
}),
|
|
20
|
-
}) {}
|
|
21
|
-
|
|
22
|
-
export const UploadAnnotationId = Symbol.for(`${meta.id}/annotation/upload`);
|
|
23
|
-
|
|
24
|
-
export const UploadFileSchema = Schema.Struct({
|
|
25
|
-
file: Schema.instanceOf(File).annotations({
|
|
26
|
-
[UploadAnnotationId]: {
|
|
27
|
-
// Accept file types.
|
|
28
|
-
'image/*': ['.jpg', '.jpeg', '.png', '.gif'],
|
|
29
|
-
'application/pdf': ['.pdf'],
|
|
30
|
-
// TODO(wittjosiah): Add more file types.
|
|
31
|
-
},
|
|
32
|
-
}),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export type UploadFileForm = Schema.Schema.Type<typeof UploadFileSchema>;
|
|
36
|
-
|
|
37
|
-
export class Upload extends Schema.TaggedClass<Upload>()(`${meta.id}/action/upload`, {
|
|
38
|
-
input: Schema.extend(UploadFileSchema, Schema.Struct({ space: SpaceSchema })),
|
|
39
|
-
output: Schema.required(FileInfoSchema),
|
|
40
|
-
}) {}
|
|
41
|
-
}
|
|
File without changes
|