@pixerate/editor-react 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/canvas/index.cjs +352 -118
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +112 -8
- package/dist/canvas/index.d.ts +112 -8
- package/dist/canvas/index.js +258 -19
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-4ZC2WCUO.js +196 -0
- package/dist/chunk-4ZC2WCUO.js.map +1 -0
- package/dist/chunk-7SPRQZ6H.js +216 -0
- package/dist/chunk-7SPRQZ6H.js.map +1 -0
- package/dist/{chunk-25S5EHH4.js → chunk-FYIPXHAE.js} +22 -13
- package/dist/chunk-FYIPXHAE.js.map +1 -0
- package/dist/{chunk-TL27COKM.js → chunk-XTFR3IMK.js} +683 -286
- package/dist/chunk-XTFR3IMK.js.map +1 -0
- package/dist/chunk-XWFU2CSG.js +167 -0
- package/dist/chunk-XWFU2CSG.js.map +1 -0
- package/dist/commands/index.cjs +232 -0
- package/dist/commands/index.cjs.map +1 -0
- package/dist/commands/index.d.cts +69 -0
- package/dist/commands/index.d.ts +69 -0
- package/dist/commands/index.js +13 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/generation/index.cjs +235 -0
- package/dist/generation/index.cjs.map +1 -0
- package/dist/generation/index.d.cts +60 -0
- package/dist/generation/index.d.ts +60 -0
- package/dist/generation/index.js +13 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/image-editor/index.cjs +688 -299
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +65 -5
- package/dist/image-editor/index.d.ts +65 -5
- package/dist/image-editor/index.js +13 -3
- package/dist/index.cjs +2200 -683
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +201 -23
- package/dist/index.d.ts +201 -23
- package/dist/index.js +743 -129
- package/dist/index.js.map +1 -1
- package/dist/persistence/index.cjs +191 -0
- package/dist/persistence/index.cjs.map +1 -0
- package/dist/persistence/index.d.cts +77 -0
- package/dist/persistence/index.d.ts +77 -0
- package/dist/persistence/index.js +9 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/spreadsheet/index.cjs +17 -10
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +9 -7
- package/dist/spreadsheet/index.d.ts +9 -7
- package/dist/spreadsheet/index.js +1 -1
- package/package.json +26 -2
- package/dist/chunk-25S5EHH4.js.map +0 -1
- package/dist/chunk-TL27COKM.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -30,41 +30,64 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
AdjustmentLayersPanel: () => AdjustmentLayersPanel,
|
|
33
34
|
BubbleMenu: () => BubbleMenu,
|
|
35
|
+
CommandPalette: () => CommandPalette,
|
|
34
36
|
CommentButton: () => CommentButton,
|
|
35
37
|
CommentsPanel: () => CommentsPanel,
|
|
36
|
-
|
|
38
|
+
ControlSlider: () => ControlSlider,
|
|
39
|
+
CurvesEditor: () => CurvesEditor,
|
|
40
|
+
DirtyTracker: () => import_editor16.DirtyTracker,
|
|
37
41
|
EditorContent: () => EditorContent,
|
|
38
42
|
EditorSlotsProvider: () => EditorSlotsProvider,
|
|
43
|
+
FindReplaceBar: () => FindReplaceBar,
|
|
39
44
|
FormulaHints: () => FormulaHints,
|
|
40
|
-
|
|
45
|
+
GenerationSettings: () => GenerationSettings,
|
|
46
|
+
Histogram: () => Histogram,
|
|
47
|
+
HistoryManager: () => import_editor16.HistoryManager,
|
|
41
48
|
ImageEditor: () => ImageEditor,
|
|
49
|
+
JobProgress: () => JobProgress,
|
|
50
|
+
MergePreview: () => MergePreview,
|
|
42
51
|
OutlinePanel: () => OutlinePanel,
|
|
52
|
+
RecoveryPrompt: () => RecoveryPrompt,
|
|
43
53
|
ReviewToolbar: () => ReviewToolbar,
|
|
44
54
|
SpreadsheetContextMenu: () => SpreadsheetContextMenu,
|
|
45
55
|
SpreadsheetEditor: () => SpreadsheetEditor,
|
|
46
56
|
SpreadsheetGroupRow: () => SpreadsheetGroupRow,
|
|
47
57
|
TemplateRenderer: () => TemplateRenderer,
|
|
48
58
|
VariableForm: () => VariableForm,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
bindDirtyToHistory: () => import_editor16.bindDirtyToHistory,
|
|
60
|
+
createDirtyTracker: () => import_editor16.createDirtyTracker,
|
|
61
|
+
createHistoryManager: () => import_editor16.createHistoryManager,
|
|
62
|
+
defaultIsEqual: () => import_editor16.defaultIsEqual,
|
|
52
63
|
useAgentToolProvider: () => useAgentToolProvider,
|
|
64
|
+
useAutosave: () => useAutosave,
|
|
65
|
+
useCommandPalette: () => useCommandPalette,
|
|
66
|
+
useCommandShortcuts: () => useCommandShortcuts,
|
|
53
67
|
useComments: () => useComments,
|
|
54
68
|
useDismissGuard: () => useDismissGuard,
|
|
69
|
+
useDocumentCompare: () => useDocumentCompare,
|
|
55
70
|
useEditor: () => useEditor2,
|
|
56
71
|
useEditorSlots: () => useEditorSlots,
|
|
57
72
|
useFileImport: () => useFileImport,
|
|
58
73
|
useFillHandle: () => useFillHandle,
|
|
74
|
+
useFindReplace: () => useFindReplace,
|
|
75
|
+
useGenerationPlan: () => useGenerationPlan,
|
|
59
76
|
useHistory: () => useHistory,
|
|
77
|
+
useHistoryDirty: () => useHistoryDirty,
|
|
60
78
|
useHistoryShortcuts: () => useHistoryShortcuts,
|
|
61
79
|
useImageEditor: () => useImageEditor,
|
|
80
|
+
useImageHistogram: () => useImageHistogram,
|
|
81
|
+
useJobProgress: () => useJobProgress,
|
|
82
|
+
useMergePreview: () => useMergePreview,
|
|
62
83
|
useNavigationGuard: () => useNavigationGuard,
|
|
63
84
|
useOutline: () => useOutline,
|
|
64
85
|
usePromptEditor: () => usePromptEditor,
|
|
86
|
+
useRegisterCommands: () => useRegisterCommands,
|
|
65
87
|
useSpreadsheetClipboard: () => useSpreadsheetClipboard,
|
|
66
88
|
useSpreadsheetEditor: () => useSpreadsheetEditor,
|
|
67
|
-
useTrackChanges: () => useTrackChanges
|
|
89
|
+
useTrackChanges: () => useTrackChanges,
|
|
90
|
+
withoutDirty: () => import_editor16.withoutDirty
|
|
68
91
|
});
|
|
69
92
|
module.exports = __toCommonJS(src_exports);
|
|
70
93
|
|
|
@@ -470,6 +493,7 @@ function useHistory(managerOrOptions) {
|
|
|
470
493
|
() => manager.getState(),
|
|
471
494
|
() => manager.getState()
|
|
472
495
|
);
|
|
496
|
+
const entries = (0, import_react6.useMemo)(() => manager.getEntries(), [manager, state]);
|
|
473
497
|
const undo = (0, import_react6.useCallback)(() => manager.undo(), [manager]);
|
|
474
498
|
const redo = (0, import_react6.useCallback)(() => manager.redo(), [manager]);
|
|
475
499
|
const clear = (0, import_react6.useCallback)(() => manager.clear(), [manager]);
|
|
@@ -485,60 +509,102 @@ function useHistory(managerOrOptions) {
|
|
|
485
509
|
(name, fn) => manager.batch(name, fn),
|
|
486
510
|
[manager]
|
|
487
511
|
);
|
|
512
|
+
const goTo = (0, import_react6.useCallback)((index) => manager.goTo(index), [manager]);
|
|
513
|
+
const mark = (0, import_react6.useCallback)(() => manager.mark(), [manager]);
|
|
514
|
+
const revertTo = (0, import_react6.useCallback)(
|
|
515
|
+
(m) => manager.revertTo(m),
|
|
516
|
+
[manager]
|
|
517
|
+
);
|
|
518
|
+
const beginGesture = (0, import_react6.useCallback)(
|
|
519
|
+
(key) => manager.beginGesture(key),
|
|
520
|
+
[manager]
|
|
521
|
+
);
|
|
522
|
+
const endGesture = (0, import_react6.useCallback)(() => manager.endGesture(), [manager]);
|
|
488
523
|
return {
|
|
489
524
|
...state,
|
|
490
525
|
manager,
|
|
526
|
+
entries,
|
|
491
527
|
undo,
|
|
492
528
|
redo,
|
|
493
529
|
clear,
|
|
494
530
|
execute,
|
|
495
531
|
executeMerged,
|
|
496
|
-
batch
|
|
532
|
+
batch,
|
|
533
|
+
goTo,
|
|
534
|
+
mark,
|
|
535
|
+
revertTo,
|
|
536
|
+
beginGesture,
|
|
537
|
+
endGesture
|
|
497
538
|
};
|
|
498
539
|
}
|
|
499
540
|
|
|
500
|
-
// src/hooks/
|
|
541
|
+
// src/hooks/useHistoryDirty.ts
|
|
501
542
|
var import_react7 = require("react");
|
|
543
|
+
var import_editor4 = require("@pixerate/editor");
|
|
544
|
+
function useHistoryDirty(options) {
|
|
545
|
+
const getValueRef = (0, import_react7.useRef)(options.getValue);
|
|
546
|
+
getValueRef.current = options.getValue;
|
|
547
|
+
const [tracker] = (0, import_react7.useState)(
|
|
548
|
+
() => options.tracker ?? new import_editor4.DirtyTracker(options.getValue(), {
|
|
549
|
+
isEqual: options.isEqual,
|
|
550
|
+
normalize: options.normalize
|
|
551
|
+
})
|
|
552
|
+
);
|
|
553
|
+
const bindingRef = (0, import_react7.useRef)(null);
|
|
554
|
+
(0, import_react7.useEffect)(() => {
|
|
555
|
+
const binding = (0, import_editor4.bindDirtyToHistory)({
|
|
556
|
+
history: options.history,
|
|
557
|
+
getValue: () => getValueRef.current(),
|
|
558
|
+
tracker
|
|
559
|
+
});
|
|
560
|
+
bindingRef.current = binding;
|
|
561
|
+
return () => {
|
|
562
|
+
binding.destroy();
|
|
563
|
+
if (bindingRef.current === binding) bindingRef.current = null;
|
|
564
|
+
};
|
|
565
|
+
}, [options.history, tracker]);
|
|
566
|
+
const isDirty = (0, import_react7.useSyncExternalStore)(
|
|
567
|
+
(0, import_react7.useCallback)((onChange) => tracker.subscribe(onChange), [tracker]),
|
|
568
|
+
() => tracker.dirty,
|
|
569
|
+
() => tracker.dirty
|
|
570
|
+
);
|
|
571
|
+
const markClean = (0, import_react7.useCallback)(
|
|
572
|
+
() => tracker.markClean(getValueRef.current()),
|
|
573
|
+
[tracker]
|
|
574
|
+
);
|
|
575
|
+
const suppress = (0, import_react7.useCallback)(
|
|
576
|
+
(fn) => bindingRef.current ? bindingRef.current.suppress(fn) : tracker.suppress(fn),
|
|
577
|
+
[tracker]
|
|
578
|
+
);
|
|
579
|
+
const refresh = (0, import_react7.useCallback)(
|
|
580
|
+
() => tracker.update(getValueRef.current()),
|
|
581
|
+
[tracker]
|
|
582
|
+
);
|
|
583
|
+
return { isDirty, markClean, suppress, refresh, tracker };
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/hooks/useHistoryShortcuts.ts
|
|
587
|
+
var import_react8 = require("react");
|
|
588
|
+
var import_commands = require("@pixerate/editor/commands");
|
|
502
589
|
function useHistoryShortcuts(manager, options = {}) {
|
|
503
590
|
const {
|
|
504
591
|
enabled = true,
|
|
505
592
|
target,
|
|
506
593
|
ignoreEditableElements = true
|
|
507
594
|
} = options;
|
|
508
|
-
(0,
|
|
595
|
+
(0, import_react8.useEffect)(() => {
|
|
509
596
|
if (!enabled) return;
|
|
510
597
|
const eventTarget = target ?? (typeof window !== "undefined" ? window : null);
|
|
511
598
|
if (!eventTarget) return;
|
|
512
599
|
const handleKeyDown = (event) => {
|
|
513
600
|
const e = event;
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const isInput = tagName === "input" || tagName === "textarea";
|
|
522
|
-
const isContentEditable = targetEl.isContentEditable || targetEl.getAttribute?.("contenteditable") === "true";
|
|
523
|
-
if (isInput || isContentEditable) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
const key = e.key.toLowerCase();
|
|
529
|
-
if (key === "z" && !e.shiftKey) {
|
|
530
|
-
if (manager.getState().canUndo) {
|
|
531
|
-
e.preventDefault();
|
|
532
|
-
manager.undo();
|
|
533
|
-
}
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
const isRedoKey = key === "z" && e.shiftKey || !isMac && key === "y" && !e.shiftKey;
|
|
537
|
-
if (isRedoKey) {
|
|
538
|
-
if (manager.getState().canRedo) {
|
|
539
|
-
e.preventDefault();
|
|
540
|
-
manager.redo();
|
|
541
|
-
}
|
|
601
|
+
const action = (0, import_commands.matchHistoryShortcut)(e, { allowInEditable: !ignoreEditableElements });
|
|
602
|
+
if (action === "undo" && manager.getState().canUndo) {
|
|
603
|
+
e.preventDefault();
|
|
604
|
+
manager.undo();
|
|
605
|
+
} else if (action === "redo" && manager.getState().canRedo) {
|
|
606
|
+
e.preventDefault();
|
|
607
|
+
manager.redo();
|
|
542
608
|
}
|
|
543
609
|
};
|
|
544
610
|
eventTarget.addEventListener("keydown", handleKeyDown);
|
|
@@ -549,22 +615,22 @@ function useHistoryShortcuts(manager, options = {}) {
|
|
|
549
615
|
}
|
|
550
616
|
|
|
551
617
|
// src/hooks/useFileImport.ts
|
|
552
|
-
var
|
|
553
|
-
var
|
|
618
|
+
var import_react9 = require("react");
|
|
619
|
+
var import_editor5 = require("@pixerate/editor");
|
|
554
620
|
function useFileImport(editor, options = {}) {
|
|
555
|
-
const editorRef = (0,
|
|
621
|
+
const editorRef = (0, import_react9.useRef)(editor);
|
|
556
622
|
editorRef.current = editor;
|
|
557
|
-
const controller = (0,
|
|
558
|
-
() => new
|
|
623
|
+
const controller = (0, import_react9.useMemo)(
|
|
624
|
+
() => new import_editor5.FileImportController(() => {
|
|
559
625
|
const current = editorRef.current;
|
|
560
626
|
return current && !current.isDestroyed ? current : null;
|
|
561
627
|
}, options),
|
|
562
628
|
[]
|
|
563
629
|
);
|
|
564
|
-
(0,
|
|
630
|
+
(0, import_react9.useEffect)(() => {
|
|
565
631
|
controller.setOptions(options);
|
|
566
632
|
});
|
|
567
|
-
const state = (0,
|
|
633
|
+
const state = (0, import_react9.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
|
|
568
634
|
return {
|
|
569
635
|
...state,
|
|
570
636
|
accept: controller.accept,
|
|
@@ -574,27 +640,27 @@ function useFileImport(editor, options = {}) {
|
|
|
574
640
|
}
|
|
575
641
|
|
|
576
642
|
// src/hooks/useAgentToolProvider.ts
|
|
577
|
-
var
|
|
643
|
+
var import_react10 = require("react");
|
|
578
644
|
function useAgentToolProvider(registry, provider) {
|
|
579
|
-
(0,
|
|
645
|
+
(0, import_react10.useEffect)(() => {
|
|
580
646
|
if (!registry || !provider) return;
|
|
581
647
|
return registry.register(provider);
|
|
582
648
|
}, [registry, provider]);
|
|
583
649
|
}
|
|
584
650
|
|
|
585
651
|
// src/components/EditorContent.tsx
|
|
586
|
-
var
|
|
652
|
+
var import_react11 = require("@tiptap/react");
|
|
587
653
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
588
654
|
var EditorContent = ({
|
|
589
655
|
className,
|
|
590
656
|
editor,
|
|
591
657
|
...props
|
|
592
658
|
}) => {
|
|
593
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react11.EditorContent, { editor, ...props }) });
|
|
594
660
|
};
|
|
595
661
|
|
|
596
662
|
// src/components/BubbleMenu.tsx
|
|
597
|
-
var
|
|
663
|
+
var import_react12 = require("react");
|
|
598
664
|
var import_react_dom = require("react-dom");
|
|
599
665
|
var import_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
|
|
600
666
|
var BubbleMenu = ({
|
|
@@ -606,12 +672,12 @@ var BubbleMenu = ({
|
|
|
606
672
|
pluginKey = "bubbleMenu",
|
|
607
673
|
updateDelay
|
|
608
674
|
}) => {
|
|
609
|
-
const menuEl = (0,
|
|
610
|
-
const [mounted, setMounted] = (0,
|
|
675
|
+
const menuEl = (0, import_react12.useRef)(null);
|
|
676
|
+
const [mounted, setMounted] = (0, import_react12.useState)(false);
|
|
611
677
|
if (!menuEl.current && typeof document !== "undefined") {
|
|
612
678
|
menuEl.current = document.createElement("div");
|
|
613
679
|
}
|
|
614
|
-
(0,
|
|
680
|
+
(0, import_react12.useEffect)(() => {
|
|
615
681
|
if (!editor || !menuEl.current || editor.isDestroyed) {
|
|
616
682
|
return;
|
|
617
683
|
}
|
|
@@ -642,10 +708,9 @@ var BubbleMenu = ({
|
|
|
642
708
|
};
|
|
643
709
|
|
|
644
710
|
// src/components/TemplateRenderer.tsx
|
|
645
|
-
var
|
|
646
|
-
var
|
|
711
|
+
var import_react13 = require("react");
|
|
712
|
+
var import_editor6 = require("@pixerate/editor");
|
|
647
713
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
648
|
-
var CONDITIONAL_PREFIX = { if: "?", unless: "!", end: "/" };
|
|
649
714
|
var TemplateRenderer = ({
|
|
650
715
|
content,
|
|
651
716
|
templates = [],
|
|
@@ -657,12 +722,12 @@ var TemplateRenderer = ({
|
|
|
657
722
|
onTemplateClick,
|
|
658
723
|
onVariableClick
|
|
659
724
|
}) => {
|
|
660
|
-
const displayText = (0,
|
|
725
|
+
const displayText = (0, import_react13.useMemo)(() => {
|
|
661
726
|
if (!resolve || templates.length === 0) return content;
|
|
662
|
-
return (0,
|
|
727
|
+
return (0, import_editor6.resolveTemplates)(content, templates, { bindings });
|
|
663
728
|
}, [content, templates, resolve, bindings]);
|
|
664
|
-
const tokens = (0,
|
|
665
|
-
return (0,
|
|
729
|
+
const tokens = (0, import_react13.useMemo)(() => {
|
|
730
|
+
return (0, import_editor6.evaluatePromptTokens)(displayText, values);
|
|
666
731
|
}, [displayText, values]);
|
|
667
732
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, children: tokens.map((token, index) => {
|
|
668
733
|
if (token.type === "template") {
|
|
@@ -728,8 +793,8 @@ var TemplateRenderer = ({
|
|
|
728
793
|
{
|
|
729
794
|
"data-conditional": token.conditional,
|
|
730
795
|
className: "inline-flex items-center px-1 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-amber-400/40 bg-amber-400/15 text-amber-700 dark:text-amber-300",
|
|
731
|
-
title:
|
|
732
|
-
children:
|
|
796
|
+
title: (0, import_editor6.describeConditionalTag)(token),
|
|
797
|
+
children: (0, import_editor6.formatConditionalTag)(token)
|
|
733
798
|
},
|
|
734
799
|
index
|
|
735
800
|
);
|
|
@@ -750,8 +815,8 @@ var TemplateRenderer = ({
|
|
|
750
815
|
};
|
|
751
816
|
|
|
752
817
|
// src/components/VariableForm.tsx
|
|
753
|
-
var
|
|
754
|
-
var
|
|
818
|
+
var import_react14 = require("react");
|
|
819
|
+
var import_editor7 = require("@pixerate/editor");
|
|
755
820
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
756
821
|
var VariableForm = ({
|
|
757
822
|
content,
|
|
@@ -762,19 +827,19 @@ var VariableForm = ({
|
|
|
762
827
|
onChange,
|
|
763
828
|
className = "flex flex-col gap-3 text-sm"
|
|
764
829
|
}) => {
|
|
765
|
-
const idPrefix = (0,
|
|
766
|
-
const [internal, setInternal] = (0,
|
|
830
|
+
const idPrefix = (0, import_react14.useId)();
|
|
831
|
+
const [internal, setInternal] = (0, import_react14.useState)(
|
|
767
832
|
() => defaultValues ?? {}
|
|
768
833
|
);
|
|
769
834
|
const entered = values ?? internal;
|
|
770
|
-
const fields = (0,
|
|
771
|
-
() => (0,
|
|
835
|
+
const fields = (0, import_react14.useMemo)(
|
|
836
|
+
() => (0, import_editor7.getVariableFormFields)(content, { templates, bindings }),
|
|
772
837
|
[content, templates, bindings]
|
|
773
838
|
);
|
|
774
839
|
const update = (name, value) => {
|
|
775
840
|
const next = { ...entered, [name]: value };
|
|
776
841
|
if (values === void 0) setInternal(next);
|
|
777
|
-
onChange?.((0,
|
|
842
|
+
onChange?.((0, import_editor7.compactVariableValues)(next), next);
|
|
778
843
|
};
|
|
779
844
|
if (fields.length === 0) return null;
|
|
780
845
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { className, onSubmit: (event) => event.preventDefault(), children: fields.map((field) => {
|
|
@@ -834,17 +899,305 @@ var VariableForm = ({
|
|
|
834
899
|
}) });
|
|
835
900
|
};
|
|
836
901
|
|
|
902
|
+
// src/components/MergePreview.tsx
|
|
903
|
+
var import_react16 = require("react");
|
|
904
|
+
|
|
905
|
+
// src/hooks/useMergePreview.ts
|
|
906
|
+
var import_react15 = require("react");
|
|
907
|
+
var import_editor8 = require("@pixerate/editor");
|
|
908
|
+
function useMergePreview(options) {
|
|
909
|
+
const { template, templates, bindings, parseOptions } = options;
|
|
910
|
+
const [sourceText, setSourceTextState] = (0, import_react15.useState)(options.initialSource ?? "");
|
|
911
|
+
const [overrides, setOverrides] = (0, import_react15.useState)({});
|
|
912
|
+
const [index, setIndexState] = (0, import_react15.useState)(0);
|
|
913
|
+
const [result, setResult] = (0, import_react15.useState)(null);
|
|
914
|
+
const source = (0, import_react15.useMemo)(
|
|
915
|
+
() => (0, import_editor8.parseDataSource)(sourceText, parseOptions),
|
|
916
|
+
[sourceText, parseOptions]
|
|
917
|
+
);
|
|
918
|
+
const variables = (0, import_react15.useMemo)(
|
|
919
|
+
() => (0, import_editor8.extractVariables)(template, { templates, bindings }).map((v) => v.name),
|
|
920
|
+
[template, templates, bindings]
|
|
921
|
+
);
|
|
922
|
+
const mapping = (0, import_react15.useMemo)(() => {
|
|
923
|
+
const merged = {
|
|
924
|
+
...(0, import_editor8.matchFields)(variables, source.fields).mapping
|
|
925
|
+
};
|
|
926
|
+
for (const [variable, field] of Object.entries(overrides)) {
|
|
927
|
+
if (variables.includes(variable) && (field === null || source.fields.includes(field))) {
|
|
928
|
+
merged[variable] = field;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return merged;
|
|
932
|
+
}, [variables, source.fields, overrides]);
|
|
933
|
+
const count = source.records.length;
|
|
934
|
+
const current = count === 0 ? 0 : Math.min(index, count - 1);
|
|
935
|
+
const record = source.records[current];
|
|
936
|
+
const values = (0, import_react15.useMemo)(
|
|
937
|
+
() => record ? (0, import_editor8.recordToValues)(record, mapping) : {},
|
|
938
|
+
[record, mapping]
|
|
939
|
+
);
|
|
940
|
+
const setSourceText = (0, import_react15.useCallback)((text) => {
|
|
941
|
+
setSourceTextState(text);
|
|
942
|
+
setIndexState(0);
|
|
943
|
+
setResult(null);
|
|
944
|
+
}, []);
|
|
945
|
+
const loadFile = (0, import_react15.useCallback)(
|
|
946
|
+
async (file) => setSourceText(await file.text()),
|
|
947
|
+
[setSourceText]
|
|
948
|
+
);
|
|
949
|
+
const setIndex = (0, import_react15.useCallback)(
|
|
950
|
+
(next) => setIndexState(Math.max(0, Math.min(next, count - 1))),
|
|
951
|
+
[count]
|
|
952
|
+
);
|
|
953
|
+
const setFieldMapping = (0, import_react15.useCallback)((variable, field) => {
|
|
954
|
+
setOverrides((previous) => ({ ...previous, [variable]: field }));
|
|
955
|
+
setResult(null);
|
|
956
|
+
}, []);
|
|
957
|
+
const resetMapping = (0, import_react15.useCallback)(() => setOverrides({}), []);
|
|
958
|
+
const merge = (0, import_react15.useCallback)(() => {
|
|
959
|
+
const merged = (0, import_editor8.mergeRecords)(template, source, {
|
|
960
|
+
templates,
|
|
961
|
+
bindings,
|
|
962
|
+
mapping,
|
|
963
|
+
skipIf: options.skipIf,
|
|
964
|
+
separator: options.separator,
|
|
965
|
+
maxOutputs: options.maxOutputs
|
|
966
|
+
});
|
|
967
|
+
setResult(merged);
|
|
968
|
+
return merged;
|
|
969
|
+
}, [template, source, templates, bindings, mapping, options.skipIf, options.separator, options.maxOutputs]);
|
|
970
|
+
const serialize = (0, import_react15.useCallback)(
|
|
971
|
+
(format) => result ? (0, import_editor8.serializeMergeOutputs)(result.outputs, format, options.separator) : "",
|
|
972
|
+
[result, options.separator]
|
|
973
|
+
);
|
|
974
|
+
return {
|
|
975
|
+
sourceText,
|
|
976
|
+
setSourceText,
|
|
977
|
+
loadFile,
|
|
978
|
+
source,
|
|
979
|
+
variables,
|
|
980
|
+
mapping,
|
|
981
|
+
setFieldMapping,
|
|
982
|
+
resetMapping,
|
|
983
|
+
index: current,
|
|
984
|
+
count,
|
|
985
|
+
setIndex,
|
|
986
|
+
next: () => setIndex(current + 1),
|
|
987
|
+
prev: () => setIndex(current - 1),
|
|
988
|
+
record,
|
|
989
|
+
values,
|
|
990
|
+
merge,
|
|
991
|
+
result,
|
|
992
|
+
serialize
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// src/components/MergePreview.tsx
|
|
997
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
998
|
+
function downloadText(text, fileName, type) {
|
|
999
|
+
if (typeof document === "undefined" || typeof URL.createObjectURL !== "function") {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
const url = URL.createObjectURL(new Blob([text], { type }));
|
|
1003
|
+
const link = document.createElement("a");
|
|
1004
|
+
link.href = url;
|
|
1005
|
+
link.download = fileName;
|
|
1006
|
+
link.click();
|
|
1007
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
1008
|
+
}
|
|
1009
|
+
var MergePreview = ({
|
|
1010
|
+
content,
|
|
1011
|
+
templates,
|
|
1012
|
+
bindings,
|
|
1013
|
+
templateColorMap,
|
|
1014
|
+
defaultSource,
|
|
1015
|
+
parseOptions,
|
|
1016
|
+
skipIf,
|
|
1017
|
+
separator,
|
|
1018
|
+
maxOutputs,
|
|
1019
|
+
onMerge,
|
|
1020
|
+
downloadName = "merged",
|
|
1021
|
+
className = "flex flex-col gap-3 text-sm"
|
|
1022
|
+
}) => {
|
|
1023
|
+
const id = (0, import_react16.useId)();
|
|
1024
|
+
const merge = useMergePreview({
|
|
1025
|
+
template: content,
|
|
1026
|
+
templates,
|
|
1027
|
+
bindings,
|
|
1028
|
+
initialSource: defaultSource,
|
|
1029
|
+
parseOptions,
|
|
1030
|
+
skipIf,
|
|
1031
|
+
separator,
|
|
1032
|
+
maxOutputs
|
|
1033
|
+
});
|
|
1034
|
+
const [copied, setCopied] = (0, import_react16.useState)(false);
|
|
1035
|
+
const { source, result } = merge;
|
|
1036
|
+
const copy = async () => {
|
|
1037
|
+
try {
|
|
1038
|
+
await navigator.clipboard.writeText(merge.serialize("text"));
|
|
1039
|
+
setCopied(true);
|
|
1040
|
+
} catch {
|
|
1041
|
+
setCopied(false);
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, "data-merge-preview": "", children: [
|
|
1045
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { htmlFor: `${id}-source`, className: "flex flex-col gap-1", children: [
|
|
1046
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "font-medium", children: "Data source (CSV, TSV or JSON)" }),
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1048
|
+
"textarea",
|
|
1049
|
+
{
|
|
1050
|
+
id: `${id}-source`,
|
|
1051
|
+
"data-merge-source": "",
|
|
1052
|
+
value: merge.sourceText,
|
|
1053
|
+
rows: 5,
|
|
1054
|
+
spellCheck: false,
|
|
1055
|
+
onChange: (event) => merge.setSourceText(event.target.value),
|
|
1056
|
+
className: "px-2 py-1 font-mono text-xs rounded-md border border-violet-400/40 bg-transparent"
|
|
1057
|
+
}
|
|
1058
|
+
)
|
|
1059
|
+
] }),
|
|
1060
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1061
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1062
|
+
"input",
|
|
1063
|
+
{
|
|
1064
|
+
type: "file",
|
|
1065
|
+
accept: ".csv,.tsv,.txt,.json,text/csv,application/json",
|
|
1066
|
+
"aria-label": "Load data source file",
|
|
1067
|
+
"data-merge-file": "",
|
|
1068
|
+
onChange: (event) => {
|
|
1069
|
+
const file = event.target.files?.[0];
|
|
1070
|
+
if (file) void merge.loadFile(file);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
),
|
|
1074
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-merge-summary": "", className: "opacity-70", children: `${source.records.length} record${source.records.length === 1 ? "" : "s"} \xB7 ${source.fields.length} field${source.fields.length === 1 ? "" : "s"}` })
|
|
1075
|
+
] }),
|
|
1076
|
+
source.warnings.length > 0 && merge.sourceText.trim() !== "" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { "data-merge-warnings": "", className: "text-amber-600 text-xs", children: source.warnings.map((warning, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: warning.message }, i)) }),
|
|
1077
|
+
merge.variables.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("fieldset", { className: "flex flex-col gap-1", children: [
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("legend", { className: "font-medium", children: "Field mapping" }),
|
|
1079
|
+
merge.variables.map((variable) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1080
|
+
"label",
|
|
1081
|
+
{
|
|
1082
|
+
className: "flex items-center gap-2",
|
|
1083
|
+
"data-merge-mapping": variable,
|
|
1084
|
+
children: [
|
|
1085
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-24 font-mono", children: `{${variable}}` }),
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1087
|
+
"select",
|
|
1088
|
+
{
|
|
1089
|
+
value: merge.mapping[variable] ?? "",
|
|
1090
|
+
"aria-label": `Field for ${variable}`,
|
|
1091
|
+
onChange: (event) => merge.setFieldMapping(variable, event.target.value || null),
|
|
1092
|
+
className: "px-1 py-0.5 rounded-md border border-violet-400/40 bg-transparent",
|
|
1093
|
+
children: [
|
|
1094
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "(none)" }),
|
|
1095
|
+
source.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: field, children: field }, field))
|
|
1096
|
+
]
|
|
1097
|
+
}
|
|
1098
|
+
)
|
|
1099
|
+
]
|
|
1100
|
+
},
|
|
1101
|
+
variable
|
|
1102
|
+
))
|
|
1103
|
+
] }),
|
|
1104
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1105
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1106
|
+
"button",
|
|
1107
|
+
{
|
|
1108
|
+
type: "button",
|
|
1109
|
+
"data-merge-prev": "",
|
|
1110
|
+
onClick: merge.prev,
|
|
1111
|
+
disabled: merge.index <= 0,
|
|
1112
|
+
children: "Prev"
|
|
1113
|
+
}
|
|
1114
|
+
),
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-merge-position": "", children: merge.count === 0 ? "No records" : `Record ${merge.index + 1} of ${merge.count}` }),
|
|
1116
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1117
|
+
"button",
|
|
1118
|
+
{
|
|
1119
|
+
type: "button",
|
|
1120
|
+
"data-merge-next": "",
|
|
1121
|
+
onClick: merge.next,
|
|
1122
|
+
disabled: merge.index >= merge.count - 1,
|
|
1123
|
+
children: "Next"
|
|
1124
|
+
}
|
|
1125
|
+
)
|
|
1126
|
+
] }),
|
|
1127
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-merge-output": "", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1128
|
+
TemplateRenderer,
|
|
1129
|
+
{
|
|
1130
|
+
content,
|
|
1131
|
+
templates,
|
|
1132
|
+
bindings,
|
|
1133
|
+
templateColorMap,
|
|
1134
|
+
resolve: !!templates && templates.length > 0,
|
|
1135
|
+
values: merge.values
|
|
1136
|
+
}
|
|
1137
|
+
) }),
|
|
1138
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1139
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1140
|
+
"button",
|
|
1141
|
+
{
|
|
1142
|
+
type: "button",
|
|
1143
|
+
"data-merge-all": "",
|
|
1144
|
+
disabled: merge.count === 0,
|
|
1145
|
+
onClick: () => {
|
|
1146
|
+
const merged = merge.merge();
|
|
1147
|
+
setCopied(false);
|
|
1148
|
+
onMerge?.(merged);
|
|
1149
|
+
},
|
|
1150
|
+
children: "Merge all"
|
|
1151
|
+
}
|
|
1152
|
+
),
|
|
1153
|
+
result && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1154
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-merge-result": "", children: `${result.outputs.length} merged${result.skipped.length ? `, ${result.skipped.length} skipped` : ""}${result.truncated ? " (stopped early)" : ""}` }),
|
|
1155
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "data-merge-copy": "", onClick: copy, children: copied ? "Copied" : "Copy" }),
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1157
|
+
"button",
|
|
1158
|
+
{
|
|
1159
|
+
type: "button",
|
|
1160
|
+
"data-merge-download": "text",
|
|
1161
|
+
onClick: () => downloadText(merge.serialize("text"), `${downloadName}.txt`, "text/plain"),
|
|
1162
|
+
children: "Download .txt"
|
|
1163
|
+
}
|
|
1164
|
+
),
|
|
1165
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1166
|
+
"button",
|
|
1167
|
+
{
|
|
1168
|
+
type: "button",
|
|
1169
|
+
"data-merge-download": "json",
|
|
1170
|
+
onClick: () => downloadText(merge.serialize("json"), `${downloadName}.json`, "application/json"),
|
|
1171
|
+
children: "Download .json"
|
|
1172
|
+
}
|
|
1173
|
+
)
|
|
1174
|
+
] })
|
|
1175
|
+
] }),
|
|
1176
|
+
result && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1177
|
+
"textarea",
|
|
1178
|
+
{
|
|
1179
|
+
readOnly: true,
|
|
1180
|
+
"aria-label": "Merged output",
|
|
1181
|
+
"data-merge-text": "",
|
|
1182
|
+
rows: 6,
|
|
1183
|
+
value: merge.serialize("text"),
|
|
1184
|
+
className: "px-2 py-1 font-mono text-xs rounded-md border border-violet-400/40 bg-transparent"
|
|
1185
|
+
}
|
|
1186
|
+
)
|
|
1187
|
+
] });
|
|
1188
|
+
};
|
|
1189
|
+
|
|
837
1190
|
// src/hooks/useOutline.ts
|
|
838
|
-
var
|
|
839
|
-
var
|
|
1191
|
+
var import_react17 = require("react");
|
|
1192
|
+
var import_editor9 = require("@pixerate/editor");
|
|
840
1193
|
function useOutline(editor) {
|
|
841
|
-
const store = (0,
|
|
842
|
-
return (0,
|
|
1194
|
+
const store = (0, import_react17.useMemo)(() => (0, import_editor9.createOutlineStore)(editor), [editor]);
|
|
1195
|
+
return (0, import_react17.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
843
1196
|
}
|
|
844
1197
|
|
|
845
1198
|
// src/components/OutlinePanel.tsx
|
|
846
|
-
var
|
|
847
|
-
var
|
|
1199
|
+
var import_editor10 = require("@pixerate/editor");
|
|
1200
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
848
1201
|
var OutlinePanel = ({
|
|
849
1202
|
editor,
|
|
850
1203
|
title = "Outline",
|
|
@@ -858,11 +1211,11 @@ var OutlinePanel = ({
|
|
|
858
1211
|
}) => {
|
|
859
1212
|
const { headings, activeId } = useOutline(editor);
|
|
860
1213
|
const visible = headings.filter((h) => h.level <= maxLevel);
|
|
861
|
-
return /* @__PURE__ */ (0,
|
|
862
|
-
title != null && /* @__PURE__ */ (0,
|
|
863
|
-
visible.length === 0 ? /* @__PURE__ */ (0,
|
|
1214
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("nav", { className, "aria-label": "Document outline", "data-outline-panel": "", children: [
|
|
1215
|
+
title != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pixerate-outline-title", children: title }),
|
|
1216
|
+
visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pixerate-outline-empty", children: emptyState }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { role: "list", style: { listStyle: "none", margin: 0, padding: 0 }, children: visible.map((heading) => {
|
|
864
1217
|
const active = heading.id === activeId;
|
|
865
|
-
return /* @__PURE__ */ (0,
|
|
1218
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { "data-level": heading.level, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
866
1219
|
"button",
|
|
867
1220
|
{
|
|
868
1221
|
type: "button",
|
|
@@ -872,7 +1225,7 @@ var OutlinePanel = ({
|
|
|
872
1225
|
"data-heading-id": heading.id,
|
|
873
1226
|
onClick: () => {
|
|
874
1227
|
if (!editor) return;
|
|
875
|
-
if ((0,
|
|
1228
|
+
if ((0, import_editor10.navigateToHeading)(editor, heading, { focus: focusOnNavigate })) {
|
|
876
1229
|
onNavigate?.(heading);
|
|
877
1230
|
}
|
|
878
1231
|
},
|
|
@@ -884,16 +1237,16 @@ var OutlinePanel = ({
|
|
|
884
1237
|
};
|
|
885
1238
|
|
|
886
1239
|
// src/index.ts
|
|
887
|
-
var
|
|
1240
|
+
var import_editor16 = require("@pixerate/editor");
|
|
888
1241
|
|
|
889
1242
|
// src/spreadsheet/useSpreadsheetEditor.ts
|
|
890
|
-
var
|
|
1243
|
+
var import_react18 = require("react");
|
|
891
1244
|
var import_spreadsheet = require("@pixerate/editor/spreadsheet");
|
|
892
1245
|
function useSpreadsheetEditor(options = {}) {
|
|
893
|
-
const optionsRef = (0,
|
|
1246
|
+
const optionsRef = (0, import_react18.useRef)(options);
|
|
894
1247
|
optionsRef.current = options;
|
|
895
|
-
const settersRef = (0,
|
|
896
|
-
const [controller] = (0,
|
|
1248
|
+
const settersRef = (0, import_react18.useRef)({});
|
|
1249
|
+
const [controller] = (0, import_react18.useState)(
|
|
897
1250
|
() => new import_spreadsheet.SpreadsheetController({
|
|
898
1251
|
...options,
|
|
899
1252
|
onDocumentChange: (updated) => {
|
|
@@ -910,97 +1263,97 @@ function useSpreadsheetEditor(options = {}) {
|
|
|
910
1263
|
}
|
|
911
1264
|
})
|
|
912
1265
|
);
|
|
913
|
-
const [doc, setDoc] = (0,
|
|
914
|
-
const [activeCell, setActiveCell] = (0,
|
|
915
|
-
const [selectedRange, setSelectedRange] = (0,
|
|
916
|
-
const [editingCell, setEditingCell] = (0,
|
|
917
|
-
const [draftValue, setDraftState] = (0,
|
|
1266
|
+
const [doc, setDoc] = (0, import_react18.useState)(() => ({ ...controller.document }));
|
|
1267
|
+
const [activeCell, setActiveCell] = (0, import_react18.useState)(controller.activeCell);
|
|
1268
|
+
const [selectedRange, setSelectedRange] = (0, import_react18.useState)(controller.selectedRange);
|
|
1269
|
+
const [editingCell, setEditingCell] = (0, import_react18.useState)(null);
|
|
1270
|
+
const [draftValue, setDraftState] = (0, import_react18.useState)("");
|
|
918
1271
|
settersRef.current = { setDoc, setActiveCell, setSelectedRange };
|
|
919
|
-
const setDraftValue = (0,
|
|
1272
|
+
const setDraftValue = (0, import_react18.useCallback)((value) => {
|
|
920
1273
|
controller.draftValue = value;
|
|
921
1274
|
setDraftState(value);
|
|
922
1275
|
}, [controller]);
|
|
923
|
-
const setCellValue = (0,
|
|
1276
|
+
const setCellValue = (0, import_react18.useCallback)((rowId, colId, raw) => {
|
|
924
1277
|
controller.setCellValue(rowId, colId, raw);
|
|
925
1278
|
setDoc({ ...controller.document });
|
|
926
1279
|
}, [controller]);
|
|
927
|
-
const selectCell = (0,
|
|
1280
|
+
const selectCell = (0, import_react18.useCallback)((row, col, extendSelection = false) => {
|
|
928
1281
|
controller.selectCell(row, col, extendSelection);
|
|
929
1282
|
setActiveCell(controller.activeCell);
|
|
930
1283
|
setSelectedRange(controller.selectedRange);
|
|
931
1284
|
}, [controller]);
|
|
932
|
-
const selectRange = (0,
|
|
1285
|
+
const selectRange = (0, import_react18.useCallback)((range) => {
|
|
933
1286
|
controller.selectRange(range);
|
|
934
1287
|
setSelectedRange(controller.selectedRange);
|
|
935
1288
|
}, [controller]);
|
|
936
|
-
const startEditing = (0,
|
|
1289
|
+
const startEditing = (0, import_react18.useCallback)((row, col, initialVal) => {
|
|
937
1290
|
controller.startEditing(row, col, initialVal);
|
|
938
1291
|
setActiveCell(controller.activeCell);
|
|
939
1292
|
setEditingCell(controller.editingCell);
|
|
940
1293
|
setDraftValue(controller.draftValue);
|
|
941
1294
|
}, [controller]);
|
|
942
|
-
const commitEditing = (0,
|
|
1295
|
+
const commitEditing = (0, import_react18.useCallback)((val) => {
|
|
943
1296
|
controller.commitEditing(val);
|
|
944
1297
|
setEditingCell(null);
|
|
945
1298
|
setDraftValue("");
|
|
946
1299
|
setDoc({ ...controller.document });
|
|
947
1300
|
}, [controller]);
|
|
948
|
-
const cancelEditing = (0,
|
|
1301
|
+
const cancelEditing = (0, import_react18.useCallback)(() => {
|
|
949
1302
|
controller.cancelEditing();
|
|
950
1303
|
setEditingCell(null);
|
|
951
1304
|
setDraftValue("");
|
|
952
1305
|
}, [controller]);
|
|
953
|
-
const moveCursor = (0,
|
|
1306
|
+
const moveCursor = (0, import_react18.useCallback)((dir, extend = false) => {
|
|
954
1307
|
controller.moveCursor(dir, extend);
|
|
955
1308
|
setActiveCell(controller.activeCell);
|
|
956
1309
|
setSelectedRange(controller.selectedRange);
|
|
957
1310
|
}, [controller]);
|
|
958
|
-
const insertColumn = (0,
|
|
1311
|
+
const insertColumn = (0, import_react18.useCallback)((index, config) => {
|
|
959
1312
|
controller.insertColumn(index, config);
|
|
960
1313
|
setDoc({ ...controller.document });
|
|
961
1314
|
}, [controller]);
|
|
962
|
-
const deleteColumn = (0,
|
|
1315
|
+
const deleteColumn = (0, import_react18.useCallback)((colId) => {
|
|
963
1316
|
controller.deleteColumn(colId);
|
|
964
1317
|
setDoc({ ...controller.document });
|
|
965
1318
|
}, [controller]);
|
|
966
|
-
const insertRow = (0,
|
|
1319
|
+
const insertRow = (0, import_react18.useCallback)((index, config) => {
|
|
967
1320
|
controller.insertRow(index, config);
|
|
968
1321
|
setDoc({ ...controller.document });
|
|
969
1322
|
}, [controller]);
|
|
970
|
-
const deleteRow = (0,
|
|
1323
|
+
const deleteRow = (0, import_react18.useCallback)((rowId) => {
|
|
971
1324
|
controller.deleteRow(rowId);
|
|
972
1325
|
setDoc({ ...controller.document });
|
|
973
1326
|
}, [controller]);
|
|
974
|
-
const setColumnWidth = (0,
|
|
1327
|
+
const setColumnWidth = (0, import_react18.useCallback)((colId, width) => {
|
|
975
1328
|
controller.setColumnWidth(colId, width);
|
|
976
1329
|
setDoc({ ...controller.document });
|
|
977
1330
|
}, [controller]);
|
|
978
|
-
const autoFitColumnWidth = (0,
|
|
1331
|
+
const autoFitColumnWidth = (0, import_react18.useCallback)((colId, customWidth) => {
|
|
979
1332
|
controller.autoFitColumnWidth(colId, customWidth);
|
|
980
1333
|
setDoc({ ...controller.document });
|
|
981
1334
|
}, [controller]);
|
|
982
|
-
const registerDataSource = (0,
|
|
1335
|
+
const registerDataSource = (0, import_react18.useCallback)((ds) => {
|
|
983
1336
|
controller.registerDataSource(ds);
|
|
984
1337
|
setDoc({ ...controller.document });
|
|
985
1338
|
}, [controller]);
|
|
986
|
-
const setPrimaryDataSource = (0,
|
|
1339
|
+
const setPrimaryDataSource = (0, import_react18.useCallback)((id) => {
|
|
987
1340
|
controller.setPrimaryDataSource(id);
|
|
988
1341
|
setDoc({ ...controller.document });
|
|
989
1342
|
}, [controller]);
|
|
990
|
-
const recalculateAll = (0,
|
|
1343
|
+
const recalculateAll = (0, import_react18.useCallback)(() => {
|
|
991
1344
|
controller.recalculateAll();
|
|
992
1345
|
setDoc({ ...controller.document });
|
|
993
1346
|
}, [controller]);
|
|
994
|
-
const undo = (0,
|
|
1347
|
+
const undo = (0, import_react18.useCallback)(() => {
|
|
995
1348
|
controller.undo();
|
|
996
1349
|
setDoc({ ...controller.document });
|
|
997
1350
|
}, [controller]);
|
|
998
|
-
const redo = (0,
|
|
1351
|
+
const redo = (0, import_react18.useCallback)(() => {
|
|
999
1352
|
controller.redo();
|
|
1000
1353
|
setDoc({ ...controller.document });
|
|
1001
1354
|
}, [controller]);
|
|
1002
|
-
const tableActions = (0,
|
|
1003
|
-
const viewModel = (0,
|
|
1355
|
+
const tableActions = (0, import_react18.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
|
|
1356
|
+
const viewModel = (0, import_react18.useMemo)(() => controller.getViewModel(), [controller, doc]);
|
|
1004
1357
|
return {
|
|
1005
1358
|
document: doc,
|
|
1006
1359
|
activeCell,
|
|
@@ -1052,20 +1405,20 @@ function useSpreadsheetEditor(options = {}) {
|
|
|
1052
1405
|
}
|
|
1053
1406
|
|
|
1054
1407
|
// src/spreadsheet/SpreadsheetEditor.tsx
|
|
1055
|
-
var
|
|
1408
|
+
var import_react22 = require("react");
|
|
1056
1409
|
var import_spreadsheet5 = require("@pixerate/editor/spreadsheet");
|
|
1057
1410
|
|
|
1058
1411
|
// src/spreadsheet/SpreadsheetContextMenu.tsx
|
|
1059
|
-
var
|
|
1412
|
+
var import_react19 = __toESM(require("react"), 1);
|
|
1060
1413
|
var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
|
|
1061
|
-
var
|
|
1414
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1062
1415
|
var VIEWPORT_MARGIN = 4;
|
|
1063
1416
|
var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
|
|
1064
|
-
const menuRef = (0,
|
|
1065
|
-
const onCloseRef = (0,
|
|
1417
|
+
const menuRef = (0, import_react19.useRef)(null);
|
|
1418
|
+
const onCloseRef = (0, import_react19.useRef)(onClose);
|
|
1066
1419
|
onCloseRef.current = onClose;
|
|
1067
|
-
const [position, setPosition] = (0,
|
|
1068
|
-
(0,
|
|
1420
|
+
const [position, setPosition] = (0, import_react19.useState)({ x, y });
|
|
1421
|
+
(0, import_react19.useLayoutEffect)(() => {
|
|
1069
1422
|
const menu = menuRef.current;
|
|
1070
1423
|
if (!menu || typeof window === "undefined") return;
|
|
1071
1424
|
const { width, height } = menu.getBoundingClientRect();
|
|
@@ -1077,7 +1430,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
|
|
|
1077
1430
|
);
|
|
1078
1431
|
setPosition((prev) => prev.x === next.x && prev.y === next.y ? prev : next);
|
|
1079
1432
|
}, [x, y, items]);
|
|
1080
|
-
(0,
|
|
1433
|
+
(0, import_react19.useEffect)(() => {
|
|
1081
1434
|
const handlePointer = (e) => {
|
|
1082
1435
|
if (!menuRef.current?.contains(e.target)) onCloseRef.current();
|
|
1083
1436
|
};
|
|
@@ -1091,7 +1444,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
|
|
|
1091
1444
|
window.removeEventListener("keydown", handleKey);
|
|
1092
1445
|
};
|
|
1093
1446
|
}, []);
|
|
1094
|
-
return /* @__PURE__ */ (0,
|
|
1447
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1095
1448
|
"div",
|
|
1096
1449
|
{
|
|
1097
1450
|
ref: menuRef,
|
|
@@ -1107,19 +1460,20 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
|
|
|
1107
1460
|
},
|
|
1108
1461
|
onContextMenu: (e) => e.preventDefault(),
|
|
1109
1462
|
onKeyDown: (e) => e.stopPropagation(),
|
|
1110
|
-
children: items.map((item) => /* @__PURE__ */ (0,
|
|
1111
|
-
item.separatorBefore && /* @__PURE__ */ (0,
|
|
1112
|
-
/* @__PURE__ */ (0,
|
|
1463
|
+
children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react19.default.Fragment, { children: [
|
|
1464
|
+
item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { role: "separator", className: "my-1 h-px bg-border" }),
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1113
1466
|
"button",
|
|
1114
1467
|
{
|
|
1115
1468
|
type: "button",
|
|
1116
1469
|
role: "menuitem",
|
|
1117
1470
|
"data-action": item.id,
|
|
1471
|
+
"data-checked": item.checked ? "" : void 0,
|
|
1118
1472
|
disabled: item.disabled,
|
|
1119
|
-
className:
|
|
1473
|
+
className: `block w-full px-3 py-1 text-left hover:bg-muted disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent ${item.checked ? "font-semibold" : ""}`,
|
|
1120
1474
|
onClick: () => {
|
|
1121
1475
|
onClose();
|
|
1122
|
-
|
|
1476
|
+
(0, import_spreadsheet2.runSpreadsheetMenuItem)(item);
|
|
1123
1477
|
},
|
|
1124
1478
|
children: item.label
|
|
1125
1479
|
}
|
|
@@ -1130,7 +1484,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
|
|
|
1130
1484
|
};
|
|
1131
1485
|
|
|
1132
1486
|
// src/spreadsheet/SpreadsheetGroupRow.tsx
|
|
1133
|
-
var
|
|
1487
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1134
1488
|
var formatSummary = (value) => value === null ? "\u2013" : String(value);
|
|
1135
1489
|
var SpreadsheetGroupRow = ({
|
|
1136
1490
|
group,
|
|
@@ -1140,8 +1494,8 @@ var SpreadsheetGroupRow = ({
|
|
|
1140
1494
|
trailingCell
|
|
1141
1495
|
}) => {
|
|
1142
1496
|
const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
|
|
1143
|
-
return /* @__PURE__ */ (0,
|
|
1144
|
-
/* @__PURE__ */ (0,
|
|
1497
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { className: "border-b border-border bg-muted/40 font-semibold", "data-group-key": group.key, style: { height: 28 }, children: [
|
|
1498
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1145
1499
|
"button",
|
|
1146
1500
|
{
|
|
1147
1501
|
type: "button",
|
|
@@ -1157,22 +1511,22 @@ var SpreadsheetGroupRow = ({
|
|
|
1157
1511
|
) }),
|
|
1158
1512
|
columns.map(({ column }) => {
|
|
1159
1513
|
const summary = group.summaries[column.id];
|
|
1160
|
-
return /* @__PURE__ */ (0,
|
|
1514
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "border-r border-border px-2 py-1 truncate max-w-0 text-foreground", style: { maxWidth: 0, overflow: "hidden" }, children: column.id === labelColId ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "truncate", children: [
|
|
1161
1515
|
group.label,
|
|
1162
1516
|
" ",
|
|
1163
|
-
/* @__PURE__ */ (0,
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-normal text-muted-foreground", children: [
|
|
1164
1518
|
"(",
|
|
1165
1519
|
group.rowIndices.length,
|
|
1166
1520
|
")"
|
|
1167
1521
|
] })
|
|
1168
|
-
] }) : summary !== void 0 ? /* @__PURE__ */ (0,
|
|
1522
|
+
] }) : summary !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null }, column.id);
|
|
1169
1523
|
}),
|
|
1170
|
-
trailingCell && /* @__PURE__ */ (0,
|
|
1524
|
+
trailingCell && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
|
|
1171
1525
|
] });
|
|
1172
1526
|
};
|
|
1173
1527
|
|
|
1174
1528
|
// src/spreadsheet/useSpreadsheetTableInteractions.ts
|
|
1175
|
-
var
|
|
1529
|
+
var import_react20 = require("react");
|
|
1176
1530
|
var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
|
|
1177
1531
|
function gridHasFocus(grid) {
|
|
1178
1532
|
if (!grid || typeof document === "undefined") return false;
|
|
@@ -1181,12 +1535,12 @@ function gridHasFocus(grid) {
|
|
|
1181
1535
|
return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
|
|
1182
1536
|
}
|
|
1183
1537
|
function useSpreadsheetClipboard(gridRef, state, readOnly) {
|
|
1184
|
-
const stateRef = (0,
|
|
1538
|
+
const stateRef = (0, import_react20.useRef)(state);
|
|
1185
1539
|
stateRef.current = state;
|
|
1186
|
-
const readOnlyRef = (0,
|
|
1540
|
+
const readOnlyRef = (0, import_react20.useRef)(readOnly);
|
|
1187
1541
|
readOnlyRef.current = readOnly;
|
|
1188
|
-
const copiedRef = (0,
|
|
1189
|
-
(0,
|
|
1542
|
+
const copiedRef = (0, import_react20.useRef)(false);
|
|
1543
|
+
(0, import_react20.useEffect)(() => {
|
|
1190
1544
|
if (typeof document === "undefined") return;
|
|
1191
1545
|
const handleCopy = (e) => {
|
|
1192
1546
|
const s = stateRef.current;
|
|
@@ -1209,7 +1563,7 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
|
|
|
1209
1563
|
document.removeEventListener("paste", handlePaste);
|
|
1210
1564
|
};
|
|
1211
1565
|
}, [gridRef]);
|
|
1212
|
-
const copyShortcutFallback = (0,
|
|
1566
|
+
const copyShortcutFallback = (0, import_react20.useCallback)(() => {
|
|
1213
1567
|
copiedRef.current = false;
|
|
1214
1568
|
setTimeout(() => {
|
|
1215
1569
|
if (copiedRef.current) return;
|
|
@@ -1221,13 +1575,13 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
|
|
|
1221
1575
|
return { copyShortcutFallback };
|
|
1222
1576
|
}
|
|
1223
1577
|
function useFillHandle(state) {
|
|
1224
|
-
const [drag, setDrag] = (0,
|
|
1225
|
-
const dragRef = (0,
|
|
1578
|
+
const [drag, setDrag] = (0, import_react20.useState)(null);
|
|
1579
|
+
const dragRef = (0, import_react20.useRef)(drag);
|
|
1226
1580
|
dragRef.current = drag;
|
|
1227
|
-
const stateRef = (0,
|
|
1581
|
+
const stateRef = (0, import_react20.useRef)(state);
|
|
1228
1582
|
stateRef.current = state;
|
|
1229
1583
|
const dragging = drag !== null;
|
|
1230
|
-
(0,
|
|
1584
|
+
(0, import_react20.useEffect)(() => {
|
|
1231
1585
|
if (!dragging) return;
|
|
1232
1586
|
const handleMouseUp = () => {
|
|
1233
1587
|
const current = dragRef.current;
|
|
@@ -1237,13 +1591,13 @@ function useFillHandle(state) {
|
|
|
1237
1591
|
window.addEventListener("mouseup", handleMouseUp);
|
|
1238
1592
|
return () => window.removeEventListener("mouseup", handleMouseUp);
|
|
1239
1593
|
}, [dragging]);
|
|
1240
|
-
const startFill = (0,
|
|
1594
|
+
const startFill = (0, import_react20.useCallback)((e) => {
|
|
1241
1595
|
e.preventDefault();
|
|
1242
1596
|
e.stopPropagation();
|
|
1243
1597
|
const range = stateRef.current.selectedRange;
|
|
1244
1598
|
if (range) setDrag({ source: (0, import_spreadsheet3.normalizeRange)(range), target: null });
|
|
1245
1599
|
}, []);
|
|
1246
|
-
const extendFill = (0,
|
|
1600
|
+
const extendFill = (0, import_react20.useCallback)((row, col) => {
|
|
1247
1601
|
const current = dragRef.current;
|
|
1248
1602
|
if (!current) return;
|
|
1249
1603
|
setDrag({ source: current.source, target: (0, import_spreadsheet3.computeFillTarget)(current.source, { row, col }) });
|
|
@@ -1252,24 +1606,24 @@ function useFillHandle(state) {
|
|
|
1252
1606
|
}
|
|
1253
1607
|
|
|
1254
1608
|
// src/spreadsheet/FormulaHints.tsx
|
|
1255
|
-
var
|
|
1609
|
+
var import_react21 = require("react");
|
|
1256
1610
|
var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
|
|
1257
|
-
var
|
|
1611
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1258
1612
|
var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
1259
|
-
const [caret, setCaret] = (0,
|
|
1260
|
-
const [focused, setFocused] = (0,
|
|
1261
|
-
const [selected, setSelected] = (0,
|
|
1262
|
-
const [dismissed, setDismissed] = (0,
|
|
1263
|
-
const [position, setPosition] = (0,
|
|
1264
|
-
const pendingCaret = (0,
|
|
1265
|
-
const hints = (0,
|
|
1613
|
+
const [caret, setCaret] = (0, import_react21.useState)(null);
|
|
1614
|
+
const [focused, setFocused] = (0, import_react21.useState)(false);
|
|
1615
|
+
const [selected, setSelected] = (0, import_react21.useState)(0);
|
|
1616
|
+
const [dismissed, setDismissed] = (0, import_react21.useState)(false);
|
|
1617
|
+
const [position, setPosition] = (0, import_react21.useState)(null);
|
|
1618
|
+
const pendingCaret = (0, import_react21.useRef)(null);
|
|
1619
|
+
const hints = (0, import_react21.useMemo)(
|
|
1266
1620
|
() => (0, import_spreadsheet4.getFormulaHints)(value, caret ?? value.length, { limit }),
|
|
1267
1621
|
[value, caret, limit]
|
|
1268
1622
|
);
|
|
1269
1623
|
const suggestions = dismissed ? [] : hints.suggestions;
|
|
1270
1624
|
const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
|
|
1271
1625
|
const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
|
|
1272
|
-
const latest = (0,
|
|
1626
|
+
const latest = (0, import_react21.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
|
|
1273
1627
|
latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
|
|
1274
1628
|
const accept = (index) => {
|
|
1275
1629
|
const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
|
|
@@ -1279,7 +1633,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1279
1633
|
pendingCaret.current = next.caret;
|
|
1280
1634
|
commit(next.value);
|
|
1281
1635
|
};
|
|
1282
|
-
(0,
|
|
1636
|
+
(0, import_react21.useEffect)(() => {
|
|
1283
1637
|
const input = inputRef.current;
|
|
1284
1638
|
if (!input) return;
|
|
1285
1639
|
const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
|
|
@@ -1329,7 +1683,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1329
1683
|
input.removeEventListener("keydown", onKeyDown);
|
|
1330
1684
|
};
|
|
1331
1685
|
}, [inputRef]);
|
|
1332
|
-
(0,
|
|
1686
|
+
(0, import_react21.useLayoutEffect)(() => {
|
|
1333
1687
|
const input = inputRef.current;
|
|
1334
1688
|
const at = pendingCaret.current;
|
|
1335
1689
|
if (!input || at === null || input.value !== value) return;
|
|
@@ -1338,7 +1692,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1338
1692
|
setCaret(at);
|
|
1339
1693
|
setSelected(0);
|
|
1340
1694
|
}, [value, inputRef]);
|
|
1341
|
-
(0,
|
|
1695
|
+
(0, import_react21.useLayoutEffect)(() => {
|
|
1342
1696
|
const input = inputRef.current;
|
|
1343
1697
|
if (!visible || !input) return;
|
|
1344
1698
|
const rect = input.getBoundingClientRect();
|
|
@@ -1346,7 +1700,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1346
1700
|
}, [visible, value, inputRef]);
|
|
1347
1701
|
if (!visible) return null;
|
|
1348
1702
|
const { activeCall } = hints;
|
|
1349
|
-
return /* @__PURE__ */ (0,
|
|
1703
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1350
1704
|
"div",
|
|
1351
1705
|
{
|
|
1352
1706
|
"data-formula-hints": "",
|
|
@@ -1362,8 +1716,8 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1362
1716
|
},
|
|
1363
1717
|
onMouseDown: (e) => e.preventDefault(),
|
|
1364
1718
|
children: [
|
|
1365
|
-
activeCall && /* @__PURE__ */ (0,
|
|
1366
|
-
/* @__PURE__ */ (0,
|
|
1719
|
+
activeCall && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
|
|
1720
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1367
1721
|
"span",
|
|
1368
1722
|
{
|
|
1369
1723
|
"data-active": part.active ? "" : void 0,
|
|
@@ -1372,13 +1726,13 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1372
1726
|
},
|
|
1373
1727
|
i
|
|
1374
1728
|
)) }),
|
|
1375
|
-
/* @__PURE__ */ (0,
|
|
1376
|
-
/* @__PURE__ */ (0,
|
|
1729
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold", children: activeCall.argument.name }),
|
|
1377
1731
|
": ",
|
|
1378
1732
|
activeCall.argument.description
|
|
1379
1733
|
] }) : activeCall.description })
|
|
1380
1734
|
] }),
|
|
1381
|
-
suggestions.length > 0 && /* @__PURE__ */ (0,
|
|
1735
|
+
suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { role: "listbox", "aria-label": "Formula suggestions", className: "max-h-64 overflow-auto py-1 m-0 list-none p-0", children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1382
1736
|
"li",
|
|
1383
1737
|
{
|
|
1384
1738
|
role: "option",
|
|
@@ -1390,8 +1744,8 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1390
1744
|
accept(i);
|
|
1391
1745
|
},
|
|
1392
1746
|
children: [
|
|
1393
|
-
/* @__PURE__ */ (0,
|
|
1394
|
-
/* @__PURE__ */ (0,
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-muted-foreground truncate", children: s.description })
|
|
1395
1749
|
]
|
|
1396
1750
|
},
|
|
1397
1751
|
s.name
|
|
@@ -1402,7 +1756,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
|
1402
1756
|
};
|
|
1403
1757
|
|
|
1404
1758
|
// src/spreadsheet/SpreadsheetEditor.tsx
|
|
1405
|
-
var
|
|
1759
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1406
1760
|
var COLUMN_HEADER_HEIGHT = 28;
|
|
1407
1761
|
var ROW_HEADER_WIDTH = 48;
|
|
1408
1762
|
function frozenCellStyle(viewRow, viewCol) {
|
|
@@ -1429,6 +1783,19 @@ function spillBorderStyle(range, row, col) {
|
|
|
1429
1783
|
borderRight: col === range.endCol ? line : void 0
|
|
1430
1784
|
};
|
|
1431
1785
|
}
|
|
1786
|
+
function CellText({ cell }) {
|
|
1787
|
+
const shown = (0, import_spreadsheet5.formatCellForDisplay)(cell);
|
|
1788
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1789
|
+
"span",
|
|
1790
|
+
{
|
|
1791
|
+
className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
|
|
1792
|
+
title: cell.error ? cell.errorMessage ?? cell.error : void 0,
|
|
1793
|
+
"data-numeric": shown.numeric ? "" : void 0,
|
|
1794
|
+
style: shown.numeric || shown.color ? { ...shown.numeric ? { textAlign: "right" } : {}, ...shown.color ? { color: (0, import_spreadsheet5.numberFormatColorToCss)(shown.color) } : {} } : void 0,
|
|
1795
|
+
children: shown.text
|
|
1796
|
+
}
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1432
1799
|
var SpreadsheetEditor = ({
|
|
1433
1800
|
state: externalState,
|
|
1434
1801
|
readOnly = false,
|
|
@@ -1439,23 +1806,23 @@ var SpreadsheetEditor = ({
|
|
|
1439
1806
|
}) => {
|
|
1440
1807
|
const defaultState = useSpreadsheetEditor();
|
|
1441
1808
|
const state = externalState || defaultState;
|
|
1442
|
-
const gridRef = (0,
|
|
1443
|
-
const formulaBarRef = (0,
|
|
1444
|
-
const cellInputRef = (0,
|
|
1445
|
-
const [resizingColId, setResizingColId] = (0,
|
|
1446
|
-
const [hoveredColId, setHoveredColId] = (0,
|
|
1447
|
-
const [resizeStartX, setResizeStartX] = (0,
|
|
1448
|
-
const [resizeStartWidth, setResizeStartWidth] = (0,
|
|
1809
|
+
const gridRef = (0, import_react22.useRef)(null);
|
|
1810
|
+
const formulaBarRef = (0, import_react22.useRef)(null);
|
|
1811
|
+
const cellInputRef = (0, import_react22.useRef)(null);
|
|
1812
|
+
const [resizingColId, setResizingColId] = (0, import_react22.useState)(null);
|
|
1813
|
+
const [hoveredColId, setHoveredColId] = (0, import_react22.useState)(null);
|
|
1814
|
+
const [resizeStartX, setResizeStartX] = (0, import_react22.useState)(0);
|
|
1815
|
+
const [resizeStartWidth, setResizeStartWidth] = (0, import_react22.useState)(0);
|
|
1449
1816
|
const view = state.viewModel;
|
|
1450
1817
|
const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
|
|
1451
1818
|
const { fillTarget, startFill, extendFill } = useFillHandle(state);
|
|
1452
|
-
const [menu, setMenu] = (0,
|
|
1819
|
+
const [menu, setMenu] = (0, import_react22.useState)(null);
|
|
1453
1820
|
const filter = state.document.table?.filter;
|
|
1454
|
-
const filteredColIds = (0,
|
|
1821
|
+
const filteredColIds = (0, import_react22.useMemo)(
|
|
1455
1822
|
() => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
|
|
1456
1823
|
[filter]
|
|
1457
1824
|
);
|
|
1458
|
-
const handleCellContextMenu = (0,
|
|
1825
|
+
const handleCellContextMenu = (0, import_react22.useCallback)((rIdx, cIdx, e) => {
|
|
1459
1826
|
if (readOnly) return;
|
|
1460
1827
|
e.preventDefault();
|
|
1461
1828
|
if (state.editingCell) state.commitEditing();
|
|
@@ -1464,28 +1831,28 @@ var SpreadsheetEditor = ({
|
|
|
1464
1831
|
}
|
|
1465
1832
|
setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet5.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
|
|
1466
1833
|
}, [readOnly, state]);
|
|
1467
|
-
const activeRawValue = (0,
|
|
1834
|
+
const activeRawValue = (0, import_react22.useMemo)(() => {
|
|
1468
1835
|
if (!state.activeCell) return "";
|
|
1469
1836
|
const row = state.document.rows[state.activeCell.row];
|
|
1470
1837
|
const col = state.document.columns[state.activeCell.col];
|
|
1471
1838
|
if (!row || !col) return "";
|
|
1472
1839
|
return state.getCell(row.id, col.id).raw;
|
|
1473
1840
|
}, [state.activeCell, state.document]);
|
|
1474
|
-
const activeSpill = (0,
|
|
1841
|
+
const activeSpill = (0, import_react22.useMemo)(
|
|
1475
1842
|
() => state.activeCell ? state.controller.getSpillAt(state.activeCell.row, state.activeCell.col) : null,
|
|
1476
1843
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1477
1844
|
[state.controller, state.activeCell, state.document]
|
|
1478
1845
|
);
|
|
1479
1846
|
const spillFormula = activeSpill && !activeRawValue && activeSpill.key !== coordKey(state.activeCell) ? activeSpill.formula : "";
|
|
1480
|
-
const coordLabel = (0,
|
|
1847
|
+
const coordLabel = (0, import_react22.useMemo)(() => {
|
|
1481
1848
|
if (!state.activeCell) return "--";
|
|
1482
1849
|
const colName = (0, import_spreadsheet5.indexToColName)(state.activeCell.col);
|
|
1483
1850
|
return `${colName}${state.activeCell.row + 1}`;
|
|
1484
1851
|
}, [state.activeCell]);
|
|
1485
|
-
const [formulaDraft, setFormulaDraft] = (0,
|
|
1852
|
+
const [formulaDraft, setFormulaDraft] = (0, import_react22.useState)(null);
|
|
1486
1853
|
const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
|
|
1487
1854
|
const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
|
|
1488
|
-
(0,
|
|
1855
|
+
(0, import_react22.useEffect)(() => {
|
|
1489
1856
|
setFormulaDraft(null);
|
|
1490
1857
|
}, [state.activeCell?.row, state.activeCell?.col]);
|
|
1491
1858
|
const commitFormulaBar = () => {
|
|
@@ -1501,19 +1868,19 @@ var SpreadsheetEditor = ({
|
|
|
1501
1868
|
}
|
|
1502
1869
|
setFormulaDraft(null);
|
|
1503
1870
|
};
|
|
1504
|
-
const handleCellClick = (0,
|
|
1871
|
+
const handleCellClick = (0, import_react22.useCallback)((rIdx, cIdx, e) => {
|
|
1505
1872
|
if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
|
|
1506
1873
|
state.commitEditing();
|
|
1507
1874
|
}
|
|
1508
1875
|
state.selectCell(rIdx, cIdx, e.shiftKey);
|
|
1509
1876
|
}, [state]);
|
|
1510
|
-
const handleCellDblClick = (0,
|
|
1877
|
+
const handleCellDblClick = (0, import_react22.useCallback)((rIdx, cIdx) => {
|
|
1511
1878
|
if (readOnly) return;
|
|
1512
1879
|
const col = state.document.columns[cIdx];
|
|
1513
1880
|
if (col?.readOnly) return;
|
|
1514
1881
|
state.startEditing(rIdx, cIdx);
|
|
1515
1882
|
}, [readOnly, state]);
|
|
1516
|
-
const handleKeyDown = (0,
|
|
1883
|
+
const handleKeyDown = (0, import_react22.useCallback)((e) => {
|
|
1517
1884
|
if (state.editingCell) {
|
|
1518
1885
|
if (e.key === "Enter") {
|
|
1519
1886
|
e.preventDefault();
|
|
@@ -1582,11 +1949,11 @@ var SpreadsheetEditor = ({
|
|
|
1582
1949
|
}
|
|
1583
1950
|
}
|
|
1584
1951
|
}, [readOnly, state, copyShortcutFallback]);
|
|
1585
|
-
const stateRef = (0,
|
|
1952
|
+
const stateRef = (0, import_react22.useRef)(state);
|
|
1586
1953
|
stateRef.current = state;
|
|
1587
|
-
const onColumnResizeRef = (0,
|
|
1954
|
+
const onColumnResizeRef = (0, import_react22.useRef)(onColumnResize);
|
|
1588
1955
|
onColumnResizeRef.current = onColumnResize;
|
|
1589
|
-
(0,
|
|
1956
|
+
(0, import_react22.useEffect)(() => {
|
|
1590
1957
|
if (!resizingColId) return;
|
|
1591
1958
|
if (typeof document !== "undefined") {
|
|
1592
1959
|
document.body.style.cursor = "col-resize";
|
|
@@ -1637,7 +2004,7 @@ var SpreadsheetEditor = ({
|
|
|
1637
2004
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
1638
2005
|
};
|
|
1639
2006
|
}, [resizingColId, resizeStartX, resizeStartWidth]);
|
|
1640
|
-
const measureElementContentWidth = (0,
|
|
2007
|
+
const measureElementContentWidth = (0, import_react22.useCallback)((el, container) => {
|
|
1641
2008
|
if (typeof document === "undefined") return 0;
|
|
1642
2009
|
let maxW = el.scrollWidth || 0;
|
|
1643
2010
|
const firstChild = el.firstElementChild;
|
|
@@ -1685,7 +2052,7 @@ var SpreadsheetEditor = ({
|
|
|
1685
2052
|
}
|
|
1686
2053
|
return maxW;
|
|
1687
2054
|
}, []);
|
|
1688
|
-
const autoFitColumn = (0,
|
|
2055
|
+
const autoFitColumn = (0, import_react22.useCallback)((colId, colIndex) => {
|
|
1689
2056
|
const col = state.document.columns.find((c) => c.id === colId);
|
|
1690
2057
|
if (!col) return;
|
|
1691
2058
|
let calculatedWidth = 0;
|
|
@@ -1721,12 +2088,12 @@ var SpreadsheetEditor = ({
|
|
|
1721
2088
|
onColumnResize(updatedCol.id, updatedCol.width || 130);
|
|
1722
2089
|
}
|
|
1723
2090
|
}, [state, measureElementContentWidth, onColumnResize]);
|
|
1724
|
-
const handleResizeDblClick = (0,
|
|
2091
|
+
const handleResizeDblClick = (0, import_react22.useCallback)((colId, colIndex, e) => {
|
|
1725
2092
|
e.preventDefault();
|
|
1726
2093
|
e.stopPropagation();
|
|
1727
2094
|
autoFitColumn(colId, colIndex);
|
|
1728
2095
|
}, [autoFitColumn]);
|
|
1729
|
-
const handleHeaderDblClick = (0,
|
|
2096
|
+
const handleHeaderDblClick = (0, import_react22.useCallback)((colId, colIndex, e) => {
|
|
1730
2097
|
const target = e.currentTarget;
|
|
1731
2098
|
if (target) {
|
|
1732
2099
|
const rect = target.getBoundingClientRect();
|
|
@@ -1748,7 +2115,7 @@ var SpreadsheetEditor = ({
|
|
|
1748
2115
|
const isActiveCell = (rIdx, cIdx) => {
|
|
1749
2116
|
return state.activeCell?.row === rIdx && state.activeCell?.col === cIdx;
|
|
1750
2117
|
};
|
|
1751
|
-
return /* @__PURE__ */ (0,
|
|
2118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1752
2119
|
"div",
|
|
1753
2120
|
{
|
|
1754
2121
|
ref: gridRef,
|
|
@@ -1758,10 +2125,10 @@ var SpreadsheetEditor = ({
|
|
|
1758
2125
|
role: "grid",
|
|
1759
2126
|
"aria-label": "Spreadsheet",
|
|
1760
2127
|
children: [
|
|
1761
|
-
/* @__PURE__ */ (0,
|
|
1762
|
-
/* @__PURE__ */ (0,
|
|
1763
|
-
/* @__PURE__ */ (0,
|
|
1764
|
-
/* @__PURE__ */ (0,
|
|
2128
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
|
|
2129
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex h-6 min-w-[3rem] items-center justify-center rounded border border-border bg-background px-1.5 font-semibold text-foreground", children: coordLabel }),
|
|
2130
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
|
|
2131
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1765
2132
|
"input",
|
|
1766
2133
|
{
|
|
1767
2134
|
ref: formulaBarRef,
|
|
@@ -1797,7 +2164,7 @@ var SpreadsheetEditor = ({
|
|
|
1797
2164
|
}
|
|
1798
2165
|
}
|
|
1799
2166
|
),
|
|
1800
|
-
!formulaBarReadOnly && /* @__PURE__ */ (0,
|
|
2167
|
+
!formulaBarReadOnly && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1801
2168
|
FormulaHints,
|
|
1802
2169
|
{
|
|
1803
2170
|
inputRef: formulaBarRef,
|
|
@@ -1806,10 +2173,10 @@ var SpreadsheetEditor = ({
|
|
|
1806
2173
|
}
|
|
1807
2174
|
)
|
|
1808
2175
|
] }),
|
|
1809
|
-
/* @__PURE__ */ (0,
|
|
1810
|
-
/* @__PURE__ */ (0,
|
|
1811
|
-
/* @__PURE__ */ (0,
|
|
1812
|
-
view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0,
|
|
2176
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { className: "h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs", children: [
|
|
2178
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
|
|
2179
|
+
view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1813
2180
|
"th",
|
|
1814
2181
|
{
|
|
1815
2182
|
"data-col": cIdx,
|
|
@@ -1824,16 +2191,16 @@ var SpreadsheetEditor = ({
|
|
|
1824
2191
|
onClick: () => onColumnHeaderClick?.(col),
|
|
1825
2192
|
onDoubleClick: (e) => handleHeaderDblClick(col.id, cIdx, e),
|
|
1826
2193
|
children: [
|
|
1827
|
-
/* @__PURE__ */ (0,
|
|
1828
|
-
/* @__PURE__ */ (0,
|
|
1829
|
-
filteredColIds.has(col.id) && /* @__PURE__ */ (0,
|
|
1830
|
-
/* @__PURE__ */ (0,
|
|
2194
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
|
|
2195
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
|
|
2196
|
+
filteredColIds.has(col.id) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
|
|
2197
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
|
|
1831
2198
|
"(",
|
|
1832
2199
|
col.key,
|
|
1833
2200
|
")"
|
|
1834
2201
|
] })
|
|
1835
2202
|
] }),
|
|
1836
|
-
/* @__PURE__ */ (0,
|
|
2203
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1837
2204
|
"div",
|
|
1838
2205
|
{
|
|
1839
2206
|
className: "col-resize-handle",
|
|
@@ -1850,7 +2217,7 @@ var SpreadsheetEditor = ({
|
|
|
1850
2217
|
onDoubleClick: (e) => handleResizeDblClick(col.id, cIdx, e),
|
|
1851
2218
|
role: "separator",
|
|
1852
2219
|
"aria-orientation": "vertical",
|
|
1853
|
-
children: /* @__PURE__ */ (0,
|
|
2220
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1854
2221
|
"div",
|
|
1855
2222
|
{
|
|
1856
2223
|
className: "col-resize-handle-line",
|
|
@@ -1869,19 +2236,19 @@ var SpreadsheetEditor = ({
|
|
|
1869
2236
|
},
|
|
1870
2237
|
col.id
|
|
1871
2238
|
)),
|
|
1872
|
-
onAddColumnClick && /* @__PURE__ */ (0,
|
|
2239
|
+
onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1873
2240
|
"th",
|
|
1874
2241
|
{
|
|
1875
2242
|
className: "w-10 min-w-[2.5rem] px-1 text-center bg-muted/40 hover:bg-muted cursor-pointer transition-colors",
|
|
1876
2243
|
onClick: onAddColumnClick,
|
|
1877
2244
|
title: "Add Column",
|
|
1878
|
-
children: /* @__PURE__ */ (0,
|
|
2245
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
|
|
1879
2246
|
}
|
|
1880
2247
|
)
|
|
1881
2248
|
] }) }),
|
|
1882
|
-
/* @__PURE__ */ (0,
|
|
2249
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("tbody", { children: view.rows.map((viewRow) => {
|
|
1883
2250
|
if (viewRow.kind === "group") {
|
|
1884
|
-
return /* @__PURE__ */ (0,
|
|
2251
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1885
2252
|
SpreadsheetGroupRow,
|
|
1886
2253
|
{
|
|
1887
2254
|
group: viewRow.group,
|
|
@@ -1895,14 +2262,14 @@ var SpreadsheetEditor = ({
|
|
|
1895
2262
|
}
|
|
1896
2263
|
const displayRowIndex = viewRow.index;
|
|
1897
2264
|
const selection = state.selectedRange ? (0, import_spreadsheet5.normalizeRange)(state.selectedRange) : null;
|
|
1898
|
-
return /* @__PURE__ */ (0,
|
|
2265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1899
2266
|
"tr",
|
|
1900
2267
|
{
|
|
1901
2268
|
"data-row": displayRowIndex,
|
|
1902
2269
|
className: `border-b border-border hover:bg-muted/10 transition-colors ${viewRow.section !== "body" ? "bg-muted/30 font-semibold" : ""}`,
|
|
1903
2270
|
style: { height: viewRow.row.height || 32 },
|
|
1904
2271
|
children: [
|
|
1905
|
-
/* @__PURE__ */ (0,
|
|
2272
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1906
2273
|
"td",
|
|
1907
2274
|
{
|
|
1908
2275
|
className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-mono select-none",
|
|
@@ -1923,7 +2290,7 @@ var SpreadsheetEditor = ({
|
|
|
1923
2290
|
const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
|
|
1924
2291
|
const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet5.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
|
|
1925
2292
|
const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet5.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
|
|
1926
|
-
return /* @__PURE__ */ (0,
|
|
2293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1927
2294
|
"td",
|
|
1928
2295
|
{
|
|
1929
2296
|
"data-row": rIdx,
|
|
@@ -1942,8 +2309,8 @@ var SpreadsheetEditor = ({
|
|
|
1942
2309
|
onContextMenu: (e) => handleCellContextMenu(rIdx, cIdx, e),
|
|
1943
2310
|
onMouseOver: () => extendFill(displayRowIndex, viewCol.index),
|
|
1944
2311
|
children: [
|
|
1945
|
-
isEditing ? /* @__PURE__ */ (0,
|
|
1946
|
-
/* @__PURE__ */ (0,
|
|
2312
|
+
isEditing ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2313
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1947
2314
|
"input",
|
|
1948
2315
|
{
|
|
1949
2316
|
ref: cellInputRef,
|
|
@@ -1959,17 +2326,10 @@ var SpreadsheetEditor = ({
|
|
|
1959
2326
|
autoFocus: true
|
|
1960
2327
|
}
|
|
1961
2328
|
),
|
|
1962
|
-
/* @__PURE__ */ (0,
|
|
1963
|
-
] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0,
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
|
|
1967
|
-
title: cell.error ? cell.errorMessage ?? cell.error : void 0,
|
|
1968
|
-
children: cell.error || (cell.value ?? "")
|
|
1969
|
-
}
|
|
1970
|
-
),
|
|
1971
|
-
activeSpill && (0, import_spreadsheet5.rangeContains)(activeSpill.range, rIdx, cIdx) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-spill-border", style: spillBorderStyle(activeSpill.range, rIdx, cIdx) }),
|
|
1972
|
-
showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2329
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue })
|
|
2330
|
+
] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CellText, { cell }),
|
|
2331
|
+
activeSpill && (0, import_spreadsheet5.rangeContains)(activeSpill.range, rIdx, cIdx) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-spill-border", style: spillBorderStyle(activeSpill.range, rIdx, cIdx) }),
|
|
2332
|
+
showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1973
2333
|
"div",
|
|
1974
2334
|
{
|
|
1975
2335
|
className: "spreadsheet-fill-handle",
|
|
@@ -1984,14 +2344,14 @@ var SpreadsheetEditor = ({
|
|
|
1984
2344
|
viewCol.column.id
|
|
1985
2345
|
);
|
|
1986
2346
|
}),
|
|
1987
|
-
onAddColumnClick && /* @__PURE__ */ (0,
|
|
2347
|
+
onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
|
|
1988
2348
|
]
|
|
1989
2349
|
},
|
|
1990
2350
|
viewRow.row.id
|
|
1991
2351
|
);
|
|
1992
2352
|
}) })
|
|
1993
2353
|
] }) }),
|
|
1994
|
-
menu && /* @__PURE__ */ (0,
|
|
2354
|
+
menu && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1995
2355
|
SpreadsheetContextMenu,
|
|
1996
2356
|
{
|
|
1997
2357
|
items: menu.items,
|
|
@@ -2003,7 +2363,7 @@ var SpreadsheetEditor = ({
|
|
|
2003
2363
|
}
|
|
2004
2364
|
}
|
|
2005
2365
|
),
|
|
2006
|
-
/* @__PURE__ */ (0,
|
|
2366
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("style", { children: `
|
|
2007
2367
|
.col-resize-handle:hover .col-resize-handle-line {
|
|
2008
2368
|
background-color: var(--primary, #6366f1);
|
|
2009
2369
|
opacity: 0.75;
|
|
@@ -2026,10 +2386,10 @@ var SpreadsheetEditor = ({
|
|
|
2026
2386
|
};
|
|
2027
2387
|
|
|
2028
2388
|
// src/slots.tsx
|
|
2029
|
-
var
|
|
2030
|
-
var
|
|
2389
|
+
var import_react23 = require("react");
|
|
2390
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2031
2391
|
var defaultSlots = {
|
|
2032
|
-
Button: (props) => /* @__PURE__ */ (0,
|
|
2392
|
+
Button: (props) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2033
2393
|
"button",
|
|
2034
2394
|
{
|
|
2035
2395
|
type: "button",
|
|
@@ -2039,111 +2399,128 @@ var defaultSlots = {
|
|
|
2039
2399
|
children: props.children
|
|
2040
2400
|
}
|
|
2041
2401
|
),
|
|
2042
|
-
Tooltip: (props) => /* @__PURE__ */ (0,
|
|
2043
|
-
NodeHeader: (props) => /* @__PURE__ */ (0,
|
|
2044
|
-
/* @__PURE__ */ (0,
|
|
2402
|
+
Tooltip: (props) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
|
|
2403
|
+
NodeHeader: (props) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
|
|
2404
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2045
2405
|
props.icon,
|
|
2046
|
-
/* @__PURE__ */ (0,
|
|
2406
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold", children: props.title })
|
|
2047
2407
|
] }),
|
|
2048
|
-
props.onClose && /* @__PURE__ */ (0,
|
|
2408
|
+
props.onClose && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
|
|
2049
2409
|
] })
|
|
2050
2410
|
};
|
|
2051
|
-
var EditorSlotsContext = (0,
|
|
2411
|
+
var EditorSlotsContext = (0, import_react23.createContext)(defaultSlots);
|
|
2052
2412
|
function EditorSlotsProvider({ slots, children }) {
|
|
2053
|
-
const mergedSlots = (0,
|
|
2054
|
-
return /* @__PURE__ */ (0,
|
|
2413
|
+
const mergedSlots = (0, import_react23.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
|
|
2414
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(EditorSlotsContext.Provider, { value: mergedSlots, children });
|
|
2055
2415
|
}
|
|
2056
2416
|
function useEditorSlots() {
|
|
2057
|
-
return (0,
|
|
2417
|
+
return (0, import_react23.useContext)(EditorSlotsContext);
|
|
2058
2418
|
}
|
|
2059
2419
|
|
|
2060
2420
|
// src/image-editor/useImageEditor.ts
|
|
2061
|
-
var
|
|
2421
|
+
var import_react24 = require("react");
|
|
2062
2422
|
var import_image_editor = require("@pixerate/editor/image-editor");
|
|
2063
2423
|
function useImageEditor(options = {}) {
|
|
2064
|
-
const controllerRef = (0,
|
|
2424
|
+
const controllerRef = (0, import_react24.useRef)(null);
|
|
2065
2425
|
if (!controllerRef.current) {
|
|
2066
2426
|
controllerRef.current = new import_image_editor.ImageEditorController(options);
|
|
2067
2427
|
}
|
|
2068
2428
|
const controller = controllerRef.current;
|
|
2069
|
-
const [state, setState] = (0,
|
|
2070
|
-
const [historyTicks, setHistoryTicks] = (0,
|
|
2071
|
-
(0,
|
|
2429
|
+
const [state, setState] = (0, import_react24.useState)(() => controller.getState());
|
|
2430
|
+
const [historyTicks, setHistoryTicks] = (0, import_react24.useState)(0);
|
|
2431
|
+
const [histogram, setHistogram] = (0, import_react24.useState)(() => controller.getHistogram());
|
|
2432
|
+
(0, import_react24.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
|
|
2433
|
+
(0, import_react24.useEffect)(() => {
|
|
2072
2434
|
const unsub = controller.subscribe((next) => {
|
|
2073
2435
|
setState(next);
|
|
2074
2436
|
setHistoryTicks((t) => t + 1);
|
|
2075
2437
|
});
|
|
2076
2438
|
return unsub;
|
|
2077
2439
|
}, [controller]);
|
|
2078
|
-
const loadedImageRef = (0,
|
|
2079
|
-
(0,
|
|
2440
|
+
const loadedImageRef = (0, import_react24.useRef)(options.image);
|
|
2441
|
+
(0, import_react24.useEffect)(() => {
|
|
2080
2442
|
if (options.image === loadedImageRef.current) return;
|
|
2081
2443
|
loadedImageRef.current = options.image;
|
|
2082
2444
|
if (options.image) {
|
|
2083
2445
|
controller.loadImage(options.image);
|
|
2084
2446
|
}
|
|
2085
2447
|
}, [options.image, controller]);
|
|
2086
|
-
const setTool = (0,
|
|
2087
|
-
const setCrop = (0,
|
|
2448
|
+
const setTool = (0, import_react24.useCallback)((tool) => controller.setTool(tool), [controller]);
|
|
2449
|
+
const setCrop = (0, import_react24.useCallback)(
|
|
2088
2450
|
(crop, addToHistory = true) => controller.setCrop(crop, addToHistory),
|
|
2089
2451
|
[controller]
|
|
2090
2452
|
);
|
|
2091
|
-
const applyCropPreset = (0,
|
|
2092
|
-
const resetCrop = (0,
|
|
2093
|
-
const rotate = (0,
|
|
2094
|
-
const setRotation = (0,
|
|
2095
|
-
const flipHorizontal = (0,
|
|
2096
|
-
const flipVertical = (0,
|
|
2097
|
-
const setAdjustments = (0,
|
|
2453
|
+
const applyCropPreset = (0, import_react24.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
|
|
2454
|
+
const resetCrop = (0, import_react24.useCallback)(() => controller.resetCrop(), [controller]);
|
|
2455
|
+
const rotate = (0, import_react24.useCallback)((deg) => controller.rotate(deg), [controller]);
|
|
2456
|
+
const setRotation = (0, import_react24.useCallback)((deg) => controller.setRotation(deg), [controller]);
|
|
2457
|
+
const flipHorizontal = (0, import_react24.useCallback)(() => controller.flipHorizontal(), [controller]);
|
|
2458
|
+
const flipVertical = (0, import_react24.useCallback)(() => controller.flipVertical(), [controller]);
|
|
2459
|
+
const setAdjustments = (0, import_react24.useCallback)(
|
|
2098
2460
|
(partial, commitToHistory = true) => controller.setAdjustments(partial, commitToHistory),
|
|
2099
2461
|
[controller]
|
|
2100
2462
|
);
|
|
2101
|
-
const resetAdjustments = (0,
|
|
2102
|
-
const
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
|
|
2463
|
+
const resetAdjustments = (0, import_react24.useCallback)(() => controller.resetAdjustments(), [controller]);
|
|
2464
|
+
const upgradeProcess = (0, import_react24.useCallback)(() => controller.upgradeProcess(), [controller]);
|
|
2465
|
+
const addAdjustmentLayer = (0, import_react24.useCallback)(
|
|
2466
|
+
(init, index) => controller.addAdjustmentLayer(init, index),
|
|
2467
|
+
[controller]
|
|
2468
|
+
);
|
|
2469
|
+
const updateAdjustmentLayer = (0, import_react24.useCallback)(
|
|
2470
|
+
(id, update, commitToHistory = true) => controller.updateAdjustmentLayer(id, update, commitToHistory),
|
|
2471
|
+
[controller]
|
|
2472
|
+
);
|
|
2473
|
+
const removeAdjustmentLayer = (0, import_react24.useCallback)((id) => controller.removeAdjustmentLayer(id), [controller]);
|
|
2474
|
+
const moveAdjustmentLayer = (0, import_react24.useCallback)((id, to) => controller.moveAdjustmentLayer(id, to), [controller]);
|
|
2475
|
+
const toggleAdjustmentLayer = (0, import_react24.useCallback)(
|
|
2476
|
+
(id, enabled) => controller.toggleAdjustmentLayer(id, enabled),
|
|
2477
|
+
[controller]
|
|
2478
|
+
);
|
|
2479
|
+
const setDepthMask = (0, import_react24.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
|
|
2480
|
+
const resetDepthMask = (0, import_react24.useCallback)(() => controller.resetDepthMask(), [controller]);
|
|
2481
|
+
const addAnnotation = (0, import_react24.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
|
|
2482
|
+
const updateAnnotation = (0, import_react24.useCallback)(
|
|
2106
2483
|
(id, partial, commitToHistory = true) => controller.updateAnnotation(id, partial, commitToHistory),
|
|
2107
2484
|
[controller]
|
|
2108
2485
|
);
|
|
2109
|
-
const removeAnnotation = (0,
|
|
2110
|
-
const clearAnnotations = (0,
|
|
2111
|
-
const selectAnnotation = (0,
|
|
2112
|
-
const undo = (0,
|
|
2113
|
-
const redo = (0,
|
|
2114
|
-
const reset = (0,
|
|
2115
|
-
const zoomIn = (0,
|
|
2116
|
-
const zoomOut = (0,
|
|
2117
|
-
const resetZoom = (0,
|
|
2118
|
-
const setZoom = (0,
|
|
2119
|
-
const setPan = (0,
|
|
2120
|
-
const setComparing = (0,
|
|
2121
|
-
const loadImage = (0,
|
|
2122
|
-
const toDataURL = (0,
|
|
2123
|
-
const toBlob = (0,
|
|
2124
|
-
const toJSON = (0,
|
|
2125
|
-
const loadJSON = (0,
|
|
2126
|
-
const renderMask = (0,
|
|
2486
|
+
const removeAnnotation = (0, import_react24.useCallback)((id) => controller.removeAnnotation(id), [controller]);
|
|
2487
|
+
const clearAnnotations = (0, import_react24.useCallback)(() => controller.clearAnnotations(), [controller]);
|
|
2488
|
+
const selectAnnotation = (0, import_react24.useCallback)((id) => controller.selectAnnotation(id), [controller]);
|
|
2489
|
+
const undo = (0, import_react24.useCallback)(() => controller.undo(), [controller]);
|
|
2490
|
+
const redo = (0, import_react24.useCallback)(() => controller.redo(), [controller]);
|
|
2491
|
+
const reset = (0, import_react24.useCallback)(() => controller.reset(), [controller]);
|
|
2492
|
+
const zoomIn = (0, import_react24.useCallback)((factor) => controller.zoomIn(factor), [controller]);
|
|
2493
|
+
const zoomOut = (0, import_react24.useCallback)((factor) => controller.zoomOut(factor), [controller]);
|
|
2494
|
+
const resetZoom = (0, import_react24.useCallback)(() => controller.resetZoom(), [controller]);
|
|
2495
|
+
const setZoom = (0, import_react24.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
|
|
2496
|
+
const setPan = (0, import_react24.useCallback)((pan) => controller.setPan(pan), [controller]);
|
|
2497
|
+
const setComparing = (0, import_react24.useCallback)((comp) => controller.setComparing(comp), [controller]);
|
|
2498
|
+
const loadImage = (0, import_react24.useCallback)((src) => controller.loadImage(src), [controller]);
|
|
2499
|
+
const toDataURL = (0, import_react24.useCallback)((opts) => controller.toDataURL(opts), [controller]);
|
|
2500
|
+
const toBlob = (0, import_react24.useCallback)((opts) => controller.toBlob(opts), [controller]);
|
|
2501
|
+
const toJSON = (0, import_react24.useCallback)(() => controller.toJSON(), [controller]);
|
|
2502
|
+
const loadJSON = (0, import_react24.useCallback)((data) => controller.loadJSON(data), [controller]);
|
|
2503
|
+
const renderMask = (0, import_react24.useCallback)(
|
|
2127
2504
|
(opts) => controller.renderMask(opts),
|
|
2128
2505
|
[controller]
|
|
2129
2506
|
);
|
|
2130
|
-
const toMaskDataURL = (0,
|
|
2507
|
+
const toMaskDataURL = (0, import_react24.useCallback)(
|
|
2131
2508
|
(opts) => controller.toMaskDataURL(opts),
|
|
2132
2509
|
[controller]
|
|
2133
2510
|
);
|
|
2134
|
-
const toSourceDataURL = (0,
|
|
2511
|
+
const toSourceDataURL = (0, import_react24.useCallback)(
|
|
2135
2512
|
(opts) => controller.toSourceDataURL(opts),
|
|
2136
2513
|
[controller]
|
|
2137
2514
|
);
|
|
2138
|
-
const applyInpaintedImage = (0,
|
|
2515
|
+
const applyInpaintedImage = (0, import_react24.useCallback)(
|
|
2139
2516
|
(newDataUrl) => controller.applyInpaintedImage(newDataUrl),
|
|
2140
2517
|
[controller]
|
|
2141
2518
|
);
|
|
2142
|
-
const applyBackgroundRemovedImage = (0,
|
|
2519
|
+
const applyBackgroundRemovedImage = (0, import_react24.useCallback)(
|
|
2143
2520
|
(newDataUrl) => controller.applyBackgroundRemovedImage(newDataUrl),
|
|
2144
2521
|
[controller]
|
|
2145
2522
|
);
|
|
2146
|
-
return (0,
|
|
2523
|
+
return (0, import_react24.useMemo)(
|
|
2147
2524
|
() => ({
|
|
2148
2525
|
controller,
|
|
2149
2526
|
state,
|
|
@@ -2159,6 +2536,13 @@ function useImageEditor(options = {}) {
|
|
|
2159
2536
|
flipVertical,
|
|
2160
2537
|
setAdjustments,
|
|
2161
2538
|
resetAdjustments,
|
|
2539
|
+
upgradeProcess,
|
|
2540
|
+
addAdjustmentLayer,
|
|
2541
|
+
updateAdjustmentLayer,
|
|
2542
|
+
removeAdjustmentLayer,
|
|
2543
|
+
moveAdjustmentLayer,
|
|
2544
|
+
toggleAdjustmentLayer,
|
|
2545
|
+
histogram,
|
|
2162
2546
|
setDepthMask,
|
|
2163
2547
|
resetDepthMask,
|
|
2164
2548
|
addAnnotation,
|
|
@@ -2187,69 +2571,501 @@ function useImageEditor(options = {}) {
|
|
|
2187
2571
|
applyBackgroundRemovedImage
|
|
2188
2572
|
}),
|
|
2189
2573
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2190
|
-
[controller, state, historyTicks]
|
|
2574
|
+
[controller, state, historyTicks, histogram]
|
|
2191
2575
|
);
|
|
2192
2576
|
}
|
|
2193
2577
|
|
|
2194
2578
|
// src/image-editor/ImageEditor.tsx
|
|
2195
|
-
var
|
|
2579
|
+
var import_react28 = __toESM(require("react"), 1);
|
|
2580
|
+
var import_image_editor6 = require("@pixerate/editor/image-editor");
|
|
2581
|
+
|
|
2582
|
+
// src/image-editor/ControlSlider.tsx
|
|
2196
2583
|
var import_image_editor2 = require("@pixerate/editor/image-editor");
|
|
2197
|
-
var
|
|
2198
|
-
var
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
const [isRemovingBg, setIsRemovingBg] = (0, import_react22.useState)(false);
|
|
2219
|
-
const [isInpainting, setIsInpainting] = (0, import_react22.useState)(false);
|
|
2220
|
-
const [aiError, setAiError] = (0, import_react22.useState)(null);
|
|
2221
|
-
const [aiSuccess, setAiSuccess] = (0, import_react22.useState)(null);
|
|
2222
|
-
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
2223
|
-
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
2224
|
-
const handleRemoveBackground = async () => {
|
|
2225
|
-
if (!effectiveOnRemoveBg) {
|
|
2226
|
-
setAiError("No background removal hook is configured.");
|
|
2227
|
-
return;
|
|
2228
|
-
}
|
|
2229
|
-
setAiError(null);
|
|
2230
|
-
setAiSuccess(null);
|
|
2231
|
-
setIsRemovingBg(true);
|
|
2232
|
-
try {
|
|
2233
|
-
const imageDataUrl = await editor.toSourceDataURL();
|
|
2234
|
-
const result = await effectiveOnRemoveBg({ imageDataUrl });
|
|
2235
|
-
if (typeof result === "string") {
|
|
2236
|
-
await editor.applyBackgroundRemovedImage(result);
|
|
2237
|
-
setAiSuccess("Background removed successfully!");
|
|
2238
|
-
} else {
|
|
2239
|
-
setAiSuccess("Background removal complete.");
|
|
2584
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2585
|
+
var ControlSlider = ({ spec, value, onChange, disabled, label }) => {
|
|
2586
|
+
const name = label ?? spec.label;
|
|
2587
|
+
const track = (0, import_image_editor2.controlTrackBackground)(spec);
|
|
2588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { "data-control-id": spec.id, children: [
|
|
2589
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
2590
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: name }),
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-mono", children: (0, import_image_editor2.formatControlValue)(spec, value) })
|
|
2592
|
+
] }),
|
|
2593
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2594
|
+
"input",
|
|
2595
|
+
{
|
|
2596
|
+
type: "range",
|
|
2597
|
+
min: spec.min,
|
|
2598
|
+
max: spec.max,
|
|
2599
|
+
step: spec.step,
|
|
2600
|
+
"aria-label": name,
|
|
2601
|
+
disabled,
|
|
2602
|
+
value,
|
|
2603
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
2604
|
+
className: "w-full accent-indigo-500 disabled:opacity-40"
|
|
2240
2605
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2606
|
+
),
|
|
2607
|
+
track && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "h-0.5 rounded-full opacity-70", style: { background: track } })
|
|
2608
|
+
] });
|
|
2609
|
+
};
|
|
2610
|
+
|
|
2611
|
+
// src/image-editor/Histogram.tsx
|
|
2612
|
+
var import_react25 = require("react");
|
|
2613
|
+
var import_image_editor3 = require("@pixerate/editor/image-editor");
|
|
2614
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2615
|
+
var FILL = {
|
|
2616
|
+
luminance: "rgba(148, 163, 184, 0.45)",
|
|
2617
|
+
red: "rgba(239, 68, 68, 0.45)",
|
|
2618
|
+
green: "rgba(34, 197, 94, 0.45)",
|
|
2619
|
+
blue: "rgba(59, 130, 246, 0.45)"
|
|
2620
|
+
};
|
|
2621
|
+
var W = 256;
|
|
2622
|
+
var Histogram = ({ histogram, channel = "rgb", height = 64, className = "", style }) => {
|
|
2623
|
+
const channels = channel === "rgb" ? ["luminance", "red", "green", "blue"] : [channel];
|
|
2624
|
+
const paths = (0, import_react25.useMemo)(
|
|
2625
|
+
() => histogram && histogram.samples > 0 ? channels.map((c) => ({ c, d: (0, import_image_editor3.histogramPath)(histogram, c, W, height) })) : [],
|
|
2626
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2627
|
+
[histogram, channel, height]
|
|
2628
|
+
);
|
|
2629
|
+
const clipLow = histogram && histogram.samples ? Math.max(...histogram.shadows) / histogram.samples : 0;
|
|
2630
|
+
const clipHigh = histogram && histogram.samples ? Math.max(...histogram.highlights) / histogram.samples : 0;
|
|
2631
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2632
|
+
"svg",
|
|
2633
|
+
{
|
|
2634
|
+
viewBox: `0 0 ${W} ${height}`,
|
|
2635
|
+
preserveAspectRatio: "none",
|
|
2636
|
+
role: "img",
|
|
2637
|
+
"aria-label": histogram ? `Histogram of ${histogram.samples} pixels` : "Histogram (no data)",
|
|
2638
|
+
"data-testid": "image-editor-histogram",
|
|
2639
|
+
className: `w-full rounded bg-slate-950/60 ${className}`,
|
|
2640
|
+
style: { height, ...style },
|
|
2641
|
+
children: [
|
|
2642
|
+
paths.map(({ c, d }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d, fill: FILL[c], stroke: "none" }, c)),
|
|
2643
|
+
clipLow > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: 0, y: 0, width: 3, height, fill: "#3b82f6", "data-testid": "histogram-clip-shadows" }),
|
|
2644
|
+
clipHigh > 5e-3 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: W - 3, y: 0, width: 3, height, fill: "#ef4444", "data-testid": "histogram-clip-highlights" })
|
|
2645
|
+
]
|
|
2245
2646
|
}
|
|
2647
|
+
);
|
|
2648
|
+
};
|
|
2649
|
+
function useImageHistogram(controller) {
|
|
2650
|
+
const [histogram, setHistogram] = (0, import_react25.useState)(() => controller.getHistogram());
|
|
2651
|
+
(0, import_react25.useEffect)(() => controller.subscribeHistogram(setHistogram), [controller]);
|
|
2652
|
+
return histogram;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
// src/image-editor/AdjustmentLayersPanel.tsx
|
|
2656
|
+
var import_react27 = require("react");
|
|
2657
|
+
var import_image_editor5 = require("@pixerate/editor/image-editor");
|
|
2658
|
+
|
|
2659
|
+
// src/image-editor/CurvesEditor.tsx
|
|
2660
|
+
var import_react26 = require("react");
|
|
2661
|
+
var import_image_editor4 = require("@pixerate/editor/image-editor");
|
|
2662
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2663
|
+
var BOX = 256;
|
|
2664
|
+
var STROKE = { master: "#e2e8f0", red: "#ef4444", green: "#22c55e", blue: "#3b82f6" };
|
|
2665
|
+
var CurvesEditor = ({ points, onChange, histogram, channel = "master", size = 200 }) => {
|
|
2666
|
+
const svgRef = (0, import_react26.useRef)(null);
|
|
2667
|
+
const [drag, setDrag] = (0, import_react26.useState)(null);
|
|
2668
|
+
const toBox = (e) => {
|
|
2669
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
2670
|
+
if (!rect || !rect.width || !rect.height) return { x: 0, y: 0 };
|
|
2671
|
+
return { x: (e.clientX - rect.left) / rect.width * BOX, y: (e.clientY - rect.top) / rect.height * BOX };
|
|
2246
2672
|
};
|
|
2247
|
-
const
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2673
|
+
const onPointerDown = (e) => {
|
|
2674
|
+
const { x, y } = toBox(e);
|
|
2675
|
+
let index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
|
|
2676
|
+
if (index === -1) {
|
|
2677
|
+
const inserted = (0, import_image_editor4.insertCurvePoint)(points, (0, import_image_editor4.boxToCurvePoint)(x, y, BOX, BOX));
|
|
2678
|
+
onChange(inserted.points, true);
|
|
2679
|
+
index = inserted.index;
|
|
2680
|
+
} else {
|
|
2681
|
+
onChange(points, true);
|
|
2251
2682
|
}
|
|
2252
|
-
|
|
2683
|
+
setDrag(index);
|
|
2684
|
+
e.target.setPointerCapture?.(e.pointerId);
|
|
2685
|
+
};
|
|
2686
|
+
const onPointerMove = (e) => {
|
|
2687
|
+
if (drag === null) return;
|
|
2688
|
+
onChange((0, import_image_editor4.moveCurvePoint)(points, drag, (0, import_image_editor4.boxToCurvePoint)(toBox(e).x, toBox(e).y, BOX, BOX)), false);
|
|
2689
|
+
};
|
|
2690
|
+
const onDoubleClick = (e) => {
|
|
2691
|
+
const { x, y } = toBox(e);
|
|
2692
|
+
const index = (0, import_image_editor4.hitTestCurvePoint)(points, x, y, BOX, BOX, 10);
|
|
2693
|
+
if (index !== -1 && points.length > 2) onChange((0, import_image_editor4.removeCurvePoint)(points, index), true);
|
|
2694
|
+
};
|
|
2695
|
+
const histChannel = channel === "master" ? "luminance" : channel;
|
|
2696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2697
|
+
"svg",
|
|
2698
|
+
{
|
|
2699
|
+
ref: svgRef,
|
|
2700
|
+
viewBox: `0 0 ${BOX} ${BOX}`,
|
|
2701
|
+
width: size,
|
|
2702
|
+
height: size,
|
|
2703
|
+
role: "application",
|
|
2704
|
+
"aria-label": `${channel} curve`,
|
|
2705
|
+
"data-testid": "curves-editor",
|
|
2706
|
+
className: "rounded bg-slate-950/70 touch-none select-none cursor-crosshair",
|
|
2707
|
+
onPointerDown,
|
|
2708
|
+
onPointerMove,
|
|
2709
|
+
onPointerUp: () => setDrag(null),
|
|
2710
|
+
onPointerCancel: () => setDrag(null),
|
|
2711
|
+
onDoubleClick,
|
|
2712
|
+
children: [
|
|
2713
|
+
histogram && histogram.samples > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: (0, import_image_editor4.histogramPath)(histogram, histChannel, BOX, BOX), fill: "rgba(148, 163, 184, 0.25)" }),
|
|
2714
|
+
[64, 128, 192].map((v) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("g", { stroke: "rgba(148, 163, 184, 0.2)", strokeWidth: 1, children: [
|
|
2715
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("line", { x1: v, y1: 0, x2: v, y2: BOX }),
|
|
2716
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("line", { x1: 0, y1: v, x2: BOX, y2: v })
|
|
2717
|
+
] }, v)),
|
|
2718
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("line", { x1: 0, y1: BOX, x2: BOX, y2: 0, stroke: "rgba(148, 163, 184, 0.35)", strokeDasharray: "4 4" }),
|
|
2719
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: (0, import_image_editor4.curveSvgPath)(points, BOX, BOX), fill: "none", stroke: STROKE[channel], strokeWidth: 2 }),
|
|
2720
|
+
points.map((p, i) => {
|
|
2721
|
+
const b = (0, import_image_editor4.curvePointToBox)(p, BOX, BOX);
|
|
2722
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2723
|
+
"circle",
|
|
2724
|
+
{
|
|
2725
|
+
cx: b.x,
|
|
2726
|
+
cy: b.y,
|
|
2727
|
+
r: 5,
|
|
2728
|
+
fill: drag === i ? STROKE[channel] : "#0f172a",
|
|
2729
|
+
stroke: STROKE[channel],
|
|
2730
|
+
strokeWidth: 2,
|
|
2731
|
+
"aria-label": `Point ${p.input} to ${p.output}`
|
|
2732
|
+
},
|
|
2733
|
+
i
|
|
2734
|
+
);
|
|
2735
|
+
})
|
|
2736
|
+
]
|
|
2737
|
+
}
|
|
2738
|
+
);
|
|
2739
|
+
};
|
|
2740
|
+
|
|
2741
|
+
// src/image-editor/AdjustmentLayersPanel.tsx
|
|
2742
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2743
|
+
var CHANNELS = [
|
|
2744
|
+
{ id: "master", label: "RGB" },
|
|
2745
|
+
{ id: "red", label: "Red" },
|
|
2746
|
+
{ id: "green", label: "Green" },
|
|
2747
|
+
{ id: "blue", label: "Blue" }
|
|
2748
|
+
];
|
|
2749
|
+
var TONES = [
|
|
2750
|
+
{ id: "shadows", label: "Shadows" },
|
|
2751
|
+
{ id: "midtones", label: "Midtones" },
|
|
2752
|
+
{ id: "highlights", label: "Highlights" }
|
|
2753
|
+
];
|
|
2754
|
+
var selectClass = "bg-slate-800 border border-slate-700 rounded px-1.5 py-1 text-slate-200";
|
|
2755
|
+
var chipClass = (on) => `px-2 py-0.5 rounded text-[10px] ${on ? "bg-indigo-600 text-white" : "bg-slate-800 text-slate-400 hover:text-slate-200"}`;
|
|
2756
|
+
var HueRangeStrip = ({ bounds }) => {
|
|
2757
|
+
const pts = Array.from({ length: 73 }, (_, i) => `${i / 72 * 360},${20 - (0, import_image_editor5.hueRangeWeight)({ bounds }, i * 5) * 18}`).join(" ");
|
|
2758
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 360 20", preserveAspectRatio: "none", className: "w-full h-4 rounded", "data-testid": "hue-range-strip", children: [
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("linearGradient", { id: "pixerate-hue-strip", children: [0, 60, 120, 180, 240, 300, 360].map((h) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("stop", { offset: `${h / 360 * 100}%`, stopColor: `hsl(${h}, 90%, 50%)` }, h)) }) }),
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("rect", { width: 360, height: 20, fill: "url(#pixerate-hue-strip)", opacity: 0.6 }),
|
|
2761
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("polyline", { points: pts, fill: "none", stroke: "#fff", strokeWidth: 1.5 })
|
|
2762
|
+
] });
|
|
2763
|
+
};
|
|
2764
|
+
var AdjustmentLayersPanel = ({ controller, layers: layersProp, histogram }) => {
|
|
2765
|
+
const follow = layersProp === void 0;
|
|
2766
|
+
const [, setTick] = (0, import_react27.useState)(0);
|
|
2767
|
+
(0, import_react27.useEffect)(() => follow ? controller.subscribe(() => setTick((n) => n + 1)) : void 0, [controller, follow]);
|
|
2768
|
+
const layers = layersProp ?? controller.getAdjustmentLayers();
|
|
2769
|
+
const [newKind, setNewKind] = (0, import_react27.useState)("curves");
|
|
2770
|
+
const [selectedId, setSelectedId] = (0, import_react27.useState)(null);
|
|
2771
|
+
const [channel, setChannel] = (0, import_react27.useState)("master");
|
|
2772
|
+
const [range, setRange] = (0, import_react27.useState)(-1);
|
|
2773
|
+
const [tone, setTone] = (0, import_react27.useState)("midtones");
|
|
2774
|
+
const selected = layers.find((l) => l.id === selectedId) ?? null;
|
|
2775
|
+
const topFirst = [...layers].reverse();
|
|
2776
|
+
const setParam = (layer, path, value, commit = true) => controller.updateAdjustmentLayer(layer.id, { params: (0, import_image_editor5.setControlPath)(layer.params, path, value) }, commit);
|
|
2777
|
+
const slider = (layer, spec, label) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2778
|
+
ControlSlider,
|
|
2779
|
+
{
|
|
2780
|
+
spec,
|
|
2781
|
+
label,
|
|
2782
|
+
value: Number((0, import_image_editor5.getControlPath)(layer.params, spec.id) ?? spec.default),
|
|
2783
|
+
onChange: (v) => setParam(layer, spec.id, v)
|
|
2784
|
+
},
|
|
2785
|
+
spec.id
|
|
2786
|
+
);
|
|
2787
|
+
const controlsOf = (layer, filter = () => true) => import_image_editor5.ADJUSTMENT_LAYER_CONTROLS[layer.kind].filter(filter);
|
|
2788
|
+
const checkbox = (layer, path, label) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
2789
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2790
|
+
"input",
|
|
2791
|
+
{
|
|
2792
|
+
type: "checkbox",
|
|
2793
|
+
checked: Boolean((0, import_image_editor5.getControlPath)(layer.params, path)),
|
|
2794
|
+
onChange: (e) => setParam(layer, path, e.target.checked),
|
|
2795
|
+
className: "accent-indigo-500"
|
|
2796
|
+
}
|
|
2797
|
+
),
|
|
2798
|
+
label
|
|
2799
|
+
] });
|
|
2800
|
+
const editor = (layer) => {
|
|
2801
|
+
switch (layer.kind) {
|
|
2802
|
+
case "curves":
|
|
2803
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2804
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Curve channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
|
|
2805
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2806
|
+
CurvesEditor,
|
|
2807
|
+
{
|
|
2808
|
+
points: layer.params[channel],
|
|
2809
|
+
channel,
|
|
2810
|
+
histogram,
|
|
2811
|
+
onChange: (points, commit) => setParam(layer, channel, points, commit)
|
|
2812
|
+
}
|
|
2813
|
+
)
|
|
2814
|
+
] });
|
|
2815
|
+
case "levels":
|
|
2816
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2817
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Levels channel", children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(channel === c.id), onClick: () => setChannel(c.id), children: c.label }, c.id)) }),
|
|
2818
|
+
controlsOf(layer, (s) => s.id.startsWith(`${channel}.`)).map((s) => slider(layer, s))
|
|
2819
|
+
] });
|
|
2820
|
+
case "hueSaturation":
|
|
2821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2822
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Hue range", children: [
|
|
2823
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(range === -1), onClick: () => setRange(-1), children: "Master" }),
|
|
2824
|
+
import_image_editor5.HUE_RANGE_LABELS.map((name, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(range === i), onClick: () => setRange(i), disabled: layer.params.colorize, children: name }, name))
|
|
2825
|
+
] }),
|
|
2826
|
+
range >= 0 && !layer.params.colorize && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HueRangeStrip, { bounds: layer.params.ranges[range].bounds }),
|
|
2827
|
+
controlsOf(layer, (s) => range === -1 || layer.params.colorize ? !s.id.startsWith("ranges.") : s.id.startsWith(`ranges.${range}.`)).map(
|
|
2828
|
+
(s) => slider(layer, s)
|
|
2829
|
+
),
|
|
2830
|
+
range === -1 && checkbox(layer, "colorize", "Colorize")
|
|
2831
|
+
] });
|
|
2832
|
+
case "colorBalance":
|
|
2833
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2834
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex gap-1", role: "group", "aria-label": "Tone", children: TONES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: chipClass(tone === t.id), onClick: () => setTone(t.id), children: t.label }, t.id)) }),
|
|
2835
|
+
controlsOf(layer, (s) => s.id.startsWith(`${tone}.`)).map((s) => slider(layer, s)),
|
|
2836
|
+
checkbox(layer, "preserveLuminosity", "Preserve luminosity")
|
|
2837
|
+
] });
|
|
2838
|
+
case "blackWhite":
|
|
2839
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2840
|
+
controlsOf(layer).map((s) => slider(layer, s)),
|
|
2841
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
2842
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2843
|
+
"input",
|
|
2844
|
+
{
|
|
2845
|
+
type: "checkbox",
|
|
2846
|
+
checked: layer.params.tint !== null,
|
|
2847
|
+
onChange: (e) => setParam(layer, "tint", e.target.checked ? "#e1d3b3" : null),
|
|
2848
|
+
className: "accent-indigo-500"
|
|
2849
|
+
}
|
|
2850
|
+
),
|
|
2851
|
+
"Tint",
|
|
2852
|
+
layer.params.tint && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2853
|
+
"input",
|
|
2854
|
+
{
|
|
2855
|
+
type: "color",
|
|
2856
|
+
"aria-label": "Tint colour",
|
|
2857
|
+
value: layer.params.tint,
|
|
2858
|
+
onChange: (e) => setParam(layer, "tint", e.target.value),
|
|
2859
|
+
className: "h-5 w-8 bg-transparent"
|
|
2860
|
+
}
|
|
2861
|
+
)
|
|
2862
|
+
] })
|
|
2863
|
+
] });
|
|
2864
|
+
case "photoFilter":
|
|
2865
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2866
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-slate-300", children: [
|
|
2867
|
+
"Filter colour",
|
|
2868
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2869
|
+
"input",
|
|
2870
|
+
{
|
|
2871
|
+
type: "color",
|
|
2872
|
+
"aria-label": "Filter colour",
|
|
2873
|
+
value: layer.params.color,
|
|
2874
|
+
onChange: (e) => setParam(layer, "color", e.target.value),
|
|
2875
|
+
className: "h-5 w-8 bg-transparent"
|
|
2876
|
+
}
|
|
2877
|
+
)
|
|
2878
|
+
] }),
|
|
2879
|
+
controlsOf(layer).map((s) => slider(layer, s)),
|
|
2880
|
+
checkbox(layer, "preserveLuminosity", "Preserve luminosity")
|
|
2881
|
+
] });
|
|
2882
|
+
case "invert":
|
|
2883
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-slate-500 text-[11px]", children: "Invert has no settings." });
|
|
2884
|
+
default:
|
|
2885
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex flex-col gap-2", children: controlsOf(layer).map((s) => slider(layer, s)) });
|
|
2886
|
+
}
|
|
2887
|
+
};
|
|
2888
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2 text-xs", "data-testid": "adjustment-layers-panel", children: [
|
|
2889
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Adjustment Layers" }) }),
|
|
2890
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-1.5", children: [
|
|
2891
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2892
|
+
"select",
|
|
2893
|
+
{
|
|
2894
|
+
"aria-label": "New adjustment layer kind",
|
|
2895
|
+
className: `${selectClass} flex-1`,
|
|
2896
|
+
value: newKind,
|
|
2897
|
+
onChange: (e) => setNewKind(e.target.value),
|
|
2898
|
+
children: import_image_editor5.ADJUSTMENT_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: k.kind, children: k.label }, k.kind))
|
|
2899
|
+
}
|
|
2900
|
+
),
|
|
2901
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2902
|
+
"button",
|
|
2903
|
+
{
|
|
2904
|
+
type: "button",
|
|
2905
|
+
className: "px-2 py-1 rounded bg-indigo-600 text-white hover:bg-indigo-500",
|
|
2906
|
+
onClick: () => {
|
|
2907
|
+
setSelectedId(controller.addAdjustmentLayer(newKind));
|
|
2908
|
+
setChannel("master");
|
|
2909
|
+
setRange(-1);
|
|
2910
|
+
},
|
|
2911
|
+
children: "Add Layer"
|
|
2912
|
+
}
|
|
2913
|
+
)
|
|
2914
|
+
] }),
|
|
2915
|
+
topFirst.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "flex flex-col gap-1", "aria-label": "Adjustment layers", children: topFirst.map((layer) => {
|
|
2916
|
+
const index = layers.indexOf(layer);
|
|
2917
|
+
const label = layer.name || (0, import_image_editor5.adjustmentKindLabel)(layer.kind);
|
|
2918
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2919
|
+
"li",
|
|
2920
|
+
{
|
|
2921
|
+
className: `flex items-center gap-1.5 rounded px-1.5 py-1 ${layer.id === selectedId ? "bg-indigo-600/20 ring-1 ring-indigo-500/50" : "bg-slate-800/60"}`,
|
|
2922
|
+
children: [
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2924
|
+
"input",
|
|
2925
|
+
{
|
|
2926
|
+
type: "checkbox",
|
|
2927
|
+
"aria-label": `Show ${label}`,
|
|
2928
|
+
checked: layer.enabled,
|
|
2929
|
+
onChange: () => controller.toggleAdjustmentLayer(layer.id),
|
|
2930
|
+
className: "accent-indigo-500"
|
|
2931
|
+
}
|
|
2932
|
+
),
|
|
2933
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2934
|
+
"button",
|
|
2935
|
+
{
|
|
2936
|
+
type: "button",
|
|
2937
|
+
className: `flex-1 text-left truncate ${layer.enabled ? "text-slate-200" : "text-slate-500 line-through"}`,
|
|
2938
|
+
onClick: () => setSelectedId(layer.id === selectedId ? null : layer.id),
|
|
2939
|
+
"aria-pressed": layer.id === selectedId,
|
|
2940
|
+
children: label
|
|
2941
|
+
}
|
|
2942
|
+
),
|
|
2943
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2944
|
+
"button",
|
|
2945
|
+
{
|
|
2946
|
+
type: "button",
|
|
2947
|
+
"aria-label": `Move ${label} up`,
|
|
2948
|
+
disabled: index === layers.length - 1,
|
|
2949
|
+
onClick: () => controller.moveAdjustmentLayer(layer.id, index + 1),
|
|
2950
|
+
className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
|
|
2951
|
+
children: "\u2191"
|
|
2952
|
+
}
|
|
2953
|
+
),
|
|
2954
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2955
|
+
"button",
|
|
2956
|
+
{
|
|
2957
|
+
type: "button",
|
|
2958
|
+
"aria-label": `Move ${label} down`,
|
|
2959
|
+
disabled: index === 0,
|
|
2960
|
+
onClick: () => controller.moveAdjustmentLayer(layer.id, index - 1),
|
|
2961
|
+
className: "px-1 text-slate-400 hover:text-slate-200 disabled:opacity-30",
|
|
2962
|
+
children: "\u2193"
|
|
2963
|
+
}
|
|
2964
|
+
),
|
|
2965
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2966
|
+
"button",
|
|
2967
|
+
{
|
|
2968
|
+
type: "button",
|
|
2969
|
+
"aria-label": `Remove ${label}`,
|
|
2970
|
+
onClick: () => {
|
|
2971
|
+
controller.removeAdjustmentLayer(layer.id);
|
|
2972
|
+
if (selectedId === layer.id) setSelectedId(null);
|
|
2973
|
+
},
|
|
2974
|
+
className: "px-1 text-rose-400 hover:text-rose-300",
|
|
2975
|
+
children: "\u2715"
|
|
2976
|
+
}
|
|
2977
|
+
)
|
|
2978
|
+
]
|
|
2979
|
+
},
|
|
2980
|
+
layer.id
|
|
2981
|
+
);
|
|
2982
|
+
}) }),
|
|
2983
|
+
selected && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-2 rounded border border-slate-700/60 p-2", "data-testid": "adjustment-layer-editor", children: [
|
|
2984
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2985
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-slate-400", children: "Blend" }),
|
|
2986
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2987
|
+
"select",
|
|
2988
|
+
{
|
|
2989
|
+
"aria-label": "Blend mode",
|
|
2990
|
+
className: `${selectClass} flex-1`,
|
|
2991
|
+
value: selected.blend,
|
|
2992
|
+
onChange: (e) => controller.updateAdjustmentLayer(selected.id, { blend: e.target.value }),
|
|
2993
|
+
children: import_image_editor5.BLEND_MODES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: m.id, children: m.label }, m.id))
|
|
2994
|
+
}
|
|
2995
|
+
)
|
|
2996
|
+
] }),
|
|
2997
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2998
|
+
ControlSlider,
|
|
2999
|
+
{
|
|
3000
|
+
spec: import_image_editor5.ADJUSTMENT_LAYER_OPACITY_CONTROL,
|
|
3001
|
+
label: "Layer opacity",
|
|
3002
|
+
value: selected.opacity,
|
|
3003
|
+
onChange: (v) => controller.updateAdjustmentLayer(selected.id, { opacity: v })
|
|
3004
|
+
}
|
|
3005
|
+
),
|
|
3006
|
+
editor(selected)
|
|
3007
|
+
] })
|
|
3008
|
+
] });
|
|
3009
|
+
};
|
|
3010
|
+
|
|
3011
|
+
// src/image-editor/ImageEditor.tsx
|
|
3012
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3013
|
+
var ImageEditor = ({
|
|
3014
|
+
editor: externalEditor,
|
|
3015
|
+
src,
|
|
3016
|
+
className = "",
|
|
3017
|
+
style,
|
|
3018
|
+
onSave,
|
|
3019
|
+
exportOptions,
|
|
3020
|
+
onInpaint,
|
|
3021
|
+
onRemoveBackground,
|
|
3022
|
+
aiHooks
|
|
3023
|
+
}) => {
|
|
3024
|
+
const internalEditor = useImageEditor({ image: src });
|
|
3025
|
+
const editor = externalEditor || internalEditor;
|
|
3026
|
+
const { state, controller } = editor;
|
|
3027
|
+
const legacyProcess = (0, import_image_editor6.normalizeProcessVersion)(state.adjustments.process) < import_image_editor6.LATEST_PROCESS_VERSION;
|
|
3028
|
+
const canvasRef = (0, import_react28.useRef)(null);
|
|
3029
|
+
const containerRef = (0, import_react28.useRef)(null);
|
|
3030
|
+
const [activeTab, setActiveTab] = (0, import_react28.useState)("crop");
|
|
3031
|
+
const [activeCropRatio, setActiveCropRatio] = (0, import_react28.useState)(null);
|
|
3032
|
+
const [inpaintPrompt, setInpaintPrompt] = (0, import_react28.useState)("");
|
|
3033
|
+
const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react28.useState)("annotations");
|
|
3034
|
+
const [isRemovingBg, setIsRemovingBg] = (0, import_react28.useState)(false);
|
|
3035
|
+
const [isInpainting, setIsInpainting] = (0, import_react28.useState)(false);
|
|
3036
|
+
const [aiError, setAiError] = (0, import_react28.useState)(null);
|
|
3037
|
+
const [aiSuccess, setAiSuccess] = (0, import_react28.useState)(null);
|
|
3038
|
+
const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
|
|
3039
|
+
const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
|
|
3040
|
+
const handleRemoveBackground = async () => {
|
|
3041
|
+
if (!effectiveOnRemoveBg) {
|
|
3042
|
+
setAiError("No background removal hook is configured.");
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
setAiError(null);
|
|
3046
|
+
setAiSuccess(null);
|
|
3047
|
+
setIsRemovingBg(true);
|
|
3048
|
+
try {
|
|
3049
|
+
const imageDataUrl = await editor.toSourceDataURL();
|
|
3050
|
+
const result = await effectiveOnRemoveBg({ imageDataUrl });
|
|
3051
|
+
if (typeof result === "string") {
|
|
3052
|
+
await editor.applyBackgroundRemovedImage(result);
|
|
3053
|
+
setAiSuccess("Background removed successfully!");
|
|
3054
|
+
} else {
|
|
3055
|
+
setAiSuccess("Background removal complete.");
|
|
3056
|
+
}
|
|
3057
|
+
} catch (err) {
|
|
3058
|
+
setAiError(err?.message || "Failed to remove background.");
|
|
3059
|
+
} finally {
|
|
3060
|
+
setIsRemovingBg(false);
|
|
3061
|
+
}
|
|
3062
|
+
};
|
|
3063
|
+
const handleInpaint = async () => {
|
|
3064
|
+
if (!effectiveOnInpaint) {
|
|
3065
|
+
setAiError("No inpainting hook is configured.");
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
if (!inpaintPrompt.trim()) {
|
|
2253
3069
|
setAiError("Please enter an inpainting prompt.");
|
|
2254
3070
|
return;
|
|
2255
3071
|
}
|
|
@@ -2280,18 +3096,18 @@ var ImageEditor = ({
|
|
|
2280
3096
|
setIsInpainting(false);
|
|
2281
3097
|
}
|
|
2282
3098
|
};
|
|
2283
|
-
const [activeColor, setActiveColor] = (0,
|
|
2284
|
-
const [strokeWidth, setStrokeWidth] = (0,
|
|
2285
|
-
const [fontSize, setFontSize] = (0,
|
|
2286
|
-
const [textInput, setTextInput] = (0,
|
|
2287
|
-
const draftAnnotationRef = (0,
|
|
2288
|
-
const draftCropRef = (0,
|
|
2289
|
-
const cropDragInfoRef = (0,
|
|
2290
|
-
const annotationDragInfoRef = (0,
|
|
2291
|
-
const isDrawingRef = (0,
|
|
2292
|
-
const currentPathRef = (0,
|
|
2293
|
-
const dragStartPointRef = (0,
|
|
2294
|
-
const renderCanvas = (0,
|
|
3099
|
+
const [activeColor, setActiveColor] = (0, import_react28.useState)("#ef4444");
|
|
3100
|
+
const [strokeWidth, setStrokeWidth] = (0, import_react28.useState)(3);
|
|
3101
|
+
const [fontSize, setFontSize] = (0, import_react28.useState)(24);
|
|
3102
|
+
const [textInput, setTextInput] = (0, import_react28.useState)("");
|
|
3103
|
+
const draftAnnotationRef = (0, import_react28.useRef)(null);
|
|
3104
|
+
const draftCropRef = (0, import_react28.useRef)(null);
|
|
3105
|
+
const cropDragInfoRef = (0, import_react28.useRef)(null);
|
|
3106
|
+
const annotationDragInfoRef = (0, import_react28.useRef)(null);
|
|
3107
|
+
const isDrawingRef = (0, import_react28.useRef)(false);
|
|
3108
|
+
const currentPathRef = (0, import_react28.useRef)([]);
|
|
3109
|
+
const dragStartPointRef = (0, import_react28.useRef)(null);
|
|
3110
|
+
const renderCanvas = (0, import_react28.useCallback)(
|
|
2295
3111
|
async (draftAnnotation, draftCrop) => {
|
|
2296
3112
|
const canvas = canvasRef.current;
|
|
2297
3113
|
const container = containerRef.current;
|
|
@@ -2310,7 +3126,7 @@ var ImageEditor = ({
|
|
|
2310
3126
|
},
|
|
2311
3127
|
[controller, activeTab]
|
|
2312
3128
|
);
|
|
2313
|
-
(0,
|
|
3129
|
+
(0, import_react28.useEffect)(() => {
|
|
2314
3130
|
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
2315
3131
|
const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
|
|
2316
3132
|
const animId = raf(() => {
|
|
@@ -2318,12 +3134,12 @@ var ImageEditor = ({
|
|
|
2318
3134
|
});
|
|
2319
3135
|
return () => caf(animId);
|
|
2320
3136
|
}, [renderCanvas, state]);
|
|
2321
|
-
(0,
|
|
3137
|
+
(0, import_react28.useEffect)(() => {
|
|
2322
3138
|
if (activeTab === "crop" && editor.state.activeTool !== "crop") {
|
|
2323
3139
|
editor.setTool("crop");
|
|
2324
3140
|
}
|
|
2325
3141
|
}, [activeTab, editor]);
|
|
2326
|
-
(0,
|
|
3142
|
+
(0, import_react28.useEffect)(() => {
|
|
2327
3143
|
const container = containerRef.current;
|
|
2328
3144
|
if (!container) return;
|
|
2329
3145
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -2360,8 +3176,8 @@ var ImageEditor = ({
|
|
|
2360
3176
|
width: state.imageDimensions.width,
|
|
2361
3177
|
height: state.imageDimensions.height
|
|
2362
3178
|
};
|
|
2363
|
-
const handle = (0,
|
|
2364
|
-
const startImgPt = (0,
|
|
3179
|
+
const handle = (0, import_image_editor6.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
3180
|
+
const startImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
2365
3181
|
if (handle) {
|
|
2366
3182
|
cropDragInfoRef.current = {
|
|
2367
3183
|
handle,
|
|
@@ -2380,12 +3196,12 @@ var ImageEditor = ({
|
|
|
2380
3196
|
return;
|
|
2381
3197
|
}
|
|
2382
3198
|
if (activeTab === "annotate") {
|
|
2383
|
-
const imgPt = (0,
|
|
3199
|
+
const imgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
2384
3200
|
dragStartPointRef.current = imgPt;
|
|
2385
3201
|
if (state.activeTool === "select") {
|
|
2386
3202
|
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
2387
3203
|
const ann = state.annotations[i];
|
|
2388
|
-
if ((0,
|
|
3204
|
+
if ((0, import_image_editor6.hitTestAnnotation)(imgPt, ann)) {
|
|
2389
3205
|
editor.selectAnnotation(ann.id);
|
|
2390
3206
|
annotationDragInfoRef.current = {
|
|
2391
3207
|
annId: ann.id,
|
|
@@ -2422,7 +3238,7 @@ var ImageEditor = ({
|
|
|
2422
3238
|
if (activeTab === "crop") {
|
|
2423
3239
|
const drag = cropDragInfoRef.current;
|
|
2424
3240
|
if (drag) {
|
|
2425
|
-
const currentImgPt = (0,
|
|
3241
|
+
const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, true);
|
|
2426
3242
|
let newCrop;
|
|
2427
3243
|
if (drag.handle === "new") {
|
|
2428
3244
|
const minX = Math.max(0, Math.min(drag.startImgPt.x, currentImgPt.x));
|
|
@@ -2436,7 +3252,7 @@ var ImageEditor = ({
|
|
|
2436
3252
|
height: Math.round(Math.max(20, maxY - minY))
|
|
2437
3253
|
};
|
|
2438
3254
|
} else {
|
|
2439
|
-
newCrop = (0,
|
|
3255
|
+
newCrop = (0, import_image_editor6.calculateCropDrag)(
|
|
2440
3256
|
drag.startCrop,
|
|
2441
3257
|
drag.handle,
|
|
2442
3258
|
drag.startImgPt,
|
|
@@ -2456,15 +3272,15 @@ var ImageEditor = ({
|
|
|
2456
3272
|
width: state.imageDimensions.width,
|
|
2457
3273
|
height: state.imageDimensions.height
|
|
2458
3274
|
};
|
|
2459
|
-
const handle = (0,
|
|
2460
|
-
canvas.style.cursor = (0,
|
|
3275
|
+
const handle = (0, import_image_editor6.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
|
|
3276
|
+
canvas.style.cursor = (0, import_image_editor6.getCropCursor)(handle, state.transform);
|
|
2461
3277
|
}
|
|
2462
3278
|
return;
|
|
2463
3279
|
}
|
|
2464
3280
|
if (activeTab === "annotate") {
|
|
2465
3281
|
if (annotationDragInfoRef.current) {
|
|
2466
3282
|
const drag = annotationDragInfoRef.current;
|
|
2467
|
-
const currentImgPt2 = (0,
|
|
3283
|
+
const currentImgPt2 = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
2468
3284
|
const dx = currentImgPt2.x - drag.startImgPt.x;
|
|
2469
3285
|
const dy = currentImgPt2.y - drag.startImgPt.y;
|
|
2470
3286
|
editor.updateAnnotation(drag.annId, {
|
|
@@ -2476,10 +3292,10 @@ var ImageEditor = ({
|
|
|
2476
3292
|
const startPt = dragStartPointRef.current;
|
|
2477
3293
|
if (!startPt) {
|
|
2478
3294
|
if (state.activeTool === "select") {
|
|
2479
|
-
const imgPt = (0,
|
|
3295
|
+
const imgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
2480
3296
|
let hovered = false;
|
|
2481
3297
|
for (let i = state.annotations.length - 1; i >= 0; i--) {
|
|
2482
|
-
if ((0,
|
|
3298
|
+
if ((0, import_image_editor6.hitTestAnnotation)(imgPt, state.annotations[i])) {
|
|
2483
3299
|
hovered = true;
|
|
2484
3300
|
break;
|
|
2485
3301
|
}
|
|
@@ -2490,7 +3306,7 @@ var ImageEditor = ({
|
|
|
2490
3306
|
}
|
|
2491
3307
|
return;
|
|
2492
3308
|
}
|
|
2493
|
-
const currentImgPt = (0,
|
|
3309
|
+
const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
2494
3310
|
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
2495
3311
|
currentPathRef.current.push(currentImgPt);
|
|
2496
3312
|
const draft = {
|
|
@@ -2585,7 +3401,7 @@ var ImageEditor = ({
|
|
|
2585
3401
|
}
|
|
2586
3402
|
const startPt = dragStartPointRef.current;
|
|
2587
3403
|
if (startPt) {
|
|
2588
|
-
const currentImgPt = (0,
|
|
3404
|
+
const currentImgPt = (0, import_image_editor6.canvasToImagePoint)(canvasPt, state, canvas.width, canvas.height, false);
|
|
2589
3405
|
if (state.activeTool === "pen" && isDrawingRef.current) {
|
|
2590
3406
|
if (currentPathRef.current.length > 1) {
|
|
2591
3407
|
editor.addAnnotation({
|
|
@@ -2681,15 +3497,15 @@ var ImageEditor = ({
|
|
|
2681
3497
|
link.click();
|
|
2682
3498
|
}
|
|
2683
3499
|
};
|
|
2684
|
-
return /* @__PURE__ */ (0,
|
|
3500
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2685
3501
|
"div",
|
|
2686
3502
|
{
|
|
2687
3503
|
className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
|
|
2688
3504
|
style,
|
|
2689
3505
|
children: [
|
|
2690
|
-
/* @__PURE__ */ (0,
|
|
2691
|
-
/* @__PURE__ */ (0,
|
|
2692
|
-
/* @__PURE__ */ (0,
|
|
3506
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
|
|
3507
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
|
|
3508
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2693
3509
|
"button",
|
|
2694
3510
|
{
|
|
2695
3511
|
type: "button",
|
|
@@ -2701,7 +3517,7 @@ var ImageEditor = ({
|
|
|
2701
3517
|
children: "Transform & Crop"
|
|
2702
3518
|
}
|
|
2703
3519
|
),
|
|
2704
|
-
/* @__PURE__ */ (0,
|
|
3520
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2705
3521
|
"button",
|
|
2706
3522
|
{
|
|
2707
3523
|
type: "button",
|
|
@@ -2713,7 +3529,7 @@ var ImageEditor = ({
|
|
|
2713
3529
|
children: "Adjustments"
|
|
2714
3530
|
}
|
|
2715
3531
|
),
|
|
2716
|
-
/* @__PURE__ */ (0,
|
|
3532
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2717
3533
|
"button",
|
|
2718
3534
|
{
|
|
2719
3535
|
type: "button",
|
|
@@ -2725,7 +3541,7 @@ var ImageEditor = ({
|
|
|
2725
3541
|
children: "Annotations"
|
|
2726
3542
|
}
|
|
2727
3543
|
),
|
|
2728
|
-
/* @__PURE__ */ (0,
|
|
3544
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2729
3545
|
"button",
|
|
2730
3546
|
{
|
|
2731
3547
|
type: "button",
|
|
@@ -2737,7 +3553,7 @@ var ImageEditor = ({
|
|
|
2737
3553
|
children: "Depth Mask"
|
|
2738
3554
|
}
|
|
2739
3555
|
),
|
|
2740
|
-
/* @__PURE__ */ (0,
|
|
3556
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2741
3557
|
"button",
|
|
2742
3558
|
{
|
|
2743
3559
|
type: "button",
|
|
@@ -2747,14 +3563,14 @@ var ImageEditor = ({
|
|
|
2747
3563
|
},
|
|
2748
3564
|
className: `px-3 py-1.5 rounded-md font-medium transition-colors flex items-center gap-1.5 ${activeTab === "ai" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
|
|
2749
3565
|
children: [
|
|
2750
|
-
/* @__PURE__ */ (0,
|
|
3566
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
2751
3567
|
"AI Tools"
|
|
2752
3568
|
]
|
|
2753
3569
|
}
|
|
2754
3570
|
)
|
|
2755
3571
|
] }),
|
|
2756
|
-
/* @__PURE__ */ (0,
|
|
2757
|
-
/* @__PURE__ */ (0,
|
|
3572
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
3573
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2758
3574
|
"button",
|
|
2759
3575
|
{
|
|
2760
3576
|
type: "button",
|
|
@@ -2765,7 +3581,7 @@ var ImageEditor = ({
|
|
|
2765
3581
|
children: "\u21A9"
|
|
2766
3582
|
}
|
|
2767
3583
|
),
|
|
2768
|
-
/* @__PURE__ */ (0,
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2769
3585
|
"button",
|
|
2770
3586
|
{
|
|
2771
3587
|
type: "button",
|
|
@@ -2776,7 +3592,7 @@ var ImageEditor = ({
|
|
|
2776
3592
|
children: "\u21AA"
|
|
2777
3593
|
}
|
|
2778
3594
|
),
|
|
2779
|
-
/* @__PURE__ */ (0,
|
|
3595
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2780
3596
|
"button",
|
|
2781
3597
|
{
|
|
2782
3598
|
type: "button",
|
|
@@ -2785,8 +3601,8 @@ var ImageEditor = ({
|
|
|
2785
3601
|
children: "Reset"
|
|
2786
3602
|
}
|
|
2787
3603
|
),
|
|
2788
|
-
/* @__PURE__ */ (0,
|
|
2789
|
-
/* @__PURE__ */ (0,
|
|
3604
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
3605
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2790
3606
|
"button",
|
|
2791
3607
|
{
|
|
2792
3608
|
type: "button",
|
|
@@ -2796,11 +3612,11 @@ var ImageEditor = ({
|
|
|
2796
3612
|
children: "-"
|
|
2797
3613
|
}
|
|
2798
3614
|
),
|
|
2799
|
-
/* @__PURE__ */ (0,
|
|
3615
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
|
|
2800
3616
|
Math.round(state.zoom * 100),
|
|
2801
3617
|
"%"
|
|
2802
3618
|
] }),
|
|
2803
|
-
/* @__PURE__ */ (0,
|
|
3619
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2804
3620
|
"button",
|
|
2805
3621
|
{
|
|
2806
3622
|
type: "button",
|
|
@@ -2810,8 +3626,8 @@ var ImageEditor = ({
|
|
|
2810
3626
|
children: "+"
|
|
2811
3627
|
}
|
|
2812
3628
|
),
|
|
2813
|
-
/* @__PURE__ */ (0,
|
|
2814
|
-
/* @__PURE__ */ (0,
|
|
3629
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
|
|
3630
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2815
3631
|
"button",
|
|
2816
3632
|
{
|
|
2817
3633
|
type: "button",
|
|
@@ -2822,7 +3638,7 @@ var ImageEditor = ({
|
|
|
2822
3638
|
children: "Hold Compare"
|
|
2823
3639
|
}
|
|
2824
3640
|
),
|
|
2825
|
-
/* @__PURE__ */ (0,
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2826
3642
|
"button",
|
|
2827
3643
|
{
|
|
2828
3644
|
type: "button",
|
|
@@ -2833,13 +3649,13 @@ var ImageEditor = ({
|
|
|
2833
3649
|
)
|
|
2834
3650
|
] })
|
|
2835
3651
|
] }),
|
|
2836
|
-
/* @__PURE__ */ (0,
|
|
2837
|
-
/* @__PURE__ */ (0,
|
|
3652
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
|
|
3653
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2838
3654
|
"div",
|
|
2839
3655
|
{
|
|
2840
3656
|
ref: containerRef,
|
|
2841
3657
|
className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
|
|
2842
|
-
children: /* @__PURE__ */ (0,
|
|
3658
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2843
3659
|
"canvas",
|
|
2844
3660
|
{
|
|
2845
3661
|
ref: canvasRef,
|
|
@@ -2852,11 +3668,11 @@ var ImageEditor = ({
|
|
|
2852
3668
|
)
|
|
2853
3669
|
}
|
|
2854
3670
|
),
|
|
2855
|
-
/* @__PURE__ */ (0,
|
|
2856
|
-
activeTab === "crop" && /* @__PURE__ */ (0,
|
|
2857
|
-
/* @__PURE__ */ (0,
|
|
2858
|
-
/* @__PURE__ */ (0,
|
|
2859
|
-
/* @__PURE__ */ (0,
|
|
3671
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
|
|
3672
|
+
activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
3673
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
|
|
3674
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
3675
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2860
3676
|
"button",
|
|
2861
3677
|
{
|
|
2862
3678
|
type: "button",
|
|
@@ -2868,7 +3684,7 @@ var ImageEditor = ({
|
|
|
2868
3684
|
children: "Freeform"
|
|
2869
3685
|
}
|
|
2870
3686
|
),
|
|
2871
|
-
/* @__PURE__ */ (0,
|
|
3687
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2872
3688
|
"button",
|
|
2873
3689
|
{
|
|
2874
3690
|
type: "button",
|
|
@@ -2880,7 +3696,7 @@ var ImageEditor = ({
|
|
|
2880
3696
|
children: "1:1 Square"
|
|
2881
3697
|
}
|
|
2882
3698
|
),
|
|
2883
|
-
/* @__PURE__ */ (0,
|
|
3699
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2884
3700
|
"button",
|
|
2885
3701
|
{
|
|
2886
3702
|
type: "button",
|
|
@@ -2892,7 +3708,7 @@ var ImageEditor = ({
|
|
|
2892
3708
|
children: "4:3 Standard"
|
|
2893
3709
|
}
|
|
2894
3710
|
),
|
|
2895
|
-
/* @__PURE__ */ (0,
|
|
3711
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2896
3712
|
"button",
|
|
2897
3713
|
{
|
|
2898
3714
|
type: "button",
|
|
@@ -2904,7 +3720,7 @@ var ImageEditor = ({
|
|
|
2904
3720
|
children: "16:9 Wide"
|
|
2905
3721
|
}
|
|
2906
3722
|
),
|
|
2907
|
-
/* @__PURE__ */ (0,
|
|
3723
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2908
3724
|
"button",
|
|
2909
3725
|
{
|
|
2910
3726
|
type: "button",
|
|
@@ -2916,7 +3732,7 @@ var ImageEditor = ({
|
|
|
2916
3732
|
children: "3:2 Photo"
|
|
2917
3733
|
}
|
|
2918
3734
|
),
|
|
2919
|
-
/* @__PURE__ */ (0,
|
|
3735
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2920
3736
|
"button",
|
|
2921
3737
|
{
|
|
2922
3738
|
type: "button",
|
|
@@ -2929,10 +3745,10 @@ var ImageEditor = ({
|
|
|
2929
3745
|
}
|
|
2930
3746
|
)
|
|
2931
3747
|
] }),
|
|
2932
|
-
/* @__PURE__ */ (0,
|
|
2933
|
-
/* @__PURE__ */ (0,
|
|
2934
|
-
/* @__PURE__ */ (0,
|
|
2935
|
-
/* @__PURE__ */ (0,
|
|
3748
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
3749
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
|
|
3750
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
3751
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2936
3752
|
"button",
|
|
2937
3753
|
{
|
|
2938
3754
|
type: "button",
|
|
@@ -2941,7 +3757,7 @@ var ImageEditor = ({
|
|
|
2941
3757
|
children: "Rotate -90\xB0"
|
|
2942
3758
|
}
|
|
2943
3759
|
),
|
|
2944
|
-
/* @__PURE__ */ (0,
|
|
3760
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2945
3761
|
"button",
|
|
2946
3762
|
{
|
|
2947
3763
|
type: "button",
|
|
@@ -2950,7 +3766,7 @@ var ImageEditor = ({
|
|
|
2950
3766
|
children: "Rotate +90\xB0"
|
|
2951
3767
|
}
|
|
2952
3768
|
),
|
|
2953
|
-
/* @__PURE__ */ (0,
|
|
3769
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2954
3770
|
"button",
|
|
2955
3771
|
{
|
|
2956
3772
|
type: "button",
|
|
@@ -2959,7 +3775,7 @@ var ImageEditor = ({
|
|
|
2959
3775
|
children: "Flip Horizontal"
|
|
2960
3776
|
}
|
|
2961
3777
|
),
|
|
2962
|
-
/* @__PURE__ */ (0,
|
|
3778
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2963
3779
|
"button",
|
|
2964
3780
|
{
|
|
2965
3781
|
type: "button",
|
|
@@ -2970,10 +3786,10 @@ var ImageEditor = ({
|
|
|
2970
3786
|
)
|
|
2971
3787
|
] })
|
|
2972
3788
|
] }),
|
|
2973
|
-
activeTab === "adjust" && /* @__PURE__ */ (0,
|
|
2974
|
-
/* @__PURE__ */ (0,
|
|
2975
|
-
/* @__PURE__ */ (0,
|
|
2976
|
-
/* @__PURE__ */ (0,
|
|
3789
|
+
activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
3790
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
3791
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
|
|
3792
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2977
3793
|
"button",
|
|
2978
3794
|
{
|
|
2979
3795
|
type: "button",
|
|
@@ -2983,136 +3799,49 @@ var ImageEditor = ({
|
|
|
2983
3799
|
}
|
|
2984
3800
|
)
|
|
2985
3801
|
] }),
|
|
2986
|
-
/* @__PURE__ */ (0,
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
] }),
|
|
2991
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2992
|
-
"input",
|
|
2993
|
-
{
|
|
2994
|
-
type: "range",
|
|
2995
|
-
min: "-100",
|
|
2996
|
-
max: "100",
|
|
2997
|
-
value: state.adjustments.brightness,
|
|
2998
|
-
onChange: (e) => editor.setAdjustments({ brightness: Number(e.target.value) }),
|
|
2999
|
-
className: "w-full accent-indigo-500"
|
|
3000
|
-
}
|
|
3001
|
-
)
|
|
3002
|
-
] }),
|
|
3003
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
3004
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
3005
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Contrast" }),
|
|
3006
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
|
|
3007
|
-
] }),
|
|
3008
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3009
|
-
"input",
|
|
3802
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Histogram, { histogram: editor.histogram }),
|
|
3803
|
+
import_image_editor6.IMAGE_ADJUSTMENT_CONTROLS.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react28.default.Fragment, { children: [
|
|
3804
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3805
|
+
ControlSlider,
|
|
3010
3806
|
{
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
onChange: (e) => editor.setAdjustments({ contrast: Number(e.target.value) }),
|
|
3016
|
-
className: "w-full accent-indigo-500"
|
|
3807
|
+
spec,
|
|
3808
|
+
value: (0, import_image_editor6.readAdjustmentControls)(state.adjustments)[spec.id],
|
|
3809
|
+
disabled: spec.requiresProcess !== void 0 && legacyProcess,
|
|
3810
|
+
onChange: (v) => editor.setAdjustments({ [spec.id]: v })
|
|
3017
3811
|
}
|
|
3018
|
-
)
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
3022
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Saturation" }),
|
|
3023
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
|
|
3024
|
-
] }),
|
|
3025
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3026
|
-
"input",
|
|
3027
|
-
{
|
|
3028
|
-
type: "range",
|
|
3029
|
-
min: "-100",
|
|
3030
|
-
max: "100",
|
|
3031
|
-
value: state.adjustments.saturation,
|
|
3032
|
-
onChange: (e) => editor.setAdjustments({ saturation: Number(e.target.value) }),
|
|
3033
|
-
className: "w-full accent-indigo-500"
|
|
3034
|
-
}
|
|
3035
|
-
)
|
|
3036
|
-
] }),
|
|
3037
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
3038
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
3039
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Exposure" }),
|
|
3040
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
|
|
3041
|
-
] }),
|
|
3042
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3043
|
-
"input",
|
|
3044
|
-
{
|
|
3045
|
-
type: "range",
|
|
3046
|
-
min: "-100",
|
|
3047
|
-
max: "100",
|
|
3048
|
-
value: state.adjustments.exposure,
|
|
3049
|
-
onChange: (e) => editor.setAdjustments({ exposure: Number(e.target.value) }),
|
|
3050
|
-
className: "w-full accent-indigo-500"
|
|
3051
|
-
}
|
|
3052
|
-
)
|
|
3053
|
-
] }),
|
|
3054
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
3055
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
3056
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Warmth / Temperature" }),
|
|
3057
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
|
|
3058
|
-
] }),
|
|
3059
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3060
|
-
"input",
|
|
3061
|
-
{
|
|
3062
|
-
type: "range",
|
|
3063
|
-
min: "-100",
|
|
3064
|
-
max: "100",
|
|
3065
|
-
value: state.adjustments.temperature,
|
|
3066
|
-
onChange: (e) => editor.setAdjustments({ temperature: Number(e.target.value) }),
|
|
3067
|
-
className: "w-full accent-indigo-500"
|
|
3068
|
-
}
|
|
3069
|
-
)
|
|
3070
|
-
] }),
|
|
3071
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
3072
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
3073
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Blur" }),
|
|
3074
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
|
|
3075
|
-
state.adjustments.blur,
|
|
3076
|
-
"px"
|
|
3077
|
-
] })
|
|
3078
|
-
] }),
|
|
3079
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3080
|
-
"input",
|
|
3081
|
-
{
|
|
3082
|
-
type: "range",
|
|
3083
|
-
min: "0",
|
|
3084
|
-
max: "30",
|
|
3085
|
-
value: state.adjustments.blur,
|
|
3086
|
-
onChange: (e) => editor.setAdjustments({ blur: Number(e.target.value) }),
|
|
3087
|
-
className: "w-full accent-indigo-500"
|
|
3088
|
-
}
|
|
3089
|
-
)
|
|
3090
|
-
] }),
|
|
3091
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
3092
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
3093
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Opacity" }),
|
|
3094
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
|
|
3095
|
-
Math.round(state.adjustments.opacity * 100),
|
|
3096
|
-
"%"
|
|
3097
|
-
] })
|
|
3098
|
-
] }),
|
|
3099
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3100
|
-
"input",
|
|
3812
|
+
),
|
|
3813
|
+
spec.id === "tint" && legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
3814
|
+
"div",
|
|
3101
3815
|
{
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3816
|
+
className: "flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200",
|
|
3817
|
+
"data-testid": "image-editor-process-notice",
|
|
3818
|
+
children: [
|
|
3819
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
|
|
3820
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3821
|
+
"button",
|
|
3822
|
+
{
|
|
3823
|
+
type: "button",
|
|
3824
|
+
onClick: () => editor.upgradeProcess(),
|
|
3825
|
+
className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
|
|
3826
|
+
children: "Update"
|
|
3827
|
+
}
|
|
3828
|
+
)
|
|
3829
|
+
]
|
|
3109
3830
|
}
|
|
3110
3831
|
)
|
|
3111
|
-
] })
|
|
3832
|
+
] }, spec.id)),
|
|
3833
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-t border-slate-700/60 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3834
|
+
AdjustmentLayersPanel,
|
|
3835
|
+
{
|
|
3836
|
+
controller,
|
|
3837
|
+
layers: state.adjustments.layers,
|
|
3838
|
+
histogram: editor.histogram
|
|
3839
|
+
}
|
|
3840
|
+
) })
|
|
3112
3841
|
] }),
|
|
3113
|
-
activeTab === "annotate" && /* @__PURE__ */ (0,
|
|
3114
|
-
/* @__PURE__ */ (0,
|
|
3115
|
-
/* @__PURE__ */ (0,
|
|
3842
|
+
activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
3843
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
|
|
3844
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3116
3845
|
"button",
|
|
3117
3846
|
{
|
|
3118
3847
|
type: "button",
|
|
@@ -3122,9 +3851,9 @@ var ImageEditor = ({
|
|
|
3122
3851
|
},
|
|
3123
3852
|
t
|
|
3124
3853
|
)) }),
|
|
3125
|
-
/* @__PURE__ */ (0,
|
|
3126
|
-
/* @__PURE__ */ (0,
|
|
3127
|
-
/* @__PURE__ */ (0,
|
|
3854
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
3855
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { className: "text-slate-400", children: "Color:" }),
|
|
3856
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3128
3857
|
"input",
|
|
3129
3858
|
{
|
|
3130
3859
|
type: "color",
|
|
@@ -3133,15 +3862,15 @@ var ImageEditor = ({
|
|
|
3133
3862
|
className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
|
|
3134
3863
|
}
|
|
3135
3864
|
),
|
|
3136
|
-
/* @__PURE__ */ (0,
|
|
3137
|
-
/* @__PURE__ */ (0,
|
|
3138
|
-
/* @__PURE__ */ (0,
|
|
3139
|
-
/* @__PURE__ */ (0,
|
|
3865
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex-1 ml-2", children: [
|
|
3866
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
|
|
3867
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Width:" }),
|
|
3868
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-mono", children: [
|
|
3140
3869
|
strokeWidth,
|
|
3141
3870
|
"px"
|
|
3142
3871
|
] })
|
|
3143
3872
|
] }),
|
|
3144
|
-
/* @__PURE__ */ (0,
|
|
3873
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3145
3874
|
"input",
|
|
3146
3875
|
{
|
|
3147
3876
|
type: "range",
|
|
@@ -3154,10 +3883,10 @@ var ImageEditor = ({
|
|
|
3154
3883
|
)
|
|
3155
3884
|
] })
|
|
3156
3885
|
] }),
|
|
3157
|
-
/* @__PURE__ */ (0,
|
|
3158
|
-
/* @__PURE__ */ (0,
|
|
3159
|
-
/* @__PURE__ */ (0,
|
|
3160
|
-
/* @__PURE__ */ (0,
|
|
3886
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
3887
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
|
|
3888
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-2", children: [
|
|
3889
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3161
3890
|
"input",
|
|
3162
3891
|
{
|
|
3163
3892
|
type: "text",
|
|
@@ -3168,7 +3897,7 @@ var ImageEditor = ({
|
|
|
3168
3897
|
className: "flex-1 bg-slate-950 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-slate-100 placeholder-slate-500 focus:outline-none focus:border-indigo-500"
|
|
3169
3898
|
}
|
|
3170
3899
|
),
|
|
3171
|
-
/* @__PURE__ */ (0,
|
|
3900
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3172
3901
|
"button",
|
|
3173
3902
|
{
|
|
3174
3903
|
type: "button",
|
|
@@ -3178,14 +3907,14 @@ var ImageEditor = ({
|
|
|
3178
3907
|
}
|
|
3179
3908
|
)
|
|
3180
3909
|
] }),
|
|
3181
|
-
/* @__PURE__ */ (0,
|
|
3182
|
-
/* @__PURE__ */ (0,
|
|
3183
|
-
/* @__PURE__ */ (0,
|
|
3910
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
|
|
3911
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Font Size:" }),
|
|
3912
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-mono", children: [
|
|
3184
3913
|
fontSize,
|
|
3185
3914
|
"px"
|
|
3186
3915
|
] })
|
|
3187
3916
|
] }),
|
|
3188
|
-
/* @__PURE__ */ (0,
|
|
3917
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3189
3918
|
"input",
|
|
3190
3919
|
{
|
|
3191
3920
|
type: "range",
|
|
@@ -3196,14 +3925,14 @@ var ImageEditor = ({
|
|
|
3196
3925
|
className: "w-full accent-indigo-500"
|
|
3197
3926
|
}
|
|
3198
3927
|
),
|
|
3199
|
-
/* @__PURE__ */ (0,
|
|
3200
|
-
/* @__PURE__ */ (0,
|
|
3201
|
-
/* @__PURE__ */ (0,
|
|
3928
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
|
|
3929
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
3930
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "text-slate-400", children: [
|
|
3202
3931
|
"Layers (",
|
|
3203
3932
|
state.annotations.length,
|
|
3204
3933
|
")"
|
|
3205
3934
|
] }),
|
|
3206
|
-
state.selectedAnnotationId && /* @__PURE__ */ (0,
|
|
3935
|
+
state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3207
3936
|
"button",
|
|
3208
3937
|
{
|
|
3209
3938
|
type: "button",
|
|
@@ -3212,7 +3941,7 @@ var ImageEditor = ({
|
|
|
3212
3941
|
children: "Delete Selected"
|
|
3213
3942
|
}
|
|
3214
3943
|
),
|
|
3215
|
-
state.annotations.length > 0 && /* @__PURE__ */ (0,
|
|
3944
|
+
state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3216
3945
|
"button",
|
|
3217
3946
|
{
|
|
3218
3947
|
type: "button",
|
|
@@ -3223,10 +3952,10 @@ var ImageEditor = ({
|
|
|
3223
3952
|
)
|
|
3224
3953
|
] })
|
|
3225
3954
|
] }),
|
|
3226
|
-
activeTab === "depth" && /* @__PURE__ */ (0,
|
|
3227
|
-
/* @__PURE__ */ (0,
|
|
3228
|
-
/* @__PURE__ */ (0,
|
|
3229
|
-
/* @__PURE__ */ (0,
|
|
3955
|
+
activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
|
|
3956
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
3957
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
|
|
3958
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3230
3959
|
"button",
|
|
3231
3960
|
{
|
|
3232
3961
|
type: "button",
|
|
@@ -3236,8 +3965,8 @@ var ImageEditor = ({
|
|
|
3236
3965
|
}
|
|
3237
3966
|
)
|
|
3238
3967
|
] }),
|
|
3239
|
-
/* @__PURE__ */ (0,
|
|
3240
|
-
/* @__PURE__ */ (0,
|
|
3968
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
|
|
3969
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3241
3970
|
"input",
|
|
3242
3971
|
{
|
|
3243
3972
|
type: "checkbox",
|
|
@@ -3246,12 +3975,12 @@ var ImageEditor = ({
|
|
|
3246
3975
|
className: "rounded accent-indigo-500"
|
|
3247
3976
|
}
|
|
3248
3977
|
),
|
|
3249
|
-
/* @__PURE__ */ (0,
|
|
3978
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Enable Depth / Alpha Mask" })
|
|
3250
3979
|
] }),
|
|
3251
|
-
state.depthMask.enabled && /* @__PURE__ */ (0,
|
|
3252
|
-
/* @__PURE__ */ (0,
|
|
3253
|
-
/* @__PURE__ */ (0,
|
|
3254
|
-
/* @__PURE__ */ (0,
|
|
3980
|
+
state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
3981
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
3982
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
|
|
3983
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3255
3984
|
"input",
|
|
3256
3985
|
{
|
|
3257
3986
|
type: "text",
|
|
@@ -3262,8 +3991,8 @@ var ImageEditor = ({
|
|
|
3262
3991
|
}
|
|
3263
3992
|
)
|
|
3264
3993
|
] }),
|
|
3265
|
-
/* @__PURE__ */ (0,
|
|
3266
|
-
/* @__PURE__ */ (0,
|
|
3994
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-2", children: [
|
|
3995
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3267
3996
|
"button",
|
|
3268
3997
|
{
|
|
3269
3998
|
type: "button",
|
|
@@ -3272,7 +4001,7 @@ var ImageEditor = ({
|
|
|
3272
4001
|
children: "Threshold Mode"
|
|
3273
4002
|
}
|
|
3274
4003
|
),
|
|
3275
|
-
/* @__PURE__ */ (0,
|
|
4004
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3276
4005
|
"button",
|
|
3277
4006
|
{
|
|
3278
4007
|
type: "button",
|
|
@@ -3282,12 +4011,12 @@ var ImageEditor = ({
|
|
|
3282
4011
|
}
|
|
3283
4012
|
)
|
|
3284
4013
|
] }),
|
|
3285
|
-
/* @__PURE__ */ (0,
|
|
3286
|
-
/* @__PURE__ */ (0,
|
|
3287
|
-
/* @__PURE__ */ (0,
|
|
3288
|
-
/* @__PURE__ */ (0,
|
|
4014
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
4015
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
4016
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Depth Start (Lower):" }),
|
|
4017
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
|
|
3289
4018
|
] }),
|
|
3290
|
-
/* @__PURE__ */ (0,
|
|
4019
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3291
4020
|
"input",
|
|
3292
4021
|
{
|
|
3293
4022
|
type: "range",
|
|
@@ -3301,12 +4030,12 @@ var ImageEditor = ({
|
|
|
3301
4030
|
}
|
|
3302
4031
|
)
|
|
3303
4032
|
] }),
|
|
3304
|
-
/* @__PURE__ */ (0,
|
|
3305
|
-
/* @__PURE__ */ (0,
|
|
3306
|
-
/* @__PURE__ */ (0,
|
|
3307
|
-
/* @__PURE__ */ (0,
|
|
4033
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
4034
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
4035
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Depth Stop (Upper):" }),
|
|
4036
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
|
|
3308
4037
|
] }),
|
|
3309
|
-
/* @__PURE__ */ (0,
|
|
4038
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3310
4039
|
"input",
|
|
3311
4040
|
{
|
|
3312
4041
|
type: "range",
|
|
@@ -3320,12 +4049,12 @@ var ImageEditor = ({
|
|
|
3320
4049
|
}
|
|
3321
4050
|
)
|
|
3322
4051
|
] }),
|
|
3323
|
-
/* @__PURE__ */ (0,
|
|
3324
|
-
/* @__PURE__ */ (0,
|
|
3325
|
-
/* @__PURE__ */ (0,
|
|
3326
|
-
/* @__PURE__ */ (0,
|
|
4052
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
4053
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
|
|
4054
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Softness:" }),
|
|
4055
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
|
|
3327
4056
|
] }),
|
|
3328
|
-
/* @__PURE__ */ (0,
|
|
4057
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3329
4058
|
"input",
|
|
3330
4059
|
{
|
|
3331
4060
|
type: "range",
|
|
@@ -3337,8 +4066,8 @@ var ImageEditor = ({
|
|
|
3337
4066
|
}
|
|
3338
4067
|
)
|
|
3339
4068
|
] }),
|
|
3340
|
-
/* @__PURE__ */ (0,
|
|
3341
|
-
/* @__PURE__ */ (0,
|
|
4069
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
|
|
4070
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3342
4071
|
"input",
|
|
3343
4072
|
{
|
|
3344
4073
|
type: "checkbox",
|
|
@@ -3347,52 +4076,52 @@ var ImageEditor = ({
|
|
|
3347
4076
|
className: "rounded accent-indigo-500"
|
|
3348
4077
|
}
|
|
3349
4078
|
),
|
|
3350
|
-
/* @__PURE__ */ (0,
|
|
4079
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Invert Mask" })
|
|
3351
4080
|
] })
|
|
3352
4081
|
] })
|
|
3353
4082
|
] }),
|
|
3354
|
-
activeTab === "ai" && /* @__PURE__ */ (0,
|
|
3355
|
-
/* @__PURE__ */ (0,
|
|
3356
|
-
/* @__PURE__ */ (0,
|
|
3357
|
-
/* @__PURE__ */ (0,
|
|
3358
|
-
/* @__PURE__ */ (0,
|
|
4083
|
+
activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
|
|
4084
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
4085
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
4086
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
4087
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
|
|
3359
4088
|
"Background Removal"
|
|
3360
4089
|
] }),
|
|
3361
|
-
isRemovingBg && /* @__PURE__ */ (0,
|
|
4090
|
+
isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
|
|
3362
4091
|
] }),
|
|
3363
|
-
/* @__PURE__ */ (0,
|
|
3364
|
-
/* @__PURE__ */ (0,
|
|
4092
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
|
|
4093
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3365
4094
|
"button",
|
|
3366
4095
|
{
|
|
3367
4096
|
type: "button",
|
|
3368
4097
|
onClick: handleRemoveBackground,
|
|
3369
4098
|
disabled: isRemovingBg || isInpainting,
|
|
3370
4099
|
className: "mt-1 px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
3371
|
-
children: isRemovingBg ? /* @__PURE__ */ (0,
|
|
3372
|
-
/* @__PURE__ */ (0,
|
|
4100
|
+
children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
4101
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
3373
4102
|
"Removing Background..."
|
|
3374
|
-
] }) : /* @__PURE__ */ (0,
|
|
3375
|
-
/* @__PURE__ */ (0,
|
|
4103
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
4104
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
3376
4105
|
"Remove Background"
|
|
3377
4106
|
] })
|
|
3378
4107
|
}
|
|
3379
4108
|
)
|
|
3380
4109
|
] }),
|
|
3381
|
-
/* @__PURE__ */ (0,
|
|
3382
|
-
/* @__PURE__ */ (0,
|
|
3383
|
-
/* @__PURE__ */ (0,
|
|
3384
|
-
/* @__PURE__ */ (0,
|
|
3385
|
-
/* @__PURE__ */ (0,
|
|
3386
|
-
/* @__PURE__ */ (0,
|
|
4110
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
|
|
4111
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
4112
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
|
|
4113
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
4114
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
|
|
4115
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "m14 7 3 3" })
|
|
3387
4116
|
] }),
|
|
3388
4117
|
"Generative Inpainting"
|
|
3389
4118
|
] }),
|
|
3390
|
-
isInpainting && /* @__PURE__ */ (0,
|
|
4119
|
+
isInpainting && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
|
|
3391
4120
|
] }),
|
|
3392
|
-
/* @__PURE__ */ (0,
|
|
3393
|
-
/* @__PURE__ */ (0,
|
|
3394
|
-
/* @__PURE__ */ (0,
|
|
3395
|
-
/* @__PURE__ */ (0,
|
|
4121
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
|
|
4122
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4123
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
|
|
4124
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3396
4125
|
"textarea",
|
|
3397
4126
|
{
|
|
3398
4127
|
id: "ai-inpaint-prompt-react",
|
|
@@ -3404,10 +4133,10 @@ var ImageEditor = ({
|
|
|
3404
4133
|
}
|
|
3405
4134
|
)
|
|
3406
4135
|
] }),
|
|
3407
|
-
/* @__PURE__ */ (0,
|
|
3408
|
-
/* @__PURE__ */ (0,
|
|
3409
|
-
/* @__PURE__ */ (0,
|
|
3410
|
-
/* @__PURE__ */ (0,
|
|
4136
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4137
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
|
|
4138
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
|
|
4139
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
3411
4140
|
"button",
|
|
3412
4141
|
{
|
|
3413
4142
|
type: "button",
|
|
@@ -3420,7 +4149,7 @@ var ImageEditor = ({
|
|
|
3420
4149
|
]
|
|
3421
4150
|
}
|
|
3422
4151
|
),
|
|
3423
|
-
/* @__PURE__ */ (0,
|
|
4152
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
3424
4153
|
"button",
|
|
3425
4154
|
{
|
|
3426
4155
|
type: "button",
|
|
@@ -3435,25 +4164,25 @@ var ImageEditor = ({
|
|
|
3435
4164
|
)
|
|
3436
4165
|
] })
|
|
3437
4166
|
] }),
|
|
3438
|
-
/* @__PURE__ */ (0,
|
|
4167
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3439
4168
|
"button",
|
|
3440
4169
|
{
|
|
3441
4170
|
type: "button",
|
|
3442
4171
|
onClick: handleInpaint,
|
|
3443
4172
|
disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
|
|
3444
4173
|
className: "mt-1 px-3 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
|
|
3445
|
-
children: isInpainting ? /* @__PURE__ */ (0,
|
|
3446
|
-
/* @__PURE__ */ (0,
|
|
4174
|
+
children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
4175
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
3447
4176
|
"Generating Inpaint..."
|
|
3448
|
-
] }) : /* @__PURE__ */ (0,
|
|
3449
|
-
/* @__PURE__ */ (0,
|
|
4177
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
4178
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
|
|
3450
4179
|
"Apply Inpainting"
|
|
3451
4180
|
] })
|
|
3452
4181
|
}
|
|
3453
4182
|
)
|
|
3454
4183
|
] }),
|
|
3455
|
-
aiError && /* @__PURE__ */ (0,
|
|
3456
|
-
aiSuccess && /* @__PURE__ */ (0,
|
|
4184
|
+
aiError && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
|
|
4185
|
+
aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
|
|
3457
4186
|
] })
|
|
3458
4187
|
] })
|
|
3459
4188
|
] })
|
|
@@ -3463,8 +4192,8 @@ var ImageEditor = ({
|
|
|
3463
4192
|
};
|
|
3464
4193
|
|
|
3465
4194
|
// src/review/useReview.ts
|
|
3466
|
-
var
|
|
3467
|
-
var
|
|
4195
|
+
var import_react29 = require("react");
|
|
4196
|
+
var import_editor11 = require("@pixerate/editor");
|
|
3468
4197
|
var EMPTY_COMMENTS = Object.freeze({
|
|
3469
4198
|
threads: [],
|
|
3470
4199
|
activeThreadId: null
|
|
@@ -3481,24 +4210,24 @@ function isCommentStore(value) {
|
|
|
3481
4210
|
return !!value && typeof value.getSnapshot === "function" && typeof value.createThread === "function";
|
|
3482
4211
|
}
|
|
3483
4212
|
function useComments(source) {
|
|
3484
|
-
const store = isCommentStore(source) ? source : (0,
|
|
3485
|
-
const subscribe = (0,
|
|
4213
|
+
const store = isCommentStore(source) ? source : (0, import_editor11.getCommentStore)(source);
|
|
4214
|
+
const subscribe = (0, import_react29.useCallback)(
|
|
3486
4215
|
(onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
|
|
3487
4216
|
[store]
|
|
3488
4217
|
);
|
|
3489
4218
|
const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
|
|
3490
|
-
const snapshot = (0,
|
|
3491
|
-
return (0,
|
|
4219
|
+
const snapshot = (0, import_react29.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
4220
|
+
return (0, import_react29.useMemo)(() => ({ ...snapshot, store }), [snapshot, store]);
|
|
3492
4221
|
}
|
|
3493
4222
|
function useTrackChanges(editor) {
|
|
3494
|
-
const store = (0,
|
|
3495
|
-
const subscribe = (0,
|
|
4223
|
+
const store = (0, import_editor11.getTrackChangesStore)(editor ?? null);
|
|
4224
|
+
const subscribe = (0, import_react29.useCallback)(
|
|
3496
4225
|
(onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
|
|
3497
4226
|
[store]
|
|
3498
4227
|
);
|
|
3499
4228
|
const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
|
|
3500
|
-
const snapshot = (0,
|
|
3501
|
-
return (0,
|
|
4229
|
+
const snapshot = (0, import_react29.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
4230
|
+
return (0, import_react29.useMemo)(() => {
|
|
3502
4231
|
const run = (fn) => () => {
|
|
3503
4232
|
if (editor && !editor.isDestroyed) fn(editor);
|
|
3504
4233
|
};
|
|
@@ -3518,9 +4247,9 @@ function useTrackChanges(editor) {
|
|
|
3518
4247
|
}
|
|
3519
4248
|
|
|
3520
4249
|
// src/review/CommentsPanel.tsx
|
|
3521
|
-
var
|
|
3522
|
-
var
|
|
3523
|
-
var
|
|
4250
|
+
var import_react30 = require("react");
|
|
4251
|
+
var import_editor12 = require("@pixerate/editor");
|
|
4252
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3524
4253
|
var buttonClass = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
|
|
3525
4254
|
var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
|
|
3526
4255
|
var textareaClass = "w-full resize-y rounded-md border border-input bg-background p-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary";
|
|
@@ -3530,22 +4259,22 @@ var CommentsPanel = ({
|
|
|
3530
4259
|
author: authorProp,
|
|
3531
4260
|
showResolved: showResolvedInitial = false,
|
|
3532
4261
|
className = "flex flex-col gap-2 text-sm text-foreground",
|
|
3533
|
-
formatTime = (t) => (0,
|
|
4262
|
+
formatTime = (t) => (0, import_editor12.formatReviewTime)(t),
|
|
3534
4263
|
emptyMessage = "No comments yet. Select text and add a comment."
|
|
3535
4264
|
}) => {
|
|
3536
4265
|
const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
|
|
3537
|
-
const [showResolved, setShowResolved] = (0,
|
|
3538
|
-
const author = authorProp ?? (0,
|
|
4266
|
+
const [showResolved, setShowResolved] = (0, import_react30.useState)(showResolvedInitial);
|
|
4267
|
+
const author = authorProp ?? (0, import_editor12.getCommentAuthor)(editor) ?? { id: "anonymous", name: "Anonymous" };
|
|
3539
4268
|
const visible = threads.filter((t) => showResolved || !t.resolved);
|
|
3540
4269
|
const resolvedCount = threads.filter((t) => t.resolved).length;
|
|
3541
|
-
return /* @__PURE__ */ (0,
|
|
3542
|
-
/* @__PURE__ */ (0,
|
|
3543
|
-
/* @__PURE__ */ (0,
|
|
4270
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("aside", { className, "aria-label": "Comments", children: [
|
|
4271
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
4272
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-xs font-medium text-muted-foreground", children: [
|
|
3544
4273
|
threads.length - resolvedCount,
|
|
3545
4274
|
" open",
|
|
3546
4275
|
resolvedCount > 0 ? ` \xB7 ${resolvedCount} resolved` : ""
|
|
3547
4276
|
] }),
|
|
3548
|
-
resolvedCount > 0 && /* @__PURE__ */ (0,
|
|
4277
|
+
resolvedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3549
4278
|
"button",
|
|
3550
4279
|
{
|
|
3551
4280
|
type: "button",
|
|
@@ -3556,7 +4285,7 @@ var CommentsPanel = ({
|
|
|
3556
4285
|
}
|
|
3557
4286
|
)
|
|
3558
4287
|
] }),
|
|
3559
|
-
visible.length === 0 ? /* @__PURE__ */ (0,
|
|
4288
|
+
visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3560
4289
|
CommentThreadCard,
|
|
3561
4290
|
{
|
|
3562
4291
|
thread,
|
|
@@ -3578,7 +4307,7 @@ var CommentThreadCard = ({
|
|
|
3578
4307
|
author,
|
|
3579
4308
|
formatTime
|
|
3580
4309
|
}) => {
|
|
3581
|
-
const [draft, setDraft] = (0,
|
|
4310
|
+
const [draft, setDraft] = (0, import_react30.useState)("");
|
|
3582
4311
|
const isDraft = thread.body === "";
|
|
3583
4312
|
const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
|
|
3584
4313
|
const live = editor && !editor.isDestroyed ? editor : null;
|
|
@@ -3604,7 +4333,7 @@ var CommentThreadCard = ({
|
|
|
3604
4333
|
setDraft("");
|
|
3605
4334
|
};
|
|
3606
4335
|
const stop = (e) => e.stopPropagation();
|
|
3607
|
-
return /* @__PURE__ */ (0,
|
|
4336
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3608
4337
|
"li",
|
|
3609
4338
|
{
|
|
3610
4339
|
className: `flex flex-col gap-2 rounded-md border bg-background p-3 cursor-pointer ${active ? "border-primary ring-1 ring-primary" : "border-border"} ${thread.resolved ? "opacity-70" : ""}`,
|
|
@@ -3620,24 +4349,24 @@ var CommentThreadCard = ({
|
|
|
3620
4349
|
}
|
|
3621
4350
|
},
|
|
3622
4351
|
children: [
|
|
3623
|
-
/* @__PURE__ */ (0,
|
|
3624
|
-
/* @__PURE__ */ (0,
|
|
3625
|
-
/* @__PURE__ */ (0,
|
|
4352
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
4353
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: thread.author.name }),
|
|
4354
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
|
|
3626
4355
|
] }),
|
|
3627
|
-
quote && /* @__PURE__ */ (0,
|
|
3628
|
-
thread.orphaned && /* @__PURE__ */ (0,
|
|
3629
|
-
thread.resolved && /* @__PURE__ */ (0,
|
|
3630
|
-
!isDraft && /* @__PURE__ */ (0,
|
|
3631
|
-
thread.replies.length > 0 && /* @__PURE__ */ (0,
|
|
3632
|
-
/* @__PURE__ */ (0,
|
|
3633
|
-
/* @__PURE__ */ (0,
|
|
4356
|
+
quote && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
|
|
4357
|
+
thread.orphaned && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
|
|
4358
|
+
thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
|
|
4359
|
+
!isDraft && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
|
|
4360
|
+
thread.replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
|
|
4361
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-xs", children: [
|
|
4362
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: reply.author.name }),
|
|
3634
4363
|
" ",
|
|
3635
|
-
/* @__PURE__ */ (0,
|
|
4364
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
|
|
3636
4365
|
] }),
|
|
3637
|
-
/* @__PURE__ */ (0,
|
|
4366
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
|
|
3638
4367
|
] }, reply.id)) }),
|
|
3639
|
-
(active || isDraft) && !thread.resolved && /* @__PURE__ */ (0,
|
|
3640
|
-
/* @__PURE__ */ (0,
|
|
4368
|
+
(active || isDraft) && !thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-1", onClick: stop, children: [
|
|
4369
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3641
4370
|
"textarea",
|
|
3642
4371
|
{
|
|
3643
4372
|
className: textareaClass,
|
|
@@ -3655,9 +4384,9 @@ var CommentThreadCard = ({
|
|
|
3655
4384
|
}
|
|
3656
4385
|
}
|
|
3657
4386
|
),
|
|
3658
|
-
/* @__PURE__ */ (0,
|
|
3659
|
-
isDraft && /* @__PURE__ */ (0,
|
|
3660
|
-
/* @__PURE__ */ (0,
|
|
4387
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex justify-end gap-1", children: [
|
|
4388
|
+
isDraft && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
|
|
4389
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3661
4390
|
"button",
|
|
3662
4391
|
{
|
|
3663
4392
|
type: "button",
|
|
@@ -3669,9 +4398,9 @@ var CommentThreadCard = ({
|
|
|
3669
4398
|
)
|
|
3670
4399
|
] })
|
|
3671
4400
|
] }),
|
|
3672
|
-
!isDraft && /* @__PURE__ */ (0,
|
|
3673
|
-
/* @__PURE__ */ (0,
|
|
3674
|
-
/* @__PURE__ */ (0,
|
|
4401
|
+
!isDraft && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex justify-end gap-1", onClick: stop, children: [
|
|
4402
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
|
|
4403
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3675
4404
|
"button",
|
|
3676
4405
|
{
|
|
3677
4406
|
type: "button",
|
|
@@ -3687,7 +4416,7 @@ var CommentThreadCard = ({
|
|
|
3687
4416
|
};
|
|
3688
4417
|
|
|
3689
4418
|
// src/review/ReviewToolbar.tsx
|
|
3690
|
-
var
|
|
4419
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3691
4420
|
var defaultDescriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground";
|
|
3692
4421
|
var buttonClass2 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
|
|
3693
4422
|
var ReviewToolbar = ({
|
|
@@ -3700,8 +4429,8 @@ var ReviewToolbar = ({
|
|
|
3700
4429
|
const count = review.changes.length;
|
|
3701
4430
|
const current = review.currentChange;
|
|
3702
4431
|
const index = current ? review.changes.findIndex((c) => c.id === current.id) + 1 : 0;
|
|
3703
|
-
return /* @__PURE__ */ (0,
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
4432
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className, role: "toolbar", "aria-label": "Review", children: [
|
|
4433
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3705
4434
|
"button",
|
|
3706
4435
|
{
|
|
3707
4436
|
type: "button",
|
|
@@ -3712,8 +4441,8 @@ var ReviewToolbar = ({
|
|
|
3712
4441
|
children: review.enabled ? "Tracking on" : "Track changes"
|
|
3713
4442
|
}
|
|
3714
4443
|
),
|
|
3715
|
-
/* @__PURE__ */ (0,
|
|
3716
|
-
/* @__PURE__ */ (0,
|
|
4444
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
|
|
4445
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3717
4446
|
"button",
|
|
3718
4447
|
{
|
|
3719
4448
|
type: "button",
|
|
@@ -3724,8 +4453,8 @@ var ReviewToolbar = ({
|
|
|
3724
4453
|
children: "\u2039"
|
|
3725
4454
|
}
|
|
3726
4455
|
),
|
|
3727
|
-
/* @__PURE__ */ (0,
|
|
3728
|
-
/* @__PURE__ */ (0,
|
|
4456
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
|
|
4457
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3729
4458
|
"button",
|
|
3730
4459
|
{
|
|
3731
4460
|
type: "button",
|
|
@@ -3736,7 +4465,7 @@ var ReviewToolbar = ({
|
|
|
3736
4465
|
children: "\u203A"
|
|
3737
4466
|
}
|
|
3738
4467
|
),
|
|
3739
|
-
showDescription && current && /* @__PURE__ */ (0,
|
|
4468
|
+
showDescription && current && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
3740
4469
|
"span",
|
|
3741
4470
|
{
|
|
3742
4471
|
className: descriptionClassName,
|
|
@@ -3748,8 +4477,8 @@ var ReviewToolbar = ({
|
|
|
3748
4477
|
]
|
|
3749
4478
|
}
|
|
3750
4479
|
),
|
|
3751
|
-
/* @__PURE__ */ (0,
|
|
3752
|
-
/* @__PURE__ */ (0,
|
|
4480
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
|
|
4481
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3753
4482
|
"button",
|
|
3754
4483
|
{
|
|
3755
4484
|
type: "button",
|
|
@@ -3759,7 +4488,7 @@ var ReviewToolbar = ({
|
|
|
3759
4488
|
children: "Accept"
|
|
3760
4489
|
}
|
|
3761
4490
|
),
|
|
3762
|
-
/* @__PURE__ */ (0,
|
|
4491
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
3763
4492
|
"button",
|
|
3764
4493
|
{
|
|
3765
4494
|
type: "button",
|
|
@@ -3769,18 +4498,18 @@ var ReviewToolbar = ({
|
|
|
3769
4498
|
children: "Reject"
|
|
3770
4499
|
}
|
|
3771
4500
|
),
|
|
3772
|
-
/* @__PURE__ */ (0,
|
|
3773
|
-
/* @__PURE__ */ (0,
|
|
4501
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
|
|
4502
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
|
|
3774
4503
|
] });
|
|
3775
4504
|
};
|
|
3776
4505
|
|
|
3777
4506
|
// src/review/CommentButton.tsx
|
|
3778
|
-
var
|
|
4507
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
3779
4508
|
var CommentButton = ({
|
|
3780
4509
|
editor,
|
|
3781
4510
|
className = "rounded px-2 py-1 text-xs text-popover-foreground hover:bg-muted",
|
|
3782
4511
|
children = "Comment"
|
|
3783
|
-
}) => /* @__PURE__ */ (0,
|
|
4512
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3784
4513
|
"button",
|
|
3785
4514
|
{
|
|
3786
4515
|
type: "button",
|
|
@@ -3790,42 +4519,830 @@ var CommentButton = ({
|
|
|
3790
4519
|
children
|
|
3791
4520
|
}
|
|
3792
4521
|
);
|
|
4522
|
+
|
|
4523
|
+
// src/review/useDocumentCompare.ts
|
|
4524
|
+
var import_react31 = require("react");
|
|
4525
|
+
var import_editor13 = require("@pixerate/editor");
|
|
4526
|
+
function useDocumentCompare(editor, options) {
|
|
4527
|
+
const [summary, setSummary] = (0, import_react31.useState)(null);
|
|
4528
|
+
const defaults = (0, import_react31.useRef)(options);
|
|
4529
|
+
defaults.current = options;
|
|
4530
|
+
const compare = (0, import_react31.useCallback)(
|
|
4531
|
+
(original, revised, overrides) => {
|
|
4532
|
+
if (!editor || editor.isDestroyed) return null;
|
|
4533
|
+
const result = (0, import_editor13.applyDocumentComparison)(editor, original, revised, {
|
|
4534
|
+
...defaults.current,
|
|
4535
|
+
...overrides
|
|
4536
|
+
});
|
|
4537
|
+
setSummary(result.summary);
|
|
4538
|
+
return result.summary;
|
|
4539
|
+
},
|
|
4540
|
+
[editor]
|
|
4541
|
+
);
|
|
4542
|
+
const clear = (0, import_react31.useCallback)(() => setSummary(null), []);
|
|
4543
|
+
return (0, import_react31.useMemo)(() => ({ summary, compare, clear }), [summary, compare, clear]);
|
|
4544
|
+
}
|
|
4545
|
+
|
|
4546
|
+
// src/commands/useCommandPalette.ts
|
|
4547
|
+
var import_react32 = require("react");
|
|
4548
|
+
var import_commands2 = require("@pixerate/editor/commands");
|
|
4549
|
+
function useCommandPalette(registry, options = {}) {
|
|
4550
|
+
const optionsRef = (0, import_react32.useRef)(options);
|
|
4551
|
+
optionsRef.current = options;
|
|
4552
|
+
const controller = (0, import_react32.useMemo)(
|
|
4553
|
+
() => (0, import_commands2.createCommandPaletteController)(registry, {
|
|
4554
|
+
context: () => {
|
|
4555
|
+
const c = optionsRef.current.context;
|
|
4556
|
+
if (c === void 0) return registry.context;
|
|
4557
|
+
return typeof c === "function" ? c() : c;
|
|
4558
|
+
},
|
|
4559
|
+
scope: () => {
|
|
4560
|
+
const s = optionsRef.current.scope;
|
|
4561
|
+
return typeof s === "function" ? s() : s;
|
|
4562
|
+
},
|
|
4563
|
+
recents: options.recents,
|
|
4564
|
+
recentLimit: options.recentLimit,
|
|
4565
|
+
limit: options.limit,
|
|
4566
|
+
platform: options.platform,
|
|
4567
|
+
onRecentsChange: (recents) => optionsRef.current.onRecentsChange?.(recents),
|
|
4568
|
+
onRun: (result) => optionsRef.current.onRun?.(result)
|
|
4569
|
+
}),
|
|
4570
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4571
|
+
[registry]
|
|
4572
|
+
);
|
|
4573
|
+
const state = (0, import_react32.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
|
|
4574
|
+
const { hotkey, hotkeyTarget } = options;
|
|
4575
|
+
(0, import_react32.useEffect)(() => {
|
|
4576
|
+
if (!hotkey) return;
|
|
4577
|
+
const target = hotkeyTarget ?? (typeof window !== "undefined" ? window : null);
|
|
4578
|
+
if (!target) return;
|
|
4579
|
+
const onKeyDown = (event) => {
|
|
4580
|
+
const e = event;
|
|
4581
|
+
if (e.defaultPrevented || !(0, import_commands2.matchPaletteHotkey)(e, hotkey, optionsRef.current.platform)) return;
|
|
4582
|
+
e.preventDefault();
|
|
4583
|
+
controller.toggle();
|
|
4584
|
+
};
|
|
4585
|
+
target.addEventListener("keydown", onKeyDown);
|
|
4586
|
+
return () => target.removeEventListener("keydown", onKeyDown);
|
|
4587
|
+
}, [controller, hotkey, hotkeyTarget]);
|
|
4588
|
+
return {
|
|
4589
|
+
...state,
|
|
4590
|
+
controller,
|
|
4591
|
+
setOpen: controller.setOpen,
|
|
4592
|
+
toggle: controller.toggle,
|
|
4593
|
+
setQuery: controller.setQuery,
|
|
4594
|
+
setActiveIndex: controller.setActiveIndex,
|
|
4595
|
+
move: controller.move,
|
|
4596
|
+
run: controller.run,
|
|
4597
|
+
runActive: controller.runActive,
|
|
4598
|
+
handleKeyDown: controller.handleKeyDown
|
|
4599
|
+
};
|
|
4600
|
+
}
|
|
4601
|
+
|
|
4602
|
+
// src/commands/CommandPalette.tsx
|
|
4603
|
+
var import_react33 = __toESM(require("react"), 1);
|
|
4604
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
4605
|
+
function HighlightedLabel({ item }) {
|
|
4606
|
+
if (item.labelIndices.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_jsx_runtime19.Fragment, { children: item.label });
|
|
4607
|
+
const marked = new Set(item.labelIndices);
|
|
4608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_jsx_runtime19.Fragment, { children: Array.from(item.label).map(
|
|
4609
|
+
(ch, i) => marked.has(i) ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-semibold text-foreground", "data-match": "", children: ch }, i) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react33.default.Fragment, { children: ch }, i)
|
|
4610
|
+
) });
|
|
4611
|
+
}
|
|
4612
|
+
var CommandPalette = ({
|
|
4613
|
+
palette,
|
|
4614
|
+
placeholder = "Type a command\u2026",
|
|
4615
|
+
emptyMessage = "No matching commands",
|
|
4616
|
+
className = ""
|
|
4617
|
+
}) => {
|
|
4618
|
+
const inputRef = (0, import_react33.useRef)(null);
|
|
4619
|
+
const listRef = (0, import_react33.useRef)(null);
|
|
4620
|
+
const baseId = (0, import_react33.useId)();
|
|
4621
|
+
const listId = `${baseId}-list`;
|
|
4622
|
+
const optionId = (index) => `${baseId}-option-${index}`;
|
|
4623
|
+
const { open, query, items, activeIndex } = palette;
|
|
4624
|
+
(0, import_react33.useEffect)(() => {
|
|
4625
|
+
if (open) inputRef.current?.focus();
|
|
4626
|
+
}, [open]);
|
|
4627
|
+
(0, import_react33.useEffect)(() => {
|
|
4628
|
+
if (!open) return;
|
|
4629
|
+
const active = listRef.current?.querySelector('[aria-selected="true"]');
|
|
4630
|
+
active?.scrollIntoView?.({ block: "nearest" });
|
|
4631
|
+
}, [open, activeIndex]);
|
|
4632
|
+
if (!open) return null;
|
|
4633
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4634
|
+
"div",
|
|
4635
|
+
{
|
|
4636
|
+
className: "fixed inset-0 z-50 flex items-start justify-center bg-black/30 px-4 pt-[15vh]",
|
|
4637
|
+
"data-command-palette-backdrop": "",
|
|
4638
|
+
onMouseDown: (e) => {
|
|
4639
|
+
if (e.target === e.currentTarget) palette.setOpen(false);
|
|
4640
|
+
},
|
|
4641
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4642
|
+
"div",
|
|
4643
|
+
{
|
|
4644
|
+
role: "dialog",
|
|
4645
|
+
"aria-modal": "true",
|
|
4646
|
+
"aria-label": "Command palette",
|
|
4647
|
+
className: `w-full max-w-lg overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-xl ${className}`,
|
|
4648
|
+
style: { background: "var(--popover, var(--background, #fff))" },
|
|
4649
|
+
children: [
|
|
4650
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
4651
|
+
"input",
|
|
4652
|
+
{
|
|
4653
|
+
ref: inputRef,
|
|
4654
|
+
type: "text",
|
|
4655
|
+
role: "combobox",
|
|
4656
|
+
"aria-expanded": "true",
|
|
4657
|
+
"aria-controls": listId,
|
|
4658
|
+
"aria-autocomplete": "list",
|
|
4659
|
+
"aria-activedescendant": items.length ? optionId(activeIndex) : void 0,
|
|
4660
|
+
"aria-label": "Search commands",
|
|
4661
|
+
placeholder,
|
|
4662
|
+
value: query,
|
|
4663
|
+
onChange: (e) => palette.setQuery(e.target.value),
|
|
4664
|
+
onKeyDown: (e) => {
|
|
4665
|
+
if (palette.handleKeyDown(e.nativeEvent)) e.stopPropagation();
|
|
4666
|
+
},
|
|
4667
|
+
className: "w-full border-b border-border bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
|
|
4668
|
+
}
|
|
4669
|
+
),
|
|
4670
|
+
items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { ref: listRef, id: listId, role: "listbox", "aria-label": "Commands", className: "max-h-80 overflow-y-auto py-1", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
4671
|
+
"li",
|
|
4672
|
+
{
|
|
4673
|
+
id: optionId(index),
|
|
4674
|
+
role: "option",
|
|
4675
|
+
"aria-selected": index === activeIndex,
|
|
4676
|
+
"aria-disabled": !item.enabled || void 0,
|
|
4677
|
+
"data-command-id": item.id,
|
|
4678
|
+
"data-recent": item.recent ? "" : void 0,
|
|
4679
|
+
title: item.disabledReason,
|
|
4680
|
+
onMouseMove: () => index !== activeIndex && palette.setActiveIndex(index),
|
|
4681
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4682
|
+
onClick: () => palette.run(item.id),
|
|
4683
|
+
className: `flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm ${index === activeIndex ? "bg-muted" : ""} ${item.enabled ? "" : "cursor-default opacity-50"}`,
|
|
4684
|
+
children: [
|
|
4685
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
|
|
4686
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(HighlightedLabel, { item }),
|
|
4687
|
+
item.disabledReason && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ml-2 text-xs text-muted-foreground", "data-disabled-reason": "", children: item.disabledReason })
|
|
4688
|
+
] }),
|
|
4689
|
+
item.group && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.group }),
|
|
4690
|
+
item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("kbd", { className: "shrink-0 rounded border border-border px-1.5 font-mono text-[10px] text-muted-foreground", children: item.shortcut })
|
|
4691
|
+
]
|
|
4692
|
+
},
|
|
4693
|
+
item.id
|
|
4694
|
+
)) })
|
|
4695
|
+
]
|
|
4696
|
+
}
|
|
4697
|
+
)
|
|
4698
|
+
}
|
|
4699
|
+
);
|
|
4700
|
+
};
|
|
4701
|
+
|
|
4702
|
+
// src/commands/useCommandShortcuts.ts
|
|
4703
|
+
var import_react34 = require("react");
|
|
4704
|
+
function useCommandShortcuts(registry, options = {}) {
|
|
4705
|
+
const optionsRef = (0, import_react34.useRef)(options);
|
|
4706
|
+
optionsRef.current = options;
|
|
4707
|
+
const { enabled = true, target } = options;
|
|
4708
|
+
(0, import_react34.useEffect)(() => {
|
|
4709
|
+
if (!registry || !enabled) return;
|
|
4710
|
+
const eventTarget = target ?? (typeof window !== "undefined" ? window : null);
|
|
4711
|
+
if (!eventTarget) return;
|
|
4712
|
+
const onKeyDown = (event) => {
|
|
4713
|
+
const { scopes, context, allowInEditable, onRun } = optionsRef.current;
|
|
4714
|
+
const result = registry.handleKeyDown(event, {
|
|
4715
|
+
scopes,
|
|
4716
|
+
...allowInEditable !== void 0 ? { allowInEditable } : {},
|
|
4717
|
+
...context !== void 0 ? { context } : {}
|
|
4718
|
+
});
|
|
4719
|
+
if (result) onRun?.(result);
|
|
4720
|
+
};
|
|
4721
|
+
eventTarget.addEventListener("keydown", onKeyDown);
|
|
4722
|
+
return () => eventTarget.removeEventListener("keydown", onKeyDown);
|
|
4723
|
+
}, [registry, enabled, target]);
|
|
4724
|
+
}
|
|
4725
|
+
function useRegisterCommands(registry, specs) {
|
|
4726
|
+
(0, import_react34.useEffect)(() => {
|
|
4727
|
+
if (!registry || !specs || specs.length === 0) return;
|
|
4728
|
+
return registry.register(specs);
|
|
4729
|
+
}, [registry, specs]);
|
|
4730
|
+
}
|
|
4731
|
+
|
|
4732
|
+
// src/persistence/useAutosave.ts
|
|
4733
|
+
var import_react35 = require("react");
|
|
4734
|
+
var import_persistence = require("@pixerate/editor/persistence");
|
|
4735
|
+
var IDLE = {
|
|
4736
|
+
status: "idle",
|
|
4737
|
+
error: null,
|
|
4738
|
+
failures: 0,
|
|
4739
|
+
nextAttemptAt: null,
|
|
4740
|
+
lastSavedAt: null,
|
|
4741
|
+
ready: false
|
|
4742
|
+
};
|
|
4743
|
+
function useAutosave(options) {
|
|
4744
|
+
const optionsRef = (0, import_react35.useRef)(options);
|
|
4745
|
+
optionsRef.current = options;
|
|
4746
|
+
const { storage, key, history, dirty } = options;
|
|
4747
|
+
const recover = options.recover !== false;
|
|
4748
|
+
const flushOnHide = options.flushOnHide !== false;
|
|
4749
|
+
const ready = options.ready !== false;
|
|
4750
|
+
const [controller, setController] = (0, import_react35.useState)(null);
|
|
4751
|
+
const [status, setStatus] = (0, import_react35.useState)(IDLE);
|
|
4752
|
+
const [recovery, setRecovery] = (0, import_react35.useState)(null);
|
|
4753
|
+
const [pendingRecovery, setPendingRecovery] = (0, import_react35.useState)(recover);
|
|
4754
|
+
const revisionsRef = (0, import_react35.useRef)(null);
|
|
4755
|
+
(0, import_react35.useEffect)(() => {
|
|
4756
|
+
const revisions = (0, import_persistence.createRevisionTracker)({ history, dirty });
|
|
4757
|
+
const o = optionsRef.current;
|
|
4758
|
+
const instance = (0, import_persistence.createAutosaveController)({
|
|
4759
|
+
storage,
|
|
4760
|
+
key,
|
|
4761
|
+
getSnapshot: () => optionsRef.current.getSnapshot(),
|
|
4762
|
+
serialize: o.serialize,
|
|
4763
|
+
deserialize: o.deserialize,
|
|
4764
|
+
name: () => optionsRef.current.name,
|
|
4765
|
+
revision: revisions.revision,
|
|
4766
|
+
savedRevision: revisions.savedRevision,
|
|
4767
|
+
interval: o.interval,
|
|
4768
|
+
debounceMs: o.debounceMs,
|
|
4769
|
+
retryBaseMs: o.retryBaseMs,
|
|
4770
|
+
retryMaxMs: o.retryMaxMs
|
|
4771
|
+
});
|
|
4772
|
+
const unsubscribeStatus = instance.subscribe(setStatus);
|
|
4773
|
+
const unsubscribeRevisions = revisions.subscribe(() => instance.notify());
|
|
4774
|
+
revisionsRef.current = revisions;
|
|
4775
|
+
setController(instance);
|
|
4776
|
+
setStatus(instance.getStatus());
|
|
4777
|
+
let active = true;
|
|
4778
|
+
if (recover) {
|
|
4779
|
+
setPendingRecovery(true);
|
|
4780
|
+
instance.checkRecovery().catch(() => null).then((found) => {
|
|
4781
|
+
if (!active) return;
|
|
4782
|
+
setRecovery(found);
|
|
4783
|
+
setPendingRecovery(found !== null);
|
|
4784
|
+
});
|
|
4785
|
+
} else {
|
|
4786
|
+
setRecovery(null);
|
|
4787
|
+
setPendingRecovery(false);
|
|
4788
|
+
}
|
|
4789
|
+
return () => {
|
|
4790
|
+
active = false;
|
|
4791
|
+
unsubscribeStatus();
|
|
4792
|
+
unsubscribeRevisions();
|
|
4793
|
+
revisions.destroy();
|
|
4794
|
+
instance.destroy();
|
|
4795
|
+
if (revisionsRef.current === revisions) revisionsRef.current = null;
|
|
4796
|
+
};
|
|
4797
|
+
}, [storage, key, history, dirty, recover]);
|
|
4798
|
+
(0, import_react35.useEffect)(() => {
|
|
4799
|
+
if (controller && ready && !pendingRecovery) controller.ready();
|
|
4800
|
+
}, [controller, ready, pendingRecovery]);
|
|
4801
|
+
(0, import_react35.useEffect)(() => {
|
|
4802
|
+
if (!controller || !flushOnHide || typeof window === "undefined") return;
|
|
4803
|
+
const flush = () => void controller.flushNow();
|
|
4804
|
+
const onVisibility = () => {
|
|
4805
|
+
if (document.visibilityState === "hidden") flush();
|
|
4806
|
+
};
|
|
4807
|
+
window.addEventListener("pagehide", flush);
|
|
4808
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
4809
|
+
return () => {
|
|
4810
|
+
window.removeEventListener("pagehide", flush);
|
|
4811
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
4812
|
+
};
|
|
4813
|
+
}, [controller, flushOnHide]);
|
|
4814
|
+
const restore = (0, import_react35.useCallback)(() => {
|
|
4815
|
+
const snapshot = recovery?.snapshot;
|
|
4816
|
+
setRecovery(null);
|
|
4817
|
+
setPendingRecovery(false);
|
|
4818
|
+
return snapshot;
|
|
4819
|
+
}, [recovery]);
|
|
4820
|
+
const discard = (0, import_react35.useCallback)(async () => {
|
|
4821
|
+
if (controller) await controller.discardRecovery();
|
|
4822
|
+
setRecovery(null);
|
|
4823
|
+
setPendingRecovery(false);
|
|
4824
|
+
}, [controller]);
|
|
4825
|
+
const flushNow = (0, import_react35.useCallback)(async () => {
|
|
4826
|
+
await controller?.flushNow();
|
|
4827
|
+
}, [controller]);
|
|
4828
|
+
const markClean = (0, import_react35.useCallback)(async () => {
|
|
4829
|
+
revisionsRef.current?.markSaved();
|
|
4830
|
+
await controller?.markClean();
|
|
4831
|
+
}, [controller]);
|
|
4832
|
+
const notifyChange = (0, import_react35.useCallback)(() => revisionsRef.current?.bump(), []);
|
|
4833
|
+
return { status, recovery, pendingRecovery, restore, discard, flushNow, markClean, notifyChange, controller };
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
// src/persistence/RecoveryPrompt.tsx
|
|
4837
|
+
var import_persistence2 = require("@pixerate/editor/persistence");
|
|
4838
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
4839
|
+
var RecoveryPrompt = ({
|
|
4840
|
+
recovery,
|
|
4841
|
+
onRestore,
|
|
4842
|
+
onDiscard,
|
|
4843
|
+
formatTime,
|
|
4844
|
+
restoreLabel = "Restore",
|
|
4845
|
+
discardLabel = "Discard",
|
|
4846
|
+
className = ""
|
|
4847
|
+
}) => {
|
|
4848
|
+
if (!recovery) return null;
|
|
4849
|
+
const { name } = recovery.meta;
|
|
4850
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
4851
|
+
"div",
|
|
4852
|
+
{
|
|
4853
|
+
role: "alertdialog",
|
|
4854
|
+
"aria-label": "Unsaved changes found",
|
|
4855
|
+
"data-recovery-prompt": "",
|
|
4856
|
+
className: `flex flex-wrap items-center gap-3 rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground shadow-sm ${className}`,
|
|
4857
|
+
children: [
|
|
4858
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "min-w-0 flex-1", children: [
|
|
4859
|
+
(0, import_persistence2.formatRecoveryMessage)(recovery.meta, formatTime),
|
|
4860
|
+
name ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "ml-1 text-xs text-muted-foreground", children: [
|
|
4861
|
+
"(",
|
|
4862
|
+
name,
|
|
4863
|
+
")"
|
|
4864
|
+
] }) : null
|
|
4865
|
+
] }),
|
|
4866
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4867
|
+
"button",
|
|
4868
|
+
{
|
|
4869
|
+
type: "button",
|
|
4870
|
+
"data-recovery-restore": "",
|
|
4871
|
+
onClick: onRestore,
|
|
4872
|
+
className: "rounded-md bg-primary px-3 py-1 text-xs font-medium text-primary-foreground hover:opacity-90",
|
|
4873
|
+
children: restoreLabel
|
|
4874
|
+
}
|
|
4875
|
+
),
|
|
4876
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4877
|
+
"button",
|
|
4878
|
+
{
|
|
4879
|
+
type: "button",
|
|
4880
|
+
"data-recovery-discard": "",
|
|
4881
|
+
onClick: onDiscard,
|
|
4882
|
+
className: "rounded-md border border-border px-3 py-1 text-xs font-medium hover:bg-muted",
|
|
4883
|
+
children: discardLabel
|
|
4884
|
+
}
|
|
4885
|
+
)
|
|
4886
|
+
]
|
|
4887
|
+
}
|
|
4888
|
+
);
|
|
4889
|
+
};
|
|
4890
|
+
|
|
4891
|
+
// src/generation/useGenerationPlan.ts
|
|
4892
|
+
var import_react36 = require("react");
|
|
4893
|
+
var import_generation = require("@pixerate/editor/generation");
|
|
4894
|
+
function useGenerationPlan(request, capabilities, options = {}) {
|
|
4895
|
+
const key = JSON.stringify([request, capabilities, options]);
|
|
4896
|
+
return (0, import_react36.useMemo)(() => (0, import_generation.planGenerationRequest)(request, capabilities, options), [key]);
|
|
4897
|
+
}
|
|
4898
|
+
function useJobProgress(options) {
|
|
4899
|
+
const { startedAt, expectedMs, done, now, intervalMs = 1e3 } = options;
|
|
4900
|
+
const [tick, setTick] = (0, import_react36.useState)(() => Date.now());
|
|
4901
|
+
const ticking = !done && now === void 0;
|
|
4902
|
+
(0, import_react36.useEffect)(() => {
|
|
4903
|
+
if (!ticking) return;
|
|
4904
|
+
setTick(Date.now());
|
|
4905
|
+
const id = setInterval(() => setTick(Date.now()), Math.max(16, intervalMs));
|
|
4906
|
+
return () => clearInterval(id);
|
|
4907
|
+
}, [ticking, intervalMs]);
|
|
4908
|
+
return (0, import_generation.estimateJobProgress)({ startedAt, expectedMs, done, now: now ?? tick });
|
|
4909
|
+
}
|
|
4910
|
+
|
|
4911
|
+
// src/generation/GenerationSettings.tsx
|
|
4912
|
+
var import_react37 = require("react");
|
|
4913
|
+
var import_generation2 = require("@pixerate/editor/generation");
|
|
4914
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
4915
|
+
var inputClass = "w-full rounded-md border border-border bg-background px-2 py-1 text-sm outline-none focus:ring-1 focus:ring-ring";
|
|
4916
|
+
function Control({
|
|
4917
|
+
control,
|
|
4918
|
+
value,
|
|
4919
|
+
id,
|
|
4920
|
+
onValue
|
|
4921
|
+
}) {
|
|
4922
|
+
const shown = value ?? control.defaultValue;
|
|
4923
|
+
if (control.kind === "checkbox") {
|
|
4924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4925
|
+
"input",
|
|
4926
|
+
{
|
|
4927
|
+
id,
|
|
4928
|
+
type: "checkbox",
|
|
4929
|
+
"data-field": control.key,
|
|
4930
|
+
checked: Boolean(shown),
|
|
4931
|
+
onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.checked))
|
|
4932
|
+
}
|
|
4933
|
+
);
|
|
4934
|
+
}
|
|
4935
|
+
if (control.kind === "select") {
|
|
4936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4937
|
+
"select",
|
|
4938
|
+
{
|
|
4939
|
+
id,
|
|
4940
|
+
"data-field": control.key,
|
|
4941
|
+
className: inputClass,
|
|
4942
|
+
value: shown === void 0 ? "" : String(shown),
|
|
4943
|
+
onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value)),
|
|
4944
|
+
children: [
|
|
4945
|
+
shown === void 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "", children: "Default" }),
|
|
4946
|
+
control.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: String(option.value), children: option.label }, String(option.value)))
|
|
4947
|
+
]
|
|
4948
|
+
}
|
|
4949
|
+
);
|
|
4950
|
+
}
|
|
4951
|
+
if (control.kind === "textarea") {
|
|
4952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4953
|
+
"textarea",
|
|
4954
|
+
{
|
|
4955
|
+
id,
|
|
4956
|
+
"data-field": control.key,
|
|
4957
|
+
className: inputClass,
|
|
4958
|
+
rows: 2,
|
|
4959
|
+
maxLength: control.maxLength,
|
|
4960
|
+
value: typeof value === "string" ? value : "",
|
|
4961
|
+
onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value))
|
|
4962
|
+
}
|
|
4963
|
+
);
|
|
4964
|
+
}
|
|
4965
|
+
const numeric = typeof shown === "number" ? shown : void 0;
|
|
4966
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4967
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4968
|
+
"input",
|
|
4969
|
+
{
|
|
4970
|
+
id,
|
|
4971
|
+
type: control.kind === "range" ? "range" : "number",
|
|
4972
|
+
"data-field": control.key,
|
|
4973
|
+
className: control.kind === "range" ? "flex-1" : inputClass,
|
|
4974
|
+
min: control.min,
|
|
4975
|
+
max: control.max,
|
|
4976
|
+
step: control.step ?? "any",
|
|
4977
|
+
value: numeric ?? (control.kind === "range" ? control.min ?? 0 : ""),
|
|
4978
|
+
onChange: (e) => onValue((0, import_generation2.parseControlValue)(control, e.target.value))
|
|
4979
|
+
}
|
|
4980
|
+
),
|
|
4981
|
+
control.kind === "range" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-10 text-right tabular-nums text-muted-foreground", "data-field-value": "", children: numeric ?? "\u2013" })
|
|
4982
|
+
] });
|
|
4983
|
+
}
|
|
4984
|
+
var GenerationSettings = ({
|
|
4985
|
+
capabilities,
|
|
4986
|
+
value,
|
|
4987
|
+
onChange,
|
|
4988
|
+
plan: planProp,
|
|
4989
|
+
strategy,
|
|
4990
|
+
showAdjustments = true,
|
|
4991
|
+
onAddReference,
|
|
4992
|
+
renderReference,
|
|
4993
|
+
className = ""
|
|
4994
|
+
}) => {
|
|
4995
|
+
const baseId = (0, import_react37.useId)();
|
|
4996
|
+
const controls = (0, import_react37.useMemo)(() => (0, import_generation2.getGenerationControls)(capabilities), [capabilities]);
|
|
4997
|
+
const computed = useGenerationPlan(value, capabilities, { strategy });
|
|
4998
|
+
const plan = planProp ?? computed;
|
|
4999
|
+
const slots = (0, import_generation2.getReferenceSlots)(capabilities, value);
|
|
5000
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `flex flex-col gap-3 text-sm ${className}`, "data-generation-settings": "", "data-model": capabilities.id, children: [
|
|
5001
|
+
controls.map((control) => {
|
|
5002
|
+
const id = `${baseId}-${control.key}`;
|
|
5003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-1", "data-control": control.key, children: [
|
|
5004
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { htmlFor: id, className: "text-xs font-medium text-muted-foreground", title: control.description, children: control.label }),
|
|
5005
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5006
|
+
Control,
|
|
5007
|
+
{
|
|
5008
|
+
control,
|
|
5009
|
+
id,
|
|
5010
|
+
value: (0, import_generation2.getRequestValue)(value, control.key),
|
|
5011
|
+
onValue: (next) => onChange((0, import_generation2.setRequestValue)(value, control.key, next))
|
|
5012
|
+
}
|
|
5013
|
+
)
|
|
5014
|
+
] }, control.key);
|
|
5015
|
+
}),
|
|
5016
|
+
slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("fieldset", { className: "flex flex-col gap-1", "data-reference-role": slot.role, children: [
|
|
5017
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("legend", { className: "text-xs font-medium text-muted-foreground", title: slot.description, children: [
|
|
5018
|
+
slot.label,
|
|
5019
|
+
" ",
|
|
5020
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "tabular-nums", "data-reference-count": "", children: [
|
|
5021
|
+
"(",
|
|
5022
|
+
slot.references.length,
|
|
5023
|
+
"/",
|
|
5024
|
+
slot.max,
|
|
5025
|
+
")"
|
|
5026
|
+
] }),
|
|
5027
|
+
slot.min > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-destructive", children: " *" })
|
|
5028
|
+
] }),
|
|
5029
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("ul", { className: "flex flex-wrap gap-1", children: [
|
|
5030
|
+
slot.references.map((ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-1 rounded border border-border px-1.5 py-0.5", "data-reference-id": ref.id, children: [
|
|
5031
|
+
renderReference ? renderReference(ref) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "max-w-[10rem] truncate", children: ref.id }),
|
|
5032
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5033
|
+
"button",
|
|
5034
|
+
{
|
|
5035
|
+
type: "button",
|
|
5036
|
+
"aria-label": `Remove ${slot.label} ${ref.id}`,
|
|
5037
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
5038
|
+
onClick: () => onChange((0, import_generation2.removeReference)(value, slot.role, ref.id)),
|
|
5039
|
+
children: "\xD7"
|
|
5040
|
+
}
|
|
5041
|
+
)
|
|
5042
|
+
] }, ref.id)),
|
|
5043
|
+
onAddReference && slot.canAdd && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
5044
|
+
"button",
|
|
5045
|
+
{
|
|
5046
|
+
type: "button",
|
|
5047
|
+
className: "rounded border border-dashed border-border px-1.5 py-0.5 text-muted-foreground hover:text-foreground",
|
|
5048
|
+
onClick: () => onAddReference(slot.role),
|
|
5049
|
+
children: "Add"
|
|
5050
|
+
}
|
|
5051
|
+
) })
|
|
5052
|
+
] })
|
|
5053
|
+
] }, slot.role)),
|
|
5054
|
+
showAdjustments && plan.adjustments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { role: "status", className: "flex flex-col gap-0.5 text-xs text-muted-foreground", "data-generation-adjustments": "", children: plan.adjustments.map((adjustment, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { "data-adjustment-field": adjustment.field, "data-adjustment-kind": adjustment.kind, children: adjustment.reason }, index)) }),
|
|
5055
|
+
showAdjustments && plan.errors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { role: "alert", className: "flex flex-col gap-0.5 text-xs text-destructive", "data-generation-errors": "", children: plan.errors.map((error, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { "data-error-field": error.field, children: error.message }, index)) })
|
|
5056
|
+
] });
|
|
5057
|
+
};
|
|
5058
|
+
|
|
5059
|
+
// src/generation/JobProgress.tsx
|
|
5060
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
5061
|
+
var JobProgress = ({ title, className = "", ...options }) => {
|
|
5062
|
+
const progress = useJobProgress(options);
|
|
5063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: `flex flex-col gap-1 text-sm ${className}`, "data-job-progress": "", "data-done": options.done ? "" : void 0, children: [
|
|
5064
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center justify-between gap-2 text-muted-foreground", children: [
|
|
5065
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-foreground", children: title }),
|
|
5066
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "data-job-progress-label": "", "aria-live": "polite", children: progress.label })
|
|
5067
|
+
] }),
|
|
5068
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5069
|
+
"div",
|
|
5070
|
+
{
|
|
5071
|
+
role: "progressbar",
|
|
5072
|
+
"aria-label": title ?? "Generation progress",
|
|
5073
|
+
"aria-valuemin": 0,
|
|
5074
|
+
"aria-valuemax": 100,
|
|
5075
|
+
"aria-valuenow": progress.percent,
|
|
5076
|
+
"aria-valuetext": `${progress.percent}%, ${progress.label}`,
|
|
5077
|
+
className: "h-1.5 w-full overflow-hidden rounded-full bg-muted",
|
|
5078
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5079
|
+
"div",
|
|
5080
|
+
{
|
|
5081
|
+
className: "h-full rounded-full bg-primary transition-[width] duration-500",
|
|
5082
|
+
style: { width: `${progress.percent}%`, background: "var(--primary, currentColor)" }
|
|
5083
|
+
}
|
|
5084
|
+
)
|
|
5085
|
+
}
|
|
5086
|
+
)
|
|
5087
|
+
] });
|
|
5088
|
+
};
|
|
5089
|
+
|
|
5090
|
+
// src/find-replace/useFindReplace.ts
|
|
5091
|
+
var import_react38 = require("react");
|
|
5092
|
+
var import_editor14 = require("@pixerate/editor");
|
|
5093
|
+
var noopUnsubscribe2 = () => {
|
|
5094
|
+
};
|
|
5095
|
+
function useFindReplace(editor) {
|
|
5096
|
+
const store = (0, import_editor14.getFindReplaceStore)(editor ?? null);
|
|
5097
|
+
const subscribe = (0, import_react38.useCallback)(
|
|
5098
|
+
(onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe2,
|
|
5099
|
+
[store]
|
|
5100
|
+
);
|
|
5101
|
+
const getSnapshot = () => store?.getSnapshot() ?? import_editor14.EMPTY_FIND_REPLACE_SNAPSHOT;
|
|
5102
|
+
const snapshot = (0, import_react38.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
5103
|
+
return (0, import_react38.useMemo)(() => {
|
|
5104
|
+
const run = (fn) => {
|
|
5105
|
+
if (editor && !editor.isDestroyed && store) fn(editor);
|
|
5106
|
+
};
|
|
5107
|
+
return {
|
|
5108
|
+
...snapshot,
|
|
5109
|
+
available: !!store,
|
|
5110
|
+
matchCount: snapshot.matches.length,
|
|
5111
|
+
show: (mode = "find") => run((e) => e.commands.openFindReplace({ mode })),
|
|
5112
|
+
close: () => run((e) => e.commands.closeFindReplace()),
|
|
5113
|
+
setQuery: (query) => run((e) => e.commands.setSearch({ query })),
|
|
5114
|
+
setReplacement: (replacement) => run((e) => e.commands.setSearch({ replacement })),
|
|
5115
|
+
setOptions: (options) => run((e) => e.commands.setSearch({ options })),
|
|
5116
|
+
toggleOption: (option) => run((e) => e.commands.setSearch({ options: { [option]: !snapshot.options[option] } })),
|
|
5117
|
+
next: () => run((e) => e.commands.findNext()),
|
|
5118
|
+
previous: () => run((e) => e.commands.findPrevious()),
|
|
5119
|
+
replace: () => run((e) => e.commands.replaceCurrent()),
|
|
5120
|
+
replaceAll: () => run((e) => e.commands.replaceAll()),
|
|
5121
|
+
clear: () => run((e) => e.commands.clearSearch())
|
|
5122
|
+
};
|
|
5123
|
+
}, [snapshot, editor, store]);
|
|
5124
|
+
}
|
|
5125
|
+
|
|
5126
|
+
// src/find-replace/FindReplaceBar.tsx
|
|
5127
|
+
var import_react39 = require("react");
|
|
5128
|
+
var import_editor15 = require("@pixerate/editor");
|
|
5129
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
5130
|
+
var inputClass2 = "min-w-0 flex-1 rounded border border-input bg-background px-2 py-1 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-ring";
|
|
5131
|
+
var buttonClass3 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
|
|
5132
|
+
var toggleClass = (on) => `${buttonClass3} font-mono ${on ? "bg-primary/10 text-primary" : "text-muted-foreground"}`;
|
|
5133
|
+
var isMod = (e) => e.metaKey || e.ctrlKey;
|
|
5134
|
+
var FindReplaceBar = ({
|
|
5135
|
+
editor,
|
|
5136
|
+
className = "flex flex-col gap-1 rounded-md border border-border bg-background p-1 text-sm shadow-sm",
|
|
5137
|
+
alwaysOpen = false,
|
|
5138
|
+
showMoreOptions = true,
|
|
5139
|
+
onClose
|
|
5140
|
+
}) => {
|
|
5141
|
+
const find = useFindReplace(editor);
|
|
5142
|
+
const findInput = (0, import_react39.useRef)(null);
|
|
5143
|
+
const [moreOpen, setMoreOpen] = (0, import_react39.useState)(false);
|
|
5144
|
+
const visible = find.available && (alwaysOpen || find.open);
|
|
5145
|
+
(0, import_react39.useEffect)(() => {
|
|
5146
|
+
if (!visible || find.focusRequest === 0) return;
|
|
5147
|
+
findInput.current?.focus();
|
|
5148
|
+
findInput.current?.select();
|
|
5149
|
+
}, [visible, find.focusRequest]);
|
|
5150
|
+
if (!visible) return null;
|
|
5151
|
+
const showReplace = find.mode === "replace";
|
|
5152
|
+
const status = (0, import_editor15.formatFindStatus)(find);
|
|
5153
|
+
const hasMatches = find.matchCount > 0;
|
|
5154
|
+
const close = () => {
|
|
5155
|
+
find.close();
|
|
5156
|
+
editor?.commands.focus();
|
|
5157
|
+
onClose?.();
|
|
5158
|
+
};
|
|
5159
|
+
const onBarKeyDown = (e) => {
|
|
5160
|
+
if (e.key === "Escape") {
|
|
5161
|
+
e.preventDefault();
|
|
5162
|
+
close();
|
|
5163
|
+
} else if (isMod(e) && (e.key === "f" || e.key === "F")) {
|
|
5164
|
+
e.preventDefault();
|
|
5165
|
+
find.show("find");
|
|
5166
|
+
} else if (isMod(e) && (e.key === "h" || e.key === "H")) {
|
|
5167
|
+
e.preventDefault();
|
|
5168
|
+
find.show("replace");
|
|
5169
|
+
}
|
|
5170
|
+
};
|
|
5171
|
+
const onFindKeyDown = (e) => {
|
|
5172
|
+
if (e.key !== "Enter") return;
|
|
5173
|
+
e.preventDefault();
|
|
5174
|
+
if (e.shiftKey) find.previous();
|
|
5175
|
+
else find.next();
|
|
5176
|
+
};
|
|
5177
|
+
const onReplaceKeyDown = (e) => {
|
|
5178
|
+
if (e.key !== "Enter") return;
|
|
5179
|
+
e.preventDefault();
|
|
5180
|
+
if (isMod(e)) find.replaceAll();
|
|
5181
|
+
else find.replace();
|
|
5182
|
+
};
|
|
5183
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className, role: "search", "aria-label": "Find and replace", onKeyDown: onBarKeyDown, children: [
|
|
5184
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
5185
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5186
|
+
"button",
|
|
5187
|
+
{
|
|
5188
|
+
type: "button",
|
|
5189
|
+
className: buttonClass3,
|
|
5190
|
+
"aria-label": "Toggle replace",
|
|
5191
|
+
"aria-expanded": showReplace,
|
|
5192
|
+
onClick: () => find.show(showReplace ? "find" : "replace"),
|
|
5193
|
+
children: showReplace ? "\u25BE" : "\u25B8"
|
|
5194
|
+
}
|
|
5195
|
+
),
|
|
5196
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5197
|
+
"input",
|
|
5198
|
+
{
|
|
5199
|
+
ref: findInput,
|
|
5200
|
+
type: "text",
|
|
5201
|
+
className: inputClass2,
|
|
5202
|
+
placeholder: "Find",
|
|
5203
|
+
"aria-label": "Find",
|
|
5204
|
+
"aria-invalid": !!find.error,
|
|
5205
|
+
value: find.query,
|
|
5206
|
+
onChange: (e) => find.setQuery(e.target.value),
|
|
5207
|
+
onKeyDown: onFindKeyDown
|
|
5208
|
+
}
|
|
5209
|
+
),
|
|
5210
|
+
import_editor15.FIND_OPTION_TOGGLES.filter((t) => !t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5211
|
+
"button",
|
|
5212
|
+
{
|
|
5213
|
+
type: "button",
|
|
5214
|
+
className: toggleClass(!!find.options[t.key]),
|
|
5215
|
+
"aria-label": t.label,
|
|
5216
|
+
title: t.label,
|
|
5217
|
+
"aria-pressed": !!find.options[t.key],
|
|
5218
|
+
onClick: () => find.toggleOption(t.key),
|
|
5219
|
+
children: t.short
|
|
5220
|
+
},
|
|
5221
|
+
t.key
|
|
5222
|
+
)),
|
|
5223
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5224
|
+
"span",
|
|
5225
|
+
{
|
|
5226
|
+
className: `min-w-[5rem] px-1 text-center text-xs ${find.error ? "text-destructive" : "text-muted-foreground"}`,
|
|
5227
|
+
"aria-live": "polite",
|
|
5228
|
+
role: find.error ? "alert" : "status",
|
|
5229
|
+
"data-testid": "find-status",
|
|
5230
|
+
children: status
|
|
5231
|
+
}
|
|
5232
|
+
),
|
|
5233
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Previous match", disabled: !hasMatches, onClick: find.previous, children: "\u2191" }),
|
|
5234
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Next match", disabled: !hasMatches, onClick: find.next, children: "\u2193" }),
|
|
5235
|
+
showMoreOptions && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5236
|
+
"button",
|
|
5237
|
+
{
|
|
5238
|
+
type: "button",
|
|
5239
|
+
className: buttonClass3,
|
|
5240
|
+
"aria-label": "More options",
|
|
5241
|
+
"aria-expanded": moreOpen,
|
|
5242
|
+
onClick: () => setMoreOpen((v) => !v),
|
|
5243
|
+
children: "\u2026"
|
|
5244
|
+
}
|
|
5245
|
+
),
|
|
5246
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, "aria-label": "Close find", onClick: close, children: "\xD7" })
|
|
5247
|
+
] }),
|
|
5248
|
+
showReplace && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1 pl-7", children: [
|
|
5249
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5250
|
+
"input",
|
|
5251
|
+
{
|
|
5252
|
+
type: "text",
|
|
5253
|
+
className: inputClass2,
|
|
5254
|
+
placeholder: "Replace",
|
|
5255
|
+
"aria-label": "Replace",
|
|
5256
|
+
value: find.replacement,
|
|
5257
|
+
onChange: (e) => find.setReplacement(e.target.value),
|
|
5258
|
+
onKeyDown: onReplaceKeyDown
|
|
5259
|
+
}
|
|
5260
|
+
),
|
|
5261
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5262
|
+
"button",
|
|
5263
|
+
{
|
|
5264
|
+
type: "button",
|
|
5265
|
+
className: buttonClass3,
|
|
5266
|
+
disabled: !hasMatches || find.currentIndex < 0,
|
|
5267
|
+
onClick: find.replace,
|
|
5268
|
+
children: "Replace"
|
|
5269
|
+
}
|
|
5270
|
+
),
|
|
5271
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: buttonClass3, disabled: !hasMatches, onClick: find.replaceAll, children: "Replace all" }),
|
|
5272
|
+
find.lastReplaceCount !== null && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "px-1 text-xs text-muted-foreground", "aria-live": "polite", children: find.lastReplaceCount === 1 ? "1 replaced" : `${find.lastReplaceCount} replaced` })
|
|
5273
|
+
] }),
|
|
5274
|
+
showMoreOptions && moreOpen && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 pl-7 text-xs text-muted-foreground", children: import_editor15.FIND_OPTION_TOGGLES.filter((t) => t.advanced).map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex items-center gap-1", children: [
|
|
5275
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
5276
|
+
"input",
|
|
5277
|
+
{
|
|
5278
|
+
type: "checkbox",
|
|
5279
|
+
checked: !!find.options[t.key],
|
|
5280
|
+
onChange: () => find.toggleOption(t.key)
|
|
5281
|
+
}
|
|
5282
|
+
),
|
|
5283
|
+
t.label
|
|
5284
|
+
] }, t.key)) })
|
|
5285
|
+
] });
|
|
5286
|
+
};
|
|
3793
5287
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3794
5288
|
0 && (module.exports = {
|
|
5289
|
+
AdjustmentLayersPanel,
|
|
3795
5290
|
BubbleMenu,
|
|
5291
|
+
CommandPalette,
|
|
3796
5292
|
CommentButton,
|
|
3797
5293
|
CommentsPanel,
|
|
5294
|
+
ControlSlider,
|
|
5295
|
+
CurvesEditor,
|
|
3798
5296
|
DirtyTracker,
|
|
3799
5297
|
EditorContent,
|
|
3800
5298
|
EditorSlotsProvider,
|
|
5299
|
+
FindReplaceBar,
|
|
3801
5300
|
FormulaHints,
|
|
5301
|
+
GenerationSettings,
|
|
5302
|
+
Histogram,
|
|
3802
5303
|
HistoryManager,
|
|
3803
5304
|
ImageEditor,
|
|
5305
|
+
JobProgress,
|
|
5306
|
+
MergePreview,
|
|
3804
5307
|
OutlinePanel,
|
|
5308
|
+
RecoveryPrompt,
|
|
3805
5309
|
ReviewToolbar,
|
|
3806
5310
|
SpreadsheetContextMenu,
|
|
3807
5311
|
SpreadsheetEditor,
|
|
3808
5312
|
SpreadsheetGroupRow,
|
|
3809
5313
|
TemplateRenderer,
|
|
3810
5314
|
VariableForm,
|
|
5315
|
+
bindDirtyToHistory,
|
|
3811
5316
|
createDirtyTracker,
|
|
3812
5317
|
createHistoryManager,
|
|
3813
5318
|
defaultIsEqual,
|
|
3814
5319
|
useAgentToolProvider,
|
|
5320
|
+
useAutosave,
|
|
5321
|
+
useCommandPalette,
|
|
5322
|
+
useCommandShortcuts,
|
|
3815
5323
|
useComments,
|
|
3816
5324
|
useDismissGuard,
|
|
5325
|
+
useDocumentCompare,
|
|
3817
5326
|
useEditor,
|
|
3818
5327
|
useEditorSlots,
|
|
3819
5328
|
useFileImport,
|
|
3820
5329
|
useFillHandle,
|
|
5330
|
+
useFindReplace,
|
|
5331
|
+
useGenerationPlan,
|
|
3821
5332
|
useHistory,
|
|
5333
|
+
useHistoryDirty,
|
|
3822
5334
|
useHistoryShortcuts,
|
|
3823
5335
|
useImageEditor,
|
|
5336
|
+
useImageHistogram,
|
|
5337
|
+
useJobProgress,
|
|
5338
|
+
useMergePreview,
|
|
3824
5339
|
useNavigationGuard,
|
|
3825
5340
|
useOutline,
|
|
3826
5341
|
usePromptEditor,
|
|
5342
|
+
useRegisterCommands,
|
|
3827
5343
|
useSpreadsheetClipboard,
|
|
3828
5344
|
useSpreadsheetEditor,
|
|
3829
|
-
useTrackChanges
|
|
5345
|
+
useTrackChanges,
|
|
5346
|
+
withoutDirty
|
|
3830
5347
|
});
|
|
3831
5348
|
//# sourceMappingURL=index.cjs.map
|