sanjang 0.3.3 → 0.3.5
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/README.md +25 -13
- package/dashboard/app.js +1026 -147
- package/dashboard/index.html +23 -33
- package/dashboard/style.css +126 -295
- package/dist/bin/sanjang.js +48 -4
- package/dist/lib/config.js +3 -5
- package/dist/lib/engine/change-report.d.ts +27 -0
- package/dist/lib/engine/change-report.js +233 -0
- package/dist/lib/engine/diagnostics.js +2 -6
- package/dist/lib/engine/main-server.d.ts +15 -0
- package/dist/lib/engine/main-server.js +111 -0
- package/dist/lib/engine/naming.js +11 -2
- package/dist/lib/engine/pr.js +1 -1
- package/dist/lib/engine/process.js +4 -1
- package/dist/lib/engine/self-heal.js +16 -5
- package/dist/lib/engine/smart-init.js +7 -6
- package/dist/lib/engine/state.js +1 -1
- package/dist/lib/engine/suggest.js +1 -4
- package/dist/lib/engine/warp.d.ts +1 -1
- package/dist/lib/engine/warp.js +1 -1
- package/dist/lib/server.js +241 -49
- package/dist/lib/types.d.ts +19 -0
- package/package.json +2 -2
package/dashboard/index.html
CHANGED
|
@@ -19,38 +19,14 @@
|
|
|
19
19
|
<div id="portal">
|
|
20
20
|
<!-- 베이스캠프 씬 -->
|
|
21
21
|
<div class="portal-section" id="basecamp-scene">
|
|
22
|
-
<div class="bc-scene">
|
|
23
|
-
<!--
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<div class="bc-
|
|
28
|
-
<div class="bc-star twinkle" style="top:10%;left:80%"></div>
|
|
29
|
-
<div class="bc-star twinkle-slow" style="top:25%;left:92%"></div>
|
|
30
|
-
<div class="bc-star twinkle" style="top:18%;left:5%"></div>
|
|
31
|
-
<div class="bc-star twinkle-slow" style="top:6%;left:72%"></div>
|
|
32
|
-
<div class="bc-star twinkle" style="top:22%;left:42%"></div>
|
|
33
|
-
<div class="bc-star twinkle-slow" style="top:12%;left:55%"></div>
|
|
34
|
-
|
|
35
|
-
<!-- Mountains -->
|
|
36
|
-
<div class="bc-mountain bc-mountain-back"></div>
|
|
37
|
-
<div class="bc-mountain bc-mountain-front"></div>
|
|
38
|
-
|
|
39
|
-
<!-- Snow caps -->
|
|
40
|
-
<div class="bc-snow" style="bottom:64px;left:48%;width:12px"></div>
|
|
41
|
-
<div class="bc-snow" style="bottom:60px;left:50%;width:8px"></div>
|
|
42
|
-
<div class="bc-snow" style="bottom:56px;left:46%;width:4px"></div>
|
|
43
|
-
|
|
44
|
-
<!-- Campfire glow + campfire -->
|
|
45
|
-
<div class="bc-glow"></div>
|
|
46
|
-
<div class="bc-campfire"></div>
|
|
47
|
-
|
|
48
|
-
<!-- Sherpa -->
|
|
49
|
-
<div class="bc-sherpa"></div>
|
|
50
|
-
|
|
51
|
-
<!-- Speech bubble -->
|
|
22
|
+
<div class="bc-scene" id="bc-scene-container">
|
|
23
|
+
<!-- JS renders time-based SVG scene here -->
|
|
24
|
+
</div>
|
|
25
|
+
<!-- Speech bubble stays outside SVG for text rendering -->
|
|
26
|
+
<div class="bc-speech-wrap">
|
|
27
|
+
<div class="bc-sherpa-hitbox" onclick="toggleSherpaMode()" title="클릭해서 모드 변경"></div>
|
|
52
28
|
<div class="bc-speech fade-in" id="sherpa-speech">
|
|
53
|
-
<span id="sherpa-quote"
|
|
29
|
+
<span id="sherpa-quote"></span>
|
|
54
30
|
</div>
|
|
55
31
|
</div>
|
|
56
32
|
</div>
|
|
@@ -101,11 +77,17 @@
|
|
|
101
77
|
</div>
|
|
102
78
|
<div style="flex:1"></div>
|
|
103
79
|
<button class="btn btn-sub btn-sm" id="ws-terminal-btn" onclick="wsOpenTerminal()">💻 터미널</button>
|
|
80
|
+
<button class="btn btn-sub btn-sm" id="ws-compare-btn" onclick="toggleCompare()" title="원본과 비교">🔀 비교</button>
|
|
104
81
|
<button class="btn btn-sub btn-sm" onclick="togglePanel()">⛰ 패널</button>
|
|
105
82
|
</div>
|
|
106
83
|
|
|
107
|
-
<!-- Preview — full screen -->
|
|
108
|
-
<div class="ws-preview-
|
|
84
|
+
<!-- Preview — full screen with optional split -->
|
|
85
|
+
<div class="ws-preview-container" id="ws-preview-container">
|
|
86
|
+
<div class="ws-preview-full" id="ws-preview"></div>
|
|
87
|
+
<div class="ws-preview-main hidden" id="ws-preview-main">
|
|
88
|
+
<div class="ws-preview-label">🏔️ 원본 (main)</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
109
91
|
|
|
110
92
|
<!-- Slide panel — right side -->
|
|
111
93
|
<div class="ws-panel" id="ws-panel">
|
|
@@ -124,6 +106,12 @@
|
|
|
124
106
|
<div id="ws-changes"></div>
|
|
125
107
|
</details>
|
|
126
108
|
</div>
|
|
109
|
+
<div class="workspace-section ws-report-section" id="ws-report-section" style="display:none">
|
|
110
|
+
<h3>📋 변경 리포트</h3>
|
|
111
|
+
<div class="ws-report-summary" id="ws-report-summary"></div>
|
|
112
|
+
<div class="ws-report-warnings" id="ws-report-warnings"></div>
|
|
113
|
+
<div class="ws-report-categories" id="ws-report-categories"></div>
|
|
114
|
+
</div>
|
|
127
115
|
<div class="workspace-section">
|
|
128
116
|
<h3>📜 세이브 기록</h3>
|
|
129
117
|
<div id="ws-actions"></div>
|
|
@@ -133,6 +121,7 @@
|
|
|
133
121
|
<div class="ws-browser-error-panel" id="ws-browser-errors">
|
|
134
122
|
<span style="color:var(--text-muted);font-size:12px">에러 없음</span>
|
|
135
123
|
</div>
|
|
124
|
+
<button class="btn btn-fix" id="ws-fix-btn" onclick="copyFixPrompt()" style="display:none">🩹 고쳐줘 — 클립보드 복사</button>
|
|
136
125
|
</details>
|
|
137
126
|
<details class="workspace-section ws-log-details">
|
|
138
127
|
<summary>📜 로그</summary>
|
|
@@ -243,6 +232,7 @@
|
|
|
243
232
|
<p id="ship-file-count" style="font-size:13px;color:var(--text-muted);margin-bottom:12px"></p>
|
|
244
233
|
|
|
245
234
|
<div class="form-group">
|
|
235
|
+
<div id="ship-report-preview" class="ship-report-preview" style="display:none"></div>
|
|
246
236
|
<label class="form-label" for="ship-message">뭘 바꿨나요? (한 줄로)</label>
|
|
247
237
|
<input
|
|
248
238
|
class="form-input"
|
package/dashboard/style.css
CHANGED
|
@@ -1090,6 +1090,23 @@ header h1::before {
|
|
|
1090
1090
|
word-break: break-all;
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
+
.btn-fix {
|
|
1094
|
+
display: block;
|
|
1095
|
+
width: 100%;
|
|
1096
|
+
margin-top: 8px;
|
|
1097
|
+
padding: 8px 12px;
|
|
1098
|
+
background: linear-gradient(135deg, #ef4444 0%, #f97316 100%);
|
|
1099
|
+
color: #fff;
|
|
1100
|
+
border: none;
|
|
1101
|
+
border-radius: 6px;
|
|
1102
|
+
font-size: 13px;
|
|
1103
|
+
font-weight: 600;
|
|
1104
|
+
cursor: pointer;
|
|
1105
|
+
transition: opacity 0.15s, transform 0.1s;
|
|
1106
|
+
}
|
|
1107
|
+
.btn-fix:hover { opacity: 0.9; transform: translateY(-1px); }
|
|
1108
|
+
.btn-fix:active { transform: translateY(0); }
|
|
1109
|
+
|
|
1093
1110
|
.ws-error-badge {
|
|
1094
1111
|
background: #ef4444;
|
|
1095
1112
|
color: white;
|
|
@@ -1707,11 +1724,8 @@ header.hidden {
|
|
|
1707
1724
|
margin-top: 8px;
|
|
1708
1725
|
}
|
|
1709
1726
|
.ws-commit-item {
|
|
1710
|
-
display:
|
|
1711
|
-
|
|
1712
|
-
align-items: baseline;
|
|
1713
|
-
gap: 8px;
|
|
1714
|
-
padding: 4px 0;
|
|
1727
|
+
display: block;
|
|
1728
|
+
padding: 0;
|
|
1715
1729
|
font-size: 13px;
|
|
1716
1730
|
border-bottom: 1px solid var(--border);
|
|
1717
1731
|
}
|
|
@@ -1720,6 +1734,10 @@ header.hidden {
|
|
|
1720
1734
|
}
|
|
1721
1735
|
.ws-commit-msg {
|
|
1722
1736
|
color: var(--text-primary);
|
|
1737
|
+
white-space: nowrap;
|
|
1738
|
+
overflow: hidden;
|
|
1739
|
+
text-overflow: ellipsis;
|
|
1740
|
+
min-width: 0;
|
|
1723
1741
|
}
|
|
1724
1742
|
.ws-commit-date {
|
|
1725
1743
|
color: var(--text-muted);
|
|
@@ -2046,331 +2064,73 @@ header.hidden {
|
|
|
2046
2064
|
Onboarding Tutorial
|
|
2047
2065
|
============================================================ */
|
|
2048
2066
|
|
|
2049
|
-
.onboarding-overlay {
|
|
2050
|
-
position: fixed;
|
|
2051
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
2052
|
-
background: rgba(0, 0, 0, 0.6);
|
|
2053
|
-
z-index: 10000;
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
.onboarding-highlight {
|
|
2057
|
-
position: fixed;
|
|
2058
|
-
border: 2px solid var(--accent, #6366f1);
|
|
2059
|
-
border-radius: 8px;
|
|
2060
|
-
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5), 0 0 20px rgba(99, 102, 241, 0.4);
|
|
2061
|
-
z-index: 10001;
|
|
2062
|
-
pointer-events: none;
|
|
2063
|
-
animation: onboard-pulse 1.5s ease-in-out infinite;
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
@keyframes onboard-pulse {
|
|
2067
|
-
0%, 100% { box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5), 0 0 20px rgba(99, 102, 241, 0.3); }
|
|
2068
|
-
50% { box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5), 0 0 30px rgba(99, 102, 241, 0.6); }
|
|
2069
|
-
}
|
|
2070
|
-
|
|
2071
|
-
.onboarding-tooltip {
|
|
2072
|
-
position: fixed;
|
|
2073
|
-
background: var(--bg-elevated, #1e1e2e);
|
|
2074
|
-
border: 1px solid var(--border, #333);
|
|
2075
|
-
border-radius: 12px;
|
|
2076
|
-
padding: 16px 20px;
|
|
2077
|
-
max-width: 320px;
|
|
2078
|
-
z-index: 10002;
|
|
2079
|
-
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
|
|
2080
|
-
animation: onboard-fade-in 0.3s ease-out;
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
@keyframes onboard-fade-in {
|
|
2084
|
-
from { opacity: 0; transform: translateY(8px); }
|
|
2085
|
-
to { opacity: 1; transform: translateY(0); }
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
.onboarding-title {
|
|
2089
|
-
font-size: 15px;
|
|
2090
|
-
font-weight: 700;
|
|
2091
|
-
color: var(--text-primary, #fff);
|
|
2092
|
-
margin-bottom: 6px;
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
.onboarding-text {
|
|
2096
|
-
font-size: 13px;
|
|
2097
|
-
color: var(--text-secondary, #aaa);
|
|
2098
|
-
line-height: 1.5;
|
|
2099
|
-
margin-bottom: 12px;
|
|
2100
|
-
}
|
|
2101
|
-
|
|
2102
|
-
.onboarding-actions {
|
|
2103
|
-
display: flex;
|
|
2104
|
-
align-items: center;
|
|
2105
|
-
gap: 8px;
|
|
2106
|
-
}
|
|
2107
|
-
|
|
2108
|
-
.onboarding-step {
|
|
2109
|
-
font-size: 11px;
|
|
2110
|
-
color: var(--text-muted, #666);
|
|
2111
|
-
flex: 1;
|
|
2112
|
-
}
|
|
2113
2067
|
|
|
2114
2068
|
/* ============================================================
|
|
2115
|
-
Basecamp Scene
|
|
2069
|
+
Basecamp Scene — Himalaya
|
|
2116
2070
|
============================================================ */
|
|
2117
2071
|
|
|
2118
2072
|
.bc-scene {
|
|
2119
2073
|
position: relative;
|
|
2120
|
-
height:
|
|
2121
|
-
background: linear-gradient(180deg, #080a10 0%, #0c0e14 60%, #12151e 100%);
|
|
2074
|
+
height: 220px;
|
|
2122
2075
|
border-radius: 12px;
|
|
2123
2076
|
overflow: hidden;
|
|
2124
2077
|
border: 1px solid #1c2030;
|
|
2125
2078
|
}
|
|
2126
2079
|
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
height: 4px;
|
|
2132
|
-
background: #fff;
|
|
2133
|
-
image-rendering: pixelated;
|
|
2134
|
-
}
|
|
2135
|
-
|
|
2136
|
-
.bc-star.twinkle {
|
|
2137
|
-
animation: twinkle 2s steps(2) infinite;
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2140
|
-
.bc-star.twinkle-slow {
|
|
2141
|
-
animation: twinkle 3.5s steps(2) infinite 0.8s;
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
@keyframes twinkle {
|
|
2145
|
-
0%, 100% { opacity: 0.6; }
|
|
2146
|
-
50% { opacity: 0.1; }
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
/* Mountains */
|
|
2150
|
-
.bc-mountain {
|
|
2151
|
-
position: absolute;
|
|
2152
|
-
bottom: 0;
|
|
2153
|
-
left: 0;
|
|
2154
|
-
right: 0;
|
|
2155
|
-
image-rendering: pixelated;
|
|
2156
|
-
}
|
|
2157
|
-
|
|
2158
|
-
.bc-mountain-back {
|
|
2159
|
-
height: 100px;
|
|
2160
|
-
background: #1a1d2a;
|
|
2161
|
-
clip-path: polygon(
|
|
2162
|
-
0% 100%, 0% 70%, 5% 70%, 5% 60%, 10% 60%, 10% 50%, 15% 50%, 15% 40%,
|
|
2163
|
-
20% 40%, 20% 35%, 25% 35%, 25% 45%, 30% 45%, 30% 55%, 35% 55%, 35% 50%,
|
|
2164
|
-
40% 50%, 40% 35%, 45% 35%, 45% 25%, 50% 25%, 50% 20%, 55% 20%, 55% 30%,
|
|
2165
|
-
60% 30%, 60% 40%, 65% 40%, 65% 50%, 70% 50%, 70% 40%, 75% 40%, 75% 50%,
|
|
2166
|
-
80% 50%, 80% 60%, 85% 60%, 85% 70%, 90% 70%, 90% 80%, 95% 80%, 100% 80%,
|
|
2167
|
-
100% 100%
|
|
2168
|
-
);
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
.bc-mountain-front {
|
|
2172
|
-
height: 80px;
|
|
2173
|
-
background: #12151e;
|
|
2174
|
-
clip-path: polygon(
|
|
2175
|
-
0% 100%, 0% 80%, 5% 80%, 5% 70%, 10% 70%, 10% 55%, 15% 55%, 15% 45%,
|
|
2176
|
-
20% 45%, 20% 40%, 25% 40%, 25% 50%, 30% 50%, 30% 60%, 35% 60%, 35% 55%,
|
|
2177
|
-
40% 55%, 40% 40%, 45% 40%, 45% 30%, 48% 30%, 48% 25%, 52% 25%, 52% 20%,
|
|
2178
|
-
55% 20%, 55% 30%, 58% 30%, 58% 40%, 62% 40%, 62% 50%, 65% 50%, 65% 45%,
|
|
2179
|
-
70% 45%, 70% 35%, 75% 35%, 75% 45%, 80% 45%, 80% 55%, 85% 55%, 85% 65%,
|
|
2180
|
-
90% 65%, 90% 75%, 95% 75%, 95% 85%, 100% 85%, 100% 100%
|
|
2181
|
-
);
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
/* Snow caps */
|
|
2185
|
-
.bc-snow {
|
|
2186
|
-
position: absolute;
|
|
2187
|
-
height: 4px;
|
|
2188
|
-
background: rgba(255, 255, 255, 0.25);
|
|
2189
|
-
image-rendering: pixelated;
|
|
2080
|
+
.bc-scene svg {
|
|
2081
|
+
display: block;
|
|
2082
|
+
width: 100%;
|
|
2083
|
+
height: 100%;
|
|
2190
2084
|
}
|
|
2191
2085
|
|
|
2192
|
-
/*
|
|
2193
|
-
.bc-
|
|
2086
|
+
/* Speech bubble positioning */
|
|
2087
|
+
.bc-speech-wrap {
|
|
2194
2088
|
position: absolute;
|
|
2195
|
-
bottom:
|
|
2196
|
-
left: 50
|
|
2197
|
-
|
|
2198
|
-
width: 80px;
|
|
2199
|
-
height: 40px;
|
|
2200
|
-
background: radial-gradient(ellipse at center bottom, rgba(255, 140, 50, 0.15) 0%, transparent 70%);
|
|
2201
|
-
animation: glow-pulse 2s ease infinite;
|
|
2202
|
-
}
|
|
2203
|
-
|
|
2204
|
-
@keyframes glow-pulse {
|
|
2205
|
-
0%, 100% { opacity: 1; }
|
|
2206
|
-
50% { opacity: 0.6; }
|
|
2089
|
+
bottom: 56px;
|
|
2090
|
+
left: calc(50% + 16px);
|
|
2091
|
+
z-index: 5;
|
|
2207
2092
|
}
|
|
2208
2093
|
|
|
2209
|
-
|
|
2210
|
-
.bc-campfire {
|
|
2094
|
+
.bc-sherpa-hitbox {
|
|
2211
2095
|
position: absolute;
|
|
2212
|
-
bottom:
|
|
2213
|
-
left:
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
image-rendering: pixelated;
|
|
2219
|
-
box-shadow:
|
|
2220
|
-
-4px 0 0 #8b4513,
|
|
2221
|
-
4px 0 0 #8b4513,
|
|
2222
|
-
0 -4px 0 #ff6600,
|
|
2223
|
-
-4px -4px 0 #ff8800,
|
|
2224
|
-
4px -4px 0 #ff8800,
|
|
2225
|
-
0 -8px 0 #ffaa00,
|
|
2226
|
-
-4px -8px 0 #ff6600,
|
|
2227
|
-
4px -8px 0 #ff6600,
|
|
2228
|
-
0 -12px 0 #ffcc00;
|
|
2229
|
-
animation: bc-fire 0.4s steps(1) infinite;
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
@keyframes bc-fire {
|
|
2233
|
-
0% {
|
|
2234
|
-
box-shadow:
|
|
2235
|
-
-4px 0 0 #8b4513,
|
|
2236
|
-
4px 0 0 #8b4513,
|
|
2237
|
-
0 -4px 0 #ff6600,
|
|
2238
|
-
-4px -4px 0 #ff8800,
|
|
2239
|
-
4px -4px 0 #ff8800,
|
|
2240
|
-
0 -8px 0 #ffaa00,
|
|
2241
|
-
-4px -8px 0 #ff6600,
|
|
2242
|
-
4px -8px 0 #ff6600,
|
|
2243
|
-
0 -12px 0 #ffcc00;
|
|
2244
|
-
}
|
|
2245
|
-
50% {
|
|
2246
|
-
box-shadow:
|
|
2247
|
-
-4px 0 0 #8b4513,
|
|
2248
|
-
4px 0 0 #8b4513,
|
|
2249
|
-
0 -4px 0 #ff8800,
|
|
2250
|
-
-4px -4px 0 #ff6600,
|
|
2251
|
-
4px -4px 0 #ffaa00,
|
|
2252
|
-
0 -8px 0 #ff6600,
|
|
2253
|
-
-4px -8px 0 #ffcc00,
|
|
2254
|
-
4px -8px 0 #ff8800,
|
|
2255
|
-
0 -12px 0 #ff6600;
|
|
2256
|
-
}
|
|
2096
|
+
bottom: -44px;
|
|
2097
|
+
left: -8px;
|
|
2098
|
+
width: 44px;
|
|
2099
|
+
height: 44px;
|
|
2100
|
+
cursor: pointer;
|
|
2101
|
+
z-index: 6;
|
|
2257
2102
|
}
|
|
2258
2103
|
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
position: absolute;
|
|
2262
|
-
bottom: 16px;
|
|
2263
|
-
left: calc(50% + 24px);
|
|
2264
|
-
width: 4px;
|
|
2265
|
-
height: 4px;
|
|
2266
|
-
background: transparent;
|
|
2267
|
-
image-rendering: pixelated;
|
|
2268
|
-
box-shadow:
|
|
2269
|
-
/* Hat */
|
|
2270
|
-
0 -20px 0 #e74c3c,
|
|
2271
|
-
-4px -20px 0 #e74c3c,
|
|
2272
|
-
4px -20px 0 #e74c3c,
|
|
2273
|
-
-4px -24px 0 #e74c3c,
|
|
2274
|
-
0 -24px 0 #e74c3c,
|
|
2275
|
-
4px -24px 0 #e74c3c,
|
|
2276
|
-
/* Face */
|
|
2277
|
-
-4px -16px 0 #f5c6a0,
|
|
2278
|
-
0 -16px 0 #f5c6a0,
|
|
2279
|
-
4px -16px 0 #f5c6a0,
|
|
2280
|
-
/* Body */
|
|
2281
|
-
-4px -12px 0 #3498db,
|
|
2282
|
-
0 -12px 0 #3498db,
|
|
2283
|
-
4px -12px 0 #3498db,
|
|
2284
|
-
-4px -8px 0 #3498db,
|
|
2285
|
-
0 -8px 0 #3498db,
|
|
2286
|
-
4px -8px 0 #3498db,
|
|
2287
|
-
/* Backpack */
|
|
2288
|
-
8px -12px 0 #8b6914,
|
|
2289
|
-
8px -8px 0 #8b6914,
|
|
2290
|
-
/* Legs */
|
|
2291
|
-
-4px -4px 0 #2c3e50,
|
|
2292
|
-
0 -4px 0 #2c3e50,
|
|
2293
|
-
-4px 0 0 #2c3e50,
|
|
2294
|
-
4px 0 0 #2c3e50;
|
|
2295
|
-
animation: sherpa-idle 1s steps(1) infinite;
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
|
-
@keyframes sherpa-idle {
|
|
2299
|
-
0%, 100% {
|
|
2300
|
-
box-shadow:
|
|
2301
|
-
0 -20px 0 #e74c3c,
|
|
2302
|
-
-4px -20px 0 #e74c3c,
|
|
2303
|
-
4px -20px 0 #e74c3c,
|
|
2304
|
-
-4px -24px 0 #e74c3c,
|
|
2305
|
-
0 -24px 0 #e74c3c,
|
|
2306
|
-
4px -24px 0 #e74c3c,
|
|
2307
|
-
-4px -16px 0 #f5c6a0,
|
|
2308
|
-
0 -16px 0 #f5c6a0,
|
|
2309
|
-
4px -16px 0 #f5c6a0,
|
|
2310
|
-
-4px -12px 0 #3498db,
|
|
2311
|
-
0 -12px 0 #3498db,
|
|
2312
|
-
4px -12px 0 #3498db,
|
|
2313
|
-
-4px -8px 0 #3498db,
|
|
2314
|
-
0 -8px 0 #3498db,
|
|
2315
|
-
4px -8px 0 #3498db,
|
|
2316
|
-
8px -12px 0 #8b6914,
|
|
2317
|
-
8px -8px 0 #8b6914,
|
|
2318
|
-
-4px -4px 0 #2c3e50,
|
|
2319
|
-
0 -4px 0 #2c3e50,
|
|
2320
|
-
-4px 0 0 #2c3e50,
|
|
2321
|
-
4px 0 0 #2c3e50;
|
|
2322
|
-
}
|
|
2323
|
-
50% {
|
|
2324
|
-
box-shadow:
|
|
2325
|
-
0 -20px 0 #e74c3c,
|
|
2326
|
-
-4px -20px 0 #e74c3c,
|
|
2327
|
-
4px -20px 0 #e74c3c,
|
|
2328
|
-
-4px -24px 0 #e74c3c,
|
|
2329
|
-
0 -24px 0 #e74c3c,
|
|
2330
|
-
4px -24px 0 #e74c3c,
|
|
2331
|
-
-4px -16px 0 #f5c6a0,
|
|
2332
|
-
0 -16px 0 #f5c6a0,
|
|
2333
|
-
4px -16px 0 #f5c6a0,
|
|
2334
|
-
-4px -12px 0 #3498db,
|
|
2335
|
-
0 -12px 0 #3498db,
|
|
2336
|
-
4px -12px 0 #3498db,
|
|
2337
|
-
-4px -8px 0 #3498db,
|
|
2338
|
-
0 -8px 0 #3498db,
|
|
2339
|
-
4px -8px 0 #3498db,
|
|
2340
|
-
8px -12px 0 #8b6914,
|
|
2341
|
-
8px -8px 0 #8b6914,
|
|
2342
|
-
-4px -4px 0 #2c3e50,
|
|
2343
|
-
4px -4px 0 #2c3e50,
|
|
2344
|
-
-4px 0 0 #2c3e50,
|
|
2345
|
-
0 0 0 #2c3e50;
|
|
2346
|
-
}
|
|
2104
|
+
#basecamp-scene {
|
|
2105
|
+
position: relative;
|
|
2347
2106
|
}
|
|
2348
2107
|
|
|
2349
|
-
/* Speech bubble */
|
|
2350
2108
|
.bc-speech {
|
|
2351
|
-
position:
|
|
2352
|
-
bottom: 52px;
|
|
2353
|
-
left: calc(50% + 8px);
|
|
2109
|
+
position: relative;
|
|
2354
2110
|
background: #1c2030;
|
|
2355
2111
|
border: 1px solid #2a2f42;
|
|
2356
2112
|
border-radius: 8px;
|
|
2357
2113
|
padding: 6px 10px;
|
|
2358
2114
|
font-size: 11px;
|
|
2115
|
+
color: #e4e8f0;
|
|
2359
2116
|
white-space: nowrap;
|
|
2360
|
-
|
|
2117
|
+
max-width: min(320px, calc(100vw - 120px));
|
|
2118
|
+
overflow: hidden;
|
|
2119
|
+
text-overflow: ellipsis;
|
|
2361
2120
|
animation: bubble-float 3s ease-in-out infinite;
|
|
2362
2121
|
}
|
|
2363
2122
|
|
|
2364
2123
|
.bc-speech::after {
|
|
2365
2124
|
content: '';
|
|
2366
2125
|
position: absolute;
|
|
2367
|
-
bottom: -
|
|
2126
|
+
bottom: -5px;
|
|
2368
2127
|
left: 20px;
|
|
2369
|
-
width:
|
|
2370
|
-
height:
|
|
2371
|
-
|
|
2372
|
-
border-right:
|
|
2373
|
-
border-
|
|
2128
|
+
width: 8px;
|
|
2129
|
+
height: 8px;
|
|
2130
|
+
background: #1c2030;
|
|
2131
|
+
border-right: 1px solid #2a2f42;
|
|
2132
|
+
border-bottom: 1px solid #2a2f42;
|
|
2133
|
+
transform: rotate(45deg);
|
|
2374
2134
|
}
|
|
2375
2135
|
|
|
2376
2136
|
@keyframes bubble-float {
|
|
@@ -2388,6 +2148,20 @@ header.hidden {
|
|
|
2388
2148
|
transition: opacity 0.5s;
|
|
2389
2149
|
}
|
|
2390
2150
|
|
|
2151
|
+
.bc-speech.guide-mode {
|
|
2152
|
+
border-color: rgba(99, 102, 241, 0.4);
|
|
2153
|
+
background: #1a1d2e;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
.bc-speech.guide-mode::after {
|
|
2157
|
+
background: #1a1d2e;
|
|
2158
|
+
border-color: rgba(99, 102, 241, 0.4);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
#sherpa-quote {
|
|
2162
|
+
transition: opacity 0.5s;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2391
2165
|
/* ============================================================
|
|
2392
2166
|
Activity Trail
|
|
2393
2167
|
============================================================ */
|
|
@@ -2416,3 +2190,60 @@ header.hidden {
|
|
|
2416
2190
|
font-size: 11px;
|
|
2417
2191
|
color: var(--text-muted);
|
|
2418
2192
|
}
|
|
2193
|
+
|
|
2194
|
+
/* Change Report */
|
|
2195
|
+
.ws-report-section { border-top: 1px solid var(--border); padding-top: 12px; }
|
|
2196
|
+
.ws-report-desc { font-size: 14px; line-height: 1.6; color: var(--text); margin-bottom: 8px; white-space: pre-line; }
|
|
2197
|
+
.ws-report-warning { display: flex; align-items: center; gap: 6px; padding: 6px 8px; margin-bottom: 4px; background: rgba(255, 180, 0, 0.08); border-radius: 6px; font-size: 13px; color: var(--text-muted); }
|
|
2198
|
+
.ws-report-warning-icon { font-size: 14px; flex-shrink: 0; }
|
|
2199
|
+
.ws-report-categories { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
|
|
2200
|
+
.ws-report-cat-group { }
|
|
2201
|
+
.ws-report-cat-header { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
|
|
2202
|
+
.ws-report-cat-label { font-size: 13px; font-weight: 600; color: var(--text); }
|
|
2203
|
+
.ws-report-cat-count { font-size: 11px; color: var(--text-muted); background: var(--surface); padding: 1px 6px; border-radius: 8px; }
|
|
2204
|
+
.ws-report-cat-items { list-style: none; margin: 0; padding: 0; }
|
|
2205
|
+
.ws-report-cat-items li { font-size: 13px; color: var(--text-muted); padding: 2px 0 2px 16px; position: relative; line-height: 1.5; }
|
|
2206
|
+
.ws-report-cat-items li::before { content: '•'; position: absolute; left: 4px; color: var(--text-muted); }
|
|
2207
|
+
.ws-report-nav-item { cursor: pointer; transition: color 0.15s, background 0.15s; border-radius: 4px; }
|
|
2208
|
+
.ws-report-nav-item:hover { color: var(--accent) !important; background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
2209
|
+
.ws-report-nav-hint { font-size: 11px; color: var(--accent); opacity: 0; transition: opacity 0.15s; margin-left: 4px; }
|
|
2210
|
+
.ws-report-nav-item:hover .ws-report-nav-hint { opacity: 1; }
|
|
2211
|
+
.ws-report-saved { opacity: 0.7; }
|
|
2212
|
+
.ws-report-saved-desc { font-size: 13px; color: var(--text-muted); }
|
|
2213
|
+
|
|
2214
|
+
/* Commit report (details/summary dropdown) */
|
|
2215
|
+
details.ws-commit-item { border-radius: 6px; margin-bottom: 4px; }
|
|
2216
|
+
details.ws-commit-item > summary.ws-commit-summary { display: flex; align-items: center; gap: 6px; cursor: pointer; list-style: none; padding: 6px 8px; border-radius: 6px; flex-wrap: nowrap; }
|
|
2217
|
+
details.ws-commit-item > summary.ws-commit-summary::-webkit-details-marker { display: none; }
|
|
2218
|
+
details.ws-commit-item > summary.ws-commit-summary:hover { background: var(--surface); }
|
|
2219
|
+
.ws-commit-arrow { font-size: 10px; color: var(--text-muted); flex-shrink: 0; transition: transform 0.15s; display: inline-block; }
|
|
2220
|
+
details.ws-commit-item[open] .ws-commit-arrow { transform: rotate(90deg); }
|
|
2221
|
+
.ws-commit-report { margin: 4px 8px 8px 8px; padding: 10px 12px; background: var(--surface); border-radius: 6px; font-size: 12px; width: auto; }
|
|
2222
|
+
.ws-commit-report-loading, .ws-commit-report-empty { color: var(--text-muted); }
|
|
2223
|
+
.ws-commit-cat { margin-bottom: 6px; }
|
|
2224
|
+
.ws-commit-cat:last-child { margin-bottom: 0; }
|
|
2225
|
+
.ws-commit-cat-label { font-size: 11px; font-weight: 600; color: var(--text-muted); display: block; margin-bottom: 2px; }
|
|
2226
|
+
.ws-commit-cat-item { color: var(--text-muted); padding-left: 12px; position: relative; line-height: 1.5; }
|
|
2227
|
+
.ws-commit-cat-item::before { content: '•'; position: absolute; left: 3px; color: var(--text-muted); }
|
|
2228
|
+
|
|
2229
|
+
/* Split-view compare */
|
|
2230
|
+
.ws-preview-container { position: relative; flex: 1; display: flex; }
|
|
2231
|
+
.ws-preview-container .ws-preview-full { flex: 1; }
|
|
2232
|
+
.ws-preview-main { display: none; flex: 1; flex-direction: column; border-left: 2px solid var(--accent); position: relative; }
|
|
2233
|
+
.ws-preview-main.hidden { display: none !important; }
|
|
2234
|
+
.ws-split-view .ws-preview-full,
|
|
2235
|
+
.ws-split-view .ws-preview-main { flex: 1; display: flex; flex-direction: column; }
|
|
2236
|
+
.ws-preview-label { position: absolute; top: 8px; left: 8px; z-index: 10; background: var(--surface); padding: 2px 8px; border-radius: 4px; font-size: 11px; color: var(--text-muted); pointer-events: none; }
|
|
2237
|
+
.ws-preview-loading { display: flex; align-items: center; justify-content: center; flex: 1; color: var(--text-muted); font-size: 14px; }
|
|
2238
|
+
.btn-active { background: var(--accent) !important; color: white !important; }
|
|
2239
|
+
.ws-split-view .ws-preview-full { position: relative; }
|
|
2240
|
+
.ws-split-view .ws-preview-full::before {
|
|
2241
|
+
content: '⛺ 내 캠프';
|
|
2242
|
+
position: absolute; top: 8px; left: 8px; z-index: 10;
|
|
2243
|
+
background: var(--surface); padding: 2px 8px; border-radius: 4px;
|
|
2244
|
+
font-size: 11px; color: var(--text-muted); pointer-events: none;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
/* Ship report preview */
|
|
2248
|
+
.ship-report-preview { margin-bottom: 12px; padding: 10px; background: var(--surface); border-radius: 6px; font-size: 13px; line-height: 1.5; }
|
|
2249
|
+
.ship-report-desc { margin-bottom: 8px; }
|
package/dist/bin/sanjang.js
CHANGED
|
@@ -14,7 +14,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
14
14
|
i++;
|
|
15
15
|
}
|
|
16
16
|
if (args[i] === "--port" && args[i + 1]) {
|
|
17
|
-
port = parseInt(args[i + 1]);
|
|
17
|
+
port = parseInt(args[i + 1], 10);
|
|
18
18
|
i++;
|
|
19
19
|
}
|
|
20
20
|
if (args[i] === "--force") {
|
|
@@ -53,8 +53,8 @@ if (command === "init") {
|
|
|
53
53
|
rl.question(" 어떤 앱을 띄울까요? [번호]: ", resolve);
|
|
54
54
|
});
|
|
55
55
|
rl.close();
|
|
56
|
-
const idx = parseInt(answer) - 1;
|
|
57
|
-
if (idx < 0 || idx >= apps.length || isNaN(idx)) {
|
|
56
|
+
const idx = parseInt(answer, 10) - 1;
|
|
57
|
+
if (idx < 0 || idx >= apps.length || Number.isNaN(idx)) {
|
|
58
58
|
console.error("⛰ 잘못된 선택입니다.");
|
|
59
59
|
process.exit(1);
|
|
60
60
|
}
|
|
@@ -132,7 +132,51 @@ else if (command === "help" || command === "--help" || command === "-h") {
|
|
|
132
132
|
`);
|
|
133
133
|
}
|
|
134
134
|
else {
|
|
135
|
-
// Default: start server
|
|
135
|
+
// Default: start server — auto-init if no config exists
|
|
136
|
+
const configPath = resolve(projectRoot, "sanjang.config.js");
|
|
137
|
+
if (!existsSync(configPath)) {
|
|
138
|
+
console.log("⛰ 설정 파일이 없습니다. 프로젝트를 분석합니다...\n");
|
|
139
|
+
const { generateConfig, detectApps } = await import("../lib/config.js");
|
|
140
|
+
const apps = detectApps(projectRoot);
|
|
141
|
+
let appDir;
|
|
142
|
+
if (apps.length >= 2) {
|
|
143
|
+
console.log("⛰ 여러 앱이 감지되었습니다:");
|
|
144
|
+
for (let i = 0; i < apps.length; i++) {
|
|
145
|
+
console.log(` ${i + 1}) ${apps[i].dir}/\t(${apps[i].framework})`);
|
|
146
|
+
}
|
|
147
|
+
console.log("");
|
|
148
|
+
const { createInterface } = await import("node:readline");
|
|
149
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
150
|
+
const answer = await new Promise((r) => {
|
|
151
|
+
rl.question(" 어떤 앱을 띄울까요? [번호]: ", r);
|
|
152
|
+
});
|
|
153
|
+
rl.close();
|
|
154
|
+
const idx = parseInt(answer, 10) - 1;
|
|
155
|
+
if (idx < 0 || idx >= apps.length || Number.isNaN(idx)) {
|
|
156
|
+
console.error("⛰ 잘못된 선택입니다.");
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
appDir = apps[idx].dir;
|
|
160
|
+
console.log(` → ${appDir}/ (${apps[idx].framework}) 선택됨\n`);
|
|
161
|
+
}
|
|
162
|
+
else if (apps.length === 1) {
|
|
163
|
+
appDir = apps[0].dir;
|
|
164
|
+
}
|
|
165
|
+
const result = generateConfig(projectRoot, { appDir, force });
|
|
166
|
+
if (result.created) {
|
|
167
|
+
console.log(`⛰ ${result.message}`);
|
|
168
|
+
console.log(` 프레임워크: ${result.framework}\n`);
|
|
169
|
+
}
|
|
170
|
+
// Add .sanjang to .gitignore
|
|
171
|
+
const gitignorePath = resolve(projectRoot, ".gitignore");
|
|
172
|
+
if (existsSync(gitignorePath)) {
|
|
173
|
+
const { readFileSync, appendFileSync } = await import("node:fs");
|
|
174
|
+
const content = readFileSync(gitignorePath, "utf8");
|
|
175
|
+
if (!content.includes(".sanjang")) {
|
|
176
|
+
appendFileSync(gitignorePath, "\n# Sanjang local dev camps\n.sanjang/\n");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
136
180
|
const { startServer } = await import("../lib/server.js");
|
|
137
181
|
await startServer(projectRoot, { port });
|
|
138
182
|
}
|
package/dist/lib/config.js
CHANGED
|
@@ -230,13 +230,11 @@ function detectTurboMainApp(root) {
|
|
|
230
230
|
const port = portMatch?.[1] ? parseInt(portMatch[1], 10) : 3000;
|
|
231
231
|
candidates.push({ name: entry.name, port });
|
|
232
232
|
}
|
|
233
|
-
catch {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
233
|
+
catch { }
|
|
236
234
|
}
|
|
237
235
|
}
|
|
238
236
|
// Prefer app with explicit port, then first candidate
|
|
239
|
-
return candidates.find(c => c.port !== 3000) ?? candidates[0] ?? null;
|
|
237
|
+
return candidates.find((c) => c.port !== 3000) ?? candidates[0] ?? null;
|
|
240
238
|
}
|
|
241
239
|
function detectPackageManager(root) {
|
|
242
240
|
if (existsSync(join(root, "bun.lockb")) || existsSync(join(root, "bun.lock")))
|
|
@@ -272,7 +270,7 @@ export function generateConfig(projectRoot, options = {}) {
|
|
|
272
270
|
// Exclude .env.example, .env.test, .env.template
|
|
273
271
|
(f) => !f.includes("example") && !f.includes("template") && !f.includes(".test"));
|
|
274
272
|
// Detect potential issues
|
|
275
|
-
const
|
|
273
|
+
const _issues = detectSetupIssues(detectRoot);
|
|
276
274
|
const lines = [
|
|
277
275
|
"export default {",
|
|
278
276
|
` // ${detected.framework} detected`,
|