@dxos/plugin-wnfs 0.8.4-main.7996785055 → 0.8.4-main.8baae0fced
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/README.md +2 -2
- package/dist/lib/neutral/WnfsPlugin.mjs +39 -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-DHBQUTPC.mjs +47 -0
- package/dist/lib/neutral/backend-DHBQUTPC.mjs.map +7 -0
- package/dist/lib/{browser/blockstore-TUV5YIC5.mjs → neutral/blockstore-3NLSWC7Z.mjs} +6 -7
- package/dist/lib/neutral/blockstore-3NLSWC7Z.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/{browser/chunk-A7ETWNGC.mjs → neutral/chunk-7KFJ6LYH.mjs} +32 -17
- package/dist/lib/neutral/chunk-7KFJ6LYH.mjs.map +7 -0
- package/dist/lib/neutral/chunk-EKFVDDRY.mjs +26 -0
- package/dist/lib/neutral/chunk-EKFVDDRY.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -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/index.mjs +34 -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-UD3ZP77Y.mjs +47 -0
- package/dist/lib/neutral/url-resolver-UD3ZP77Y.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/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 -0
- package/dist/types/src/capabilities/index.d.ts +5 -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 +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 +2 -2
- package/dist/types/src/index.d.ts.map +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 +96 -49
- package/src/WnfsPlugin.node.ts +11 -0
- package/src/WnfsPlugin.test.ts +20 -0
- package/src/WnfsPlugin.tsx +11 -40
- package/src/WnfsPlugin.workerd.ts +11 -0
- package/src/capabilities/backend.ts +43 -0
- package/src/capabilities/{blockstore/blockstore.ts → blockstore.ts} +3 -2
- package/src/capabilities/index.ts +14 -5
- package/src/capabilities/url-resolver.ts +44 -0
- package/src/helpers/load.ts +3 -3
- package/src/helpers/upload.ts +2 -2
- package/src/index.ts +2 -2
- package/src/meta.ts +4 -0
- package/src/plugin.ts +9 -0
- package/src/translations.ts +2 -15
- package/src/types/WnfsCapabilities.ts +18 -0
- package/src/types/index.ts +1 -3
- package/dist/lib/browser/FileContainer-TFENLFD5.mjs +0 -72
- package/dist/lib/browser/FileContainer-TFENLFD5.mjs.map +0 -7
- package/dist/lib/browser/blockstore-TUV5YIC5.mjs.map +0 -7
- package/dist/lib/browser/chunk-3I3QQNA3.mjs +0 -84
- package/dist/lib/browser/chunk-3I3QQNA3.mjs.map +0 -7
- package/dist/lib/browser/chunk-A7ETWNGC.mjs.map +0 -7
- package/dist/lib/browser/chunk-CXWUR7YI.mjs +0 -8
- package/dist/lib/browser/chunk-CXWUR7YI.mjs.map +0 -7
- package/dist/lib/browser/chunk-HXGK35WW.mjs +0 -65
- package/dist/lib/browser/chunk-HXGK35WW.mjs.map +0 -7
- package/dist/lib/browser/chunk-QE7ZSOYG.mjs +0 -69
- package/dist/lib/browser/chunk-QE7ZSOYG.mjs.map +0 -7
- package/dist/lib/browser/create-WQ6CUUDN.mjs +0 -24
- package/dist/lib/browser/create-WQ6CUUDN.mjs.map +0 -7
- package/dist/lib/browser/create-file-NGKX6CFL.mjs +0 -49
- package/dist/lib/browser/create-file-NGKX6CFL.mjs.map +0 -7
- package/dist/lib/browser/file-uploader-THECEDM2.mjs +0 -37
- package/dist/lib/browser/file-uploader-THECEDM2.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -124
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/markdown-IEDBXCLX.mjs +0 -201
- package/dist/lib/browser/markdown-IEDBXCLX.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/operation-handler-YAYDKG7R.mjs +0 -16
- package/dist/lib/browser/operation-handler-YAYDKG7R.mjs.map +0 -7
- package/dist/lib/browser/operations/index.mjs +0 -12
- package/dist/lib/browser/react-surface-UGRUPU56.mjs +0 -60
- package/dist/lib/browser/react-surface-UGRUPU56.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -11
- package/dist/lib/browser/upload-42ZL2AE4.mjs +0 -42
- package/dist/lib/browser/upload-42ZL2AE4.mjs.map +0 -7
- package/dist/types/src/capabilities/blockstore/blockstore.d.ts.map +0 -1
- package/dist/types/src/capabilities/blockstore/index.d.ts +0 -3
- package/dist/types/src/capabilities/blockstore/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/file-uploader/file-uploader.d.ts +0 -6
- package/dist/types/src/capabilities/file-uploader/file-uploader.d.ts.map +0 -1
- package/dist/types/src/capabilities/file-uploader/index.d.ts +0 -3
- package/dist/types/src/capabilities/file-uploader/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/markdown/index.d.ts +0 -3
- package/dist/types/src/capabilities/markdown/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/markdown/markdown.d.ts +0 -5
- package/dist/types/src/capabilities/markdown/markdown.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-handler/index.d.ts +0 -4
- package/dist/types/src/capabilities/operation-handler/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-handler/operation-handler.d.ts +0 -6
- package/dist/types/src/capabilities/operation-handler/operation-handler.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +0 -5
- package/dist/types/src/capabilities/react-surface/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 -16
- 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 -3
- 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/blockstore/index.ts +0 -7
- package/src/capabilities/file-uploader/file-uploader.ts +0 -34
- package/src/capabilities/file-uploader/index.ts +0 -7
- package/src/capabilities/markdown/index.ts +0 -7
- package/src/capabilities/markdown/markdown.ts +0 -33
- package/src/capabilities/operation-handler/index.ts +0 -11
- package/src/capabilities/operation-handler/operation-handler.ts +0 -16
- package/src/capabilities/react-surface/index.ts +0 -7
- package/src/capabilities/react-surface/react-surface.tsx +0 -51
- 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 -56
- package/src/containers/FileContainer/index.ts +0 -7
- package/src/containers/index.ts +0 -7
- package/src/extensions/image.stories.tsx +0 -104
- package/src/extensions/image.tsx +0 -203
- package/src/extensions/index.ts +0 -5
- package/src/operations/create-file.ts +0 -32
- package/src/operations/create.ts +0 -23
- package/src/operations/definitions.ts +0 -40
- package/src/operations/index.ts +0 -13
- package/src/operations/upload.ts +0 -30
- 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/operations/index.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
- /package/dist/lib/{browser/types → neutral/helpers}/index.mjs.map +0 -0
- /package/dist/types/src/capabilities/{blockstore/blockstore.d.ts → blockstore.d.ts} +0 -0
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import * as Effect from 'effect/Effect';
|
|
6
|
-
import type * as Schema from 'effect/Schema';
|
|
7
|
-
import React, { useCallback } from 'react';
|
|
8
|
-
|
|
9
|
-
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
10
|
-
import { Surface } from '@dxos/app-framework/ui';
|
|
11
|
-
import { Obj } from '@dxos/echo';
|
|
12
|
-
import { findAnnotation } from '@dxos/effect';
|
|
13
|
-
import { type FormFieldComponentProps } from '@dxos/react-ui-form';
|
|
14
|
-
|
|
15
|
-
import { FileInput } from '../../components';
|
|
16
|
-
import { FileContainer } from '../../containers';
|
|
17
|
-
import { meta } from '../../meta';
|
|
18
|
-
import { WnfsAction, WnfsFile } from '../../types';
|
|
19
|
-
|
|
20
|
-
export default Capability.makeModule(() =>
|
|
21
|
-
Effect.succeed(
|
|
22
|
-
Capability.contributes(Capabilities.ReactSurface, [
|
|
23
|
-
Surface.create({
|
|
24
|
-
id: `${meta.id}.article`,
|
|
25
|
-
role: ['article', 'section', 'slide'],
|
|
26
|
-
filter: (data): data is { subject: WnfsFile.File } => Obj.instanceOf(WnfsFile.File, data.subject),
|
|
27
|
-
component: ({ data, role }) => <FileContainer role={role} subject={data.subject} />,
|
|
28
|
-
}),
|
|
29
|
-
Surface.create({
|
|
30
|
-
id: `${meta.id}.create-form`,
|
|
31
|
-
role: 'form-input',
|
|
32
|
-
filter: (data): data is { prop: string; schema: Schema.Schema.Any } => {
|
|
33
|
-
const annotation = findAnnotation<boolean>(
|
|
34
|
-
(data.schema as Schema.Schema.All).ast,
|
|
35
|
-
WnfsAction.UploadAnnotationId,
|
|
36
|
-
);
|
|
37
|
-
return !!annotation;
|
|
38
|
-
},
|
|
39
|
-
component: ({ data: { schema }, ...props }) => {
|
|
40
|
-
const inputProps = props as unknown as FormFieldComponentProps;
|
|
41
|
-
const handleChange = useCallback(
|
|
42
|
-
(file: File) => inputProps.onValueChange?.(inputProps.type, file),
|
|
43
|
-
[inputProps.type, inputProps.onValueChange],
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
return <FileInput schema={schema} onChange={handleChange} />;
|
|
47
|
-
},
|
|
48
|
-
}),
|
|
49
|
-
]),
|
|
50
|
-
),
|
|
51
|
-
);
|
|
@@ -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/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-surface',
|
|
40
|
-
isDragReject && 'border-rose-fill',
|
|
41
|
-
)}
|
|
42
|
-
>
|
|
43
|
-
<input {...getInputProps()} />
|
|
44
|
-
{acceptedFiles[0] ? <p>{acceptedFiles[0].name}</p> : <p>{t('file input placeholder')}</p>}
|
|
45
|
-
</div>
|
|
46
|
-
);
|
|
47
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { ForwardedRef } from 'react';
|
|
6
|
-
|
|
7
|
-
import { composable, composableProps } from '@dxos/ui-theme';
|
|
8
|
-
|
|
9
|
-
export type FilePreviewProps = { type: string; url: string };
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* File/content preview iframe.
|
|
13
|
-
*/
|
|
14
|
-
export const FilePreview = composable<HTMLElement, FilePreviewProps>(
|
|
15
|
-
({ type, url, children, ...props }, forwardedRef) => {
|
|
16
|
-
if (type.startsWith('image/')) {
|
|
17
|
-
return (
|
|
18
|
-
<img
|
|
19
|
-
{...composableProps(props, { classNames: 'h-full w-full object-contain' })}
|
|
20
|
-
src={url}
|
|
21
|
-
ref={forwardedRef as ForwardedRef<HTMLImageElement>}
|
|
22
|
-
/>
|
|
23
|
-
);
|
|
24
|
-
} else if (type.startsWith('video/')) {
|
|
25
|
-
return (
|
|
26
|
-
<video
|
|
27
|
-
{...composableProps(props, { classNames: 'h-full w-full object-contain' })}
|
|
28
|
-
src={url}
|
|
29
|
-
controls
|
|
30
|
-
ref={forwardedRef as ForwardedRef<HTMLVideoElement>}
|
|
31
|
-
/>
|
|
32
|
-
);
|
|
33
|
-
} else {
|
|
34
|
-
return (
|
|
35
|
-
<iframe
|
|
36
|
-
{...composableProps(props, { classNames: 'h-full w-full overflow-auto' })}
|
|
37
|
-
src={url}
|
|
38
|
-
ref={forwardedRef as ForwardedRef<HTMLIFrameElement>}
|
|
39
|
-
/>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
);
|
package/src/components/index.ts
DELETED
|
@@ -1,56 +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 SurfaceComponentProps } 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 { filePath, getBlobUrl, loadWnfs, wnfsUrl } from '../../helpers';
|
|
15
|
-
import { WnfsCapabilities, type WnfsFile } from '../../types';
|
|
16
|
-
|
|
17
|
-
export type FileContainerProps = SurfaceComponentProps<WnfsFile.File>;
|
|
18
|
-
|
|
19
|
-
export const FileContainer = ({ role, subject: file }: FileContainerProps) => {
|
|
20
|
-
const blockstore = useCapability(WnfsCapabilities.Blockstore);
|
|
21
|
-
const instances = useCapability(WnfsCapabilities.Instances);
|
|
22
|
-
const [blobUrl, setBlobUrl] = useState<string>();
|
|
23
|
-
|
|
24
|
-
useAsyncEffect(async () => {
|
|
25
|
-
const space = getSpace(file);
|
|
26
|
-
invariant(space);
|
|
27
|
-
const { directory, forest } = await loadWnfs({
|
|
28
|
-
blockstore,
|
|
29
|
-
instances,
|
|
30
|
-
space,
|
|
31
|
-
});
|
|
32
|
-
const path = filePath(file.cid.toString(), space);
|
|
33
|
-
const url = await getBlobUrl({
|
|
34
|
-
wnfsUrl: wnfsUrl(path),
|
|
35
|
-
blockstore,
|
|
36
|
-
directory,
|
|
37
|
-
forest,
|
|
38
|
-
type: file.type,
|
|
39
|
-
});
|
|
40
|
-
setBlobUrl(url);
|
|
41
|
-
}, [file]);
|
|
42
|
-
|
|
43
|
-
if (!blobUrl) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<Panel.Root role={role} className='dx-document'>
|
|
49
|
-
<Panel.Content asChild>
|
|
50
|
-
<FilePreview type={file.type} url={blobUrl} />
|
|
51
|
-
</Panel.Content>
|
|
52
|
-
</Panel.Root>
|
|
53
|
-
);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export default FileContainer;
|
package/src/containers/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 { useTextEditor } from '@dxos/react-ui-editor';
|
|
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
|
-
|
|
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/extensions/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/ui-editor';
|
|
15
|
-
import { defaultTx } from '@dxos/ui-theme';
|
|
16
|
-
import { type MaybePromise } from '@dxos/util';
|
|
17
|
-
|
|
18
|
-
import { getBlobUrl, getPathFromUrl, loadWnfs } from '../helpers';
|
|
19
|
-
import { type WnfsCapabilities } from '../types';
|
|
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) {
|
|
169
|
-
return this._wnfsUrl === other._wnfsUrl;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
override toDOM(view: EditorView) {
|
|
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
|
@@ -1,32 +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';
|
|
11
|
-
|
|
12
|
-
import { upload } from '../helpers';
|
|
13
|
-
import { WnfsCapabilities, WnfsFile } from '../types';
|
|
14
|
-
|
|
15
|
-
import { CreateFile } from './definitions';
|
|
16
|
-
|
|
17
|
-
const handler: Operation.WithHandler<typeof CreateFile> = CreateFile.pipe(
|
|
18
|
-
Operation.withHandler(
|
|
19
|
-
Effect.fnUntraced(function* ({ file, db }) {
|
|
20
|
-
const client = yield* Capability.get(ClientCapabilities.Client);
|
|
21
|
-
const blockstore = yield* Capability.get(WnfsCapabilities.Blockstore);
|
|
22
|
-
const space = client.spaces.get(db.spaceId);
|
|
23
|
-
invariant(space, 'Space not found');
|
|
24
|
-
const info = yield* Effect.promise(() => upload({ file, blockstore, space }));
|
|
25
|
-
return {
|
|
26
|
-
object: WnfsFile.make({ name: info.name, type: info.type, cid: info.cid }),
|
|
27
|
-
};
|
|
28
|
-
}),
|
|
29
|
-
),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
export default handler;
|
package/src/operations/create.ts
DELETED
|
@@ -1,23 +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
|
-
|
|
11
|
-
import { Create } from './definitions';
|
|
12
|
-
|
|
13
|
-
const handler: Operation.WithHandler<typeof Create> = Create.pipe(
|
|
14
|
-
Operation.withHandler(
|
|
15
|
-
Effect.fnUntraced(function* ({ name, type, cid }) {
|
|
16
|
-
return {
|
|
17
|
-
object: WnfsFile.make({ name, type, cid }),
|
|
18
|
-
};
|
|
19
|
-
}),
|
|
20
|
-
),
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
export default handler;
|
|
@@ -1,40 +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
|
-
import { WnfsFile, WnfsAction } from '../types';
|
|
14
|
-
|
|
15
|
-
const WNFS_OPERATION = `${meta.id}.operation`;
|
|
16
|
-
|
|
17
|
-
export const Create = Operation.make({
|
|
18
|
-
meta: { key: `${WNFS_OPERATION}.create`, name: 'Create WNFS File' },
|
|
19
|
-
services: [Capability.Service],
|
|
20
|
-
input: FileInfoSchema.pick('name', 'type', 'cid').pipe(Schema.required),
|
|
21
|
-
output: Schema.Struct({
|
|
22
|
-
object: WnfsFile.File,
|
|
23
|
-
}),
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
export const Upload = Operation.make({
|
|
27
|
-
meta: { key: `${WNFS_OPERATION}.upload`, name: 'Upload File' },
|
|
28
|
-
services: [Capability.Service],
|
|
29
|
-
input: Schema.extend(WnfsAction.UploadFileSchema, Schema.Struct({ db: Database.Database })),
|
|
30
|
-
output: Schema.required(FileInfoSchema),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
export const CreateFile = Operation.make({
|
|
34
|
-
meta: { key: `${WNFS_OPERATION}.create-file`, name: 'Create File' },
|
|
35
|
-
services: [Capability.Service],
|
|
36
|
-
input: Schema.extend(WnfsAction.UploadFileSchema, Schema.Struct({ db: Database.Database })),
|
|
37
|
-
output: Schema.Struct({
|
|
38
|
-
object: WnfsFile.File,
|
|
39
|
-
}),
|
|
40
|
-
});
|
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
|
-
);
|