@k2b/cloud 0.32.0 → 0.33.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/define-app.ts +17 -8
- package/src/_internal/page-responses.ts +5 -1
- 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/browser/navigation-search.ts +4 -1
- package/src/services/outbox.ts +13 -6
- package/src/shared/ai-platform-prompt.ts +3 -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 +56 -85
- package/src/ssr/LayoutHeader.tsx +31 -77
- package/src/ssr/LayoutHelp.tsx +2 -57
- package/src/ssr/LayoutHelpTrigger.tsx +51 -0
- package/src/ssr/{LayoutPreferences.island.tsx → LayoutPreferences.tsx} +2 -2
- package/src/ssr/LayoutRail.tsx +15 -69
- package/src/ssr/MinimalLayout.tsx +2 -3
- package/src/ssr/MinimalLayoutPreferences.island.tsx +12 -15
- package/src/ssr/MobileNavigation.tsx +13 -8
- package/src/ssr/MobileProfileActions.tsx +10 -7
- package/src/ssr/PageTrailRow.tsx +14 -0
- package/src/ssr/{ProfilePreferences.island.tsx → ProfilePreferences.tsx} +12 -7
- package/src/ssr/PwaLayout.tsx +0 -2
- package/src/ssr/PwaRuntime.island.tsx +1 -1
- package/src/ssr/ShellRuntime.island.tsx +170 -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/{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/mobile-navigation.browser-harness.tsx +20 -0
- package/src/ssr/preference-controller.ts +30 -21
- package/src/ssr/profile-actions.ts +26 -15
- package/src/ssr/profile-preferences-messages.ts +8 -4
- package/src/ssr/rail-context.ts +8 -12
- 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/global.css +17 -5
- package/src/styles/utilities-navigation.css +13 -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/GlobalSearchTrigger.island.tsx +0 -75
- package/src/ssr/LayoutHelpTrigger.island.tsx +0 -61
- 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/styles/global.css
CHANGED
|
@@ -320,22 +320,33 @@ solid-island {
|
|
|
320
320
|
edges, without padding, radius, shadow, or border. The rail keeps the app
|
|
321
321
|
canvas, so the change of surface alone separates the two; no line does. The
|
|
322
322
|
outermost workspace and the notices above it lose their frame, so nothing
|
|
323
|
-
draws a box inside the app area.
|
|
323
|
+
draws a box inside the app area.
|
|
324
|
+
The rail and the app area reach the window's edges also where the installed
|
|
325
|
+
app reports safe areas, such as the status bar and the home indicator. Their
|
|
326
|
+
own surfaces fill the insets, and their content stays inside them. */
|
|
324
327
|
@media (min-width: 768px) and (max-width: 1535px) {
|
|
325
328
|
.cloud-app-canvas[data-layout-authenticated] {
|
|
326
|
-
padding:
|
|
329
|
+
padding: 0;
|
|
327
330
|
}
|
|
328
331
|
.cloud-app-canvas[data-layout-authenticated] > .layout-rail {
|
|
329
|
-
width: 3rem;
|
|
332
|
+
width: calc(3rem + env(safe-area-inset-left, 0px));
|
|
330
333
|
margin-inline-start: 0;
|
|
331
|
-
padding
|
|
334
|
+
padding: env(safe-area-inset-top, 0px) 0 calc(var(--ui-space-shell) + env(safe-area-inset-bottom, 0px))
|
|
335
|
+
env(safe-area-inset-left, 0px);
|
|
332
336
|
}
|
|
333
337
|
.cloud-app-canvas[data-layout-authenticated] > .layout-shell-content {
|
|
334
338
|
gap: 0;
|
|
339
|
+
padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) 0 0;
|
|
335
340
|
background: var(--k2b-surface);
|
|
336
341
|
}
|
|
342
|
+
/* A regular page scrolls `main`, so its end clears the home indicator. A
|
|
343
|
+
workspace reaches the bottom edge and clears it inside its own surfaces. */
|
|
344
|
+
.cloud-app-canvas[data-layout-authenticated] .layout-content-main:not(:has(.k2b-app-workspace)) {
|
|
345
|
+
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
346
|
+
}
|
|
337
347
|
.cloud-app-canvas[data-layout-authenticated] .layout-content-main .k2b-app-workspace:not(.k2b-app-workspace *) {
|
|
338
348
|
--k2b-workspace-radius: 0px;
|
|
349
|
+
--k2b-workspace-inset-bottom: env(safe-area-inset-bottom, 0px);
|
|
339
350
|
border: 0;
|
|
340
351
|
box-shadow: none;
|
|
341
352
|
}
|
|
@@ -376,4 +387,5 @@ solid-island {
|
|
|
376
387
|
width: 100%; white-space: normal; overflow-wrap: anywhere; font-size: .8125rem; line-height: 1.3;
|
|
377
388
|
}
|
|
378
389
|
.cloud-mobile-profile { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; border-top: 1px solid var(--ui-border); padding-top: 1rem; }
|
|
379
|
-
.cloud-mobile-profile :is(a, button) { min-height: 2.75rem; white-space: normal; text-align: start; justify-content: flex-start; }
|
|
390
|
+
.cloud-mobile-profile > :is(a, button) { min-height: 2.75rem; white-space: normal; text-align: start; justify-content: flex-start; }
|
|
391
|
+
.cloud-mobile-profile__appearance { grid-column: 1 / -1; }
|
|
@@ -883,6 +883,19 @@
|
|
|
883
883
|
transition: opacity 120ms ease-out;
|
|
884
884
|
}
|
|
885
885
|
|
|
886
|
+
.layout-profile-preferences__appearance {
|
|
887
|
+
padding: 0.125rem 0.125rem 0.25rem;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.layout-profile-preferences__appearance .k2b-segmented-control {
|
|
891
|
+
width: 100%;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/* Content-sized segments: "Automatisch" needs more room than "Hell" in the narrow panel. */
|
|
895
|
+
.layout-profile-preferences__appearance .k2b-segmented-control__option {
|
|
896
|
+
flex: 1 1 auto;
|
|
897
|
+
}
|
|
898
|
+
|
|
886
899
|
.dark .layout-profile-preferences__panel {
|
|
887
900
|
color: var(--color-zinc-300);
|
|
888
901
|
}
|
|
@@ -292,7 +292,7 @@ const compileSteps = (
|
|
|
292
292
|
if (hasOwn(step, "switch")) {
|
|
293
293
|
validateExactKeys(step, ["switch", "cases", "default"], stepPath, context);
|
|
294
294
|
if (!Array.isArray(step.cases) || step.cases.length === 0) {
|
|
295
|
-
addDiagnostic(context, "switch.cases", "switch
|
|
295
|
+
addDiagnostic(context, "switch.cases", "switch cases must be a non-empty list of { when, do } entries", [...stepPath, "cases"]);
|
|
296
296
|
return;
|
|
297
297
|
}
|
|
298
298
|
const cases: Array<{ when: WorkflowJsonValue; steps: WorkflowIrStep[] }> = [];
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { installAppWorkspaceController, serializeAppWorkspaceLayoutState } from "@k2b/ui";
|
|
2
|
-
import { onCleanup, onMount } from "solid-js";
|
|
3
|
-
import { appWorkspaceCookieName, readAppWorkspaceLayoutCookie } from "../_internal/app-workspace-state";
|
|
4
|
-
|
|
5
|
-
const writeLayoutCookie = (appId: string | null | undefined, state: Parameters<typeof serializeAppWorkspaceLayoutState>[0]) => {
|
|
6
|
-
if (!appId) return;
|
|
7
|
-
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
8
|
-
document.cookie = `${appWorkspaceCookieName(appId)}=${serializeAppWorkspaceLayoutState(state)}; Path=/; Max-Age=31536000; SameSite=Lax${secure}`;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export default function AppWorkspaceController(props: { appId?: string | null }) {
|
|
12
|
-
let dispose = () => {};
|
|
13
|
-
onMount(() => {
|
|
14
|
-
dispose = installAppWorkspaceController({
|
|
15
|
-
readState: () => readAppWorkspaceLayoutCookie(document.cookie, props.appId),
|
|
16
|
-
writeState: (state) => writeLayoutCookie(props.appId, state),
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
onCleanup(() => dispose());
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { onMount } from "solid-js";
|
|
2
|
-
import { browserNotificationClient } from "../browser/notifications";
|
|
3
|
-
|
|
4
|
-
export default function BrowserPushRegistration() {
|
|
5
|
-
onMount(() => {
|
|
6
|
-
void browserNotificationClient.refreshExisting().catch((error) => {
|
|
7
|
-
console.warn("[notifications] Failed to refresh the browser endpoint", error);
|
|
8
|
-
});
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { Button, dialogCore, IconButton, MarkdownView, NoticeCard, PanelDialog, panelDialogOptions, useLocale } from "@k2b/ui";
|
|
2
|
-
import { createSignal, For, onMount, Show } from "solid-js";
|
|
3
|
-
import {
|
|
4
|
-
ANNOUNCEMENTS_COOKIE,
|
|
5
|
-
ANNOUNCEMENTS_COOKIE_MAX_AGE_SECONDS,
|
|
6
|
-
type AnnouncementCookieState,
|
|
7
|
-
mergeAnnouncementCookieState,
|
|
8
|
-
serializeAnnouncementCookieState,
|
|
9
|
-
} from "../contracts/announcement-cookie";
|
|
10
|
-
import type { AnnouncementDisplayEntry } from "../contracts/announcements";
|
|
11
|
-
import { platformMessages } from "./platform-messages";
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
banners: AnnouncementDisplayEntry[];
|
|
15
|
-
announcements: AnnouncementDisplayEntry[];
|
|
16
|
-
latestAnnouncementVersion: number;
|
|
17
|
-
cookieState: AnnouncementCookieState;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const writeCookieState = (state: AnnouncementCookieState) => {
|
|
21
|
-
document.cookie = `${ANNOUNCEMENTS_COOKIE}=${serializeAnnouncementCookieState(state)}; Path=/; Max-Age=${ANNOUNCEMENTS_COOKIE_MAX_AGE_SECONDS}; SameSite=Lax`;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default function GlobalAnnouncements(props: Props) {
|
|
25
|
-
const locale = useLocale();
|
|
26
|
-
const t = () => platformMessages.resolve([locale()]).t;
|
|
27
|
-
const [cookieState, setCookieState] = createSignal(props.cookieState);
|
|
28
|
-
const [banners, setBanners] = createSignal(props.banners);
|
|
29
|
-
|
|
30
|
-
const dismissBanner = (version: number) => {
|
|
31
|
-
const next = mergeAnnouncementCookieState(cookieState(), { dismissedBannerVersions: [version] });
|
|
32
|
-
setCookieState(next);
|
|
33
|
-
writeCookieState(next);
|
|
34
|
-
setBanners((items) => items.filter((item) => item.version !== version));
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const closeAnnouncements = () => {
|
|
38
|
-
const next = mergeAnnouncementCookieState(cookieState(), {
|
|
39
|
-
seenAnnouncementVersion: props.latestAnnouncementVersion,
|
|
40
|
-
});
|
|
41
|
-
setCookieState(next);
|
|
42
|
-
writeCookieState(next);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
onMount(() => {
|
|
46
|
-
if (props.announcements.length === 0) return;
|
|
47
|
-
|
|
48
|
-
void dialogCore
|
|
49
|
-
.open<void>(
|
|
50
|
-
(close) => (
|
|
51
|
-
<PanelDialog>
|
|
52
|
-
<PanelDialog.Header
|
|
53
|
-
title={t().announcements}
|
|
54
|
-
subtitle={t().latestPlatformUpdates}
|
|
55
|
-
icon="ti ti-speakerphone text-blue-500"
|
|
56
|
-
close={() => close()}
|
|
57
|
-
/>
|
|
58
|
-
<PanelDialog.Body>
|
|
59
|
-
<For each={props.announcements}>
|
|
60
|
-
{(entry) => (
|
|
61
|
-
<article class="panel-dialog-section rounded-[var(--ui-radius-surface)] p-4">
|
|
62
|
-
<div class="mb-3 flex items-start justify-between gap-3">
|
|
63
|
-
<div class="min-w-0">
|
|
64
|
-
<h2 class="text-base font-semibold text-primary">{entry.title}</h2>
|
|
65
|
-
<p class="mt-0.5 text-xs text-dimmed">
|
|
66
|
-
{new Date(entry.publishedAt).toLocaleDateString(locale(), {
|
|
67
|
-
year: "numeric",
|
|
68
|
-
month: "short",
|
|
69
|
-
day: "numeric",
|
|
70
|
-
})}
|
|
71
|
-
</p>
|
|
72
|
-
</div>
|
|
73
|
-
<span class="rounded bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-dimmed dark:bg-zinc-800">
|
|
74
|
-
v{entry.version}
|
|
75
|
-
</span>
|
|
76
|
-
</div>
|
|
77
|
-
<MarkdownView trustedHtml={entry.bodyHtml} />
|
|
78
|
-
</article>
|
|
79
|
-
)}
|
|
80
|
-
</For>
|
|
81
|
-
</PanelDialog.Body>
|
|
82
|
-
<PanelDialog.Footer>
|
|
83
|
-
<span />
|
|
84
|
-
<Button size="sm" onClick={() => close()}>
|
|
85
|
-
{t().gotIt}
|
|
86
|
-
</Button>
|
|
87
|
-
</PanelDialog.Footer>
|
|
88
|
-
</PanelDialog>
|
|
89
|
-
),
|
|
90
|
-
panelDialogOptions,
|
|
91
|
-
)
|
|
92
|
-
.then(closeAnnouncements);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<Show when={banners().length > 0}>
|
|
97
|
-
<div class="flex shrink-0 flex-col gap-1">
|
|
98
|
-
<For each={banners()}>
|
|
99
|
-
{(banner) => (
|
|
100
|
-
<div class="cloud-announcement">
|
|
101
|
-
<NoticeCard tone={banner.tone} title={banner.title}>
|
|
102
|
-
<MarkdownView
|
|
103
|
-
trustedHtml={banner.bodyHtml}
|
|
104
|
-
headingScale="compact"
|
|
105
|
-
class="-mx-2 max-h-36 overflow-y-auto overscroll-contain pl-2 pr-3 [&_p]:my-0"
|
|
106
|
-
/>
|
|
107
|
-
</NoticeCard>
|
|
108
|
-
<IconButton size="sm" label={t().dismissBanner} onClick={() => dismissBanner(banner.version)}>
|
|
109
|
-
<i class="ti ti-x" aria-hidden="true" />
|
|
110
|
-
</IconButton>
|
|
111
|
-
</div>
|
|
112
|
-
)}
|
|
113
|
-
</For>
|
|
114
|
-
</div>
|
|
115
|
-
</Show>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { IconButton, Tooltip, useLocale } from "@k2b/ui";
|
|
2
|
-
import { createEffect, onCleanup, onMount } from "solid-js";
|
|
3
|
-
import { registerContextAwareCommand } from "../browser/command-bridge";
|
|
4
|
-
import { attachCommandShortcuts } from "../browser/command-shortcuts";
|
|
5
|
-
import type { NavigationSearchItem } from "../browser/navigation-search";
|
|
6
|
-
import { importOnDemand } from "../browser/reload";
|
|
7
|
-
import { openGlobalSearch } from "../browser/search";
|
|
8
|
-
import { registerGlobalSearchHost } from "../browser/search-bridge";
|
|
9
|
-
import { createGlobalSearchHost } from "./global-search-host";
|
|
10
|
-
import { platformMessages } from "./platform-messages";
|
|
11
|
-
|
|
12
|
-
type GlobalSearchTriggerProps = {
|
|
13
|
-
variant: "header" | "rail" | "host";
|
|
14
|
-
class?: string;
|
|
15
|
-
registerCommand?: boolean;
|
|
16
|
-
searchLinks?: NavigationSearchItem[];
|
|
17
|
-
searchResources?: boolean;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/** Opens the spotlight-style global search dialog from nav/header trigger points. */
|
|
21
|
-
export default function GlobalSearchTrigger(props: GlobalSearchTriggerProps) {
|
|
22
|
-
const locale = useLocale();
|
|
23
|
-
const t = () => platformMessages.resolve([locale()]).t;
|
|
24
|
-
|
|
25
|
-
if (props.registerCommand) {
|
|
26
|
-
const host = createGlobalSearchHost(() => props.searchLinks ?? [], props.searchResources);
|
|
27
|
-
onMount(() => {
|
|
28
|
-
onCleanup(registerGlobalSearchHost(host.open, host.dispose));
|
|
29
|
-
});
|
|
30
|
-
// Commands, their schemas and the capability client load on the first shortcut, not with every page.
|
|
31
|
-
onMount(() =>
|
|
32
|
-
onCleanup(
|
|
33
|
-
attachCommandShortcuts(
|
|
34
|
-
(command) =>
|
|
35
|
-
void importOnDemand(() => import("../browser/commands")).then((commands) => commands?.runContextAwareCommand(command)),
|
|
36
|
-
),
|
|
37
|
-
),
|
|
38
|
-
);
|
|
39
|
-
createEffect(() =>
|
|
40
|
-
onCleanup(
|
|
41
|
-
registerContextAwareCommand({
|
|
42
|
-
id: "cloud.search",
|
|
43
|
-
title: t().openGlobalSearch,
|
|
44
|
-
description: t().globalSearchDescription,
|
|
45
|
-
icon: "ti ti-search",
|
|
46
|
-
shortcut: "mod+k",
|
|
47
|
-
action: { search: {} },
|
|
48
|
-
}),
|
|
49
|
-
),
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (props.variant === "host") return null;
|
|
54
|
-
|
|
55
|
-
if (props.variant === "rail")
|
|
56
|
-
return (
|
|
57
|
-
<Tooltip.Trigger
|
|
58
|
-
type="button"
|
|
59
|
-
class={`rail-item text-blue-500 hover:bg-blue-500/10 hover:text-blue-600 dark:text-blue-400 dark:hover:bg-blue-500/15 dark:hover:text-blue-300 ${props.class ?? ""}`}
|
|
60
|
-
onClick={() => openGlobalSearch()}
|
|
61
|
-
aria-label={t().openGlobalSearch}
|
|
62
|
-
placement="right"
|
|
63
|
-
delay={0}
|
|
64
|
-
content={t().searchShortcut}
|
|
65
|
-
>
|
|
66
|
-
<i class="ti ti-search text-base" />
|
|
67
|
-
</Tooltip.Trigger>
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<IconButton class={props.class} onClick={() => openGlobalSearch()} label={t().openGlobalSearch} title={t().searchShortcut}>
|
|
72
|
-
<i class="ti ti-search text-base" />
|
|
73
|
-
</IconButton>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { IconButton, Tooltip, useLocale } from "@k2b/ui";
|
|
2
|
-
import { createEffect, onCleanup } from "solid-js";
|
|
3
|
-
import { registerContextAwareCommand } from "../browser/command-bridge";
|
|
4
|
-
import type { GlobalSearchHelpApp } from "./GlobalSearchHelpDialog";
|
|
5
|
-
import { openLayoutHelpDialog } from "./LayoutHelp";
|
|
6
|
-
import { platformMessages } from "./platform-messages";
|
|
7
|
-
|
|
8
|
-
type LayoutHelpTriggerProps = {
|
|
9
|
-
variant: "header" | "rail";
|
|
10
|
-
class?: string;
|
|
11
|
-
registerCommand?: boolean;
|
|
12
|
-
searchHelpApps?: GlobalSearchHelpApp[];
|
|
13
|
-
accent?: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/** Opens end-user help from either the desktop rail or the compact header. */
|
|
17
|
-
export default function LayoutHelpTrigger(props: LayoutHelpTriggerProps) {
|
|
18
|
-
const locale = useLocale();
|
|
19
|
-
const t = () => platformMessages.resolve([locale()]).t;
|
|
20
|
-
const searchHelpApps = props.searchHelpApps ?? [];
|
|
21
|
-
|
|
22
|
-
const openHelp = () => {
|
|
23
|
-
openLayoutHelpDialog(searchHelpApps, props.accent);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
if (props.registerCommand) {
|
|
27
|
-
createEffect(() =>
|
|
28
|
-
onCleanup(
|
|
29
|
-
registerContextAwareCommand({
|
|
30
|
-
id: "cloud.help",
|
|
31
|
-
title: t().openShortcutHelp,
|
|
32
|
-
description: t().shortcutHelpDescription,
|
|
33
|
-
icon: "ti ti-help-circle",
|
|
34
|
-
shortcut: "shift+/",
|
|
35
|
-
action: openHelp,
|
|
36
|
-
}),
|
|
37
|
-
),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (props.variant === "rail")
|
|
42
|
-
return (
|
|
43
|
-
<Tooltip.Trigger
|
|
44
|
-
type="button"
|
|
45
|
-
class={`rail-item text-blue-500 hover:bg-blue-500/10 hover:text-blue-600 dark:text-blue-400 dark:hover:bg-blue-500/15 dark:hover:text-blue-300 ${props.class ?? ""}`}
|
|
46
|
-
onClick={openHelp}
|
|
47
|
-
aria-label={t().openHelp}
|
|
48
|
-
placement="right"
|
|
49
|
-
delay={0}
|
|
50
|
-
content={t().helpShortcut}
|
|
51
|
-
>
|
|
52
|
-
<i class="ti ti-help-circle text-base" />
|
|
53
|
-
</Tooltip.Trigger>
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<IconButton class={props.class} onClick={openHelp} label={t().openHelp} title={t().helpShortcut}>
|
|
58
|
-
<i class="ti ti-help-circle text-base" />
|
|
59
|
-
</IconButton>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { PageTitle, usePageTrail } from "@k2b/ui";
|
|
2
|
-
import { Show } from "solid-js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The shell's own place for the way up, above pages without a workspace. It
|
|
6
|
-
* shows only on a page with a page above it, and steps back when the page
|
|
7
|
-
* renders a workspace (whose head shows it) or its own `PageTrail`.
|
|
8
|
-
*/
|
|
9
|
-
export default function PageTrailRow() {
|
|
10
|
-
const trail = usePageTrail();
|
|
11
|
-
const deep = () => {
|
|
12
|
-
const current = trail();
|
|
13
|
-
return current?.ancestors.length ? current : undefined;
|
|
14
|
-
};
|
|
15
|
-
return (
|
|
16
|
-
<div class="layout-page-head" data-trail={deep() ? "up" : undefined}>
|
|
17
|
-
<Show when={deep()}>{(current) => <PageTitle trail={current()} as="p" fallback />}</Show>
|
|
18
|
-
</div>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { followPageTrail, installPageTitleController, useLocale } from "@k2b/ui";
|
|
2
|
-
import { onCleanup, onMount } from "solid-js";
|
|
3
|
-
import { installPageTrailRuntime } from "./page-trail-runtime";
|
|
4
|
-
import { platformMessages } from "./platform-messages";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* One owner per document for the way up: it collapses every page title path
|
|
8
|
-
* to its space, keeps server-rendered workspace heads current after
|
|
9
|
-
* `layout.update()`, runs the per-tab memory behind up links, and offers
|
|
10
|
-
* "One level up" (Alt+↑) while the page has a page above it.
|
|
11
|
-
*/
|
|
12
|
-
export default function PageTrailRuntime() {
|
|
13
|
-
const locale = useLocale();
|
|
14
|
-
onMount(() => {
|
|
15
|
-
const t = platformMessages.resolve([locale()]).t;
|
|
16
|
-
const disposeTitles = installPageTitleController(document);
|
|
17
|
-
const disposeFollow = followPageTrail(document);
|
|
18
|
-
const disposeRuntime = installPageTrailRuntime((page) => ({ title: t.pageUp({ page }), description: t.pageUpDescription }));
|
|
19
|
-
onCleanup(() => {
|
|
20
|
-
disposeTitles();
|
|
21
|
-
disposeFollow();
|
|
22
|
-
disposeRuntime();
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
File without changes
|
|
File without changes
|