@k2b/cloud 0.6.0-rc.0 → 0.7.0
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/README.md +2 -2
- package/package.json +30 -17
- package/scripts/browser-performance.ts +14 -0
- package/scripts/build-pdf-renderer.ts +39 -0
- package/scripts/build.ts +15 -35
- package/scripts/preload.ts +9 -12
- package/scripts/sync-recovery-smoke.ts +2 -1
- package/src/_internal/app-presentation.ts +14 -2
- package/src/_internal/capabilities.ts +108 -7
- package/src/_internal/capability-streams.ts +81 -0
- package/src/_internal/define-app.ts +62 -36
- package/src/_internal/heartbeat.ts +4 -0
- package/src/_internal/help-catalog.ts +27 -172
- package/src/_internal/help.ts +13 -76
- package/src/_internal/page-responses.ts +2 -0
- package/src/_internal/postgres-application-name.ts +30 -0
- package/src/_internal/process-identity.ts +15 -0
- package/src/_internal/process-sync.ts +1 -1
- package/src/_internal/registry-validation.ts +29 -25
- package/src/_internal/registry.ts +2 -53
- package/src/_internal/runtime-context.ts +5 -16
- package/src/_internal/server-timing.ts +13 -0
- package/src/_internal/status-preserving-ssr.ts +18 -6
- package/src/_internal/web-vitals-asset.ts +10 -0
- package/src/access/PermissionEditor.tsx +14 -153
- package/src/access/PrincipalPicker.tsx +104 -0
- package/src/access/ui.ts +2 -0
- package/src/ai/admin.ts +14 -13
- package/src/ai/assistant-models.ts +12 -1
- package/src/ai/audio-format.ts +34 -0
- package/src/ai/audio-tool.ts +90 -0
- package/src/ai/browser-code-contracts.ts +79 -0
- package/src/ai/browser.ts +2 -0
- package/src/ai/capabilities.ts +55 -36
- package/src/ai/capability-execution.ts +47 -12
- package/src/ai/chat/blocks.tsx +157 -95
- package/src/ai/chat/capability-result.ts +15 -0
- package/src/ai/chat/capability-table.tsx +2 -10
- package/src/ai/chat/composer-adapter.ts +48 -3
- package/src/ai/chat/message-actions.tsx +8 -7
- package/src/ai/chat/message-utils.ts +12 -1
- package/src/ai/chat/messages.ts +2 -0
- package/src/ai/chat/presentation.tsx +19 -48
- package/src/ai/chat/primitives.tsx +43 -1
- package/src/ai/chat/tool-disclosure.tsx +17 -2
- package/src/ai/chat/tool-groups.ts +52 -0
- package/src/ai/chat-quotas.ts +25 -0
- package/src/ai/chat-task-contracts.ts +9 -1
- package/src/ai/chat-tasks.ts +276 -43
- package/src/ai/client/controller.ts +110 -33
- package/src/ai/client/file-source.ts +12 -6
- package/src/ai/client/live-connection.ts +1 -0
- package/src/ai/client/projection.ts +22 -11
- package/src/ai/client/transport.ts +4 -0
- package/src/ai/code-capability-routes.ts +110 -0
- package/src/ai/code-capability-transport.ts +38 -0
- package/src/ai/code-mode-skill.ts +114 -0
- package/src/ai/code-runtime-tools.ts +166 -0
- package/src/ai/code-source-contracts.ts +202 -0
- package/src/ai/code-source-tools.ts +145 -0
- package/src/ai/compaction.ts +27 -6
- package/src/ai/data-analysis-skill.ts +10 -0
- package/src/ai/default-tools.ts +25 -2
- package/src/ai/dictation-runtime.ts +171 -0
- package/src/ai/dictations.ts +188 -0
- package/src/ai/draft-content.ts +34 -0
- package/src/ai/enrich.ts +6 -0
- package/src/ai/executor.ts +191 -31
- package/src/ai/file-content-version.ts +22 -0
- package/src/ai/file-context.ts +2 -1
- package/src/ai/file-media-type.ts +12 -0
- package/src/ai/file-reference-contracts.ts +15 -0
- package/src/ai/file-tools.ts +2 -1
- package/src/ai/files-store.ts +174 -71
- package/src/ai/fixtures/vision-encrypted.pdf +0 -0
- package/src/ai/grids-skill.ts +40 -10
- package/src/ai/http.ts +15 -8
- package/src/ai/index.ts +26 -2
- package/src/ai/inference-calls.ts +198 -0
- package/src/ai/live-events.ts +5 -1
- package/src/ai/memory-learning.ts +60 -28
- package/src/ai/message-queue.ts +167 -0
- package/src/ai/migrate.ts +187 -11
- package/src/ai/model-pricing.ts +43 -0
- package/src/ai/pdf-render-worker.ts +74 -0
- package/src/ai/pdf-render.fixture.ts +28 -0
- package/src/ai/pdf-render.ts +77 -0
- package/src/ai/projects-routes.ts +3 -3
- package/src/ai/projects.ts +20 -6
- package/src/ai/protocol.ts +3 -0
- package/src/ai/provider.ts +1 -8
- package/src/ai/quota-provider.ts +191 -0
- package/src/ai/quota-report.ts +152 -0
- package/src/ai/quotas-migrate.ts +45 -0
- package/src/ai/quotas.ts +168 -0
- package/src/ai/routes.ts +215 -11
- package/src/ai/run-timeout.ts +10 -0
- package/src/ai/runtime-tools.ts +3 -0
- package/src/ai/runtime.ts +55 -16
- package/src/ai/settings.ts +36 -8
- package/src/ai/short-id.ts +2 -2
- package/src/ai/skill-catalog.ts +0 -10
- package/src/ai/skill-search.ts +47 -0
- package/src/ai/skill-seeds.ts +105 -20
- package/src/ai/skill-tool.ts +16 -5
- package/src/ai/skills-routes.ts +32 -4
- package/src/ai/skills.ts +324 -155
- package/src/ai/store.ts +373 -191
- package/src/ai/stream.ts +5 -0
- package/src/ai/structured-runs.ts +4 -3
- package/src/ai/structured.ts +8 -1
- package/src/ai/system-prompt.ts +12 -2
- package/src/ai/timeline.ts +2 -5
- package/src/ai/todo-contracts.ts +20 -0
- package/src/ai/todo-tool.ts +16 -0
- package/src/ai/tool-audit.ts +30 -40
- package/src/ai/tools.ts +6 -0
- package/src/ai/transcription.ts +202 -0
- package/src/ai/turn-timing.ts +114 -0
- package/src/ai/types.ts +58 -10
- package/src/ai/ui.tsx +2 -0
- package/src/ai/usage.ts +118 -118
- package/src/ai/vision-tool.ts +39 -10
- package/src/api/admin-ai-projects.ts +2 -2
- package/src/api/admin-ai-quotas.ts +74 -0
- package/src/api/admin-ai-skills.ts +48 -5
- package/src/api/admin-ai-usage.ts +2 -2
- package/src/api/admin-core-settings.ts +25 -0
- package/src/api/admin-identity.ts +91 -51
- package/src/api/admin-rail.ts +58 -0
- package/src/api/announcements.ts +14 -0
- package/src/api/capabilities.ts +292 -43
- package/src/api/capability-streams.ts +262 -0
- package/src/api/help.ts +8 -44
- package/src/api/index.ts +6 -0
- package/src/api/mcp.ts +44 -26
- package/src/api/me-web-vitals.ts +35 -0
- package/src/api/me.ts +2 -0
- package/src/api/search/schemas.ts +26 -15
- package/src/api/search.bench.ts +1 -1
- package/src/api/search.ts +54 -17
- package/src/browser/CloudResourceSearch.tsx +640 -702
- package/src/browser/app-approval-vault.ts +19 -7
- package/src/browser/command-bridge.ts +109 -0
- package/src/browser/command-shortcuts.ts +76 -0
- package/src/browser/commands.ts +66 -0
- package/src/browser/navigation-search.ts +48 -0
- package/src/browser/notifications.ts +1 -1
- package/src/browser/resource-picker.tsx +5 -18
- package/src/browser/resource-search-dialog.ts +9 -0
- package/src/browser/resource-search-input.ts +46 -0
- package/src/browser/resource-search-messages.ts +76 -40
- package/src/browser/resource-search.ts +7 -0
- package/src/browser/search-bridge.ts +102 -0
- package/src/browser/search-commands.ts +59 -0
- package/src/browser/search.ts +13 -0
- package/src/browser/spotlight-position.ts +208 -0
- package/src/browser/testing.ts +5 -0
- package/src/browser/web-vitals.ts +25 -0
- package/src/capabilities/claims.ts +179 -0
- package/src/capabilities/client.ts +34 -0
- package/src/capabilities/command-link.ts +16 -0
- package/src/capabilities/executions.ts +298 -0
- package/src/capabilities/migrate.ts +81 -0
- package/src/capabilities/server.ts +114 -34
- package/src/capabilities/stream-body.ts +16 -0
- package/src/capabilities/streams.ts +28 -0
- package/src/cli/admin/ai-quotas.ts +161 -0
- package/src/cli/admin/ai-skills.ts +64 -0
- package/src/cli/admin/ai-usage.ts +53 -30
- package/src/cli/admin/app-credentials.ts +79 -0
- package/src/cli/admin/data.ts +17 -0
- package/src/cli/admin/index.ts +9 -0
- package/src/cli/capabilities.ts +29 -0
- package/src/cli/capability-streams.ts +25 -0
- package/src/cli/index.ts +3 -2
- package/src/config/env.ts +11 -8
- package/src/contracts/app.ts +23 -3
- package/src/contracts/capabilities.ts +68 -5
- package/src/contracts/capability-streams.ts +46 -0
- package/src/contracts/commands.ts +49 -0
- package/src/contracts/index.ts +6 -1
- package/src/contracts/rail-admin.ts +38 -0
- package/src/contracts/rail-preferences.ts +16 -0
- package/src/contracts/registry.ts +3 -27
- package/src/contracts/settings-types.ts +1 -1
- package/src/contracts/shared.ts +9 -5
- package/src/contracts/web-vitals.ts +27 -0
- package/src/desktop/solid.tsx +3 -3
- package/src/index.ts +0 -4
- package/src/server/index.ts +3 -0
- package/src/server/middleware/auth.ts +3 -2
- package/src/server/middleware/route-template.ts +9 -3
- package/src/server/middleware/runtime.ts +2 -1
- package/src/server/middleware/settings.ts +17 -11
- package/src/server/services/access-revision.ts +10 -0
- package/src/server/services/access.ts +1 -1
- package/src/server/services/index.ts +1 -0
- package/src/services/account-category-policy.ts +9 -0
- package/src/services/account-lifecycle/audit.ts +1 -1
- package/src/services/accounts/app.ts +12 -2
- package/src/services/accounts/email-write.ts +23 -0
- package/src/services/accounts/groups.ts +15 -4
- package/src/services/accounts/identities.ts +249 -0
- package/src/services/accounts/identity-reconciliation.ts +87 -0
- package/src/services/accounts/ipa-data.ts +2 -2
- package/src/services/accounts/local-groups.ts +2 -2
- package/src/services/accounts/posix.ts +45 -29
- package/src/services/accounts/users.ts +2 -2
- package/src/services/announcements/index.ts +69 -8
- package/src/services/audit/index.ts +1 -1
- package/src/services/cache-fill.ts +35 -0
- package/src/services/help/index.ts +109 -0
- package/src/services/help/maintenance.ts +25 -0
- package/src/services/help/store.ts +180 -0
- package/src/services/help/types.ts +46 -0
- package/src/services/identity/invocation-actor.ts +2 -2
- package/src/services/identity/key-config.ts +2 -1
- package/src/services/identity/key-ring.ts +1 -1
- package/src/services/index.ts +15 -0
- package/src/services/ipa/users.ts +268 -221
- package/src/services/legal-consent.ts +1 -1
- package/src/services/logging/index.ts +15 -13
- package/src/services/logging/trace.ts +2 -1
- package/src/services/logging/web-vitals.ts +81 -0
- package/src/services/mandates/index.ts +18 -4
- package/src/services/mandates/policy.ts +65 -2
- package/src/services/notifications/batches.ts +124 -57
- package/src/services/notifications/browser.ts +12 -0
- package/src/services/notifications/index.ts +0 -2
- package/src/services/notifications/platform.ts +0 -4
- package/src/services/oauth-tokens.ts +3 -3
- package/src/services/pdf/gotenberg.ts +107 -84
- package/src/services/pdf/index.ts +3 -0
- package/src/services/providers/local/users.ts +25 -16
- package/src/services/public-http.ts +101 -0
- package/src/services/rail-shortcuts.ts +95 -0
- package/src/services/rail-snapshot.ts +53 -0
- package/src/services/request-cache-redis.ts +28 -0
- package/src/services/session/index.ts +15 -2
- package/src/services/session/user.ts +62 -37
- package/src/services/settings/core-settings.ts +21 -28
- package/src/services/settings/defaults.ts +5 -0
- package/src/services/settings/store.ts +58 -84
- package/src/shared/ai-costs.ts +42 -0
- package/src/shared/ai-platform-prompt.ts +1 -1
- package/src/shared/ai-quotas.ts +136 -0
- package/src/shared/ai-usage.ts +6 -16
- package/src/shared/app-presentation.ts +21 -15
- package/src/shared/capability-messages.ts +14 -0
- package/src/shared/help.ts +6 -42
- package/src/shared/index.ts +6 -0
- package/src/ssr/AdminLayout.tsx +7 -5
- package/src/ssr/AdminSidebar.tsx +29 -15
- package/src/ssr/AppLaunchpad.island.tsx +24 -137
- package/src/ssr/AppLaunchpadPanel.tsx +181 -0
- package/src/ssr/BrowserPushRegistration.island.tsx +12 -0
- package/src/ssr/GlobalSearchDialog.tsx +167 -20
- package/src/ssr/GlobalSearchTrigger.island.tsx +33 -22
- package/src/ssr/Layout.tsx +17 -6
- package/src/ssr/LayoutBreadcrumbs.island.tsx +3 -5
- package/src/ssr/LayoutFooter.tsx +1 -1
- package/src/ssr/LayoutHeader.tsx +46 -14
- package/src/ssr/LayoutHelp.tsx +23 -22
- package/src/ssr/{HotkeysHelpRail.island.tsx → LayoutHelpTrigger.island.tsx} +18 -12
- package/src/ssr/LayoutRail.tsx +17 -9
- package/src/ssr/MobileNavigation.tsx +71 -0
- package/src/ssr/MobileProfileActions.tsx +49 -0
- package/src/ssr/ProfilePreferences.island.tsx +3 -36
- package/src/ssr/RailApps.island.tsx +2 -2
- package/src/ssr/RailEditor.tsx +17 -2
- package/src/ssr/WorkspaceNavigation.island.tsx +7 -0
- package/src/ssr/WorkspaceNavigationProvider.tsx +16 -0
- package/src/ssr/admin-navigation.ts +7 -1
- package/src/ssr/help.ts +29 -0
- package/src/ssr/islands/SearchBar.tsx +67 -0
- package/src/ssr/islands/index.ts +3 -1
- package/src/ssr/layout-context.ts +2 -2
- package/src/ssr/layout-help-search.ts +15 -0
- package/src/ssr/mobile-menu-history.ts +70 -0
- package/src/ssr/platform-messages.ts +16 -8
- package/src/ssr/profile-actions.ts +42 -0
- package/src/ssr/rail-context.ts +8 -4
- package/src/ssr/rail-messages.ts +2 -0
- package/src/ssr/rail-navigation.ts +3 -3
- package/src/ssr/workspace-navigation.ts +75 -0
- package/src/styles/effects.css +6 -6
- package/src/styles/global.css +54 -0
- package/src/styles/input.css +43 -41
- package/src/styles/resource-search.css +505 -0
- package/src/styles/utilities-feedback.css +13 -0
- package/src/styles/utilities-layout.css +1 -5
- package/src/styles/utilities-navigation.css +3 -39
- package/src/types/ambient.d.ts +5 -0
- package/src/workflows/ai/runtime.ts +5 -1
- package/src/workflows/ai/store.ts +2 -0
- package/src/workflows/ai-actions.ts +16 -13
- package/src/workflows/definition.ts +10 -5
- package/src/workflows/store/actions.ts +44 -20
- package/src/workflows/store/index.ts +2 -0
- package/src/workflows/store/runs.ts +36 -0
- package/src/workflows/store/worker-pool.ts +55 -0
- package/src/workflows/store/worker-runtime.ts +90 -0
- package/scripts/app-favicon.test.ts +0 -49
- package/src/ai/chat/blocks.render.test.tsx +0 -1108
- package/src/ai/kit-skill.ts +0 -22
- package/src/ai/usage-migrate.ts +0 -81
- package/src/browser/CloudResourceSearch.behavior.test.tsx +0 -74
- package/src/browser/notification-target.ts +0 -27
- package/src/contracts/notification-live.ts +0 -66
- package/src/services/notifications/live.ts +0 -50
- package/src/ssr/BrowserNotifications.island.tsx +0 -135
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +0 -96
- package/src/ssr/Layout.render.test.tsx +0 -148
- package/src/ssr/islands/SearchBar.island.tsx +0 -80
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { PrincipalSchema } from "../contracts/shared";
|
|
3
|
+
import { AiPriceSchema } from "./ai-costs";
|
|
4
|
+
|
|
5
|
+
export const AiBackgroundBudgetSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
enabled: z.boolean(),
|
|
8
|
+
warnAt: AiPriceSchema.nullable(),
|
|
9
|
+
stopAt: AiPriceSchema.positive(),
|
|
10
|
+
})
|
|
11
|
+
.strict()
|
|
12
|
+
.refine((value) => value.warnAt === null || value.warnAt < value.stopAt, "Warning must be below the stop amount.");
|
|
13
|
+
|
|
14
|
+
export const AiQuotaRuleSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
scope: z.string().min(1).max(128),
|
|
17
|
+
hours: z.number().int().min(1).max(8760),
|
|
18
|
+
anchor: z.iso.datetime(),
|
|
19
|
+
grants: z
|
|
20
|
+
.array(
|
|
21
|
+
z
|
|
22
|
+
.object({
|
|
23
|
+
principal: PrincipalSchema.refine((p) => p.type !== "public", "Chat quotas require an identity."),
|
|
24
|
+
displayName: z.string().max(300).optional(),
|
|
25
|
+
limit: AiPriceSchema.nullable(),
|
|
26
|
+
})
|
|
27
|
+
.strict(),
|
|
28
|
+
)
|
|
29
|
+
.max(1000)
|
|
30
|
+
.refine((grants) => new Set(grants.map((g) => JSON.stringify(g.principal))).size === grants.length, "Duplicate quota principal."),
|
|
31
|
+
})
|
|
32
|
+
.strict();
|
|
33
|
+
export const AiQuotaConfigSchema = z
|
|
34
|
+
.object({
|
|
35
|
+
enabled: z.boolean(),
|
|
36
|
+
revision: z.number().int().nonnegative(),
|
|
37
|
+
rules: z.array(AiQuotaRuleSchema).max(256),
|
|
38
|
+
unit: z.string().trim().min(1).max(16).optional(),
|
|
39
|
+
background: AiBackgroundBudgetSchema.optional(),
|
|
40
|
+
})
|
|
41
|
+
.strict()
|
|
42
|
+
.refine((c) => new Set(c.rules.map((r) => r.scope)).size === c.rules.length, "Duplicate quota scope.");
|
|
43
|
+
export type AiQuotaConfig = z.infer<typeof AiQuotaConfigSchema>;
|
|
44
|
+
export type AiQuotaRule = z.infer<typeof AiQuotaRuleSchema>;
|
|
45
|
+
export type AiQuotaBalance = {
|
|
46
|
+
scope: string;
|
|
47
|
+
limit: number | null;
|
|
48
|
+
input: number;
|
|
49
|
+
output: number;
|
|
50
|
+
used: number;
|
|
51
|
+
unknown: number;
|
|
52
|
+
estimated?: number;
|
|
53
|
+
resetsAt: string;
|
|
54
|
+
sources: string[];
|
|
55
|
+
sourceDetails?: { principal: z.infer<typeof PrincipalSchema>; displayName: string }[];
|
|
56
|
+
bypassed: boolean;
|
|
57
|
+
};
|
|
58
|
+
export type AiQuotaSnapshot = {
|
|
59
|
+
unit?: string;
|
|
60
|
+
enabled: boolean;
|
|
61
|
+
balances: AiQuotaBalance[];
|
|
62
|
+
usage: { model: string; input: number; output: number; unknown: number }[];
|
|
63
|
+
};
|
|
64
|
+
export type AiQuotaIdentity = { type: "user" | "service_account"; id: string; label: string; lastUsed: string | null };
|
|
65
|
+
export const AiQuotaIdentitySchema = z.object({ type: z.enum(["user", "service_account"]), id: z.uuid() });
|
|
66
|
+
export const AiQuotaResetSchema = AiQuotaIdentitySchema.extend({ scope: z.string().min(1).max(128), requestId: z.uuid() }).strict();
|
|
67
|
+
export const AiQuotaUsersQuerySchema = z
|
|
68
|
+
.object({
|
|
69
|
+
search: z.string().max(200).default(""),
|
|
70
|
+
page: z.coerce.number().int().positive().default(1),
|
|
71
|
+
})
|
|
72
|
+
.strict();
|
|
73
|
+
|
|
74
|
+
export type AiChatQuotaSnapshot = {
|
|
75
|
+
unit?: string;
|
|
76
|
+
unlimitedModels?: string[];
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
balances: Omit<AiQuotaBalance, "sources" | "sourceDetails">[];
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const AiQuotaReportQuerySchema = z.object({
|
|
82
|
+
view: z.enum(["users", "rules"]).default("users"),
|
|
83
|
+
range: z.enum(["24h", "7d", "30d", "90d"]).default("30d"),
|
|
84
|
+
until: z.iso.datetime({ offset: true }).optional(),
|
|
85
|
+
search: z.string().trim().max(200).default(""),
|
|
86
|
+
model: z.string().max(128).default(""),
|
|
87
|
+
status: z.enum(["all", "available", "exhausted", "unknown", "unlimited", "disabled"]).default("all"),
|
|
88
|
+
sort: z.enum(["label", "cost", "lastUsed"]).default("lastUsed"),
|
|
89
|
+
direction: z.enum(["asc", "desc"]).default("desc"),
|
|
90
|
+
page: z.coerce.number().int().min(1).max(1000000).default(1),
|
|
91
|
+
identity: z.uuid().optional(),
|
|
92
|
+
identityType: z.enum(["user", "service_account"]).default("user"),
|
|
93
|
+
});
|
|
94
|
+
export type AiQuotaReportQuery = z.infer<typeof AiQuotaReportQuerySchema>;
|
|
95
|
+
export type AiQuotaStatus = Exclude<AiQuotaReportQuery["status"], "all">;
|
|
96
|
+
export type AiQuotaReportRow = AiQuotaIdentity & {
|
|
97
|
+
cost: number | null;
|
|
98
|
+
input: number;
|
|
99
|
+
output: number;
|
|
100
|
+
calls: number;
|
|
101
|
+
measured: number;
|
|
102
|
+
estimated: number;
|
|
103
|
+
unknown: number;
|
|
104
|
+
status: AiQuotaStatus;
|
|
105
|
+
scopes: number;
|
|
106
|
+
exhausted: number;
|
|
107
|
+
balances: Pick<AiQuotaBalance, "scope" | "limit" | "used" | "unknown" | "bypassed">[];
|
|
108
|
+
};
|
|
109
|
+
export type AiQuotaReport = {
|
|
110
|
+
query: AiQuotaReportQuery;
|
|
111
|
+
since: string;
|
|
112
|
+
until: string;
|
|
113
|
+
asOf: string;
|
|
114
|
+
overview: {
|
|
115
|
+
cost: number | null;
|
|
116
|
+
accounts: number;
|
|
117
|
+
input: number;
|
|
118
|
+
output: number;
|
|
119
|
+
calls: number;
|
|
120
|
+
measured: number;
|
|
121
|
+
estimated: number;
|
|
122
|
+
unknown: number;
|
|
123
|
+
};
|
|
124
|
+
timeline: { cost: number | null; at: string; input: number; output: number; calls: number; measured: number; unknown: number }[];
|
|
125
|
+
models: { cost: number | null; model: string; input: number; output: number; calls: number; measured: number; unknown: number }[];
|
|
126
|
+
selected: AiQuotaIdentity | null;
|
|
127
|
+
items: AiQuotaReportRow[];
|
|
128
|
+
total: number;
|
|
129
|
+
page: number;
|
|
130
|
+
perPage: number;
|
|
131
|
+
};
|
|
132
|
+
export const aiQuotaHref = (query: Partial<AiQuotaReportQuery>) => {
|
|
133
|
+
const params = new URLSearchParams({ tab: "ai-quotas" });
|
|
134
|
+
for (const [key, value] of Object.entries(query)) if (value !== undefined && value !== "") params.set(key, String(value));
|
|
135
|
+
return `/admin/settings?${params}`;
|
|
136
|
+
};
|
package/src/shared/ai-usage.ts
CHANGED
|
@@ -11,28 +11,18 @@ export const AiUsageQuerySchema = z.object({
|
|
|
11
11
|
modelProfileId: text,
|
|
12
12
|
providerModel: text,
|
|
13
13
|
appId: text,
|
|
14
|
+
workflowId: z.uuid().optional(),
|
|
15
|
+
workflowRunId: z.uuid().optional(),
|
|
14
16
|
view: z.enum(AI_USAGE_VIEWS).default("overview"),
|
|
15
|
-
kind: z.enum(["chat", "background"
|
|
16
|
-
status: z
|
|
17
|
-
.enum([
|
|
18
|
-
"completed",
|
|
19
|
-
"failed",
|
|
20
|
-
"aborted",
|
|
21
|
-
"queued",
|
|
22
|
-
"running",
|
|
23
|
-
"waiting_for_action",
|
|
24
|
-
"rejected",
|
|
25
|
-
"pending",
|
|
26
|
-
"waiting_for_approval",
|
|
27
|
-
"waiting_for_frontend",
|
|
28
|
-
])
|
|
29
|
-
.optional(),
|
|
17
|
+
kind: z.enum(["chat", "background"]).optional(),
|
|
18
|
+
status: z.enum(["completed", "failed", "running"]).optional(),
|
|
30
19
|
task: text,
|
|
31
20
|
errorCode: text,
|
|
32
21
|
search: z.string().trim().max(200).optional(),
|
|
33
22
|
rating: z.enum(["up", "down"]).optional(),
|
|
34
23
|
reason: z.enum(AI_USAGE_REASONS).optional(),
|
|
35
|
-
|
|
24
|
+
direction: z.enum(["asc", "desc"]).default("desc"),
|
|
25
|
+
sort: z.enum(["newest", "runs", "tokens", "cost", "errors", "negative", "negativeRate"]).default("runs"),
|
|
36
26
|
page: z.coerce.number().int().min(1).max(1000000).default(1),
|
|
37
27
|
perPage: z.coerce.number().int().min(1).max(100).default(50),
|
|
38
28
|
});
|
|
@@ -9,21 +9,22 @@ const mergedTranslation = (catalog: AppPresentationCatalog, requestedLocale: str
|
|
|
9
9
|
return canonical ? [[canonical, translation] as const] : [];
|
|
10
10
|
}),
|
|
11
11
|
);
|
|
12
|
-
const overlays = localeFallbackChain(requestedLocale, baseLocale)
|
|
13
|
-
|
|
14
|
-
(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
const overlays = localeFallbackChain(requestedLocale, baseLocale)
|
|
13
|
+
.filter((locale) => locale !== baseLocale)
|
|
14
|
+
.reverse();
|
|
15
|
+
return overlays.reduce<AppPresentationTranslation>((current, locale) => {
|
|
16
|
+
const next = byLocale.get(locale);
|
|
17
|
+
if (!next) return current;
|
|
18
|
+
return {
|
|
19
|
+
...current,
|
|
20
|
+
...next,
|
|
21
|
+
adminGroups: { ...current.adminGroups, ...next.adminGroups },
|
|
22
|
+
adminLinks: { ...current.adminLinks, ...next.adminLinks },
|
|
23
|
+
legalLinks: { ...current.legalLinks, ...next.legalLinks },
|
|
24
|
+
searchLinks: { ...current.searchLinks, ...next.searchLinks },
|
|
25
|
+
searchLinkDescriptions: { ...current.searchLinkDescriptions, ...next.searchLinkDescriptions },
|
|
26
|
+
};
|
|
27
|
+
}, {});
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
/** Resolve only human presentation; stable app identity, routes, icons, and authorization stay untouched. */
|
|
@@ -40,6 +41,11 @@ export const resolveAppPresentation = <T extends AppMeta>(app: T, requestedLocal
|
|
|
40
41
|
label: (group.id && translation.adminGroups?.[group.id]) || group.label,
|
|
41
42
|
links: group.links.map((link) => ({ ...link, label: translation.adminLinks?.[link.href] ?? link.label })),
|
|
42
43
|
})),
|
|
44
|
+
searchLinks: app.searchLinks?.map((link) => ({
|
|
45
|
+
...link,
|
|
46
|
+
label: translation.searchLinks?.[link.href] ?? link.label,
|
|
47
|
+
description: translation.searchLinkDescriptions?.[link.href] ?? link.description,
|
|
48
|
+
})),
|
|
43
49
|
legalLinks: app.legalLinks?.map((link) => ({ ...link, label: translation.legalLinks?.[link.href] ?? link.label })),
|
|
44
50
|
};
|
|
45
51
|
};
|
|
@@ -11,10 +11,17 @@ const catalog = i18n.define({
|
|
|
11
11
|
catalogLimit: ({ max }: { max: number }) => `Capability catalog limit must be between 1 and ${max}`,
|
|
12
12
|
cloudUnavailable: "Cloud is unavailable",
|
|
13
13
|
deadlineExceeded: "The capability deadline was exceeded",
|
|
14
|
+
idempotencyConflict: "This Idempotency-Key was already used with different input",
|
|
15
|
+
idempotencyInProgress:
|
|
16
|
+
"An identical call with this Idempotency-Key is still running; verify the outcome with a Query before retrying",
|
|
14
17
|
idempotencyInvalid: "Idempotency-Key is invalid",
|
|
15
18
|
idempotencyNotAllowed: "This Action does not support idempotent retries; omit Idempotency-Key",
|
|
16
19
|
idempotencyOnlyActions: "Idempotency-Key is only valid for Actions that require it",
|
|
17
20
|
idempotencyRequired: "This Action requires an Idempotency-Key",
|
|
21
|
+
idempotencyResultNotRetained:
|
|
22
|
+
"This Action already succeeded, but its result was too large to retain; read the current state with a Query",
|
|
23
|
+
idempotencyUncertain:
|
|
24
|
+
"An earlier call with this Idempotency-Key ended with an unknown outcome; verify the current state with a Query",
|
|
18
25
|
inputNotSerializable: "Capability input must be JSON-serializable",
|
|
19
26
|
inputSchemaMismatch: "Capability input did not match the registered schema",
|
|
20
27
|
invalidCapabilityError: ({ appId }: { appId: string }) => `App ${appId} returned an invalid capability error`,
|
|
@@ -39,10 +46,17 @@ const catalog = i18n.define({
|
|
|
39
46
|
catalogLimit: ({ max }) => `Das Limit des Capability-Katalogs muss zwischen 1 und ${max} liegen`,
|
|
40
47
|
cloudUnavailable: "Cloud ist nicht verfügbar",
|
|
41
48
|
deadlineExceeded: "Das Zeitlimit der Capability wurde überschritten",
|
|
49
|
+
idempotencyConflict: "Dieser Idempotency-Key wurde bereits mit anderen Eingaben verwendet",
|
|
50
|
+
idempotencyInProgress:
|
|
51
|
+
"Ein identischer Aufruf mit diesem Idempotency-Key läuft noch; prüfe das Ergebnis mit einer Query, bevor du erneut aufrufst",
|
|
42
52
|
idempotencyInvalid: "Der Idempotency-Key ist ungültig",
|
|
43
53
|
idempotencyNotAllowed: "Diese Action unterstützt keine idempotenten Wiederholungen; Idempotency-Key weglassen",
|
|
44
54
|
idempotencyOnlyActions: "Ein Idempotency-Key ist nur für Actions gültig, die ihn voraussetzen",
|
|
45
55
|
idempotencyRequired: "Diese Action benötigt einen Idempotency-Key",
|
|
56
|
+
idempotencyResultNotRetained:
|
|
57
|
+
"Diese Action war bereits erfolgreich, ihr Ergebnis war jedoch zu groß zum Aufbewahren; lies den aktuellen Stand mit einer Query",
|
|
58
|
+
idempotencyUncertain:
|
|
59
|
+
"Ein früherer Aufruf mit diesem Idempotency-Key endete mit unbekanntem Ergebnis; prüfe den aktuellen Stand mit einer Query",
|
|
46
60
|
inputNotSerializable: "Die Capability-Eingabe muss als JSON serialisierbar sein",
|
|
47
61
|
inputSchemaMismatch: "Die Capability-Eingabe entspricht nicht dem registrierten Schema",
|
|
48
62
|
invalidCapabilityError: ({ appId }) => `App ${appId} hat einen ungültigen Capability-Fehler zurückgegeben`,
|
package/src/shared/help.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { localeFallbackChain } from "./locale";
|
|
2
2
|
|
|
3
3
|
export type HelpDocumentManifest = {
|
|
4
|
-
/** Resolved content locale
|
|
4
|
+
/** Resolved content locale on central Help manifests. */
|
|
5
5
|
locale?: string;
|
|
6
6
|
id: string;
|
|
7
7
|
title: string;
|
|
@@ -13,56 +13,20 @@ export type HelpDocumentManifest = {
|
|
|
13
13
|
url: string;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export type HelpManifest = {
|
|
17
|
-
manifestHash: string;
|
|
18
|
-
pageBase: string;
|
|
19
|
-
/** Base language that owns the complete logical article set. Absent on legacy registrations. */
|
|
20
|
-
baseLocale?: string;
|
|
21
|
-
/** Localized metadata variants keyed by canonical BCP 47 locale. */
|
|
22
|
-
documentsByLocale?: Readonly<Record<string, readonly HelpDocumentManifest[]>>;
|
|
23
|
-
documents: readonly HelpDocumentManifest[];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
16
|
export type HelpSearchPayload = {
|
|
27
|
-
/** Resolved request locale.
|
|
28
|
-
locale
|
|
17
|
+
/** Resolved request locale. */
|
|
18
|
+
locale: string;
|
|
29
19
|
ids: string[];
|
|
30
20
|
};
|
|
31
21
|
|
|
32
22
|
export type HelpDocumentPayload = {
|
|
33
|
-
/** Resolved request locale.
|
|
34
|
-
locale
|
|
23
|
+
/** Resolved request locale. */
|
|
24
|
+
locale: string;
|
|
35
25
|
id: string;
|
|
36
26
|
title: string;
|
|
37
27
|
markdown: string;
|
|
38
28
|
html: string;
|
|
39
29
|
};
|
|
40
30
|
|
|
41
|
-
export type ResolvedHelpManifest = Omit<HelpManifest, "documentsByLocale" | "documents"> & {
|
|
42
|
-
locale: string;
|
|
43
|
-
documents: readonly HelpDocumentManifest[];
|
|
44
|
-
};
|
|
45
|
-
|
|
46
31
|
/** Exact locale, then BCP 47 ancestors, then the corpus base locale. */
|
|
47
32
|
export const helpLocaleChain = localeFallbackChain;
|
|
48
|
-
|
|
49
|
-
/** Resolve localized Help metadata per article while preserving the base article set and order. */
|
|
50
|
-
export const resolveHelpManifest = (manifest: HelpManifest, requestedLocale: string): ResolvedHelpManifest => {
|
|
51
|
-
const baseLocale = canonicalLocale(manifest.baseLocale ?? "en") ?? "en";
|
|
52
|
-
const variants = manifest.documentsByLocale ?? {};
|
|
53
|
-
const chain = helpLocaleChain(requestedLocale, baseLocale);
|
|
54
|
-
const selectedLocale = chain.find((locale) => locale === baseLocale || variants[locale] !== undefined) ?? baseLocale;
|
|
55
|
-
const overlays = [...chain.slice(0, chain.indexOf(baseLocale)), baseLocale].reverse();
|
|
56
|
-
const byId = new Map(manifest.documents.map((document) => [document.id, document]));
|
|
57
|
-
for (const locale of overlays) {
|
|
58
|
-
if (locale === baseLocale) continue;
|
|
59
|
-
for (const document of variants[locale] ?? []) byId.set(document.id, { ...byId.get(document.id), ...document });
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
manifestHash: manifest.manifestHash,
|
|
63
|
-
pageBase: manifest.pageBase,
|
|
64
|
-
baseLocale,
|
|
65
|
-
locale: selectedLocale,
|
|
66
|
-
documents: manifest.documents.map((document) => ({ ...(byId.get(document.id) ?? document), locale: selectedLocale })),
|
|
67
|
-
};
|
|
68
|
-
};
|
package/src/shared/index.ts
CHANGED
package/src/ssr/AdminLayout.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AppWorkspace } from "@k2b/ui";
|
|
2
2
|
import type { JSX } from "solid-js/jsx-runtime";
|
|
3
|
-
import type {
|
|
3
|
+
import type { RailSnapshot } from "../contracts/rail-preferences";
|
|
4
4
|
import { getLocale } from "../server/locale";
|
|
5
5
|
import type { LayoutAnnouncementsState } from "../server/middleware/settings";
|
|
6
6
|
import AdminSidebar from "./AdminSidebar";
|
|
@@ -10,7 +10,7 @@ import { getLocalizedRuntimeContext, type RuntimeContext } from "./runtime";
|
|
|
10
10
|
|
|
11
11
|
type Breadcrumb = { title: string; href?: string };
|
|
12
12
|
type AdminLayoutContext = {
|
|
13
|
-
get(key: "railPreferences"):
|
|
13
|
+
get(key: "railPreferences"): RailSnapshot | undefined;
|
|
14
14
|
get(key: "user"): any;
|
|
15
15
|
get(key: "page"): any;
|
|
16
16
|
get(key: "runtime"): RuntimeContext;
|
|
@@ -25,7 +25,9 @@ type Props = {
|
|
|
25
25
|
/** Disable when a bounded child such as SettingsPage owns scrolling. */
|
|
26
26
|
scroll?: boolean;
|
|
27
27
|
};
|
|
28
|
-
export default function AdminLayout(
|
|
28
|
+
export default function AdminLayout(props: Props) {
|
|
29
|
+
// Keep children lazy so Layout installs the request locale before rendering them.
|
|
30
|
+
const { c, title, scroll = true } = props;
|
|
29
31
|
const locale = getLocale(c);
|
|
30
32
|
const t = platformMessages.resolve([locale]).t;
|
|
31
33
|
const url = new URL(c.req.raw.url);
|
|
@@ -40,11 +42,11 @@ export default function AdminLayout({ children, c, title, scroll = true }: Props
|
|
|
40
42
|
}
|
|
41
43
|
return (
|
|
42
44
|
<Layout c={c} fullWidth fullPage title={breadcrumbs}>
|
|
43
|
-
<AppWorkspace class="min-h-0 flex-1" resizable={false}>
|
|
45
|
+
<AppWorkspace mobileSurface="flush" class="min-h-0 flex-1" resizable={false}>
|
|
44
46
|
<AdminSidebar currentPath={currentPath} apps={runtime.apps} />
|
|
45
47
|
<AppWorkspace.Content>
|
|
46
48
|
<AppWorkspace.Main scroll={scroll} class={scroll ? "p-[var(--ui-space-shell)]" : undefined}>
|
|
47
|
-
{children}
|
|
49
|
+
{props.children}
|
|
48
50
|
</AppWorkspace.Main>
|
|
49
51
|
</AppWorkspace.Content>
|
|
50
52
|
</AppWorkspace>
|
package/src/ssr/AdminSidebar.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import WorkspaceNavigation from "./WorkspaceNavigation.island";
|
|
1
2
|
import { AppWorkspace, useLocale } from "@k2b/ui";
|
|
2
3
|
import { activeAdminHref } from "./admin-active-link";
|
|
3
4
|
import { type AdminLink, buildAdminGroups } from "./admin-navigation";
|
|
@@ -31,20 +32,33 @@ export default function AdminSidebar({ currentPath, apps }: { currentPath: strin
|
|
|
31
32
|
const groups = () => buildAdminGroups(apps, locale());
|
|
32
33
|
|
|
33
34
|
return (
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
<>
|
|
36
|
+
<WorkspaceNavigation
|
|
37
|
+
label={t().admin}
|
|
38
|
+
items={groups().map((group) => ({
|
|
39
|
+
id: group.label,
|
|
40
|
+
label: group.label,
|
|
41
|
+
children: group.links.map((link) => ({
|
|
42
|
+
id: link.href,
|
|
43
|
+
label: link.label,
|
|
44
|
+
href: link.href,
|
|
45
|
+
icon: `ti ${link.icon}`,
|
|
46
|
+
active:
|
|
47
|
+
link.href ===
|
|
48
|
+
activeAdminHref(
|
|
49
|
+
currentPath,
|
|
50
|
+
groups().flatMap((entry) => entry.links.map((entry) => entry.href)),
|
|
51
|
+
),
|
|
52
|
+
})),
|
|
53
|
+
}))}
|
|
54
|
+
/>
|
|
55
|
+
<AppWorkspace.Sidebar resizable={false}>
|
|
56
|
+
<AppWorkspace.SidebarDesktop>
|
|
57
|
+
<AppWorkspace.SidebarBody scrollPreserveKey="admin-sidebar">
|
|
58
|
+
<AdminNavigation currentPath={currentPath} groups={groups()} />
|
|
59
|
+
</AppWorkspace.SidebarBody>
|
|
60
|
+
</AppWorkspace.SidebarDesktop>
|
|
61
|
+
</AppWorkspace.Sidebar>
|
|
62
|
+
</>
|
|
49
63
|
);
|
|
50
64
|
}
|
|
@@ -1,42 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { AppLaunchpadPanel, type AppLaunchpadApp, type AppLaunchpadLegalLink, type AppLaunchpadContext } from "./AppLaunchpadPanel";
|
|
2
|
+
import { openCloudMobileMenu } from "./MobileNavigation";
|
|
3
|
+
export type { AppLaunchpadApp, AppLaunchpadLegalLink } from "./AppLaunchpadPanel";
|
|
4
|
+
import { dialogCore, IconButton, Tooltip, useLocale } from "@k2b/ui";
|
|
5
|
+
import { createEffect } from "solid-js";
|
|
3
6
|
import { platformMessages } from "./platform-messages";
|
|
4
|
-
import { openRailEditor } from "./RailEditor";
|
|
5
|
-
import { readRailContext } from "./rail-context";
|
|
6
|
-
import { railMessages } from "./rail-messages";
|
|
7
|
-
import { projectRailNavigation } from "./rail-navigation";
|
|
8
|
-
|
|
9
|
-
export type AppLaunchpadApp = {
|
|
10
|
-
id: string;
|
|
11
|
-
iconClass: string;
|
|
12
|
-
label: string;
|
|
13
|
-
href: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
accent?: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type AppLaunchpadLegalLink = {
|
|
19
|
-
label: string;
|
|
20
|
-
href: string;
|
|
21
|
-
icon?: string;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
type AppLaunchpadContext = {
|
|
25
|
-
apps: AppLaunchpadApp[];
|
|
26
|
-
legalLinks: AppLaunchpadLegalLink[];
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
type AppIconPaletteEntry = { from: string };
|
|
30
|
-
|
|
31
|
-
const appIconPalette: readonly [AppIconPaletteEntry, ...AppIconPaletteEntry[]] = [
|
|
32
|
-
{ from: "#2563eb" },
|
|
33
|
-
{ from: "#059669" },
|
|
34
|
-
{ from: "#7c3aed" },
|
|
35
|
-
{ from: "#d97706" },
|
|
36
|
-
{ from: "#e11d48" },
|
|
37
|
-
{ from: "#0891b2" },
|
|
38
|
-
{ from: "#52525b" },
|
|
39
|
-
];
|
|
40
7
|
|
|
41
8
|
declare global {
|
|
42
9
|
interface Window {
|
|
@@ -47,17 +14,6 @@ declare global {
|
|
|
47
14
|
}
|
|
48
15
|
}
|
|
49
16
|
|
|
50
|
-
const paletteForId = (id: string) => {
|
|
51
|
-
let hash = 0;
|
|
52
|
-
for (let i = 0; i < id.length; i++) hash = (hash + id.charCodeAt(i)) % appIconPalette.length;
|
|
53
|
-
return appIconPalette[hash] ?? appIconPalette[0];
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const appIconStyle = (app: AppLaunchpadApp) => {
|
|
57
|
-
const tone = /^#[0-9a-f]{6}$/i.test(app.accent ?? "") ? app.accent! : paletteForId(app.id).from;
|
|
58
|
-
return `--app-icon-color:${tone}`;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
17
|
const readEmbeddedContext = (): AppLaunchpadContext | undefined => {
|
|
62
18
|
if (typeof document === "undefined") return undefined;
|
|
63
19
|
const element = document.getElementById("cloud-app-launchpad-data");
|
|
@@ -69,6 +25,7 @@ const readEmbeddedContext = (): AppLaunchpadContext | undefined => {
|
|
|
69
25
|
if (!Array.isArray(parsed.apps)) return undefined;
|
|
70
26
|
return {
|
|
71
27
|
apps: parsed.apps,
|
|
28
|
+
profile: parsed.profile?.name && (parsed.profile.theme === "dark" || parsed.profile.theme === "light") ? parsed.profile : undefined,
|
|
72
29
|
legalLinks: Array.isArray(parsed.legalLinks) ? parsed.legalLinks : [],
|
|
73
30
|
};
|
|
74
31
|
} catch {
|
|
@@ -76,99 +33,28 @@ const readEmbeddedContext = (): AppLaunchpadContext | undefined => {
|
|
|
76
33
|
}
|
|
77
34
|
};
|
|
78
35
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<ScrollArea class="launchpad-panel mx-auto max-h-[min(86vh,var(--ui-dialog-available-height))] w-[var(--ui-dialog-available-width)] max-w-[var(--ui-dialog-available-width)] overscroll-contain p-4 text-primary sm:w-fit sm:p-6 md:p-7 dark:text-white">
|
|
86
|
-
<Show when={rail}>
|
|
87
|
-
<div class="mb-2 flex justify-end">
|
|
88
|
-
<IconButton
|
|
89
|
-
variant="ghost"
|
|
90
|
-
size="sm"
|
|
91
|
-
label={t().customize}
|
|
92
|
-
tooltip={t().customize}
|
|
93
|
-
tooltipDelay={0}
|
|
94
|
-
onClick={() => {
|
|
95
|
-
props.close();
|
|
96
|
-
openRailEditor(locale());
|
|
97
|
-
}}
|
|
98
|
-
>
|
|
99
|
-
<i class="ti ti-adjustments-horizontal" aria-hidden="true" />
|
|
100
|
-
</IconButton>
|
|
101
|
-
</div>
|
|
102
|
-
</Show>
|
|
103
|
-
<Show when={shortcuts().length > 0}>
|
|
104
|
-
<section class="mb-5 flex flex-col gap-3" aria-label={t().shortcuts}>
|
|
105
|
-
<h3 class="text-center text-sm font-medium">{t().shortcuts}</h3>
|
|
106
|
-
<div class="flex flex-wrap justify-center gap-2">
|
|
107
|
-
<For each={shortcuts()}>
|
|
108
|
-
{(shortcut) => (
|
|
109
|
-
<a
|
|
110
|
-
href={shortcut.href}
|
|
111
|
-
class="inline-flex items-center gap-2 rounded-lg border border-[var(--ui-border)] px-3 py-2 text-sm"
|
|
112
|
-
>
|
|
113
|
-
<i class={shortcut.iconClass} aria-hidden="true" />
|
|
114
|
-
{shortcut.label}
|
|
115
|
-
</a>
|
|
116
|
-
)}
|
|
117
|
-
</For>
|
|
118
|
-
</div>
|
|
119
|
-
</section>
|
|
120
|
-
</Show>
|
|
121
|
-
<div class="flex flex-wrap justify-center gap-x-4 gap-y-4 sm:gap-x-7 sm:gap-y-6">
|
|
122
|
-
<For each={props.apps}>
|
|
123
|
-
{(app) => (
|
|
124
|
-
<a
|
|
125
|
-
href={app.href}
|
|
126
|
-
class="group flex w-[4.75rem] min-w-0 flex-col items-center gap-1.5 rounded-2xl p-1 text-center outline-none focus-visible:ring-2 focus-visible:ring-white/60 sm:w-[6.25rem] sm:gap-2 sm:p-2"
|
|
127
|
-
>
|
|
128
|
-
<span
|
|
129
|
-
class="app-icon grid h-12 w-12 place-items-center rounded-[0.95rem] text-[1.25rem] sm:h-16 sm:w-16 sm:rounded-[1.25rem] sm:text-[1.7rem]"
|
|
130
|
-
style={appIconStyle(app)}
|
|
131
|
-
>
|
|
132
|
-
<i class={app.iconClass} />
|
|
133
|
-
</span>
|
|
134
|
-
<span class="max-w-full truncate text-[11px] font-medium text-primary sm:text-xs dark:text-white">{app.label}</span>
|
|
135
|
-
</a>
|
|
136
|
-
)}
|
|
137
|
-
</For>
|
|
138
|
-
</div>
|
|
139
|
-
<Show when={props.legalLinks.length > 0}>
|
|
140
|
-
<div class="mt-7 flex flex-wrap justify-center text-[11px] text-dimmed dark:text-white/56">
|
|
141
|
-
<For each={props.legalLinks}>
|
|
142
|
-
{(link) => (
|
|
143
|
-
<a
|
|
144
|
-
href={link.href}
|
|
145
|
-
class="inline-flex items-center gap-1.5 rounded-md px-2 py-1 transition-colors hover:text-primary dark:hover:text-white"
|
|
146
|
-
>
|
|
147
|
-
<i class={link.icon ?? "ti ti-file-text"} />
|
|
148
|
-
{link.label}
|
|
149
|
-
</a>
|
|
150
|
-
)}
|
|
151
|
-
</For>
|
|
152
|
-
</div>
|
|
153
|
-
</Show>
|
|
154
|
-
</ScrollArea>
|
|
155
|
-
);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
export function setAppLaunchpadContext(apps: AppLaunchpadApp[], legalLinks: AppLaunchpadLegalLink[] = []) {
|
|
36
|
+
export function setAppLaunchpadContext(
|
|
37
|
+
apps: AppLaunchpadApp[],
|
|
38
|
+
legalLinks: AppLaunchpadLegalLink[] = [],
|
|
39
|
+
profile?: AppLaunchpadContext["profile"],
|
|
40
|
+
) {
|
|
159
41
|
if (typeof window === "undefined") return;
|
|
160
|
-
window.__cloudAppLaunchpad = { apps, legalLinks };
|
|
42
|
+
window.__cloudAppLaunchpad = { apps, legalLinks, profile };
|
|
161
43
|
window.cloud ??= {};
|
|
162
44
|
window.cloud.openAppLaunchpad = () => {
|
|
163
45
|
openAppLaunchpad();
|
|
164
46
|
};
|
|
165
47
|
}
|
|
166
48
|
|
|
167
|
-
export function openAppLaunchpad(apps?: AppLaunchpadApp[], legalLinks?: AppLaunchpadLegalLink[]) {
|
|
49
|
+
export function openAppLaunchpad(apps?: AppLaunchpadApp[], legalLinks?: AppLaunchpadLegalLink[], profile?: AppLaunchpadContext["profile"]) {
|
|
168
50
|
if (typeof window === "undefined") return;
|
|
169
|
-
const context = apps ? { apps, legalLinks: legalLinks ?? [] } : (window.__cloudAppLaunchpad ?? readEmbeddedContext());
|
|
170
|
-
if (!context
|
|
51
|
+
const context = apps ? { apps, legalLinks: legalLinks ?? [], profile } : (window.__cloudAppLaunchpad ?? readEmbeddedContext());
|
|
52
|
+
if (!context) return;
|
|
171
53
|
window.__cloudAppLaunchpad = context;
|
|
54
|
+
if (window.matchMedia("(max-width: 1023px)").matches) {
|
|
55
|
+
void openCloudMobileMenu(context);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
172
58
|
void dialogCore.open<void>((close) => <AppLaunchpadPanel apps={context.apps} legalLinks={context.legalLinks} close={close} />, {
|
|
173
59
|
panelClassName: "k2b-dialog k2b-dialog--large is-bare",
|
|
174
60
|
contentClassName: "k2b-dialog__viewport is-bare",
|
|
@@ -178,7 +64,7 @@ export function openAppLaunchpad(apps?: AppLaunchpadApp[], legalLinks?: AppLaunc
|
|
|
178
64
|
|
|
179
65
|
export function AppLaunchpadProvider(props: AppLaunchpadContext) {
|
|
180
66
|
createEffect(() => {
|
|
181
|
-
setAppLaunchpadContext(props.apps, props.legalLinks);
|
|
67
|
+
setAppLaunchpadContext(props.apps, props.legalLinks, props.profile);
|
|
182
68
|
});
|
|
183
69
|
|
|
184
70
|
return <span class="hidden" data-cloud-app-launchpad-provider />;
|
|
@@ -187,13 +73,14 @@ export function AppLaunchpadProvider(props: AppLaunchpadContext) {
|
|
|
187
73
|
export function AppLaunchpadButton(props: AppLaunchpadContext & { variant: "rail" | "header" | "menu"; label?: string }) {
|
|
188
74
|
const locale = useLocale();
|
|
189
75
|
const t = () => platformMessages.resolve([locale()]).t;
|
|
190
|
-
const open = () => openAppLaunchpad(props.apps, props.legalLinks);
|
|
76
|
+
const open = () => openAppLaunchpad(props.apps, props.legalLinks, props.profile);
|
|
191
77
|
|
|
192
78
|
if (props.variant === "rail") {
|
|
193
79
|
return (
|
|
194
80
|
<Tooltip.Trigger
|
|
195
81
|
type="button"
|
|
196
82
|
class="rail-item shrink-0"
|
|
83
|
+
data-cloud-launchpad
|
|
197
84
|
content={props.label ?? t().apps}
|
|
198
85
|
placement="right"
|
|
199
86
|
delay={0}
|
|
@@ -227,10 +114,10 @@ export function AppLaunchpadButton(props: AppLaunchpadContext & { variant: "rail
|
|
|
227
114
|
|
|
228
115
|
export function AppLaunchpad(props: AppLaunchpadContext & { variant?: "provider" | "rail" | "header" | "menu"; label?: string }) {
|
|
229
116
|
if (!props.variant || props.variant === "provider") {
|
|
230
|
-
return <AppLaunchpadProvider apps={props.apps} legalLinks={props.legalLinks} />;
|
|
117
|
+
return <AppLaunchpadProvider apps={props.apps} legalLinks={props.legalLinks} profile={props.profile} />;
|
|
231
118
|
}
|
|
232
119
|
|
|
233
|
-
return <AppLaunchpadButton apps={props.apps} legalLinks={props.legalLinks} variant={props.variant} label={props.label} />;
|
|
120
|
+
return <AppLaunchpadButton apps={props.apps} legalLinks={props.legalLinks} profile={props.profile} variant={props.variant} label={props.label} />;
|
|
234
121
|
}
|
|
235
122
|
|
|
236
123
|
export default AppLaunchpad;
|