@lemonriver/core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{build-f-fM2ZY4.js → build-Df_xwIpg.js} +1 -1
  3. package/dist/cli/bin.js +4 -4
  4. package/dist/{config-DO45GU3e.js → config-BjWCLoMQ.js} +2343 -363
  5. package/dist/{config-B6JaKJM4.d.ts → config-rMGCkz6i.d.ts} +6 -2
  6. package/dist/{dev-CqS2xpgE.js → dev-Ctsx9IeO.js} +2 -2
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +4 -3
  9. package/dist/locale/index.d.ts +5 -2
  10. package/dist/locale/index.js +2 -2
  11. package/dist/{preview-BihgMlEl.js → preview-CJgcqsPk.js} +1 -1
  12. package/dist/{types-BjW4glip.d.ts → types-8VUyA2bu.d.ts} +133 -5
  13. package/dist/{update-C_R0ycVC.js → update-BKOA3Z6o.js} +153 -21
  14. package/dist/vite/index.d.ts +1 -1
  15. package/dist/vite/index.js +1 -1
  16. package/dist/{zh-tw-Ci0jK8eg.js → zh-tw-CeLrYwRE.js} +1178 -25
  17. package/package.json +21 -15
  18. package/skills/apply-comments/SKILL.md +10 -6
  19. package/skills/create-slide/SKILL.md +2 -0
  20. package/skills/current-slide/SKILL.md +1 -0
  21. package/skills/slide-authoring/SKILL.md +31 -1
  22. package/src/agent/prompt.ts +141 -0
  23. package/src/agent/provider.ts +123 -0
  24. package/src/agent/types.ts +151 -0
  25. package/src/app/app.tsx +2 -0
  26. package/src/app/assets/open-slide.svg +39 -0
  27. package/src/app/components/agent/agent-dock.tsx +35 -0
  28. package/src/app/components/agent/agent-markdown.tsx +45 -0
  29. package/src/app/components/agent/agent-panel.tsx +692 -0
  30. package/src/app/components/agent/agent-presence.tsx +74 -0
  31. package/src/app/components/agent/composer.tsx +120 -0
  32. package/src/app/components/agent/connect-view.tsx +184 -0
  33. package/src/app/components/agent/history-view.tsx +135 -0
  34. package/src/app/components/agent/provider-logo.tsx +34 -0
  35. package/src/app/components/agent/transcript.tsx +609 -0
  36. package/src/app/components/board/board-canvas.tsx +143 -12
  37. package/src/app/components/board/board.css +51 -0
  38. package/src/app/components/board/layout.test.ts +53 -1
  39. package/src/app/components/board/layout.ts +50 -2
  40. package/src/app/components/board/page-node.tsx +287 -13
  41. package/src/app/components/board/use-board-cards.ts +210 -0
  42. package/src/app/components/board/use-page-order.ts +17 -9
  43. package/src/app/components/command/slide-command-menu.tsx +10 -0
  44. package/src/app/components/icon-switcher-indicator.tsx +1 -1
  45. package/src/app/components/inspector/comment-layer.tsx +1011 -0
  46. package/src/app/components/inspector/image-crop-dialog.tsx +1 -5
  47. package/src/app/components/inspector/inspect-overlay.tsx +2 -39
  48. package/src/app/components/inspector/inspector-panel.tsx +10 -93
  49. package/src/app/components/inspector/inspector-provider.tsx +121 -47
  50. package/src/app/components/panel/editor-sidebar.tsx +85 -16
  51. package/src/app/components/panel/panel-shell.tsx +2 -2
  52. package/src/app/components/panel/resize-handle.tsx +129 -0
  53. package/src/app/components/player.tsx +0 -1
  54. package/src/app/components/preview-step-host.tsx +16 -0
  55. package/src/app/components/sidebar/sidebar.tsx +2 -2
  56. package/src/app/components/style-panel/style-panel.tsx +3 -2
  57. package/src/app/components/thumbnail-rail.tsx +76 -1
  58. package/src/app/favicon.ico +0 -0
  59. package/src/app/favicon.svg +39 -0
  60. package/src/app/index.html +2 -1
  61. package/src/app/lib/agent/use-agent.ts +543 -0
  62. package/src/app/lib/inspector/freeze-motion.ts +61 -0
  63. package/src/app/lib/inspector/use-comments.ts +32 -3
  64. package/src/app/lib/locale-store.ts +6 -1
  65. package/src/app/lib/print-ready.ts +9 -6
  66. package/src/app/lib/use-auto-grow.ts +15 -0
  67. package/src/app/lib/utils.ts +4 -0
  68. package/src/app/routes/board.tsx +48 -11
  69. package/src/app/routes/home.tsx +43 -7
  70. package/src/app/routes/presenter.tsx +1 -16
  71. package/src/app/routes/preview.tsx +169 -0
  72. package/src/app/routes/slide.tsx +221 -173
  73. package/src/app/styles.css +234 -80
  74. package/src/editing/placeholder-id.ts +1 -0
  75. package/src/locale/en.ts +127 -5
  76. package/src/locale/es.ts +692 -0
  77. package/src/locale/index.ts +1 -0
  78. package/src/locale/ja.ts +120 -6
  79. package/src/locale/types.ts +132 -5
  80. package/src/locale/zh-cn.ts +117 -5
  81. package/src/locale/zh-tw.ts +117 -5
  82. package/src/app/assets/open-slide.png +0 -0
  83. package/src/app/components/inspector/comment-widget.tsx +0 -119
@@ -1,10 +1,13 @@
1
- import { t as Locale } from "./types-BjW4glip.js";
1
+ import { t as Locale } from "./types-8VUyA2bu.js";
2
2
  //#region src/config.d.ts
3
3
  type LemonRiverBuildConfig = {
4
4
  showSlideBrowser?: boolean;
5
5
  showSlideUi?: boolean;
6
6
  allowHtmlDownload?: boolean;
7
7
  };
8
+ type LemonRiverAgentConfig = {
9
+ enabled?: boolean;
10
+ };
8
11
  type LemonRiverConfig = {
9
12
  base?: string;
10
13
  slidesDir?: string;
@@ -19,8 +22,9 @@ type LemonRiverConfig = {
19
22
  */
20
23
  locale?: Locale;
21
24
  build?: LemonRiverBuildConfig;
25
+ agent?: LemonRiverAgentConfig;
22
26
  };
23
27
  /** @deprecated Renamed to {@link LemonRiverConfig}; kept for config files written before the rename. */
24
28
  type OpenSlideConfig = LemonRiverConfig;
25
29
  //#endregion
26
- export { LemonRiverConfig as n, OpenSlideConfig as r, LemonRiverBuildConfig as t };
30
+ export { OpenSlideConfig as i, LemonRiverBuildConfig as n, LemonRiverConfig as r, LemonRiverAgentConfig as t };
@@ -1,6 +1,6 @@
1
- import { F as PKG_NAMES } from "./update-C_R0ycVC.js";
1
+ import { U as PKG_NAMES } from "./update-BKOA3Z6o.js";
2
2
  import { a as printUrls, i as printShortcutsHint, n as formatError, o as shortcutsEnabled, r as printHeader, s as startupDuration, t as createCliLogger } from "./cli/bin.js";
3
- import { n as DEV_SUPERVISED_ENV, t as createViteConfig } from "./config-DO45GU3e.js";
3
+ import { n as DEV_SUPERVISED_ENV, t as createViteConfig } from "./config-BjWCLoMQ.js";
4
4
  import path from "node:path";
5
5
  import { existsSync } from "node:fs";
6
6
  import { fork } from "node:child_process";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { n as Plural, t as Locale } from "./types-BjW4glip.js";
2
- import { n as LemonRiverConfig, r as OpenSlideConfig, t as LemonRiverBuildConfig } from "./config-B6JaKJM4.js";
1
+ import { n as Plural, t as Locale } from "./types-8VUyA2bu.js";
2
+ import { i as OpenSlideConfig, n as LemonRiverBuildConfig, r as LemonRiverConfig, t as LemonRiverAgentConfig } from "./config-rMGCkz6i.js";
3
3
  import { CSSProperties, ComponentType, HTMLAttributes, PropsWithChildren, ReactNode } from "react";
4
4
  //#region src/app/components/image-placeholder.d.ts
5
5
  type ImagePlaceholderProps = {
@@ -103,4 +103,4 @@ type InternalStepProps = StepProps & {
103
103
  };
104
104
  declare function Step({ children, duration, _revealed }: InternalStepProps): import("react").JSX.Element;
105
105
  //#endregion
106
- export { CANVAS_HEIGHT, CANVAS_WIDTH, type DesignFonts, type DesignPalette, type DesignSystem, type DesignTypeScale, ImagePlaceholder, type ImagePlaceholderProps, type LemonRiverBuildConfig, type LemonRiverConfig, type Locale, MorphElement, type MorphElementProps, type MorphTransition, type OpenSlideConfig, type Page, type Plural, type SlideMeta, type SlideModule, type SlideTransition, Step, type StepProps, Steps, type StepsProps, type TransitionPhase, cssVarsToString, defaultDesign, designToCssVars, useIsActivePage, useSlidePageNumber };
106
+ export { CANVAS_HEIGHT, CANVAS_WIDTH, type DesignFonts, type DesignPalette, type DesignSystem, type DesignTypeScale, ImagePlaceholder, type ImagePlaceholderProps, type LemonRiverAgentConfig, type LemonRiverBuildConfig, type LemonRiverConfig, type Locale, MorphElement, type MorphElementProps, type MorphTransition, type OpenSlideConfig, type Page, type Plural, type SlideMeta, type SlideModule, type SlideTransition, Step, type StepProps, Steps, type StepsProps, type TransitionPhase, cssVarsToString, defaultDesign, designToCssVars, useIsActivePage, useSlidePageNumber };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as en, n as zhCN, r as ja, t as zhTW } from "./zh-tw-Ci0jK8eg.js";
1
+ import { a as en, i as es, n as zhCN, r as ja, t as zhTW } from "./zh-tw-CeLrYwRE.js";
2
2
  import { n as defaultDesign, r as designToCssVars, t as cssVarsToString } from "./design-Cpqx7SJs.js";
3
3
  import { Children, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { toast } from "sonner";
@@ -85,12 +85,13 @@ const LOCALES = {
85
85
  en,
86
86
  "zh-TW": zhTW,
87
87
  "zh-CN": zhCN,
88
- ja
88
+ ja,
89
+ es
89
90
  };
90
91
  const STORAGE_KEY = "open-slide:locale";
91
92
  const configLocale = config.locale;
92
93
  function isLocaleId(value) {
93
- return value === "en" || value === "zh-TW" || value === "zh-CN" || value === "ja";
94
+ return value === "en" || value === "zh-TW" || value === "zh-CN" || value === "ja" || value === "es";
94
95
  }
95
96
  function readStored() {
96
97
  try {
@@ -1,7 +1,10 @@
1
- import { n as Plural, t as Locale } from "../types-BjW4glip.js";
1
+ import { n as Plural, t as Locale } from "../types-8VUyA2bu.js";
2
2
  //#region src/locale/en.d.ts
3
3
  declare const en: Locale;
4
4
  //#endregion
5
+ //#region src/locale/es.d.ts
6
+ declare const es: Locale;
7
+ //#endregion
5
8
  //#region src/locale/format.d.ts
6
9
  declare function format(template: string, vars: Record<string, string | number>): string;
7
10
  declare function plural(count: number, forms: Plural): string;
@@ -15,4 +18,4 @@ declare const zhCN: Locale;
15
18
  //#region src/locale/zh-tw.d.ts
16
19
  declare const zhTW: Locale;
17
20
  //#endregion
18
- export { type Locale, type Plural, en, format, ja, plural, zhCN, zhTW };
21
+ export { type Locale, type Plural, en, es, format, ja, plural, zhCN, zhTW };
@@ -1,4 +1,4 @@
1
- import { i as en, n as zhCN, r as ja, t as zhTW } from "../zh-tw-Ci0jK8eg.js";
1
+ import { a as en, i as es, n as zhCN, r as ja, t as zhTW } from "../zh-tw-CeLrYwRE.js";
2
2
  //#region src/locale/format.ts
3
3
  function format(template, vars) {
4
4
  return template.replace(/\{(\w+)\}/g, (m, key) => {
@@ -10,4 +10,4 @@ function plural(count, forms) {
10
10
  return count === 1 ? forms.one : forms.other;
11
11
  }
12
12
  //#endregion
13
- export { en, format, ja, plural, zhCN, zhTW };
13
+ export { en, es, format, ja, plural, zhCN, zhTW };
@@ -1,5 +1,5 @@
1
1
  import { a as printUrls, i as printShortcutsHint, o as shortcutsEnabled, r as printHeader, s as startupDuration, t as createCliLogger } from "./cli/bin.js";
2
- import { t as createViteConfig } from "./config-DO45GU3e.js";
2
+ import { t as createViteConfig } from "./config-BjWCLoMQ.js";
3
3
  import { mergeConfig, preview as preview$1 } from "vite";
4
4
  //#region src/cli/preview.ts
5
5
  async function preview(opts = {}) {
@@ -1,10 +1,14 @@
1
+ //#region src/agent/types.d.ts
2
+ declare const AGENT_EFFORTS: readonly ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
3
+ type AgentEffort = (typeof AGENT_EFFORTS)[number];
4
+ //#endregion
1
5
  //#region src/locale/types.d.ts
2
6
  type Plural = {
3
7
  one: string;
4
8
  other: string;
5
9
  };
6
10
  type Locale = {
7
- id: 'en' | 'zh-TW' | 'zh-CN' | 'ja';
11
+ id: 'en' | 'zh-TW' | 'zh-CN' | 'ja' | 'es';
8
12
  common: {
9
13
  cancel: string;
10
14
  save: string;
@@ -99,6 +103,7 @@ type Locale = {
99
103
  pickIcon: string;
100
104
  };
101
105
  slide: {
106
+ resizeSidePanel: string;
102
107
  home: string;
103
108
  backToHome: string;
104
109
  agentConnected: string;
@@ -283,20 +288,25 @@ type Locale = {
283
288
  cropFitCover: string;
284
289
  cropFitContain: string;
285
290
  cropApply: string;
286
- leaveComment: string;
291
+ commentMode: string;
287
292
  commentPlaceholder: string;
288
- commentShortcutHint: string;
289
- addComment: string;
293
+ commentPost: string;
290
294
  /** templates: "{count} unsaved change" / "{count} unsaved changes" */
291
295
  unsavedChanges: Plural;
292
296
  /** templates: "{count} comment" / "{count} comments" */
293
297
  commentsCount: Plural;
294
298
  /** template: "line {n}" */
295
299
  commentLineLabel: string;
296
- commentsEmpty: string;
300
+ commentRegionLabel: string;
301
+ commentEmptyHint: string;
297
302
  commentsApplyHintPrefix: string;
298
303
  commentsApplyHintSuffix: string;
299
304
  commentDeleteAria: string;
305
+ /** Prefix for the toast shown when a comment edit fails to write to disk. */
306
+ commentUpdateFailed: string;
307
+ /** Prefix for the toast shown when a comment fails to write to disk. */
308
+ commentAddFailed: string;
309
+ commentRegionNoTarget: string;
300
310
  /** Prefix for the toast shown when one or more buffered edits fail to write to disk. */
301
311
  saveFailed: string;
302
312
  decreaseFontSize: string;
@@ -487,6 +497,19 @@ type Locale = {
487
497
  /** template: "Added a blank page at {n}" */
488
498
  toastInserted: string;
489
499
  insertFailed: string;
500
+ cardTitlePlaceholder: string;
501
+ renameCard: string;
502
+ status: string;
503
+ statusNone: string;
504
+ statusTodo: string;
505
+ statusDoing: string;
506
+ statusReview: string;
507
+ statusDone: string;
508
+ memoPlaceholder: string;
509
+ linkHandle: string;
510
+ showLinks: string;
511
+ hideLinks: string;
512
+ removeLink: string;
490
513
  };
491
514
  pdfToast: {
492
515
  title: string;
@@ -577,6 +600,111 @@ type Locale = {
577
600
  expandPromptAria: string;
578
601
  collapsePromptAria: string;
579
602
  };
603
+ agent: {
604
+ title: string;
605
+ beta: string;
606
+ betaTitle: string;
607
+ betaHint: string;
608
+ open: string;
609
+ openTitle: string;
610
+ close: string;
611
+ newSession: string;
612
+ sessions: string;
613
+ noSessions: string;
614
+ deleteSession: string;
615
+ historyThisDeck: string;
616
+ historyOtherDecks: string;
617
+ historyEmptyHint: string;
618
+ sessionRunning: string;
619
+ /** template: "Ask {provider} to create or edit slides…" */
620
+ composerPlaceholder: string;
621
+ send: string;
622
+ stop: string;
623
+ working: string;
624
+ /** template: "Page {n}" */
625
+ contextPage: string;
626
+ contextSelection: string;
627
+ /** plural, template: "{count} comments" */
628
+ pendingComments: Plural;
629
+ applyComments: string;
630
+ /** template: "Apply with {provider}" */
631
+ applyCommentsWith: string;
632
+ createDeck: string;
633
+ /** template: "Create a deck with {provider}" */
634
+ createDeckWith: string;
635
+ quickCreate: string;
636
+ quickCreateHint: string;
637
+ quickApply: string;
638
+ quickApplyHint: string;
639
+ quickTheme: string;
640
+ quickThemeHint: string;
641
+ quickEdit: string;
642
+ quickEditHint: string;
643
+ emptyTitle: string;
644
+ emptyHint: string;
645
+ questionSubmit: string;
646
+ questionOther: string;
647
+ questionOtherPlaceholder: string;
648
+ questionCancelled: string;
649
+ permissionTitle: string;
650
+ allow: string;
651
+ deny: string;
652
+ allowed: string;
653
+ denied: string;
654
+ autoApprove: string;
655
+ autoApproveHint: string;
656
+ resultDone: string;
657
+ resultStopped: string;
658
+ /** template: "{seconds}s" */
659
+ resultSeconds: string;
660
+ errorEyebrow: string;
661
+ statusLoading: string;
662
+ disabled: string;
663
+ connectTitle: string;
664
+ connectDescription: string;
665
+ connect: string;
666
+ connecting: string;
667
+ connected: string;
668
+ /** template: "Adds {pkg} to this workspace with {pm}." */
669
+ connectInstallHint: string;
670
+ /** template: "CLI found · v{version}" */
671
+ cliFound: string;
672
+ cliFoundNoVersion: string;
673
+ cliMissing: string;
674
+ /** template: "Install the {name} CLI and sign in once; the panel reuses its login." */
675
+ cliHint: string;
676
+ needCliTitle: string;
677
+ needCliDescription: string;
678
+ checkAgain: string;
679
+ retry: string;
680
+ /** template: "{name} connected" */
681
+ toastConnected: string;
682
+ /** template: "Could not install {pkg}" */
683
+ toastConnectFailed: string;
684
+ manageConnections: string;
685
+ backToChat: string;
686
+ toastSendFailed: string;
687
+ newDeckTitle: string;
688
+ newDeckHint: string;
689
+ newDeckPlaceholder: string;
690
+ toastCreateDeckFailed: string;
691
+ steps: Plural;
692
+ questionSkip: string;
693
+ copyMessage: string;
694
+ copied: string;
695
+ editMessage: string;
696
+ cancelEdit: string;
697
+ sendEdit: string;
698
+ editHint: string;
699
+ modelDefault: string;
700
+ reasoning: string;
701
+ effortAuto: string;
702
+ efforts: Record<AgentEffort, string>;
703
+ /** template: "{provider} is editing" */
704
+ presenceEditing: string;
705
+ presenceThinking: string;
706
+ presenceWaiting: string;
707
+ };
580
708
  };
581
709
  //#endregion
582
710
  export { Plural as n, Locale as t };
@@ -76,6 +76,10 @@ function validateMutationRequest(req, opts = {}) {
76
76
  };
77
77
  return { ok: true };
78
78
  }
79
+ function isLoopbackRequest(req) {
80
+ const address = req.socket.remoteAddress ?? "";
81
+ return address === "::1" || address.startsWith("127.") || address.startsWith("::ffff:127.");
82
+ }
79
83
  //#endregion
80
84
  //#region src/workspace-specifier.ts
81
85
  function readDeps(userCwd) {
@@ -122,6 +126,21 @@ function readFrameworkDependency(userCwd) {
122
126
  //#endregion
123
127
  //#region src/editing/slide-ops.ts
124
128
  const SLIDE_ID_RE = /^[a-z0-9_-]+$/i;
129
+ /**
130
+ * True when a directory under root already uses this id, including a
131
+ * different case. ext4 keeps `Cover` and `cover` as two folders; macOS
132
+ * and Windows usually collapse them. Listing names catches both.
133
+ */
134
+ async function slideIdTaken(root, slideId) {
135
+ let names;
136
+ try {
137
+ names = await fs.readdir(root);
138
+ } catch {
139
+ return false;
140
+ }
141
+ const want = slideId.toLowerCase();
142
+ return names.some((name) => name.toLowerCase() === want);
143
+ }
125
144
  function validateSlideName(v) {
126
145
  if (typeof v !== "string") return null;
127
146
  const trimmed = v.trim();
@@ -226,30 +245,25 @@ async function duplicateSlideDir(slidesRoot, slideId, desiredId) {
226
245
  error: "invalid newId"
227
246
  };
228
247
  newId = desiredId;
229
- const dstDir = path.resolve(root, newId);
230
- if (!dstDir.startsWith(root + path.sep)) return {
248
+ if (!path.resolve(root, newId).startsWith(root + path.sep)) return {
231
249
  ok: false,
232
250
  status: 400,
233
251
  error: "invalid newId"
234
252
  };
235
- try {
236
- await fs.access(dstDir);
237
- return {
238
- ok: false,
239
- status: 409,
240
- error: "slide already exists"
241
- };
242
- } catch {}
253
+ if (await slideIdTaken(root, newId)) return {
254
+ ok: false,
255
+ status: 409,
256
+ error: "slide already exists"
257
+ };
243
258
  } else {
244
259
  let suffix = 1;
245
260
  while (true) {
246
261
  newId = suffix === 1 ? `${slideId}-copy` : `${slideId}-copy-${suffix}`;
247
- try {
248
- await fs.access(path.resolve(root, newId));
262
+ if (await slideIdTaken(root, newId)) {
249
263
  suffix++;
250
- } catch {
251
- break;
264
+ continue;
252
265
  }
266
+ break;
253
267
  }
254
268
  }
255
269
  const dstDir = path.resolve(root, newId);
@@ -306,6 +320,107 @@ async function duplicateSlideDir(slidesRoot, slideId, desiredId) {
306
320
  };
307
321
  }
308
322
  }
323
+ const BLANK_SLIDE_TITLE = "Untitled";
324
+ function blankSlideSource(createdAt, specifier) {
325
+ const q = (value) => `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
326
+ return `import type { Page, SlideMeta } from '${specifier}';
327
+
328
+ export const meta: SlideMeta = { title: ${q(BLANK_SLIDE_TITLE)}, createdAt: ${q(createdAt)} };
329
+
330
+ const Blank: Page = () => <div style={{ width: '100%', height: '100%', background: '#ffffff' }} />;
331
+
332
+ export default [Blank] satisfies Page[];
333
+ `;
334
+ }
335
+ async function createBlankSlideDir(slidesRoot, specifier = PKG_NAME) {
336
+ const root = path.resolve(slidesRoot);
337
+ await fs.mkdir(root, { recursive: true });
338
+ for (let suffix = 1; suffix < 1e3; suffix++) {
339
+ const slideId = suffix === 1 ? "untitled" : `untitled-${suffix}`;
340
+ if (await slideIdTaken(root, slideId)) continue;
341
+ const dir = path.join(root, slideId);
342
+ try {
343
+ await fs.mkdir(dir);
344
+ } catch (err) {
345
+ if (err.code === "EEXIST") continue;
346
+ return {
347
+ ok: false,
348
+ status: 500,
349
+ error: String(err.message ?? err)
350
+ };
351
+ }
352
+ const file = path.join(dir, "index.tsx");
353
+ await fs.writeFile(file, blankSlideSource((/* @__PURE__ */ new Date()).toISOString(), specifier), "utf8");
354
+ return {
355
+ ok: true,
356
+ slideId,
357
+ file
358
+ };
359
+ }
360
+ return {
361
+ ok: false,
362
+ status: 409,
363
+ error: "no free slide id"
364
+ };
365
+ }
366
+ function slugifyTitle(title) {
367
+ return title.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+/, "").slice(0, 48).replace(/-+$/, "");
368
+ }
369
+ async function renameSlideDir(slidesRoot, slideId, desiredId) {
370
+ if (!SLIDE_ID_RE.test(slideId) || !SLIDE_ID_RE.test(desiredId)) return {
371
+ ok: false,
372
+ status: 400,
373
+ error: "invalid slideId"
374
+ };
375
+ const root = path.resolve(slidesRoot);
376
+ const from = path.resolve(root, slideId);
377
+ if (!from.startsWith(root + path.sep)) return {
378
+ ok: false,
379
+ status: 400,
380
+ error: "invalid slideId"
381
+ };
382
+ try {
383
+ await fs.access(path.join(from, "index.tsx"));
384
+ } catch {
385
+ return {
386
+ ok: false,
387
+ status: 404,
388
+ error: "slide not found"
389
+ };
390
+ }
391
+ for (let suffix = 1; suffix < 1e3; suffix++) {
392
+ const candidate = suffix === 1 ? desiredId : `${desiredId}-${suffix}`;
393
+ if (candidate === slideId) return {
394
+ ok: false,
395
+ status: 409,
396
+ error: "slide id unchanged"
397
+ };
398
+ if (await slideIdTaken(root, candidate)) continue;
399
+ const to = path.join(root, candidate);
400
+ try {
401
+ await fs.rename(from, to);
402
+ } catch (err) {
403
+ const code = err.code;
404
+ if (code === "EEXIST" || code === "ENOTEMPTY") continue;
405
+ return {
406
+ ok: false,
407
+ status: 500,
408
+ error: String(err.message ?? err)
409
+ };
410
+ }
411
+ return {
412
+ ok: true,
413
+ slideId: candidate,
414
+ from,
415
+ to
416
+ };
417
+ }
418
+ return {
419
+ ok: false,
420
+ status: 409,
421
+ error: "no free slide id"
422
+ };
423
+ }
309
424
  function resolveSlideEntry(slidesRoot, slideId) {
310
425
  if (!SLIDE_ID_RE.test(slideId)) return null;
311
426
  const dir = path.resolve(slidesRoot, slideId);
@@ -798,13 +913,29 @@ function makeContext(opts) {
798
913
  globalAssetsRoot: path.resolve(userCwd, assetsDir),
799
914
  manifestPath: foldersManifestPath(slidesRoot),
800
915
  coreVersion: opts.coreVersion,
801
- frameworkSpecifier: declaredFrameworkSpecifier(userCwd)
916
+ frameworkSpecifier: declaredFrameworkSpecifier(userCwd),
917
+ agentEnabled: opts.agentEnabled
802
918
  };
803
919
  }
804
- async function readBody(req) {
920
+ var BodyTooLargeError = class extends Error {
921
+ constructor() {
922
+ super("request body too large");
923
+ this.name = "BodyTooLargeError";
924
+ }
925
+ };
926
+ async function readBody(req, limit = Number.POSITIVE_INFINITY) {
805
927
  return await new Promise((resolve, reject) => {
806
928
  const chunks = [];
807
- req.on("data", (c) => chunks.push(c));
929
+ let size = 0;
930
+ req.on("data", (c) => {
931
+ size += c.length;
932
+ if (size > limit) {
933
+ req.destroy();
934
+ reject(new BodyTooLargeError());
935
+ return;
936
+ }
937
+ chunks.push(c);
938
+ });
808
939
  req.on("end", () => {
809
940
  const raw = Buffer.concat(chunks).toString("utf8");
810
941
  if (!raw) return resolve({});
@@ -944,7 +1075,7 @@ async function localSyncCommand(cwd) {
944
1075
  function formatCommand(spec) {
945
1076
  return [spec.cmd, ...spec.args].join(" ");
946
1077
  }
947
- async function runCommand(spec, cwd) {
1078
+ async function runCommand(spec, cwd, timeoutMs = COMMAND_TIMEOUT_MS) {
948
1079
  await new Promise((resolve, reject) => {
949
1080
  const child = spawn(spec.cmd, spec.args, {
950
1081
  cwd,
@@ -954,13 +1085,14 @@ async function runCommand(spec, cwd) {
954
1085
  "ignore",
955
1086
  "ignore",
956
1087
  "pipe"
957
- ]
1088
+ ],
1089
+ windowsHide: true
958
1090
  });
959
1091
  let stderr = "";
960
1092
  const timer = setTimeout(() => {
961
1093
  child.kill();
962
1094
  reject(/* @__PURE__ */ new Error(`${formatCommand(spec)} timed out`));
963
- }, COMMAND_TIMEOUT_MS);
1095
+ }, timeoutMs);
964
1096
  child.stderr.on("data", (chunk) => {
965
1097
  stderr += chunk.toString("utf8");
966
1098
  if (stderr.length > 2e3) stderr = stderr.slice(-2e3);
@@ -1023,4 +1155,4 @@ function registerUpdateRoutes(server, ctx) {
1023
1155
  });
1024
1156
  }
1025
1157
  //#endregion
1026
- export { validateSlideName as A, removeNotesElementInSource as C, resolveSlideEntry as D, reorderNotesArrayInSource as E, PKG_NAMES as F, isFrameworkSpecifier as I, validateMutationRequest as M, BIN_NAME as N, rmSlideDir as O, CONFIG_FILES as P, insertNotesElementInSource as S, reorderDefaultExportPagesInSource as T, SLIDE_ID_RE as _, readBody as a, duplicateSlideDir as b, FOLDER_ID_RE as c, readManifest as d, validateIcon as f, shortId as g, writeManifest as h, makeContext as i, declaredFrameworkSpecifier as j, updateMetaTitleInSource as k, foldersManifestPath as l, validateReorder as m, registerUpdateRoutes as n, readSlideSource as o, validateName as p, json as r, resolveSlideEntryPath as s, detectPackageManager as t, newFolderId as u, duplicateNotesElementInSource as v, removePageFromDefaultExportInSource as w, insertBlankPageInDefaultExportInSource as x, duplicatePageInDefaultExportInSource as y };
1158
+ export { renameSlideDir as A, validateMutationRequest as B, duplicatePageInDefaultExportInSource as C, readMetaTitleInSource as D, insertNotesElementInSource as E, slugifyTitle as F, CONFIG_FILES as H, updateMetaTitleInSource as I, validateSlideName as L, reorderNotesArrayInSource as M, resolveSlideEntry as N, removeNotesElementInSource as O, rmSlideDir as P, declaredFrameworkSpecifier as R, duplicateNotesElementInSource as S, insertBlankPageInDefaultExportInSource as T, PKG_NAMES as U, BIN_NAME as V, isFrameworkSpecifier as W, writeManifest as _, json as a, SLIDE_ID_RE as b, readSlideSource as c, foldersManifestPath as d, newFolderId as f, validateReorder as g, validateName as h, BodyTooLargeError as i, reorderDefaultExportPagesInSource as j, removePageFromDefaultExportInSource as k, resolveSlideEntryPath as l, validateIcon as m, registerUpdateRoutes as n, makeContext as o, readManifest as p, runCommand as r, readBody as s, detectPackageManager as t, FOLDER_ID_RE as u, shortId as v, duplicateSlideDir as w, createBlankSlideDir as x, BLANK_SLIDE_TITLE as y, isLoopbackRequest as z };
@@ -1,4 +1,4 @@
1
- import { n as LemonRiverConfig } from "../config-B6JaKJM4.js";
1
+ import { r as LemonRiverConfig } from "../config-rMGCkz6i.js";
2
2
  import { InlineConfig } from "vite";
3
3
  //#region src/vite/config.d.ts
4
4
  type CreateViteConfigOptions = {
@@ -1,2 +1,2 @@
1
- import { t as createViteConfig } from "../config-DO45GU3e.js";
1
+ import { t as createViteConfig } from "../config-BjWCLoMQ.js";
2
2
  export { createViteConfig };