idea-manager 0.8.0 → 0.8.1

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": "idea-manager",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "AI 기반 브레인스토밍 → 구조화 → 프롬프트 생성 도구. MCP Server 내장.",
5
5
  "keywords": [
6
6
  "brainstorm",
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState, useEffect, useCallback } from 'react';
3
+ import { useState, useEffect, useCallback, useRef } from 'react';
4
4
  import type { ITask, TaskStatus, ItemPriority } from '@/types';
5
5
  import StatusFlow from './StatusFlow';
6
6
  import PromptEditor from './PromptEditor';
@@ -24,20 +24,15 @@ export default function TaskDetail({
24
24
  const [promptContent, setPromptContent] = useState('');
25
25
  const [refining, setRefining] = useState(false);
26
26
  const [editingTitle, setEditingTitle] = useState(false);
27
- const [showChat, setShowChat] = useState(false);
27
+ const [showPromptModal, setShowPromptModal] = useState(false);
28
28
 
29
29
  const basePath = `/api/projects/${projectId}/sub-projects/${subProjectId}/tasks/${task.id}`;
30
-
31
- // Auto-show chat when task is being executed by watcher
32
- useEffect(() => {
33
- if (task.status === 'testing') setShowChat(true);
34
- }, [task.status]);
30
+ const overlayRef = useRef<HTMLDivElement>(null);
35
31
 
36
32
  // Load prompt
37
33
  useEffect(() => {
38
34
  setTitle(task.title);
39
35
  setDescription(task.description);
40
- setShowChat(task.status === 'testing');
41
36
  fetch(`${basePath}/prompt`)
42
37
  .then(r => r.json())
43
38
  .then(data => setPromptContent(data.content || ''));
@@ -97,113 +92,128 @@ export default function TaskDetail({
97
92
 
98
93
  return (
99
94
  <div className="flex flex-col h-full">
100
- {/* Upper: Task info + Prompt */}
101
- <div className={`overflow-y-auto ${showChat ? 'flex-1 min-h-0' : 'flex-1'}`}>
102
- <div className="p-4 space-y-4">
103
- {/* Title */}
104
- <div>
105
- {editingTitle ? (
106
- <input
107
- value={title}
108
- onChange={(e) => setTitle(e.target.value)}
109
- onBlur={saveTitle}
110
- onKeyDown={(e) => { if (e.key === 'Enter') saveTitle(); if (e.key === 'Escape') { setTitle(task.title); setEditingTitle(false); } }}
111
- className="w-full bg-transparent text-xl font-semibold border-b border-primary
112
- focus:outline-none pb-1 text-foreground"
113
- autoFocus
114
- />
115
- ) : (
116
- <h2
117
- onClick={() => setEditingTitle(true)}
118
- className="text-xl font-semibold cursor-text hover:text-primary transition-colors"
95
+ {/* Compact header: Title + Status + Actions */}
96
+ <div className="px-4 py-3 border-b border-border flex-shrink-0 space-y-2">
97
+ {/* Title */}
98
+ {editingTitle ? (
99
+ <input
100
+ value={title}
101
+ onChange={(e) => setTitle(e.target.value)}
102
+ onBlur={saveTitle}
103
+ onKeyDown={(e) => { if (e.key === 'Enter') saveTitle(); if (e.key === 'Escape') { setTitle(task.title); setEditingTitle(false); } }}
104
+ className="w-full bg-transparent text-lg font-semibold border-b border-primary
105
+ focus:outline-none pb-1 text-foreground"
106
+ autoFocus
107
+ />
108
+ ) : (
109
+ <h2
110
+ onClick={() => setEditingTitle(true)}
111
+ className="text-lg font-semibold cursor-text hover:text-primary transition-colors"
112
+ >
113
+ {task.title}
114
+ </h2>
115
+ )}
116
+
117
+ {/* Status + Priority + Today + Prompt + Delete */}
118
+ <div className="flex items-center gap-3 flex-wrap">
119
+ <StatusFlow status={task.status} onChange={(status: TaskStatus) => onUpdate({ status })} />
120
+ <div className="flex items-center gap-1">
121
+ {priorities.map(p => (
122
+ <button
123
+ key={p}
124
+ onClick={() => onUpdate({ priority: p })}
125
+ className={`px-2 py-0.5 text-xs rounded transition-colors ${
126
+ task.priority === p
127
+ ? p === 'high' ? 'bg-destructive/20 text-destructive' : p === 'medium' ? 'bg-warning/20 text-warning' : 'bg-muted text-muted-foreground'
128
+ : 'text-muted-foreground/40 hover:text-muted-foreground'
129
+ }`}
119
130
  >
120
- {task.title}
121
- </h2>
122
- )}
123
- </div>
124
-
125
- {/* Status + Priority + Today */}
126
- <div className="flex items-center gap-4 flex-wrap">
127
- <StatusFlow status={task.status} onChange={(status: TaskStatus) => onUpdate({ status })} />
128
- <div className="flex items-center gap-1">
129
- {priorities.map(p => (
130
- <button
131
- key={p}
132
- onClick={() => onUpdate({ priority: p })}
133
- className={`px-2.5 py-1 text-sm rounded transition-colors ${
134
- task.priority === p
135
- ? p === 'high' ? 'bg-destructive/20 text-destructive' : p === 'medium' ? 'bg-warning/20 text-warning' : 'bg-muted text-muted-foreground'
136
- : 'text-muted-foreground/40 hover:text-muted-foreground'
137
- }`}
138
- >
139
- {p}
140
- </button>
141
- ))}
142
- </div>
143
- <button
144
- onClick={() => onUpdate({ is_today: !task.is_today })}
145
- className={`text-sm px-2.5 py-1 rounded transition-colors ${
146
- task.is_today
147
- ? 'bg-primary/20 text-primary'
148
- : 'text-muted-foreground hover:text-foreground'
149
- }`}
150
- >
151
- {task.is_today ? 'Today *' : 'Mark today'}
152
- </button>
153
- </div>
154
-
155
- {/* Description */}
156
- <div>
157
- <textarea
158
- value={description}
159
- onChange={(e) => setDescription(e.target.value)}
160
- onBlur={saveDescription}
161
- placeholder="Background, conditions, notes..."
162
- className="w-full bg-input border border-border rounded-lg px-3 py-2.5 text-sm
163
- focus:border-primary focus:outline-none text-foreground resize-y min-h-[60px]
164
- leading-relaxed"
165
- rows={3}
166
- />
131
+ {p}
132
+ </button>
133
+ ))}
167
134
  </div>
135
+ <button
136
+ onClick={() => onUpdate({ is_today: !task.is_today })}
137
+ className={`text-xs px-2 py-0.5 rounded transition-colors ${
138
+ task.is_today
139
+ ? 'bg-primary/20 text-primary'
140
+ : 'text-muted-foreground hover:text-foreground'
141
+ }`}
142
+ >
143
+ {task.is_today ? 'Today *' : 'Mark today'}
144
+ </button>
145
+
146
+ <span className="text-border">|</span>
147
+
148
+ <button
149
+ onClick={() => setShowPromptModal(true)}
150
+ className={`text-xs px-2 py-0.5 rounded transition-colors border ${
151
+ promptContent
152
+ ? 'bg-accent/15 text-accent border-accent/30 hover:bg-accent/25'
153
+ : 'text-muted-foreground border-border hover:text-foreground hover:border-muted-foreground'
154
+ }`}
155
+ >
156
+ Prompt{promptContent ? ' *' : ''}
157
+ </button>
158
+
159
+ <button
160
+ onClick={onDelete}
161
+ className="text-xs text-muted-foreground hover:text-destructive transition-colors ml-auto"
162
+ >
163
+ Delete
164
+ </button>
165
+ </div>
168
166
 
169
- {/* Prompt */}
170
- <PromptEditor
171
- content={promptContent}
172
- onSave={savePrompt}
173
- onRefine={handleRefine}
174
- refining={refining}
175
- />
167
+ {/* Description - compact */}
168
+ <textarea
169
+ value={description}
170
+ onChange={(e) => setDescription(e.target.value)}
171
+ onBlur={saveDescription}
172
+ placeholder="Background, conditions, notes..."
173
+ className="w-full bg-input border border-border rounded-lg px-3 py-2 text-sm
174
+ focus:border-primary focus:outline-none text-foreground resize-none
175
+ leading-relaxed"
176
+ rows={2}
177
+ />
178
+ </div>
176
179
 
177
- {/* Actions */}
178
- <div className="pt-4 border-t border-border flex items-center justify-between">
179
- <button
180
- onClick={() => setShowChat(!showChat)}
181
- className={`text-xs px-2.5 py-1 rounded-md transition-colors border ${
182
- showChat
183
- ? 'bg-accent/20 text-accent border-accent/30'
184
- : 'text-muted-foreground hover:text-foreground border-border hover:border-muted-foreground'
185
- }`}
186
- >
187
- {showChat ? 'Hide AI Chat' : 'AI Chat'}
188
- </button>
189
- <button
190
- onClick={onDelete}
191
- className="text-xs text-muted-foreground hover:text-destructive transition-colors"
192
- >
193
- Delete task
194
- </button>
195
- </div>
196
- </div>
180
+ {/* AI Chat - takes remaining space */}
181
+ <div className="flex-1 min-h-0">
182
+ <TaskChat
183
+ basePath={basePath}
184
+ taskStatus={task.status}
185
+ onApplyToPrompt={handleApplyToPrompt}
186
+ />
197
187
  </div>
198
188
 
199
- {/* Lower: AI Chat */}
200
- {showChat && (
201
- <div className="h-[45%] flex-shrink-0">
202
- <TaskChat
203
- basePath={basePath}
204
- taskStatus={task.status}
205
- onApplyToPrompt={handleApplyToPrompt}
206
- />
189
+ {/* Prompt Modal */}
190
+ {showPromptModal && (
191
+ <div
192
+ ref={overlayRef}
193
+ onClick={(e) => { if (e.target === overlayRef.current) setShowPromptModal(false); }}
194
+ className="fixed inset-0 z-50 flex items-center justify-center"
195
+ style={{ background: 'rgba(0,0,0,0.5)', backdropFilter: 'blur(2px)' }}
196
+ >
197
+ <div className="bg-card border border-border rounded-xl shadow-2xl shadow-black/40
198
+ w-full max-w-2xl mx-4 max-h-[80vh] flex flex-col animate-dialog-in">
199
+ <div className="flex items-center justify-between px-5 py-3 border-b border-border">
200
+ <h3 className="text-sm font-semibold text-foreground">Prompt</h3>
201
+ <button
202
+ onClick={() => setShowPromptModal(false)}
203
+ className="text-muted-foreground hover:text-foreground transition-colors text-sm"
204
+ >
205
+ Close
206
+ </button>
207
+ </div>
208
+ <div className="flex-1 overflow-y-auto p-5">
209
+ <PromptEditor
210
+ content={promptContent}
211
+ onSave={savePrompt}
212
+ onRefine={handleRefine}
213
+ refining={refining}
214
+ />
215
+ </div>
216
+ </div>
207
217
  </div>
208
218
  )}
209
219
  </div>