+
+ {pages.map((pageItems, pageNumber) => (
+
+ {pageItems.map((item, itemIndex) => {
+ const resultIndex = pageNumber * SEARCH_RESULT_PAGE_SIZE + itemIndex;
+ return (
+
- {item.keywords.length > 0 ? (
-
- {item.keywords.map((keyword) => (
-
- {keyword}
-
- ))}
-
- ) : item.keywordStatus === 'PROCESSING' ? (
-
- AI가 키워드를 분석 중이에요. 잠시 후 자동으로 채워집니다
-
- ) : (
-
이 기록엔 키워드가 없어요
- )}
-
-
-
- ))}
+ {pageCount > 1 && (
+
+ )}
);
}
diff --git a/src/features/home/paperAperture.css b/src/features/home/paperAperture.css
index d1e79e8..f87a853 100644
--- a/src/features/home/paperAperture.css
+++ b/src/features/home/paperAperture.css
@@ -1097,23 +1097,26 @@
position: absolute;
z-index: 15;
left: 50%;
- top: 50%;
- width: min(960px, calc(100% - 320px));
+ top: calc(50% + 38px);
+ width: min(1020px, calc(100% - 280px));
transform: translate(-50%, -50%);
}
-/* 437: 세 카드가 한 화면의 한 "쪽"이다. 네 번째부터는 같은 폭으로 오른쪽에 이어지고, 각 쪽의
- 첫 카드(1·4·7…)에만 snap을 걸어 휠·트랙패드 탐색이 세 장 단위로 안정된다. 스크롤 영역 자체에
- 포커스를 줄 수 있어 키보드 화살표/페이지 이동도 브라우저 기본 동작으로 사용할 수 있다. */
+/* 438: 한 쪽은 2행 × 3열, 총 6장이다. gallery는 쪽을 가로로 넘기는 레일이고 page가 실제 6칸
+ 종이판이다. 한 카드씩 auto-column으로 흘리면 2행에서 순서가 1·3·5 / 2·4·6으로 섞이므로,
+ 반드시 6장씩 DOM을 묶은 page 안에서 row-major grid를 쓴다. */
+.pl-results__viewport {
+ min-width: 0;
+}
+
.pl-results__gallery {
- display: grid;
- grid-auto-flow: column;
- grid-auto-columns: calc((100% - 2rem) / 3);
- gap: 1rem;
+ display: flex;
+ height: min(610px, calc(100cqh - 230px));
+ min-height: 440px;
overflow-x: auto;
overflow-y: hidden;
- padding: 4px 2px 12px;
- scroll-padding-inline: 2px;
+ padding: 10px 12px 8px;
+ scroll-padding-inline: 12px;
scroll-snap-type: x mandatory;
scrollbar-color: theme('colors.log-mint') rgba(250, 247, 246, 0.82);
outline: none;
@@ -1125,13 +1128,146 @@
outline-offset: 4px;
}
-.pl-results__card:nth-child(3n + 1) {
+.pl-results__page {
+ box-sizing: border-box;
+ display: grid;
+ flex: 0 0 100%;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ grid-template-rows: repeat(2, minmax(0, 1fr));
+ gap: 16px 18px;
+ min-width: 0;
+ padding: 4px 8px 8px;
scroll-snap-align: start;
scroll-snap-stop: always;
}
+/* 438 사용자 피드백: 찢긴 가장자리와 장식 종류를 여러 개 섞으면 종이보다 실루엣이 먼저 보여
+ 글을 읽는 데 방해가 된다. 테두리 없는 흰 노트 한 페이지와 은은한 괘선만 남기고,
+ 고정 장치는 앱 공용 PinPushMount 하나만 쓴다. */
+.pl-results__card {
+ position: relative;
+ isolation: isolate;
+ display: flex;
+ min-width: 0;
+ min-height: 0;
+ flex-direction: column;
+ gap: 7px;
+ padding: 19px 18px 15px;
+ border: 0;
+ background:
+ repeating-linear-gradient(0deg, transparent 0 30px, rgba(59, 183, 162, 0.04) 30px 31px), #fff;
+ color: theme('colors.pin-navy');
+ text-align: left;
+ transition:
+ box-shadow 180ms ease,
+ transform 180ms ease;
+ box-shadow: 0 8px 16px rgba(4, 33, 66, 0.11);
+}
+
+.pl-results__card:hover {
+ z-index: 2;
+ box-shadow: 0 12px 20px rgba(4, 33, 66, 0.15);
+ transform: translateY(-3px);
+}
+
+.pl-results__card:focus-visible {
+ outline: 2px solid theme('colors.log-mint');
+ outline-offset: 2px;
+}
+
+.pl-results__index {
+ flex: none;
+ font-family: theme('fontFamily.hand');
+ font-size: 14px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ color: theme('colors.log-mint');
+}
+
+.pl-results__place {
+ min-width: 0;
+ flex: none;
+ padding-bottom: 5px;
+ border-bottom: 1px dashed rgba(4, 33, 66, 0.16);
+}
+
+.pl-results__place-name {
+ overflow: hidden;
+ font-size: 16px;
+ font-weight: 800;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.pl-results__address {
+ overflow: hidden;
+ margin-top: 1px;
+ color: theme('colors.log-mint');
+ font-size: 11px;
+ font-weight: 600;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.pl-results__keywords {
+ display: flex;
+ min-height: 19px;
+ flex: none;
+ gap: 10px;
+ overflow: hidden;
+}
+
+/* 칩 대신 메모지에 형광펜으로 밑줄 친 낱말처럼 보이게 한다. */
+.pl-results__keyword {
+ position: relative;
+ z-index: 0;
+ flex: none;
+ padding-inline: 2px;
+ font-size: 11px;
+ font-weight: 700;
+ color: theme('colors.pin-navy');
+}
+
+.pl-results__keyword::before {
+ content: '';
+ position: absolute;
+ z-index: -1;
+ right: 0;
+ bottom: 1px;
+ left: 0;
+ height: 7px;
+ background: rgba(59, 183, 162, 0.24);
+ transform: rotate(-1.5deg);
+}
+
+.pl-results__keyword-status {
+ min-height: 19px;
+ flex: none;
+ font-size: 10px;
+ color: theme('colors.ink-gray-light');
+}
+
+.pl-results__context {
+ min-height: 0;
+ flex: 1;
+ overflow: visible;
+ padding: 13px 7px 7px;
+}
+
+.pl-results__context .context-sticky-note {
+ min-height: calc(100% - 4px);
+}
+
+/* 회전된 Context의 모서리는 자르지 않되, 긴 본문이 다음 카드까지 침범하지 않게 본문만 3줄로 묶는다. */
+.pl-results__context .context-sticky-note > p:first-of-type {
+ display: -webkit-box;
+ overflow: hidden;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 3;
+}
+
.pl-results__gallery::-webkit-scrollbar {
- height: 12px;
+ height: 10px;
}
.pl-results__gallery::-webkit-scrollbar-track {
@@ -1151,6 +1287,75 @@
background-color: theme('colors.pin-navy');
}
+.pl-results__controls {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ width: min(520px, calc(100% - 24px));
+ margin: 8px auto 0;
+ color: theme('colors.pin-navy');
+}
+
+.pl-results__page-button {
+ display: grid;
+ flex: none;
+ width: 30px;
+ height: 30px;
+ place-items: center;
+ border: 1px solid theme('colors.line-card');
+ border-radius: 9999px;
+ background: rgba(250, 247, 246, 0.94);
+ box-shadow: 0 5px 14px rgba(4, 33, 66, 0.12);
+ font-size: 22px;
+ line-height: 1;
+ transition:
+ color 160ms ease,
+ background-color 160ms ease,
+ transform 160ms ease;
+}
+
+.pl-results__page-button:not(:disabled):hover {
+ color: white;
+ background: theme('colors.log-mint');
+ transform: translateY(-1px);
+}
+
+.pl-results__page-button:focus-visible {
+ outline: 2px solid theme('colors.log-mint');
+ outline-offset: 2px;
+}
+
+.pl-results__page-button:disabled {
+ cursor: default;
+ opacity: 0.35;
+}
+
+.pl-results__page-track {
+ position: relative;
+ flex: 1;
+ height: 8px;
+ overflow: hidden;
+ border: 1px solid theme('colors.line-card');
+ border-radius: 9999px;
+ background: rgba(250, 247, 246, 0.88);
+}
+
+.pl-results__page-thumb {
+ position: absolute;
+ inset-block: 0;
+ left: 0;
+ border-radius: inherit;
+ background: theme('colors.log-mint');
+ transition: transform 220ms ease;
+}
+
+.pl-results__page-status {
+ min-width: 32px;
+ font-size: 11px;
+ font-weight: 700;
+ text-align: center;
+}
+
/* ── 반응형: 축소가 아니라 제본 방식이 바뀐다 ─────────────────────────
넓으면 4판(세로쓰기 곁텍스트 포함), 중간이면 상하판만, 좁으면 상판 위주.
창의 비율 자체가 판형을 따라 바뀐다.
@@ -1202,11 +1407,14 @@
font-size: 12px;
}
.pl-results__gallery {
- grid-auto-columns: min(86%, 288px);
- scroll-snap-type: x mandatory;
+ height: min(610px, calc(100cqh - 210px));
+ }
+ .pl-results__page {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ grid-template-rows: repeat(3, minmax(0, 1fr));
}
- .pl-results__card:nth-child(n) {
- scroll-snap-align: start;
+ .pl-results__controls {
+ width: calc(100% - 24px);
}
}
diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx
index 3300207..4c3249e 100644
--- a/src/pages/HomePage.tsx
+++ b/src/pages/HomePage.tsx
@@ -178,6 +178,7 @@ export function HomePage() {
{hasResults && (
item.recordId).join('-')}
items={searchMutation.data.items}
onSelectRecord={setOpenRecordId}
/>
diff --git a/tests/__pycache__/test_frontend_ci_contract.cpython-312.pyc b/tests/__pycache__/test_frontend_ci_contract.cpython-312.pyc
deleted file mode 100644
index 3b3723c..0000000
Binary files a/tests/__pycache__/test_frontend_ci_contract.cpython-312.pyc and /dev/null differ