@alpaca-editor/core 1.0.4094 → 1.0.4096
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/dist/editor/ai/AgentTerminal.js +151 -58
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.d.ts +11 -0
- package/dist/editor/ai/ContextInfoBar.js +347 -0
- package/dist/editor/ai/ContextInfoBar.js.map +1 -0
- package/dist/editor/ai/DancingDots.js +1 -1
- package/dist/editor/ai/DancingDots.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +5 -2
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/control-center/setup-steps/{AiSetupStep.d.ts → AiSetupStep/index.d.ts} +1 -0
- package/dist/editor/control-center/setup-steps/{AiSetupStep.js → AiSetupStep/index.js} +11 -17
- package/dist/editor/control-center/setup-steps/AiSetupStep/index.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +15 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js +10 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +15 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +14 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +1 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.d.ts +7 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +8 -0
- package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -0
- package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +102 -7
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +5 -2
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/package.json +1 -1
- package/src/editor/ai/AgentTerminal.tsx +181 -157
- package/src/editor/ai/ContextInfoBar.tsx +551 -0
- package/src/editor/ai/DancingDots.tsx +1 -1
- package/src/editor/client/itemsRepository.ts +5 -5
- package/src/editor/control-center/setup-steps/{AiSetupStep.tsx → AiSetupStep/index.tsx} +38 -131
- package/src/editor/control-center/setup-steps/AiSetupStep/provider/ProviderSection.tsx +74 -0
- package/src/editor/control-center/setup-steps/AiSetupStep/required-containers/RequiredContainersList.tsx +92 -0
- package/src/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.tsx +32 -0
- package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
- package/src/editor/page-editor-chrome/InlineEditor.tsx +129 -7
- package/src/page-wizard/steps/ContentStep.tsx +46 -0
- package/src/revision.ts +2 -2
- package/dist/editor/control-center/setup-steps/AiSetupStep.js.map +0 -1
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import React, { useCallback, useMemo, useState } from "react";
|
|
2
|
-
import { Card } from "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
AlertCircle,
|
|
15
|
-
} from "lucide-react";
|
|
16
|
-
import { contentItemId } from "../../../config/config";
|
|
17
|
-
|
|
18
|
-
type StepState = "idle" | "checking" | "success" | "error";
|
|
2
|
+
import { Card } from "../../../../components/ui/card";
|
|
3
|
+
import { useEditContext } from "../../../client/editContext";
|
|
4
|
+
import { ItemDescriptor } from "../../../pageModel";
|
|
5
|
+
import { getChildren } from "../../../services/contentService";
|
|
6
|
+
import type { ItemTreeNodeData } from "../../../services/contentService";
|
|
7
|
+
import { SparklesIcon } from "lucide-react";
|
|
8
|
+
import { contentItemId } from "../../../../config/config";
|
|
9
|
+
import { RequiredContainersList } from "./required-containers/RequiredContainersList";
|
|
10
|
+
import { ProviderSection } from "./provider/ProviderSection";
|
|
11
|
+
import { GenerateToolsSection } from "./tools/GenerateToolsSection";
|
|
12
|
+
|
|
13
|
+
export type StepState = "idle" | "checking" | "success" | "error";
|
|
19
14
|
|
|
20
15
|
function findByName(
|
|
21
16
|
items: ItemTreeNodeData[],
|
|
@@ -577,7 +572,7 @@ export function AiSetupStep() {
|
|
|
577
572
|
} finally {
|
|
578
573
|
setAiBusy(false);
|
|
579
574
|
}
|
|
580
|
-
}, [aiProvider, editContext, userLang, resolvePathUnderContent]);
|
|
575
|
+
}, [aiProvider, editContext, userLang, resolvePathUnderContent, apiKey]);
|
|
581
576
|
|
|
582
577
|
const checkRequiredContainers = useCallback(async () => {
|
|
583
578
|
try {
|
|
@@ -659,119 +654,31 @@ export function AiSetupStep() {
|
|
|
659
654
|
title="AI setup"
|
|
660
655
|
description="Add an AI endpoint (OpenAI, Azure OpenAI, or OpenRouter)."
|
|
661
656
|
>
|
|
662
|
-
<
|
|
663
|
-
{requiredContainers
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
<span className="text-sm text-gray-700">
|
|
688
|
-
{containerStates[rc.name] === "checking"
|
|
689
|
-
? `Checking ${rc.name}...`
|
|
690
|
-
: containerStates[rc.name] === "success"
|
|
691
|
-
? `${rc.name} present`
|
|
692
|
-
: `${rc.name} missing`}
|
|
693
|
-
</span>
|
|
694
|
-
</div>
|
|
695
|
-
{containerStates[rc.name] !== "success" && (
|
|
696
|
-
<Button
|
|
697
|
-
size="sm"
|
|
698
|
-
onClick={() => createContainer(rc.name, rc.templateId)}
|
|
699
|
-
disabled={!editorSettingsItem || !!busyMap[rc.name]}
|
|
700
|
-
>
|
|
701
|
-
{busyMap[rc.name] ? (
|
|
702
|
-
<RefreshCw
|
|
703
|
-
strokeWidth={1}
|
|
704
|
-
className="h-4 w-4 animate-spin"
|
|
705
|
-
/>
|
|
706
|
-
) : (
|
|
707
|
-
<CheckCircle strokeWidth={1} className="h-4 w-4" />
|
|
708
|
-
)}
|
|
709
|
-
Fix
|
|
710
|
-
</Button>
|
|
711
|
-
)}
|
|
712
|
-
</div>
|
|
713
|
-
))}
|
|
714
|
-
</div>
|
|
715
|
-
{containersError && (
|
|
716
|
-
<div className="mt-2 rounded border border-yellow-200 bg-yellow-50 p-2 text-xs whitespace-pre-wrap text-yellow-800">
|
|
717
|
-
{containersError}
|
|
718
|
-
</div>
|
|
719
|
-
)}
|
|
720
|
-
<div className="flex items-center justify-between gap-2">
|
|
721
|
-
<div className="flex items-center gap-2">
|
|
722
|
-
<CheckCircle strokeWidth={1} className="h-4 w-4 text-green-600" />
|
|
723
|
-
<span className="text-sm text-gray-700">
|
|
724
|
-
Select provider and add endpoint
|
|
725
|
-
</span>
|
|
726
|
-
</div>
|
|
727
|
-
</div>
|
|
728
|
-
<div className="mt-3 flex items-center gap-2">
|
|
729
|
-
<Select
|
|
730
|
-
value={aiProvider}
|
|
731
|
-
onValueChange={setAiProvider}
|
|
732
|
-
options={aiOptions}
|
|
733
|
-
placeholder="Choose provider"
|
|
734
|
-
/>
|
|
735
|
-
{aiProvider && (
|
|
736
|
-
<Input
|
|
737
|
-
type="password"
|
|
738
|
-
placeholder="API key"
|
|
739
|
-
value={apiKey}
|
|
740
|
-
onChange={(e) => setApiKey(e.target.value)}
|
|
741
|
-
className="w-56"
|
|
742
|
-
/>
|
|
743
|
-
)}
|
|
744
|
-
<Button
|
|
745
|
-
size="sm"
|
|
746
|
-
disabled={!aiProvider || !apiKey || aiBusy}
|
|
747
|
-
onClick={createAiEndpoint}
|
|
748
|
-
>
|
|
749
|
-
{aiBusy ? (
|
|
750
|
-
<RefreshCw strokeWidth={1} className="h-4 w-4 animate-spin" />
|
|
751
|
-
) : (
|
|
752
|
-
<CheckCircle strokeWidth={1} className="h-4 w-4" />
|
|
753
|
-
)}
|
|
754
|
-
Add endpoint
|
|
755
|
-
</Button>
|
|
756
|
-
</div>
|
|
757
|
-
<div className="mt-4 flex items-center gap-2">
|
|
758
|
-
<Button
|
|
759
|
-
size="sm"
|
|
760
|
-
variant="outline"
|
|
761
|
-
disabled={toolsBusy}
|
|
762
|
-
onClick={ensureAiToolsAndGenerate}
|
|
763
|
-
>
|
|
764
|
-
{toolsBusy ? (
|
|
765
|
-
<RefreshCw strokeWidth={1} className="h-4 w-4 animate-spin" />
|
|
766
|
-
) : (
|
|
767
|
-
<CheckCircle strokeWidth={1} className="h-4 w-4" />
|
|
768
|
-
)}
|
|
769
|
-
Generate AI tool items
|
|
770
|
-
</Button>
|
|
771
|
-
{toolsError && (
|
|
772
|
-
<span className="text-xs text-red-600">{toolsError}</span>
|
|
773
|
-
)}
|
|
774
|
-
</div>
|
|
657
|
+
<RequiredContainersList
|
|
658
|
+
requiredContainers={requiredContainers}
|
|
659
|
+
containerStates={containerStates}
|
|
660
|
+
busyMap={busyMap}
|
|
661
|
+
canFix={!!editorSettingsItem}
|
|
662
|
+
containersError={containersError}
|
|
663
|
+
onFixContainer={createContainer}
|
|
664
|
+
/>
|
|
665
|
+
|
|
666
|
+
<ProviderSection
|
|
667
|
+
aiProvider={aiProvider}
|
|
668
|
+
aiOptions={aiOptions}
|
|
669
|
+
onProviderChange={setAiProvider}
|
|
670
|
+
apiKey={apiKey}
|
|
671
|
+
onApiKeyChange={setApiKey}
|
|
672
|
+
aiBusy={aiBusy}
|
|
673
|
+
onAddEndpoint={createAiEndpoint}
|
|
674
|
+
/>
|
|
675
|
+
|
|
676
|
+
<GenerateToolsSection
|
|
677
|
+
toolsBusy={toolsBusy}
|
|
678
|
+
toolsError={toolsError}
|
|
679
|
+
onGenerateTools={ensureAiToolsAndGenerate}
|
|
680
|
+
/>
|
|
681
|
+
|
|
775
682
|
{aiError && (
|
|
776
683
|
<div className="mt-2 rounded border border-red-200 bg-red-50 p-2 text-xs whitespace-pre-wrap text-red-700">
|
|
777
684
|
{aiError}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Select } from "../../../../../components/ui/select";
|
|
3
|
+
import { Input } from "../../../../../components/ui/input";
|
|
4
|
+
import { Button } from "../../../../../components/ui/button";
|
|
5
|
+
import { CheckCircle, RefreshCw } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
interface ProviderOption {
|
|
8
|
+
value: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ProviderSectionProps {
|
|
13
|
+
aiProvider: string;
|
|
14
|
+
aiOptions: ProviderOption[];
|
|
15
|
+
onProviderChange: (value: string) => void;
|
|
16
|
+
apiKey: string;
|
|
17
|
+
onApiKeyChange: (value: string) => void;
|
|
18
|
+
aiBusy: boolean;
|
|
19
|
+
onAddEndpoint: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function ProviderSection(props: ProviderSectionProps) {
|
|
23
|
+
const {
|
|
24
|
+
aiProvider,
|
|
25
|
+
aiOptions,
|
|
26
|
+
onProviderChange,
|
|
27
|
+
apiKey,
|
|
28
|
+
onApiKeyChange,
|
|
29
|
+
aiBusy,
|
|
30
|
+
onAddEndpoint,
|
|
31
|
+
} = props;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<div className="flex items-center justify-between gap-2">
|
|
36
|
+
<div className="flex items-center gap-2">
|
|
37
|
+
<CheckCircle strokeWidth={1} className="h-4 w-4 text-green-600" />
|
|
38
|
+
<span className="text-sm text-gray-700">
|
|
39
|
+
Select provider and add endpoint
|
|
40
|
+
</span>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div className="mt-3 flex items-center gap-2">
|
|
44
|
+
<Select
|
|
45
|
+
value={aiProvider}
|
|
46
|
+
onValueChange={onProviderChange}
|
|
47
|
+
options={aiOptions}
|
|
48
|
+
placeholder="Choose provider"
|
|
49
|
+
/>
|
|
50
|
+
{aiProvider && (
|
|
51
|
+
<Input
|
|
52
|
+
type="password"
|
|
53
|
+
placeholder="API key"
|
|
54
|
+
value={apiKey}
|
|
55
|
+
onChange={(e) => onApiKeyChange(e.target.value)}
|
|
56
|
+
className="w-56"
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
<Button
|
|
60
|
+
size="sm"
|
|
61
|
+
disabled={!aiProvider || !apiKey || aiBusy}
|
|
62
|
+
onClick={onAddEndpoint}
|
|
63
|
+
>
|
|
64
|
+
{aiBusy ? (
|
|
65
|
+
<RefreshCw strokeWidth={1} className="h-4 w-4 animate-spin" />
|
|
66
|
+
) : (
|
|
67
|
+
<CheckCircle strokeWidth={1} className="h-4 w-4" />
|
|
68
|
+
)}
|
|
69
|
+
Add endpoint
|
|
70
|
+
</Button>
|
|
71
|
+
</div>
|
|
72
|
+
</>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Button } from "../../../../../components/ui/button";
|
|
3
|
+
import { AlertCircle, CheckCircle, RefreshCw } from "lucide-react";
|
|
4
|
+
import type { StepState } from "../index";
|
|
5
|
+
|
|
6
|
+
interface RequiredContainerDef {
|
|
7
|
+
name: string;
|
|
8
|
+
templateId: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface RequiredContainersListProps {
|
|
12
|
+
requiredContainers: RequiredContainerDef[];
|
|
13
|
+
containerStates: Record<string, StepState>;
|
|
14
|
+
busyMap: Record<string, boolean>;
|
|
15
|
+
canFix: boolean;
|
|
16
|
+
containersError: string | null;
|
|
17
|
+
onFixContainer: (name: string, templateId: string) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function RequiredContainersList(props: RequiredContainersListProps) {
|
|
21
|
+
const {
|
|
22
|
+
requiredContainers,
|
|
23
|
+
containerStates,
|
|
24
|
+
busyMap,
|
|
25
|
+
canFix,
|
|
26
|
+
containersError,
|
|
27
|
+
onFixContainer,
|
|
28
|
+
} = props;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<div className="flex flex-col gap-2">
|
|
33
|
+
{requiredContainers
|
|
34
|
+
.filter((rc) => containerStates[rc.name] === "error")
|
|
35
|
+
.map((rc) => (
|
|
36
|
+
<div
|
|
37
|
+
key={rc.name}
|
|
38
|
+
className="flex items-center justify-between gap-2"
|
|
39
|
+
>
|
|
40
|
+
<div className="flex items-center gap-2">
|
|
41
|
+
{containerStates[rc.name] === "checking" ? (
|
|
42
|
+
<RefreshCw
|
|
43
|
+
strokeWidth={1}
|
|
44
|
+
className="h-4 w-4 animate-spin text-amber-600"
|
|
45
|
+
/>
|
|
46
|
+
) : containerStates[rc.name] === "success" ? (
|
|
47
|
+
<CheckCircle
|
|
48
|
+
strokeWidth={1}
|
|
49
|
+
className="h-4 w-4 text-green-600"
|
|
50
|
+
/>
|
|
51
|
+
) : (
|
|
52
|
+
<AlertCircle
|
|
53
|
+
strokeWidth={1}
|
|
54
|
+
className="h-4 w-4 text-red-600"
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
<span className="text-sm text-gray-700">
|
|
58
|
+
{containerStates[rc.name] === "checking"
|
|
59
|
+
? `Checking ${rc.name}...`
|
|
60
|
+
: containerStates[rc.name] === "success"
|
|
61
|
+
? `${rc.name} present`
|
|
62
|
+
: `${rc.name} missing`}
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
{containerStates[rc.name] !== "success" && (
|
|
66
|
+
<Button
|
|
67
|
+
size="sm"
|
|
68
|
+
onClick={() => onFixContainer(rc.name, rc.templateId)}
|
|
69
|
+
disabled={!canFix || !!busyMap[rc.name]}
|
|
70
|
+
>
|
|
71
|
+
{busyMap[rc.name] ? (
|
|
72
|
+
<RefreshCw
|
|
73
|
+
strokeWidth={1}
|
|
74
|
+
className="h-4 w-4 animate-spin"
|
|
75
|
+
/>
|
|
76
|
+
) : (
|
|
77
|
+
<CheckCircle strokeWidth={1} className="h-4 w-4" />
|
|
78
|
+
)}
|
|
79
|
+
Fix
|
|
80
|
+
</Button>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
{containersError && (
|
|
86
|
+
<div className="mt-2 rounded border border-yellow-200 bg-yellow-50 p-2 text-xs whitespace-pre-wrap text-yellow-800">
|
|
87
|
+
{containersError}
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
</>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Button } from "../../../../../components/ui/button";
|
|
3
|
+
import { CheckCircle, RefreshCw } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
interface GenerateToolsSectionProps {
|
|
6
|
+
toolsBusy: boolean;
|
|
7
|
+
toolsError: string | null;
|
|
8
|
+
onGenerateTools: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function GenerateToolsSection(props: GenerateToolsSectionProps) {
|
|
12
|
+
const { toolsBusy, toolsError, onGenerateTools } = props;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="mt-4 flex items-center gap-2">
|
|
16
|
+
<Button
|
|
17
|
+
size="sm"
|
|
18
|
+
variant="outline"
|
|
19
|
+
disabled={toolsBusy}
|
|
20
|
+
onClick={onGenerateTools}
|
|
21
|
+
>
|
|
22
|
+
{toolsBusy ? (
|
|
23
|
+
<RefreshCw strokeWidth={1} className="h-4 w-4 animate-spin" />
|
|
24
|
+
) : (
|
|
25
|
+
<CheckCircle strokeWidth={1} className="h-4 w-4" />
|
|
26
|
+
)}
|
|
27
|
+
Generate AI tool items
|
|
28
|
+
</Button>
|
|
29
|
+
{toolsError && <span className="text-xs text-red-600">{toolsError}</span>}
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -56,6 +56,7 @@ export function EditControls({
|
|
|
56
56
|
icon={<MessagesSquare strokeWidth={1} className="h-6 w-6 p-1" />}
|
|
57
57
|
label="Write suggestions"
|
|
58
58
|
size="large"
|
|
59
|
+
data-testid="suggestions-mode-button"
|
|
59
60
|
onClick={() => editContext?.setMode("suggestions")}
|
|
60
61
|
>
|
|
61
62
|
{totalCount > 0 && (
|
|
@@ -48,6 +48,73 @@ export function InlineEditor({
|
|
|
48
48
|
);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
// Helper: set innerHTML and log with stack trace when content changes
|
|
52
|
+
function setInnerHTMLWithStackTrace(
|
|
53
|
+
element: HTMLElement,
|
|
54
|
+
nextHTML: string,
|
|
55
|
+
meta: {
|
|
56
|
+
source: string;
|
|
57
|
+
fieldId?: string | null;
|
|
58
|
+
itemId?: string | null;
|
|
59
|
+
language?: string | null;
|
|
60
|
+
version?: number | null;
|
|
61
|
+
},
|
|
62
|
+
): boolean {
|
|
63
|
+
const prevHTML = element.innerHTML ?? "";
|
|
64
|
+
if (prevHTML === nextHTML) return false;
|
|
65
|
+
|
|
66
|
+
element.innerHTML = nextHTML;
|
|
67
|
+
|
|
68
|
+
// try {
|
|
69
|
+
// const resolvedFieldId =
|
|
70
|
+
// meta.fieldId ?? element.getAttribute("data-fieldid");
|
|
71
|
+
// const resolvedItemId = meta.itemId ?? element.getAttribute("data-itemid");
|
|
72
|
+
// const resolvedLanguage =
|
|
73
|
+
// meta.language ?? element.getAttribute("data-language");
|
|
74
|
+
// const resolvedVersion =
|
|
75
|
+
// meta.version ??
|
|
76
|
+
// (element.getAttribute("data-version")
|
|
77
|
+
// ? parseInt(element.getAttribute("data-version") as string, 10)
|
|
78
|
+
// : null);
|
|
79
|
+
|
|
80
|
+
// // Log concise info and stack trace
|
|
81
|
+
// // Limit previews to keep console tidy
|
|
82
|
+
// const preview = (s: string) =>
|
|
83
|
+
// s.length > 200 ? s.slice(0, 200) + "…" : s;
|
|
84
|
+
// console.groupCollapsed(
|
|
85
|
+
// `[InlineEditor] DOM field update (${meta.source}) ${nextHTML} `,
|
|
86
|
+
// );
|
|
87
|
+
|
|
88
|
+
// const modifiedFieldValue = modifiedFieldsContext?.modifiedFields.find(
|
|
89
|
+
// (x) =>
|
|
90
|
+
// x.fieldId === resolvedFieldId &&
|
|
91
|
+
// x.item.id === resolvedItemId &&
|
|
92
|
+
// x.item.language === resolvedLanguage &&
|
|
93
|
+
// x.item.version === resolvedVersion,
|
|
94
|
+
// )?.value;
|
|
95
|
+
|
|
96
|
+
// console.log({
|
|
97
|
+
// fieldId: resolvedFieldId,
|
|
98
|
+
// itemId: resolvedItemId,
|
|
99
|
+
// language: resolvedLanguage,
|
|
100
|
+
// version: resolvedVersion,
|
|
101
|
+
// prevLength: prevHTML.length,
|
|
102
|
+
// nextLength: nextHTML.length,
|
|
103
|
+
// prevPreview: preview(prevHTML),
|
|
104
|
+
// nextPreview: preview(nextHTML),
|
|
105
|
+
// modifiedFieldsContext: modifiedFieldsContext?.modifiedFields,
|
|
106
|
+
// repository: contextRef.current?.itemsRepository,
|
|
107
|
+
// modifiedFieldValue,
|
|
108
|
+
// });
|
|
109
|
+
// console.trace();
|
|
110
|
+
// console.groupEnd();
|
|
111
|
+
// } catch {
|
|
112
|
+
// // no-op logging failure
|
|
113
|
+
// }
|
|
114
|
+
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
51
118
|
const debouncedSetFieldvalue = useThrottledCallback(
|
|
52
119
|
(capturedValue, fieldId, fieldName, itemId, language, version) => {
|
|
53
120
|
// Use the value captured at mutation time to avoid cross-field leakage
|
|
@@ -362,12 +429,24 @@ export function InlineEditor({
|
|
|
362
429
|
}
|
|
363
430
|
// Update focused field element with the merged plain text value.
|
|
364
431
|
|
|
365
|
-
element
|
|
432
|
+
setInnerHTMLWithStackTrace(element as HTMLElement, mergedValue, {
|
|
433
|
+
source: "updateFocusedFieldContent:mergedValue",
|
|
434
|
+
fieldId,
|
|
435
|
+
itemId,
|
|
436
|
+
language,
|
|
437
|
+
version,
|
|
438
|
+
});
|
|
366
439
|
contentWasUpdated = true;
|
|
367
440
|
} else {
|
|
368
441
|
if (element.innerHTML !== baseValue) {
|
|
369
442
|
// If suggestions mode is off, update with the base value.
|
|
370
|
-
element
|
|
443
|
+
setInnerHTMLWithStackTrace(element as HTMLElement, baseValue, {
|
|
444
|
+
source: "updateFocusedFieldContent:baseValue",
|
|
445
|
+
fieldId,
|
|
446
|
+
itemId,
|
|
447
|
+
language,
|
|
448
|
+
version,
|
|
449
|
+
});
|
|
371
450
|
contentWasUpdated = true;
|
|
372
451
|
}
|
|
373
452
|
}
|
|
@@ -540,7 +619,18 @@ export function InlineEditor({
|
|
|
540
619
|
if (fieldType && isTextFieldType(fieldType)) {
|
|
541
620
|
const next = field?.value ? (field.value as string) : "";
|
|
542
621
|
if (element.innerHTML !== next) {
|
|
543
|
-
element
|
|
622
|
+
setInnerHTMLWithStackTrace(element as HTMLElement, next, {
|
|
623
|
+
source: "updateFieldsWithSuggestions:modifiedField",
|
|
624
|
+
fieldId: element.getAttribute("data-fieldid"),
|
|
625
|
+
itemId: element.getAttribute("data-itemid"),
|
|
626
|
+
language: element.getAttribute("data-language"),
|
|
627
|
+
version: element.getAttribute("data-version")
|
|
628
|
+
? parseInt(
|
|
629
|
+
element.getAttribute("data-version") as string,
|
|
630
|
+
10,
|
|
631
|
+
)
|
|
632
|
+
: null,
|
|
633
|
+
});
|
|
544
634
|
}
|
|
545
635
|
}
|
|
546
636
|
}
|
|
@@ -645,7 +735,17 @@ export function InlineEditor({
|
|
|
645
735
|
// If showSuggestedEdits is false, update with the base value.
|
|
646
736
|
if (!context.showSuggestedEdits && context.mode !== "suggestions") {
|
|
647
737
|
if (fieldElement.innerHTML !== originalValue) {
|
|
648
|
-
|
|
738
|
+
setInnerHTMLWithStackTrace(
|
|
739
|
+
fieldElement as HTMLElement,
|
|
740
|
+
originalValue,
|
|
741
|
+
{
|
|
742
|
+
source: "updateFieldsWithSuggestions:baselineOnly",
|
|
743
|
+
fieldId,
|
|
744
|
+
itemId,
|
|
745
|
+
language,
|
|
746
|
+
version,
|
|
747
|
+
},
|
|
748
|
+
);
|
|
649
749
|
}
|
|
650
750
|
|
|
651
751
|
return;
|
|
@@ -700,7 +800,17 @@ export function InlineEditor({
|
|
|
700
800
|
// If showSuggestedEditsDiff is false, show only the merged text
|
|
701
801
|
if (!context.showSuggestedEditsDiff) {
|
|
702
802
|
if (fieldElement.innerHTML !== mergedValue) {
|
|
703
|
-
|
|
803
|
+
setInnerHTMLWithStackTrace(
|
|
804
|
+
fieldElement as HTMLElement,
|
|
805
|
+
mergedValue,
|
|
806
|
+
{
|
|
807
|
+
source: "updateFieldsWithSuggestions:mergedNoDiff",
|
|
808
|
+
fieldId,
|
|
809
|
+
itemId,
|
|
810
|
+
language,
|
|
811
|
+
version,
|
|
812
|
+
},
|
|
813
|
+
);
|
|
704
814
|
}
|
|
705
815
|
return;
|
|
706
816
|
}
|
|
@@ -725,7 +835,13 @@ export function InlineEditor({
|
|
|
725
835
|
|
|
726
836
|
// Update the element's innerHTML with the diff markup.
|
|
727
837
|
if (fieldElement.innerHTML !== diffHTML) {
|
|
728
|
-
fieldElement
|
|
838
|
+
setInnerHTMLWithStackTrace(fieldElement as HTMLElement, diffHTML, {
|
|
839
|
+
source: "updateFieldsWithSuggestions:diffMarkup",
|
|
840
|
+
fieldId,
|
|
841
|
+
itemId,
|
|
842
|
+
language,
|
|
843
|
+
version,
|
|
844
|
+
});
|
|
729
845
|
}
|
|
730
846
|
});
|
|
731
847
|
};
|
|
@@ -823,7 +939,13 @@ export function InlineEditor({
|
|
|
823
939
|
|
|
824
940
|
// write it in
|
|
825
941
|
if (el.innerHTML !== value) {
|
|
826
|
-
el
|
|
942
|
+
setInnerHTMLWithStackTrace(el as HTMLElement, value, {
|
|
943
|
+
source: "resetAllFieldsToBaselineOrLocal",
|
|
944
|
+
fieldId,
|
|
945
|
+
itemId,
|
|
946
|
+
language,
|
|
947
|
+
version,
|
|
948
|
+
});
|
|
827
949
|
}
|
|
828
950
|
});
|
|
829
951
|
}, [context.mode, context.showSuggestedEdits, context.suggestedEdits]);
|
|
@@ -84,6 +84,7 @@ export function ContentStep({
|
|
|
84
84
|
const [message, setMessage] = useState<string>();
|
|
85
85
|
const [isCreatingComponents, setIsCreatingComponents] = useState(false);
|
|
86
86
|
const [pageItem, setPageItem] = useState<ItemDescriptor>();
|
|
87
|
+
const [showDataObject, setShowDataObject] = useState(false);
|
|
87
88
|
|
|
88
89
|
// New state for name and language functionality
|
|
89
90
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
@@ -1192,6 +1193,51 @@ export function ContentStep({
|
|
|
1192
1193
|
}
|
|
1193
1194
|
>
|
|
1194
1195
|
<div>{customInstructionsPanel()}</div>
|
|
1196
|
+
<div className="mt-2">
|
|
1197
|
+
<button
|
|
1198
|
+
className="flex items-center text-xs text-blue-600 hover:underline"
|
|
1199
|
+
onClick={() => setShowDataObject(!showDataObject)}
|
|
1200
|
+
>
|
|
1201
|
+
<ChevronDown
|
|
1202
|
+
strokeWidth={1}
|
|
1203
|
+
className={`mr-1 h-4 w-4 transition-transform duration-200 ${showDataObject ? "rotate-180" : "rotate-0"}`}
|
|
1204
|
+
/>
|
|
1205
|
+
{showDataObject
|
|
1206
|
+
? "Hide data available to instructions"
|
|
1207
|
+
: "Show data available to instructions"}
|
|
1208
|
+
</button>
|
|
1209
|
+
<div className="mt-2 text-xs text-gray-600">
|
|
1210
|
+
<div className="font-medium">
|
|
1211
|
+
Referencing data in instructions
|
|
1212
|
+
</div>
|
|
1213
|
+
<div>Use curly braces to inject values from the data object:</div>
|
|
1214
|
+
<div className="mt-1 grid gap-1">
|
|
1215
|
+
<div>
|
|
1216
|
+
<code>{"{title}"}</code> inserts <code>data.title</code>
|
|
1217
|
+
</div>
|
|
1218
|
+
<div>
|
|
1219
|
+
<code>{"{data.user.name}"}</code> inserts a nested property
|
|
1220
|
+
</div>
|
|
1221
|
+
<div>
|
|
1222
|
+
<code>{"{items.filter(x => x.active).length}"}</code> supports
|
|
1223
|
+
JavaScript expressions
|
|
1224
|
+
</div>
|
|
1225
|
+
<div>
|
|
1226
|
+
<code>{"{data}"}</code> inserts the entire data object as JSON
|
|
1227
|
+
</div>
|
|
1228
|
+
</div>
|
|
1229
|
+
<div className="mt-1">
|
|
1230
|
+
Arrays/objects are inserted as JSON text.
|
|
1231
|
+
</div>
|
|
1232
|
+
</div>
|
|
1233
|
+
{showDataObject && (
|
|
1234
|
+
<div className="mt-2 rounded border border-gray-200 bg-gray-50 p-2">
|
|
1235
|
+
<pre className="max-h-64 overflow-auto text-xs break-words whitespace-pre-wrap">
|
|
1236
|
+
{JSON.stringify(data, null, 2)}
|
|
1237
|
+
</pre>
|
|
1238
|
+
</div>
|
|
1239
|
+
)}
|
|
1240
|
+
</div>
|
|
1195
1241
|
</WizardBox>
|
|
1196
1242
|
</div>
|
|
1197
1243
|
{pageItem && (
|
package/src/revision.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = "1.0.
|
|
2
|
-
export const buildDate = "2025-09-
|
|
1
|
+
export const version = "1.0.4096";
|
|
2
|
+
export const buildDate = "2025-09-19 08:03:30";
|