feat: publication IA (magazine/brief/essay) + fixes critique
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m22s
CI / Deploy production (on server) (push) Has been skipped

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:
Antigravity
2026-06-28 07:32:57 +00:00
parent 902fe95a69
commit 96e7902f01
169 changed files with 5382 additions and 1527 deletions

View File

@@ -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}`)
}}
/>
)}