diff --git a/apps/admin-x-framework/src/api/newsletters.ts b/apps/admin-x-framework/src/api/newsletters.ts index 481a07dfd4..a7290e5409 100644 --- a/apps/admin-x-framework/src/api/newsletters.ts +++ b/apps/admin-x-framework/src/api/newsletters.ts @@ -21,6 +21,7 @@ export type Newsletter = { show_header_title: boolean; title_font_category: string; title_alignment: string; + show_subhead: boolean; show_feature_image: boolean; body_font_category: string; footer_content: string | null; diff --git a/apps/admin-x-framework/src/test/responses/newsletters.json b/apps/admin-x-framework/src/test/responses/newsletters.json index 96a971542e..49c51b2668 100644 --- a/apps/admin-x-framework/src/test/responses/newsletters.json +++ b/apps/admin-x-framework/src/test/responses/newsletters.json @@ -19,6 +19,7 @@ "show_header_title": true, "title_font_category": "serif", "title_alignment": "center", + "show_subhead": true, "show_feature_image": true, "body_font_category": "serif", "footer_content": "", diff --git a/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx b/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx index 1b0d609505..5c632a9e74 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx @@ -67,6 +67,10 @@ const features = [{ title: 'ActivityPub', description: '(Highly) Experimental support for ActivityPub.', flag: 'ActivityPub' +},{ + title: 'Subhead', + description: 'Using custom excerpts as subheads in editor and newsletter', + flag: 'subhead' }]; const AlphaFeatures: React.FC = () => { diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx index fa6b1a38c3..ebb5bf11b4 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx @@ -103,6 +103,7 @@ const Sidebar: React.FC<{ const {mutateAsync: uploadImage} = useUploadImage(); const [selectedTab, setSelectedTab] = useState('generalSettings'); const hasEmailCustomization = useFeatureFlag('emailCustomization'); + const hasSubhead = useFeatureFlag('subhead'); const {localSettings} = useSettingGroup(); const [siteTitle] = getSettingValues(localSettings, ['title']) as string[]; const handleError = useHandleError(); @@ -423,6 +424,14 @@ const Sidebar: React.FC<{ title='Body style' onSelect={option => updateNewsletter({body_font_category: option?.value})} /> + {hasSubhead && + updateNewsletter({show_subhead: e.target.checked})} + /> + } = ({newsletter}) => showFeedback={showFeedback} showLatestPosts={newsletter.show_latest_posts} showPostTitleSection={newsletter.show_post_title_section} + showSubhead={newsletter.show_subhead} showSubscriptionDetails={newsletter.show_subscription_details} siteTitle={title} titleAlignment={newsletter.title_alignment} diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterPreviewContent.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterPreviewContent.tsx index fe7d9f4b01..dbd46cfe59 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterPreviewContent.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterPreviewContent.tsx @@ -18,6 +18,7 @@ const NewsletterPreviewContent: React.FC<{ headerTitle?: string | null; headerSubtitle?: string | null; showPostTitleSection: boolean; + showSubhead: boolean; titleAlignment?: string; titleFontCategory?: string; bodyFontCategory?: string; @@ -49,6 +50,7 @@ const NewsletterPreviewContent: React.FC<{ headerTitle, headerSubtitle, showPostTitleSection, + showSubhead, titleAlignment, titleFontCategory, bodyFontCategory, @@ -75,6 +77,7 @@ const NewsletterPreviewContent: React.FC<{ const showHeader = headerIcon || headerTitle; const {config} = useGlobalData(); const hasNewEmailAddresses = useFeatureFlag('newEmailAddresses'); + const hasSubhead = useFeatureFlag('subhead'); const currentDate = new Date().toLocaleDateString('default', { year: 'numeric', @@ -131,6 +134,9 @@ const NewsletterPreviewContent: React.FC<{ )} style={{color: titleColor}}> Your email newsletter + {(hasSubhead && showSubhead) && ( +

A subhead that highlights the key points of your newsletter.

+ )}
+ + {{#if (feature 'subhead')}} + +
+ {{/if}}
+ {{/unless}} {{#unless this.session.user.isAuthorOrContributor}} diff --git a/ghost/admin/app/controllers/lexical-editor.js b/ghost/admin/app/controllers/lexical-editor.js index 48416986f0..b0240287bb 100644 --- a/ghost/admin/app/controllers/lexical-editor.js +++ b/ghost/admin/app/controllers/lexical-editor.js @@ -284,6 +284,11 @@ export default class LexicalEditorController extends Controller { this.set('post.titleScratch', title); } + @action + updateExcerptScratch(excerpt) { + this.set('post.customExcerptScratch', excerpt); + } + // updates local willPublish/Schedule values, does not get applied to // the post's `status` value until a save is triggered @action diff --git a/ghost/admin/app/models/newsletter.js b/ghost/admin/app/models/newsletter.js index 4c725c0399..3098f5a84c 100644 --- a/ghost/admin/app/models/newsletter.js +++ b/ghost/admin/app/models/newsletter.js @@ -24,6 +24,7 @@ export default class Newsletter extends Model.extend(ValidationEngine) { @attr({defaultValue: true}) showHeaderTitle; @attr({defaultValue: true}) showHeaderName; @attr({defaultValue: true}) showPostTitleSection; + @attr({defaultValue: false}) showSubhead; @attr({defaultValue: true}) showCommentCta; @attr({defaultValue: false}) showSubscriptionDetails; @attr({defaultValue: false}) showLatestPosts; diff --git a/ghost/admin/app/services/feature.js b/ghost/admin/app/services/feature.js index 318a398807..84a8e1834c 100644 --- a/ghost/admin/app/services/feature.js +++ b/ghost/admin/app/services/feature.js @@ -83,6 +83,7 @@ export default class FeatureService extends Service { @feature('onboardingChecklist') onboardingChecklist; @feature('ActivityPub') ActivityPub; @feature('internalLinking') internalLinking; + @feature('subhead') subhead; _user = null; diff --git a/ghost/admin/app/styles/layouts/editor.css b/ghost/admin/app/styles/layouts/editor.css index ee0c1b7427..42302f7edd 100644 --- a/ghost/admin/app/styles/layouts/editor.css +++ b/ghost/admin/app/styles/layouts/editor.css @@ -681,7 +681,7 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone { .gh-editor-feature-image-caption { width: 100%; min-height: 24px; - margin: 0 0 1.7em 0; + margin: 0 0 1.2rem 0; padding: 0; outline: none; border-width: 0; @@ -717,12 +717,25 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone { opacity: .5; } +.gh-editor-title-container { + position: relative; + max-width: 740px; + width: 100%; + margin-right: auto; + margin-left: auto; + border: none; + background: transparent; +} + .gh-editor-title { display: block; width: 100%; + max-width: unset; min-height: auto; - margin-bottom: 1.2rem; + margin: 0 0 1.6rem; + padding: 0 0 4px; border: none; + background: transparent; color: var(--black); font-size: 4.8rem; letter-spacing: -0.017em; @@ -732,6 +745,18 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone { box-shadow: none; } +@media (min-width: 500px) and (max-width: 768px) { + .gh-editor-title { + font-size: 3.6rem; + } +} + +@media (max-width: 500px) { + .gh-editor-title { + font-size: 2.8rem; + } +} + .gh-editor-title:focus { box-shadow: none !important; border: none !important; @@ -741,6 +766,55 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone { opacity: .5; } +.gh-editor-title::placeholder { + color: var(--lightgrey-d1); + font-weight: 700; + opacity: 1; +} + +.gh-editor-hidden-indicator { + position: absolute; + top: -1px; + height: 2.4rem; + margin-left: -6rem; + color: var(--midgrey-l2); +} + +.gh-editor-title-container .gh-editor-hidden-indicator { + top: 1.8rem; +} + +.gh-editor-hidden-indicator svg { + height: 2.4rem; +} + +.gh-editor-subhead { + display: block; + width: 100%; + max-width: unset; + min-width: auto; + margin: 0; + padding: 0; + border: none; + background: transparent; + color: var(--darkgrey); + font-size: 1.9rem; + font-weight: 440; + line-height: 1.4em; + letter-spacing: -.018em; + overflow: hidden; + box-shadow: none; +} + +.gh-editor-subhead:focus { + box-shadow: none !important; + border: none !important; +} + +.gh-editor-title-divider { + margin: 1.6rem 0 4.8rem; +} + .gh-editor .tk-indicator { position: absolute; top: 15px; @@ -927,21 +1001,6 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone { } } -@media (max-width: 500px) { - .gh-editor-title { - font-size: 3.4rem; - } -} - -.gh-editor-title { - padding: 0 0 4px; -} - -.gh-editor-title::placeholder { - color: var(--lightgrey-d1); - font-weight: 700; - opacity: 1; -} .gh-editor .editor-preview { height: auto; margin-top: 4px; @@ -1168,61 +1227,6 @@ figure { /* Labs /* ---------------------------------------------------------- */ -.gh-editor-title-container { - position: relative; - max-width: 740px; - width: 100%; - margin-right: auto; - margin-left: auto; - border: none; - background: transparent; -} - -.gh-editor .gh-editor-title { - display: block; - width: 100%; - max-width: unset; - min-height: auto; - margin: 0 0 1.2rem; - border: none; - background: transparent; - color: var(--black); - font-size: 4.8rem; - letter-spacing: -0.017em; - line-height: 1.1em; - font-weight: 700; - overflow: hidden; - box-shadow: none; -} - -@media (min-width: 500px) and (max-width: 768px) { - .gh-editor .gh-editor-title { - font-size: 3.6rem; - } -} - -@media (max-width: 500px) { - .gh-editor .gh-editor-title { - font-size: 2.8rem; - } -} - -.gh-editor-hidden-indicator { - position: absolute; - top: -1px; - height: 2.4rem; - margin-left: -6rem; - color: var(--midgrey-l2); -} - -.gh-editor-title-container .gh-editor-hidden-indicator { - top: 1.8rem; -} - -.gh-editor-hidden-indicator svg { - height: 2.4rem; -} - .gh-setting-error { margin-top: 1em; line-height: 1.3em; diff --git a/ghost/admin/app/styles/layouts/post-history.css b/ghost/admin/app/styles/layouts/post-history.css index b9a3c1f219..590550a85f 100644 --- a/ghost/admin/app/styles/layouts/post-history.css +++ b/ghost/admin/app/styles/layouts/post-history.css @@ -199,3 +199,7 @@ .gh-post-history-hidden-lexical { display: none; } + +.gh-post-history .gh-editor-feature-image p { + margin: 0 0 1.2rem; +} diff --git a/ghost/admin/app/templates/lexical-editor.hbs b/ghost/admin/app/templates/lexical-editor.hbs index 015bbe9969..8db8681d03 100644 --- a/ghost/admin/app/templates/lexical-editor.hbs +++ b/ghost/admin/app/templates/lexical-editor.hbs @@ -61,10 +61,12 @@ --}}