sh-ui-cli 0.62.0 → 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,18 @@
|
|
|
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
|
+
},
|
|
5
17
|
{
|
|
6
18
|
"version": "0.62.0",
|
|
7
19
|
"date": "2026-05-08",
|
|
@@ -1768,20 +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
|
-
"next-themes"
|
|
1782
|
-
],
|
|
1783
|
-
"registryDependencies": []
|
|
1784
|
-
},
|
|
1785
1771
|
"checkbox": {
|
|
1786
1772
|
"name": "checkbox",
|
|
1787
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,109 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import {
|
|
5
|
-
ThemeProvider as NextThemesProvider,
|
|
6
|
-
useTheme as useNextTheme,
|
|
7
|
-
} from "next-themes";
|
|
8
|
-
|
|
9
|
-
type Theme = "light" | "dark";
|
|
10
|
-
|
|
11
|
-
/* ───────────── Context ───────────── */
|
|
12
|
-
|
|
13
|
-
type ThemeContextValue = {
|
|
14
|
-
theme: Theme;
|
|
15
|
-
setTheme: (theme: Theme) => void;
|
|
16
|
-
toggleTheme: () => void;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 현재 테마와 setter 를 반환한다. ThemeProvider (또는 next-themes 의
|
|
21
|
-
* ThemeProvider) 안에서만 호출 가능.
|
|
22
|
-
*
|
|
23
|
-
* 내부적으로 next-themes 의 useTheme 를 어댑팅 — `resolvedTheme` 을
|
|
24
|
-
* `light`/`dark` 로 좁혀 노출하고, system 모드는 감추는 형태.
|
|
25
|
-
*/
|
|
26
|
-
export function useTheme(): ThemeContextValue {
|
|
27
|
-
const { resolvedTheme, setTheme: setNextTheme } = useNextTheme();
|
|
28
|
-
const theme: Theme = resolvedTheme === "dark" ? "dark" : "light";
|
|
29
|
-
|
|
30
|
-
const setTheme = React.useCallback(
|
|
31
|
-
(next: Theme) => setNextTheme(next),
|
|
32
|
-
[setNextTheme],
|
|
33
|
-
);
|
|
34
|
-
const toggleTheme = React.useCallback(
|
|
35
|
-
() => setNextTheme(theme === "dark" ? "light" : "dark"),
|
|
36
|
-
[setNextTheme, theme],
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
return React.useMemo(
|
|
40
|
-
() => ({ theme, setTheme, toggleTheme }),
|
|
41
|
-
[theme, setTheme, toggleTheme],
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/* ───────────── Provider ───────────── */
|
|
46
|
-
|
|
47
|
-
export interface ThemeProviderProps {
|
|
48
|
-
/**
|
|
49
|
-
* 비제어 모드의 초기 테마. next-themes 가 storage(localStorage) 에 저장된
|
|
50
|
-
* 값을 우선하므로, 사용자가 한 번 선택한 후에는 이 값이 무시된다.
|
|
51
|
-
*
|
|
52
|
-
* @default "light"
|
|
53
|
-
*/
|
|
54
|
-
defaultTheme?: Theme;
|
|
55
|
-
/**
|
|
56
|
-
* 제어 모드 — 지정 시 강제 테마로 고정 (next-themes `forcedTheme`).
|
|
57
|
-
* 보통 `defaultTheme` 비제어로 충분.
|
|
58
|
-
*/
|
|
59
|
-
theme?: Theme;
|
|
60
|
-
/**
|
|
61
|
-
* 테마 변경 콜백. next-themes 자체는 setter 호출 시 콜백을 노출하지 않으므로
|
|
62
|
-
* 내부 effect 로 변화를 감지해 호출한다.
|
|
63
|
-
*/
|
|
64
|
-
onThemeChange?: (theme: Theme) => void;
|
|
65
|
-
children: React.ReactNode;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 다크/라이트 테마와 `<html class="dark">` 토글을 담당하는 Provider 어댑터.
|
|
70
|
-
*
|
|
71
|
-
* 내부 구현은 next-themes — `attribute='class'`, `enableSystem={false}`,
|
|
72
|
-
* `disableTransitionOnChange` 로 고정. SSR/hydration mismatch 방지를 위해
|
|
73
|
-
* `<html suppressHydrationWarning>` 을 RootLayout 에 함께 둘 것.
|
|
74
|
-
*/
|
|
75
|
-
export function ThemeProvider({
|
|
76
|
-
defaultTheme = "light",
|
|
77
|
-
theme,
|
|
78
|
-
onThemeChange,
|
|
79
|
-
children,
|
|
80
|
-
}: ThemeProviderProps) {
|
|
81
|
-
return (
|
|
82
|
-
<NextThemesProvider
|
|
83
|
-
attribute="class"
|
|
84
|
-
defaultTheme={defaultTheme}
|
|
85
|
-
enableSystem={false}
|
|
86
|
-
disableTransitionOnChange
|
|
87
|
-
forcedTheme={theme}
|
|
88
|
-
themes={["light", "dark"]}
|
|
89
|
-
>
|
|
90
|
-
{onThemeChange ? <ThemeChangeBridge onThemeChange={onThemeChange} /> : null}
|
|
91
|
-
{children}
|
|
92
|
-
</NextThemesProvider>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function ThemeChangeBridge({
|
|
97
|
-
onThemeChange,
|
|
98
|
-
}: {
|
|
99
|
-
onThemeChange: (theme: Theme) => void;
|
|
100
|
-
}) {
|
|
101
|
-
const { theme } = useTheme();
|
|
102
|
-
const last = React.useRef<Theme | null>(null);
|
|
103
|
-
React.useEffect(() => {
|
|
104
|
-
if (last.current === theme) return;
|
|
105
|
-
last.current = theme;
|
|
106
|
-
onThemeChange(theme);
|
|
107
|
-
}, [theme, onThemeChange]);
|
|
108
|
-
return null;
|
|
109
|
-
}
|