@jogak/ui 0.1.0-alpha.4 → 0.1.0-alpha.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jogak/ui",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.6",
4
4
  "description": "Showcase viewer UI for Jogak — Sidebar / Preview / Controls / Actions and the JogakApp shell.",
5
5
  "keywords": [
6
6
  "jogak",
@@ -60,11 +60,12 @@
60
60
  "registry": "https://registry.npmjs.org/"
61
61
  },
62
62
  "dependencies": {
63
+ "clsx": "^2.1.1",
63
64
  "prism-react-renderer": "^2.4.1",
64
65
  "tailwindcss": "^4.0.0",
65
66
  "@tailwindcss/vite": "^4.0.0",
66
- "@jogak/core": "0.1.0-alpha.4",
67
- "@jogak/react": "0.1.0-alpha.4"
67
+ "@jogak/core": "0.1.0-alpha.6",
68
+ "@jogak/react": "0.1.0-alpha.6"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@types/node": "^20.14.0",
package/src/app/App.tsx CHANGED
@@ -127,19 +127,14 @@ export function JogakApp({
127
127
  <JogakProvider registry={registry}>
128
128
  <div
129
129
  data-jogak-shell
130
- style={{
131
- display: 'grid',
132
- gridTemplateColumns: '260px 1fr',
133
- height: '100dvh',
134
- overflow: 'hidden',
135
- }}
130
+ className="jogak:grid jogak:grid-cols-[260px_1fr] jogak:h-dvh jogak:overflow-hidden"
136
131
  >
137
132
  <Sidebar
138
133
  selectedEntryId={selectedEntryId}
139
134
  selectedJogakName={selectedJogakName}
140
135
  onSelect={handleSelect}
141
136
  />
142
- <main style={{ overflow: 'hidden', minHeight: 0 }}>
137
+ <main className="jogak:overflow-hidden jogak:min-h-0">
143
138
  {selectedEntryId !== null ? (
144
139
  <Preview
145
140
  entryId={selectedEntryId}
@@ -151,15 +146,7 @@ export function JogakApp({
151
146
  onResolveJogak={handleResolveJogak}
152
147
  />
153
148
  ) : (
154
- <div
155
- style={{
156
- display: 'flex',
157
- alignItems: 'center',
158
- justifyContent: 'center',
159
- height: '100%',
160
- color: '#9ca3af',
161
- }}
162
- >
149
+ <div className="jogak:flex jogak:items-center jogak:justify-center jogak:h-full jogak:text-[var(--jogak-color-fg-subtle)]">
163
150
  Select a component from the sidebar
164
151
  </div>
165
152
  )}
package/src/app/main.tsx CHANGED
@@ -3,6 +3,12 @@ import { createRoot } from 'react-dom/client'
3
3
  import 'virtual:jogak'
4
4
  import { _jogakCodeTheme } from 'virtual:jogak'
5
5
  import '../styles/jogak.css'
6
+ // 알파.6: 사용자 globalCss opt-in.
7
+ // JogakPluginOptions.globalCss=false (default) → 빈 모듈 (no-op, SPA 번들 영향 zero).
8
+ // true / string / string[] → plugin이 사용자 css를 import한다.
9
+ // jogak.css 뒤에 둬서 사용자가 jogak chrome 기본값을 명시적으로 override 가능 —
10
+ // 단, jogak utility는 prefix=jogak로 격리되어 사용자 utility와 충돌하지 않는다.
11
+ import 'virtual:jogak/global-css'
6
12
  import { JogakApp } from './App.js'
7
13
 
8
14
  const rootEl = document.getElementById('root')
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useState } from 'react'
2
2
  import type { ReactElement } from 'react'
3
+ import clsx from 'clsx'
3
4
  import { defaultActionChannel } from '@jogak/core'
4
5
  import type { ActionLog } from '@jogak/core'
5
6
 
@@ -41,75 +42,44 @@ export function Actions(): ReactElement {
41
42
  return defaultActionChannel.subscribe(setLogs)
42
43
  }, [])
43
44
 
45
+ const isEmpty = logs.length === 0
46
+
44
47
  return (
45
- <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
46
- <div
47
- style={{
48
- padding: '6px 20px',
49
- fontSize: 11,
50
- fontWeight: 700,
51
- color: '#9ca3af',
52
- textTransform: 'uppercase',
53
- letterSpacing: '0.08em',
54
- borderBottom: '1px solid #e5e7eb',
55
- background: '#f9fafb',
56
- display: 'flex',
57
- alignItems: 'center',
58
- justifyContent: 'space-between',
59
- flexShrink: 0,
60
- }}
61
- >
48
+ <div className="jogak:h-full jogak:flex jogak:flex-col">
49
+ <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">
62
50
  <span>Actions {logs.length > 0 && `(${logs.length.toString()})`}</span>
63
51
  <button
64
52
  type="button"
65
53
  onClick={() => { defaultActionChannel.clear() }}
66
- disabled={logs.length === 0}
67
- style={{
68
- fontSize: 10,
69
- fontWeight: 600,
70
- padding: '2px 8px',
71
- border: '1px solid #d1d5db',
72
- borderRadius: 3,
73
- background: '#fff',
74
- color: logs.length === 0 ? '#9ca3af' : '#374151',
75
- cursor: logs.length === 0 ? 'default' : 'pointer',
76
- textTransform: 'none',
77
- letterSpacing: 0,
78
- }}
54
+ disabled={isEmpty}
55
+ className={clsx(
56
+ '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',
57
+ isEmpty
58
+ ? 'jogak:text-[var(--jogak-color-fg-subtle)] jogak:cursor-default'
59
+ : 'jogak:text-[var(--jogak-color-fg)] jogak:cursor-pointer',
60
+ )}
79
61
  >
80
62
  Clear
81
63
  </button>
82
64
  </div>
83
65
 
84
- <div style={{ flex: 1, overflow: 'auto' }}>
85
- {logs.length === 0 ? (
86
- <div
87
- style={{
88
- padding: '12px 20px',
89
- color: '#9ca3af',
90
- fontSize: 13,
91
- }}
92
- >
66
+ <div className="jogak:flex-1 jogak:overflow-auto">
67
+ {isEmpty ? (
68
+ <div className="jogak:px-5 jogak:py-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px] jogak:leading-none">
93
69
  함수 prop이 호출되면 여기에 기록됩니다
94
70
  </div>
95
71
  ) : (
96
- <ul style={{ listStyle: 'none', margin: 0, padding: 0, fontFamily: 'monospace', fontSize: 12 }}>
72
+ <ul className="jogak:list-none jogak:m-0 jogak:p-0 jogak:font-[family-name:var(--jogak-font-mono)] jogak:text-[12px]">
97
73
  {logs.map((log) => (
98
74
  <li
99
75
  key={log.id}
100
- style={{
101
- display: 'flex',
102
- alignItems: 'baseline',
103
- gap: 10,
104
- padding: '6px 20px',
105
- borderBottom: '1px solid #f3f4f6',
106
- }}
76
+ 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)]"
107
77
  >
108
- <span style={{ color: '#9ca3af', fontSize: 11, minWidth: 92 }}>
78
+ <span className="jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[11px] jogak:min-w-[92px]">
109
79
  {formatTime(log.timestamp)}
110
80
  </span>
111
- <span style={{ color: '#7c3aed', fontWeight: 600 }}>{log.name}</span>
112
- <span style={{ color: '#374151', wordBreak: 'break-all', flex: 1 }}>
81
+ <span className="jogak:text-[var(--jogak-color-violet)] jogak:font-semibold">{log.name}</span>
82
+ <span className="jogak:text-[var(--jogak-color-fg)] jogak:break-all jogak:flex-1">
113
83
  ({formatArgs(log.args)})
114
84
  </span>
115
85
  </li>
@@ -1,4 +1,5 @@
1
- import type { ReactElement, ChangeEvent, CSSProperties } from 'react'
1
+ import type { ReactElement, ChangeEvent } from 'react'
2
+ import clsx from 'clsx'
2
3
  import type { ArgType } from '@jogak/core'
3
4
 
4
5
  export interface ControlsProps {
@@ -34,6 +35,34 @@ interface ControlInputProps {
34
35
  readonly onArgChange: (key: string, value: unknown) => void
35
36
  }
36
37
 
38
+ /**
39
+ * input/select 공용 className — §6.10 의 inputClass 상수.
40
+ *
41
+ * `<select>` 에 `appearance-none` 을 일부러 적용하지 않는다 (UA dropdown 화살표 보존, §3.5).
42
+ * alpha.4 baseline 이 UA 기본 select 화살표를 캡처한 상태라 픽셀 동등을 위해 유지.
43
+ */
44
+ const inputClass =
45
+ 'jogak:px-2 jogak:py-1 ' +
46
+ 'jogak:border jogak:border-[var(--jogak-color-border-strong)] ' +
47
+ 'jogak:rounded-[var(--jogak-radius-md)] ' +
48
+ 'jogak:text-[13px] ' +
49
+ 'jogak:w-full jogak:max-w-[280px]'
50
+
51
+ /** th className 상수 — Controls table header cell. */
52
+ const thClass =
53
+ 'jogak:px-5 jogak:py-1.5 ' +
54
+ 'jogak:text-left ' +
55
+ 'jogak:text-[var(--jogak-color-fg-muted)] ' +
56
+ 'jogak:font-medium ' +
57
+ 'jogak:text-[12px] ' +
58
+ 'jogak:border-b jogak:border-[var(--jogak-color-border)]'
59
+
60
+ /** td className 상수 — Controls table body cell. */
61
+ const tdClass =
62
+ 'jogak:px-5 jogak:py-2 ' +
63
+ 'jogak:align-middle ' +
64
+ 'jogak:border-b jogak:border-[var(--jogak-color-border-muted)]'
65
+
37
66
  function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps): ReactElement {
38
67
  const kind = resolveControlKind(value, argType)
39
68
 
@@ -46,7 +75,7 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
46
75
  onChange={(e: ChangeEvent<HTMLInputElement>) => {
47
76
  onArgChange(argKey, e.target.checked)
48
77
  }}
49
- style={{ cursor: 'pointer', width: 16, height: 16, accentColor: '#2563eb' }}
78
+ className="jogak:cursor-pointer jogak:w-4 jogak:h-4 jogak:accent-[var(--jogak-color-accent)]"
50
79
  />
51
80
  )
52
81
  case 'number':
@@ -57,7 +86,7 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
57
86
  onChange={(e: ChangeEvent<HTMLInputElement>) => {
58
87
  onArgChange(argKey, e.target.valueAsNumber)
59
88
  }}
60
- style={inputStyle}
89
+ className={inputClass}
61
90
  />
62
91
  )
63
92
  case 'select': {
@@ -68,7 +97,7 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
68
97
  onChange={(e: ChangeEvent<HTMLSelectElement>) => {
69
98
  onArgChange(argKey, e.target.value)
70
99
  }}
71
- style={inputStyle}
100
+ className={inputClass}
72
101
  >
73
102
  {options.map((opt) => (
74
103
  <option key={String(opt)} value={String(opt)}>
@@ -86,91 +115,44 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
86
115
  onChange={(e: ChangeEvent<HTMLInputElement>) => {
87
116
  onArgChange(argKey, e.target.value)
88
117
  }}
89
- style={inputStyle}
118
+ className={inputClass}
90
119
  />
91
120
  )
92
121
  case 'action':
93
122
  return (
94
- <span
95
- style={{
96
- display: 'inline-block',
97
- padding: '2px 8px',
98
- fontSize: 11,
99
- fontWeight: 600,
100
- color: '#7c3aed',
101
- background: '#f5f3ff',
102
- border: '1px solid #ddd6fe',
103
- borderRadius: 4,
104
- fontFamily: 'monospace',
105
- }}
106
- >
123
+ <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">
107
124
  (action)
108
125
  </span>
109
126
  )
110
127
  case 'json':
111
128
  return (
112
- <code style={{ fontSize: 12, color: '#6b7280', fontFamily: 'monospace' }}>
129
+ <code className="jogak:text-[12px] jogak:text-[var(--jogak-color-fg-muted)] jogak:font-[family-name:var(--jogak-font-mono)]">
113
130
  {JSON.stringify(value)}
114
131
  </code>
115
132
  )
116
133
  }
117
134
  }
118
135
 
119
- const inputStyle: CSSProperties = {
120
- padding: '4px 8px',
121
- border: '1px solid #d1d5db',
122
- borderRadius: 4,
123
- fontSize: 13,
124
- width: '100%',
125
- maxWidth: 280,
126
- }
127
-
128
- const thStyle: CSSProperties = {
129
- padding: '6px 20px',
130
- textAlign: 'left',
131
- color: '#6b7280',
132
- fontWeight: 500,
133
- fontSize: 12,
134
- borderBottom: '1px solid #e5e7eb',
135
- }
136
-
137
- const tdStyle: CSSProperties = {
138
- padding: '8px 20px',
139
- verticalAlign: 'middle',
140
- borderBottom: '1px solid #f3f4f6',
141
- }
142
-
143
136
  export function Controls({ args, argTypes, onArgChange }: ControlsProps): ReactElement {
144
137
  const keys = Array.from(new Set([...Object.keys(args), ...Object.keys(argTypes)]))
145
138
  const entries = keys.map((k) => [k, args[k]] as const)
146
139
 
147
140
  return (
148
- <div style={{ borderTop: '2px solid #e5e7eb' }}>
149
- <div
150
- style={{
151
- padding: '6px 20px',
152
- fontSize: 11,
153
- fontWeight: 700,
154
- color: '#9ca3af',
155
- textTransform: 'uppercase',
156
- letterSpacing: '0.08em',
157
- borderBottom: '1px solid #e5e7eb',
158
- background: '#f9fafb',
159
- }}
160
- >
141
+ <div className="jogak:border-t-2 jogak:border-[var(--jogak-color-border)]">
142
+ <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)]">
161
143
  Controls
162
144
  </div>
163
145
  {entries.length === 0 ? (
164
- <div style={{ padding: '12px 20px', color: '#9ca3af', fontSize: 13 }}>
146
+ <div className="jogak:px-5 jogak:py-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px]">
165
147
  No args defined
166
148
  </div>
167
149
  ) : (
168
- <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
150
+ <table className="jogak:w-full jogak:border-collapse jogak:text-[13px]">
169
151
  <thead>
170
152
  <tr>
171
- <th style={thStyle}>Name</th>
172
- <th style={thStyle}>Control</th>
173
- <th style={thStyle}>Description</th>
153
+ <th className={thClass}>Name</th>
154
+ <th className={thClass}>Control</th>
155
+ <th className={thClass}>Description</th>
174
156
  </tr>
175
157
  </thead>
176
158
  <tbody>
@@ -179,17 +161,14 @@ export function Controls({ args, argTypes, onArgChange }: ControlsProps): ReactE
179
161
  return (
180
162
  <tr key={key}>
181
163
  <td
182
- style={{
183
- ...tdStyle,
184
- fontFamily: 'monospace',
185
- fontSize: 12,
186
- color: '#374151',
187
- whiteSpace: 'nowrap',
188
- }}
164
+ className={clsx(
165
+ tdClass,
166
+ 'jogak:font-[family-name:var(--jogak-font-mono)] jogak:text-[12px] jogak:text-[var(--jogak-color-fg)] jogak:whitespace-nowrap',
167
+ )}
189
168
  >
190
169
  {key}
191
170
  </td>
192
- <td style={tdStyle}>
171
+ <td className={tdClass}>
193
172
  <ControlInput
194
173
  argKey={key}
195
174
  value={value}
@@ -197,7 +176,7 @@ export function Controls({ args, argTypes, onArgChange }: ControlsProps): ReactE
197
176
  onArgChange={onArgChange}
198
177
  />
199
178
  </td>
200
- <td style={{ ...tdStyle, color: '#9ca3af' }}>
179
+ <td className={clsx(tdClass, 'jogak:text-[var(--jogak-color-fg-subtle)]')}>
201
180
  {argType?.description ?? ''}
202
181
  </td>
203
182
  </tr>