|
7 | 7 | WidgetType, |
8 | 8 | } from "@codemirror/view"; |
9 | 9 | import { EditorState, Range, StateField } from "@codemirror/state"; |
10 | | -import { TFile, App, MetadataCache, editorInfoField } from "obsidian"; |
| 10 | +import { TFile, App, MetadataCache, editorInfoField, editorViewField } from "obsidian"; |
11 | 11 | import { TaskTimerSettings } from "../common/setting-definition"; |
12 | 12 | import { TaskTimerMetadataDetector } from "../utils/TaskTimerMetadataDetector"; |
13 | 13 | import { TaskTimerManager, TimerState } from "../utils/TaskTimerManager"; |
@@ -138,42 +138,76 @@ class TaskTimerWidget extends WidgetType { |
138 | 138 | if (!taskId) { |
139 | 139 | const blockId = this.timerManager.generateBlockId(this.settings.blockRefPrefix); |
140 | 140 |
|
141 | | - // Get the active editor through the app |
142 | | - const activeView = (window as any).app?.workspace?.getActiveViewOfType((window as any).app?.viewRegistry?.getViewCreatorByType("markdown")); |
143 | | - const editor = activeView?.editor; |
| 141 | + // Get EditorView - try multiple methods |
| 142 | + let view = this.state.field(editorViewField, false); |
144 | 143 |
|
145 | | - console.log("[TaskTimer] Active view:", activeView); |
146 | | - console.log("[TaskTimer] Editor:", editor); |
| 144 | + // If that doesn't work, try through the app |
| 145 | + if (!view) { |
| 146 | + const app = (window as any).app; |
| 147 | + const activeLeaf = app?.workspace?.activeLeaf; |
| 148 | + if (activeLeaf?.view?.editor?.cm) { |
| 149 | + view = activeLeaf.view.editor.cm; |
| 150 | + console.log("[TaskTimer] Got EditorView from activeLeaf"); |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + console.log("[TaskTimer] EditorView:", view); |
147 | 155 |
|
148 | | - if (editor) { |
| 156 | + if (view) { |
149 | 157 | const line = this.state.doc.lineAt(this.lineFrom); |
150 | 158 | const lineText = line.text; |
151 | | - const updatedText = lineText.trimEnd() + ` ^${blockId}`; |
| 159 | + const blockRef = ` ^${blockId}`; |
152 | 160 |
|
153 | 161 | console.log(`[TaskTimer] Inserting block ID at line ${line.number}: ^${blockId}`); |
154 | 162 | console.log("[TaskTimer] Original text:", lineText); |
155 | | - console.log("[TaskTimer] Updated text:", updatedText); |
| 163 | + console.log("[TaskTimer] Insertion position:", line.to); |
156 | 164 |
|
157 | 165 | try { |
158 | | - // Use Obsidian's Editor API |
159 | | - const lineNumber = line.number - 1; |
160 | | - editor.replaceRange(updatedText, |
161 | | - { line: lineNumber, ch: 0 }, |
162 | | - { line: lineNumber, ch: lineText.length } |
163 | | - ); |
| 166 | + // Use CodeMirror's dispatch to insert the block reference |
| 167 | + view.dispatch({ |
| 168 | + changes: { |
| 169 | + from: line.to, |
| 170 | + insert: blockRef |
| 171 | + } |
| 172 | + }); |
164 | 173 |
|
165 | 174 | // Update our local reference |
166 | 175 | this.existingBlockId = blockId; |
167 | 176 | taskId = `${this.filePath}#^${blockId}`; |
| 177 | + |
| 178 | + // Force widget refresh after successful insertion |
| 179 | + setTimeout(() => { |
| 180 | + this.updateTimerState(); |
| 181 | + this.refreshUI(); |
| 182 | + }, 100); |
168 | 183 | } catch (err) { |
169 | | - console.error("[TaskTimer] Error replacing text:", err); |
| 184 | + console.error("[TaskTimer] Error dispatching change:", err); |
170 | 185 | return; |
171 | 186 | } |
172 | 187 | } else { |
173 | | - console.error("[TaskTimer] No editor available to insert block ID"); |
174 | | - // Try alternative approach |
| 188 | + console.error("[TaskTimer] No EditorView available"); |
| 189 | + // Fallback: try to get editor from editorInfoField |
175 | 190 | const editorInfo = this.state.field(editorInfoField); |
176 | | - console.log("[TaskTimer] EditorInfo from state:", editorInfo); |
| 191 | + console.log("[TaskTimer] Trying editorInfo:", editorInfo); |
| 192 | + |
| 193 | + if (editorInfo?.editor) { |
| 194 | + const line = this.state.doc.lineAt(this.lineFrom); |
| 195 | + const lineText = line.text; |
| 196 | + const updatedText = lineText.trimEnd() + ` ^${blockId}`; |
| 197 | + |
| 198 | + try { |
| 199 | + editorInfo.editor.replaceRange(updatedText, |
| 200 | + { line: line.number - 1, ch: 0 }, |
| 201 | + { line: line.number - 1, ch: lineText.length } |
| 202 | + ); |
| 203 | + |
| 204 | + this.existingBlockId = blockId; |
| 205 | + taskId = `${this.filePath}#^${blockId}`; |
| 206 | + } catch (err) { |
| 207 | + console.error("[TaskTimer] Fallback also failed:", err); |
| 208 | + return; |
| 209 | + } |
| 210 | + } |
177 | 211 | return; |
178 | 212 | } |
179 | 213 | } |
@@ -281,26 +315,56 @@ class TaskTimerWidget extends WidgetType { |
281 | 315 | const elapsedMs = this.timerManager.completeTimer(taskId); |
282 | 316 | const formattedDuration = TaskTimerFormatter.formatDuration(elapsedMs, this.settings.timeFormat); |
283 | 317 |
|
284 | | - // Get editor info to access editor |
285 | | - const editorInfo = this.state.field(editorInfoField); |
286 | | - if (!editorInfo?.editor) { |
287 | | - console.warn("[TaskTimer] Cannot access editor"); |
| 318 | + // Get EditorView to modify document |
| 319 | + let view = this.state.field(editorViewField, false); |
| 320 | + |
| 321 | + // If that doesn't work, try through the app |
| 322 | + if (!view) { |
| 323 | + const app = (window as any).app; |
| 324 | + const activeLeaf = app?.workspace?.activeLeaf; |
| 325 | + if (activeLeaf?.view?.editor?.cm) { |
| 326 | + view = activeLeaf.view.editor.cm; |
| 327 | + console.log("[TaskTimer] Got EditorView from activeLeaf for completion"); |
| 328 | + } |
| 329 | + } |
| 330 | + |
| 331 | + if (view) { |
| 332 | + const line = this.state.doc.lineAt(this.lineFrom); |
| 333 | + const lineText = line.text; |
| 334 | + |
| 335 | + // Create the updated text |
| 336 | + const updatedText = lineText |
| 337 | + .replace(/\[[ ]\]/, '[x]') // Mark as completed |
| 338 | + .replace(/\s*$/, ` (${formattedDuration})`); // Add duration at end |
| 339 | + |
| 340 | + console.log("[TaskTimer] Completing task - original:", lineText); |
| 341 | + console.log("[TaskTimer] Completing task - updated:", updatedText); |
| 342 | + |
| 343 | + try { |
| 344 | + // Use dispatch to replace the entire line |
| 345 | + view.dispatch({ |
| 346 | + changes: { |
| 347 | + from: line.from, |
| 348 | + to: line.to, |
| 349 | + insert: updatedText |
| 350 | + } |
| 351 | + }); |
| 352 | + } catch (err) { |
| 353 | + console.error("[TaskTimer] Error updating task:", err); |
| 354 | + // Try fallback |
| 355 | + const editorInfo = this.state.field(editorInfoField); |
| 356 | + if (editorInfo?.editor) { |
| 357 | + editorInfo.editor.replaceRange(updatedText, |
| 358 | + { line: line.number - 1, ch: 0 }, |
| 359 | + { line: line.number - 1, ch: lineText.length } |
| 360 | + ); |
| 361 | + } |
| 362 | + } |
| 363 | + } else { |
| 364 | + console.error("[TaskTimer] No view available to complete task"); |
288 | 365 | return; |
289 | 366 | } |
290 | 367 |
|
291 | | - // Update the task line to mark as complete and add duration |
292 | | - const lineText = this.state.doc.lineAt(this.lineFrom).text; |
293 | | - const completedTaskText = lineText |
294 | | - .replace(/\[[ ]\]/, '[x]') // Mark as completed |
295 | | - .replace(/\s*$/, ` (${formattedDuration})`); // Add duration at end |
296 | | - |
297 | | - // Apply the change to the document using the editor |
298 | | - const line = this.state.doc.lineAt(this.lineFrom); |
299 | | - editorInfo.editor.replaceRange(completedTaskText, |
300 | | - { line: line.number - 1, ch: 0 }, |
301 | | - { line: line.number - 1, ch: line.text.length } |
302 | | - ); |
303 | | - |
304 | 368 | this.stopRealtimeUpdates(); |
305 | 369 | this.updateTimerState(); |
306 | 370 | console.log(`[TaskTimer] Timer completed successfully: ${formattedDuration}`); |
|
0 commit comments