camox 0.29.0 → 0.31.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.
@@ -0,0 +1,200 @@
1
+ //#region src/features/agent-chat/agent-chat-labels.ts
2
+ const TOOL_LABELS = {
3
+ listPages: "List pages",
4
+ getPage: "Inspect page",
5
+ getBlock: "Inspect block",
6
+ getBlocks: "Inspect blocks",
7
+ listBlockTypes: "List block types",
8
+ describeBlockTypes: "Describe block types",
9
+ listLayouts: "List layouts",
10
+ createBlock: "Create block",
11
+ editBlock: "Edit block",
12
+ moveBlock: "Move block",
13
+ deleteBlock: "Delete block",
14
+ createPage: "Create page",
15
+ updatePage: "Update page",
16
+ setPageLayout: "Set page layout",
17
+ setPageMetaTitle: "Set meta title",
18
+ setPageMetaDescription: "Set meta description",
19
+ deletePage: "Delete page",
20
+ publishPage: "Publish page",
21
+ unpublishPage: "Unpublish page",
22
+ discardPageChanges: "Discard page changes"
23
+ };
24
+ function getToolLabel(name) {
25
+ return TOOL_LABELS[name] ?? name;
26
+ }
27
+ function isRecord(value) {
28
+ return !!value && typeof value === "object" && !Array.isArray(value);
29
+ }
30
+ function getNumber(value) {
31
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
32
+ }
33
+ function getString(value) {
34
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
35
+ }
36
+ function getToolCallInput(part) {
37
+ const input = part.input;
38
+ if (isRecord(input)) return input;
39
+ const args = part.arguments;
40
+ if (isRecord(args)) return args;
41
+ if (typeof args !== "string") return {};
42
+ try {
43
+ const parsed = JSON.parse(args);
44
+ return isRecord(parsed) ? parsed : {};
45
+ } catch {
46
+ return {};
47
+ }
48
+ }
49
+ function getToolOutput(part) {
50
+ if (part.type === "tool-call") return part.output;
51
+ if (part.type !== "tool-result") return void 0;
52
+ return part.output ?? part.result;
53
+ }
54
+ function rememberPage(value, maps) {
55
+ if (!isRecord(value)) return;
56
+ const fullPath = getString(value.fullPath);
57
+ if (!fullPath) return;
58
+ const id = getNumber(value.id) ?? void 0;
59
+ if (id !== void 0) maps.pagesById.set(id, {
60
+ id,
61
+ fullPath
62
+ });
63
+ }
64
+ function rememberBlock(value, maps) {
65
+ if (!isRecord(value)) return;
66
+ const id = getNumber(value.id);
67
+ if (id === null) return;
68
+ const summary = getString(value.summary);
69
+ const type = getString(value.type);
70
+ const typeTitle = type ? maps.blockTypeTitlesByType.get(type) : null;
71
+ const label = summary ?? typeTitle ?? type;
72
+ if (label) maps.blockLabelsById.set(id, label);
73
+ }
74
+ function rememberBlockType(value, maps) {
75
+ if (!isRecord(value)) return;
76
+ const type = getString(value.type) ?? getString(value.blockId);
77
+ const title = getString(value.title);
78
+ if (type && title) maps.blockTypeTitlesByType.set(type, title);
79
+ }
80
+ function rememberToolOutput(part, output, maps) {
81
+ if (part.name === "listPages" && Array.isArray(output)) {
82
+ output.forEach((page) => rememberPage(page, maps));
83
+ return;
84
+ }
85
+ if ((part.name === "listBlockTypes" || part.name === "describeBlockTypes") && isRecord(output)) {
86
+ const blockTypes = output.blockTypes;
87
+ if (Array.isArray(blockTypes)) blockTypes.forEach((type) => rememberBlockType(type, maps));
88
+ return;
89
+ }
90
+ if (part.name === "getBlocks" && Array.isArray(output)) {
91
+ output.forEach((item) => {
92
+ if (isRecord(item)) rememberBlock(item.block, maps);
93
+ });
94
+ return;
95
+ }
96
+ if (!isRecord(output)) return;
97
+ rememberPage(output.page, maps);
98
+ rememberBlock(output.block, maps);
99
+ rememberBlock(output, maps);
100
+ const blocks = output.blocks;
101
+ if (Array.isArray(blocks)) blocks.forEach((block) => rememberBlock(block, maps));
102
+ }
103
+ function getPageFromInput(input, context) {
104
+ const id = getNumber(input.id);
105
+ if (id !== null) return context.pagesById.get(id) ?? {
106
+ id,
107
+ fullPath: `#${id}`
108
+ };
109
+ const path = getString(input.path);
110
+ return path ? { fullPath: path } : null;
111
+ }
112
+ function formatPageTarget(page, currentPath) {
113
+ if (!page) return "page";
114
+ if (page.fullPath === currentPath) return "current page";
115
+ if (page.fullPath.startsWith("/")) return `${page.fullPath} page`;
116
+ return `page ${page.fullPath}`;
117
+ }
118
+ function formatBlockTarget(id, context) {
119
+ if (id === null) return "block";
120
+ const label = context.blockLabelsById.get(id);
121
+ return label ? `${label} block` : `block #${id}`;
122
+ }
123
+ function getSourcePrefix(input, runContext) {
124
+ return input.source === "live" || runContext.source === "live" ? "live " : "";
125
+ }
126
+ function getCreatePageTarget(input) {
127
+ const pathSegment = getString(input.pathSegment);
128
+ const nickname = getString(input.nickname);
129
+ if (pathSegment) return `/${pathSegment} page`;
130
+ if (nickname) return `${nickname} page`;
131
+ return "page";
132
+ }
133
+ function getBlockTypeTarget(input, context) {
134
+ const type = getString(input.type);
135
+ if (!type) return "block";
136
+ return `${context.blockTypeTitlesByType.get(type) ?? type} block`;
137
+ }
138
+ function buildAgentChatToolLabelContext(params) {
139
+ const maps = {
140
+ pagesById: /* @__PURE__ */ new Map(),
141
+ blockLabelsById: /* @__PURE__ */ new Map(),
142
+ blockTypeTitlesByType: /* @__PURE__ */ new Map()
143
+ };
144
+ const toolCallsById = /* @__PURE__ */ new Map();
145
+ for (const message of params.messages) for (const part of message.parts) {
146
+ if (part.type === "tool-call") {
147
+ toolCallsById.set(part.id, part);
148
+ const output = getToolOutput(part);
149
+ if (output !== void 0) rememberToolOutput(part, output, maps);
150
+ continue;
151
+ }
152
+ if (part.type !== "tool-result") continue;
153
+ const toolCallId = part.toolCallId;
154
+ if (typeof toolCallId !== "string") continue;
155
+ const toolCall = toolCallsById.get(toolCallId);
156
+ if (!toolCall) continue;
157
+ const output = getToolOutput(part);
158
+ if (output !== void 0) rememberToolOutput(toolCall, output, maps);
159
+ }
160
+ return {
161
+ currentPath: params.currentPath,
162
+ source: params.source,
163
+ toolCallContextById: params.toolCallContextById ?? /* @__PURE__ */ new Map(),
164
+ pagesById: maps.pagesById,
165
+ blockLabelsById: maps.blockLabelsById,
166
+ blockTypeTitlesByType: maps.blockTypeTitlesByType
167
+ };
168
+ }
169
+ function getToolCallLabel(part, context) {
170
+ const input = getToolCallInput(part);
171
+ const runContext = context.toolCallContextById.get(part.id) ?? {
172
+ currentPath: context.currentPath,
173
+ source: context.source
174
+ };
175
+ switch (part.name) {
176
+ case "getPage": return `Inspect ${getSourcePrefix(input, runContext)}${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
177
+ case "getBlock": return `Inspect ${getSourcePrefix(input, runContext)}${formatBlockTarget(getNumber(input.id), context)}`;
178
+ case "getBlocks": {
179
+ const ids = Array.isArray(input.ids) ? input.ids : [];
180
+ return `Inspect ${getSourcePrefix(input, runContext)}${ids.length || "multiple"} blocks`;
181
+ }
182
+ case "createBlock": return `Create ${getBlockTypeTarget(input, context)}`;
183
+ case "editBlock": return `Edit ${formatBlockTarget(getNumber(input.id), context)}`;
184
+ case "moveBlock": return `Move ${formatBlockTarget(getNumber(input.id), context)}`;
185
+ case "deleteBlock": return `Delete ${formatBlockTarget(getNumber(input.id), context)}`;
186
+ case "createPage": return `Create ${getCreatePageTarget(input)}`;
187
+ case "updatePage": return `Update ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
188
+ case "setPageLayout": return `Set layout for ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
189
+ case "setPageMetaTitle": return `Set meta title for ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
190
+ case "setPageMetaDescription": return `Set meta description for ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
191
+ case "deletePage": return `Delete ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
192
+ case "publishPage": return `Publish ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
193
+ case "unpublishPage": return `Unpublish ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
194
+ case "discardPageChanges": return `Discard changes to ${formatPageTarget(getPageFromInput(input, context), runContext.currentPath)}`;
195
+ default: return getToolLabel(part.name);
196
+ }
197
+ }
198
+
199
+ //#endregion
200
+ export { buildAgentChatToolLabelContext, getToolCallLabel };
@@ -0,0 +1,207 @@
1
+ import { previewStore } from "../../preview/previewStore.js";
2
+ import { PreviewSideSheet, Sheet } from "../../preview/components/PreviewSideSheet.js";
3
+ import { useProjectSlug } from "../../../lib/auth.js";
4
+ import { projectQueries } from "../../../lib/queries.js";
5
+ import { AgentChatThread } from "./AgentChatThread.js";
6
+ import { c } from "react/compiler-runtime";
7
+ import { useQuery } from "@tanstack/react-query";
8
+ import { useLocation } from "@tanstack/react-router";
9
+ import { useSelector } from "@xstate/store-react";
10
+ import * as React from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { Button } from "@camox/ui/button";
13
+ import { Info, Lock, Plus, X } from "lucide-react";
14
+ import { Alert, AlertDescription, AlertTitle } from "@camox/ui/alert";
15
+
16
+ //#region src/features/agent-chat/components/AgentChatSheet.tsx
17
+ const AgentChatSheet = () => {
18
+ const $ = c(25);
19
+ if ($[0] !== "66d93480e99ec8c64f18df9857d7ce7ad49013bef915961bf486d380834988ee") {
20
+ for (let $i = 0; $i < 25; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
21
+ $[0] = "66d93480e99ec8c64f18df9857d7ce7ad49013bef915961bf486d380834988ee";
22
+ }
23
+ const isOpen = useSelector(previewStore, _temp);
24
+ const previewSource = useSelector(previewStore, _temp2);
25
+ const pageScaffoldContext = useSelector(previewStore, _temp3);
26
+ const [agentChatKey, setAgentChatKey] = React.useState(0);
27
+ const [composerFocusKey, setComposerFocusKey] = React.useState(0);
28
+ const { pathname } = useLocation();
29
+ const projectSlug = useProjectSlug();
30
+ let t0;
31
+ if ($[1] !== projectSlug) {
32
+ t0 = projectQueries.getBySlug(projectSlug);
33
+ $[1] = projectSlug;
34
+ $[2] = t0;
35
+ } else t0 = $[2];
36
+ const { data: project } = useQuery(t0);
37
+ const isLiveSource = previewSource === "live";
38
+ let t1;
39
+ let t2;
40
+ if ($[3] !== isOpen) {
41
+ t1 = () => {
42
+ if (!isOpen) return;
43
+ setComposerFocusKey(_temp4);
44
+ };
45
+ t2 = [isOpen];
46
+ $[3] = isOpen;
47
+ $[4] = t1;
48
+ $[5] = t2;
49
+ } else {
50
+ t1 = $[4];
51
+ t2 = $[5];
52
+ }
53
+ React.useEffect(t1, t2);
54
+ const handleOpenChange = _temp5;
55
+ let t3;
56
+ if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
57
+ t3 = /* @__PURE__ */ jsx(Sheet.SheetTitle, {
58
+ className: "flex-1",
59
+ children: "Agent Chat"
60
+ });
61
+ $[6] = t3;
62
+ } else t3 = $[6];
63
+ let t4;
64
+ if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
65
+ t4 = /* @__PURE__ */ jsx(Button, {
66
+ type: "button",
67
+ variant: "ghost",
68
+ size: "icon-sm",
69
+ "aria-label": "Start a new Agent Chat",
70
+ onClick: () => {
71
+ previewStore.send({ type: "clearAgentChatPageScaffoldContext" });
72
+ setAgentChatKey(_temp6);
73
+ setComposerFocusKey(_temp7);
74
+ },
75
+ children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
76
+ });
77
+ $[7] = t4;
78
+ } else t4 = $[7];
79
+ let t5;
80
+ if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
81
+ t5 = /* @__PURE__ */ jsxs("div", {
82
+ className: "flex items-center gap-1",
83
+ children: [
84
+ t3,
85
+ t4,
86
+ /* @__PURE__ */ jsx(Sheet.SheetClose, {
87
+ render: /* @__PURE__ */ jsx(Button, {
88
+ type: "button",
89
+ variant: "ghost",
90
+ size: "icon-sm",
91
+ "aria-label": "Close"
92
+ }),
93
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
94
+ })
95
+ ]
96
+ });
97
+ $[8] = t5;
98
+ } else t5 = $[8];
99
+ let t6;
100
+ if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
101
+ t6 = /* @__PURE__ */ jsxs(Sheet.SheetHeader, {
102
+ className: "gap-4 pb-0",
103
+ children: [t5, /* @__PURE__ */ jsxs(Alert, { children: [
104
+ /* @__PURE__ */ jsx(Info, { className: "size-4" }),
105
+ /* @__PURE__ */ jsx(AlertTitle, { children: "Camox is most powerful in your coding agent" }),
106
+ /* @__PURE__ */ jsx(AlertDescription, { children: "Use Claude Code or Codex to manage your site with both code and content access." })
107
+ ] })]
108
+ });
109
+ $[9] = t6;
110
+ } else t6 = $[9];
111
+ let t7;
112
+ if ($[10] !== isLiveSource) {
113
+ t7 = isLiveSource && /* @__PURE__ */ jsx("div", {
114
+ className: "p-4 pb-0",
115
+ children: /* @__PURE__ */ jsxs(Alert, { children: [
116
+ /* @__PURE__ */ jsx(Lock, { className: "size-4" }),
117
+ /* @__PURE__ */ jsx(AlertTitle, { children: "Live Source is read-only" }),
118
+ /* @__PURE__ */ jsxs(AlertDescription, {
119
+ className: "space-y-3",
120
+ children: [/* @__PURE__ */ jsx("p", { children: "Agent Chat can inspect live content, but edits require switching to Draft Source." }), /* @__PURE__ */ jsx(Button, {
121
+ type: "button",
122
+ size: "sm",
123
+ onClick: _temp8,
124
+ children: "Switch to draft"
125
+ })]
126
+ })
127
+ ] })
128
+ });
129
+ $[10] = isLiveSource;
130
+ $[11] = t7;
131
+ } else t7 = $[11];
132
+ let t8;
133
+ if ($[12] !== agentChatKey || $[13] !== composerFocusKey || $[14] !== pageScaffoldContext || $[15] !== pathname || $[16] !== previewSource || $[17] !== project) {
134
+ t8 = project ? /* @__PURE__ */ jsx(AgentChatThread, {
135
+ projectId: project.id,
136
+ currentPath: pathname,
137
+ source: previewSource,
138
+ focusKey: composerFocusKey,
139
+ pageScaffoldContext: pageScaffoldContext ?? void 0
140
+ }, `${agentChatKey}:${pageScaffoldContext?.id ?? 0}`) : /* @__PURE__ */ jsx("div", {
141
+ className: "text-muted-foreground flex flex-1 items-center justify-center p-6 text-sm",
142
+ children: "Loading project…"
143
+ });
144
+ $[12] = agentChatKey;
145
+ $[13] = composerFocusKey;
146
+ $[14] = pageScaffoldContext;
147
+ $[15] = pathname;
148
+ $[16] = previewSource;
149
+ $[17] = project;
150
+ $[18] = t8;
151
+ } else t8 = $[18];
152
+ let t9;
153
+ if ($[19] !== t7 || $[20] !== t8) {
154
+ t9 = /* @__PURE__ */ jsxs("div", {
155
+ className: "flex min-h-0 flex-1 flex-col",
156
+ children: [t7, t8]
157
+ });
158
+ $[19] = t7;
159
+ $[20] = t8;
160
+ $[21] = t9;
161
+ } else t9 = $[21];
162
+ let t10;
163
+ if ($[22] !== isOpen || $[23] !== t9) {
164
+ t10 = /* @__PURE__ */ jsxs(PreviewSideSheet, {
165
+ open: isOpen,
166
+ onOpenChange: handleOpenChange,
167
+ keepMounted: true,
168
+ showCloseButton: false,
169
+ initialFocus: false,
170
+ children: [t6, t9]
171
+ });
172
+ $[22] = isOpen;
173
+ $[23] = t9;
174
+ $[24] = t10;
175
+ } else t10 = $[24];
176
+ return t10;
177
+ };
178
+ function _temp(state) {
179
+ return state.context.isAgentChatSheetOpen;
180
+ }
181
+ function _temp2(state_0) {
182
+ return state_0.context.previewSource;
183
+ }
184
+ function _temp3(state_1) {
185
+ return state_1.context.agentChatPageScaffoldContext;
186
+ }
187
+ function _temp4(key) {
188
+ return key + 1;
189
+ }
190
+ function _temp5(open) {
191
+ if (!open) previewStore.send({ type: "closeAgentChatSheet" });
192
+ }
193
+ function _temp6(key_0) {
194
+ return key_0 + 1;
195
+ }
196
+ function _temp7(key_1) {
197
+ return key_1 + 1;
198
+ }
199
+ function _temp8() {
200
+ return previewStore.send({
201
+ type: "setPreviewSource",
202
+ source: "draft"
203
+ });
204
+ }
205
+
206
+ //#endregion
207
+ export { AgentChatSheet };