@lemonriver/core 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -2
- package/bin.js +2 -0
- package/dist/build-f-fM2ZY4.js +19 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +478 -0
- package/dist/config-B6JaKJM4.d.ts +26 -0
- package/dist/config-DO45GU3e.js +3549 -0
- package/dist/design-Cpqx7SJs.js +34 -0
- package/dist/dev-CqS2xpgE.js +120 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.js +469 -0
- package/dist/locale/index.d.ts +18 -0
- package/dist/locale/index.js +13 -0
- package/dist/preview-BihgMlEl.js +24 -0
- package/dist/types-BjW4glip.d.ts +582 -0
- package/dist/update-C_R0ycVC.js +1026 -0
- package/dist/vite/index.d.ts +11 -0
- package/dist/vite/index.js +2 -0
- package/dist/zh-tw-Ci0jK8eg.js +2154 -0
- package/env.d.ts +59 -0
- package/package.json +108 -4
- package/skills/apply-comments/SKILL.md +82 -0
- package/skills/create-slide/SKILL.md +94 -0
- package/skills/create-theme/SKILL.md +253 -0
- package/skills/current-slide/SKILL.md +110 -0
- package/skills/slide-authoring/SKILL.md +373 -0
- package/skills/slide-authoring/references/assets.md +52 -0
- package/skills/slide-authoring/references/design-system.md +48 -0
- package/skills/slide-authoring/references/morph.md +67 -0
- package/skills/slide-authoring/references/page-numbers.md +16 -0
- package/skills/slide-authoring/references/steps.md +43 -0
- package/skills/slide-authoring/references/transitions.md +176 -0
- package/skills/slide-authoring/references/webfonts.md +39 -0
- package/src/app/app.tsx +66 -0
- package/src/app/assets/open-slide.png +0 -0
- package/src/app/components/asset-view.tsx +2120 -0
- package/src/app/components/board/board-canvas.tsx +439 -0
- package/src/app/components/board/board.css +75 -0
- package/src/app/components/board/layout.test.ts +109 -0
- package/src/app/components/board/layout.ts +133 -0
- package/src/app/components/board/page-node.tsx +204 -0
- package/src/app/components/board/use-board-columns.ts +149 -0
- package/src/app/components/board/use-page-order.ts +126 -0
- package/src/app/components/command/command-menu.tsx +229 -0
- package/src/app/components/command/command.tsx +144 -0
- package/src/app/components/command/home-command-menu.tsx +96 -0
- package/src/app/components/command/slide-command-menu.tsx +181 -0
- package/src/app/components/export-progress-toast.tsx +80 -0
- package/src/app/components/history-provider.tsx +162 -0
- package/src/app/components/icon-switcher-indicator.tsx +10 -0
- package/src/app/components/icon-tooltip.tsx +43 -0
- package/src/app/components/image-placeholder.tsx +235 -0
- package/src/app/components/inspector/arrange-panel.tsx +449 -0
- package/src/app/components/inspector/asset-picker-dialog.tsx +188 -0
- package/src/app/components/inspector/comment-widget.tsx +119 -0
- package/src/app/components/inspector/image-crop-dialog.tsx +215 -0
- package/src/app/components/inspector/inline-text-editor.tsx +684 -0
- package/src/app/components/inspector/inspect-overlay.tsx +684 -0
- package/src/app/components/inspector/inspector-panel.tsx +1105 -0
- package/src/app/components/inspector/inspector-provider.tsx +1616 -0
- package/src/app/components/inspector/save-bar.tsx +84 -0
- package/src/app/components/language-toggle.tsx +45 -0
- package/src/app/components/morph-element.tsx +43 -0
- package/src/app/components/note-markdown.test.tsx +42 -0
- package/src/app/components/note-markdown.tsx +85 -0
- package/src/app/components/notes-drawer.tsx +117 -0
- package/src/app/components/overview-grid.tsx +372 -0
- package/src/app/components/panel/editor-sidebar.tsx +50 -0
- package/src/app/components/panel/panel-fields.tsx +248 -0
- package/src/app/components/panel/panel-shell.tsx +63 -0
- package/src/app/components/panel/save-card.tsx +155 -0
- package/src/app/components/player.tsx +472 -0
- package/src/app/components/present/blackout-overlay.tsx +31 -0
- package/src/app/components/present/control-bar.tsx +326 -0
- package/src/app/components/present/help-overlay.tsx +57 -0
- package/src/app/components/present/jump-input.tsx +96 -0
- package/src/app/components/present/laser-pointer.tsx +62 -0
- package/src/app/components/present/progress-bar.tsx +26 -0
- package/src/app/components/present/use-idle.ts +46 -0
- package/src/app/components/present/use-pointer-near-bottom.ts +34 -0
- package/src/app/components/present/use-presenter-channel.ts +66 -0
- package/src/app/components/present/use-touch-swipe.ts +73 -0
- package/src/app/components/sidebar/folder-item.tsx +296 -0
- package/src/app/components/sidebar/icon-picker.tsx +61 -0
- package/src/app/components/sidebar/sidebar-footer.tsx +152 -0
- package/src/app/components/sidebar/sidebar.tsx +329 -0
- package/src/app/components/slide-canvas.tsx +98 -0
- package/src/app/components/slide-preload-layer.tsx +185 -0
- package/src/app/components/slide-transition-layer.tsx +827 -0
- package/src/app/components/style-panel/design-provider.tsx +135 -0
- package/src/app/components/style-panel/style-panel.tsx +292 -0
- package/src/app/components/style-panel/use-design.ts +85 -0
- package/src/app/components/theme-toggle.tsx +65 -0
- package/src/app/components/themes/theme-detail.tsx +308 -0
- package/src/app/components/themes/themes-gallery.tsx +154 -0
- package/src/app/components/thumbnail-rail-scroll.test.ts +60 -0
- package/src/app/components/thumbnail-rail-scroll.ts +33 -0
- package/src/app/components/thumbnail-rail.tsx +1000 -0
- package/src/app/components/ui/badge.tsx +48 -0
- package/src/app/components/ui/button.tsx +93 -0
- package/src/app/components/ui/card.tsx +92 -0
- package/src/app/components/ui/context-menu.tsx +247 -0
- package/src/app/components/ui/dialog.tsx +155 -0
- package/src/app/components/ui/dropdown-menu.tsx +267 -0
- package/src/app/components/ui/input.tsx +25 -0
- package/src/app/components/ui/label.tsx +21 -0
- package/src/app/components/ui/popover.tsx +82 -0
- package/src/app/components/ui/progress.tsx +33 -0
- package/src/app/components/ui/scroll-area.tsx +53 -0
- package/src/app/components/ui/select.tsx +195 -0
- package/src/app/components/ui/separator.tsx +26 -0
- package/src/app/components/ui/slider.tsx +68 -0
- package/src/app/components/ui/sonner.tsx +48 -0
- package/src/app/components/ui/tabs.tsx +79 -0
- package/src/app/components/ui/textarea.tsx +22 -0
- package/src/app/components/ui/toggle-group.tsx +84 -0
- package/src/app/components/ui/toggle.tsx +45 -0
- package/src/app/components/ui/tooltip.tsx +75 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/index.html +34 -0
- package/src/app/lib/asset-filter.test.ts +176 -0
- package/src/app/lib/asset-filter.ts +80 -0
- package/src/app/lib/assets.ts +289 -0
- package/src/app/lib/design-presets.ts +94 -0
- package/src/app/lib/design.ts +58 -0
- package/src/app/lib/dom.ts +42 -0
- package/src/app/lib/export-html.ts +367 -0
- package/src/app/lib/export-pdf.ts +281 -0
- package/src/app/lib/export-pptx.ts +183 -0
- package/src/app/lib/folders.ts +244 -0
- package/src/app/lib/inspector/fiber.test.ts +154 -0
- package/src/app/lib/inspector/fiber.ts +85 -0
- package/src/app/lib/inspector/geometry.test.ts +324 -0
- package/src/app/lib/inspector/geometry.ts +207 -0
- package/src/app/lib/inspector/inline-text.ts +31 -0
- package/src/app/lib/inspector/pick-target.ts +38 -0
- package/src/app/lib/inspector/text-edit-timeline.test.ts +121 -0
- package/src/app/lib/inspector/text-edit-timeline.ts +12 -0
- package/src/app/lib/inspector/text-selection.ts +117 -0
- package/src/app/lib/inspector/use-comments.ts +74 -0
- package/src/app/lib/inspector/use-editor.ts +73 -0
- package/src/app/lib/inspector/use-notes.ts +134 -0
- package/src/app/lib/inspector/use-visual-editor.ts +429 -0
- package/src/app/lib/inspector/visual-dom.ts +255 -0
- package/src/app/lib/keys.ts +30 -0
- package/src/app/lib/last-home-location.ts +18 -0
- package/src/app/lib/locale-store.ts +67 -0
- package/src/app/lib/note-markdown.test.ts +147 -0
- package/src/app/lib/note-markdown.ts +116 -0
- package/src/app/lib/page-context.tsx +38 -0
- package/src/app/lib/pptx/color.ts +49 -0
- package/src/app/lib/pptx/css.test.ts +205 -0
- package/src/app/lib/pptx/css.ts +462 -0
- package/src/app/lib/pptx/fonts.ts +145 -0
- package/src/app/lib/pptx/images.ts +116 -0
- package/src/app/lib/pptx/measure.ts +1281 -0
- package/src/app/lib/pptx/ooxml.test.ts +199 -0
- package/src/app/lib/pptx/ooxml.ts +470 -0
- package/src/app/lib/pptx/raster.ts +189 -0
- package/src/app/lib/pptx/render.ts +102 -0
- package/src/app/lib/pptx/scene.ts +116 -0
- package/src/app/lib/pptx/text-lines.test.ts +82 -0
- package/src/app/lib/pptx/text-lines.ts +109 -0
- package/src/app/lib/pptx/units.ts +22 -0
- package/src/app/lib/print-ready.test.ts +32 -0
- package/src/app/lib/print-ready.ts +49 -0
- package/src/app/lib/sdk.test.ts +13 -0
- package/src/app/lib/sdk.ts +37 -0
- package/src/app/lib/slides.ts +29 -0
- package/src/app/lib/step-context.tsx +269 -0
- package/src/app/lib/text-diff.ts +19 -0
- package/src/app/lib/themes.ts +22 -0
- package/src/app/lib/transition.test.ts +31 -0
- package/src/app/lib/transition.ts +43 -0
- package/src/app/lib/use-agent-socket.ts +18 -0
- package/src/app/lib/use-click-page-navigation.ts +56 -0
- package/src/app/lib/use-document-title.test.ts +14 -0
- package/src/app/lib/use-document-title.ts +16 -0
- package/src/app/lib/use-is-mobile.ts +9 -0
- package/src/app/lib/use-locale.ts +8 -0
- package/src/app/lib/use-media-query.ts +20 -0
- package/src/app/lib/use-prefers-reduced-motion.ts +11 -0
- package/src/app/lib/use-restart-server.ts +78 -0
- package/src/app/lib/use-slide-module.ts +48 -0
- package/src/app/lib/use-slide-titles.ts +29 -0
- package/src/app/lib/use-wheel-page-navigation.ts +99 -0
- package/src/app/lib/utils.test.ts +49 -0
- package/src/app/lib/utils.ts +14 -0
- package/src/app/main.tsx +14 -0
- package/src/app/routes/assets.tsx +9 -0
- package/src/app/routes/board.tsx +141 -0
- package/src/app/routes/home-shell.tsx +261 -0
- package/src/app/routes/home.tsx +856 -0
- package/src/app/routes/presenter.tsx +707 -0
- package/src/app/routes/slide.tsx +1273 -0
- package/src/app/routes/themes.tsx +33 -0
- package/src/app/styles.css +401 -0
- package/src/app/virtual.d.ts +51 -0
- package/src/locale/en.ts +562 -0
- package/src/locale/format.ts +12 -0
- package/src/locale/index.ts +6 -0
- package/src/locale/ja.ts +569 -0
- package/src/locale/types.ts +596 -0
- package/src/locale/zh-cn.ts +558 -0
- package/src/locale/zh-tw.ts +558 -0
package/README.md
CHANGED
|
@@ -1,3 +1,98 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lemonriver/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runtime and CLI for [LemonRiver](https://github.com/SegawaBeer/lemonriver) — a React-based slide framework where you write slides and the framework handles the Vite/React stack, layout, navigation, hot reload, and fullscreen play mode.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @lemonriver/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Most users get this installed automatically by running `npx @lemonriver/cli init`. Use this package directly only if you're wiring up an existing workspace by hand.
|
|
12
|
+
|
|
13
|
+
## What's inside
|
|
14
|
+
|
|
15
|
+
- **Runtime** — home page, slide viewer, thumbnail rail, keyboard navigation, and fullscreen presenter mode. Every slide renders into a fixed **1920×1080** canvas; the framework scales it.
|
|
16
|
+
- **Vite plugin** — discovers `slides/<id>/index.{tsx,jsx,ts,js}`, exposes them via virtual modules, and reloads when slides are added or removed.
|
|
17
|
+
- **CLI** — `lemonriver dev | build | preview` so workspaces never need to touch Vite, React, or tsconfig directly.
|
|
18
|
+
|
|
19
|
+
## CLI
|
|
20
|
+
|
|
21
|
+
Once installed, the `lemonriver` bin is available in the workspace:
|
|
22
|
+
|
|
23
|
+
| Command | Description |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `lemonriver dev` | Start the dev server. Flags: `-p, --port <port>`, `--host [host]`, `--open`. |
|
|
26
|
+
| `lemonriver build` | Build a static site. Flags: `--out-dir <dir>` (defaults to `dist`). |
|
|
27
|
+
| `lemonriver preview` | Preview the production build. Flags: `-p, --port <port>`, `--host [host]`, `--open`. |
|
|
28
|
+
|
|
29
|
+
## Config
|
|
30
|
+
|
|
31
|
+
Create `lemonriver.config.ts` in the workspace root (all fields optional):
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import type { OpenSlideConfig } from '@lemonriver/core';
|
|
35
|
+
|
|
36
|
+
const openSlideConfig: OpenSlideConfig = {
|
|
37
|
+
slidesDir: 'slides',
|
|
38
|
+
port: 5173,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default openSlideConfig;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Hosting under a subpath
|
|
45
|
+
|
|
46
|
+
Set `base` to deploy the built site under a sub-directory (intranet folders, GitHub Pages project sites, reverse proxies). Use a leading and trailing slash:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const openSlideConfig: OpenSlideConfig = {
|
|
50
|
+
base: '/my-slides/',
|
|
51
|
+
};
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The value is passed straight to Vite's `base` and to React Router's `basename`, so client-side navigation matches the deployed path.
|
|
55
|
+
|
|
56
|
+
## Authoring slides
|
|
57
|
+
|
|
58
|
+
Slides live under `slides/<kebab-case-id>/index.tsx` and default-export an array of `Page` components:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import type { Page } from '@lemonriver/core';
|
|
62
|
+
|
|
63
|
+
const Cover: Page = () => (
|
|
64
|
+
<div className="flex h-full w-full items-center justify-center">
|
|
65
|
+
<h1 className="text-[120px] font-bold">Hello, LemonRiver</h1>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const pages: Page[] = [Cover];
|
|
70
|
+
export default pages;
|
|
71
|
+
|
|
72
|
+
export const meta = { title: 'Hello' };
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Exports
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import {
|
|
79
|
+
CANVAS_WIDTH, // 1920
|
|
80
|
+
CANVAS_HEIGHT, // 1080
|
|
81
|
+
MorphElement, // match or fade objects across pages for morph transitions
|
|
82
|
+
type Page,
|
|
83
|
+
type SlideMeta,
|
|
84
|
+
type SlideModule,
|
|
85
|
+
type SlideTransition,
|
|
86
|
+
type OpenSlideConfig,
|
|
87
|
+
} from '@lemonriver/core';
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The Vite plugin is exposed under a subpath for advanced setups:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { createViteConfig } from '@lemonriver/core/vite';
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT
|
package/bin.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { r as printHeader, t as createCliLogger } from "./cli/bin.js";
|
|
2
|
+
import { t as createViteConfig } from "./config-DO45GU3e.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { build as build$1, mergeConfig } from "vite";
|
|
5
|
+
//#region src/cli/build.ts
|
|
6
|
+
async function build(opts = {}) {
|
|
7
|
+
printHeader("building for production");
|
|
8
|
+
const base = await createViteConfig({
|
|
9
|
+
userCwd: process.cwd(),
|
|
10
|
+
mode: "build"
|
|
11
|
+
});
|
|
12
|
+
const config = mergeConfig(base, {
|
|
13
|
+
customLogger: createCliLogger(),
|
|
14
|
+
build: { ...opts.outDir !== void 0 ? { outDir: path.resolve(process.cwd(), opts.outDir) } : {} }
|
|
15
|
+
});
|
|
16
|
+
await build$1(config);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { build };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli/bin.js
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { I as isFrameworkSpecifier, N as BIN_NAME, t as detectPackageManager } from "../update-C_R0ycVC.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import * as readline from "node:readline/promises";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { Command, Option } from "commander";
|
|
8
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
9
|
+
import { cp, lstat, mkdir, readFile, readdir, readlink, rm, symlink } from "node:fs/promises";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import * as readline$1 from "node:readline";
|
|
12
|
+
import { stripVTControlCharacters } from "node:util";
|
|
13
|
+
//#region src/cli/preflight.ts
|
|
14
|
+
const VITE_CONSUMERS = ["@vitejs/plugin-react", "@tailwindcss/vite"];
|
|
15
|
+
const DEPENDENCY_FIELDS = [
|
|
16
|
+
"dependencies",
|
|
17
|
+
"devDependencies",
|
|
18
|
+
"optionalDependencies",
|
|
19
|
+
"peerDependencies"
|
|
20
|
+
];
|
|
21
|
+
const PROJECT_ROOT_MARKERS = [
|
|
22
|
+
"pnpm-workspace.yaml",
|
|
23
|
+
"pnpm-lock.yaml",
|
|
24
|
+
"yarn.lock",
|
|
25
|
+
"bun.lock",
|
|
26
|
+
"bun.lockb",
|
|
27
|
+
"package-lock.json",
|
|
28
|
+
".git"
|
|
29
|
+
];
|
|
30
|
+
function locatePackage(name, fromDir) {
|
|
31
|
+
let dir = fromDir;
|
|
32
|
+
while (true) {
|
|
33
|
+
const candidate = path.join(dir, "node_modules", name);
|
|
34
|
+
const pkgPath = path.join(candidate, "package.json");
|
|
35
|
+
if (existsSync(pkgPath)) {
|
|
36
|
+
let version = "unknown";
|
|
37
|
+
try {
|
|
38
|
+
version = JSON.parse(readFileSync(pkgPath, "utf8")).version ?? version;
|
|
39
|
+
} catch {}
|
|
40
|
+
return {
|
|
41
|
+
dir: realpathSync(candidate),
|
|
42
|
+
root: dir,
|
|
43
|
+
version
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const parent = path.dirname(dir);
|
|
47
|
+
if (parent === dir) return null;
|
|
48
|
+
dir = parent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function findViteMismatch(coreDir) {
|
|
52
|
+
const coreVite = locatePackage("vite", coreDir);
|
|
53
|
+
if (!coreVite) return null;
|
|
54
|
+
for (const consumer of VITE_CONSUMERS) {
|
|
55
|
+
const consumerPkg = locatePackage(consumer, coreDir);
|
|
56
|
+
if (!consumerPkg) continue;
|
|
57
|
+
const consumerVite = locatePackage("vite", consumerPkg.dir);
|
|
58
|
+
if (!consumerVite || consumerVite.version === coreVite.version) continue;
|
|
59
|
+
return {
|
|
60
|
+
consumer,
|
|
61
|
+
coreVite,
|
|
62
|
+
consumerVite
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function declarationIn(dir) {
|
|
68
|
+
const file = path.join(dir, "package.json");
|
|
69
|
+
if (!existsSync(file)) return null;
|
|
70
|
+
try {
|
|
71
|
+
const pkg = JSON.parse(readFileSync(file, "utf8"));
|
|
72
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
73
|
+
const deps = pkg[field];
|
|
74
|
+
if (deps && Object.hasOwn(deps, "vite")) return {
|
|
75
|
+
file,
|
|
76
|
+
field
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
} catch {}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
function isProjectRoot(dir) {
|
|
83
|
+
return PROJECT_ROOT_MARKERS.some((marker) => existsSync(path.join(dir, marker)));
|
|
84
|
+
}
|
|
85
|
+
function isAncestorOrSelf(ancestor, dir) {
|
|
86
|
+
const rel = path.relative(ancestor, dir);
|
|
87
|
+
return !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
88
|
+
}
|
|
89
|
+
function findViteDeclaration(cwd, installRoot) {
|
|
90
|
+
if (installRoot && (isAncestorOrSelf(installRoot, cwd) || isAncestorOrSelf(cwd, installRoot))) {
|
|
91
|
+
const owner = declarationIn(installRoot);
|
|
92
|
+
if (owner) return owner;
|
|
93
|
+
}
|
|
94
|
+
const stopAt = installRoot && isAncestorOrSelf(installRoot, cwd) ? installRoot : null;
|
|
95
|
+
let dir = cwd;
|
|
96
|
+
while (dir !== stopAt) {
|
|
97
|
+
const found = declarationIn(dir);
|
|
98
|
+
if (found) return found;
|
|
99
|
+
const parent = path.dirname(dir);
|
|
100
|
+
if (parent === dir || !stopAt && isProjectRoot(dir)) return null;
|
|
101
|
+
dir = parent;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function removeCommand(pm) {
|
|
106
|
+
return pm === "npm" ? "npm uninstall vite" : `${pm} remove vite`;
|
|
107
|
+
}
|
|
108
|
+
function formatViteMismatch(mismatch, { cwd = process.cwd(), packageManager = "npm", declaration = null } = {}) {
|
|
109
|
+
const rel = (p) => path.relative(cwd, p) || p;
|
|
110
|
+
const want = chalk.bold(`vite@${mismatch.coreVite.version}`);
|
|
111
|
+
const got = chalk.bold(`vite@${mismatch.consumerVite.version}`);
|
|
112
|
+
const from = chalk.dim(rel(mismatch.consumerVite.dir));
|
|
113
|
+
const command = (cmd) => [
|
|
114
|
+
"",
|
|
115
|
+
` ${chalk.dim("$")} ${chalk.cyan(cmd)}`,
|
|
116
|
+
""
|
|
117
|
+
];
|
|
118
|
+
if (!declaration) return [
|
|
119
|
+
chalk.bold("Conflicting vite versions in node_modules"),
|
|
120
|
+
"",
|
|
121
|
+
`LemonRiver ships ${want}, but ${mismatch.consumer} is loading ${got}`,
|
|
122
|
+
`from ${from}. Make sure no package.json lists vite, then reinstall:`,
|
|
123
|
+
...command(`${packageManager} install`)
|
|
124
|
+
].join("\n");
|
|
125
|
+
const dir = path.dirname(declaration.file);
|
|
126
|
+
const cd = path.resolve(dir) === path.resolve(cwd) ? "" : `cd ${rel(dir)} && `;
|
|
127
|
+
return [
|
|
128
|
+
chalk.bold(`Remove ${chalk.yellow("vite")} from your package.json`),
|
|
129
|
+
"",
|
|
130
|
+
`${chalk.bold(declaration.field)} in ${chalk.dim(rel(declaration.file))} still lists vite, a leftover from v1.`,
|
|
131
|
+
`It shadows the ${want} bundled with LemonRiver: ${mismatch.consumer}`,
|
|
132
|
+
`is loading ${got} from ${from} instead.`,
|
|
133
|
+
...command(cd + removeCommand(packageManager))
|
|
134
|
+
].join("\n");
|
|
135
|
+
}
|
|
136
|
+
async function assertViteResolvesToCore(cwd = process.cwd()) {
|
|
137
|
+
const mismatch = findViteMismatch(realpathSync(path.dirname(fileURLToPath(import.meta.url))));
|
|
138
|
+
if (!mismatch) return;
|
|
139
|
+
const declaration = findViteDeclaration(cwd, mismatch.consumerVite.root);
|
|
140
|
+
const packageManager = await detectPackageManager(declaration ? path.dirname(declaration.file) : cwd);
|
|
141
|
+
throw new Error(formatViteMismatch(mismatch, {
|
|
142
|
+
cwd,
|
|
143
|
+
packageManager,
|
|
144
|
+
declaration
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/cli/sync.ts
|
|
149
|
+
async function detectSkillsDrift(skillsDir) {
|
|
150
|
+
if (!existsSync(skillsDir)) return [];
|
|
151
|
+
const cwd = process.cwd();
|
|
152
|
+
const agentsSkillsDir = path.join(cwd, ".agents", "skills");
|
|
153
|
+
const skillNames = (await readdir(skillsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
154
|
+
const results = [];
|
|
155
|
+
for (const name of skillNames) {
|
|
156
|
+
const src = path.join(skillsDir, name);
|
|
157
|
+
const dst = path.join(agentsSkillsDir, name);
|
|
158
|
+
const srcHash = await hashDir(src);
|
|
159
|
+
const dstHash = existsSync(dst) ? await hashDir(dst) : null;
|
|
160
|
+
let status;
|
|
161
|
+
if (dstHash === null) status = "added";
|
|
162
|
+
else if (dstHash !== srcHash) status = "updated";
|
|
163
|
+
else status = "unchanged";
|
|
164
|
+
results.push({
|
|
165
|
+
name,
|
|
166
|
+
status
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return results;
|
|
170
|
+
}
|
|
171
|
+
async function syncSkills(skillsDir, opts = {}) {
|
|
172
|
+
const { dryRun = false } = opts;
|
|
173
|
+
if (!existsSync(skillsDir)) throw new Error(`Built-in skills directory missing at ${skillsDir}. The @lemonriver/core package may be corrupt — try reinstalling.`);
|
|
174
|
+
const cwd = process.cwd();
|
|
175
|
+
const agentsSkillsDir = path.join(cwd, ".agents", "skills");
|
|
176
|
+
const claudeSkillsDir = path.join(cwd, ".claude", "skills");
|
|
177
|
+
const results = await detectSkillsDrift(skillsDir);
|
|
178
|
+
if (results.length === 0) {
|
|
179
|
+
process.stdout.write(chalk.yellow("No skills found to sync.\n"));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
for (const { name, status } of results) {
|
|
183
|
+
const src = path.join(skillsDir, name);
|
|
184
|
+
const dst = path.join(agentsSkillsDir, name);
|
|
185
|
+
if (dryRun) continue;
|
|
186
|
+
if (status === "unchanged") {
|
|
187
|
+
await ensureClaudeSymlink(claudeSkillsDir, name);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
await mkdir(path.dirname(dst), { recursive: true });
|
|
191
|
+
if (existsSync(dst)) await rm(dst, {
|
|
192
|
+
recursive: true,
|
|
193
|
+
force: true
|
|
194
|
+
});
|
|
195
|
+
await cp(src, dst, { recursive: true });
|
|
196
|
+
await ensureClaudeSymlink(claudeSkillsDir, name);
|
|
197
|
+
}
|
|
198
|
+
printSummary(results, dryRun);
|
|
199
|
+
}
|
|
200
|
+
async function ensureClaudeSymlink(claudeSkillsDir, name) {
|
|
201
|
+
await mkdir(claudeSkillsDir, { recursive: true });
|
|
202
|
+
const linkPath = path.join(claudeSkillsDir, name);
|
|
203
|
+
const target = path.join("..", "..", ".agents", "skills", name);
|
|
204
|
+
if (existsSync(linkPath)) try {
|
|
205
|
+
if ((await lstat(linkPath)).isSymbolicLink()) {
|
|
206
|
+
if (await readlink(linkPath) === target) return;
|
|
207
|
+
}
|
|
208
|
+
await rm(linkPath, {
|
|
209
|
+
recursive: true,
|
|
210
|
+
force: true
|
|
211
|
+
});
|
|
212
|
+
} catch {
|
|
213
|
+
await rm(linkPath, {
|
|
214
|
+
recursive: true,
|
|
215
|
+
force: true
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
await symlink(target, linkPath, "dir");
|
|
220
|
+
} catch (err) {
|
|
221
|
+
const code = err.code;
|
|
222
|
+
if (code === "EPERM" || code === "EEXIST") {
|
|
223
|
+
const absoluteTarget = path.resolve(claudeSkillsDir, target);
|
|
224
|
+
await cp(absoluteTarget, linkPath, { recursive: true });
|
|
225
|
+
} else throw err;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function hashDir(dir) {
|
|
229
|
+
const hash = createHash("sha256");
|
|
230
|
+
const files = await collectFiles(dir);
|
|
231
|
+
files.sort();
|
|
232
|
+
for (const rel of files) {
|
|
233
|
+
const abs = path.join(dir, rel);
|
|
234
|
+
const data = await readFile(abs);
|
|
235
|
+
hash.update(rel);
|
|
236
|
+
hash.update("\0");
|
|
237
|
+
hash.update(data);
|
|
238
|
+
hash.update("\0");
|
|
239
|
+
}
|
|
240
|
+
return hash.digest("hex");
|
|
241
|
+
}
|
|
242
|
+
async function collectFiles(dir, prefix = "") {
|
|
243
|
+
const out = [];
|
|
244
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
245
|
+
for (const entry of entries) {
|
|
246
|
+
const rel = prefix ? path.join(prefix, entry.name) : entry.name;
|
|
247
|
+
if (entry.isDirectory()) out.push(...await collectFiles(path.join(dir, entry.name), rel));
|
|
248
|
+
else if (entry.isFile()) out.push(rel);
|
|
249
|
+
}
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
252
|
+
function printSummary(results, dryRun) {
|
|
253
|
+
const symbols = {
|
|
254
|
+
added: chalk.green("+"),
|
|
255
|
+
updated: chalk.yellow("~"),
|
|
256
|
+
unchanged: chalk.dim("=")
|
|
257
|
+
};
|
|
258
|
+
const labels = {
|
|
259
|
+
added: chalk.green("added"),
|
|
260
|
+
updated: chalk.yellow("updated"),
|
|
261
|
+
unchanged: chalk.dim("unchanged")
|
|
262
|
+
};
|
|
263
|
+
const header = dryRun ? chalk.bold("Dry run — no files written:") : chalk.bold("Synced built-in skills:");
|
|
264
|
+
process.stdout.write(`${header}\n`);
|
|
265
|
+
for (const { name, status } of results) process.stdout.write(` ${symbols[status]} ${name} ${chalk.dim(`(${labels[status]})`)}\n`);
|
|
266
|
+
const counts = results.reduce((acc, { status }) => {
|
|
267
|
+
acc[status] += 1;
|
|
268
|
+
return acc;
|
|
269
|
+
}, {
|
|
270
|
+
added: 0,
|
|
271
|
+
updated: 0,
|
|
272
|
+
unchanged: 0
|
|
273
|
+
});
|
|
274
|
+
process.stdout.write(chalk.dim(`\n${counts.added} added, ${counts.updated} updated, ${counts.unchanged} unchanged.\n`));
|
|
275
|
+
}
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/cli/ui.ts
|
|
278
|
+
const UNICODE = process.platform !== "win32" || Boolean(process.env.WT_SESSION) || process.env.TERM_PROGRAM === "vscode";
|
|
279
|
+
const glyph = {
|
|
280
|
+
bar: UNICODE ? "┃" : "|",
|
|
281
|
+
warn: UNICODE ? "▲" : "!",
|
|
282
|
+
cross: UNICODE ? "✖" : "x"
|
|
283
|
+
};
|
|
284
|
+
function readVersion() {
|
|
285
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
286
|
+
while (dir !== path.dirname(dir)) {
|
|
287
|
+
const pkgPath = path.join(dir, "package.json");
|
|
288
|
+
if (existsSync(pkgPath)) {
|
|
289
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
290
|
+
if (pkg.name && isFrameworkSpecifier(pkg.name) && pkg.version) return pkg.version;
|
|
291
|
+
}
|
|
292
|
+
dir = path.dirname(dir);
|
|
293
|
+
}
|
|
294
|
+
return "0.0.0";
|
|
295
|
+
}
|
|
296
|
+
function brand() {
|
|
297
|
+
return chalk.inverse.bold(` ${BIN_NAME} `);
|
|
298
|
+
}
|
|
299
|
+
function printHeader(status) {
|
|
300
|
+
const parts = [brand(), chalk.dim(`v${readVersion()}`)];
|
|
301
|
+
if (status) parts.push(chalk.dim(status));
|
|
302
|
+
process.stdout.write(`\n ${parts.join(" ")}\n\n`);
|
|
303
|
+
}
|
|
304
|
+
function startupDuration() {
|
|
305
|
+
return `ready in ${Math.round(process.uptime() * 1e3)} ms`;
|
|
306
|
+
}
|
|
307
|
+
function printUrls(urls, host) {
|
|
308
|
+
if (!urls) return;
|
|
309
|
+
const rows = [];
|
|
310
|
+
for (const url of urls.local) rows.push(urlRow("Local", chalk.cyan(url)));
|
|
311
|
+
urls.network.forEach((url, index) => {
|
|
312
|
+
const iface = urls.networkInterfaceNames?.[index];
|
|
313
|
+
rows.push(urlRow("Network", chalk.cyan(url) + (iface ? ` ${chalk.dim(iface)}` : "")));
|
|
314
|
+
});
|
|
315
|
+
if (urls.network.length === 0 && host === void 0) rows.push(urlRow("Network", chalk.dim("use --host to expose")));
|
|
316
|
+
process.stdout.write(`${rows.join("\n")}\n`);
|
|
317
|
+
}
|
|
318
|
+
function urlRow(label, value) {
|
|
319
|
+
return ` ${chalk.dim(glyph.bar)} ${chalk.bold(label.padEnd(8))} ${value}`;
|
|
320
|
+
}
|
|
321
|
+
function shortcutsEnabled() {
|
|
322
|
+
return Boolean(process.stdin.isTTY) && !process.env.CI;
|
|
323
|
+
}
|
|
324
|
+
function printShortcutsHint() {
|
|
325
|
+
process.stdout.write(`\n ${chalk.dim("press")} ${chalk.bold("h + enter")} ${chalk.dim("for shortcuts")}\n`);
|
|
326
|
+
}
|
|
327
|
+
function formatError(message) {
|
|
328
|
+
const [first, ...rest] = message.split("\n");
|
|
329
|
+
const body = rest.map((line) => line ? `\n ${line}` : "\n").join("");
|
|
330
|
+
return `\n ${chalk.red(glyph.cross)} ${first}${body}\n\n`;
|
|
331
|
+
}
|
|
332
|
+
const DROPPED = [/^vite v\d+\.\d+\.\d+ building /];
|
|
333
|
+
const REWRITES = [
|
|
334
|
+
[/hmr update /, "updated "],
|
|
335
|
+
[/hmr invalidate /, "invalidated "],
|
|
336
|
+
[/trigger page reload /, "full reload "],
|
|
337
|
+
[/page reload/, "full reload"],
|
|
338
|
+
[/server restarted\./, "server restarted"],
|
|
339
|
+
[/optimized dependencies changed\. reloading/, "dependencies changed, reloading"],
|
|
340
|
+
[/Re-optimizing dependencies because vite config has changed/, "config changed, re-bundling"],
|
|
341
|
+
[/Re-optimizing dependencies because lockfile has changed/, "lockfile changed, re-bundling"],
|
|
342
|
+
[/Forced re-optimization of dependencies/, "re-bundling dependencies"],
|
|
343
|
+
[/\[plugin builtin:vite-[a-z-]+\] ?\n?/, ""],
|
|
344
|
+
[/\[vite\] ?/g, ""]
|
|
345
|
+
];
|
|
346
|
+
function rewriteViteMessage(msg) {
|
|
347
|
+
const plain = stripVTControlCharacters(msg);
|
|
348
|
+
if (DROPPED.some((re) => re.test(plain))) return null;
|
|
349
|
+
let out = msg;
|
|
350
|
+
for (const [re, replacement] of REWRITES) out = out.replace(re, replacement);
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
function timestamp() {
|
|
354
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hourCycle: "h23" });
|
|
355
|
+
}
|
|
356
|
+
function formatLine(type, text, opts) {
|
|
357
|
+
if (!opts?.timestamp) return text;
|
|
358
|
+
const mark = type === "warn" ? `${chalk.yellow(glyph.warn)} ` : type === "error" ? `${chalk.red(glyph.cross)} ` : "";
|
|
359
|
+
return ` ${chalk.dim(timestamp())} ${mark}${text}`;
|
|
360
|
+
}
|
|
361
|
+
function clearTerminal() {
|
|
362
|
+
if (!process.stdout.isTTY || process.env.CI) return;
|
|
363
|
+
const blank = "\n".repeat(Math.max(process.stdout.rows - 2, 0));
|
|
364
|
+
console.log(blank);
|
|
365
|
+
readline$1.cursorTo(process.stdout, 0, 0);
|
|
366
|
+
readline$1.clearScreenDown(process.stdout);
|
|
367
|
+
}
|
|
368
|
+
function createCliLogger() {
|
|
369
|
+
const loggedErrors = /* @__PURE__ */ new WeakSet();
|
|
370
|
+
const warnedOnce = /* @__PURE__ */ new Set();
|
|
371
|
+
const write = (type, msg, opts) => {
|
|
372
|
+
const text = rewriteViteMessage(msg);
|
|
373
|
+
if (text === null) return;
|
|
374
|
+
console[type === "info" ? "log" : type](formatLine(type, text, opts));
|
|
375
|
+
};
|
|
376
|
+
const logger = {
|
|
377
|
+
hasWarned: false,
|
|
378
|
+
info(msg, opts) {
|
|
379
|
+
write("info", msg, opts);
|
|
380
|
+
},
|
|
381
|
+
warn(msg, opts) {
|
|
382
|
+
logger.hasWarned = true;
|
|
383
|
+
write("warn", msg, opts);
|
|
384
|
+
},
|
|
385
|
+
warnOnce(msg, opts) {
|
|
386
|
+
if (warnedOnce.has(msg)) return;
|
|
387
|
+
warnedOnce.add(msg);
|
|
388
|
+
logger.warn(msg, opts);
|
|
389
|
+
},
|
|
390
|
+
error(msg, opts) {
|
|
391
|
+
logger.hasWarned = true;
|
|
392
|
+
if (opts?.error) loggedErrors.add(opts.error);
|
|
393
|
+
write("error", msg, opts);
|
|
394
|
+
},
|
|
395
|
+
clearScreen() {
|
|
396
|
+
clearTerminal();
|
|
397
|
+
},
|
|
398
|
+
hasErrorLogged(error) {
|
|
399
|
+
return loggedErrors.has(error);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
return logger;
|
|
403
|
+
}
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/cli/run.ts
|
|
406
|
+
function parsePort(value) {
|
|
407
|
+
const n = Number(value);
|
|
408
|
+
if (!Number.isInteger(n) || n < 0 || n > 65535) throw new Error(`Invalid port: ${value}`);
|
|
409
|
+
return n;
|
|
410
|
+
}
|
|
411
|
+
async function runSkillsDriftCheck(skillsDir) {
|
|
412
|
+
if (process.env.OPEN_SLIDE_SKIP_SKILLS_CHECK === "1") return;
|
|
413
|
+
let drift;
|
|
414
|
+
try {
|
|
415
|
+
drift = await detectSkillsDrift(skillsDir);
|
|
416
|
+
} catch {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const stale = drift.filter((d) => d.status !== "unchanged");
|
|
420
|
+
if (stale.length === 0) return;
|
|
421
|
+
const names = stale.map((d) => d.name).join(", ");
|
|
422
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
423
|
+
const notice = `${chalk.yellow(glyph.warn)} Built-in skills are out of date: ${chalk.bold(names)}`;
|
|
424
|
+
if (!interactive) {
|
|
425
|
+
process.stderr.write(`\n ${notice}\n ${chalk.dim("Run `lemonriver sync:skills` to update.")}\n`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const rl = readline.createInterface({
|
|
429
|
+
input: process.stdin,
|
|
430
|
+
output: process.stdout
|
|
431
|
+
});
|
|
432
|
+
try {
|
|
433
|
+
const answer = (await rl.question(`\n ${notice}\n Sync now? ${chalk.dim("(Y/n)")} `)).trim().toLowerCase();
|
|
434
|
+
if (answer === "" || answer === "y" || answer === "yes") {
|
|
435
|
+
process.stdout.write("\n");
|
|
436
|
+
await syncSkills(skillsDir);
|
|
437
|
+
} else process.stdout.write(chalk.dim(" Skipped. Run `lemonriver sync:skills` later to update.\n"));
|
|
438
|
+
} finally {
|
|
439
|
+
rl.close();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function resolveBuiltinSkillsDir() {
|
|
443
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
444
|
+
return path.resolve(here, "..", "..", "skills");
|
|
445
|
+
}
|
|
446
|
+
async function run(argv) {
|
|
447
|
+
const program = new Command();
|
|
448
|
+
program.name("lemonriver").description("Author slides in React — LemonRiver runs the rest.").version(readVersion(), "-v, --version", "print version").helpOption("-h, --help", "show help").showHelpAfterError(chalk.dim("(run `lemonriver --help` for usage)"));
|
|
449
|
+
program.command("dev").description("Start the dev server").addOption(new Option("-p, --port <port>", "port to listen on").argParser(parsePort)).addOption(new Option("--host [host]", "expose on the network (optional host)")).option("--open", "open the browser on start").option("--no-skills-check", "skip the built-in skills drift check").action(async (flags) => {
|
|
450
|
+
if (flags.skillsCheck !== false) await runSkillsDriftCheck(resolveBuiltinSkillsDir());
|
|
451
|
+
await assertViteResolvesToCore();
|
|
452
|
+
const { dev } = await import("../dev-CqS2xpgE.js");
|
|
453
|
+
await dev(flags);
|
|
454
|
+
});
|
|
455
|
+
program.command("build").description("Build a static site").option("--out-dir <dir>", "output directory (defaults to `dist`)").action(async (flags) => {
|
|
456
|
+
await assertViteResolvesToCore();
|
|
457
|
+
const { build } = await import("../build-f-fM2ZY4.js");
|
|
458
|
+
await build(flags);
|
|
459
|
+
});
|
|
460
|
+
program.command("preview").description("Preview the production build").addOption(new Option("-p, --port <port>", "port to listen on").argParser(parsePort)).addOption(new Option("--host [host]", "expose on the network (optional host)")).option("--open", "open the browser on start").action(async (flags) => {
|
|
461
|
+
await assertViteResolvesToCore();
|
|
462
|
+
const { preview } = await import("../preview-BihgMlEl.js");
|
|
463
|
+
await preview(flags);
|
|
464
|
+
});
|
|
465
|
+
program.command("sync:skills").description("Sync built-in skills from @lemonriver/core into this workspace").option("--dry-run", "show what would change without writing").action(async (flags) => {
|
|
466
|
+
await syncSkills(resolveBuiltinSkillsDir(), flags);
|
|
467
|
+
});
|
|
468
|
+
await program.parseAsync(argv, { from: "user" });
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/cli/bin.ts
|
|
472
|
+
run(process.argv.slice(2)).catch((err) => {
|
|
473
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
474
|
+
process.stderr.write(formatError(message));
|
|
475
|
+
process.exit(1);
|
|
476
|
+
});
|
|
477
|
+
//#endregion
|
|
478
|
+
export { printUrls as a, printShortcutsHint as i, formatError as n, shortcutsEnabled as o, printHeader as r, startupDuration as s, createCliLogger as t };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { t as Locale } from "./types-BjW4glip.js";
|
|
2
|
+
//#region src/config.d.ts
|
|
3
|
+
type LemonRiverBuildConfig = {
|
|
4
|
+
showSlideBrowser?: boolean;
|
|
5
|
+
showSlideUi?: boolean;
|
|
6
|
+
allowHtmlDownload?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type LemonRiverConfig = {
|
|
9
|
+
base?: string;
|
|
10
|
+
slidesDir?: string;
|
|
11
|
+
themesDir?: string;
|
|
12
|
+
assetsDir?: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
allowedHosts?: string[] | true;
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Pick the UI language from the language switcher in the slide UI
|
|
17
|
+
* instead. When set, this only seeds the initial language until the user
|
|
18
|
+
* chooses one (their choice is then remembered locally).
|
|
19
|
+
*/
|
|
20
|
+
locale?: Locale;
|
|
21
|
+
build?: LemonRiverBuildConfig;
|
|
22
|
+
};
|
|
23
|
+
/** @deprecated Renamed to {@link LemonRiverConfig}; kept for config files written before the rename. */
|
|
24
|
+
type OpenSlideConfig = LemonRiverConfig;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { LemonRiverConfig as n, OpenSlideConfig as r, LemonRiverBuildConfig as t };
|