@memberjunction/ng-explorer-core 5.46.0 → 5.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/lazy-feature-config.js +126 -126
- package/dist/generated/lazy-feature-config.js.map +1 -1
- package/dist/lib/omnibar/index.d.ts +15 -0
- package/dist/lib/omnibar/index.d.ts.map +1 -0
- package/dist/lib/omnibar/index.js +24 -0
- package/dist/lib/omnibar/index.js.map +1 -0
- package/dist/lib/omnibar/omnibar-palette.component.d.ts +130 -0
- package/dist/lib/omnibar/omnibar-palette.component.d.ts.map +1 -0
- package/dist/lib/omnibar/omnibar-palette.component.js +829 -0
- package/dist/lib/omnibar/omnibar-palette.component.js.map +1 -0
- package/dist/lib/omnibar/omnibar-provider.d.ts +94 -0
- package/dist/lib/omnibar/omnibar-provider.d.ts.map +1 -0
- package/dist/lib/omnibar/omnibar-provider.js +84 -0
- package/dist/lib/omnibar/omnibar-provider.js.map +1 -0
- package/dist/lib/omnibar/omnibar-shortcut.d.ts +12 -0
- package/dist/lib/omnibar/omnibar-shortcut.d.ts.map +1 -0
- package/dist/lib/omnibar/omnibar-shortcut.js +16 -0
- package/dist/lib/omnibar/omnibar-shortcut.js.map +1 -0
- package/dist/lib/omnibar/omnibar-user-setting.d.ts +38 -0
- package/dist/lib/omnibar/omnibar-user-setting.d.ts.map +1 -0
- package/dist/lib/omnibar/omnibar-user-setting.js +53 -0
- package/dist/lib/omnibar/omnibar-user-setting.js.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-agent.provider.d.ts +24 -0
- package/dist/lib/omnibar/providers/omnibar-agent.provider.d.ts.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-agent.provider.js +69 -0
- package/dist/lib/omnibar/providers/omnibar-agent.provider.js.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-command.provider.d.ts +29 -0
- package/dist/lib/omnibar/providers/omnibar-command.provider.d.ts.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-command.provider.js +130 -0
- package/dist/lib/omnibar/providers/omnibar-command.provider.js.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-record.provider.d.ts +35 -0
- package/dist/lib/omnibar/providers/omnibar-record.provider.d.ts.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-record.provider.js +176 -0
- package/dist/lib/omnibar/providers/omnibar-record.provider.js.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-search.provider.d.ts +21 -0
- package/dist/lib/omnibar/providers/omnibar-search.provider.d.ts.map +1 -0
- package/dist/lib/omnibar/providers/omnibar-search.provider.js +94 -0
- package/dist/lib/omnibar/providers/omnibar-search.provider.js.map +1 -0
- package/dist/lib/profile/profile-dialog.component.d.ts +12 -0
- package/dist/lib/profile/profile-dialog.component.d.ts.map +1 -1
- package/dist/lib/profile/profile-dialog.component.js +189 -78
- package/dist/lib/profile/profile-dialog.component.js.map +1 -1
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts +16 -0
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +66 -6
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.js +6 -2
- package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts +7 -0
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.js +26 -4
- package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
- package/dist/lib/shell/shell.component.d.ts +62 -2
- package/dist/lib/shell/shell.component.d.ts.map +1 -1
- package/dist/lib/shell/shell.component.js +341 -162
- package/dist/lib/shell/shell.component.js.map +1 -1
- package/dist/lib/shell/shell.module.d.ts +8 -7
- package/dist/lib/shell/shell.module.d.ts.map +1 -1
- package/dist/lib/shell/shell.module.js +5 -2
- package/dist/lib/shell/shell.module.js.map +1 -1
- package/dist/lib/single-list-detail/single-list-detail.component.d.ts.map +1 -1
- package/dist/lib/single-list-detail/single-list-detail.component.js +29 -20
- package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +2 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +49 -49
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omnibar-command.provider.d.ts","sourceRoot":"","sources":["../../../../src/lib/omnibar/providers/omnibar-command.provider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAsC,MAAM,qBAAqB,CAAC;AAE1F;;;;;GAKG;AACH,qBACa,sBAAuB,SAAQ,eAAe;IACvD,SAAgB,WAAW,OAAO;IAClC,SAAgB,GAAG,sBAAsB;IACzC,SAAyB,QAAQ,MAAM;IAIvC,SAAgB,SAAS,eAAe;IACxC,SAAyB,WAAW,8BAAyB;IAEhD,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAsB7F,mEAAmE;IAC7C,qBAAqB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;YAW/F,QAAQ;IActB,OAAO,CAAC,aAAa;YAcP,kBAAkB;IAmBhC;;;OAGG;IACH,OAAO,CAAC,UAAU;CAWrB;AAED,iEAAiE;AACjE,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
8
|
+
import { firstValueFrom } from 'rxjs';
|
|
9
|
+
import { OmnibarProvider, OMNIBAR_NAV_KEY } from '../omnibar-provider';
|
|
10
|
+
/**
|
|
11
|
+
* '/' — commands: switch apps and jump to their nav items. Absorbs the old Ctrl+/
|
|
12
|
+
* app command palette: same fuzzy scoring philosophy (exact > starts-with >
|
|
13
|
+
* contains > description > initials) and the same UserInfoEngine-persisted recent
|
|
14
|
+
* apps (via CommandPaletteService, which stays as the recents engine).
|
|
15
|
+
*/
|
|
16
|
+
let OmnibarCommandProvider = class OmnibarCommandProvider extends OmnibarProvider {
|
|
17
|
+
TriggerChar = '/';
|
|
18
|
+
Key = 'omnibar-commands';
|
|
19
|
+
Priority = 40;
|
|
20
|
+
// 'Go to App', not 'Commands': this mode only NAVIGATES (apps + their nav
|
|
21
|
+
// items) — a verb-of-execution label misled the first design review. Revisit
|
|
22
|
+
// if the mode ever grows real executable commands (MJ Actions would back it).
|
|
23
|
+
ModeLabel = 'Go to App';
|
|
24
|
+
Placeholder = 'App or destination…';
|
|
25
|
+
async GetSuggestions(request) {
|
|
26
|
+
const apps = await this.loadApps();
|
|
27
|
+
const query = request.Query.trim().toLowerCase();
|
|
28
|
+
const scored = apps
|
|
29
|
+
.map((app) => ({ app, score: query.length === 0 ? 1 : this.matchScore(app, query) }))
|
|
30
|
+
.filter((x) => x.score > 0)
|
|
31
|
+
.sort((a, b) => b.score - a.score);
|
|
32
|
+
const suggestions = [];
|
|
33
|
+
for (const { app } of scored) {
|
|
34
|
+
suggestions.push(this.appSuggestion(app));
|
|
35
|
+
// Nav items of strong matches surface as sub-destinations.
|
|
36
|
+
if (query.length > 0 && suggestions.length < request.MaxResults) {
|
|
37
|
+
suggestions.push(...await this.navItemSuggestions(app, query));
|
|
38
|
+
}
|
|
39
|
+
if (suggestions.length >= request.MaxResults) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return suggestions.slice(0, request.MaxResults);
|
|
44
|
+
}
|
|
45
|
+
/** Empty '/' query = recent apps first, then the full app list. */
|
|
46
|
+
async EmptyStateSuggestions(request) {
|
|
47
|
+
const apps = await this.loadApps();
|
|
48
|
+
const recents = await this.context.PaletteService?.GetRecentApps().catch(() => []) ?? [];
|
|
49
|
+
const byRecency = [...apps].sort((a, b) => {
|
|
50
|
+
const ra = recents.indexOf(a.ID);
|
|
51
|
+
const rb = recents.indexOf(b.ID);
|
|
52
|
+
return (ra === -1 ? recents.length : ra) - (rb === -1 ? recents.length : rb);
|
|
53
|
+
});
|
|
54
|
+
return byRecency.slice(0, request.MaxResults).map((app) => this.appSuggestion(app));
|
|
55
|
+
}
|
|
56
|
+
async loadApps() {
|
|
57
|
+
const manager = this.context.Apps;
|
|
58
|
+
if (!manager) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
// Applications (NOT AllApplications): the user's INSTALLED apps only —
|
|
63
|
+
// system-wide apps the user hasn't installed would no-op on SwitchToApp.
|
|
64
|
+
return await firstValueFrom(manager.Applications);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
appSuggestion(app) {
|
|
71
|
+
const nav = { kind: 'app', appId: app.ID, appName: app.Name };
|
|
72
|
+
return {
|
|
73
|
+
type: 'app',
|
|
74
|
+
id: app.ID,
|
|
75
|
+
name: app.Name,
|
|
76
|
+
displayName: app.Name,
|
|
77
|
+
description: app.Description || 'Switch app',
|
|
78
|
+
icon: app.Icon || 'fa-solid fa-grid-2',
|
|
79
|
+
color: app.Color || undefined,
|
|
80
|
+
data: { [OMNIBAR_NAV_KEY]: nav, group: 'Apps' },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async navItemSuggestions(app, query) {
|
|
84
|
+
const items = await app.GetNavItems().catch(() => []);
|
|
85
|
+
return items
|
|
86
|
+
.filter((item) => item.Label?.toLowerCase().includes(query))
|
|
87
|
+
.slice(0, 3)
|
|
88
|
+
.map((item) => {
|
|
89
|
+
const nav = { kind: 'nav', appId: app.ID, navItemName: item.Label };
|
|
90
|
+
return {
|
|
91
|
+
type: 'nav',
|
|
92
|
+
id: `${app.ID}:${item.Label}`,
|
|
93
|
+
name: `${app.Name} → ${item.Label}`,
|
|
94
|
+
displayName: `${app.Name} → ${item.Label}`,
|
|
95
|
+
description: 'Explorer navigation',
|
|
96
|
+
icon: item.Icon || 'fa-solid fa-diagram-next',
|
|
97
|
+
data: { [OMNIBAR_NAV_KEY]: nav, group: 'Navigation' },
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Fuzzy scoring, same tiers as the retired Ctrl+/ palette component:
|
|
103
|
+
* exact 1000 · starts-with 500 · contains 100 · description 50 · initials 25.
|
|
104
|
+
*/
|
|
105
|
+
matchScore(app, query) {
|
|
106
|
+
const name = app.Name.toLowerCase();
|
|
107
|
+
const desc = (app.Description || '').toLowerCase();
|
|
108
|
+
if (name === query)
|
|
109
|
+
return 1000;
|
|
110
|
+
if (name.startsWith(query))
|
|
111
|
+
return 500;
|
|
112
|
+
if (name.includes(query))
|
|
113
|
+
return 100;
|
|
114
|
+
if (desc.includes(query))
|
|
115
|
+
return 50;
|
|
116
|
+
const initials = app.Name.split(/\s+/).map((w) => w.charAt(0).toLowerCase()).join('');
|
|
117
|
+
if (initials.startsWith(query))
|
|
118
|
+
return 25;
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
OmnibarCommandProvider = __decorate([
|
|
123
|
+
RegisterClass(OmnibarProvider, 'omnibar-commands')
|
|
124
|
+
], OmnibarCommandProvider);
|
|
125
|
+
export { OmnibarCommandProvider };
|
|
126
|
+
/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */
|
|
127
|
+
export function LoadOmnibarCommandProvider() {
|
|
128
|
+
// intentional no-op
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=omnibar-command.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omnibar-command.provider.js","sourceRoot":"","sources":["../../../../src/lib/omnibar/providers/omnibar-command.provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { RegisterClass } from '@memberjunction/global';\nimport type { BaseApplication } from '@memberjunction/ng-base-application';\nimport type { MentionSuggestion, ComposerSuggestionRequest } from '@memberjunction/ng-composer';\nimport { firstValueFrom } from 'rxjs';\nimport { OmnibarProvider, OMNIBAR_NAV_KEY, OmnibarNavPayload } from '../omnibar-provider';\n\n/**\n * '/' — commands: switch apps and jump to their nav items. Absorbs the old Ctrl+/\n * app command palette: same fuzzy scoring philosophy (exact > starts-with >\n * contains > description > initials) and the same UserInfoEngine-persisted recent\n * apps (via CommandPaletteService, which stays as the recents engine).\n */\n@RegisterClass(OmnibarProvider, 'omnibar-commands')\nexport class OmnibarCommandProvider extends OmnibarProvider {\n public readonly TriggerChar = '/';\n public readonly Key = 'omnibar-commands';\n public override readonly Priority = 40;\n // 'Go to App', not 'Commands': this mode only NAVIGATES (apps + their nav\n // items) — a verb-of-execution label misled the first design review. Revisit\n // if the mode ever grows real executable commands (MJ Actions would back it).\n public readonly ModeLabel = 'Go to App';\n public override readonly Placeholder = 'App or destination…';\n\n public async GetSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]> {\n const apps = await this.loadApps();\n const query = request.Query.trim().toLowerCase();\n const scored = apps\n .map((app) => ({ app, score: query.length === 0 ? 1 : this.matchScore(app, query) }))\n .filter((x) => x.score > 0)\n .sort((a, b) => b.score - a.score);\n\n const suggestions: MentionSuggestion[] = [];\n for (const { app } of scored) {\n suggestions.push(this.appSuggestion(app));\n // Nav items of strong matches surface as sub-destinations.\n if (query.length > 0 && suggestions.length < request.MaxResults) {\n suggestions.push(...await this.navItemSuggestions(app, query));\n }\n if (suggestions.length >= request.MaxResults) {\n break;\n }\n }\n return suggestions.slice(0, request.MaxResults);\n }\n\n /** Empty '/' query = recent apps first, then the full app list. */\n public override async EmptyStateSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]> {\n const apps = await this.loadApps();\n const recents = await this.context.PaletteService?.GetRecentApps().catch(() => []) ?? [];\n const byRecency = [...apps].sort((a, b) => {\n const ra = recents.indexOf(a.ID);\n const rb = recents.indexOf(b.ID);\n return (ra === -1 ? recents.length : ra) - (rb === -1 ? recents.length : rb);\n });\n return byRecency.slice(0, request.MaxResults).map((app) => this.appSuggestion(app));\n }\n\n private async loadApps(): Promise<BaseApplication[]> {\n const manager = this.context.Apps;\n if (!manager) {\n return [];\n }\n try {\n // Applications (NOT AllApplications): the user's INSTALLED apps only —\n // system-wide apps the user hasn't installed would no-op on SwitchToApp.\n return await firstValueFrom(manager.Applications);\n } catch {\n return [];\n }\n }\n\n private appSuggestion(app: BaseApplication): MentionSuggestion {\n const nav: OmnibarNavPayload = { kind: 'app', appId: app.ID, appName: app.Name };\n return {\n type: 'app',\n id: app.ID,\n name: app.Name,\n displayName: app.Name,\n description: app.Description || 'Switch app',\n icon: app.Icon || 'fa-solid fa-grid-2',\n color: app.Color || undefined,\n data: { [OMNIBAR_NAV_KEY]: nav, group: 'Apps' },\n };\n }\n\n private async navItemSuggestions(app: BaseApplication, query: string): Promise<MentionSuggestion[]> {\n const items = await app.GetNavItems().catch(() => []);\n return items\n .filter((item) => item.Label?.toLowerCase().includes(query))\n .slice(0, 3)\n .map((item) => {\n const nav: OmnibarNavPayload = { kind: 'nav', appId: app.ID, navItemName: item.Label };\n return {\n type: 'nav',\n id: `${app.ID}:${item.Label}`,\n name: `${app.Name} → ${item.Label}`,\n displayName: `${app.Name} → ${item.Label}`,\n description: 'Explorer navigation',\n icon: item.Icon || 'fa-solid fa-diagram-next',\n data: { [OMNIBAR_NAV_KEY]: nav, group: 'Navigation' },\n } satisfies MentionSuggestion;\n });\n }\n\n /**\n * Fuzzy scoring, same tiers as the retired Ctrl+/ palette component:\n * exact 1000 · starts-with 500 · contains 100 · description 50 · initials 25.\n */\n private matchScore(app: BaseApplication, query: string): number {\n const name = app.Name.toLowerCase();\n const desc = (app.Description || '').toLowerCase();\n if (name === query) return 1000;\n if (name.startsWith(query)) return 500;\n if (name.includes(query)) return 100;\n if (desc.includes(query)) return 50;\n const initials = app.Name.split(/\\s+/).map((w) => w.charAt(0).toLowerCase()).join('');\n if (initials.startsWith(query)) return 25;\n return 0;\n }\n}\n\n/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */\nexport function LoadOmnibarCommandProvider(): void {\n // intentional no-op\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { MentionSuggestion, ComposerSuggestionRequest } from '@memberjunction/ng-composer';
|
|
2
|
+
import { OmnibarProvider } from '../omnibar-provider';
|
|
3
|
+
/**
|
|
4
|
+
* '#' — jump straight to a record. Two-phase within one query:
|
|
5
|
+
* 1. Entity-name matches from metadata (grouped "Entities").
|
|
6
|
+
* 2. Top records of the BEST-matching entity, searched by its name field
|
|
7
|
+
* (grouped "Matching records") — fail-soft per entity (RLS/permission errors
|
|
8
|
+
* simply yield no record rows).
|
|
9
|
+
*/
|
|
10
|
+
export declare class OmnibarRecordProvider extends OmnibarProvider {
|
|
11
|
+
readonly TriggerChar = "#";
|
|
12
|
+
readonly Key = "omnibar-records";
|
|
13
|
+
readonly Priority = 50;
|
|
14
|
+
readonly ModeLabel = "Jump to Record";
|
|
15
|
+
readonly Placeholder = "Type an entity, then a record \u2014 e.g. #accounts acme";
|
|
16
|
+
/**
|
|
17
|
+
* Bare '#' empty state: the user's recently OPENED records (from the
|
|
18
|
+
* UserInfoEngine's cached 'MJ: User Record Logs'), newest first. Previously
|
|
19
|
+
* this returned nothing — deliberate (avoid a full-entity dump) but it read
|
|
20
|
+
* as "no results / broken" in the design review. Fail-soft throughout:
|
|
21
|
+
* any error just yields an empty list, never blocks the palette.
|
|
22
|
+
*/
|
|
23
|
+
EmptyStateSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]>;
|
|
24
|
+
/** Batch display-name lookup; entries that fail resolve to absent (caller falls back to the ID). */
|
|
25
|
+
private resolveRecordNames;
|
|
26
|
+
GetSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]>;
|
|
27
|
+
/** Name-ranked entity matches: startsWith beats contains; empty query = none (avoid a 300-entity dump). */
|
|
28
|
+
private matchEntities;
|
|
29
|
+
private entitySuggestion;
|
|
30
|
+
/** Top records of one entity whose name field matches the query. Fail-soft. */
|
|
31
|
+
private recordSuggestions;
|
|
32
|
+
}
|
|
33
|
+
/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */
|
|
34
|
+
export declare function LoadOmnibarRecordProvider(): void;
|
|
35
|
+
//# sourceMappingURL=omnibar-record.provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omnibar-record.provider.d.ts","sourceRoot":"","sources":["../../../../src/lib/omnibar/providers/omnibar-record.provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAChG,OAAO,EAAE,eAAe,EAAsC,MAAM,qBAAqB,CAAC;AAK1F;;;;;;GAMG;AACH,qBACa,qBAAsB,SAAQ,eAAe;IACtD,SAAgB,WAAW,OAAO;IAClC,SAAgB,GAAG,qBAAqB;IACxC,SAAyB,QAAQ,MAAM;IACvC,SAAgB,SAAS,oBAAoB;IAG7C,SAAyB,WAAW,8DAAyD;IAE7F;;;;;;OAMG;IACmB,qBAAqB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAmC7G,oGAAoG;YACtF,kBAAkB;IAoBnB,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAiB7F,2GAA2G;IAC3G,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,gBAAgB;IAexB,+EAA+E;YACjE,iBAAiB;CAqClC;AAED,iEAAiE;AACjE,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
8
|
+
import { CompositeKey, Metadata, RunView } from '@memberjunction/core';
|
|
9
|
+
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
10
|
+
import { OmnibarProvider, OMNIBAR_NAV_KEY } from '../omnibar-provider';
|
|
11
|
+
/** How many record rows the best-matching entity contributes. */
|
|
12
|
+
const MAX_RECORD_ROWS = 5;
|
|
13
|
+
/**
|
|
14
|
+
* '#' — jump straight to a record. Two-phase within one query:
|
|
15
|
+
* 1. Entity-name matches from metadata (grouped "Entities").
|
|
16
|
+
* 2. Top records of the BEST-matching entity, searched by its name field
|
|
17
|
+
* (grouped "Matching records") — fail-soft per entity (RLS/permission errors
|
|
18
|
+
* simply yield no record rows).
|
|
19
|
+
*/
|
|
20
|
+
let OmnibarRecordProvider = class OmnibarRecordProvider extends OmnibarProvider {
|
|
21
|
+
TriggerChar = '#';
|
|
22
|
+
Key = 'omnibar-records';
|
|
23
|
+
Priority = 50;
|
|
24
|
+
ModeLabel = 'Jump to Record';
|
|
25
|
+
// Placeholder doubles as syntax education — the two-phase '#<entity> <record>'
|
|
26
|
+
// pattern wasn't discoverable from a generic hint (design-review finding).
|
|
27
|
+
Placeholder = 'Type an entity, then a record — e.g. #accounts acme';
|
|
28
|
+
/**
|
|
29
|
+
* Bare '#' empty state: the user's recently OPENED records (from the
|
|
30
|
+
* UserInfoEngine's cached 'MJ: User Record Logs'), newest first. Previously
|
|
31
|
+
* this returned nothing — deliberate (avoid a full-entity dump) but it read
|
|
32
|
+
* as "no results / broken" in the design review. Fail-soft throughout:
|
|
33
|
+
* any error just yields an empty list, never blocks the palette.
|
|
34
|
+
*/
|
|
35
|
+
async EmptyStateSuggestions(request) {
|
|
36
|
+
try {
|
|
37
|
+
const md = request.Provider ?? Metadata.Provider;
|
|
38
|
+
const user = request.ContextUser ?? md.CurrentUser;
|
|
39
|
+
const engine = UserInfoEngine.Instance;
|
|
40
|
+
await engine.Config(false, user ?? undefined); // no-op when already loaded
|
|
41
|
+
const logs = [...engine.UserRecordLogs]
|
|
42
|
+
.sort((a, b) => new Date(b.LatestAt).getTime() - new Date(a.LatestAt).getTime())
|
|
43
|
+
.slice(0, request.MaxResults);
|
|
44
|
+
if (logs.length === 0) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
const names = await this.resolveRecordNames(md, logs.map((l) => ({ entityName: l.Entity, recordId: l.RecordID })), user ?? undefined);
|
|
48
|
+
return logs.map((log) => {
|
|
49
|
+
const nav = { kind: 'record', entityName: log.Entity, recordId: log.RecordID };
|
|
50
|
+
const display = names.get(`${log.Entity}||${log.RecordID}`) ?? log.RecordID;
|
|
51
|
+
return {
|
|
52
|
+
type: 'record',
|
|
53
|
+
id: `recent-record:${log.Entity}:${log.RecordID}`,
|
|
54
|
+
name: display,
|
|
55
|
+
displayName: display,
|
|
56
|
+
description: log.Entity,
|
|
57
|
+
icon: 'fa-solid fa-clock-rotate-left',
|
|
58
|
+
data: { [OMNIBAR_NAV_KEY]: nav, group: 'Recently opened' },
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Batch display-name lookup; entries that fail resolve to absent (caller falls back to the ID). */
|
|
67
|
+
async resolveRecordNames(md, targets, user) {
|
|
68
|
+
const resolved = new Map();
|
|
69
|
+
try {
|
|
70
|
+
const inputs = targets.map((t) => ({ EntityName: t.entityName, CompositeKey: CompositeKey.FromID(t.recordId) }));
|
|
71
|
+
const results = await md.GetEntityRecordNames(inputs, user);
|
|
72
|
+
for (const r of results ?? []) {
|
|
73
|
+
if (r.Success && r.RecordName) {
|
|
74
|
+
resolved.set(`${r.EntityName}||${r.CompositeKey.GetValueByIndex(0)}`, r.RecordName);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// name lookup is cosmetic — IDs are an acceptable fallback
|
|
80
|
+
}
|
|
81
|
+
return resolved;
|
|
82
|
+
}
|
|
83
|
+
async GetSuggestions(request) {
|
|
84
|
+
const query = request.Query.trim().toLowerCase();
|
|
85
|
+
const md = request.Provider ?? Metadata.Provider;
|
|
86
|
+
const matches = this.matchEntities(md.Entities, query);
|
|
87
|
+
const suggestions = matches.slice(0, 4).map((e) => this.entitySuggestion(e));
|
|
88
|
+
if (matches.length > 0 && query.length > 0) {
|
|
89
|
+
// Record term = the query MINUS the matched entity name when the query leads
|
|
90
|
+
// with it ('#mj: ai agents amanda' → term 'amanda'); an exact entity-name
|
|
91
|
+
// query lists the entity's top records (empty term = browse).
|
|
92
|
+
const best = matches[0];
|
|
93
|
+
const bestName = best.Name.toLowerCase();
|
|
94
|
+
const term = query.startsWith(bestName) ? query.substring(bestName.length).trim() : query;
|
|
95
|
+
suggestions.push(...await this.recordSuggestions(best, term, request));
|
|
96
|
+
}
|
|
97
|
+
return suggestions.slice(0, request.MaxResults);
|
|
98
|
+
}
|
|
99
|
+
/** Name-ranked entity matches: startsWith beats contains; empty query = none (avoid a 300-entity dump). */
|
|
100
|
+
matchEntities(entities, query) {
|
|
101
|
+
if (query.length === 0) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const scored = entities
|
|
105
|
+
.map((e) => {
|
|
106
|
+
const name = e.Name.toLowerCase();
|
|
107
|
+
const score = name === query ? 3 : name.startsWith(query) ? 2 : name.includes(query) ? 1 : 0;
|
|
108
|
+
return { e, score };
|
|
109
|
+
})
|
|
110
|
+
.filter((x) => x.score > 0)
|
|
111
|
+
.sort((a, b) => (b.score - a.score) || a.e.Name.localeCompare(b.e.Name));
|
|
112
|
+
return scored.map((x) => x.e);
|
|
113
|
+
}
|
|
114
|
+
entitySuggestion(entity) {
|
|
115
|
+
// Enter/click on an entity row NAVIGATES: opens the entity's dynamic list view.
|
|
116
|
+
// Drilling into records happens by TYPING ('#<entity name> <record term>').
|
|
117
|
+
const nav = { kind: 'entity-list', entityName: entity.Name };
|
|
118
|
+
return {
|
|
119
|
+
type: 'entity',
|
|
120
|
+
id: entity.ID,
|
|
121
|
+
name: entity.Name,
|
|
122
|
+
displayName: entity.Name,
|
|
123
|
+
description: entity.Description ? entity.Description.substring(0, 120) : 'Entity — open list view',
|
|
124
|
+
icon: entity.Icon || 'fa-solid fa-table',
|
|
125
|
+
data: { [OMNIBAR_NAV_KEY]: nav, group: 'Entities', entityName: entity.Name },
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** Top records of one entity whose name field matches the query. Fail-soft. */
|
|
129
|
+
async recordSuggestions(entity, term, request) {
|
|
130
|
+
const nameField = entity.NameField;
|
|
131
|
+
if (!nameField) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const escaped = term.replace(/'/g, "''");
|
|
136
|
+
const rv = request.Provider ? RunView.FromMetadataProvider(request.Provider) : new RunView();
|
|
137
|
+
const result = await rv.RunView({
|
|
138
|
+
EntityName: entity.Name,
|
|
139
|
+
ExtraFilter: escaped.length > 0 ? `${nameField.Name} LIKE '%${escaped}%'` : '',
|
|
140
|
+
Fields: [entity.FirstPrimaryKey.Name, nameField.Name],
|
|
141
|
+
OrderBy: nameField.Name,
|
|
142
|
+
MaxRows: MAX_RECORD_ROWS,
|
|
143
|
+
ResultType: 'simple',
|
|
144
|
+
}, request.ContextUser ?? undefined);
|
|
145
|
+
if (!result.Success) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
return result.Results.map((row) => {
|
|
149
|
+
const recordId = String(row[entity.FirstPrimaryKey.Name]);
|
|
150
|
+
const name = String(row[nameField.Name] ?? recordId);
|
|
151
|
+
const nav = { kind: 'record', entityName: entity.Name, recordId };
|
|
152
|
+
return {
|
|
153
|
+
type: 'record',
|
|
154
|
+
id: `${entity.Name}:${recordId}`,
|
|
155
|
+
name,
|
|
156
|
+
displayName: name,
|
|
157
|
+
description: entity.Name,
|
|
158
|
+
icon: 'fa-solid fa-id-card',
|
|
159
|
+
data: { [OMNIBAR_NAV_KEY]: nav, group: 'Matching records' },
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
OmnibarRecordProvider = __decorate([
|
|
169
|
+
RegisterClass(OmnibarProvider, 'omnibar-records')
|
|
170
|
+
], OmnibarRecordProvider);
|
|
171
|
+
export { OmnibarRecordProvider };
|
|
172
|
+
/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */
|
|
173
|
+
export function LoadOmnibarRecordProvider() {
|
|
174
|
+
// intentional no-op
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=omnibar-record.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omnibar-record.provider.js","sourceRoot":"","sources":["../../../../src/lib/omnibar/providers/omnibar-record.provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { RegisterClass } from '@memberjunction/global';\nimport { CompositeKey, Metadata, RunView, EntityInfo, IMetadataProvider, IRunViewProvider, UserInfo } from '@memberjunction/core';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport type { MentionSuggestion, ComposerSuggestionRequest } from '@memberjunction/ng-composer';\nimport { OmnibarProvider, OMNIBAR_NAV_KEY, OmnibarNavPayload } from '../omnibar-provider';\n\n/** How many record rows the best-matching entity contributes. */\nconst MAX_RECORD_ROWS = 5;\n\n/**\n * '#' — jump straight to a record. Two-phase within one query:\n * 1. Entity-name matches from metadata (grouped \"Entities\").\n * 2. Top records of the BEST-matching entity, searched by its name field\n * (grouped \"Matching records\") — fail-soft per entity (RLS/permission errors\n * simply yield no record rows).\n */\n@RegisterClass(OmnibarProvider, 'omnibar-records')\nexport class OmnibarRecordProvider extends OmnibarProvider {\n public readonly TriggerChar = '#';\n public readonly Key = 'omnibar-records';\n public override readonly Priority = 50;\n public readonly ModeLabel = 'Jump to Record';\n // Placeholder doubles as syntax education — the two-phase '#<entity> <record>'\n // pattern wasn't discoverable from a generic hint (design-review finding).\n public override readonly Placeholder = 'Type an entity, then a record — e.g. #accounts acme';\n\n /**\n * Bare '#' empty state: the user's recently OPENED records (from the\n * UserInfoEngine's cached 'MJ: User Record Logs'), newest first. Previously\n * this returned nothing — deliberate (avoid a full-entity dump) but it read\n * as \"no results / broken\" in the design review. Fail-soft throughout:\n * any error just yields an empty list, never blocks the palette.\n */\n public override async EmptyStateSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]> {\n try {\n const md: IMetadataProvider = request.Provider ?? Metadata.Provider;\n const user = request.ContextUser ?? md.CurrentUser;\n const engine = UserInfoEngine.Instance;\n await engine.Config(false, user ?? undefined); // no-op when already loaded\n const logs = [...engine.UserRecordLogs]\n .sort((a, b) => new Date(b.LatestAt).getTime() - new Date(a.LatestAt).getTime())\n .slice(0, request.MaxResults);\n if (logs.length === 0) {\n return [];\n }\n const names = await this.resolveRecordNames(\n md,\n logs.map((l) => ({ entityName: l.Entity, recordId: l.RecordID })),\n user ?? undefined,\n );\n return logs.map((log) => {\n const nav: OmnibarNavPayload = { kind: 'record', entityName: log.Entity, recordId: log.RecordID };\n const display = names.get(`${log.Entity}||${log.RecordID}`) ?? log.RecordID;\n return {\n type: 'record',\n id: `recent-record:${log.Entity}:${log.RecordID}`,\n name: display,\n displayName: display,\n description: log.Entity,\n icon: 'fa-solid fa-clock-rotate-left',\n data: { [OMNIBAR_NAV_KEY]: nav, group: 'Recently opened' },\n } satisfies MentionSuggestion;\n });\n } catch {\n return [];\n }\n }\n\n /** Batch display-name lookup; entries that fail resolve to absent (caller falls back to the ID). */\n private async resolveRecordNames(\n md: IMetadataProvider,\n targets: Array<{ entityName: string; recordId: string }>,\n user?: UserInfo,\n ): Promise<Map<string, string>> {\n const resolved = new Map<string, string>();\n try {\n const inputs = targets.map((t) => ({ EntityName: t.entityName, CompositeKey: CompositeKey.FromID(t.recordId) }));\n const results = await md.GetEntityRecordNames(inputs, user);\n for (const r of results ?? []) {\n if (r.Success && r.RecordName) {\n resolved.set(`${r.EntityName}||${r.CompositeKey.GetValueByIndex(0)}`, r.RecordName);\n }\n }\n } catch {\n // name lookup is cosmetic — IDs are an acceptable fallback\n }\n return resolved;\n }\n\n public async GetSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]> {\n const query = request.Query.trim().toLowerCase();\n const md: IMetadataProvider = request.Provider ?? Metadata.Provider;\n const matches = this.matchEntities(md.Entities, query);\n const suggestions: MentionSuggestion[] = matches.slice(0, 4).map((e) => this.entitySuggestion(e));\n if (matches.length > 0 && query.length > 0) {\n // Record term = the query MINUS the matched entity name when the query leads\n // with it ('#mj: ai agents amanda' → term 'amanda'); an exact entity-name\n // query lists the entity's top records (empty term = browse).\n const best = matches[0];\n const bestName = best.Name.toLowerCase();\n const term = query.startsWith(bestName) ? query.substring(bestName.length).trim() : query;\n suggestions.push(...await this.recordSuggestions(best, term, request));\n }\n return suggestions.slice(0, request.MaxResults);\n }\n\n /** Name-ranked entity matches: startsWith beats contains; empty query = none (avoid a 300-entity dump). */\n private matchEntities(entities: EntityInfo[], query: string): EntityInfo[] {\n if (query.length === 0) {\n return [];\n }\n const scored = entities\n .map((e) => {\n const name = e.Name.toLowerCase();\n const score = name === query ? 3 : name.startsWith(query) ? 2 : name.includes(query) ? 1 : 0;\n return { e, score };\n })\n .filter((x) => x.score > 0)\n .sort((a, b) => (b.score - a.score) || a.e.Name.localeCompare(b.e.Name));\n return scored.map((x) => x.e);\n }\n\n private entitySuggestion(entity: EntityInfo): MentionSuggestion {\n // Enter/click on an entity row NAVIGATES: opens the entity's dynamic list view.\n // Drilling into records happens by TYPING ('#<entity name> <record term>').\n const nav: OmnibarNavPayload = { kind: 'entity-list', entityName: entity.Name };\n return {\n type: 'entity',\n id: entity.ID,\n name: entity.Name,\n displayName: entity.Name,\n description: entity.Description ? entity.Description.substring(0, 120) : 'Entity — open list view',\n icon: entity.Icon || 'fa-solid fa-table',\n data: { [OMNIBAR_NAV_KEY]: nav, group: 'Entities', entityName: entity.Name },\n };\n }\n\n /** Top records of one entity whose name field matches the query. Fail-soft. */\n private async recordSuggestions(entity: EntityInfo, term: string, request: ComposerSuggestionRequest): Promise<MentionSuggestion[]> {\n const nameField = entity.NameField;\n if (!nameField) {\n return [];\n }\n try {\n const escaped = term.replace(/'/g, \"''\");\n const rv = request.Provider ? RunView.FromMetadataProvider(request.Provider) : new RunView();\n const result = await rv.RunView<Record<string, unknown>>({\n EntityName: entity.Name,\n ExtraFilter: escaped.length > 0 ? `${nameField.Name} LIKE '%${escaped}%'` : '',\n Fields: [entity.FirstPrimaryKey.Name, nameField.Name],\n OrderBy: nameField.Name,\n MaxRows: MAX_RECORD_ROWS,\n ResultType: 'simple',\n }, request.ContextUser ?? undefined);\n if (!result.Success) {\n return [];\n }\n return result.Results.map((row) => {\n const recordId = String(row[entity.FirstPrimaryKey.Name]);\n const name = String(row[nameField.Name] ?? recordId);\n const nav: OmnibarNavPayload = { kind: 'record', entityName: entity.Name, recordId };\n return {\n type: 'record',\n id: `${entity.Name}:${recordId}`,\n name,\n displayName: name,\n description: entity.Name,\n icon: 'fa-solid fa-id-card',\n data: { [OMNIBAR_NAV_KEY]: nav, group: 'Matching records' },\n } satisfies MentionSuggestion;\n });\n } catch {\n return [];\n }\n }\n}\n\n/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */\nexport function LoadOmnibarRecordProvider(): void {\n // intentional no-op\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MentionSuggestion, ComposerSuggestionRequest } from '@memberjunction/ng-composer';
|
|
2
|
+
import { OmnibarProvider } from '../omnibar-provider';
|
|
3
|
+
/**
|
|
4
|
+
* The omnibar's DEFAULT mode (empty TriggerChar): plain text = the same
|
|
5
|
+
* cross-source search that backs the Search Results page (vectors + full-text +
|
|
6
|
+
* entities + storage), served through `SearchService.PreviewSearch`. Always appends
|
|
7
|
+
* a trailing "See all results" suggestion that opens the full Search workspace.
|
|
8
|
+
*/
|
|
9
|
+
export declare class OmnibarSearchProvider extends OmnibarProvider {
|
|
10
|
+
readonly TriggerChar = "";
|
|
11
|
+
readonly Key = "omnibar-search";
|
|
12
|
+
readonly Priority = 100;
|
|
13
|
+
readonly ModeLabel = "Global Search";
|
|
14
|
+
readonly Placeholder = "Search everything \u2014 or type #, /, @ \u2026";
|
|
15
|
+
GetSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]>;
|
|
16
|
+
private toSuggestion;
|
|
17
|
+
private seeAllSuggestion;
|
|
18
|
+
}
|
|
19
|
+
/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */
|
|
20
|
+
export declare function LoadOmnibarSearchProvider(): void;
|
|
21
|
+
//# sourceMappingURL=omnibar-search.provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omnibar-search.provider.d.ts","sourceRoot":"","sources":["../../../../src/lib/omnibar/providers/omnibar-search.provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAsC,MAAM,qBAAqB,CAAC;AAoB1F;;;;;GAKG;AACH,qBACa,qBAAsB,SAAQ,eAAe;IACtD,SAAgB,WAAW,MAAM;IACjC,SAAgB,GAAG,oBAAoB;IACvC,SAAyB,QAAQ,OAAO;IACxC,SAAgB,SAAS,mBAAmB;IAC5C,SAAyB,WAAW,qDAA2C;IAElE,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAiB7F,OAAO,CAAC,YAAY;IAoBpB,OAAO,CAAC,gBAAgB;CAY3B;AAED,iEAAiE;AACjE,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
8
|
+
import { OmnibarProvider, OMNIBAR_NAV_KEY } from '../omnibar-provider';
|
|
9
|
+
/** Icon per cross-source result type (matches the Search Results page vocabulary). */
|
|
10
|
+
function iconForResult(item) {
|
|
11
|
+
switch (item.ResultType) {
|
|
12
|
+
case 'storage-file': return 'fa-solid fa-file-lines';
|
|
13
|
+
case 'content-item': return 'fa-solid fa-newspaper';
|
|
14
|
+
default: return 'fa-solid fa-id-card';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Group label per result type — drives the palette's section headers. */
|
|
18
|
+
function groupForResult(item) {
|
|
19
|
+
switch (item.ResultType) {
|
|
20
|
+
case 'storage-file': return 'Files';
|
|
21
|
+
case 'content-item': return 'Content';
|
|
22
|
+
default: return 'Records';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The omnibar's DEFAULT mode (empty TriggerChar): plain text = the same
|
|
27
|
+
* cross-source search that backs the Search Results page (vectors + full-text +
|
|
28
|
+
* entities + storage), served through `SearchService.PreviewSearch`. Always appends
|
|
29
|
+
* a trailing "See all results" suggestion that opens the full Search workspace.
|
|
30
|
+
*/
|
|
31
|
+
let OmnibarSearchProvider = class OmnibarSearchProvider extends OmnibarProvider {
|
|
32
|
+
TriggerChar = '';
|
|
33
|
+
Key = 'omnibar-search';
|
|
34
|
+
Priority = 100;
|
|
35
|
+
ModeLabel = 'Global Search';
|
|
36
|
+
Placeholder = 'Search everything — or type #, /, @ …';
|
|
37
|
+
async GetSuggestions(request) {
|
|
38
|
+
const search = this.context.Search;
|
|
39
|
+
const query = request.Query.trim();
|
|
40
|
+
if (!search || query.length === 0) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const response = await search.PreviewSearch(query, Math.max(1, request.MaxResults - 1));
|
|
45
|
+
const suggestions = (response.Success ? response.Results : []).map((item) => this.toSuggestion(item));
|
|
46
|
+
suggestions.push(this.seeAllSuggestion(query));
|
|
47
|
+
return suggestions;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Fail soft — the palette still offers the full-search escape hatch.
|
|
51
|
+
return [this.seeAllSuggestion(query)];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
toSuggestion(item) {
|
|
55
|
+
const isFile = item.ResultType === 'storage-file';
|
|
56
|
+
const nav = isFile
|
|
57
|
+
? { kind: 'file', fileName: item.Title, rawMetadata: item.RawMetadata }
|
|
58
|
+
: { kind: 'record', entityName: item.EntityName, recordId: item.RecordID };
|
|
59
|
+
return {
|
|
60
|
+
type: groupForResult(item).toLowerCase(),
|
|
61
|
+
id: `${item.EntityName}:${item.RecordID}`,
|
|
62
|
+
name: item.Title,
|
|
63
|
+
displayName: item.Title,
|
|
64
|
+
description: item.Snippet || item.EntityName,
|
|
65
|
+
icon: iconForResult(item),
|
|
66
|
+
data: {
|
|
67
|
+
[OMNIBAR_NAV_KEY]: nav,
|
|
68
|
+
group: groupForResult(item),
|
|
69
|
+
score: item.Score,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
seeAllSuggestion(query) {
|
|
74
|
+
const nav = { kind: 'search', query };
|
|
75
|
+
return {
|
|
76
|
+
type: 'see-all',
|
|
77
|
+
id: `see-all:${query}`,
|
|
78
|
+
name: `See all results for “${query}”`,
|
|
79
|
+
displayName: `See all results for “${query}”`,
|
|
80
|
+
description: 'Opens the full Search Results workspace with scopes & relevance controls',
|
|
81
|
+
icon: 'fa-solid fa-arrow-right',
|
|
82
|
+
data: { [OMNIBAR_NAV_KEY]: nav, group: '' },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
OmnibarSearchProvider = __decorate([
|
|
87
|
+
RegisterClass(OmnibarProvider, 'omnibar-search')
|
|
88
|
+
], OmnibarSearchProvider);
|
|
89
|
+
export { OmnibarSearchProvider };
|
|
90
|
+
/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */
|
|
91
|
+
export function LoadOmnibarSearchProvider() {
|
|
92
|
+
// intentional no-op
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=omnibar-search.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omnibar-search.provider.js","sourceRoot":"","sources":["../../../../src/lib/omnibar/providers/omnibar-search.provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { RegisterClass } from '@memberjunction/global';\nimport type { MentionSuggestion, ComposerSuggestionRequest } from '@memberjunction/ng-composer';\nimport type { SearchResultItem } from '@memberjunction/ng-search';\nimport { OmnibarProvider, OMNIBAR_NAV_KEY, OmnibarNavPayload } from '../omnibar-provider';\n\n/** Icon per cross-source result type (matches the Search Results page vocabulary). */\nfunction iconForResult(item: SearchResultItem): string {\n switch (item.ResultType) {\n case 'storage-file': return 'fa-solid fa-file-lines';\n case 'content-item': return 'fa-solid fa-newspaper';\n default: return 'fa-solid fa-id-card';\n }\n}\n\n/** Group label per result type — drives the palette's section headers. */\nfunction groupForResult(item: SearchResultItem): string {\n switch (item.ResultType) {\n case 'storage-file': return 'Files';\n case 'content-item': return 'Content';\n default: return 'Records';\n }\n}\n\n/**\n * The omnibar's DEFAULT mode (empty TriggerChar): plain text = the same\n * cross-source search that backs the Search Results page (vectors + full-text +\n * entities + storage), served through `SearchService.PreviewSearch`. Always appends\n * a trailing \"See all results\" suggestion that opens the full Search workspace.\n */\n@RegisterClass(OmnibarProvider, 'omnibar-search')\nexport class OmnibarSearchProvider extends OmnibarProvider {\n public readonly TriggerChar = '';\n public readonly Key = 'omnibar-search';\n public override readonly Priority = 100;\n public readonly ModeLabel = 'Global Search';\n public override readonly Placeholder = 'Search everything — or type #, /, @ …';\n\n public async GetSuggestions(request: ComposerSuggestionRequest): Promise<MentionSuggestion[]> {\n const search = this.context.Search;\n const query = request.Query.trim();\n if (!search || query.length === 0) {\n return [];\n }\n try {\n const response = await search.PreviewSearch(query, Math.max(1, request.MaxResults - 1));\n const suggestions = (response.Success ? response.Results : []).map((item) => this.toSuggestion(item));\n suggestions.push(this.seeAllSuggestion(query));\n return suggestions;\n } catch {\n // Fail soft — the palette still offers the full-search escape hatch.\n return [this.seeAllSuggestion(query)];\n }\n }\n\n private toSuggestion(item: SearchResultItem): MentionSuggestion {\n const isFile = item.ResultType === 'storage-file';\n const nav: OmnibarNavPayload = isFile\n ? { kind: 'file', fileName: item.Title, rawMetadata: item.RawMetadata }\n : { kind: 'record', entityName: item.EntityName, recordId: item.RecordID };\n return {\n type: groupForResult(item).toLowerCase(),\n id: `${item.EntityName}:${item.RecordID}`,\n name: item.Title,\n displayName: item.Title,\n description: item.Snippet || item.EntityName,\n icon: iconForResult(item),\n data: {\n [OMNIBAR_NAV_KEY]: nav,\n group: groupForResult(item),\n score: item.Score,\n },\n };\n }\n\n private seeAllSuggestion(query: string): MentionSuggestion {\n const nav: OmnibarNavPayload = { kind: 'search', query };\n return {\n type: 'see-all',\n id: `see-all:${query}`,\n name: `See all results for “${query}”`,\n displayName: `See all results for “${query}”`,\n description: 'Opens the full Search Results workspace with scopes & relevance controls',\n icon: 'fa-solid fa-arrow-right',\n data: { [OMNIBAR_NAV_KEY]: nav, group: '' },\n };\n }\n}\n\n/** Tree-shaking guard — referenced by LoadOmnibarProviders(). */\nexport function LoadOmnibarSearchProvider(): void {\n // intentional no-op\n}\n"]}
|
|
@@ -33,6 +33,11 @@ export declare class ProfileDialogComponent extends BaseAngularComponent impleme
|
|
|
33
33
|
LoadingNotifications: boolean;
|
|
34
34
|
Channels: NotificationChannel[];
|
|
35
35
|
UnreadCount: number;
|
|
36
|
+
OmnibarAvailable: boolean;
|
|
37
|
+
OmnibarEnabled: boolean;
|
|
38
|
+
SavingOmnibar: boolean;
|
|
39
|
+
/** Platform-correct summon-shortcut label ('⌘K' on Mac, 'Ctrl+K' elsewhere). */
|
|
40
|
+
get OmnibarShortcutLabel(): string;
|
|
36
41
|
ActivePanel: ProfilePanel;
|
|
37
42
|
ThemeOptions: Array<{
|
|
38
43
|
id: string;
|
|
@@ -57,6 +62,13 @@ export declare class ProfileDialogComponent extends BaseAngularComponent impleme
|
|
|
57
62
|
ClosePanel(): void;
|
|
58
63
|
OnSignOut(): Promise<void>;
|
|
59
64
|
SelectTheme(themeId: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Per-user omnibar opt-in/out. Persists via UserInfoEngine (MJ: User
|
|
67
|
+
* Settings) so the choice follows the user across browsers/devices. The
|
|
68
|
+
* shell reads the same setting on every change-detection pass, so the
|
|
69
|
+
* header affordance and Ctrl+K behavior flip live — no reload needed.
|
|
70
|
+
*/
|
|
71
|
+
ToggleOmnibar(): Promise<void>;
|
|
60
72
|
ToggleChannel(channel: NotificationChannel): Promise<void>;
|
|
61
73
|
private populateIdentity;
|
|
62
74
|
private populateThemeOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-dialog.component.d.ts","sourceRoot":"","sources":["../../../src/lib/profile/profile-dialog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,YAAY,EAEZ,SAAS,EACT,MAAM,EAIT,MAAM,eAAe,CAAC;AASvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;
|
|
1
|
+
{"version":3,"file":"profile-dialog.component.d.ts","sourceRoot":"","sources":["../../../src/lib/profile/profile-dialog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,YAAY,EAEZ,SAAS,EACT,MAAM,EAIT,MAAM,eAAe,CAAC;AASvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AAOrE,UAAU,mBAAmB;IACzB,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACnB;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE/C;;;;GAIG;AACH,qBA8sBa,sBAAuB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS;IAC/E,cAAc,qBAA4B;IAE3C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAExC,QAAQ,SAAM;IACd,SAAS,SAAM;IACf,YAAY,SAAO;IACnB,WAAW,SAAM;IACjB,aAAa,SAAM;IACnB,WAAW,SAAM;IACjB,UAAU,SAAM;IAChB,SAAS,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAM;IAE5E,oBAAoB,UAAQ;IAC5B,QAAQ,EAAE,mBAAmB,EAAE,CAIpC;IACK,WAAW,SAAK;IAGhB,gBAAgB,UAAS;IACzB,cAAc,UAAS;IACvB,aAAa,UAAS;IAE7B,gFAAgF;IAChF,IAAW,oBAAoB,IAAI,MAAM,CAExC;IAGM,WAAW,EAAE,YAAY,CAAU;IACnC,YAAY,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;KAAE,CAAC,CAAM;IAC/H,eAAe,SAAM;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEzC,IAAW,UAAU,IAAI,MAAM,CAM9B;IAED,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,GAAG,CAA6B;IAExC,OAAO,CAAC,QAAQ,CAAC,CAAe;IAEzB,QAAQ,IAAI,IAAI;IAahB,WAAW,IAAI,IAAI;IAInB,YAAY,IAAI,IAAI;IAQpB,iBAAiB,IAAI,IAAI;IAKzB,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAIpC,UAAU,IAAI,IAAI;IAIZ,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAW1B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxD;;;;;OAKG;IACU,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB9B,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiEvE,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,oBAAoB;YAkBd,iBAAiB;IA6B/B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,iBAAiB;yCA3ShB,sBAAsB;2CAAtB,sBAAsB;CAkTlC"}
|