@aiquants/contact-map 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/client.d.mts +399 -0
- package/dist/client.d.ts +399 -0
- package/dist/client.js +2 -0
- package/dist/client.mjs +2 -0
- package/dist/index.d.mts +267 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/dist/server.d.mts +89 -0
- package/dist/server.d.ts +89 -0
- package/dist/server.js +4 -0
- package/dist/server.mjs +4 -0
- package/dist/styles/contact-map.standalone.css +3 -0
- package/dist/tree-types-Dl6iBQze.d.mts +37 -0
- package/dist/tree-types-Dl6iBQze.d.ts +37 -0
- package/dist/wire-BxEK32O0.d.mts +148 -0
- package/dist/wire-BxEK32O0.d.ts +148 -0
- package/dist/worker/treeWorker.mjs +1 -0
- package/package.json +132 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AI Quants
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @aiquants/contact-map
|
|
2
|
+
|
|
3
|
+
Reusable, host-agnostic business contact map and corporate directory feature package.
|
|
4
|
+
再利用可能かつホスト非依存の名刺・連絡先マップおよび組織ディレクトリ機能パッケージ。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview / 概要
|
|
9
|
+
|
|
10
|
+
`@aiquants/contact-map` provides a complete full-stack contact directory solution featuring:
|
|
11
|
+
`@aiquants/contact-map` は以下の特徴を備えたフルスタックの連絡先ディレクトリ機能を提供します:
|
|
12
|
+
|
|
13
|
+
- **Isomorphic Core (`@aiquants/contact-map`)**:
|
|
14
|
+
- Shared domain models (`ContactRecord`), Zod wire-validation schemas, and NDJSON streaming serializers/deserializers.
|
|
15
|
+
- 共有ドメインモデル (`ContactRecord`)、Zod ワイヤ検証スキーマ、および NDJSON ストリーミングのシリアライザー/デシリアライザー。
|
|
16
|
+
- Incremental tree builder (`IncrementalTreeBuilder`) and hierarchical Japanese department parser (`StandardDepartmentParser`, `TitleBasedDepartmentParser`).
|
|
17
|
+
- インクリメンタルツリービルダー (`IncrementalTreeBuilder`) および日本語組織階層パーサー。
|
|
18
|
+
- Deterministic layout geometry calculations with hysteresis deadband (`calculateResponsiveThresholds`, `getHysteresisBreakpointState`).
|
|
19
|
+
- ヒステリシス不感帯を備えた決定論的レイアウト幾何学計算。
|
|
20
|
+
|
|
21
|
+
- **Client Layer (`@aiquants/contact-map/client`)**:
|
|
22
|
+
- Resilient NDJSON streaming client (`ContactMapStreamClient`) with keyset cursor resume, exponential backoff, and offline awareness.
|
|
23
|
+
- キーセットカーソル再開、指数バックオフ、オフライン復帰検知を備えた堅牢な NDJSON ストリームクライアント。
|
|
24
|
+
- High-performance virtualized list view with swipe gestures and detail popups.
|
|
25
|
+
- スワイプジェスチャおよび詳細ポップアップを備えた高性能仮想スクロール一覧ビュー。
|
|
26
|
+
- Dedicated background Web Worker (`treeWorkerCore`) for incremental directory tree synthesis without UI blocking.
|
|
27
|
+
- メインスレッドを阻害しない増分ディレクトリツリー合成用バックグラウンド Web Worker。
|
|
28
|
+
- Mathematical 2-column responsive layout with resize handles (`@aiquants/resize-panels`).
|
|
29
|
+
- リサイズハンドルを備えた数学的 2 カラムレスポンシブレイアウト。
|
|
30
|
+
- Multi-script fuzzy search and normalization (`@aiquants/fuzzy-search`, `@aiquants/select-box`).
|
|
31
|
+
- 多言語・クロス表記対応のあいまい検索および入力正規化。
|
|
32
|
+
|
|
33
|
+
- **Server Layer (`@aiquants/contact-map/server`)**:
|
|
34
|
+
- Dependency Injection (DI) port architecture decouples database connections, schemas, authentication, and loggers from host apps.
|
|
35
|
+
- DB接続、スキーマ、認証、ロガーをホストアプリから疎結合化する DI ポート設計。
|
|
36
|
+
- Streaming keyset pagination handler (`createContactMapStreamHandler`) preserving millisecond UTC precision (`datetime2(3)`).
|
|
37
|
+
- ミリ秒精度 (`datetime2(3)`) を維持したストリーミングキーセットパジネーションハンドラー。
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Installation / インストール
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm add @aiquants/contact-map
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Peer dependencies:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@aiquants/directory-tree": "workspace:^",
|
|
53
|
+
"@aiquants/fuzzy-search": "workspace:^",
|
|
54
|
+
"@aiquants/resize-panels": "workspace:^",
|
|
55
|
+
"@aiquants/select-box": "workspace:^",
|
|
56
|
+
"@aiquants/swipe-overlay": "workspace:^",
|
|
57
|
+
"@aiquants/virtualscroll": "workspace:^",
|
|
58
|
+
"drizzle-orm": ">=1.0.0-beta.4 <2.0.0",
|
|
59
|
+
"react": ">=18",
|
|
60
|
+
"react-dom": ">=18",
|
|
61
|
+
"react-router": "^7.0.0 || ^8.0.0",
|
|
62
|
+
"zod": ">=3.25.0 <5.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Architecture & Entry Points / アーキテクチャとエントリポイント
|
|
70
|
+
|
|
71
|
+
| Entry Point | Target | Contents |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| `@aiquants/contact-map` | Universal | Domain types, Zod schemas, wire protocols, geometry, parsers, and tree builders. |
|
|
74
|
+
| `@aiquants/contact-map/client` | Browser / SSR | React components (`ContactMapSearchPage`, `ContactMapSearchListView`, `ContactMapSearchTreeView`), hooks, and Worker clients. |
|
|
75
|
+
| `@aiquants/contact-map/server` | Node / Server | DI port interfaces (`ContactMapServerPorts`), streaming handlers, and Drizzle MSSQL service queries. |
|
|
76
|
+
| `@aiquants/contact-map/styles/contact-map.standalone.css` | CSS | Pre-compiled Tailwind v4 standalone utility styles. |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Usage / 使用方法
|
|
81
|
+
|
|
82
|
+
### 1. Server Route Setup / サーバー側ストリーミングルートの設定
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// app/routes/api.contact_map.stream.ts
|
|
86
|
+
import { createContactMapStreamHandler } from "@aiquants/contact-map/server"
|
|
87
|
+
import { db } from "~/services/drizzle-db/client.server.ts"
|
|
88
|
+
import { contactTable } from "~/services/drizzle-db/schema/contacts.table.ts"
|
|
89
|
+
import { authenticateInLoader } from "~/services/auth.server.ts"
|
|
90
|
+
|
|
91
|
+
const handler = createContactMapStreamHandler({
|
|
92
|
+
db,
|
|
93
|
+
table: contactTable,
|
|
94
|
+
authenticate: async (request) => {
|
|
95
|
+
const user = await authenticateInLoader(request)
|
|
96
|
+
return { user }
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
export async function loader({ request }: { request: Request }) {
|
|
101
|
+
return handler(request)
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 2. Client Page Setup / クライアント側ページの設定
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
// app/routes/contact_map.tsx
|
|
109
|
+
import { ContactMapConfigProvider, ContactMapSearchPage } from "@aiquants/contact-map/client"
|
|
110
|
+
import "@aiquants/contact-map/styles/contact-map.standalone.css"
|
|
111
|
+
|
|
112
|
+
export default function ContactMapRoute() {
|
|
113
|
+
return (
|
|
114
|
+
<ContactMapConfigProvider
|
|
115
|
+
config={{
|
|
116
|
+
streamEndpoint: "/api/contact_map/stream",
|
|
117
|
+
openEmailsWithGmail: true,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<ContactMapSearchPage />
|
|
121
|
+
</ContactMapConfigProvider>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## License / ライセンス
|
|
129
|
+
|
|
130
|
+
MIT
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import * as react from "react";
|
|
2
|
+
import { CSSProperties, ReactNode, RefObject, PointerEvent, MouseEvent } from "react";
|
|
3
|
+
import { UseFuzzySearchOptions } from "@aiquants/fuzzy-search/react";
|
|
4
|
+
import { C as ContactRecord, a as ContactMapStreamPhase, b as ContactMapSearchOutletContext, c as ContactMapOutletContext, d as ContactMapLogger, S as SerializedContactStreamRow } from "./wire-BxEK32O0.mjs";
|
|
5
|
+
import * as _aiquants_virtualscroll from "@aiquants/virtualscroll";
|
|
6
|
+
import { DirectoryTreeVirtualScrollOptions } from "@aiquants/directory-tree";
|
|
7
|
+
import * as _aiquants_fuzzy_search from "@aiquants/fuzzy-search";
|
|
8
|
+
import { UseFuzzySearchOptions as UseFuzzySearchOptions$1, UnifiedWorkerStats } from "@aiquants/fuzzy-search";
|
|
9
|
+
import { R as ResponsiveLayoutSpec, a as ResponsiveThresholds, C as ContactMapDirectoryEntry, b as ContactRecordForTree } from "./tree-types-Dl6iBQze.mjs";
|
|
10
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
11
|
+
interface AdvancedSearchSettingsProps {
|
|
12
|
+
searchOptions: UseFuzzySearchOptions;
|
|
13
|
+
setSearchOptions: React.Dispatch<React.SetStateAction<UseFuzzySearchOptions>>;
|
|
14
|
+
}
|
|
15
|
+
declare const AdvancedSearchSettings: react.NamedExoticComponent<AdvancedSearchSettingsProps>;
|
|
16
|
+
interface BasicSearchSettingsProps {
|
|
17
|
+
searchOptions: UseFuzzySearchOptions;
|
|
18
|
+
setSearchOptions: React.Dispatch<React.SetStateAction<UseFuzzySearchOptions>>;
|
|
19
|
+
enableHighlighting: boolean;
|
|
20
|
+
setEnableHighlighting: React.Dispatch<React.SetStateAction<boolean>>;
|
|
21
|
+
openEmailsWithGmail: boolean;
|
|
22
|
+
setOpenEmailsWithGmail: React.Dispatch<React.SetStateAction<boolean>>;
|
|
23
|
+
}
|
|
24
|
+
declare const BasicSearchSettings: react.NamedExoticComponent<BasicSearchSettingsProps>;
|
|
25
|
+
type PopupCloseOptions = {
|
|
26
|
+
suppressLongPress?: boolean;
|
|
27
|
+
cancelLongPressTimer?: boolean;
|
|
28
|
+
};
|
|
29
|
+
type ContactDetailPopupProps = {
|
|
30
|
+
contactData: ContactRecord;
|
|
31
|
+
openEmailsWithGmail: boolean;
|
|
32
|
+
openGmailCompose: (email: string) => void;
|
|
33
|
+
onRequestClose: (options?: PopupCloseOptions) => void;
|
|
34
|
+
};
|
|
35
|
+
declare const ContactDetailPopup: ({ contactData, openEmailsWithGmail, openGmailCompose, onRequestClose }: ContactDetailPopupProps) => react.ReactPortal | null;
|
|
36
|
+
interface ContactDetailGridProps {
|
|
37
|
+
contactData: ContactRecord;
|
|
38
|
+
searchTerm: string;
|
|
39
|
+
enableHighlighting: boolean;
|
|
40
|
+
openEmailsWithGmail: boolean;
|
|
41
|
+
openGmailCompose: (email: string) => void;
|
|
42
|
+
isPopupVisible: boolean;
|
|
43
|
+
onRequestClose: (options?: PopupCloseOptions) => void;
|
|
44
|
+
}
|
|
45
|
+
declare const ContactDetailGrid: ({ contactData, searchTerm, enableHighlighting, openEmailsWithGmail, openGmailCompose, isPopupVisible, onRequestClose }: ContactDetailGridProps) => react.JSX.Element;
|
|
46
|
+
type ContactDetailPanelProps = {
|
|
47
|
+
contact: ContactRecord | null;
|
|
48
|
+
searchTerm?: string;
|
|
49
|
+
enableHighlighting?: boolean;
|
|
50
|
+
style?: CSSProperties;
|
|
51
|
+
className?: string;
|
|
52
|
+
};
|
|
53
|
+
declare const ContactDetailPanel: ({ contact, searchTerm, enableHighlighting, style, className }: ContactDetailPanelProps) => react.JSX.Element;
|
|
54
|
+
interface ContactDetailRowLayoutProps {
|
|
55
|
+
fullName: ReactNode;
|
|
56
|
+
title?: ReactNode;
|
|
57
|
+
companyName?: ReactNode;
|
|
58
|
+
department?: ReactNode;
|
|
59
|
+
phoneNumber?: ReactNode;
|
|
60
|
+
email?: ReactNode;
|
|
61
|
+
updatedAtText?: ReactNode;
|
|
62
|
+
}
|
|
63
|
+
declare const ContactDetailRowLayout: ({ fullName, title, companyName, department, phoneNumber, email, updatedAtText }: ContactDetailRowLayoutProps) => react.JSX.Element;
|
|
64
|
+
interface ContactMapAnnotationProps {
|
|
65
|
+
recordCount: number;
|
|
66
|
+
companyCount: number;
|
|
67
|
+
phase: ContactMapStreamPhase;
|
|
68
|
+
loadedCount: number;
|
|
69
|
+
totalCount: number | null;
|
|
70
|
+
errorMessage: string | null;
|
|
71
|
+
onRetry?: () => void;
|
|
72
|
+
}
|
|
73
|
+
declare const ContactMapAnnotation: react.NamedExoticComponent<ContactMapAnnotationProps>;
|
|
74
|
+
interface ContactMapAppViewProps {
|
|
75
|
+
renderHeader?: (props: {
|
|
76
|
+
title: string;
|
|
77
|
+
annotation: ReactNode;
|
|
78
|
+
}) => ReactNode;
|
|
79
|
+
title?: string;
|
|
80
|
+
streamEndpoint?: string;
|
|
81
|
+
workerUrls?: {
|
|
82
|
+
indexWorker?: string;
|
|
83
|
+
levenshteinWorker?: string;
|
|
84
|
+
treeWorker?: string;
|
|
85
|
+
};
|
|
86
|
+
onFooterVisibilityChange?: (mode: "visible" | "mobile-hidden") => void;
|
|
87
|
+
}
|
|
88
|
+
declare const ContactMapAppView: (props: ContactMapAppViewProps) => react.JSX.Element;
|
|
89
|
+
declare const LIST_PANEL_MIN_WIDTH: number;
|
|
90
|
+
declare const LIST_DETAIL_PANEL_MIN_WIDTH: number;
|
|
91
|
+
declare const LIST_RESIZE_HANDLE_THICKNESS: number;
|
|
92
|
+
declare const LIST_COLLAPSE_THRESHOLD: number;
|
|
93
|
+
declare const LIST_EXPAND_THRESHOLD: number;
|
|
94
|
+
type ContactDetailGridSkeletonProps = {
|
|
95
|
+
contactData: ContactRecord | null;
|
|
96
|
+
};
|
|
97
|
+
type PanelSizingResult = {
|
|
98
|
+
normalizedViewportHeight: number;
|
|
99
|
+
normalizedDetailHeight: number;
|
|
100
|
+
panelSizingStyle: CSSProperties;
|
|
101
|
+
};
|
|
102
|
+
declare const useContactPanelSizing: (isSearchTabActive: boolean, containerRef: RefObject<HTMLDivElement | null>, listColumnRef: RefObject<HTMLDivElement | null>) => PanelSizingResult;
|
|
103
|
+
declare const useRowPopupRegistry: () => {
|
|
104
|
+
isAnyPopupVisible: boolean;
|
|
105
|
+
handlePopupVisibilityChange: (personId: number, isVisible: boolean) => void;
|
|
106
|
+
};
|
|
107
|
+
declare const useReportPopupVisibility: (personId: number | null, isVisible: boolean, onVisibilityChange: (personId: number, isVisible: boolean) => void) => void;
|
|
108
|
+
type ContactMapSearchListViewProps = {
|
|
109
|
+
context: ContactMapSearchOutletContext;
|
|
110
|
+
};
|
|
111
|
+
declare const ContactMapSearchListView: ({ context }: ContactMapSearchListViewProps) => react.JSX.Element;
|
|
112
|
+
type ContactMapTab = "search" | "settings" | "statistics";
|
|
113
|
+
interface ContactMapSearchPageProps {
|
|
114
|
+
streamState?: ContactMapOutletContext;
|
|
115
|
+
workerUrls?: {
|
|
116
|
+
indexWorker?: string;
|
|
117
|
+
levenshteinWorker?: string;
|
|
118
|
+
};
|
|
119
|
+
onFooterVisibilityChange?: (mode: "visible" | "mobile-hidden") => void;
|
|
120
|
+
children?: (context: ContactMapSearchOutletContext) => ReactNode;
|
|
121
|
+
}
|
|
122
|
+
declare const ContactMapSearchPage: ({ streamState, workerUrls, onFooterVisibilityChange, children }?: ContactMapSearchPageProps) => react.JSX.Element;
|
|
123
|
+
interface ContactMapSearchTabGroupProps {
|
|
124
|
+
context: ContactMapSearchOutletContext;
|
|
125
|
+
}
|
|
126
|
+
declare const ContactMapSearchTabGroup: ({ context }: ContactMapSearchTabGroupProps) => react.JSX.Element;
|
|
127
|
+
declare const TREE_PANEL_MIN_WIDTH: number;
|
|
128
|
+
declare const TREE_DETAIL_PANEL_MIN_WIDTH: number;
|
|
129
|
+
declare const TREE_RESIZE_HANDLE_THICKNESS: number;
|
|
130
|
+
declare const TREE_COLLAPSE_THRESHOLD: number;
|
|
131
|
+
declare const TREE_EXPAND_THRESHOLD: number;
|
|
132
|
+
interface ContactMapSearchTreeViewProps {
|
|
133
|
+
context: ContactMapSearchOutletContext;
|
|
134
|
+
controlsContainer?: HTMLDivElement | null;
|
|
135
|
+
}
|
|
136
|
+
declare const composeTreeVirtualScrollOptions: <T extends DirectoryTreeVirtualScrollOptions>(base: T, viewportHeight: number, isPopupOpen: boolean) => T & {
|
|
137
|
+
viewportHeightOverride: number;
|
|
138
|
+
behaviorOptions: {
|
|
139
|
+
enablePointerDrag: boolean;
|
|
140
|
+
enableKeyboardNavigation: boolean;
|
|
141
|
+
pointerDragInputs?: _aiquants_virtualscroll.ScrollPaneProps["pointerDragInputs"];
|
|
142
|
+
enableEscapeRowReturn?: boolean;
|
|
143
|
+
wheelSpeedMultiplier?: number;
|
|
144
|
+
inertiaOptions?: _aiquants_virtualscroll.ScrollPaneProps["inertiaOptions"];
|
|
145
|
+
overscrollBehavior?: _aiquants_virtualscroll.ScrollPaneProps["overscrollBehavior"];
|
|
146
|
+
clipItemHeight?: boolean;
|
|
147
|
+
resetOnGetItemHeightChange?: boolean;
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
declare const ContactMapSearchTreeView: ({ context, controlsContainer }: ContactMapSearchTreeViewProps) => react.JSX.Element;
|
|
151
|
+
interface HighlightedTextProps {
|
|
152
|
+
text: string;
|
|
153
|
+
query: string;
|
|
154
|
+
enabled?: boolean;
|
|
155
|
+
}
|
|
156
|
+
declare const HighlightedText: react.NamedExoticComponent<HighlightedTextProps>;
|
|
157
|
+
interface IndexWorkerSettingsProps {
|
|
158
|
+
searchOptions: UseFuzzySearchOptions;
|
|
159
|
+
setSearchOptions: React.Dispatch<React.SetStateAction<UseFuzzySearchOptions>>;
|
|
160
|
+
}
|
|
161
|
+
declare const IndexWorkerSettings: react.NamedExoticComponent<IndexWorkerSettingsProps>;
|
|
162
|
+
interface LevenshteinWorkerSettingsProps {
|
|
163
|
+
searchOptions: UseFuzzySearchOptions;
|
|
164
|
+
setSearchOptions: React.Dispatch<React.SetStateAction<UseFuzzySearchOptions>>;
|
|
165
|
+
}
|
|
166
|
+
declare const LevenshteinWorkerSettings: react.NamedExoticComponent<LevenshteinWorkerSettingsProps>;
|
|
167
|
+
type SearchOptionsPanelTab = "basic" | "advanced" | "index" | "levenshtein";
|
|
168
|
+
interface SearchOptionsPanelProps {
|
|
169
|
+
searchOptions: UseFuzzySearchOptions$1;
|
|
170
|
+
setSearchOptions: React.Dispatch<React.SetStateAction<UseFuzzySearchOptions$1>>;
|
|
171
|
+
enableHighlighting: boolean;
|
|
172
|
+
setEnableHighlighting: React.Dispatch<React.SetStateAction<boolean>>;
|
|
173
|
+
openEmailsWithGmail: boolean;
|
|
174
|
+
setOpenEmailsWithGmail: React.Dispatch<React.SetStateAction<boolean>>;
|
|
175
|
+
activeTab: SearchOptionsPanelTab;
|
|
176
|
+
onTabChange: (tab: SearchOptionsPanelTab) => void;
|
|
177
|
+
}
|
|
178
|
+
declare const SearchOptionsPanel: react.NamedExoticComponent<SearchOptionsPanelProps>;
|
|
179
|
+
interface StatItemProps {
|
|
180
|
+
label: string;
|
|
181
|
+
value: string | number;
|
|
182
|
+
unit?: string;
|
|
183
|
+
color?: string;
|
|
184
|
+
}
|
|
185
|
+
declare const StatItem: react.NamedExoticComponent<StatItemProps>;
|
|
186
|
+
interface StatisticsPanelProps {
|
|
187
|
+
performanceMetrics: {
|
|
188
|
+
totalSearches: number;
|
|
189
|
+
averageSearchTime: number;
|
|
190
|
+
itemCount: number;
|
|
191
|
+
resultsCount: number;
|
|
192
|
+
workerStats?: {
|
|
193
|
+
index?: _aiquants_fuzzy_search.UnifiedWorkerStats;
|
|
194
|
+
levenshtein?: _aiquants_fuzzy_search.UnifiedWorkerStats;
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
searchTerm: string;
|
|
198
|
+
totalDataCount: number;
|
|
199
|
+
resultsCount: number;
|
|
200
|
+
resetStats: () => Promise<void>;
|
|
201
|
+
rebuildIndex: () => Promise<void>;
|
|
202
|
+
isIndexBuilding: boolean;
|
|
203
|
+
activeTab: StatisticsPanelTab;
|
|
204
|
+
onTabChange: (tab: StatisticsPanelTab) => void;
|
|
205
|
+
}
|
|
206
|
+
type StatisticsPanelTab = "overview" | "index" | "levenshtein";
|
|
207
|
+
declare const StatisticsPanel: react.NamedExoticComponent<StatisticsPanelProps>;
|
|
208
|
+
interface WorkerStatsSectionProps {
|
|
209
|
+
title: string;
|
|
210
|
+
icon: string;
|
|
211
|
+
stats: UnifiedWorkerStats | undefined;
|
|
212
|
+
color: string;
|
|
213
|
+
}
|
|
214
|
+
declare const WorkerStatsSection: react.NamedExoticComponent<WorkerStatsSectionProps>;
|
|
215
|
+
interface ContactMapConfig {
|
|
216
|
+
streamEndpoint?: string;
|
|
217
|
+
openEmailsWithGmail?: boolean;
|
|
218
|
+
treeWorkerFactory?: () => Worker;
|
|
219
|
+
workerUrls?: {
|
|
220
|
+
indexWorker?: string;
|
|
221
|
+
levenshteinWorker?: string;
|
|
222
|
+
treeWorker?: string;
|
|
223
|
+
};
|
|
224
|
+
fuzzyWorkerUrls?: {
|
|
225
|
+
indexWorkerUrl?: string;
|
|
226
|
+
levenshteinWorkerUrl?: string;
|
|
227
|
+
};
|
|
228
|
+
layoutSpec?: Partial<ResponsiveLayoutSpec>;
|
|
229
|
+
}
|
|
230
|
+
declare function ContactMapConfigProvider({ config, children }: {
|
|
231
|
+
config?: ContactMapConfig;
|
|
232
|
+
children: ReactNode;
|
|
233
|
+
}): react.JSX.Element;
|
|
234
|
+
declare function useContactMapConfig(): ContactMapConfig;
|
|
235
|
+
type ContactMapStreamState = {
|
|
236
|
+
records: ContactRecord[];
|
|
237
|
+
loadedCount: number;
|
|
238
|
+
totalCount: number | null;
|
|
239
|
+
isLoading: boolean;
|
|
240
|
+
isComplete: boolean;
|
|
241
|
+
phase: ContactMapStreamPhase;
|
|
242
|
+
error: Error | null;
|
|
243
|
+
};
|
|
244
|
+
type ContactMapStreamClientDeps = {
|
|
245
|
+
fetchFn?: typeof fetch;
|
|
246
|
+
endpoint?: string;
|
|
247
|
+
initialRetryDelayMs?: number;
|
|
248
|
+
maxRetryDelayMs?: number;
|
|
249
|
+
maxConsecutiveFailures?: number;
|
|
250
|
+
logger?: ContactMapLogger;
|
|
251
|
+
};
|
|
252
|
+
declare const deserializeStreamRecord: (row: SerializedContactStreamRow) => ContactRecord;
|
|
253
|
+
declare class ContactMapStreamClient {
|
|
254
|
+
private readonly recordMap;
|
|
255
|
+
private readonly listeners;
|
|
256
|
+
private readonly fetchFn;
|
|
257
|
+
private readonly endpoint;
|
|
258
|
+
private readonly initialRetryDelayMs;
|
|
259
|
+
private readonly maxRetryDelayMs;
|
|
260
|
+
private readonly maxConsecutiveFailures;
|
|
261
|
+
private readonly logger;
|
|
262
|
+
private lastCursor;
|
|
263
|
+
private consecutiveFailures;
|
|
264
|
+
private started;
|
|
265
|
+
private disposed;
|
|
266
|
+
private activeController;
|
|
267
|
+
private wakeRetryWait;
|
|
268
|
+
private state;
|
|
269
|
+
constructor(deps?: ContactMapStreamClientDeps);
|
|
270
|
+
getState(): ContactMapStreamState;
|
|
271
|
+
getRecordMap(): ReadonlyMap<number, ContactRecord>;
|
|
272
|
+
subscribe(listener: (state: ContactMapStreamState) => void): () => void;
|
|
273
|
+
start(): void;
|
|
274
|
+
retry(): void;
|
|
275
|
+
retryNow(): void;
|
|
276
|
+
dispose(): void;
|
|
277
|
+
private readonly handleOnline;
|
|
278
|
+
private setState;
|
|
279
|
+
private publishRecords;
|
|
280
|
+
private runStreamLoop;
|
|
281
|
+
private waitBeforeRetry;
|
|
282
|
+
private buildStreamUrl;
|
|
283
|
+
private streamOnce;
|
|
284
|
+
}
|
|
285
|
+
declare const useAnimatedNumber: (targetValue: number, durationMs?: number) => number;
|
|
286
|
+
declare const getContactMapStreamClient: (endpoint?: string) => ContactMapStreamClient;
|
|
287
|
+
declare const resetContactMapStreamClientForTesting: () => void;
|
|
288
|
+
declare const getContactMapRecordMap: () => ReadonlyMap<number, ContactRecord>;
|
|
289
|
+
declare function useContactMapStream(endpointOverride?: string): ContactMapStreamState & {
|
|
290
|
+
retryNow: () => void;
|
|
291
|
+
};
|
|
292
|
+
interface UseDynamicViewportHeightOptions {
|
|
293
|
+
enableMutationObserver?: boolean;
|
|
294
|
+
bottomGap?: number;
|
|
295
|
+
minFallbackHeight?: number;
|
|
296
|
+
}
|
|
297
|
+
interface UseDynamicViewportHeightResult {
|
|
298
|
+
viewportHeight: number;
|
|
299
|
+
updateViewportHeight: () => void;
|
|
300
|
+
}
|
|
301
|
+
declare const useDynamicViewportHeight: (containerRef: RefObject<HTMLDivElement | null>, options?: UseDynamicViewportHeightOptions) => UseDynamicViewportHeightResult;
|
|
302
|
+
interface UseHorizontalDragScrollOptions {
|
|
303
|
+
dragThreshold?: number;
|
|
304
|
+
}
|
|
305
|
+
interface HorizontalDragScrollHandlers {
|
|
306
|
+
setScrollableRef: (element: HTMLDivElement | null) => void;
|
|
307
|
+
onPointerDown: (event: PointerEvent<HTMLDivElement>) => void;
|
|
308
|
+
onPointerMove: (event: PointerEvent<HTMLDivElement>) => void;
|
|
309
|
+
onPointerUp: (event: PointerEvent<HTMLDivElement>) => void;
|
|
310
|
+
onPointerLeave: (event: PointerEvent<HTMLDivElement>) => void;
|
|
311
|
+
onPointerCancel: (event: PointerEvent<HTMLDivElement>) => void;
|
|
312
|
+
onClickCapture: (event: MouseEvent<HTMLDivElement>) => void;
|
|
313
|
+
}
|
|
314
|
+
declare const useHorizontalDragScroll: (options?: UseHorizontalDragScrollOptions) => HorizontalDragScrollHandlers;
|
|
315
|
+
type UseLongPressPopupOptions<T> = {
|
|
316
|
+
ref: React.RefObject<HTMLElement | null>;
|
|
317
|
+
onSelect?: (item: T) => void;
|
|
318
|
+
resolveItem?: (target: EventTarget) => T | null;
|
|
319
|
+
item?: T;
|
|
320
|
+
};
|
|
321
|
+
interface UseLongPressPopupReturn<T> {
|
|
322
|
+
isPopupVisible: boolean;
|
|
323
|
+
popupItem: T | null;
|
|
324
|
+
handleClosePopup: (options?: PopupCloseOptions) => void;
|
|
325
|
+
}
|
|
326
|
+
declare const useLongPressPopup: <T>({ ref, onSelect, resolveItem, item }: UseLongPressPopupOptions<T>) => UseLongPressPopupReturn<T>;
|
|
327
|
+
interface UseResponsiveTwoColumnLayoutOptions {
|
|
328
|
+
mediaQuery?: string;
|
|
329
|
+
}
|
|
330
|
+
interface UseResponsiveTwoColumnLayoutResult {
|
|
331
|
+
isTwoColumn: boolean;
|
|
332
|
+
isContainerNarrow: boolean;
|
|
333
|
+
isMediaMatches: boolean;
|
|
334
|
+
containerWidth: number;
|
|
335
|
+
}
|
|
336
|
+
declare function useResponsiveTwoColumnLayout(containerRef: RefObject<HTMLElement | null>, thresholds: Pick<ResponsiveThresholds, "collapseThreshold" | "expandThreshold">, options?: UseResponsiveTwoColumnLayoutOptions): UseResponsiveTwoColumnLayoutResult;
|
|
337
|
+
interface TreeWorkerSnapshot {
|
|
338
|
+
tree: ContactMapDirectoryEntry[];
|
|
339
|
+
directoryPaths?: string[];
|
|
340
|
+
consumedRecords: number;
|
|
341
|
+
uniquePersonCount: number;
|
|
342
|
+
isIdle: boolean;
|
|
343
|
+
rebuildEpoch: number;
|
|
344
|
+
}
|
|
345
|
+
interface TreeWorkerState {
|
|
346
|
+
snapshot: TreeWorkerSnapshot | null;
|
|
347
|
+
isBuilding: boolean;
|
|
348
|
+
}
|
|
349
|
+
declare class TreeWorkerSession {
|
|
350
|
+
private worker;
|
|
351
|
+
private generation;
|
|
352
|
+
private postedCount;
|
|
353
|
+
private latestSnapshot;
|
|
354
|
+
private workerNeedsReset;
|
|
355
|
+
private lastSyncedRecords;
|
|
356
|
+
private consecutiveFailures;
|
|
357
|
+
private finalizeRequested;
|
|
358
|
+
private retryTimer;
|
|
359
|
+
private readonly listeners;
|
|
360
|
+
private readonly workerFactory;
|
|
361
|
+
private state;
|
|
362
|
+
constructor(workerFactory: () => Worker);
|
|
363
|
+
getState: () => TreeWorkerState;
|
|
364
|
+
private updateState;
|
|
365
|
+
subscribe: (listener: () => void) => (() => void);
|
|
366
|
+
private notify;
|
|
367
|
+
private ensureWorker;
|
|
368
|
+
private readonly handleMessage;
|
|
369
|
+
private readonly handleFailure;
|
|
370
|
+
syncRecords(records: readonly ContactRecordForTree[]): void;
|
|
371
|
+
finalize(): void;
|
|
372
|
+
dispose(): void;
|
|
373
|
+
}
|
|
374
|
+
declare const getTreeWorkerSession: (factory?: () => Worker, treeWorkerUrl?: string) => TreeWorkerSession;
|
|
375
|
+
declare const syncTreeWorkerRecords: (records: readonly ContactRecordForTree[]) => void;
|
|
376
|
+
declare const finalizeTreeWorker: () => void;
|
|
377
|
+
declare const resetTreeWorkerSessionForTesting: () => void;
|
|
378
|
+
declare function useTreeWorker(records: readonly ContactRecordForTree[], isComplete?: boolean): TreeWorkerState;
|
|
379
|
+
declare function contactMapShouldRevalidate(): boolean;
|
|
380
|
+
type ProgressProps = react.ComponentPropsWithoutRef<"div"> & {
|
|
381
|
+
value?: number;
|
|
382
|
+
max?: number;
|
|
383
|
+
indicatorClassName?: string;
|
|
384
|
+
};
|
|
385
|
+
declare function Progress({ value, max, className, indicatorClassName, ...props }: ProgressProps): react.JSX.Element;
|
|
386
|
+
interface SwipeOverlayProps {
|
|
387
|
+
isOpen: boolean;
|
|
388
|
+
onClose: () => void;
|
|
389
|
+
children: ReactNode;
|
|
390
|
+
className?: string;
|
|
391
|
+
title?: string;
|
|
392
|
+
direction?: "bottom" | "top" | "left" | "right";
|
|
393
|
+
}
|
|
394
|
+
declare const SwipeOverlay: ({ isOpen, onClose, children, className, title }: SwipeOverlayProps) => react.JSX.Element | null;
|
|
395
|
+
declare const Tabs: react.ForwardRefExoticComponent<TabsPrimitive.TabsProps & react.RefAttributes<HTMLDivElement>>;
|
|
396
|
+
declare const TabsList: react.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsListProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
397
|
+
declare const TabsTrigger: react.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsTriggerProps & react.RefAttributes<HTMLButtonElement>, "ref"> & react.RefAttributes<HTMLButtonElement>>;
|
|
398
|
+
declare const TabsContent: react.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
399
|
+
export { AdvancedSearchSettings, type AdvancedSearchSettingsProps, BasicSearchSettings, type BasicSearchSettingsProps, ContactDetailGrid, type ContactDetailGridProps, type ContactDetailGridSkeletonProps, ContactDetailPanel, type ContactDetailPanelProps, ContactDetailPopup, type ContactDetailPopupProps, ContactDetailRowLayout, type ContactDetailRowLayoutProps, ContactMapAnnotation, type ContactMapAnnotationProps, ContactMapAppView, type ContactMapAppViewProps, type ContactMapConfig, ContactMapConfigProvider, ContactMapSearchListView, ContactMapSearchPage, type ContactMapSearchPageProps, ContactMapSearchTabGroup, type ContactMapSearchTabGroupProps, ContactMapSearchTreeView, type ContactMapSearchTreeViewProps, ContactMapStreamClient, type ContactMapStreamClientDeps, ContactMapStreamPhase, type ContactMapStreamState, type ContactMapTab, HighlightedText, type HighlightedTextProps, type HorizontalDragScrollHandlers, IndexWorkerSettings, type IndexWorkerSettingsProps, LIST_COLLAPSE_THRESHOLD, LIST_DETAIL_PANEL_MIN_WIDTH, LIST_EXPAND_THRESHOLD, LIST_PANEL_MIN_WIDTH, LIST_RESIZE_HANDLE_THICKNESS, LevenshteinWorkerSettings, type LevenshteinWorkerSettingsProps, type PopupCloseOptions, Progress, type ProgressProps, SearchOptionsPanel, type SearchOptionsPanelProps, type SearchOptionsPanelTab, StatItem, type StatItemProps, StatisticsPanel, type StatisticsPanelProps, type StatisticsPanelTab, SwipeOverlay, type SwipeOverlayProps, TREE_COLLAPSE_THRESHOLD, TREE_DETAIL_PANEL_MIN_WIDTH, TREE_EXPAND_THRESHOLD, TREE_PANEL_MIN_WIDTH, TREE_RESIZE_HANDLE_THICKNESS, Tabs, TabsContent, TabsList, TabsTrigger, TreeWorkerSession, type TreeWorkerSnapshot, type TreeWorkerState, type UseDynamicViewportHeightOptions, type UseDynamicViewportHeightResult, type UseHorizontalDragScrollOptions, type UseLongPressPopupOptions, type UseLongPressPopupReturn, type UseResponsiveTwoColumnLayoutOptions, type UseResponsiveTwoColumnLayoutResult, WorkerStatsSection, type WorkerStatsSectionProps, composeTreeVirtualScrollOptions, contactMapShouldRevalidate, deserializeStreamRecord, finalizeTreeWorker, getContactMapRecordMap, getContactMapStreamClient, getTreeWorkerSession, resetContactMapStreamClientForTesting, resetTreeWorkerSessionForTesting, syncTreeWorkerRecords, useAnimatedNumber, useContactMapConfig, useContactMapStream, useContactPanelSizing, useDynamicViewportHeight, useHorizontalDragScroll, useLongPressPopup, useReportPopupVisibility, useResponsiveTwoColumnLayout, useRowPopupRegistry, useTreeWorker };
|