@happyvertical/smrt-svelte 0.55.9 → 0.55.11

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.
Files changed (119) hide show
  1. package/AGENTS.md +2 -1
  2. package/agents/cookbook-shell.md +176 -0
  3. package/agents/overview-presentation.md +8 -0
  4. package/agents/overview-surfaces.md +133 -17
  5. package/agents/screens.md +5 -0
  6. package/agents/workspace.md +6 -0
  7. package/dist/components/activity/ActivityList.svelte +35 -0
  8. package/dist/components/activity/ActivityList.svelte.d.ts +11 -0
  9. package/dist/components/activity/ActivityList.svelte.d.ts.map +1 -0
  10. package/dist/components/activity/index.d.ts +3 -0
  11. package/dist/components/activity/index.d.ts.map +1 -0
  12. package/dist/components/activity/index.js +1 -0
  13. package/dist/components/activity/safe-href.d.ts +3 -0
  14. package/dist/components/activity/safe-href.d.ts.map +1 -0
  15. package/dist/components/activity/safe-href.js +12 -0
  16. package/dist/components/activity/types.d.ts +9 -0
  17. package/dist/components/activity/types.d.ts.map +1 -0
  18. package/dist/components/activity/types.js +1 -0
  19. package/dist/components/cookbook/CookbookApp.svelte +236 -0
  20. package/dist/components/cookbook/CookbookApp.svelte.d.ts +5 -0
  21. package/dist/components/cookbook/CookbookApp.svelte.d.ts.map +1 -0
  22. package/dist/components/cookbook/CookbookSectionOverview.svelte +71 -0
  23. package/dist/components/cookbook/CookbookSectionOverview.svelte.d.ts +20 -0
  24. package/dist/components/cookbook/CookbookSectionOverview.svelte.d.ts.map +1 -0
  25. package/dist/components/cookbook/CookbookSectionPage.svelte +133 -0
  26. package/dist/components/cookbook/CookbookSectionPage.svelte.d.ts +5 -0
  27. package/dist/components/cookbook/CookbookSectionPage.svelte.d.ts.map +1 -0
  28. package/dist/components/cookbook/brand-theme.d.ts +11 -0
  29. package/dist/components/cookbook/brand-theme.d.ts.map +1 -0
  30. package/dist/components/cookbook/brand-theme.js +24 -0
  31. package/dist/components/cookbook/build.d.ts +8 -0
  32. package/dist/components/cookbook/build.d.ts.map +1 -0
  33. package/dist/components/cookbook/build.js +277 -0
  34. package/dist/components/cookbook/catalog.d.ts +37 -0
  35. package/dist/components/cookbook/catalog.d.ts.map +1 -0
  36. package/dist/components/cookbook/catalog.js +99 -0
  37. package/dist/components/cookbook/context.d.ts +23 -0
  38. package/dist/components/cookbook/context.d.ts.map +1 -0
  39. package/dist/components/cookbook/context.js +15 -0
  40. package/dist/components/cookbook/index.d.ts +22 -0
  41. package/dist/components/cookbook/index.d.ts.map +1 -0
  42. package/dist/components/cookbook/index.js +20 -0
  43. package/dist/components/cookbook/page.d.ts +23 -0
  44. package/dist/components/cookbook/page.d.ts.map +1 -0
  45. package/dist/components/cookbook/page.js +32 -0
  46. package/dist/components/cookbook/registry.d.ts +37 -0
  47. package/dist/components/cookbook/registry.d.ts.map +1 -0
  48. package/dist/components/cookbook/registry.js +38 -0
  49. package/dist/components/cookbook/sections.d.ts +60 -0
  50. package/dist/components/cookbook/sections.d.ts.map +1 -0
  51. package/dist/components/cookbook/sections.js +88 -0
  52. package/dist/components/cookbook/server.d.ts +61 -0
  53. package/dist/components/cookbook/server.d.ts.map +1 -0
  54. package/dist/components/cookbook/server.js +108 -0
  55. package/dist/components/cookbook/theme.d.ts +21 -0
  56. package/dist/components/cookbook/theme.d.ts.map +1 -0
  57. package/dist/components/cookbook/theme.js +53 -0
  58. package/dist/components/cookbook/types.d.ts +204 -0
  59. package/dist/components/cookbook/types.d.ts.map +1 -0
  60. package/dist/components/cookbook/types.js +1 -0
  61. package/dist/components/notifications/LocalizedNotifications.fixture.svelte +16 -0
  62. package/dist/components/notifications/LocalizedNotifications.fixture.svelte.d.ts +11 -0
  63. package/dist/components/notifications/LocalizedNotifications.fixture.svelte.d.ts.map +1 -0
  64. package/dist/components/notifications/NotificationBell.svelte +136 -0
  65. package/dist/components/notifications/NotificationBell.svelte.d.ts +13 -0
  66. package/dist/components/notifications/NotificationBell.svelte.d.ts.map +1 -0
  67. package/dist/components/notifications/index.d.ts +3 -0
  68. package/dist/components/notifications/index.d.ts.map +1 -0
  69. package/dist/components/notifications/index.js +1 -0
  70. package/dist/components/notifications/types.d.ts +27 -0
  71. package/dist/components/notifications/types.d.ts.map +1 -0
  72. package/dist/components/notifications/types.js +1 -0
  73. package/dist/components/overview/OverviewAssistantUndo.svelte +77 -0
  74. package/dist/components/overview/OverviewAssistantUndo.svelte.d.ts +13 -0
  75. package/dist/components/overview/OverviewAssistantUndo.svelte.d.ts.map +1 -0
  76. package/dist/components/overview/OverviewGrid.svelte +5 -2
  77. package/dist/components/overview/OverviewGrid.svelte.d.ts.map +1 -1
  78. package/dist/components/overview/assistant-surface.d.ts +90 -0
  79. package/dist/components/overview/assistant-surface.d.ts.map +1 -0
  80. package/dist/components/overview/assistant-surface.js +65 -0
  81. package/dist/components/overview/assistant.svelte.d.ts +115 -0
  82. package/dist/components/overview/assistant.svelte.d.ts.map +1 -0
  83. package/dist/components/overview/assistant.svelte.js +260 -0
  84. package/dist/components/overview/controller.svelte.d.ts +8 -0
  85. package/dist/components/overview/controller.svelte.d.ts.map +1 -1
  86. package/dist/components/overview/controller.svelte.js +76 -13
  87. package/dist/components/overview/grid-types.d.ts +6 -2
  88. package/dist/components/overview/grid-types.d.ts.map +1 -1
  89. package/dist/components/overview/index.d.ts +2 -0
  90. package/dist/components/overview/index.d.ts.map +1 -1
  91. package/dist/components/overview/index.js +2 -0
  92. package/dist/components/overview/operations.d.ts +169 -0
  93. package/dist/components/overview/operations.d.ts.map +1 -0
  94. package/dist/components/overview/operations.js +358 -0
  95. package/dist/components/overview/server.d.ts +2 -0
  96. package/dist/components/overview/server.d.ts.map +1 -1
  97. package/dist/components/overview/server.js +2 -0
  98. package/dist/components/workspace/admin-shell/settings-delta.d.ts +21 -0
  99. package/dist/components/workspace/admin-shell/settings-delta.d.ts.map +1 -0
  100. package/dist/components/workspace/admin-shell/settings-delta.js +142 -0
  101. package/dist/components/workspace/index.d.ts +1 -0
  102. package/dist/components/workspace/index.d.ts.map +1 -1
  103. package/dist/components/workspace/index.js +1 -0
  104. package/dist/components/workspace/server/index.d.ts +6 -2
  105. package/dist/components/workspace/server/index.d.ts.map +1 -1
  106. package/dist/components/workspace/server/index.js +5 -2
  107. package/dist/i18n/server.d.ts +1 -0
  108. package/dist/i18n/server.d.ts.map +1 -1
  109. package/dist/i18n/server.js +1 -0
  110. package/dist/i18n/strings.notifications.d.ts +12 -0
  111. package/dist/i18n/strings.notifications.d.ts.map +1 -0
  112. package/dist/i18n/strings.notifications.js +13 -0
  113. package/dist/i18n/strings.overview.d.ts +9 -0
  114. package/dist/i18n/strings.overview.d.ts.map +1 -1
  115. package/dist/i18n/strings.overview.js +11 -0
  116. package/dist/index.d.ts +2 -0
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +2 -0
  119. package/package.json +31 -14
@@ -0,0 +1,236 @@
1
+ <script lang="ts">
2
+ /**
3
+ * `CookbookApp` (#3749): `AppShell` driven by a cookbook. A generated app
4
+ * needs a layout that renders this with `smrt.cookbook.json` (validated) and
5
+ * the catalog the server helper loads, plus one route that renders
6
+ * `CookbookSectionPage`.
7
+ *
8
+ * It builds the navigation, layout, theme and section overviews with
9
+ * `buildCookbookShell`, keeps layout and overview edits in memory, and hands
10
+ * each edit to `oncookbookchange` for the host to persist.
11
+ */
12
+ import { createRawSnippet, mount, unmount, untrack } from 'svelte';
13
+ import AppShell from '../app/AppShell.svelte';
14
+ import type { ShellSlotItem } from '../app/slot-item.js';
15
+ import { parseRecipeExportRef } from '../overview/recipe-widgets.js';
16
+ import {
17
+ resolveWidgetComponents,
18
+ type WidgetComponents,
19
+ type WidgetRegistry,
20
+ } from '../overview/registry.js';
21
+ import type { OverviewOverride } from '../overview/types.js';
22
+ import {
23
+ isShellLayoutEmpty,
24
+ type ShellLayout,
25
+ } from '../workspace/admin-shell/layout.js';
26
+ import { applyCookbookTheme } from './brand-theme.js';
27
+ import { buildCookbookShell, humanize, pluralize } from './build.js';
28
+ import { type CookbookContext, setCookbookContext } from './context.js';
29
+ import { createCookbookRegistry } from './registry.js';
30
+ import type { CookbookAppProps } from './types.js';
31
+
32
+ let {
33
+ cookbook,
34
+ catalog,
35
+ loaders,
36
+ widgetContext,
37
+ resolveExport,
38
+ oncookbookchange,
39
+ recipeOrder,
40
+ entryPath,
41
+ sectionPath,
42
+ href,
43
+ title,
44
+ slotItems = [],
45
+ children,
46
+ ...shellProps
47
+ }: CookbookAppProps = $props();
48
+
49
+ const shell = $derived(
50
+ buildCookbookShell({
51
+ cookbook,
52
+ catalog,
53
+ ...(title ? { title } : {}),
54
+ ...(recipeOrder ? { recipeOrder } : {}),
55
+ ...(entryPath ? { entryPath } : {}),
56
+ ...(sectionPath ? { sectionPath } : {}),
57
+ ...(href ? { href } : {}),
58
+ dataWidgets: Boolean(loaders?.metric && loaders?.records),
59
+ }),
60
+ );
61
+
62
+ // Local copies the user edits; they follow the cookbook whenever it changes.
63
+ let layout = $state.raw<ShellLayout | null>(untrack(() => shell.layout));
64
+ let overrides = $state.raw<Record<string, unknown>>(
65
+ untrack(() => storedOverrides()),
66
+ );
67
+
68
+ function storedOverrides(): Record<string, unknown> {
69
+ return Object.fromEntries(
70
+ Object.entries(shell.overviews)
71
+ .filter(([, page]) => page.override != null)
72
+ .map(([id, page]) => [id, page.override]),
73
+ );
74
+ }
75
+
76
+ $effect(() => {
77
+ layout = shell.layout;
78
+ overrides = storedOverrides();
79
+ });
80
+
81
+ function emit() {
82
+ if (!oncookbookchange) return;
83
+ const next = { ...cookbook };
84
+ if (isShellLayoutEmpty(layout)) delete next.layout;
85
+ else next.layout = layout as ShellLayout;
86
+ if (Object.keys(overrides).length > 0) {
87
+ next.overviews = overrides as NonNullable<typeof next.overviews>;
88
+ } else delete next.overviews;
89
+ oncookbookchange(next);
90
+ }
91
+
92
+ const preset = $derived(applyCookbookTheme(shell.theme));
93
+
94
+ // Widget registry: core widgets plus the recipes' `widget` surfaces.
95
+ let registry = $state.raw<WidgetRegistry | null>(null);
96
+ let components = $state.raw<WidgetComponents>(new Map());
97
+
98
+ // Keyed by content: an edit rebuilds `shell` but must not rebuild the registry.
99
+ const widgetsKey = $derived(JSON.stringify(shell.widgets));
100
+ const shellWidgetsKey = $derived(JSON.stringify(shell.shellWidgets));
101
+
102
+ $effect(() => {
103
+ void widgetsKey;
104
+ const widgets = untrack(() => shell.widgets);
105
+ const resolve = resolveExport;
106
+ const dataLoaders = loaders;
107
+ let cancelled = false;
108
+ void (async () => {
109
+ const created = await createCookbookRegistry({
110
+ ...(dataLoaders ? { loaders: dataLoaders } : {}),
111
+ widgets,
112
+ ...(resolve ? { resolveExport: resolve } : {}),
113
+ });
114
+ const resolved = await resolveWidgetComponents(
115
+ created.registry.list().map((def) => def.type),
116
+ created.registry,
117
+ );
118
+ if (cancelled) return;
119
+ components = resolved;
120
+ registry = created.registry;
121
+ })();
122
+ return () => {
123
+ cancelled = true;
124
+ };
125
+ });
126
+
127
+ // Recipe `shell-widget` surfaces become movable slot items.
128
+ let recipeSlotItems = $state.raw<ShellSlotItem[]>([]);
129
+
130
+ $effect(() => {
131
+ void shellWidgetsKey;
132
+ const widgets = untrack(() => shell.shellWidgets);
133
+ const resolve = resolveExport;
134
+ if (!resolve || widgets.length === 0) {
135
+ recipeSlotItems = [];
136
+ return;
137
+ }
138
+ let cancelled = false;
139
+ void (async () => {
140
+ const items: ShellSlotItem[] = [];
141
+ for (const widget of widgets) {
142
+ const ref = parseRecipeExportRef(widget.export);
143
+ if (!ref) continue;
144
+ try {
145
+ const loaded = (await resolve(ref.specifier, ref.exportName)) as
146
+ | { default?: unknown }
147
+ | ((...args: never[]) => unknown)
148
+ | undefined;
149
+ const component =
150
+ loaded && typeof loaded === 'object' && 'default' in loaded
151
+ ? loaded.default
152
+ : loaded;
153
+ if (typeof component !== 'function') continue;
154
+ items.push({
155
+ id: `recipe:${widget.recipeId}:${ref.exportName}`,
156
+ label: widget.label,
157
+ slot: widget.slot,
158
+ render: createRawSnippet(() => ({
159
+ render: () => '<span data-cookbook-slot-widget></span>',
160
+ setup: (node: Element) => {
161
+ const instance = mount(component as never, {
162
+ target: node as HTMLElement,
163
+ });
164
+ return () => {
165
+ void unmount(instance);
166
+ };
167
+ },
168
+ })),
169
+ });
170
+ } catch {
171
+ // A widget that cannot load is left out; the shell still renders.
172
+ }
173
+ }
174
+ if (!cancelled) recipeSlotItems = items;
175
+ })();
176
+ return () => {
177
+ cancelled = true;
178
+ };
179
+ });
180
+
181
+ const models = $derived.by(() => {
182
+ const seen = new Set<string>();
183
+ const choices: { value: string; label: string }[] = [];
184
+ for (const entry of shell.entries.values()) {
185
+ if (seen.has(entry.model.id)) continue;
186
+ seen.add(entry.model.id);
187
+ choices.push({
188
+ value: entry.model.id,
189
+ label: pluralize(humanize(entry.model.name)),
190
+ });
191
+ }
192
+ return choices;
193
+ });
194
+
195
+ setCookbookContext({
196
+ get shell() {
197
+ return shell;
198
+ },
199
+ get registry() {
200
+ return registry;
201
+ },
202
+ get components() {
203
+ return components;
204
+ },
205
+ get models() {
206
+ return models;
207
+ },
208
+ overrideFor: (id) => overrides[id] ?? null,
209
+ setOverride: (id, override: OverviewOverride | null) => {
210
+ const next = { ...overrides };
211
+ if (override === null) delete next[id];
212
+ else next[id] = override;
213
+ overrides = next;
214
+ emit();
215
+ },
216
+ widgetContext: () => widgetContext?.() ?? {},
217
+ } satisfies CookbookContext);
218
+ </script>
219
+
220
+ <AppShell
221
+ {...shellProps}
222
+ title={shell.title}
223
+ navMode="sections"
224
+ navGroups={shell.navGroups}
225
+ {layout}
226
+ onlayoutchange={(next) => {
227
+ layout = next;
228
+ emit();
229
+ }}
230
+ {preset}
231
+ colorScheme={shell.theme.colorScheme}
232
+ sectionHref={(id) => shell.sectionPath(id)}
233
+ slotItems={[...slotItems, ...recipeSlotItems]}
234
+ >
235
+ {@render children()}
236
+ </AppShell>
@@ -0,0 +1,5 @@
1
+ import type { CookbookAppProps } from './types.js';
2
+ declare const CookbookApp: import("svelte").Component<CookbookAppProps, {}, "">;
3
+ type CookbookApp = ReturnType<typeof CookbookApp>;
4
+ export default CookbookApp;
5
+ //# sourceMappingURL=CookbookApp.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CookbookApp.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/cookbook/CookbookApp.svelte.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAsNnD,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,71 @@
1
+ <script lang="ts">
2
+ /**
3
+ * A section page's editable overview (#3749): `OverviewGrid` over the
4
+ * section's definition. The cookbook owns the override (the controller reads
5
+ * it and writes each edit back). Mount it under `{#key}` on the section id.
6
+ */
7
+ import { onDestroy, untrack } from 'svelte';
8
+ import type { OverviewModelChoice } from '../overview/grid-types.js';
9
+ import OverviewGrid from '../overview/OverviewGrid.svelte';
10
+ import type { WidgetComponents, WidgetRegistry } from '../overview/registry.js';
11
+ import type { OverviewDefinition } from '../overview/types.js';
12
+ import { useShellLayout } from '../workspace/admin-shell/layout-context.js';
13
+ import { type CookbookOverviewStore, createSectionOverview } from './page.js';
14
+ import type { CookbookEntry } from './types.js';
15
+
16
+ interface Props {
17
+ definition: OverviewDefinition;
18
+ registry: WidgetRegistry;
19
+ store: CookbookOverviewStore;
20
+ entries: ReadonlyMap<string, CookbookEntry>;
21
+ components: WidgetComponents;
22
+ models: readonly OverviewModelChoice[];
23
+ /** Host capabilities for widget loaders. */
24
+ hostContext: () => Record<string, unknown>;
25
+ label: string;
26
+ }
27
+
28
+ let {
29
+ definition,
30
+ registry,
31
+ store,
32
+ entries,
33
+ components,
34
+ models,
35
+ hostContext,
36
+ label,
37
+ }: Props = $props();
38
+
39
+ const layout = useShellLayout();
40
+
41
+ const controller = createSectionOverview(
42
+ untrack(() => definition),
43
+ untrack(() => registry),
44
+ untrack(() => store),
45
+ () => ({
46
+ ...hostContext(),
47
+ sections: layout.sections,
48
+ entries,
49
+ }),
50
+ );
51
+
52
+ // The shortcuts follow the menu (renames, hides, moves); the grid already
53
+ // requested the first load, so skip that run.
54
+ let first = true;
55
+ $effect(() => {
56
+ void JSON.stringify(layout.sections);
57
+ if (first) {
58
+ first = false;
59
+ return;
60
+ }
61
+ untrack(() => {
62
+ for (const widget of controller.document.widgets) {
63
+ controller.reload(widget.id);
64
+ }
65
+ });
66
+ });
67
+
68
+ onDestroy(() => controller.destroy());
69
+ </script>
70
+
71
+ <OverviewGrid {controller} {components} {models} {label} />
@@ -0,0 +1,20 @@
1
+ import type { OverviewModelChoice } from '../overview/grid-types.js';
2
+ import type { WidgetComponents, WidgetRegistry } from '../overview/registry.js';
3
+ import type { OverviewDefinition } from '../overview/types.js';
4
+ import { type CookbookOverviewStore } from './page.js';
5
+ import type { CookbookEntry } from './types.js';
6
+ interface Props {
7
+ definition: OverviewDefinition;
8
+ registry: WidgetRegistry;
9
+ store: CookbookOverviewStore;
10
+ entries: ReadonlyMap<string, CookbookEntry>;
11
+ components: WidgetComponents;
12
+ models: readonly OverviewModelChoice[];
13
+ /** Host capabilities for widget loaders. */
14
+ hostContext: () => Record<string, unknown>;
15
+ label: string;
16
+ }
17
+ declare const CookbookSectionOverview: import("svelte").Component<Props, {}, "">;
18
+ type CookbookSectionOverview = ReturnType<typeof CookbookSectionOverview>;
19
+ export default CookbookSectionOverview;
20
+ //# sourceMappingURL=CookbookSectionOverview.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CookbookSectionOverview.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/cookbook/CookbookSectionOverview.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,KAAK,qBAAqB,EAAyB,MAAM,WAAW,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,UAAU,KAAK;IACb,UAAU,EAAE,kBAAkB,CAAC;IAC/B,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,qBAAqB,CAAC;IAC7B,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC5C,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACvC,4CAA4C;IAC5C,WAAW,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AA4DD,QAAA,MAAM,uBAAuB,2CAAwC,CAAC;AACtE,KAAK,uBAAuB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC1E,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,133 @@
1
+ <script lang="ts">
2
+ /**
3
+ * A navigation section's own page in a cookbook app (#3749). The sidebar lists
4
+ * only sections, so this is where a section's entries are: an editable
5
+ * overview (the entries as shortcut cards by default) with the shell's layout
6
+ * edit mode driving its grid. A section with no overview shows its menu.
7
+ *
8
+ * Render it from one route (`s/[slug]/+page.svelte`), inside `CookbookApp`.
9
+ */
10
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
11
+ import { M } from '../../i18n/strings.overview.js';
12
+ import type { OverviewOverride } from '../overview/types.js';
13
+ import { useShellLayout } from '../workspace/admin-shell/layout-context.js';
14
+ import ShellSectionIcon from '../workspace/admin-shell/ShellSectionIcon.svelte';
15
+ import ShellSectionMenu from '../workspace/admin-shell/ShellSectionMenu.svelte';
16
+ import CookbookSectionOverview from './CookbookSectionOverview.svelte';
17
+ import { getCookbookContext } from './context.js';
18
+ import { DEFAULT_SECTION_ICON, sectionIdFromSlug } from './sections.js';
19
+ import type { CookbookSectionPageProps } from './types.js';
20
+
21
+ let { sectionId, slug, children }: CookbookSectionPageProps = $props();
22
+
23
+ const { t } = useI18n();
24
+ const cookbook = getCookbookContext();
25
+ const layout = useShellLayout();
26
+
27
+ const id = $derived(sectionId ?? (slug ? sectionIdFromSlug(slug) : ''));
28
+ const section = $derived(layout.sections.find((s) => s.id === id));
29
+ const suggested = $derived(cookbook.shell.sections.find((s) => s.id === id));
30
+ const title = $derived(section?.heading?.trim() || suggested?.label || '');
31
+ const icon = $derived(section?.icon ?? suggested?.icon ?? DEFAULT_SECTION_ICON);
32
+ const description = $derived(suggested?.description);
33
+ const page = $derived(cookbook.shell.overviews[id]);
34
+ const registry = $derived(cookbook.registry);
35
+ const store = {
36
+ overview: (key: string) => cookbook.overrideFor(key),
37
+ setOverview: (key: string, override: OverviewOverride | null) =>
38
+ cookbook.setOverride(key, override),
39
+ };
40
+ </script>
41
+
42
+ <svelte:head>
43
+ {#if title}<title>{title}</title>{/if}
44
+ </svelte:head>
45
+
46
+ <main class="cookbook-section">
47
+ {#if section}
48
+ <header>
49
+ <span class="mark"><ShellSectionIcon name={icon} size={28} /></span>
50
+ <div class="heading">
51
+ <h1>{title}</h1>
52
+ {#if description}<p>{description}</p>{/if}
53
+ </div>
54
+ </header>
55
+ {#if page && registry}
56
+ {#key id}
57
+ <CookbookSectionOverview
58
+ definition={page.definition}
59
+ {registry}
60
+ {store}
61
+ entries={cookbook.shell.entries}
62
+ components={cookbook.components}
63
+ models={cookbook.models}
64
+ hostContext={cookbook.widgetContext}
65
+ label={`${title} overview`}
66
+ />
67
+ {/key}
68
+ {#if layout.editing}
69
+ <section class="entries" aria-labelledby="cookbook-section-entries">
70
+ <h2 id="cookbook-section-entries">{t(M['ui.cookbook.menu_entries'])}</h2>
71
+ <ShellSectionMenu sectionId={id} />
72
+ </section>
73
+ {/if}
74
+ {:else if !page}
75
+ <ShellSectionMenu sectionId={id} layout="cards" />
76
+ {/if}
77
+ {@render children?.()}
78
+ {:else}
79
+ <h1>{t(M['ui.cookbook.section_not_found'])}</h1>
80
+ <p>{t(M['ui.cookbook.section_not_found_hint'])}</p>
81
+ {/if}
82
+ </main>
83
+
84
+ <style>
85
+ .cookbook-section {
86
+ display: grid;
87
+ gap: var(--smrt-spacing-6);
88
+ width: min(100%, 64rem);
89
+ margin-inline: auto;
90
+ padding: var(--smrt-spacing-6);
91
+ }
92
+
93
+ header {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: var(--smrt-spacing-4);
97
+ }
98
+
99
+ .mark {
100
+ display: inline-grid;
101
+ place-items: center;
102
+ flex: 0 0 auto;
103
+ inline-size: 3rem;
104
+ block-size: 3rem;
105
+ border-radius: var(--smrt-radius-large, 0.75rem);
106
+ background: var(--smrt-color-primary-container);
107
+ color: var(--smrt-color-on-primary-container);
108
+ }
109
+
110
+ .heading {
111
+ flex: 1 1 auto;
112
+ min-inline-size: 0;
113
+ }
114
+
115
+ h1 {
116
+ margin: 0;
117
+ }
118
+
119
+ .heading p {
120
+ margin: var(--smrt-spacing-1) 0 0;
121
+ color: var(--smrt-color-on-surface-variant);
122
+ }
123
+
124
+ .entries {
125
+ display: grid;
126
+ gap: var(--smrt-spacing-3);
127
+ }
128
+
129
+ .entries h2 {
130
+ margin: 0;
131
+ font-size: var(--smrt-typography-title-medium-size, 1rem);
132
+ }
133
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { CookbookSectionPageProps } from './types.js';
2
+ declare const CookbookSectionPage: import("svelte").Component<CookbookSectionPageProps, {}, "">;
3
+ type CookbookSectionPage = ReturnType<typeof CookbookSectionPage>;
4
+ export default CookbookSectionPage;
5
+ //# sourceMappingURL=CookbookSectionPage.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CookbookSectionPage.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/cookbook/CookbookSectionPage.svelte.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAyE3D,QAAA,MAAM,mBAAmB,8DAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type ThemePreset } from '@happyvertical/smrt-ui/themes';
2
+ import type { ResolvedCookbookTheme } from './types.js';
3
+ /**
4
+ * The preset id `ThemeProvider` should use for a resolved cookbook theme.
5
+ * A brand colour is registered through `createThemeFromColor` first; a
6
+ * registered id is never reused for another colour because `ThemeProvider`
7
+ * looks a theme up once per preset id (the id encodes colour and font).
8
+ * An unknown preset name falls back to the default rather than breaking.
9
+ */
10
+ export declare function applyCookbookTheme(theme: ResolvedCookbookTheme): ThemePreset;
11
+ //# sourceMappingURL=brand-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-theme.d.ts","sourceRoot":"","sources":["../../../src/components/cookbook/brand-theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIxD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,GAAG,WAAW,CAiB5E"}
@@ -0,0 +1,24 @@
1
+ import { createThemeFromColor, isValidPreset, registerTheme, } from '@happyvertical/smrt-ui/themes';
2
+ import { DEFAULT_THEME_PRESET, THEME_FONTS } from './theme.js';
3
+ const registered = new Set();
4
+ /**
5
+ * The preset id `ThemeProvider` should use for a resolved cookbook theme.
6
+ * A brand colour is registered through `createThemeFromColor` first; a
7
+ * registered id is never reused for another colour because `ThemeProvider`
8
+ * looks a theme up once per preset id (the id encodes colour and font).
9
+ * An unknown preset name falls back to the default rather than breaking.
10
+ */
11
+ export function applyCookbookTheme(theme) {
12
+ const { brand } = theme;
13
+ if (brand) {
14
+ if (!registered.has(theme.preset)) {
15
+ const font = brand.fontFamily ? THEME_FONTS[brand.fontFamily] : undefined;
16
+ registerTheme(createThemeFromColor(brand.primary, theme.preset, 'Brand colour', {
17
+ ...(font ? { fontFamily: font } : {}),
18
+ }));
19
+ registered.add(theme.preset);
20
+ }
21
+ return theme.preset;
22
+ }
23
+ return (isValidPreset(theme.preset) ? theme.preset : DEFAULT_THEME_PRESET);
24
+ }
@@ -0,0 +1,8 @@
1
+ import type { BuildCookbookShellOptions, CookbookShell } from './types.js';
2
+ /** `PurchaseOrder` -> `Purchase Order`. */
3
+ export declare function humanize(name: string): string;
4
+ /** `Customer` -> `Customers`, `Category` -> `Categories`. */
5
+ export declare function pluralize(noun: string): string;
6
+ /** Derive the shell configuration from a cookbook (see {@link CookbookShell}). */
7
+ export declare function buildCookbookShell(options: BuildCookbookShellOptions): CookbookShell;
8
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/components/cookbook/build.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,yBAAyB,EAQzB,aAAa,EAEd,MAAM,YAAY,CAAC;AAEpB,2CAA2C;AAC3C,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9C;AA0ED,kFAAkF;AAClF,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,yBAAyB,GACjC,aAAa,CAyOf"}