generated from ttipo/project-base
docs: 14단계 검색 화면의 주소 규칙과 구현하며 정한 보완을 적는다
- 계약 2절 "검색 — 14단계"에 클라이언트 줄: 화면 주소(조건 이름 · since · until · attachment), 손으로 고친 주소의 검사, 읽기 화면에 넘기는 다섯 칸, 이어 찾기 5번, 준비 상태를 모를 때, 보낸 사람이 쓴 <mark> 의 한계 - 디자인 README "14단계 구현하며 정한 보완 — 검색": [조건] 32 밀도 예외 재사용, 패널 · 조건 줄 · 결과 없음 · [더 찾기] · 낱말이 너무 많은 주소의 글, 목록 보기는 행 38 에 제목만 강조, 결과 줄의 별 · 고르기 칸(3단계 여러 통 처리), 강조 색 토큰 - vue3 스킬에 14단계 검색 줄과 밀도 예외 재사용을 더한다 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ description: Vue 3 프론트엔드(frontend/) 개발 규칙 — Composition API
|
||||
| 외부 HTML 격리 렌더링 | `src/components/mail/MailBodyFrame.vue` · `src/shared/utils/mail-html.ts` | 메일 본문 등 외부 HTML 은 이 컴포넌트로만 그린다 (sandbox·내부 CSP 근거는 파일 머리말). 보여 줄 HTML 고르기는 `displayedHtmlBody()` 하나다 — null 이거나 공백뿐이면 textBody (읽기 화면·인용 공용, 서버의 `isBodyTruncated` 와 같은 규칙) |
|
||||
| 메일 모듈 | `src/pages/mail/*` · `src/components/mail/*` | API 계약 `docs/mail/api-contract.md`, 디자인 원본 `docs/design/webmail/` |
|
||||
| 콘텐츠 열 토큰 | `src/styles/tokens.css` 의 `--content-*` · `--content-toolbar-start` | 읽기·쓰기 화면이 같은 최대 폭·여백·32px 시작선을 쓴다 (새 화면도 이 토큰으로) |
|
||||
| 밀도 예외 크기 | `src/styles/tokens.css` 의 `--size-compact-control` · `--size-segment-item` · `--size-segment-item-lg` · `--size-editor-tool` · `--size-editor-menu-item` · `--size-editor-swatch-cell` · `--size-editor-table-cell` · `--table-col-check` · `--size-row-check` · `--size-search-clear-width`/`-height` · `--size-notice-bar` · `--size-mailbox-more` · `--size-notice-action` · `--size-filter-control` · `--size-filter-remove` (주석에 "밀도 예외") | 클릭 영역 44px 기준의 의도된 예외(목록 표의 별·보기 전환, 설정 화면의 미리 보기 전환 36(`.segmented`)·서명 그림 크기 줄 36(작게·보통·원본·그림 빼기), 편집기 툴바 버튼 40×44·메뉴 항목 40·색 견본 40·표 고르기 칸 22, 3단계의 목록 표 고르기 칸 32·분할 행 고르기 칸 폭 36·검색 칸 ✕ 40×38·새 메일 알림 줄 40·검색 범위 전환 36·이동 메뉴 항목 40, 4단계의 메일함 행 ⋯ 32×40 · ⋯ 메뉴 항목 40, 5단계의 목록 위 알림 권하기 줄 40 · 줄 안의 단추 36, 6단계의 필터 조건 칸 40 · 조건 ✕ 36 · 필터 행 ▲▼ 32×40 · 켜기 스위치 누르는 곳 40×44 · 고치기 창 안 [다시 불러오기] 36, 8단계의 패널 그룹 줄 ⋯ 32×40 · 연락처 창의 줄 · 칸 40 · 줄 ✕ 36×40 · 구성원 넣기 창 전환 36 · 찾기 칸 40 — 새 토큰 없이 위 것을 다시 쓴다, 9단계의 읽기 화면 안내 줄 단추([이미지 보기] · [차단 풀기] · [고치기] · [예약 취소]) · 차단한 주소 줄의 [차단 풀기] 36(`--size-notice-action`) — 역시 새 토큰 없음, tokens.css 주석)에만 쓴다 — 사용자 확정(캔버스). 그 밖의 `--size-editor-*`(툴바 높이·글꼴 단추·본문 최소 높이 등)는 예외가 아니라 보통 크기다 |
|
||||
| 밀도 예외 크기 | `src/styles/tokens.css` 의 `--size-compact-control` · `--size-segment-item` · `--size-segment-item-lg` · `--size-editor-tool` · `--size-editor-menu-item` · `--size-editor-swatch-cell` · `--size-editor-table-cell` · `--table-col-check` · `--size-row-check` · `--size-search-clear-width`/`-height` · `--size-notice-bar` · `--size-mailbox-more` · `--size-notice-action` · `--size-filter-control` · `--size-filter-remove` (주석에 "밀도 예외") | 클릭 영역 44px 기준의 의도된 예외(목록 표의 별·보기 전환, 설정 화면의 미리 보기 전환 36(`.segmented`)·서명 그림 크기 줄 36(작게·보통·원본·그림 빼기), 편집기 툴바 버튼 40×44·메뉴 항목 40·색 견본 40·표 고르기 칸 22, 3단계의 목록 표 고르기 칸 32·분할 행 고르기 칸 폭 36·검색 칸 ✕ 40×38·새 메일 알림 줄 40·검색 범위 전환 36·이동 메뉴 항목 40, 4단계의 메일함 행 ⋯ 32×40 · ⋯ 메뉴 항목 40, 5단계의 목록 위 알림 권하기 줄 40 · 줄 안의 단추 36, 6단계의 필터 조건 칸 40 · 조건 ✕ 36 · 필터 행 ▲▼ 32×40 · 켜기 스위치 누르는 곳 40×44 · 고치기 창 안 [다시 불러오기] 36, 8단계의 패널 그룹 줄 ⋯ 32×40 · 연락처 창의 줄 · 칸 40 · 줄 ✕ 36×40 · 구성원 넣기 창 전환 36 · 찾기 칸 40 — 새 토큰 없이 위 것을 다시 쓴다, 9단계의 읽기 화면 안내 줄 단추([이미지 보기] · [차단 풀기] · [고치기] · [예약 취소]) · 차단한 주소 줄의 [차단 풀기] 36(`--size-notice-action`) — 역시 새 토큰 없음, 14단계의 검색 칸 [조건] 32(`--size-compact-control`) · 조건 패널의 칸 · 단추 40(`--size-filter-control`) · 조건 줄 40 · 줄 안 [조건 지우기] 36(`--size-notice-action`) — 역시 새 토큰 없음, tokens.css 주석)에만 쓴다 — 사용자 확정(캔버스). 그 밖의 `--size-editor-*`(툴바 높이·글꼴 단추·본문 최소 높이 등)는 예외가 아니라 보통 크기다 |
|
||||
| 사용자별 화면 설정 | `src/stores/mail-view.store.ts` | localStorage(키에 사용자 email, try/catch, 실패 시 기본값). 서버 설정 API 가 생기면 교체 |
|
||||
| 화면 이동 규칙 | `src/composables/useMailNavigation.ts` | 이전/다음은 replace, "목록으로"는 뒤로 가기 또는 replace (기록이 쌓이지 않게) |
|
||||
| 발송 키 규칙 | `src/shared/utils/idempotency-key.ts` 의 `shouldRenewSendKey()` | `Idempotency-Key` 는 성공과 `MAIL_005` 때만 바꾼다 — 그 밖의 실패 뒤 다시 보내기는 같은 키 (계약 2절 "발송 본문"). UUID 만들기는 `shared/utils/uuid.ts` 하나(필터 id 와 공용 — `crypto.randomUUID` 가 없으면 대체 경로) |
|
||||
@@ -52,6 +52,7 @@ description: Vue 3 프론트엔드(frontend/) 개발 규칙 — Composition API
|
||||
| 바탕화면 알림 (5단계) | `src/composables/useNewMailNotifier.ts` · `useDesktopNotification.ts` · `stores/mail-notify.store.ts`(브라우저 · 사용자마다 localStorage) · `shared/utils/desktop-notify.ts` · 목록 위 권하기 `components/mail/MailNotifyOffer.vue` · 설정 `pages/settings/MailNotifyPage.vue`(+ `components/settings/NotifySettingsForm.vue` · `RealtimeLinkStatus.vue`) · 아이콘 `assets/brand/notification-icon.png` · 문구 `shared/constants/mail-notify.ts` | 연결한 탭이 `new-mail` 을 받고 설정이 켜져 있고 허락됐고 **웹메일을 보고 있는 탭이 없을 때만**(보이고 초점이 있음 — `visibilityState === 'visible' && document.hasFocus()`, 다른 탭에 짧게 묻는다) 띄운다 — tag 하나로 앞 알림을 바꿔 쓰고, 사용자가 돌아오면 닫는다. 허락을 묻는 동안 단추는 `disabled` 가 아니라 `aria-disabled`(초점을 잃지 않게). 허락은 사용자가 누른 뒤에만(`Notification.requestPermission()`). 누르면 그 메일로(`useMailNavigation` 의 `openFromNotification` — 세션이 바뀌었으면 이동하지 않는다) |
|
||||
| 실시간 테스트 도구 | `src/test-utils/event-stream.ts`(가짜 SSE 서버) · `notification.ts`(가짜 Notification) · `web-locks.ts`(가짜 `navigator.locks` · BroadcastChannel) | 실시간 · 알림 테스트는 이것들과 손으로 돌리는 시계(`manual-clock.ts`)로 — 진짜 타이머 · 고정 대기 없이 |
|
||||
| 메일 검색 | `src/components/mail/MailSearchBox.vue`(상단 바의 search 슬롯) · `composables/useMailSearchNavigation.ts` · `MailSearchHeader.vue` · `MailSearchScope.vue` · `MailSearchEmpty.vue` · 라우트 `/mail/search` · `/mail/search/:messageId` | Enter 로만 찾는다(`isImeComposing()` 이면 무시), ✕ · Esc 는 검색을 시작한 메일함으로. 주소 `?q=…&in=<메일함 id \| all>` — 검색 화면 안의 다시 찾기 · 범위 바꾸기는 replace. 서버와 같은 한도(200자 · 낱말 8개)를 먼저 본다. 조사 처리 · 한계는 서버 몫(계약 2절 "검색") |
|
||||
| 메일 검색 — 14단계 (조건 · 강조 · 준비 중) | `src/components/mail/MailSearchConditions.vue`(조건 패널 — 칸 `MailSearchConditionFields.vue` · 기간 `MailSearchPeriodField.vue` · [조건] `MailSearchConditionsToggle.vue`, 고치는 값 `composables/useSearchConditionsDraft.ts` · 열고 닫기 `useSearchConditionsPopup.ts`) · `MailSearchConditionLine.vue`(조건 줄) · `MailSearchNotices.vue`(준비 중 · 한 글자 안내) · `MailSearchMore.vue`([더 찾기]) · `HighlightedText.vue` · `shared/utils/mail-search.ts`(조건 ↔ 주소 ↔ API · 기간 · 조건 줄 글) · `search-highlight.ts` · `search-match.ts` · `shared/constants/mail-search.ts` | 조건 이름 · 화면 주소(`since` · `until` 은 브라우저 시간대 날짜 · 양 끝 포함, 첨부 `attachment=1`) · 이어 찾기(`MAIL_SEARCH_FILL_MAX_REQUESTS` 5번)는 계약 2절 "검색 — 14단계"의 클라이언트 줄. **`search-match.ts` 는 backend `shared/utils/search-query.ts` · `search-text.ts` · `subject-base.ts` 와 짝** — 같은 표본 표 `test-utils/search-text-samples.ts`(backend `test/search-text-samples.ts` 와 행 · 차례까지 같다, 보이지 않는 글자는 `\u` 로 적는다 — 한쪽을 고치면 둘 다). 맞은 말은 `HighlightedText` 가 글 조각 + `<mark>` 로 그린다(v-html 없음 — 위치가 글 밖 · 거꾸로 · 정수가 아니면 버리고, 서로게이트 쌍을 가르지 않는다). **강조가 든 응답은 보이기만 한다** — `openMessage(id, highlight)` 는 강조 쿼리가 다르면 다시 받고, 답장 · 전달 · 인쇄 · 예약 · 대용량 첨부 다시 받기는 강조 쿼리 없이 받는다. 손으로 고친 주소도 패널과 같은 검사(8낱말 넘으면 묻지 않고 "찾을 말이 너무 많습니다"). 강조 노랑은 `--color-search-mark` — 본문 틀(`mail-html.ts`)의 같은 값과 짝 |
|
||||
| 여러 통 처리 · 삭제 | `src/components/mail/MailCheckbox.vue` · `MailSelectionToolbar.vue`(+ `MailBulkButton` · `MailBulkMoveButton`) · `MailPurgeDialog.vue` · `MessageDeleteButton.vue` · `composables/useBulkMailActions.ts` · `useMessageRemoval.ts` · `useMailMoveFeedback.ts` · `shared/utils/mail-selection.ts` · `mail-bulk.ts` | 100통씩 차례로. 읽음 · 중요는 먼저 바꾸고 `failedIds` 만 되돌리고, 이동 · 삭제는 응답의 id 만 뺀다. **웹은 `DELETE /api/messages/{id}` 를 쓰지 않는다** — 휴지통 밖 삭제는 `PATCH` 이동(+ 되돌리기), 휴지통에서는 확인 대화상자(처음 초점 「취소」) 뒤 `POST /message-deletions`. 읽기 화면도 그 메일의 `mailboxIds` 로 같은 규칙을 탄다. 도구 줄은 role="toolbar" 로빙 tabindex(바쁜 · 꺼진 단추는 건너뛴다) — 계약 2절 "여러 통 한꺼번에" |
|
||||
| 설정 › 비밀번호 · 부재중 (4단계) | `src/pages/settings/AccountPasswordPage.vue` · `MailVacationPage.vue` · `components/settings/`(`SettingsPageFrame` 공용 틀 — 머리 오른쪽 단추는 `actions` 슬롯 · `PasswordChangeForm` · `PasswordField` · `Vacation*`) · `stores/mail-vacation.store.ts` · `shared/utils/password-change.ts` · `mail-vacation.ts` · 메뉴 `shared/constants/settings-menu.ts` | 메뉴는 묶음(계정 › 비밀번호 / 메일 › 서명 · 부재중 자동 응답 · 필터(6단계) · 알림(5단계)), 라우트 이름은 `SETTINGS_ROUTES` 하나. 비밀번호는 서버와 같은 규칙을 먼저 보고, 서버 거절(`AUTH_005` · `VAL_001`)은 칸 아래에만(알림 없음) — 성공하면 세 칸을 비운다. 부재중은 날짜만 고르고 브라우저 시간대의 시작일 0시 · 끝나는 날 다음 날 0시를 UTC 로 보낸다(변환은 `mail-vacation.ts` 한 곳 — 되읽을 때는 1ms 앞 날짜). 켤 때만 검사하고 꺼 둔 채로는 빈 값도 저장한다(길이 150 · 600 도 켤 때만 — 꺼 둔 채로는 4단계 한도까지, 끄기는 늘 저장되게). 부재중 상태는 메일 화면에 들어올 때 받고, 패널 안내 `components/mail/MailVacationNotice.vue` 가 1분마다 다시 계산한다(`useClock` · `VACATION_NOTICE_REFRESH_MS`). 저장 응답은 세션 세대(`authStore.generation`)가 바뀌었으면 쓰지 않는다. 저장한 값이 켜짐인데 엔진에서 규칙이 꺼져 있으면(응답의 `isActive: false` — 7단계) 머리 아래 노란 줄(`MailFilterNotice` 를 단추 없이 — `actionLabel` 을 주지 않는다). 칸을 고친 값이 아니라 저장한 값으로 보고, `isActive` 는 PUT 본문에 넣지 않는다(`useMail().saveVacation` 이 다섯 칸만) — 계약 1절 · 2절 "부재중 자동 응답" |
|
||||
| 메일함 관리 · 오래 걸리는 처리 (4단계) | `src/components/mail/MailboxTree.vue` · `MailboxPanelRow.vue` · `MailboxMoreButton.vue`(⋯ 메뉴) · `MailboxDialogs.vue`(+ `Mailbox*Dialog.vue`) · `MailboxEmptyButton.vue`(목록 머리 「비우기」) · `composables/useMailboxActions.ts` · `stores/mail/mailbox-admin.ts` · `shared/utils/mailbox-operation.ts` · 상수 `shared/constants/mailbox-manage.ts` | 만들기 · 이름 바꾸기는 칸 안 실패(`MAIL_009` · `VAL_001`)를 알림 없이 칸 아래에. 모두 읽음 · 비우기 · 지우기는 남은 수가 0(지우기는 `deleted: true`)이 될 때까지 다시 보내며 진행 알림을 고친다 — 진행은 응답의 `processed`, 세션이 바뀌면 멈춘다(`epoch` — 늦게 온 만들기 · 이름 바꾸기 응답도 버린다), 같은 메일함의 같은 처리는 하나, **휴지통 비우기와 메일함 지우기는 함께 돌리지 않는다**(비우기는 그사이 휴지통에 들어온 메일도 지운다 — 판정은 스토어의 `isMailboxOperationBlocked` 하나, 메뉴 · 단추 · 대화상자 확인이 모두 이것을 본다). 끝난 뒤 누른 단추가 사라져 초점을 잃었으면 패널의 지금 메일함 항목으로 옮긴다. ⋯ 메뉴는 보이는 영역 안에 다 들게 연다(`shared/utils/popup-placement.ts` — 아래가 모자라면 위로). 되돌릴 수 없는 일은 확인 대화상자(처음 초점 「취소」) 뒤에, 확인한 그 메일함만 — 계약 2절 "메일함 관리" |
|
||||
|
||||
@@ -1034,7 +1034,7 @@ API 와 서버 규칙은 계약 2절 "실시간 알림".
|
||||
같은 대화(답장 · 전달로 이어진 메일)를 읽기 화면에 늘 보이고, 목록은 사용자가 켜면 대화로 묶는다.
|
||||
까닭은 엔진 실측(2026-11-03)이다 — 엔진 스레드는 `회신:` · `답장:` · `전달:` 을 떼지 않아 한국어 Outlook 의 답장이 따로 떨어지고, 엔진 검색은 어절이 통째로 같을 때만 맞는다.
|
||||
데이터 · API 규칙(색인 · 대화 · 조건)은 구현 때 계약 2절에 더한다. 구현 · 배포는 전환 뒤 안정화(1~2주) 다음이다.
|
||||
새 토큰은 없다 — 조건 칸 40 은 필터 조건 칸의 밀도 예외(`--size-filter-control`), [대화로 묶기] 36 은 안내 줄 단추의 밀도 예외(`--size-notice-action`)를 다시 쓴다.
|
||||
새 크기 토큰은 없다 — 조건 칸 40 은 필터 조건 칸의 밀도 예외(`--size-filter-control`), [대화로 묶기] 36 은 안내 줄 단추의 밀도 예외(`--size-notice-action`)를 다시 쓴다(구현 때 [조건] 32 도 — 아래 "14단계 구현하며 정한 보완"). 강조 노랑만 색 토큰(`--color-search-mark`)을 하나 둔다.
|
||||
문구는 위 「화면 문구 — 사용자에게 말하듯」을 따른다.
|
||||
|
||||
### 검색 · 조건 검색 · 강조 (`Search14`)
|
||||
@@ -1374,3 +1374,29 @@ API 와 서버 규칙은 계약 2절 "실시간 알림".
|
||||
- **설정 › 대용량 첨부**: 보낸 링크 머리의 메일 제목이 없으면(지웠거나 읽지 못함 — `subject: null`) 글자만, 빈 제목은 메일이 있는 것이라 링크다.
|
||||
[지우기]가 `FILE_003`(그사이 보내져 링크에 실림)이면 목록 · 사용량을 다시 받고 사전 문구로 알린다.
|
||||
- **내려받기 화면**: 보낸 사람 이름이 비어 있으면 주소를 쓴다. 불러오는 동안은 `role="status"` "불러오는 중"(자리 막대는 `aria-hidden`).
|
||||
|
||||
### 14단계 구현하며 정한 보완 — 검색 (2026-11-06 — 캔버스에는 아직 없다)
|
||||
|
||||
주소 · 이어 찾기의 값은 계약 2절 "검색 — 14단계"의 클라이언트 줄이 같은 내용이다.
|
||||
|
||||
- **크기**: [조건] 단추는 높이 32 — 목록 표 별 · 보기 전환의 밀도 예외(`--size-compact-control`)를 다시 쓴다. 패널의 칸 · 단추 40(`--size-filter-control`), 조건 줄 40 · 줄 안 [조건 지우기] 36(`--size-notice-action`). 새 크기 토큰은 없다.
|
||||
- **[조건] 패널**
|
||||
- 단추의 title "조건으로 찾기", 조건이 걸려 있으면 화면 낭독에 ", 조건이 걸려 있음"을 붙인다.
|
||||
- 칸 안내는 "이름이나 메일 주소"(보낸 사람 · 받는 사람) · "제목에 든 말" · "본문에 든 말"이고, 날짜 칸은 "시작 날짜" · "끝 날짜"다.
|
||||
- 날짜가 거꾸로면 알림 대신 칸 아래 "끝 날짜를 시작 날짜와 같거나 그 뒤로 골라 주세요.", 고를 수 있는 날짜 밖이면 "날짜는 1970년 1월 1일부터 9999년 12월 30일 사이에서 골라 주세요."(날짜 고르기는 이미 막고, 해를 손으로 칠 때만 보인다).
|
||||
- 패널의 [조건 지우기]는 보드대로 패널을 닫고 걸린 조건을 뺀다. 조건만 걸린 검색에서 조건을 지우면 검색을 닫는다.
|
||||
- 패널이 열린 채 검색 칸에서 Esc 를 누르면 패널만 닫고 초점은 검색 칸에 남는다.
|
||||
- **기간**: "최근 N"은 고른 순간 날짜로 바꾼다 — 그만큼 앞의 같은 날부터 오늘까지(1주는 7일 전, 달은 말일을 넘지 않는다).
|
||||
- **조건 줄 글**: "조건: " 뒤에 쉼표로 잇는다 — "{이름} 님이 보낸 메일" · "{이름} 님이 받은 메일" · "제목에 ‘{말}’이/가 든 메일" · "내용에 ‘{말}’이/가 든 메일" · "최근 1달" 또는 "2026. 10. 1.부터 11. 5.까지"(같은 해면 끝 날짜의 해를 뺀다) · "…부터" · "…까지" · "첨부 있음".
|
||||
- **결과 줄**: 분할 보기는 보드대로 세 줄 64 다. **목록 보기(표)는 행 38 그대로 두고 제목만 강조한다**(미리 보기 줄 없음).
|
||||
결과 줄에도 별 · 고르기 칸을 남겼다 — 3단계부터 검색 결과에서도 여러 통을 고르고 처리한다(보드의 결과 줄에는 없다).
|
||||
- **글**
|
||||
- 조건만으로 찾았을 때 머리 글은 "N통" · "찾는 중", 결과 없음은 "조건에 맞는 메일이 없습니다" / "조건을 바꾸거나 줄여 보세요.".
|
||||
- 옛 한계 안내("낱말 단위로 찾습니다 …")는 색인을 채우는 동안(`building`)만 결과 없음 아래에 더한다.
|
||||
- 한 글자 안내: "한 글자로만 찾을 때는 본문은 빼고 제목, 보낸 사람과 받는 사람, 첨부 파일 이름에서 찾습니다. 본문까지 찾으려면 두 글자 이상으로 찾아 주세요." — 색인이 준비됐고 본문을 찾을 말(검색어나 내용 조건)이 있을 때만.
|
||||
- 준비 상태를 받지 못하면(요청 실패) 안내 줄 · 옛 한계 안내 · 한 글자 안내를 모두 보이지 않는다.
|
||||
- **[더 찾기]**: 한 번에 다 찾지 못하면 "아직 맞는 메일을 찾지 못했습니다" / "메일이 많아 한 번에 다 찾지 못했습니다. [더 찾기]를 누르면 이어서 찾습니다." + [더 찾기](찾는 동안 "찾는 중…").
|
||||
하나도 못 찾았으면 "찾는 중"을 이어 보이고, 하나라도 찾으면 먼저 보이면서 뒤에서 잇는다. 목록 보기의 [다음]은 이어 받는 동안 기다렸다가 넘기고, 그동안 꺼짐 · `aria-busy` 다.
|
||||
- **낱말이 너무 많은 주소**(손으로 고친 주소): "찾을 말이 너무 많습니다" / "검색어와 조건에 넣은 낱말은 모두 합쳐 8개까지 찾을 수 있습니다. 검색 칸이나 [조건]에서 낱말을 줄여 주세요." — [전체 메일함에서 찾기] · 알림 없이, 조건 줄의 [조건 지우기]는 남긴다.
|
||||
- **강조 색**: 노랑 `#FCE7A4` 는 색 토큰 `--color-search-mark` 하나로 두고, 본문 틀 안 CSS 는 같은 값을 직접 적는다(짝). 보낸 사람이 본문에 쓴 `<mark>` 도 같은 노랑으로 보인다(드물어 가르지 않는다).
|
||||
- 긴 안내 글(결과 없음 · [더 찾기] · 안내 줄)은 낱말 가운데서 줄을 바꾸지 않는다(`word-break: keep-all`).
|
||||
|
||||
@@ -701,7 +701,21 @@ interface MailState {
|
||||
- 강조할 말: `q` 는 제목 · 본문 · 첨부 이름에, `subject` 는 제목에, `body` 는 본문에. `from` · `to` 는 강조하지 않는다. 화면은 `<mark>` 를 노랑 `#FCE7A4` · 글자색 그대로로 그린다(디자인 README 14단계).
|
||||
- **강조가 든 응답은 보이기만 한다** — 답장 · 전달의 원문 인용 · 인쇄 · 다른 화면의 메일 기억(목록 · 스토어)에는 검색 쿼리 없이 받은 응답을 쓴다(보내는 정화는 `mark` 를 지우지만, 인용 · 인쇄 모양이 검색에 따라 달라지지 않게).
|
||||
- 한 글자 낱말뿐이라 본문을 찾지 않았다는 표시는 응답에 없다 — 화면은 서버와 같은 낱말 나누기(짝 — backend `shared/utils/search-text.ts` · 표본 표 `backend/test/search-text-samples.ts`)로 알아보고 결과 머리에 한 줄 알린다.
|
||||
- 화면은 조건도 화면 주소에 싣는다(검색어처럼 — 앞단 nginx 의 접근 로그는 쿼리를 남기지 않는다). 새 에러 코드는 없다(`VAL_001` · `RES_001` · `MAIL_001`).
|
||||
- 보낸 사람이 본문에 직접 쓴 `<mark>`(받은 메일 정화가 남긴다)도 같은 노랑으로 보여 검색 강조와 가르지 않는다(드물고 브라우저 기본도 노랑이다 — 알려진 한계).
|
||||
- **클라이언트 — 화면 주소**(14단계 웹, 3단계 주소에 더한다): 조건도 검색어처럼 화면 주소에 싣는다(앞단 nginx 의 접근 로그는 쿼리를 남기지 않는다).
|
||||
- 꼴: `/mail/search?q=…&in=<메일함 id | all>&from=…&to=…&subject=…&body=…&since=YYYY-MM-DD&until=YYYY-MM-DD&attachment=1`, 결과에서 연 메일은 `/mail/search/<메일 id>?…`(같은 쿼리).
|
||||
빈 칸은 싣지 않는다(검색어가 비면 `q` 도 뺀다 — 조건만으로 찾는다). `in` 이 비면 `all`.
|
||||
- `q` · `from` · `to` · `subject` · `body`: 앞뒤 공백을 지우고 200자에서 자른다. 같은 이름이 여럿이면 첫 값, 모르는 이름은 무시한다.
|
||||
- `since` · `until`: 브라우저 시간대의 날짜이고 **양 끝을 포함한다** — API 에는 `after` = `since` 날 0시, `before` = `until` 다음 날 0시(UTC ISO)로 보낸다. "최근 1주" 같은 기간은 고른 순간 날짜로 바꿔 싣는다(7일 전 · 달은 말일을 넘지 않는다).
|
||||
있는 날짜이고 1970-01-01 ~ 9999-12-30 이어야 한다(아니면 그 날짜만 버린다). `since` 가 `until` 보다 늦으면 기간을 통째로 버린다. 한쪽만 있어도 된다.
|
||||
- `attachment`: `1` 일 때만 `hasAttachment=true` 다(다른 값은 무시).
|
||||
- 검색어와 조건 칸 넷을 합친 낱말이 8개를 넘으면 목록을 묻지 않고 "찾을 말이 너무 많습니다"를 보인다(손으로 고친 주소 — 화면의 [찾기]는 보내기 전에 막는다). 그때 연 메일은 강조 쿼리 없이 받는다.
|
||||
- 읽기 화면 API 에는 `q` · `from` · `to` · `subject` · `body` 다섯만 넘긴다([이미지 보기]도 같은 쿼리로).
|
||||
- **클라이언트 — 이어 찾기**: `hasNext` 인데 쪽이 모자라면 한 번에(열 때 · [메일 더 보기] · [더 찾기] · 목록 보기의 [다음]) 요청을 5번까지 보낸다(화면 상수 `MAIL_SEARCH_FILL_MAX_REQUESTS`) — 새로 20통이 차거나 `hasNext` 가 false 면 멈춘다.
|
||||
그래도 못 찾았으면 [더 찾기] 화면이다. 다음 쪽의 `total` 이 `null` 이면 첫 쪽의 수를 남기고, 첫 쪽부터 `null` 이면 통 수 · "n / 전체"를 보이지 않는다.
|
||||
목록을 새로 고치는 동안은 이어 받지 않는다(옛 커서로 붙지 않게).
|
||||
- **클라이언트 — 준비 상태**: `GET /api/search-index` 를 받지 못하면(실패) "검색 준비 중" · 옛 한계 안내 · 한 글자 안내를 모두 보이지 않는다. 한 글자 안내는 `ready` 이고 본문을 찾을 말(검색어나 내용 조건)이 있을 때만이다.
|
||||
- 새 에러 코드는 없다(`VAL_001` · `RES_001` · `MAIL_001`).
|
||||
|
||||
### 대화 — 14단계 (같은 대화 · 대화로 묶어 보기)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user