summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/files/BodyRenderer.tsx
blob: 9d008e21b05f809ebddc9bd6645503c9d522fe4c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import type { BodyElement } from "../../lib/api";
import { ChartRenderer } from "../charts/ChartRenderer";

interface BodyRendererProps {
  elements: BodyElement[];
}

export function BodyRenderer({ elements }: BodyRendererProps) {
  if (elements.length === 0) {
    return (
      <div className="text-[#555] font-mono text-sm italic">
        No content yet. Use the CLI below to add content.
      </div>
    );
  }

  return (
    <div className="space-y-4">
      {elements.map((element, index) => (
        <BodyElementRenderer key={index} element={element} />
      ))}
    </div>
  );
}

function BodyElementRenderer({ element }: { element: BodyElement }) {
  switch (element.type) {
    case "heading":
      return <HeadingElement level={element.level} text={element.text} />;
    case "paragraph":
      return <ParagraphElement text={element.text} />;
    case "chart":
      return (
        <ChartElement
          chartType={element.chartType}
          data={element.data}
          title={element.title}
          config={element.config}
        />
      );
    case "image":
      return (
        <ImageElement
          src={element.src}
          alt={element.alt}
          caption={element.caption}
        />
      );
    default:
      return null;
  }
}

function HeadingElement({ level, text }: { level: number; text: string }) {
  const className = "font-mono text-[#9bc3ff]";

  switch (level) {
    case 1:
      return <h1 className={`${className} text-2xl font-bold`}>{text}</h1>;
    case 2:
      return <h2 className={`${className} text-xl font-bold`}>{text}</h2>;
    case 3:
      return <h3 className={`${className} text-lg font-semibold`}>{text}</h3>;
    case 4:
      return <h4 className={`${className} text-base font-semibold`}>{text}</h4>;
    case 5:
      return <h5 className={`${className} text-sm font-semibold`}>{text}</h5>;
    case 6:
      return <h6 className={`${className} text-xs font-semibold`}>{text}</h6>;
    default:
      return <h3 className={`${className} text-lg font-semibold`}>{text}</h3>;
  }
}

function ParagraphElement({ text }: { text: string }) {
  return <p className="font-mono text-sm text-white/80 leading-relaxed">{text}</p>;
}

function ChartElement({
  chartType,
  data,
  title,
  config,
}: {
  chartType: "line" | "bar" | "pie" | "area";
  data: Record<string, unknown>[];
  title?: string;
  config?: Record<string, unknown>;
}) {
  return (
    <div className="border border-[#333] p-4 bg-black/30">
      <ChartRenderer
        chartType={chartType}
        data={data}
        title={title}
        config={config}
      />
    </div>
  );
}

function ImageElement({
  src,
  alt,
  caption,
}: {
  src: string;
  alt?: string;
  caption?: string;
}) {
  return (
    <figure className="space-y-2">
      <img
        src={src}
        alt={alt || ""}
        className="max-w-full border border-[#333]"
      />
      {caption && (
        <figcaption className="text-[#555] font-mono text-xs italic">
          {caption}
        </figcaption>
      )}
    </figure>
  );
}