@tutti-os/workspace-app-center 0.0.11 → 0.0.13
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/chunk-2HXFGEJS.js +110 -0
- package/dist/chunk-2HXFGEJS.js.map +1 -0
- package/dist/{chunk-PD2WLRHE.js → chunk-7BJAJ7F3.js} +1 -1
- package/dist/chunk-7BJAJ7F3.js.map +1 -0
- package/dist/{chunk-5SANC3S6.js → chunk-DPJRK63W.js} +249 -62
- package/dist/chunk-DPJRK63W.js.map +1 -0
- package/dist/{chunk-UBERYRMA.js → chunk-Y63TZ5JT.js} +1 -1
- package/dist/chunk-Y63TZ5JT.js.map +1 -0
- package/dist/contracts/index.d.ts +8 -4
- package/dist/contracts/index.js +2 -2
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.js +3 -3
- package/dist/i18n/index.js +115 -145
- package/dist/i18n/index.js.map +1 -1
- package/dist/{index-B1t4jDvQ.d.ts → index-Clfpl59g.d.ts} +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/ui/index.d.ts +3 -2
- package/dist/ui/index.js +296 -177
- package/dist/ui/index.js.map +1 -1
- package/dist/{viewModel-BEb0vvYM.d.ts → viewModel-D_kMlhKv.d.ts} +17 -5
- package/package.json +3 -3
- package/dist/chunk-5SANC3S6.js.map +0 -1
- package/dist/chunk-LBSZLLZK.js +0 -94
- package/dist/chunk-LBSZLLZK.js.map +0 -1
- package/dist/chunk-PD2WLRHE.js.map +0 -1
- package/dist/chunk-UBERYRMA.js.map +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/core/appCenterAppOrdering.ts
|
|
2
|
+
var installableRecommendedAppIds = [
|
|
3
|
+
["ai-media-canvas", "media-canvas"],
|
|
4
|
+
["vibe-design"],
|
|
5
|
+
["group-chat"],
|
|
6
|
+
["automation"],
|
|
7
|
+
["daily-product-radar", "daily-tech-radar", "radar"]
|
|
8
|
+
];
|
|
9
|
+
var comingSoonRecommendedAppIds = [
|
|
10
|
+
"ai-ppt",
|
|
11
|
+
"ai-document",
|
|
12
|
+
"ai-sheet",
|
|
13
|
+
"open-cut",
|
|
14
|
+
"product-competition",
|
|
15
|
+
"design-review",
|
|
16
|
+
"calendar",
|
|
17
|
+
"document-summarizer"
|
|
18
|
+
];
|
|
19
|
+
var recommendedAppDisplayRankById = buildRecommendedAppDisplayRankById();
|
|
20
|
+
function sortMyAppsByCreatedDesc(apps) {
|
|
21
|
+
return [...apps].sort((left, right) => {
|
|
22
|
+
const createdOrder = (right.createdAtUnixMs ?? 0) - (left.createdAtUnixMs ?? 0);
|
|
23
|
+
if (createdOrder !== 0) {
|
|
24
|
+
return createdOrder;
|
|
25
|
+
}
|
|
26
|
+
const nameOrder = left.name.localeCompare(right.name);
|
|
27
|
+
if (nameOrder !== 0) {
|
|
28
|
+
return nameOrder;
|
|
29
|
+
}
|
|
30
|
+
return left.id.localeCompare(right.id);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function sortRecommendedApps(apps) {
|
|
34
|
+
return [...apps].sort(compareRecommendedApps);
|
|
35
|
+
}
|
|
36
|
+
function sortRecommendedAppsForAllTab(apps) {
|
|
37
|
+
return [...apps].sort(compareRecommendedApps);
|
|
38
|
+
}
|
|
39
|
+
function compareRecommendedApps(left, right) {
|
|
40
|
+
const sectionOrder = getRecommendedAppSectionRank(left) - getRecommendedAppSectionRank(right);
|
|
41
|
+
if (sectionOrder !== 0) {
|
|
42
|
+
return sectionOrder;
|
|
43
|
+
}
|
|
44
|
+
const displayOrder = getRecommendedAppDisplayRank(left.id) - getRecommendedAppDisplayRank(right.id);
|
|
45
|
+
if (displayOrder !== 0) {
|
|
46
|
+
return displayOrder;
|
|
47
|
+
}
|
|
48
|
+
const comingSoonOrder = getRecommendedAppComingSoonRank(left) - getRecommendedAppComingSoonRank(right);
|
|
49
|
+
if (comingSoonOrder !== 0) {
|
|
50
|
+
return comingSoonOrder;
|
|
51
|
+
}
|
|
52
|
+
return left.name.localeCompare(right.name);
|
|
53
|
+
}
|
|
54
|
+
function buildRecommendedAppDisplayRankById() {
|
|
55
|
+
const rankById = /* @__PURE__ */ new Map();
|
|
56
|
+
let rank = 0;
|
|
57
|
+
for (const aliases of installableRecommendedAppIds) {
|
|
58
|
+
for (const appId of aliases) {
|
|
59
|
+
rankById.set(appId, rank);
|
|
60
|
+
}
|
|
61
|
+
rank += 1;
|
|
62
|
+
}
|
|
63
|
+
for (const appId of comingSoonRecommendedAppIds) {
|
|
64
|
+
rankById.set(appId, rank);
|
|
65
|
+
rank += 1;
|
|
66
|
+
}
|
|
67
|
+
return rankById;
|
|
68
|
+
}
|
|
69
|
+
function getRecommendedAppDisplayRank(appId) {
|
|
70
|
+
return recommendedAppDisplayRankById.get(appId.trim().toLowerCase()) ?? Number.MAX_SAFE_INTEGER;
|
|
71
|
+
}
|
|
72
|
+
function getRecommendedAppSectionRank(app) {
|
|
73
|
+
const normalizedAppId = app.id.trim().toLowerCase();
|
|
74
|
+
if (recommendedAppDisplayRankById.has(normalizedAppId)) {
|
|
75
|
+
return isConfiguredComingSoonApp(normalizedAppId) ? 1 : 0;
|
|
76
|
+
}
|
|
77
|
+
return getRecommendedAppComingSoonRank(app);
|
|
78
|
+
}
|
|
79
|
+
function isConfiguredComingSoonApp(appId) {
|
|
80
|
+
return comingSoonRecommendedAppIds.some(
|
|
81
|
+
(configuredAppId) => configuredAppId === appId
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
function getRecommendedAppComingSoonRank(app) {
|
|
85
|
+
return app.statusLabelKey === "status.comingSoon" || app.tags.some((tag) => tag.trim().toLowerCase() === "coming-soon") ? 1 : 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/core/appFactoryProviderDefaults.ts
|
|
89
|
+
var DEFAULT_APP_FACTORY_PROVIDER = "codex";
|
|
90
|
+
function resolveDefaultAppFactoryProvider(options, preferredProvider) {
|
|
91
|
+
const normalizedPreferredProvider = preferredProvider?.trim() ?? "";
|
|
92
|
+
return options.find(
|
|
93
|
+
(option) => normalizedPreferredProvider !== "" && option.disabled !== true && option.provider === normalizedPreferredProvider
|
|
94
|
+
)?.provider ?? options.find(
|
|
95
|
+
(option) => option.disabled !== true && option.provider === DEFAULT_APP_FACTORY_PROVIDER
|
|
96
|
+
)?.provider ?? options.find((option) => option.disabled !== true)?.provider ?? options.find((option) => option.provider === DEFAULT_APP_FACTORY_PROVIDER)?.provider ?? options[0]?.provider ?? "";
|
|
97
|
+
}
|
|
98
|
+
function resolveSelectedAppFactoryProvider(currentProvider, options, preferredProvider) {
|
|
99
|
+
return options.some((option) => option.provider === currentProvider) ? currentProvider : resolveDefaultAppFactoryProvider(options, preferredProvider);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
sortMyAppsByCreatedDesc,
|
|
104
|
+
sortRecommendedApps,
|
|
105
|
+
sortRecommendedAppsForAllTab,
|
|
106
|
+
DEFAULT_APP_FACTORY_PROVIDER,
|
|
107
|
+
resolveDefaultAppFactoryProvider,
|
|
108
|
+
resolveSelectedAppFactoryProvider
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=chunk-2HXFGEJS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/appCenterAppOrdering.ts","../src/core/appFactoryProviderDefaults.ts"],"sourcesContent":["import type { AppCenterViewModel } from \"../contracts/viewModel.ts\";\n\nconst installableRecommendedAppIds = [\n [\"ai-media-canvas\", \"media-canvas\"],\n [\"vibe-design\"],\n [\"group-chat\"],\n [\"automation\"],\n [\"daily-product-radar\", \"daily-tech-radar\", \"radar\"]\n] as const;\n\nconst comingSoonRecommendedAppIds = [\n \"ai-ppt\",\n \"ai-document\",\n \"ai-sheet\",\n \"open-cut\",\n \"product-competition\",\n \"design-review\",\n \"calendar\",\n \"document-summarizer\"\n] as const;\n\nconst recommendedAppDisplayRankById = buildRecommendedAppDisplayRankById();\n\nexport function sortMyAppsByCreatedDesc(\n apps: readonly AppCenterViewModel[\"apps\"][number][]\n): AppCenterViewModel[\"apps\"] {\n return [...apps].sort((left, right) => {\n const createdOrder =\n (right.createdAtUnixMs ?? 0) - (left.createdAtUnixMs ?? 0);\n if (createdOrder !== 0) {\n return createdOrder;\n }\n const nameOrder = left.name.localeCompare(right.name);\n if (nameOrder !== 0) {\n return nameOrder;\n }\n return left.id.localeCompare(right.id);\n });\n}\n\nexport function sortRecommendedApps(\n apps: readonly AppCenterViewModel[\"apps\"][number][]\n): AppCenterViewModel[\"apps\"] {\n return [...apps].sort(compareRecommendedApps);\n}\n\nexport function sortRecommendedAppsForAllTab(\n apps: readonly AppCenterViewModel[\"apps\"][number][]\n): AppCenterViewModel[\"apps\"] {\n return [...apps].sort(compareRecommendedApps);\n}\n\nfunction compareRecommendedApps(\n left: AppCenterViewModel[\"apps\"][number],\n right: AppCenterViewModel[\"apps\"][number]\n): number {\n const sectionOrder =\n getRecommendedAppSectionRank(left) - getRecommendedAppSectionRank(right);\n if (sectionOrder !== 0) {\n return sectionOrder;\n }\n\n const displayOrder =\n getRecommendedAppDisplayRank(left.id) -\n getRecommendedAppDisplayRank(right.id);\n if (displayOrder !== 0) {\n return displayOrder;\n }\n\n const comingSoonOrder =\n getRecommendedAppComingSoonRank(left) -\n getRecommendedAppComingSoonRank(right);\n if (comingSoonOrder !== 0) {\n return comingSoonOrder;\n }\n\n return left.name.localeCompare(right.name);\n}\n\nfunction buildRecommendedAppDisplayRankById(): Map<string, number> {\n const rankById = new Map<string, number>();\n let rank = 0;\n\n for (const aliases of installableRecommendedAppIds) {\n for (const appId of aliases) {\n rankById.set(appId, rank);\n }\n rank += 1;\n }\n\n for (const appId of comingSoonRecommendedAppIds) {\n rankById.set(appId, rank);\n rank += 1;\n }\n\n return rankById;\n}\n\nfunction getRecommendedAppDisplayRank(appId: string): number {\n return (\n recommendedAppDisplayRankById.get(appId.trim().toLowerCase()) ??\n Number.MAX_SAFE_INTEGER\n );\n}\n\nfunction getRecommendedAppSectionRank(\n app: AppCenterViewModel[\"apps\"][number]\n): number {\n const normalizedAppId = app.id.trim().toLowerCase();\n if (recommendedAppDisplayRankById.has(normalizedAppId)) {\n return isConfiguredComingSoonApp(normalizedAppId) ? 1 : 0;\n }\n\n return getRecommendedAppComingSoonRank(app);\n}\n\nfunction isConfiguredComingSoonApp(appId: string): boolean {\n return comingSoonRecommendedAppIds.some(\n (configuredAppId) => configuredAppId === appId\n );\n}\n\nfunction getRecommendedAppComingSoonRank(\n app: AppCenterViewModel[\"apps\"][number]\n): number {\n return app.statusLabelKey === \"status.comingSoon\" ||\n app.tags.some((tag) => tag.trim().toLowerCase() === \"coming-soon\")\n ? 1\n : 0;\n}\n","export interface AppFactoryProviderDefaultOption {\n readonly disabled?: boolean;\n readonly provider: string;\n}\n\nexport const DEFAULT_APP_FACTORY_PROVIDER = \"codex\";\n\nexport function resolveDefaultAppFactoryProvider(\n options: readonly AppFactoryProviderDefaultOption[],\n preferredProvider?: string | null\n): string {\n const normalizedPreferredProvider = preferredProvider?.trim() ?? \"\";\n return (\n options.find(\n (option) =>\n normalizedPreferredProvider !== \"\" &&\n option.disabled !== true &&\n option.provider === normalizedPreferredProvider\n )?.provider ??\n options.find(\n (option) =>\n option.disabled !== true &&\n option.provider === DEFAULT_APP_FACTORY_PROVIDER\n )?.provider ??\n options.find((option) => option.disabled !== true)?.provider ??\n options.find((option) => option.provider === DEFAULT_APP_FACTORY_PROVIDER)\n ?.provider ??\n options[0]?.provider ??\n \"\"\n );\n}\n\nexport function resolveSelectedAppFactoryProvider(\n currentProvider: string,\n options: readonly AppFactoryProviderDefaultOption[],\n preferredProvider?: string | null\n): string {\n return options.some((option) => option.provider === currentProvider)\n ? currentProvider\n : resolveDefaultAppFactoryProvider(options, preferredProvider);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contracts/runtime.ts"],"sourcesContent":["export const workspaceAppRuntimeStatuses = [\n \"idle\",\n \"installing\",\n \"preparing\",\n \"starting\",\n \"running\",\n \"failed\",\n \"stopping\",\n \"unavailable\"\n] as const;\n\nexport type WorkspaceAppRuntimeStatus =\n (typeof workspaceAppRuntimeStatuses)[number];\n\nexport interface WorkspaceAppRuntimeError {\n readonly code?: string;\n readonly message: string;\n}\n\nexport type WorkspaceAppInstallUserPhase =\n | \"downloading\"\n | \"installing\"\n | \"starting\";\n\nexport interface WorkspaceAppInstallProgress {\n readonly userPhase: WorkspaceAppInstallUserPhase;\n readonly overallPercent: number;\n readonly downloadedBytes?: number | null;\n readonly totalBytes?: number | null;\n readonly indeterminate: boolean;\n}\n\nexport interface WorkspaceAppRuntimeState {\n readonly runtimeId?: string;\n readonly installationId?: string;\n readonly appId: string;\n readonly status: WorkspaceAppRuntimeStatus;\n readonly launchUrl?: string | null;\n readonly installProgress?: WorkspaceAppInstallProgress | null;\n readonly error?: WorkspaceAppRuntimeError | null;\n readonly startedAt?: string | null;\n readonly updatedAt?: string | null;\n}\n"],"mappings":";AAAO,IAAM,8BAA8B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
workspaceAppManifestSchemaVersion
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Y63TZ5JT.js";
|
|
4
4
|
|
|
5
5
|
// src/core/appCenterControllerHelpers.ts
|
|
6
6
|
function resolveAppRunDurationMs(startedAtUnixMs, now) {
|
|
@@ -30,16 +30,28 @@ function mergeWorkspaceAppCatalogFields(currentApp, snapshotApp) {
|
|
|
30
30
|
updateAvailable: snapshotApp.updateAvailable
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
function areWorkspaceAppInstallProgressEqual(left, right) {
|
|
34
|
+
if (left == null && right == null) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (left == null || right == null) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return left.userPhase === right.userPhase && left.overallPercent === right.overallPercent && left.downloadedBytes === right.downloadedBytes && left.totalBytes === right.totalBytes && left.indeterminate === right.indeterminate;
|
|
41
|
+
}
|
|
33
42
|
function areWorkspaceAppCenterAppsEqual(left, right) {
|
|
34
43
|
if (left.length !== right.length) {
|
|
35
44
|
return false;
|
|
36
45
|
}
|
|
37
46
|
return left.every((leftApp, index) => {
|
|
38
47
|
const rightApp = right[index];
|
|
39
|
-
return rightApp !== void 0 && leftApp.appId === rightApp.appId && leftApp.availableIconUrl === rightApp.availableIconUrl && leftApp.availableVersion === rightApp.availableVersion && leftApp.createdAtUnixMs === rightApp.createdAtUnixMs && leftApp.description === rightApp.description && leftApp.enabled === rightApp.enabled && leftApp.exportable === rightApp.exportable && leftApp.iconUrl === rightApp.iconUrl && leftApp.installed === rightApp.installed &&
|
|
48
|
+
return rightApp !== void 0 && leftApp.appId === rightApp.appId && leftApp.availableIconUrl === rightApp.availableIconUrl && leftApp.availableVersion === rightApp.availableVersion && leftApp.createdAtUnixMs === rightApp.createdAtUnixMs && leftApp.description === rightApp.description && leftApp.enabled === rightApp.enabled && leftApp.exportable === rightApp.exportable && leftApp.iconUrl === rightApp.iconUrl && leftApp.installed === rightApp.installed && areWorkspaceAppInstallProgressEqual(
|
|
49
|
+
leftApp.installProgress,
|
|
50
|
+
rightApp.installProgress
|
|
51
|
+
) && leftApp.installationId === rightApp.installationId && areWorkspaceAppCenterLocalizationsEqual(
|
|
40
52
|
leftApp.localizations ?? [],
|
|
41
53
|
rightApp.localizations ?? []
|
|
42
|
-
) && leftApp.minimizeBehavior === rightApp.minimizeBehavior && leftApp.name === rightApp.name && leftApp.references.
|
|
54
|
+
) && leftApp.minimizeBehavior === rightApp.minimizeBehavior && leftApp.name === rightApp.name && leftApp.references.listSupported === rightApp.references.listSupported && leftApp.runtimeId === rightApp.runtimeId && leftApp.runtimeStatus === rightApp.runtimeStatus && leftApp.source === rightApp.source && leftApp.stateRevision === rightApp.stateRevision && areStringArraysEqual(leftApp.tags ?? [], rightApp.tags ?? []) && (leftApp.updateAvailable ?? false) === (rightApp.updateAvailable ?? false) && leftApp.launchUrl === rightApp.launchUrl && leftApp.version === rightApp.version && leftApp.windowMinHeight === rightApp.windowMinHeight && leftApp.windowMinWidth === rightApp.windowMinWidth;
|
|
43
55
|
});
|
|
44
56
|
}
|
|
45
57
|
function normalizeWorkspaceAppCenterViewState(value) {
|
|
@@ -276,6 +288,85 @@ var WorkspaceAppCenterControllerBase = class {
|
|
|
276
288
|
}
|
|
277
289
|
};
|
|
278
290
|
|
|
291
|
+
// src/core/installProgressMerge.ts
|
|
292
|
+
var installUserPhaseOrder = {
|
|
293
|
+
downloading: 0,
|
|
294
|
+
installing: 1,
|
|
295
|
+
starting: 2
|
|
296
|
+
};
|
|
297
|
+
var defaultDownloadCompletePercent = 70;
|
|
298
|
+
var defaultInstallingPartialPercent = 79;
|
|
299
|
+
var defaultStartingPartialPercent = 96;
|
|
300
|
+
function reconcilePendingInstallProgress(input) {
|
|
301
|
+
const runtimePhase = runtimeStatusToInstallUserPhase(input.runtimeStatus);
|
|
302
|
+
const progress = input.incoming ?? input.previous ?? (runtimePhase == null ? null : createProgressForRuntimePhase(runtimePhase));
|
|
303
|
+
if (progress == null) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
if (runtimePhase == null) {
|
|
307
|
+
return clearDownloadBytesUnlessDownloading(progress);
|
|
308
|
+
}
|
|
309
|
+
if (compareInstallUserPhase(progress.userPhase, runtimePhase) >= 0) {
|
|
310
|
+
return clearDownloadBytesUnlessDownloading(progress);
|
|
311
|
+
}
|
|
312
|
+
return advanceInstallProgressToPhase(progress, runtimePhase);
|
|
313
|
+
}
|
|
314
|
+
function runtimeStatusToInstallUserPhase(runtimeStatus) {
|
|
315
|
+
switch (runtimeStatus) {
|
|
316
|
+
case "preparing":
|
|
317
|
+
return "installing";
|
|
318
|
+
case "starting":
|
|
319
|
+
return "starting";
|
|
320
|
+
default:
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function compareInstallUserPhase(left, right) {
|
|
325
|
+
return installUserPhaseOrder[left] - installUserPhaseOrder[right];
|
|
326
|
+
}
|
|
327
|
+
function advanceInstallProgressToPhase(progress, targetPhase) {
|
|
328
|
+
let overallPercent = progress.overallPercent;
|
|
329
|
+
let userPhase = progress.userPhase;
|
|
330
|
+
if (targetPhase === "installing" || targetPhase === "starting") {
|
|
331
|
+
overallPercent = Math.max(overallPercent, defaultDownloadCompletePercent);
|
|
332
|
+
userPhase = "installing";
|
|
333
|
+
}
|
|
334
|
+
if (targetPhase === "starting") {
|
|
335
|
+
overallPercent = Math.max(overallPercent, defaultStartingPartialPercent);
|
|
336
|
+
userPhase = "starting";
|
|
337
|
+
} else if (targetPhase === "installing") {
|
|
338
|
+
overallPercent = Math.max(overallPercent, defaultInstallingPartialPercent);
|
|
339
|
+
}
|
|
340
|
+
return clearDownloadBytesUnlessDownloading({
|
|
341
|
+
...progress,
|
|
342
|
+
indeterminate: false,
|
|
343
|
+
overallPercent,
|
|
344
|
+
userPhase
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
function createProgressForRuntimePhase(userPhase) {
|
|
348
|
+
return advanceInstallProgressToPhase(
|
|
349
|
+
{
|
|
350
|
+
downloadedBytes: null,
|
|
351
|
+
indeterminate: false,
|
|
352
|
+
overallPercent: 0,
|
|
353
|
+
totalBytes: null,
|
|
354
|
+
userPhase: "downloading"
|
|
355
|
+
},
|
|
356
|
+
userPhase
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
function clearDownloadBytesUnlessDownloading(progress) {
|
|
360
|
+
if (progress.userPhase === "downloading") {
|
|
361
|
+
return progress;
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
...progress,
|
|
365
|
+
downloadedBytes: null,
|
|
366
|
+
totalBytes: null
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
279
370
|
// src/core/appCenterControllerState.ts
|
|
280
371
|
var WorkspaceAppCenterControllerState = class extends WorkspaceAppCenterControllerBase {
|
|
281
372
|
applySnapshot(workspaceId, snapshot) {
|
|
@@ -371,11 +462,16 @@ var WorkspaceAppCenterControllerState = class extends WorkspaceAppCenterControll
|
|
|
371
462
|
return;
|
|
372
463
|
}
|
|
373
464
|
const appIdsToClose = currentApp?.installed === true && !nextApp.installed ? [nextApp.appId] : [];
|
|
465
|
+
const mergedApp = this.mergeActiveInstallAppState(
|
|
466
|
+
workspaceId,
|
|
467
|
+
nextApp,
|
|
468
|
+
currentApp
|
|
469
|
+
);
|
|
374
470
|
const nextApps = sortWorkspaceAppCenterApps([
|
|
375
471
|
...this.store.apps.filter(
|
|
376
472
|
(candidate) => candidate.appId !== nextApp.appId
|
|
377
473
|
),
|
|
378
|
-
|
|
474
|
+
mergedApp
|
|
379
475
|
]);
|
|
380
476
|
if (areWorkspaceAppCenterAppsEqual(this.store.apps, nextApps)) {
|
|
381
477
|
return;
|
|
@@ -385,8 +481,8 @@ var WorkspaceAppCenterControllerState = class extends WorkspaceAppCenterControll
|
|
|
385
481
|
this.store.loadStatus = "ready";
|
|
386
482
|
this.bumpRevision();
|
|
387
483
|
this.settlePendingInstallReport({
|
|
388
|
-
app:
|
|
389
|
-
failureReason: options.installFailureReason ??
|
|
484
|
+
app: mergedApp,
|
|
485
|
+
failureReason: options.installFailureReason ?? mergedApp.failureReason ?? mergedApp.lastError ?? null,
|
|
390
486
|
workspaceId
|
|
391
487
|
});
|
|
392
488
|
this.closeWorkspaceAppViews(workspaceId, appIdsToClose);
|
|
@@ -495,15 +591,24 @@ var WorkspaceAppCenterControllerState = class extends WorkspaceAppCenterControll
|
|
|
495
591
|
this.bumpRevision();
|
|
496
592
|
}
|
|
497
593
|
}
|
|
498
|
-
isPendingInstallSettled(installKey, app) {
|
|
594
|
+
isPendingInstallSettled(installKey, app, previousApp) {
|
|
595
|
+
if (this.isPendingInstallAbandoned(installKey, app, previousApp)) {
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
499
598
|
if (app.runtimeStatus === "failed") {
|
|
500
599
|
return true;
|
|
501
600
|
}
|
|
601
|
+
if (this.isRuntimeInstallBusy(app.runtimeStatus)) {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
502
604
|
if (this.pendingInstallReportKeys.has(installKey)) {
|
|
503
605
|
return app.installed;
|
|
504
606
|
}
|
|
505
607
|
return app.installed && !app.updateAvailable && !app.availableVersion;
|
|
506
608
|
}
|
|
609
|
+
isRuntimeInstallBusy(runtimeStatus) {
|
|
610
|
+
return runtimeStatus === "installing" || runtimeStatus === "preparing" || runtimeStatus === "starting";
|
|
611
|
+
}
|
|
507
612
|
mergeSnapshotAppsByStateRevision(workspaceId, snapshotApps) {
|
|
508
613
|
if (this.store.workspaceId !== workspaceId) {
|
|
509
614
|
return [...snapshotApps];
|
|
@@ -520,7 +625,8 @@ var WorkspaceAppCenterControllerState = class extends WorkspaceAppCenterControll
|
|
|
520
625
|
if (this.pendingInstallKeys.has(installKey)) {
|
|
521
626
|
const pendingSettled = this.isPendingInstallSettled(
|
|
522
627
|
installKey,
|
|
523
|
-
snapshotApp
|
|
628
|
+
snapshotApp,
|
|
629
|
+
currentApp
|
|
524
630
|
);
|
|
525
631
|
if (pendingSettled && currentApp.stateRevision <= snapshotApp.stateRevision) {
|
|
526
632
|
return snapshotApp;
|
|
@@ -545,22 +651,50 @@ var WorkspaceAppCenterControllerState = class extends WorkspaceAppCenterControll
|
|
|
545
651
|
void this.refresh(workspaceId);
|
|
546
652
|
}, this.dependencies.catalogLoadingRefreshDelayMs ?? defaultCatalogLoadingRefreshDelayMs);
|
|
547
653
|
}
|
|
654
|
+
mergeActiveInstallAppState(workspaceId, app, previousApp) {
|
|
655
|
+
const installKey = appRuntimeKey(workspaceId, app.appId);
|
|
656
|
+
if (!this.pendingInstallKeys.has(installKey)) {
|
|
657
|
+
return app;
|
|
658
|
+
}
|
|
659
|
+
if (this.isPendingInstallSettled(installKey, app, previousApp)) {
|
|
660
|
+
return app;
|
|
661
|
+
}
|
|
662
|
+
const incomingRuntimeStatus = app.runtimeStatus;
|
|
663
|
+
const runtimeStatus = this.isRuntimeInstallLifecycleStatus(
|
|
664
|
+
incomingRuntimeStatus
|
|
665
|
+
) ? incomingRuntimeStatus : "installing";
|
|
666
|
+
return {
|
|
667
|
+
...app,
|
|
668
|
+
enabled: true,
|
|
669
|
+
installed: this.pendingInstallReportKeys.has(installKey) ? false : app.installed,
|
|
670
|
+
installProgress: reconcilePendingInstallProgress({
|
|
671
|
+
incoming: app.installProgress,
|
|
672
|
+
previous: previousApp?.installProgress ?? null,
|
|
673
|
+
runtimeStatus: incomingRuntimeStatus
|
|
674
|
+
}),
|
|
675
|
+
runtimeStatus
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
isRuntimeInstallLifecycleStatus(runtimeStatus) {
|
|
679
|
+
return runtimeStatus === "installing" || runtimeStatus === "preparing" || runtimeStatus === "starting";
|
|
680
|
+
}
|
|
681
|
+
isPendingInstallAbandoned(installKey, app, previousApp) {
|
|
682
|
+
return this.pendingInstallReportKeys.has(installKey) && !app.installed && app.runtimeStatus === "idle" && app.installProgress == null && this.hasObservedInstallActivity(previousApp);
|
|
683
|
+
}
|
|
684
|
+
hasObservedInstallActivity(app) {
|
|
685
|
+
return app?.installProgress != null || app?.runtimeStatus === "preparing" || app?.runtimeStatus === "starting";
|
|
686
|
+
}
|
|
548
687
|
withPendingInstallState(workspaceId, apps) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
enabled: true,
|
|
560
|
-
installed: this.pendingInstallReportKeys.has(installKey) ? false : app.installed,
|
|
561
|
-
runtimeStatus: "installing"
|
|
562
|
-
};
|
|
563
|
-
});
|
|
688
|
+
const previousAppsById = new Map(
|
|
689
|
+
this.store.apps.map((app) => [app.appId, app])
|
|
690
|
+
);
|
|
691
|
+
return apps.map(
|
|
692
|
+
(app) => this.mergeActiveInstallAppState(
|
|
693
|
+
workspaceId,
|
|
694
|
+
app,
|
|
695
|
+
previousAppsById.get(app.appId)
|
|
696
|
+
)
|
|
697
|
+
);
|
|
564
698
|
}
|
|
565
699
|
async refreshInstallState(workspaceId, appId) {
|
|
566
700
|
try {
|
|
@@ -595,6 +729,9 @@ var WorkspaceAppCenterControllerState = class extends WorkspaceAppCenterControll
|
|
|
595
729
|
this.dependencies.hooks?.onAppInstalled?.(input.app);
|
|
596
730
|
return;
|
|
597
731
|
}
|
|
732
|
+
if (input.app.runtimeStatus === "idle" && input.app.installProgress == null && input.failureReason == null && input.app.failureReason == null && input.app.lastError == null) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
598
735
|
this.dependencies.hooks?.onAppInstallFailed?.({
|
|
599
736
|
app: input.app,
|
|
600
737
|
appId: input.app.appId,
|
|
@@ -954,11 +1091,16 @@ var WorkspaceAppCenterController = class extends WorkspaceAppCenterControllerSta
|
|
|
954
1091
|
this.pendingInstallKeys.add(installKey);
|
|
955
1092
|
this.markAppInstalling(input.appId, { preserveInstalled: true });
|
|
956
1093
|
try {
|
|
1094
|
+
const restartRunning = app?.installed === true && app.runtimeStatus === "running";
|
|
957
1095
|
const snapshot = await this.dependencies.gateway.installWorkspaceApp(
|
|
958
1096
|
input.workspaceId,
|
|
959
|
-
input.appId
|
|
1097
|
+
input.appId,
|
|
1098
|
+
restartRunning ? { restartRunning: true } : void 0
|
|
960
1099
|
);
|
|
961
1100
|
this.applySnapshot(input.workspaceId, snapshot);
|
|
1101
|
+
if (restartRunning) {
|
|
1102
|
+
this.closeWorkspaceAppViews(input.workspaceId, [input.appId]);
|
|
1103
|
+
}
|
|
962
1104
|
this.dependencies.hooks?.onAppUpdated?.({
|
|
963
1105
|
app,
|
|
964
1106
|
trigger: input.trigger
|
|
@@ -1146,9 +1288,9 @@ function validateWorkspaceAppManifest(value) {
|
|
|
1146
1288
|
}
|
|
1147
1289
|
const icon = validateIcon(value.icon, issues);
|
|
1148
1290
|
const runtime = validateRuntime(value.runtime, issues);
|
|
1291
|
+
const references = validateReferences(value.references, issues);
|
|
1149
1292
|
const window = validateWindow(value.window, issues);
|
|
1150
1293
|
const author = validateAuthor(value.author, issues);
|
|
1151
|
-
const references = validateReferences(value.references, issues);
|
|
1152
1294
|
const tags = validateTags(value.tags, issues);
|
|
1153
1295
|
const localizationInfo = validateLocalizationInfo(
|
|
1154
1296
|
value.localizationInfo,
|
|
@@ -1170,9 +1312,9 @@ function validateWorkspaceAppManifest(value) {
|
|
|
1170
1312
|
description,
|
|
1171
1313
|
...icon ? { icon } : {},
|
|
1172
1314
|
runtime,
|
|
1315
|
+
...references ? { references } : {},
|
|
1173
1316
|
...window ? { window } : {},
|
|
1174
1317
|
...author ? { author } : {},
|
|
1175
|
-
...references ? { references } : {},
|
|
1176
1318
|
...tags ? { tags } : {},
|
|
1177
1319
|
...localizationInfo ? { localizationInfo } : {}
|
|
1178
1320
|
},
|
|
@@ -1239,6 +1381,40 @@ function validateRuntime(value, issues) {
|
|
|
1239
1381
|
healthcheckPath
|
|
1240
1382
|
};
|
|
1241
1383
|
}
|
|
1384
|
+
function validateReferences(value, issues) {
|
|
1385
|
+
if (value === void 0) {
|
|
1386
|
+
return void 0;
|
|
1387
|
+
}
|
|
1388
|
+
if (!isRecord(value)) {
|
|
1389
|
+
issues.push({
|
|
1390
|
+
code: "manifest.references",
|
|
1391
|
+
message: "references must be an object when provided.",
|
|
1392
|
+
path: "$.references"
|
|
1393
|
+
});
|
|
1394
|
+
return void 0;
|
|
1395
|
+
}
|
|
1396
|
+
const unsupportedKey = Object.keys(value).find(
|
|
1397
|
+
(key) => key !== "listEndpoint"
|
|
1398
|
+
);
|
|
1399
|
+
if (unsupportedKey) {
|
|
1400
|
+
issues.push({
|
|
1401
|
+
code: "manifest.references",
|
|
1402
|
+
message: `references.${unsupportedKey} is unsupported.`,
|
|
1403
|
+
path: `$.references.${unsupportedKey}`
|
|
1404
|
+
});
|
|
1405
|
+
return void 0;
|
|
1406
|
+
}
|
|
1407
|
+
const listEndpoint = readOptionalString(value.listEndpoint);
|
|
1408
|
+
if (!listEndpoint || !isRelativeUrlPath(listEndpoint)) {
|
|
1409
|
+
issues.push({
|
|
1410
|
+
code: "manifest.references",
|
|
1411
|
+
message: "references.listEndpoint must be a relative URL path without query or fragment.",
|
|
1412
|
+
path: "$.references.listEndpoint"
|
|
1413
|
+
});
|
|
1414
|
+
return void 0;
|
|
1415
|
+
}
|
|
1416
|
+
return { listEndpoint };
|
|
1417
|
+
}
|
|
1242
1418
|
function validateWindow(value, issues) {
|
|
1243
1419
|
if (value === void 0) {
|
|
1244
1420
|
return void 0;
|
|
@@ -1324,29 +1500,6 @@ function validateAuthor(value, issues) {
|
|
|
1324
1500
|
...url ? { url } : {}
|
|
1325
1501
|
};
|
|
1326
1502
|
}
|
|
1327
|
-
function validateReferences(value, issues) {
|
|
1328
|
-
if (value === void 0) {
|
|
1329
|
-
return void 0;
|
|
1330
|
-
}
|
|
1331
|
-
if (!isRecord(value)) {
|
|
1332
|
-
issues.push({
|
|
1333
|
-
code: "manifest.references",
|
|
1334
|
-
message: "references must be an object when provided.",
|
|
1335
|
-
path: "$.references"
|
|
1336
|
-
});
|
|
1337
|
-
return void 0;
|
|
1338
|
-
}
|
|
1339
|
-
const searchEndpoint = readOptionalString(value.searchEndpoint);
|
|
1340
|
-
if (!searchEndpoint || !isRelativeUrlPath(searchEndpoint)) {
|
|
1341
|
-
issues.push({
|
|
1342
|
-
code: "manifest.references",
|
|
1343
|
-
message: "references.searchEndpoint must be a relative URL path without query or hash.",
|
|
1344
|
-
path: "$.references.searchEndpoint"
|
|
1345
|
-
});
|
|
1346
|
-
return void 0;
|
|
1347
|
-
}
|
|
1348
|
-
return { searchEndpoint };
|
|
1349
|
-
}
|
|
1350
1503
|
function validateTags(value, issues) {
|
|
1351
1504
|
if (value === void 0) {
|
|
1352
1505
|
return void 0;
|
|
@@ -1452,11 +1605,15 @@ function isRelativePackagePath(value) {
|
|
|
1452
1605
|
return !value.startsWith("/") && !value.split(/[\\/]/u).includes("..");
|
|
1453
1606
|
}
|
|
1454
1607
|
function isRelativeUrlPath(value) {
|
|
1455
|
-
|
|
1456
|
-
|
|
1608
|
+
if (!value || !value.startsWith("/") || value.startsWith("//") || value.includes("\0")) {
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
try {
|
|
1612
|
+
const parsed = new URL(value, "http://tutti.local");
|
|
1613
|
+
return parsed.origin === "http://tutti.local" && parsed.pathname === value && parsed.search === "" && parsed.hash === "";
|
|
1614
|
+
} catch {
|
|
1457
1615
|
return false;
|
|
1458
1616
|
}
|
|
1459
|
-
return true;
|
|
1460
1617
|
}
|
|
1461
1618
|
function isRecord(value) {
|
|
1462
1619
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1585,9 +1742,11 @@ function createAppCenterViewModel({
|
|
|
1585
1742
|
manifest: app.manifest
|
|
1586
1743
|
});
|
|
1587
1744
|
const factoryAgentSessionId = factoryJob?.agentSessionId?.trim() || null;
|
|
1588
|
-
const
|
|
1745
|
+
const factoryEditAction = factoryJob && factoryAgentSessionId ? factoryJob.status === "published" ? "prepare_modification" : "open_session" : null;
|
|
1746
|
+
const status = resolveRuntimeStatusForViewModel(runtime);
|
|
1589
1747
|
const presentation = resolveWorkspaceAppStatusPresentation(status);
|
|
1590
1748
|
const installed = Boolean(app.install);
|
|
1749
|
+
const installBusy = runtime?.installProgress != null || status === "installing";
|
|
1591
1750
|
const sourceKind = resolveCatalogSourceKind(app.catalog);
|
|
1592
1751
|
const localApp = sourceKind === "local";
|
|
1593
1752
|
const runtimeId = normalizeOptionalString(runtime?.runtimeId);
|
|
@@ -1609,6 +1768,7 @@ function createAppCenterViewModel({
|
|
|
1609
1768
|
installed,
|
|
1610
1769
|
status
|
|
1611
1770
|
});
|
|
1771
|
+
const iconUrl = resolveWorkspaceAppIconUrl(app);
|
|
1612
1772
|
return {
|
|
1613
1773
|
id: app.manifest.appId,
|
|
1614
1774
|
installationId,
|
|
@@ -1621,30 +1781,37 @@ function createAppCenterViewModel({
|
|
|
1621
1781
|
...app.availableVersion && !comingSoon ? { availableVersion: app.availableVersion } : {},
|
|
1622
1782
|
...app.category?.trim() ? { category: app.category.trim() } : {},
|
|
1623
1783
|
updateAvailable: app.updateAvailable ?? false,
|
|
1624
|
-
...
|
|
1784
|
+
...iconUrl ? {
|
|
1785
|
+
icon: {
|
|
1786
|
+
type: "asset",
|
|
1787
|
+
src: iconUrl
|
|
1788
|
+
}
|
|
1789
|
+
} : {},
|
|
1625
1790
|
tags: metadata.tags,
|
|
1626
1791
|
installed,
|
|
1627
1792
|
status,
|
|
1628
1793
|
statusLabelKey: comingSoon ? "status.comingSoon" : resolvePrimaryActionLabelKey(primaryAction, presentation.labelKey),
|
|
1629
1794
|
statusTone: presentation.tone,
|
|
1630
|
-
statusPulse: presentation.pulse,
|
|
1795
|
+
statusPulse: runtime?.installProgress ? false : presentation.pulse,
|
|
1631
1796
|
primaryAction,
|
|
1632
1797
|
sourceKind,
|
|
1633
1798
|
canOpen,
|
|
1634
1799
|
canExport: localApp,
|
|
1635
1800
|
canDelete: localApp,
|
|
1636
1801
|
canReplaceIcon: replaceableIconAppIdSet.has(app.manifest.appId),
|
|
1637
|
-
canOpenFolder: installed,
|
|
1638
|
-
canOpenPackageFolder: installed && localApp && Boolean(displayVersion),
|
|
1802
|
+
canOpenFolder: installed && !installBusy,
|
|
1803
|
+
canOpenPackageFolder: installed && localApp && Boolean(displayVersion) && !installBusy,
|
|
1639
1804
|
canOpenFactorySession: Boolean(factoryAgentSessionId),
|
|
1640
1805
|
canPublishFactoryUpdate: installed && factoryJob?.status === "ready" && Boolean(factoryJob.publishedVersion?.trim()),
|
|
1641
|
-
canUninstall: installed,
|
|
1806
|
+
canUninstall: installed && !installBusy,
|
|
1642
1807
|
canRetry,
|
|
1643
1808
|
canUpdate,
|
|
1644
1809
|
...factoryAgentSessionId ? { factoryAgentSessionId } : {},
|
|
1810
|
+
...factoryEditAction ? { factoryEditAction } : {},
|
|
1645
1811
|
...factoryJob?.jobId ? { factoryJobId: factoryJob.jobId } : {},
|
|
1646
1812
|
...factoryJob?.provider ? { factoryProvider: factoryJob.provider } : {},
|
|
1647
|
-
...runtime?.error?.message ? { errorMessage: runtime.error.message } : {}
|
|
1813
|
+
...runtime?.error?.message ? { errorMessage: runtime.error.message } : {},
|
|
1814
|
+
...runtime?.installProgress ? { installProgress: runtime.installProgress } : {}
|
|
1648
1815
|
};
|
|
1649
1816
|
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
1650
1817
|
return {
|
|
@@ -1662,7 +1829,8 @@ function createRuntimeStateMaps(runtimeStates) {
|
|
|
1662
1829
|
for (const state of runtimeStates) {
|
|
1663
1830
|
const runtime = {
|
|
1664
1831
|
...state,
|
|
1665
|
-
status: mapWorkspaceAppRuntimeStatus(state.status)
|
|
1832
|
+
status: mapWorkspaceAppRuntimeStatus(state.status),
|
|
1833
|
+
installProgress: state.installProgress ?? null
|
|
1666
1834
|
};
|
|
1667
1835
|
const installationId = normalizeOptionalString(runtime.installationId);
|
|
1668
1836
|
if (installationId) {
|
|
@@ -1688,6 +1856,15 @@ function findRuntimeStateForApp(maps, appId, installationId) {
|
|
|
1688
1856
|
}
|
|
1689
1857
|
return maps.fallbackByAppId.get(appId);
|
|
1690
1858
|
}
|
|
1859
|
+
function resolveRuntimeStatusForViewModel(runtime) {
|
|
1860
|
+
if (!runtime) {
|
|
1861
|
+
return "idle";
|
|
1862
|
+
}
|
|
1863
|
+
if (runtime.installProgress != null && runtime.status === "idle") {
|
|
1864
|
+
return "installing";
|
|
1865
|
+
}
|
|
1866
|
+
return runtime.status;
|
|
1867
|
+
}
|
|
1691
1868
|
function resolveCatalogSourceKind(catalog) {
|
|
1692
1869
|
return catalog?.source?.kind ?? "bundled";
|
|
1693
1870
|
}
|
|
@@ -1734,6 +1911,16 @@ function normalizeOptionalString(value) {
|
|
|
1734
1911
|
const normalized = value?.trim() ?? "";
|
|
1735
1912
|
return normalized.length > 0 ? normalized : null;
|
|
1736
1913
|
}
|
|
1914
|
+
function resolveWorkspaceAppIconUrl(app) {
|
|
1915
|
+
const manifestIconUrl = normalizeOptionalString(app.manifest.icon?.src);
|
|
1916
|
+
if (manifestIconUrl && isResolvedIconUrl(manifestIconUrl)) {
|
|
1917
|
+
return manifestIconUrl;
|
|
1918
|
+
}
|
|
1919
|
+
return normalizeOptionalString(app.availableIconUrl) ?? manifestIconUrl;
|
|
1920
|
+
}
|
|
1921
|
+
function isResolvedIconUrl(value) {
|
|
1922
|
+
return /^[a-z][a-z0-9+.-]*:/iu.test(value) || value.startsWith("/");
|
|
1923
|
+
}
|
|
1737
1924
|
function resolvePrimaryAction(input) {
|
|
1738
1925
|
if (input.comingSoon) {
|
|
1739
1926
|
return "none";
|
|
@@ -1819,7 +2006,7 @@ function createFactoryJobViewModel(job) {
|
|
|
1819
2006
|
canFix: canFixValidationFailure,
|
|
1820
2007
|
canOpenAgentSession: Boolean(agentSessionId),
|
|
1821
2008
|
canPublish: job.status === "ready",
|
|
1822
|
-
canRetryValidation:
|
|
2009
|
+
canRetryValidation: false,
|
|
1823
2010
|
failureReason: job.failureReason,
|
|
1824
2011
|
updatedAtUnixMs: job.updatedAtUnixMs
|
|
1825
2012
|
};
|
|
@@ -1854,4 +2041,4 @@ export {
|
|
|
1854
2041
|
resolveWorkspaceAppCatalogMetadata,
|
|
1855
2042
|
createWorkspaceAppRecord
|
|
1856
2043
|
};
|
|
1857
|
-
//# sourceMappingURL=chunk-
|
|
2044
|
+
//# sourceMappingURL=chunk-DPJRK63W.js.map
|