@happyvertical/smrt-svelte 0.51.39 → 0.53.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.
Files changed (143) hide show
  1. package/AGENTS.md +3 -0
  2. package/README.md +214 -1
  3. package/agents/components.md +8 -1
  4. package/agents/workspace.md +24 -0
  5. package/dist/Provider.svelte +19 -15
  6. package/dist/Provider.svelte.d.ts +2 -2
  7. package/dist/Provider.svelte.d.ts.map +1 -1
  8. package/dist/__tests__/provider-mode-harness.svelte +14 -0
  9. package/dist/__tests__/provider-mode-harness.svelte.d.ts +11 -0
  10. package/dist/__tests__/provider-mode-harness.svelte.d.ts.map +1 -0
  11. package/dist/__tests__/provider-mode-probe.svelte +13 -0
  12. package/dist/__tests__/provider-mode-probe.svelte.d.ts +8 -0
  13. package/dist/__tests__/provider-mode-probe.svelte.d.ts.map +1 -0
  14. package/dist/browser-ai/adapters/stt/browser-speech.d.ts +11 -0
  15. package/dist/browser-ai/adapters/stt/browser-speech.d.ts.map +1 -1
  16. package/dist/browser-ai/adapters/stt/browser-speech.js +20 -4
  17. package/dist/browser-ai/adapters/stt/browser-speech.test.js +113 -0
  18. package/dist/browser-ai/adapters/stt/dictation-source.d.ts +3 -0
  19. package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -0
  20. package/dist/browser-ai/adapters/stt/dictation-source.js +38 -0
  21. package/dist/browser-ai/adapters/stt/dictation-source.test.js +52 -0
  22. package/dist/browser-ai/adapters/stt/index.d.ts +2 -1
  23. package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
  24. package/dist/browser-ai/adapters/stt/index.js +2 -1
  25. package/dist/components/forms/FileUpload.svelte +120 -16
  26. package/dist/components/forms/FileUpload.svelte.d.ts +7 -1
  27. package/dist/components/forms/FileUpload.svelte.d.ts.map +1 -1
  28. package/dist/components/forms/Form.svelte +118 -8
  29. package/dist/components/forms/Form.svelte.d.ts +18 -2
  30. package/dist/components/forms/Form.svelte.d.ts.map +1 -1
  31. package/dist/components/forms/FormScope.svelte +202 -0
  32. package/dist/components/forms/FormScope.svelte.d.ts +22 -0
  33. package/dist/components/forms/FormScope.svelte.d.ts.map +1 -0
  34. package/dist/components/forms/__tests__/FileUpload.native-form.test.js +249 -0
  35. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte +24 -0
  36. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts +10 -0
  37. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts.map +1 -0
  38. package/dist/components/forms/__tests__/Form.ui-controls.test.js +119 -0
  39. package/dist/components/forms/__tests__/Form.webmcp.test.js +5 -3
  40. package/dist/components/forms/__tests__/FormScope.test.js +39 -0
  41. package/dist/components/forms/__tests__/form-scope.fixture.svelte +30 -0
  42. package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts +10 -0
  43. package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts.map +1 -0
  44. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte +84 -0
  45. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts +14 -0
  46. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts.map +1 -0
  47. package/dist/components/forms/index.d.ts +1 -0
  48. package/dist/components/forms/index.d.ts.map +1 -1
  49. package/dist/components/forms/index.js +1 -0
  50. package/dist/components/workspace/README.md +11 -0
  51. package/dist/components/workspace/__tests__/AdminShell.responsive.test.js +597 -0
  52. package/dist/components/workspace/__tests__/AdminShell.test.js +257 -1
  53. package/dist/components/workspace/__tests__/TenantNav.test.js +115 -0
  54. package/dist/components/workspace/__tests__/WorkspaceAccountMenu.test.js +6 -0
  55. package/dist/components/workspace/__tests__/admin-shell-header-overflow.test.js +25 -0
  56. package/dist/components/workspace/__tests__/admin-shell-mobile.test.js +217 -0
  57. package/dist/components/workspace/__tests__/admin-shell-page-contracts.test.js +30 -0
  58. package/dist/components/workspace/__tests__/admin-shell-responsive-state.test.js +184 -0
  59. package/dist/components/workspace/__tests__/admin-shell-state.test.js +25 -0
  60. package/dist/components/workspace/__tests__/admin-shell-theme-hooks.test.js +36 -0
  61. package/dist/components/workspace/__tests__/shell-chrome-components.test.js +108 -0
  62. package/dist/components/workspace/admin-shell/AdminShell.svelte +1099 -59
  63. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +44 -1
  64. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  65. package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte +170 -0
  66. package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte.d.ts +11 -0
  67. package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte.d.ts.map +1 -0
  68. package/dist/components/workspace/admin-shell/PhoneTopBar.svelte +93 -0
  69. package/dist/components/workspace/admin-shell/PhoneTopBar.svelte.d.ts +11 -0
  70. package/dist/components/workspace/admin-shell/PhoneTopBar.svelte.d.ts.map +1 -0
  71. package/dist/components/workspace/admin-shell/ShellNavToggle.svelte +88 -0
  72. package/dist/components/workspace/admin-shell/ShellNavToggle.svelte.d.ts +14 -0
  73. package/dist/components/workspace/admin-shell/ShellNavToggle.svelte.d.ts.map +1 -0
  74. package/dist/components/workspace/admin-shell/ShellTitle.svelte +42 -0
  75. package/dist/components/workspace/admin-shell/ShellTitle.svelte.d.ts +11 -0
  76. package/dist/components/workspace/admin-shell/ShellTitle.svelte.d.ts.map +1 -0
  77. package/dist/components/workspace/admin-shell/TenantNav.svelte +116 -13
  78. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -2
  79. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  80. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte +16 -2
  81. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts +2 -0
  82. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts.map +1 -1
  83. package/dist/components/workspace/admin-shell/mobile-shell-dom.d.ts +37 -0
  84. package/dist/components/workspace/admin-shell/mobile-shell-dom.d.ts.map +1 -0
  85. package/dist/components/workspace/admin-shell/mobile-shell-dom.js +150 -0
  86. package/dist/components/workspace/admin-shell/mobile-shell.d.ts +191 -0
  87. package/dist/components/workspace/admin-shell/mobile-shell.d.ts.map +1 -0
  88. package/dist/components/workspace/admin-shell/mobile-shell.js +165 -0
  89. package/dist/components/workspace/admin-shell/settings.d.ts +27 -2
  90. package/dist/components/workspace/admin-shell/settings.d.ts.map +1 -1
  91. package/dist/components/workspace/admin-shell/settings.js +84 -2
  92. package/dist/components/workspace/admin-shell/state.svelte.d.ts +57 -1
  93. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  94. package/dist/components/workspace/admin-shell/state.svelte.js +156 -6
  95. package/dist/components/workspace/admin-shell/types.d.ts +115 -1
  96. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  97. package/dist/components/workspace/admin-shell/types.js +13 -0
  98. package/dist/components/workspace/index.d.ts +11 -4
  99. package/dist/components/workspace/index.d.ts.map +1 -1
  100. package/dist/components/workspace/index.js +12 -3
  101. package/dist/i18n/strings.forms.d.ts +3 -0
  102. package/dist/i18n/strings.forms.d.ts.map +1 -1
  103. package/dist/i18n/strings.forms.js +3 -0
  104. package/dist/i18n/strings.workspace.d.ts +14 -0
  105. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  106. package/dist/i18n/strings.workspace.js +14 -0
  107. package/dist/index.d.ts +1 -1
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +1 -1
  110. package/dist/provider-mode.test.js +86 -0
  111. package/dist/state/app-state.svelte.d.ts.map +1 -1
  112. package/dist/state/app-state.svelte.js +8 -8
  113. package/dist/web/__tests__/assistant-dock.integration.svelte.test.js +2 -2
  114. package/dist/web/__tests__/list-sort-surface.fixture.svelte +41 -0
  115. package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts +15 -0
  116. package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts.map +1 -0
  117. package/dist/web/__tests__/list-sort-surface.svelte.test.js +59 -0
  118. package/dist/web/__tests__/surfaces.child.fixture.svelte +69 -0
  119. package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts +16 -0
  120. package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts.map +1 -0
  121. package/dist/web/__tests__/surfaces.fixture.svelte +26 -0
  122. package/dist/web/__tests__/surfaces.fixture.svelte.d.ts +18 -0
  123. package/dist/web/__tests__/surfaces.fixture.svelte.d.ts.map +1 -0
  124. package/dist/web/__tests__/surfaces.svelte.test.js +140 -0
  125. package/dist/web/__tests__/webmcp-ui.test.js +18 -0
  126. package/dist/web/index.d.ts +4 -1
  127. package/dist/web/index.d.ts.map +1 -1
  128. package/dist/web/index.js +4 -1
  129. package/dist/web/link-surface.svelte.d.ts +14 -0
  130. package/dist/web/link-surface.svelte.d.ts.map +1 -0
  131. package/dist/web/link-surface.svelte.js +49 -0
  132. package/dist/web/list-surface.svelte.d.ts +80 -0
  133. package/dist/web/list-surface.svelte.d.ts.map +1 -0
  134. package/dist/web/list-surface.svelte.js +309 -0
  135. package/dist/web/step-surface.svelte.d.ts +17 -0
  136. package/dist/web/step-surface.svelte.d.ts.map +1 -0
  137. package/dist/web/step-surface.svelte.js +56 -0
  138. package/dist/web/webmcp-ui-context.d.ts +7 -0
  139. package/dist/web/webmcp-ui-context.d.ts.map +1 -1
  140. package/dist/web/webmcp-ui-context.js +10 -0
  141. package/dist/web/webmcp-ui.d.ts.map +1 -1
  142. package/dist/web/webmcp-ui.js +16 -3
  143. package/package.json +17 -17
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-sort-surface.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../src/web/__tests__/list-sort-surface.fixture.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,QAAQ,EACT,MAAM,6BAA6B,CAAC;AAGpC,KAAK,gBAAgB,GAAI;IACxB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,IAAI,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAqCF,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { createDataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
2
+ import { render, screen } from '@testing-library/svelte';
3
+ import { tick } from 'svelte';
4
+ import { describe, expect, it } from 'vitest';
5
+ import Fixture from './list-sort-surface.fixture.svelte';
6
+ const rows = [
7
+ { id: 's1', title: 'Budget passes', published: '2026-09-01' },
8
+ { id: 's2', title: 'Arena opens', published: '2026-09-20' },
9
+ ];
10
+ const identity = { surfaceId: 'stories-ui', kind: 'list' };
11
+ let sequence = 0;
12
+ function setSorting(expectedRevision, sorting) {
13
+ sequence += 1;
14
+ return {
15
+ version: 1,
16
+ commandId: `sort-${sequence}`,
17
+ identity,
18
+ expectedRevision,
19
+ controlId: 'set-sorting',
20
+ payload: { sorting },
21
+ };
22
+ }
23
+ describe('useListSurface sorting', () => {
24
+ it('reports the page sort and applies an agent sort through onSort', async () => {
25
+ const registry = createDataSurfaceRegistry();
26
+ render(Fixture, { props: { registry, rows } });
27
+ await tick();
28
+ const before = registry.inspect(identity);
29
+ expect([...(before?.descriptor.query.sortableColumnIds ?? [])].sort()).toEqual(['published', 'title']);
30
+ expect(before?.descriptor.controls.map((control) => control.id)).toContain('set-sorting');
31
+ expect(before?.state.sort).toEqual({
32
+ columnId: 'published',
33
+ direction: 'desc',
34
+ });
35
+ const result = await registry.execute(setSorting(before?.revision ?? 0, [
36
+ { columnId: 'title', direction: 'asc' },
37
+ ]));
38
+ expect(result.ok).toBe(true);
39
+ await tick();
40
+ expect(screen.getByTestId('sort').textContent).toBe('title:asc');
41
+ expect(registry.inspect(identity)?.state.sort).toEqual({
42
+ columnId: 'title',
43
+ direction: 'asc',
44
+ });
45
+ });
46
+ it('refuses unsortable columns, multiple rules, and a denied onSort', async () => {
47
+ const registry = createDataSurfaceRegistry();
48
+ render(Fixture, { props: { registry, rows, deny: true } });
49
+ await tick();
50
+ const revision = () => registry.inspect(identity)?.revision ?? 0;
51
+ expect((await registry.execute(setSorting(revision(), [{ columnId: 'id', direction: 'asc' }]))).ok).toBe(false);
52
+ expect((await registry.execute(setSorting(revision(), [
53
+ { columnId: 'title', direction: 'asc' },
54
+ { columnId: 'published', direction: 'desc' },
55
+ ]))).ok).toBe(false);
56
+ expect((await registry.execute(setSorting(revision(), [{ columnId: 'title', direction: 'asc' }]))).ok).toBe(false);
57
+ expect(screen.getByTestId('sort').textContent).toBe('published:desc');
58
+ });
59
+ });
@@ -0,0 +1,69 @@
1
+ <script lang="ts">
2
+ import { useLinkSurface } from '../link-surface.svelte.js';
3
+ import { useListSurface } from '../list-surface.svelte.js';
4
+ import { useStepSurface } from '../step-surface.svelte.js';
5
+ import { tryUseWebMcpUi } from '../webmcp-ui-context.js';
6
+
7
+ let {
8
+ rows,
9
+ step = $bindable('town'),
10
+ navigate,
11
+ onContext,
12
+ create,
13
+ }: {
14
+ rows: Array<{ id: string; title: string; status: string; token: string }>;
15
+ step?: string;
16
+ navigate: (href: string) => void;
17
+ onContext?: (enabled: boolean) => void;
18
+ create: () => void;
19
+ } = $props();
20
+
21
+ onContext?.(tryUseWebMcpUi() !== null);
22
+
23
+ useListSurface(() => ({
24
+ surfaceId: 'articles-ui',
25
+ label: 'Articles',
26
+ description: 'Articles on this page',
27
+ columns: [
28
+ { id: 'id', label: 'ID', rowKey: true },
29
+ { id: 'title', label: 'Title', searchable: true },
30
+ { id: 'status', label: 'Status', status: true },
31
+ ],
32
+ rows,
33
+ maxRows: 2,
34
+ }));
35
+
36
+ useLinkSurface(() => ({
37
+ surfaceId: 'site-sections-ui',
38
+ label: 'Site sections',
39
+ description: 'Pages in this site',
40
+ links: [
41
+ { id: 'events', label: 'Events', href: '/s/events' },
42
+ { id: 'articles', label: 'Articles', href: '/s/articles' },
43
+ ],
44
+ navigate,
45
+ state: { active: 'articles' },
46
+ }));
47
+
48
+ useStepSurface(() => ({
49
+ surfaceId: 'new-site-steps',
50
+ label: 'New site',
51
+ description: 'Start a new town site',
52
+ steps: [
53
+ { id: 'town', label: 'Your town', complete: step !== 'town' },
54
+ { id: 'features', label: 'Features' },
55
+ ],
56
+ current: step,
57
+ next: () => {
58
+ step = 'features';
59
+ },
60
+ nextWrites: step === 'features',
61
+ showNext: () => document.getElementById('create')?.focus(),
62
+ back: () => {
63
+ step = 'town';
64
+ },
65
+ }));
66
+ </script>
67
+
68
+ <p data-testid="step">{step}</p>
69
+ <button id="create" type="button" onclick={create}>Create site</button>
@@ -0,0 +1,16 @@
1
+ type $$ComponentProps = {
2
+ rows: Array<{
3
+ id: string;
4
+ title: string;
5
+ status: string;
6
+ token: string;
7
+ }>;
8
+ step?: string;
9
+ navigate: (href: string) => void;
10
+ onContext?: (enabled: boolean) => void;
11
+ create: () => void;
12
+ };
13
+ declare const Surfaces: import("svelte").Component<$$ComponentProps, {}, "step">;
14
+ type Surfaces = ReturnType<typeof Surfaces>;
15
+ export default Surfaces;
16
+ //# sourceMappingURL=surfaces.child.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surfaces.child.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../src/web/__tests__/surfaces.child.fixture.svelte.ts"],"names":[],"mappings":"AAQC,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAmEF,QAAA,MAAM,QAAQ,0DAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import type { DataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
3
+ import Provider from '../../Provider.svelte';
4
+ import Child from './surfaces.child.fixture.svelte';
5
+
6
+ let {
7
+ dataSurfaceRegistry,
8
+ withProvider = true,
9
+ ...childProps
10
+ }: {
11
+ dataSurfaceRegistry: DataSurfaceRegistry;
12
+ withProvider?: boolean;
13
+ rows: Array<{ id: string; title: string; status: string; token: string }>;
14
+ navigate: (href: string) => void;
15
+ onContext?: (enabled: boolean) => void;
16
+ create: () => void;
17
+ } = $props();
18
+ </script>
19
+
20
+ {#if withProvider}
21
+ <Provider webmcp={{ ui: { dataSurfaceRegistry } }}>
22
+ <Child {...childProps} />
23
+ </Provider>
24
+ {:else}
25
+ <Child {...childProps} />
26
+ {/if}
@@ -0,0 +1,18 @@
1
+ import type { DataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
2
+ type $$ComponentProps = {
3
+ dataSurfaceRegistry: DataSurfaceRegistry;
4
+ withProvider?: boolean;
5
+ rows: Array<{
6
+ id: string;
7
+ title: string;
8
+ status: string;
9
+ token: string;
10
+ }>;
11
+ navigate: (href: string) => void;
12
+ onContext?: (enabled: boolean) => void;
13
+ create: () => void;
14
+ };
15
+ declare const Surfaces: import("svelte").Component<$$ComponentProps, {}, "">;
16
+ type Surfaces = ReturnType<typeof Surfaces>;
17
+ export default Surfaces;
18
+ //# sourceMappingURL=surfaces.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surfaces.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../src/web/__tests__/surfaces.fixture.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAItE,KAAK,gBAAgB,GAAI;IACxB,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAuBF,QAAA,MAAM,QAAQ,sDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,140 @@
1
+ import { createDataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
2
+ import { render, screen } from '@testing-library/svelte';
3
+ import { tick } from 'svelte';
4
+ import { afterEach, describe, expect, it, vi } from 'vitest';
5
+ import Fixture from './surfaces.fixture.svelte';
6
+ const rows = [
7
+ {
8
+ id: 'a1',
9
+ title: 'Council approves budget',
10
+ status: 'published',
11
+ token: 's3cret',
12
+ },
13
+ { id: 'a2', title: 'Café opens downtown', status: 'draft', token: 's3cret' },
14
+ {
15
+ id: 'a3',
16
+ title: 'Council meeting moved',
17
+ status: 'review',
18
+ token: 's3cret',
19
+ },
20
+ ];
21
+ let sequence = 0;
22
+ function command(identity, controlId, expectedRevision, payload) {
23
+ sequence += 1;
24
+ return {
25
+ version: 1,
26
+ commandId: `cmd-${sequence}`,
27
+ identity,
28
+ expectedRevision,
29
+ controlId,
30
+ ...(payload === undefined ? {} : { payload: payload }),
31
+ };
32
+ }
33
+ afterEach(() => {
34
+ delete document.modelContext;
35
+ });
36
+ describe('surface hooks on the Provider registry', () => {
37
+ it('tryUseWebMcpUi is null without a Provider and the hooks register nothing', () => {
38
+ const registry = createDataSurfaceRegistry();
39
+ const onContext = vi.fn();
40
+ render(Fixture, {
41
+ props: {
42
+ dataSurfaceRegistry: registry,
43
+ withProvider: false,
44
+ rows,
45
+ navigate: vi.fn(),
46
+ onContext,
47
+ create: vi.fn(),
48
+ },
49
+ });
50
+ expect(onContext).toHaveBeenCalledWith(false);
51
+ expect(registry.list()).toEqual([]);
52
+ });
53
+ it('useListSurface publishes projected rows and finds rows by title across all rows', async () => {
54
+ const registry = createDataSurfaceRegistry();
55
+ const onContext = vi.fn();
56
+ const view = render(Fixture, {
57
+ props: {
58
+ dataSurfaceRegistry: registry,
59
+ rows,
60
+ navigate: vi.fn(),
61
+ onContext,
62
+ create: vi.fn(),
63
+ },
64
+ });
65
+ await tick();
66
+ expect(onContext).toHaveBeenCalledWith(true);
67
+ const identity = { surfaceId: 'articles-ui', kind: 'list' };
68
+ const before = registry.inspect(identity);
69
+ expect(before?.state.rows).toEqual([
70
+ { id: 'a1', title: 'Council approves budget', status: 'published' },
71
+ { id: 'a2', title: 'Café opens downtown', status: 'draft' },
72
+ ]);
73
+ expect(before?.state.rowsTruncated).toBe(true);
74
+ expect(JSON.stringify(before?.state)).not.toContain('s3cret');
75
+ expect(before?.descriptor.query.searchableColumnIds).toEqual(['title']);
76
+ const found = await registry.execute(command(identity, 'find', before?.revision ?? 0, { text: 'MEETING' }));
77
+ expect(found.ok).toBe(true);
78
+ const after = registry.inspect(identity);
79
+ expect(after?.state.rows).toEqual([
80
+ { id: 'a3', title: 'Council meeting moved', status: 'review' },
81
+ ]);
82
+ expect(after?.state.find).toEqual({ text: 'MEETING', matches: 1 });
83
+ const accent = await registry.execute(command(identity, 'find', after?.revision ?? 0, 'cafe'));
84
+ expect(accent.ok).toBe(true);
85
+ expect(registry.inspect(identity)?.state.rows).toEqual([
86
+ { id: 'a2', title: 'Café opens downtown', status: 'draft' },
87
+ ]);
88
+ const cleared = await registry.execute(command(identity, 'find', registry.inspect(identity)?.revision ?? 0, {
89
+ text: '',
90
+ }));
91
+ expect(cleared.ok).toBe(true);
92
+ expect(registry.inspect(identity)?.state.find).toBeUndefined();
93
+ // Table commands stay refused: the page's own controls own its state.
94
+ const refused = await registry.execute(command(identity, 'set-search', registry.inspect(identity)?.revision ?? 0, {
95
+ search: 'x',
96
+ }));
97
+ expect(refused.ok).toBe(false);
98
+ view.unmount();
99
+ expect(registry.list()).toEqual([]);
100
+ });
101
+ it('useLinkSurface navigates to a published link', async () => {
102
+ const registry = createDataSurfaceRegistry();
103
+ const navigate = vi.fn();
104
+ render(Fixture, {
105
+ props: { dataSurfaceRegistry: registry, rows, navigate, create: vi.fn() },
106
+ });
107
+ await tick();
108
+ const identity = { surfaceId: 'site-sections-ui', kind: 'list' };
109
+ const snapshot = registry.inspect(identity);
110
+ expect(snapshot?.state.active).toBe('articles');
111
+ const result = await registry.execute(command(identity, 'open', snapshot?.revision ?? 0, { target: 'events' }));
112
+ expect(result.ok).toBe(true);
113
+ expect(navigate).toHaveBeenCalledWith('/s/events');
114
+ });
115
+ it('useStepSurface moves a wizard forward and leaves the writing step to the person', async () => {
116
+ const registry = createDataSurfaceRegistry();
117
+ const create = vi.fn();
118
+ render(Fixture, {
119
+ props: { dataSurfaceRegistry: registry, rows, navigate: vi.fn(), create },
120
+ });
121
+ await tick();
122
+ const identity = { surfaceId: 'new-site-steps', kind: 'custom' };
123
+ const first = registry.inspect(identity);
124
+ expect(first?.state.current).toBe('town');
125
+ expect((await registry.execute(command(identity, 'next', first?.revision ?? 0)))
126
+ .ok).toBe(true);
127
+ await tick();
128
+ expect(screen.getByTestId('step')).toHaveTextContent('features');
129
+ const second = registry.inspect(identity);
130
+ expect(second?.state).toMatchObject({
131
+ current: 'features',
132
+ nextWrites: true,
133
+ });
134
+ expect((await registry.execute(command(identity, 'next', second?.revision ?? 0)))
135
+ .ok).toBe(true);
136
+ expect(create).not.toHaveBeenCalled();
137
+ expect(document.activeElement).toBe(screen.getByRole('button', { name: 'Create site' }));
138
+ expect(registry.inspect(identity)?.state.awaitingPerson).toBe(true);
139
+ });
140
+ });
@@ -1,6 +1,7 @@
1
1
  import { createDataSurfaceRegistry, } from '@happyvertical/smrt-ui/data';
2
2
  import { createControlInteractionRegistry, } from '@happyvertical/smrt-ui/forms';
3
3
  import { registerWebMcpBespokeTool, WebMcpToolNameCollisionError, } from '@happyvertical/smrt-web';
4
+ import { isWebMcpProposalTool } from '@happyvertical/smrt-web/webmcp-tool-names';
4
5
  import { afterEach, describe, expect, it, vi } from 'vitest';
5
6
  import { registerWebMcpUiTools } from '../webmcp-ui.js';
6
7
  function modelContext() {
@@ -207,6 +208,10 @@ describe('registerWebMcpUiTools', () => {
207
208
  focus: () => {
208
209
  throw new Error('never-serialize-this cannot be focused');
209
210
  },
211
+ // An agent's `focus` reveals instead of moving keyboard focus.
212
+ reveal: () => {
213
+ throw new Error('never-serialize-this cannot be revealed');
214
+ },
210
215
  });
211
216
  registerWebMcpUiTools({
212
217
  controlRegistry: controls,
@@ -702,4 +707,17 @@ describe('cross-path tool-name lock (#2613)', () => {
702
707
  expect(registered).toHaveLength(19);
703
708
  bespokeDisposer();
704
709
  });
710
+ it('brands every fixed UI tool as proposal-only for the in-page assistant', () => {
711
+ const browser = modelContext();
712
+ const dispose = registerWebMcpUiTools({
713
+ controlRegistry: createControlInteractionRegistry(),
714
+ dataSurfaceRegistry: createDataSurfaceRegistry(),
715
+ document: browser.document,
716
+ });
717
+ expect(browser.registered).toHaveLength(6);
718
+ for (const tool of browser.registered) {
719
+ expect(isWebMcpProposalTool(tool.execute)).toBe(true);
720
+ }
721
+ dispose();
722
+ });
705
723
  });
@@ -15,11 +15,14 @@
15
15
  * @packageDocumentation
16
16
  */
17
17
  export { type ActivityFeedHandle, type ActivityFeedMap, type ActivityFeedOptions, activityFeed, type ShellActivityInput, } from './activity-feed.svelte.js';
18
+ export { type UseLinkSurfaceOptions, useLinkSurface, } from './link-surface.svelte.js';
18
19
  export { type ListDataSurfaceContext, type ListDataSurfaceContextPatch, type ListDataSurfaceControlResult, type ListDataSurfaceHandle, type MountListDataSurfaceOptions, mountListDataSurface, } from './list-data-surface.svelte.js';
20
+ export { findListRows, type ListSurfaceColumn, type ListSurfaceOptions, listSurfaceDescriptor, projectListRows, useListSurface, } from './list-surface.svelte.js';
19
21
  export { type LiveCollection, type LiveCollectionMutation, type LiveCollectionOptions, type LiveCollectionStatus, liveCollection, } from './live-collection.svelte.js';
20
22
  export { type RemoteQueryBinding, remoteQuery, } from './remote-query.svelte.js';
23
+ export { type UseStepSurfaceOptions, useStepSurface, } from './step-surface.svelte.js';
21
24
  export { type UpdateAvailableView, type UseUpdateAvailableOptions, useUpdateAvailable, } from './update-available.svelte.js';
22
25
  export type { WebMcpProviderConfig, WebMcpUiProviderConfig, } from './webmcp-provider.js';
23
26
  export { type RegisterWebMcpUiToolsOptions, registerWebMcpUiTools, } from './webmcp-ui.js';
24
- export { useWebMcpUi, type WebMcpUiContext, } from './webmcp-ui-context.js';
27
+ export { tryUseWebMcpUi, useWebMcpUi, type WebMcpUiContext, } from './webmcp-ui-context.js';
25
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,YAAY,EACZ,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,cAAc,GACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,kBAAkB,GACnB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,4BAA4B,EACjC,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EACX,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,YAAY,EACZ,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,qBAAqB,EACrB,eAAe,EACf,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,cAAc,GACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,kBAAkB,GACnB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,4BAA4B,EACjC,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,WAAW,EACX,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC"}
package/dist/web/index.js CHANGED
@@ -15,9 +15,12 @@
15
15
  * @packageDocumentation
16
16
  */
17
17
  export { activityFeed, } from './activity-feed.svelte.js';
18
+ export { useLinkSurface, } from './link-surface.svelte.js';
18
19
  export { mountListDataSurface, } from './list-data-surface.svelte.js';
20
+ export { findListRows, listSurfaceDescriptor, projectListRows, useListSurface, } from './list-surface.svelte.js';
19
21
  export { liveCollection, } from './live-collection.svelte.js';
20
22
  export { remoteQuery, } from './remote-query.svelte.js';
23
+ export { useStepSurface, } from './step-surface.svelte.js';
21
24
  export { useUpdateAvailable, } from './update-available.svelte.js';
22
25
  export { registerWebMcpUiTools, } from './webmcp-ui.js';
23
- export { useWebMcpUi, } from './webmcp-ui-context.js';
26
+ export { tryUseWebMcpUi, useWebMcpUi, } from './webmcp-ui-context.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `useLinkSurface` — mount a navigation list (a menu, a tab row, a section
3
+ * list) on the nearest `<Provider webmcp>` data-surface registry for the
4
+ * calling component's lifetime, so an agent can move the person between
5
+ * pages. See `registerLinkSurface` in `@happyvertical/smrt-ui/data`.
6
+ *
7
+ * Re-mounts when the identity (surface id + kind + subject) changes and
8
+ * publishes link/state changes as new revisions. A no-op without a WebMCP
9
+ * Provider (nothing could dispatch to it). Call during component init.
10
+ */
11
+ import { type DataSurfaceRegistry, type LinkSurfaceOptions } from '@happyvertical/smrt-ui/data';
12
+ export type UseLinkSurfaceOptions = Omit<LinkSurfaceOptions, 'registry'>;
13
+ export declare function useLinkSurface(getOptions: () => UseLinkSurfaceOptions | null, registry?: DataSurfaceRegistry): void;
14
+ //# sourceMappingURL=link-surface.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-surface.svelte.d.ts","sourceRoot":"","sources":["../../src/web/link-surface.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,kBAAkB,EAExB,MAAM,6BAA6B,CAAC;AAIrC,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;AAEzE,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,qBAAqB,GAAG,IAAI,EAC9C,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,IAAI,CAuCN"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `useLinkSurface` — mount a navigation list (a menu, a tab row, a section
3
+ * list) on the nearest `<Provider webmcp>` data-surface registry for the
4
+ * calling component's lifetime, so an agent can move the person between
5
+ * pages. See `registerLinkSurface` in `@happyvertical/smrt-ui/data`.
6
+ *
7
+ * Re-mounts when the identity (surface id + kind + subject) changes and
8
+ * publishes link/state changes as new revisions. A no-op without a WebMCP
9
+ * Provider (nothing could dispatch to it). Call during component init.
10
+ */
11
+ import { registerLinkSurface, } from '@happyvertical/smrt-ui/data';
12
+ import { onDestroy } from 'svelte';
13
+ import { tryGetWebMcpUiContext } from './webmcp-ui-context.js';
14
+ export function useLinkSurface(getOptions, registry) {
15
+ const uiContext = tryGetWebMcpUiContext();
16
+ let handle = null;
17
+ let mountedKey = null;
18
+ let mountedRegistry = null;
19
+ const teardown = () => {
20
+ handle?.destroy();
21
+ handle = null;
22
+ mountedKey = null;
23
+ mountedRegistry = null;
24
+ };
25
+ $effect(() => {
26
+ const options = getOptions();
27
+ const resolved = registry ??
28
+ (uiContext?.enabled ? uiContext.dataSurfaceRegistry : undefined);
29
+ if (!resolved || !options) {
30
+ teardown();
31
+ return;
32
+ }
33
+ const key = JSON.stringify([
34
+ options.surfaceId,
35
+ options.kind ?? 'list',
36
+ options.subject ?? null,
37
+ (options.controls ?? []).map((control) => control.id),
38
+ ]);
39
+ if (key !== mountedKey || resolved !== mountedRegistry) {
40
+ teardown();
41
+ handle = registerLinkSurface({ ...options, registry: resolved });
42
+ mountedKey = key;
43
+ mountedRegistry = resolved;
44
+ return;
45
+ }
46
+ handle?.update({ links: options.links, state: options.state });
47
+ });
48
+ onDestroy(teardown);
49
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * `useListSurface` — mount a page's rendered list on the nearest
3
+ * `<Provider webmcp>` data-surface registry in one call.
4
+ *
5
+ * A thin convenience over {@link mountListDataSurface} that owns the whole
6
+ * lifecycle (descriptor, controller, identity-keyed re-mount, context
7
+ * updates, destroy) and adds what a bare mount does not:
8
+ *
9
+ * - the visible rows, projected to the declared columns and capped, are
10
+ * published as `state.rows`, so an agent reading the surface sees what the
11
+ * person sees even on a page with no server catalog surface;
12
+ * - a `find` control (when any column is `searchable`) looks rows up by text:
13
+ * payload `{ text }` (or a bare string) keeps the rows whose searchable
14
+ * columns contain it, case-insensitively, across ALL rows — not only the
15
+ * published first page — and publishes them as `state.rows` with
16
+ * `state.find = { text, matches }`. An empty text clears it. It narrows
17
+ * only what the agent reads; the page's own list is unchanged.
18
+ *
19
+ * - sorting (when `sort` and `onSort` are given): the current order is
20
+ * published as `state.sort` and in the table snapshot, `sortable` columns
21
+ * are declared sortable, and a `set-sorting` control
22
+ * (`{ sorting: [{ columnId, direction }] }`, one rule) calls `onSort` — the
23
+ * same path the page's own column headers take, so a server-paginated
24
+ * list re-queries in that order (see `list-sort` in
25
+ * `@happyvertical/smrt-ui/data`).
26
+ *
27
+ * Otherwise a read-only mirror: no remote filter/page/selection commands are
28
+ * accepted (the page's own controls stay the source of truth); `refresh` is
29
+ * honoured when supplied. Only declared columns leave the page: never declare
30
+ * a secret (tokens, keys), and mark names/emails the person can see as
31
+ * `personal`. No-op without a WebMCP Provider. Call during component init.
32
+ */
33
+ import { type DataSurfaceDescriptor, type DataSurfaceJsonValue, type DataSurfaceKind, type DataSurfaceRegistry, type DataSurfaceSubject, type ListSort } from '@happyvertical/smrt-ui/data';
34
+ export interface ListSurfaceColumn {
35
+ id: string;
36
+ label: string;
37
+ description?: string;
38
+ /** The row identity column (exactly one; defaults to the first column). */
39
+ rowKey?: boolean;
40
+ /** A status column (drives status badges in generic renderers). */
41
+ status?: boolean;
42
+ /** Text an agent can look rows up by with the `find` control. */
43
+ searchable?: boolean;
44
+ /** `personal` for names/emails a person sees on the page; never declare secrets. */
45
+ sensitivity?: 'public' | 'personal';
46
+ /** The list can be ordered by this column (needs `sort` + `onSort` on the options). */
47
+ sortable?: boolean;
48
+ }
49
+ type JsonPrimitive = string | number | boolean | null;
50
+ export interface ListSurfaceOptions {
51
+ surfaceId: string;
52
+ kind?: DataSurfaceKind;
53
+ subject?: DataSurfaceSubject;
54
+ label: string;
55
+ description: string;
56
+ columns: readonly ListSurfaceColumn[];
57
+ /** The rows as rendered (after the page's own filters), any shape. */
58
+ rows: readonly object[];
59
+ /** Extra JSON state, e.g. the active filter. */
60
+ context?: Record<string, DataSurfaceJsonValue>;
61
+ refresh?: () => boolean | Promise<boolean>;
62
+ /** Cap on published rows (default 50). */
63
+ maxRows?: number;
64
+ /** The list's current order, as the page shows it. */
65
+ sort?: ListSort | null;
66
+ /**
67
+ * Re-order the list (the page's header-click path). Return `false` to deny.
68
+ * Server-sorted lists navigate to the new `?sort=&dir=` URL here.
69
+ */
70
+ onSort?: (sort: ListSort) => boolean | void | Promise<boolean | void>;
71
+ }
72
+ /** Project rows to the declared columns only; everything else is dropped. */
73
+ export declare function projectListRows(rows: readonly object[], columns: readonly ListSurfaceColumn[], maxRows?: number): Array<Record<string, JsonPrimitive>>;
74
+ /** Rows whose searchable columns contain `text` (case- and accent-insensitive). */
75
+ export declare function findListRows<T extends object>(rows: readonly T[], columns: readonly ListSurfaceColumn[], text: string): T[];
76
+ /** The descriptor {@link useListSurface} mounts. */
77
+ export declare function listSurfaceDescriptor(options: ListSurfaceOptions): DataSurfaceDescriptor;
78
+ export declare function useListSurface(getOptions: () => ListSurfaceOptions | null, registry?: DataSurfaceRegistry): void;
79
+ export {};
80
+ //# sourceMappingURL=list-surface.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-surface.svelte.d.ts","sourceRoot":"","sources":["../../src/web/list-surface.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGvB,KAAK,QAAQ,EACd,MAAM,6BAA6B,CAAC;AAQrC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oFAAoF;IACpF,WAAW,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IACpC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,sEAAsE;IACtE,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACvB;;;OAGG;IAEH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CACvE;AAsBD,6EAA6E;AAC7E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,OAAO,SAAmB,GACzB,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAUtC;AAMD,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,IAAI,EAAE,MAAM,GACX,CAAC,EAAE,CAeL;AAED,oDAAoD;AACpD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,kBAAkB,GAC1B,qBAAqB,CAwFvB;AA4CD,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,kBAAkB,GAAG,IAAI,EAC3C,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,IAAI,CA+GN"}