@bud.sh/app 0.5.1 → 0.6.0
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/README.md +41 -31
- package/dist/client/assets/bud/design.js +1 -1
- package/dist/client/assets/canvas-Bih9EdKJ.js +1 -0
- package/dist/client/assets/difference-worker-Bzg-rizy.js +8 -0
- package/dist/client/assets/dist-CdE5wBkg.js +5 -0
- package/dist/client/assets/files-1dLuoM2f.js +1 -0
- package/dist/client/assets/i18n-CkO6VFI0.js +1 -0
- package/dist/client/assets/index-Cn2mxroU.css +2 -0
- package/dist/client/assets/{index-Dn2pfmXf.js → index-DtISWZp9.js} +2 -2
- package/dist/client/assets/pages-BJ-tNJ4J.js +1 -0
- package/dist/client/assets/{project-branches-CMGRmrqr.js → project-branches-Ce3rXrkc.js} +1 -1
- package/dist/client/assets/project-comments-CPUONfs8.js +1 -0
- package/dist/client/assets/project-thread-links-C-TGkq59.js +1 -0
- package/dist/client/assets/{project-threads-ZbKDh78d.js → project-threads-Bv7IDBgi.js} +105 -105
- package/dist/client/assets/project-work-DAE9-olg.js +1 -0
- package/dist/client/assets/{task-design-BPpc4Gd7.js → task-design-CwySIAOl.js} +3 -3
- package/dist/client/assets/tasks-KWsCLi_h.js +3 -0
- package/dist/client/assets/wasm-inline-DSUyFn1L.js +1 -0
- package/dist/client/assets/workspace-CVdcfaaB.js +2 -0
- package/dist/client/index.html +3 -3
- package/dist/hosted/assets/{canvas-BYTOp1p7.js → canvas-AII16n9i.js} +97 -6
- package/dist/hosted/assets/{comment-data-Cn5y2VxV.js → comment-data-BXfMElfx.js} +1998 -381
- package/dist/hosted/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/hosted/assets/{files-B1P2_24q.js → files-D_1yeAVO.js} +10 -5
- package/dist/hosted/assets/{pages-B0ZlVdci.js → pages-CN_SbKWA.js} +3 -3
- package/dist/hosted/assets/{project-branches-DlCWmfRo.js → project-branches-Bxp0DCwg.js} +1 -1
- package/dist/hosted/assets/{project-thread-links-BZXQ200n.js → project-thread-links-KqQoKBLE.js} +76 -4
- package/dist/{node-host/assets/screen-DapOVrga.js → hosted/assets/screen-DpAcDUKR.js} +25 -1
- package/dist/hosted/assets/tasks-D66LlwxM.js +1423 -0
- package/dist/hosted/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/hosted/hosted.js +7885 -6219
- package/dist/node/assets/Stream-CAm1eJ2n.js +16604 -0
- package/dist/{server/assets/bridge-B_Blt55E.js → node/assets/bridge-DQSBYHmw.js} +124 -290
- package/dist/node/assets/config-9_jWwNBh.js +1067 -0
- package/dist/node/assets/load-config.server-CMVtRVmO.js +261 -0
- package/dist/node/assets/module-3GgtT4Xs.js +308 -0
- package/dist/node/assets/server-DTQFuoX2.js +846 -0
- package/dist/node/assets/src-D-kG1GxB.js +2 -0
- package/dist/node/assets/{src-CerrKjID.js → src-Dwe9APUx.js} +93 -3
- package/dist/node/assets/src-DysjfbM8.js +8592 -0
- package/dist/node/bridge.js +1 -845
- package/dist/node/cli.js +206 -0
- package/dist/node/design.js +811 -8
- package/dist/node/index.js +2 -739
- package/dist/node/plugins.js +5711 -0
- package/dist/node/server.js +1 -6746
- package/dist/node/vite.js +121 -0
- package/dist/{server/assets/canvas-CkMK4hoN.js → node-host/assets/canvas-BeDoufy1.js} +97 -7
- package/dist/node-host/assets/{comment-data-yfWEdDQL.js → comment-data-DE2T9yTj.js} +1974 -355
- package/dist/node-host/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/node-host/assets/{files-anHi-apm.js → files-Dl-PTS09.js} +11 -6
- package/dist/node-host/assets/{pages-CW4aF6dK.js → pages-DL2sk6_U.js} +3 -3
- package/dist/node-host/assets/{project-branches-D7rVCxQX.js → project-branches-DGp5ELct.js} +1 -1
- package/dist/{server/assets/project-thread-links-lLS_rq7c.js → node-host/assets/project-thread-links-CgBV3Jcm.js} +76 -5
- package/dist/{hosted/assets/screen-CfdC3NbR.js → node-host/assets/screen-B55yyVfb.js} +25 -1
- package/dist/node-host/assets/tasks-DZRNkoxe.js +1425 -0
- package/dist/node-host/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/node-host/node-host.js +6127 -4438
- package/dist/server/assets/{branch-previews-ClkoJ0lM.js → branch-previews-DedrpnVR.js} +1555 -673
- package/dist/{node-host/assets/canvas-DxmcFZCh.js → server/assets/canvas-CJWQ-jjv.js} +98 -6
- package/dist/server/assets/{comment-data-NLeHG89b.js → comment-data-Rg42ToTW.js} +1660 -26
- package/dist/server/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/server/assets/{events-iHpLGr3k.js → events-BH_rAu7O.js} +155 -20
- package/dist/server/assets/{files-CRdl489E.js → files-CPJqKeMo.js} +12 -7
- package/dist/server/assets/{listener-BAYn9Q-u.js → listener-D61O6PzF.js} +708 -12
- package/dist/server/assets/{pages-DXVlJlBC.js → pages-KJY_Ba92.js} +4 -4
- package/dist/server/assets/{project-branches-CwTsf6WQ.js → project-branches-qG9ACZvo.js} +2 -2
- package/dist/{node-host/assets/project-thread-links-m-OLpsD9.js → server/assets/project-thread-links-CqK0lqCF.js} +77 -4
- package/dist/{node-host/assets/tasks-request-B0aeNocI.js → server/assets/project-work-DoIwxWMm.js} +87 -2
- package/dist/server/assets/run-BJ8r5jSh.js +572 -0
- package/dist/server/assets/{screen-B1zH_1O3.js → screen-CBrHPFug.js} +26 -2
- package/dist/server/assets/{server-DxK-UB_Z.js → server-Dkbp5stG.js} +1521 -793
- package/dist/server/assets/{services-BRrgXYlT.js → services-BTfig8A8.js} +234 -997
- package/dist/server/assets/tasks-Bgu5Lg9Y.js +1427 -0
- package/dist/server/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/server/capture.js +1 -2
- package/dist/server/migration.js +569 -12
- package/dist/server/runtime.js +7 -5
- package/dist/server/server.js +3 -3
- package/dist/types/assets/{config-DIC2cf_S.d.ts → config-CvcYmE2s.d.ts} +188 -50
- package/dist/types/assets/{contract-CAT-XFNq.d.ts → contract-B7yuuqSn.d.ts} +44 -2
- package/dist/types/assets/{items-CaVkByHk.d.ts → items-BQdaVksf.d.ts} +12 -1
- package/dist/types/assets/memory-state-BBW-FAVA.d.ts +236 -0
- package/dist/types/assets/picker-Be5Gx4WR.d.ts +9 -0
- package/dist/types/assets/{task-design-xJOhRNWh.d.ts → task-design-BDHKFHpp.d.ts} +3 -1
- package/dist/types/bridge.d.ts +1 -1
- package/dist/types/companion.d.ts +3 -3
- package/dist/types/design.d.ts +2 -2
- package/dist/types/hosted.d.ts +3 -3
- package/dist/types/index.d.ts +2 -2
- package/dist/types/node-host.d.ts +2 -2
- package/dist/types/plugins.d.ts +445 -0
- package/dist/types/screen.d.ts +2 -2
- package/dist/types/server.d.ts +2 -228
- package/dist/types/vite.d.ts +16 -0
- package/package.json +19 -3
- package/dist/client/assets/canvas-8GXJIuDK.js +0 -1
- package/dist/client/assets/difference-worker-C99ychYw.js +0 -8
- package/dist/client/assets/files-DkWt1l9H.js +0 -1
- package/dist/client/assets/i18n-hH_Ba5QO.js +0 -1
- package/dist/client/assets/index-tpHYVXma.css +0 -2
- package/dist/client/assets/pages-BVd6MQmu.js +0 -1
- package/dist/client/assets/project-comments-D1IIQwsW.js +0 -1
- package/dist/client/assets/project-thread-links-DKwe_SYb.js +0 -1
- package/dist/client/assets/project-work-D4DZqBCZ.js +0 -1
- package/dist/client/assets/tasks-Cn0VOZ9Z.js +0 -1
- package/dist/client/assets/tasks-request-BLyOzokZ.js +0 -1
- package/dist/client/assets/workspace-DGErsSg6.js +0 -2
- package/dist/hosted/assets/tasks-_yKh2Z6q.js +0 -649
- package/dist/hosted/assets/tasks-request-CFPLvLc7.js +0 -113
- package/dist/node-host/assets/tasks-BBK_CybR.js +0 -651
- package/dist/server/assets/project-work-CSZlCwH-.js +0 -88
- package/dist/server/assets/tasks-CGdz-uox.js +0 -652
- package/dist/server/assets/tasks-request-D2NBjCTl.js +0 -115
- package/dist/types/assets/picker-D1Bw3ndx.d.ts +0 -14
|
@@ -1,649 +0,0 @@
|
|
|
1
|
-
import { $t as Button, B as TaskWorkspace, C as ThreadTaskLinks, Dt as taskHref, Et as selectedTask, Jt as CommentThreadDetail, Mi as parseWorkSyncResult, Oi as parseWorkMutationResult, Ot as taskRole, Pn as Fragment, Rn as jsx, S as writeRows, T as ItemDiscussion, Ti as parseWorkDetail, Tt as readConversationLocation, Xt as CommentComposer, Yt as CommentThreadItem, b as plainRow, bi as parseSectionData, cn as toast, cr as predictWorkUpdate, gn as Text, h as storedWorkspace, l as projectStore, mn as t, nt as CommentThreadHeading, or as predictWorkAction, qt as taskRoutes, sr as predictWorkCreate, vn as ref, x as writeOptimistically, xn as on, xt as conversationLabel, y as routes } from "./comment-data-Cn5y2VxV.js";
|
|
2
|
-
import { n as requestTasks, t as TaskRequestError } from "./tasks-request-CFPLvLc7.js";
|
|
3
|
-
import { r as projectWork } from "../hosted.js";
|
|
4
|
-
import { i as loadItemComments, n as createProjectComments, t as defineScreen } from "./screen-CfdC3NbR.js";
|
|
5
|
-
import { n as ProjectTaskPicker, t as ProjectThreadLinks } from "./project-thread-links-BZXQ200n.js";
|
|
6
|
-
//#region app/client/task-discussion.tsx
|
|
7
|
-
/**
|
|
8
|
-
* Owns a task's filtered discussions and lazy detail requests within its mounted lifetime.
|
|
9
|
-
* @param handle - The trusted project capability and currently displayed work-item ID.
|
|
10
|
-
* @returns The discussion block supplied to the task design.
|
|
11
|
-
*/
|
|
12
|
-
function TaskDiscussion(handle) {
|
|
13
|
-
let requested = handle.props.conversationId;
|
|
14
|
-
let selected = requested;
|
|
15
|
-
let explicit = Boolean(requested);
|
|
16
|
-
let creating = false;
|
|
17
|
-
let picking = false;
|
|
18
|
-
let draftItemIds = [handle.props.itemId];
|
|
19
|
-
let comments = createProjectComments({
|
|
20
|
-
store: projectStore(handle.props.project),
|
|
21
|
-
token: handle.props.project.token,
|
|
22
|
-
signal: handle.signal,
|
|
23
|
-
filter: { itemId: handle.props.itemId },
|
|
24
|
-
initial: handle.props.initial,
|
|
25
|
-
onChange() {
|
|
26
|
-
if (!handle.signal.aborted) handle.update();
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
if (comments.loaded) selected ??= comments.snapshot.threads[0]?.id;
|
|
30
|
-
let mount = ref(() => {
|
|
31
|
-
if (comments.loaded) return;
|
|
32
|
-
comments.load().then(() => {
|
|
33
|
-
selected ??= comments.snapshot.threads[0]?.id;
|
|
34
|
-
if (!handle.signal.aborted) handle.update();
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
async function write(change) {
|
|
38
|
-
let result = await comments.write(change);
|
|
39
|
-
await handle.props.onChange?.();
|
|
40
|
-
return result;
|
|
41
|
-
}
|
|
42
|
-
function threadLinks(thread) {
|
|
43
|
-
return {
|
|
44
|
-
project: handle.props.project,
|
|
45
|
-
thread,
|
|
46
|
-
items: handle.props.items,
|
|
47
|
-
canLink: comments.snapshot.capabilities.link,
|
|
48
|
-
busy: comments.busy,
|
|
49
|
-
onChange: async () => {
|
|
50
|
-
await comments.load();
|
|
51
|
-
if (comments.snapshot.threads.some((item) => item.id === thread.id)) await comments.loadThread(thread.id, true);
|
|
52
|
-
await handle.props.onChange?.();
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function links(thread) {
|
|
57
|
-
return /* @__PURE__ */ jsx("div", {
|
|
58
|
-
class: "flex items-start justify-between gap-2",
|
|
59
|
-
children: [handle.props.renderThreadLinks ? handle.props.renderThreadLinks(thread) : /* @__PURE__ */ jsx(ProjectThreadLinks, {
|
|
60
|
-
part: thread.anchor ? "links" : void 0,
|
|
61
|
-
...threadLinks(thread)
|
|
62
|
-
}), !thread.anchor && comments.snapshot.capabilities.reply && comments.snapshot.threads.length > 1 ? /* @__PURE__ */ jsx(Button, {
|
|
63
|
-
variant: "ghost",
|
|
64
|
-
size: "small",
|
|
65
|
-
disabled: comments.busy,
|
|
66
|
-
"aria-pressed": !creating && selected === thread.id,
|
|
67
|
-
mix: on("click", () => {
|
|
68
|
-
selected = thread.id;
|
|
69
|
-
explicit = true;
|
|
70
|
-
creating = false;
|
|
71
|
-
handle.update();
|
|
72
|
-
}),
|
|
73
|
-
children: t("app.client.taskDiscussion.reply")
|
|
74
|
-
}) : null]
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
function heading(thread) {
|
|
78
|
-
return /* @__PURE__ */ jsx(CommentThreadHeading, {
|
|
79
|
-
location: thread.anchor ? conversationLabel(thread) : t("app.client.taskDiscussion.discussion"),
|
|
80
|
-
actions: handle.props.renderThreadLinks ? void 0 : /* @__PURE__ */ jsx(ProjectThreadLinks, {
|
|
81
|
-
part: "action",
|
|
82
|
-
...threadLinks(thread)
|
|
83
|
-
}),
|
|
84
|
-
status: thread.status,
|
|
85
|
-
busy: comments.busy,
|
|
86
|
-
onStatusChange: comments.snapshot.capabilities.status ? (status) => {
|
|
87
|
-
write({
|
|
88
|
-
id: thread.id,
|
|
89
|
-
status
|
|
90
|
-
}).catch(() => {});
|
|
91
|
-
} : void 0
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
return () => {
|
|
95
|
-
if (requested !== handle.props.conversationId) {
|
|
96
|
-
requested = handle.props.conversationId;
|
|
97
|
-
explicit = Boolean(requested);
|
|
98
|
-
if (requested) {
|
|
99
|
-
selected = requested;
|
|
100
|
-
creating = false;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
let { threads, capabilities } = comments.snapshot;
|
|
104
|
-
let reply = creating ? void 0 : threads.find((thread) => thread.id === selected && !thread.anchor) ?? (explicit ? void 0 : threads.find((thread) => !thread.anchor && thread.status !== "resolved") ?? (!capabilities.create ? threads.find((thread) => !thread.anchor) : void 0));
|
|
105
|
-
let replyPending = !creating && (explicit && (!reply || !capabilities.reply) || Boolean(reply && !reply.messagesLoaded));
|
|
106
|
-
if (comments.ready && !comments.busy && !comments.loading) handle.queueTask(() => {
|
|
107
|
-
if (explicit && selected) comments.loadThread(selected);
|
|
108
|
-
for (let thread of comments.snapshot.threads) if (!thread.messagesLoaded && (!thread.anchor || thread.id === selected)) comments.loadThread(thread.id);
|
|
109
|
-
});
|
|
110
|
-
let count = threads.reduce((sum, thread) => sum + thread.messageCount, 0);
|
|
111
|
-
return /* @__PURE__ */ jsx("section", {
|
|
112
|
-
class: "flex min-w-0 flex-col gap-3",
|
|
113
|
-
"aria-label": t("app.client.taskDiscussion.taskDiscussion"),
|
|
114
|
-
mix: mount,
|
|
115
|
-
children: [
|
|
116
|
-
/* @__PURE__ */ jsx("div", {
|
|
117
|
-
class: "flex items-center gap-2",
|
|
118
|
-
children: [
|
|
119
|
-
/* @__PURE__ */ jsx(Text, {
|
|
120
|
-
as: "h3",
|
|
121
|
-
variant: "ui",
|
|
122
|
-
weight: "medium",
|
|
123
|
-
children: t("app.client.taskDiscussion.discussion")
|
|
124
|
-
}),
|
|
125
|
-
count ? /* @__PURE__ */ jsx(Text, {
|
|
126
|
-
variant: "ui-caption",
|
|
127
|
-
tone: "muted",
|
|
128
|
-
children: count
|
|
129
|
-
}) : null,
|
|
130
|
-
comments.ready && capabilities.create && threads.length ? /* @__PURE__ */ jsx(Button, {
|
|
131
|
-
variant: "ghost",
|
|
132
|
-
size: "small",
|
|
133
|
-
class: "ms-auto",
|
|
134
|
-
disabled: comments.busy || creating,
|
|
135
|
-
mix: on("click", () => {
|
|
136
|
-
creating = true;
|
|
137
|
-
handle.update();
|
|
138
|
-
}),
|
|
139
|
-
children: t("app.client.taskDiscussion.newDiscussion")
|
|
140
|
-
}) : null
|
|
141
|
-
]
|
|
142
|
-
}),
|
|
143
|
-
comments.error ? /* @__PURE__ */ jsx("div", {
|
|
144
|
-
class: "flex flex-col items-start gap-2",
|
|
145
|
-
children: [/* @__PURE__ */ jsx(Text, {
|
|
146
|
-
variant: "ui-caption",
|
|
147
|
-
role: "alert",
|
|
148
|
-
children: comments.error
|
|
149
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
150
|
-
variant: "secondary",
|
|
151
|
-
size: "small",
|
|
152
|
-
mix: on("click", () => {
|
|
153
|
-
comments.load().then(() => selected ? comments.loadThread(selected, true) : void 0);
|
|
154
|
-
}),
|
|
155
|
-
children: t("app.client.taskDiscussion.reloadComments")
|
|
156
|
-
})]
|
|
157
|
-
}) : null,
|
|
158
|
-
!comments.loaded ? /* @__PURE__ */ jsx(Text, {
|
|
159
|
-
variant: "ui-caption",
|
|
160
|
-
tone: "muted",
|
|
161
|
-
role: "status",
|
|
162
|
-
children: t("app.client.taskDiscussion.loadingDiscussion")
|
|
163
|
-
}) : !comments.ready ? /* @__PURE__ */ jsx(Text, {
|
|
164
|
-
variant: "ui-caption",
|
|
165
|
-
tone: "muted",
|
|
166
|
-
children: t("app.client.taskDiscussion.commentsUnavailable")
|
|
167
|
-
}) : /* @__PURE__ */ jsx(Fragment, { children: [threads.length ? threads.map((thread) => !thread.anchor ? /* @__PURE__ */ jsx(ItemDiscussion, {
|
|
168
|
-
thread,
|
|
169
|
-
links: links(thread)
|
|
170
|
-
}, thread.id) : /* @__PURE__ */ jsx(CommentThreadItem, {
|
|
171
|
-
id: thread.id,
|
|
172
|
-
expanded: selected === thread.id,
|
|
173
|
-
heading: heading(thread),
|
|
174
|
-
messages: thread.messages,
|
|
175
|
-
messageCount: thread.messageCount,
|
|
176
|
-
now: comments.now,
|
|
177
|
-
onExpand: () => {
|
|
178
|
-
selected = thread.id;
|
|
179
|
-
explicit = false;
|
|
180
|
-
comments.loadThread(thread.id);
|
|
181
|
-
handle.update();
|
|
182
|
-
},
|
|
183
|
-
renderDetail: () => /* @__PURE__ */ jsx(CommentThreadDetail, {
|
|
184
|
-
heading: heading(thread),
|
|
185
|
-
messages: thread.messages,
|
|
186
|
-
messageCount: thread.messageCount,
|
|
187
|
-
messagesLoaded: thread.messagesLoaded,
|
|
188
|
-
messagesTruncated: thread.messagesTruncated,
|
|
189
|
-
now: comments.now,
|
|
190
|
-
busy: comments.busy,
|
|
191
|
-
links: links(thread),
|
|
192
|
-
onReply: capabilities.reply ? (body, parentId) => write({
|
|
193
|
-
id: thread.id,
|
|
194
|
-
body,
|
|
195
|
-
parentId
|
|
196
|
-
}) : void 0,
|
|
197
|
-
viewerId: comments.viewer?.id,
|
|
198
|
-
onDelete: capabilities.delete ? async (messageId) => {
|
|
199
|
-
let whole = thread.messages[0]?.id === messageId;
|
|
200
|
-
await comments.remove(thread.id, messageId);
|
|
201
|
-
if (whole && selected === thread.id) selected = void 0;
|
|
202
|
-
handle.update();
|
|
203
|
-
} : void 0
|
|
204
|
-
})
|
|
205
|
-
}, thread.id)) : /* @__PURE__ */ jsx(Text, {
|
|
206
|
-
variant: "ui-caption",
|
|
207
|
-
tone: "muted",
|
|
208
|
-
children: t("app.client.taskDiscussion.noDiscussionYet")
|
|
209
|
-
}), capabilities.create || capabilities.reply && (explicit || threads.some((thread) => !thread.anchor)) ? /* @__PURE__ */ jsx("div", {
|
|
210
|
-
class: "flex min-w-0 flex-col gap-2",
|
|
211
|
-
children: [capabilities.create && (creating || !explicit && !threads.length) ? /* @__PURE__ */ jsx("div", {
|
|
212
|
-
class: "flex items-start justify-between gap-2",
|
|
213
|
-
"aria-label": t("app.client.taskDiscussion.newDiscussionTasks"),
|
|
214
|
-
children: [/* @__PURE__ */ jsx(ThreadTaskLinks, {
|
|
215
|
-
itemIds: draftItemIds,
|
|
216
|
-
items: handle.props.items ?? [],
|
|
217
|
-
href: taskHref,
|
|
218
|
-
busy: comments.busy,
|
|
219
|
-
onEdit: capabilities.link ? () => {
|
|
220
|
-
picking = true;
|
|
221
|
-
handle.update();
|
|
222
|
-
} : void 0
|
|
223
|
-
}), creating ? /* @__PURE__ */ jsx(Button, {
|
|
224
|
-
variant: "ghost",
|
|
225
|
-
size: "small",
|
|
226
|
-
disabled: comments.busy,
|
|
227
|
-
mix: on("click", () => {
|
|
228
|
-
creating = false;
|
|
229
|
-
handle.update();
|
|
230
|
-
}),
|
|
231
|
-
children: t("app.client.taskDiscussion.cancel")
|
|
232
|
-
}) : null]
|
|
233
|
-
}) : threads.length > 1 ? /* @__PURE__ */ jsx(Text, {
|
|
234
|
-
variant: "ui-caption",
|
|
235
|
-
tone: "muted",
|
|
236
|
-
children: t("app.client.taskDiscussion.replyingTo", { name: reply?.messages[0]?.author.name ?? t("app.client.taskDiscussion.theDiscussion") })
|
|
237
|
-
}) : null, /* @__PURE__ */ jsx(CommentComposer, {
|
|
238
|
-
label: t("app.client.taskDiscussion.addTaskComment"),
|
|
239
|
-
placeholder: t("app.client.taskDiscussion.addContextPlaceholder"),
|
|
240
|
-
busy: comments.busy || replyPending,
|
|
241
|
-
class: "w-full",
|
|
242
|
-
onSubmit: async (body) => {
|
|
243
|
-
if (replyPending) throw new Error(t("app.client.taskDiscussion.loadBeforeReplying"));
|
|
244
|
-
if (reply && capabilities.reply) {
|
|
245
|
-
await write({
|
|
246
|
-
id: reply.id,
|
|
247
|
-
body
|
|
248
|
-
});
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
selected = (await write({
|
|
252
|
-
anchor: null,
|
|
253
|
-
itemIds: draftItemIds,
|
|
254
|
-
body
|
|
255
|
-
})).thread.id;
|
|
256
|
-
explicit = Boolean(selected);
|
|
257
|
-
creating = false;
|
|
258
|
-
draftItemIds = [handle.props.itemId];
|
|
259
|
-
handle.update();
|
|
260
|
-
}
|
|
261
|
-
}, "task-composer")]
|
|
262
|
-
}) : null] }),
|
|
263
|
-
picking ? /* @__PURE__ */ jsx(ProjectTaskPicker, {
|
|
264
|
-
project: handle.props.project,
|
|
265
|
-
selected: draftItemIds,
|
|
266
|
-
required: [handle.props.itemId],
|
|
267
|
-
onSave: async (itemIds) => {
|
|
268
|
-
draftItemIds = itemIds;
|
|
269
|
-
handle.update();
|
|
270
|
-
},
|
|
271
|
-
onClose: () => {
|
|
272
|
-
picking = false;
|
|
273
|
-
handle.update();
|
|
274
|
-
}
|
|
275
|
-
}) : null
|
|
276
|
-
]
|
|
277
|
-
});
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
//#endregion
|
|
281
|
-
//#region app/client/project-tasks.tsx
|
|
282
|
-
function changedSince(row, item) {
|
|
283
|
-
if (row.revision !== null) return row.revision !== item.revision;
|
|
284
|
-
return Object.entries(row.values).some(([field, value]) => JSON.stringify(value) !== JSON.stringify(item.values[field]));
|
|
285
|
-
}
|
|
286
|
-
async function loadTask(token, id, schema, signal, conversationId) {
|
|
287
|
-
let [value, discussion, resolved] = await Promise.all([
|
|
288
|
-
requestTasks(routes.tasksApi.show.href({ id }), token, signal, (value) => value),
|
|
289
|
-
loadItemComments(token, id, signal, conversationId),
|
|
290
|
-
schema
|
|
291
|
-
]);
|
|
292
|
-
let detail = parseWorkDetail(value, resolved);
|
|
293
|
-
if (detail.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefresh"));
|
|
294
|
-
return {
|
|
295
|
-
detail,
|
|
296
|
-
discussion
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Opens the project's stored board, reading it from the companion only when this browser has
|
|
301
|
-
* none yet, and reads the task the URL opens with its discussions in parallel. An adapter
|
|
302
|
-
* failure is kept as data for the board.
|
|
303
|
-
* @param project - The project whose store and companion to read.
|
|
304
|
-
* @param signal - The screen's lifetime.
|
|
305
|
-
* @param href - The screen's location; a task page also reads that task.
|
|
306
|
-
* @param conversationId - A discussion the URL selects on that task page.
|
|
307
|
-
* @returns The store, or the error the board shows until a refresh succeeds.
|
|
308
|
-
*/
|
|
309
|
-
async function loadTasks(project, signal, href, conversationId) {
|
|
310
|
-
let store = await projectStore(project);
|
|
311
|
-
let stored = storedWorkspace(store);
|
|
312
|
-
let workspace = stored ? Promise.resolve(stored) : projectWork(project).refresh(signal);
|
|
313
|
-
let id = href ? selectedTask(href) : null;
|
|
314
|
-
let open = (schema) => loadTask(project.token, id, schema, signal, conversationId);
|
|
315
|
-
let taskError = "";
|
|
316
|
-
let task = id ? open(workspace.then((value) => value.schema)).catch((cause) => {
|
|
317
|
-
if (!stored || cause instanceof TaskRequestError) throw cause;
|
|
318
|
-
return open(projectWork(project).refresh(signal).then((value) => value.schema));
|
|
319
|
-
}).catch((cause) => {
|
|
320
|
-
taskError = cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTask");
|
|
321
|
-
return null;
|
|
322
|
-
}) : null;
|
|
323
|
-
try {
|
|
324
|
-
await workspace;
|
|
325
|
-
let opened = await task;
|
|
326
|
-
return {
|
|
327
|
-
store,
|
|
328
|
-
stored: Boolean(stored),
|
|
329
|
-
error: "",
|
|
330
|
-
task: opened,
|
|
331
|
-
taskError
|
|
332
|
-
};
|
|
333
|
-
} catch (cause) {
|
|
334
|
-
signal.throwIfAborted();
|
|
335
|
-
return {
|
|
336
|
-
store,
|
|
337
|
-
stored: false,
|
|
338
|
-
error: cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTasks")
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* The project's work board, its task details and task writes.
|
|
344
|
-
* @param handle - The project and its first board.
|
|
345
|
-
* @returns The board render function.
|
|
346
|
-
*/
|
|
347
|
-
function ProjectTasks(handle) {
|
|
348
|
-
let project = handle.props.project;
|
|
349
|
-
let store = handle.props.initial.store;
|
|
350
|
-
let work = projectWork(project);
|
|
351
|
-
let workspace = storedWorkspace(store);
|
|
352
|
-
let stored = () => {
|
|
353
|
-
let version = workspace?.snapshot.schemaVersion;
|
|
354
|
-
workspace = storedWorkspace(store);
|
|
355
|
-
let row = detail ? store.work.get(detail.item.id) : void 0;
|
|
356
|
-
if (row && !row.$hasPendingWrites && changedSince(plainRow(row), detail.item)) loadDetail();
|
|
357
|
-
else if (!detail && detailError && workspace?.snapshot.schemaVersion !== version) loadDetail();
|
|
358
|
-
changed();
|
|
359
|
-
};
|
|
360
|
-
let subscriptions = [store.boards.subscribeChanges(stored), store.work.subscribeChanges(stored)];
|
|
361
|
-
let selectedId = selectedTask(handle.props.href);
|
|
362
|
-
let opened = handle.props.initial.task;
|
|
363
|
-
let detail = opened?.detail.item.id === selectedId ? opened.detail : null;
|
|
364
|
-
let discussion = detail ? opened.discussion : void 0;
|
|
365
|
-
let loading = false;
|
|
366
|
-
let detailError = handle.props.initial.task ? "" : handle.props.initial.taskError ?? "";
|
|
367
|
-
let detailLoading = selectedId !== null && !detail && !detailError;
|
|
368
|
-
let error = handle.props.initial.error;
|
|
369
|
-
let listRequest = null;
|
|
370
|
-
let detailRequest = null;
|
|
371
|
-
let writeRequests = /* @__PURE__ */ new Set();
|
|
372
|
-
let renderedHref = handle.props.href;
|
|
373
|
-
let pendingHref;
|
|
374
|
-
let deferredLocation = false;
|
|
375
|
-
let mounted = false;
|
|
376
|
-
function pending(open) {
|
|
377
|
-
let row = open ? store.work.get(open.item.id) : void 0;
|
|
378
|
-
if (!open || !row?.$hasPendingWrites) return open;
|
|
379
|
-
let values = {
|
|
380
|
-
...open.item.values,
|
|
381
|
-
...plainRow(row).values
|
|
382
|
-
};
|
|
383
|
-
return {
|
|
384
|
-
...open,
|
|
385
|
-
item: {
|
|
386
|
-
...open.item,
|
|
387
|
-
values
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
function currentSchema() {
|
|
392
|
-
if (!workspace) throw new Error(t("app.client.projectTasks.refreshBeforeChanges"));
|
|
393
|
-
return workspace.schema;
|
|
394
|
-
}
|
|
395
|
-
async function write(url, body, parse) {
|
|
396
|
-
let controller = new AbortController();
|
|
397
|
-
writeRequests.add(controller);
|
|
398
|
-
try {
|
|
399
|
-
return await requestTasks(url, project.token, AbortSignal.any([handle.signal, controller.signal]), parse, fetch, { body });
|
|
400
|
-
} finally {
|
|
401
|
-
writeRequests.delete(controller);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
async function accepted(issue) {
|
|
405
|
-
await writeRows(store.work, { upsert: [issue] });
|
|
406
|
-
if (detail?.item.id === issue.id) detail = {
|
|
407
|
-
...detail,
|
|
408
|
-
item: issue
|
|
409
|
-
};
|
|
410
|
-
changed();
|
|
411
|
-
}
|
|
412
|
-
async function refreshAfterWrite() {
|
|
413
|
-
await load();
|
|
414
|
-
if (selectedId) await loadDetail();
|
|
415
|
-
}
|
|
416
|
-
let queues = /* @__PURE__ */ new Map();
|
|
417
|
-
async function save(url, body, predict, id) {
|
|
418
|
-
let previous = id ? queues.get(id) : void 0;
|
|
419
|
-
let settled = Promise.withResolvers();
|
|
420
|
-
if (id) queues.set(id, settled.promise);
|
|
421
|
-
try {
|
|
422
|
-
let item = await writeOptimistically(predict, async () => {
|
|
423
|
-
let prior = await previous;
|
|
424
|
-
let from = "expectedRevision" in body ? body.expectedRevision : void 0;
|
|
425
|
-
let result = await write(url, prior && from === prior.from && prior.to ? {
|
|
426
|
-
...body,
|
|
427
|
-
expectedRevision: prior.to
|
|
428
|
-
} : body, (value) => parseWorkMutationResult(value, currentSchema()));
|
|
429
|
-
if (id && result.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefreshRetry"));
|
|
430
|
-
await accepted(result.item);
|
|
431
|
-
settled.resolve(from ? {
|
|
432
|
-
from,
|
|
433
|
-
to: result.item.revision
|
|
434
|
-
} : null);
|
|
435
|
-
return result.item;
|
|
436
|
-
});
|
|
437
|
-
if (selectedId) await loadDetail();
|
|
438
|
-
return item;
|
|
439
|
-
} catch (cause) {
|
|
440
|
-
settled.resolve(null);
|
|
441
|
-
if (!handle.signal.aborted) toast.error(t("app.client.projectTasks.undidTaskChange"), { description: cause instanceof Error ? cause.message : t("app.client.projectTasks.writeNotConfirmed") });
|
|
442
|
-
refreshAfterWrite();
|
|
443
|
-
throw cause;
|
|
444
|
-
} finally {
|
|
445
|
-
if (id && queues.get(id) === settled.promise) queues.delete(id);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
function predicted(id, next) {
|
|
449
|
-
return () => {
|
|
450
|
-
let row = store.work.get(id);
|
|
451
|
-
if (!row) return;
|
|
452
|
-
let { values } = next(plainRow(row));
|
|
453
|
-
store.work.update(id, (draft) => {
|
|
454
|
-
draft.values = values;
|
|
455
|
-
});
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
let operations = {
|
|
459
|
-
create: (draft) => save(routes.tasksApi.create.href(), draft, () => {
|
|
460
|
-
store.work.insert(predictWorkCreate(currentSchema(), `pending-${crypto.randomUUID()}`, draft));
|
|
461
|
-
}),
|
|
462
|
-
update: (id, action) => save(routes.tasksApi.update.href({ id }), action, predicted(id, (item) => predictWorkUpdate(item, action)), id),
|
|
463
|
-
act: (id, action) => save(routes.tasksApi.mutate.href({ id }), action, predicted(id, (item) => predictWorkAction(currentSchema(), item, action)), id),
|
|
464
|
-
get: async (id) => {
|
|
465
|
-
let next = await requestTasks(routes.tasksApi.show.href({ id }), project.token, handle.signal, (value) => parseWorkDetail(value, currentSchema()));
|
|
466
|
-
if (next.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefresh"));
|
|
467
|
-
return next.item;
|
|
468
|
-
},
|
|
469
|
-
section: (id, section, signal = handle.signal) => requestTasks(routes.tasksApi.section.href({
|
|
470
|
-
id,
|
|
471
|
-
section
|
|
472
|
-
}), project.token, AbortSignal.any([handle.signal, signal]), (value) => parseSectionData(value, currentSchema())),
|
|
473
|
-
sync: async (action) => {
|
|
474
|
-
try {
|
|
475
|
-
let result = await write(routes.tasksApi.sync.href(), action, parseWorkSyncResult);
|
|
476
|
-
await refreshAfterWrite();
|
|
477
|
-
return result;
|
|
478
|
-
} catch (cause) {
|
|
479
|
-
refreshAfterWrite();
|
|
480
|
-
throw cause;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
};
|
|
484
|
-
function changed() {
|
|
485
|
-
if (!handle.signal.aborted) handle.update();
|
|
486
|
-
}
|
|
487
|
-
async function load() {
|
|
488
|
-
listRequest?.abort();
|
|
489
|
-
let controller = new AbortController();
|
|
490
|
-
listRequest = controller;
|
|
491
|
-
loading = true;
|
|
492
|
-
error = "";
|
|
493
|
-
changed();
|
|
494
|
-
try {
|
|
495
|
-
await work.refresh(AbortSignal.any([handle.signal, controller.signal]));
|
|
496
|
-
} catch (cause) {
|
|
497
|
-
if (!controller.signal.aborted && !handle.signal.aborted) error = cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTasks");
|
|
498
|
-
} finally {
|
|
499
|
-
if (listRequest === controller) {
|
|
500
|
-
loading = false;
|
|
501
|
-
changed();
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
async function loadDetail() {
|
|
506
|
-
detailRequest?.abort();
|
|
507
|
-
if (!selectedId) {
|
|
508
|
-
detail = null;
|
|
509
|
-
detailLoading = false;
|
|
510
|
-
detailError = "";
|
|
511
|
-
changed();
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
let id = selectedId;
|
|
515
|
-
let controller = new AbortController();
|
|
516
|
-
detailRequest = controller;
|
|
517
|
-
let reopening = detail?.item.id !== id;
|
|
518
|
-
if (reopening) detail = null;
|
|
519
|
-
detailLoading = true;
|
|
520
|
-
detailError = "";
|
|
521
|
-
changed();
|
|
522
|
-
try {
|
|
523
|
-
let signal = AbortSignal.any([handle.signal, controller.signal]);
|
|
524
|
-
if (reopening) {
|
|
525
|
-
let next = await loadTask(project.token, id, currentSchema(), signal, handle.props.conversationId);
|
|
526
|
-
if (controller.signal.aborted || id !== selectedId) return;
|
|
527
|
-
detail = next.detail;
|
|
528
|
-
discussion = next.discussion;
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
let next = await requestTasks(routes.tasksApi.show.href({ id }), project.token, signal, (value) => parseWorkDetail(value, currentSchema()));
|
|
532
|
-
if (controller.signal.aborted || id !== selectedId) return;
|
|
533
|
-
if (next.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefresh"));
|
|
534
|
-
detail = next;
|
|
535
|
-
} catch (cause) {
|
|
536
|
-
if (!controller.signal.aborted && !handle.signal.aborted && id === selectedId) detailError = cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTask");
|
|
537
|
-
} finally {
|
|
538
|
-
if (detailRequest === controller) {
|
|
539
|
-
detailLoading = false;
|
|
540
|
-
changed();
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
function select(id, history = true) {
|
|
545
|
-
if (id === selectedId) return;
|
|
546
|
-
selectedId = id;
|
|
547
|
-
if (history) location(false);
|
|
548
|
-
loadDetail();
|
|
549
|
-
changed();
|
|
550
|
-
}
|
|
551
|
-
function location(replace) {
|
|
552
|
-
let root = taskHref(null);
|
|
553
|
-
if (!handle.props.active || !readConversationLocation(window.location.href) && window.location.pathname !== root && !window.location.pathname.startsWith(`${root}/`)) {
|
|
554
|
-
deferredLocation = true;
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
deferredLocation = false;
|
|
558
|
-
let href = taskHref(selectedId);
|
|
559
|
-
if (window.location.pathname + window.location.search + window.location.hash === href) return;
|
|
560
|
-
pendingHref = new URL(href, window.location.origin).href;
|
|
561
|
-
window.history[replace ? "replaceState" : "pushState"]({
|
|
562
|
-
...window.history.state,
|
|
563
|
-
budTask: selectedId
|
|
564
|
-
}, "", href);
|
|
565
|
-
}
|
|
566
|
-
let mount = ref((_node, signal) => {
|
|
567
|
-
mounted = true;
|
|
568
|
-
let loaded = selectedId;
|
|
569
|
-
selectedId = selectedTask(handle.props.href);
|
|
570
|
-
if (detail?.item.id !== selectedId && !(detailError && loaded === selectedId)) loadDetail();
|
|
571
|
-
if (handle.props.initial.stored) load();
|
|
572
|
-
signal.addEventListener("abort", () => {
|
|
573
|
-
for (let subscription of subscriptions) subscription.unsubscribe();
|
|
574
|
-
listRequest?.abort();
|
|
575
|
-
detailRequest?.abort();
|
|
576
|
-
for (let controller of writeRequests) controller.abort();
|
|
577
|
-
}, { once: true });
|
|
578
|
-
});
|
|
579
|
-
return () => {
|
|
580
|
-
if (mounted && renderedHref !== handle.props.href) {
|
|
581
|
-
renderedHref = handle.props.href;
|
|
582
|
-
if (pendingHref !== renderedHref) {
|
|
583
|
-
deferredLocation = false;
|
|
584
|
-
let id = selectedTask(renderedHref);
|
|
585
|
-
if (id !== selectedId) handle.queueTask(() => select(id, false));
|
|
586
|
-
}
|
|
587
|
-
pendingHref = void 0;
|
|
588
|
-
}
|
|
589
|
-
if (deferredLocation && handle.props.active) handle.queueTask(() => location(true));
|
|
590
|
-
let current = workspace;
|
|
591
|
-
let linked = current?.snapshot.items.map((item) => ({
|
|
592
|
-
id: item.id,
|
|
593
|
-
title: taskRole(item, current.schema, "title") || void 0
|
|
594
|
-
}));
|
|
595
|
-
return /* @__PURE__ */ jsx("div", {
|
|
596
|
-
class: "h-full min-h-0",
|
|
597
|
-
mix: mount,
|
|
598
|
-
children: /* @__PURE__ */ jsx(TaskWorkspace, {
|
|
599
|
-
projectName: project.name,
|
|
600
|
-
project: {
|
|
601
|
-
id: project.id,
|
|
602
|
-
name: project.name
|
|
603
|
-
},
|
|
604
|
-
design: handle.props.design,
|
|
605
|
-
storageKey: `bud:tasks:${project.id}:v1`,
|
|
606
|
-
snapshot: current?.snapshot ?? null,
|
|
607
|
-
schema: current?.schema ?? null,
|
|
608
|
-
loading,
|
|
609
|
-
error,
|
|
610
|
-
selectedId,
|
|
611
|
-
detail: pending(detail),
|
|
612
|
-
discussion: detail ? /* @__PURE__ */ jsx(TaskDiscussion, {
|
|
613
|
-
project,
|
|
614
|
-
itemId: detail.item.id,
|
|
615
|
-
initial: discussion,
|
|
616
|
-
items: linked,
|
|
617
|
-
conversationId: handle.props.conversationId,
|
|
618
|
-
onChange: refreshAfterWrite
|
|
619
|
-
}, detail.item.id) : void 0,
|
|
620
|
-
detailLoading,
|
|
621
|
-
detailError,
|
|
622
|
-
operations,
|
|
623
|
-
onSelect: select,
|
|
624
|
-
onReload: () => {
|
|
625
|
-
load();
|
|
626
|
-
},
|
|
627
|
-
onReloadDetail: () => {
|
|
628
|
-
loadDetail();
|
|
629
|
-
}
|
|
630
|
-
})
|
|
631
|
-
});
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
//#endregion
|
|
635
|
-
//#region app/screens/tasks.tsx
|
|
636
|
-
var tasks_default = defineScreen({
|
|
637
|
-
routes: taskRoutes,
|
|
638
|
-
load: ({ project, signal, url, conversation }) => loadTasks(project, signal, url.href, conversation?.id),
|
|
639
|
-
render: (tasks, { project, url, active, taskDesign, conversation }) => /* @__PURE__ */ jsx(ProjectTasks, {
|
|
640
|
-
project,
|
|
641
|
-
initial: tasks,
|
|
642
|
-
design: taskDesign,
|
|
643
|
-
href: url.href,
|
|
644
|
-
active,
|
|
645
|
-
conversationId: conversation?.id
|
|
646
|
-
})
|
|
647
|
-
});
|
|
648
|
-
//#endregion
|
|
649
|
-
export { tasks_default as default };
|