@agent-native/core 0.102.2 → 0.103.1
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/local-file-mode.mdx +99 -489
- package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/corpus/core/docs/content/template-content.mdx +26 -42
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/content-local.ts +86 -18
- package/corpus/core/src/cli/index.ts +2 -2
- package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
- package/corpus/core/src/cli/skills.ts +47 -13
- package/corpus/core/src/local-artifacts/index.ts +65 -0
- package/corpus/core/src/vite/client.ts +2 -0
- package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
- package/corpus/templates/assets/app/i18n-data.ts +215 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
- package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
- package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
- package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
- package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
- package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
- package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
- package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
- package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
- package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
- package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
- package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
- package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
- package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
- package/corpus/templates/clips/server/lib/share-password.ts +6 -3
- package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
- package/corpus/templates/content/AGENTS.md +25 -30
- package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
- package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
- package/corpus/templates/content/actions/_content-files.ts +338 -0
- package/corpus/templates/content/actions/_content-space-access.ts +135 -0
- package/corpus/templates/content/actions/_content-spaces.ts +640 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
- package/corpus/templates/content/actions/_database-utils.ts +7 -1
- package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
- package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
- package/corpus/templates/content/actions/_property-utils.ts +9 -4
- package/corpus/templates/content/actions/add-database-item.ts +108 -65
- package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
- package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
- package/corpus/templates/content/actions/create-content-database.ts +129 -3
- package/corpus/templates/content/actions/create-document.ts +65 -55
- package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
- package/corpus/templates/content/actions/delete-content-database.ts +3 -0
- package/corpus/templates/content/actions/delete-document.ts +7 -11
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
- package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
- package/corpus/templates/content/actions/edit-document.ts +2 -28
- package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
- package/corpus/templates/content/actions/export-content-source.ts +47 -9
- package/corpus/templates/content/actions/get-document.ts +0 -14
- package/corpus/templates/content/actions/import-content-source.ts +43 -3
- package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
- package/corpus/templates/content/actions/list-documents.ts +12 -9
- package/corpus/templates/content/actions/move-document.ts +3 -14
- package/corpus/templates/content/actions/pull-document.ts +0 -23
- package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
- package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
- package/corpus/templates/content/actions/search-documents.ts +0 -34
- package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
- package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
- package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
- package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
- package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
- package/corpus/templates/content/actions/update-document.ts +0 -15
- package/corpus/templates/content/actions/view-screen.ts +2 -50
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
- package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
- package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
- package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
- package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
- package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
- package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
- package/corpus/templates/content/app/i18n-data.ts +75 -0
- package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
- package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
- package/corpus/templates/content/parity/matrix.md +2 -1
- package/corpus/templates/content/parity/matrix.ts +37 -1
- package/corpus/templates/content/scripts/dev-database.mjs +7 -5
- package/corpus/templates/content/server/db/schema.ts +113 -40
- package/corpus/templates/content/server/lib/document-context.ts +15 -1
- package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
- package/corpus/templates/content/server/plugins/db.ts +153 -0
- package/corpus/templates/content/shared/api.ts +17 -3
- package/corpus/templates/content/shared/builder-mdx.ts +163 -16
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
- package/corpus/templates/design/AGENTS.md +6 -1
- package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
- package/corpus/templates/design/actions/list-design-templates.ts +69 -21
- package/corpus/templates/design/actions/navigate.ts +2 -2
- package/corpus/templates/design/actions/view-screen.ts +15 -2
- package/corpus/templates/design/agent-native.app-skill.json +5 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
- package/corpus/templates/design/app/i18n-data.ts +21 -2
- package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
- package/corpus/templates/design/app/pages/Index.tsx +195 -24
- package/corpus/templates/design/app/pages/Templates.tsx +144 -55
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
- package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/mail/AGENTS.md +23 -18
- package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
- package/corpus/templates/mail/actions/mark-read.ts +131 -20
- package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
- package/corpus/templates/mail/actions/move-email.ts +30 -24
- package/corpus/templates/mail/actions/send-email.ts +73 -68
- package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +218 -209
- package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
- package/corpus/templates/mail/server/lib/email-state.ts +198 -113
- package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
- package/corpus/templates/mail/server/lib/jobs.ts +69 -74
- package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
- package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
- package/dist/cli/content-local.d.ts +1 -0
- package/dist/cli/content-local.d.ts.map +1 -1
- package/dist/cli/content-local.js +75 -17
- package/dist/cli/content-local.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills-content/content-skill.d.ts +1 -1
- package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/content-skill.js +36 -25
- package/dist/cli/skills-content/content-skill.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +47 -14
- package/dist/cli/skills.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/local-artifacts/index.d.ts +10 -0
- package/dist/local-artifacts/index.d.ts.map +1 -1
- package/dist/local-artifacts/index.js +39 -0
- package/dist/local-artifacts/index.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/local-file-mode.mdx +99 -489
- package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/docs/content/template-content.mdx +26 -42
- package/package.json +1 -1
- package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
|
@@ -16,6 +16,14 @@ export interface AgentNativeManifestRoot {
|
|
|
16
16
|
extensions?: string[];
|
|
17
17
|
include?: string[];
|
|
18
18
|
hide?: string[];
|
|
19
|
+
source?: {
|
|
20
|
+
type: "local-folder";
|
|
21
|
+
connectionId: string;
|
|
22
|
+
truthPolicy?:
|
|
23
|
+
| "database_primary"
|
|
24
|
+
| "source_primary"
|
|
25
|
+
| "reviewed_bidirectional";
|
|
26
|
+
};
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
export interface AgentNativeManifestApp {
|
|
@@ -73,6 +81,7 @@ export interface LoadedLocalArtifactRoot {
|
|
|
73
81
|
extensions: string[];
|
|
74
82
|
hide: string[];
|
|
75
83
|
include: string[];
|
|
84
|
+
source?: AgentNativeManifestRoot["source"];
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
export interface LoadedLocalArtifactApp {
|
|
@@ -267,6 +276,26 @@ function rootNameFromPath(rootPath: string): string {
|
|
|
267
276
|
function normalizeManifestRoot(value: unknown): AgentNativeManifestRoot | null {
|
|
268
277
|
if (typeof value === "string") return { path: value };
|
|
269
278
|
if (!isRecord(value) || typeof value.path !== "string") return null;
|
|
279
|
+
const truthPolicy:
|
|
280
|
+
| NonNullable<AgentNativeManifestRoot["source"]>["truthPolicy"]
|
|
281
|
+
| undefined =
|
|
282
|
+
value.source &&
|
|
283
|
+
isRecord(value.source) &&
|
|
284
|
+
(value.source.truthPolicy === "database_primary" ||
|
|
285
|
+
value.source.truthPolicy === "source_primary" ||
|
|
286
|
+
value.source.truthPolicy === "reviewed_bidirectional")
|
|
287
|
+
? value.source.truthPolicy
|
|
288
|
+
: undefined;
|
|
289
|
+
const source =
|
|
290
|
+
isRecord(value.source) &&
|
|
291
|
+
value.source.type === "local-folder" &&
|
|
292
|
+
typeof value.source.connectionId === "string"
|
|
293
|
+
? {
|
|
294
|
+
type: "local-folder" as const,
|
|
295
|
+
connectionId: value.source.connectionId,
|
|
296
|
+
...(truthPolicy ? { truthPolicy } : {}),
|
|
297
|
+
}
|
|
298
|
+
: undefined;
|
|
270
299
|
return {
|
|
271
300
|
name: typeof value.name === "string" ? value.name : undefined,
|
|
272
301
|
path: value.path,
|
|
@@ -275,6 +304,7 @@ function normalizeManifestRoot(value: unknown): AgentNativeManifestRoot | null {
|
|
|
275
304
|
extensions: normalizeExtensions(value.extensions),
|
|
276
305
|
include: asStringArray(value.include),
|
|
277
306
|
hide: asStringArray(value.hide),
|
|
307
|
+
...(source ? { source } : {}),
|
|
278
308
|
};
|
|
279
309
|
}
|
|
280
310
|
|
|
@@ -483,6 +513,7 @@ export async function getLocalArtifactApp(
|
|
|
483
513
|
extensions,
|
|
484
514
|
hide: [...DEFAULT_HIDE_PATTERNS, ...asStringArray(root.hide)],
|
|
485
515
|
include: asStringArray(root.include),
|
|
516
|
+
source: root.source,
|
|
486
517
|
};
|
|
487
518
|
});
|
|
488
519
|
|
|
@@ -683,6 +714,16 @@ export async function listLocalArtifactFiles(
|
|
|
683
714
|
return files.sort((a, b) => a.path.localeCompare(b.path));
|
|
684
715
|
}
|
|
685
716
|
|
|
717
|
+
export async function listConfiguredLocalArtifactFiles(
|
|
718
|
+
options: LocalArtifactOptions,
|
|
719
|
+
): Promise<LocalArtifactFileMeta[]> {
|
|
720
|
+
const app = await getLocalArtifactApp(options);
|
|
721
|
+
const files = (await Promise.all(app.roots.map((root) => walkRoot(root))))
|
|
722
|
+
.flat()
|
|
723
|
+
.filter((file) => !matchesPatterns(file.path, app.hide));
|
|
724
|
+
return files.sort((a, b) => a.path.localeCompare(b.path));
|
|
725
|
+
}
|
|
726
|
+
|
|
686
727
|
function rootForArtifactPath(
|
|
687
728
|
app: LoadedLocalArtifactApp,
|
|
688
729
|
artifactPath: string,
|
|
@@ -758,6 +799,30 @@ export async function readLocalArtifactFile(
|
|
|
758
799
|
}
|
|
759
800
|
}
|
|
760
801
|
|
|
802
|
+
export async function readConfiguredLocalArtifactFile(
|
|
803
|
+
options: LocalArtifactOptions & { path: string },
|
|
804
|
+
): Promise<LocalArtifactFile | null> {
|
|
805
|
+
const app = await getLocalArtifactApp(options);
|
|
806
|
+
const { root, safePath, absolutePath } = await resolveArtifactPath(
|
|
807
|
+
app,
|
|
808
|
+
options.path,
|
|
809
|
+
);
|
|
810
|
+
try {
|
|
811
|
+
const { content, stat } = readTextFileWithoutSymlink(root, absolutePath);
|
|
812
|
+
const meta = await fileMetaForPath(
|
|
813
|
+
root,
|
|
814
|
+
safePath,
|
|
815
|
+
absolutePath,
|
|
816
|
+
content,
|
|
817
|
+
stat,
|
|
818
|
+
);
|
|
819
|
+
return { ...meta, content };
|
|
820
|
+
} catch (error) {
|
|
821
|
+
if (errorCode(error) === "ENOENT") return null;
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
761
826
|
export async function writeLocalArtifactFile(
|
|
762
827
|
options: WriteLocalArtifactFileOptions & { path: string },
|
|
763
828
|
): Promise<LocalArtifactFileMeta> {
|
|
@@ -1969,6 +1969,7 @@ function ssrStubPlugin(packages: string[]): Plugin | null {
|
|
|
1969
1969
|
"FitAddon",
|
|
1970
1970
|
"Fragment",
|
|
1971
1971
|
"Image",
|
|
1972
|
+
"InputRule",
|
|
1972
1973
|
"Link",
|
|
1973
1974
|
"Map",
|
|
1974
1975
|
"Markdown",
|
|
@@ -2022,6 +2023,7 @@ function ssrStubPlugin(packages: string[]): Plugin | null {
|
|
|
2022
2023
|
"useEditor",
|
|
2023
2024
|
"useLocalRuntime",
|
|
2024
2025
|
"useMessagePartReasoning",
|
|
2026
|
+
"useMessagePartRuntime",
|
|
2025
2027
|
"useMessagePartText",
|
|
2026
2028
|
"useMessageRuntime",
|
|
2027
2029
|
"useThread",
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { useActionMutation, useT } from "@agent-native/core/client";
|
|
2
|
+
import {
|
|
3
|
+
ASPECT_RATIOS,
|
|
4
|
+
IMAGE_CATEGORIES,
|
|
5
|
+
type AspectRatio,
|
|
6
|
+
type ImageCategory,
|
|
7
|
+
} from "@shared/api";
|
|
8
|
+
import { IconTrash } from "@tabler/icons-react";
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import { Link } from "react-router";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
AlertDialog,
|
|
15
|
+
AlertDialogAction,
|
|
16
|
+
AlertDialogCancel,
|
|
17
|
+
AlertDialogContent,
|
|
18
|
+
AlertDialogDescription,
|
|
19
|
+
AlertDialogFooter,
|
|
20
|
+
AlertDialogHeader,
|
|
21
|
+
AlertDialogTitle,
|
|
22
|
+
} from "@/components/ui/alert-dialog";
|
|
23
|
+
import { Badge } from "@/components/ui/badge";
|
|
24
|
+
import { Button } from "@/components/ui/button";
|
|
25
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
26
|
+
import {
|
|
27
|
+
Dialog,
|
|
28
|
+
DialogContent,
|
|
29
|
+
DialogDescription,
|
|
30
|
+
DialogFooter,
|
|
31
|
+
DialogHeader,
|
|
32
|
+
DialogTitle,
|
|
33
|
+
} from "@/components/ui/dialog";
|
|
34
|
+
import { Input } from "@/components/ui/input";
|
|
35
|
+
import { Label } from "@/components/ui/label";
|
|
36
|
+
import {
|
|
37
|
+
Select,
|
|
38
|
+
SelectContent,
|
|
39
|
+
SelectItem,
|
|
40
|
+
SelectTrigger,
|
|
41
|
+
SelectValue,
|
|
42
|
+
} from "@/components/ui/select";
|
|
43
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
44
|
+
|
|
45
|
+
export function GenerationPresetsPanel({
|
|
46
|
+
libraryId,
|
|
47
|
+
presets,
|
|
48
|
+
}: {
|
|
49
|
+
libraryId: string;
|
|
50
|
+
presets: any[];
|
|
51
|
+
}) {
|
|
52
|
+
const t = useT();
|
|
53
|
+
const createPreset = useActionMutation("create-generation-preset");
|
|
54
|
+
const deletePreset = useActionMutation("delete-generation-preset");
|
|
55
|
+
const [open, setOpen] = useState(false);
|
|
56
|
+
const [confirmPresetId, setConfirmPresetId] = useState<string | null>(null);
|
|
57
|
+
const [title, setTitle] = useState("");
|
|
58
|
+
const [category, setCategory] = useState<ImageCategory>("social");
|
|
59
|
+
const [aspectRatio, setAspectRatio] = useState<AspectRatio>("1:1");
|
|
60
|
+
const [promptTemplate, setPromptTemplate] = useState("");
|
|
61
|
+
const [textPolicy, setTextPolicy] = useState(t("library.defaultTextPolicy"));
|
|
62
|
+
const [includeLogo, setIncludeLogo] = useState(false);
|
|
63
|
+
|
|
64
|
+
function reset() {
|
|
65
|
+
setTitle("");
|
|
66
|
+
setCategory("social");
|
|
67
|
+
setAspectRatio("1:1");
|
|
68
|
+
setPromptTemplate("");
|
|
69
|
+
setTextPolicy(t("library.defaultTextPolicy"));
|
|
70
|
+
setIncludeLogo(false);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function submit() {
|
|
74
|
+
const trimmed = title.trim();
|
|
75
|
+
if (!trimmed) return;
|
|
76
|
+
createPreset.mutate(
|
|
77
|
+
{
|
|
78
|
+
libraryId,
|
|
79
|
+
title: trimmed,
|
|
80
|
+
category,
|
|
81
|
+
aspectRatio,
|
|
82
|
+
imageSize: "2K",
|
|
83
|
+
promptTemplate: promptTemplate.trim() || undefined,
|
|
84
|
+
textPolicy,
|
|
85
|
+
referencePolicy: "auto",
|
|
86
|
+
includeLogo,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
onSuccess: () => {
|
|
90
|
+
toast.success(t("brandKitDetail.generationPresetCreated"));
|
|
91
|
+
reset();
|
|
92
|
+
setOpen(false);
|
|
93
|
+
},
|
|
94
|
+
onError: (error: Error) => {
|
|
95
|
+
toast.error(
|
|
96
|
+
error.message || t("brandKitDetail.couldNotCreatePreset"),
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div className="rounded-lg border border-border p-4">
|
|
105
|
+
<div className="flex items-center justify-between gap-3">
|
|
106
|
+
<div>
|
|
107
|
+
<h3 className="text-sm font-semibold">
|
|
108
|
+
{t("brandKitDetail.generationPresets")}
|
|
109
|
+
</h3>
|
|
110
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
111
|
+
{t("brandKitDetail.generationPresetsDescription")}
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
114
|
+
<Button variant="outline" size="sm" onClick={() => setOpen(true)}>
|
|
115
|
+
{t("brandKitDetail.new")}
|
|
116
|
+
</Button>
|
|
117
|
+
</div>
|
|
118
|
+
<div className="mt-3 space-y-2">
|
|
119
|
+
{presets.map((preset) => (
|
|
120
|
+
<div
|
|
121
|
+
key={preset.id}
|
|
122
|
+
className="flex items-start justify-between gap-3 rounded-md border border-border bg-background p-3"
|
|
123
|
+
>
|
|
124
|
+
<div className="min-w-0">
|
|
125
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
126
|
+
<Link
|
|
127
|
+
to={`/brand-kits/${libraryId}/presets/${preset.id}`}
|
|
128
|
+
className="truncate text-sm font-medium underline-offset-4 hover:underline"
|
|
129
|
+
>
|
|
130
|
+
{preset.title}
|
|
131
|
+
</Link>
|
|
132
|
+
<Badge variant="outline">{preset.aspectRatio}</Badge>
|
|
133
|
+
{preset.includeLogo ? (
|
|
134
|
+
<Badge variant="secondary">{t("brandKitDetail.logo")}</Badge>
|
|
135
|
+
) : null}
|
|
136
|
+
</div>
|
|
137
|
+
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
|
|
138
|
+
{preset.textPolicy || preset.description || preset.category}
|
|
139
|
+
</p>
|
|
140
|
+
</div>
|
|
141
|
+
<div className="flex shrink-0 items-center gap-1">
|
|
142
|
+
<Button variant="ghost" size="sm" asChild>
|
|
143
|
+
<Link to={`/brand-kits/${libraryId}/presets/${preset.id}`}>
|
|
144
|
+
{t("brandKitDetail.edit")}
|
|
145
|
+
</Link>
|
|
146
|
+
</Button>
|
|
147
|
+
<Button
|
|
148
|
+
variant="ghost"
|
|
149
|
+
size="icon"
|
|
150
|
+
className="h-8 w-8 text-muted-foreground hover:text-destructive"
|
|
151
|
+
aria-label={`${t("brandKitDetail.delete")} ${preset.title}`}
|
|
152
|
+
onClick={() => setConfirmPresetId(preset.id)}
|
|
153
|
+
>
|
|
154
|
+
<IconTrash className="h-4 w-4" />
|
|
155
|
+
</Button>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
))}
|
|
159
|
+
{!presets.length ? (
|
|
160
|
+
<p className="rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground">
|
|
161
|
+
{t("brandKitDetail.noPresetsYet")}
|
|
162
|
+
</p>
|
|
163
|
+
) : null}
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<AlertDialog
|
|
167
|
+
open={confirmPresetId !== null}
|
|
168
|
+
onOpenChange={(isOpen) => {
|
|
169
|
+
if (!isOpen) setConfirmPresetId(null);
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
<AlertDialogContent>
|
|
173
|
+
<AlertDialogHeader>
|
|
174
|
+
<AlertDialogTitle>
|
|
175
|
+
{t("brandKitDetail.deleteGenerationPreset")}
|
|
176
|
+
</AlertDialogTitle>
|
|
177
|
+
<AlertDialogDescription>
|
|
178
|
+
{t("brandKitDetail.deleteGenerationPresetDescription")}
|
|
179
|
+
</AlertDialogDescription>
|
|
180
|
+
</AlertDialogHeader>
|
|
181
|
+
<AlertDialogFooter>
|
|
182
|
+
<AlertDialogCancel>{t("brandKitDetail.cancel")}</AlertDialogCancel>
|
|
183
|
+
<AlertDialogAction
|
|
184
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
185
|
+
disabled={!confirmPresetId || deletePreset.isPending}
|
|
186
|
+
onClick={(event) => {
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
if (!confirmPresetId) return;
|
|
189
|
+
deletePreset.mutate(
|
|
190
|
+
{ id: confirmPresetId },
|
|
191
|
+
{
|
|
192
|
+
onSuccess: () => {
|
|
193
|
+
setConfirmPresetId(null);
|
|
194
|
+
toast.success(
|
|
195
|
+
t("brandKitDetail.generationPresetDeleted"),
|
|
196
|
+
);
|
|
197
|
+
},
|
|
198
|
+
onError: (error: Error) => {
|
|
199
|
+
toast.error(
|
|
200
|
+
error.message ||
|
|
201
|
+
t("brandKitDetail.couldNotDeletePreset"),
|
|
202
|
+
);
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
}}
|
|
207
|
+
>
|
|
208
|
+
{t("assetDetail.delete")}
|
|
209
|
+
</AlertDialogAction>
|
|
210
|
+
</AlertDialogFooter>
|
|
211
|
+
</AlertDialogContent>
|
|
212
|
+
</AlertDialog>
|
|
213
|
+
|
|
214
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
215
|
+
<DialogContent className="sm:max-w-lg">
|
|
216
|
+
<DialogHeader>
|
|
217
|
+
<DialogTitle>{t("brandKitDetail.newGenerationPreset")}</DialogTitle>
|
|
218
|
+
<DialogDescription>
|
|
219
|
+
{t("brandKitDetail.newGenerationPresetDescription")}
|
|
220
|
+
</DialogDescription>
|
|
221
|
+
</DialogHeader>
|
|
222
|
+
<div className="grid gap-4">
|
|
223
|
+
<div className="grid gap-2">
|
|
224
|
+
<Label htmlFor="preset-title">{t("brandKitDetail.name")}</Label>
|
|
225
|
+
<Input
|
|
226
|
+
id="preset-title"
|
|
227
|
+
value={title}
|
|
228
|
+
onChange={(event) => setTitle(event.target.value)}
|
|
229
|
+
placeholder={t("brandKitDetail.campaignLaunch")}
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
<div className="grid grid-cols-2 gap-3">
|
|
233
|
+
<div className="grid gap-2">
|
|
234
|
+
<Label>{t("brandKitDetail.category")}</Label>
|
|
235
|
+
<Select
|
|
236
|
+
value={category}
|
|
237
|
+
onValueChange={(value) => setCategory(value as ImageCategory)}
|
|
238
|
+
>
|
|
239
|
+
<SelectTrigger>
|
|
240
|
+
<SelectValue />
|
|
241
|
+
</SelectTrigger>
|
|
242
|
+
<SelectContent>
|
|
243
|
+
{IMAGE_CATEGORIES.map((item) => (
|
|
244
|
+
<SelectItem key={item} value={item}>
|
|
245
|
+
{item}
|
|
246
|
+
</SelectItem>
|
|
247
|
+
))}
|
|
248
|
+
</SelectContent>
|
|
249
|
+
</Select>
|
|
250
|
+
</div>
|
|
251
|
+
<div className="grid gap-2">
|
|
252
|
+
<Label>{t("brandKitDetail.aspectRatio")}</Label>
|
|
253
|
+
<Select
|
|
254
|
+
value={aspectRatio}
|
|
255
|
+
onValueChange={(value) =>
|
|
256
|
+
setAspectRatio(value as AspectRatio)
|
|
257
|
+
}
|
|
258
|
+
>
|
|
259
|
+
<SelectTrigger>
|
|
260
|
+
<SelectValue />
|
|
261
|
+
</SelectTrigger>
|
|
262
|
+
<SelectContent>
|
|
263
|
+
{ASPECT_RATIOS.map((ratio) => (
|
|
264
|
+
<SelectItem key={ratio} value={ratio}>
|
|
265
|
+
{ratio}
|
|
266
|
+
</SelectItem>
|
|
267
|
+
))}
|
|
268
|
+
</SelectContent>
|
|
269
|
+
</Select>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
<div className="grid gap-2">
|
|
273
|
+
<Label htmlFor="preset-template">
|
|
274
|
+
{t("brandKitDetail.promptTemplate")}
|
|
275
|
+
</Label>
|
|
276
|
+
<Textarea
|
|
277
|
+
id="preset-template"
|
|
278
|
+
value={promptTemplate}
|
|
279
|
+
onChange={(event) => setPromptTemplate(event.target.value)}
|
|
280
|
+
placeholder={t("library.promptTemplatePlaceholder")}
|
|
281
|
+
/>
|
|
282
|
+
</div>
|
|
283
|
+
<div className="grid gap-2">
|
|
284
|
+
<Label htmlFor="preset-text-policy">
|
|
285
|
+
{t("brandKitDetail.textPolicy")}
|
|
286
|
+
</Label>
|
|
287
|
+
<Textarea
|
|
288
|
+
id="preset-text-policy"
|
|
289
|
+
value={textPolicy}
|
|
290
|
+
onChange={(event) => setTextPolicy(event.target.value)}
|
|
291
|
+
/>
|
|
292
|
+
</div>
|
|
293
|
+
<label
|
|
294
|
+
htmlFor="preset-include-logo"
|
|
295
|
+
className="flex items-start gap-3 rounded-md border border-border p-3"
|
|
296
|
+
>
|
|
297
|
+
<Checkbox
|
|
298
|
+
id="preset-include-logo"
|
|
299
|
+
checked={includeLogo}
|
|
300
|
+
onCheckedChange={(checked) => setIncludeLogo(checked === true)}
|
|
301
|
+
className="mt-0.5"
|
|
302
|
+
/>
|
|
303
|
+
<span className="grid gap-1">
|
|
304
|
+
<span className="text-sm font-medium leading-none">
|
|
305
|
+
{t("brandKitDetail.compositeCanonicalLogo")}
|
|
306
|
+
</span>
|
|
307
|
+
<span className="text-xs text-muted-foreground">
|
|
308
|
+
{t("brandKitDetail.compositeCanonicalLogoHint")}
|
|
309
|
+
</span>
|
|
310
|
+
</span>
|
|
311
|
+
</label>
|
|
312
|
+
</div>
|
|
313
|
+
<DialogFooter>
|
|
314
|
+
<Button variant="outline" onClick={() => setOpen(false)}>
|
|
315
|
+
{t("brandKitDetail.cancel")}
|
|
316
|
+
</Button>
|
|
317
|
+
<Button disabled={!title.trim()} onClick={submit}>
|
|
318
|
+
{t("brandKitDetail.create")}
|
|
319
|
+
</Button>
|
|
320
|
+
</DialogFooter>
|
|
321
|
+
</DialogContent>
|
|
322
|
+
</Dialog>
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
@@ -47,6 +47,15 @@ function navigationFromPath(pathname: string, search = "") {
|
|
|
47
47
|
presetId: decodePathParam(preset[2]),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
const brandKitSettings = pathname.match(/^\/brand-kits\/([^/]+)\/settings/);
|
|
51
|
+
if (brandKitSettings) {
|
|
52
|
+
return {
|
|
53
|
+
view: "library",
|
|
54
|
+
selection: decodePathParam(brandKitSettings[1]),
|
|
55
|
+
libraryId: decodePathParam(brandKitSettings[1]),
|
|
56
|
+
activeTab: "settings",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
50
59
|
// The "library" view is the unified Library workspace. Keep the internal
|
|
51
60
|
// detail key stable for agent/MCP callers that already navigate by brand-kit
|
|
52
61
|
// id, while the URL is now /library/:id.
|
|
@@ -115,6 +124,9 @@ function pathFromCommand(command: any): string | null {
|
|
|
115
124
|
if (!command) return null;
|
|
116
125
|
if (typeof command.path === "string") return command.path;
|
|
117
126
|
if (command.view === "library" && command.libraryId) {
|
|
127
|
+
if (command.activeTab === "settings") {
|
|
128
|
+
return `/brand-kits/${encodeURIComponent(command.libraryId)}/settings`;
|
|
129
|
+
}
|
|
118
130
|
const params = new URLSearchParams();
|
|
119
131
|
if (typeof command.activeTab === "string") {
|
|
120
132
|
params.set("tab", command.activeTab);
|
|
@@ -698,6 +698,27 @@ const messages = {
|
|
|
698
698
|
"清除即時候選面板並刪除所有未儲存的候選行。",
|
|
699
699
|
couldNotClearLiveCandidates: "無法清除即時候選項。",
|
|
700
700
|
liveCandidatesActions: "即時候選項操作",
|
|
701
|
+
settingsTitle: "品牌套件設定",
|
|
702
|
+
unsavedChangesTitle: "未儲存的變更",
|
|
703
|
+
unsavedChangesDescription:
|
|
704
|
+
"此品牌套件有未儲存的變更。要放棄變更還是繼續編輯?",
|
|
705
|
+
discardChanges: "放棄變更",
|
|
706
|
+
keepEditing: "繼續編輯",
|
|
707
|
+
setupGuide: "設定指南",
|
|
708
|
+
setupGuideDescription:
|
|
709
|
+
"為了從此套件獲得最一致、最符合品牌調性的生成結果的建議。",
|
|
710
|
+
setupGuideReferences: "上傳參考素材",
|
|
711
|
+
setupGuideReferencesHint:
|
|
712
|
+
"將 3-5 個強而有力的範例標記為參考,讓分析品牌能從真實作品中學習調色板和視覺特徵,而非猜測。",
|
|
713
|
+
setupGuideStyleDescription: "撰寫具體的風格描述",
|
|
714
|
+
setupGuideStyleDescriptionHint:
|
|
715
|
+
"使用具體特徵(光線、構圖、質感)而非模糊的形容詞,讓每次生成都符合品牌調性。",
|
|
716
|
+
setupGuideInstructions: "使用自訂指令來設定硬性限制",
|
|
717
|
+
setupGuideInstructionsHint:
|
|
718
|
+
"將必須始終成立的事項(標誌位置、禁用顏色、法律文字)放在此處,讓代理永遠不會忘記。",
|
|
719
|
+
setupGuidePresets: "為每種常用格式建立一個預設",
|
|
720
|
+
setupGuidePresetsHint:
|
|
721
|
+
"將長寬比、模型和骨架/參考設定儲存為預設,讓每次都重複使用相同的輸出形式。",
|
|
701
722
|
},
|
|
702
723
|
brandKits: {
|
|
703
724
|
title: "品牌套件",
|