@promptbook/components 0.112.0-59 → 0.112.0-60

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/umd/index.umd.js CHANGED
@@ -30,7 +30,7 @@
30
30
  * @generated
31
31
  * @see https://github.com/webgptorg/promptbook
32
32
  */
33
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-59';
33
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-60';
34
34
  /**
35
35
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
36
36
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -23551,6 +23551,241 @@
23551
23551
  }, [diagnostics, editor, monaco]);
23552
23552
  }
23553
23553
 
23554
+ /**
23555
+ * Clipboard MIME types treated as rich text documents for upload.
23556
+ *
23557
+ * @private function of BookEditorMonaco
23558
+ */
23559
+ const RICH_CLIPBOARD_TEXT_MIME_TYPES = new Set(['text/html', 'text/rtf']);
23560
+ /**
23561
+ * Uploaded filename mapping for known rich clipboard MIME types.
23562
+ *
23563
+ * @private function of BookEditorMonaco
23564
+ */
23565
+ const CLIPBOARD_RICH_CONTENT_FILENAMES = {
23566
+ 'text/html': 'clipboard-content.html',
23567
+ 'text/rtf': 'clipboard-content.rtf',
23568
+ 'application/rtf': 'clipboard-content.rtf',
23569
+ };
23570
+ /**
23571
+ * Fallback filename used when clipboard MIME type is unknown.
23572
+ *
23573
+ * @private function of BookEditorMonaco
23574
+ */
23575
+ const DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME = 'clipboard-content.txt';
23576
+ /**
23577
+ * Maximum pointer travel still treated as a tap on the touch focus overlay.
23578
+ *
23579
+ * @private function of BookEditorMonaco
23580
+ */
23581
+ const TOUCH_TAP_THRESHOLD = 10;
23582
+ /**
23583
+ * Lists transferable items from a browser `DataTransfer` object.
23584
+ *
23585
+ * @private function of BookEditorMonaco
23586
+ */
23587
+ function listDataTransferItems(dataTransfer) {
23588
+ const items = [];
23589
+ for (let index = 0; index < dataTransfer.items.length; index++) {
23590
+ const item = dataTransfer.items[index];
23591
+ if (item) {
23592
+ items.push(item);
23593
+ }
23594
+ }
23595
+ return items;
23596
+ }
23597
+ /**
23598
+ * Removes duplicate files by using stable file metadata signature.
23599
+ *
23600
+ * @private function of BookEditorMonaco
23601
+ */
23602
+ function deduplicateFiles(files) {
23603
+ const uniqueFiles = new Map();
23604
+ for (const file of files) {
23605
+ uniqueFiles.set(`${file.name}:${file.type}:${file.size}`, file);
23606
+ }
23607
+ return Array.from(uniqueFiles.values());
23608
+ }
23609
+ /**
23610
+ * Extracts all file-like clipboard/drop payloads from a `DataTransfer`.
23611
+ *
23612
+ * @private function of BookEditorMonaco
23613
+ */
23614
+ function getDataTransferFiles(dataTransfer) {
23615
+ const directFiles = Array.from(dataTransfer.files || []);
23616
+ const itemFiles = listDataTransferItems(dataTransfer)
23617
+ .filter((item) => item.kind === 'file')
23618
+ .map((item) => item.getAsFile())
23619
+ .filter((file) => file !== null);
23620
+ return deduplicateFiles([...directFiles, ...itemFiles]);
23621
+ }
23622
+ /**
23623
+ * Picks the richest textual clipboard item that should be uploaded as a document.
23624
+ *
23625
+ * @private function of BookEditorMonaco
23626
+ */
23627
+ function getRichClipboardTextItem(dataTransfer) {
23628
+ const items = listDataTransferItems(dataTransfer);
23629
+ const hasPlainTextItem = items.some((item) => item.kind === 'string' && item.type.toLowerCase() === 'text/plain');
23630
+ const applicationItem = items.find((item) => item.kind === 'string' && item.type.toLowerCase().startsWith('application/'));
23631
+ if (applicationItem) {
23632
+ return applicationItem;
23633
+ }
23634
+ if (hasPlainTextItem) {
23635
+ return null;
23636
+ }
23637
+ const richTextItem = items.find((item) => {
23638
+ if (item.kind !== 'string') {
23639
+ return false;
23640
+ }
23641
+ return RICH_CLIPBOARD_TEXT_MIME_TYPES.has(item.type.toLowerCase());
23642
+ });
23643
+ return richTextItem || null;
23644
+ }
23645
+ /**
23646
+ * Resolves whether paste should route into upload workflow instead of text insert.
23647
+ *
23648
+ * @private function of BookEditorMonaco
23649
+ */
23650
+ function hasUploadableClipboardContent(dataTransfer) {
23651
+ if (getDataTransferFiles(dataTransfer).length > 0) {
23652
+ return true;
23653
+ }
23654
+ return getRichClipboardTextItem(dataTransfer) !== null;
23655
+ }
23656
+ /**
23657
+ * Reads string payload from clipboard item.
23658
+ *
23659
+ * @private function of BookEditorMonaco
23660
+ */
23661
+ function getClipboardItemString(item) {
23662
+ return new Promise((resolve) => {
23663
+ try {
23664
+ item.getAsString((value) => resolve(value || ''));
23665
+ }
23666
+ catch (_a) {
23667
+ resolve('');
23668
+ }
23669
+ });
23670
+ }
23671
+ /**
23672
+ * Determines filename for generated clipboard rich-content uploads.
23673
+ *
23674
+ * @private function of BookEditorMonaco
23675
+ */
23676
+ function getClipboardRichContentFilename(mimeType) {
23677
+ return CLIPBOARD_RICH_CONTENT_FILENAMES[mimeType.toLowerCase()] || DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME;
23678
+ }
23679
+ /**
23680
+ * Converts clipboard payload into upload-ready files.
23681
+ *
23682
+ * @private function of BookEditorMonaco
23683
+ */
23684
+ async function resolveClipboardUploadFiles(dataTransfer) {
23685
+ const files = getDataTransferFiles(dataTransfer);
23686
+ if (files.length > 0) {
23687
+ return files;
23688
+ }
23689
+ const richTextItem = getRichClipboardTextItem(dataTransfer);
23690
+ if (!richTextItem) {
23691
+ return [];
23692
+ }
23693
+ const content = await getClipboardItemString(richTextItem);
23694
+ const mimeType = richTextItem.type || 'text/plain';
23695
+ if (!content.trim()) {
23696
+ return [];
23697
+ }
23698
+ return [new File([content], getClipboardRichContentFilename(mimeType), { type: mimeType })];
23699
+ }
23700
+ /**
23701
+ * Manages drag, paste and file input interactions for `BookEditorMonaco`.
23702
+ *
23703
+ * @private function of BookEditorMonaco
23704
+ */
23705
+ function useBookEditorMonacoInteractions({ editor, handleFiles, }) {
23706
+ const [isDragOver, setIsDragOver] = react.useState(false);
23707
+ const touchStartRef = react.useRef(null);
23708
+ const fileUploadInputRef = react.useRef(null);
23709
+ const cameraInputRef = react.useRef(null);
23710
+ const handleDrop = react.useCallback(async (event) => {
23711
+ event.preventDefault();
23712
+ setIsDragOver(false);
23713
+ const files = getDataTransferFiles(event.dataTransfer);
23714
+ await handleFiles(files);
23715
+ }, [handleFiles]);
23716
+ const handlePaste = react.useCallback(async (event) => {
23717
+ const clipboardData = event.clipboardData;
23718
+ if (!hasUploadableClipboardContent(clipboardData)) {
23719
+ return;
23720
+ }
23721
+ event.preventDefault();
23722
+ event.stopPropagation();
23723
+ const files = await resolveClipboardUploadFiles(clipboardData);
23724
+ await handleFiles(files);
23725
+ }, [handleFiles]);
23726
+ const handleUploadDocument = react.useCallback(() => {
23727
+ var _a;
23728
+ (_a = fileUploadInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
23729
+ }, []);
23730
+ const handleTakePhoto = react.useCallback(() => {
23731
+ var _a;
23732
+ (_a = cameraInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
23733
+ }, []);
23734
+ const handleFileInputChange = react.useCallback((event) => {
23735
+ const files = Array.from(event.target.files || []);
23736
+ void handleFiles(files);
23737
+ event.target.value = '';
23738
+ }, [handleFiles]);
23739
+ const handleDragOver = react.useCallback((event) => {
23740
+ event.preventDefault();
23741
+ setIsDragOver(true);
23742
+ }, []);
23743
+ const handleDragEnter = react.useCallback((event) => {
23744
+ event.preventDefault();
23745
+ setIsDragOver(true);
23746
+ }, []);
23747
+ const handleDragLeave = react.useCallback((event) => {
23748
+ event.preventDefault();
23749
+ setIsDragOver(false);
23750
+ }, []);
23751
+ const handleFocusOverlayTouchStart = react.useCallback((event) => {
23752
+ const touch = event.touches[0];
23753
+ if (touch) {
23754
+ touchStartRef.current = { x: touch.clientX, y: touch.clientY };
23755
+ }
23756
+ }, []);
23757
+ const handleFocusOverlayTouchEnd = react.useCallback((event) => {
23758
+ event.preventDefault();
23759
+ const touch = event.changedTouches[0];
23760
+ if (touch && touchStartRef.current) {
23761
+ const deltaX = Math.abs(touch.clientX - touchStartRef.current.x);
23762
+ const deltaY = Math.abs(touch.clientY - touchStartRef.current.y);
23763
+ if (deltaX < TOUCH_TAP_THRESHOLD && deltaY < TOUCH_TAP_THRESHOLD) {
23764
+ editor === null || editor === void 0 ? void 0 : editor.focus();
23765
+ }
23766
+ }
23767
+ touchStartRef.current = null;
23768
+ }, [editor]);
23769
+ const focusOverlayTouchHandlers = react.useMemo(() => ({
23770
+ onTouchStart: handleFocusOverlayTouchStart,
23771
+ onTouchEnd: handleFocusOverlayTouchEnd,
23772
+ }), [handleFocusOverlayTouchEnd, handleFocusOverlayTouchStart]);
23773
+ return {
23774
+ isDragOver,
23775
+ fileUploadInputRef,
23776
+ cameraInputRef,
23777
+ handleDrop,
23778
+ handlePaste,
23779
+ handleUploadDocument,
23780
+ handleTakePhoto,
23781
+ handleFileInputChange,
23782
+ handleDragOver,
23783
+ handleDragEnter,
23784
+ handleDragLeave,
23785
+ focusOverlayTouchHandlers,
23786
+ };
23787
+ }
23788
+
23554
23789
  /**
23555
23790
  * Priority order for the important commitments shown first in catalogues and intellisense.
23556
23791
  *
@@ -24213,6 +24448,181 @@
24213
24448
  }, [monaco, theme]);
24214
24449
  }
24215
24450
 
24451
+ /**
24452
+ * Local storage key that enables BookEditor Monaco lifecycle debug logs in development.
24453
+ *
24454
+ * @private function of BookEditorMonaco
24455
+ */
24456
+ const BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY = 'promptbook-debug-book-editor-monaco';
24457
+ /**
24458
+ * Duration of the save notification.
24459
+ *
24460
+ * @private function of BookEditorMonaco
24461
+ */
24462
+ const SAVE_NOTIFICATION_HIDE_DELAY_MS = 2000;
24463
+ /**
24464
+ * Resolves whether verbose BookEditor Monaco lifecycle logs are enabled.
24465
+ *
24466
+ * @private function of BookEditorMonaco
24467
+ */
24468
+ function isBookEditorMonacoDebugEnabled() {
24469
+ if (process.env.NODE_ENV === 'production' || typeof window === 'undefined') {
24470
+ return false;
24471
+ }
24472
+ try {
24473
+ return window.localStorage.getItem(BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY) === '1';
24474
+ }
24475
+ catch (_a) {
24476
+ return false;
24477
+ }
24478
+ }
24479
+ /**
24480
+ * Prints one BookEditor Monaco debug line when the dev debug flag is enabled.
24481
+ *
24482
+ * @param message - Human-readable lifecycle message.
24483
+ *
24484
+ * @private function of BookEditorMonaco
24485
+ */
24486
+ function logBookEditorMonacoDebug(message) {
24487
+ if (!isBookEditorMonacoDebugEnabled()) {
24488
+ return;
24489
+ }
24490
+ console.info(`[BookEditorMonaco] ${message}`);
24491
+ }
24492
+ /**
24493
+ * Detects whether the current device primarily uses a coarse pointer.
24494
+ *
24495
+ * @private function of BookEditorMonaco
24496
+ */
24497
+ function detectIsTouchDevice() {
24498
+ if (typeof window === 'undefined') {
24499
+ return false;
24500
+ }
24501
+ return window.matchMedia('(pointer: coarse)').matches;
24502
+ }
24503
+ /**
24504
+ * Manages Monaco lifecycle wiring for `BookEditorMonaco`.
24505
+ *
24506
+ * @private function of BookEditorMonaco
24507
+ */
24508
+ function useBookEditorMonacoLifecycle({ monaco, theme, }) {
24509
+ const [editor, setEditor] = react.useState(null);
24510
+ const [isFocused, setIsFocused] = react.useState(false);
24511
+ const [isTouchDevice, setIsTouchDevice] = react.useState(false);
24512
+ const [isSavedShown, setIsSavedShown] = react.useState(false);
24513
+ /**
24514
+ * Re-applies Book language + theme to the currently mounted Monaco model.
24515
+ */
24516
+ const reapplyBookLanguageAndTheme = react.useCallback((reason) => {
24517
+ if (!editor || !monaco) {
24518
+ return;
24519
+ }
24520
+ ensureBookEditorMonacoLanguageForEditor({ monaco, monacoEditor: editor, theme });
24521
+ logBookEditorMonacoDebug(`Re-applied Book Monaco language/theme (${reason}).`);
24522
+ }, [editor, monaco, theme]);
24523
+ /**
24524
+ * Re-triggers the transient save toast without preventing the browser save dialog.
24525
+ */
24526
+ const showSavedNotification = react.useCallback(() => {
24527
+ setIsSavedShown(false);
24528
+ setTimeout(() => setIsSavedShown(true), 0);
24529
+ }, []);
24530
+ react.useEffect(() => {
24531
+ setIsTouchDevice(detectIsTouchDevice());
24532
+ }, []);
24533
+ react.useEffect(() => {
24534
+ if (!editor || !monaco) {
24535
+ return;
24536
+ }
24537
+ const focusListener = editor.onDidFocusEditorWidget(() => {
24538
+ setIsFocused(true);
24539
+ reapplyBookLanguageAndTheme('focus');
24540
+ });
24541
+ const blurListener = editor.onDidBlurEditorWidget(() => {
24542
+ setIsFocused(false);
24543
+ });
24544
+ const saveAction = editor.addAction({
24545
+ id: 'save-book',
24546
+ label: 'Save',
24547
+ keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],
24548
+ run: () => {
24549
+ showSavedNotification();
24550
+ // Note: We don't prevent default, so browser's save dialog still opens
24551
+ },
24552
+ });
24553
+ return () => {
24554
+ focusListener.dispose();
24555
+ blurListener.dispose();
24556
+ saveAction.dispose();
24557
+ };
24558
+ }, [editor, monaco, reapplyBookLanguageAndTheme, showSavedNotification]);
24559
+ react.useEffect(() => {
24560
+ reapplyBookLanguageAndTheme('editor-ready');
24561
+ }, [reapplyBookLanguageAndTheme]);
24562
+ react.useEffect(() => {
24563
+ if (!editor || !monaco) {
24564
+ return;
24565
+ }
24566
+ const handlePopState = () => {
24567
+ reapplyBookLanguageAndTheme('history-popstate');
24568
+ };
24569
+ const handlePageShow = () => {
24570
+ reapplyBookLanguageAndTheme('pageshow');
24571
+ };
24572
+ const handleWindowFocus = () => {
24573
+ reapplyBookLanguageAndTheme('window-focus');
24574
+ };
24575
+ const handleVisibilityChange = () => {
24576
+ if (document.visibilityState === 'visible') {
24577
+ reapplyBookLanguageAndTheme('visibility-visible');
24578
+ }
24579
+ };
24580
+ window.addEventListener('popstate', handlePopState);
24581
+ window.addEventListener('pageshow', handlePageShow);
24582
+ window.addEventListener('focus', handleWindowFocus);
24583
+ document.addEventListener('visibilitychange', handleVisibilityChange);
24584
+ return () => {
24585
+ window.removeEventListener('popstate', handlePopState);
24586
+ window.removeEventListener('pageshow', handlePageShow);
24587
+ window.removeEventListener('focus', handleWindowFocus);
24588
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
24589
+ };
24590
+ }, [editor, monaco, reapplyBookLanguageAndTheme]);
24591
+ react.useEffect(() => {
24592
+ if (!isSavedShown) {
24593
+ return;
24594
+ }
24595
+ const timer = setTimeout(() => {
24596
+ setIsSavedShown(false);
24597
+ }, SAVE_NOTIFICATION_HIDE_DELAY_MS);
24598
+ return () => {
24599
+ clearTimeout(timer);
24600
+ };
24601
+ }, [isSavedShown]);
24602
+ /**
24603
+ * Ensures Book language/tokenizer is ready before Monaco creates the editor model.
24604
+ */
24605
+ const handleBeforeMonacoMount = react.useCallback((beforeMountMonaco) => {
24606
+ ensureBookEditorMonacoLanguage(beforeMountMonaco, theme);
24607
+ }, [theme]);
24608
+ /**
24609
+ * Re-applies Book language/theme once Monaco editor is mounted.
24610
+ */
24611
+ const handleMonacoMount = react.useCallback((mountedEditor, mountedMonaco) => {
24612
+ setEditor(mountedEditor);
24613
+ ensureBookEditorMonacoLanguageForEditor({ monaco: mountedMonaco, monacoEditor: mountedEditor, theme });
24614
+ logBookEditorMonacoDebug('Mounted Monaco editor and re-applied Book language/theme.');
24615
+ }, [theme]);
24616
+ return {
24617
+ editor,
24618
+ isFocused,
24619
+ isTouchDevice,
24620
+ isSavedShown,
24621
+ handleBeforeMonacoMount,
24622
+ handleMonacoMount,
24623
+ };
24624
+ }
24625
+
24216
24626
  /**
24217
24627
  * Relative Y offset multiplier for aligning line background with Monaco rendering.
24218
24628
  *
@@ -25022,190 +25432,101 @@
25022
25432
  */
25023
25433
  const INVALID_CSS_IDENTIFIER_CHARACTER_PATTERN = /[^a-zA-Z0-9_-]/g;
25024
25434
  /**
25025
- * Clipboard MIME types treated as rich text documents for upload.
25435
+ * Base Book editor font size before zoom scaling.
25026
25436
  *
25027
25437
  * @private Internal utility of `BookEditorMonaco`.
25028
25438
  */
25029
- const RICH_CLIPBOARD_TEXT_MIME_TYPES = new Set(['text/html', 'text/rtf']);
25439
+ const BASE_FONT_SIZE = 20;
25030
25440
  /**
25031
- * Uploaded filename mapping for known rich clipboard MIME types.
25441
+ * Minimum supported Book editor font size after zoom scaling.
25032
25442
  *
25033
25443
  * @private Internal utility of `BookEditorMonaco`.
25034
25444
  */
25035
- const CLIPBOARD_RICH_CONTENT_FILENAMES = {
25036
- 'text/html': 'clipboard-content.html',
25037
- 'text/rtf': 'clipboard-content.rtf',
25038
- 'application/rtf': 'clipboard-content.rtf',
25039
- };
25445
+ const MIN_FONT_SIZE = 8;
25040
25446
  /**
25041
- * Fallback filename used when clipboard MIME type is unknown.
25042
- *
25043
- * @private Internal utility of `BookEditorMonaco`.
25044
- */
25045
- const DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME = 'clipboard-content.txt';
25046
- /**
25047
- * Local storage key that enables BookEditor Monaco lifecycle debug logs in development.
25048
- *
25049
- * @private Internal utility of `BookEditorMonaco`.
25050
- */
25051
- const BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY = 'promptbook-debug-book-editor-monaco';
25052
- /**
25053
- * Resolves whether verbose BookEditor Monaco lifecycle logs are enabled.
25447
+ * Default Monaco scrollbar size before zoom scaling.
25054
25448
  *
25055
25449
  * @private Internal utility of `BookEditorMonaco`.
25056
25450
  */
25057
- function isBookEditorMonacoDebugEnabled() {
25058
- if (process.env.NODE_ENV === 'production' || typeof window === 'undefined') {
25059
- return false;
25060
- }
25061
- try {
25062
- return window.localStorage.getItem(BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY) === '1';
25063
- }
25064
- catch (_a) {
25065
- return false;
25066
- }
25067
- }
25068
- /**
25069
- * Prints one BookEditor Monaco debug line when the dev debug flag is enabled.
25070
- *
25071
- * @param message - Human-readable lifecycle message.
25072
- *
25073
- * @private Internal utility of `BookEditorMonaco`.
25074
- */
25075
- function logBookEditorMonacoDebug(message) {
25076
- if (!isBookEditorMonacoDebugEnabled()) {
25077
- return;
25078
- }
25079
- console.info(`[BookEditorMonaco] ${message}`);
25080
- }
25081
- /**
25082
- * Lists transferable items from a browser `DataTransfer` object.
25083
- *
25084
- * @private Internal utility of `BookEditorMonaco`.
25085
- */
25086
- function listDataTransferItems(dataTransfer) {
25087
- const items = [];
25088
- for (let index = 0; index < dataTransfer.items.length; index++) {
25089
- const item = dataTransfer.items[index];
25090
- if (item) {
25091
- items.push(item);
25092
- }
25093
- }
25094
- return items;
25095
- }
25096
- /**
25097
- * Removes duplicate files by using stable file metadata signature.
25098
- *
25099
- * @private Internal utility of `BookEditorMonaco`.
25100
- */
25101
- function deduplicateFiles(files) {
25102
- const uniqueFiles = new Map();
25103
- for (const file of files) {
25104
- uniqueFiles.set(`${file.name}:${file.type}:${file.size}`, file);
25105
- }
25106
- return Array.from(uniqueFiles.values());
25107
- }
25108
- /**
25109
- * Extracts all file-like clipboard/drop payloads from a `DataTransfer`.
25110
- *
25111
- * @private Internal utility of `BookEditorMonaco`.
25112
- */
25113
- function getDataTransferFiles(dataTransfer) {
25114
- const directFiles = Array.from(dataTransfer.files || []);
25115
- const itemFiles = listDataTransferItems(dataTransfer)
25116
- .filter((item) => item.kind === 'file')
25117
- .map((item) => item.getAsFile())
25118
- .filter((file) => file !== null);
25119
- return deduplicateFiles([...directFiles, ...itemFiles]);
25120
- }
25451
+ const BASE_SCROLLBAR_SIZE = 5;
25121
25452
  /**
25122
- * Picks the richest textual clipboard item that should be uploaded as a document.
25453
+ * Minimum Monaco scrollbar size after zoom scaling.
25123
25454
  *
25124
25455
  * @private Internal utility of `BookEditorMonaco`.
25125
25456
  */
25126
- function getRichClipboardTextItem(dataTransfer) {
25127
- const items = listDataTransferItems(dataTransfer);
25128
- const hasPlainTextItem = items.some((item) => item.kind === 'string' && item.type.toLowerCase() === 'text/plain');
25129
- const applicationItem = items.find((item) => item.kind === 'string' && item.type.toLowerCase().startsWith('application/'));
25130
- if (applicationItem) {
25131
- return applicationItem;
25132
- }
25133
- if (hasPlainTextItem) {
25134
- return null;
25135
- }
25136
- const richTextItem = items.find((item) => {
25137
- if (item.kind !== 'string') {
25138
- return false;
25139
- }
25140
- return RICH_CLIPBOARD_TEXT_MIME_TYPES.has(item.type.toLowerCase());
25141
- });
25142
- if (richTextItem) {
25143
- return richTextItem;
25144
- }
25145
- return null;
25146
- }
25457
+ const MIN_SCROLLBAR_SIZE = 2;
25147
25458
  /**
25148
- * Resolves whether paste should route into upload workflow instead of text insert.
25459
+ * Minimum left padding Monaco reserves for custom line decorations.
25149
25460
  *
25150
25461
  * @private Internal utility of `BookEditorMonaco`.
25151
25462
  */
25152
- function hasUploadableClipboardContent(dataTransfer) {
25153
- if (getDataTransferFiles(dataTransfer).length > 0) {
25154
- return true;
25155
- }
25156
- return getRichClipboardTextItem(dataTransfer) !== null;
25157
- }
25463
+ const MIN_CONTENT_PADDING_LEFT = 8;
25158
25464
  /**
25159
- * Reads string payload from clipboard item.
25465
+ * Creates a hydration-stable CSS class name from React's `useId()` value.
25160
25466
  *
25161
25467
  * @private Internal utility of `BookEditorMonaco`.
25162
25468
  */
25163
- function getClipboardItemString(item) {
25164
- return new Promise((resolve) => {
25165
- try {
25166
- item.getAsString((value) => resolve(value || ''));
25167
- }
25168
- catch (_a) {
25169
- resolve('');
25170
- }
25171
- });
25469
+ function createStableBookEditorInstanceClassName(reactId) {
25470
+ return `book-editor-instance-${reactId.replace(INVALID_CSS_IDENTIFIER_CHARACTER_PATTERN, '-')}`;
25172
25471
  }
25173
25472
  /**
25174
- * Determines filename for generated clipboard rich-content uploads.
25473
+ * Resolves Monaco layout values from the active Book editor zoom level.
25175
25474
  *
25176
25475
  * @private Internal utility of `BookEditorMonaco`.
25177
25476
  */
25178
- function getClipboardRichContentFilename(mimeType) {
25179
- return CLIPBOARD_RICH_CONTENT_FILENAMES[mimeType.toLowerCase()] || DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME;
25477
+ function createBookEditorMonacoScale(zoomLevel) {
25478
+ return {
25479
+ scaledLineHeight: Math.round(BookEditorMonacoConstants.LINE_HEIGHT * zoomLevel),
25480
+ scaledContentPaddingLeft: Math.max(MIN_CONTENT_PADDING_LEFT, Math.round(BookEditorMonacoConstants.CONTENT_PADDING_LEFT * zoomLevel)),
25481
+ scaledVerticalLineLeft: Math.max(0, Math.round(BookEditorMonacoConstants.VERTICAL_LINE_LEFT * zoomLevel)),
25482
+ scaledFontSize: Math.max(MIN_FONT_SIZE, Math.round(BASE_FONT_SIZE * zoomLevel)),
25483
+ scaledScrollbarSize: Math.max(MIN_SCROLLBAR_SIZE, Math.round(BASE_SCROLLBAR_SIZE * zoomLevel)),
25484
+ };
25180
25485
  }
25181
25486
  /**
25182
- * Creates a hydration-stable CSS class name from React's `useId()` value.
25487
+ * Determines whether the Book editor action bar should be shown.
25183
25488
  *
25184
25489
  * @private Internal utility of `BookEditorMonaco`.
25185
25490
  */
25186
- function createStableBookEditorInstanceClassName(reactId) {
25187
- return `book-editor-instance-${reactId.replace(INVALID_CSS_IDENTIFIER_CHARACTER_PATTERN, '-')}`;
25491
+ function isBookEditorMonacoActionbarVisible({ hoistedMenuItems, isUploadButtonShown, isCameraButtonShown, isDownloadButtonShown, isAboutButtonShown, isFullscreenButtonShown, }) {
25492
+ return (Boolean(hoistedMenuItems && hoistedMenuItems.length > 0) ||
25493
+ Boolean(isUploadButtonShown) ||
25494
+ Boolean(isCameraButtonShown) ||
25495
+ Boolean(isDownloadButtonShown) ||
25496
+ Boolean(isAboutButtonShown) ||
25497
+ Boolean(isFullscreenButtonShown));
25188
25498
  }
25189
25499
  /**
25190
- * Converts clipboard payload into upload-ready files.
25500
+ * Builds the Monaco editor options consumed by `MonacoEditorWithShadowDom`.
25191
25501
  *
25192
25502
  * @private Internal utility of `BookEditorMonaco`.
25193
25503
  */
25194
- async function resolveClipboardUploadFiles(dataTransfer) {
25195
- const files = getDataTransferFiles(dataTransfer);
25196
- if (files.length > 0) {
25197
- return files;
25198
- }
25199
- const richTextItem = getRichClipboardTextItem(dataTransfer);
25200
- if (!richTextItem) {
25201
- return [];
25202
- }
25203
- const content = await getClipboardItemString(richTextItem);
25204
- const mimeType = richTextItem.type || 'text/plain';
25205
- if (!content.trim()) {
25206
- return [];
25207
- }
25208
- return [new File([content], getClipboardRichContentFilename(mimeType), { type: mimeType })];
25504
+ function createBookEditorMonacoOptions({ isReadonly, translations, scaledFontSize, scaledLineHeight, scaledContentPaddingLeft, scaledScrollbarSize, }) {
25505
+ return {
25506
+ readOnly: isReadonly,
25507
+ readOnlyMessage: {
25508
+ value: (translations === null || translations === void 0 ? void 0 : translations.readonlyMessage) || 'You cannot edit this book',
25509
+ },
25510
+ wordWrap: 'on',
25511
+ minimap: { enabled: false },
25512
+ lineNumbers: 'off',
25513
+ fontSize: scaledFontSize,
25514
+ fontFamily: `"Playfair Display", serif`,
25515
+ lineHeight: scaledLineHeight,
25516
+ renderLineHighlight: 'none',
25517
+ lineDecorationsWidth: scaledContentPaddingLeft,
25518
+ glyphMargin: false,
25519
+ folding: false,
25520
+ lineNumbersMinChars: 0,
25521
+ links: true,
25522
+ scrollbar: {
25523
+ vertical: 'auto',
25524
+ horizontal: 'hidden',
25525
+ verticalScrollbarSize: scaledScrollbarSize,
25526
+ arrowSize: 0,
25527
+ useShadows: false,
25528
+ },
25529
+ };
25209
25530
  }
25210
25531
  /**
25211
25532
  * Handles book editor monaco.
@@ -25215,29 +25536,40 @@
25215
25536
  function BookEditorMonaco(props) {
25216
25537
  const { value, onChange, diagnostics, isReadonly, theme = 'LIGHT', translations, onFileUpload, isUploadButtonShown, isCameraButtonShown, isDownloadButtonShown, isAboutButtonShown = true, isFullscreenButtonShown = true, onFullscreenClick, isFullscreen, zoom = 1, monacoModelPath, hoistedMenuItems, } = props;
25217
25538
  const zoomLevel = zoom;
25218
- const scaledLineHeight = Math.round(BookEditorMonacoConstants.LINE_HEIGHT * zoomLevel);
25219
- const scaledContentPaddingLeft = Math.max(8, Math.round(BookEditorMonacoConstants.CONTENT_PADDING_LEFT * zoomLevel));
25220
- const scaledVerticalLineLeft = Math.max(0, Math.round(BookEditorMonacoConstants.VERTICAL_LINE_LEFT * zoomLevel));
25221
- const baseFontSize = 20;
25222
- const scaledFontSize = Math.max(8, Math.round(baseFontSize * zoomLevel));
25223
- const scaledScrollbarSize = Math.max(2, Math.round(5 * zoomLevel));
25224
- const [isDragOver, setIsDragOver] = react.useState(false);
25225
- const [editor, setEditor] = react.useState(null);
25226
- const [isFocused, setIsFocused] = react.useState(false);
25227
- const [isTouchDevice, setIsTouchDevice] = react.useState(false);
25228
- const [isSavedShown, setIsSavedShown] = react.useState(false);
25539
+ const { scaledLineHeight, scaledContentPaddingLeft, scaledVerticalLineLeft, scaledFontSize, scaledScrollbarSize } = createBookEditorMonacoScale(zoomLevel);
25229
25540
  const monaco = MonacoEditor.useMonaco();
25230
25541
  const reactId = react.useId();
25231
25542
  const instanceClass = createStableBookEditorInstanceClassName(reactId);
25232
- const touchStartRef = react.useRef(null);
25233
- const fileUploadInputRef = react.useRef(null);
25234
- const cameraInputRef = react.useRef(null);
25543
+ const { editor, isFocused, isTouchDevice, isSavedShown, handleBeforeMonacoMount, handleMonacoMount } = useBookEditorMonacoLifecycle({
25544
+ monaco,
25545
+ theme,
25546
+ });
25235
25547
  const { activeUploadItems, uploadStats, pauseUpload, resumeUpload, handleFiles } = useBookEditorMonacoUploads({
25236
25548
  editor,
25237
25549
  monaco,
25238
25550
  onFileUpload,
25239
25551
  });
25552
+ const { isDragOver, fileUploadInputRef, cameraInputRef, handleDrop, handlePaste, handleUploadDocument, handleTakePhoto, handleFileInputChange, handleDragOver, handleDragEnter, handleDragLeave, focusOverlayTouchHandlers, } = useBookEditorMonacoInteractions({
25553
+ editor,
25554
+ handleFiles,
25555
+ });
25240
25556
  const combinedDiagnostics = [...(diagnostics || []), ...createDeprecatedCommitmentDiagnostics(value)];
25557
+ const isActionBarVisible = isBookEditorMonacoActionbarVisible({
25558
+ hoistedMenuItems,
25559
+ isUploadButtonShown,
25560
+ isCameraButtonShown,
25561
+ isDownloadButtonShown,
25562
+ isAboutButtonShown,
25563
+ isFullscreenButtonShown,
25564
+ });
25565
+ const monacoOptions = createBookEditorMonacoOptions({
25566
+ isReadonly,
25567
+ translations,
25568
+ scaledFontSize,
25569
+ scaledLineHeight,
25570
+ scaledContentPaddingLeft,
25571
+ scaledScrollbarSize,
25572
+ });
25241
25573
  useBookEditorMonacoLanguage({ monaco, theme });
25242
25574
  useBookEditorMonacoDiagnostics({ monaco, editor, diagnostics: combinedDiagnostics });
25243
25575
  useBookEditorMonacoDecorations({ editor, monaco });
@@ -25249,171 +25581,6 @@
25249
25581
  zoomLevel,
25250
25582
  theme,
25251
25583
  });
25252
- /**
25253
- * Re-applies Book language + theme to the currently mounted Monaco model.
25254
- */
25255
- const reapplyBookLanguageAndTheme = react.useCallback((reason) => {
25256
- if (!editor || !monaco) {
25257
- return;
25258
- }
25259
- ensureBookEditorMonacoLanguageForEditor({ monaco, monacoEditor: editor, theme });
25260
- logBookEditorMonacoDebug(`Re-applied Book Monaco language/theme (${reason}).`);
25261
- }, [editor, monaco, theme]);
25262
- react.useEffect(() => {
25263
- setIsTouchDevice(typeof window !== 'undefined' && window.matchMedia('(pointer: coarse)').matches);
25264
- }, []);
25265
- react.useEffect(() => {
25266
- if (!editor || !monaco) {
25267
- return;
25268
- }
25269
- const focusListener = editor.onDidFocusEditorWidget(() => {
25270
- setIsFocused(true);
25271
- reapplyBookLanguageAndTheme('focus');
25272
- });
25273
- const blurListener = editor.onDidBlurEditorWidget(() => {
25274
- setIsFocused(false);
25275
- });
25276
- const saveAction = editor.addAction({
25277
- id: 'save-book',
25278
- label: 'Save',
25279
- keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],
25280
- run: () => {
25281
- setIsSavedShown(false);
25282
- setTimeout(() => setIsSavedShown(true), 0);
25283
- // Note: We don't prevent default, so browser's save dialog still opens
25284
- },
25285
- });
25286
- return () => {
25287
- focusListener.dispose();
25288
- blurListener.dispose();
25289
- saveAction.dispose();
25290
- };
25291
- }, [editor, monaco, reapplyBookLanguageAndTheme]);
25292
- react.useEffect(() => {
25293
- reapplyBookLanguageAndTheme('editor-ready');
25294
- }, [reapplyBookLanguageAndTheme]);
25295
- react.useEffect(() => {
25296
- if (!editor || !monaco) {
25297
- return;
25298
- }
25299
- const handlePopState = () => {
25300
- reapplyBookLanguageAndTheme('history-popstate');
25301
- };
25302
- const handlePageShow = () => {
25303
- reapplyBookLanguageAndTheme('pageshow');
25304
- };
25305
- const handleWindowFocus = () => {
25306
- reapplyBookLanguageAndTheme('window-focus');
25307
- };
25308
- const handleVisibilityChange = () => {
25309
- if (document.visibilityState === 'visible') {
25310
- reapplyBookLanguageAndTheme('visibility-visible');
25311
- }
25312
- };
25313
- window.addEventListener('popstate', handlePopState);
25314
- window.addEventListener('pageshow', handlePageShow);
25315
- window.addEventListener('focus', handleWindowFocus);
25316
- document.addEventListener('visibilitychange', handleVisibilityChange);
25317
- return () => {
25318
- window.removeEventListener('popstate', handlePopState);
25319
- window.removeEventListener('pageshow', handlePageShow);
25320
- window.removeEventListener('focus', handleWindowFocus);
25321
- document.removeEventListener('visibilitychange', handleVisibilityChange);
25322
- };
25323
- }, [editor, monaco, reapplyBookLanguageAndTheme]);
25324
- react.useEffect(() => {
25325
- if (!isSavedShown) {
25326
- return;
25327
- }
25328
- const timer = setTimeout(() => {
25329
- setIsSavedShown(false);
25330
- }, 2000);
25331
- return () => {
25332
- clearTimeout(timer);
25333
- };
25334
- }, [isSavedShown]);
25335
- const handleDrop = react.useCallback(async (event) => {
25336
- event.preventDefault();
25337
- setIsDragOver(false);
25338
- const files = getDataTransferFiles(event.dataTransfer);
25339
- await handleFiles(files);
25340
- }, [handleFiles]);
25341
- const handlePaste = react.useCallback(async (event) => {
25342
- const clipboardData = event.clipboardData;
25343
- if (!hasUploadableClipboardContent(clipboardData)) {
25344
- return;
25345
- }
25346
- event.preventDefault();
25347
- event.stopPropagation();
25348
- const files = await resolveClipboardUploadFiles(clipboardData);
25349
- await handleFiles(files);
25350
- }, [handleFiles]);
25351
- const handleUploadDocument = react.useCallback(() => {
25352
- var _a;
25353
- (_a = fileUploadInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
25354
- }, []);
25355
- const handleTakePhoto = react.useCallback(() => {
25356
- var _a;
25357
- (_a = cameraInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
25358
- }, []);
25359
- const handleFileInputChange = react.useCallback((event) => {
25360
- const files = Array.from(event.target.files || []);
25361
- void handleFiles(files);
25362
- event.target.value = '';
25363
- }, [handleFiles]);
25364
- /**
25365
- * Ensures Book language/tokenizer is ready before Monaco creates the editor model.
25366
- */
25367
- const handleBeforeMonacoMount = react.useCallback((beforeMountMonaco) => {
25368
- ensureBookEditorMonacoLanguage(beforeMountMonaco, theme);
25369
- }, [theme]);
25370
- /**
25371
- * Re-applies Book language/theme once Monaco editor is mounted.
25372
- */
25373
- const handleMonacoMount = react.useCallback((mountedEditor, mountedMonaco) => {
25374
- setEditor(mountedEditor);
25375
- ensureBookEditorMonacoLanguageForEditor({ monaco: mountedMonaco, monacoEditor: mountedEditor, theme });
25376
- logBookEditorMonacoDebug('Mounted Monaco editor and re-applied Book language/theme.');
25377
- }, [theme]);
25378
- const handleDragOver = react.useCallback((event) => {
25379
- event.preventDefault();
25380
- setIsDragOver(true);
25381
- }, []);
25382
- const handleDragEnter = react.useCallback((event) => {
25383
- event.preventDefault();
25384
- setIsDragOver(true);
25385
- }, []);
25386
- const handleDragLeave = react.useCallback((event) => {
25387
- event.preventDefault();
25388
- setIsDragOver(false);
25389
- }, []);
25390
- const focusOverlayTouchHandlers = {
25391
- onTouchStart: (event) => {
25392
- const touch = event.touches[0];
25393
- if (touch) {
25394
- touchStartRef.current = { x: touch.clientX, y: touch.clientY };
25395
- }
25396
- },
25397
- onTouchEnd: (event) => {
25398
- event.preventDefault();
25399
- const touch = event.changedTouches[0];
25400
- if (touch && touchStartRef.current) {
25401
- const deltaX = Math.abs(touch.clientX - touchStartRef.current.x);
25402
- const deltaY = Math.abs(touch.clientY - touchStartRef.current.y);
25403
- const threshold = 10;
25404
- if (deltaX < threshold && deltaY < threshold) {
25405
- editor === null || editor === void 0 ? void 0 : editor.focus();
25406
- }
25407
- }
25408
- touchStartRef.current = null;
25409
- },
25410
- };
25411
- const isActionBarVisible = Boolean(hoistedMenuItems && hoistedMenuItems.length > 0) ||
25412
- isUploadButtonShown ||
25413
- isCameraButtonShown ||
25414
- isDownloadButtonShown ||
25415
- isAboutButtonShown ||
25416
- isFullscreenButtonShown;
25417
25584
  return (jsxRuntime.jsxs("div", { className: classNames(styles$d.bookEditorContainer, instanceClass), onDrop: handleDrop, onPaste: handlePaste, onDragOver: handleDragOver, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, children: [isActionBarVisible && (jsxRuntime.jsx(BookEditorActionbar, { value,
25418
25585
  isUploadButtonShown,
25419
25586
  isCameraButtonShown: isCameraButtonShown !== null && isCameraButtonShown !== void 0 ? isCameraButtonShown : isTouchDevice,
@@ -25439,31 +25606,7 @@
25439
25606
  height: '100%',
25440
25607
  width: '100%',
25441
25608
  backgroundColor: 'transparent',
25442
- }, ...focusOverlayTouchHandlers })), jsxRuntime.jsx(MonacoEditorWithShadowDom, { language: BookEditorMonacoConstants.BOOK_LANGUAGE_ID, theme: BookEditorMonacoConstants.BOOK_THEME_ID, path: monacoModelPath, saveViewState: Boolean(monacoModelPath), value: value, beforeMount: handleBeforeMonacoMount, onMount: handleMonacoMount, onChange: (newValue) => onChange === null || onChange === void 0 ? void 0 : onChange(newValue), options: {
25443
- readOnly: isReadonly,
25444
- readOnlyMessage: {
25445
- value: (translations === null || translations === void 0 ? void 0 : translations.readonlyMessage) || 'You cannot edit this book',
25446
- },
25447
- wordWrap: 'on',
25448
- minimap: { enabled: false },
25449
- lineNumbers: 'off',
25450
- fontSize: scaledFontSize,
25451
- fontFamily: `"Playfair Display", serif`,
25452
- lineHeight: scaledLineHeight,
25453
- renderLineHighlight: 'none',
25454
- lineDecorationsWidth: scaledContentPaddingLeft,
25455
- glyphMargin: false,
25456
- folding: false,
25457
- lineNumbersMinChars: 0,
25458
- links: true,
25459
- scrollbar: {
25460
- vertical: 'auto',
25461
- horizontal: 'hidden',
25462
- verticalScrollbarSize: scaledScrollbarSize,
25463
- arrowSize: 0,
25464
- useShadows: false,
25465
- },
25466
- }, loading: jsxRuntime.jsx("div", { className: styles$d.loading, children: "\uD83D\uDCD6" }) })] })] }));
25609
+ }, ...focusOverlayTouchHandlers })), jsxRuntime.jsx(MonacoEditorWithShadowDom, { language: BookEditorMonacoConstants.BOOK_LANGUAGE_ID, theme: BookEditorMonacoConstants.BOOK_THEME_ID, path: monacoModelPath, saveViewState: Boolean(monacoModelPath), value: value, beforeMount: handleBeforeMonacoMount, onMount: handleMonacoMount, onChange: (newValue) => onChange === null || onChange === void 0 ? void 0 : onChange(newValue), options: monacoOptions, loading: jsxRuntime.jsx("div", { className: styles$d.loading, children: "\uD83D\uDCD6" }) })] })] }));
25467
25610
  }
25468
25611
 
25469
25612
  /**
@@ -41080,8 +41223,8 @@
41080
41223
  * Prepares an AgentKit agent with optional knowledge sources and tool definitions.
41081
41224
  */
41082
41225
  async prepareAgentKitAgent(options) {
41083
- var _a, _b;
41084
- const { name, instructions, knowledgeSources, tools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
41226
+ var _a, _b, _c;
41227
+ const { name, instructions, knowledgeSources, tools, nativeAgentKitTools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
41085
41228
  await this.ensureAgentKitDefaults();
41086
41229
  if (this.options.isVerbose) {
41087
41230
  console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
@@ -41089,6 +41232,7 @@
41089
41232
  instructionsLength: instructions.length,
41090
41233
  knowledgeSourcesCount: (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0,
41091
41234
  toolsCount: (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0,
41235
+ nativeAgentKitToolsCount: (_c = nativeAgentKitTools === null || nativeAgentKitTools === void 0 ? void 0 : nativeAgentKitTools.length) !== null && _c !== void 0 ? _c : 0,
41092
41236
  });
41093
41237
  }
41094
41238
  let vectorStoreId = cachedVectorStoreId;
@@ -41107,7 +41251,7 @@
41107
41251
  vectorStoreId,
41108
41252
  });
41109
41253
  }
41110
- const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
41254
+ const agentKitTools = this.buildAgentKitTools({ tools, nativeAgentKitTools, vectorStoreId });
41111
41255
  const openAiAgentKitAgent = new agents.Agent({
41112
41256
  name,
41113
41257
  model: this.agentKitModelName,
@@ -41146,11 +41290,14 @@
41146
41290
  * Builds the tool list for AgentKit, including hosted file search when applicable.
41147
41291
  */
41148
41292
  buildAgentKitTools(options) {
41149
- const { tools, vectorStoreId } = options;
41293
+ const { tools, nativeAgentKitTools, vectorStoreId } = options;
41150
41294
  const agentKitTools = [];
41151
41295
  if (vectorStoreId) {
41152
41296
  agentKitTools.push(agents.fileSearchTool(vectorStoreId));
41153
41297
  }
41298
+ if (nativeAgentKitTools && nativeAgentKitTools.length > 0) {
41299
+ agentKitTools.push(...nativeAgentKitTools);
41300
+ }
41154
41301
  if (tools && tools.length > 0) {
41155
41302
  let scriptTools = null;
41156
41303
  for (const toolDefinition of tools) {