diff --git a/README.md b/README.md index 345598dd..9a22c449 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Notes -[![Version](https://img.shields.io/badge/version-5.31.0-blue.svg)](https://github.com/antoniwan/notes/releases) +[![Version](https://img.shields.io/badge/version-6.0.0-blue.svg)](https://github.com/antoniwan/notes/releases) Personal writing site: essays and notes on fatherhood, masculinity, culture, and day-to-day life. Some posts are in English, some in Spanish, with links between translations where it applies. @@ -24,7 +24,6 @@ Live site: [notes.antoniwan.online](https://notes.antoniwan.online) - **Table of contents on long posts** — floating contents control with section links and a jump to the top - **RSS** (`/rss.xml`) and **JSON Feed** (`/feed.json`) - **Random quotes API** — `GET /api/quotes` (Stoic excerpts, other philosophy, lines from posts; optional `?kind=`) -- **Public API** page at `/api/` — lists endpoints in plain language - **Schema.org JSON-LD** where it fits the page type - **Comments** — optional [Remark42](https://remark42.com/) embed when you set env vars (see `docs/comments-setup.md`) - **Service worker** — registered for caching; the registration URL includes the **package version** from `package.json` so a version bump can nudge browsers to pick up updates diff --git a/astro.config.mjs b/astro.config.mjs index 14ac5023..c0cda91e 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -10,6 +10,35 @@ import { remarkReadingTime } from './remark-reading-time.mjs'; import { buildSeoRedirects, shouldIncludeInSitemap } from './src/utils/seoRouting'; import { getSitemapTranslationLinksByUrl } from './src/utils/sitemapTranslations'; +/** Vite connect middleware: `/path/` → `/path` before Astro trailingSlash 404. */ +function trailingSlashDevRedirectPlugin() { + return { + name: 'trailing-slash-dev-redirect', + configureServer(server) { + const handler = (req, res, next) => { + const raw = req.url ?? '/'; + const qIndex = raw.indexOf('?'); + const pathname = qIndex === -1 ? raw : raw.slice(0, qIndex); + const search = qIndex === -1 ? '' : raw.slice(qIndex); + + if (pathname.length > 1 && pathname.endsWith('/')) { + res.statusCode = 301; + res.setHeader('Location', `${pathname.replace(/\/+$/, '') || '/'}${search}`); + res.end(); + return; + } + + next(); + }; + + // Post hook: run after Vite/Astro install their middlewares, then jump to front. + return () => { + server.middlewares.stack.unshift({ route: '', handle: handler }); + }; + }, + }; +} + // https://astro.build/config export default defineConfig({ site: SITE_URL, @@ -102,7 +131,7 @@ export default defineConfig({ }, // Vite optimizations for better performance vite: { - plugins: [tailwindcss()], + plugins: [trailingSlashDevRedirectPlugin(), tailwindcss()], server: { watch: { // Polling can break HMR on macOS; use native events for hot-reload diff --git a/docs/TECHNICAL-AUDIT.md b/docs/TECHNICAL-AUDIT.md index 76fda1fa..ba091d1b 100644 --- a/docs/TECHNICAL-AUDIT.md +++ b/docs/TECHNICAL-AUDIT.md @@ -1,7 +1,7 @@ # Notes — Technical Audit **Audit date:** 2026-07-28 -**App version:** 5.30.1 (service-worker API cache fix + audit hygiene) +**App version:** 6.0.0 (visual UI overhaul — monochrome + marigold/violet accents) **Production:** [notes.antoniwan.online](https://notes.antoniwan.online) · Vercel project `notes` (`prj_MrjdKV4wL7ubFNGKhVBASHub9rmb`) **Companion product map:** [roadmap.md](./roadmap.md) (§7 product audit, §8 technical roadmap) @@ -48,7 +48,7 @@ A **hybrid Astro site**: almost everything is statically prerendered at build ti ── ReadingProgress / ReadState / Footer / SW │ ┌─────┴──────┬────────────┬─────────────┬──────────────┐ - PageLayout BlogLayout BrainScience* Feeds/API docs + PageLayout BlogLayout BrainScience* Feeds │ │ Content Posts (MD/MDX) ← content.config.ts schema collections publishFilters · translationUtils · tagVocabulary @@ -76,7 +76,7 @@ A **hybrid Astro site**: almost everything is statically prerendered at build ti | Author tools | `/brain-science/*`, `/tag-management` | `noindex` + sitemap-excluded | | Library | `/library`, `/library/books` | Static data in `src/data/library.ts` | | Syndication | `/rss.xml`, `/feed.json`, `@astrojs/sitemap` | Feed eligibility ≠ listing eligibility | -| API | `/api/`, `/api/quotes` | Quotes is SSR; index is static docs | +| API | `/api/quotes` | SSR JSON quotes endpoint | | System | `/404`, `/sitemap.xml` → 301 to sitemap-index | | Redirects live in two places: Astro `buildSeoRedirects()` (`src/utils/seoRouting.ts`) and `vercel.json` (legacy hosts + Remark42 rewrite). Prefer adding post/tag redirects in `seoRouting.ts` going forward. Remark42 upstream: set `REMARK42_UPSTREAM_ORIGIN` and run `pnpm run sync-remark42-rewrite` (see `docs/comments-setup.md`). diff --git a/docs/multilingual-setup.md b/docs/multilingual-setup.md index 43dc243b..534097e0 100644 --- a/docs/multilingual-setup.md +++ b/docs/multilingual-setup.md @@ -76,7 +76,9 @@ The current language is automatically hidden from the toggle. - Discoverable via language toggle when `translationGroup` is set - Excluded from RSS/JSON feeds, `/everything`, category/tag indexes, Guided Path, and search (via `isListingEligiblePost` / `isFeedEligiblePost`) -Spanish-only orphans without an English pair stay indexable with correct `lang="es"` metadata; add a `translationGroup` only when a real pair exists. If a Spanish post should appear in listings/feeds, set `featured: true`. +**Guided Path** always excludes Spanish posts (`language: ["es"]`), even if `featured: true`. Read Spanish via the language toggle on the English note. + +Spanish-only orphans without an English pair stay indexable with correct `lang="es"` metadata; add a `translationGroup` only when a real pair exists. If a Spanish post should appear in category/tag/feed listings, set `featured: true` (Guided Path still omits it). ## Technical Implementation diff --git a/docs/quotes-api.md b/docs/quotes-api.md index dc28f6c4..ad8e939c 100644 --- a/docs/quotes-api.md +++ b/docs/quotes-api.md @@ -94,6 +94,4 @@ if (quote.sourceUrl) { console.log(metadata.countsByKind); ``` -## Human-readable docs - -The `/api/` page on the live site summarizes the same behavior and includes a small tester UI. +See also `src/pages/api/quotes.ts` and `src/data/quotes.ts`. diff --git a/docs/roadmap.md b/docs/roadmap.md index 89a12be8..8f3e5eae 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -22,7 +22,7 @@ This section is a **checkpoint** so the roadmap does not read like the site is o - **Home**: **highlight** masonry for posts marked `featured` or `highlight` in frontmatter. - **Brain Science**: multi-page **stats and charts** (cadence, topics, sentiment, etc.). - **Library**: dedicated **books** pages (see §4). -- **Feeds & APIs**: **RSS**, **JSON Feed**, **GET `/api/quotes`**, human-readable **`/api/`** overview. +- **Feeds & APIs**: **RSS**, **JSON Feed**, **GET `/api/quotes`**. - **Quality & distribution**: **Schema.org** where it fits, optional **Remark42** comments, **service worker** for caching (version bumped on build), **Vercel Web Analytics** and **Speed Insights** in the base layout when those products are enabled on Vercel. - **About**: curated topic grid; optional **Letterboxd “latest watched”** when `LETTERBOXD_*` env vars are set. diff --git a/measure-bs.js b/measure-bs.js new file mode 100644 index 00000000..13ab4364 --- /dev/null +++ b/measure-bs.js @@ -0,0 +1,33 @@ +const { chromium } = require('playwright'); +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await browser.newPage({ viewport: { width: 1280, height: 900 } }); + await page.goto('http://localhost:4321/brain-science/insights', { waitUntil: 'networkidle', timeout: 90000 }); + await page.waitForTimeout(1500); + const info = await page.evaluate(() => { + const p = document.querySelector('#vuln-bar > p'); + if (!p) return { err: 'no p' }; + const cs = getComputedStyle(p); + const rect = p.getBoundingClientRect(); + const parent = p.parentElement; + const pcs = parent ? getComputedStyle(parent) : null; + return { + text: p.textContent.slice(0, 80), + width: rect.width, + height: rect.height, + display: cs.display, + maxWidth: cs.maxWidth, + widthCss: cs.width, + float: cs.float, + writingMode: cs.writingMode, + whiteSpace: cs.whiteSpace, + wordBreak: cs.wordBreak, + parentDisplay: pcs?.display, + parentWidth: parent?.getBoundingClientRect().width, + parentClass: parent?.className, + canvasWidths: [...document.querySelectorAll('canvas')].slice(0,3).map(c => ({id:c.id, w:c.width, cw:c.clientWidth, style:c.getAttribute('style')})), + }; + }); + console.log(JSON.stringify(info, null, 2)); + await browser.close(); +})(); diff --git a/package.json b/package.json index 7dca9a8b..e608193c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "notes blog", "type": "module", - "version": "5.31.0", + "version": "6.0.0", "repository": { "type": "git", "url": "https://github.com/antoniwan/notes.git" @@ -58,6 +58,7 @@ "@typescript-eslint/parser": "^8.59.2", "eslint": "^10.3.0", "eslint-plugin-astro": "^1.7.0", + "playwright": "1.55.0", "prettier": "^3.8.3", "prettier-plugin-astro": "^0.14.1", "prettier-plugin-tailwindcss": "^0.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ded6149..4e85bb03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,6 +86,9 @@ importers: eslint-plugin-astro: specifier: ^1.7.0 version: 1.7.0(eslint@10.8.0(jiti@2.7.0)) + playwright: + specifier: 1.55.0 + version: 1.55.0 prettier: specifier: ^3.8.3 version: 3.9.6 @@ -1914,6 +1917,11 @@ packages: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2640,6 +2648,16 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + playwright-core@1.55.0: + resolution: {integrity: sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.55.0: + resolution: {integrity: sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==} + engines: {node: '>=18'} + hasBin: true + postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} @@ -5277,6 +5295,9 @@ snapshots: dependencies: tiny-inflate: 1.0.3 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -6261,6 +6282,14 @@ snapshots: picomatch@4.0.5: {} + playwright-core@1.55.0: {} + + playwright@1.55.0: + dependencies: + playwright-core: 1.55.0 + optionalDependencies: + fsevents: 2.3.2 + postcss-selector-parser@6.0.10: dependencies: cssesc: 3.0.0 diff --git a/specs/004-ui-ux-refinement/data-model.md b/specs/004-ui-ux-refinement/data-model.md index 27206e75..1ecbf0a8 100644 --- a/specs/004-ui-ux-refinement/data-model.md +++ b/specs/004-ui-ux-refinement/data-model.md @@ -61,5 +61,5 @@ This feature does not add runtime-persisted entities. The model below defines de - `library books` -> `/library/books` (`src/pages/library/books.astro`) - `tag management` -> `/tag-management` (`src/pages/tag-management.astro`) - `brain science landing` -> `/brain-science` (`src/pages/brain-science/index.astro`) -- `api docs` -> `/api` (`src/pages/api/index.astro`) +- `api quotes` -> `/api/quotes` (`src/pages/api/quotes.ts`) - `not found` -> `/404` (`src/pages/404.astro`) diff --git a/specs/004-ui-ux-refinement/quickstart.md b/specs/004-ui-ux-refinement/quickstart.md index b5e0ef63..fd17f09c 100644 --- a/specs/004-ui-ux-refinement/quickstart.md +++ b/specs/004-ui-ux-refinement/quickstart.md @@ -19,7 +19,7 @@ - `/`, `/about`, `/p/[...slug]`, `/everything`, `/category`, `/category/[category]` - `/tag`, `/tag/[tag]`, `/guided-path`, `/library`, `/library/books` -- `/tag-management`, `/brain-science`, `/api`, `/404` +- `/tag-management`, `/brain-science`, `/404` ### Baseline capture notes diff --git a/src/components/BackToTop.astro b/src/components/BackToTop.astro index 2bbbeadb..8bd996ed 100644 --- a/src/components/BackToTop.astro +++ b/src/components/BackToTop.astro @@ -9,7 +9,7 @@ const { class: className = '' } = Astro.props; - - - - -
-

Choose a button to fetch a quote.

-
- - -
-

Code snippets

-
-
-

fetch

-
{`const res = await fetch('/api/quotes');
-const { quote, metadata } = await res.json();
-// quote.kind — 'stoic' | 'philosophical' | 'site'
-// quote.sourceUrl — '/p/slug/' or null`}
-
-
-

Filtered

-
{`const res = await fetch(
-  '/api/quotes?kind=site'
-);`}
-
-
-
- - - - - diff --git a/src/pages/brain-science/cadence.astro b/src/pages/brain-science/cadence.astro index c12e09a3..e9eb6801 100644 --- a/src/pages/brain-science/cadence.astro +++ b/src/pages/brain-science/cadence.astro @@ -162,7 +162,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'cadence'); >
{longestStreak}
@@ -175,14 +175,14 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'cadence');
Current streak
-
+
{sortedDrySpells[0]?.days ?? 0}
Longest gap (days)
avgPostsPerMonth ? 'text-green-600 dark:text-green-400' : recentPosts.length < avgPostsPerMonth * 0.5 ? 'text-red-600 dark:text-red-400' : 'text-[rgb(var(--color-text))]'}`} + class={`text-2xl font-semibold tabular-nums md:text-3xl text-[rgb(var(--color-text))]`} > {recentPosts.length}
@@ -496,8 +496,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'cadence'); { balancedAnalysis.strengths.length > 0 && (
-

- ✅ Strengths +

+ Strengths

{balancedAnalysis.strengths.map((insight) => ( @@ -514,8 +514,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'cadence'); { balancedAnalysis.challenges.length > 0 && (
-

- ❌ Challenges +

+ Challenges

{balancedAnalysis.challenges.map((insight) => ( @@ -533,7 +533,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'cadence'); balancedAnalysis.neutral.length > 0 && (

- ℹ️ Objective Metrics + Objective Metrics

{balancedAnalysis.neutral.map((insight) => ( diff --git a/src/pages/brain-science/evolution.astro b/src/pages/brain-science/evolution.astro index ae8ae8d9..59bff55f 100644 --- a/src/pages/brain-science/evolution.astro +++ b/src/pages/brain-science/evolution.astro @@ -489,14 +489,14 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'evolution');
{growingTopics.length}
Tags rising
-
+
{decliningTopics.length}
Tags falling
@@ -591,12 +591,12 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'evolution');

+{Math.round(topic.growthRate)}% @@ -867,7 +867,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'evolution');
{Math.round(topic.growthRate)}% @@ -1022,9 +1022,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'evolution');

- 🧠 Readability & language heuristics

@@ -1062,9 +1061,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'evolution');

- 📚 Knowledge Area Evolution

@@ -1091,9 +1089,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'evolution');

- 🎓 Maturity Indicators

@@ -1117,9 +1114,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'evolution');

- 📈 Learning Trajectory

diff --git a/src/pages/brain-science/index.astro b/src/pages/brain-science/index.astro index 27bd6221..25294ad7 100644 --- a/src/pages/brain-science/index.astro +++ b/src/pages/brain-science/index.astro @@ -269,19 +269,19 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'index'); > - Positive + Positive {objectiveMetrics.sentimentAnalysis.positive} - Negative + Negative {objectiveMetrics.sentimentAnalysis.negative} - Neutral + Neutral {objectiveMetrics.sentimentAnalysis.neutral} @@ -375,7 +375,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'index');
{challenge.severity} @@ -472,8 +472,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'index'); { maslowAnalysis.map((category) => (
-
- {category.icon} +
{category.title} diff --git a/src/pages/brain-science/insights.astro b/src/pages/brain-science/insights.astro index 5c7798cd..077d8cc7 100644 --- a/src/pages/brain-science/insights.astro +++ b/src/pages/brain-science/insights.astro @@ -349,8 +349,8 @@ const vulnerabilityConfidenceChart = { : emotionalPatterns.avgVulnerabilityScore, isNaN(emotionalPatterns.avgConfidenceScore) ? 0 : emotionalPatterns.avgConfidenceScore, ], - backgroundColor: ['rgba(236, 72, 153, 0.6)', 'rgba(34, 197, 94, 0.6)'], - borderColor: ['rgb(236, 72, 153)', 'rgb(34, 197, 94)'], + backgroundColor: ['rgb(var(--color-accent) / 0.55)', 'rgb(var(--color-primary) / 0.55)'], + borderColor: ['rgb(var(--color-accent))', 'rgb(var(--color-primary))'], }, ], }; @@ -374,8 +374,8 @@ const wordCountEmotionalScatter = { x: post.wordCount, y: post.emotionalIntensity, })), - backgroundColor: 'rgba(59, 130, 246, 0.6)', // Will be overridden by theme colors - borderColor: 'rgb(59, 130, 246)', // Will be overridden by theme colors + backgroundColor: 'rgb(var(--color-accent) / 0.55)', + borderColor: 'rgb(var(--color-accent))', }, ], }; @@ -540,8 +540,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); { label: 'Emotional Intensity', data: emotionalIntensityTimeSeries.map((q) => (isNaN(q.value) ? 0 : q.value)), - borderColor: 'rgb(236, 72, 153)', - backgroundColor: 'rgba(236, 72, 153, 0.1)', + borderColor: 'rgb(var(--color-accent))', + backgroundColor: 'rgb(var(--color-accent) / 0.12)', }, ]} title="Average Emotional Intensity Per Quarter" @@ -604,9 +604,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights');
{index + 1}. - + {discovery.impact}
@@ -623,147 +621,110 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); {/* Emotional Processing Analysis */}

- 📊 Emotional Processing Metrics + Emotional Processing Metrics

-
-
+
+
{emotionalPatterns.avgEmotionalIntensity}
Avg Emotional Intensity
-
- Mean emotional intensity score -
+
Mean emotional intensity score
-
-
+
+
{emotionalPatterns.avgVulnerabilityScore}
Avg Vulnerability Score
-
- Mean vulnerability word count -
+
Mean vulnerability word count
-
-
+
+
{emotionalPatterns.avgConfidenceScore}
Avg Confidence Score
-
- Mean confidence word count -
+
Mean confidence word count
{/* Emotional Analysis Formulas */}
-

- 📊 +

Emotional Analysis Formulas

-
-
-
-
- 💜 - Emotional Intensity Score -
-
-

- Formula: - Exclamation marks + Emotional keywords -

-

- Threshold: >5 = emotionally intense - post -

-

- Keywords: love, hate, fear, joy, - sadness, anger, peace, anxiety, hope, despair, gratitude, frustration -

-
+
+
+
+ Emotional Intensity Score +
+
+

+ Formula: + Exclamation marks + Emotional keywords +

+

+ Threshold: >5 = emotionally intense + post +

+

+ Keywords: love, hate, fear, joy, + sadness, anger, peace, anxiety, hope, despair, gratitude, frustration +

-
-
- 🫂 - Vulnerability Score -
-
-

- Formula: - Struggle words + Challenge words + Pain words -

-

- Interpretation: Higher scores - = more vulnerable content -

-

- Keywords: struggle, challenge, - pain, hurt, broken, difficult, failure, disappointed, defeated, hopeless -

-
+
+
+
Vulnerability Score
+
+

+ Formula: + Struggle words + Challenge words + Pain words +

+

+ Interpretation: Higher scores + = more vulnerable content +

+

+ Keywords: struggle, challenge, + pain, hurt, broken, difficult, failure, disappointed, defeated, hopeless +

-
-
-
- 🌱 - Growth Focus Ratio -
-
-

- Formula: - Growth posts / Total posts × 100 -

-

- Growth Tags: personal-growth, - transformation, healing, self-improvement, learning, consciousness -

-

- Growth Keywords: growth, change, - transform, learn, evolve, improve, heal, discover -

-
+
+
Growth Focus Ratio
+
+

+ Formula: + Growth posts / Total posts × 100 +

+

+ Growth Tags: personal-growth, + transformation, healing, self-improvement, learning, consciousness +

+

+ Growth Keywords: growth, change, + transform, learn, evolve, improve, heal, discover +

-
-
- 🎭 - Voice Evolution Metrics -
-
-

- Word Count Trend: Average words - per period -

-

- Emotional Density: Emotional - words / Total words -

-

- Confidence Indicators: Confident - words frequency over time -

-
+
+
+
Voice Evolution Metrics
+
+

+ Word Count Trend: Average words + per period +

+

+ Emotional Density: Emotional + words / Total words +

+

+ Confidence Indicators: Confident + words frequency over time +

@@ -773,41 +734,31 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); {/* Most Emotionally Intense Posts */}

- 📊 Posts with Highest Emotional Intensity + Posts with Highest Emotional Intensity

-
+
{ emotionalPosts.map((post, index) => ( -
-
-
- {index === 0 ? '💜' : index === 1 ? '💖' : index === 2 ? '💙' : `#${index + 1}`} +
+
+
+ {index + 1}.
-
+
{post.title}
- {format(post.date, 'MMM d, yyyy')} • {post.wordCount} words + {format(post.date, 'MMM d, yyyy')} · {post.wordCount} words
-
+
{post.emotionalIntensity}
-
emotional intensity
+
intensity
)) @@ -818,41 +769,31 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); {/* Most Vulnerable Posts */}

- 📊 Posts with Highest Vulnerability Scores + Posts with Highest Vulnerability Scores

-
+
{ vulnerablePosts.map((post, index) => ( -
-
-
- {index === 0 ? '🫂' : index === 1 ? '💔' : index === 2 ? '🩹' : `#${index + 1}`} +
+
+
+ {index + 1}.
-
+
{post.title}
- {format(post.date, 'MMM d, yyyy')} • {post.wordCount} words + {format(post.date, 'MMM d, yyyy')} · {post.wordCount} words
-
+
{post.vulnerabilityScore}
-
vulnerability score
+
vulnerability
)) @@ -863,47 +804,47 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); {/* Writing Voice Evolution */}

- 📈 Voice Metrics by Period + Voice Metrics by Period

-
+
{ voiceStats.map((period) => ( -
-
+
+
{period.period} Voice
-
-
- Avg Words: +
+
+ Avg Words {period.avgWordCount}
-
- Emotional Intensity: +
+ Emotional Intensity {period.avgEmotionalIntensity}
-
- Vulnerability: +
+ Vulnerability {period.avgVulnerabilityScore}
-
- Confidence: +
+ Confidence {period.avgConfidenceScore} @@ -919,42 +860,24 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); {/* Knowledge Area Evolution */}

- 📊 Knowledge Area Expansion Analysis + Knowledge Area Expansion

-
+
{ knowledgeAreas .sort((a, b) => Math.abs(b.expansionRate) - Math.abs(a.expansionRate)) .slice(0, 10) .map((area) => ( -
-
-
0 - ? 'text-green-500' - : area.expansionRate < 0 - ? 'text-red-500' - : 'text-[rgb(var(--color-accent))]' - }`} - > - {area.expansionRate > 0 ? '📈' : area.expansionRate < 0 ? '📉' : '➡️'} -
-
- {area.icon} -
-
- {area.title} -
-
- {area.earlyPosts} → {area.recentPosts} posts -
-
+
+
+
{area.title}
+
+ {area.earlyPosts} → {area.recentPosts} posts
-
+
0 ? 'text-green-500' : area.expansionRate < 0 ? 'text-red-500' : 'text-body-muted'}`} + class="text-sm font-medium tabular-nums text-[rgb(var(--color-text))]" title={`${area.expansionRate > 0 ? 'Growth' : area.expansionRate < 0 ? 'Decline' : 'Stable'} rate: ${Math.round(area.expansionRate)}% change from early to recent periods`} > {area.expansionRate > 0 ? '+' : ''} @@ -976,121 +899,102 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); {/* Personal Insights Formulas */}
-

- 📊 +

Personal Insights Formulas

-
-
-
-

- 💜 - Emotional Processing Metrics -

-
-

- Emotional Expression Ratio: - Emotional posts / Total posts × 100 -

-

- Intensity Distribution: High/Medium/Low - emotional intensity posts -

-

- Vulnerability Frequency: - Vulnerability words per 1000 words -

-

- Emotional Evolution: Sentiment - trend over time periods -

-
+
+
+

+ Emotional Processing Metrics +

+
+

+ Emotional Expression Ratio: + Emotional posts / Total posts × 100 +

+

+ Intensity Distribution: High/Medium/Low + emotional intensity posts +

+

+ Vulnerability Frequency: + Vulnerability words per 1000 words +

+

+ Emotional Evolution: Sentiment + trend over time periods +

-
-

- 🌱 - Growth Journey Metrics -

-
-

- Growth Focus Percentage: - Growth-oriented posts / Total posts × 100 -

-

- Knowledge Area Trends: Growing/Declining/Stable - topic areas -

-

- Confidence Indicators: Confident - words frequency over time -

-

- Transformation Markers: Breakthrough - moments and insights -

-
+
+
+

+ Growth Journey Metrics +

+
+

+ Growth Focus Percentage: + Growth-oriented posts / Total posts × 100 +

+

+ Knowledge Area Trends: Growing/Declining/Stable + topic areas +

+

+ Confidence Indicators: Confident + words frequency over time +

+

+ Transformation Markers: Breakthrough + moments and insights +

- -
-
-

- 📈 - Content Volume Analysis -

-
-

- Total Word Count: Sum of all words - across all posts -

-

- Average Post Length: Total words - / Total posts -

-

- Content Density: - Words per emotional/growth post -

-

- Writing Momentum: Posts per time - period trends -

-
+
+

+ Content Volume Analysis +

+
+

+ Total Word Count: Sum of all words + across all posts +

+

+ Average Post Length: Total words + / Total posts +

+

+ Content Density: + Words per emotional/growth post +

+

+ Writing Momentum: Posts per time + period trends +

-
-

- 🎯 - Self-Understanding Metrics -

-
-

- Pattern Recognition: Recurring - themes and topics -

-

- Emotional Cycles: High/low emotional - intensity patterns -

-

- Growth Trajectory: Personal development - indicators -

-

- Writing Purpose: Processing vs. - sharing vs. discovery ratios -

-
+
+
+

+ Self-Understanding Metrics +

+
+

+ Pattern Recognition: Recurring + themes and topics +

+

+ Emotional Cycles: High/low emotional + intensity patterns +

+

+ Growth Trajectory: Personal development + indicators +

+

+ Writing Purpose: Processing vs. + sharing vs. discovery ratios +

@@ -1098,10 +1002,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); {/* Balanced Analysis Section */}
-

- ⚖️ - Balanced Analysis -

+

Balanced Analysis

Objective assessment showing strengths, challenges, and neutral metrics without bias toward positive outcomes. @@ -1111,9 +1012,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); { balancedAnalysis.strengths.length > 0 && (

-

- ✅ Strengths -

+

Strengths

{balancedAnalysis.strengths.map((insight) => (
@@ -1129,9 +1028,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); { balancedAnalysis.challenges.length > 0 && (
-

- ❌ Challenges -

+

Challenges

{balancedAnalysis.challenges.map((insight) => (
@@ -1147,9 +1044,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'insights'); { balancedAnalysis.neutral.length > 0 && (
-

- ℹ️ Objective Metrics -

+

Objective Metrics

{balancedAnalysis.neutral.map((insight) => (
diff --git a/src/pages/brain-science/meta.astro b/src/pages/brain-science/meta.astro index 3966d90d..61b83d7e 100644 --- a/src/pages/brain-science/meta.astro +++ b/src/pages/brain-science/meta.astro @@ -335,11 +335,11 @@ const topPhrases = Object.entries(phraseFrequency) {format(analysis.postDate, 'MMM d, yyyy')}
- + {analysis.metaLanguageCount} patterns {analysis.recursiveThinking && ( - + Recursive )} diff --git a/src/pages/brain-science/patterns.astro b/src/pages/brain-science/patterns.astro index d8cd8842..227cadf3 100644 --- a/src/pages/brain-science/patterns.astro +++ b/src/pages/brain-science/patterns.astro @@ -34,13 +34,9 @@ function getGrade(score: number, maxScore: number, minScore: number = 0): string return 'F'; } -// Get grade color for visual feedback -function getGradeColor(grade: string): string { - if (grade.startsWith('A')) return 'text-green-500'; - if (grade.startsWith('B')) return 'text-blue-500'; - if (grade.startsWith('C')) return 'text-yellow-500'; - if (grade.startsWith('D')) return 'text-orange-500'; - return 'text-red-500'; +// Grade label styling — monochrome (no rainbow grade colors) +function getGradeColor(_grade: string): string { + return 'text-[rgb(var(--color-text))]'; } // Get all published posts @@ -623,7 +619,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); complexityDayScatter.datasets[0].data.length > 0 && (

- 📊 Complexity vs Day of Week + Complexity vs Day of Week

Flesch reading ease score distribution by day of week. Higher scores indicate easier @@ -770,7 +766,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); {/* Pattern Analysis */}

- 📊 Statistical Pattern Analysis + Statistical Pattern Analysis

Identified patterns based on quantitative analysis. Confidence levels determined by @@ -779,31 +775,13 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns');

{ patternInsights.map((insight, index) => ( -
+
-
- {index === 0 ? '🥇' : index === 1 ? '🥈' : index === 2 ? '🥉' : `#${index + 1}`} +
+ {index + 1}.
- - {insight.confidence} Confidence + + {insight.confidence} confidence
@@ -819,7 +797,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); {/* Day of Week Metrics */}

- 📊 Day of Week Metrics + Day of Week Metrics

Quantitative metrics by day of week. Productivity = avg_word_count × post_count. Grades @@ -901,7 +879,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); {/* Seasonal Metrics */}

- 📊 Seasonal Metrics + Seasonal Metrics

Quantitative metrics by month of year. Creativity = avg_word_count × avg_complexity. Grades @@ -985,7 +963,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); {/* Creative Cycle Metrics */}

- 📊 Creative Cycle Phase Metrics + Creative Cycle Phase Metrics

Metrics by creative cycle phase. Phases assigned sequentially: cycle_phase = post_index % 4. @@ -1069,7 +1047,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); {/* Tag Correlations */}

- 🔗 Tag Co-occurrence Analysis + Tag Co-occurrence Analysis

Tag pairs that appear together frequently. Average word count calculated for posts @@ -1081,22 +1059,12 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); correlations.tagCombinations.map(([combo, data], index) => { const wordCountGrade = getGrade(data.avgWordCount, maxWordCount); return ( -

-
-
- {index === 0 ? '🔗' : index === 1 ? '⚡' : index === 2 ? '💫' : `#${index + 1}`} +
+
+
+ {index + 1}.
-
+
{combo}
{data.count} posts with this combination @@ -1130,7 +1098,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); {/* High Word Count Posts */}

- 📊 Posts with Highest Word Count + Posts with Highest Word Count

Posts ranked by word count. Pattern classification: Long-form (>1000 words) vs Short-form, @@ -1141,27 +1109,17 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'patterns'); productivePatterns.map((post, index) => { const wordCountGrade = getGrade(post.wordCount, maxWordCount); return ( -

-
-
- {index === 0 ? '🥇' : index === 1 ? '🥈' : index === 2 ? '🥉' : `#${index + 1}`} +
+
+
+ {index + 1}.
-
+
{post.title}
- {format(post.date, 'MMM d, yyyy')} • {post.pattern} + {format(post.date, 'MMM d, yyyy')} · {post.pattern}
diff --git a/src/pages/brain-science/topics.astro b/src/pages/brain-science/topics.astro index 6cf1984d..818a9dcb 100644 --- a/src/pages/brain-science/topics.astro +++ b/src/pages/brain-science/topics.astro @@ -159,8 +159,8 @@ const topicClusters = [ 'mindfulness', ].includes(tag.tag), ), - color: 'text-green-500', - icon: '🌱', + color: 'text-[rgb(var(--color-text))]', + icon: '', }, { name: 'Technical & Creative', @@ -169,8 +169,8 @@ const topicClusters = [ tag.tag, ), ), - color: 'text-blue-500', - icon: '💻', + color: 'text-[rgb(var(--color-text))]', + icon: '', }, { name: 'Life & Relationships', @@ -179,8 +179,8 @@ const topicClusters = [ tag.tag, ), ), - color: 'text-purple-500', - icon: '💜', + color: 'text-[rgb(var(--color-text))]', + icon: '', }, { name: 'Philosophy & Reflection', @@ -195,8 +195,8 @@ const topicClusters = [ 'consciousness', ].includes(tag.tag), ), - color: 'text-yellow-500', - icon: '🤔', + color: 'text-[rgb(var(--color-text))]', + icon: '', }, ].filter((cluster) => cluster.topics.length > 0); @@ -458,9 +458,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics');
{index + 1}. - + {insight.impact}
@@ -518,17 +516,14 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics'); { topicClusters.map((cluster) => (
-
- {cluster.icon} -
+
{cluster.name}
- {cluster.topics.length} topics •{' '} + {cluster.topics.length} topics ·{' '} {cluster.topics.reduce((sum, tag) => sum + tag.count, 0)} writings
-
{cluster.topics.map((tag) => ( @@ -537,7 +532,7 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics');
t.count))) * 100}%`} />
@@ -566,14 +561,11 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics'); { maslowAnalysis.map((category) => (
-
- {category.icon} -
+
{category.title}
{category.description}
-
@@ -623,16 +615,14 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics');

- 📊 Core Themes Formulas

- 🏷️ Topic Analysis

@@ -658,9 +648,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics');

- 📈 Topic Trends

@@ -687,9 +676,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics');

- 🔗 Topic Relationships

@@ -715,9 +703,8 @@ const pageInfo = BRAIN_SCIENCE_PAGES.find((p) => p.id === 'topics');

- 🎯 Life Areas Focus

diff --git a/src/pages/everything.astro b/src/pages/everything.astro index a9a7f65b..228178b2 100644 --- a/src/pages/everything.astro +++ b/src/pages/everything.astro @@ -16,6 +16,7 @@ const sortedPosts = posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDa description="The full record, newest to oldest. Scroll away! 📝" path="/everything" structuredDataType="website" + mode="chrome" > { sortedPosts.length > 0 ? ( diff --git a/src/pages/guided-path.astro b/src/pages/guided-path.astro index 6cd16ec8..01ab24d7 100644 --- a/src/pages/guided-path.astro +++ b/src/pages/guided-path.astro @@ -3,10 +3,10 @@ import PageLayout from '../layouts/PageLayout.astro'; import Chapter from '../components/Chapter.astro'; import BackToTop from '../components/BackToTop.astro'; import { getCollection } from 'astro:content'; -import { isCollectionListed } from '../utils/publishFilters'; +import { isGuidedPathEligiblePost } from '../utils/publishFilters'; -// Get all published posts (excludes secondary-language translations) -const allPosts = await getCollection('blog', ({ data }) => isCollectionListed(data)); +// English-primary path: Spanish posts stay off this list (use language toggle on each note) +const allPosts = await getCollection('blog', ({ data }) => isGuidedPathEligiblePost(data)); // Define seasonal chapter structure interface SeasonalChapter { @@ -117,6 +117,7 @@ const sortedChapters = nonEmptyChapters.sort( title="Guided Path" description="Read writings grouped by season and year. Each chapter is a season (Winter, Spring, Summer, Fall) with posts from that period. Only seasons with content are shown, and your reading progress is stored locally on your device." path="/guided-path" + mode="chrome" > <>
@@ -336,53 +337,55 @@ const sortedChapters = nonEmptyChapters.sort( diff --git a/src/pages/index.astro b/src/pages/index.astro index 2395340d..674990b8 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -9,46 +9,87 @@ const highlightsByCategory = await getHighlightsByCategory(); + + -

- If those highlights resonate, keep going. Start with context, follow a theme, or read everything - in sequence. -

- - -

Why this space exists right now

- -

- This space keeps me accountable to what I claim to value. I use it to document the gap between - what I know and what I actually do, then close that gap in public and in private, even when - nobody is watching. -

- -

- I also write for fun. This is a valve for pressure, a creative output, and in many ways, art. - You will still find experiments and unfinished thoughts here. The difference is the standard: - less performance, more practice. -

+
+

Why this space exists right now

+

+ This space keeps me accountable to what I claim to value. I use it to document the gap between + what I know and what I actually do, then close that gap in public and in private, even when + nobody is watching. +

+

+ I also write for fun. This is a valve for pressure, a creative output, and in many ways, art. + You will still find experiments and unfinished thoughts here. The difference is the standard: + less performance, more practice. +

+
+ + diff --git a/src/pages/library.astro b/src/pages/library.astro index 5f115811..d240e381 100644 --- a/src/pages/library.astro +++ b/src/pages/library.astro @@ -11,10 +11,9 @@ const shelves = Array.from(new Set(books.map((book) => book.shelf))).sort((a, b) title="Library of Sources (Books)" description="A living, honest catalog of the books that shape these notes — what I'm reading now, what has formed my thinking, and what is quietly waiting on the shelf." path="/library" + mode="chrome" >
-

All books

-