@tutti-os/workbench-launchpad 0.0.27
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/LICENSE +202 -0
- package/README.md +3 -0
- package/dist/index.d.ts +178 -0
- package/dist/index.js +816 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/workbench-launchpad.css +485 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
// src/WorkbenchLaunchpadOverlay.tsx
|
|
2
|
+
import {
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useLayoutEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
10
|
+
import {
|
|
11
|
+
CloseIcon,
|
|
12
|
+
NavApplicationsFilledIcon,
|
|
13
|
+
SearchIcon
|
|
14
|
+
} from "@tutti-os/ui-system";
|
|
15
|
+
|
|
16
|
+
// src/launchpadModel.ts
|
|
17
|
+
var workbenchLaunchpadDockActionId = "open-launchpad";
|
|
18
|
+
var workbenchLaunchpadDockEntryId = "workspace-launchpad";
|
|
19
|
+
var launchpadPreviewIconCount = 4;
|
|
20
|
+
var launchpadMinColumns = 2;
|
|
21
|
+
var launchpadMaxColumns = 7;
|
|
22
|
+
var launchpadMinRows = 1;
|
|
23
|
+
var launchpadMaxRows = 5;
|
|
24
|
+
var launchpadTileWidth = 136;
|
|
25
|
+
var launchpadTileHeight = 138;
|
|
26
|
+
function buildWorkbenchLaunchpadItems(input) {
|
|
27
|
+
const nodeItems = (input.nodeDescriptors ?? []).map((node) => ({
|
|
28
|
+
disabledReason: node.disabledReason,
|
|
29
|
+
dockEntryId: node.dockEntryId,
|
|
30
|
+
iconUrl: node.iconUrl,
|
|
31
|
+
id: `node:${node.id}`,
|
|
32
|
+
kind: "node",
|
|
33
|
+
label: node.label,
|
|
34
|
+
launchEnabled: node.launchEnabled ?? true,
|
|
35
|
+
typeId: node.typeId
|
|
36
|
+
}));
|
|
37
|
+
const pinnedNodeItems = nodeItems.slice(0, 2);
|
|
38
|
+
const remainingNodeItems = nodeItems.slice(2);
|
|
39
|
+
return [
|
|
40
|
+
...pinnedNodeItems,
|
|
41
|
+
...(input.apps ?? []).map((app) => ({
|
|
42
|
+
appId: app.appId,
|
|
43
|
+
disabledReason: app.disabledReason,
|
|
44
|
+
iconUrl: app.iconUrl,
|
|
45
|
+
id: app.id ?? `app:${app.appId}`,
|
|
46
|
+
kind: "app",
|
|
47
|
+
label: app.label,
|
|
48
|
+
launchEnabled: app.launchEnabled
|
|
49
|
+
})),
|
|
50
|
+
...remainingNodeItems,
|
|
51
|
+
...(input.agentDescriptors ?? []).map((agent) => ({
|
|
52
|
+
action: agent.action,
|
|
53
|
+
actions: agent.actions,
|
|
54
|
+
comingSoon: agent.comingSoon,
|
|
55
|
+
disabledReason: agent.disabledReason,
|
|
56
|
+
iconUrl: agent.iconUrl,
|
|
57
|
+
id: agent.id ?? `agent:${agent.provider}`,
|
|
58
|
+
kind: "agent",
|
|
59
|
+
label: agent.label,
|
|
60
|
+
launchEnabled: agent.launchEnabled,
|
|
61
|
+
provider: agent.provider,
|
|
62
|
+
reason: agent.reason
|
|
63
|
+
}))
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
function filterWorkbenchLaunchpadItems(items, query) {
|
|
67
|
+
const normalizedQuery = normalizeLaunchpadSearchText(query);
|
|
68
|
+
if (!normalizedQuery) {
|
|
69
|
+
return [...items];
|
|
70
|
+
}
|
|
71
|
+
return items.filter(
|
|
72
|
+
(item) => normalizeLaunchpadSearchText(item.label).includes(normalizedQuery)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function paginateWorkbenchLaunchpadItems(items, input) {
|
|
76
|
+
const pageSize = Math.max(1, Math.floor(input.pageSize));
|
|
77
|
+
const pageCount = Math.max(1, Math.ceil(items.length / pageSize));
|
|
78
|
+
const currentPage = clampInteger(input.page, 0, pageCount - 1);
|
|
79
|
+
const pageStart = currentPage * pageSize;
|
|
80
|
+
return {
|
|
81
|
+
currentPage,
|
|
82
|
+
pageCount,
|
|
83
|
+
pageItems: items.slice(pageStart, pageStart + pageSize)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function resolveWorkbenchLaunchpadGrid(input) {
|
|
87
|
+
const columns = clampInteger(
|
|
88
|
+
Math.floor(input.width / launchpadTileWidth),
|
|
89
|
+
launchpadMinColumns,
|
|
90
|
+
launchpadMaxColumns
|
|
91
|
+
);
|
|
92
|
+
const rows = clampInteger(
|
|
93
|
+
Math.floor(input.height / launchpadTileHeight),
|
|
94
|
+
launchpadMinRows,
|
|
95
|
+
launchpadMaxRows
|
|
96
|
+
);
|
|
97
|
+
return {
|
|
98
|
+
columns,
|
|
99
|
+
pageSize: Math.max(1, columns * rows),
|
|
100
|
+
rows
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function resolveWorkbenchLaunchpadPreviewIconUrls(input) {
|
|
104
|
+
const icons = uniqueLaunchpadPreviewIconUrls([
|
|
105
|
+
...input.appIcons ?? [],
|
|
106
|
+
...input.nodeIconUrls ?? [],
|
|
107
|
+
...input.agentIcons ?? []
|
|
108
|
+
]).slice(0, launchpadPreviewIconCount);
|
|
109
|
+
while (icons.length < launchpadPreviewIconCount) {
|
|
110
|
+
icons.push(input.fallbackIconUrl);
|
|
111
|
+
}
|
|
112
|
+
return icons;
|
|
113
|
+
}
|
|
114
|
+
function uniqueLaunchpadPreviewIconUrls(iconUrls) {
|
|
115
|
+
const seen = /* @__PURE__ */ new Set();
|
|
116
|
+
const result = [];
|
|
117
|
+
for (const iconUrl of iconUrls) {
|
|
118
|
+
const normalizedIconUrl = iconUrl?.trim();
|
|
119
|
+
if (!normalizedIconUrl || seen.has(normalizedIconUrl)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
seen.add(normalizedIconUrl);
|
|
123
|
+
result.push(normalizedIconUrl);
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
function normalizeLaunchpadSearchText(value) {
|
|
128
|
+
return value.trim().toLocaleLowerCase();
|
|
129
|
+
}
|
|
130
|
+
function clampInteger(value, min, max) {
|
|
131
|
+
if (!Number.isFinite(value)) {
|
|
132
|
+
return min;
|
|
133
|
+
}
|
|
134
|
+
return Math.min(Math.max(Math.floor(value), min), max);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/launchpadWheelNavigation.ts
|
|
138
|
+
var wheelDeltaLinePx = 16;
|
|
139
|
+
var wheelDeltaPagePx = 360;
|
|
140
|
+
var launchpadWheelNavigationThresholdPx = 64;
|
|
141
|
+
var launchpadWheelNavigationCooldownMs = 420;
|
|
142
|
+
var launchpadWheelHorizontalIntentRatio = 1.2;
|
|
143
|
+
function createWorkbenchLaunchpadWheelNavigationState() {
|
|
144
|
+
return {
|
|
145
|
+
accumulatedDeltaX: 0,
|
|
146
|
+
lastNavigationAt: 0
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function resolveWorkbenchLaunchpadWheelNavigation(input) {
|
|
150
|
+
if (input.pageCount <= 1) {
|
|
151
|
+
return {
|
|
152
|
+
nextPageIndex: null,
|
|
153
|
+
shouldPreventDefault: false,
|
|
154
|
+
state: createWorkbenchLaunchpadWheelNavigationState()
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const delta = normalizeWorkbenchLaunchpadWheelDelta(input);
|
|
158
|
+
const absDeltaX = Math.abs(delta.x);
|
|
159
|
+
const absDeltaY = Math.abs(delta.y);
|
|
160
|
+
const isHorizontalIntent = absDeltaX > 0 && absDeltaX >= absDeltaY * launchpadWheelHorizontalIntentRatio;
|
|
161
|
+
if (!isHorizontalIntent) {
|
|
162
|
+
return {
|
|
163
|
+
nextPageIndex: null,
|
|
164
|
+
shouldPreventDefault: false,
|
|
165
|
+
state: {
|
|
166
|
+
accumulatedDeltaX: 0,
|
|
167
|
+
lastNavigationAt: input.state.lastNavigationAt
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const direction = delta.x > 0 ? 1 : -1;
|
|
172
|
+
const previousDirection = input.state.accumulatedDeltaX > 0 ? 1 : -1;
|
|
173
|
+
const accumulatedDeltaX = input.state.accumulatedDeltaX !== 0 && previousDirection === direction ? input.state.accumulatedDeltaX + delta.x : delta.x;
|
|
174
|
+
if (input.timestamp - input.state.lastNavigationAt < launchpadWheelNavigationCooldownMs) {
|
|
175
|
+
return {
|
|
176
|
+
nextPageIndex: null,
|
|
177
|
+
shouldPreventDefault: true,
|
|
178
|
+
state: {
|
|
179
|
+
accumulatedDeltaX: 0,
|
|
180
|
+
lastNavigationAt: input.state.lastNavigationAt
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (Math.abs(accumulatedDeltaX) < launchpadWheelNavigationThresholdPx) {
|
|
185
|
+
return {
|
|
186
|
+
nextPageIndex: null,
|
|
187
|
+
shouldPreventDefault: true,
|
|
188
|
+
state: {
|
|
189
|
+
accumulatedDeltaX,
|
|
190
|
+
lastNavigationAt: input.state.lastNavigationAt
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const nextPageIndex = input.currentPage + direction;
|
|
195
|
+
if (nextPageIndex < 0 || nextPageIndex >= input.pageCount) {
|
|
196
|
+
return {
|
|
197
|
+
nextPageIndex: null,
|
|
198
|
+
shouldPreventDefault: true,
|
|
199
|
+
state: {
|
|
200
|
+
accumulatedDeltaX: 0,
|
|
201
|
+
lastNavigationAt: input.timestamp
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
nextPageIndex,
|
|
207
|
+
shouldPreventDefault: true,
|
|
208
|
+
state: {
|
|
209
|
+
accumulatedDeltaX: 0,
|
|
210
|
+
lastNavigationAt: input.timestamp
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function normalizeWorkbenchLaunchpadWheelDelta(input) {
|
|
215
|
+
const unit = input.deltaMode === 1 ? wheelDeltaLinePx : input.deltaMode === 2 ? wheelDeltaPagePx : 1;
|
|
216
|
+
return {
|
|
217
|
+
x: input.deltaX * unit,
|
|
218
|
+
y: input.deltaY * unit
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/WorkbenchLaunchpadOverlay.tsx
|
|
223
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
224
|
+
var defaultLaunchpadGrid = {
|
|
225
|
+
columns: 5,
|
|
226
|
+
pageSize: 15,
|
|
227
|
+
rows: 3
|
|
228
|
+
};
|
|
229
|
+
var launchpadExitAnimationMs = 180;
|
|
230
|
+
var launchpadHoverPanelViewportInsetPx = 16;
|
|
231
|
+
function WorkbenchLaunchpadOverlay({
|
|
232
|
+
copy,
|
|
233
|
+
dockPlacement = "bottom",
|
|
234
|
+
getAgentActionLabel,
|
|
235
|
+
getAgentActionPending,
|
|
236
|
+
getAgentReason,
|
|
237
|
+
items,
|
|
238
|
+
onClose,
|
|
239
|
+
onLaunchItem,
|
|
240
|
+
onPageChange,
|
|
241
|
+
onRunAgentAction,
|
|
242
|
+
onSearch,
|
|
243
|
+
open
|
|
244
|
+
}) {
|
|
245
|
+
const [query, setQuery] = useState("");
|
|
246
|
+
const [pageIndex, setPageIndex] = useState(0);
|
|
247
|
+
const [shouldRender, setShouldRender] = useState(open);
|
|
248
|
+
const searchInputRef = useRef(null);
|
|
249
|
+
const wheelNavigationRef = useRef(
|
|
250
|
+
createWorkbenchLaunchpadWheelNavigationState()
|
|
251
|
+
);
|
|
252
|
+
const { grid, ref: gridViewportRef } = useLaunchpadGridMetrics(open);
|
|
253
|
+
const isClosing = shouldRender && !open;
|
|
254
|
+
const filteredItems = useMemo(
|
|
255
|
+
() => filterWorkbenchLaunchpadItems(items, query),
|
|
256
|
+
[items, query]
|
|
257
|
+
);
|
|
258
|
+
const page = useMemo(
|
|
259
|
+
() => paginateWorkbenchLaunchpadItems(filteredItems, {
|
|
260
|
+
page: pageIndex,
|
|
261
|
+
pageSize: grid.pageSize
|
|
262
|
+
}),
|
|
263
|
+
[filteredItems, grid.pageSize, pageIndex]
|
|
264
|
+
);
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
if (open) {
|
|
267
|
+
setShouldRender(true);
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
if (!shouldRender) {
|
|
271
|
+
return void 0;
|
|
272
|
+
}
|
|
273
|
+
const timeoutId = window.setTimeout(() => {
|
|
274
|
+
setShouldRender(false);
|
|
275
|
+
}, launchpadExitAnimationMs);
|
|
276
|
+
return () => {
|
|
277
|
+
window.clearTimeout(timeoutId);
|
|
278
|
+
};
|
|
279
|
+
}, [open, shouldRender]);
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
if (!open) {
|
|
282
|
+
wheelNavigationRef.current = createWorkbenchLaunchpadWheelNavigationState();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
setQuery("");
|
|
286
|
+
setPageIndex(0);
|
|
287
|
+
window.setTimeout(() => searchInputRef.current?.focus(), 0);
|
|
288
|
+
}, [open]);
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
setPageIndex(0);
|
|
291
|
+
wheelNavigationRef.current = createWorkbenchLaunchpadWheelNavigationState();
|
|
292
|
+
}, [query]);
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
if (!open || !onSearch) {
|
|
295
|
+
return void 0;
|
|
296
|
+
}
|
|
297
|
+
const queryLength = query.trim().length;
|
|
298
|
+
if (queryLength === 0) {
|
|
299
|
+
return void 0;
|
|
300
|
+
}
|
|
301
|
+
const timeoutId = window.setTimeout(() => {
|
|
302
|
+
onSearch({
|
|
303
|
+
queryLength,
|
|
304
|
+
resultCount: filteredItems.length
|
|
305
|
+
});
|
|
306
|
+
}, 300);
|
|
307
|
+
return () => {
|
|
308
|
+
window.clearTimeout(timeoutId);
|
|
309
|
+
};
|
|
310
|
+
}, [filteredItems.length, onSearch, open, query]);
|
|
311
|
+
const changePage = useCallback(
|
|
312
|
+
(nextPageIndex) => {
|
|
313
|
+
setPageIndex((currentPageIndex) => {
|
|
314
|
+
const clampedPageIndex = Math.max(
|
|
315
|
+
0,
|
|
316
|
+
Math.min(nextPageIndex, page.pageCount - 1)
|
|
317
|
+
);
|
|
318
|
+
if (clampedPageIndex !== currentPageIndex) {
|
|
319
|
+
onPageChange?.({
|
|
320
|
+
pageIndex: clampedPageIndex,
|
|
321
|
+
totalPages: page.pageCount
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
return clampedPageIndex;
|
|
325
|
+
});
|
|
326
|
+
},
|
|
327
|
+
[onPageChange, page.pageCount]
|
|
328
|
+
);
|
|
329
|
+
useEffect(() => {
|
|
330
|
+
if (!open) {
|
|
331
|
+
return void 0;
|
|
332
|
+
}
|
|
333
|
+
const handleKeyDown = (event) => {
|
|
334
|
+
if (event.key === "Escape") {
|
|
335
|
+
event.preventDefault();
|
|
336
|
+
onClose();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (isTextInputTarget(event.target)) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (event.key === "ArrowRight" && page.pageCount > 1) {
|
|
343
|
+
event.preventDefault();
|
|
344
|
+
changePage(page.currentPage + 1);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (event.key === "ArrowLeft" && page.pageCount > 1) {
|
|
348
|
+
event.preventDefault();
|
|
349
|
+
changePage(page.currentPage - 1);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
353
|
+
return () => {
|
|
354
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
355
|
+
};
|
|
356
|
+
}, [changePage, onClose, open, page.currentPage, page.pageCount]);
|
|
357
|
+
const handleOverlayClick = useCallback(
|
|
358
|
+
(event) => {
|
|
359
|
+
if (isClosing || isLaunchpadInteractiveTarget(event.target)) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
onClose();
|
|
363
|
+
},
|
|
364
|
+
[isClosing, onClose]
|
|
365
|
+
);
|
|
366
|
+
const handleOverlayWheel = useCallback(
|
|
367
|
+
(event) => {
|
|
368
|
+
if (!open || isClosing || isLaunchpadWheelIgnoredTarget(event.target)) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const result = resolveWorkbenchLaunchpadWheelNavigation({
|
|
372
|
+
currentPage: page.currentPage,
|
|
373
|
+
deltaMode: event.deltaMode,
|
|
374
|
+
deltaX: event.deltaX,
|
|
375
|
+
deltaY: event.deltaY,
|
|
376
|
+
pageCount: page.pageCount,
|
|
377
|
+
state: wheelNavigationRef.current,
|
|
378
|
+
timestamp: event.timeStamp
|
|
379
|
+
});
|
|
380
|
+
wheelNavigationRef.current = result.state;
|
|
381
|
+
if (result.shouldPreventDefault) {
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
}
|
|
384
|
+
if (result.nextPageIndex !== null) {
|
|
385
|
+
changePage(result.nextPageIndex);
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
[changePage, isClosing, open, page.currentPage, page.pageCount]
|
|
389
|
+
);
|
|
390
|
+
if (!shouldRender) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
return /* @__PURE__ */ jsxs(
|
|
394
|
+
"div",
|
|
395
|
+
{
|
|
396
|
+
"aria-label": copy.label,
|
|
397
|
+
"aria-modal": "false",
|
|
398
|
+
className: classNames(
|
|
399
|
+
"workspace-launchpad-overlay",
|
|
400
|
+
isClosing && "workspace-launchpad-overlay--closing"
|
|
401
|
+
),
|
|
402
|
+
role: "dialog",
|
|
403
|
+
onClick: handleOverlayClick,
|
|
404
|
+
onWheel: handleOverlayWheel,
|
|
405
|
+
children: [
|
|
406
|
+
/* @__PURE__ */ jsx(
|
|
407
|
+
"button",
|
|
408
|
+
{
|
|
409
|
+
"aria-hidden": "true",
|
|
410
|
+
className: "workspace-launchpad-overlay__dismiss",
|
|
411
|
+
tabIndex: -1,
|
|
412
|
+
type: "button",
|
|
413
|
+
onClick: onClose
|
|
414
|
+
}
|
|
415
|
+
),
|
|
416
|
+
/* @__PURE__ */ jsxs(
|
|
417
|
+
"div",
|
|
418
|
+
{
|
|
419
|
+
className: "workspace-launchpad-overlay__content",
|
|
420
|
+
"data-dock-placement": dockPlacement,
|
|
421
|
+
children: [
|
|
422
|
+
/* @__PURE__ */ jsx("div", { className: "workspace-launchpad-overlay__topbar", children: /* @__PURE__ */ jsxs("div", { className: "workspace-launchpad-search", children: [
|
|
423
|
+
/* @__PURE__ */ jsx(
|
|
424
|
+
"span",
|
|
425
|
+
{
|
|
426
|
+
"aria-hidden": "true",
|
|
427
|
+
className: "workspace-launchpad-search__icon",
|
|
428
|
+
children: /* @__PURE__ */ jsx(SearchIcon, {})
|
|
429
|
+
}
|
|
430
|
+
),
|
|
431
|
+
/* @__PURE__ */ jsx(
|
|
432
|
+
"input",
|
|
433
|
+
{
|
|
434
|
+
ref: searchInputRef,
|
|
435
|
+
"aria-label": copy.searchPlaceholder,
|
|
436
|
+
className: "workspace-launchpad-search__input",
|
|
437
|
+
placeholder: copy.searchPlaceholder,
|
|
438
|
+
type: "search",
|
|
439
|
+
value: query,
|
|
440
|
+
onChange: (event) => setQuery(event.target.value)
|
|
441
|
+
}
|
|
442
|
+
),
|
|
443
|
+
query ? /* @__PURE__ */ jsx(
|
|
444
|
+
"button",
|
|
445
|
+
{
|
|
446
|
+
"aria-label": copy.clearSearch,
|
|
447
|
+
className: "workspace-launchpad-search__clear",
|
|
448
|
+
type: "button",
|
|
449
|
+
onClick: (event) => {
|
|
450
|
+
event.stopPropagation();
|
|
451
|
+
setQuery("");
|
|
452
|
+
searchInputRef.current?.focus();
|
|
453
|
+
},
|
|
454
|
+
onMouseDown: (event) => {
|
|
455
|
+
event.preventDefault();
|
|
456
|
+
event.stopPropagation();
|
|
457
|
+
},
|
|
458
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
459
|
+
}
|
|
460
|
+
) : null
|
|
461
|
+
] }) }),
|
|
462
|
+
/* @__PURE__ */ jsx(
|
|
463
|
+
"div",
|
|
464
|
+
{
|
|
465
|
+
ref: gridViewportRef,
|
|
466
|
+
className: "workspace-launchpad-grid-viewport",
|
|
467
|
+
children: filteredItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "workspace-launchpad-empty", children: copy.empty }) : /* @__PURE__ */ jsx(
|
|
468
|
+
"div",
|
|
469
|
+
{
|
|
470
|
+
className: "workspace-launchpad-grid",
|
|
471
|
+
style: {
|
|
472
|
+
gridTemplateColumns: `repeat(${grid.columns}, minmax(96px, 136px))`
|
|
473
|
+
},
|
|
474
|
+
children: page.pageItems.map((item, index) => /* @__PURE__ */ jsx(
|
|
475
|
+
WorkbenchLaunchpadItemButton,
|
|
476
|
+
{
|
|
477
|
+
animationIndex: index,
|
|
478
|
+
animationRisePx: 18 + index % grid.columns * 3,
|
|
479
|
+
copy,
|
|
480
|
+
getAgentActionLabel,
|
|
481
|
+
getAgentReason,
|
|
482
|
+
isAgentActionPending: getAgentActionPending,
|
|
483
|
+
item,
|
|
484
|
+
onLaunch: onLaunchItem,
|
|
485
|
+
onRunAgentAction
|
|
486
|
+
},
|
|
487
|
+
item.id
|
|
488
|
+
))
|
|
489
|
+
}
|
|
490
|
+
)
|
|
491
|
+
}
|
|
492
|
+
),
|
|
493
|
+
page.pageCount > 1 ? /* @__PURE__ */ jsx(
|
|
494
|
+
"div",
|
|
495
|
+
{
|
|
496
|
+
"aria-label": copy.pages ?? "Pages",
|
|
497
|
+
className: "workspace-launchpad-pages",
|
|
498
|
+
children: Array.from({ length: page.pageCount }, (_, index) => /* @__PURE__ */ jsx(
|
|
499
|
+
"button",
|
|
500
|
+
{
|
|
501
|
+
"aria-label": copy.pageDot?.({
|
|
502
|
+
page: index + 1,
|
|
503
|
+
pageCount: page.pageCount
|
|
504
|
+
}) ?? `${index + 1} / ${page.pageCount}`,
|
|
505
|
+
className: "workspace-launchpad-page-dot",
|
|
506
|
+
"data-active": index === page.currentPage ? "true" : void 0,
|
|
507
|
+
type: "button",
|
|
508
|
+
onClick: () => changePage(index)
|
|
509
|
+
},
|
|
510
|
+
index
|
|
511
|
+
))
|
|
512
|
+
}
|
|
513
|
+
) : /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "workspace-launchpad-pages" })
|
|
514
|
+
]
|
|
515
|
+
}
|
|
516
|
+
)
|
|
517
|
+
]
|
|
518
|
+
}
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
function WorkbenchLaunchpadItemButton({
|
|
522
|
+
animationIndex,
|
|
523
|
+
animationRisePx,
|
|
524
|
+
copy,
|
|
525
|
+
getAgentActionLabel,
|
|
526
|
+
getAgentReason,
|
|
527
|
+
isAgentActionPending,
|
|
528
|
+
item,
|
|
529
|
+
onLaunch,
|
|
530
|
+
onRunAgentAction
|
|
531
|
+
}) {
|
|
532
|
+
const disabledLabel = item.disabledReason === void 0 ? null : copy.unavailableItem?.({
|
|
533
|
+
reason: item.disabledReason,
|
|
534
|
+
title: item.label
|
|
535
|
+
}) ?? item.disabledReason;
|
|
536
|
+
const [hoverPanelOpen, setHoverPanelOpen] = useState(false);
|
|
537
|
+
const [hoverPanelShift, setHoverPanelShift] = useState(0);
|
|
538
|
+
const hoverPanelRef = useRef(null);
|
|
539
|
+
const hoverTimerRef = useRef(null);
|
|
540
|
+
const agentActions = item.kind === "agent" && !item.launchEnabled ? resolveLaunchpadAgentActions(item) : [];
|
|
541
|
+
const agentReason = item.kind === "agent" ? getAgentReason?.(item) ?? item.reason ?? null : null;
|
|
542
|
+
const showComingSoonTooltip = item.kind === "agent" && item.comingSoon === true && agentReason !== null;
|
|
543
|
+
const showAgentHoverPanel = item.kind === "agent" && item.comingSoon !== true && !item.launchEnabled && hoverPanelOpen && (agentActions.length > 0 || agentReason !== null);
|
|
544
|
+
const clearHoverTimer = useCallback(() => {
|
|
545
|
+
if (hoverTimerRef.current === null) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
clearTimeout(hoverTimerRef.current);
|
|
549
|
+
hoverTimerRef.current = null;
|
|
550
|
+
}, []);
|
|
551
|
+
useEffect(
|
|
552
|
+
() => () => {
|
|
553
|
+
clearHoverTimer();
|
|
554
|
+
},
|
|
555
|
+
[clearHoverTimer]
|
|
556
|
+
);
|
|
557
|
+
useLayoutEffect(() => {
|
|
558
|
+
if (!showAgentHoverPanel || typeof window === "undefined") {
|
|
559
|
+
setHoverPanelShift(0);
|
|
560
|
+
return void 0;
|
|
561
|
+
}
|
|
562
|
+
const frameId = window.requestAnimationFrame(() => {
|
|
563
|
+
const panel = hoverPanelRef.current;
|
|
564
|
+
if (!panel) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const rect = panel.getBoundingClientRect();
|
|
568
|
+
const maxRight = window.innerWidth - launchpadHoverPanelViewportInsetPx;
|
|
569
|
+
const nextShift = rect.left < launchpadHoverPanelViewportInsetPx ? launchpadHoverPanelViewportInsetPx - rect.left : rect.right > maxRight ? maxRight - rect.right : 0;
|
|
570
|
+
setHoverPanelShift(
|
|
571
|
+
(current) => current === nextShift ? current : nextShift
|
|
572
|
+
);
|
|
573
|
+
});
|
|
574
|
+
return () => {
|
|
575
|
+
window.cancelAnimationFrame(frameId);
|
|
576
|
+
};
|
|
577
|
+
}, [agentActions.length, agentReason, showAgentHoverPanel]);
|
|
578
|
+
const handlePointerEnter = useCallback(() => {
|
|
579
|
+
if (item.kind !== "agent" || item.launchEnabled || item.comingSoon) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
clearHoverTimer();
|
|
583
|
+
hoverTimerRef.current = setTimeout(() => {
|
|
584
|
+
hoverTimerRef.current = null;
|
|
585
|
+
setHoverPanelOpen(true);
|
|
586
|
+
}, 500);
|
|
587
|
+
}, [clearHoverTimer, item]);
|
|
588
|
+
const handlePointerLeave = useCallback(() => {
|
|
589
|
+
clearHoverTimer();
|
|
590
|
+
setHoverPanelOpen(false);
|
|
591
|
+
}, [clearHoverTimer]);
|
|
592
|
+
const animationStyle = {
|
|
593
|
+
"--workspace-launchpad-item-delay": `${Math.min(animationIndex, 24) * 28}ms`,
|
|
594
|
+
"--workspace-launchpad-item-rise": `${animationRisePx}px`
|
|
595
|
+
};
|
|
596
|
+
const itemNode = /* @__PURE__ */ jsxs(
|
|
597
|
+
"div",
|
|
598
|
+
{
|
|
599
|
+
className: "workspace-launchpad-item-wrap",
|
|
600
|
+
style: animationStyle,
|
|
601
|
+
onPointerEnter: handlePointerEnter,
|
|
602
|
+
onPointerLeave: handlePointerLeave,
|
|
603
|
+
children: [
|
|
604
|
+
/* @__PURE__ */ jsxs(
|
|
605
|
+
"button",
|
|
606
|
+
{
|
|
607
|
+
"aria-label": disabledLabel ?? item.label,
|
|
608
|
+
className: classNames(
|
|
609
|
+
"workspace-launchpad-item",
|
|
610
|
+
!item.launchEnabled && "workspace-launchpad-item--disabled"
|
|
611
|
+
),
|
|
612
|
+
disabled: !item.launchEnabled,
|
|
613
|
+
title: showComingSoonTooltip ? void 0 : disabledLabel ?? item.label,
|
|
614
|
+
type: "button",
|
|
615
|
+
onClick: () => onLaunch(item),
|
|
616
|
+
children: [
|
|
617
|
+
/* @__PURE__ */ jsx(
|
|
618
|
+
"span",
|
|
619
|
+
{
|
|
620
|
+
"aria-hidden": "true",
|
|
621
|
+
className: "workspace-launchpad-item__icon",
|
|
622
|
+
"data-workspace-app-icon": item.iconUrl ? "true" : void 0,
|
|
623
|
+
children: item.iconUrl ? /* @__PURE__ */ jsx("img", { alt: "", draggable: false, src: item.iconUrl }) : /* @__PURE__ */ jsx(NavApplicationsFilledIcon, { className: "size-10" })
|
|
624
|
+
}
|
|
625
|
+
),
|
|
626
|
+
/* @__PURE__ */ jsx("span", { className: "workspace-launchpad-item__label", children: item.label })
|
|
627
|
+
]
|
|
628
|
+
}
|
|
629
|
+
),
|
|
630
|
+
showAgentHoverPanel ? /* @__PURE__ */ jsxs(
|
|
631
|
+
"div",
|
|
632
|
+
{
|
|
633
|
+
ref: hoverPanelRef,
|
|
634
|
+
className: "desktop-dock__hover-panel workspace-launchpad-dock-hover-panel",
|
|
635
|
+
role: "group",
|
|
636
|
+
style: {
|
|
637
|
+
"--workspace-launchpad-hover-panel-shift": `${Math.round(
|
|
638
|
+
hoverPanelShift
|
|
639
|
+
)}px`
|
|
640
|
+
},
|
|
641
|
+
children: [
|
|
642
|
+
/* @__PURE__ */ jsx("div", { className: "desktop-dock__hover-panel-title", children: item.label }),
|
|
643
|
+
agentReason ? /* @__PURE__ */ jsx("div", { className: "desktop-dock__hover-panel-description", children: agentReason }) : null,
|
|
644
|
+
agentActions.length ? /* @__PURE__ */ jsx("div", { className: "desktop-dock__hover-actions", children: agentActions.map((action) => {
|
|
645
|
+
const isPending = isAgentActionPending?.(item, action.id) ?? false;
|
|
646
|
+
return /* @__PURE__ */ jsx(
|
|
647
|
+
"button",
|
|
648
|
+
{
|
|
649
|
+
"aria-busy": isPending || void 0,
|
|
650
|
+
className: "desktop-dock__hover-action",
|
|
651
|
+
disabled: isPending,
|
|
652
|
+
type: "button",
|
|
653
|
+
onClick: (event) => {
|
|
654
|
+
event.preventDefault();
|
|
655
|
+
event.stopPropagation();
|
|
656
|
+
if (isPending) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
onRunAgentAction?.(item, action.id);
|
|
660
|
+
},
|
|
661
|
+
children: resolveAgentActionLabel(
|
|
662
|
+
isPending && action.id === "install" ? "installing" : action.id,
|
|
663
|
+
copy,
|
|
664
|
+
getAgentActionLabel
|
|
665
|
+
)
|
|
666
|
+
},
|
|
667
|
+
action.id
|
|
668
|
+
);
|
|
669
|
+
}) }) : null
|
|
670
|
+
]
|
|
671
|
+
}
|
|
672
|
+
) : null
|
|
673
|
+
]
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
return showComingSoonTooltip ? /* @__PURE__ */ jsx(
|
|
677
|
+
"span",
|
|
678
|
+
{
|
|
679
|
+
className: "workspace-launchpad-tooltip-host",
|
|
680
|
+
title: agentReason ?? void 0,
|
|
681
|
+
children: itemNode
|
|
682
|
+
}
|
|
683
|
+
) : itemNode;
|
|
684
|
+
}
|
|
685
|
+
function useLaunchpadGridMetrics(open) {
|
|
686
|
+
const ref = useRef(null);
|
|
687
|
+
const [grid, setGrid] = useState(defaultLaunchpadGrid);
|
|
688
|
+
useEffect(() => {
|
|
689
|
+
if (!open) {
|
|
690
|
+
return void 0;
|
|
691
|
+
}
|
|
692
|
+
const element = ref.current;
|
|
693
|
+
if (!element) {
|
|
694
|
+
return void 0;
|
|
695
|
+
}
|
|
696
|
+
const update = () => {
|
|
697
|
+
const rect = element.getBoundingClientRect();
|
|
698
|
+
setGrid(
|
|
699
|
+
resolveWorkbenchLaunchpadGrid({
|
|
700
|
+
height: rect.height,
|
|
701
|
+
width: rect.width
|
|
702
|
+
})
|
|
703
|
+
);
|
|
704
|
+
};
|
|
705
|
+
update();
|
|
706
|
+
const resizeObserver = new ResizeObserver(update);
|
|
707
|
+
resizeObserver.observe(element);
|
|
708
|
+
return () => {
|
|
709
|
+
resizeObserver.disconnect();
|
|
710
|
+
};
|
|
711
|
+
}, [open]);
|
|
712
|
+
return { grid, ref };
|
|
713
|
+
}
|
|
714
|
+
function resolveLaunchpadAgentActions(item) {
|
|
715
|
+
if (item.comingSoon) {
|
|
716
|
+
return [];
|
|
717
|
+
}
|
|
718
|
+
if (item.actions?.length) {
|
|
719
|
+
return item.actions;
|
|
720
|
+
}
|
|
721
|
+
if (item.action) {
|
|
722
|
+
return [{ id: item.action }];
|
|
723
|
+
}
|
|
724
|
+
return [];
|
|
725
|
+
}
|
|
726
|
+
function resolveAgentActionLabel(actionId, copy, getAgentActionLabel) {
|
|
727
|
+
if (getAgentActionLabel) {
|
|
728
|
+
return getAgentActionLabel(actionId);
|
|
729
|
+
}
|
|
730
|
+
switch (actionId) {
|
|
731
|
+
case "install":
|
|
732
|
+
return copy.installAction ?? actionId;
|
|
733
|
+
case "installing":
|
|
734
|
+
return copy.installingAction ?? copy.installAction ?? actionId;
|
|
735
|
+
case "sync":
|
|
736
|
+
return copy.syncAction ?? actionId;
|
|
737
|
+
default:
|
|
738
|
+
return copy.refreshAction ?? actionId;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function isTextInputTarget(target) {
|
|
742
|
+
return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
743
|
+
}
|
|
744
|
+
function isLaunchpadInteractiveTarget(target) {
|
|
745
|
+
return target instanceof HTMLElement && Boolean(
|
|
746
|
+
target.closest(
|
|
747
|
+
".workspace-launchpad-overlay__topbar, .workspace-launchpad-search, .workspace-launchpad-item, .desktop-dock__hover-panel, .workspace-launchpad-pages"
|
|
748
|
+
)
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
function isLaunchpadWheelIgnoredTarget(target) {
|
|
752
|
+
return isTextInputTarget(target) || target instanceof HTMLElement && Boolean(
|
|
753
|
+
target.closest(
|
|
754
|
+
".workspace-launchpad-overlay__topbar, .workspace-launchpad-search, .desktop-dock__hover-panel, .workspace-launchpad-pages"
|
|
755
|
+
)
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
function classNames(...values) {
|
|
759
|
+
return values.filter(Boolean).join(" ");
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// src/workbenchLaunchpadDockEntry.ts
|
|
763
|
+
import { createElement } from "react";
|
|
764
|
+
function createWorkbenchLaunchpadDockIcon(input) {
|
|
765
|
+
return createElement(
|
|
766
|
+
"span",
|
|
767
|
+
{
|
|
768
|
+
"aria-hidden": "true",
|
|
769
|
+
className: "workspace-launchpad-dock-icon"
|
|
770
|
+
},
|
|
771
|
+
input.tileIconUrls.map(
|
|
772
|
+
(src, index) => createElement(
|
|
773
|
+
"span",
|
|
774
|
+
{
|
|
775
|
+
className: "workspace-launchpad-dock-icon__tile",
|
|
776
|
+
key: `${src}:${index}`
|
|
777
|
+
},
|
|
778
|
+
createElement("img", {
|
|
779
|
+
alt: "",
|
|
780
|
+
draggable: false,
|
|
781
|
+
src
|
|
782
|
+
})
|
|
783
|
+
)
|
|
784
|
+
)
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
function createWorkbenchLaunchpadDockEntry(input) {
|
|
788
|
+
return {
|
|
789
|
+
clickActionId: workbenchLaunchpadDockActionId,
|
|
790
|
+
icon: createWorkbenchLaunchpadDockIcon({
|
|
791
|
+
tileIconUrls: input.tileIconUrls
|
|
792
|
+
}),
|
|
793
|
+
id: workbenchLaunchpadDockEntryId,
|
|
794
|
+
label: input.label,
|
|
795
|
+
launchBehavior: "enabled",
|
|
796
|
+
order: input.order ?? 0,
|
|
797
|
+
sectionId: "launchpad",
|
|
798
|
+
typeId: workbenchLaunchpadDockEntryId,
|
|
799
|
+
visibility: "always"
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
export {
|
|
803
|
+
WorkbenchLaunchpadOverlay,
|
|
804
|
+
buildWorkbenchLaunchpadItems,
|
|
805
|
+
createWorkbenchLaunchpadDockEntry,
|
|
806
|
+
createWorkbenchLaunchpadDockIcon,
|
|
807
|
+
createWorkbenchLaunchpadWheelNavigationState,
|
|
808
|
+
filterWorkbenchLaunchpadItems,
|
|
809
|
+
paginateWorkbenchLaunchpadItems,
|
|
810
|
+
resolveWorkbenchLaunchpadGrid,
|
|
811
|
+
resolveWorkbenchLaunchpadPreviewIconUrls,
|
|
812
|
+
resolveWorkbenchLaunchpadWheelNavigation,
|
|
813
|
+
workbenchLaunchpadDockActionId,
|
|
814
|
+
workbenchLaunchpadDockEntryId
|
|
815
|
+
};
|
|
816
|
+
//# sourceMappingURL=index.js.map
|