@aphexcms/cms-core 9.4.0 → 9.5.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.
- package/dist/admin/block-previews.svelte.d.ts +46 -0
- package/dist/admin/block-previews.svelte.d.ts.map +1 -0
- package/dist/admin/block-previews.svelte.js +26 -0
- package/dist/admin/field-components.svelte.d.ts +16 -0
- package/dist/admin/field-components.svelte.d.ts.map +1 -0
- package/dist/admin/field-components.svelte.js +18 -0
- package/dist/admin/nav.svelte.d.ts +34 -0
- package/dist/admin/nav.svelte.d.ts.map +1 -0
- package/dist/admin/nav.svelte.js +78 -0
- package/dist/admin/slots.svelte.d.ts +50 -0
- package/dist/admin/slots.svelte.d.ts.map +1 -0
- package/dist/admin/slots.svelte.js +67 -0
- package/dist/admin/types.d.ts +7 -0
- package/dist/admin/types.d.ts.map +1 -0
- package/dist/admin/types.js +1 -0
- package/dist/api/schemas/api-keys.d.ts +4 -37
- package/dist/api/schemas/api-keys.d.ts.map +1 -1
- package/dist/api/schemas/api-keys.js +5 -2
- package/dist/api/schemas/assets.d.ts +4 -0
- package/dist/api/schemas/assets.d.ts.map +1 -1
- package/dist/api/schemas/assets.js +3 -0
- package/dist/api/schemas/plugin-settings.d.ts +6 -0
- package/dist/api/schemas/plugin-settings.d.ts.map +1 -0
- package/dist/api/schemas/plugin-settings.js +8 -0
- package/dist/api/schemas/roles.d.ts +7 -56
- package/dist/api/schemas/roles.d.ts.map +1 -1
- package/dist/api/schemas/roles.js +11 -6
- package/dist/api/schemas/user.d.ts +2 -1
- package/dist/api/schemas/user.d.ts.map +1 -1
- package/dist/api/schemas/user.js +7 -2
- package/dist/app-augment.d.ts +11 -0
- package/dist/app-augment.d.ts.map +1 -1
- package/dist/app.d.ts +2 -0
- package/dist/auth/provider.d.ts +3 -0
- package/dist/auth/provider.d.ts.map +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +9 -0
- package/dist/components/AdminApp.svelte +259 -113
- package/dist/components/AdminApp.svelte.d.ts +18 -2
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/AdminSlot.svelte +18 -0
- package/dist/components/admin/AdminSlot.svelte.d.ts +12 -0
- package/dist/components/admin/AdminSlot.svelte.d.ts.map +1 -0
- package/dist/components/admin/DocumentEditor.svelte +646 -385
- package/dist/components/admin/DocumentEditor.svelte.d.ts +13 -0
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/MediaBrowser.svelte +13 -3
- package/dist/components/admin/MediaBrowser.svelte.d.ts.map +1 -1
- package/dist/components/admin/PluginSettingsPanel.svelte +231 -0
- package/dist/components/admin/PluginSettingsPanel.svelte.d.ts +8 -0
- package/dist/components/admin/PluginSettingsPanel.svelte.d.ts.map +1 -0
- package/dist/components/admin/SchemaField.svelte +76 -95
- package/dist/components/admin/SchemaField.svelte.d.ts +7 -0
- package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +24 -2
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/FieldInput.svelte +109 -0
- package/dist/components/admin/fields/FieldInput.svelte.d.ts +17 -0
- package/dist/components/admin/fields/FieldInput.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte +5 -12
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts +2 -9
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte +18 -17
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts +2 -9
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/RichtextField.svelte +55 -2
- package/dist/components/admin/fields/richtext/RichtextField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts +7 -0
- package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/portable-text-object-node.js +4 -2
- package/dist/components/admin/fields/richtext/svelte-node-view.d.ts +16 -1
- package/dist/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/svelte-node-view.js +20 -1
- package/dist/components/layout/OrganizationSwitcher.svelte +31 -10
- package/dist/components/layout/OrganizationSwitcher.svelte.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.svelte +49 -11
- package/dist/components/layout/Sidebar.svelte.d.ts +5 -1
- package/dist/components/layout/Sidebar.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/AppSidebar.svelte +66 -3
- package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +5 -0
- package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/NavMain.svelte +3 -2
- package/dist/components/layout/sidebar/NavMain.svelte.d.ts +4 -3
- package/dist/components/layout/sidebar/NavMain.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/NavUser.svelte +5 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -0
- package/dist/db/interfaces/asset.d.ts +1 -0
- package/dist/db/interfaces/asset.d.ts.map +1 -1
- package/dist/db/interfaces/index.d.ts +3 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/plugin-settings.d.ts +23 -0
- package/dist/db/interfaces/plugin-settings.d.ts.map +1 -0
- package/dist/db/interfaces/plugin-settings.js +13 -0
- package/dist/db/interfaces/role.d.ts +14 -5
- package/dist/db/interfaces/role.d.ts.map +1 -1
- package/dist/engine.d.ts +28 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +44 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +59 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/lib/admin/block-previews.svelte.d.ts +46 -0
- package/dist/lib/admin/block-previews.svelte.d.ts.map +1 -0
- package/dist/lib/admin/block-previews.svelte.js +27 -0
- package/dist/lib/admin/block-previews.svelte.js.map +1 -0
- package/dist/lib/admin/field-components.svelte.d.ts +16 -0
- package/dist/lib/admin/field-components.svelte.d.ts.map +1 -0
- package/dist/lib/admin/field-components.svelte.js +19 -0
- package/dist/lib/admin/field-components.svelte.js.map +1 -0
- package/dist/lib/admin/nav.svelte.d.ts +34 -0
- package/dist/lib/admin/nav.svelte.d.ts.map +1 -0
- package/dist/lib/admin/nav.svelte.js +79 -0
- package/dist/lib/admin/nav.svelte.js.map +1 -0
- package/dist/lib/admin/slots.svelte.d.ts +50 -0
- package/dist/lib/admin/slots.svelte.d.ts.map +1 -0
- package/dist/lib/admin/slots.svelte.js +68 -0
- package/dist/lib/admin/slots.svelte.js.map +1 -0
- package/dist/lib/admin/types.d.ts +7 -0
- package/dist/lib/admin/types.d.ts.map +1 -0
- package/dist/lib/admin/types.js +2 -0
- package/dist/lib/admin/types.js.map +1 -0
- package/dist/lib/api/schemas/api-keys.d.ts +4 -37
- package/dist/lib/api/schemas/api-keys.d.ts.map +1 -1
- package/dist/lib/api/schemas/api-keys.js +5 -2
- package/dist/lib/api/schemas/api-keys.js.map +1 -1
- package/dist/lib/api/schemas/assets.d.ts +4 -0
- package/dist/lib/api/schemas/assets.d.ts.map +1 -1
- package/dist/lib/api/schemas/assets.js +3 -0
- package/dist/lib/api/schemas/assets.js.map +1 -1
- package/dist/lib/api/schemas/plugin-settings.d.ts +6 -0
- package/dist/lib/api/schemas/plugin-settings.d.ts.map +1 -0
- package/dist/lib/api/schemas/plugin-settings.js +9 -0
- package/dist/lib/api/schemas/plugin-settings.js.map +1 -0
- package/dist/lib/api/schemas/roles.d.ts +7 -56
- package/dist/lib/api/schemas/roles.d.ts.map +1 -1
- package/dist/lib/api/schemas/roles.js +11 -6
- package/dist/lib/api/schemas/roles.js.map +1 -1
- package/dist/lib/api/schemas/user.d.ts +2 -1
- package/dist/lib/api/schemas/user.d.ts.map +1 -1
- package/dist/lib/api/schemas/user.js +7 -2
- package/dist/lib/api/schemas/user.js.map +1 -1
- package/dist/lib/app-augment.d.ts +11 -0
- package/dist/lib/app-augment.d.ts.map +1 -1
- package/dist/lib/auth/provider.d.ts +3 -0
- package/dist/lib/auth/provider.d.ts.map +1 -1
- package/dist/lib/client/index.d.ts +6 -0
- package/dist/lib/client/index.d.ts.map +1 -1
- package/dist/lib/client/index.js +9 -0
- package/dist/lib/client/index.js.map +1 -1
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts +7 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -1
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js +4 -2
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js.map +1 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts +16 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js +20 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +11 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/db/interfaces/asset.d.ts +1 -0
- package/dist/lib/db/interfaces/asset.d.ts.map +1 -1
- package/dist/lib/db/interfaces/index.d.ts +3 -1
- package/dist/lib/db/interfaces/index.d.ts.map +1 -1
- package/dist/lib/db/interfaces/plugin-settings.d.ts +23 -0
- package/dist/lib/db/interfaces/plugin-settings.d.ts.map +1 -0
- package/dist/lib/db/interfaces/plugin-settings.js +14 -0
- package/dist/lib/db/interfaces/plugin-settings.js.map +1 -0
- package/dist/lib/db/interfaces/role.d.ts +14 -5
- package/dist/lib/db/interfaces/role.d.ts.map +1 -1
- package/dist/lib/engine.d.ts +28 -0
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/engine.js +44 -0
- package/dist/lib/engine.js.map +1 -1
- package/dist/lib/hooks.d.ts +6 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +59 -2
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +5 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +4 -2
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/local-api/types.d.ts +8 -0
- package/dist/lib/local-api/types.d.ts.map +1 -1
- package/dist/lib/mcp/tools.d.ts.map +1 -1
- package/dist/lib/mcp/tools.js +69 -1
- package/dist/lib/mcp/tools.js.map +1 -1
- package/dist/lib/permissions-context.svelte.d.ts +7 -4
- package/dist/lib/permissions-context.svelte.d.ts.map +1 -1
- package/dist/lib/permissions-context.svelte.js.map +1 -1
- package/dist/lib/plugins/index.d.ts +3 -0
- package/dist/lib/plugins/index.d.ts.map +1 -0
- package/dist/lib/plugins/index.js +3 -0
- package/dist/lib/plugins/index.js.map +1 -0
- package/dist/lib/plugins/resolver.d.ts +49 -0
- package/dist/lib/plugins/resolver.d.ts.map +1 -0
- package/dist/lib/plugins/resolver.js +66 -0
- package/dist/lib/plugins/resolver.js.map +1 -0
- package/dist/lib/plugins/types.d.ts +233 -0
- package/dist/lib/plugins/types.d.ts.map +1 -0
- package/dist/lib/plugins/types.js +9 -0
- package/dist/lib/plugins/types.js.map +1 -0
- package/dist/lib/schema-utils/desugar.d.ts +27 -0
- package/dist/lib/schema-utils/desugar.d.ts.map +1 -0
- package/dist/lib/schema-utils/desugar.js +102 -0
- package/dist/lib/schema-utils/desugar.js.map +1 -0
- package/dist/lib/schema-utils/index.d.ts +2 -0
- package/dist/lib/schema-utils/index.d.ts.map +1 -1
- package/dist/lib/schema-utils/index.js +2 -0
- package/dist/lib/schema-utils/index.js.map +1 -1
- package/dist/lib/schema-utils/settings.d.ts +17 -0
- package/dist/lib/schema-utils/settings.d.ts.map +1 -0
- package/dist/lib/schema-utils/settings.js +23 -0
- package/dist/lib/schema-utils/settings.js.map +1 -0
- package/dist/lib/schema-utils/validator.d.ts.map +1 -1
- package/dist/lib/schema-utils/validator.js +11 -0
- package/dist/lib/schema-utils/validator.js.map +1 -1
- package/dist/lib/security/secret-crypto.d.ts +18 -0
- package/dist/lib/security/secret-crypto.d.ts.map +1 -0
- package/dist/lib/security/secret-crypto.js +71 -0
- package/dist/lib/security/secret-crypto.js.map +1 -0
- package/dist/lib/server/api/index.d.ts.map +1 -1
- package/dist/lib/server/api/index.js +3 -0
- package/dist/lib/server/api/index.js.map +1 -1
- package/dist/lib/server/api/routes/assets.d.ts.map +1 -1
- package/dist/lib/server/api/routes/assets.js +8 -2
- package/dist/lib/server/api/routes/assets.js.map +1 -1
- package/dist/lib/server/api/routes/organizations.d.ts.map +1 -1
- package/dist/lib/server/api/routes/organizations.js +4 -1
- package/dist/lib/server/api/routes/organizations.js.map +1 -1
- package/dist/lib/server/api/routes/plugin-settings.d.ts +4 -0
- package/dist/lib/server/api/routes/plugin-settings.d.ts.map +1 -0
- package/dist/lib/server/api/routes/plugin-settings.js +109 -0
- package/dist/lib/server/api/routes/plugin-settings.js.map +1 -0
- package/dist/lib/server/api/routes/roles.d.ts.map +1 -1
- package/dist/lib/server/api/routes/roles.js +37 -0
- package/dist/lib/server/api/routes/roles.js.map +1 -1
- package/dist/lib/server/api/routes/user.d.ts.map +1 -1
- package/dist/lib/server/api/routes/user.js +14 -3
- package/dist/lib/server/api/routes/user.js.map +1 -1
- package/dist/lib/services/asset-service.d.ts +1 -0
- package/dist/lib/services/asset-service.d.ts.map +1 -1
- package/dist/lib/services/asset-service.js.map +1 -1
- package/dist/lib/services/index.d.ts +2 -0
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +1 -0
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/services/plugin-settings-service.d.ts +76 -0
- package/dist/lib/services/plugin-settings-service.d.ts.map +1 -0
- package/dist/lib/services/plugin-settings-service.js +236 -0
- package/dist/lib/services/plugin-settings-service.js.map +1 -0
- package/dist/lib/services/roles-service.d.ts +2 -2
- package/dist/lib/services/roles-service.d.ts.map +1 -1
- package/dist/lib/services/roles-service.js.map +1 -1
- package/dist/lib/theme/css.d.ts +30 -0
- package/dist/lib/theme/css.d.ts.map +1 -0
- package/dist/lib/theme/css.js +54 -0
- package/dist/lib/theme/css.js.map +1 -0
- package/dist/lib/theme/derive.d.ts +27 -0
- package/dist/lib/theme/derive.d.ts.map +1 -0
- package/dist/lib/theme/derive.js +108 -0
- package/dist/lib/theme/derive.js.map +1 -0
- package/dist/lib/theme/index.d.ts +5 -0
- package/dist/lib/theme/index.d.ts.map +1 -0
- package/dist/lib/theme/index.js +5 -0
- package/dist/lib/theme/index.js.map +1 -0
- package/dist/lib/theme/schemes.d.ts +60 -0
- package/dist/lib/theme/schemes.d.ts.map +1 -0
- package/dist/lib/theme/schemes.js +150 -0
- package/dist/lib/theme/schemes.js.map +1 -0
- package/dist/lib/theme/tokens.d.ts +57 -0
- package/dist/lib/theme/tokens.d.ts.map +1 -0
- package/dist/lib/theme/tokens.js +11 -0
- package/dist/lib/theme/tokens.js.map +1 -0
- package/dist/{cli/generate-types.d.ts → lib/type-gen.d.ts} +3 -6
- package/dist/lib/type-gen.d.ts.map +1 -0
- package/dist/lib/type-gen.js +658 -0
- package/dist/lib/type-gen.js.map +1 -0
- package/dist/lib/types/auth.d.ts +10 -10
- package/dist/lib/types/auth.d.ts.map +1 -1
- package/dist/lib/types/capabilities.d.ts +50 -9
- package/dist/lib/types/capabilities.d.ts.map +1 -1
- package/dist/lib/types/capabilities.js +147 -5
- package/dist/lib/types/capabilities.js.map +1 -1
- package/dist/lib/types/config.d.ts +30 -1
- package/dist/lib/types/config.d.ts.map +1 -1
- package/dist/lib/types/schemas.d.ts +90 -2
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/types/schemas.js.map +1 -1
- package/dist/lib/types/sidebar.d.ts +3 -1
- package/dist/lib/types/sidebar.d.ts.map +1 -1
- package/dist/lib/utils/index.d.ts +1 -0
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +1 -0
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/lib/utils/preview.d.ts +16 -5
- package/dist/lib/utils/preview.d.ts.map +1 -1
- package/dist/lib/utils/preview.js +7 -2
- package/dist/lib/utils/preview.js.map +1 -1
- package/dist/lib/vite/index.d.ts +7 -0
- package/dist/lib/vite/index.d.ts.map +1 -1
- package/dist/lib/vite/index.js +37 -6
- package/dist/lib/vite/index.js.map +1 -1
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +4 -2
- package/dist/local-api/types.d.ts +8 -0
- package/dist/local-api/types.d.ts.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +69 -1
- package/dist/permissions-context.svelte.d.ts +7 -4
- package/dist/permissions-context.svelte.d.ts.map +1 -1
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +2 -0
- package/dist/plugins/resolver.d.ts +49 -0
- package/dist/plugins/resolver.d.ts.map +1 -0
- package/dist/plugins/resolver.js +65 -0
- package/dist/plugins/types.d.ts +233 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +8 -0
- package/dist/schema-utils/desugar.d.ts +27 -0
- package/dist/schema-utils/desugar.d.ts.map +1 -0
- package/dist/schema-utils/desugar.js +101 -0
- package/dist/schema-utils/index.d.ts +2 -0
- package/dist/schema-utils/index.d.ts.map +1 -1
- package/dist/schema-utils/index.js +2 -0
- package/dist/schema-utils/settings.d.ts +17 -0
- package/dist/schema-utils/settings.d.ts.map +1 -0
- package/dist/schema-utils/settings.js +22 -0
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +11 -0
- package/dist/security/secret-crypto.d.ts +18 -0
- package/dist/security/secret-crypto.d.ts.map +1 -0
- package/dist/security/secret-crypto.js +70 -0
- package/dist/server/api/index.d.ts.map +1 -1
- package/dist/server/api/index.js +3 -0
- package/dist/server/api/routes/assets.d.ts.map +1 -1
- package/dist/server/api/routes/assets.js +8 -2
- package/dist/server/api/routes/organizations.d.ts.map +1 -1
- package/dist/server/api/routes/organizations.js +4 -1
- package/dist/server/api/routes/plugin-settings.d.ts +4 -0
- package/dist/server/api/routes/plugin-settings.d.ts.map +1 -0
- package/dist/server/api/routes/plugin-settings.js +108 -0
- package/dist/server/api/routes/roles.d.ts.map +1 -1
- package/dist/server/api/routes/roles.js +37 -0
- package/dist/server/api/routes/user.d.ts.map +1 -1
- package/dist/server/api/routes/user.js +14 -3
- package/dist/services/asset-service.d.ts +1 -0
- package/dist/services/asset-service.d.ts.map +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/plugin-settings-service.d.ts +76 -0
- package/dist/services/plugin-settings-service.d.ts.map +1 -0
- package/dist/services/plugin-settings-service.js +235 -0
- package/dist/services/roles-service.d.ts +2 -2
- package/dist/services/roles-service.d.ts.map +1 -1
- package/dist/theme/css.d.ts +30 -0
- package/dist/theme/css.d.ts.map +1 -0
- package/dist/theme/css.js +53 -0
- package/dist/theme/derive.d.ts +27 -0
- package/dist/theme/derive.d.ts.map +1 -0
- package/dist/theme/derive.js +107 -0
- package/dist/theme/index.d.ts +5 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +4 -0
- package/dist/theme/schemes.d.ts +60 -0
- package/dist/theme/schemes.d.ts.map +1 -0
- package/dist/theme/schemes.js +149 -0
- package/dist/theme/tokens.d.ts +57 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/dist/theme/tokens.js +10 -0
- package/dist/type-gen.d.ts +19 -0
- package/dist/type-gen.d.ts.map +1 -0
- package/dist/{cli/generate-types.js → type-gen.js} +101 -68
- package/dist/types/auth.d.ts +10 -10
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/capabilities.d.ts +50 -9
- package/dist/types/capabilities.d.ts.map +1 -1
- package/dist/types/capabilities.js +147 -5
- package/dist/types/config.d.ts +30 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +90 -2
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/sidebar.d.ts +3 -1
- package/dist/types/sidebar.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/preview.d.ts +16 -5
- package/dist/utils/preview.d.ts.map +1 -1
- package/dist/utils/preview.js +7 -2
- package/dist/vite/index.d.ts +7 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +37 -6
- package/package.json +6 -4
- package/dist/cli/generate-types.d.ts.map +0 -1
- package/dist/cli/generate-types.js.map +0 -1
|
@@ -13,10 +13,14 @@
|
|
|
13
13
|
import { setSchemaContext } from '../../schema-context.svelte';
|
|
14
14
|
import { setSaveStateContext } from '../../save-state-context.svelte';
|
|
15
15
|
import { usePermissions } from '../../permissions-context.svelte';
|
|
16
|
+
import AdminSlot from './AdminSlot.svelte';
|
|
17
|
+
import { createPartResolver } from '../../plugins/resolver';
|
|
18
|
+
import type { CMSPlugin, DocumentActionProps } from '../../plugins/types';
|
|
16
19
|
import { getDefaultValueForFieldType } from '../../utils/field-defaults';
|
|
17
20
|
import { notifyDocumentChanged } from '../../document-refresh.svelte';
|
|
18
21
|
import { collectReferenceIds } from '../../utils/reference-walk';
|
|
19
22
|
import { cmsLogger } from '../../utils/logger';
|
|
23
|
+
import { resolvePreviewTitle } from '../../utils/preview';
|
|
20
24
|
import { toast } from 'svelte-sonner';
|
|
21
25
|
import { confirmDialog } from './confirm-dialog/confirm-dialog.svelte';
|
|
22
26
|
import {
|
|
@@ -28,7 +32,12 @@
|
|
|
28
32
|
Minimize2,
|
|
29
33
|
Monitor,
|
|
30
34
|
RefreshCw,
|
|
31
|
-
ExternalLink
|
|
35
|
+
ExternalLink,
|
|
36
|
+
Tablet,
|
|
37
|
+
Smartphone,
|
|
38
|
+
ZoomIn,
|
|
39
|
+
ZoomOut,
|
|
40
|
+
ArrowLeft
|
|
32
41
|
} from '@lucide/svelte';
|
|
33
42
|
import { stegaEncodeDocument } from '../../preview/stega.js';
|
|
34
43
|
import { collectAssetRefs, injectAssetData, type ResolvedAsset } from '../../preview/assets.js';
|
|
@@ -40,6 +49,10 @@
|
|
|
40
49
|
documentId?: string | null;
|
|
41
50
|
isCreating: boolean;
|
|
42
51
|
onBack: () => void;
|
|
52
|
+
/** When set, the close control renders as a labelled "← {backLabel}" button
|
|
53
|
+
* instead of a bare close (✕) icon — used for reference editors so the action
|
|
54
|
+
* reads as "go back" rather than "close". */
|
|
55
|
+
backLabel?: string;
|
|
43
56
|
onSaved?: (documentId: string) => void;
|
|
44
57
|
onAutoSaved?: (documentId: string, title: string) => void;
|
|
45
58
|
onDeleted?: () => void;
|
|
@@ -61,10 +74,18 @@
|
|
|
61
74
|
onToggleFocus?: () => void;
|
|
62
75
|
/** When true, split the editor with a live preview iframe on the right. */
|
|
63
76
|
presentationMode?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Bump this to ask the preview iframe to re-fetch its server-loaded data
|
|
79
|
+
* (`aphex:refresh` → `invalidateAll` in the overlay). Used when a *different*
|
|
80
|
+
* document — e.g. one this page renders in a list — was edited elsewhere.
|
|
81
|
+
*/
|
|
82
|
+
refreshToken?: number;
|
|
64
83
|
/** Toggle host-driven presentation mode. Omit to hide the button entirely. */
|
|
65
84
|
onTogglePresentation?: () => void;
|
|
66
85
|
/** Organization ID from the host context — used as fallback for new docs that haven't been saved yet. */
|
|
67
86
|
organizationId?: string | null;
|
|
87
|
+
/** Build-time plugins; their document-action parts render in the toolbar. */
|
|
88
|
+
plugins?: CMSPlugin[];
|
|
68
89
|
}
|
|
69
90
|
|
|
70
91
|
let {
|
|
@@ -73,6 +94,7 @@
|
|
|
73
94
|
documentId,
|
|
74
95
|
isCreating,
|
|
75
96
|
onBack,
|
|
97
|
+
backLabel,
|
|
76
98
|
onSaved,
|
|
77
99
|
onAutoSaved,
|
|
78
100
|
onDeleted,
|
|
@@ -86,8 +108,10 @@
|
|
|
86
108
|
focusMode = false,
|
|
87
109
|
onToggleFocus,
|
|
88
110
|
presentationMode = false,
|
|
111
|
+
refreshToken = 0,
|
|
89
112
|
onTogglePresentation,
|
|
90
|
-
organizationId = null
|
|
113
|
+
organizationId = null,
|
|
114
|
+
plugins = []
|
|
91
115
|
}: Props = $props();
|
|
92
116
|
|
|
93
117
|
// Set schema context for child components (ArrayField, etc.)
|
|
@@ -136,6 +160,44 @@
|
|
|
136
160
|
let publishedData = $state<Record<string, any> | null>(null);
|
|
137
161
|
const isViewingPublished = $derived(perspective === 'published');
|
|
138
162
|
|
|
163
|
+
// Plugin document actions applicable to this type. Capability-gated, with
|
|
164
|
+
// built-in privileged roles bypassing. Cheap to rebuild from the plugin list.
|
|
165
|
+
const pluginDocumentActions = $derived(
|
|
166
|
+
createPartResolver(plugins).documentActions({
|
|
167
|
+
schemaName: documentType,
|
|
168
|
+
capabilities: [...perms.capabilities],
|
|
169
|
+
overrideAccess: perms.role === 'super_admin' || perms.role === 'admin'
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// A minimal, stable context handed to each document-action component —
|
|
174
|
+
// deliberately decoupled from editor internals so plugins never touch them.
|
|
175
|
+
const documentActionContext = $derived<DocumentActionProps | null>(
|
|
176
|
+
schema
|
|
177
|
+
? {
|
|
178
|
+
documentId: documentId ?? fullDocument?.id ?? null,
|
|
179
|
+
schema,
|
|
180
|
+
data: documentData,
|
|
181
|
+
status: !documentId
|
|
182
|
+
? 'new'
|
|
183
|
+
: fullDocument?._meta?.status === 'published'
|
|
184
|
+
? 'published'
|
|
185
|
+
: fullDocument?._meta?.status === 'unpublished'
|
|
186
|
+
? 'unpublished'
|
|
187
|
+
: 'draft',
|
|
188
|
+
updateData: (patch: Record<string, unknown>) => {
|
|
189
|
+
documentData = { ...documentData, ...patch };
|
|
190
|
+
},
|
|
191
|
+
save: async () => {
|
|
192
|
+
await saveDocument();
|
|
193
|
+
},
|
|
194
|
+
publish: async () => {
|
|
195
|
+
await publishDocument();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
: null
|
|
199
|
+
);
|
|
200
|
+
|
|
139
201
|
// Field-level access: mirrors the server's strip/drop logic so the form
|
|
140
202
|
// only shows fields the user can see and disables fields they can't write.
|
|
141
203
|
function fieldRoleAllowed(list: string[] | undefined): boolean {
|
|
@@ -169,6 +231,31 @@
|
|
|
169
231
|
let fieldsWidth = $state(500);
|
|
170
232
|
let isResizing = $state(false);
|
|
171
233
|
|
|
234
|
+
// Preview canvas — a viewport width and a zoom level so the preview reads like
|
|
235
|
+
// a real device canvas rather than a cramped pane.
|
|
236
|
+
let previewViewport = $state<'desktop' | 'tablet' | 'mobile'>('desktop');
|
|
237
|
+
let previewZoom = $state(1);
|
|
238
|
+
const VIEWPORT_WIDTH: Record<'desktop' | 'tablet' | 'mobile', number> = {
|
|
239
|
+
desktop: 0, // 0 = fill available width
|
|
240
|
+
tablet: 768,
|
|
241
|
+
mobile: 390
|
|
242
|
+
};
|
|
243
|
+
function zoomBy(delta: number) {
|
|
244
|
+
previewZoom = Math.round(Math.min(1.5, Math.max(0.5, previewZoom + delta)) * 100) / 100;
|
|
245
|
+
}
|
|
246
|
+
// Scale-compensated frame sizing: at desktop the iframe fills (up-sized then
|
|
247
|
+
// scaled so content stays crisp); device modes get a fixed centered width.
|
|
248
|
+
const frameStyle = $derived.by(() => {
|
|
249
|
+
const z = previewZoom;
|
|
250
|
+
if (previewViewport === 'desktop') {
|
|
251
|
+
if (z === 1) return '';
|
|
252
|
+
return `width:${100 / z}%;height:${100 / z}%;transform:scale(${z});transform-origin:top left;`;
|
|
253
|
+
}
|
|
254
|
+
const w = VIEWPORT_WIDTH[previewViewport];
|
|
255
|
+
const h = z === 1 ? '100%' : `${100 / z}%`;
|
|
256
|
+
return `width:${w}px;height:${h};transform:scale(${z});transform-origin:top center;`;
|
|
257
|
+
});
|
|
258
|
+
|
|
172
259
|
function refreshPreview() {
|
|
173
260
|
if (!iframeRef || !iframeUrl) return;
|
|
174
261
|
iframeRef.src = iframeUrl;
|
|
@@ -210,6 +297,8 @@
|
|
|
210
297
|
win.postMessage(
|
|
211
298
|
{
|
|
212
299
|
type: 'aphex:data',
|
|
300
|
+
documentType,
|
|
301
|
+
documentId: documentId ?? undefined,
|
|
213
302
|
document: iframeStega ? stegaEncodeDocument(snapshot, schema?.fields ?? []) : snapshot
|
|
214
303
|
},
|
|
215
304
|
'*'
|
|
@@ -226,7 +315,7 @@
|
|
|
226
315
|
const startX = e.clientX;
|
|
227
316
|
const startWidth = fieldsWidth;
|
|
228
317
|
function onMove(ev: MouseEvent) {
|
|
229
|
-
fieldsWidth = Math.max(500, Math.min(
|
|
318
|
+
fieldsWidth = Math.max(500, Math.min(720, startWidth + (ev.clientX - startX)));
|
|
230
319
|
}
|
|
231
320
|
function onUp() {
|
|
232
321
|
isResizing = false;
|
|
@@ -269,6 +358,11 @@
|
|
|
269
358
|
$effect(() => {
|
|
270
359
|
if (!presentationMode) return;
|
|
271
360
|
const handler = (e: MessageEvent) => {
|
|
361
|
+
// Only handle messages from *this* editor's own preview iframe. Presentation
|
|
362
|
+
// editors can be nested (a base editor + a reference modal), and both listen
|
|
363
|
+
// on window — without this guard a click in one preview would also fire the
|
|
364
|
+
// other's handler (and could open a stray reference).
|
|
365
|
+
if (iframeRef?.contentWindow && e.source !== iframeRef.contentWindow) return;
|
|
272
366
|
if (!e.data || typeof e.data !== 'object') return;
|
|
273
367
|
const msg = e.data as {
|
|
274
368
|
type: string;
|
|
@@ -278,6 +372,8 @@
|
|
|
278
372
|
arrayIndex?: number;
|
|
279
373
|
objectPath?: string;
|
|
280
374
|
linkHref?: string;
|
|
375
|
+
documentId?: string;
|
|
376
|
+
documentType?: string;
|
|
281
377
|
};
|
|
282
378
|
if (msg.type === 'aphex:ready') {
|
|
283
379
|
iframeStega = (msg as any).stega !== false;
|
|
@@ -286,6 +382,14 @@
|
|
|
286
382
|
iframeRef?.contentWindow?.postMessage({ type: 'aphex:edit-mode', enabled: false }, '*');
|
|
287
383
|
}
|
|
288
384
|
} else if (msg.type === 'aphex:field-click' && msg.fieldPath) {
|
|
385
|
+
// Cross-document click: the rendered element belongs to a *different*
|
|
386
|
+
// document than the one being edited (e.g. an app-level "list of posts"
|
|
387
|
+
// block whose cards aren't a stored reference). Open that document rather
|
|
388
|
+
// than trying to resolve the field in the current one.
|
|
389
|
+
if (msg.documentId && msg.documentType && msg.documentId !== documentId) {
|
|
390
|
+
onOpenReference?.(msg.documentId, msg.documentType);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
289
393
|
focusFieldByName(msg.fieldPath, {
|
|
290
394
|
blockIndex: msg.blockIndex,
|
|
291
395
|
blockKey: msg.blockKey,
|
|
@@ -309,6 +413,14 @@
|
|
|
309
413
|
return () => clearTimeout(timer);
|
|
310
414
|
});
|
|
311
415
|
|
|
416
|
+
// Host bumped refreshToken (a different document changed) — ask the preview to
|
|
417
|
+
// re-fetch its server-loaded data. The overlay maps this to `invalidateAll()`.
|
|
418
|
+
$effect(() => {
|
|
419
|
+
if (refreshToken === 0) return; // initial value — nothing to refresh yet
|
|
420
|
+
if (!presentationMode || !iframeRef?.contentWindow) return;
|
|
421
|
+
iframeRef.contentWindow.postMessage({ type: 'aphex:refresh' }, '*');
|
|
422
|
+
});
|
|
423
|
+
|
|
312
424
|
/**
|
|
313
425
|
* Map a portable text array index to the absolute ProseMirror cursor position (end of content)
|
|
314
426
|
* for that block in the TipTap editor. Accounts for list grouping (multiple PT list-item blocks
|
|
@@ -420,7 +532,14 @@
|
|
|
420
532
|
await tick();
|
|
421
533
|
const container = document.querySelector<HTMLElement>(`[data-field-name="${fieldName}"]`);
|
|
422
534
|
if (!container) return;
|
|
423
|
-
|
|
535
|
+
// For rich-text caret navigation, scroll the wrapper instantly to 'nearest' so it
|
|
536
|
+
// doesn't smooth-animate the (possibly tall) field back to centre and shove the
|
|
537
|
+
// caret off-screen — the editor's own scrollIntoView positions the caret precisely.
|
|
538
|
+
const isRichtextNav = opts.blockIndex != null || !!opts.blockKey || !!opts.linkHref;
|
|
539
|
+
container.scrollIntoView({
|
|
540
|
+
behavior: isRichtextNav ? 'auto' : 'smooth',
|
|
541
|
+
block: isRichtextNav ? 'nearest' : 'center'
|
|
542
|
+
});
|
|
424
543
|
setTimeout(() => {
|
|
425
544
|
if (focusedFieldName === fieldName) focusedFieldName = null;
|
|
426
545
|
}, 2000);
|
|
@@ -449,8 +568,9 @@
|
|
|
449
568
|
return true;
|
|
450
569
|
});
|
|
451
570
|
if (linkPos !== null) {
|
|
452
|
-
|
|
453
|
-
|
|
571
|
+
// Chain focus → select → scrollIntoView so the caret is scrolled to the
|
|
572
|
+
// actual selection, not left off-screen when the field scrolled to center.
|
|
573
|
+
richtextEditor.chain().focus().setTextSelection(linkPos).scrollIntoView().run();
|
|
454
574
|
richtextHandle?.openLinkPopover();
|
|
455
575
|
return;
|
|
456
576
|
}
|
|
@@ -489,8 +609,9 @@
|
|
|
489
609
|
if (Array.isArray(ptBlocks)) {
|
|
490
610
|
const cursorPos = findTiptapCursorForPTBlock(ptBlocks, blockIndex!, doc);
|
|
491
611
|
if (cursorPos !== null) {
|
|
492
|
-
|
|
493
|
-
|
|
612
|
+
// Scroll to the caret itself — the field-level center scroll above can
|
|
613
|
+
// leave the actual cursor position off-screen in a long block.
|
|
614
|
+
richtextEditor.chain().focus().setTextSelection(cursorPos).scrollIntoView().run();
|
|
494
615
|
return;
|
|
495
616
|
}
|
|
496
617
|
}
|
|
@@ -702,10 +823,10 @@
|
|
|
702
823
|
// fields, which is confusing.
|
|
703
824
|
function getPreviewTitle(): string {
|
|
704
825
|
const source = perspective === 'published' && publishedData ? publishedData : documentData;
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
return source
|
|
826
|
+
// Shared resolver: honors preview.prepare(), the literal preview.title, the
|
|
827
|
+
// select.title dot-path, and conventional field fallbacks — the same title an
|
|
828
|
+
// item shows in list/array/reference rows.
|
|
829
|
+
return resolvePreviewTitle(source, schema);
|
|
709
830
|
}
|
|
710
831
|
|
|
711
832
|
// CRITICAL: Clear state IMMEDIATELY when switching documents to prevent cross-contamination
|
|
@@ -1428,421 +1549,503 @@
|
|
|
1428
1549
|
}
|
|
1429
1550
|
</script>
|
|
1430
1551
|
|
|
1552
|
+
{#snippet editorActions()}
|
|
1553
|
+
<div class="flex shrink-0 items-center gap-2">
|
|
1554
|
+
{#if saving}
|
|
1555
|
+
<span
|
|
1556
|
+
class="text-muted-foreground hidden items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase sm:inline-flex"
|
|
1557
|
+
>
|
|
1558
|
+
<span class="bg-muted-foreground/60 h-1.5 w-1.5 animate-pulse rounded-full"></span>
|
|
1559
|
+
Saving
|
|
1560
|
+
</span>
|
|
1561
|
+
{:else if hasUnsavedChanges}
|
|
1562
|
+
<span
|
|
1563
|
+
class="text-muted-foreground hidden items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase sm:inline-flex"
|
|
1564
|
+
>
|
|
1565
|
+
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1566
|
+
Unsaved
|
|
1567
|
+
</span>
|
|
1568
|
+
{:else if savedAgoText}
|
|
1569
|
+
<span
|
|
1570
|
+
class="text-muted-foreground hidden items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase sm:inline-flex"
|
|
1571
|
+
>
|
|
1572
|
+
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1573
|
+
Auto-saved
|
|
1574
|
+
</span>
|
|
1575
|
+
{/if}
|
|
1576
|
+
|
|
1577
|
+
{#if documentId && fullDocument?._meta?.publishedHash}
|
|
1578
|
+
{@const isPublished =
|
|
1579
|
+
fullDocument?._meta?.status === 'published' && fullDocument?._meta?.publishedAt}
|
|
1580
|
+
{@const isUnpub = fullDocument?._meta?.status === 'unpublished'}
|
|
1581
|
+
<div class="flex items-center gap-1.5">
|
|
1582
|
+
<button
|
|
1583
|
+
class="inline-flex cursor-pointer items-center gap-1.5 rounded-full border px-2.5 py-1 text-[10px] font-medium tracking-wider uppercase transition-colors {perspective ===
|
|
1584
|
+
'draft'
|
|
1585
|
+
? 'bg-primary/90 text-primary-foreground border-transparent'
|
|
1586
|
+
: 'text-muted-foreground hover:bg-muted'}"
|
|
1587
|
+
onclick={() => switchPerspective('draft')}
|
|
1588
|
+
>
|
|
1589
|
+
<span
|
|
1590
|
+
class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full {perspective === 'draft'
|
|
1591
|
+
? 'bg-primary-foreground/60'
|
|
1592
|
+
: ''}"
|
|
1593
|
+
></span>
|
|
1594
|
+
Draft
|
|
1595
|
+
</button>
|
|
1596
|
+
<button
|
|
1597
|
+
class="inline-flex cursor-pointer items-center gap-1.5 rounded-full border px-2.5 py-1 text-[10px] font-medium tracking-wider uppercase transition-colors {perspective ===
|
|
1598
|
+
'published'
|
|
1599
|
+
? 'bg-primary text-primary-foreground border-transparent'
|
|
1600
|
+
: 'text-muted-foreground hover:bg-muted'}"
|
|
1601
|
+
onclick={() => switchPerspective('published')}
|
|
1602
|
+
>
|
|
1603
|
+
{#if isPublished}
|
|
1604
|
+
<span
|
|
1605
|
+
class="h-1.5 w-1.5 rounded-full {perspective === 'published'
|
|
1606
|
+
? 'bg-primary-foreground/60'
|
|
1607
|
+
: 'bg-green-500'}"
|
|
1608
|
+
></span>
|
|
1609
|
+
Published · {timeAgo(new Date(fullDocument._meta.publishedAt))}
|
|
1610
|
+
{:else if isUnpub}
|
|
1611
|
+
<span
|
|
1612
|
+
class="h-1.5 w-1.5 rounded-full {perspective === 'published'
|
|
1613
|
+
? 'bg-primary-foreground/60'
|
|
1614
|
+
: 'bg-muted-foreground/60'}"
|
|
1615
|
+
></span>
|
|
1616
|
+
Unpublished
|
|
1617
|
+
{:else}
|
|
1618
|
+
Published
|
|
1619
|
+
{/if}
|
|
1620
|
+
</button>
|
|
1621
|
+
</div>
|
|
1622
|
+
{/if}
|
|
1623
|
+
|
|
1624
|
+
<!-- Plugin document actions (aphex/document/action), applicable to this type -->
|
|
1625
|
+
{#if documentActionContext}
|
|
1626
|
+
{#each pluginDocumentActions as action (action.id)}
|
|
1627
|
+
{@const ActionComponent = action.component}
|
|
1628
|
+
<ActionComponent action={documentActionContext} />
|
|
1629
|
+
{/each}
|
|
1630
|
+
{/if}
|
|
1631
|
+
|
|
1632
|
+
{#if documentId}
|
|
1633
|
+
<div class="relative">
|
|
1634
|
+
<Button
|
|
1635
|
+
variant="ghost"
|
|
1636
|
+
size="icon"
|
|
1637
|
+
onclick={() => (showHeaderMenu = !showHeaderMenu)}
|
|
1638
|
+
class="h-8 w-8 cursor-pointer"
|
|
1639
|
+
>
|
|
1640
|
+
<Ellipsis class="h-4 w-4" />
|
|
1641
|
+
</Button>
|
|
1642
|
+
{#if showHeaderMenu}
|
|
1643
|
+
<div
|
|
1644
|
+
class="bg-background border-rule absolute top-full right-0 z-[60] mt-1 min-w-[160px] rounded-md border py-1 shadow-lg"
|
|
1645
|
+
>
|
|
1646
|
+
<button
|
|
1647
|
+
onclick={() => {
|
|
1648
|
+
showHeaderMenu = false;
|
|
1649
|
+
if (onOpenVersionHistory && documentId) {
|
|
1650
|
+
onOpenVersionHistory(documentId);
|
|
1651
|
+
} else {
|
|
1652
|
+
showVersionHistory = true;
|
|
1653
|
+
}
|
|
1654
|
+
}}
|
|
1655
|
+
class="hover:bg-muted flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left text-sm transition-colors"
|
|
1656
|
+
>
|
|
1657
|
+
<History class="h-3.5 w-3.5" /> History
|
|
1658
|
+
</button>
|
|
1659
|
+
<button
|
|
1660
|
+
onclick={() => {
|
|
1661
|
+
showHeaderMenu = false;
|
|
1662
|
+
showInspect = true;
|
|
1663
|
+
}}
|
|
1664
|
+
class="hover:bg-muted flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left text-sm transition-colors"
|
|
1665
|
+
>
|
|
1666
|
+
<Code class="h-3.5 w-3.5" /> Inspect
|
|
1667
|
+
</button>
|
|
1668
|
+
</div>
|
|
1669
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
1670
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
1671
|
+
<div class="fixed inset-0 z-[55]" onclick={() => (showHeaderMenu = false)}></div>
|
|
1672
|
+
{/if}
|
|
1673
|
+
</div>
|
|
1674
|
+
{/if}
|
|
1675
|
+
|
|
1676
|
+
<!-- Toggle presentation. In the visual editor it's a second way to switch off
|
|
1677
|
+
(alongside the breadcrumb back arrow); highlighted while active. -->
|
|
1678
|
+
{#if onTogglePresentation && schema?.previewUrl}
|
|
1679
|
+
<Button
|
|
1680
|
+
variant="ghost"
|
|
1681
|
+
size="icon"
|
|
1682
|
+
onclick={onTogglePresentation}
|
|
1683
|
+
class="hidden h-8 w-8 hover:cursor-pointer lg:flex {presentationMode ? 'text-primary' : ''}"
|
|
1684
|
+
title={presentationMode ? 'Exit visual editing' : 'Present'}
|
|
1685
|
+
>
|
|
1686
|
+
<Monitor class="h-4 w-4" />
|
|
1687
|
+
</Button>
|
|
1688
|
+
{/if}
|
|
1689
|
+
|
|
1690
|
+
<!-- Focus mode hides the side panels; presentation already does that, so it's
|
|
1691
|
+
redundant in the visual editor — hide it there. -->
|
|
1692
|
+
{#if onToggleFocus && !presentationMode}
|
|
1693
|
+
<Button
|
|
1694
|
+
variant="ghost"
|
|
1695
|
+
size="icon"
|
|
1696
|
+
onclick={onToggleFocus}
|
|
1697
|
+
class="hidden h-8 w-8 hover:cursor-pointer lg:flex"
|
|
1698
|
+
title={focusMode ? 'Exit focus mode' : 'Enter focus mode'}
|
|
1699
|
+
>
|
|
1700
|
+
{#if focusMode}
|
|
1701
|
+
<Minimize2 class="h-4 w-4" />
|
|
1702
|
+
{:else}
|
|
1703
|
+
<Maximize2 class="h-4 w-4" />
|
|
1704
|
+
{/if}
|
|
1705
|
+
</Button>
|
|
1706
|
+
{/if}
|
|
1707
|
+
|
|
1708
|
+
{#if backLabel}
|
|
1709
|
+
<Button
|
|
1710
|
+
variant="ghost"
|
|
1711
|
+
size="sm"
|
|
1712
|
+
onclick={onBack}
|
|
1713
|
+
class="hidden h-8 gap-1.5 px-2 hover:cursor-pointer lg:flex"
|
|
1714
|
+
title={backLabel}
|
|
1715
|
+
>
|
|
1716
|
+
<ArrowLeft class="h-4 w-4" />
|
|
1717
|
+
<span class="text-sm font-medium">{backLabel}</span>
|
|
1718
|
+
</Button>
|
|
1719
|
+
{:else if !presentationMode}
|
|
1720
|
+
<!-- Non-presentation only: the visual editor exits via the breadcrumb back. -->
|
|
1721
|
+
<Button
|
|
1722
|
+
variant="ghost"
|
|
1723
|
+
size="icon"
|
|
1724
|
+
onclick={onBack}
|
|
1725
|
+
class="hidden h-8 w-8 hover:cursor-pointer lg:flex"
|
|
1726
|
+
title="Close"
|
|
1727
|
+
>
|
|
1728
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
1729
|
+
<path
|
|
1730
|
+
stroke-linecap="round"
|
|
1731
|
+
stroke-linejoin="round"
|
|
1732
|
+
stroke-width="2"
|
|
1733
|
+
d="M6 18L18 6M6 6l12 12"
|
|
1734
|
+
/>
|
|
1735
|
+
</svg>
|
|
1736
|
+
</Button>
|
|
1737
|
+
{/if}
|
|
1738
|
+
</div>
|
|
1739
|
+
{/snippet}
|
|
1740
|
+
|
|
1741
|
+
{#snippet editorBreadcrumb()}
|
|
1742
|
+
<div
|
|
1743
|
+
class="text-muted-foreground flex min-w-0 items-center gap-1.5 text-[11px] font-medium tracking-wider uppercase"
|
|
1744
|
+
>
|
|
1745
|
+
<!-- In the visual editor, a breadcrumb-style back arrow before the type · title
|
|
1746
|
+
exits visual editing (back to the document list), replacing the close X. -->
|
|
1747
|
+
{#if presentationMode && onTogglePresentation}
|
|
1748
|
+
<button
|
|
1749
|
+
type="button"
|
|
1750
|
+
onclick={onTogglePresentation}
|
|
1751
|
+
class="hover:text-foreground hover:bg-muted -ml-1 flex shrink-0 cursor-pointer items-center rounded p-1 transition-colors"
|
|
1752
|
+
title="Exit visual editing"
|
|
1753
|
+
aria-label="Exit visual editing"
|
|
1754
|
+
>
|
|
1755
|
+
<ArrowLeft class="h-4 w-4" />
|
|
1756
|
+
</button>
|
|
1757
|
+
{/if}
|
|
1758
|
+
<span class="shrink-0 whitespace-nowrap">{schema?.title || documentType}</span>
|
|
1759
|
+
<!-- The title is only shown here in presentation mode, where there's no <h1>.
|
|
1760
|
+
In the normal editor the <h1> already renders it, so don't duplicate it. -->
|
|
1761
|
+
{#if presentationMode}
|
|
1762
|
+
<span class="shrink-0" aria-hidden="true">·</span>
|
|
1763
|
+
<span class="max-w-[24rem] min-w-0 truncate">{getPreviewTitle()}</span>
|
|
1764
|
+
{/if}
|
|
1765
|
+
</div>
|
|
1766
|
+
{/snippet}
|
|
1767
|
+
|
|
1768
|
+
{#if presentationMode}
|
|
1769
|
+
<AdminSlot name="navbar-start" id="editor-breadcrumb">
|
|
1770
|
+
{@render editorBreadcrumb()}
|
|
1771
|
+
</AdminSlot>
|
|
1772
|
+
<AdminSlot name="navbar-end" id="editor-actions">
|
|
1773
|
+
{@render editorActions()}
|
|
1774
|
+
</AdminSlot>
|
|
1775
|
+
{/if}
|
|
1776
|
+
|
|
1431
1777
|
<div class="relative flex h-full w-full min-w-0 flex-col overflow-hidden">
|
|
1432
1778
|
<!-- Hero Header -->
|
|
1433
|
-
|
|
1434
|
-
<div class="w-full">
|
|
1435
|
-
|
|
1436
|
-
|
|
1779
|
+
{#if !presentationMode}
|
|
1780
|
+
<div class="bg-background w-full min-w-0 overflow-x-clip px-4 pt-4 pb-5 lg:px-6 lg:pt-5">
|
|
1781
|
+
<div class="w-full">
|
|
1782
|
+
<!-- Top row: breadcrumb + actions. Wraps rather than crumpling when the
|
|
1783
|
+
editor is narrow (e.g. the reference panel). -->
|
|
1437
1784
|
<div
|
|
1438
|
-
class="
|
|
1785
|
+
class="flex flex-wrap items-center justify-between gap-x-3 gap-y-2 {presentationMode
|
|
1786
|
+
? ''
|
|
1787
|
+
: 'mb-4'}"
|
|
1439
1788
|
>
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
<span class="truncate">{getPreviewTitle()}</span>
|
|
1789
|
+
{@render editorBreadcrumb()}
|
|
1790
|
+
{@render editorActions()}
|
|
1443
1791
|
</div>
|
|
1444
1792
|
|
|
1445
|
-
|
|
1793
|
+
<!-- Title (the whole hero is already hidden in presentation mode, where
|
|
1794
|
+
the live preview shows the title instead). -->
|
|
1795
|
+
<!-- Always one line (`truncate`); font shrinks where space is tight (narrow
|
|
1796
|
+
editor / reference panel) so more of the title fits before the ellipsis,
|
|
1797
|
+
and stays large on wide screens. -->
|
|
1798
|
+
<h1 class="block w-full min-w-0 truncate text-xl font-semibold tracking-tight lg:text-2xl">
|
|
1799
|
+
{getPreviewTitle()}
|
|
1800
|
+
</h1>
|
|
1801
|
+
|
|
1802
|
+
<!-- Mobile-only status row: save state + draft pill for narrow viewports -->
|
|
1803
|
+
<div class="mt-3 flex items-center justify-between gap-3 sm:hidden">
|
|
1804
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
1805
|
+
{#if !fullDocument?._meta?.publishedHash && documentId && fullDocument?._meta?.status !== 'unpublished'}
|
|
1806
|
+
<span
|
|
1807
|
+
class="text-muted-foreground inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[10px] font-medium tracking-wider uppercase"
|
|
1808
|
+
>
|
|
1809
|
+
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1810
|
+
Draft
|
|
1811
|
+
</span>
|
|
1812
|
+
{/if}
|
|
1813
|
+
</div>
|
|
1814
|
+
|
|
1446
1815
|
{#if saving}
|
|
1447
1816
|
<span
|
|
1448
|
-
class="text-muted-foreground
|
|
1817
|
+
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
1449
1818
|
>
|
|
1450
1819
|
<span class="bg-muted-foreground/60 h-1.5 w-1.5 animate-pulse rounded-full"></span>
|
|
1451
1820
|
Saving
|
|
1452
1821
|
</span>
|
|
1453
1822
|
{:else if hasUnsavedChanges}
|
|
1454
1823
|
<span
|
|
1455
|
-
class="text-muted-foreground
|
|
1824
|
+
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
1456
1825
|
>
|
|
1457
1826
|
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1458
1827
|
Unsaved
|
|
1459
1828
|
</span>
|
|
1460
1829
|
{:else if savedAgoText}
|
|
1461
1830
|
<span
|
|
1462
|
-
class="text-muted-foreground
|
|
1831
|
+
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
1463
1832
|
>
|
|
1464
1833
|
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1465
1834
|
Auto-saved
|
|
1466
1835
|
</span>
|
|
1467
1836
|
{/if}
|
|
1468
|
-
|
|
1469
|
-
{#if documentId && fullDocument?._meta?.publishedHash}
|
|
1470
|
-
{@const isPublished =
|
|
1471
|
-
fullDocument?._meta?.status === 'published' && fullDocument?._meta?.publishedAt}
|
|
1472
|
-
{@const isUnpub = fullDocument?._meta?.status === 'unpublished'}
|
|
1473
|
-
<div class="flex items-center gap-1.5">
|
|
1474
|
-
<button
|
|
1475
|
-
class="inline-flex cursor-pointer items-center gap-1.5 rounded-full border px-2.5 py-1 text-[10px] font-medium tracking-wider uppercase transition-colors {perspective ===
|
|
1476
|
-
'draft'
|
|
1477
|
-
? 'bg-primary/90 text-primary-foreground border-transparent'
|
|
1478
|
-
: 'text-muted-foreground hover:bg-muted'}"
|
|
1479
|
-
onclick={() => switchPerspective('draft')}
|
|
1480
|
-
>
|
|
1481
|
-
<span
|
|
1482
|
-
class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full {perspective === 'draft'
|
|
1483
|
-
? 'bg-primary-foreground/60'
|
|
1484
|
-
: ''}"
|
|
1485
|
-
></span>
|
|
1486
|
-
Draft
|
|
1487
|
-
</button>
|
|
1488
|
-
<button
|
|
1489
|
-
class="inline-flex cursor-pointer items-center gap-1.5 rounded-full border px-2.5 py-1 text-[10px] font-medium tracking-wider uppercase transition-colors {perspective ===
|
|
1490
|
-
'published'
|
|
1491
|
-
? 'bg-primary text-primary-foreground border-transparent'
|
|
1492
|
-
: 'text-muted-foreground hover:bg-muted'}"
|
|
1493
|
-
onclick={() => switchPerspective('published')}
|
|
1494
|
-
>
|
|
1495
|
-
{#if isPublished}
|
|
1496
|
-
<span
|
|
1497
|
-
class="h-1.5 w-1.5 rounded-full {perspective === 'published'
|
|
1498
|
-
? 'bg-primary-foreground/60'
|
|
1499
|
-
: 'bg-green-500'}"
|
|
1500
|
-
></span>
|
|
1501
|
-
Published · {timeAgo(new Date(fullDocument._meta.publishedAt))}
|
|
1502
|
-
{:else if isUnpub}
|
|
1503
|
-
<span
|
|
1504
|
-
class="h-1.5 w-1.5 rounded-full {perspective === 'published'
|
|
1505
|
-
? 'bg-primary-foreground/60'
|
|
1506
|
-
: 'bg-muted-foreground/60'}"
|
|
1507
|
-
></span>
|
|
1508
|
-
Unpublished
|
|
1509
|
-
{:else}
|
|
1510
|
-
Published
|
|
1511
|
-
{/if}
|
|
1512
|
-
</button>
|
|
1513
|
-
</div>
|
|
1514
|
-
{/if}
|
|
1515
|
-
|
|
1516
|
-
{#if documentId}
|
|
1517
|
-
<div class="relative">
|
|
1518
|
-
<Button
|
|
1519
|
-
variant="ghost"
|
|
1520
|
-
size="icon"
|
|
1521
|
-
onclick={() => (showHeaderMenu = !showHeaderMenu)}
|
|
1522
|
-
class="h-8 w-8 cursor-pointer"
|
|
1523
|
-
>
|
|
1524
|
-
<Ellipsis class="h-4 w-4" />
|
|
1525
|
-
</Button>
|
|
1526
|
-
{#if showHeaderMenu}
|
|
1527
|
-
<div
|
|
1528
|
-
class="bg-background border-rule absolute top-full right-0 z-[60] mt-1 min-w-[160px] rounded-md border py-1 shadow-lg"
|
|
1529
|
-
>
|
|
1530
|
-
<button
|
|
1531
|
-
onclick={() => {
|
|
1532
|
-
showHeaderMenu = false;
|
|
1533
|
-
if (onOpenVersionHistory && documentId) {
|
|
1534
|
-
onOpenVersionHistory(documentId);
|
|
1535
|
-
} else {
|
|
1536
|
-
showVersionHistory = true;
|
|
1537
|
-
}
|
|
1538
|
-
}}
|
|
1539
|
-
class="hover:bg-muted flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left text-sm transition-colors"
|
|
1540
|
-
>
|
|
1541
|
-
<History class="h-3.5 w-3.5" /> History
|
|
1542
|
-
</button>
|
|
1543
|
-
<button
|
|
1544
|
-
onclick={() => {
|
|
1545
|
-
showHeaderMenu = false;
|
|
1546
|
-
showInspect = true;
|
|
1547
|
-
}}
|
|
1548
|
-
class="hover:bg-muted flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left text-sm transition-colors"
|
|
1549
|
-
>
|
|
1550
|
-
<Code class="h-3.5 w-3.5" /> Inspect
|
|
1551
|
-
</button>
|
|
1552
|
-
</div>
|
|
1553
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
1554
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
1555
|
-
<div class="fixed inset-0 z-[55]" onclick={() => (showHeaderMenu = false)}></div>
|
|
1556
|
-
{/if}
|
|
1557
|
-
</div>
|
|
1558
|
-
{/if}
|
|
1559
|
-
|
|
1560
|
-
{#if onTogglePresentation && schema?.previewUrl}
|
|
1561
|
-
<Button
|
|
1562
|
-
variant="ghost"
|
|
1563
|
-
size="icon"
|
|
1564
|
-
onclick={onTogglePresentation}
|
|
1565
|
-
class="hidden h-8 w-8 hover:cursor-pointer lg:flex {presentationMode
|
|
1566
|
-
? 'text-primary'
|
|
1567
|
-
: ''}"
|
|
1568
|
-
title={presentationMode ? 'Exit presentation mode' : 'Present'}
|
|
1569
|
-
>
|
|
1570
|
-
<Monitor class="h-4 w-4" />
|
|
1571
|
-
</Button>
|
|
1572
|
-
{/if}
|
|
1573
|
-
|
|
1574
|
-
{#if onToggleFocus}
|
|
1575
|
-
<Button
|
|
1576
|
-
variant="ghost"
|
|
1577
|
-
size="icon"
|
|
1578
|
-
onclick={onToggleFocus}
|
|
1579
|
-
class="hidden h-8 w-8 hover:cursor-pointer lg:flex"
|
|
1580
|
-
title={focusMode ? 'Exit focus mode' : 'Enter focus mode'}
|
|
1581
|
-
>
|
|
1582
|
-
{#if focusMode}
|
|
1583
|
-
<Minimize2 class="h-4 w-4" />
|
|
1584
|
-
{:else}
|
|
1585
|
-
<Maximize2 class="h-4 w-4" />
|
|
1586
|
-
{/if}
|
|
1587
|
-
</Button>
|
|
1588
|
-
{/if}
|
|
1589
|
-
|
|
1590
|
-
<Button
|
|
1591
|
-
variant="ghost"
|
|
1592
|
-
size="icon"
|
|
1593
|
-
onclick={onBack}
|
|
1594
|
-
class="hidden h-8 w-8 hover:cursor-pointer lg:flex"
|
|
1595
|
-
title="Close"
|
|
1596
|
-
>
|
|
1597
|
-
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
1598
|
-
<path
|
|
1599
|
-
stroke-linecap="round"
|
|
1600
|
-
stroke-linejoin="round"
|
|
1601
|
-
stroke-width="2"
|
|
1602
|
-
d="M6 18L18 6M6 6l12 12"
|
|
1603
|
-
/>
|
|
1604
|
-
</svg>
|
|
1605
|
-
</Button>
|
|
1606
|
-
</div>
|
|
1607
|
-
</div>
|
|
1608
|
-
|
|
1609
|
-
<!-- Title -->
|
|
1610
|
-
<h1 class="block w-full min-w-0 truncate text-3xl font-semibold tracking-tight">
|
|
1611
|
-
{getPreviewTitle()}
|
|
1612
|
-
</h1>
|
|
1613
|
-
|
|
1614
|
-
<!-- Mobile-only status row: save state + draft pill for narrow viewports -->
|
|
1615
|
-
<div class="mt-3 flex items-center justify-between gap-3 sm:hidden">
|
|
1616
|
-
<div class="flex flex-wrap items-center gap-2">
|
|
1617
|
-
{#if !fullDocument?._meta?.publishedHash && documentId && fullDocument?._meta?.status !== 'unpublished'}
|
|
1618
|
-
<span
|
|
1619
|
-
class="text-muted-foreground inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[10px] font-medium tracking-wider uppercase"
|
|
1620
|
-
>
|
|
1621
|
-
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1622
|
-
Draft
|
|
1623
|
-
</span>
|
|
1624
|
-
{/if}
|
|
1625
1837
|
</div>
|
|
1626
|
-
|
|
1627
|
-
{#if saving}
|
|
1628
|
-
<span
|
|
1629
|
-
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
1630
|
-
>
|
|
1631
|
-
<span class="bg-muted-foreground/60 h-1.5 w-1.5 animate-pulse rounded-full"></span>
|
|
1632
|
-
Saving
|
|
1633
|
-
</span>
|
|
1634
|
-
{:else if hasUnsavedChanges}
|
|
1635
|
-
<span
|
|
1636
|
-
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
1637
|
-
>
|
|
1638
|
-
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1639
|
-
Unsaved
|
|
1640
|
-
</span>
|
|
1641
|
-
{:else if savedAgoText}
|
|
1642
|
-
<span
|
|
1643
|
-
class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
|
|
1644
|
-
>
|
|
1645
|
-
<span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
|
|
1646
|
-
Auto-saved
|
|
1647
|
-
</span>
|
|
1648
|
-
{/if}
|
|
1649
1838
|
</div>
|
|
1650
1839
|
</div>
|
|
1651
|
-
|
|
1840
|
+
{/if}
|
|
1652
1841
|
|
|
1653
1842
|
<!-- Content Form -->
|
|
1654
1843
|
<div
|
|
1655
1844
|
data-document-editor
|
|
1656
1845
|
class="relative flex min-h-0 flex-1 {presentationMode ? 'flex-row' : 'flex-col'}"
|
|
1657
1846
|
>
|
|
1847
|
+
<!-- Non-scrolling positioning boundary: field modals (ObjectModal, asset browser)
|
|
1848
|
+
are `absolute inset-0`, so they bind here and stay contained to the fields
|
|
1849
|
+
column in presentation mode instead of covering the preview iframe. Scroll
|
|
1850
|
+
lives on the inner container so the overlay doesn't scroll with the content. -->
|
|
1658
1851
|
<div
|
|
1659
|
-
class="{presentationMode
|
|
1660
|
-
? 'shrink-0 overflow-y-auto'
|
|
1661
|
-
: 'flex flex-1 flex-col overflow-auto'} p-4 lg:p-6"
|
|
1852
|
+
class="relative flex min-h-0 flex-col {presentationMode ? 'shrink-0' : 'flex-1'}"
|
|
1662
1853
|
style={presentationMode ? `width: ${fieldsWidth}px; min-width: 500px` : undefined}
|
|
1663
1854
|
>
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1855
|
+
<!-- Field Group Tabs — a fixed header OUTSIDE the scroll container, so they
|
|
1856
|
+
never scroll away or get clipped by a field (previous sticky-inside-scroll
|
|
1857
|
+
approach was fragile across field types). Only the fields below scroll. -->
|
|
1858
|
+
{#if schema && schema.groups && schema.groups.length > 0}
|
|
1859
|
+
{@const visibleGroups = schema.groups.filter((g) => !g.hidden)}
|
|
1860
|
+
<div
|
|
1861
|
+
class="border-rule/60 flex shrink-0 items-center gap-1 overflow-x-auto border-b px-4 py-2 [scrollbar-width:none] lg:px-6 [&::-webkit-scrollbar]:hidden"
|
|
1862
|
+
>
|
|
1863
|
+
<button
|
|
1864
|
+
type="button"
|
|
1865
|
+
onclick={() => (activeGroup = 'all')}
|
|
1866
|
+
class="cursor-pointer rounded-md px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-colors {activeGroup ===
|
|
1867
|
+
'all'
|
|
1868
|
+
? 'bg-muted text-foreground'
|
|
1869
|
+
: 'text-muted-foreground hover:text-foreground'}"
|
|
1870
|
+
>
|
|
1871
|
+
All fields
|
|
1872
|
+
</button>
|
|
1873
|
+
{#each visibleGroups as group (group.name)}
|
|
1874
|
+
<button
|
|
1875
|
+
type="button"
|
|
1876
|
+
onclick={() => (activeGroup = group.name)}
|
|
1877
|
+
class="flex cursor-pointer items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-colors {activeGroup ===
|
|
1878
|
+
group.name
|
|
1879
|
+
? 'bg-muted text-foreground'
|
|
1880
|
+
: 'text-muted-foreground hover:text-foreground'}"
|
|
1881
|
+
>
|
|
1882
|
+
{#if group.icon}
|
|
1883
|
+
{@const Icon = group.icon}
|
|
1884
|
+
<Icon class="h-4 w-4" />
|
|
1885
|
+
{/if}
|
|
1886
|
+
{group.title}
|
|
1887
|
+
</button>
|
|
1888
|
+
{/each}
|
|
1889
|
+
</div>
|
|
1890
|
+
{/if}
|
|
1891
|
+
<!-- gap between fields is intentionally wider than each field's internal
|
|
1892
|
+
spacing (space-y-2), so a label+description+input reads as one unit and
|
|
1893
|
+
fields don't blur together — tighter within, looser between. Uniform 2rem:
|
|
1894
|
+
mobile felt cramped on the smaller base gap while desktop was fine. -->
|
|
1895
|
+
<div class="min-h-0 w-full flex-1 overflow-y-auto">
|
|
1896
|
+
<div class="flex flex-col gap-8 p-4 lg:p-6">
|
|
1897
|
+
{#if saveError}
|
|
1898
|
+
<div class="bg-destructive/10 border-destructive/20 rounded-md border p-3">
|
|
1899
|
+
<p class="text-destructive text-sm">{saveError}</p>
|
|
1900
|
+
</div>
|
|
1901
|
+
{/if}
|
|
1670
1902
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1903
|
+
{#if schemaError}
|
|
1904
|
+
<div class="bg-destructive/10 border-destructive/20 rounded-md border p-3">
|
|
1905
|
+
<p class="text-destructive text-sm">Schema Error: {schemaError}</p>
|
|
1906
|
+
</div>
|
|
1907
|
+
{:else if schemaLoading}
|
|
1908
|
+
<div class="p-6 text-center">
|
|
1909
|
+
<div class="text-muted-foreground text-sm">Loading schema...</div>
|
|
1910
|
+
</div>
|
|
1911
|
+
{:else if schema}
|
|
1912
|
+
<!-- Orphaned Fields Warning -->
|
|
1913
|
+
{#if showOrphanedFields && orphanedFields.length > 0}
|
|
1914
|
+
<div class="space-y-3 rounded-md border border-orange-200 bg-orange-50 p-4">
|
|
1915
|
+
<div class="flex items-center gap-2">
|
|
1916
|
+
<svg
|
|
1917
|
+
class="h-5 w-5 text-orange-600"
|
|
1918
|
+
fill="none"
|
|
1919
|
+
viewBox="0 0 24 24"
|
|
1920
|
+
stroke="currentColor"
|
|
1921
|
+
>
|
|
1922
|
+
<path
|
|
1923
|
+
stroke-linecap="round"
|
|
1924
|
+
stroke-linejoin="round"
|
|
1925
|
+
stroke-width="2"
|
|
1926
|
+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L5.081 16.5c-.77.833.192 2.5 1.732 2.5z"
|
|
1927
|
+
/>
|
|
1928
|
+
</svg>
|
|
1929
|
+
<h4 class="text-sm font-medium text-orange-800">
|
|
1930
|
+
{orphanedFields.length} orphaned field{orphanedFields.length === 1 ? '' : 's'} detected
|
|
1931
|
+
</h4>
|
|
1932
|
+
</div>
|
|
1701
1933
|
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1934
|
+
<p class="text-sm text-orange-700">
|
|
1935
|
+
These fields exist in your document but are no longer defined in the schema:
|
|
1936
|
+
</p>
|
|
1705
1937
|
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1938
|
+
<div class="space-y-2">
|
|
1939
|
+
{#each orphanedFields as field, index (index)}
|
|
1940
|
+
<div
|
|
1941
|
+
class="flex items-center justify-between rounded border border-orange-200 bg-white p-3"
|
|
1942
|
+
>
|
|
1943
|
+
<div class="flex-1">
|
|
1944
|
+
<div class="font-mono text-sm font-medium text-orange-800">
|
|
1945
|
+
{field.path || field.key}
|
|
1946
|
+
</div>
|
|
1947
|
+
<div class="mt-1 text-xs text-orange-600">
|
|
1948
|
+
<code class="rounded bg-orange-100 px-1"
|
|
1949
|
+
>{JSON.stringify(field.value)}</code
|
|
1950
|
+
>
|
|
1951
|
+
</div>
|
|
1718
1952
|
</div>
|
|
1953
|
+
<Button
|
|
1954
|
+
size="sm"
|
|
1955
|
+
variant="outline"
|
|
1956
|
+
onclick={() => removeOrphanedField(field)}
|
|
1957
|
+
class="ml-3 h-8 border-red-200 px-3 text-red-600 hover:border-red-300 hover:bg-red-50"
|
|
1958
|
+
>
|
|
1959
|
+
Remove
|
|
1960
|
+
</Button>
|
|
1719
1961
|
</div>
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
variant="outline"
|
|
1723
|
-
onclick={() => removeOrphanedField(field)}
|
|
1724
|
-
class="ml-3 h-8 border-red-200 px-3 text-red-600 hover:border-red-300 hover:bg-red-50"
|
|
1725
|
-
>
|
|
1726
|
-
Remove
|
|
1727
|
-
</Button>
|
|
1728
|
-
</div>
|
|
1729
|
-
{/each}
|
|
1730
|
-
</div>
|
|
1962
|
+
{/each}
|
|
1963
|
+
</div>
|
|
1731
1964
|
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1965
|
+
<div class="flex gap-2 border-t border-orange-200 pt-2">
|
|
1966
|
+
<Button
|
|
1967
|
+
size="sm"
|
|
1968
|
+
variant="outline"
|
|
1969
|
+
onclick={cleanupAllOrphanedFields}
|
|
1970
|
+
class="border-orange-600 bg-orange-600 text-white hover:bg-orange-700"
|
|
1971
|
+
>
|
|
1972
|
+
Remove All
|
|
1973
|
+
</Button>
|
|
1974
|
+
<Button
|
|
1975
|
+
size="sm"
|
|
1976
|
+
variant="ghost"
|
|
1977
|
+
onclick={dismissOrphanedFields}
|
|
1978
|
+
class="text-orange-700 hover:text-orange-800"
|
|
1979
|
+
>
|
|
1980
|
+
Dismiss
|
|
1981
|
+
</Button>
|
|
1982
|
+
</div>
|
|
1749
1983
|
</div>
|
|
1750
|
-
|
|
1751
|
-
{/if}
|
|
1984
|
+
{/if}
|
|
1752
1985
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
onclick={() => (activeGroup = group.name)}
|
|
1771
|
-
class="flex cursor-pointer items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-colors {activeGroup ===
|
|
1772
|
-
group.name
|
|
1773
|
-
? 'bg-muted text-foreground'
|
|
1774
|
-
: 'text-muted-foreground hover:text-foreground'}"
|
|
1986
|
+
<!-- Dynamic Schema Fields -->
|
|
1987
|
+
<svelte:boundary
|
|
1988
|
+
onerror={(error) =>
|
|
1989
|
+
cmsLogger.error('[DocumentEditor]', 'Error in editor content:', error)}
|
|
1990
|
+
>
|
|
1991
|
+
{#each schema.fields.filter((f) => fieldInGroup(f, activeGroup) && !hiddenFieldNames.has(f.name)) as field (field.name)}
|
|
1992
|
+
{@const viewData =
|
|
1993
|
+
isPreviewingVersion && activePreview
|
|
1994
|
+
? activePreview.data
|
|
1995
|
+
: isViewingPublished && publishedData
|
|
1996
|
+
? publishedData
|
|
1997
|
+
: documentData}
|
|
1998
|
+
<div
|
|
1999
|
+
data-field-name={field.name}
|
|
2000
|
+
class="rounded-md transition-all duration-300 {focusedFieldName === field.name
|
|
2001
|
+
? 'ring-primary/40 ring-2 ring-offset-2'
|
|
2002
|
+
: ''}"
|
|
1775
2003
|
>
|
|
1776
|
-
|
|
1777
|
-
{
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
2004
|
+
<SchemaField
|
|
2005
|
+
{field}
|
|
2006
|
+
value={viewData[field.name]}
|
|
2007
|
+
documentData={viewData}
|
|
2008
|
+
onUpdate={(newValue) => {
|
|
2009
|
+
if (isViewingPublished) return;
|
|
2010
|
+
documentData = { ...documentData, [field.name]: newValue };
|
|
2011
|
+
hasUnsavedChanges = true;
|
|
2012
|
+
}}
|
|
2013
|
+
{onOpenReference}
|
|
2014
|
+
schemaType={documentType}
|
|
2015
|
+
readonly={isReadOnly ||
|
|
2016
|
+
isViewingPublished ||
|
|
2017
|
+
isPreviewingVersion ||
|
|
2018
|
+
isFieldReadonly(field.name)}
|
|
2019
|
+
organizationId={fullDocument?._meta?.organizationId}
|
|
2020
|
+
/>
|
|
2021
|
+
</div>
|
|
1782
2022
|
{/each}
|
|
1783
|
-
</div>
|
|
1784
|
-
{/if}
|
|
1785
|
-
|
|
1786
|
-
<!-- Dynamic Schema Fields -->
|
|
1787
|
-
<svelte:boundary
|
|
1788
|
-
onerror={(error) =>
|
|
1789
|
-
cmsLogger.error('[DocumentEditor]', 'Error in editor content:', error)}
|
|
1790
|
-
>
|
|
1791
|
-
{#each schema.fields.filter((f) => fieldInGroup(f, activeGroup) && !hiddenFieldNames.has(f.name)) as field (field.name)}
|
|
1792
|
-
{@const viewData =
|
|
1793
|
-
isPreviewingVersion && activePreview
|
|
1794
|
-
? activePreview.data
|
|
1795
|
-
: isViewingPublished && publishedData
|
|
1796
|
-
? publishedData
|
|
1797
|
-
: documentData}
|
|
1798
|
-
<div
|
|
1799
|
-
data-field-name={field.name}
|
|
1800
|
-
class="rounded-md transition-all duration-300 {focusedFieldName === field.name
|
|
1801
|
-
? 'ring-primary/40 ring-2 ring-offset-2'
|
|
1802
|
-
: ''}"
|
|
1803
|
-
>
|
|
1804
|
-
<SchemaField
|
|
1805
|
-
{field}
|
|
1806
|
-
value={viewData[field.name]}
|
|
1807
|
-
documentData={viewData}
|
|
1808
|
-
onUpdate={(newValue) => {
|
|
1809
|
-
if (isViewingPublished) return;
|
|
1810
|
-
documentData = { ...documentData, [field.name]: newValue };
|
|
1811
|
-
hasUnsavedChanges = true;
|
|
1812
|
-
}}
|
|
1813
|
-
{onOpenReference}
|
|
1814
|
-
schemaType={documentType}
|
|
1815
|
-
readonly={isReadOnly ||
|
|
1816
|
-
isViewingPublished ||
|
|
1817
|
-
isPreviewingVersion ||
|
|
1818
|
-
isFieldReadonly(field.name)}
|
|
1819
|
-
organizationId={fullDocument?._meta?.organizationId}
|
|
1820
|
-
/>
|
|
1821
|
-
</div>
|
|
1822
|
-
{/each}
|
|
1823
2023
|
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
<p class="text-muted-foreground mt-1 text-sm">
|
|
1828
|
-
{error instanceof Error ? error.message : 'Unknown error'}
|
|
1829
|
-
</p>
|
|
1830
|
-
<button
|
|
1831
|
-
class="bg-primary text-primary-foreground mt-3 rounded px-4 py-2 text-sm"
|
|
1832
|
-
onclick={reset}
|
|
2024
|
+
{#snippet failed(error, reset)}
|
|
2025
|
+
<div
|
|
2026
|
+
class="border-destructive/30 bg-destructive/5 rounded-md border p-4 text-center"
|
|
1833
2027
|
>
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
2028
|
+
<p class="text-destructive font-medium">Document editor encountered an error</p>
|
|
2029
|
+
<p class="text-muted-foreground mt-1 text-sm">
|
|
2030
|
+
{error instanceof Error ? error.message : 'Unknown error'}
|
|
2031
|
+
</p>
|
|
2032
|
+
<button
|
|
2033
|
+
class="bg-primary text-primary-foreground mt-3 rounded px-4 py-2 text-sm"
|
|
2034
|
+
onclick={reset}
|
|
2035
|
+
>
|
|
2036
|
+
Reload editor
|
|
2037
|
+
</button>
|
|
2038
|
+
</div>
|
|
2039
|
+
{/snippet}
|
|
2040
|
+
</svelte:boundary>
|
|
2041
|
+
{:else}
|
|
2042
|
+
<div class="border-muted-foreground/30 rounded-md border border-dashed p-4">
|
|
2043
|
+
<p class="text-muted-foreground text-center text-sm">
|
|
2044
|
+
No schema found for document type: {documentType}
|
|
2045
|
+
</p>
|
|
2046
|
+
</div>
|
|
2047
|
+
{/if}
|
|
2048
|
+
</div>
|
|
1846
2049
|
</div>
|
|
1847
2050
|
</div>
|
|
1848
2051
|
|
|
@@ -1909,6 +2112,49 @@
|
|
|
1909
2112
|
>
|
|
1910
2113
|
</div>
|
|
1911
2114
|
|
|
2115
|
+
<!-- Viewport switcher -->
|
|
2116
|
+
<div class="bg-muted flex items-center gap-0.5 rounded p-0.5">
|
|
2117
|
+
{#each [{ v: 'desktop', Icon: Monitor, label: 'Desktop' }, { v: 'tablet', Icon: Tablet, label: 'Tablet' }, { v: 'mobile', Icon: Smartphone, label: 'Mobile' }] as { v, Icon, label } (v)}
|
|
2118
|
+
<button
|
|
2119
|
+
onclick={() => (previewViewport = v as typeof previewViewport)}
|
|
2120
|
+
class="cursor-pointer rounded p-1 transition-colors {previewViewport === v
|
|
2121
|
+
? 'bg-background text-foreground shadow-sm'
|
|
2122
|
+
: 'text-muted-foreground hover:text-foreground'}"
|
|
2123
|
+
title={label}
|
|
2124
|
+
aria-pressed={previewViewport === v}
|
|
2125
|
+
>
|
|
2126
|
+
<Icon class="h-3.5 w-3.5" />
|
|
2127
|
+
</button>
|
|
2128
|
+
{/each}
|
|
2129
|
+
</div>
|
|
2130
|
+
|
|
2131
|
+
<!-- Zoom -->
|
|
2132
|
+
<div class="ml-1 flex items-center gap-0.5">
|
|
2133
|
+
<button
|
|
2134
|
+
onclick={() => zoomBy(-0.1)}
|
|
2135
|
+
disabled={previewZoom <= 0.5}
|
|
2136
|
+
class="hover:bg-muted text-muted-foreground hover:text-foreground cursor-pointer rounded p-1.5 transition-colors disabled:cursor-default disabled:opacity-40"
|
|
2137
|
+
title="Zoom out"
|
|
2138
|
+
>
|
|
2139
|
+
<ZoomOut class="h-3.5 w-3.5" />
|
|
2140
|
+
</button>
|
|
2141
|
+
<button
|
|
2142
|
+
onclick={() => (previewZoom = 1)}
|
|
2143
|
+
class="text-muted-foreground hover:text-foreground w-9 cursor-pointer text-center font-mono text-[11px] tabular-nums transition-colors"
|
|
2144
|
+
title="Reset zoom"
|
|
2145
|
+
>
|
|
2146
|
+
{Math.round(previewZoom * 100)}%
|
|
2147
|
+
</button>
|
|
2148
|
+
<button
|
|
2149
|
+
onclick={() => zoomBy(0.1)}
|
|
2150
|
+
disabled={previewZoom >= 1.5}
|
|
2151
|
+
class="hover:bg-muted text-muted-foreground hover:text-foreground cursor-pointer rounded p-1.5 transition-colors disabled:cursor-default disabled:opacity-40"
|
|
2152
|
+
title="Zoom in"
|
|
2153
|
+
>
|
|
2154
|
+
<ZoomIn class="h-3.5 w-3.5" />
|
|
2155
|
+
</button>
|
|
2156
|
+
</div>
|
|
2157
|
+
|
|
1912
2158
|
<!-- Open in new tab -->
|
|
1913
2159
|
<button
|
|
1914
2160
|
onclick={openPreviewInNewTab}
|
|
@@ -1919,13 +2165,28 @@
|
|
|
1919
2165
|
</button>
|
|
1920
2166
|
</div>
|
|
1921
2167
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2168
|
+
<!-- `safe center`: centre the frame when it fits, but align to start when
|
|
2169
|
+
it's wider than the container (zoomed in) so you can scroll to the
|
|
2170
|
+
overflow — plain `justify-center` makes the left side unreachable. -->
|
|
2171
|
+
<div
|
|
2172
|
+
class="bg-muted/20 relative flex min-h-0 flex-1 [justify-content:safe_center] {previewViewport ===
|
|
2173
|
+
'desktop'
|
|
2174
|
+
? 'overflow-hidden'
|
|
2175
|
+
: 'overflow-auto p-4'}"
|
|
2176
|
+
>
|
|
2177
|
+
<div
|
|
2178
|
+
class={previewViewport === 'desktop'
|
|
2179
|
+
? 'h-full w-full'
|
|
2180
|
+
: 'bg-background h-full shrink-0 overflow-hidden rounded-xl border shadow-sm'}
|
|
2181
|
+
style={frameStyle}
|
|
2182
|
+
>
|
|
2183
|
+
<iframe
|
|
2184
|
+
bind:this={iframeRef}
|
|
2185
|
+
src={iframeUrl}
|
|
2186
|
+
class="h-full w-full border-none"
|
|
2187
|
+
title="Page preview"
|
|
2188
|
+
></iframe>
|
|
2189
|
+
</div>
|
|
1929
2190
|
{#if isResizing}
|
|
1930
2191
|
<div class="absolute inset-0"></div>
|
|
1931
2192
|
{/if}
|