@jant/core 0.6.14 → 0.6.15
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/bin/commands/import-site.js +81 -13
- package/bin/commands/migrate.js +55 -0
- package/bin/commands/posts/rebuild-html.js +184 -0
- package/bin/commands/site/snapshot/import.js +14 -10
- package/bin/lib/d1-query.js +23 -5
- package/bin/lib/migration-runner.js +29 -7
- package/bin/lib/node-database.js +32 -0
- package/bin/lib/site-snapshot.js +347 -46
- package/bin/lib/sql-export.js +1 -1
- package/bin/lib/thread-collection-migration.js +326 -0
- package/dist/app-BxCOR3Uc.js +6 -0
- package/dist/{app-2i7-WQHg.js → app-C-oi_t8W.js} +7408 -6343
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-B_eGKN5p.css +2 -0
- package/dist/client/_assets/{client-BvID5ucz.js → client-DxY5BFe8.js} +58 -37
- package/dist/client/_assets/client-auth-CakPESv9.js +5533 -0
- package/dist/{export-DWn149b7.js → export-CsFx6F_M.js} +1259 -125
- package/dist/{github-sync-Ckk0sJxK.js → github-sync-Cq1pzzOI.js} +1 -1
- package/dist/{github-sync-C0zyF6XS.js → github-sync-ppWXN3jb.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/node.js +5 -5
- package/package.json +11 -10
- package/src/__tests__/bin/posts-rebuild-html.test.ts +137 -0
- package/src/__tests__/dev-navigation-defaults.test.ts +22 -0
- package/src/__tests__/export-hugo-build.test.ts +63 -4
- package/src/__tests__/export-import-roundtrip.test.ts +6 -4
- package/src/__tests__/export-service.test.ts +142 -9
- package/src/__tests__/helpers/app.ts +3 -0
- package/src/__tests__/helpers/export-fixtures.ts +2 -0
- package/src/__tests__/import-site-command.test.ts +65 -0
- package/src/__tests__/vite-config.test.ts +45 -0
- package/src/app.tsx +16 -3
- package/src/client/__tests__/collection-created-notice.test.ts +36 -0
- package/src/client/__tests__/collection-form-bridge.test.ts +10 -1
- package/src/client/__tests__/collection-page-actions.test.ts +71 -2
- package/src/client/__tests__/compose-bridge.test.ts +64 -0
- package/src/client/__tests__/feed-video-player.test.ts +16 -1
- package/src/client/__tests__/footnote-rail.test.ts +472 -0
- package/src/client/__tests__/hydrate-partial.test.ts +3 -0
- package/src/client/__tests__/toast.test.ts +18 -1
- package/src/client/collection-created-notice.ts +66 -0
- package/src/client/collection-form-bridge.ts +17 -0
- package/src/client/collection-navigation.ts +44 -0
- package/src/client/collection-page-actions.ts +57 -1
- package/src/client/components/__tests__/jant-collection-directory.test.ts +155 -6
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +370 -5
- package/src/client/components/__tests__/jant-compose-editor.test.ts +97 -0
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +104 -0
- package/src/client/components/__tests__/jant-config-editor.test.ts +405 -0
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +121 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +538 -2
- package/src/client/components/__tests__/jant-post-form.test.ts +2 -2
- package/src/client/components/__tests__/jant-post-menu.test.ts +47 -5
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-manager-types.ts +9 -3
- package/src/client/components/compose-types.ts +8 -0
- package/src/client/components/jant-collection-directory.ts +165 -30
- package/src/client/components/jant-command-palette.ts +2 -1
- package/src/client/components/jant-compose-dialog.ts +85 -21
- package/src/client/components/jant-compose-editor.ts +13 -7
- package/src/client/components/jant-compose-fullscreen.ts +24 -4
- package/src/client/components/jant-config-editor.ts +722 -0
- package/src/client/components/jant-media-lightbox.ts +100 -18
- package/src/client/components/jant-nav-manager.ts +1283 -54
- package/src/client/components/jant-post-form.ts +3 -3
- package/src/client/components/jant-post-menu.ts +86 -74
- package/src/client/components/nav-manager-types.ts +51 -2
- package/src/client/components/post-form-types.ts +1 -1
- package/src/client/compose-bridge.ts +4 -1
- package/src/client/feed-video-player.ts +62 -2
- package/src/client/footnote-rail.ts +478 -0
- package/src/client/hydrate-partial.ts +2 -0
- package/src/client/media-lightbox-events.ts +63 -0
- package/src/client/site-header-fragment.ts +38 -0
- package/src/client/tiptap/__tests__/blockquote-editing.test.ts +201 -0
- package/src/client/tiptap/__tests__/clear-formatting.test.ts +168 -0
- package/src/client/tiptap/__tests__/list-editing.test.ts +447 -0
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +727 -4
- package/src/client/tiptap/__tests__/table-controls.test.ts +263 -0
- package/src/client/tiptap/__tests__/table-size-picker.test.ts +179 -0
- package/src/client/tiptap/bubble-menu.ts +88 -10
- package/src/client/tiptap/continuous-lists.ts +102 -0
- package/src/client/tiptap/create-editor.ts +3 -0
- package/src/client/tiptap/extensions.ts +14 -5
- package/src/client/tiptap/markdown-clipboard.ts +608 -11
- package/src/client/tiptap/slash-commands.ts +8 -1
- package/src/client/tiptap/structural-keymap.ts +211 -0
- package/src/client/tiptap/tab-indent.ts +8 -22
- package/src/client/tiptap/table-control-labels.ts +30 -0
- package/src/client/tiptap/table-controls.ts +537 -0
- package/src/client/tiptap/table-size-picker.ts +254 -0
- package/src/client/toast.ts +8 -6
- package/src/client-auth.ts +1 -0
- package/src/client.ts +1 -0
- package/src/db/__tests__/d1-query.test.ts +43 -1
- package/src/db/__tests__/migration-runner.test.ts +63 -0
- package/src/db/__tests__/migrations.test.ts +469 -0
- package/src/db/__tests__/thread-collection-migration.test.ts +364 -0
- package/src/db/migrations/0027_old_blue_blade.sql +39 -0
- package/src/db/migrations/0028_superb_post.sql +2 -0
- package/src/db/migrations/0029_boring_magma.sql +1 -0
- package/src/db/migrations/0030_typical_vivisector.sql +50 -0
- package/src/db/migrations/meta/0027_snapshot.json +2511 -0
- package/src/db/migrations/meta/0028_snapshot.json +2511 -0
- package/src/db/migrations/meta/0029_snapshot.json +2519 -0
- package/src/db/migrations/meta/0030_snapshot.json +2541 -0
- package/src/db/migrations/meta/_journal.json +28 -0
- package/src/db/migrations/pg/0025_moaning_epoch.sql +39 -0
- package/src/db/migrations/pg/0026_talented_killer_shrike.sql +2 -0
- package/src/db/migrations/pg/0027_short_cannonball.sql +1 -0
- package/src/db/migrations/pg/0028_perfect_sharon_carter.sql +27 -0
- package/src/db/migrations/pg/meta/0025_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0026_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0027_snapshot.json +3211 -0
- package/src/db/migrations/pg/meta/0028_snapshot.json +3248 -0
- package/src/db/migrations/pg/meta/_journal.json +28 -0
- package/src/db/pg/schema.ts +28 -20
- package/src/db/rehearsal-fixtures/demo-current.json +39 -4
- package/src/db/rehearsal-fixtures/demo-current.sql +121 -6
- package/src/db/schema.ts +28 -20
- package/src/i18n/__tests__/detect.test.ts +8 -2
- package/src/i18n/detect.ts +14 -5
- package/src/i18n/locales/public/en.po +133 -30
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +128 -25
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +128 -25
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +513 -10
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +515 -12
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +515 -12
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/api-settings.test.ts +299 -0
- package/src/lib/__tests__/constants.test.ts +5 -0
- package/src/lib/__tests__/feed-policy.test.ts +25 -0
- package/src/lib/__tests__/footnotes-upgrade.test.ts +181 -0
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +61 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +32 -4
- package/src/lib/__tests__/markdown.test.ts +8 -6
- package/src/lib/__tests__/navigation.test.ts +59 -2
- package/src/lib/__tests__/post-body-html.test.ts +176 -0
- package/src/lib/__tests__/post-display.test.ts +64 -0
- package/src/lib/__tests__/resolve-config.test.ts +113 -1
- package/src/lib/__tests__/summary.test.ts +82 -0
- package/src/lib/__tests__/timeline.test.ts +109 -70
- package/src/lib/__tests__/timezones.test.ts +31 -3
- package/src/lib/__tests__/tiptap-render.test.ts +268 -9
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +6 -0
- package/src/lib/__tests__/view.test.ts +53 -0
- package/src/lib/__tests__/worker-response-cache.test.ts +18 -1
- package/src/lib/api-settings.ts +239 -12
- package/src/lib/collection-sort.ts +7 -5
- package/src/lib/constants.ts +2 -0
- package/src/lib/feed-path.ts +25 -0
- package/src/lib/feed-policy.ts +64 -0
- package/src/lib/footnotes.ts +264 -0
- package/src/lib/github-sync-site-config.ts +2 -0
- package/src/lib/hugo-markdown.ts +10 -3
- package/src/lib/jant-branding.ts +1 -0
- package/src/lib/markdown-manager.ts +188 -5
- package/src/lib/markdown.ts +10 -3
- package/src/lib/navigation.ts +16 -3
- package/src/lib/post-body-html.ts +131 -0
- package/src/lib/post-display.ts +7 -1
- package/src/lib/render.tsx +9 -2
- package/src/lib/resolve-config.ts +100 -16
- package/src/lib/schemas.ts +133 -1
- package/src/lib/settings-paths.ts +5 -0
- package/src/lib/site-skill.ts +78 -0
- package/src/lib/summary.ts +16 -5
- package/src/lib/timeline.ts +98 -122
- package/src/lib/timezones.ts +54 -14
- package/src/lib/tiptap-render.ts +355 -81
- package/src/lib/view.ts +30 -20
- package/src/lib/worker-response-cache.ts +11 -18
- package/src/middleware/__tests__/public-content-access.test.ts +91 -0
- package/src/middleware/config.ts +2 -2
- package/src/middleware/public-content-access.ts +87 -0
- package/src/node/__tests__/cli-export.test.ts +14 -0
- package/src/node/__tests__/cli-migrate.test.ts +6 -0
- package/src/node/__tests__/cli-site-snapshot.test.ts +205 -2
- package/src/node/__tests__/cli-snapshot-meta.test.ts +100 -0
- package/src/node/index.ts +2 -1
- package/src/preset.css +16 -2
- package/src/routes/__tests__/compose.test.ts +51 -0
- package/src/routes/__tests__/site-skill.test.ts +112 -0
- package/src/routes/api/__tests__/collections.test.ts +91 -19
- package/src/routes/api/__tests__/mcp.test.ts +65 -1
- package/src/routes/api/__tests__/nav-items.test.ts +91 -0
- package/src/routes/api/__tests__/public-access.test.ts +81 -0
- package/src/routes/api/__tests__/settings.test.ts +217 -0
- package/src/routes/api/collections.ts +22 -26
- package/src/routes/api/export.ts +2 -0
- package/src/routes/api/internal/__tests__/post-body-html.test.ts +163 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +35 -2
- package/src/routes/api/internal/post-body-html.ts +43 -0
- package/src/routes/api/internal/sites.ts +24 -0
- package/src/routes/api/nav-items.ts +24 -1
- package/src/routes/api/posts.ts +2 -2
- package/src/routes/api/public/__tests__/archive.test.ts +35 -0
- package/src/routes/api/public/__tests__/posts.test.ts +208 -0
- package/src/routes/api/public/archive.ts +4 -0
- package/src/routes/api/public/posts.ts +41 -24
- package/src/routes/api/search.ts +3 -0
- package/src/routes/api/settings.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +7 -4
- package/src/routes/compose.tsx +2 -1
- package/src/routes/dash/settings.tsx +45 -2
- package/src/routes/feed/__tests__/feed.test.ts +210 -6
- package/src/routes/feed/feed.ts +23 -14
- package/src/routes/pages/__tests__/archive-params.test.ts +92 -3
- package/src/routes/pages/__tests__/collection-routing.test.ts +63 -5
- package/src/routes/pages/__tests__/collections.test.ts +9 -9
- package/src/routes/pages/__tests__/preview.test.ts +174 -0
- package/src/routes/pages/__tests__/search.test.ts +69 -0
- package/src/routes/pages/archive.tsx +27 -7
- package/src/routes/pages/collection.tsx +32 -19
- package/src/routes/pages/collections.tsx +6 -1
- package/src/routes/pages/page.tsx +83 -11
- package/src/routes/site-skill.ts +18 -0
- package/src/services/__tests__/auth.test.ts +7 -5
- package/src/services/__tests__/collection.test.ts +159 -44
- package/src/services/__tests__/navigation.test.ts +229 -21
- package/src/services/__tests__/post-timeline.test.ts +109 -30
- package/src/services/__tests__/post.test.ts +567 -5
- package/src/services/__tests__/search.test.ts +48 -1
- package/src/services/__tests__/settings.test.ts +8 -0
- package/src/services/__tests__/site-admin.test.ts +121 -1
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +3 -2
- package/src/services/collection.ts +226 -167
- package/src/services/export-theme/assets/client-site.css +1 -1
- package/src/services/export-theme/assets/client-site.js +46 -25
- package/src/services/export-theme/layouts/_default/list.html +18 -4
- package/src/services/export-theme/layouts/_default/rss.xml +8 -12
- package/src/services/export-theme/layouts/featured/list.html +5 -4
- package/src/services/export-theme/layouts/partials/featured-thread.html +56 -0
- package/src/services/export-theme/layouts/partials/footer.html +1 -1
- package/src/services/export-theme/layouts/partials/media-gallery.html +1 -0
- package/src/services/export-theme/styles/main.css +30 -2
- package/src/services/export.ts +91 -80
- package/src/services/mcp.ts +37 -27
- package/src/services/media.ts +1 -1
- package/src/services/navigation.ts +185 -46
- package/src/services/post.ts +874 -271
- package/src/services/search.ts +8 -2
- package/src/services/settings.ts +2 -10
- package/src/services/site-admin.ts +63 -43
- package/src/style-cjk-jp.css +1 -1
- package/src/style-cjk-kr.css +1 -1
- package/src/style-cjk-tc.css +1 -1
- package/src/style-cjk.css +1 -1
- package/src/styles/components.css +540 -0
- package/src/styles/site-media.css +111 -19
- package/src/styles/tokens.css +14 -9
- package/src/styles/ui.css +829 -29
- package/src/types/bindings.ts +3 -0
- package/src/types/config.ts +243 -12
- package/src/types/constants.ts +22 -1
- package/src/types/entities.ts +9 -8
- package/src/types/operations.ts +18 -10
- package/src/types/props.ts +8 -2
- package/src/types/raw-assets.d.ts +5 -0
- package/src/types/views.ts +16 -4
- package/src/ui/__tests__/font-themes.test.ts +42 -30
- package/src/ui/compose/ComposeDialog.tsx +92 -2
- package/src/ui/dash/appearance/NavigationContent.tsx +332 -4
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +23 -0
- package/src/ui/dash/settings/ConfigEditorContent.tsx +578 -0
- package/src/ui/dash/settings/GeneralContent.tsx +15 -5
- package/src/ui/dash/settings/SettingsRootContent.tsx +21 -1
- package/src/ui/dash/settings/__tests__/ConfigEditorContent.test.tsx +98 -0
- package/src/ui/dash/settings/__tests__/SettingsRootContent.test.tsx +15 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +16 -4
- package/src/ui/feed/LinkCard.tsx +2 -12
- package/src/ui/feed/NoteCard.tsx +10 -20
- package/src/ui/feed/QuoteCard.tsx +2 -12
- package/src/ui/feed/ThreadPreview.tsx +26 -34
- package/src/ui/feed/TimelineFeed.tsx +2 -2
- package/src/ui/feed/__tests__/thread-preview.test.ts +197 -56
- package/src/ui/feed/__tests__/timeline-cards.test.ts +178 -3
- package/src/ui/feed/thread-preview-state.ts +5 -7
- package/src/ui/font-themes.ts +74 -26
- package/src/ui/layouts/BaseLayout.tsx +22 -8
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +64 -1
- package/src/ui/pages/CollectionPage.tsx +101 -22
- package/src/ui/pages/CollectionsPage.tsx +3 -1
- package/src/ui/pages/PostPage.tsx +39 -12
- package/src/ui/pages/SearchPage.tsx +6 -14
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +7 -0
- package/src/ui/pages/__tests__/PostPage.test.tsx +88 -0
- package/src/ui/shared/CollectionDirectory.tsx +13 -9
- package/src/ui/shared/CollectionsManager.tsx +17 -7
- package/src/ui/shared/DraftPreviewBar.tsx +33 -0
- package/src/ui/shared/HomePageBranding.tsx +2 -2
- package/src/ui/shared/MediaGallery.tsx +1 -0
- package/src/ui/shared/PostFooter.tsx +3 -1
- package/src/ui/shared/__tests__/home-page-branding.test.tsx +1 -1
- package/src/ui/shared/__tests__/media-gallery.test.ts +1 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +2 -2
- package/src/ui/shared/__tests__/post-footer.test.ts +15 -0
- package/src/ui/shared/collection-management-labels.ts +33 -2
- package/src/ui/shared/navigation-labels.ts +2 -4
- package/src/ui/shared/post-article-attributes.ts +46 -0
- package/dist/app-DZDlmh7C.js +0 -6
- package/dist/client/_assets/client-Cevfpm8H.css +0 -2
- package/dist/client/_assets/client-auth-v3yvhbgZ.js +0 -4736
- package/src/client/tiptap/wrapping-input-rules.ts +0 -102
|
@@ -7,6 +7,8 @@ import type { Editor } from "@tiptap/core";
|
|
|
7
7
|
import { MAX_THREAD_POSTS } from "../../../types.js";
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
|
+
ComposeFullscreenCloseDetail,
|
|
11
|
+
ComposeFullscreenOpenDetail,
|
|
10
12
|
ComposeLabels,
|
|
11
13
|
ComposeCollection,
|
|
12
14
|
ComposeSubmitDetail,
|
|
@@ -181,6 +183,8 @@ const labels: ComposeLabels = {
|
|
|
181
183
|
discardChangesConfirm: "Discard changes?",
|
|
182
184
|
drafts: "Drafts",
|
|
183
185
|
draftsEmpty: "No drafts yet. Save a draft to find it here.",
|
|
186
|
+
previewDraft: "Preview",
|
|
187
|
+
draftActions: "Draft actions",
|
|
184
188
|
deleteDraft: "Delete Draft",
|
|
185
189
|
draftDeleted: "Draft deleted.",
|
|
186
190
|
publishFailedDraft: "Couldn't publish. Saved as draft.",
|
|
@@ -233,6 +237,20 @@ const labels: ComposeLabels = {
|
|
|
233
237
|
replyTitle: "Reply",
|
|
234
238
|
editTitle: "Edit",
|
|
235
239
|
slashHint: "Type / for commands",
|
|
240
|
+
tableControls: {
|
|
241
|
+
toolbarLabel: "Table controls",
|
|
242
|
+
addRowAbove: "Add row above",
|
|
243
|
+
addRowBelow: "Add row below",
|
|
244
|
+
addColumnBefore: "Add column before",
|
|
245
|
+
addColumnAfter: "Add column after",
|
|
246
|
+
options: "Table options",
|
|
247
|
+
deleteRow: "Delete row",
|
|
248
|
+
deleteColumn: "Delete column",
|
|
249
|
+
toggleHeaderRow: "Toggle header row",
|
|
250
|
+
deleteTable: "Delete table",
|
|
251
|
+
sizePickerLabel: "Choose table size",
|
|
252
|
+
insertTableSize: "Insert %rows% by %cols% table",
|
|
253
|
+
},
|
|
236
254
|
collectionFormLabels: {
|
|
237
255
|
titleLabel: "Title",
|
|
238
256
|
titlePlaceholder: "My Collection",
|
|
@@ -280,6 +298,7 @@ describe("JantComposeDialog", () => {
|
|
|
280
298
|
vi.restoreAllMocks();
|
|
281
299
|
document.body.innerHTML = "";
|
|
282
300
|
globalThis.localStorage.clear();
|
|
301
|
+
delete document.documentElement.dataset.sitePathPrefix;
|
|
283
302
|
Object.defineProperty(globalThis, "matchMedia", {
|
|
284
303
|
configurable: true,
|
|
285
304
|
writable: true,
|
|
@@ -647,6 +666,11 @@ describe("JantComposeDialog", () => {
|
|
|
647
666
|
expect(
|
|
648
667
|
el.querySelector(".compose-thread-layout.compose-reply-compose-layout"),
|
|
649
668
|
).not.toBeNull();
|
|
669
|
+
expect(el.querySelector(".compose-collection-trigger")).toBeNull();
|
|
670
|
+
expect(
|
|
671
|
+
el.querySelector(".compose-action-row-without-collection"),
|
|
672
|
+
).not.toBeNull();
|
|
673
|
+
expect(el.querySelector(".compose-publish-main")).not.toBeNull();
|
|
650
674
|
});
|
|
651
675
|
|
|
652
676
|
it("shows an Edit title with the format selector above the post when editing a reply", async () => {
|
|
@@ -668,6 +692,7 @@ describe("JantComposeDialog", () => {
|
|
|
668
692
|
title: "Hello",
|
|
669
693
|
body: null,
|
|
670
694
|
replyToId: parentId,
|
|
695
|
+
collectionIds: ["col-thread"],
|
|
671
696
|
};
|
|
672
697
|
return Promise.resolve(
|
|
673
698
|
new Response(JSON.stringify(json), {
|
|
@@ -687,6 +712,8 @@ describe("JantComposeDialog", () => {
|
|
|
687
712
|
el.querySelector(".compose-dialog-header-center .compose-segmented"),
|
|
688
713
|
).toBeNull();
|
|
689
714
|
expect(el.querySelector(".compose-thread-post-header")).not.toBeNull();
|
|
715
|
+
expect(el._collectionIds).toEqual([]);
|
|
716
|
+
expect(el.querySelector(".compose-collection-trigger")).toBeNull();
|
|
690
717
|
});
|
|
691
718
|
|
|
692
719
|
it("switches format from the inline selector when replying", async () => {
|
|
@@ -2281,7 +2308,7 @@ describe("JantComposeDialog", () => {
|
|
|
2281
2308
|
rating: 0,
|
|
2282
2309
|
showTitle: false,
|
|
2283
2310
|
showRating: false,
|
|
2284
|
-
collectionIds: [],
|
|
2311
|
+
collectionIds: ["col-stale"],
|
|
2285
2312
|
replyToId,
|
|
2286
2313
|
attachedTexts: [],
|
|
2287
2314
|
attachmentOrder: [],
|
|
@@ -2300,6 +2327,8 @@ describe("JantComposeDialog", () => {
|
|
|
2300
2327
|
"expected compose editor",
|
|
2301
2328
|
);
|
|
2302
2329
|
expect(el._replyToId).toBe(replyToId);
|
|
2330
|
+
expect(el._collectionIds).toEqual([]);
|
|
2331
|
+
expect(el.querySelector(".compose-collection-trigger")).toBeNull();
|
|
2303
2332
|
expect(editor.getData().body).toContain("Recovered reply draft");
|
|
2304
2333
|
});
|
|
2305
2334
|
|
|
@@ -2348,6 +2377,56 @@ describe("JantComposeDialog", () => {
|
|
|
2348
2377
|
);
|
|
2349
2378
|
});
|
|
2350
2379
|
|
|
2380
|
+
it("includes quiet reply intent when submitting multiple reply posts", async () => {
|
|
2381
|
+
const el = await createElement();
|
|
2382
|
+
await el.openReply("019ce8ce-d6d8-7fda-a5df-c2da2bef5ade", {
|
|
2383
|
+
contentHtml: "<p>Parent</p>",
|
|
2384
|
+
dateText: "Mar 14",
|
|
2385
|
+
});
|
|
2386
|
+
|
|
2387
|
+
(
|
|
2388
|
+
el as unknown as {
|
|
2389
|
+
_addThreadItem: () => void;
|
|
2390
|
+
}
|
|
2391
|
+
)._addThreadItem();
|
|
2392
|
+
await flushUpdates(el);
|
|
2393
|
+
|
|
2394
|
+
el._quietReply = true;
|
|
2395
|
+
const editors = Array.from(
|
|
2396
|
+
el.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
2397
|
+
);
|
|
2398
|
+
expect(editors).toHaveLength(2);
|
|
2399
|
+
|
|
2400
|
+
editors.forEach((editor, index) => {
|
|
2401
|
+
editor._bodyJson = {
|
|
2402
|
+
type: "doc",
|
|
2403
|
+
content: [
|
|
2404
|
+
{
|
|
2405
|
+
type: "paragraph",
|
|
2406
|
+
content: [{ type: "text", text: `Quiet reply ${index + 1}` }],
|
|
2407
|
+
},
|
|
2408
|
+
],
|
|
2409
|
+
};
|
|
2410
|
+
});
|
|
2411
|
+
await Promise.all(editors.map((editor) => editor.updateComplete));
|
|
2412
|
+
await el.updateComplete;
|
|
2413
|
+
|
|
2414
|
+
let receivedDetail: ComposeSubmitDetail | null = null;
|
|
2415
|
+
el.addEventListener("jant:compose-submit-deferred", (event) => {
|
|
2416
|
+
receivedDetail = (event as CustomEvent<ComposeSubmitDetail>).detail;
|
|
2417
|
+
});
|
|
2418
|
+
|
|
2419
|
+
requireElement(
|
|
2420
|
+
el.querySelector<HTMLButtonElement>(".compose-publish-main"),
|
|
2421
|
+
"expected reply button",
|
|
2422
|
+
).click();
|
|
2423
|
+
|
|
2424
|
+
expect(receivedDetail).not.toBeNull();
|
|
2425
|
+
const detail = receivedDetail as unknown as ComposeSubmitDetail;
|
|
2426
|
+
expect(detail.quietReply).toBe(true);
|
|
2427
|
+
expect(detail.threadPosts?.[0]?.quietReply).toBe(true);
|
|
2428
|
+
});
|
|
2429
|
+
|
|
2351
2430
|
it("does not add more thread items than the shared limit", async () => {
|
|
2352
2431
|
const el = await createElement();
|
|
2353
2432
|
el._threadItems = Array.from({ length: MAX_THREAD_POSTS }, (_, index) => ({
|
|
@@ -2364,6 +2443,34 @@ describe("JantComposeDialog", () => {
|
|
|
2364
2443
|
expect(el._threadItems).toHaveLength(MAX_THREAD_POSTS);
|
|
2365
2444
|
});
|
|
2366
2445
|
|
|
2446
|
+
it("scrolls the thread composer to the bottom after adding an item", async () => {
|
|
2447
|
+
const el = await createElement();
|
|
2448
|
+
el._threadItems = [
|
|
2449
|
+
{ id: "thread-1", format: "note" },
|
|
2450
|
+
{ id: "thread-2", format: "note" },
|
|
2451
|
+
];
|
|
2452
|
+
await el.updateComplete;
|
|
2453
|
+
|
|
2454
|
+
const threadLayout = requireElement(
|
|
2455
|
+
el.querySelector<HTMLElement>(".compose-thread-compose-layout"),
|
|
2456
|
+
"expected thread compose layout",
|
|
2457
|
+
);
|
|
2458
|
+
Object.defineProperty(threadLayout, "scrollHeight", {
|
|
2459
|
+
configurable: true,
|
|
2460
|
+
value: 640,
|
|
2461
|
+
});
|
|
2462
|
+
threadLayout.scrollTop = 120;
|
|
2463
|
+
|
|
2464
|
+
(
|
|
2465
|
+
el as unknown as {
|
|
2466
|
+
_addThreadItem: () => void;
|
|
2467
|
+
}
|
|
2468
|
+
)._addThreadItem();
|
|
2469
|
+
await flushUpdates(el);
|
|
2470
|
+
|
|
2471
|
+
expect(threadLayout.scrollTop).toBe(640);
|
|
2472
|
+
});
|
|
2473
|
+
|
|
2367
2474
|
it("omits visibility from locked edit submissions", async () => {
|
|
2368
2475
|
const el = await createElement();
|
|
2369
2476
|
const editor = requireElement(
|
|
@@ -2978,6 +3085,67 @@ describe("JantComposeDialog", () => {
|
|
|
2978
3085
|
fetchSpy.mockRestore();
|
|
2979
3086
|
});
|
|
2980
3087
|
|
|
3088
|
+
it("opens a prefixed draft preview from the overflow menu without loading the editor", async () => {
|
|
3089
|
+
document.documentElement.dataset.sitePathPrefix = "/blog";
|
|
3090
|
+
const el = await createElement();
|
|
3091
|
+
el._draftsPanelOpen = true;
|
|
3092
|
+
el._draftsLoading = false;
|
|
3093
|
+
el._drafts = [
|
|
3094
|
+
{
|
|
3095
|
+
id: "pst_draft",
|
|
3096
|
+
slug: "draft-slug",
|
|
3097
|
+
format: "note",
|
|
3098
|
+
title: "Preview me",
|
|
3099
|
+
bodyText: "Draft body",
|
|
3100
|
+
bodyHtml: "<p>Draft body</p>",
|
|
3101
|
+
url: null,
|
|
3102
|
+
quoteText: null,
|
|
3103
|
+
replyToId: null,
|
|
3104
|
+
updatedAt: 0,
|
|
3105
|
+
mediaAttachments: [],
|
|
3106
|
+
},
|
|
3107
|
+
];
|
|
3108
|
+
await el.updateComplete;
|
|
3109
|
+
|
|
3110
|
+
const trigger = requireElement(
|
|
3111
|
+
el.querySelector<HTMLButtonElement>(".compose-draft-more"),
|
|
3112
|
+
"expected draft actions trigger",
|
|
3113
|
+
);
|
|
3114
|
+
expect(trigger.getAttribute("aria-label")).toBe("Draft actions");
|
|
3115
|
+
expect(trigger.getAttribute("aria-haspopup")).toBe("menu");
|
|
3116
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("false");
|
|
3117
|
+
|
|
3118
|
+
trigger.click();
|
|
3119
|
+
await el.updateComplete;
|
|
3120
|
+
|
|
3121
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("true");
|
|
3122
|
+
const menu = requireElement(
|
|
3123
|
+
el.querySelector<HTMLElement>('[role="menu"]'),
|
|
3124
|
+
"expected draft actions menu",
|
|
3125
|
+
);
|
|
3126
|
+
const items = menu.querySelectorAll<HTMLElement>('[role="menuitem"]');
|
|
3127
|
+
expect(items).toHaveLength(2);
|
|
3128
|
+
expect(items[0]?.textContent?.trim()).toBe("Preview");
|
|
3129
|
+
expect(items[1]?.textContent?.trim()).toBe("Delete Draft");
|
|
3130
|
+
|
|
3131
|
+
const previewLink = requireElement(
|
|
3132
|
+
menu.querySelector<HTMLAnchorElement>('a[role="menuitem"]'),
|
|
3133
|
+
"expected preview link",
|
|
3134
|
+
);
|
|
3135
|
+
expect(previewLink.getAttribute("href")).toBe("/blog/preview/draft-slug");
|
|
3136
|
+
expect(previewLink.target).toBe("_blank");
|
|
3137
|
+
expect(previewLink.rel).toBe("noopener noreferrer");
|
|
3138
|
+
|
|
3139
|
+
const fetchSpy = vi.spyOn(globalThis, "fetch");
|
|
3140
|
+
previewLink.addEventListener("click", (event) => event.preventDefault());
|
|
3141
|
+
previewLink.click();
|
|
3142
|
+
await el.updateComplete;
|
|
3143
|
+
|
|
3144
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
3145
|
+
expect(el._draftsPanelOpen).toBe(true);
|
|
3146
|
+
expect(el._draftMenuOpenId).toBeNull();
|
|
3147
|
+
});
|
|
3148
|
+
|
|
2981
3149
|
it("does not dispatch submit when loading", async () => {
|
|
2982
3150
|
const el = await createElement();
|
|
2983
3151
|
el._loading = true;
|
|
@@ -3103,6 +3271,17 @@ describe("JantComposeDialog", () => {
|
|
|
3103
3271
|
);
|
|
3104
3272
|
});
|
|
3105
3273
|
|
|
3274
|
+
it("keeps passive footnote references quiet until the editor selects them", () => {
|
|
3275
|
+
const css = readFileSync(resolve("src/styles/ui.css"), "utf8");
|
|
3276
|
+
|
|
3277
|
+
expect(css).toMatch(
|
|
3278
|
+
/\.compose-tiptap-body \.tiptap \.tiptap-footnote-reference\s*\{[^}]*color:\s*var\(--site-footnote-marker\);/,
|
|
3279
|
+
);
|
|
3280
|
+
expect(css).toMatch(
|
|
3281
|
+
/\.tiptap-footnote-reference\.ProseMirror-selectednode\s*\{[^}]*color:\s*var\(--site-accent\);[^}]*outline:\s*1px solid var\(--site-accent\);/,
|
|
3282
|
+
);
|
|
3283
|
+
});
|
|
3284
|
+
|
|
3106
3285
|
it("remove button clears attachment", async () => {
|
|
3107
3286
|
const el = await createElement();
|
|
3108
3287
|
const editor = requireElement(
|
|
@@ -3879,6 +4058,57 @@ describe("JantComposeDialog", () => {
|
|
|
3879
4058
|
expect(tiptap.getText()).toBe("");
|
|
3880
4059
|
});
|
|
3881
4060
|
|
|
4061
|
+
it("Escape cancels the table size picker without closing compose", async () => {
|
|
4062
|
+
const el = await createElement();
|
|
4063
|
+
const editor = requireElement(
|
|
4064
|
+
el.querySelector<JantComposeEditor>("jant-compose-editor"),
|
|
4065
|
+
"expected compose editor",
|
|
4066
|
+
);
|
|
4067
|
+
const tiptap = requireEditor(editor);
|
|
4068
|
+
|
|
4069
|
+
tiptap.commands.focus("end");
|
|
4070
|
+
tiptap.commands.insertContent("/tab");
|
|
4071
|
+
await flushUpdates(el);
|
|
4072
|
+
|
|
4073
|
+
const tableItem = Array.from(
|
|
4074
|
+
document.querySelectorAll<HTMLElement>(".tiptap-slash-item"),
|
|
4075
|
+
).find((item) => item.textContent?.includes("Table"));
|
|
4076
|
+
requireElement(
|
|
4077
|
+
tableItem ?? null,
|
|
4078
|
+
"expected Table slash item",
|
|
4079
|
+
).dispatchEvent(
|
|
4080
|
+
new globalThis.MouseEvent("mousedown", {
|
|
4081
|
+
bubbles: true,
|
|
4082
|
+
cancelable: true,
|
|
4083
|
+
}),
|
|
4084
|
+
);
|
|
4085
|
+
await vi.waitFor(() => {
|
|
4086
|
+
expect(
|
|
4087
|
+
document.querySelector(".tiptap-table-size-picker"),
|
|
4088
|
+
).not.toBeNull();
|
|
4089
|
+
});
|
|
4090
|
+
|
|
4091
|
+
const requestCloseSpy = vi.spyOn(el, "requestClose");
|
|
4092
|
+
const currentSize = requireElement(
|
|
4093
|
+
document.querySelector<HTMLButtonElement>(
|
|
4094
|
+
".tiptap-table-size-cell.is-current",
|
|
4095
|
+
),
|
|
4096
|
+
"expected current table size",
|
|
4097
|
+
);
|
|
4098
|
+
const escapeEvent = new globalThis.KeyboardEvent("keydown", {
|
|
4099
|
+
key: "Escape",
|
|
4100
|
+
bubbles: true,
|
|
4101
|
+
cancelable: true,
|
|
4102
|
+
});
|
|
4103
|
+
currentSize.dispatchEvent(escapeEvent);
|
|
4104
|
+
await flushUpdates(el);
|
|
4105
|
+
|
|
4106
|
+
expect(escapeEvent.defaultPrevented).toBe(true);
|
|
4107
|
+
expect(requestCloseSpy).not.toHaveBeenCalled();
|
|
4108
|
+
expect(document.querySelector(".tiptap-table-size-picker")).toBeNull();
|
|
4109
|
+
expect(tiptap.getText()).toBe("");
|
|
4110
|
+
});
|
|
4111
|
+
|
|
3882
4112
|
it("shows a clear empty state when slash commands have no matches", async () => {
|
|
3883
4113
|
const el = await createElement();
|
|
3884
4114
|
const editor = requireElement(
|
|
@@ -4101,10 +4331,12 @@ describe("JantComposeDialog", () => {
|
|
|
4101
4331
|
};
|
|
4102
4332
|
await editor.updateComplete;
|
|
4103
4333
|
|
|
4104
|
-
const submitSpy = vi
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4334
|
+
const submitSpy = vi
|
|
4335
|
+
.spyOn(
|
|
4336
|
+
el as unknown as { _submit: (status: "published" | "draft") => void },
|
|
4337
|
+
"_submit",
|
|
4338
|
+
)
|
|
4339
|
+
.mockImplementation(() => {});
|
|
4108
4340
|
|
|
4109
4341
|
el.dispatchEvent(
|
|
4110
4342
|
new globalThis.KeyboardEvent("keydown", {
|
|
@@ -4117,6 +4349,139 @@ describe("JantComposeDialog", () => {
|
|
|
4117
4349
|
expect(submitSpy).toHaveBeenCalledWith("published");
|
|
4118
4350
|
});
|
|
4119
4351
|
|
|
4352
|
+
it("publishes the latest fullscreen content after handing it back", async () => {
|
|
4353
|
+
const el = await createElement();
|
|
4354
|
+
const editor = requireElement(
|
|
4355
|
+
el.querySelector<JantComposeEditor>("jant-compose-editor"),
|
|
4356
|
+
"expected compose editor",
|
|
4357
|
+
);
|
|
4358
|
+
const submitSpy = vi
|
|
4359
|
+
.spyOn(
|
|
4360
|
+
el as unknown as { _submit: (status: "published" | "draft") => void },
|
|
4361
|
+
"_submit",
|
|
4362
|
+
)
|
|
4363
|
+
.mockImplementation(() => {});
|
|
4364
|
+
const setEditorStateSpy = vi.spyOn(editor, "setEditorState");
|
|
4365
|
+
|
|
4366
|
+
(
|
|
4367
|
+
el as unknown as {
|
|
4368
|
+
_handleFullscreenClose: (
|
|
4369
|
+
event: CustomEvent<ComposeFullscreenCloseDetail>,
|
|
4370
|
+
) => void;
|
|
4371
|
+
}
|
|
4372
|
+
)._handleFullscreenClose(
|
|
4373
|
+
new CustomEvent<ComposeFullscreenCloseDetail>("jant:fullscreen-close", {
|
|
4374
|
+
detail: {
|
|
4375
|
+
json: {
|
|
4376
|
+
type: "doc",
|
|
4377
|
+
content: [
|
|
4378
|
+
{
|
|
4379
|
+
type: "paragraph",
|
|
4380
|
+
content: [{ type: "text", text: "Written while fullscreen" }],
|
|
4381
|
+
},
|
|
4382
|
+
],
|
|
4383
|
+
},
|
|
4384
|
+
title: "Fullscreen title",
|
|
4385
|
+
showTitle: true,
|
|
4386
|
+
replyExpanded: false,
|
|
4387
|
+
intent: "publish",
|
|
4388
|
+
editorIndex: 0,
|
|
4389
|
+
},
|
|
4390
|
+
}),
|
|
4391
|
+
);
|
|
4392
|
+
|
|
4393
|
+
expect(setEditorStateSpy).toHaveBeenCalled();
|
|
4394
|
+
expect(editor.getData()).toMatchObject({
|
|
4395
|
+
title: "Fullscreen title",
|
|
4396
|
+
body: expect.stringContaining("Written while fullscreen"),
|
|
4397
|
+
});
|
|
4398
|
+
expect(submitSpy).toHaveBeenCalledWith("published");
|
|
4399
|
+
});
|
|
4400
|
+
|
|
4401
|
+
it("does not submit an empty fullscreen post", async () => {
|
|
4402
|
+
const el = await createElement();
|
|
4403
|
+
const submitSpy = vi.spyOn(
|
|
4404
|
+
el as unknown as { _submit: (status: "published" | "draft") => void },
|
|
4405
|
+
"_submit",
|
|
4406
|
+
);
|
|
4407
|
+
|
|
4408
|
+
(
|
|
4409
|
+
el as unknown as {
|
|
4410
|
+
_handleFullscreenClose: (
|
|
4411
|
+
event: CustomEvent<ComposeFullscreenCloseDetail>,
|
|
4412
|
+
) => void;
|
|
4413
|
+
}
|
|
4414
|
+
)._handleFullscreenClose(
|
|
4415
|
+
new CustomEvent<ComposeFullscreenCloseDetail>("jant:fullscreen-close", {
|
|
4416
|
+
detail: {
|
|
4417
|
+
json: { type: "doc", content: [{ type: "paragraph" }] },
|
|
4418
|
+
title: "",
|
|
4419
|
+
showTitle: false,
|
|
4420
|
+
replyExpanded: false,
|
|
4421
|
+
intent: "publish",
|
|
4422
|
+
editorIndex: 0,
|
|
4423
|
+
},
|
|
4424
|
+
}),
|
|
4425
|
+
);
|
|
4426
|
+
|
|
4427
|
+
expect(submitSpy).not.toHaveBeenCalled();
|
|
4428
|
+
});
|
|
4429
|
+
|
|
4430
|
+
it("tracks the source editor for fullscreen thread editing", async () => {
|
|
4431
|
+
const el = await createElement();
|
|
4432
|
+
el._threadItems = [
|
|
4433
|
+
{ id: "thread-1", format: "note" },
|
|
4434
|
+
{ id: "thread-2", format: "note" },
|
|
4435
|
+
];
|
|
4436
|
+
await el.updateComplete;
|
|
4437
|
+
|
|
4438
|
+
const editors = Array.from(
|
|
4439
|
+
el.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
4440
|
+
);
|
|
4441
|
+
const openDetail: ComposeFullscreenOpenDetail = {
|
|
4442
|
+
json: null,
|
|
4443
|
+
title: "",
|
|
4444
|
+
showTitle: false,
|
|
4445
|
+
};
|
|
4446
|
+
editors[1]?.dispatchEvent(
|
|
4447
|
+
new CustomEvent<ComposeFullscreenOpenDetail>("jant:fullscreen-open", {
|
|
4448
|
+
bubbles: true,
|
|
4449
|
+
detail: openDetail,
|
|
4450
|
+
}),
|
|
4451
|
+
);
|
|
4452
|
+
|
|
4453
|
+
expect(openDetail).toMatchObject({ editorIndex: 1 });
|
|
4454
|
+
|
|
4455
|
+
(
|
|
4456
|
+
el as unknown as {
|
|
4457
|
+
_handleFullscreenClose: (
|
|
4458
|
+
event: CustomEvent<ComposeFullscreenCloseDetail>,
|
|
4459
|
+
) => void;
|
|
4460
|
+
}
|
|
4461
|
+
)._handleFullscreenClose(
|
|
4462
|
+
new CustomEvent<ComposeFullscreenCloseDetail>("jant:fullscreen-close", {
|
|
4463
|
+
detail: {
|
|
4464
|
+
json: {
|
|
4465
|
+
type: "doc",
|
|
4466
|
+
content: [
|
|
4467
|
+
{
|
|
4468
|
+
type: "paragraph",
|
|
4469
|
+
content: [{ type: "text", text: "Second thread post" }],
|
|
4470
|
+
},
|
|
4471
|
+
],
|
|
4472
|
+
},
|
|
4473
|
+
title: "",
|
|
4474
|
+
showTitle: false,
|
|
4475
|
+
replyExpanded: false,
|
|
4476
|
+
editorIndex: 1,
|
|
4477
|
+
},
|
|
4478
|
+
}),
|
|
4479
|
+
);
|
|
4480
|
+
|
|
4481
|
+
expect(editors[0]?.getData().body).toBe("");
|
|
4482
|
+
expect(editors[1]?.getData().body).toContain("Second thread post");
|
|
4483
|
+
});
|
|
4484
|
+
|
|
4120
4485
|
it("Cmd/Ctrl+Enter focuses the link URL when the URL is invalid", async () => {
|
|
4121
4486
|
const el = await createElement();
|
|
4122
4487
|
el._format = "link";
|
|
@@ -215,6 +215,8 @@ const labels: ComposeLabels = {
|
|
|
215
215
|
discardChangesConfirm: "Discard changes?",
|
|
216
216
|
drafts: "Drafts",
|
|
217
217
|
draftsEmpty: "No drafts yet. Save a draft to find it here.",
|
|
218
|
+
previewDraft: "Preview",
|
|
219
|
+
draftActions: "Draft actions",
|
|
218
220
|
deleteDraft: "Delete Draft",
|
|
219
221
|
draftDeleted: "Draft deleted.",
|
|
220
222
|
publishFailedDraft: "Couldn't publish. Saved as draft.",
|
|
@@ -267,6 +269,20 @@ const labels: ComposeLabels = {
|
|
|
267
269
|
replyTitle: "Reply",
|
|
268
270
|
editTitle: "Edit",
|
|
269
271
|
slashHint: "Type / for commands",
|
|
272
|
+
tableControls: {
|
|
273
|
+
toolbarLabel: "Table controls",
|
|
274
|
+
addRowAbove: "Add row above",
|
|
275
|
+
addRowBelow: "Add row below",
|
|
276
|
+
addColumnBefore: "Add column before",
|
|
277
|
+
addColumnAfter: "Add column after",
|
|
278
|
+
options: "Table options",
|
|
279
|
+
deleteRow: "Delete row",
|
|
280
|
+
deleteColumn: "Delete column",
|
|
281
|
+
toggleHeaderRow: "Toggle header row",
|
|
282
|
+
deleteTable: "Delete table",
|
|
283
|
+
sizePickerLabel: "Choose table size",
|
|
284
|
+
insertTableSize: "Insert %rows% by %cols% table",
|
|
285
|
+
},
|
|
270
286
|
collectionFormLabels: {
|
|
271
287
|
titleLabel: "Title",
|
|
272
288
|
titlePlaceholder: "My Collection",
|
|
@@ -788,6 +804,87 @@ describe("JantComposeEditor", () => {
|
|
|
788
804
|
expect(el.querySelector('.compose-tool-btn[title="Title"]')).toBeNull();
|
|
789
805
|
});
|
|
790
806
|
|
|
807
|
+
it.each([
|
|
808
|
+
["note", ".compose-note-title"],
|
|
809
|
+
["link", ".compose-link-title"],
|
|
810
|
+
] as const)(
|
|
811
|
+
"moves from the %s title to the start of the body on Enter",
|
|
812
|
+
async (format, selector) => {
|
|
813
|
+
const el = await createElement(format);
|
|
814
|
+
if (format === "note") {
|
|
815
|
+
el._showTitle = true;
|
|
816
|
+
await el.updateComplete;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const titleInput = requireElement(
|
|
820
|
+
el.querySelector<HTMLInputElement>(selector),
|
|
821
|
+
`expected ${format} title input`,
|
|
822
|
+
);
|
|
823
|
+
const editor = requireEditor(el);
|
|
824
|
+
titleInput.focus();
|
|
825
|
+
|
|
826
|
+
const event = new globalThis.KeyboardEvent("keydown", {
|
|
827
|
+
key: "Enter",
|
|
828
|
+
bubbles: true,
|
|
829
|
+
cancelable: true,
|
|
830
|
+
});
|
|
831
|
+
titleInput.dispatchEvent(event);
|
|
832
|
+
await new Promise<void>((resolve) => {
|
|
833
|
+
globalThis.requestAnimationFrame(() => resolve());
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
expect(event.defaultPrevented).toBe(true);
|
|
837
|
+
expect(document.activeElement).toBe(editor.view.dom);
|
|
838
|
+
expect(editor.state.selection.from).toBe(1);
|
|
839
|
+
},
|
|
840
|
+
);
|
|
841
|
+
|
|
842
|
+
it("keeps focus in the title while an IME composition is active", async () => {
|
|
843
|
+
const el = await createElement("note");
|
|
844
|
+
el._showTitle = true;
|
|
845
|
+
await el.updateComplete;
|
|
846
|
+
|
|
847
|
+
const titleInput = requireElement(
|
|
848
|
+
el.querySelector<HTMLInputElement>(".compose-note-title"),
|
|
849
|
+
"expected note title input",
|
|
850
|
+
);
|
|
851
|
+
titleInput.focus();
|
|
852
|
+
|
|
853
|
+
const event = new globalThis.KeyboardEvent("keydown", {
|
|
854
|
+
key: "Enter",
|
|
855
|
+
isComposing: true,
|
|
856
|
+
bubbles: true,
|
|
857
|
+
cancelable: true,
|
|
858
|
+
});
|
|
859
|
+
titleInput.dispatchEvent(event);
|
|
860
|
+
|
|
861
|
+
expect(event.defaultPrevented).toBe(false);
|
|
862
|
+
expect(document.activeElement).toBe(titleInput);
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
it("leaves Mod-Enter available for the publish shortcut", async () => {
|
|
866
|
+
const el = await createElement("note");
|
|
867
|
+
el._showTitle = true;
|
|
868
|
+
await el.updateComplete;
|
|
869
|
+
|
|
870
|
+
const titleInput = requireElement(
|
|
871
|
+
el.querySelector<HTMLInputElement>(".compose-note-title"),
|
|
872
|
+
"expected note title input",
|
|
873
|
+
);
|
|
874
|
+
titleInput.focus();
|
|
875
|
+
|
|
876
|
+
const event = new globalThis.KeyboardEvent("keydown", {
|
|
877
|
+
key: "Enter",
|
|
878
|
+
metaKey: true,
|
|
879
|
+
bubbles: true,
|
|
880
|
+
cancelable: true,
|
|
881
|
+
});
|
|
882
|
+
titleInput.dispatchEvent(event);
|
|
883
|
+
|
|
884
|
+
expect(event.defaultPrevented).toBe(false);
|
|
885
|
+
expect(document.activeElement).toBe(titleInput);
|
|
886
|
+
});
|
|
887
|
+
|
|
791
888
|
it("keeps title after rate and places fullscreen at the far right of the toolbar", async () => {
|
|
792
889
|
const el = await createElement("note");
|
|
793
890
|
const toolTitles = [
|
|
@@ -90,6 +90,110 @@ describe("JantComposeFullscreen", () => {
|
|
|
90
90
|
).toBeNull();
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
it.each(["metaKey", "ctrlKey"] as const)(
|
|
94
|
+
"publishes with %s+Enter and returns the latest editor state",
|
|
95
|
+
async (modifier) => {
|
|
96
|
+
const el = document.createElement(
|
|
97
|
+
"jant-compose-fullscreen",
|
|
98
|
+
) as JantComposeFullscreen;
|
|
99
|
+
el.labels = labels;
|
|
100
|
+
document.body.appendChild(el);
|
|
101
|
+
await flush(el);
|
|
102
|
+
|
|
103
|
+
document.dispatchEvent(
|
|
104
|
+
new CustomEvent("jant:fullscreen-open", {
|
|
105
|
+
detail: {
|
|
106
|
+
json: null,
|
|
107
|
+
title: "Keyboard post",
|
|
108
|
+
showTitle: true,
|
|
109
|
+
labels,
|
|
110
|
+
editorIndex: 2,
|
|
111
|
+
},
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
await flush(el);
|
|
115
|
+
|
|
116
|
+
requireEditor(el).commands.setContent({
|
|
117
|
+
type: "doc",
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: "paragraph",
|
|
121
|
+
content: [{ type: "text", text: "Publish from fullscreen" }],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
let detail: ComposeFullscreenCloseDetail | null = null;
|
|
127
|
+
document.addEventListener(
|
|
128
|
+
"jant:fullscreen-close",
|
|
129
|
+
(event) => {
|
|
130
|
+
detail = (event as CustomEvent<ComposeFullscreenCloseDetail>).detail;
|
|
131
|
+
},
|
|
132
|
+
{ once: true },
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
el.querySelector(".compose-fullscreen-dialog")?.dispatchEvent(
|
|
136
|
+
new globalThis.KeyboardEvent("keydown", {
|
|
137
|
+
key: "Enter",
|
|
138
|
+
[modifier]: true,
|
|
139
|
+
bubbles: true,
|
|
140
|
+
cancelable: true,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
await flush(el);
|
|
144
|
+
|
|
145
|
+
expect(detail).toMatchObject({
|
|
146
|
+
title: "Keyboard post",
|
|
147
|
+
intent: "publish",
|
|
148
|
+
editorIndex: 2,
|
|
149
|
+
json: {
|
|
150
|
+
type: "doc",
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: "paragraph",
|
|
154
|
+
content: [{ type: "text", text: "Publish from fullscreen" }],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
expect(el.querySelector(".compose-fullscreen-dialog")).toBeNull();
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
it("does not publish while an IME composition is active", async () => {
|
|
164
|
+
const el = document.createElement(
|
|
165
|
+
"jant-compose-fullscreen",
|
|
166
|
+
) as JantComposeFullscreen;
|
|
167
|
+
el.labels = labels;
|
|
168
|
+
document.body.appendChild(el);
|
|
169
|
+
await flush(el);
|
|
170
|
+
|
|
171
|
+
document.dispatchEvent(
|
|
172
|
+
new CustomEvent("jant:fullscreen-open", {
|
|
173
|
+
detail: {
|
|
174
|
+
json: null,
|
|
175
|
+
title: "",
|
|
176
|
+
showTitle: false,
|
|
177
|
+
labels,
|
|
178
|
+
},
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
await flush(el);
|
|
182
|
+
|
|
183
|
+
el.querySelector(".compose-fullscreen-dialog")?.dispatchEvent(
|
|
184
|
+
new globalThis.KeyboardEvent("keydown", {
|
|
185
|
+
key: "Enter",
|
|
186
|
+
metaKey: true,
|
|
187
|
+
isComposing: true,
|
|
188
|
+
bubbles: true,
|
|
189
|
+
cancelable: true,
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
await flush(el);
|
|
193
|
+
|
|
194
|
+
expect(el.querySelector(".compose-fullscreen-dialog")).not.toBeNull();
|
|
195
|
+
});
|
|
196
|
+
|
|
93
197
|
it("reveals the title on demand and returns its visibility on close", async () => {
|
|
94
198
|
const el = document.createElement(
|
|
95
199
|
"jant-compose-fullscreen",
|