@k2b/cloud 0.32.0 → 0.34.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/package.json +5 -5
- package/src/_internal/app-presentation.ts +40 -11
- package/src/_internal/define-app.ts +18 -8
- package/src/_internal/page-responses.ts +5 -1
- package/src/_internal/registry.ts +10 -1
- package/src/_internal/widget-declarations.ts +16 -3
- package/src/_internal/widget-settings.ts +93 -0
- package/src/ai/capabilities.ts +8 -5
- package/src/ai/code-source-contracts.ts +17 -2
- package/src/ai/code-source-tools.ts +1 -0
- package/src/ai/default-tools.ts +4 -4
- package/src/ai/memories.ts +48 -1
- package/src/ai/memory-learning.ts +17 -6
- package/src/ai/memory-tool.ts +1 -0
- package/src/ai/migrate.ts +8 -0
- package/src/ai/provider.ts +4 -3
- package/src/ai/resource-refs.ts +35 -3
- package/src/ai/system-prompt.ts +3 -2
- package/src/api/index.ts +2 -0
- package/src/api/mcp.ts +48 -17
- package/src/api/pwa.ts +7 -2
- package/src/api/resource-links.ts +43 -0
- package/src/api/widgets.ts +163 -82
- package/src/browser/CloudResourceSearch.tsx +6 -6
- package/src/browser/command-shortcuts.ts +2 -11
- package/src/browser/navigation-search.ts +4 -1
- package/src/browser/resource-picker.tsx +1 -0
- package/src/browser/resource-search.ts +7 -2
- package/src/browser/widgets.ts +23 -5
- package/src/capabilities/testing.ts +13 -1
- package/src/cli/capabilities.ts +61 -17
- package/src/cli/index.ts +2 -0
- package/src/cli/resource-links.ts +13 -0
- package/src/contracts/app.ts +7 -1
- package/src/contracts/capabilities.ts +25 -5
- package/src/contracts/registry.ts +3 -1
- package/src/contracts/resource-href.ts +7 -0
- package/src/contracts/widgets.ts +80 -6
- package/src/desktop/index.ts +3 -2
- package/src/desktop/solid.tsx +7 -5
- package/src/server/widget-request.ts +34 -5
- package/src/services/app-approval.ts +4 -2
- package/src/services/approvals/index.ts +40 -11
- package/src/services/approvals/list.ts +1 -0
- package/src/services/approvals/migrate.ts +10 -0
- package/src/services/outbox.ts +13 -6
- package/src/services/pdf/markdown.ts +11 -7
- package/src/services/pwa-devices.ts +68 -15
- package/src/shared/ai-platform-prompt.ts +3 -2
- package/src/shared/app-presentation.ts +49 -1
- package/src/shared/index.ts +1 -0
- package/src/shared/markdown/callout-sanitizer.ts +37 -0
- package/src/shared/markdown/index.ts +8 -2
- package/src/shared/theme.ts +41 -20
- package/src/ssr/AnnouncementBanners.tsx +30 -0
- package/src/ssr/AppLaunchpadPanel.tsx +2 -2
- package/src/ssr/CloudShell.island.tsx +91 -0
- package/src/ssr/GlobalSearchTrigger.tsx +51 -0
- package/src/ssr/Layout.tsx +58 -99
- package/src/ssr/LayoutHeader.tsx +31 -77
- package/src/ssr/LayoutHelp.browser-harness.tsx +43 -0
- package/src/ssr/LayoutHelp.tsx +319 -238
- package/src/ssr/LayoutHelpTrigger.tsx +49 -0
- package/src/ssr/LayoutPreferences.tsx +31 -0
- package/src/ssr/LayoutRail.tsx +15 -69
- package/src/ssr/MinimalLayout.tsx +2 -3
- package/src/ssr/MinimalLayoutPreferences.island.tsx +6 -15
- package/src/ssr/MobileNavigation.tsx +13 -8
- package/src/ssr/MobileProfileActions.tsx +4 -6
- package/src/ssr/PageTrailRow.tsx +14 -0
- package/src/ssr/{ProfilePreferences.island.tsx → ProfilePreferences.tsx} +8 -5
- package/src/ssr/PwaLayout.tsx +0 -2
- package/src/ssr/PwaRuntime.island.tsx +1 -1
- package/src/ssr/ShellRuntime.island.tsx +166 -0
- package/src/ssr/ThemeCycleButton.tsx +46 -0
- package/src/ssr/TimezoneCookie.island.tsx +3 -19
- package/src/ssr/WorkspaceNavigationProvider.tsx +20 -1
- package/src/ssr/announcements-dialog.tsx +66 -0
- package/src/ssr/app-appearance.ts +6 -1
- package/src/ssr/{AppLaunchpad.island.tsx → app-launchpad.tsx} +42 -62
- package/src/ssr/busy-action.ts +19 -0
- package/src/ssr/islands/index.ts +9 -6
- package/src/ssr/launchpad-surface.tsx +15 -0
- package/src/ssr/layout-help-registry.tsx +60 -0
- package/src/ssr/layout-help-url.ts +39 -0
- package/src/ssr/mobile-navigation.browser-harness.tsx +20 -0
- package/src/ssr/platform-messages.ts +0 -8
- package/src/ssr/preference-controller.ts +62 -19
- package/src/ssr/profile-actions.ts +12 -5
- package/src/ssr/profile-preferences-messages.ts +10 -4
- package/src/ssr/rail-context.ts +8 -12
- package/src/ssr/rail-navigation.ts +3 -0
- package/src/ssr/shell-data.tsx +48 -0
- package/src/ssr/timezone-cookie.ts +22 -0
- package/src/ssr/workspace-navigation.ts +25 -2
- package/src/styles/effects.css +13 -3
- package/src/styles/global.css +43 -5
- package/src/styles/input.css +0 -8
- package/src/styles/tokens.css +0 -2
- package/src/styles/utilities-navigation.css +10 -0
- package/src/workflows/language/compiler.ts +1 -1
- package/src/ssr/AppWorkspaceController.island.tsx +0 -21
- package/src/ssr/BrowserPushRegistration.island.tsx +0 -12
- package/src/ssr/GlobalAnnouncements.island.tsx +0 -117
- package/src/ssr/GlobalSearchHelpDialog.tsx +0 -104
- package/src/ssr/GlobalSearchTrigger.island.tsx +0 -75
- package/src/ssr/LayoutHelpTrigger.island.tsx +0 -61
- package/src/ssr/LayoutPreferences.island.tsx +0 -21
- package/src/ssr/PageTrailRow.island.tsx +0 -20
- package/src/ssr/PageTrailRuntime.island.tsx +0 -26
- package/src/ssr/RegisteredHelpDocuments.island.tsx +0 -3
- /package/src/ssr/{LayoutBreadcrumbs.island.tsx → LayoutBreadcrumbs.tsx} +0 -0
- /package/src/ssr/{RailApps.island.tsx → RailApps.tsx} +0 -0
package/src/ssr/Layout.tsx
CHANGED
|
@@ -2,7 +2,6 @@ import type { DateContext } from "@k2b/stdlib";
|
|
|
2
2
|
import { AppWorkspace, appWorkspaceLayoutStyle, LocaleProvider, NoticeCard, PageTrailProvider } from "@k2b/ui";
|
|
3
3
|
import type { JSX } from "solid-js/jsx-runtime";
|
|
4
4
|
import { readAppWorkspaceLayoutCookie, resolveAppWorkspaceLayoutForSidebar } from "../_internal/app-workspace-state";
|
|
5
|
-
import { navigationSearchItems } from "../browser/navigation-search";
|
|
6
5
|
import { resolveNavMatch } from "../contracts/app"; // ==========================
|
|
7
6
|
import { isPwaShellAvailable } from "../contracts/pwa";
|
|
8
7
|
import { defaultRailPreferences } from "../contracts/rail-preferences";
|
|
@@ -10,29 +9,24 @@ import { hasRole, type User } from "../contracts/shared";
|
|
|
10
9
|
import { getLocale } from "../server/locale";
|
|
11
10
|
import { getDateConfig } from "../server/time";
|
|
12
11
|
import { dates } from "../shared";
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import { appAppearanceStyle } from "./app-appearance";
|
|
12
|
+
import { readThemePreferenceFromCookieHeader } from "../shared/theme";
|
|
13
|
+
import AnnouncementBanners from "./AnnouncementBanners";
|
|
14
|
+
import { appAppearanceStyle, resolveCurrentApp } from "./app-appearance";
|
|
17
15
|
import { resolveRuntimeRoute, visibleNavigationApps } from "./app-navigation";
|
|
18
|
-
import BrowserPushRegistration from "./BrowserPushRegistration.island";
|
|
19
|
-
import GlobalAnnouncements from "./GlobalAnnouncements.island";
|
|
20
|
-
import type { GlobalSearchHelpApp } from "./GlobalSearchHelpDialog";
|
|
21
16
|
import { getLayoutHelp } from "./help";
|
|
22
17
|
import LayoutFooter from "./LayoutFooter";
|
|
23
18
|
import LayoutHeader from "./LayoutHeader";
|
|
24
19
|
import LayoutRail, { type LayoutAppLink } from "./LayoutRail";
|
|
25
20
|
import type { LayoutContext } from "./layout-context";
|
|
26
21
|
import type { LayoutBreadcrumb } from "./layout-runtime";
|
|
27
|
-
import PageTrailRow from "./PageTrailRow
|
|
28
|
-
import PageTrailRuntime from "./PageTrailRuntime.island";
|
|
22
|
+
import PageTrailRow from "./PageTrailRow";
|
|
29
23
|
import { derivePageTrail } from "./page-trail";
|
|
30
24
|
import { platformMessages } from "./platform-messages";
|
|
31
|
-
import RegisteredHelpDocuments from "./RegisteredHelpDocuments.island";
|
|
32
25
|
import { sortRailApps } from "./rail-navigation";
|
|
33
26
|
import { requestPath } from "./request-path";
|
|
34
27
|
import { getLocalizedRuntimeContext, type RuntimeContext } from "./runtime";
|
|
35
|
-
import
|
|
28
|
+
import ShellRuntime from "./ShellRuntime.island";
|
|
29
|
+
import { type ShellData, ShellDataProvider } from "./shell-data";
|
|
36
30
|
|
|
37
31
|
// Types
|
|
38
32
|
type Breadcrumb = LayoutBreadcrumb;
|
|
@@ -146,8 +140,7 @@ export default function Layout(props: LayoutProps) {
|
|
|
146
140
|
const { c, title, fullPage, fullWidth, focusMode, flushCanvas, workspaceSidebarCollapsible } = props;
|
|
147
141
|
const runtime = getLocalizedRuntimeContext(c);
|
|
148
142
|
const cookie = c.req.raw.headers.get("Cookie") ?? "";
|
|
149
|
-
const theme =
|
|
150
|
-
c.get("page").theme = theme;
|
|
143
|
+
const theme = readThemePreferenceFromCookieHeader(cookie);
|
|
151
144
|
// One request-scoped locale drives <html lang>, the LocaleProvider below,
|
|
152
145
|
// and date formatting. The SSR seam and this component both resolve through
|
|
153
146
|
// the same canonical getLocale(c), so pages cannot diverge the three seams.
|
|
@@ -157,38 +150,19 @@ export default function Layout(props: LayoutProps) {
|
|
|
157
150
|
const user = c.get("user");
|
|
158
151
|
const pathname = new URL(c.req.raw.url).pathname;
|
|
159
152
|
const currentRoute = resolveRuntimeRoute(runtime.apps, pathname);
|
|
160
|
-
const currentApp =
|
|
153
|
+
const currentApp = resolveCurrentApp(runtime.apps, pathname);
|
|
161
154
|
const registeredHelp = getLayoutHelp(c);
|
|
155
|
+
const runtimeHelp = registeredHelp ? { documents: registeredHelp.documents, pageBase: registeredHelp.pageBase } : undefined;
|
|
162
156
|
const workspaceLayout = resolveAppWorkspaceLayoutForSidebar(
|
|
163
157
|
readAppWorkspaceLayoutCookie(cookie, currentApp?.id),
|
|
164
158
|
workspaceSidebarCollapsible,
|
|
165
159
|
);
|
|
166
160
|
const { primary: primaryApps, more: moreApps } = buildNavLinks(runtime.apps, user, lang);
|
|
167
161
|
const allApps = sortRailApps([...primaryApps, ...moreApps], lang);
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
label: app.label,
|
|
172
|
-
href: app.href,
|
|
173
|
-
description: app.description,
|
|
174
|
-
accent: app.accent,
|
|
175
|
-
badge: app.badge,
|
|
176
|
-
}));
|
|
177
|
-
const searchLinks = navigationSearchItems(
|
|
178
|
-
runtime.apps,
|
|
179
|
-
allApps.map((app) => app.id),
|
|
162
|
+
const visibleAppIds = new Set(allApps.map((app) => app.id));
|
|
163
|
+
const searchLinks = Object.fromEntries(
|
|
164
|
+
runtime.apps.flatMap((app) => (visibleAppIds.has(app.id) && app.searchLinks?.length ? [[app.id, app.searchLinks] as const] : [])),
|
|
180
165
|
);
|
|
181
|
-
const searchHelpApps: GlobalSearchHelpApp[] = runtime.apps
|
|
182
|
-
.filter((app) => (app.searchTags?.length ?? 0) > 0)
|
|
183
|
-
.map((app) => ({
|
|
184
|
-
appId: app.id,
|
|
185
|
-
appName: app.name,
|
|
186
|
-
appIcon: app.icon,
|
|
187
|
-
help: app.searchHelp,
|
|
188
|
-
tags: [...new Set((app.searchTags ?? []).map((tag) => tag.toLowerCase()))],
|
|
189
|
-
tagHelp: [...(app.searchTagHelp ?? [])],
|
|
190
|
-
}))
|
|
191
|
-
.sort((a, b) => a.appName.localeCompare(b.appName));
|
|
192
166
|
const settings = c.get("settings");
|
|
193
167
|
const announcements = c.get("announcements");
|
|
194
168
|
const appName = settings?.app?.name || "Cloud";
|
|
@@ -227,6 +201,18 @@ export default function Layout(props: LayoutProps) {
|
|
|
227
201
|
[appAppearanceStyle(currentApp?.appearance), appWorkspaceLayoutStyle(workspaceLayout), focusMode && flushCanvas ? "padding:0" : ""]
|
|
228
202
|
.filter(Boolean)
|
|
229
203
|
.join(";") || undefined;
|
|
204
|
+
const appLabel = currentApp?.name ?? (pathname.startsWith("/admin") ? t.admin : undefined);
|
|
205
|
+
const shellData: ShellData = {
|
|
206
|
+
apps: allApps,
|
|
207
|
+
...(user
|
|
208
|
+
? { rail: c.get("railPreferences") ?? defaultRailPreferences(), profile: { name: profileName, avatarSrc: profileAvatarSrc, appHref } }
|
|
209
|
+
: {}),
|
|
210
|
+
legalLinks,
|
|
211
|
+
theme,
|
|
212
|
+
accent: currentApp?.appearance?.accent,
|
|
213
|
+
searchLinks,
|
|
214
|
+
appId: currentApp?.id,
|
|
215
|
+
};
|
|
230
216
|
if (focusMode) {
|
|
231
217
|
return (
|
|
232
218
|
<LocaleProvider locale={lang}>
|
|
@@ -239,10 +225,8 @@ export default function Layout(props: LayoutProps) {
|
|
|
239
225
|
data-layout-full-page="true"
|
|
240
226
|
data-workspace-sidebar-collapsed={workspaceLayout?.sidebarCollapsed ? "true" : undefined}
|
|
241
227
|
>
|
|
242
|
-
<
|
|
243
|
-
{registeredHelp && <RegisteredHelpDocuments documents={registeredHelp.documents} pageBase={registeredHelp.pageBase} />}
|
|
228
|
+
<ShellRuntime pageTrail help={runtimeHelp} />
|
|
244
229
|
<PageTrailProvider trail={pageTrail}>
|
|
245
|
-
<PageTrailRuntime />
|
|
246
230
|
<main class="min-h-0 min-w-0 flex-1">
|
|
247
231
|
<AppWorkspace.LayoutStateProvider state={workspaceLayout}>{props.children}</AppWorkspace.LayoutStateProvider>
|
|
248
232
|
</main>
|
|
@@ -266,65 +250,40 @@ export default function Layout(props: LayoutProps) {
|
|
|
266
250
|
data-layout-authenticated={showRail ? "true" : undefined}
|
|
267
251
|
data-workspace-sidebar-collapsed={workspaceLayout?.sidebarCollapsed ? "true" : undefined}
|
|
268
252
|
>
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
/>
|
|
291
|
-
)}
|
|
292
|
-
<div class="layout-shell-content flex min-h-0 min-w-0 flex-1 flex-col">
|
|
293
|
-
<LayoutHeader
|
|
294
|
-
appHref={appHref}
|
|
295
|
-
appLabel={currentApp?.name ?? (pathname.startsWith("/admin") ? t.admin : undefined)}
|
|
296
|
-
accent={currentApp?.appearance?.accent}
|
|
297
|
-
authenticated={showRail}
|
|
298
|
-
breadcrumbs={breadcrumbs}
|
|
299
|
-
homeLabel={t.home}
|
|
300
|
-
launchpadApps={launchpadApps}
|
|
301
|
-
legalLinks={legalLinks}
|
|
302
|
-
openAppsLabel={t.openApps}
|
|
303
|
-
profileName={profileName}
|
|
304
|
-
searchHelpApps={searchHelpApps}
|
|
305
|
-
searchLinks={searchLinks}
|
|
306
|
-
signInLabel={t.signIn}
|
|
307
|
-
theme={theme}
|
|
308
|
-
/>
|
|
309
|
-
{user && announcements && (
|
|
310
|
-
<GlobalAnnouncements
|
|
311
|
-
banners={announcements.banners}
|
|
312
|
-
announcements={announcements.announcements}
|
|
313
|
-
latestAnnouncementVersion={announcements.latestAnnouncementVersion}
|
|
314
|
-
cookieState={announcements.cookieState}
|
|
253
|
+
<ShellRuntime
|
|
254
|
+
workspace={{ appId: currentApp?.id }}
|
|
255
|
+
pageTrail
|
|
256
|
+
help={runtimeHelp}
|
|
257
|
+
commands={{ searchResources: !!user }}
|
|
258
|
+
push={!!user}
|
|
259
|
+
announcements={
|
|
260
|
+
user && announcements?.announcements.length
|
|
261
|
+
? { entries: announcements.announcements, latestVersion: announcements.latestAnnouncementVersion }
|
|
262
|
+
: undefined
|
|
263
|
+
}
|
|
264
|
+
banners={user && announcements?.banners.length ? true : undefined}
|
|
265
|
+
/>
|
|
266
|
+
<ShellDataProvider data={shellData}>
|
|
267
|
+
{showRail && <LayoutRail currentPath={requestPath(c)} />}
|
|
268
|
+
<div class="layout-shell-content flex min-h-0 min-w-0 flex-1 flex-col">
|
|
269
|
+
<LayoutHeader
|
|
270
|
+
authenticated={showRail}
|
|
271
|
+
title={appLabel ?? breadcrumbs.at(-1)?.title}
|
|
272
|
+
launchpad={!!(user || appLabel)}
|
|
273
|
+
breadcrumbs={user ? undefined : breadcrumbs}
|
|
315
274
|
/>
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
</
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
</
|
|
275
|
+
{user && announcements && <AnnouncementBanners banners={announcements.banners} />}
|
|
276
|
+
{user && <ProfileWarnings user={user} locale={lang} />}
|
|
277
|
+
{user && <ExpiryWarnings user={user} dateConfig={dateConfig} locale={lang} />}
|
|
278
|
+
<PageTrailProvider trail={pageTrail}>
|
|
279
|
+
<PageTrailRow trail={pageTrail} />
|
|
280
|
+
<main class={`layout-content-main min-h-0 min-w-0 flex-1 ${mainLayoutClass}`}>
|
|
281
|
+
<AppWorkspace.LayoutStateProvider state={workspaceLayout}>{props.children}</AppWorkspace.LayoutStateProvider>
|
|
282
|
+
</main>
|
|
283
|
+
</PageTrailProvider>
|
|
284
|
+
{!fullPage && !showRail && <LayoutFooter appName={settings?.app?.copyright || appName} legalLinks={legalLinks} />}
|
|
285
|
+
</div>
|
|
286
|
+
</ShellDataProvider>
|
|
328
287
|
</div>
|
|
329
288
|
</LocaleProvider>
|
|
330
289
|
);
|
package/src/ssr/LayoutHeader.tsx
CHANGED
|
@@ -1,30 +1,13 @@
|
|
|
1
|
-
import { ButtonLink, IconButtonLink } from "@k2b/ui";
|
|
2
|
-
import
|
|
3
|
-
import type { CloudTheme } from "../shared/theme";
|
|
4
|
-
import AppLaunchpad, { type AppLaunchpadApp } from "./AppLaunchpad.island";
|
|
5
|
-
import type { GlobalSearchHelpApp } from "./GlobalSearchHelpDialog";
|
|
6
|
-
import GlobalSearchTrigger from "./GlobalSearchTrigger.island";
|
|
7
|
-
import LayoutBreadcrumbs from "./LayoutBreadcrumbs.island";
|
|
8
|
-
import LayoutHelpTrigger from "./LayoutHelpTrigger.island";
|
|
9
|
-
import LayoutPreferences from "./LayoutPreferences.island";
|
|
1
|
+
import { ButtonLink, IconButtonLink, useLocale } from "@k2b/ui";
|
|
2
|
+
import CloudShell from "./CloudShell.island";
|
|
10
3
|
import type { LayoutBreadcrumb } from "./layout-runtime";
|
|
4
|
+
import { platformMessages } from "./platform-messages";
|
|
11
5
|
|
|
12
6
|
type LayoutHeaderProps = {
|
|
13
|
-
accent?: string;
|
|
14
|
-
/** Set while the installation offers the mobile app. */
|
|
15
|
-
appHref?: string;
|
|
16
|
-
appLabel?: string;
|
|
17
7
|
authenticated: boolean;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
legalLinks: Array<{ label: string; href: string; icon?: string }>;
|
|
22
|
-
openAppsLabel: string;
|
|
23
|
-
profileName: string;
|
|
24
|
-
searchHelpApps: GlobalSearchHelpApp[];
|
|
25
|
-
searchLinks?: NavigationSearchItem[];
|
|
26
|
-
signInLabel: string;
|
|
27
|
-
theme: CloudTheme;
|
|
8
|
+
title?: string;
|
|
9
|
+
launchpad: boolean;
|
|
10
|
+
breadcrumbs?: LayoutBreadcrumb[];
|
|
28
11
|
};
|
|
29
12
|
|
|
30
13
|
/**
|
|
@@ -33,68 +16,39 @@ type LayoutHeaderProps = {
|
|
|
33
16
|
* and their profile through the rail, so the app area starts at the top edge.
|
|
34
17
|
* Anonymous pages have no rail: up to `lg` their header keeps the app name,
|
|
35
18
|
* help, search, and the app grid, and from `lg` it shows the breadcrumbs.
|
|
19
|
+
* Home and Sign in stay server markup, so focus on them survives the shell's
|
|
20
|
+
* mount; the shell renders the title and the controls between them.
|
|
36
21
|
*/
|
|
37
22
|
export default function LayoutHeader(props: LayoutHeaderProps) {
|
|
23
|
+
const locale = useLocale();
|
|
24
|
+
const t = () => platformMessages.resolve([locale()]).t;
|
|
38
25
|
return (
|
|
39
26
|
<header
|
|
40
|
-
class={`layout-header paper flex min-h-[2.875rem] shrink-0 items-center
|
|
27
|
+
class={`layout-header paper flex min-h-[2.875rem] shrink-0 items-center px-2 py-1.5 md:px-3 md:py-2 ${props.authenticated ? "md:hidden" : ""}`}
|
|
41
28
|
style="box-shadow: var(--ui-shadow-surface)"
|
|
42
29
|
>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
</IconButtonLink>
|
|
50
|
-
</div>
|
|
51
|
-
{!props.authenticated && (
|
|
52
|
-
<div class="hidden min-w-0 items-center lg:flex">
|
|
53
|
-
<LayoutBreadcrumbs breadcrumbs={props.breadcrumbs} />
|
|
54
|
-
</div>
|
|
55
|
-
)}
|
|
56
|
-
<div class="flex min-w-0 items-center lg:hidden">
|
|
57
|
-
<span class="truncate font-semibold">{props.appLabel ?? props.breadcrumbs.at(-1)?.title}</span>
|
|
58
|
-
</div>
|
|
30
|
+
{/* A long title must not squeeze the button; `shrink-0` sits on a wrapper because the button's own
|
|
31
|
+
styles would win over a utility on the link. */}
|
|
32
|
+
<div class="flex shrink-0">
|
|
33
|
+
<IconButtonLink href="/" label={t().home}>
|
|
34
|
+
<img src="/branding/logo" alt="" class={props.authenticated ? "h-4 w-4" : "h-6 w-6"} />
|
|
35
|
+
</IconButtonLink>
|
|
59
36
|
</div>
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/>
|
|
74
|
-
{(props.authenticated || props.appLabel) && (
|
|
75
|
-
<AppLaunchpad
|
|
76
|
-
profile={
|
|
77
|
-
props.authenticated
|
|
78
|
-
? { name: props.profileName, theme: props.theme, ...(props.appHref ? { appHref: props.appHref } : {}) }
|
|
79
|
-
: undefined
|
|
80
|
-
}
|
|
81
|
-
apps={props.launchpadApps}
|
|
82
|
-
legalLinks={props.legalLinks}
|
|
83
|
-
variant="header"
|
|
84
|
-
label={props.openAppsLabel}
|
|
85
|
-
/>
|
|
86
|
-
)}
|
|
37
|
+
<CloudShell
|
|
38
|
+
slot="header"
|
|
39
|
+
authenticated={props.authenticated}
|
|
40
|
+
title={props.title}
|
|
41
|
+
launchpad={props.launchpad}
|
|
42
|
+
breadcrumbs={props.breadcrumbs}
|
|
43
|
+
/>
|
|
44
|
+
{!props.authenticated && (
|
|
45
|
+
<div class="ml-1 flex shrink-0">
|
|
46
|
+
<ButtonLink href="/auth/login" size="sm" variant="primary">
|
|
47
|
+
<i class="ti ti-login" aria-hidden="true" />
|
|
48
|
+
{t().signIn}
|
|
49
|
+
</ButtonLink>
|
|
87
50
|
</div>
|
|
88
|
-
|
|
89
|
-
<>
|
|
90
|
-
<LayoutPreferences initialTheme={props.theme} position="bottom-left" />
|
|
91
|
-
<ButtonLink href="/auth/login" size="sm" variant="primary">
|
|
92
|
-
<i class="ti ti-login" aria-hidden="true" />
|
|
93
|
-
{props.signInLabel}
|
|
94
|
-
</ButtonLink>
|
|
95
|
-
</>
|
|
96
|
-
)}
|
|
97
|
-
</div>
|
|
51
|
+
)}
|
|
98
52
|
</header>
|
|
99
53
|
);
|
|
100
54
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { LocaleProvider } from "@k2b/ui";
|
|
2
|
+
import { render } from "solid-js/web";
|
|
3
|
+
import type { HelpDocumentManifest, HelpDocumentPayload } from "../shared/help";
|
|
4
|
+
import { LayoutHelpPage } from "./LayoutHelp";
|
|
5
|
+
|
|
6
|
+
const documents: HelpDocumentManifest[] = Array.from({ length: 8 }, (_, index) => ({
|
|
7
|
+
id: `topic-${index}`,
|
|
8
|
+
title: `Topic ${index}`,
|
|
9
|
+
description: "Help browser fixture",
|
|
10
|
+
order: index,
|
|
11
|
+
url: `/api/help/topic-${index}`,
|
|
12
|
+
searchUrl: "/api/help/search",
|
|
13
|
+
}));
|
|
14
|
+
const html = Array.from(
|
|
15
|
+
{ length: 8 },
|
|
16
|
+
(_, index) =>
|
|
17
|
+
`<h2 id="section-${index}" class="help-section-title" data-help-icon="ti ti-point">Section ${index}</h2>${Array.from({ length: 18 }, () => "<p>Read this guidance to finish the task and understand how the app works.</p>").join("")}`,
|
|
18
|
+
).join("");
|
|
19
|
+
window.fetch = Object.assign(
|
|
20
|
+
async (input: URL | RequestInfo) => {
|
|
21
|
+
await new Promise((resolve) => setTimeout(resolve, 80));
|
|
22
|
+
const path = String(input);
|
|
23
|
+
if (path.includes("/search")) return Response.json({ locale: "en", ids: ["topic-0", "topic-1"] });
|
|
24
|
+
const id = path.split("/").at(-1) ?? "topic-0";
|
|
25
|
+
const payload: HelpDocumentPayload = { id, locale: "en", title: id, markdown: "Help", html };
|
|
26
|
+
return Response.json(payload);
|
|
27
|
+
},
|
|
28
|
+
{ preconnect: () => {} },
|
|
29
|
+
);
|
|
30
|
+
const main = document.querySelector<HTMLElement>(".layout-content-main");
|
|
31
|
+
if (!main) throw new Error("Missing Layout main");
|
|
32
|
+
const embedded = new URL(location.href).searchParams.has("embedded");
|
|
33
|
+
const host = document.createElement("div");
|
|
34
|
+
if (embedded) host.style.cssText = "width:360px;height:700px";
|
|
35
|
+
main.append(host);
|
|
36
|
+
render(
|
|
37
|
+
() => (
|
|
38
|
+
<LocaleProvider locale="en">
|
|
39
|
+
<LayoutHelpPage documents={documents} embedded={embedded} pageBase="/app/spaces/help" />
|
|
40
|
+
</LocaleProvider>
|
|
41
|
+
),
|
|
42
|
+
host,
|
|
43
|
+
);
|