chore(ci): correct Gitea runner to runs-on ubuntu-24.04 and feat(billing): implement US-3.7 billing/subscription UX
Some checks failed
CI / Deploy production (on server) (push) Has been cancelled
CI / Lint, Unit Tests & Build (push) Has been cancelled

This commit is contained in:
Antigravity
2026-05-28 21:39:08 +00:00
parent 4bfa7c6b69
commit 3b2570d981
11 changed files with 180 additions and 71 deletions

View File

@@ -8,6 +8,7 @@ import { useAiConsent } from '@/components/legal/ai-consent-provider'
import { cn } from '@/lib/utils'
import { toast } from 'sonner'
import { resolveTitleDirection, resolveTitleLang } from '@/lib/clip/rtl-content'
import { InlinePaywall } from '@/components/settings/inline-paywall'
export function NoteTitleBlock() {
const { note, state, actions, readOnly, fullPage } = useNoteEditorContext()
@@ -30,6 +31,12 @@ export function NoteTitleBlock() {
return (
<div className="space-y-4">
{state.quotaExceededFeature === 'auto_title' && (
<InlinePaywall
feature="auto_title"
onDismiss={() => actions.setQuotaExceededFeature(null)}
/>
)}
{/* Title — auto-resizing textarea, adaptive size */}
<div className="group relative">
<textarea
@@ -84,6 +91,10 @@ export function NoteTitleBlock() {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: plain }),
})
if (res.status === 402) {
actions.setQuotaExceededFeature('auto_title')
return
}
if (res.ok) {
const data = await res.json()
const s = data.suggestions?.[0]?.title ?? ''
@@ -127,32 +138,40 @@ export function NoteTitleBlock() {
// Dialog mode title block
return (
<div className="relative">
<input
dir={titleDir}
lang={titleLang}
placeholder={t('notes.titlePlaceholder')}
value={state.title}
onChange={(e) => actions.setTitle(e.target.value)}
disabled={readOnly}
className={cn(
'w-full text-lg font-semibold border-0 focus-visible:ring-0 px-0 bg-transparent',
titleIsRtl ? 'text-right font-[family-name:var(--font-sans)] ps-10' : 'text-left pe-10',
readOnly && 'cursor-default',
)}
/>
<button
onClick={actions.handleGenerateTitles}
disabled={state.isGeneratingTitles || readOnly}
className="absolute right-0 top-1/2 -translate-y-1/2 p-1 hover:bg-purple-100 dark:hover:bg-purple-900 rounded transition-colors"
title={state.isGeneratingTitles ? t('ai.titleGenerating') : t('ai.titleGenerateWithAI')}
>
{state.isGeneratingTitles ? (
<div className="w-4 h-4 border-2 border-purple-500 border-t-transparent rounded-full animate-spin" />
) : (
<Sparkles className="w-4 h-4 text-purple-600 hover:text-purple-700 dark:text-purple-400" />
)}
</button>
<div className="space-y-2">
<div className="relative">
<input
dir={titleDir}
lang={titleLang}
placeholder={t('notes.titlePlaceholder')}
value={state.title}
onChange={(e) => actions.setTitle(e.target.value)}
disabled={readOnly}
className={cn(
'w-full text-lg font-semibold border-0 focus-visible:ring-0 px-0 bg-transparent',
titleIsRtl ? 'text-right font-[family-name:var(--font-sans)] ps-10' : 'text-left pe-10',
readOnly && 'cursor-default',
)}
/>
<button
onClick={actions.handleGenerateTitles}
disabled={state.isGeneratingTitles || readOnly}
className="absolute right-0 top-1/2 -translate-y-1/2 p-1 hover:bg-purple-100 dark:hover:bg-purple-900 rounded transition-colors"
title={state.isGeneratingTitles ? t('ai.titleGenerating') : t('ai.titleGenerateWithAI')}
>
{state.isGeneratingTitles ? (
<div className="w-4 h-4 border-2 border-purple-500 border-t-transparent rounded-full animate-spin" />
) : (
<Sparkles className="w-4 h-4 text-purple-600 hover:text-purple-700 dark:text-purple-400" />
)}
</button>
</div>
{state.quotaExceededFeature === 'auto_title' && (
<InlinePaywall
feature="auto_title"
onDismiss={() => actions.setQuotaExceededFeature(null)}
/>
)}
</div>
)
}