@dxos/plugin-presenter 0.9.0 → 0.9.1-main.c7dcc2e112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/neutral/{CollectionPresenterArticle-DFREOQTG.mjs → CollectionPresenterArticle-ZRYWBX2H.mjs} +7 -6
- package/dist/lib/neutral/CollectionPresenterArticle-ZRYWBX2H.mjs.map +7 -0
- package/dist/lib/neutral/{DocumentPresenterContainer-KCDZ2O2C.mjs → DocumentPresenterContainer-5QF4P736.mjs} +7 -6
- package/dist/lib/neutral/DocumentPresenterContainer-5QF4P736.mjs.map +7 -0
- package/dist/lib/neutral/PresenterPlugin.mjs +10 -3
- package/dist/lib/neutral/PresenterPlugin.mjs.map +4 -4
- package/dist/lib/neutral/{PresenterSettings-2G4XD4QY.mjs → PresenterSettings-4YFP4K5G.mjs} +3 -3
- package/dist/lib/neutral/{PresenterSettings-2G4XD4QY.mjs.map → PresenterSettings-4YFP4K5G.mjs.map} +3 -3
- package/dist/lib/neutral/{app-graph-builder-DIEDSRPX.mjs → app-graph-builder-JMQVBFG2.mjs} +7 -23
- package/dist/lib/neutral/app-graph-builder-JMQVBFG2.mjs.map +7 -0
- package/dist/lib/neutral/capabilities/index.mjs +7 -3
- package/dist/lib/neutral/capabilities/index.mjs.map +3 -3
- package/dist/lib/neutral/chunk-63IF7OXT.mjs +19 -0
- package/dist/lib/neutral/chunk-63IF7OXT.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-VVALMI52.mjs → chunk-H5JLSLAO.mjs} +7 -3
- package/dist/lib/neutral/chunk-H5JLSLAO.mjs.map +7 -0
- package/dist/lib/neutral/chunk-MSQDHOPV.mjs +47 -0
- package/dist/lib/neutral/chunk-MSQDHOPV.mjs.map +7 -0
- package/dist/lib/neutral/components/index.mjs +87 -18
- package/dist/lib/neutral/components/index.mjs.map +4 -4
- package/dist/lib/neutral/containers/index.mjs +2 -2
- package/dist/lib/neutral/index.mjs +2 -2
- package/dist/lib/neutral/markdown-extension-HGLRVZDF.mjs +37 -0
- package/dist/lib/neutral/markdown-extension-HGLRVZDF.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/lib/neutral/meta.mjs +1 -1
- package/dist/lib/neutral/operation-handler-3ZESW5AK.mjs +13 -0
- package/dist/lib/neutral/operation-handler-3ZESW5AK.mjs.map +7 -0
- package/dist/lib/neutral/operations/index.mjs +9 -0
- package/dist/lib/neutral/operations/index.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +1 -1
- package/dist/lib/neutral/{react-surface-SPJGAJIF.mjs → react-surface-YGBE3TDK.mjs} +4 -4
- package/dist/lib/neutral/react-surface-YGBE3TDK.mjs.map +7 -0
- package/dist/lib/neutral/{settings-R6LRDAAK.mjs → settings-DBV7N5HT.mjs} +3 -3
- package/dist/lib/neutral/{settings-R6LRDAAK.mjs.map → settings-DBV7N5HT.mjs.map} +3 -3
- package/dist/lib/neutral/toggle-presentation-PH7ZJJDD.mjs +60 -0
- package/dist/lib/neutral/toggle-presentation-PH7ZJJDD.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +1 -1
- package/dist/lib/neutral/translations.mjs.map +3 -3
- package/dist/lib/neutral/types/index.mjs +1 -1
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/PresenterPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +6 -3
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/markdown-extension.d.ts +9 -0
- package/dist/types/src/capabilities/markdown-extension.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +2 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/components/Presenter/PresentationShell.d.ts +18 -0
- package/dist/types/src/components/Presenter/PresentationShell.d.ts.map +1 -0
- package/dist/types/src/components/Presenter/index.d.ts +1 -0
- package/dist/types/src/components/Presenter/index.d.ts.map +1 -1
- package/dist/types/src/containers/CollectionPresenterArticle/CollectionPresenterArticle.d.ts.map +1 -1
- package/dist/types/src/containers/DocumentPresenterContainer/DocumentPresenterContainer.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +28 -2
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/toggle-presentation.d.ts +10 -0
- package/dist/types/src/operations/toggle-presentation.d.ts.map +1 -0
- package/dist/types/src/paths.d.ts +3 -0
- package/dist/types/src/paths.d.ts.map +1 -0
- package/dist/types/src/types/PresenterCapabilities.d.ts.map +1 -1
- package/dist/types/src/types/PresenterOperation.d.ts +2 -1
- package/dist/types/src/types/PresenterOperation.d.ts.map +1 -1
- package/dist/types/src/useExitPresenter.d.ts +6 -1
- package/dist/types/src/useExitPresenter.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dx.config.ts +37 -0
- package/package.json +30 -22
- package/src/PresenterPlugin.test.ts +1 -1
- package/src/PresenterPlugin.tsx +9 -2
- package/src/capabilities/app-graph-builder.ts +5 -21
- package/src/capabilities/index.ts +6 -0
- package/src/capabilities/markdown-extension.ts +45 -0
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +7 -7
- package/src/capabilities/settings.ts +2 -2
- package/src/components/Presenter/PresentationShell.tsx +96 -0
- package/src/components/Presenter/index.ts +1 -0
- package/src/components/PresenterSettings/PresenterSettings.tsx +2 -2
- package/src/containers/CollectionPresenterArticle/CollectionPresenterArticle.tsx +14 -20
- package/src/containers/DocumentPresenterContainer/DocumentPresenterContainer.tsx +4 -2
- package/src/meta.ts +2 -30
- package/src/operations/index.ts +7 -0
- package/src/operations/toggle-presentation.ts +59 -0
- package/src/paths.ts +8 -0
- package/src/translations.ts +1 -1
- package/src/types/PresenterCapabilities.ts +3 -1
- package/src/types/PresenterOperation.ts +3 -2
- package/src/useExitPresenter.ts +13 -26
- package/dist/lib/neutral/CollectionPresenterArticle-DFREOQTG.mjs.map +0 -7
- package/dist/lib/neutral/DocumentPresenterContainer-KCDZ2O2C.mjs.map +0 -7
- package/dist/lib/neutral/app-graph-builder-DIEDSRPX.mjs.map +0 -7
- package/dist/lib/neutral/chunk-PPL2FF6R.mjs +0 -38
- package/dist/lib/neutral/chunk-PPL2FF6R.mjs.map +0 -7
- package/dist/lib/neutral/chunk-V323QBC3.mjs +0 -41
- package/dist/lib/neutral/chunk-V323QBC3.mjs.map +0 -7
- package/dist/lib/neutral/chunk-VVALMI52.mjs.map +0 -7
- package/dist/lib/neutral/react-surface-SPJGAJIF.mjs.map +0 -7
package/dx.config.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config2 } from '@dxos/app-framework/config';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
export default Config2.make({
|
|
9
|
+
plugin: {
|
|
10
|
+
key: 'org.dxos.plugin.presenter',
|
|
11
|
+
name: 'Presenter',
|
|
12
|
+
author: 'DXOS',
|
|
13
|
+
description: trim`
|
|
14
|
+
Transform existing workspace objects into interactive presentation slideshows without
|
|
15
|
+
duplicating content. Markdown documents are split into slides on horizontal \`---\`
|
|
16
|
+
separators and rendered with Reveal.js, giving authors a familiar authoring surface
|
|
17
|
+
with full Markdown syntax. Collections can optionally be presented slide-by-slide,
|
|
18
|
+
with each member object rendered by its own plugin surface.
|
|
19
|
+
|
|
20
|
+
Activate presentation mode for any supported object via the action menu or the
|
|
21
|
+
Shift+Cmd+P (macOS) / Shift+Alt+P (Windows/Linux) keyboard shortcut. The presenter
|
|
22
|
+
opens in a solo fullscreen layout managed by the Deck plugin, keeping the rest of the
|
|
23
|
+
workspace undisturbed.
|
|
24
|
+
|
|
25
|
+
Navigation controls and a page-number indicator are shown during collection
|
|
26
|
+
slideshows. Pressing Escape exits fullscreen and returns to the originating document
|
|
27
|
+
or collection view. All navigation is keyboard-accessible.
|
|
28
|
+
|
|
29
|
+
Collaboration is inherent: because slides are backed by live ECHO objects, edits made
|
|
30
|
+
by any peer in the workspace are immediately reflected in an open presentation without
|
|
31
|
+
any manual refresh.
|
|
32
|
+
`,
|
|
33
|
+
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-presenter',
|
|
34
|
+
icon: { key: 'ph--presentation--regular', hue: 'indigo' },
|
|
35
|
+
spec: 'PLUGIN.mdl',
|
|
36
|
+
},
|
|
37
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-presenter",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1-main.c7dcc2e112",
|
|
4
4
|
"description": "Braneframe presenter plugin",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -33,6 +33,11 @@
|
|
|
33
33
|
"types": "./dist/types/src/meta.d.ts",
|
|
34
34
|
"default": "./dist/lib/neutral/meta.mjs"
|
|
35
35
|
},
|
|
36
|
+
"#operations": {
|
|
37
|
+
"source": "./src/operations/index.ts",
|
|
38
|
+
"types": "./dist/types/src/operations/index.d.ts",
|
|
39
|
+
"default": "./dist/lib/neutral/operations/index.mjs"
|
|
40
|
+
},
|
|
36
41
|
"#plugin": {
|
|
37
42
|
"source": {
|
|
38
43
|
"workerd": "./src/PresenterPlugin.workerd.ts",
|
|
@@ -77,10 +82,12 @@
|
|
|
77
82
|
"types": "dist/types/src/index.d.ts",
|
|
78
83
|
"files": [
|
|
79
84
|
"dist",
|
|
85
|
+
"dx.config.ts",
|
|
80
86
|
"src",
|
|
81
87
|
"PLUGIN.mdl"
|
|
82
88
|
],
|
|
83
89
|
"dependencies": {
|
|
90
|
+
"@codemirror/state": "^6.6.0",
|
|
84
91
|
"@effect-atom/atom-react": "^0.5.0",
|
|
85
92
|
"hastscript": "^7.1.0",
|
|
86
93
|
"highlight.js": "^11.9.0",
|
|
@@ -92,20 +99,21 @@
|
|
|
92
99
|
"remark-frontmatter": "^5.0.0",
|
|
93
100
|
"remark-parse-frontmatter": "^1.0.3",
|
|
94
101
|
"reveal.js": "^5.1.0",
|
|
95
|
-
"@dxos/app-
|
|
96
|
-
"@dxos/app-
|
|
97
|
-
"@dxos/
|
|
98
|
-
"@dxos/
|
|
99
|
-
"@dxos/
|
|
100
|
-
"@dxos/
|
|
101
|
-
"@dxos/
|
|
102
|
-
"@dxos/
|
|
103
|
-
"@dxos/plugin-
|
|
104
|
-
"@dxos/
|
|
105
|
-
"@dxos/plugin-
|
|
106
|
-
"@dxos/
|
|
107
|
-
"@dxos/react-ui-
|
|
108
|
-
"@dxos/
|
|
102
|
+
"@dxos/app-toolkit": "0.9.1-main.c7dcc2e112",
|
|
103
|
+
"@dxos/app-framework": "0.9.1-main.c7dcc2e112",
|
|
104
|
+
"@dxos/compute": "0.9.1-main.c7dcc2e112",
|
|
105
|
+
"@dxos/echo": "0.9.1-main.c7dcc2e112",
|
|
106
|
+
"@dxos/keys": "0.9.1-main.c7dcc2e112",
|
|
107
|
+
"@dxos/effect": "0.9.1-main.c7dcc2e112",
|
|
108
|
+
"@dxos/plugin-client": "0.9.1-main.c7dcc2e112",
|
|
109
|
+
"@dxos/log": "0.9.1-main.c7dcc2e112",
|
|
110
|
+
"@dxos/plugin-deck": "0.9.1-main.c7dcc2e112",
|
|
111
|
+
"@dxos/plugin-graph": "0.9.1-main.c7dcc2e112",
|
|
112
|
+
"@dxos/plugin-markdown": "0.9.1-main.c7dcc2e112",
|
|
113
|
+
"@dxos/react-ui-attention": "0.9.1-main.c7dcc2e112",
|
|
114
|
+
"@dxos/react-ui-form": "0.9.1-main.c7dcc2e112",
|
|
115
|
+
"@dxos/ui-editor": "0.9.1-main.c7dcc2e112",
|
|
116
|
+
"@dxos/util": "0.9.1-main.c7dcc2e112"
|
|
109
117
|
},
|
|
110
118
|
"devDependencies": {
|
|
111
119
|
"@effect-atom/atom-react": "^0.5.0",
|
|
@@ -117,11 +125,11 @@
|
|
|
117
125
|
"react": "~19.2.3",
|
|
118
126
|
"react-dom": "~19.2.3",
|
|
119
127
|
"vite": "^8.0.16",
|
|
120
|
-
"@dxos/plugin-testing": "0.9.
|
|
121
|
-
"@dxos/random": "0.9.
|
|
122
|
-
"@dxos/react-ui": "0.9.
|
|
123
|
-
"@dxos/
|
|
124
|
-
"@dxos/
|
|
128
|
+
"@dxos/plugin-testing": "0.9.1-main.c7dcc2e112",
|
|
129
|
+
"@dxos/random": "0.9.1-main.c7dcc2e112",
|
|
130
|
+
"@dxos/react-ui": "0.9.1-main.c7dcc2e112",
|
|
131
|
+
"@dxos/ui-theme": "0.9.1-main.c7dcc2e112",
|
|
132
|
+
"@dxos/storybook-utils": "0.9.1-main.c7dcc2e112"
|
|
125
133
|
},
|
|
126
134
|
"peerDependencies": {
|
|
127
135
|
"@effect-atom/atom-react": "^0.5.0",
|
|
@@ -129,8 +137,8 @@
|
|
|
129
137
|
"effect": "3.21.3",
|
|
130
138
|
"react": "~19.2.3",
|
|
131
139
|
"react-dom": "~19.2.3",
|
|
132
|
-
"@dxos/
|
|
133
|
-
"@dxos/ui
|
|
140
|
+
"@dxos/ui-theme": "0.9.1-main.c7dcc2e112",
|
|
141
|
+
"@dxos/react-ui": "0.9.1-main.c7dcc2e112"
|
|
134
142
|
},
|
|
135
143
|
"publishConfig": {
|
|
136
144
|
"access": "public"
|
|
@@ -10,7 +10,7 @@ import { PresenterPlugin } from '#plugin';
|
|
|
10
10
|
|
|
11
11
|
import { meta } from './meta';
|
|
12
12
|
|
|
13
|
-
const moduleId = (name: string) => `${meta.
|
|
13
|
+
const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
|
|
14
14
|
|
|
15
15
|
describe('PresenterPlugin', () => {
|
|
16
16
|
test('modules activate on the expected events', async ({ expect }) => {
|
package/src/PresenterPlugin.tsx
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import { Plugin } from '@dxos/app-framework';
|
|
6
6
|
import { AppPlugin } from '@dxos/app-toolkit';
|
|
7
|
+
import { MarkdownEvents } from '@dxos/plugin-markdown';
|
|
7
8
|
|
|
8
|
-
import { AppGraphBuilder, PresenterSettings, ReactSurface } from '#capabilities';
|
|
9
|
+
import { AppGraphBuilder, MarkdownExtension, OperationHandler, PresenterSettings, ReactSurface } from '#capabilities';
|
|
9
10
|
import { meta } from '#meta';
|
|
10
11
|
import { translations } from '#translations';
|
|
11
12
|
|
|
@@ -17,11 +18,17 @@ import pluginSpec from '../PLUGIN.mdl?raw';
|
|
|
17
18
|
|
|
18
19
|
export const PresenterPlugin = Plugin.define(meta).pipe(
|
|
19
20
|
AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),
|
|
21
|
+
AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),
|
|
20
22
|
AppPlugin.addSettingsModule({ activate: PresenterSettings }),
|
|
21
23
|
AppPlugin.addSurfaceModule({ activate: ReactSurface }),
|
|
24
|
+
Plugin.addModule({
|
|
25
|
+
id: `${meta.profile.key}/markdown`,
|
|
26
|
+
activatesOn: MarkdownEvents.SetupExtensions,
|
|
27
|
+
activate: MarkdownExtension,
|
|
28
|
+
}),
|
|
22
29
|
AppPlugin.addTranslationsModule({ translations }),
|
|
23
30
|
AppPlugin.addPluginAssetModule({
|
|
24
|
-
asset: { pluginId: meta.
|
|
31
|
+
asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },
|
|
25
32
|
}),
|
|
26
33
|
Plugin.make,
|
|
27
34
|
);
|
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
import * as Option from 'effect/Option';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { AppCapabilities, AppNode
|
|
8
|
+
import { Capability } from '@dxos/app-framework';
|
|
9
|
+
import { AppCapabilities, AppNode } from '@dxos/app-toolkit';
|
|
10
10
|
import { Operation } from '@dxos/compute';
|
|
11
11
|
import { Collection, Obj } from '@dxos/echo';
|
|
12
|
-
import { DeckCapabilities, DeckOperation } from '@dxos/plugin-deck';
|
|
13
12
|
import { GraphBuilder, type Node, NodeMatcher } from '@dxos/plugin-graph';
|
|
14
13
|
import { Markdown } from '@dxos/plugin-markdown';
|
|
15
14
|
import { linkedSegment } from '@dxos/react-ui-attention';
|
|
@@ -47,7 +46,7 @@ export default Capability.makeModule(
|
|
|
47
46
|
id: linkedSegment('presenter'),
|
|
48
47
|
label: 'Presenter',
|
|
49
48
|
icon: 'ph--presentation--regular',
|
|
50
|
-
data: { type: meta.
|
|
49
|
+
data: { type: meta.profile.key, object },
|
|
51
50
|
}),
|
|
52
51
|
]);
|
|
53
52
|
},
|
|
@@ -61,30 +60,15 @@ export default Capability.makeModule(
|
|
|
61
60
|
if (!isPresentable || !db) {
|
|
62
61
|
return Effect.succeed([]);
|
|
63
62
|
}
|
|
64
|
-
const objectPath = getObjectPathFromObject(object);
|
|
65
63
|
|
|
66
64
|
return Effect.succeed([
|
|
67
65
|
{
|
|
68
66
|
id: PresenterOperation.TogglePresentation.meta.key,
|
|
69
|
-
// TODO(burdon): Allow function so can generate state when activated.
|
|
70
|
-
// So can set explicit fullscreen state coordinated with current presenter state.
|
|
71
67
|
data: Effect.fnUntraced(function* () {
|
|
72
|
-
|
|
73
|
-
const deck = deckState.decks[deckState.activeDeck];
|
|
74
|
-
const presenterId = `${objectPath}/${linkedSegment('presenter')}`;
|
|
75
|
-
if (!deck?.fullscreen) {
|
|
76
|
-
yield* Operation.invoke(DeckOperation.Adjust, {
|
|
77
|
-
type: 'solo--fullscreen' as const,
|
|
78
|
-
id: presenterId,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
yield* Operation.invoke(LayoutOperation.Open, {
|
|
82
|
-
subject: [presenterId],
|
|
83
|
-
workspace: getSpacePath(db.spaceId),
|
|
84
|
-
});
|
|
68
|
+
yield* Operation.invoke(PresenterOperation.TogglePresentation, { object });
|
|
85
69
|
}),
|
|
86
70
|
properties: {
|
|
87
|
-
label: ['toggle-presentation.label', { ns: meta.
|
|
71
|
+
label: ['toggle-presentation.label', { ns: meta.profile.key }],
|
|
88
72
|
icon: 'ph--presentation--regular',
|
|
89
73
|
disposition: 'list-item',
|
|
90
74
|
keyBinding: {
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Capability } from '@dxos/app-framework';
|
|
6
|
+
import type { OperationHandlerSet } from '@dxos/compute';
|
|
6
7
|
|
|
7
8
|
export const AppGraphBuilder = Capability.lazy('AppGraphBuilder', () => import('./app-graph-builder'));
|
|
9
|
+
export const MarkdownExtension = Capability.lazy('MarkdownExtension', () => import('./markdown-extension'));
|
|
10
|
+
export const OperationHandler = Capability.lazy<OperationHandlerSet.OperationHandlerSet>(
|
|
11
|
+
'OperationHandler',
|
|
12
|
+
() => import('./operation-handler'),
|
|
13
|
+
);
|
|
8
14
|
export const ReactSurface = Capability.lazy('ReactSurface', () => import('./react-surface'));
|
|
9
15
|
export const PresenterSettings = Capability.lazy('PresenterSettings', () => import('./settings'));
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Prec } from '@codemirror/state';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
|
|
8
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
9
|
+
import { MarkdownCapabilities } from '@dxos/plugin-markdown/types';
|
|
10
|
+
import { keymap } from '@dxos/ui-editor';
|
|
11
|
+
|
|
12
|
+
import { PresenterOperation } from '#types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Contributes the present shortcut (Shift+Cmd+P) to the markdown editor so presentation
|
|
16
|
+
* can be toggled while editing without relying on the global navtree keyboard context.
|
|
17
|
+
*/
|
|
18
|
+
export default Capability.makeModule(
|
|
19
|
+
Effect.fnUntraced(function* () {
|
|
20
|
+
const capabilities = yield* Capability.Service;
|
|
21
|
+
|
|
22
|
+
return Capability.contributes(MarkdownCapabilities.ExtensionProvider, [
|
|
23
|
+
({ document }) => {
|
|
24
|
+
if (!document) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { invokePromise } = capabilities.get(Capabilities.OperationInvoker);
|
|
29
|
+
return Prec.highest(
|
|
30
|
+
keymap.of([
|
|
31
|
+
{
|
|
32
|
+
key: 'Shift-Mod-p',
|
|
33
|
+
preventDefault: true,
|
|
34
|
+
stopPropagation: true,
|
|
35
|
+
run: () => {
|
|
36
|
+
void invokePromise(PresenterOperation.TogglePresentation, { object: document });
|
|
37
|
+
return true;
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
]),
|
|
41
|
+
);
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
+
import type { OperationHandlerSet } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
import { PresenterOperationHandlerSet } from '#operations';
|
|
11
|
+
|
|
12
|
+
export default Capability.makeModule<OperationHandlerSet.OperationHandlerSet>(
|
|
13
|
+
Effect.fnUntraced(function* () {
|
|
14
|
+
return Capability.contributes(Capabilities.OperationHandler, PresenterOperationHandlerSet);
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
@@ -22,14 +22,14 @@ export default Capability.makeModule(() =>
|
|
|
22
22
|
Surface.create({
|
|
23
23
|
id: 'document',
|
|
24
24
|
position: 'first',
|
|
25
|
-
filter:
|
|
25
|
+
filter: Surface.makeFilter(
|
|
26
26
|
AppSurface.Article,
|
|
27
|
-
(data): data is AppSurface.ArticleData<{ type: typeof meta.
|
|
27
|
+
(data): data is AppSurface.ArticleData<{ type: typeof meta.profile.key; object: Markdown.Document }> =>
|
|
28
28
|
!!data.subject &&
|
|
29
29
|
typeof data.subject === 'object' &&
|
|
30
30
|
'type' in data.subject &&
|
|
31
31
|
'object' in data.subject &&
|
|
32
|
-
data.subject.type === meta.
|
|
32
|
+
data.subject.type === meta.profile.key &&
|
|
33
33
|
Obj.instanceOf(Markdown.Document, data.subject.object),
|
|
34
34
|
),
|
|
35
35
|
component: ({ data }) => <DocumentPresenterContainer document={data.subject.object} />,
|
|
@@ -37,14 +37,14 @@ export default Capability.makeModule(() =>
|
|
|
37
37
|
Surface.create({
|
|
38
38
|
id: 'collection',
|
|
39
39
|
position: 'first',
|
|
40
|
-
filter:
|
|
40
|
+
filter: Surface.makeFilter(
|
|
41
41
|
AppSurface.Article,
|
|
42
|
-
(data): data is AppSurface.ArticleData<{ type: typeof meta.
|
|
42
|
+
(data): data is AppSurface.ArticleData<{ type: typeof meta.profile.key; object: Collection.Collection }> =>
|
|
43
43
|
!!data.subject &&
|
|
44
44
|
typeof data.subject === 'object' &&
|
|
45
45
|
'type' in data.subject &&
|
|
46
46
|
'object' in data.subject &&
|
|
47
|
-
data.subject.type === meta.
|
|
47
|
+
data.subject.type === meta.profile.key &&
|
|
48
48
|
Obj.instanceOf(Collection.Collection, data.subject.object),
|
|
49
49
|
),
|
|
50
50
|
component: ({ role, data }) => <CollectionPresenterArticle role={role} subject={data.subject.object} />,
|
|
@@ -56,7 +56,7 @@ export default Capability.makeModule(() =>
|
|
|
56
56
|
}),
|
|
57
57
|
Surface.create({
|
|
58
58
|
id: 'pluginSettings',
|
|
59
|
-
filter: AppSurface.settings(AppSurface.Article, meta.
|
|
59
|
+
filter: AppSurface.settings(AppSurface.Article, meta.profile.key),
|
|
60
60
|
component: ({ data: { subject } }) => {
|
|
61
61
|
const { settings, updateSettings } = useSettingsState<Settings.Settings>(subject.atom);
|
|
62
62
|
return <PresenterSettings settings={settings} onSettingsChange={updateSettings} />;
|
|
@@ -14,7 +14,7 @@ import { PresenterCapabilities, Settings } from '#types';
|
|
|
14
14
|
export default Capability.makeModule(() =>
|
|
15
15
|
Effect.sync(() => {
|
|
16
16
|
const settingsAtom = createKvsStore({
|
|
17
|
-
key: meta.
|
|
17
|
+
key: meta.profile.key,
|
|
18
18
|
schema: Settings.Settings,
|
|
19
19
|
defaultValue: () => ({}),
|
|
20
20
|
});
|
|
@@ -22,7 +22,7 @@ export default Capability.makeModule(() =>
|
|
|
22
22
|
return [
|
|
23
23
|
Capability.contributes(PresenterCapabilities.Settings, settingsAtom),
|
|
24
24
|
Capability.contributes(AppCapabilities.Settings, {
|
|
25
|
-
prefix: meta.
|
|
25
|
+
prefix: meta.profile.key,
|
|
26
26
|
schema: Settings.Settings,
|
|
27
27
|
atom: settingsAtom,
|
|
28
28
|
}),
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { composable, composableProps } from '@dxos/react-ui';
|
|
8
|
+
import { mx } from '@dxos/ui-theme';
|
|
9
|
+
|
|
10
|
+
export type PresentationShellProps = PropsWithChildren<{
|
|
11
|
+
/** Invoked once the exit fade-out has completed. */
|
|
12
|
+
onExit?: () => void;
|
|
13
|
+
/** Fade-in/out duration (ms). */
|
|
14
|
+
fadeDuration?: number;
|
|
15
|
+
/** Duration the [ESC] hint remains visible (ms). */
|
|
16
|
+
hintDuration?: number;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wraps presentation content with a fade-in/out transition, an ESC handler that exits in a
|
|
21
|
+
* single keypress (intercepting before the deck's fullscreen handler), and a transient [ESC]
|
|
22
|
+
* caption shown on enter.
|
|
23
|
+
*/
|
|
24
|
+
export const PresentationShell = composable<HTMLDivElement, PresentationShellProps>(
|
|
25
|
+
({ children, onExit, fadeDuration = 300, hintDuration = 3000, ...props }, forwardedRef) => {
|
|
26
|
+
const [visible, setVisible] = useState(false);
|
|
27
|
+
const [exiting, setExiting] = useState(false);
|
|
28
|
+
const [showHint, setShowHint] = useState(true);
|
|
29
|
+
const exitTimeout = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
30
|
+
// Guards against exiting twice; a state updater can't gate this since React double-invokes
|
|
31
|
+
// updaters in dev, which would schedule the exit (and its side effects) more than once.
|
|
32
|
+
const exitingRef = useRef(false);
|
|
33
|
+
|
|
34
|
+
// Fade in once mounted.
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const frame = requestAnimationFrame(() => setVisible(true));
|
|
37
|
+
return () => cancelAnimationFrame(frame);
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
// Hide the hint after the configured duration.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const timer = setTimeout(() => setShowHint(false), hintDuration);
|
|
43
|
+
return () => clearTimeout(timer);
|
|
44
|
+
}, [hintDuration]);
|
|
45
|
+
|
|
46
|
+
const handleExit = useCallback(() => {
|
|
47
|
+
if (exitingRef.current) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
exitingRef.current = true;
|
|
51
|
+
setExiting(true);
|
|
52
|
+
setVisible(false);
|
|
53
|
+
exitTimeout.current = setTimeout(() => onExit?.(), fadeDuration);
|
|
54
|
+
}, [onExit, fadeDuration]);
|
|
55
|
+
|
|
56
|
+
useEffect(() => () => clearTimeout(exitTimeout.current), []);
|
|
57
|
+
|
|
58
|
+
// Capture ESC before the deck/reveal handlers so a single keypress exits directly.
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
const handler = (event: KeyboardEvent) => {
|
|
61
|
+
if (event.key === 'Escape') {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
event.stopImmediatePropagation();
|
|
64
|
+
handleExit();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
document.addEventListener('keydown', handler, { capture: true });
|
|
69
|
+
return () => document.removeEventListener('keydown', handler, { capture: true });
|
|
70
|
+
}, [handleExit]);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
{...composableProps(props, {
|
|
75
|
+
classNames: [
|
|
76
|
+
'relative grow overflow-hidden bg-black transition-opacity',
|
|
77
|
+
visible && !exiting ? 'opacity-100' : 'opacity-0',
|
|
78
|
+
],
|
|
79
|
+
style: { transitionDuration: `${fadeDuration}ms` },
|
|
80
|
+
})}
|
|
81
|
+
ref={forwardedRef}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
|
|
85
|
+
<div
|
|
86
|
+
className={mx(
|
|
87
|
+
'absolute top-4 left-4 z-[300] transition-opacity duration-500',
|
|
88
|
+
showHint && !exiting ? 'opacity-100' : 'opacity-0',
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
<span className='rounded-sm bg-white/10 px-2 py-1 font-mono text-sm text-white/70'>[ESC]</span>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
);
|
|
@@ -14,11 +14,11 @@ import { Settings } from '#types';
|
|
|
14
14
|
export type PresenterSettingsProps = AppSurface.SettingsArticleProps<Settings.Settings>;
|
|
15
15
|
|
|
16
16
|
export const PresenterSettings = ({ settings, onSettingsChange }: PresenterSettingsProps) => {
|
|
17
|
-
const { t } = useTranslation(meta.
|
|
17
|
+
const { t } = useTranslation(meta.profile.key);
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
20
|
<SettingsForm.Viewport>
|
|
21
|
-
<SettingsForm.Section title={t('settings.title', { ns: meta.
|
|
21
|
+
<SettingsForm.Section title={t('settings.title', { ns: meta.profile.key })}>
|
|
22
22
|
<SettingsForm.FieldSet
|
|
23
23
|
readonly={!onSettingsChange}
|
|
24
24
|
schema={Settings.Settings}
|
|
@@ -9,7 +9,7 @@ import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
|
9
9
|
import { type Collection, Obj } from '@dxos/echo';
|
|
10
10
|
import { Panel } from '@dxos/react-ui';
|
|
11
11
|
|
|
12
|
-
import { PageNumber, Pager, Layout as PresenterLayout } from '#components';
|
|
12
|
+
import { PageNumber, Pager, PresentationShell, Layout as PresenterLayout } from '#components';
|
|
13
13
|
import { PresenterContext } from '#types';
|
|
14
14
|
|
|
15
15
|
import { useExitPresenter } from '../../useExitPresenter';
|
|
@@ -24,26 +24,20 @@ export const CollectionPresenterArticle = ({ role, subject: collection }: Collec
|
|
|
24
24
|
return (
|
|
25
25
|
<Panel.Root role={role} classNames='relative'>
|
|
26
26
|
<Panel.Content asChild>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<Pager
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
<PresentationShell onExit={handleExit}>
|
|
28
|
+
<PresenterLayout
|
|
29
|
+
bottomRight={<PageNumber index={slide} count={collection.objects.length} />}
|
|
30
|
+
bottomLeft={<Pager index={slide} count={collection.objects.length} keys={running} onChange={setSlide} />}
|
|
31
|
+
>
|
|
32
|
+
<Surface.Surface
|
|
33
|
+
type={AppSurface.Slide}
|
|
34
|
+
data={{
|
|
35
|
+
subject: collection.objects[slide],
|
|
36
|
+
attendableId: Obj.getURI(collection),
|
|
37
|
+
}}
|
|
36
38
|
/>
|
|
37
|
-
|
|
38
|
-
>
|
|
39
|
-
<Surface.Surface
|
|
40
|
-
type={AppSurface.Slide}
|
|
41
|
-
data={{
|
|
42
|
-
subject: collection.objects[slide],
|
|
43
|
-
attendableId: Obj.getURI(collection),
|
|
44
|
-
}}
|
|
45
|
-
/>
|
|
46
|
-
</PresenterLayout>
|
|
39
|
+
</PresenterLayout>
|
|
40
|
+
</PresentationShell>
|
|
47
41
|
</Panel.Content>
|
|
48
42
|
</Panel.Root>
|
|
49
43
|
);
|
|
@@ -7,7 +7,7 @@ import React, { type FC } from 'react';
|
|
|
7
7
|
import { type Markdown } from '@dxos/plugin-markdown';
|
|
8
8
|
import { Panel } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
|
-
import { RevealPlayer } from '#components';
|
|
10
|
+
import { PresentationShell, RevealPlayer } from '#components';
|
|
11
11
|
|
|
12
12
|
import { useExitPresenter } from '../../useExitPresenter';
|
|
13
13
|
|
|
@@ -17,7 +17,9 @@ export const DocumentPresenterContainer: FC<{ document: Markdown.Document }> = (
|
|
|
17
17
|
return (
|
|
18
18
|
<Panel.Root classNames='relative'>
|
|
19
19
|
<Panel.Content asChild>
|
|
20
|
-
<
|
|
20
|
+
<PresentationShell onExit={handleExit}>
|
|
21
|
+
<RevealPlayer content={document.content.target?.content ?? ''} />
|
|
22
|
+
</PresentationShell>
|
|
21
23
|
</Panel.Content>
|
|
22
24
|
</Panel.Root>
|
|
23
25
|
);
|
package/src/meta.ts
CHANGED
|
@@ -3,35 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Plugin } from '@dxos/app-framework';
|
|
6
|
-
import { DXN } from '@dxos/keys';
|
|
7
|
-
import { trim } from '@dxos/util';
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
key: DXN.make('org.dxos.plugin.presenter'),
|
|
11
|
-
name: 'Presenter',
|
|
12
|
-
author: 'DXOS',
|
|
13
|
-
description: trim`
|
|
14
|
-
Transform existing workspace objects into interactive presentation slideshows without
|
|
15
|
-
duplicating content. Markdown documents are split into slides on horizontal \`---\`
|
|
16
|
-
separators and rendered with Reveal.js, giving authors a familiar authoring surface
|
|
17
|
-
with full Markdown syntax. Collections can optionally be presented slide-by-slide,
|
|
18
|
-
with each member object rendered by its own plugin surface.
|
|
7
|
+
import config from '../dx.config';
|
|
19
8
|
|
|
20
|
-
|
|
21
|
-
Shift+Cmd+P (macOS) / Shift+Alt+P (Windows/Linux) keyboard shortcut. The presenter
|
|
22
|
-
opens in a solo fullscreen layout managed by the Deck plugin, keeping the rest of the
|
|
23
|
-
workspace undisturbed.
|
|
24
|
-
|
|
25
|
-
Navigation controls and a page-number indicator are shown during collection
|
|
26
|
-
slideshows. Pressing Escape exits fullscreen and returns to the originating document
|
|
27
|
-
or collection view. All navigation is keyboard-accessible.
|
|
28
|
-
|
|
29
|
-
Collaboration is inherent: because slides are backed by live ECHO objects, edits made
|
|
30
|
-
by any peer in the workspace are immediately reflected in an open presentation without
|
|
31
|
-
any manual refresh.
|
|
32
|
-
`,
|
|
33
|
-
icon: 'ph--presentation--regular',
|
|
34
|
-
iconHue: 'indigo',
|
|
35
|
-
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-presenter',
|
|
36
|
-
spec: 'PLUGIN.mdl',
|
|
37
|
-
});
|
|
9
|
+
export const meta = Plugin.getMetaFromConfig(config);
|