@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.
- package/LICENSE +202 -0
- package/README.md +5 -0
- package/dist/chunk-3Z2WIHMP.js +7 -0
- package/dist/chunk-3Z2WIHMP.js.map +1 -0
- package/dist/chunk-FENSLRNH.js +690 -0
- package/dist/chunk-FENSLRNH.js.map +1 -0
- package/dist/chunk-LBSZLLZK.js +94 -0
- package/dist/chunk-LBSZLLZK.js.map +1 -0
- package/dist/chunk-VLUZD2IT.js +15 -0
- package/dist/chunk-VLUZD2IT.js.map +1 -0
- package/dist/contracts/index.d.ts +158 -0
- package/dist/contracts/index.js +11 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.js +42 -0
- package/dist/core/index.js.map +1 -0
- package/dist/i18n/index.d.ts +18 -0
- package/dist/i18n/index.js +574 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index-Bert0Egy.d.ts +71 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/ui/index.d.ts +88 -0
- package/dist/ui/index.js +1619 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
import {
|
|
2
|
+
workspaceAppManifestSchemaVersion
|
|
3
|
+
} from "./chunk-3Z2WIHMP.js";
|
|
4
|
+
|
|
5
|
+
// src/core/appIdentity.ts
|
|
6
|
+
var workspaceAppIdPattern = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
|
|
7
|
+
function isWorkspaceAppId(value) {
|
|
8
|
+
return workspaceAppIdPattern.test(value);
|
|
9
|
+
}
|
|
10
|
+
function normalizeWorkspaceAppId(value) {
|
|
11
|
+
return value.trim().toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
function createWorkspaceAppIdentity(input) {
|
|
14
|
+
return normalizeWorkspaceAppId(input.id);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/core/manifestValidation.ts
|
|
18
|
+
var appWindowMinWidth = 280;
|
|
19
|
+
var appWindowMinHeight = 160;
|
|
20
|
+
var appWindowMaxWidth = 1600;
|
|
21
|
+
var appWindowMaxHeight = 1200;
|
|
22
|
+
function validateWorkspaceAppManifest(value) {
|
|
23
|
+
const issues = [];
|
|
24
|
+
if (!isRecord(value)) {
|
|
25
|
+
return {
|
|
26
|
+
issues: [
|
|
27
|
+
{
|
|
28
|
+
code: "manifest.notObject",
|
|
29
|
+
message: "Manifest must be an object.",
|
|
30
|
+
path: "$"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
valid: false
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (value.schemaVersion !== workspaceAppManifestSchemaVersion) {
|
|
37
|
+
issues.push({
|
|
38
|
+
code: "manifest.schemaVersion",
|
|
39
|
+
message: `schemaVersion must be ${workspaceAppManifestSchemaVersion}.`,
|
|
40
|
+
path: "$.schemaVersion"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const appId = readOptionalString(value.appId);
|
|
44
|
+
if (!appId || !isWorkspaceAppId(normalizeWorkspaceAppId(appId))) {
|
|
45
|
+
issues.push({
|
|
46
|
+
code: "manifest.appId",
|
|
47
|
+
message: "appId must contain lowercase letters, numbers, dots, hyphens, or underscores.",
|
|
48
|
+
path: "$.appId"
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const name = readOptionalString(value.name);
|
|
52
|
+
if (!name) {
|
|
53
|
+
issues.push({
|
|
54
|
+
code: "manifest.name",
|
|
55
|
+
message: "name is required.",
|
|
56
|
+
path: "$.name"
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const version = readOptionalString(value.version);
|
|
60
|
+
if (!version) {
|
|
61
|
+
issues.push({
|
|
62
|
+
code: "manifest.version",
|
|
63
|
+
message: "version is required.",
|
|
64
|
+
path: "$.version"
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const description = readOptionalString(value.description);
|
|
68
|
+
if (!description) {
|
|
69
|
+
issues.push({
|
|
70
|
+
code: "manifest.description",
|
|
71
|
+
message: "description is required.",
|
|
72
|
+
path: "$.description"
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const icon = validateIcon(value.icon, issues);
|
|
76
|
+
const runtime = validateRuntime(value.runtime, issues);
|
|
77
|
+
const window = validateWindow(value.window, issues);
|
|
78
|
+
const author = validateAuthor(value.author, issues);
|
|
79
|
+
const tags = validateTags(value.tags, issues);
|
|
80
|
+
const localizationInfo = validateLocalizationInfo(
|
|
81
|
+
value.localizationInfo,
|
|
82
|
+
issues
|
|
83
|
+
);
|
|
84
|
+
if (issues.length > 0 || !appId || !name || !version || !description || !runtime) {
|
|
85
|
+
return {
|
|
86
|
+
issues,
|
|
87
|
+
valid: false
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
issues,
|
|
92
|
+
manifest: {
|
|
93
|
+
schemaVersion: workspaceAppManifestSchemaVersion,
|
|
94
|
+
appId: normalizeWorkspaceAppId(appId),
|
|
95
|
+
name,
|
|
96
|
+
version,
|
|
97
|
+
description,
|
|
98
|
+
...icon ? { icon } : {},
|
|
99
|
+
runtime,
|
|
100
|
+
...window ? { window } : {},
|
|
101
|
+
...author ? { author } : {},
|
|
102
|
+
...tags ? { tags } : {},
|
|
103
|
+
...localizationInfo ? { localizationInfo } : {}
|
|
104
|
+
},
|
|
105
|
+
valid: true
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function validateIcon(value, issues) {
|
|
109
|
+
if (value === void 0) {
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
if (!isRecord(value)) {
|
|
113
|
+
issues.push({
|
|
114
|
+
code: "manifest.icon",
|
|
115
|
+
message: "icon must be an object when provided.",
|
|
116
|
+
path: "$.icon"
|
|
117
|
+
});
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
const type = value.type;
|
|
121
|
+
const src = readOptionalString(value.src);
|
|
122
|
+
if (type !== "asset" || !src || !isRelativePackagePath(src)) {
|
|
123
|
+
issues.push({
|
|
124
|
+
code: "manifest.icon",
|
|
125
|
+
message: "icon must include type=asset and a relative src.",
|
|
126
|
+
path: "$.icon"
|
|
127
|
+
});
|
|
128
|
+
return void 0;
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
type,
|
|
132
|
+
src
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function validateRuntime(value, issues) {
|
|
136
|
+
if (!isRecord(value)) {
|
|
137
|
+
issues.push({
|
|
138
|
+
code: "manifest.runtime",
|
|
139
|
+
message: "runtime is required.",
|
|
140
|
+
path: "$.runtime"
|
|
141
|
+
});
|
|
142
|
+
return void 0;
|
|
143
|
+
}
|
|
144
|
+
const bootstrap = readOptionalString(value.bootstrap);
|
|
145
|
+
const healthcheckPath = readOptionalString(value.healthcheckPath);
|
|
146
|
+
if (!bootstrap || !isRelativePackagePath(bootstrap)) {
|
|
147
|
+
issues.push({
|
|
148
|
+
code: "manifest.runtime",
|
|
149
|
+
message: "runtime.bootstrap must be a relative package path.",
|
|
150
|
+
path: "$.runtime.bootstrap"
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (!healthcheckPath || !healthcheckPath.startsWith("/")) {
|
|
154
|
+
issues.push({
|
|
155
|
+
code: "manifest.runtime",
|
|
156
|
+
message: "runtime.healthcheckPath must start with /.",
|
|
157
|
+
path: "$.runtime.healthcheckPath"
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (!bootstrap || !healthcheckPath || !isRelativePackagePath(bootstrap) || !healthcheckPath.startsWith("/")) {
|
|
161
|
+
return void 0;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
bootstrap,
|
|
165
|
+
healthcheckPath
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function validateWindow(value, issues) {
|
|
169
|
+
if (value === void 0) {
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
if (!isRecord(value)) {
|
|
173
|
+
issues.push({
|
|
174
|
+
code: "manifest.window",
|
|
175
|
+
message: "window must be an object when provided.",
|
|
176
|
+
path: "$.window"
|
|
177
|
+
});
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
const minimizeBehavior = value.minimizeBehavior;
|
|
181
|
+
if (minimizeBehavior !== void 0 && minimizeBehavior !== "keep-mounted" && minimizeBehavior !== "hibernate") {
|
|
182
|
+
issues.push({
|
|
183
|
+
code: "manifest.window",
|
|
184
|
+
message: "window.minimizeBehavior must be keep-mounted or hibernate when provided.",
|
|
185
|
+
path: "$.window.minimizeBehavior"
|
|
186
|
+
});
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
const minWidth = validateWindowSize(
|
|
190
|
+
value.minWidth,
|
|
191
|
+
"minWidth",
|
|
192
|
+
appWindowMinWidth,
|
|
193
|
+
appWindowMaxWidth,
|
|
194
|
+
issues
|
|
195
|
+
);
|
|
196
|
+
const minHeight = validateWindowSize(
|
|
197
|
+
value.minHeight,
|
|
198
|
+
"minHeight",
|
|
199
|
+
appWindowMinHeight,
|
|
200
|
+
appWindowMaxHeight,
|
|
201
|
+
issues
|
|
202
|
+
);
|
|
203
|
+
if (minWidth === null || minHeight === null) {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
...minimizeBehavior ? { minimizeBehavior } : {},
|
|
208
|
+
...minHeight === void 0 ? {} : { minHeight },
|
|
209
|
+
...minWidth === void 0 ? {} : { minWidth }
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function validateWindowSize(value, field, minimum, maximum, issues) {
|
|
213
|
+
if (value === void 0) {
|
|
214
|
+
return void 0;
|
|
215
|
+
}
|
|
216
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) {
|
|
217
|
+
issues.push({
|
|
218
|
+
code: "manifest.window",
|
|
219
|
+
message: `window.${field} must be an integer between ${minimum} and ${maximum} when provided.`,
|
|
220
|
+
path: `$.window.${field}`
|
|
221
|
+
});
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
function validateAuthor(value, issues) {
|
|
227
|
+
if (value === void 0) {
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
if (!isRecord(value)) {
|
|
231
|
+
issues.push({
|
|
232
|
+
code: "manifest.author",
|
|
233
|
+
message: "author must be an object when provided.",
|
|
234
|
+
path: "$.author"
|
|
235
|
+
});
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
const name = readOptionalString(value.name);
|
|
239
|
+
const url = readOptionalString(value.url);
|
|
240
|
+
if (!name || value.url !== void 0 && !url) {
|
|
241
|
+
issues.push({
|
|
242
|
+
code: "manifest.author",
|
|
243
|
+
message: "author must include name and an optional non-empty url.",
|
|
244
|
+
path: "$.author"
|
|
245
|
+
});
|
|
246
|
+
return void 0;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
name,
|
|
250
|
+
...url ? { url } : {}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function validateTags(value, issues) {
|
|
254
|
+
if (value === void 0) {
|
|
255
|
+
return void 0;
|
|
256
|
+
}
|
|
257
|
+
if (!Array.isArray(value)) {
|
|
258
|
+
issues.push({
|
|
259
|
+
code: "manifest.tags",
|
|
260
|
+
message: "tags must be an array when provided.",
|
|
261
|
+
path: "$.tags"
|
|
262
|
+
});
|
|
263
|
+
return void 0;
|
|
264
|
+
}
|
|
265
|
+
const tags = value.map((tag) => readOptionalString(tag)).filter((tag) => Boolean(tag));
|
|
266
|
+
if (tags.length !== value.length) {
|
|
267
|
+
issues.push({
|
|
268
|
+
code: "manifest.tags",
|
|
269
|
+
message: "tags must only contain non-empty strings.",
|
|
270
|
+
path: "$.tags"
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return Array.from(new Set(tags));
|
|
274
|
+
}
|
|
275
|
+
function validateLocalizationInfo(value, issues) {
|
|
276
|
+
if (value === void 0) {
|
|
277
|
+
return void 0;
|
|
278
|
+
}
|
|
279
|
+
if (!isRecord(value)) {
|
|
280
|
+
issues.push({
|
|
281
|
+
code: "manifest.localizationInfo",
|
|
282
|
+
message: "localizationInfo must be an object when provided.",
|
|
283
|
+
path: "$.localizationInfo"
|
|
284
|
+
});
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
const defaultLocale = readOptionalString(value.defaultLocale);
|
|
288
|
+
if (!defaultLocale) {
|
|
289
|
+
issues.push({
|
|
290
|
+
code: "manifest.localizationInfo",
|
|
291
|
+
message: "localizationInfo.defaultLocale is required.",
|
|
292
|
+
path: "$.localizationInfo.defaultLocale"
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const additionalLocalesValue = value.additionalLocales;
|
|
296
|
+
if (additionalLocalesValue !== void 0 && !Array.isArray(additionalLocalesValue)) {
|
|
297
|
+
issues.push({
|
|
298
|
+
code: "manifest.localizationInfo",
|
|
299
|
+
message: "localizationInfo.additionalLocales must be an array.",
|
|
300
|
+
path: "$.localizationInfo.additionalLocales"
|
|
301
|
+
});
|
|
302
|
+
return void 0;
|
|
303
|
+
}
|
|
304
|
+
const seenLocales = new Set(
|
|
305
|
+
defaultLocale ? [defaultLocale.toLowerCase()] : []
|
|
306
|
+
);
|
|
307
|
+
const additionalLocales = [];
|
|
308
|
+
for (const [index, entry] of (additionalLocalesValue ?? []).entries()) {
|
|
309
|
+
if (!isRecord(entry)) {
|
|
310
|
+
issues.push({
|
|
311
|
+
code: "manifest.localizationInfo",
|
|
312
|
+
message: "localizationInfo.additionalLocales entries must be objects.",
|
|
313
|
+
path: `$.localizationInfo.additionalLocales[${index}]`
|
|
314
|
+
});
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const locale = readOptionalString(entry.locale);
|
|
318
|
+
const file = readOptionalString(entry.file);
|
|
319
|
+
if (!locale || !file || !isRelativePackagePath(file)) {
|
|
320
|
+
issues.push({
|
|
321
|
+
code: "manifest.localizationInfo",
|
|
322
|
+
message: "localizationInfo.additionalLocales entries must include locale and a relative file path.",
|
|
323
|
+
path: `$.localizationInfo.additionalLocales[${index}]`
|
|
324
|
+
});
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const localeKey = locale.toLowerCase();
|
|
328
|
+
if (seenLocales.has(localeKey)) {
|
|
329
|
+
issues.push({
|
|
330
|
+
code: "manifest.localizationInfo",
|
|
331
|
+
message: "localizationInfo locales must be unique.",
|
|
332
|
+
path: `$.localizationInfo.additionalLocales[${index}].locale`
|
|
333
|
+
});
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
seenLocales.add(localeKey);
|
|
337
|
+
additionalLocales.push({ locale, file });
|
|
338
|
+
}
|
|
339
|
+
if (!defaultLocale || issues.some((issue) => issue.code === "manifest.localizationInfo")) {
|
|
340
|
+
return void 0;
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
defaultLocale,
|
|
344
|
+
...additionalLocales.length > 0 ? { additionalLocales } : {}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function readOptionalString(value) {
|
|
348
|
+
if (typeof value !== "string") {
|
|
349
|
+
return void 0;
|
|
350
|
+
}
|
|
351
|
+
const trimmed = value.trim();
|
|
352
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
353
|
+
}
|
|
354
|
+
function isRelativePackagePath(value) {
|
|
355
|
+
return !value.startsWith("/") && !value.split(/[\\/]/u).includes("..");
|
|
356
|
+
}
|
|
357
|
+
function isRecord(value) {
|
|
358
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/core/statusMapping.ts
|
|
362
|
+
var statusAliases = /* @__PURE__ */ new Map([
|
|
363
|
+
["active", "running"],
|
|
364
|
+
["created", "idle"],
|
|
365
|
+
["crashed", "failed"],
|
|
366
|
+
["error", "failed"],
|
|
367
|
+
["failed", "failed"],
|
|
368
|
+
["idle", "idle"],
|
|
369
|
+
["installed", "idle"],
|
|
370
|
+
["installing", "installing"],
|
|
371
|
+
["downloading_runtime", "preparing"],
|
|
372
|
+
["launching", "starting"],
|
|
373
|
+
["pending", "starting"],
|
|
374
|
+
["preparing", "preparing"],
|
|
375
|
+
["ready", "idle"],
|
|
376
|
+
["running", "running"],
|
|
377
|
+
["started", "running"],
|
|
378
|
+
["starting", "starting"],
|
|
379
|
+
["stopped", "idle"],
|
|
380
|
+
["stopping", "stopping"],
|
|
381
|
+
["terminated", "idle"],
|
|
382
|
+
["terminating", "stopping"]
|
|
383
|
+
]);
|
|
384
|
+
function mapWorkspaceAppRuntimeStatus(value) {
|
|
385
|
+
if (typeof value !== "string") {
|
|
386
|
+
return "idle";
|
|
387
|
+
}
|
|
388
|
+
return statusAliases.get(value.trim().toLowerCase()) ?? "idle";
|
|
389
|
+
}
|
|
390
|
+
function normalizeWorkspaceAppRuntimeState(state) {
|
|
391
|
+
return {
|
|
392
|
+
...state,
|
|
393
|
+
status: mapWorkspaceAppRuntimeStatus(state.status)
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function resolveWorkspaceAppStatusPresentation(status) {
|
|
397
|
+
switch (status) {
|
|
398
|
+
case "installing":
|
|
399
|
+
return {
|
|
400
|
+
labelKey: "status.installing",
|
|
401
|
+
pulse: true,
|
|
402
|
+
tone: "blue"
|
|
403
|
+
};
|
|
404
|
+
case "preparing":
|
|
405
|
+
return {
|
|
406
|
+
labelKey: "status.preparing",
|
|
407
|
+
pulse: true,
|
|
408
|
+
tone: "blue"
|
|
409
|
+
};
|
|
410
|
+
case "starting":
|
|
411
|
+
return {
|
|
412
|
+
labelKey: "status.starting",
|
|
413
|
+
pulse: true,
|
|
414
|
+
tone: "blue"
|
|
415
|
+
};
|
|
416
|
+
case "running":
|
|
417
|
+
return {
|
|
418
|
+
labelKey: "status.running",
|
|
419
|
+
pulse: false,
|
|
420
|
+
tone: "green"
|
|
421
|
+
};
|
|
422
|
+
case "failed":
|
|
423
|
+
return {
|
|
424
|
+
labelKey: "status.failed",
|
|
425
|
+
pulse: false,
|
|
426
|
+
tone: "red"
|
|
427
|
+
};
|
|
428
|
+
case "stopping":
|
|
429
|
+
return {
|
|
430
|
+
labelKey: "status.stopping",
|
|
431
|
+
pulse: true,
|
|
432
|
+
tone: "amber"
|
|
433
|
+
};
|
|
434
|
+
case "idle":
|
|
435
|
+
return {
|
|
436
|
+
labelKey: "actions.openApp",
|
|
437
|
+
pulse: false,
|
|
438
|
+
tone: "neutral"
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/core/appCenterViewModel.ts
|
|
444
|
+
function createAppCenterViewModel({
|
|
445
|
+
apps,
|
|
446
|
+
factoryJobs = [],
|
|
447
|
+
locale = null,
|
|
448
|
+
replaceableIconAppIds = [],
|
|
449
|
+
runtimeStates = []
|
|
450
|
+
}) {
|
|
451
|
+
const runtimeByAppId = new Map(
|
|
452
|
+
runtimeStates.map((state) => [
|
|
453
|
+
state.appId,
|
|
454
|
+
{
|
|
455
|
+
...state,
|
|
456
|
+
status: mapWorkspaceAppRuntimeStatus(state.status)
|
|
457
|
+
}
|
|
458
|
+
])
|
|
459
|
+
);
|
|
460
|
+
const appFactoryJobByAppId = createAppFactoryJobByAppId(factoryJobs);
|
|
461
|
+
const replaceableIconAppIdSet = new Set(
|
|
462
|
+
replaceableIconAppIds.map((appId) => appId.trim()).filter(Boolean)
|
|
463
|
+
);
|
|
464
|
+
const appCards = apps.map((app) => {
|
|
465
|
+
const runtime = runtimeByAppId.get(app.manifest.appId);
|
|
466
|
+
const factoryJob = appFactoryJobByAppId.get(app.manifest.appId);
|
|
467
|
+
const metadata = resolveWorkspaceAppCatalogMetadata({
|
|
468
|
+
catalog: app.catalog,
|
|
469
|
+
locale,
|
|
470
|
+
manifest: app.manifest
|
|
471
|
+
});
|
|
472
|
+
const factoryAgentSessionId = factoryJob?.agentSessionId?.trim() || null;
|
|
473
|
+
const status = runtime?.status ?? "idle";
|
|
474
|
+
const presentation = resolveWorkspaceAppStatusPresentation(status);
|
|
475
|
+
const installed = Boolean(app.install);
|
|
476
|
+
const sourceKind = resolveCatalogSourceKind(app.catalog);
|
|
477
|
+
const localApp = sourceKind === "local";
|
|
478
|
+
const comingSoon = isComingSoonApp(metadata.tags) || isComingSoonApp(app.manifest.tags ?? []);
|
|
479
|
+
const canUpdate = installed && (app.updateAvailable ?? false);
|
|
480
|
+
const canOpen = !comingSoon && installed && canOpenInstalledApp(status);
|
|
481
|
+
const canRetry = installed && status === "failed";
|
|
482
|
+
const primaryAction = resolvePrimaryAction({
|
|
483
|
+
canOpen,
|
|
484
|
+
canRetry,
|
|
485
|
+
canUpdate,
|
|
486
|
+
comingSoon,
|
|
487
|
+
installed,
|
|
488
|
+
status
|
|
489
|
+
});
|
|
490
|
+
return {
|
|
491
|
+
id: app.manifest.appId,
|
|
492
|
+
name: metadata.name,
|
|
493
|
+
createdAtUnixMs: app.createdAtUnixMs ?? null,
|
|
494
|
+
...metadata.description ? { description: metadata.description } : {},
|
|
495
|
+
...app.manifest.version && !comingSoon ? { version: app.manifest.version } : {},
|
|
496
|
+
...app.availableVersion && !comingSoon ? { availableVersion: app.availableVersion } : {},
|
|
497
|
+
...app.category?.trim() ? { category: app.category.trim() } : {},
|
|
498
|
+
updateAvailable: app.updateAvailable ?? false,
|
|
499
|
+
...app.manifest.icon ? { icon: app.manifest.icon } : {},
|
|
500
|
+
tags: metadata.tags,
|
|
501
|
+
installed,
|
|
502
|
+
status,
|
|
503
|
+
statusLabelKey: comingSoon ? "status.comingSoon" : resolvePrimaryActionLabelKey(primaryAction, presentation.labelKey),
|
|
504
|
+
statusTone: presentation.tone,
|
|
505
|
+
statusPulse: presentation.pulse,
|
|
506
|
+
primaryAction,
|
|
507
|
+
sourceKind,
|
|
508
|
+
canOpen,
|
|
509
|
+
canExport: localApp,
|
|
510
|
+
canDelete: localApp,
|
|
511
|
+
canReplaceIcon: replaceableIconAppIdSet.has(app.manifest.appId),
|
|
512
|
+
canOpenFolder: installed,
|
|
513
|
+
canOpenPackageFolder: installed && localApp && Boolean(app.manifest.version?.trim()),
|
|
514
|
+
canOpenFactorySession: Boolean(factoryAgentSessionId),
|
|
515
|
+
canPublishFactoryUpdate: installed && factoryJob?.status === "ready" && Boolean(factoryJob.publishedVersion?.trim()),
|
|
516
|
+
canUninstall: installed,
|
|
517
|
+
canRetry,
|
|
518
|
+
canUpdate,
|
|
519
|
+
...factoryAgentSessionId ? { factoryAgentSessionId } : {},
|
|
520
|
+
...factoryJob?.jobId ? { factoryJobId: factoryJob.jobId } : {},
|
|
521
|
+
...factoryJob?.provider ? { factoryProvider: factoryJob.provider } : {},
|
|
522
|
+
...runtime?.error?.message ? { errorMessage: runtime.error.message } : {}
|
|
523
|
+
};
|
|
524
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
525
|
+
return {
|
|
526
|
+
apps: appCards,
|
|
527
|
+
factoryJobs: factoryJobs.filter((job) => !isPublishedAppFactoryJob(job)).map(createFactoryJobViewModel),
|
|
528
|
+
empty: appCards.length === 0,
|
|
529
|
+
failedCount: appCards.filter((app) => app.status === "failed").length,
|
|
530
|
+
installedCount: appCards.filter((app) => app.installed).length,
|
|
531
|
+
runningCount: appCards.filter((app) => app.status === "running").length
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function resolveCatalogSourceKind(catalog) {
|
|
535
|
+
return catalog?.source?.kind ?? "bundled";
|
|
536
|
+
}
|
|
537
|
+
function resolveWorkspaceAppCatalogMetadata(input) {
|
|
538
|
+
const manifest = input.manifest;
|
|
539
|
+
const localization = findWorkspaceAppCatalogLocalization(
|
|
540
|
+
input.catalog,
|
|
541
|
+
input.locale
|
|
542
|
+
);
|
|
543
|
+
const name = localization?.name?.trim() || manifest.name;
|
|
544
|
+
const description = localization?.description?.trim() || manifest.description || "";
|
|
545
|
+
const tags = localization?.tags?.map((tag) => tag.trim()).filter((tag) => tag.length > 0) ?? manifest.tags ?? [];
|
|
546
|
+
return {
|
|
547
|
+
description,
|
|
548
|
+
name,
|
|
549
|
+
tags: Array.from(new Set(tags))
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function findWorkspaceAppCatalogLocalization(catalog, locale) {
|
|
553
|
+
const localizations = catalog?.localizations ?? [];
|
|
554
|
+
const normalizedLocale = normalizeLocale(locale);
|
|
555
|
+
if (!normalizedLocale || localizations.length === 0) {
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
const exact = localizations.find(
|
|
559
|
+
(localization) => normalizeLocale(localization.locale) === normalizedLocale
|
|
560
|
+
);
|
|
561
|
+
if (exact) {
|
|
562
|
+
return exact;
|
|
563
|
+
}
|
|
564
|
+
const language = normalizedLocale.split("-")[0];
|
|
565
|
+
if (!language) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
return localizations.find(
|
|
569
|
+
(localization) => normalizeLocale(localization.locale)?.split("-")[0] === language
|
|
570
|
+
) ?? null;
|
|
571
|
+
}
|
|
572
|
+
function normalizeLocale(value) {
|
|
573
|
+
const normalized = value?.trim().replace(/_/gu, "-").toLowerCase() ?? "";
|
|
574
|
+
return normalized.length > 0 ? normalized : null;
|
|
575
|
+
}
|
|
576
|
+
function resolvePrimaryAction(input) {
|
|
577
|
+
if (input.comingSoon) {
|
|
578
|
+
return "none";
|
|
579
|
+
}
|
|
580
|
+
if (isBusyRuntimeStatus(input.status)) {
|
|
581
|
+
return "none";
|
|
582
|
+
}
|
|
583
|
+
if (!input.installed) {
|
|
584
|
+
return "install";
|
|
585
|
+
}
|
|
586
|
+
if (input.canUpdate) {
|
|
587
|
+
return "update";
|
|
588
|
+
}
|
|
589
|
+
if (input.canRetry) {
|
|
590
|
+
return "retry";
|
|
591
|
+
}
|
|
592
|
+
if (input.canOpen) {
|
|
593
|
+
return "open";
|
|
594
|
+
}
|
|
595
|
+
return "none";
|
|
596
|
+
}
|
|
597
|
+
function isComingSoonApp(tags) {
|
|
598
|
+
return tags.some((tag) => tag.trim().toLowerCase() === "coming-soon");
|
|
599
|
+
}
|
|
600
|
+
function canOpenInstalledApp(status) {
|
|
601
|
+
return status === "idle" || status === "running";
|
|
602
|
+
}
|
|
603
|
+
function isBusyRuntimeStatus(status) {
|
|
604
|
+
return status === "installing" || status === "preparing" || status === "starting" || status === "stopping";
|
|
605
|
+
}
|
|
606
|
+
function resolvePrimaryActionLabelKey(primaryAction, fallbackLabelKey) {
|
|
607
|
+
switch (primaryAction) {
|
|
608
|
+
case "install":
|
|
609
|
+
return "actions.installApp";
|
|
610
|
+
case "open":
|
|
611
|
+
return "actions.openApp";
|
|
612
|
+
case "retry":
|
|
613
|
+
return "actions.retryApp";
|
|
614
|
+
case "update":
|
|
615
|
+
return "actions.updateApp";
|
|
616
|
+
case "none":
|
|
617
|
+
return fallbackLabelKey;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function createAppFactoryJobByAppId(factoryJobs) {
|
|
621
|
+
const result = /* @__PURE__ */ new Map();
|
|
622
|
+
for (const job of [...factoryJobs].sort(
|
|
623
|
+
(left, right) => right.updatedAtUnixMs - left.updatedAtUnixMs
|
|
624
|
+
)) {
|
|
625
|
+
if (!isPublishedAppFactoryJob(job)) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
const appId = job.appId?.trim();
|
|
629
|
+
if (!appId || result.has(appId)) {
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
result.set(appId, job);
|
|
633
|
+
}
|
|
634
|
+
return result;
|
|
635
|
+
}
|
|
636
|
+
function isPublishedAppFactoryJob(job) {
|
|
637
|
+
return job.status === "published" || Boolean(job.publishedVersion?.trim());
|
|
638
|
+
}
|
|
639
|
+
function createFactoryJobViewModel(job) {
|
|
640
|
+
const statusLabelKey = `factory.status.${job.status}`;
|
|
641
|
+
const title = job.displayName.trim();
|
|
642
|
+
const agentSessionId = job.agentSessionId?.trim() || null;
|
|
643
|
+
const canFixValidationFailure = job.status === "failed" && job.validationResult != null;
|
|
644
|
+
return {
|
|
645
|
+
id: job.jobId,
|
|
646
|
+
agentSessionId,
|
|
647
|
+
appId: job.appId,
|
|
648
|
+
title,
|
|
649
|
+
prompt: job.prompt,
|
|
650
|
+
provider: job.provider,
|
|
651
|
+
status: job.status,
|
|
652
|
+
statusLabelKey,
|
|
653
|
+
canCancel: job.status === "queued" || job.status === "generating" || job.status === "preparing" || job.status === "validating",
|
|
654
|
+
canDelete: job.status === "canceled" || job.status === "failed" || job.status === "published" || job.status === "ready",
|
|
655
|
+
canFix: canFixValidationFailure,
|
|
656
|
+
canOpenAgentSession: Boolean(agentSessionId),
|
|
657
|
+
canPublish: job.status === "ready",
|
|
658
|
+
canRetryValidation: canFixValidationFailure,
|
|
659
|
+
failureReason: job.failureReason,
|
|
660
|
+
updatedAtUnixMs: job.updatedAtUnixMs
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function createWorkspaceAppRecord(input) {
|
|
664
|
+
const manifest = input.catalog?.manifest;
|
|
665
|
+
if (!manifest) {
|
|
666
|
+
throw new Error("catalog manifest is required.");
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
catalog: input.catalog,
|
|
670
|
+
category: input.category,
|
|
671
|
+
createdAtUnixMs: input.createdAtUnixMs ?? null,
|
|
672
|
+
install: input.install ?? null,
|
|
673
|
+
manifest
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
export {
|
|
678
|
+
workspaceAppIdPattern,
|
|
679
|
+
isWorkspaceAppId,
|
|
680
|
+
normalizeWorkspaceAppId,
|
|
681
|
+
createWorkspaceAppIdentity,
|
|
682
|
+
validateWorkspaceAppManifest,
|
|
683
|
+
mapWorkspaceAppRuntimeStatus,
|
|
684
|
+
normalizeWorkspaceAppRuntimeState,
|
|
685
|
+
resolveWorkspaceAppStatusPresentation,
|
|
686
|
+
createAppCenterViewModel,
|
|
687
|
+
resolveWorkspaceAppCatalogMetadata,
|
|
688
|
+
createWorkspaceAppRecord
|
|
689
|
+
};
|
|
690
|
+
//# sourceMappingURL=chunk-FENSLRNH.js.map
|