react-embed-docs 0.1.0 → 0.2.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 (29) hide show
  1. package/dist/client/components/Breadcrumbs.d.ts.map +1 -1
  2. package/dist/client/components/Breadcrumbs.js +9 -6
  3. package/dist/client/components/DocumentEdit.d.ts.map +1 -1
  4. package/dist/client/components/DocumentEdit.js +6 -4
  5. package/dist/client/components/DocumentList.d.ts.map +1 -1
  6. package/dist/client/components/DocumentList.js +16 -8
  7. package/dist/client/components/DocumentView.d.ts.map +1 -1
  8. package/dist/client/components/DocumentView.js +4 -2
  9. package/dist/client/components/EmojiPicker.d.ts.map +1 -1
  10. package/dist/client/components/EmojiPicker.js +3 -1
  11. package/dist/client/components/ExportButton.d.ts.map +1 -1
  12. package/dist/client/components/ExportButton.js +14 -12
  13. package/dist/client/components/Layout.d.ts.map +1 -1
  14. package/dist/client/components/Layout.js +5 -3
  15. package/dist/client/components/ReactEmbedDocs.d.ts.map +1 -1
  16. package/dist/client/components/ReactEmbedDocs.js +2 -1
  17. package/dist/client/components/Sidebar.d.ts.map +1 -1
  18. package/dist/client/components/Sidebar.js +4 -2
  19. package/dist/client/components/VersionHistory.d.ts.map +1 -1
  20. package/dist/client/components/VersionHistory.js +13 -11
  21. package/dist/client/hooks/useTranslation.d.ts +13 -0
  22. package/dist/client/hooks/useTranslation.d.ts.map +1 -0
  23. package/dist/client/hooks/useTranslation.js +27 -0
  24. package/dist/client/index.d.ts +1 -0
  25. package/dist/client/index.d.ts.map +1 -1
  26. package/dist/client/index.js +1 -0
  27. package/dist/client/locales/en.json +82 -0
  28. package/dist/client/locales/ru.json +82 -0
  29. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Breadcrumbs.d.ts","sourceRoot":"","sources":["../../../src/client/components/Breadcrumbs.tsx"],"names":[],"mappings":"AAKA,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,UAAU,gBAAgB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;IAC3B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwCD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,SAAS,EACT,UAAU,EACV,SAAkB,EAClB,SAAc,GACf,EAAE,gBAAgB,2CAmLlB"}
1
+ {"version":3,"file":"Breadcrumbs.d.ts","sourceRoot":"","sources":["../../../src/client/components/Breadcrumbs.tsx"],"names":[],"mappings":"AAMA,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,UAAU,gBAAgB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;IAC3B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwCD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAc,GACf,EAAE,gBAAgB,2CAqLlB"}
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ChevronRight, Home, Loader2 } from 'lucide-react';
4
4
  import { useEffect, useMemo, useState } from 'react';
5
+ import { useTranslation } from '../hooks/useTranslation.js';
5
6
  // Fetch breadcrumbs from API
6
7
  async function fetchBreadcrumbs(docId) {
7
8
  const response = await fetch(`/api/docs/${docId}/breadcrumbs`);
@@ -31,7 +32,9 @@ function buildPath(docId, allDocs, basePath = '/docs') {
31
32
  return basePath;
32
33
  return `${basePath}/${pathParts.join('/')}`;
33
34
  }
34
- export function Breadcrumbs({ docId, documents, onNavigate, homeLabel = 'Home', className = '', }) {
35
+ export function Breadcrumbs({ docId, documents, onNavigate, homeLabel, className = '', }) {
36
+ const { t } = useTranslation();
37
+ const label = homeLabel || t('breadcrumbs.home');
35
38
  const [fetchedBreadcrumbs, setFetchedBreadcrumbs] = useState([]);
36
39
  const [isLoading, setIsLoading] = useState(false);
37
40
  const [error, setError] = useState(null);
@@ -104,18 +107,18 @@ export function Breadcrumbs({ docId, documents, onNavigate, homeLabel = 'Home',
104
107
  }
105
108
  };
106
109
  if (!docId) {
107
- return (_jsx("nav", { className: `flex items-center gap-1 text-sm ${className}`, children: _jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 text-gray-500 hover:text-gray-700 transition-colors", children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { children: homeLabel })] }) }));
110
+ return (_jsx("nav", { className: `flex items-center gap-1 text-sm ${className}`, children: _jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 text-gray-500 hover:text-gray-700 transition-colors", children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { children: label })] }) }));
108
111
  }
109
112
  if (isLoading && !documents) {
110
- return (_jsxs("div", { className: `flex items-center gap-2 text-sm text-gray-400 ${className}`, children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), _jsx("span", { children: "Loading..." })] }));
113
+ return (_jsxs("div", { className: `flex items-center gap-2 text-sm text-gray-400 ${className}`, children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), _jsx("span", { children: t('breadcrumbs.loading') })] }));
111
114
  }
112
115
  if (error && !documents) {
113
- return (_jsxs("div", { className: `flex items-center gap-1 text-sm text-gray-500 ${className}`, children: [_jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 hover:text-gray-700 transition-colors", children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { children: homeLabel })] }), _jsx(ChevronRight, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-gray-400", children: "Error loading path" })] }));
116
+ return (_jsxs("div", { className: `flex items-center gap-1 text-sm text-gray-500 ${className}`, children: [_jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 hover:text-gray-700 transition-colors", children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { children: label })] }), _jsx(ChevronRight, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-gray-400", children: t('breadcrumbs.error') })] }));
114
117
  }
115
118
  if (breadcrumbs.length === 0) {
116
- return (_jsx("nav", { className: `flex items-center gap-1 text-sm ${className}`, children: _jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 text-gray-500 hover:text-gray-700 transition-colors", children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { children: homeLabel })] }) }));
119
+ return (_jsx("nav", { className: `flex items-center gap-1 text-sm ${className}`, children: _jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 text-gray-500 hover:text-gray-700 transition-colors", children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { children: label })] }) }));
117
120
  }
118
- return (_jsxs("nav", { className: `flex items-center gap-1 text-sm ${className}`, children: [_jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 text-gray-500 hover:text-gray-700 transition-colors shrink-0", title: homeLabel, children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: homeLabel })] }), breadcrumbs.map((doc, index) => {
121
+ return (_jsxs("nav", { className: `flex items-center gap-1 text-sm ${className}`, children: [_jsxs("button", { onClick: () => handleNavigate(null), className: "flex items-center gap-1.5 text-gray-500 hover:text-gray-700 transition-colors shrink-0", title: label, children: [_jsx(Home, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: label })] }), breadcrumbs.map((doc, index) => {
119
122
  const isLast = index === breadcrumbs.length - 1;
120
123
  const displayTitle = doc.title || 'Untitled';
121
124
  return (_jsxs("span", { className: "flex items-center gap-1 min-w-0", children: [_jsx(ChevronRight, { className: "h-4 w-4 text-gray-400 shrink-0" }), isLast ? (_jsxs("span", { className: "font-medium text-gray-900 truncate max-w-[200px] sm:max-w-[300px] md:max-w-[400px]", title: displayTitle, children: [doc.emoji && _jsx("span", { className: "mr-1", children: doc.emoji }), displayTitle] })) : (_jsxs("button", { onClick: () => handleNavigate(doc.id), className: "text-gray-500 hover:text-gray-700 transition-colors truncate max-w-[150px] sm:max-w-[200px]", title: displayTitle, children: [doc.emoji && _jsx("span", { className: "mr-1", children: doc.emoji }), displayTitle] }))] }, doc.id));
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentEdit.d.ts","sourceRoot":"","sources":["../../../src/client/components/DocumentEdit.tsx"],"names":[],"mappings":"AAIA,OAAO,8BAA8B,CAAA;AAerC,UAAU,iBAAiB;CAAG;AAqB9B,wBAAgB,YAAY,CAAC,EAAE,EAAE,iBAAiB,2CA6PjD"}
1
+ {"version":3,"file":"DocumentEdit.d.ts","sourceRoot":"","sources":["../../../src/client/components/DocumentEdit.tsx"],"names":[],"mappings":"AAIA,OAAO,8BAA8B,CAAA;AAgBrC,UAAU,iBAAiB;CAAG;AAqB9B,wBAAgB,YAAY,CAAC,EAAE,EAAE,iBAAiB,2CA8PjD"}
@@ -8,6 +8,7 @@ import { useEffect, useState } from 'react';
8
8
  import { useCreateDocumentMutation, useDocumentQuery, useUpdateDocumentMutation, } from '../hooks/useDocsQuery.js';
9
9
  import { useFileUpload } from '../hooks/useFileUpload.js';
10
10
  import { blockNoteTheme } from '../lib/blocknoteTheme.js';
11
+ import { useTranslation } from '../hooks/useTranslation.js';
11
12
  import { useDocument } from './DocumentProvider.js';
12
13
  import { EmojiPicker } from './EmojiPicker.js';
13
14
  // Default initial content for new documents
@@ -27,6 +28,7 @@ const generateSlug = (title) => {
27
28
  };
28
29
  export function DocumentEdit({}) {
29
30
  const { onOpen, onEdit, theme, params } = useDocument();
31
+ const { t } = useTranslation();
30
32
  const isNewDocument = params.documentSlug === undefined;
31
33
  // Fetch existing document data if editing
32
34
  const { data: existingDoc, isLoading: isLoadingDoc } = useDocumentQuery(params.documentSlug);
@@ -78,7 +80,7 @@ export function DocumentEdit({}) {
78
80
  };
79
81
  const handleSave = async () => {
80
82
  if (!title.trim()) {
81
- alert('Please enter a document title');
83
+ alert(t('editor.titleRequired'));
82
84
  return;
83
85
  }
84
86
  setIsSaving(true);
@@ -114,7 +116,7 @@ export function DocumentEdit({}) {
114
116
  }
115
117
  catch (error) {
116
118
  console.error('Failed to save document:', error);
117
- alert('Failed to save document. Please try again.');
119
+ alert(t('editor.saveFailed'));
118
120
  }
119
121
  finally {
120
122
  setIsSaving(false);
@@ -147,7 +149,7 @@ export function DocumentEdit({}) {
147
149
  if (!isNewDocument && isLoadingDoc) {
148
150
  return (_jsx("div", { className: "flex items-center justify-center min-h-100", children: _jsx(Loader2, { className: "h-8 w-8 animate-spin text-gray-400" }) }));
149
151
  }
150
- return (_jsxs("div", { className: "mx-auto h-full flex flex-col", children: [_jsxs("div", { className: "mb-6 shrink-0", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-2 flex-1", children: [_jsx(EmojiPicker, { value: emoji, onChange: setEmoji }), _jsx("input", { type: "text", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "Document title", className: "text-2xl font-bold bg-transparent border-none outline-none px-0 flex-1 placeholder:text-gray-400" })] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [!isNewDocument && existingDoc && (_jsxs("button", { onClick: handleView, className: "px-3 py-1.5 text-sm border border-border rounded-md hover:bg-gray-50 flex items-center gap-2 transition-colors", children: [_jsx(Eye, { className: "h-4 w-4" }), "View"] })), _jsx("button", { onClick: handleSave, disabled: isSaving || createMutation.isPending || updateMutation.isPending, className: "px-3 py-1.5 text-sm bg-primary text-white rounded-md hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2 transition-colors", children: isSaving ||
152
+ return (_jsxs("div", { className: "mx-auto h-full flex flex-col", children: [_jsxs("div", { className: "mb-6 shrink-0", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-2 flex-1", children: [_jsx(EmojiPicker, { value: emoji, onChange: setEmoji }), _jsx("input", { type: "text", value: title, onChange: (e) => setTitle(e.target.value), placeholder: t('editor.titlePlaceholder'), className: "text-2xl font-bold bg-transparent border-none outline-none px-0 flex-1 placeholder:text-gray-400" })] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [!isNewDocument && existingDoc && (_jsxs("button", { onClick: handleView, className: "px-3 py-1.5 text-sm border border-border rounded-md hover:bg-gray-50 flex items-center gap-2 transition-colors", children: [_jsx(Eye, { className: "h-4 w-4" }), t('editor.view')] })), _jsx("button", { onClick: handleSave, disabled: isSaving || createMutation.isPending || updateMutation.isPending, className: "px-3 py-1.5 text-sm bg-primary text-white rounded-md hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2 transition-colors", children: isSaving ||
151
153
  createMutation.isPending ||
152
- updateMutation.isPending ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Saving..."] })) : (_jsxs(_Fragment, { children: [_jsx(Save, { className: "h-4 w-4" }), isNewDocument ? 'Create' : 'Save'] })) })] })] }), _jsxs("div", { className: "text-sm text-gray-500 mt-1", children: ["Slug: ", generateSlug(title)] })] }), _jsx("div", { className: "mb-6 shrink-0", children: cover ? (_jsxs("div", { className: "relative w-full h-80 rounded-lg overflow-hidden bg-gray-100", children: [_jsx("img", { src: cover, alt: "Cover", className: "w-full h-full object-cover" }), _jsx("button", { onClick: () => setCover(null), className: "absolute top-2 right-2 p-1.5 bg-secondary hover:bg-white rounded-md shadow-sm transition-colors", title: "Remove cover", children: _jsx(X, { className: "h-4 w-4" }) })] })) : (_jsxs("div", { onDrop: handleDrop, onDragOver: handleDragOver, className: "relative w-full h-32 border-2 border-dashed border-border rounded-lg bg-secondary hover:bg-gray-100 transition-colors cursor-pointer", children: [_jsx("input", { type: "file", accept: "image/jpeg,image/png,image/gif,image/webp", onChange: handleFileInput, className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer" }), _jsx("div", { className: "flex flex-col items-center justify-center h-full gap-2 text-gray-400", children: isUploadingCover ? (_jsx(Loader2, { className: "h-6 w-6 animate-spin" })) : (_jsxs(_Fragment, { children: [_jsx(ImageIcon, { className: "h-6 w-6" }), _jsx("span", { className: "text-sm", children: "Drag & drop cover image here or click to upload" }), _jsx("span", { className: "text-xs text-gray-400", children: "JPEG, PNG, GIF, WebP up to 5MB" })] })) })] })) }), _jsx("div", { className: "flex-1 min-h-0 overflow-auto", children: _jsx("div", { className: "h-full", children: _jsx(BlockNoteView, { editor: editor, theme: blockNoteTheme[theme], className: "h-full" }) }) })] }));
154
+ updateMutation.isPending ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), t('editor.saving')] })) : (_jsxs(_Fragment, { children: [_jsx(Save, { className: "h-4 w-4" }), isNewDocument ? t('editor.create') : t('editor.save')] })) })] })] }), _jsxs("div", { className: "text-sm text-gray-500 mt-1", children: [t('editor.slug'), ": ", generateSlug(title)] })] }), _jsx("div", { className: "mb-6 shrink-0", children: cover ? (_jsxs("div", { className: "relative w-full h-80 rounded-lg overflow-hidden bg-gray-100", children: [_jsx("img", { src: cover, alt: t('editor.coverAlt'), className: "w-full h-full object-cover" }), _jsx("button", { onClick: () => setCover(null), className: "absolute top-2 right-2 p-1.5 bg-secondary hover:bg-white rounded-md shadow-sm transition-colors", title: t('editor.removeCover'), children: _jsx(X, { className: "h-4 w-4" }) })] })) : (_jsxs("div", { onDrop: handleDrop, onDragOver: handleDragOver, className: "relative w-full h-32 border-2 border-dashed border-border rounded-lg bg-secondary hover:bg-gray-100 transition-colors cursor-pointer", children: [_jsx("input", { type: "file", accept: "image/jpeg,image/png,image/gif,image/webp", onChange: handleFileInput, className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer" }), _jsx("div", { className: "flex flex-col items-center justify-center h-full gap-2 text-gray-400", children: isUploadingCover ? (_jsx(Loader2, { className: "h-6 w-6 animate-spin" })) : (_jsxs(_Fragment, { children: [_jsx(ImageIcon, { className: "h-6 w-6" }), _jsx("span", { className: "text-sm", children: t('editor.coverDropzone') }), _jsx("span", { className: "text-xs text-gray-400", children: t('editor.coverFormats') })] })) })] })) }), _jsx("div", { className: "flex-1 min-h-0 overflow-auto", children: _jsx("div", { className: "h-full", children: _jsx(BlockNoteView, { editor: editor, theme: blockNoteTheme[theme], className: "h-full" }) }) })] }));
153
155
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentList.d.ts","sourceRoot":"","sources":["../../../src/client/components/DocumentList.tsx"],"names":[],"mappings":"AAiBA,UAAU,iBAAiB;CAAG;AAuB9B,wBAAgB,YAAY,CAAC,EAAE,EAAE,iBAAiB,2CAiIjD"}
1
+ {"version":3,"file":"DocumentList.d.ts","sourceRoot":"","sources":["../../../src/client/components/DocumentList.tsx"],"names":[],"mappings":"AAkBA,UAAU,iBAAiB;CAAG;AAgC9B,wBAAgB,YAAY,CAAC,EAAE,EAAE,iBAAiB,2CAiIjD"}
@@ -2,8 +2,9 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { FileText, Folder, Plus } from 'lucide-react';
4
4
  import { useDocumentsQuery } from '../hooks/useDocsQuery.js';
5
+ import { useTranslation } from '../hooks/useTranslation.js';
5
6
  import { useDocument } from './DocumentProvider.js';
6
- function formatDistanceToNow(date) {
7
+ function formatDistanceToNow(date, t) {
7
8
  const now = new Date();
8
9
  const diffMs = now.getTime() - new Date(date).getTime();
9
10
  const diffSec = Math.round(diffMs / 1000);
@@ -11,16 +12,22 @@ function formatDistanceToNow(date) {
11
12
  const diffHour = Math.round(diffMin / 60);
12
13
  const diffDay = Math.round(diffHour / 24);
13
14
  if (diffSec < 60) {
14
- return 'just now';
15
+ return t('docList.justNow');
15
16
  }
16
17
  else if (diffMin < 60) {
17
- return `${diffMin} minute${diffMin === 1 ? '' : 's'} ago`;
18
+ return diffMin === 1
19
+ ? t('docList.minuteAgo', { n: diffMin })
20
+ : t('docList.minutesAgo', { n: diffMin });
18
21
  }
19
22
  else if (diffHour < 24) {
20
- return `${diffHour} hour${diffHour === 1 ? '' : 's'} ago`;
23
+ return diffHour === 1
24
+ ? t('docList.hourAgo', { n: diffHour })
25
+ : t('docList.hoursAgo', { n: diffHour });
21
26
  }
22
27
  else if (diffDay < 7) {
23
- return `${diffDay} day${diffDay === 1 ? '' : 's'} ago`;
28
+ return diffDay === 1
29
+ ? t('docList.dayAgo', { n: diffDay })
30
+ : t('docList.daysAgo', { n: diffDay });
24
31
  }
25
32
  else {
26
33
  return date.toLocaleDateString();
@@ -28,12 +35,13 @@ function formatDistanceToNow(date) {
28
35
  }
29
36
  export function DocumentList({}) {
30
37
  const { onOpen, onCreate } = useDocument();
38
+ const { t } = useTranslation();
31
39
  const { data, isLoading, error } = useDocumentsQuery();
32
40
  const documents = data?.documents ?? [];
33
41
  // Separate documents into folders (documents with children) and regular documents
34
42
  const folders = documents.filter((doc) => documents.some((d) => d.parentId === doc.id));
35
43
  const regularDocuments = documents.filter((doc) => !doc.parentId && !folders.some((f) => f.id === doc.id));
36
- return (_jsxs("div", { className: "space-y-6 max-w-5xl mx-auto", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("h2", { className: "text-2xl font-bold", children: "Welcome to Documentation" }), _jsx("p", { className: "text-gray-500", children: "Browse and manage your documentation. Create new documents or organize them into folders." })] }), folders.length > 0 && (_jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", children: "Folders" }), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [folders.map((folder) => (_jsxs("div", { onClick: () => onOpen?.(folder.slug), className: "border border-gray-200 rounded-lg p-4 hover:bg-gray-50 transition-colors cursor-pointer", children: [_jsxs("div", { className: "flex items-start justify-between mb-2", children: [folder.emoji ? (_jsx("span", { className: "text-2xl", children: folder.emoji })) : (_jsx(Folder, { className: "h-8 w-8 text-blue-600" })), _jsxs("span", { className: "text-xs text-gray-500", children: [documents.filter((d) => d.parentId === folder.id).length, ' ', "docs"] })] }), _jsx("h4", { className: "text-base font-medium", children: folder.title })] }, folder.id))), _jsxs("div", { onClick: onCreate, className: "border border-dashed border-gray-300 rounded-lg p-4 hover:bg-gray-50 transition-colors cursor-pointer", children: [_jsx("div", { className: "flex items-center justify-center h-8 mb-2", children: _jsx(Plus, { className: "h-6 w-6 text-gray-400" }) }), _jsx("h4", { className: "text-base font-medium text-center text-gray-500", children: "New Folder" })] })] })] })), _jsxs("section", { children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: folders.length > 0 ? 'Documents' : 'Recent Documents' }), _jsxs("button", { onClick: onCreate, className: "px-3 py-1.5 text-sm bg-gray-900 text-white rounded-md hover:bg-gray-800 flex items-center gap-2 transition-colors", children: [_jsx(Plus, { className: "h-4 w-4" }), "New Document"] })] }), regularDocuments.length === 0 ? (_jsx("div", { className: "border border-dashed border-gray-300 rounded-lg p-8", children: _jsxs("div", { className: "flex flex-col items-center justify-center text-center", children: [_jsx(FileText, { className: "h-12 w-12 text-gray-400 mb-4" }), _jsx("h4", { className: "text-lg font-medium mb-2", children: "No documents yet" }), _jsx("p", { className: "text-sm text-gray-500 mb-4", children: "Get started by creating your first document" }), _jsxs("button", { onClick: onCreate, className: "px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-800 flex items-center gap-2 transition-colors", children: [_jsx(Plus, { className: "h-4 w-4" }), "Create Document"] })] }) })) : (_jsx("div", { className: "space-y-2", children: regularDocuments.map((doc) => (_jsxs("div", { onClick: () => onOpen?.(doc.slug), className: "flex items-center justify-between p-4 rounded-lg border border-gray-200 bg-white hover:bg-gray-50 transition-colors group cursor-pointer", children: [_jsxs("div", { className: "flex items-center gap-3", children: [doc.emoji ? (_jsx("span", { className: "text-xl", children: doc.emoji })) : (_jsx(FileText, { className: "h-5 w-5 text-gray-400 group-hover:text-gray-600 transition-colors" })), _jsxs("div", { children: [_jsx("h4", { className: "font-medium text-gray-900", children: doc.title }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Updated", ' ', doc.updatedAt
37
- ? formatDistanceToNow(new Date(doc.updatedAt))
38
- : 'N/A'] })] })] }), _jsx("button", { className: "px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-100 rounded-md transition-colors", children: "Open" })] }, doc.id))) }))] })] }));
44
+ return (_jsxs("div", { className: "space-y-6 max-w-5xl mx-auto", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("h2", { className: "text-2xl font-bold", children: t('docList.welcome') }), _jsx("p", { className: "text-gray-500", children: t('docList.welcomeDescription') })] }), folders.length > 0 && (_jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", children: t('docList.folders') }), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [folders.map((folder) => (_jsxs("div", { onClick: () => onOpen?.(folder.slug), className: "border border-gray-200 rounded-lg p-4 hover:bg-gray-50 transition-colors cursor-pointer", children: [_jsxs("div", { className: "flex items-start justify-between mb-2", children: [folder.emoji ? (_jsx("span", { className: "text-2xl", children: folder.emoji })) : (_jsx(Folder, { className: "h-8 w-8 text-blue-600" })), _jsxs("span", { className: "text-xs text-gray-500", children: [documents.filter((d) => d.parentId === folder.id).length, ' ', t('docList.docs')] })] }), _jsx("h4", { className: "text-base font-medium", children: folder.title })] }, folder.id))), _jsxs("div", { onClick: onCreate, className: "border border-dashed border-gray-300 rounded-lg p-4 hover:bg-gray-50 transition-colors cursor-pointer", children: [_jsx("div", { className: "flex items-center justify-center h-8 mb-2", children: _jsx(Plus, { className: "h-6 w-6 text-gray-400" }) }), _jsx("h4", { className: "text-base font-medium text-center text-gray-500", children: t('docList.newFolder') })] })] })] })), _jsxs("section", { children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h3", { className: "text-lg font-semibold", children: folders.length > 0 ? t('docList.documents') : t('docList.recentDocuments') }), _jsxs("button", { onClick: onCreate, className: "px-3 py-1.5 text-sm bg-gray-900 text-white rounded-md hover:bg-gray-800 flex items-center gap-2 transition-colors", children: [_jsx(Plus, { className: "h-4 w-4" }), t('docList.newDocument')] })] }), regularDocuments.length === 0 ? (_jsx("div", { className: "border border-dashed border-gray-300 rounded-lg p-8", children: _jsxs("div", { className: "flex flex-col items-center justify-center text-center", children: [_jsx(FileText, { className: "h-12 w-12 text-gray-400 mb-4" }), _jsx("h4", { className: "text-lg font-medium mb-2", children: t('docList.noDocuments') }), _jsx("p", { className: "text-sm text-gray-500 mb-4", children: t('docList.getStarted') }), _jsxs("button", { onClick: onCreate, className: "px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-800 flex items-center gap-2 transition-colors", children: [_jsx(Plus, { className: "h-4 w-4" }), t('docList.createDocument')] })] }) })) : (_jsx("div", { className: "space-y-2", children: regularDocuments.map((doc) => (_jsxs("div", { onClick: () => onOpen?.(doc.slug), className: "flex items-center justify-between p-4 rounded-lg border border-gray-200 bg-white hover:bg-gray-50 transition-colors group cursor-pointer", children: [_jsxs("div", { className: "flex items-center gap-3", children: [doc.emoji ? (_jsx("span", { className: "text-xl", children: doc.emoji })) : (_jsx(FileText, { className: "h-5 w-5 text-gray-400 group-hover:text-gray-600 transition-colors" })), _jsxs("div", { children: [_jsx("h4", { className: "font-medium text-gray-900", children: doc.title }), _jsxs("p", { className: "text-sm text-gray-500", children: [t('docList.updated'), ' ', doc.updatedAt
45
+ ? formatDistanceToNow(new Date(doc.updatedAt), t)
46
+ : 'N/A'] })] })] }), _jsx("button", { className: "px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-100 rounded-md transition-colors", children: t('docList.open') })] }, doc.id))) }))] })] }));
39
47
  }
@@ -1 +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"}
1
+ {"version":3,"file":"DocumentView.d.ts","sourceRoot":"","sources":["../../../src/client/components/DocumentView.tsx"],"names":[],"mappings":"AAIA,OAAO,8BAA8B,CAAA;AAUrC,UAAU,iBAAiB;CAAG;AAU9B,wBAAgB,YAAY,CAAC,EAAE,EAAE,iBAAiB,2CA2HjD"}
@@ -7,6 +7,7 @@ import { Clock, Edit, Loader2, User } from 'lucide-react';
7
7
  import { useCallback, useEffect, useState } from 'react';
8
8
  import { useDocumentQuery } from '../hooks/useDocsQuery.js';
9
9
  import { blockNoteTheme } from '../lib/blocknoteTheme.js';
10
+ import { useTranslation } from '../hooks/useTranslation.js';
10
11
  import { useDocument } from './DocumentProvider.js';
11
12
  import { ExportButton } from './ExportButton.js';
12
13
  // Default content when document has no content
@@ -18,6 +19,7 @@ const getDefaultContent = () => [
18
19
  ];
19
20
  export function DocumentView({}) {
20
21
  const { params, onCreate, onEdit, theme } = useDocument();
22
+ const { t } = useTranslation();
21
23
  const { data: doc, isLoading, error } = useDocumentQuery(params.documentSlug);
22
24
  const [hasLoaded, setHasLoaded] = useState(false);
23
25
  // Creates a new editor instance in read-only mode
@@ -50,9 +52,9 @@ export function DocumentView({}) {
50
52
  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
53
  }
52
54
  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." }) }));
55
+ return (_jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: _jsx("p", { className: "text-red-500", children: t('view.loadFailed') }) }));
54
56
  }
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
57
+ 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" }), t('view.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: [t('view.authorId'), ": ", doc.authorId] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Clock, { className: "h-4 w-4" }), _jsxs("span", { children: [t('view.updated'), ' ', doc.updatedAt
56
58
  ? new Date(doc.updatedAt).toLocaleDateString()
57
59
  : '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
60
  }
@@ -1 +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"}
1
+ {"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../src/client/components/EmojiPicker.tsx"],"names":[],"mappings":"AAKA,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,2CAqE3E"}
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTranslation } from '../hooks/useTranslation.js';
3
4
  import { useState, useRef, useEffect } from 'react';
4
5
  // Popular emojis for documents
5
6
  const POPULAR_EMOJIS = [
@@ -15,6 +16,7 @@ const POPULAR_EMOJIS = [
15
16
  '✅', '❌', '⭕', '🚫', '💯', '💢', '♨️', '🚷', '🚯', '🚳',
16
17
  ];
17
18
  export function EmojiPicker({ value, onChange, className }) {
19
+ const { t } = useTranslation();
18
20
  const [isOpen, setIsOpen] = useState(false);
19
21
  const dropdownRef = useRef(null);
20
22
  // Close dropdown when clicking outside
@@ -44,5 +46,5 @@ export function EmojiPicker({ value, onChange, className }) {
44
46
  ].join(' '), children: emoji }, emoji))) }), value && (_jsx("button", { onClick: () => {
45
47
  onChange('');
46
48
  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" }))] }))] }));
49
+ }, 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: t('emoji.remove') }))] }))] }));
48
50
  }
@@ -1 +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"}
1
+ {"version":3,"file":"ExportButton.d.ts","sourceRoot":"","sources":["../../../src/client/components/ExportButton.tsx"],"names":[],"mappings":"AAKA,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,2CAqHnB"}
@@ -2,32 +2,33 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useRef, useEffect } from 'react';
3
3
  import { Download, FileText, FileType, FileCode, File } from 'lucide-react';
4
4
  import { useExport } from '../hooks/useExport.js';
5
+ import { useTranslation } from '../hooks/useTranslation.js';
5
6
  const EXPORT_OPTIONS = [
6
7
  {
7
8
  format: 'docx',
8
- label: 'Microsoft Word',
9
- description: 'Editable document format',
9
+ labelKey: 'export.word',
10
+ descKey: 'export.wordDesc',
10
11
  icon: _jsx(FileText, { className: "w-4 h-4 text-blue-600" }),
11
12
  extension: 'docx',
12
13
  },
13
14
  {
14
15
  format: 'pdf',
15
- label: 'PDF Document',
16
- description: 'Print-ready format',
16
+ labelKey: 'export.pdf',
17
+ descKey: 'export.pdfDesc',
17
18
  icon: _jsx(FileType, { className: "w-4 h-4 text-red-600" }),
18
19
  extension: 'pdf',
19
20
  },
20
21
  {
21
22
  format: 'html',
22
- label: 'HTML Page',
23
- description: 'Web page format',
23
+ labelKey: 'export.html',
24
+ descKey: 'export.htmlDesc',
24
25
  icon: _jsx(FileCode, { className: "w-4 h-4 text-orange-600" }),
25
26
  extension: 'html',
26
27
  },
27
28
  {
28
29
  format: 'markdown',
29
- label: 'Markdown',
30
- description: 'Plain text with formatting',
30
+ labelKey: 'export.markdown',
31
+ descKey: 'export.markdownDesc',
31
32
  icon: _jsx(File, { className: "w-4 h-4 text-gray-600" }),
32
33
  extension: 'md',
33
34
  },
@@ -48,6 +49,7 @@ export function ExportButton({ documentId, documentTitle, baseUrl, className = '
48
49
  const [isOpen, setIsOpen] = useState(false);
49
50
  const menuRef = useRef(null);
50
51
  const { exportDocument, isExporting } = useExport({ baseUrl });
52
+ const { t } = useTranslation();
51
53
  // Close menu when clicking outside
52
54
  useEffect(() => {
53
55
  function handleClickOutside(event) {
@@ -72,10 +74,10 @@ export function ExportButton({ documentId, documentTitle, baseUrl, className = '
72
74
  const message = error instanceof Error ? error.message : 'Export failed';
73
75
  // Show alert for now - could be replaced with a toast notification
74
76
  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'}`);
77
+ alert(t('export.missingDep', { message, command: format === 'docx' ? 'npm install docx' : 'npm install puppeteer' }));
76
78
  }
77
79
  else {
78
- alert(`Export failed: ${message}`);
80
+ alert(t('export.failed', { message }));
79
81
  }
80
82
  }
81
83
  };
@@ -88,10 +90,10 @@ export function ExportButton({ documentId, documentTitle, baseUrl, className = '
88
90
  disabled:opacity-50 disabled:cursor-not-allowed
89
91
  transition-colors
90
92
  ${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: `
93
+ `, children: [_jsx(Download, { className: `w-4 h-4 ${isExporting ? 'animate-bounce' : ''}` }), _jsx("span", { children: t('export.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
94
  absolute right-0 mt-2 w-64
93
95
  bg-white border border-gray-200 rounded-lg shadow-lg
94
96
  z-50 overflow-hidden
95
97
  ${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)))] }) }))] }));
98
+ `, 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: t('export.exportAs') }), 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: t(option.labelKey) }), _jsx("div", { className: "text-xs text-gray-500", children: t(option.descKey) })] }), _jsxs("div", { className: "flex-shrink-0 text-xs text-gray-400 uppercase", children: [".", option.extension] })] }, option.format)))] }) }))] }));
97
99
  }
@@ -1 +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"}
1
+ {"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../src/client/components/Layout.tsx"],"names":[],"mappings":"AAiBA,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,2CA+J/D"}
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { FileText, PanelLeftCloseIcon, PanelLeftOpenIcon, Plus, Search, } from 'lucide-react';
4
4
  import { useEffect, useRef, useState } from 'react';
5
5
  import { useDocumentsQuery } from '../hooks/useDocsQuery.js';
6
+ import { useTranslation } from '../hooks/useTranslation.js';
6
7
  import { Breadcrumbs } from './Breadcrumbs.js';
7
8
  import { useDocument } from './DocumentProvider.js';
8
9
  import { Sidebar } from './Sidebar.js';
@@ -129,6 +130,7 @@ function extractTextSnippets(content, query) {
129
130
  }
130
131
  export function Layout({ children, userAvatar }) {
131
132
  const { onOpen, onCreate, params } = useDocument();
133
+ const { t } = useTranslation();
132
134
  const [expandedFolders, setExpandedFolders] = useState(new Set());
133
135
  const [isSidebarOpen, setIsSidebarOpen] = useState(true);
134
136
  // Header search state
@@ -152,14 +154,14 @@ export function Layout({ children, userAvatar }) {
152
154
  document.addEventListener('mousedown', handleClickOutside);
153
155
  return () => document.removeEventListener('mousedown', handleClickOutside);
154
156
  }, []);
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) => {
157
+ 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 ? t('layout.closeSidebar') : t('layout.openSidebar'), children: isSidebarOpen ? (_jsx(PanelLeftCloseIcon, { className: "h-6 w-6" })) : (_jsx(PanelLeftOpenIcon, { className: "h-6 w-6" })) }), _jsx(Breadcrumbs, { homeLabel: t('sidebar.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: t('layout.searchPlaceholder'), 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
158
  setHeaderSearchQuery(e.target.value);
157
159
  setShowSearchResults(e.target.value.length > 0);
158
160
  }, onFocus: () => {
159
161
  if (headerSearchQuery.length > 0) {
160
162
  setShowSearchResults(true);
161
163
  }
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) => {
164
+ } }), 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: [t('layout.noResults'), " \"", headerSearchQuery, "\""] })) : (_jsx("div", { className: "py-1", children: searchResults.map((doc) => {
163
165
  const breadcrumb = doc.parentId
164
166
  ? buildBreadcrumb(doc, documents)
165
167
  : null;
@@ -168,5 +170,5 @@ export function Layout({ children, userAvatar }) {
168
170
  setHeaderSearchQuery('');
169
171
  setShowSearchResults(false);
170
172
  }, 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 })] })] }));
173
+ }) })) }))] }), _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" }), t('layout.create')] }), userAvatar] })] })), _jsx("div", { className: "flex-1 overflow-auto p-6", children: children })] })] }));
172
174
  }
@@ -1 +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"}
1
+ {"version":3,"file":"ReactEmbedDocs.d.ts","sourceRoot":"","sources":["../../../src/client/components/ReactEmbedDocs.tsx"],"names":[],"mappings":"AAMA,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,2CAgBxD"}
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { TranslationProvider } from '../hooks/useTranslation.js';
2
3
  import { processPath } from '../lib/path.js';
3
4
  import { DocumentContent } from './DocumentContent.js';
4
5
  import { DocumentProvider } from './DocumentProvider.js';
5
6
  import { Layout } from './Layout.js';
6
7
  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
+ return (_jsx(TranslationProvider, { locale: props.locale, children: _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
9
  }
@@ -1 +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"}
1
+ {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/client/components/Sidebar.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAiB,MAAM,oBAAoB,CAAA;AAOnE,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,2CA2Q5E"}
@@ -3,11 +3,13 @@ import { closestCenter, DndContext, DragOverlay, KeyboardSensor, PointerSensor,
3
3
  import { Plus } from 'lucide-react';
4
4
  import { useCallback, useMemo, useState } from 'react';
5
5
  import { useReorderDocumentMutation } from '../hooks/useDocsQuery';
6
+ import { useTranslation } from '../hooks/useTranslation';
6
7
  import { useDocument } from './DocumentProvider';
7
8
  import { DragOverlayItem } from './DragOverlayItem';
8
9
  import { SortableTreeItem } from './SortableTreeItem';
9
10
  export function Sidebar({ isOpen, onToggle, documents, currentDocId }) {
10
11
  const { onCreate, onOpen } = useDocument();
12
+ const { t } = useTranslation();
11
13
  const [expandedFolders, setExpandedFolders] = useState(new Set());
12
14
  const [activeId, setActiveId] = useState(null);
13
15
  const [dropIndicator, setDropIndicator] = useState(null);
@@ -170,7 +172,7 @@ export function Sidebar({ isOpen, onToggle, documents, currentDocId }) {
170
172
  return (_jsxs("aside", { className: [
171
173
  'border-r border-border flex flex-col transition-all duration-200 ease-in-out overflow-hidden',
172
174
  isOpen ? 'w-64 opacity-100' : 'w-0 opacity-0',
173
- ].join(' '), children: [_jsx("div", { className: "p-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h2", { className: "font-semibold text-sm text-muted-foreground", children: "Documents" }), _jsx("button", { className: "p-2 hover:bg-primary rounded-md transition-colors cursor-pointer", onClick: onCreate, children: _jsx(Plus, { className: "h-4 w-4" }) })] }) }), _jsxs("div", { className: "flex-1 overflow-y-auto p-2 space-y-1 relative", children: [_jsxs(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragStart: handleDragStart, onDragOver: handleDragOver, onDragEnd: handleDragEnd, onDragCancel: handleDragCancel, children: [flattenedTree
175
+ ].join(' '), children: [_jsx("div", { className: "p-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h2", { className: "font-semibold text-sm text-muted-foreground", children: t('sidebar.documents') }), _jsx("button", { className: "p-2 hover:bg-primary rounded-md transition-colors cursor-pointer", onClick: onCreate, children: _jsx(Plus, { className: "h-4 w-4" }) })] }) }), _jsxs("div", { className: "flex-1 overflow-y-auto p-2 space-y-1 relative", children: [_jsxs(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragStart: handleDragStart, onDragOver: handleDragOver, onDragEnd: handleDragEnd, onDragCancel: handleDragCancel, children: [flattenedTree
174
176
  // .filter((doc) => doc.id !== activeId)
175
- .map((doc) => (_jsx(SortableTreeItem, { id: doc.id, doc: doc, isExpanded: expandedFolders.has(doc.id), isCurrent: doc.id === resolvedCurrentDocId, dropIndicator: dropIndicator?.id === doc.id ? dropIndicator.position : null, onToggle: toggleFolder, onOpen: onOpen }, doc.id))), _jsx(DragOverlay, { dropAnimation: dropAnimation, children: activeDoc ? _jsx(DragOverlayItem, { doc: activeDoc }) : null })] }), documents.length === 0 && (_jsx("div", { className: "text-sm text-gray-500 text-center py-4", children: "No documents yet" }))] })] }));
177
+ .map((doc) => (_jsx(SortableTreeItem, { id: doc.id, doc: doc, isExpanded: expandedFolders.has(doc.id), isCurrent: doc.id === resolvedCurrentDocId, dropIndicator: dropIndicator?.id === doc.id ? dropIndicator.position : null, onToggle: toggleFolder, onOpen: onOpen }, doc.id))), _jsx(DragOverlay, { dropAnimation: dropAnimation, children: activeDoc ? _jsx(DragOverlayItem, { doc: activeDoc }) : null })] }), documents.length === 0 && (_jsx("div", { className: "text-sm text-gray-500 text-center py-4", children: t('sidebar.noDocuments') }))] })] }));
176
178
  }
@@ -1 +1 @@
1
- {"version":3,"file":"VersionHistory.d.ts","sourceRoot":"","sources":["../../../src/client/components/VersionHistory.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,aAAa,EACb,OAAgB,EAChB,SAAS,EACT,QAAsB,EACtB,MAAM,GACP,EAAE,mBAAmB,2CAsQrB;AAED,eAAe,cAAc,CAAA"}
1
+ {"version":3,"file":"VersionHistory.d.ts","sourceRoot":"","sources":["../../../src/client/components/VersionHistory.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,aAAa,EACb,OAAgB,EAChB,SAAS,EACT,QAAsB,EACtB,MAAM,GACP,EAAE,mBAAmB,2CAuQrB;AAED,eAAe,cAAc,CAAA"}
@@ -2,10 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { History, RotateCcw, Trash2, GitCompare, Clock, User, MessageSquare, } from 'lucide-react';
4
4
  import { useDocumentVersionsQuery, useCreateVersionMutation, useRestoreVersionMutation, useDeleteVersionMutation, } from '../hooks/useVersions.js';
5
+ import { useTranslation } from '../hooks/useTranslation.js';
5
6
  /**
6
7
  * Component for viewing and managing document version history
7
8
  */
8
9
  export function VersionHistory({ documentId, documentTitle, baseUrl = '/api', onRestore, userName = 'Anonymous', userId, }) {
10
+ const { t } = useTranslation();
9
11
  const { data, isLoading, error } = useDocumentVersionsQuery(documentId, baseUrl);
10
12
  const createVersionMutation = useCreateVersionMutation(baseUrl);
11
13
  const restoreMutation = useRestoreVersionMutation(baseUrl);
@@ -20,7 +22,7 @@ export function VersionHistory({ documentId, documentTitle, baseUrl = '/api', on
20
22
  await createVersionMutation.mutateAsync({
21
23
  documentId,
22
24
  data: {
23
- description: versionDescription || `Version created by ${userName}`,
25
+ description: versionDescription || t('versions.createdBy', { name: userName }),
24
26
  createdBy: userId,
25
27
  createdByName: userName,
26
28
  },
@@ -29,11 +31,11 @@ export function VersionHistory({ documentId, documentTitle, baseUrl = '/api', on
29
31
  setVersionDescription('');
30
32
  }
31
33
  catch (error) {
32
- alert('Failed to create version');
34
+ alert(t('versions.createFailed'));
33
35
  }
34
36
  };
35
37
  const handleRestore = async (version) => {
36
- if (!confirm(`Restore "${documentTitle}" to version ${version.versionNumber}?\n\nCurrent state will be saved as a backup.`)) {
38
+ if (!confirm(t('versions.restoreConfirm', { title: documentTitle, version: version.versionNumber }))) {
37
39
  return;
38
40
  }
39
41
  setRestoringVersion(version.id);
@@ -47,24 +49,24 @@ export function VersionHistory({ documentId, documentTitle, baseUrl = '/api', on
47
49
  },
48
50
  });
49
51
  onRestore?.();
50
- alert(`Restored to version ${version.versionNumber}`);
52
+ alert(t('versions.restored', { version: version.versionNumber }));
51
53
  }
52
54
  catch (error) {
53
- alert('Failed to restore version');
55
+ alert(t('versions.restoreFailed'));
54
56
  }
55
57
  finally {
56
58
  setRestoringVersion(null);
57
59
  }
58
60
  };
59
61
  const handleDelete = async (version) => {
60
- if (!confirm(`Delete version ${version.versionNumber}? This cannot be undone.`)) {
62
+ if (!confirm(t('versions.deleteConfirm', { version: version.versionNumber }))) {
61
63
  return;
62
64
  }
63
65
  try {
64
66
  await deleteMutation.mutateAsync({ versionId: version.id, documentId });
65
67
  }
66
68
  catch (error) {
67
- alert('Failed to delete version');
69
+ alert(t('versions.deleteFailed'));
68
70
  }
69
71
  };
70
72
  const toggleVersionSelection = (versionId) => {
@@ -89,14 +91,14 @@ export function VersionHistory({ documentId, documentTitle, baseUrl = '/api', on
89
91
  });
90
92
  };
91
93
  if (isLoading) {
92
- return (_jsxs("div", { className: "p-4 text-center text-gray-500", children: [_jsx("div", { className: "animate-spin inline-block w-5 h-5 border-2 border-gray-300 border-t-gray-600 rounded-full mr-2" }), "Loading versions..."] }));
94
+ return (_jsxs("div", { className: "p-4 text-center text-gray-500", children: [_jsx("div", { className: "animate-spin inline-block w-5 h-5 border-2 border-gray-300 border-t-gray-600 rounded-full mr-2" }), t('versions.loading')] }));
93
95
  }
94
96
  if (error) {
95
- return (_jsx("div", { className: "p-4 text-center text-red-500", children: "Failed to load version history" }));
97
+ return (_jsx("div", { className: "p-4 text-center text-red-500", children: t('versions.loadFailed') }));
96
98
  }
97
- return (_jsxs("div", { className: "version-history bg-white rounded-lg border border-gray-200", children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b border-gray-200", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(History, { className: "w-5 h-5 text-gray-500" }), _jsx("h3", { className: "font-semibold text-gray-900", children: "Version History" }), _jsxs("span", { className: "text-sm text-gray-500", children: ["(", versions.length, ")"] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [selectedVersions.length === 2 && (_jsxs("button", { className: "flex items-center gap-1 px-3 py-1.5 text-sm bg-blue-50 text-blue-600 rounded-md hover:bg-blue-100", onClick: () => { }, children: [_jsx(GitCompare, { className: "w-4 h-4" }), "Compare"] })), _jsx("button", { className: "flex items-center gap-1 px-3 py-1.5 text-sm bg-gray-900 text-white rounded-md hover:bg-gray-800", onClick: () => setShowCreateDialog(true), children: "Save Version" })] })] }), showCreateDialog && (_jsx("div", { className: "p-4 bg-gray-50 border-b border-gray-200", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(MessageSquare, { className: "w-5 h-5 text-gray-400 mt-2" }), _jsxs("div", { className: "flex-1", children: [_jsx("input", { type: "text", placeholder: "Describe what's changed in this version...", className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500", value: versionDescription, onChange: (e) => setVersionDescription(e.target.value), onKeyDown: (e) => e.key === 'Enter' && handleCreateVersion() }), _jsxs("div", { className: "flex items-center gap-2 mt-2", children: [_jsx("button", { className: "px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50", onClick: handleCreateVersion, disabled: createVersionMutation.isPending, children: createVersionMutation.isPending ? 'Saving...' : 'Save' }), _jsx("button", { className: "px-3 py-1.5 text-sm text-gray-600 hover:text-gray-900", onClick: () => {
99
+ return (_jsxs("div", { className: "version-history bg-white rounded-lg border border-gray-200", children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b border-gray-200", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(History, { className: "w-5 h-5 text-gray-500" }), _jsx("h3", { className: "font-semibold text-gray-900", children: t('versions.title') }), _jsxs("span", { className: "text-sm text-gray-500", children: ["(", versions.length, ")"] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [selectedVersions.length === 2 && (_jsxs("button", { className: "flex items-center gap-1 px-3 py-1.5 text-sm bg-blue-50 text-blue-600 rounded-md hover:bg-blue-100", onClick: () => { }, children: [_jsx(GitCompare, { className: "w-4 h-4" }), t('versions.compare')] })), _jsx("button", { className: "flex items-center gap-1 px-3 py-1.5 text-sm bg-gray-900 text-white rounded-md hover:bg-gray-800", onClick: () => setShowCreateDialog(true), children: t('versions.saveVersion') })] })] }), showCreateDialog && (_jsx("div", { className: "p-4 bg-gray-50 border-b border-gray-200", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(MessageSquare, { className: "w-5 h-5 text-gray-400 mt-2" }), _jsxs("div", { className: "flex-1", children: [_jsx("input", { type: "text", placeholder: t('versions.descPlaceholder'), className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500", value: versionDescription, onChange: (e) => setVersionDescription(e.target.value), onKeyDown: (e) => e.key === 'Enter' && handleCreateVersion() }), _jsxs("div", { className: "flex items-center gap-2 mt-2", children: [_jsx("button", { className: "px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50", onClick: handleCreateVersion, disabled: createVersionMutation.isPending, children: createVersionMutation.isPending ? t('versions.saving') : t('versions.save') }), _jsx("button", { className: "px-3 py-1.5 text-sm text-gray-600 hover:text-gray-900", onClick: () => {
98
100
  setShowCreateDialog(false);
99
101
  setVersionDescription('');
100
- }, children: "Cancel" })] })] })] }) })), _jsx("div", { className: "max-h-96 overflow-y-auto", children: versions.length === 0 ? (_jsxs("div", { className: "p-8 text-center text-gray-500", children: [_jsx(History, { className: "w-12 h-12 mx-auto mb-3 text-gray-300" }), _jsx("p", { children: "No versions yet" }), _jsx("p", { className: "text-sm mt-1", children: "Save a version to track changes" })] })) : (_jsx("div", { className: "divide-y divide-gray-100", children: versions.map((version) => (_jsx("div", { className: `p-4 hover:bg-gray-50 transition-colors ${selectedVersions.includes(version.id) ? 'bg-blue-50' : ''}`, children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("input", { type: "checkbox", checked: selectedVersions.includes(version.id), onChange: () => toggleVersionSelection(version.id), className: "mt-1 w-4 h-4 text-blue-600 rounded border-gray-300" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "font-mono text-sm font-medium text-gray-900", children: ["v", version.versionNumber] }), version.versionNumber === versions[0]?.versionNumber && (_jsx("span", { className: "px-2 py-0.5 text-xs bg-blue-100 text-blue-700 rounded-full", children: "Latest" }))] }), version.changeDescription && (_jsx("p", { className: "text-sm text-gray-700 mt-1", children: version.changeDescription })), _jsxs("div", { className: "flex items-center gap-4 mt-2 text-xs text-gray-500", children: [_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Clock, { className: "w-3 h-3" }), formatDate(version.createdAt)] }), version.createdByName && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(User, { className: "w-3 h-3" }), version.createdByName] }))] })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { className: "p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded", onClick: () => handleRestore(version), disabled: restoringVersion === version.id, title: "Restore this version", children: restoringVersion === version.id ? (_jsx("div", { className: "w-4 h-4 border-2 border-blue-300 border-t-blue-600 rounded-full animate-spin" })) : (_jsx(RotateCcw, { className: "w-4 h-4" })) }), _jsx("button", { className: "p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded", onClick: () => handleDelete(version), disabled: deleteMutation.isPending, title: "Delete version", children: _jsx(Trash2, { className: "w-4 h-4" }) })] })] }) }, version.id))) })) })] }));
102
+ }, children: t('versions.cancel') })] })] })] }) })), _jsx("div", { className: "max-h-96 overflow-y-auto", children: versions.length === 0 ? (_jsxs("div", { className: "p-8 text-center text-gray-500", children: [_jsx(History, { className: "w-12 h-12 mx-auto mb-3 text-gray-300" }), _jsx("p", { children: t('versions.noVersions') }), _jsx("p", { className: "text-sm mt-1", children: t('versions.noVersionsHint') })] })) : (_jsx("div", { className: "divide-y divide-gray-100", children: versions.map((version) => (_jsx("div", { className: `p-4 hover:bg-gray-50 transition-colors ${selectedVersions.includes(version.id) ? 'bg-blue-50' : ''}`, children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("input", { type: "checkbox", checked: selectedVersions.includes(version.id), onChange: () => toggleVersionSelection(version.id), className: "mt-1 w-4 h-4 text-blue-600 rounded border-gray-300" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "font-mono text-sm font-medium text-gray-900", children: ["v", version.versionNumber] }), version.versionNumber === versions[0]?.versionNumber && (_jsx("span", { className: "px-2 py-0.5 text-xs bg-blue-100 text-blue-700 rounded-full", children: t('versions.latest') }))] }), version.changeDescription && (_jsx("p", { className: "text-sm text-gray-700 mt-1", children: version.changeDescription })), _jsxs("div", { className: "flex items-center gap-4 mt-2 text-xs text-gray-500", children: [_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Clock, { className: "w-3 h-3" }), formatDate(version.createdAt)] }), version.createdByName && (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(User, { className: "w-3 h-3" }), version.createdByName] }))] })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { className: "p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded", onClick: () => handleRestore(version), disabled: restoringVersion === version.id, title: t('versions.restore'), children: restoringVersion === version.id ? (_jsx("div", { className: "w-4 h-4 border-2 border-blue-300 border-t-blue-600 rounded-full animate-spin" })) : (_jsx(RotateCcw, { className: "w-4 h-4" })) }), _jsx("button", { className: "p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded", onClick: () => handleDelete(version), disabled: deleteMutation.isPending, title: t('versions.delete'), children: _jsx(Trash2, { className: "w-4 h-4" }) })] })] }) }, version.id))) })) })] }));
101
103
  }
102
104
  export default VersionHistory;
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ interface TranslationContextValue {
3
+ t: (key: string, params?: Record<string, string | number>) => string;
4
+ locale: string;
5
+ }
6
+ interface TranslationProviderProps {
7
+ locale?: string;
8
+ children: ReactNode;
9
+ }
10
+ export declare function TranslationProvider({ locale, children, }: TranslationProviderProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function useTranslation(): TranslationContextValue;
12
+ export {};
13
+ //# sourceMappingURL=useTranslation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTranslation.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useTranslation.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAiB,SAAS,EAA2B,MAAM,OAAO,CAAA;AAQzE,UAAU,uBAAuB;IAC/B,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAA;IACpE,MAAM,EAAE,MAAM,CAAA;CACf;AAID,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAa,EACb,QAAQ,GACT,EAAE,wBAAwB,2CAqB1B;AAED,wBAAgB,cAAc,IAAI,uBAAuB,CAMxD"}
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useCallback, useContext } from 'react';
4
+ import en from '../locales/en.json';
5
+ import ru from '../locales/ru.json';
6
+ const locales = { en, ru };
7
+ const TranslationContext = createContext(null);
8
+ export function TranslationProvider({ locale = 'en', children, }) {
9
+ const translations = locales[locale] || locales.en;
10
+ const t = useCallback((key, params) => {
11
+ let value = translations[key] || locales.en[key] || key;
12
+ if (params) {
13
+ for (const [k, v] of Object.entries(params)) {
14
+ value = value.replace(`{${k}}`, String(v));
15
+ }
16
+ }
17
+ return value;
18
+ }, [translations]);
19
+ return (_jsx(TranslationContext.Provider, { value: { t, locale }, children: children }));
20
+ }
21
+ export function useTranslation() {
22
+ const context = useContext(TranslationContext);
23
+ if (!context) {
24
+ throw new Error('useTranslation must be used within a TranslationProvider');
25
+ }
26
+ return context;
27
+ }
@@ -10,6 +10,7 @@ export { VersionHistory } from './components/VersionHistory.js';
10
10
  export { docsQueryKeys, useCreateDocumentMutation, useDeleteDocumentMutation, useDocumentBySlugQuery, useDocumentQuery, useDocumentsQuery, useReorderDocumentMutation, useUpdateDocumentMutation, useUpdateOrderMutation } from './hooks/useDocsQuery.js';
11
11
  export { useFileUpload, useMultiFileUpload } from './hooks/useFileUpload.js';
12
12
  export { useExport } from './hooks/useExport.js';
13
+ export { TranslationProvider, useTranslation } from './hooks/useTranslation.js';
13
14
  export type { ExportFormat, UseExportOptions, UseExportReturn } from './hooks/useExport.js';
14
15
  export { useCollaborativeEditor, useCollaborators, useEditor } from './hooks/useCollaboration.js';
15
16
  export type { CollaboratorInfo, CollaboratorState, UseCollaborativeEditorOptions, UseCollaborativeEditorReturn } from './hooks/useCollaboration.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAG/D,OAAO,EACL,aAAa,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,sBAAsB,EAChN,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,aAAa,EACb,kBAAkB,EACnB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,SAAS,EACV,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAG3F,OAAO,EACL,sBAAsB,EAAE,gBAAgB,EAAE,SAAS,EACpD,MAAM,6BAA6B,CAAA;AACpC,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,6BAA6B,EAC7B,4BAA4B,EAC7B,MAAM,6BAA6B,CAAA;AAGpC,OAAO,EACL,uBAAuB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,eAAe,EAAE,iBAAiB,EACrK,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EACV,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAG/B,YAAY,EACV,cAAc,EAEd,gBAAgB,EAAE,oBAAoB,EAAE,oBAAoB,EAE5D,QAAQ,EAAE,kBAAkB,EAE5B,cAAc,EAAE,UAAU,EAE1B,UAAU,EAAE,cAAc,IAAI,cAAc,EAAE,gBAAgB,EAG9D,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EAEnB,YAAY,EACZ,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EACzE,MAAM,oBAAoB,CAAA;AAG3B,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,YAAY,EACb,MAAM,0BAA0B,CAAA;AAGjC,YAAY,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAClH,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAG/D,OAAO,EACL,aAAa,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,sBAAsB,EAChN,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,aAAa,EACb,kBAAkB,EACnB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,SAAS,EACV,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACf,MAAM,2BAA2B,CAAA;AAClC,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAG3F,OAAO,EACL,sBAAsB,EAAE,gBAAgB,EAAE,SAAS,EACpD,MAAM,6BAA6B,CAAA;AACpC,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,6BAA6B,EAC7B,4BAA4B,EAC7B,MAAM,6BAA6B,CAAA;AAGpC,OAAO,EACL,uBAAuB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,eAAe,EAAE,iBAAiB,EACrK,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EACV,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAG/B,YAAY,EACV,cAAc,EAEd,gBAAgB,EAAE,oBAAoB,EAAE,oBAAoB,EAE5D,QAAQ,EAAE,kBAAkB,EAE5B,cAAc,EAAE,UAAU,EAE1B,UAAU,EAAE,cAAc,IAAI,cAAc,EAAE,gBAAgB,EAG9D,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EAEnB,YAAY,EACZ,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EACzE,MAAM,oBAAoB,CAAA;AAG3B,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,YAAY,EACb,MAAM,0BAA0B,CAAA;AAGjC,YAAY,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAClH,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA"}
@@ -12,6 +12,7 @@ export { VersionHistory } from './components/VersionHistory.js';
12
12
  export { docsQueryKeys, useCreateDocumentMutation, useDeleteDocumentMutation, useDocumentBySlugQuery, useDocumentQuery, useDocumentsQuery, useReorderDocumentMutation, useUpdateDocumentMutation, useUpdateOrderMutation } from './hooks/useDocsQuery.js';
13
13
  export { useFileUpload, useMultiFileUpload } from './hooks/useFileUpload.js';
14
14
  export { useExport } from './hooks/useExport.js';
15
+ export { TranslationProvider, useTranslation } from './hooks/useTranslation.js';
15
16
  // Collaboration hooks
16
17
  export { useCollaborativeEditor, useCollaborators, useEditor } from './hooks/useCollaboration.js';
17
18
  // Versioning hooks
@@ -0,0 +1,82 @@
1
+ {
2
+ "sidebar.documents": "Documents",
3
+ "sidebar.noDocuments": "No documents yet",
4
+ "layout.closeSidebar": "Close sidebar",
5
+ "layout.openSidebar": "Open sidebar",
6
+ "layout.searchPlaceholder": "Search documentation...",
7
+ "layout.noResults": "No results found for",
8
+ "layout.create": "Create",
9
+ "breadcrumbs.home": "Home",
10
+ "breadcrumbs.loading": "Loading...",
11
+ "breadcrumbs.error": "Error loading path",
12
+ "emoji.remove": "Remove emoji",
13
+ "editor.titlePlaceholder": "Document title",
14
+ "editor.titleRequired": "Please enter a document title",
15
+ "editor.saveFailed": "Failed to save document. Please try again.",
16
+ "editor.view": "View",
17
+ "editor.saving": "Saving...",
18
+ "editor.create": "Create",
19
+ "editor.save": "Save",
20
+ "editor.slug": "Slug",
21
+ "editor.coverAlt": "Cover",
22
+ "editor.removeCover": "Remove cover",
23
+ "editor.coverDropzone": "Drag & drop cover image here or click to upload",
24
+ "editor.coverFormats": "JPEG, PNG, GIF, WebP up to 5MB",
25
+ "view.loadFailed": "Failed to load document. Please try again.",
26
+ "view.edit": "Edit",
27
+ "view.authorId": "Author ID",
28
+ "view.updated": "Updated",
29
+ "docList.welcome": "Welcome to Documentation",
30
+ "docList.welcomeDescription": "Browse and manage your documentation. Create new documents or organize them into folders.",
31
+ "docList.folders": "Folders",
32
+ "docList.docs": "docs",
33
+ "docList.newFolder": "New Folder",
34
+ "docList.documents": "Documents",
35
+ "docList.recentDocuments": "Recent Documents",
36
+ "docList.newDocument": "New Document",
37
+ "docList.noDocuments": "No documents yet",
38
+ "docList.getStarted": "Get started by creating your first document",
39
+ "docList.createDocument": "Create Document",
40
+ "docList.updated": "Updated",
41
+ "docList.open": "Open",
42
+ "docList.justNow": "just now",
43
+ "docList.minuteAgo": "{n} minute ago",
44
+ "docList.minutesAgo": "{n} minutes ago",
45
+ "docList.hourAgo": "{n} hour ago",
46
+ "docList.hoursAgo": "{n} hours ago",
47
+ "docList.dayAgo": "{n} day ago",
48
+ "docList.daysAgo": "{n} days ago",
49
+ "export.export": "Export",
50
+ "export.exportAs": "Export as",
51
+ "export.word": "Microsoft Word",
52
+ "export.wordDesc": "Editable document format",
53
+ "export.pdf": "PDF Document",
54
+ "export.pdfDesc": "Print-ready format",
55
+ "export.html": "HTML Page",
56
+ "export.htmlDesc": "Web page format",
57
+ "export.markdown": "Markdown",
58
+ "export.markdownDesc": "Plain text with formatting",
59
+ "export.missingDep": "Missing dependency: {message}\n\nInstall with:\n{command}",
60
+ "export.failed": "Export failed: {message}",
61
+ "versions.title": "Version History",
62
+ "versions.compare": "Compare",
63
+ "versions.saveVersion": "Save Version",
64
+ "versions.descPlaceholder": "Describe what's changed in this version...",
65
+ "versions.saving": "Saving...",
66
+ "versions.save": "Save",
67
+ "versions.cancel": "Cancel",
68
+ "versions.noVersions": "No versions yet",
69
+ "versions.noVersionsHint": "Save a version to track changes",
70
+ "versions.latest": "Latest",
71
+ "versions.restore": "Restore this version",
72
+ "versions.delete": "Delete version",
73
+ "versions.createdBy": "Version created by {name}",
74
+ "versions.createFailed": "Failed to create version",
75
+ "versions.restoreConfirm": "Restore \"{title}\" to version {version}?\n\nCurrent state will be saved as a backup.",
76
+ "versions.restored": "Restored to version {version}",
77
+ "versions.restoreFailed": "Failed to restore version",
78
+ "versions.deleteConfirm": "Delete version {version}? This cannot be undone.",
79
+ "versions.deleteFailed": "Failed to delete version",
80
+ "versions.loading": "Loading versions...",
81
+ "versions.loadFailed": "Failed to load version history"
82
+ }
@@ -0,0 +1,82 @@
1
+ {
2
+ "sidebar.documents": "Документы",
3
+ "sidebar.noDocuments": "Документов пока нет",
4
+ "layout.closeSidebar": "Закрыть боковую панель",
5
+ "layout.openSidebar": "Открыть боковую панель",
6
+ "layout.searchPlaceholder": "Поиск по документации...",
7
+ "layout.noResults": "Ничего не найдено по запросу",
8
+ "layout.create": "Создать",
9
+ "breadcrumbs.home": "Главная",
10
+ "breadcrumbs.loading": "Загрузка...",
11
+ "breadcrumbs.error": "Ошибка загрузки пути",
12
+ "emoji.remove": "Убрать эмодзи",
13
+ "editor.titlePlaceholder": "Название документа",
14
+ "editor.titleRequired": "Пожалуйста, введите название документа",
15
+ "editor.saveFailed": "Не удалось сохранить документ. Попробуйте ещё раз.",
16
+ "editor.view": "Просмотр",
17
+ "editor.saving": "Сохранение...",
18
+ "editor.create": "Создать",
19
+ "editor.save": "Сохранить",
20
+ "editor.slug": "Slug",
21
+ "editor.coverAlt": "Обложка",
22
+ "editor.removeCover": "Удалить обложку",
23
+ "editor.coverDropzone": "Перетащите изображение обложки сюда или нажмите для загрузки",
24
+ "editor.coverFormats": "JPEG, PNG, GIF, WebP до 5МБ",
25
+ "view.loadFailed": "Не удалось загрузить документ. Попробуйте ещё раз.",
26
+ "view.edit": "Редактировать",
27
+ "view.authorId": "ID автора",
28
+ "view.updated": "Обновлено",
29
+ "docList.welcome": "Добро пожаловать в Документацию",
30
+ "docList.welcomeDescription": "Просматривайте и управляйте документацией. Создавайте новые документы или организуйте их в папки.",
31
+ "docList.folders": "Папки",
32
+ "docList.docs": "док.",
33
+ "docList.newFolder": "Новая папка",
34
+ "docList.documents": "Документы",
35
+ "docList.recentDocuments": "Недавние документы",
36
+ "docList.newDocument": "Новый документ",
37
+ "docList.noDocuments": "Документов пока нет",
38
+ "docList.getStarted": "Начните с создания первого документа",
39
+ "docList.createDocument": "Создать документ",
40
+ "docList.updated": "Обновлено",
41
+ "docList.open": "Открыть",
42
+ "docList.justNow": "только что",
43
+ "docList.minuteAgo": "{n} минуту назад",
44
+ "docList.minutesAgo": "{n} минут назад",
45
+ "docList.hourAgo": "{n} час назад",
46
+ "docList.hoursAgo": "{n} часов назад",
47
+ "docList.dayAgo": "{n} день назад",
48
+ "docList.daysAgo": "{n} дней назад",
49
+ "export.export": "Экспорт",
50
+ "export.exportAs": "Экспортировать как",
51
+ "export.word": "Microsoft Word",
52
+ "export.wordDesc": "Редактируемый формат документа",
53
+ "export.pdf": "PDF документ",
54
+ "export.pdfDesc": "Формат для печати",
55
+ "export.html": "HTML страница",
56
+ "export.htmlDesc": "Формат веб-страницы",
57
+ "export.markdown": "Markdown",
58
+ "export.markdownDesc": "Текст с форматированием",
59
+ "export.missingDep": "Отсутствует зависимость: {message}\n\nУстановите:\n{command}",
60
+ "export.failed": "Ошибка экспорта: {message}",
61
+ "versions.title": "История версий",
62
+ "versions.compare": "Сравнить",
63
+ "versions.saveVersion": "Сохранить версию",
64
+ "versions.descPlaceholder": "Опишите изменения в этой версии...",
65
+ "versions.saving": "Сохранение...",
66
+ "versions.save": "Сохранить",
67
+ "versions.cancel": "Отмена",
68
+ "versions.noVersions": "Версий пока нет",
69
+ "versions.noVersionsHint": "Сохраните версию для отслеживания изменений",
70
+ "versions.latest": "Последняя",
71
+ "versions.restore": "Восстановить эту версию",
72
+ "versions.delete": "Удалить версию",
73
+ "versions.createdBy": "Версия создана пользователем {name}",
74
+ "versions.createFailed": "Не удалось создать версию",
75
+ "versions.restoreConfirm": "Восстановить \"{title}\" до версии {version}?\n\nТекущее состояние будет сохранено как резервная копия.",
76
+ "versions.restored": "Восстановлено до версии {version}",
77
+ "versions.restoreFailed": "Не удалось восстановить версию",
78
+ "versions.deleteConfirm": "Удалить версию {version}? Это действие нельзя отменить.",
79
+ "versions.deleteFailed": "Не удалось удалить версию",
80
+ "versions.loading": "Загрузка версий...",
81
+ "versions.loadFailed": "Не удалось загрузить историю версий"
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-embed-docs",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Full-stack documentation system with BlockNote editor, hierarchical structure, and file uploads",
5
5
  "type": "module",
6
6
  "main": "./dist/server/index.js",