@tutti-os/workspace-issue-manager 0.0.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.
Files changed (47) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +15 -0
  3. package/dist/assets/workspace-dock-task.d.ts +3 -0
  4. package/dist/assets/workspace-dock-task.png +0 -0
  5. package/dist/chunk-3XE7PXVP.js +14 -0
  6. package/dist/chunk-3XE7PXVP.js.map +1 -0
  7. package/dist/chunk-4B2P64PY.js +938 -0
  8. package/dist/chunk-4B2P64PY.js.map +1 -0
  9. package/dist/chunk-DLB2WIV6.js +5033 -0
  10. package/dist/chunk-DLB2WIV6.js.map +1 -0
  11. package/dist/chunk-JDYJIVGG.js +445 -0
  12. package/dist/chunk-JDYJIVGG.js.map +1 -0
  13. package/dist/chunk-LEL6VWU4.js +1 -0
  14. package/dist/chunk-LEL6VWU4.js.map +1 -0
  15. package/dist/chunk-T5TTRS5M.js +1 -0
  16. package/dist/chunk-T5TTRS5M.js.map +1 -0
  17. package/dist/chunk-WJSZFKUD.js +292 -0
  18. package/dist/chunk-WJSZFKUD.js.map +1 -0
  19. package/dist/contracts/index.d.ts +492 -0
  20. package/dist/contracts/index.js +1 -0
  21. package/dist/contracts/index.js.map +1 -0
  22. package/dist/core/index.d.ts +81 -0
  23. package/dist/core/index.js +66 -0
  24. package/dist/core/index.js.map +1 -0
  25. package/dist/feature-D16xMEZU.d.ts +47 -0
  26. package/dist/i18n/index.d.ts +18 -0
  27. package/dist/i18n/index.js +14 -0
  28. package/dist/i18n/index.js.map +1 -0
  29. package/dist/index.d.ts +12 -0
  30. package/dist/index.js +95 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/issueManagerControllerService.interface-D8Z1B9k4.d.ts +106 -0
  33. package/dist/latestRunStatusRenderer-B_9yU0CM.d.ts +14 -0
  34. package/dist/services/index.d.ts +13 -0
  35. package/dist/services/index.js +24 -0
  36. package/dist/services/index.js.map +1 -0
  37. package/dist/ui/index.d.ts +58 -0
  38. package/dist/ui/index.js +12 -0
  39. package/dist/ui/index.js.map +1 -0
  40. package/dist/workbench/constants.d.ts +6 -0
  41. package/dist/workbench/constants.js +9 -0
  42. package/dist/workbench/constants.js.map +1 -0
  43. package/dist/workbench/index.d.ts +76 -0
  44. package/dist/workbench/index.js +276 -0
  45. package/dist/workbench/index.js.map +1 -0
  46. package/openapi/issue-manager.v1.yaml +1682 -0
  47. package/package.json +93 -0
@@ -0,0 +1,938 @@
1
+ import {
2
+ normalizeIssueManagerContent,
3
+ normalizeIssueManagerNodeState
4
+ } from "./chunk-WJSZFKUD.js";
5
+
6
+ // src/services/internal/model.ts
7
+ import { formatNextopShortDateTime } from "@tutti-os/ui-system/date-format";
8
+ function createIssueManagerDate(value) {
9
+ if (!value || !Number.isFinite(value)) {
10
+ return null;
11
+ }
12
+ const normalized = value < 1e12 ? value * 1e3 : value;
13
+ const date = new Date(normalized);
14
+ return Number.isNaN(date.getTime()) ? null : date;
15
+ }
16
+ function formatIssueManagerTimestamp(value) {
17
+ const date = createIssueManagerDate(value);
18
+ return date ? formatNextopShortDateTime(date) : null;
19
+ }
20
+ function formatIssueManagerDate(value) {
21
+ const date = createIssueManagerDate(value);
22
+ return date ? formatNextopShortDateTime(date) : "";
23
+ }
24
+ function resolveIssueManagerStatusLabel(copy, status) {
25
+ switch (status) {
26
+ case "not_started":
27
+ return copy.t("status.notStarted");
28
+ case "running":
29
+ return copy.t("status.running");
30
+ case "in_progress":
31
+ return copy.t("status.inProgress");
32
+ case "pending_acceptance":
33
+ return copy.t("status.pendingAcceptance");
34
+ case "completed":
35
+ return copy.t("status.completed");
36
+ case "failed":
37
+ return copy.t("status.failed");
38
+ case "canceled":
39
+ return copy.t("status.canceled");
40
+ default:
41
+ return copy.t("status.unknown");
42
+ }
43
+ }
44
+ function resolveIssueManagerPriorityLabel(copy, priority) {
45
+ switch (priority) {
46
+ case "high":
47
+ return copy.t("priority.high");
48
+ case "low":
49
+ return copy.t("priority.low");
50
+ default:
51
+ return copy.t("priority.medium");
52
+ }
53
+ }
54
+ function uniqueIssueManagerFileReferences(refs) {
55
+ const unique = /* @__PURE__ */ new Map();
56
+ for (const ref of refs) {
57
+ const normalizedPath = ref.path.trim();
58
+ if (!normalizedPath || unique.has(normalizedPath)) {
59
+ continue;
60
+ }
61
+ unique.set(normalizedPath, {
62
+ displayName: ref.displayName?.trim() || void 0,
63
+ kind: ref.kind === "folder" ? "folder" : "file",
64
+ path: normalizedPath
65
+ });
66
+ }
67
+ return [...unique.values()];
68
+ }
69
+ function parentDirectoryPath(path) {
70
+ const trimmed = path.trim();
71
+ if (!trimmed || trimmed === "/") {
72
+ return null;
73
+ }
74
+ const normalized = trimmed.endsWith("/") && trimmed.length > 1 ? trimmed.slice(0, -1) : trimmed;
75
+ const index = normalized.lastIndexOf("/");
76
+ if (index <= 0) {
77
+ return "/";
78
+ }
79
+ return normalized.slice(0, index) || "/";
80
+ }
81
+
82
+ // src/services/internal/controllerRuntime.ts
83
+ import { proxy } from "valtio/vanilla";
84
+
85
+ // src/services/internal/controllerUtils.ts
86
+ var defaultTaskPriority = "medium";
87
+ function resolveIssueManagerSelectedIssueId(currentSelectedIssueId, issues) {
88
+ if (currentSelectedIssueId && issues.some((issue) => issue.issueId === currentSelectedIssueId)) {
89
+ return currentSelectedIssueId;
90
+ }
91
+ return issues[0]?.issueId ?? null;
92
+ }
93
+ function resolveIssueManagerSelectedTaskId(currentSelectedTaskId, tasks) {
94
+ if (currentSelectedTaskId && tasks.some((task) => task.taskId === currentSelectedTaskId)) {
95
+ return currentSelectedTaskId;
96
+ }
97
+ return null;
98
+ }
99
+ function toContextRefInput(ref) {
100
+ return {
101
+ displayName: ref.displayName,
102
+ path: ref.path,
103
+ refType: ref.kind === "folder" ? "folder" : "file"
104
+ };
105
+ }
106
+ function toIssueManagerWorkspaceFileLinkInput(ref) {
107
+ return {
108
+ kind: ref.kind === "folder" ? "folder" : "file",
109
+ name: ref.displayName,
110
+ path: ref.path
111
+ };
112
+ }
113
+ function confirmIssueManagerMessage(message) {
114
+ try {
115
+ return globalThis.confirm?.(message) ?? true;
116
+ } catch {
117
+ return true;
118
+ }
119
+ }
120
+ function resolveIssueManagerErrorMessage(error, copy, fallbackKey) {
121
+ const raw = error instanceof Error ? error.message : typeof error === "string" ? error : String(error);
122
+ if (!copy) {
123
+ return raw;
124
+ }
125
+ if (raw === "issue_manager.upload_type_conflict") {
126
+ return copy.t("messages.uploadTypeConflict");
127
+ }
128
+ if (raw === "issue_manager.clipboard_unavailable") {
129
+ return copy.t("messages.clipboardUnavailable");
130
+ }
131
+ if (raw === "issue_manager.workspace_path_unavailable") {
132
+ return copy.t("messages.workspacePathUnavailable");
133
+ }
134
+ if (raw === "issue_manager.run_status_missing") {
135
+ return copy.t("messages.runStatusMissing");
136
+ }
137
+ if (raw === "issue_manager.run_timed_out") {
138
+ return copy.t("messages.runTimedOut");
139
+ }
140
+ if (raw.startsWith("issue_manager.run_exit_code:")) {
141
+ return copy.t("messages.runExitCode", {
142
+ code: raw.slice("issue_manager.run_exit_code:".length)
143
+ });
144
+ }
145
+ return fallbackKey ? copy.t(fallbackKey) : raw;
146
+ }
147
+ function resolveIssueManagerTopicDeleteErrorMessage(error, copy) {
148
+ const protocolError = extractProtocolErrorShape(error);
149
+ if (!protocolError) {
150
+ return resolveIssueManagerErrorMessage(
151
+ error,
152
+ copy,
153
+ "messages.topicDeleteFailed"
154
+ );
155
+ }
156
+ if (protocolError.reason === "workspace_issue_topic_not_empty") {
157
+ return copy.t("messages.topicDeleteNotEmpty");
158
+ }
159
+ if (protocolError.reason === "workspace_issue_topic_not_found") {
160
+ return copy.t("messages.topicDeleteNotFound");
161
+ }
162
+ if (protocolError.code === "invalid_request" && protocolError.reason === "malformed_request") {
163
+ return copy.t("messages.topicDeleteDefaultForbidden");
164
+ }
165
+ return copy.t("messages.topicDeleteFailed");
166
+ }
167
+ function extractProtocolErrorShape(error) {
168
+ if (isProtocolErrorShape(error)) {
169
+ return error;
170
+ }
171
+ if (typeof error !== "object" || error === null || !("error" in error)) {
172
+ return null;
173
+ }
174
+ const nestedError = error.error;
175
+ return isProtocolErrorShape(nestedError) ? nestedError : null;
176
+ }
177
+ function isProtocolErrorShape(value) {
178
+ return typeof value === "object" && value !== null && "code" in value && typeof value.code === "string" && (!("reason" in value) || typeof value.reason === "string" || typeof value.reason === "undefined");
179
+ }
180
+
181
+ // src/services/internal/controllerState.ts
182
+ function createIssueManagerIssueDraftFromNodeState(state) {
183
+ return {
184
+ content: state?.issueDraftContent ?? "",
185
+ title: state?.issueDraftTitle ?? ""
186
+ };
187
+ }
188
+ function createIssueManagerTaskDraftFromNodeState(state, priority = defaultTaskPriority) {
189
+ return {
190
+ content: state?.taskDraftContent ?? "",
191
+ priority,
192
+ title: state?.taskDraftTitle ?? ""
193
+ };
194
+ }
195
+ function applyIssueManagerIssueSelection(current, issueId) {
196
+ return {
197
+ ...current,
198
+ selectedIssueId: issueId,
199
+ selectedTaskId: null
200
+ };
201
+ }
202
+ function applyIssueManagerTaskSelection(current, taskId) {
203
+ return {
204
+ ...current,
205
+ selectedTaskId: taskId
206
+ };
207
+ }
208
+ function applyIssueManagerIssueDeleted(current) {
209
+ return {
210
+ ...current,
211
+ selectedIssueId: null,
212
+ selectedTaskId: null
213
+ };
214
+ }
215
+ function applyIssueManagerTaskDeleted(current) {
216
+ return {
217
+ ...current,
218
+ selectedTaskId: null
219
+ };
220
+ }
221
+ function applyIssueManagerSelectedAgentProvider(current, provider) {
222
+ return {
223
+ ...current,
224
+ selectedAgentProvider: provider
225
+ };
226
+ }
227
+ function applyIssueManagerSelectedExecutionDirectory(current, executionDirectory) {
228
+ return {
229
+ ...current,
230
+ selectedExecutionDirectory: executionDirectory?.trim() || null
231
+ };
232
+ }
233
+ function applyIssueManagerIssueListResultToNodeState(current, result) {
234
+ return {
235
+ ...current,
236
+ issueListNextPageToken: result.nextPageToken ?? null,
237
+ selectedIssueId: resolveIssueManagerSelectedIssueId(
238
+ current.selectedIssueId,
239
+ result.issues
240
+ )
241
+ };
242
+ }
243
+ function applyIssueManagerIssueDetailResultToNodeState(current, detail) {
244
+ return {
245
+ ...current,
246
+ selectedTaskId: resolveIssueManagerSelectedTaskId(
247
+ current.selectedTaskId,
248
+ detail.tasks
249
+ )
250
+ };
251
+ }
252
+ function syncIssueManagerIssueDraftFromDetail(current, detail, editorMode) {
253
+ if (editorMode !== "read") {
254
+ return current;
255
+ }
256
+ return {
257
+ content: normalizeIssueManagerContent(detail?.issue.content ?? ""),
258
+ title: detail?.issue.title ?? ""
259
+ };
260
+ }
261
+ function syncIssueManagerTaskDraftFromDetail(current, detail, editorMode) {
262
+ if (editorMode !== "read") {
263
+ return current;
264
+ }
265
+ return {
266
+ content: normalizeIssueManagerContent(detail?.task.content ?? ""),
267
+ priority: detail?.task.priority ?? defaultTaskPriority,
268
+ title: detail?.task.title ?? ""
269
+ };
270
+ }
271
+ function applyIssueManagerIssueEditorModeToNodeState(current, editorMode) {
272
+ if (editorMode === "create") {
273
+ return current;
274
+ }
275
+ return {
276
+ ...current,
277
+ issueDraftContent: null,
278
+ issueDraftTitle: null
279
+ };
280
+ }
281
+ function applyIssueManagerTaskEditorModeToNodeState(current, editorMode) {
282
+ if (editorMode === "create") {
283
+ return current;
284
+ }
285
+ return {
286
+ ...current,
287
+ taskDraftContent: null,
288
+ taskDraftTitle: null
289
+ };
290
+ }
291
+ function applyIssueManagerIssueSaved(current, issueId) {
292
+ return {
293
+ ...current,
294
+ issueDraftContent: null,
295
+ issueDraftTitle: null,
296
+ selectedIssueId: issueId
297
+ };
298
+ }
299
+ function applyIssueManagerTaskSaved(current, taskId) {
300
+ return {
301
+ ...current,
302
+ selectedTaskId: taskId,
303
+ taskDraftContent: null,
304
+ taskDraftTitle: null
305
+ };
306
+ }
307
+ function persistIssueManagerIssueDraftContent(current, editorMode, content) {
308
+ if (editorMode !== "create") {
309
+ return current;
310
+ }
311
+ return {
312
+ ...current,
313
+ issueDraftContent: content
314
+ };
315
+ }
316
+ function persistIssueManagerTaskDraftContent(current, editorMode, content) {
317
+ if (editorMode !== "create") {
318
+ return current;
319
+ }
320
+ return {
321
+ ...current,
322
+ taskDraftContent: content
323
+ };
324
+ }
325
+
326
+ // src/services/internal/controllerRuntime.ts
327
+ function createIssueManagerOpenedParams(source) {
328
+ return {
329
+ source,
330
+ trigger: source === "restore" || source === "agent_command" ? "automatic" : "manual"
331
+ };
332
+ }
333
+ function createIssueManagerControllerRuntime(input) {
334
+ const normalizedNodeState = normalizeIssueManagerNodeState(input.state);
335
+ const listeners = /* @__PURE__ */ new Set();
336
+ let deferredIssueSearch = normalizedNodeState.issueSearchQuery;
337
+ let issueDetailSequence = 0;
338
+ let issueListSequence = 0;
339
+ let lastResolvedIssueSearchQuery = null;
340
+ let hasReportedOpened = false;
341
+ let onStateChange;
342
+ let pendingIssueSearchAnalyticsQuery = null;
343
+ let refCount = 0;
344
+ let retained = false;
345
+ let taskDetailSequence = 0;
346
+ let topicListSequence = 0;
347
+ let unsubscribeIssueUpdates = null;
348
+ let snapshot = {
349
+ issueDetail: createAsyncCollectionState(
350
+ null
351
+ ),
352
+ issueDraft: createIssueManagerIssueDraftFromNodeState(normalizedNodeState),
353
+ issueEditorMode: "read",
354
+ issues: createAsyncCollectionState([], {
355
+ hasResolved: false
356
+ }),
357
+ isRunningTask: false,
358
+ nodeState: normalizedNodeState,
359
+ notification: null,
360
+ referenceTarget: null,
361
+ taskDetail: createAsyncCollectionState(null),
362
+ taskDraft: createIssueManagerTaskDraftFromNodeState(normalizedNodeState),
363
+ taskEditorMode: "read",
364
+ topics: createAsyncCollectionState([], {
365
+ hasResolved: false
366
+ })
367
+ };
368
+ const store = proxy(snapshot);
369
+ const notify = () => {
370
+ for (const listener of listeners) {
371
+ listener();
372
+ }
373
+ };
374
+ const setSnapshot = (updater) => {
375
+ const next = typeof updater === "function" ? updater(snapshot) : {
376
+ ...snapshot,
377
+ ...updater
378
+ };
379
+ if (next === snapshot) {
380
+ return;
381
+ }
382
+ snapshot = next;
383
+ Object.assign(store, next);
384
+ notify();
385
+ };
386
+ const syncIssueDraftFromCurrentDetail = () => {
387
+ const nextIssueDraft = syncIssueManagerIssueDraftFromDetail(
388
+ snapshot.issueDraft,
389
+ snapshot.issueDetail.value,
390
+ snapshot.issueEditorMode
391
+ );
392
+ if (nextIssueDraft === snapshot.issueDraft) {
393
+ return;
394
+ }
395
+ setSnapshot((current) => ({
396
+ ...current,
397
+ issueDraft: nextIssueDraft
398
+ }));
399
+ };
400
+ const syncTaskDraftFromCurrentDetail = () => {
401
+ const nextTaskDraft = syncIssueManagerTaskDraftFromDetail(
402
+ snapshot.taskDraft,
403
+ snapshot.taskDetail.value,
404
+ snapshot.taskEditorMode
405
+ );
406
+ if (nextTaskDraft === snapshot.taskDraft) {
407
+ return;
408
+ }
409
+ setSnapshot((current) => ({
410
+ ...current,
411
+ taskDraft: nextTaskDraft
412
+ }));
413
+ };
414
+ const applyNodeState = (updater) => {
415
+ const previous = snapshot.nodeState;
416
+ const next = typeof updater === "function" ? updater(previous) : {
417
+ ...previous,
418
+ ...updater
419
+ };
420
+ if (next === previous) {
421
+ return;
422
+ }
423
+ setSnapshot((current) => ({
424
+ ...current,
425
+ nodeState: next
426
+ }));
427
+ onStateChange?.(next);
428
+ handleNodeStateTransition(previous, next);
429
+ };
430
+ const loadTopics = async () => {
431
+ const sequence = ++topicListSequence;
432
+ setSnapshot((current) => ({
433
+ ...current,
434
+ topics: {
435
+ ...current.topics,
436
+ error: null,
437
+ isLoading: true
438
+ }
439
+ }));
440
+ try {
441
+ const result = await input.feature.backend.listTopics({
442
+ workspaceId: input.workspaceId
443
+ });
444
+ if (!retained || sequence !== topicListSequence) {
445
+ return;
446
+ }
447
+ if (result.topics.length === 0) {
448
+ throw new Error(input.feature.i18n.t("messages.topicListEmpty"));
449
+ }
450
+ setSnapshot((current) => ({
451
+ ...current,
452
+ topics: {
453
+ error: null,
454
+ hasResolved: true,
455
+ isLoading: false,
456
+ value: result.topics
457
+ }
458
+ }));
459
+ const currentActiveTopicId = snapshot.nodeState.activeTopicId;
460
+ const currentTopicExists = result.topics.some(
461
+ (topic) => topic.topicId === currentActiveTopicId
462
+ );
463
+ const activeTopicId = currentTopicExists ? currentActiveTopicId : result.topics[0]?.topicId ?? null;
464
+ applyNodeState({
465
+ activeTopicId
466
+ });
467
+ if (activeTopicId && activeTopicId === currentActiveTopicId) {
468
+ void loadIssues();
469
+ }
470
+ } catch (error) {
471
+ if (!retained || sequence !== topicListSequence) {
472
+ return;
473
+ }
474
+ setSnapshot((current) => ({
475
+ ...current,
476
+ topics: {
477
+ error: resolveIssueManagerErrorMessage(error, input.feature.i18n),
478
+ hasResolved: true,
479
+ isLoading: false,
480
+ value: []
481
+ }
482
+ }));
483
+ }
484
+ };
485
+ const trackIssueSearchAnalytics = (inputValue, resultCount) => {
486
+ const query = inputValue.trim();
487
+ if (!query) {
488
+ return;
489
+ }
490
+ void Promise.resolve(
491
+ input.feature.analytics?.track({
492
+ name: "issue_manager.task_searched",
493
+ params: {
494
+ queryLength: query.length,
495
+ resultCount
496
+ }
497
+ })
498
+ ).catch(() => void 0);
499
+ };
500
+ const flushPendingIssueSearchAnalytics = (searchQuery, resultCount) => {
501
+ if (pendingIssueSearchAnalyticsQuery !== searchQuery || !searchQuery) {
502
+ return;
503
+ }
504
+ pendingIssueSearchAnalyticsQuery = null;
505
+ trackIssueSearchAnalytics(searchQuery, resultCount);
506
+ };
507
+ const loadIssues = async () => {
508
+ const searchQuery = deferredIssueSearch.trim();
509
+ const activeTopicId = snapshot.nodeState.activeTopicId;
510
+ const sequence = ++issueListSequence;
511
+ if (!activeTopicId) {
512
+ lastResolvedIssueSearchQuery = searchQuery;
513
+ setSnapshot((current) => ({
514
+ ...current,
515
+ issues: {
516
+ error: null,
517
+ hasResolved: true,
518
+ isLoading: false,
519
+ value: []
520
+ }
521
+ }));
522
+ flushPendingIssueSearchAnalytics(searchQuery, 0);
523
+ return;
524
+ }
525
+ setSnapshot((current) => ({
526
+ ...current,
527
+ issues: {
528
+ ...current.issues,
529
+ error: null,
530
+ isLoading: true
531
+ }
532
+ }));
533
+ try {
534
+ const result = await input.feature.backend.listIssues({
535
+ searchQuery: searchQuery || void 0,
536
+ statusFilter: snapshot.nodeState.issueStatusFilter,
537
+ topicId: activeTopicId,
538
+ workspaceId: input.workspaceId
539
+ });
540
+ if (!retained || sequence !== issueListSequence) {
541
+ return;
542
+ }
543
+ lastResolvedIssueSearchQuery = searchQuery;
544
+ setSnapshot((current) => ({
545
+ ...current,
546
+ issues: {
547
+ error: null,
548
+ hasResolved: true,
549
+ isLoading: false,
550
+ statusCounts: result.statusCounts ?? current.issues.statusCounts,
551
+ value: result.issues
552
+ }
553
+ }));
554
+ applyNodeState(
555
+ (current) => applyIssueManagerIssueListResultToNodeState(current, result)
556
+ );
557
+ flushPendingIssueSearchAnalytics(searchQuery, result.issues.length);
558
+ } catch (error) {
559
+ if (!retained || sequence !== issueListSequence) {
560
+ return;
561
+ }
562
+ setSnapshot((current) => ({
563
+ ...current,
564
+ issues: {
565
+ error: resolveIssueManagerErrorMessage(error, input.feature.i18n),
566
+ hasResolved: true,
567
+ isLoading: false,
568
+ statusCounts: current.issues.statusCounts,
569
+ value: []
570
+ }
571
+ }));
572
+ }
573
+ };
574
+ const clearIssueDetail = () => {
575
+ setSnapshot((current) => ({
576
+ ...current,
577
+ issueDetail: createAsyncCollectionState(
578
+ null
579
+ )
580
+ }));
581
+ syncIssueDraftFromCurrentDetail();
582
+ };
583
+ const clearTaskDetail = () => {
584
+ setSnapshot((current) => ({
585
+ ...current,
586
+ taskDetail: createAsyncCollectionState(
587
+ null
588
+ )
589
+ }));
590
+ syncTaskDraftFromCurrentDetail();
591
+ };
592
+ const loadIssueDetail = async () => {
593
+ const selectedIssueId = snapshot.nodeState.selectedIssueId;
594
+ issueDetailSequence += 1;
595
+ const sequence = issueDetailSequence;
596
+ if (!selectedIssueId) {
597
+ clearIssueDetail();
598
+ clearTaskDetail();
599
+ return;
600
+ }
601
+ setSnapshot((current) => ({
602
+ ...current,
603
+ issueDetail: {
604
+ ...current.issueDetail,
605
+ error: null,
606
+ isLoading: true
607
+ }
608
+ }));
609
+ try {
610
+ const result = await input.feature.backend.getIssueDetail({
611
+ issueId: selectedIssueId,
612
+ workspaceId: input.workspaceId
613
+ });
614
+ if (!retained || sequence !== issueDetailSequence) {
615
+ return;
616
+ }
617
+ setSnapshot((current) => ({
618
+ ...current,
619
+ issueDetail: {
620
+ error: null,
621
+ isLoading: false,
622
+ value: result
623
+ }
624
+ }));
625
+ syncIssueDraftFromCurrentDetail();
626
+ applyNodeState(
627
+ (current) => applyIssueManagerIssueDetailResultToNodeState(current, result)
628
+ );
629
+ } catch (error) {
630
+ if (!retained || sequence !== issueDetailSequence) {
631
+ return;
632
+ }
633
+ setSnapshot((current) => ({
634
+ ...current,
635
+ issueDetail: {
636
+ error: resolveIssueManagerErrorMessage(error, input.feature.i18n),
637
+ isLoading: false,
638
+ value: null
639
+ }
640
+ }));
641
+ syncIssueDraftFromCurrentDetail();
642
+ }
643
+ };
644
+ const loadTaskDetail = async () => {
645
+ const selectedIssueId = snapshot.nodeState.selectedIssueId;
646
+ const selectedTaskId = snapshot.nodeState.selectedTaskId;
647
+ taskDetailSequence += 1;
648
+ const sequence = taskDetailSequence;
649
+ if (!selectedIssueId || !selectedTaskId) {
650
+ clearTaskDetail();
651
+ return;
652
+ }
653
+ setSnapshot((current) => ({
654
+ ...current,
655
+ taskDetail: {
656
+ ...current.taskDetail,
657
+ error: null,
658
+ isLoading: true
659
+ }
660
+ }));
661
+ try {
662
+ const result = await input.feature.backend.getTaskDetail({
663
+ issueId: selectedIssueId,
664
+ taskId: selectedTaskId,
665
+ workspaceId: input.workspaceId
666
+ });
667
+ if (!retained || sequence !== taskDetailSequence) {
668
+ return;
669
+ }
670
+ setSnapshot((current) => ({
671
+ ...current,
672
+ taskDetail: {
673
+ error: null,
674
+ isLoading: false,
675
+ value: result
676
+ }
677
+ }));
678
+ syncTaskDraftFromCurrentDetail();
679
+ } catch (error) {
680
+ if (!retained || sequence !== taskDetailSequence) {
681
+ return;
682
+ }
683
+ setSnapshot((current) => ({
684
+ ...current,
685
+ taskDetail: {
686
+ error: resolveIssueManagerErrorMessage(error, input.feature.i18n),
687
+ isLoading: false,
688
+ value: null
689
+ }
690
+ }));
691
+ syncTaskDraftFromCurrentDetail();
692
+ }
693
+ };
694
+ const handleNodeStateTransition = (previous, next) => {
695
+ if (!retained) {
696
+ return;
697
+ }
698
+ if (previous.activeTopicId !== next.activeTopicId && next.activeTopicId) {
699
+ applyNodeState({
700
+ selectedIssueId: null,
701
+ selectedTaskId: null
702
+ });
703
+ void loadIssues();
704
+ return;
705
+ }
706
+ if (previous.issueStatusFilter !== next.issueStatusFilter && deferredIssueSearch === snapshot.nodeState.issueSearchQuery) {
707
+ void loadIssues();
708
+ }
709
+ if (previous.selectedIssueId !== next.selectedIssueId) {
710
+ void loadIssueDetail();
711
+ return;
712
+ }
713
+ if (previous.selectedTaskId !== next.selectedTaskId) {
714
+ void loadTaskDetail();
715
+ }
716
+ };
717
+ const retainIssueUpdates = () => {
718
+ if (unsubscribeIssueUpdates || !input.feature.eventSource) {
719
+ return;
720
+ }
721
+ unsubscribeIssueUpdates = input.feature.eventSource.subscribeToIssueUpdates(
722
+ input.workspaceId,
723
+ (event) => {
724
+ if (event.workspaceId.trim() !== input.workspaceId.trim()) {
725
+ return;
726
+ }
727
+ void loadTopics();
728
+ void loadIssueDetail();
729
+ void loadTaskDetail();
730
+ }
731
+ );
732
+ try {
733
+ const connectResult = input.feature.eventSource.connect?.();
734
+ if (connectResult) {
735
+ void connectResult.catch(() => {
736
+ });
737
+ }
738
+ } catch {
739
+ }
740
+ };
741
+ const releaseIssueUpdates = () => {
742
+ unsubscribeIssueUpdates?.();
743
+ unsubscribeIssueUpdates = null;
744
+ };
745
+ return {
746
+ getSnapshot() {
747
+ return snapshot;
748
+ },
749
+ refreshAll() {
750
+ if (!retained) {
751
+ return;
752
+ }
753
+ void loadTopics();
754
+ void loadIssueDetail();
755
+ void loadTaskDetail();
756
+ },
757
+ refreshDetails() {
758
+ if (!retained) {
759
+ return;
760
+ }
761
+ void loadIssueDetail();
762
+ void loadTaskDetail();
763
+ },
764
+ release() {
765
+ refCount = Math.max(0, refCount - 1);
766
+ if (refCount > 0) {
767
+ return;
768
+ }
769
+ retained = false;
770
+ issueListSequence += 1;
771
+ issueDetailSequence += 1;
772
+ taskDetailSequence += 1;
773
+ topicListSequence += 1;
774
+ releaseIssueUpdates();
775
+ },
776
+ reportIssueSearchUsage(query) {
777
+ const searchQuery = query.trim();
778
+ if (!searchQuery) {
779
+ return;
780
+ }
781
+ pendingIssueSearchAnalyticsQuery = searchQuery;
782
+ if (lastResolvedIssueSearchQuery === searchQuery && !snapshot.issues.isLoading) {
783
+ flushPendingIssueSearchAnalytics(
784
+ searchQuery,
785
+ snapshot.issues.value.length
786
+ );
787
+ }
788
+ },
789
+ retain() {
790
+ refCount += 1;
791
+ if (retained) {
792
+ return;
793
+ }
794
+ retained = true;
795
+ if (!hasReportedOpened) {
796
+ hasReportedOpened = true;
797
+ void Promise.resolve(
798
+ input.feature.analytics?.track({
799
+ name: "issue_manager.opened",
800
+ params: createIssueManagerOpenedParams(
801
+ input.openSource ?? "restore"
802
+ )
803
+ })
804
+ ).catch(() => void 0);
805
+ }
806
+ retainIssueUpdates();
807
+ void loadTopics();
808
+ void loadIssueDetail();
809
+ void loadTaskDetail();
810
+ },
811
+ setIsRunningTask(update) {
812
+ setSnapshot((current) => ({
813
+ ...current,
814
+ isRunningTask: resolveSetStateAction(update, current.isRunningTask)
815
+ }));
816
+ },
817
+ setIssueDraftInternal(update) {
818
+ setSnapshot((current) => ({
819
+ ...current,
820
+ issueDraft: resolveSetStateAction(update, current.issueDraft)
821
+ }));
822
+ },
823
+ setIssueEditorModeState(update) {
824
+ const nextMode = resolveSetStateAction(update, snapshot.issueEditorMode);
825
+ setSnapshot((current) => ({
826
+ ...current,
827
+ issueEditorMode: nextMode
828
+ }));
829
+ syncIssueDraftFromCurrentDetail();
830
+ },
831
+ setReferenceTarget(update) {
832
+ setSnapshot((current) => ({
833
+ ...current,
834
+ referenceTarget: resolveSetStateAction(update, current.referenceTarget)
835
+ }));
836
+ },
837
+ setNotification(update) {
838
+ setSnapshot((current) => ({
839
+ ...current,
840
+ notification: resolveSetStateAction(update, current.notification)
841
+ }));
842
+ },
843
+ setTaskDraftInternal(update) {
844
+ setSnapshot((current) => ({
845
+ ...current,
846
+ taskDraft: resolveSetStateAction(update, current.taskDraft)
847
+ }));
848
+ },
849
+ setTaskEditorModeState(update) {
850
+ const nextMode = resolveSetStateAction(update, snapshot.taskEditorMode);
851
+ setSnapshot((current) => ({
852
+ ...current,
853
+ taskEditorMode: nextMode
854
+ }));
855
+ syncTaskDraftFromCurrentDetail();
856
+ },
857
+ subscribe(listener) {
858
+ listeners.add(listener);
859
+ return () => {
860
+ listeners.delete(listener);
861
+ };
862
+ },
863
+ syncInput(nextInput) {
864
+ onStateChange = nextInput.onStateChange;
865
+ if (deferredIssueSearch !== nextInput.deferredIssueSearch) {
866
+ deferredIssueSearch = nextInput.deferredIssueSearch;
867
+ if (retained) {
868
+ void loadIssues();
869
+ }
870
+ }
871
+ if (snapshot.nodeState.taskListCollapsed === true !== nextInput.taskListCollapsed) {
872
+ applyNodeState((current) => ({
873
+ ...current,
874
+ taskListCollapsed: nextInput.taskListCollapsed
875
+ }));
876
+ }
877
+ },
878
+ updateNodeState(updater) {
879
+ applyNodeState(updater);
880
+ },
881
+ store
882
+ };
883
+ }
884
+ function createAsyncCollectionState(value, options) {
885
+ return {
886
+ error: null,
887
+ ...options,
888
+ isLoading: false,
889
+ value
890
+ };
891
+ }
892
+ function resolveSetStateAction(update, current) {
893
+ return typeof update === "function" ? update(current) : update;
894
+ }
895
+
896
+ // src/services/internal/issueManagerControllerService.ts
897
+ var DefaultIssueManagerControllerService = class {
898
+ createSession(input) {
899
+ return createIssueManagerControllerRuntime(input);
900
+ }
901
+ };
902
+
903
+ // src/services/createIssueManagerControllerService.ts
904
+ function createIssueManagerControllerService() {
905
+ return new DefaultIssueManagerControllerService();
906
+ }
907
+
908
+ export {
909
+ createIssueManagerDate,
910
+ formatIssueManagerTimestamp,
911
+ formatIssueManagerDate,
912
+ resolveIssueManagerStatusLabel,
913
+ resolveIssueManagerPriorityLabel,
914
+ uniqueIssueManagerFileReferences,
915
+ parentDirectoryPath,
916
+ defaultTaskPriority,
917
+ toContextRefInput,
918
+ toIssueManagerWorkspaceFileLinkInput,
919
+ confirmIssueManagerMessage,
920
+ resolveIssueManagerErrorMessage,
921
+ resolveIssueManagerTopicDeleteErrorMessage,
922
+ createIssueManagerIssueDraftFromNodeState,
923
+ createIssueManagerTaskDraftFromNodeState,
924
+ applyIssueManagerIssueSelection,
925
+ applyIssueManagerTaskSelection,
926
+ applyIssueManagerIssueDeleted,
927
+ applyIssueManagerTaskDeleted,
928
+ applyIssueManagerSelectedAgentProvider,
929
+ applyIssueManagerSelectedExecutionDirectory,
930
+ applyIssueManagerIssueEditorModeToNodeState,
931
+ applyIssueManagerTaskEditorModeToNodeState,
932
+ applyIssueManagerIssueSaved,
933
+ applyIssueManagerTaskSaved,
934
+ persistIssueManagerIssueDraftContent,
935
+ persistIssueManagerTaskDraftContent,
936
+ createIssueManagerControllerService
937
+ };
938
+ //# sourceMappingURL=chunk-4B2P64PY.js.map