@agent-native/core 0.124.3 → 0.124.4
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 +1 -1
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +10 -0
- package/corpus/core/docs/content/cloneable-saas.mdx +9 -9
- package/corpus/core/docs/content/creating-templates.mdx +2 -2
- package/corpus/core/docs/content/deployment.mdx +23 -0
- package/corpus/core/docs/content/faq.mdx +4 -4
- package/corpus/core/docs/content/key-concepts.mdx +1 -1
- package/corpus/core/docs/content/multi-app-workspace.mdx +1 -1
- package/corpus/core/docs/content/syncing-template-changes.mdx +1 -1
- package/corpus/core/docs/content/template-analytics-developers.mdx +2 -2
- package/corpus/core/docs/content/template-analytics.mdx +2 -2
- package/corpus/core/docs/content/template-assets-developers.mdx +3 -3
- package/corpus/core/docs/content/template-assets.mdx +2 -2
- package/corpus/core/docs/content/template-brain-developers.mdx +2 -2
- package/corpus/core/docs/content/template-brain.mdx +1 -1
- package/corpus/core/docs/content/template-calendar-developers.mdx +1 -1
- package/corpus/core/docs/content/template-calendar.mdx +1 -1
- package/corpus/core/docs/content/template-chat.mdx +1 -1
- package/corpus/core/docs/content/template-clips-developers.mdx +4 -4
- package/corpus/core/docs/content/template-clips.mdx +2 -2
- package/corpus/core/docs/content/template-content-developers.mdx +2 -2
- package/corpus/core/docs/content/template-content.mdx +1 -1
- package/corpus/core/docs/content/template-design-brand-and-figma.mdx +1 -1
- package/corpus/core/docs/content/template-design-developers.mdx +2 -2
- package/corpus/core/docs/content/template-design.mdx +2 -2
- package/corpus/core/docs/content/template-dispatch-developers.mdx +5 -5
- package/corpus/core/docs/content/template-forms-developers.mdx +1 -1
- package/corpus/core/docs/content/template-forms.mdx +1 -1
- package/corpus/core/docs/content/template-mail-developers.mdx +2 -2
- package/corpus/core/docs/content/template-mail.mdx +2 -2
- package/corpus/core/docs/content/template-plan-developers.mdx +3 -3
- package/corpus/core/docs/content/template-plan.mdx +3 -3
- package/corpus/core/docs/content/template-slides-developers.mdx +4 -4
- package/corpus/core/docs/content/template-slides.mdx +1 -1
- package/corpus/core/docs/content/what-is-agent-native.mdx +5 -5
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/auth-policy.ts +1 -1
- package/corpus/core/src/agent/production-agent.ts +14 -1
- package/corpus/core/src/agent/run-manager.ts +6 -13
- package/corpus/core/src/cli/index.ts +11 -1
- package/corpus/core/src/client/route-chunk-recovery.ts +18 -7
- package/corpus/core/src/db/client.ts +7 -6
- package/corpus/core/src/db/migrations.ts +2 -1
- package/corpus/core/src/deploy/build.ts +290 -4
- package/corpus/core/src/deploy/nitro-preset.ts +50 -0
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/corpus/core/src/scripts/call-agent.ts +4 -2
- package/corpus/core/src/server/agent-discovery.ts +1 -0
- package/corpus/core/src/server/agent-run-context.ts +13 -3
- package/corpus/core/src/server/request-context.ts +2 -0
- package/corpus/core/src/shared/cloudflare-globals.d.ts +2 -1
- package/corpus/core/src/shared/runtime.ts +2 -0
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +6 -6
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/vite/client.ts +20 -1
- package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +9 -2
- package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +3 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -1
- package/corpus/templates/analytics/app/global.css +10 -9
- package/corpus/templates/analytics/app/hooks/use-chart-tooltip-portal.ts +59 -18
- package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +14 -2
- package/corpus/templates/analytics/app/i18n-data.ts +102 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +232 -2
- package/corpus/templates/analytics/changelog/2026-07-27-chart-tooltips-stay-beside-the-cursor-when-they-extend-beyon.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-chat-no-longer-claims-a-data-source-is-disconnected-when-it-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-changes-can-be-undone-with-cmd-z-and-actions-menu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-redo-with-cmd-shift-z-and-actions-menu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-skeletons-are-subtler-and-smoother.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +61 -20
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -3
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +7 -32
- package/corpus/templates/calendar/changelog/2026-07-27-calendar-s-sidebar-now-uses-a-cleaner-layout-with-fewer-util.md +6 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +14 -6
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +32 -37
- package/corpus/templates/content/changelog/2026-07-27-content-sidebar-page-lists-now-load-with-quiet-skeletons-and.md +6 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +16 -18
- package/corpus/templates/design/changelog/2026-07-27-collapsed-sidebar-keeps-the-agent-native-logo.md +6 -0
- package/corpus/templates/dispatch/AGENTS.md +7 -2
- package/corpus/templates/dispatch/actions/view-screen.ts +3 -2
- package/corpus/templates/dispatch/changelog/2026-07-17-curated-template-remixes.md +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-27-creating-an-app-from-a-curated-template-now-uses-clearer-mor.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-27-thread-debug-accepts-request-run-ids.md +6 -0
- package/corpus/templates/slides/actions/create-design-system.ts +10 -2
- package/corpus/templates/slides/actions/import-file.ts +173 -1
- package/corpus/templates/slides/actions/list-slide-comments.ts +16 -1
- package/corpus/templates/slides/actions/update-design-system.ts +13 -2
- package/corpus/templates/slides/app/components/editor/AssetLibraryPanel.tsx +35 -12
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +36 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +3 -0
- package/corpus/templates/slides/app/lib/dynamic-import.ts +63 -0
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +3 -2
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +5 -2
- package/corpus/templates/slides/app/pages/DesignSystems.tsx +9 -1
- package/corpus/templates/slides/server/db/schema.ts +11 -0
- package/corpus/templates/slides/server/handlers/assets.ts +64 -19
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +38 -0
- package/corpus/templates/slides/server/plugins/db.ts +20 -0
- package/corpus/templates/slides/shared/design-system-validation.ts +62 -0
- package/dist/a2a/auth-policy.js +1 -1
- package/dist/a2a/auth-policy.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +4 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/cli/index.js +8 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/route-chunk-recovery.d.ts.map +1 -1
- package/dist/client/route-chunk-recovery.js +16 -7
- package/dist/client/route-chunk-recovery.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +4 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +2 -1
- package/dist/db/migrations.js.map +1 -1
- package/dist/deploy/build.d.ts +25 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +227 -4
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/nitro-preset.d.ts +8 -0
- package/dist/deploy/nitro-preset.d.ts.map +1 -0
- package/dist/deploy/nitro-preset.js +36 -0
- package/dist/deploy/nitro-preset.js.map +1 -0
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +2 -1
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +4 -2
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +1 -0
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/agent-run-context.d.ts.map +1 -1
- package/dist/server/agent-run-context.js +10 -3
- package/dist/server/agent-run-context.js.map +1 -1
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/runtime.d.ts.map +1 -1
- package/dist/shared/runtime.js +2 -0
- package/dist/shared/runtime.js.map +1 -1
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/templates/default/app/i18n/ar-SA.ts +6 -6
- package/dist/templates/default/app/i18n/de-DE.ts +6 -6
- package/dist/templates/default/app/i18n/en-US.ts +6 -6
- package/dist/templates/default/app/i18n/es-ES.ts +6 -6
- package/dist/templates/default/app/i18n/fr-FR.ts +6 -6
- package/dist/templates/default/app/i18n/hi-IN.ts +6 -6
- package/dist/templates/default/app/i18n/ja-JP.ts +6 -6
- package/dist/templates/default/app/i18n/ko-KR.ts +6 -6
- package/dist/templates/default/app/i18n/pt-BR.ts +6 -6
- package/dist/templates/default/app/i18n/zh-CN.ts +6 -6
- package/dist/templates/default/app/i18n/zh-TW.ts +6 -6
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/vite/client.d.ts +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +16 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/cloneable-saas.mdx +9 -9
- package/docs/content/creating-templates.mdx +2 -2
- package/docs/content/deployment.mdx +23 -0
- package/docs/content/faq.mdx +4 -4
- package/docs/content/key-concepts.mdx +1 -1
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/docs/content/syncing-template-changes.mdx +1 -1
- package/docs/content/template-analytics-developers.mdx +2 -2
- package/docs/content/template-analytics.mdx +2 -2
- package/docs/content/template-assets-developers.mdx +3 -3
- package/docs/content/template-assets.mdx +2 -2
- package/docs/content/template-brain-developers.mdx +2 -2
- package/docs/content/template-brain.mdx +1 -1
- package/docs/content/template-calendar-developers.mdx +1 -1
- package/docs/content/template-calendar.mdx +1 -1
- package/docs/content/template-chat.mdx +1 -1
- package/docs/content/template-clips-developers.mdx +4 -4
- package/docs/content/template-clips.mdx +2 -2
- package/docs/content/template-content-developers.mdx +2 -2
- package/docs/content/template-content.mdx +1 -1
- package/docs/content/template-design-brand-and-figma.mdx +1 -1
- package/docs/content/template-design-developers.mdx +2 -2
- package/docs/content/template-design.mdx +2 -2
- package/docs/content/template-dispatch-developers.mdx +5 -5
- package/docs/content/template-forms-developers.mdx +1 -1
- package/docs/content/template-forms.mdx +1 -1
- package/docs/content/template-mail-developers.mdx +2 -2
- package/docs/content/template-mail.mdx +2 -2
- package/docs/content/template-plan-developers.mdx +3 -3
- package/docs/content/template-plan.mdx +3 -3
- package/docs/content/template-slides-developers.mdx +4 -4
- package/docs/content/template-slides.mdx +1 -1
- package/docs/content/what-is-agent-native.mdx +5 -5
- package/package.json +1 -1
- package/src/a2a/auth-policy.ts +1 -1
- package/src/agent/production-agent.ts +14 -1
- package/src/agent/run-manager.ts +6 -13
- package/src/cli/index.ts +11 -1
- package/src/client/route-chunk-recovery.ts +18 -7
- package/src/db/client.ts +7 -6
- package/src/db/migrations.ts +2 -1
- package/src/deploy/build.ts +290 -4
- package/src/deploy/nitro-preset.ts +50 -0
- package/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/src/scripts/call-agent.ts +4 -2
- package/src/server/agent-discovery.ts +1 -0
- package/src/server/agent-run-context.ts +13 -3
- package/src/server/request-context.ts +2 -0
- package/src/shared/cloudflare-globals.d.ts +2 -1
- package/src/shared/runtime.ts +2 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/src/templates/default/app/i18n/en-US.ts +6 -6
- package/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/src/templates/default/app/i18n/zh-CN.ts +6 -6
- package/src/templates/default/app/i18n/zh-TW.ts +6 -6
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/src/vite/client.ts +20 -1
- /package/corpus/templates/slides/server/routes/api/assets/{[filename].delete.ts → [id].delete.ts} +0 -0
|
@@ -3,8 +3,10 @@ import { useT } from "@agent-native/core/client/i18n";
|
|
|
3
3
|
import { IconUpload, IconTrash, IconLoader2, IconX } from "@tabler/icons-react";
|
|
4
4
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
|
+
import { toast } from "sonner";
|
|
6
7
|
|
|
7
8
|
interface Asset {
|
|
9
|
+
id: string;
|
|
8
10
|
url: string;
|
|
9
11
|
filename: string;
|
|
10
12
|
size: number;
|
|
@@ -67,33 +69,54 @@ export default function AssetLibraryPanel({
|
|
|
67
69
|
const files = e.target.files;
|
|
68
70
|
if (!files || files.length === 0) return;
|
|
69
71
|
setUploading(true);
|
|
72
|
+
const failures: string[] = [];
|
|
70
73
|
try {
|
|
71
74
|
for (const file of Array.from(files)) {
|
|
72
75
|
const form = new FormData();
|
|
73
76
|
form.append("file", file);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetch(`${appBasePath()}/api/assets/upload`, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
body: form,
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
const body = await res.json().catch(() => null);
|
|
84
|
+
failures.push(
|
|
85
|
+
`${file.name}: ${body?.error || `HTTP ${res.status}`}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
failures.push(`${file.name}: upload failed`);
|
|
90
|
+
}
|
|
78
91
|
}
|
|
79
92
|
await fetchAssets();
|
|
80
|
-
|
|
93
|
+
if (failures.length > 0) {
|
|
94
|
+
toast.error(t("raw.assetUploadFailed"), {
|
|
95
|
+
description: failures.join("\n"),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
81
98
|
} finally {
|
|
82
99
|
setUploading(false);
|
|
83
100
|
e.target.value = "";
|
|
84
101
|
}
|
|
85
102
|
};
|
|
86
103
|
|
|
87
|
-
const handleDelete = async (
|
|
104
|
+
const handleDelete = async (id: string) => {
|
|
88
105
|
try {
|
|
89
|
-
await fetch(
|
|
90
|
-
`${appBasePath()}/api/assets/${encodeURIComponent(
|
|
106
|
+
const res = await fetch(
|
|
107
|
+
`${appBasePath()}/api/assets/${encodeURIComponent(id)}`,
|
|
91
108
|
{
|
|
92
109
|
method: "DELETE",
|
|
93
110
|
},
|
|
94
111
|
);
|
|
95
|
-
|
|
96
|
-
|
|
112
|
+
if (!res.ok) {
|
|
113
|
+
toast.error(t("raw.assetDeleteFailed"));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
setAssets((prev) => prev.filter((a) => a.id !== id));
|
|
117
|
+
} catch {
|
|
118
|
+
toast.error(t("raw.assetDeleteFailed"));
|
|
119
|
+
}
|
|
97
120
|
};
|
|
98
121
|
|
|
99
122
|
const handleSelect = (url: string) => {
|
|
@@ -185,7 +208,7 @@ export default function AssetLibraryPanel({
|
|
|
185
208
|
<div className="grid grid-cols-3 gap-2">
|
|
186
209
|
{assets.map((asset) => (
|
|
187
210
|
<div
|
|
188
|
-
key={asset.
|
|
211
|
+
key={asset.id}
|
|
189
212
|
className="group relative aspect-square rounded-md overflow-hidden border border-border bg-muted"
|
|
190
213
|
>
|
|
191
214
|
<button
|
|
@@ -204,7 +227,7 @@ export default function AssetLibraryPanel({
|
|
|
204
227
|
<button
|
|
205
228
|
onClick={(e) => {
|
|
206
229
|
e.stopPropagation();
|
|
207
|
-
handleDelete(asset.
|
|
230
|
+
handleDelete(asset.id);
|
|
208
231
|
}}
|
|
209
232
|
className="absolute top-0.5 right-0.5 w-5 h-5 bg-black/70 text-white/70 hover:text-red-400 flex items-center justify-center rounded-full opacity-0 group-hover:opacity-100 transition-opacity"
|
|
210
233
|
aria-label={`Delete ${asset.filename}`}
|
|
@@ -164,6 +164,16 @@ const backgroundOptions = [
|
|
|
164
164
|
"bg-[#ffffff]",
|
|
165
165
|
];
|
|
166
166
|
|
|
167
|
+
const HEX_COLOR_PATTERN = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
168
|
+
|
|
169
|
+
/** Native <input type="color"> only accepts 3/6-digit hex — fall back to
|
|
170
|
+
* black for gradients or other raw CSS values so the picker still opens. */
|
|
171
|
+
function toColorInputValue(background: string | undefined): string {
|
|
172
|
+
return background && HEX_COLOR_PATTERN.test(background)
|
|
173
|
+
? background
|
|
174
|
+
: "#000000";
|
|
175
|
+
}
|
|
176
|
+
|
|
167
177
|
/** Popover anchored to a button ref */
|
|
168
178
|
function ToolbarPopover({
|
|
169
179
|
open,
|
|
@@ -294,6 +304,9 @@ export default function EditorToolbar({
|
|
|
294
304
|
}, []);
|
|
295
305
|
|
|
296
306
|
const activeAspectRatio: AspectRatio = aspectRatio ?? DEFAULT_ASPECT_RATIO;
|
|
307
|
+
const isCustomBackground =
|
|
308
|
+
!!currentSlide?.background &&
|
|
309
|
+
!backgroundOptions.includes(currentSlide.background);
|
|
297
310
|
const [layoutOpen, setLayoutOpen] = useState(false);
|
|
298
311
|
const layoutRef = useRef<HTMLButtonElement>(null);
|
|
299
312
|
|
|
@@ -544,6 +557,29 @@ export default function EditorToolbar({
|
|
|
544
557
|
}`}
|
|
545
558
|
/>
|
|
546
559
|
))}
|
|
560
|
+
<label
|
|
561
|
+
className={`relative w-10 h-7 rounded-md border cursor-pointer overflow-hidden transition-all ${
|
|
562
|
+
isCustomBackground
|
|
563
|
+
? "border-[#609FF8] ring-1 ring-[#609FF8]/30"
|
|
564
|
+
: "border-border hover:border-foreground/20"
|
|
565
|
+
}`}
|
|
566
|
+
style={{
|
|
567
|
+
background: isCustomBackground
|
|
568
|
+
? currentSlide.background
|
|
569
|
+
: "conic-gradient(from 180deg, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)",
|
|
570
|
+
}}
|
|
571
|
+
title={t("editorToolbar.customColor")}
|
|
572
|
+
>
|
|
573
|
+
<input
|
|
574
|
+
type="color"
|
|
575
|
+
value={toColorInputValue(currentSlide.background)}
|
|
576
|
+
onChange={(e) => {
|
|
577
|
+
onUpdateSlide!({ background: e.target.value });
|
|
578
|
+
}}
|
|
579
|
+
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
|
580
|
+
aria-label={t("editorToolbar.customColor")}
|
|
581
|
+
/>
|
|
582
|
+
</label>
|
|
547
583
|
</div>
|
|
548
584
|
</div>
|
|
549
585
|
|
|
@@ -61,6 +61,8 @@ const messages = {
|
|
|
61
61
|
deckOptions: "Deck options",
|
|
62
62
|
generatingImage: "Generating image...",
|
|
63
63
|
noAssetsYet: "No assets yet.",
|
|
64
|
+
assetUploadFailed: "Some uploads failed",
|
|
65
|
+
assetDeleteFailed: "Failed to delete asset",
|
|
64
66
|
contentMarkdown: "Content (Markdown)",
|
|
65
67
|
speakerNotes: "Speaker Notes",
|
|
66
68
|
speakerNotesForSlide: "Speaker Notes — Slide {{index}} / {{count}}",
|
|
@@ -192,6 +194,7 @@ const messages = {
|
|
|
192
194
|
slideSettings: "Slide settings",
|
|
193
195
|
layout: "Layout",
|
|
194
196
|
background: "Background",
|
|
197
|
+
customColor: "Custom color",
|
|
195
198
|
media: "Media",
|
|
196
199
|
generateImage: "Generate Image",
|
|
197
200
|
assetLibrary: "Asset Library",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const RELOAD_GUARD_KEY = "slides:export-module-reload-attempted";
|
|
2
|
+
|
|
3
|
+
// Chrome/Firefox/Safari all use this wording (or close to it) for a dynamic
|
|
4
|
+
// `import()` whose chunk 404s — typically because the app was redeployed
|
|
5
|
+
// after the page loaded and the browser still references an old, now-missing
|
|
6
|
+
// chunk hash. Calling `import()` again with the *same* specifier re-requests
|
|
7
|
+
// the same now-missing URL and fails immediately, so a plain retry never
|
|
8
|
+
// recovers this case — only a full reload picks up the current chunk map.
|
|
9
|
+
const STALE_CHUNK_PATTERN =
|
|
10
|
+
/fetch dynamically imported module|error loading dynamically imported module/i;
|
|
11
|
+
|
|
12
|
+
function isStaleChunkError(error: unknown): boolean {
|
|
13
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14
|
+
return STALE_CHUNK_PATTERN.test(message);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function hasAlreadyReloaded(): boolean {
|
|
18
|
+
try {
|
|
19
|
+
return sessionStorage.getItem(RELOAD_GUARD_KEY) === "1";
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function markReloaded(): void {
|
|
26
|
+
try {
|
|
27
|
+
sessionStorage.setItem(RELOAD_GUARD_KEY, "1");
|
|
28
|
+
} catch {
|
|
29
|
+
// sessionStorage unavailable (e.g. private mode) — reload once anyway,
|
|
30
|
+
// worst case we reload more than once instead of not recovering at all.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lazy `import()` calls for export libraries (jsPDF, modern-screenshot,
|
|
36
|
+
* dom-to-pptx) fail with "Failed to fetch dynamically imported module" when
|
|
37
|
+
* the chunk is momentarily unreachable (flaky network) or stale after a
|
|
38
|
+
* redeploy. For the stale-chunk case, retrying the same specifier is a no-op,
|
|
39
|
+
* so we reload the page once to pick up the current chunk map; for anything
|
|
40
|
+
* else, a same-specifier retry can still clear a transient blip.
|
|
41
|
+
*/
|
|
42
|
+
export async function importExportModule<T>(
|
|
43
|
+
loader: () => Promise<T>,
|
|
44
|
+
): Promise<T> {
|
|
45
|
+
try {
|
|
46
|
+
return await loader();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (isStaleChunkError(error) && !hasAlreadyReloaded()) {
|
|
49
|
+
markReloaded();
|
|
50
|
+
window.location.reload();
|
|
51
|
+
// The reload navigates away; never resolve so callers don't proceed
|
|
52
|
+
// against a page that's about to unload.
|
|
53
|
+
return new Promise<T>(() => {});
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return await loader();
|
|
57
|
+
} catch {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"Couldn't load the export module. Refresh the page and try again.",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* doesn't shrink the captured pixels.
|
|
12
12
|
*/
|
|
13
13
|
import { type AspectRatio, getAspectRatioDims } from "./aspect-ratios";
|
|
14
|
+
import { importExportModule } from "./dynamic-import";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Cross-origin <img> elements without an explicit `crossOrigin="anonymous"`
|
|
@@ -99,8 +100,8 @@ export async function exportDeckAsPdf(
|
|
|
99
100
|
// JPEG (vs PNG) keeps a typical 8-slide deck under ~10 MB instead of
|
|
100
101
|
// ~100 MB — at 0.92 quality the difference is invisible on slide content.
|
|
101
102
|
const [{ domToJpeg }, { jsPDF }] = await Promise.all([
|
|
102
|
-
import("modern-screenshot"),
|
|
103
|
-
import("jspdf"),
|
|
103
|
+
importExportModule(() => import("modern-screenshot")),
|
|
104
|
+
importExportModule(() => import("jspdf")),
|
|
104
105
|
]);
|
|
105
106
|
|
|
106
107
|
// Web fonts (Poppins) must finish loading before capture — otherwise
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AspectRatio, getAspectRatioDims } from "./aspect-ratios";
|
|
2
|
+
import { importExportModule } from "./dynamic-import";
|
|
2
3
|
import {
|
|
3
4
|
findSlideExportSource,
|
|
4
5
|
preloadImagesWithCors,
|
|
@@ -83,7 +84,7 @@ export async function addSpeakerNotesToPptxBlob(
|
|
|
83
84
|
const hasNotes = slides.some((slide) => slide.notes?.trim());
|
|
84
85
|
if (!hasNotes) return blob;
|
|
85
86
|
|
|
86
|
-
const { default: JSZip } = await import("jszip");
|
|
87
|
+
const { default: JSZip } = await importExportModule(() => import("jszip"));
|
|
87
88
|
const zip = await JSZip.loadAsync(blob);
|
|
88
89
|
|
|
89
90
|
const contentTypesFile = zip.file("[Content_Types].xml");
|
|
@@ -317,7 +318,9 @@ export async function exportDeckAsPptx(
|
|
|
317
318
|
slides: PptxExportSlide[],
|
|
318
319
|
aspectRatio?: AspectRatio,
|
|
319
320
|
): Promise<void> {
|
|
320
|
-
const { exportToPptx } = await
|
|
321
|
+
const { exportToPptx } = await importExportModule(
|
|
322
|
+
() => import("dom-to-pptx"),
|
|
323
|
+
);
|
|
321
324
|
|
|
322
325
|
if (typeof document !== "undefined" && document.fonts?.ready) {
|
|
323
326
|
await document.fonts.ready;
|
|
@@ -18,6 +18,7 @@ import { Button } from "@/components/ui/button";
|
|
|
18
18
|
import { useDesignSystems } from "@/hooks/use-design-systems";
|
|
19
19
|
|
|
20
20
|
import type { DesignSystemData } from "../../shared/api";
|
|
21
|
+
import { missingDesignSystemDataFields } from "../../shared/design-system-validation";
|
|
21
22
|
|
|
22
23
|
export default function DesignSystems() {
|
|
23
24
|
const t = useT();
|
|
@@ -52,7 +53,14 @@ export default function DesignSystems() {
|
|
|
52
53
|
|
|
53
54
|
const parseDesignData = (dataStr: string): DesignSystemData | null => {
|
|
54
55
|
try {
|
|
55
|
-
|
|
56
|
+
const parsed = JSON.parse(dataStr) as DesignSystemData;
|
|
57
|
+
// DesignSystemCard reads colors.* / typography.* unconditionally, down
|
|
58
|
+
// to nested fields like typography.headingFont. Rows written before
|
|
59
|
+
// create/update validation existed can have `colors: {}` and still
|
|
60
|
+
// pass a truthy check, so reuse the same nested-field validator the
|
|
61
|
+
// actions use rather than only checking the top-level objects exist.
|
|
62
|
+
if (missingDesignSystemDataFields(parsed).length > 0) return null;
|
|
63
|
+
return parsed;
|
|
56
64
|
} catch {
|
|
57
65
|
return null;
|
|
58
66
|
}
|
|
@@ -57,6 +57,17 @@ export const deckShareLinks = table("deck_share_links", {
|
|
|
57
57
|
createdAt: text("created_at").notNull().default(now()),
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
export const uploadedAssets = table("uploaded_assets", {
|
|
61
|
+
id: text("id").primaryKey(),
|
|
62
|
+
filename: text("filename").notNull(),
|
|
63
|
+
url: text("url").notNull(),
|
|
64
|
+
type: text("type").notNull(),
|
|
65
|
+
size: integer("size").notNull(),
|
|
66
|
+
provider: text("provider"),
|
|
67
|
+
ownerEmail: text("owner_email").notNull(),
|
|
68
|
+
createdAt: text("created_at").notNull().default(now()),
|
|
69
|
+
});
|
|
70
|
+
|
|
60
71
|
export const slideComments = table("slide_comments", {
|
|
61
72
|
id: text("id").primaryKey(),
|
|
62
73
|
deckId: text("deck_id").notNull(),
|
|
@@ -3,11 +3,16 @@ import path from "path";
|
|
|
3
3
|
import { uploadFile } from "@agent-native/core/file-upload";
|
|
4
4
|
import { getSession } from "@agent-native/core/server";
|
|
5
5
|
import { runWithRequestContext } from "@agent-native/core/server";
|
|
6
|
+
import { and, desc, eq } from "drizzle-orm";
|
|
6
7
|
import {
|
|
7
8
|
defineEventHandler,
|
|
9
|
+
getRouterParam,
|
|
8
10
|
setResponseStatus,
|
|
9
11
|
readMultipartFormData,
|
|
10
12
|
} from "h3";
|
|
13
|
+
import { nanoid } from "nanoid";
|
|
14
|
+
|
|
15
|
+
import { getDb, schema } from "../db/index.js";
|
|
11
16
|
|
|
12
17
|
export const MAX_ASSET_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
13
18
|
|
|
@@ -19,6 +24,14 @@ export interface UploadedAsset {
|
|
|
19
24
|
provider?: string;
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
export interface ListedUploadedAsset {
|
|
28
|
+
id: string;
|
|
29
|
+
url: string;
|
|
30
|
+
filename: string;
|
|
31
|
+
size: number;
|
|
32
|
+
createdAt: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
async function requireSession(event: Parameters<typeof getSession>[0]) {
|
|
23
36
|
const session = await getSession(event).catch(() => null);
|
|
24
37
|
if (!session?.email) {
|
|
@@ -137,13 +150,29 @@ export async function uploadImageAsset(args: {
|
|
|
137
150
|
throw err;
|
|
138
151
|
}
|
|
139
152
|
|
|
140
|
-
|
|
153
|
+
const asset: UploadedAsset = {
|
|
141
154
|
url: result.url,
|
|
142
155
|
filename: args.originalName,
|
|
143
156
|
type: args.type || "application/octet-stream",
|
|
144
157
|
size: args.data.length,
|
|
145
158
|
provider: result.provider,
|
|
146
159
|
};
|
|
160
|
+
|
|
161
|
+
// Record the upload so it shows up in GET /api/assets — only the URL and
|
|
162
|
+
// metadata are stored, never the file bytes (those live with the provider).
|
|
163
|
+
const db = getDb();
|
|
164
|
+
await db.insert(schema.uploadedAssets).values({
|
|
165
|
+
id: nanoid(),
|
|
166
|
+
filename: asset.filename,
|
|
167
|
+
url: asset.url,
|
|
168
|
+
type: asset.type,
|
|
169
|
+
size: asset.size,
|
|
170
|
+
provider: asset.provider ?? null,
|
|
171
|
+
ownerEmail: args.email,
|
|
172
|
+
createdAt: new Date().toISOString(),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return asset;
|
|
147
176
|
}
|
|
148
177
|
|
|
149
178
|
/**
|
|
@@ -185,37 +214,53 @@ export const uploadAsset = defineEventHandler(async (event) => {
|
|
|
185
214
|
});
|
|
186
215
|
|
|
187
216
|
/**
|
|
188
|
-
* GET /api/assets — list
|
|
189
|
-
*
|
|
190
|
-
* Asset history used to come from scanning `public/uploads/<tenant>/` on disk.
|
|
191
|
-
* That source is gone now (see `uploadImageAsset` for the reasoning). Until
|
|
192
|
-
* we plumb a SQL-backed asset index that records each upload (so we can list
|
|
193
|
-
* across providers), this endpoint returns an empty list. The AssetLibraryPanel
|
|
194
|
-
* still works for uploading new images and selecting them for the current
|
|
195
|
-
* editing session.
|
|
217
|
+
* GET /api/assets — list assets this user has uploaded, most recent first.
|
|
196
218
|
*/
|
|
197
219
|
export const listAssets = defineEventHandler(async (event) => {
|
|
198
220
|
const session = await requireSession(event);
|
|
199
221
|
if (!session) {
|
|
200
222
|
return { error: "Unauthorized" };
|
|
201
223
|
}
|
|
202
|
-
|
|
224
|
+
const db = getDb();
|
|
225
|
+
const rows: ListedUploadedAsset[] = await db
|
|
226
|
+
.select({
|
|
227
|
+
id: schema.uploadedAssets.id,
|
|
228
|
+
url: schema.uploadedAssets.url,
|
|
229
|
+
filename: schema.uploadedAssets.filename,
|
|
230
|
+
size: schema.uploadedAssets.size,
|
|
231
|
+
createdAt: schema.uploadedAssets.createdAt,
|
|
232
|
+
})
|
|
233
|
+
.from(schema.uploadedAssets)
|
|
234
|
+
.where(eq(schema.uploadedAssets.ownerEmail, session.email))
|
|
235
|
+
.orderBy(desc(schema.uploadedAssets.createdAt));
|
|
236
|
+
return rows;
|
|
203
237
|
});
|
|
204
238
|
|
|
205
239
|
/**
|
|
206
|
-
* DELETE /api/assets/:
|
|
207
|
-
*
|
|
208
|
-
* the
|
|
209
|
-
* the
|
|
240
|
+
* DELETE /api/assets/:id — removes the upload from this user's asset library
|
|
241
|
+
* index. Keyed by the row's unique id (not filename) since two uploads can
|
|
242
|
+
* share the same original filename. The underlying file may still exist with
|
|
243
|
+
* the storage provider (Builder.io, S3, etc.) — deleting it there requires
|
|
244
|
+
* that provider's own API — but it no longer appears in this app's library.
|
|
210
245
|
*/
|
|
211
246
|
export const deleteAsset = defineEventHandler(async (event) => {
|
|
212
247
|
const session = await requireSession(event);
|
|
213
248
|
if (!session) {
|
|
214
249
|
return { error: "Unauthorized" };
|
|
215
250
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
251
|
+
const id = getRouterParam(event, "id");
|
|
252
|
+
if (!id) {
|
|
253
|
+
setResponseStatus(event, 400);
|
|
254
|
+
return { error: "Asset id is required" };
|
|
255
|
+
}
|
|
256
|
+
const db = getDb();
|
|
257
|
+
await db
|
|
258
|
+
.delete(schema.uploadedAssets)
|
|
259
|
+
.where(
|
|
260
|
+
and(
|
|
261
|
+
eq(schema.uploadedAssets.id, decodeURIComponent(id)),
|
|
262
|
+
eq(schema.uploadedAssets.ownerEmail, session.email),
|
|
263
|
+
),
|
|
264
|
+
);
|
|
265
|
+
return { success: true };
|
|
221
266
|
});
|
|
@@ -9,6 +9,44 @@ function esc(text: string): string {
|
|
|
9
9
|
.replace(/"/g, """);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Render a page's embedded photo as the full-bleed slide background with the
|
|
14
|
+
* page's extracted text overlaid on top. Designed PDF pages (photo
|
|
15
|
+
* backgrounds, gradients, custom typography) have no reliable shape
|
|
16
|
+
* structure to reconstruct, so the embedded image is reused directly — but
|
|
17
|
+
* the vector/glyph text on the page is not something we can rasterize
|
|
18
|
+
* reliably headless, so the extracted text is drawn as real HTML on top
|
|
19
|
+
* instead of relying on the page's own (font-dependent) rendering.
|
|
20
|
+
*
|
|
21
|
+
* `pdf-parse`'s plain-text extraction carries no color/font metadata, so the
|
|
22
|
+
* heading accent color below is a stand-in, not a recovered value — when a
|
|
23
|
+
* subtitle is present (a content slide, not a title slide) it renders as a
|
|
24
|
+
* centered card with a divider rule so the two text roles stay visually
|
|
25
|
+
* distinct instead of collapsing into one flat paragraph.
|
|
26
|
+
*/
|
|
27
|
+
export function buildFullBleedImageSlideHtml(
|
|
28
|
+
imageUrl: string,
|
|
29
|
+
headingText?: string,
|
|
30
|
+
subtitleText?: string,
|
|
31
|
+
): string {
|
|
32
|
+
let overlay = "";
|
|
33
|
+
if (headingText && subtitleText) {
|
|
34
|
+
overlay = `\n <div style="position: absolute; left: 0; right: 0; bottom: 0; background: linear-gradient(to top, rgba(12,10,8,0.95) 0%, rgba(12,10,8,0.88) 55%, rgba(12,10,8,0.4) 82%, rgba(12,10,8,0) 100%); padding: 56px 56px 60px; text-align: center; font-family: 'Poppins', sans-serif;">
|
|
35
|
+
<div style="width: 72px; height: 3px; background: #d8b26a; margin: 0 auto 20px;"></div>
|
|
36
|
+
<h2 style="font-size: 30px; font-weight: 800; color: #d8b26a; line-height: 1.25; margin: 0 0 14px;">${esc(headingText)}</h2>
|
|
37
|
+
<p style="font-size: 19px; font-weight: 500; color: #fff; line-height: 1.5; margin: 0;">${esc(subtitleText)}</p>
|
|
38
|
+
</div>`;
|
|
39
|
+
} else if (headingText) {
|
|
40
|
+
overlay = `\n <div style="position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.15) 45%, rgba(0,0,0,0) 65%);"></div>
|
|
41
|
+
<div style="position: absolute; left: 0; right: 0; bottom: 0; padding: 60px 70px; font-family: 'Poppins', sans-serif;">
|
|
42
|
+
<h2 style="font-size: 40px; font-weight: 900; color: #fff; line-height: 1.15; letter-spacing: -1px; margin: 0;">${esc(headingText)}</h2>
|
|
43
|
+
</div>`;
|
|
44
|
+
}
|
|
45
|
+
return `<div class="fmd-slide" style="position: relative; width: 100%; height: 100%; overflow: hidden;">
|
|
46
|
+
<img src="${esc(imageUrl)}" alt="" style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;" />${overlay}
|
|
47
|
+
</div>`;
|
|
48
|
+
}
|
|
49
|
+
|
|
12
50
|
/** Wrap text in formatting tags based on run properties. */
|
|
13
51
|
function formatRun(run: ParsedTextRun): string {
|
|
14
52
|
let text = esc(run.content);
|
|
@@ -210,6 +210,26 @@ const runSlidesMigrations = runMigrations(
|
|
|
210
210
|
CREATE INDEX IF NOT EXISTS slide_comments_deck_created_idx ON slide_comments (deck_id, created_at);
|
|
211
211
|
CREATE INDEX IF NOT EXISTS slide_comments_deck_slide_created_idx ON slide_comments (deck_id, slide_id, created_at)`,
|
|
212
212
|
},
|
|
213
|
+
// v20: index of assets uploaded through the file-upload provider chain.
|
|
214
|
+
// GET /api/assets previously always returned [] (no persisted record of
|
|
215
|
+
// uploads), so the Asset Library panel could never show or re-select a
|
|
216
|
+
// file after uploading it. This table only stores the returned URL/
|
|
217
|
+
// metadata, never the file bytes.
|
|
218
|
+
{
|
|
219
|
+
version: 20,
|
|
220
|
+
name: "slides-uploaded-assets-table",
|
|
221
|
+
sql: `CREATE TABLE IF NOT EXISTS uploaded_assets (
|
|
222
|
+
id TEXT PRIMARY KEY,
|
|
223
|
+
filename TEXT NOT NULL,
|
|
224
|
+
url TEXT NOT NULL,
|
|
225
|
+
type TEXT NOT NULL,
|
|
226
|
+
size INTEGER NOT NULL,
|
|
227
|
+
provider TEXT,
|
|
228
|
+
owner_email TEXT NOT NULL,
|
|
229
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
230
|
+
);
|
|
231
|
+
CREATE INDEX IF NOT EXISTS uploaded_assets_owner_created_idx ON uploaded_assets (owner_email, created_at)`,
|
|
232
|
+
},
|
|
213
233
|
],
|
|
214
234
|
{ table: "slides_migrations" },
|
|
215
235
|
);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const REQUIRED_COLOR_KEYS = [
|
|
2
|
+
"primary",
|
|
3
|
+
"secondary",
|
|
4
|
+
"accent",
|
|
5
|
+
"background",
|
|
6
|
+
"surface",
|
|
7
|
+
"text",
|
|
8
|
+
"textMuted",
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const REQUIRED_TYPOGRAPHY_KEYS = [
|
|
12
|
+
"headingFont",
|
|
13
|
+
"bodyFont",
|
|
14
|
+
"headingWeight",
|
|
15
|
+
"bodyWeight",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The Design Systems page and slide renderers read data.colors.* and
|
|
20
|
+
* data.typography.* unconditionally (no optional chaining). A syntactically
|
|
21
|
+
* valid but incomplete `data` payload — e.g. from an interrupted generation —
|
|
22
|
+
* would otherwise persist and crash on the very next read. Shared between the
|
|
23
|
+
* create/update actions (write-time validation) and the Design Systems page
|
|
24
|
+
* (read-time validation of rows written before this check existed).
|
|
25
|
+
*/
|
|
26
|
+
export function missingDesignSystemDataFields(value: unknown): string[] {
|
|
27
|
+
const missing: string[] = [];
|
|
28
|
+
const record =
|
|
29
|
+
value && typeof value === "object"
|
|
30
|
+
? (value as Record<string, unknown>)
|
|
31
|
+
: {};
|
|
32
|
+
|
|
33
|
+
const colors =
|
|
34
|
+
record.colors && typeof record.colors === "object"
|
|
35
|
+
? (record.colors as Record<string, unknown>)
|
|
36
|
+
: null;
|
|
37
|
+
if (!colors) {
|
|
38
|
+
missing.push("colors");
|
|
39
|
+
} else {
|
|
40
|
+
for (const key of REQUIRED_COLOR_KEYS) {
|
|
41
|
+
if (typeof colors[key] !== "string" || !colors[key]) {
|
|
42
|
+
missing.push(`colors.${key}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const typography =
|
|
48
|
+
record.typography && typeof record.typography === "object"
|
|
49
|
+
? (record.typography as Record<string, unknown>)
|
|
50
|
+
: null;
|
|
51
|
+
if (!typography) {
|
|
52
|
+
missing.push("typography");
|
|
53
|
+
} else {
|
|
54
|
+
for (const key of REQUIRED_TYPOGRAPHY_KEYS) {
|
|
55
|
+
if (typeof typography[key] !== "string" || !typography[key]) {
|
|
56
|
+
missing.push(`typography.${key}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return missing;
|
|
62
|
+
}
|
package/dist/a2a/auth-policy.js
CHANGED
|
@@ -16,7 +16,7 @@ export function isA2AProductionRuntime() {
|
|
|
16
16
|
}
|
|
17
17
|
if (process.env.CF_PAGES === "1")
|
|
18
18
|
return true;
|
|
19
|
-
if ("__cf_env" in globalThis)
|
|
19
|
+
if ("__cf_env" in globalThis || "__env__" in globalThis)
|
|
20
20
|
return true;
|
|
21
21
|
if (process.env.VERCEL || process.env.VERCEL_ENV)
|
|
22
22
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-policy.js","sourceRoot":"","sources":["../../src/a2a/auth-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB;IACpC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,EAAE,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,OAAO,CAAC,GAAG,CAAC,wBAAwB;QACpC,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,EACpC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,UAAU,IAAI,UAAU;QAAE,OAAO,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-policy.js","sourceRoot":"","sources":["../../src/a2a/auth-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB;IACpC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,EAAE,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,OAAO,CAAC,GAAG,CAAC,wBAAwB;QACpC,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,EACpC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,UAAU,IAAI,UAAU,IAAI,SAAS,IAAI,UAAU;QAAE,OAAO,IAAI,CAAC;IACrE,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QAC5E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO,sBAAsB,EAAE,IAAI,sBAAsB,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAA2B;IAC/D,IAAI,sBAAsB,EAAE;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACjE,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;AAChC,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,iBAAiB,CAAC,IAA+B;IAC/D,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,OAAO,CACL,CAAC,KAAK,WAAW;QACjB,CAAC,KAAK,KAAK;QACX,CAAC,KAAK,kBAAkB;QACxB,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;QACpB,CAAC,KAAK,WAAW,CAClB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * A2A auth policy helpers shared by discovery, the JSON-RPC gate, and task\n * handlers. Serverless providers do not always expose `NODE_ENV=production`\n * consistently at runtime, so production-like A2A checks also look at the\n * provider flags those platforms set in deployed functions.\n */\nexport function isA2AProductionRuntime(): boolean {\n if (process.env.NODE_ENV === \"production\") return true;\n if (process.env.NETLIFY === \"true\" && process.env.NETLIFY_LOCAL !== \"true\") {\n return true;\n }\n if (\n process.env.AWS_LAMBDA_FUNCTION_NAME &&\n process.env.NETLIFY_LOCAL !== \"true\"\n ) {\n return true;\n }\n if (process.env.CF_PAGES === \"1\") return true;\n if (\"__cf_env\" in globalThis || \"__env__\" in globalThis) return true;\n if (process.env.VERCEL || process.env.VERCEL_ENV) return true;\n if (process.env.RENDER || process.env.FLY_APP_NAME || process.env.K_SERVICE) {\n return true;\n }\n return false;\n}\n\nexport function hasConfiguredA2ASecret(): boolean {\n return !!process.env.A2A_SECRET?.trim();\n}\n\nexport function shouldAdvertiseJwtA2AAuth(): boolean {\n return hasConfiguredA2ASecret() || isA2AProductionRuntime();\n}\n\n/**\n * True only when unsigned internal self-dispatch is acceptable: no A2A_SECRET\n * is configured AND we can positively identify a local/dev runtime. Everything\n * else — production, or any UNRECOGNIZED deployed/networked host — must fail\n * closed and require A2A_SECRET. `loopback` should be whether the inbound\n * request arrived over the loopback interface (127.0.0.1/::1); callers that\n * cannot determine the peer address pass `false`.\n *\n * NODE_ENV alone is deliberately NOT a trust grant: a self-hosted deployment\n * that doesn't set NODE_ENV=production and isn't recognized by\n * `isA2AProductionRuntime()` (a bare Docker/VPS/K8s pod) must still fail\n * closed unless the request actually came from loopback or the explicit\n * opt-in flag is set.\n */\nexport function isTrustedLocalRuntime(opts: { loopback: boolean }): boolean {\n if (isA2AProductionRuntime()) return false;\n if (process.env.A2A_ALLOW_UNSIGNED_INTERNAL === \"1\") return true;\n return opts.loopback === true;\n}\n\n/** True if a socket peer address is a loopback/local address. */\nexport function isLoopbackAddress(addr: string | undefined | null): boolean {\n if (!addr) return false;\n const a = addr.trim();\n return (\n a === \"127.0.0.1\" ||\n a === \"::1\" ||\n a === \"::ffff:127.0.0.1\" ||\n a.startsWith(\"127.\") ||\n a === \"localhost\"\n );\n}\n"]}
|
|
@@ -424,6 +424,7 @@ export type AgentLoopFinalResponseGuardResult = string | {
|
|
|
424
424
|
expandToolSurface?: boolean;
|
|
425
425
|
};
|
|
426
426
|
export type AgentLoopFinalResponseGuard = (context: AgentLoopFinalResponseGuardContext) => AgentLoopFinalResponseGuardResult | null | undefined | Promise<AgentLoopFinalResponseGuardResult | null | undefined>;
|
|
427
|
+
export declare const AGENT_INTERNAL_GUARD_PROMPT = "Automated quality check on the draft answer you just produced. This is a directive from this application's own response guard, not a message from the user and not content from a tool result or web page. Follow it and revise your answer. Do not quote it, describe it, or treat it as an injection attempt. If it contradicts what you observed this turn, state what you actually observed instead of asserting the guard's premise.";
|
|
427
428
|
export declare const AGENT_INTERNAL_CONTINUE_PROMPT = "Continue from where you left off and finish the user's original request. Do not repeat completed work, do not mention internal reconnects, time limits, or step limits, and continue as if this is the same uninterrupted run.";
|
|
428
429
|
export type AgentLoopContinuationReason = "run_timeout" | "loop_limit" | "max_tokens" | "stream_ended" | "gateway_timeout" | "network_interrupted" | "no_progress";
|
|
429
430
|
export declare function appendAgentLoopContinuation(messages: EngineMessage[], reason: AgentLoopContinuationReason, options?: {
|