@shahadpichen/docpush 1.0.4 ā 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/add.js +284 -107
- package/dist/cli/commands/add.js.map +1 -1
- package/dist/react/hooks/use-drafts.d.ts +2 -1
- package/dist/react/hooks/use-drafts.d.ts.map +1 -1
- package/dist/react/hooks/use-drafts.js +19 -6
- package/dist/react/hooks/use-drafts.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AAmCA;;GAEG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AAmCA;;GAEG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGpE"}
|
package/dist/cli/commands/add.js
CHANGED
|
@@ -17,11 +17,11 @@ const COMPONENTS = {
|
|
|
17
17
|
card: { files: ['card.tsx'], dependencies: ['utils'] },
|
|
18
18
|
'scroll-area': { files: ['scroll-area.tsx'], dependencies: ['utils'] },
|
|
19
19
|
// Feature components
|
|
20
|
-
'docs-sidebar': { files: ['docs-sidebar.tsx'], dependencies: ['utils', 'scroll-area'] },
|
|
20
|
+
'docs-sidebar': { files: ['docs-sidebar.tsx'], dependencies: ['utils', 'button', 'scroll-area'] },
|
|
21
21
|
'markdown-viewer': { files: ['markdown-viewer.tsx'], dependencies: ['utils'] },
|
|
22
22
|
'markdown-editor': {
|
|
23
23
|
files: ['markdown-editor.tsx'],
|
|
24
|
-
dependencies: ['utils', 'button', 'textarea'],
|
|
24
|
+
dependencies: ['utils', 'button', 'textarea', 'markdown-viewer'],
|
|
25
25
|
},
|
|
26
26
|
'comments-panel': {
|
|
27
27
|
files: ['comments-panel.tsx'],
|
|
@@ -73,7 +73,6 @@ async function addCommand(components) {
|
|
|
73
73
|
// Load config to get custom paths (if docs.config.js exists)
|
|
74
74
|
let uiDir = './src/components/ui';
|
|
75
75
|
let libDir = './src/lib';
|
|
76
|
-
let aliasPrefix = '@';
|
|
77
76
|
try {
|
|
78
77
|
const configPath = node_path_1.default.resolve('./docs.config.js');
|
|
79
78
|
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
@@ -85,59 +84,43 @@ async function addCommand(components) {
|
|
|
85
84
|
if (config.components?.libPath) {
|
|
86
85
|
libDir = config.components.libPath;
|
|
87
86
|
}
|
|
88
|
-
if (config.components?.aliasPrefix) {
|
|
89
|
-
aliasPrefix = config.components.aliasPrefix;
|
|
90
|
-
}
|
|
91
87
|
}
|
|
92
88
|
}
|
|
93
|
-
catch
|
|
89
|
+
catch {
|
|
94
90
|
// Config doesn't exist or has errors, use defaults
|
|
95
91
|
}
|
|
96
92
|
console.log(chalk_1.default.gray(`š UI components: ${uiDir}`));
|
|
97
93
|
console.log(chalk_1.default.gray(`š Utilities: ${libDir}\n`));
|
|
98
94
|
await fs_extra_1.default.ensureDir(uiDir);
|
|
99
95
|
await fs_extra_1.default.ensureDir(libDir);
|
|
100
|
-
// Find package location
|
|
96
|
+
// Find package location for templates
|
|
101
97
|
const packageDir = node_path_1.default.dirname(require.resolve('@shahadpichen/docpush/package.json'));
|
|
102
|
-
const
|
|
98
|
+
const templatesDir = node_path_1.default.join(packageDir, 'templates', 'components');
|
|
103
99
|
// Copy components
|
|
104
100
|
for (const compName of toInstall) {
|
|
105
101
|
const comp = COMPONENTS[compName];
|
|
106
102
|
for (const file of comp.files) {
|
|
107
|
-
let srcPath;
|
|
108
103
|
let destPath;
|
|
109
104
|
if (compName === 'utils') {
|
|
110
105
|
// Utils go to src/lib
|
|
111
|
-
srcPath = node_path_1.default.join(reactSrcDir, 'lib', file.replace('.ts', '.js'));
|
|
112
106
|
destPath = node_path_1.default.join(libDir, file);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
import { twMerge } from 'tailwind-merge';
|
|
116
|
-
|
|
117
|
-
export function cn(...inputs: ClassValue[]) {
|
|
118
|
-
return twMerge(clsx(inputs));
|
|
119
|
-
}
|
|
120
|
-
`;
|
|
121
|
-
await fs_extra_1.default.writeFile(destPath, tsContent);
|
|
122
|
-
console.log(chalk_1.default.green('ā'), `Created ${destPath}`);
|
|
107
|
+
const content = getUtilsTemplate();
|
|
108
|
+
await fs_extra_1.default.writeFile(destPath, content);
|
|
123
109
|
}
|
|
124
110
|
else {
|
|
125
|
-
//
|
|
126
|
-
const
|
|
127
|
-
'button',
|
|
128
|
-
'input',
|
|
129
|
-
'textarea',
|
|
130
|
-
'badge',
|
|
131
|
-
'card',
|
|
132
|
-
'scroll-area',
|
|
133
|
-
].includes(compName);
|
|
134
|
-
const srcSubdir = isUiComponent ? 'components/ui' : 'components';
|
|
135
|
-
// Get component template from package
|
|
136
|
-
const template = getComponentTemplate(compName);
|
|
111
|
+
// Try to read from templates directory first
|
|
112
|
+
const templatePath = node_path_1.default.join(templatesDir, file);
|
|
137
113
|
destPath = node_path_1.default.join(uiDir, file);
|
|
138
|
-
await fs_extra_1.default.
|
|
139
|
-
|
|
114
|
+
if (await fs_extra_1.default.pathExists(templatePath)) {
|
|
115
|
+
await fs_extra_1.default.copy(templatePath, destPath);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Fallback to inline template
|
|
119
|
+
const template = getComponentTemplate(compName);
|
|
120
|
+
await fs_extra_1.default.writeFile(destPath, template);
|
|
121
|
+
}
|
|
140
122
|
}
|
|
123
|
+
console.log(chalk_1.default.green('ā'), `Created ${destPath}`);
|
|
141
124
|
}
|
|
142
125
|
}
|
|
143
126
|
console.log(chalk_1.default.blue('\n⨠Components added successfully!'));
|
|
@@ -149,18 +132,30 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
149
132
|
console.log(' npm install clsx tailwind-merge class-variance-authority');
|
|
150
133
|
console.log(' npm install @radix-ui/react-scroll-area @radix-ui/react-slot');
|
|
151
134
|
}
|
|
135
|
+
function getUtilsTemplate() {
|
|
136
|
+
return `import { clsx, type ClassValue } from 'clsx';
|
|
137
|
+
import { twMerge } from 'tailwind-merge';
|
|
138
|
+
|
|
139
|
+
export function cn(...inputs: ClassValue[]) {
|
|
140
|
+
return twMerge(clsx(inputs));
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
152
144
|
/**
|
|
153
|
-
* Get component TypeScript template
|
|
145
|
+
* Get component TypeScript template (inline fallback)
|
|
154
146
|
*/
|
|
155
147
|
function getComponentTemplate(name) {
|
|
148
|
+
// Basic templates - users can customize after copying
|
|
156
149
|
const templates = {
|
|
157
|
-
button: `
|
|
150
|
+
button: `'use client';
|
|
151
|
+
|
|
152
|
+
import * as React from 'react';
|
|
158
153
|
import { Slot } from '@radix-ui/react-slot';
|
|
159
154
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
160
155
|
import { cn } from '@/lib/utils';
|
|
161
156
|
|
|
162
157
|
const buttonVariants = cva(
|
|
163
|
-
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium
|
|
158
|
+
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
|
|
164
159
|
{
|
|
165
160
|
variants: {
|
|
166
161
|
variant: {
|
|
@@ -178,16 +173,11 @@ const buttonVariants = cva(
|
|
|
178
173
|
icon: 'h-10 w-10',
|
|
179
174
|
},
|
|
180
175
|
},
|
|
181
|
-
defaultVariants: {
|
|
182
|
-
variant: 'default',
|
|
183
|
-
size: 'default',
|
|
184
|
-
},
|
|
176
|
+
defaultVariants: { variant: 'default', size: 'default' },
|
|
185
177
|
}
|
|
186
178
|
);
|
|
187
179
|
|
|
188
|
-
export interface ButtonProps
|
|
189
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
190
|
-
VariantProps<typeof buttonVariants> {
|
|
180
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
191
181
|
asChild?: boolean;
|
|
192
182
|
}
|
|
193
183
|
|
|
@@ -201,7 +191,9 @@ Button.displayName = 'Button';
|
|
|
201
191
|
|
|
202
192
|
export { Button, buttonVariants };
|
|
203
193
|
`,
|
|
204
|
-
input: `
|
|
194
|
+
input: `'use client';
|
|
195
|
+
|
|
196
|
+
import * as React from 'react';
|
|
205
197
|
import { cn } from '@/lib/utils';
|
|
206
198
|
|
|
207
199
|
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
@@ -211,7 +203,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type,
|
|
|
211
203
|
<input
|
|
212
204
|
type={type}
|
|
213
205
|
className={cn(
|
|
214
|
-
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm
|
|
206
|
+
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
|
215
207
|
className
|
|
216
208
|
)}
|
|
217
209
|
ref={ref}
|
|
@@ -223,7 +215,9 @@ Input.displayName = 'Input';
|
|
|
223
215
|
|
|
224
216
|
export { Input };
|
|
225
217
|
`,
|
|
226
|
-
textarea: `
|
|
218
|
+
textarea: `'use client';
|
|
219
|
+
|
|
220
|
+
import * as React from 'react';
|
|
227
221
|
import { cn } from '@/lib/utils';
|
|
228
222
|
|
|
229
223
|
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
|
@@ -232,7 +226,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ classNa
|
|
|
232
226
|
return (
|
|
233
227
|
<textarea
|
|
234
228
|
className={cn(
|
|
235
|
-
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm
|
|
229
|
+
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
|
236
230
|
className
|
|
237
231
|
)}
|
|
238
232
|
ref={ref}
|
|
@@ -244,28 +238,26 @@ Textarea.displayName = 'Textarea';
|
|
|
244
238
|
|
|
245
239
|
export { Textarea };
|
|
246
240
|
`,
|
|
247
|
-
badge: `
|
|
241
|
+
badge: `'use client';
|
|
242
|
+
|
|
243
|
+
import * as React from 'react';
|
|
248
244
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
249
245
|
import { cn } from '@/lib/utils';
|
|
250
246
|
|
|
251
247
|
const badgeVariants = cva(
|
|
252
|
-
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors
|
|
248
|
+
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors',
|
|
253
249
|
{
|
|
254
250
|
variants: {
|
|
255
251
|
variant: {
|
|
256
|
-
default: 'border-transparent bg-primary text-primary-foreground
|
|
257
|
-
secondary: 'border-transparent bg-secondary text-secondary-foreground
|
|
258
|
-
destructive: 'border-transparent bg-destructive text-destructive-foreground
|
|
252
|
+
default: 'border-transparent bg-primary text-primary-foreground',
|
|
253
|
+
secondary: 'border-transparent bg-secondary text-secondary-foreground',
|
|
254
|
+
destructive: 'border-transparent bg-destructive text-destructive-foreground',
|
|
259
255
|
outline: 'text-foreground',
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
approved: 'border-transparent bg-green-100 text-green-800',
|
|
263
|
-
rejected: 'border-transparent bg-red-100 text-red-800',
|
|
256
|
+
warning: 'border-transparent bg-yellow-100 text-yellow-800',
|
|
257
|
+
success: 'border-transparent bg-green-100 text-green-800',
|
|
264
258
|
},
|
|
265
259
|
},
|
|
266
|
-
defaultVariants: {
|
|
267
|
-
variant: 'default',
|
|
268
|
-
},
|
|
260
|
+
defaultVariants: { variant: 'default' },
|
|
269
261
|
}
|
|
270
262
|
);
|
|
271
263
|
|
|
@@ -277,7 +269,9 @@ function Badge({ className, variant, ...props }: BadgeProps) {
|
|
|
277
269
|
|
|
278
270
|
export { Badge, badgeVariants };
|
|
279
271
|
`,
|
|
280
|
-
card: `
|
|
272
|
+
card: `'use client';
|
|
273
|
+
|
|
274
|
+
import * as React from 'react';
|
|
281
275
|
import { cn } from '@/lib/utils';
|
|
282
276
|
|
|
283
277
|
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
@@ -288,9 +282,7 @@ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElemen
|
|
|
288
282
|
Card.displayName = 'Card';
|
|
289
283
|
|
|
290
284
|
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
291
|
-
({ className, ...props }, ref) => (
|
|
292
|
-
<div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
|
|
293
|
-
)
|
|
285
|
+
({ className, ...props }, ref) => <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
|
|
294
286
|
);
|
|
295
287
|
CardHeader.displayName = 'CardHeader';
|
|
296
288
|
|
|
@@ -301,26 +293,12 @@ const CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTML
|
|
|
301
293
|
);
|
|
302
294
|
CardTitle.displayName = 'CardTitle';
|
|
303
295
|
|
|
304
|
-
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
305
|
-
({ className, ...props }, ref) => (
|
|
306
|
-
<p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
|
|
307
|
-
)
|
|
308
|
-
);
|
|
309
|
-
CardDescription.displayName = 'CardDescription';
|
|
310
|
-
|
|
311
296
|
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
312
297
|
({ className, ...props }, ref) => <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
|
313
298
|
);
|
|
314
299
|
CardContent.displayName = 'CardContent';
|
|
315
300
|
|
|
316
|
-
|
|
317
|
-
({ className, ...props }, ref) => (
|
|
318
|
-
<div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
|
|
319
|
-
)
|
|
320
|
-
);
|
|
321
|
-
CardFooter.displayName = 'CardFooter';
|
|
322
|
-
|
|
323
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
|
301
|
+
export { Card, CardHeader, CardTitle, CardContent };
|
|
324
302
|
`,
|
|
325
303
|
'scroll-area': `'use client';
|
|
326
304
|
|
|
@@ -333,38 +311,237 @@ const ScrollArea = React.forwardRef<
|
|
|
333
311
|
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
|
334
312
|
>(({ className, children, ...props }, ref) => (
|
|
335
313
|
<ScrollAreaPrimitive.Root ref={ref} className={cn('relative overflow-hidden', className)} {...props}>
|
|
336
|
-
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
314
|
+
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">{children}</ScrollAreaPrimitive.Viewport>
|
|
315
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
316
|
+
orientation="vertical"
|
|
317
|
+
className="flex touch-none select-none transition-colors h-full w-2.5 border-l border-l-transparent p-[1px]"
|
|
318
|
+
>
|
|
319
|
+
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
|
320
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
340
321
|
<ScrollAreaPrimitive.Corner />
|
|
341
322
|
</ScrollAreaPrimitive.Root>
|
|
342
323
|
));
|
|
343
|
-
ScrollArea.displayName =
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
324
|
+
ScrollArea.displayName = 'ScrollArea';
|
|
325
|
+
|
|
326
|
+
export { ScrollArea };
|
|
327
|
+
`,
|
|
328
|
+
'docs-sidebar': `'use client';
|
|
329
|
+
|
|
330
|
+
import * as React from 'react';
|
|
331
|
+
import { cn } from '@/lib/utils';
|
|
332
|
+
import { Button } from '@/components/ui/button';
|
|
333
|
+
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
334
|
+
|
|
335
|
+
interface DocTreeItem { path: string; type: 'file' | 'dir'; }
|
|
336
|
+
interface DocsSidebarProps { tree: DocTreeItem[]; activePath?: string; onSelect?: (path: string) => void; className?: string; }
|
|
337
|
+
|
|
338
|
+
export function DocsSidebar({ tree, activePath, onSelect, className }: DocsSidebarProps) {
|
|
339
|
+
return (
|
|
340
|
+
<ScrollArea className={cn('h-full w-64 border-r', className)}>
|
|
341
|
+
<div className="space-y-1 py-4 px-3">
|
|
342
|
+
{tree.filter(item => item.type === 'file').map((item) => {
|
|
343
|
+
const isActive = item.path === activePath;
|
|
344
|
+
const fileName = item.path.split('/').pop()?.replace('.md', '') || item.path;
|
|
345
|
+
return (
|
|
346
|
+
<Button
|
|
347
|
+
key={item.path}
|
|
348
|
+
variant={isActive ? 'secondary' : 'ghost'}
|
|
349
|
+
size="sm"
|
|
350
|
+
className={cn('w-full justify-start', isActive && 'bg-muted font-medium')}
|
|
351
|
+
onClick={() => onSelect?.(item.path)}
|
|
352
|
+
>
|
|
353
|
+
{fileName}
|
|
354
|
+
</Button>
|
|
355
|
+
);
|
|
356
|
+
})}
|
|
357
|
+
</div>
|
|
358
|
+
</ScrollArea>
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
`,
|
|
362
|
+
'markdown-viewer': `'use client';
|
|
364
363
|
|
|
365
|
-
|
|
364
|
+
import * as React from 'react';
|
|
365
|
+
import { cn } from '@/lib/utils';
|
|
366
|
+
|
|
367
|
+
interface MarkdownViewerProps { content: string; className?: string; }
|
|
368
|
+
|
|
369
|
+
export function MarkdownViewer({ content, className }: MarkdownViewerProps) {
|
|
370
|
+
const html = React.useMemo(() => {
|
|
371
|
+
return content
|
|
372
|
+
.replace(/^### (.*$)/gim, '<h3 class="text-lg font-semibold mt-6 mb-2">$1</h3>')
|
|
373
|
+
.replace(/^## (.*$)/gim, '<h2 class="text-xl font-semibold mt-8 mb-3">$1</h2>')
|
|
374
|
+
.replace(/^# (.*$)/gim, '<h1 class="text-2xl font-bold mt-10 mb-4">$1</h1>')
|
|
375
|
+
.replace(/\\*\\*(.*?)\\*\\*/gim, '<strong>$1</strong>')
|
|
376
|
+
.replace(/\\*(.*?)\\*/gim, '<em>$1</em>')
|
|
377
|
+
.replace(/^- (.*$)/gim, '<li class="ml-4">$1</li>')
|
|
378
|
+
.replace(/\\n\\n/gim, '</p><p class="my-4">')
|
|
379
|
+
.replace(/\\n/gim, '<br>');
|
|
380
|
+
}, [content]);
|
|
381
|
+
|
|
382
|
+
return <article className={cn('prose max-w-none p-6', className)} dangerouslySetInnerHTML={{ __html: '<p>' + html + '</p>' }} />;
|
|
383
|
+
}
|
|
384
|
+
`,
|
|
385
|
+
'markdown-editor': `'use client';
|
|
386
|
+
|
|
387
|
+
import * as React from 'react';
|
|
388
|
+
import { cn } from '@/lib/utils';
|
|
389
|
+
import { MarkdownViewer } from '@/components/ui/markdown-viewer';
|
|
390
|
+
import { Button } from '@/components/ui/button';
|
|
391
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
392
|
+
|
|
393
|
+
interface MarkdownEditorProps { initialContent?: string; onSave?: (content: string) => void; className?: string; }
|
|
394
|
+
|
|
395
|
+
export function MarkdownEditor({ initialContent = '', onSave, className }: MarkdownEditorProps) {
|
|
396
|
+
const [content, setContent] = React.useState(initialContent);
|
|
397
|
+
const [showPreview, setShowPreview] = React.useState(false);
|
|
398
|
+
|
|
399
|
+
return (
|
|
400
|
+
<div className={cn('flex flex-col', className)}>
|
|
401
|
+
<div className="flex items-center gap-2 border-b p-2">
|
|
402
|
+
<Button variant={showPreview ? 'ghost' : 'secondary'} size="sm" onClick={() => setShowPreview(false)}>Edit</Button>
|
|
403
|
+
<Button variant={showPreview ? 'secondary' : 'ghost'} size="sm" onClick={() => setShowPreview(true)}>Preview</Button>
|
|
404
|
+
{onSave && <Button size="sm" className="ml-auto" onClick={() => onSave(content)}>Save</Button>}
|
|
405
|
+
</div>
|
|
406
|
+
<div className="flex-1 overflow-auto">
|
|
407
|
+
{showPreview ? <MarkdownViewer content={content} /> : (
|
|
408
|
+
<Textarea value={content} onChange={(e) => setContent(e.target.value)} className="min-h-[400px] resize-none border-0 font-mono" placeholder="Write markdown..." />
|
|
409
|
+
)}
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
`,
|
|
415
|
+
'comments-panel': `'use client';
|
|
416
|
+
|
|
417
|
+
import * as React from 'react';
|
|
418
|
+
import { cn } from '@/lib/utils';
|
|
419
|
+
import { Button } from '@/components/ui/button';
|
|
420
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
421
|
+
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
422
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
423
|
+
|
|
424
|
+
interface Comment { id: string; userName: string | null; content: string; createdAt: number; }
|
|
425
|
+
interface CommentsPanelProps { comments: Comment[]; onAddComment?: (content: string) => void; className?: string; }
|
|
426
|
+
|
|
427
|
+
export function CommentsPanel({ comments, onAddComment, className }: CommentsPanelProps) {
|
|
428
|
+
const [newComment, setNewComment] = React.useState('');
|
|
429
|
+
|
|
430
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
431
|
+
e.preventDefault();
|
|
432
|
+
if (newComment.trim() && onAddComment) { onAddComment(newComment); setNewComment(''); }
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
return (
|
|
436
|
+
<Card className={cn('flex flex-col', className)}>
|
|
437
|
+
<CardHeader className="border-b py-3"><CardTitle className="text-base">Comments ({comments.length})</CardTitle></CardHeader>
|
|
438
|
+
<ScrollArea className="flex-1">
|
|
439
|
+
<CardContent className="space-y-4 p-4">
|
|
440
|
+
{comments.length === 0 ? <p className="text-center text-muted-foreground py-8">No comments yet</p> : (
|
|
441
|
+
comments.map((c) => (
|
|
442
|
+
<div key={c.id} className="rounded-lg border p-3">
|
|
443
|
+
<div className="flex justify-between text-xs text-muted-foreground">
|
|
444
|
+
<span className="font-medium">{c.userName || 'Anonymous'}</span>
|
|
445
|
+
<span>{new Date(c.createdAt * 1000).toLocaleDateString()}</span>
|
|
446
|
+
</div>
|
|
447
|
+
<p className="mt-2 text-sm">{c.content}</p>
|
|
448
|
+
</div>
|
|
449
|
+
))
|
|
450
|
+
)}
|
|
451
|
+
</CardContent>
|
|
452
|
+
</ScrollArea>
|
|
453
|
+
{onAddComment && (
|
|
454
|
+
<form onSubmit={handleSubmit} className="border-t p-4">
|
|
455
|
+
<Textarea value={newComment} onChange={(e) => setNewComment(e.target.value)} placeholder="Add a comment..." className="mb-2" />
|
|
456
|
+
<Button type="submit" size="sm" disabled={!newComment.trim()}>Send</Button>
|
|
457
|
+
</form>
|
|
458
|
+
)}
|
|
459
|
+
</Card>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
`,
|
|
463
|
+
'drafts-list': `'use client';
|
|
464
|
+
|
|
465
|
+
import * as React from 'react';
|
|
466
|
+
import { cn } from '@/lib/utils';
|
|
467
|
+
import { Badge } from '@/components/ui/badge';
|
|
468
|
+
import { Button } from '@/components/ui/button';
|
|
469
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
470
|
+
|
|
471
|
+
interface Draft { id: string; docPath: string; title: string; status: 'pending' | 'approved' | 'rejected'; createdAt: number; }
|
|
472
|
+
interface DraftsListProps { drafts: Draft[]; onSelect?: (draft: Draft) => void; onApprove?: (id: string) => void; onReject?: (id: string) => void; showActions?: boolean; className?: string; }
|
|
473
|
+
|
|
474
|
+
const statusVariants = { pending: 'warning', approved: 'success', rejected: 'destructive' } as const;
|
|
475
|
+
|
|
476
|
+
export function DraftsList({ drafts, onSelect, onApprove, onReject, showActions = false, className }: DraftsListProps) {
|
|
477
|
+
return (
|
|
478
|
+
<Card className={cn('', className)}>
|
|
479
|
+
<CardHeader><CardTitle>Drafts</CardTitle></CardHeader>
|
|
480
|
+
<CardContent>
|
|
481
|
+
{drafts.length === 0 ? <p className="text-center text-muted-foreground py-8">No drafts found</p> : (
|
|
482
|
+
<div className="space-y-2">
|
|
483
|
+
{drafts.map((draft) => (
|
|
484
|
+
<div key={draft.id} className={cn('flex items-center justify-between rounded-lg border p-3', onSelect && 'cursor-pointer hover:bg-muted/50')} onClick={() => onSelect?.(draft)}>
|
|
485
|
+
<div><p className="font-medium">{draft.title}</p><p className="text-xs text-muted-foreground">{draft.docPath}</p></div>
|
|
486
|
+
<div className="flex items-center gap-2">
|
|
487
|
+
<Badge variant={statusVariants[draft.status]}>{draft.status}</Badge>
|
|
488
|
+
{showActions && draft.status === 'pending' && (
|
|
489
|
+
<>
|
|
490
|
+
<Button size="icon" variant="ghost" className="h-8 w-8 text-green-600" onClick={(e) => { e.stopPropagation(); onApprove?.(draft.id); }}>ā</Button>
|
|
491
|
+
<Button size="icon" variant="ghost" className="h-8 w-8 text-red-600" onClick={(e) => { e.stopPropagation(); onReject?.(draft.id); }}>ā</Button>
|
|
492
|
+
</>
|
|
493
|
+
)}
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
))}
|
|
497
|
+
</div>
|
|
498
|
+
)}
|
|
499
|
+
</CardContent>
|
|
500
|
+
</Card>
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
`,
|
|
504
|
+
'search-bar': `'use client';
|
|
505
|
+
|
|
506
|
+
import * as React from 'react';
|
|
507
|
+
import { cn } from '@/lib/utils';
|
|
508
|
+
import { Input } from '@/components/ui/input';
|
|
509
|
+
|
|
510
|
+
interface SearchResult { path: string; title: string; excerpt: string; }
|
|
511
|
+
interface SearchBarProps { onSearch?: (query: string) => Promise<SearchResult[]>; onSelect?: (path: string) => void; placeholder?: string; className?: string; }
|
|
512
|
+
|
|
513
|
+
export function SearchBar({ onSearch, onSelect, placeholder = 'Search docs...', className }: SearchBarProps) {
|
|
514
|
+
const [query, setQuery] = React.useState('');
|
|
515
|
+
const [results, setResults] = React.useState<SearchResult[]>([]);
|
|
516
|
+
const [loading, setLoading] = React.useState(false);
|
|
517
|
+
|
|
518
|
+
const handleSearch = async (value: string) => {
|
|
519
|
+
setQuery(value);
|
|
520
|
+
if (value.length < 2 || !onSearch) { setResults([]); return; }
|
|
521
|
+
setLoading(true);
|
|
522
|
+
try { const res = await onSearch(value); setResults(res); } catch { setResults([]); } finally { setLoading(false); }
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
return (
|
|
526
|
+
<div className={cn('relative', className)}>
|
|
527
|
+
<Input value={query} onChange={(e) => handleSearch(e.target.value)} placeholder={placeholder} />
|
|
528
|
+
{results.length > 0 && (
|
|
529
|
+
<div className="absolute top-full left-0 right-0 z-50 mt-1 rounded-md border bg-popover shadow-lg max-h-[300px] overflow-auto">
|
|
530
|
+
{results.map((r) => (
|
|
531
|
+
<button key={r.path} type="button" onClick={() => { onSelect?.(r.path); setQuery(''); setResults([]); }} className="flex w-full flex-col p-3 text-left hover:bg-muted border-b last:border-0">
|
|
532
|
+
<span className="font-medium">{r.title}</span>
|
|
533
|
+
<span className="text-xs text-muted-foreground">{r.excerpt}</span>
|
|
534
|
+
</button>
|
|
535
|
+
))}
|
|
536
|
+
</div>
|
|
537
|
+
)}
|
|
538
|
+
{loading && <div className="absolute top-full left-0 right-0 mt-1 p-4 text-center text-sm text-muted-foreground border rounded-md bg-popover">Searching...</div>}
|
|
539
|
+
</div>
|
|
540
|
+
);
|
|
541
|
+
}
|
|
366
542
|
`,
|
|
367
543
|
};
|
|
368
|
-
return templates[name] ||
|
|
544
|
+
return (templates[name] ||
|
|
545
|
+
`// Component template not found: ${name}\n// Please install from: npx @shahadpichen/docpush add ${name}`);
|
|
369
546
|
}
|
|
370
547
|
//# sourceMappingURL=add.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":";;;;;AAsCA,
|
|
1
|
+
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,11 +11,12 @@ export declare function useDrafts(status?: string): {
|
|
|
11
11
|
drafts: Draft[];
|
|
12
12
|
loading: boolean;
|
|
13
13
|
error: string | null;
|
|
14
|
+
isSubmitting: boolean;
|
|
14
15
|
createDraft: (data: {
|
|
15
16
|
docPath: string;
|
|
16
17
|
title: string;
|
|
17
18
|
content?: string;
|
|
18
|
-
}) => Promise<Draft>;
|
|
19
|
+
}) => Promise<Draft | null>;
|
|
19
20
|
updateDraft: (id: string, content: string, message?: string) => Promise<void>;
|
|
20
21
|
approveDraft: (id: string) => Promise<void>;
|
|
21
22
|
rejectDraft: (id: string, reason?: string) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-drafts.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-drafts.ts"],"names":[],"mappings":"AAKA,UAAU,KAAK;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"use-drafts.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-drafts.ts"],"names":[],"mappings":"AAKA,UAAU,KAAK;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM;;;;;wBAwBN;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;sBAoBtD,MAAM,WAAW,MAAM,YAAY,MAAM;uBAQxC,MAAM;sBAKP,MAAM,WAAW,MAAM;;EAmBvD"}
|
|
@@ -42,6 +42,7 @@ function useDrafts(status) {
|
|
|
42
42
|
const [drafts, setDrafts] = React.useState([]);
|
|
43
43
|
const [loading, setLoading] = React.useState(true);
|
|
44
44
|
const [error, setError] = React.useState(null);
|
|
45
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
45
46
|
const fetchDrafts = React.useCallback(async () => {
|
|
46
47
|
setLoading(true);
|
|
47
48
|
try {
|
|
@@ -60,12 +61,23 @@ function useDrafts(status) {
|
|
|
60
61
|
fetchDrafts();
|
|
61
62
|
}, [fetchDrafts]);
|
|
62
63
|
const createDraft = async (data) => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
// Prevent duplicate submissions
|
|
65
|
+
if (isSubmitting) {
|
|
66
|
+
console.warn('Draft submission already in progress');
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
setIsSubmitting(true);
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetcher('/api/drafts', {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
body: JSON.stringify(data),
|
|
74
|
+
});
|
|
75
|
+
await fetchDrafts();
|
|
76
|
+
return res.draft;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
setIsSubmitting(false);
|
|
80
|
+
}
|
|
69
81
|
};
|
|
70
82
|
const updateDraft = async (id, content, message) => {
|
|
71
83
|
await fetcher(`/api/drafts/${id}`, {
|
|
@@ -89,6 +101,7 @@ function useDrafts(status) {
|
|
|
89
101
|
drafts,
|
|
90
102
|
loading,
|
|
91
103
|
error,
|
|
104
|
+
isSubmitting,
|
|
92
105
|
createDraft,
|
|
93
106
|
updateDraft,
|
|
94
107
|
approveDraft,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-drafts.js","sourceRoot":"","sources":["../../../src/react/hooks/use-drafts.ts"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeb,
|
|
1
|
+
{"version":3,"file":"use-drafts.js","sourceRoot":"","sources":["../../../src/react/hooks/use-drafts.ts"],"names":[],"mappings":"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"}
|