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.
Files changed (45) hide show
  1. package/dist/Sidebar.d.ts +2 -0
  2. package/dist/SidebarContent.d.ts +7 -0
  3. package/dist/SidebarFooter.d.ts +37 -0
  4. package/dist/SidebarToolbar.d.ts +89 -0
  5. package/dist/SidebarViewMenu.d.ts +20 -0
  6. package/dist/app-types/fileSource.d.ts +124 -0
  7. package/dist/app-types/toc.d.ts +2 -0
  8. package/dist/app-ui/FileTypeIcon.d.ts +11 -0
  9. package/dist/app-ui/alert-dialog.d.ts +14 -0
  10. package/dist/app-ui/badge.d.ts +13 -0
  11. package/dist/app-ui/button.d.ts +15 -0
  12. package/dist/app-ui/context-menu.d.ts +28 -0
  13. package/dist/app-ui/dialog.d.ts +15 -0
  14. package/dist/app-ui/dropdown-menu.d.ts +25 -0
  15. package/dist/app-ui/input.d.ts +7 -0
  16. package/dist/app-ui/sheet.d.ts +13 -0
  17. package/dist/app-ui/tooltip.d.ts +7 -0
  18. package/dist/app-ui/tree.d.ts +23 -0
  19. package/dist/app-utils/file-sources/sources.d.ts +43 -0
  20. package/dist/app-utils/utils.d.ts +1 -0
  21. package/dist/dialogs/FileManagerModal.d.ts +11 -0
  22. package/dist/dialogs/filemanager-data.d.ts +17 -0
  23. package/dist/documents/DocumentTree.d.ts +59 -0
  24. package/dist/documents/defaultDocuments.d.ts +12 -0
  25. package/dist/features/ai-rewrite/AIRewriteSuggestion.d.ts +27 -0
  26. package/dist/features/ai-rewrite/rewriteModes.d.ts +37 -0
  27. package/dist/file-tree/FileTreeContextMenu.d.ts +18 -0
  28. package/dist/file-tree/documentUtils.d.ts +63 -0
  29. package/dist/file-tree/expandLevels.d.ts +32 -0
  30. package/dist/file-tree/filetree.d.ts +66 -0
  31. package/dist/file-tree/index.d.ts +9 -0
  32. package/dist/file-tree/useFileTreeOperations.d.ts +27 -0
  33. package/dist/fileSourceUtils.d.ts +2 -0
  34. package/dist/index.cjs +46 -0
  35. package/dist/index.d.ts +37 -0
  36. package/dist/index.mjs +20301 -0
  37. package/dist/layout/sidebar/index.d.ts +10 -0
  38. package/dist/layout/sidebar/panelOptions.d.ts +37 -0
  39. package/dist/layout/sidebar/types.d.ts +227 -0
  40. package/dist/search/OutlineView.d.ts +51 -0
  41. package/dist/search/relatedDocuments.d.ts +46 -0
  42. package/dist/search/useActiveHeading.d.ts +40 -0
  43. package/dist/style.css +2 -0
  44. package/dist/utils/storage.d.ts +7 -0
  45. 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 {};