sanjang 0.3.4 → 0.3.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.
@@ -12,7 +12,7 @@
12
12
  <!-- Header -->
13
13
  <header>
14
14
  <h1>산장 <span id="project-name" style="font-weight:400;color:var(--text-muted);font-size:0.6em"></span></h1>
15
- <button class="btn btn-primary" onclick="document.getElementById('quickstart-input').focus();document.getElementById('quickstart-input').scrollIntoView({behavior:'smooth'})">+ 새 캠프</button>
15
+ <button class="btn btn-primary" onclick="openNewModal()">+ 새 캠프</button>
16
16
  </header>
17
17
 
18
18
  <!-- Portal Home -->
@@ -31,9 +31,13 @@
31
31
  </div>
32
32
  </div>
33
33
 
34
+ <!-- Stale camp banner -->
35
+ <div class="portal-stale-banner" id="portal-stale-banner" style="display:none"></div>
36
+
34
37
  <!-- 새로 시작 (핵심 액션, 맨 위) -->
35
38
  <div class="portal-section">
36
39
  <div class="portal-quickstart">
40
+ <label for="quickstart-input" class="sr-only">뭘 하고 싶어?</label>
37
41
  <input class="form-input portal-quickstart-input" id="quickstart-input"
38
42
  type="text" placeholder="뭘 하고 싶어? (예: 로그인 버튼 색상 변경)"
39
43
  autocomplete="off" spellcheck="false"
@@ -65,7 +69,10 @@
65
69
  <!-- Top bar — minimal, over the preview -->
66
70
  <div class="ws-topbar">
67
71
  <button class="btn btn-ghost btn-sm" onclick="exitWorkspace()">← 목록</button>
68
- <span class="workspace-title" id="ws-title"></span>
72
+ <div class="ws-camp-switcher">
73
+ <button class="ws-camp-switch-btn" id="ws-title" onclick="toggleCampSwitcher()"></button>
74
+ <div class="ws-camp-dropdown" id="ws-camp-dropdown"></div>
75
+ </div>
69
76
  <span class="workspace-status" id="ws-status"></span>
70
77
  <div class="ws-mini-char" id="ws-mini-char"></div>
71
78
  <div class="ws-quest" id="ws-quest">
@@ -76,12 +83,34 @@
76
83
  <div class="ws-quest-step" id="ws-step-ship"><div class="ws-quest-dot"></div><span>보내기</span></div>
77
84
  </div>
78
85
  <div style="flex:1"></div>
86
+ <button class="btn btn-sub btn-sm" id="ws-test-btn" onclick="wsRunTest()">🧪 테스트</button>
79
87
  <button class="btn btn-sub btn-sm" id="ws-terminal-btn" onclick="wsOpenTerminal()">💻 터미널</button>
88
+ <button class="btn btn-sub btn-sm" id="ws-compare-btn" onclick="toggleCompare()" title="원본과 비교">🔀 비교</button>
80
89
  <button class="btn btn-sub btn-sm" onclick="togglePanel()">⛰ 패널</button>
81
90
  </div>
82
91
 
83
- <!-- Preview — full screen -->
84
- <div class="ws-preview-full" id="ws-preview"></div>
92
+ <!-- Preview toolbar URL bar + viewport switcher -->
93
+ <div class="ws-preview-toolbar" id="ws-preview-toolbar" style="display:none">
94
+ <div class="ws-url-bar">
95
+ <button class="ws-url-back" onclick="previewBack()" title="뒤로">←</button>
96
+ <button class="ws-url-refresh" onclick="previewRefresh()" title="새로고침">↻</button>
97
+ <input class="ws-url-input" id="ws-url-input" type="text" placeholder="/" spellcheck="false"
98
+ onkeydown="if(event.key==='Enter')navigatePreview(this.value)">
99
+ </div>
100
+ <div class="ws-viewport-switcher">
101
+ <button class="ws-vp-btn ws-vp-active" onclick="setViewport('desktop')" title="데스크탑">🖥</button>
102
+ <button class="ws-vp-btn" onclick="setViewport('tablet')" title="태블릿">📱</button>
103
+ <button class="ws-vp-btn" onclick="setViewport('mobile')" title="모바일">📲</button>
104
+ </div>
105
+ </div>
106
+
107
+ <!-- Preview — full screen with optional split -->
108
+ <div class="ws-preview-container" id="ws-preview-container">
109
+ <div class="ws-preview-full" id="ws-preview"></div>
110
+ <div class="ws-preview-main hidden" id="ws-preview-main">
111
+ <div class="ws-preview-label">🏔️ 원본 (main)</div>
112
+ </div>
113
+ </div>
85
114
 
86
115
  <!-- Slide panel — right side -->
87
116
  <div class="ws-panel" id="ws-panel">
@@ -100,19 +129,49 @@
100
129
  <div id="ws-changes"></div>
101
130
  </details>
102
131
  </div>
132
+ <div class="workspace-section ws-report-section" id="ws-report-section" style="display:none">
133
+ <h3>📋 변경 리포트</h3>
134
+ <div class="ws-report-summary" id="ws-report-summary"></div>
135
+ <div class="ws-report-warnings" id="ws-report-warnings"></div>
136
+ <div class="ws-report-categories" id="ws-report-categories"></div>
137
+ </div>
103
138
  <div class="workspace-section">
104
139
  <h3>📜 세이브 기록</h3>
105
140
  <div id="ws-actions"></div>
106
141
  </div>
107
- <details class="workspace-section ws-log-details">
108
- <summary>🔴 브라우저 에러 <span class="ws-error-badge" id="ws-browser-error-badge" style="display:none">0</span></summary>
109
- <div class="ws-browser-error-panel" id="ws-browser-errors">
110
- <span style="color:var(--text-muted);font-size:12px">에러 없음</span>
142
+ <details class="workspace-section ws-log-details" open>
143
+ <summary>🛠 개발자 도구</summary>
144
+ <div class="ws-devtabs">
145
+ <button class="ws-devtab-btn ws-devtab-active" data-tab="errors" onclick="switchDevTab('errors')">🔴 에러 <span class="ws-error-badge" id="ws-browser-error-badge" style="display:none">0</span></button>
146
+ <button class="ws-devtab-btn" data-tab="console" onclick="switchDevTab('console')">콘솔 <span class="ws-devtab-badge" id="ws-console-badge" style="display:none">0</span></button>
147
+ <button class="ws-devtab-btn" data-tab="network" onclick="switchDevTab('network')">네트워크 <span class="ws-devtab-badge" id="ws-network-badge" style="display:none">0</span></button>
148
+ <button class="ws-devtab-btn" data-tab="test" onclick="switchDevTab('test')">🧪 테스트 <span class="ws-devtab-badge ws-test-badge" id="ws-test-badge" style="display:none"></span></button>
149
+ <button class="ws-devtab-btn" data-tab="log" onclick="switchDevTab('log')">📜 로그</button>
150
+ </div>
151
+ <div class="ws-devtab-panel" id="ws-devtab-errors">
152
+ <div class="ws-browser-error-panel" id="ws-browser-errors">
153
+ <span style="color:var(--text-muted);font-size:12px">에러 없음</span>
154
+ </div>
155
+ <button class="btn btn-fix" id="ws-fix-btn" onclick="copyFixPrompt()" style="display:none">🩹 고쳐줘 — 클립보드 복사</button>
156
+ </div>
157
+ <div class="ws-devtab-panel" id="ws-devtab-console" style="display:none">
158
+ <div class="ws-console-panel" id="ws-console-panel">
159
+ <span style="color:var(--text-muted);font-size:12px">로그 없음</span>
160
+ </div>
161
+ </div>
162
+ <div class="ws-devtab-panel" id="ws-devtab-network" style="display:none">
163
+ <div class="ws-network-panel" id="ws-network-panel">
164
+ <span style="color:var(--text-muted);font-size:12px">요청 없음</span>
165
+ </div>
166
+ </div>
167
+ <div class="ws-devtab-panel" id="ws-devtab-test" style="display:none">
168
+ <div class="ws-test-panel" id="ws-test-panel">
169
+ <span style="color:var(--text-muted);font-size:12px">🧪 버튼을 눌러 테스트 실행</span>
170
+ </div>
171
+ </div>
172
+ <div class="ws-devtab-panel" id="ws-devtab-log" style="display:none">
173
+ <div class="ws-log-panel" id="ws-log"><pre></pre></div>
111
174
  </div>
112
- </details>
113
- <details class="workspace-section ws-log-details">
114
- <summary>📜 로그</summary>
115
- <div class="ws-log-panel" id="ws-log"><pre></pre></div>
116
175
  </details>
117
176
  </div>
118
177
  <div class="ws-panel-actions">
@@ -132,39 +191,68 @@
132
191
  <div class="modal" role="dialog" aria-modal="true" aria-labelledby="new-pg-modal-title">
133
192
  <h2 id="new-pg-modal-title">새 캠프</h2>
134
193
 
135
- <div class="form-group">
136
- <label class="form-label" for="new-pg-name">이름</label>
137
- <input
138
- class="form-input"
139
- id="new-pg-name"
140
- type="text"
141
- placeholder="예: my-feature"
142
- autocomplete="off"
143
- spellcheck="false"
144
- >
145
- <span class="form-hint">소문자, 숫자, 하이픈만 사용 가능</span>
146
- <span id="new-pg-name-error" style="color: var(--status-error-fg); font-size: 12px;"></span>
194
+ <div class="new-camp-tabs">
195
+ <button class="new-camp-tab active" data-tab="quick" onclick="switchNewCampTab('quick')">뭘 하고 싶어?</button>
196
+ <button class="new-camp-tab" data-tab="branch" onclick="switchNewCampTab('branch')">브랜치 선택</button>
147
197
  </div>
148
198
 
149
- <div class="form-group">
150
- <label class="form-label" for="new-pg-branch">브랜치</label>
151
- <div class="branch-picker" id="branch-picker">
199
+ <!-- 탭 1: 자연어 퀵스타트 -->
200
+ <div class="new-camp-panel" id="new-camp-quick">
201
+ <div class="form-group">
202
+ <label for="modal-quickstart-input" class="sr-only">뭘 하고 싶어?</label>
152
203
  <input
153
204
  class="form-input"
154
- id="new-pg-branch"
205
+ id="modal-quickstart-input"
155
206
  type="text"
156
- placeholder="브랜치 이름 검색..."
207
+ placeholder="예: 로그인 버튼 색상 변경"
157
208
  autocomplete="off"
158
209
  spellcheck="false"
210
+ onkeydown="if(event.key==='Enter')modalQuickStart()"
159
211
  >
160
- <div class="branch-dropdown" id="branch-dropdown"></div>
212
+ <span class="form-hint">AI가 브랜치 이름과 캠프를 자동으로 만들어줍니다</span>
213
+ </div>
214
+ <div class="modal-actions">
215
+ <button class="btn btn-ghost" onclick="closeNewModal()">취소</button>
216
+ <button class="btn btn-primary" id="modal-quickstart-btn" onclick="modalQuickStart()">시작</button>
161
217
  </div>
162
- <span class="form-hint" id="branch-count"></span>
163
218
  </div>
164
219
 
165
- <div class="modal-actions">
166
- <button class="btn btn-ghost" onclick="closeNewModal()">취소</button>
167
- <button class="btn btn-primary" id="create-pg-btn" onclick="createPg()">생성</button>
220
+ <!-- 탭 2: 기존 브랜치 선택 -->
221
+ <div class="new-camp-panel" id="new-camp-branch" style="display:none">
222
+ <div class="form-group">
223
+ <label class="form-label" for="new-pg-name">이름</label>
224
+ <input
225
+ class="form-input"
226
+ id="new-pg-name"
227
+ type="text"
228
+ placeholder="예: my-feature"
229
+ autocomplete="off"
230
+ spellcheck="false"
231
+ >
232
+ <span class="form-hint">소문자, 숫자, 하이픈만 사용 가능</span>
233
+ <span id="new-pg-name-error" style="color: var(--status-error-fg); font-size: 12px;"></span>
234
+ </div>
235
+
236
+ <div class="form-group">
237
+ <label class="form-label" for="new-pg-branch">브랜치</label>
238
+ <div class="branch-picker" id="branch-picker">
239
+ <input
240
+ class="form-input"
241
+ id="new-pg-branch"
242
+ type="text"
243
+ placeholder="브랜치 이름 검색..."
244
+ autocomplete="off"
245
+ spellcheck="false"
246
+ >
247
+ <div class="branch-dropdown" id="branch-dropdown"></div>
248
+ </div>
249
+ <span class="form-hint" id="branch-count"></span>
250
+ </div>
251
+
252
+ <div class="modal-actions">
253
+ <button class="btn btn-ghost" onclick="closeNewModal()">취소</button>
254
+ <button class="btn btn-primary" id="create-pg-btn" onclick="createPg()">생성</button>
255
+ </div>
168
256
  </div>
169
257
  </div>
170
258
  </div>
@@ -201,6 +289,19 @@
201
289
  </div>
202
290
  </div>
203
291
 
292
+ <!-- Stale Cleanup Modal -->
293
+ <div class="modal-backdrop" id="stale-modal">
294
+ <div class="modal" role="dialog" aria-modal="true">
295
+ <h2>캠프 정리</h2>
296
+ <p style="font-size:13px;color:var(--text-muted);margin-bottom:12px">삭제할 캠프를 선택하세요. 이 작업은 되돌릴 수 없습니다.</p>
297
+ <div id="stale-list"></div>
298
+ <div class="modal-actions">
299
+ <button class="btn btn-ghost" onclick="closeStaleModal()">취소</button>
300
+ <button class="btn btn-danger" onclick="confirmStaleCleanup()">삭제</button>
301
+ </div>
302
+ </div>
303
+ </div>
304
+
204
305
  <!-- Changes Modal -->
205
306
  <div class="modal-backdrop" id="changes-modal">
206
307
  <div class="modal" role="dialog" aria-modal="true">
@@ -212,6 +313,17 @@
212
313
  </div>
213
314
  </div>
214
315
 
316
+ <!-- Diff Modal -->
317
+ <div class="modal-backdrop" id="diff-modal">
318
+ <div class="modal modal-wide" role="dialog" aria-modal="true">
319
+ <h2 id="diff-modal-title">변경 내용</h2>
320
+ <div class="diff-content" id="diff-content"></div>
321
+ <div class="modal-actions">
322
+ <button class="btn btn-ghost" onclick="closeDiffModal()">닫기</button>
323
+ </div>
324
+ </div>
325
+ </div>
326
+
215
327
  <!-- Ship Modal -->
216
328
  <div class="modal-backdrop" id="ship-modal">
217
329
  <div class="modal" role="dialog" aria-modal="true">
@@ -219,6 +331,7 @@
219
331
  <p id="ship-file-count" style="font-size:13px;color:var(--text-muted);margin-bottom:12px"></p>
220
332
 
221
333
  <div class="form-group">
334
+ <div id="ship-report-preview" class="ship-report-preview" style="display:none"></div>
222
335
  <label class="form-label" for="ship-message">뭘 바꿨나요? (한 줄로)</label>
223
336
  <input
224
337
  class="form-input"
@@ -256,13 +369,13 @@
256
369
 
257
370
  <!-- Conflict Modal -->
258
371
  <div class="modal-backdrop" id="conflict-modal">
259
- <div class="modal" role="dialog" aria-modal="true">
372
+ <div class="modal modal-wide" role="dialog" aria-modal="true">
260
373
  <h2>충돌이 발생했어요</h2>
261
374
  <p style="font-size:13px;color:var(--text-muted);margin-bottom:12px">
262
- 팀의 변경사항과 변경사항이 같은 부분을 수정해서 충돌이 생겼어요.
375
+ 같은 파일을 동시에 수정해서 충돌이 생겼어요. 파일별로 선택하거나, 한꺼번에 해결할 수 있어요.
263
376
  </p>
264
- <div id="conflict-files" style="margin-bottom:16px"></div>
265
- <p style="font-size:13px;margin-bottom:16px">어떻게 할까요?</p>
377
+ <div id="conflict-files" style="margin-bottom:16px;max-height:50vh;overflow:auto"></div>
378
+ <p style="font-size:13px;margin-bottom:16px">또는 한꺼번에:</p>
266
379
  <div style="display:flex;flex-direction:column;gap:8px">
267
380
  <button class="btn btn-primary" onclick="resolveConflict('claude')">
268
381
  Claude에게 맡기기 (추천)