react-embed-docs 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/LICENSE +21 -0
- package/README.md +422 -0
- package/dist/client/components/Breadcrumbs.d.ts +21 -0
- package/dist/client/components/Breadcrumbs.d.ts.map +1 -0
- package/dist/client/components/Breadcrumbs.js +123 -0
- package/dist/client/components/DocsLayout.d.ts +20 -0
- package/dist/client/components/DocsLayout.d.ts.map +1 -0
- package/dist/client/components/DocsLayout.js +387 -0
- package/dist/client/components/DocumentContent.d.ts +5 -0
- package/dist/client/components/DocumentContent.d.ts.map +1 -0
- package/dist/client/components/DocumentContent.js +15 -0
- package/dist/client/components/DocumentEdit.d.ts +6 -0
- package/dist/client/components/DocumentEdit.d.ts.map +1 -0
- package/dist/client/components/DocumentEdit.js +153 -0
- package/dist/client/components/DocumentList.d.ts +5 -0
- package/dist/client/components/DocumentList.d.ts.map +1 -0
- package/dist/client/components/DocumentList.js +39 -0
- package/dist/client/components/DocumentProvider.d.ts +42 -0
- package/dist/client/components/DocumentProvider.d.ts.map +1 -0
- package/dist/client/components/DocumentProvider.js +47 -0
- package/dist/client/components/DocumentView.d.ts +6 -0
- package/dist/client/components/DocumentView.d.ts.map +1 -0
- package/dist/client/components/DocumentView.js +58 -0
- package/dist/client/components/DragOverlayItem.d.ts +5 -0
- package/dist/client/components/DragOverlayItem.d.ts.map +1 -0
- package/dist/client/components/DragOverlayItem.js +9 -0
- package/dist/client/components/EmojiPicker.d.ts +8 -0
- package/dist/client/components/EmojiPicker.d.ts.map +1 -0
- package/dist/client/components/EmojiPicker.js +48 -0
- package/dist/client/components/ExportButton.d.ts +22 -0
- package/dist/client/components/ExportButton.d.ts.map +1 -0
- package/dist/client/components/ExportButton.js +97 -0
- package/dist/client/components/Layout.d.ts +7 -0
- package/dist/client/components/Layout.d.ts.map +1 -0
- package/dist/client/components/Layout.js +172 -0
- package/dist/client/components/ReactEmbedDocs.d.ts +8 -0
- package/dist/client/components/ReactEmbedDocs.d.ts.map +1 -0
- package/dist/client/components/ReactEmbedDocs.js +8 -0
- package/dist/client/components/SearchInput.d.ts +2 -0
- package/dist/client/components/SearchInput.d.ts.map +1 -0
- package/dist/client/components/SearchInput.js +7 -0
- package/dist/client/components/Sidebar.d.ts +10 -0
- package/dist/client/components/Sidebar.d.ts.map +1 -0
- package/dist/client/components/Sidebar.js +176 -0
- package/dist/client/components/SortableTreeItem.d.ts +13 -0
- package/dist/client/components/SortableTreeItem.d.ts.map +1 -0
- package/dist/client/components/SortableTreeItem.js +24 -0
- package/dist/client/components/VersionHistory.d.ts +14 -0
- package/dist/client/components/VersionHistory.d.ts.map +1 -0
- package/dist/client/components/VersionHistory.js +102 -0
- package/dist/client/hooks/useCollaboration.d.ts +99 -0
- package/dist/client/hooks/useCollaboration.d.ts.map +1 -0
- package/dist/client/hooks/useCollaboration.js +180 -0
- package/dist/client/hooks/useDocsQuery.d.ts +84 -0
- package/dist/client/hooks/useDocsQuery.d.ts.map +1 -0
- package/dist/client/hooks/useDocsQuery.js +241 -0
- package/dist/client/hooks/useExport.d.ts +31 -0
- package/dist/client/hooks/useExport.d.ts.map +1 -0
- package/dist/client/hooks/useExport.js +66 -0
- package/dist/client/hooks/useFileUpload.d.ts +44 -0
- package/dist/client/hooks/useFileUpload.d.ts.map +1 -0
- package/dist/client/hooks/useFileUpload.js +193 -0
- package/dist/client/hooks/useSystemTheme.d.ts +2 -0
- package/dist/client/hooks/useSystemTheme.d.ts.map +1 -0
- package/dist/client/hooks/useSystemTheme.js +19 -0
- package/dist/client/hooks/useVersions.d.ts +105 -0
- package/dist/client/hooks/useVersions.d.ts.map +1 -0
- package/dist/client/hooks/useVersions.js +129 -0
- package/dist/client/index.d.ts +23 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +18 -0
- package/dist/client/lib/blocknoteTheme.d.ts +13 -0
- package/dist/client/lib/blocknoteTheme.d.ts.map +1 -0
- package/dist/client/lib/blocknoteTheme.js +76 -0
- package/dist/client/lib/path.d.ts +8 -0
- package/dist/client/lib/path.d.ts.map +1 -0
- package/dist/client/lib/path.js +30 -0
- package/dist/client/providers/DocumentProvider.d.ts +1 -0
- package/dist/client/providers/DocumentProvider.d.ts.map +1 -0
- package/dist/client/providers/DocumentProvider.js +1 -0
- package/dist/server/CollaborationService.d.ts +134 -0
- package/dist/server/CollaborationService.d.ts.map +1 -0
- package/dist/server/CollaborationService.js +307 -0
- package/dist/server/DocsService.d.ts +115 -0
- package/dist/server/DocsService.d.ts.map +1 -0
- package/dist/server/DocsService.js +512 -0
- package/dist/server/ExportService.d.ts +106 -0
- package/dist/server/ExportService.d.ts.map +1 -0
- package/dist/server/ExportService.js +501 -0
- package/dist/server/FilesService.d.ts +44 -0
- package/dist/server/FilesService.d.ts.map +1 -0
- package/dist/server/FilesService.js +78 -0
- package/dist/server/VersioningService.d.ts +112 -0
- package/dist/server/VersioningService.d.ts.map +1 -0
- package/dist/server/VersioningService.js +264 -0
- package/dist/server/db.d.ts +7 -0
- package/dist/server/db.d.ts.map +1 -0
- package/dist/server/db.js +22 -0
- package/dist/server/index.d.ts +55 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +36 -0
- package/dist/server/routes.d.ts +9 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/routes.js +483 -0
- package/dist/server/schema.d.ts +587 -0
- package/dist/server/schema.d.ts.map +1 -0
- package/dist/server/schema.js +126 -0
- package/dist/shared/types.d.ts +314 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +48 -0
- package/drizzle/migrations/0000_gray_monster_badoon.sql +88 -0
- package/drizzle/migrations/meta/0000_snapshot.json +574 -0
- package/drizzle/migrations/meta/_journal.json +13 -0
- package/package.json +109 -0
- package/styles/docs.css +981 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import type { DocumentSummary } from '../../shared/types.js';
|
|
3
|
+
import { Params } from '../lib/path.js';
|
|
4
|
+
/**
|
|
5
|
+
* Theme type for the document provider
|
|
6
|
+
*/
|
|
7
|
+
export type DocumentTheme = 'light' | 'dark';
|
|
8
|
+
/**
|
|
9
|
+
* Document context value interface
|
|
10
|
+
*/
|
|
11
|
+
export interface DocumentContextValue {
|
|
12
|
+
/** Current theme */
|
|
13
|
+
theme: DocumentTheme;
|
|
14
|
+
path: string;
|
|
15
|
+
locale: string;
|
|
16
|
+
params: Params;
|
|
17
|
+
onCreate: () => void;
|
|
18
|
+
onOpen: (path: string) => void;
|
|
19
|
+
onEdit: (slug: string) => void;
|
|
20
|
+
onDelete: (doc: DocumentSummary) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Props for DocumentProvider component
|
|
24
|
+
*/
|
|
25
|
+
export interface DocumentProviderProps {
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
theme?: DocumentTheme;
|
|
28
|
+
params: Params;
|
|
29
|
+
path: string;
|
|
30
|
+
onNavigate?: (path: string) => void;
|
|
31
|
+
locale?: 'ru' | 'en';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Provider component for document context
|
|
35
|
+
*/
|
|
36
|
+
export declare function DocumentProvider({ children, params, theme, path, onNavigate, locale, }: DocumentProviderProps): JSX.Element;
|
|
37
|
+
/**
|
|
38
|
+
* Hook to access document context
|
|
39
|
+
* @throws Error if used outside of DocumentProvider
|
|
40
|
+
*/
|
|
41
|
+
export declare function useDocument(): DocumentContextValue;
|
|
42
|
+
//# sourceMappingURL=DocumentProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentProvider.d.ts","sourceRoot":"","sources":["../../../src/client/components/DocumentProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAiB,SAAS,EAA2B,MAAM,OAAO,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvC;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAA;AAE5C;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,oBAAoB;IACpB,KAAK,EAAE,aAAa,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;CACzC;AAOD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,SAAS,CAAA;IACnB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;CACrB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,KAAe,EACf,IAAI,EACJ,UAAU,EACV,MAAa,GACd,EAAE,qBAAqB,GAAG,GAAG,CAAC,OAAO,CAyCrC;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,oBAAoB,CAQlD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Document context
|
|
6
|
+
*/
|
|
7
|
+
const DocumentContext = createContext(null);
|
|
8
|
+
/**
|
|
9
|
+
* Provider component for document context
|
|
10
|
+
*/
|
|
11
|
+
export function DocumentProvider({ children, params, theme = 'light', path, onNavigate, locale = 'en', }) {
|
|
12
|
+
console.log('params', params);
|
|
13
|
+
const onOpen = useCallback((path) => {
|
|
14
|
+
onNavigate?.(path);
|
|
15
|
+
}, [onNavigate]);
|
|
16
|
+
const onEdit = useCallback((slug) => {
|
|
17
|
+
onNavigate?.(`${slug}/edit`);
|
|
18
|
+
}, [onNavigate]);
|
|
19
|
+
const onCreate = useCallback(() => {
|
|
20
|
+
onNavigate?.(`${path}/new`);
|
|
21
|
+
}, [onNavigate]);
|
|
22
|
+
const onDelete = useCallback((doc) => {
|
|
23
|
+
// alert
|
|
24
|
+
}, []);
|
|
25
|
+
const value = {
|
|
26
|
+
theme,
|
|
27
|
+
path,
|
|
28
|
+
locale,
|
|
29
|
+
params,
|
|
30
|
+
onCreate,
|
|
31
|
+
onEdit,
|
|
32
|
+
onOpen,
|
|
33
|
+
onDelete,
|
|
34
|
+
};
|
|
35
|
+
return (_jsx(DocumentContext.Provider, { value: value, children: children }));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Hook to access document context
|
|
39
|
+
* @throws Error if used outside of DocumentProvider
|
|
40
|
+
*/
|
|
41
|
+
export function useDocument() {
|
|
42
|
+
const context = useContext(DocumentContext);
|
|
43
|
+
if (context === null) {
|
|
44
|
+
throw new Error('useDocument must be used within a DocumentProvider');
|
|
45
|
+
}
|
|
46
|
+
return context;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentView.d.ts","sourceRoot":"","sources":["../../../src/client/components/DocumentView.tsx"],"names":[],"mappings":"AAIA,OAAO,8BAA8B,CAAA;AASrC,UAAU,iBAAiB;CAAG;AAU9B,wBAAgB,YAAY,CAAC,EAAE,EAAE,iBAAiB,2CA0HjD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { BlockNoteView } from '@blocknote/mantine';
|
|
4
|
+
import '@blocknote/mantine/style.css';
|
|
5
|
+
import { useCreateBlockNote } from '@blocknote/react';
|
|
6
|
+
import { Clock, Edit, Loader2, User } from 'lucide-react';
|
|
7
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
8
|
+
import { useDocumentQuery } from '../hooks/useDocsQuery.js';
|
|
9
|
+
import { blockNoteTheme } from '../lib/blocknoteTheme.js';
|
|
10
|
+
import { useDocument } from './DocumentProvider.js';
|
|
11
|
+
import { ExportButton } from './ExportButton.js';
|
|
12
|
+
// Default content when document has no content
|
|
13
|
+
const getDefaultContent = () => [
|
|
14
|
+
{
|
|
15
|
+
type: 'paragraph',
|
|
16
|
+
content: '',
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
export function DocumentView({}) {
|
|
20
|
+
const { params, onCreate, onEdit, theme } = useDocument();
|
|
21
|
+
const { data: doc, isLoading, error } = useDocumentQuery(params.documentSlug);
|
|
22
|
+
const [hasLoaded, setHasLoaded] = useState(false);
|
|
23
|
+
// Creates a new editor instance in read-only mode
|
|
24
|
+
const editor = useCreateBlockNote({
|
|
25
|
+
initialContent: getDefaultContent(),
|
|
26
|
+
});
|
|
27
|
+
const handleEdit = useCallback(() => {
|
|
28
|
+
if (!doc)
|
|
29
|
+
return;
|
|
30
|
+
onEdit(params.slugs.join('/') + '/' + doc.slug);
|
|
31
|
+
}, [doc, onEdit]);
|
|
32
|
+
// Load document content when available
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (doc?.content && editor && !hasLoaded) {
|
|
35
|
+
try {
|
|
36
|
+
const content = typeof doc.content === 'string'
|
|
37
|
+
? JSON.parse(doc.content)
|
|
38
|
+
: doc.content;
|
|
39
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
40
|
+
editor.replaceBlocks(editor.document, content);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
console.error('Failed to parse document content:', e);
|
|
45
|
+
}
|
|
46
|
+
setHasLoaded(true);
|
|
47
|
+
}
|
|
48
|
+
}, [doc, editor, hasLoaded]);
|
|
49
|
+
if (isLoading) {
|
|
50
|
+
return (_jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: _jsx(Loader2, { className: "h-8 w-8 animate-spin text-gray-400" }) }));
|
|
51
|
+
}
|
|
52
|
+
if (error || !doc) {
|
|
53
|
+
return (_jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: _jsx("p", { className: "text-red-500", children: "Failed to load document. Please try again." }) }));
|
|
54
|
+
}
|
|
55
|
+
return (_jsxs("div", { className: "max-w-[80%] mx-auto", children: [_jsxs("div", { className: "mb-8", children: [_jsxs("div", { className: "flex items-start justify-between gap-4 mb-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [doc.emoji && (_jsx("span", { className: "text-5xl leading-none", role: "img", "aria-label": "document emoji", children: doc.emoji })), _jsx("h1", { className: "text-3xl font-bold", children: doc.title })] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [doc && (_jsx(ExportButton, { documentId: doc.id, documentTitle: doc.title })), _jsxs("button", { onClick: handleEdit, className: "px-3 py-1.5 text-sm bg-secondary text-white rounded-md hover:bg-primary flex items-center gap-2 transition-colors", children: [_jsx(Edit, { className: "h-4 w-4" }), "Edit"] })] })] }), _jsxs("div", { className: "flex items-center gap-6 text-sm text-gray-500", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "h-4 w-4" }), _jsxs("span", { children: ["Author ID: ", doc.authorId] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Clock, { className: "h-4 w-4" }), _jsxs("span", { children: ["Updated", ' ', doc.updatedAt
|
|
56
|
+
? new Date(doc.updatedAt).toLocaleDateString()
|
|
57
|
+
: 'N/A'] })] })] })] }), doc.cover && (_jsx("div", { className: "mb-8 -mx-4 sm:-mx-8 lg:-mx-12", children: _jsx("div", { className: "relative w-full max-h-80 overflow-hidden", children: _jsx("img", { src: doc.cover, alt: doc.title, className: "w-full h-full object-cover" }) }) })), _jsx("div", { children: _jsx(BlockNoteView, { editor: editor, editable: false, theme: blockNoteTheme[theme], className: "[&_.bn-editor]:p-0 [&_.bn-editor]:px-0 [&_.bn-container]:max-w-none [&_.bn-editor]:!px-0" }) })] }));
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DragOverlayItem.d.ts","sourceRoot":"","sources":["../../../src/client/components/DragOverlayItem.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGlD,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,aAAa,CAAA;CAAE,2CAyB9D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FileTextIcon, FolderOpenIcon } from 'lucide-react';
|
|
3
|
+
// Drag Overlay Item - shown while dragging
|
|
4
|
+
export function DragOverlayItem({ doc }) {
|
|
5
|
+
return (_jsxs("div", { className: [
|
|
6
|
+
'flex items-center gap-1 w-full px-2 py-1.5 text-sm rounded-md shadow-lg',
|
|
7
|
+
'bg-white dark:bg-zinc-800 border border-border opacity-90 cursor-grabbing',
|
|
8
|
+
].join(' '), style: { paddingLeft: doc.depth * 12 + 8 }, children: [_jsx("div", { className: "w-5" }), doc.emoji ? (_jsx("span", { className: "text-base shrink-0", children: doc.emoji })) : doc.hasChildren ? (_jsx(FolderOpenIcon, { className: "h-4 w-4 shrink-0 text-muted-foreground" })) : (_jsx(FileTextIcon, { className: "h-4 w-4 shrink-0 text-muted-foreground" })), _jsx("span", { className: "truncate flex-1 font-medium", children: doc.title })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface EmojiPickerProps {
|
|
2
|
+
value: string | null | undefined;
|
|
3
|
+
onChange: (emoji: string) => void;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function EmojiPicker({ value, onChange, className }: EmojiPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=EmojiPicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../src/client/components/EmojiPicker.tsx"],"names":[],"mappings":"AAIA,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAgBD,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,gBAAgB,2CAoE3E"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useRef, useEffect } from 'react';
|
|
4
|
+
// Popular emojis for documents
|
|
5
|
+
const POPULAR_EMOJIS = [
|
|
6
|
+
'📄', '📝', '📊', '📈', '📉', '📑', '📋', '📁', '📂', '🗂️',
|
|
7
|
+
'📦', '📮', '📯', '📰', '📱', '💻', '🖥️', '📀', '💿', '📼',
|
|
8
|
+
'🌸', '🌺', '🌻', '🌼', '🌷', '🌹', '🌵', '🌲', '🌳', '🌴',
|
|
9
|
+
'🍏', '🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', '🫐',
|
|
10
|
+
'⚽', '🏀', '🏈', '⚾', '🥎', '🎾', '🏐', '🏉', '🥏', '🎱',
|
|
11
|
+
'🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐',
|
|
12
|
+
'❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '❣️',
|
|
13
|
+
'⭐', '🌟', '✨', '💫', '⚡', '🔥', '💥', '☄️', '☀️', '🌤️',
|
|
14
|
+
'🔴', '🟠', '🟡', '🟢', '🔵', '🟣', '⚫', '⚪', '🟤', '🔘',
|
|
15
|
+
'✅', '❌', '⭕', '🚫', '💯', '💢', '♨️', '🚷', '🚯', '🚳',
|
|
16
|
+
];
|
|
17
|
+
export function EmojiPicker({ value, onChange, className }) {
|
|
18
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
19
|
+
const dropdownRef = useRef(null);
|
|
20
|
+
// Close dropdown when clicking outside
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
function handleClickOutside(event) {
|
|
23
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
24
|
+
setIsOpen(false);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (isOpen) {
|
|
28
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
29
|
+
}
|
|
30
|
+
return () => {
|
|
31
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
32
|
+
};
|
|
33
|
+
}, [isOpen]);
|
|
34
|
+
return (_jsxs("div", { ref: dropdownRef, className: "relative", children: [_jsx("button", { onClick: () => setIsOpen(!isOpen), className: [
|
|
35
|
+
'h-10 w-10 rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900',
|
|
36
|
+
'hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center justify-center text-xl transition-colors',
|
|
37
|
+
className,
|
|
38
|
+
].join(' '), children: value || '📄' }), isOpen && (_jsxs("div", { className: "absolute top-full left-0 mt-1 z-50 w-64 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg p-2", children: [_jsx("div", { className: "grid grid-cols-8 gap-1", children: POPULAR_EMOJIS.map((emoji) => (_jsx("button", { onClick: () => {
|
|
39
|
+
onChange(emoji);
|
|
40
|
+
setIsOpen(false);
|
|
41
|
+
}, className: [
|
|
42
|
+
'h-8 w-8 rounded hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center justify-center text-lg transition-colors',
|
|
43
|
+
value === emoji && 'bg-gray-100 dark:bg-gray-800',
|
|
44
|
+
].join(' '), children: emoji }, emoji))) }), value && (_jsx("button", { onClick: () => {
|
|
45
|
+
onChange('');
|
|
46
|
+
setIsOpen(false);
|
|
47
|
+
}, className: "mt-2 w-full py-1.5 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors", children: "Remove emoji" }))] }))] }));
|
|
48
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ExportButtonProps {
|
|
2
|
+
documentId: string;
|
|
3
|
+
documentTitle?: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
className?: string;
|
|
6
|
+
buttonClassName?: string;
|
|
7
|
+
menuClassName?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Export button with dropdown menu for different formats
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <ExportButton
|
|
15
|
+
* documentId="doc-123"
|
|
16
|
+
* documentTitle="My Document"
|
|
17
|
+
* baseUrl="/api"
|
|
18
|
+
* />
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function ExportButton({ documentId, documentTitle, baseUrl, className, buttonClassName, menuClassName, }: ExportButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=ExportButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExportButton.d.ts","sourceRoot":"","sources":["../../../src/client/components/ExportButton.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAyCD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,aAAa,EACb,OAAO,EACP,SAAc,EACd,eAAoB,EACpB,aAAkB,GACnB,EAAE,iBAAiB,2CAoHnB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import { Download, FileText, FileType, FileCode, File } from 'lucide-react';
|
|
4
|
+
import { useExport } from '../hooks/useExport.js';
|
|
5
|
+
const EXPORT_OPTIONS = [
|
|
6
|
+
{
|
|
7
|
+
format: 'docx',
|
|
8
|
+
label: 'Microsoft Word',
|
|
9
|
+
description: 'Editable document format',
|
|
10
|
+
icon: _jsx(FileText, { className: "w-4 h-4 text-blue-600" }),
|
|
11
|
+
extension: 'docx',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
format: 'pdf',
|
|
15
|
+
label: 'PDF Document',
|
|
16
|
+
description: 'Print-ready format',
|
|
17
|
+
icon: _jsx(FileType, { className: "w-4 h-4 text-red-600" }),
|
|
18
|
+
extension: 'pdf',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
format: 'html',
|
|
22
|
+
label: 'HTML Page',
|
|
23
|
+
description: 'Web page format',
|
|
24
|
+
icon: _jsx(FileCode, { className: "w-4 h-4 text-orange-600" }),
|
|
25
|
+
extension: 'html',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
format: 'markdown',
|
|
29
|
+
label: 'Markdown',
|
|
30
|
+
description: 'Plain text with formatting',
|
|
31
|
+
icon: _jsx(File, { className: "w-4 h-4 text-gray-600" }),
|
|
32
|
+
extension: 'md',
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Export button with dropdown menu for different formats
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <ExportButton
|
|
41
|
+
* documentId="doc-123"
|
|
42
|
+
* documentTitle="My Document"
|
|
43
|
+
* baseUrl="/api"
|
|
44
|
+
* />
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function ExportButton({ documentId, documentTitle, baseUrl, className = '', buttonClassName = '', menuClassName = '', }) {
|
|
48
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
49
|
+
const menuRef = useRef(null);
|
|
50
|
+
const { exportDocument, isExporting } = useExport({ baseUrl });
|
|
51
|
+
// Close menu when clicking outside
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
function handleClickOutside(event) {
|
|
54
|
+
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
55
|
+
setIsOpen(false);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
59
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
60
|
+
}, []);
|
|
61
|
+
const handleExport = async (format) => {
|
|
62
|
+
setIsOpen(false);
|
|
63
|
+
const baseName = documentTitle
|
|
64
|
+
? `${documentTitle.replace(/[^a-zA-Z0-9\s-]/g, '').replace(/\s+/g, '-')}`
|
|
65
|
+
: 'document';
|
|
66
|
+
const extension = format === 'markdown' ? 'md' : format;
|
|
67
|
+
const defaultFilename = `${baseName}.${extension}`;
|
|
68
|
+
try {
|
|
69
|
+
await exportDocument(documentId, format, defaultFilename);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : 'Export failed';
|
|
73
|
+
// Show alert for now - could be replaced with a toast notification
|
|
74
|
+
if (message.includes('docx') || message.includes('puppeteer') || message.includes('playwright')) {
|
|
75
|
+
alert(`Missing dependency: ${message}\n\nInstall with:\n${format === 'docx' ? 'npm install docx' : 'npm install puppeteer'}`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
alert(`Export failed: ${message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
return (_jsxs("div", { className: `relative inline-block ${className}`, ref: menuRef, children: [_jsxs("button", { onClick: () => setIsOpen(!isOpen), disabled: isExporting, className: `
|
|
83
|
+
inline-flex items-center gap-2 px-3 py-1.5
|
|
84
|
+
bg-white border border-gray-300 rounded-md
|
|
85
|
+
text-sm font-medium text-gray-700
|
|
86
|
+
hover:bg-gray-50 hover:border-gray-400
|
|
87
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1
|
|
88
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
89
|
+
transition-colors
|
|
90
|
+
${buttonClassName}
|
|
91
|
+
`, children: [_jsx(Download, { className: `w-4 h-4 ${isExporting ? 'animate-bounce' : ''}` }), _jsx("span", { children: "Export" }), _jsx("svg", { className: `w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx("div", { className: `
|
|
92
|
+
absolute right-0 mt-2 w-64
|
|
93
|
+
bg-white border border-gray-200 rounded-lg shadow-lg
|
|
94
|
+
z-50 overflow-hidden
|
|
95
|
+
${menuClassName}
|
|
96
|
+
`, children: _jsxs("div", { className: "py-1", children: [_jsx("div", { className: "px-3 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Export as" }), EXPORT_OPTIONS.map((option) => (_jsxs("button", { onClick: () => handleExport(option.format), disabled: isExporting, className: "\n w-full flex items-start gap-3 px-3 py-2.5\n text-left hover:bg-gray-50\n focus:outline-none focus:bg-gray-50\n disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors\n ", children: [_jsx("div", { className: "flex-shrink-0 mt-0.5", children: option.icon }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "text-sm font-medium text-gray-900", children: option.label }), _jsx("div", { className: "text-xs text-gray-500", children: option.description })] }), _jsxs("div", { className: "flex-shrink-0 text-xs text-gray-400 uppercase", children: [".", option.extension] })] }, option.format)))] }) }))] }));
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../src/client/components/Layout.tsx"],"names":[],"mappings":"AAgBA,UAAU,eAAe;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AAmJD,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,eAAe,2CA8J/D"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FileText, PanelLeftCloseIcon, PanelLeftOpenIcon, Plus, Search, } from 'lucide-react';
|
|
4
|
+
import { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { useDocumentsQuery } from '../hooks/useDocsQuery.js';
|
|
6
|
+
import { Breadcrumbs } from './Breadcrumbs.js';
|
|
7
|
+
import { useDocument } from './DocumentProvider.js';
|
|
8
|
+
import { Sidebar } from './Sidebar.js';
|
|
9
|
+
// Simple debounce hook
|
|
10
|
+
function useDebounce(value, delay) {
|
|
11
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const timer = setTimeout(() => {
|
|
14
|
+
setDebouncedValue(value);
|
|
15
|
+
}, delay);
|
|
16
|
+
return () => clearTimeout(timer);
|
|
17
|
+
}, [value, delay]);
|
|
18
|
+
return debouncedValue;
|
|
19
|
+
}
|
|
20
|
+
// Build breadcrumb path for a document
|
|
21
|
+
function buildBreadcrumb(doc, allDocs) {
|
|
22
|
+
const parts = [doc.title];
|
|
23
|
+
let current = doc;
|
|
24
|
+
while (current.parentId) {
|
|
25
|
+
const parent = allDocs.find((d) => d.id === current.parentId);
|
|
26
|
+
if (!parent)
|
|
27
|
+
break;
|
|
28
|
+
parts.unshift(parent.title);
|
|
29
|
+
current = parent;
|
|
30
|
+
}
|
|
31
|
+
return parts.join(' / ');
|
|
32
|
+
}
|
|
33
|
+
// Build full path from root to document (for URL navigation)
|
|
34
|
+
function buildDocumentPath(docId, allDocs, basePath) {
|
|
35
|
+
const pathParts = [];
|
|
36
|
+
const visited = new Set();
|
|
37
|
+
let currentId = docId;
|
|
38
|
+
while (currentId) {
|
|
39
|
+
// Prevent infinite loops from circular references
|
|
40
|
+
if (visited.has(currentId))
|
|
41
|
+
break;
|
|
42
|
+
visited.add(currentId);
|
|
43
|
+
const doc = allDocs.find((d) => d.id === currentId);
|
|
44
|
+
if (!doc)
|
|
45
|
+
break;
|
|
46
|
+
pathParts.unshift(doc.slug || doc.id);
|
|
47
|
+
currentId = doc.parentId ?? null;
|
|
48
|
+
}
|
|
49
|
+
if (pathParts.length === 0)
|
|
50
|
+
return basePath;
|
|
51
|
+
return `${basePath}/${pathParts.join('/')}`;
|
|
52
|
+
}
|
|
53
|
+
// Get all ancestor IDs of a document (for auto-expanding)
|
|
54
|
+
function getAncestorIds(docId, allDocs) {
|
|
55
|
+
const ancestors = [];
|
|
56
|
+
const visited = new Set();
|
|
57
|
+
let currentId = docId;
|
|
58
|
+
while (currentId) {
|
|
59
|
+
if (visited.has(currentId))
|
|
60
|
+
break;
|
|
61
|
+
visited.add(currentId);
|
|
62
|
+
const doc = allDocs.find((d) => d.id === currentId);
|
|
63
|
+
if (!doc)
|
|
64
|
+
break;
|
|
65
|
+
if (doc.parentId) {
|
|
66
|
+
ancestors.push(doc.parentId);
|
|
67
|
+
currentId = doc.parentId;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return ancestors;
|
|
74
|
+
}
|
|
75
|
+
// Extract text snippets from BlockNote content
|
|
76
|
+
function extractTextSnippets(content, query) {
|
|
77
|
+
if (!content || typeof content !== 'object')
|
|
78
|
+
return [];
|
|
79
|
+
const snippets = [];
|
|
80
|
+
const queryLower = query.toLowerCase();
|
|
81
|
+
const extractFromBlock = (block) => {
|
|
82
|
+
if (!block || typeof block !== 'object')
|
|
83
|
+
return '';
|
|
84
|
+
const b = block;
|
|
85
|
+
// Handle text content
|
|
86
|
+
if (b.content && Array.isArray(b.content)) {
|
|
87
|
+
return b.content
|
|
88
|
+
.map((c) => {
|
|
89
|
+
if (typeof c === 'string')
|
|
90
|
+
return c;
|
|
91
|
+
if (c && typeof c === 'object') {
|
|
92
|
+
const contentItem = c;
|
|
93
|
+
if (contentItem.text && typeof contentItem.text === 'string') {
|
|
94
|
+
return contentItem.text;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return '';
|
|
98
|
+
})
|
|
99
|
+
.join(' ');
|
|
100
|
+
}
|
|
101
|
+
// Handle children
|
|
102
|
+
if (b.children && Array.isArray(b.children)) {
|
|
103
|
+
return b.children.map(extractFromBlock).join(' ');
|
|
104
|
+
}
|
|
105
|
+
return '';
|
|
106
|
+
};
|
|
107
|
+
const contentObj = content;
|
|
108
|
+
if (contentObj.content && Array.isArray(contentObj.content)) {
|
|
109
|
+
for (const block of contentObj.content) {
|
|
110
|
+
const text = extractFromBlock(block);
|
|
111
|
+
if (text.toLowerCase().includes(queryLower)) {
|
|
112
|
+
// Find the position of the match
|
|
113
|
+
const matchIndex = text.toLowerCase().indexOf(queryLower);
|
|
114
|
+
const start = Math.max(0, matchIndex - 20);
|
|
115
|
+
const end = Math.min(text.length, matchIndex + query.length + 60);
|
|
116
|
+
let snippet = text.slice(start, end);
|
|
117
|
+
// Add ellipsis if truncated
|
|
118
|
+
if (start > 0)
|
|
119
|
+
snippet = '...' + snippet;
|
|
120
|
+
if (end < text.length)
|
|
121
|
+
snippet = snippet + '...';
|
|
122
|
+
snippets.push(snippet);
|
|
123
|
+
if (snippets.length >= 2)
|
|
124
|
+
break; // Limit to 2 snippets
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return snippets;
|
|
129
|
+
}
|
|
130
|
+
export function Layout({ children, userAvatar }) {
|
|
131
|
+
const { onOpen, onCreate, params } = useDocument();
|
|
132
|
+
const [expandedFolders, setExpandedFolders] = useState(new Set());
|
|
133
|
+
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
|
134
|
+
// Header search state
|
|
135
|
+
const [headerSearchQuery, setHeaderSearchQuery] = useState('');
|
|
136
|
+
const [showSearchResults, setShowSearchResults] = useState(false);
|
|
137
|
+
const debouncedSearchQuery = useDebounce(headerSearchQuery, 300);
|
|
138
|
+
const searchInputRef = useRef(null);
|
|
139
|
+
const { data } = useDocumentsQuery();
|
|
140
|
+
const documents = data?.documents ?? [];
|
|
141
|
+
// Search query for header search
|
|
142
|
+
const { data: searchResultsData } = useDocumentsQuery(debouncedSearchQuery.length > 0 ? { search: debouncedSearchQuery } : {});
|
|
143
|
+
const searchResults = searchResultsData?.documents ?? [];
|
|
144
|
+
// Handle click outside to close search results
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
const handleClickOutside = (event) => {
|
|
147
|
+
if (searchInputRef.current &&
|
|
148
|
+
!searchInputRef.current.contains(event.target)) {
|
|
149
|
+
setShowSearchResults(false);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
153
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
154
|
+
}, []);
|
|
155
|
+
return (_jsxs("div", { className: "flex h-screen w-full", children: [_jsx(Sidebar, { isOpen: isSidebarOpen, onToggle: setIsSidebarOpen, documents: documents }), _jsxs("main", { className: "flex-1 flex flex-col min-w-0 overflow-hidden", children: [params.mode === 'view' && (_jsxs("header", { className: "h-16 border-b border-border flex items-center justify-between px-6 shrink-0", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("button", { onClick: () => setIsSidebarOpen(!isSidebarOpen), className: "p-2 hover:bg-primary rounded-md transition-colors text-muted-foreground", title: isSidebarOpen ? 'Close sidebar' : 'Open sidebar', children: isSidebarOpen ? (_jsx(PanelLeftCloseIcon, { className: "h-6 w-6" })) : (_jsx(PanelLeftOpenIcon, { className: "h-6 w-6" })) }), _jsx(Breadcrumbs, { homeLabel: "Documents" })] }), _jsxs("div", { className: "flex items-center gap-4 py-2", children: [_jsxs("div", { ref: searchInputRef, className: "relative w-64", children: [_jsx(Search, { className: "absolute left-2 top-2.5 h-4 w-4 z-10" }), _jsx("input", { type: "text", placeholder: "Search documentation...", className: "w-full pl-8 pr-3 py-2 text-sm border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent", value: headerSearchQuery, onChange: (e) => {
|
|
156
|
+
setHeaderSearchQuery(e.target.value);
|
|
157
|
+
setShowSearchResults(e.target.value.length > 0);
|
|
158
|
+
}, onFocus: () => {
|
|
159
|
+
if (headerSearchQuery.length > 0) {
|
|
160
|
+
setShowSearchResults(true);
|
|
161
|
+
}
|
|
162
|
+
} }), showSearchResults && headerSearchQuery.length > 0 && (_jsx("div", { className: "absolute top-full left-0 right-0 mt-1 bg-background border border-border rounded-md shadow-2xl max-h-80 overflow-y-auto z-50", children: searchResults.length === 0 ? (_jsxs("div", { className: "px-4 py-3 text-sm text-gray-500", children: ["No results found for \"", headerSearchQuery, "\""] })) : (_jsx("div", { className: "py-1", children: searchResults.map((doc) => {
|
|
163
|
+
const breadcrumb = doc.parentId
|
|
164
|
+
? buildBreadcrumb(doc, documents)
|
|
165
|
+
: null;
|
|
166
|
+
return (_jsx("button", { onClick: () => {
|
|
167
|
+
onOpen(doc.id);
|
|
168
|
+
setHeaderSearchQuery('');
|
|
169
|
+
setShowSearchResults(false);
|
|
170
|
+
}, className: "w-full px-3 py-2 text-left hover:bg-primary hover:text-primary-foreground transition-colors", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("div", { className: "shrink-0 mt-0.5", children: doc.emoji ? (_jsx("span", { className: "text-base", children: doc.emoji })) : (_jsx(FileText, { className: "h-4 w-4 text-muted-foreground" })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-sm truncate", children: doc.title }), breadcrumb && (_jsx("div", { className: "text-xs text-muted-foreground truncate", children: breadcrumb }))] })] }) }, doc.id));
|
|
171
|
+
}) })) }))] }), _jsxs("button", { onClick: onCreate, className: "px-4 py-2 bg-secondary text-white text-sm rounded-md hover:bg-gray-800 flex items-center gap-2 transition-colors", children: [_jsx(Plus, { className: "h-4 w-4" }), "Create"] }), userAvatar] })] })), _jsx("div", { className: "flex-1 overflow-auto p-6", children: children })] })] }));
|
|
172
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ReactEmbedDocsProps {
|
|
2
|
+
path?: string;
|
|
3
|
+
theme?: 'light' | 'dark';
|
|
4
|
+
locale?: 'ru' | 'en';
|
|
5
|
+
onNavigate?: (path: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function ReactEmbedDocs(props: ReactEmbedDocsProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=ReactEmbedDocs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactEmbedDocs.d.ts","sourceRoot":"","sources":["../../../src/client/components/ReactEmbedDocs.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,2CAcxD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { processPath } from '../lib/path.js';
|
|
3
|
+
import { DocumentContent } from './DocumentContent.js';
|
|
4
|
+
import { DocumentProvider } from './DocumentProvider.js';
|
|
5
|
+
import { Layout } from './Layout.js';
|
|
6
|
+
export function ReactEmbedDocs(props) {
|
|
7
|
+
return (_jsx(DocumentProvider, { theme: props.theme, path: props.path ?? '', locale: props.locale, onNavigate: props.onNavigate, params: processPath(props.path), children: _jsx(Layout, { children: _jsx(DocumentContent, {}) }) }));
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchInput.d.ts","sourceRoot":"","sources":["../../../src/client/components/SearchInput.tsx"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,4CAc1B"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SearchIcon } from 'lucide-react';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
export function SearchInput() {
|
|
5
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
6
|
+
return (_jsxs("div", { className: "relative", children: [_jsx(SearchIcon, { className: "absolute left-2 top-2.5 h-4 w-4 text-gray-400" }), _jsx("input", { type: "text", placeholder: "Search docs...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full pl-8 pr-3 py-2 text-sm border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DocumentSummary } from '../../shared/types';
|
|
2
|
+
interface IProps {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
currentDocId?: string | null;
|
|
5
|
+
documents: DocumentSummary[];
|
|
6
|
+
onToggle: (isOpen: boolean) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function Sidebar({ isOpen, onToggle, documents, currentDocId }: IProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=Sidebar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/client/components/Sidebar.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAiB,MAAM,oBAAoB,CAAA;AAMnE,UAAU,MAAM;IACd,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,EAAE,eAAe,EAAE,CAAA;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;CACpC;AACD,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,MAAM,2CA0Q5E"}
|