yumiamd 0.1.9 → 0.1.10
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/dist/bin.js +1 -1
- package/dist/{chunk-F3CX7UG6.js → chunk-PQYPVPV2.js} +848 -25
- package/dist/index.d.ts +16 -2
- package/dist/index.js +5 -1
- package/package.json +9 -8
package/dist/bin.js
CHANGED
|
@@ -2017,10 +2017,756 @@ var PptxRenderer = class {
|
|
|
2017
2017
|
}
|
|
2018
2018
|
};
|
|
2019
2019
|
|
|
2020
|
+
// ../renderer-html/dist/renderer.js
|
|
2021
|
+
var HtmlRenderer = class {
|
|
2022
|
+
name = "HtmlRenderer";
|
|
2023
|
+
targetFormat = "html";
|
|
2024
|
+
async render(presentation, context = {}) {
|
|
2025
|
+
const colorOverrides = presentation.metadata.colors ? { colors: presentation.metadata.colors } : void 0;
|
|
2026
|
+
const resolvedTheme = resolveTheme(presentation.metadata.theme, colorOverrides);
|
|
2027
|
+
const theme = context.theme || resolvedTheme || defaultTheme;
|
|
2028
|
+
const title = presentation.metadata.title || "YumiaMD Presentation";
|
|
2029
|
+
const aspectRatio = presentation.metadata.aspectRatio || "16:9";
|
|
2030
|
+
const is43 = aspectRatio === "4:3";
|
|
2031
|
+
const ratioAspect = is43 ? "4 / 3" : "16 / 9";
|
|
2032
|
+
const options = context.options || {};
|
|
2033
|
+
const liveReloadScript = options.liveReload ? `
|
|
2034
|
+
<!-- YumiaMD Live Reload -->
|
|
2035
|
+
<script>
|
|
2036
|
+
(function() {
|
|
2037
|
+
const port = ${options.liveReloadPort || 3e3};
|
|
2038
|
+
const evtSource = new EventSource('/__yumia_live_reload');
|
|
2039
|
+
evtSource.onmessage = function(event) {
|
|
2040
|
+
if (event.data === 'reload') {
|
|
2041
|
+
const currentHash = window.location.hash;
|
|
2042
|
+
fetch(window.location.href)
|
|
2043
|
+
.then(res => res.text())
|
|
2044
|
+
.then(html => {
|
|
2045
|
+
const parser = new DOMParser();
|
|
2046
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
2047
|
+
const newDeck = doc.getElementById('yumia-deck');
|
|
2048
|
+
const oldDeck = document.getElementById('yumia-deck');
|
|
2049
|
+
if (newDeck && oldDeck) {
|
|
2050
|
+
oldDeck.innerHTML = newDeck.innerHTML;
|
|
2051
|
+
window.deckController.init();
|
|
2052
|
+
if (currentHash) window.location.hash = currentHash;
|
|
2053
|
+
} else {
|
|
2054
|
+
window.location.reload();
|
|
2055
|
+
}
|
|
2056
|
+
})
|
|
2057
|
+
.catch(() => window.location.reload());
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
})();
|
|
2061
|
+
</script>
|
|
2062
|
+
` : "";
|
|
2063
|
+
const slidesHtml = presentation.slides.map((slide, idx) => this.renderSlide(slide, idx + 1, presentation.slides.length, theme)).join("\n");
|
|
2064
|
+
const html = `<!DOCTYPE html>
|
|
2065
|
+
<html lang="en">
|
|
2066
|
+
<head>
|
|
2067
|
+
<meta charset="UTF-8">
|
|
2068
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2069
|
+
<title>${this.escapeHtml(title)}</title>
|
|
2070
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
2071
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
2072
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&family=Outfit:wght@600;700;800&display=swap" rel="stylesheet">
|
|
2073
|
+
<style>
|
|
2074
|
+
:root {
|
|
2075
|
+
--yumia-bg: ${theme.colors.background};
|
|
2076
|
+
--yumia-surface: ${theme.colors.surface};
|
|
2077
|
+
--yumia-text: ${theme.colors.text};
|
|
2078
|
+
--yumia-muted: ${theme.colors.muted || "#94a3b8"};
|
|
2079
|
+
--yumia-primary: ${theme.colors.primary};
|
|
2080
|
+
--yumia-secondary: ${theme.colors.secondary || theme.colors.primary};
|
|
2081
|
+
--yumia-accent: ${theme.colors.accent || theme.colors.primary};
|
|
2082
|
+
--yumia-border: ${theme.colors.border || "rgba(255,255,255,0.1)"};
|
|
2083
|
+
--yumia-success: ${theme.colors.success || "#10b981"};
|
|
2084
|
+
--yumia-warning: ${theme.colors.warning || "#f59e0b"};
|
|
2085
|
+
--yumia-danger: ${theme.colors.danger || "#ef4444"};
|
|
2086
|
+
--yumia-info: ${theme.colors.info || "#3b82f6"};
|
|
2087
|
+
--yumia-font-heading: ${theme.typography.headingFont};
|
|
2088
|
+
--yumia-font-body: ${theme.typography.bodyFont};
|
|
2089
|
+
--yumia-font-code: ${theme.typography.codeFont || "monospace"};
|
|
2090
|
+
--yumia-ratio: ${ratioAspect};
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
* {
|
|
2094
|
+
box-sizing: border-box;
|
|
2095
|
+
margin: 0;
|
|
2096
|
+
padding: 0;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
body {
|
|
2100
|
+
background-color: #050508;
|
|
2101
|
+
color: var(--yumia-text);
|
|
2102
|
+
font-family: var(--yumia-font-body);
|
|
2103
|
+
overflow: hidden;
|
|
2104
|
+
width: 100vw;
|
|
2105
|
+
height: 100vh;
|
|
2106
|
+
display: flex;
|
|
2107
|
+
align-items: center;
|
|
2108
|
+
justify-content: center;
|
|
2109
|
+
user-select: none;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
#yumia-deck {
|
|
2113
|
+
position: relative;
|
|
2114
|
+
width: 100%;
|
|
2115
|
+
height: 100%;
|
|
2116
|
+
display: flex;
|
|
2117
|
+
align-items: center;
|
|
2118
|
+
justify-content: center;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
.yumia-slide-wrapper {
|
|
2122
|
+
position: relative;
|
|
2123
|
+
width: min(94vw, calc(94vh * (${is43 ? "4 / 3" : "16 / 9"})));
|
|
2124
|
+
height: min(calc(94vw / (${is43 ? "4 / 3" : "16 / 9"})), 94vh);
|
|
2125
|
+
aspect-ratio: var(--yumia-ratio);
|
|
2126
|
+
background-color: var(--yumia-bg);
|
|
2127
|
+
border-radius: 12px;
|
|
2128
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7), 0 0 0 1px var(--yumia-border);
|
|
2129
|
+
overflow: hidden;
|
|
2130
|
+
display: none;
|
|
2131
|
+
flex-direction: column;
|
|
2132
|
+
padding: 4.5% 5.5%;
|
|
2133
|
+
animation: fadeIn 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
.yumia-slide-wrapper.active {
|
|
2137
|
+
display: flex;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
@keyframes fadeIn {
|
|
2141
|
+
from { opacity: 0; transform: scale(0.985); }
|
|
2142
|
+
to { opacity: 1; transform: scale(1); }
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
/* Headings */
|
|
2146
|
+
h1, h2, h3, h4 {
|
|
2147
|
+
font-family: var(--yumia-font-heading);
|
|
2148
|
+
font-weight: 700;
|
|
2149
|
+
line-height: 1.15;
|
|
2150
|
+
margin-bottom: 0.7em;
|
|
2151
|
+
letter-spacing: -0.02em;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
h1 {
|
|
2155
|
+
font-size: clamp(2rem, 3.8vw, 3.4rem);
|
|
2156
|
+
color: var(--yumia-primary);
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
h2 {
|
|
2160
|
+
font-size: clamp(1.6rem, 2.8vw, 2.5rem);
|
|
2161
|
+
color: var(--yumia-text);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
h3 {
|
|
2165
|
+
font-size: clamp(1.3rem, 2.2vw, 1.9rem);
|
|
2166
|
+
color: var(--yumia-text);
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
h4 {
|
|
2170
|
+
font-size: clamp(1.1rem, 1.7vw, 1.4rem);
|
|
2171
|
+
color: var(--yumia-muted);
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
/* Paragraphs */
|
|
2175
|
+
p {
|
|
2176
|
+
font-size: clamp(1rem, 1.4vw, 1.25rem);
|
|
2177
|
+
line-height: 1.6;
|
|
2178
|
+
color: var(--yumia-text);
|
|
2179
|
+
margin-bottom: 0.8em;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
p strong, li strong {
|
|
2183
|
+
color: var(--yumia-primary);
|
|
2184
|
+
font-weight: 700;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
p em, li em {
|
|
2188
|
+
font-style: italic;
|
|
2189
|
+
color: var(--yumia-secondary);
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
p code, li code {
|
|
2193
|
+
font-family: var(--yumia-font-code);
|
|
2194
|
+
background: rgba(255, 255, 255, 0.08);
|
|
2195
|
+
padding: 0.15em 0.4em;
|
|
2196
|
+
border-radius: 4px;
|
|
2197
|
+
font-size: 0.9em;
|
|
2198
|
+
color: var(--yumia-accent);
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
/* Lists */
|
|
2202
|
+
ul, ol {
|
|
2203
|
+
font-size: clamp(1rem, 1.35vw, 1.2rem);
|
|
2204
|
+
line-height: 1.65;
|
|
2205
|
+
margin-bottom: 1em;
|
|
2206
|
+
padding-left: 1.5em;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
li {
|
|
2210
|
+
margin-bottom: 0.5em;
|
|
2211
|
+
color: var(--yumia-text);
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
li::marker {
|
|
2215
|
+
color: var(--yumia-primary);
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
/* Columns */
|
|
2219
|
+
.yumia-columns {
|
|
2220
|
+
display: grid;
|
|
2221
|
+
gap: 1.5rem;
|
|
2222
|
+
width: 100%;
|
|
2223
|
+
margin: 0.8rem 0;
|
|
2224
|
+
align-items: stretch;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
.yumia-column {
|
|
2228
|
+
display: flex;
|
|
2229
|
+
flex-direction: column;
|
|
2230
|
+
gap: 0.8rem;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
/* Cards */
|
|
2234
|
+
.yumia-card {
|
|
2235
|
+
background: var(--yumia-surface);
|
|
2236
|
+
border: 1.5px solid var(--yumia-border);
|
|
2237
|
+
border-radius: 12px;
|
|
2238
|
+
padding: 1.25rem 1.5rem;
|
|
2239
|
+
display: flex;
|
|
2240
|
+
flex-direction: column;
|
|
2241
|
+
gap: 0.6rem;
|
|
2242
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
.yumia-card[data-variant="primary"] {
|
|
2246
|
+
border-color: var(--yumia-primary);
|
|
2247
|
+
}
|
|
2248
|
+
.yumia-card[data-variant="primary"] .yumia-card-title {
|
|
2249
|
+
color: var(--yumia-primary);
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
.yumia-card[data-variant="warning"] {
|
|
2253
|
+
border-color: var(--yumia-warning);
|
|
2254
|
+
}
|
|
2255
|
+
.yumia-card[data-variant="warning"] .yumia-card-title {
|
|
2256
|
+
color: var(--yumia-warning);
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
.yumia-card[data-variant="success"] {
|
|
2260
|
+
border-color: var(--yumia-success);
|
|
2261
|
+
}
|
|
2262
|
+
.yumia-card[data-variant="success"] .yumia-card-title {
|
|
2263
|
+
color: var(--yumia-success);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
.yumia-card[data-variant="info"] {
|
|
2267
|
+
border-color: var(--yumia-info);
|
|
2268
|
+
}
|
|
2269
|
+
.yumia-card[data-variant="info"] .yumia-card-title {
|
|
2270
|
+
color: var(--yumia-info);
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
.yumia-card-title {
|
|
2274
|
+
font-family: var(--yumia-font-heading);
|
|
2275
|
+
font-size: 1.25rem;
|
|
2276
|
+
font-weight: 700;
|
|
2277
|
+
color: var(--yumia-primary);
|
|
2278
|
+
margin-bottom: 0.3rem;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
/* Metrics */
|
|
2282
|
+
.yumia-metric {
|
|
2283
|
+
background: var(--yumia-surface);
|
|
2284
|
+
border: 1.5px solid var(--yumia-border);
|
|
2285
|
+
border-radius: 12px;
|
|
2286
|
+
padding: 1rem 1.2rem;
|
|
2287
|
+
display: flex;
|
|
2288
|
+
flex-direction: column;
|
|
2289
|
+
align-items: center;
|
|
2290
|
+
justify-content: center;
|
|
2291
|
+
text-align: center;
|
|
2292
|
+
gap: 0.25rem;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
.yumia-metric[data-variant="primary"] { border-color: var(--yumia-primary); }
|
|
2296
|
+
.yumia-metric[data-variant="primary"] .yumia-metric-value { color: var(--yumia-primary); }
|
|
2297
|
+
.yumia-metric[data-variant="success"] { border-color: var(--yumia-success); }
|
|
2298
|
+
.yumia-metric[data-variant="success"] .yumia-metric-value { color: var(--yumia-success); }
|
|
2299
|
+
.yumia-metric[data-variant="info"] { border-color: var(--yumia-info); }
|
|
2300
|
+
.yumia-metric[data-variant="info"] .yumia-metric-value { color: var(--yumia-info); }
|
|
2301
|
+
.yumia-metric[data-variant="warning"] { border-color: var(--yumia-warning); }
|
|
2302
|
+
.yumia-metric[data-variant="warning"] .yumia-metric-value { color: var(--yumia-warning); }
|
|
2303
|
+
.yumia-metric[data-variant="danger"] { border-color: var(--yumia-danger); }
|
|
2304
|
+
.yumia-metric[data-variant="danger"] .yumia-metric-value { color: var(--yumia-danger); }
|
|
2305
|
+
|
|
2306
|
+
.yumia-metric-label {
|
|
2307
|
+
font-size: 0.75rem;
|
|
2308
|
+
font-weight: 700;
|
|
2309
|
+
text-transform: uppercase;
|
|
2310
|
+
letter-spacing: 0.08em;
|
|
2311
|
+
color: var(--yumia-muted);
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
.yumia-metric-value {
|
|
2315
|
+
font-family: var(--yumia-font-heading);
|
|
2316
|
+
font-size: clamp(1.8rem, 3.2vw, 2.6rem);
|
|
2317
|
+
font-weight: 800;
|
|
2318
|
+
line-height: 1.1;
|
|
2319
|
+
color: var(--yumia-primary);
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
.yumia-metric-change {
|
|
2323
|
+
font-size: 0.85rem;
|
|
2324
|
+
font-weight: 600;
|
|
2325
|
+
}
|
|
2326
|
+
.yumia-metric-change.positive { color: var(--yumia-success); }
|
|
2327
|
+
.yumia-metric-change.negative { color: var(--yumia-danger); }
|
|
2328
|
+
|
|
2329
|
+
/* Tables */
|
|
2330
|
+
table {
|
|
2331
|
+
width: 100%;
|
|
2332
|
+
border-collapse: collapse;
|
|
2333
|
+
margin: 1rem 0;
|
|
2334
|
+
font-size: clamp(0.9rem, 1.15vw, 1.05rem);
|
|
2335
|
+
border: 1px solid var(--yumia-border);
|
|
2336
|
+
border-radius: 8px;
|
|
2337
|
+
overflow: hidden;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
th {
|
|
2341
|
+
background: var(--yumia-primary);
|
|
2342
|
+
color: #ffffff;
|
|
2343
|
+
font-weight: 700;
|
|
2344
|
+
padding: 0.75rem 1rem;
|
|
2345
|
+
text-align: left;
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
td {
|
|
2349
|
+
padding: 0.7rem 1rem;
|
|
2350
|
+
border-top: 1px solid var(--yumia-border);
|
|
2351
|
+
color: var(--yumia-text);
|
|
2352
|
+
background: var(--yumia-surface);
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
tr:nth-child(even) td {
|
|
2356
|
+
background: rgba(255, 255, 255, 0.03);
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
/* Code Blocks */
|
|
2360
|
+
pre {
|
|
2361
|
+
background: #0a0a10;
|
|
2362
|
+
border: 1px solid var(--yumia-border);
|
|
2363
|
+
border-radius: 8px;
|
|
2364
|
+
padding: 1rem 1.25rem;
|
|
2365
|
+
overflow-x: auto;
|
|
2366
|
+
margin: 0.8rem 0;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
pre code {
|
|
2370
|
+
font-family: var(--yumia-font-code);
|
|
2371
|
+
font-size: 0.95rem;
|
|
2372
|
+
color: #00F0FF;
|
|
2373
|
+
background: none;
|
|
2374
|
+
padding: 0;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
/* Blockquotes */
|
|
2378
|
+
blockquote {
|
|
2379
|
+
border-left: 4px solid var(--yumia-accent);
|
|
2380
|
+
padding: 0.6rem 1.2rem;
|
|
2381
|
+
margin: 1rem 0;
|
|
2382
|
+
font-style: italic;
|
|
2383
|
+
color: var(--yumia-muted);
|
|
2384
|
+
font-size: 1.1rem;
|
|
2385
|
+
background: rgba(255, 255, 255, 0.02);
|
|
2386
|
+
border-radius: 0 8px 8px 0;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
/* Controls Overlay */
|
|
2390
|
+
.yumia-controls {
|
|
2391
|
+
position: fixed;
|
|
2392
|
+
bottom: 20px;
|
|
2393
|
+
right: 24px;
|
|
2394
|
+
display: flex;
|
|
2395
|
+
align-items: center;
|
|
2396
|
+
gap: 12px;
|
|
2397
|
+
background: rgba(15, 23, 42, 0.85);
|
|
2398
|
+
backdrop-filter: blur(12px);
|
|
2399
|
+
padding: 8px 16px;
|
|
2400
|
+
border-radius: 30px;
|
|
2401
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
2402
|
+
z-index: 1000;
|
|
2403
|
+
font-size: 13px;
|
|
2404
|
+
color: #e2e8f0;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
.yumia-btn {
|
|
2408
|
+
background: none;
|
|
2409
|
+
border: none;
|
|
2410
|
+
color: #f8fafc;
|
|
2411
|
+
cursor: pointer;
|
|
2412
|
+
padding: 4px 8px;
|
|
2413
|
+
border-radius: 6px;
|
|
2414
|
+
font-size: 14px;
|
|
2415
|
+
display: flex;
|
|
2416
|
+
align-items: center;
|
|
2417
|
+
justify-content: center;
|
|
2418
|
+
transition: background 0.15s ease;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
.yumia-btn:hover {
|
|
2422
|
+
background: rgba(255, 255, 255, 0.15);
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
.yumia-progress-bar {
|
|
2426
|
+
position: absolute;
|
|
2427
|
+
bottom: 0;
|
|
2428
|
+
left: 0;
|
|
2429
|
+
height: 4px;
|
|
2430
|
+
background: var(--yumia-primary);
|
|
2431
|
+
transition: width 0.25s ease;
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
/* Notes Drawer */
|
|
2435
|
+
.yumia-notes-drawer {
|
|
2436
|
+
position: fixed;
|
|
2437
|
+
bottom: 0;
|
|
2438
|
+
left: 0;
|
|
2439
|
+
right: 0;
|
|
2440
|
+
max-height: 220px;
|
|
2441
|
+
background: rgba(10, 10, 18, 0.95);
|
|
2442
|
+
backdrop-filter: blur(16px);
|
|
2443
|
+
border-top: 1px solid var(--yumia-primary);
|
|
2444
|
+
padding: 16px 24px;
|
|
2445
|
+
overflow-y: auto;
|
|
2446
|
+
display: none;
|
|
2447
|
+
z-index: 2000;
|
|
2448
|
+
font-size: 14px;
|
|
2449
|
+
line-height: 1.6;
|
|
2450
|
+
color: #cbd5e1;
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
.yumia-notes-drawer.open {
|
|
2454
|
+
display: block;
|
|
2455
|
+
}
|
|
2456
|
+
</style>
|
|
2457
|
+
</head>
|
|
2458
|
+
<body>
|
|
2459
|
+
<div id="yumia-deck">
|
|
2460
|
+
${slidesHtml}
|
|
2461
|
+
</div>
|
|
2462
|
+
|
|
2463
|
+
<div class="yumia-controls">
|
|
2464
|
+
<button class="yumia-btn" id="btn-prev" title="Previous Slide (\u2190)">\u25C0</button>
|
|
2465
|
+
<span id="slide-indicator">1 / ${presentation.slides.length}</span>
|
|
2466
|
+
<button class="yumia-btn" id="btn-next" title="Next Slide (\u2192)">\u25B6</button>
|
|
2467
|
+
<button class="yumia-btn" id="btn-notes" title="Toggle Notes (N)">\u{1F4DD}</button>
|
|
2468
|
+
<button class="yumia-btn" id="btn-fs" title="Fullscreen (F)">\u26F6</button>
|
|
2469
|
+
</div>
|
|
2470
|
+
|
|
2471
|
+
<div id="notes-drawer" class="yumia-notes-drawer"></div>
|
|
2472
|
+
|
|
2473
|
+
<script>
|
|
2474
|
+
(function() {
|
|
2475
|
+
const slides = document.querySelectorAll('.yumia-slide-wrapper');
|
|
2476
|
+
const indicator = document.getElementById('slide-indicator');
|
|
2477
|
+
const notesDrawer = document.getElementById('notes-drawer');
|
|
2478
|
+
let currentIdx = 0;
|
|
2479
|
+
|
|
2480
|
+
function updateSlide(newIdx) {
|
|
2481
|
+
if (newIdx < 0 || newIdx >= slides.length) return;
|
|
2482
|
+
slides[currentIdx]?.classList.remove('active');
|
|
2483
|
+
currentIdx = newIdx;
|
|
2484
|
+
slides[currentIdx]?.classList.add('active');
|
|
2485
|
+
|
|
2486
|
+
if (indicator) {
|
|
2487
|
+
indicator.textContent = (currentIdx + 1) + ' / ' + slides.length;
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
const currentSlide = slides[currentIdx];
|
|
2491
|
+
const notes = currentSlide ? currentSlide.getAttribute('data-notes') : '';
|
|
2492
|
+
if (notesDrawer) {
|
|
2493
|
+
notesDrawer.innerHTML = notes ? '<strong>Speaker Notes:</strong><br>' + notes : '<em>No notes for this slide.</em>';
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
window.location.hash = '#' + (currentIdx + 1);
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
function init() {
|
|
2500
|
+
const hash = window.location.hash.replace('#', '');
|
|
2501
|
+
const initialIdx = parseInt(hash, 10) - 1;
|
|
2502
|
+
updateSlide(isNaN(initialIdx) ? 0 : initialIdx);
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
window.deckController = {
|
|
2506
|
+
init: init,
|
|
2507
|
+
next: function() { updateSlide(currentIdx + 1); },
|
|
2508
|
+
prev: function() { updateSlide(currentIdx - 1); },
|
|
2509
|
+
toggleNotes: function() { notesDrawer?.classList.toggle('open'); },
|
|
2510
|
+
toggleFs: function() {
|
|
2511
|
+
if (!document.fullscreenElement) {
|
|
2512
|
+
document.documentElement.requestFullscreen().catch(() => {});
|
|
2513
|
+
} else {
|
|
2514
|
+
document.exitFullscreen().catch(() => {});
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
|
|
2519
|
+
document.getElementById('btn-next')?.addEventListener('click', window.deckController.next);
|
|
2520
|
+
document.getElementById('btn-prev')?.addEventListener('click', window.deckController.prev);
|
|
2521
|
+
document.getElementById('btn-notes')?.addEventListener('click', window.deckController.toggleNotes);
|
|
2522
|
+
document.getElementById('btn-fs')?.addEventListener('click', window.deckController.toggleFs);
|
|
2523
|
+
|
|
2524
|
+
window.addEventListener('keydown', function(e) {
|
|
2525
|
+
if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') {
|
|
2526
|
+
e.preventDefault();
|
|
2527
|
+
window.deckController.next();
|
|
2528
|
+
} else if (e.key === 'ArrowLeft' || e.key === 'Backspace' || e.key === 'PageUp') {
|
|
2529
|
+
e.preventDefault();
|
|
2530
|
+
window.deckController.prev();
|
|
2531
|
+
} else if (e.key.toLowerCase() === 'f') {
|
|
2532
|
+
window.deckController.toggleFs();
|
|
2533
|
+
} else if (e.key.toLowerCase() === 'n') {
|
|
2534
|
+
window.deckController.toggleNotes();
|
|
2535
|
+
}
|
|
2536
|
+
});
|
|
2537
|
+
|
|
2538
|
+
// Touch swipe support
|
|
2539
|
+
let touchStartX = 0;
|
|
2540
|
+
window.addEventListener('touchstart', function(e) {
|
|
2541
|
+
touchStartX = e.changedTouches[0]?.screenX || 0;
|
|
2542
|
+
});
|
|
2543
|
+
window.addEventListener('touchend', function(e) {
|
|
2544
|
+
const touchEndX = e.changedTouches[0]?.screenX || 0;
|
|
2545
|
+
if (touchStartX - touchEndX > 50) window.deckController.next();
|
|
2546
|
+
if (touchEndX - touchStartX > 50) window.deckController.prev();
|
|
2547
|
+
});
|
|
2548
|
+
|
|
2549
|
+
init();
|
|
2550
|
+
})();
|
|
2551
|
+
</script>
|
|
2552
|
+
${liveReloadScript}
|
|
2553
|
+
</body>
|
|
2554
|
+
</html>`;
|
|
2555
|
+
return {
|
|
2556
|
+
format: "html",
|
|
2557
|
+
html,
|
|
2558
|
+
slideCount: presentation.slides.length
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
renderSlide(slide, slideNum, totalSlides, theme) {
|
|
2562
|
+
const activeClass = slideNum === 1 ? "active" : "";
|
|
2563
|
+
const notesAttr = slide.notes ? this.escapeHtml(slide.notes.replace(/\n/g, "<br>")) : "";
|
|
2564
|
+
const progressPercent = Math.round(slideNum / totalSlides * 100);
|
|
2565
|
+
const elementsHtml = slide.elements.map((el) => this.renderElement(el, theme)).join("\n");
|
|
2566
|
+
return `
|
|
2567
|
+
<div class="yumia-slide-wrapper ${activeClass}" id="slide-${slideNum}" data-notes="${notesAttr}">
|
|
2568
|
+
${elementsHtml}
|
|
2569
|
+
<div class="yumia-progress-bar" style="width: ${progressPercent}%;"></div>
|
|
2570
|
+
</div>`;
|
|
2571
|
+
}
|
|
2572
|
+
renderElement(element, theme) {
|
|
2573
|
+
switch (element.type) {
|
|
2574
|
+
case "heading": {
|
|
2575
|
+
const h = element;
|
|
2576
|
+
const tag = `h${Math.min(4, Math.max(1, h.level))}`;
|
|
2577
|
+
return `<${tag}>${this.formatInline(h.text)}</${tag}>`;
|
|
2578
|
+
}
|
|
2579
|
+
case "paragraph": {
|
|
2580
|
+
const p = element;
|
|
2581
|
+
return `<p>${this.formatInline(p.text)}</p>`;
|
|
2582
|
+
}
|
|
2583
|
+
case "list": {
|
|
2584
|
+
const l = element;
|
|
2585
|
+
const tag = l.ordered ? "ol" : "ul";
|
|
2586
|
+
const items = l.items.map((i) => `<li>${this.formatInline(i.text)}</li>`).join("\n");
|
|
2587
|
+
return `<${tag}>${items}</${tag}>`;
|
|
2588
|
+
}
|
|
2589
|
+
case "code": {
|
|
2590
|
+
const c = element;
|
|
2591
|
+
const langClass = c.language ? `class="language-${c.language}"` : "";
|
|
2592
|
+
return `<pre><code ${langClass}>${this.escapeHtml(c.code)}</code></pre>`;
|
|
2593
|
+
}
|
|
2594
|
+
case "quote": {
|
|
2595
|
+
const q = element;
|
|
2596
|
+
const author = q.author ? `<br><small>\u2014 ${this.escapeHtml(q.author)}</small>` : "";
|
|
2597
|
+
return `<blockquote>\u201C${this.formatInline(q.text)}\u201D${author}</blockquote>`;
|
|
2598
|
+
}
|
|
2599
|
+
case "table": {
|
|
2600
|
+
const t = element;
|
|
2601
|
+
let html = "<table>";
|
|
2602
|
+
if (t.headers && t.headers.length > 0) {
|
|
2603
|
+
html += "<thead><tr>";
|
|
2604
|
+
html += t.headers.map((h) => `<th>${this.formatInline(h.replace(/\*\*/g, ""))}</th>`).join("");
|
|
2605
|
+
html += "</tr></thead>";
|
|
2606
|
+
}
|
|
2607
|
+
if (t.rows) {
|
|
2608
|
+
html += "<tbody>";
|
|
2609
|
+
for (const row of t.rows) {
|
|
2610
|
+
html += "<tr>";
|
|
2611
|
+
html += row.map((c) => `<td>${this.formatInline(c.replace(/\*\*/g, ""))}</td>`).join("");
|
|
2612
|
+
html += "</tr>";
|
|
2613
|
+
}
|
|
2614
|
+
html += "</tbody>";
|
|
2615
|
+
}
|
|
2616
|
+
html += "</table>";
|
|
2617
|
+
return html;
|
|
2618
|
+
}
|
|
2619
|
+
case "image": {
|
|
2620
|
+
const img = element;
|
|
2621
|
+
const alt = img.alt ? `alt="${this.escapeHtml(img.alt)}"` : "";
|
|
2622
|
+
return `<img src="${this.escapeHtml(img.src)}" ${alt} style="max-width: 100%; border-radius: 8px;">`;
|
|
2623
|
+
}
|
|
2624
|
+
case "metric": {
|
|
2625
|
+
const m = element;
|
|
2626
|
+
const variant = m.variant || "primary";
|
|
2627
|
+
const displayVal = m.unit ? `${m.value} ${m.unit}` : m.value;
|
|
2628
|
+
const changeHtml = m.change ? `<span class="yumia-metric-change ${m.change.startsWith("+") ? "positive" : "negative"}">${this.escapeHtml(m.change)}</span>` : "";
|
|
2629
|
+
return `
|
|
2630
|
+
<div class="yumia-metric" data-variant="${variant}">
|
|
2631
|
+
<span class="yumia-metric-label">${this.escapeHtml(m.label)}</span>
|
|
2632
|
+
<span class="yumia-metric-value">${this.escapeHtml(displayVal)}</span>
|
|
2633
|
+
${changeHtml}
|
|
2634
|
+
</div>`;
|
|
2635
|
+
}
|
|
2636
|
+
case "card": {
|
|
2637
|
+
const card = element;
|
|
2638
|
+
const variant = card.variant || "default";
|
|
2639
|
+
const titleHtml = card.title ? `<div class="yumia-card-title">${this.escapeHtml(card.title)}</div>` : "";
|
|
2640
|
+
const innerHtml = card.elements ? card.elements.map((child) => this.renderElement(child, theme)).join("\n") : "";
|
|
2641
|
+
return `
|
|
2642
|
+
<div class="yumia-card" data-variant="${variant}">
|
|
2643
|
+
${titleHtml}
|
|
2644
|
+
${innerHtml}
|
|
2645
|
+
</div>`;
|
|
2646
|
+
}
|
|
2647
|
+
case "columns": {
|
|
2648
|
+
const cols = element;
|
|
2649
|
+
const colCount = cols.columns.length;
|
|
2650
|
+
const ratioTemplate = cols.ratios ? cols.ratios.split(":").map((r) => `${r}fr`).join(" ") : `repeat(${colCount}, 1fr)`;
|
|
2651
|
+
const colsHtml = cols.columns.map((col) => {
|
|
2652
|
+
const inner = col.elements.map((child) => this.renderElement(child, theme)).join("\n");
|
|
2653
|
+
return `<div class="yumia-column">${inner}</div>`;
|
|
2654
|
+
}).join("\n");
|
|
2655
|
+
return `<div class="yumia-columns" style="grid-template-columns: ${ratioTemplate};">${colsHtml}</div>`;
|
|
2656
|
+
}
|
|
2657
|
+
default:
|
|
2658
|
+
return "";
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
formatInline(text) {
|
|
2662
|
+
const escaped = this.escapeHtml(text);
|
|
2663
|
+
return escaped.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\*(.*?)\*/g, "<em>$1</em>").replace(/`(.*?)`/g, "<code>$1</code>");
|
|
2664
|
+
}
|
|
2665
|
+
escapeHtml(str) {
|
|
2666
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2667
|
+
}
|
|
2668
|
+
};
|
|
2669
|
+
|
|
2670
|
+
// src/dev-server.ts
|
|
2671
|
+
import { createServer } from "http";
|
|
2672
|
+
import { readFileSync, watch } from "fs";
|
|
2673
|
+
import { resolve } from "path";
|
|
2674
|
+
function startDevServer(filePath, options = {}) {
|
|
2675
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
2676
|
+
const port = options.port || 3e3;
|
|
2677
|
+
const resolvedPath = resolve(process.cwd(), filePath);
|
|
2678
|
+
const compiler = new YumiaCompiler();
|
|
2679
|
+
const renderer = new HtmlRenderer();
|
|
2680
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
2681
|
+
let watcher = null;
|
|
2682
|
+
let debounceTimer = null;
|
|
2683
|
+
async function getCompiledHtml() {
|
|
2684
|
+
try {
|
|
2685
|
+
const source = readFileSync(resolvedPath, "utf-8");
|
|
2686
|
+
const output = await compiler.compile(source, renderer, {
|
|
2687
|
+
renderContext: {
|
|
2688
|
+
options: {
|
|
2689
|
+
liveReload: true,
|
|
2690
|
+
liveReloadPort: port
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
});
|
|
2694
|
+
return output.html;
|
|
2695
|
+
} catch (err) {
|
|
2696
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2697
|
+
return `<!DOCTYPE html><html><head><title>YumiaMD Compile Error</title><style>body{background:#0b0b12;color:#ef4444;font-family:monospace;padding:40px;}</style></head><body><h1>Compile Error</h1><pre>${errMsg}</pre></body></html>`;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
const server = createServer(async (req, res) => {
|
|
2701
|
+
const url = req.url || "/";
|
|
2702
|
+
if (url === "/__yumia_live_reload") {
|
|
2703
|
+
res.writeHead(200, {
|
|
2704
|
+
"Content-Type": "text/event-stream",
|
|
2705
|
+
"Cache-Control": "no-cache",
|
|
2706
|
+
Connection: "keep-alive",
|
|
2707
|
+
"Access-Control-Allow-Origin": "*"
|
|
2708
|
+
});
|
|
2709
|
+
res.write("data: connected\n\n");
|
|
2710
|
+
sseClients.add(res);
|
|
2711
|
+
req.on("close", () => {
|
|
2712
|
+
sseClients.delete(res);
|
|
2713
|
+
});
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
if (url === "/" || url.startsWith("/#")) {
|
|
2717
|
+
const html = await getCompiledHtml();
|
|
2718
|
+
res.writeHead(200, {
|
|
2719
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
2720
|
+
"Cache-Control": "no-store"
|
|
2721
|
+
});
|
|
2722
|
+
res.end(html);
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
2726
|
+
res.end("Not Found");
|
|
2727
|
+
});
|
|
2728
|
+
server.on("error", (err) => {
|
|
2729
|
+
rejectPromise(err);
|
|
2730
|
+
});
|
|
2731
|
+
server.listen(port, () => {
|
|
2732
|
+
const url = `http://localhost:${port}`;
|
|
2733
|
+
try {
|
|
2734
|
+
watcher = watch(resolvedPath, () => {
|
|
2735
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
2736
|
+
debounceTimer = setTimeout(() => {
|
|
2737
|
+
for (const client of sseClients) {
|
|
2738
|
+
try {
|
|
2739
|
+
client.write("data: reload\n\n");
|
|
2740
|
+
} catch {
|
|
2741
|
+
sseClients.delete(client);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
}, 40);
|
|
2745
|
+
});
|
|
2746
|
+
} catch (watchErr) {
|
|
2747
|
+
console.warn(`[Yumia Dev] Warning: could not watch file: ${watchErr}`);
|
|
2748
|
+
}
|
|
2749
|
+
resolvePromise({
|
|
2750
|
+
server,
|
|
2751
|
+
port,
|
|
2752
|
+
url,
|
|
2753
|
+
close: () => new Promise((resClose) => {
|
|
2754
|
+
if (watcher) watcher.close();
|
|
2755
|
+
for (const client of sseClients) {
|
|
2756
|
+
client.end();
|
|
2757
|
+
}
|
|
2758
|
+
sseClients.clear();
|
|
2759
|
+
server.close(() => resClose());
|
|
2760
|
+
})
|
|
2761
|
+
});
|
|
2762
|
+
});
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2020
2766
|
// src/cli.ts
|
|
2021
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2022
|
-
import { basename, dirname, extname, join, resolve } from "path";
|
|
2023
|
-
var VERSION = "0.1.
|
|
2767
|
+
import { mkdirSync, readFileSync as readFileSync2, watch as fsWatch, writeFileSync } from "fs";
|
|
2768
|
+
import { basename, dirname, extname, join, resolve as resolve2 } from "path";
|
|
2769
|
+
var VERSION = "0.1.10";
|
|
2024
2770
|
function printHelp() {
|
|
2025
2771
|
return `
|
|
2026
2772
|
YumiaMD \u2014 Markdown-based presentation compiler (v${VERSION})
|
|
@@ -2030,11 +2776,13 @@ Usage:
|
|
|
2030
2776
|
|
|
2031
2777
|
Commands:
|
|
2032
2778
|
init [name] Scaffold a new YumiaMD presentation project
|
|
2779
|
+
dev <file> Start live-reload dev server with instant HTML preview
|
|
2780
|
+
watch <file> Watch presentation file and recompile automatically on save
|
|
2033
2781
|
validate <file> Validate a .yumia.md presentation syntax and structure
|
|
2034
2782
|
lint <file> Analyze presentation for layout overflows and accessibility
|
|
2035
2783
|
inspect <file> Inspect the AST and geometric layout tree
|
|
2036
2784
|
schema Output machine-readable JSON schema for AI agents
|
|
2037
|
-
build <file> Compile a presentation to
|
|
2785
|
+
build <file> Compile a presentation to PowerPoint (.pptx) or HTML (.html)
|
|
2038
2786
|
|
|
2039
2787
|
Theming & Color Options:
|
|
2040
2788
|
--theme, -t <name> Base theme: default | cyberpunk | minimal | corporate | terminal | academic
|
|
@@ -2044,9 +2792,12 @@ Theming & Color Options:
|
|
|
2044
2792
|
--text Hex text color (e.g. "#FFFFFF" or "#0F172A")
|
|
2045
2793
|
--accent Hex accent bar & highlight color
|
|
2046
2794
|
|
|
2047
|
-
|
|
2048
|
-
--
|
|
2049
|
-
--
|
|
2795
|
+
Server & Compiler Options:
|
|
2796
|
+
--port <num> Port for live dev server (default: 3000)
|
|
2797
|
+
--open Open default browser automatically in dev mode
|
|
2798
|
+
--watch, -w Watch for file changes during compilation
|
|
2799
|
+
--out, -o <file> Specify output file path (default: dist/<name>.<format>)
|
|
2800
|
+
--format, -f <fmt> Target output format: pptx (default) | html
|
|
2050
2801
|
--strict Enforce zero warnings in 'lint' (exits with code 1 on warning)
|
|
2051
2802
|
--json Output results formatted as JSON for CI/CD and AI tools
|
|
2052
2803
|
--layout Show computed geometric bounding boxes in 'inspect'
|
|
@@ -2079,6 +2830,7 @@ async function runCli(argv) {
|
|
|
2079
2830
|
}
|
|
2080
2831
|
const isJson = args.includes("--json");
|
|
2081
2832
|
const isStrict = args.includes("--strict");
|
|
2833
|
+
const isWatch = args.includes("--watch") || args.includes("-w");
|
|
2082
2834
|
const nonFlagArgs = args.filter((a) => !a.startsWith("-"));
|
|
2083
2835
|
const command = nonFlagArgs[0];
|
|
2084
2836
|
const target = nonFlagArgs[1];
|
|
@@ -2088,6 +2840,7 @@ async function runCli(argv) {
|
|
|
2088
2840
|
const cliSecondary = getFlagValue(args, ["--secondary"]);
|
|
2089
2841
|
const cliText = getFlagValue(args, ["--text"]);
|
|
2090
2842
|
const cliAccent = getFlagValue(args, ["--accent"]);
|
|
2843
|
+
const cliFormat = (getFlagValue(args, ["--format", "-f"]) || "pptx").toLowerCase();
|
|
2091
2844
|
if (command === "schema") {
|
|
2092
2845
|
const compiler = new YumiaCompiler();
|
|
2093
2846
|
return {
|
|
@@ -2175,7 +2928,7 @@ Opening slide introducing the presentation deck.
|
|
|
2175
2928
|
} catch (err) {
|
|
2176
2929
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2177
2930
|
if (isJson) {
|
|
2178
|
-
return { exitCode: 1, output: JSON.stringify({ success: false, error: msg }
|
|
2931
|
+
return { exitCode: 1, output: JSON.stringify({ success: false, error: msg }) };
|
|
2179
2932
|
}
|
|
2180
2933
|
return {
|
|
2181
2934
|
exitCode: 1,
|
|
@@ -2183,13 +2936,56 @@ Opening slide introducing the presentation deck.
|
|
|
2183
2936
|
};
|
|
2184
2937
|
}
|
|
2185
2938
|
}
|
|
2939
|
+
if (command === "dev") {
|
|
2940
|
+
if (!target) {
|
|
2941
|
+
const msg = "Error: Please specify a presentation file to preview in dev server.";
|
|
2942
|
+
return { exitCode: 1, output: isJson ? JSON.stringify({ error: msg }) : msg };
|
|
2943
|
+
}
|
|
2944
|
+
try {
|
|
2945
|
+
const resolvedInput = resolve2(process.cwd(), target);
|
|
2946
|
+
const portStr = getFlagValue(args, ["--port"]);
|
|
2947
|
+
const port = portStr ? parseInt(portStr, 10) : 3e3;
|
|
2948
|
+
const open = args.includes("--open");
|
|
2949
|
+
const devInstance = await startDevServer(resolvedInput, { port, open });
|
|
2950
|
+
if (isJson) {
|
|
2951
|
+
return {
|
|
2952
|
+
exitCode: 0,
|
|
2953
|
+
output: JSON.stringify(
|
|
2954
|
+
{
|
|
2955
|
+
success: true,
|
|
2956
|
+
url: devInstance.url,
|
|
2957
|
+
port: devInstance.port,
|
|
2958
|
+
file: resolvedInput
|
|
2959
|
+
},
|
|
2960
|
+
null,
|
|
2961
|
+
2
|
|
2962
|
+
)
|
|
2963
|
+
};
|
|
2964
|
+
}
|
|
2965
|
+
return {
|
|
2966
|
+
exitCode: 0,
|
|
2967
|
+
output: `\u{1F680} YumiaMD Live Dev Server running at: ${devInstance.url}
|
|
2968
|
+
Watching: ${target}
|
|
2969
|
+
Hot-reloading active via SSE. Press Ctrl+C to stop.`
|
|
2970
|
+
};
|
|
2971
|
+
} catch (err) {
|
|
2972
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2973
|
+
if (isJson) {
|
|
2974
|
+
return { exitCode: 1, output: JSON.stringify({ success: false, error: msg }) };
|
|
2975
|
+
}
|
|
2976
|
+
return {
|
|
2977
|
+
exitCode: 1,
|
|
2978
|
+
output: `\u2717 Dev server failed: ${msg}`
|
|
2979
|
+
};
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2186
2982
|
if (command === "validate") {
|
|
2187
2983
|
if (!target) {
|
|
2188
2984
|
const msg = "Error: Please specify a file to validate.";
|
|
2189
2985
|
return { exitCode: 1, output: isJson ? JSON.stringify({ error: msg }) : msg };
|
|
2190
2986
|
}
|
|
2191
2987
|
try {
|
|
2192
|
-
const source =
|
|
2988
|
+
const source = readFileSync2(target, "utf-8");
|
|
2193
2989
|
const compiler = new YumiaCompiler();
|
|
2194
2990
|
const validation = compiler.validate(source);
|
|
2195
2991
|
if (isJson) {
|
|
@@ -2231,7 +3027,7 @@ ${errorLines.join("\n")}`
|
|
|
2231
3027
|
return { exitCode: 1, output: isJson ? JSON.stringify({ error: msg }) : msg };
|
|
2232
3028
|
}
|
|
2233
3029
|
try {
|
|
2234
|
-
const source =
|
|
3030
|
+
const source = readFileSync2(target, "utf-8");
|
|
2235
3031
|
const compiler = new YumiaCompiler();
|
|
2236
3032
|
const report = compiler.lint(source, { strict: isStrict });
|
|
2237
3033
|
if (isJson) {
|
|
@@ -2291,7 +3087,7 @@ ${summary}${isStrict && report.warnings.length > 0 ? " (failed due to --strict)"
|
|
|
2291
3087
|
return { exitCode: 1, output: isJson ? JSON.stringify({ error: msg }) : msg };
|
|
2292
3088
|
}
|
|
2293
3089
|
try {
|
|
2294
|
-
const source =
|
|
3090
|
+
const source = readFileSync2(target, "utf-8");
|
|
2295
3091
|
const presentation = parseYumia(source);
|
|
2296
3092
|
if (args.includes("--layout")) {
|
|
2297
3093
|
const engine = new DefaultLayoutEngine();
|
|
@@ -2316,25 +3112,25 @@ ${summary}${isStrict && report.warnings.length > 0 ? " (failed due to --strict)"
|
|
|
2316
3112
|
};
|
|
2317
3113
|
}
|
|
2318
3114
|
}
|
|
2319
|
-
if (command === "build") {
|
|
3115
|
+
if (command === "build" || command === "watch") {
|
|
2320
3116
|
if (!target) {
|
|
2321
|
-
const msg =
|
|
3117
|
+
const msg = `Error: Please specify a presentation file to ${command}.`;
|
|
2322
3118
|
return { exitCode: 1, output: isJson ? JSON.stringify({ error: msg }) : msg };
|
|
2323
3119
|
}
|
|
2324
3120
|
try {
|
|
2325
|
-
const resolvedInput =
|
|
2326
|
-
const
|
|
3121
|
+
const resolvedInput = resolve2(process.cwd(), target);
|
|
3122
|
+
const isHtml = cliFormat === "html";
|
|
3123
|
+
const targetExtension = isHtml ? ".html" : ".pptx";
|
|
2327
3124
|
let outputPath = "";
|
|
2328
3125
|
const outFlagIndex = args.findIndex((a) => a === "--out" || a === "-o");
|
|
2329
3126
|
if (outFlagIndex !== -1 && args[outFlagIndex + 1]) {
|
|
2330
|
-
outputPath =
|
|
3127
|
+
outputPath = resolve2(process.cwd(), args[outFlagIndex + 1]);
|
|
2331
3128
|
} else {
|
|
2332
3129
|
const fileBase = basename(resolvedInput, extname(resolvedInput)).replace(/\.yumia$/, "");
|
|
2333
|
-
outputPath = join(dirname(resolvedInput), "dist", `${fileBase}
|
|
3130
|
+
outputPath = join(dirname(resolvedInput), "dist", `${fileBase}${targetExtension}`);
|
|
2334
3131
|
}
|
|
2335
3132
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
2336
3133
|
const compiler = new YumiaCompiler();
|
|
2337
|
-
const renderer = new PptxRenderer();
|
|
2338
3134
|
const cliColorOverrides = {};
|
|
2339
3135
|
if (cliBg) cliColorOverrides.background = cliBg;
|
|
2340
3136
|
if (cliPrimary) cliColorOverrides.primary = cliPrimary;
|
|
@@ -2344,11 +3140,34 @@ ${summary}${isStrict && report.warnings.length > 0 ? " (failed due to --strict)"
|
|
|
2344
3140
|
const renderTheme = cliTheme || Object.keys(cliColorOverrides).length > 0 ? resolveTheme(cliTheme || "default", {
|
|
2345
3141
|
...Object.keys(cliColorOverrides).length > 0 ? { colors: cliColorOverrides } : {}
|
|
2346
3142
|
}) : void 0;
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
3143
|
+
const compileFile = async () => {
|
|
3144
|
+
const source = readFileSync2(resolvedInput, "utf-8");
|
|
3145
|
+
if (isHtml) {
|
|
3146
|
+
const htmlRenderer = new HtmlRenderer();
|
|
3147
|
+
const result2 = await compiler.compile(source, htmlRenderer, {
|
|
3148
|
+
...renderTheme ? { renderContext: { theme: renderTheme } } : {}
|
|
3149
|
+
});
|
|
3150
|
+
writeFileSync(outputPath, result2.html, "utf-8");
|
|
3151
|
+
return { slideCount: result2.slideCount, format: result2.format };
|
|
3152
|
+
} else {
|
|
3153
|
+
const pptxRenderer = new PptxRenderer();
|
|
3154
|
+
const result2 = await compiler.compile(source, pptxRenderer, {
|
|
3155
|
+
...renderTheme ? { renderContext: { theme: renderTheme } } : {}
|
|
3156
|
+
});
|
|
3157
|
+
const buffer = result2.data instanceof Uint8Array ? Buffer.from(result2.data) : Buffer.from(new Uint8Array(result2.data));
|
|
3158
|
+
writeFileSync(outputPath, buffer);
|
|
3159
|
+
return { slideCount: result2.slideCount, format: result2.format };
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
const result = await compileFile();
|
|
3163
|
+
if (command === "watch" || isWatch) {
|
|
3164
|
+
fsWatch(resolvedInput, async () => {
|
|
3165
|
+
try {
|
|
3166
|
+
await compileFile();
|
|
3167
|
+
} catch {
|
|
3168
|
+
}
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
2352
3171
|
if (isJson) {
|
|
2353
3172
|
return {
|
|
2354
3173
|
exitCode: 0,
|
|
@@ -2358,16 +3177,18 @@ ${summary}${isStrict && report.warnings.length > 0 ? " (failed due to --strict)"
|
|
|
2358
3177
|
source: target,
|
|
2359
3178
|
output: outputPath,
|
|
2360
3179
|
slideCount: result.slideCount,
|
|
2361
|
-
format: result.format
|
|
3180
|
+
format: result.format,
|
|
3181
|
+
watching: command === "watch" || isWatch
|
|
2362
3182
|
},
|
|
2363
3183
|
null,
|
|
2364
3184
|
2
|
|
2365
3185
|
)
|
|
2366
3186
|
};
|
|
2367
3187
|
}
|
|
3188
|
+
const watchMsg = command === "watch" || isWatch ? " [Watching for changes...]" : "";
|
|
2368
3189
|
return {
|
|
2369
3190
|
exitCode: 0,
|
|
2370
|
-
output: `\u2713 Successfully compiled '${target}' \u2794 '${outputPath}' (${result.slideCount} native editable slides)`
|
|
3191
|
+
output: `\u2713 Successfully compiled '${target}' \u2794 '${outputPath}' (${result.slideCount} ${isHtml ? "interactive HTML slides" : "native editable slides"})${watchMsg}`
|
|
2371
3192
|
};
|
|
2372
3193
|
} catch (err) {
|
|
2373
3194
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2421,6 +3242,8 @@ export {
|
|
|
2421
3242
|
cleanFontFace,
|
|
2422
3243
|
parseInlineMarkdown,
|
|
2423
3244
|
PptxRenderer,
|
|
3245
|
+
HtmlRenderer,
|
|
3246
|
+
startDevServer,
|
|
2424
3247
|
VERSION,
|
|
2425
3248
|
printHelp,
|
|
2426
3249
|
runCli
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Server } from 'node:http';
|
|
1
2
|
export * from '@yumiamd/core';
|
|
2
3
|
export * from '@yumiamd/ast';
|
|
3
4
|
export * from '@yumiamd/parser';
|
|
@@ -5,12 +6,25 @@ export * from '@yumiamd/theme';
|
|
|
5
6
|
export * from '@yumiamd/layout';
|
|
6
7
|
export * from '@yumiamd/renderer';
|
|
7
8
|
export * from '@yumiamd/renderer-pptx';
|
|
9
|
+
export * from '@yumiamd/renderer-html';
|
|
8
10
|
|
|
9
|
-
declare const VERSION = "0.1.
|
|
11
|
+
declare const VERSION = "0.1.10";
|
|
10
12
|
declare function printHelp(): string;
|
|
11
13
|
declare function runCli(argv: string[]): Promise<{
|
|
12
14
|
exitCode: number;
|
|
13
15
|
output: string;
|
|
14
16
|
}>;
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
interface DevServerOptions {
|
|
19
|
+
port?: number;
|
|
20
|
+
open?: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface DevServerInstance {
|
|
23
|
+
server: Server;
|
|
24
|
+
port: number;
|
|
25
|
+
url: string;
|
|
26
|
+
close: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
declare function startDevServer(filePath: string, options?: DevServerOptions): Promise<DevServerInstance>;
|
|
29
|
+
|
|
30
|
+
export { type DevServerInstance, type DevServerOptions, VERSION, printHelp, runCli, startDevServer };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
DEFAULT_VIEWPORT,
|
|
3
3
|
DefaultLayoutEngine,
|
|
4
4
|
DefaultYumiaParser,
|
|
5
|
+
HtmlRenderer,
|
|
5
6
|
PptxRenderer,
|
|
6
7
|
THEME_REGISTRY,
|
|
7
8
|
VERSION,
|
|
@@ -35,12 +36,14 @@ import {
|
|
|
35
36
|
printHelp,
|
|
36
37
|
resolveTheme,
|
|
37
38
|
runCli,
|
|
39
|
+
startDevServer,
|
|
38
40
|
terminalTheme
|
|
39
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-PQYPVPV2.js";
|
|
40
42
|
export {
|
|
41
43
|
DEFAULT_VIEWPORT,
|
|
42
44
|
DefaultLayoutEngine,
|
|
43
45
|
DefaultYumiaParser,
|
|
46
|
+
HtmlRenderer,
|
|
44
47
|
PptxRenderer,
|
|
45
48
|
THEME_REGISTRY,
|
|
46
49
|
VERSION,
|
|
@@ -74,5 +77,6 @@ export {
|
|
|
74
77
|
printHelp,
|
|
75
78
|
resolveTheme,
|
|
76
79
|
runCli,
|
|
80
|
+
startDevServer,
|
|
77
81
|
terminalTheme
|
|
78
82
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yumiamd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Command line interface and compiler for YumiaMD presentations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,13 +20,14 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsup": "^8.5.1",
|
|
23
|
-
"@yumiamd/
|
|
24
|
-
"@yumiamd/
|
|
25
|
-
"@yumiamd/
|
|
26
|
-
"@yumiamd/
|
|
27
|
-
"@yumiamd/
|
|
28
|
-
"@yumiamd/renderer-pptx": "0.1.
|
|
29
|
-
"@yumiamd/
|
|
23
|
+
"@yumiamd/ast": "0.1.10",
|
|
24
|
+
"@yumiamd/layout": "0.1.10",
|
|
25
|
+
"@yumiamd/core": "0.1.10",
|
|
26
|
+
"@yumiamd/parser": "0.1.10",
|
|
27
|
+
"@yumiamd/renderer": "0.1.10",
|
|
28
|
+
"@yumiamd/renderer-pptx": "0.1.10",
|
|
29
|
+
"@yumiamd/renderer-html": "0.1.10",
|
|
30
|
+
"@yumiamd/theme": "0.1.10"
|
|
30
31
|
},
|
|
31
32
|
"keywords": [
|
|
32
33
|
"yumia",
|