react-reason-editor-sidebar 0.1.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/Sidebar.d.ts +2 -0
- package/dist/SidebarContent.d.ts +7 -0
- package/dist/SidebarFooter.d.ts +37 -0
- package/dist/SidebarToolbar.d.ts +89 -0
- package/dist/SidebarViewMenu.d.ts +20 -0
- package/dist/app-types/fileSource.d.ts +124 -0
- package/dist/app-types/toc.d.ts +2 -0
- package/dist/app-ui/FileTypeIcon.d.ts +11 -0
- package/dist/app-ui/alert-dialog.d.ts +14 -0
- package/dist/app-ui/badge.d.ts +13 -0
- package/dist/app-ui/button.d.ts +15 -0
- package/dist/app-ui/context-menu.d.ts +28 -0
- package/dist/app-ui/dialog.d.ts +15 -0
- package/dist/app-ui/dropdown-menu.d.ts +25 -0
- package/dist/app-ui/input.d.ts +7 -0
- package/dist/app-ui/sheet.d.ts +13 -0
- package/dist/app-ui/tooltip.d.ts +7 -0
- package/dist/app-ui/tree.d.ts +23 -0
- package/dist/app-utils/file-sources/sources.d.ts +43 -0
- package/dist/app-utils/utils.d.ts +1 -0
- package/dist/dialogs/FileManagerModal.d.ts +11 -0
- package/dist/dialogs/filemanager-data.d.ts +17 -0
- package/dist/documents/DocumentTree.d.ts +59 -0
- package/dist/documents/defaultDocuments.d.ts +12 -0
- package/dist/features/ai-rewrite/AIRewriteSuggestion.d.ts +27 -0
- package/dist/features/ai-rewrite/rewriteModes.d.ts +37 -0
- package/dist/file-tree/FileTreeContextMenu.d.ts +18 -0
- package/dist/file-tree/documentUtils.d.ts +63 -0
- package/dist/file-tree/expandLevels.d.ts +32 -0
- package/dist/file-tree/filetree.d.ts +66 -0
- package/dist/file-tree/index.d.ts +9 -0
- package/dist/file-tree/useFileTreeOperations.d.ts +27 -0
- package/dist/fileSourceUtils.d.ts +2 -0
- package/dist/index.cjs +46 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.mjs +20301 -0
- package/dist/layout/sidebar/index.d.ts +10 -0
- package/dist/layout/sidebar/panelOptions.d.ts +37 -0
- package/dist/layout/sidebar/types.d.ts +227 -0
- package/dist/search/OutlineView.d.ts +51 -0
- package/dist/search/relatedDocuments.d.ts +46 -0
- package/dist/search/useActiveHeading.d.ts +40 -0
- package/dist/style.css +2 -0
- package/dist/utils/storage.d.ts +7 -0
- package/package.json +77 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sidebar/index
|
|
3
|
+
* @description Barrel export for the sidebar "contract": the shared types
|
|
4
|
+
* and pure panel-toggle config/helpers that the Sidebar/SidebarContent
|
|
5
|
+
* components (this package) implement, and that `react-reason-editor`'s
|
|
6
|
+
* ReasonDocs/RightPanel type their `SidebarComponent`/
|
|
7
|
+
* `SidebarContentComponent` props against.
|
|
8
|
+
*/
|
|
9
|
+
export * from './types';
|
|
10
|
+
export * from './panelOptions';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sidebar/panelOptions
|
|
3
|
+
* @description Shared metadata and pure helpers for the per-side panel
|
|
4
|
+
* toggle system (Open Tabs / Files / Outline / Related / AI) used by both
|
|
5
|
+
* the left sidebar and the right panel, and by the {@link SidebarViewMenu}
|
|
6
|
+
* dropdown that controls them.
|
|
7
|
+
*/
|
|
8
|
+
import { type LucideIcon } from 'lucide-react';
|
|
9
|
+
import type { SidebarPanelType } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* Ordered list of togglable panel kinds shown in the view menu, with display
|
|
12
|
+
* metadata. This order is also the canonical stacking order for the panels
|
|
13
|
+
* within a sidebar (Open Tabs always stacks above the Files tree).
|
|
14
|
+
*/
|
|
15
|
+
export declare const PANEL_OPTIONS: {
|
|
16
|
+
type: SidebarPanelType;
|
|
17
|
+
label: string;
|
|
18
|
+
icon: LucideIcon;
|
|
19
|
+
}[];
|
|
20
|
+
/**
|
|
21
|
+
* Sorts a panel list into the canonical stacking order defined by
|
|
22
|
+
* {@link PANEL_OPTIONS} (e.g. Open Tabs above Files), regardless of the
|
|
23
|
+
* order the panels were toggled on in.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sortPanels(panels: SidebarPanelType[]): SidebarPanelType[];
|
|
26
|
+
/**
|
|
27
|
+
* Computes the next panel list when a single panel checkbox is toggled.
|
|
28
|
+
*
|
|
29
|
+
* Panels are independently added/removed and may stack; whether a side is
|
|
30
|
+
* "split" is inferred from the result (2 or more panels selected) rather
|
|
31
|
+
* than being a separate mode.
|
|
32
|
+
*
|
|
33
|
+
* @param panels - Current panels visible on this side.
|
|
34
|
+
* @param type - The panel being toggled.
|
|
35
|
+
* @param allowEmpty - Whether the resulting list may become empty (hides the side entirely).
|
|
36
|
+
*/
|
|
37
|
+
export declare function togglePanel(panels: SidebarPanelType[], type: SidebarPanelType, allowEmpty: boolean): SidebarPanelType[];
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sidebar/types
|
|
3
|
+
* @description The sidebar "contract" owned by reason-editor: SidebarProps,
|
|
4
|
+
* SidebarContentProps, and the panel-based view configuration types. The
|
|
5
|
+
* actual sidebar UI (Sidebar, SidebarContent, SidebarToolbar, SidebarFooter,
|
|
6
|
+
* SidebarViewMenu) lives in the separate `react-reason-editor-sidebar`
|
|
7
|
+
* package and is injected into ReasonDocs/RightPanel via props typed
|
|
8
|
+
* against these interfaces, so reason-editor never imports that package
|
|
9
|
+
* directly (avoiding a circular workspace dependency).
|
|
10
|
+
*/
|
|
11
|
+
import type { RefObject } from "react";
|
|
12
|
+
import { Document } from "../../documents/DocumentTree";
|
|
13
|
+
import type { TocEntry } from "../../app-types/toc";
|
|
14
|
+
import type { ActiveHeadingEditorHandle } from "../../search/useActiveHeading";
|
|
15
|
+
import type { DocumentTreeHandle } from "../../file-tree/filetree";
|
|
16
|
+
import type { OutlineViewHandle } from "../../search/OutlineView";
|
|
17
|
+
/** A single togglable panel kind that can appear in the left or right sidebar. */
|
|
18
|
+
export type SidebarPanelType = "ai" | "files" | "outline" | "openTabs" | "related";
|
|
19
|
+
/** The kind of resource a tab in the "Open Tabs" panel represents. */
|
|
20
|
+
export type OpenTabKind = "file" | "chat";
|
|
21
|
+
/**
|
|
22
|
+
* A single entry in the unified "Open Tabs" list. When provided, this
|
|
23
|
+
* overrides the legacy document-only tab rendering so tabs for other
|
|
24
|
+
* resource kinds (e.g. chat conversations) can be interleaved with files.
|
|
25
|
+
*/
|
|
26
|
+
export interface OpenTabItem {
|
|
27
|
+
id: string;
|
|
28
|
+
title: string;
|
|
29
|
+
kind: OpenTabKind;
|
|
30
|
+
}
|
|
31
|
+
/** Shared AI-suggestion props needed to render an "ai" panel. */
|
|
32
|
+
export interface SidebarAiProps {
|
|
33
|
+
isAiLoading?: boolean;
|
|
34
|
+
aiSuggestion?: {
|
|
35
|
+
originalText: string;
|
|
36
|
+
suggestedText: string;
|
|
37
|
+
range: {
|
|
38
|
+
from: number;
|
|
39
|
+
to: number;
|
|
40
|
+
};
|
|
41
|
+
mode?: string;
|
|
42
|
+
} | null;
|
|
43
|
+
onAiApprove?: () => void;
|
|
44
|
+
onAiReject?: () => void;
|
|
45
|
+
onAiRegenerate?: (mode: any) => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* AI-generated tips about the currently active document, shown as a section
|
|
49
|
+
* of the "ai" panel. Omitted entirely (and the section hidden) when the
|
|
50
|
+
* host app has no tips-generation capability to offer.
|
|
51
|
+
*/
|
|
52
|
+
export interface SidebarTipsProps {
|
|
53
|
+
/** Tips generated for the active document by the most recent request. */
|
|
54
|
+
tips?: string[];
|
|
55
|
+
/** Whether a tips-generation request is in flight. */
|
|
56
|
+
isTipsLoading?: boolean;
|
|
57
|
+
/** Requests (re)generation of tips for the active document. */
|
|
58
|
+
onGenerateTips?: () => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* AI-generated suggested search queries ("topics") related to the currently
|
|
62
|
+
* active document, shown as a section of the "related" panel. Omitted
|
|
63
|
+
* entirely (and the section hidden) when the host app has no
|
|
64
|
+
* topics-generation capability to offer.
|
|
65
|
+
*/
|
|
66
|
+
export interface SidebarTopicsProps {
|
|
67
|
+
/** Suggested search queries generated for the active document by the most recent request. */
|
|
68
|
+
topics?: string[];
|
|
69
|
+
/** Whether a topics-generation request is in flight. */
|
|
70
|
+
isTopicsLoading?: boolean;
|
|
71
|
+
/** Requests (re)generation of topics for the active document. */
|
|
72
|
+
onGenerateTopics?: () => void;
|
|
73
|
+
/** Runs a search for the given topic (e.g. opens a new chat seeded with it). */
|
|
74
|
+
onSearchTopic?: (topic: string) => void;
|
|
75
|
+
}
|
|
76
|
+
export interface SidebarProps {
|
|
77
|
+
documents: Document[];
|
|
78
|
+
activeId: string | null;
|
|
79
|
+
activeDocument: Document | undefined;
|
|
80
|
+
onSelect: (id: string) => void;
|
|
81
|
+
onAdd: (parentId: string | null, isFolder?: boolean) => void;
|
|
82
|
+
onDelete: (id: string) => void;
|
|
83
|
+
onDuplicate: (id: string) => void;
|
|
84
|
+
onToggleExpand: (id: string) => void;
|
|
85
|
+
/**
|
|
86
|
+
* Replaces the set of expanded folders wholesale, used by the toolbar's
|
|
87
|
+
* expand/collapse toggle as it steps through folder levels. Hosts should
|
|
88
|
+
* persist it the same way they persist `onToggleExpand`; without it the
|
|
89
|
+
* stepped expansion lives only in the tree and is lost on the next
|
|
90
|
+
* document change.
|
|
91
|
+
*/
|
|
92
|
+
onSetExpandedFolders?: (folderIds: string[]) => void;
|
|
93
|
+
onMove: (draggedId: string, targetId: string | null, position: "before" | "after" | "child") => void;
|
|
94
|
+
onManageTags?: (id: string) => void;
|
|
95
|
+
onRename?: (id: string, newTitle: string) => void;
|
|
96
|
+
searchQuery: string;
|
|
97
|
+
onSearchChange: (query: string) => void;
|
|
98
|
+
onSearchClear: () => void;
|
|
99
|
+
onSearchFocus: () => void;
|
|
100
|
+
isOpen?: boolean;
|
|
101
|
+
onOpenChange?: (open: boolean) => void;
|
|
102
|
+
isMobile?: boolean;
|
|
103
|
+
leftPanels: SidebarPanelType[];
|
|
104
|
+
onLeftPanelsChange: (panels: SidebarPanelType[]) => void;
|
|
105
|
+
rightPanels: SidebarPanelType[];
|
|
106
|
+
onRightPanelsChange: (panels: SidebarPanelType[]) => void;
|
|
107
|
+
onSettingsClick?: (section?: string) => void;
|
|
108
|
+
onInviteClick?: () => void;
|
|
109
|
+
onRestore?: (id: string) => void;
|
|
110
|
+
onPermanentDelete?: (id: string) => void;
|
|
111
|
+
newDocumentId?: string | null;
|
|
112
|
+
showDynamicIsland?: boolean;
|
|
113
|
+
onToggleDynamicIsland?: () => void;
|
|
114
|
+
activeFileSourceId?: string;
|
|
115
|
+
onFileSourceChange?: (sourceId: string) => void;
|
|
116
|
+
headings?: TocEntry[];
|
|
117
|
+
onNavigate?: (key: string) => void;
|
|
118
|
+
editorRef?: RefObject<ActiveHeadingEditorHandle | null>;
|
|
119
|
+
openTabs?: string[];
|
|
120
|
+
activeTab?: string | null;
|
|
121
|
+
onTabChange?: (id: string) => void;
|
|
122
|
+
onTabClose?: (id: string) => void;
|
|
123
|
+
onTabsClose?: (ids: string[]) => void;
|
|
124
|
+
onTabRename?: (id: string, newTitle: string) => void;
|
|
125
|
+
onSplitRight?: (id: string) => void;
|
|
126
|
+
onReopenLastClosed?: () => void;
|
|
127
|
+
canReopenLastClosed?: boolean;
|
|
128
|
+
tabItems?: OpenTabItem[];
|
|
129
|
+
onNewChat?: () => void;
|
|
130
|
+
aiProps?: SidebarAiProps;
|
|
131
|
+
tipsProps?: SidebarTipsProps;
|
|
132
|
+
topicsProps?: SidebarTopicsProps;
|
|
133
|
+
}
|
|
134
|
+
/** Props for the `SidebarContent` component (the shared panel-body renderer used by both the left sidebar and `RightPanel`). */
|
|
135
|
+
export interface SidebarContentProps {
|
|
136
|
+
/** Which panels are active on this side. Two or more panels render stacked in a split (canonical stacking order applies). */
|
|
137
|
+
panels: SidebarPanelType[];
|
|
138
|
+
/** Storage key suffix so left/right panel sizes persist independently. */
|
|
139
|
+
persistenceKey: string;
|
|
140
|
+
/** Filtered/flat document list to pass down to the file tree. */
|
|
141
|
+
activeDocuments: Document[];
|
|
142
|
+
/** Heading entries from the Tiptap table of contents. */
|
|
143
|
+
headings?: TocEntry[];
|
|
144
|
+
/** ID of the currently active/selected document. */
|
|
145
|
+
activeId: string | null;
|
|
146
|
+
/** The full `Document` object for `activeId` (used for outline section search). */
|
|
147
|
+
activeDocument?: Document;
|
|
148
|
+
/** Whether the sidebar is being displayed in a mobile sheet. */
|
|
149
|
+
isMobile?: boolean;
|
|
150
|
+
/** Selects a document by ID; closes the mobile sheet when on mobile. */
|
|
151
|
+
onSelect: (id: string) => void;
|
|
152
|
+
/** Creates a new note or folder under `parentId`. */
|
|
153
|
+
onAdd: (parentId: string | null, isFolder?: boolean) => void;
|
|
154
|
+
/** Soft-deletes a document by ID. */
|
|
155
|
+
onDelete: (id: string) => void;
|
|
156
|
+
/** Duplicates a document by ID. */
|
|
157
|
+
onDuplicate: (id: string) => void;
|
|
158
|
+
/** Moves a document in the tree via drag-and-drop. */
|
|
159
|
+
onMove: (draggedId: string, targetId: string | null, position: 'before' | 'after' | 'child') => void;
|
|
160
|
+
/** Opens the tag management UI for a document. */
|
|
161
|
+
onManageTags?: (id: string) => void;
|
|
162
|
+
/** Renames a document. */
|
|
163
|
+
onRename?: (id: string, newTitle: string) => void;
|
|
164
|
+
/** Callback to control the mobile sidebar sheet open state. */
|
|
165
|
+
onOpenChange?: (open: boolean) => void;
|
|
166
|
+
/** Ref forwarded to the `FileTree` component for imperative control. */
|
|
167
|
+
treeRef?: RefObject<DocumentTreeHandle | null>;
|
|
168
|
+
/**
|
|
169
|
+
* Reports the file tree's live expansion depth (and the tree's deepest
|
|
170
|
+
* folder level) so the toolbar's expand/collapse toggle can label the next
|
|
171
|
+
* step of its one-level-at-a-time cycle.
|
|
172
|
+
*/
|
|
173
|
+
onExpandStateChange?: (state: {
|
|
174
|
+
level: number;
|
|
175
|
+
maxLevel: number;
|
|
176
|
+
}) => void;
|
|
177
|
+
/**
|
|
178
|
+
* Replaces the set of expanded folders wholesale when the toolbar steps the
|
|
179
|
+
* tree to a new expansion level, so the host can persist it.
|
|
180
|
+
*/
|
|
181
|
+
onSetExpandedFolders?: (folderIds: string[]) => void;
|
|
182
|
+
/** Ref forwarded to the `OutlineView` component for imperative control. */
|
|
183
|
+
outlineRef?: RefObject<OutlineViewHandle | null>;
|
|
184
|
+
/** Editor handle passed to `OutlineView` to scroll-spy the active heading. */
|
|
185
|
+
editorRef?: RefObject<ActiveHeadingEditorHandle | null>;
|
|
186
|
+
/** Currently open tab IDs shown in the top pane. */
|
|
187
|
+
openTabs?: string[];
|
|
188
|
+
/** ID of the currently active tab. */
|
|
189
|
+
activeTab?: string | null;
|
|
190
|
+
/** Switches to a tab. */
|
|
191
|
+
onTabChange?: (id: string) => void;
|
|
192
|
+
/** Closes a tab. */
|
|
193
|
+
onTabClose?: (id: string) => void;
|
|
194
|
+
/**
|
|
195
|
+
* Closes several tabs in one operation (used by "Close Tabs Below" and
|
|
196
|
+
* "Close Other Tabs"). When omitted the panel falls back to calling
|
|
197
|
+
* `onTabClose` once per tab, which only works if the host applies each
|
|
198
|
+
* close against the latest state.
|
|
199
|
+
*/
|
|
200
|
+
onTabsClose?: (ids: string[]) => void;
|
|
201
|
+
/** Renames a tab's document. */
|
|
202
|
+
onTabRename?: (id: string, newTitle: string) => void;
|
|
203
|
+
/** Opens a tab in a split view to the right. */
|
|
204
|
+
onSplitRight?: (id: string) => void;
|
|
205
|
+
/** Reopens the last closed tab. */
|
|
206
|
+
onReopenLastClosed?: () => void;
|
|
207
|
+
/** Whether there is a closed tab that can be reopened. */
|
|
208
|
+
canReopenLastClosed?: boolean;
|
|
209
|
+
/** Navigates the editor to a heading (used by the "outline" panel). */
|
|
210
|
+
onNavigate?: (key: string) => void;
|
|
211
|
+
/** AI suggestion state/handlers (used by the "ai" panel). */
|
|
212
|
+
aiProps?: SidebarAiProps;
|
|
213
|
+
/** AI-generated page tips state/handlers (used by the "ai" panel). */
|
|
214
|
+
tipsProps?: SidebarTipsProps;
|
|
215
|
+
/** AI-generated search topics state/handlers (used by the "related" panel). */
|
|
216
|
+
topicsProps?: SidebarTopicsProps;
|
|
217
|
+
/** Unified tab list (files + chats). Overrides file-only tab derivation when set. */
|
|
218
|
+
tabItems?: OpenTabItem[];
|
|
219
|
+
/** Opens a new chat tab from the "Open Tabs" panel header. */
|
|
220
|
+
onNewChat?: () => void;
|
|
221
|
+
/** Opens the file manager modal from the "Files" panel header. */
|
|
222
|
+
onFileManagerOpen?: () => void;
|
|
223
|
+
/** Soft-deleted documents listed in the "Files" panel header trash menu. */
|
|
224
|
+
deletedDocs?: Document[];
|
|
225
|
+
/** Restores a soft-deleted document by ID (from the trash menu). */
|
|
226
|
+
onRestore?: (id: string) => void;
|
|
227
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { TocEntry } from '../app-types/toc';
|
|
3
|
+
import { type ActiveHeadingEditorHandle } from './useActiveHeading';
|
|
4
|
+
/**
|
|
5
|
+
* Imperative handle exposed to parent components via a forwarded ref.
|
|
6
|
+
* Allows programmatic expand/collapse of the entire outline.
|
|
7
|
+
*/
|
|
8
|
+
export interface OutlineViewHandle {
|
|
9
|
+
/** Expands all heading nodes. */
|
|
10
|
+
expandAll: () => void;
|
|
11
|
+
/** Collapses all H1 nodes (hiding their children). */
|
|
12
|
+
collapseAll: () => void;
|
|
13
|
+
}
|
|
14
|
+
/** Props for the {@link OutlineView} component. */
|
|
15
|
+
interface OutlineViewProps {
|
|
16
|
+
/** Heading entries from the Tiptap editor's TOC extractor. */
|
|
17
|
+
headings?: TocEntry[];
|
|
18
|
+
/** Called when the user clicks a heading; receives the heading key. */
|
|
19
|
+
onNavigate?: (key: string) => void;
|
|
20
|
+
/** Optional drag-reorder callback receiving source and target indices. */
|
|
21
|
+
onReorder?: (fromIndex: number, toIndex: number) => void;
|
|
22
|
+
/** Filters the heading list to items whose text contains this query. */
|
|
23
|
+
searchQuery?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Ref to the editor's imperative handle, used to resolve heading DOM
|
|
26
|
+
* elements for scroll-spy active-heading highlighting. When omitted, no
|
|
27
|
+
* heading is ever highlighted as active.
|
|
28
|
+
*/
|
|
29
|
+
editorRef?: RefObject<ActiveHeadingEditorHandle | null>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Given the outline panel's own scroll container (`scrollTop`/`clientHeight`)
|
|
33
|
+
* and the active row's offset within it (`offsetTop`/`offsetHeight`), returns
|
|
34
|
+
* the `scrollTop` needed to bring the row fully into view — scrolling up if
|
|
35
|
+
* it's above the visible area, down if it's below — or `null` if the row is
|
|
36
|
+
* already fully visible and no scrolling is needed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function computeScrollIntoViewOffset(container: {
|
|
39
|
+
scrollTop: number;
|
|
40
|
+
clientHeight: number;
|
|
41
|
+
}, row: {
|
|
42
|
+
offsetTop: number;
|
|
43
|
+
offsetHeight: number;
|
|
44
|
+
}): number | null;
|
|
45
|
+
/**
|
|
46
|
+
* Collapsible document outline panel. Renders a heading tree built from
|
|
47
|
+
* `headings` with click-to-navigate, expand/collapse, drag-to-reorder,
|
|
48
|
+
* search filtering, and a context menu for bulk collapse-level controls.
|
|
49
|
+
*/
|
|
50
|
+
export declare const OutlineView: import("react").ForwardRefExoticComponent<OutlineViewProps & import("react").RefAttributes<OutlineViewHandle>>;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module relatedDocuments
|
|
3
|
+
* @description Client-side "related documents" suggestions for the sidebar's
|
|
4
|
+
* Related panel. Scores every other document by how many significant
|
|
5
|
+
* keywords it shares with the active document's title and plain-text
|
|
6
|
+
* content — no server calls or embeddings involved — boosted by any shared
|
|
7
|
+
* user-assigned tags, a more deliberate relatedness signal than incidental
|
|
8
|
+
* keyword overlap.
|
|
9
|
+
*/
|
|
10
|
+
import type { Document } from '../documents/DocumentTree';
|
|
11
|
+
/** A related-document suggestion returned by {@link findRelatedDocuments}. */
|
|
12
|
+
export interface RelatedDocumentResult {
|
|
13
|
+
/** The related document. */
|
|
14
|
+
document: Document;
|
|
15
|
+
/** Number of significant keywords shared with the active document. */
|
|
16
|
+
sharedKeywordCount: number;
|
|
17
|
+
/** Number of tags shared with the active document (case-insensitive). */
|
|
18
|
+
sharedTagCount: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Ranks candidate documents by how many significant keywords and tags they
|
|
22
|
+
* share with `activeDocument`. Each shared tag counts as {@link
|
|
23
|
+
* TAG_MATCH_WEIGHT} shared keywords when ranking, since a user-assigned tag
|
|
24
|
+
* is a more deliberate relatedness signal than incidental keyword overlap. A
|
|
25
|
+
* document with no shared keywords but at least one shared tag still
|
|
26
|
+
* qualifies. Folders, soft-deleted documents, and the active document itself
|
|
27
|
+
* are never suggested.
|
|
28
|
+
*
|
|
29
|
+
* @param documents - Candidate documents to search (typically all documents).
|
|
30
|
+
* @param activeDocument - The currently open document, or `undefined`/`null` when none is active.
|
|
31
|
+
* @param limit - Maximum number of suggestions to return (default 10).
|
|
32
|
+
*/
|
|
33
|
+
export declare function findRelatedDocuments(documents: Document[], activeDocument: Document | undefined | null, limit?: number): RelatedDocumentResult[];
|
|
34
|
+
/** The single top-ranked suggestion, split from the rest, returned by {@link splitTopSuggestion}. */
|
|
35
|
+
export interface RelatedDocumentsSplit {
|
|
36
|
+
/** The highest-ranked related document, or `null` when there are no results. */
|
|
37
|
+
suggested: RelatedDocumentResult | null;
|
|
38
|
+
/** The remaining results, in their existing rank order. */
|
|
39
|
+
others: RelatedDocumentResult[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Splits an already-ranked {@link findRelatedDocuments} result list into its
|
|
43
|
+
* single top match — the "suggested next" document — and the rest, so the
|
|
44
|
+
* sidebar can render the top match more prominently than the flat list.
|
|
45
|
+
*/
|
|
46
|
+
export declare function splitTopSuggestion(results: RelatedDocumentResult[]): RelatedDocumentsSplit;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module useActiveHeading
|
|
3
|
+
* @description Scroll-spy: tracks which heading is currently "active" (in
|
|
4
|
+
* view) as the user scrolls the editor, for highlighting in a table-of-
|
|
5
|
+
* contents / outline sidebar. Mirrors the scroll-spy logic already used by
|
|
6
|
+
* {@link DynamicIslandTOC}, factored out so other TOC-style views (like the
|
|
7
|
+
* sidebar outline) can reuse it instead of re-deriving it.
|
|
8
|
+
*/
|
|
9
|
+
import { type RefObject } from 'react';
|
|
10
|
+
import type { TocEntry } from '../app-types/toc';
|
|
11
|
+
/** Minimal shape this hook needs from an editor ref — matches `TiptapEditorHandle`. */
|
|
12
|
+
export interface ActiveHeadingEditorHandle {
|
|
13
|
+
getElementByKey: (key: string) => HTMLElement | null;
|
|
14
|
+
}
|
|
15
|
+
interface HeadingTop {
|
|
16
|
+
key: string;
|
|
17
|
+
/** Heading element's top offset relative to the scroll container's top. */
|
|
18
|
+
top: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Given each visible heading's top offset (relative to the scroll
|
|
22
|
+
* container) and a threshold distance from the container's top, returns the
|
|
23
|
+
* key of the last heading whose top is at or above the threshold — i.e. the
|
|
24
|
+
* heading the reader has scrolled past most recently. Falls back to the
|
|
25
|
+
* first heading when none qualify (e.g. the reader is above the first
|
|
26
|
+
* heading), and to `null` when there are no headings at all.
|
|
27
|
+
*/
|
|
28
|
+
export declare function computeActiveHeadingKey(headings: HeadingTop[], threshold: number): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Tracks the currently active heading key while the document scrolls.
|
|
31
|
+
* Resolves heading DOM elements via `editorRef.current.getElementByKey`, so
|
|
32
|
+
* it stays in sync with whatever container the editor actually scrolls in
|
|
33
|
+
* (found by walking up from the first heading to its nearest scrollable
|
|
34
|
+
* ancestor, falling back to `window`).
|
|
35
|
+
*
|
|
36
|
+
* Returns `null` when there is no `editorRef`/no headings, or before the
|
|
37
|
+
* first scroll-position measurement.
|
|
38
|
+
*/
|
|
39
|
+
export declare function useActiveHeading(headings: TocEntry[], editorRef?: RefObject<ActiveHeadingEditorHandle | null>): string | null;
|
|
40
|
+
export {};
|