claude-code-templates 1.8.0 → 1.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.
@@ -0,0 +1,357 @@
1
+ /**
2
+ * DataService - Handles API communication and data caching
3
+ * Part of the modular frontend architecture
4
+ */
5
+ class DataService {
6
+ constructor(webSocketService = null) {
7
+ this.cache = new Map();
8
+ this.eventListeners = new Set();
9
+ this.baseURL = '';
10
+ this.lastFetch = {};
11
+ this.webSocketService = webSocketService;
12
+ this.realTimeEnabled = false;
13
+
14
+ // Setup WebSocket integration if available
15
+ if (this.webSocketService) {
16
+ this.setupWebSocketIntegration();
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Add event listener for data changes
22
+ * @param {Function} callback - Callback function to call on data changes
23
+ */
24
+ addEventListener(callback) {
25
+ this.eventListeners.add(callback);
26
+ }
27
+
28
+ /**
29
+ * Remove event listener
30
+ * @param {Function} callback - Callback function to remove
31
+ */
32
+ removeEventListener(callback) {
33
+ this.eventListeners.delete(callback);
34
+ }
35
+
36
+ /**
37
+ * Notify all listeners of data changes
38
+ * @param {string} type - Type of data that changed
39
+ * @param {*} data - New data
40
+ */
41
+ notifyListeners(type, data) {
42
+ this.eventListeners.forEach(callback => {
43
+ try {
44
+ callback(type, data);
45
+ } catch (error) {
46
+ console.error('Error in DataService listener:', error);
47
+ }
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Generic fetch with caching support
53
+ * @param {string} endpoint - API endpoint
54
+ * @param {Object} options - Fetch options
55
+ * @returns {Promise<*>} Response data
56
+ */
57
+ async cachedFetch(endpoint, options = {}) {
58
+ const cacheKey = `${endpoint}_${JSON.stringify(options)}`;
59
+ const now = Date.now();
60
+ const cacheDuration = options.cacheDuration || 30000; // 30 seconds default
61
+
62
+ // Check if we have cached data that's still valid
63
+ if (this.cache.has(cacheKey)) {
64
+ const cached = this.cache.get(cacheKey);
65
+ if (now - cached.timestamp < cacheDuration) {
66
+ return cached.data;
67
+ }
68
+ }
69
+
70
+ try {
71
+ const response = await fetch(this.baseURL + endpoint, {
72
+ method: 'GET',
73
+ headers: {
74
+ 'Content-Type': 'application/json',
75
+ ...options.headers
76
+ },
77
+ ...options
78
+ });
79
+
80
+ if (!response.ok) {
81
+ throw new Error(`HTTP error! status: ${response.status}`);
82
+ }
83
+
84
+ const data = await response.json();
85
+
86
+ // Cache the response
87
+ this.cache.set(cacheKey, {
88
+ data,
89
+ timestamp: now
90
+ });
91
+
92
+ return data;
93
+ } catch (error) {
94
+ console.error(`Error fetching ${endpoint}:`, error);
95
+
96
+ // Return cached data if available, even if stale
97
+ if (this.cache.has(cacheKey)) {
98
+ console.warn('Using stale cached data due to fetch error');
99
+ return this.cache.get(cacheKey).data;
100
+ }
101
+
102
+ throw error;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Get conversations data
108
+ * @returns {Promise<Object>} Conversations data
109
+ */
110
+ async getConversations() {
111
+ return await this.cachedFetch('/api/data');
112
+ }
113
+
114
+ /**
115
+ * Get conversation states for real-time updates
116
+ * @returns {Promise<Object>} Conversation states
117
+ */
118
+ async getConversationStates() {
119
+ const cacheDuration = this.realTimeEnabled ? 30000 : 5000; // Longer cache with real-time
120
+ return await this.cachedFetch('/api/conversation-state', {
121
+ cacheDuration
122
+ });
123
+ }
124
+
125
+ /**
126
+ * Get chart data for visualizations
127
+ * @returns {Promise<Object>} Chart data
128
+ */
129
+ async getChartData() {
130
+ return await this.cachedFetch('/api/charts');
131
+ }
132
+
133
+ /**
134
+ * Get session data for Max plan usage tracking
135
+ * @returns {Promise<Object>} Session data including timer and usage info
136
+ */
137
+ async getSessionData() {
138
+ const cacheDuration = this.realTimeEnabled ? 30000 : 5000; // 30s with real-time, 5s without
139
+ return await this.cachedFetch('/api/session/data', { cacheDuration });
140
+ }
141
+
142
+ /**
143
+ * Get project statistics
144
+ * @returns {Promise<Object>} Project statistics
145
+ */
146
+ async getProjectStats() {
147
+ return await this.cachedFetch('/api/session/projects');
148
+ }
149
+
150
+ /**
151
+ * Get system health information
152
+ * @returns {Promise<Object>} System health data
153
+ */
154
+ async getSystemHealth() {
155
+ return await this.cachedFetch('/api/system/health');
156
+ }
157
+
158
+ /**
159
+ * Clear all cached data
160
+ */
161
+ clearCache() {
162
+ this.cache.clear();
163
+ console.log('DataService cache cleared');
164
+ }
165
+
166
+ /**
167
+ * Clear specific cache entry
168
+ * @param {string} endpoint - Endpoint to clear from cache
169
+ */
170
+ clearCacheEntry(endpoint) {
171
+ const keysToDelete = [];
172
+ for (const key of this.cache.keys()) {
173
+ if (key.startsWith(endpoint)) {
174
+ keysToDelete.push(key);
175
+ }
176
+ }
177
+ keysToDelete.forEach(key => this.cache.delete(key));
178
+ }
179
+
180
+ /**
181
+ * Setup WebSocket integration for real-time updates
182
+ */
183
+ setupWebSocketIntegration() {
184
+ if (!this.webSocketService) return;
185
+
186
+ console.log('🔌 Setting up WebSocket integration for DataService');
187
+
188
+ // Listen for data refresh events
189
+ this.webSocketService.on('data_refresh', (data) => {
190
+ console.log('📊 Real-time data refresh received');
191
+ this.handleRealTimeDataRefresh(data);
192
+ });
193
+
194
+ // Listen for conversation state changes
195
+ this.webSocketService.on('conversation_state_change', (data) => {
196
+ console.log('🔄 Real-time conversation state change');
197
+ this.handleRealTimeStateChange(data);
198
+ });
199
+
200
+ // Listen for connection status
201
+ this.webSocketService.on('connected', () => {
202
+ console.log('✅ WebSocket connected - enabling real-time updates');
203
+ this.realTimeEnabled = true;
204
+ this.subscribeToChannels();
205
+ });
206
+
207
+ this.webSocketService.on('disconnected', () => {
208
+ console.log('❌ WebSocket disconnected - falling back to polling');
209
+ this.realTimeEnabled = false;
210
+ this.startFallbackPolling();
211
+ });
212
+ }
213
+
214
+ /**
215
+ * Subscribe to WebSocket channels
216
+ */
217
+ async subscribeToChannels() {
218
+ if (!this.webSocketService || !this.realTimeEnabled) return;
219
+
220
+ try {
221
+ await this.webSocketService.subscribe('data_updates');
222
+ await this.webSocketService.subscribe('conversation_updates');
223
+ await this.webSocketService.subscribe('system_updates');
224
+ console.log('📡 Subscribed to real-time channels');
225
+ } catch (error) {
226
+ console.error('Error subscribing to channels:', error);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Handle real-time data refresh
232
+ * @param {Object} data - Fresh data from server
233
+ */
234
+ handleRealTimeDataRefresh(data) {
235
+ // Clear relevant cache entries
236
+ this.clearCacheEntry('/api/data');
237
+ this.clearCacheEntry('/api/conversation-state');
238
+
239
+ // Notify listeners
240
+ this.notifyListeners('data_refresh', data);
241
+ }
242
+
243
+ /**
244
+ * Handle real-time conversation state change
245
+ * @param {Object} data - State change data
246
+ */
247
+ handleRealTimeStateChange(data) {
248
+ // Clear conversation state cache
249
+ this.clearCacheEntry('/api/conversation-state');
250
+
251
+ // Notify listeners
252
+ this.notifyListeners('conversation_state_change', data);
253
+ }
254
+
255
+ /**
256
+ * Start periodic data refresh (fallback when WebSocket unavailable)
257
+ * @param {number} interval - Refresh interval in milliseconds
258
+ */
259
+ startPeriodicRefresh(interval = 30000) {
260
+ // Don't start polling if real-time is enabled
261
+ if (this.realTimeEnabled) {
262
+ console.log('⚡ Real-time updates enabled - skipping periodic refresh');
263
+ return;
264
+ }
265
+
266
+ if (this.refreshInterval) {
267
+ clearInterval(this.refreshInterval);
268
+ }
269
+
270
+ console.log('📅 Starting periodic refresh (fallback mode)');
271
+ this.refreshInterval = setInterval(async () => {
272
+ try {
273
+ // Only refresh if real-time is not available
274
+ if (!this.realTimeEnabled) {
275
+ const [conversations, states] = await Promise.all([
276
+ this.getConversations(),
277
+ this.getConversationStates()
278
+ ]);
279
+
280
+ // Notify listeners of fresh data
281
+ this.notifyListeners('conversations', conversations);
282
+ this.notifyListeners('states', states);
283
+ }
284
+ } catch (error) {
285
+ console.error('Error during periodic refresh:', error);
286
+ }
287
+ }, interval);
288
+ }
289
+
290
+ /**
291
+ * Start fallback polling when WebSocket disconnects
292
+ */
293
+ startFallbackPolling() {
294
+ if (!this.refreshInterval) {
295
+ console.log('🔄 Starting fallback polling due to WebSocket disconnect');
296
+ this.startPeriodicRefresh(10000); // More frequent polling as fallback
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Stop periodic refresh
302
+ */
303
+ stopPeriodicRefresh() {
304
+ if (this.refreshInterval) {
305
+ clearInterval(this.refreshInterval);
306
+ this.refreshInterval = null;
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Request real-time data refresh
312
+ */
313
+ async requestRefresh() {
314
+ if (this.webSocketService && this.realTimeEnabled) {
315
+ console.log('🔄 Requesting refresh via WebSocket');
316
+ try {
317
+ await this.webSocketService.requestRefresh();
318
+ return true;
319
+ } catch (error) {
320
+ console.error('Error requesting WebSocket refresh:', error);
321
+ }
322
+ }
323
+
324
+ // Fallback to cache clearing
325
+ console.log('🔄 Falling back to cache clear for refresh');
326
+ this.clearCache();
327
+ return false;
328
+ }
329
+
330
+ /**
331
+ * Set WebSocket service (for late initialization)
332
+ * @param {WebSocketService} webSocketService - WebSocket service instance
333
+ */
334
+ setWebSocketService(webSocketService) {
335
+ this.webSocketService = webSocketService;
336
+ this.setupWebSocketIntegration();
337
+ }
338
+
339
+ /**
340
+ * Get cache statistics
341
+ * @returns {Object} Cache statistics
342
+ */
343
+ getCacheStats() {
344
+ return {
345
+ size: this.cache.size,
346
+ keys: Array.from(this.cache.keys()),
347
+ listeners: this.eventListeners.size,
348
+ realTimeEnabled: this.realTimeEnabled,
349
+ webSocketConnected: this.webSocketService ? this.webSocketService.getStatus().isConnected : false
350
+ };
351
+ }
352
+ }
353
+
354
+ // Export for module use
355
+ if (typeof module !== 'undefined' && module.exports) {
356
+ module.exports = DataService;
357
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * StateService - Manages application state and state changes
3
+ * Part of the modular frontend architecture
4
+ */
5
+ class StateService {
6
+ constructor() {
7
+ this.state = {
8
+ conversations: [],
9
+ summary: {},
10
+ chartData: {},
11
+ selectedConversation: null,
12
+ conversationStates: {},
13
+ systemHealth: {},
14
+ isLoading: false,
15
+ error: null,
16
+ lastUpdate: null
17
+ };
18
+
19
+ this.subscribers = new Set();
20
+ this.stateHistory = [];
21
+ this.maxHistorySize = 50;
22
+ }
23
+
24
+ /**
25
+ * Subscribe to state changes
26
+ * @param {Function} callback - Callback to call on state changes
27
+ * @returns {Function} Unsubscribe function
28
+ */
29
+ subscribe(callback) {
30
+ this.subscribers.add(callback);
31
+
32
+ // Return unsubscribe function
33
+ return () => {
34
+ this.subscribers.delete(callback);
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Get current state
40
+ * @returns {Object} Current state
41
+ */
42
+ getState() {
43
+ return { ...this.state };
44
+ }
45
+
46
+ /**
47
+ * Get specific state property
48
+ * @param {string} key - State property key
49
+ * @returns {*} State property value
50
+ */
51
+ getStateProperty(key) {
52
+ return this.state[key];
53
+ }
54
+
55
+ /**
56
+ * Update state and notify subscribers
57
+ * @param {Object} newState - New state object
58
+ * @param {string} action - Action that caused the state change
59
+ */
60
+ setState(newState, action = 'setState') {
61
+ // Save current state to history
62
+ this.saveStateToHistory(action);
63
+
64
+ // Update state
65
+ this.state = {
66
+ ...this.state,
67
+ ...newState,
68
+ lastUpdate: Date.now()
69
+ };
70
+
71
+ // Notify all subscribers
72
+ this.notifySubscribers(action, newState);
73
+ }
74
+
75
+ /**
76
+ * Update specific state property
77
+ * @param {string} key - State property key
78
+ * @param {*} value - New value
79
+ * @param {string} action - Action that caused the change
80
+ */
81
+ setStateProperty(key, value, action = `set_${key}`) {
82
+ this.setState({ [key]: value }, action);
83
+ }
84
+
85
+ /**
86
+ * Save current state to history
87
+ * @param {string} action - Action that caused the state change
88
+ */
89
+ saveStateToHistory(action) {
90
+ this.stateHistory.push({
91
+ state: { ...this.state },
92
+ action,
93
+ timestamp: Date.now()
94
+ });
95
+
96
+ // Keep history size manageable
97
+ if (this.stateHistory.length > this.maxHistorySize) {
98
+ this.stateHistory.shift();
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Notify all subscribers of state changes
104
+ * @param {string} action - Action that caused the change
105
+ * @param {Object} changedState - The state that changed
106
+ */
107
+ notifySubscribers(action, changedState) {
108
+ this.subscribers.forEach(callback => {
109
+ try {
110
+ callback(this.state, action, changedState);
111
+ } catch (error) {
112
+ console.error('Error in StateService subscriber:', error);
113
+ }
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Update conversations data
119
+ * @param {Array} conversations - New conversations data
120
+ */
121
+ updateConversations(conversations) {
122
+ this.setState({ conversations }, 'update_conversations');
123
+ }
124
+
125
+ /**
126
+ * Update conversation states for real-time updates
127
+ * @param {Object} states - New conversation states
128
+ */
129
+ updateConversationStates(states) {
130
+ this.setState({ conversationStates: states }, 'update_conversation_states');
131
+ }
132
+
133
+ /**
134
+ * Update summary statistics
135
+ * @param {Object} summary - New summary data
136
+ */
137
+ updateSummary(summary) {
138
+ this.setState({ summary }, 'update_summary');
139
+ }
140
+
141
+ /**
142
+ * Update chart data
143
+ * @param {Object} chartData - New chart data
144
+ */
145
+ updateChartData(chartData) {
146
+ this.setState({ chartData }, 'update_chart_data');
147
+ }
148
+
149
+ /**
150
+ * Set selected conversation
151
+ * @param {Object} conversation - Selected conversation
152
+ */
153
+ setSelectedConversation(conversation) {
154
+ this.setState({ selectedConversation: conversation }, 'select_conversation');
155
+ }
156
+
157
+ /**
158
+ * Set loading state
159
+ * @param {boolean} isLoading - Loading state
160
+ */
161
+ setLoading(isLoading) {
162
+ this.setState({ isLoading }, 'set_loading');
163
+ }
164
+
165
+ /**
166
+ * Set error state
167
+ * @param {Error|string} error - Error object or message
168
+ */
169
+ setError(error) {
170
+ this.setState({ error }, 'set_error');
171
+ }
172
+
173
+ /**
174
+ * Clear error state
175
+ */
176
+ clearError() {
177
+ this.setState({ error: null }, 'clear_error');
178
+ }
179
+
180
+ /**
181
+ * Update system health
182
+ * @param {Object} health - System health data
183
+ */
184
+ updateSystemHealth(health) {
185
+ this.setState({ systemHealth: health }, 'update_system_health');
186
+ }
187
+
188
+ /**
189
+ * Handle conversation state change notification
190
+ * @param {string} conversationId - ID of the conversation that changed
191
+ * @param {string} newState - New state of the conversation
192
+ */
193
+ notifyConversationStateChange(conversationId, newState) {
194
+ const currentStates = { ...this.state.conversationStates };
195
+ currentStates[conversationId] = newState;
196
+
197
+ this.setState({ conversationStates: currentStates }, 'conversation_state_change');
198
+
199
+ // Also update the conversation in the conversations array
200
+ const updatedConversations = this.state.conversations.map(conv =>
201
+ conv.id === conversationId ? { ...conv, status: newState } : conv
202
+ );
203
+
204
+ this.setState({ conversations: updatedConversations }, 'update_conversation_status');
205
+ }
206
+
207
+ /**
208
+ * Get conversation by ID
209
+ * @param {string} conversationId - Conversation ID
210
+ * @returns {Object|null} Conversation object or null if not found
211
+ */
212
+ getConversationById(conversationId) {
213
+ return this.state.conversations.find(conv => conv.id === conversationId) || null;
214
+ }
215
+
216
+ /**
217
+ * Get conversations by status
218
+ * @param {string} status - Conversation status
219
+ * @returns {Array} Array of conversations with specified status
220
+ */
221
+ getConversationsByStatus(status) {
222
+ return this.state.conversations.filter(conv => conv.status === status);
223
+ }
224
+
225
+ /**
226
+ * Get state history
227
+ * @returns {Array} State history
228
+ */
229
+ getStateHistory() {
230
+ return [...this.stateHistory];
231
+ }
232
+
233
+ /**
234
+ * Clear state history
235
+ */
236
+ clearStateHistory() {
237
+ this.stateHistory = [];
238
+ }
239
+
240
+ /**
241
+ * Reset state to initial values
242
+ */
243
+ resetState() {
244
+ this.setState({
245
+ conversations: [],
246
+ summary: {},
247
+ chartData: {},
248
+ selectedConversation: null,
249
+ conversationStates: {},
250
+ systemHealth: {},
251
+ isLoading: false,
252
+ error: null,
253
+ lastUpdate: null
254
+ }, 'reset_state');
255
+ }
256
+
257
+ /**
258
+ * Get state statistics
259
+ * @returns {Object} State statistics
260
+ */
261
+ getStateStats() {
262
+ return {
263
+ subscribers: this.subscribers.size,
264
+ historySize: this.stateHistory.length,
265
+ conversationsCount: this.state.conversations.length,
266
+ lastUpdate: this.state.lastUpdate,
267
+ hasError: !!this.state.error,
268
+ isLoading: this.state.isLoading
269
+ };
270
+ }
271
+ }
272
+
273
+ // Export for module use
274
+ if (typeof module !== 'undefined' && module.exports) {
275
+ module.exports = StateService;
276
+ }