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

Merged
mjasin merged 15 commits from feature/stage3-book-versioning into develop 2026-06-15 17:15:43 +00:00
3 changed files with 192 additions and 131 deletions
Showing only changes of commit 08905a248d - Show all commits
+28 -5
View File
@@ -4,11 +4,23 @@
# -------------------------------------------------------------
set -e
NEXUS_ONLY=false
for arg in "$@"; do
case $arg in
--nexus-only|-n)
NEXUS_ONLY=true
;;
esac
done
ENV_FILE=".env.stage"
TEMPLATE_FILE=".env.stage.template"
COMPOSE_FILE="docker-compose.stage.yml"
echo "🏁 Starting staging environment orchestration..."
if [ "$NEXUS_ONLY" = true ]; then
echo "️ Mode: --nexus-only (only the web/nexus application container will be modified)"
fi
# 1. Create .env.stage if it doesn't exist
if [ ! -f "$ENV_FILE" ]; then
@@ -58,13 +70,24 @@ POSTGRES_PORT=${POSTGRES_PORT:-5438}
WEB_PORT=${WEB_PORT:-5080}
# 3. Stop any conflicting Docker Compose environments
echo "🧹 Stopping existing containers..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down --remove-orphans || true
docker compose down --remove-orphans 2>/dev/null || true
if [ "$NEXUS_ONLY" = true ]; then
echo "🧹 Stopping and removing only the web (nexus) container..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop web || true
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" rm -f web || true
else
echo "🧹 Stopping existing containers..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down --remove-orphans || true
docker compose down --remove-orphans 2>/dev/null || true
fi
# 4. Build and start containers
echo "🚀 Building and starting staging containers..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --build
if [ "$NEXUS_ONLY" = true ]; then
echo "🚀 Building and restarting only the web (nexus) container..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --build web
else
echo "🚀 Building and starting staging containers..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --build
fi
# 5. Wait for Database to be healthy
echo "⏳ Waiting for database (nexus-db-stage) to become healthy..."
@@ -6,15 +6,18 @@
<div class="creator-edit-fullscreen-wrapper">
<div class="chapters-sidebar">
<div class="sidebar-meta-header">
<h2>Rozdziały</h2>
</div>
<div class="chapters-list-wrapper">
<div class="chapter-item active">
<div class="active-indicator"></div>
<i class="fa-solid fa-book-open chapter-icon"></i>
<span>1. Rozdział 1: Wprowadzenie do Zen Mode</span>
<span class="chapter-title-text">1. Rozdział 1: Wprowadzenie do Zen Mode</span>
</div>
<div class="chapter-item">
<i class="fa-solid fa-lock chapter-icon"></i>
<span>2. Rozdział 2: Zabezpieczenia i Architektura</span>
<i class="fa-solid fa-file-lines chapter-icon"></i>
<span class="chapter-title-text">2. Rozdział 2: Zabezpieczenia i Architektura</span>
</div>
</div>
</div>
@@ -22,8 +25,10 @@
<div class="editor-workspace-area">
<div class="editor-header-row">
<div class="title-zone">
<h1 class="chapter-title">Rozdział 1: Wprowadzenie do Zen Mode</h1>
<div class="chapter-meta-zone">
</div>
<div class="telemetry-zone">
<span class="chapter-id-badge">ID: @ChapterId</span>
</div>
</div>
@@ -42,7 +47,7 @@
<button class="btn-nexus-premium" @onclick="FetchContent">
<span>Fetch Markdown Content</span>
<i class="fa-solid fa-arrow-right"></i>
<i class="fa-solid fa-arrow-right-long"></i>
</button>
</div>
@@ -57,7 +62,6 @@
private async Task FetchContent()
{
// Tutaj trafia Twoja logika wyciągania zawartości z edytora Milkdown
await Task.CompletedTask;
}
}
@@ -1,150 +1,179 @@
/* ==========================================================================
NEXUSREADER CREATOR EDIT MODE - PREMIUM SAAS CORE STYLES
NEXUSREADER CREATOR EDIT MODE - HIGH-FIDELITY SAAS PREMIUM DESIGN OVERRIDE
========================================================================== */
/* 1. MASTER WRAPPER (Zgładzenie globalnego scrolla przeglądarki) */
/* 1. ARCHITECTURAL BOUNDARY CONTROL */
.creator-edit-fullscreen-wrapper {
width: 100% !important;
max-width: 100% !important;
height: calc(100vh - 4rem) !important; /* Sztywne cięcie pod wysokość topbaru platformy */
height: calc(100vh - 4rem) !important;
margin: 0 !important;
padding: 0 !important;
display: flex !important;
overflow: hidden !important; /* Całkowity zakaz scrollowania okna głównego */
background-color: var(--bg-base);
overflow: hidden !important;
background-color: #121214;
box-sizing: border-box;
}
/* 2. MATTE CHAPTERS SIDEBAR (Luksusowy panel boczny) */
/* Dynamic theme bridge mapping for Warm Paper mode */
.theme-light .creator-edit-fullscreen-wrapper {
background-color: #f4f1ea;
}
/* 2. UNIFIED SIDEBAR DESIGN (Eliminating layout color fragmentation) */
.chapters-sidebar {
width: 290px !important;
width: 300px !important;
flex-shrink: 0;
background-color: #141417 !important; /* Matowy głęboki antracyt z readera */
border-right: 1px solid rgba(255, 255, 255, 0.05) !important;
background-color: #16161a !important;
border-right: 1px solid rgba(255, 255, 255, 0.04) !important;
display: flex;
flex-direction: column;
padding: 2rem 1.25rem !important;
padding: 2.5rem 1.5rem !important;
box-sizing: border-box;
}
.theme-light .chapters-sidebar {
background-color: #ede9df !important; /* Dopasowanie do motywu Warm Paper */
border-right: 1px solid var(--border) !important;
background-color: #eae6db !important; /* Rich warm tone that remains fully cohesive with warm paper base */
border-right: 1px solid #dcd7cc !important;
}
.chapters-sidebar h2 {
font-size: 0.85rem;
.sidebar-meta-header h2 {
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.5px;
color: var(--text-muted);
margin: 0 0 1.5rem 0;
padding-left: 0.5rem;
letter-spacing: 2px;
color: #a1a1aa;
margin: 0 0 1.75rem 0;
}
.theme-light .sidebar-meta-header h2 {
color: #78716c;
}
.chapters-list-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
gap: 6px;
}
/* Elementy listy rozdziałów */
/* Premium Navigation Links */
.chapter-item {
position: relative;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 14px !important;
border-radius: 8px;
color: var(--text-muted);
padding: 12px 16px !important;
border-radius: 10px;
color: #a1a1aa;
font-size: 0.95rem;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
border-left: 3px solid transparent !important;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.theme-light .chapter-item {
color: #78716c;
}
.chapter-item i.chapter-icon {
font-size: 0.9rem;
color: var(--text-muted);
font-size: 0.95rem;
color: #71717a;
transition: color 0.25s ease;
}
/* Aktywny stan unifikacji wizualnej */
/* Active Indicator Node Alignment */
.chapter-item.active {
background-color: rgba(0, 255, 153, 0.04) !important;
color: var(--accent) !important;
background-color: rgba(0, 255, 153, 0.05) !important;
color: #00ff99 !important;
font-weight: 600;
border-left: 3px solid var(--accent) !important;
}
.chapter-item.active i.chapter-icon {
color: var(--accent) !important;
}
.theme-light .chapter-item.active {
background-color: rgba(16, 185, 129, 0.06) !important;
color: #10b981 !important;
}
.chapter-item.active i.chapter-icon {
color: inherit !important;
}
.chapter-item:hover:not(.active) {
background-color: rgba(255, 255, 255, 0.02);
color: var(--text-main);
color: #ffffff;
}
/* 3. WORKSPACE CORE (Główna oś edytora) */
.theme-light .chapter-item:hover:not(.active) {
background-color: rgba(0, 0, 0, 0.02);
color: #2d2a26;
}
/* 3. WORKSPACE METRICS (Zen presentation spacing) */
.editor-workspace-area {
flex-grow: 1;
display: flex;
flex-direction: column;
height: 100%;
padding: 2.5rem 3.5rem 2rem 3.5rem !important; /* Odpowiedni, dostojny margines Zen Mode */
padding: 3rem 4rem 2.5rem 4rem !important; /* Generous padding context for premium scale */
box-sizing: border-box;
overflow: hidden;
}
/* Nagłówek i ID bez ryzyka kolizji */
.editor-header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
margin-bottom: 2rem;
flex-shrink: 0;
width: 100%;
}
.editor-workspace-area h1.chapter-title {
font-size: 2.2rem;
font-size: 2.4rem;
font-weight: 700;
color: var(--text-main);
color: #ffffff;
margin: 0;
letter-spacing: -0.5px;
letter-spacing: -0.75px;
}
.theme-light .editor-workspace-area h1.chapter-title {
color: #2d2a26;
}
.chapter-id-badge {
font-family: 'Azeret Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
background: rgba(255, 255, 255, 0.03);
padding: 5px 12px;
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.08);
white-space: nowrap;
font-size: 0.72rem;
color: #71717a;
background: #1a1a1e;
padding: 6px 14px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.05);
letter-spacing: 0.2px;
}
.theme-light .chapter-id-badge {
background: rgba(0, 0, 0, 0.02);
border: 1px solid var(--border);
background: #ffffff;
color: #78716c;
border: 1px solid #dcd7cc;
}
/* 4. PREMIUM CANVAS CARD (Rozciąganie Flex na 100% wysokości) */
/* 4. ELEVATED EDITOR CANVAS CARD (Introducing layered shadow mechanics) */
.editor-canvas-card {
background-color: var(--bg-surface) !important;
border: 1px solid var(--border) !important;
border-radius: 16px;
padding: 2.5rem !important;
background-color: #1a1a1e !important;
border: 1px solid rgba(255, 255, 255, 0.04) !important;
border-radius: 20px;
padding: 3rem !important;
display: flex;
flex-direction: column;
flex-grow: 1; /* Wymusza rozciągnięcie do samego paska stopki */
flex-grow: 1;
overflow: hidden;
box-sizing: border-box;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
/* Soft diffuse structural shadows mimicking actual surface elevation */
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4), 0 4px 12px rgba(0, 0, 0, 0.2);
}
.theme-light .editor-canvas-card {
background-color: #ffffff !important;
border: 1px solid #dcd7cc !important;
box-shadow: 0 20px 50px rgba(45, 42, 38, 0.04), 0 4px 12px rgba(45, 42, 38, 0.02);
}
.milkdown-premium-container {
@@ -155,8 +184,7 @@
width: 100%;
}
/* --- DEEP SELECTORS DLA PROSEMIRROR (MILKDOWN RENDER) --- */
/* DEEP MOUNTING COMPONENT INTEROP */
::deep .milkdown {
background: transparent !important;
box-shadow: none !important;
@@ -169,51 +197,32 @@
}
::deep .ProseMirror {
color: var(--text-main) !important;
color: #e4e1d9 !important;
background-color: transparent !important;
font-family: inherit !important;
font-size: 1.1rem !important;
line-height: 1.75 !important;
font-size: 1.15rem !important;
line-height: 1.8 !important;
flex-grow: 1;
overflow-y: auto !important; /* Scroll pojawia się TYLKO na tekście rozdziału */
padding-right: 15px !important;
overflow-y: auto !important;
padding-right: 24px !important;
outline: none !important;
box-sizing: border-box;
width: 100%;
}
/* Kontekst zaznaczenia zunifikowany z readerem */
.theme-light ::deep .ProseMirror {
color: #2d2a26 !important;
}
/* Precise matching text selection token */
::deep .ProseMirror ::selection {
background-color: rgba(0, 255, 153, 0.25) !important;
color: inherit !important;
background-color: rgba(0, 255, 153, 0.2) !important;
}
.theme-light ::deep .ProseMirror ::selection {
background-color: rgba(16, 185, 129, 0.2) !important;
background-color: rgba(16, 185, 129, 0.18) !important;
}
/* Popover / dymek formatowania zintegrowany z czytnikiem */
::deep .milkdown .popover,
::deep .milkdown-popover,
::deep .prosemirror-bubble-menu {
background-color: #1e1e22 !important;
border: 1px solid rgba(255, 255, 255, 0.08) !important;
border-radius: 12px !important;
padding: 6px 10px !important;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5) !important;
display: flex !important;
align-items: center !important;
gap: 12px !important;
}
.theme-light ::deep .milkdown .popover,
.theme-light ::deep .prosemirror-bubble-menu {
background-color: #ffffff !important;
border: 1px solid var(--border) !important;
box-shadow: 0 10px 30px rgba(45, 42, 38, 0.06) !important;
}
/* Customowy, dyskretny scrollbar dla tekstu książki */
/* Core webkit custom scrollbar mapping */
::deep .ProseMirror::-webkit-scrollbar {
width: 6px;
}
@@ -221,71 +230,96 @@
background: transparent;
}
::deep .ProseMirror::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 3px;
background: rgba(255, 255, 255, 0.08);
border-radius: 4px;
}
/* 5. FIXED FOOTER BAR (Zintegrowany dół pancernej karty) */
.theme-light ::deep .ProseMirror::-webkit-scrollbar-thumb {
background: #dcd7cc;
}
/* 5. SEAMLESS INTEGRATED ACTIONS FOOTER BAR */
.editor-footer-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1.5rem;
padding-top: 1.25rem;
border-top: 1px solid var(--border);
flex-shrink: 0; /* Gwarancja, że pasek nigdy nie ucieknie poza kartę */
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.04);
flex-shrink: 0;
width: 100%;
}
/* Status zapisu w chmurze z pulsującą diodą akcentową */
.theme-light .editor-footer-bar {
border-top: 1px solid #dcd7cc;
}
/* Telemetry cloud synchronization line */
.cloud-status-container {
display: flex;
align-items: center;
gap: 10px;
gap: 12px;
}
.cloud-status-pulse {
width: 8px;
height: 8px;
background-color: var(--accent);
width: 7px;
height: 7px;
background-color: #00ff99;
border-radius: 50%;
display: inline-block;
box-shadow: 0 0 10px var(--accent);
position: relative;
box-shadow: 0 0 10px rgba(0, 255, 153, 0.8);
}
.theme-light .cloud-status-pulse {
background-color: #10b981;
box-shadow: 0 0 10px rgba(16, 185, 129, 0.6);
}
.cloud-status-text {
font-family: 'Azeret Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
font-size: 0.82rem;
color: #71717a;
letter-spacing: 0.1px;
}
/* Przycisk akcji premium */
/* Premium Tactile Operational Button Trigger */
.btn-nexus-premium {
background-color: var(--accent) !important;
background-color: #00ff99 !important;
color: #121214 !important;
font-weight: 700;
font-size: 0.95rem;
padding: 10px 22px;
font-size: 0.9rem;
letter-spacing: -0.1px;
padding: 11px 24px;
border: none !important;
border-radius: 8px;
border-radius: 10px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 10px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 14px var(--accent-glow);
box-shadow: 0 4px 20px rgba(0, 255, 153, 0.15);
}
.theme-light .btn-nexus-premium {
background-color: #10b981 !important;
color: #ffffff !important;
box-shadow: 0 4px 20px rgba(16, 185, 129, 0.15);
}
.btn-nexus-premium i {
font-size: 0.85rem;
transition: transform 0.2s ease;
}
.btn-nexus-premium:hover {
transform: translateY(-1px);
box-shadow: 0 6px 20px var(--accent-glow);
box-shadow: 0 8px 24px rgba(0, 255, 153, 0.3);
}
.btn-nexus-premium:active {
transform: translateY(0);
.theme-light .btn-nexus-premium:hover {
box-shadow: 0 8px 24px rgba(16, 185, 129, 0.3);
}
.btn-nexus-premium:hover i {
transform: translateX(3px);
}