@sentry/junior-dashboard 0.276.0 → 0.278.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 +196 -72
- package/dist/client.js +94 -89
- package/dist/index.d.ts +1 -1
- package/dist/index.js +191 -70
- package/dist/tailwind.css +1 -1
- package/package.json +5 -4
- package/src/api/schema.ts +20 -2
- package/src/app.ts +80 -12
- package/src/auth.ts +5 -1
- package/src/client/App.tsx +9 -4
- package/src/client/components/DetailsPanel.tsx +1 -1
- package/src/client/components/FormNotice.tsx +11 -14
- package/src/client/components/ProfileMenu.tsx +5 -6
- package/src/client/components/ui/resizable.tsx +50 -0
- package/src/client/conversations/AutoArchiveToggle.tsx +71 -0
- package/src/client/conversations/ConversationArchiveNotices.tsx +1 -1
- package/src/client/conversations/ConversationChildAgents.tsx +2 -1
- package/src/client/conversations/ConversationDetails.tsx +6 -3
- package/src/client/conversations/ConversationHeader.tsx +15 -2
- package/src/client/conversations/ConversationHeaderActions.tsx +27 -0
- package/src/client/conversations/ConversationHomeList.tsx +1 -1
- package/src/client/conversations/ConversationMemories.tsx +64 -67
- package/src/client/conversations/ConversationMeta.tsx +2 -7
- package/src/client/conversations/ConversationPage.tsx +175 -79
- package/src/client/conversations/ConversationSidebar.tsx +82 -25
- package/src/client/conversations/ConversationTranscript.tsx +11 -8
- package/src/client/conversations/ConversationWatches.tsx +15 -21
- package/src/client/conversations/ConversationWorkspace.tsx +132 -44
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +17 -3
- package/src/client/conversations/DetailsList.tsx +8 -14
- package/src/client/conversations/EventDetails.tsx +2 -62
- package/src/client/conversations/MemoryIcons.tsx +23 -0
- package/src/client/conversations/TranscriptActivityGroup.tsx +36 -14
- package/src/client/conversations/TranscriptContextEventView.tsx +7 -18
- package/src/client/conversations/TranscriptMemoryPopover.tsx +20 -25
- package/src/client/conversations/TranscriptRailEvent.tsx +0 -8
- package/src/client/conversations/TranscriptTurnContextView.tsx +9 -5
- package/src/client/conversations/TranscriptView.tsx +89 -20
- package/src/client/conversations/autoArchiveQueries.ts +53 -0
- package/src/client/conversations/conversationArchive.ts +255 -0
- package/src/client/conversations/conversationMemory.ts +2 -1
- package/src/client/conversations/conversationSections.ts +24 -18
- package/src/client/conversations/conversationSidebarLayout.ts +61 -0
- package/src/client/conversations/eventLog.ts +2 -31
- package/src/client/conversations/eventTranscript.ts +9 -42
- package/src/client/conversations/queries.ts +121 -250
- package/src/client/conversations/transcript.ts +46 -16
- package/src/client/conversations/transcriptBottomPinning.ts +40 -24
- package/src/client/conversations/transcriptReadPosition.ts +31 -7
- package/src/client/conversations/transcriptRenderModel.ts +1 -14
- package/src/client/conversations/transcriptSearch.tsx +3 -11
- package/src/client/conversations/transcriptUnreadHistory.ts +248 -0
- package/src/client/conversations/usePinConversation.ts +88 -0
- package/src/client/display-text/DisplayText.tsx +7 -2
- package/src/client/display-text/codeBlocks.ts +92 -0
- package/src/client/format.ts +3 -67
- package/src/client/http.ts +5 -5
- package/src/client/markdownExport.ts +32 -42
- package/src/client/pages/code/RepositoryTabs.tsx +2 -2
- package/src/client/pages/memory/MemoryDetails.tsx +6 -33
- package/src/client/pages/memory/MemoryPage.tsx +7 -92
- package/src/client/pages/memory/MemoryTimeline.tsx +16 -32
- package/src/client/pages/memory/memoryDashboard.ts +2 -38
- package/src/client/pages/memory/memoryRecord.ts +1 -6
- package/src/client/pages/settings/AutoArchiveSettings.tsx +145 -0
- package/src/client/pages/settings/SettingsPage.tsx +2 -0
- package/src/client/review-desk/ConversationReviewItems.tsx +7 -9
- package/src/client/review-desk/queries.ts +2 -1
- package/src/client/types.ts +19 -33
- package/src/conversation-cache.ts +7 -9
- package/src/mock-reporting/fixtures.ts +126 -60
- package/src/mock-reporting/routes.ts +50 -1
- package/src/tailwind.css +30 -0
|
@@ -135,12 +135,36 @@ const DASHBOARD_QA_PLAN_CHECK_ID =
|
|
|
135
135
|
"junior:internal:dashboard-qa:advisor-plan:claim-check";
|
|
136
136
|
/** Process-local archive state so mock restore can be exercised in visual QA. */
|
|
137
137
|
const mockArchivedConversationIds = new Set<string>([ARCHIVED_CONVERSATION_ID]);
|
|
138
|
+
/** Process-local pins so the Pinned section can be exercised in visual QA. */
|
|
139
|
+
const mockPinnedConversationIds = new Set<string>();
|
|
138
140
|
|
|
139
|
-
/**
|
|
140
|
-
|
|
141
|
+
/** Process-local auto-archive choices for mock visual QA. */
|
|
142
|
+
const mockAutoArchiveByConversationId = new Map<string, boolean>();
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Restore process-local archive, pin, and auto-archive state so tests sharing
|
|
146
|
+
* a worker do not leak state.
|
|
147
|
+
*/
|
|
148
|
+
export function resetMockConversationViewerState(): void {
|
|
141
149
|
mockArchivedConversationIds.clear();
|
|
142
150
|
mockArchivedConversationIds.add(ARCHIVED_CONVERSATION_ID);
|
|
151
|
+
mockPinnedConversationIds.clear();
|
|
152
|
+
mockAutoArchiveByConversationId.clear();
|
|
143
153
|
}
|
|
154
|
+
|
|
155
|
+
/** Add the viewer's mock archive and pin times to one conversation. */
|
|
156
|
+
function withMockViewerState<T extends { conversationId: string }>(
|
|
157
|
+
report: T & { archivedAt?: string | null },
|
|
158
|
+
): T & { archivedAt: string | undefined; pinnedAt: string | null } {
|
|
159
|
+
return {
|
|
160
|
+
...report,
|
|
161
|
+
archivedAt: mockArchivedConversationIds.has(report.conversationId)
|
|
162
|
+
? (report.archivedAt ?? iso(NOW_MS, -2 * 24 * 60 * 60_000))
|
|
163
|
+
: undefined,
|
|
164
|
+
pinnedAt: mockPinnedConversationIds.has(report.conversationId) ? NOW : null,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
144
168
|
const PEOPLE_ACTIVITY_DAYS = 90;
|
|
145
169
|
const PEOPLE_PROFILE_ACTIVITY_DAYS = 365;
|
|
146
170
|
const PUBLIC_MOCK_CHANNEL_IDS = new Set([
|
|
@@ -452,16 +476,27 @@ function activeConversation(nowMs: number): ConversationDetailReport {
|
|
|
452
476
|
],
|
|
453
477
|
},
|
|
454
478
|
}),
|
|
455
|
-
reportEvent(
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
479
|
+
reportEvent(
|
|
480
|
+
3,
|
|
481
|
+
iso(Date.parse(startedAt), 9_000),
|
|
482
|
+
{
|
|
483
|
+
type: "assistant_message",
|
|
484
|
+
parts: [
|
|
485
|
+
{
|
|
486
|
+
type: "reasoning",
|
|
487
|
+
text: "Compare the latest deploy with the previous baseline.",
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
model: { modelId: "openai/gpt-5.6-sol" },
|
|
493
|
+
modelCall: {
|
|
494
|
+
provider: "vercel-ai-gateway",
|
|
495
|
+
api: "openai-responses",
|
|
496
|
+
stopReason: "toolUse",
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
),
|
|
465
500
|
reportEvent(
|
|
466
501
|
4,
|
|
467
502
|
iso(Date.parse(startedAt), 10_000),
|
|
@@ -476,11 +511,7 @@ function activeConversation(nowMs: number): ConversationDetailReport {
|
|
|
476
511
|
],
|
|
477
512
|
},
|
|
478
513
|
{
|
|
479
|
-
model: {
|
|
480
|
-
modelId: "openai/gpt-5.6-sol",
|
|
481
|
-
modelProfile: "handoff",
|
|
482
|
-
reasoningLevel: "high",
|
|
483
|
-
},
|
|
514
|
+
model: { modelId: "openai/gpt-5.6-sol" },
|
|
484
515
|
modelCall: {
|
|
485
516
|
provider: "vercel-ai-gateway",
|
|
486
517
|
api: "openai-responses",
|
|
@@ -528,11 +559,7 @@ function activeConversation(nowMs: number): ConversationDetailReport {
|
|
|
528
559
|
],
|
|
529
560
|
},
|
|
530
561
|
{
|
|
531
|
-
model: {
|
|
532
|
-
modelId: "openai/gpt-5.6-sol",
|
|
533
|
-
modelProfile: "handoff",
|
|
534
|
-
reasoningLevel: "high",
|
|
535
|
-
},
|
|
562
|
+
model: { modelId: "openai/gpt-5.6-sol" },
|
|
536
563
|
},
|
|
537
564
|
),
|
|
538
565
|
// Mixed markdown keeps font/legibility QA honest for long assistant replies.
|
|
@@ -889,7 +916,6 @@ function dashboardQaConversation(nowMs: number): ConversationDetailReport {
|
|
|
889
916
|
}),
|
|
890
917
|
reportEvent(11, iso(Date.parse(startedAt), 56_000), {
|
|
891
918
|
type: "compaction",
|
|
892
|
-
modelProfile: "standard",
|
|
893
919
|
modelId: "openai/gpt-5.4",
|
|
894
920
|
details: {
|
|
895
921
|
reason: "capacity",
|
|
@@ -1095,7 +1121,6 @@ function longConversation(nowMs: number): ConversationDetailReport {
|
|
|
1095
1121
|
events.push(
|
|
1096
1122
|
reportEvent(nextSeq, iso(Date.parse(startedAt), 53_000), {
|
|
1097
1123
|
type: "compaction",
|
|
1098
|
-
modelProfile: "standard",
|
|
1099
1124
|
modelId: "openai/gpt-5.4",
|
|
1100
1125
|
summary:
|
|
1101
1126
|
"The release is complete. Preserve the published version and continue by checking the update pull request.",
|
|
@@ -1112,17 +1137,6 @@ function longConversation(nowMs: number): ConversationDetailReport {
|
|
|
1112
1137
|
}),
|
|
1113
1138
|
);
|
|
1114
1139
|
nextSeq += 1;
|
|
1115
|
-
events.push(
|
|
1116
|
-
reportEvent(nextSeq, iso(Date.parse(startedAt), 90_000), {
|
|
1117
|
-
type: "handoff",
|
|
1118
|
-
modelProfile: "fast",
|
|
1119
|
-
modelId: "openai/gpt-5-mini",
|
|
1120
|
-
reasoningLevel: "medium",
|
|
1121
|
-
summary:
|
|
1122
|
-
"Investigate the remaining deployment checks and report any actionable failure.",
|
|
1123
|
-
}),
|
|
1124
|
-
);
|
|
1125
|
-
nextSeq += 1;
|
|
1126
1140
|
events.push(
|
|
1127
1141
|
reportEvent(nextSeq, iso(Date.parse(startedAt), 166_000), {
|
|
1128
1142
|
type: "message",
|
|
@@ -1657,19 +1671,11 @@ function summaryFromConversation(
|
|
|
1657
1671
|
...summary,
|
|
1658
1672
|
...(activityPreview ? { activityPreview } : undefined),
|
|
1659
1673
|
};
|
|
1660
|
-
const
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
? {
|
|
1664
|
-
|
|
1665
|
-
archivedAt:
|
|
1666
|
-
summaryWithActivity.archivedAt ?? iso(NOW_MS, -2 * 24 * 60 * 60_000),
|
|
1667
|
-
}
|
|
1668
|
-
: { ...summaryWithActivity, archivedAt: undefined };
|
|
1669
|
-
return withArchiveState.channel &&
|
|
1670
|
-
PUBLIC_MOCK_CHANNEL_IDS.has(withArchiveState.channel)
|
|
1671
|
-
? { ...withArchiveState, locationId: `mock:${withArchiveState.channel}` }
|
|
1672
|
-
: withArchiveState;
|
|
1674
|
+
const withViewerState = withMockViewerState(summaryWithActivity);
|
|
1675
|
+
return withViewerState.channel &&
|
|
1676
|
+
PUBLIC_MOCK_CHANNEL_IDS.has(withViewerState.channel)
|
|
1677
|
+
? { ...withViewerState, locationId: `mock:${withViewerState.channel}` }
|
|
1678
|
+
: withViewerState;
|
|
1673
1679
|
}
|
|
1674
1680
|
|
|
1675
1681
|
function mockConversationFeed(nowMs: number): ConversationFeed {
|
|
@@ -1924,11 +1930,70 @@ export function setMockConversationArchived(
|
|
|
1924
1930
|
(conversation) => conversation.conversationId === conversationId,
|
|
1925
1931
|
);
|
|
1926
1932
|
if (!exists) return undefined;
|
|
1927
|
-
if (archived)
|
|
1928
|
-
|
|
1933
|
+
if (archived) {
|
|
1934
|
+
mockArchivedConversationIds.add(conversationId);
|
|
1935
|
+
// Match the API: archive also unpins.
|
|
1936
|
+
mockPinnedConversationIds.delete(conversationId);
|
|
1937
|
+
} else {
|
|
1938
|
+
mockArchivedConversationIds.delete(conversationId);
|
|
1939
|
+
// Match the API: a manual restore turns auto-archive off.
|
|
1940
|
+
mockAutoArchiveByConversationId.set(conversationId, false);
|
|
1941
|
+
}
|
|
1929
1942
|
return { archivedAt: archived ? NOW : null };
|
|
1930
1943
|
}
|
|
1931
1944
|
|
|
1945
|
+
/** Pin or unpin one mock conversation for local dashboard visual QA. */
|
|
1946
|
+
export function setMockConversationPinned(
|
|
1947
|
+
conversationId: string,
|
|
1948
|
+
pinned: boolean,
|
|
1949
|
+
): { pinnedAt: string | null } | undefined {
|
|
1950
|
+
const exists = mockConversations(NOW_MS).some(
|
|
1951
|
+
(conversation) => conversation.conversationId === conversationId,
|
|
1952
|
+
);
|
|
1953
|
+
if (!exists) return undefined;
|
|
1954
|
+
if (pinned) {
|
|
1955
|
+
mockPinnedConversationIds.add(conversationId);
|
|
1956
|
+
// Match the API: a pin also restores an archived conversation.
|
|
1957
|
+
mockArchivedConversationIds.delete(conversationId);
|
|
1958
|
+
} else {
|
|
1959
|
+
mockPinnedConversationIds.delete(conversationId);
|
|
1960
|
+
}
|
|
1961
|
+
return { pinnedAt: pinned ? NOW : null };
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
/** Archive every done mock conversation once, like the archive-done API. */
|
|
1965
|
+
export function archiveMockDoneConversations(): { archived: number } {
|
|
1966
|
+
let archived = 0;
|
|
1967
|
+
for (const summary of mockConversationFeed(NOW_MS).conversations) {
|
|
1968
|
+
if (
|
|
1969
|
+
summary.archivedAt ||
|
|
1970
|
+
summary.pinnedAt ||
|
|
1971
|
+
!summary.assignedWork ||
|
|
1972
|
+
summary.unfinishedWork ||
|
|
1973
|
+
!summary.finishedWorkAt ||
|
|
1974
|
+
mockAutoArchiveByConversationId.get(summary.conversationId) === false
|
|
1975
|
+
) {
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
mockArchivedConversationIds.add(summary.conversationId);
|
|
1979
|
+
archived += 1;
|
|
1980
|
+
}
|
|
1981
|
+
return { archived };
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
/** Save a mock auto-archive choice for local dashboard visual QA. */
|
|
1985
|
+
export function setMockConversationAutoArchive(
|
|
1986
|
+
conversationId: string,
|
|
1987
|
+
autoArchive: boolean,
|
|
1988
|
+
): { autoArchive: boolean } | undefined {
|
|
1989
|
+
const exists = mockConversations(NOW_MS).some(
|
|
1990
|
+
(conversation) => conversation.conversationId === conversationId,
|
|
1991
|
+
);
|
|
1992
|
+
if (!exists) return undefined;
|
|
1993
|
+
mockAutoArchiveByConversationId.set(conversationId, autoArchive);
|
|
1994
|
+
return { autoArchive };
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1932
1997
|
/** Return accepted mailbox rows for local dashboard visual QA. */
|
|
1933
1998
|
export function readMockConversationPendingMessages(
|
|
1934
1999
|
conversationId: string,
|
|
@@ -2151,22 +2216,23 @@ export function readMockConversationDetail(
|
|
|
2151
2216
|
canFork:
|
|
2152
2217
|
!parentConversationId &&
|
|
2153
2218
|
conversationDetail.eventHistory.status !== "redacted",
|
|
2219
|
+
...(conversationDetail.isParticipant
|
|
2220
|
+
? {
|
|
2221
|
+
autoArchive:
|
|
2222
|
+
mockAutoArchiveByConversationId.get(conversationId) ?? false,
|
|
2223
|
+
}
|
|
2224
|
+
: undefined),
|
|
2154
2225
|
};
|
|
2155
|
-
const
|
|
2156
|
-
|
|
2157
|
-
...detail,
|
|
2158
|
-
archivedAt: detail.archivedAt ?? iso(NOW_MS, -2 * 24 * 60 * 60_000),
|
|
2159
|
-
}
|
|
2160
|
-
: { ...detail, archivedAt: undefined };
|
|
2161
|
-
const events = withArchiveState.events.slice(-limit);
|
|
2226
|
+
const withViewerState = withMockViewerState(detail);
|
|
2227
|
+
const events = withViewerState.events.slice(-limit);
|
|
2162
2228
|
const bounded =
|
|
2163
|
-
events.length <
|
|
2229
|
+
events.length < withViewerState.events.length && events[0]
|
|
2164
2230
|
? {
|
|
2165
|
-
...
|
|
2231
|
+
...withViewerState,
|
|
2166
2232
|
events,
|
|
2167
2233
|
previousCursor: mockBeforeCursor(conversationId, events[0].seq),
|
|
2168
2234
|
}
|
|
2169
|
-
:
|
|
2235
|
+
: withViewerState;
|
|
2170
2236
|
return bounded.channel && PUBLIC_MOCK_CHANNEL_IDS.has(bounded.channel)
|
|
2171
2237
|
? { ...bounded, locationId: `mock:${bounded.channel}` }
|
|
2172
2238
|
: bounded;
|
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
actorProfileReportSchema,
|
|
8
8
|
archiveConversationBodySchema,
|
|
9
9
|
archiveConversationResponseSchema,
|
|
10
|
+
pinConversationBodySchema,
|
|
11
|
+
pinConversationResponseSchema,
|
|
12
|
+
archiveDoneConversationsResponseSchema,
|
|
13
|
+
conversationAutoArchiveSchema,
|
|
10
14
|
conversationChildAgentsReportSchema,
|
|
11
15
|
conversationDetailQuerySchema,
|
|
12
16
|
conversationDetailReportSchema,
|
|
@@ -66,6 +70,9 @@ import {
|
|
|
66
70
|
readMockAutomationExecutions,
|
|
67
71
|
readMockAutomationList,
|
|
68
72
|
setMockConversationArchived,
|
|
73
|
+
setMockConversationPinned,
|
|
74
|
+
setMockConversationAutoArchive,
|
|
75
|
+
archiveMockDoneConversations,
|
|
69
76
|
} from "./fixtures";
|
|
70
77
|
import { readMockConversationWatches } from "./watches";
|
|
71
78
|
|
|
@@ -190,7 +197,9 @@ export function createMockReportingApi(): Hono<{
|
|
|
190
197
|
if (!params.success) {
|
|
191
198
|
return errorResponse("Invalid route parameters.", 400);
|
|
192
199
|
}
|
|
193
|
-
|
|
200
|
+
// Mock details fit one page and have no unread cursor, so mock history
|
|
201
|
+
// only pages backward.
|
|
202
|
+
if (!query.success || !("before" in query.data)) {
|
|
194
203
|
return errorResponse("Invalid query parameters.", 400);
|
|
195
204
|
}
|
|
196
205
|
if (!readMockConversationDetail(params.data.conversationId)) {
|
|
@@ -289,6 +298,46 @@ export function createMockReportingApi(): Hono<{
|
|
|
289
298
|
? jsonResponse(archiveConversationResponseSchema, result)
|
|
290
299
|
: errorResponse("Conversation not found.", 404);
|
|
291
300
|
});
|
|
301
|
+
app.patch("/conversations/:conversationId/pin", async (c) => {
|
|
302
|
+
const params = conversationParamsSchema.safeParse(c.req.param());
|
|
303
|
+
if (!params.success) {
|
|
304
|
+
return errorResponse("Invalid route parameters.", 400);
|
|
305
|
+
}
|
|
306
|
+
const body = pinConversationBodySchema.safeParse(await c.req.json());
|
|
307
|
+
if (!body.success) {
|
|
308
|
+
return errorResponse("Invalid request body.", 400);
|
|
309
|
+
}
|
|
310
|
+
const result = setMockConversationPinned(
|
|
311
|
+
params.data.conversationId,
|
|
312
|
+
body.data.pinned,
|
|
313
|
+
);
|
|
314
|
+
return result
|
|
315
|
+
? jsonResponse(pinConversationResponseSchema, result)
|
|
316
|
+
: errorResponse("Conversation not found.", 404);
|
|
317
|
+
});
|
|
318
|
+
app.post("/conversations/archive-done", () =>
|
|
319
|
+
jsonResponse(
|
|
320
|
+
archiveDoneConversationsResponseSchema,
|
|
321
|
+
archiveMockDoneConversations(),
|
|
322
|
+
),
|
|
323
|
+
);
|
|
324
|
+
app.patch("/conversations/:conversationId/auto-archive", async (c) => {
|
|
325
|
+
const params = conversationParamsSchema.safeParse(c.req.param());
|
|
326
|
+
if (!params.success) {
|
|
327
|
+
return errorResponse("Invalid route parameters.", 400);
|
|
328
|
+
}
|
|
329
|
+
const body = conversationAutoArchiveSchema.safeParse(await c.req.json());
|
|
330
|
+
if (!body.success) {
|
|
331
|
+
return errorResponse("Invalid request body.", 400);
|
|
332
|
+
}
|
|
333
|
+
const result = setMockConversationAutoArchive(
|
|
334
|
+
params.data.conversationId,
|
|
335
|
+
body.data.autoArchive,
|
|
336
|
+
);
|
|
337
|
+
return result
|
|
338
|
+
? jsonResponse(conversationAutoArchiveSchema, result)
|
|
339
|
+
: errorResponse("Conversation not found.", 404);
|
|
340
|
+
});
|
|
292
341
|
// Fixed bodies so dashboard mock can exercise image/file attachment cards.
|
|
293
342
|
app.get("/conversations/:conversationId/attachments/:attachmentId", (c) => {
|
|
294
343
|
const conversationId = c.req.param("conversationId");
|
package/src/tailwind.css
CHANGED
|
@@ -327,6 +327,36 @@
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
@utility thin-scrollbar {
|
|
331
|
+
--thin-scrollbar-thumb: color-mix(
|
|
332
|
+
in srgb,
|
|
333
|
+
var(--color-dashboard-scrollbar-thumb) 80%,
|
|
334
|
+
transparent
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
&::-webkit-scrollbar {
|
|
338
|
+
width: 6px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
&::-webkit-scrollbar-track {
|
|
342
|
+
background: transparent;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
&::-webkit-scrollbar-thumb {
|
|
346
|
+
border-inline: 1px solid transparent;
|
|
347
|
+
background: var(--thin-scrollbar-thumb) padding-box;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
351
|
+
background-color: var(--color-dashboard-scrollbar-thumb-hover);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@supports not selector(::-webkit-scrollbar-thumb) {
|
|
355
|
+
scrollbar-color: var(--thin-scrollbar-thumb) transparent;
|
|
356
|
+
scrollbar-width: thin;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
330
360
|
@source "./app.ts";
|
|
331
361
|
@source "./dashboardLoader.ts";
|
|
332
362
|
@source "./client.tsx";
|