create-vizcraft-playground 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/index.js +171 -0
  2. package/package.json +44 -0
  3. package/template/.github/skills/vizcraft-playground/SKILL.md +573 -0
  4. package/template/README.md +59 -0
  5. package/template/eslint.config.js +23 -0
  6. package/template/index.html +13 -0
  7. package/template/package.json +37 -0
  8. package/template/public/vite.svg +1 -0
  9. package/template/scripts/generate-plugin.js +594 -0
  10. package/template/scripts/init-playground.js +119 -0
  11. package/template/src/App.scss +137 -0
  12. package/template/src/App.tsx +72 -0
  13. package/template/src/assets/react.svg +1 -0
  14. package/template/src/components/InfoModal/InfoModal.scss +211 -0
  15. package/template/src/components/InfoModal/InfoModal.tsx +85 -0
  16. package/template/src/components/Landing/Landing.scss +85 -0
  17. package/template/src/components/Landing/Landing.tsx +55 -0
  18. package/template/src/components/Shell.tsx +144 -0
  19. package/template/src/components/StepIndicator/StepIndicator.scss +151 -0
  20. package/template/src/components/StepIndicator/StepIndicator.tsx +73 -0
  21. package/template/src/components/VizInfoBeacon/VizInfoBeacon.scss +41 -0
  22. package/template/src/components/VizInfoBeacon/VizInfoBeacon.tsx +157 -0
  23. package/template/src/components/plugin-kit/CanvasStage.tsx +30 -0
  24. package/template/src/components/plugin-kit/ConceptPills.tsx +55 -0
  25. package/template/src/components/plugin-kit/PluginLayout.tsx +41 -0
  26. package/template/src/components/plugin-kit/SidePanel.tsx +69 -0
  27. package/template/src/components/plugin-kit/StageHeader.tsx +35 -0
  28. package/template/src/components/plugin-kit/StatBadge.tsx +35 -0
  29. package/template/src/components/plugin-kit/index.ts +42 -0
  30. package/template/src/components/plugin-kit/plugin-kit.scss +241 -0
  31. package/template/src/components/plugin-kit/useConceptModal.tsx +51 -0
  32. package/template/src/index.scss +81 -0
  33. package/template/src/main.tsx +14 -0
  34. package/template/src/playground.config.ts +27 -0
  35. package/template/src/plugins/hello-world/concepts.tsx +70 -0
  36. package/template/src/plugins/hello-world/helloWorldSlice.ts +29 -0
  37. package/template/src/plugins/hello-world/index.ts +48 -0
  38. package/template/src/plugins/hello-world/main.scss +32 -0
  39. package/template/src/plugins/hello-world/main.tsx +144 -0
  40. package/template/src/plugins/hello-world/useHelloWorldAnimation.ts +99 -0
  41. package/template/src/registry.ts +73 -0
  42. package/template/src/store/slices/simulationSlice.ts +47 -0
  43. package/template/src/store/store.ts +13 -0
  44. package/template/src/types/ModelPlugin.ts +55 -0
  45. package/template/src/utils/random.ts +11 -0
  46. package/template/tsconfig.app.json +35 -0
  47. package/template/tsconfig.json +7 -0
  48. package/template/tsconfig.node.json +26 -0
  49. package/template/vite.config.ts +7 -0
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+
3
+ export interface CanvasStageProps {
4
+ /** Ref forwarded to the inner canvas div (for VizCraft mounting). */
5
+ canvasRef?: React.Ref<HTMLDivElement>;
6
+ /** Content rendered inside the stage but outside the canvas (e.g. VizInfoBeacons). */
7
+ children?: React.ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ /**
12
+ * The main visualisation canvas wrapper with grid-dot background.
13
+ *
14
+ * @example
15
+ * <CanvasStage canvasRef={containerRef}>
16
+ * <VizInfoBeacon … />
17
+ * </CanvasStage>
18
+ */
19
+ const CanvasStage: React.FC<CanvasStageProps> = ({
20
+ canvasRef,
21
+ children,
22
+ className,
23
+ }) => (
24
+ <div className={`vc-canvas-stage${className ? ` ${className}` : ""}`}>
25
+ <div className="vc-canvas-stage__canvas" ref={canvasRef} />
26
+ {children}
27
+ </div>
28
+ );
29
+
30
+ export default CanvasStage;
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+
3
+ export interface PillDef {
4
+ key: string;
5
+ label: string;
6
+ /** CSS class modifier appended as `vc-pill--{variant}`.
7
+ * Alternatively supply `color` + `borderColor` inline. */
8
+ variant?: string;
9
+ color?: string;
10
+ borderColor?: string;
11
+ }
12
+
13
+ export interface ConceptPillsProps {
14
+ pills: PillDef[];
15
+ onOpen: (key: string) => void;
16
+ className?: string;
17
+ }
18
+
19
+ /**
20
+ * A horizontal row of clickable concept pills.
21
+ * Each pill fires `onOpen(pill.key)` — typically to open an InfoModal.
22
+ *
23
+ * @example
24
+ * <ConceptPills
25
+ * pills={[
26
+ * { key: "kafka", label: "Kafka", variant: "kafka" },
27
+ * { key: "partitioning", label: "Partitioning", color: "#fde68a", borderColor: "#f59e0b" },
28
+ * ]}
29
+ * onOpen={openConcept}
30
+ * />
31
+ */
32
+ const ConceptPills: React.FC<ConceptPillsProps> = ({
33
+ pills,
34
+ onOpen,
35
+ className,
36
+ }) => (
37
+ <div className={`vc-pills${className ? ` ${className}` : ""}`}>
38
+ {pills.map((p) => (
39
+ <button
40
+ key={p.key}
41
+ className={`vc-pill${p.variant ? ` vc-pill--${p.variant}` : ""}`}
42
+ style={
43
+ !p.variant && p.color
44
+ ? { color: p.color, borderColor: p.borderColor ?? p.color }
45
+ : undefined
46
+ }
47
+ onClick={() => onOpen(p.key)}
48
+ >
49
+ {p.label}
50
+ </button>
51
+ ))}
52
+ </div>
53
+ );
54
+
55
+ export default ConceptPills;
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+
3
+ export interface PluginLayoutProps {
4
+ /** Content rendered above the body grid (pills bar, toolbar). */
5
+ toolbar?: React.ReactNode;
6
+ /** The main visualisation area (left column). */
7
+ canvas: React.ReactNode;
8
+ /** The right-hand sidebar (right column). Omit for full-width canvas. */
9
+ sidebar?: React.ReactNode;
10
+ /** Extra class on the root. */
11
+ className?: string;
12
+ }
13
+
14
+ /**
15
+ * Standard two-column plugin layout: toolbar → body (canvas + sidebar).
16
+ *
17
+ * @example
18
+ * <PluginLayout
19
+ * toolbar={<ConceptPills pills={pills} onOpen={openConcept} />}
20
+ * canvas={<CanvasStage canvasRef={ref} />}
21
+ * sidebar={<SidePanel>…</SidePanel>}
22
+ * />
23
+ */
24
+ const PluginLayout: React.FC<PluginLayoutProps> = ({
25
+ toolbar,
26
+ canvas,
27
+ sidebar,
28
+ className,
29
+ }) => (
30
+ <div className={`vc-plugin-layout${className ? ` ${className}` : ""}`}>
31
+ {toolbar}
32
+ <div
33
+ className={`vc-plugin-layout__body${sidebar ? "" : " vc-plugin-layout__body--full"}`}
34
+ >
35
+ {canvas}
36
+ {sidebar}
37
+ </div>
38
+ </div>
39
+ );
40
+
41
+ export default PluginLayout;
@@ -0,0 +1,69 @@
1
+ import React from "react";
2
+
3
+ /* ─── SidePanel ───────────────────────────────────────── */
4
+
5
+ export interface SidePanelProps {
6
+ children: React.ReactNode;
7
+ className?: string;
8
+ }
9
+
10
+ /**
11
+ * Scrollable sidebar column. Place `<SideCard>` elements inside.
12
+ *
13
+ * @example
14
+ * <SidePanel>
15
+ * <SideCard label="What's happening"><p>{explanation}</p></SideCard>
16
+ * <SideCard variant="code" label="Source">…</SideCard>
17
+ * </SidePanel>
18
+ */
19
+ const SidePanel: React.FC<SidePanelProps> = ({ children, className }) => (
20
+ <aside className={`vc-side-panel${className ? ` ${className}` : ""}`}>
21
+ {children}
22
+ </aside>
23
+ );
24
+
25
+ /* ─── SideCard ────────────────────────────────────────── */
26
+
27
+ export interface SideCardProps {
28
+ /** Small uppercase label at the top of the card. */
29
+ label?: string;
30
+ /** Optional heading + sub rendered in a head row. */
31
+ heading?: string;
32
+ sub?: string;
33
+ /** Extra class for variant styling, e.g. `vc-side-card--explanation`. */
34
+ variant?: string;
35
+ children: React.ReactNode;
36
+ className?: string;
37
+ }
38
+
39
+ /**
40
+ * A card inside a SidePanel.
41
+ *
42
+ * @example
43
+ * <SideCard label="What just happened?" variant="explanation">
44
+ * <p>{explanation}</p>
45
+ * </SideCard>
46
+ */
47
+ const SideCard: React.FC<SideCardProps> = ({
48
+ label,
49
+ heading,
50
+ sub,
51
+ variant,
52
+ children,
53
+ className,
54
+ }) => (
55
+ <div
56
+ className={`vc-side-card${variant ? ` vc-side-card--${variant}` : ""}${className ? ` ${className}` : ""}`}
57
+ >
58
+ {label && <div className="vc-side-card__label">{label}</div>}
59
+ {(heading || sub) && (
60
+ <div className="vc-side-card__head">
61
+ {heading && <h3>{heading}</h3>}
62
+ {sub && <span className="vc-side-card__sub">{sub}</span>}
63
+ </div>
64
+ )}
65
+ {children}
66
+ </div>
67
+ );
68
+
69
+ export { SidePanel, SideCard };
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+
3
+ export interface StageHeaderProps {
4
+ title: string;
5
+ subtitle?: string;
6
+ /** Slot rendered on the right — typically StatBadge components. */
7
+ children?: React.ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ /**
12
+ * Title + subtitle + right-aligned stats area.
13
+ *
14
+ * @example
15
+ * <StageHeader title="ECS Autoscaling" subtitle="Watch containers scale">
16
+ * <StatBadge label="Phase" value="alarm" color="#fda4af" />
17
+ * <StatBadge label="Tasks" value="3/5" />
18
+ * </StageHeader>
19
+ */
20
+ const StageHeader: React.FC<StageHeaderProps> = ({
21
+ title,
22
+ subtitle,
23
+ children,
24
+ className,
25
+ }) => (
26
+ <div className={`vc-stage-header${className ? ` ${className}` : ""}`}>
27
+ <div className="vc-stage-header__text">
28
+ <h2>{title}</h2>
29
+ {subtitle && <p>{subtitle}</p>}
30
+ </div>
31
+ {children && <div className="vc-stage-header__stats">{children}</div>}
32
+ </div>
33
+ );
34
+
35
+ export default StageHeader;
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+
3
+ export interface StatBadgeProps {
4
+ label: string;
5
+ value: React.ReactNode;
6
+ /** Colour applied to the value text. */
7
+ color?: string;
8
+ className?: string;
9
+ }
10
+
11
+ /**
12
+ * A small stat/phase chip: uppercase label over a bold value.
13
+ *
14
+ * @example
15
+ * <StatBadge label="Phase" value="sync" color="#93c5fd" />
16
+ * <StatBadge label="Tasks" value="3/5" />
17
+ */
18
+ const StatBadge: React.FC<StatBadgeProps> = ({
19
+ label,
20
+ value,
21
+ color,
22
+ className,
23
+ }) => (
24
+ <div className={`vc-stat-badge${className ? ` ${className}` : ""}`}>
25
+ <span className="vc-stat-badge__label">{label}</span>
26
+ <span
27
+ className="vc-stat-badge__value"
28
+ style={color ? { color } : undefined}
29
+ >
30
+ {value}
31
+ </span>
32
+ </div>
33
+ );
34
+
35
+ export default StatBadge;
@@ -0,0 +1,42 @@
1
+ /* ═══════════════════════════════════════════════════════
2
+ * VizCraft Plugin Kit
3
+ *
4
+ * Shared, composable building blocks for plugin authors.
5
+ *
6
+ * Usage:
7
+ * import {
8
+ * ConceptPills,
9
+ * useConceptModal,
10
+ * PluginLayout,
11
+ * StageHeader,
12
+ * StatBadge,
13
+ * SidePanel,
14
+ * SideCard,
15
+ * CanvasStage,
16
+ * } from "../../components/plugin-kit";
17
+ *
18
+ * Styles:
19
+ * @use "../../components/plugin-kit/plugin-kit";
20
+ * — or import plugin-kit.scss once in your main.scss
21
+ * ═══════════════════════════════════════════════════════ */
22
+
23
+ export { default as ConceptPills } from "./ConceptPills";
24
+ export type { PillDef, ConceptPillsProps } from "./ConceptPills";
25
+
26
+ export { useConceptModal } from "./useConceptModal";
27
+ export type { ConceptDefinition } from "./useConceptModal";
28
+
29
+ export { default as PluginLayout } from "./PluginLayout";
30
+ export type { PluginLayoutProps } from "./PluginLayout";
31
+
32
+ export { default as StageHeader } from "./StageHeader";
33
+ export type { StageHeaderProps } from "./StageHeader";
34
+
35
+ export { default as StatBadge } from "./StatBadge";
36
+ export type { StatBadgeProps } from "./StatBadge";
37
+
38
+ export { SidePanel, SideCard } from "./SidePanel";
39
+ export type { SidePanelProps, SideCardProps } from "./SidePanel";
40
+
41
+ export { default as CanvasStage } from "./CanvasStage";
42
+ export type { CanvasStageProps } from "./CanvasStage";
@@ -0,0 +1,241 @@
1
+ /* ═══════════════════════════════════════════════════════
2
+ * VizCraft Plugin Kit — shared styles
3
+ *
4
+ * Import this once in your plugin's main.scss:
5
+ * @use "../../components/plugin-kit/plugin-kit";
6
+ *
7
+ * Or import globally in index.scss.
8
+ *
9
+ * All classes are prefixed `vc-` to avoid collisions
10
+ * with plugin-specific styles.
11
+ * ═══════════════════════════════════════════════════════ */
12
+
13
+ /* ── Plugin Layout ──────────────────────────────────── */
14
+ .vc-plugin-layout {
15
+ display: flex;
16
+ flex-direction: column;
17
+ width: 100%;
18
+ height: 100%;
19
+ overflow: hidden;
20
+ }
21
+
22
+ .vc-plugin-layout__body {
23
+ flex: 1;
24
+ min-height: 0;
25
+ display: grid;
26
+ grid-template-columns: minmax(0, 1fr) 320px;
27
+ gap: 1rem;
28
+ padding: 1rem;
29
+
30
+ &--full {
31
+ grid-template-columns: 1fr;
32
+ }
33
+ }
34
+
35
+ /* ── Concept Pills ──────────────────────────────────── */
36
+ .vc-pills {
37
+ display: flex;
38
+ gap: 0.5rem;
39
+ flex-wrap: wrap;
40
+ padding: 0.65rem 1rem;
41
+ flex-shrink: 0;
42
+ border-bottom: 1px solid rgba(148, 163, 184, 0.12);
43
+ background: rgba(2, 6, 23, 0.52);
44
+ backdrop-filter: blur(12px);
45
+ }
46
+
47
+ .vc-pill {
48
+ border: 1px solid transparent;
49
+ border-radius: 999px;
50
+ padding: 0.32rem 0.75rem;
51
+ background: rgba(15, 23, 42, 0.9);
52
+ font-size: 0.72rem;
53
+ font-weight: 700;
54
+ color: #fff;
55
+ cursor: pointer;
56
+ line-height: 1.3;
57
+ transition:
58
+ transform 0.14s ease,
59
+ background 0.14s ease,
60
+ border-color 0.14s ease;
61
+
62
+ &:hover {
63
+ transform: translateY(-1px);
64
+ }
65
+
66
+ &:active {
67
+ transform: translateY(0);
68
+ }
69
+ }
70
+
71
+ /* ── Light-theme pills (e.g. Big O) ─────────────────── */
72
+ .vc-pills--light {
73
+ background: rgba(255, 255, 255, 0.72);
74
+ backdrop-filter: blur(10px);
75
+ border-bottom-color: rgba(251, 146, 60, 0.18);
76
+
77
+ .vc-pill {
78
+ background: #fff;
79
+ color: #1f2937;
80
+ box-shadow: 0 14px 30px -18px rgba(15, 23, 42, 0.45);
81
+ }
82
+ }
83
+
84
+ /* ── Stage Header ───────────────────────────────────── */
85
+ .vc-stage-header {
86
+ display: flex;
87
+ align-items: flex-start;
88
+ justify-content: space-between;
89
+ gap: 0.8rem;
90
+ margin-bottom: 0.9rem;
91
+
92
+ h2 {
93
+ margin: 0;
94
+ font-size: 1.45rem;
95
+ letter-spacing: -0.02em;
96
+ }
97
+
98
+ p {
99
+ margin: 0.35rem 0 0;
100
+ color: #94a3b8;
101
+ font-size: 0.9rem;
102
+ line-height: 1.5;
103
+ max-width: 42rem;
104
+ }
105
+ }
106
+
107
+ .vc-stage-header__stats {
108
+ display: flex;
109
+ gap: 0.55rem;
110
+ flex-wrap: wrap;
111
+ justify-content: flex-end;
112
+ }
113
+
114
+ /* ── Stat Badge ─────────────────────────────────────── */
115
+ .vc-stat-badge {
116
+ min-width: 88px;
117
+ padding: 0.55rem 0.75rem;
118
+ border-radius: 16px;
119
+ background: rgba(15, 23, 42, 0.78);
120
+ border: 1px solid rgba(148, 163, 184, 0.12);
121
+ }
122
+
123
+ .vc-stat-badge__label {
124
+ display: block;
125
+ color: #94a3b8;
126
+ font-size: 0.66rem;
127
+ font-weight: 700;
128
+ text-transform: uppercase;
129
+ letter-spacing: 0.07em;
130
+ }
131
+
132
+ .vc-stat-badge__value {
133
+ display: block;
134
+ margin-top: 0.2rem;
135
+ font-weight: 700;
136
+ }
137
+
138
+ /* ── Side Panel ─────────────────────────────────────── */
139
+ .vc-side-panel {
140
+ min-height: 0;
141
+ display: flex;
142
+ flex-direction: column;
143
+ gap: 0.8rem;
144
+ overflow-y: auto;
145
+ padding-right: 0.15rem;
146
+ }
147
+
148
+ /* ── Side Card ──────────────────────────────────────── */
149
+ .vc-side-card {
150
+ border-radius: 20px;
151
+ padding: 0.95rem 1rem;
152
+ background: rgba(7, 17, 34, 0.88);
153
+ border: 1px solid rgba(148, 163, 184, 0.18);
154
+ box-shadow: 0 20px 42px -28px rgba(0, 0, 0, 0.7);
155
+
156
+ h3 {
157
+ margin: 0;
158
+ font-size: 0.96rem;
159
+ }
160
+
161
+ p {
162
+ margin: 0.4rem 0 0;
163
+ color: #cbd5e1;
164
+ font-size: 0.84rem;
165
+ line-height: 1.55;
166
+ }
167
+ }
168
+
169
+ .vc-side-card__label {
170
+ display: block;
171
+ color: #94a3b8;
172
+ font-size: 0.66rem;
173
+ font-weight: 700;
174
+ text-transform: uppercase;
175
+ letter-spacing: 0.07em;
176
+ }
177
+
178
+ .vc-side-card__head {
179
+ display: flex;
180
+ align-items: flex-start;
181
+ justify-content: space-between;
182
+ gap: 0.8rem;
183
+ }
184
+
185
+ .vc-side-card__sub {
186
+ display: block;
187
+ color: #94a3b8;
188
+ font-size: 0.66rem;
189
+ font-weight: 700;
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.07em;
192
+ text-align: right;
193
+ }
194
+
195
+ .vc-side-card--explanation {
196
+ background:
197
+ linear-gradient(135deg, rgba(15, 23, 42, 0.96), rgba(12, 74, 110, 0.44)),
198
+ rgba(7, 17, 34, 0.88);
199
+ }
200
+
201
+ /* ── Light-theme card (e.g. Big O) ───────────────────── */
202
+ .vc-side-card--light {
203
+ background: rgba(255, 255, 255, 0.82);
204
+ border-color: rgba(148, 163, 184, 0.32);
205
+ box-shadow: 0 14px 30px -18px rgba(15, 23, 42, 0.45);
206
+
207
+ p {
208
+ color: #4b5563;
209
+ }
210
+ }
211
+
212
+ /* ── Canvas Stage ───────────────────────────────────── */
213
+ .vc-canvas-stage {
214
+ position: relative;
215
+ flex: 1;
216
+ min-height: 0;
217
+ border-radius: 22px;
218
+ overflow: hidden;
219
+ border: 1px solid rgba(148, 163, 184, 0.12);
220
+ background:
221
+ linear-gradient(180deg, rgba(2, 6, 23, 0.96), rgba(8, 15, 30, 0.96)),
222
+ repeating-linear-gradient(
223
+ 0deg,
224
+ rgba(148, 163, 184, 0.03),
225
+ rgba(148, 163, 184, 0.03) 1px,
226
+ transparent 1px,
227
+ transparent 34px
228
+ ),
229
+ repeating-linear-gradient(
230
+ 90deg,
231
+ rgba(148, 163, 184, 0.03),
232
+ rgba(148, 163, 184, 0.03) 1px,
233
+ transparent 1px,
234
+ transparent 34px
235
+ );
236
+ }
237
+
238
+ .vc-canvas-stage__canvas {
239
+ width: 100%;
240
+ height: 100%;
241
+ }
@@ -0,0 +1,51 @@
1
+ import React, { useCallback, useState } from "react";
2
+ import InfoModal from "../InfoModal/InfoModal";
3
+ import type { InfoModalSection } from "../InfoModal/InfoModal";
4
+
5
+ export interface ConceptDefinition {
6
+ title: string;
7
+ subtitle: string;
8
+ accentColor: string;
9
+ sections: InfoModalSection[];
10
+ aside?: React.ReactNode;
11
+ }
12
+
13
+ /**
14
+ * Manages concept-modal state for a plugin.
15
+ *
16
+ * @example
17
+ * const { openConcept, ConceptModal } = useConceptModal(concepts);
18
+ *
19
+ * return (
20
+ * <>
21
+ * <ConceptPills pills={pills} onOpen={openConcept} />
22
+ * <ConceptModal />
23
+ * </>
24
+ * );
25
+ */
26
+ export function useConceptModal<K extends string>(
27
+ concepts: Record<K, ConceptDefinition>,
28
+ ) {
29
+ const [activeConcept, setActiveConcept] = useState<K | null>(null);
30
+
31
+ const openConcept = useCallback((key: K) => setActiveConcept(key), []);
32
+ const closeConcept = useCallback(() => setActiveConcept(null), []);
33
+
34
+ const ConceptModal: React.FC = () => {
35
+ if (!activeConcept) return null;
36
+ const c = concepts[activeConcept];
37
+ return (
38
+ <InfoModal
39
+ isOpen
40
+ onClose={closeConcept}
41
+ title={c.title}
42
+ subtitle={c.subtitle}
43
+ accentColor={c.accentColor}
44
+ sections={c.sections}
45
+ aside={c.aside}
46
+ />
47
+ );
48
+ };
49
+
50
+ return { activeConcept, openConcept, closeConcept, ConceptModal } as const;
51
+ }
@@ -0,0 +1,81 @@
1
+ @import "./components/plugin-kit/plugin-kit.scss";
2
+
3
+ :root {
4
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
5
+ line-height: 1.5;
6
+ font-weight: 400;
7
+
8
+ color-scheme: light dark;
9
+ color: rgba(255, 255, 255, 0.87);
10
+ background-color: #242424;
11
+
12
+ font-synthesis: none;
13
+ text-rendering: optimizeLegibility;
14
+ -webkit-font-smoothing: antialiased;
15
+ -moz-osx-font-smoothing: grayscale;
16
+ }
17
+
18
+ a {
19
+ font-weight: 500;
20
+ color: #646cff;
21
+ text-decoration: inherit;
22
+ }
23
+ a:hover {
24
+ color: #535bf2;
25
+ }
26
+
27
+ html,
28
+ body {
29
+ margin: 0;
30
+ padding: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ }
34
+
35
+ body {
36
+ min-height: 100vh;
37
+ }
38
+
39
+ #root {
40
+ width: 100%;
41
+ height: 100%;
42
+ display: flex;
43
+ flex-direction: column;
44
+ }
45
+
46
+ h1 {
47
+ font-size: 3.2em;
48
+ line-height: 1.1;
49
+ }
50
+
51
+ button {
52
+ border-radius: 8px;
53
+ border: 1px solid transparent;
54
+ padding: 0.6em 1.2em;
55
+ font-size: 1em;
56
+ font-weight: 500;
57
+ font-family: inherit;
58
+ background-color: #1a1a1a;
59
+ cursor: pointer;
60
+ transition: border-color 0.25s;
61
+ }
62
+ button:hover {
63
+ border-color: #646cff;
64
+ }
65
+ button:focus,
66
+ button:focus-visible {
67
+ outline: 4px auto -webkit-focus-ring-color;
68
+ }
69
+
70
+ @media (prefers-color-scheme: light) {
71
+ :root {
72
+ color: #213547;
73
+ background-color: #ffffff;
74
+ }
75
+ a:hover {
76
+ color: #747bff;
77
+ }
78
+ button {
79
+ background-color: #f9f9f9;
80
+ }
81
+ }
@@ -0,0 +1,14 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import { Provider } from 'react-redux'
4
+ import { store } from './store/store'
5
+ import './index.scss'
6
+ import App from './App.tsx'
7
+
8
+ createRoot(document.getElementById('root')!).render(
9
+ <StrictMode>
10
+ <Provider store={store}>
11
+ <App />
12
+ </Provider>
13
+ </StrictMode>,
14
+ )