gloomberb 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +106 -54
  2. package/package.json +14 -13
  3. package/patches/{@opentui%2Fcore@0.1.90.patch → @opentui%2Fcore@0.3.2.patch} +175 -90
  4. package/src/api-client/chat.ts +8 -0
  5. package/src/api-client/index.test.ts +30 -0
  6. package/src/api-client/index.ts +4 -0
  7. package/src/api-client/normalizers.ts +1 -0
  8. package/src/api-client/types.ts +1 -0
  9. package/src/app/global-shortcuts.test.tsx +54 -4
  10. package/src/app/global-shortcuts.ts +11 -5
  11. package/src/capabilities/factories.ts +4 -0
  12. package/src/components/chart/comparison/data.test.ts +54 -0
  13. package/src/components/chart/comparison/data.ts +47 -24
  14. package/src/components/chart/core/projection.ts +51 -27
  15. package/src/components/command-bar/action-runtime.ts +1 -0
  16. package/src/components/command-bar/layout-items/current-layout.test.ts +115 -0
  17. package/src/components/command-bar/layout-items/current-layout.ts +33 -0
  18. package/src/components/command-bar/list/view.tsx +3 -1
  19. package/src/components/command-bar/pane-templates/items.ts +4 -1
  20. package/src/components/command-bar/panel/body.tsx +1 -0
  21. package/src/components/command-bar/routes/root/results.ts +0 -30
  22. package/src/components/command-bar/routes/root/runtime.ts +2 -20
  23. package/src/components/command-bar/routes/ticker-search/actions.ts +42 -4
  24. package/src/components/command-bar/routes/ticker-search/route.ts +4 -1
  25. package/src/components/command-bar/routing/launch-request.ts +14 -7
  26. package/src/components/command-bar/routing/list-state.ts +1 -3
  27. package/src/components/command-bar/surface/index.test.tsx +76 -4
  28. package/src/components/command-bar/surface/index.tsx +0 -2
  29. package/src/components/command-bar/surface/pane.test.tsx +1 -0
  30. package/src/components/command-bar/surface/test-harness.tsx +1 -0
  31. package/src/components/command-bar/view-model.ts +3 -3
  32. package/src/components/data-table/stack-view.test.tsx +14 -11
  33. package/src/components/data-table/stack-view.tsx +1 -2
  34. package/src/components/data-table/view.test.tsx +24 -18
  35. package/src/components/data-table/view.tsx +313 -83
  36. package/src/components/feed-data-table/stack-view.tsx +6 -8
  37. package/src/components/layout/pane/footer/index.test.tsx +6 -0
  38. package/src/components/layout/pane/footer/index.tsx +14 -2
  39. package/src/components/layout/pane/title.ts +5 -1
  40. package/src/components/layout/shell/index.test.tsx +120 -2
  41. package/src/components/layout/shell/index.tsx +2 -0
  42. package/src/components/layout/shell/menu.ts +1 -7
  43. package/src/components/layout/shell/pane/actions.ts +8 -0
  44. package/src/components/layout/shell/pane/management-shortcuts.ts +12 -2
  45. package/src/components/layout/shell/shortcuts.ts +14 -6
  46. package/src/components/layout/shell/window-mode/index.ts +8 -3
  47. package/src/components/layout/window-edit/presentation.ts +4 -4
  48. package/src/components/onboarding/onboarding-steps.tsx +3 -2
  49. package/src/components/pane-settings-dialog/field-dialogs.tsx +0 -2
  50. package/src/components/pane-settings-dialog/tui.tsx +1 -1
  51. package/src/components/ticker/list-table-view.tsx +17 -25
  52. package/src/components/ui/choice-dialog.tsx +12 -2
  53. package/src/components/ui/multi-select/dialog.tsx +17 -5
  54. package/src/components/ui/remote-image.tsx +1 -14
  55. package/src/components/ui/shortcut-hint.tsx +14 -2
  56. package/src/components/ui/table-layout.test.ts +2 -2
  57. package/src/components/ui/table-layout.ts +8 -2
  58. package/src/components/ui/tabs.tsx +115 -86
  59. package/src/components/ui/ui.test.tsx +83 -4
  60. package/src/core/state/app/types.ts +12 -4
  61. package/src/market-data/coordinator/auxiliary.ts +29 -1
  62. package/src/market-data/coordinator/index.ts +17 -1
  63. package/src/market-data/hooks.tsx +17 -1
  64. package/src/market-data/selectors.ts +4 -0
  65. package/src/plugins/builtin/account-management/form-components.tsx +0 -4
  66. package/src/plugins/builtin/account-management/model.test.ts +54 -0
  67. package/src/plugins/builtin/account-management/model.ts +13 -3
  68. package/src/plugins/builtin/account-management/pane.tsx +5 -11
  69. package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
  70. package/src/plugins/builtin/alerts/pane.tsx +5 -3
  71. package/src/plugins/builtin/analytics/index.tsx +0 -3
  72. package/src/plugins/builtin/analytics/view.tsx +6 -5
  73. package/src/plugins/builtin/broker-manager/index.tsx +6 -6
  74. package/src/plugins/builtin/buildout/pane/index.tsx +6 -6
  75. package/src/plugins/builtin/changelog/index.tsx +40 -27
  76. package/src/plugins/builtin/changelog/model.test.ts +77 -0
  77. package/src/plugins/builtin/changelog/model.ts +98 -0
  78. package/src/plugins/builtin/chat/channel-labels.ts +53 -0
  79. package/src/plugins/builtin/chat/channels.test.ts +28 -0
  80. package/src/plugins/builtin/chat/channels.ts +36 -19
  81. package/src/plugins/builtin/chat/chat.test.tsx +264 -5
  82. package/src/plugins/builtin/chat/content/channel-navigation.ts +50 -11
  83. package/src/plugins/builtin/chat/content/composer-runtime.test.tsx +144 -0
  84. package/src/plugins/builtin/chat/content/composer-runtime.ts +151 -20
  85. package/src/plugins/builtin/chat/content/composer.tsx +23 -2
  86. package/src/plugins/builtin/chat/content/index.tsx +66 -4
  87. package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -1
  88. package/src/plugins/builtin/chat/content/shortcuts.ts +27 -1
  89. package/src/plugins/builtin/chat/content/transcript.tsx +8 -0
  90. package/src/plugins/builtin/chat/content/types.ts +1 -0
  91. package/src/plugins/builtin/chat/controller/index.ts +41 -0
  92. package/src/plugins/builtin/chat/controller/messages.ts +18 -0
  93. package/src/plugins/builtin/chat/controller/notifications.ts +10 -4
  94. package/src/plugins/builtin/chat/controller/utils.ts +6 -3
  95. package/src/plugins/builtin/chat/controller/view.ts +1 -1
  96. package/src/plugins/builtin/chat/controller.test.ts +189 -0
  97. package/src/plugins/builtin/chat/edit-window.ts +24 -0
  98. package/src/plugins/builtin/chat/layout.ts +94 -31
  99. package/src/plugins/builtin/chat/message/action-chip.tsx +1 -0
  100. package/src/plugins/builtin/chat/message/desktop.tsx +53 -20
  101. package/src/plugins/builtin/chat/message/inline-tokens.tsx +16 -10
  102. package/src/plugins/builtin/chat/message/render-state.ts +5 -1
  103. package/src/plugins/builtin/chat/message/terminal.tsx +49 -20
  104. package/src/plugins/builtin/chat/message/types.ts +2 -0
  105. package/src/plugins/builtin/chat/pane.tsx +35 -7
  106. package/src/plugins/builtin/chat/sidebar.test.tsx +72 -0
  107. package/src/plugins/builtin/chat/status-widget.tsx +29 -11
  108. package/src/plugins/builtin/chat/test-harness.tsx +2 -0
  109. package/src/plugins/builtin/cloud/plugin.tsx +3 -0
  110. package/src/plugins/builtin/cloud-tweets/table.tsx +7 -11
  111. package/src/plugins/builtin/congress-trades/pane.tsx +14 -22
  112. package/src/plugins/builtin/earnings/index.tsx +8 -7
  113. package/src/plugins/builtin/econ/index.tsx +6 -8
  114. package/src/plugins/builtin/help/components.tsx +1 -1
  115. package/src/plugins/builtin/help/index.test.tsx +105 -30
  116. package/src/plugins/builtin/help/index.tsx +326 -164
  117. package/src/plugins/builtin/help/shortcut-model.ts +5 -6
  118. package/src/plugins/builtin/holders/pane.tsx +7 -6
  119. package/src/plugins/builtin/market-movers/index.tsx +6 -3
  120. package/src/plugins/builtin/news/wire/news/table.tsx +7 -6
  121. package/src/plugins/builtin/options/view.tsx +16 -8
  122. package/src/plugins/builtin/portfolio-list/column-values.ts +304 -1
  123. package/src/plugins/builtin/portfolio-list/metrics.test.ts +93 -0
  124. package/src/plugins/builtin/portfolio-list/pane/data.ts +7 -0
  125. package/src/plugins/builtin/portfolio-list/pane/index.tsx +25 -2
  126. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +281 -0
  127. package/src/plugins/builtin/portfolio-list/settings.ts +27 -0
  128. package/src/plugins/builtin/portfolio-list/table.tsx +4 -2
  129. package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +10 -68
  130. package/src/plugins/builtin/research/analyst-pane.tsx +1 -2
  131. package/src/plugins/builtin/research/corporate-actions-pane.tsx +358 -7
  132. package/src/plugins/builtin/research/index.test.ts +30 -1
  133. package/src/plugins/builtin/research/relative-valuation-pane.tsx +6 -8
  134. package/src/plugins/builtin/sec/filing-documents.ts +66 -0
  135. package/src/plugins/builtin/sec/index.tsx +132 -20
  136. package/src/plugins/builtin/sectors/index.tsx +13 -23
  137. package/src/plugins/builtin/shared/ticker-request.ts +1 -0
  138. package/src/plugins/builtin/substack/api/auth.ts +185 -0
  139. package/src/plugins/builtin/substack/api/cache.ts +78 -0
  140. package/src/plugins/builtin/substack/api/loaders.ts +146 -0
  141. package/src/plugins/builtin/substack/api/store.ts +186 -0
  142. package/src/plugins/builtin/substack/api/types.ts +40 -0
  143. package/src/plugins/builtin/substack/api.test.ts +298 -0
  144. package/src/plugins/builtin/substack/article-detail.tsx +359 -0
  145. package/src/plugins/builtin/substack/article-stack.tsx +129 -0
  146. package/src/plugins/builtin/substack/content.ts +478 -0
  147. package/src/plugins/builtin/substack/feed-tabs.tsx +41 -0
  148. package/src/plugins/builtin/substack/index.tsx +49 -0
  149. package/src/plugins/builtin/substack/login-view.tsx +126 -0
  150. package/src/plugins/builtin/substack/model.test.ts +155 -0
  151. package/src/plugins/builtin/substack/normalize.ts +343 -0
  152. package/src/plugins/builtin/substack/pane-footer.ts +71 -0
  153. package/src/plugins/builtin/substack/pane-state.ts +170 -0
  154. package/src/plugins/builtin/substack/pane.tsx +500 -0
  155. package/src/plugins/builtin/substack/table.ts +111 -0
  156. package/src/plugins/builtin/substack/types.ts +71 -0
  157. package/src/plugins/builtin/substack/utils.ts +131 -0
  158. package/src/plugins/builtin/thirteenf/pane.tsx +26 -25
  159. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +5 -4
  160. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +5 -4
  161. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +6 -5
  162. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +10 -8
  163. package/src/plugins/builtin/ticker-detail/index.test.tsx +16 -2
  164. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  165. package/src/plugins/builtin/ticker-detail/pane.tsx +13 -1
  166. package/src/plugins/builtin/world-indices/index.tsx +9 -6
  167. package/src/plugins/catalog-ui.ts +2 -0
  168. package/src/plugins/pane-manager/layout-state.ts +11 -0
  169. package/src/plugins/pane-manager.ts +1 -1
  170. package/src/plugins/prediction-markets/pane.tsx +10 -9
  171. package/src/plugins/registry/index.ts +3 -0
  172. package/src/plugins/runtime/context.tsx +1 -0
  173. package/src/plugins/runtime/index.tsx +4 -0
  174. package/src/react/use-throttled-commit-value.ts +116 -0
  175. package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +42 -0
  176. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +5 -0
  177. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
  178. package/src/renderers/electrobun/view/data-table/index.tsx +3 -2
  179. package/src/renderers/electrobun/view/data-table/row.tsx +2 -2
  180. package/src/renderers/electrobun/view/host/input.tsx +23 -2
  181. package/src/renderers/electrobun/view/http-fetch.ts +10 -3
  182. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
  183. package/src/sources/gloomberb-cloud/index.ts +4 -0
  184. package/src/sources/provider-router/cache.ts +1 -0
  185. package/src/sources/provider-router/document-routes.ts +45 -0
  186. package/src/sources/provider-router/index.ts +4 -0
  187. package/src/sources/sec-edgar/content.ts +9 -3
  188. package/src/sources/sec-edgar.test.ts +108 -1
  189. package/src/sources/sec-edgar.ts +105 -1
  190. package/src/sources/yahoo-finance.ts +4 -0
  191. package/src/state/app/context/index.test.tsx +16 -0
  192. package/src/test-support/plugin-runtime.ts +1 -0
  193. package/src/tickers/search/index.ts +1 -0
  194. package/src/types/data-provider.ts +11 -0
  195. package/src/ui/host.tsx +1 -0
  196. package/src/version.ts +1 -1
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test";
2
2
  import {
3
3
  SecEdgarClient,
4
4
  extractFilingContent,
5
+ parseFilingDocuments,
5
6
  parseRecentFilings,
6
7
  parseTickerLookup,
7
8
  } from "./sec-edgar";
@@ -65,6 +66,59 @@ describe("parseRecentFilings", () => {
65
66
  });
66
67
  });
67
68
 
69
+ describe("parseFilingDocuments", () => {
70
+ test("maps filing index document rows into primary and exhibit documents", () => {
71
+ const documents = parseFilingDocuments(`
72
+ <html>
73
+ <body>
74
+ <table class="tableFile" summary="Document Format Files">
75
+ <tr>
76
+ <th>Seq</th><th>Description</th><th>Document</th><th>Type</th><th>Size</th>
77
+ </tr>
78
+ <tr>
79
+ <td>1</td>
80
+ <td>Current report</td>
81
+ <td><a href="/ixviewer/doc/action?doc=/Archives/edgar/data/320193/000032019324000123/aapl-8k.htm">aapl-8k.htm</a></td>
82
+ <td>8-K</td>
83
+ <td>10000</td>
84
+ </tr>
85
+ <tr>
86
+ <td>2</td>
87
+ <td>Results of Operations and Financial Condition</td>
88
+ <td><a href="/Archives/edgar/data/320193/000032019324000123/aapl-ex991.htm">aapl-ex991.htm</a></td>
89
+ <td>EX-99.1</td>
90
+ <td>42000</td>
91
+ </tr>
92
+ </table>
93
+ </body>
94
+ </html>
95
+ `, {
96
+ accessionNumber: "0000320193-24-000123",
97
+ form: "8-K",
98
+ filingDate: new Date("2024-08-01T00:00:00Z"),
99
+ cik: "0000320193",
100
+ filingUrl: "https://www.sec.gov/Archives/edgar/data/320193/0000320193-24-000123-index.htm",
101
+ primaryDocument: "aapl-8k.htm",
102
+ primaryDocumentUrl: "https://www.sec.gov/Archives/edgar/data/320193/000032019324000123/aapl-8k.htm",
103
+ });
104
+
105
+ expect(documents).toMatchObject([
106
+ {
107
+ type: "8-K",
108
+ document: "aapl-8k.htm",
109
+ isPrimary: true,
110
+ url: "https://www.sec.gov/Archives/edgar/data/320193/000032019324000123/aapl-8k.htm",
111
+ },
112
+ {
113
+ type: "EX-99.1",
114
+ description: "Results of Operations and Financial Condition",
115
+ document: "aapl-ex991.htm",
116
+ isPrimary: false,
117
+ },
118
+ ]);
119
+ });
120
+ });
121
+
68
122
  describe("SecEdgarClient", () => {
69
123
  test("extracts readable text from filing html", () => {
70
124
  const content = extractFilingContent(`
@@ -81,6 +135,30 @@ describe("SecEdgarClient", () => {
81
135
  expect(content).toContain("Revenue increased 12% year over year.");
82
136
  });
83
137
 
138
+ test("trims SEC document wrapper metadata before exhibit content", () => {
139
+ const content = extractFilingContent(`
140
+ <SEC-DOCUMENT>
141
+ <DOCUMENT>
142
+ <TYPE>EX-99.1
143
+ <SEQUENCE>2
144
+ <FILENAME>q42026er-991.htm
145
+ <DESCRIPTION>EX-99.1
146
+ <TEXT>
147
+ <html><body>
148
+ <p>Exhibit 99.1</p>
149
+ <h1>e.l.f. Beauty Announces Fourth Quarter Fiscal 2026 Results</h1>
150
+ <p>Delivered fiscal 2026 net sales growth of 25% year over year.</p>
151
+ </body></html>
152
+ </TEXT>
153
+ </DOCUMENT>
154
+ </SEC-DOCUMENT>
155
+ `, "text/html", { form: "EX-99.1" });
156
+
157
+ expect(content?.startsWith("Exhibit 99.1")).toBe(true);
158
+ expect(content).toContain("Fourth Quarter Fiscal 2026 Results");
159
+ expect(content).not.toContain("q42026er-991.htm");
160
+ });
161
+
84
162
  test("drops hidden inline xbrl boilerplate from filing html", () => {
85
163
  const content = extractFilingContent(`
86
164
  <?xml version="1.0"?>
@@ -119,7 +197,7 @@ describe("SecEdgarClient", () => {
119
197
  sourceUrl: "https://www.sec.gov/Archives/edgar/data/2488/example.pdf",
120
198
  });
121
199
 
122
- expect(content).toContain("primary document is a PDF");
200
+ expect(content).toContain("document is a PDF");
123
201
  expect(content).not.toContain("%PDF-1.6");
124
202
  });
125
203
 
@@ -283,4 +361,33 @@ describe("SecEdgarClient", () => {
283
361
  expect(content).toContain("Proxy Statement");
284
362
  expect(content).toContain("Annual meeting details.");
285
363
  });
364
+
365
+ test("loads filing documents from the filing index", async () => {
366
+ globalThis.fetch = (async () => new Response(`
367
+ <html>
368
+ <body>
369
+ <table class="tableFile">
370
+ <tr><td>1</td><td>Current report</td><td><a href="/Archives/edgar/data/320193/000032019324000123/aapl-8k.htm">aapl-8k.htm</a></td><td>8-K</td><td>10000</td></tr>
371
+ <tr><td>2</td><td>Investor presentation</td><td><a href="/Archives/edgar/data/320193/000032019324000123/aapl-ex992.pdf">aapl-ex992.pdf</a></td><td>EX-99.2</td><td>90000</td></tr>
372
+ </table>
373
+ </body>
374
+ </html>
375
+ `, {
376
+ status: 200,
377
+ headers: { "content-type": "text/html" },
378
+ })) as unknown as typeof fetch;
379
+
380
+ const client = new SecEdgarClient();
381
+ const documents = await client.getFilingDocuments({
382
+ accessionNumber: "0000320193-24-000123",
383
+ form: "8-K",
384
+ filingDate: new Date("2024-08-01T00:00:00Z"),
385
+ cik: "0000320193",
386
+ filingUrl: "https://www.sec.gov/Archives/edgar/data/320193/0000320193-24-000123-index.htm",
387
+ primaryDocument: "aapl-8k.htm",
388
+ });
389
+
390
+ expect(documents.map((document) => document.type)).toEqual(["8-K", "EX-99.2"]);
391
+ expect(documents[1]?.description).toBe("Investor presentation");
392
+ });
286
393
  });
@@ -1,4 +1,4 @@
1
- import type { SecFilingItem } from "../types/data-provider";
1
+ import type { SecFilingDocument, SecFilingItem } from "../types/data-provider";
2
2
  import { truncateWithEllipsis } from "../utils/text-wrap";
3
3
  import {
4
4
  PDF_FALLBACK_MESSAGE,
@@ -84,6 +84,56 @@ function stripAccessionDashes(accessionNumber: string): string {
84
84
  return accessionNumber.replace(/-/g, "");
85
85
  }
86
86
 
87
+ function documentNameFromUrl(url: string): string {
88
+ try {
89
+ return decodeURIComponent(new URL(url).pathname.split("/").pop() ?? "");
90
+ } catch {
91
+ return url.split("/").pop() ?? url;
92
+ }
93
+ }
94
+
95
+ function normalizeDocumentName(value: string | undefined): string {
96
+ return (value ?? "").trim().toLowerCase();
97
+ }
98
+
99
+ function decodeSecHtmlEntities(text: string): string {
100
+ return text
101
+ .replace(/&nbsp;/gi, " ")
102
+ .replace(/&amp;/gi, "&")
103
+ .replace(/&lt;/gi, "<")
104
+ .replace(/&gt;/gi, ">")
105
+ .replace(/&quot;/gi, "\"")
106
+ .replace(/&#39;|&#x27;/gi, "'")
107
+ .replace(/&#(\d+);/g, (_match, digits: string) => String.fromCodePoint(Number(digits)))
108
+ .replace(/&#x([0-9a-f]+);/gi, (_match, hex: string) => String.fromCodePoint(parseInt(hex, 16)));
109
+ }
110
+
111
+ function cleanSecTableText(value: string): string {
112
+ return decodeSecHtmlEntities(
113
+ value
114
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
115
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
116
+ .replace(/<[^>]+>/g, " ")
117
+ .replace(/\s+/g, " ")
118
+ .trim(),
119
+ );
120
+ }
121
+
122
+ function resolveSecArchiveUrl(href: string, filingUrl: string): string | null {
123
+ const trimmed = href.trim();
124
+ if (!trimmed) return null;
125
+ try {
126
+ const url = new URL(trimmed, filingUrl);
127
+ const ixviewerDocument = url.searchParams.get("doc");
128
+ if (ixviewerDocument?.startsWith("/Archives/")) {
129
+ return new URL(ixviewerDocument, "https://www.sec.gov").toString();
130
+ }
131
+ return url.toString();
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
136
+
87
137
  function isHtmlResponse(body: string): boolean {
88
138
  return /^\s*<!DOCTYPE html/i.test(body) || /^\s*<html/i.test(body);
89
139
  }
@@ -201,6 +251,52 @@ export function parseRecentFilings(payload: unknown, count = 15): SecFilingItem[
201
251
  return results;
202
252
  }
203
253
 
254
+ export function parseFilingDocuments(indexHtml: string, filing: SecFilingItem): SecFilingDocument[] {
255
+ const documents: SecFilingDocument[] = [];
256
+ const primaryUrl = normalizeDocumentName(filing.primaryDocumentUrl);
257
+ const primaryDocument = normalizeDocumentName(filing.primaryDocument);
258
+ const rows = [...indexHtml.matchAll(/<tr\b[^>]*>([\s\S]*?)<\/tr>/gi)];
259
+
260
+ for (const rowMatch of rows) {
261
+ const rowHtml = rowMatch[1] ?? "";
262
+ const cells = [...rowHtml.matchAll(/<td\b[^>]*>([\s\S]*?)<\/td>/gi)].map((match) => match[1] ?? "");
263
+ if (cells.length < 3) continue;
264
+
265
+ const hrefMatch = rowHtml.match(/<a\b[^>]*href="([^"]+)"[^>]*>/i);
266
+ if (!hrefMatch?.[1]) continue;
267
+ const url = resolveSecArchiveUrl(hrefMatch[1], filing.filingUrl);
268
+ if (!url || !url.includes("/Archives/edgar/data/")) continue;
269
+
270
+ const sequence = cleanSecTableText(cells[0] ?? "") || undefined;
271
+ const description = cleanSecTableText(cells[1] ?? "") || undefined;
272
+ const linkedDocument = cleanSecTableText(cells[2] ?? "") || documentNameFromUrl(url);
273
+ const type = cleanSecTableText(cells[3] ?? "") || linkedDocument;
274
+ const size = cleanSecTableText(cells[4] ?? "") || undefined;
275
+ const document = linkedDocument || documentNameFromUrl(url);
276
+ const normalizedDocument = normalizeDocumentName(document);
277
+ const normalizedUrl = normalizeDocumentName(url);
278
+
279
+ documents.push({
280
+ sequence,
281
+ type,
282
+ description,
283
+ document,
284
+ url,
285
+ size,
286
+ isPrimary: (
287
+ (primaryDocument.length > 0 && normalizedDocument === primaryDocument)
288
+ || (primaryUrl.length > 0 && normalizedUrl === primaryUrl)
289
+ || (
290
+ documents.length === 0
291
+ && normalizeDocumentName(type) === normalizeDocumentName(filing.form)
292
+ )
293
+ ),
294
+ });
295
+ }
296
+
297
+ return documents;
298
+ }
299
+
204
300
  export class SecEdgarClient {
205
301
  private lookupPromise: Promise<Map<string, LookupEntry>> | null = null;
206
302
 
@@ -311,6 +407,11 @@ export class SecEdgarClient {
311
407
  return parseRecentFilings(payload, count);
312
408
  }
313
409
 
410
+ async getFilingDocuments(filing: SecFilingItem): Promise<SecFilingDocument[]> {
411
+ const { body } = await this.fetchText(filing.filingUrl);
412
+ return parseFilingDocuments(body, filing);
413
+ }
414
+
314
415
  async getFilingContent(filing: Pick<SecFilingItem, "primaryDocumentUrl" | "filingUrl" | "form">): Promise<string | null> {
315
416
  let targetUrl = filing.primaryDocumentUrl || filing.filingUrl;
316
417
  if (!targetUrl) return null;
@@ -328,6 +429,9 @@ export class SecEdgarClient {
328
429
  }
329
430
 
330
431
  if (filing.primaryDocumentUrl && isPdfDocument("", "", filing.primaryDocumentUrl)) {
432
+ if (filing.filingUrl === filing.primaryDocumentUrl) {
433
+ return PDF_FALLBACK_MESSAGE;
434
+ }
331
435
  const alternativeUrl = filing.filingUrl
332
436
  ? await this.findAlternativeHtmlDocumentUrl(filing.filingUrl, filing.primaryDocumentUrl)
333
437
  : null;
@@ -226,6 +226,10 @@ export class YahooFinanceClient implements DataProvider {
226
226
  return this.secClient.getRecentFilings(ticker, count);
227
227
  }
228
228
 
229
+ async getSecFilingDocuments(filing: SecFilingItem) {
230
+ return this.secClient.getFilingDocuments(filing);
231
+ }
232
+
229
233
  async getSecFilingContent(filing: SecFilingItem): Promise<string | null> {
230
234
  return this.secClient.getFilingContent(filing);
231
235
  }
@@ -46,6 +46,22 @@ describe("appReducer command bar state", () => {
46
46
  expect(closed.commandBarLaunchRequest).toBeNull();
47
47
  });
48
48
 
49
+ test("stores ticker search command bar launch requests", () => {
50
+ const initial = createInitialState(createDefaultConfig("/tmp/gloomberb-test"));
51
+ const opened = appReducer(initial, {
52
+ type: "SET_COMMAND_BAR",
53
+ open: true,
54
+ launch: { kind: "ticker-search", query: "" },
55
+ });
56
+
57
+ expect(opened.commandBarOpen).toBe(true);
58
+ expect(opened.commandBarLaunchRequest).toEqual({
59
+ kind: "ticker-search",
60
+ query: "",
61
+ sequence: 1,
62
+ });
63
+ });
64
+
49
65
  test("toggle opens blank and clears on close", () => {
50
66
  const initial = createInitialState(createDefaultConfig("/tmp/gloomberb-test"));
51
67
  const opened = appReducer(initial, { type: "TOGGLE_COMMAND_BAR" });
@@ -26,6 +26,7 @@ export function createTestPluginRuntime(
26
26
  showPane() {},
27
27
  createPaneFromTemplate() {},
28
28
  hidePane() {},
29
+ focusPane() {},
29
30
  openPaneSettings() {},
30
31
  openPluginCommandWorkflow() {},
31
32
  notify() {},
@@ -22,6 +22,7 @@ import {
22
22
  export type {
23
23
  ResolvedTickerSearch,
24
24
  TickerSearchCandidate,
25
+ TickerSearchInstrumentClass,
25
26
  } from "./types";
26
27
  export {
27
28
  findExactTickerSearchMatch,
@@ -34,6 +34,16 @@ export interface SecFilingItem {
34
34
  primaryDocumentUrl?: string;
35
35
  }
36
36
 
37
+ export interface SecFilingDocument {
38
+ sequence?: string;
39
+ type: string;
40
+ description?: string;
41
+ document: string;
42
+ url: string;
43
+ size?: string;
44
+ isPrimary: boolean;
45
+ }
46
+
37
47
  export interface EarningsEvent {
38
48
  symbol: string;
39
49
  name: string;
@@ -127,6 +137,7 @@ export interface AssetDataProvider {
127
137
  getAnalystResearch?(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<AnalystResearchData>;
128
138
  getCorporateActions?(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<CorporateActionsData>;
129
139
  getEarningsCalendar?(symbols: string[], context?: MarketDataRequestContext): Promise<EarningsEvent[]>;
140
+ getSecFilingDocuments?(filing: SecFilingItem): Promise<SecFilingDocument[]>;
130
141
  getSecFilingContent?(filing: SecFilingItem): Promise<string | null>;
131
142
  /** Fetch article summary/description by URL (lazy-loaded on selection) */
132
143
  getArticleSummary(url: string): Promise<string | null>;
package/src/ui/host.tsx CHANGED
@@ -122,6 +122,7 @@ export interface ScrollBoxRenderable {
122
122
  export interface InputRenderable {
123
123
  editBuffer: TextEditBuffer;
124
124
  cursorOffset?: number;
125
+ setCursorOffset?(offset: number): void;
125
126
  focus?(): void;
126
127
  }
127
128
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.8.1";
1
+ export const VERSION = "0.9.0";