diff --git a/apps/admin-x-activitypub/package.json b/apps/admin-x-activitypub/package.json index 22174dfda7..5a4df2df64 100644 --- a/apps/admin-x-activitypub/package.json +++ b/apps/admin-x-activitypub/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/admin-x-activitypub", - "version": "0.3.37", + "version": "0.3.38", "license": "MIT", "repository": { "type": "git", diff --git a/apps/admin-x-activitypub/src/components/articleBodyStyles.ts b/apps/admin-x-activitypub/src/components/articleBodyStyles.ts index bda970052c..1081426e7a 100644 --- a/apps/admin-x-activitypub/src/components/articleBodyStyles.ts +++ b/apps/admin-x-activitypub/src/components/articleBodyStyles.ts @@ -312,14 +312,14 @@ button.gh-form-input { .gh-article-title { font-weight: 700; text-wrap: pretty; - font-size: calc(3rem * var(--content-spacing-factor, 1)); - letter-spacing: -0.021em; - line-height: 1.4; + font-size: calc(3.6rem * var(--content-spacing-factor, 1)); + letter-spacing: -0.025em; + line-height: 1.1; } .gh-article-excerpt { margin-top: 16px; - font-size: calc(2rem * var(--content-spacing-factor, 1)); + font-size: calc(1.8rem * var(--content-spacing-factor, 1)); line-height: 1.4; letter-spacing: -0.017em; text-wrap: pretty; @@ -344,7 +344,7 @@ headings, text, images and lists. We deal with cards lower down. */ .gh-content { font-size: var(--font-size); overflow-x: hidden; - letter-spacing: -0.013em; + letter-spacing: var(--letter-spacing); line-height: var(--line-height); font-family: var(--font-family); } @@ -363,7 +363,7 @@ headings, text, images and lists. We deal with cards lower down. */ /* Add back a top margin to all headings, unless a heading is the very first element in the post content */ .gh-content > [id]:not(:first-child) { - margin-top: calc(56px * var(--content-spacing-factor, 1)); + margin-top: calc(40px * var(--content-spacing-factor, 1)); } /* Add drop cap setting */ diff --git a/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx index 760f1df0a4..988726a99a 100644 --- a/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx +++ b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx @@ -4,10 +4,11 @@ import NiceModal from '@ebay/nice-modal-react'; import React, {useEffect, useRef, useState} from 'react'; import articleBodyStyles from '../articleBodyStyles'; import getUsername from '../../utils/get-username'; +import {OptionProps, SingleValueProps, components} from 'react-select'; import {type Activity} from '../activities/ActivityItem'; import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; -import {Button, LoadingIndicator, Modal, Popover, Select, SelectOption} from '@tryghost/admin-x-design-system'; +import {Button, Icon, LoadingIndicator, Modal, Popover, Select, SelectOption} from '@tryghost/admin-x-design-system'; import {renderTimestamp} from '../../utils/render-timestamp'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; import {useModal} from '@ebay/nice-modal-react'; @@ -24,7 +25,6 @@ interface ArticleModalProps { focusReply: boolean; focusReplies: boolean; width?: 'narrow' | 'wide'; - backDrop?: boolean; updateActivity: (id: string, updated: Partial) => void; history: { activityId: string; @@ -63,6 +63,7 @@ const ArticleBody: React.FC<{heading: string, image: string|undefined, excerpt: --font-size: ${fontSize}; --line-height: ${lineHeight}; --font-family: ${fontFamily.value}; + --letter-spacing: ${fontFamily.label === 'Clean sans-serif' ? '-0.013em' : '0'}; --content-spacing-factor: ${SPACING_FACTORS[FONT_SIZES.indexOf(fontSize)]}; } body { @@ -184,6 +185,7 @@ const ArticleBody: React.FC<{heading: string, image: string|undefined, excerpt: root.style.setProperty('--font-size', fontSize); root.style.setProperty('--line-height', lineHeight); root.style.setProperty('--font-family', fontFamily.value); + root.style.setProperty('--letter-spacing', fontFamily.label === 'Clean sans-serif' ? '-0.013em' : '0'); root.style.setProperty('--content-spacing-factor', SPACING_FACTORS[FONT_SIZES.indexOf(fontSize)]); const iframeWindow = iframe.contentWindow as IframeWindow; @@ -226,28 +228,55 @@ const FeedItemDivider: React.FC = () => (
); -const FONT_SIZES = ['1.4rem', '1.7rem', '1.9rem', '2.1rem', '2.4rem'] as const; -const LINE_HEIGHTS = ['1.3', '1.4', '1.5', '1.6', '1.7', '1.8'] as const; -const SPACING_FACTORS = ['0.7', '1', '1.1', '1.2', '1.3'] as const; +const FONT_SIZES = ['1.5rem', '1.6rem', '1.7rem', '1.8rem', '2rem'] as const; +const LINE_HEIGHTS = ['1.3', '1.4', '1.5', '1.6', '1.7'] as const; +const SPACING_FACTORS = ['0.85', '1', '1.1', '1.2', '1.3'] as const; type FontSize = typeof FONT_SIZES[number]; -// Add constants for localStorage keys const STORAGE_KEYS = { FONT_SIZE: 'ghost-ap-font-size', LINE_HEIGHT: 'ghost-ap-line-height', FONT_FAMILY: 'ghost-ap-font-family' } as const; -// Add this constant near your other constants const MAX_WIDTHS = { - '1.4rem': '544px', - '1.7rem': '644px', - '1.9rem': '684px', - '2.1rem': '724px', - '2.4rem': '764px' + '1.5rem': '544px', + '1.6rem': '644px', + '1.7rem': '684px', + '1.8rem': '724px', + '2rem': '764px' } as const; +const SingleValue: React.FC> = ({children, ...props}) => ( + +
+
+
Aa
+ {children} +
+
+
+); + +const Option: React.FC> = ({children, ...props}) => ( + +
+
+
Aa
+ {children} +
+ {props.isSelected && } +
+
+); + +interface FontSelectOption { + value: string; + label: string; + className?: string; +} + const ArticleModal: React.FC = ({ activityId, object, @@ -255,7 +284,6 @@ const ArticleModal: React.FC = ({ focusReply, focusReplies, width = 'narrow', - backDrop = false, updateActivity = () => {}, history = [] }) => { @@ -367,7 +395,7 @@ const ArticleModal: React.FC = ({ const [currentLineHeightIndex, setCurrentLineHeightIndex] = useState(() => { const saved = localStorage.getItem(STORAGE_KEYS.LINE_HEIGHT); - return saved ? parseInt(saved) : 3; + return saved ? parseInt(saved) : 1; }); const [fontFamily, setFontFamily] = useState(() => { @@ -415,6 +443,7 @@ const ArticleModal: React.FC = ({ iframeDocument.documentElement.style.setProperty('--font-size', FONT_SIZES[currentFontSizeIndex]); iframeDocument.documentElement.style.setProperty('--line-height', LINE_HEIGHTS[currentLineHeightIndex]); iframeDocument.documentElement.style.setProperty('--font-family', fontFamily.value); + iframeDocument.documentElement.style.setProperty('--letter-spacing', fontFamily.label === 'Clean sans-serif' ? '-0.013em' : '0'); iframeDocument.documentElement.style.setProperty('--content-spacing-factor', SPACING_FACTORS[FONT_SIZES.indexOf(FONT_SIZES[currentFontSizeIndex])]); } } @@ -427,7 +456,6 @@ const ArticleModal: React.FC = ({ const [readingProgress, setReadingProgress] = useState(0); - // Add the scroll handler useEffect(() => { const container = document.querySelector('.overflow-y-auto'); const article = document.getElementById('object-content'); @@ -439,14 +467,11 @@ const ArticleModal: React.FC = ({ const articleRect = article.getBoundingClientRect(); const containerRect = container.getBoundingClientRect(); - const totalHeight = article.offsetHeight; - - // Calculate how much of the article has been scrolled past the viewport const scrolledPast = Math.max(0, containerRect.top - articleRect.top); + const totalHeight = (article as HTMLElement).offsetHeight - (container as HTMLElement).offsetHeight; - // Only add the visible portion if we've started scrolling - const visibleHeight = scrolledPast > 0 ? Math.min(containerRect.height, articleRect.height) : 0; - const progress = Math.round(Math.min(Math.max(((scrolledPast + visibleHeight) / totalHeight) * 100, 0), 100)); + const rawProgress = Math.min(Math.max((scrolledPast / totalHeight) * 100, 0), 100); + const progress = Math.round(rawProgress / 5) * 5; setReadingProgress(progress); }; @@ -458,9 +483,9 @@ const ArticleModal: React.FC = ({ return ( } height={'full'} @@ -470,9 +495,9 @@ const ArticleModal: React.FC = ({ width={modalSize === MODAL_SIZE_LG ? 'toSidebar' : modalSize} >
-
+
= ({
)} -
+
{modalSize === MODAL_SIZE_LG && object.type === 'Article' && {}}/> }> -
+