feat: ship v0.9.0 with pomodoro, achievements, and injection history
Add pomodoro timer with goal notifications, writing streak milestones, and knowledge injection logging with UI previews across AI writing flows. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import { useReferenceStore } from '@renderer/stores/useReferenceStore'
|
||||
import { useSettingsStore } from '@renderer/stores/useSettingsStore'
|
||||
import { useCockpitStore } from '@renderer/stores/useCockpitStore'
|
||||
import { useWritingStatsStore } from '@renderer/stores/useWritingStatsStore'
|
||||
import { usePomodoroStore, formatPomodoroTime } from '@renderer/stores/usePomodoroStore'
|
||||
import { ipcCall } from '@renderer/lib/ipc-client'
|
||||
import { TipTapEditor, flushEditorSave } from '@renderer/components/editor/TipTapEditor'
|
||||
import { insertLandmarkInEditor } from '@renderer/lib/editor-commands'
|
||||
@@ -94,6 +95,15 @@ export function EditorLayout(): React.JSX.Element {
|
||||
const dailyWordGoal = useSettingsStore((s) => s.dailyWordGoal)
|
||||
const writingStats = useWritingStatsStore((s) => s.stats)
|
||||
const refreshWritingStats = useWritingStatsStore((s) => s.refresh)
|
||||
const pomodoroRunning = usePomodoroStore((s) => s.running)
|
||||
const pomodoroPaused = usePomodoroStore((s) => s.paused)
|
||||
const pomodoroRemaining = usePomodoroStore((s) => s.remainingSec)
|
||||
const pomodoroBookId = usePomodoroStore((s) => s.bookId)
|
||||
const pomodoroInit = usePomodoroStore((s) => s.init)
|
||||
const pomodoroStart = usePomodoroStore((s) => s.start)
|
||||
const pomodoroPause = usePomodoroStore((s) => s.pause)
|
||||
const pomodoroResume = usePomodoroStore((s) => s.resume)
|
||||
const pomodoroCancel = usePomodoroStore((s) => s.cancel)
|
||||
const bridgeChapterId = useCockpitStore((s) => s.bridgeChapterId)
|
||||
const clearBridgeChapter = useCockpitStore((s) => s.clearBridgeChapter)
|
||||
const updateChapterLocal = useBookStore((s) => s.updateChapterLocal)
|
||||
@@ -111,11 +121,21 @@ export function EditorLayout(): React.JSX.Element {
|
||||
const isChapterTarget = target?.kind === 'chapter'
|
||||
const chapterId = target?.kind === 'chapter' ? target.id : selectedChapterId
|
||||
|
||||
useEffect(() => {
|
||||
void pomodoroInit()
|
||||
}, [pomodoroInit])
|
||||
|
||||
useEffect(() => {
|
||||
if (!pomodoroRunning || !pomodoroBookId || !currentBookId) return
|
||||
if (pomodoroBookId === currentBookId) return
|
||||
void pomodoroCancel()
|
||||
}, [currentBookId, pomodoroRunning, pomodoroBookId, pomodoroCancel])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedChapterId && (!target || target.kind === 'chapter')) {
|
||||
setTarget({ kind: 'chapter', id: selectedChapterId })
|
||||
}
|
||||
}, [selectedChapterId, setTarget])
|
||||
}, [selectedChapterId, setTarget, target])
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentBookId || target?.kind !== 'chapter') return
|
||||
@@ -426,6 +446,39 @@ export function EditorLayout(): React.JSX.Element {
|
||||
bookId={currentBookId}
|
||||
/>
|
||||
<div id="editor-statusbar">
|
||||
<div className="pomodoro-control" data-testid="pomodoro-control">
|
||||
{!pomodoroRunning ? (
|
||||
<button
|
||||
type="button"
|
||||
className="pomodoro-btn"
|
||||
title={t('pomodoro.start')}
|
||||
disabled={!currentBookId}
|
||||
onClick={() => currentBookId && void pomodoroStart(currentBookId)}
|
||||
>
|
||||
▶
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="pomodoro-btn"
|
||||
title={pomodoroPaused ? t('pomodoro.resume') : t('pomodoro.pause')}
|
||||
onClick={() => void (pomodoroPaused ? pomodoroResume() : pomodoroPause())}
|
||||
>
|
||||
{pomodoroPaused ? '▶' : '⏸'}
|
||||
</button>
|
||||
<span className="pomodoro-time">{formatPomodoroTime(pomodoroRemaining)}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="pomodoro-btn"
|
||||
title={t('pomodoro.cancel')}
|
||||
onClick={() => void pomodoroCancel()}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<span>{saveStatus}{lastSaved ? ` · ${lastSaved.toLocaleTimeString()}` : ''}</span>
|
||||
{dailyWordGoal > 0 && writingStats && (
|
||||
<span
|
||||
|
||||
Reference in New Issue
Block a user