@dxos/plugin-search 0.10.0 → 0.11.1

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 (152) hide show
  1. package/PLUGIN.mdl +8 -16
  2. package/dist/lib/SearchPlugin.mjs +2 -0
  3. package/dist/lib/capabilities.mjs +9 -0
  4. package/dist/lib/capabilities.mjs.map +1 -0
  5. package/dist/lib/chunk-SearchArticle.mjs +43 -0
  6. package/dist/lib/chunk-SearchArticle.mjs.map +1 -0
  7. package/dist/lib/chunk-SearchDialog.mjs +62 -0
  8. package/dist/lib/chunk-SearchDialog.mjs.map +1 -0
  9. package/dist/lib/chunk-SearchPlugin.mjs +17 -0
  10. package/dist/lib/chunk-SearchPlugin.mjs.map +1 -0
  11. package/dist/lib/chunk-app-graph-builder.mjs +52 -0
  12. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
  13. package/dist/lib/{neutral/chunk-H4PKSTAJ.mjs → chunk-constants.mjs} +4 -5
  14. package/dist/lib/chunk-constants.mjs.map +1 -0
  15. package/dist/lib/chunk-hooks.mjs +281 -0
  16. package/dist/lib/chunk-hooks.mjs.map +1 -0
  17. package/dist/lib/chunk-meta.mjs +40 -0
  18. package/dist/lib/chunk-meta.mjs.map +1 -0
  19. package/dist/lib/chunk-open-search.mjs +16 -0
  20. package/dist/lib/chunk-open-search.mjs.map +1 -0
  21. package/dist/lib/chunk-operation-handler.mjs +11 -0
  22. package/dist/lib/chunk-operation-handler.mjs.map +1 -0
  23. package/dist/lib/chunk-react-surface.mjs +54 -0
  24. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  25. package/dist/lib/chunk-types.mjs +32 -0
  26. package/dist/lib/chunk-types.mjs.map +1 -0
  27. package/dist/lib/components.mjs +148 -0
  28. package/dist/lib/components.mjs.map +1 -0
  29. package/dist/lib/containers.mjs +8 -0
  30. package/dist/lib/containers.mjs.map +1 -0
  31. package/dist/lib/hooks.mjs +2 -0
  32. package/dist/lib/index.mjs +4 -0
  33. package/dist/lib/meta.mjs +2 -0
  34. package/dist/lib/operations.mjs +7 -0
  35. package/dist/lib/operations.mjs.map +1 -0
  36. package/dist/lib/plugin.mjs +9 -0
  37. package/dist/lib/plugin.mjs.map +1 -0
  38. package/dist/lib/testing.mjs +2 -0
  39. package/dist/lib/translations.mjs +18 -0
  40. package/dist/lib/translations.mjs.map +1 -0
  41. package/dist/lib/types.mjs +2 -0
  42. package/dist/types/src/capabilities/SearchSurfaces.d.ts +14 -0
  43. package/dist/types/src/capabilities/SearchSurfaces.d.ts.map +1 -0
  44. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  45. package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
  46. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  47. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  48. package/dist/types/src/components/SearchResultList/SearchResultList.d.ts +15 -0
  49. package/dist/types/src/components/SearchResultList/SearchResultList.d.ts.map +1 -0
  50. package/dist/types/src/components/SearchResultList/SearchResultList.stories.d.ts +16 -0
  51. package/dist/types/src/components/SearchResultList/SearchResultList.stories.d.ts.map +1 -0
  52. package/dist/types/src/components/SearchResultList/SearchResultList.test.d.ts +2 -0
  53. package/dist/types/src/components/SearchResultList/SearchResultList.test.d.ts.map +1 -0
  54. package/dist/types/src/components/SearchResultList/index.d.ts +2 -0
  55. package/dist/types/src/components/SearchResultList/index.d.ts.map +1 -0
  56. package/dist/types/src/components/SearchResultStack/SearchResultStack.d.ts +2 -1
  57. package/dist/types/src/components/SearchResultStack/SearchResultStack.d.ts.map +1 -1
  58. package/dist/types/src/components/index.d.ts +1 -0
  59. package/dist/types/src/components/index.d.ts.map +1 -1
  60. package/dist/types/src/containers/SearchArticle/SearchArticle.d.ts +4 -1
  61. package/dist/types/src/containers/SearchArticle/SearchArticle.d.ts.map +1 -1
  62. package/dist/types/src/containers/SearchDialog/SearchDialog.d.ts +4 -1
  63. package/dist/types/src/containers/SearchDialog/SearchDialog.d.ts.map +1 -1
  64. package/dist/types/src/containers/SearchDialog/SearchDialog.stories.d.ts.map +1 -1
  65. package/dist/types/src/hooks/SearchContext.d.ts +10 -0
  66. package/dist/types/src/hooks/SearchContext.d.ts.map +1 -0
  67. package/dist/types/src/hooks/index.d.ts +3 -0
  68. package/dist/types/src/hooks/index.d.ts.map +1 -1
  69. package/dist/types/src/hooks/search-query.d.ts +26 -0
  70. package/dist/types/src/hooks/search-query.d.ts.map +1 -0
  71. package/dist/types/src/hooks/search-query.test.d.ts +2 -0
  72. package/dist/types/src/hooks/search-query.test.d.ts.map +1 -0
  73. package/dist/types/src/hooks/sync.d.ts +2 -1
  74. package/dist/types/src/hooks/sync.d.ts.map +1 -1
  75. package/dist/types/src/hooks/useGlobalSearch.d.ts +0 -9
  76. package/dist/types/src/hooks/useGlobalSearch.d.ts.map +1 -1
  77. package/dist/types/src/translations.d.ts +2 -0
  78. package/dist/types/src/translations.d.ts.map +1 -1
  79. package/dist/types/tsconfig.tsbuildinfo +1 -1
  80. package/dx.config.ts +5 -7
  81. package/package.json +47 -44
  82. package/src/capabilities/SearchSurfaces.tsx +58 -0
  83. package/src/capabilities/app-graph-builder.ts +4 -4
  84. package/src/capabilities/react-surface.ts +40 -0
  85. package/src/components/SearchResultList/SearchResultList.stories.tsx +152 -0
  86. package/src/components/SearchResultList/SearchResultList.test.tsx +64 -0
  87. package/src/components/SearchResultList/SearchResultList.tsx +60 -0
  88. package/src/components/SearchResultList/index.ts +5 -0
  89. package/src/components/SearchResultStack/SearchResultStack.tsx +15 -8
  90. package/src/components/index.ts +1 -0
  91. package/src/containers/SearchArticle/SearchArticle.tsx +10 -18
  92. package/src/containers/SearchDialog/SearchDialog.stories.tsx +5 -4
  93. package/src/containers/SearchDialog/SearchDialog.tsx +10 -16
  94. package/src/hooks/SearchContext.ts +31 -0
  95. package/src/hooks/index.ts +3 -0
  96. package/src/hooks/search-query.test.ts +27 -0
  97. package/src/hooks/search-query.ts +85 -0
  98. package/src/hooks/sync.ts +1 -1
  99. package/src/hooks/useGlobalSearch.tsx +2 -23
  100. package/src/search/exa.ts +1 -1
  101. package/src/translations.ts +2 -0
  102. package/dist/lib/neutral/SearchArticle-XEP4DYSI.mjs +0 -51
  103. package/dist/lib/neutral/SearchArticle-XEP4DYSI.mjs.map +0 -7
  104. package/dist/lib/neutral/SearchDialog-37ZDKFPS.mjs +0 -76
  105. package/dist/lib/neutral/SearchDialog-37ZDKFPS.mjs.map +0 -7
  106. package/dist/lib/neutral/SearchPlugin.mjs +0 -10
  107. package/dist/lib/neutral/SearchPlugin.mjs.map +0 -7
  108. package/dist/lib/neutral/app-graph-builder-43LAJI5B.mjs +0 -71
  109. package/dist/lib/neutral/app-graph-builder-43LAJI5B.mjs.map +0 -7
  110. package/dist/lib/neutral/capabilities/index.mjs +0 -13
  111. package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
  112. package/dist/lib/neutral/chunk-2JPRW3GN.mjs +0 -50
  113. package/dist/lib/neutral/chunk-2JPRW3GN.mjs.map +0 -7
  114. package/dist/lib/neutral/chunk-BODTE4FK.mjs +0 -28
  115. package/dist/lib/neutral/chunk-BODTE4FK.mjs.map +0 -7
  116. package/dist/lib/neutral/chunk-E2F6MF44.mjs +0 -8
  117. package/dist/lib/neutral/chunk-E2F6MF44.mjs.map +0 -7
  118. package/dist/lib/neutral/chunk-H4PKSTAJ.mjs.map +0 -7
  119. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  120. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  121. package/dist/lib/neutral/chunk-KY3X3WAN.mjs +0 -282
  122. package/dist/lib/neutral/chunk-KY3X3WAN.mjs.map +0 -7
  123. package/dist/lib/neutral/chunk-P6LRQZVR.mjs +0 -34
  124. package/dist/lib/neutral/chunk-P6LRQZVR.mjs.map +0 -7
  125. package/dist/lib/neutral/components/index.mjs +0 -99
  126. package/dist/lib/neutral/components/index.mjs.map +0 -7
  127. package/dist/lib/neutral/containers/index.mjs +0 -11
  128. package/dist/lib/neutral/containers/index.mjs.map +0 -7
  129. package/dist/lib/neutral/hooks/index.mjs +0 -30
  130. package/dist/lib/neutral/hooks/index.mjs.map +0 -7
  131. package/dist/lib/neutral/index.mjs +0 -40
  132. package/dist/lib/neutral/index.mjs.map +0 -7
  133. package/dist/lib/neutral/meta.json +0 -1
  134. package/dist/lib/neutral/meta.mjs +0 -10
  135. package/dist/lib/neutral/meta.mjs.map +0 -7
  136. package/dist/lib/neutral/open-search-STQH3TXW.mjs +0 -23
  137. package/dist/lib/neutral/open-search-STQH3TXW.mjs.map +0 -7
  138. package/dist/lib/neutral/operation-handler-DY624P2R.mjs +0 -13
  139. package/dist/lib/neutral/operation-handler-DY624P2R.mjs.map +0 -7
  140. package/dist/lib/neutral/operations/index.mjs +0 -8
  141. package/dist/lib/neutral/operations/index.mjs.map +0 -7
  142. package/dist/lib/neutral/plugin.mjs +0 -16
  143. package/dist/lib/neutral/plugin.mjs.map +0 -7
  144. package/dist/lib/neutral/react-surface-VQXLVMC2.mjs +0 -60
  145. package/dist/lib/neutral/react-surface-VQXLVMC2.mjs.map +0 -7
  146. package/dist/lib/neutral/testing.mjs +0 -8
  147. package/dist/lib/neutral/testing.mjs.map +0 -7
  148. package/dist/lib/neutral/translations.mjs +0 -25
  149. package/dist/lib/neutral/translations.mjs.map +0 -7
  150. package/dist/lib/neutral/types/index.mjs +0 -8
  151. package/dist/lib/neutral/types/index.mjs.map +0 -7
  152. package/src/capabilities/react-surface.tsx +0 -71
@@ -5,14 +5,14 @@
5
5
  import React, { type KeyboardEvent, forwardRef, useCallback, useMemo, useState } from 'react';
6
6
 
7
7
  import { Surface } from '@dxos/app-framework/ui';
8
- import { AppSurface, useObjectMenuItems } from '@dxos/app-toolkit/ui';
8
+ import { AppSurface, useCardPivot, useObjectMenuItems } from '@dxos/app-toolkit/ui';
9
9
  import { Entity } from '@dxos/echo';
10
10
  import { Card, IconButton } from '@dxos/react-ui';
11
11
  import { ScrollArea } from '@dxos/react-ui';
12
12
  import { composable, composableProps } from '@dxos/react-ui';
13
13
  import { Menu } from '@dxos/react-ui-menu';
14
14
  import { Focus, Mosaic, type MosaicTileProps, useMosaicContainer } from '@dxos/react-ui-mosaic';
15
- import { type SearchResult } from '@dxos/react-ui-search';
15
+ import { Highlighted, type SearchResult } from '@dxos/react-ui-search';
16
16
 
17
17
  //
18
18
  // SearchResultStack
@@ -20,12 +20,13 @@ import { type SearchResult } from '@dxos/react-ui-search';
20
20
 
21
21
  export type SearchResultStackProps = {
22
22
  results: SearchResult[];
23
+ query: string;
23
24
  };
24
25
 
25
26
  export const SearchResultStack = composable<HTMLDivElement, SearchResultStackProps>(
26
- ({ results, ...props }, forwardedRef) => {
27
+ ({ results, query, ...props }, forwardedRef) => {
27
28
  const [viewport, setViewport] = useState<HTMLElement | null>(null);
28
- const items = useMemo(() => results.map((result) => ({ result })), [results]);
29
+ const items = useMemo(() => results.map((result) => ({ result, query })), [results, query]);
29
30
 
30
31
  const handleKeyDown = useCallback((event: KeyboardEvent<HTMLDivElement>) => {
31
32
  if (event.key === 'Enter') {
@@ -65,6 +66,7 @@ SearchResultStack.displayName = 'SearchResultStack';
65
66
 
66
67
  type SearchResultTileData = {
67
68
  result: SearchResult;
69
+ query: string;
68
70
  };
69
71
 
70
72
  type SearchResultTileProps = Pick<MosaicTileProps<SearchResultTileData>, 'location' | 'data' | 'current'>;
@@ -74,8 +76,11 @@ type SearchResultTileProps = Pick<MosaicTileProps<SearchResultTileData>, 'locati
74
76
  */
75
77
  const SearchResultTile = forwardRef<HTMLDivElement, SearchResultTileProps>(
76
78
  ({ data, location, current }, forwardedRef) => {
77
- const { result } = data;
78
- const menuItems = useObjectMenuItems(result.object);
79
+ const { result, query } = data;
80
+ const label = result.label ?? (result.object && Entity.getLabel(result.object)) ?? '';
81
+ // Card.Root already takes the forwarded ref; walk from the header to resolve the origin plank.
82
+ const [cardRef, pivotId] = useCardPivot();
83
+ const menuItems = useObjectMenuItems(result.object, pivotId);
79
84
  const { setCurrentId } = useMosaicContainer('SearchResultTile');
80
85
 
81
86
  const handleCurrentChange = useCallback(() => {
@@ -93,9 +98,11 @@ const SearchResultTile = forwardRef<HTMLDivElement, SearchResultTileProps>(
93
98
  >
94
99
  <Focus.Item asChild current={current} onCurrentChange={handleCurrentChange}>
95
100
  <Card.Root ref={forwardedRef} role='button' classNames='cursor-pointer'>
96
- <Card.Header>
101
+ <Card.Header ref={cardRef}>
97
102
  <Card.Block />
98
- <Card.Title>{result.label ?? (result.object && Entity.getLabel(result.object))}</Card.Title>
103
+ <Card.Title>
104
+ <Highlighted text={label} query={query} />
105
+ </Card.Title>
99
106
  <Card.Block end>
100
107
  <Menu.Trigger asChild disabled={!menuItems?.length}>
101
108
  <IconButton iconOnly variant='ghost' icon='ph--dots-three-vertical--regular' label='Actions' />
@@ -2,4 +2,5 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './SearchResultList';
5
6
  export * from './SearchResultStack';
@@ -5,32 +5,22 @@
5
5
  import React, { useCallback, useMemo, useState } from 'react';
6
6
 
7
7
  import { AppSurface } from '@dxos/app-toolkit/ui';
8
- import { Entity, Filter, Query } from '@dxos/echo';
9
- import { useQuery } from '@dxos/react-client/echo';
8
+ import { Entity } from '@dxos/echo';
9
+ import { useQuery } from '@dxos/echo-react';
10
10
  import { Panel, Toolbar } from '@dxos/react-ui';
11
11
  import { SearchList } from '@dxos/react-ui-search';
12
- import { Text } from '@dxos/schema';
13
12
  import { getHostPlatform, isTauri } from '@dxos/util';
14
13
 
15
14
  import { SearchResultStack } from '#components';
16
- import { useGlobalSearch, useGlobalSearchResults, useWebSearch } from '#hooks';
15
+ import { buildSearchQuery, toSearchResults, useGlobalSearch } from '#hooks';
17
16
 
18
17
  export const SearchArticle = ({ space }: AppSurface.SpaceArticleProps) => {
19
- // TODO(burdon): Option to query across spaces.
18
+ // TODO(burdon): Cross-space search — Milestone 2 (fan-out + merge).
20
19
  const [query, setQuery] = useState<string>();
21
- // TODO(burdon): Re-enable full-text search when indexer is available in all environments.
22
- const objects = useQuery(
23
- space.db,
24
- query === undefined ? Query.select(Filter.nothing()) : Query.select(Filter.not(Filter.type(Text.Text))),
25
- );
26
-
20
+ const objects = useQuery(space.db, buildSearchQuery(query));
27
21
  const { setMatch } = useGlobalSearch();
28
- const results = useGlobalSearchResults(objects);
29
- const { results: webResults } = useWebSearch({ query });
30
- const allResults = useMemo(
31
- () => [...results, ...webResults].filter(({ object }) => object && Entity.getLabel(object)),
32
- [results, webResults],
33
- );
22
+ const results = useMemo(() => (query ? toSearchResults(objects, query) : []), [objects, query]);
23
+ const allResults = useMemo(() => results.filter(({ object }) => object && Entity.getLabel(object)), [results]);
34
24
 
35
25
  const handleSearch = useCallback(
36
26
  (text: string) => {
@@ -47,7 +37,7 @@ export const SearchArticle = ({ space }: AppSurface.SpaceArticleProps) => {
47
37
  <SearchList.Root onSearch={handleSearch}>
48
38
  <Panel.Root>
49
39
  <Panel.Content asChild>
50
- <SearchResultStack results={allResults} />
40
+ <SearchResultStack results={allResults} query={query ?? ''} />
51
41
  </Panel.Content>
52
42
  <Panel.Statusbar asChild>
53
43
  <Toolbar.Root>
@@ -58,3 +48,5 @@ export const SearchArticle = ({ space }: AppSurface.SpaceArticleProps) => {
58
48
  </SearchList.Root>
59
49
  );
60
50
  };
51
+
52
+ SearchArticle.displayName = 'SearchArticle';
@@ -93,16 +93,17 @@ export const Test: Story = {
93
93
  const searchInput = await body.findByRole('textbox', undefined, { timeout: 10_000 });
94
94
  await expect(searchInput).toBeInTheDocument();
95
95
 
96
- // Type a search term that should match generated Person/Organization names.
97
- await userEvent.type(searchInput, 'a');
96
+ // Type a 3+ char term (FTS trigram minimum) likely present across 60 seeded objects.
97
+ await userEvent.type(searchInput, 'the');
98
98
 
99
- // Wait for search results to appear as options in the listbox.
99
+ // Wait for search results to appear as options in the listbox — this is the
100
+ // end-to-end proof that the FTS5 index is wired and populated.
100
101
  await waitFor(
101
102
  async () => {
102
103
  const options = body.queryAllByRole('option');
103
104
  await expect(options.length).toBeGreaterThan(0);
104
105
  },
105
- { timeout: 10_000 },
106
+ { timeout: 15_000 },
106
107
  );
107
108
  },
108
109
  };
@@ -5,16 +5,15 @@
5
5
  import React, { useCallback, useMemo, useState } from 'react';
6
6
 
7
7
  import { useOperationInvoker } from '@dxos/app-framework/ui';
8
- import { LayoutOperation, Paths } from '@dxos/app-toolkit';
8
+ import { GraphPath, LayoutOperation } from '@dxos/app-toolkit';
9
9
  import { type AppSurface } from '@dxos/app-toolkit/ui';
10
10
  import { useLayout } from '@dxos/app-toolkit/ui';
11
- import { Entity, Filter, Obj, Query } from '@dxos/echo';
12
- import { useQuery } from '@dxos/react-client/echo';
11
+ import { Entity, Obj } from '@dxos/echo';
12
+ import { useQuery } from '@dxos/echo-react';
13
13
  import { Dialog, useTranslation } from '@dxos/react-ui';
14
14
  import { SearchList } from '@dxos/react-ui-search';
15
- import { Text } from '@dxos/schema';
16
15
 
17
- import { useGlobalSearch, useGlobalSearchResults } from '#hooks';
16
+ import { buildSearchQuery, toSearchResults, useGlobalSearch } from '#hooks';
18
17
  import { meta } from '#meta';
19
18
  import { type SearchResult } from '#types';
20
19
 
@@ -30,13 +29,8 @@ export const SearchDialog = ({ space, pivotId: pivotIdProp }: SearchDialogProps)
30
29
  const pivotId = pivotIdProp ?? layout.active[layout.active.length - 1];
31
30
  const [query, setQuery] = useState<string>();
32
31
 
33
- // TODO(burdon): Re-enable full-text search when indexer is available in all environments.
34
- const objects = useQuery(
35
- space?.db,
36
- query === undefined ? Query.select(Filter.nothing()) : Query.select(Filter.not(Filter.type(Text.Text))),
37
- );
38
-
39
- const results = useGlobalSearchResults(objects);
32
+ const objects = useQuery(space?.db, buildSearchQuery(query));
33
+ const results = useMemo(() => (query ? toSearchResults(objects, query) : []), [objects, query]);
40
34
  const allResults = useMemo(() => results.filter(({ object }) => object && Entity.getLabel(object)), [results]);
41
35
 
42
36
  const handleSearch = useCallback(
@@ -53,12 +47,12 @@ export const SearchDialog = ({ space, pivotId: pivotIdProp }: SearchDialogProps)
53
47
  return;
54
48
  }
55
49
 
56
- const qualifiedPath = Paths.getObjectPathFromObject(result.object);
50
+ const qualifiedPath = GraphPath.getObjectPathFromObject(result.object);
57
51
  await invokePromise(LayoutOperation.UpdateDialog, { state: false });
58
52
  await invokePromise(LayoutOperation.Open, {
59
53
  subject: [qualifiedPath],
60
54
  pivotId,
61
- positioning: 'end',
55
+ disposition: 'add',
62
56
  });
63
57
  },
64
58
  [pivotId, invokePromise],
@@ -72,8 +66,6 @@ export const SearchDialog = ({ space, pivotId: pivotIdProp }: SearchDialogProps)
72
66
  <Dialog.ActionIconButton action='close' />
73
67
  </Dialog.Close>
74
68
  </Dialog.Header>
75
- {/* Dialog.Body is the column propagator; without it the SearchList input/viewport are direct
76
- children of Dialog.Content's Column grid and land in the gutter (misplaced searchbox). */}
77
69
  <Dialog.Body>
78
70
  <SearchList.Root onSearch={handleSearch}>
79
71
  <SearchList.Input classNames='px-0' autoFocus placeholder={t('search.placeholder')} />
@@ -95,3 +87,5 @@ export const SearchDialog = ({ space, pivotId: pivotIdProp }: SearchDialogProps)
95
87
  </Dialog.Content>
96
88
  );
97
89
  };
90
+
91
+ SearchDialog.displayName = 'SearchDialog';
@@ -0,0 +1,31 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { createContext, useContext } from 'react';
6
+
7
+ import { raise } from '@dxos/debug';
8
+ import { type Entity } from '@dxos/echo';
9
+
10
+ import { type SearchResult } from '#types';
11
+
12
+ import { filterObjectsSync } from './sync';
13
+
14
+ // Kept out of `useGlobalSearch.tsx`: react-refresh only fast-refreshes a module whose exports are all
15
+ // components, so a context and its hooks exported beside them force a full page reload on every edit.
16
+
17
+ export type SearchContextType = {
18
+ match?: RegExp;
19
+ setMatch?: (text?: string) => void;
20
+ };
21
+
22
+ export const SearchContext = createContext<SearchContextType>({});
23
+
24
+ export const useGlobalSearch = () => {
25
+ return useContext(SearchContext) ?? raise(new Error('Missing SearchContext.'));
26
+ };
27
+
28
+ export const useGlobalSearchResults = <T extends Entity.Unknown>(objects?: T[]): SearchResult<T>[] => {
29
+ const { match } = useGlobalSearch();
30
+ return objects && match ? filterObjectsSync(objects, match) : [];
31
+ };
@@ -2,8 +2,11 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ export * from './search-query';
5
6
  export * from './sync';
6
7
 
8
+ export { GlobalFilterProvider, useGlobalFilteredObjects } from '@dxos/react-ui-search';
9
+ export { useGlobalSearch, useGlobalSearchResults } from './SearchContext';
7
10
  export * from './useGlobalSearch';
8
11
  export * from './useSearchResults';
9
12
  export * from './useWebSearch';
@@ -0,0 +1,27 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, expect, test } from 'vitest';
6
+
7
+ import { buildSearchFilter, buildSearchQuery, byRelevance } from './search-query';
8
+
9
+ describe('search-query', () => {
10
+ test('buildSearchFilter emits a full-text text-search node', () => {
11
+ const filter = buildSearchFilter('invoice');
12
+ expect(filter.ast.type).toBe('text-search');
13
+ expect(filter.ast).toMatchObject({ type: 'text-search', text: 'invoice', searchKind: 'full-text' });
14
+ });
15
+
16
+ test('buildSearchQuery is empty for blank input', () => {
17
+ // `Filter.nothing()` is a negated match-all.
18
+ expect(buildSearchQuery(undefined).ast).toBeDefined();
19
+ expect(buildSearchQuery(' ').ast).toBeDefined();
20
+ });
21
+
22
+ test('byRelevance ranks exact, then prefix, then substring, then length', () => {
23
+ const items = [{ label: 'Alicia' }, { label: 'Al' }, { label: 'Sal' }, { label: 'al' }];
24
+ const sorted = [...items].sort(byRelevance('al'));
25
+ expect(sorted.map((i) => i.label)).toEqual(['al', 'Al', 'Alicia', 'Sal']);
26
+ });
27
+ });
@@ -0,0 +1,85 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { Entity, Filter, Obj, Query } from '@dxos/echo';
6
+ import { Text } from '@dxos/schema';
7
+
8
+ import { type SearchResult } from '#types';
9
+
10
+ import { getIcon, mapObjectToTextFields } from './sync';
11
+
12
+ /** Full-text search filter over the FTS5 index. */
13
+ export const buildSearchFilter = (text: string): Filter.Any => Filter.text(text, { type: 'full-text' });
14
+
15
+ /**
16
+ * Build the ECHO query for a search box value. Empty input matches nothing; a term
17
+ * routes to the FTS index via a single text-search select (never combined with a
18
+ * type filter — that composition is unsupported by the executor).
19
+ */
20
+ export const buildSearchQuery = (text: string | undefined): Query.Any => {
21
+ const trimmed = text?.trim();
22
+ return trimmed ? Query.select(buildSearchFilter(trimmed)) : Query.select(Filter.nothing());
23
+ };
24
+
25
+ /**
26
+ * Presentation ordering for already-matched results: exact label, then prefix, then
27
+ * substring, then shorter labels first. The FTS index does the semantic matching;
28
+ * this only orders what it returned. (Exposing the engine's BM25 rank is a follow-up.)
29
+ */
30
+ export const byRelevance =
31
+ (query: string) =>
32
+ (a: { label?: string }, b: { label?: string }): number => {
33
+ const trimmedQuery = query.trim();
34
+ const needle = trimmedQuery.toLowerCase();
35
+ const rank = (label?: string): number => {
36
+ const value = (label ?? '').toLowerCase();
37
+ if (value === needle) {
38
+ return 0;
39
+ }
40
+ if (value.startsWith(needle)) {
41
+ return 1;
42
+ }
43
+ if (value.includes(needle)) {
44
+ return 2;
45
+ }
46
+ return 3;
47
+ };
48
+ const byRank = rank(a.label) - rank(b.label);
49
+ if (byRank !== 0) {
50
+ return byRank;
51
+ }
52
+ // Case-insensitive rank alone can tie two differently-cased matches (e.g. "al" vs "Al");
53
+ // prefer the one that matches the query's exact case.
54
+ const exactCase = (label?: string): number => ((label ?? '') === trimmedQuery ? 0 : 1);
55
+ const byExactCase = exactCase(a.label) - exactCase(b.label);
56
+ if (byExactCase !== 0) {
57
+ return byExactCase;
58
+ }
59
+ return (a.label?.length ?? 0) - (b.label?.length ?? 0);
60
+ };
61
+
62
+ /**
63
+ * Map FTS-matched ECHO objects to ranked search results. Text objects are dropped
64
+ * (they carry no independent label and are indexed via their host object).
65
+ */
66
+ export const toSearchResults = <T extends Entity.Unknown>(objects: T[], text: string): SearchResult<T>[] => {
67
+ const results = objects.reduce<SearchResult<T>[]>((acc, object) => {
68
+ if (Obj.instanceOf(Text.Text, object)) {
69
+ return acc;
70
+ }
71
+ // TODO(burdon): Use schema (matches the pre-existing pattern in sync.ts's filterObjectsSync).
72
+ const label = Entity.getLabel(object);
73
+ const fields = mapObjectToTextFields(object);
74
+ const snippet = fields.content ?? fields.description ?? Object.values(fields).find((value) => value !== label);
75
+ acc.push({
76
+ id: object.id,
77
+ icon: getIcon(Entity.getType(object)),
78
+ label,
79
+ snippet,
80
+ object,
81
+ });
82
+ return acc;
83
+ }, []);
84
+ return results.sort(byRelevance(text));
85
+ };
package/src/hooks/sync.ts CHANGED
@@ -15,7 +15,7 @@ export const queryStringToMatch = (queryString?: string): RegExp | undefined =>
15
15
  };
16
16
 
17
17
  // TODO(burdon): Type name registry linked to schema?
18
- const getIcon = (type: Type.AnyEntity | undefined): string | undefined => {
18
+ export const getIcon = (type: Type.AnyEntity | undefined): string | undefined => {
19
19
  if (!type) {
20
20
  return undefined;
21
21
  }
@@ -2,26 +2,14 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React, { type PropsWithChildren, createContext, useCallback, useContext, useState } from 'react';
5
+ import React, { type PropsWithChildren, useCallback, useState } from 'react';
6
6
 
7
- import { raise } from '@dxos/debug';
8
7
  import { type Entity } from '@dxos/echo';
9
8
  import { GlobalFilterProvider } from '@dxos/react-ui-search';
10
9
 
11
- import { type SearchResult } from '#types';
12
-
10
+ import { SearchContext } from './SearchContext';
13
11
  import { filterObjectsSync, queryStringToMatch } from './sync';
14
12
 
15
- // Re-export for backward compatibility.
16
- export { GlobalFilterProvider, useGlobalFilteredObjects } from '@dxos/react-ui-search';
17
-
18
- type SearchContextType = {
19
- match?: RegExp;
20
- setMatch?: (text?: string) => void;
21
- };
22
-
23
- const SearchContext = createContext<SearchContextType>({});
24
-
25
13
  /**
26
14
  * Provider for global search context.
27
15
  * Also provides the GlobalFilterProvider for useGlobalFilteredObjects to work.
@@ -50,12 +38,3 @@ export const SearchContextProvider = ({ children }: PropsWithChildren) => {
50
38
  </SearchContext.Provider>
51
39
  );
52
40
  };
53
-
54
- export const useGlobalSearch = () => {
55
- return useContext(SearchContext) ?? raise(new Error('Missing SearchContext.'));
56
- };
57
-
58
- export const useGlobalSearchResults = <T extends Entity.Unknown>(objects?: T[]): SearchResult<T>[] => {
59
- const { match } = useGlobalSearch();
60
- return objects && match ? filterObjectsSync(objects, match) : [];
61
- };
package/src/search/exa.ts CHANGED
@@ -196,7 +196,7 @@ const sanitizeObjects = (entries: { data: any; schema: Type.AnyObj }[]) => {
196
196
  const ref = value['/'];
197
197
  if (idMap.has(ref)) {
198
198
  // TODO(dmaretskyi): Whats the best way to represent a local url.
199
- return { '/': `echo:/${idMap.get(ref)}` };
199
+ return { '/': `echo:///${idMap.get(ref)}` };
200
200
  } else {
201
201
  // Search URIs?
202
202
  return { '/': `search:?q=${encodeURIComponent(ref)}` };
@@ -19,6 +19,8 @@ export const translations = [
19
19
  'recently-closed.heading': 'Recently closed',
20
20
  'pending-results.message': 'Searching…',
21
21
  'search.label': 'Search',
22
+ 'search-result-list.label': 'Search results',
23
+ 'search-result-list.empty.label': 'No results',
22
24
  },
23
25
  },
24
26
  },
@@ -1,51 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/containers/SearchArticle/SearchArticle.tsx
4
- import React, { useCallback, useMemo, useState } from "react";
5
- import { Entity, Filter, Query } from "@dxos/echo";
6
- import { useQuery } from "@dxos/react-client/echo";
7
- import { Panel, Toolbar } from "@dxos/react-ui";
8
- import { SearchList } from "@dxos/react-ui-search";
9
- import { Text } from "@dxos/schema";
10
- import { getHostPlatform, isTauri } from "@dxos/util";
11
- import { SearchResultStack } from "#components";
12
- import { useGlobalSearch, useGlobalSearchResults, useWebSearch } from "#hooks";
13
- var SearchArticle = ({ space }) => {
14
- const [query, setQuery] = useState();
15
- const objects = useQuery(space.db, query === void 0 ? Query.select(Filter.nothing()) : Query.select(Filter.not(Filter.type(Text.Text))));
16
- const { setMatch } = useGlobalSearch();
17
- const results = useGlobalSearchResults(objects);
18
- const { results: webResults } = useWebSearch({
19
- query
20
- });
21
- const allResults = useMemo(() => [
22
- ...results,
23
- ...webResults
24
- ].filter(({ object }) => object && Entity.getLabel(object)), [
25
- results,
26
- webResults
27
- ]);
28
- const handleSearch = useCallback((text) => {
29
- setQuery(text);
30
- setMatch?.(text);
31
- }, [
32
- setMatch
33
- ]);
34
- const autoFocus = !isTauri() || getHostPlatform() !== "ios";
35
- return /* @__PURE__ */ React.createElement(SearchList.Root, {
36
- onSearch: handleSearch
37
- }, /* @__PURE__ */ React.createElement(Panel.Root, null, /* @__PURE__ */ React.createElement(Panel.Content, {
38
- asChild: true
39
- }, /* @__PURE__ */ React.createElement(SearchResultStack, {
40
- results: allResults
41
- })), /* @__PURE__ */ React.createElement(Panel.Statusbar, {
42
- asChild: true
43
- }, /* @__PURE__ */ React.createElement(Toolbar.Root, null, /* @__PURE__ */ React.createElement(SearchList.Input, {
44
- placeholder: "Search...",
45
- autoFocus
46
- })))));
47
- };
48
- export {
49
- SearchArticle as default
50
- };
51
- //# sourceMappingURL=SearchArticle-XEP4DYSI.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/containers/SearchArticle/SearchArticle.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport React, { useCallback, useMemo, useState } from 'react';\n\nimport { AppSurface } from '@dxos/app-toolkit/ui';\nimport { Entity, Filter, Query } from '@dxos/echo';\nimport { useQuery } from '@dxos/react-client/echo';\nimport { Panel, Toolbar } from '@dxos/react-ui';\nimport { SearchList } from '@dxos/react-ui-search';\nimport { Text } from '@dxos/schema';\nimport { getHostPlatform, isTauri } from '@dxos/util';\n\nimport { SearchResultStack } from '#components';\nimport { useGlobalSearch, useGlobalSearchResults, useWebSearch } from '#hooks';\n\nexport const SearchArticle = ({ space }: AppSurface.SpaceArticleProps) => {\n // TODO(burdon): Option to query across spaces.\n const [query, setQuery] = useState<string>();\n // TODO(burdon): Re-enable full-text search when indexer is available in all environments.\n const objects = useQuery(\n space.db,\n query === undefined ? Query.select(Filter.nothing()) : Query.select(Filter.not(Filter.type(Text.Text))),\n );\n\n const { setMatch } = useGlobalSearch();\n const results = useGlobalSearchResults(objects);\n const { results: webResults } = useWebSearch({ query });\n const allResults = useMemo(\n () => [...results, ...webResults].filter(({ object }) => object && Entity.getLabel(object)),\n [results, webResults],\n );\n\n const handleSearch = useCallback(\n (text: string) => {\n setQuery(text);\n setMatch?.(text);\n },\n [setMatch],\n );\n\n const autoFocus = !isTauri() || getHostPlatform() !== 'ios';\n\n // TODO(burdon): Move current up/down without losing focus.\n return (\n <SearchList.Root onSearch={handleSearch}>\n <Panel.Root>\n <Panel.Content asChild>\n <SearchResultStack results={allResults} />\n </Panel.Content>\n <Panel.Statusbar asChild>\n <Toolbar.Root>\n <SearchList.Input placeholder='Search...' autoFocus={autoFocus} />\n </Toolbar.Root>\n </Panel.Statusbar>\n </Panel.Root>\n </SearchList.Root>\n );\n};\n"],
5
- "mappings": ";;;AAIA,OAAOA,SAASC,aAAaC,SAASC,gBAAgB;AAGtD,SAASC,QAAQC,QAAQC,aAAa;AACtC,SAASC,gBAAgB;AACzB,SAASC,OAAOC,eAAe;AAC/B,SAASC,kBAAkB;AAC3B,SAASC,YAAY;AACrB,SAASC,iBAAiBC,eAAe;AAEzC,SAASC,yBAAyB;AAClC,SAASC,iBAAiBC,wBAAwBC,oBAAoB;AAE/D,IAAMC,gBAAgB,CAAC,EAAEC,MAAK,MAAgC;AAEnE,QAAM,CAACC,OAAOC,QAAAA,IAAYlB,SAAAA;AAE1B,QAAMmB,UAAUf,SACdY,MAAMI,IACNH,UAAUI,SAAYlB,MAAMmB,OAAOpB,OAAOqB,QAAO,CAAA,IAAMpB,MAAMmB,OAAOpB,OAAOsB,IAAItB,OAAOuB,KAAKjB,KAAKA,IAAI,CAAA,CAAA,CAAA;AAGtG,QAAM,EAAEkB,SAAQ,IAAKd,gBAAAA;AACrB,QAAMe,UAAUd,uBAAuBM,OAAAA;AACvC,QAAM,EAAEQ,SAASC,WAAU,IAAKd,aAAa;IAAEG;EAAM,CAAA;AACrD,QAAMY,aAAa9B,QACjB,MAAM;OAAI4B;OAAYC;IAAYE,OAAO,CAAC,EAAEC,OAAM,MAAOA,UAAU9B,OAAO+B,SAASD,MAAAA,CAAAA,GACnF;IAACJ;IAASC;GAAW;AAGvB,QAAMK,eAAenC,YACnB,CAACoC,SAAAA;AACChB,aAASgB,IAAAA;AACTR,eAAWQ,IAAAA;EACb,GACA;IAACR;GAAS;AAGZ,QAAMS,YAAY,CAACzB,QAAAA,KAAaD,gBAAAA,MAAsB;AAGtD,SACE,sBAAA,cAACF,WAAW6B,MAAI;IAACC,UAAUJ;KACzB,sBAAA,cAAC5B,MAAM+B,MAAI,MACT,sBAAA,cAAC/B,MAAMiC,SAAO;IAACC,SAAAA;KACb,sBAAA,cAAC5B,mBAAAA;IAAkBgB,SAASE;OAE9B,sBAAA,cAACxB,MAAMmC,WAAS;IAACD,SAAAA;KACf,sBAAA,cAACjC,QAAQ8B,MAAI,MACX,sBAAA,cAAC7B,WAAWkC,OAAK;IAACC,aAAY;IAAYP;;AAMtD;",
6
- "names": ["React", "useCallback", "useMemo", "useState", "Entity", "Filter", "Query", "useQuery", "Panel", "Toolbar", "SearchList", "Text", "getHostPlatform", "isTauri", "SearchResultStack", "useGlobalSearch", "useGlobalSearchResults", "useWebSearch", "SearchArticle", "space", "query", "setQuery", "objects", "db", "undefined", "select", "nothing", "not", "type", "setMatch", "results", "webResults", "allResults", "filter", "object", "getLabel", "handleSearch", "text", "autoFocus", "Root", "onSearch", "Content", "asChild", "Statusbar", "Input", "placeholder"]
7
- }
@@ -1,76 +0,0 @@
1
- import "./chunk-J5LGTIGS.mjs";
2
-
3
- // src/containers/SearchDialog/SearchDialog.tsx
4
- import React, { useCallback, useMemo, useState } from "react";
5
- import { useOperationInvoker } from "@dxos/app-framework/ui";
6
- import { LayoutOperation, Paths } from "@dxos/app-toolkit";
7
- import { useLayout } from "@dxos/app-toolkit/ui";
8
- import { Entity, Filter, Obj, Query } from "@dxos/echo";
9
- import { useQuery } from "@dxos/react-client/echo";
10
- import { Dialog, useTranslation } from "@dxos/react-ui";
11
- import { SearchList } from "@dxos/react-ui-search";
12
- import { Text } from "@dxos/schema";
13
- import { useGlobalSearch, useGlobalSearchResults } from "#hooks";
14
- import { meta } from "#meta";
15
- var SearchDialog = ({ space, pivotId: pivotIdProp }) => {
16
- const { t } = useTranslation(meta.profile.key);
17
- const { invokePromise } = useOperationInvoker();
18
- const { setMatch } = useGlobalSearch();
19
- const layout = useLayout();
20
- const pivotId = pivotIdProp ?? layout.active[layout.active.length - 1];
21
- const [query, setQuery] = useState();
22
- const objects = useQuery(space?.db, query === void 0 ? Query.select(Filter.nothing()) : Query.select(Filter.not(Filter.type(Text.Text))));
23
- const results = useGlobalSearchResults(objects);
24
- const allResults = useMemo(() => results.filter(({ object }) => object && Entity.getLabel(object)), [
25
- results
26
- ]);
27
- const handleSearch = useCallback((text) => {
28
- setQuery(text);
29
- setMatch?.(text);
30
- }, [
31
- setMatch
32
- ]);
33
- const handleSelect = useCallback(async (result) => {
34
- if (!result.object || !Obj.isObject(result.object)) {
35
- return;
36
- }
37
- const qualifiedPath = Paths.getObjectPathFromObject(result.object);
38
- await invokePromise(LayoutOperation.UpdateDialog, {
39
- state: false
40
- });
41
- await invokePromise(LayoutOperation.Open, {
42
- subject: [
43
- qualifiedPath
44
- ],
45
- pivotId,
46
- positioning: "end"
47
- });
48
- }, [
49
- pivotId,
50
- invokePromise
51
- ]);
52
- return /* @__PURE__ */ React.createElement(Dialog.Content, null, /* @__PURE__ */ React.createElement(Dialog.Header, null, /* @__PURE__ */ React.createElement(Dialog.Title, null, t("search-dialog.title")), /* @__PURE__ */ React.createElement(Dialog.Close, {
53
- asChild: true
54
- }, /* @__PURE__ */ React.createElement(Dialog.ActionIconButton, {
55
- action: "close"
56
- }))), /* @__PURE__ */ React.createElement(Dialog.Body, null, /* @__PURE__ */ React.createElement(SearchList.Root, {
57
- onSearch: handleSearch
58
- }, /* @__PURE__ */ React.createElement(SearchList.Input, {
59
- classNames: "px-0",
60
- autoFocus: true,
61
- placeholder: t("search.placeholder")
62
- }), /* @__PURE__ */ React.createElement(SearchList.Viewport, {
63
- classNames: "max-h-[24rem]"
64
- }, query && allResults.length === 0 && /* @__PURE__ */ React.createElement(SearchList.Empty, null), allResults.map((result) => /* @__PURE__ */ React.createElement(SearchList.Item, {
65
- key: result.id,
66
- classNames: "flex gap-2 items-center",
67
- icon: result.icon,
68
- value: result.id,
69
- label: result.label ?? (result.object ? Entity.getLabel(result.object) : void 0) ?? result.id,
70
- onSelect: () => void handleSelect(result)
71
- }))))));
72
- };
73
- export {
74
- SearchDialog as default
75
- };
76
- //# sourceMappingURL=SearchDialog-37ZDKFPS.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/containers/SearchDialog/SearchDialog.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { useCallback, useMemo, useState } from 'react';\n\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { LayoutOperation, Paths } from '@dxos/app-toolkit';\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { useLayout } from '@dxos/app-toolkit/ui';\nimport { Entity, Filter, Obj, Query } from '@dxos/echo';\nimport { useQuery } from '@dxos/react-client/echo';\nimport { Dialog, useTranslation } from '@dxos/react-ui';\nimport { SearchList } from '@dxos/react-ui-search';\nimport { Text } from '@dxos/schema';\n\nimport { useGlobalSearch, useGlobalSearchResults } from '#hooks';\nimport { meta } from '#meta';\nimport { type SearchResult } from '#types';\n\nexport type SearchDialogProps = AppSurface.SpaceArticleProps<{\n pivotId?: string;\n}>;\n\nexport const SearchDialog = ({ space, pivotId: pivotIdProp }: SearchDialogProps) => {\n const { t } = useTranslation(meta.profile.key);\n const { invokePromise } = useOperationInvoker();\n const { setMatch } = useGlobalSearch();\n const layout = useLayout();\n const pivotId = pivotIdProp ?? layout.active[layout.active.length - 1];\n const [query, setQuery] = useState<string>();\n\n // TODO(burdon): Re-enable full-text search when indexer is available in all environments.\n const objects = useQuery(\n space?.db,\n query === undefined ? Query.select(Filter.nothing()) : Query.select(Filter.not(Filter.type(Text.Text))),\n );\n\n const results = useGlobalSearchResults(objects);\n const allResults = useMemo(() => results.filter(({ object }) => object && Entity.getLabel(object)), [results]);\n\n const handleSearch = useCallback(\n (text: string) => {\n setQuery(text);\n setMatch?.(text);\n },\n [setMatch],\n );\n\n const handleSelect = useCallback(\n async (result: SearchResult) => {\n if (!result.object || !Obj.isObject(result.object)) {\n return;\n }\n\n const qualifiedPath = Paths.getObjectPathFromObject(result.object);\n await invokePromise(LayoutOperation.UpdateDialog, { state: false });\n await invokePromise(LayoutOperation.Open, {\n subject: [qualifiedPath],\n pivotId,\n positioning: 'end',\n });\n },\n [pivotId, invokePromise],\n );\n\n return (\n <Dialog.Content>\n <Dialog.Header>\n <Dialog.Title>{t('search-dialog.title')}</Dialog.Title>\n <Dialog.Close asChild>\n <Dialog.ActionIconButton action='close' />\n </Dialog.Close>\n </Dialog.Header>\n {/* Dialog.Body is the column propagator; without it the SearchList input/viewport are direct\n children of Dialog.Content's Column grid and land in the gutter (misplaced searchbox). */}\n <Dialog.Body>\n <SearchList.Root onSearch={handleSearch}>\n <SearchList.Input classNames='px-0' autoFocus placeholder={t('search.placeholder')} />\n <SearchList.Viewport classNames='max-h-[24rem]'>\n {query && allResults.length === 0 && <SearchList.Empty />}\n {allResults.map((result) => (\n <SearchList.Item\n key={result.id}\n classNames='flex gap-2 items-center'\n icon={result.icon}\n value={result.id}\n label={result.label ?? (result.object ? Entity.getLabel(result.object) : undefined) ?? result.id}\n onSelect={() => void handleSelect(result)}\n />\n ))}\n </SearchList.Viewport>\n </SearchList.Root>\n </Dialog.Body>\n </Dialog.Content>\n );\n};\n"],
5
- "mappings": ";;;AAIA,OAAOA,SAASC,aAAaC,SAASC,gBAAgB;AAEtD,SAASC,2BAA2B;AACpC,SAASC,iBAAiBC,aAAa;AAEvC,SAASC,iBAAiB;AAC1B,SAASC,QAAQC,QAAQC,KAAKC,aAAa;AAC3C,SAASC,gBAAgB;AACzB,SAASC,QAAQC,sBAAsB;AACvC,SAASC,kBAAkB;AAC3B,SAASC,YAAY;AAErB,SAASC,iBAAiBC,8BAA8B;AACxD,SAASC,YAAY;AAOd,IAAMC,eAAe,CAAC,EAAEC,OAAOC,SAASC,YAAW,MAAqB;AAC7E,QAAM,EAAEC,EAAC,IAAKV,eAAeK,KAAKM,QAAQC,GAAG;AAC7C,QAAM,EAAEC,cAAa,IAAKvB,oBAAAA;AAC1B,QAAM,EAAEwB,SAAQ,IAAKX,gBAAAA;AACrB,QAAMY,SAAStB,UAAAA;AACf,QAAMe,UAAUC,eAAeM,OAAOC,OAAOD,OAAOC,OAAOC,SAAS,CAAA;AACpE,QAAM,CAACC,OAAOC,QAAAA,IAAY9B,SAAAA;AAG1B,QAAM+B,UAAUtB,SACdS,OAAOc,IACPH,UAAUI,SAAYzB,MAAM0B,OAAO5B,OAAO6B,QAAO,CAAA,IAAM3B,MAAM0B,OAAO5B,OAAO8B,IAAI9B,OAAO+B,KAAKxB,KAAKA,IAAI,CAAA,CAAA,CAAA;AAGtG,QAAMyB,UAAUvB,uBAAuBgB,OAAAA;AACvC,QAAMQ,aAAaxC,QAAQ,MAAMuC,QAAQE,OAAO,CAAC,EAAEC,OAAM,MAAOA,UAAUpC,OAAOqC,SAASD,MAAAA,CAAAA,GAAU;IAACH;GAAQ;AAE7G,QAAMK,eAAe7C,YACnB,CAAC8C,SAAAA;AACCd,aAASc,IAAAA;AACTnB,eAAWmB,IAAAA;EACb,GACA;IAACnB;GAAS;AAGZ,QAAMoB,eAAe/C,YACnB,OAAOgD,WAAAA;AACL,QAAI,CAACA,OAAOL,UAAU,CAAClC,IAAIwC,SAASD,OAAOL,MAAM,GAAG;AAClD;IACF;AAEA,UAAMO,gBAAgB7C,MAAM8C,wBAAwBH,OAAOL,MAAM;AACjE,UAAMjB,cAActB,gBAAgBgD,cAAc;MAAEC,OAAO;IAAM,CAAA;AACjE,UAAM3B,cAActB,gBAAgBkD,MAAM;MACxCC,SAAS;QAACL;;MACV7B;MACAmC,aAAa;IACf,CAAA;EACF,GACA;IAACnC;IAASK;GAAc;AAG1B,SACE,sBAAA,cAACd,OAAO6C,SAAO,MACb,sBAAA,cAAC7C,OAAO8C,QAAM,MACZ,sBAAA,cAAC9C,OAAO+C,OAAK,MAAEpC,EAAE,qBAAA,CAAA,GACjB,sBAAA,cAACX,OAAOgD,OAAK;IAACC,SAAAA;KACZ,sBAAA,cAACjD,OAAOkD,kBAAgB;IAACC,QAAO;QAKpC,sBAAA,cAACnD,OAAOoD,MAAI,MACV,sBAAA,cAAClD,WAAWmD,MAAI;IAACC,UAAUrB;KACzB,sBAAA,cAAC/B,WAAWqD,OAAK;IAACC,YAAW;IAAOC,WAAAA;IAAUC,aAAa/C,EAAE,oBAAA;MAC7D,sBAAA,cAACT,WAAWyD,UAAQ;IAACH,YAAW;KAC7BrC,SAASU,WAAWX,WAAW,KAAK,sBAAA,cAAChB,WAAW0D,OAAK,IAAA,GACrD/B,WAAWgC,IAAI,CAACzB,WACf,sBAAA,cAAClC,WAAW4D,MAAI;IACdjD,KAAKuB,OAAO2B;IACZP,YAAW;IACXQ,MAAM5B,OAAO4B;IACbC,OAAO7B,OAAO2B;IACdG,OAAO9B,OAAO8B,UAAU9B,OAAOL,SAASpC,OAAOqC,SAASI,OAAOL,MAAM,IAAIR,WAAca,OAAO2B;IAC9FI,UAAU,MAAM,KAAKhC,aAAaC,MAAAA;;AAQlD;",
6
- "names": ["React", "useCallback", "useMemo", "useState", "useOperationInvoker", "LayoutOperation", "Paths", "useLayout", "Entity", "Filter", "Obj", "Query", "useQuery", "Dialog", "useTranslation", "SearchList", "Text", "useGlobalSearch", "useGlobalSearchResults", "meta", "SearchDialog", "space", "pivotId", "pivotIdProp", "t", "profile", "key", "invokePromise", "setMatch", "layout", "active", "length", "query", "setQuery", "objects", "db", "undefined", "select", "nothing", "not", "type", "results", "allResults", "filter", "object", "getLabel", "handleSearch", "text", "handleSelect", "result", "isObject", "qualifiedPath", "getObjectPathFromObject", "UpdateDialog", "state", "Open", "subject", "positioning", "Content", "Header", "Title", "Close", "asChild", "ActionIconButton", "action", "Body", "Root", "onSearch", "Input", "classNames", "autoFocus", "placeholder", "Viewport", "Empty", "map", "Item", "id", "icon", "value", "label", "onSelect"]
7
- }
@@ -1,10 +0,0 @@
1
- import {
2
- SearchPlugin,
3
- SearchPlugin_default
4
- } from "./chunk-P6LRQZVR.mjs";
5
- import "./chunk-J5LGTIGS.mjs";
6
- export {
7
- SearchPlugin,
8
- SearchPlugin_default as default
9
- };
10
- //# sourceMappingURL=SearchPlugin.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": [],
4
- "sourcesContent": [],
5
- "mappings": "",
6
- "names": []
7
- }