9 Commits

Author SHA1 Message Date
YeonGyu-Kim dc845a8808 chore(web): migrate canonical domain to omo.dev
Switch the production worker route and all hardcoded URLs from
ohmyopenagent.com to omo.dev. Six legacy domains (ohmyopenagent.com,
ohmyopencode.org, ulw.dev, ultrawork.ai, ultrawork.dev,
ultrawork.engineer) are configured to permanently (301) redirect to
omo.dev via Cloudflare Page Rules set up out-of-band via flarectl
and the CF API.

- wrangler.toml: bind worker to omo.dev + www.omo.dev only
- middleware.ts: primaryHost to omo.dev, www to apex redirect retained
- layout.tsx: primarySiteUrl + gaTrackedDomain to omo.dev (GA still G-S0QJFKT46Q)
- sitemap.ts, robots.ts: BASE_URL to https://omo.dev
- npm-downloads/route.ts: usage comment URL refreshed
- web-deploy.yml: environment URL to https://omo.dev
- manifesto.md: domain reality-check line lists omo.dev as canonical
- README*.md (5 langs): npm-downloads badge endpoint to omo.dev
2026-05-25 17:15:16 +09:00
YeonGyu-Kim 6fd5e7c9ec fix(web): switch OG image from next/og to static PNG file convention
PR #4202 introduced dynamic OG via app/opengraph-image.tsx + app/twitter-image.tsx
using next/og ImageResponse. Build succeeds, but deploy to Cloudflare Workers
fails:

  ✘ [ERROR] Unable to extract npm package name from
    .open-next/server-functions/default/node_modules/next/dist/compiled/
    @vercel/og/yoga.wasm?module
  [plugin wrangler-module-collector]
  ##[error]The process '/home/runner/.bun/bin/bunx' failed with exit code 1

Root cause: wrangler-module-collector can't resolve @vercel/og's bundled
yoga.wasm import from the OpenNext server-functions output. Known regression
in @opennextjs/cloudflare interop with @vercel/og (related to issue #1163 +
PRs #1169/#1176 in opennextjs/opennextjs-cloudflare, plus newer bundling
discussion in #1221).

Fix: switch to Next.js file convention static OG images. Place
app/opengraph-image.png + app/twitter-image.png (the same 1200x630
PNG that was being rendered dynamically). Next.js auto-emits og:image +
twitter:image metadata pointing at these routes, with correct
og:image:width/height/type tags. No @vercel/og, no WASM, no edge runtime.

The static PNGs were generated from PR #4202's dynamic ImageResponse during
local QA - identical visual output: dark #0a0a0a background, cyan
#00d4ff brand wordmark, headline, install command pill, terminal cursor.

Build verified: /opengraph-image.png and /twitter-image.png now show as
static routes (0 B route handler size). First-load JS unchanged.

Future-revisit: re-enable dynamic OG when @opennextjs/cloudflare ships the
fixes from PRs #1169/#1176/#1221 stable for Next 15.x deploys.
2026-05-20 15:16:02 +09:00
YeonGyu-Kim e9c44ddb1b fix(web): drop nested <main> on manifesto (WCAG 1.3.1)
LocalizedPageShell at app/_components/localized-page-shell.tsx:34
already renders <main className='flex-1'>{children}</main> as the
page landmark. The manifesto/page.tsx shell was wrapping that with
its own <main>, producing nested main landmarks (axe-core:
landmark-no-duplicate-main + landmark-main-is-top-level).

Switch outer wrapper to <div>; keep styling (bg-background,
text-foreground, min-h-screen, overflow-x-hidden) intact. The
inner <main> from the shell remains the canonical landmark.

Pre-existing on origin/dev (the pre-decomposition manifesto/page.tsx
also had a top-level <main>), surfaced by PR #4202 hands-on QA
via axe-core run on /ko/manifesto.
2026-05-20 15:00:22 +09:00
YeonGyu-Kim 623af75962 feat(web): dynamic OG + Twitter card images via next/og
app/opengraph-image.tsx (new):
- next/og ImageResponse, 1200x630 PNG
- Dark #0a0a0a background + cyan #00d4ff brand wordmark + headline
  + install command pill + terminal cursor
- Google Fonts loadGoogleFont helper for Geist 400/700 +
  Geist Mono 500 (runtime fetch, Cloudflare Workers compatible)
- Cache-Control: public, immutable, max-age=31536000

app/twitter-image.tsx (new):
- Same shape as OG with Twitter-card alt text
- Inherits twitter:card=summary_large_image from layout

app/layout.tsx:
- metadata.alternates.languages: explicit canonical URLs for en/ko/
  ja/zh so search engines pick up hreflang
- metadata.openGraph.alternateLocale: ko_KR, ja_JP, zh_CN
- Drop manual openGraph.images / twitter.images: file-based metadata
  convention now auto-emits og:image:width, og:image:height,
  og:image:type, twitter:image:width, twitter:image:height
- metadata.robots: max-image-preview=large, max-snippet=-1
  (richer SERP previews)

Verified: curl /opengraph-image returns 200, content-type image/png,
1200x630 PNG body. All 4 hreflang links present in <head>.
2026-05-20 14:27:02 +09:00
YeonGyu-Kim 022eb78440 refactor(web): decompose 358-LOC manifesto into 9 section components
app/[locale]/manifesto/page.tsx: 358 LOC -> 22-LOC composition shell.

components/manifesto/sections/:
- hero.tsx
- pain-points.tsx
- indistinguishable.tsx (the 'AI vs senior engineer' argument)
- token-cost.tsx
- cognitive-load.tsx
- principles.tsx
- core-loop.tsx
- future.tsx
- final-cta.tsx

Each section 26-68 LOC. Copy unchanged; section order unchanged.
Same i18n namespace ('manifesto'). Renders identically to baseline
in all 4 locales (en/ko/ja/zh).
2026-05-20 14:27:02 +09:00
YeonGyu-Kim 4d5f58f0c3 refactor(web): decompose 832-LOC landing into 10 section components
app/_components/landing-page.tsx: 832 LOC -> 33-LOC composition shell.
Each section becomes a focused, named component under
components/landing/sections/:

- hero.tsx: terminal headline + install command + primary CTA
- ultrawork.tsx: ultrawork mode pitch
- sisyphus.tsx: Sisyphus orchestrator showcase
- prometheus-atlas.tsx: planner + executor pair (was amber+indigo,
  now violet — single secondary accent)
- hephaestus.tsx: code surgeon (was orange, now cyan)
- team-mode.tsx: parallel team coordination (was fuchsia/purple/cyan
  gradient, now solid cyan headline + violet skills accent)
- sub-agents.tsx: explore/librarian/oracle/multimodal etc.
- architecture.tsx: 4-tier diagram
- reviews.tsx: testimonials (Star kept text-cyan-500 / fill-cyan-500,
  on-brand)
- cta.tsx: final call-to-action

components/landing/constants.ts (new):
- SUB_AGENT_KEYS, AGENT_STYLES, PRINCIPLE_KEYS, PRINCIPLE_ICONS,
  REVIEW_KEYS, CATEGORY_ROUTING, SKILL_INJECTIONS
- Single source of truth for agent/principle/review metadata used
  across sections

components/icons/github-icon.tsx (new):
- Extracted inline SVG into a typed component with default
  aria-hidden and overridable size/className

Color consolidation: 9-color rainbow per-section -> cyan (primary)
+ violet (secondary, agent identity) + neutral grays + status colors
only. 'Evolution not revolution': dark + cyan brand soul intact.
2026-05-20 14:27:02 +09:00
YeonGyu-Kim 78ca837e82 style(web): extract design system tokens + DESIGN.md
DESIGN.md: 7-section design system (palette, typography, spacing,
motion, components, sections, banned patterns). Documents existing
dark + cyan brand to enforce 'evolution not revolution'.

globals.css: add design tokens — --surface-{0..3}, --text-{primary,
secondary,tertiary}, --accent-primary{,-soft,-border}, --border-
{default,subtle}, status tokens (success/warning/danger/info).

Add .reveal-on-enter utility: opacity 0 → 1, translateY(8px) → 0
over 600ms cubic-bezier(0.16, 1, 0.3, 1). Triggered via
'data-revealed' attribute set by IntersectionObserver upstream.
Respects 'prefers-reduced-motion: reduce' — falls back to instant
opacity transition.

Add .ring-cyan focus utility consolidating focus-visible rings
across components (was ad-hoc per-component).
2026-05-20 14:26:09 +09:00
YeonGyu-Kim 7e0406f4ec fix(web): UX/a11y polish + middleware metadata route fix
- footer.tsx: Discord link → canonical discord.gg/PUwSMR9XNk
  (was outdated invite that 404'd).
- [locale]/layout.tsx: hooks count 40 → 54 (matches AGENTS.md).
- lib/stats.ts:88: drop '!' non-null assertion; use '??' fallback.
  Removes the only '!' in packages/web (project rule).
- nav-header.tsx: hamburger Button size='icon' → explicit h-11 w-11;
  mobile drawer links min-h-11 px-3 rounded-md. WCAG 2.5.5 tap
  target 44x44 minimum.
- install-command.tsx: copy button h-8 w-8 → h-11 w-11 (was 32px,
  below WCAG). Icon stays 16x16; padding fills to 44.
- middleware.ts: matcher excludes opengraph-image, twitter-image,
  icon, apple-icon, manifest.webmanifest, robots.txt, sitemap.xml.
  Previously next-intl redirected /opengraph-image → /en/opengraph-image
  breaking OG previews for crawlers that hit the unlocalized path.
2026-05-20 14:26:09 +09:00
YeonGyu-Kim bf96794599 chore(web): move site under packages 2026-05-18 16:44:02 +09:00