fix: apply tag removal and source stripping to root-level plugin files
The deploy workflow reads from the repo root (admin/, includes/, public/), not the peptide-news-plugin/ subdirectory. Applied the same changes: - Removed Tag component and tag rendering from ArticleCard - Removed all tag-related CSS (variables, styles, skeleton, responsive) - Added strip_source_suffix() to REST API for cleaning source names from titles/summaries with case-insensitive and domain-based matching Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -146,6 +146,17 @@ class Peptide_News_Rest_API {
|
|||||||
$offset
|
$offset
|
||||||
) );
|
) );
|
||||||
|
|
||||||
|
// Strip source name from titles and summaries (e.g. "Article Title - Toronto Star").
|
||||||
|
foreach ( $articles as $article ) {
|
||||||
|
$article->title = $this->strip_source_suffix( $article->title, $article->source, $article->source_url );
|
||||||
|
if ( ! empty( $article->ai_summary ) ) {
|
||||||
|
$article->ai_summary = $this->strip_source_suffix( $article->ai_summary, $article->source, $article->source_url );
|
||||||
|
}
|
||||||
|
if ( ! empty( $article->excerpt ) ) {
|
||||||
|
$article->excerpt = $this->strip_source_suffix( $article->excerpt, $article->source, $article->source_url );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$total = $wpdb->get_var( "SELECT COUNT(*) FROM {$table} WHERE is_active = 1" );
|
$total = $wpdb->get_var( "SELECT COUNT(*) FROM {$table} WHERE is_active = 1" );
|
||||||
|
|
||||||
$response = rest_ensure_response( array(
|
$response = rest_ensure_response( array(
|
||||||
@@ -163,6 +174,69 @@ class Peptide_News_Rest_API {
|
|||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip the source/publisher name from the end of a string.
|
||||||
|
*
|
||||||
|
* Handles common separator patterns like " - Source", " | Source",
|
||||||
|
* " — Source", and " – Source". Uses case-insensitive matching against
|
||||||
|
* the source field. Also extracts publisher names from source URLs
|
||||||
|
* and has a regex fallback for common RSS title patterns.
|
||||||
|
*
|
||||||
|
* @param string $text The text to clean.
|
||||||
|
* @param string $source The source name to strip.
|
||||||
|
* @param string $source_url The source URL (used to extract domain-based publisher names).
|
||||||
|
* @return string Cleaned text.
|
||||||
|
*/
|
||||||
|
private function strip_source_suffix( $text, $source, $source_url = '' ) {
|
||||||
|
if ( empty( $text ) ) {
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
$separators = array( ' - ', ' | ', ' — ', ' – ', ' // ' );
|
||||||
|
|
||||||
|
// Build list of source names to try matching against.
|
||||||
|
$source_names = array();
|
||||||
|
if ( ! empty( $source ) ) {
|
||||||
|
$source_names[] = $source;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract domain-based publisher name (e.g., "News-Medical" from "news-medical.net").
|
||||||
|
if ( ! empty( $source_url ) ) {
|
||||||
|
$host = wp_parse_url( $source_url, PHP_URL_HOST );
|
||||||
|
if ( $host ) {
|
||||||
|
$host = preg_replace( '/^www\./', '', $host );
|
||||||
|
$parts = explode( '.', $host );
|
||||||
|
if ( count( $parts ) >= 2 ) {
|
||||||
|
$domain_name = $parts[ count( $parts ) - 2 ];
|
||||||
|
$source_names[] = str_replace( '-', ' ', $domain_name );
|
||||||
|
$source_names[] = $domain_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try exact (case-insensitive) match against known source names.
|
||||||
|
foreach ( $source_names as $name ) {
|
||||||
|
foreach ( $separators as $sep ) {
|
||||||
|
$suffix = $sep . $name;
|
||||||
|
$suffix_len = strlen( $suffix );
|
||||||
|
if ( strlen( $text ) > $suffix_len && strcasecmp( substr( $text, -$suffix_len ), $suffix ) === 0 ) {
|
||||||
|
return rtrim( substr( $text, 0, -$suffix_len ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: strip trailing " - Short Publisher Name" patterns (1-5 words).
|
||||||
|
// Only strips if the part after the separator looks like a publisher name
|
||||||
|
// (starts with uppercase, no sentence-ending punctuation).
|
||||||
|
$pattern = '/\s+[-\x{2013}\x{2014}|]\s+[A-Z][A-Za-z0-9\s.\'\-]{0,50}$/u';
|
||||||
|
$candidate = preg_replace( $pattern, '', $text );
|
||||||
|
if ( $candidate !== $text && strlen( $candidate ) > strlen( $text ) * 0.4 ) {
|
||||||
|
return rtrim( $candidate );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /analytics/top
|
* GET /analytics/top
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -35,10 +35,6 @@
|
|||||||
--pn-source-color: #1f2937;
|
--pn-source-color: #1f2937;
|
||||||
--pn-source-bg: #f3f4f6;
|
--pn-source-bg: #f3f4f6;
|
||||||
|
|
||||||
/* Tag Colors */
|
|
||||||
--pn-tag-bg: #eff6ff;
|
|
||||||
--pn-tag-color: #1e40af;
|
|
||||||
|
|
||||||
/* Focus & System */
|
/* Focus & System */
|
||||||
--pn-focus-ring: #2563eb;
|
--pn-focus-ring: #2563eb;
|
||||||
--pn-error: #dc2626;
|
--pn-error: #dc2626;
|
||||||
@@ -71,10 +67,6 @@
|
|||||||
--pn-source-color: #e2e8f0;
|
--pn-source-color: #e2e8f0;
|
||||||
--pn-source-bg: #1e293b;
|
--pn-source-bg: #1e293b;
|
||||||
|
|
||||||
/* Tag Colors */
|
|
||||||
--pn-tag-bg: #1e3a8a;
|
|
||||||
--pn-tag-color: #bfdbfe;
|
|
||||||
|
|
||||||
/* Focus & System */
|
/* Focus & System */
|
||||||
--pn-focus-ring: #60a5fa;
|
--pn-focus-ring: #60a5fa;
|
||||||
--pn-error: #f87171;
|
--pn-error: #f87171;
|
||||||
@@ -258,42 +250,6 @@
|
|||||||
content: '';
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ────────────────────────────────────────────────────────────────
|
|
||||||
TAGS
|
|
||||||
────────────────────────────────────────────────────────────── */
|
|
||||||
|
|
||||||
.pn-tags {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
margin: auto 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pn-tag {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 4px 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.3;
|
|
||||||
letter-spacing: 0.15px;
|
|
||||||
color: var(--pn-tag-color);
|
|
||||||
background: var(--pn-tag-bg);
|
|
||||||
border-radius: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pn-tag:hover {
|
|
||||||
background: color-mix(in srgb, var(--pn-tag-bg) 70%, var(--pn-tag-color) 30%);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pn-tag:focus-visible {
|
|
||||||
outline: 2px solid var(--pn-focus-ring);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════════════
|
||||||
LOADING STATES & SKELETON
|
LOADING STATES & SKELETON
|
||||||
══════════════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════════════ */
|
||||||
@@ -367,19 +323,6 @@
|
|||||||
animation: pn-shimmer 2s infinite 0.4s;
|
animation: pn-shimmer 2s infinite 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pn-skeleton-tags {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pn-skeleton-tag {
|
|
||||||
height: 10px;
|
|
||||||
width: 60px;
|
|
||||||
border-radius: 20px;
|
|
||||||
animation: pn-shimmer 2s infinite 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pn-shimmer {
|
@keyframes pn-shimmer {
|
||||||
0% { background-position: 200% 0; }
|
0% { background-position: 200% 0; }
|
||||||
100% { background-position: -200% 0; }
|
100% { background-position: -200% 0; }
|
||||||
@@ -597,16 +540,6 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pn-tag {
|
|
||||||
padding: 3px 8px;
|
|
||||||
font-size: 11.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pn-tags {
|
|
||||||
gap: 6px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pn-skeleton-card {
|
.pn-skeleton-card {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
@@ -699,11 +632,6 @@
|
|||||||
color: var(--pn-text-tertiary);
|
color: var(--pn-text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pn-tag {
|
|
||||||
background: var(--pn-tag-bg);
|
|
||||||
color: var(--pn-tag-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pn-page-btn {
|
.pn-page-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-color: var(--pn-border-light);
|
border-color: var(--pn-border-light);
|
||||||
@@ -748,8 +676,7 @@
|
|||||||
color: var(--pn-text-tertiary);
|
color: var(--pn-text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pn-skeleton-line,
|
.pn-skeleton-line {
|
||||||
.pn-skeleton-tag {
|
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
90deg,
|
90deg,
|
||||||
var(--pn-loading) 0%,
|
var(--pn-loading) 0%,
|
||||||
@@ -781,8 +708,7 @@
|
|||||||
|
|
||||||
.pn-article-title a:focus-visible,
|
.pn-article-title a:focus-visible,
|
||||||
.pn-page-btn:focus-visible,
|
.pn-page-btn:focus-visible,
|
||||||
.pn-retry-btn:focus-visible,
|
.pn-retry-btn:focus-visible {
|
||||||
.pn-tag:focus-visible {
|
|
||||||
outline-width: 3px;
|
outline-width: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -791,11 +717,9 @@
|
|||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.pn-article,
|
.pn-article,
|
||||||
.pn-article-title a,
|
.pn-article-title a,
|
||||||
.pn-tag,
|
|
||||||
.pn-page-btn,
|
.pn-page-btn,
|
||||||
.pn-retry-btn,
|
.pn-retry-btn,
|
||||||
.pn-skeleton-line,
|
.pn-skeleton-line {
|
||||||
.pn-skeleton-tag {
|
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,17 +148,6 @@ function trackClick(articleId) {
|
|||||||
|
|
||||||
/* ── Components ─────────────────────────────────────────────────────── */
|
/* ── Components ─────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
/**
|
|
||||||
* Single keyword tag pill.
|
|
||||||
*/
|
|
||||||
const Tag = memo(function Tag({
|
|
||||||
label
|
|
||||||
}) {
|
|
||||||
return /*#__PURE__*/React.createElement("span", {
|
|
||||||
className: "pn-tag"
|
|
||||||
}, label);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single article card.
|
* Single article card.
|
||||||
*/
|
*/
|
||||||
@@ -166,8 +155,6 @@ const ArticleCard = memo(function ArticleCard({
|
|||||||
article
|
article
|
||||||
}) {
|
}) {
|
||||||
const summary = decodeEntities(article.ai_summary || article.excerpt || '');
|
const summary = decodeEntities(article.ai_summary || article.excerpt || '');
|
||||||
const tagSource = article.tags || article.categories || '';
|
|
||||||
const tags = tagSource ? tagSource.split(',').map(t => t.trim()).filter(Boolean).slice(0, 5) : [];
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
trackClick(article.id);
|
trackClick(article.id);
|
||||||
}, [article.id]);
|
}, [article.id]);
|
||||||
@@ -196,14 +183,7 @@ const ArticleCard = memo(function ArticleCard({
|
|||||||
className: "pn-article-excerpt"
|
className: "pn-article-excerpt"
|
||||||
}, summary), article.author && /*#__PURE__*/React.createElement("span", {
|
}, summary), article.author && /*#__PURE__*/React.createElement("span", {
|
||||||
className: "pn-author"
|
className: "pn-author"
|
||||||
}, article.author), tags.length > 0 && /*#__PURE__*/React.createElement("div", {
|
}, article.author));
|
||||||
className: "pn-tags",
|
|
||||||
role: "list",
|
|
||||||
"aria-label": "Keywords"
|
|
||||||
}, tags.map(tag => /*#__PURE__*/React.createElement(Tag, {
|
|
||||||
key: tag,
|
|
||||||
label: tag
|
|
||||||
}))));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,15 +209,7 @@ function LoadingSkeleton({
|
|||||||
className: "pn-skeleton-line pn-skeleton-excerpt"
|
className: "pn-skeleton-line pn-skeleton-excerpt"
|
||||||
}), /*#__PURE__*/React.createElement("div", {
|
}), /*#__PURE__*/React.createElement("div", {
|
||||||
className: "pn-skeleton-line pn-skeleton-excerpt-short"
|
className: "pn-skeleton-line pn-skeleton-excerpt-short"
|
||||||
}), /*#__PURE__*/React.createElement("div", {
|
}))));
|
||||||
className: "pn-skeleton-tags"
|
|
||||||
}, /*#__PURE__*/React.createElement("div", {
|
|
||||||
className: "pn-skeleton-tag"
|
|
||||||
}), /*#__PURE__*/React.createElement("div", {
|
|
||||||
className: "pn-skeleton-tag"
|
|
||||||
}), /*#__PURE__*/React.createElement("div", {
|
|
||||||
className: "pn-skeleton-tag"
|
|
||||||
})))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user