Skip to content

deploy: leonard475192#279

Open
leonard475192 wants to merge 73 commits intoCyberAgentHack:mainfrom
leonard475192:main
Open

deploy: leonard475192#279
leonard475192 wants to merge 73 commits intoCyberAgentHack:mainfrom
leonard475192:main

Conversation

@leonard475192
Copy link
Copy Markdown

No description provided.

leonard475192 and others added 30 commits March 20, 2026 11:06
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
perf: enable webpack production mode and optimization
perf: add compression middleware and optimize cache headers
perf: migrate Tailwind CSS from CDN runtime to build-time PostCSS

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: migrate Tailwind CSS from CDN runtime to build-time PostCSS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: replace jQuery, moment.js, lodash, bluebird with native APIs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: keep Buffer in ProvidePlugin (needed by CoveredImage until PR6)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: migrate Tailwind CSS from CDN runtime to build-time PostCSS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: replace jQuery, moment.js, lodash, bluebird with native APIs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: add route-based code splitting with React.lazy and dynamic imports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: add route-based code splitting with React.lazy and dynamic imports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: add CI workflow for lint, typecheck, build, and E2E

Add GitHub Actions CI workflow that runs on PRs and pushes to main.
Jobs run in parallel (lint, typecheck, build) with E2E depending on
build. E2E uses continue-on-error since Linux VRT snapshots are not
yet available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): run corepack enable before setup-node

setup-node's pnpm cache requires pnpm to be on PATH, so corepack
enable must run before setup-node, not after.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve typecheck and formatting errors

Remove unused RequestHandler import in static.ts and apply oxfmt
formatting fixes to pass CI checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): correct build artifact path to application/dist/

Webpack outputs to application/dist/, not application/client/dist/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ci: parallelize E2E tests with sharding and cache Playwright browsers

Split E2E tests into 3 shards running in parallel, cache Playwright
browser binaries across runs, and set E2E_WORKERS=2 to utilize both
vCPUs on ubuntu-latest runners.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore: add pre-commit hook with lint-staged for automated linting

Set up git hooks via .husky/pre-commit to run oxlint and oxfmt on staged
files automatically before each commit using lint-staged.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: migrate Tailwind CSS from CDN runtime to build-time PostCSS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: replace jQuery, moment.js, lodash, bluebird with native APIs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: add route-based code splitting with React.lazy and dynamic imports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: optimize fonts (WOFF2), images (WebP), and simplify CoveredImage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ci: fix VRT snapshots for Linux CI and add snapshot update workflow

- Remove *.png from e2e .gitignore to track VRT snapshots in git
- Change Playwright browser from chromium to chrome to match config
- Add workflow_dispatch workflow for updating Linux snapshots
- Include existing darwin snapshots in git

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace intentionally heavy main-thread patterns with efficient observers:
- InfiniteScroll: remove 2^18 DOM read loop, use single check with rAF throttle and passive listeners
- DirectMessagePage: replace 1ms setInterval+getComputedStyle with MutationObserver
- useHasContentBelow: replace 1ms postTask polling with IntersectionObserver
- AspectRatioBox: replace setTimeout(500)+resize listener with ResizeObserver

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
perf: optimize initial render (remove load listener, react-helmet, add font preload)

- Remove window.addEventListener("load") wrapper for immediate React render
- Replace react-helmet with document.title via useEffect across all containers
- Add font preload links for WOFF2 files in index.html
- Add decoding="async" to CoveredImage img tag

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
perf: reduce bundle size (splitChunks, remove polyfills, native compression)

- Add webpack splitChunks cacheGroups for React vendor chunk + maxSize 200KB
- Remove standardized-audio-context ProvidePlugin (use native AudioContext)
- Replace pako gzip with native CompressionStream API
- Switch react-syntax-highlighter to light build with selective language registration

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
perf: subset fonts (3.8MB→260KB) and dynamic import katex CSS

- Subset ReiNoAreMincho WOFF2 fonts to only include used characters (1255 chars)
- Regular: 3.8MB → 262KB, Heavy: 3.9MB → 260KB
- Move katex CSS import to dynamic import for code-split chunk loading

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
perf: optimize media (native GIF rendering, direct audio URL)

- Replace gifler/omggif canvas-based GIF decoding with native <img> tag
- Use direct audio URL in <audio> element instead of fetchBinary + blob URL
- SoundWaveSVG fetches audio internally on demand instead of receiving ArrayBuffer
- Remove gifler, omggif, @types/omggif dependencies

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
)

- Replace 1ms scheduler.postTask polling in useSearchParams with popstate/pushState interception
- Add React.memo to TimelineItem, PostItem, ChatMessage
- Extract Markdown plugin arrays to module scope in ChatMessage
- Debounce sentiment analysis (300ms) in SearchPage
- Debounce BM25 suggestion search (200ms) in ChatInput
- Scope MutationObserver to message list container in DirectMessagePage

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add indexes to FK columns (Post, Comment, DirectMessage, DirectMessageConversation, User)
- Optimize DirectMessage afterSave hook (skip redundant findByPk, use unscoped)
- Merge dual search queries into single query with Op.or
- Use unscoped() in DM list endpoint to control eager loading

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: add Brotli compression via shrink-ray-current

- Replace compression middleware with shrink-ray-current for Brotli support
- Remove compression and @types/compression dependencies
- Add pnpm.onlyBuiltDependencies for native build deps (iltorb, node-zopfli-es)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add negaposi-analyzer-ja to onlyBuiltDependencies

The negaposi-analyzer-ja package downloads its dictionary file
(pn_ja.dic.json) via a postinstall script. Without listing it in
pnpm.onlyBuiltDependencies, the script is blocked and the build
fails with a missing module error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove node-zopfli-es from onlyBuiltDependencies

node-zopfli-es fails to build with node-gyp on Node 24 (both CI
and local). It is only used for zopfli compression by
shrink-ray-current and is not required for Brotli. Removing it
from the allowlist lets pnpm skip its broken native build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npx was resolving to the latest global version (0.41.0) instead of
the project-pinned version (0.36.0), causing formatting check failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove core-js, image-size (installed but never imported)
- Remove buffer package and Buffer ProvidePlugin from webpack
- Replace encoding-japanese with native TextDecoder in extract_metadata_from_sound

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: add SubmissionError guard for search form validation on React 19

redux-form 8.x relies on UNSAFE_componentWillReceiveProps for initial
syncErrors hydration. React 19's batching changes prevent this from
firing on mount when initialValues are provided, leaving the store
without syncErrors and allowing invalid submissions to bypass validation.

Adding explicit validate + SubmissionError in onSubmit ensures the error
message is displayed even when redux-form's internal sync validation
pipeline is not triggered.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…26)

shrink-ray-current depends on iltorb, a deprecated native Brotli module
that fails to build with node-gyp on Linux (Docker). Replace it with the
compression middleware which uses Node.js built-in zlib and requires no
native compilation.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: make AuthModalContainer static import to prevent sign-in dialog timeout

AuthModalContainer was lazy-loaded with <Suspense fallback={null}>, which meant
the <dialog> element didn't exist in the DOM until the chunk finished loading.
When the sign-in button (using Invoker Commands API: command="show-modal") was
clicked before the chunk loaded, nothing happened — causing E2E test timeouts.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove default loading="lazy" and fetchPriority="low" from CoveredImage
  so above-fold images use browser native heuristics instead of being
  forced lazy
- Correctly identify first image post in Timeline for isAboveFold marking
- Set profile images in viewport to loading="eager"
- Change PostItem (detail page) profile image to loading="eager"
- Parallelize waveform computation in static.ts with Promise.all
- Pre-compute all waveforms at server startup to warm cache and eliminate
  TTFB blocking on first home page request

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: use inline style for dynamic background color in UserProfileHeader

Tailwind CSS cannot detect dynamically generated class names like
`bg-[${averageColor}]` at build time, so the average color extracted
from the profile image was never applied (header stayed white instead
of the expected color). Switch to an inline `style` attribute to
ensure the computed color is rendered correctly.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
perf: defer modal chunk loading to reduce TBT variability

AuthModalContainer and NewPostModalContainer were loaded via React.lazy() + Suspense
on every page, causing their chunk parse/eval to sometimes fall within the FCP→TTI
measurement window and inflate TBT scores unpredictably (0–30 range).

Replace with DeferredModal component that:
- Renders a lightweight placeholder <dialog> immediately (preserving commandfor compat)
- Loads the actual chunk via requestIdleCallback (outside measurement window)
- Falls back to toggle-event-driven loading if user interacts before idle load completes

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove FFmpeg WASM / ImageMagick WASM dead code (unused util files)
- Move negaposi-analyzer-ja sentiment analysis to server API (/api/v1/sentiment)
- Move kuromoji / BM25 suggestion filtering to server API (/api/v1/crok/suggestions?q=)
- Move web-llm translation to server API (/api/v1/translate) using MyMemory
- Remove 17MB kuromoji dict files from public/dicts/
- Clean up webpack config (aliases, cacheGroups, ignoreWarnings)
- Remove 13 client dependencies: @ffmpeg/*, @imagemagick/*, @mlc-ai/web-llm,
  kuromoji, bayesian-bm25, negaposi-analyzer-ja, common-tags, json-repair-js,
  langs, piexifjs and related @types

Client bundle reduced from ~10MB to ~2.78MB.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use 2-phase query for search API to fix pagination with subQuery: false

subQuery: false causes LIMIT to apply after JOIN expansion, so posts
with multiple images consume multiple rows. Split into Phase 1 (ID-only
query without images JOIN) and Phase 2 (full data fetch by IDs).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: sort search results by createdAt DESC to match original behavior

The original search code sorted results by createdAt DESC. The 2-phase
query fix needs to preserve this ordering since defaultScope uses id DESC
(UUIDs don't correlate with creation time).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: fix LCP image loading and pre-compute waveforms

- Remove default loading="lazy" and fetchPriority="low" from CoveredImage
  so above-fold images use browser native heuristics instead of being
  forced lazy
- Correctly identify first image post in Timeline for isAboveFold marking
- Set profile images in viewport to loading="eager"
- Change PostItem (detail page) profile image to loading="eager"
- Parallelize waveform computation in static.ts with Promise.all
- Pre-compute all waveforms at server startup to warm cache and eliminate
  TTFB blocking on first home page request

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: add server-side image resizing with disk cache

Add sharp-based on-the-fly image resize middleware that intercepts
image requests with ?w= query parameter, resizes to the requested
width, and caches results to /tmp/image-cache/. All image components
now request appropriately sized images (686px for post images,
80-256px for profile images) instead of full-resolution originals.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use nearLossless WebP and skip resize for color-extracted profile image

- UserProfileHeader uses FastAverageColor on the profile image, so resizing
  changes the extracted color and breaks VRT. Use original image here.
- Switch WebP encoding to nearLossless for better VRT compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore: improve CLAUDE.md with missing API routes and dev notes

- Add complete API route list (including sentiment, translate)
- Add GITHUB_TOKEN note to Git & PRs section
- Add parallel development workflow section for worktree-based agents

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: replace redux-form with react-hook-form and remove Redux

Migrate all 3 forms (auth, search, DM) from redux-form HOC to
react-hook-form hooks. Remove redux, react-redux, redux-form and
their type definitions. This eliminates ~55KB gzip from the initial
bundle (redux-form + lodash transitive dependency + redux + react-redux).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use mode onChange and show errors after submit attempt

- Change react-hook-form mode from onBlur to onChange so isValid
  updates on every keystroke (matching redux-form's invalid behavior)
- Show validation errors after submit attempt (isSubmitted) to match
  redux-form marking all fields as touched on submit
- Remove redundant manual validation in SearchPage onSubmit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redux removal, Webpack optimization enablement, and heavy dependency
cleanup were already done in prior PRs but CLAUDE.md still described
the initial unoptimized state.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
)

perf: lazy-load Markdown renderer and use startTransition for Crok chat

Reduce TBT/INP on Crok AI chat by:
- Wrapping onDone state updates in startTransition so React can split
  the heavy Markdown rendering into interruptible chunks
- Extracting MarkdownRenderer (react-markdown + KaTeX + GFM) into a
  lazy-loaded component with Suspense fallback showing plain text

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update CLAUDE.md to reflect current codebase state

Redux removal, Webpack optimization enablement, and heavy dependency
cleanup were already done in prior PRs but CLAUDE.md still described
the initial unoptimized state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: cache home HTML response and inject initial data for all routes

Home page HTML is now cached after first build (with dedup for concurrent
requests). Non-home routes (/posts/:id, /users/:username, /search) get
server-injected __INITIAL_DATA__ to eliminate the JS→API waterfall.
Cache is invalidated on POST /posts and rebuilt on POST /initialize.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: use raw SQL subquery for correct per-conversation latest message in DM list

Sequelize 6's `separate: true` + `limit: 1` applies LIMIT globally
instead of per-parent, causing each conversation to show an incorrect
"latest" message. Replace with a raw SQL subquery that correctly gets
MAX(createdAt) per conversation, fixing DM list ordering and preview.

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant