camox 0.31.5 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/core/components/lexical/InlineLexicalEditor.js +79 -11
  2. package/dist/core/components/lexical/SelectionBroadcaster.js +106 -20
  3. package/dist/core/components/lexical/SidebarLexicalEditor.js +442 -8
  4. package/dist/core/components/lexical/TextLinkPopover.js +327 -0
  5. package/dist/core/components/lexical/editorConfig.js +11 -5
  6. package/dist/core/createApp.d.ts +9 -0
  7. package/dist/core/createBlock.d.ts +7 -2
  8. package/dist/core/createBlock.js +101 -110
  9. package/dist/core/hooks/useIsEditable.js +6 -10
  10. package/dist/core/lib/fieldTypes.js +10 -219
  11. package/dist/core/lib/lexicalReact.d.ts +26 -0
  12. package/dist/core/lib/lexicalReact.js +50 -15
  13. package/dist/core/lib/lexicalState.js +34 -0
  14. package/dist/core/lib/textLinks.js +28 -0
  15. package/dist/features/agent-chat/components/AgentChatSidebar.js +172 -0
  16. package/dist/features/agent-chat/components/AgentChatThread.js +9 -9
  17. package/dist/features/preview/CamoxPreview.js +137 -729
  18. package/dist/features/preview/components/{AddBlockSheet.js → AddBlockSidebar.js} +121 -131
  19. package/dist/features/preview/components/AssetFieldEditor.js +107 -82
  20. package/dist/features/preview/components/AssetLightbox.js +4 -4
  21. package/dist/features/preview/components/AssetPickerModal.js +277 -0
  22. package/dist/features/preview/components/BlockActionsPopover.js +18 -24
  23. package/dist/features/preview/components/CreatePageModal.js +6 -6
  24. package/dist/features/preview/components/FieldToolbar.js +118 -111
  25. package/dist/features/preview/components/ItemFieldsEditor.js +98 -125
  26. package/dist/features/preview/components/LeftSidebar.js +64 -0
  27. package/dist/features/preview/components/LinkFieldEditor.js +1 -1
  28. package/dist/features/preview/components/MultipleAssetFieldEditor.js +94 -56
  29. package/dist/features/preview/components/OverlayTracker.js +15 -15
  30. package/dist/features/preview/components/Overlays.js +12 -12
  31. package/dist/features/preview/components/PageEditorSidebar.js +563 -0
  32. package/dist/features/preview/components/PageInfoSidebar.js +1453 -0
  33. package/dist/features/preview/components/PageNavigatorSidebar.js +590 -0
  34. package/dist/features/preview/components/PagePicker.js +3 -3
  35. package/dist/features/preview/components/PageStatusBadge.js +1 -1
  36. package/dist/features/preview/components/PageTree.js +204 -564
  37. package/dist/features/preview/components/PreviewPanel.js +123 -158
  38. package/dist/features/preview/components/PreviewToolbar.js +260 -256
  39. package/dist/features/preview/components/PublishDialog.js +2 -2
  40. package/dist/features/preview/components/RepeatableItemsList.js +2 -2
  41. package/dist/features/preview/components/RightSidebar.js +109 -0
  42. package/dist/features/preview/components/UnlinkAssetButton.js +2 -2
  43. package/dist/features/preview/components/useRepeatableItemActions.js +3 -138
  44. package/dist/features/preview/components/useUpdateBlockPosition.js +1 -1
  45. package/dist/features/preview/previewQueryFns.js +23 -0
  46. package/dist/features/preview/previewStore.js +46 -63
  47. package/dist/features/provider/CamoxProvider.js +255 -70
  48. package/dist/features/provider/useAdminShortcuts.js +5 -48
  49. package/dist/features/routes/pageRoute.js +1 -1
  50. package/dist/features/studio/CamoxStudio.js +6 -5
  51. package/dist/features/studio/components/EnvironmentMenu.js +2 -2
  52. package/dist/features/studio/components/Navbar.js +53 -71
  53. package/dist/features/studio/components/ProjectMenu.js +1 -1
  54. package/dist/features/studio/components/UserButton.js +1 -1
  55. package/dist/features/studio/routes.js +7 -0
  56. package/dist/features/studio/useTheme.js +67 -33
  57. package/dist/features/vite/routeGeneration.js +68 -66
  58. package/dist/features/vite/vite.js +2 -0
  59. package/dist/hooks/use-file-upload.js +1 -1
  60. package/dist/lib/auth.js +23 -22
  61. package/dist/lib/utils.js +1 -1
  62. package/dist/studio-overlays.css +1 -1
  63. package/dist/studio.css +1 -1
  64. package/package.json +5 -4
  65. package/skills/camox-block/SKILL.md +23 -1
  66. package/dist/features/agent-chat/components/AgentChatSheet.js +0 -207
  67. package/dist/features/preview/components/AssetPickerGrid.js +0 -236
  68. package/dist/features/preview/components/PageContentSheet.js +0 -608
  69. package/dist/features/preview/components/PageMetadataModal.js +0 -908
  70. package/dist/features/preview/components/PreviewSideSheet.js +0 -76
@@ -1,224 +1,15 @@
1
- import { previewStore } from "../../features/preview/previewStore.js";
2
- import { jsx } from "react/jsx-runtime";
3
- import { ChevronDown, FileIcon, FrameIcon, ImageIcon, Images, Link2, List, ToggleLeft, Type } from "lucide-react";
4
-
5
1
  //#region src/core/lib/fieldTypes.tsx
6
2
  const fieldTypesDictionary = {
7
- String: {
8
- label: "String",
9
- isScalar: true,
10
- isContentEditable: true,
11
- hasOwnView: false,
12
- getIcon: () => (props) => /* @__PURE__ */ jsx(Type, { ...props }),
13
- getLabel: (value) => {
14
- if (typeof value !== "string") return "";
15
- return value.replace(/\*{1,3}(.+?)\*{1,3}/g, "$1");
16
- },
17
- onTreeDoubleClick: ({ blockId, fieldName }) => {
18
- previewStore.send({
19
- type: "selectBlockField",
20
- blockId,
21
- fieldName,
22
- fieldType: "String"
23
- });
24
- previewStore.send({
25
- type: "openBlockContentSheet",
26
- blockId
27
- });
28
- }
29
- },
30
- Repeater: {
31
- label: "Repeater",
32
- isScalar: false,
33
- isContentEditable: false,
34
- hasOwnView: false,
35
- getIcon: () => (props) => /* @__PURE__ */ jsx(List, { ...props }),
36
- getLabel: (_value, { schemaTitle, fieldName }) => schemaTitle ?? fieldName,
37
- onTreeDoubleClick: ({ blockId }) => {
38
- previewStore.send({
39
- type: "setFocusedBlock",
40
- blockId
41
- });
42
- previewStore.send({
43
- type: "openBlockContentSheet",
44
- blockId
45
- });
46
- }
47
- },
48
- Enum: {
49
- label: "Enum",
50
- isScalar: true,
51
- isContentEditable: false,
52
- hasOwnView: false,
53
- getIcon: () => (props) => /* @__PURE__ */ jsx(ChevronDown, { ...props }),
54
- getLabel: (value) => value,
55
- onTreeDoubleClick: ({ blockId, fieldName }) => {
56
- previewStore.send({
57
- type: "selectBlockField",
58
- blockId,
59
- fieldName,
60
- fieldType: "Enum"
61
- });
62
- previewStore.send({
63
- type: "openBlockContentSheet",
64
- blockId
65
- });
66
- }
67
- },
68
- Boolean: {
69
- label: "Boolean",
70
- isScalar: true,
71
- isContentEditable: false,
72
- hasOwnView: false,
73
- getIcon: () => (props) => /* @__PURE__ */ jsx(ToggleLeft, { ...props }),
74
- getLabel: (value) => JSON.stringify(value),
75
- onTreeDoubleClick: ({ blockId, fieldName }) => {
76
- previewStore.send({
77
- type: "selectBlockField",
78
- blockId,
79
- fieldName,
80
- fieldType: "Boolean"
81
- });
82
- previewStore.send({
83
- type: "openBlockContentSheet",
84
- blockId
85
- });
86
- }
87
- },
88
- Embed: {
89
- label: "Embed",
90
- isScalar: true,
91
- isContentEditable: false,
92
- hasOwnView: false,
93
- getIcon: () => (props) => /* @__PURE__ */ jsx(FrameIcon, { ...props }),
94
- getLabel: (value, { schemaTitle, fieldName, fetchedTitle }) => {
95
- let domain = null;
96
- try {
97
- domain = new URL(value).hostname.replace(/^www\./, "");
98
- } catch {}
99
- return fetchedTitle ?? schemaTitle ?? domain ?? fieldName;
100
- },
101
- onTreeDoubleClick: ({ blockId, fieldName }) => {
102
- previewStore.send({
103
- type: "selectBlockField",
104
- blockId,
105
- fieldName,
106
- fieldType: "Embed"
107
- });
108
- previewStore.send({
109
- type: "openBlockContentSheet",
110
- blockId
111
- });
112
- }
113
- },
114
- Link: {
115
- label: "Link",
116
- isScalar: false,
117
- isContentEditable: false,
118
- hasOwnView: true,
119
- getIcon: () => (props) => /* @__PURE__ */ jsx(Link2, { ...props }),
120
- getLabel: (value) => value?.text ?? "",
121
- onTreeDoubleClick: ({ blockId, fieldName }) => {
122
- previewStore.send({
123
- type: "selectBlockField",
124
- blockId,
125
- fieldName,
126
- fieldType: "Link"
127
- });
128
- previewStore.send({
129
- type: "openBlockContentSheet",
130
- blockId
131
- });
132
- }
133
- },
134
- Image: {
135
- label: "Image",
136
- isScalar: false,
137
- isContentEditable: false,
138
- hasOwnView: true,
139
- getIcon: () => (props) => /* @__PURE__ */ jsx(ImageIcon, { ...props }),
140
- getLabel: (value, { schemaTitle, fieldName }) => {
141
- const filename = value?.filename;
142
- if (!filename) return `Missing ${schemaTitle ?? fieldName}`;
143
- return filename;
144
- },
145
- onTreeDoubleClick: ({ blockId, fieldName }) => {
146
- previewStore.send({
147
- type: "selectBlockField",
148
- blockId,
149
- fieldName,
150
- fieldType: "Image"
151
- });
152
- previewStore.send({
153
- type: "openBlockContentSheet",
154
- blockId
155
- });
156
- }
157
- },
158
- File: {
159
- label: "File",
160
- isScalar: false,
161
- isContentEditable: false,
162
- hasOwnView: true,
163
- getIcon: () => (props) => /* @__PURE__ */ jsx(FileIcon, { ...props }),
164
- getLabel: (value, { schemaTitle, fieldName }) => {
165
- const filename = value?.filename;
166
- if (!filename) return `Missing ${schemaTitle ?? fieldName}`;
167
- return filename;
168
- },
169
- onTreeDoubleClick: ({ blockId, fieldName }) => {
170
- previewStore.send({
171
- type: "selectBlockField",
172
- blockId,
173
- fieldName,
174
- fieldType: "File"
175
- });
176
- previewStore.send({
177
- type: "openBlockContentSheet",
178
- blockId
179
- });
180
- }
181
- },
182
- ImageList: {
183
- label: "Image list",
184
- isScalar: false,
185
- isContentEditable: false,
186
- hasOwnView: true,
187
- getIcon: () => (props) => /* @__PURE__ */ jsx(Images, { ...props }),
188
- getLabel: (_value, { schemaTitle, fieldName }) => schemaTitle ?? fieldName,
189
- onTreeDoubleClick: ({ blockId, fieldName }) => {
190
- previewStore.send({
191
- type: "selectBlockField",
192
- blockId,
193
- fieldName,
194
- fieldType: "Image"
195
- });
196
- previewStore.send({
197
- type: "openBlockContentSheet",
198
- blockId
199
- });
200
- }
201
- },
202
- FileList: {
203
- label: "File list",
204
- isScalar: false,
205
- isContentEditable: false,
206
- hasOwnView: true,
207
- getIcon: () => (props) => /* @__PURE__ */ jsx(FileIcon, { ...props }),
208
- getLabel: (_value, { schemaTitle, fieldName }) => schemaTitle ?? fieldName,
209
- onTreeDoubleClick: ({ blockId, fieldName }) => {
210
- previewStore.send({
211
- type: "selectBlockField",
212
- blockId,
213
- fieldName,
214
- fieldType: "File"
215
- });
216
- previewStore.send({
217
- type: "openBlockContentSheet",
218
- blockId
219
- });
220
- }
221
- }
3
+ String: { hasOwnView: false },
4
+ Repeater: { hasOwnView: false },
5
+ Enum: { hasOwnView: false },
6
+ Boolean: { hasOwnView: false },
7
+ Embed: { hasOwnView: false },
8
+ Link: { hasOwnView: true },
9
+ Image: { hasOwnView: true },
10
+ File: { hasOwnView: true },
11
+ ImageList: { hasOwnView: true },
12
+ FileList: { hasOwnView: true }
222
13
  };
223
14
 
224
15
  //#endregion
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+
3
+ //#region src/core/lib/lexicalReact.d.ts
4
+ interface MarkdownLinkRenderProps {
5
+ href: string;
6
+ target?: string;
7
+ rel?: string;
8
+ children: React.ReactNode;
9
+ }
10
+ interface MarkdownLinkRenderData {
11
+ target: string;
12
+ href: string;
13
+ external: boolean;
14
+ pageId?: string;
15
+ }
16
+ interface MarkdownInlineComponents {
17
+ link?: (props: MarkdownLinkRenderProps, data: MarkdownLinkRenderData) => React.ReactNode;
18
+ strong?: (props: {
19
+ children: React.ReactNode;
20
+ }) => React.ReactNode;
21
+ emphasis?: (props: {
22
+ children: React.ReactNode;
23
+ }) => React.ReactNode;
24
+ }
25
+ //#endregion
26
+ export { MarkdownInlineComponents, MarkdownLinkRenderData, MarkdownLinkRenderProps };
@@ -1,35 +1,70 @@
1
- import "react";
1
+ import { getPageIdFromTextLinkTarget, isHttpTextLinkTarget, isValidTextLinkTarget, resolveTextLinkHref, shouldOpenTextLinkInNewTab } from "./textLinks.js";
2
+ import * as React from "react";
2
3
  import { Fragment, jsx } from "react/jsx-runtime";
3
4
 
4
5
  //#region src/core/lib/lexicalReact.tsx
5
6
  /**
6
- * Parse a markdown string with **bold** and *italic* into React nodes.
7
+ * Parse a markdown string with **bold**, *italic*, and text links into React nodes.
7
8
  * Falls back to rendering the raw string if it's not a string value.
8
9
  */
9
- function markdownToReactNodes(value) {
10
+ function markdownToReactNodes(value, options = {}) {
10
11
  if (typeof value !== "string") return null;
11
12
  if (!value) return null;
12
- const regex = /(\*{1,3})((?:(?!\1).)+)\1/g;
13
13
  const parts = [];
14
- let lastIndex = 0;
15
- let match;
16
14
  let key = 0;
15
+ const fallbackHref = options.fallbackHref ?? "#";
17
16
  const pushWithLineBreaks = (text) => {
18
17
  text.split("\n").forEach((line, i) => {
19
18
  if (i > 0) parts.push(/* @__PURE__ */ jsx("br", {}, key++));
20
19
  if (line) parts.push(line);
21
20
  });
22
21
  };
23
- while ((match = regex.exec(value)) !== null) {
24
- if (match.index > lastIndex) pushWithLineBreaks(value.slice(lastIndex, match.index));
25
- const stars = match[1].length;
26
- const content = match[2];
27
- if (stars === 3) parts.push(/* @__PURE__ */ jsx("strong", { children: /* @__PURE__ */ jsx("em", { children: content }) }, key++));
28
- else if (stars === 2) parts.push(/* @__PURE__ */ jsx("strong", { children: content }, key++));
29
- else parts.push(/* @__PURE__ */ jsx("em", { children: content }, key++));
30
- lastIndex = match.index + match[0].length;
22
+ const pushFormatted = (text) => {
23
+ const regex = /(\*{1,3})((?:(?!\1).)+)\1/g;
24
+ let lastIndex = 0;
25
+ let match;
26
+ while ((match = regex.exec(text)) !== null) {
27
+ if (match.index > lastIndex) pushWithLineBreaks(text.slice(lastIndex, match.index));
28
+ const stars = match[1].length;
29
+ const content = match[2];
30
+ if (stars === 3) {
31
+ const emphasis = options.components?.emphasis?.({ children: content }) ?? /* @__PURE__ */ jsx("em", { children: content });
32
+ parts.push(/* @__PURE__ */ jsx(React.Fragment, { children: options.components?.strong?.({ children: emphasis }) ?? /* @__PURE__ */ jsx("strong", { children: emphasis }) }, key++));
33
+ } else if (stars === 2) parts.push(/* @__PURE__ */ jsx(React.Fragment, { children: options.components?.strong?.({ children: content }) ?? /* @__PURE__ */ jsx("strong", { children: content }) }, key++));
34
+ else parts.push(/* @__PURE__ */ jsx(React.Fragment, { children: options.components?.emphasis?.({ children: content }) ?? /* @__PURE__ */ jsx("em", { children: content }) }, key++));
35
+ lastIndex = match.index + match[0].length;
36
+ }
37
+ if (lastIndex < text.length) pushWithLineBreaks(text.slice(lastIndex));
38
+ };
39
+ const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
40
+ let lastLinkIndex = 0;
41
+ let linkMatch;
42
+ while ((linkMatch = linkRegex.exec(value)) !== null) {
43
+ if (linkMatch.index > lastLinkIndex) pushFormatted(value.slice(lastLinkIndex, linkMatch.index));
44
+ const [, label, target] = linkMatch;
45
+ const href = isValidTextLinkTarget(target) ? resolveTextLinkHref(target, options.pages, fallbackHref) : null;
46
+ if (href) {
47
+ const openInNewTab = shouldOpenTextLinkInNewTab(target);
48
+ const linkProps = {
49
+ href,
50
+ target: openInNewTab ? "_blank" : void 0,
51
+ rel: openInNewTab ? "noreferrer" : void 0,
52
+ children: label
53
+ };
54
+ const linkData = {
55
+ target,
56
+ href,
57
+ external: isHttpTextLinkTarget(target),
58
+ pageId: getPageIdFromTextLinkTarget(target) ?? void 0
59
+ };
60
+ parts.push(/* @__PURE__ */ jsx(React.Fragment, { children: options.components?.link?.(linkProps, linkData) ?? /* @__PURE__ */ jsx("a", {
61
+ ...linkProps,
62
+ style: { textDecorationLine: "underline" }
63
+ }) }, key++));
64
+ } else pushFormatted(label);
65
+ lastLinkIndex = linkMatch.index + linkMatch[0].length;
31
66
  }
32
- if (lastIndex < value.length) pushWithLineBreaks(value.slice(lastIndex));
67
+ if (lastLinkIndex < value.length) pushFormatted(value.slice(lastLinkIndex));
33
68
  if (parts.length === 0) return value;
34
69
  return /* @__PURE__ */ jsx(Fragment, { children: parts });
35
70
  }
@@ -1,4 +1,5 @@
1
1
  import { FORMAT_FLAGS, lexicalTextToMarkdown } from "./modifierFormats.js";
2
+ import { isValidTextLinkTarget } from "./textLinks.js";
2
3
 
3
4
  //#region src/core/lib/lexicalState.ts
4
5
  function isLexicalState(value) {
@@ -19,6 +20,12 @@ function lexicalStateToMarkdown(serialized) {
19
20
  }
20
21
  function extractMarkdownFromNode(node) {
21
22
  if (node.type === "text") return lexicalTextToMarkdown(node.text ?? "", node.format ?? 0);
23
+ if (node.type === "link") {
24
+ const text = (node.children ?? []).map(extractMarkdownFromNode).join("");
25
+ const url = typeof node.url === "string" ? node.url : "";
26
+ if (!url) return text;
27
+ return `[${text}](${url})`;
28
+ }
22
29
  if (node.type === "linebreak") return "\n";
23
30
  if (!node.children) return "";
24
31
  const parts = [];
@@ -67,6 +74,33 @@ function parseParagraphWithLineBreaks(para) {
67
74
  return nodes;
68
75
  }
69
76
  function parseInlineMarkdown(text) {
77
+ const nodes = [];
78
+ const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
79
+ let lastLinkIndex = 0;
80
+ let linkMatch;
81
+ while ((linkMatch = linkRegex.exec(text)) !== null) {
82
+ if (linkMatch.index > lastLinkIndex) nodes.push(...parseFormattedText(text.slice(lastLinkIndex, linkMatch.index)));
83
+ const [, label, target] = linkMatch;
84
+ if (isValidTextLinkTarget(target)) nodes.push({
85
+ children: parseFormattedText(label),
86
+ direction: "ltr",
87
+ format: "",
88
+ indent: 0,
89
+ rel: null,
90
+ target: null,
91
+ title: null,
92
+ type: "link",
93
+ url: target,
94
+ version: 1
95
+ });
96
+ else nodes.push(...parseFormattedText(label));
97
+ lastLinkIndex = linkMatch.index + linkMatch[0].length;
98
+ }
99
+ if (lastLinkIndex < text.length) nodes.push(...parseFormattedText(text.slice(lastLinkIndex)));
100
+ if (nodes.length === 0) return parseFormattedText(text);
101
+ return nodes;
102
+ }
103
+ function parseFormattedText(text) {
70
104
  const segments = [];
71
105
  const regex = /(\*{1,3})((?:(?!\1).)+)\1/g;
72
106
  let lastIndex = 0;
@@ -0,0 +1,28 @@
1
+ //#region src/core/lib/textLinks.ts
2
+ const PAGE_TEXT_LINK_PREFIX = "camox:page:";
3
+ function getPageIdFromTextLinkTarget(target) {
4
+ if (!target.startsWith("camox:page:")) return null;
5
+ const pageId = target.slice(11);
6
+ if (!pageId) return null;
7
+ return pageId;
8
+ }
9
+ function createPageTextLinkTarget(pageId) {
10
+ return `${PAGE_TEXT_LINK_PREFIX}${pageId}`;
11
+ }
12
+ function isHttpTextLinkTarget(target) {
13
+ return /^https?:\/\//i.test(target);
14
+ }
15
+ function isValidTextLinkTarget(target) {
16
+ return getPageIdFromTextLinkTarget(target) != null || isHttpTextLinkTarget(target);
17
+ }
18
+ function resolveTextLinkHref(target, pages, fallbackHref) {
19
+ const pageId = getPageIdFromTextLinkTarget(target);
20
+ if (pageId == null) return isHttpTextLinkTarget(target) ? target : null;
21
+ return (pages?.find((p) => String(p.id) === pageId))?.fullPath ?? fallbackHref;
22
+ }
23
+ function shouldOpenTextLinkInNewTab(target) {
24
+ return isHttpTextLinkTarget(target);
25
+ }
26
+
27
+ //#endregion
28
+ export { createPageTextLinkTarget, getPageIdFromTextLinkTarget, isHttpTextLinkTarget, isValidTextLinkTarget, resolveTextLinkHref, shouldOpenTextLinkInNewTab };
@@ -0,0 +1,172 @@
1
+ import { useProjectSlug } from "../../../lib/auth.js";
2
+ import { projectQueries } from "../../../lib/queries.js";
3
+ import { previewStore } from "../../preview/previewStore.js";
4
+ import { AgentChatThread } from "./AgentChatThread.js";
5
+ import { c } from "react/compiler-runtime";
6
+ import { useQuery } from "@tanstack/react-query";
7
+ import { useLocation } from "@tanstack/react-router";
8
+ import { useSelector } from "@xstate/store-react";
9
+ import * as React from "react";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import { Button } from "@camox/ui/button";
12
+ import { Lock, Plus, X } from "lucide-react";
13
+ import { Alert, AlertDescription, AlertTitle } from "@camox/ui/alert";
14
+
15
+ //#region src/features/agent-chat/components/AgentChatSidebar.tsx
16
+ const AgentChatSidebar = () => {
17
+ const $ = c(20);
18
+ if ($[0] !== "fa80c907e2ec181da4f3cb91fbf7d8dc7f1330b0892397f5d2dd7e7058b18f66") {
19
+ for (let $i = 0; $i < 20; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
20
+ $[0] = "fa80c907e2ec181da4f3cb91fbf7d8dc7f1330b0892397f5d2dd7e7058b18f66";
21
+ }
22
+ const previewSource = useSelector(previewStore, _temp);
23
+ const pageScaffoldContext = useSelector(previewStore, _temp2);
24
+ const [agentChatKey, setAgentChatKey] = React.useState(0);
25
+ const [composerFocusKey, setComposerFocusKey] = React.useState(0);
26
+ const { pathname } = useLocation();
27
+ const projectSlug = useProjectSlug();
28
+ let t0;
29
+ if ($[1] !== projectSlug) {
30
+ t0 = projectQueries.getBySlug(projectSlug);
31
+ $[1] = projectSlug;
32
+ $[2] = t0;
33
+ } else t0 = $[2];
34
+ const { data: project } = useQuery(t0);
35
+ const isLiveSource = previewSource === "live";
36
+ let t1;
37
+ let t2;
38
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
39
+ t1 = () => {
40
+ setComposerFocusKey(_temp3);
41
+ };
42
+ t2 = [];
43
+ $[3] = t1;
44
+ $[4] = t2;
45
+ } else {
46
+ t1 = $[3];
47
+ t2 = $[4];
48
+ }
49
+ React.useEffect(t1, t2);
50
+ let t3;
51
+ if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
52
+ t3 = /* @__PURE__ */ jsx("p", {
53
+ className: "flex-1 text-base font-semibold",
54
+ children: "Agent Chat"
55
+ });
56
+ $[5] = t3;
57
+ } else t3 = $[5];
58
+ let t4;
59
+ if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
60
+ t4 = /* @__PURE__ */ jsx(Button, {
61
+ type: "button",
62
+ variant: "ghost",
63
+ size: "icon-sm",
64
+ "aria-label": "Start a new Agent Chat",
65
+ onClick: () => {
66
+ previewStore.send({ type: "clearAgentChatPageScaffoldContext" });
67
+ setAgentChatKey(_temp4);
68
+ setComposerFocusKey(_temp5);
69
+ },
70
+ children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
71
+ });
72
+ $[6] = t4;
73
+ } else t4 = $[6];
74
+ let t5;
75
+ if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
76
+ t5 = /* @__PURE__ */ jsxs("div", {
77
+ className: "flex items-center gap-1 p-2",
78
+ children: [
79
+ t3,
80
+ t4,
81
+ /* @__PURE__ */ jsx(Button, {
82
+ type: "button",
83
+ variant: "ghost",
84
+ size: "icon-sm",
85
+ "aria-label": "Close",
86
+ onClick: _temp6,
87
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
88
+ })
89
+ ]
90
+ });
91
+ $[7] = t5;
92
+ } else t5 = $[7];
93
+ let t6;
94
+ if ($[8] !== isLiveSource) {
95
+ t6 = isLiveSource && /* @__PURE__ */ jsx("div", {
96
+ className: "p-2 pb-0",
97
+ children: /* @__PURE__ */ jsxs(Alert, { children: [
98
+ /* @__PURE__ */ jsx(Lock, { className: "size-4" }),
99
+ /* @__PURE__ */ jsx(AlertTitle, { children: "Live Source is read-only" }),
100
+ /* @__PURE__ */ jsxs(AlertDescription, {
101
+ className: "space-y-3",
102
+ children: [/* @__PURE__ */ jsx("p", { children: "Agent Chat can inspect live content, but edits require switching to Draft Source." }), /* @__PURE__ */ jsx(Button, {
103
+ type: "button",
104
+ size: "sm",
105
+ onClick: _temp7,
106
+ children: "Switch to draft"
107
+ })]
108
+ })
109
+ ] })
110
+ });
111
+ $[8] = isLiveSource;
112
+ $[9] = t6;
113
+ } else t6 = $[9];
114
+ let t7;
115
+ if ($[10] !== agentChatKey || $[11] !== composerFocusKey || $[12] !== pageScaffoldContext || $[13] !== pathname || $[14] !== previewSource || $[15] !== project) {
116
+ t7 = project ? /* @__PURE__ */ jsx(AgentChatThread, {
117
+ projectId: project.id,
118
+ currentPath: pathname,
119
+ source: previewSource,
120
+ focusKey: composerFocusKey,
121
+ pageScaffoldContext: pageScaffoldContext ?? void 0
122
+ }, `${agentChatKey}:${pageScaffoldContext?.id ?? 0}`) : /* @__PURE__ */ jsx("div", {
123
+ className: "text-muted-foreground flex flex-1 items-center justify-center p-6 text-sm",
124
+ children: "Loading project…"
125
+ });
126
+ $[10] = agentChatKey;
127
+ $[11] = composerFocusKey;
128
+ $[12] = pageScaffoldContext;
129
+ $[13] = pathname;
130
+ $[14] = previewSource;
131
+ $[15] = project;
132
+ $[16] = t7;
133
+ } else t7 = $[16];
134
+ let t8;
135
+ if ($[17] !== t6 || $[18] !== t7) {
136
+ t8 = /* @__PURE__ */ jsxs(Fragment, { children: [t5, /* @__PURE__ */ jsxs("div", {
137
+ className: "flex min-h-0 flex-1 flex-col",
138
+ children: [t6, t7]
139
+ })] });
140
+ $[17] = t6;
141
+ $[18] = t7;
142
+ $[19] = t8;
143
+ } else t8 = $[19];
144
+ return t8;
145
+ };
146
+ function _temp(state) {
147
+ return state.context.previewSource;
148
+ }
149
+ function _temp2(state_0) {
150
+ return state_0.context.agentChatPageScaffoldContext;
151
+ }
152
+ function _temp3(key) {
153
+ return key + 1;
154
+ }
155
+ function _temp4(key_0) {
156
+ return key_0 + 1;
157
+ }
158
+ function _temp5(key_1) {
159
+ return key_1 + 1;
160
+ }
161
+ function _temp6() {
162
+ return previewStore.send({ type: "closeAgentChatSidebar" });
163
+ }
164
+ function _temp7() {
165
+ return previewStore.send({
166
+ type: "setPreviewSource",
167
+ source: "draft"
168
+ });
169
+ }
170
+
171
+ //#endregion
172
+ export { AgentChatSidebar };
@@ -1,7 +1,7 @@
1
- import { previewStore } from "../../preview/previewStore.js";
2
1
  import { getAuthCookieHeader } from "../../../lib/auth.js";
3
2
  import { getApiClient, getApiUrl, getEnvironmentName } from "../../../lib/api-client.js";
4
3
  import { cn } from "../../../lib/utils.js";
4
+ import { previewStore } from "../../preview/previewStore.js";
5
5
  import { buildAgentChatToolLabelContext, getToolCallLabel } from "../agent-chat-labels.js";
6
6
  import { AgentToolCallCard } from "./AgentToolCallCard.js";
7
7
  import { c } from "react/compiler-runtime";
@@ -106,9 +106,9 @@ const markdownComponents = {
106
106
  };
107
107
  const MessageBubble = (t0) => {
108
108
  const $ = c(34);
109
- if ($[0] !== "ac29c5bab5ba745d35d55b8522bbfdd5d2f187fdcb71b67d53addf6539f9af82") {
109
+ if ($[0] !== "f3b8a7664c3b99923b26867f113f97eb01d8277569c870abd2bc42ddb6f634bf") {
110
110
  for (let $i = 0; $i < 34; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
111
- $[0] = "ac29c5bab5ba745d35d55b8522bbfdd5d2f187fdcb71b67d53addf6539f9af82";
111
+ $[0] = "f3b8a7664c3b99923b26867f113f97eb01d8277569c870abd2bc42ddb6f634bf";
112
112
  }
113
113
  const { message, source, isThinkingActive, activeThinkingDurationKeys, thinkingDurations, toolLabelContext, onApprovalResponse } = t0;
114
114
  const isUser = message.role === "user";
@@ -158,7 +158,7 @@ const MessageBubble = (t0) => {
158
158
  if (text != null) {
159
159
  if (text.trim().length === 0) return null;
160
160
  return /* @__PURE__ */ jsx(Streamdown, {
161
- className: cn("wrap-break-words space-y-2", !isUser && "pl-6"),
161
+ className: "wrap-break-words space-y-2",
162
162
  components: markdownComponents,
163
163
  controls: false,
164
164
  isAnimating: !isUser,
@@ -226,9 +226,9 @@ const MessageBubble = (t0) => {
226
226
  };
227
227
  const AgentThinkingIndicator = (t0) => {
228
228
  const $ = c(7);
229
- if ($[0] !== "ac29c5bab5ba745d35d55b8522bbfdd5d2f187fdcb71b67d53addf6539f9af82") {
229
+ if ($[0] !== "f3b8a7664c3b99923b26867f113f97eb01d8277569c870abd2bc42ddb6f634bf") {
230
230
  for (let $i = 0; $i < 7; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
231
- $[0] = "ac29c5bab5ba745d35d55b8522bbfdd5d2f187fdcb71b67d53addf6539f9af82";
231
+ $[0] = "f3b8a7664c3b99923b26867f113f97eb01d8277569c870abd2bc42ddb6f634bf";
232
232
  }
233
233
  const { durationSeconds } = t0;
234
234
  if (durationSeconds !== void 0) {
@@ -517,7 +517,7 @@ const AgentChatThread = ({ projectId, currentPath, source, disabled, focusKey =
517
517
  className: "flex min-h-0 flex-1 flex-col",
518
518
  children: [/* @__PURE__ */ jsx("div", {
519
519
  ref: messagesScrollRef,
520
- className: "min-h-0 flex-1 overflow-y-auto border-t p-4",
520
+ className: "min-h-0 flex-1 overflow-y-auto border-t px-2 py-4",
521
521
  children: /* @__PURE__ */ jsxs("div", {
522
522
  className: "flex min-h-full flex-col justify-end",
523
523
  children: [
@@ -552,7 +552,7 @@ const AgentChatThread = ({ projectId, currentPath, source, disabled, focusKey =
552
552
  })
553
553
  }), /* @__PURE__ */ jsx("form", {
554
554
  onSubmit: handleSubmit,
555
- className: "border-border border-t p-4",
555
+ className: "border-border border-t px-2 py-4",
556
556
  children: /* @__PURE__ */ jsxs("div", {
557
557
  className: "border-input focus-within:border-ring focus-within:ring-ring/50 flex items-center gap-2 rounded-2xl border px-3 transition-colors focus-within:ring-[3px]",
558
558
  children: [/* @__PURE__ */ jsx(Textarea, {
@@ -566,7 +566,7 @@ const AgentChatThread = ({ projectId, currentPath, source, disabled, focusKey =
566
566
  }
567
567
  },
568
568
  disabled,
569
- placeholder: disabled ? "Switch to draft to chat" : "Ask for changes or inspect this page…",
569
+ placeholder: disabled ? "Switch to draft to chat" : "Ask for anything",
570
570
  className: "max-h-32 min-h-10 resize-none border-0 bg-inherit! px-0 py-4 leading-6 shadow-none focus-visible:ring-0"
571
571
  }), isLoading ? /* @__PURE__ */ jsx(Button, {
572
572
  type: "button",