feat(resourceEditor): introduce resourceEditor extension type#2571
Draft
dschmidt wants to merge 3 commits into
Draft
feat(resourceEditor): introduce resourceEditor extension type#2571dschmidt wants to merge 3 commits into
dschmidt wants to merge 3 commits into
Conversation
Introduces a new `resourceEditor` extension type that lets file viewers and editors register themselves typed against `ResourceEditorBindings` instead of the previous untyped `wrappedComponent` slot contract. The loading / saving / dirty / autosave logic moves out of `AppWrapper.vue` into a `useResourceEditor` composable; the route-mounted host becomes `ResourceEditorRouteHost`. A lightweight `ResourceEditorHost` exists for embed use-cases (still route-bound today, made fully functional in a follow-up). AppWrapper and AppWrapperRoute remain as @deprecated shims that synthesize a ResourceEditorExtension from the legacy props so web-app- external and the out-of-repo web-extensions (codemirror, tiptap, json-viewer, draw-io) keep working untouched. Apps migrated to the new path: web-app-pdf-viewer, web-app-preview, web-app-text-editor, web-app-epub-reader. web-app-external stays on the legacy shim by design (AppProviderService doesn't fit cleanly into the bindings contract).
Makes `ResourceEditorHost` actually usable outside a route. The composable is now resource-agnostic: it takes `resource`/`space` as refs and runs capability-driven loading plus save/dirty/autosave on top. Resource resolution is split off into `useRouteFileLoader`, which the route host wires together with `useResourceEditor` for its existing behaviour. The embed host receives `resource` + `space` as props, auto-resolves a matching `resourceEditor` extension from the registry via the new `resolveResourceEditor` helper (extension/mimeType with `family/*` glob support, optional `matches()` predicate, `hasPriority` tie-break), and also accepts an explicit `extension` / `extensionId` override. A `readOnly` prop forces preview-only mounts; the host exposes its editor state via slot bindings and `defineExpose` for parents that drive their own toolbar/modal chrome. To make external read-only control actually work, `useTextEditor.readonly` now accepts a `MaybeRefOrGetter<boolean>` and tracks it via a computed. The local writable ref and its sync watcher are gone. ResourceEditorExtension regains the discriminator metadata that Phase A had stripped (`extensions`, `mimeTypes`, `matches`, `hasPriority`); the host consumes them for auto-resolution. The `useFileActions` integration keeps going through `appInfo.extensions[]` for now, collapsing that duplication is a follow-up.
f56de8b to
7f61723
Compare
Three issues from a thorough multi-reviewer pass on the branch:
1. `ResourceEditorHost` constructed `useResourceEditor` unconditionally
with `extension: () => resolvedExtension.value!`. When no extension
matched (e.g. a resource the registry can't handle), the composable
read `unref(extensionRef).appId` and crashed before the template
could render the `#no-editor` slot. Split the host into an outer
shell that gates on `resolvedExtension` and an inner
`ResourceEditorMount` that runs the composable only once an
extension is resolved. Mount stays internal to the package.
2. `web-app-epub-reader` wrapped its App in `defineAsyncComponent` to
defer the epubjs bundle. The wrapper has no `props`/`emits` until
the chunk resolves, so the composable's capability introspection
(`hasProp('currentContent')`) returned `false` and content was
never loaded. App.vue is now statically imported and the epubjs
dependency itself is dynamically imported inside App.vue's setup,
keeping the heavy lib deferred while exposing the component's
props/emits to the host. Test helper exports `flushPromises` so
the existing spec can wait for the dynamic import.
3. `useResourceEditor`'s `fileSizeLimit` modal was dispatched whenever
`resource` or `space` changed. Preview's photo-roll re-fires that
watcher on every active-file swap, so the modal popped on every
click. Track the last resource id we prompted for and only
re-dispatch on actual transitions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a new
resourceEditorextension type that lets file viewersand editors register themselves typed against
ResourceEditorBindings,and ships a working embed host so the same editor component is mountable
both behind a route and inside a modal / sidebar / mail-widget / sharing
dialog.
Replaces the previous
AppWrapper+AppWrapperRoutepair with fourclearer building blocks:
useResourceEditor: resource-agnostic composable. Takes resourceurl/currentContentbased on what the editor component declares), and the save / dirty /
autosave flow for editors.
useRouteFileLoader: encapsulates the route-bound resourceresolution (driveAliasAndItem + fileId, share-space reconstruction,
import-with-extension copy) that the route host needs.
ResourceEditorRouteHost: drop-in replacement for the old routemount: TopBar, FileSideBar, Ctrl+S, onBeforeRouteLeave guard.
ResourceEditorHost: embed host. Acceptsresource+spaceasprops, auto-resolves the matching extension from the registry (or takes
an explicit
extension/extensionId), supports areadOnlyprop andexposes editor state via slot bindings and
defineExpose.AppWrapper.vueandAppWrapperRouteremain as@deprecatedshims thatsynthesise a
ResourceEditorExtensionfrom the legacy props, soweb-app-externaland the out-of-repoweb-extensionskeep working.Apps migrated to the new path:
web-app-pdf-viewer,web-app-preview,web-app-text-editor,web-app-epub-reader.Side note:
useTextEditor.readonlyTo make external read-only control work for the embed host's preview /
edit toggle,
useTextEditornow acceptsreadonlyasMaybeRefOrGetter<boolean>and tracks it via a computed. The exposedTextEditorInstance.readonlyis now aComputedRef<boolean>, no in-treecaller wrote to it.