# 플러그인 기여 가이드 Voyager의 플러그인 시스템은 선언형 플러그인을 우선합니다. `plugin.json`에는 플러그인 정보와 DOM 작업을 적고, CSS에는 시각적 변경을 적습니다. 플러그인은 원격 JavaScript를 실행하지 않으며, Voyager에 내장된 플러그인 엔진이 manifest와 스타일을 해석합니다. 이 방식은 리뷰와 유지보수를 쉽게 만듭니다. 플러그인을 기여하고 싶다면 여기서 시작하는 것을 권장합니다. ## 권장 흐름 1. 먼저 플러그인에 적합한지 확인하세요. 읽기 폭, 레이아웃 수정, 테마 조정, 페이지 요소 숨김 또는 표시, 간단한 사이트 대응은 보통 좋은 후보입니다. 2. Voyager 메인 저장소에 Issue 또는 PR을 먼저 열어 해결하려는 문제, 대상 웹사이트, 기존 플러그인과의 차이를 설명하세요. 3. `plugin.json`에 메타데이터, 사이트 매칭, 설정, 기여 내용을 작성하세요. 4. 스타일은 같은 디렉터리의 `style.css`에 넣고 `contributes.styles`에서 참조하세요. 5. 로컬에서 테스트한 뒤 PR에 테스트 페이지, 스크린샷 또는 짧은 녹화를 첨부하세요. 유지관리자는 완성도를 보고 공식 catalog 포함 여부를 결정합니다. ## 플러그인 범위 플러그인은 플랫폼별로 기계적으로 나누기보다, 사용자가 해결하려는 문제를 기준으로 나누는 것이 좋습니다. 같은 기능이 여러 플랫폼에서 거의 같은 경험과 설정을 제공한다면 하나의 크로스 플랫폼 플러그인을 권장합니다. 예를 들어 읽기 폭, 페이지 넘김 경험, 코드 블록 레이아웃은 여러 `matches`로 Claude, ChatGPT 등을 함께 커버할 수 있습니다. 반대로 플랫폼마다 설정, DOM 로직, 사용자 문구가 완전히 다르다면 여러 플러그인으로 나누는 편이 명확합니다. "하나로 모든 것을 처리"하기 위해 관련 없는 기능을 억지로 넣지 마세요. 하나의 플러그인은 하나의 분명한 문제를 해결하는 것이 가장 좋습니다. 간단한 기준: - 같은 사용자 목표, 같은 설정, 다른 것은 사이트 선택자뿐: 하나의 플러그인을 우선합니다. - 같은 주제지만 플랫폼별 경험 차이가 큼: 나눌 수 있지만 이름과 설명의 관련성을 유지합니다. - 기능 목표가 다름: 합치지 않습니다. ## 중복 플러그인 피하기 제출 전 플러그인 마켓플레이스와 기존 공식 플러그인을 확인하세요. 이미 좋은 플러그인이 있다면 비슷한 것을 새로 만들기보다 기존 플러그인을 개선하는 PR을 우선하세요. 중복 플러그인은 다음처럼 명확한 개선이 있을 때만 받을 가치가 있습니다. - 기존 플러그인이 지원하지 않는 중요한 플랫폼을 지원합니다. - 기존 플러그인이 오랫동안 해결하지 못한 호환성 문제를 고칩니다. - 성능, 접근성, 유지보수성이 명확히 더 좋습니다. - 이름이나 스타일을 조금 바꾼 수준이 아니라 충분히 다른 사용자 경험을 제공합니다. 이렇게 해야 마켓플레이스가 깔끔해지고 사용자가 선택하기 쉬워집니다. ## 최소 예시 ```json { "id": "your-name.example-plugin", "name": "Example Plugin", "version": "1.0.0", "description": "A short description of what this plugin improves.", "author": "your-name", "category": "readability", "license": "MIT", "engine": ">=1.0.0", "tier": "declarative", "matches": ["https://claude.ai/*"], "contributes": { "styles": [{ "file": "style.css" }], "domOps": [ { "op": "addClass", "target": "body", "className": "gv-plugin-example" } ] } } ``` `style.css`는 일반 CSS처럼 작성할 수 있지만, 플러그인 스타일은 자신의 `gv-plugin-*` 클래스 아래에 두는 것을 권장합니다. ```css .gv-plugin-example .some-target { max-width: 880px; } ``` ## Manifest 주의사항 - `id`는 `your-name.reading-width`처럼 작성자 접두사나 역도메인 스타일을 사용해 충돌을 피하세요. - `matches`는 좁게 유지하고, 플러그인이 실제로 필요한 사이트만 포함하세요. - 여러 플랫폼이 하나의 명확한 기능 목표를 공유한다면 하나의 플러그인에 여러 `matches`를 포함할 수 있습니다. - `category`는 `render-fix`, `theme`, `layout`, `readability`, `productivity`, `integration`, `other`를 권장합니다. - 필요한 플러그인 엔진 버전을 `engine`에 명확히 적으세요. 공식 플러그인을 예시로 참고할 수 있습니다. - `i18n`에는 가능하면 중국어, 영어, 기타 자주 쓰이는 언어의 이름, 설명, 설정 문구를 추가하세요. ## CSS와 리소스 제한 선언형 플러그인은 신뢰할 수 없는 입력으로 검증되므로 리소스를 자체 포함 형태로 유지하세요. - `@import`를 사용하지 마세요. - 원격 이미지, 외부 폰트, 원격 CSS를 참조하지 마세요. - 일반 CSS, 사용자 정의 속성, Voyager가 제공하는 설정값 치환은 사용할 수 있습니다. - 클래스 이름은 `gv-plugin-` 접두사를 사용해 호스트 사이트나 Voyager 자체 스타일을 오염시키지 않도록 하세요. 설정이 필요하다면 먼저 숫자 설정을 사용하는 것을 권장합니다. 예를 들어 읽기 폭 플러그인은 설정값을 CSS 변수로 쓰고 CSS에서 그 값을 사용할 수 있습니다. ## DOM 작업 범위 현재 선언형 플러그인은 다음 작업을 지원합니다. - `addClass`: 대상 요소에 클래스를 추가합니다. - `setAttribute`: 속성을 설정합니다. - `setStyle`: 인라인 스타일 또는 CSS 변수를 설정합니다. - `hide`: 대상 요소를 숨깁니다. 대상은 CSS 선택자이거나 Voyager 사이트 어댑터가 제공하는 의미 선택자일 수 있습니다. 의미 선택자는 보통 더 안정적이지만, 현재 사이트 어댑터가 해당 대상을 제공해야 합니다. 선언형 작업은 되돌릴 수 있고 반복 실행해도 안전해야 합니다. 한 번뿐인 페이지 상태에 의존하지 말고, DOM이 항상 그대로라고 가정하지 마세요. ## 일반 플러그인에 적합하지 않은 경우 JavaScript 실행, 요청 가로채기, Voyager 내부 데이터 읽기/쓰기, 복잡한 런타임 로직이 필요한 기능은 일반 선언형 플러그인에 적합하지 않습니다. 이런 기능은 먼저 Issue를 열어 요구사항을 설명하세요. 내장 기능이 꼭 필요하다면 Formula Copy처럼 Voyager 메인 저장소의 builtin/native 플러그인으로 구현하는 것을 검토할 수 있습니다. ## PR 전 확인 - 플러그인은 기본적으로 꺼져 있고 사용자가 직접 켭니다. - 거의 동일한 기존 플러그인이 없는지 확인했습니다. 있다면 기존 플러그인 개선을 우선했습니다. - 대상 사이트의 라이트/다크 테마에서 테스트했습니다. - `matches`가 관련 없는 사이트를 포함하지 않습니다. - 원격 리소스 참조가 없습니다. - 플러그인 디렉터리에 `plugin.json`, 필요한 CSS 파일, 짧은 README가 있습니다. - PR 설명에 테스트 페이지, 스크린샷 또는 녹화, 영향을 받을 페이지 영역을 적었습니다. 단순하고 절제되며 되돌릴 수 있게 유지하세요. 하나의 분명한 문제를 해결하는 플러그인이 보통 더 쉽게 병합되고 유지보수됩니다.