Pointf5ive commited on
Commit
9cc52b4
·
1 Parent(s): 5432dfe

stage 4: sidebar and topbar

Browse files

Scope: implement render_sidebar(active='Home') and render_topbar(state) with required navigation labels and topbar chips/search/workspace visuals.

Validation: compile/import + label/chip/home-active checks passed; tabs/callback wiring unchanged.

Files changed (1) hide show
  1. app.py +148 -21
app.py CHANGED
@@ -331,9 +331,10 @@ TOTEM_CSS = """
331
 
332
  .totem-topbar {
333
  min-height: 64px;
334
- display: flex;
 
335
  align-items: center;
336
- justify-content: space-between;
337
  padding: 0 var(--space-5);
338
  }
339
 
@@ -371,6 +372,28 @@ TOTEM_CSS = """
371
  margin-bottom: var(--space-6);
372
  }
373
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  .totem-shell .totem-nav-item {
375
  height: 48px;
376
  display: flex;
@@ -393,6 +416,62 @@ TOTEM_CSS = """
393
  font-family: var(--font-ui);
394
  }
395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  .totem-shell .totem-placeholder {
397
  border: 1px dashed rgba(174, 183, 204, 0.45);
398
  border-radius: var(--radius-card);
@@ -410,6 +489,10 @@ TOTEM_CSS = """
410
  }
411
 
412
  @media (max-width: 1200px) {
 
 
 
 
413
  .totem-shell .totem-row {
414
  grid-template-columns: repeat(3, minmax(0, 1fr));
415
  }
@@ -426,6 +509,9 @@ TOTEM_CSS = """
426
  width: 100%;
427
  flex-basis: auto;
428
  }
 
 
 
429
  .totem-shell .totem-row {
430
  grid-template-columns: 1fr;
431
  }
@@ -661,34 +747,75 @@ def _clean_path(uploaded_file) -> Path:
661
  return workbook_path(uploaded_file)
662
 
663
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
  def render_dashboard(state: dict) -> str:
665
  """
666
- Stage 3 static shell wrapper.
667
- Callbacks are intentionally not rewired in this stage.
668
  """
669
  s = normalize_dashboard_state(state)
670
- project_name = esc(s.get("project_name", "Editorial Workspace"))
671
- analysis_status = esc(s.get("analysis_status", "idle")).upper()
672
  signal = int(s.get("totem_signal", 0) or 0)
673
  return f"""
674
  <div class="totem-shell">
675
- <aside class="totem-sidebar">
676
- <div class="totem-brand">TOTEM Studio</div>
677
- <div class="totem-nav-item active">Home</div>
678
- <div class="totem-nav-item">Projects</div>
679
- <div class="totem-nav-item">Workbook Upload</div>
680
- <div class="totem-nav-item">TOTEM Analytics</div>
681
- <div class="totem-nav-item">Revision Queue</div>
682
- <div class="totem-nav-item">Risk Clusters</div>
683
- <div class="totem-nav-item">Codex Extractor</div>
684
- <div class="totem-nav-item">Export</div>
685
- </aside>
686
  <main class="totem-main">
687
  <div class="totem-main-inner">
688
- <section class="totem-topbar">
689
- <div>{project_name}</div>
690
- <div class="totem-muted">STATUS: {analysis_status}</div>
691
- </section>
692
  <section class="totem-hero">
693
  <h2 style="margin:0;font-family:var(--font-display);font-size:44px;color:var(--totem-gold);">TOTEM Studio</h2>
694
  <p style="margin:10px 0 0 0;font-size:22px;color:rgba(246,241,232,.92);font-family:var(--font-ui);">
 
331
 
332
  .totem-topbar {
333
  min-height: 64px;
334
+ display: grid;
335
+ grid-template-columns: auto auto minmax(260px, 1fr) auto;
336
  align-items: center;
337
+ gap: var(--space-3);
338
  padding: 0 var(--space-5);
339
  }
340
 
 
372
  margin-bottom: var(--space-6);
373
  }
374
 
375
+ .totem-shell .totem-brand small {
376
+ display: block;
377
+ margin-top: var(--space-2);
378
+ font-family: var(--font-ui);
379
+ font-size: 12px;
380
+ letter-spacing: 1.5px;
381
+ color: rgba(174, 183, 204, 0.9);
382
+ }
383
+
384
+ .totem-shell .totem-quote-card {
385
+ margin-top: calc(var(--space-8) + var(--space-8));
386
+ padding: var(--space-5);
387
+ border-radius: var(--radius-card);
388
+ border: 1px solid rgba(242, 193, 78, 0.28);
389
+ background: radial-gradient(circle at 82% 70%, rgba(138, 77, 255, 0.22), transparent 45%),
390
+ linear-gradient(180deg, rgba(16, 26, 58, 0.95), rgba(11, 23, 51, 0.96));
391
+ color: rgba(246, 241, 232, 0.95);
392
+ font-family: var(--font-display);
393
+ font-size: 24px;
394
+ line-height: 1.25;
395
+ }
396
+
397
  .totem-shell .totem-nav-item {
398
  height: 48px;
399
  display: flex;
 
416
  font-family: var(--font-ui);
417
  }
418
 
419
+ .totem-shell .totem-chip {
420
+ min-height: 36px;
421
+ border-radius: 10px;
422
+ border: 1px solid rgba(174, 183, 204, 0.3);
423
+ background: rgba(7, 18, 45, 0.5);
424
+ color: rgba(246, 241, 232, 0.9);
425
+ display: inline-flex;
426
+ align-items: center;
427
+ padding: 0 var(--space-4);
428
+ font-family: var(--font-ui);
429
+ margin-right: var(--space-2);
430
+ }
431
+
432
+ .totem-shell .totem-chip.active {
433
+ border-color: rgba(242, 193, 78, 0.5);
434
+ background: linear-gradient(90deg, rgba(242, 193, 78, 0.18), rgba(138, 77, 255, 0.18));
435
+ }
436
+
437
+ .totem-shell .totem-search {
438
+ min-height: 38px;
439
+ border-radius: 10px;
440
+ border: 1px solid rgba(174, 183, 204, 0.35);
441
+ background: rgba(7, 18, 45, 0.42);
442
+ color: rgba(174, 183, 204, 0.95);
443
+ display: flex;
444
+ align-items: center;
445
+ padding: 0 var(--space-4);
446
+ font-family: var(--font-ui);
447
+ }
448
+
449
+ .totem-shell .totem-workspace {
450
+ display: inline-flex;
451
+ align-items: center;
452
+ gap: var(--space-3);
453
+ color: rgba(246, 241, 232, 0.96);
454
+ font-family: var(--font-ui);
455
+ }
456
+
457
+ .totem-shell .totem-avatar {
458
+ width: 34px;
459
+ height: 34px;
460
+ border-radius: 999px;
461
+ border: 1px solid rgba(242, 193, 78, 0.45);
462
+ display: grid;
463
+ place-items: center;
464
+ background: rgba(242, 193, 78, 0.16);
465
+ }
466
+
467
+ .totem-shell .totem-badge {
468
+ border: 1px solid rgba(242, 193, 78, 0.45);
469
+ border-radius: 8px;
470
+ padding: 3px 7px;
471
+ color: var(--totem-gold);
472
+ font-size: 12px;
473
+ }
474
+
475
  .totem-shell .totem-placeholder {
476
  border: 1px dashed rgba(174, 183, 204, 0.45);
477
  border-radius: var(--radius-card);
 
489
  }
490
 
491
  @media (max-width: 1200px) {
492
+ .totem-topbar {
493
+ grid-template-columns: auto minmax(200px, 1fr);
494
+ row-gap: var(--space-2);
495
+ }
496
  .totem-shell .totem-row {
497
  grid-template-columns: repeat(3, minmax(0, 1fr));
498
  }
 
509
  width: 100%;
510
  flex-basis: auto;
511
  }
512
+ .totem-topbar {
513
+ grid-template-columns: 1fr;
514
+ }
515
  .totem-shell .totem-row {
516
  grid-template-columns: 1fr;
517
  }
 
747
  return workbook_path(uploaded_file)
748
 
749
 
750
+ def render_sidebar(active: str = "Home") -> str:
751
+ nav_labels = [
752
+ "Home",
753
+ "Projects",
754
+ "Workbook Upload",
755
+ "TOTEM Analytics",
756
+ "Revision Queue",
757
+ "Risk Clusters",
758
+ "Codex Extractor",
759
+ "Export",
760
+ ]
761
+ active_label = str(active or "").strip()
762
+ items = []
763
+ for label in nav_labels:
764
+ classes = ["totem-nav-item"]
765
+ attrs = []
766
+ if label == active_label:
767
+ classes.append("active")
768
+ if label == "Codex Extractor":
769
+ classes.append("js-open-codex")
770
+ attrs.append('role="button"')
771
+ attrs.append('tabindex="0"')
772
+ attrs.append('aria-label="Open Codex Extractor tab"')
773
+ class_attr = " ".join(classes)
774
+ extra_attrs = f" {' '.join(attrs)}" if attrs else ""
775
+ items.append(f'<div class="{class_attr}"{extra_attrs}>{esc(label)}</div>')
776
+
777
+ return f"""
778
+ <aside class="totem-sidebar">
779
+ <div class="totem-brand">TOTEM Studio<small>Story intelligence OS</small></div>
780
+ {''.join(items)}
781
+ <div class="totem-quote-card">Story intelligence turns inspiration into impact.</div>
782
+ </aside>
783
+ """
784
+
785
+
786
+ def render_topbar(state: dict) -> str:
787
+ project_name = esc(state.get("project_name", "Editorial Workspace"))
788
+ return f"""
789
+ <section class="totem-topbar">
790
+ <div>
791
+ <span class="totem-chip active">Dashboard</span>
792
+ <span class="totem-chip">Codex Extractor</span>
793
+ <span class="totem-chip">Smoke Signal</span>
794
+ </div>
795
+ <div class="totem-search">Search projects, workbooks, blocks...</div>
796
+ <div class="totem-workspace">
797
+ <span>{project_name}</span>
798
+ <span aria-hidden="true">🔔</span>
799
+ <span class="totem-avatar">T</span>
800
+ <span class="totem-badge">TOTEM Studio</span>
801
+ </div>
802
+ </section>
803
+ """
804
+
805
+
806
  def render_dashboard(state: dict) -> str:
807
  """
808
+ Stage 3/4 static shell wrapper.
809
+ Callback wiring remains untouched until later stages.
810
  """
811
  s = normalize_dashboard_state(state)
 
 
812
  signal = int(s.get("totem_signal", 0) or 0)
813
  return f"""
814
  <div class="totem-shell">
815
+ {render_sidebar(active='Home')}
 
 
 
 
 
 
 
 
 
 
816
  <main class="totem-main">
817
  <div class="totem-main-inner">
818
+ {render_topbar(s)}
 
 
 
819
  <section class="totem-hero">
820
  <h2 style="margin:0;font-family:var(--font-display);font-size:44px;color:var(--totem-gold);">TOTEM Studio</h2>
821
  <p style="margin:10px 0 0 0;font-size:22px;color:rgba(246,241,232,.92);font-family:var(--font-ui);">