papyr-react 1.0.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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/dist/blocks/WorkspaceBlock/WorkspaceBlock.d.ts +79 -0
  4. package/dist/blocks/index.d.ts +2 -0
  5. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +12 -0
  6. package/dist/components/Breadcrumbs/index.d.ts +2 -0
  7. package/dist/components/DoubleSidebarLayout/DoubleSidebarLayout.d.ts +49 -0
  8. package/dist/components/DoubleSidebarLayout/index.d.ts +2 -0
  9. package/dist/components/EmptyState/EmptyState.d.ts +31 -0
  10. package/dist/components/EmptyState/index.d.ts +2 -0
  11. package/dist/components/FileHierarchy/FileHierarchy.d.ts +19 -0
  12. package/dist/components/FileHierarchy/FileHierarchy.test.d.ts +1 -0
  13. package/dist/components/FileHierarchy/MemoizedFolderTree.d.ts +16 -0
  14. package/dist/components/FileHierarchy/index.d.ts +3 -0
  15. package/dist/components/FileHierarchy/types.d.ts +9 -0
  16. package/dist/components/FileHierarchy/utils.d.ts +5 -0
  17. package/dist/components/FileSearch/FileSearch.d.ts +27 -0
  18. package/dist/components/FileSearch/index.d.ts +2 -0
  19. package/dist/components/GraphView/GraphView.d.ts +19 -0
  20. package/dist/components/GraphView/index.d.ts +2 -0
  21. package/dist/components/HoverPreview/HoverPreview.d.ts +12 -0
  22. package/dist/components/HoverPreview/index.d.ts +2 -0
  23. package/dist/components/MiniGraph/MiniGraph.d.ts +8 -0
  24. package/dist/components/MiniGraph/MiniGraph.test.d.ts +1 -0
  25. package/dist/components/MiniGraph/index.d.ts +2 -0
  26. package/dist/components/NotePreview/NotePreviewContent.d.ts +14 -0
  27. package/dist/components/NotePreview/index.d.ts +2 -0
  28. package/dist/components/NoteViewer/NoteViewer.d.ts +15 -0
  29. package/dist/components/NoteViewer/NoteViewer.test.d.ts +1 -0
  30. package/dist/components/NoteViewer/index.d.ts +2 -0
  31. package/dist/components/SearchBar/SearchBar.d.ts +9 -0
  32. package/dist/components/SearchBar/index.d.ts +2 -0
  33. package/dist/components/SearchDropdown/SearchDropdown.d.ts +12 -0
  34. package/dist/components/SearchDropdown/index.d.ts +1 -0
  35. package/dist/components/SidebarLayout/SidebarLayout.d.ts +33 -0
  36. package/dist/components/SidebarLayout/index.d.ts +2 -0
  37. package/dist/components/TableOfContents/TableOfContents.d.ts +10 -0
  38. package/dist/components/TableOfContents/TableOfContents.test.d.ts +1 -0
  39. package/dist/components/TableOfContents/index.d.ts +1 -0
  40. package/dist/components/TagFilter/TagFilter.d.ts +9 -0
  41. package/dist/components/TagFilter/index.d.ts +2 -0
  42. package/dist/components/Toast/Toast.d.ts +11 -0
  43. package/dist/components/index.d.ts +16 -0
  44. package/dist/hooks/index.d.ts +3 -0
  45. package/dist/hooks/useActiveNote.d.ts +27 -0
  46. package/dist/hooks/useActiveNote.test.d.ts +1 -0
  47. package/dist/hooks/useNotes.d.ts +3 -0
  48. package/dist/hooks/useRoutableActiveNote.d.ts +16 -0
  49. package/dist/index.d.ts +6 -0
  50. package/dist/index.js +3178 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/providers/PapyrProvider.d.ts +24 -0
  53. package/dist/providers/index.d.ts +2 -0
  54. package/dist/style.css +1548 -0
  55. package/dist/styles/index.d.ts +0 -0
  56. package/dist/types/index.d.ts +1 -0
  57. package/dist/utils/graphUtils.d.ts +18 -0
  58. package/dist/utils/hydration.d.ts +8 -0
  59. package/dist/utils/index.d.ts +3 -0
  60. package/dist/utils/linkUtils.d.ts +10 -0
  61. package/package.json +87 -0
  62. package/src/styles/index.ts +2 -0
  63. package/src/styles/theme.css +184 -0
  64. package/style.css +1 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RayKong
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,87 @@
1
+ # papyr-react
2
+
3
+ ## Components vs. Blocks
4
+
5
+ Papyr exposes two layers, similar to shadcn/ui:
6
+
7
+ - **Components** – focused building blocks such as `FileHierarchy`, `FileSearch`, `NoteViewer`, and `TableOfContents`.
8
+ - **Blocks** – opinionated compositions that wire multiple components together. The new `WorkspaceBlock` uses `DoubleSidebarLayout` to render a file hierarchy + main + outline surface with independent scrolling.
9
+
10
+ ```tsx
11
+ import {
12
+ WorkspaceBlock,
13
+ hydrateSearchIndex,
14
+ type WorkspaceBlockProps
15
+ } from 'papyr-react'
16
+
17
+ const searchIndex = hydrateSearchIndex(serializedIndex)
18
+
19
+ const workspaceProps: WorkspaceBlockProps = {
20
+ notes,
21
+ tree,
22
+ searchIndex,
23
+ graph,
24
+ initialSlug: notes[0]?.slug
25
+ }
26
+
27
+ export function App() {
28
+ return <WorkspaceBlock {...workspaceProps} />
29
+ }
30
+ ```
31
+
32
+ Blocks accept slots so you can swap any pane:
33
+
34
+ ```tsx
35
+ <WorkspaceBlock
36
+ {...workspaceProps}
37
+ rightSidebar={({ activeNote }) => (
38
+ <CustomMetadataPanel note={activeNote} />
39
+ )}
40
+ />
41
+ ```
42
+
43
+ Use components directly when you need finer control, or drop in a block for a ready-made layout.
44
+
45
+ ## Hydrating build output
46
+
47
+ When you load JSON produced by `papyr-core`, hydrate the search index before
48
+ passing it to `PapyrProvider`:
49
+
50
+ ```ts
51
+ import { hydrateSearchIndex } from 'papyr-react'
52
+ import { type SerializedSearchIndex } from 'papyr-core/runtime'
53
+
54
+ const serialized: SerializedSearchIndex = /* build output */
55
+ const searchIndex = hydrateSearchIndex(serialized)
56
+ ```
57
+
58
+ This accepts either the serialized payload or an already hydrated index, so it
59
+ can be used safely in shared code paths.
60
+
61
+ ## Syncing with a router
62
+
63
+ Use `useRoutableActiveNote` to keep Papyr state in sync with your routing
64
+ library. Provide two callbacks: one to read the current slug from the router and
65
+ another to push updates when the user selects a different note.
66
+
67
+ ```tsx
68
+ import { useCallback } from 'react'
69
+ import { useNavigate, useParams } from 'react-router-dom'
70
+ import { useRoutableActiveNote } from 'papyr-react'
71
+
72
+ const navigate = useNavigate()
73
+ const { slug } = useParams<{ slug?: string }>()
74
+
75
+ const { activeNote, activeSlug, setActiveSlug } = useRoutableActiveNote(notes, {
76
+ getCurrentSlug: useCallback(() => slug ?? null, [slug]),
77
+ onSlugChange: useCallback(
78
+ (nextSlug) => {
79
+ navigate(nextSlug ? `/notes/${nextSlug}` : '/')
80
+ },
81
+ [navigate]
82
+ )
83
+ })
84
+ ```
85
+
86
+ The hook exposes the same API as `useActiveNote`, so existing components (e.g.
87
+ sidebar or note viewer) continue to work while the URL stays in sync.
@@ -0,0 +1,79 @@
1
+ import { FolderNode, NoteGraph, SearchIndex, Slug, WebReadyNote } from 'papyr-core/runtime';
2
+ import { ReactNode } from 'react';
3
+ import { DoubleSidebarLayoutProps } from '../../components/DoubleSidebarLayout';
4
+ import { FileHierarchyProps } from '../../components/FileHierarchy';
5
+ import { TableOfContentsProps } from '../../components/TableOfContents';
6
+
7
+ export interface WorkspaceBlockContext {
8
+ notes: WebReadyNote[];
9
+ searchIndex: SearchIndex;
10
+ graph: NoteGraph | null;
11
+ activeSlug: Slug | null;
12
+ activeNote: WebReadyNote | null;
13
+ setActiveSlug: (slug: Slug | null) => void;
14
+ setActiveNote: (note: WebReadyNote | null) => void;
15
+ }
16
+ type SlotRenderer = ReactNode | ((context: WorkspaceBlockContext) => ReactNode);
17
+ type FileHierarchyOverrides = Omit<FileHierarchyProps, 'tree' | 'onNoteClick' | 'activeSlug' | 'notes' | 'searchable' | 'searchIndex'>;
18
+ type TableOfContentsOverrides = Omit<TableOfContentsProps, 'note'>;
19
+ export interface WorkspaceBlockProps {
20
+ notes: WebReadyNote[];
21
+ tree: FolderNode[] | FolderNode;
22
+ searchIndex: SearchIndex;
23
+ graph?: NoteGraph | null;
24
+ /**
25
+ * UNCONTROLLED MODE: Initial slug for internal state management.
26
+ * Use this when you don't need to sync with external state (e.g., router).
27
+ */
28
+ initialSlug?: Slug | null;
29
+ /**
30
+ * CONTROLLED MODE: Current active slug managed by parent.
31
+ * When provided, WorkspaceBlock delegates state to parent via onActiveSlugChange.
32
+ * Use with useRoutableActiveNote for router integration.
33
+ */
34
+ activeSlug?: Slug | null;
35
+ /**
36
+ * CONTROLLED MODE: Callback when active slug should change.
37
+ * Required when using controlled mode (activeSlug prop).
38
+ */
39
+ onActiveSlugChange?: (slug: Slug | null) => void;
40
+ className?: string;
41
+ /**
42
+ * Customise layout widths. Defaults to 280px for each sidebar.
43
+ */
44
+ leftWidth?: DoubleSidebarLayoutProps['leftWidth'];
45
+ rightWidth?: DoubleSidebarLayoutProps['rightWidth'];
46
+ /**
47
+ * Render custom content for each pane. Provide either a React node or a
48
+ * function that receives the block context.
49
+ */
50
+ leftSidebar?: SlotRenderer;
51
+ main?: SlotRenderer;
52
+ rightSidebar?: SlotRenderer;
53
+ /**
54
+ * When true, omits the default table of contents from the right sidebar.
55
+ */
56
+ hideTableOfContents?: boolean;
57
+ /**
58
+ * Called whenever the active note changes.
59
+ */
60
+ onNoteChange?: (note: WebReadyNote | null) => void;
61
+ /**
62
+ * Optional overrides forwarded to the default FileHierarchy implementation.
63
+ */
64
+ fileHierarchyProps?: FileHierarchyOverrides;
65
+ /**
66
+ * Optional overrides forwarded to the default TableOfContents implementation.
67
+ */
68
+ tableOfContentsProps?: TableOfContentsOverrides;
69
+ /**
70
+ * Custom empty state rendered when no note is selected.
71
+ */
72
+ emptyState?: ReactNode;
73
+ /**
74
+ * Toggle native sidebar scrollbars. Default: hidden.
75
+ */
76
+ hideSidebarScrollbars?: boolean;
77
+ }
78
+ export declare const WorkspaceBlock: ({ notes, tree, searchIndex, graph, initialSlug, activeSlug: controlledActiveSlug, onActiveSlugChange, className, leftWidth, rightWidth, leftSidebar, main, rightSidebar, hideTableOfContents, onNoteChange, fileHierarchyProps, tableOfContentsProps, emptyState, hideSidebarScrollbars }: WorkspaceBlockProps) => import("react/jsx-runtime").JSX.Element;
79
+ export {};
@@ -0,0 +1,2 @@
1
+ export { WorkspaceBlock } from './WorkspaceBlock/WorkspaceBlock';
2
+ export type { WorkspaceBlockProps, WorkspaceBlockContext } from './WorkspaceBlock/WorkspaceBlock';
@@ -0,0 +1,12 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface BreadcrumbItem {
4
+ label: string;
5
+ href?: string;
6
+ }
7
+ export interface BreadcrumbsProps {
8
+ trail: BreadcrumbItem[];
9
+ className?: string;
10
+ separator?: string;
11
+ }
12
+ export declare const Breadcrumbs: FC<BreadcrumbsProps>;
@@ -0,0 +1,2 @@
1
+ export type { BreadcrumbItem, BreadcrumbsProps } from './Breadcrumbs';
2
+ export { Breadcrumbs } from './Breadcrumbs';
@@ -0,0 +1,49 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ type SidebarWidth = string | number;
4
+ export interface DoubleSidebarLayoutProps {
5
+ /**
6
+ * Node rendered in the left sidebar column.
7
+ */
8
+ leftSidebar: ReactNode;
9
+ /**
10
+ * Node rendered as the primary content column.
11
+ */
12
+ main: ReactNode;
13
+ /**
14
+ * Optional node rendered in the right sidebar column.
15
+ */
16
+ rightSidebar?: ReactNode;
17
+ /**
18
+ * Custom width for the left sidebar column.
19
+ * @default 280
20
+ */
21
+ leftWidth?: SidebarWidth;
22
+ /**
23
+ * Custom width for the right sidebar column.
24
+ * @default 480
25
+ */
26
+ rightWidth?: SidebarWidth;
27
+ /**
28
+ * When true (default) the layout stretches to fill the viewport height and
29
+ * constrains scrolling to the individual panes. Set to false to allow the
30
+ * page to dictate overall height/scrolling.
31
+ */
32
+ fullHeight?: boolean;
33
+ /**
34
+ * When true (default) sidebars hide native scrollbars while keeping their
35
+ * content scrollable.
36
+ */
37
+ hideSidebarScrollbars?: boolean;
38
+ /**
39
+ * Additional className forwarded to the root container.
40
+ */
41
+ className?: string;
42
+ }
43
+ /**
44
+ * Three column workspace layout with independent scrolling for each pane.
45
+ * Mirrors the behaviour of the Obsidian help site where the main pane owns the
46
+ * visible scrollbar and sidebars scroll without showing native scrollbars.
47
+ */
48
+ export declare const DoubleSidebarLayout: FC<DoubleSidebarLayoutProps>;
49
+ export {};
@@ -0,0 +1,2 @@
1
+ export { DoubleSidebarLayout } from './DoubleSidebarLayout';
2
+ export type { DoubleSidebarLayoutProps } from './DoubleSidebarLayout';
@@ -0,0 +1,31 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ export interface EmptyStateProps {
4
+ icon?: ReactNode;
5
+ title: string;
6
+ description?: string;
7
+ children?: ReactNode;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * Generic empty state component for displaying placeholder content
12
+ * when no data is available.
13
+ *
14
+ * @example
15
+ * // Simple usage
16
+ * <EmptyState
17
+ * title="No notes found"
18
+ * description="Add some markdown files to get started"
19
+ * />
20
+ *
21
+ * @example
22
+ * // With icon and custom content
23
+ * <EmptyState
24
+ * icon="📝"
25
+ * title="No notes selected"
26
+ * description="Choose a note from the sidebar"
27
+ * >
28
+ * <button>Create New Note</button>
29
+ * </EmptyState>
30
+ */
31
+ export declare const EmptyState: FC<EmptyStateProps>;
@@ -0,0 +1,2 @@
1
+ export { EmptyState } from './EmptyState';
2
+ export type { EmptyStateProps } from './EmptyState';
@@ -0,0 +1,19 @@
1
+ import { FC } from 'react';
2
+ import { FolderNode, Slug, SearchIndex } from 'papyr-core/runtime';
3
+ import { NoteLinkRenderer } from './types';
4
+
5
+ export interface FileHierarchyProps {
6
+ tree: FolderNode[] | FolderNode;
7
+ className?: string;
8
+ onNoteClick?: (slug: Slug) => void;
9
+ renderNoteLink?: NoteLinkRenderer;
10
+ activeSlug?: string;
11
+ notes?: Array<{
12
+ slug: string;
13
+ title?: string;
14
+ }>;
15
+ searchable?: boolean;
16
+ searchIndex?: SearchIndex;
17
+ searchPlaceholder?: string;
18
+ }
19
+ export declare const FileHierarchy: FC<FileHierarchyProps>;
@@ -0,0 +1,16 @@
1
+ import { FC } from 'react';
2
+ import { FolderNode, Slug } from 'papyr-core/runtime';
3
+ import { NoteLinkRenderer } from './types';
4
+ import { NoteTitleMap } from './utils';
5
+
6
+ export interface MemoizedFolderTreeProps {
7
+ node: FolderNode;
8
+ onNoteClick?: (slug: Slug) => void;
9
+ renderNoteLink?: NoteLinkRenderer;
10
+ activeSlug?: string;
11
+ noteTitleMap?: NoteTitleMap;
12
+ depth?: number;
13
+ isSearching?: boolean;
14
+ searchQuery?: string;
15
+ }
16
+ export declare const MemoizedFolderTree: FC<MemoizedFolderTreeProps>;
@@ -0,0 +1,3 @@
1
+ export type { FileHierarchyProps } from './FileHierarchy';
2
+ export type { NoteLinkRenderContext, NoteLinkRenderer } from './types';
3
+ export { FileHierarchy } from './FileHierarchy';
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ import { Slug } from 'papyr-core/runtime';
3
+
4
+ export interface NoteLinkRenderContext {
5
+ onClick?: () => void;
6
+ isActive: boolean;
7
+ isSearchMatch: boolean;
8
+ }
9
+ export type NoteLinkRenderer = (slug: Slug, context: NoteLinkRenderContext) => ReactNode;
@@ -0,0 +1,5 @@
1
+ import { Slug } from 'papyr-core/runtime';
2
+
3
+ export type NoteTitleMap = Record<string, string | undefined>;
4
+ export declare const formatFolderName: (name: string) => string;
5
+ export declare const getNoteDisplayText: (slug: Slug, noteTitleMap?: NoteTitleMap) => string;
@@ -0,0 +1,27 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface FileSearchProps {
4
+ query: string;
5
+ onChange: (query: string) => void;
6
+ placeholder?: string;
7
+ className?: string;
8
+ /** When true, applies compact styling suitable for sidebars */
9
+ compact?: boolean;
10
+ }
11
+ /**
12
+ * Standalone search input component for filtering files/notes.
13
+ * Can be used independently or embedded in other components like FileHierarchy.
14
+ *
15
+ * @example
16
+ * // Standalone usage
17
+ * <FileSearch query={query} onChange={setQuery} />
18
+ *
19
+ * @example
20
+ * // In sidebar with compact styling
21
+ * <FileSearch query={query} onChange={setQuery} compact />
22
+ *
23
+ * @example
24
+ * // Custom styling
25
+ * <FileSearch query={query} onChange={setQuery} className="my-search" />
26
+ */
27
+ export declare const FileSearch: FC<FileSearchProps>;
@@ -0,0 +1,2 @@
1
+ export { FileSearch } from './FileSearch';
2
+ export type { FileSearchProps } from './FileSearch';
@@ -0,0 +1,19 @@
1
+ import { GraphNode, NoteGraph, Slug } from 'papyr-core/runtime';
2
+
3
+ interface GraphCanvasProps {
4
+ graph: NoteGraph | null;
5
+ className?: string;
6
+ filterNode?: (node: GraphNode) => boolean;
7
+ focusPredicate?: (node: GraphNode) => boolean;
8
+ nodeColor?: (node: GraphNode) => string;
9
+ zoomExtent?: [number, number];
10
+ focusNodeId?: string | null;
11
+ onNodeSelect?: (node: GraphNode) => void;
12
+ }
13
+ export interface GraphViewProps extends GraphCanvasProps {
14
+ activeSlug?: Slug | null;
15
+ showFullscreenToggle?: boolean;
16
+ fullscreenTitle?: string;
17
+ }
18
+ export declare const GraphView: ({ graph, className, activeSlug, filterNode, focusPredicate, nodeColor, zoomExtent, onNodeSelect, showFullscreenToggle, fullscreenTitle }: GraphViewProps) => import("react/jsx-runtime").JSX.Element;
19
+ export {};
@@ -0,0 +1,2 @@
1
+ export type { GraphViewProps } from './GraphView';
2
+ export { GraphView } from './GraphView';
@@ -0,0 +1,12 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ export interface HoverPreviewProps {
4
+ trigger: ReactNode;
5
+ renderPreview: () => ReactNode;
6
+ /**
7
+ * When true, applies the default dotted underline styling to visually indicate the trigger is interactive.
8
+ * Set to false when the supplied trigger already communicates interactivity (e.g. buttons, links).
9
+ */
10
+ highlightTrigger?: boolean;
11
+ }
12
+ export declare const HoverPreview: FC<HoverPreviewProps>;
@@ -0,0 +1,2 @@
1
+ export type { HoverPreviewProps } from './HoverPreview';
2
+ export { HoverPreview } from './HoverPreview';
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+ import { NoteGraph } from 'papyr-core/runtime';
3
+
4
+ export interface MiniGraphProps {
5
+ graph: NoteGraph;
6
+ className?: string;
7
+ }
8
+ export declare const MiniGraph: FC<MiniGraphProps>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export type { MiniGraphProps } from './MiniGraph';
2
+ export { MiniGraph } from './MiniGraph';
@@ -0,0 +1,14 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface NotePreviewData {
4
+ slug: string;
5
+ title?: string | null;
6
+ excerpt?: string | null;
7
+ description?: string | null;
8
+ tags?: string[];
9
+ }
10
+ interface NotePreviewContentProps {
11
+ note: NotePreviewData;
12
+ }
13
+ export declare const NotePreviewContent: FC<NotePreviewContentProps>;
14
+ export {};
@@ -0,0 +1,2 @@
1
+ export { NotePreviewContent } from './NotePreviewContent';
2
+ export type { NotePreviewData } from './NotePreviewContent';
@@ -0,0 +1,15 @@
1
+ import { FC, ReactNode } from 'react';
2
+ import { WebReadyNote, Slug } from 'papyr-core/runtime';
3
+
4
+ export interface NoteViewerNavigateOptions {
5
+ fragment?: string;
6
+ }
7
+ export interface NoteViewerProps {
8
+ note: WebReadyNote | null;
9
+ emptyState?: ReactNode;
10
+ onNavigateNote?: (slug: Slug, options?: NoteViewerNavigateOptions) => void;
11
+ }
12
+ /**
13
+ * Rich note renderer with link previews, breadcrumbs, and metadata.
14
+ */
15
+ export declare const NoteViewer: FC<NoteViewerProps>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export type { NoteViewerProps } from './NoteViewer';
2
+ export { NoteViewer } from './NoteViewer';
@@ -0,0 +1,9 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface SearchBarProps {
4
+ query: string;
5
+ onQueryChange: (value: string) => void;
6
+ placeholder?: string;
7
+ className?: string;
8
+ }
9
+ export declare const SearchBar: FC<SearchBarProps>;
@@ -0,0 +1,2 @@
1
+ export type { SearchBarProps } from './SearchBar';
2
+ export { SearchBar } from './SearchBar';
@@ -0,0 +1,12 @@
1
+ import { FC } from 'react';
2
+ import { SearchIndex } from 'papyr-core/runtime';
3
+
4
+ export interface SearchDropdownProps {
5
+ searchIndex: SearchIndex;
6
+ onResultClick: (slug: string) => void;
7
+ placeholder?: string;
8
+ className?: string;
9
+ maxResults?: number;
10
+ showCategories?: boolean;
11
+ }
12
+ export declare const SearchDropdown: FC<SearchDropdownProps>;
@@ -0,0 +1 @@
1
+ export * from './SearchDropdown';
@@ -0,0 +1,33 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ export interface SidebarLayoutProps {
4
+ sidebar: ReactNode;
5
+ main: ReactNode;
6
+ className?: string;
7
+ sidebarWidth?: string | number;
8
+ /**
9
+ * When true (default), the layout fills the viewport height and scrolls the main pane.
10
+ * Set to false to allow the page to control scrolling.
11
+ */
12
+ fullHeight?: boolean;
13
+ }
14
+ /**
15
+ * Common two-column layout with sidebar and main content area.
16
+ * Provides a responsive, full-height layout pattern.
17
+ *
18
+ * @example
19
+ * // Basic usage
20
+ * <SidebarLayout
21
+ * sidebar={<FileHierarchy tree={tree} />}
22
+ * main={<NoteViewer note={note} />}
23
+ * />
24
+ *
25
+ * @example
26
+ * // Custom sidebar width
27
+ * <SidebarLayout
28
+ * sidebar={<FileHierarchy tree={tree} />}
29
+ * main={<NoteViewer note={note} />}
30
+ * sidebarWidth={320}
31
+ * />
32
+ */
33
+ export declare const SidebarLayout: FC<SidebarLayoutProps>;
@@ -0,0 +1,2 @@
1
+ export { SidebarLayout } from './SidebarLayout';
2
+ export type { SidebarLayoutProps } from './SidebarLayout';
@@ -0,0 +1,10 @@
1
+ import { FC } from 'react';
2
+ import { WebReadyNote, Heading } from 'papyr-core/runtime';
3
+
4
+ export interface TableOfContentsProps {
5
+ note: WebReadyNote | null;
6
+ className?: string;
7
+ title?: string;
8
+ onNavigate?: (heading: Heading) => void;
9
+ }
10
+ export declare const TableOfContents: FC<TableOfContentsProps>;
@@ -0,0 +1 @@
1
+ export * from './TableOfContents';
@@ -0,0 +1,9 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface TagFilterProps {
4
+ availableTags: string[];
5
+ selectedTags: string[];
6
+ onChange: (tags: string[]) => void;
7
+ className?: string;
8
+ }
9
+ export declare const TagFilter: FC<TagFilterProps>;
@@ -0,0 +1,2 @@
1
+ export type { TagFilterProps } from './TagFilter';
2
+ export { TagFilter } from './TagFilter';
@@ -0,0 +1,11 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface ToastDescriptor {
4
+ id: string;
5
+ message: string;
6
+ }
7
+ export interface ToastContainerProps {
8
+ toasts: ToastDescriptor[];
9
+ onDismiss: (id: string) => void;
10
+ }
11
+ export declare const ToastContainer: FC<ToastContainerProps>;
@@ -0,0 +1,16 @@
1
+ export * from './GraphView';
2
+ export * from './MiniGraph';
3
+ export * from './NoteViewer';
4
+ export * from './SearchBar';
5
+ export * from './Breadcrumbs';
6
+ export * from './HoverPreview';
7
+ export * from './NotePreview';
8
+ export * from './TagFilter';
9
+ export * from './FileHierarchy';
10
+ export * from './FileSearch';
11
+ export * from './EmptyState';
12
+ export * from './SidebarLayout';
13
+ export * from './DoubleSidebarLayout';
14
+ export * from './SearchDropdown';
15
+ export * from './TableOfContents';
16
+ export * from './Toast/Toast';
@@ -0,0 +1,3 @@
1
+ export * from './useNotes';
2
+ export * from './useActiveNote';
3
+ export * from './useRoutableActiveNote';