@mandujs/core 0.19.0 → 0.20.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.
Files changed (100) hide show
  1. package/README.ko.md +0 -14
  2. package/package.json +4 -1
  3. package/src/brain/architecture/analyzer.ts +4 -4
  4. package/src/brain/doctor/analyzer.ts +18 -14
  5. package/src/bundler/build.test.ts +127 -0
  6. package/src/bundler/build.ts +385 -115
  7. package/src/bundler/css.ts +20 -5
  8. package/src/bundler/dev.ts +55 -2
  9. package/src/bundler/prerender.ts +195 -0
  10. package/src/bundler/types.ts +20 -0
  11. package/src/change/snapshot.ts +4 -23
  12. package/src/change/types.ts +2 -3
  13. package/src/client/Form.tsx +105 -0
  14. package/src/client/__tests__/use-sse.test.ts +153 -0
  15. package/src/client/hooks.ts +105 -6
  16. package/src/client/index.ts +35 -6
  17. package/src/client/router.ts +670 -433
  18. package/src/client/rpc.ts +140 -0
  19. package/src/client/runtime.ts +24 -21
  20. package/src/client/use-fetch.ts +239 -0
  21. package/src/client/use-head.ts +197 -0
  22. package/src/client/use-sse.ts +378 -0
  23. package/src/components/Image.tsx +162 -0
  24. package/src/config/mandu.ts +5 -0
  25. package/src/config/validate.ts +34 -0
  26. package/src/content/index.ts +5 -1
  27. package/src/devtools/client/catchers/error-catcher.ts +17 -0
  28. package/src/devtools/client/catchers/network-proxy.ts +390 -367
  29. package/src/devtools/client/components/kitchen-root.tsx +479 -467
  30. package/src/devtools/client/components/mandu-character.tsx +77 -53
  31. package/src/devtools/client/components/panel/diff-viewer.tsx +219 -0
  32. package/src/devtools/client/components/panel/errors-panel.tsx +2 -2
  33. package/src/devtools/client/components/panel/guard-panel.tsx +363 -234
  34. package/src/devtools/client/components/panel/index.ts +45 -32
  35. package/src/devtools/client/components/panel/islands-panel.tsx +30 -14
  36. package/src/devtools/client/components/panel/network-panel.tsx +2 -3
  37. package/src/devtools/client/components/panel/panel-container.tsx +273 -100
  38. package/src/devtools/client/components/panel/preview-panel.tsx +212 -0
  39. package/src/devtools/client/state-manager.ts +535 -478
  40. package/src/devtools/design-tokens.ts +265 -264
  41. package/src/devtools/init.ts +1 -1
  42. package/src/devtools/types.ts +321 -295
  43. package/src/filling/filling.ts +328 -6
  44. package/src/filling/index.ts +5 -1
  45. package/src/filling/session.ts +216 -0
  46. package/src/filling/ws.ts +78 -0
  47. package/src/generator/generate.ts +2 -2
  48. package/src/guard/auto-correct.ts +0 -29
  49. package/src/guard/check.ts +14 -31
  50. package/src/guard/presets/index.ts +296 -294
  51. package/src/guard/rules.ts +15 -19
  52. package/src/guard/validator.ts +834 -834
  53. package/src/index.ts +6 -1
  54. package/src/island/index.ts +373 -304
  55. package/src/kitchen/api/contract-api.ts +225 -0
  56. package/src/kitchen/api/diff-parser.ts +108 -0
  57. package/src/kitchen/api/file-api.ts +273 -0
  58. package/src/kitchen/api/guard-api.ts +83 -0
  59. package/src/kitchen/api/guard-decisions.ts +100 -0
  60. package/src/kitchen/api/routes-api.ts +50 -0
  61. package/src/kitchen/index.ts +21 -0
  62. package/src/kitchen/kitchen-handler.ts +335 -0
  63. package/src/kitchen/kitchen-ui.ts +1732 -0
  64. package/src/kitchen/stream/activity-sse.ts +145 -0
  65. package/src/kitchen/stream/file-tailer.ts +99 -0
  66. package/src/middleware/compress.ts +62 -0
  67. package/src/middleware/cors.ts +47 -0
  68. package/src/middleware/index.ts +10 -0
  69. package/src/middleware/jwt.ts +134 -0
  70. package/src/middleware/logger.ts +58 -0
  71. package/src/middleware/timeout.ts +55 -0
  72. package/src/observability/event-bus.ts +79 -0
  73. package/src/observability/index.ts +8 -0
  74. package/src/observability/logger-adapter.ts +36 -0
  75. package/src/paths.ts +0 -4
  76. package/src/plugins/hooks.ts +64 -0
  77. package/src/plugins/index.ts +3 -0
  78. package/src/plugins/types.ts +5 -0
  79. package/src/report/build.ts +0 -6
  80. package/src/resource/__tests__/backward-compat.test.ts +0 -1
  81. package/src/router/fs-patterns.ts +11 -1
  82. package/src/router/fs-routes.ts +78 -14
  83. package/src/router/fs-scanner.ts +2 -2
  84. package/src/router/fs-types.ts +2 -1
  85. package/src/runtime/adapter-bun.ts +62 -0
  86. package/src/runtime/adapter.ts +47 -0
  87. package/src/runtime/cache.ts +310 -0
  88. package/src/runtime/handler.ts +65 -0
  89. package/src/runtime/image-handler.ts +195 -0
  90. package/src/runtime/index.ts +13 -0
  91. package/src/runtime/middleware.ts +263 -0
  92. package/src/runtime/ppr.ts +74 -0
  93. package/src/runtime/server.ts +706 -71
  94. package/src/runtime/ssr.ts +70 -31
  95. package/src/runtime/streaming-ssr.ts +121 -78
  96. package/src/spec/index.ts +0 -1
  97. package/src/spec/schema.ts +1 -0
  98. package/src/testing/index.ts +189 -0
  99. package/src/watcher/watcher.ts +27 -1
  100. package/src/spec/lock.ts +0 -56
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Mandu Kitchen DevTools - Preview Panel
3
+ * @version 2.0.0
4
+ *
5
+ * Shows recent file changes with inline diff viewer.
6
+ */
7
+
8
+ import React, { useState, useCallback } from 'react';
9
+ import type { RecentChange } from '../../../types';
10
+ import { DiffViewer, type FileDiff } from './diff-viewer';
11
+ import { colors, typography, spacing, borderRadius, animation } from '../../../design-tokens';
12
+
13
+ // ============================================================================
14
+ // Styles
15
+ // ============================================================================
16
+
17
+ const styles = {
18
+ container: {
19
+ padding: spacing.md,
20
+ height: '100%',
21
+ display: 'flex',
22
+ flexDirection: 'column' as const,
23
+ gap: spacing.md,
24
+ },
25
+ header: {
26
+ display: 'flex',
27
+ alignItems: 'center',
28
+ justifyContent: 'space-between',
29
+ },
30
+ clearButton: {
31
+ padding: `${spacing.xs} ${spacing.sm}`,
32
+ borderRadius: borderRadius.sm,
33
+ backgroundColor: colors.background.light,
34
+ border: 'none',
35
+ color: colors.text.secondary,
36
+ fontSize: typography.fontSize.xs,
37
+ cursor: 'pointer',
38
+ transition: `all ${animation.duration.fast}`,
39
+ },
40
+ list: {
41
+ flex: 1,
42
+ overflow: 'auto',
43
+ display: 'flex',
44
+ flexDirection: 'column' as const,
45
+ gap: spacing.xs,
46
+ },
47
+ emptyState: {
48
+ flex: 1,
49
+ display: 'flex',
50
+ flexDirection: 'column' as const,
51
+ alignItems: 'center',
52
+ justifyContent: 'center',
53
+ gap: spacing.md,
54
+ padding: spacing.xl,
55
+ color: colors.text.muted,
56
+ fontSize: typography.fontSize.sm,
57
+ textAlign: 'center' as const,
58
+ },
59
+ changeItem: {
60
+ display: 'flex',
61
+ alignItems: 'center',
62
+ gap: spacing.sm,
63
+ padding: `${spacing.sm} ${spacing.md}`,
64
+ borderRadius: borderRadius.sm,
65
+ backgroundColor: colors.background.medium,
66
+ cursor: 'pointer',
67
+ transition: `all ${animation.duration.fast}`,
68
+ fontSize: typography.fontSize.sm,
69
+ },
70
+ changeIcon: {
71
+ flexShrink: 0,
72
+ display: 'inline-flex',
73
+ alignItems: 'center',
74
+ justifyContent: 'center',
75
+ minWidth: '42px',
76
+ height: '24px',
77
+ padding: '0 8px',
78
+ borderRadius: borderRadius.full,
79
+ fontSize: typography.fontSize.xs,
80
+ fontWeight: typography.fontWeight.semibold,
81
+ fontFamily: typography.fontFamily.mono,
82
+ letterSpacing: '0.08em',
83
+ },
84
+ changePath: {
85
+ flex: 1,
86
+ overflow: 'hidden',
87
+ textOverflow: 'ellipsis',
88
+ whiteSpace: 'nowrap' as const,
89
+ fontFamily: typography.fontFamily.mono,
90
+ fontSize: typography.fontSize.xs,
91
+ color: colors.text.primary,
92
+ },
93
+ changeTime: {
94
+ flexShrink: 0,
95
+ fontSize: typography.fontSize.xs,
96
+ color: colors.text.muted,
97
+ },
98
+ };
99
+
100
+ const typeIcons: Record<string, string> = {
101
+ add: 'ADD',
102
+ change: 'EDIT',
103
+ delete: 'DEL',
104
+ };
105
+
106
+ const typeStyles: Record<string, { bg: string; color: string }> = {
107
+ add: { bg: `${colors.semantic.success}20`, color: colors.semantic.success },
108
+ change: { bg: `${colors.semantic.info}20`, color: colors.semantic.info },
109
+ delete: { bg: `${colors.semantic.error}20`, color: colors.semantic.error },
110
+ };
111
+
112
+ // ============================================================================
113
+ // Props
114
+ // ============================================================================
115
+
116
+ export interface PreviewPanelProps {
117
+ recentChanges: RecentChange[];
118
+ onClearChanges?: () => void;
119
+ }
120
+
121
+ // ============================================================================
122
+ // Component
123
+ // ============================================================================
124
+
125
+ export function PreviewPanel({ recentChanges, onClearChanges }: PreviewPanelProps): React.ReactElement {
126
+ const [selectedDiff, setSelectedDiff] = useState<FileDiff | null>(null);
127
+ const [loadingDiff, setLoadingDiff] = useState<string | null>(null);
128
+
129
+ const handleFileClick = useCallback(async (filePath: string) => {
130
+ setLoadingDiff(filePath);
131
+ try {
132
+ const res = await fetch(`/__kitchen/api/file/diff?path=${encodeURIComponent(filePath)}`);
133
+ if (res.ok) {
134
+ const diff = await res.json();
135
+ setSelectedDiff(diff);
136
+ }
137
+ } catch {
138
+ // API not available
139
+ } finally {
140
+ setLoadingDiff(null);
141
+ }
142
+ }, []);
143
+
144
+ const handleCloseDiff = useCallback(() => {
145
+ setSelectedDiff(null);
146
+ }, []);
147
+
148
+ if (selectedDiff) {
149
+ return <DiffViewer diff={selectedDiff} onClose={handleCloseDiff} />;
150
+ }
151
+
152
+ if (recentChanges.length === 0) {
153
+ return (
154
+ <div style={styles.container}>
155
+ <div style={styles.emptyState}>
156
+ <p>파일을 수정하면 여기에 최근 변경사항이 표시됩니다.</p>
157
+ </div>
158
+ </div>
159
+ );
160
+ }
161
+
162
+ return (
163
+ <div style={styles.container}>
164
+ <div style={styles.header}>
165
+ <span style={{ fontSize: typography.fontSize.sm, color: colors.text.secondary }}>
166
+ {recentChanges.length}개의 변경사항
167
+ </span>
168
+ {onClearChanges && (
169
+ <button style={styles.clearButton} onClick={onClearChanges}>
170
+ 모두 지우기
171
+ </button>
172
+ )}
173
+ </div>
174
+
175
+ <div style={styles.list}>
176
+ {recentChanges.map((change, i) => {
177
+ const isLoading = loadingDiff === change.filePath;
178
+ const changeStyle = typeStyles[change.type] ?? typeStyles.change;
179
+ return (
180
+ <div
181
+ key={`${change.filePath}-${change.timestamp}-${i}`}
182
+ style={{
183
+ ...styles.changeItem,
184
+ opacity: isLoading ? 0.5 : 1,
185
+ }}
186
+ onClick={() => !isLoading && handleFileClick(change.filePath)}
187
+ >
188
+ <span
189
+ style={{
190
+ ...styles.changeIcon,
191
+ backgroundColor: changeStyle.bg,
192
+ color: changeStyle.color,
193
+ }}
194
+ >
195
+ {typeIcons[change.type] ?? 'EDIT'}
196
+ </span>
197
+ <span style={styles.changePath}>{change.filePath}</span>
198
+ <span style={styles.changeTime}>
199
+ {formatTime(change.timestamp)}
200
+ </span>
201
+ </div>
202
+ );
203
+ })}
204
+ </div>
205
+ </div>
206
+ );
207
+ }
208
+
209
+ function formatTime(ts: number): string {
210
+ const d = new Date(ts);
211
+ return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
212
+ }