122 lines
7.5 KiB
TypeScript
122 lines
7.5 KiB
TypeScript
import React, { useEffect, useRef } from 'react';
|
||
|
||
export type SyncHelpAnchor = 'main-conflict' | 'auto' | 'silent' | 'setup';
|
||
|
||
interface Props {
|
||
onClose: () => void;
|
||
initialAnchor?: SyncHelpAnchor;
|
||
}
|
||
|
||
const overlayStyle: React.CSSProperties = {
|
||
position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh',
|
||
background: 'rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center',
|
||
justifyContent: 'center', zIndex: 110
|
||
};
|
||
|
||
const modalStyle: React.CSSProperties = {
|
||
background: '#fff', borderRadius: 8, padding: 20, width: 640,
|
||
maxHeight: '80vh', overflow: 'auto', boxShadow: '0 4px 16px rgba(0,0,0,0.2)'
|
||
};
|
||
|
||
const sectionStyle: React.CSSProperties = {
|
||
marginTop: 18, paddingTop: 12, borderTop: '1px solid #eee'
|
||
};
|
||
|
||
const h4Style: React.CSSProperties = { fontSize: 14, margin: '0 0 8px 0' };
|
||
const pStyle: React.CSSProperties = { fontSize: 12, color: '#444', lineHeight: 1.6, margin: '4px 0' };
|
||
const liStyle: React.CSSProperties = { fontSize: 12, color: '#444', lineHeight: 1.6, marginBottom: 4 };
|
||
const codeStyle: React.CSSProperties = { background: '#f4f4f4', padding: '1px 4px', borderRadius: 3, fontSize: 11 };
|
||
|
||
export function SyncHelpModal({ onClose, initialAnchor }: Props): React.ReactElement {
|
||
const bodyRef = useRef<HTMLDivElement>(null);
|
||
|
||
useEffect(() => {
|
||
if (!initialAnchor) return;
|
||
const el = bodyRef.current?.querySelector(`#${initialAnchor}`);
|
||
if (el) el.scrollIntoView({ behavior: 'auto', block: 'start' });
|
||
}, [initialAnchor]);
|
||
|
||
return (
|
||
<div style={overlayStyle} onClick={onClose}>
|
||
<div ref={bodyRef} style={modalStyle} onClick={(e) => e.stopPropagation()}>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||
<h3 style={{ margin: 0, fontSize: 16 }}>동기화 도움말</h3>
|
||
<button onClick={onClose} aria-label="닫기" style={{ background: 'none', border: 'none', fontSize: 18, cursor: 'pointer', color: '#888' }}>×</button>
|
||
</div>
|
||
|
||
<section id="main-conflict" style={sectionStyle}>
|
||
<h4 style={h4Style}>1. 충돌 해결 (메인 시나리오)</h4>
|
||
<p style={pStyle}>같은 노트를 두 기기에서 동시에 수정하면 충돌이 발생한다. "충돌 해결…" 버튼이 활성화되면 ConflictModal 이 열려 path 별 결정 (내 것 사용 / 원격 사용) 을 받는다.</p>
|
||
|
||
<p style={{ ...pStyle, marginTop: 10, fontWeight: 600 }}>편집/편집 — 가장 흔한 경우</p>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}>두 기기에서 같은 노트 본문 수정 → 양 텍스트가 ConflictModal 에 좌우로 나란히 표시</li>
|
||
<li style={liStyle}>결정 트리: 어느 쪽 변경이 더 새롭고 완전한지 비교 → 더 나은 쪽 선택</li>
|
||
<li style={liStyle}>둘 다 보존하려면? 현재 'both' 미지원 — 한쪽 선택 후 사후 수동 병합 (다른 쪽 텍스트 메모 → 모달 닫고 노트 편집)</li>
|
||
</ul>
|
||
|
||
<p style={{ ...pStyle, marginTop: 10, fontWeight: 600 }}>삭제/편집</p>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}>한쪽에서 trash 처리, 다른 쪽에서 같은 노트 본문 수정</li>
|
||
<li style={liStyle}>"삭제가 의도였다" → 원격 사용 (trash 측 적용)</li>
|
||
<li style={liStyle}>"수정이 더 중요" → 내 것 사용 (편집 측 적용 = trash 취소)</li>
|
||
</ul>
|
||
|
||
<p style={{ ...pStyle, marginTop: 10, fontWeight: 600 }}>AI 결과 충돌</p>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}>양 기기에서 AI 자동 처리 결과 (태그 / 주제 / 요약) 가 다름</li>
|
||
<li style={liStyle}>대부분 어느 쪽이든 무관 → 한쪽 선택 후 AI 재실행 권장 (가장 최신 모델 결과로 통일)</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section id="auto" style={sectionStyle}>
|
||
<h4 style={h4Style}>2. 자동 처리 (내가 안 해도 되는 일)</h4>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}><b>fetch + rebase</b>: sync 시작 시 원격 변경을 가져와 내 변경 위에 다시 쌓음 (linear history). conflict 없으면 자동 진행</li>
|
||
<li style={liStyle}><b>첫 sync 순서</b>: 빈 원격에는 어느 기기든 먼저 push 가능. 두 번째 기기는 fetch 후 자동 rebase</li>
|
||
<li style={liStyle}><b>push 거부 (non-fast-forward)</b>: 다른 기기가 먼저 push 했어도 자동 fetch + rebase + 재시도. 사용자 개입은 rebase conflict 발생 시에만</li>
|
||
<li style={liStyle}><b>자동 sync 주기</b>: 기본 30분 (설정에서 변경). 앱 종료 시 자동 1회 추가</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section id="silent" style={sectionStyle}>
|
||
<h4 style={h4Style}>3. 조용히 잘못될 수 있는 케이스 (silent risk)</h4>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}><b>시계 어긋남 (NTP)</b>: 양 기기 시계가 다르면 timestamp 기반 merge 가 잘못된 결과를 낼 수 있음. macOS / Windows 모두 기본 NTP 동기화 켜져 있음 — 수동으로 끄지 말 것</li>
|
||
<li style={liStyle}><b>두 기기 동시 수정 회피</b>: 같은 노트를 동시에 수정하면 conflict 가 더 자주 발생. 한 기기에서 작업 중이면 다른 기기에서 같은 노트 수정 자제</li>
|
||
<li style={liStyle}><b>자동 sync 실패 silent</b>: 주기적 sync 실패 시 토스트 안 뜸. 마지막 sync 시각 / 결과는 설정 페이지에서 확인 — 주 1회 점검 권장</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section id="setup" style={sectionStyle}>
|
||
<h4 style={h4Style}>4. Setup / 인증 (troubleshoot)</h4>
|
||
<p style={{ ...pStyle, fontWeight: 600 }}>URL 형식 (둘 중 하나)</p>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}>SSH: <code style={codeStyle}>git@host:user/repo.git</code></li>
|
||
<li style={liStyle}>HTTPS: <code style={codeStyle}>https://host/user/repo.git</code></li>
|
||
<li style={liStyle}>잘못된 형식: <code style={codeStyle}>git@https://...</code> 같은 혼합 형식 ✗</li>
|
||
</ul>
|
||
|
||
<p style={{ ...pStyle, fontWeight: 600, marginTop: 10 }}>인증</p>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}>SSH: 기기에 SSH key 등록 + 원격에 public key 추가</li>
|
||
<li style={liStyle}>HTTPS: OS credential helper (Windows Credential Manager / macOS Keychain) 가 첫 push 시 token 입력받아 저장. 매 push 마다 재입력 X</li>
|
||
</ul>
|
||
|
||
<p style={{ ...pStyle, fontWeight: 600, marginTop: 10 }}>"연결 테스트" 실패 시</p>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}>네트워크: 원격 host 에 브라우저로 접속해 응답 확인</li>
|
||
<li style={liStyle}>인증: 위 인증 절차 점검</li>
|
||
<li style={liStyle}>URL: 형식 (SSH/HTTPS) + 오타 점검</li>
|
||
</ul>
|
||
|
||
<p style={{ ...pStyle, fontWeight: 600, marginTop: 10 }}>재설정</p>
|
||
<ul style={{ paddingLeft: 18, margin: '4px 0' }}>
|
||
<li style={liStyle}>URL 변경 시 설정 → 동기화 저장소에서 새 URL 입력 → 저장. 내부적으로 <code style={codeStyle}>git remote set-url origin</code> 자동 처리</li>
|
||
</ul>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|