@manifest-editor/shell 2.2.1-cdn.1.2412bf12 → 2.2.1

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.
Files changed (2) hide show
  1. package/README.md +145 -217
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -1,278 +1,206 @@
1
- # Shell
1
+ <p align="center">
2
+ <a href="https://github.com/digirati-co-uk/iiif-manifest-editor"><img src="https://raw.githubusercontent.com/digirati-co-uk/iiif-manifest-editor/main/apps/docs/content/img/logo.svg" width="360" alt="Manifest Editor"></a>
3
+ </p>
4
+ <p align="center">
5
+ <a href="https://www.npmjs.com/package/@manifest-editor/shell"><img src="https://img.shields.io/npm/v/%40manifest-editor%2Fshell" alt="npm version"></a>
6
+ <a href="https://github.com/digirati-co-uk/iiif-manifest-editor/actions/workflows/build-test.yml"><img src="https://img.shields.io/github/actions/workflow/status/digirati-co-uk/iiif-manifest-editor/build-test.yml?branch=main&amp;label=build" alt="Build status"></a>
7
+ <a href="https://manifest-editor-docs.netlify.app/"><img src="https://img.shields.io/badge/docs-Manifest_Editor-blue" alt="Documentation"></a>
8
+ </p>
2
9
 
10
+ # @manifest-editor/shell
3
11
 
4
- ## App context
12
+ ## Getting started with Manifest Editor
5
13
 
6
- ### useApps()
14
+ For the complete editor, start with [`manifest-editor`](https://www.npmjs.com/package/manifest-editor) and its [installation and integration guide](https://github.com/digirati-co-uk/iiif-manifest-editor/tree/main/packages/manifest-editor#readme). It combines the editor UI, presets, creators, and editing APIs. This package is one part of that workspace.
7
15
 
8
- * Get current app
9
- * Change app
10
- * List all apps
16
+ ## About
11
17
 
12
- ```js
13
- const { currentApp, apps, changeApp } = useApps();
18
+ Application composition, state, layouts, plugins, configuration, and background tasks. Use this lower-level package when you need to assemble a custom editor beyond the combined component.
14
19
 
15
- // All app information.
16
- apps['manifest-editor'];
20
+ ## Quick start
17
21
 
18
- // Current app
19
- currentApp.id;
20
-
21
- // Change app
22
- changeApp({ id: 'manifest-editor' });
23
-
24
- // Change app with argument (launcher)
25
- changeApp({ id: 'collection-explorer', args: 'https://example.org/collection-123' });
22
+ ```sh
23
+ npm install manifest-editor @manifest-editor/shell @manifest-editor/manifest-preset @iiif/helpers react-iiif-vault react@19 react-dom@19
26
24
  ```
27
25
 
28
- ### useAppState()
29
- Global app state, currently only used for `canvasId`.
30
- * Get global state
31
- * Set global state
32
-
33
- ```js
34
- const appState = useAppState();
35
-
36
- // Setting
37
- appState.setState({ canvasId: 'https://example.org/canvas-1' })
38
-
39
- // Getting
40
- appState.state.canvasId;
41
-
26
+ ```tsx
27
+ import { AppProvider, Layout, PluginProvider, ShellProvider, mapApp } from "@manifest-editor/shell";
28
+ import * as manifestPreset from "@manifest-editor/manifest-preset";
29
+ import { VaultProvider } from "react-iiif-vault";
30
+ import { Vault } from "@iiif/helpers";
31
+ import "manifest-editor/index.css";
32
+
33
+ const app = mapApp(manifestPreset);
34
+ const vault = new Vault();
35
+ const manifest = await vault.loadManifest("https://wellcomelibrary.org/iiif/b18035723/manifest");
36
+ if (!manifest) throw new Error("Manifest could not be loaded");
37
+ const resource = { id: manifest.id, type: "Manifest" as const };
38
+
39
+ export function CustomEditor() {
40
+ return (
41
+ <PluginProvider plugins={manifestPreset.plugins}>
42
+ <AppProvider appId={app.metadata.id} definition={app} instanceId="my-editor">
43
+ <VaultProvider vault={vault}>
44
+ <ShellProvider resource={resource}>
45
+ <Layout />
46
+ </ShellProvider>
47
+ </VaultProvider>
48
+ </AppProvider>
49
+ </PluginProvider>
50
+ );
51
+ }
42
52
  ```
43
53
 
44
- ## Config context
54
+ This browser-side example loads the manifest before mounting. Use a client-only entry point in applications with server rendering. The provider nesting is deliberate.
45
55
 
46
- ```js
47
- const config = useConfig();
56
+ ## What the shell owns
48
57
 
58
+ The shell connects a preset's UI definitions to a loaded IIIF resource. It owns panel state, editing selection, creator/editor dispatch, configuration, previews, plugins, and background work. It does not replace the Vault and does not itself implement a project-storage service.
49
59
 
50
- // Languages for the editor ['en, 'none']
51
- config.i18n.availableLanguages;
60
+ | Layer | Responsibility |
61
+ | --- | --- |
62
+ | `PluginProvider` | Available plugin modules, enable/disable state, and global plugin settings. |
63
+ | `AppProvider` | One mapped app definition, metadata, instance identity, and app-local state. |
64
+ | `MultiAppProvider` | Optional app registry and switching; supplies the selected app through `AppProvider`. |
65
+ | `VaultProvider` from `react-iiif-vault` | Loaded IIIF data and subscriptions. |
66
+ | `ShellProvider` | The active root resource and the editing/layout/configuration contexts. |
67
+ | `Layout` | Renders the selected panels and supporting UI. |
52
68
 
53
- // Behaviours string[]
54
- config.behaviorPresets;
69
+ The quick start shows a single app. Keep providers and Vault instances stable when adding controls inside it. A hook that reads a provider's context must run below that provider.
55
70
 
56
- // IIIF collection for templates
57
- config.newTemplates;
71
+ ## Define or extend an app
58
72
 
59
- // List of previews.
60
- config.previews;
61
- ```
73
+ An app module supplies default metadata plus panel, editor, creator, and background definitions. `mapApp(module)` turns that into a `MappedApp` with `metadata`, `layout`, and optional configuration/preset data. Already mapped presets, such as `collectionPreset`, can be passed directly to `AppProvider`.
62
74
 
63
- ## Layout
64
- * Get available layouts in app
65
- * Get layout state
66
- * Get layout actions
75
+ `extendApp` is the composition helper used by the exhibition preset to reuse manifest editing while adding or replacing layout and configuration. The [exhibition preset source](../../presets/exhibition-preset/src/index.tsx) is a concrete example. Panel IDs are registration keys; actions must refer to an ID actually included in the active app.
67
76
 
77
+ ### A custom panel
68
78
 
69
- ### Available layouts
70
- ```js
71
- const layouts = useAvailableLayouts();
79
+ ```tsx
80
+ import type { LayoutPanel } from "@manifest-editor/shell";
72
81
 
73
- // Loading status, check before listing.
74
- layouts.loading;
75
-
76
- // List of left panels.
77
- layouts.leftPanels;
78
-
79
- // List of center panels.
80
- layouts.centerPanels;
81
-
82
- // List of right panels
83
- layouts.rightPanels;
82
+ export const notesPanel: LayoutPanel = {
83
+ id: "example-notes",
84
+ label: "Notes",
85
+ render: () => <p>Instructions for this editing workflow.</p>,
86
+ };
84
87
  ```
85
88
 
86
- ### Get layout state
87
- ```js
88
- const {
89
- leftPanel,
90
- rightPanel,
91
- pinnedRightPanel,
92
- centerPanel,
93
- } = useLayoutState();
94
-
95
- // Following are the same for all.
89
+ Add this definition to the app module's `leftPanels`, `rightPanels`, or `centerPanels` before mapping it. Defining a panel does not automatically select/open it.
96
90
 
97
- // Panel state (defined by app)
98
- leftPanel.state;
91
+ ## App selection and app-local state
99
92
 
100
- // Current identifier for panel
101
- leftPanel.current;
93
+ `useApps()` requires `MultiAppProvider`. It returns `apps`, `currentApp`, `initialApp`, `changeApp`, and `changeProjectType`. `apps` is keyed by app ID; switching can carry arguments:
102
94
 
103
- // If the panel is open or not.
104
- leftPanel.open;
95
+ ```tsx
96
+ import { useApps } from "@manifest-editor/shell";
105
97
 
106
- // If the panel is minimised (not yet implemented)
107
- leftPanel.minimised;
108
-
109
- // Custom width of panel, if set.
110
- leftPanel.customWidth;
111
-
112
- // Only for pinned:
98
+ export function SwitchToManifestEditor() {
99
+ const { changeApp } = useApps();
100
+ return <button onClick={() => changeApp({ id: "manifest-editor" })}>Manifest editor</button>;
101
+ }
102
+ ```
113
103
 
114
- // Can this be pinned;
115
- pinnedRightPanel.pinnable;
104
+ `useApp()` reads the current mapped definition. `useAppInstance()` reads instance identity. `useAppState<T>()` exposes `state` and `setState` for application-local state; keep that separate from IIIF resource data in the Vault. The old `canvasId` example was an app convention, not the full scope of this store.
116
105
 
117
- // Is this pinned?
118
- pinnedRightPanel.pinned;
119
- ```
106
+ ## Configuration
120
107
 
121
- ### Get layout actions
122
- ```js
123
- const actions = useLayoutActions();
108
+ `useConfig()` exposes the merged editor configuration. `useSaveConfig()` supplies the persistence/update callback used by configuration controls. A host passes `config` and `saveConfig` into `ShellProvider` (or the combined `ManifestEditor`).
124
109
 
125
- // Open a panel
126
- actions.open('manifest-properties');
127
- actions.open('manifest-properties', { some: 'state' });
110
+ Useful configuration areas include:
128
111
 
129
- // Change a panels state (does not open)
130
- actions.change('canvas-properties', { tab: 2 });
112
+ | Key | Purpose |
113
+ | --- | --- |
114
+ | `i18n` | Default/available languages and advanced language editing. |
115
+ | `editorConfig` | Per-resource editor settings, including hidden tabs. |
116
+ | `editorFeatureFlags` | Canvas/media and interface options. |
117
+ | `creators` | Configuration for individual creators. |
118
+ | `previews`, `defaultPreview` | Available viewers and the preferred preview. |
119
+ | `behaviorPresets`, `newTemplates` | Editing guidance and resource templates. |
120
+ | `export` | Serialization version and identifier options. |
121
+ | `plugins` | Per-editor plugin configuration. |
122
+ | `urlState` | Host adapters for reading and replacing URL search parameters. |
131
123
 
132
- // Close panel
133
- actions.close('canvas-properties');
124
+ The `Config` type is the authoritative list. It is separate from the lower-level `EditorConfig` in `@manifest-editor/editor-api`, which controls an individual editing operation.
134
125
 
135
- // Toggle panel
136
- actions.toggle('canvas-properties');
126
+ ## Layout state and actions
137
127
 
128
+ `useAvailableLayouts()` returns panel definitions and optional loading state. `useLayoutState()` returns `leftPanel`, `centerPanel`, `rightPanel`, `pinnedRightPanel`, and `modal`. Each slot tracks `current`, `state`, `open`, `minimised`, `customWidth`, and a stack; the pinned slot also records pinning state.
138
129
 
139
- // Same actions as above are available PER panel
140
- actions.leftPanel;
141
- actions.rightPanel;
142
- actions.centerPanel;
130
+ ```tsx
131
+ import { useLayoutActions, useLayoutState } from "@manifest-editor/shell";
143
132
 
144
- // And some additional on each:
145
- actions.leftPanel.resetSize();
146
- actions.leftPanel.setCustomWidth(450);
147
- actions.leftPanel.maximise(); // N/A
148
- actions.leftPanel.minimise(); // N/A
133
+ export function PanelControls() {
134
+ const actions = useLayoutActions();
135
+ const { rightPanel } = useLayoutState();
136
+ return (
137
+ <button onClick={() => actions.rightPanel.close()} disabled={!rightPanel.open}>
138
+ Close inspector
139
+ </button>
140
+ );
141
+ }
149
142
  ```
150
143
 
144
+ Actions on registered panel IDs include `open(id, state?)`, `change(id, state?)`, `close(id)`, `toggle(id)`, and `stack(id, state?)`. Object overloads allow explicit stacking/state options. Per-slot actions such as `actions.leftPanel.open({ id, state })` use object arguments; they are not identical to the ID-based top-level helpers. Slots also expose resize, minimise/maximise, and stack navigation operations; the pinned slot adds `pin`/`unpin`.
151
145
 
152
- ## Preview Context
153
- * Get selected preview
154
- * Get all activated previews
155
- * List all configurations and their class handlers
156
- * Activate, select and focus previews
157
- * Delete previews from storage
158
- * Update all existing previews (vault -> external)
159
-
160
- ```js
161
- const preview = usePreviewContext();
146
+ `actions.edit(reference, context?, options?)` routes a resource to the editor UI, with options for a property, selected tab, stacking, or forced opening. `actions.create(creatableResource)` opens the contextual creation workflow. Use `Creator.create()` from creator-api when you want headless execution instead of showing a picker/form.
162
147
 
163
- // Which preview is selected.
164
- preview.selected;
148
+ ## Editing selection and reusable controls
165
149
 
166
- // List of active preview ids
167
- preview.active;
150
+ `useAppResource()` reads the root resource. The editing stack can point at a different resource, such as a Canvas or Annotation. `ResourceEditingProvider` supplies the reference for a reusable property control; `useResource<T>()` resolves it from the Vault and subscribes to changes:
168
151
 
169
- // List of all available preview configurations
170
- preview.configs;
152
+ ```tsx
153
+ import type { DescriptiveProperties } from "@iiif/presentation-3";
154
+ import { ResourceEditingProvider, useResource } from "@manifest-editor/shell";
171
155
 
172
- // List of all available config classes.
173
- preview.handlers;
156
+ function ResourceIdentity() {
157
+ const resource = useResource<DescriptiveProperties>();
158
+ return <p>{resource.id}</p>;
159
+ }
174
160
 
175
- // And actions
176
- preview.actions.activatePreview('universal-viewer');
177
- preview.actions.selectPreview('universal-viewer');
178
- preview.actions.deletePreview('universal-viewer');
179
- preview.actions.focusPreview('universal-viewer');
180
- preview.actions.updatePreviews();
161
+ export function SelectedResource({ id }: { id: string }) {
162
+ return (
163
+ <ResourceEditingProvider resource={{ id, type: "Canvas" }}>
164
+ <ResourceIdentity />
165
+ </ResourceEditingProvider>
166
+ );
167
+ }
181
168
  ```
182
169
 
183
- ## Project context
184
- * Create project (then select automatically)
185
- * List all recent projects
186
- * Select a project to work on
170
+ This example also needs a surrounding VaultProvider with the Canvas already loaded. `useResource` throws if nothing is selected. `useOptionalResource()` returns the optional **reference**, not a fully resolved entity. `useEditingResource` and `useEditingResourceStack` expose broader editing-navigation state.
187
171
 
172
+ ## Preview lifecycle
188
173
 
189
- ### useProjectCreators()
190
- ```js
191
- const { createProjectFromManifestId, createBlankManifest } = useProjectCreators();
174
+ `usePreviewContext()` exposes `selected`, `active`, `configs`, `handlers`, and `actions`. A configuration selects a viewer; its handler implements creation, updates, focus, and deletion of preview instances.
192
175
 
193
- // Create and select a blank manifest
194
- await createBlankManifest();
176
+ The actions are `activatePreview(id)`, `selectPreview(id)`, `focusPreview(id)`, `deletePreview(id)`, `updatePreviews()`, and `getPreviewLink()`. Use IDs from the configured previews rather than assuming a viewer is installed. Updating previews serializes or forwards the edited Vault state through the handler; it is not the same as saving the project.
195
177
 
196
- // Create a new project from external manifest id
197
- await createProjectFromManifestId('https://example.org/manifest');
198
- ```
178
+ For a live iframe, `createIframeVaultBridge(vault, port)` forwards host Vault state/actions over a MessagePort and returns cleanup. Its preview side is read-only: incoming mutation actions are rejected. `MessagePortClientVault` is re-exported for preview consumers. The host owns iframe creation, port transfer, and teardown.
199
179
 
200
- ### useProjectContext()
201
- ```js
202
- const ctx = useProjectContext();
203
-
204
- // Loading status (projects can be loaded from network)
205
- ctx.loadingStatus.loading;
206
- ctx.loadingStatus.loaded;
207
- ctx.loadingStatus.lastLoaded; // time.
208
-
209
- // Current proejct details (same for listing)
210
- ctx.current;
211
- ctx.current.name;
212
- ctx.current.thumbnail; // Optional
213
- ctx.current.previews; // list of previews
214
- ctx.current.resource.id; // Top level resource {id, type: Manifest}
215
- ctx.current.storage; // Configured storage for project
216
- ctx.current.settings; // Unused, future settings
217
- ctx.current.filename; // File name (unused)
218
- ctx.current.publications; // Future publish options
219
- ctx.current.metadata.modified; // Last modified
220
- ctx.current.metadata.created; // Created at
221
-
222
- // List of all projects
223
- ctx.allProjects;
224
-
225
- // Actions
226
- ctx.actions.switchProject('some-project-id');
227
- ctx.actions.createProject({ ... });
228
- ctx.actions.deselectProject('some-project-id')
229
- ctx.actions.saveProject({ ... });
230
- ctx.actions.updateThumbnail('...');
231
- ctx.actions.updateDetails({name: '..', filename: '..'});
232
- ctx.actions.updateSettings({ ... });
233
-
234
- // More internal actions
235
- ctx.actions.createPublication({ ... }); // unused
236
- ctx.actions.removePublication('publication-id');
237
- ctx.actions.updatePublication({ ... });
238
- ctx.actions.updateStorage({ ... });
239
- ctx.actions.setLoadingStatus({ ... })
240
- ctx.actions.load({ ... });
241
- ```
242
-
243
- ## Resource editing context
180
+ ## Plugins, background work, and specifications
244
181
 
245
- Simple Context/hook combo
182
+ Plugin modules contribute panels, editor/creator definitions, background tasks, settings, and metadata. Register the module namespace through `PluginProvider`; a metadata-only default export is not the complete plugin. `usePlugins`, `useEnabledPlugins`, and `usePluginActions` expose management state; `usePluginSettings` and `usePluginConfigApi` support plugin-specific preferences.
246
183
 
247
- Top level:
184
+ Background actions are app/plugin contributions with their own execution and progress lifecycle. `useAvailableBackgroundActions()` groups the currently available actions; the supplied background action menu and toast components surface them. The specification context exposes configured rules and their reports for interfaces such as the Specifications plugin. These features belong in the shell because they coordinate UI, resource context, and longer-running work.
248
185
 
249
- ```jsx
250
- const anno = { id: 'https://e.org/anno-1', type: 'Annotation' }
186
+ ## Projects are a separate package
251
187
 
252
- <ResourceEditingProvider resource={anno}>
253
- <MyGenericEditor />
254
- </ResourceEditingProvider>
255
- ```
188
+ The previous shell README also documented project creation, recent projects, loading, storage, and saving. Those APIs now belong to [`@manifest-editor/projects`](../projects), not this package's exports. A multi-project host combines that package's `ProjectProvider` with `MultiAppProvider` and a Vault/storage backend. A single embedded editor does not need a project provider.
256
189
 
257
- And then inside the generic editor
190
+ ## Where to look next
258
191
 
259
- ```typescript
260
- import {
261
- DescriptiveProperties
262
- } from "@iiif/presentation-3";
192
+ - [Layout contracts](./src/Layout/Layout.types.ts): panel definitions, action overloads, state, and render contexts.
193
+ - [App composition](./src/AppContext): mapped apps, app state, and multi-app switching.
194
+ - [Plugin contracts](./src/PluginContext/PluginContext.types.ts): modules, metadata, lazy loading, and settings.
195
+ - [Configuration](./src/ConfigContext/ConfigContext.tsx): current configuration fields and defaults.
196
+ - [Background tasks](./src/BackgroundTasks): action definitions and progress integration.
263
197
 
264
- function MyGenericEditor() {
198
+ ## API overview
265
199
 
266
- // You can narrow with typescript
267
- const resource = useResource<DescriptiveProperties>();
200
+ - Composition: `mapApp`, `extendApp`, `AppProvider`, `PluginProvider`, `ShellProvider`, and `Layout`.
201
+ - Layout definitions, panels, editing stacks, context menus, and resource hooks.
202
+ - Plugin metadata, registration, settings, and enable/disable management.
203
+ - Background actions, task progress, specifications, previews, and export controls.
204
+ - `Config` and configuration providers for host-managed preferences.
268
205
 
269
- // Although technical properties, the "ref" is always
270
- // merged with the type.
271
- resource.id
272
- resource.type
273
- resource.label
274
- resource.summary
275
- // ...
276
-
277
- }
278
- ```
206
+ See the [source exports](https://github.com/digirati-co-uk/iiif-manifest-editor/tree/main/packages/shell/src) for the complete API and TypeScript definitions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manifest-editor/shell",
3
- "version": "2.2.1-cdn.1.2412bf12",
3
+ "version": "2.2.1",
4
4
  "type": "module",
5
5
  "bugs": "https://github.com/digirati-co-uk/iiif-manifest-editor/issues",
6
6
  "repository": {
@@ -46,10 +46,10 @@
46
46
  "@iiif/parser": "4.0.1",
47
47
  "@iiif/presentation-3": "^2.2.3",
48
48
  "@iiif/presentation-3-normalized": "^0.9.7",
49
- "@manifest-editor/client-vault": "2.2.1-cdn.1.2412bf12",
50
- "@manifest-editor/components": "2.2.1-cdn.1.2412bf12",
51
- "@manifest-editor/creator-api": "2.2.1-cdn.1.2412bf12",
52
- "@manifest-editor/editor-api": "2.2.1-cdn.1.2412bf12",
49
+ "@manifest-editor/client-vault": "2.2.1",
50
+ "@manifest-editor/components": "2.2.1",
51
+ "@manifest-editor/creator-api": "2.2.1",
52
+ "@manifest-editor/editor-api": "2.2.1",
53
53
  "@react-stately/toast": "^3.1.2",
54
54
  "@tanstack/react-query": "^5.56.2",
55
55
  "immer": "^10.0.4",
@@ -70,7 +70,7 @@
70
70
  "zustand": "4.5.2"
71
71
  },
72
72
  "devDependencies": {
73
- "@manifest-editor/ui": "2.2.0",
73
+ "@manifest-editor/ui": "2.2.1",
74
74
  "@tsdown/css": "0.22.14",
75
75
  "@types/react": "19.2.18",
76
76
  "@types/react-dom": "19.2.4",
@@ -84,7 +84,7 @@
84
84
  "publishConfig": {
85
85
  "access": "public"
86
86
  },
87
- "gitHead": "2412bf12476cf583d14bccf34444dbf8741145b2",
87
+ "gitHead": "903673e7c35feed673fc100cb0efe34733057c91",
88
88
  "inlinedDependencies": {
89
89
  "react-accessible-dropdown-menu-hook": "4.0.0",
90
90
  "tiny-invariant": "1.3.3"