@k2b/cloud 0.40.0 → 0.42.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 +9 -5
- package/src/_internal/bounded-json.ts +14 -2
- package/src/_internal/capabilities.ts +87 -8
- package/src/_internal/capability-reviews.ts +6 -0
- package/src/_internal/capability-sentences.ts +2 -1
- package/src/_internal/capability-timeout.ts +71 -0
- package/src/_internal/define-app.ts +158 -92
- package/src/_internal/heartbeat.ts +203 -14
- package/src/_internal/http-drain.ts +56 -0
- package/src/_internal/registry-validation.ts +3 -0
- package/src/_internal/registry.ts +28 -1
- package/src/_internal/runtime-context.ts +1 -0
- package/src/_internal/setup-lock.ts +103 -0
- package/src/_internal/status-preserving-ssr.ts +2 -0
- package/src/access/PermissionEditor.tsx +16 -17
- package/src/access/PrincipalPicker.tsx +45 -10
- package/src/access/messages.ts +8 -0
- package/src/access/principal-label.ts +18 -0
- package/src/access/ui.ts +1 -0
- package/src/account/EntitySearch.tsx +5 -4
- package/src/ai/action-review.ts +2 -1
- package/src/ai/app-skill-store.ts +1 -1
- package/src/ai/capabilities.ts +21 -3
- package/src/ai/capability-execution.ts +14 -12
- package/src/ai/chat/action-card.ts +76 -4
- package/src/ai/chat/action-messages.ts +14 -0
- package/src/ai/chat/action-question.browser-harness.tsx +12 -0
- package/src/ai/chat/action-question.tsx +214 -23
- package/src/ai/chat/action-text.ts +288 -0
- package/src/ai/chat/tool-action-words.ts +31 -0
- package/src/ai/chat/tool-action.tsx +1 -26
- package/src/ai/chat-tasks.ts +2 -1
- package/src/ai/client/controller.ts +150 -32
- package/src/ai/client/transport.ts +14 -2
- package/src/ai/code-capability-routes.ts +27 -23
- package/src/ai/code-runtime-tools.ts +14 -16
- package/src/ai/code-source-tools.ts +16 -18
- package/src/ai/executor.ts +64 -21
- package/src/ai/fetch-file-tool.ts +67 -76
- package/src/ai/firecrawl-tools.ts +15 -2
- package/src/ai/http.ts +1 -0
- package/src/ai/index.ts +1 -0
- package/src/ai/migrate.ts +28 -0
- package/src/ai/open-questions.ts +168 -0
- package/src/ai/projects.ts +1 -2
- package/src/ai/protocol.ts +2 -2
- package/src/ai/question-text.ts +8 -0
- package/src/ai/recent-resources.ts +12 -5
- package/src/ai/review-content.ts +77 -0
- package/src/ai/routes.ts +119 -35
- package/src/ai/runtime.ts +1 -0
- package/src/ai/skill-tool.ts +17 -6
- package/src/ai/skills-routes.ts +72 -27
- package/src/ai/skills.ts +139 -60
- package/src/ai/store.ts +184 -31
- package/src/ai/testing.ts +36 -0
- package/src/ai/tool-sentences.ts +152 -34
- package/src/ai/tool-wording.ts +12 -6
- package/src/ai/tools.ts +8 -3
- package/src/ai/types.ts +25 -4
- package/src/ai/usage.ts +2 -2
- package/src/api/admin-core-settings.ts +15 -2
- package/src/api/admin-geo.ts +54 -0
- package/src/api/admin-linux-identities.ts +6 -3
- package/src/api/admin-outgoing-mail.ts +29 -15
- package/src/api/app-approval-actions.ts +31 -7
- package/src/api/approvals-loader.ts +3 -3
- package/src/api/approvals.ts +51 -0
- package/src/api/apps.ts +4 -2
- package/src/api/auth/schemas.ts +3 -0
- package/src/api/auth.ts +54 -18
- package/src/api/capabilities.ts +258 -181
- package/src/api/capability-streams.ts +21 -13
- package/src/api/cli-plugins.ts +4 -1
- package/src/api/geo-glyphs.ts +11 -0
- package/src/api/geo.ts +128 -0
- package/src/api/help.ts +12 -4
- package/src/api/index.ts +5 -0
- package/src/api/mcp.ts +73 -41
- package/src/api/me-panel-consent.ts +8 -5
- package/src/api/me.ts +79 -3
- package/src/api/search.ts +21 -14
- package/src/api/sync-ops.ts +19 -13
- package/src/api/widgets.ts +42 -18
- package/src/approvals/Approvals.tsx +30 -66
- package/src/approvals/approval-card.ts +68 -0
- package/src/browser/CloudResourceSearch.browser-harness.tsx +53 -1
- package/src/browser/CloudResourceSearch.tsx +3 -13
- package/src/browser/PanelLeaveNotice.tsx +18 -0
- package/src/browser/navigation-search.ts +78 -25
- package/src/browser/panel-leave.ts +69 -0
- package/src/browser/panel-messages.ts +12 -0
- package/src/browser/panel-slot.ts +173 -24
- package/src/browser/panel.ts +3 -0
- package/src/browser/resource-search-messages.ts +2 -0
- package/src/capabilities/client.ts +28 -1
- package/src/capabilities/executions.ts +22 -3
- package/src/capabilities/geo-client.ts +57 -0
- package/src/capabilities/response.ts +2 -1
- package/src/capabilities/review-storage.ts +24 -0
- package/src/capabilities/reviews.ts +17 -3
- package/src/capabilities/server.ts +36 -16
- package/src/cli/access.ts +15 -23
- package/src/cli/admin/linux.ts +13 -2
- package/src/cli/admin/outgoing-mail.ts +26 -4
- package/src/cli/capabilities.ts +19 -0
- package/src/config/env.ts +0 -6
- package/src/contracts/app-access.ts +48 -0
- package/src/contracts/app-approval.ts +3 -3
- package/src/contracts/app.ts +4 -1
- package/src/contracts/approvals.ts +2 -2
- package/src/contracts/avatar.ts +186 -0
- package/src/contracts/capabilities.ts +149 -55
- package/src/contracts/geo-admin.ts +127 -0
- package/src/contracts/geo-map.ts +19 -0
- package/src/contracts/geo-route.ts +168 -0
- package/src/contracts/geo.ts +248 -0
- package/src/contracts/index.ts +4 -0
- package/src/contracts/outgoing-mail.ts +17 -4
- package/src/contracts/registry.ts +2 -0
- package/src/contracts/shared.ts +3 -0
- package/src/events/app-access.ts +68 -0
- package/src/events/live-delivery.ts +128 -0
- package/src/events/live.ts +14 -130
- package/src/geo/CloudMap.tsx +95 -0
- package/src/geo/map-messages.ts +17 -0
- package/src/geo/ui.ts +1 -0
- package/src/index.ts +2 -0
- package/src/server/client-ip.ts +36 -0
- package/src/server/index.ts +3 -4
- package/src/server/middleware/app-access.ts +312 -0
- package/src/server/middleware/auth.ts +5 -3
- package/src/server/middleware/invocation.ts +11 -0
- package/src/server/middleware/rate-limit.ts +4 -4
- package/src/server/services/access.ts +13 -9
- package/src/server/services/index.ts +0 -2
- package/src/server/services/services.ts +0 -2
- package/src/services/accounts/app.ts +23 -3
- package/src/services/accounts/posix.ts +21 -7
- package/src/services/accounts/users.ts +12 -1
- package/src/services/allowed-network-fetch.ts +16 -10
- package/src/services/app-access-metrics.ts +24 -0
- package/src/services/app-access.ts +123 -0
- package/src/services/app-discovery.ts +27 -0
- package/src/services/approvals/content.ts +40 -0
- package/src/services/approvals/device.ts +49 -33
- package/src/services/approvals/index.ts +15 -6
- package/src/services/approvals/list.ts +6 -11
- package/src/services/approvals/question.ts +72 -0
- package/src/services/audit/index.ts +1 -0
- package/src/services/auth-flows/ipa.ts +39 -2
- package/src/services/auth-flows/password-reset.ts +32 -6
- package/src/services/downstream-budget.ts +87 -0
- package/src/services/geo/admin.ts +262 -0
- package/src/services/geo/bucket.ts +67 -0
- package/src/services/geo/cache.ts +57 -0
- package/src/services/geo/costs.ts +5 -0
- package/src/services/geo/errors.ts +41 -0
- package/src/services/geo/fixtures/README.md +17 -0
- package/src/services/geo/fixtures/along.json +355 -0
- package/src/services/geo/fixtures/autocomplete.json +218 -0
- package/src/services/geo/fixtures/geocode.json +129 -0
- package/src/services/geo/fixtures/places.json +355 -0
- package/src/services/geo/fixtures/reverse.json +61 -0
- package/src/services/geo/fixtures/routing.json +57 -0
- package/src/services/geo/geoapify-route.ts +93 -0
- package/src/services/geo/geoapify.ts +281 -0
- package/src/services/geo/ledger.ts +122 -0
- package/src/services/geo/map.ts +160 -0
- package/src/services/geo/migrate.ts +25 -0
- package/src/services/geo/polyline.ts +184 -0
- package/src/services/geo/route-chain.ts +145 -0
- package/src/services/geo/route-costs.ts +9 -0
- package/src/services/geo/route-geometry.ts +39 -0
- package/src/services/geo/route-plan.ts +172 -0
- package/src/services/geo/route-service.ts +433 -0
- package/src/services/geo/service.ts +300 -0
- package/src/services/geo/settings.ts +49 -0
- package/src/services/help/index.ts +16 -7
- package/src/services/help/types.ts +6 -1
- package/src/services/identity/invocation-actor.ts +20 -2
- package/src/services/identity/issue-invocation.ts +67 -0
- package/src/services/identity/key-ring.ts +5 -3
- package/src/services/identity/runtime-config.ts +23 -0
- package/src/services/index.ts +11 -0
- package/src/services/mandates/index.ts +40 -3
- package/src/services/network-security.ts +6 -3
- package/src/services/network-transport.ts +99 -0
- package/src/services/notifications/catalog.ts +3 -2
- package/src/services/notifications/runtime.ts +1 -0
- package/src/services/notifications/web-push-transport.ts +45 -38
- package/src/services/outgoing-mail/dispatcher.ts +6 -1
- package/src/services/outgoing-mail/index.ts +55 -1
- package/src/services/outgoing-mail/messages.ts +82 -3
- package/src/services/outgoing-mail/resolve-host.ts +2 -2
- package/src/services/outgoing-mail/retention.ts +13 -1
- package/src/services/outgoing-mail/store.ts +36 -6
- package/src/services/outgoing-mail/test-send.ts +34 -4
- package/src/services/outgoing-mail/transport.ts +1 -1
- package/src/services/pdf/gotenberg.ts +11 -38
- package/src/services/pdf/markdown.ts +160 -2
- package/src/services/pdf/template-preview.ts +1 -1
- package/src/services/public-http.ts +28 -40
- package/src/services/session/index.ts +203 -29
- package/src/services/settings/app.ts +4 -1
- package/src/services/settings/core-settings.ts +88 -0
- package/src/services/settings/defaults.ts +8 -2
- package/src/services/settings/errors.ts +11 -0
- package/src/services/settings/geo-budget.ts +40 -0
- package/src/services/settings/index.ts +4 -3
- package/src/services/settings/store.ts +25 -17
- package/src/services/weather/geo.ts +29 -111
- package/src/services/weather/index.ts +4 -3
- package/src/services/weather/locations.ts +8 -5
- package/src/services/weather/migrate.ts +1 -0
- package/src/services/weather/settings.ts +0 -16
- package/src/shared/__snapshots__/avatar-art.test.ts.snap +5 -0
- package/src/shared/avatar-art.ts +400 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/network-address.ts +22 -3
- package/src/shared/template-rendering.ts +36 -4
- package/src/shared-text/documents.ts +1 -1
- package/src/shared-text/index.ts +2 -0
- package/src/shared-text/log.ts +51 -3
- package/src/shared-text/snapshots.ts +340 -0
- package/src/ssr/AdminLayout.tsx +4 -2
- package/src/ssr/CloudShell.island.tsx +6 -0
- package/src/ssr/HelpPanelArticle.island.tsx +308 -0
- package/src/ssr/HelpPanelBack.island.tsx +32 -0
- package/src/ssr/HelpPanelMenu.island.tsx +133 -0
- package/src/ssr/HelpPanelSearch.island.tsx +123 -0
- package/src/ssr/HelpPanelShortcuts.island.tsx +22 -0
- package/src/ssr/HelpPanelView.island.tsx +27 -0
- package/src/ssr/Layout.tsx +65 -15
- package/src/ssr/LayoutHelp.browser-harness.tsx +1 -1
- package/src/ssr/LayoutHelp.tsx +185 -542
- package/src/ssr/LayoutHelpPanel.browser-harness.tsx +6 -7
- package/src/ssr/LayoutHelpPanel.server.browser-harness.tsx +131 -0
- package/src/ssr/LayoutHelpPanel.server.tsx +115 -0
- package/src/ssr/PanelRuntime.island.tsx +11 -7
- package/src/ssr/PwaLayout.tsx +2 -2
- package/src/ssr/RailApps.tsx +6 -0
- package/src/ssr/ShellRuntime.island.tsx +23 -7
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +151 -0
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +29 -0
- package/src/ssr/admin-navigation.ts +1 -0
- package/src/ssr/app-badges.ts +6 -1
- package/src/ssr/compact-shell.browser-harness.ts +44 -5
- package/src/ssr/help.ts +7 -5
- package/src/ssr/index.ts +2 -1
- package/src/ssr/islands/index.ts +3 -4
- package/src/ssr/layout-context.ts +2 -0
- package/src/ssr/layout-help-panel-controls.tsx +43 -0
- package/src/ssr/layout-help-panel-document.ts +67 -0
- package/src/ssr/layout-help-panel-lifecycle.ts +32 -0
- package/src/ssr/layout-help-panel-menu.tsx +113 -0
- package/src/ssr/layout-help-panel-modules.ts +7 -0
- package/src/ssr/layout-help-panel-reader.tsx +159 -0
- package/src/ssr/layout-help-panel-search.tsx +69 -0
- package/src/ssr/layout-help-panel-shortcuts.ts +7 -0
- package/src/ssr/layout-help-panel-state.ts +141 -0
- package/src/ssr/layout-help-panel-types.ts +19 -0
- package/src/ssr/layout-help-views.tsx +484 -0
- package/src/ssr/mobile-menu-history.ts +4 -0
- package/src/ssr/page-swap-fixture/Column.island.tsx +15 -0
- package/src/ssr/page-swap-fixture/ProviderProbe.island.tsx +31 -0
- package/src/ssr/page-swap.browser-harness.tsx +177 -0
- package/src/ssr/page-swap.ts +28 -0
- package/src/ssr/page-trail-runtime.ts +32 -13
- package/src/ssr/panel-consent.ts +2 -2
- package/src/ssr/panel-idle.ts +9 -0
- package/src/ssr/panel-slot.browser-harness.tsx +3 -0
- package/src/ssr/platform-messages.ts +8 -0
- package/src/ssr/runtime.ts +17 -0
- package/src/ssr/shell-data.tsx +10 -2
- package/src/styles/approvals.css +6 -0
- package/src/styles/effects.css +15 -0
- package/src/styles/global.css +70 -6
- package/src/server/services/geo.ts +0 -215
package/src/ssr/LayoutHelp.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HelpArticleLink, HelpDocumentManifest, HelpDocumentPayload } from "@k2b/cloud/shared";
|
|
2
|
+
import { listenPopState } from "@k2b/ssr/nav";
|
|
2
3
|
import { clipboard } from "@k2b/stdlib/solid";
|
|
3
4
|
import {
|
|
4
5
|
BottomSheet,
|
|
@@ -7,8 +8,6 @@ import {
|
|
|
7
8
|
Dropdown,
|
|
8
9
|
dialogCore,
|
|
9
10
|
IconButton,
|
|
10
|
-
Kbd,
|
|
11
|
-
type KeyboardPlatform,
|
|
12
11
|
MarkdownView,
|
|
13
12
|
NoticeCard,
|
|
14
13
|
Placeholder,
|
|
@@ -16,14 +15,12 @@ import {
|
|
|
16
15
|
prompts,
|
|
17
16
|
ScrollArea,
|
|
18
17
|
TableOfContents,
|
|
19
|
-
TextInput,
|
|
20
18
|
toast,
|
|
21
19
|
useLocale,
|
|
22
20
|
} from "@k2b/ui";
|
|
23
|
-
import { createEffect, createMemo, createSignal,
|
|
21
|
+
import { createEffect, createMemo, createSignal, type JSX, onCleanup, onMount, Show, untrack } from "solid-js";
|
|
24
22
|
import { COMMANDS_CHANGED, contextCommandsWithShortcuts } from "../browser/command-shortcuts";
|
|
25
|
-
import {
|
|
26
|
-
import { onPanelShortcutsChange, panelShortcutsReceived, readPanelShortcuts } from "../browser/panel-shortcuts";
|
|
23
|
+
import { openPanel } from "../browser/panel";
|
|
27
24
|
import { HELP_ACCESS_LEVELS, HELP_KINDS } from "../shared/help";
|
|
28
25
|
import { appAccentStyle } from "./app-appearance";
|
|
29
26
|
import type { LayoutHelpArticle } from "./help";
|
|
@@ -33,14 +30,9 @@ import { formatHelpBundleMarkdown, formatHelpDocumentMarkdown } from "./layout-h
|
|
|
33
30
|
import { focusHelpArticleHeading, resetHelpArticleScroll } from "./layout-help-navigation";
|
|
34
31
|
import { HELP_TOPICS_EVENT, type HelpTopic, registry } from "./layout-help-registry";
|
|
35
32
|
import { searchHelpTopics } from "./layout-help-search";
|
|
36
|
-
import {
|
|
37
|
-
type HelpShortcutCommand,
|
|
38
|
-
type HelpShortcutScope,
|
|
39
|
-
type HelpStartGroupKind,
|
|
40
|
-
helpShortcutGroups,
|
|
41
|
-
helpStart,
|
|
42
|
-
} from "./layout-help-start";
|
|
33
|
+
import { type HelpShortcutCommand, helpShortcutGroups, helpStart } from "./layout-help-start";
|
|
43
34
|
import { helpLinkTarget, layoutHelpTopicHref } from "./layout-help-url";
|
|
35
|
+
import { HelpArticleView, HelpHead, HelpResultsView, HelpShortcutsView, HelpStartView, type HelpSurface } from "./layout-help-views";
|
|
44
36
|
import { platformMessages } from "./platform-messages";
|
|
45
37
|
import { readShellData, useShellData } from "./shell-data";
|
|
46
38
|
import { shellShortcuts } from "./shell-shortcuts";
|
|
@@ -58,22 +50,6 @@ export type LayoutHelpPageProps = {
|
|
|
58
50
|
pageBase?: string;
|
|
59
51
|
};
|
|
60
52
|
|
|
61
|
-
export type LayoutHelpPanelProps = {
|
|
62
|
-
documents: readonly HelpDocumentManifest[];
|
|
63
|
-
/** Absent when the context selected Help the reader may not see. */
|
|
64
|
-
app?: { id: string; name: string; icon?: string };
|
|
65
|
-
/** appAppearanceStyle() of the app, from the server. */
|
|
66
|
-
appearance?: string;
|
|
67
|
-
/** Server-filtered For this view ids. */
|
|
68
|
-
view?: readonly string[];
|
|
69
|
-
initialTopic?: string;
|
|
70
|
-
/** Authorized article and server-parsed parts for an identical first frame. */
|
|
71
|
-
initialArticle?: LayoutHelpArticle;
|
|
72
|
-
pageBase?: string;
|
|
73
|
-
platform: KeyboardPlatform;
|
|
74
|
-
state?: "failed" | "unavailable";
|
|
75
|
-
};
|
|
76
|
-
|
|
77
53
|
type HelpView = "hub" | "article";
|
|
78
54
|
type HelpSession = {
|
|
79
55
|
view: HelpView;
|
|
@@ -101,8 +77,6 @@ const articleLink = (value: unknown): HelpArticleLink | undefined => {
|
|
|
101
77
|
return { appId, appName, accent, documentId, title, description, icon, url };
|
|
102
78
|
};
|
|
103
79
|
|
|
104
|
-
/** An article without its own icon gets a neutral page tile. */
|
|
105
|
-
const iconClass = (icon?: string) => (icon?.startsWith("ti ") ? icon : `ti ${icon ?? "ti-file-text"}`);
|
|
106
80
|
const byOrder = (a: HelpTopic, b: HelpTopic) => (a.order ?? 100) - (b.order ?? 100) || a.title.localeCompare(b.title);
|
|
107
81
|
const sortedTopics = () => [...(registry()?.values() ?? [])].sort(byOrder);
|
|
108
82
|
const mergeTopics = (registered: readonly HelpTopic[], documents: readonly HelpDocumentManifest[] = []): HelpTopic[] => {
|
|
@@ -110,11 +84,6 @@ const mergeTopics = (registered: readonly HelpTopic[], documents: readonly HelpD
|
|
|
110
84
|
for (const topic of documents) topics.set(topic.id, topic);
|
|
111
85
|
return [...topics.values()].sort(byOrder);
|
|
112
86
|
};
|
|
113
|
-
const GROUP_ICONS: Record<HelpStartGroupKind, string> = {
|
|
114
|
-
start: "ti ti-rocket",
|
|
115
|
-
reference: "ti ti-book-2",
|
|
116
|
-
troubleshoot: "ti ti-lifebuoy",
|
|
117
|
-
};
|
|
118
87
|
|
|
119
88
|
/**
|
|
120
89
|
* Keyboard shortcuts come last in the start. The browser knows the page's commands. On a Layout page (it has shell
|
|
@@ -122,68 +91,22 @@ const GROUP_ICONS: Record<HelpStartGroupKind, string> = {
|
|
|
122
91
|
* first client render on, also before the shell has registered them. The groups reserve their height and place in CSS,
|
|
123
92
|
* so the ones the browser adds fill space that is already there.
|
|
124
93
|
*/
|
|
125
|
-
const StartShortcuts = (props: { appName?: string
|
|
94
|
+
const StartShortcuts = (props: { appName?: string }) => {
|
|
126
95
|
const locale = useLocale();
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const seed: HelpShortcutCommand[] =
|
|
130
|
-
props.panel || shell ? shellShortcuts(platformMessages.resolve([locale()]).t, { panel: props.panel || !!shell?.panel }) : [];
|
|
96
|
+
const shell = useShellData();
|
|
97
|
+
const seed: HelpShortcutCommand[] = shell ? shellShortcuts(platformMessages.resolve([locale()]).t, { panel: !!shell.panel }) : [];
|
|
131
98
|
const read = (): HelpShortcutCommand[] => {
|
|
132
|
-
// Once received, the host list is authoritative: omitted shortcuts must stay omitted.
|
|
133
|
-
if (props.panel) return panelShortcutsReceived() && getContext() !== null ? readPanelShortcuts() : seed;
|
|
134
99
|
const commands = contextCommandsWithShortcuts();
|
|
135
100
|
return [...seed.filter((command) => !commands.some((other) => other.id === command.id)), ...commands];
|
|
136
101
|
};
|
|
137
|
-
const [commands, setCommands] = createSignal<HelpShortcutCommand[]>(
|
|
102
|
+
const [commands, setCommands] = createSignal<HelpShortcutCommand[]>(read());
|
|
138
103
|
onMount(() => {
|
|
139
104
|
const update = () => setCommands(read());
|
|
140
|
-
if (props.panel) {
|
|
141
|
-
onCleanup(onPanelShortcutsChange(update));
|
|
142
|
-
onCleanup(onContextChange(update));
|
|
143
|
-
}
|
|
144
105
|
update();
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
onCleanup(() => window.removeEventListener(COMMANDS_CHANGED, update));
|
|
148
|
-
}
|
|
106
|
+
window.addEventListener(COMMANDS_CHANGED, update);
|
|
107
|
+
onCleanup(() => window.removeEventListener(COMMANDS_CHANGED, update));
|
|
149
108
|
});
|
|
150
|
-
|
|
151
|
-
scope === "everywhere"
|
|
152
|
-
? t().shortcutsEverywhere
|
|
153
|
-
: scope === "selection"
|
|
154
|
-
? t().shortcutsWithSelection
|
|
155
|
-
: props.appName
|
|
156
|
-
? t().shortcutsInApp({ app: props.appName })
|
|
157
|
-
: t().shortcutsInThisApp;
|
|
158
|
-
return (
|
|
159
|
-
<section class="help-start-section help-start-shortcuts" data-help-shortcuts="" aria-labelledby="help-start-shortcuts-title">
|
|
160
|
-
<h3 id="help-start-shortcuts-title" class="help-start-heading">
|
|
161
|
-
<i class="ti ti-keyboard" aria-hidden="true" />
|
|
162
|
-
{t().keyboardShortcuts}
|
|
163
|
-
</h3>
|
|
164
|
-
<div class="help-shortcut-groups">
|
|
165
|
-
<For each={helpShortcutGroups(commands())}>
|
|
166
|
-
{(group) => (
|
|
167
|
-
<section class="help-shortcut-group" data-help-shortcut-group={group.scope} aria-label={label(group.scope)}>
|
|
168
|
-
<h4 class="help-shortcut-group-title" aria-hidden="true">
|
|
169
|
-
{label(group.scope)}
|
|
170
|
-
</h4>
|
|
171
|
-
<ul>
|
|
172
|
-
<For each={group.shortcuts}>
|
|
173
|
-
{(command) => (
|
|
174
|
-
<li class="help-shortcut" title={command.description}>
|
|
175
|
-
<span class="help-shortcut-title">{command.title}</span>
|
|
176
|
-
<Kbd keys={command.shortcut} platform={props.platform ?? shell?.platform} />
|
|
177
|
-
</li>
|
|
178
|
-
)}
|
|
179
|
-
</For>
|
|
180
|
-
</ul>
|
|
181
|
-
</section>
|
|
182
|
-
)}
|
|
183
|
-
</For>
|
|
184
|
-
</div>
|
|
185
|
-
</section>
|
|
186
|
-
);
|
|
109
|
+
return <HelpShortcutsView groups={helpShortcutGroups(commands())} appName={props.appName} platform={shell?.platform} />;
|
|
187
110
|
};
|
|
188
111
|
|
|
189
112
|
const HelpShell = (props: {
|
|
@@ -196,29 +119,29 @@ const HelpShell = (props: {
|
|
|
196
119
|
accent?: string;
|
|
197
120
|
/** The app's canvas colours for a reader outside the app canvas. */
|
|
198
121
|
appearance?: string;
|
|
199
|
-
surface
|
|
200
|
-
panel?: LayoutHelpPanelProps;
|
|
122
|
+
surface: HelpSurface;
|
|
201
123
|
syncPageUrl?: boolean;
|
|
202
124
|
}) => {
|
|
203
125
|
const locale = useLocale();
|
|
204
126
|
const t = () => helpMessages.resolve([locale()]).t;
|
|
205
127
|
// Links to this app's Help, by its own route or Core's `/help/apps/<id>`, open in place.
|
|
206
|
-
const shell =
|
|
207
|
-
const appId =
|
|
128
|
+
const shell = useShellData();
|
|
129
|
+
const appId = shell?.appId;
|
|
208
130
|
const app = shell?.apps.find((candidate) => candidate.id === appId);
|
|
209
|
-
const appName =
|
|
210
|
-
const
|
|
211
|
-
const registeredTopics = () => (props.surface === "panel" ? [] : sortedTopics());
|
|
212
|
-
const accessLabel = (level: HelpAccessLevel) => ({ view: t().accessView, edit: t().accessEdit, manage: t().accessManage })[level];
|
|
131
|
+
const appName = app?.label;
|
|
132
|
+
const registeredTopics = () => sortedTopics();
|
|
213
133
|
const [externalTopics, setExternalTopics] = createSignal(mergeTopics(registeredTopics(), props.documents));
|
|
214
134
|
const [view, setView] = createSignal<HelpView>(props.session.view);
|
|
215
135
|
const [query, setQuery] = createSignal(props.session.query);
|
|
216
136
|
const [activeId, setActiveId] = createSignal<string | null>(props.session.activeId);
|
|
217
137
|
const initialTopic = props.documents?.find((topic) => topic.id === props.session.activeId);
|
|
218
138
|
const initialArticle = initialTopic && props.initialArticle?.payload.id === initialTopic.id ? props.initialArticle : undefined;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
139
|
+
// HTML travels once as parts; rebuild it for the cache and the article settle effect.
|
|
140
|
+
const seeded = initialArticle
|
|
141
|
+
? { ...initialArticle.payload, html: (initialArticle.parts.lead ?? "") + initialArticle.parts.body }
|
|
142
|
+
: undefined;
|
|
143
|
+
if (seeded && initialTopic) props.session.articleCache.set(`${initialTopic.locale ?? "en"}:${initialTopic.url}`, seeded);
|
|
144
|
+
const [payload, setPayload] = createSignal<HelpDocumentPayload | null>(seeded ?? null);
|
|
222
145
|
const [loading, setLoading] = createSignal(props.session.view === "article" && !initialArticle);
|
|
223
146
|
const [loadError, setLoadError] = createSignal<string | null>(null);
|
|
224
147
|
const [loadAttempt, setLoadAttempt] = createSignal(0);
|
|
@@ -266,7 +189,7 @@ const HelpShell = (props: {
|
|
|
266
189
|
};
|
|
267
190
|
|
|
268
191
|
const restoreArticleScroll = () => {
|
|
269
|
-
if (
|
|
192
|
+
if (props.surface !== "modal" || pendingNavigation || pendingHash) return;
|
|
270
193
|
requestAnimationFrame(() => {
|
|
271
194
|
if (scrollArea && view() === "article") scrollArea.scrollTop = props.session.articleScrollTop;
|
|
272
195
|
});
|
|
@@ -311,14 +234,12 @@ const HelpShell = (props: {
|
|
|
311
234
|
};
|
|
312
235
|
|
|
313
236
|
const topics = externalTopics;
|
|
314
|
-
const start = createMemo(() =>
|
|
315
|
-
helpStart(topics(), props.surface === "panel" ? viewIds() : props.surface === "modal" ? shell?.help : undefined),
|
|
316
|
-
);
|
|
237
|
+
const start = createMemo(() => helpStart(topics(), props.surface === "modal" ? shell?.help : undefined));
|
|
317
238
|
const activeTopic = createMemo(() => topics().find((topic) => topic.id === activeId()) ?? null);
|
|
318
239
|
// Reuse the serialized parts for the initial object on both sides of the island boundary.
|
|
319
240
|
const articleParts = createMemo(() => {
|
|
320
241
|
const article = payload();
|
|
321
|
-
if (initialArticle && article ===
|
|
242
|
+
if (initialArticle && article === seeded) return initialArticle.parts;
|
|
322
243
|
return article?.html ? helpArticleParts(article.html) : null;
|
|
323
244
|
});
|
|
324
245
|
const articleSections = (): HelpSection[] => (view() === "article" ? articleParts()?.sections : undefined) ?? [];
|
|
@@ -409,10 +330,7 @@ const HelpShell = (props: {
|
|
|
409
330
|
createEffect(syncSession);
|
|
410
331
|
|
|
411
332
|
onMount(() => {
|
|
412
|
-
if (props.surface === "
|
|
413
|
-
onCleanup(onContextChange((context) => setViewIds(context && context.app === appId ? (context.help ?? []) : [])));
|
|
414
|
-
}
|
|
415
|
-
if (props.surface === "panel" || props.surface === "modal") {
|
|
333
|
+
if (props.surface === "modal") {
|
|
416
334
|
const key = (event: KeyboardEvent) => {
|
|
417
335
|
if (event.defaultPrevented || event.isComposing) return;
|
|
418
336
|
const target = event.target;
|
|
@@ -445,7 +363,7 @@ const HelpShell = (props: {
|
|
|
445
363
|
const onScroll = () => {
|
|
446
364
|
if (view() === "article" && scrollArea) props.session.articleScrollTop = scrollArea.scrollTop;
|
|
447
365
|
};
|
|
448
|
-
if (props.surface === "modal"
|
|
366
|
+
if (props.surface === "modal") scrollArea?.addEventListener("scroll", onScroll, { passive: true });
|
|
449
367
|
// Layout's breakpoints move page scrolling between its main column and the window; the contents follow.
|
|
450
368
|
let resizeFrame: number | undefined;
|
|
451
369
|
const onResize = () => {
|
|
@@ -466,12 +384,12 @@ const HelpShell = (props: {
|
|
|
466
384
|
// The hub keeps its search, as the reader's own Back button does. An unknown topic shows the hub, as the server does.
|
|
467
385
|
else setView("hub");
|
|
468
386
|
};
|
|
469
|
-
|
|
387
|
+
const stopPopState = props.syncPageUrl ? listenPopState(onPopState) : undefined;
|
|
470
388
|
onCleanup(() => {
|
|
471
389
|
scrollArea?.removeEventListener("scroll", onScroll);
|
|
472
390
|
window.removeEventListener("resize", onResize);
|
|
473
391
|
if (resizeFrame !== undefined) cancelAnimationFrame(resizeFrame);
|
|
474
|
-
|
|
392
|
+
stopPopState?.();
|
|
475
393
|
});
|
|
476
394
|
});
|
|
477
395
|
onCleanup(() => copyAllController?.abort());
|
|
@@ -704,99 +622,6 @@ const HelpShell = (props: {
|
|
|
704
622
|
}
|
|
705
623
|
};
|
|
706
624
|
|
|
707
|
-
/** Rows: icon tile, title, and description; the start's groups, its further tasks, and search results use them. */
|
|
708
|
-
const TopicList = (listProps: { items: HelpTopic[]; marker?: string }) => (
|
|
709
|
-
<ul class="help-start-rows" {...(listProps.marker ? { [listProps.marker]: "" } : {})}>
|
|
710
|
-
<For each={listProps.items}>
|
|
711
|
-
{(topic) => (
|
|
712
|
-
<li>
|
|
713
|
-
<button type="button" class="help-start-row focus-ui" data-help-topic={topic.id} onClick={() => openTopic(topic.id)}>
|
|
714
|
-
<span class="help-topic-icon k2b-icon-tile help-start-row-icon app-accent-text" aria-hidden="true">
|
|
715
|
-
<i class={iconClass(topic.icon)} />
|
|
716
|
-
</span>
|
|
717
|
-
<span class="help-article-link-copy">
|
|
718
|
-
<span class="help-article-link-title">{topic.title}</span>
|
|
719
|
-
<Show when={topic.description}>{(description) => <span class="help-article-link-description">{description()}</span>}</Show>
|
|
720
|
-
</span>
|
|
721
|
-
<i class="ti ti-chevron-right help-start-row-arrow" aria-hidden="true" />
|
|
722
|
-
</button>
|
|
723
|
-
</li>
|
|
724
|
-
)}
|
|
725
|
-
</For>
|
|
726
|
-
</ul>
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
/** At most eight tasks as tiles; the app colour shows only in the icon. */
|
|
730
|
-
const TaskTiles = (tilesProps: { items: HelpTopic[] }) => (
|
|
731
|
-
<ul class="help-start-tiles" data-help-tiles="">
|
|
732
|
-
<For each={tilesProps.items}>
|
|
733
|
-
{(topic) => (
|
|
734
|
-
<li>
|
|
735
|
-
<button type="button" class="help-start-tile focus-ui" data-help-topic={topic.id} onClick={() => openTopic(topic.id)}>
|
|
736
|
-
<span class="help-topic-icon k2b-icon-tile help-start-tile-icon app-accent-text" aria-hidden="true">
|
|
737
|
-
<i class={iconClass(topic.icon)} />
|
|
738
|
-
</span>
|
|
739
|
-
<span class="help-start-tile-title">{topic.title}</span>
|
|
740
|
-
<Show when={topic.description}>{(description) => <span class="help-start-tile-description">{description()}</span>}</Show>
|
|
741
|
-
</button>
|
|
742
|
-
</li>
|
|
743
|
-
)}
|
|
744
|
-
</For>
|
|
745
|
-
</ul>
|
|
746
|
-
);
|
|
747
|
-
|
|
748
|
-
const groupTitle = (kind: HelpStartGroupKind) =>
|
|
749
|
-
({ start: t().groupStart, reference: t().groupReference, troubleshoot: t().groupTroubleshoot })[kind];
|
|
750
|
-
|
|
751
|
-
const ArticleLinkTitle = (link: { link: HelpArticleLink }) => (
|
|
752
|
-
<span class="help-article-link-copy">
|
|
753
|
-
<span class="help-article-link-title">{link.link.title}</span>
|
|
754
|
-
<Show when={link.link.description}>{(description) => <span class="help-article-link-description">{description()}</span>}</Show>
|
|
755
|
-
</span>
|
|
756
|
-
);
|
|
757
|
-
|
|
758
|
-
/** "Next" leads on; related articles keep their own app's colour, and another app says whose article it is. */
|
|
759
|
-
const ArticleEnd = (end: { document: HelpDocumentPayload }) => (
|
|
760
|
-
<Show when={end.document.next || end.document.related.length > 0}>
|
|
761
|
-
<footer class="help-article-end">
|
|
762
|
-
<Show when={end.document.next}>
|
|
763
|
-
{(next) => (
|
|
764
|
-
<a class="help-article-next app-accent-scope focus-ui" href={next().url} style={appAccentStyle(next().accent)}>
|
|
765
|
-
<span class="help-article-next-label">{t().next}</span>
|
|
766
|
-
<span class="help-article-next-row">
|
|
767
|
-
<i class={`${iconClass(next().icon)} help-article-next-icon`} aria-hidden="true" />
|
|
768
|
-
<ArticleLinkTitle link={next()} />
|
|
769
|
-
<i class="ti ti-arrow-right help-article-next-arrow" aria-hidden="true" />
|
|
770
|
-
</span>
|
|
771
|
-
</a>
|
|
772
|
-
)}
|
|
773
|
-
</Show>
|
|
774
|
-
<Show when={end.document.related.length > 0}>
|
|
775
|
-
<section class="help-article-related" aria-labelledby="help-article-related-title">
|
|
776
|
-
<h4 id="help-article-related-title">{t().related}</h4>
|
|
777
|
-
<ul>
|
|
778
|
-
<For each={end.document.related}>
|
|
779
|
-
{(link) => (
|
|
780
|
-
<li>
|
|
781
|
-
<a class="help-article-related-link app-accent-scope focus-ui" href={link.url} style={appAccentStyle(link.accent)}>
|
|
782
|
-
<span class="help-topic-icon k2b-icon-tile help-article-related-icon app-accent-text" aria-hidden="true">
|
|
783
|
-
<i class={iconClass(link.icon)} />
|
|
784
|
-
</span>
|
|
785
|
-
<ArticleLinkTitle link={link} />
|
|
786
|
-
<Show when={link.appId !== appId}>
|
|
787
|
-
<span class="help-article-related-app app-accent-text">{link.appName}</span>
|
|
788
|
-
</Show>
|
|
789
|
-
</a>
|
|
790
|
-
</li>
|
|
791
|
-
)}
|
|
792
|
-
</For>
|
|
793
|
-
</ul>
|
|
794
|
-
</section>
|
|
795
|
-
</Show>
|
|
796
|
-
</footer>
|
|
797
|
-
</Show>
|
|
798
|
-
);
|
|
799
|
-
|
|
800
625
|
const ReaderContent = (content: { children: JSX.Element }) =>
|
|
801
626
|
props.surface === "page" ? (
|
|
802
627
|
<div class="p-4 sm:p-5">{content.children}</div>
|
|
@@ -806,8 +631,8 @@ const HelpShell = (props: {
|
|
|
806
631
|
</ScrollArea>
|
|
807
632
|
);
|
|
808
633
|
|
|
809
|
-
// The dialog
|
|
810
|
-
const copyInMenu = props.surface === "modal"
|
|
634
|
+
// The dialog copies from its ⋯ menu; only the page repeats the copy action beside the article.
|
|
635
|
+
const copyInMenu = props.surface === "modal";
|
|
811
636
|
const pageHref = () => (props.pageBase ? layoutHelpTopicHref(props.pageBase, view() === "article" ? activeId() : null) : undefined);
|
|
812
637
|
const menuItems = () => [
|
|
813
638
|
{
|
|
@@ -823,335 +648,169 @@ const HelpShell = (props: {
|
|
|
823
648
|
return (
|
|
824
649
|
<div
|
|
825
650
|
ref={root}
|
|
826
|
-
class={
|
|
827
|
-
props.surface === "
|
|
828
|
-
? "
|
|
829
|
-
:
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
? ""
|
|
834
|
-
: "h-full"
|
|
835
|
-
}`
|
|
836
|
-
}
|
|
651
|
+
class={`help-shell app-accent-scope flex min-h-0 flex-col ${props.surface === "page" ? "" : "bg-[var(--ui-surface-raised)]"} ${
|
|
652
|
+
props.surface === "modal"
|
|
653
|
+
? "h-[min(48rem,var(--ui-dialog-available-height))] overflow-hidden panel-dialog-shell [box-shadow:var(--ui-shadow-float)]"
|
|
654
|
+
: props.surface === "page"
|
|
655
|
+
? ""
|
|
656
|
+
: "h-full"
|
|
657
|
+
}`}
|
|
837
658
|
data-surface={props.surface}
|
|
838
|
-
style={
|
|
839
|
-
props.surface === "panel"
|
|
840
|
-
? props.panel?.appearance
|
|
841
|
-
: [appAccentStyle(props.accent), props.appearance].filter(Boolean).join(";") || undefined
|
|
842
|
-
}
|
|
659
|
+
style={[appAccentStyle(props.accent), props.appearance].filter(Boolean).join(";") || undefined}
|
|
843
660
|
>
|
|
844
|
-
{
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
<
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
{appName ? (props.surface === "panel" ? appName : t().helpFor({ app: appName })) : t().help}
|
|
867
|
-
</h2>
|
|
868
|
-
<div class="help-head-actions" role="group" aria-label={t().actions}>
|
|
869
|
-
<Dropdown.Root label={t().moreActions} position="bottom-left" items={menuItems()}>
|
|
870
|
-
<Dropdown.Trigger iconOnly label={t().moreActions} variant="ghost" data-help-menu="">
|
|
871
|
-
<i class="ti ti-dots" aria-hidden="true" />
|
|
872
|
-
</Dropdown.Trigger>
|
|
873
|
-
</Dropdown.Root>
|
|
874
|
-
<Show when={props.surface === "modal"}>
|
|
875
|
-
<IconButton label={t().closeHelp} title={t().close} onClick={props.close}>
|
|
876
|
-
<i class="ti ti-x" />
|
|
877
|
-
</IconButton>
|
|
878
|
-
</Show>
|
|
879
|
-
</div>
|
|
880
|
-
</div>
|
|
881
|
-
</Show>
|
|
882
|
-
<TextInput
|
|
883
|
-
ref={searchInput}
|
|
884
|
-
type="search"
|
|
885
|
-
class="help-head-search"
|
|
886
|
-
icon="ti ti-search"
|
|
887
|
-
aria-label={t().searchHelp}
|
|
888
|
-
data-help-search=""
|
|
889
|
-
value={query()}
|
|
890
|
-
placeholder={t().searchPlaceholder}
|
|
891
|
-
onValueChange={changeQuery}
|
|
892
|
-
/>
|
|
893
|
-
</div>
|
|
894
|
-
</header>
|
|
661
|
+
<Show when={props.surface !== "embedded" || view() === "hub"}>
|
|
662
|
+
<HelpHead
|
|
663
|
+
row={props.surface !== "embedded"}
|
|
664
|
+
back={view() === "article"}
|
|
665
|
+
tile={app?.iconClass ?? "ti ti-help-circle"}
|
|
666
|
+
title={appName ? t().helpFor({ app: appName }) : t().help}
|
|
667
|
+
menu={
|
|
668
|
+
<Dropdown.Root label={t().moreActions} position="bottom-left" items={menuItems()}>
|
|
669
|
+
<Dropdown.Trigger iconOnly label={t().moreActions} variant="ghost" data-help-menu="">
|
|
670
|
+
<i class="ti ti-dots" aria-hidden="true" />
|
|
671
|
+
</Dropdown.Trigger>
|
|
672
|
+
</Dropdown.Root>
|
|
673
|
+
}
|
|
674
|
+
closable={props.surface === "modal"}
|
|
675
|
+
onBack={goBack}
|
|
676
|
+
onClose={props.close}
|
|
677
|
+
query={query()}
|
|
678
|
+
onQuery={changeQuery}
|
|
679
|
+
searchRef={(element) => {
|
|
680
|
+
searchInput = element;
|
|
681
|
+
}}
|
|
682
|
+
/>
|
|
895
683
|
</Show>
|
|
896
684
|
|
|
897
685
|
<ReaderContent>
|
|
898
|
-
<Show
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
<h3 id="help-start-view-title" class="help-start-heading">
|
|
911
|
-
<i class="ti ti-focus-2" aria-hidden="true" />
|
|
912
|
-
{t().forThisView}
|
|
913
|
-
</h3>
|
|
914
|
-
<TopicList items={start().view} />
|
|
915
|
-
</section>
|
|
916
|
-
</Show>
|
|
917
|
-
<Show when={start().tiles.length > 0}>
|
|
918
|
-
<section class="help-start-section" data-help-tasks="" aria-labelledby="help-start-tasks-title">
|
|
919
|
-
<h3 id="help-start-tasks-title" class="help-start-heading">
|
|
920
|
-
<i class="ti ti-checklist" aria-hidden="true" />
|
|
921
|
-
{t().whatToDo}
|
|
922
|
-
</h3>
|
|
923
|
-
<TaskTiles items={start().tiles} />
|
|
924
|
-
<Show when={start().moreTasks.length > 0}>
|
|
925
|
-
<TopicList items={start().moreTasks} marker="data-help-more-tasks" />
|
|
926
|
-
</Show>
|
|
927
|
-
</section>
|
|
928
|
-
</Show>
|
|
929
|
-
<Show when={start().groups.length > 0}>
|
|
930
|
-
<div class="help-start-groups">
|
|
931
|
-
<For each={start().groups}>
|
|
932
|
-
{(group) => (
|
|
933
|
-
<section
|
|
934
|
-
class="help-start-section"
|
|
935
|
-
data-help-group={group.kind}
|
|
936
|
-
aria-labelledby={`help-start-${group.kind}-title`}
|
|
937
|
-
>
|
|
938
|
-
<h3 id={`help-start-${group.kind}-title`} class="help-start-heading">
|
|
939
|
-
<i class={GROUP_ICONS[group.kind]} aria-hidden="true" />
|
|
940
|
-
{groupTitle(group.kind)}
|
|
941
|
-
</h3>
|
|
942
|
-
<TopicList items={group.topics} />
|
|
943
|
-
</section>
|
|
944
|
-
)}
|
|
945
|
-
</For>
|
|
946
|
-
</div>
|
|
947
|
-
</Show>
|
|
948
|
-
{/* An app without Help says so plainly; only a failed read of its Help offers a way to try again. */}
|
|
949
|
-
<Show when={topics().length === 0}>
|
|
950
|
-
<Show
|
|
951
|
-
when={props.panel?.state === "failed" || shell?.helpFailed}
|
|
952
|
-
fallback={<Placeholder icon="ti ti-book-off" title={t().noArticles} description={t().noArticlesDetail} />}
|
|
953
|
-
>
|
|
954
|
-
<NoticeCard tone="danger" title={t().helpUnavailable} detail={t().helpUnavailableDetail} data-help-unavailable="">
|
|
955
|
-
<Button size="sm" variant="secondary" onClick={() => location.reload()}>
|
|
956
|
-
{t().reload}
|
|
957
|
-
</Button>
|
|
958
|
-
</NoticeCard>
|
|
959
|
-
</Show>
|
|
960
|
-
</Show>
|
|
686
|
+
<Show when={view() === "hub"}>
|
|
687
|
+
<div ref={resultsRegion} data-help-results="" class="help-start help-measure" style={{ "min-height": resultsMinHeight() }}>
|
|
688
|
+
<Show
|
|
689
|
+
when={normalizedQuery()}
|
|
690
|
+
fallback={
|
|
691
|
+
<HelpStartView
|
|
692
|
+
start={start()}
|
|
693
|
+
empty={topics().length === 0}
|
|
694
|
+
failed={!!shell?.helpFailed}
|
|
695
|
+
onOpen={openTopic}
|
|
696
|
+
onReload={() => location.reload()}
|
|
697
|
+
shortcuts={
|
|
961
698
|
<Show when={props.includeShortcuts !== false}>
|
|
962
|
-
<StartShortcuts appName={appName}
|
|
699
|
+
<StartShortcuts appName={appName} />
|
|
963
700
|
</Show>
|
|
964
|
-
|
|
701
|
+
}
|
|
702
|
+
/>
|
|
703
|
+
}
|
|
704
|
+
>
|
|
705
|
+
<HelpResultsView
|
|
706
|
+
results={results()}
|
|
707
|
+
searching={searching()}
|
|
708
|
+
failed={searchFailed()}
|
|
709
|
+
onRetry={() => setSearchAttempt((value) => value + 1)}
|
|
710
|
+
onOpen={openTopic}
|
|
711
|
+
/>
|
|
712
|
+
</Show>
|
|
713
|
+
</div>
|
|
714
|
+
</Show>
|
|
715
|
+
|
|
716
|
+
<Show when={view() === "article" && activeTopic()}>
|
|
717
|
+
{(topic) => (
|
|
718
|
+
<article
|
|
719
|
+
class="help-article"
|
|
720
|
+
data-surface={props.surface}
|
|
721
|
+
ref={(element) => {
|
|
722
|
+
// Delegated from the article: its body, table of contents, and end hold the links.
|
|
723
|
+
element.addEventListener("click", followHelpLink);
|
|
724
|
+
onCleanup(() => element.removeEventListener("click", followHelpLink));
|
|
725
|
+
}}
|
|
726
|
+
>
|
|
727
|
+
<HelpArticleView
|
|
728
|
+
surface={props.surface}
|
|
729
|
+
topic={topic()}
|
|
730
|
+
appName={appName}
|
|
731
|
+
appId={appId}
|
|
732
|
+
parts={articleParts()}
|
|
733
|
+
document={payload()}
|
|
734
|
+
Markdown={MarkdownView}
|
|
735
|
+
showTools={articleSections().length > 0 || !copyInMenu}
|
|
736
|
+
sectionMenu={
|
|
737
|
+
<Show when={articleSections().length > 0}>
|
|
738
|
+
<Dropdown.Root
|
|
739
|
+
label={t().onThisPage}
|
|
740
|
+
position="bottom-left"
|
|
741
|
+
items={articleSections().map((section) => ({
|
|
742
|
+
label: section.label,
|
|
743
|
+
choice: "radio" as const,
|
|
744
|
+
checked: () => activeSectionId() === section.id,
|
|
745
|
+
action: () => openArticleSection(section),
|
|
746
|
+
}))}
|
|
747
|
+
>
|
|
748
|
+
<Dropdown.Trigger label={t().onThisPage} size="sm" tooltip={false} variant="secondary">
|
|
749
|
+
<i class="ti ti-list" aria-hidden="true" />
|
|
750
|
+
<span>{t().onThisPage}</span>
|
|
751
|
+
<i class="ti ti-chevron-down" aria-hidden="true" />
|
|
752
|
+
</Dropdown.Trigger>
|
|
753
|
+
</Dropdown.Root>
|
|
754
|
+
</Show>
|
|
965
755
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
<i class="ti ti-alert-triangle" aria-hidden="true" />
|
|
978
|
-
<span aria-hidden="true">{t().titlesOnly}</span>
|
|
979
|
-
<span class="sr-only">{t().searchTitlesOnly}</span>
|
|
980
|
-
</span>
|
|
981
|
-
<Button
|
|
982
|
-
size="xs"
|
|
983
|
-
variant="ghost"
|
|
984
|
-
class="help-start-status-retry"
|
|
985
|
-
onClick={() => setSearchAttempt((value) => value + 1)}
|
|
986
|
-
>
|
|
987
|
-
{t().tryAgain}
|
|
988
|
-
</Button>
|
|
989
|
-
</Show>
|
|
990
|
-
</div>
|
|
991
|
-
<Show
|
|
992
|
-
when={results().length > 0}
|
|
993
|
-
fallback={
|
|
994
|
-
<Show when={!searching()}>
|
|
995
|
-
<Placeholder icon="ti ti-search-off" title={t().noResults} />
|
|
996
|
-
</Show>
|
|
997
|
-
}
|
|
998
|
-
>
|
|
999
|
-
<TopicList items={results()} />
|
|
756
|
+
copyButton={
|
|
757
|
+
<Show when={!copyInMenu}>
|
|
758
|
+
<IconButton
|
|
759
|
+
class="help-article-tools-copy"
|
|
760
|
+
disabled={!payload()}
|
|
761
|
+
label={articleClipboard.wasCopied() ? t().articleCopied : t().copyArticle}
|
|
762
|
+
title={articleClipboard.wasCopied() ? t().copied : t().copyArticle}
|
|
763
|
+
onClick={() => void copyArticle(topic(), payload())}
|
|
764
|
+
>
|
|
765
|
+
<i class={articleClipboard.wasCopied() ? "ti ti-check" : "ti ti-markdown"} />
|
|
766
|
+
</IconButton>
|
|
1000
767
|
</Show>
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
<Show when={view() === "article" && activeTopic()}>
|
|
1007
|
-
{(topic) => (
|
|
1008
|
-
<article
|
|
1009
|
-
class="help-article"
|
|
1010
|
-
data-surface={props.surface}
|
|
1011
|
-
ref={(element) => {
|
|
1012
|
-
// Delegated from the article: its body, table of contents, and end hold the links.
|
|
1013
|
-
element.addEventListener("click", followHelpLink);
|
|
1014
|
-
onCleanup(() => element.removeEventListener("click", followHelpLink));
|
|
1015
|
-
}}
|
|
1016
|
-
>
|
|
1017
|
-
<div class="help-article-layout">
|
|
1018
|
-
<div class="help-article-main">
|
|
1019
|
-
<header class="help-article-head" data-help-article-head="">
|
|
1020
|
-
<div class="help-article-head-copy">
|
|
1021
|
-
{/* In the panel, its head already names the app and leads back to the start. */}
|
|
1022
|
-
<Show when={props.surface !== "panel"}>
|
|
1023
|
-
<p class="help-article-path">
|
|
1024
|
-
<Show when={appName}>
|
|
1025
|
-
{(name) => (
|
|
1026
|
-
<>
|
|
1027
|
-
<span class="app-accent-text">{name()}</span>
|
|
1028
|
-
<i class="ti ti-chevron-right" aria-hidden="true" />
|
|
1029
|
-
</>
|
|
1030
|
-
)}
|
|
1031
|
-
</Show>
|
|
1032
|
-
<button type="button" class="help-article-path-link focus-ui" onClick={goBack}>
|
|
1033
|
-
{t().help}
|
|
1034
|
-
</button>
|
|
1035
|
-
</p>
|
|
1036
|
-
</Show>
|
|
1037
|
-
<h3 ref={articleHeading} tabindex="-1" class="help-article-title focus-ui">
|
|
1038
|
-
{topic().title}
|
|
1039
|
-
</h3>
|
|
1040
|
-
<Show
|
|
1041
|
-
when={articleParts()?.lead}
|
|
1042
|
-
fallback={
|
|
1043
|
-
// An article without a leading paragraph, or a built-in topic, shows its description; a loading article shows neither yet.
|
|
1044
|
-
<Show when={articleParts() && topic().description}>
|
|
1045
|
-
{(description) => <p class="help-article-lead">{description()}</p>}
|
|
1046
|
-
</Show>
|
|
1047
|
-
}
|
|
1048
|
-
>
|
|
1049
|
-
{(lead) => <MarkdownView trustedHtml={lead()} class="help-document help-article-lead" />}
|
|
1050
|
-
</Show>
|
|
1051
|
-
<Show when={payload()?.needs || articleParts()?.steps}>
|
|
1052
|
-
{/* One row of facts; the access level appears here and nowhere else in the reader's chrome. */}
|
|
1053
|
-
<ul class="help-article-facts" aria-label={t().articleFacts}>
|
|
1054
|
-
<Show when={payload()?.needs}>
|
|
1055
|
-
{(level) => (
|
|
1056
|
-
<li class="help-article-fact" data-fact="needs">
|
|
1057
|
-
<i class="ti ti-key" aria-hidden="true" />
|
|
1058
|
-
<span>
|
|
1059
|
-
{t().youNeed} <strong>{accessLabel(level())}</strong>
|
|
1060
|
-
</span>
|
|
1061
|
-
</li>
|
|
1062
|
-
)}
|
|
1063
|
-
</Show>
|
|
1064
|
-
<Show when={articleParts()?.steps}>
|
|
1065
|
-
{(count) => (
|
|
1066
|
-
<li class="help-article-fact" data-fact="steps">
|
|
1067
|
-
<i class="ti ti-list-numbers" aria-hidden="true" />
|
|
1068
|
-
<span>{t().steps({ count: count() })}</span>
|
|
1069
|
-
</li>
|
|
1070
|
-
)}
|
|
1071
|
-
</Show>
|
|
1072
|
-
</ul>
|
|
1073
|
-
</Show>
|
|
1074
|
-
<Show when={articleSections().length > 0 || !copyInMenu}>
|
|
1075
|
-
<div class="help-article-tools">
|
|
1076
|
-
<Show when={articleSections().length > 0}>
|
|
1077
|
-
<Dropdown.Root
|
|
1078
|
-
label={t().onThisPage}
|
|
1079
|
-
position="bottom-left"
|
|
1080
|
-
items={articleSections().map((section) => ({
|
|
1081
|
-
label: section.label,
|
|
1082
|
-
choice: "radio" as const,
|
|
1083
|
-
checked: () => activeSectionId() === section.id,
|
|
1084
|
-
action: () => openArticleSection(section),
|
|
1085
|
-
}))}
|
|
1086
|
-
>
|
|
1087
|
-
<Dropdown.Trigger label={t().onThisPage} size="sm" tooltip={false} variant="secondary">
|
|
1088
|
-
<i class="ti ti-list" aria-hidden="true" />
|
|
1089
|
-
<span>{t().onThisPage}</span>
|
|
1090
|
-
<i class="ti ti-chevron-down" aria-hidden="true" />
|
|
1091
|
-
</Dropdown.Trigger>
|
|
1092
|
-
</Dropdown.Root>
|
|
1093
|
-
</Show>
|
|
1094
|
-
<Show when={!copyInMenu}>
|
|
1095
|
-
<IconButton
|
|
1096
|
-
class="help-article-tools-copy"
|
|
1097
|
-
disabled={!payload()}
|
|
1098
|
-
label={articleClipboard.wasCopied() ? t().articleCopied : t().copyArticle}
|
|
1099
|
-
title={articleClipboard.wasCopied() ? t().copied : t().copyArticle}
|
|
1100
|
-
onClick={() => void copyArticle(topic(), payload())}
|
|
1101
|
-
>
|
|
1102
|
-
<i class={articleClipboard.wasCopied() ? "ti ti-check" : "ti ti-markdown"} />
|
|
1103
|
-
</IconButton>
|
|
1104
|
-
</Show>
|
|
1105
|
-
</div>
|
|
1106
|
-
</Show>
|
|
1107
|
-
</div>
|
|
1108
|
-
{/* The embedded readers sit in the app's own chrome and keep the flat head. */}
|
|
1109
|
-
<Show when={props.surface !== "embedded"}>
|
|
1110
|
-
<span class="help-article-icon k2b-icon-tile app-accent-text" aria-hidden="true">
|
|
1111
|
-
<i class={iconClass(topic().icon)} />
|
|
1112
|
-
</span>
|
|
1113
|
-
</Show>
|
|
1114
|
-
</header>
|
|
1115
|
-
<Show when={loading()}>
|
|
1116
|
-
<div class="min-h-[24rem]">
|
|
1117
|
-
<Placeholder state="loading" variant="panel" title={t().loading} />
|
|
1118
|
-
</div>
|
|
1119
|
-
</Show>
|
|
1120
|
-
<Show when={loadError()}>
|
|
1121
|
-
{(message) => (
|
|
1122
|
-
<NoticeCard tone="danger" title={t().loadFailed} detail={message()}>
|
|
1123
|
-
<Button size="sm" variant="secondary" onClick={() => setLoadAttempt((value) => value + 1)}>
|
|
1124
|
-
{t().tryAgain}
|
|
1125
|
-
</Button>
|
|
1126
|
-
</NoticeCard>
|
|
1127
|
-
)}
|
|
1128
|
-
</Show>
|
|
1129
|
-
<div ref={articleContent}>
|
|
1130
|
-
<Show when={articleParts()}>{(parts) => <MarkdownView trustedHtml={parts().body} class="help-document" />}</Show>
|
|
768
|
+
}
|
|
769
|
+
loading={
|
|
770
|
+
<Show when={loading()}>
|
|
771
|
+
<div class="min-h-[24rem]">
|
|
772
|
+
<Placeholder state="loading" variant="panel" title={t().loading} />
|
|
1131
773
|
</div>
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
contentRoot={() => articleContent}
|
|
1141
|
-
onActiveChange={(id) => setActiveSectionId(id ?? null)}
|
|
1142
|
-
/>
|
|
1143
|
-
<Show when={!copyInMenu}>
|
|
1144
|
-
<Button size="sm" variant="ghost" disabled={!payload()} onClick={() => void copyArticle(topic(), payload())}>
|
|
1145
|
-
<i class={articleClipboard.wasCopied() ? "ti ti-check" : "ti ti-markdown"} />
|
|
1146
|
-
{articleClipboard.wasCopied() ? t().copied : t().copyMarkdown}
|
|
774
|
+
</Show>
|
|
775
|
+
}
|
|
776
|
+
failure={
|
|
777
|
+
<Show when={loadError()}>
|
|
778
|
+
{(message) => (
|
|
779
|
+
<NoticeCard tone="danger" title={t().loadFailed} detail={message()}>
|
|
780
|
+
<Button size="sm" variant="secondary" onClick={() => setLoadAttempt((value) => value + 1)}>
|
|
781
|
+
{t().tryAgain}
|
|
1147
782
|
</Button>
|
|
1148
|
-
</
|
|
1149
|
-
|
|
1150
|
-
</
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
783
|
+
</NoticeCard>
|
|
784
|
+
)}
|
|
785
|
+
</Show>
|
|
786
|
+
}
|
|
787
|
+
toc={
|
|
788
|
+
<TableOfContents
|
|
789
|
+
items={articleSections()}
|
|
790
|
+
title={t().onThisPage}
|
|
791
|
+
scrollRoot={tocScrollRoot}
|
|
792
|
+
contentRoot={() => articleContent}
|
|
793
|
+
onActiveChange={(id) => setActiveSectionId(id ?? null)}
|
|
794
|
+
/>
|
|
795
|
+
}
|
|
796
|
+
copyAction={
|
|
797
|
+
<Show when={!copyInMenu}>
|
|
798
|
+
<Button size="sm" variant="ghost" disabled={!payload()} onClick={() => void copyArticle(topic(), payload())}>
|
|
799
|
+
<i class={articleClipboard.wasCopied() ? "ti ti-check" : "ti ti-markdown"} />
|
|
800
|
+
{articleClipboard.wasCopied() ? t().copied : t().copyMarkdown}
|
|
801
|
+
</Button>
|
|
802
|
+
</Show>
|
|
803
|
+
}
|
|
804
|
+
headingRef={(element) => {
|
|
805
|
+
articleHeading = element;
|
|
806
|
+
}}
|
|
807
|
+
contentRef={(element) => {
|
|
808
|
+
articleContent = element;
|
|
809
|
+
}}
|
|
810
|
+
onBack={goBack}
|
|
811
|
+
/>
|
|
812
|
+
</article>
|
|
813
|
+
)}
|
|
1155
814
|
</Show>
|
|
1156
815
|
<span class="sr-only" aria-live="polite">
|
|
1157
816
|
{articleClipboard.wasCopied() ? t().articleCopiedAnnouncement : ""}
|
|
@@ -1201,22 +860,6 @@ export function LayoutHelpPage(props: LayoutHelpPageProps) {
|
|
|
1201
860
|
);
|
|
1202
861
|
}
|
|
1203
862
|
|
|
1204
|
-
/** Help inside a provider document; the server snapshot also owns the first browser render. */
|
|
1205
|
-
export function LayoutHelpPanel(props: LayoutHelpPanelProps) {
|
|
1206
|
-
const session = createSession(props.initialTopic);
|
|
1207
|
-
return (
|
|
1208
|
-
<HelpShell
|
|
1209
|
-
session={session}
|
|
1210
|
-
initialArticle={props.initialTopic ? props.initialArticle : undefined}
|
|
1211
|
-
close={() => {}}
|
|
1212
|
-
documents={props.documents}
|
|
1213
|
-
pageBase={props.pageBase}
|
|
1214
|
-
surface="panel"
|
|
1215
|
-
panel={props}
|
|
1216
|
-
/>
|
|
1217
|
-
);
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
863
|
/** The dialog renders outside the app canvas; it takes the app's canvas colours along for the article head. */
|
|
1221
864
|
const canvasAppearance = () => {
|
|
1222
865
|
const canvas = document.querySelector(".cloud-app-canvas");
|