feat: rhythm tracking and structured review
This commit is contained in:
@@ -41,6 +41,7 @@ func (s *Store) UpdateForeshadow(chapter int, updates []domain.ForeshadowUpdate)
|
||||
for _, u := range updates {
|
||||
switch u.Action {
|
||||
case "plant":
|
||||
idx[u.ID] = len(entries)
|
||||
entries = append(entries, domain.ForeshadowEntry{
|
||||
ID: u.ID,
|
||||
Description: u.Description,
|
||||
@@ -61,6 +62,21 @@ func (s *Store) UpdateForeshadow(chapter int, updates []domain.ForeshadowUpdate)
|
||||
return s.SaveForeshadowLedger(entries)
|
||||
}
|
||||
|
||||
// LoadActiveForeshadow 返回未回收的伏笔条目(status != "resolved")。
|
||||
func (s *Store) LoadActiveForeshadow() ([]domain.ForeshadowEntry, error) {
|
||||
all, err := s.LoadForeshadowLedger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var active []domain.ForeshadowEntry
|
||||
for _, e := range all {
|
||||
if e.Status != "resolved" {
|
||||
active = append(active, e)
|
||||
}
|
||||
}
|
||||
return active, nil
|
||||
}
|
||||
|
||||
func renderForeshadow(entries []domain.ForeshadowEntry) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("# 伏笔账本\n\n")
|
||||
|
||||
@@ -62,7 +62,8 @@ func (s *Store) UpdatePhase(phase domain.Phase) error {
|
||||
|
||||
// MarkChapterComplete 标记章节完成,原子性更新进度。
|
||||
// 支持重写场景:如果章节已完成,先减去旧字数再加新字数。
|
||||
func (s *Store) MarkChapterComplete(chapter, wordCount int) error {
|
||||
// hookType 和 dominantStrand 用于节奏追踪,可为空。
|
||||
func (s *Store) MarkChapterComplete(chapter, wordCount int, hookType, dominantStrand string) error {
|
||||
p, err := s.LoadProgress()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -89,6 +90,29 @@ func (s *Store) MarkChapterComplete(chapter, wordCount int) error {
|
||||
p.InProgressChapter = 0
|
||||
p.CompletedScenes = nil
|
||||
p.Phase = domain.PhaseWriting
|
||||
|
||||
// 节奏追踪:按章节顺序填充 history(确保索引对齐)
|
||||
if dominantStrand != "" {
|
||||
for len(p.StrandHistory) < chapter-1 {
|
||||
p.StrandHistory = append(p.StrandHistory, "")
|
||||
}
|
||||
if len(p.StrandHistory) < chapter {
|
||||
p.StrandHistory = append(p.StrandHistory, dominantStrand)
|
||||
} else {
|
||||
p.StrandHistory[chapter-1] = dominantStrand
|
||||
}
|
||||
}
|
||||
if hookType != "" {
|
||||
for len(p.HookHistory) < chapter-1 {
|
||||
p.HookHistory = append(p.HookHistory, "")
|
||||
}
|
||||
if len(p.HookHistory) < chapter {
|
||||
p.HookHistory = append(p.HookHistory, hookType)
|
||||
} else {
|
||||
p.HookHistory[chapter-1] = hookType
|
||||
}
|
||||
}
|
||||
|
||||
return s.SaveProgress(p)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,22 @@ func (s *Store) AppendTimelineEvents(newEvents []domain.TimelineEvent) error {
|
||||
return s.SaveTimeline(append(existing, newEvents...))
|
||||
}
|
||||
|
||||
// LoadRecentTimeline 返回最近 window 章内的时间线事件(chapter >= current-window)。
|
||||
func (s *Store) LoadRecentTimeline(current, window int) ([]domain.TimelineEvent, error) {
|
||||
all, err := s.LoadTimeline()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
minCh := max(current-window, 1)
|
||||
var filtered []domain.TimelineEvent
|
||||
for _, e := range all {
|
||||
if e.Chapter >= minCh {
|
||||
filtered = append(filtered, e)
|
||||
}
|
||||
}
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
func renderTimeline(events []domain.TimelineEvent) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("# 时间线\n\n")
|
||||
|
||||
Reference in New Issue
Block a user