veslx 0.1.2 → 0.1.4
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.
- package/bin/lib/build.ts +2 -0
- package/bin/lib/serve.ts +2 -0
- package/dist/client/App.js +17 -0
- package/dist/client/App.js.map +1 -0
- package/dist/client/components/front-matter.js +33 -0
- package/dist/client/components/front-matter.js.map +1 -0
- package/dist/client/components/gallery/components/figure-caption.js +14 -0
- package/dist/client/components/gallery/components/figure-caption.js.map +1 -0
- package/dist/client/components/gallery/components/figure-header.js +13 -0
- package/dist/client/components/gallery/components/figure-header.js.map +1 -0
- package/dist/client/components/gallery/components/lightbox.js +96 -0
- package/dist/client/components/gallery/components/lightbox.js.map +1 -0
- package/dist/client/components/gallery/components/loading-image.js +46 -0
- package/dist/client/components/gallery/components/loading-image.js.map +1 -0
- package/dist/client/components/gallery/hooks/use-gallery-images.js +84 -0
- package/dist/client/components/gallery/hooks/use-gallery-images.js.map +1 -0
- package/dist/client/components/gallery/hooks/use-lightbox.js +37 -0
- package/dist/client/components/gallery/hooks/use-lightbox.js.map +1 -0
- package/dist/client/components/gallery/index.js +96 -0
- package/dist/client/components/gallery/index.js.map +1 -0
- package/dist/client/components/gallery/lib/render-math-in-text.js +42 -0
- package/dist/client/components/gallery/lib/render-math-in-text.js.map +1 -0
- package/dist/client/components/header.js +60 -0
- package/dist/client/components/header.js.map +1 -0
- package/dist/client/components/loading.js +15 -0
- package/dist/client/components/loading.js.map +1 -0
- package/dist/client/components/mdx-components.js +134 -0
- package/dist/client/components/mdx-components.js.map +1 -0
- package/dist/client/components/mode-toggle.js +39 -0
- package/dist/client/components/mode-toggle.js.map +1 -0
- package/dist/client/components/page-error.js +39 -0
- package/dist/client/components/page-error.js.map +1 -0
- package/dist/client/components/parameter-badge.js +48 -0
- package/dist/client/components/parameter-badge.js.map +1 -0
- package/dist/client/components/parameter-table.js +301 -0
- package/dist/client/components/parameter-table.js.map +1 -0
- package/dist/client/components/post-list.js +119 -0
- package/dist/client/components/post-list.js.map +1 -0
- package/dist/client/components/running-bar.js +15 -0
- package/dist/client/components/running-bar.js.map +1 -0
- package/dist/client/components/slides-renderer.js +75 -0
- package/dist/client/components/slides-renderer.js.map +1 -0
- package/dist/client/components/theme-provider.js +9 -0
- package/dist/client/components/theme-provider.js.map +1 -0
- package/dist/client/components/ui/button.js +49 -0
- package/dist/client/components/ui/button.js.map +1 -0
- package/dist/client/components/ui/carousel.js +195 -0
- package/dist/client/components/ui/carousel.js.map +1 -0
- package/dist/client/components/ui/dropdown-menu.js +132 -0
- package/dist/client/components/ui/dropdown-menu.js.map +1 -0
- package/dist/client/hooks/use-key-bindings.js +40 -0
- package/dist/client/hooks/use-key-bindings.js.map +1 -0
- package/dist/client/hooks/use-mdx-content.js +78 -0
- package/dist/client/hooks/use-mdx-content.js.map +1 -0
- package/dist/client/lib/constants.js +5 -0
- package/dist/client/lib/constants.js.map +1 -0
- package/dist/client/lib/format-date.js +8 -0
- package/dist/client/lib/format-date.js.map +1 -0
- package/dist/client/lib/parameter-utils.js +110 -0
- package/dist/client/lib/parameter-utils.js.map +1 -0
- package/dist/client/lib/utils.js +9 -0
- package/dist/client/lib/utils.js.map +1 -0
- package/dist/client/logo_dark.png +0 -0
- package/dist/client/logo_light.png +0 -0
- package/dist/client/main.js +9 -0
- package/dist/client/main.js.map +1 -0
- package/dist/client/pages/home.js +30 -0
- package/dist/client/pages/home.js.map +1 -0
- package/dist/client/pages/post.js +53 -0
- package/dist/client/pages/post.js.map +1 -0
- package/dist/client/pages/slides.js +137 -0
- package/dist/client/pages/slides.js.map +1 -0
- package/dist/client/plugin/src/client.js +185 -0
- package/dist/client/plugin/src/client.js.map +1 -0
- package/package.json +8 -4
- package/src/main.tsx +1 -1
- package/vite.config.ts +22 -3
- package/vite.lib.config.ts +47 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useFileContent } from "../plugin/src/client.js";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
import { cn } from "../lib/utils.js";
|
|
5
|
+
import { parseConfigFile, extractPath, getValueType, formatValue } from "../lib/parameter-utils.js";
|
|
6
|
+
function filterData(data, keys) {
|
|
7
|
+
const result = {};
|
|
8
|
+
for (const keyPath of keys) {
|
|
9
|
+
const extracted = extractPath(data, keyPath);
|
|
10
|
+
if (extracted === void 0) continue;
|
|
11
|
+
const cleanPath = keyPath.startsWith(".") ? keyPath.slice(1) : keyPath;
|
|
12
|
+
let keyName;
|
|
13
|
+
if (cleanPath.includes("[")) {
|
|
14
|
+
keyName = cleanPath.replace(/\[\]/g, "").replace(/\[(\d+)\]/g, "_$1");
|
|
15
|
+
} else {
|
|
16
|
+
const parts = cleanPath.split(".");
|
|
17
|
+
keyName = parts[parts.length - 1];
|
|
18
|
+
}
|
|
19
|
+
result[keyName] = extracted;
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
function ParameterGrid({ entries }) {
|
|
24
|
+
if (entries.length === 0) return null;
|
|
25
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-[repeat(auto-fill,minmax(180px,1fr))] gap-x-6 gap-y-px", children: entries.map(([key, value]) => {
|
|
26
|
+
const type = getValueType(value);
|
|
27
|
+
return /* @__PURE__ */ jsxs(
|
|
28
|
+
"div",
|
|
29
|
+
{
|
|
30
|
+
className: "flex items-baseline justify-between gap-2 py-1 group hover:bg-muted/30 -mx-1.5 px-1.5 rounded-sm transition-colors",
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground font-mono truncate", children: key }),
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
"span",
|
|
35
|
+
{
|
|
36
|
+
className: cn(
|
|
37
|
+
"text-[11px] font-mono tabular-nums font-medium shrink-0",
|
|
38
|
+
type === "number" && "text-foreground",
|
|
39
|
+
type === "string" && "text-amber-600 dark:text-amber-500",
|
|
40
|
+
type === "boolean" && "text-cyan-600 dark:text-cyan-500",
|
|
41
|
+
type === "null" && "text-muted-foreground/50"
|
|
42
|
+
),
|
|
43
|
+
children: type === "string" ? `"${formatValue(value)}"` : formatValue(value)
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
key
|
|
49
|
+
);
|
|
50
|
+
}) });
|
|
51
|
+
}
|
|
52
|
+
function ParameterSection({
|
|
53
|
+
name,
|
|
54
|
+
data,
|
|
55
|
+
depth = 0
|
|
56
|
+
}) {
|
|
57
|
+
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
58
|
+
const entries = Object.entries(data);
|
|
59
|
+
const leafEntries = entries.filter(([, v]) => {
|
|
60
|
+
const t = getValueType(v);
|
|
61
|
+
return t !== "object" && t !== "array";
|
|
62
|
+
});
|
|
63
|
+
const nestedEntries = entries.filter(([, v]) => {
|
|
64
|
+
const t = getValueType(v);
|
|
65
|
+
return t === "object" || t === "array";
|
|
66
|
+
});
|
|
67
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(depth === 0 && "mb-4 last:mb-0"), children: [
|
|
68
|
+
/* @__PURE__ */ jsxs(
|
|
69
|
+
"button",
|
|
70
|
+
{
|
|
71
|
+
onClick: () => setIsCollapsed(!isCollapsed),
|
|
72
|
+
className: cn(
|
|
73
|
+
"flex items-center gap-2 w-full text-left group mb-1.5",
|
|
74
|
+
depth === 0 && "pb-1 border-b border-border/50"
|
|
75
|
+
),
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ jsx("span", { className: cn(
|
|
78
|
+
"text-[10px] text-muted-foreground/60 transition-transform duration-150 select-none",
|
|
79
|
+
isCollapsed && "-rotate-90"
|
|
80
|
+
), children: isCollapsed ? "+" : "-" }),
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: cn(
|
|
82
|
+
"font-mono text-[11px] uppercase tracking-widest",
|
|
83
|
+
depth === 0 ? "text-foreground/80 font-semibold" : "text-muted-foreground/70"
|
|
84
|
+
), children: name.replace(/_/g, " ") }),
|
|
85
|
+
/* @__PURE__ */ jsx("span", { className: "text-[9px] font-mono text-muted-foreground/40 ml-auto", children: entries.length })
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
),
|
|
89
|
+
!isCollapsed && /* @__PURE__ */ jsxs("div", { className: cn(
|
|
90
|
+
depth > 0 && "pl-3 ml-1 border-l border-border/40"
|
|
91
|
+
), children: [
|
|
92
|
+
leafEntries.length > 0 && /* @__PURE__ */ jsx("div", { className: cn(nestedEntries.length > 0 && "mb-3"), children: /* @__PURE__ */ jsx(ParameterGrid, { entries: leafEntries }) }),
|
|
93
|
+
nestedEntries.map(([key, value]) => {
|
|
94
|
+
const type = getValueType(value);
|
|
95
|
+
if (type === "array") {
|
|
96
|
+
const arr = value;
|
|
97
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-2 last:mb-0", children: [
|
|
98
|
+
/* @__PURE__ */ jsxs("div", { className: "text-[10px] font-mono text-muted-foreground/60 uppercase tracking-wider mb-1", children: [
|
|
99
|
+
key,
|
|
100
|
+
" [",
|
|
101
|
+
arr.length,
|
|
102
|
+
"]"
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsx("div", { className: "pl-3 ml-1 border-l border-border/40", children: arr.map((item, i) => {
|
|
105
|
+
const itemType = getValueType(item);
|
|
106
|
+
if (itemType === "object") {
|
|
107
|
+
return /* @__PURE__ */ jsx(
|
|
108
|
+
ParameterSection,
|
|
109
|
+
{
|
|
110
|
+
name: `${i}`,
|
|
111
|
+
data: item,
|
|
112
|
+
depth: depth + 1
|
|
113
|
+
},
|
|
114
|
+
i
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return /* @__PURE__ */ jsxs("div", { className: "text-[11px] font-mono text-foreground py-0.5", children: [
|
|
118
|
+
"[",
|
|
119
|
+
i,
|
|
120
|
+
"] ",
|
|
121
|
+
formatValue(item)
|
|
122
|
+
] }, i);
|
|
123
|
+
}) })
|
|
124
|
+
] }, key);
|
|
125
|
+
}
|
|
126
|
+
return /* @__PURE__ */ jsx(
|
|
127
|
+
ParameterSection,
|
|
128
|
+
{
|
|
129
|
+
name: key,
|
|
130
|
+
data: value,
|
|
131
|
+
depth: depth + 1
|
|
132
|
+
},
|
|
133
|
+
key
|
|
134
|
+
);
|
|
135
|
+
})
|
|
136
|
+
] })
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
139
|
+
function estimateHeight(data, depth = 0) {
|
|
140
|
+
const entries = Object.entries(data);
|
|
141
|
+
let height = 0;
|
|
142
|
+
for (const [, value] of entries) {
|
|
143
|
+
const type = getValueType(value);
|
|
144
|
+
if (type === "object") {
|
|
145
|
+
height += 28 + estimateHeight(value, depth + 1);
|
|
146
|
+
} else if (type === "array") {
|
|
147
|
+
const arr = value;
|
|
148
|
+
height += 28;
|
|
149
|
+
for (const item of arr) {
|
|
150
|
+
if (getValueType(item) === "object") {
|
|
151
|
+
height += 24 + estimateHeight(item, depth + 1);
|
|
152
|
+
} else {
|
|
153
|
+
height += 24;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
height += 24;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return height;
|
|
161
|
+
}
|
|
162
|
+
function splitIntoColumns(entries, numColumns) {
|
|
163
|
+
if (numColumns <= 1) return [entries];
|
|
164
|
+
const entryHeights = entries.map(([, value]) => {
|
|
165
|
+
const type = getValueType(value);
|
|
166
|
+
if (type === "object") {
|
|
167
|
+
return 28 + estimateHeight(value);
|
|
168
|
+
} else if (type === "array") {
|
|
169
|
+
const arr = value;
|
|
170
|
+
let h = 28;
|
|
171
|
+
for (const item of arr) {
|
|
172
|
+
if (getValueType(item) === "object") {
|
|
173
|
+
h += 24 + estimateHeight(item);
|
|
174
|
+
} else {
|
|
175
|
+
h += 24;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return h;
|
|
179
|
+
}
|
|
180
|
+
return 24;
|
|
181
|
+
});
|
|
182
|
+
const totalHeight = entryHeights.reduce((a, b) => a + b, 0);
|
|
183
|
+
const targetPerColumn = totalHeight / numColumns;
|
|
184
|
+
const columns = [];
|
|
185
|
+
let currentColumn = [];
|
|
186
|
+
let currentHeight = 0;
|
|
187
|
+
for (let i = 0; i < entries.length; i++) {
|
|
188
|
+
const entry = entries[i];
|
|
189
|
+
const entryHeight = entryHeights[i];
|
|
190
|
+
if (currentHeight >= targetPerColumn && columns.length < numColumns - 1 && currentColumn.length > 0) {
|
|
191
|
+
columns.push(currentColumn);
|
|
192
|
+
currentColumn = [];
|
|
193
|
+
currentHeight = 0;
|
|
194
|
+
}
|
|
195
|
+
currentColumn.push(entry);
|
|
196
|
+
currentHeight += entryHeight;
|
|
197
|
+
}
|
|
198
|
+
if (currentColumn.length > 0) {
|
|
199
|
+
columns.push(currentColumn);
|
|
200
|
+
}
|
|
201
|
+
return columns;
|
|
202
|
+
}
|
|
203
|
+
function ParameterTable({ path, keys }) {
|
|
204
|
+
const { content, loading, error } = useFileContent(path);
|
|
205
|
+
const parsed = useMemo(() => {
|
|
206
|
+
if (!content) return null;
|
|
207
|
+
const data = parseConfigFile(content, path);
|
|
208
|
+
if (!data) return null;
|
|
209
|
+
if (keys && keys.length > 0) {
|
|
210
|
+
return filterData(data, keys);
|
|
211
|
+
}
|
|
212
|
+
return data;
|
|
213
|
+
}, [content, path, keys]);
|
|
214
|
+
if (loading) {
|
|
215
|
+
return /* @__PURE__ */ jsx("div", { className: "my-6 p-4 rounded border border-border/50 bg-card/30", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground/60", children: [
|
|
216
|
+
/* @__PURE__ */ jsx("div", { className: "w-3 h-3 border border-current border-t-transparent rounded-full animate-spin" }),
|
|
217
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-mono", children: "loading parameters..." })
|
|
218
|
+
] }) });
|
|
219
|
+
}
|
|
220
|
+
if (error) {
|
|
221
|
+
return /* @__PURE__ */ jsx("div", { className: "my-6 p-3 rounded border border-destructive/30 bg-destructive/5", children: /* @__PURE__ */ jsx("p", { className: "text-[11px] font-mono text-destructive", children: error }) });
|
|
222
|
+
}
|
|
223
|
+
if (!parsed) {
|
|
224
|
+
return /* @__PURE__ */ jsx("div", { className: "my-6 p-3 rounded border border-border/50 bg-card/30", children: /* @__PURE__ */ jsx("p", { className: "text-[11px] font-mono text-muted-foreground", children: "unable to parse config" }) });
|
|
225
|
+
}
|
|
226
|
+
const entries = Object.entries(parsed);
|
|
227
|
+
const topLeaves = entries.filter(([, v]) => {
|
|
228
|
+
const t = getValueType(v);
|
|
229
|
+
return t !== "object" && t !== "array";
|
|
230
|
+
});
|
|
231
|
+
const topNested = entries.filter(([, v]) => {
|
|
232
|
+
const t = getValueType(v);
|
|
233
|
+
return t === "object" || t === "array";
|
|
234
|
+
});
|
|
235
|
+
const estHeight = estimateHeight(parsed);
|
|
236
|
+
const HEIGHT_THRESHOLD = 500;
|
|
237
|
+
const numColumns = estHeight > HEIGHT_THRESHOLD ? Math.min(Math.ceil(estHeight / HEIGHT_THRESHOLD), 3) : 1;
|
|
238
|
+
const useColumns = numColumns > 1 && topNested.length > 1;
|
|
239
|
+
const columns = useColumns ? splitIntoColumns(topNested, numColumns) : [topNested];
|
|
240
|
+
path.split("/").pop() || path;
|
|
241
|
+
const renderNestedEntry = ([key, value]) => {
|
|
242
|
+
const type = getValueType(value);
|
|
243
|
+
if (type === "array") {
|
|
244
|
+
const arr = value;
|
|
245
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-4 last:mb-0", children: [
|
|
246
|
+
/* @__PURE__ */ jsxs("div", { className: "text-[11px] font-mono text-foreground/80 uppercase tracking-widest font-semibold mb-1.5 pb-1 border-b border-border/50", children: [
|
|
247
|
+
key.replace(/_/g, " "),
|
|
248
|
+
" [",
|
|
249
|
+
arr.length,
|
|
250
|
+
"]"
|
|
251
|
+
] }),
|
|
252
|
+
/* @__PURE__ */ jsx("div", { className: "pl-3 ml-1 border-l border-border/40", children: arr.map((item, i) => {
|
|
253
|
+
const itemType = getValueType(item);
|
|
254
|
+
if (itemType === "object") {
|
|
255
|
+
return /* @__PURE__ */ jsx(
|
|
256
|
+
ParameterSection,
|
|
257
|
+
{
|
|
258
|
+
name: `${i}`,
|
|
259
|
+
data: item,
|
|
260
|
+
depth: 1
|
|
261
|
+
},
|
|
262
|
+
i
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
return /* @__PURE__ */ jsxs("div", { className: "text-[11px] font-mono text-foreground py-0.5", children: [
|
|
266
|
+
"[",
|
|
267
|
+
i,
|
|
268
|
+
"] ",
|
|
269
|
+
formatValue(item)
|
|
270
|
+
] }, i);
|
|
271
|
+
}) })
|
|
272
|
+
] }, key);
|
|
273
|
+
}
|
|
274
|
+
return /* @__PURE__ */ jsx(
|
|
275
|
+
ParameterSection,
|
|
276
|
+
{
|
|
277
|
+
name: key,
|
|
278
|
+
data: value,
|
|
279
|
+
depth: 0
|
|
280
|
+
},
|
|
281
|
+
key
|
|
282
|
+
);
|
|
283
|
+
};
|
|
284
|
+
return /* @__PURE__ */ jsx("div", { className: "my-6 not-prose", children: /* @__PURE__ */ jsxs("div", { className: "rounded border border-border/60 bg-card/20 p-3 overflow-hidden", children: [
|
|
285
|
+
topLeaves.length > 0 && /* @__PURE__ */ jsx("div", { className: cn(topNested.length > 0 && "mb-4 pb-3 border-b border-border/30"), children: /* @__PURE__ */ jsx(ParameterGrid, { entries: topLeaves }) }),
|
|
286
|
+
useColumns ? /* @__PURE__ */ jsx(
|
|
287
|
+
"div",
|
|
288
|
+
{
|
|
289
|
+
className: "grid gap-6",
|
|
290
|
+
style: { gridTemplateColumns: `repeat(${columns.length}, 1fr)` },
|
|
291
|
+
children: columns.map((columnEntries, colIndex) => /* @__PURE__ */ jsx("div", { className: cn(
|
|
292
|
+
colIndex > 0 && "border-l border-border/30 pl-6"
|
|
293
|
+
), children: columnEntries.map(renderNestedEntry) }, colIndex))
|
|
294
|
+
}
|
|
295
|
+
) : topNested.map(renderNestedEntry)
|
|
296
|
+
] }) });
|
|
297
|
+
}
|
|
298
|
+
export {
|
|
299
|
+
ParameterTable
|
|
300
|
+
};
|
|
301
|
+
//# sourceMappingURL=parameter-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parameter-table.js","sources":["../../../src/components/parameter-table.tsx"],"sourcesContent":["import { useFileContent } from \"../../plugin/src/client\";\nimport { useMemo, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n type ParameterValue,\n extractPath,\n getValueType,\n formatValue,\n parseConfigFile,\n} from \"@/lib/parameter-utils\";\n\n/**\n * Build a filtered data object from an array of jq-like paths.\n * Each path extracts data and places it in the result under the final key name.\n */\nfunction filterData(\n data: Record<string, ParameterValue>,\n keys: string[]\n): Record<string, ParameterValue> {\n const result: Record<string, ParameterValue> = {};\n\n for (const keyPath of keys) {\n const extracted = extractPath(data, keyPath);\n if (extracted === undefined) continue;\n\n const cleanPath = keyPath.startsWith(\".\") ? keyPath.slice(1) : keyPath;\n\n // For simple paths like .base.N_E, use \"N_E\" as key\n // For paths with [], preserve more context\n let keyName: string;\n if (cleanPath.includes(\"[\")) {\n keyName = cleanPath.replace(/\\[\\]/g, \"\").replace(/\\[(\\d+)\\]/g, \"_$1\");\n } else {\n const parts = cleanPath.split(\".\");\n keyName = parts[parts.length - 1];\n }\n\n result[keyName] = extracted;\n }\n\n return result;\n}\n\n// Renders a flat section of key-value pairs in a dense grid\nfunction ParameterGrid({ entries }: { entries: [string, ParameterValue][] }) {\n if (entries.length === 0) return null;\n\n return (\n <div className=\"grid grid-cols-[repeat(auto-fill,minmax(180px,1fr))] gap-x-6 gap-y-px\">\n {entries.map(([key, value]) => {\n const type = getValueType(value);\n return (\n <div\n key={key}\n className=\"flex items-baseline justify-between gap-2 py-1 group hover:bg-muted/30 -mx-1.5 px-1.5 rounded-sm transition-colors\"\n >\n <span className=\"text-[11px] text-muted-foreground font-mono truncate\">\n {key}\n </span>\n <span\n className={cn(\n \"text-[11px] font-mono tabular-nums font-medium shrink-0\",\n type === \"number\" && \"text-foreground\",\n type === \"string\" && \"text-amber-600 dark:text-amber-500\",\n type === \"boolean\" && \"text-cyan-600 dark:text-cyan-500\",\n type === \"null\" && \"text-muted-foreground/50\"\n )}\n >\n {type === \"string\" ? `\"${formatValue(value)}\"` : formatValue(value)}\n </span>\n </div>\n );\n })}\n </div>\n );\n}\n\n// Renders a nested section with its own header\nfunction ParameterSection({\n name,\n data,\n depth = 0\n}: {\n name: string;\n data: Record<string, ParameterValue>;\n depth?: number;\n}) {\n const [isCollapsed, setIsCollapsed] = useState(false);\n\n const entries = Object.entries(data);\n const leafEntries = entries.filter(([, v]) => {\n const t = getValueType(v);\n return t !== \"object\" && t !== \"array\";\n });\n const nestedEntries = entries.filter(([, v]) => {\n const t = getValueType(v);\n return t === \"object\" || t === \"array\";\n });\n\n return (\n <div className={cn(depth === 0 && \"mb-4 last:mb-0\")}>\n <button\n onClick={() => setIsCollapsed(!isCollapsed)}\n className={cn(\n \"flex items-center gap-2 w-full text-left group mb-1.5\",\n depth === 0 && \"pb-1 border-b border-border/50\"\n )}\n >\n <span className={cn(\n \"text-[10px] text-muted-foreground/60 transition-transform duration-150 select-none\",\n isCollapsed && \"-rotate-90\"\n )}>\n {isCollapsed ? \"+\" : \"-\"}\n </span>\n\n <span className={cn(\n \"font-mono text-[11px] uppercase tracking-widest\",\n depth === 0\n ? \"text-foreground/80 font-semibold\"\n : \"text-muted-foreground/70\"\n )}>\n {name.replace(/_/g, \" \")}\n </span>\n\n <span className=\"text-[9px] font-mono text-muted-foreground/40 ml-auto\">\n {entries.length}\n </span>\n </button>\n\n {!isCollapsed && (\n <div className={cn(\n depth > 0 && \"pl-3 ml-1 border-l border-border/40\"\n )}>\n {leafEntries.length > 0 && (\n <div className={cn(nestedEntries.length > 0 && \"mb-3\")}>\n <ParameterGrid entries={leafEntries} />\n </div>\n )}\n\n {nestedEntries.map(([key, value]) => {\n const type = getValueType(value);\n if (type === \"array\") {\n const arr = value as ParameterValue[];\n return (\n <div key={key} className=\"mb-2 last:mb-0\">\n <div className=\"text-[10px] font-mono text-muted-foreground/60 uppercase tracking-wider mb-1\">\n {key} [{arr.length}]\n </div>\n <div className=\"pl-3 ml-1 border-l border-border/40\">\n {arr.map((item, i) => {\n const itemType = getValueType(item);\n if (itemType === \"object\") {\n return (\n <ParameterSection\n key={i}\n name={`${i}`}\n data={item as Record<string, ParameterValue>}\n depth={depth + 1}\n />\n );\n }\n return (\n <div key={i} className=\"text-[11px] font-mono text-foreground py-0.5\">\n [{i}] {formatValue(item)}\n </div>\n );\n })}\n </div>\n </div>\n );\n }\n return (\n <ParameterSection\n key={key}\n name={key}\n data={value as Record<string, ParameterValue>}\n depth={depth + 1}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\ninterface ParameterTableProps {\n /** Path to the YAML or JSON file */\n path: string;\n /**\n * Optional array of jq-like paths to filter which parameters to show.\n * Examples:\n * - [\".base.N_E\", \".base.N_I\"] → show only N_E and N_I from base\n * - [\".base\"] → show entire base section\n * - [\".default_inputs\", \".base.dt\"] → show default_inputs section and dt from base\n */\n keys?: string[];\n}\n\n/**\n * Estimate the height contribution of a data structure.\n */\nfunction estimateHeight(data: Record<string, ParameterValue>, depth = 0): number {\n const entries = Object.entries(data);\n let height = 0;\n\n for (const [, value] of entries) {\n const type = getValueType(value);\n if (type === \"object\") {\n height += 28 + estimateHeight(value as Record<string, ParameterValue>, depth + 1);\n } else if (type === \"array\") {\n const arr = value as ParameterValue[];\n height += 28;\n for (const item of arr) {\n if (getValueType(item) === \"object\") {\n height += 24 + estimateHeight(item as Record<string, ParameterValue>, depth + 1);\n } else {\n height += 24;\n }\n }\n } else {\n height += 24;\n }\n }\n\n return height;\n}\n\n/**\n * Split entries into balanced columns based on estimated height.\n */\nfunction splitIntoColumns<T extends [string, ParameterValue]>(\n entries: T[],\n numColumns: number\n): T[][] {\n if (numColumns <= 1) return [entries];\n\n const entryHeights = entries.map(([, value]) => {\n const type = getValueType(value);\n if (type === \"object\") {\n return 28 + estimateHeight(value as Record<string, ParameterValue>);\n } else if (type === \"array\") {\n const arr = value as ParameterValue[];\n let h = 28;\n for (const item of arr) {\n if (getValueType(item) === \"object\") {\n h += 24 + estimateHeight(item as Record<string, ParameterValue>);\n } else {\n h += 24;\n }\n }\n return h;\n }\n return 24;\n });\n\n const totalHeight = entryHeights.reduce((a, b) => a + b, 0);\n const targetPerColumn = totalHeight / numColumns;\n\n const columns: T[][] = [];\n let currentColumn: T[] = [];\n let currentHeight = 0;\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const entryHeight = entryHeights[i];\n\n if (currentHeight >= targetPerColumn && columns.length < numColumns - 1 && currentColumn.length > 0) {\n columns.push(currentColumn);\n currentColumn = [];\n currentHeight = 0;\n }\n\n currentColumn.push(entry);\n currentHeight += entryHeight;\n }\n\n if (currentColumn.length > 0) {\n columns.push(currentColumn);\n }\n\n return columns;\n}\n\nexport function ParameterTable({ path, keys }: ParameterTableProps) {\n const { content, loading, error } = useFileContent(path);\n\n const parsed = useMemo(() => {\n if (!content) return null;\n\n const data = parseConfigFile(content, path);\n if (!data) return null;\n\n if (keys && keys.length > 0) {\n return filterData(data, keys);\n }\n\n return data;\n }, [content, path, keys]);\n\n if (loading) {\n return (\n <div className=\"my-6 p-4 rounded border border-border/50 bg-card/30\">\n <div className=\"flex items-center gap-2 text-muted-foreground/60\">\n <div className=\"w-3 h-3 border border-current border-t-transparent rounded-full animate-spin\" />\n <span className=\"text-[11px] font-mono\">loading parameters...</span>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"my-6 p-3 rounded border border-destructive/30 bg-destructive/5\">\n <p className=\"text-[11px] font-mono text-destructive\">{error}</p>\n </div>\n );\n }\n\n if (!parsed) {\n return (\n <div className=\"my-6 p-3 rounded border border-border/50 bg-card/30\">\n <p className=\"text-[11px] font-mono text-muted-foreground\">unable to parse config</p>\n </div>\n );\n }\n\n const entries = Object.entries(parsed);\n\n const topLeaves = entries.filter(([, v]) => {\n const t = getValueType(v);\n return t !== \"object\" && t !== \"array\";\n });\n const topNested = entries.filter(([, v]) => {\n const t = getValueType(v);\n return t === \"object\" || t === \"array\";\n });\n\n const estHeight = estimateHeight(parsed);\n const HEIGHT_THRESHOLD = 500;\n const numColumns = estHeight > HEIGHT_THRESHOLD ? Math.min(Math.ceil(estHeight / HEIGHT_THRESHOLD), 3) : 1;\n const useColumns = numColumns > 1 && topNested.length > 1;\n\n const columns = useColumns\n ? splitIntoColumns(topNested as [string, ParameterValue][], numColumns)\n : [topNested];\n\n const filename = path.split(\"/\").pop() || path;\n\n const renderNestedEntry = ([key, value]: [string, ParameterValue]) => {\n const type = getValueType(value);\n if (type === \"array\") {\n const arr = value as ParameterValue[];\n return (\n <div key={key} className=\"mb-4 last:mb-0\">\n <div className=\"text-[11px] font-mono text-foreground/80 uppercase tracking-widest font-semibold mb-1.5 pb-1 border-b border-border/50\">\n {key.replace(/_/g, \" \")} [{arr.length}]\n </div>\n <div className=\"pl-3 ml-1 border-l border-border/40\">\n {arr.map((item, i) => {\n const itemType = getValueType(item);\n if (itemType === \"object\") {\n return (\n <ParameterSection\n key={i}\n name={`${i}`}\n data={item as Record<string, ParameterValue>}\n depth={1}\n />\n );\n }\n return (\n <div key={i} className=\"text-[11px] font-mono text-foreground py-0.5\">\n [{i}] {formatValue(item)}\n </div>\n );\n })}\n </div>\n </div>\n );\n }\n return (\n <ParameterSection\n key={key}\n name={key}\n data={value as Record<string, ParameterValue>}\n depth={0}\n />\n );\n };\n\n return (\n <div className=\"my-6 not-prose\">\n <div className=\"rounded border border-border/60 bg-card/20 p-3 overflow-hidden\">\n {topLeaves.length > 0 && (\n <div className={cn(topNested.length > 0 && \"mb-4 pb-3 border-b border-border/30\")}>\n <ParameterGrid entries={topLeaves} />\n </div>\n )}\n\n {useColumns ? (\n <div\n className=\"grid gap-6\"\n style={{ gridTemplateColumns: `repeat(${columns.length}, 1fr)` }}\n >\n {columns.map((columnEntries, colIndex) => (\n <div key={colIndex} className={cn(\n colIndex > 0 && \"border-l border-border/30 pl-6\"\n )}>\n {columnEntries.map(renderNestedEntry)}\n </div>\n ))}\n </div>\n ) : (\n topNested.map(renderNestedEntry)\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Link } from "react-router-dom";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import { findReadme, findSlides } from "../plugin/src/client.js";
|
|
5
|
+
import { formatDate } from "../lib/format-date.js";
|
|
6
|
+
import { ArrowRight } from "lucide-react";
|
|
7
|
+
function PostList({ directory }) {
|
|
8
|
+
const folders = directory.children.filter((c) => c.type === "directory");
|
|
9
|
+
if (folders.length === 0) {
|
|
10
|
+
return /* @__PURE__ */ jsx("div", { className: "py-24 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground font-mono text-sm tracking-wide", children: "no entries" }) });
|
|
11
|
+
}
|
|
12
|
+
let posts = folders.map((folder) => {
|
|
13
|
+
const readme = findReadme(folder);
|
|
14
|
+
const slides = findSlides(folder);
|
|
15
|
+
return {
|
|
16
|
+
...folder,
|
|
17
|
+
readme,
|
|
18
|
+
slides
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
posts = posts.filter((post) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
return ((_b = (_a = post.readme) == null ? void 0 : _a.frontmatter) == null ? void 0 : _b.visibility) !== "hidden";
|
|
24
|
+
});
|
|
25
|
+
posts = posts.sort((a, b) => {
|
|
26
|
+
var _a, _b, _c, _d, _e, _f;
|
|
27
|
+
let aDate = ((_b = (_a = a.readme) == null ? void 0 : _a.frontmatter) == null ? void 0 : _b.date) ? new Date(a.readme.frontmatter.date) : null;
|
|
28
|
+
let bDate = ((_d = (_c = b.readme) == null ? void 0 : _c.frontmatter) == null ? void 0 : _d.date) ? new Date(b.readme.frontmatter.date) : null;
|
|
29
|
+
if (!aDate && a.slides) {
|
|
30
|
+
aDate = ((_e = a.slides.frontmatter) == null ? void 0 : _e.date) ? new Date(a.slides.frontmatter.date) : null;
|
|
31
|
+
}
|
|
32
|
+
if (!bDate && b.slides) {
|
|
33
|
+
bDate = ((_f = b.slides.frontmatter) == null ? void 0 : _f.date) ? new Date(b.slides.frontmatter.date) : null;
|
|
34
|
+
}
|
|
35
|
+
if (aDate && bDate) {
|
|
36
|
+
return bDate.getTime() - aDate.getTime();
|
|
37
|
+
} else if (aDate) {
|
|
38
|
+
return -1;
|
|
39
|
+
} else if (bDate) {
|
|
40
|
+
return 1;
|
|
41
|
+
} else {
|
|
42
|
+
return a.name.localeCompare(b.name);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const postsGroupedByMonthAndYear = {};
|
|
46
|
+
posts.forEach((post) => {
|
|
47
|
+
var _a, _b, _c;
|
|
48
|
+
let date = ((_b = (_a = post.readme) == null ? void 0 : _a.frontmatter) == null ? void 0 : _b.date) ? new Date(post.readme.frontmatter.date) : null;
|
|
49
|
+
if (!date && post.slides) {
|
|
50
|
+
date = ((_c = post.slides.frontmatter) == null ? void 0 : _c.date) ? new Date(post.slides.frontmatter.date) : null;
|
|
51
|
+
}
|
|
52
|
+
const monthYear = date ? `${date.getFullYear()}-${date.getMonth() + 1}` : "unknown";
|
|
53
|
+
if (!postsGroupedByMonthAndYear[monthYear]) {
|
|
54
|
+
postsGroupedByMonthAndYear[monthYear] = [];
|
|
55
|
+
}
|
|
56
|
+
postsGroupedByMonthAndYear[monthYear].push(post);
|
|
57
|
+
});
|
|
58
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-8", children: Object.entries(postsGroupedByMonthAndYear).sort(([a], [b]) => {
|
|
59
|
+
if (a === "unknown") return 1;
|
|
60
|
+
if (b === "unknown") return -1;
|
|
61
|
+
return b.localeCompare(a);
|
|
62
|
+
}).map(([monthYear, monthPosts]) => {
|
|
63
|
+
const [year, month] = monthYear.split("-");
|
|
64
|
+
const displayDate = monthYear === "unknown" ? "Unknown Date" : new Date(parseInt(year), parseInt(month) - 1).toLocaleDateString("en-US", {
|
|
65
|
+
year: "numeric",
|
|
66
|
+
month: "long"
|
|
67
|
+
});
|
|
68
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
69
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xs font-mono uppercase tracking-wider text-muted-foreground mb-3", children: displayDate }),
|
|
70
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: monthPosts.map((post) => {
|
|
71
|
+
var _a;
|
|
72
|
+
let frontmatter = (_a = post.readme) == null ? void 0 : _a.frontmatter;
|
|
73
|
+
if (!post.readme && post.slides) {
|
|
74
|
+
frontmatter = post.slides.frontmatter;
|
|
75
|
+
}
|
|
76
|
+
const title = (frontmatter == null ? void 0 : frontmatter.title) || post.name;
|
|
77
|
+
const description = frontmatter == null ? void 0 : frontmatter.description;
|
|
78
|
+
const date = (frontmatter == null ? void 0 : frontmatter.date) ? new Date(frontmatter.date) : null;
|
|
79
|
+
return /* @__PURE__ */ jsx(
|
|
80
|
+
Link,
|
|
81
|
+
{
|
|
82
|
+
to: post.slides && !post.readme ? `/${post.slides.path}` : `/${post.readme.path}`,
|
|
83
|
+
className: cn(
|
|
84
|
+
"group block py-3 px-3 -mx-3 rounded-md",
|
|
85
|
+
"transition-colors duration-150"
|
|
86
|
+
// "hover:bg-accent"
|
|
87
|
+
),
|
|
88
|
+
children: /* @__PURE__ */ jsxs("article", { className: "flex items-start gap-4", children: [
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
"time",
|
|
91
|
+
{
|
|
92
|
+
dateTime: date == null ? void 0 : date.toISOString(),
|
|
93
|
+
className: "font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0 pt-0.5",
|
|
94
|
+
children: date ? formatDate(date) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/30", children: "—" })
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
98
|
+
/* @__PURE__ */ jsxs("h3", { className: cn(
|
|
99
|
+
"text-sm font-medium text-foreground",
|
|
100
|
+
"group-hover:underline",
|
|
101
|
+
"flex items-center gap-2"
|
|
102
|
+
), children: [
|
|
103
|
+
/* @__PURE__ */ jsx("span", { children: title }),
|
|
104
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "h-3 w-3 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200 text-primary" })
|
|
105
|
+
] }),
|
|
106
|
+
description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground line-clamp-1 mt-0.5", children: description })
|
|
107
|
+
] })
|
|
108
|
+
] })
|
|
109
|
+
},
|
|
110
|
+
post.path
|
|
111
|
+
);
|
|
112
|
+
}) })
|
|
113
|
+
] }, monthYear);
|
|
114
|
+
}) });
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
PostList as default
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=post-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-list.js","sources":["../../../src/components/post-list.tsx"],"sourcesContent":["import { Link } from \"react-router-dom\";\nimport { cn } from \"@/lib/utils\";\nimport { DirectoryEntry } from \"../../plugin/src/lib\";\nimport { findReadme, findSlides } from \"../../plugin/src/client\";\nimport { formatDate } from \"@/lib/format-date\";\nimport { ArrowRight } from \"lucide-react\";\n\nexport default function PostList({ directory }: { directory: DirectoryEntry }) {\n const folders = directory.children.filter((c): c is DirectoryEntry => c.type === \"directory\");\n\n if (folders.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n let posts = folders.map((folder) => {\n const readme = findReadme(folder);\n const slides = findSlides(folder);\n return {\n ...folder,\n readme,\n slides,\n }\n })\n\n posts = posts.filter((post) => {\n return post.readme?.frontmatter?.visibility !== \"hidden\";\n });\n\n posts = posts.sort((a, b) => {\n let aDate = a.readme?.frontmatter?.date ? new Date(a.readme.frontmatter.date as string) : null;\n let bDate = b.readme?.frontmatter?.date ? new Date(b.readme.frontmatter.date as string) : null;\n\n if (!aDate && a.slides) {\n aDate = a.slides.frontmatter?.date ? new Date(a.slides.frontmatter.date as string) : null;\n }\n if (!bDate && b.slides) {\n bDate = b.slides.frontmatter?.date ? new Date(b.slides.frontmatter.date as string) : null;\n }\n\n if (aDate && bDate) {\n return bDate.getTime() - aDate.getTime();\n } else if (aDate) {\n return -1;\n } else if (bDate) {\n return 1;\n } else {\n return a.name.localeCompare(b.name);\n }\n });\n\n const postsGroupedByMonthAndYear: { [key: string]: typeof posts } = {};\n posts.forEach((post) => {\n let date = post.readme?.frontmatter?.date ? new Date(post.readme.frontmatter.date as string) : null;\n if (!date && post.slides) {\n date = post.slides.frontmatter?.date ? new Date(post.slides.frontmatter.date as string) : null;\n }\n const monthYear = date ? `${date.getFullYear()}-${date.getMonth() + 1}` : \"unknown\";\n if (!postsGroupedByMonthAndYear[monthYear]) {\n postsGroupedByMonthAndYear[monthYear] = [];\n }\n postsGroupedByMonthAndYear[monthYear].push(post);\n });\n\n return (\n <div className=\"space-y-8\">\n {Object.entries(postsGroupedByMonthAndYear)\n .sort(([a], [b]) => {\n if (a === \"unknown\") return 1;\n if (b === \"unknown\") return -1;\n return b.localeCompare(a);\n })\n .map(([monthYear, monthPosts]) => {\n const [year, month] = monthYear.split(\"-\");\n const displayDate = monthYear === \"unknown\" \n ? \"Unknown Date\" \n : new Date(parseInt(year), parseInt(month) - 1).toLocaleDateString(\"en-US\", { \n year: \"numeric\", \n month: \"long\" \n });\n\n return (\n <div key={monthYear}>\n <h2 className=\"text-xs font-mono uppercase tracking-wider text-muted-foreground mb-3\">\n {displayDate}\n </h2>\n <div className=\"space-y-1\">\n {monthPosts.map((post) => {\n let frontmatter = post.readme?.frontmatter;\n\n if (!post.readme && post.slides) {\n frontmatter = post.slides.frontmatter;\n }\n\n const title = (frontmatter?.title as string) || post.name;\n const description = frontmatter?.description as string | undefined;\n const date = frontmatter?.date ? new Date(frontmatter.date as string) : null;\n\n return (\n <Link\n key={post.path}\n to={(post.slides && !post.readme) ? `/${post.slides.path}` : `/${post.readme.path}`}\n className={cn(\n \"group block py-3 px-3 -mx-3 rounded-md\",\n \"transition-colors duration-150\",\n // \"hover:bg-accent\"\n )}\n >\n <article className=\"flex items-start gap-4\">\n {/* Date - left side, fixed width */}\n <time\n dateTime={date?.toISOString()}\n className=\"font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0 pt-0.5\"\n >\n {date ? formatDate(date) : <span className=\"text-muted-foreground/30\">—</span>}\n </time>\n\n {/* Main content */}\n <div className=\"flex-1 min-w-0\">\n <h3 className={cn(\n \"text-sm font-medium text-foreground\",\n \"group-hover:underline\",\n \"flex items-center gap-2\"\n )}>\n <span>{title}</span>\n <ArrowRight className=\"h-3 w-3 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200 text-primary\" />\n </h3>\n\n {description && (\n <p className=\"text-sm text-muted-foreground line-clamp-1 mt-0.5\">\n {description}\n </p>\n )}\n </div>\n </article>\n </Link>\n );\n })}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx, Fragment, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { isSimulationRunning } from "../plugin/src/client.js";
|
|
3
|
+
function RunningBar() {
|
|
4
|
+
const isRunning = isSimulationRunning();
|
|
5
|
+
return /* @__PURE__ */ jsx(Fragment, { children: isRunning && // this should stay red not another color
|
|
6
|
+
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-50 px-[var(--page-padding)] py-2 bg-red-500 text-primary-foreground font-mono text-xs text-center tracking-wide", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-3", children: [
|
|
7
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-current animate-pulse" }),
|
|
8
|
+
/* @__PURE__ */ jsx("span", { className: "uppercase tracking-widest", children: "simulation running" }),
|
|
9
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-foreground/60", children: "Page will auto-refresh on completion" })
|
|
10
|
+
] }) }) });
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
RunningBar
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=running-bar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"running-bar.js","sources":["../../../src/components/running-bar.tsx"],"sourcesContent":["import { isSimulationRunning } from \"../../plugin/src/client\";\n\n\nexport function RunningBar() {\n const isRunning = isSimulationRunning();\n\n return (\n <>\n {isRunning && (\n // this should stay red not another color\n <div className=\"sticky top-0 z-50 px-[var(--page-padding)] py-2 bg-red-500 text-primary-foreground font-mono text-xs text-center tracking-wide\">\n <span className=\"inline-flex items-center gap-3\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-current animate-pulse\" />\n <span className=\"uppercase tracking-widest\">simulation running</span>\n <span className=\"text-primary-foreground/60\">Page will auto-refresh on completion</span>\n </span>\n </div>\n )}\n </>\n )\n}"],"names":[],"mappings":";;AAGO,SAAS,aAAa;AAC3B,QAAM,YAAY,oBAAA;AAElB,SACE,oBAAA,UAAA,EACG,UAAA;AAAA,sBAEE,OAAA,EAAI,WAAU,kIACb,UAAA,qBAAC,QAAA,EAAK,WAAU,kCACd,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAU,oDAAA,CAAoD;AAAA,IACpE,oBAAC,QAAA,EAAK,WAAU,6BAA4B,UAAA,sBAAkB;AAAA,IAC9D,oBAAC,QAAA,EAAK,WAAU,8BAA6B,UAAA,uCAAA,CAAoC;AAAA,EAAA,EAAA,CACnF,GACF,GAEJ;AAEJ;"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, isValidElement, Children } from "react";
|
|
3
|
+
import { mdxComponents } from "./mdx-components.js";
|
|
4
|
+
function useSlidesFromMDX({ Content, frontmatter }) {
|
|
5
|
+
const slides = useMemo(() => {
|
|
6
|
+
if (!Content) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
const SlideBreak = () => /* @__PURE__ */ jsx("hr", { "data-slide-break": "true" });
|
|
10
|
+
const componentsWithBreak = {
|
|
11
|
+
...mdxComponents,
|
|
12
|
+
hr: SlideBreak
|
|
13
|
+
};
|
|
14
|
+
const rendered = /* @__PURE__ */ jsx(Content, { components: componentsWithBreak });
|
|
15
|
+
return splitByHr(rendered);
|
|
16
|
+
}, [Content]);
|
|
17
|
+
return { slides, frontmatter };
|
|
18
|
+
}
|
|
19
|
+
function splitByHr(element) {
|
|
20
|
+
var _a;
|
|
21
|
+
const slides = [[]];
|
|
22
|
+
function traverse(node) {
|
|
23
|
+
var _a2;
|
|
24
|
+
if (!node) return;
|
|
25
|
+
if (Array.isArray(node)) {
|
|
26
|
+
node.forEach(traverse);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (isValidElement(node)) {
|
|
30
|
+
if (node.type === "hr" || node.props && node.props["data-slide-break"] === "true") {
|
|
31
|
+
slides.push([]);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const nodeType = node.type;
|
|
35
|
+
if (typeof nodeType === "function" && nodeType.name === "SlideBreak") {
|
|
36
|
+
slides.push([]);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if ((_a2 = node.props) == null ? void 0 : _a2.children) {
|
|
40
|
+
const children = Children.toArray(node.props.children);
|
|
41
|
+
const hasHrChild = children.some(
|
|
42
|
+
(child) => isValidElement(child) && (child.type === "hr" || child.props && child.props["data-slide-break"] === "true")
|
|
43
|
+
);
|
|
44
|
+
if (hasHrChild) {
|
|
45
|
+
children.forEach((child) => {
|
|
46
|
+
if (isValidElement(child) && (child.type === "hr" || child.props && child.props["data-slide-break"] === "true")) {
|
|
47
|
+
slides.push([]);
|
|
48
|
+
} else {
|
|
49
|
+
slides[slides.length - 1].push(child);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
} else {
|
|
53
|
+
slides[slides.length - 1].push(node);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
slides[slides.length - 1].push(node);
|
|
59
|
+
}
|
|
60
|
+
if (isValidElement(element) && ((_a = element.props) == null ? void 0 : _a.children)) {
|
|
61
|
+
const children = Children.toArray(element.props.children);
|
|
62
|
+
children.forEach(traverse);
|
|
63
|
+
} else {
|
|
64
|
+
traverse(element);
|
|
65
|
+
}
|
|
66
|
+
return slides.filter((slide) => slide.length > 0);
|
|
67
|
+
}
|
|
68
|
+
function SlideContent({ children }) {
|
|
69
|
+
return /* @__PURE__ */ jsx("div", { className: "slide-content prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed max-w-xl", children });
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
SlideContent,
|
|
73
|
+
useSlidesFromMDX
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=slides-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slides-renderer.js","sources":["../../../src/components/slides-renderer.tsx"],"sourcesContent":["import React, { Children, isValidElement, ReactNode, useMemo } from 'react'\nimport { mdxComponents } from '@/components/mdx-components'\n\ninterface SlidesRendererProps {\n Content: React.ComponentType<{ components?: Record<string, React.ComponentType> }> | null\n frontmatter?: {\n title?: string\n description?: string\n date?: string\n }\n}\n\n/**\n * Splits MDX content into slides by <hr> elements.\n * The MDX is compiled at build time, so imports work.\n * Slide boundaries are marked with --- in MDX (which becomes <hr>).\n */\nexport function useSlidesFromMDX({ Content, frontmatter }: SlidesRendererProps) {\n const slides = useMemo(() => {\n // Handle null Content (loading state)\n if (!Content) {\n return []\n }\n\n // Create a custom hr component that we can detect\n const SlideBreak = () => <hr data-slide-break=\"true\" />\n\n // Render the MDX with our custom hr\n const componentsWithBreak = {\n ...mdxComponents,\n hr: SlideBreak,\n }\n\n // Render to get the element tree\n const rendered = <Content components={componentsWithBreak} />\n\n // Split children by hr elements\n return splitByHr(rendered)\n }, [Content])\n\n return { slides, frontmatter }\n}\n\n/**\n * Recursively traverses React element tree and splits by <hr> elements\n */\nfunction splitByHr(element: ReactNode): ReactNode[][] {\n const slides: ReactNode[][] = [[]]\n\n function traverse(node: ReactNode) {\n if (!node) return\n\n if (Array.isArray(node)) {\n node.forEach(traverse)\n return\n }\n\n if (isValidElement(node)) {\n // Check if this is our slide break marker\n if (node.type === 'hr' || (node.props && node.props['data-slide-break'] === 'true')) {\n // Start a new slide\n slides.push([])\n return\n }\n\n // Check if it's an hr component from mdxComponents\n const nodeType = node.type as any\n if (typeof nodeType === 'function' && nodeType.name === 'SlideBreak') {\n slides.push([])\n return\n }\n\n // For fragments or elements with children, we need to check children\n if (node.props?.children) {\n const children = Children.toArray(node.props.children)\n\n // Check if any child is an hr - if so, we need to split this element\n const hasHrChild = children.some(child =>\n isValidElement(child) && (\n child.type === 'hr' ||\n (child.props && child.props['data-slide-break'] === 'true')\n )\n )\n\n if (hasHrChild) {\n // Split the children\n children.forEach(child => {\n if (isValidElement(child) && (\n child.type === 'hr' ||\n (child.props && child.props['data-slide-break'] === 'true')\n )) {\n slides.push([])\n } else {\n slides[slides.length - 1].push(child)\n }\n })\n } else {\n // No hr children, add the whole element\n slides[slides.length - 1].push(node)\n }\n return\n }\n }\n\n // Add to current slide\n slides[slides.length - 1].push(node)\n }\n\n // Get the children of the rendered element\n if (isValidElement(element) && element.props?.children) {\n const children = Children.toArray(element.props.children)\n children.forEach(traverse)\n } else {\n traverse(element)\n }\n\n // Filter out empty slides\n return slides.filter(slide => slide.length > 0)\n}\n\n/**\n * Renders a single slide's content\n */\nexport function SlideContent({ children }: { children: ReactNode[] }) {\n return (\n <div className=\"slide-content prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed max-w-xl\">\n {children}\n </div>\n )\n}\n"],"names":["_a"],"mappings":";;;AAiBO,SAAS,iBAAiB,EAAE,SAAS,eAAoC;AAC9E,QAAM,SAAS,QAAQ,MAAM;AAE3B,QAAI,CAAC,SAAS;AACZ,aAAO,CAAA;AAAA,IACT;AAGA,UAAM,aAAa,MAAM,oBAAC,MAAA,EAAG,oBAAiB,QAAO;AAGrD,UAAM,sBAAsB;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI;AAAA,IAAA;AAIN,UAAM,WAAW,oBAAC,SAAA,EAAQ,YAAY,oBAAA,CAAqB;AAG3D,WAAO,UAAU,QAAQ;AAAA,EAC3B,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,QAAQ,YAAA;AACnB;AAKA,SAAS,UAAU,SAAmC;;AACpD,QAAM,SAAwB,CAAC,EAAE;AAEjC,WAAS,SAAS,MAAiB;;AACjC,QAAI,CAAC,KAAM;AAEX,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAK,QAAQ,QAAQ;AACrB;AAAA,IACF;AAEA,QAAI,eAAe,IAAI,GAAG;AAExB,UAAI,KAAK,SAAS,QAAS,KAAK,SAAS,KAAK,MAAM,kBAAkB,MAAM,QAAS;AAEnF,eAAO,KAAK,EAAE;AACd;AAAA,MACF;AAGA,YAAM,WAAW,KAAK;AACtB,UAAI,OAAO,aAAa,cAAc,SAAS,SAAS,cAAc;AACpE,eAAO,KAAK,EAAE;AACd;AAAA,MACF;AAGA,WAAIA,MAAA,KAAK,UAAL,gBAAAA,IAAY,UAAU;AACxB,cAAM,WAAW,SAAS,QAAQ,KAAK,MAAM,QAAQ;AAGrD,cAAM,aAAa,SAAS;AAAA,UAAK,CAAA,UAC/B,eAAe,KAAK,MAClB,MAAM,SAAS,QACd,MAAM,SAAS,MAAM,MAAM,kBAAkB,MAAM;AAAA,QAAA;AAIxD,YAAI,YAAY;AAEd,mBAAS,QAAQ,CAAA,UAAS;AACxB,gBAAI,eAAe,KAAK,MACtB,MAAM,SAAS,QACd,MAAM,SAAS,MAAM,MAAM,kBAAkB,MAAM,SACnD;AACD,qBAAO,KAAK,EAAE;AAAA,YAChB,OAAO;AACL,qBAAO,OAAO,SAAS,CAAC,EAAE,KAAK,KAAK;AAAA,YACtC;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AAEL,iBAAO,OAAO,SAAS,CAAC,EAAE,KAAK,IAAI;AAAA,QACrC;AACA;AAAA,MACF;AAAA,IACF;AAGA,WAAO,OAAO,SAAS,CAAC,EAAE,KAAK,IAAI;AAAA,EACrC;AAGA,MAAI,eAAe,OAAO,OAAK,aAAQ,UAAR,mBAAe,WAAU;AACtD,UAAM,WAAW,SAAS,QAAQ,QAAQ,MAAM,QAAQ;AACxD,aAAS,QAAQ,QAAQ;AAAA,EAC3B,OAAO;AACL,aAAS,OAAO;AAAA,EAClB;AAGA,SAAO,OAAO,OAAO,CAAA,UAAS,MAAM,SAAS,CAAC;AAChD;AAKO,SAAS,aAAa,EAAE,YAAuC;AACpE,SACE,oBAAC,OAAA,EAAI,WAAU,wGACZ,SAAA,CACH;AAEJ;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ThemeProvider as ThemeProvider$1 } from "next-themes";
|
|
3
|
+
function ThemeProvider({ children, ...props }) {
|
|
4
|
+
return /* @__PURE__ */ jsx(ThemeProvider$1, { ...props, children });
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
ThemeProvider
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=theme-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-provider.js","sources":["../../../src/components/theme-provider.tsx"],"sourcesContent":["import { ThemeProvider as NextThemesProvider } from \"next-themes\"\nimport { type ThemeProviderProps } from \"next-themes\"\n\nexport function ThemeProvider({ children, ...props }: ThemeProviderProps) {\n return <NextThemesProvider {...props}>{children}</NextThemesProvider>\n}\n"],"names":["NextThemesProvider"],"mappings":";;AAGO,SAAS,cAAc,EAAE,UAAU,GAAG,SAA6B;AACxE,SAAO,oBAACA,iBAAA,EAAoB,GAAG,OAAQ,SAAA,CAAS;AAClD;"}
|