wizards 0.2.18 → 0.2.20
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/apps/runtime/dist/app.js +2 -1
- package/apps/runtime/dist/connectors/external.js +4 -1
- package/apps/runtime/dist/manage.js +4 -0
- package/apps/runtime/dist/mcp/auth.js +4 -0
- package/apps/runtime/dist/mcp/handler.js +2 -1
- package/apps/runtime/dist/routes/api.js +12 -9
- package/apps/runtime/dist/routes/internal.js +17 -1
- package/apps/runtime/dist/routes/studio.js +55 -7
- package/apps/runtime/dist/services/access.js +25 -0
- package/apps/runtime/dist/services/errors.js +1 -0
- package/apps/runtime/dist/services/package.js +2 -0
- package/apps/runtime/dist/services/projects.js +15 -2
- package/apps/runtime/dist/services/wizards.js +4 -1
- package/apps/web/dist/assets/{Account-Cp1cgqjV.js → Account-0f8W571o.js} +1 -1
- package/apps/web/dist/assets/{ChangelogDialog-Chke1qUe.js → ChangelogDialog-BBmJedTW.js} +1 -1
- package/apps/web/dist/assets/{ChatPanel-DWrwewr6.js → ChatPanel-Bi3tnxOD.js} +1 -1
- package/apps/web/dist/assets/{CreditsDialog-Ckema8Ss.js → CreditsDialog-C_lMgenE.js} +1 -1
- package/apps/web/dist/assets/EditorPage-2-DypFBB.js +2 -0
- package/apps/web/dist/assets/{FlowPreview-DS545n78.js → FlowPreview-DBroqouz.js} +1 -1
- package/apps/web/dist/assets/{Harness-H3yXP-2L.js → Harness-Dkw5Zldw.js} +1 -1
- package/apps/web/dist/assets/HomePage-D9f7DKEw.js +1 -0
- package/apps/web/dist/assets/{ImportWizard-0_NkA8aS.js → ImportWizard-Br-Ys-Np.js} +1 -1
- package/apps/web/dist/assets/{LocalRuntime-DnABzTOe.js → LocalRuntime-CbA3kdFH.js} +1 -1
- package/apps/web/dist/assets/NewWizardPage-Cvt9fKwq.js +2 -0
- package/apps/web/dist/assets/ProjectPage-CWn7fwSr.js +5 -0
- package/apps/web/dist/assets/{PublicRunner-CtZ7vxl_.js → PublicRunner-DFUM0aP5.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-xV5bWclu.js → RunnerView-BAmePlD3.js} +2 -2
- package/apps/web/dist/assets/SettingsPage-CLLlnzeS.js +1 -0
- package/apps/web/dist/assets/{SetupPage-c6mMD9zN.js → SetupPage-GWL_IhbR.js} +1 -1
- package/apps/web/dist/assets/{SharePage-DTKIbMj-.js → SharePage-DZzIMn0m.js} +1 -1
- package/apps/web/dist/assets/{api-CVG-xDEn.js → api-Byy4NRvQ.js} +1 -1
- package/apps/web/dist/assets/changelog-DMAoeotb.js +1 -0
- package/apps/web/dist/assets/{esm-DLM3jdbm.js → esm-WstF63Ly.js} +1 -1
- package/apps/web/dist/assets/index-udMtduDM.js +10 -0
- package/apps/web/dist/assets/{meta-m947x6uG.js → meta-CUWuppZ0.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-DUY6__C3.js → settings-menu-GUFlmH3n.js} +1 -1
- package/apps/web/dist/assets/ui-CwU0VR_B.js +1 -0
- package/apps/web/dist/index.html +3 -3
- package/apps/web/dist/mcp/flow.html +1 -1
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +13 -1
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +3 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/EditorPage-XX6pfHk5.js +0 -2
- package/apps/web/dist/assets/HomePage-Bds84ShK.js +0 -1
- package/apps/web/dist/assets/NewWizardPage-BqP75V9K.js +0 -2
- package/apps/web/dist/assets/ProjectPage-DbOvwOdZ.js +0 -5
- package/apps/web/dist/assets/SettingsPage-DKt55zFh.js +0 -1
- package/apps/web/dist/assets/changelog-CXXT0zsp.js +0 -1
- package/apps/web/dist/assets/index-1gmaxDSk.js +0 -10
- package/apps/web/dist/assets/ui-BPY2pIh0.js +0 -1
package/apps/runtime/dist/app.js
CHANGED
|
@@ -25,6 +25,7 @@ import { connectCallback, publicRoutes, runRoutes, shareRoutes } from "./routes/
|
|
|
25
25
|
import { studio } from "./routes/studio.js";
|
|
26
26
|
import { wizardStream } from "./routes/wizard-stream.js";
|
|
27
27
|
import { runTicketValid } from "./secrets/signing.js";
|
|
28
|
+
import { asRole } from "./services/access.js";
|
|
28
29
|
import { ServiceError } from "./services/errors.js";
|
|
29
30
|
import { tenantOfLink, tenantStatus } from "./tenants/control.js";
|
|
30
31
|
const app = new Hono();
|
|
@@ -158,7 +159,7 @@ app.use("/api/studio/*", async (c, next) => {
|
|
|
158
159
|
return c.json({ error: "Dieses Konto ist gesperrt.", code: "suspended" }, 403);
|
|
159
160
|
}
|
|
160
161
|
c.set("user", user);
|
|
161
|
-
return withTenant(user.tenantId, next);
|
|
162
|
+
return withTenant(user.tenantId, () => asRole(user.role, next));
|
|
162
163
|
});
|
|
163
164
|
const mcp = mcpHandler();
|
|
164
165
|
app.on(["GET", "POST", "DELETE"], "/api/mcp", (c) => mcp(c.req.raw));
|
|
@@ -9,7 +9,7 @@ import { discoverImportableSources, discoverOAuthFacts, registryDiscover, regist
|
|
|
9
9
|
import { resolveRegistrySource } from "../engenty/connections-external/registry-source.js";
|
|
10
10
|
import { encryptToken } from "../engenty/shims/connections-sdk.js";
|
|
11
11
|
import { env } from "../env.js";
|
|
12
|
-
import { requireWritableProject } from "../services/access.js";
|
|
12
|
+
import { requireAdmin, requireWritableProject } from "../services/access.js";
|
|
13
13
|
import { ServiceError } from "../services/errors.js";
|
|
14
14
|
import { BUILTIN_CONNECTORS, builtinConnector, missingSetup, usable } from "./builtin.js";
|
|
15
15
|
/**
|
|
@@ -272,6 +272,7 @@ export async function resolveConnector(projectId, id) {
|
|
|
272
272
|
}
|
|
273
273
|
export async function importConnector(userId, projectId, input) {
|
|
274
274
|
await requireWritableProject(projectId);
|
|
275
|
+
await requireAdmin();
|
|
275
276
|
if (await load(projectId, input.id)) {
|
|
276
277
|
throw new ServiceError("invalid", `Connector "${input.id}" already exists in this project.`);
|
|
277
278
|
}
|
|
@@ -314,6 +315,7 @@ export async function importConnector(userId, projectId, input) {
|
|
|
314
315
|
*/
|
|
315
316
|
export async function importFromRegistry(userId, projectId, input) {
|
|
316
317
|
await requireWritableProject(projectId);
|
|
318
|
+
await requireAdmin();
|
|
317
319
|
const service = await registryService(input.domain);
|
|
318
320
|
const order = input.kind ? [input.kind] : ["mcp", "openapi"];
|
|
319
321
|
const source = order
|
|
@@ -338,6 +340,7 @@ export async function importFromRegistry(userId, projectId, input) {
|
|
|
338
340
|
}
|
|
339
341
|
export async function removeConnector(projectId, id) {
|
|
340
342
|
await requireWritableProject(projectId);
|
|
343
|
+
await requireAdmin();
|
|
341
344
|
await db.delete(schema.projectConnector).where(where(projectId, id));
|
|
342
345
|
}
|
|
343
346
|
/**
|
|
@@ -100,6 +100,10 @@ async function call(method, path, body) {
|
|
|
100
100
|
}
|
|
101
101
|
return data;
|
|
102
102
|
}
|
|
103
|
+
/** The people of a tenant, as the Manage-App keeps them; they are managed there. */
|
|
104
|
+
export function tenantMembers(id) {
|
|
105
|
+
return call("GET", `/v1/tenants/${encodeURIComponent(id)}/members`);
|
|
106
|
+
}
|
|
103
107
|
const tenants = new Map();
|
|
104
108
|
/** Plan, limits and balance of a tenant, cached for a minute. */
|
|
105
109
|
export async function tenantInfo(id, fresh = false) {
|
|
@@ -47,6 +47,7 @@ async function apiKeyAuth(key) {
|
|
|
47
47
|
const who = {
|
|
48
48
|
userId: "local",
|
|
49
49
|
tenantId: LOCAL_TENANT,
|
|
50
|
+
role: "owner",
|
|
50
51
|
client: found.name || "MCP",
|
|
51
52
|
scopes: [...SCOPES],
|
|
52
53
|
};
|
|
@@ -64,6 +65,7 @@ async function apiKeyAuth(key) {
|
|
|
64
65
|
const who = {
|
|
65
66
|
userId: result.userId,
|
|
66
67
|
tenantId: result.tenantId,
|
|
68
|
+
role: result.role ?? "member",
|
|
67
69
|
client: result.name || "MCP",
|
|
68
70
|
scopes: [...SCOPES],
|
|
69
71
|
};
|
|
@@ -83,6 +85,7 @@ async function tokenAuth(token) {
|
|
|
83
85
|
const who = {
|
|
84
86
|
userId: claims.userId,
|
|
85
87
|
tenantId: claims.tenantId,
|
|
88
|
+
role: claims.role,
|
|
86
89
|
client: claims.clientId || "MCP",
|
|
87
90
|
scopes: SCOPES.filter((s) => granted.has(s)),
|
|
88
91
|
};
|
|
@@ -104,6 +107,7 @@ function localAuth(request, ticket) {
|
|
|
104
107
|
const who = {
|
|
105
108
|
userId: "local",
|
|
106
109
|
tenantId: LOCAL_TENANT,
|
|
110
|
+
role: "owner",
|
|
107
111
|
client,
|
|
108
112
|
scopes: [...SCOPES],
|
|
109
113
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createMcpHandler, McpServer, originValidationResponse, } from "@modelcontextprotocol/server";
|
|
2
2
|
import { withTenant } from "../db/client.js";
|
|
3
3
|
import { env } from "../env.js";
|
|
4
|
+
import { asRole } from "../services/access.js";
|
|
4
5
|
import { tenantStatus } from "../tenants/control.js";
|
|
5
6
|
import { authenticate, principalOf } from "./auth.js";
|
|
6
7
|
import { registerFlowApp } from "./flow-app.js";
|
|
@@ -47,6 +48,6 @@ export function mcpHandler() {
|
|
|
47
48
|
.catch(() => null);
|
|
48
49
|
noteRequest(who.tenantId, authInfo.clientId, who.client, body);
|
|
49
50
|
}
|
|
50
|
-
return withTenant(who.tenantId, () => handler.fetch(request, { authInfo }));
|
|
51
|
+
return withTenant(who.tenantId, () => asRole(who.role, () => handler.fetch(request, { authInfo })));
|
|
51
52
|
};
|
|
52
53
|
}
|
|
@@ -4,6 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { withTenant } from "../db/client.js";
|
|
5
5
|
import { allowed } from "../limits.js";
|
|
6
6
|
import { authenticate, principalOf } from "../mcp/auth.js";
|
|
7
|
+
import { asRole } from "../services/access.js";
|
|
7
8
|
import { ServiceError } from "../services/errors.js";
|
|
8
9
|
import { deleteFile, draftFiles, writeFile } from "../services/files.js";
|
|
9
10
|
import { checkOnServer, checkSchema, listSyncedSpaces, patchSyncedWizard, removeSyncedSpace, removeSyncedWizard, rotateSyncedLink, syncDataSchema, syncSettingsSchema, syncSpaceData, syncWizard, syncWizardSchema, } from "../services/spaces.js";
|
|
@@ -20,6 +21,8 @@ const importSchema = z.object({
|
|
|
20
21
|
.default([]),
|
|
21
22
|
publish: z.boolean().default(false),
|
|
22
23
|
});
|
|
24
|
+
/** Runs a call in the caller's tenant, as the caller's role. */
|
|
25
|
+
const inTenantAs = (who, fn) => withTenant(who.tenantId, () => asRole(who.role, fn));
|
|
23
26
|
const HOUR = 60 * 60 * 1000;
|
|
24
27
|
/** A wizard with its workspace: 25 MB of files as base64, and the definition. */
|
|
25
28
|
const WIZARD_BYTES = 48 * 1024 * 1024;
|
|
@@ -62,7 +65,7 @@ export const apiRoutes = new Hono()
|
|
|
62
65
|
if (who instanceof Response) {
|
|
63
66
|
return who;
|
|
64
67
|
}
|
|
65
|
-
return
|
|
68
|
+
return inTenantAs(who, async () => c.json({ spaces: await listSyncedSpaces() }));
|
|
66
69
|
})
|
|
67
70
|
.post("/spaces/check", async (c) => {
|
|
68
71
|
const who = await caller(c, ["wizards:read"]);
|
|
@@ -70,7 +73,7 @@ export const apiRoutes = new Hono()
|
|
|
70
73
|
return who;
|
|
71
74
|
}
|
|
72
75
|
const body = checkSchema.parse(await c.req.json());
|
|
73
|
-
return
|
|
76
|
+
return inTenantAs(who, async () => c.json({ problems: await checkOnServer(body) }));
|
|
74
77
|
})
|
|
75
78
|
.put("/spaces/:spaceId/wizards/:wizardId", tooLarge, async (c) => {
|
|
76
79
|
const who = await caller(c, ["wizards:write", "wizards:publish"]);
|
|
@@ -82,7 +85,7 @@ export const apiRoutes = new Hono()
|
|
|
82
85
|
return limited;
|
|
83
86
|
}
|
|
84
87
|
const body = syncWizardSchema.parse(await c.req.json());
|
|
85
|
-
return
|
|
88
|
+
return inTenantAs(who, async () => c.json(await syncWizard(c.req.param("spaceId"), c.req.param("wizardId"), body)));
|
|
86
89
|
})
|
|
87
90
|
// The space's own tables and pages, sent with a wizard when they changed.
|
|
88
91
|
.put("/spaces/:spaceId/data", tooLarge, async (c) => {
|
|
@@ -95,7 +98,7 @@ export const apiRoutes = new Hono()
|
|
|
95
98
|
return limited;
|
|
96
99
|
}
|
|
97
100
|
const body = syncDataSchema.parse(await c.req.json());
|
|
98
|
-
return
|
|
101
|
+
return inTenantAs(who, async () => c.json(await syncSpaceData(c.req.param("spaceId"), body)));
|
|
99
102
|
})
|
|
100
103
|
.patch("/spaces/:spaceId/wizards/:wizardId", async (c) => {
|
|
101
104
|
const who = await caller(c, ["wizards:publish"]);
|
|
@@ -107,7 +110,7 @@ export const apiRoutes = new Hono()
|
|
|
107
110
|
return limited;
|
|
108
111
|
}
|
|
109
112
|
const body = syncSettingsSchema.parse(await c.req.json());
|
|
110
|
-
return
|
|
113
|
+
return inTenantAs(who, async () => c.json(await patchSyncedWizard(c.req.param("spaceId"), c.req.param("wizardId"), body)));
|
|
111
114
|
})
|
|
112
115
|
.post("/spaces/:spaceId/wizards/:wizardId/rotate-link", async (c) => {
|
|
113
116
|
const who = await caller(c, ["wizards:publish"]);
|
|
@@ -118,21 +121,21 @@ export const apiRoutes = new Hono()
|
|
|
118
121
|
if (limited) {
|
|
119
122
|
return limited;
|
|
120
123
|
}
|
|
121
|
-
return
|
|
124
|
+
return inTenantAs(who, async () => c.json(await rotateSyncedLink(who.userId, c.req.param("spaceId"), c.req.param("wizardId"))));
|
|
122
125
|
})
|
|
123
126
|
.delete("/spaces/:spaceId/wizards/:wizardId", async (c) => {
|
|
124
127
|
const who = await caller(c, ["wizards:write"]);
|
|
125
128
|
if (who instanceof Response) {
|
|
126
129
|
return who;
|
|
127
130
|
}
|
|
128
|
-
return
|
|
131
|
+
return inTenantAs(who, async () => c.json(await removeSyncedWizard(who.userId, c.req.param("spaceId"), c.req.param("wizardId"))));
|
|
129
132
|
})
|
|
130
133
|
.delete("/spaces/:spaceId", async (c) => {
|
|
131
134
|
const who = await caller(c, ["wizards:write"]);
|
|
132
135
|
if (who instanceof Response) {
|
|
133
136
|
return who;
|
|
134
137
|
}
|
|
135
|
-
return
|
|
138
|
+
return inTenantAs(who, async () => c.json(await removeSyncedSpace(c.req.param("spaceId"))));
|
|
136
139
|
})
|
|
137
140
|
.post("/wizards/import", tooLarge, async (c) => {
|
|
138
141
|
const authInfo = await authenticate(c.req.raw);
|
|
@@ -148,7 +151,7 @@ export const apiRoutes = new Hono()
|
|
|
148
151
|
if ((await tenantStatus(who.tenantId)) === "suspended") {
|
|
149
152
|
return c.json({ error: "tenant_suspended" }, 403);
|
|
150
153
|
}
|
|
151
|
-
return
|
|
154
|
+
return inTenantAs(who, async () => {
|
|
152
155
|
const writer = { source: "mcp", client: who.client };
|
|
153
156
|
let wizardId = body.wizardId;
|
|
154
157
|
if (wizardId) {
|
|
@@ -7,6 +7,7 @@ import { closeTenantDb, tenantDb } from "../db/client.js";
|
|
|
7
7
|
import { env } from "../env.js";
|
|
8
8
|
import { removeTenantObjects } from "../files/objects.js";
|
|
9
9
|
import { forgetTenant, managed } from "../manage.js";
|
|
10
|
+
import { loadedPlugins } from "../plugins/registry.js";
|
|
10
11
|
import { forgetTenantRows, setTenantStatus } from "../tenants/control.js";
|
|
11
12
|
function fromManage(header) {
|
|
12
13
|
const given = Buffer.from(header?.replace(/^Bearer\s+/i, "") ?? "");
|
|
@@ -21,7 +22,22 @@ function fromManage(header) {
|
|
|
21
22
|
* removes the tenant's files and everything the control database knows; the tenant's database
|
|
22
23
|
* itself is deleted by whoever created it (the Manage-App, or here when it is a file).
|
|
23
24
|
*/
|
|
24
|
-
export const internalRoutes = new Hono()
|
|
25
|
+
export const internalRoutes = new Hono()
|
|
26
|
+
// The plugins this runtime carries: the Manage-App offers them when a plan's modules are picked.
|
|
27
|
+
.get("/plugins", (c) => {
|
|
28
|
+
if (!fromManage(c.req.header("authorization"))) {
|
|
29
|
+
return c.json({ error: "unauthorized" }, 401);
|
|
30
|
+
}
|
|
31
|
+
return c.json({
|
|
32
|
+
plugins: loadedPlugins().map((p) => ({
|
|
33
|
+
id: p.source.id,
|
|
34
|
+
name: p.source.manifest.name ?? p.source.id,
|
|
35
|
+
description: p.source.manifest.description ?? "",
|
|
36
|
+
version: p.source.manifest.version ?? null,
|
|
37
|
+
})),
|
|
38
|
+
});
|
|
39
|
+
})
|
|
40
|
+
.post("/tenants/:id", async (c) => {
|
|
25
41
|
if (!fromManage(c.req.header("authorization"))) {
|
|
26
42
|
return c.json({ error: "unauthorized" }, 401);
|
|
27
43
|
}
|
|
@@ -22,13 +22,13 @@ import { env } from "../env.js";
|
|
|
22
22
|
import { freshAuth, loginEnv } from "../harness/env.js";
|
|
23
23
|
import { detectHarness, detectHarnesses, HARNESS_IDS, harness, installSpec, } from "../harness/index.js";
|
|
24
24
|
import { closeTerminal, openTerminal, resizeTerminal, subscribeTerminal, writeTerminal, } from "../harness/terminal.js";
|
|
25
|
-
import { managed } from "../manage.js";
|
|
25
|
+
import { managed, tenantInfo, tenantMembers } from "../manage.js";
|
|
26
26
|
import { seenApps } from "../mcp/seen.js";
|
|
27
27
|
import { generateImageMedia, generateSpeechMedia, generateVideoMedia } from "../media/generate.js";
|
|
28
28
|
import { transcribeAudio } from "../media/transcribe.js";
|
|
29
29
|
import { checkKey, classProblem, embeddingModel, hasTextModel, LOCAL_KEYS, localModelSettings, ModelUnavailableError, modelSettings, saveLocalModels, saveModels, storedKey, textModel, } from "../models.js";
|
|
30
30
|
import { HTML_RESPONSE_CSP } from "../render/guard.js";
|
|
31
|
-
import { mayBuild, projectWritable, requireWritable } from "../services/access.js";
|
|
31
|
+
import { isAdminRole, mayBuild, mayCreate, projectWritable, requireWritable, } from "../services/access.js";
|
|
32
32
|
import { architectTurn } from "../services/architect.js";
|
|
33
33
|
import { checkForCloud, cloudLinked, cloudSpaces, cloudState, forgetCloud, publishAndSync, pushSharing, removeFromCloud, replaceCloudSpaces, rotateInCloud, syncToCloud, } from "../services/cloud.js";
|
|
34
34
|
import { notFound, ServiceError } from "../services/errors.js";
|
|
@@ -39,7 +39,7 @@ import { exportWizard, importWizard, PACKAGE_EXTENSION, PACKAGE_MAX_BYTES, PACKA
|
|
|
39
39
|
import { profileSchema, saveProfile, userProfile } from "../services/profile.js";
|
|
40
40
|
import { addProjectFile, fileView, orderProjectFiles, projectFile, projectFileContent, projectFiles, reindexProjectFile, removeProjectFile, updateProjectFile, } from "../services/project-files.js";
|
|
41
41
|
import { documentText, queueIndex, searchKnowledge } from "../services/project-index.js";
|
|
42
|
-
import { createProject, deleteProject, listProjects, maskedServers, ownedProject,
|
|
42
|
+
import { createProject, deleteProject, listProjects, maskedServers, ownedProject, projectLimitOrNull, projectPatchSchema, updateProject, } from "../services/projects.js";
|
|
43
43
|
import { listResults, listRuns, startTestRun } from "../services/runs.js";
|
|
44
44
|
import { addCategoryValue, categoryRow, categoryValue, deleteCategory, deleteCategoryValue, mergeCategoryValues, orderCategoryValues, putCategory, renameCategoryValue, setItemCategory, updateCategory, } from "../services/space-categories.js";
|
|
45
45
|
import { addTableRow, createPage, createTable, deletePage, deleteTable, deleteTableRows, getPage, getTable, listSpaceData, orderSubPages, updatePage, updateTable, updateTableRow, } from "../services/space-data.js";
|
|
@@ -47,6 +47,7 @@ import { draftWidgetPreview } from "../services/widgets.js";
|
|
|
47
47
|
import { createWizard, deleteWizard, duplicateWizard, listWizards, ownedWizard, rotateShareLink, updateWizardSettings, wizardMessages, wizardState, writableWizard, writeDraft, } from "../services/wizards.js";
|
|
48
48
|
import { readSetting, writeSetting } from "../settings.js";
|
|
49
49
|
import { codeOf } from "../tenants/control.js";
|
|
50
|
+
import { currentTenant } from "../tenants/tenant.js";
|
|
50
51
|
import { applyUpdate, updateStatus } from "../update.js";
|
|
51
52
|
import { byteRange } from "./delivery.js";
|
|
52
53
|
/** The model sources a test call has worked on: the setup is done for those, not for the others. */
|
|
@@ -186,11 +187,20 @@ export const studio = new Hono()
|
|
|
186
187
|
const linked = managed ? null : await linkedAccount();
|
|
187
188
|
const overview = linked ? await accountOverview() : null;
|
|
188
189
|
const profile = await userProfile(user);
|
|
190
|
+
// What the Manage-App says of the tenant: its plan and feature switches.
|
|
191
|
+
const info = managed ? await tenantInfo(user.tenantId).catch(() => null) : null;
|
|
189
192
|
return c.json({
|
|
190
193
|
user: { id: user.id, name: profile.name, email: profile.email, image: user.image ?? null },
|
|
191
194
|
/** What the person says about themselves; name and e-mail are the account's when managed. */
|
|
192
195
|
profile,
|
|
193
|
-
tenant: {
|
|
196
|
+
tenant: {
|
|
197
|
+
id: user.tenantId,
|
|
198
|
+
role: user.role,
|
|
199
|
+
/** The plan the team is on; null alone, or where the Manage-App names none. */
|
|
200
|
+
plan: info?.plan ?? null,
|
|
201
|
+
},
|
|
202
|
+
/** The plan's feature switches (`ownKeys`, …); alone, everything is on. */
|
|
203
|
+
features: managed ? (info?.features ?? {}) : { ownKeys: true },
|
|
194
204
|
mode: managed ? "managed" : "local",
|
|
195
205
|
/** The tenant's balance; null where the runtime resolves models itself. */
|
|
196
206
|
credits: managed ? await balanceCredits() : null,
|
|
@@ -220,10 +230,36 @@ export const studio = new Hono()
|
|
|
220
230
|
aiReady: (await hasTextModel()) || (!managed && (await subscriptionClients()).length > 0),
|
|
221
231
|
mcpUrl: `${env.appUrl}/api/mcp`,
|
|
222
232
|
/**
|
|
223
|
-
* One project: the studio shows no project switcher
|
|
224
|
-
* changed here; the studio shows what the person's local
|
|
233
|
+
* One project: the studio shows no project switcher; null: as many as wanted. `build:
|
|
234
|
+
* false`: nothing is made or changed here; the studio shows what the person's local
|
|
235
|
+
* install synced. `create`: the person makes and deletes here (builds, and is no mere
|
|
236
|
+
* member). `members`: the people the plan allows, null for no limit.
|
|
225
237
|
*/
|
|
226
|
-
limits: {
|
|
238
|
+
limits: {
|
|
239
|
+
projects: await projectLimitOrNull(),
|
|
240
|
+
build: await mayBuild(),
|
|
241
|
+
create: await mayCreate(),
|
|
242
|
+
members: info?.limits.members ?? null,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
})
|
|
246
|
+
// The team as the Manage-App keeps it: its people, who is invited, the places the plan gives.
|
|
247
|
+
.get("/team", async (c) => {
|
|
248
|
+
if (!managed) {
|
|
249
|
+
return c.json({ error: "not_managed" }, 404);
|
|
250
|
+
}
|
|
251
|
+
const user = c.get("user");
|
|
252
|
+
const [info, team] = await Promise.all([
|
|
253
|
+
tenantInfo(user.tenantId).catch(() => null),
|
|
254
|
+
tenantMembers(currentTenant()),
|
|
255
|
+
]);
|
|
256
|
+
return c.json({
|
|
257
|
+
...team,
|
|
258
|
+
plan: info?.plan ?? null,
|
|
259
|
+
/** Where members are invited and roles changed: the account pages of the Manage-App. */
|
|
260
|
+
manageUrl: env.manage.url,
|
|
261
|
+
/** Whether this person manages the team there. */
|
|
262
|
+
canManage: isAdminRole(user.role),
|
|
227
263
|
});
|
|
228
264
|
})
|
|
229
265
|
// A runtime that runs alone says when a newer release is out and can run the update itself.
|
|
@@ -976,6 +1012,18 @@ export const studio = new Hono()
|
|
|
976
1012
|
Object.values(input.bindings ?? {}).some((ref) => ref && !cloudBinding(ref)))) {
|
|
977
1013
|
return c.json({ error: "Not available in the cloud." }, 400);
|
|
978
1014
|
}
|
|
1015
|
+
// Own keys and bindings to a provider's model are a feature of the team's plan.
|
|
1016
|
+
if (managed) {
|
|
1017
|
+
const info = await tenantInfo(c.get("user").tenantId).catch(() => null);
|
|
1018
|
+
const own = Object.values(input.keys ?? {}).some((key) => key?.trim()) ||
|
|
1019
|
+
Object.values(input.bindings ?? {}).some((ref) => ref && !isCreditsRef(ref));
|
|
1020
|
+
if (own && info && info.features?.ownKeys === false) {
|
|
1021
|
+
return c.json({
|
|
1022
|
+
error: `Eigene API Keys gibt es nicht im Paket ${info.plan?.name ?? "des Teams"}.`,
|
|
1023
|
+
code: "plan",
|
|
1024
|
+
}, 403);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
979
1027
|
await saveModels(input);
|
|
980
1028
|
return c.json(await modelSettings());
|
|
981
1029
|
})
|
|
@@ -17,6 +17,31 @@ export function asSync(fn) {
|
|
|
17
17
|
return sync.run(true, fn);
|
|
18
18
|
}
|
|
19
19
|
const syncing = () => sync.getStore() === true;
|
|
20
|
+
// --- roles -----------------------------------------------------------------------------
|
|
21
|
+
// In a team, an owner or admin makes and deletes (wizards, spaces, connectors); a member edits
|
|
22
|
+
// the wizards there are. A runtime that runs alone has one person, who does everything.
|
|
23
|
+
const actor = new AsyncLocalStorage();
|
|
24
|
+
/** Runs a request as the person's role in the tenant; set once per entry, like the tenant. */
|
|
25
|
+
export function asRole(role, fn) {
|
|
26
|
+
return actor.run(role, fn);
|
|
27
|
+
}
|
|
28
|
+
/** The role of who acts now. A context without one (a job, a sync) acts as the owner. */
|
|
29
|
+
export function currentRole() {
|
|
30
|
+
return actor.getStore() ?? "owner";
|
|
31
|
+
}
|
|
32
|
+
export const isAdminRole = (role) => role === "owner" || role === "admin";
|
|
33
|
+
export const MEMBERS_EDIT_ONLY = "Das dürfen nur Admins des Teams. Als Mitglied bearbeitest du die Wizards, die es gibt.";
|
|
34
|
+
/** Refuses a member: making and deleting is for the owner and the admins of the team. */
|
|
35
|
+
export async function requireAdmin() {
|
|
36
|
+
if (syncing() || !managed || isAdminRole(currentRole())) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw new ServiceError("forbidden", MEMBERS_EDIT_ONLY);
|
|
40
|
+
}
|
|
41
|
+
/** Whether the person makes and deletes here: builds at all, and is no mere member. */
|
|
42
|
+
export async function mayCreate() {
|
|
43
|
+
return (await mayBuild()) && (!managed || isAdminRole(currentRole()));
|
|
44
|
+
}
|
|
20
45
|
/** Whether the tenant builds wizards on this runtime. A runtime that runs alone always does. */
|
|
21
46
|
export async function mayBuild() {
|
|
22
47
|
if (!managed) {
|
|
@@ -3,6 +3,7 @@ import { strFromU8, strToU8, unzipSync, zipSync } from "fflate";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { listConnectors } from "../connectors/external.js";
|
|
5
5
|
import { getBlob } from "../files/blobs.js";
|
|
6
|
+
import { requireAdmin } from "./access.js";
|
|
6
7
|
import { ServiceError } from "./errors.js";
|
|
7
8
|
import { draftFiles, writeFile } from "./files.js";
|
|
8
9
|
import { ownedProject } from "./projects.js";
|
|
@@ -176,6 +177,7 @@ async function missing(userId, projectId, draft, manifest) {
|
|
|
176
177
|
}
|
|
177
178
|
/** Makes a new wizard of a package in the project: the draft and its workspace, not published. */
|
|
178
179
|
export async function importWizard(userId, projectId, data, name) {
|
|
180
|
+
await requireAdmin();
|
|
179
181
|
const { manifest, files } = readPackage(data);
|
|
180
182
|
// The shape is checked before anything is written; a wrong one leaves no wizard behind.
|
|
181
183
|
const { draft } = parseDraft(manifest.definition, files.map((f) => f.path));
|
|
@@ -7,7 +7,7 @@ import { env } from "../env.js";
|
|
|
7
7
|
import { managed, tenantInfo } from "../manage.js";
|
|
8
8
|
import { spaceChanged } from "../plugins/events.js";
|
|
9
9
|
import { currentTenant } from "../tenants/tenant.js";
|
|
10
|
-
import { mayBuild, requireBuild, requireWritable } from "./access.js";
|
|
10
|
+
import { mayBuild, requireAdmin, requireBuild, requireWritable } from "./access.js";
|
|
11
11
|
import { notFound, ServiceError } from "./errors.js";
|
|
12
12
|
import { forgetWizardLinks } from "./links.js";
|
|
13
13
|
import { removeProjectFiles } from "./project-files.js";
|
|
@@ -48,12 +48,21 @@ export const projectPatchSchema = z.object({
|
|
|
48
48
|
export async function projectLimit() {
|
|
49
49
|
if (managed) {
|
|
50
50
|
const info = await tenantInfo(currentTenant()).catch(() => null);
|
|
51
|
+
// Null: the plan sets no limit.
|
|
52
|
+
if (info && info.limits.projects === null) {
|
|
53
|
+
return Number.POSITIVE_INFINITY;
|
|
54
|
+
}
|
|
51
55
|
if (info?.limits.projects) {
|
|
52
56
|
return info.limits.projects;
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
return Math.max(1, env.limits.projects);
|
|
56
60
|
}
|
|
61
|
+
/** The number for the studio: null where there is no limit (JSON has no infinity). */
|
|
62
|
+
export async function projectLimitOrNull() {
|
|
63
|
+
const limit = await projectLimit();
|
|
64
|
+
return Number.isFinite(limit) ? limit : null;
|
|
65
|
+
}
|
|
57
66
|
export async function ownedProject(_userId, projectId) {
|
|
58
67
|
const p = await db.query.project.findFirst({
|
|
59
68
|
where: and(eq(schema.project.id, projectId), eq(schema.project.tenantId, currentTenant())),
|
|
@@ -109,10 +118,11 @@ export async function listProjects(_userId) {
|
|
|
109
118
|
await ensureProject();
|
|
110
119
|
}
|
|
111
120
|
// The oldest come first: with a limit of one, that is the project everything lands in.
|
|
121
|
+
const limit = await projectLimit();
|
|
112
122
|
const projects = await db.query.project.findMany({
|
|
113
123
|
where: build ? eq(schema.project.tenantId, currentTenant()) : synced(),
|
|
114
124
|
orderBy: [schema.project.createdAt],
|
|
115
|
-
limit:
|
|
125
|
+
...(Number.isFinite(limit) ? { limit } : {}),
|
|
116
126
|
});
|
|
117
127
|
const counts = await db
|
|
118
128
|
.select({ projectId: schema.wizard.projectId, n: count() })
|
|
@@ -135,6 +145,7 @@ export function maskedServers(p) {
|
|
|
135
145
|
}
|
|
136
146
|
export async function createProject(_userId, name) {
|
|
137
147
|
await requireBuild();
|
|
148
|
+
await requireAdmin();
|
|
138
149
|
const [{ n }] = await db
|
|
139
150
|
.select({ n: count() })
|
|
140
151
|
.from(schema.project)
|
|
@@ -153,6 +164,7 @@ export async function createProject(_userId, name) {
|
|
|
153
164
|
export async function updateProject(userId, projectId, patch) {
|
|
154
165
|
const p = await ownedProject(userId, projectId);
|
|
155
166
|
await requireWritable(p);
|
|
167
|
+
await requireAdmin();
|
|
156
168
|
const mcpServers = patch.mcpServers?.map((s) => {
|
|
157
169
|
const before = p.mcpServers.find((x) => x.id === s.id);
|
|
158
170
|
const headers = s.headers
|
|
@@ -188,6 +200,7 @@ export function projectProfile(p) {
|
|
|
188
200
|
}
|
|
189
201
|
export async function deleteProject(userId, projectId) {
|
|
190
202
|
await requireWritable(await ownedProject(userId, projectId));
|
|
203
|
+
await requireAdmin();
|
|
191
204
|
const all = await db.query.project.findMany({ where: madeHere() });
|
|
192
205
|
if (all.length <= 1) {
|
|
193
206
|
throw new ServiceError("refused", "Das letzte Projekt bleibt.");
|
|
@@ -8,7 +8,7 @@ import { env } from "../env.js";
|
|
|
8
8
|
import { pluginToolsOf } from "../plugins/registry.js";
|
|
9
9
|
import { dropLinks, putLink } from "../tenants/control.js";
|
|
10
10
|
import { currentTenant } from "../tenants/tenant.js";
|
|
11
|
-
import { requireBuild, requireWritable, requireWritableProject } from "./access.js";
|
|
11
|
+
import { requireAdmin, requireBuild, requireWritable, requireWritableProject } from "./access.js";
|
|
12
12
|
import { changedSteps, emitDraftChanged } from "./draft-events.js";
|
|
13
13
|
import { notFound, ServiceError } from "./errors.js";
|
|
14
14
|
import { copyFiles, draftFiles, sameFiles, seedFiles } from "./files.js";
|
|
@@ -202,6 +202,7 @@ export function shapeIssues(error, raw) {
|
|
|
202
202
|
export async function createWizard(userId, input, writer = { source: "studio" }) {
|
|
203
203
|
// Asked first: a tenant that builds nothing here has no project of its own to name.
|
|
204
204
|
await requireBuild();
|
|
205
|
+
await requireAdmin();
|
|
205
206
|
const project = input.projectId
|
|
206
207
|
? await ownedProject(userId, input.projectId)
|
|
207
208
|
: await defaultProject(userId);
|
|
@@ -330,6 +331,7 @@ export async function rotateShareLink(userId, wizardId) {
|
|
|
330
331
|
return { shareToken: token, shareUrl: shareUrl(token) };
|
|
331
332
|
}
|
|
332
333
|
export async function duplicateWizard(userId, wizardId) {
|
|
334
|
+
await requireAdmin();
|
|
333
335
|
const w = await writableWizard(userId, wizardId);
|
|
334
336
|
const id = nanoid(12);
|
|
335
337
|
const draft = { ...w.draft, title: `${w.draft.title} (Kopie)` };
|
|
@@ -349,6 +351,7 @@ export async function duplicateWizard(userId, wizardId) {
|
|
|
349
351
|
return { id };
|
|
350
352
|
}
|
|
351
353
|
export async function deleteWizard(userId, wizardId) {
|
|
354
|
+
await requireAdmin();
|
|
352
355
|
await writableWizard(userId, wizardId);
|
|
353
356
|
await forgetWizardLinks(wizardId);
|
|
354
357
|
await db
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{c as r,j as i,n as a,nt as o}from"./api-CVG-xDEn.js";import{t as s}from"./createLucideIcon-q3MZMmnf.js";import{t as c}from"./check-CUpby_J9.js";import{n as l,r as u}from"./upload-DNRENWzQ.js";import{t as d}from"./external-link-MAL7vJYj.js";import{b as f,c as p,d as m,h,m as g,n as _,o as v,r as y,s as b,t as x,v as S}from"./ui-BPY2pIh0.js";import{M as ee,R as C,U as w,f as T,o as te,p as E,u as D,x as O}from"./index-1gmaxDSk.js";import{n as k}from"./LocalRuntime-DnABzTOe.js";var A={name:`hard-drive`,size:24,node:[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]};A.node;var j=s(A),M={name:`layout-grid`,size:24,node:[[`rect`,{width:`7`,height:`7`,x:`3`,y:`3`,rx:`1`,key:`1g98yp`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`3`,rx:`1`,key:`6d4xhi`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`14`,rx:`1`,key:`nxv5o0`}],[`rect`,{width:`7`,height:`7`,x:`3`,y:`14`,rx:`1`,key:`1bb6yr`}]]};M.node;var N=s(M),P={name:`list`,size:24,node:[[`path`,{d:`M3 5h.01`,key:`18ugdj`}],[`path`,{d:`M3 12h.01`,key:`nlz23k`}],[`path`,{d:`M3 19h.01`,key:`noohij`}],[`path`,{d:`M8 5h13`,key:`1pao27`}],[`path`,{d:`M8 12h13`,key:`1za7za`}],[`path`,{d:`M8 19h13`,key:`m83p4d`}]]};P.node;var F=s(P),I=e(t(),1),L=e=>[`project-files`,e];function R(e){return i({queryKey:L(e),queryFn:()=>a.get(`/api/studio/projects/${e}/files`),refetchInterval:e=>e.state.data?.files.some(e=>e.status===`pending`)?2e3:!1})}function z(e,t){return r(`/api/studio/projects/${e}/files/${t}/content`)}function B(e,t=!1){let n=o(),[r,i]=(0,I.useState)(!1),[s,c]=(0,I.useState)(null),l=()=>n.invalidateQueries({queryKey:L(e)}),u=async e=>{c(null);try{await e()}catch(e){c(e.message)}finally{await l()}};return{busy:r,error:s,readOnly:t,upload:async(t,n)=>{i(!0),await u(async()=>{for(let r of n)await a.upload(`/api/studio/projects/${e}/files?kind=${t}`,r),await l()}),i(!1)},describe:(t,n)=>u(()=>a.patch(`/api/studio/projects/${e}/files/${t}`,{description:n})),remove:t=>u(()=>a.del(`/api/studio/projects/${e}/files/${t}`)),reindex:t=>u(()=>a.post(`/api/studio/projects/${e}/files/${t}/reindex`)),order:(t,n)=>u(()=>a.put(`/api/studio/projects/${e}/files/order`,{kind:t,ids:n}))}}function V(e,t){let n=`wz.layout.${e}`,[r,i]=(0,I.useState)(()=>{try{let e=localStorage.getItem(n);return e===`list`||e===`cards`?e:t}catch{return t}});return[r,()=>i(e=>{let t=e===`list`?`cards`:`list`;try{localStorage.setItem(n,t)}catch{}return t})]}function H(e,t,n=700){let[r,i]=(0,I.useState)(`idle`),[a,o]=(0,I.useState)(null),s=JSON.stringify(e),c=(0,I.useRef)(s),l=(0,I.useRef)({value:e,json:s,save:t});l.current={value:e,json:s,save:t};let u=(0,I.useRef)(Promise.resolve()),d=(0,I.useCallback)(()=>{let e=l.current;e.json!==c.current&&(c.current=e.json,i(`saving`),u.current=u.current.then(async()=>{try{await e.save(e.value),o(null),i(`saved`)}catch(e){c.current=``,o(e.message),i(`error`)}}))},[]);return(0,I.useEffect)(()=>{if(s===c.current)return;let e=setTimeout(d,n);return()=>clearTimeout(e)},[s,n,d]),(0,I.useEffect)(()=>d,[d]),{state:r,error:a}}function U(e){return e<1e6?`${Math.max(1,Math.round(e/1e3))} KB`:`${(e/1e6).toFixed(1)} MB`}var W=e(n(),1);function G({id:e,children:t}){return(0,W.jsx)(`div`,{id:e,className:`scroll-mt-24 max-md:scroll-mt-36`,children:t})}function K({title:e,hint:t,save:n,action:r,locked:i,plain:a,children:o}){return(0,W.jsxs)(`section`,{className:`flex flex-col gap-2.5`,children:[(0,W.jsxs)(`div`,{className:`flex items-start justify-between gap-4 px-1`,children:[(0,W.jsxs)(`div`,{className:`min-w-0`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,W.jsx)(`h2`,{className:`font-display font-semibold text-lg leading-tight`,children:e}),n?.state===`saving`?(0,W.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-[0.75rem] text-ink-3`,children:[(0,W.jsx)(m,{className:`size-3`}),` `,f(`project.saving`)]}):n?.state===`saved`?(0,W.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-[0.75rem] text-moss`,children:[(0,W.jsx)(c,{className:`size-3.5`}),` `,f(`settings.saved`)]}):null]}),t?(0,W.jsx)(`p`,{className:`mt-1 text-[0.875rem] text-ink-3 leading-snug`,children:t}):null]}),r?(0,W.jsx)(`div`,{className:`shrink-0`,children:r}):null]}),a?o:(0,W.jsxs)(_,{className:`p-3.5 sm:p-5`,children:[i?(0,W.jsx)(`fieldset`,{disabled:!0,className:`min-w-0`,children:o}):o,n?.state===`error`&&n.error?(0,W.jsx)(`p`,{className:`mt-4 text-[0.875rem] text-rose`,children:n.error}):null]})]})}function q({children:e,layout:t,onToggle:n}){return(0,W.jsxs)(`div`,{className:`mb-3 flex items-start gap-2`,children:[(0,W.jsx)(`div`,{className:`min-w-0 flex-1`,children:(0,W.jsx)(`div`,{className:`w-56 max-w-full transition-[width] duration-200 focus-within:w-full has-[input:not(:placeholder-shown)]:w-full`,children:e})}),(0,W.jsx)(v,{label:f(t===`list`?`project.viewCards`:`project.viewList`),onClick:n,children:t===`list`?(0,W.jsx)(N,{className:`size-4`}):(0,W.jsx)(F,{className:`size-4`})})]})}function J({value:e,onChange:t,onEnter:n,placeholder:r,busy:i}){return(0,W.jsxs)(`div`,{className:`relative`,children:[(0,W.jsx)(l,{className:`-translate-y-1/2 pointer-events-none absolute top-1/2 left-3 size-4 text-ink-4`}),(0,W.jsx)(`input`,{type:`search`,value:e,placeholder:r,onChange:e=>t(e.target.value),onKeyDown:e=>e.key===`Enter`&&n?.(),className:`h-9 w-full rounded-lg border border-input bg-card pr-3 pl-9 text-[0.875rem] text-ink outline-none transition placeholder:text-ink-4 focus:border-focus focus:ring-4 focus:ring-focus-glow`}),i?(0,W.jsx)(m,{className:`-translate-y-1/2 absolute top-1/2 right-3 size-4 text-ink-3`}):null]})}function Y({onFiles:e,accept:t,multiple:n=!0,className:r,children:i}){return(0,W.jsxs)(`label`,{onDragOver:e=>{e.preventDefault(),e.currentTarget.dataset.over=`1`},onDragLeave:e=>{delete e.currentTarget.dataset.over},onDrop:t=>{t.preventDefault(),delete t.currentTarget.dataset.over;let r=Array.from(t.dataTransfer.files);r.length&&e(n?r:r.slice(0,1))},className:h(`flex cursor-pointer items-center justify-center gap-2 rounded-lg border border-input border-dashed text-[0.875rem] text-ink-3 transition focus-within:border-focus focus-within:ring-4 focus-within:ring-focus-glow hover:border-ink-4 hover:text-ink data-[over]:border-focus data-[over]:bg-paper-2 data-[over]:text-ink`,r),children:[i,(0,W.jsx)(`input`,{className:`sr-only`,type:`file`,accept:t,multiple:n,onChange:t=>{let n=Array.from(t.target.files??[]);t.target.value=``,n.length&&e(n)}})]})}var X=`mt-1.5 text-[0.8125rem] text-ink-3`;function Z(e){let t=o(),[n,r]=(0,I.useState)(!1),i=w({mutationFn:e=>a.post(`/api/studio/account/link`,e),onSuccess:({url:e})=>{k(e),r(!0)}});return(0,I.useEffect)(()=>{if(!n)return;let e=setInterval(()=>void t.invalidateQueries({queryKey:[`me`]}),2e3),i=setTimeout(()=>r(!1),3e5);return()=>{clearInterval(e),clearTimeout(i)}},[n,t]),(0,I.useEffect)(()=>{e&&r(!1)},[e]),{link:i,waiting:n}}function Q({link:e,waiting:t}){return e.isError?(0,W.jsx)(`p`,{className:`text-[0.8125rem] text-rose`,children:f(`local.accountFailed`)}):t?(0,W.jsxs)(`p`,{className:`flex items-center gap-2 text-[0.8125rem] text-ink-3`,children:[(0,W.jsx)(m,{className:`size-3.5`}),` `,f(`local.accountWaiting`)]}):null}function ne(){let e=Z(!1),{link:t}=e,[n,r]=(0,I.useState)(``);return(0,W.jsxs)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:[(0,W.jsxs)(`div`,{className:`flex items-start gap-4 py-5 first:pt-0 last:pb-0`,children:[(0,W.jsx)(`div`,{className:`flex size-11 shrink-0 items-center justify-center rounded-full bg-paper-2 text-ink-2`,children:(0,W.jsx)(j,{className:`size-5`})}),(0,W.jsxs)(`div`,{className:`min-w-0`,children:[(0,W.jsx)(`div`,{className:`font-medium text-[0.9375rem]`,children:f(`account.local`)}),(0,W.jsx)(`p`,{className:`mt-1 text-[0.875rem] text-ink-3 leading-relaxed`,children:f(`account.localText`)})]})]}),E.account?(0,W.jsxs)(`div`,{className:`flex flex-col gap-4 py-5 last:pb-0`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`div`,{className:`font-medium text-[0.9375rem]`,children:f(`account.with`)}),(0,W.jsxs)(`ul`,{className:`mt-2 flex flex-col gap-2 text-[0.875rem] text-ink-2 leading-snug`,children:[(0,W.jsxs)(`li`,{className:`flex items-start gap-2.5`,children:[(0,W.jsx)(u,{className:`mt-0.5 size-4 shrink-0 text-amber`}),f(`account.withCredits`)]}),(0,W.jsxs)(`li`,{className:`flex items-start gap-2.5`,children:[(0,W.jsx)(C,{className:`mt-0.5 size-4 shrink-0 text-cobalt`}),f(`account.withCloud`)]})]})]}),(0,W.jsx)(`div`,{children:(0,W.jsx)(x,{busy:t.isPending&&!t.variables?.signup,onClick:()=>t.mutate({}),children:f(`account.signIn`)})})]}):null,E.account?(0,W.jsxs)(`form`,{className:`flex flex-col gap-3 py-5 last:pb-0`,onSubmit:e=>{e.preventDefault(),t.mutate({signup:!0,code:n.trim()||void 0})},children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`div`,{className:`font-medium text-[0.9375rem]`,children:f(`account.new`)}),(0,W.jsx)(`p`,{className:`mt-1 text-[0.875rem] text-ink-3 leading-relaxed`,children:f(`account.newHint`)})]}),(0,W.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,W.jsx)(b,{value:n,maxLength:64,autoComplete:`off`,spellCheck:!1,placeholder:f(`account.code`),"aria-label":f(`account.code`),onChange:e=>r(e.target.value),className:`w-56 font-mono text-[0.875rem]`}),(0,W.jsx)(x,{type:`submit`,variant:`secondary`,className:`h-11`,busy:t.isPending&&!!t.variables?.signup,children:f(`account.create`)})]}),(0,W.jsx)(Q,{...e})]}):null]})}var $={start:`account.kind.start`,monthly:`account.kind.monthly`,gift:`account.kind.gift`};function re({account:e}){let t=o(),n=Z(e.signedIn),r=w({mutationFn:()=>a.del(`/api/studio/account`),onSuccess:()=>t.invalidateQueries({queryKey:[`me`]})}),i=new Intl.DateTimeFormat(S,{dateStyle:`medium`});return(0,W.jsxs)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:[(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-3 py-5 first:pt-0`,children:[(0,W.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`span`,{className:`truncate font-medium text-[0.9375rem]`,children:e.name||e.email}),e.signedIn?(0,W.jsx)(y,{tone:`live`,children:f(`account.linked`)}):null]}),(0,W.jsx)(`div`,{className:`truncate text-[0.875rem] text-ink-3`,children:e.email})]}),(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,W.jsxs)(x,{variant:`secondary`,onClick:()=>k(e.url),children:[f(`setup.openAccount`),` `,(0,W.jsx)(d,{className:`size-4`})]}),(0,W.jsxs)(x,{variant:`ghost`,busy:r.isPending,onClick:()=>r.mutate(),children:[(0,W.jsx)(ee,{className:`size-4`}),` `,f(`nav.logout`)]})]})]}),e.signedIn?null:(0,W.jsxs)(`div`,{className:`flex flex-col gap-3 py-5`,children:[(0,W.jsx)(`p`,{className:`text-[0.875rem] text-ink-2`,children:f(`local.accountExpired`)}),(0,W.jsx)(`div`,{children:(0,W.jsx)(x,{busy:n.link.isPending,onClick:()=>n.link.mutate({}),children:f(`account.signInAgain`)})}),(0,W.jsx)(Q,{...n})]}),e.credits===null?null:(0,W.jsxs)(`div`,{className:`py-5`,children:[(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-3`,children:[(0,W.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,W.jsx)(`div`,{className:`text-[0.8125rem] text-ink-3`,children:f(`account.credits`)}),(0,W.jsx)(`div`,{className:`font-display font-semibold text-[1.375rem] tabular-nums leading-tight`,children:f(`nav.credits`,{n:Math.floor(e.credits).toLocaleString()})})]}),(0,W.jsxs)(x,{variant:`secondary`,onClick:()=>k(`${e.url}/billing`),children:[f(`account.topUp`),` `,(0,W.jsx)(d,{className:`size-4`})]})]}),e.expiring.length?(0,W.jsx)(`ul`,{className:`mt-3 flex flex-col gap-1 text-[0.8125rem] text-ink-3 tabular-nums`,children:e.expiring.map(e=>(0,W.jsxs)(`li`,{children:[f(`account.expires`,{n:Math.floor(e.credits).toLocaleString(),date:i.format(new Date(e.expiresAt))}),$[e.kind]?(0,W.jsxs)(`span`,{className:`text-ink-4`,children:[` · `,f($[e.kind])]}):null]},`${e.kind}:${e.expiresAt}`))}):null]}),(0,W.jsxs)(`div`,{className:`flex items-start gap-2.5 py-5 text-[0.875rem] text-ink-2 leading-snug last:pb-0`,children:[(0,W.jsx)(C,{className:`mt-0.5 size-4 shrink-0 text-cobalt`}),(0,W.jsx)(`p`,{children:f(`account.cloud`,{host:te(e.cloudUrl)})})]})]})}function ie({me:e,fromAccount:t}){let n=o(),[r,i]=(0,I.useState)(e.profile),s=e=>i(t=>({...t,...e})),c=H(r,async e=>{await a.put(`/api/studio/profile`,e),await n.invalidateQueries({queryKey:[`me`]})});return(0,W.jsx)(K,{title:f(`account.profile`),hint:f(`account.hint`),save:c,children:(0,W.jsxs)(`div`,{className:`flex flex-col gap-6 sm:flex-row sm:gap-8`,children:[(0,W.jsxs)(`div`,{className:`flex shrink-0 flex-col items-center gap-2 sm:w-32`,children:[(0,W.jsx)(`div`,{className:`flex size-20 items-center justify-center overflow-hidden rounded-full bg-ember-tint font-semibold text-[1.75rem] text-ember-strong ring-1 ring-border-soft`,children:e.user.image?(0,W.jsx)(`img`,{src:e.user.image,alt:``,className:`size-full object-cover`}):O(r.name)||`?`}),(0,W.jsx)(`p`,{className:`text-center text-[0.75rem] text-ink-3 leading-snug`,children:f(`account.initials`)})]}),(0,W.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col gap-5`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.name`)}),(0,W.jsx)(b,{value:r.name,maxLength:80,disabled:t,onChange:e=>s({name:e.target.value})}),(0,W.jsx)(`p`,{className:X,children:f(t?`account.fromAccount`:`account.nameHint`)})]}),(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.about`)}),(0,W.jsx)(g,{minRows:3,maxLength:2e3,value:r.about,onChange:e=>s({about:e.target.value})}),(0,W.jsx)(`p`,{className:X,children:f(`account.aboutHint`)})]}),(0,W.jsxs)(`div`,{className:`grid gap-5 sm:grid-cols-2`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.email`)}),(0,W.jsx)(b,{type:`email`,value:r.email,maxLength:200,disabled:t,onChange:e=>s({email:e.target.value})})]}),(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.phone`)}),(0,W.jsx)(b,{type:`tel`,value:r.phone,maxLength:40,onChange:e=>s({phone:e.target.value})})]})]})]})]})})}function ae({me:e}){let t=e.mode===`managed`,n=t||!!e.account;return(0,W.jsxs)(`div`,{className:`flex flex-col gap-9`,children:[(0,W.jsx)(K,{title:f(`account.connection`),hint:f(`account.connectionHint`),children:t?(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-4`,children:[(0,W.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`span`,{className:`truncate font-medium text-[0.9375rem]`,children:e.user.name}),(0,W.jsx)(y,{tone:`live`,children:f(`account.linked`)})]}),(0,W.jsx)(`div`,{className:`truncate text-[0.875rem] text-ink-3`,children:e.user.email})]}),e.manageUrl?(0,W.jsxs)(x,{variant:`secondary`,onClick:()=>k(`${e.manageUrl}/account`),children:[f(`setup.openAccount`),` `,(0,W.jsx)(d,{className:`size-4`})]}):null]}):e.account?(0,W.jsx)(re,{account:e.account}):(0,W.jsx)(ne,{})}),(0,W.jsx)(ie,{me:e,fromAccount:n},n?`account`:`own`),(0,W.jsx)(K,{title:f(`account.look`),hint:f(`account.lookHint`),children:(0,W.jsxs)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between gap-4 pb-4`,children:[(0,W.jsx)(`span`,{className:`font-medium text-[0.875rem]`,children:f(`nav.language`)}),(0,W.jsx)(T,{tone:`surface`})]}),(0,W.jsxs)(`div`,{className:`flex items-center justify-between gap-4 pt-4`,children:[(0,W.jsx)(`span`,{className:`font-medium text-[0.875rem]`,children:f(`nav.theme`)}),(0,W.jsx)(D,{})]})]})})]})}export{Y as a,K as c,H as d,B as f,N as h,G as i,U as l,R as m,Q as n,q as o,V as p,Z as r,J as s,ae as t,z as u};
|
|
1
|
+
import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{c as r,j as i,n as a,nt as o}from"./api-Byy4NRvQ.js";import{t as s}from"./createLucideIcon-q3MZMmnf.js";import{t as c}from"./check-CUpby_J9.js";import{n as l,r as u}from"./upload-DNRENWzQ.js";import{t as d}from"./external-link-MAL7vJYj.js";import{b as f,c as p,d as m,h,m as g,n as _,o as v,r as y,s as b,t as x,v as S}from"./ui-CwU0VR_B.js";import{F as ee,K as C,V as w,f as T,o as te,p as E,u as D,x as O}from"./index-udMtduDM.js";import{n as k}from"./LocalRuntime-CbA3kdFH.js";var A={name:`hard-drive`,size:24,node:[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]};A.node;var j=s(A),M={name:`layout-grid`,size:24,node:[[`rect`,{width:`7`,height:`7`,x:`3`,y:`3`,rx:`1`,key:`1g98yp`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`3`,rx:`1`,key:`6d4xhi`}],[`rect`,{width:`7`,height:`7`,x:`14`,y:`14`,rx:`1`,key:`nxv5o0`}],[`rect`,{width:`7`,height:`7`,x:`3`,y:`14`,rx:`1`,key:`1bb6yr`}]]};M.node;var N=s(M),P={name:`list`,size:24,node:[[`path`,{d:`M3 5h.01`,key:`18ugdj`}],[`path`,{d:`M3 12h.01`,key:`nlz23k`}],[`path`,{d:`M3 19h.01`,key:`noohij`}],[`path`,{d:`M8 5h13`,key:`1pao27`}],[`path`,{d:`M8 12h13`,key:`1za7za`}],[`path`,{d:`M8 19h13`,key:`m83p4d`}]]};P.node;var F=s(P),I=e(t(),1),L=e=>[`project-files`,e];function R(e){return i({queryKey:L(e),queryFn:()=>a.get(`/api/studio/projects/${e}/files`),refetchInterval:e=>e.state.data?.files.some(e=>e.status===`pending`)?2e3:!1})}function z(e,t){return r(`/api/studio/projects/${e}/files/${t}/content`)}function B(e,t=!1){let n=o(),[r,i]=(0,I.useState)(!1),[s,c]=(0,I.useState)(null),l=()=>n.invalidateQueries({queryKey:L(e)}),u=async e=>{c(null);try{await e()}catch(e){c(e.message)}finally{await l()}};return{busy:r,error:s,readOnly:t,upload:async(t,n)=>{i(!0),await u(async()=>{for(let r of n)await a.upload(`/api/studio/projects/${e}/files?kind=${t}`,r),await l()}),i(!1)},describe:(t,n)=>u(()=>a.patch(`/api/studio/projects/${e}/files/${t}`,{description:n})),remove:t=>u(()=>a.del(`/api/studio/projects/${e}/files/${t}`)),reindex:t=>u(()=>a.post(`/api/studio/projects/${e}/files/${t}/reindex`)),order:(t,n)=>u(()=>a.put(`/api/studio/projects/${e}/files/order`,{kind:t,ids:n}))}}function V(e,t){let n=`wz.layout.${e}`,[r,i]=(0,I.useState)(()=>{try{let e=localStorage.getItem(n);return e===`list`||e===`cards`?e:t}catch{return t}});return[r,()=>i(e=>{let t=e===`list`?`cards`:`list`;try{localStorage.setItem(n,t)}catch{}return t})]}function H(e,t,n=700){let[r,i]=(0,I.useState)(`idle`),[a,o]=(0,I.useState)(null),s=JSON.stringify(e),c=(0,I.useRef)(s),l=(0,I.useRef)({value:e,json:s,save:t});l.current={value:e,json:s,save:t};let u=(0,I.useRef)(Promise.resolve()),d=(0,I.useCallback)(()=>{let e=l.current;e.json!==c.current&&(c.current=e.json,i(`saving`),u.current=u.current.then(async()=>{try{await e.save(e.value),o(null),i(`saved`)}catch(e){c.current=``,o(e.message),i(`error`)}}))},[]);return(0,I.useEffect)(()=>{if(s===c.current)return;let e=setTimeout(d,n);return()=>clearTimeout(e)},[s,n,d]),(0,I.useEffect)(()=>d,[d]),{state:r,error:a}}function U(e){return e<1e6?`${Math.max(1,Math.round(e/1e3))} KB`:`${(e/1e6).toFixed(1)} MB`}var W=e(n(),1);function G({id:e,children:t}){return(0,W.jsx)(`div`,{id:e,className:`scroll-mt-24 max-md:scroll-mt-36`,children:t})}function K({title:e,hint:t,save:n,action:r,locked:i,plain:a,children:o}){return(0,W.jsxs)(`section`,{className:`flex flex-col gap-2.5`,children:[(0,W.jsxs)(`div`,{className:`flex items-start justify-between gap-4 px-1`,children:[(0,W.jsxs)(`div`,{className:`min-w-0`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,W.jsx)(`h2`,{className:`font-display font-semibold text-lg leading-tight`,children:e}),n?.state===`saving`?(0,W.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-[0.75rem] text-ink-3`,children:[(0,W.jsx)(m,{className:`size-3`}),` `,f(`project.saving`)]}):n?.state===`saved`?(0,W.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-[0.75rem] text-moss`,children:[(0,W.jsx)(c,{className:`size-3.5`}),` `,f(`settings.saved`)]}):null]}),t?(0,W.jsx)(`p`,{className:`mt-1 text-[0.875rem] text-ink-3 leading-snug`,children:t}):null]}),r?(0,W.jsx)(`div`,{className:`shrink-0`,children:r}):null]}),a?o:(0,W.jsxs)(_,{className:`p-3.5 sm:p-5`,children:[i?(0,W.jsx)(`fieldset`,{disabled:!0,className:`min-w-0`,children:o}):o,n?.state===`error`&&n.error?(0,W.jsx)(`p`,{className:`mt-4 text-[0.875rem] text-rose`,children:n.error}):null]})]})}function q({children:e,layout:t,onToggle:n}){return(0,W.jsxs)(`div`,{className:`mb-3 flex items-start gap-2`,children:[(0,W.jsx)(`div`,{className:`min-w-0 flex-1`,children:(0,W.jsx)(`div`,{className:`w-56 max-w-full transition-[width] duration-200 focus-within:w-full has-[input:not(:placeholder-shown)]:w-full`,children:e})}),(0,W.jsx)(v,{label:f(t===`list`?`project.viewCards`:`project.viewList`),onClick:n,children:t===`list`?(0,W.jsx)(N,{className:`size-4`}):(0,W.jsx)(F,{className:`size-4`})})]})}function J({value:e,onChange:t,onEnter:n,placeholder:r,busy:i}){return(0,W.jsxs)(`div`,{className:`relative`,children:[(0,W.jsx)(l,{className:`-translate-y-1/2 pointer-events-none absolute top-1/2 left-3 size-4 text-ink-4`}),(0,W.jsx)(`input`,{type:`search`,value:e,placeholder:r,onChange:e=>t(e.target.value),onKeyDown:e=>e.key===`Enter`&&n?.(),className:`h-9 w-full rounded-lg border border-input bg-card pr-3 pl-9 text-[0.875rem] text-ink outline-none transition placeholder:text-ink-4 focus:border-focus focus:ring-4 focus:ring-focus-glow`}),i?(0,W.jsx)(m,{className:`-translate-y-1/2 absolute top-1/2 right-3 size-4 text-ink-3`}):null]})}function Y({onFiles:e,accept:t,multiple:n=!0,className:r,children:i}){return(0,W.jsxs)(`label`,{onDragOver:e=>{e.preventDefault(),e.currentTarget.dataset.over=`1`},onDragLeave:e=>{delete e.currentTarget.dataset.over},onDrop:t=>{t.preventDefault(),delete t.currentTarget.dataset.over;let r=Array.from(t.dataTransfer.files);r.length&&e(n?r:r.slice(0,1))},className:h(`flex cursor-pointer items-center justify-center gap-2 rounded-lg border border-input border-dashed text-[0.875rem] text-ink-3 transition focus-within:border-focus focus-within:ring-4 focus-within:ring-focus-glow hover:border-ink-4 hover:text-ink data-[over]:border-focus data-[over]:bg-paper-2 data-[over]:text-ink`,r),children:[i,(0,W.jsx)(`input`,{className:`sr-only`,type:`file`,accept:t,multiple:n,onChange:t=>{let n=Array.from(t.target.files??[]);t.target.value=``,n.length&&e(n)}})]})}var X=`mt-1.5 text-[0.8125rem] text-ink-3`;function Z(e){let t=o(),[n,r]=(0,I.useState)(!1),i=C({mutationFn:e=>a.post(`/api/studio/account/link`,e),onSuccess:({url:e})=>{k(e),r(!0)}});return(0,I.useEffect)(()=>{if(!n)return;let e=setInterval(()=>void t.invalidateQueries({queryKey:[`me`]}),2e3),i=setTimeout(()=>r(!1),3e5);return()=>{clearInterval(e),clearTimeout(i)}},[n,t]),(0,I.useEffect)(()=>{e&&r(!1)},[e]),{link:i,waiting:n}}function Q({link:e,waiting:t}){return e.isError?(0,W.jsx)(`p`,{className:`text-[0.8125rem] text-rose`,children:f(`local.accountFailed`)}):t?(0,W.jsxs)(`p`,{className:`flex items-center gap-2 text-[0.8125rem] text-ink-3`,children:[(0,W.jsx)(m,{className:`size-3.5`}),` `,f(`local.accountWaiting`)]}):null}function ne(){let e=Z(!1),{link:t}=e,[n,r]=(0,I.useState)(``);return(0,W.jsxs)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:[(0,W.jsxs)(`div`,{className:`flex items-start gap-4 py-5 first:pt-0 last:pb-0`,children:[(0,W.jsx)(`div`,{className:`flex size-11 shrink-0 items-center justify-center rounded-full bg-paper-2 text-ink-2`,children:(0,W.jsx)(j,{className:`size-5`})}),(0,W.jsxs)(`div`,{className:`min-w-0`,children:[(0,W.jsx)(`div`,{className:`font-medium text-[0.9375rem]`,children:f(`account.local`)}),(0,W.jsx)(`p`,{className:`mt-1 text-[0.875rem] text-ink-3 leading-relaxed`,children:f(`account.localText`)})]})]}),E.account?(0,W.jsxs)(`div`,{className:`flex flex-col gap-4 py-5 last:pb-0`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`div`,{className:`font-medium text-[0.9375rem]`,children:f(`account.with`)}),(0,W.jsxs)(`ul`,{className:`mt-2 flex flex-col gap-2 text-[0.875rem] text-ink-2 leading-snug`,children:[(0,W.jsxs)(`li`,{className:`flex items-start gap-2.5`,children:[(0,W.jsx)(u,{className:`mt-0.5 size-4 shrink-0 text-amber`}),f(`account.withCredits`)]}),(0,W.jsxs)(`li`,{className:`flex items-start gap-2.5`,children:[(0,W.jsx)(w,{className:`mt-0.5 size-4 shrink-0 text-cobalt`}),f(`account.withCloud`)]})]})]}),(0,W.jsx)(`div`,{children:(0,W.jsx)(x,{busy:t.isPending&&!t.variables?.signup,onClick:()=>t.mutate({}),children:f(`account.signIn`)})})]}):null,E.account?(0,W.jsxs)(`form`,{className:`flex flex-col gap-3 py-5 last:pb-0`,onSubmit:e=>{e.preventDefault(),t.mutate({signup:!0,code:n.trim()||void 0})},children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`div`,{className:`font-medium text-[0.9375rem]`,children:f(`account.new`)}),(0,W.jsx)(`p`,{className:`mt-1 text-[0.875rem] text-ink-3 leading-relaxed`,children:f(`account.newHint`)})]}),(0,W.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,W.jsx)(b,{value:n,maxLength:64,autoComplete:`off`,spellCheck:!1,placeholder:f(`account.code`),"aria-label":f(`account.code`),onChange:e=>r(e.target.value),className:`w-56 font-mono text-[0.875rem]`}),(0,W.jsx)(x,{type:`submit`,variant:`secondary`,className:`h-11`,busy:t.isPending&&!!t.variables?.signup,children:f(`account.create`)})]}),(0,W.jsx)(Q,{...e})]}):null]})}var $={start:`account.kind.start`,monthly:`account.kind.monthly`,gift:`account.kind.gift`};function re({account:e}){let t=o(),n=Z(e.signedIn),r=C({mutationFn:()=>a.del(`/api/studio/account`),onSuccess:()=>t.invalidateQueries({queryKey:[`me`]})}),i=new Intl.DateTimeFormat(S,{dateStyle:`medium`});return(0,W.jsxs)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:[(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-3 py-5 first:pt-0`,children:[(0,W.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`span`,{className:`truncate font-medium text-[0.9375rem]`,children:e.name||e.email}),e.signedIn?(0,W.jsx)(y,{tone:`live`,children:f(`account.linked`)}):null]}),(0,W.jsx)(`div`,{className:`truncate text-[0.875rem] text-ink-3`,children:e.email})]}),(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,W.jsxs)(x,{variant:`secondary`,onClick:()=>k(e.url),children:[f(`setup.openAccount`),` `,(0,W.jsx)(d,{className:`size-4`})]}),(0,W.jsxs)(x,{variant:`ghost`,busy:r.isPending,onClick:()=>r.mutate(),children:[(0,W.jsx)(ee,{className:`size-4`}),` `,f(`nav.logout`)]})]})]}),e.signedIn?null:(0,W.jsxs)(`div`,{className:`flex flex-col gap-3 py-5`,children:[(0,W.jsx)(`p`,{className:`text-[0.875rem] text-ink-2`,children:f(`local.accountExpired`)}),(0,W.jsx)(`div`,{children:(0,W.jsx)(x,{busy:n.link.isPending,onClick:()=>n.link.mutate({}),children:f(`account.signInAgain`)})}),(0,W.jsx)(Q,{...n})]}),e.credits===null?null:(0,W.jsxs)(`div`,{className:`py-5`,children:[(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-3`,children:[(0,W.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,W.jsx)(`div`,{className:`text-[0.8125rem] text-ink-3`,children:f(`account.credits`)}),(0,W.jsx)(`div`,{className:`font-display font-semibold text-[1.375rem] tabular-nums leading-tight`,children:f(`nav.credits`,{n:Math.floor(e.credits).toLocaleString()})})]}),(0,W.jsxs)(x,{variant:`secondary`,onClick:()=>k(`${e.url}/billing`),children:[f(`account.topUp`),` `,(0,W.jsx)(d,{className:`size-4`})]})]}),e.expiring.length?(0,W.jsx)(`ul`,{className:`mt-3 flex flex-col gap-1 text-[0.8125rem] text-ink-3 tabular-nums`,children:e.expiring.map(e=>(0,W.jsxs)(`li`,{children:[f(`account.expires`,{n:Math.floor(e.credits).toLocaleString(),date:i.format(new Date(e.expiresAt))}),$[e.kind]?(0,W.jsxs)(`span`,{className:`text-ink-4`,children:[` · `,f($[e.kind])]}):null]},`${e.kind}:${e.expiresAt}`))}):null]}),(0,W.jsxs)(`div`,{className:`flex items-start gap-2.5 py-5 text-[0.875rem] text-ink-2 leading-snug last:pb-0`,children:[(0,W.jsx)(w,{className:`mt-0.5 size-4 shrink-0 text-cobalt`}),(0,W.jsx)(`p`,{children:f(`account.cloud`,{host:te(e.cloudUrl)})})]})]})}function ie({me:e,fromAccount:t}){let n=o(),[r,i]=(0,I.useState)(e.profile),s=e=>i(t=>({...t,...e})),c=H(r,async e=>{await a.put(`/api/studio/profile`,e),await n.invalidateQueries({queryKey:[`me`]})});return(0,W.jsx)(K,{title:f(`account.profile`),hint:f(`account.hint`),save:c,children:(0,W.jsxs)(`div`,{className:`flex flex-col gap-6 sm:flex-row sm:gap-8`,children:[(0,W.jsxs)(`div`,{className:`flex shrink-0 flex-col items-center gap-2 sm:w-32`,children:[(0,W.jsx)(`div`,{className:`flex size-20 items-center justify-center overflow-hidden rounded-full bg-ember-tint font-semibold text-[1.75rem] text-ember-strong ring-1 ring-border-soft`,children:e.user.image?(0,W.jsx)(`img`,{src:e.user.image,alt:``,className:`size-full object-cover`}):O(r.name)||`?`}),(0,W.jsx)(`p`,{className:`text-center text-[0.75rem] text-ink-3 leading-snug`,children:f(`account.initials`)})]}),(0,W.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col gap-5`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.name`)}),(0,W.jsx)(b,{value:r.name,maxLength:80,disabled:t,onChange:e=>s({name:e.target.value})}),(0,W.jsx)(`p`,{className:X,children:f(t?`account.fromAccount`:`account.nameHint`)})]}),(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.about`)}),(0,W.jsx)(g,{minRows:3,maxLength:2e3,value:r.about,onChange:e=>s({about:e.target.value})}),(0,W.jsx)(`p`,{className:X,children:f(`account.aboutHint`)})]}),(0,W.jsxs)(`div`,{className:`grid gap-5 sm:grid-cols-2`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.email`)}),(0,W.jsx)(b,{type:`email`,value:r.email,maxLength:200,disabled:t,onChange:e=>s({email:e.target.value})})]}),(0,W.jsxs)(`div`,{children:[(0,W.jsx)(p,{children:f(`account.phone`)}),(0,W.jsx)(b,{type:`tel`,value:r.phone,maxLength:40,onChange:e=>s({phone:e.target.value})})]})]})]})]})})}function ae({me:e}){let t=e.mode===`managed`,n=t||!!e.account;return(0,W.jsxs)(`div`,{className:`flex flex-col gap-9`,children:[(0,W.jsx)(K,{title:f(`account.connection`),hint:f(`account.connectionHint`),children:t?(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-4`,children:[(0,W.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`span`,{className:`truncate font-medium text-[0.9375rem]`,children:e.user.name}),(0,W.jsx)(y,{tone:`live`,children:f(`account.linked`)})]}),(0,W.jsx)(`div`,{className:`truncate text-[0.875rem] text-ink-3`,children:e.user.email})]}),e.manageUrl?(0,W.jsxs)(x,{variant:`secondary`,onClick:()=>k(`${e.manageUrl}/account`),children:[f(`setup.openAccount`),` `,(0,W.jsx)(d,{className:`size-4`})]}):null]}):e.account?(0,W.jsx)(re,{account:e.account}):(0,W.jsx)(ne,{})}),(0,W.jsx)(ie,{me:e,fromAccount:n},n?`account`:`own`),(0,W.jsx)(K,{title:f(`account.look`),hint:f(`account.lookHint`),children:(0,W.jsxs)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between gap-4 pb-4`,children:[(0,W.jsx)(`span`,{className:`font-medium text-[0.875rem]`,children:f(`nav.language`)}),(0,W.jsx)(T,{tone:`surface`})]}),(0,W.jsxs)(`div`,{className:`flex items-center justify-between gap-4 pt-4`,children:[(0,W.jsx)(`span`,{className:`font-medium text-[0.875rem]`,children:f(`nav.theme`)}),(0,W.jsx)(D,{})]})]})})]})}export{Y as a,K as c,H as d,B as f,N as h,G as i,U as l,R as m,Q as n,q as o,V as p,Z as r,J as s,ae as t,z as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{a as i,b as a,d as o,h as s,i as c,s as l}from"./ui-
|
|
1
|
+
import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{a as i,b as a,d as o,h as s,i as c,s as l}from"./ui-CwU0VR_B.js";var u=e(t(),1),d=e(n(),1);function f(e){switch(e.group){case`Added`:return{label:`NEW`,dot:`bg-moss`};case`Fixed`:return{label:`FIXED`,dot:`bg-amber`};case`Changed`:return{label:`CHANGED`,dot:`bg-cobalt`};case`Performance`:return{label:`PERFORMANCE`,dot:`bg-ember`};case`Deploy`:return{label:`DEPLOY`,dot:`bg-ink-4`};case`Docs`:return{label:`DOCS`,dot:`bg-ink-4`};default:return{label:`OTHER`,dot:`bg-ink-4`}}}var p=e=>e.version?e.version.replace(/^v/,``):a(`about.unreleased`),m=e=>e.timestamp?new Date(e.timestamp*1e3).toISOString().slice(0,10):``,h=e=>e.charAt(0).toUpperCase()+e.slice(1);function g(e,t){let n=t.trim().toLowerCase();if(!n)return e;let r=[];for(let t of e){if(`${p(t)} ${m(t)}`.toLowerCase().includes(n)){r.push(t);continue}let e=t.commits.filter(e=>`${f(e).label} ${e.scope??``} ${e.message}`.toLowerCase().includes(n));e.length>0&&r.push({...t,commits:e})}return r}async function _(e){if(e)try{let t=await fetch(`https://raw.githubusercontent.com/engenty/engenty-wizards/v${e}/apps/web/src/about/changelog.json`,{signal:AbortSignal.timeout(5e3)});if(t.ok)return await t.json()}catch{}return(await r(async()=>{let{default:e}=await import(`./changelog-DMAoeotb.js`);return{default:e}},[])).default}function v({open:e,onClose:t,version:n}){let[r,v]=(0,u.useState)(null),[y,b]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||r)return;let t=!1;return _(n).then(e=>{t||v(e)}),()=>{t=!0}},[e,r,n]),(0,u.useEffect)(()=>{e||b(``)},[e]);let x=r?g(r,y):[];return(0,d.jsxs)(c,{open:e,onClose:t,title:a(`about.changelog`),wide:!0,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[0.875rem] text-ink-3`,children:a(`about.changelogHint`)}),r===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(o,{})}):r.length===0?(0,d.jsx)(i,{children:a(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(l,{type:`search`,value:y,placeholder:a(`about.search`),onChange:e=>b(e.target.value)}),x.length===0?(0,d.jsx)(i,{children:a(`about.noMatches`)}):null,x.map(e=>(0,d.jsxs)(`section`,{className:`mt-6`,children:[(0,d.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[p(e),(0,d.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:m(e)})]}),(0,d.jsx)(`ul`,{className:`mt-3 flex flex-col gap-2`,children:e.commits.map(e=>{let t=f(e);return(0,d.jsxs)(`li`,{className:`flex items-start gap-2.5 text-[0.875rem]`,children:[(0,d.jsx)(`span`,{className:s(`mt-[7px] size-1.5 shrink-0 rounded-full`,t.dot)}),(0,d.jsxs)(`span`,{className:`min-w-0 text-ink-2`,children:[(0,d.jsxs)(`span`,{className:`font-semibold text-[0.75rem] text-ink tracking-wide`,children:[t.label,e.scope?` [${e.scope}]`:``]}),` `,h(e.message),e.breaking?(0,d.jsx)(`span`,{className:`ml-1.5 rounded-full bg-rose-tint px-1.5 py-0.5 font-medium text-[0.75rem] text-rose`,children:a(`about.breaking`)}):null]})]},e.id)})})]},e.version??`unreleased`))]})]})}export{v as ChangelogDialog};
|