feat: publication IA (magazine/brief/essay) + fixes critique
Publication IA: - 4 templates (magazine, brief, essay, simple) avec CSS riche - Rewrite IA (article/exercises/tutorial/reference/mixed) - Modération avec timeout 12s + fallback safe - Quotas publish_enhance par tier (basic=2, pro=15, business=100) - Détection contenu stale (hash) - Migration DB publishedContent/publishedTemplate/publishedSourceHash Fixes: - cheerio v1.2: Element -> AnyNode (domhandler), decodeEntities cast - _isShared ajouté au type Note (champ virtuel serveur) - callout colors PDF export: extraction fonction pure testable - admin/published: guard note.userId null - Cmd+S fonctionne en mode dialog (pas seulement fullPage) i18n: - 23 clés publish* traduites dans les 15 locales - Extension Web Clipper: 13 locales mise à jour Tests: - callout-colors.test.ts (6 tests) - note-visible-in-view.test.ts (5 tests) - entitlements.test.ts + byok-entitlements.test.ts: mock usageLog + unstubAllEnvs - 199/199 tests passent Tracker: user-stories.md sync avec sprint-status.yaml
This commit is contained in:
@@ -398,25 +398,27 @@ function SidebarCarnetItem({
|
||||
<div className="absolute start-[8px] top-1/2 w-[8px] h-px bg-border/40" />
|
||||
)}
|
||||
|
||||
<div
|
||||
{...dragHandleProps}
|
||||
className="absolute start-1 top-1/2 -translate-y-1/2 p-1 rounded text-muted-foreground/30 hover:text-muted-foreground cursor-grab active:cursor-grabbing opacity-0 group-hover:opacity-100 transition-opacity z-10"
|
||||
>
|
||||
<GripVertical size={12} />
|
||||
</div>
|
||||
<div className="flex-1 flex items-center gap-0.5">
|
||||
{/* Drag handle — visible au hover, dans son propre slot avant le chevron */}
|
||||
<div
|
||||
{...dragHandleProps}
|
||||
className="shrink-0 w-5 h-5 flex items-center justify-center rounded text-muted-foreground/0 group-hover:text-muted-foreground/50 hover:!text-muted-foreground cursor-grab active:cursor-grabbing transition-colors"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<GripVertical size={11} />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex items-center gap-1">
|
||||
{hasChildren ? (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); toggleExpand() }}
|
||||
className="p-1 hover:bg-foreground/5 rounded-md transition-colors text-muted-foreground"
|
||||
className="shrink-0 w-5 h-5 flex items-center justify-center hover:bg-foreground/5 rounded-md transition-colors text-muted-foreground"
|
||||
>
|
||||
<motion.div animate={{ rotate: isExpanded ? 90 : 0 }} transition={{ duration: 0.2 }}>
|
||||
<ChevronRight size={14} className="rtl:scale-x-[-1]" />
|
||||
<ChevronRight size={13} className="rtl:scale-x-[-1]" />
|
||||
</motion.div>
|
||||
</button>
|
||||
) : (
|
||||
<div className="w-6" />
|
||||
<div className="w-5" />
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
@@ -1704,9 +1706,9 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
{showAiWizard && (
|
||||
<AiNotebookWizard
|
||||
onClose={() => setShowAiWizard(false)}
|
||||
onComplete={() => {
|
||||
onComplete={(notebookId: string) => {
|
||||
setShowAiWizard(false)
|
||||
window.location.reload()
|
||||
router.push(`/home?notebook=${notebookId}`)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user