@june-ai/june-ui 0.2.10 → 0.2.11

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 CHANGED
@@ -645,6 +645,7 @@ Any other prop of OpenUI's `Renderer` is forwarded and typed from your installed
645
645
  | `onError` | `(errors: unknown[]) => void` | — | Raw OpenUI errors (`[]` when cleared). |
646
646
  | `onJuneError` | `(error: JuneError) => void` | — | Structured errors. |
647
647
  | `documents` | `JuneDocumentOptions` | — | Document store and callbacks. Memoize. |
648
+ | `documentEditor` | `DocumentEditorConfig` | `{ presentation: "inline" }` | Where Edit opens the editor. `"side-panel"` uses `width` (for example `"65%"`). A document's `presentation` argument wins. |
648
649
  | `onResponseChange` | `(response, info) => void` | — | Program rewritten with user document edits. |
649
650
  | `responseChangeDelay` | `number` | `300` | Debounce in ms. |
650
651
  | `loadingFallback` | `ReactNode` | `null` | Streaming, nothing to draw yet. |
@@ -141,7 +141,7 @@ function OpenUIDevtoolsGate() {
141
141
 
142
142
  // src/engine/renderer/june-renderer.tsx
143
143
  import { Renderer } from "@openuidev/react-lang";
144
- import { Fragment, useCallback as useCallback2, useMemo as useMemo3, useRef as useRef2 } from "react";
144
+ import { Fragment, useCallback as useCallback2, useMemo as useMemo4, useRef as useRef2 } from "react";
145
145
 
146
146
  // src/core/context.ts
147
147
  import { createContext as createContext2, useContext as useContext2 } from "react";
@@ -224,9 +224,16 @@ function createActionHandler(options) {
224
224
  };
225
225
  }
226
226
 
227
+ // src/features/document-editor/document-bridge.tsx
228
+ import { useMemo as useMemo2 } from "react";
229
+
227
230
  // src/features/document-editor/document-context.ts
228
231
  import { createContext as createContext3, useContext as useContext3, useSyncExternalStore } from "react";
229
232
  var JuneDocumentContext = createContext3({});
233
+ var DocumentEditorConfigContext = createContext3({});
234
+ function useDocumentEditorConfig() {
235
+ return useContext3(DocumentEditorConfigContext);
236
+ }
230
237
  function useDocumentOptions() {
231
238
  return useContext3(JuneDocumentContext);
232
239
  }
@@ -608,13 +615,20 @@ function useDocumentBridge(options) {
608
615
 
609
616
  // src/features/document-editor/document-bridge.tsx
610
617
  import { jsx as jsx2 } from "react/jsx-runtime";
611
- function DocumentBridge({ children, ...options }) {
618
+ function DocumentBridge({
619
+ children,
620
+ documentEditor,
621
+ ...options
622
+ }) {
612
623
  const value = useDocumentBridge(options);
613
- return /* @__PURE__ */ jsx2(JuneDocumentContext.Provider, { value, children });
624
+ const presentation = documentEditor?.presentation;
625
+ const width = documentEditor?.width;
626
+ const config = useMemo2(() => ({ presentation, width }), [presentation, width]);
627
+ return /* @__PURE__ */ jsx2(JuneDocumentContext.Provider, { value, children: /* @__PURE__ */ jsx2(DocumentEditorConfigContext.Provider, { value: config, children }) });
614
628
  }
615
629
 
616
630
  // src/themes/theme-provider.tsx
617
- import { useMemo as useMemo2, useSyncExternalStore as useSyncExternalStore2 } from "react";
631
+ import { useMemo as useMemo3, useSyncExternalStore as useSyncExternalStore2 } from "react";
618
632
 
619
633
  // src/themes/dark.ts
620
634
  var darkTheme = {
@@ -675,7 +689,7 @@ function useResolvedTheme(mode) {
675
689
  function JuneThemeProvider({ mode, vars, className, style, children }) {
676
690
  const explicit = mode !== void 0;
677
691
  const resolved = useResolvedTheme(mode ?? "system");
678
- const mergedStyle = useMemo2(() => {
692
+ const mergedStyle = useMemo3(() => {
679
693
  const own = resolveThemeVars(vars, resolved);
680
694
  if (!explicit && !own && !style) return void 0;
681
695
  const base = explicit ? resolved === "dark" ? darkTheme : lightTheme : void 0;
@@ -686,7 +700,7 @@ function JuneThemeProvider({ mode, vars, className, style, children }) {
686
700
  ...style
687
701
  };
688
702
  }, [explicit, resolved, vars, style]);
689
- const context = useMemo2(() => ({ mode: mode ?? "system", theme: resolved }), [mode, resolved]);
703
+ const context = useMemo3(() => ({ mode: mode ?? "system", theme: resolved }), [mode, resolved]);
690
704
  return /* @__PURE__ */ jsx3(JuneContext.Provider, { value: context, children: /* @__PURE__ */ jsx3(
691
705
  "div",
692
706
  {
@@ -1320,6 +1334,7 @@ function JuneRenderer(props) {
1320
1334
  onError,
1321
1335
  onJuneError,
1322
1336
  documents,
1337
+ documentEditor,
1323
1338
  onResponseChange,
1324
1339
  responseChangeDelay,
1325
1340
  loadingFallback = null,
@@ -1363,8 +1378,8 @@ function JuneRenderer(props) {
1363
1378
  );
1364
1379
  }, []);
1365
1380
  const handleDocumentError = useCallback2((error) => latest.current.onJuneError?.(error), []);
1366
- const runtime = useMemo3(() => ({ isStreaming }), [isStreaming]);
1367
- const content = useMemo3(
1381
+ const runtime = useMemo4(() => ({ isStreaming }), [isStreaming]);
1382
+ const content = useMemo4(
1368
1383
  () => resolveMixedContent(response, { mixedContent, isStreaming, repair, markdown: textFormat === "markdown" }),
1369
1384
  [response, mixedContent, isStreaming, repair, textFormat]
1370
1385
  );
@@ -1376,6 +1391,7 @@ function JuneRenderer(props) {
1376
1391
  DocumentBridge,
1377
1392
  {
1378
1393
  documents,
1394
+ documentEditor,
1379
1395
  response,
1380
1396
  onResponseChange,
1381
1397
  responseChangeDelay,
@@ -1739,7 +1755,7 @@ function defineJuneAction(definition) {
1739
1755
  }
1740
1756
 
1741
1757
  // src/features/streaming/use-june-stream.ts
1742
- import { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo4, useRef as useRef3, useState } from "react";
1758
+ import { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo5, useRef as useRef3, useState } from "react";
1743
1759
 
1744
1760
  // src/features/streaming/stream-handler.ts
1745
1761
  function isReadableStream(source) {
@@ -2002,7 +2018,7 @@ function useJuneStream(options = {}) {
2002
2018
  },
2003
2019
  [clearTimer]
2004
2020
  );
2005
- return useMemo4(
2021
+ return useMemo5(
2006
2022
  () => ({ ...state, start, startFetch, abort, reset }),
2007
2023
  [state, start, startFetch, abort, reset]
2008
2024
  );
@@ -2031,7 +2047,7 @@ function DocumentDraft({ title, words, state }) {
2031
2047
 
2032
2048
  // src/features/document-editor/document-editor.tsx
2033
2049
  import { useEditor } from "@tiptap/react";
2034
- import { useCallback as useCallback4, useEffect as useEffect7, useId, useMemo as useMemo5, useRef as useRef8, useState as useState6 } from "react";
2050
+ import { useCallback as useCallback4, useEffect as useEffect7, useId, useMemo as useMemo6, useRef as useRef8, useState as useState6 } from "react";
2035
2051
 
2036
2052
  // src/features/document-editor/document-bubble.tsx
2037
2053
  import { useEditorState as useEditorState2 } from "@tiptap/react";
@@ -3703,15 +3719,15 @@ function DocumentEditorImpl(props) {
3703
3719
  className
3704
3720
  } = props;
3705
3721
  const ctxOptions = useDocumentOptions();
3706
- const options = useMemo5(() => ({ ...ctxOptions, ...props.options }), [ctxOptions, props.options]);
3722
+ const options = useMemo6(() => ({ ...ctxOptions, ...props.options }), [ctxOptions, props.options]);
3707
3723
  const optionsRef = useRef8(options);
3708
3724
  optionsRef.current = options;
3709
- const store = useMemo5(() => options.store ?? getDefaultDocumentStore(), [options.store]);
3725
+ const store = useMemo6(() => options.store ?? getDefaultDocumentStore(), [options.store]);
3710
3726
  const instanceId = useId();
3711
3727
  const { theme: contextTheme } = useJuneTheme();
3712
3728
  const resolvedTheme = theme === "auto" ? contextTheme : theme;
3713
3729
  const contentKey = typeof content === "string" ? content : content ? JSON.stringify(content) : "";
3714
- const parsed = useMemo5(() => {
3730
+ const parsed = useMemo6(() => {
3715
3731
  if (!contentKey) return { json: null, error: null };
3716
3732
  try {
3717
3733
  return { json: parseDocumentContent(contentKey), error: null };
@@ -3763,7 +3779,7 @@ function DocumentEditorImpl(props) {
3763
3779
  );
3764
3780
  const saveRef = useRef8(async () => {
3765
3781
  });
3766
- const autosave = useMemo5(() => {
3782
+ const autosave = useMemo6(() => {
3767
3783
  const delay = options.autosaveDelay ?? 1500;
3768
3784
  return delay > 0 ? debounce(() => void saveRef.current(), delay) : null;
3769
3785
  }, [options.autosaveDelay]);
@@ -3815,7 +3831,7 @@ function DocumentEditorImpl(props) {
3815
3831
  return () => clearTimeout(t);
3816
3832
  }, [saveState, options.onSave]);
3817
3833
  const [initialJson] = useState6(() => store.get(documentId)?.json ?? parsed.json ?? emptyDoc());
3818
- const extensions = useMemo5(() => createDocumentExtensions(), []);
3834
+ const extensions = useMemo6(() => createDocumentExtensions(), []);
3819
3835
  const onUpdateRef = useRef8(() => {
3820
3836
  });
3821
3837
  onUpdateRef.current = (json) => {
@@ -4041,52 +4057,155 @@ function DocumentEditorImpl(props) {
4041
4057
  }
4042
4058
 
4043
4059
  // src/features/document-editor/document-preview.tsx
4044
- import { useEffect as useEffect8, useMemo as useMemo6, useState as useState7 } from "react";
4060
+ import { useEffect as useEffect8, useMemo as useMemo7, useState as useState7 } from "react";
4045
4061
  import { createPortal } from "react-dom";
4062
+
4063
+ // src/features/document-editor/document-presentation.ts
4064
+ function resolveDocumentPresentation(documentPresentation, configured) {
4065
+ if (documentPresentation === "inline" || documentPresentation === "side-panel") return documentPresentation;
4066
+ if (configured === "inline" || configured === "side-panel") return configured;
4067
+ return "inline";
4068
+ }
4069
+ function documentPanelWidth(width) {
4070
+ if (typeof width === "number" && Number.isFinite(width)) return `${width}px`;
4071
+ if (typeof width === "string" && width.trim()) return width.trim();
4072
+ return void 0;
4073
+ }
4074
+
4075
+ // src/features/document-editor/document-preview.tsx
4046
4076
  import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
4077
+ var FORMAT_LABEL = {
4078
+ docx: "Word document",
4079
+ pdf: "PDF",
4080
+ html: "Web page",
4081
+ markdown: "Markdown",
4082
+ txt: "Plain text"
4083
+ };
4047
4084
  function DocumentPreview(props) {
4048
- const { documentId, title = "Untitled document", content, editable = true } = props;
4085
+ const { documentId, title = "Untitled document", content, editable = true, exportFormats } = props;
4049
4086
  const { theme: contextTheme } = useJuneTheme();
4050
4087
  const resolvedTheme = props.theme && props.theme !== "auto" ? props.theme : contextTheme;
4051
4088
  const ctxOptions = useDocumentOptions();
4052
- const store = useMemo6(() => props.options?.store ?? ctxOptions.store ?? getDefaultDocumentStore(), [props.options?.store, ctxOptions.store]);
4089
+ const editorConfig = useDocumentEditorConfig();
4090
+ const presentation = resolveDocumentPresentation(props.presentation, editorConfig.presentation);
4091
+ const panelWidth = documentPanelWidth(editorConfig.width);
4092
+ const store = useMemo7(() => props.options?.store ?? ctxOptions.store ?? getDefaultDocumentStore(), [props.options?.store, ctxOptions.store]);
4053
4093
  const storedJson = useDocumentValue(store, documentId, (record) => record?.json);
4054
4094
  const [open, setOpen] = useState7(false);
4055
4095
  const [preferStore, setPreferStore] = useState7(false);
4096
+ const [notice, setNotice] = useState7(null);
4056
4097
  const contentKey = typeof content === "string" ? content : content ? JSON.stringify(content) : "";
4098
+ const options = props.options ?? ctxOptions;
4057
4099
  useEffect8(() => {
4058
4100
  setPreferStore(false);
4059
4101
  }, [contentKey]);
4060
- const html = useMemo6(() => {
4102
+ const doc = useMemo7(() => {
4061
4103
  try {
4062
4104
  const fromContent = contentKey ? parseDocumentContent(contentKey) : null;
4063
- const doc = preferStore && storedJson ? storedJson : fromContent;
4064
- return doc ? docToHtml(doc) : "";
4105
+ return preferStore && storedJson ? storedJson : fromContent;
4065
4106
  } catch {
4066
- return "";
4107
+ return null;
4067
4108
  }
4068
4109
  }, [contentKey, preferStore, storedJson]);
4110
+ const html = useMemo7(() => doc ? docToHtml(doc) : "", [doc]);
4111
+ const formats = (exportFormats ?? [...ALL_EXPORT_FORMATS]).filter((format2) => listDocumentExporters().includes(format2));
4069
4112
  useEffect8(() => {
4070
- if (!open || typeof document === "undefined") return;
4113
+ if (!notice) return;
4114
+ const timer = setTimeout(() => setNotice(null), 2e3);
4115
+ return () => clearTimeout(timer);
4116
+ }, [notice]);
4117
+ useEffect8(() => {
4118
+ if (!open || presentation !== "side-panel" || typeof document === "undefined") return;
4071
4119
  const prev = document.body.style.overflow;
4072
4120
  document.body.style.overflow = "hidden";
4073
4121
  return () => {
4074
4122
  document.body.style.overflow = prev;
4075
4123
  };
4076
- }, [open]);
4124
+ }, [open, presentation]);
4077
4125
  const close = () => {
4078
4126
  setPreferStore(true);
4079
4127
  setOpen(false);
4080
4128
  };
4129
+ const handleExport = async (format2) => {
4130
+ if (!doc) return;
4131
+ try {
4132
+ const result = await exportDocument(format2, { documentId, title, json: doc });
4133
+ if (result.blob) downloadResult(result);
4134
+ options.onExport?.({ documentId, format: format2, ...result });
4135
+ } catch (error) {
4136
+ options.onError?.(toJuneError(error, "EXPORT_ERROR"));
4137
+ setNotice("Export failed");
4138
+ }
4139
+ };
4140
+ const handleCopy = async () => {
4141
+ if (!doc) return;
4142
+ setNotice(await copyDocumentToClipboard({ title, json: doc }) ? "Copied" : "Copy failed");
4143
+ };
4144
+ const editor = /* @__PURE__ */ jsx17(
4145
+ DocumentEditorImpl,
4146
+ {
4147
+ documentId: props.documentId,
4148
+ title: props.title,
4149
+ content: props.content,
4150
+ documentType: props.documentType,
4151
+ status: props.status,
4152
+ editable: props.editable,
4153
+ exportFormats: props.exportFormats,
4154
+ theme: props.theme,
4155
+ generating: props.generating,
4156
+ options: props.options
4157
+ }
4158
+ );
4159
+ const closeBar = /* @__PURE__ */ jsx17("div", { className: "june-doc-editor__bar", children: /* @__PURE__ */ jsxs11("button", { type: "button", className: "june-doc__btn june-doc__btn--ghost", "aria-label": "Close document", onClick: close, children: [
4160
+ /* @__PURE__ */ jsx17(Icon, { name: "close", size: 16 }),
4161
+ /* @__PURE__ */ jsx17("span", { children: "Close" })
4162
+ ] }) });
4081
4163
  return /* @__PURE__ */ jsxs11(Fragment5, { children: [
4082
4164
  !open ? /* @__PURE__ */ jsxs11("section", { className: "june-doc june-doc-card", "data-theme": resolvedTheme, "aria-label": title, children: [
4083
4165
  /* @__PURE__ */ jsxs11("div", { className: "june-doc-card__bar", children: [
4084
4166
  /* @__PURE__ */ jsx17("span", { className: "june-doc-card__title", children: title }),
4085
- editable ? /* @__PURE__ */ jsx17("button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Edit document", title: "Edit", onClick: () => setOpen(true), children: /* @__PURE__ */ jsx17(Icon, { name: "pencil", size: 16 }) }) : null
4167
+ /* @__PURE__ */ jsxs11("div", { className: "june-doc-card__actions", children: [
4168
+ notice ? /* @__PURE__ */ jsx17("span", { className: "june-doc-card__notice", children: notice }) : null,
4169
+ /* @__PURE__ */ jsx17(
4170
+ Dropdown,
4171
+ {
4172
+ align: "end",
4173
+ className: "june-doc__menu",
4174
+ trigger: ({ open: menuOpen, toggle }) => /* @__PURE__ */ jsxs11("button", { type: "button", className: "june-doc__btn june-doc__btn--ghost", "aria-label": "Download", "aria-haspopup": "menu", "aria-expanded": menuOpen, onClick: toggle, children: [
4175
+ /* @__PURE__ */ jsx17(Icon, { name: "download", size: 16 }),
4176
+ /* @__PURE__ */ jsx17("span", { children: "Download" })
4177
+ ] }),
4178
+ children: (dismiss) => /* @__PURE__ */ jsx17("div", { role: "menu", "aria-label": "Download options", children: formats.map((format2) => /* @__PURE__ */ jsx17(
4179
+ "button",
4180
+ {
4181
+ type: "button",
4182
+ role: "menuitem",
4183
+ className: "june-doc__menu-item",
4184
+ onClick: () => {
4185
+ dismiss();
4186
+ void handleExport(format2);
4187
+ },
4188
+ children: /* @__PURE__ */ jsx17("span", { className: "june-doc__menu-label", children: FORMAT_LABEL[format2] ?? format2.toUpperCase() })
4189
+ },
4190
+ format2
4191
+ )) })
4192
+ }
4193
+ ),
4194
+ /* @__PURE__ */ jsx17("button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Copy document", title: "Copy", onClick: () => void handleCopy(), children: /* @__PURE__ */ jsx17(Icon, { name: "copy", size: 16 }) }),
4195
+ editable ? /* @__PURE__ */ jsx17("button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Edit document", title: "Edit", onClick: () => setOpen(true), children: /* @__PURE__ */ jsx17(Icon, { name: "pencil", size: 16 }) }) : null
4196
+ ] })
4086
4197
  ] }),
4087
4198
  /* @__PURE__ */ jsx17("div", { className: "june-doc-card__canvas", children: /* @__PURE__ */ jsx17("div", { className: "june-doc-card__sheet", children: /* @__PURE__ */ jsx17("div", { className: "june-doc__prose", dangerouslySetInnerHTML: { __html: html } }) }) })
4088
4199
  ] }) : null,
4089
- open && typeof document !== "undefined" ? createPortal(
4200
+ open && presentation === "inline" ? /* @__PURE__ */ jsxs11("section", { className: "june-doc june-doc-inline", "data-theme": resolvedTheme, "aria-label": title, onKeyDown: (event) => {
4201
+ if (event.key !== "Escape" || event.defaultPrevented) return;
4202
+ if (event.currentTarget.querySelector(".june-doc--fullscreen")) return;
4203
+ close();
4204
+ }, children: [
4205
+ closeBar,
4206
+ editor
4207
+ ] }) : null,
4208
+ open && presentation === "side-panel" && typeof document !== "undefined" ? createPortal(
4090
4209
  /* @__PURE__ */ jsx17("div", { className: "june-doc-modal", "data-theme": resolvedTheme, onMouseDown: close, children: /* @__PURE__ */ jsxs11(
4091
4210
  "div",
4092
4211
  {
@@ -4096,6 +4215,7 @@ function DocumentPreview(props) {
4096
4215
  "aria-label": title,
4097
4216
  tabIndex: -1,
4098
4217
  "data-theme": resolvedTheme,
4218
+ style: panelWidth ? { width: panelWidth, maxWidth: "100vw" } : void 0,
4099
4219
  onMouseDown: (event) => event.stopPropagation(),
4100
4220
  onKeyDown: (event) => {
4101
4221
  if (event.key !== "Escape" || event.defaultPrevented) return;
@@ -4103,25 +4223,8 @@ function DocumentPreview(props) {
4103
4223
  close();
4104
4224
  },
4105
4225
  children: [
4106
- /* @__PURE__ */ jsx17("div", { className: "june-doc-modal__bar", children: /* @__PURE__ */ jsxs11("button", { type: "button", className: "june-doc__btn june-doc__btn--ghost", "aria-label": "Close document", onClick: close, children: [
4107
- /* @__PURE__ */ jsx17(Icon, { name: "close", size: 16 }),
4108
- /* @__PURE__ */ jsx17("span", { children: "Close" })
4109
- ] }) }),
4110
- /* @__PURE__ */ jsx17("div", { className: "june-doc-modal__body", children: /* @__PURE__ */ jsx17(
4111
- DocumentEditorImpl,
4112
- {
4113
- documentId: props.documentId,
4114
- title: props.title,
4115
- content: props.content,
4116
- documentType: props.documentType,
4117
- status: props.status,
4118
- editable: props.editable,
4119
- exportFormats: props.exportFormats,
4120
- theme: props.theme,
4121
- generating: props.generating,
4122
- options: props.options
4123
- }
4124
- ) })
4226
+ closeBar,
4227
+ /* @__PURE__ */ jsx17("div", { className: "june-doc-modal__body", children: editor })
4125
4228
  ]
4126
4229
  }
4127
4230
  ) }),
@@ -4132,7 +4235,7 @@ function DocumentPreview(props) {
4132
4235
 
4133
4236
  // src/features/document-editor/document-component.tsx
4134
4237
  import { jsx as jsx18 } from "react/jsx-runtime";
4135
- var DOCUMENT_COMPONENT_DESCRIPTION = 'Custom component. An editable document. DocumentEditor(documentId, title, content, documentType?, status?, editable?, exportFormats?). Place it in the root container (Card or Stack). "content" is the full document body as one Markdown string. Reuse documentId when revising the same document. Use it only when the user asks to create or revise a document.';
4238
+ var DOCUMENT_COMPONENT_DESCRIPTION = 'Custom component. An editable document. DocumentEditor(documentId, title, content, documentType?, status?, editable?, exportFormats?, presentation?). Place it in the root container (Card or Stack). "content" is the full document body as one Markdown string. Reuse documentId when revising the same document. Use it only when the user asks to create or revise a document. presentation is "inline" or "side-panel"; omit it to use the app default.';
4136
4239
  var documentProps = z.object({
4137
4240
  documentId: z.string().describe("Stable id. Reuse it when revising the same document."),
4138
4241
  title: z.string().describe("Document title. Do not repeat it as a heading in content."),
@@ -4140,7 +4243,8 @@ var documentProps = z.object({
4140
4243
  documentType: z.string().optional().describe("Kind of document, such as proposal, contract or invoice."),
4141
4244
  status: z.string().optional().describe('Use "draft" for generated documents.'),
4142
4245
  editable: z.boolean().optional().describe("Whether the user can edit. Defaults to true."),
4143
- exportFormats: z.array(z.string()).optional().describe("Export formats to offer, such as html, markdown, docx.")
4246
+ exportFormats: z.array(z.string()).optional().describe("Export formats to offer, such as html, markdown, docx."),
4247
+ presentation: z.enum(["inline", "side-panel"]).optional().describe('Where Edit opens the editor: "inline" in the chat, or "side-panel". Omit to use the app default.')
4144
4248
  });
4145
4249
  function DocumentEditor(props) {
4146
4250
  const { isStreaming } = useJuneRuntime();
@@ -4158,10 +4262,10 @@ function OpenUIDocument({ props }) {
4158
4262
  documentType: props.documentType,
4159
4263
  status: props.status,
4160
4264
  editable: props.editable,
4161
- exportFormats: props.exportFormats
4265
+ exportFormats: props.exportFormats,
4266
+ presentation: props.presentation
4162
4267
  };
4163
4268
  if (isStreaming) return /* @__PURE__ */ jsx18(DocumentDraft, { title: props.title, state: "drafting" });
4164
- if (props.editable === false) return /* @__PURE__ */ jsx18(DocumentEditor, { ...shared });
4165
4269
  return /* @__PURE__ */ jsx18(DocumentPreview, { ...shared });
4166
4270
  }
4167
4271
  var JuneDocumentEditorComponent = defineComponent3({
@@ -4200,7 +4304,7 @@ function getDocumentEditorRules(options = {}) {
4200
4304
  return [
4201
4305
  `WHEN TO USE ${name}: use it only when the user asks you to create, draft, write, prepare, generate or revise an editable document (${types}, or similar). Put the finished document inside ${name}.`,
4202
4306
  `WHEN NOT TO USE ${name}: for explanations ("explain what an NDA is"), factual answers, comparisons, brainstorming and general questions, answer with normal text and the other components. Never open ${name} for those.`,
4203
- `${name}(documentId, title, content, documentType?, status?, editable?, exportFormats?): "content" is the whole document body as Markdown inside ONE double-quoted string. Write line breaks as \\n and escape double quotes as \\". Supported Markdown: headings (#, ##, ###), paragraphs, **bold**, *italic*, lists, > quotes, [links](https://...), and tables.`,
4307
+ `${name}(documentId, title, content, documentType?, status?, editable?, exportFormats?, presentation?): "content" is the whole document body as Markdown inside ONE double-quoted string. Write line breaks as \\n and escape double quotes as \\". Supported Markdown: headings (#, ##, ###), paragraphs, **bold**, *italic*, lists, > quotes, [links](https://...), and tables. presentation is optional: "inline" or "side-panel". Omit it to use the app default.`,
4204
4308
  `Do not repeat the title as a heading inside "content"; the title is shown separately. Structure the document with headings, short paragraphs and tables where useful. Keep a professional tone.`,
4205
4309
  `EDITING: when the user asks to change an existing document (add a section, rewrite a part, change the title), write ${name} again with the SAME documentId and the COMPLETE updated content, starting from the current document text you were given. Copy every unchanged section exactly as it was. Change only what was requested. The app merges your version with the user's own edits.`,
4206
4310
  `If the user asks for a document but leaves out details, write the document anyway and use clear placeholders such as [CLIENT NAME], [DATE], [AMOUNT]. Do not stop to ask unless the request is impossible to interpret.`,
@@ -4365,7 +4469,7 @@ function ToolApprovalList({ approvals, pendingOnly = false, renderItem, labels,
4365
4469
  }
4366
4470
 
4367
4471
  // src/features/tool-approval/use-tool-approval.ts
4368
- import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo7, useRef as useRef9, useState as useState9 } from "react";
4472
+ import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo8, useRef as useRef9, useState as useState9 } from "react";
4369
4473
  var counter = 0;
4370
4474
  var nextId = () => `june-approval-${++counter}`;
4371
4475
  function useToolApproval() {
@@ -4421,8 +4525,8 @@ function useToolApproval() {
4421
4525
  map.clear();
4422
4526
  };
4423
4527
  }, []);
4424
- const pending = useMemo7(() => entries.filter((e) => e.status === "pending"), [entries]);
4425
- return useMemo7(
4528
+ const pending = useMemo8(() => entries.filter((e) => e.status === "pending"), [entries]);
4529
+ return useMemo8(
4426
4530
  () => ({ entries, pending, request, decide, setStatus, dismiss, confirmAction }),
4427
4531
  [entries, pending, request, decide, setStatus, dismiss, confirmAction]
4428
4532
  );
@@ -4526,6 +4630,8 @@ export {
4526
4630
  runAction,
4527
4631
  createActionHandler,
4528
4632
  JuneDocumentContext,
4633
+ DocumentEditorConfigContext,
4634
+ useDocumentEditorConfig,
4529
4635
  useDocumentOptions,
4530
4636
  mergeDocuments,
4531
4637
  createDocumentStore,
@@ -4570,6 +4676,8 @@ export {
4570
4676
  downloadResult,
4571
4677
  copyDocumentToClipboard,
4572
4678
  ALL_EXPORT_FORMATS,
4679
+ resolveDocumentPresentation,
4680
+ documentPanelWidth,
4573
4681
  DOCUMENT_COMPONENT_DESCRIPTION,
4574
4682
  DocumentEditor,
4575
4683
  JuneDocumentEditorComponent,