memi-board 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -221,6 +221,7 @@ const { post: postSeo } = await useMemiBoardPostSeo({
221
221
  - [ ] 목록·상세 페이지에 `await useMemiBoard*Seo(…)` 한 줄
222
222
  - [ ] 호스트 SEO API **만들지 않음**
223
223
  - [ ] CSS `@source` (아래)
224
+ - [ ] (선택) 읽은 글 표시 CSS (아래)
224
225
  - [ ] Rules·indexes **deploy**
225
226
  - [ ] 최초 admin + 첫 카테고리
226
227
 
@@ -235,7 +236,20 @@ const { post: postSeo } = await useMemiBoardPostSeo({
235
236
  @source "../../../node_modules/memi-board/dist/runtime/components";
236
237
  ```
237
238
 
238
- ### 5. Security Rules + 인덱스 배포
239
+ ### 5. 읽은 글 표시 (선택)
240
+
241
+ 목록·미니 컴포넌트의 글 링크는 `.memi-board-post-link` 클래스가 붙어 있고, 제목 텍스트는 `text-inherit`로 링크 색을 따른다. 패키지는 색을 강제하지 않으므로, 방문한 글을 흐리게 보이고 싶으면 호스트 CSS에 `:visited` 스타일만 추가하면 된다.
242
+
243
+ ```css
244
+ /* app/assets/css/main.css */
245
+ .memi-board-post-link:visited {
246
+ color: var(--ui-text-muted);
247
+ }
248
+ ```
249
+
250
+ 브라우저 네이티브 `:visited` 라서 서버·DB 저장이 필요 없다. 단, SPA 클라이언트 라우팅(`NuxtLink`)으로만 이동한 경우 브라우저에 "실제 방문"으로 기록되지 않을 수 있어 완전히 신뢰할 수는 없다 — 정확한 읽음 추적이 필요하면 별도로 방문 글 ID를 저장하는 방식을 구현해야 한다.
251
+
252
+ ### 6. Security Rules + 인덱스 배포
239
253
 
240
254
  1. [docs/firestore.rules.example](docs/firestore.rules.example) → 호스트 `firestore.rules`에 병합
241
255
  (`memiBoardPosts`/`memiBoardComments`/`memiBoardLikes`/`memiBoardSettings`/`memiBoardUsers` — 전부 최상위 flat 컬렉션)
@@ -257,7 +271,7 @@ const { post: postSeo } = await useMemiBoardPostSeo({
257
271
 
258
272
  > ⚠️ npm 패키지 tarball에도 `docs/` 가 포함된다(0.4.7+). 구버전이면 GitHub의 `docs/`를 보면 된다.
259
273
 
260
- ### 6. 최초 관리자와 카테고리 만들기
274
+ ### 7. 최초 관리자와 카테고리 만들기
261
275
 
262
276
  Rules는 일반 사용자가 스스로 관리자가 되는 것을 막는다. 따라서 최초 한 번은 다음 순서가 필요하다.
263
277
 
@@ -493,6 +507,7 @@ export default defineNuxtPlugin(() => {
493
507
  - [ ] 목록·상세 페이지에 `await useMemiBoardListSeo` / `useMemiBoardPostSeo`
494
508
  - [ ] 호스트 SEO API 없음
495
509
  - [ ] CSS `@source` …/memi-board/dist/runtime/components
510
+ - [ ] (선택) `.memi-board-post-link:visited` 읽은 글 표시 CSS
496
511
  - [ ] Rules 예시 병합 + **deploy**
497
512
  - [ ] 카테고리·listed 쿼리 사용 시 indexes deploy
498
513
  - [ ] 페이지·미들웨어 직접 작성 (또는 playground 복사)
@@ -35,7 +35,7 @@ function image(post: PostModel): string | undefined {
35
35
  <component
36
36
  :is="postTo(post) ? NuxtLink : 'button'"
37
37
  :to="postTo(post)"
38
- class="flex min-w-0 flex-1 text-left transition-colors hover:bg-elevated/50"
38
+ class="memi-board-post-link flex min-w-0 flex-1 text-left text-highlighted transition-colors hover:bg-elevated/50"
39
39
  @click="!postTo(post) && emit('select', post)"
40
40
  >
41
41
  <div class="min-w-0 flex-1 flex flex-col justify-center gap-1 overflow-hidden py-2 pl-3 pr-2">
@@ -56,7 +56,7 @@ function image(post: PostModel): string | undefined {
56
56
  size="sm"
57
57
  class="mt-0.5 shrink-0"
58
58
  />
59
- <h3 class="min-w-0 line-clamp-2 break-words text-sm font-medium leading-snug sm:text-base">
59
+ <h3 class="min-w-0 line-clamp-2 break-words text-sm font-medium leading-snug text-inherit sm:text-base">
60
60
  {{ post.title?.trim() || post.summary || '사진' }}
61
61
  </h3>
62
62
  <span
@@ -31,7 +31,7 @@ function thumb(post: PostModel): string | undefined {
31
31
  <component
32
32
  :is="postTo(post) ? NuxtLink : 'button'"
33
33
  :to="postTo(post)"
34
- class="group flex min-w-0 w-full items-baseline gap-1.5 rounded-lg text-left transition-colors hover:bg-elevated/60"
34
+ class="memi-board-post-link group flex min-w-0 w-full items-baseline gap-1.5 rounded-lg text-left text-highlighted transition-colors hover:bg-elevated/60"
35
35
  @click="!postTo(post) && emit('select', post)"
36
36
  >
37
37
  <UBadge
@@ -42,7 +42,7 @@ function thumb(post: PostModel): string | undefined {
42
42
  size="sm"
43
43
  class="shrink-0"
44
44
  />
45
- <h3 class="min-w-0 truncate text-sm font-medium leading-snug text-highlighted transition-colors group-hover:text-primary">
45
+ <h3 class="min-w-0 truncate text-sm font-medium leading-snug text-inherit transition-colors group-hover:text-primary">
46
46
  {{ post.title?.trim() || post.summary || '사진' }}
47
47
  </h3>
48
48
  <span
@@ -32,7 +32,7 @@ function cover(post: PostModel): string | undefined {
32
32
  <component
33
33
  :is="postTo(post) ? NuxtLink : 'button'"
34
34
  :to="postTo(post)"
35
- class="block w-full text-left transition hover:bg-elevated/50"
35
+ class="memi-board-post-link block w-full text-left text-highlighted transition hover:bg-elevated/50"
36
36
  @click="!postTo(post) && emit('select', post)"
37
37
  >
38
38
  <div class="relative aspect-video overflow-hidden bg-elevated">
@@ -52,7 +52,7 @@ function cover(post: PostModel): string | undefined {
52
52
  size="sm"
53
53
  class="mt-0.5 shrink-0"
54
54
  />
55
- <h3 class="min-w-0 line-clamp-2 font-medium">{{ post.title }}</h3>
55
+ <h3 class="min-w-0 line-clamp-2 font-medium text-inherit">{{ post.title }}</h3>
56
56
  <span
57
57
  v-if="(post.commentCount ?? 0) > 0"
58
58
  class="mt-0.5 inline-flex shrink-0 items-center rounded-full bg-primary/10 px-1.5 py-px text-[11px] font-semibold tabular-nums text-primary"
@@ -19,7 +19,7 @@ const to = computed(() => props.getPostLink?.(props.post) ?? miniPostLink(props.
19
19
  <component
20
20
  :is="to ? NuxtLink : 'div'"
21
21
  :to="to"
22
- class="group flex h-full min-w-0 flex-col overflow-hidden rounded-lg bg-elevated/40 text-left"
22
+ class="memi-board-post-link group flex h-full min-w-0 flex-col overflow-hidden rounded-lg bg-elevated/40 text-left text-highlighted"
23
23
  >
24
24
  <div
25
25
  class="overflow-hidden bg-muted"
@@ -39,7 +39,7 @@ const to = computed(() => props.getPostLink?.(props.post) ?? miniPostLink(props.
39
39
  </div>
40
40
  </div>
41
41
  <div class="flex flex-1 flex-col gap-0.5 p-2.5">
42
- <p class="line-clamp-2 text-sm font-medium leading-snug text-highlighted group-hover:text-primary">
42
+ <p class="line-clamp-2 text-sm font-medium leading-snug text-inherit group-hover:text-primary">
43
43
  {{ title }}
44
44
  </p>
45
45
  <span class="mt-1 inline-flex items-center gap-0.5 text-[11px] tabular-nums text-muted">
@@ -80,11 +80,11 @@ function postTo(post: PostModel) {
80
80
  <component
81
81
  :is="postTo(post) ? NuxtLink : 'div'"
82
82
  :to="postTo(post)"
83
- class="flex items-center gap-2 px-3 py-2 transition-colors hover:bg-elevated/60"
83
+ class="memi-board-post-link flex items-center gap-2 px-3 py-2 text-highlighted transition-colors hover:bg-elevated/60"
84
84
  >
85
85
  <div class="min-w-0 flex-1">
86
86
  <div class="flex min-w-0 items-start gap-1.5">
87
- <p class="min-w-0 flex-1 truncate text-sm font-medium text-highlighted">
87
+ <p class="min-w-0 flex-1 truncate text-sm font-medium text-inherit">
88
88
  {{ miniPostTitle(post) }}
89
89
  </p>
90
90
  <span class="mt-0.5 inline-flex shrink-0 items-center gap-0.5 text-[11px] tabular-nums text-muted">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memi-board",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "type": "module",
5
5
  "description": "Nuxt 4 + Nuxt UI + VueFire/Firebase 전용 게시판 모듈",
6
6
  "license": "MIT",