@cosmicdrift/kumiko-samples 0.389.0 → 0.390.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/package.json +1 -1
- package/packages/bundled-features/package.json +25 -13
- package/packages/bundled-features/src/audit/feature.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +13 -1
- package/packages/bundled-features/src/auth-email-password/changes.json +6 -0
- package/packages/bundled-features/src/auth-email-password/feature.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +7 -7
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.write.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +9 -7
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +11 -7
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -4
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +9 -7
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +23 -16
- package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +12 -8
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +4 -2
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
- package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +24 -14
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +9 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +6 -1
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start.write.ts +3 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +14 -9
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -4
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +25 -14
- package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +10 -4
- package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +9 -3
- package/packages/bundled-features/src/auth-mfa/verify-factor.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/changes.json +6 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +5 -5
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +10 -7
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +15 -12
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-open-cancels.ts +22 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-provider-lock.ts +4 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +17 -58
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +10 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +18 -15
- package/packages/bundled-features/src/billing-foundation/events.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/feature.ts +8 -3
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +5 -5
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/projection.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/types.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +5 -4
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +1 -1
- package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
- package/packages/bundled-features/src/cap-counter/feature.ts +2 -2
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
- package/packages/bundled-features/src/cap-counter/reservation-chain.ts +1 -1
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +4 -4
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -1
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +12 -7
- package/packages/bundled-features/src/config/feature.ts +6 -6
- package/packages/bundled-features/src/config/handlers/cascade.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +23 -13
- package/packages/bundled-features/src/config/handlers/reset.write.ts +3 -3
- package/packages/bundled-features/src/config/handlers/selected-extensions.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +8 -8
- package/packages/bundled-features/src/crypto-shredding/resolve-file-bytes-deleter.ts +3 -3
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/index.ts +4 -0
- package/packages/bundled-features/src/data-retention/feature.ts +1 -1
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +12 -2
- package/packages/bundled-features/src/delivery/attempt-log.ts +1 -1
- package/packages/bundled-features/src/delivery/delivery-service.ts +6 -5
- package/packages/bundled-features/src/delivery/feature.ts +2 -2
- package/packages/bundled-features/src/delivery/jobs.ts +11 -11
- package/packages/bundled-features/src/delivery/testing.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +5 -1
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +7 -5
- package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -5
- package/packages/bundled-features/src/file-provider-s3/feature.ts +4 -1
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +8 -8
- package/packages/bundled-features/src/form-draft/feature.ts +1 -1
- package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +22 -12
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +3 -3
- package/packages/bundled-features/src/form-draft/handlers/get.query.ts +1 -1
- package/packages/bundled-features/src/foundation-shared/config-helpers.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +4 -1
- package/packages/bundled-features/src/jobs/changes.json +6 -0
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/consumer.write.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +11 -5
- package/packages/bundled-features/src/jobs/handlers/retention-cleanup.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/trigger.write.ts +2 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +139 -90
- package/packages/bundled-features/src/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +12 -8
- package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/mail-transport-smtp/changes.json +6 -0
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +4 -1
- package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
- package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +2 -2
- package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/rate-limiting/handlers/status.query.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +4 -1
- package/packages/bundled-features/src/renderer-foundation/api.ts +3 -3
- package/packages/bundled-features/src/secrets/announce-secret-change.ts +4 -4
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/handlers/delete.write.ts +4 -3
- package/packages/bundled-features/src/secrets/handlers/rotate.job.ts +13 -8
- package/packages/bundled-features/src/secrets/handlers/set.write.ts +4 -3
- package/packages/bundled-features/src/secrets/secrets-context.ts +16 -6
- package/packages/bundled-features/src/secrets/write-gate.ts +1 -1
- package/packages/bundled-features/src/seo/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/changes.json +12 -0
- package/packages/bundled-features/src/sessions/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/cleanup.job.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/detail.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/mine.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +3 -3
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/screens.ts +8 -4
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +1 -1
- package/packages/bundled-features/src/sessions/web/session-device-cell.tsx +10 -12
- package/packages/bundled-features/src/sessions/web/user-agent.ts +12 -0
- package/packages/bundled-features/src/shared/index.ts +15 -0
- package/packages/bundled-features/src/shared/purge-files-reaching-subject.ts +12 -2
- package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -5
- package/packages/bundled-features/src/shared/stream-tenant-backfill.ts +201 -0
- package/packages/bundled-features/src/shared/stream-tenant-derived-state.ts +107 -0
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +1 -1
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +7 -7
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +5 -3
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +7 -7
- package/packages/bundled-features/src/template-resolver/api.ts +3 -3
- package/packages/bundled-features/src/template-resolver/changes.json +18 -0
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +5 -4
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +6 -2
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +4 -2
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/seeding.ts +12 -2
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +18 -13
- package/packages/bundled-features/src/template-resolver/template-resources.ts +4 -4
- package/packages/bundled-features/src/template-resolver/testing.ts +2 -2
- package/packages/bundled-features/src/template-resolver/web/document-insert-fields.tsx +222 -0
- package/packages/bundled-features/src/template-resolver/web/document-insert-model.ts +164 -0
- package/packages/bundled-features/src/template-resolver/web/document-insert-panel.tsx +115 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-bubble.tsx +177 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-content.tsx +38 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-context.ts +119 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +51 -1098
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-helpers.ts +161 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-layout.tsx +83 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar-groups.tsx +153 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar.tsx +182 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +18 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +19 -6
- package/packages/bundled-features/src/template-resolver/web/use-document-tiptap-model.ts +138 -0
- package/packages/bundled-features/src/tenant/changes.json +19 -0
- package/packages/bundled-features/src/tenant/handlers/cancel-invitation.write.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/remove-member.write.ts +2 -2
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -1
- package/packages/bundled-features/src/tenant/index.ts +5 -0
- package/packages/bundled-features/src/tenant/invited-membership.ts +6 -1
- package/packages/bundled-features/src/tenant/schema/tenant.ts +4 -1
- package/packages/bundled-features/src/tenant/seeding.ts +65 -62
- package/packages/bundled-features/src/tenant/stream-tenant-backfill.ts +115 -0
- package/packages/bundled-features/src/tenant/testing.ts +77 -0
- package/packages/bundled-features/src/tenant-handover/events.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +5 -5
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +22 -10
- package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +22 -11
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +23 -12
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +7 -3
- package/packages/bundled-features/src/tenant-lifecycle/testing.ts +9 -8
- package/packages/bundled-features/src/tier-engine/entity.ts +1 -1
- package/packages/bundled-features/src/tier-engine/feature.ts +8 -3
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +3 -3
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +7 -1
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +3 -3
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +71 -97
- package/packages/bundled-features/src/user/handlers/find-for-auth.query.ts +2 -2
- package/packages/bundled-features/src/user/handlers/update-roles.ts +2 -2
- package/packages/bundled-features/src/user/index.ts +2 -0
- package/packages/bundled-features/src/user/invited-global-roles.ts +6 -1
- package/packages/bundled-features/src/user/principal-status.ts +1 -1
- package/packages/bundled-features/src/user/seeding.ts +24 -6
- package/packages/bundled-features/src/user/testing.ts +22 -0
- package/packages/bundled-features/src/user-data-rights/COMPLIANCE.md +1 -1
- package/packages/bundled-features/src/user-data-rights/README.md +1 -1
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +12 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +24 -0
- package/packages/bundled-features/src/user-data-rights/feature.ts +29 -24
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +12 -6
- package/packages/bundled-features/src/user-data-rights/index.ts +2 -0
- package/packages/bundled-features/src/user-data-rights/lib/mail-transport-resolver.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/storage-provider-resolver.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +6 -1
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +8 -2
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +14 -3
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +11 -1
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +10 -0
- package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -1
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
- package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +2 -2
- package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +9 -4
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +6 -1
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -3
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +8 -8
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -4
- package/samples/apps/cap-billing-demo/src/feature.ts +19 -5
- package/samples/apps/config-demo/src/app/server.ts +1 -1
- package/samples/apps/styleguide/src/app/server.ts +2 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +1 -1
- package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
- package/samples/apps/user-data-rights-demo/README.md +1 -1
- package/samples/apps/user-data-rights-demo/src/feature.ts +3 -2
- package/samples/recipes/api-worker-split/README.md +2 -1
- package/samples/recipes/api-worker-split/src/feature.ts +2 -1
- package/samples/recipes/cross-feature-events/README.md +5 -5
- package/samples/recipes/cross-feature-events/package.json +1 -1
- package/samples/recipes/cross-feature-events/src/feature.ts +4 -4
- package/samples/recipes/event-sourcing/README.md +5 -5
- package/samples/recipes/event-sourcing/package.json +1 -1
- package/samples/recipes/event-sourcing/src/feature.ts +15 -15
- package/samples/recipes/files-post-processing/README.md +1 -1
- package/samples/recipes/files-post-processing/package.json +1 -1
- package/samples/recipes/files-post-processing/src/feature.ts +6 -4
- package/samples/recipes/lane-routing/package.json +1 -1
- package/samples/recipes/legal-pages/README.md +2 -2
- package/samples/recipes/lifecycle-hooks/README.md +2 -2
- package/samples/recipes/lifecycle-hooks/src/feature.ts +1 -1
- package/samples/recipes/managed-config/README.md +3 -3
- package/samples/recipes/search/README.md +1 -1
- package/samples/recipes/search/src/feature.ts +3 -3
- package/samples/recipes/secrets-demo/README.md +1 -1
- package/samples/recipes/secrets-demo/src/feature.ts +5 -2
- package/samples/recipes/state-machine/src/events.ts +1 -1
- package/samples/recipes/state-machine/src/reducer.ts +1 -1
- package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
- package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import type { Editor, JSONContent } from "@tiptap/core";
|
|
3
|
+
import type { DocumentResourcePreview, DocumentToolbarBlock } from "./document-toolbar-blocks.ts";
|
|
4
|
+
|
|
5
|
+
export const NO_LABELS: Readonly<Record<string, string>> = {};
|
|
6
|
+
export const NO_NAMES: readonly string[] = [];
|
|
7
|
+
export const NO_PREVIEWS: Readonly<Record<string, DocumentResourcePreview>> = {};
|
|
8
|
+
|
|
9
|
+
export function previewsKey(previews: Readonly<Record<string, DocumentResourcePreview>>): string {
|
|
10
|
+
return Object.entries(previews)
|
|
11
|
+
.map(([key, { src, caption }]) => `${key}|${src.length}|${src.slice(-24)}|${caption ?? ""}`)
|
|
12
|
+
.join("\n");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type PanelKind =
|
|
16
|
+
| "link"
|
|
17
|
+
| "placeholder"
|
|
18
|
+
| "image"
|
|
19
|
+
| "button"
|
|
20
|
+
| "repeat"
|
|
21
|
+
| "align"
|
|
22
|
+
| "steps"
|
|
23
|
+
| "images";
|
|
24
|
+
|
|
25
|
+
// Blocks that stay in the canvas toolbar; the rest moves into the overflow menu.
|
|
26
|
+
export const CANVAS_INLINE_BLOCKS: readonly DocumentToolbarBlock[] = ["image", "table"];
|
|
27
|
+
|
|
28
|
+
export const BLOCK_PANEL: Partial<Record<DocumentToolbarBlock, PanelKind>> = {
|
|
29
|
+
image: "image",
|
|
30
|
+
button: "button",
|
|
31
|
+
repeat: "repeat",
|
|
32
|
+
align: "align",
|
|
33
|
+
steps: "steps",
|
|
34
|
+
images: "images",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type RepeatTableState = {
|
|
38
|
+
readonly lists: readonly string[];
|
|
39
|
+
readonly headerRepeats: boolean;
|
|
40
|
+
readonly firstRowPos: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// print-document moves the leading rows flagged repeatHeader into thead, so the
|
|
44
|
+
// toggle reads and writes the table's first row.
|
|
45
|
+
export function findRepeatTable(editor: Editor): RepeatTableState | null {
|
|
46
|
+
const { $from } = editor.state.selection;
|
|
47
|
+
for (let depth = $from.depth; depth > 0; depth--) {
|
|
48
|
+
const node = $from.node(depth);
|
|
49
|
+
if (node.type.name !== "table") continue;
|
|
50
|
+
const lists: string[] = [];
|
|
51
|
+
node.forEach((row) => {
|
|
52
|
+
const each: unknown = row.attrs["each"];
|
|
53
|
+
if (typeof each === "string" && each !== "" && !lists.includes(each)) lists.push(each);
|
|
54
|
+
});
|
|
55
|
+
if (lists.length === 0) return null;
|
|
56
|
+
return {
|
|
57
|
+
lists,
|
|
58
|
+
headerRepeats: node.firstChild?.attrs["repeatHeader"] === true,
|
|
59
|
+
firstRowPos: $from.start(depth),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function toggleRepeatHeader(editor: Editor): void {
|
|
66
|
+
const found = findRepeatTable(editor);
|
|
67
|
+
// skip: the cursor is not inside a repeat table
|
|
68
|
+
if (found === null) return;
|
|
69
|
+
editor
|
|
70
|
+
.chain()
|
|
71
|
+
.focus()
|
|
72
|
+
.command(({ tr }) => {
|
|
73
|
+
const row = tr.doc.nodeAt(found.firstRowPos);
|
|
74
|
+
if (row === null) return false;
|
|
75
|
+
tr.setNodeMarkup(found.firstRowPos, undefined, {
|
|
76
|
+
...row.attrs,
|
|
77
|
+
repeatHeader: !found.headerRepeats,
|
|
78
|
+
});
|
|
79
|
+
return true;
|
|
80
|
+
})
|
|
81
|
+
.run();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const PARAGRAPH_STYLES = ["paragraph", "1", "2", "3"] as const;
|
|
85
|
+
type ParagraphStyle = (typeof PARAGRAPH_STYLES)[number];
|
|
86
|
+
|
|
87
|
+
export const HEADING_LEVELS = { "1": 1, "2": 2, "3": 3 } as const;
|
|
88
|
+
|
|
89
|
+
const PLACEHOLDER_HREF = /^\{\{([^{}]+)\}\}$/;
|
|
90
|
+
|
|
91
|
+
export function variableLabelOf(name: string, labels: Readonly<Record<string, string>>): string {
|
|
92
|
+
return Object.hasOwn(labels, name) ? (labels[name] ?? name) : name;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function buttonTargetLabel(href: string, labels: Readonly<Record<string, string>>): string {
|
|
96
|
+
const name = PLACEHOLDER_HREF.exec(href)?.[1];
|
|
97
|
+
return name === undefined ? href : variableLabelOf(name, labels);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function isParagraphStyle(value: string): value is ParagraphStyle {
|
|
101
|
+
return isOneOf(PARAGRAPH_STYLES, value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function emptyParagraph(): JSONContent {
|
|
105
|
+
return { type: "paragraph" };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function repeatTableContent(listName: string): JSONContent {
|
|
109
|
+
const headerRow: JSONContent = {
|
|
110
|
+
type: "tableRow",
|
|
111
|
+
content: [
|
|
112
|
+
{ type: "tableHeader", content: [emptyParagraph()] },
|
|
113
|
+
{ type: "tableHeader", content: [emptyParagraph()] },
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
const repeatRow: JSONContent = {
|
|
117
|
+
type: "tableRow",
|
|
118
|
+
attrs: { each: listName },
|
|
119
|
+
content: [
|
|
120
|
+
{ type: "tableCell", content: [emptyParagraph()] },
|
|
121
|
+
{ type: "tableCell", content: [emptyParagraph()] },
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
return { type: "table", content: [headerRow, repeatRow] };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function blockStyleOf(e: Editor): "1" | "2" | "3" | "paragraph" {
|
|
128
|
+
if (e.isActive("heading", { level: 1 })) return "1";
|
|
129
|
+
if (e.isActive("heading", { level: 2 })) return "2";
|
|
130
|
+
if (e.isActive("heading", { level: 3 })) return "3";
|
|
131
|
+
return "paragraph";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function selectedBlockOf(
|
|
135
|
+
e: Editor,
|
|
136
|
+
): { readonly type: "resource" | "button"; readonly detail: string } | null {
|
|
137
|
+
if (e.isActive("resource")) {
|
|
138
|
+
return { type: "resource", detail: String(e.getAttributes("resource")["resource"]) };
|
|
139
|
+
}
|
|
140
|
+
if (e.isActive("button")) {
|
|
141
|
+
return { type: "button", detail: String(e.getAttributes("button")["href"]) };
|
|
142
|
+
}
|
|
143
|
+
return null; // skip: no resource or button selected
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function activeStateOf(e: Editor, showHtml: boolean) {
|
|
147
|
+
return {
|
|
148
|
+
blockStyle: blockStyleOf(e),
|
|
149
|
+
bold: e.isActive("bold"),
|
|
150
|
+
italic: e.isActive("italic"),
|
|
151
|
+
underline: e.isActive("underline"),
|
|
152
|
+
selectedBlock: selectedBlockOf(e),
|
|
153
|
+
html: showHtml ? e.getHTML() : "",
|
|
154
|
+
bulletList: e.isActive("bulletList"),
|
|
155
|
+
orderedList: e.isActive("orderedList"),
|
|
156
|
+
link: e.isActive("link"),
|
|
157
|
+
repeatTable: findRepeatTable(e),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type ActiveState = ReturnType<typeof activeStateOf>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { cn } from "@cosmicdrift/kumiko-renderer/web";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import { InsertPanel } from "./document-insert-panel.tsx";
|
|
5
|
+
import { renderBubbleMenu, renderInlineRepeatBar } from "./document-tiptap-bubble.tsx";
|
|
6
|
+
import type { TiptapContext } from "./document-tiptap-context.ts";
|
|
7
|
+
import { renderToolbarBar } from "./document-tiptap-toolbar.tsx";
|
|
8
|
+
|
|
9
|
+
function renderInsertPanel(c: TiptapContext): ReactNode {
|
|
10
|
+
const { openPanel } = c;
|
|
11
|
+
return (
|
|
12
|
+
openPanel !== null &&
|
|
13
|
+
!c.readOnly && (
|
|
14
|
+
<InsertPanel
|
|
15
|
+
key={openPanel}
|
|
16
|
+
editor={c.editor}
|
|
17
|
+
editorId={c.id}
|
|
18
|
+
kind={openPanel}
|
|
19
|
+
variables={openPanel === "images" ? c.imageVariables : c.variables}
|
|
20
|
+
variableLabels={c.stableLabels}
|
|
21
|
+
onClose={() => c.setOpenPanel(null)}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function renderHostedEditor(
|
|
28
|
+
c: TiptapContext,
|
|
29
|
+
toolbarHost: HTMLElement | null,
|
|
30
|
+
editorContent: ReactNode,
|
|
31
|
+
): ReactNode {
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
{toolbarHost !== null &&
|
|
35
|
+
createPortal(
|
|
36
|
+
<>
|
|
37
|
+
{renderToolbarBar(c)}
|
|
38
|
+
{renderInsertPanel(c)}
|
|
39
|
+
</>,
|
|
40
|
+
toolbarHost,
|
|
41
|
+
)}
|
|
42
|
+
{!c.readOnly && renderBubbleMenu(c)}
|
|
43
|
+
{editorContent}
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function renderHtmlSource(c: TiptapContext): ReactNode {
|
|
49
|
+
return (
|
|
50
|
+
c.canvas &&
|
|
51
|
+
!c.hosted &&
|
|
52
|
+
c.showHtml && (
|
|
53
|
+
<section aria-label={c.t("template-resolver.documentEditor.htmlSource")}>
|
|
54
|
+
<pre className="h-40 overflow-auto whitespace-pre-wrap break-all border-b border-input bg-muted p-2 font-mono text-xs">
|
|
55
|
+
{c.activeState?.html ?? ""}
|
|
56
|
+
</pre>
|
|
57
|
+
</section>
|
|
58
|
+
)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function renderInlineEditor(c: TiptapContext, paper: ReactNode): ReactNode {
|
|
63
|
+
const { canvas, hosted } = c;
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
className={cn(
|
|
67
|
+
canvas ? "flex min-h-0 flex-1 flex-col" : "rounded-md border border-input bg-transparent",
|
|
68
|
+
)}
|
|
69
|
+
>
|
|
70
|
+
{!hosted && renderToolbarBar(c)}
|
|
71
|
+
{canvas && !c.readOnly && renderBubbleMenu(c)}
|
|
72
|
+
{renderHtmlSource(c)}
|
|
73
|
+
{!hosted && renderInsertPanel(c)}
|
|
74
|
+
{renderInlineRepeatBar(c)}
|
|
75
|
+
<div
|
|
76
|
+
ref={c.setCanvasScroller}
|
|
77
|
+
className={cn(canvas && "min-h-0 flex-1 overflow-auto bg-muted px-8 py-6")}
|
|
78
|
+
>
|
|
79
|
+
{paper}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar-groups.tsx
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { cn } from "@cosmicdrift/kumiko-renderer/web";
|
|
2
|
+
import {
|
|
3
|
+
Bold as BoldIcon,
|
|
4
|
+
Italic as ItalicIcon,
|
|
5
|
+
Link as LinkIcon,
|
|
6
|
+
List,
|
|
7
|
+
ListOrdered,
|
|
8
|
+
Underline as UnderlineIcon,
|
|
9
|
+
} from "lucide-react";
|
|
10
|
+
import type { ReactNode } from "react";
|
|
11
|
+
import type { TiptapContext } from "./document-tiptap-context.ts";
|
|
12
|
+
|
|
13
|
+
// Canvas chrome is one flat row: unframed ghost buttons, glyph plus text for the longer names.
|
|
14
|
+
export function iconButton(
|
|
15
|
+
c: TiptapContext,
|
|
16
|
+
label: string,
|
|
17
|
+
icon: ReactNode,
|
|
18
|
+
isActive: boolean,
|
|
19
|
+
onClick: () => void,
|
|
20
|
+
text?: string,
|
|
21
|
+
): ReactNode {
|
|
22
|
+
const { Button, canvas, readOnly } = c;
|
|
23
|
+
return canvas ? (
|
|
24
|
+
<Button
|
|
25
|
+
type="button"
|
|
26
|
+
variant="ghost"
|
|
27
|
+
size="sm"
|
|
28
|
+
pressed={isActive}
|
|
29
|
+
disabled={readOnly}
|
|
30
|
+
onClick={onClick}
|
|
31
|
+
ariaLabel={label}
|
|
32
|
+
title={label}
|
|
33
|
+
>
|
|
34
|
+
{icon}
|
|
35
|
+
{text}
|
|
36
|
+
</Button>
|
|
37
|
+
) : (
|
|
38
|
+
<Button
|
|
39
|
+
type="button"
|
|
40
|
+
variant={isActive ? "primary" : "secondary"}
|
|
41
|
+
size="icon"
|
|
42
|
+
pressed={isActive}
|
|
43
|
+
disabled={readOnly}
|
|
44
|
+
onClick={onClick}
|
|
45
|
+
ariaLabel={label}
|
|
46
|
+
title={label}
|
|
47
|
+
>
|
|
48
|
+
{icon}
|
|
49
|
+
</Button>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function markContent(
|
|
54
|
+
canvas: boolean,
|
|
55
|
+
letter: string,
|
|
56
|
+
icon: ReactNode,
|
|
57
|
+
className: string,
|
|
58
|
+
): ReactNode {
|
|
59
|
+
return canvas ? <span className={className}>{letter}</span> : icon;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function renderParagraphStyleSelect(c: TiptapContext): ReactNode {
|
|
63
|
+
const { t, Field, Input, id, canvas, compact, readOnly, activeState } = c;
|
|
64
|
+
const paragraphStyleLabel = t("template-resolver.documentEditor.paragraphStyle");
|
|
65
|
+
const styleOptions = [
|
|
66
|
+
{ value: "paragraph", label: t("template-resolver.documentEditor.paragraph") },
|
|
67
|
+
{ value: "1", label: t("kumiko.contentEditor.heading1") },
|
|
68
|
+
{ value: "2", label: t("kumiko.contentEditor.heading2") },
|
|
69
|
+
{ value: "3", label: t("template-resolver.documentEditor.heading3") },
|
|
70
|
+
];
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
// kumiko-lint-ignore raw-classname the toolbar paragraph select sits flush in the editor toolbar; ComboboxInput has no flat trigger variant
|
|
74
|
+
className={cn(
|
|
75
|
+
!canvas && (compact ? "w-24" : "w-40"),
|
|
76
|
+
canvas &&
|
|
77
|
+
"w-auto shrink-0 [&_button[aria-haspopup=listbox]]:border-transparent [&_button[aria-haspopup=listbox]]:bg-transparent [&_button[aria-haspopup=listbox]]:shadow-none",
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<Field id={`${id}-paragraph-style`} label={paragraphStyleLabel} hideLabel>
|
|
81
|
+
<Input
|
|
82
|
+
kind="select"
|
|
83
|
+
id={`${id}-paragraph-style`}
|
|
84
|
+
name="paragraph-style"
|
|
85
|
+
value={activeState?.blockStyle ?? "paragraph"}
|
|
86
|
+
onChange={c.setBlockStyle}
|
|
87
|
+
options={styleOptions}
|
|
88
|
+
display="dropdown"
|
|
89
|
+
disabled={readOnly}
|
|
90
|
+
/>
|
|
91
|
+
</Field>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function renderMarkButtons(c: TiptapContext): ReactNode {
|
|
97
|
+
const { t, editor, canvas, activeState } = c;
|
|
98
|
+
return (
|
|
99
|
+
<>
|
|
100
|
+
{iconButton(
|
|
101
|
+
c,
|
|
102
|
+
t("kumiko.contentEditor.bold"),
|
|
103
|
+
markContent(canvas, "B", <BoldIcon size={16} />, "font-bold"),
|
|
104
|
+
activeState?.bold ?? false,
|
|
105
|
+
() => editor.chain().focus().toggleBold().run(),
|
|
106
|
+
)}
|
|
107
|
+
{iconButton(
|
|
108
|
+
c,
|
|
109
|
+
t("kumiko.contentEditor.italic"),
|
|
110
|
+
markContent(canvas, "I", <ItalicIcon size={16} />, "italic"),
|
|
111
|
+
activeState?.italic ?? false,
|
|
112
|
+
() => editor.chain().focus().toggleItalic().run(),
|
|
113
|
+
)}
|
|
114
|
+
{iconButton(
|
|
115
|
+
c,
|
|
116
|
+
t("template-resolver.documentEditor.underline"),
|
|
117
|
+
markContent(canvas, "U", <UnderlineIcon size={16} />, "underline"),
|
|
118
|
+
activeState?.underline ?? false,
|
|
119
|
+
() => editor.chain().focus().toggleUnderline().run(),
|
|
120
|
+
)}
|
|
121
|
+
</>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function renderListButtons(c: TiptapContext): ReactNode {
|
|
126
|
+
const { t, editor, canvas, activeState } = c;
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
{iconButton(
|
|
130
|
+
c,
|
|
131
|
+
t("kumiko.contentEditor.bulletList"),
|
|
132
|
+
<List size={16} />,
|
|
133
|
+
activeState?.bulletList ?? false,
|
|
134
|
+
() => editor.chain().focus().toggleBulletList().run(),
|
|
135
|
+
canvas ? undefined : t("template-resolver.documentEditor.list"),
|
|
136
|
+
)}
|
|
137
|
+
{iconButton(
|
|
138
|
+
c,
|
|
139
|
+
t("kumiko.contentEditor.orderedList"),
|
|
140
|
+
<ListOrdered size={16} />,
|
|
141
|
+
activeState?.orderedList ?? false,
|
|
142
|
+
() => editor.chain().focus().toggleOrderedList().run(),
|
|
143
|
+
)}
|
|
144
|
+
{iconButton(
|
|
145
|
+
c,
|
|
146
|
+
t("template-resolver.documentEditor.link"),
|
|
147
|
+
<LinkIcon size={16} />,
|
|
148
|
+
activeState?.link ?? false,
|
|
149
|
+
() => c.togglePanel("link"),
|
|
150
|
+
)}
|
|
151
|
+
</>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { ActionMenuItemSpec } from "@cosmicdrift/kumiko-renderer";
|
|
2
|
+
import { cn } from "@cosmicdrift/kumiko-renderer/web";
|
|
3
|
+
import {
|
|
4
|
+
AlignLeft,
|
|
5
|
+
Braces,
|
|
6
|
+
ChevronDown,
|
|
7
|
+
GalleryThumbnails,
|
|
8
|
+
Image as ImageIcon,
|
|
9
|
+
ListChecks,
|
|
10
|
+
Minus,
|
|
11
|
+
MousePointerClick,
|
|
12
|
+
Repeat,
|
|
13
|
+
Scissors,
|
|
14
|
+
Table as TableIcon,
|
|
15
|
+
} from "lucide-react";
|
|
16
|
+
import { Fragment, type ReactNode } from "react";
|
|
17
|
+
import type { TiptapContext } from "./document-tiptap-context.ts";
|
|
18
|
+
import { CANVAS_INLINE_BLOCKS } from "./document-tiptap-helpers.ts";
|
|
19
|
+
import {
|
|
20
|
+
iconButton,
|
|
21
|
+
renderListButtons,
|
|
22
|
+
renderMarkButtons,
|
|
23
|
+
renderParagraphStyleSelect,
|
|
24
|
+
} from "./document-tiptap-toolbar-groups.tsx";
|
|
25
|
+
import type { DocumentToolbarBlock } from "./document-toolbar-blocks.ts";
|
|
26
|
+
|
|
27
|
+
type BlockButtons = Record<DocumentToolbarBlock, { label: string; icon: ReactNode }>;
|
|
28
|
+
|
|
29
|
+
function Separator({ compact = false }: { readonly compact?: boolean }): ReactNode {
|
|
30
|
+
return (
|
|
31
|
+
<span aria-hidden="true" className={cn("h-5 w-px bg-border", compact ? "mx-0.5" : "mx-2")} />
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function blockButtonsOf(c: TiptapContext): BlockButtons {
|
|
36
|
+
const { t } = c;
|
|
37
|
+
const { imageLabel, pageBreakLabel, stepsLabel, imagesLabel } = c.labels;
|
|
38
|
+
return {
|
|
39
|
+
image: { label: imageLabel, icon: <ImageIcon size={16} /> },
|
|
40
|
+
table: { label: t("template-resolver.documentEditor.table"), icon: <TableIcon size={16} /> },
|
|
41
|
+
button: {
|
|
42
|
+
label: t("template-resolver.documentEditor.button"),
|
|
43
|
+
icon: <MousePointerClick size={16} />,
|
|
44
|
+
},
|
|
45
|
+
divider: { label: t("template-resolver.documentEditor.divider"), icon: <Minus size={16} /> },
|
|
46
|
+
repeat: { label: t("template-resolver.documentEditor.repeat"), icon: <Repeat size={16} /> },
|
|
47
|
+
pageBreak: { label: pageBreakLabel, icon: <Scissors size={16} /> },
|
|
48
|
+
align: { label: t("template-resolver.documentEditor.align"), icon: <AlignLeft size={16} /> },
|
|
49
|
+
steps: { label: stepsLabel, icon: <ListChecks size={16} /> },
|
|
50
|
+
images: { label: imagesLabel, icon: <GalleryThumbnails size={16} /> },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type LabelledButtonSpec = {
|
|
55
|
+
readonly key?: string;
|
|
56
|
+
readonly label: string;
|
|
57
|
+
readonly icon: ReactNode;
|
|
58
|
+
readonly showIcon: boolean;
|
|
59
|
+
readonly trailingIcon?: ReactNode;
|
|
60
|
+
readonly expanded?: boolean;
|
|
61
|
+
readonly onClick: () => void;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function labelledButton(c: TiptapContext, spec: LabelledButtonSpec): ReactNode {
|
|
65
|
+
const { Button, canvas, compact, readOnly } = c;
|
|
66
|
+
const flatVariant = canvas ? "ghost" : "secondary";
|
|
67
|
+
const shared = {
|
|
68
|
+
type: "button",
|
|
69
|
+
variant: flatVariant,
|
|
70
|
+
disabled: readOnly,
|
|
71
|
+
onClick: spec.onClick,
|
|
72
|
+
...(spec.expanded !== undefined && { expanded: spec.expanded }),
|
|
73
|
+
} as const;
|
|
74
|
+
return compact ? (
|
|
75
|
+
<Button key={spec.key} {...shared} size="icon" ariaLabel={spec.label} title={spec.label}>
|
|
76
|
+
{spec.icon}
|
|
77
|
+
</Button>
|
|
78
|
+
) : (
|
|
79
|
+
<Button key={spec.key} {...shared} size="sm">
|
|
80
|
+
{spec.showIcon && spec.icon}
|
|
81
|
+
{spec.label}
|
|
82
|
+
{spec.trailingIcon}
|
|
83
|
+
</Button>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function renderInlineBlock(
|
|
88
|
+
c: TiptapContext,
|
|
89
|
+
block: DocumentToolbarBlock,
|
|
90
|
+
blockButtons: BlockButtons,
|
|
91
|
+
): ReactNode {
|
|
92
|
+
if (c.canvas) {
|
|
93
|
+
return (
|
|
94
|
+
<Fragment key={block}>
|
|
95
|
+
{iconButton(c, blockButtons[block].label, blockButtons[block].icon, false, () =>
|
|
96
|
+
c.runBlock(block),
|
|
97
|
+
)}
|
|
98
|
+
</Fragment>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return labelledButton(c, {
|
|
102
|
+
key: block,
|
|
103
|
+
label: blockButtons[block].label,
|
|
104
|
+
icon: blockButtons[block].icon,
|
|
105
|
+
showIcon: true,
|
|
106
|
+
onClick: () => c.runBlock(block),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function overflowItemsOf(
|
|
111
|
+
c: TiptapContext,
|
|
112
|
+
menuBlocks: readonly DocumentToolbarBlock[],
|
|
113
|
+
blockButtons: BlockButtons,
|
|
114
|
+
): readonly ActionMenuItemSpec[] {
|
|
115
|
+
const { t, canvas, hosted, readOnly } = c;
|
|
116
|
+
return [
|
|
117
|
+
...menuBlocks.map((block) => ({
|
|
118
|
+
id: block,
|
|
119
|
+
label: blockButtons[block].label,
|
|
120
|
+
disabled: readOnly,
|
|
121
|
+
onSelect: () => c.runBlock(block),
|
|
122
|
+
})),
|
|
123
|
+
...(canvas && !hosted
|
|
124
|
+
? [
|
|
125
|
+
{
|
|
126
|
+
id: "html",
|
|
127
|
+
label: t("template-resolver.documentEditor.showHtml"),
|
|
128
|
+
onSelect: () => c.setShowHtml((current) => !current),
|
|
129
|
+
},
|
|
130
|
+
]
|
|
131
|
+
: []),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function renderToolbarBar(c: TiptapContext): ReactNode {
|
|
136
|
+
const { t, id, canvas, compact, ActionOverflowMenu } = c;
|
|
137
|
+
const blockButtons = blockButtonsOf(c);
|
|
138
|
+
// The canvas toolbar never scrolls sideways: the rarer blocks and the HTML view sit in the menu.
|
|
139
|
+
const menuBlocks =
|
|
140
|
+
canvas && ActionOverflowMenu !== undefined
|
|
141
|
+
? c.blocks.filter((block) => !CANVAS_INLINE_BLOCKS.includes(block))
|
|
142
|
+
: [];
|
|
143
|
+
const inlineBlocks = c.blocks.filter((block) => !menuBlocks.includes(block));
|
|
144
|
+
const overflowItems = overflowItemsOf(c, menuBlocks, blockButtons);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div
|
|
148
|
+
className={cn(
|
|
149
|
+
"flex items-center border-b border-input",
|
|
150
|
+
canvas
|
|
151
|
+
? "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"
|
|
152
|
+
: "flex-wrap gap-1 p-1",
|
|
153
|
+
)}
|
|
154
|
+
>
|
|
155
|
+
{renderParagraphStyleSelect(c)}
|
|
156
|
+
<Separator compact={compact || canvas} />
|
|
157
|
+
{renderMarkButtons(c)}
|
|
158
|
+
<Separator compact={compact || canvas} />
|
|
159
|
+
{renderListButtons(c)}
|
|
160
|
+
<Separator compact={compact || canvas} />
|
|
161
|
+
{labelledButton(c, {
|
|
162
|
+
label: t("template-resolver.documentEditor.insertPlaceholder"),
|
|
163
|
+
icon: <Braces size={16} />,
|
|
164
|
+
showIcon: true,
|
|
165
|
+
trailingIcon: canvas ? <ChevronDown size={12} /> : undefined,
|
|
166
|
+
expanded: c.openPanel === "placeholder",
|
|
167
|
+
onClick: () => c.togglePanel("placeholder"),
|
|
168
|
+
})}
|
|
169
|
+
{inlineBlocks.length > 0 && <Separator compact={compact || canvas} />}
|
|
170
|
+
{inlineBlocks.map((block) => renderInlineBlock(c, block, blockButtons))}
|
|
171
|
+
{overflowItems.length > 0 && ActionOverflowMenu !== undefined && (
|
|
172
|
+
<span className="ml-auto shrink-0">
|
|
173
|
+
<ActionOverflowMenu
|
|
174
|
+
items={overflowItems}
|
|
175
|
+
label={t("template-resolver.documentEditor.more")}
|
|
176
|
+
testId={`${id}-toolbar-more`}
|
|
177
|
+
/>
|
|
178
|
+
</span>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Editor } from "@tiptap/core";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
2
3
|
import { isDocumentReferenceName, isDocumentResourceKey } from "./document-schema.ts";
|
|
3
4
|
import type { DocumentEditorHandle } from "./document-toolbar-blocks.ts";
|
|
4
5
|
|
|
@@ -23,3 +24,20 @@ export function createEditorHandle(editor: Editor): DocumentEditorHandle {
|
|
|
23
24
|
},
|
|
24
25
|
};
|
|
25
26
|
}
|
|
27
|
+
|
|
28
|
+
// Reported from the commit, not tiptap's onCreate/onDestroy: tiptap destroys an unmounted editor on
|
|
29
|
+
// a later timer than it announces its replacement, so the old editor's late null would unregister
|
|
30
|
+
// the new handle after a remount. Effect cleanups of the unmounting component run before the new report.
|
|
31
|
+
export function useEditorHandleReport(
|
|
32
|
+
editor: Editor | null,
|
|
33
|
+
onEditorReady: ((handle: DocumentEditorHandle | null) => void) | undefined,
|
|
34
|
+
): void {
|
|
35
|
+
const reportRef = useRef(onEditorReady);
|
|
36
|
+
reportRef.current = onEditorReady;
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
// skip: useEditor creates the instance in a later effect; the next run reports it
|
|
39
|
+
if (editor === null) return;
|
|
40
|
+
reportRef.current?.(createEditorHandle(editor));
|
|
41
|
+
return () => reportRef.current?.(null);
|
|
42
|
+
}, [editor]);
|
|
43
|
+
}
|
|
@@ -8,11 +8,11 @@ import { Extension, Node } from "@tiptap/core";
|
|
|
8
8
|
import { Placeholder, UndoRedo } from "@tiptap/extensions";
|
|
9
9
|
import { EditorContent, useEditor } from "@tiptap/react";
|
|
10
10
|
import StarterKit from "@tiptap/starter-kit";
|
|
11
|
-
import { type ReactNode, useMemo, useState } from "react";
|
|
11
|
+
import { type ReactNode, useMemo, useRef, useState } from "react";
|
|
12
12
|
import { createVariableView } from "./document-editor-extensions.ts";
|
|
13
13
|
import { DocumentVariable } from "./document-schema.ts";
|
|
14
14
|
import type { DocumentEditorHandle } from "./document-toolbar-blocks.ts";
|
|
15
|
-
import {
|
|
15
|
+
import { useEditorHandleReport } from "./editor-handle.ts";
|
|
16
16
|
import { contentToLine, lineToContent, singleLine } from "./placeholder-text.ts";
|
|
17
17
|
import { useStableByKey } from "./use-stable-by-key.ts";
|
|
18
18
|
|
|
@@ -118,10 +118,9 @@ function LineEditorBody({
|
|
|
118
118
|
// Rich pastes would bring several paragraphs; only their text is kept.
|
|
119
119
|
transformPastedHTML: (html) => singleLine(html.replace(/<[^>]*>/g, " ")),
|
|
120
120
|
},
|
|
121
|
-
onCreate: ({ editor: created }) => onEditorReady?.(createEditorHandle(created)),
|
|
122
|
-
onDestroy: () => onEditorReady?.(null),
|
|
123
121
|
onUpdate: ({ editor: e }) => onChange(contentToLine(e.getJSON().content?.[0]?.content)),
|
|
124
122
|
});
|
|
123
|
+
useEditorHandleReport(editor, onEditorReady);
|
|
125
124
|
|
|
126
125
|
return (
|
|
127
126
|
<EditorContent
|
|
@@ -165,9 +164,23 @@ export default function PlaceholderLineEditor({
|
|
|
165
164
|
setEpoch((current) => current + 1);
|
|
166
165
|
}
|
|
167
166
|
|
|
167
|
+
const bodyKey = `${epoch}-${readOnly}-${JSON.stringify(stableLabels)}-${stableSystem.join("\n")}`;
|
|
168
|
+
const liveBodyKey = useRef(bodyKey);
|
|
169
|
+
liveBodyKey.current = bodyKey;
|
|
170
|
+
|
|
171
|
+
// TipTap destroys a replaced editor in a deferred timer that can fire after the successor's
|
|
172
|
+
// create; its late null must not delete the successor's handle.
|
|
173
|
+
const reportEditorReady =
|
|
174
|
+
onEditorReady === undefined
|
|
175
|
+
? undefined
|
|
176
|
+
: (handle: DocumentEditorHandle | null): void => {
|
|
177
|
+
if (handle === null && liveBodyKey.current !== bodyKey) return;
|
|
178
|
+
onEditorReady(handle);
|
|
179
|
+
};
|
|
180
|
+
|
|
168
181
|
return (
|
|
169
182
|
<LineEditorBody
|
|
170
|
-
key={
|
|
183
|
+
key={bodyKey}
|
|
171
184
|
id={id}
|
|
172
185
|
initialValue={value}
|
|
173
186
|
onChange={(next) => {
|
|
@@ -179,7 +192,7 @@ export default function PlaceholderLineEditor({
|
|
|
179
192
|
readOnly={readOnly}
|
|
180
193
|
flat={flat}
|
|
181
194
|
placeholder={placeholder}
|
|
182
|
-
onEditorReady={
|
|
195
|
+
onEditorReady={reportEditorReady}
|
|
183
196
|
ariaLabel={ariaLabel}
|
|
184
197
|
/>
|
|
185
198
|
);
|