gloomberb 0.7.1 → 0.7.3

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 (131) hide show
  1. package/README.md +79 -132
  2. package/package.json +1 -1
  3. package/src/app.tsx +73 -67
  4. package/src/components/chart/chart-data.ts +7 -2
  5. package/src/components/chart/chart-pane-settings.ts +7 -4
  6. package/src/components/chart/chart-renderer.ts +49 -15
  7. package/src/components/chart/chart-types.ts +2 -2
  8. package/src/components/chart/chart.test.ts +27 -8
  9. package/src/components/chart/comparison-stock-chart.tsx +84 -121
  10. package/src/components/chart/native/chart-rasterizer.ts +43 -17
  11. package/src/components/chart/price-axis-labels.tsx +25 -9
  12. package/src/components/chart/stock-chart-renderer-switch.test.tsx +1 -1
  13. package/src/components/chart/stock-chart.tsx +59 -41
  14. package/src/components/command-bar/command-bar.test.tsx +446 -120
  15. package/src/components/command-bar/command-bar.tsx +528 -383
  16. package/src/components/command-bar/command-registry.ts +16 -6
  17. package/src/components/command-bar/root-shortcuts.ts +4 -5
  18. package/src/components/command-bar/view-model.test.ts +1 -0
  19. package/src/components/command-bar/view-model.ts +2 -1
  20. package/src/components/command-bar/workflow-ops.ts +11 -32
  21. package/src/components/data-table-view.test.tsx +24 -1
  22. package/src/components/data-table-view.tsx +3 -2
  23. package/src/components/feed-data-table-stack-view.tsx +3 -2
  24. package/src/components/layout/detached-pane-shell.tsx +2 -2
  25. package/src/components/layout/floating-pane.tsx +9 -2
  26. package/src/components/layout/pane-footer.tsx +1 -1
  27. package/src/components/layout/pane-header.tsx +4 -2
  28. package/src/components/layout/pane-sizing.ts +12 -0
  29. package/src/components/layout/pane.tsx +24 -2
  30. package/src/components/layout/shell.tsx +24 -10
  31. package/src/components/layout/status-bar.test.tsx +4 -1
  32. package/src/components/layout/status-bar.tsx +4 -24
  33. package/src/components/markdown-text.tsx +53 -8
  34. package/src/components/onboarding/onboarding-wizard.tsx +7 -6
  35. package/src/components/pane-settings-dialog.tsx +9 -6
  36. package/src/components/pane-template-wizard.tsx +3 -2
  37. package/src/components/price-selector-dialog.tsx +3 -2
  38. package/src/components/price-sparkline-view.tsx +463 -0
  39. package/src/components/price-sparkline.ts +46 -0
  40. package/src/components/table-view-shared.tsx +47 -7
  41. package/src/components/ticker-list-table-view.tsx +3 -2
  42. package/src/components/ticker-list-table.tsx +11 -51
  43. package/src/components/toggle-list.tsx +3 -3
  44. package/src/components/ui/choice-dialog.tsx +3 -2
  45. package/src/components/ui/multi-select-dialog.tsx +3 -2
  46. package/src/components/ui/native-select.tsx +3 -3
  47. package/src/core/state/app-state.ts +210 -80
  48. package/src/data/config-store-node.ts +113 -25
  49. package/src/data/config-store.test.ts +65 -3
  50. package/src/data/plugin-state-store.ts +39 -13
  51. package/src/data/sqlite-cache.test.ts +13 -0
  52. package/src/market-data/coordinator.ts +8 -1
  53. package/src/news/aggregator.test.ts +41 -0
  54. package/src/news/aggregator.ts +61 -9
  55. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +25 -6
  56. package/src/plugins/builtin/ai/settings.ts +3 -0
  57. package/src/plugins/builtin/broker-manager/index.tsx +4 -3
  58. package/src/plugins/builtin/chat-controller.test.ts +398 -17
  59. package/src/plugins/builtin/chat-controller.ts +258 -44
  60. package/src/plugins/builtin/chat.test.tsx +257 -6
  61. package/src/plugins/builtin/chat.tsx +225 -60
  62. package/src/plugins/builtin/comparison-chart.test.tsx +117 -3
  63. package/src/plugins/builtin/correlation/colors.test.ts +39 -0
  64. package/src/plugins/builtin/correlation/colors.ts +87 -0
  65. package/src/plugins/builtin/correlation/index.test.tsx +151 -0
  66. package/src/plugins/builtin/correlation/index.tsx +92 -21
  67. package/src/plugins/builtin/debug.tsx +4 -3
  68. package/src/plugins/builtin/econ/index.tsx +3 -2
  69. package/src/plugins/builtin/help.test.tsx +2 -2
  70. package/src/plugins/builtin/holders/index.tsx +25 -13
  71. package/src/plugins/builtin/layout-manager.tsx +1 -7
  72. package/src/plugins/builtin/news-wire/index.tsx +1 -1
  73. package/src/plugins/builtin/news-wire/news-detail-view.tsx +3 -2
  74. package/src/plugins/builtin/news-wire/news-preset-pane.tsx +4 -1
  75. package/src/plugins/builtin/news-wire/persisted-articles.ts +7 -1
  76. package/src/plugins/builtin/options.tsx +9 -17
  77. package/src/plugins/builtin/portfolio-list/cli/commands.ts +6 -10
  78. package/src/plugins/builtin/portfolio-list/metrics.ts +11 -0
  79. package/src/plugins/builtin/portfolio-list/mutations.ts +17 -0
  80. package/src/plugins/builtin/portfolio-list/pane.tsx +117 -18
  81. package/src/plugins/builtin/portfolio-list/quick-add.tsx +422 -0
  82. package/src/plugins/builtin/portfolio-list/settings.ts +20 -12
  83. package/src/plugins/builtin/portfolio-list/table.tsx +3 -3
  84. package/src/plugins/builtin/portfolio-list.test.tsx +282 -2
  85. package/src/plugins/builtin/quote-monitor.test.tsx +158 -25
  86. package/src/plugins/builtin/sectors/index.tsx +252 -41
  87. package/src/plugins/builtin/sectors/sector-data.ts +41 -1
  88. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +3 -2
  89. package/src/plugins/builtin/ticker-detail/index.tsx +30 -9
  90. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +25 -15
  91. package/src/plugins/builtin/ticker-detail/pane.tsx +0 -6
  92. package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +442 -63
  93. package/src/plugins/builtin/ticker-detail/settings.ts +84 -3
  94. package/src/plugins/builtin/ticker-detail.test.tsx +2 -2
  95. package/src/plugins/ibkr/trading-pane.tsx +3 -2
  96. package/src/plugins/plugin-runtime.tsx +2 -2
  97. package/src/plugins/prediction-markets/settings.ts +4 -0
  98. package/src/plugins/registry.test.ts +55 -0
  99. package/src/plugins/registry.ts +11 -4
  100. package/src/plugins/ticker-navigation.test.ts +35 -5
  101. package/src/plugins/ticker-navigation.ts +16 -22
  102. package/src/renderers/electrobun/bun/application-menu-click.test.ts +9 -0
  103. package/src/renderers/electrobun/bun/application-menu-click.ts +7 -4
  104. package/src/renderers/electrobun/bun/application-menu.ts +6 -1
  105. package/src/renderers/electrobun/bun/desktop-workspace.ts +47 -29
  106. package/src/renderers/electrobun/bun/index.ts +52 -13
  107. package/src/renderers/electrobun/view/app-services.ts +51 -9
  108. package/src/renderers/electrobun/view/backend-rpc.ts +5 -8
  109. package/src/renderers/electrobun/view/desktop-controls.tsx +4 -4
  110. package/src/renderers/electrobun/view/main.tsx +0 -16
  111. package/src/renderers/electrobun/view/styles.css +23 -21
  112. package/src/renderers/electrobun/view/ui-host.tsx +67 -11
  113. package/src/renderers/opentui/host.tsx +2 -1
  114. package/src/renderers/opentui/start.tsx +1 -1
  115. package/src/renderers/opentui/test-utils.tsx +6 -5
  116. package/src/state/app-bootstrap.ts +3 -3
  117. package/src/state/app-context.test.ts +113 -0
  118. package/src/state/app-context.tsx +8 -4
  119. package/src/theme/colors.ts +13 -14
  120. package/src/types/config.ts +9 -4
  121. package/src/types/plugin.ts +8 -1
  122. package/src/ui/context-menu.tsx +4 -4
  123. package/src/ui/host.tsx +8 -2
  124. package/src/ui/index.tsx +1 -0
  125. package/src/utils/api-client.test.ts +128 -0
  126. package/src/utils/api-client.ts +112 -7
  127. package/src/utils/fuzzy-search.ts +12 -1
  128. package/src/utils/keyboard.ts +31 -0
  129. package/src/utils/ticker-search.test.ts +2 -2
  130. package/src/utils/ticker-search.ts +48 -1
  131. package/src/version.ts +1 -1
@@ -48,7 +48,7 @@ export interface ChatMessage {
48
48
  replyToId: string | null;
49
49
  createdAt: string;
50
50
  user: { id: string; username: string; displayName: string };
51
- replyTo?: { content: string; user: { username: string } } | null;
51
+ replyTo?: { content: string; user: { id?: string; username: string } } | null;
52
52
  clientStatus?: "sending" | "failed";
53
53
  clientError?: string | null;
54
54
  }
@@ -59,6 +59,29 @@ export interface ChatChannel {
59
59
  created_at: string;
60
60
  }
61
61
 
62
+ export interface ChatChannelState {
63
+ channelId: string;
64
+ notificationsEnabled: boolean;
65
+ lastReadMessageId: string | null;
66
+ unreadCount: number;
67
+ }
68
+
69
+ export interface ChatNotification {
70
+ id: string;
71
+ type: "reply" | "mention" | "channel";
72
+ channelId: string;
73
+ messageId: string;
74
+ createdAt: string;
75
+ message: ChatMessage;
76
+ }
77
+
78
+ export interface ChatStateResponse {
79
+ channels: ChatChannel[];
80
+ onlineCount: number;
81
+ channelStates: ChatChannelState[];
82
+ notifications: ChatNotification[];
83
+ }
84
+
62
85
  export interface AuthUser {
63
86
  id: string;
64
87
  name: string;
@@ -352,6 +375,21 @@ function normalizeChatMessages(messages: ChatMessage[]): ChatMessage[] {
352
375
  return messages.map((message) => normalizeChatMessage(message));
353
376
  }
354
377
 
378
+ function normalizeChatNotification(notification: ChatNotification): ChatNotification {
379
+ return {
380
+ ...notification,
381
+ createdAt: normalizeTimestamp(notification.createdAt),
382
+ message: normalizeChatMessage(notification.message),
383
+ };
384
+ }
385
+
386
+ function normalizeChatState(response: ChatStateResponse): ChatStateResponse {
387
+ return {
388
+ ...response,
389
+ notifications: response.notifications.map(normalizeChatNotification),
390
+ };
391
+ }
392
+
355
393
  function normalizeTweet(tweet: CloudTweetPayload): CloudTweetPayload {
356
394
  return {
357
395
  ...tweet,
@@ -370,6 +408,8 @@ function normalizeTweetSearchResponse(response: CloudTweetSearchResponse): Cloud
370
408
  }
371
409
 
372
410
  type ChannelListener = (message: ChatMessage) => void;
411
+ type ChatNotificationListener = (notification: ChatNotification) => void;
412
+ type ChatPresenceListener = (onlineCount: number) => void;
373
413
  type QuoteListener = (target: QuoteStreamTarget, quote: CloudQuotePayload) => void;
374
414
  type SessionCookieName = (typeof SESSION_COOKIE_NAMES)[number];
375
415
 
@@ -408,6 +448,8 @@ class GloomApiClient {
408
448
  private reconnectDelayMs = 1000;
409
449
 
410
450
  private readonly channelListeners = new Map<string, Set<ChannelListener>>();
451
+ private readonly chatNotificationListeners = new Set<ChatNotificationListener>();
452
+ private readonly chatPresenceListeners = new Set<ChatPresenceListener>();
411
453
  private readonly quoteListeners = new Map<string, Set<QuoteListener>>();
412
454
  private readonly quoteTargets = new Map<string, QuoteStreamTarget>();
413
455
  private readonly pendingQuoteSubscribes = new Map<string, QuoteStreamTarget>();
@@ -650,7 +692,12 @@ class GloomApiClient {
650
692
  if (this.ws?.readyState !== WebSocket.OPEN) return;
651
693
  if (payload && typeof payload === "object" && "type" in (payload as Record<string, unknown>)) {
652
694
  const type = (payload as Record<string, unknown>).type;
653
- if (type === "market.subscribe" || type === "market.unsubscribe" || type === "chat.subscribe") {
695
+ if (
696
+ type === "market.subscribe"
697
+ || type === "market.unsubscribe"
698
+ || type === "chat.subscribe"
699
+ || type === "chat.unsubscribe"
700
+ ) {
654
701
  cloudApiLog.info("send websocket message", payload);
655
702
  }
656
703
  }
@@ -773,6 +820,21 @@ class GloomApiClient {
773
820
  return;
774
821
  }
775
822
 
823
+ if (parsed?.type === "chat.notification" && parsed.data) {
824
+ const notification = normalizeChatNotification(parsed.data as ChatNotification);
825
+ for (const listener of this.chatNotificationListeners) {
826
+ listener(notification);
827
+ }
828
+ return;
829
+ }
830
+
831
+ if (parsed?.type === "chat.presence" && typeof parsed.onlineCount === "number") {
832
+ for (const listener of this.chatPresenceListeners) {
833
+ listener(parsed.onlineCount);
834
+ }
835
+ return;
836
+ }
837
+
776
838
  if (parsed?.type === "market.quote" && parsed.quote && typeof parsed.symbol === "string") {
777
839
  const key = marketKey(parsed.symbol, parsed.exchange);
778
840
  const target = this.quoteTargets.get(key) ?? {
@@ -853,6 +915,32 @@ class GloomApiClient {
853
915
  return this.request<ChatChannel[]>("/chat/channels");
854
916
  }
855
917
 
918
+ async getChatPresence(): Promise<{ onlineCount: number }> {
919
+ return this.request<{ onlineCount: number }>("/chat/presence");
920
+ }
921
+
922
+ async getChatState(): Promise<ChatStateResponse> {
923
+ const state = await this.request<ChatStateResponse>("/chat/state");
924
+ return normalizeChatState(state);
925
+ }
926
+
927
+ async updateChatChannelState(
928
+ channelId: string,
929
+ body: { notificationsEnabled?: boolean; readThroughMessageId?: string },
930
+ ): Promise<ChatChannelState> {
931
+ return this.request<ChatChannelState>(`/chat/channels/${channelId}/state`, {
932
+ method: "PATCH",
933
+ body: JSON.stringify(body),
934
+ });
935
+ }
936
+
937
+ async markChatNotificationsDelivered(notificationIds: string[]): Promise<{ delivered: number }> {
938
+ return this.request<{ delivered: number }>("/chat/notifications/delivered", {
939
+ method: "POST",
940
+ body: JSON.stringify({ notificationIds }),
941
+ });
942
+ }
943
+
856
944
  async getMessages(
857
945
  channelId: string,
858
946
  opts?: { after?: string; before?: string; limit?: number },
@@ -866,10 +954,10 @@ class GloomApiClient {
866
954
  return normalizeChatMessages(messages);
867
955
  }
868
956
 
869
- async sendMessage(channelId: string, content: string, replyToId?: string): Promise<ChatMessage> {
957
+ async sendMessage(channelId: string, content: string, replyToId?: string, clientMessageId?: string): Promise<ChatMessage> {
870
958
  const message = await this.request<ChatMessage>(`/chat/channels/${channelId}/messages`, {
871
959
  method: "POST",
872
- body: JSON.stringify({ content, replyToId }),
960
+ body: JSON.stringify({ content, replyToId, clientMessageId }),
873
961
  });
874
962
  return normalizeChatMessage(message);
875
963
  }
@@ -878,7 +966,7 @@ class GloomApiClient {
878
966
  channelId: string,
879
967
  onMessage: (msg: ChatMessage) => void,
880
968
  onError?: (err: string) => void,
881
- ): { send: (content: string, replyToId?: string) => Promise<ChatMessage>; close: () => void } {
969
+ ): { send: (content: string, replyToId?: string, clientMessageId?: string) => Promise<ChatMessage>; close: () => void } {
882
970
  if (!channelId) {
883
971
  return {
884
972
  send: async () => {
@@ -898,9 +986,9 @@ class GloomApiClient {
898
986
  }
899
987
 
900
988
  return {
901
- send: async (content: string, replyToId?: string) => {
989
+ send: async (content: string, replyToId?: string, clientMessageId?: string) => {
902
990
  try {
903
- const message = await this.sendMessage(channelId, content, replyToId);
991
+ const message = await this.sendMessage(channelId, content, replyToId, clientMessageId);
904
992
  onMessage(message);
905
993
  return message;
906
994
  } catch (error) {
@@ -914,6 +1002,7 @@ class GloomApiClient {
914
1002
  current.delete(onMessage);
915
1003
  if (current.size === 0) {
916
1004
  this.channelListeners.delete(channelId);
1005
+ this.sendSocketMessage({ type: "chat.unsubscribe", channelId });
917
1006
  }
918
1007
  if (!this.shouldKeepSocketOpen()) {
919
1008
  this.teardownSocket();
@@ -922,6 +1011,20 @@ class GloomApiClient {
922
1011
  };
923
1012
  }
924
1013
 
1014
+ subscribeChatNotifications(listener: ChatNotificationListener): () => void {
1015
+ this.chatNotificationListeners.add(listener);
1016
+ return () => {
1017
+ this.chatNotificationListeners.delete(listener);
1018
+ };
1019
+ }
1020
+
1021
+ subscribeChatPresence(listener: ChatPresenceListener): () => void {
1022
+ this.chatPresenceListeners.add(listener);
1023
+ return () => {
1024
+ this.chatPresenceListeners.delete(listener);
1025
+ };
1026
+ }
1027
+
925
1028
  subscribeQuotes(
926
1029
  targets: QuoteStreamTarget[],
927
1030
  onQuote: (target: QuoteStreamTarget, quote: CloudQuotePayload) => void,
@@ -997,6 +1100,8 @@ class GloomApiClient {
997
1100
  channelTargets: this.channelListeners.size,
998
1101
  });
999
1102
  this.channelListeners.clear();
1103
+ this.chatNotificationListeners.clear();
1104
+ this.chatPresenceListeners.clear();
1000
1105
  this.quoteListeners.clear();
1001
1106
  this.quoteTargets.clear();
1002
1107
  this.pendingQuoteSubscribes.clear();
@@ -5,7 +5,18 @@ export function fuzzyMatch(query: string, target: string): { match: boolean; sco
5
5
  const t = target.toLowerCase();
6
6
 
7
7
  if (q.length === 0) return { match: true, score: 0 };
8
- if (t.startsWith(q)) return { match: true, score: 1000 - q.length }; // exact prefix match
8
+ if (t === q) return { match: true, score: 2500 - q.length };
9
+
10
+ const tokens = t.split(/\s+/).filter(Boolean);
11
+ const exactTokenIndex = tokens.findIndex((token) => token === q);
12
+ if (exactTokenIndex >= 0) return { match: true, score: 2200 - q.length - exactTokenIndex };
13
+
14
+ const prefixTokenIndex = tokens.findIndex((token) => token.startsWith(q));
15
+ if (prefixTokenIndex >= 0) {
16
+ const token = tokens[prefixTokenIndex]!;
17
+ return { match: true, score: 1000 - q.length - prefixTokenIndex - (token.length - q.length) };
18
+ }
19
+
9
20
  if (t.includes(q)) return { match: true, score: 500 - q.length }; // substring match
10
21
 
11
22
  let qi = 0;
@@ -0,0 +1,31 @@
1
+ export interface KeyboardModifierEventLike {
2
+ ctrl?: boolean;
3
+ meta?: boolean;
4
+ super?: boolean;
5
+ alt?: boolean;
6
+ option?: boolean;
7
+ shift?: boolean;
8
+ name?: string;
9
+ }
10
+
11
+ export function hasKeyboardModifier(event: KeyboardModifierEventLike): boolean {
12
+ return !!(
13
+ event.ctrl
14
+ || event.meta
15
+ || event.super
16
+ || event.alt
17
+ || event.option
18
+ || event.shift
19
+ );
20
+ }
21
+
22
+ export function isPlainKeyboardEvent(event: KeyboardModifierEventLike): boolean {
23
+ return !hasKeyboardModifier(event);
24
+ }
25
+
26
+ export function isPlainKey(
27
+ event: KeyboardModifierEventLike,
28
+ ...names: string[]
29
+ ): boolean {
30
+ return isPlainKeyboardEvent(event) && names.includes(event.name ?? "");
31
+ }
@@ -82,7 +82,7 @@ describe("ticker-search utilities", () => {
82
82
  ]),
83
83
  });
84
84
 
85
- expect(results.map((item) => item.id)).toEqual(["goto:AAPL", "search:APP"]);
85
+ expect(results.map((item) => item.id)).toEqual(["goto:AAPL", "search:APP:NASDAQ:TEST"]);
86
86
  expect(findExactTickerSearchMatch(results, "AAPL")?.id).toBe("goto:AAPL");
87
87
  });
88
88
 
@@ -97,7 +97,7 @@ describe("ticker-search utilities", () => {
97
97
  ]),
98
98
  });
99
99
 
100
- expect(results.map((item) => item.id)).toEqual(["goto:AAPL", "search:APLE"]);
100
+ expect(results.map((item) => item.id)).toEqual(["goto:AAPL", "search:APLE:NASDAQ:TEST"]);
101
101
  expect(results[0]).toMatchObject({
102
102
  id: "goto:AAPL",
103
103
  detail: "Apple Inc",
@@ -140,7 +140,7 @@ export function createProviderTickerSearchCandidates(
140
140
  const symbol = getSearchResultSymbol(result);
141
141
  const saved = localTickers.has(symbol);
142
142
  return [{
143
- id: `search:${result.symbol}`,
143
+ id: buildProviderCandidateId(result, symbol),
144
144
  label: symbol,
145
145
  symbol,
146
146
  detail: [result.name, result.brokerLabel, result.type].filter(Boolean).join(" | "),
@@ -357,6 +357,7 @@ export function rankTickerSearchItems<T extends Pick<TickerSearchRankableItem, "
357
357
  item,
358
358
  index,
359
359
  normalizedSymbol: normalizeSearchText(item.symbol || item.label),
360
+ symbolMatchRank: scoreSymbolMatchRank(intent, item),
360
361
  textScore,
361
362
  score: textScore + priorityScore + (textScore > 0 && saved ? 900 : 0),
362
363
  };
@@ -375,6 +376,7 @@ export function rankTickerSearchItems<T extends Pick<TickerSearchRankableItem, "
375
376
  return !matchedLocalSymbols.has(normalizedSymbol);
376
377
  })
377
378
  .sort((a, b) => {
379
+ if (b.symbolMatchRank !== a.symbolMatchRank) return b.symbolMatchRank - a.symbolMatchRank;
378
380
  if (b.score !== a.score) return b.score - a.score;
379
381
  const aSaved = isSavedSearchItem(a.item);
380
382
  const bSaved = isSavedSearchItem(b.item);
@@ -463,6 +465,16 @@ function buildProviderSearchResultKey(result: InstrumentSearchResult): string {
463
465
  ].join("|");
464
466
  }
465
467
 
468
+ function buildProviderCandidateId(result: InstrumentSearchResult, symbol: string): string {
469
+ return [
470
+ "search",
471
+ normalizeTickerSymbol(symbol),
472
+ normalizeSearchText(result.exchange || result.primaryExchange || result.type || ""),
473
+ normalizeSearchText(result.currency || ""),
474
+ normalizeSearchText(result.providerId || ""),
475
+ ].filter(Boolean).join(":");
476
+ }
477
+
466
478
  function getProviderHintRichness(result: InstrumentSearchResult): number {
467
479
  let score = 0;
468
480
  if (result.name) score += Math.min(120, result.name.length);
@@ -728,6 +740,41 @@ function scoreSearchAlias(intent: SearchQueryIntent, alias: string): number {
728
740
  return score;
729
741
  }
730
742
 
743
+ function scoreSymbolMatchRank(
744
+ intent: SearchQueryIntent,
745
+ item: Pick<TickerSearchRankableItem, "label"> & Partial<TickerSearchRankableItem>,
746
+ ): number {
747
+ if (!intent.normalizedQuery && !intent.compactQuery) return 0;
748
+ const displaySymbol = normalizeSearchText(item.symbol || item.label);
749
+ const compactDisplaySymbol = compactSearchText(item.symbol || item.label);
750
+
751
+ if (
752
+ displaySymbol === intent.normalizedQuery
753
+ || (intent.compactQuery && compactDisplaySymbol === intent.compactQuery)
754
+ ) {
755
+ return 3;
756
+ }
757
+
758
+ const aliases = getItemSearchAliases(item);
759
+ if (aliases.some((alias) => {
760
+ const normalizedAlias = normalizeSearchText(alias);
761
+ const compactAlias = compactSearchText(alias);
762
+ return normalizedAlias === intent.normalizedQuery
763
+ || (intent.compactQuery && compactAlias === intent.compactQuery);
764
+ })) {
765
+ return 2;
766
+ }
767
+
768
+ if (
769
+ displaySymbol.startsWith(intent.normalizedQuery)
770
+ || (intent.compactQuery && compactDisplaySymbol.startsWith(intent.compactQuery))
771
+ ) {
772
+ return 1;
773
+ }
774
+
775
+ return 0;
776
+ }
777
+
731
778
  function scoreAssetPreference(intent: SearchQueryIntent, instrumentClass?: TickerSearchInstrumentClass): number {
732
779
  const itemClass = instrumentClass || "other";
733
780
  if (!intent.assetPreference) {
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.7.1";
1
+ export const VERSION = "0.7.3";