Skip to content

feat: 아티클 하이라이트 기능 구현 (커스텀 팔레트, 하이라이트 목록)#66

Merged
gomminjae merged 2 commits intodevelopfrom
feat/highlighting
Feb 24, 2026
Merged

feat: 아티클 하이라이트 기능 구현 (커스텀 팔레트, 하이라이트 목록)#66
gomminjae merged 2 commits intodevelopfrom
feat/highlighting

Conversation

@gomminjae
Copy link
Copy Markdown
Owner

  • 네이티브 iOS 메뉴 대신 JS 커스텀 플로팅 팔레트 구현 (5색 + 밑줄 + 삭제)
  • 기존 하이라이트 탭 시 에디트 메뉴로 색상 변경/삭제 기능
  • 하이라이트 목록 바텀시트 디자인 (nohighlight 에셋, 컬러바, lineTrash)
  • 글자 크기 기본 20pt, 범위 15~25pt, 아웃라인 스타일 버튼
  • 스크롤 최상단 버튼 추가
  • 밑줄 색상 #EF4444, highlight-focused 색상 #2866D3 통일
  • SwiftData 기반 하이라이트 저장/조회/삭제/타입변경

- 네이티브 iOS 메뉴 대신 JS 커스텀 플로팅 팔레트 구현 (5색 + 밑줄 + 삭제)
- 기존 하이라이트 탭 시 에디트 메뉴로 색상 변경/삭제 기능
- 하이라이트 목록 바텀시트 디자인 (nohighlight 에셋, 컬러바, lineTrash)
- 글자 크기 기본 20pt, 범위 15~25pt, 아웃라인 스타일 버튼
- 스크롤 최상단 버튼 추가
- 밑줄 색상 #EF4444, highlight-focused 색상 #2866D3 통일
- SwiftData 기반 하이라이트 저장/조회/삭제/타입변경

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @gomminjae, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 아티클 하이라이트 기능을 대폭 개선하여 사용자 경험을 향상시킵니다. 네이티브 iOS 메뉴 대신 커스텀 JavaScript 기반의 플로팅 팔레트를 도입하여 하이라이트 생성 및 편집을 더욱 유연하게 만들었으며, 하이라이트 목록 UI를 재설계하고 글꼴 크기 조절 및 스크롤 최상단 이동과 같은 편의 기능을 추가했습니다. 또한, SwiftData를 활용하여 하이라이트 데이터를 효율적으로 관리할 수 있도록 백엔드 로직을 강화했습니다.

Highlights

  • 커스텀 하이라이트 팔레트: 네이티브 iOS 메뉴 대신 JavaScript 기반의 커스텀 플로팅 팔레트를 구현하여 5가지 색상, 밑줄, 삭제 기능을 제공합니다.
  • 기존 하이라이트 편집 기능: 이미 적용된 하이라이트를 탭하면 에디트 메뉴가 나타나 색상을 변경하거나 삭제할 수 있습니다.
  • 하이라이트 목록 UI 재설계: 하이라이트 목록 바텀시트의 디자인을 개선했으며, nohighlight 에셋, 컬러바, lineTrash 아이콘을 포함합니다.
  • 글꼴 크기 조절 기능 개선: 글꼴 크기의 기본값을 20pt로 설정하고, 조절 범위를 15pt에서 25pt로 변경했으며, 아웃라인 스타일의 버튼을 적용했습니다.
  • 스크롤 최상단 버튼 추가: 웹뷰 스크롤 시 최상단으로 빠르게 이동할 수 있는 버튼을 추가했습니다.
  • 색상 통일: 밑줄 색상을 #EF4444로, 하이라이트 포커스 색상을 #2866D3로 통일했습니다.
  • SwiftData 기반 하이라이트 관리: SwiftData를 활용하여 하이라이트 저장, 조회, 삭제, 타입 변경 기능을 구현했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Modules/DesignSystem/Derived/Sources/TuistAssets+DesignSystem.swift
    • AppKitUIKit 임포트 구문을 업데이트하고, swiftlint 지시어를 제거했으며, nohighlight 에셋을 추가했습니다.
  • Modules/DesignSystem/Resources/Assets.xcassets/nohighlight.imageset/Contents.json
    • nohighlight 에셋을 추가했습니다.
  • Modules/DesignSystem/Resources/Assets.xcassets/trashbin.imageset/Contents.json
    • trashbin 에셋을 추가했습니다.
  • Modules/Features/Detail/Sources/Article/ArticleDetailView.swift
    • 기본 글꼴 크기를 20pt로 변경했습니다.
    • showScrollToTop 상태 변수를 추가했습니다.
    • 스크롤 최상단 버튼 로직을 통합했습니다.
    • 네이티브 WKWebView 편집 메뉴 커스터마이징을 제거했습니다.
    • FullWebViewonHighlightTypeChangedonHighlightDeleted 콜백을 추가했습니다.
    • FullWebViewshowScrollToTop 바인딩 및 UIScrollViewDelegate를 포함하도록 업데이트했습니다.
    • 글꼴 크기 조절 뷰의 최대 글꼴 크기를 25pt로 변경하고 버튼 스타일을 업데이트했습니다.
    • HighlightListViewNavigationViewList 대신 커스텀 VStackScrollView를 사용하도록 재설계하고, 빈 상태 UI 및 HighlightRowView의 디자인을 변경했습니다.
  • Modules/Features/Detail/Sources/Article/ArticleDetailViewModel.swift
    • 하이라이트 수정 사항을 처리하기 위한 changeHighlightTypedeleteHighlightByText 메서드를 추가했습니다.
  • Modules/Features/Detail/Sources/Article/ArticleHTMLBuilder.swift
    • 초기 JS 실행에 setupHighlightClickHandlers()를 추가했습니다.
    • 하이라이트 색상, 밑줄 스타일을 위한 CSS를 업데이트하고, 커스텀 하이라이트 팔레트(hl-palette, hl-dot, hl-underline, hl-trash, highlight-focused)를 위한 스타일을 추가했습니다.
  • Modules/Features/Detail/Sources/Article/ArticleHighlightJS.swift
    • 편집 메뉴가 활성화된 경우 팔레트 표시를 방지하도록 touchend 이벤트 리스너를 수정했습니다.
    • 커스텀 선택 팔레트를 위한 showSelectionPalettehideSelectionPalette 함수를 도입했습니다.
    • 커스텀 JS 팔레트를 통해 하이라이트 편집 및 삭제를 관리하기 위한 setupHighlightClickHandlers, getHighlightType, showHighlightEditMenu, hideHighlightEditMenu, changeHighlightTypeFromMenu, deleteHighlightFromMenu 함수를 추가했습니다.
    • 삭제 버튼 아이콘을 위한 TRASH_SVG를 정의했습니다.
  • Modules/Features/Detail/Sources/Article/HighlightModel.swift
    • SwiftData 작업을 위한 updateHighlightTypefetchHighlight 메서드를 추가했습니다.
Activity
  • 이 PR에 대한 특정 활동 (댓글, 리뷰 등)은 제공되지 않았습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

이 PR은 아티클 하이라이트 기능을 성공적으로 구현했습니다. 네이티브 메뉴 대신 커스텀 팔레트를 도입하고, 하이라이트 목록 및 SwiftData를 이용한 영속성까지 잘 구현되었습니다. 전반적으로 코드가 잘 구조화되어 있지만, 몇몇 부분에서 매직 넘버 사용과 코드 중복이 발견되었습니다. 이 부분들을 개선하면 코드의 유지보수성이 더욱 향상될 것입니다. 자세한 내용은 아래의 개별 코멘트를 참고해주세요.

Comment on lines +1 to +12
{
"images" : [
{
"filename" : "Icon Button.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

trashbin.imageset 애셋은 코드에서 사용되지 않는 것으로 보입니다. 실제 코드에서는 DesignSystemAsset.lineTrash가 사용되고 있으며, 이는 Line Trash.imageset에 해당합니다. 이 파일은 개발 과정에서 남은 불필요한 파일일 수 있으므로, 프로젝트를 깔끔하게 유지하기 위해 삭제하는 것을 고려해 보세요. 또한 내부의 이미지 파일명인 Icon Button.pdf도 좀 더 설명적인 이름으로 바꾸면 좋을 것 같습니다.

// MARK: - Scroll Tracking

func scrollViewDidScroll(_ scrollView: UIScrollView) {
let shouldShow = scrollView.contentOffset.y > 200
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

scrollViewDidScroll 함수 내에서 스크롤 최상단 버튼 표시 여부를 결정하는 값 200이 매직 넘버로 사용되었습니다. 이 값을 의미를 알 수 있는 상수로 추출하면 가독성과 유지보수성을 높일 수 있습니다. 예를 들어, Coordinator 내부에 private let scrollToTopButtonVisibleOffsetY: CGFloat = 200와 같이 정의할 수 있습니다.

Comment on lines 597 to 606
private var highlightColor: Color {
switch highlight.highlightType {
case "yellow": return Color(red: 1.0, green: 0.96, blue: 0.62)
case "pink": return Color(red: 0.97, green: 0.73, blue: 0.85)
case "green": return Color(red: 0.78, green: 0.90, blue: 0.79)
case "blue": return Color(red: 0.73, green: 0.87, blue: 0.98)
case "underline": return .clear
default: return .clear
case "yellow": return Color(hex: "#FBE96C")
case "orange": return Color(hex: "#FFC194")
case "pink": return Color(hex: "#F1B2C7")
case "green": return Color(hex: "#D7EDA1")
case "blue": return Color(hex: "#95D5EC")
case "underline": return Color(hex: "#EF4444")
default: return Color.gray.opacity(0.4)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

HighlightRowViewhighlightColor 연산 프로퍼티 내에서 하이라이트 색상들이 하드코딩된 hex 값으로 사용되고 있습니다. 이 색상들은 ArticleHTMLBuilder.swift의 CSS에서도 중복으로 사용되고 있어 유지보수를 어렵게 만듭니다. 이 색상들을 디자인 시스템의 일부로 통합하거나, 최소한 한 곳에서 상수로 정의하여 공유하는 것이 좋습니다.

Comment on lines +612 to +614
RoundedRectangle(cornerRadius: highlight.highlightType == "underline" ? 1 : 3)
.fill(highlightColor)
.frame(width: highlight.highlightType == "underline" ? 2 : 6)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

HighlightRowView의 body 내에서 좌측 컬러 바의 cornerRadiuswidth를 설정할 때 1, 3, 2, 6과 같은 매직 넘버가 사용되었습니다. 이 값들을 의미있는 이름의 상수로 정의하면 코드의 가독성을 높일 수 있습니다.

Comment on lines 653 to 658
private func formatDate(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "M월 d일 HH:mm"
formatter.dateFormat = "yyyy-MM-dd HH:mm"
return formatter.string(from: date)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

formatDate 함수가 ArticleDetailViewHighlightRowView 두 곳에 중복으로 구현되어 있습니다. 코드 중복을 피하고 재사용성을 높이기 위해 이 함수를 Date의 extension이나 별도의 유틸리티 파일로 옮기는 것을 추천합니다.

Comment on lines +320 to +321
palette.style.top = (menuTop < 10 ? rect.bottom + 8 : menuTop) + 'px';
palette.style.left = Math.min(Math.max(rect.left + rect.width / 2, 120), window.innerWidth - 120) + 'px';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

showSelectionPalette 함수에서 팔레트의 위치를 계산할 때 46, 120과 같은 매직 넘버가 사용되었습니다. 이 값들은 팔레트의 높이나 여백과 관련이 있을 것으로 보입니다. 스크립트 상단에 상수로 정의해두면 (예: const PALETTE_TOP_OFFSET = 46;) 나중에 스타일을 변경할 때 더 쉽게 관리할 수 있습니다. showHighlightEditMenu 함수에서도 동일한 문제가 발견되었습니다.

- TRASH_SVG, hlTypes 등 긴 줄 분리하여 line_length 해결
- ArticleHighlightJS enum body를 extension으로 분리하여 type_body_length 해결
- HighlightListView/HighlightRowView를 별도 파일로 분리하여 file_length 해결
- identifier_name (h → highlight), attributes 위반 수정
- highlight-underline CSS 줄바꿈 처리

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gomminjae gomminjae merged commit c4db718 into develop Feb 24, 2026
2 checks passed
@gomminjae gomminjae deleted the feat/highlighting branch February 25, 2026 00:13
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