feat(creator): overhaul Creator flow, editor duplication, and staging setup (#83)

This pull request completely overhauls the Creator editor flow, resolves the editor duplication race condition, aligns layout/styling themes in light and dark mode, and adds Docker staging setups.

### Key Changes
1. **Creator Flow Polish**: Redesigned the editor canvas to prevent double scrolling by delegating overflow to the editor canvas layer, updated styles to a premium aesthetic.
2. **Race Condition Prevention**: Resolved Crepe editor duplication when loading or switching chapters by tracking state via shared window maps (`window.editorCache`, `window.editorStates`) and checking `_lastInitializedEditorId` synchronously in Blazor.
3. **Theme Synchronization**: Integrated explicit theme initialization (`ThemeService.InitializeAsync()`) and anchored CSS isolation selectors to correctly sync with Light (Soft Sepia) and Deep Dark theme preferences.
4. **Staging Automation**: Created staging docker configurations with `--nexus-only` flag to allow iterative development without resetting PG/Neo4j database containers.

---------

Co-authored-by: Marek Jasiński <jasins.marek@gmail.com>
Reviewed-on: #83
Co-authored-by: Antigravity <antigravity@google.com>
Co-committed-by: Antigravity <antigravity@google.com>
This commit was merged in pull request #83.
This commit is contained in:
2026-06-15 17:15:42 +00:00
committed by Marek Jaisński
parent ec3fc52a73
commit c94e8f0acb
51 changed files with 5868 additions and 493 deletions
@@ -172,6 +172,103 @@ namespace NexusReader.Data.Migrations
b.ToTable("Authors");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Book", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("CurrentDraftRevisionId")
.HasColumnType("uuid");
b.Property<Guid?>("LivePublishedRevisionId")
.HasColumnType("uuid");
b.Property<string>("TenantId")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CurrentDraftRevisionId");
b.HasIndex("LivePublishedRevisionId");
b.HasIndex("TenantId");
b.HasIndex("UserId");
b.ToTable("Books");
});
modelBuilder.Entity("NexusReader.Domain.Entities.BookRevision", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("BookId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsPublished")
.HasColumnType("boolean");
b.Property<DateTime?>("PublishedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("VersionString")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.HasKey("Id");
b.HasIndex("BookId");
b.ToTable("BookRevisions");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Chapter", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("BookRevisionId")
.HasColumnType("uuid");
b.Property<string>("MarkdownContent")
.IsRequired()
.HasColumnType("text");
b.Property<int>("SortOrder")
.HasColumnType("integer");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.HasKey("Id");
b.HasIndex("BookRevisionId");
b.ToTable("Chapters");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Ebook", b =>
{
b.Property<Guid>("Id")
@@ -614,6 +711,53 @@ namespace NexusReader.Data.Migrations
.IsRequired();
});
modelBuilder.Entity("NexusReader.Domain.Entities.Book", b =>
{
b.HasOne("NexusReader.Domain.Entities.BookRevision", "CurrentDraftRevision")
.WithMany()
.HasForeignKey("CurrentDraftRevisionId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("NexusReader.Domain.Entities.BookRevision", "LivePublishedRevision")
.WithMany()
.HasForeignKey("LivePublishedRevisionId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("NexusReader.Domain.Entities.NexusUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CurrentDraftRevision");
b.Navigation("LivePublishedRevision");
b.Navigation("User");
});
modelBuilder.Entity("NexusReader.Domain.Entities.BookRevision", b =>
{
b.HasOne("NexusReader.Domain.Entities.Book", "Book")
.WithMany("Revisions")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Chapter", b =>
{
b.HasOne("NexusReader.Domain.Entities.BookRevision", "BookRevision")
.WithMany("Chapters")
.HasForeignKey("BookRevisionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("BookRevision");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Ebook", b =>
{
b.HasOne("NexusReader.Domain.Entities.Author", "Author")
@@ -689,6 +833,16 @@ namespace NexusReader.Data.Migrations
b.Navigation("Ebooks");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Book", b =>
{
b.Navigation("Revisions");
});
modelBuilder.Entity("NexusReader.Domain.Entities.BookRevision", b =>
{
b.Navigation("Chapters");
});
modelBuilder.Entity("NexusReader.Domain.Entities.KnowledgeUnit", b =>
{
b.Navigation("IncomingLinks");