Editable tag text in the table (tag-UI cleanup 2) #68

Merged
bjoern merged 2 commits from feat/tag-text-edit into main 2026-08-13 06:23:58 +02:00
Member

Task 2 of the tag-UI cleanup: the table let you change a tag's strength but not fix its text. Now each row can be edited in place. As with the strength slider, this fixes both the character editor and the settings authors, since TagAuthoring is the shared component.

What changed

  • Each row gains an edit (pencil) action. Clicking it swaps the tag name for an inline text field; Save (check icon / Enter) commits, Cancel (× icon / Escape) discards. The slider and move/remove hide while editing so the row stays clean.
  • A new pure transform PromptTagEditing.Rename: re-canonicalises the term (lowercase, spaces → underscores), updates the display name, and flags it freeform — it's now hand-edited, so unverified against the database (the same rule as the freeform box). Strength and polarity are kept. It no-ops on a blank name or one that would duplicate another authored tag (a tag can't appear twice).

Tests & verification

Four new tests: Rename canonicalises + keeps strength/polarity + flags freeform, rejects a duplicate, and no-ops on blank; plus the edit-in-place and cancel flows through the component. Full suite green (899). Browser-verified on the settings authors: a typo'd long_hai → edit → long hair saved and shown corrected, the editor closing on save.

Last in the batch: D full-width tabs.

🤖 Generated with Claude Code

Task 2 of the tag-UI cleanup: the table let you change a tag's *strength* but not fix its *text*. Now each row can be edited in place. As with the strength slider, this fixes both the character editor and the settings authors, since `TagAuthoring` is the shared component. ## What changed - Each row gains an **edit (pencil)** action. Clicking it swaps the tag name for an inline text field; **Save** (check icon / Enter) commits, **Cancel** (× icon / Escape) discards. The slider and move/remove hide while editing so the row stays clean. - A new pure transform `PromptTagEditing.Rename`: re-canonicalises the term (lowercase, spaces → underscores), updates the display name, and flags it **freeform** — it's now hand-edited, so unverified against the database (the same rule as the freeform box). Strength and polarity are kept. It **no-ops** on a blank name or one that would **duplicate** another authored tag (a tag can't appear twice). ## Tests & verification Four new tests: `Rename` canonicalises + keeps strength/polarity + flags freeform, rejects a duplicate, and no-ops on blank; plus the edit-in-place and cancel flows through the component. Full suite green (899). Browser-verified on the settings authors: a typo'd `long_hai` → edit → `long hair` saved and shown corrected, the editor closing on save. Last in the batch: **D** full-width tabs. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(tags): edit a tag's text in place after adding (tag-UI cleanup 2)
All checks were successful
CI / build (pull_request) Successful in 20s
CI / test (pull_request) Successful in 40s
98ec7a6f38
The table let you change a tag's strength but not fix its text. Each row now
has an edit (pencil) action that swaps the name for an inline field — save
(check / Enter) commits, cancel (× / Escape) discards. A new PromptTagEditing.
Rename re-canonicalises the term, updates the display, flags it freeform (it's
now hand-edited, unverified), and keeps the strength and polarity; it no-ops
on a blank name or one that would duplicate another authored tag.

Fixes both consumers (character editor + settings authors, shared component).
Four tests (rename canonicalises/keeps strength/rejects duplicate/blank no-op;
edit-in-place and cancel through the UI). Full suite green (899); browser-
verified: a typo'd tag edited and saved shows the corrected text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Summary

Summary
Generated on: 07/11/2026 - 12:04:27
Coverage date: 07/11/2026 - 12:04:14 - 07/11/2026 - 12:04:25
Parser: MultiReport (4x Cobertura)
Assemblies: 7
Classes: 449
Files: 308
Line coverage: 95.3% (11620 of 12185)
Covered lines: 11620
Uncovered lines: 565
Coverable lines: 12185
Total lines: 23655
Branch coverage: 85.1% (1984 of 2330)
Covered branches: 1984
Total branches: 2330
Method coverage: Feature is only available for sponsors

Coverage

Kagura.BlazorAdapter - 88.9%
Name Line Branch
Kagura.BlazorAdapter 88.9% 81%
Kagura.BlazorAdapter.BlazorAdapterAssembly 100%
Kagura.BlazorAdapter.Connections.AddLinkModal 91.4% 81.5%
Kagura.BlazorAdapter.Connections.ConnectionsEffects 95.4% 80%
Kagura.BlazorAdapter.Connections.ConnectionsFailed 100%
Kagura.BlazorAdapter.Connections.ConnectionsLoaded 100%
Kagura.BlazorAdapter.Connections.ConnectionsPanel 100% 95%
Kagura.BlazorAdapter.Connections.ConnectionsReducers 100% 75%
Kagura.BlazorAdapter.Connections.ConnectionsState 90%
Kagura.BlazorAdapter.Connections.KindIcon 100%
Kagura.BlazorAdapter.Connections.LinkRequested 100%
Kagura.BlazorAdapter.Connections.LinkTargetsFound 100%
Kagura.BlazorAdapter.Connections.LoadConnections 100%
Kagura.BlazorAdapter.Connections.NodeKindIcons 100%
Kagura.BlazorAdapter.Connections.RemoveLinkRequested 100%
Kagura.BlazorAdapter.Connections.SearchLinkTargets 100%
Kagura.BlazorAdapter.Design 0% 0%
Kagura.BlazorAdapter.EditorComponentsDemo 0%
Kagura.BlazorAdapter.FileUploadDemo 0%
Kagura.BlazorAdapter.Generation.ActivityQueueBridge 87.5% 50%
Kagura.BlazorAdapter.Generation.CancelActivityRequested 100%
Kagura.BlazorAdapter.Generation.GenerationQueueEffects 100%
Kagura.BlazorAdapter.Generation.GenerationQueueLoaded 100%
Kagura.BlazorAdapter.Generation.GenerationQueuePage 100% 100%
Kagura.BlazorAdapter.Generation.GenerationQueueReducers 100%
Kagura.BlazorAdapter.Generation.GenerationQueueState 100%
Kagura.BlazorAdapter.Generation.GenerationStatusBar 100% 95.4%
Kagura.BlazorAdapter.Generation.RemoveActivityRequested 100%
Kagura.BlazorAdapter.History.DiffValue 100% 87.5%
Kagura.BlazorAdapter.History.DocumentHistoryEffects 90% 68.7%
Kagura.BlazorAdapter.History.DocumentHistoryFailed 100%
Kagura.BlazorAdapter.History.DocumentHistoryLoaded 100%
Kagura.BlazorAdapter.History.DocumentHistoryPanel 79.3% 75%
Kagura.BlazorAdapter.History.DocumentHistoryReducers 95% 83.3%
Kagura.BlazorAdapter.History.DocumentHistoryState 100%
Kagura.BlazorAdapter.History.LoadDocumentHistory 100%
Kagura.BlazorAdapter.History.RevertCompleted 100%
Kagura.BlazorAdapter.History.RevertRequested 100%
Kagura.BlazorAdapter.History.RevisionDiffLoaded 100%
Kagura.BlazorAdapter.History.SelectRevision 100%
Kagura.BlazorAdapter.KnowledgeBase.BaseSpriteFailed 100%
Kagura.BlazorAdapter.KnowledgeBase.BaseSpriteGenerated 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterBaseSprite 95.2% 88.8%
Kagura.BlazorAdapter.KnowledgeBase.CharacterCreated 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterDeleted 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorEffects 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorNotFound 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorPage 91.3% 81.8%
Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorReducers 100% 92.8%
Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorState 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterOperationFailed 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterSaved 100%
Kagura.BlazorAdapter.KnowledgeBase.CharactersEffects 94.1% 75%
Kagura.BlazorAdapter.KnowledgeBase.CharactersLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.CharactersPage 94.7% 90%
Kagura.BlazorAdapter.KnowledgeBase.CharacterSpriteEffects 83.3% 100%
Kagura.BlazorAdapter.KnowledgeBase.CharacterSpriteReducers 100% 75%
Kagura.BlazorAdapter.KnowledgeBase.CharacterSpriteState 100%
Kagura.BlazorAdapter.KnowledgeBase.CharactersReducers 100% 87.5%
Kagura.BlazorAdapter.KnowledgeBase.CharactersState 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.CreateCharacterFailed 0%
Kagura.BlazorAdapter.KnowledgeBase.CreateCharacterRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.CreateLocationFailed 0%
Kagura.BlazorAdapter.KnowledgeBase.CreateLocationRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.CreateLoreFailed 0%
Kagura.BlazorAdapter.KnowledgeBase.CreateLoreRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.CreateRelationshipRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.CreateTimelineEventRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.DeleteCharacterRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.DeleteLocationRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.DeleteLoreRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.DeleteRelationshipRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.DeleteTimelineEventRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.GenerateBaseSprite 100%
Kagura.BlazorAdapter.KnowledgeBase.LabeledEntryMapping 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadCharacter 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadCharacters 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadLocation 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadLocationHierarchy 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadLocations 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadLore 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadLoreEntry 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadRelationshipCast 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadRelationships 100%
Kagura.BlazorAdapter.KnowledgeBase.LoadTimeline 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationCreated 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationDeleted 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationEditorEffects 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationEditorLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationEditorNotFound 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationEditorPage 86.9% 71.7%
Kagura.BlazorAdapter.KnowledgeBase.LocationEditorReducers 86.9% 71.4%
Kagura.BlazorAdapter.KnowledgeBase.LocationEditorState 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyEffects 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyFailed 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyReducers 100% 75%
Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyState 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationOperationFailed 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationSaved 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationsEffects 94.1% 75%
Kagura.BlazorAdapter.KnowledgeBase.LocationsLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationsPage 94.7% 95%
Kagura.BlazorAdapter.KnowledgeBase.LocationsReducers 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.LocationsState 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreCategories 100% 50%
Kagura.BlazorAdapter.KnowledgeBase.LoreCreated 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreDeleted 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreEditorEffects 96.6% 87.5%
Kagura.BlazorAdapter.KnowledgeBase.LoreEditorLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreEditorNotFound 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreEditorPage 90.8% 75%
Kagura.BlazorAdapter.KnowledgeBase.LoreEditorReducers 86.9% 71.4%
Kagura.BlazorAdapter.KnowledgeBase.LoreEditorState 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreEffects 94.1% 75%
Kagura.BlazorAdapter.KnowledgeBase.LoreLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreOperationFailed 100%
Kagura.BlazorAdapter.KnowledgeBase.LorePage 80% 78.5%
Kagura.BlazorAdapter.KnowledgeBase.LoreReducers 90.9% 87.5%
Kagura.BlazorAdapter.KnowledgeBase.LoreSaved 100%
Kagura.BlazorAdapter.KnowledgeBase.LoreState 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.NewTimelineEvent 100%
Kagura.BlazorAdapter.KnowledgeBase.ParentCandidatesFound 100%
Kagura.BlazorAdapter.KnowledgeBase.ParentPickerModal 84.6% 89.2%
Kagura.BlazorAdapter.KnowledgeBase.PositioningPanel 100% 95.4%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipCard 75.6% 60.7%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipCastLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipList 97.3% 88.4%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipModal 94.1% 94.7%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipsEffects 92.8% 77.7%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipsFailed 0%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipsLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipsPage 92.8% 87.5%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipsReducers 94.7% 75%
Kagura.BlazorAdapter.KnowledgeBase.RelationshipsState 100% 100%
Kagura.BlazorAdapter.KnowledgeBase.ReorderTimelineEventRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.SaveCharacterRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.SaveLocationRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.SaveLoreRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.SearchParentCandidates 100%
Kagura.BlazorAdapter.KnowledgeBase.SetCharactersFilter 100%
Kagura.BlazorAdapter.KnowledgeBase.SetLocationsFilter 100%
Kagura.BlazorAdapter.KnowledgeBase.SetLoreCategory 100%
Kagura.BlazorAdapter.KnowledgeBase.SetLoreFilter 100%
Kagura.BlazorAdapter.KnowledgeBase.SetParentRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.SetRelationshipsFilter 100%
Kagura.BlazorAdapter.KnowledgeBase.SpriteCapabilityChecked 100%
Kagura.BlazorAdapter.KnowledgeBase.TimelineEffects 93.5% 70%
Kagura.BlazorAdapter.KnowledgeBase.TimelineEventCard 92.5% 80%
Kagura.BlazorAdapter.KnowledgeBase.TimelineEventEdit 100%
Kagura.BlazorAdapter.KnowledgeBase.TimelineFailed 0%
Kagura.BlazorAdapter.KnowledgeBase.TimelineLoaded 100%
Kagura.BlazorAdapter.KnowledgeBase.TimelinePage 95.8% 90%
Kagura.BlazorAdapter.KnowledgeBase.TimelineReducers 91.6% 75%
Kagura.BlazorAdapter.KnowledgeBase.TimelineSection 96.6% 91.6%
Kagura.BlazorAdapter.KnowledgeBase.TimelineState 100%
Kagura.BlazorAdapter.KnowledgeBase.UpdateRelationshipRequested 100%
Kagura.BlazorAdapter.KnowledgeBase.UpdateTimelineEventRequested 100%
Kagura.BlazorAdapter.Notifications.DomainChangedBridge 88.8% 58.3%
Kagura.BlazorAdapter.Notifications.DomainChangesReceived 100% 100%
Kagura.BlazorAdapter.OverlayDemo 0% 0%
Kagura.BlazorAdapter.Projects.ClearStyleReferenceRequested 100%
Kagura.BlazorAdapter.Projects.CreateProjectRequested 100%
Kagura.BlazorAdapter.Projects.DeleteProjectRequested 100%
Kagura.BlazorAdapter.Projects.LoadWorkspace 100%
Kagura.BlazorAdapter.Projects.ProjectCreated 100%
Kagura.BlazorAdapter.Projects.ProjectCreateFailed 100%
Kagura.BlazorAdapter.Projects.ProjectDeleted 100%
Kagura.BlazorAdapter.Projects.ProjectSaved 100%
Kagura.BlazorAdapter.Projects.ProjectSaveFailed 100%
Kagura.BlazorAdapter.Projects.ProjectsEffects 100% 100%
Kagura.BlazorAdapter.Projects.ProjectsLoaded 100%
Kagura.BlazorAdapter.Projects.ProjectsPage 94.7% 100%
Kagura.BlazorAdapter.Projects.ProjectsReducers 100% 100%
Kagura.BlazorAdapter.Projects.ProjectsState 100% 100%
Kagura.BlazorAdapter.Projects.ProjectWorkspacePage 96.2% 96.1%
Kagura.BlazorAdapter.Projects.SaveProjectRequested 100%
Kagura.BlazorAdapter.Projects.SetProjectsFilter 100%
Kagura.BlazorAdapter.Projects.SetStyleReferenceRequested 100%
Kagura.BlazorAdapter.Projects.StyleReferenceChanged 100%
Kagura.BlazorAdapter.Projects.StyleReferenceFailed 100%
Kagura.BlazorAdapter.Projects.WorkspaceEffects 97.7% 90.9%
Kagura.BlazorAdapter.Projects.WorkspaceLoaded 100%
Kagura.BlazorAdapter.Projects.WorkspaceReducers 100%
Kagura.BlazorAdapter.Projects.WorkspaceSectionPage 100% 66.6%
Kagura.BlazorAdapter.Projects.WorkspaceShell 100% 93.7%
Kagura.BlazorAdapter.Projects.WorkspaceState 100%
Kagura.BlazorAdapter.QuicklinkDemo 0%
Kagura.BlazorAdapter.SelectDemo 0%
Kagura.BlazorAdapter.Settings.NovelAiSettingsEffects 68.8% 50%
Kagura.BlazorAdapter.Settings.NovelAiSettingsFailed 100%
Kagura.BlazorAdapter.Settings.NovelAiSettingsLoaded 100%
Kagura.BlazorAdapter.Settings.NovelAiSettingsReducers 90.9% 25%
Kagura.BlazorAdapter.Settings.NovelAiSettingsSection 94.1% 91.6%
Kagura.BlazorAdapter.Settings.NovelAiSettingsState 100%
Kagura.BlazorAdapter.Settings.NovelAiTokenChecked 100%
Kagura.BlazorAdapter.Settings.SaveDefaultTagsRequested 100%
Kagura.BlazorAdapter.Settings.SaveNovelAiModelRequested 100%
Kagura.BlazorAdapter.Settings.SaveNovelAiTokenRequested 100%
Kagura.BlazorAdapter.Settings.TokenVerdict 100%
Kagura.BlazorAdapter.SliderDemo 0% 0%
Kagura.BlazorAdapter.Tags.ForgetTagAuthoringInstance 100%
Kagura.BlazorAdapter.Tags.PromptTagEditing 100% 71.8%
Kagura.BlazorAdapter.Tags.RequestSuggestions 100%
Kagura.BlazorAdapter.Tags.SearchTags 100%
Kagura.BlazorAdapter.Tags.SuggestionsReceived 100%
Kagura.BlazorAdapter.Tags.TagAuthoring 96.8% 70%
Kagura.BlazorAdapter.Tags.TagAuthoringEffects 100% 100%
Kagura.BlazorAdapter.Tags.TagAuthoringInstance 100%
Kagura.BlazorAdapter.Tags.TagAuthoringPage 0%
Kagura.BlazorAdapter.Tags.TagAuthoringReducers 100% 100%
Kagura.BlazorAdapter.Tags.TagAuthoringState 100% 100%
Kagura.BlazorAdapter.Tags.TagDatabaseChecked 100%
Kagura.BlazorAdapter.Tags.TagsSearched 100%
Kagura.BlazorAdapter.ToggleGroupDemo 0% 0%
Kagura.Domain - 95.9%
Name Line Branch
Kagura.Domain 95.9% 87.5%
Kagura.Domain.Graph.Entry 100% 100%
Kagura.Domain.Graph.Link 100% 100%
Kagura.Domain.Graph.LinkRole 100% 100%
Kagura.Domain.Graph.LinkRoles 100%
Kagura.Domain.Graph.StructuralRef 100% 100%
Kagura.Domain.Graph.StructuralRefs 100% 50%
Kagura.Domain.Journal.ChangeLogEntry 100%
Kagura.Domain.KnowledgeBase.Character 91.6% 100%
Kagura.Domain.KnowledgeBase.CharacterProfile 100%
Kagura.Domain.KnowledgeBase.LabeledEntry 100%
Kagura.Domain.KnowledgeBase.Location 100% 100%
Kagura.Domain.KnowledgeBase.LoreEntry 100%
Kagura.Domain.KnowledgeBase.LoreProfile 100%
Kagura.Domain.KnowledgeBase.PromptTag 42.8% 100%
Kagura.Domain.KnowledgeBase.Relationship 91.8% 100%
Kagura.Domain.KnowledgeBase.RelationshipProfile 100%
Kagura.Domain.KnowledgeBase.RelationshipSecret 100% 75%
Kagura.Domain.KnowledgeBase.TimelineEvent 100% 100%
Kagura.Domain.Projects.Project 100% 100%
Kagura.Domain.Projects.Slug 100% 100%
Kagura.Domain.Settings.AppSetting 100%
System.Text.RegularExpressions.Generated 90.2% 72.2%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>FE06CC341D340484
D04ADFED3A21D401C2764A1D17367E35BEB556CBB3B4B0B74__NonSlugChars_0
89.4% 75%
Kagura.Infrastructure - 97.6%
Name Line Branch
Kagura.Infrastructure 97.6% 88.6%
Kagura.Infrastructure.DependencyInjection 100% 100%
Kagura.Infrastructure.Diagnostics.SqliteDatabaseSnapshotStore 100% 100%
Kagura.Infrastructure.Generation.ActivityQueue 99.3% 97.5%
Kagura.Infrastructure.Generation.ActivityQueueWorker 100%
Kagura.Infrastructure.Graph.EfGraphStore 94.5% 75%
Kagura.Infrastructure.Journal.EfChangeJournal 100%
Kagura.Infrastructure.Journal.EfDocumentReverter 95% 91.6%
Kagura.Infrastructure.Journal.EfUndoStore 98.4% 90.9%
Kagura.Infrastructure.Journal.OperationContext 100% 100%
Kagura.Infrastructure.Journal.SnapshotApplier 95% 90%
Kagura.Infrastructure.KnowledgeBase.CharacterImageGenerator 97.8% 81.8%
Kagura.Infrastructure.KnowledgeBase.EfCharacterStore 96.6% 83.3%
Kagura.Infrastructure.KnowledgeBase.EfLocationStore 98.2% 92.8%
Kagura.Infrastructure.KnowledgeBase.EfLoreStore 100% 100%
Kagura.Infrastructure.KnowledgeBase.EfRelationshipStore 93% 66.6%
Kagura.Infrastructure.KnowledgeBase.EfTimelineStore 93.9% 75%
Kagura.Infrastructure.Notifications.InProcessDomainChangedBus 100% 100%
Kagura.Infrastructure.Persistence.Configurations.AppSettingConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.ChangeLogEntryConfiguratio
n
100%
Kagura.Infrastructure.Persistence.Configurations.CharacterConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.EntryConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.LinkConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.LocationConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.LoreEntryConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.ProjectConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.RelationshipConfiguration 100%
Kagura.Infrastructure.Persistence.Configurations.TimelineEventConfiguration 100%
Kagura.Infrastructure.Persistence.Converters.UtcTicksConverter 100%
Kagura.Infrastructure.Persistence.KaguraDbContext 86% 88%
Kagura.Infrastructure.Persistence.KaguraDbContextFactory 0%
Kagura.Infrastructure.Persistence.Migrations.AddAppSettings 99.3%
Kagura.Infrastructure.Persistence.Migrations.AddChangeLogUndoFlag 96.8%
Kagura.Infrastructure.Persistence.Migrations.AddCharacterBaseAppearance 98.4%
Kagura.Infrastructure.Persistence.Migrations.AddCharacterBaseImage 99%
Kagura.Infrastructure.Persistence.Migrations.AddCharacterProfile 92.9%
Kagura.Infrastructure.Persistence.Migrations.AddCharacters 98.7%
Kagura.Infrastructure.Persistence.Migrations.AddGraphEntryAndLink 97.7%
Kagura.Infrastructure.Persistence.Migrations.AddLocationHierarchy 96.6%
Kagura.Infrastructure.Persistence.Migrations.AddLocations 98.9%
Kagura.Infrastructure.Persistence.Migrations.AddLoreEntries 99%
Kagura.Infrastructure.Persistence.Migrations.AddLoreEntryTables 97.8%
Kagura.Infrastructure.Persistence.Migrations.AddProjectDescription 98.1%
Kagura.Infrastructure.Persistence.Migrations.AddProjectStyleReference 99%
Kagura.Infrastructure.Persistence.Migrations.AddRelationships 99.2%
Kagura.Infrastructure.Persistence.Migrations.AddSoftDeleteAndChangeLog 90.3%
Kagura.Infrastructure.Persistence.Migrations.AddTimelineEvents 99.2%
Kagura.Infrastructure.Persistence.Migrations.InitialCreate 94.4%
Kagura.Infrastructure.Persistence.Migrations.KaguraDbContextModelSnapshot 100%
Kagura.Infrastructure.Projects.EfProjectStore 97.2% 87.5%
Kagura.Infrastructure.Projects.FileSystemProjectAssetStore 94.8% 77.7%
Kagura.Infrastructure.Settings.EfAppSettingsStore 100% 100%
Kagura.Infrastructure.Settings.NovelAiGateway 76.4% 50%
Kagura.Infrastructure.Settings.SpriteRequestMapper 100% 100%
Kagura.Infrastructure.Tags.SqliteTagSearch 97.8% 87.9%
Kagura.Infrastructure.Tags.TagDatabaseOptions 100%
Kagura.Kernel - 90%
Name Line Branch
Kagura.Kernel 90% 75%
Kagura.Kernel.Err`1 100%
Kagura.Kernel.Ok`1 100%
Kagura.Kernel.Result`1 87.5% 75%
Kagura.Server - 97.3%
Name Line Branch
Kagura.Server 97.3% 80.9%
Kagura.Server.Components.App 100%
Kagura.Server.Components.Layout.MainLayout 100%
Kagura.Server.Components.Pages.Error 100% 50%
Kagura.Server.Components.Pages.Gate 100% 100%
Kagura.Server.Security.AccessGate 100% 83.3%
Kagura.Server.Security.AccessSecret 100% 100%
Program 95.2% 85.7%
Kagura.UI - 96.7%
Name Line Branch
Kagura.UI 96.7% 92.5%
Kagura.UI.Badge 100% 100%
Kagura.UI.Breadcrumb 100%
Kagura.UI.BreadcrumbItem 100% 100%
Kagura.UI.Button 100% 100%
Kagura.UI.Card 100% 100%
Kagura.UI.ConfirmDialog 100%
Kagura.UI.CssClassExtensions 100%
Kagura.UI.DebouncedSearchField 100% 88.8%
Kagura.UI.DocumentTitle 76.9% 100%
Kagura.UI.EmptyState 100% 100%
Kagura.UI.Field 100% 100%
Kagura.UI.FileUpload 100% 91.6%
Kagura.UI.Icon 100% 100%
Kagura.UI.IconCatalog 100%
Kagura.UI.InlineAlert 91.6% 87.5%
Kagura.UI.InputFieldBase 94.2% 87.5%
Kagura.UI.LabeledEntriesTable 96.7% 66.6%
Kagura.UI.LabeledEntry 100%
Kagura.UI.MaskedSecretField 95.2% 83.3%
Kagura.UI.Menu 90% 75%
Kagura.UI.MenuItem 100% 100%
Kagura.UI.Modal 87.1% 90%
Kagura.UI.NavGroup 100% 100%
Kagura.UI.NavItem 100% 100%
Kagura.UI.NavList 100%
Kagura.UI.PreviewImage 100% 100%
Kagura.UI.QuicklinkNav 85.2% 95.8%
Kagura.UI.QuicklinkSection 100%
Kagura.UI.RelativeTime 100% 93.7%
Kagura.UI.SaveIndicator 100% 100%
Kagura.UI.Select`1 96.1% 95.8%
Kagura.UI.SelectOption`1 100%
Kagura.UI.Separator 100%
Kagura.UI.Slider 100% 91.6%
Kagura.UI.StatusDot 100%
Kagura.UI.Tab 100%
Kagura.UI.Table`1 100% 92.3%
Kagura.UI.TableColumn`1 100%
Kagura.UI.Tabs 94.2% 88.8%
Kagura.UI.TextArea 100% 100%
Kagura.UI.TextField 100%
Kagura.UI.ThemeSwitcher 100% 100%
Kagura.UI.ToggleButtonGroup`1 100% 100%
Kagura.UI.ToggleOption`1 100%
Kagura.UseCases - 96.1%
Name Line Branch
Kagura.UseCases 96.1% 87.2%
Kagura.UseCases.DependencyInjection 100%
Kagura.UseCases.Diagnostics.DatabaseSnapshot 100%
Kagura.UseCases.Diagnostics.DownloadDatabase 100%
Kagura.UseCases.Generation.Activity`1 100% 50%
Kagura.UseCases.Generation.ActivityChanged 100%
Kagura.UseCases.Generation.ActivityOutcome 100%
Kagura.UseCases.Generation.ActivityProgress 100%
Kagura.UseCases.Generation.ActivityView 83.3%
Kagura.UseCases.Generation.GeneratedImage 100%
Kagura.UseCases.Generation.JobResult`1 100%
Kagura.UseCases.Generation.SpriteGenerationSpec 100%
Kagura.UseCases.Graph.EdgeGroup 100%
Kagura.UseCases.Graph.FindLinkTargets 100%
Kagura.UseCases.Graph.GetNodeGraph 97.5% 78.5%
Kagura.UseCases.Graph.GraphEdgeView 87.5%
Kagura.UseCases.Graph.LinkNodes 100% 100%
Kagura.UseCases.Graph.NodeGraphView 100%
Kagura.UseCases.Graph.NodeSummary 100%
Kagura.UseCases.Graph.RemoveLink 100% 100%
Kagura.UseCases.Graph.RestoreLink 100% 100%
Kagura.UseCases.Graph.StructuralEdge 100%
Kagura.UseCases.Journal.ChangeRecordView 57.1%
Kagura.UseCases.Journal.DocumentRevision 100%
Kagura.UseCases.Journal.FieldChange 100%
Kagura.UseCases.Journal.GetDocumentHistory 100%
Kagura.UseCases.Journal.GetEntityHistory 100%
Kagura.UseCases.Journal.GetRevisionDiff 100% 50%
Kagura.UseCases.Journal.GetUndoStatus 100%
Kagura.UseCases.Journal.Redo 100% 100%
Kagura.UseCases.Journal.RevertDocument 100% 100%
Kagura.UseCases.Journal.RevertOutcome 100%
Kagura.UseCases.Journal.RevisionCoalescer 100% 100%
Kagura.UseCases.Journal.RevisionDiff 80% 50%
Kagura.UseCases.Journal.RevisionDiffBuilder 98.4% 86.5%
Kagura.UseCases.Journal.RowChange 100%
Kagura.UseCases.Journal.Undo 100% 100%
Kagura.UseCases.Journal.UndoOutcome 100%
Kagura.UseCases.Journal.UndoStatus 100%
Kagura.UseCases.KnowledgeBase.CharacterDto 97.2%
Kagura.UseCases.KnowledgeBase.CharacterSpriteContent 100%
Kagura.UseCases.KnowledgeBase.CreateCharacter 100%
Kagura.UseCases.KnowledgeBase.CreateLocation 100%
Kagura.UseCases.KnowledgeBase.CreateLore 100%
Kagura.UseCases.KnowledgeBase.CreateRelationship 93.3% 80%
Kagura.UseCases.KnowledgeBase.CreateTimelineEvent 100%
Kagura.UseCases.KnowledgeBase.DeleteCharacter 100% 100%
Kagura.UseCases.KnowledgeBase.DeleteLocation 100% 100%
Kagura.UseCases.KnowledgeBase.DeleteLore 100% 100%
Kagura.UseCases.KnowledgeBase.DeleteRelationship 100% 50%
Kagura.UseCases.KnowledgeBase.DeleteTimelineEvent 100% 50%
Kagura.UseCases.KnowledgeBase.FindParentCandidates 100% 50%
Kagura.UseCases.KnowledgeBase.GetCharacter 100% 100%
Kagura.UseCases.KnowledgeBase.GetLocation 100% 100%
Kagura.UseCases.KnowledgeBase.GetLocationHierarchy 100%
Kagura.UseCases.KnowledgeBase.GetLore 100% 100%
Kagura.UseCases.KnowledgeBase.GetRelationshipsForCharacter 100%
Kagura.UseCases.KnowledgeBase.ListCharacters 100%
Kagura.UseCases.KnowledgeBase.ListLocations 100%
Kagura.UseCases.KnowledgeBase.ListLore 100%
Kagura.UseCases.KnowledgeBase.ListRelationships 100%
Kagura.UseCases.KnowledgeBase.ListTimeline 100%
Kagura.UseCases.KnowledgeBase.LocationDto 81.8%
Kagura.UseCases.KnowledgeBase.LocationHierarchyView 100%
Kagura.UseCases.KnowledgeBase.LoreDto 85.7%
Kagura.UseCases.KnowledgeBase.OpenCharacterBaseSprite 88.8% 70%
Kagura.UseCases.KnowledgeBase.RelationshipDto 88%
Kagura.UseCases.KnowledgeBase.RelationshipEndpoint 100%
Kagura.UseCases.KnowledgeBase.ReorderTimelineEvent 94.1% 87.5%
Kagura.UseCases.KnowledgeBase.SetLocationParent 90.4% 81.2%
Kagura.UseCases.KnowledgeBase.TimelineEventDto 88.8%
Kagura.UseCases.KnowledgeBase.UpdateCharacter 100% 100%
Kagura.UseCases.KnowledgeBase.UpdateLocation 100% 100%
Kagura.UseCases.KnowledgeBase.UpdateLore 100% 100%
Kagura.UseCases.KnowledgeBase.UpdateRelationship 100% 50%
Kagura.UseCases.KnowledgeBase.UpdateTimelineEvent 100% 50%
Kagura.UseCases.Notifications.DomainChanged 100%
Kagura.UseCases.Projects.ClearProjectStyleReference 100% 100%
Kagura.UseCases.Projects.CreateProject 100% 100%
Kagura.UseCases.Projects.DeleteProject 100% 100%
Kagura.UseCases.Projects.GetProject 100% 100%
Kagura.UseCases.Projects.ListProjects 100%
Kagura.UseCases.Projects.OpenProjectStyleReference 100% 83.3%
Kagura.UseCases.Projects.ProjectDto 100%
Kagura.UseCases.Projects.SetProjectStyleReference 100% 100%
Kagura.UseCases.Projects.StyleReferenceContent 100%
Kagura.UseCases.Projects.UpdateProject 100% 100%
Kagura.UseCases.Settings.CheckNovelAiToken 100% 100%
Kagura.UseCases.Settings.GenerationDefaultTagsJson 100% 100%
Kagura.UseCases.Settings.GetNovelAiSettings 100%
Kagura.UseCases.Settings.NovelAiModelOption 100%
Kagura.UseCases.Settings.NovelAiSettingsView 100%
Kagura.UseCases.Settings.SaveGenerationDefaultTags 100%
Kagura.UseCases.Settings.SaveNovelAiModel 100%
Kagura.UseCases.Settings.SaveNovelAiToken 100% 100%
Kagura.UseCases.Settings.SettingKeys 83.3% 75%
Kagura.UseCases.Tags.SimilarTag 100%
Kagura.UseCases.Tags.TagCategoryCount 100%
Kagura.UseCases.Tags.TagDetails 54.5%
Kagura.UseCases.Tags.TagResolution 100%
Kagura.UseCases.Tags.TagSearchFilter 100%
Kagura.UseCases.Tags.TagSuggestion 71.4%
Kagura.UseCases.Tags.TagSummary 83.3%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 07/11/2026 - 12:04:27 | | Coverage date: | 07/11/2026 - 12:04:14 - 07/11/2026 - 12:04:25 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 7 | | Classes: | 449 | | Files: | 308 | | **Line coverage:** | 95.3% (11620 of 12185) | | Covered lines: | 11620 | | Uncovered lines: | 565 | | Coverable lines: | 12185 | | Total lines: | 23655 | | **Branch coverage:** | 85.1% (1984 of 2330) | | Covered branches: | 1984 | | Total branches: | 2330 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Kagura.BlazorAdapter - 88.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagura.BlazorAdapter**|**88.9%**|**81%**| |Kagura.BlazorAdapter.BlazorAdapterAssembly|100%|| |Kagura.BlazorAdapter.Connections.AddLinkModal|91.4%|81.5%| |Kagura.BlazorAdapter.Connections.ConnectionsEffects|95.4%|80%| |Kagura.BlazorAdapter.Connections.ConnectionsFailed|100%|| |Kagura.BlazorAdapter.Connections.ConnectionsLoaded|100%|| |Kagura.BlazorAdapter.Connections.ConnectionsPanel|100%|95%| |Kagura.BlazorAdapter.Connections.ConnectionsReducers|100%|75%| |Kagura.BlazorAdapter.Connections.ConnectionsState|90%|| |Kagura.BlazorAdapter.Connections.KindIcon|100%|| |Kagura.BlazorAdapter.Connections.LinkRequested|100%|| |Kagura.BlazorAdapter.Connections.LinkTargetsFound|100%|| |Kagura.BlazorAdapter.Connections.LoadConnections|100%|| |Kagura.BlazorAdapter.Connections.NodeKindIcons|100%|| |Kagura.BlazorAdapter.Connections.RemoveLinkRequested|100%|| |Kagura.BlazorAdapter.Connections.SearchLinkTargets|100%|| |Kagura.BlazorAdapter.Design|0%|0%| |Kagura.BlazorAdapter.EditorComponentsDemo|0%|| |Kagura.BlazorAdapter.FileUploadDemo|0%|| |Kagura.BlazorAdapter.Generation.ActivityQueueBridge|87.5%|50%| |Kagura.BlazorAdapter.Generation.CancelActivityRequested|100%|| |Kagura.BlazorAdapter.Generation.GenerationQueueEffects|100%|| |Kagura.BlazorAdapter.Generation.GenerationQueueLoaded|100%|| |Kagura.BlazorAdapter.Generation.GenerationQueuePage|100%|100%| |Kagura.BlazorAdapter.Generation.GenerationQueueReducers|100%|| |Kagura.BlazorAdapter.Generation.GenerationQueueState|100%|| |Kagura.BlazorAdapter.Generation.GenerationStatusBar|100%|95.4%| |Kagura.BlazorAdapter.Generation.RemoveActivityRequested|100%|| |Kagura.BlazorAdapter.History.DiffValue|100%|87.5%| |Kagura.BlazorAdapter.History.DocumentHistoryEffects|90%|68.7%| |Kagura.BlazorAdapter.History.DocumentHistoryFailed|100%|| |Kagura.BlazorAdapter.History.DocumentHistoryLoaded|100%|| |Kagura.BlazorAdapter.History.DocumentHistoryPanel|79.3%|75%| |Kagura.BlazorAdapter.History.DocumentHistoryReducers|95%|83.3%| |Kagura.BlazorAdapter.History.DocumentHistoryState|100%|| |Kagura.BlazorAdapter.History.LoadDocumentHistory|100%|| |Kagura.BlazorAdapter.History.RevertCompleted|100%|| |Kagura.BlazorAdapter.History.RevertRequested|100%|| |Kagura.BlazorAdapter.History.RevisionDiffLoaded|100%|| |Kagura.BlazorAdapter.History.SelectRevision|100%|| |Kagura.BlazorAdapter.KnowledgeBase.BaseSpriteFailed|100%|| |Kagura.BlazorAdapter.KnowledgeBase.BaseSpriteGenerated|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharacterBaseSprite|95.2%|88.8%| |Kagura.BlazorAdapter.KnowledgeBase.CharacterCreated|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharacterDeleted|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorEffects|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorNotFound|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorPage|91.3%|81.8%| |Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorReducers|100%|92.8%| |Kagura.BlazorAdapter.KnowledgeBase.CharacterEditorState|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharacterOperationFailed|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharacterSaved|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharactersEffects|94.1%|75%| |Kagura.BlazorAdapter.KnowledgeBase.CharactersLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharactersPage|94.7%|90%| |Kagura.BlazorAdapter.KnowledgeBase.CharacterSpriteEffects|83.3%|100%| |Kagura.BlazorAdapter.KnowledgeBase.CharacterSpriteReducers|100%|75%| |Kagura.BlazorAdapter.KnowledgeBase.CharacterSpriteState|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CharactersReducers|100%|87.5%| |Kagura.BlazorAdapter.KnowledgeBase.CharactersState|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.CreateCharacterFailed|0%|| |Kagura.BlazorAdapter.KnowledgeBase.CreateCharacterRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CreateLocationFailed|0%|| |Kagura.BlazorAdapter.KnowledgeBase.CreateLocationRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CreateLoreFailed|0%|| |Kagura.BlazorAdapter.KnowledgeBase.CreateLoreRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CreateRelationshipRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.CreateTimelineEventRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.DeleteCharacterRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.DeleteLocationRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.DeleteLoreRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.DeleteRelationshipRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.DeleteTimelineEventRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.GenerateBaseSprite|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LabeledEntryMapping|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadCharacter|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadCharacters|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadLocation|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadLocationHierarchy|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadLocations|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadLore|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadLoreEntry|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadRelationshipCast|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadRelationships|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoadTimeline|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationCreated|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationDeleted|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationEditorEffects|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.LocationEditorLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationEditorNotFound|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationEditorPage|86.9%|71.7%| |Kagura.BlazorAdapter.KnowledgeBase.LocationEditorReducers|86.9%|71.4%| |Kagura.BlazorAdapter.KnowledgeBase.LocationEditorState|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyEffects|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyFailed|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyReducers|100%|75%| |Kagura.BlazorAdapter.KnowledgeBase.LocationHierarchyState|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.LocationOperationFailed|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationSaved|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationsEffects|94.1%|75%| |Kagura.BlazorAdapter.KnowledgeBase.LocationsLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LocationsPage|94.7%|95%| |Kagura.BlazorAdapter.KnowledgeBase.LocationsReducers|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.LocationsState|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.LoreCategories|100%|50%| |Kagura.BlazorAdapter.KnowledgeBase.LoreCreated|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoreDeleted|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoreEditorEffects|96.6%|87.5%| |Kagura.BlazorAdapter.KnowledgeBase.LoreEditorLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoreEditorNotFound|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoreEditorPage|90.8%|75%| |Kagura.BlazorAdapter.KnowledgeBase.LoreEditorReducers|86.9%|71.4%| |Kagura.BlazorAdapter.KnowledgeBase.LoreEditorState|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoreEffects|94.1%|75%| |Kagura.BlazorAdapter.KnowledgeBase.LoreLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoreOperationFailed|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LorePage|80%|78.5%| |Kagura.BlazorAdapter.KnowledgeBase.LoreReducers|90.9%|87.5%| |Kagura.BlazorAdapter.KnowledgeBase.LoreSaved|100%|| |Kagura.BlazorAdapter.KnowledgeBase.LoreState|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.NewTimelineEvent|100%|| |Kagura.BlazorAdapter.KnowledgeBase.ParentCandidatesFound|100%|| |Kagura.BlazorAdapter.KnowledgeBase.ParentPickerModal|84.6%|89.2%| |Kagura.BlazorAdapter.KnowledgeBase.PositioningPanel|100%|95.4%| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipCard|75.6%|60.7%| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipCastLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipList|97.3%|88.4%| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipModal|94.1%|94.7%| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipsEffects|92.8%|77.7%| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipsFailed|0%|| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipsLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipsPage|92.8%|87.5%| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipsReducers|94.7%|75%| |Kagura.BlazorAdapter.KnowledgeBase.RelationshipsState|100%|100%| |Kagura.BlazorAdapter.KnowledgeBase.ReorderTimelineEventRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SaveCharacterRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SaveLocationRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SaveLoreRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SearchParentCandidates|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SetCharactersFilter|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SetLocationsFilter|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SetLoreCategory|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SetLoreFilter|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SetParentRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SetRelationshipsFilter|100%|| |Kagura.BlazorAdapter.KnowledgeBase.SpriteCapabilityChecked|100%|| |Kagura.BlazorAdapter.KnowledgeBase.TimelineEffects|93.5%|70%| |Kagura.BlazorAdapter.KnowledgeBase.TimelineEventCard|92.5%|80%| |Kagura.BlazorAdapter.KnowledgeBase.TimelineEventEdit|100%|| |Kagura.BlazorAdapter.KnowledgeBase.TimelineFailed|0%|| |Kagura.BlazorAdapter.KnowledgeBase.TimelineLoaded|100%|| |Kagura.BlazorAdapter.KnowledgeBase.TimelinePage|95.8%|90%| |Kagura.BlazorAdapter.KnowledgeBase.TimelineReducers|91.6%|75%| |Kagura.BlazorAdapter.KnowledgeBase.TimelineSection|96.6%|91.6%| |Kagura.BlazorAdapter.KnowledgeBase.TimelineState|100%|| |Kagura.BlazorAdapter.KnowledgeBase.UpdateRelationshipRequested|100%|| |Kagura.BlazorAdapter.KnowledgeBase.UpdateTimelineEventRequested|100%|| |Kagura.BlazorAdapter.Notifications.DomainChangedBridge|88.8%|58.3%| |Kagura.BlazorAdapter.Notifications.DomainChangesReceived|100%|100%| |Kagura.BlazorAdapter.OverlayDemo|0%|0%| |Kagura.BlazorAdapter.Projects.ClearStyleReferenceRequested|100%|| |Kagura.BlazorAdapter.Projects.CreateProjectRequested|100%|| |Kagura.BlazorAdapter.Projects.DeleteProjectRequested|100%|| |Kagura.BlazorAdapter.Projects.LoadWorkspace|100%|| |Kagura.BlazorAdapter.Projects.ProjectCreated|100%|| |Kagura.BlazorAdapter.Projects.ProjectCreateFailed|100%|| |Kagura.BlazorAdapter.Projects.ProjectDeleted|100%|| |Kagura.BlazorAdapter.Projects.ProjectSaved|100%|| |Kagura.BlazorAdapter.Projects.ProjectSaveFailed|100%|| |Kagura.BlazorAdapter.Projects.ProjectsEffects|100%|100%| |Kagura.BlazorAdapter.Projects.ProjectsLoaded|100%|| |Kagura.BlazorAdapter.Projects.ProjectsPage|94.7%|100%| |Kagura.BlazorAdapter.Projects.ProjectsReducers|100%|100%| |Kagura.BlazorAdapter.Projects.ProjectsState|100%|100%| |Kagura.BlazorAdapter.Projects.ProjectWorkspacePage|96.2%|96.1%| |Kagura.BlazorAdapter.Projects.SaveProjectRequested|100%|| |Kagura.BlazorAdapter.Projects.SetProjectsFilter|100%|| |Kagura.BlazorAdapter.Projects.SetStyleReferenceRequested|100%|| |Kagura.BlazorAdapter.Projects.StyleReferenceChanged|100%|| |Kagura.BlazorAdapter.Projects.StyleReferenceFailed|100%|| |Kagura.BlazorAdapter.Projects.WorkspaceEffects|97.7%|90.9%| |Kagura.BlazorAdapter.Projects.WorkspaceLoaded|100%|| |Kagura.BlazorAdapter.Projects.WorkspaceReducers|100%|| |Kagura.BlazorAdapter.Projects.WorkspaceSectionPage|100%|66.6%| |Kagura.BlazorAdapter.Projects.WorkspaceShell|100%|93.7%| |Kagura.BlazorAdapter.Projects.WorkspaceState|100%|| |Kagura.BlazorAdapter.QuicklinkDemo|0%|| |Kagura.BlazorAdapter.SelectDemo|0%|| |Kagura.BlazorAdapter.Settings.NovelAiSettingsEffects|68.8%|50%| |Kagura.BlazorAdapter.Settings.NovelAiSettingsFailed|100%|| |Kagura.BlazorAdapter.Settings.NovelAiSettingsLoaded|100%|| |Kagura.BlazorAdapter.Settings.NovelAiSettingsReducers|90.9%|25%| |Kagura.BlazorAdapter.Settings.NovelAiSettingsSection|94.1%|91.6%| |Kagura.BlazorAdapter.Settings.NovelAiSettingsState|100%|| |Kagura.BlazorAdapter.Settings.NovelAiTokenChecked|100%|| |Kagura.BlazorAdapter.Settings.SaveDefaultTagsRequested|100%|| |Kagura.BlazorAdapter.Settings.SaveNovelAiModelRequested|100%|| |Kagura.BlazorAdapter.Settings.SaveNovelAiTokenRequested|100%|| |Kagura.BlazorAdapter.Settings.TokenVerdict|100%|| |Kagura.BlazorAdapter.SliderDemo|0%|0%| |Kagura.BlazorAdapter.Tags.ForgetTagAuthoringInstance|100%|| |Kagura.BlazorAdapter.Tags.PromptTagEditing|100%|71.8%| |Kagura.BlazorAdapter.Tags.RequestSuggestions|100%|| |Kagura.BlazorAdapter.Tags.SearchTags|100%|| |Kagura.BlazorAdapter.Tags.SuggestionsReceived|100%|| |Kagura.BlazorAdapter.Tags.TagAuthoring|96.8%|70%| |Kagura.BlazorAdapter.Tags.TagAuthoringEffects|100%|100%| |Kagura.BlazorAdapter.Tags.TagAuthoringInstance|100%|| |Kagura.BlazorAdapter.Tags.TagAuthoringPage|0%|| |Kagura.BlazorAdapter.Tags.TagAuthoringReducers|100%|100%| |Kagura.BlazorAdapter.Tags.TagAuthoringState|100%|100%| |Kagura.BlazorAdapter.Tags.TagDatabaseChecked|100%|| |Kagura.BlazorAdapter.Tags.TagsSearched|100%|| |Kagura.BlazorAdapter.ToggleGroupDemo|0%|0%| </details> <details><summary>Kagura.Domain - 95.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagura.Domain**|**95.9%**|**87.5%**| |Kagura.Domain.Graph.Entry|100%|100%| |Kagura.Domain.Graph.Link|100%|100%| |Kagura.Domain.Graph.LinkRole|100%|100%| |Kagura.Domain.Graph.LinkRoles|100%|| |Kagura.Domain.Graph.StructuralRef|100%|100%| |Kagura.Domain.Graph.StructuralRefs|100%|50%| |Kagura.Domain.Journal.ChangeLogEntry|100%|| |Kagura.Domain.KnowledgeBase.Character|91.6%|100%| |Kagura.Domain.KnowledgeBase.CharacterProfile|100%|| |Kagura.Domain.KnowledgeBase.LabeledEntry|100%|| |Kagura.Domain.KnowledgeBase.Location|100%|100%| |Kagura.Domain.KnowledgeBase.LoreEntry|100%|| |Kagura.Domain.KnowledgeBase.LoreProfile|100%|| |Kagura.Domain.KnowledgeBase.PromptTag|42.8%|100%| |Kagura.Domain.KnowledgeBase.Relationship|91.8%|100%| |Kagura.Domain.KnowledgeBase.RelationshipProfile|100%|| |Kagura.Domain.KnowledgeBase.RelationshipSecret|100%|75%| |Kagura.Domain.KnowledgeBase.TimelineEvent|100%|100%| |Kagura.Domain.Projects.Project|100%|100%| |Kagura.Domain.Projects.Slug|100%|100%| |Kagura.Domain.Settings.AppSetting|100%|| |System.Text.RegularExpressions.Generated|90.2%|72.2%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>FE06CC341D340484<br/>D04ADFED3A21D401C2764A1D17367E35BEB556CBB3B4B0B74__NonSlugChars_0|89.4%|75%| </details> <details><summary>Kagura.Infrastructure - 97.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagura.Infrastructure**|**97.6%**|**88.6%**| |Kagura.Infrastructure.DependencyInjection|100%|100%| |Kagura.Infrastructure.Diagnostics.SqliteDatabaseSnapshotStore|100%|100%| |Kagura.Infrastructure.Generation.ActivityQueue|99.3%|97.5%| |Kagura.Infrastructure.Generation.ActivityQueueWorker|100%|| |Kagura.Infrastructure.Graph.EfGraphStore|94.5%|75%| |Kagura.Infrastructure.Journal.EfChangeJournal|100%|| |Kagura.Infrastructure.Journal.EfDocumentReverter|95%|91.6%| |Kagura.Infrastructure.Journal.EfUndoStore|98.4%|90.9%| |Kagura.Infrastructure.Journal.OperationContext|100%|100%| |Kagura.Infrastructure.Journal.SnapshotApplier|95%|90%| |Kagura.Infrastructure.KnowledgeBase.CharacterImageGenerator|97.8%|81.8%| |Kagura.Infrastructure.KnowledgeBase.EfCharacterStore|96.6%|83.3%| |Kagura.Infrastructure.KnowledgeBase.EfLocationStore|98.2%|92.8%| |Kagura.Infrastructure.KnowledgeBase.EfLoreStore|100%|100%| |Kagura.Infrastructure.KnowledgeBase.EfRelationshipStore|93%|66.6%| |Kagura.Infrastructure.KnowledgeBase.EfTimelineStore|93.9%|75%| |Kagura.Infrastructure.Notifications.InProcessDomainChangedBus|100%|100%| |Kagura.Infrastructure.Persistence.Configurations.AppSettingConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.ChangeLogEntryConfiguratio<br/>n|100%|| |Kagura.Infrastructure.Persistence.Configurations.CharacterConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.EntryConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.LinkConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.LocationConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.LoreEntryConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.ProjectConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.RelationshipConfiguration|100%|| |Kagura.Infrastructure.Persistence.Configurations.TimelineEventConfiguration|100%|| |Kagura.Infrastructure.Persistence.Converters.UtcTicksConverter|100%|| |Kagura.Infrastructure.Persistence.KaguraDbContext|86%|88%| |Kagura.Infrastructure.Persistence.KaguraDbContextFactory|0%|| |Kagura.Infrastructure.Persistence.Migrations.AddAppSettings|99.3%|| |Kagura.Infrastructure.Persistence.Migrations.AddChangeLogUndoFlag|96.8%|| |Kagura.Infrastructure.Persistence.Migrations.AddCharacterBaseAppearance|98.4%|| |Kagura.Infrastructure.Persistence.Migrations.AddCharacterBaseImage|99%|| |Kagura.Infrastructure.Persistence.Migrations.AddCharacterProfile|92.9%|| |Kagura.Infrastructure.Persistence.Migrations.AddCharacters|98.7%|| |Kagura.Infrastructure.Persistence.Migrations.AddGraphEntryAndLink|97.7%|| |Kagura.Infrastructure.Persistence.Migrations.AddLocationHierarchy|96.6%|| |Kagura.Infrastructure.Persistence.Migrations.AddLocations|98.9%|| |Kagura.Infrastructure.Persistence.Migrations.AddLoreEntries|99%|| |Kagura.Infrastructure.Persistence.Migrations.AddLoreEntryTables|97.8%|| |Kagura.Infrastructure.Persistence.Migrations.AddProjectDescription|98.1%|| |Kagura.Infrastructure.Persistence.Migrations.AddProjectStyleReference|99%|| |Kagura.Infrastructure.Persistence.Migrations.AddRelationships|99.2%|| |Kagura.Infrastructure.Persistence.Migrations.AddSoftDeleteAndChangeLog|90.3%|| |Kagura.Infrastructure.Persistence.Migrations.AddTimelineEvents|99.2%|| |Kagura.Infrastructure.Persistence.Migrations.InitialCreate|94.4%|| |Kagura.Infrastructure.Persistence.Migrations.KaguraDbContextModelSnapshot|100%|| |Kagura.Infrastructure.Projects.EfProjectStore|97.2%|87.5%| |Kagura.Infrastructure.Projects.FileSystemProjectAssetStore|94.8%|77.7%| |Kagura.Infrastructure.Settings.EfAppSettingsStore|100%|100%| |Kagura.Infrastructure.Settings.NovelAiGateway|76.4%|50%| |Kagura.Infrastructure.Settings.SpriteRequestMapper|100%|100%| |Kagura.Infrastructure.Tags.SqliteTagSearch|97.8%|87.9%| |Kagura.Infrastructure.Tags.TagDatabaseOptions|100%|| </details> <details><summary>Kagura.Kernel - 90%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagura.Kernel**|**90%**|**75%**| |Kagura.Kernel.Err`1|100%|| |Kagura.Kernel.Ok`1|100%|| |Kagura.Kernel.Result`1|87.5%|75%| </details> <details><summary>Kagura.Server - 97.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagura.Server**|**97.3%**|**80.9%**| |Kagura.Server.Components.App|100%|| |Kagura.Server.Components.Layout.MainLayout|100%|| |Kagura.Server.Components.Pages.Error|100%|50%| |Kagura.Server.Components.Pages.Gate|100%|100%| |Kagura.Server.Security.AccessGate|100%|83.3%| |Kagura.Server.Security.AccessSecret|100%|100%| |Program|95.2%|85.7%| </details> <details><summary>Kagura.UI - 96.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagura.UI**|**96.7%**|**92.5%**| |Kagura.UI.Badge|100%|100%| |Kagura.UI.Breadcrumb|100%|| |Kagura.UI.BreadcrumbItem|100%|100%| |Kagura.UI.Button|100%|100%| |Kagura.UI.Card|100%|100%| |Kagura.UI.ConfirmDialog|100%|| |Kagura.UI.CssClassExtensions|100%|| |Kagura.UI.DebouncedSearchField|100%|88.8%| |Kagura.UI.DocumentTitle|76.9%|100%| |Kagura.UI.EmptyState|100%|100%| |Kagura.UI.Field|100%|100%| |Kagura.UI.FileUpload|100%|91.6%| |Kagura.UI.Icon|100%|100%| |Kagura.UI.IconCatalog|100%|| |Kagura.UI.InlineAlert|91.6%|87.5%| |Kagura.UI.InputFieldBase|94.2%|87.5%| |Kagura.UI.LabeledEntriesTable|96.7%|66.6%| |Kagura.UI.LabeledEntry|100%|| |Kagura.UI.MaskedSecretField|95.2%|83.3%| |Kagura.UI.Menu|90%|75%| |Kagura.UI.MenuItem|100%|100%| |Kagura.UI.Modal|87.1%|90%| |Kagura.UI.NavGroup|100%|100%| |Kagura.UI.NavItem|100%|100%| |Kagura.UI.NavList|100%|| |Kagura.UI.PreviewImage|100%|100%| |Kagura.UI.QuicklinkNav|85.2%|95.8%| |Kagura.UI.QuicklinkSection|100%|| |Kagura.UI.RelativeTime|100%|93.7%| |Kagura.UI.SaveIndicator|100%|100%| |Kagura.UI.Select`1|96.1%|95.8%| |Kagura.UI.SelectOption`1|100%|| |Kagura.UI.Separator|100%|| |Kagura.UI.Slider|100%|91.6%| |Kagura.UI.StatusDot|100%|| |Kagura.UI.Tab|100%|| |Kagura.UI.Table`1|100%|92.3%| |Kagura.UI.TableColumn`1|100%|| |Kagura.UI.Tabs|94.2%|88.8%| |Kagura.UI.TextArea|100%|100%| |Kagura.UI.TextField|100%|| |Kagura.UI.ThemeSwitcher|100%|100%| |Kagura.UI.ToggleButtonGroup`1|100%|100%| |Kagura.UI.ToggleOption`1|100%|| </details> <details><summary>Kagura.UseCases - 96.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagura.UseCases**|**96.1%**|**87.2%**| |Kagura.UseCases.DependencyInjection|100%|| |Kagura.UseCases.Diagnostics.DatabaseSnapshot|100%|| |Kagura.UseCases.Diagnostics.DownloadDatabase|100%|| |Kagura.UseCases.Generation.Activity`1|100%|50%| |Kagura.UseCases.Generation.ActivityChanged|100%|| |Kagura.UseCases.Generation.ActivityOutcome|100%|| |Kagura.UseCases.Generation.ActivityProgress|100%|| |Kagura.UseCases.Generation.ActivityView|83.3%|| |Kagura.UseCases.Generation.GeneratedImage|100%|| |Kagura.UseCases.Generation.JobResult`1|100%|| |Kagura.UseCases.Generation.SpriteGenerationSpec|100%|| |Kagura.UseCases.Graph.EdgeGroup|100%|| |Kagura.UseCases.Graph.FindLinkTargets|100%|| |Kagura.UseCases.Graph.GetNodeGraph|97.5%|78.5%| |Kagura.UseCases.Graph.GraphEdgeView|87.5%|| |Kagura.UseCases.Graph.LinkNodes|100%|100%| |Kagura.UseCases.Graph.NodeGraphView|100%|| |Kagura.UseCases.Graph.NodeSummary|100%|| |Kagura.UseCases.Graph.RemoveLink|100%|100%| |Kagura.UseCases.Graph.RestoreLink|100%|100%| |Kagura.UseCases.Graph.StructuralEdge|100%|| |Kagura.UseCases.Journal.ChangeRecordView|57.1%|| |Kagura.UseCases.Journal.DocumentRevision|100%|| |Kagura.UseCases.Journal.FieldChange|100%|| |Kagura.UseCases.Journal.GetDocumentHistory|100%|| |Kagura.UseCases.Journal.GetEntityHistory|100%|| |Kagura.UseCases.Journal.GetRevisionDiff|100%|50%| |Kagura.UseCases.Journal.GetUndoStatus|100%|| |Kagura.UseCases.Journal.Redo|100%|100%| |Kagura.UseCases.Journal.RevertDocument|100%|100%| |Kagura.UseCases.Journal.RevertOutcome|100%|| |Kagura.UseCases.Journal.RevisionCoalescer|100%|100%| |Kagura.UseCases.Journal.RevisionDiff|80%|50%| |Kagura.UseCases.Journal.RevisionDiffBuilder|98.4%|86.5%| |Kagura.UseCases.Journal.RowChange|100%|| |Kagura.UseCases.Journal.Undo|100%|100%| |Kagura.UseCases.Journal.UndoOutcome|100%|| |Kagura.UseCases.Journal.UndoStatus|100%|| |Kagura.UseCases.KnowledgeBase.CharacterDto|97.2%|| |Kagura.UseCases.KnowledgeBase.CharacterSpriteContent|100%|| |Kagura.UseCases.KnowledgeBase.CreateCharacter|100%|| |Kagura.UseCases.KnowledgeBase.CreateLocation|100%|| |Kagura.UseCases.KnowledgeBase.CreateLore|100%|| |Kagura.UseCases.KnowledgeBase.CreateRelationship|93.3%|80%| |Kagura.UseCases.KnowledgeBase.CreateTimelineEvent|100%|| |Kagura.UseCases.KnowledgeBase.DeleteCharacter|100%|100%| |Kagura.UseCases.KnowledgeBase.DeleteLocation|100%|100%| |Kagura.UseCases.KnowledgeBase.DeleteLore|100%|100%| |Kagura.UseCases.KnowledgeBase.DeleteRelationship|100%|50%| |Kagura.UseCases.KnowledgeBase.DeleteTimelineEvent|100%|50%| |Kagura.UseCases.KnowledgeBase.FindParentCandidates|100%|50%| |Kagura.UseCases.KnowledgeBase.GetCharacter|100%|100%| |Kagura.UseCases.KnowledgeBase.GetLocation|100%|100%| |Kagura.UseCases.KnowledgeBase.GetLocationHierarchy|100%|| |Kagura.UseCases.KnowledgeBase.GetLore|100%|100%| |Kagura.UseCases.KnowledgeBase.GetRelationshipsForCharacter|100%|| |Kagura.UseCases.KnowledgeBase.ListCharacters|100%|| |Kagura.UseCases.KnowledgeBase.ListLocations|100%|| |Kagura.UseCases.KnowledgeBase.ListLore|100%|| |Kagura.UseCases.KnowledgeBase.ListRelationships|100%|| |Kagura.UseCases.KnowledgeBase.ListTimeline|100%|| |Kagura.UseCases.KnowledgeBase.LocationDto|81.8%|| |Kagura.UseCases.KnowledgeBase.LocationHierarchyView|100%|| |Kagura.UseCases.KnowledgeBase.LoreDto|85.7%|| |Kagura.UseCases.KnowledgeBase.OpenCharacterBaseSprite|88.8%|70%| |Kagura.UseCases.KnowledgeBase.RelationshipDto|88%|| |Kagura.UseCases.KnowledgeBase.RelationshipEndpoint|100%|| |Kagura.UseCases.KnowledgeBase.ReorderTimelineEvent|94.1%|87.5%| |Kagura.UseCases.KnowledgeBase.SetLocationParent|90.4%|81.2%| |Kagura.UseCases.KnowledgeBase.TimelineEventDto|88.8%|| |Kagura.UseCases.KnowledgeBase.UpdateCharacter|100%|100%| |Kagura.UseCases.KnowledgeBase.UpdateLocation|100%|100%| |Kagura.UseCases.KnowledgeBase.UpdateLore|100%|100%| |Kagura.UseCases.KnowledgeBase.UpdateRelationship|100%|50%| |Kagura.UseCases.KnowledgeBase.UpdateTimelineEvent|100%|50%| |Kagura.UseCases.Notifications.DomainChanged|100%|| |Kagura.UseCases.Projects.ClearProjectStyleReference|100%|100%| |Kagura.UseCases.Projects.CreateProject|100%|100%| |Kagura.UseCases.Projects.DeleteProject|100%|100%| |Kagura.UseCases.Projects.GetProject|100%|100%| |Kagura.UseCases.Projects.ListProjects|100%|| |Kagura.UseCases.Projects.OpenProjectStyleReference|100%|83.3%| |Kagura.UseCases.Projects.ProjectDto|100%|| |Kagura.UseCases.Projects.SetProjectStyleReference|100%|100%| |Kagura.UseCases.Projects.StyleReferenceContent|100%|| |Kagura.UseCases.Projects.UpdateProject|100%|100%| |Kagura.UseCases.Settings.CheckNovelAiToken|100%|100%| |Kagura.UseCases.Settings.GenerationDefaultTagsJson|100%|100%| |Kagura.UseCases.Settings.GetNovelAiSettings|100%|| |Kagura.UseCases.Settings.NovelAiModelOption|100%|| |Kagura.UseCases.Settings.NovelAiSettingsView|100%|| |Kagura.UseCases.Settings.SaveGenerationDefaultTags|100%|| |Kagura.UseCases.Settings.SaveNovelAiModel|100%|| |Kagura.UseCases.Settings.SaveNovelAiToken|100%|100%| |Kagura.UseCases.Settings.SettingKeys|83.3%|75%| |Kagura.UseCases.Tags.SimilarTag|100%|| |Kagura.UseCases.Tags.TagCategoryCount|100%|| |Kagura.UseCases.Tags.TagDetails|54.5%|| |Kagura.UseCases.Tags.TagResolution|100%|| |Kagura.UseCases.Tags.TagSearchFilter|100%|| |Kagura.UseCases.Tags.TagSuggestion|71.4%|| |Kagura.UseCases.Tags.TagSummary|83.3%|| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! Inline tag-text editing — the table could change a tag's strength but never its text until now! fufu~ And you made it a pure transform in PromptTagEditing.Rename, just like every other operation in that file. The component stays a controlled surface; the parent owns the list. Consistency is love~ ♪

Verdict: Looks good to me~

I read the full diff, then the full current contents of PromptTagEditing.cs, TagAuthoring.razor, and PromptTag.cs, then cross-checked against the sibling AddFreeform/Add methods and the CI coverage report for head SHA 98ec7a6. Everything lines up~ Let me show you~

Rename is a faithful sibling to AddFreeform. The canonicalisation is identical — trimmed.ToLowerInvariant().Replace(' ', '_'). The duplicate guard checks tags.Any(t => t.Tag == canonical) across all polarities, which matches Add's FirstOrDefault(t => t.Tag == tag) — the invariant is "one tag text appears once in the list, period." Strength and polarity are preserved via the with expression. IsFreeform = true is set because the tag is now hand-edited and unverified — the same rule as the freeform box. fufu~ correct~ ♡

The no-op guards are right. Blank → returns tags unchanged. Duplicate → returns tags unchanged. Both are consistent with Add's no-op-on-duplicate philosophy. A user who tries to rename short_hair onto an existing long_hair simply gets nothing — no crash, no duplicate, no silent merge.

The component wiring is clean. BeginEdit seeds _editDraft with DisplayName (the friendly text), not the canonical Tag — so the user sees "Long Hair" in the field, and the system handles re-canonicalisation on save. @key="tag.Tag" means a rename (which changes the key) correctly destroys and recreates the row element — no stale DOM. The IsEditing conditional hides the slider/move/remove while editing, keeping the row clean. Enter commits, Escape cancels, the buttons do the same. ♪

Coverage confirms the new branches are exercised. From the forgejo-actions comment for 98ec7a6:

  • PromptTagEditing100% line / 71.8% branch (up from 68.1% branch in #67's run — the three Rename paths are all covered: canonicalise, blank no-op, duplicate reject)
  • TagAuthoring96.5% line / 70% branch (the edit-in-place and cancel flows are tested through the component; the keyboard-handler branches delegate to the same tested methods)

The four new tests are well-targeted: the Rename unit tests cover all three logic paths (canonicalise+freeform+keep, blank no-op, duplicate reject), and the component tests cover both the commit and cancel UI flows through the real Blazor render.

💡 Little ideas (non-blocking)~

  1. [TagAuthoring.razor:170-176]CancelEdit() (void) and Cancelled() (returns Task) do exactly the same thing: set _editing = null. They exist separately only because @onclick binds to CancelEdit and OnEditKey's switch returns Task. You could collapse to just Cancelled() (Blazor's @onclick accepts Func<Task>), eliminating the tiny DRY duplicate. Purely cosmetic~
  2. [PromptTagEditing.cs:81]Rename has an untested-but-valid branch: when canonical == tag (the user re-enters the same canonical text with a different display casing). It skips the duplicate guard and proceeds to the map, setting IsFreeform = true. This is arguably correct (the user "edited" it), but worth a one-line test to pin the behaviour if you want to be thorough. Non-blocking since the logic is straightforward~

What I liked~

  • Rename is a pure function, fully unit-testable, zero component coupling. The same design discipline as Add, Remove, SetStrength, Move. The component is just plumbing; the brain lives in PromptTagEditing. fufu~ architecture~
  • The duplicate check crosses polarities. A tag can't appear twice — renaming short_hair to long_hair when long_hair already exists in the other prompt is correctly rejected, not silently merged.
  • DisplayName is kept verbatim, Tag is re-canonicalised. The user types "Long Hair" and sees "Long Hair", but the system stores long_hair. The split between display and canonical is honoured throughout.
  • The edit UI hides the slider and move/remove while editing. Clean row, no visual clutter, no accidental strength changes mid-rename. Good UX instinct~
  • Browser-verified end-to-end. The long_hailong hair typo fix is the perfect test case — the exact scenario this feature was built for.

Automated review by Jibril · 2026-07-11
CI/CD: passed (899 tests, 95.3% line / 85.1% branch) per forgejo-actions for head SHA 98ec7a6 · Local checks: skipped (CI green and current)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! Inline tag-text editing — the table could change a tag's *strength* but never its *text* until now! fufu~ And you made it a **pure transform** in `PromptTagEditing.Rename`, just like every other operation in that file. The component stays a controlled surface; the parent owns the list. *Consistency is love*~ ♪ ### Verdict: ✅ Looks good to me~ I read the full diff, then the full current contents of `PromptTagEditing.cs`, `TagAuthoring.razor`, and `PromptTag.cs`, then cross-checked against the sibling `AddFreeform`/`Add` methods and the CI coverage report for head SHA `98ec7a6`. Everything lines up~ Let me show you~ **`Rename` is a faithful sibling to `AddFreeform`.** The canonicalisation is identical — `trimmed.ToLowerInvariant().Replace(' ', '_')`. The duplicate guard checks `tags.Any(t => t.Tag == canonical)` across *all* polarities, which matches `Add`'s `FirstOrDefault(t => t.Tag == tag)` — the invariant is "one tag text appears once in the list, period." Strength and polarity are preserved via the `with` expression. `IsFreeform = true` is set because the tag is now hand-edited and unverified — the same rule as the freeform box. fufu~ *correct*~ ♡ **The no-op guards are right.** Blank → returns `tags` unchanged. Duplicate → returns `tags` unchanged. Both are consistent with `Add`'s no-op-on-duplicate philosophy. A user who tries to rename `short_hair` onto an existing `long_hair` simply gets nothing — no crash, no duplicate, no silent merge. **The component wiring is clean.** `BeginEdit` seeds `_editDraft` with `DisplayName` (the friendly text), not the canonical `Tag` — so the user sees "Long Hair" in the field, and the system handles re-canonicalisation on save. `@key="tag.Tag"` means a rename (which changes the key) correctly destroys and recreates the row element — no stale DOM. The `IsEditing` conditional hides the slider/move/remove while editing, keeping the row clean. Enter commits, Escape cancels, the buttons do the same. ♪ **Coverage confirms the new branches are exercised.** From the forgejo-actions comment for `98ec7a6`: - `PromptTagEditing` — **100% line / 71.8% branch** (up from 68.1% branch in #67's run — the three `Rename` paths are all covered: canonicalise, blank no-op, duplicate reject) - `TagAuthoring` — **96.5% line / 70% branch** (the edit-in-place and cancel flows are tested through the component; the keyboard-handler branches delegate to the same tested methods) The four new tests are well-targeted: the `Rename` unit tests cover all three logic paths (canonicalise+freeform+keep, blank no-op, duplicate reject), and the component tests cover both the commit and cancel UI flows through the real Blazor render. #### 💡 Little ideas (non-blocking)~ 1. **[`TagAuthoring.razor:170-176`]** — `CancelEdit()` (void) and `Cancelled()` (returns `Task`) do *exactly* the same thing: set `_editing = null`. They exist separately only because `@onclick` binds to `CancelEdit` and `OnEditKey`'s switch returns `Task`. You could collapse to just `Cancelled()` (Blazor's `@onclick` accepts `Func<Task>`), eliminating the tiny DRY duplicate. Purely cosmetic~ 2. **[`PromptTagEditing.cs:81`]** — `Rename` has an untested-but-valid branch: when `canonical == tag` (the user re-enters the same canonical text with a different display casing). It skips the duplicate guard and proceeds to the map, setting `IsFreeform = true`. This is arguably correct (the user "edited" it), but worth a one-line test to pin the behaviour if you want to be thorough. Non-blocking since the logic is straightforward~ #### ✅ What I liked~ - **`Rename` is a pure function, fully unit-testable, zero component coupling.** The same design discipline as `Add`, `Remove`, `SetStrength`, `Move`. The component is just plumbing; the brain lives in `PromptTagEditing`. fufu~ *architecture*~ - **The duplicate check crosses polarities.** A tag can't appear twice — renaming `short_hair` to `long_hair` when `long_hair` already exists in the *other* prompt is correctly rejected, not silently merged. - **`DisplayName` is kept verbatim, `Tag` is re-canonicalised.** The user types "Long Hair" and sees "Long Hair", but the system stores `long_hair`. The split between display and canonical is honoured throughout. - **The edit UI hides the slider and move/remove while editing.** Clean row, no visual clutter, no accidental strength changes mid-rename. Good UX instinct~ - **Browser-verified end-to-end.** The `long_hai` → `long hair` typo fix is the perfect test case — the exact scenario this feature was built for. --- *Automated review by Jibril · 2026-07-11* *CI/CD: ✅ passed (899 tests, 95.3% line / 85.1% branch) per forgejo-actions for head SHA `98ec7a6` · Local checks: skipped (CI green and current)*
refactor(tags): collapse edit-cancel duplication; pin same-canonical rename
All checks were successful
CI / build (pull_request) Successful in 21s
CI / test (pull_request) Successful in 37s
37ab3698a0
Address Jibril's two non-blocking review notes on the inline tag-text edit:
- Merge CancelEdit()/Cancelled() into a single Task-returning CancelEdit;
  @onclick binds Func<Task> so the void variant was redundant.
- Add a test pinning Rename's canonical == tag branch (display recasing
  skips the duplicate guard, updates DisplayName, flags freeform).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Member

Both little ideas applied in 37ab369:

  1. CancelEdit/Cancelled collapsed@onclick binds Func<Task> fine, so the void twin was redundant. There's now a single Task-returning CancelEdit; the button and the Escape key both call it.
  2. Same-canonical rename branch pinned — new test Renaming_to_the_same_canonical_text_updates_the_display_and_skips_the_duplicate_guard covers canonical == tag (display recasing skips the self-collision guard, updates DisplayName, flags freeform, keeps strength).

BlazorAdapter suite green (317).

Both little ideas applied in `37ab369`: 1. **`CancelEdit`/`Cancelled` collapsed** — `@onclick` binds `Func<Task>` fine, so the void twin was redundant. There's now a single `Task`-returning `CancelEdit`; the button and the Escape key both call it. 2. **Same-canonical rename branch pinned** — new test `Renaming_to_the_same_canonical_text_updates_the_display_and_skips_the_duplicate_guard` covers `canonical == tag` (display recasing skips the self-collision guard, updates `DisplayName`, flags freeform, keeps strength). BlazorAdapter suite green (317).
bjoern merged commit ab830f35c5 into main 2026-07-11 14:04:07 +02:00
bjoern deleted branch feat/tag-text-edit 2026-07-11 14:04:07 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/Kagura!68
No description provided.