@jogak/ui 0.1.0-alpha.7.1 → 0.1.0-alpha.9
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/CHANGELOG.md +13 -114
- package/README.md +41 -23
- package/dist/app/App.d.ts +18 -5
- package/dist/components/Preview/IframeMount.d.ts +22 -24
- package/dist/components/Preview/index.d.ts +14 -5
- package/dist/host/index.cjs +1 -0
- package/dist/host/index.d.ts +21 -4
- package/dist/host/index.mjs +32 -32
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +342 -313
- package/package.json +6 -6
- package/src/app/App.tsx +25 -5
- package/src/app/main.tsx +10 -7
- package/src/app/preview-frame.tsx +45 -30
- package/src/components/Preview/IframeMount.tsx +60 -43
- package/src/components/Preview/index.tsx +39 -14
- package/src/vite-env.d.ts +17 -2
- package/dist/host/index.js +0 -1
- package/dist/index.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jogak/ui",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
4
|
"description": "Showcase viewer UI for Jogak — Sidebar / Preview / Controls / Actions and the JogakApp shell.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jogak",
|
|
@@ -24,19 +24,19 @@
|
|
|
24
24
|
},
|
|
25
25
|
"type": "module",
|
|
26
26
|
"sideEffects": false,
|
|
27
|
-
"main": "./dist/index.
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
28
|
"module": "./dist/index.mjs",
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"exports": {
|
|
31
31
|
".": {
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
33
|
"import": "./dist/index.mjs",
|
|
34
|
-
"require": "./dist/index.
|
|
34
|
+
"require": "./dist/index.cjs"
|
|
35
35
|
},
|
|
36
36
|
"./host": {
|
|
37
37
|
"types": "./dist/host/index.d.ts",
|
|
38
38
|
"import": "./dist/host/index.mjs",
|
|
39
|
-
"require": "./dist/host/index.
|
|
39
|
+
"require": "./dist/host/index.cjs"
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"prism-react-renderer": "^2.4.1",
|
|
66
66
|
"tailwindcss": "^4.0.0",
|
|
67
67
|
"@tailwindcss/vite": "^4.0.0",
|
|
68
|
-
"@jogak/core": "0.1.0-alpha.
|
|
69
|
-
"@jogak/react": "0.1.0-alpha.
|
|
68
|
+
"@jogak/core": "0.1.0-alpha.9",
|
|
69
|
+
"@jogak/react": "0.1.0-alpha.9"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^20.14.0",
|
package/src/app/App.tsx
CHANGED
|
@@ -21,15 +21,28 @@ export interface JogakAppProps {
|
|
|
21
21
|
readonly metas?: readonly RegistryEntryMeta[]
|
|
22
22
|
readonly codeTheme?: string
|
|
23
23
|
/**
|
|
24
|
-
* 알파.
|
|
24
|
+
* 알파.8: Preview 영역 격리 모드. default `'iframe'`.
|
|
25
25
|
*
|
|
26
|
-
* - `'
|
|
27
|
-
* - `'shadow'` — ShadowRoot 안에 마운트. 사용자
|
|
28
|
-
* - `'
|
|
26
|
+
* - `'iframe'` (default) — 사용자 vite 정상 client(iframe)에 마운트. 사용자 utility 정상 컴파일.
|
|
27
|
+
* - `'shadow'` (deprecated) — ShadowRoot 안에 마운트. 사용자 utility 미적용.
|
|
28
|
+
* - `'none'` (deprecated) — chrome 같은 document에 렌더. 알파.6까지의 동작.
|
|
29
29
|
*
|
|
30
30
|
* 자세한 트레이드오프는 `@jogak/ui` README의 "previewIsolation 사용 가이드" 참조.
|
|
31
31
|
*/
|
|
32
32
|
readonly previewIsolation?: 'none' | 'shadow' | 'iframe'
|
|
33
|
+
/**
|
|
34
|
+
* 알파.9: 어댑터 dev URL. iframe `src` base.
|
|
35
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 preview-frame.tsx).
|
|
36
|
+
*/
|
|
37
|
+
readonly userPreviewUrl?: string
|
|
38
|
+
/**
|
|
39
|
+
* 알파.9: iframe entry path (예: `/__jogak_preview__/index.html`).
|
|
40
|
+
*/
|
|
41
|
+
readonly previewEntryPath?: string
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated 알파.10 제거 예정. `userPreviewUrl` 사용.
|
|
44
|
+
*/
|
|
45
|
+
readonly userViteUrl?: string
|
|
33
46
|
}
|
|
34
47
|
|
|
35
48
|
function readUrlParams(): { entryId: string; jogakName: string | null } | null {
|
|
@@ -52,8 +65,13 @@ export function JogakApp({
|
|
|
52
65
|
entries,
|
|
53
66
|
metas,
|
|
54
67
|
codeTheme = 'vsDark',
|
|
55
|
-
previewIsolation = '
|
|
68
|
+
previewIsolation = 'iframe',
|
|
69
|
+
userPreviewUrl = '',
|
|
70
|
+
previewEntryPath = '/__jogak_preview__/index.html',
|
|
71
|
+
userViteUrl,
|
|
56
72
|
}: JogakAppProps = {}): ReactElement {
|
|
73
|
+
// 알파.9: userViteUrl alias (deprecated). userPreviewUrl 우선.
|
|
74
|
+
const resolvedPreviewUrl = userPreviewUrl !== '' ? userPreviewUrl : (userViteUrl ?? '')
|
|
57
75
|
// ── 4가지 모드 결정 (계약 §5.2) ─────────────────────────────────────
|
|
58
76
|
// 1) entries가 주어지면: 새 ComponentRegistry에 register (eager, 기존 동작)
|
|
59
77
|
// 2) metas만 주어지면: defaultRegistry 사용 + metas를 registerMeta로 등록
|
|
@@ -156,6 +174,8 @@ export function JogakApp({
|
|
|
156
174
|
codeTheme={codeTheme}
|
|
157
175
|
onResolveJogak={handleResolveJogak}
|
|
158
176
|
previewIsolation={previewIsolation}
|
|
177
|
+
userPreviewUrl={resolvedPreviewUrl}
|
|
178
|
+
previewEntryPath={previewEntryPath}
|
|
159
179
|
/>
|
|
160
180
|
) : (
|
|
161
181
|
<div className="jogak:flex jogak:items-center jogak:justify-center jogak:h-full jogak:text-[var(--jogak-color-fg-subtle)]">
|
package/src/app/main.tsx
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { StrictMode } from 'react'
|
|
2
2
|
import { createRoot } from 'react-dom/client'
|
|
3
3
|
import 'virtual:jogak'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
_jogakCodeTheme,
|
|
6
|
+
_jogakPreviewIsolation,
|
|
7
|
+
_jogakUserPreviewUrl,
|
|
8
|
+
_jogakPreviewEntryPath,
|
|
9
|
+
} from 'virtual:jogak'
|
|
5
10
|
import '../styles/jogak.css'
|
|
6
11
|
import { JogakApp } from './App.js'
|
|
7
12
|
|
|
8
|
-
// 알파.
|
|
9
|
-
//
|
|
10
|
-
// 사용자 css를 자체 import하므로 outer document inject가 불필요하고, 오히려
|
|
11
|
-
// chrome을 침범한다 (알파.7 결함).
|
|
12
|
-
// - top-level await로 가드 — Vite는 string literal specifier의 dynamic import를
|
|
13
|
-
// 정적 분석하여 별도 chunk + css HMR 표준 경로로 처리한다.
|
|
13
|
+
// 알파.9: 사용자 globalCss는 어댑터 scope(iframe entry)에서 처리되므로 jogak SPA outer
|
|
14
|
+
// document에는 import하지 않는다. 'none' 모드(deprecated)에서만 outer inject.
|
|
14
15
|
if (_jogakPreviewIsolation === 'none') {
|
|
15
16
|
await import('virtual:jogak/global-css')
|
|
16
17
|
}
|
|
@@ -23,6 +24,8 @@ createRoot(rootEl).render(
|
|
|
23
24
|
<JogakApp
|
|
24
25
|
codeTheme={_jogakCodeTheme}
|
|
25
26
|
previewIsolation={_jogakPreviewIsolation}
|
|
27
|
+
userPreviewUrl={_jogakUserPreviewUrl}
|
|
28
|
+
previewEntryPath={_jogakPreviewEntryPath}
|
|
26
29
|
/>
|
|
27
30
|
</StrictMode>,
|
|
28
31
|
)
|
|
@@ -1,46 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 알파.
|
|
2
|
+
* 알파.9: standalone-adapter 또는 fallback 시 사용되는 same-origin iframe entry.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* `IframeMount`는 알파.9에서 postMessage 프로토콜로 통일됐다 (cross-origin 어댑터와 동일).
|
|
5
|
+
* preview-frame.tsx도 같은 프로토콜을 따라야 한다 — 부모는 `jogak:setProps` 메시지를,
|
|
6
|
+
* iframe은 `jogak:ready` / `jogak:rendered` / `jogak:error`를 emit한다.
|
|
7
|
+
*
|
|
8
|
+
* jogak host vite scope에서 동작하므로 `virtual:jogak` (registry metas + entry loader)와
|
|
9
|
+
* `virtual:jogak/global-css` (사용자 globalCss) 가상 모듈을 그대로 사용한다.
|
|
10
10
|
*/
|
|
11
11
|
import { reactAdapter } from '@jogak/react'
|
|
12
|
-
import
|
|
12
|
+
import { defaultRegistry } from '@jogak/core'
|
|
13
13
|
import 'virtual:jogak'
|
|
14
14
|
import 'virtual:jogak/global-css'
|
|
15
15
|
|
|
16
|
-
interface SetPropsArgs {
|
|
17
|
-
readonly entry: RegistryEntry
|
|
18
|
-
readonly args: Readonly<Record<string, unknown>>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
declare global {
|
|
22
|
-
interface Window {
|
|
23
|
-
__jogak_setProps__?: (args: SetPropsArgs) => void
|
|
24
|
-
__jogak_unmount__?: () => void
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
16
|
const rootEl = document.getElementById('jogak-preview-root')
|
|
29
17
|
if (rootEl === null) throw new Error('#jogak-preview-root not found')
|
|
30
18
|
|
|
31
|
-
let
|
|
19
|
+
let currentContainer: HTMLDivElement | null = null
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
21
|
+
async function renderEntry(
|
|
22
|
+
entryId: string,
|
|
23
|
+
args: Readonly<Record<string, unknown>>,
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
const entry = await defaultRegistry.requestEntry(entryId)
|
|
26
|
+
if (currentContainer === null) {
|
|
27
|
+
currentContainer = document.createElement('div')
|
|
28
|
+
rootEl?.replaceChildren(currentContainer)
|
|
37
29
|
}
|
|
38
|
-
reactAdapter.render(entry, args,
|
|
30
|
+
reactAdapter.render(entry, args, currentContainer)
|
|
39
31
|
}
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
reactAdapter.unmount(
|
|
44
|
-
|
|
33
|
+
function unmount(): void {
|
|
34
|
+
if (currentContainer !== null) {
|
|
35
|
+
reactAdapter.unmount(currentContainer)
|
|
36
|
+
currentContainer = null
|
|
45
37
|
}
|
|
46
38
|
}
|
|
39
|
+
|
|
40
|
+
window.addEventListener('message', (event: MessageEvent) => {
|
|
41
|
+
const data = event.data as { type?: unknown; entryId?: unknown; args?: unknown } | null
|
|
42
|
+
if (data === null || typeof data !== 'object') return
|
|
43
|
+
if (data.type === 'jogak:setProps' && typeof data.entryId === 'string') {
|
|
44
|
+
const args = (data.args ?? {}) as Readonly<Record<string, unknown>>
|
|
45
|
+
void renderEntry(data.entryId, args)
|
|
46
|
+
.then(() => {
|
|
47
|
+
window.parent.postMessage(
|
|
48
|
+
{ type: 'jogak:rendered', entryId: data.entryId },
|
|
49
|
+
'*',
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
.catch((err: unknown) => {
|
|
53
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
54
|
+
window.parent.postMessage({ type: 'jogak:error', message }, '*')
|
|
55
|
+
})
|
|
56
|
+
} else if (data.type === 'jogak:unmount') {
|
|
57
|
+
unmount()
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
window.parent.postMessage({ type: 'jogak:ready' }, '*')
|
|
@@ -1,81 +1,98 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
2
2
|
import type { ReactElement } from 'react'
|
|
3
3
|
import type { RegistryEntry } from '@jogak/core'
|
|
4
4
|
|
|
5
5
|
export interface IframeMountProps {
|
|
6
6
|
readonly entry: RegistryEntry
|
|
7
7
|
readonly args: Readonly<Record<string, unknown>>
|
|
8
|
+
/**
|
|
9
|
+
* 알파.9: 어댑터 dev URL (예: `http://localhost:5174`).
|
|
10
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 `/preview-frame.html`).
|
|
11
|
+
*/
|
|
12
|
+
readonly userPreviewUrl: string
|
|
13
|
+
/**
|
|
14
|
+
* 알파.9: iframe entry path (예: `/__jogak_preview__/index.html`).
|
|
15
|
+
* 어댑터의 `previewEntryMeta.devEntryPath`.
|
|
16
|
+
*/
|
|
17
|
+
readonly previewEntryPath: string
|
|
8
18
|
readonly className?: string
|
|
9
19
|
readonly 'data-testid'?: string
|
|
10
20
|
}
|
|
11
21
|
|
|
12
|
-
interface SetPropsArgs {
|
|
13
|
-
readonly entry: RegistryEntry
|
|
14
|
-
readonly args: Readonly<Record<string, unknown>>
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
declare global {
|
|
18
|
-
interface Window {
|
|
19
|
-
__jogak_setProps__?: (args: SetPropsArgs) => void
|
|
20
|
-
__jogak_unmount__?: () => void
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
22
|
/**
|
|
25
|
-
* 알파.
|
|
23
|
+
* 알파.8: previewIsolation='iframe' 모드의 mount 컴포넌트.
|
|
26
24
|
*
|
|
27
|
-
*
|
|
28
|
-
* -
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* - entry/args 변경 시 setProps 재호출 (load 완료 이후).
|
|
25
|
+
* 통신:
|
|
26
|
+
* - 사용자 vite spawn URL이 주어지면(`userViteUrl !== ''`) iframe src를
|
|
27
|
+
* `${userViteUrl}/__jogak_preview__/index.html` (cross-origin)로 설정.
|
|
28
|
+
* - 동일 origin fallback 시 `/preview-frame.html` (jogak SPA Vite scope).
|
|
32
29
|
*
|
|
33
|
-
*
|
|
34
|
-
* - iframe
|
|
35
|
-
*
|
|
36
|
-
* - previewIsolation 모드 자체 변경은 가상 모듈 invalidate → full reload.
|
|
30
|
+
* 양쪽 모두 postMessage로 통신:
|
|
31
|
+
* - 부모 → iframe: `{ type: 'jogak:setProps', entryId, args }` | `{ type: 'jogak:unmount' }`
|
|
32
|
+
* - iframe → 부모: `{ type: 'jogak:ready' }` | `{ type: 'jogak:rendered', entryId }`
|
|
37
33
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
34
|
+
* `entry`는 객체가 아닌 **id만 전달** — iframe 안에서 `defaultRegistry.requestEntry(id)`로
|
|
35
|
+
* dynamic import. 사용자 vite scope의 entry 가상 모듈이 사용자 컴포넌트를 fetch하므로
|
|
36
|
+
* 사용자 plugins(@tailwindcss/vite, custom alias 등)이 정상 작동.
|
|
40
37
|
*/
|
|
41
38
|
export function IframeMount({
|
|
42
39
|
entry,
|
|
43
40
|
args,
|
|
41
|
+
userPreviewUrl,
|
|
42
|
+
previewEntryPath,
|
|
44
43
|
className,
|
|
45
44
|
'data-testid': dataTestId,
|
|
46
45
|
}: IframeMountProps): ReactElement {
|
|
47
46
|
const iframeRef = useRef<HTMLIFrameElement | null>(null)
|
|
48
|
-
const
|
|
47
|
+
const [ready, setReady] = useState(false)
|
|
48
|
+
|
|
49
|
+
const src =
|
|
50
|
+
userPreviewUrl !== ''
|
|
51
|
+
? `${userPreviewUrl}${previewEntryPath}`
|
|
52
|
+
: '/preview-frame.html'
|
|
49
53
|
|
|
50
|
-
// iframe
|
|
54
|
+
// postMessage 리스너 — iframe contentWindow 일치성 검증 후 처리.
|
|
51
55
|
useEffect(() => {
|
|
56
|
+
const handler = (event: MessageEvent): void => {
|
|
57
|
+
const iframe = iframeRef.current
|
|
58
|
+
if (iframe === null) return
|
|
59
|
+
if (event.source !== iframe.contentWindow) return
|
|
60
|
+
const data = event.data
|
|
61
|
+
if (data == null || typeof data !== 'object') return
|
|
62
|
+
if (data.type === 'jogak:ready') setReady(true)
|
|
63
|
+
}
|
|
64
|
+
window.addEventListener('message', handler)
|
|
65
|
+
return () => {
|
|
66
|
+
window.removeEventListener('message', handler)
|
|
67
|
+
}
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
// iframe ready 또는 entry/args 변경 시 setProps.
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!ready) return
|
|
52
73
|
const iframe = iframeRef.current
|
|
53
74
|
if (iframe === null) return
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
iframe.contentWindow?.postMessage(
|
|
76
|
+
{ type: 'jogak:setProps', entryId: entry.id, args },
|
|
77
|
+
'*',
|
|
78
|
+
)
|
|
79
|
+
}, [ready, entry, args])
|
|
80
|
+
|
|
81
|
+
// unmount 시 unmount 메시지 (race 회피 microtask defer).
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const iframe = iframeRef.current
|
|
59
84
|
return () => {
|
|
60
|
-
iframe
|
|
61
|
-
// 알파.7.1: unmount race 회피 — iframe contentWindow 정리도 microtask defer.
|
|
85
|
+
if (iframe === null) return
|
|
62
86
|
queueMicrotask(() => {
|
|
63
|
-
iframe.contentWindow?.
|
|
87
|
+
iframe.contentWindow?.postMessage({ type: 'jogak:unmount' }, '*')
|
|
64
88
|
})
|
|
65
89
|
}
|
|
66
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
90
|
}, [])
|
|
68
91
|
|
|
69
|
-
// entry/args 변경 시 setProps 재호출 (load 후에만)
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
if (!readyRef.current) return
|
|
72
|
-
iframeRef.current?.contentWindow?.__jogak_setProps__?.({ entry, args })
|
|
73
|
-
}, [entry, args])
|
|
74
|
-
|
|
75
92
|
return (
|
|
76
93
|
<iframe
|
|
77
94
|
ref={iframeRef}
|
|
78
|
-
src=
|
|
95
|
+
src={src}
|
|
79
96
|
title="Preview"
|
|
80
97
|
className={className}
|
|
81
98
|
data-testid={dataTestId}
|
|
@@ -24,13 +24,22 @@ export interface PreviewProps {
|
|
|
24
24
|
*/
|
|
25
25
|
readonly onResolveJogak?: (entryId: string, jogakName: string) => void
|
|
26
26
|
/**
|
|
27
|
-
* 알파.
|
|
27
|
+
* 알파.8: Preview 영역 격리 모드. default `'iframe'`.
|
|
28
28
|
*
|
|
29
|
-
* - `'
|
|
30
|
-
* - `'shadow'` — ShadowRoot에 마운트. 사용자
|
|
31
|
-
* - `'
|
|
29
|
+
* - `'iframe'` (default) — 사용자 vite scope에 마운트. 사용자 utility 정상 컴파일.
|
|
30
|
+
* - `'shadow'` (deprecated) — ShadowRoot에 마운트. 사용자 utility 미적용.
|
|
31
|
+
* - `'none'` (deprecated) — chrome과 같은 document에 렌더.
|
|
32
32
|
*/
|
|
33
33
|
readonly previewIsolation?: 'none' | 'shadow' | 'iframe'
|
|
34
|
+
/**
|
|
35
|
+
* 알파.9: 어댑터 dev URL. iframe `src` base.
|
|
36
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 `/preview-frame.html`).
|
|
37
|
+
*/
|
|
38
|
+
readonly userPreviewUrl?: string
|
|
39
|
+
/**
|
|
40
|
+
* 알파.9: iframe entry path.
|
|
41
|
+
*/
|
|
42
|
+
readonly previewEntryPath?: string
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
type ViewportKey = 'mobile' | 'tablet' | 'desktop'
|
|
@@ -120,7 +129,9 @@ export function Preview({
|
|
|
120
129
|
onReset,
|
|
121
130
|
codeTheme,
|
|
122
131
|
onResolveJogak,
|
|
123
|
-
previewIsolation = '
|
|
132
|
+
previewIsolation = 'iframe',
|
|
133
|
+
userPreviewUrl = '',
|
|
134
|
+
previewEntryPath = '/__jogak_preview__/index.html',
|
|
124
135
|
}: PreviewProps): ReactElement {
|
|
125
136
|
const state = useEntry(entryId)
|
|
126
137
|
const [viewport, setViewport] = useState<ViewportKey>('desktop')
|
|
@@ -187,6 +198,8 @@ export function Preview({
|
|
|
187
198
|
onBottomTabChange={setBottomTab}
|
|
188
199
|
prismTheme={prismTheme}
|
|
189
200
|
previewIsolation={previewIsolation}
|
|
201
|
+
userPreviewUrl={userPreviewUrl}
|
|
202
|
+
previewEntryPath={previewEntryPath}
|
|
190
203
|
/>
|
|
191
204
|
)
|
|
192
205
|
}
|
|
@@ -275,6 +288,8 @@ interface ReadyFrameProps {
|
|
|
275
288
|
readonly onBottomTabChange: (tab: 'controls' | 'actions') => void
|
|
276
289
|
readonly prismTheme: PrismTheme
|
|
277
290
|
readonly previewIsolation: 'none' | 'shadow' | 'iframe'
|
|
291
|
+
readonly userPreviewUrl: string
|
|
292
|
+
readonly previewEntryPath: string
|
|
278
293
|
}
|
|
279
294
|
|
|
280
295
|
function ReadyFrame({
|
|
@@ -292,6 +307,8 @@ function ReadyFrame({
|
|
|
292
307
|
onBottomTabChange,
|
|
293
308
|
prismTheme,
|
|
294
309
|
previewIsolation,
|
|
310
|
+
userPreviewUrl,
|
|
311
|
+
previewEntryPath,
|
|
295
312
|
}: ReadyFrameProps): ReactElement {
|
|
296
313
|
// jogakName이 비어있으면 (deep link `?entry=...&jogak` 누락) 첫 jogak로 보정.
|
|
297
314
|
const resolvedJogakName = jogakName ?? entry.jogaks[0]?.name ?? null
|
|
@@ -364,6 +381,8 @@ function ReadyFrame({
|
|
|
364
381
|
source={entry.source}
|
|
365
382
|
theme={prismTheme}
|
|
366
383
|
previewIsolation={previewIsolation}
|
|
384
|
+
userPreviewUrl={userPreviewUrl}
|
|
385
|
+
previewEntryPath={previewEntryPath}
|
|
367
386
|
/>
|
|
368
387
|
</div>
|
|
369
388
|
</div>
|
|
@@ -514,18 +533,18 @@ interface JogakRendererProps {
|
|
|
514
533
|
readonly source: string | undefined
|
|
515
534
|
readonly theme: PrismTheme
|
|
516
535
|
readonly previewIsolation: 'none' | 'shadow' | 'iframe'
|
|
536
|
+
readonly userPreviewUrl: string
|
|
537
|
+
readonly previewEntryPath: string
|
|
517
538
|
}
|
|
518
539
|
|
|
519
540
|
/**
|
|
520
|
-
* 알파.
|
|
521
|
-
*
|
|
522
|
-
* - `'none'` — 같은 document에 직접 마운트 (알파.6까지의 동작 그대로).
|
|
523
|
-
* - `'shadow'` — `<ShadowMount>` 안에 마운트해 ShadowRoot 격리.
|
|
524
|
-
* - `'iframe'` — `<IframeMount>`로 별도 document에 마운트.
|
|
541
|
+
* 알파.9: previewIsolation 모드별로 사용자 콘텐츠 마운트 방식을 분기한다.
|
|
525
542
|
*
|
|
526
|
-
*
|
|
543
|
+
* - `'iframe'` (default) — 어댑터 dev URL의 `<IframeMount>`로 별도 document.
|
|
544
|
+
* - `'shadow'` (deprecated) — `<ShadowMount>` 안에 마운트.
|
|
545
|
+
* - `'none'` (deprecated) — 같은 document에 직접 마운트.
|
|
527
546
|
*/
|
|
528
|
-
function JogakRenderer({ entry, args, source, theme, previewIsolation }: JogakRendererProps): ReactElement {
|
|
547
|
+
function JogakRenderer({ entry, args, source, theme, previewIsolation, userPreviewUrl, previewEntryPath }: JogakRendererProps): ReactElement {
|
|
529
548
|
const [showCode, setShowCode] = useState(false)
|
|
530
549
|
|
|
531
550
|
const previewBody = (
|
|
@@ -534,6 +553,8 @@ function JogakRenderer({ entry, args, source, theme, previewIsolation }: JogakRe
|
|
|
534
553
|
entry={entry}
|
|
535
554
|
args={args}
|
|
536
555
|
previewIsolation={previewIsolation}
|
|
556
|
+
userPreviewUrl={userPreviewUrl}
|
|
557
|
+
previewEntryPath={previewEntryPath}
|
|
537
558
|
/>
|
|
538
559
|
<button
|
|
539
560
|
type="button"
|
|
@@ -575,13 +596,15 @@ interface PreviewMountProps {
|
|
|
575
596
|
readonly entry: RegistryEntry
|
|
576
597
|
readonly args: Readonly<Record<string, unknown>>
|
|
577
598
|
readonly previewIsolation: 'none' | 'shadow' | 'iframe'
|
|
599
|
+
readonly userPreviewUrl: string
|
|
600
|
+
readonly previewEntryPath: string
|
|
578
601
|
}
|
|
579
602
|
|
|
580
603
|
const PREVIEW_HOST_CLASS =
|
|
581
604
|
'jogak:border jogak:border-dashed jogak:border-[var(--jogak-color-border)] ' +
|
|
582
605
|
'jogak:rounded-[var(--jogak-radius-xl)] jogak:p-4 jogak:pb-9'
|
|
583
606
|
|
|
584
|
-
function PreviewMount({ entry, args, previewIsolation }: PreviewMountProps): ReactElement {
|
|
607
|
+
function PreviewMount({ entry, args, previewIsolation, userPreviewUrl, previewEntryPath }: PreviewMountProps): ReactElement {
|
|
585
608
|
if (previewIsolation === 'shadow') {
|
|
586
609
|
return (
|
|
587
610
|
<ShadowMount
|
|
@@ -598,13 +621,15 @@ function PreviewMount({ entry, args, previewIsolation }: PreviewMountProps): Rea
|
|
|
598
621
|
<IframeMount
|
|
599
622
|
entry={entry}
|
|
600
623
|
args={args}
|
|
624
|
+
userPreviewUrl={userPreviewUrl}
|
|
625
|
+
previewEntryPath={previewEntryPath}
|
|
601
626
|
data-testid="preview-content"
|
|
602
627
|
className={`${PREVIEW_HOST_CLASS} jogak:block jogak:w-full jogak:bg-transparent jogak:min-h-[256px]`}
|
|
603
628
|
/>
|
|
604
629
|
)
|
|
605
630
|
}
|
|
606
631
|
|
|
607
|
-
// 'none' —
|
|
632
|
+
// 'none' — deprecated 경로 (알파.7.1 동등 동작 보존, back-compat)
|
|
608
633
|
return <NoneAdapterContent entry={entry} args={args} />
|
|
609
634
|
}
|
|
610
635
|
|
package/src/vite-env.d.ts
CHANGED
|
@@ -4,10 +4,25 @@ declare module 'virtual:jogak' {
|
|
|
4
4
|
/** 플러그인 설정에서 지정한 prism-react-renderer 테마 이름 */
|
|
5
5
|
export const _jogakCodeTheme: string
|
|
6
6
|
/**
|
|
7
|
-
* 알파.
|
|
8
|
-
* `JogakPluginOptions.previewIsolation` (default '
|
|
7
|
+
* 알파.8: Preview 영역 격리 모드 ('none' | 'shadow' | 'iframe').
|
|
8
|
+
* `JogakPluginOptions.previewIsolation` (default 'iframe')의 literal emit.
|
|
9
9
|
*/
|
|
10
10
|
export const _jogakPreviewIsolation: 'none' | 'shadow' | 'iframe'
|
|
11
|
+
/**
|
|
12
|
+
* 알파.9: 어댑터 dev URL. iframe `src` base로 사용 (예: `http://localhost:5174`).
|
|
13
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 preview-frame.tsx).
|
|
14
|
+
*/
|
|
15
|
+
export const _jogakUserPreviewUrl: string
|
|
16
|
+
/**
|
|
17
|
+
* 알파.9: iframe entry path (`BuilderAdapter.previewEntryMeta.devEntryPath`).
|
|
18
|
+
* 어댑터별 routing (vite: `/__jogak_preview__/index.html`).
|
|
19
|
+
*/
|
|
20
|
+
export const _jogakPreviewEntryPath: string
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated 알파.10 제거 예정. `_jogakUserPreviewUrl` 사용.
|
|
23
|
+
* 알파.8 호환 alias.
|
|
24
|
+
*/
|
|
25
|
+
export const _jogakUserViteUrl: string
|
|
11
26
|
}
|
|
12
27
|
|
|
13
28
|
declare module 'virtual:jogak/global-css' {
|
package/dist/host/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";var S=Object.create;var m=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var N=(t,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of A(e))!H.call(t,n)&&n!==o&&m(t,n,{get:()=>e[n],enumerable:!(r=k(e,n))||r.enumerable});return t};var c=(t,e,o)=>(o=t!=null?S(B(t)):{},N(e||!t||!t.__esModule?m(o,"default",{value:t,enumerable:!0}):o,t));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const x=require("node:url"),s=require("node:path");var f=typeof document<"u"?document.currentScript:null;const F=x.fileURLToPath(typeof document>"u"?require("url").pathToFileURL(__filename).href:f&&f.tagName.toUpperCase()==="SCRIPT"&&f.src||new URL("host/index.js",document.baseURI).href),i=s.resolve(s.dirname(F),"..",".."),E=s.resolve(i,"index.html"),Y=s.resolve(i,"src/app/main.tsx");async function q(t){const e=await import("vite"),o=await import("@vitejs/plugin-react"),r=await import("@jogak/core/vite"),n=await import("@tailwindcss/vite"),{createServer:h,build:v}=e,b=o.default,w=n.default,{jogak:_}=r,C=t.codeTheme??"vsDark",u={patterns:t.patterns,codeTheme:C,cwd:t.userRoot};t.tsConfigFilePath!==void 0&&(u.tsConfigFilePath=t.tsConfigFilePath),t.globalCss!==void 0&&(u.globalCss=t.globalCss),t.previewIsolation!==void 0&&(u.previewIsolation=t.previewIsolation);const R=t.extraPlugins??[],d={root:i,configFile:!1,plugins:[b(),w(),_(u),...R],optimizeDeps:{include:["react","react-dom/client","@jogak/core","@jogak/react"]}};if(t.mode==="dev"){const D={port:t.port??5173,host:t.host??"localhost",open:t.open??!1,fs:{allow:[i,t.userRoot]}},L={...d,server:D},a=await h(L);await a.listen();const p=a.config.server.port??t.port??5173,l=t.host??"localhost",M=`http://${typeof l=="boolean"?l?"0.0.0.0":"localhost":l}:${p.toString()}/`;let g=!1;return{url:M,port:p,close:async()=>{g||(g=!0,await a.close())},printUrls:()=>{a.printUrls()}}}const I={...d,base:t.base??"./",build:{outDir:t.outDir,emptyOutDir:!0,sourcemap:t.sourcemap??!1,minify:t.minify??"esbuild",rollupOptions:{input:{main:s.resolve(i,"index.html"),preview:s.resolve(i,"preview-frame.html")}}}},P=Date.now(),T=await v(I),U=Date.now()-P,{assetCount:O,totalBytes:j}=z(T);return{outDir:t.outDir,elapsedMs:U,assetCount:O,totalBytes:j}}function z(t){const e=$(t);if(e===void 0)return{assetCount:0,totalBytes:0};let o=0,r=0;for(const n of e)o+=1,r+=G(n);return{assetCount:o,totalBytes:r}}function y(t){return typeof t=="object"&&t!==null&&Array.isArray(t.output)}function $(t){if(Array.isArray(t)){const e=[];for(const o of t)y(o)&&e.push(...o.output);return e}if(y(t))return t.output}function G(t){if(typeof t!="object"||t===null)return 0;const e=t;if(e.type==="chunk"&&typeof e.code=="string")return Buffer.byteLength(e.code,"utf8");if(e.type==="asset"){const o=e.source;if(typeof o=="string")return Buffer.byteLength(o,"utf8");if(o instanceof Uint8Array)return o.byteLength}return 0}exports.UI_HTML_ENTRY=E;exports.UI_MAIN_ENTRY=Y;exports.runHost=q;
|
package/dist/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),k=require("react"),y=require("@jogak/core"),m=require("@jogak/react"),_=require("prism-react-renderer"),G=require("react-dom");function J(a){var e,t,r="";if(typeof a=="string"||typeof a=="number")r+=a;else if(typeof a=="object")if(Array.isArray(a)){var j=a.length;for(e=0;e<j;e++)a[e]&&(t=J(a[e]))&&(r&&(r+=" "),r+=t)}else for(t in a)a[t]&&(r&&(r+=" "),r+=t);return r}function h(){for(var a,e,t=0,r="",j=arguments.length;t<j;t++)(a=arguments[t])&&(e=J(a))&&(r&&(r+=" "),r+=e);return r}function q({selectedEntryId:a,selectedJogakName:e,onSelect:t}){const[r,j]=k.useState(""),{metaTree:n,searchMeta:g}=m.useRegistryMeta(),l=r.trim().length>0?g(r):null;return o.jsxs("aside",{"data-testid":"sidebar",className:"jogak:flex jogak:flex-col jogak:h-full jogak:overflow-auto jogak:border-r jogak:border-[var(--jogak-color-border)]",children:[o.jsx("div",{className:"jogak:p-3 jogak:border-b jogak:border-[var(--jogak-color-border)]",children:o.jsx("input",{type:"search",placeholder:"Search components...",value:r,onChange:s=>{j(s.target.value)},className:"jogak:w-full jogak:px-2 jogak:py-1.5 jogak:border jogak:border-[var(--jogak-color-border-strong)] jogak:rounded-[var(--jogak-radius-md)]","aria-label":"Search components"})}),o.jsx("nav",{className:"jogak:flex-1 jogak:overflow-auto jogak:py-2",children:l!==null?o.jsx(I,{metas:l,selectedEntryId:a,selectedJogakName:e,onSelect:t}):o.jsx(V,{node:n,selectedEntryId:a,selectedJogakName:e,onSelect:t})})]})}function I({metas:a,selectedEntryId:e,selectedJogakName:t,onSelect:r}){return a.length===0?o.jsx("p",{className:"jogak:px-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px]",children:"No results"}):o.jsx("ul",{className:"jogak:list-none jogak:m-0 jogak:p-0",children:a.map(j=>o.jsx("li",{children:o.jsx(B,{meta:j,selectedEntryId:e,selectedJogakName:t,onSelect:r,indent:0})},j.id))})}function V({node:a,selectedEntryId:e,selectedJogakName:t,onSelect:r,depth:j=0}){return o.jsx("ul",{className:"jogak:list-none jogak:m-0 jogak:pr-0 jogak:py-0 jogak:pl-[var(--jogak-tree-pl)]",style:{"--jogak-tree-pl":`${j*12}px`},children:Object.entries(a).map(([n,g])=>o.jsx("li",{children:"id"in g?o.jsx(B,{meta:g,selectedEntryId:e,selectedJogakName:t,onSelect:r,indent:0}):o.jsx(Q,{label:n,node:g,selectedEntryId:e,selectedJogakName:t,onSelect:r,depth:j+1})},n))})}function Q({label:a,node:e,selectedEntryId:t,selectedJogakName:r,onSelect:j,depth:n}){const[g,l]=k.useState(!0);return o.jsxs("div",{children:[o.jsxs("button",{type:"button",onClick:()=>{l(s=>!s)},className:"jogak:flex jogak:items-center jogak:gap-1 jogak:w-full jogak:px-3 jogak:py-1 jogak:bg-transparent jogak:border-none jogak:cursor-pointer jogak:text-[12px] jogak:font-semibold jogak:text-[var(--jogak-color-fg-muted)] jogak:uppercase jogak:tracking-wider","aria-expanded":g,children:[o.jsx("span",{children:g?"▾":"▸"}),a]}),g&&o.jsx(V,{node:e,selectedEntryId:t,selectedJogakName:r,onSelect:j,depth:n})]})}function B({meta:a,selectedEntryId:e,selectedJogakName:t,onSelect:r,indent:j}){const n=a.id===e,[g,l]=k.useState(n);k.useEffect(()=>{n&&l(!0)},[n]);const s=a.title.split("/").pop()??a.title,c=16+j*12;return o.jsxs("div",{children:[o.jsxs("button",{type:"button",onClick:()=>{if(n)l(i=>!i);else{l(!0);const i=a.jogakNames[0];i!==void 0&&r(a.id,i)}},className:h("jogak:flex jogak:items-center jogak:gap-1.5 jogak:w-full jogak:pr-3 jogak:py-[5px]","jogak:pl-[var(--jogak-entry-pl)]","jogak:border-none jogak:cursor-pointer jogak:text-left jogak:text-[13px]",n?"jogak:bg-[var(--jogak-color-accent-bg)] jogak:text-[var(--jogak-color-accent)] jogak:font-medium":"jogak:bg-transparent jogak:text-[var(--jogak-color-fg)] jogak:font-normal"),style:{"--jogak-entry-pl":`${c}px`},"aria-expanded":g,children:[o.jsx("span",{className:"jogak:text-[10px] jogak:shrink-0 jogak:leading-none",children:g?"▾":"▸"}),s]}),g&&o.jsx("ul",{className:"jogak:list-none jogak:m-0 jogak:p-0",children:a.jogakNames.map(i=>{const x=n&&i===t;return o.jsx("li",{children:o.jsx("button",{type:"button",onClick:()=>{r(a.id,i)},className:h("jogak:block jogak:w-full jogak:text-left jogak:pr-3 jogak:py-1","jogak:pl-[var(--jogak-jogak-pl)]","jogak:border-none jogak:cursor-pointer jogak:text-[12px]",x?"jogak:bg-[var(--jogak-color-accent-bg-soft)] jogak:text-[var(--jogak-color-accent-fg)] jogak:font-medium":"jogak:bg-transparent jogak:text-[var(--jogak-color-fg-muted)] jogak:font-normal"),style:{"--jogak-jogak-pl":`${c+18}px`},"aria-current":x?"true":void 0,children:i})},i)})})]})}function X(a,e){const t=e==null?void 0:e.control,r=(e==null?void 0:e.action)!==void 0&&e.action!==!1,j=(e==null?void 0:e.type)==="function"||typeof a=="function";return r||j?"action":t==="boolean"||typeof a=="boolean"?"boolean":t==="number"||t==="range"||typeof a=="number"?"number":t==="select"||t==="radio"||(e==null?void 0:e.options)!==void 0&&e.options.length>0?"select":t==="text"||t==="color"||typeof a=="string"?"text":"json"}const S="jogak:px-2 jogak:py-1 jogak:border jogak:border-[var(--jogak-color-border-strong)] jogak:rounded-[var(--jogak-radius-md)] jogak:text-[13px] jogak:w-full jogak:max-w-[280px]",C="jogak:px-5 jogak:py-1.5 jogak:text-left jogak:text-[var(--jogak-color-fg-muted)] jogak:font-medium jogak:text-[12px] jogak:border-b jogak:border-[var(--jogak-color-border)]",R="jogak:px-5 jogak:py-2 jogak:align-middle jogak:border-b jogak:border-[var(--jogak-color-border-muted)]";function Y({argKey:a,value:e,argType:t,onArgChange:r}){switch(X(e,t)){case"boolean":return o.jsx("input",{type:"checkbox",checked:e===!0,onChange:n=>{r(a,n.target.checked)},className:"jogak:cursor-pointer jogak:w-4 jogak:h-4 jogak:accent-[var(--jogak-color-accent)]"});case"number":return o.jsx("input",{type:"number",value:typeof e=="number"?e:"",onChange:n=>{r(a,n.target.valueAsNumber)},className:S});case"select":{const n=(t==null?void 0:t.options)??[];return o.jsx("select",{value:String(e??""),onChange:g=>{r(a,g.target.value)},className:S,children:n.map(g=>o.jsx("option",{value:String(g),children:String(g)},String(g)))})}case"text":return o.jsx("input",{type:"text",value:typeof e=="string"?e:String(e??""),onChange:n=>{r(a,n.target.value)},className:S});case"action":return o.jsx("span",{className:"jogak:inline-block jogak:px-2 jogak:py-0.5 jogak:text-[11px] jogak:font-semibold jogak:text-[var(--jogak-color-violet)] jogak:bg-[var(--jogak-color-violet-bg)] jogak:border jogak:border-[var(--jogak-color-violet-border)] jogak:rounded-[var(--jogak-radius-md)] jogak:font-[family-name:var(--jogak-font-mono)] jogak:leading-none",children:"(action)"});case"json":return o.jsx("code",{className:"jogak:text-[12px] jogak:text-[var(--jogak-color-fg-muted)] jogak:font-[family-name:var(--jogak-font-mono)]",children:JSON.stringify(e)})}}function W({args:a,argTypes:e,onArgChange:t}){const j=Array.from(new Set([...Object.keys(a),...Object.keys(e)])).map(n=>[n,a[n]]);return o.jsxs("div",{className:"jogak:border-t-2 jogak:border-[var(--jogak-color-border)]",children:[o.jsx("div",{className:"jogak:px-5 jogak:py-1.5 jogak:text-[11px] jogak:font-bold jogak:text-[var(--jogak-color-fg-subtle)] jogak:uppercase jogak:tracking-[0.08em] jogak:border-b jogak:border-[var(--jogak-color-border)] jogak:bg-[var(--jogak-color-bg-subtle)]",children:"Controls"}),j.length===0?o.jsx("div",{className:"jogak:px-5 jogak:py-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px]",children:"No args defined"}):o.jsxs("table",{className:"jogak:w-full jogak:border-collapse jogak:text-[13px]",children:[o.jsx("thead",{children:o.jsxs("tr",{children:[o.jsx("th",{className:C,children:"Name"}),o.jsx("th",{className:C,children:"Control"}),o.jsx("th",{className:C,children:"Description"})]})}),o.jsx("tbody",{children:j.map(([n,g])=>{const l=e[n];return o.jsxs("tr",{children:[o.jsx("td",{className:h(R,"jogak:font-[family-name:var(--jogak-font-mono)] jogak:text-[12px] jogak:text-[var(--jogak-color-fg)] jogak:whitespace-nowrap"),children:n}),o.jsx("td",{className:R,children:o.jsx(Y,{argKey:n,value:g,argType:l,onArgChange:t})}),o.jsx("td",{className:h(R,"jogak:text-[var(--jogak-color-fg-subtle)]"),children:(l==null?void 0:l.description)??""})]},n)})})]})]})}function Z(a){if(a.length===0)return"()";try{return a.map(e=>{var t;if(e===null)return"null";if(e===void 0)return"undefined";if(typeof e=="function")return"[Function]";if(typeof e=="object"){const r=((t=e.constructor)==null?void 0:t.name)??"Object";return r!=="Object"&&r!=="Array"?`[${r}]`:JSON.stringify(e)}return JSON.stringify(e)}).join(", ")}catch{return"[unserializable]"}}function K(a){const e=new Date(a),t=e.getHours().toString().padStart(2,"0"),r=e.getMinutes().toString().padStart(2,"0"),j=e.getSeconds().toString().padStart(2,"0"),n=e.getMilliseconds().toString().padStart(3,"0");return`${t}:${r}:${j}.${n}`}function z(){const[a,e]=k.useState(()=>y.defaultActionChannel.getLogs());k.useEffect(()=>y.defaultActionChannel.subscribe(e),[]);const t=a.length===0;return o.jsxs("div",{className:"jogak:h-full jogak:flex jogak:flex-col",children:[o.jsxs("div",{className:"jogak:px-5 jogak:py-1.5 jogak:text-[11px] jogak:font-bold jogak:text-[var(--jogak-color-fg-subtle)] jogak:uppercase jogak:tracking-[0.08em] jogak:border-b jogak:border-[var(--jogak-color-border)] jogak:bg-[var(--jogak-color-bg-subtle)] jogak:flex jogak:items-center jogak:justify-between jogak:shrink-0",children:[o.jsxs("span",{children:["Actions ",a.length>0&&`(${a.length.toString()})`]}),o.jsx("button",{type:"button",onClick:()=>{y.defaultActionChannel.clear()},disabled:t,className:h("jogak:text-[10px] jogak:font-semibold jogak:px-2 jogak:py-0.5 jogak:border jogak:border-[var(--jogak-color-border-strong)] jogak:rounded-[var(--jogak-radius-sm)] jogak:bg-[var(--jogak-color-bg)] jogak:normal-case jogak:tracking-normal",t?"jogak:text-[var(--jogak-color-fg-subtle)] jogak:cursor-default":"jogak:text-[var(--jogak-color-fg)] jogak:cursor-pointer"),children:"Clear"})]}),o.jsx("div",{className:"jogak:flex-1 jogak:overflow-auto",children:t?o.jsx("div",{className:"jogak:px-5 jogak:py-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px] jogak:leading-none",children:"함수 prop이 호출되면 여기에 기록됩니다"}):o.jsx("ul",{className:"jogak:list-none jogak:m-0 jogak:p-0 jogak:font-[family-name:var(--jogak-font-mono)] jogak:text-[12px]",children:a.map(r=>o.jsxs("li",{className:"jogak:flex jogak:items-baseline jogak:gap-[10px] jogak:px-5 jogak:py-1.5 jogak:border-b jogak:border-[var(--jogak-color-border-muted)]",children:[o.jsx("span",{className:"jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[11px] jogak:min-w-[92px]",children:K(r.timestamp)}),o.jsx("span",{className:"jogak:text-[var(--jogak-color-violet)] jogak:font-semibold",children:r.name}),o.jsxs("span",{className:"jogak:text-[var(--jogak-color-fg)] jogak:break-all jogak:flex-1",children:["(",Z(r.args),")"]})]},r.id))})})]})}function T({children:a,className:e,style:t,"data-testid":r}){const j=k.useRef(null),[n,g]=k.useState(null);return k.useEffect(()=>{const l=j.current;if(l===null)return;const s=l.shadowRoot??l.attachShadow({mode:"open"});g(s)},[]),o.jsx("div",{ref:j,className:e,"data-testid":r,style:t,children:n!==null?G.createPortal(a,n):null})}function oo({entry:a,args:e,className:t,"data-testid":r}){const j=k.useRef(null),n=k.useRef(!1);return k.useEffect(()=>{const g=j.current;if(g===null)return;const l=()=>{var s,c;n.current=!0,(c=(s=g.contentWindow)==null?void 0:s.__jogak_setProps__)==null||c.call(s,{entry:a,args:e})};return g.addEventListener("load",l),()=>{g.removeEventListener("load",l),queueMicrotask(()=>{var s,c;(c=(s=g.contentWindow)==null?void 0:s.__jogak_unmount__)==null||c.call(s)})}},[]),k.useEffect(()=>{var g,l,s;n.current&&((s=(l=(g=j.current)==null?void 0:g.contentWindow)==null?void 0:l.__jogak_setProps__)==null||s.call(l,{entry:a,args:e}))},[a,e]),o.jsx("iframe",{ref:j,src:"/preview-frame.html",title:"Preview",className:t,"data-testid":r})}const D={mobile:375,tablet:768,desktop:"none"},ao={mobile:"Mobile",tablet:"Tablet",desktop:"Desktop"},E={white:{"--jogak-canvas-bg":"#ffffff","--jogak-canvas-bg-image":"none","--jogak-canvas-bg-size":"auto","--jogak-canvas-bg-position":"0 0"},dark:{"--jogak-canvas-bg":"#1f2937","--jogak-canvas-bg-image":"none","--jogak-canvas-bg-size":"auto","--jogak-canvas-bg-position":"0 0"},transparent:{"--jogak-canvas-bg":"#ffffff","--jogak-canvas-bg-image":"linear-gradient(45deg, #e2e8f0 25%, transparent 25%), linear-gradient(-45deg, #e2e8f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e2e8f0 75%), linear-gradient(-45deg, transparent 75%, #e2e8f0 75%)","--jogak-canvas-bg-size":"16px 16px","--jogak-canvas-bg-position":"0 0, 0 8px, 8px -8px, -8px 0px"}},$="jogak:bg-[var(--jogak-canvas-bg)] jogak:bg-[image:var(--jogak-canvas-bg-image)] jogak:bg-[length:var(--jogak-canvas-bg-size)] jogak:bg-[position:var(--jogak-canvas-bg-position)]";function eo(a){return _.themes[a]??_.themes.vsDark}function F({entryId:a,jogakName:e,overrideArgs:t,onArgChange:r,onReset:j,codeTheme:n,onResolveJogak:g,previewIsolation:l="shadow"}){const s=m.useEntry(a),[c,i]=k.useState("desktop"),[x,v]=k.useState("white"),[w,f]=k.useState("controls"),b=eo(n);return s.status==="unknown"?o.jsxs("div",{"data-testid":"preview-not-found",className:"jogak:p-6 jogak:text-[var(--jogak-color-error)]",children:["Entry not found: ",a]}):s.status==="error"?o.jsxs("div",{"data-testid":"preview-error",className:"jogak:p-6 jogak:text-[var(--jogak-color-error-fg)] jogak:bg-[var(--jogak-color-bg-error)] jogak:h-full jogak:flex jogak:flex-col jogak:gap-3 jogak:items-start",children:[o.jsxs("div",{className:"jogak:font-semibold",children:["Failed to load entry: ",a]}),o.jsx("pre",{className:"jogak:m-0 jogak:p-3 jogak:bg-[var(--jogak-color-bg)] jogak:border jogak:border-[var(--jogak-color-error-border)] jogak:rounded-[var(--jogak-radius-lg)] jogak:text-[12px] jogak:whitespace-pre-wrap jogak:max-w-full",children:s.error.message})]}):s.status==="loading"?o.jsx(to,{meta:s.meta,jogakName:e,viewport:c,bgMode:x,onViewportChange:i,onBgModeChange:v}):o.jsx(ro,{entry:s.entry,jogakName:e,overrideArgs:t,onArgChange:r,onReset:j,onResolveJogak:g,viewport:c,bgMode:x,bottomTab:w,onViewportChange:i,onBgModeChange:v,onBottomTabChange:f,prismTheme:b,previewIsolation:l})}function to({meta:a,jogakName:e,viewport:t,bgMode:r,onViewportChange:j,onBgModeChange:n}){const g=e??a.jogakNames[0]??"...",l=D[t];return o.jsxs("div",{"data-testid":"preview-loading",className:"jogak:flex jogak:flex-col jogak:h-full",children:[o.jsx(H,{title:a.title,jogakName:g,viewport:t,bgMode:r,onViewportChange:j,onBgModeChange:n,showReset:!1,onReset:()=>{}}),o.jsx("div",{className:`jogak:flex-1 jogak:overflow-auto jogak:min-h-[320px] ${$}`,style:E[r],children:o.jsx("div",{className:"jogak:mx-auto jogak:p-6 jogak:max-w-[var(--jogak-canvas-mw)]",style:{"--jogak-canvas-mw":l==="none"?"100%":`${l}px`},children:o.jsxs("div",{className:"jogak-skeleton-shimmer jogak:border jogak:border-dashed jogak:border-[var(--jogak-color-border)] jogak:rounded-[var(--jogak-radius-xl)] jogak:p-4 jogak:flex jogak:items-center jogak:justify-center jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px] jogak:min-h-[256px]",children:["Loading ",a.title,"…"]})})})]})}function ro({entry:a,jogakName:e,overrideArgs:t,onArgChange:r,onReset:j,onResolveJogak:n,viewport:g,bgMode:l,bottomTab:s,onViewportChange:c,onBgModeChange:i,onBottomTabChange:x,prismTheme:v,previewIsolation:w}){var L;const f=e??((L=a.jogaks[0])==null?void 0:L.name)??null;if(k.useEffect(()=>{e===null&&f!==null&&n!==void 0&&n(a.id,f)},[e,f,a.id,n]),f===null)return o.jsxs("div",{className:"jogak:p-6 jogak:text-[var(--jogak-color-error)]",children:["Entry has no jogaks: ",a.id]});const b=a.jogaks.find(N=>N.name===f);if(b===void 0)return o.jsxs("div",{className:"jogak:p-6 jogak:text-[var(--jogak-color-error)]",children:["Jogak not found: ",f]});const u={...b.args??{},...t},p={...a.meta.argTypes??{},...b.argTypes??{}},U=Object.keys(t).length>0,M=D[g];return o.jsxs("div",{className:"jogak:flex jogak:flex-col jogak:h-full",children:[o.jsx(H,{title:a.title,jogakName:b.name,viewport:g,bgMode:l,onViewportChange:c,onBgModeChange:i,showReset:U,onReset:j}),o.jsx("div",{className:`jogak:flex-1 jogak:overflow-auto jogak:min-h-[320px] ${$}`,style:E[l],children:o.jsx("div",{"data-jogak-content":!0,className:"jogak:mx-auto jogak:p-6 jogak:max-w-[var(--jogak-canvas-mw)]",style:{"--jogak-canvas-mw":M==="none"?"100%":`${M}px`},children:o.jsx(no,{entry:a,args:u,source:a.source,theme:v,previewIsolation:w},`${a.id}/${b.name}`)})}),o.jsxs("div",{"data-testid":"bottom-panel",className:"jogak:h-[260px] jogak:shrink-0 jogak:flex jogak:flex-col jogak:border-t-2 jogak:border-[var(--jogak-color-border)]",children:[o.jsx("div",{role:"tablist",className:"jogak:flex jogak:gap-1 jogak:pt-1 jogak:px-3 jogak:pb-0 jogak:bg-[var(--jogak-color-bg)] jogak:border-b jogak:border-[var(--jogak-color-border)] jogak:shrink-0",children:["controls","actions"].map(N=>{const O=s===N;return o.jsx("button",{type:"button",role:"tab","aria-selected":O,onClick:()=>{x(N)},className:h("jogak:px-[14px] jogak:py-[6px] jogak:text-[12px] jogak:bg-transparent jogak:border-x-0 jogak:border-t-0 jogak:border-b-2 jogak:border-solid jogak:-mb-px jogak:cursor-pointer jogak:capitalize",O?"jogak:font-semibold jogak:text-[var(--jogak-color-fg-strong)] jogak:border-[var(--jogak-color-accent)]":"jogak:font-medium jogak:text-[var(--jogak-color-fg-muted)] jogak:border-transparent"),children:N},N)})}),o.jsx("div",{className:"jogak:flex-1 jogak:min-h-0 jogak:overflow-auto",children:s==="controls"?o.jsx(W,{args:u,argTypes:p,onArgChange:r}):o.jsx(z,{})})]})]})}function H({title:a,jogakName:e,viewport:t,bgMode:r,onViewportChange:j,onBgModeChange:n,showReset:g,onReset:l}){return o.jsxs("div",{className:"jogak:flex jogak:items-center jogak:gap-[10px] jogak:px-[14px] jogak:py-[7px] jogak:border-b jogak:border-[var(--jogak-color-border)] jogak:bg-[var(--jogak-color-bg)] jogak:shrink-0",children:[o.jsxs("div",{className:"jogak:flex-1 jogak:text-[13px]",children:[o.jsx("span",{className:"jogak:text-[var(--jogak-color-fg-subtle)]",children:a}),o.jsx("span",{className:"jogak:text-[var(--jogak-color-border-strong)] jogak:mx-1.5 jogak:leading-none",children:"/"}),o.jsx("span",{className:"jogak:text-[var(--jogak-color-fg-strong)] jogak:font-semibold",children:e})]}),o.jsx("div",{className:"jogak:flex jogak:gap-0.5 jogak:bg-[var(--jogak-color-bg-subtle)] jogak:rounded-[var(--jogak-radius-lg)] jogak:p-0.5",children:["mobile","tablet","desktop"].map(s=>o.jsx("button",{type:"button",onClick:()=>{j(s)},"aria-pressed":t===s,className:h("jogak:px-[9px] jogak:py-[3px] jogak:text-[12px] jogak:border-none jogak:rounded-[var(--jogak-radius-md)] jogak:cursor-pointer jogak:transition-all jogak:duration-100",t===s?"jogak:bg-[var(--jogak-color-bg-elevated)] jogak:text-[var(--jogak-color-fg-strong)] jogak:font-semibold jogak:shadow-[0_1px_2px_rgba(0,0,0,0.08)]":"jogak:bg-transparent jogak:text-[var(--jogak-color-fg-muted)] jogak:font-normal jogak:shadow-none"),children:ao[s]},s))}),o.jsx("div",{className:"jogak:flex jogak:gap-1 jogak:items-center",children:["white","dark","transparent"].map(s=>o.jsx("button",{type:"button",onClick:()=>{n(s)},"aria-pressed":r===s,"aria-label":`${s} background`,className:h("jogak:w-5 jogak:h-5 jogak:rounded-[var(--jogak-radius-md)] jogak:border-2 jogak:cursor-pointer jogak:p-0 jogak:shrink-0",$,r===s?"jogak:border-[var(--jogak-color-accent)]":"jogak:border-[var(--jogak-color-border-strong)]"),style:E[s]},s))}),g&&o.jsx("button",{type:"button",onClick:l,className:"jogak:px-[10px] jogak:py-[3px] jogak:text-[12px] jogak:border jogak:border-[var(--jogak-color-border-strong)] jogak:rounded-[var(--jogak-radius-md)] jogak:bg-[var(--jogak-color-bg)] jogak:cursor-pointer jogak:text-[var(--jogak-color-fg)] jogak:leading-none",children:"Reset"})]})}function no({entry:a,args:e,source:t,theme:r,previewIsolation:j}){const[n,g]=k.useState(!1),l=o.jsxs("div",{className:"jogak:relative",children:[o.jsx(so,{entry:a,args:e,previewIsolation:j}),o.jsx("button",{type:"button",onClick:()=>{g(s=>!s)},"aria-pressed":n,"aria-label":n?"Hide source code":"Show source code",className:h("jogak:absolute jogak:bottom-2 jogak:right-2 jogak:px-[9px] jogak:py-1","jogak:text-[11px] jogak:font-[family-name:var(--jogak-font-mono)] jogak:font-semibold jogak:tracking-[0.02em]","jogak:text-[var(--jogak-color-bg)] jogak:border-none jogak:rounded-[5px] jogak:cursor-pointer","jogak:shadow-[0_1px_4px_rgba(0,0,0,0.2)] jogak:transition-[background-color] jogak:duration-150 jogak:leading-none",n?"jogak:bg-[var(--jogak-color-accent)]":"jogak:bg-[#1e293b]"),children:"</>"})]});return o.jsxs("div",{children:[l,n&&o.jsx("div",{className:"jogak:mt-2 jogak:rounded-[var(--jogak-radius-xl)] jogak:overflow-hidden jogak:h-[320px] jogak:shadow-[0_0_0_1px_rgba(0,0,0,0.08),_0_4px_16px_rgba(0,0,0,0.12)]",children:o.jsx(lo,{source:t,theme:r})})]})}const A="jogak:border jogak:border-dashed jogak:border-[var(--jogak-color-border)] jogak:rounded-[var(--jogak-radius-xl)] jogak:p-4 jogak:pb-9";function so({entry:a,args:e,previewIsolation:t}){return t==="shadow"?o.jsx(T,{"data-testid":"preview-content",className:A,children:o.jsx(jo,{entry:a,args:e})}):t==="iframe"?o.jsx(oo,{entry:a,args:e,"data-testid":"preview-content",className:`${A} jogak:block jogak:w-full jogak:bg-transparent jogak:min-h-[256px]`}):o.jsx(go,{entry:a,args:e})}function go({entry:a,args:e}){const t=k.useRef(null);return k.useEffect(()=>{const r=t.current;if(r!==null)return m.reactAdapter.render(a,e,r),()=>{queueMicrotask(()=>{m.reactAdapter.unmount(r)})}},[a]),k.useEffect(()=>{const r=t.current;r!==null&&m.reactAdapter.render(a,e,r)},[a,e]),o.jsx("div",{ref:t,"data-testid":"preview-content",className:A})}function jo({entry:a,args:e}){const t=k.useRef(null);return k.useEffect(()=>{const r=t.current;if(r!==null)return m.reactAdapter.render(a,e,r),()=>{queueMicrotask(()=>{m.reactAdapter.unmount(r)})}},[a]),k.useEffect(()=>{const r=t.current;r!==null&&m.reactAdapter.render(a,e,r)},[a,e]),o.jsx("div",{ref:t,"data-testid":"preview-content-shadow"})}function lo({source:a,theme:e}){const[t,r]=k.useState(!1),j=e.plain.backgroundColor??"#1e293b";if(a===void 0)return o.jsx("div",{className:"jogak:h-full jogak:flex jogak:items-center jogak:justify-center jogak:bg-[var(--jogak-source-bg)] jogak:text-[#94a3b8] jogak:text-[13px]",style:{"--jogak-source-bg":j},children:"Source not available"});const n=()=>{navigator.clipboard.writeText(a).then(()=>{r(!0),setTimeout(()=>{r(!1)},2e3)})};return o.jsxs("div",{className:"jogak:relative jogak:h-full",children:[o.jsx("button",{type:"button",onClick:n,className:"jogak:absolute jogak:top-[10px] jogak:right-3 jogak:z-[1] jogak:px-[9px] jogak:py-[3px] jogak:text-[11px] jogak:bg-[rgba(255,255,255,0.1)] jogak:text-[#e2e8f0] jogak:border jogak:border-[rgba(255,255,255,0.18)] jogak:rounded-[var(--jogak-radius-md)] jogak:cursor-pointer jogak:leading-none",children:t?"✓ Copied":"Copy"}),o.jsx(_.Highlight,{code:a.trim(),language:"tsx",theme:e,children:({style:g,tokens:l,getLineProps:s,getTokenProps:c})=>o.jsx("pre",{className:"jogak:m-0 jogak:py-3 jogak:px-0 jogak:text-[12.5px] jogak:leading-[1.7] jogak:font-[family-name:var(--jogak-font-mono)] jogak:h-full jogak:box-border jogak:overflow-auto",style:g,children:l.map((i,x)=>o.jsxs("div",{...s({line:i}),className:"jogak:flex jogak:pr-6",style:s({line:i}).style,children:[o.jsx("span",{className:"jogak:select-none jogak:min-w-10 jogak:pl-[14px] jogak:pr-[14px] jogak:text-right jogak:text-[rgba(148,163,184,0.45)] jogak:shrink-0 jogak:leading-[1.7]",children:x+1}),o.jsx("span",{children:i.map((v,w)=>o.jsx("span",{...c({token:v})},w))})]},x))})})]})}function P(){if(typeof window>"u")return null;const a=new URLSearchParams(window.location.search),e=a.get("entry");if(e===null)return null;const t=a.get("jogak");return{entryId:e,jogakName:t}}function ko(a,e){const t=new URLSearchParams;t.set("entry",a),t.set("jogak",e),window.history.pushState({},"",`?${t.toString()}`)}function co({entries:a,metas:e,codeTheme:t="vsDark",previewIsolation:r="shadow"}={}){const j=k.useMemo(()=>{if(a!==void 0){e!==void 0&&console.warn("[jogak] JogakApp received both `entries` and `metas` — `entries` (eager) takes precedence.");const d=new y.ComponentRegistry;for(const u of a)d.register(u);return d}if(e!==void 0)for(const d of e)y.defaultRegistry.registerMeta(d);return y.defaultRegistry},[a,e]),n=k.useMemo(()=>P(),[]),[g,l]=k.useState((n==null?void 0:n.entryId)??null),[s,c]=k.useState((n==null?void 0:n.jogakName)??null),[i,x]=k.useState({});k.useEffect(()=>{const d=()=>{const u=P();u!==null?(l(u.entryId),c(u.jogakName),x({})):(l(null),c(null))};return window.addEventListener("popstate",d),()=>{window.removeEventListener("popstate",d)}},[]);const v=k.useCallback((d,u)=>{l(d),c(u),x({}),ko(d,u)},[]),w=k.useCallback((d,u)=>{if(l(p=>p===d?d:p),c(p=>p??u),typeof window<"u"){const p=new URLSearchParams(window.location.search);p.get("entry")===d&&p.get("jogak")===null&&(p.set("jogak",u),window.history.replaceState({},"",`?${p.toString()}`))}},[]),f=k.useCallback((d,u)=>{x(p=>({...p,[d]:u}))},[]),b=k.useCallback(()=>{x({})},[]);return o.jsx(m.JogakProvider,{registry:j,children:o.jsxs("div",{"data-jogak-shell":!0,className:"jogak:grid jogak:grid-cols-[260px_1fr] jogak:h-dvh jogak:overflow-hidden",children:[o.jsx(q,{selectedEntryId:g,selectedJogakName:s,onSelect:v}),o.jsx("main",{className:"jogak:overflow-hidden jogak:min-h-0",children:g!==null?o.jsx(F,{entryId:g,jogakName:s,overrideArgs:i,onArgChange:f,onReset:b,codeTheme:t,onResolveJogak:w,previewIsolation:r}):o.jsx("div",{className:"jogak:flex jogak:items-center jogak:justify-center jogak:h-full jogak:text-[var(--jogak-color-fg-subtle)]",children:"Select a component from the sidebar"})})]})})}function io(){const a=m.useRegistry(),e=k.useMemo(()=>a.getAll(),[a]),t=k.useMemo(()=>a.getTree(),[a]),r=k.useMemo(()=>j=>a.search(j),[a]);return{entries:e,tree:t,search:r}}exports.Actions=z;exports.Controls=W;exports.JogakApp=co;exports.Preview=F;exports.Sidebar=q;exports.useRegistry=io;
|