@tutti-os/workspace-app-center 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1619 @@
1
+ import {
2
+ resolveDefaultAppFactoryProvider,
3
+ resolveSelectedAppFactoryProvider,
4
+ sortMyAppsByCreatedDesc,
5
+ sortRecommendedApps,
6
+ sortRecommendedAppsForAllTab
7
+ } from "../chunk-LBSZLLZK.js";
8
+
9
+ // src/ui/AppCard.tsx
10
+ import {
11
+ Button,
12
+ ChatIcon,
13
+ DeleteIcon,
14
+ EditIcon,
15
+ DropdownMenu,
16
+ DropdownMenuContent,
17
+ DropdownMenuGroup,
18
+ DropdownMenuItem,
19
+ DropdownMenuTrigger,
20
+ FolderIcon,
21
+ MoreHorizontalIcon,
22
+ NavApplicationsFilledIcon,
23
+ UninstallIcon,
24
+ UploadIcon,
25
+ cn
26
+ } from "@tutti-os/ui-system";
27
+ import { jsx, jsxs } from "react/jsx-runtime";
28
+ function AppCard({
29
+ actions,
30
+ app,
31
+ className,
32
+ copy
33
+ }) {
34
+ const statusLabel = copy.t(app.statusLabelKey);
35
+ const primaryActionLabel = app.primaryAction === "install" ? copy.t("actions.installApp") : app.primaryAction === "retry" ? copy.t("actions.retryApp") : app.primaryAction === "update" ? copy.t("actions.updateApp") : copy.t("actions.openApp");
36
+ const canExecutePrimaryAction = app.primaryAction !== "none";
37
+ const canOpenFromCard = app.canOpen;
38
+ const canPublishFactoryUpdate = app.canPublishFactoryUpdate && !!app.factoryJobId;
39
+ const canOpenFactorySession = app.canOpenFactorySession && !!app.factoryAgentSessionId && !!app.factoryJobId;
40
+ const hasMoreActions = canPublishFactoryUpdate || canOpenFactorySession || app.canExport || app.canDelete || app.canReplaceIcon || app.canOpenFolder || app.canOpenPackageFolder || app.canUninstall;
41
+ const executePrimaryAction = () => {
42
+ if (app.primaryAction === "retry") {
43
+ void actions.retryApp?.(app.id);
44
+ return;
45
+ }
46
+ if (app.primaryAction === "install") {
47
+ void actions.installApp?.(app.id);
48
+ return;
49
+ }
50
+ if (app.primaryAction === "update") {
51
+ void actions.updateApp?.(app.id, "primary_action");
52
+ return;
53
+ }
54
+ if (app.primaryAction === "open") {
55
+ void actions.openApp?.(app.id);
56
+ }
57
+ };
58
+ const executeCardAction = () => {
59
+ if (canOpenFromCard) {
60
+ void actions.openApp?.(app.id);
61
+ }
62
+ };
63
+ return /* @__PURE__ */ jsxs(
64
+ "article",
65
+ {
66
+ "aria-disabled": canOpenFromCard ? void 0 : true,
67
+ className: cn(
68
+ "group flex h-full min-h-[168px] min-w-0 flex-col rounded-[12px] border border-[color:var(--line-2)] bg-[var(--background-fronted)] p-[12px] text-left text-[var(--text-primary)] transition-transform duration-200 ease-out will-change-transform hover:-translate-y-0.5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color-mix(in_srgb,var(--border-focus)_70%,transparent)] motion-reduce:transition-none motion-reduce:hover:translate-y-0",
69
+ canOpenFromCard ? "cursor-pointer" : "cursor-default",
70
+ className
71
+ ),
72
+ "data-app-center-app-id": app.id,
73
+ role: "listitem",
74
+ tabIndex: canOpenFromCard ? 0 : -1,
75
+ onClick: executeCardAction,
76
+ onKeyDown: (event) => {
77
+ if (!canOpenFromCard || event.key !== "Enter" && event.key !== " ") {
78
+ return;
79
+ }
80
+ event.preventDefault();
81
+ executeCardAction();
82
+ },
83
+ children: [
84
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
85
+ /* @__PURE__ */ jsx(
86
+ AppIcon,
87
+ {
88
+ app,
89
+ replaceIconLabel: copy.t("actions.replaceIcon"),
90
+ onReplaceIcon: () => {
91
+ void actions.replaceAppIcon?.(app.id);
92
+ }
93
+ }
94
+ ),
95
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
96
+ hasMoreActions ? /* @__PURE__ */ jsx(
97
+ AppCardMoreActions,
98
+ {
99
+ actions,
100
+ app,
101
+ canOpenFactorySession,
102
+ canPublishFactoryUpdate,
103
+ copy
104
+ }
105
+ ) : null,
106
+ /* @__PURE__ */ jsx(
107
+ Button,
108
+ {
109
+ className: cn(
110
+ "min-w-[56px]",
111
+ !canExecutePrimaryAction ? "cursor-default" : null,
112
+ canExecutePrimaryAction ? app.primaryAction === "retry" ? statusClassName(app.status) : "text-[var(--text-primary)]" : statusClassName(app.status)
113
+ ),
114
+ disabled: !canExecutePrimaryAction,
115
+ size: "default",
116
+ title: statusLabel,
117
+ type: "button",
118
+ variant: "ghost",
119
+ onClick: (event) => {
120
+ event.stopPropagation();
121
+ executePrimaryAction();
122
+ },
123
+ children: canExecutePrimaryAction ? primaryActionLabel : statusLabel
124
+ }
125
+ )
126
+ ] })
127
+ ] }),
128
+ /* @__PURE__ */ jsxs("div", { className: "mt-5 flex min-h-0 min-w-0 flex-1 flex-col p-1", children: [
129
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
130
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [
131
+ /* @__PURE__ */ jsx("h3", { className: "block min-w-0 truncate text-[16px] font-semibold leading-6 tracking-[0] text-[var(--text-primary)]", children: app.name }),
132
+ app.version ? /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-none text-[11px] leading-4 text-[var(--text-tertiary)] opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100", children: copy.t("labels.version", { version: app.version }) }) : null
133
+ ] }),
134
+ app.updateAvailable && app.availableVersion ? /* @__PURE__ */ jsx(
135
+ "button",
136
+ {
137
+ className: cn(
138
+ "mt-1 inline-flex max-w-full items-center rounded-[4px] bg-[color-mix(in_srgb,var(--state-warning)_12%,transparent)] px-2 py-0.5 text-[11px] leading-4 text-[var(--state-warning)] outline-none transition-colors hover:bg-[color-mix(in_srgb,var(--state-warning)_18%,transparent)] focus-visible:ring-2 focus-visible:ring-[color-mix(in_srgb,var(--state-warning)_42%,transparent)]",
139
+ app.canUpdate ? "cursor-pointer" : "cursor-default"
140
+ ),
141
+ disabled: !app.canUpdate,
142
+ title: copy.t("actions.updateApp"),
143
+ type: "button",
144
+ onClick: (event) => {
145
+ event.stopPropagation();
146
+ if (app.canUpdate) {
147
+ void actions.updateApp?.(app.id, "badge_button");
148
+ }
149
+ },
150
+ children: /* @__PURE__ */ jsx("span", { className: "truncate", children: copy.t("labels.updateAvailable", {
151
+ version: app.availableVersion
152
+ }) })
153
+ }
154
+ ) : null,
155
+ app.description ? /* @__PURE__ */ jsx("p", { className: "mt-2 line-clamp-3 text-[13px] font-normal leading-[1.3] text-[var(--text-secondary)]", children: app.description }) : null
156
+ ] }),
157
+ app.errorMessage || app.tags.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-auto flex min-w-0 flex-col gap-3 pt-3", children: [
158
+ app.errorMessage ? /* @__PURE__ */ jsx(
159
+ "p",
160
+ {
161
+ className: "min-w-0 rounded-[6px] bg-[color-mix(in_srgb,var(--state-danger)_10%,transparent)] px-2 py-1 text-[12px] leading-4 text-[var(--state-danger)]",
162
+ title: app.errorMessage,
163
+ children: copy.t("messages.appRuntimeFailed")
164
+ }
165
+ ) : null,
166
+ app.tags.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-wrap gap-1.5", children: app.tags.slice(0, 3).map((tag) => /* @__PURE__ */ jsx(
167
+ "span",
168
+ {
169
+ className: "inline-flex h-5 max-w-full items-center rounded-[4px] bg-[var(--transparency-block)] px-2 text-[11px] leading-4 text-[var(--text-secondary)]",
170
+ children: /* @__PURE__ */ jsx("span", { className: "truncate", children: tag })
171
+ },
172
+ tag
173
+ )) }) : null
174
+ ] }) : null
175
+ ] })
176
+ ]
177
+ }
178
+ );
179
+ }
180
+ function AppCardMoreActions({
181
+ actions,
182
+ app,
183
+ canOpenFactorySession,
184
+ canPublishFactoryUpdate,
185
+ copy
186
+ }) {
187
+ const menuItems = [];
188
+ if (canPublishFactoryUpdate) {
189
+ menuItems.push({
190
+ attention: true,
191
+ icon: /* @__PURE__ */ jsx(UploadIcon, {}),
192
+ key: "publish",
193
+ label: copy.t("actions.publishAppUpdate"),
194
+ onSelect: () => {
195
+ void actions.publishFactoryJob?.(app.factoryJobId ?? "");
196
+ }
197
+ });
198
+ }
199
+ if (canOpenFactorySession) {
200
+ menuItems.push({
201
+ icon: /* @__PURE__ */ jsx(ChatIcon, {}),
202
+ key: "modify-app-with-agent",
203
+ label: copy.t("actions.modifyAppWithAgent"),
204
+ onSelect: () => {
205
+ void actions.modifyAppWithAgent?.(
206
+ app.factoryJobId ?? "",
207
+ app.factoryAgentSessionId ?? "",
208
+ app.factoryProvider
209
+ );
210
+ }
211
+ });
212
+ }
213
+ if (app.canReplaceIcon) {
214
+ menuItems.push({
215
+ icon: /* @__PURE__ */ jsx(EditIcon, {}),
216
+ key: "replace-icon",
217
+ label: copy.t("actions.replaceIcon"),
218
+ onSelect: () => {
219
+ void actions.replaceAppIcon?.(app.id);
220
+ }
221
+ });
222
+ }
223
+ if (app.canOpenFolder) {
224
+ menuItems.push({
225
+ icon: /* @__PURE__ */ jsx(FolderIcon, {}),
226
+ key: "open-folder",
227
+ label: copy.t("actions.openAppFolder"),
228
+ onSelect: () => {
229
+ void actions.openAppFolder?.(app.id);
230
+ }
231
+ });
232
+ }
233
+ if (app.canOpenPackageFolder) {
234
+ menuItems.push({
235
+ icon: /* @__PURE__ */ jsx(FolderIcon, {}),
236
+ key: "open-package-folder",
237
+ label: copy.t("actions.openAppPackageFolder"),
238
+ onSelect: () => {
239
+ void actions.openAppPackageFolder?.(app.id);
240
+ }
241
+ });
242
+ }
243
+ if (app.canExport) {
244
+ menuItems.push({
245
+ icon: /* @__PURE__ */ jsx(UploadIcon, {}),
246
+ key: "export",
247
+ label: copy.t("actions.exportApp"),
248
+ onSelect: () => {
249
+ void actions.exportApp?.(app.id);
250
+ }
251
+ });
252
+ }
253
+ if (app.canUninstall) {
254
+ menuItems.push({
255
+ icon: /* @__PURE__ */ jsx(UninstallIcon, {}),
256
+ key: "uninstall",
257
+ label: copy.t("actions.uninstallApp"),
258
+ onSelect: () => {
259
+ void actions.uninstallApp?.(app.id);
260
+ },
261
+ variant: "destructive"
262
+ });
263
+ }
264
+ if (app.canDelete) {
265
+ menuItems.push({
266
+ icon: /* @__PURE__ */ jsx(DeleteIcon, {}),
267
+ key: "delete",
268
+ label: copy.t(
269
+ app.installed ? "actions.uninstallAndDeleteApp" : "actions.deleteApp"
270
+ ),
271
+ onSelect: () => {
272
+ void actions.deleteApp?.(app.id, app.name);
273
+ },
274
+ variant: "destructive"
275
+ });
276
+ }
277
+ return /* @__PURE__ */ jsx(
278
+ AppCenterActionMenu,
279
+ {
280
+ ariaLabel: copy.t("actions.moreActions"),
281
+ attention: canPublishFactoryUpdate,
282
+ items: menuItems,
283
+ triggerTitle: copy.t("actions.moreActions")
284
+ }
285
+ );
286
+ }
287
+ function AppCenterActionMenu({
288
+ ariaLabel,
289
+ attention = false,
290
+ items,
291
+ triggerTitle
292
+ }) {
293
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
294
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
295
+ Button,
296
+ {
297
+ "aria-label": ariaLabel,
298
+ className: "relative",
299
+ size: "icon",
300
+ title: triggerTitle,
301
+ type: "button",
302
+ variant: "ghost",
303
+ onClick: (event) => {
304
+ event.stopPropagation();
305
+ },
306
+ children: [
307
+ /* @__PURE__ */ jsx(MoreHorizontalIcon, {}),
308
+ attention ? /* @__PURE__ */ jsx(
309
+ "span",
310
+ {
311
+ "aria-hidden": "true",
312
+ className: "absolute right-1.5 top-1.5 size-1.5 rounded-full bg-[var(--state-danger)] shadow-[0_0_0_2px_var(--background-fronted)]"
313
+ }
314
+ ) : null
315
+ ]
316
+ }
317
+ ) }),
318
+ /* @__PURE__ */ jsx(
319
+ DropdownMenuContent,
320
+ {
321
+ align: "start",
322
+ className: "min-w-[192px]",
323
+ collisionPadding: 12,
324
+ side: "bottom",
325
+ style: { zIndex: "var(--z-panel-popover)" },
326
+ onClick: (event) => {
327
+ event.stopPropagation();
328
+ },
329
+ onPointerDown: (event) => {
330
+ event.stopPropagation();
331
+ },
332
+ onKeyDown: (event) => {
333
+ event.stopPropagation();
334
+ },
335
+ children: /* @__PURE__ */ jsx(DropdownMenuGroup, { className: "gap-[2px]", children: items.map((item) => /* @__PURE__ */ jsxs(
336
+ DropdownMenuItem,
337
+ {
338
+ className: "font-normal",
339
+ variant: item.variant,
340
+ onSelect: (event) => {
341
+ event.stopPropagation();
342
+ item.onSelect();
343
+ },
344
+ children: [
345
+ item.icon,
346
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
347
+ item.attention ? /* @__PURE__ */ jsx(
348
+ "span",
349
+ {
350
+ "aria-hidden": "true",
351
+ className: "ml-2 size-1.5 flex-none rounded-full bg-[var(--state-danger)]"
352
+ }
353
+ ) : null
354
+ ]
355
+ },
356
+ item.key
357
+ )) })
358
+ }
359
+ )
360
+ ] });
361
+ }
362
+ function AppIcon({
363
+ app,
364
+ onReplaceIcon,
365
+ replaceIconLabel
366
+ }) {
367
+ const icon = app.icon?.type === "asset" ? /* @__PURE__ */ jsx(
368
+ "img",
369
+ {
370
+ alt: "",
371
+ className: "size-11 flex-none rounded-[10px] object-contain object-center select-none",
372
+ draggable: false,
373
+ src: app.icon.src
374
+ }
375
+ ) : /* @__PURE__ */ jsx("span", { className: "flex size-11 flex-none items-center justify-center rounded-[10px] bg-[var(--transparency-block)] text-[var(--text-secondary)]", children: /* @__PURE__ */ jsx(NavApplicationsFilledIcon, { className: "size-[22px]" }) });
376
+ return /* @__PURE__ */ jsxs("span", { className: "group/app-icon relative block size-11 flex-none", children: [
377
+ icon,
378
+ app.canReplaceIcon ? /* @__PURE__ */ jsx(
379
+ "button",
380
+ {
381
+ "aria-label": replaceIconLabel,
382
+ className: "absolute inset-0 flex size-11 items-center justify-center rounded-[10px] bg-black/55 p-0 text-white opacity-0 transition-opacity duration-150 group-hover/app-icon:opacity-100 focus-visible:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color-mix(in_srgb,var(--border-focus)_70%,transparent)]",
383
+ title: replaceIconLabel,
384
+ type: "button",
385
+ onClick: (event) => {
386
+ event.stopPropagation();
387
+ event.currentTarget.blur();
388
+ onReplaceIcon();
389
+ },
390
+ onPointerDown: (event) => {
391
+ event.stopPropagation();
392
+ },
393
+ children: /* @__PURE__ */ jsx(EditIcon, { className: "size-3.5" })
394
+ }
395
+ ) : null
396
+ ] });
397
+ }
398
+ function statusClassName(status) {
399
+ if (status === "failed") {
400
+ return "text-[var(--state-danger)]";
401
+ }
402
+ if (status === "running") {
403
+ return "text-[var(--text-primary)]";
404
+ }
405
+ return "text-[var(--text-secondary)]";
406
+ }
407
+
408
+ // src/ui/AppCenterPanel.tsx
409
+ import { useEffect, useId, useMemo, useState } from "react";
410
+ import {
411
+ Badge,
412
+ BareIconButton,
413
+ Button as Button2,
414
+ CloseIcon,
415
+ ConfirmationDialog,
416
+ DeleteIcon as DeleteIcon2,
417
+ Dialog,
418
+ DialogContent,
419
+ DialogFooter,
420
+ DialogHeader,
421
+ DialogTitle,
422
+ FileCreateIcon,
423
+ Input,
424
+ OpenSessionsIcon as OpenSessionsFilledIcon,
425
+ Select,
426
+ SelectContent,
427
+ SelectItem,
428
+ SelectSplitColumn,
429
+ SelectSplitColumnItems,
430
+ SelectSplitColumnLabel,
431
+ SelectSplitDivider,
432
+ SelectSplitLayout,
433
+ SelectTrigger,
434
+ SelectValue,
435
+ SectionTabs,
436
+ Spinner,
437
+ Textarea,
438
+ Tooltip,
439
+ TooltipContent,
440
+ TooltipTrigger,
441
+ ToastProvider,
442
+ ToastRoot,
443
+ ToastTitle,
444
+ UploadIcon as ImportIcon,
445
+ cn as cn2
446
+ } from "@tutti-os/ui-system";
447
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
448
+ var factoryTemplates = [
449
+ {
450
+ defaultNameKey: "factory.templates.weather.defaultName",
451
+ id: "weather",
452
+ promptKey: "factory.templates.weather.prompt",
453
+ titleKey: "factory.templates.weather.title"
454
+ },
455
+ {
456
+ defaultNameKey: "factory.templates.lookup.defaultName",
457
+ id: "lookup",
458
+ promptKey: "factory.templates.lookup.prompt",
459
+ titleKey: "factory.templates.lookup.title"
460
+ },
461
+ {
462
+ defaultNameKey: "factory.templates.system.defaultName",
463
+ id: "system",
464
+ promptKey: "factory.templates.system.prompt",
465
+ titleKey: "factory.templates.system.title"
466
+ },
467
+ {
468
+ defaultNameKey: "factory.templates.news.defaultName",
469
+ id: "news",
470
+ promptKey: "factory.templates.news.prompt",
471
+ titleKey: "factory.templates.news.title"
472
+ },
473
+ {
474
+ defaultNameKey: "factory.templates.gomoku.defaultName",
475
+ id: "gomoku",
476
+ promptKey: "factory.templates.gomoku.prompt",
477
+ titleKey: "factory.templates.gomoku.title"
478
+ }
479
+ ];
480
+ var recommendedCategoryTabDefinitions = [
481
+ { id: "all", labelKey: null },
482
+ { id: "tools", labelKey: "categories.tools" },
483
+ { id: "content-creation", labelKey: "categories.contentCreation" },
484
+ { id: "product-design", labelKey: "categories.productDesign" },
485
+ { id: "office", labelKey: "categories.office" }
486
+ ];
487
+ function AppCenterPanel({
488
+ actions,
489
+ activeAppTab: controlledActiveAppTab,
490
+ catalogStatus,
491
+ className,
492
+ copy,
493
+ defaultAgentProvider = null,
494
+ errorMessage,
495
+ loadProviderConfiguration,
496
+ onActiveAppTabChange,
497
+ providerErrorMessage = null,
498
+ providerLoading = false,
499
+ providerOptions = [],
500
+ viewModel
501
+ }) {
502
+ const promptTextareaId = useId();
503
+ const [factoryDialogOpen, setFactoryDialogOpen] = useState(false);
504
+ const [deleteAppBusy, setDeleteAppBusy] = useState(false);
505
+ const [pendingDeleteApp, setPendingDeleteApp] = useState(null);
506
+ const [uninstallAppBusy, setUninstallAppBusy] = useState(false);
507
+ const [pendingUninstallApp, setPendingUninstallApp] = useState(null);
508
+ const [displayName, setDisplayName] = useState("");
509
+ const [prompt, setPrompt] = useState("");
510
+ const [providerConfiguration, setProviderConfiguration] = useState(null);
511
+ const [providerConfigurationStatus, setProviderConfigurationStatus] = useState("idle");
512
+ const [openFactorySettingsMenu, setOpenFactorySettingsMenu] = useState(null);
513
+ const [selectedModel, setSelectedModel] = useState("");
514
+ const [selectedPermissionModeId, setSelectedPermissionModeId] = useState("");
515
+ const [selectedReasoningEffort, setSelectedReasoningEffort] = useState("");
516
+ const normalizedProviderOptions = useMemo(
517
+ () => providerOptions.map((option) => {
518
+ const provider = option.provider.trim();
519
+ const label = option.label.trim() || provider;
520
+ if (!provider || !label) {
521
+ return null;
522
+ }
523
+ return {
524
+ ...option,
525
+ ...option.disabledReason?.trim() ? { disabledReason: option.disabledReason.trim() } : {},
526
+ ...option.iconUrl?.trim() ? { iconUrl: option.iconUrl.trim() } : {},
527
+ label,
528
+ provider
529
+ };
530
+ }).filter((option) => option != null),
531
+ [providerOptions]
532
+ );
533
+ const [selectedProvider, setSelectedProvider] = useState(
534
+ () => resolveDefaultAppFactoryProvider(
535
+ normalizedProviderOptions,
536
+ defaultAgentProvider
537
+ )
538
+ );
539
+ const [uncontrolledActiveAppTab, setUncontrolledActiveAppTab] = useState("recommended");
540
+ const [activeRecommendedCategoryTab, setActiveRecommendedCategoryTab] = useState("all");
541
+ const activeAppTab = controlledActiveAppTab ?? uncontrolledActiveAppTab;
542
+ useEffect(() => {
543
+ setSelectedProvider(
544
+ (current) => resolveSelectedAppFactoryProvider(
545
+ current,
546
+ normalizedProviderOptions,
547
+ defaultAgentProvider
548
+ )
549
+ );
550
+ }, [defaultAgentProvider, normalizedProviderOptions]);
551
+ const selectedProviderOption = normalizedProviderOptions.find(
552
+ (option) => option.provider === selectedProvider
553
+ ) ?? null;
554
+ useEffect(() => {
555
+ if (!factoryDialogOpen) {
556
+ setProviderConfiguration(null);
557
+ setProviderConfigurationStatus("idle");
558
+ return;
559
+ }
560
+ const provider = selectedProviderOption?.provider?.trim() ?? "";
561
+ if (!provider || !loadProviderConfiguration) {
562
+ setProviderConfiguration(null);
563
+ setProviderConfigurationStatus("ready");
564
+ return;
565
+ }
566
+ let canceled = false;
567
+ setProviderConfiguration(null);
568
+ setProviderConfigurationStatus("loading");
569
+ void loadProviderConfiguration(provider).then((configuration) => {
570
+ if (canceled) {
571
+ return;
572
+ }
573
+ setProviderConfiguration(configuration);
574
+ setProviderConfigurationStatus("ready");
575
+ }).catch(() => {
576
+ if (canceled) {
577
+ return;
578
+ }
579
+ setProviderConfiguration(null);
580
+ setProviderConfigurationStatus("ready");
581
+ });
582
+ return () => {
583
+ canceled = true;
584
+ };
585
+ }, [
586
+ factoryDialogOpen,
587
+ loadProviderConfiguration,
588
+ selectedProvider,
589
+ selectedProviderOption?.provider
590
+ ]);
591
+ const modelOptions = providerConfiguration?.modelOptions ?? [];
592
+ const permissionModeOptions = providerConfiguration?.permissionModeOptions ?? [];
593
+ const reasoningEffortOptions = providerConfiguration?.reasoningEffortOptions ?? [];
594
+ useEffect(() => {
595
+ setSelectedModel(
596
+ (current) => resolveSelectedFactoryOptionValue(
597
+ current,
598
+ modelOptions,
599
+ providerConfiguration?.defaultModel
600
+ )
601
+ );
602
+ }, [modelOptions, providerConfiguration?.defaultModel]);
603
+ useEffect(() => {
604
+ setSelectedPermissionModeId(
605
+ (current) => resolveSelectedFactoryOptionValue(
606
+ current,
607
+ permissionModeOptions,
608
+ providerConfiguration?.defaultPermissionModeId
609
+ )
610
+ );
611
+ }, [permissionModeOptions, providerConfiguration?.defaultPermissionModeId]);
612
+ useEffect(() => {
613
+ setSelectedReasoningEffort(
614
+ (current) => resolveSelectedFactoryOptionValue(
615
+ current,
616
+ reasoningEffortOptions,
617
+ providerConfiguration?.defaultReasoningEffort
618
+ )
619
+ );
620
+ }, [providerConfiguration?.defaultReasoningEffort, reasoningEffortOptions]);
621
+ const setActiveAppTab = (tab) => {
622
+ if (controlledActiveAppTab === void 0) {
623
+ setUncontrolledActiveAppTab(tab);
624
+ }
625
+ onActiveAppTabChange?.(tab);
626
+ };
627
+ const factoryJobs = viewModel.factoryJobs ?? [];
628
+ const hasFactoryJobs = factoryJobs.length > 0;
629
+ const closeFactoryDialog = () => {
630
+ setFactoryDialogOpen(false);
631
+ setDisplayName("");
632
+ setPrompt("");
633
+ setProviderConfiguration(null);
634
+ setProviderConfigurationStatus("idle");
635
+ setOpenFactorySettingsMenu(null);
636
+ setSelectedModel("");
637
+ setSelectedPermissionModeId("");
638
+ setSelectedReasoningEffort("");
639
+ };
640
+ const openFactoryDialog = () => {
641
+ setSelectedProvider(
642
+ resolveDefaultAppFactoryProvider(
643
+ normalizedProviderOptions,
644
+ defaultAgentProvider
645
+ )
646
+ );
647
+ setSelectedModel("");
648
+ setSelectedPermissionModeId("");
649
+ setSelectedReasoningEffort("");
650
+ setProviderConfiguration(null);
651
+ setProviderConfigurationStatus("idle");
652
+ setOpenFactorySettingsMenu(null);
653
+ setFactoryDialogOpen(true);
654
+ };
655
+ const canCreateFactoryJob = !!displayName.trim() && !!prompt.trim() && providerConfigurationStatus !== "loading" && !!selectedProviderOption && selectedProviderOption.disabled !== true;
656
+ const submitCreate = (event) => {
657
+ event.preventDefault();
658
+ const normalizedDisplayName = displayName.trim();
659
+ const normalizedPrompt = prompt.trim();
660
+ if (!normalizedDisplayName || !normalizedPrompt || !selectedProviderOption || selectedProviderOption.disabled === true) {
661
+ return;
662
+ }
663
+ setDisplayName("");
664
+ setPrompt("");
665
+ setFactoryDialogOpen(false);
666
+ void actions.createFactoryJob?.({
667
+ displayName: normalizedDisplayName,
668
+ ...selectedModel.trim() ? { model: selectedModel.trim() } : {},
669
+ ...selectedPermissionModeId.trim() ? { permissionModeId: selectedPermissionModeId.trim() } : {},
670
+ provider: selectedProviderOption.provider,
671
+ prompt: normalizedPrompt,
672
+ ...selectedReasoningEffort.trim() ? { reasoningEffort: selectedReasoningEffort.trim() } : {}
673
+ });
674
+ };
675
+ const selectTemplate = (template) => {
676
+ setDisplayName(copy.t(template.defaultNameKey));
677
+ setPrompt(copy.t(template.promptKey));
678
+ };
679
+ const openFactoryJobAgentSession = (job) => {
680
+ const agentSessionId = job.agentSessionId?.trim();
681
+ if (!agentSessionId) {
682
+ return;
683
+ }
684
+ void actions.openFactoryJobAgentSession?.(agentSessionId, job.provider);
685
+ };
686
+ const cardActions = {
687
+ ...actions,
688
+ deleteApp: (appId, appName) => {
689
+ setPendingDeleteApp({
690
+ id: appId,
691
+ installed: viewModel.apps.find((app) => app.id === appId)?.installed ?? false,
692
+ name: appName
693
+ });
694
+ },
695
+ uninstallApp: (appId) => {
696
+ const app = viewModel.apps.find((item) => item.id === appId);
697
+ if (!app) {
698
+ return;
699
+ }
700
+ setPendingUninstallApp({
701
+ id: app.id,
702
+ name: app.name,
703
+ sourceKind: app.sourceKind
704
+ });
705
+ }
706
+ };
707
+ const loadingMessage = catalogStatus === "loading" ? copy.t("messages.catalogLoading") : null;
708
+ const failedMessage = catalogStatus === "failed" ? copy.t("messages.catalogFailed") : null;
709
+ const statusToast = errorMessage != null ? {
710
+ busy: false,
711
+ message: errorMessage,
712
+ tone: "destructive"
713
+ } : failedMessage != null ? {
714
+ busy: false,
715
+ message: failedMessage,
716
+ tone: "destructive"
717
+ } : loadingMessage != null ? {
718
+ busy: true,
719
+ message: loadingMessage,
720
+ tone: "default"
721
+ } : null;
722
+ const myApps = sortMyAppsByCreatedDesc(
723
+ viewModel.apps.filter((app) => app.sourceKind === "local")
724
+ );
725
+ const recommendedSourceApps = viewModel.apps.filter(
726
+ (app) => app.sourceKind !== "local"
727
+ );
728
+ const recommendedApps = sortRecommendedApps(recommendedSourceApps);
729
+ const recommendedAppsForAllTab = sortRecommendedAppsForAllTab(
730
+ recommendedSourceApps
731
+ );
732
+ const recommendedCategoryTabs = createRecommendedCategoryTabs(
733
+ recommendedApps,
734
+ copy
735
+ );
736
+ const activeRecommendedCategoryLabel = recommendedCategoryTabs.find(
737
+ (tab) => tab.id === activeRecommendedCategoryTab
738
+ )?.category ?? null;
739
+ const activeRecommendedApps = activeRecommendedCategoryLabel == null ? recommendedAppsForAllTab : recommendedApps.filter(
740
+ (app) => app.category === activeRecommendedCategoryLabel
741
+ );
742
+ const activeApps = activeAppTab === "recommended" ? activeRecommendedApps : myApps;
743
+ const activeAppTabTitle = activeAppTab === "recommended" ? copy.t("labels.recommendedApps") : copy.t("labels.myApps");
744
+ const activeAppEmptyMessage = activeAppTab === "recommended" ? copy.t("messages.recommendedAppsEmpty") : copy.t("messages.myAppsEmpty");
745
+ const pendingDeleteAppInstalled = pendingDeleteApp?.installed ?? false;
746
+ const deleteAppConfirmLabel = copy.t(
747
+ pendingDeleteAppInstalled ? "actions.uninstallAndDeleteApp" : "actions.deleteApp"
748
+ );
749
+ const deleteAppConfirmDescription = copy.t(
750
+ pendingDeleteAppInstalled ? "confirmations.uninstallAndDeleteAppDescription" : "confirmations.deleteAppDescription"
751
+ );
752
+ const deleteAppConfirmTitle = copy.t(
753
+ pendingDeleteAppInstalled ? "confirmations.uninstallAndDeleteAppTitle" : "confirmations.deleteAppTitle",
754
+ {
755
+ name: pendingDeleteApp?.name ?? ""
756
+ }
757
+ );
758
+ const pendingUninstallAppLocal = pendingUninstallApp?.sourceKind === "local";
759
+ const uninstallAppConfirmDescription = copy.t(
760
+ pendingUninstallAppLocal ? "confirmations.uninstallAppDescriptionLocal" : "confirmations.uninstallAppDescriptionRecommended"
761
+ );
762
+ const uninstallAppConfirmTitle = copy.t("confirmations.uninstallAppTitle", {
763
+ name: pendingUninstallApp?.name ?? ""
764
+ });
765
+ return /* @__PURE__ */ jsxs2(
766
+ "section",
767
+ {
768
+ "aria-label": copy.t("title"),
769
+ className: cn2(
770
+ "relative flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden bg-[var(--background-panel)] text-[var(--text-primary)]",
771
+ className
772
+ ),
773
+ children: [
774
+ statusToast ? /* @__PURE__ */ jsx2(AppCenterStatusToast, { toast: statusToast }) : null,
775
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 flex-1 flex-col gap-5 overflow-auto px-6 pb-6 pt-5 [container-type:inline-size]", children: [
776
+ hasFactoryJobs ? /* @__PURE__ */ jsxs2("section", { className: "min-w-0", children: [
777
+ /* @__PURE__ */ jsx2("h2", { className: "mb-3 text-[15px] font-semibold leading-5 tracking-[0] text-[var(--text-primary)]", children: copy.t("factory.labels.jobs") }),
778
+ /* @__PURE__ */ jsx2("div", { className: "flex min-w-0 flex-col gap-2", children: factoryJobs.map((job) => /* @__PURE__ */ jsxs2(
779
+ "article",
780
+ {
781
+ "aria-disabled": job.canOpenAgentSession ? void 0 : true,
782
+ className: cn2(
783
+ "group flex min-w-0 items-center justify-between gap-3 rounded-[8px] border border-[color:var(--line-2)] bg-[var(--background-fronted)] p-[12px] text-left transition-colors duration-150 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--border-focus)]",
784
+ job.canOpenAgentSession ? "cursor-pointer hover:bg-[var(--transparency-block)]" : "cursor-default"
785
+ ),
786
+ role: "button",
787
+ tabIndex: job.canOpenAgentSession ? 0 : -1,
788
+ onClick: () => openFactoryJobAgentSession(job),
789
+ onKeyDown: (event) => {
790
+ if (!job.canOpenAgentSession || event.currentTarget !== event.target || event.key !== "Enter" && event.key !== " ") {
791
+ return;
792
+ }
793
+ event.preventDefault();
794
+ openFactoryJobAgentSession(job);
795
+ },
796
+ children: [
797
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
798
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 items-center gap-2", children: [
799
+ /* @__PURE__ */ jsx2("h3", { className: "truncate text-[13px] font-semibold leading-5 text-[var(--text-primary)]", children: job.title }),
800
+ /* @__PURE__ */ jsx2(FactoryJobStatusIndicator, { copy, job })
801
+ ] }),
802
+ job.failureReason ? /* @__PURE__ */ jsx2(
803
+ "p",
804
+ {
805
+ className: "mt-2 truncate text-[12px] leading-4 text-[var(--state-danger)]",
806
+ title: job.failureReason,
807
+ children: copy.t("factory.messages.factoryJobFailed")
808
+ }
809
+ ) : /* @__PURE__ */ jsx2("p", { className: "mt-2 truncate text-[12px] leading-4 text-[var(--text-secondary)]", children: job.prompt })
810
+ ] }),
811
+ /* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-1", children: [
812
+ job.canRetryValidation ? /* @__PURE__ */ jsx2(
813
+ Button2,
814
+ {
815
+ size: "sm",
816
+ type: "button",
817
+ variant: "ghost",
818
+ onClick: (event) => {
819
+ event.stopPropagation();
820
+ void actions.retryFactoryValidation?.(job.id);
821
+ },
822
+ children: copy.t("factory.actions.validate")
823
+ }
824
+ ) : null,
825
+ job.canPublish ? /* @__PURE__ */ jsx2(
826
+ Button2,
827
+ {
828
+ size: "sm",
829
+ type: "button",
830
+ variant: "ghost",
831
+ onClick: (event) => {
832
+ event.stopPropagation();
833
+ void actions.publishFactoryJob?.(job.id);
834
+ },
835
+ children: copy.t("factory.actions.publish")
836
+ }
837
+ ) : null,
838
+ job.canFix ? /* @__PURE__ */ jsx2(
839
+ Button2,
840
+ {
841
+ size: "sm",
842
+ type: "button",
843
+ variant: "ghost",
844
+ onClick: (event) => {
845
+ event.stopPropagation();
846
+ void actions.fixFactoryJob?.(
847
+ job.id,
848
+ copy.t("factory.prompts.fixDefault")
849
+ );
850
+ },
851
+ children: copy.t("factory.actions.fix")
852
+ }
853
+ ) : null,
854
+ job.canCancel ? /* @__PURE__ */ jsx2(
855
+ Button2,
856
+ {
857
+ "aria-label": copy.t("factory.actions.cancel"),
858
+ className: "opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100",
859
+ size: "icon-sm",
860
+ title: copy.t("factory.actions.cancel"),
861
+ type: "button",
862
+ variant: "ghost",
863
+ onClick: (event) => {
864
+ event.stopPropagation();
865
+ void actions.cancelFactoryJob?.(job.id);
866
+ },
867
+ children: /* @__PURE__ */ jsx2(CloseIcon, {})
868
+ }
869
+ ) : null,
870
+ job.canDelete ? /* @__PURE__ */ jsx2(
871
+ BareIconButton,
872
+ {
873
+ "aria-label": copy.t("factory.actions.delete"),
874
+ className: "text-[var(--text-secondary)]",
875
+ size: "md",
876
+ title: copy.t("factory.actions.delete"),
877
+ type: "button",
878
+ onClick: (event) => {
879
+ event.stopPropagation();
880
+ void actions.deleteFactoryJob?.(job.id);
881
+ },
882
+ children: /* @__PURE__ */ jsx2(DeleteIcon2, {})
883
+ }
884
+ ) : null
885
+ ] })
886
+ ]
887
+ },
888
+ job.id
889
+ )) })
890
+ ] }) : null,
891
+ /* @__PURE__ */ jsxs2("section", { className: "flex min-w-0 flex-col gap-3", children: [
892
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 items-center justify-between gap-3", children: [
893
+ /* @__PURE__ */ jsx2(
894
+ SectionTabs,
895
+ {
896
+ ariaLabel: copy.t("labels.appList"),
897
+ tabs: [
898
+ {
899
+ label: copy.t("labels.recommendedApps"),
900
+ value: "recommended"
901
+ },
902
+ {
903
+ label: copy.t("labels.myApps"),
904
+ value: "my"
905
+ }
906
+ ],
907
+ value: activeAppTab,
908
+ onValueChange: setActiveAppTab
909
+ }
910
+ ),
911
+ /* @__PURE__ */ jsx2("div", { className: "flex shrink-0 items-center gap-1", children: activeAppTab === "my" ? /* @__PURE__ */ jsx2(
912
+ AppCenterHeaderActions,
913
+ {
914
+ copy,
915
+ onCreateApp: () => {
916
+ openFactoryDialog();
917
+ },
918
+ onImportApp: () => {
919
+ void actions.importApp?.();
920
+ }
921
+ }
922
+ ) : /* @__PURE__ */ jsx2("div", { "aria-hidden": "true", className: "h-8 w-[172px]" }) })
923
+ ] }),
924
+ viewModel.empty ? /* @__PURE__ */ jsx2("p", { className: "text-[13px] leading-5 text-[var(--text-secondary)]", children: copy.t("messages.empty") }) : null,
925
+ activeAppTab === "recommended" ? /* @__PURE__ */ jsx2(
926
+ RecommendedCategoryTabs,
927
+ {
928
+ copy,
929
+ tabs: recommendedCategoryTabs,
930
+ value: activeRecommendedCategoryTab,
931
+ onValueChange: setActiveRecommendedCategoryTab
932
+ }
933
+ ) : null,
934
+ /* @__PURE__ */ jsx2(
935
+ AppCardGrid,
936
+ {
937
+ actions: cardActions,
938
+ apps: activeApps,
939
+ copy,
940
+ emptyMessage: activeAppEmptyMessage,
941
+ title: activeAppTabTitle
942
+ }
943
+ )
944
+ ] })
945
+ ] }),
946
+ /* @__PURE__ */ jsx2(
947
+ ConfirmationDialog,
948
+ {
949
+ cancelLabel: copy.t("actions.cancel"),
950
+ confirmBusy: deleteAppBusy,
951
+ confirmLabel: deleteAppConfirmLabel,
952
+ description: deleteAppConfirmDescription,
953
+ open: pendingDeleteApp != null,
954
+ title: deleteAppConfirmTitle,
955
+ tone: "destructive",
956
+ onConfirm: () => {
957
+ const app = pendingDeleteApp;
958
+ if (!app) {
959
+ return;
960
+ }
961
+ setDeleteAppBusy(true);
962
+ void Promise.resolve(actions.deleteApp?.(app.id, app.name)).finally(
963
+ () => {
964
+ setDeleteAppBusy(false);
965
+ setPendingDeleteApp(null);
966
+ }
967
+ );
968
+ },
969
+ onOpenChange: (nextOpen) => {
970
+ if (!nextOpen && !deleteAppBusy) {
971
+ setPendingDeleteApp(null);
972
+ }
973
+ }
974
+ }
975
+ ),
976
+ /* @__PURE__ */ jsx2(
977
+ ConfirmationDialog,
978
+ {
979
+ cancelLabel: copy.t("actions.cancel"),
980
+ confirmBusy: uninstallAppBusy,
981
+ confirmLabel: copy.t("actions.uninstallApp"),
982
+ description: uninstallAppConfirmDescription,
983
+ open: pendingUninstallApp != null,
984
+ title: uninstallAppConfirmTitle,
985
+ tone: "destructive",
986
+ onConfirm: () => {
987
+ const app = pendingUninstallApp;
988
+ if (!app) {
989
+ return;
990
+ }
991
+ setUninstallAppBusy(true);
992
+ void Promise.resolve(actions.uninstallApp?.(app.id)).finally(() => {
993
+ setUninstallAppBusy(false);
994
+ setPendingUninstallApp(null);
995
+ });
996
+ },
997
+ onOpenChange: (nextOpen) => {
998
+ if (!nextOpen && !uninstallAppBusy) {
999
+ setPendingUninstallApp(null);
1000
+ }
1001
+ }
1002
+ }
1003
+ ),
1004
+ /* @__PURE__ */ jsx2(
1005
+ Dialog,
1006
+ {
1007
+ open: factoryDialogOpen,
1008
+ onOpenChange: (nextOpen) => {
1009
+ if (!nextOpen) {
1010
+ closeFactoryDialog();
1011
+ return;
1012
+ }
1013
+ openFactoryDialog();
1014
+ },
1015
+ children: /* @__PURE__ */ jsx2(
1016
+ DialogContent,
1017
+ {
1018
+ className: "sm:max-w-[min(640px,calc(100%-2rem))]",
1019
+ showCloseButton: false,
1020
+ children: /* @__PURE__ */ jsxs2(
1021
+ "form",
1022
+ {
1023
+ className: "grid max-h-[min(760px,calc(100vh-64px))] min-w-0 gap-4 overflow-auto",
1024
+ onSubmit: submitCreate,
1025
+ children: [
1026
+ /* @__PURE__ */ jsx2(DialogHeader, { children: /* @__PURE__ */ jsx2(DialogTitle, { children: copy.t("factory.labels.create") }) }),
1027
+ /* @__PURE__ */ jsxs2("label", { className: "grid min-w-0 gap-2", children: [
1028
+ /* @__PURE__ */ jsx2("span", { className: "text-[12px] font-semibold leading-4 text-[var(--text-secondary)]", children: copy.t("factory.labels.appName") }),
1029
+ /* @__PURE__ */ jsx2(
1030
+ Input,
1031
+ {
1032
+ autoFocus: true,
1033
+ className: "h-9 rounded-[8px]",
1034
+ placeholder: copy.t("factory.placeholders.appName"),
1035
+ value: displayName,
1036
+ onChange: (event) => setDisplayName(event.currentTarget.value)
1037
+ }
1038
+ )
1039
+ ] }),
1040
+ /* @__PURE__ */ jsxs2("div", { className: "grid min-w-0 gap-4", children: [
1041
+ /* @__PURE__ */ jsxs2("div", { className: "grid min-w-0 gap-2", children: [
1042
+ /* @__PURE__ */ jsx2(
1043
+ "label",
1044
+ {
1045
+ className: "text-[12px] font-semibold leading-4 text-[var(--text-secondary)]",
1046
+ htmlFor: promptTextareaId,
1047
+ children: copy.t("factory.labels.prompt")
1048
+ }
1049
+ ),
1050
+ /* @__PURE__ */ jsxs2("div", { className: "relative min-w-0", children: [
1051
+ /* @__PURE__ */ jsx2(
1052
+ Textarea,
1053
+ {
1054
+ className: "min-h-[148px] resize-none rounded-[10px] border border-[color:var(--line-2)] bg-[var(--background-fronted)] pb-14 leading-[1.45] sm:pb-8",
1055
+ id: promptTextareaId,
1056
+ placeholder: copy.t("factory.placeholders.prompt"),
1057
+ value: prompt,
1058
+ onChange: (event) => setPrompt(event.currentTarget.value)
1059
+ }
1060
+ ),
1061
+ /* @__PURE__ */ jsxs2("div", { className: "pointer-events-none absolute inset-x-3 bottom-1.5 flex min-w-0 flex-wrap items-end justify-between gap-2", children: [
1062
+ providerErrorMessage && normalizedProviderOptions.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "pointer-events-auto max-w-[240px] text-[12px] leading-4 text-[var(--state-danger)]", children: providerErrorMessage }) : /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "min-h-4 flex-1" }),
1063
+ /* @__PURE__ */ jsxs2("div", { className: "pointer-events-auto flex min-w-0 max-w-full flex-wrap items-center justify-end gap-0.5 px-0.5 py-0", children: [
1064
+ /* @__PURE__ */ jsx2(
1065
+ FactoryProviderSelect,
1066
+ {
1067
+ copy,
1068
+ errorMessage: providerErrorMessage,
1069
+ loading: providerLoading,
1070
+ open: openFactorySettingsMenu === "provider",
1071
+ options: normalizedProviderOptions,
1072
+ selectedProvider,
1073
+ triggerClassName: "h-6 w-auto max-w-full border-0 bg-transparent px-1.5 text-[11px] font-medium text-[var(--text-secondary)] shadow-none hover:bg-transparent focus-visible:bg-transparent data-[placeholder]:text-[var(--text-tertiary)]",
1074
+ onOpenChange: (nextOpen) => setOpenFactorySettingsMenu(
1075
+ nextOpen ? "provider" : null
1076
+ ),
1077
+ onSelectProvider: setSelectedProvider
1078
+ }
1079
+ ),
1080
+ /* @__PURE__ */ jsx2(
1081
+ "div",
1082
+ {
1083
+ "aria-hidden": "true",
1084
+ className: "h-4 w-px bg-[var(--line-2)]"
1085
+ }
1086
+ ),
1087
+ /* @__PURE__ */ jsx2(
1088
+ FactoryPermissionDropdown,
1089
+ {
1090
+ copy,
1091
+ loading: providerConfigurationStatus === "loading",
1092
+ open: openFactorySettingsMenu === "permission",
1093
+ options: permissionModeOptions,
1094
+ selectedPermissionModeId,
1095
+ triggerClassName: "h-6 w-auto max-w-full border-0 bg-transparent px-1.5 text-[11px] font-medium text-[var(--text-secondary)] shadow-none hover:bg-transparent focus-visible:bg-transparent",
1096
+ onOpenChange: (nextOpen) => setOpenFactorySettingsMenu(
1097
+ nextOpen ? "permission" : null
1098
+ ),
1099
+ onSelectPermissionMode: setSelectedPermissionModeId
1100
+ }
1101
+ ),
1102
+ /* @__PURE__ */ jsx2(
1103
+ "div",
1104
+ {
1105
+ "aria-hidden": "true",
1106
+ className: "h-4 w-px bg-[var(--line-2)]"
1107
+ }
1108
+ ),
1109
+ /* @__PURE__ */ jsx2(
1110
+ FactoryModelReasoningDropdown,
1111
+ {
1112
+ copy,
1113
+ loading: providerConfigurationStatus === "loading",
1114
+ modelOptions,
1115
+ open: openFactorySettingsMenu === "modelReasoning",
1116
+ reasoningEffortOptions,
1117
+ selectedModel,
1118
+ selectedReasoningEffort,
1119
+ triggerClassName: "h-6 w-auto max-w-full border-0 bg-transparent px-1.5 text-[11px] font-medium text-[var(--text-secondary)] shadow-none hover:bg-transparent focus-visible:bg-transparent",
1120
+ onOpenChange: (nextOpen) => setOpenFactorySettingsMenu(
1121
+ nextOpen ? "modelReasoning" : null
1122
+ ),
1123
+ onSelectModel: setSelectedModel,
1124
+ onSelectReasoningEffort: setSelectedReasoningEffort
1125
+ }
1126
+ )
1127
+ ] })
1128
+ ] })
1129
+ ] })
1130
+ ] }),
1131
+ /* @__PURE__ */ jsxs2("fieldset", { className: "min-w-0 border-0 p-0", children: [
1132
+ /* @__PURE__ */ jsx2("legend", { className: "sr-only", children: copy.t("factory.labels.templates") }),
1133
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2", children: [
1134
+ /* @__PURE__ */ jsx2("span", { className: "shrink-0 text-[14px] font-medium leading-5 text-[var(--text-secondary)]", children: copy.t("factory.labels.templateInspirationPrefix") }),
1135
+ factoryTemplates.map((template) => /* @__PURE__ */ jsxs2(
1136
+ "button",
1137
+ {
1138
+ className: "inline-flex max-w-full items-center gap-1 border-0 bg-transparent p-0 text-[14px] font-medium leading-5 text-[var(--text-secondary)] transition-colors duration-150 hover:text-[var(--text-primary)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--border-focus)]",
1139
+ type: "button",
1140
+ onClick: () => selectTemplate(template),
1141
+ children: [
1142
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: copy.t(template.titleKey) }),
1143
+ /* @__PURE__ */ jsx2(
1144
+ OpenSessionsFilledIcon,
1145
+ {
1146
+ "aria-hidden": "true",
1147
+ className: "shrink-0",
1148
+ size: 14
1149
+ }
1150
+ )
1151
+ ]
1152
+ },
1153
+ template.id
1154
+ ))
1155
+ ] })
1156
+ ] })
1157
+ ] }),
1158
+ /* @__PURE__ */ jsxs2(DialogFooter, { children: [
1159
+ /* @__PURE__ */ jsx2(
1160
+ Button2,
1161
+ {
1162
+ size: "dialog",
1163
+ type: "button",
1164
+ variant: "ghost",
1165
+ onClick: closeFactoryDialog,
1166
+ children: copy.t("factory.actions.cancel")
1167
+ }
1168
+ ),
1169
+ /* @__PURE__ */ jsx2(
1170
+ Button2,
1171
+ {
1172
+ disabled: !canCreateFactoryJob,
1173
+ size: "dialog",
1174
+ type: "submit",
1175
+ children: copy.t("factory.actions.create")
1176
+ }
1177
+ )
1178
+ ] })
1179
+ ]
1180
+ }
1181
+ )
1182
+ }
1183
+ )
1184
+ }
1185
+ )
1186
+ ]
1187
+ }
1188
+ );
1189
+ }
1190
+ function FactoryProviderSelect({
1191
+ copy,
1192
+ errorMessage,
1193
+ loading,
1194
+ onOpenChange,
1195
+ onSelectProvider,
1196
+ open,
1197
+ options,
1198
+ selectedProvider,
1199
+ triggerClassName
1200
+ }) {
1201
+ const selectedOption = options.find((option) => option.provider === selectedProvider) ?? null;
1202
+ const placeholder = options.length > 0 ? copy.t("factory.messages.noAgentProviders") : loading ? copy.t("factory.messages.loadingProviders") : errorMessage?.trim() || copy.t("factory.messages.noAgentProviders");
1203
+ return /* @__PURE__ */ jsxs2(
1204
+ Select,
1205
+ {
1206
+ disabled: options.length === 0,
1207
+ open,
1208
+ value: selectedOption?.provider,
1209
+ onOpenChange,
1210
+ onValueChange: onSelectProvider,
1211
+ children: [
1212
+ /* @__PURE__ */ jsx2(
1213
+ SelectTrigger,
1214
+ {
1215
+ "aria-label": copy.t("factory.labels.agent"),
1216
+ className: cn2(
1217
+ "h-9 w-full rounded-[8px] px-3 font-normal [&_[data-slot=select-value]]:flex [&_[data-slot=select-value]]:min-w-0 [&_[data-slot=select-value]]:items-center [&_[data-slot=select-value]]:gap-2",
1218
+ loading && options.length === 0 ? "animate-pulse" : null,
1219
+ triggerClassName
1220
+ ),
1221
+ children: /* @__PURE__ */ jsx2(SelectValue, { placeholder, children: selectedOption ? /* @__PURE__ */ jsxs2("span", { className: "flex min-w-0 items-center gap-2", children: [
1222
+ /* @__PURE__ */ jsx2(AppCenterAgentProviderIcon, { iconUrl: selectedOption.iconUrl }),
1223
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: selectedOption.label })
1224
+ ] }) : null })
1225
+ }
1226
+ ),
1227
+ /* @__PURE__ */ jsx2(
1228
+ SelectContent,
1229
+ {
1230
+ align: "start",
1231
+ className: "w-[300px] min-w-[240px] max-w-[min(100vw-64px,480px)]",
1232
+ style: { zIndex: "var(--z-dialog-popover)" },
1233
+ children: options.length === 0 ? /* @__PURE__ */ jsx2(SelectItem, { disabled: true, value: "__no-provider__", children: copy.t("factory.messages.noAgentProviders") }) : options.map((option) => /* @__PURE__ */ jsx2(
1234
+ SelectItem,
1235
+ {
1236
+ disabled: option.disabled === true,
1237
+ title: option.disabledReason,
1238
+ value: option.provider,
1239
+ children: /* @__PURE__ */ jsxs2("span", { className: "flex min-w-0 items-center gap-2", children: [
1240
+ /* @__PURE__ */ jsx2(AppCenterAgentProviderIcon, { iconUrl: option.iconUrl }),
1241
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: option.label })
1242
+ ] })
1243
+ },
1244
+ option.provider
1245
+ ))
1246
+ }
1247
+ )
1248
+ ]
1249
+ }
1250
+ );
1251
+ }
1252
+ function FactoryPermissionDropdown({
1253
+ copy,
1254
+ loading,
1255
+ onOpenChange,
1256
+ onSelectPermissionMode,
1257
+ open,
1258
+ options,
1259
+ selectedPermissionModeId,
1260
+ triggerClassName
1261
+ }) {
1262
+ const selectedOption = options.find((option) => option.value === selectedPermissionModeId) ?? null;
1263
+ const disabled = loading || options.length === 0;
1264
+ const selectedLabel = selectedOption ? permissionOptionLabel(copy, selectedOption) : selectedPermissionModeId;
1265
+ return /* @__PURE__ */ jsxs2(
1266
+ Select,
1267
+ {
1268
+ disabled,
1269
+ open,
1270
+ value: selectedOption?.value,
1271
+ onOpenChange,
1272
+ onValueChange: onSelectPermissionMode,
1273
+ children: [
1274
+ /* @__PURE__ */ jsx2(
1275
+ SelectTrigger,
1276
+ {
1277
+ "aria-label": copy.t("factory.labels.review"),
1278
+ className: cn2(
1279
+ "h-9 max-w-full rounded-[999px] border border-[color:var(--line-2)] bg-[var(--background-panel)] px-3 text-[13px] font-medium text-[var(--text-primary)] shadow-none hover:bg-[var(--transparency-block)] [&>svg:last-child]:opacity-70",
1280
+ loading ? "animate-pulse" : null,
1281
+ disabled ? "cursor-not-allowed text-[var(--text-tertiary)] opacity-60 hover:bg-[var(--background-panel)]" : null,
1282
+ triggerClassName
1283
+ ),
1284
+ children: /* @__PURE__ */ jsx2("span", { className: "flex min-w-0 items-center overflow-hidden", children: selectedLabel ? /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: selectedLabel }) : /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate text-[var(--text-tertiary)]", children: loading ? copy.t("factory.messages.loadingConfiguration") : copy.t("factory.messages.noPermissionOptions") }) })
1285
+ }
1286
+ ),
1287
+ /* @__PURE__ */ jsx2(
1288
+ SelectContent,
1289
+ {
1290
+ align: "end",
1291
+ className: "w-max min-w-[220px] max-w-[min(100vw-32px,360px)] data-[side=top]:!translate-y-0",
1292
+ sideOffset: 4,
1293
+ style: { zIndex: "var(--z-dialog-popover)" },
1294
+ children: options.length > 0 ? options.map((option) => /* @__PURE__ */ jsx2(SelectItem, { value: option.value, children: /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: permissionOptionLabel(copy, option) }) }, option.value)) : /* @__PURE__ */ jsx2(SelectItem, { disabled: true, value: "__empty__", children: copy.t("factory.messages.noPermissionOptions") })
1295
+ }
1296
+ )
1297
+ ]
1298
+ }
1299
+ );
1300
+ }
1301
+ function FactoryModelReasoningDropdown({
1302
+ copy,
1303
+ loading,
1304
+ modelOptions,
1305
+ onOpenChange,
1306
+ onSelectModel,
1307
+ onSelectReasoningEffort,
1308
+ open,
1309
+ reasoningEffortOptions,
1310
+ selectedModel,
1311
+ selectedReasoningEffort,
1312
+ triggerClassName
1313
+ }) {
1314
+ const showModelSection = modelOptions.length > 0;
1315
+ const showReasoningSection = reasoningEffortOptions.length > 0;
1316
+ const disabled = loading || !showModelSection && !showReasoningSection;
1317
+ const selectedModelLabel = modelOptions.find((option) => option.value === selectedModel)?.label ?? selectedModel;
1318
+ const selectedReasoningLabel = reasoningEffortOptions.find(
1319
+ (option) => option.value === selectedReasoningEffort
1320
+ )?.label ?? selectedReasoningEffort;
1321
+ const triggerLabel = [selectedModelLabel, selectedReasoningLabel].filter((value) => value.trim().length > 0).join(" ");
1322
+ const selectedValue = showModelSection ? selectedModel ? `model:${selectedModel}` : void 0 : selectedReasoningEffort ? `reasoning:${selectedReasoningEffort}` : void 0;
1323
+ const applySettingsValue = (nextValue) => {
1324
+ if (nextValue.startsWith("reasoning:")) {
1325
+ onSelectReasoningEffort(nextValue.slice("reasoning:".length));
1326
+ return;
1327
+ }
1328
+ if (nextValue.startsWith("model:")) {
1329
+ onSelectModel(nextValue.slice("model:".length));
1330
+ }
1331
+ };
1332
+ return /* @__PURE__ */ jsxs2(
1333
+ Select,
1334
+ {
1335
+ disabled,
1336
+ open,
1337
+ value: selectedValue,
1338
+ onOpenChange,
1339
+ onValueChange: applySettingsValue,
1340
+ children: [
1341
+ /* @__PURE__ */ jsx2(
1342
+ SelectTrigger,
1343
+ {
1344
+ "aria-label": copy.t("factory.labels.modelReasoning"),
1345
+ className: cn2(
1346
+ "h-9 max-w-full rounded-[999px] border border-[color:var(--line-2)] bg-[var(--background-panel)] px-3 text-[13px] font-medium text-[var(--text-primary)] shadow-none hover:bg-[var(--transparency-block)] [&>svg:last-child]:opacity-70",
1347
+ loading ? "animate-pulse" : null,
1348
+ disabled ? "cursor-not-allowed text-[var(--text-tertiary)] opacity-60 hover:bg-[var(--background-panel)]" : null,
1349
+ triggerClassName
1350
+ ),
1351
+ children: /* @__PURE__ */ jsx2("span", { className: "flex min-w-0 items-center gap-2 overflow-hidden", children: triggerLabel ? selectedModelLabel && selectedReasoningLabel ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1352
+ /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: selectedModelLabel }),
1353
+ /* @__PURE__ */ jsx2("span", { className: "shrink-0 text-[var(--text-secondary)]", children: selectedReasoningLabel })
1354
+ ] }) : /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: triggerLabel }) : /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate text-[var(--text-tertiary)]", children: loading ? copy.t("factory.messages.loadingConfiguration") : copy.t("factory.messages.noConfigurationOptions") }) })
1355
+ }
1356
+ ),
1357
+ /* @__PURE__ */ jsx2(
1358
+ SelectContent,
1359
+ {
1360
+ align: "end",
1361
+ className: cn2(
1362
+ "max-w-[min(100vw-32px,460px)] data-[side=top]:!translate-y-0",
1363
+ showModelSection && showReasoningSection ? "w-[430px]" : "w-max min-w-[240px]"
1364
+ ),
1365
+ sideOffset: 4,
1366
+ style: { zIndex: "var(--z-dialog-popover)" },
1367
+ children: showModelSection && showReasoningSection ? /* @__PURE__ */ jsxs2(SelectSplitLayout, { children: [
1368
+ /* @__PURE__ */ jsxs2(SelectSplitColumn, { children: [
1369
+ /* @__PURE__ */ jsx2(SelectSplitColumnLabel, { children: copy.t("factory.labels.model") }),
1370
+ /* @__PURE__ */ jsx2(SelectSplitColumnItems, { children: modelOptions.map((option) => /* @__PURE__ */ jsx2(
1371
+ SelectItem,
1372
+ {
1373
+ value: `model:${option.value}`,
1374
+ children: /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: option.label })
1375
+ },
1376
+ option.value
1377
+ )) })
1378
+ ] }),
1379
+ /* @__PURE__ */ jsx2(SelectSplitDivider, {}),
1380
+ /* @__PURE__ */ jsxs2(SelectSplitColumn, { children: [
1381
+ /* @__PURE__ */ jsx2(SelectSplitColumnLabel, { children: copy.t("factory.labels.reasoningEffort") }),
1382
+ /* @__PURE__ */ jsx2(SelectSplitColumnItems, { children: reasoningEffortOptions.map((option) => /* @__PURE__ */ jsx2(
1383
+ SelectItem,
1384
+ {
1385
+ forceSelectedIndicator: selectedReasoningEffort === option.value,
1386
+ value: `reasoning:${option.value}`,
1387
+ children: /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: option.label })
1388
+ },
1389
+ option.value
1390
+ )) })
1391
+ ] })
1392
+ ] }) : showModelSection ? modelOptions.map((option) => /* @__PURE__ */ jsx2(SelectItem, { value: `model:${option.value}`, children: /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: option.label }) }, option.value)) : showReasoningSection ? reasoningEffortOptions.map((option) => /* @__PURE__ */ jsx2(SelectItem, { value: `reasoning:${option.value}`, children: /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: option.label }) }, option.value)) : /* @__PURE__ */ jsx2(SelectItem, { disabled: true, value: "__empty__", children: copy.t("factory.messages.noConfigurationOptions") })
1393
+ }
1394
+ )
1395
+ ]
1396
+ }
1397
+ );
1398
+ }
1399
+ function permissionOptionLabel(copy, option) {
1400
+ const semantic = option.semantic?.trim();
1401
+ if (semantic) {
1402
+ const key = `factory.permissionSemantics.${semantic}.label`;
1403
+ const translated = copy.t(key);
1404
+ if (translated !== key) {
1405
+ return translated;
1406
+ }
1407
+ }
1408
+ return option.label.trim() || option.value;
1409
+ }
1410
+ function AppCenterAgentProviderIcon({
1411
+ iconUrl
1412
+ }) {
1413
+ const normalizedIconUrl = iconUrl?.trim();
1414
+ if (!normalizedIconUrl) {
1415
+ return /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "size-4 shrink-0 rounded-[4px]" });
1416
+ }
1417
+ return /* @__PURE__ */ jsx2(
1418
+ "img",
1419
+ {
1420
+ alt: "",
1421
+ "aria-hidden": "true",
1422
+ className: "size-4 shrink-0 rounded-[4px] object-contain",
1423
+ decoding: "async",
1424
+ draggable: false,
1425
+ src: normalizedIconUrl
1426
+ }
1427
+ );
1428
+ }
1429
+ function resolveSelectedFactoryOptionValue(currentValue, options, defaultValue) {
1430
+ if (options.some((option) => option.value === currentValue)) {
1431
+ return currentValue;
1432
+ }
1433
+ const normalizedDefault = defaultValue?.trim() ?? "";
1434
+ if (normalizedDefault && options.some((option) => option.value === normalizedDefault)) {
1435
+ return normalizedDefault;
1436
+ }
1437
+ return options[0]?.value ?? "";
1438
+ }
1439
+ function AppCardGrid({
1440
+ actions,
1441
+ apps,
1442
+ copy,
1443
+ emptyMessage,
1444
+ title
1445
+ }) {
1446
+ if (apps.length === 0) {
1447
+ return /* @__PURE__ */ jsx2(
1448
+ "div",
1449
+ {
1450
+ "aria-label": title,
1451
+ className: "flex min-h-[min(360px,45vh)] min-w-0 flex-1 items-center justify-center rounded-[8px] px-6 text-center text-[13px] leading-5 text-[var(--text-secondary)]",
1452
+ role: "status",
1453
+ children: emptyMessage
1454
+ }
1455
+ );
1456
+ }
1457
+ return /* @__PURE__ */ jsx2(
1458
+ "div",
1459
+ {
1460
+ "aria-label": title,
1461
+ className: "grid min-h-0 min-w-0 grid-cols-[repeat(auto-fill,minmax(min(100%,260px),1fr))] gap-3",
1462
+ role: "list",
1463
+ children: apps.map((app) => /* @__PURE__ */ jsx2(AppCard, { actions, app, copy }, app.id))
1464
+ }
1465
+ );
1466
+ }
1467
+ function createRecommendedCategoryTabs(apps, copy) {
1468
+ const categoryCounts = /* @__PURE__ */ new Map();
1469
+ for (const app of apps) {
1470
+ const category = app.category?.trim();
1471
+ if (!category) {
1472
+ continue;
1473
+ }
1474
+ categoryCounts.set(category, (categoryCounts.get(category) ?? 0) + 1);
1475
+ }
1476
+ return recommendedCategoryTabDefinitions.map((definition) => {
1477
+ if (definition.id === "all") {
1478
+ return {
1479
+ category: null,
1480
+ count: apps.length,
1481
+ id: definition.id,
1482
+ label: copy.t("labels.allApps")
1483
+ };
1484
+ }
1485
+ const category = copy.t(definition.labelKey);
1486
+ return {
1487
+ category,
1488
+ count: categoryCounts.get(category) ?? 0,
1489
+ id: definition.id,
1490
+ label: category
1491
+ };
1492
+ });
1493
+ }
1494
+ function RecommendedCategoryTabs({
1495
+ copy,
1496
+ tabs,
1497
+ value,
1498
+ onValueChange
1499
+ }) {
1500
+ return /* @__PURE__ */ jsx2(
1501
+ "div",
1502
+ {
1503
+ "aria-label": copy.t("labels.appCategories"),
1504
+ className: "-mx-1 flex min-w-0 items-center gap-2 overflow-x-auto px-1 py-1",
1505
+ role: "tablist",
1506
+ children: tabs.map((tab) => {
1507
+ const selected = tab.id === value;
1508
+ return /* @__PURE__ */ jsxs2(
1509
+ "button",
1510
+ {
1511
+ "aria-selected": selected,
1512
+ className: cn2(
1513
+ "flex h-8 shrink-0 items-center rounded-[8px] px-3 text-[13px] font-semibold leading-5 tracking-[0] transition-colors duration-150 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--border-focus)]",
1514
+ selected ? "border border-[color:var(--line-1)] bg-[var(--background-fronted)] text-[var(--text-primary)]" : "border border-transparent text-[var(--text-tertiary)] hover:text-[var(--text-primary)]"
1515
+ ),
1516
+ role: "tab",
1517
+ type: "button",
1518
+ onClick: () => onValueChange(tab.id),
1519
+ children: [
1520
+ tab.label,
1521
+ " ",
1522
+ tab.count
1523
+ ]
1524
+ },
1525
+ tab.id
1526
+ );
1527
+ })
1528
+ }
1529
+ );
1530
+ }
1531
+ function AppCenterStatusToast({
1532
+ toast
1533
+ }) {
1534
+ return /* @__PURE__ */ jsx2(ToastProvider, { children: /* @__PURE__ */ jsx2(
1535
+ ToastRoot,
1536
+ {
1537
+ open: true,
1538
+ anchor: "node",
1539
+ busy: toast.busy,
1540
+ className: cn2(
1541
+ "z-30 w-[calc(100%_-_48px)] max-w-[640px] justify-start px-4 py-3 text-left text-[12px] leading-5 shadow-[0_14px_36px_var(--shadow-elevated)]",
1542
+ toast.tone === "default" ? "border-[var(--line-2)] bg-[var(--background-fronted)] text-[var(--text-secondary)]" : ""
1543
+ ),
1544
+ nodeInsetTopPx: 20,
1545
+ variant: toast.tone,
1546
+ children: /* @__PURE__ */ jsx2(ToastTitle, { className: "w-full justify-start gap-2 text-left text-[12px] leading-5", children: /* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: toast.message }) })
1547
+ },
1548
+ `${toast.tone}:${toast.message}`
1549
+ ) });
1550
+ }
1551
+ function AppCenterHeaderActions({
1552
+ copy,
1553
+ onCreateApp,
1554
+ onImportApp
1555
+ }) {
1556
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
1557
+ /* @__PURE__ */ jsxs2(Tooltip, { children: [
1558
+ /* @__PURE__ */ jsx2(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
1559
+ Button2,
1560
+ {
1561
+ size: "default",
1562
+ type: "button",
1563
+ variant: "ghost",
1564
+ onClick: onImportApp,
1565
+ children: [
1566
+ /* @__PURE__ */ jsx2(ImportIcon, {}),
1567
+ /* @__PURE__ */ jsx2("span", { children: copy.t("actions.importApp") })
1568
+ ]
1569
+ }
1570
+ ) }),
1571
+ /* @__PURE__ */ jsx2(TooltipContent, { side: "bottom", children: copy.t("actions.importAppTooltip") })
1572
+ ] }),
1573
+ /* @__PURE__ */ jsxs2(
1574
+ Button2,
1575
+ {
1576
+ size: "default",
1577
+ type: "button",
1578
+ variant: "ghost",
1579
+ onClick: onCreateApp,
1580
+ children: [
1581
+ /* @__PURE__ */ jsx2(FileCreateIcon, {}),
1582
+ copy.t("factory.actions.create")
1583
+ ]
1584
+ }
1585
+ )
1586
+ ] });
1587
+ }
1588
+ function FactoryJobStatusIndicator({
1589
+ copy,
1590
+ job
1591
+ }) {
1592
+ const statusLabel = copy.t(job.statusLabelKey);
1593
+ if (job.status === "generating") {
1594
+ return /* @__PURE__ */ jsx2(
1595
+ "span",
1596
+ {
1597
+ "aria-label": statusLabel,
1598
+ className: "inline-flex size-3 shrink-0 items-center justify-center",
1599
+ role: "status",
1600
+ title: statusLabel,
1601
+ children: /* @__PURE__ */ jsx2(
1602
+ Spinner,
1603
+ {
1604
+ className: "text-[var(--text-tertiary)]",
1605
+ size: 12,
1606
+ strokeWidth: 2,
1607
+ trackColor: "var(--line-2)"
1608
+ }
1609
+ )
1610
+ }
1611
+ );
1612
+ }
1613
+ return /* @__PURE__ */ jsx2(Badge, { variant: job.status === "failed" ? "destructive" : "secondary", children: statusLabel });
1614
+ }
1615
+ export {
1616
+ AppCard,
1617
+ AppCenterPanel
1618
+ };
1619
+ //# sourceMappingURL=index.js.map