claude-controller 0.1.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.
@@ -0,0 +1,219 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko" data-locale="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title data-i18n="title">Controller Service</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="styles.css">
10
+ </head>
11
+ <body>
12
+
13
+ <!-- ── Layout ── -->
14
+ <div class="layout">
15
+
16
+ <!-- Main Content -->
17
+ <main class="main">
18
+
19
+ <!-- Section 1: Send Task -->
20
+ <div class="section" id="sendTask">
21
+ <div class="card">
22
+ <div class="card-header">
23
+ <div class="card-title">
24
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
25
+ <span data-i18n="send_task">새 작업 전송</span>
26
+ <span class="ctx-session-label" id="ctxSessionLabel"></span>
27
+ </div>
28
+ <div class="ctx-toolbar-wrap">
29
+ <div class="ctx-toolbar">
30
+ <button type="button" class="ctx-btn-x" onclick="clearContext()" title="컨텍스트 초기화">
31
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
32
+ </button>
33
+ <button type="button" class="ctx-btn active" id="ctxNew" onclick="setContextMode('new')">new</button>
34
+ <button type="button" class="ctx-btn" id="ctxResume" onclick="openSessionPicker('resume')">resume</button>
35
+ <button type="button" class="ctx-btn" id="ctxFork" onclick="openSessionPicker('fork')">fork</button>
36
+ </div>
37
+ <div class="session-picker" id="sessionPicker">
38
+ <div class="session-picker-header">
39
+ <span class="session-picker-title" id="sessionPickerTitle" data-i18n="select_session">세션 선택</span>
40
+ </div>
41
+ <div class="session-filter-bar" id="sessionFilterBar" style="display:none;">
42
+ <button type="button" class="session-filter-toggle active" id="sessionFilterBtn" onclick="_toggleProjectFilter()">
43
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
44
+ <span data-i18n="this_project_only">이 프로젝트만</span>
45
+ </button>
46
+ <span class="session-filter-project" id="sessionFilterProject"></span>
47
+ </div>
48
+ <div class="session-picker-search">
49
+ <input type="text" id="sessionSearchInput" data-i18n-placeholder="search_prompt" placeholder="프롬프트 검색..." oninput="_filterSessions(this.value)">
50
+ </div>
51
+ <div class="session-picker-list" id="sessionPickerList"></div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ <div class="card-body">
56
+ <form id="sendForm" onsubmit="return sendTask(event)">
57
+ <!-- Recent directory chips -->
58
+ <div class="recent-dirs" id="recentDirs"></div>
59
+
60
+ <div class="form-group">
61
+ <label class="form-label"><span data-i18n="prompt">프롬프트</span> <span class="img-count-badge" id="imgCountBadge"></span></label>
62
+ <div class="prompt-wrapper" id="promptWrapper">
63
+ <textarea id="promptInput" data-i18n-placeholder="prompt_placeholder" placeholder="Claude에게 전달할 명령을 입력하세요... (파일/이미지 드래그 또는 붙여넣기 가능)" rows="2"></textarea>
64
+ <div class="prompt-mirror" id="promptMirror"></div>
65
+ <div class="drop-overlay" id="dropOverlay">
66
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
67
+ <span data-i18n="drop_files">파일을 여기에 놓으세요</span>
68
+ </div>
69
+ <button type="button" class="attach-float-btn" onclick="openFilePicker()" title="파일 첨부 (드래그&드롭도 가능)">
70
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
71
+ </button>
72
+ </div>
73
+ <input type="file" id="filePickerInput" multiple hidden>
74
+ <div class="image-previews" id="attachmentPreviews"></div>
75
+ </div>
76
+
77
+ <!-- Inline directory picker + browser -->
78
+ <input type="hidden" id="cwdInput">
79
+ <div class="form-group" style="margin-top: 4px;">
80
+ <div class="dir-picker">
81
+ <div class="dir-picker-display" id="dirPickerDisplay" onclick="toggleDirBrowser()" title="클릭하여 디렉토리 선택">
82
+ <svg class="dir-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
83
+ <span id="dirPickerText" data-i18n="select_directory">디렉토리를 선택하세요...</span>
84
+ <svg id="dirPickerChevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-left:auto; flex-shrink:0; transition:transform 0.2s;"><polyline points="6 9 12 15 18 9"/></svg>
85
+ </div>
86
+ <button type="button" class="dir-picker-clear" id="dirPickerClear" onclick="clearDirSelection()" title="선택 해제">
87
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
88
+ </button>
89
+ </div>
90
+ <!-- Inline directory browser (replaces modal) -->
91
+ <div class="dir-browser-panel" id="dirBrowserPanel">
92
+ <div class="dir-modal-header">
93
+ <span class="dir-modal-title" data-i18n="browse_directory">디렉토리 탐색</span>
94
+ <button type="button" class="dir-modal-close" onclick="closeDirBrowser()">
95
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
96
+ </button>
97
+ </div>
98
+ <div class="dir-modal-path" id="dirBreadcrumb"></div>
99
+ <div class="dir-modal-list" id="dirList"></div>
100
+ <div class="dir-modal-footer">
101
+ <div class="dir-modal-current" id="dirCurrentPath"></div>
102
+ <button type="button" class="btn btn-sm" onclick="closeDirBrowser()" data-i18n="close">닫기</button>
103
+ </div>
104
+ </div>
105
+ </div>
106
+
107
+ <div class="form-actions">
108
+ <button type="button" class="btn" onclick="clearPromptForm();" data-i18n="reset">초기화</button>
109
+ <button type="submit" class="btn btn-primary" id="btnSend">
110
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
111
+ <span data-i18n="send">전송</span>
112
+ </button>
113
+ </div>
114
+ </form>
115
+ </div>
116
+ </div>
117
+ </div>
118
+
119
+ <!-- Section 2: Job List -->
120
+ <div class="section" id="jobList">
121
+ <div class="card">
122
+ <div class="card-header">
123
+ <div class="card-title">
124
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
125
+ <span data-i18n="job_list">작업 목록</span>
126
+ <span id="jobCount" style="font-size:0.72rem; color:var(--text-muted); font-weight:400;"></span>
127
+ </div>
128
+ <div class="card-header-actions">
129
+ <button class="btn btn-sm btn-danger" id="btnDeleteCompleted" onclick="deleteCompletedJobs()" title="완료된 작업 모두 제거" style="display:none;">
130
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
131
+ <span data-i18n="delete_completed">완료 삭제</span>
132
+ </button>
133
+ <button class="btn btn-sm btn-icon" onclick="fetchJobs()" title="새로고침">
134
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
135
+ </button>
136
+ </div>
137
+ </div>
138
+ <div class="table-wrap">
139
+ <table>
140
+ <thead>
141
+ <tr>
142
+ <th style="width:5%;">ID</th>
143
+ <th style="width:8%;" data-i18n="status">상태</th>
144
+ <th data-i18n="prompt">프롬프트</th>
145
+ <th style="width:12%;" data-i18n="folder">폴더</th>
146
+ <th style="width:9%;">Session</th>
147
+ <th style="width:14%;" data-i18n="created_at">생성 시간</th>
148
+ <th style="width:10%;"></th>
149
+ </tr>
150
+ </thead>
151
+ <tbody id="jobTableBody">
152
+ <tr data-job-id="__loading__"><td colspan="7" class="empty-state">
153
+ <div data-i18n="loading_jobs">작업 목록을 불러오는 중...</div>
154
+ </td></tr>
155
+ </tbody>
156
+ </table>
157
+ </div>
158
+ </div>
159
+ </div>
160
+
161
+ </main>
162
+ </div>
163
+
164
+ <!-- Settings FAB -->
165
+ <button class="settings-fab" onclick="openSettings()" title="설정">
166
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
167
+ </button>
168
+
169
+ <!-- Settings Panel Overlay -->
170
+ <div class="settings-overlay" id="settingsOverlay" onclick="if(event.target===this)closeSettings()">
171
+ <div class="settings-panel">
172
+ <div class="settings-header">
173
+ <div class="settings-title">
174
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
175
+ <span data-i18n="settings">설정</span>
176
+ </div>
177
+ <button class="settings-close" onclick="closeSettings()">
178
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
179
+ </button>
180
+ </div>
181
+ <div class="settings-body">
182
+
183
+ <!-- 언어 / Language -->
184
+ <div class="settings-section">
185
+ <div class="settings-section-title" data-i18n="language_settings">언어 설정</div>
186
+ <div class="setting-row">
187
+ <div class="setting-info">
188
+ <div class="setting-label" data-i18n="display_language">표시 언어</div>
189
+ <div class="setting-desc" data-i18n="display_language_desc">인터페이스에 표시되는 언어를 선택합니다</div>
190
+ </div>
191
+ <div class="setting-control">
192
+ <select class="setting-input" id="cfgLocale" style="width:160px;" onchange="onLocaleChange()">
193
+ <option value="ko">한국어</option>
194
+ <option value="en">English</option>
195
+ <option value="ja">日本語</option>
196
+ <option value="zh-CN">简体中文</option>
197
+ <option value="zh-TW">繁體中文</option>
198
+ <option value="es">Español</option>
199
+ <option value="fr">Français</option>
200
+ <option value="de">Deutsch</option>
201
+ </select>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ </div>
207
+ <div class="settings-footer">
208
+ <button class="btn" onclick="closeSettings()" data-i18n="close">닫기</button>
209
+ <button class="btn btn-primary" id="btnSaveSettings" onclick="saveSettings()" data-i18n="save">저장</button>
210
+ </div>
211
+ </div>
212
+ </div>
213
+
214
+ <!-- Toast Container -->
215
+ <div class="toast-container" id="toastContainer"></div>
216
+
217
+ <script src="app.js"></script>
218
+ </body>
219
+ </html>