@dxos/plugin-file 0.9.1-staging.ee54ba693a → 0.11.0
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/PLUGIN.mdl +86 -77
- package/dist/lib/FilePlugin.mjs +37 -0
- package/dist/lib/FilePlugin.mjs.map +1 -0
- package/dist/lib/FilePlugin.node.mjs +12 -0
- package/dist/lib/FilePlugin.node.mjs.map +1 -0
- package/dist/lib/capabilities/node.mjs +8 -0
- package/dist/lib/capabilities/node.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +15 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-FileArticle.mjs +62 -0
- package/dist/lib/chunk-FileArticle.mjs.map +1 -0
- package/dist/lib/chunk-create-object.mjs +28 -0
- package/dist/lib/chunk-create-object.mjs.map +1 -0
- package/dist/lib/chunk-create.mjs +78 -0
- package/dist/lib/chunk-create.mjs.map +1 -0
- package/dist/lib/chunk-edge-backend.mjs +18 -0
- package/dist/lib/chunk-edge-backend.mjs.map +1 -0
- package/dist/lib/chunk-file-uploader.mjs +37 -0
- package/dist/lib/chunk-file-uploader.mjs.map +1 -0
- package/dist/lib/chunk-inline-backend.mjs +19 -0
- package/dist/lib/chunk-inline-backend.mjs.map +1 -0
- package/dist/lib/chunk-markdown-extension.mjs +182 -0
- package/dist/lib/chunk-markdown-extension.mjs.map +1 -0
- package/dist/lib/chunk-meta.mjs +44 -0
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +11 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +55 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-read.mjs +32 -0
- package/dist/lib/chunk-read.mjs.map +1 -0
- package/dist/lib/chunk-settings.mjs +23 -0
- package/dist/lib/chunk-settings.mjs.map +1 -0
- package/dist/lib/chunk-skill-definition.mjs +10 -0
- package/dist/lib/chunk-skill-definition.mjs.map +1 -0
- package/dist/lib/chunk-skills.mjs +28 -0
- package/dist/lib/chunk-skills.mjs.map +1 -0
- package/dist/lib/chunk-types.mjs +98 -0
- package/dist/lib/chunk-types.mjs.map +1 -0
- package/dist/lib/components.mjs +57 -0
- package/dist/lib/components.mjs.map +1 -0
- package/dist/lib/containers.mjs +56 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/operations.mjs +7 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/skills.mjs +2 -0
- package/dist/lib/translations.mjs +30 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/types/src/FilePlugin.d.ts.map +1 -1
- package/dist/types/src/FilePlugin.node.d.ts.map +1 -1
- package/dist/types/src/capabilities/FileUploadField.d.ts +12 -0
- package/dist/types/src/capabilities/FileUploadField.d.ts.map +1 -0
- package/dist/types/src/capabilities/create-object.d.ts +1 -1
- package/dist/types/src/capabilities/edge-backend.d.ts +5 -0
- package/dist/types/src/capabilities/edge-backend.d.ts.map +1 -0
- package/dist/types/src/capabilities/file-uploader.d.ts +1 -1
- package/dist/types/src/capabilities/file-uploader.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +1 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/inline-backend.d.ts +2 -2
- package/dist/types/src/capabilities/inline-backend.d.ts.map +1 -1
- package/dist/types/src/capabilities/markdown-extension.d.ts +1 -1
- package/dist/types/src/capabilities/node.d.ts +5 -0
- package/dist/types/src/capabilities/node.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +1 -1
- package/dist/types/src/components/FileInput/FileInput.d.ts.map +1 -1
- package/dist/types/src/components/FilePreview/FilePreview.d.ts +1 -1
- package/dist/types/src/components/FilePreview/FilePreview.d.ts.map +1 -1
- package/dist/types/src/containers/FileArticle/FileArticle.d.ts +4 -1
- package/dist/types/src/containers/FileArticle/FileArticle.d.ts.map +1 -1
- package/dist/types/src/containers/FileSettings/FileSettings.d.ts +5 -3
- package/dist/types/src/containers/FileSettings/FileSettings.d.ts.map +1 -1
- package/dist/types/src/extensions/image.d.ts +0 -2
- package/dist/types/src/extensions/image.d.ts.map +1 -1
- package/dist/types/src/operations/create.d.ts +14 -5
- package/dist/types/src/operations/create.d.ts.map +1 -1
- package/dist/types/src/operations/read.d.ts.map +1 -1
- package/dist/types/src/types/FileCapabilities.d.ts +10 -28
- package/dist/types/src/types/FileCapabilities.d.ts.map +1 -1
- package/dist/types/src/types/Settings.d.ts +1 -1
- package/dist/types/src/types/Settings.d.ts.map +1 -1
- package/dist/types/src/types/limits.d.ts +0 -1
- package/dist/types/src/types/limits.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dx.config.ts +10 -6
- package/package.json +45 -41
- package/src/FilePlugin.node.ts +2 -1
- package/src/FilePlugin.tsx +8 -1
- package/src/capabilities/FileUploadField.tsx +37 -0
- package/src/capabilities/edge-backend.ts +27 -0
- package/src/capabilities/file-uploader.ts +6 -4
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/inline-backend.ts +4 -21
- package/src/capabilities/markdown-extension.ts +3 -8
- package/src/capabilities/node.ts +16 -0
- package/src/capabilities/react-surface.ts +46 -0
- package/src/components/FileInput/FileInput.tsx +3 -2
- package/src/containers/FileArticle/FileArticle.tsx +49 -42
- package/src/containers/FileSettings/FileSettings.tsx +19 -14
- package/src/extensions/image.tsx +49 -28
- package/src/operations/create.test.ts +122 -32
- package/src/operations/create.ts +44 -24
- package/src/operations/read.ts +24 -6
- package/src/types/FileCapabilities.ts +10 -31
- package/src/types/Settings.ts +1 -1
- package/src/types/limits.ts +0 -2
- package/dist/lib/neutral/FileArticle-YU6NN3KN.mjs +0 -80
- package/dist/lib/neutral/FileArticle-YU6NN3KN.mjs.map +0 -7
- package/dist/lib/neutral/FilePlugin.mjs +0 -58
- package/dist/lib/neutral/FilePlugin.mjs.map +0 -7
- package/dist/lib/neutral/FilePlugin.node.mjs +0 -23
- package/dist/lib/neutral/FilePlugin.node.mjs.map +0 -7
- package/dist/lib/neutral/capabilities/index.mjs +0 -23
- package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
- package/dist/lib/neutral/chunk-DKGTSUUH.mjs +0 -8
- package/dist/lib/neutral/chunk-DKGTSUUH.mjs.map +0 -7
- package/dist/lib/neutral/chunk-GMYSEHV4.mjs +0 -40
- package/dist/lib/neutral/chunk-GMYSEHV4.mjs.map +0 -7
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-LBZ4MCLI.mjs +0 -45
- package/dist/lib/neutral/chunk-LBZ4MCLI.mjs.map +0 -7
- package/dist/lib/neutral/chunk-YODPX62X.mjs +0 -136
- package/dist/lib/neutral/chunk-YODPX62X.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ZUXUTRAJ.mjs +0 -80
- package/dist/lib/neutral/chunk-ZUXUTRAJ.mjs.map +0 -7
- package/dist/lib/neutral/components/index.mjs +0 -74
- package/dist/lib/neutral/components/index.mjs.map +0 -7
- package/dist/lib/neutral/containers/index.mjs +0 -59
- package/dist/lib/neutral/containers/index.mjs.map +0 -7
- package/dist/lib/neutral/create-IUUG7RI6.mjs +0 -17
- package/dist/lib/neutral/create-IUUG7RI6.mjs.map +0 -7
- package/dist/lib/neutral/create-object-V667G4ZL.mjs +0 -30
- package/dist/lib/neutral/create-object-V667G4ZL.mjs.map +0 -7
- package/dist/lib/neutral/file-uploader-BCJH6TUL.mjs +0 -41
- package/dist/lib/neutral/file-uploader-BCJH6TUL.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -32
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/inline-backend-FGGGDYRB.mjs +0 -37
- package/dist/lib/neutral/inline-backend-FGGGDYRB.mjs.map +0 -7
- package/dist/lib/neutral/markdown-extension-RA5YNMTW.mjs +0 -230
- package/dist/lib/neutral/markdown-extension-RA5YNMTW.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/meta.mjs +0 -8
- package/dist/lib/neutral/meta.mjs.map +0 -7
- package/dist/lib/neutral/operation-handler-CPDANWGH.mjs +0 -13
- package/dist/lib/neutral/operation-handler-CPDANWGH.mjs.map +0 -7
- package/dist/lib/neutral/operations/index.mjs +0 -8
- package/dist/lib/neutral/operations/index.mjs.map +0 -7
- package/dist/lib/neutral/plugin.mjs +0 -16
- package/dist/lib/neutral/plugin.mjs.map +0 -7
- package/dist/lib/neutral/react-surface-5BXKSHD2.mjs +0 -60
- package/dist/lib/neutral/react-surface-5BXKSHD2.mjs.map +0 -7
- package/dist/lib/neutral/read-HG6CSWKR.mjs +0 -28
- package/dist/lib/neutral/read-HG6CSWKR.mjs.map +0 -7
- package/dist/lib/neutral/settings-RR3RESL2.mjs +0 -28
- package/dist/lib/neutral/settings-RR3RESL2.mjs.map +0 -7
- package/dist/lib/neutral/skill-definition-YHBGWBUS.mjs +0 -15
- package/dist/lib/neutral/skill-definition-YHBGWBUS.mjs.map +0 -7
- package/dist/lib/neutral/skills/index.mjs +0 -11
- package/dist/lib/neutral/skills/index.mjs.map +0 -7
- package/dist/lib/neutral/translations.mjs +0 -36
- package/dist/lib/neutral/translations.mjs.map +0 -7
- package/dist/lib/neutral/types/index.mjs +0 -22
- package/dist/lib/neutral/types/index.mjs.map +0 -7
- package/src/capabilities/react-surface.tsx +0 -61
package/dx.config.ts
CHANGED
|
@@ -17,19 +17,23 @@ export default Config2.make({
|
|
|
17
17
|
can be contributed by other plugins.
|
|
18
18
|
|
|
19
19
|
Accepted formats are images (jpg, jpeg, png, gif, webp, svg), videos (mp4, webm, mov), and PDFs,
|
|
20
|
-
up to a 4 MB limit per file for the inline backend
|
|
21
|
-
element for each MIME class and surfaces inside article,
|
|
20
|
+
up to a 4 MB limit per file for the inline backend (other backends scale beyond it). The preview
|
|
21
|
+
surface renders the appropriate HTML element for each MIME class and surfaces inside article,
|
|
22
|
+
section, and slide roles.
|
|
22
23
|
|
|
23
|
-
A
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
A File object's \`data\` field is a reference to a \`Blob\` ECHO object (from \`@dxos/echo\`), which
|
|
25
|
+
abstracts where bytes actually live. \`Blob.url\`/\`Blob.read\` resolve a renderable URL or the raw
|
|
26
|
+
bytes regardless of backend, keeping the File object schema backend-agnostic without a separate
|
|
27
|
+
URL-resolver capability. A companion Markdown extension allows file objects to be embedded directly
|
|
28
|
+
in Markdown documents.
|
|
26
29
|
|
|
27
30
|
Backend selection is user-configurable in the plugin settings panel, which lists all registered
|
|
28
31
|
backends with their descriptions. Selecting a backend persists the choice to the ECHO-backed settings
|
|
29
32
|
atom so the preference is shared across devices.
|
|
30
33
|
`,
|
|
31
|
-
icon: { key: 'ph--file--regular', hue: '
|
|
34
|
+
icon: { key: 'ph--file--regular', hue: 'indigo' },
|
|
32
35
|
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-file',
|
|
33
36
|
spec: 'PLUGIN.mdl',
|
|
37
|
+
tags: ['alpha'],
|
|
34
38
|
},
|
|
35
39
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-file",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Inline file (image / PDF) storage plugin for DXOS",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -16,32 +16,36 @@
|
|
|
16
16
|
"#skills": {
|
|
17
17
|
"source": "./src/skills/index.ts",
|
|
18
18
|
"types": "./dist/types/src/skills/index.d.ts",
|
|
19
|
-
"default": "./dist/lib/
|
|
19
|
+
"default": "./dist/lib/skills.mjs"
|
|
20
20
|
},
|
|
21
21
|
"#capabilities": {
|
|
22
|
-
"source":
|
|
22
|
+
"source": {
|
|
23
|
+
"node": "./src/capabilities/node.ts",
|
|
24
|
+
"default": "./src/capabilities/index.ts"
|
|
25
|
+
},
|
|
23
26
|
"types": "./dist/types/src/capabilities/index.d.ts",
|
|
24
|
-
"
|
|
27
|
+
"node": "./dist/lib/capabilities/node.mjs",
|
|
28
|
+
"default": "./dist/lib/capabilities.mjs"
|
|
25
29
|
},
|
|
26
30
|
"#components": {
|
|
27
31
|
"source": "./src/components/index.ts",
|
|
28
32
|
"types": "./dist/types/src/components/index.d.ts",
|
|
29
|
-
"default": "./dist/lib/
|
|
33
|
+
"default": "./dist/lib/components.mjs"
|
|
30
34
|
},
|
|
31
35
|
"#containers": {
|
|
32
36
|
"source": "./src/containers/index.ts",
|
|
33
37
|
"types": "./dist/types/src/containers/index.d.ts",
|
|
34
|
-
"default": "./dist/lib/
|
|
38
|
+
"default": "./dist/lib/containers.mjs"
|
|
35
39
|
},
|
|
36
40
|
"#meta": {
|
|
37
41
|
"source": "./src/meta.ts",
|
|
38
42
|
"types": "./dist/types/src/meta.d.ts",
|
|
39
|
-
"default": "./dist/lib/
|
|
43
|
+
"default": "./dist/lib/meta.mjs"
|
|
40
44
|
},
|
|
41
45
|
"#operations": {
|
|
42
46
|
"source": "./src/operations/index.ts",
|
|
43
47
|
"types": "./dist/types/src/operations/index.d.ts",
|
|
44
|
-
"default": "./dist/lib/
|
|
48
|
+
"default": "./dist/lib/operations.mjs"
|
|
45
49
|
},
|
|
46
50
|
"#plugin": {
|
|
47
51
|
"source": {
|
|
@@ -49,41 +53,41 @@
|
|
|
49
53
|
"default": "./src/FilePlugin.tsx"
|
|
50
54
|
},
|
|
51
55
|
"types": "./dist/types/src/FilePlugin.d.ts",
|
|
52
|
-
"node": "./dist/lib/
|
|
53
|
-
"default": "./dist/lib/
|
|
56
|
+
"node": "./dist/lib/FilePlugin.node.mjs",
|
|
57
|
+
"default": "./dist/lib/FilePlugin.mjs"
|
|
54
58
|
},
|
|
55
59
|
"#translations": {
|
|
56
60
|
"source": "./src/translations.ts",
|
|
57
61
|
"types": "./dist/types/src/translations.d.ts",
|
|
58
|
-
"default": "./dist/lib/
|
|
62
|
+
"default": "./dist/lib/translations.mjs"
|
|
59
63
|
},
|
|
60
64
|
"#types": {
|
|
61
65
|
"source": "./src/types/index.ts",
|
|
62
66
|
"types": "./dist/types/src/types/index.d.ts",
|
|
63
|
-
"default": "./dist/lib/
|
|
67
|
+
"default": "./dist/lib/types.mjs"
|
|
64
68
|
}
|
|
65
69
|
},
|
|
66
70
|
"exports": {
|
|
67
71
|
".": {
|
|
68
72
|
"source": "./src/index.ts",
|
|
69
73
|
"types": "./dist/types/src/index.d.ts",
|
|
70
|
-
"
|
|
74
|
+
"import": "./dist/lib/index.mjs"
|
|
71
75
|
},
|
|
72
76
|
"./assets/PLUGIN.mdl": "./PLUGIN.mdl",
|
|
73
77
|
"./plugin": {
|
|
74
78
|
"source": "./src/plugin.ts",
|
|
75
79
|
"types": "./dist/types/src/plugin.d.ts",
|
|
76
|
-
"
|
|
80
|
+
"import": "./dist/lib/plugin.mjs"
|
|
77
81
|
},
|
|
78
82
|
"./translations": {
|
|
79
83
|
"source": "./src/translations.ts",
|
|
80
84
|
"types": "./dist/types/src/translations.d.ts",
|
|
81
|
-
"
|
|
85
|
+
"import": "./dist/lib/translations.mjs"
|
|
82
86
|
},
|
|
83
87
|
"./types": {
|
|
84
88
|
"source": "./src/types/index.ts",
|
|
85
89
|
"types": "./dist/types/src/types/index.d.ts",
|
|
86
|
-
"
|
|
90
|
+
"import": "./dist/lib/types.mjs"
|
|
87
91
|
}
|
|
88
92
|
},
|
|
89
93
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -94,44 +98,44 @@
|
|
|
94
98
|
"PLUGIN.mdl"
|
|
95
99
|
],
|
|
96
100
|
"dependencies": {
|
|
97
|
-
"@codemirror/language": "^6.12.
|
|
98
|
-
"@codemirror/state": "^6.
|
|
99
|
-
"@codemirror/view": "^6.43.
|
|
100
|
-
"@effect-atom/atom
|
|
101
|
+
"@codemirror/language": "^6.12.4",
|
|
102
|
+
"@codemirror/state": "^6.7.1",
|
|
103
|
+
"@codemirror/view": "^6.43.6",
|
|
104
|
+
"@effect-atom/atom": "^0.5.3",
|
|
101
105
|
"effect": "3.21.4",
|
|
102
106
|
"react-dropzone": "^14.2.3",
|
|
103
|
-
"@dxos/app-framework": "0.
|
|
104
|
-
"@dxos/app-toolkit": "0.
|
|
105
|
-
"@dxos/
|
|
106
|
-
"@dxos/
|
|
107
|
-
"@dxos/effect": "0.
|
|
108
|
-
"@dxos/
|
|
109
|
-
"@dxos/plugin-client": "0.
|
|
110
|
-
"@dxos/
|
|
111
|
-
"@dxos/plugin-space": "0.
|
|
112
|
-
"@dxos/react-client": "0.
|
|
113
|
-
"@dxos/react-ui": "0.
|
|
114
|
-
"@dxos/
|
|
115
|
-
"@dxos/
|
|
116
|
-
"@dxos/ui-editor": "0.
|
|
117
|
-
"@dxos/util": "0.
|
|
107
|
+
"@dxos/app-framework": "0.11.0",
|
|
108
|
+
"@dxos/app-toolkit": "0.11.0",
|
|
109
|
+
"@dxos/compute": "0.11.0",
|
|
110
|
+
"@dxos/echo": "0.11.0",
|
|
111
|
+
"@dxos/effect": "0.11.0",
|
|
112
|
+
"@dxos/keys": "0.11.0",
|
|
113
|
+
"@dxos/plugin-client": "0.11.0",
|
|
114
|
+
"@dxos/plugin-markdown": "0.11.0",
|
|
115
|
+
"@dxos/plugin-space": "0.11.0",
|
|
116
|
+
"@dxos/react-client": "0.11.0",
|
|
117
|
+
"@dxos/react-ui": "0.11.0",
|
|
118
|
+
"@dxos/react-ui-form": "0.11.0",
|
|
119
|
+
"@dxos/types": "0.11.0",
|
|
120
|
+
"@dxos/ui-editor": "0.11.0",
|
|
121
|
+
"@dxos/util": "0.11.0"
|
|
118
122
|
},
|
|
119
123
|
"devDependencies": {
|
|
120
124
|
"@types/react": "~19.2.17",
|
|
121
125
|
"@types/react-dom": "~19.2.3",
|
|
122
126
|
"react": "~19.2.7",
|
|
123
127
|
"react-dom": "~19.2.7",
|
|
124
|
-
"vite": "^8.
|
|
125
|
-
"@dxos/plugin-testing": "0.
|
|
126
|
-
"@dxos/
|
|
127
|
-
"@dxos/
|
|
128
|
+
"vite": "^8.1.4",
|
|
129
|
+
"@dxos/plugin-testing": "0.11.0",
|
|
130
|
+
"@dxos/storybook-utils": "0.11.0",
|
|
131
|
+
"@dxos/ui-theme": "0.11.0"
|
|
128
132
|
},
|
|
129
133
|
"peerDependencies": {
|
|
130
134
|
"effect": "3.21.4",
|
|
131
135
|
"react": "~19.2.7",
|
|
132
136
|
"react-dom": "~19.2.7",
|
|
133
|
-
"@dxos/react-ui": "0.
|
|
134
|
-
"@dxos/ui-theme": "0.
|
|
137
|
+
"@dxos/react-ui": "^0.11.0",
|
|
138
|
+
"@dxos/ui-theme": "^0.11.0"
|
|
135
139
|
},
|
|
136
140
|
"publishConfig": {
|
|
137
141
|
"access": "public"
|
package/src/FilePlugin.node.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { Plugin } from '@dxos/app-framework';
|
|
6
6
|
import { AppPlugin } from '@dxos/app-toolkit';
|
|
7
|
+
import { Blob } from '@dxos/echo';
|
|
7
8
|
|
|
8
9
|
import { CreateObject, OperationHandler } from '#capabilities';
|
|
9
10
|
import { meta } from '#meta';
|
|
@@ -12,7 +13,7 @@ import { File } from '#types';
|
|
|
12
13
|
export const FilePlugin = Plugin.define(meta).pipe(
|
|
13
14
|
AppPlugin.addCreateObjectModule({ activate: CreateObject }),
|
|
14
15
|
AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),
|
|
15
|
-
AppPlugin.addSchemaModule({ schema: [File.File] }),
|
|
16
|
+
AppPlugin.addSchemaModule({ schema: [File.File, Blob.Blob] }),
|
|
16
17
|
Plugin.make,
|
|
17
18
|
);
|
|
18
19
|
|
package/src/FilePlugin.tsx
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import { Plugin } from '@dxos/app-framework';
|
|
6
6
|
import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
|
|
7
|
+
import { Blob } from '@dxos/echo';
|
|
7
8
|
import { ClientEvents } from '@dxos/plugin-client';
|
|
8
9
|
import { MarkdownEvents } from '@dxos/plugin-markdown';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
CreateObject,
|
|
13
|
+
EdgeBackend,
|
|
12
14
|
FileUploader,
|
|
13
15
|
InlineBackend,
|
|
14
16
|
Markdown,
|
|
@@ -28,7 +30,7 @@ export const FilePlugin = Plugin.define(meta).pipe(
|
|
|
28
30
|
AppPlugin.addSkillDefinitionModule({ activate: SkillDefinition }),
|
|
29
31
|
AppPlugin.addCreateObjectModule({ activate: CreateObject }),
|
|
30
32
|
AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),
|
|
31
|
-
AppPlugin.addSchemaModule({ schema: [File.File] }),
|
|
33
|
+
AppPlugin.addSchemaModule({ schema: [File.File, Blob.Blob] }),
|
|
32
34
|
AppPlugin.addSettingsModule({ activate: Settings }),
|
|
33
35
|
AppPlugin.addSurfaceModule({ activate: ReactSurface }),
|
|
34
36
|
AppPlugin.addTranslationsModule({ translations }),
|
|
@@ -42,6 +44,11 @@ export const FilePlugin = Plugin.define(meta).pipe(
|
|
|
42
44
|
activatesOn: ClientEvents.ClientReady,
|
|
43
45
|
activate: FileUploader,
|
|
44
46
|
}),
|
|
47
|
+
Plugin.addModule({
|
|
48
|
+
id: 'edge-backend',
|
|
49
|
+
activatesOn: ClientEvents.ClientReady,
|
|
50
|
+
activate: EdgeBackend,
|
|
51
|
+
}),
|
|
45
52
|
Plugin.addModule({
|
|
46
53
|
id: 'markdown',
|
|
47
54
|
activatesOn: MarkdownEvents.SetupExtensions,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Surface } from '@dxos/app-framework/ui';
|
|
8
|
+
import { type AppSurface } from '@dxos/app-toolkit/ui';
|
|
9
|
+
import { type FormFieldRendererProps } from '@dxos/react-ui-form';
|
|
10
|
+
|
|
11
|
+
import { FileInput } from '#components';
|
|
12
|
+
|
|
13
|
+
/** The form renderer's own props ride alongside `data` on the surface envelope. */
|
|
14
|
+
export type FileUploadFieldProps = Surface.ComponentProps<AppSurface.FormInputData> &
|
|
15
|
+
Pick<FormFieldRendererProps<File>, 'onValueChange'>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Form field that uploads a file for an upload-annotated property. It consumes the whole surface
|
|
19
|
+
* envelope, so it is registered without a `props` mapper.
|
|
20
|
+
*/
|
|
21
|
+
export const FileUploadField = ({ data, onValueChange }: FileUploadFieldProps) => {
|
|
22
|
+
const ast = data.fieldPropertyAst;
|
|
23
|
+
const handleChange = useCallback(
|
|
24
|
+
(file: File) => {
|
|
25
|
+
if (ast) {
|
|
26
|
+
onValueChange?.(ast, file);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
[ast, onValueChange],
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (!ast) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return <FileInput schema={data.schema} onChange={handleChange} />;
|
|
37
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
+
import { Blob } from '@dxos/echo';
|
|
9
|
+
import { ClientCapabilities } from '@dxos/plugin-client';
|
|
10
|
+
|
|
11
|
+
import { FileCapabilities } from '#types';
|
|
12
|
+
|
|
13
|
+
export default Capability.makeModule(
|
|
14
|
+
Effect.fnUntraced(function* () {
|
|
15
|
+
const client = yield* Capability.get(ClientCapabilities.Client);
|
|
16
|
+
const edgeUrl = client.config.values.runtime?.services?.edge?.url;
|
|
17
|
+
if (!edgeUrl) {
|
|
18
|
+
return Capability.contributes(Capabilities.Null, null);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return Capability.contributes(FileCapabilities.Backend, {
|
|
22
|
+
name: 'Edge',
|
|
23
|
+
description: 'Store files on the DXOS edge network. Scales beyond the inline size cap.',
|
|
24
|
+
storage: Blob.Storage.edge,
|
|
25
|
+
});
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Option from 'effect/Option';
|
|
6
7
|
|
|
7
8
|
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
9
|
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
10
|
+
import { Blob, Database } from '@dxos/echo';
|
|
9
11
|
import { EffectEx } from '@dxos/effect';
|
|
10
12
|
import { SpaceOperation } from '@dxos/plugin-space';
|
|
11
13
|
|
|
@@ -20,12 +22,12 @@ export default Capability.makeModule(
|
|
|
20
22
|
const program = Effect.gen(function* () {
|
|
21
23
|
const { object } = yield* invoke(FileOperation.Create, { db, file });
|
|
22
24
|
yield* invoke(SpaceOperation.AddObject, { target: db, object });
|
|
23
|
-
const
|
|
25
|
+
const blob = yield* Database.load(object.data);
|
|
26
|
+
const urlOption = yield* Blob.url(blob).pipe(Effect.provide(Database.layer(db)));
|
|
24
27
|
return {
|
|
25
28
|
name: object.name ?? file.name,
|
|
26
|
-
type:
|
|
27
|
-
...(
|
|
28
|
-
...(external?.cid ? { cid: external.cid } : {}),
|
|
29
|
+
type: blob.type ?? 'application/octet-stream',
|
|
30
|
+
...(Option.isSome(urlOption) ? { url: urlOption.value } : {}),
|
|
29
31
|
};
|
|
30
32
|
});
|
|
31
33
|
|
|
@@ -7,6 +7,7 @@ import { OperationHandlerSet } from '@dxos/compute';
|
|
|
7
7
|
|
|
8
8
|
export const SkillDefinition = Capability.lazy('SkillDefinition', () => import('./skill-definition'));
|
|
9
9
|
export const CreateObject = Capability.lazy('CreateObject', () => import('./create-object'));
|
|
10
|
+
export const EdgeBackend = Capability.lazy('EdgeBackend', () => import('./edge-backend'));
|
|
10
11
|
export const FileUploader = Capability.lazy('FileUploader', () => import('./file-uploader'));
|
|
11
12
|
export const InlineBackend = Capability.lazy('InlineBackend', () => import('./inline-backend'));
|
|
12
13
|
export const Markdown = Capability.lazy('MarkdownExtension', () => import('./markdown-extension'));
|
|
@@ -5,35 +5,18 @@
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
|
|
7
7
|
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { Blob } from '@dxos/echo';
|
|
8
9
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
import { FileTooLargeError, UnsupportedFileTypeError } from '../operations/create';
|
|
10
|
+
import { FileCapabilities } from '#types';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
* Inline backend: file bytes are stored on the ECHO object itself.
|
|
13
|
+
* Inline backend descriptor: file bytes are stored on the ECHO object itself.
|
|
15
14
|
* Exported standalone for direct testing.
|
|
16
15
|
*/
|
|
17
16
|
export const inlineBackend: FileCapabilities.Backend = {
|
|
18
|
-
id: Settings.DEFAULT_BACKEND_ID,
|
|
19
17
|
name: 'Inline (ECHO)',
|
|
20
18
|
description: 'Store the file bytes directly inside the ECHO document. Capped at 4MB; images, videos, and PDFs only.',
|
|
21
|
-
|
|
22
|
-
if (!isAcceptedMimeType(file.type)) {
|
|
23
|
-
throw new UnsupportedFileTypeError(file.type);
|
|
24
|
-
}
|
|
25
|
-
// Check size before reading into memory to avoid allocating a large buffer.
|
|
26
|
-
if (file.size > MAX_FILE_SIZE) {
|
|
27
|
-
throw new FileTooLargeError(file.size);
|
|
28
|
-
}
|
|
29
|
-
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
30
|
-
return {
|
|
31
|
-
name: file.name,
|
|
32
|
-
type: file.type,
|
|
33
|
-
size: bytes.byteLength,
|
|
34
|
-
data: File.inlineData(bytes),
|
|
35
|
-
};
|
|
36
|
-
},
|
|
19
|
+
storage: Blob.Storage.inline,
|
|
37
20
|
};
|
|
38
21
|
|
|
39
22
|
export default Capability.makeModule(() =>
|
|
@@ -8,14 +8,10 @@ import { Capability } from '@dxos/app-framework';
|
|
|
8
8
|
import { MarkdownCapabilities, type MarkdownExtensionProvider } from '@dxos/plugin-markdown/types';
|
|
9
9
|
import { getSpace } from '@dxos/react-client/echo';
|
|
10
10
|
|
|
11
|
-
import { FileCapabilities } from '#types';
|
|
12
|
-
|
|
13
11
|
import { image } from '../extensions';
|
|
14
12
|
|
|
15
|
-
export default Capability.makeModule(
|
|
16
|
-
Effect.
|
|
17
|
-
const capabilities = yield* Capability.Service;
|
|
18
|
-
|
|
13
|
+
export default Capability.makeModule(() =>
|
|
14
|
+
Effect.sync(() => {
|
|
19
15
|
const provider: MarkdownExtensionProvider = ({ document, viewMode }) => {
|
|
20
16
|
if (viewMode === 'source') {
|
|
21
17
|
return undefined;
|
|
@@ -26,8 +22,7 @@ export default Capability.makeModule(
|
|
|
26
22
|
if (!space) {
|
|
27
23
|
return undefined;
|
|
28
24
|
}
|
|
29
|
-
|
|
30
|
-
return [image({ space, resolvers })];
|
|
25
|
+
return [image({ space })];
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
return undefined;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capability } from '@dxos/app-framework';
|
|
6
|
+
import { OperationHandlerSet } from '@dxos/compute';
|
|
7
|
+
|
|
8
|
+
// The capabilities `FilePlugin.node` activates, and only those. `Capability.lazy` defers the
|
|
9
|
+
// import at runtime but a bundler still walks it, so listing the React surfaces here would pull
|
|
10
|
+
// the plugin's components into every node and bun build.
|
|
11
|
+
|
|
12
|
+
export const CreateObject = Capability.lazy('CreateObject', () => import('./create-object'));
|
|
13
|
+
export const OperationHandler = Capability.lazy<OperationHandlerSet.OperationHandlerSet>(
|
|
14
|
+
'OperationHandler',
|
|
15
|
+
() => import('./operation-handler'),
|
|
16
|
+
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
+
import { Surface } from '@dxos/app-framework/ui';
|
|
9
|
+
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
10
|
+
import { SchemaEx } from '@dxos/effect';
|
|
11
|
+
|
|
12
|
+
import { FileArticle, FileSettings } from '#containers';
|
|
13
|
+
import { meta } from '#meta';
|
|
14
|
+
import { File, FileAction } from '#types';
|
|
15
|
+
|
|
16
|
+
import { FileUploadField } from './FileUploadField';
|
|
17
|
+
|
|
18
|
+
export default Capability.makeModule(() =>
|
|
19
|
+
Effect.succeed(
|
|
20
|
+
Capability.contributes(Capabilities.ReactSurface, [
|
|
21
|
+
Surface.create({
|
|
22
|
+
id: 'article',
|
|
23
|
+
filter: AppSurface.oneOf(
|
|
24
|
+
AppSurface.object(AppSurface.Article, File.File),
|
|
25
|
+
AppSurface.object(AppSurface.Section, File.File),
|
|
26
|
+
AppSurface.object(AppSurface.Slide, File.File),
|
|
27
|
+
),
|
|
28
|
+
component: FileArticle,
|
|
29
|
+
props: ({ role, data: { subject } }) => ({ role, subject }),
|
|
30
|
+
}),
|
|
31
|
+
Surface.create({
|
|
32
|
+
id: 'createForm',
|
|
33
|
+
filter: AppSurface.formInputBySchema(
|
|
34
|
+
(ast) => !!SchemaEx.findAnnotation<Record<string, string[]>>(ast, FileAction.UploadAnnotationId),
|
|
35
|
+
),
|
|
36
|
+
component: FileUploadField,
|
|
37
|
+
}),
|
|
38
|
+
Surface.create({
|
|
39
|
+
id: 'pluginSettings',
|
|
40
|
+
filter: AppSurface.settings(AppSurface.Article, meta.profile.key),
|
|
41
|
+
component: FileSettings,
|
|
42
|
+
props: ({ data: { subject } }) => ({ subject }),
|
|
43
|
+
}),
|
|
44
|
+
]),
|
|
45
|
+
),
|
|
46
|
+
);
|
|
@@ -11,7 +11,7 @@ import { useTranslation } from '@dxos/react-ui';
|
|
|
11
11
|
import { mx } from '@dxos/ui-theme';
|
|
12
12
|
|
|
13
13
|
import { meta } from '#meta';
|
|
14
|
-
import { FileAction
|
|
14
|
+
import { FileAction } from '#types';
|
|
15
15
|
|
|
16
16
|
export type FileInputProps = {
|
|
17
17
|
schema: Schema.Schema.AnyNoContext;
|
|
@@ -24,10 +24,11 @@ export const FileInput = ({ schema, onChange }: FileInputProps) => {
|
|
|
24
24
|
|
|
25
25
|
const onDropAccepted = useCallback((files: File[]) => onChange?.(files[0]), [onChange]);
|
|
26
26
|
|
|
27
|
+
// No client-side size cap here — the active backend determines the limit (inline is capped,
|
|
28
|
+
// edge/wnfs are not), and `op:Create` enforces it against the actual selected backend.
|
|
27
29
|
const { acceptedFiles, getRootProps, getInputProps, isFocused, isDragAccept, isDragReject } = useDropzone({
|
|
28
30
|
multiple: false,
|
|
29
31
|
accept,
|
|
30
|
-
maxSize: MAX_FILE_SIZE,
|
|
31
32
|
onDropAccepted,
|
|
32
33
|
});
|
|
33
34
|
|
|
@@ -2,63 +2,64 @@
|
|
|
2
2
|
// Copyright 2026 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Option from 'effect/Option';
|
|
5
7
|
import React, { useEffect, useState } from 'react';
|
|
6
8
|
|
|
7
|
-
import { useCapabilities } from '@dxos/app-framework/ui';
|
|
8
9
|
import { type AppSurface } from '@dxos/app-toolkit/ui';
|
|
9
|
-
import {
|
|
10
|
+
import { Blob, Database, Obj } from '@dxos/echo';
|
|
11
|
+
import { EffectEx } from '@dxos/effect';
|
|
10
12
|
import { Panel } from '@dxos/react-ui';
|
|
11
13
|
|
|
12
14
|
import { FilePreview } from '#components';
|
|
13
|
-
import { File
|
|
15
|
+
import { File } from '#types';
|
|
14
16
|
|
|
15
17
|
export type FileArticleProps = AppSurface.ObjectArticleProps<File.File>;
|
|
16
18
|
|
|
17
19
|
export const FileArticle = ({ role, subject: file }: FileArticleProps) => {
|
|
18
|
-
const
|
|
19
|
-
const [renderUrl, setRenderUrl] = useState<string | undefined>(undefined);
|
|
20
|
+
const [rendered, setRendered] = useState<{ url: string; type: string } | undefined>(undefined);
|
|
20
21
|
|
|
21
22
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return
|
|
23
|
+
setRendered(undefined);
|
|
24
|
+
|
|
25
|
+
const db = Obj.getDatabase(file);
|
|
26
|
+
if (!db) {
|
|
27
|
+
return;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
// External URL: pass through `http(s):`/`data:`/`blob:` directly; otherwise dispatch to a resolver.
|
|
30
30
|
let cancelled = false;
|
|
31
31
|
let createdBlobUrl: string | undefined;
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
|
|
33
|
+
const program = Effect.gen(function* () {
|
|
34
|
+
const blob = yield* Database.load(file.data);
|
|
35
|
+
const type = blob.type ?? 'application/octet-stream';
|
|
36
|
+
const urlOption = yield* Blob.url(blob);
|
|
37
|
+
if (Option.isSome(urlOption)) {
|
|
38
|
+
return { url: urlOption.value, type };
|
|
39
|
+
}
|
|
40
|
+
const bytes = yield* Blob.read(blob);
|
|
41
|
+
// `Uint8Array` is generic over `ArrayBufferLike` (incl. `SharedArrayBuffer`) while DOM's
|
|
42
|
+
// `BlobPart` only covers `ArrayBuffer`-backed views — a gap between the DOM lib types and
|
|
43
|
+
// the TS standard lib, not fixable by typing `bytes` differently.
|
|
44
|
+
const url = URL.createObjectURL(new globalThis.Blob([bytes as BlobPart], { type }));
|
|
45
|
+
return { url, type };
|
|
46
|
+
}).pipe(
|
|
47
|
+
Effect.provide(Database.layer(db)),
|
|
48
|
+
Effect.catchAll(() => Effect.succeed(undefined)),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
void EffectEx.runPromise(program).then((result) => {
|
|
52
|
+
if (cancelled) {
|
|
53
|
+
if (result?.url.startsWith('blob:')) {
|
|
54
|
+
URL.revokeObjectURL(result.url);
|
|
55
|
+
}
|
|
39
56
|
return;
|
|
40
57
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (url?.startsWith('blob:')) {
|
|
47
|
-
URL.revokeObjectURL(url);
|
|
48
|
-
}
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (url?.startsWith('blob:')) {
|
|
52
|
-
createdBlobUrl = url;
|
|
53
|
-
}
|
|
54
|
-
setRenderUrl(url);
|
|
55
|
-
})
|
|
56
|
-
.catch(() => {
|
|
57
|
-
if (!cancelled) {
|
|
58
|
-
setRenderUrl(undefined);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
}
|
|
58
|
+
if (result?.url.startsWith('blob:')) {
|
|
59
|
+
createdBlobUrl = result.url;
|
|
60
|
+
}
|
|
61
|
+
setRendered(result);
|
|
62
|
+
});
|
|
62
63
|
|
|
63
64
|
return () => {
|
|
64
65
|
cancelled = true;
|
|
@@ -66,19 +67,25 @@ export const FileArticle = ({ role, subject: file }: FileArticleProps) => {
|
|
|
66
67
|
URL.revokeObjectURL(createdBlobUrl);
|
|
67
68
|
}
|
|
68
69
|
};
|
|
69
|
-
|
|
70
|
+
// Keyed on `file.id` rather than `file`/`file.data` directly: ECHO's reactive proxy returns a
|
|
71
|
+
// fresh `Ref` wrapper for `.data` on every access, so including it (or the proxy object itself)
|
|
72
|
+
// here would rerun this effect on every render — clearing the render state and re-resolving it
|
|
73
|
+
// each time, which flickers the image while the object settles.
|
|
74
|
+
}, [file.id]);
|
|
70
75
|
|
|
71
|
-
if (!
|
|
76
|
+
if (!rendered) {
|
|
72
77
|
return null;
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
return (
|
|
76
|
-
<Panel.Root role={role}
|
|
81
|
+
<Panel.Root role={role} classNames='dx-document'>
|
|
77
82
|
<Panel.Content asChild>
|
|
78
|
-
<FilePreview type={
|
|
83
|
+
<FilePreview type={rendered.type} url={rendered.url} />
|
|
79
84
|
</Panel.Content>
|
|
80
85
|
</Panel.Root>
|
|
81
86
|
);
|
|
82
87
|
};
|
|
83
88
|
|
|
84
89
|
export default FileArticle;
|
|
90
|
+
|
|
91
|
+
FileArticle.displayName = 'FileArticle';
|