@cosmicdrift/kumiko-bundled-features 0.362.0 → 0.364.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin-shell/feature.d.ts +3 -0
- package/dist/admin-shell/feature.js +6 -1
- package/dist/agent-tools/agent-manifest.js +2 -1
- package/dist/agent-tools/filter-ops.d.ts +1 -1
- package/dist/agent-tools/filter-ops.js +1 -3
- package/dist/agent-tools/tool-dispatch.js +2 -1
- package/dist/audit/handlers/list.query.js +9 -5
- package/dist/auth-email-password/bootstrap.d.ts +8 -5
- package/dist/auth-email-password/bootstrap.js +12 -4
- package/dist/auth-email-password/email-rate-limits.d.ts +4 -0
- package/dist/auth-email-password/email-rate-limits.js +13 -0
- package/dist/auth-email-password/email-templates.d.ts +4 -26
- package/dist/auth-email-password/email-templates.js +9 -101
- package/dist/auth-email-password/feature.js +24 -6
- package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +6 -2
- package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
- package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.js +6 -2
- package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-account-unlock.write.js +2 -4
- package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-email-verification.write.js +2 -4
- package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-password-reset.write.js +2 -4
- package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.js +3 -4
- package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-request.write.js +2 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +2 -3
- package/dist/auth-email-password/handlers/token-request-handler.js +2 -2
- package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
- package/dist/auth-email-password/index.d.ts +2 -2
- package/dist/auth-email-password/index.js +1 -1
- package/dist/auth-email-password/magic-link-mail.d.ts +3 -2
- package/dist/auth-email-password/magic-link-mail.js +19 -14
- package/dist/auth-email-password/mail-notifications.d.ts +32 -0
- package/dist/auth-email-password/mail-notifications.js +116 -0
- package/dist/auth-email-password/signup-activation.d.ts +1 -1
- package/dist/auth-email-password/signup-activation.js +2 -3
- package/dist/auth-email-password/web/auth-gate.js +16 -4
- package/dist/auth-foundation/types.js +2 -8
- package/dist/auth-mfa/feature.js +4 -4
- package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
- package/dist/auth-mfa/hmac-token-codec.js +4 -4
- package/dist/auth-mfa/mfa-challenge-token.js +3 -3
- package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -3
- package/dist/auth-mfa/mfa-setup-token.js +8 -8
- package/dist/billing-foundation/constants.d.ts +3 -3
- package/dist/billing-foundation/constants.js +5 -4
- package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +32 -14
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +74 -32
- package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +3 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.js +21 -2
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +3 -3
- package/dist/billing-foundation/consumer-protection/mail-notifications.d.ts +8 -0
- package/dist/billing-foundation/consumer-protection/mail-notifications.js +95 -0
- package/dist/billing-foundation/consumer-protection/register-consent.js +2 -0
- package/dist/billing-foundation/consumer-protection/register-termination.js +11 -1
- package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +25 -13
- package/dist/billing-foundation/consumer-protection/termination-mail.js +11 -23
- package/dist/billing-foundation/consumer-protection/termination-notify.js +2 -2
- package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -3
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +6 -0
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +34 -0
- package/dist/billing-foundation/consumer-protection/termination-record.d.ts +2 -0
- package/dist/billing-foundation/consumer-protection/termination-record.js +7 -1
- package/dist/billing-foundation/consumer-protection/termination-request.js +31 -21
- package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
- package/dist/billing-foundation/consumer-protection/termination-texts.js +6 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +10 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.js +26 -0
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +11 -5
- package/dist/billing-foundation/handlers/process-event.write.js +12 -8
- package/dist/billing-foundation/handlers/process-payment-event.write.js +1 -1
- package/dist/billing-foundation/i18n.js +3 -0
- package/dist/billing-foundation/subscription-tier-sync.d.ts +7 -1
- package/dist/billing-foundation/subscription-tier-sync.js +16 -9
- package/dist/billing-foundation/types.d.ts +7 -2
- package/dist/cap-counter/calendar-cap-usage-reservation.js +2 -0
- package/dist/cap-counter/enforce-cap.d.ts +18 -1
- package/dist/cap-counter/enforce-cap.js +49 -6
- package/dist/cap-counter/index.d.ts +1 -1
- package/dist/cap-counter/index.js +1 -1
- package/dist/cap-counter/with-cap-enforcement.d.ts +2 -0
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -1
- package/dist/cap-overview/web/cap-usage-cell.js +5 -4
- package/dist/channel-email/email-channel.d.ts +2 -2
- package/dist/channel-email/email-channel.js +101 -26
- package/dist/channel-email/feature.js +3 -3
- package/dist/channel-email/index.d.ts +1 -1
- package/dist/channel-email/index.js +1 -1
- package/dist/channel-email/pii-guard.js +10 -0
- package/dist/channel-email/smtp-transport.js +13 -0
- package/dist/channel-email/types.d.ts +8 -0
- package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
- package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
- package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
- package/dist/config/handlers/reset.write.d.ts +1 -1
- package/dist/config/handlers/set.write.d.ts +1 -1
- package/dist/config/handlers/set.write.js +3 -4
- package/dist/config/resolver.d.ts +1 -0
- package/dist/config/resolver.js +11 -14
- package/dist/config/write-helpers.d.ts +2 -0
- package/dist/config/write-helpers.js +19 -0
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.js +29 -19
- package/dist/custom-fields/handlers/update-tenant-field.write.js +0 -1
- package/dist/custom-fields/lib/value-schema.js +10 -11
- package/dist/custom-fields/wire-user-data-rights.js +7 -6
- package/dist/data-retention/index.d.ts +1 -0
- package/dist/data-retention/index.js +1 -0
- package/dist/data-retention/run-retention-cleanup.js +6 -6
- package/dist/delivery/attachment-ref.d.ts +41 -0
- package/dist/delivery/attachment-ref.js +78 -0
- package/dist/delivery/channel-context.d.ts +8 -4
- package/dist/delivery/channel-context.js +94 -2
- package/dist/delivery/constants.d.ts +1 -0
- package/dist/delivery/constants.js +1 -0
- package/dist/delivery/delivery-service.d.ts +4 -1
- package/dist/delivery/delivery-service.js +38 -32
- package/dist/delivery/feature.js +1 -1
- package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
- package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
- package/dist/delivery/index.d.ts +5 -2
- package/dist/delivery/index.js +4 -1
- package/dist/delivery/jobs.js +66 -14
- package/dist/delivery/notify-factory.js +4 -1
- package/dist/delivery/testing.d.ts +4 -1
- package/dist/delivery/types.d.ts +26 -14
- package/dist/delivery/unsubscribe.js +4 -1
- package/dist/derivatives-sharp/render.js +1 -18
- package/dist/document-ingest-foundation/pages.js +4 -4
- package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
- package/dist/feature-toggles/toggle-sync-signal.js +4 -4
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-foundation/index.d.ts +1 -0
- package/dist/file-foundation/index.js +1 -0
- package/dist/file-foundation/read-file-ref-content.d.ts +27 -0
- package/dist/file-foundation/read-file-ref-content.js +51 -0
- package/dist/files-tenant-data/hooks.js +4 -0
- package/dist/folders/web/folder-manager.js +3 -2
- package/dist/form-draft/db/queries/cleanup.js +4 -4
- package/dist/form-draft/db/queries/draft-count.js +2 -2
- package/dist/form-draft/db/queries/owned-file-refs.js +2 -2
- package/dist/form-draft/handlers/discard.write.d.ts +1 -1
- package/dist/form-draft/handlers/save.write.d.ts +1 -1
- package/dist/form-draft/release-file-refs.js +3 -2
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +6 -6
- package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +1 -1
- package/dist/inbound-mail-foundation/oauth-access-token.js +8 -3
- package/dist/inbound-mail-foundation/oauth-state.js +7 -7
- package/dist/inbound-mail-foundation/types.js +4 -4
- package/dist/inbound-mail-foundation/watch-supervisor.js +24 -13
- package/dist/inbound-provider-imap/imap-client.js +4 -3
- package/dist/jobs/db/queries/stale-run-sweep.d.ts +1 -1
- package/dist/jobs/db/queries/stale-run-sweep.js +12 -5
- package/dist/jobs/feature.js +5 -3
- package/dist/jobs/handlers/list.query.js +3 -1
- package/dist/jobs/handlers/retry.write.d.ts +1 -1
- package/dist/jobs/handlers/stale-run-sweep.job.d.ts +1 -1
- package/dist/jobs/handlers/stale-run-sweep.job.js +2 -2
- package/dist/jobs/handlers/trigger.write.d.ts +1 -1
- package/dist/jobs/job-run-logger.js +32 -10
- package/dist/jobs/tenant-job-run-table.js +3 -2
- package/dist/ledger/entity.js +3 -1
- package/dist/ledger/recurring.js +1 -3
- package/dist/managed-pages/branding.d.ts +2 -0
- package/dist/managed-pages/branding.js +24 -2
- package/dist/managed-pages/feature.js +1 -0
- package/dist/managed-pages/handlers/set.write.d.ts +1 -1
- package/dist/managed-pages/i18n.js +1 -0
- package/dist/managed-pages/screens/branding-screen.js +8 -1
- package/dist/page-render/branding.d.ts +1 -0
- package/dist/page-render/branding.js +1 -0
- package/dist/page-render/markdown.js +1 -1
- package/dist/personal-access-tokens/feature.js +1 -1
- package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
- package/dist/personal-access-tokens/handlers/list.query.js +2 -1
- package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
- package/dist/personal-access-tokens/revoke-for-user.js +2 -1
- package/dist/renderer-foundation/api.d.ts +13 -13
- package/dist/renderer-foundation/api.js +1 -1
- package/dist/renderer-foundation/constants.js +2 -4
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/renderer-foundation/index.d.ts +2 -1
- package/dist/renderer-foundation/index.js +1 -0
- package/dist/renderer-foundation/types.d.ts +94 -32
- package/dist/renderer-pdf/feature.js +7 -1
- package/dist/renderer-pdf/render-table-pdf.d.ts +27 -33
- package/dist/renderer-pdf/render-table-pdf.js +95 -34
- package/dist/secrets/feature.js +4 -3
- package/dist/secrets/handlers/delete.write.d.ts +1 -1
- package/dist/secrets/handlers/set.write.d.ts +1 -1
- package/dist/secrets/secrets-context.js +15 -3
- package/dist/sessions/db/queries/cleanup.js +2 -2
- package/dist/sessions/feature.js +1 -1
- package/dist/sessions/handlers/list.query.js +2 -1
- package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke.write.d.ts +1 -1
- package/dist/shared/attachment-file-name.d.ts +3 -0
- package/dist/shared/attachment-file-name.js +35 -0
- package/dist/shared/chunked-entity-migration.d.ts +1 -0
- package/dist/shared/chunked-entity-migration.js +1 -1
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +3 -0
- package/dist/shared/is-tenant-db.js +1 -1
- package/dist/shared/mail-template-body.d.ts +7 -0
- package/dist/shared/mail-template-body.js +27 -0
- package/dist/shared/sharp-input-limits.d.ts +6 -0
- package/dist/shared/sharp-input-limits.js +22 -0
- package/dist/shared/signup-handover.js +2 -2
- package/dist/subscription-stripe/plugin-methods.js +18 -8
- package/dist/subscription-stripe/verify-webhook.d.ts +1 -1
- package/dist/tags/handlers/assign-tag.write.js +3 -1
- package/dist/tags/web/tag-chip.js +2 -0
- package/dist/template-resolver/api.d.ts +30 -2
- package/dist/template-resolver/api.js +42 -6
- package/dist/template-resolver/compile-document-body.d.ts +12 -0
- package/dist/template-resolver/compile-document-body.js +205 -0
- package/dist/template-resolver/constants.d.ts +13 -4
- package/dist/template-resolver/constants.js +32 -10
- package/dist/template-resolver/document-sanitizer.d.ts +32 -0
- package/dist/template-resolver/document-sanitizer.js +201 -0
- package/dist/template-resolver/document-schema.d.ts +21 -0
- package/dist/template-resolver/document-schema.js +316 -0
- package/dist/template-resolver/feature.d.ts +20 -0
- package/dist/template-resolver/feature.js +53 -6
- package/dist/template-resolver/handlers/bundle-import.d.ts +20 -0
- package/dist/template-resolver/handlers/bundle-import.js +147 -0
- package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-set.write.js +25 -2
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +19 -0
- package/dist/template-resolver/handlers/copy-locale.write.js +134 -0
- package/dist/template-resolver/handlers/diff-to-system.query.d.ts +22 -0
- package/dist/template-resolver/handlers/diff-to-system.query.js +30 -0
- package/dist/template-resolver/handlers/discard-draft.write.d.ts +13 -0
- package/dist/template-resolver/handlers/discard-draft.write.js +37 -0
- package/dist/template-resolver/handlers/export-bundle.query.d.ts +35 -0
- package/dist/template-resolver/handlers/export-bundle.query.js +118 -0
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +2 -0
- package/dist/template-resolver/handlers/find-by-id.query.js +9 -6
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +39 -0
- package/dist/template-resolver/handlers/import-bundle-system.write.js +36 -0
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +40 -0
- package/dist/template-resolver/handlers/import-bundle.write.js +19 -0
- package/dist/template-resolver/handlers/layout-source.query.d.ts +31 -0
- package/dist/template-resolver/handlers/layout-source.query.js +87 -0
- package/dist/template-resolver/handlers/list.query.d.ts +4 -1
- package/dist/template-resolver/handlers/list.query.js +9 -8
- package/dist/template-resolver/handlers/override-diff.d.ts +20 -0
- package/dist/template-resolver/handlers/override-diff.js +41 -0
- package/dist/template-resolver/handlers/overview.query.d.ts +65 -0
- package/dist/template-resolver/handlers/overview.query.js +205 -0
- package/dist/template-resolver/handlers/preview.query.d.ts +12 -0
- package/dist/template-resolver/handlers/preview.query.js +24 -0
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +8 -0
- package/dist/template-resolver/handlers/reset-to-system.write.js +50 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +8 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.js +32 -0
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-remove.write.js +44 -0
- package/dist/template-resolver/handlers/resource-shared.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-shared.js +14 -0
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +13 -0
- package/dist/template-resolver/handlers/resource-upload.write.js +98 -0
- package/dist/template-resolver/handlers/send-test.write.d.ts +13 -0
- package/dist/template-resolver/handlers/send-test.write.js +108 -0
- package/dist/template-resolver/handlers/set.write.d.ts +5 -2
- package/dist/template-resolver/handlers/set.write.js +25 -3
- package/dist/template-resolver/handlers/shared.d.ts +9 -5
- package/dist/template-resolver/handlers/shared.js +6 -2
- package/dist/template-resolver/handlers/template-gates.d.ts +36 -0
- package/dist/template-resolver/handlers/template-gates.js +142 -0
- package/dist/template-resolver/handlers/template-write.d.ts +45 -0
- package/dist/template-resolver/handlers/template-write.js +141 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +49 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.js +112 -0
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +27 -2
- package/dist/template-resolver/handlers/toggle-status.write.js +47 -15
- package/dist/template-resolver/handlers/upsert-system.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-system.write.js +14 -59
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-tenant.write.js +11 -62
- package/dist/template-resolver/handlers/usage.query.d.ts +17 -0
- package/dist/template-resolver/handlers/usage.query.js +24 -0
- package/dist/template-resolver/handlers/validate.query.d.ts +74 -0
- package/dist/template-resolver/handlers/validate.query.js +80 -0
- package/dist/template-resolver/index.d.ts +19 -1
- package/dist/template-resolver/index.js +17 -1
- package/dist/template-resolver/layout-appearance.d.ts +10 -0
- package/dist/template-resolver/layout-appearance.js +26 -0
- package/dist/template-resolver/layout-regions.d.ts +23 -0
- package/dist/template-resolver/layout-regions.js +125 -0
- package/dist/template-resolver/linked-resources.d.ts +17 -0
- package/dist/template-resolver/linked-resources.js +75 -0
- package/dist/template-resolver/pdf-constants.d.ts +7 -0
- package/dist/template-resolver/pdf-constants.js +8 -0
- package/dist/template-resolver/preview-display-html.d.ts +7 -0
- package/dist/template-resolver/preview-display-html.js +27 -0
- package/dist/template-resolver/print-document.d.ts +1 -0
- package/dist/template-resolver/print-document.js +55 -0
- package/dist/template-resolver/qualified-names.d.ts +34 -0
- package/dist/template-resolver/qualified-names.js +34 -0
- package/dist/template-resolver/record-mapping.d.ts +2 -0
- package/dist/template-resolver/record-mapping.js +21 -0
- package/dist/template-resolver/resource-image.d.ts +15 -0
- package/dist/template-resolver/resource-image.js +34 -0
- package/dist/template-resolver/seeding.d.ts +14 -1
- package/dist/template-resolver/seeding.js +125 -18
- package/dist/template-resolver/store-template-resource.d.ts +64 -0
- package/dist/template-resolver/store-template-resource.js +264 -0
- package/dist/template-resolver/table.d.ts +150 -8
- package/dist/template-resolver/table.js +20 -2
- package/dist/template-resolver/template-bundle.d.ts +36 -0
- package/dist/template-resolver/template-bundle.js +60 -0
- package/dist/template-resolver/template-diff.d.ts +7 -0
- package/dist/template-resolver/template-diff.js +68 -0
- package/dist/template-resolver/template-draft.d.ts +51 -0
- package/dist/template-resolver/template-draft.js +72 -0
- package/dist/template-resolver/template-meta.d.ts +85 -0
- package/dist/template-resolver/template-meta.js +145 -0
- package/dist/template-resolver/template-preview.d.ts +103 -0
- package/dist/template-resolver/template-preview.js +418 -0
- package/dist/template-resolver/template-resources.d.ts +19 -0
- package/dist/template-resolver/template-resources.js +73 -0
- package/dist/template-resolver/template-usage.d.ts +16 -0
- package/dist/template-resolver/template-usage.js +35 -0
- package/dist/template-resolver/template-validation.d.ts +22 -0
- package/dist/template-resolver/template-validation.js +173 -0
- package/dist/template-resolver/testing.js +5 -5
- package/dist/template-resolver/user-content-table.d.ts +14 -8
- package/dist/template-resolver/web/client-plugin.d.ts +1 -0
- package/dist/template-resolver/web/client-plugin.js +12 -7
- package/dist/template-resolver/web/document-editor-extensions.d.ts +18 -0
- package/dist/template-resolver/web/document-editor-extensions.js +145 -0
- package/dist/template-resolver/web/document-editor.d.ts +4 -0
- package/dist/template-resolver/web/document-editor.js +17 -0
- package/dist/template-resolver/web/document-tiptap-editor.d.ts +3 -0
- package/dist/template-resolver/web/document-tiptap-editor.js +492 -0
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +41 -0
- package/dist/template-resolver/web/document-toolbar-blocks.js +19 -0
- package/dist/template-resolver/web/editor-handle.d.ts +3 -0
- package/dist/template-resolver/web/editor-handle.js +24 -0
- package/dist/template-resolver/web/i18n.js +91 -0
- package/dist/template-resolver/web/index.d.ts +5 -0
- package/dist/template-resolver/web/index.js +5 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.js +16 -0
- package/dist/template-resolver/web/placeholder-line-editor.d.ts +17 -0
- package/dist/template-resolver/web/placeholder-line-editor.js +94 -0
- package/dist/template-resolver/web/placeholder-text.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-text.js +34 -0
- package/dist/template-resolver/web/use-stable-by-key.d.ts +1 -0
- package/dist/template-resolver/web/use-stable-by-key.js +13 -0
- package/dist/tenant/handlers/add-member.write.d.ts +1 -1
- package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
- package/dist/tenant/handlers/create.write.d.ts +1 -1
- package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
- package/dist/tenant/handlers/team-list.query.js +3 -2
- package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update.write.d.ts +1 -1
- package/dist/tenant/seeding.js +31 -6
- package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +2 -2
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +2 -1
- package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
- package/dist/tier-engine/feature.js +9 -3
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +1 -1
- package/dist/user/db/queries/stream-tenant-backfill.js +4 -4
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/detail.query.js +2 -1
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user/resolve-display-names.js +5 -9
- package/dist/user/seeding.js +20 -1
- package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
- package/dist/user-data-rights/email-templates.d.ts +1 -29
- package/dist/user-data-rights/email-templates.js +5 -106
- package/dist/user-data-rights/feature.js +53 -35
- package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +2 -2
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.js +5 -7
- package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
- package/dist/user-data-rights/lib/default-mailers.d.ts +15 -7
- package/dist/user-data-rights/lib/default-mailers.js +100 -48
- package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
- package/dist/user-data-rights/mail-notifications.d.ts +13 -0
- package/dist/user-data-rights/mail-notifications.js +118 -0
- package/dist/user-data-rights/web/privacy-center-screen.js +2 -1
- package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
- package/dist/user-profile/web/profile-screen.js +2 -1
- package/dist/waitlist/feature.js +10 -0
- package/dist/waitlist/handlers/invite.write.d.ts +1 -1
- package/dist/waitlist/handlers/reject.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.js +3 -4
- package/dist/waitlist/mail.d.ts +19 -6
- package/dist/waitlist/mail.js +50 -14
- package/dist/waitlist/options.js +1 -1
- package/dist/workflow-runner/event-trigger.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +7 -2
- package/package.json +37 -17
- package/src/admin-shell/changes.json +6 -0
- package/src/audit/changes.json +6 -0
- package/src/auth-email-password/changes.json +51 -0
- package/src/billing-foundation/changes.json +43 -0
- package/src/cap-counter/changes.json +6 -0
- package/src/channel-email/changes.json +19 -0
- package/src/config/changes.json +6 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/custom-fields/changes.json +6 -0
- package/src/data-retention/changes.json +6 -0
- package/src/delivery/changes.json +66 -0
- package/src/file-foundation/changes.json +6 -0
- package/src/files-tenant-data/changes.json +6 -0
- package/src/inbound-mail-foundation/changes.json +6 -0
- package/src/jobs/changes.json +19 -0
- package/src/ledger/changes.json +7 -0
- package/src/managed-pages/changes.json +6 -0
- package/src/renderer-foundation/changes.json +39 -0
- package/src/renderer-pdf/changes.json +12 -0
- package/src/secrets/changes.json +6 -0
- package/src/shared/changes.json +6 -0
- package/src/subscription-stripe/changes.json +6 -0
- package/src/template-resolver/changes.json +186 -0
- package/src/tier-engine/changes.json +6 -0
- package/src/user/changes.json +6 -0
- package/src/user-data-rights/changes.json +13 -0
- package/src/user-profile/changes.json +1 -1
- package/src/waitlist/changes.json +7 -0
- package/src/workflow-runner/changes.json +6 -0
- package/dist/renderer-simple/feature.d.ts +0 -10
- package/dist/renderer-simple/feature.js +0 -42
- package/dist/renderer-simple/index.d.ts +0 -3
- package/dist/renderer-simple/index.js +0 -2
- package/dist/renderer-simple/mail-blocks.d.ts +0 -21
- package/dist/renderer-simple/mail-blocks.js +0 -107
- package/dist/renderer-simple/resolve-variables.d.ts +0 -7
- package/dist/renderer-simple/resolve-variables.js +0 -47
- package/dist/renderer-simple/simple-renderer.d.ts +0 -24
- package/dist/renderer-simple/simple-renderer.js +0 -200
- package/src/renderer-simple/changes.json +0 -27
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// @runtime client
|
|
3
|
+
//
|
|
4
|
+
// tiptap-backed implementation behind DocumentContentEditor's lazy() boundary —
|
|
5
|
+
// never import this file statically, tiptap must only load when a "document"
|
|
6
|
+
// collection is rendered. Variables are inserted as `variable` nodes only,
|
|
7
|
+
// never as `{{name}}` text, so the sanitizer's schema keeps them.
|
|
8
|
+
import { TextareaContentEditor, usePrimitives, useTranslation, VariableChips, } from "@cosmicdrift/kumiko-renderer";
|
|
9
|
+
import { cn } from "@cosmicdrift/kumiko-renderer-web";
|
|
10
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
11
|
+
import { EditorContent, useEditor, useEditorState } from "@tiptap/react";
|
|
12
|
+
import { BubbleMenu } from "@tiptap/react/menus";
|
|
13
|
+
import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold as BoldIcon, Braces, ChevronDown, Image as ImageIcon, Italic as ItalicIcon, Link as LinkIcon, List, ListOrdered, Minus, MousePointerClick, Repeat, Scissors, Table as TableIcon, Underline as UnderlineIcon, } from "lucide-react";
|
|
14
|
+
import { Fragment, useEffect, useMemo, useState } from "react";
|
|
15
|
+
import { createPortal } from "react-dom";
|
|
16
|
+
import { DOCUMENT_TEXT_ALIGNMENTS, isAllowedDocumentLinkHref, isDocumentPlaceholderHref, isDocumentReferenceName, isDocumentResourceKey, } from "../document-schema.js";
|
|
17
|
+
import { createDocumentEditorExtensions, DOCUMENT_BLOCK_ATTRIBUTE, } from "./document-editor-extensions.js";
|
|
18
|
+
import { MAIL_DOCUMENT_BLOCKS, } from "./document-toolbar-blocks.js";
|
|
19
|
+
import { createEditorHandle } from "./editor-handle.js";
|
|
20
|
+
import { useStableByKey } from "./use-stable-by-key.js";
|
|
21
|
+
const NO_LABELS = {};
|
|
22
|
+
const NO_NAMES = [];
|
|
23
|
+
const NO_PREVIEWS = {};
|
|
24
|
+
function previewsKey(previews) {
|
|
25
|
+
return Object.entries(previews)
|
|
26
|
+
.map(([key, { src, caption }]) => `${key}|${src.length}|${src.slice(-24)}|${caption ?? ""}`)
|
|
27
|
+
.join("\n");
|
|
28
|
+
}
|
|
29
|
+
// Blocks that stay in the canvas toolbar; the rest moves into the overflow menu.
|
|
30
|
+
const CANVAS_INLINE_BLOCKS = ["image", "table"];
|
|
31
|
+
const BLOCK_PANEL = {
|
|
32
|
+
image: "image",
|
|
33
|
+
button: "button",
|
|
34
|
+
repeat: "repeat",
|
|
35
|
+
align: "align",
|
|
36
|
+
};
|
|
37
|
+
const ALIGNMENT_ICONS = {
|
|
38
|
+
left: _jsx(AlignLeft, { size: 16 }),
|
|
39
|
+
center: _jsx(AlignCenter, { size: 16 }),
|
|
40
|
+
right: _jsx(AlignRight, { size: 16 }),
|
|
41
|
+
justify: _jsx(AlignJustify, { size: 16 }),
|
|
42
|
+
};
|
|
43
|
+
const ALIGNMENT_LABEL_KEYS = {
|
|
44
|
+
left: "template-resolver.documentEditor.alignLeft",
|
|
45
|
+
center: "template-resolver.documentEditor.alignCenter",
|
|
46
|
+
right: "template-resolver.documentEditor.alignRight",
|
|
47
|
+
justify: "template-resolver.documentEditor.alignJustify",
|
|
48
|
+
};
|
|
49
|
+
// print-document moves the leading rows flagged repeatHeader into thead, so the
|
|
50
|
+
// toggle reads and writes the table's first row.
|
|
51
|
+
function findRepeatTable(editor) {
|
|
52
|
+
const { $from } = editor.state.selection;
|
|
53
|
+
for (let depth = $from.depth; depth > 0; depth--) {
|
|
54
|
+
const node = $from.node(depth);
|
|
55
|
+
if (node.type.name !== "table")
|
|
56
|
+
continue;
|
|
57
|
+
const lists = [];
|
|
58
|
+
node.forEach((row) => {
|
|
59
|
+
const each = row.attrs["each"];
|
|
60
|
+
if (typeof each === "string" && each !== "" && !lists.includes(each))
|
|
61
|
+
lists.push(each);
|
|
62
|
+
});
|
|
63
|
+
if (lists.length === 0)
|
|
64
|
+
return null;
|
|
65
|
+
return {
|
|
66
|
+
lists,
|
|
67
|
+
headerRepeats: node.firstChild?.attrs["repeatHeader"] === true,
|
|
68
|
+
firstRowPos: $from.start(depth),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function toggleRepeatHeader(editor) {
|
|
74
|
+
const found = findRepeatTable(editor);
|
|
75
|
+
if (found === null)
|
|
76
|
+
return;
|
|
77
|
+
editor
|
|
78
|
+
.chain()
|
|
79
|
+
.focus()
|
|
80
|
+
.command(({ tr }) => {
|
|
81
|
+
const row = tr.doc.nodeAt(found.firstRowPos);
|
|
82
|
+
if (row === null)
|
|
83
|
+
return false;
|
|
84
|
+
tr.setNodeMarkup(found.firstRowPos, undefined, {
|
|
85
|
+
...row.attrs,
|
|
86
|
+
repeatHeader: !found.headerRepeats,
|
|
87
|
+
});
|
|
88
|
+
return true;
|
|
89
|
+
})
|
|
90
|
+
.run();
|
|
91
|
+
}
|
|
92
|
+
const PARAGRAPH_STYLES = ["paragraph", "1", "2", "3"];
|
|
93
|
+
const HEADING_LEVELS = { "1": 1, "2": 2, "3": 3 };
|
|
94
|
+
const PLACEHOLDER_HREF = /^\{\{([^{}]+)\}\}$/;
|
|
95
|
+
function buttonTargetLabel(href, labels) {
|
|
96
|
+
const name = PLACEHOLDER_HREF.exec(href)?.[1];
|
|
97
|
+
if (name === undefined)
|
|
98
|
+
return href;
|
|
99
|
+
return Object.hasOwn(labels, name) ? (labels[name] ?? name) : name;
|
|
100
|
+
}
|
|
101
|
+
// The bubble menu registers its plugin once per mount, so a rebuilt editor needs a fresh mount.
|
|
102
|
+
const editorMountKeys = new WeakMap();
|
|
103
|
+
let lastMountKey = 0;
|
|
104
|
+
function mountKeyOf(editor) {
|
|
105
|
+
const known = editorMountKeys.get(editor);
|
|
106
|
+
if (known !== undefined)
|
|
107
|
+
return known;
|
|
108
|
+
lastMountKey += 1;
|
|
109
|
+
const created = lastMountKey;
|
|
110
|
+
editorMountKeys.set(editor, created);
|
|
111
|
+
return created;
|
|
112
|
+
}
|
|
113
|
+
// Board .hb: a small bar that straddles the block's top frame line on the right; above a table it sits fully
|
|
114
|
+
// outside so the header row stays readable.
|
|
115
|
+
const BUBBLE_BAR_CLASS = "flex w-max items-center gap-1 rounded border border-input bg-popover p-px text-xs [&_button]:h-5 [&_button]:px-1.5 [&_button]:text-[10px]";
|
|
116
|
+
function bubbleOffset(editor) {
|
|
117
|
+
return ({ rects }) => ({
|
|
118
|
+
mainAxis: findRepeatTable(editor) !== null ? 2 : -rects.floating.height / 2,
|
|
119
|
+
crossAxis: -8,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const BUBBLE_MENU_PLUGIN_KEY = "documentBlockOptions";
|
|
123
|
+
// The options bar belongs to the block frame, not to the caret inside it.
|
|
124
|
+
function selectedBlockReference(editor) {
|
|
125
|
+
const { node } = editor.view.domAtPos(editor.state.selection.from);
|
|
126
|
+
const start = node instanceof Element ? node : node.parentElement;
|
|
127
|
+
const block = start?.closest(`[${DOCUMENT_BLOCK_ATTRIBUTE}], table`);
|
|
128
|
+
if (block === null || block === undefined)
|
|
129
|
+
return null;
|
|
130
|
+
return {
|
|
131
|
+
getBoundingClientRect: () => block.getBoundingClientRect(),
|
|
132
|
+
getClientRects: () => [block.getBoundingClientRect()],
|
|
133
|
+
contextElement: block,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// The bubble is first measured before React has filled it, so it sits at the wrong place until a second pass.
|
|
137
|
+
function repositionBubble(editor) {
|
|
138
|
+
if (editor.isDestroyed)
|
|
139
|
+
return;
|
|
140
|
+
editor.view.dispatch(editor.state.tr.setMeta(BUBBLE_MENU_PLUGIN_KEY, "updatePosition"));
|
|
141
|
+
}
|
|
142
|
+
function isParagraphStyle(value) {
|
|
143
|
+
return isOneOf(PARAGRAPH_STYLES, value);
|
|
144
|
+
}
|
|
145
|
+
function emptyParagraph() {
|
|
146
|
+
return { type: "paragraph" };
|
|
147
|
+
}
|
|
148
|
+
function repeatTableContent(listName) {
|
|
149
|
+
const headerRow = {
|
|
150
|
+
type: "tableRow",
|
|
151
|
+
content: [
|
|
152
|
+
{ type: "tableHeader", content: [emptyParagraph()] },
|
|
153
|
+
{ type: "tableHeader", content: [emptyParagraph()] },
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
const repeatRow = {
|
|
157
|
+
type: "tableRow",
|
|
158
|
+
attrs: { each: listName },
|
|
159
|
+
content: [
|
|
160
|
+
{ type: "tableCell", content: [emptyParagraph()] },
|
|
161
|
+
{ type: "tableCell", content: [emptyParagraph()] },
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
return { type: "table", content: [headerRow, repeatRow] };
|
|
165
|
+
}
|
|
166
|
+
function Separator({ compact = false }) {
|
|
167
|
+
return (_jsx("span", { "aria-hidden": "true", className: cn("h-5 w-px bg-border", compact ? "mx-0.5" : "mx-2") }));
|
|
168
|
+
}
|
|
169
|
+
function PanelActions({ onInsert, onRemoveLink, onCancel, }) {
|
|
170
|
+
const { Button } = usePrimitives();
|
|
171
|
+
const t = useTranslation();
|
|
172
|
+
return (_jsxs("div", { className: "flex gap-2", children: [onInsert && (_jsx(Button, { type: "button", size: "sm", onClick: onInsert, children: t("template-resolver.documentEditor.insert") })), onRemoveLink && (_jsx(Button, { type: "button", size: "sm", variant: "danger-ghost", onClick: onRemoveLink, children: t("template-resolver.documentEditor.removeLink") })), _jsx(Button, { type: "button", size: "sm", variant: "secondary", onClick: onCancel, children: t("template-resolver.documentEditor.cancel") })] }));
|
|
173
|
+
}
|
|
174
|
+
function NameSelect({ id, label, names, value, onChange }) {
|
|
175
|
+
const { Field, Input } = usePrimitives();
|
|
176
|
+
return (_jsx(Field, { id: id, label: label, children: _jsx(Input, { kind: "select", id: id, name: id, value: value, onChange: onChange, display: "dropdown", options: names.map((name) => ({ value: name, label: name })) }) }));
|
|
177
|
+
}
|
|
178
|
+
function InsertPanel({ editor, editorId, kind, variables, onClose }) {
|
|
179
|
+
const { Button, Field, Input } = usePrimitives();
|
|
180
|
+
const t = useTranslation();
|
|
181
|
+
const [href, setHref] = useState(() => {
|
|
182
|
+
const current = editor.getAttributes("link")["href"];
|
|
183
|
+
return typeof current === "string" ? current : "";
|
|
184
|
+
});
|
|
185
|
+
const [resource, setResource] = useState("");
|
|
186
|
+
const [alt, setAlt] = useState("");
|
|
187
|
+
const [buttonText, setButtonText] = useState(() => t("template-resolver.documentEditor.buttonDefaultText"));
|
|
188
|
+
const [chosenName, setChosenName] = useState("");
|
|
189
|
+
const [error, setError] = useState(null);
|
|
190
|
+
const referenceNames = variables.filter(isDocumentReferenceName);
|
|
191
|
+
const buttonTargets = referenceNames.filter((name) => isDocumentPlaceholderHref(`{{${name}}}`));
|
|
192
|
+
const selectable = kind === "button" ? buttonTargets : referenceNames;
|
|
193
|
+
const selectedName = selectable.includes(chosenName) ? chosenName : (selectable[0] ?? "");
|
|
194
|
+
const insertAndClose = (content) => {
|
|
195
|
+
editor.chain().focus().insertContent(content).run();
|
|
196
|
+
onClose();
|
|
197
|
+
};
|
|
198
|
+
const submitLink = () => {
|
|
199
|
+
if (!isAllowedDocumentLinkHref(href)) {
|
|
200
|
+
setError(t("template-resolver.documentEditor.invalidLink"));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const chain = editor.chain().focus();
|
|
204
|
+
if (!editor.state.selection.empty)
|
|
205
|
+
chain.setLink({ href }).run();
|
|
206
|
+
else if (editor.isActive("link"))
|
|
207
|
+
chain.extendMarkRange("link").setLink({ href }).run();
|
|
208
|
+
else if (isDocumentPlaceholderHref(href))
|
|
209
|
+
// Placeholders are never inserted as {{name}} text; the link shows the variable's value.
|
|
210
|
+
chain
|
|
211
|
+
.insertContent({
|
|
212
|
+
type: "variable",
|
|
213
|
+
attrs: { name: href.slice(2, -2) },
|
|
214
|
+
marks: [{ type: "link", attrs: { href } }],
|
|
215
|
+
})
|
|
216
|
+
.run();
|
|
217
|
+
else
|
|
218
|
+
chain
|
|
219
|
+
.insertContent({ type: "text", text: href, marks: [{ type: "link", attrs: { href } }] })
|
|
220
|
+
.run();
|
|
221
|
+
onClose();
|
|
222
|
+
};
|
|
223
|
+
const submitImage = () => {
|
|
224
|
+
if (!isDocumentResourceKey(resource)) {
|
|
225
|
+
setError(t("template-resolver.documentEditor.invalidResourceKey"));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (/[<>]/.test(alt)) {
|
|
229
|
+
setError(t("template-resolver.documentEditor.invalidImageAlt"));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
insertAndClose({ type: "resource", attrs: { resource, alt: alt || null } });
|
|
233
|
+
};
|
|
234
|
+
const needsVariables = kind === "button" || kind === "repeat" || kind === "placeholder";
|
|
235
|
+
const noVariables = needsVariables && selectable.length === 0;
|
|
236
|
+
const idPrefix = `${editorId}-panel`;
|
|
237
|
+
return (_jsxs("div", { className: "flex flex-col gap-2 border-b border-input bg-muted/40 p-2", children: [noVariables && (_jsx("p", { className: "text-sm text-muted-foreground", children: t("template-resolver.documentEditor.noVariables") })), kind === "link" && (_jsx(Field, { id: `${idPrefix}-href`, label: t("template-resolver.documentEditor.linkHref"), children: _jsx(Input, { kind: "text", id: `${idPrefix}-href`, name: "href", value: href, onChange: setHref, placeholder: t("template-resolver.documentEditor.linkHrefPlaceholder") }) })), kind === "placeholder" && !noVariables && (_jsx("div", { className: "flex flex-wrap gap-1", children: _jsx(VariableChips, { variables: selectable, onInsert: (name) => insertAndClose({ type: "variable", attrs: { name } }) }) })), kind === "align" && (_jsx("div", { className: "flex flex-wrap gap-1", children: DOCUMENT_TEXT_ALIGNMENTS.map((alignment) => (_jsxs(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => {
|
|
238
|
+
editor
|
|
239
|
+
.chain()
|
|
240
|
+
.focus()
|
|
241
|
+
.updateAttributes("paragraph", { textAlign: alignment })
|
|
242
|
+
.updateAttributes("heading", { textAlign: alignment })
|
|
243
|
+
.run();
|
|
244
|
+
onClose();
|
|
245
|
+
}, children: [ALIGNMENT_ICONS[alignment], t(ALIGNMENT_LABEL_KEYS[alignment])] }, alignment))) })), kind === "image" && (_jsxs(_Fragment, { children: [_jsx(Field, { id: `${idPrefix}-resource`, label: t("template-resolver.documentEditor.resourceKey"), children: _jsx(Input, { kind: "text", id: `${idPrefix}-resource`, name: "resource", value: resource, onChange: setResource }) }), _jsx(Field, { id: `${idPrefix}-alt`, label: t("template-resolver.documentEditor.imageAlt"), children: _jsx(Input, { kind: "text", id: `${idPrefix}-alt`, name: "alt", value: alt, onChange: setAlt }) })] })), kind === "button" && !noVariables && (_jsxs(_Fragment, { children: [_jsx(NameSelect, { id: `${idPrefix}-target`, label: t("template-resolver.documentEditor.buttonTarget"), names: selectable, value: selectedName, onChange: setChosenName }), _jsx(Field, { id: `${idPrefix}-text`, label: t("template-resolver.documentEditor.buttonText"), children: _jsx(Input, { kind: "text", id: `${idPrefix}-text`, name: "text", value: buttonText, onChange: setButtonText }) })] })), kind === "repeat" && !noVariables && (_jsx(NameSelect, { id: `${idPrefix}-list`, label: t("template-resolver.documentEditor.repeatList"), names: selectable, value: selectedName, onChange: setChosenName })), error !== null && (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })), _jsx("div", { className: "flex flex-wrap gap-2", children: _jsx(PanelActions, { onCancel: onClose, ...(kind === "link" && { onInsert: submitLink }), ...(kind === "link" &&
|
|
246
|
+
editor.isActive("link") && {
|
|
247
|
+
onRemoveLink: () => {
|
|
248
|
+
editor.chain().focus().extendMarkRange("link").unsetLink().run();
|
|
249
|
+
onClose();
|
|
250
|
+
},
|
|
251
|
+
}), ...(kind === "image" && { onInsert: submitImage }), ...(kind === "button" &&
|
|
252
|
+
!noVariables && {
|
|
253
|
+
onInsert: () => insertAndClose({
|
|
254
|
+
type: "button",
|
|
255
|
+
attrs: { href: `{{${selectedName}}}` },
|
|
256
|
+
content: buttonText ? [{ type: "text", text: buttonText }] : [],
|
|
257
|
+
}),
|
|
258
|
+
}), ...(kind === "repeat" &&
|
|
259
|
+
!noVariables && { onInsert: () => insertAndClose(repeatTableContent(selectedName)) }) }) })] }));
|
|
260
|
+
}
|
|
261
|
+
export default function DocumentTiptapEditor({ id, value, onChange, variables, readOnly, blocks = MAIL_DOCUMENT_BLOCKS, variableLabels = NO_LABELS, systemVariables = NO_NAMES, resourcePreviews = NO_PREVIEWS, chrome = "default", toolbar = "full", renderPaper, toolbarHost, onEditorReady, }) {
|
|
262
|
+
const t = useTranslation();
|
|
263
|
+
const { ActionOverflowMenu, Button, Field, Input } = usePrimitives();
|
|
264
|
+
const [openPanel, setOpenPanel] = useState(null);
|
|
265
|
+
const [canvasScroller, setCanvasScroller] = useState(null);
|
|
266
|
+
const [showHtml, setShowHtml] = useState(false);
|
|
267
|
+
const canvas = chrome === "canvas";
|
|
268
|
+
const compact = toolbar === "compact" && !canvas;
|
|
269
|
+
const hosted = toolbarHost !== undefined;
|
|
270
|
+
const placeholderLabel = t("template-resolver.documentEditor.placeholder");
|
|
271
|
+
const imageLabel = t("template-resolver.documentEditor.image");
|
|
272
|
+
const imageBlockLabel = t("template-resolver.documentEditor.imageBlock");
|
|
273
|
+
const pageBreakLabel = t("template-resolver.documentEditor.pageBreak");
|
|
274
|
+
const buttonLabel = t("template-resolver.documentEditor.button");
|
|
275
|
+
const stableLabels = useStableByKey(variableLabels, JSON.stringify(variableLabels));
|
|
276
|
+
const stableSystem = useStableByKey(systemVariables, systemVariables.join("\n"));
|
|
277
|
+
const stablePreviews = useStableByKey(resourcePreviews, previewsKey(resourcePreviews));
|
|
278
|
+
const extensions = useMemo(() => createDocumentEditorExtensions({
|
|
279
|
+
placeholder: placeholderLabel,
|
|
280
|
+
image: imageLabel,
|
|
281
|
+
pageBreak: pageBreakLabel,
|
|
282
|
+
button: buttonLabel,
|
|
283
|
+
imageBlock: imageBlockLabel,
|
|
284
|
+
variableLabels: stableLabels,
|
|
285
|
+
systemVariables: stableSystem,
|
|
286
|
+
resourcePreviews: stablePreviews,
|
|
287
|
+
blockLabels: canvas,
|
|
288
|
+
}), [
|
|
289
|
+
placeholderLabel,
|
|
290
|
+
imageLabel,
|
|
291
|
+
imageBlockLabel,
|
|
292
|
+
pageBreakLabel,
|
|
293
|
+
buttonLabel,
|
|
294
|
+
stableLabels,
|
|
295
|
+
stableSystem,
|
|
296
|
+
stablePreviews,
|
|
297
|
+
canvas,
|
|
298
|
+
]);
|
|
299
|
+
// The node views are built from labels and previews, so a changed set
|
|
300
|
+
// rebuilds the editor; its content is re-read from `value`.
|
|
301
|
+
const editor = useEditor({
|
|
302
|
+
extensions,
|
|
303
|
+
content: value,
|
|
304
|
+
editable: !readOnly,
|
|
305
|
+
// Suspense (DocumentContentEditor's lazy boundary) speculatively mounts
|
|
306
|
+
// this component before committing it; immediate render would create an
|
|
307
|
+
// editor instance during that throwaway pass.
|
|
308
|
+
immediatelyRender: false,
|
|
309
|
+
editorProps: { attributes: { id } },
|
|
310
|
+
onCreate: ({ editor: created }) => onEditorReady?.(createEditorHandle(created)),
|
|
311
|
+
onUpdate: ({ editor: e }) => onChange(e.getHTML()),
|
|
312
|
+
}, [extensions]);
|
|
313
|
+
// @tiptap/react v3 does not re-render on selection-only transactions, so
|
|
314
|
+
// active states must come from a subscription.
|
|
315
|
+
const activeState = useEditorState({
|
|
316
|
+
editor,
|
|
317
|
+
selector: ({ editor: e }) => e === null
|
|
318
|
+
? null
|
|
319
|
+
: {
|
|
320
|
+
blockStyle: e.isActive("heading", { level: 1 })
|
|
321
|
+
? "1"
|
|
322
|
+
: e.isActive("heading", { level: 2 })
|
|
323
|
+
? "2"
|
|
324
|
+
: e.isActive("heading", { level: 3 })
|
|
325
|
+
? "3"
|
|
326
|
+
: "paragraph",
|
|
327
|
+
bold: e.isActive("bold"),
|
|
328
|
+
italic: e.isActive("italic"),
|
|
329
|
+
underline: e.isActive("underline"),
|
|
330
|
+
selectedBlock: e.isActive("resource")
|
|
331
|
+
? {
|
|
332
|
+
type: "resource",
|
|
333
|
+
detail: String(e.getAttributes("resource")["resource"]),
|
|
334
|
+
}
|
|
335
|
+
: e.isActive("button")
|
|
336
|
+
? { type: "button", detail: String(e.getAttributes("button")["href"]) }
|
|
337
|
+
: null,
|
|
338
|
+
html: showHtml ? e.getHTML() : "",
|
|
339
|
+
bulletList: e.isActive("bulletList"),
|
|
340
|
+
orderedList: e.isActive("orderedList"),
|
|
341
|
+
link: e.isActive("link"),
|
|
342
|
+
repeatTable: findRepeatTable(e),
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
// The entry loads asynchronously, so `value` arrives after useEditor read
|
|
346
|
+
// its initial content; the guard keeps in-flight typing from being clobbered.
|
|
347
|
+
// kumiko-lint-ignore no-raw-hooks DOM integration: syncs the controlled value into the imperative tiptap editor
|
|
348
|
+
useEffect(() => {
|
|
349
|
+
if (editor && value !== editor.getHTML()) {
|
|
350
|
+
editor.commands.setContent(value, { emitUpdate: false });
|
|
351
|
+
}
|
|
352
|
+
}, [value, editor]);
|
|
353
|
+
// kumiko-lint-ignore no-raw-hooks DOM integration: tiptap exposes editability only imperatively via setEditable
|
|
354
|
+
useEffect(() => {
|
|
355
|
+
editor?.setEditable(!readOnly);
|
|
356
|
+
}, [readOnly, editor]);
|
|
357
|
+
if (!editor)
|
|
358
|
+
return (_jsx(TextareaContentEditor, { id: id, value: value, onChange: onChange, variables: variables, readOnly: readOnly }));
|
|
359
|
+
const togglePanel = (kind) => setOpenPanel((current) => (current === kind ? null : kind));
|
|
360
|
+
const setBlockStyle = (style) => {
|
|
361
|
+
if (!isParagraphStyle(style))
|
|
362
|
+
return;
|
|
363
|
+
const chain = editor.chain().focus();
|
|
364
|
+
if (style === "paragraph")
|
|
365
|
+
chain.setParagraph().run();
|
|
366
|
+
else
|
|
367
|
+
chain.setHeading({ level: HEADING_LEVELS[style] }).run();
|
|
368
|
+
};
|
|
369
|
+
const runBlock = (block) => {
|
|
370
|
+
const panel = BLOCK_PANEL[block];
|
|
371
|
+
if (panel) {
|
|
372
|
+
togglePanel(panel);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const chain = editor.chain().focus();
|
|
376
|
+
if (block === "table")
|
|
377
|
+
chain.insertTable({ rows: 3, cols: 2, withHeaderRow: true }).run();
|
|
378
|
+
else if (block === "divider")
|
|
379
|
+
chain.insertContent({ type: "divider" }).run();
|
|
380
|
+
else if (block === "pageBreak")
|
|
381
|
+
chain.insertContent({ type: "pageBreak" }).run();
|
|
382
|
+
};
|
|
383
|
+
const blockButtons = {
|
|
384
|
+
image: { label: imageLabel, icon: _jsx(ImageIcon, { size: 16 }) },
|
|
385
|
+
table: { label: t("template-resolver.documentEditor.table"), icon: _jsx(TableIcon, { size: 16 }) },
|
|
386
|
+
button: {
|
|
387
|
+
label: t("template-resolver.documentEditor.button"),
|
|
388
|
+
icon: _jsx(MousePointerClick, { size: 16 }),
|
|
389
|
+
},
|
|
390
|
+
divider: { label: t("template-resolver.documentEditor.divider"), icon: _jsx(Minus, { size: 16 }) },
|
|
391
|
+
repeat: { label: t("template-resolver.documentEditor.repeat"), icon: _jsx(Repeat, { size: 16 }) },
|
|
392
|
+
pageBreak: { label: pageBreakLabel, icon: _jsx(Scissors, { size: 16 }) },
|
|
393
|
+
align: { label: t("template-resolver.documentEditor.align"), icon: _jsx(AlignLeft, { size: 16 }) },
|
|
394
|
+
};
|
|
395
|
+
const repeatTable = activeState?.repeatTable ?? null;
|
|
396
|
+
const repeatListLabel = repeatTable !== null && repeatTable.lists.length === 1
|
|
397
|
+
? (variableLabels[repeatTable.lists[0] ?? ""] ?? repeatTable.lists[0])
|
|
398
|
+
: undefined;
|
|
399
|
+
const repeatBarLabel = repeatListLabel === undefined
|
|
400
|
+
? t("template-resolver.documentEditor.repeat")
|
|
401
|
+
: t("template-resolver.documentEditor.repeatBarList", { list: repeatListLabel });
|
|
402
|
+
const paragraphStyleLabel = t("template-resolver.documentEditor.paragraphStyle");
|
|
403
|
+
const styleOptions = [
|
|
404
|
+
{ value: "paragraph", label: t("template-resolver.documentEditor.paragraph") },
|
|
405
|
+
{ value: "1", label: t("kumiko.contentEditor.heading1") },
|
|
406
|
+
{ value: "2", label: t("kumiko.contentEditor.heading2") },
|
|
407
|
+
{ value: "3", label: t("template-resolver.documentEditor.heading3") },
|
|
408
|
+
];
|
|
409
|
+
// Canvas chrome is one flat row: unframed ghost buttons, glyph plus text for the longer names.
|
|
410
|
+
const iconButton = (label, icon, isActive, onClick, text) => canvas ? (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", pressed: isActive, disabled: readOnly, onClick: onClick, ariaLabel: label, title: label, children: [icon, text] })) : (_jsx(Button, { type: "button", variant: isActive ? "primary" : "secondary", size: "icon", pressed: isActive, disabled: readOnly, onClick: onClick, ariaLabel: label, title: label, children: icon }));
|
|
411
|
+
const markContent = (letter, icon, className) => canvas ? _jsx("span", { className: className, children: letter }) : icon;
|
|
412
|
+
const selectedBlock = activeState?.selectedBlock ?? null;
|
|
413
|
+
const flatVariant = canvas ? "ghost" : "secondary";
|
|
414
|
+
const labelledButton = (spec) => {
|
|
415
|
+
const shared = {
|
|
416
|
+
type: "button",
|
|
417
|
+
variant: flatVariant,
|
|
418
|
+
disabled: readOnly,
|
|
419
|
+
onClick: spec.onClick,
|
|
420
|
+
...(spec.expanded !== undefined && { expanded: spec.expanded }),
|
|
421
|
+
};
|
|
422
|
+
return compact ? (_jsx(Button, { ...shared, size: "icon", ariaLabel: spec.label, title: spec.label, children: spec.icon }, spec.key)) : (_jsxs(Button, { ...shared, size: "sm", children: [spec.showIcon && spec.icon, spec.label, spec.trailingIcon] }, spec.key));
|
|
423
|
+
};
|
|
424
|
+
const editorContent = (_jsx(EditorContent, { editor: editor, className: cn(!canvas && "min-h-40 px-3 py-2 text-base outline-none md:text-sm", canvas && "text-sm", "[&_.ProseMirror-focused_.ProseMirror-selectednode]:ring-2 [&_.ProseMirror-focused_.ProseMirror-selectednode]:ring-primary", hosted
|
|
425
|
+
? "[&_.ProseMirror]:outline-none"
|
|
426
|
+
: "[&_.ProseMirror]:min-h-40 [&_.ProseMirror]:outline-none", "[&_ul]:list-disc [&_ol]:list-decimal [&_ul]:pl-6 [&_ol]:pl-6", "[&_h1]:text-xl [&_h1]:font-bold [&_h2]:text-lg [&_h2]:font-bold [&_h3]:text-base [&_h3]:font-bold", "[&_a]:underline [&_a]:text-primary", "[&_strong]:font-bold [&_em]:italic [&_u]:underline", canvas
|
|
427
|
+
? "[&_table]:mb-2 [&_table]:mt-6 [&_.ProseMirror>:first-child_table]:mt-0 [&_.ProseMirror>table:first-child]:mt-0 [&_td:last-child>p[data-size=small][data-align=right]]:whitespace-nowrap"
|
|
428
|
+
: "[&_table]:my-2", "[&_table]:w-full [&_table]:border-collapse", "[&_td]:border-b [&_td]:border-border [&_td]:px-2 [&_td]:py-1.5 [&_td>p]:m-0 [&_th>p]:m-0", "[&_th]:border-b [&_th]:border-border [&_th]:px-2 [&_th]:py-1.5 [&_th]:text-left [&_th]:text-xs [&_th]:font-medium [&_th]:text-muted-foreground", "[&_tr[data-each]>td]:bg-muted/50", "[&_[data-align=left]]:text-left [&_[data-align=center]]:text-center [&_[data-align=right]]:text-right [&_[data-align=justify]]:text-justify", "[&_p[data-size=small]]:text-xs [&_p[data-size=small]]:text-muted-foreground", "[&_a[data-button]]:inline-block [&_a[data-button]]:rounded-full [&_a[data-button]]:bg-primary [&_a[data-button]]:px-4 [&_a[data-button]]:py-1.5 [&_a[data-button]]:text-primary-foreground [&_a[data-button]]:no-underline", "[&_hr]:my-3 [&_hr]:border-input", "[&_.is-editor-empty]:before:pointer-events-none [&_.is-editor-empty]:before:float-left [&_.is-editor-empty]:before:h-0 [&_.is-editor-empty]:before:text-muted-foreground [&_.is-editor-empty]:before:content-[attr(data-placeholder)]") }));
|
|
429
|
+
// The canvas toolbar never scrolls sideways: the rarer blocks and the HTML view sit in the menu.
|
|
430
|
+
const menuBlocks = canvas && ActionOverflowMenu !== undefined
|
|
431
|
+
? blocks.filter((block) => !CANVAS_INLINE_BLOCKS.includes(block))
|
|
432
|
+
: [];
|
|
433
|
+
const inlineBlocks = blocks.filter((block) => !menuBlocks.includes(block));
|
|
434
|
+
const overflowItems = [
|
|
435
|
+
...menuBlocks.map((block) => ({
|
|
436
|
+
id: block,
|
|
437
|
+
label: blockButtons[block].label,
|
|
438
|
+
disabled: readOnly,
|
|
439
|
+
onSelect: () => runBlock(block),
|
|
440
|
+
})),
|
|
441
|
+
...(canvas && !hosted
|
|
442
|
+
? [
|
|
443
|
+
{
|
|
444
|
+
id: "html",
|
|
445
|
+
label: t("template-resolver.documentEditor.showHtml"),
|
|
446
|
+
onSelect: () => setShowHtml((current) => !current),
|
|
447
|
+
},
|
|
448
|
+
]
|
|
449
|
+
: []),
|
|
450
|
+
];
|
|
451
|
+
const toolbarBar = (_jsxs("div", { className: cn("flex items-center border-b border-input", canvas
|
|
452
|
+
? "min-h-9 flex-nowrap gap-0 overflow-hidden bg-background px-1.5 [&>*]:shrink-0 [&_button]:h-7 [&_button]:gap-1 [&_button]:px-1 [&_button]:text-xs"
|
|
453
|
+
: "flex-wrap gap-1 p-1"), children: [_jsx("div", {
|
|
454
|
+
// kumiko-lint-ignore raw-classname the toolbar paragraph select sits flush in the editor toolbar; ComboboxInput has no flat trigger variant
|
|
455
|
+
className: cn(!canvas && (compact ? "w-24" : "w-40"), canvas &&
|
|
456
|
+
"w-auto shrink-0 [&_button[aria-haspopup=listbox]]:border-transparent [&_button[aria-haspopup=listbox]]:bg-transparent [&_button[aria-haspopup=listbox]]:shadow-none"), children: _jsx(Field, { id: `${id}-paragraph-style`, label: paragraphStyleLabel, hideLabel: true, children: _jsx(Input, { kind: "select", id: `${id}-paragraph-style`, name: "paragraph-style", value: activeState?.blockStyle ?? "paragraph", onChange: setBlockStyle, options: styleOptions, display: "dropdown", disabled: readOnly }) }) }), _jsx(Separator, { compact: compact || canvas }), iconButton(t("kumiko.contentEditor.bold"), markContent("B", _jsx(BoldIcon, { size: 16 }), "font-bold"), activeState?.bold ?? false, () => editor.chain().focus().toggleBold().run()), iconButton(t("kumiko.contentEditor.italic"), markContent("I", _jsx(ItalicIcon, { size: 16 }), "italic"), activeState?.italic ?? false, () => editor.chain().focus().toggleItalic().run()), iconButton(t("template-resolver.documentEditor.underline"), markContent("U", _jsx(UnderlineIcon, { size: 16 }), "underline"), activeState?.underline ?? false, () => editor.chain().focus().toggleUnderline().run()), _jsx(Separator, { compact: compact || canvas }), iconButton(t("kumiko.contentEditor.bulletList"), _jsx(List, { size: 16 }), activeState?.bulletList ?? false, () => editor.chain().focus().toggleBulletList().run(), canvas ? undefined : t("template-resolver.documentEditor.list")), iconButton(t("kumiko.contentEditor.orderedList"), _jsx(ListOrdered, { size: 16 }), activeState?.orderedList ?? false, () => editor.chain().focus().toggleOrderedList().run()), iconButton(t("template-resolver.documentEditor.link"), _jsx(LinkIcon, { size: 16 }), activeState?.link ?? false, () => togglePanel("link")), _jsx(Separator, { compact: compact || canvas }), labelledButton({
|
|
457
|
+
label: t("template-resolver.documentEditor.insertPlaceholder"),
|
|
458
|
+
icon: _jsx(Braces, { size: 16 }),
|
|
459
|
+
showIcon: true,
|
|
460
|
+
trailingIcon: canvas ? _jsx(ChevronDown, { size: 12 }) : undefined,
|
|
461
|
+
expanded: openPanel === "placeholder",
|
|
462
|
+
onClick: () => togglePanel("placeholder"),
|
|
463
|
+
}), inlineBlocks.length > 0 && _jsx(Separator, { compact: compact || canvas }), inlineBlocks.map((block) => canvas ? (_jsx(Fragment, { children: iconButton(blockButtons[block].label, blockButtons[block].icon, false, () => runBlock(block)) }, block)) : (labelledButton({
|
|
464
|
+
key: block,
|
|
465
|
+
label: blockButtons[block].label,
|
|
466
|
+
icon: blockButtons[block].icon,
|
|
467
|
+
showIcon: true,
|
|
468
|
+
onClick: () => runBlock(block),
|
|
469
|
+
}))), overflowItems.length > 0 && ActionOverflowMenu !== undefined && (_jsx("span", { className: "ml-auto shrink-0", children: _jsx(ActionOverflowMenu, { items: overflowItems, label: t("template-resolver.documentEditor.more"), testId: `${id}-toolbar-more` }) }))] }));
|
|
470
|
+
const bubbleMenu = (_jsx(BubbleMenu, { pluginKey: BUBBLE_MENU_PLUGIN_KEY, editor: editor, options: {
|
|
471
|
+
strategy: "fixed",
|
|
472
|
+
placement: "top-end",
|
|
473
|
+
offset: bubbleOffset(editor),
|
|
474
|
+
scrollTarget: canvasScroller ?? window,
|
|
475
|
+
onShow: () => requestAnimationFrame(() => repositionBubble(editor)),
|
|
476
|
+
}, getReferencedVirtualElement: () => selectedBlockReference(editor), shouldShow: ({ editor: e, element }) =>
|
|
477
|
+
// A leading image is selected by default; the bubble belongs to a deliberate selection.
|
|
478
|
+
(e.isFocused || element.contains(document.activeElement)) &&
|
|
479
|
+
(e.isActive("resource") || e.isActive("button") || findRepeatTable(e) !== null), children: selectedBlock !== null ? (_jsxs("div", { className: BUBBLE_BAR_CLASS, "data-testid": "document-block-options", children: [_jsxs("span", { className: "px-1.5 text-[10px] text-muted-foreground", children: [selectedBlock.type === "resource"
|
|
480
|
+
? t("template-resolver.documentEditor.resourceKey")
|
|
481
|
+
: t("template-resolver.documentEditor.buttonTarget"), ": ", selectedBlock.type === "button" ? (_jsx("strong", { children: buttonTargetLabel(selectedBlock.detail, variableLabels) })) : (_jsx("code", { children: selectedBlock.detail }))] }), _jsx(Button, { type: "button", size: "sm", variant: "danger-ghost", onClick: () => {
|
|
482
|
+
editor.chain().focus().deleteNode(selectedBlock.type).run();
|
|
483
|
+
}, children: t("template-resolver.documentEditor.removeBlock") })] })) : (repeatTable !== null && (_jsxs("div", { "data-testid": "document-repeat-bar", className: BUBBLE_BAR_CLASS, children: [_jsx("span", { className: "px-1.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: repeatBarLabel }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", pressed: repeatTable.headerRepeats, onClick: () => toggleRepeatHeader(editor), children: t("template-resolver.documentEditor.repeatHeader") })] }))) }, mountKeyOf(editor)));
|
|
484
|
+
const insertPanel = openPanel !== null && !readOnly && (_jsx(InsertPanel, { editor: editor, editorId: id, kind: openPanel, variables: variables, onClose: () => setOpenPanel(null) }, openPanel));
|
|
485
|
+
if (hosted) {
|
|
486
|
+
return (_jsxs(_Fragment, { children: [toolbarHost !== null &&
|
|
487
|
+
createPortal(_jsxs(_Fragment, { children: [toolbarBar, insertPanel] }), toolbarHost), !readOnly && bubbleMenu, editorContent] }));
|
|
488
|
+
}
|
|
489
|
+
return (_jsxs("div", { className: cn(canvas ? "flex min-h-0 flex-1 flex-col" : "rounded-md border border-input bg-transparent"), children: [!hosted && toolbarBar, canvas && !readOnly && bubbleMenu, canvas && !hosted && showHtml && (_jsx("section", { "aria-label": t("template-resolver.documentEditor.htmlSource"), children: _jsx("pre", { className: "h-40 overflow-auto whitespace-pre-wrap break-all border-b border-input bg-muted p-2 font-mono text-xs", children: activeState?.html ?? "" }) })), !hosted && insertPanel, repeatTable !== null && !readOnly && !canvas && (_jsxs("div", { "data-testid": "document-repeat-bar", className: "flex flex-wrap items-center gap-3 border-b border-input bg-muted/40 px-2 py-1 text-xs", children: [_jsx("span", { className: "font-medium", children: repeatListLabel === undefined
|
|
490
|
+
? t("template-resolver.documentEditor.repeat")
|
|
491
|
+
: t("template-resolver.documentEditor.repeatBarList", { list: repeatListLabel }) }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", pressed: repeatTable.headerRepeats, onClick: () => toggleRepeatHeader(editor), children: t("template-resolver.documentEditor.repeatHeader") })] })), _jsx("div", { ref: setCanvasScroller, className: cn(canvas && "min-h-0 flex-1 overflow-auto bg-muted px-8 py-6"), children: canvas && renderPaper !== undefined ? renderPaper(editorContent) : editorContent })] }));
|
|
492
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ContentEditorProps } from "@cosmicdrift/kumiko-renderer";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
export type DocumentToolbarBlock = "image" | "table" | "button" | "divider" | "repeat" | "pageBreak" | "align";
|
|
4
|
+
/** Imperative access for an outside panel (placeholder list, resource list). */
|
|
5
|
+
export type DocumentEditorHandle = {
|
|
6
|
+
readonly insertPlaceholder: (name: string) => void;
|
|
7
|
+
readonly insertResource: (key: string, alt?: string) => void;
|
|
8
|
+
readonly focus: () => void;
|
|
9
|
+
};
|
|
10
|
+
export type DocumentResourcePreview = {
|
|
11
|
+
/** Shown as the image source in the editor only; never written to the document. */
|
|
12
|
+
readonly src: string;
|
|
13
|
+
readonly caption?: string;
|
|
14
|
+
};
|
|
15
|
+
/** "default" keeps the compact bordered editor; "canvas" renders a flat text
|
|
16
|
+
* toolbar and a paper-like page with block labels. */
|
|
17
|
+
export type DocumentEditorChrome = "default" | "canvas";
|
|
18
|
+
export type DocumentEditorProps = ContentEditorProps & {
|
|
19
|
+
readonly blocks?: readonly DocumentToolbarBlock[];
|
|
20
|
+
/** Display label per variable name, as inserted (list item fields use the
|
|
21
|
+
* dotted form, e.g. "lines.description"). A chip shows label and name. */
|
|
22
|
+
readonly variableLabels?: Readonly<Record<string, string>>;
|
|
23
|
+
/** Variable names rendered as system chips (a different tint). */
|
|
24
|
+
readonly systemVariables?: readonly string[];
|
|
25
|
+
/** Image previews by resource key; resources without one show a key placeholder. */
|
|
26
|
+
readonly resourcePreviews?: Readonly<Record<string, DocumentResourcePreview>>;
|
|
27
|
+
readonly chrome?: DocumentEditorChrome;
|
|
28
|
+
/** "compact" renders the placeholder and block buttons icon-only with a tooltip, for narrow columns. Default "full". */
|
|
29
|
+
readonly toolbar?: "full" | "compact";
|
|
30
|
+
/** Canvas only: wraps the editable area in a caller-owned page (letterhead, margins) instead of the default mail paper. */
|
|
31
|
+
readonly renderPaper?: (editable: ReactNode) => ReactNode;
|
|
32
|
+
/** Canvas only: renders the toolbar (and its insert panel) into this element instead of above the editor;
|
|
33
|
+
* null renders none. Lets several region editors share one toolbar that follows the focused one. */
|
|
34
|
+
readonly toolbarHost?: HTMLElement | null;
|
|
35
|
+
/** Called with the handle once the editor exists and with null when it is destroyed. */
|
|
36
|
+
readonly onEditorReady?: (handle: DocumentEditorHandle) => void;
|
|
37
|
+
};
|
|
38
|
+
export declare const MAIL_DOCUMENT_BLOCKS: readonly ["image", "table", "button", "divider", "repeat"];
|
|
39
|
+
export declare const PDF_DOCUMENT_BLOCKS: readonly ["align", "image", "table", "divider", "repeat", "pageBreak"];
|
|
40
|
+
/** Block set for a template-resource kind; every kind except "document-pdf" gets the mail set. */
|
|
41
|
+
export declare function documentBlocksForKind(kind: string | undefined): readonly DocumentToolbarBlock[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const MAIL_DOCUMENT_BLOCKS = [
|
|
2
|
+
"image",
|
|
3
|
+
"table",
|
|
4
|
+
"button",
|
|
5
|
+
"divider",
|
|
6
|
+
"repeat",
|
|
7
|
+
];
|
|
8
|
+
export const PDF_DOCUMENT_BLOCKS = [
|
|
9
|
+
"align",
|
|
10
|
+
"image",
|
|
11
|
+
"table",
|
|
12
|
+
"divider",
|
|
13
|
+
"repeat",
|
|
14
|
+
"pageBreak",
|
|
15
|
+
];
|
|
16
|
+
/** Block set for a template-resource kind; every kind except "document-pdf" gets the mail set. */
|
|
17
|
+
export function documentBlocksForKind(kind) {
|
|
18
|
+
return kind === "document-pdf" ? PDF_DOCUMENT_BLOCKS : MAIL_DOCUMENT_BLOCKS;
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { isDocumentReferenceName, isDocumentResourceKey } from "../document-schema.js";
|
|
2
|
+
export function createEditorHandle(editor) {
|
|
3
|
+
return {
|
|
4
|
+
insertPlaceholder: (name) => {
|
|
5
|
+
// skip: an invalid variable name is not insertable
|
|
6
|
+
if (!isDocumentReferenceName(name))
|
|
7
|
+
return;
|
|
8
|
+
editor.chain().focus().insertContent({ type: "variable", attrs: { name } }).run();
|
|
9
|
+
},
|
|
10
|
+
insertResource: (key, alt) => {
|
|
11
|
+
// skip: an invalid resource key is not insertable
|
|
12
|
+
if (!isDocumentResourceKey(key))
|
|
13
|
+
return;
|
|
14
|
+
editor
|
|
15
|
+
.chain()
|
|
16
|
+
.focus()
|
|
17
|
+
.insertContent({ type: "resource", attrs: { resource: key, alt: alt ?? null } })
|
|
18
|
+
.run();
|
|
19
|
+
},
|
|
20
|
+
focus: () => {
|
|
21
|
+
editor.commands.focus();
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|