@bud.sh/app 0.4.1 → 0.5.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 (74) hide show
  1. package/dist/client/assets/bud/design.js +1 -1
  2. package/dist/client/assets/{canvas-DIKX_HCJ.js → canvas-8GXJIuDK.js} +1 -1
  3. package/dist/client/assets/difference-worker-C99ychYw.js +8 -0
  4. package/dist/client/assets/{edit-B050x0rV.js → edit-DUotSxjS.js} +12 -12
  5. package/dist/client/assets/files-DkWt1l9H.js +1 -0
  6. package/dist/client/assets/i18n-hH_Ba5QO.js +1 -0
  7. package/dist/client/assets/{index-COvyj5zV.js → index-Dn2pfmXf.js} +2 -2
  8. package/dist/client/assets/index-tpHYVXma.css +2 -0
  9. package/dist/client/assets/lru-DzKYIXMA.js +1 -0
  10. package/dist/client/assets/pages-BVd6MQmu.js +1 -0
  11. package/dist/client/assets/{project-branches-DwKjCwZv.js → project-branches-CMGRmrqr.js} +1 -1
  12. package/dist/client/assets/project-comments-D1IIQwsW.js +1 -0
  13. package/dist/client/assets/project-thread-links-DKwe_SYb.js +1 -0
  14. package/dist/client/assets/{project-threads-DeKXNqeP.js → project-threads-ZbKDh78d.js} +105 -105
  15. package/dist/client/assets/{project-work-B7PGQtV9.js → project-work-D4DZqBCZ.js} +1 -1
  16. package/dist/client/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  17. package/dist/client/assets/task-design-BPpc4Gd7.js +23 -0
  18. package/dist/client/assets/tasks-Cn0VOZ9Z.js +1 -0
  19. package/dist/client/assets/{tasks-request-CsUB5__X.js → tasks-request-BLyOzokZ.js} +1 -1
  20. package/dist/client/assets/workspace-DGErsSg6.js +2 -0
  21. package/dist/client/index.html +3 -3
  22. package/dist/hosted/assets/{canvas-gWj_ocKK.js → canvas-BYTOp1p7.js} +6 -4
  23. package/dist/hosted/assets/{comment-data-C-rNCB1n.js → comment-data-Cn5y2VxV.js} +4026 -3665
  24. package/dist/hosted/assets/{files-CAPvbavY.js → files-B1P2_24q.js} +5 -3
  25. package/dist/hosted/assets/{pages-DJiOvT-l.js → pages-B0ZlVdci.js} +3 -3
  26. package/dist/hosted/assets/{project-branches-BgrfdvtL.js → project-branches-DlCWmfRo.js} +1 -1
  27. package/dist/hosted/assets/{project-thread-links-B7uzThL9.js → project-thread-links-BZXQ200n.js} +2 -2
  28. package/dist/hosted/assets/{screen-D4D9nh4V.js → screen-CfdC3NbR.js} +72 -137
  29. package/dist/hosted/assets/{tasks-C7aGLzBw.js → tasks-_yKh2Z6q.js} +12 -5
  30. package/dist/hosted/assets/{tasks-request-CikFdCgP.js → tasks-request-CFPLvLc7.js} +1 -1
  31. package/dist/hosted/hosted.js +164 -16
  32. package/dist/node/assets/{src-BdvZ2mYc.js → src-CerrKjID.js} +57 -1
  33. package/dist/node/design.js +826 -791
  34. package/dist/node/server.js +25 -3
  35. package/dist/node-host/assets/{canvas-kXzp80a5.js → canvas-DxmcFZCh.js} +6 -4
  36. package/dist/node-host/assets/{comment-data-CeuJbJKJ.js → comment-data-yfWEdDQL.js} +4004 -3643
  37. package/dist/node-host/assets/{files-0YIuvKzb.js → files-anHi-apm.js} +5 -3
  38. package/dist/node-host/assets/{pages-DOF22SyG.js → pages-CW4aF6dK.js} +3 -3
  39. package/dist/node-host/assets/{project-branches-BeEwMBIw.js → project-branches-D7rVCxQX.js} +1 -1
  40. package/dist/node-host/assets/{project-thread-links-Bs_MtfBC.js → project-thread-links-m-OLpsD9.js} +2 -2
  41. package/dist/node-host/assets/{screen-DRTAKkn3.js → screen-DapOVrga.js} +72 -137
  42. package/dist/node-host/assets/{tasks-BA_hcbj5.js → tasks-BBK_CybR.js} +12 -5
  43. package/dist/node-host/assets/{tasks-request-Doc9y_20.js → tasks-request-B0aeNocI.js} +1 -1
  44. package/dist/node-host/node-host.js +164 -16
  45. package/dist/server/assets/{canvas-CiN6jNIZ.js → canvas-CkMK4hoN.js} +7 -5
  46. package/dist/server/assets/{comment-data-9SqALQjT.js → comment-data-NLeHG89b.js} +3781 -3631
  47. package/dist/server/assets/{events-6z5v7Uca.js → events-iHpLGr3k.js} +213 -2
  48. package/dist/server/assets/{files-DBVqeLdL.js → files-CRdl489E.js} +6 -4
  49. package/dist/server/assets/{listener-DGWKUc2o.js → listener-BAYn9Q-u.js} +35 -9
  50. package/dist/server/assets/{pages-DJMLr6j9.js → pages-DXVlJlBC.js} +4 -4
  51. package/dist/server/assets/{project-branches-DbKEmiQT.js → project-branches-CwTsf6WQ.js} +2 -2
  52. package/dist/server/assets/{project-thread-links-C0kV_Pcl.js → project-thread-links-lLS_rq7c.js} +3 -3
  53. package/dist/server/assets/{project-work-C0FN3w5Y.js → project-work-CSZlCwH-.js} +3 -3
  54. package/dist/server/assets/{screen-BtvzEiIa.js → screen-B1zH_1O3.js} +73 -138
  55. package/dist/server/assets/{server-CYvuOSBA.js → server-DxK-UB_Z.js} +30 -4
  56. package/dist/server/assets/{services-DK4fqrlF.js → services-BRrgXYlT.js} +105 -9
  57. package/dist/server/assets/{tasks-DX71x8f9.js → tasks-CGdz-uox.js} +14 -7
  58. package/dist/server/assets/{tasks-request-C3jk9Jyu.js → tasks-request-D2NBjCTl.js} +2 -2
  59. package/dist/server/migration.js +3 -3
  60. package/dist/server/runtime.js +3 -3
  61. package/dist/server/server.js +2 -2
  62. package/dist/types/server.d.ts +11 -1
  63. package/package.json +1 -1
  64. package/dist/client/assets/difference-worker-CwQNxTJL.js +0 -8
  65. package/dist/client/assets/files-BQ9O3Z8-.js +0 -1
  66. package/dist/client/assets/i18n-CL0jBP7Y.js +0 -1
  67. package/dist/client/assets/index-CjhJtepP.css +0 -2
  68. package/dist/client/assets/lru-BkhKJhvt.js +0 -1
  69. package/dist/client/assets/pages-BGl2Yuza.js +0 -1
  70. package/dist/client/assets/project-comments-Cdip2Y10.js +0 -1
  71. package/dist/client/assets/project-thread-links-BGIkGQeU.js +0 -1
  72. package/dist/client/assets/task-design-DJp5yho1.js +0 -23
  73. package/dist/client/assets/tasks-BHXj727C.js +0 -1
  74. package/dist/client/assets/workspace-CbO7RsI2.js +0 -2
@@ -1,9 +1,9 @@
1
1
  import { i as __toESM } from "./rolldown-runtime-DktXgydw.js";
2
- import { $ as FileSearchPanel, $n as validateResolvedThemeName, A as parseEditorPreferences, At as defaultReviewView, Bn as areDiffTargetsEqual, Bt as fileHref, Cr as parseFileChange, D as ReviewActionDialog, E as FileConflictPanel, Er as parseFilePath, F as FileWorkspace, Fn as reviewConflictIdentity, Ft as reviewComparison, Gn as markWorkerErrorReported, Gt as fileRoutes, Hn as WorkerPoolTerminatedError, Ht as readFileLocation, I as defaultFileDestination, In as isDiffPlainText, It as reviewContextKey, Jn as areFileRenderOptionsEqual, Kn as isFilePlainText, L as fileActionIntent, Ln as renderDiffWithHighlighter, Lt as reviewHref, M as FileMutationDialog, Mt as parseReviewSession, N as FileReviewPanel, Nn as createCodeAnchor, Nt as readReviewLocation, O as FileEditorPanel, P as codeThemes, Pn as parseFileUpdate, Pt as rememberReviewPosition, Qn as prepareThemeResolution, R as mutationFileView, Rn as areDiffRenderOptionsEqual, Rt as closeFileTab, Sr as parseFileSearchResult, St as isCodeConversation, Tr as parseFileIndex, Tt as readConversationLocation, Un as WorkerPoolWorkerError, Ut as reconcileFileView, Vn as WorkerPoolTaskCanceledError, Vt as parseFileView, Wn as isHandledWorkerPoolError, Wt as rememberFile, Xn as areThemesEqual, Yn as hasResolvedThemes, Zn as getSharedHighlighter, _r as parseReviewHistory, ar as areLanguagesAttached, br as parseReviewSource, bt as conversationHref, cn as toast, cr as parseReviewActionRecoveries, dr as parseFileSaveResult, er as attachResolvedThemes, f as storeDocument, fr as maxFileSaveBytes, gn as Text, gr as parseReviewDiff, hr as parseRecoveries, ir as resolveLanguage, j as FileRecoveryDialog, k as defaultEditorPreferences, kt as defaultReviewPosition, l as projectStore, ln as readPreference, lr as parseReviewActionResult, m as storedFiles, mn as t, mr as parseMutationResult, nr as getThemes, or as ResolvedLanguages, p as storedDocument, pr as parseMutationPlan, qn as renderFileWithHighlighter, rr as getResolvedOrResolveLanguage, s as applyFileUpdate, sr as parseReviewActionPlan, tr as themeResolver, tt as ConversationSidebar, u as saveFiles, un as writePreference, ur as parseFileSaveRequest, v as projectLive, vr as parseReviewRepository, vt as codeCommentCounts, wr as parseFileDocument, wt as locateCodeConversations, xr as parseFileSearchQuery, y as routes, yr as parseReviewSnapshot, z as remainingMutationIntent, zn as areFilesEqual, zt as defaultFileView } from "./comment-data-CeuJbJKJ.js";
2
+ import { $ as FileSearchPanel, $n as markWorkerErrorReported, A as parseEditorPreferences, Ar as parseFileSearchResult, At as defaultReviewView, Bt as fileHref, Cr as parseRecoveries, D as ReviewActionDialog, Dr as parseReviewSnapshot, E as FileConflictPanel, Er as parseReviewRepository, F as FileWorkspace, Ft as reviewComparison, Gn as renderDiffWithHighlighter, Gt as fileRoutes, Hn as parseFileUpdate, Ht as readFileLocation, I as defaultFileDestination, It as reviewContextKey, Jn as areDiffTargetsEqual, Kn as areDiffRenderOptionsEqual, L as fileActionIntent, Lt as reviewHref, M as FileMutationDialog, Mr as parseFileDocument, Mt as parseReviewSession, N as FileReviewPanel, Nr as parseFileIndex, Nt as readReviewLocation, O as FileEditorPanel, Or as parseReviewSource, P as codeThemes, Pr as parseFilePath, Pt as rememberReviewPosition, Qn as isHandledWorkerPoolError, R as mutationFileView, Rt as closeFileTab, Sr as parseMutationResult, St as isCodeConversation, Tr as parseReviewHistory, Tt as readConversationLocation, Un as reviewConflictIdentity, Ut as reconcileFileView, Vn as createCodeAnchor, Vt as parseFileView, Wn as isDiffPlainText, Wt as rememberFile, Xn as WorkerPoolTerminatedError, Yn as WorkerPoolTaskCanceledError, Zn as WorkerPoolWorkerError, _r as parseReviewActionResult, ar as getSharedHighlighter, br as maxFileSaveBytes, bt as conversationHref, cn as toast, cr as attachResolvedThemes, dr as getResolvedOrResolveLanguage, er as isFilePlainText, f as storeDocument, fr as resolveLanguage, gn as Text, gr as parseReviewActionRecoveries, hr as parseReviewActionPlan, ir as areThemesEqual, j as FileRecoveryDialog, jr as parseFileChange, k as defaultEditorPreferences, kr as parseFileSearchQuery, kt as defaultReviewPosition, l as projectStore, ln as readPreference, lr as themeResolver, m as storedFiles, mn as t, mr as ResolvedLanguages, nr as areFileRenderOptionsEqual, or as prepareThemeResolution, p as storedDocument, pr as areLanguagesAttached, qn as areFilesEqual, rr as hasResolvedThemes, s as applyFileUpdate, sr as validateResolvedThemeName, tr as renderFileWithHighlighter, tt as ConversationSidebar, u as saveFiles, un as writePreference, ur as getThemes, v as projectLive, vr as parseFileSaveRequest, vt as codeCommentCounts, wr as parseReviewDiff, wt as locateCodeConversations, xr as parseMutationPlan, y as routes, yr as parseFileSaveResult, z as remainingMutationIntent, zt as defaultFileView } from "./comment-data-yfWEdDQL.js";
3
3
  import { B as DEFAULT_THEMES, C as getFiletypeFromFileName, S as getCustomExtensionsVersion, x as getCustomExtensionsMap } from "./cloneFileDiffMetadata-C1BcNpxN.js";
4
- import { n as createProjectComments, t as defineScreen } from "./screen-DRTAKkn3.js";
5
4
  import { t as require_lru } from "./lru-C0_oUet4.js";
6
- import { t as ProjectThreadLinks } from "./project-thread-links-Bs_MtfBC.js";
5
+ import { n as createProjectComments, t as defineScreen } from "./screen-DapOVrga.js";
6
+ import { t as ProjectThreadLinks } from "./project-thread-links-m-OLpsD9.js";
7
7
  import { ref } from "remix/component";
8
8
  import { jsx, jsxs } from "remix/component/jsx-runtime";
9
9
  //#region ../../node_modules/.pnpm/@pierre+diffs@1.5.2_@shikijs+themes@4.5.0_react-dom@19.3.1_react@19.3.1__react@19.3.1/node_modules/@pierre/diffs/dist/highlighter/languages/getResolvedLanguages.js
@@ -3271,6 +3271,8 @@ function ProjectFileReview(handle) {
3271
3271
  },
3272
3272
  onClose: closeComments,
3273
3273
  onWrite: comments.write,
3274
+ viewerId: comments.viewer?.id,
3275
+ onDelete: (thread, messageId) => comments.remove(thread.id, messageId),
3274
3276
  onCreate: createComment,
3275
3277
  onCancelDraft: () => {
3276
3278
  draftAnchor = void 0;
@@ -1,7 +1,7 @@
1
- import { Dr as MAX_CANVAS_Y, Kt as pageRoutes, Qt as Spinner, Zt as Loader, _t as PagesWorkspace, gn as Text, in as IconButton, l as projectStore, ln as readPreference, mn as t, rn as Select, un as writePreference } from "./comment-data-CeuJbJKJ.js";
2
- import { n as createProjectComments, t as defineScreen } from "./screen-DRTAKkn3.js";
1
+ import { Fr as MAX_CANVAS_Y, Kt as pageRoutes, Qt as Spinner, Zt as Loader, _t as PagesWorkspace, gn as Text, in as IconButton, l as projectStore, ln as readPreference, mn as t, rn as Select, un as writePreference } from "./comment-data-yfWEdDQL.js";
3
2
  import { n as workspaceTab, t as followWorkspaceTab } from "../node-host.js";
4
- import { n as createProjectPreviews, r as locatePreview, t as createProjectBranches } from "./project-branches-BeEwMBIw.js";
3
+ import { n as createProjectPreviews, r as locatePreview, t as createProjectBranches } from "./project-branches-D7rVCxQX.js";
4
+ import { n as createProjectComments, t as defineScreen } from "./screen-DapOVrga.js";
5
5
  import { array, number, object, optional, parseSafe, string } from "remix/data-schema";
6
6
  import { on, ref } from "remix/component";
7
7
  import { Fragment, jsx, jsxs } from "remix/component/jsx-runtime";
@@ -1,4 +1,4 @@
1
- import { Or as parseElementAnchor, S as writeRows, b as plainRow, c as previewKey, di as parsePreviewPins, l as projectStore, mn as t, oi as parsePreview, si as parsePreviewBranch, v as projectLive, y as routes } from "./comment-data-CeuJbJKJ.js";
1
+ import { Ir as parseElementAnchor, S as writeRows, Si as parsePreviewPins, _i as parsePreview, b as plainRow, c as previewKey, l as projectStore, mn as t, v as projectLive, vi as parsePreviewBranch, y as routes } from "./comment-data-yfWEdDQL.js";
2
2
  import { array, boolean, enum_, nullable, number, object, parseSafe, string } from "remix/data-schema";
3
3
  //#region app/client/project-previews.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
- import { C as ThreadTaskLinks, Cn as parseThread, Dt as taskHref, Gr as valueByRole, Lr as parseWorkItemRef, On as parseThreadLinkInput, _ as requestComments, in as IconButton, kr as parseWorkWorkspace, mn as t, w as ThreadTaskPicker, y as routes } from "./comment-data-CeuJbJKJ.js";
2
- import { n as requestTasks } from "./tasks-request-Doc9y_20.js";
1
+ import { $r as valueByRole, An as parseThread, C as ThreadTaskLinks, Dt as taskHref, Gr as parseWorkItemRef, In as parseThreadLinkInput, Lr as parseWorkWorkspace, _ as requestComments, in as IconButton, mn as t, w as ThreadTaskPicker, y as routes } from "./comment-data-yfWEdDQL.js";
2
+ import { n as requestTasks } from "./tasks-request-B0aeNocI.js";
3
3
  import { on, ref } from "remix/component";
4
4
  import { Fragment, jsx, jsxs } from "remix/component/jsx-runtime";
5
5
  //#region app/client/project-task-picker.tsx
@@ -1,143 +1,20 @@
1
- import { An as parseThreadSnapshot, Cn as parseThread, En as parseThreadId, Tn as parseThreadFilter, _ as requestComments, a as saveThreads, b as plainRow, cn as toast, g as CommentsRequestError, i as saveThread, l as projectStore, mn as t, n as unavailableComments, o as threadMatches, x as writeOptimistically, y as routes } from "./comment-data-CeuJbJKJ.js";
2
- //#region ../work/src/lib/predict.ts
3
- /**
4
- * How a new task shows until its adapter confirms it: the requested values, and the creation
5
- * time for timestamps the adapter sets, under a provisional identity with no revision,
6
- * relations or counts yet.
7
- * @param schema - The board's schema.
8
- * @param id - A provisional identity no adapter uses.
9
- * @param create - The create request.
10
- * @param now - The creation time.
11
- * @returns The provisional task.
12
- */
13
- function predictWorkCreate(schema, id, create, now = /* @__PURE__ */ new Date()) {
14
- let values = { ...create.values };
15
- for (let field of schema.fields) if ((field.role === "created" || field.role === "updated") && values[field.id] === void 0) values[field.id] = field.kind === "date" ? now.toISOString().slice(0, 10) : now.toISOString();
16
- return {
17
- id,
18
- revision: null,
19
- values,
20
- relations: [],
21
- counts: {},
22
- contentLoaded: false,
23
- extra: null
24
- };
25
- }
26
- /**
27
- * How a task shows until its adapter confirms an edit: the edited values over its own.
28
- * @param item - The task as last confirmed.
29
- * @param update - The update request.
30
- * @returns The predicted task.
31
- */
32
- function predictWorkUpdate(item, update) {
33
- return {
34
- ...item,
35
- values: {
36
- ...item.values,
37
- ...update.values
38
- }
39
- };
40
- }
41
- var targets = {
42
- close: ["done", "cancelled"],
43
- reopen: [
44
- "unstarted",
45
- "backlog",
46
- "started"
47
- ]
48
- };
49
- /**
50
- * How a task shows until its adapter confirms an action. Closing and reopening move it to
51
- * the schema's first matching status; otherwise only input naming the task's own fields
52
- * applies, since an action's effects belong to the adapter.
53
- * @param schema - The board's schema.
54
- * @param item - The task as last confirmed.
55
- * @param action - The action request.
56
- * @returns The predicted task.
57
- */
58
- function predictWorkAction(schema, item, action) {
59
- let fields = new Set(schema.fields.map((field) => field.id));
60
- let values = { ...item.values };
61
- for (let [id, value] of Object.entries(action.input)) if (fields.has(id)) values[id] = value;
62
- let kind = schema.actions.find((candidate) => candidate.id === action.action)?.kind;
63
- let categories = kind ? targets[kind] : void 0;
64
- let field = schema.fields.find((candidate) => candidate.role === "status");
65
- let status = categories?.map((category) => schema.statuses.find((candidate) => candidate.category === category)).find(Boolean);
66
- if (field && status) values[field.id] = status.value;
67
- return {
68
- ...item,
69
- values
70
- };
71
- }
72
- /** Who a message shows as until the server, which derives authors from Git, confirms it. */
73
- var pendingAuthor = {
74
- id: "pending",
75
- name: "You",
76
- email: ""
77
- };
78
- function message(id, body, parentId, now) {
79
- return {
80
- id,
81
- body: body.trim(),
82
- author: pendingAuthor,
83
- createdAt: now.toISOString(),
84
- parentId
85
- };
86
- }
87
- /**
88
- * How a new discussion shows until its adapter confirms it.
89
- * @param id - A provisional identity, also used for its first message.
90
- * @param input - The create request.
91
- * @param now - The creation time.
92
- * @returns The provisional discussion.
93
- */
94
- function predictThreadCreate(id, input, now = /* @__PURE__ */ new Date()) {
95
- let createdAt = now.toISOString();
96
- return {
97
- id,
98
- status: "open",
99
- anchor: input.anchor,
100
- itemIds: [...input.itemIds],
101
- messages: [message(id, input.body, null, now)],
102
- messageCount: 1,
103
- messagesLoaded: true,
104
- messagesTruncated: false,
105
- createdAt,
106
- updatedAt: createdAt,
107
- revision: null
1
+ import { An as parseThread, Cn as predictThreadStatus, Nn as parseThreadFilter, Pn as parseThreadId, Rn as parseThreadSnapshot, S as writeRows, Sn as predictThreadReply, _ as requestComments, a as saveThreads, b as plainRow, bn as predictThreadCreate, cn as toast, g as CommentsRequestError, i as saveThread, l as projectStore, mn as t, n as unavailableComments, o as threadMatches, x as writeOptimistically, xn as predictThreadDelete, y as routes } from "./comment-data-yfWEdDQL.js";
2
+ //#region app/client/project-comments.ts
3
+ var viewers = /* @__PURE__ */ new Map();
4
+ function parseViewer(value) {
5
+ let viewer = value && typeof value === "object" && "viewer" in value ? value.viewer : void 0;
6
+ if (viewer === null) return null;
7
+ if (viewer && typeof viewer === "object" && "id" in viewer && "name" in viewer && typeof viewer.id === "string" && typeof viewer.name === "string" && viewer.id.length <= 256 && viewer.name.length <= 1e3) return {
8
+ id: viewer.id,
9
+ name: viewer.name
108
10
  };
11
+ throw new TypeError("Invalid viewer.");
109
12
  }
110
- /**
111
- * How a discussion shows until its adapter confirms a reply.
112
- * @param thread - The discussion as last confirmed.
113
- * @param id - A provisional message identity.
114
- * @param input - The reply request.
115
- * @param now - The reply time.
116
- * @returns The predicted discussion.
117
- */
118
- function predictThreadReply(thread, id, input, now = /* @__PURE__ */ new Date()) {
119
- return {
120
- ...thread,
121
- messages: thread.messagesLoaded ? [...thread.messages, message(id, input.body, input.parentId, now)] : thread.messages,
122
- messageCount: thread.messageCount + 1,
123
- updatedAt: now.toISOString()
124
- };
13
+ function parseDeletion(value) {
14
+ if (!value || typeof value !== "object" || !("thread" in value)) throw new TypeError("Invalid deletion.");
15
+ return { thread: value.thread === null ? null : parseThread(value.thread) };
125
16
  }
126
17
  /**
127
- * How a discussion shows until its adapter confirms a status change.
128
- * @param thread - The discussion as last confirmed.
129
- * @param input - The status request.
130
- * @returns The predicted discussion.
131
- */
132
- function predictThreadStatus(thread, input) {
133
- return {
134
- ...thread,
135
- status: input.status
136
- };
137
- }
138
- //#endregion
139
- //#region app/client/project-comments.ts
140
- /**
141
18
  * Reads one task's discussions with their messages in a single round, so the task page
142
19
  * renders complete instead of loading its discussion after it appears.
143
20
  * @param token - The current companion capability.
@@ -245,6 +122,21 @@ function createProjectComments(options) {
245
122
  let generation = 0, reading;
246
123
  let details = /* @__PURE__ */ new Map();
247
124
  let detailFailures = /* @__PURE__ */ new Set();
125
+ let viewer = null, askedViewer = false;
126
+ function askViewer() {
127
+ if (askedViewer || !snapshot.capabilities.delete) return;
128
+ askedViewer = true;
129
+ let pending = viewers.get(options.token);
130
+ if (!pending) {
131
+ pending = requestComments(routes.comments.viewer.href(), options.token, new AbortController().signal, parseViewer, options.fetcher).catch(() => null);
132
+ viewers.set(options.token, pending);
133
+ }
134
+ pending.then((value) => {
135
+ viewer = value;
136
+ changed();
137
+ });
138
+ }
139
+ askViewer();
248
140
  let opened = Promise.resolve(options.store).then(async (ready) => {
249
141
  store = ready;
250
142
  if (options.initial?.snapshot && !options.initial.stored) await saveThreads(ready, options.initial.snapshot, filter);
@@ -264,6 +156,7 @@ function createProjectComments(options) {
264
156
  loaded = true;
265
157
  now = Date.now();
266
158
  version++;
159
+ askViewer();
267
160
  changed();
268
161
  }
269
162
  function request(url, signal, parse, body) {
@@ -281,6 +174,12 @@ function createProjectComments(options) {
281
174
  await saveThread(store, thread);
282
175
  view();
283
176
  }
177
+ async function forget(id) {
178
+ await opened;
179
+ members?.delete(id);
180
+ await writeRows(store.threads, { delete: [id] });
181
+ view();
182
+ }
284
183
  function includes(thread) {
285
184
  return threadMatches(thread, filter);
286
185
  }
@@ -334,6 +233,10 @@ function createProjectComments(options) {
334
233
  if (!detailFailures.size) error = "";
335
234
  }
336
235
  } catch (cause) {
236
+ if (!signal.aborted && turn === generation && cause instanceof CommentsRequestError && cause.code === "not-found") {
237
+ await forget(id);
238
+ return;
239
+ }
337
240
  if (!signal.aborted && turn === generation) {
338
241
  detailFailures.add(id);
339
242
  error = cause instanceof Error ? cause.message : t("app.client.projectComments.couldNotLoadDiscussion");
@@ -413,6 +316,13 @@ function createProjectComments(options) {
413
316
  get ready() {
414
317
  return loaded && snapshot.connection.state === "ready";
415
318
  },
319
+ /**
320
+ * Who this browser comments as, once the server said.
321
+ * @returns The viewer, or null until then or without one.
322
+ */
323
+ get viewer() {
324
+ return viewer;
325
+ },
416
326
  loadingThread: (id) => details.has(id),
417
327
  load,
418
328
  loadThread,
@@ -474,6 +384,31 @@ function createProjectComments(options) {
474
384
  thread: saved
475
385
  };
476
386
  },
387
+ /**
388
+ * Deletes one of the viewer's messages; its first message deletes the whole discussion.
389
+ * @param threadId - The discussion.
390
+ * @param messageId - The message to delete.
391
+ * @returns The snapshot after the confirmed deletion.
392
+ */
393
+ async remove(threadId, messageId) {
394
+ requireCapability("delete");
395
+ let id = parseThreadId(threadId), thread = snapshot.threads.find((item) => item.id === id);
396
+ if (!thread?.revision) throw new Error(t("app.client.projectComments.reloadDiscussionBeforeSaving"));
397
+ let next = predictThreadDelete(thread, messageId);
398
+ return operate(routes.comments.deleteMessage.href({ id }), {
399
+ messageId,
400
+ expectedRevision: thread.revision
401
+ }, parseDeletion, (result) => {
402
+ if (!result.thread) return forget(id);
403
+ if (result.thread.id !== id) throw new CommentsRequestError(t("app.client.projectComments.refreshBeforeTryingAgain"), 409, "uncertain");
404
+ return merge(result.thread);
405
+ }, next ? t("app.client.projectComments.commentDeleted") : t("app.client.projectComments.discussionDeleted"), id, (store) => {
406
+ if (next && store.threads.has(id)) store.threads.update(id, (draft) => {
407
+ draft.messages = next.messages;
408
+ draft.messageCount = next.messageCount;
409
+ });
410
+ });
411
+ },
477
412
  async sync() {
478
413
  requireCapability("sync");
479
414
  return operate(routes.comments.sync.href(), { direction: "both" }, parseThreadSnapshot, adopt, t("app.client.projectComments.commentsSynced"));
@@ -491,4 +426,4 @@ function defineScreen(screen) {
491
426
  return screen;
492
427
  }
493
428
  //#endregion
494
- export { predictWorkAction as a, loadItemComments as i, createProjectComments as n, predictWorkCreate as o, loadComments as r, predictWorkUpdate as s, defineScreen as t };
429
+ export { loadItemComments as i, createProjectComments as n, loadComments as r, defineScreen as t };
@@ -1,8 +1,8 @@
1
- import { $t as Button, Ar as parseSectionData, B as TaskWorkspace, C as ThreadTaskLinks, Dt as taskHref, Et as selectedTask, Fr as parseWorkDetail, Hr as parseWorkSyncResult, Jt as CommentThreadDetail, Ot as taskRole, Rr as parseWorkMutationResult, S as writeRows, T as ItemDiscussion, Tt as readConversationLocation, Xt as CommentComposer, Yt as CommentThreadItem, b as plainRow, cn as toast, gn as Text, h as storedWorkspace, l as projectStore, mn as t, nt as CommentThreadHeading, qt as taskRoutes, x as writeOptimistically, xt as conversationLabel, y as routes } from "./comment-data-CeuJbJKJ.js";
2
- import { a as predictWorkAction, i as loadItemComments, n as createProjectComments, o as predictWorkCreate, s as predictWorkUpdate, t as defineScreen } from "./screen-DRTAKkn3.js";
3
- import { n as requestTasks, t as TaskRequestError } from "./tasks-request-Doc9y_20.js";
1
+ import { $t as Button, B as TaskWorkspace, C as ThreadTaskLinks, Dt as taskHref, En as predictWorkUpdate, Et as selectedTask, Jt as CommentThreadDetail, Kr as parseWorkMutationResult, Ot as taskRole, Rr as parseSectionData, S as writeRows, T as ItemDiscussion, Tn as predictWorkCreate, Tt as readConversationLocation, Ur as parseWorkDetail, Xr as parseWorkSyncResult, Xt as CommentComposer, Yt as CommentThreadItem, b as plainRow, cn as toast, gn as Text, h as storedWorkspace, l as projectStore, mn as t, nt as CommentThreadHeading, qt as taskRoutes, wn as predictWorkAction, x as writeOptimistically, xt as conversationLabel, y as routes } from "./comment-data-yfWEdDQL.js";
2
+ import { n as requestTasks, t as TaskRequestError } from "./tasks-request-B0aeNocI.js";
4
3
  import { r as projectWork } from "../node-host.js";
5
- import { n as ProjectTaskPicker, t as ProjectThreadLinks } from "./project-thread-links-Bs_MtfBC.js";
4
+ import { i as loadItemComments, n as createProjectComments, t as defineScreen } from "./screen-DapOVrga.js";
5
+ import { n as ProjectTaskPicker, t as ProjectThreadLinks } from "./project-thread-links-m-OLpsD9.js";
6
6
  import { on, ref } from "remix/component";
7
7
  import { Fragment, jsx, jsxs } from "remix/component/jsx-runtime";
8
8
  //#region app/client/task-discussion.tsx
@@ -195,7 +195,14 @@ function TaskDiscussion(handle) {
195
195
  id: thread.id,
196
196
  body,
197
197
  parentId
198
- }) : void 0
198
+ }) : void 0,
199
+ viewerId: comments.viewer?.id,
200
+ onDelete: capabilities.delete ? async (messageId) => {
201
+ let whole = thread.messages[0]?.id === messageId;
202
+ await comments.remove(thread.id, messageId);
203
+ if (whole && selected === thread.id) selected = void 0;
204
+ handle.update();
205
+ } : void 0
199
206
  })
200
207
  }, thread.id)) : /* @__PURE__ */ jsx(Text, {
201
208
  variant: "ui-caption",
@@ -1,4 +1,4 @@
1
- import { Lr as parseWorkItemRef, mn as t, y as routes, zr as parseWorkQuery } from "./comment-data-CeuJbJKJ.js";
1
+ import { Gr as parseWorkItemRef, mn as t, qr as parseWorkQuery, y as routes } from "./comment-data-yfWEdDQL.js";
2
2
  import { createMultiMatcher } from "remix/route-pattern/match";
3
3
  //#region app/client/tasks-request.ts
4
4
  var reads = createMultiMatcher();