Skip to content

SEOJing 학습 UX 중간 점검 묶음#31

Merged
seoJing merged 9 commits into
mainfrom
feature/seojing-midpoint-ux-audio-assets
Jun 8, 2026
Merged

SEOJing 학습 UX 중간 점검 묶음#31
seoJing merged 9 commits into
mainfrom
feature/seojing-midpoint-ux-audio-assets

Conversation

@seoJing

@seoJing seoJing commented Jun 8, 2026

Copy link
Copy Markdown
Owner

이번 PR에서 정리한 것

게이트웨이 재시작으로 티켓 연쇄가 끊긴 상태라서, 로컬에 남아 있던 작업을 한 번 PR 단위로 묶었습니다. 큰 방향은 “글을 읽는 방식”을 조금 더 학습 도구답게 만드는 중간 저장입니다.

1. 블로그 오디오 플레이어

TTS artifact manifest가 있는 글에서는 본문 아래에 오디오 플레이어가 뜨도록 붙였습니다.

  • 글별 manifest를 /tts-artifacts/<slug>/manifest.json에서 읽습니다.
  • 2분 요약, 5분 핵심, 섹션별 오디오를 선택할 수 있습니다.
  • 재생 속도와 마지막 위치를 브라우저 localStorage에 저장합니다.
  • 섹션 오디오를 누르면 해당 본문 heading으로 같이 이동합니다.

여기서 중요한 점은 “오디오를 별도 기능”으로 두기보다, 글을 다시 읽는 다른 입구로 붙였다는 점입니다.

2. TTS 재생 이벤트를 analytics에 연결

오디오 플레이어에서 발생하는 이벤트를 기존 article analytics 흐름으로 흘려보내도록 연결했습니다.

  • manifest 로드
  • 오디오 종류 선택
  • 재생/일시정지/종료
  • 속도 변경
  • 재생 위치와 진행률 bucket

단, 원문 질문/답변 같은 민감한 raw text를 보내지 않았던 것처럼, 여기서도 privacy-safe한 요약 필드만 보냅니다. 즉 “무엇을 얼마나 들었는지”는 보되, 사용자의 사적인 입력을 수집하는 방향은 아닙니다.

3. 프레젠테이션 모드 회귀 복구

처음 PR에 넣었던 데스크톱 deck/card형 목차 레이아웃은 기존 프레젠테이션 모드의 핵심 동작을 깨뜨렸습니다. 화면 전체를 쓰지 못하고, 슬라이드 분할 기준과 실제 표시 영역이 어긋났습니다.

그래서 이 PR에서는 그 레이아웃 변경을 되돌리고, 기존처럼 화면 전체를 슬라이드 표시 영역으로 쓰도록 복구했습니다.

  • 가운데 16:9 카드 레이아웃 제거
  • 좌측 목차/필터 UI 제거
  • 기존 full-screen 슬라이드 표시 방식 복구
  • 기존 슬라이싱 계산과 실제 표시 영역을 다시 맞춤
  • 부가적으로 Home/End 키 이동만 유지

프레젠테이션 모드는 발표/훑어보기의 기본기가 먼저라서, 목차형 deck UX는 나중에 별도 설계로 다시 다루는 편이 맞습니다.

4. 콘텐츠 이미지 워크플로우 보강

presentation-scale-stabilization 글에 viewport 단위 비교 SVG를 추가했고, 이미지 삽입 도구도 SVG를 허용하도록 바꿨습니다.

  • 100vh, svh, dvh 차이를 설명하는 다이어그램 추가
  • 콘텐츠 이미지 정책 문서에 다이어그램용 ArticleImage 템플릿 추가
  • insert-content-image.mjs에서 .svg 허용

5. AdSense readiness 중간 점검 문서

AdSense를 바로 붙이는 단계가 아니라, 먼저 무엇이 막히는지 확인한 문서를 추가했습니다.

현재 판정은 간단합니다.

  • 콘텐츠/도메인/색인 기반은 어느 정도 준비됨
  • /privacy, 쿠키 고지, ads.txt, 실제 analytics collect surface는 아직 정리 필요
  • 그래서 광고 코드 삽입보다 privacy route와 저간섭 광고 실험 계획이 먼저입니다

어디까지 왔는지

이번 PR까지 합치면 SEOJing 고도화 흐름은 대략 여기까지 왔습니다.

  • 도메인/SEO/GEO/AEO 기반 정리 완료
  • article analytics와 Q&A/RAG MVP는 PR #30까지 반영 완료
  • TTS artifact와 블로그 오디오 플레이어는 UI까지 연결됨
  • 프레젠테이션 모드는 기존 full-screen/슬라이싱 동작을 유지하는 상태로 복구됨
  • 콘텐츠 이미지 정책과 SVG 삽입 흐름이 생김
  • AdSense는 “바로 신청/삽입”이 아니라 privacy/수집면 정리 후 실험해야 하는 상태로 판단됨

아직 다음 단계로 남은 건 privacy route, 광고 실험 계획, 포트폴리오 케이스 스터디 쪽입니다.

확인한 것

  • pnpm exec vitest run src/widgets/presentation/presentation.utils.test.ts
  • pnpm exec vitest run src/widgets/blog-audio-player/BlogAudioPlayer.test.tsx src/widgets/article-analytics/useArticleAnalytics.test.tsx src/widgets/presentation/presentation.utils.test.ts
  • pnpm run lint (apps/web)
  • pnpm run build (apps/web)
  • pnpm format:check
  • pnpm test:coverage
  • git diff --check
  • 브라우저 확인: local vinext start에서 /blog/okayJing/workflow/smart-reviewer-behavior 프레젠테이션 모드가 fixed full viewport를 덮고, 16:9 카드 축소 레이아웃이 제거된 것을 확인

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 블로그 글에 TTS(텍스트 음성변환) 플레이어 추가 - 재생 속도 조절, 섹션별 선택, 이어듣기 지원
    • 섹션별 질문 기능 추가 - 각 섹션 근처에 "질문하기" 버튼으로 맥락 기반 Q&A 활성화
    • 프레젠테이션 모드에 전체화면 캔버스 보기 추가
  • Documentation

    • 콘텐츠 자산 정책 가이드 업데이트 - 다이어그램 이미지 캡션 작성 기준 추가
    • 모바일 브라우저 UI 관련 설명에 시각적 다이어그램 추가
  • Tests

    • TTS 상호작용 및 오디오 플레이어 테스트 커버리지 추가

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@seoJing, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 6 minutes and 17 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 97f72d29-674b-4581-b11e-0e392112389c

📥 Commits

Reviewing files that changed from the base of the PR and between c62c805 and 7f68dd0.

📒 Files selected for processing (7)
  • apps/web/src/app/blog/[...slug]/page.tsx
  • apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.test.tsx
  • apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.tsx
  • apps/web/src/widgets/post-qa/PostQaPanel.test.tsx
  • apps/web/src/widgets/post-qa/SectionQaPrompts.test.tsx
  • apps/web/src/widgets/post-qa/SectionQaPrompts.tsx
  • apps/web/src/widgets/presentation/PresentationView.tsx

Walkthrough

이 PR은 블로그 기사에 TTS 오디오 플레이어와 섹션별 질문 기능을 추가하고, 프레젠테이션 뷰의 전체화면 캔버스 모드를 구현하며, AdSense 준비도 검토 문서를 포함합니다.

Changes

Blog Audio & Section QA Features

Layer / File(s) Summary
TTS Event Contracts and Analytics Schema
apps/web/src/widgets/article-analytics/useArticleAnalytics.ts, apps/web/src/widgets/post-qa/PostQaPanel.tsx
WindowEventMapseojing:tts-interactionseojing:qa-context 커스텀 이벤트 타입 정의, artifact metadata 및 playback buckets 스키마 추가
BlogAudioPlayer Component with Manifest Loading and State Management
apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.tsx
slug 기반 TTS manifest fetch, localStorage를 통한 재생 속도/위치 복원, 아티팩트 선택 버튼 렌더링, 재생 상태 변경 시 analytics 이벤트 dispatch
TTS Event Handler in useArticleAnalytics
apps/web/src/widgets/article-analytics/useArticleAnalytics.ts
seojing:tts-interaction 이벤트 리스너 추가, safe normalization 함수로 메타데이터 정제, privacy-safe tts_interaction 분석 이벤트 전송
BlogAudioPlayer and useArticleAnalytics Test Suites
apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.test.tsx, apps/web/src/widgets/article-analytics/useArticleAnalytics.test.tsx
manifest 로드 흐름, localStorage 지속성, 이벤트 payload 구조 검증 (privacy-safe 필드, raw 데이터 제외)
SectionQaPrompts Component and PostQaPanel Integration
apps/web/src/widgets/post-qa/SectionQaPrompts.tsx, apps/web/src/widgets/post-qa/PostQaPanel.tsx
h2 섹션 옆에 floating "질문하기" 버튼 렌더링, seojing:qa-context 이벤트 dispatch, PostQaPanel에서 section context 수신 후 질문에 섹션 제목 prefix 추가
Blog Page Layout Integration
apps/web/src/app/blog/[...slug]/page.tsx, apps/web/src/widgets/post-qa/index.ts
BlogAudioPlayerArticleHeader 뒤에 삽입, data-article-content 영역에 MDXContent만 포함, SectionQaPrompts 추가 및 모듈 재내보내기

Presentation View Enhancements

Layer / File(s) Summary
Presentation State and Deck Layout Constants
apps/web/src/widgets/presentation/PresentationView.tsx
isFullscreenCanvas 상태로 캔버스/덱 모드 전환, 덱 레이아웃 상수 (sidebar 너비, padding, card 크기) 및 getPresentationSource 헬퍼 추가
Slide Outline Extraction and Heading Detection
apps/web/src/widgets/presentation/presentation.utils.ts
getPrimaryHeadingElement 추가로 슬라이드 내 주 heading 탐지, extractSlideOutlineFromSlides로 outline 메타데이터 생성 (heading level, element count, code/image flags)
Responsive Slide Measurement and Scale Calculation
apps/web/src/widgets/presentation/PresentationView.tsx
isFullscreenCanvas, isMobile, 덱 레이아웃 치수 기반 scale/height/width 동적 계산, extractSlides 호출에 조정된 값 전달
Navigation and Footer UI
apps/web/src/widgets/presentation/PresentationView.tsx
goToSlide, toggleFullscreenCanvas 콜백 추가, 하단 바에 canvas toggle 버튼, 모드/기기별 responsive footer 및 컨트롤 UI
Deck Layout Rendering
apps/web/src/widgets/presentation/PresentationView.tsx
desktop 비전체화면 시 좌측 sidebar outline + 중앙 main card, mobile/fullscreen 시 중앙 canvas 레이아웃 전환, 네비게이션 overlay positioning 조정
Slide Outline Extraction Test
apps/web/src/widgets/presentation/presentation.utils.test.ts
extractSlideOutlineFromSlides 검증으로 slide DOM → outline metadata 변환 (heading level, element count, asset flags) 확인

Documentation and Content Updates

Layer / File(s) Summary
AdSense Readiness Review
docs/seojing-adsense-readiness-review.md
자격 요건/정책 compliance/privacy 공시/traffic 준비도 평가, /privacy route·footer 링크·analytics 배포 미충족 항목 식별, AdSense 신청 전 implementation checklist (privacy route, ads.txt, #75 광고 실험 준비)
Content Asset Policy
docs/seojing-content-asset-policy.md
다이어그램/설명 이미지용 ArticleImage MDX 템플릿 및 작성 기준 (alt: 연결성 설명, caption: 필요 이유)
Blog Post and Script Updates
apps/web/content/SEOJing/presentation-scale-stabilization.mdx, apps/web/scripts/insert-content-image.mjs
viewport units 비교 섹션에 SVG 다이어그램 삽입, allowedExtensions formatting 개선

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • seoJing/SEOJing#30: /api/rag/query post-QA MVP 구현과 본 PR의 PostQaPanel 섹션 컨텍스트 통합이 밀접하게 연관
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 '학습 UX 중간 점검 묶음'으로, 다양한 변경사항(오디오 플레이어, TTS analytics, 프레젠테이션 레이아웃 복구, 콘텐츠 이미지 워크플로우, AdSense 검토 등)을 포함하는 중간 저장 PR의 특성을 반영하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/seojing-midpoint-ux-audio-assets

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 8, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
seojing 7f68dd0 Commit Preview URL

Branch Preview URL
Jun 08 2026, 06:15 AM

@codecov-commenter

codecov-commenter commented Jun 8, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 78.71622% with 63 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../src/widgets/blog-audio-player/BlogAudioPlayer.tsx 74.73% 26 Missing and 22 partials ⚠️
apps/web/src/widgets/post-qa/SectionQaPrompts.tsx 81.57% 6 Missing and 8 partials ⚠️
...web/src/widgets/presentation/presentation.utils.ts 88.88% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@seoJing

seoJing commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

추가 반영했습니다.

  • 카드 슬라이스/좌측 목차는 데스크톱 기본 모드로 유지했습니다.
  • 하단바에 전체화면 슬라이드 전환 버튼을 추가해 기존처럼 꽉 찬 뷰로 전환할 수 있게 했습니다.
  • 기본 덱 모드는 cloud-dancer(종이색)·흑·백 중심으로 정리하고, 파란 포인트 사용을 줄였습니다.
  • 슬라이싱 기준을 실제 카드 캔버스에 맞추고, 프레젠테이션 대상에서 오디오/Q&A/포스트 목록을 제외해 본문만 슬라이드로 나뉘도록 정리했습니다.

검증:

  • pnpm format:check
  • pnpm lint
  • pnpm test
  • pnpm build
  • 로컬 production 서버에서 /blog/okayJing/workflow/smart-reviewer-behavior 열고 프레젠테이션 모드 브라우저 검증
    • 기본: 좌측 목차 + 우측 16:9 카드
    • 전체화면 전환: sidebar/card 없이 full viewport 캔버스
    • document.body.style.overflow === "hidden"
    • 카드 모드 현재 슬라이드 overflow 없음

원격 체크:

  • Lint / Test / Build / Workers Builds 통과
  • Deploy는 PR 조건상 skipped
  • CodeRabbit check는 pass, 최신 PR 리뷰/댓글에서 추가 actionable follow-up은 확인되지 않았습니다.

@seoJing

seoJing commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

추가 피드백 반영했습니다.

  • TTS/질문 기능은 삭제된 것이 아니라 본문 아래의 일반 글 화면에 그대로 남아 있습니다. 프레젠테이션 슬라이드 대상에서만 제외했습니다. 그래서 발표 모드에서는 글 본문만 슬라이싱되고, 듣기/질문은 프레젠테이션 종료 후 원래 위치에서 사용합니다.
  • 좌측 목차는 더 이상 모든 슬라이드/코드/이미지 필터를 보여주지 않고, H1/H2 주요 제목만 리스트업하도록 정리했습니다.
  • 카드 덱의 실제 본문 슬라이드 수는 보조 정보로만 작게 표시합니다.

검증:

  • pnpm exec vitest run src/widgets/presentation/presentation.utils.test.ts
  • pnpm run lint (@app/web)
  • pnpm run build (repo root)
  • pnpm format:check
  • pnpm test (repo root)
  • 브라우저 검증: 일반 글 화면에 LISTEN/TTS와 질문 영역 존재 확인, 프레젠테이션 목차가 H1/H2 6개만 표시되고 전체/코드/이미지 필터가 사라진 것 확인

원격 체크:

  • Lint / Test / Build / Workers Builds 통과
  • Deploy는 PR 조건상 skipped

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/web/src/widgets/post-qa/SectionQaPrompts.tsx (1)

64-65: 💤 Low value

스크롤 이벤트 리스너가 불필요할 수 있음

프롬프트의 절대 위치는 스크롤 중에 변경되지 않으므로 scroll 이벤트 리스너는 불필요할 수 있습니다. resizeResizeObserver만으로 충분합니다.

♻️ 제안된 수정
    measure();
    window.addEventListener("resize", measure);
-   window.addEventListener("scroll", measure, { passive: true });

    const resizeObserver =
      typeof ResizeObserver === "undefined"
        ? null
        : new ResizeObserver(() => measure());
    resizeObserver?.observe(article);

    return () => {
      window.removeEventListener("resize", measure);
-     window.removeEventListener("scroll", measure);
      resizeObserver?.disconnect();
    };

Also applies to: 74-75

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/widgets/post-qa/SectionQaPrompts.tsx` around lines 64 - 65,
Remove the unnecessary window "scroll" listener in SectionQaPrompts.tsx: the
absolute position of the prompt doesn't change on scroll so keep the resize
handling (window.addEventListener("resize", measure)) and the existing
ResizeObserver logic, and remove any lines that add
window.addEventListener("scroll", measure, { passive: true }) (also update the
matching removal in cleanup if present). Apply this change for both occurrences
referenced (the lines around the first addEventListener and the second pair at
74-75) so only resize/ResizeObserver drive calls to measure.
apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.tsx (1)

161-168: ⚖️ Poor tradeoff

timeupdate 이벤트 핸들러의 과도한 localStorage 쓰기 주의

handleTimeUpdatetimeupdate 이벤트마다 호출되며, 재생 중에는 초당 여러 번 발생할 수 있습니다. 매번 localStorage.setItem을 호출하면 성능 문제가 발생할 수 있습니다.

권장 사항: 쓰기를 throttle하거나 (예: 5초마다) debounce 처리를 추가하세요.

⚡ Throttle 적용 예시
+ const lastSaveRef = useRef(0);
+
  const handleTimeUpdate = useCallback(() => {
    const audio = audioRef.current;
    if (!audio || !selectedArtifact) return;
+   const now = performance.now();
+   if (now - lastSaveRef.current < 5000) return; // 5초마다만 저장
+   lastSaveRef.current = now;
    writeStoredString(
      positionKey(storageBaseKey, selectedArtifact),
      String(Math.floor(audio.currentTime)),
    );
  }, [selectedArtifact, storageBaseKey]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.tsx` around lines 161
- 168, The handleTimeUpdate function is writing to storage on every timeupdate
event (via writeStoredString(positionKey(...))), which can cause performance
issues; modify handleTimeUpdate (and any setup that attaches it to audioRef) to
throttle/debounce storage writes — e.g., track the lastSavedTime (module or
ref-level variable) and only call writeStoredString when
Math.floor(audio.currentTime) differs from lastSavedTime by a threshold (e.g., 5
seconds) or use a throttle utility (lodash.throttle) to limit writes to once
every N seconds; keep references to selectedArtifact, storageBaseKey,
positionKey and writeStoredString unchanged, just gate the calls to
writeStoredString accordingly.
apps/web/src/widgets/presentation/PresentationView.tsx (1)

433-436: ⚡ Quick win

조건부 체크가 중복됩니다.

이미 else 브랜치(덱 모드)에 있으므로 isMobile || !isFullscreenCanvas 조건을 다시 체크할 필요가 없습니다. 항상 undefined를 반환하도록 단순화할 수 있습니다.

♻️ 단순화 제안
                  <div
                    ref={slideContentRef}
                    className="mx-auto w-full max-w-5xl overflow-hidden"
-                    style={
-                      isMobile || !isFullscreenCanvas
-                        ? undefined
-                        : { zoom: pcScale }
-                    }
                  />

덱 모드에서는 zoom을 적용하지 않으므로 style 속성 자체를 제거해도 됩니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/widgets/presentation/PresentationView.tsx` around lines 433 -
436, The JSX in PresentationView redundantly re-checks isMobile ||
!isFullscreenCanvas inside the deck-mode else branch to decide the style/zoom
(pcScale); remove the redundant conditional and always return undefined for that
style (or simply omit the style prop in deck mode) so zoom: pcScale is not
applied in deck mode; update the JSX expression that currently uses "isMobile ||
!isFullscreenCanvas ? undefined : { zoom: pcScale }" to a single undefined (or
remove the prop) when in the deck-mode branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/scripts/insert-content-image.mjs`:
- Around line 13-20: The allowedExtensions Set currently includes ".svg" which
exposes XSS/JS vectors; remove ".svg" from allowedExtensions and require an
explicit opt-in (e.g., a new allowSvg boolean/CLI flag) or run a
sanitizeSvg(svgContent) validation step before accepting SVG uploads; update the
code paths that read allowedExtensions and any upload handler/validation logic
(referencing allowedExtensions and the upload processing function in
insert-content-image.mjs) to reject SVG by default and only accept after the
explicit flag or after sanitizeSvg passes.

---

Nitpick comments:
In `@apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.tsx`:
- Around line 161-168: The handleTimeUpdate function is writing to storage on
every timeupdate event (via writeStoredString(positionKey(...))), which can
cause performance issues; modify handleTimeUpdate (and any setup that attaches
it to audioRef) to throttle/debounce storage writes — e.g., track the
lastSavedTime (module or ref-level variable) and only call writeStoredString
when Math.floor(audio.currentTime) differs from lastSavedTime by a threshold
(e.g., 5 seconds) or use a throttle utility (lodash.throttle) to limit writes to
once every N seconds; keep references to selectedArtifact, storageBaseKey,
positionKey and writeStoredString unchanged, just gate the calls to
writeStoredString accordingly.

In `@apps/web/src/widgets/post-qa/SectionQaPrompts.tsx`:
- Around line 64-65: Remove the unnecessary window "scroll" listener in
SectionQaPrompts.tsx: the absolute position of the prompt doesn't change on
scroll so keep the resize handling (window.addEventListener("resize", measure))
and the existing ResizeObserver logic, and remove any lines that add
window.addEventListener("scroll", measure, { passive: true }) (also update the
matching removal in cleanup if present). Apply this change for both occurrences
referenced (the lines around the first addEventListener and the second pair at
74-75) so only resize/ResizeObserver drive calls to measure.

In `@apps/web/src/widgets/presentation/PresentationView.tsx`:
- Around line 433-436: The JSX in PresentationView redundantly re-checks
isMobile || !isFullscreenCanvas inside the deck-mode else branch to decide the
style/zoom (pcScale); remove the redundant conditional and always return
undefined for that style (or simply omit the style prop in deck mode) so zoom:
pcScale is not applied in deck mode; update the JSX expression that currently
uses "isMobile || !isFullscreenCanvas ? undefined : { zoom: pcScale }" to a
single undefined (or remove the prop) when in the deck-mode branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9df8e326-89c6-45d5-9a10-fd8e8d4b6357

📥 Commits

Reviewing files that changed from the base of the PR and between f74e419 and c62c805.

⛔ Files ignored due to path filters (1)
  • apps/web/public/images/content/seojing/presentation-scale-stabilization/viewport-units-comparison-01.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • apps/web/content/SEOJing/presentation-scale-stabilization.mdx
  • apps/web/scripts/insert-content-image.mjs
  • apps/web/src/app/blog/[...slug]/page.tsx
  • apps/web/src/widgets/article-analytics/useArticleAnalytics.test.tsx
  • apps/web/src/widgets/article-analytics/useArticleAnalytics.ts
  • apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.test.tsx
  • apps/web/src/widgets/blog-audio-player/BlogAudioPlayer.tsx
  • apps/web/src/widgets/post-qa/PostQaPanel.tsx
  • apps/web/src/widgets/post-qa/SectionQaPrompts.tsx
  • apps/web/src/widgets/post-qa/index.ts
  • apps/web/src/widgets/presentation/PresentationView.tsx
  • apps/web/src/widgets/presentation/presentation.utils.test.ts
  • apps/web/src/widgets/presentation/presentation.utils.ts
  • docs/seojing-adsense-readiness-review.md
  • docs/seojing-content-asset-policy.md

Comment thread apps/web/scripts/insert-content-image.mjs Outdated
@seoJing

seoJing commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

TTS / 질문 UX 후속 반영 완료

요청 주신 UX 피드백과 이후 CodeRabbit 후속 리뷰를 반영했습니다.

반영 내용

  • TTS를 글 최상단으로 이동하고, 질문 탭처럼 배경을 덜어낸 스타일로 정리
  • TTS 원래 영역이 화면 밖으로 나가면 오른쪽 아래 fixed 플레이어로 도킹
  • H2 섹션 끝부분마다 글 영역 왼쪽에 이 부분에 대해 질문하기 플로팅 CTA 추가
  • 하단 Q&A는 오케이징에게 물어보기 / 전체 글 질문·피드백 흐름으로 워딩 정리
  • 섹션 질문 시 해당 H2 문맥을 질문에 포함하도록 처리
  • 답변 후 이 내용을 댓글로 달아서 서징에게도 물어볼까요? 작성자가 직접 볼 수 있어요! CTA 추가
  • coverage 보강 테스트 추가
  • CodeRabbit 후속 리뷰 반영
    • SVG asset insert 기본 허용 제거
    • TTS timeupdate localStorage 저장 5초 단위 throttle
    • Section QA scroll listener 제거
    • Presentation deck mode 중복 style 조건 제거

검증

  • pnpm format:check
  • pnpm lint
  • pnpm test
  • pnpm test:coverage
  • pnpm build
  • 원격 PR checks: Lint/Test/Build/Workers/CodeRabbit ✅

최종 head: 72bf9942a032699f6c4793391f1e9e7ea9bf31d6

@seoJing

seoJing commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

섹션 질문 / 오디오 UX 추가 조정 완료

추가 피드백 반영했습니다.

반영 내용

  • 섹션별 질문 CTA를 기본 상태에서는 원형 화살표만 보이게 축소
  • hover 시 버튼이 가로로 확장되며 이 부분에 대해 질문하기 문구 노출
  • CTA 클릭 시 하단 Q&A로 스크롤하지 않고, 해당 위치에 바로 섹션 질문 입력창 표시
  • 입력창에 「섹션 제목」 부분을 기준으로 오케이징에게 물어봐요. 문구를 넣어 이 주제에 대한 질문임을 명확화
  • 섹션 질문은 해당 H2 제목을 prefix로 붙여 /api/rag/query에 전송
  • 상단 오디오 영역을 질문 영역과 같은 bg-white/70 계열 배경으로 변경하고, 여백/패딩을 늘려 본문과 덜 붙게 조정
  • PC breakpoint(xl, 1280px 이상)에서만 섹션 플로팅 질문과 오디오 dock 기능이 동작하도록 반응형 처리
    • 모바일에서는 섹션 플로팅 질문이 렌더링되지 않고, 오디오는 최상단 inline 영역으로만 유지됩니다.

검증

  • 브라우저 확인: 상단 오디오 배경/여백, 원형 섹션 CTA, 클릭 시 inline 입력창, PC 스크롤 시 오디오 fixed dock 확인 ✅
  • pnpm exec vitest run src/widgets/blog-audio-player/BlogAudioPlayer.test.tsx src/widgets/post-qa/SectionQaPrompts.test.tsx src/widgets/post-qa/PostQaPanel.test.tsx
  • pnpm format:check
  • pnpm lint
  • pnpm test
  • pnpm test:coverage
  • pnpm build
  • 원격 PR checks: Lint/Test/Build/Workers/CodeRabbit ✅

Commit: 53414e245f1ed9600b62af14b76defbc04b2e03f

@seoJing

seoJing commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

섹션 질문 hover / 오디오 도킹 위치 재조정

추가 피드백 반영했습니다.

변경

  • 섹션 질문 CTA는 hover 시 문구만 확장됩니다.
  • hover만으로 입력창이 열리지 않도록 분리했습니다.
  • CTA를 클릭해야 해당 위치에 입력창이 열립니다.
  • 입력창 카드 폭을 넓히고 높이를 낮춰, 좁고 두꺼운 느낌을 줄였습니다.
  • 섹션 제목 안내 문구가 잘리지 않도록 line-clamp를 제거했습니다.
  • docked 오디오 플레이어 폭을 26rem → 22rem으로 줄이고 right-6에 고정해, 본문 영역과 겹치거나 붙는 느낌을 줄이고 더 오른쪽으로 보이게 조정했습니다.

확인

  • hover 후 textarea가 생기지 않음 확인 ✅
  • 클릭 후 inline 질문 입력창이 생김 확인 ✅
  • docked 오디오 위치: left 904px / right 1256px / width 352px 확인 ✅
  • pnpm format:check
  • pnpm lint
  • pnpm test
  • pnpm test:coverage
  • pnpm build
  • 원격 PR checks: Lint/Test/Build/Workers/CodeRabbit ✅

Commit: 7f68dd0578a5a9543d8bdbf41caf5a5a4093845f

@seoJing seoJing merged commit 0f003bd into main Jun 8, 2026
6 checks passed
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.

2 participants