sh-ui-cli 0.61.4 → 0.63.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.
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$description": "sh-ui 릴리즈 노트 단일 소스. docs(React)와 showcase(Flutter)가 함께 읽는다. 새 릴리즈마다 맨 앞에 추가.",
|
|
4
4
|
"versions": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.63.0",
|
|
7
|
+
"date": "2026-05-08",
|
|
8
|
+
"title": "minor — `theme` 컴포넌트 제거, 사용자가 next-themes 직접 사용",
|
|
9
|
+
"type": "minor",
|
|
10
|
+
"highlights": [
|
|
11
|
+
"**`sh-ui add theme` 제거** — registry/react/components/theme 폴더와 registry.json 의 theme 항목 삭제. v0.62.0 의 어댑터는 결국 next-themes 의 얇은 wrapper 였고, 이중 layer 가 외부 deps 안내 누락 같은 잔버그를 부르던 구조. 사용자는 templates RootLayout 에 깔린 next-themes 의 ThemeProvider 위에서 `useTheme` 를 직접 import 해 쓴다.",
|
|
12
|
+
"**마이그레이션 (v0.62.0 사용자)** — `import { useTheme } from '@workspace/ui-web/components/theme'` → `import { useTheme } from 'next-themes'`. 시그니처 차이: sh-ui 의 `theme` 은 light/dark 만, next-themes 는 `resolvedTheme` 사용 (`'system'` 가능). 토글은 `setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')` 패턴.",
|
|
13
|
+
"**docs 자체도 next-themes 직접 사용으로 마이그레이션** — `apps/docs/components/dark-mode-toggle.tsx`, `apps/docs/app/[locale]/layout.tsx` 가 더 이상 sh-ui registry 의 theme 에 의존하지 않음. 컴포넌트 문서 페이지·examples(theme-dashboard, theme-login) 도 함께 정리."
|
|
14
|
+
],
|
|
15
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.63.0"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"version": "0.62.0",
|
|
19
|
+
"date": "2026-05-08",
|
|
20
|
+
"title": "minor — `theme` 컴포넌트가 next-themes 어댑터로 통합 (RootLayout 자동 wiring)",
|
|
21
|
+
"type": "minor",
|
|
22
|
+
"highlights": [
|
|
23
|
+
"**`sh-ui add theme` 의 `useTheme` / `ThemeProvider` 가 next-themes 위에서 동작** — 템플릿이 RootLayout 에 이미 깔아둔 next-themes ThemeProvider 와 동일한 컨텍스트를 공유. 사용자가 add 후 어디서든 `useTheme` 호출 시 throw 없이 즉시 동작. 이전엔 templates 의 next-themes 와 sh-ui 자체 cookie ThemeProvider 두 시스템이 분리돼 있어 혼선.",
|
|
24
|
+
"**시그니처는 보존, 내부 영속화는 cookie → next-themes localStorage 로 이전** — `{ theme, setTheme, toggleTheme }` 와 `light`/`dark` 두 값만 노출하는 정책 그대로. 사용자 코드는 변경 불필요. 다만 기존 `sh-ui-theme` cookie 값은 새 키로 자동 마이그레이션되지 않음(첫 방문 시 defaultTheme 으로 초기화).",
|
|
25
|
+
"**registry.json `theme.dependencies` 에 `next-themes` 추가** — `sh-ui add theme` 시 외부 패키지 자동 설치 안내에 포함. peer-versions.json 에 `^0.4.6` 핀."
|
|
26
|
+
],
|
|
27
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.62.0"
|
|
28
|
+
},
|
|
5
29
|
{
|
|
6
30
|
"version": "0.61.4",
|
|
7
31
|
"date": "2026-05-08",
|
|
@@ -1768,18 +1768,6 @@
|
|
|
1768
1768
|
"utils"
|
|
1769
1769
|
]
|
|
1770
1770
|
},
|
|
1771
|
-
"theme": {
|
|
1772
|
-
"name": "theme",
|
|
1773
|
-
"type": "component",
|
|
1774
|
-
"files": [
|
|
1775
|
-
{
|
|
1776
|
-
"src": "components/theme/index.tsx",
|
|
1777
|
-
"dest": "{components}/theme/index.tsx"
|
|
1778
|
-
}
|
|
1779
|
-
],
|
|
1780
|
-
"dependencies": [],
|
|
1781
|
-
"registryDependencies": []
|
|
1782
|
-
},
|
|
1783
1771
|
"checkbox": {
|
|
1784
1772
|
"name": "checkbox",
|
|
1785
1773
|
"type": "component",
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"spinner": "로딩 스피너 — size.",
|
|
38
38
|
"separator": "구분선 — orientation(horizontal/vertical).",
|
|
39
39
|
"skeleton": "스켈레톤 로딩 플레이스홀더.",
|
|
40
|
-
"theme": "테마 프로바이더 + useTheme 훅 — light/dark/system.",
|
|
41
40
|
"code-panel": "Shiki 기반 코드 하이라이트 패널 — 복사 버튼 포함.",
|
|
42
41
|
"code-tabs": "여러 코드 뷰(예: React/Flutter, 강조/전체)를 탭으로 전환 — Tabs + CodePanel 합성, 각 탭 내용은 CodePanelProps 그대로.",
|
|
43
42
|
"page-toc": "페이지 자동 목차 — 헤딩 스캔 · slugify · IntersectionObserver active 추적 · smooth scroll. routeKey 로 라우트 변경 자동 재스캔, levels/excludeSelector 커스터마이즈.",
|
package/package.json
CHANGED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
|
|
5
|
-
type Theme = "light" | "dark";
|
|
6
|
-
|
|
7
|
-
const THEME_COOKIE_NAME = "sh-ui-theme";
|
|
8
|
-
const THEME_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
9
|
-
|
|
10
|
-
/* ───────────── Context ───────────── */
|
|
11
|
-
|
|
12
|
-
type ThemeContextValue = {
|
|
13
|
-
theme: Theme;
|
|
14
|
-
setTheme: (theme: Theme) => void;
|
|
15
|
-
toggleTheme: () => void;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const ThemeContext = React.createContext<ThemeContextValue | null>(null);
|
|
19
|
-
|
|
20
|
-
/** 현재 테마와 setter를 반환한다. ThemeProvider 안에서만 호출 가능. */
|
|
21
|
-
export function useTheme() {
|
|
22
|
-
const ctx = React.useContext(ThemeContext);
|
|
23
|
-
if (!ctx) throw new Error("useTheme must be used within a ThemeProvider.");
|
|
24
|
-
return ctx;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* ───────────── Provider ───────────── */
|
|
28
|
-
|
|
29
|
-
export interface ThemeProviderProps {
|
|
30
|
-
/**
|
|
31
|
-
* SSR 시 쿠키에서 읽은 초기 테마. 클라이언트 첫 렌더와 SSR 마크업이 일치하도록
|
|
32
|
-
* 서버에서 쿠키를 읽어 주입해야 hydration mismatch가 없다.
|
|
33
|
-
*
|
|
34
|
-
* @default "light"
|
|
35
|
-
* @example
|
|
36
|
-
* // Next.js App Router
|
|
37
|
-
* const t = (await cookies()).get("sh-ui-theme")?.value;
|
|
38
|
-
* <ThemeProvider defaultTheme={t === "dark" ? "dark" : "light"}>
|
|
39
|
-
*/
|
|
40
|
-
defaultTheme?: Theme;
|
|
41
|
-
/**
|
|
42
|
-
* 외부에서 테마를 제어할 때 사용. 지정하면 내부 state 대신 이 값이 우선한다.
|
|
43
|
-
* 보통 `defaultTheme` 비제어 모드로 충분.
|
|
44
|
-
*/
|
|
45
|
-
theme?: Theme;
|
|
46
|
-
/** 테마 변경 콜백. 제어 모드에서는 이 콜백 안에서 외부 상태를 업데이트해야 한다. */
|
|
47
|
-
onThemeChange?: (theme: Theme) => void;
|
|
48
|
-
children: React.ReactNode;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 다크/라이트 테마 컨텍스트와 `<html class="dark">` 토글, 쿠키 영속화를 담당하는 Provider.
|
|
53
|
-
* SSR 하이드레이션 시프트를 피하려면 서버에서 쿠키를 읽어 `defaultTheme`로 주입할 것.
|
|
54
|
-
*/
|
|
55
|
-
export function ThemeProvider({
|
|
56
|
-
defaultTheme = "light",
|
|
57
|
-
theme: themeProp,
|
|
58
|
-
onThemeChange,
|
|
59
|
-
children,
|
|
60
|
-
}: ThemeProviderProps) {
|
|
61
|
-
const [_theme, _setTheme] = React.useState<Theme>(defaultTheme);
|
|
62
|
-
const theme = themeProp ?? _theme;
|
|
63
|
-
|
|
64
|
-
// SSR 폴백 보정 — force-static 페이지에서는 cookies() 가 throw 해 defaultTheme 이
|
|
65
|
-
// 항상 "light" 로 들어온다. mount 시 cookie 를 직접 읽어 React state 와 documentElement
|
|
66
|
-
// 클래스를 사용자가 마지막에 고른 값으로 동기화. 비제어 모드에서만.
|
|
67
|
-
React.useEffect(() => {
|
|
68
|
-
if (themeProp !== undefined) return;
|
|
69
|
-
if (typeof document === "undefined") return;
|
|
70
|
-
const m = document.cookie.match(/(?:^|; )sh-ui-theme=(dark|light)/);
|
|
71
|
-
if (!m) return;
|
|
72
|
-
const fromCookie = m[1] as Theme;
|
|
73
|
-
_setTheme(fromCookie);
|
|
74
|
-
const root = document.documentElement.classList;
|
|
75
|
-
root.toggle("dark", fromCookie === "dark");
|
|
76
|
-
root.toggle("light", fromCookie === "light");
|
|
77
|
-
}, [themeProp]);
|
|
78
|
-
|
|
79
|
-
const setTheme = React.useCallback(
|
|
80
|
-
(next: Theme) => {
|
|
81
|
-
if (onThemeChange) onThemeChange(next);
|
|
82
|
-
else _setTheme(next);
|
|
83
|
-
|
|
84
|
-
if (typeof document !== "undefined") {
|
|
85
|
-
const root = document.documentElement.classList;
|
|
86
|
-
root.toggle("dark", next === "dark");
|
|
87
|
-
root.toggle("light", next === "light");
|
|
88
|
-
document.cookie = `${THEME_COOKIE_NAME}=${next}; path=/; max-age=${THEME_COOKIE_MAX_AGE}`;
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
[onThemeChange],
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const toggleTheme = React.useCallback(() => {
|
|
95
|
-
setTheme(theme === "dark" ? "light" : "dark");
|
|
96
|
-
}, [theme, setTheme]);
|
|
97
|
-
|
|
98
|
-
const value = React.useMemo<ThemeContextValue>(
|
|
99
|
-
() => ({ theme, setTheme, toggleTheme }),
|
|
100
|
-
[theme, setTheme, toggleTheme],
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<ThemeContext.Provider value={value}>
|
|
105
|
-
{children}
|
|
106
|
-
</ThemeContext.Provider>
|
|
107
|
-
);
|
|
108
|
-
}
|