@sentry/junior-dashboard 0.294.0 → 0.295.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/dist/app.js +7 -5
- package/dist/client-manifest.json +1 -1
- package/dist/index.js +7 -5
- package/dist/public/chunks/{AdminPage-mn71yp2v.js → AdminPage-lctmhvpg.js} +1 -1
- package/dist/public/chunks/{AnchorHeading-cp3vx8qa.js → AnchorHeading-ixbupfij.js} +1 -1
- package/dist/public/chunks/{Area-h08sxa33.js → Area-d95rfkv5.js} +1 -1
- package/dist/public/chunks/AutomationPage-m78li1hv.js +3 -0
- package/dist/public/chunks/{AutomationRunDetails-jjdhbqtn.js → AutomationRunDetails-dx5lutte.js} +1 -1
- package/dist/public/chunks/{AutomationRunsPage-j50ps7nt.js → AutomationRunsPage-cba6nbxd.js} +1 -1
- package/dist/public/chunks/{AutomationStatus-czfg6ayl.js → AutomationStatus-eqq5lrad.js} +1 -1
- package/dist/public/chunks/AutomationsPage-cp7wtt2v.js +8 -0
- package/dist/public/chunks/{BarChart-e3oi6in1.js → BarChart-lbkezxbi.js} +1 -1
- package/dist/public/chunks/{BoardPage-f0uxqg7u.js → BoardPage-m4zqf62q.js} +1 -1
- package/dist/public/chunks/{CodeChangeRow-dk3gkjut.js → CodeChangeRow-d620nk75.js} +2 -2
- package/dist/public/chunks/CodePage-h2ayars2.js +1 -0
- package/dist/public/chunks/{CodeSummaryCards-o1po2jis.js → CodeSummaryCards-iotfblbp.js} +1 -1
- package/dist/public/chunks/{ConnectionsPage-mholegdk.js → ConnectionsPage-nm7w1i05.js} +2 -2
- package/dist/public/chunks/{DirectoryMetric-dgzufji4.js → DirectoryMetric-huy2ptzz.js} +1 -1
- package/dist/public/chunks/{DrawerContent-ovjgr16m.js → DrawerContent-n3biwp5m.js} +1 -1
- package/dist/public/chunks/{FilterBar-mtnh7fbi.js → FilterBar-dqnt967g.js} +1 -1
- package/dist/public/chunks/FormNotice-kj5pzsl7.js +1 -0
- package/dist/public/chunks/{LocationDetailPage-ggd6zgzp.js → LocationDetailPage-iqtx76w6.js} +2 -2
- package/dist/public/chunks/{LocationsPage-fh6vzxa1.js → LocationsPage-ju5jfejt.js} +2 -2
- package/dist/public/chunks/{McpSettingsPage-fm3fh2ux.js → McpSettingsPage-brav5c42.js} +1 -1
- package/dist/public/chunks/{PeoplePage-odrbcmhj.js → PeoplePage-lfq8nrto.js} +2 -2
- package/dist/public/chunks/{PersonProfilePage-gynexe89.js → PersonProfilePage-ep45sxr6.js} +1 -1
- package/dist/public/chunks/{PersonalTokensPage-4o4zj02w.js → PersonalTokensPage-jbt4q7uz.js} +1 -1
- package/dist/public/chunks/PluginReportRecordSet-ml18z19r.js +1 -0
- package/dist/public/chunks/{PluginUserPage-kwylbbpr.js → PluginUserPage-ilar25ic.js} +2 -2
- package/dist/public/chunks/RepositoryPage-o9m3vty4.js +1 -0
- package/dist/public/chunks/SettingsPage-jc9et1qq.js +1 -0
- package/dist/public/chunks/SystemPage-bbp8cdds.js +15 -0
- package/dist/public/chunks/{WorkspaceFormPage-hwvyvnuk.js → WorkspaceFormPage-kshmsdek.js} +2 -2
- package/dist/public/chunks/{WorkspacesPage-lu9w3ugz.js → WorkspacesPage-ohasc10u.js} +1 -1
- package/dist/public/chunks/alert-ifqo1uj7.js +1 -0
- package/dist/public/chunks/{automationFormat-hd7pnocr.js → automationFormat-kv314fzh.js} +1 -1
- package/dist/public/chunks/{chart-myr1oeen.js → chart-il2ahs5y.js} +1 -1
- package/dist/public/chunks/{circle-dollar-sign-fpl4dmok.js → circle-dollar-sign-h7k4kr7s.js} +1 -1
- package/dist/public/chunks/{code-highlighter-i3ux67or.js → code-highlighter-ea4j0ys1.js} +1 -1
- package/dist/public/chunks/{coins-bqkaxeba.js → coins-l2c8c17w.js} +1 -1
- package/dist/public/chunks/{format-gle1s0ri.js → format-flkutk9e.js} +2 -2
- package/dist/public/chunks/junior-g62bxb2t.js +4 -0
- package/dist/public/chunks/{native-select-jfknh8t4.js → native-select-fxs3p0bj.js} +1 -1
- package/dist/public/chunks/{plus-gndielka.js → plus-54e3zzys.js} +1 -1
- package/dist/public/chunks/vendor-icons-blmhaswc.js +698 -0
- package/dist/public/chunks/{workspaceQueries-m1ksz95c.js → workspaceQueries-onxawhg4.js} +1 -1
- package/dist/public/client-ctcthgam.js +165 -0
- package/dist/public/styles-4CIT7L7K.css +2 -0
- package/dist/tailwind.css +1 -1
- package/package.json +4 -4
- package/src/client/api.ts +16 -0
- package/src/client/components/MarkdownViewToggle.tsx +24 -0
- package/src/client/components/SkillLink.tsx +119 -0
- package/src/client/conversations/ConversationHomeList.tsx +87 -76
- package/src/client/conversations/ConversationListStatusIcon.tsx +39 -55
- package/src/client/conversations/ConversationSectionList.tsx +101 -0
- package/src/client/conversations/ConversationSidebar.tsx +159 -169
- package/src/client/conversations/ConversationWorkspace.tsx +5 -16
- package/src/client/conversations/TextAttachmentPreview.tsx +5 -14
- package/src/client/conversations/WaitingChip.tsx +78 -0
- package/src/client/conversations/conversationSections.ts +78 -55
- package/src/client/conversations/heldListOrder.ts +170 -0
- package/src/client/conversations/toolCallPreview.ts +3 -1
- package/src/client/conversations/transcriptSource.ts +7 -2
- package/src/client/conversations/useMarkConversationUnread.ts +2 -2
- package/src/client/format.ts +18 -2
- package/src/client/pages/automations/AutomationDraftChanges.tsx +2 -1
- package/src/client/pages/automations/AutomationOverview.tsx +3 -3
- package/src/client/pages/automations/AutomationSkillPicker.tsx +75 -31
- package/src/client/pages/automations/AutomationTaskFields.tsx +3 -13
- package/src/client/pages/automations/AutomationVersionsTab.tsx +2 -5
- package/src/client/pages/automations/skillSearch.ts +50 -0
- package/src/client/pages/code/RepositorySections.tsx +14 -5
- package/src/client/pages/code/RepositoryTabs.tsx +5 -11
- package/src/client/pages/system/PluginDetails.tsx +15 -8
- package/src/client/pages/system/SkillInventory.tsx +2 -6
- package/src/client/review-desk/queries.ts +30 -23
- package/src/client/review-desk/reviewItemFormat.ts +3 -1
- package/src/client/types.ts +5 -1
- package/src/mock-reporting/review-items.ts +6 -2
- package/dist/public/chunks/AutomationPage-e1l74ezg.js +0 -10
- package/dist/public/chunks/AutomationsPage-cphxl4l6.js +0 -8
- package/dist/public/chunks/CodePage-i97ivyji.js +0 -1
- package/dist/public/chunks/FormNotice-eyo2kdte.js +0 -1
- package/dist/public/chunks/PluginReportRecordSet-hm1cwp6c.js +0 -1
- package/dist/public/chunks/RepositoryPage-fti2vfu4.js +0 -1
- package/dist/public/chunks/SettingsPage-kxspx37h.js +0 -1
- package/dist/public/chunks/SystemPage-jurblfgy.js +0 -15
- package/dist/public/chunks/SystemPlugins-jr37f1cv.js +0 -1
- package/dist/public/chunks/junior-hdb4wc4q.js +0 -4
- package/dist/public/chunks/vendor-icons-mxz7yi04.js +0 -692
- package/dist/public/client-f7asutg5.js +0 -165
- package/dist/public/styles-ZJJGBKKX.css +0 -2
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { memo, useCallback, useMemo, useState } from "react";
|
|
1
|
+
import { memo, useCallback, useMemo, useState, type ReactNode } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Archive,
|
|
4
4
|
ArchiveRestore,
|
|
5
|
-
ChevronRight,
|
|
6
|
-
Inbox,
|
|
7
|
-
LockKeyhole,
|
|
8
5
|
Pin,
|
|
9
6
|
SquarePen,
|
|
10
7
|
} from "lucide-react";
|
|
@@ -22,7 +19,6 @@ import { useConversationSubscription } from "./useConversationSubscription";
|
|
|
22
19
|
import { ContinueInSlackDialog } from "./ContinueInSlackDialog";
|
|
23
20
|
import { ConversationRowMenu } from "./ConversationRowMenu";
|
|
24
21
|
import {
|
|
25
|
-
PinnedConversationOrder,
|
|
26
22
|
usePinnedRowDrag,
|
|
27
23
|
type PinnedRowDrag,
|
|
28
24
|
} from "./PinnedConversationOrder";
|
|
@@ -37,11 +33,10 @@ import { formatCompactAge } from "./conversationAge";
|
|
|
37
33
|
import { cn } from "../styles";
|
|
38
34
|
import type { Conversation } from "../types";
|
|
39
35
|
import { AnimatedList } from "./AnimatedList";
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} from "./conversationSections";
|
|
36
|
+
import { WaitingChip } from "./WaitingChip";
|
|
37
|
+
import type { ConversationSection } from "./conversationSections";
|
|
38
|
+
import { ConversationSectionList } from "./ConversationSectionList";
|
|
39
|
+
import { ConversationStatusText } from "./ConversationListStatusIcon";
|
|
45
40
|
import { ConversationSectionHeading } from "./ConversationSectionHeading";
|
|
46
41
|
import {
|
|
47
42
|
ConversationSidebarThreadRow,
|
|
@@ -145,40 +140,6 @@ export function ConversationSidebar(props: {
|
|
|
145
140
|
return next;
|
|
146
141
|
});
|
|
147
142
|
}, []);
|
|
148
|
-
// Rebuild section rows only when their inputs change. Avoid fresh Date.now()
|
|
149
|
-
// arrays on unrelated parent renders while the reader is scrolling.
|
|
150
|
-
const entries = useMemo(
|
|
151
|
-
() =>
|
|
152
|
-
conversationSidebarEntries(
|
|
153
|
-
buildConversationSections(props.conversations, {
|
|
154
|
-
attentionIds: attentionConversationIds(
|
|
155
|
-
props.conversations,
|
|
156
|
-
props.waitingConversations.keys(),
|
|
157
|
-
),
|
|
158
|
-
nowMs: Date.now(),
|
|
159
|
-
timeZone: props.timeZone,
|
|
160
|
-
}),
|
|
161
|
-
collapsedKeys,
|
|
162
|
-
{ expandedThreadRoots, selectedId: props.selectedId },
|
|
163
|
-
),
|
|
164
|
-
[
|
|
165
|
-
collapsedKeys,
|
|
166
|
-
expandedThreadRoots,
|
|
167
|
-
props.selectedId,
|
|
168
|
-
props.conversations,
|
|
169
|
-
props.timeZone,
|
|
170
|
-
props.waitingConversations,
|
|
171
|
-
],
|
|
172
|
-
);
|
|
173
|
-
const pinnedConversations = useMemo(
|
|
174
|
-
() =>
|
|
175
|
-
entries.flatMap((entry) =>
|
|
176
|
-
entry.kind === "conversation" && entry.conversation.pinnedAt
|
|
177
|
-
? [entry.conversation]
|
|
178
|
-
: [],
|
|
179
|
-
),
|
|
180
|
-
[entries],
|
|
181
|
-
);
|
|
182
143
|
const handleReorderError = useCallback(
|
|
183
144
|
(conversation: Conversation) =>
|
|
184
145
|
showActionError(conversation, "Could not reorder"),
|
|
@@ -203,82 +164,123 @@ export function ConversationSidebar(props: {
|
|
|
203
164
|
New conversation
|
|
204
165
|
</Button>
|
|
205
166
|
</div>
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
167
|
+
{/* The list wraps the always-shown scroll area, so a pointer that
|
|
168
|
+
rests on it through the first load, or on its padding or
|
|
169
|
+
scrollbar, still holds the order. */}
|
|
170
|
+
<ConversationSectionList
|
|
171
|
+
className="relative min-h-0"
|
|
172
|
+
conversations={props.conversations}
|
|
173
|
+
loading={props.loading}
|
|
174
|
+
onReorderError={handleReorderError}
|
|
175
|
+
onReorderPins={props.onReorderPins}
|
|
176
|
+
timeZone={props.timeZone}
|
|
177
|
+
waitingConversations={props.waitingConversations}
|
|
178
|
+
>
|
|
179
|
+
{({ releaseHold, sections }) => (
|
|
180
|
+
<ScrollArea
|
|
181
|
+
className="h-full"
|
|
182
|
+
viewportClassName="px-3 pb-[max(0.5rem,env(safe-area-inset-bottom))]"
|
|
218
183
|
>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
</
|
|
184
|
+
{props.error ? (
|
|
185
|
+
<div className="p-2">
|
|
186
|
+
<EmptyTelemetry>{props.error}</EmptyTelemetry>
|
|
187
|
+
</div>
|
|
188
|
+
) : props.loading && props.conversations.length === 0 ? (
|
|
189
|
+
<ConversationRowsLoading />
|
|
190
|
+
) : (
|
|
191
|
+
<ConversationSidebarEntries
|
|
192
|
+
collapsedKeys={collapsedKeys}
|
|
193
|
+
expandedThreadRoots={expandedThreadRoots}
|
|
194
|
+
loading={props.loading}
|
|
195
|
+
sections={sections}
|
|
196
|
+
selectedId={props.selectedId}
|
|
197
|
+
renderItem={(entry) =>
|
|
198
|
+
entry.kind === "section" ? (
|
|
199
|
+
<ConversationSectionHeading
|
|
200
|
+
className={cn("px-3 pb-1", entry.first ? "pt-1" : "pt-3")}
|
|
201
|
+
collapsed={entry.collapsed}
|
|
202
|
+
count={entry.count}
|
|
203
|
+
label={entry.label}
|
|
204
|
+
onToggle={() => toggleSection(entry.sectionKey)}
|
|
205
|
+
/>
|
|
206
|
+
) : entry.kind === "thread-toggle" ? (
|
|
207
|
+
<ConversationSidebarThreadToggle
|
|
208
|
+
expanded={entry.expanded}
|
|
209
|
+
hiddenCount={entry.hiddenCount}
|
|
210
|
+
onToggle={() => toggleThreads(entry.rootId)}
|
|
211
|
+
/>
|
|
212
|
+
) : entry.kind === "thread" ? (
|
|
213
|
+
<ConversationSidebarThreadRow
|
|
214
|
+
conversation={entry.conversation}
|
|
215
|
+
depth={entry.depth}
|
|
216
|
+
selected={entry.conversation.id === props.selectedId}
|
|
217
|
+
waiting={props.waitingConversations.has(
|
|
218
|
+
entry.conversation.id,
|
|
219
|
+
)}
|
|
220
|
+
/>
|
|
221
|
+
) : (
|
|
222
|
+
<ConversationSidebarEntryRow
|
|
223
|
+
conversation={entry.conversation}
|
|
224
|
+
onArchiveError={showArchiveError}
|
|
225
|
+
onArchived={showArchived}
|
|
226
|
+
onPinError={handlePinError}
|
|
227
|
+
onPinned={clearActionError}
|
|
228
|
+
onUnreadError={handleUnreadError}
|
|
229
|
+
onActionError={showActionError}
|
|
230
|
+
onRowAction={releaseHold}
|
|
231
|
+
selected={entry.conversation.id === props.selectedId}
|
|
232
|
+
waiting={props.waitingConversations.get(
|
|
233
|
+
entry.conversation.id,
|
|
234
|
+
)}
|
|
235
|
+
/>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
/>
|
|
239
|
+
)}
|
|
240
|
+
</ScrollArea>
|
|
276
241
|
)}
|
|
277
|
-
</
|
|
242
|
+
</ConversationSectionList>
|
|
278
243
|
</aside>
|
|
279
244
|
);
|
|
280
245
|
}
|
|
281
246
|
|
|
247
|
+
/** Show the sidebar rows of the held sections, with their motion. */
|
|
248
|
+
function ConversationSidebarEntries(props: {
|
|
249
|
+
collapsedKeys: ReadonlySet<string>;
|
|
250
|
+
expandedThreadRoots: ReadonlySet<string>;
|
|
251
|
+
loading: boolean;
|
|
252
|
+
renderItem(entry: ConversationSidebarEntry): ReactNode;
|
|
253
|
+
sections: ConversationSection[];
|
|
254
|
+
selectedId?: string;
|
|
255
|
+
}) {
|
|
256
|
+
const { collapsedKeys, expandedThreadRoots, sections, selectedId } = props;
|
|
257
|
+
const entries = useMemo(
|
|
258
|
+
() =>
|
|
259
|
+
conversationSidebarEntries(sections, collapsedKeys, {
|
|
260
|
+
expandedThreadRoots,
|
|
261
|
+
selectedId,
|
|
262
|
+
}),
|
|
263
|
+
[collapsedKeys, expandedThreadRoots, sections, selectedId],
|
|
264
|
+
);
|
|
265
|
+
return (
|
|
266
|
+
<AnimatedList
|
|
267
|
+
ariaLabel="Your conversations"
|
|
268
|
+
className="grid gap-0.5"
|
|
269
|
+
empty={
|
|
270
|
+
!props.loading ? (
|
|
271
|
+
<div className="p-2">
|
|
272
|
+
<EmptyTelemetry>No conversations match this view.</EmptyTelemetry>
|
|
273
|
+
</div>
|
|
274
|
+
) : undefined
|
|
275
|
+
}
|
|
276
|
+
getKey={conversationEntryKey}
|
|
277
|
+
items={entries}
|
|
278
|
+
renderItem={props.renderItem}
|
|
279
|
+
role="navigation"
|
|
280
|
+
/>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
282
284
|
/** Match the usual conversation row density while the first feed loads. */
|
|
283
285
|
function ConversationRowsLoading() {
|
|
284
286
|
return (
|
|
@@ -379,6 +381,8 @@ type ConversationSidebarRowProps = {
|
|
|
379
381
|
onPinned(conversation: Conversation): void;
|
|
380
382
|
onUnreadError(conversation: Conversation, wasMarkingUnread: boolean): void;
|
|
381
383
|
onActionError(conversation: Conversation, title: string): void;
|
|
384
|
+
/** Runs before a row action that can move rows, so its result shows. */
|
|
385
|
+
onRowAction(): void;
|
|
382
386
|
selected: boolean;
|
|
383
387
|
waiting?: WaitingConversation;
|
|
384
388
|
};
|
|
@@ -454,12 +458,22 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(
|
|
|
454
458
|
const participant = props.conversation.isParticipant;
|
|
455
459
|
const canToggleRead =
|
|
456
460
|
participant && !props.waiting && (unread || canMarkUnread);
|
|
457
|
-
const
|
|
458
|
-
|
|
461
|
+
const setPinned = (next: boolean) => {
|
|
462
|
+
props.onRowAction();
|
|
463
|
+
pin.mutate(next);
|
|
464
|
+
};
|
|
465
|
+
const togglePin = () => setPinned(!pinned);
|
|
466
|
+
const setUnread = (next: boolean) => {
|
|
467
|
+
props.onRowAction();
|
|
468
|
+
markUnread.mutate(next);
|
|
469
|
+
};
|
|
470
|
+
const toggleArchive = () => {
|
|
471
|
+
props.onRowAction();
|
|
459
472
|
archive.mutate({
|
|
460
473
|
archived: !archived,
|
|
461
474
|
lastSeenAt: props.conversation.lastSeenAt,
|
|
462
475
|
});
|
|
476
|
+
};
|
|
463
477
|
return (
|
|
464
478
|
<ContextMenu
|
|
465
479
|
// The menu shows rows from the detail, so read it as a hover does.
|
|
@@ -503,11 +517,6 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(
|
|
|
503
517
|
className="size-1.5 shrink-0 rounded-full bg-dashboard-accent"
|
|
504
518
|
role="img"
|
|
505
519
|
/>
|
|
506
|
-
) : props.conversation.visibility === "private" ? (
|
|
507
|
-
<LockKeyhole
|
|
508
|
-
aria-label="Private conversation"
|
|
509
|
-
className="size-3 shrink-0 text-dashboard-text-faint"
|
|
510
|
-
/>
|
|
511
520
|
) : null}
|
|
512
521
|
</span>
|
|
513
522
|
<span
|
|
@@ -524,7 +533,7 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(
|
|
|
524
533
|
{title}
|
|
525
534
|
</span>
|
|
526
535
|
<ConversationRowState
|
|
527
|
-
|
|
536
|
+
conversation={props.conversation}
|
|
528
537
|
status={status}
|
|
529
538
|
/>
|
|
530
539
|
</Link>
|
|
@@ -536,7 +545,7 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(
|
|
|
536
545
|
aria-label={`Mark ${title} as ${unread ? "read" : "unread"}`}
|
|
537
546
|
className="group/read absolute top-1 left-[0.4375rem] z-10 grid size-6 cursor-pointer place-items-center rounded-md transition hover:bg-dashboard-surface-active focus-visible:bg-dashboard-surface-active focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-dashboard-focus disabled:cursor-not-allowed"
|
|
538
547
|
disabled={markUnread.isPending}
|
|
539
|
-
onClick={() =>
|
|
548
|
+
onClick={() => setUnread(!unread)}
|
|
540
549
|
type="button"
|
|
541
550
|
>
|
|
542
551
|
{/* On hover, preview the state that a click sets. */}
|
|
@@ -558,28 +567,10 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(
|
|
|
558
567
|
</Tooltip>
|
|
559
568
|
) : null}
|
|
560
569
|
{props.waiting ? (
|
|
561
|
-
<
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
<Inbox
|
|
566
|
-
aria-hidden="true"
|
|
567
|
-
className="size-3 shrink-0 text-dashboard-text-muted"
|
|
568
|
-
/>
|
|
569
|
-
<span className="min-w-0 truncate">{props.waiting.title}</span>
|
|
570
|
-
{props.waiting.count > 1 ? (
|
|
571
|
-
<span className="ml-auto shrink-0 text-dashboard-text-faint">
|
|
572
|
-
+{props.waiting.count - 1} more
|
|
573
|
-
</span>
|
|
574
|
-
) : null}
|
|
575
|
-
<ChevronRight
|
|
576
|
-
aria-hidden="true"
|
|
577
|
-
className={cn(
|
|
578
|
-
"size-3 shrink-0 text-dashboard-text-muted",
|
|
579
|
-
props.waiting.count > 1 ? undefined : "ml-auto",
|
|
580
|
-
)}
|
|
581
|
-
/>
|
|
582
|
-
</Link>
|
|
570
|
+
<WaitingChip
|
|
571
|
+
conversationId={props.conversation.id}
|
|
572
|
+
waiting={props.waiting}
|
|
573
|
+
/>
|
|
583
574
|
) : null}
|
|
584
575
|
{/* Mobile rows pin from the conversation menu, so only the archive
|
|
585
576
|
button takes row space there. Archived rows restore before they pin. */}
|
|
@@ -649,8 +640,8 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(
|
|
|
649
640
|
replace: props.selected,
|
|
650
641
|
state: OPEN_EVENT_LOG_STATE,
|
|
651
642
|
}),
|
|
652
|
-
pin:
|
|
653
|
-
read:
|
|
643
|
+
pin: setPinned,
|
|
644
|
+
read: setUnread,
|
|
654
645
|
rename: () => setRenaming(true),
|
|
655
646
|
subscription: (subscribed) => subscription.mutate(subscribed),
|
|
656
647
|
}}
|
|
@@ -682,32 +673,31 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(
|
|
|
682
673
|
);
|
|
683
674
|
});
|
|
684
675
|
|
|
685
|
-
/**
|
|
676
|
+
/**
|
|
677
|
+
* Show the row's secondary text: a lock when private, then the status when
|
|
678
|
+
* the conversation runs, failed, or stopped, or else the age of its last
|
|
679
|
+
* Message.
|
|
680
|
+
*/
|
|
686
681
|
function ConversationRowState(props: {
|
|
687
|
-
|
|
682
|
+
conversation: Conversation;
|
|
688
683
|
status: ReturnType<typeof visualStatusForConversation>;
|
|
689
684
|
}) {
|
|
690
|
-
|
|
691
|
-
return (
|
|
692
|
-
<Spinner
|
|
693
|
-
aria-label="Running"
|
|
694
|
-
className="size-3.5 text-dashboard-text-muted"
|
|
695
|
-
/>
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
if (props.status === "failed") {
|
|
699
|
-
return <span className="font-sans text-xs text-rose-400">Failed</span>;
|
|
700
|
-
}
|
|
701
|
-
if (props.status === "stopped") {
|
|
702
|
-
return (
|
|
703
|
-
<span className="font-sans text-xs text-dashboard-text-muted">
|
|
704
|
-
Stopped
|
|
705
|
-
</span>
|
|
706
|
-
);
|
|
707
|
-
}
|
|
685
|
+
const isPrivate = props.conversation.visibility === "private";
|
|
708
686
|
return (
|
|
709
|
-
<span className="
|
|
710
|
-
{
|
|
687
|
+
<span className="flex items-center gap-1">
|
|
688
|
+
<ConversationStatusText isPrivate={isPrivate} status={props.status} />
|
|
689
|
+
{props.status === "active" ? (
|
|
690
|
+
<Spinner
|
|
691
|
+
aria-label="Running"
|
|
692
|
+
className="size-3.5 text-dashboard-text-muted"
|
|
693
|
+
/>
|
|
694
|
+
) : props.status === "failed" || props.status === "stopped" ? null : (
|
|
695
|
+
<span className="font-sans text-xs tabular-nums text-dashboard-text-faint">
|
|
696
|
+
{formatCompactAge(
|
|
697
|
+
props.conversation.lastMessageAt ?? props.conversation.lastSeenAt,
|
|
698
|
+
)}
|
|
699
|
+
</span>
|
|
700
|
+
)}
|
|
711
701
|
</span>
|
|
712
702
|
);
|
|
713
703
|
}
|
|
@@ -51,10 +51,7 @@ import {
|
|
|
51
51
|
FOCUS_REPLY_STATE,
|
|
52
52
|
NEW_CONVERSATION_PATH,
|
|
53
53
|
} from "./conversationRoutes";
|
|
54
|
-
import {
|
|
55
|
-
attentionConversationIds,
|
|
56
|
-
pinnedAndAttentionFirst,
|
|
57
|
-
} from "./conversationSections";
|
|
54
|
+
import { pinnedAndWaitingFirst } from "./conversationSections";
|
|
58
55
|
import { buildConversations, getDashboardTimeZone } from "../format";
|
|
59
56
|
import { useDebouncedValue } from "../debouncedValue";
|
|
60
57
|
import { usePushNotificationsTip } from "../notifications/pushNotificationsTip";
|
|
@@ -63,10 +60,7 @@ import type { Conversation } from "../types";
|
|
|
63
60
|
import { ConversationPage } from "./ConversationPage";
|
|
64
61
|
import { useAutoLoadHistoryPage } from "./transcriptBottomPinning";
|
|
65
62
|
import { FeedThreads, feedThreadsById } from "./feedThreads";
|
|
66
|
-
import {
|
|
67
|
-
useWaitingConversationIds,
|
|
68
|
-
useWaitingConversations,
|
|
69
|
-
} from "../review-desk/queries";
|
|
63
|
+
import { useWaitingConversations } from "../review-desk/queries";
|
|
70
64
|
import { UnsentAnswersProvider } from "../review-desk/UnsentAnswers";
|
|
71
65
|
import { useReorderPinnedConversations } from "./useReorderPinnedConversations";
|
|
72
66
|
import {
|
|
@@ -146,17 +140,12 @@ export function ConversationWorkspace(props: {
|
|
|
146
140
|
[conversations],
|
|
147
141
|
);
|
|
148
142
|
const waitingConversations = useWaitingConversations();
|
|
149
|
-
const waitingConversationIds = useWaitingConversationIds();
|
|
150
143
|
|
|
151
144
|
// The feed holds every row. The home list shows them a batch at a time
|
|
152
145
|
// and shows the next batch when the reader scrolls near its end.
|
|
153
146
|
const orderedConversations = useMemo(
|
|
154
|
-
() =>
|
|
155
|
-
|
|
156
|
-
conversations,
|
|
157
|
-
attentionConversationIds(conversations, waitingConversationIds),
|
|
158
|
-
),
|
|
159
|
-
[conversations, waitingConversationIds],
|
|
147
|
+
() => pinnedAndWaitingFirst(conversations, waitingConversations),
|
|
148
|
+
[conversations, waitingConversations],
|
|
160
149
|
);
|
|
161
150
|
const visibleConversations = useMemo(
|
|
162
151
|
() => orderedConversations.slice(0, visibleCount),
|
|
@@ -328,7 +317,7 @@ export function ConversationWorkspace(props: {
|
|
|
328
317
|
: reorderPins
|
|
329
318
|
}
|
|
330
319
|
timeZone={getDashboardTimeZone()}
|
|
331
|
-
|
|
320
|
+
waitingConversations={waitingConversations}
|
|
332
321
|
/>
|
|
333
322
|
{hiddenCount > 0 ? (
|
|
334
323
|
<div
|
|
@@ -7,9 +7,12 @@ import { createPortal } from "react-dom";
|
|
|
7
7
|
import { HighlightedCode, HighlightedCodeFallback } from "../code";
|
|
8
8
|
import { Drawer } from "../components/Drawer";
|
|
9
9
|
import { InlineError } from "../components/InlineError";
|
|
10
|
+
import {
|
|
11
|
+
MarkdownViewToggle,
|
|
12
|
+
type MarkdownView,
|
|
13
|
+
} from "../components/MarkdownViewToggle";
|
|
10
14
|
import { buttonVariants } from "../components/ui/button";
|
|
11
15
|
import { Skeleton } from "../components/ui/skeleton";
|
|
12
|
-
import { ToggleGroup, ToggleGroupItem } from "../components/ui/toggle-group";
|
|
13
16
|
import { DisplayText } from "../display-text/DisplayText";
|
|
14
17
|
import { readDashboardBytes } from "../http";
|
|
15
18
|
import {
|
|
@@ -18,8 +21,6 @@ import {
|
|
|
18
21
|
type TextPreviewLanguage,
|
|
19
22
|
} from "./textPreview";
|
|
20
23
|
|
|
21
|
-
type MarkdownView = "formatted" | "raw";
|
|
22
|
-
|
|
23
24
|
const SKELETON_LINE_WIDTHS = ["w-3/4", "w-full", "w-5/6", "w-2/3", "w-full"];
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -61,17 +62,7 @@ export function TextAttachmentPreview(props: {
|
|
|
61
62
|
actions={
|
|
62
63
|
<>
|
|
63
64
|
{markdown ? (
|
|
64
|
-
<
|
|
65
|
-
aria-label="Markdown view"
|
|
66
|
-
onValueChange={(values) => {
|
|
67
|
-
const next = values[0];
|
|
68
|
-
if (next) setView(next);
|
|
69
|
-
}}
|
|
70
|
-
value={[view]}
|
|
71
|
-
>
|
|
72
|
-
<ToggleGroupItem value="formatted">Formatted</ToggleGroupItem>
|
|
73
|
-
<ToggleGroupItem value="raw">Raw</ToggleGroupItem>
|
|
74
|
-
</ToggleGroup>
|
|
65
|
+
<MarkdownViewToggle onChange={setView} value={view} />
|
|
75
66
|
) : null}
|
|
76
67
|
<a
|
|
77
68
|
aria-label={`Download ${filename}`}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ChevronRight, Inbox } from "lucide-react";
|
|
2
|
+
import { Link } from "react-router";
|
|
3
|
+
import {
|
|
4
|
+
Tooltip,
|
|
5
|
+
TooltipContent,
|
|
6
|
+
TooltipTrigger,
|
|
7
|
+
} from "../components/ui/tooltip";
|
|
8
|
+
import type { WaitingConversation } from "../review-desk/queries";
|
|
9
|
+
import { cn } from "../styles";
|
|
10
|
+
import { conversationPath } from "./conversationRoutes";
|
|
11
|
+
import { GitHubMark } from "./GitHubMark";
|
|
12
|
+
import { pluginSource } from "./transcriptSource";
|
|
13
|
+
|
|
14
|
+
/** Show the plugin's mark for a plugin item, and the Inbox icon otherwise. */
|
|
15
|
+
function WaitingItemMark(props: { plugin?: string }) {
|
|
16
|
+
return pluginSource(props.plugin) === "github" ? (
|
|
17
|
+
<GitHubMark className="size-3 shrink-0 text-dashboard-text-muted" />
|
|
18
|
+
) : (
|
|
19
|
+
<Inbox
|
|
20
|
+
aria-hidden="true"
|
|
21
|
+
className="size-3 shrink-0 text-dashboard-text-muted"
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Link a waiting row to its open Review items. The chip shows the newest
|
|
28
|
+
* title, and its tooltip lists every title in full.
|
|
29
|
+
*/
|
|
30
|
+
export function WaitingChip(props: {
|
|
31
|
+
conversationId: string;
|
|
32
|
+
waiting: WaitingConversation;
|
|
33
|
+
}) {
|
|
34
|
+
const [newest, ...older] = props.waiting.items;
|
|
35
|
+
if (!newest) return null;
|
|
36
|
+
return (
|
|
37
|
+
<Tooltip>
|
|
38
|
+
<TooltipTrigger
|
|
39
|
+
render={
|
|
40
|
+
<Link
|
|
41
|
+
className="mb-1.5 ml-[2.125rem] mr-3 flex min-w-0 items-center gap-1.5 rounded-md border border-dashboard-border-strong bg-dashboard-surface-raised px-2 py-1 font-sans text-xs text-dashboard-text no-underline transition-colors hover:border-dashboard-border-interactive focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-dashboard-focus"
|
|
42
|
+
to={conversationPath(props.conversationId, { reviewItems: true })}
|
|
43
|
+
>
|
|
44
|
+
<WaitingItemMark plugin={newest.plugin} />
|
|
45
|
+
<span className="min-w-0 truncate">{newest.title}</span>
|
|
46
|
+
{older.length > 0 ? (
|
|
47
|
+
<span className="ml-auto shrink-0 text-dashboard-text-faint">
|
|
48
|
+
+{older.length} more
|
|
49
|
+
</span>
|
|
50
|
+
) : null}
|
|
51
|
+
<ChevronRight
|
|
52
|
+
aria-hidden="true"
|
|
53
|
+
className={cn(
|
|
54
|
+
"size-3 shrink-0 text-dashboard-text-muted",
|
|
55
|
+
older.length > 0 ? undefined : "ml-auto",
|
|
56
|
+
)}
|
|
57
|
+
/>
|
|
58
|
+
</Link>
|
|
59
|
+
}
|
|
60
|
+
/>
|
|
61
|
+
<TooltipContent align="start" side="right">
|
|
62
|
+
<ul className="flex flex-col gap-1">
|
|
63
|
+
{props.waiting.items.map((item, index) => (
|
|
64
|
+
<li
|
|
65
|
+
className="flex items-start gap-1.5 text-dashboard-text"
|
|
66
|
+
key={index}
|
|
67
|
+
>
|
|
68
|
+
<span className="mt-0.5 flex">
|
|
69
|
+
<WaitingItemMark plugin={item.plugin} />
|
|
70
|
+
</span>
|
|
71
|
+
<span className="min-w-0 break-words">{item.title}</span>
|
|
72
|
+
</li>
|
|
73
|
+
))}
|
|
74
|
+
</ul>
|
|
75
|
+
</TooltipContent>
|
|
76
|
+
</Tooltip>
|
|
77
|
+
);
|
|
78
|
+
}
|